From 677eea94b433685c4aa5f18c2ad9f18218cab30d Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Sun, 28 Jun 2026 22:23:43 -0700 Subject: Swaps: mark a submarine swap done at invoice.paid, not transaction.claimed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A submarine swap's success is invoice.paid — Boltz has paid the Lightning invoice, which is the whole point and irreversible. Boltz then claims its on-chain lockup (transaction.claim.pending → claimed), often deferred to a later batch, so the old done-set ({transaction.claimed, invoice.settled}) left Hal stuck showing 'paying / waiting for Boltz to pay' indefinitely after the payment had already gone through. Add invoice.paid + transaction.claim.pending to the done set. Verified: a live swap stuck at transaction.claim.pending flips to 'success · Invoice paid ✓' after the fix. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/swap.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/swap.js b/src/swap.js index de89214..81336f4 100644 --- a/src/swap.js +++ b/src/swap.js @@ -331,7 +331,11 @@ export class SwapManager { _watchSubmarine(id) { if (this.timers.has(id)) return; - const done = new Set(['transaction.claimed', 'invoice.settled']); + // For a submarine swap the success moment is invoice.paid — Boltz has paid the + // Lightning invoice (the whole point). It then claims its on-chain lockup + // (transaction.claim.pending → claimed), often deferred to a later batch, but + // our funds are already gone to the LN destination, so we're done either way. + const done = new Set(['invoice.paid', 'transaction.claim.pending', 'transaction.claimed', 'invoice.settled']); const bad = new Set(['invoice.failedToPay', 'swap.expired', 'transaction.lockupFailed', 'transaction.refunded']); const tick = async () => { const rec = this._get(id); -- cgit v1.2.3