aboutsummaryrefslogtreecommitdiffstats
path: root/src
Commit message (Collapse)AuthorAgeFilesLines
* Stop pointless SP catch-up scansAdam Soltys2026-07-052-2/+12
| | | | | | | | | | | | | A gift-claimed wallet is a brand-new seed but the claim flow never passed generated:true, so every gift wallet ground through the indexer's full history (~336k tweaks) hunting silent payments it could not have received — the CPU burn seen with a fresh gift wallet open. Fresh claim wallets now fast-forward their scan watermark to the tip like any other generated wallet. And two tabs on the same wallet no longer duplicate a catch-up: a localStorage lease, refreshed per chunk, lets the second tab yield. Co-Authored-By: Claude Fable 5 <[email protected]>
* Exit status: real progress, blocks-left countdown, honest failuresAdam Soltys2026-07-053-9/+45
| | | | | | | | | | | | | submitpackage answers HTTP 200 even when it rejects the package — the verdict hides in package_msg/tx-results — so a mainnet no-fee-coin exit sat at 'publishing on-chain…' forever believing its bare hop had relayed. Rejections now surface (and become the actionable needs-a-fee- coin message on the bumpless path). The status line shows hop progress (step 2 of 9), the timelock wait counts down in blocks with a rough wall-clock estimate, and a stuck exit that never published anything gets a 'Cancel this exit' link that returns the vtxo to spendable. Co-Authored-By: Claude Fable 5 <[email protected]>
* Zero-coin unilateral exit + honest actionable errorsAdam Soltys2026-07-051-3/+21
| | | | | | | | | | | | | A fresh gift-only wallet has no on-chain coin for the CPFP fee child, so its exit stalled behind 'hit a snag — retrying automatically' — a precondition dressed up as a transient. When no fee coin exists the hop is now tried bare (a zero-minrelay regtest relays it; a gift wallet can exit with literally nothing but the gift), and if the node refuses, the real message shows: exiting needs an on-chain coin for fees. Errors now carry an actionable flag so the status line knows when to tell the truth verbatim. Co-Authored-By: Claude Fable 5 <[email protected]>
* Show the ark board fee before boardingAdam Soltys2026-07-052-1/+17
| | | | | | | | | | 100,000 in, 99,670 out was a surprise: the ASP's board fee (min/base/ ppm from its published schedule; it funds the exit tx's P2A fee anchor) was only visible after the fact. The board form now shows the fee and the net amount live as you type, and the funding tx's detail view gets an 'Ark server fee' line. Co-Authored-By: Claude Fable 5 <[email protected]>
* Exit works on plain-esplora regtest; human-readable ark status linesAdam Soltys2026-07-053-10/+32
| | | | | | | | | | | | submitPackage falls back to sequential /tx posts when the esplora lacks POST /txs/package (regtest electrs) — works wherever the node relays zero-fee v3 parents. The ark card stops speaking developer: in-flight operations summarize as '{n} deposit(s) confirming' instead of 'board:funded, exit:chain, …', exit lines read 'Exiting N sats — publishing on-chain…', and transient errors show 'hit a snag — retrying automatically' instead of raw gRPC/HTTP text. Co-Authored-By: Claude Fable 5 <[email protected]>
* Chunk the silent-payment catch-up scanAdam Soltys2026-07-051-10/+26
| | | | | | | | | | | | | | | An imported mainnet wallet's first scan pulled the indexer's whole window (~336k tweaks, 25MB) and posted it to the worker as ONE message: it blew the 30s worker timeout, fell back to the inline main-thread loop (minutes of jank) while the worker kept grinding the orphaned request — the runaway CPU/memory users saw. The catch-up now processes 100-block chunks: each worker call is seconds, the scan watermark checkpoints after every chunk (an interrupted catch-up resumes instead of restarting from zero), buffers free as it goes, and the UI yields between chunks. Verified against the live indexer: full catch-up in ~5min of background work, main heap bounded (~80MB peak), no timeouts. Co-Authored-By: Claude Fable 5 <[email protected]>
* Dedupe ark receive movementsAdam Soltys2026-07-051-1/+13
| | | | | | | | | | The mailbox poll and live stream can process the same message concurrently (validation awaits in between): the vtxo itself deduped but both copies pushed a receive movement, so a claimed gift showed twice in History. The movement now only records when _addVtxo actually added the vtxo, and init() prunes duplicates the old race left behind. Co-Authored-By: Claude Fable 5 <[email protected]>
* Per-wallet networkAdam Soltys2026-07-052-17/+38
| | | | | | | | | | | | | | | | | | The network is now a property of each wallet, not a global mode. Every new wallet is born on the currently-selected network; activating a wallet activates its network (features re-init through the same networkChanged hooks the Settings switch uses); the Settings network card moves the ACTIVE wallet and sets the default for new ones. The stamp persists everywhere accounts do — session, durable directory, and vault — and legacy accounts adopt the current global once. Gift claims compose: an ark gift lists only wallets on its network as claim targets, and when none exist the fresh claim wallet is simply born on the gift's network — existing wallets keep theirs, so opening a foreign-network gift never disturbs anything. The wallet switcher tags non-mainnet wallets with their network. Co-Authored-By: Claude Fable 5 <[email protected]>
* Reconcile ark vtxos when the gift form opens with an ark sourceAdam Soltys2026-07-051-0/+1
| | | | | | | | Same trigger as pasting an ark address into Send: the gift form is a send-intent signal, so stale vtxos (spent server-side while this state was idle) drop before coin selection instead of erroring at cosign. Co-Authored-By: Claude Fable 5 <[email protected]>
* Unilateral exit: put vtxos on-chain without the serverAdam Soltys2026-07-057-17/+349
| | | | | | | | | | | | | | | | | | | | | | The trustless backstop behind the Ark card's promise. The vtxo's genesis chain reconstructs to fully-signed txs (keyspend sigs for cosigned/arkoor transitions, preimage script-path for hark leaves); they are zero-fee v3 with P2A anchors, so each hop broadcasts as a [parent, CPFP child] package via esplora POST /txs/package, one confirmed hop at a time (TRUC allows a single unconfirmed parent). After the vtxo tx confirms and the CSV exit delta passes, the claim spends the delayed_sign leaf with only the user key into the wallet's own receive address. Settings gains 'Exit on-chain without the server' with live per-exit status lines; the claim tx is labeled 'Ark exit' in History. Probing uses /tx/:txid (definitive 404), never /tx/:txid/status, which answers confirmed:false for unknown txids. Also: a fresh visitor opening an ark gift link now adopts the gift's network automatically instead of being sent to Settings. Co-Authored-By: Claude Fable 5 <[email protected]>
* Gift detail: direct Reclaim/Revoke buttons, no empty summary boxAdam Soltys2026-07-051-13/+16
| | | | | | | | | The Cancel-then-confirm step is gone: a reserved gift offers View / Reclaim / Revoke immediately, a reclaimed one just Revoke. The summary box only renders when it has content — a reclaimed gift with no recorded date drew an empty gray box. Co-Authored-By: Claude Fable 5 <[email protected]>
* Never apply legacy netName-less nostr snapshotsAdam Soltys2026-07-051-3/+6
| | | | | | | | | A pre-per-network-dtag event can hold ANY network's state; applying one put regtest coins into a mainnet wallet (and its board tx was then rightly rejected by the network). Unstamped snapshots are now skipped — a full scan is safer than a wrong snapshot. Co-Authored-By: Claude Fable 5 <[email protected]>
* Ark gifts: instant off-chain bearer gifts inside the gift flowAdam Soltys2026-07-054-30/+318
| | | | | | | | | | | | | | | | | | | | | | Ark can't presign a bearer spend, so an ark gift is a vtxo sent to an ephemeral ark identity whose seed IS the link (/ag/<code>: net, amount, 32-byte secret). The claimer rebuilds the identity, reads its mailbox, and sweeps to their own ark address — instant, free, and it works for a brand-new empty wallet with zero on-chain footprint. The sender keeps the secret, which is what makes revoke possible (first cosign wins). The gift form gains an On-chain/Ark source toggle when ark balance exists; ark gifts skip the lock/split logic entirely. Claim, choose- wallet, already-claimed, and nostr-locked (/lg/) flows all branch by code kind; unclaimed ark gifts list with one-tap revoke and lazily mark themselves claimed via GetVtxoStatus. Features stay decoupled: gifts asks for ark support via the new ctx.hook seam, so either feature builds alone. Also restores goHome in the feature ctx — lost in the plugin-seam extraction, it made the already-claimed screen throw on render. Co-Authored-By: Claude Fable 5 <[email protected]>
* Collaborative offboard: move the ark balance back on-chainAdam Soltys2026-07-055-11/+399
| | | | | | | | | | | | | | | | | | | | PrepareOffboard/FinishOffboard against captaind — per-input ownership attestations, connector-based musig forfeits (with the deterministic fanout tx when there is more than one input), and fee math that mirrors the server bit-for-bit (weight fee rounds up, ppm-by-expiry rounds down) since it rejects any other net amount. The receive pane gains a 'Move back on-chain' button that withdraws everything to the wallet's own fresh address; the incoming tx is labeled 'Ark withdrawal' in History and the ark side records the movement. Crash-safe: the txid persists before FinishOffboard and resume asks GetVtxoStatus whether the forfeits landed (the server broadcasts the tx itself). Also fixes boarding to applySentTx after broadcasting the funding tx — without it the spent coin lingered in the utxo set (a second board double-spent it), and the post-board scan() could resurrect it by racing the explorer's indexing. Co-Authored-By: Claude Fable 5 <[email protected]>
* Boarding UX: label the funding tx and show an in-flight ark balanceAdam Soltys2026-07-053-3/+34
| | | | | | | | | | The board's on-chain send showed as a generic 'Sent' and the sats vanished from the balance until the vtxo registered. The funding tx now reads 'Ark board' in History (with a detail section showing the net credit), and an 'Ark (boarding…)' line carries the pending amount from broadcast until the board completes. Co-Authored-By: Claude Fable 5 <[email protected]>
* Prune RBF-replaced pending txs from historyAdam Soltys2026-07-042-1/+56
| | | | | | | | | | | | A replaced/evicted unconfirmed tx lingered forever as a phantom pending receive (signetfaucet fee-bumps its batch payout every few minutes, so History grew a new copy per bump). The poll now status-checks pending txs — via /tx/:txid, since /tx/:txid/status answers 200 confirmed:false for txids the node no longer knows — drops the vanished ones, and re-fetches their addresses' coins and history, which _reconcileHeld skips when the replacement pays identical amounts. Co-Authored-By: Claude Fable 5 <[email protected]>
* Per-network nostr sync + honest balance loading stateAdam Soltys2026-07-044-10/+33
| | | | | | | | | | | | One shared d-tag let a network switch apply the mainnet snapshot to a signet wallet (the flash of wrong balance) and clobber mainnet state on the relays. Each network now syncs its own replaceable event (mainnet keeps the legacy bare tag), snapshots carry netName, and a foreign-net snapshot is never applied. The balance card spins until cache, Nostr, or a scan has populated this network once — 'scanning' alone missed the cache/Nostr window after wallet.load(). Co-Authored-By: Claude Fable 5 <[email protected]>
* Signet network — Second's public ASP is the default Ark providerAdam Soltys2026-07-043-7/+22
| | | | Co-Authored-By: Claude Fable 5 <[email protected]>
* Ark: recover from stale vtxo stateAdam Soltys2026-07-044-5/+61
| | | | | | | | | | Match captaind's actual 'not spendable' error so a crashed or foreign spend fails the action instead of retrying forever, and reconcile locally-spendable vtxos against the server (GetVtxoStatus + ownership attestation) when an ark address lands in the send form. boardFee now treats absent proto3 fee fields as zero (Second's signet boards free). Co-Authored-By: Claude Fable 5 <[email protected]>
* Features phase 2b: silent payments + nostr sync leave the core walletAdam Soltys2026-07-047-556/+710
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | wallet.js drops from 2,374 to ~1,970 lines, holding only BIP84 on-chain logic plus generic feature seams. New seams added this pass: registerBalanceExtra extra confirmed/pending buckets registerInputProvider extra spendable inputs for buildTx registerSendPreparer destinations needing post-selection derivation registerInputSigner non-BIP84 input signing registerHistoryProvider / registerScanHook / registerRealtimeHook / registerLoadHook / registerCacheExtension / registerCacheSavedHook src/features/sp-wallet.js: the whole BIP-352 engine — key derivation, worker-accelerated indexer scanning, the live indexer WebSocket, SP outputs as spendable inputs, sp1 send preparation, and per-output-key signing — installs via those seams (installSpWallet). src/features/sync.js: nostr identity + encrypted cross-device state sync plus its Settings card, installed via load/cache-saved/realtime hooks. profileChip/nostrProfile move here-adjacent (into gifts, the only other nostr consumer), so core app.js no longer imports nostr at all. buildTx/sign are now feature-agnostic (input providers + preparers + signers); _recomputeBalance* use the balance-extra registry, so a core build with no SP no longer calls a missing spBalance() (that regression dropped the minimal build into offline mode — caught by the smoke test). New coverage: tools/sp-test.js drives two browser "devices" through an SP send, indexer discovery, and an SP-input spend on the ark regtest chain (needs tools/sp-indexer.js pointed at bitcoind:18543). Minimal smoke now also asserts no SP/nostr code ships. Minimal profile: 314KB, down from 412KB (full: 580KB). All suites green: minimal smoke, sp-test, ark UI, gift lifecycle. Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01QFAcD4V284DvYZZHP7Rxxy
* Features phase 2: gift/swap/ark primitives leave the core walletAdam Soltys2026-07-046-429/+521
| | | | | | | | | | | | | | | | | | | | | | | | | | | 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
* Build profiles: HAL_FEATURES selects which features shipAdam Soltys2026-07-042-5/+18
| | | | | | | | | | | | | | | | | | | | | | build.js swaps src/features/index.js for a generated module importing only the enabled features (gifts,swaps,ark,sp — unset means all, "none" means a plain on-chain wallet), so excluded feature code and its network endpoints never enter the bundle. Minimal profile: 476KB vs 577KB, with the Ark/gRPC, Boltz, and MuSig2 code verifiably absent. `bun run build:minimal` builds it. tools/minimal-smoke.js proves the minimal profile end-to-end: no receive-mode dropdown, no gift button, no swap/Ark/SP settings cards, no page errors, and a plain on-chain receive still lands. The two blessed profiles are full (everything, the existing e2e suites) and minimal (this smoke test); other combos should work but aren't CI'd. Gift and SP primitives still live inside wallet.js, so those bytes remain in the minimal bundle for now — extracting them is the next step if we want the core leaner still. Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01QFAcD4V284DvYZZHP7Rxxy
* Features: extract gifts behind the plugin seamAdam Soltys2026-07-042-749/+853
| | | | | | | | | | | | | | | | | | | | | | The whole gift surface moves to src/features/gifts.js (~800 lines): create/split/lock flows, link viewing, revoke/reclaim, history rows + detail page, claim-tx row decoration and tx-detail section, the claim flow (fresh or existing wallet, nostr-locked gifts, claim codes, NIP-07 extension), the boot URL handling (/g/, /lg/, #gift=), the post-claim celebration, and the locked-in-gifts balance line. New seams this required: screenView (full-screen feature views), bootUrl (URL takeover at startup), giftOpened (claim pre-check on wallet activation), sendFormExtras, decorateTxRow, and multi-section txDetailSection. Account access goes through explicit ctx accessors (getAccounts/setAccounts) — the moved code briefly relied on an accidental cross-module binding that broke activeAccount() during claims; caught by the gift e2e. app.js: 4,206 → 3,000 lines. Ark + gift browser suites pass. Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01QFAcD4V284DvYZZHP7Rxxy
* Features: extract swaps, Ark, and SP behind a plugin seamAdam Soltys2026-07-044-619/+777
| | | | | | | | | | | | | | | | | | | | | | | First step toward optional build profiles. app.js gains a small feature registry (ctx + FEATURES + hook helpers); swaps/Ark/silent-payments UI moves wholesale to src/features/{swaps,ark,sp}.js, each exporting a factory returning integration hooks: init/stop/networkChanged wallet lifecycle receiveModes/receiveTakeover receive tab isSendDest/matchSendText/ interceptReview/sendView/ hideSendControls/sendFormNote send flow historyEntries/historyDetail/ txDetailSection history balanceLines/settingsCards balance card + settings No behavior change: ark + gift browser e2e suites pass, and a probe confirms LN receive, all receive modes, and the three settings cards render as before. Gifts extraction and build flags come next. Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01QFAcD4V284DvYZZHP7Rxxy
* History: gifts are timeline entries, not a separate sectionAdam Soltys2026-07-043-53/+93
| | | | | | | | | | | | | | | Gift links now sort into the send/receive timeline like any entry: a clickable row (🎁, status tag, age, amount) opening a detail page that carries the date/status plus the actions that used to crowd the list row (view gift link, cancel/reclaim with the existing confirm flow). A claimed gift merges into its claim transaction's row — labeled as the gift, clickable to the tx detail, which gains a "This transaction delivered your gift" section (claim txid resolved via outspend and cached on the gift record). The old pinned gifts header and the paginated all-gifts view are gone. Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01QFAcD4V284DvYZZHP7Rxxy
* Gift claim: instant 'Payment received!' after claimingAdam Soltys2026-07-041-2/+35
| | | | | | | | | | | | Claiming into an existing wallet used to drop onto the Receive tab (address/QR visibly rotating) until the background scan noticed the credit and fired the celebration. The claim already knows it succeeded and for how much, so the celebration now shows immediately; the scan runs behind it and its receive-index advance is acknowledged so the generic celebration doesn't fire a second time. Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01QFAcD4V284DvYZZHP7Rxxy
* Gift claim: name the real failure when the funding tx isn't visibleAdam Soltys2026-07-042-1/+15
| | | | | | | | | | | | | | Claiming a gift whose funding tx is unknown to the claimer's data source (different network/chain, or not yet propagated) used to fail with the misleading "already been claimed". Now: - the claim screen probes the funding tx up front (esplora's status/ outspend endpoints don't 404 for unknown txids, so outspend alone can't detect this) and shows a network/data-source notice, and - a missing-inputs broadcast rejection maps to the same explanation instead of the generic already-claimed message. Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01QFAcD4V284DvYZZHP7Rxxy
* Gift claim: correct copy when claiming into an existing walletAdam Soltys2026-07-042-1/+4
| | | | | | | | | "Claim it into a brand-new wallet that only you control." only fits the new-wallet path; claiming into an existing wallet now says which wallet it lands in ("Claim it into your wallet "Wallet 1"."). Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01QFAcD4V284DvYZZHP7Rxxy
* Gift claim: make the wallet chooser read as a choiceAdam Soltys2026-07-042-3/+9
| | | | | | | | | | | | The existing-wallet buttons looked like inert inputs. Now: a "Receive into one of your wallets:" label, a chevron on each wallet row so it reads as tappable, and an "or" divider before the new-wallet button. (tools/ark/gift-shot.js: screenshot harness that mints a real regtest gift and opens its link.) Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01QFAcD4V284DvYZZHP7Rxxy
* Ark: 'Sent!' screen matches the receive celebrationAdam Soltys2026-07-041-3/+7
| | | | | | | | Tap anywhere to proceed instead of a Done button, and the sent amount shows in red (amount-neg) rather than green. Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01QFAcD4V284DvYZZHP7Rxxy
* Ark: real-time receives over the SubscribeMailbox gRPC streamAdam Soltys2026-07-043-39/+134
| | | | | | | | | | | | grpcStream() parses gRPC-web frames incrementally off fetch's body stream; the manager keeps a long-lived SubscribeMailbox subscription (auto-reconnect, 3s backoff) whose messages flow through the same duplicate-safe processing as the poll. Receives now land the moment the sender delivers (~sub-second) instead of on the next poll; the poll stays as fallback and to drive in-flight boards/refreshes. Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01QFAcD4V284DvYZZHP7Rxxy
* Ark: history rows open a detail viewAdam Soltys2026-07-043-7/+53
| | | | | | | | | | | Ark movements are clickable like on-chain txs. The detail view shows amount, date, status, the destination address (sends, copyable), the vtxo id, and for boards the funding txid with copy + explorer link. Movements now record those fields at completion time; older records without them still render (amount/date/status only). Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01QFAcD4V284DvYZZHP7Rxxy
* Ark: hide fee-rate, coin control, Max, and add-recipient for Ark sendsAdam Soltys2026-07-042-4/+7
| | | | | | | | | An arkoor send has no mining fee and no coin selection, so pasting an Ark address now shows just the amount field plus a hint line; the irrelevant on-chain controls stay hidden. Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01QFAcD4V284DvYZZHP7Rxxy
* Ark: boarding success screen with txid link instead of a toastAdam Soltys2026-07-042-3/+23
| | | | | | | | | | Broadcasting a board now lands on a checkmark screen showing the amount that will arrive (after the board fee), a note about the confirmation, and the funding txid with copy + explorer link; Done returns to the Receive tab. Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01QFAcD4V284DvYZZHP7Rxxy
* Ark: give the boarding/refresh toasts time to be readAdam Soltys2026-07-031-4/+4
| | | | | | | | toast() takes an optional duration; the two informational Ark messages (full sentences, not confirmations) show for 5s instead of 1.6s. Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01QFAcD4V284DvYZZHP7Rxxy
* Ark: boarding lives on the Receive tab's Ark viewAdam Soltys2026-07-032-14/+22
| | | | | | | | | | | Selecting Ark on Receive now shows the address plus an inline "move on-chain funds into Ark" form (with available balance / minimum hints). The Settings card slims down to server choice, coin count, in-progress operations, and refresh/consolidate; the balance already shows on the balance card. Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01QFAcD4V284DvYZZHP7Rxxy
* Ark: connect lazily — only dial the server once the wallet uses ArkAdam Soltys2026-07-032-14/+58
| | | | | | | | | | | Opening a wallet no longer contacts the ASP unless it already holds vtxos / history / unfinished operations. Fresh wallets connect on first use instead: selecting Ark on the Receive tab, confirming an Ark send, or the Connect button on the Settings card. connectArk() is idempotent and generation-guarded against wallet switches mid-dial. Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01QFAcD4V284DvYZZHP7Rxxy
* Ark: on by default with a per-network default serverAdam Soltys2026-07-032-2/+15
| | | | | | | | | | Mainnet defaults to Second (ark.second.tech), regtest to the local stack. Testnet/mutinynet get their own preset lists (off/custom only — no public ASP exists there) instead of falling back to mainnet's, and stay off by default. Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01QFAcD4V284DvYZZHP7Rxxy
* Ark: drop the activity list from the Settings cardAdam Soltys2026-07-032-10/+0
| | | | | | | | History is the home for Ark movements now; the Settings card keeps balance, coin count, board, refresh, and in-progress operations. Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01QFAcD4V284DvYZZHP7Rxxy
* Ark: show Ark movements in the History tabAdam Soltys2026-07-032-6/+36
| | | | | | | | | Receives, sends, and boards interleave with on-chain transactions in one time-sorted, paginated timeline, each tagged 'Ark'. Refreshes are internal churn and stay in the Settings activity list only. Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01QFAcD4V284DvYZZHP7Rxxy
* Ark: 'Payment received!' celebration for Ark receivesAdam Soltys2026-07-032-0/+33
| | | | | | | | | Same card and tap-to-acknowledge flow as on-chain receives; the ack persists in the ark state (shows exactly once) and a 2h recency guard keeps old payments from celebrating when a wallet is opened later. Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01QFAcD4V284DvYZZHP7Rxxy
* Ark: wire into the wallet UIAdam Soltys2026-07-034-5/+340
| | | | | | | | | | | | | | | | | | | | - Settings: Ark server card (off by default, per-network presets incl. Second mainnet + local regtest, custom URLs) with balance, coin count, board form (on-chain -> Ark; funding output pinned to vout 0 via the new buildTx noSort option), refresh/consolidate, and recent activity. - Receive: Ark option in the address-type dropdown (QR + copy). - Send: t?ark1 addresses are recognized in the normal send form and route to an Ark confirmation (no mining fee) with a success view. - Balance card: Ark balance line under the on-chain figure. - ArkManager lifecycle: per-wallet instance created on account activation, background sync every 5s (regtest) / 30s, state persisted under the wallet cache key. Verified end-to-end in a real browser (tools/ark/ui-test.js, puppeteer against the regtest stack): receive, send, board, and refresh all green. Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01QFAcD4V284DvYZZHP7Rxxy
* Ark: native-JS protocol client + persistent ArkManagerAdam Soltys2026-07-036-0/+1830
| | | | | | | | | | | | | | | | | | | | | | | | | | | | Speaks Second's bark/captaind Ark protocol directly from the browser — gRPC-web over fetch with hand-rolled protobuf, no wasm, no new deps (crypto via the existing @scure/@noble stack incl. btc-signer's BIP-327 musig2 module). src/ark: - proto.js transport, ark address codec, mailbox blinding/auth, VTXO ProtocolEncoding v2 decode - send.js arkoor send: checkpoint+arkoor txs, BIP-341 sighashes, MuSig2 cosign ceremony, signed-VTXO assembly - board.js onchain -> ark: funding taproot, exit-tx cosign - refresh.js delegated round (hArk): participation, tx-chain reconstruction, hash-locked leaf cosign, forfeit swap - validate.js receive-side verification: chain must land on the vtxo point, anchor checked onchain, every transition sig verified - manager.js persistent wallet layer: storage-checkpointed step-machine actions (send/board/refresh) that survive crashes, mailbox sync with validation, multi-input consolidation refresh tools/ark: end-to-end regtest tests (receive, send, board, refresh, manager incl. crash-mid-send + resume), all passing against captaind with the bark CLI as counterparty. Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01QFAcD4V284DvYZZHP7Rxxy
* Add coinos (swap.coinos.io) mainnet Boltz presetAdam Soltys2026-07-021-0/+3
| | | | | | | | | | Production Boltz service now live on cs at swap.coinos.io (dedicated mainnet CLN, chain = coinos production bc on desk over Tailscale). Add it as a selectable provider. Mainnet default stays on public Boltz Exchange until our node has channel liquidity. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01RtuRuzDQ6ffngL5wFw4f9v
* Swaps: reuse the generic 'Payment received' screen for LN receivesAdam Soltys2026-06-303-13/+42
| | | | | | | | | | | | 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]>
* Receive: don't stack the generic celebration over the Lightning 'Received!'Adam Soltys2026-06-301-2/+6
| | | | | | | | | | A reverse-swap claim is also an on-chain receive, so after the LN 'Received!' card the generic 'Payment received' celebration would fire too — making the first Done tap appear to do nothing (it just revealed the second screen). Suppress the generic celebration while the LN claim card is shown, and have its Done ack the generic one as well, so a single tap dismisses both. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Wallet: don't understate 'amount sent' for multi-input/swap-funding txsAdam Soltys2026-06-291-4/+16
| | | | | | | | | | | | | _txSummary computed a tx's spend only from inputs whose prevout address is on the scanned frontier (addrMap). A coin spent from an address not currently in that set — e.g. a fresh reverse-swap claim — was dropped from the total, so the net showed e.g. -4,768 instead of -50,794, and the scan overwrote applySentTx's correct net with it. Now: applySentTx counts every input it spent (falling back to the witness-utxo value if a coin was dropped from the utxo set) and marks the tx 'local'; the rescan preserves a local tx's net/fee instead of recomputing. Funds were always correct on-chain — this was a display-only undercount. 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-292-8/+31
| | | | | | | | | | | | | | | 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]>
* Swaps: add coinos staging (mutinynet) Boltz providerAdam Soltys2026-06-291-1/+3
| | | | | | | | | Point mutinynet wallets at the new self-hosted staging Boltz backend (swap-staging.coinos.io) by default, instead of mainnet api.boltz.exchange. Backend: dedicated boltz CLN + shared mut bitcoind on cs, behind a CORS shim + cloudflared. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>