Skip to content

fix(ark): stop a stale armed reserve claiming the exit is funded - #188

Merged
1 commit merged into
mainfrom
fix/ark-exit-reserve-target-stale
Aug 23, 2026
Merged

1 commit merged into
mainfrom
fix/ark-exit-reserve-target-stale

Conversation

@CypherBoxLLC

Copy link
Copy Markdown
Owner

Found on device. The screen contradicted itself, and the safe-looking side was the wrong one.

What it did

The exit-fee gate measured the shortfall against whatever reserve the user had armed, and let that
value replace the live recommendation outright:

armed > 0 ? armed : recommended

An armed figure is a snapshot of one decision. The recommendation is what an exit is estimated to
cost now, and it moves with the wallet: with capsule count, and far more sharply with exit depth,
which the reserve is linear in and which measures 25x apart inside a single wallet. So the armed
value goes stale and nothing notices.

Observed 2026-08-20

armed reserve 3,654
on-chain 6,259
recommendation 233,120
reported shortfall 0

The same screen recommended 233,120 sats and reported the shortfall as 0, said the reserve was fully
funded, and rendered "Send at least 0 sats to this address". The user was 227k short of the app's own
estimate while being told they were ready to exit.

reserveBelowRecommended already detected exactly this condition. It produced a warning the gate
ignored.

That 0 also propagated: the funding confirm screen received shortfallSats: 0, planExitFunding
correctly answered "The exit-fee reserve is already funded", and the screen rendered that permanent
state as a spinner captioned "Please wait". Fixed separately in #183.

The fix

Take the greater of the two. Agency is kept in the safe direction, since arming MORE than recommended
is a deliberate fee-spike buffer and is honoured, and removed in the dangerous one, since an armed
value can no longer assert the exit is funded when the estimate disagrees.

Extracted as a pure module so it is testable without the native bark module, matching
exitFundingPlan.ts and refreshBatch.ts.

Verification

  • 7 tests using the exact figures read off the device, mutation-checked: restoring the old rule
    fails 2 of them
  • 37 suites, 258 tests green
  • tsc error set byte-identical to origin/main (405 errors), zero differing lines, none in any
    touched file

Follow-up, not in scope here

Auto-board is unaffected: it holds arkExitFeeReserveSats, which this does not change, and the
CoinOS funding path already arms that to current + sent before sending. But if a user funds toward
a recommendation far above their armed value by external deposit, auto-board would board the
excess away once the on-chain balance clears the board minimum. That wants fixing before anyone
funds a six-figure reserve that way.

The exit-fee gate measured the shortfall against whatever reserve the user had
armed, and let that value REPLACE the live recommendation entirely:

    armed > 0 ? armed : recommended

An armed figure is a snapshot of one moment. The recommendation is what an exit
is estimated to cost now, and it moves with the wallet: with capsule count, and
far more sharply with exit depth, which the reserve is linear in and which was
measured varying 25x inside a single wallet. So the armed value goes stale, and
nothing notices.

Observed on device 2026-08-20. Armed 3,654. On-chain 6,259. Recommendation
233,120. The same screen simultaneously recommended 233,120 sats and reported
the shortfall as 0, told the user the reserve was fully funded, and rendered
"Send at least 0 sats to this address". The user was 227k short of the app's own
estimate and was being told they were ready to exit. `reserveBelowRecommended`
already detected exactly this condition and produced only a warning that the
gate ignored.

That shortfall of 0 also propagated: the funding confirm screen received
shortfallSats 0, planExitFunding correctly returned "The exit-fee reserve is
already funded", and the screen rendered that permanent state as a spinner
captioned "Please wait" (fixed separately in #183).

The target is now the greater of the two. User agency is kept in the safe
direction, since arming MORE than recommended is a deliberate fee-spike buffer
and is honoured, and removed in the dangerous one, since an armed value can no
longer assert the exit is funded when the estimate disagrees.

Extracted as a pure module so it is testable without the native bark module,
matching exitFundingPlan.ts and refreshBatch.ts. 7 tests, using the exact
figures read off the device, mutation-checked: restoring the old rule fails 2 of
them.

Auto-board is unaffected: it holds `arkExitFeeReserveSats`, which this does not
change, and the CoinOS funding path already arms that to current + sent before
sending. Worth a follow-up though: if a user funds toward a recommendation far
above their armed value by external deposit, auto-board would board the excess
away once the on-chain balance clears the board minimum.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants