diff options
| author | Adam Soltys <[email protected]> | 2026-10-29 09:52:21 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-10-29 09:52:21 -0700 |
| commit | 0677a8b3357ffb12537081df919a7251bf63e3d4 (patch) | |
| tree | fa1eb73101187580e58b7dadd871f326673118a5 /src/ark/manager.js | |
| parent | 01b6c11555bb9a131dbcabe897327a9f928ccf1b (diff) | |
| download | coinosv3-0677a8b3357ffb12537081df919a7251bf63e3d4.tar.gz coinosv3-0677a8b3357ffb12537081df919a7251bf63e3d4.zip | |
A closed wallet whose payment fails now says so: the error (INSUFFICIENT_BALANCE or PAYMENT_FAILED, with the reason) is left with the notifier at once and goes out unless someone answers within 5 s. Waiting the grace period out in the worker ran past iOS's ten seconds after a failed send, so the other app heard nothing (PullCap, 2026-10-29). A Lightning send no longer picks a coin at the server's depth cap, so such a coin fails here with a reason instead of at the ASP; a capped coin under 330 sats (a round won't take it) says to receive more instead of "waiting to be renewed", and the worker stops booking renewals the server would refuse as dust
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VMan6TUeqTyZB8eXLGymUY
Diffstat (limited to 'src/ark/manager.js')
| -rw-r--r-- | src/ark/manager.js | 13 |
1 files changed, 10 insertions, 3 deletions
diff --git a/src/ark/manager.js b/src/ark/manager.js index aea6020..853ca84 100644 --- a/src/ark/manager.js +++ b/src/ark/manager.js @@ -1182,7 +1182,10 @@ export class ArkManager { .reduce((n, v) => n + v.amountSat, 0); if (expiredSat) return `insufficient ark balance (${expiredSat} sat expired and unusable)`; const cappedSat = this.state.vtxos.filter((v) => this._free(v) && this._capped(v)).reduce((n, v) => n + v.amountSat, 0); - if (cappedSat) return `insufficient ark balance (${cappedSat} sat have a history too long to spend and are waiting to be renewed)`; + // under dust a round won't take it either: only new money pays from here + if (cappedSat) return cappedSat < 330 + ? `insufficient ark balance (${cappedSat} sat have a history too long to spend and are too few to renew; receive more to keep paying)` + : `insufficient ark balance (${cappedSat} sat have a history too long to spend and are waiting to be renewed)`; // coins a renewal took stay unspendable until its round confirms on-chain const renewing = lnShortfall(this.state.vtxos, this.state.actions, Infinity).renewingSat; return renewing @@ -1524,8 +1527,9 @@ export class ArkManager { // fee — else gather largest-first: each input funds its own HTLC vtxo with // the same payment hash, and the server sums them into one payment. const busy = this._remoteBusy(); + // a coin at the depth cap can't be an input: the ASP refuses the cosign const spendable = this.state.vtxos - .filter((v) => this._free(v, busy) && !this._expired(v, tip)); + .filter((v) => this._free(v, busy) && !this._expired(v, tip) && !this._capped(v)); let inputs = null, feeSat = 0; for (const v of this._spendOrder(spendable, tip, (a, b) => a.amountSat - b.amountSat)) { const fee = lnSendFee(amountSat, this.info.lnSendFees, [v], tip) + routing; @@ -2711,8 +2715,11 @@ export class ArkManager { if (!tip) return 0; const covered = new Set((this.state.scheduled || []).flatMap((s) => s.inputIds)); const cap = this.info.maxVtxoAmountSat || Infinity; + // a round output under dust (330 sat) is refused, so a coin that small + // can't be renewed on its own — asking only spends the wake's budget const coins = deep - .filter((v) => !covered.has(v.id) && v.expiryHeight > tip + 2 && v.amountSat <= cap) + .filter((v) => !covered.has(v.id) && v.expiryHeight > tip + 2 && v.amountSat <= cap + && v.amountSat - this.refreshFee([v], tip + 2) >= 330) .sort((a, b) => this.exitDepth(b) - this.exitDepth(a)) .slice(0, maxCoins); let n = 0; |
