aboutsummaryrefslogtreecommitdiffstats
path: root/src/ark/manager.js
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-09-27 23:17:12 -0700
committerAdam Soltys <[email protected]>2026-09-27 23:17:12 -0700
commita9a5d1fdce7a342394c2facb2344adbc7aa69aea (patch)
tree76bc49039c6a11442ab4112373e6fc94a4b5269d /src/ark/manager.js
parent44bc9d0596f1d195367799797aca5e76e4924fe9 (diff)
downloadcoinosv3-a9a5d1fdce7a342394c2facb2344adbc7aa69aea.tar.gz
coinosv3-a9a5d1fdce7a342394c2facb2344adbc7aa69aea.zip
A renewal the round would refuse is retired, not resubmitted every sync; /feed opened before the wallet no longer doubles Popular.
A refresh at the created step with an input past its expiry (only the server's backstop can renew it now), or whose fee leaves less than the 330-sat floor (a 21-sat coin with a 150-sat fee sat "in progress" for days), is failed quietly and its inputs go back to spendable. /feed read before the wallet restored made a visitor's session copy of Popular beside the built-in one; the wallet's arrival now folds it away. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01VhUKyXKtsJ67QAFfb7QR9D
Diffstat (limited to 'src/ark/manager.js')
-rw-r--r--src/ark/manager.js17
1 files changed, 17 insertions, 0 deletions
diff --git a/src/ark/manager.js b/src/ark/manager.js
index bfb4a4e..b828d7e 100644
--- a/src/ark/manager.js
+++ b/src/ark/manager.js
@@ -1989,6 +1989,23 @@ export class ArkManager {
const inputRecs = action.inputIds.map((id) => this._vtxo(id));
if (action.step === 'created') {
+ // A set the round would refuse anyway is retired here, not resubmitted
+ // every sync: an input past its expiry (only the server's backstop can
+ // renew it now), or an output the fee leaves under the 330-sat dust
+ // floor (a 21-sat coin with a 150-sat fee sat "in progress" for days).
+ // Nothing was spent, so the inputs go back to spendable.
+ const tip = this._tipH || (await this.chain.tipHeight().catch(() => 0));
+ const inSat = inputRecs.reduce((n, v) => n + (v ? v.amountSat : 0), 0);
+ const expired = inputRecs.some((v) => v && v.expiryHeight && tip && v.expiryHeight <= tip);
+ if (expired || inSat - (action.feeSat || 0) < 330 || inputRecs.some((v) => !v)) {
+ for (const v of inputRecs) if (v && v.state === 'pending') v.state = 'spendable';
+ action.step = 'failed';
+ action.superseded = true; // no row: the renewal never began
+ action.lastError = expired ? 'an input has expired: only the server can renew it now'
+ : inputRecs.some((v) => !v) ? 'an input is no longer held' : 'the fee leaves less than the 330-sat minimum';
+ this._save();
+ return;
+ }
const inputBytes = inputRecs.map((v) => vtxoBytesFromStr(v.bytes));
await registerVtxoTransactions(this.arkUrl, inputBytes);
let unlockHash;