aboutsummaryrefslogtreecommitdiffstats
path: root/src/swap.js
Commit message (Collapse)AuthorAgeFilesLines
* Simplify: one Lightning path, one balance, fewer featuresAdam Soltys2026-08-011-459/+0
| | | | | | | | | | | | | | | | | | | | | | | | | Dropped: silent payments (feature, wallet chains, scan worker, indexer config), Boltz submarine/reverse swaps (feature + swap.js + tools), the third-party ark swap bridge (config, client flows, settings card — the ASP's own Lightning send is the one path now), and the Consolidate coins settings action. The NWC pouch is gone with them: background answering now spends the WHOLE ark balance. The IndexedDB record mirrors the real manager state plus the chain-3 keys for its live coins and a change-index window; the worker pays through the ASP exactly like the open app, and the app folds the worker's spends, change, and key allocations back in on connect (additive merge + reconcile against the server). A leftover chain-6 pouch from the short-lived pouch era is swept home automatically using the keys stored in its own record. The stated tradeoff: same-origin script access now reaches the ark balance, not a carve-out — the ark balance is walking-around money, the on-chain wallet's keys are never mirrored, and per-connection NWC budgets remain the enforced ceiling. Bundle: 726KB -> 602KB. Co-Authored-By: Claude Fable 5 <[email protected]>
* Signet network — Second's public ASP is the default Ark providerAdam Soltys2026-07-041-1/+1
| | | | Co-Authored-By: Claude Fable 5 <[email protected]>
* Features phase 2: gift/swap/ark primitives leave the core walletAdam Soltys2026-07-041-0/+38
| | | | | | | | | | | | | | | | | | | | | | | | | | | wallet.js drops from 2,762 to 2,374 lines and now exposes two generic seams instead of feature code: - registerCoinLock()/lockedCoinIds(): features contribute coin ids that spending and the balance treat as unavailable (gift reservations). - loadFeatureState()/saveFeatureState(): namespaced per-wallet persisted state. src/features/gifts-wallet.js takes the whole gift engine — reservation sets, gift records, coin selection, the SIGHASH_SINGLE/NONE PSBT builder, the compact codec, claim building, nostr-locked gifts — as an installGiftWallet(wallet) instance install plus standalone exports. Swap storage/key helpers (loadSwaps/saveSwaps/swapNode/nextSwapIndex) install from swap.js; ark state storage installs from the ark feature. The silent-payment scan worker only embeds when the sp feature ships (wallet.js already tolerated its absence). Minimal profile: 412KB, down from 476KB (full: 578KB). All suites pass: ark UI e2e, ArkManager crash-resume, gift lifecycle, minimal smoke. SP's engine (scanning, BIP-352 signing) stays in wallet.js — extracting it safely wants an SP e2e harness on this stack first. Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01QFAcD4V284DvYZZHP7Rxxy
* Swaps: reuse the generic 'Payment received' screen for LN receivesAdam Soltys2026-06-301-1/+5
| | | | | | | | | | | | A reverse-swap claim is an on-chain receive, so instead of a separate LN 'Received!' card (which stacked with the generic celebration and needed suppression), credit the claimed coin to the wallet the instant we broadcast the claim — new wallet.creditReceive() mirrors the instant-payment watcher path (adds the utxo + pending row, advances the receive index, emits). The generic 'Payment received!' then fires immediately with the right amount, one screen, one tap. Dropped the LN success card + the suppression logic. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Swaps: don't reuse a burned swap index (deterministic-preimage collision)Adam Soltys2026-06-291-7/+19
| | | | | | | | | | | | | | | A reverse swap's preimage is sha256(swapNode(idx).privateKey) — deterministic per index. nextSwapIndex was derived only from *saved* swaps, so an attempt that created the swap at Boltz but threw before saving (e.g. the lockup-address-mismatch bug) never advanced the index; the next try reused the same preimage and Boltz rejected it ('a swap with this preimage hash exists already'). Make nextSwapIndex a persisted, monotonic counter (reserves the index on every call, seeded from saved swaps), and have startReverse retry with the next index when Boltz reports the preimage already exists — which also recovers wallets already stuck on a burned index. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Swaps: fix lockup address mismatch on mutinynet (missing NETS entry)Adam Soltys2026-06-291-1/+4
| | | | | | | | | | | | swapP2TR resolves the network via netFor(name) → NETS[name] || btc.NETWORK, but NETS had no mutinynet entry, so it fell back to MAINNET — Hal computed a bc1p… swap address while Boltz (bitcoinSignet) returns tb1p…, tripping 'lockup address mismatch — not paying'. Add mutinynet: btc.TEST_NETWORK (signet/testnet params, matching the wallet's own netCfg). Verified: swapP2TR now derives tb1p… for mutinynet (was bc1p…). Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Send progressive disclosure, swap context in tx detail, LN amount placeholderAdam Soltys2026-06-291-0/+3
| | | | | | | | | | | | | | | | | | - Send: show only the destination input ('Address or Lightning invoice') until a valid on-chain/SP address is entered, then reveal amount + fee rate + coin selection + Review. A pasted bolt11 still auto-advances to its own confirmation, so those controls never appear for it. New wallet.isOnchainAddress() validator + a destReady() gate; recipient onInput/paste re-render only when validity flips. - History: stop listing swaps at the top. Instead, a send/receive that was part of a swap shows its Lightning context (invoice, swap fee, status, refund) on the tx detail screen via swaps.findByTxid(). Removed the dead swapListView. - Lightning receive: drop the '50000' amount placeholder — a bare number looked like a value was already entered; the min/max hint below covers it. Verified in-browser: controls hidden until a valid address, then revealed; LN receive placeholder empty; no errors. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Send: pay a pasted Lightning invoice via an auto submarine swap (itemized cost)Adam Soltys2026-06-291-4/+24
| | | | | | | | | | | | | | | | | Paste a bolt11 into the normal Send recipient field and Review now detects it and routes through a Boltz submarine swap — no 'swap' concept surfaced. Split startSubmarine into quoteSubmarine (POST + build-but-don't-broadcast the funding tx) and fundQuotedSubmarine, so the review shows an itemized cost before committing: Amount + Boltz fee + on-chain network fee = Total. Pay funds the lockup; Boltz pays the invoice once it confirms. Verified on regtest: a 60,000-sat invoice quotes to Amount 60,000 / Boltz 362 / network 442 / Total 60,804, with a Pay button. Lightning-address (LNURL) input + removing the standalone Swap tab are the remaining pieces. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Swaps: mark a submarine swap done at invoice.paid, not transaction.claimedAdam Soltys2026-06-281-1/+5
| | | | | | | | | | | | | | 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) <[email protected]>
* Swaps: bound reverse amount by the provider's published limits, not a ↵Adam Soltys2026-06-281-0/+16
| | | | | | | | | | | | | | | | | hardcoded 50k doReverse rejected anything under a hardcoded 50,000 sats — which happens to be the regtest provider's minimum, but mainnet boltz's real minimum is 25,000 (and providers/networks differ), so valid 25k-50k mainnet swaps were refused and the max was unchecked client-side. Add SwapManager.reverseLimits() (GET /v2/swap/reverse, cached per API) and use it in the swap UI: show the real min/max range under the amount field and validate against it. Reset the cached limits when the network or provider changes. Verified: regtest min/max 50,000/40,294,967; mainnet 25,000/25,000,000. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Add Boltz submarine + reverse swaps (Lightning <-> on-chain)Adam Soltys2026-06-271-0/+359
Non-custodial swaps via Boltz, fully client-side: hal holds the swap key + preimage and claims (reverse) / refunds (submarine) on-chain via the taproot script path, so Boltz can never strand funds. - src/swap.js: tapleaf/tree construction (byte-exact vs boltz-core vectors), swap P2TR address derivation (MuSig2 internal key + taptweak), script-path claim/refund tx builder, bolt11 decode, Boltz api-v2 client, and a polling SwapManager (reverse auto-claim; submarine fund + status + refund). - wallet.js: deterministic swap-key derivation (dedicated chain) + persistence. - api.js: configurable Boltz REST/WS endpoints. - app.js: ⚡ Swap tab — Receive over LN (reverse) + Pay an invoice (submarine). - tools/swap-*.test.js: vectors, live-address, and end-to-end regtest tests. Verified end-to-end on regtest against a local Boltz wired to coinos's bc + a dedicated CLN/hold node. Co-Authored-By: Claude Opus 4.8 <[email protected]>