aboutsummaryrefslogtreecommitdiffstats
path: root/tools/nwc-test.js
Commit message (Collapse)AuthorAgeFilesLines
* NWC: lookup_invoice, so BTCPay Server can connectAdam Soltys13 days1-0/+11
| | | | | | | | | | | | | | | | | | | BTCPay's NWC connector requires get_info, make_invoice, lookup_invoice and list_transactions; we never offered lookup_invoice, so it refused the connection string. - ArkManager.lnLookup answers lookup_invoice from wallet state (by hash or bolt11, either direction; pending/settled/expired/failed, preimage once claimed). A retry's several actions on one hash defer to the furthest. - The open app pushes a pending receive along before answering, so a merchant polling sees it settle. - The service worker now answers make_invoice and lookup_invoice too, and claims a paid invoice's HTLCs during the lookup: a BTCPay sale settles with the wallet closed. Its list_transactions reports receives as well. - get_info and the 13194 info event list the new method. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Dmcrt9KyNDKVWFKnb8BREo
* A zap answers as fast as the route allows.Adam Soltys2026-09-141-0/+9
| | | | | | | | | | | | | | | The wallet used to add two to four seconds of its own around a Lightning send: a random pause plus a relay look-back before paying, and a two-second poll for the result after. The notifier's claim now decides who pays in one round trip, with the relay look-back kept only for replays and for when the notifier gives no verdict; a phone answering from behind another app waits a second so a desktop tab wins the claim instead of paying at a throttled crawl. After the send, one held CheckLightningPayment call brings the preimage the moment CLN has it — for wallet-connect pays, the background worker, and the app's own Send. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01QYJfZ4A1TDCEY4Foq3CApR
* One device pays a zap, whichever of them heard it first.Adam Soltys2026-09-141-0/+13
| | | | | | | | | | | | | Every awake device of a wallet hears a wallet-connect pay request within the same second, and the per-tab lock only reaches one browser profile: two profiles on one laptop each locked an HTLC for the same 21-sat zap. The push notifier now hands each pay request to the first device that claims it; a device that loses the claim stands down silently, and a device that cannot reach the notifier answers as before so nobody goes quiet over a hiccup. The worker path claims the same way. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01QYJfZ4A1TDCEY4Foq3CApR
* A zap answered by two tabs no longer reports as failed.Adam Soltys2026-09-141-1/+27
| | | | | | | | | | | | | | | Every open tab of the same wallet sees every wallet-connect request. Two hidden tabs on one laptop both tried to pay the same zap: the ASP refused the second with "payment already in progress", and that error reached Amethyst half a second before the first tab's preimage. A Web Lock per request now lets one tab (or the worker) answer while the rest stay quiet, and a device that does meet "already in progress" waits for the sibling payment's outcome — relaying its preimage only if the payer never speaks — instead of shouting a failure over sats that went through. The losing side also stops writing a failed row into history for it. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01QYJfZ4A1TDCEY4Foq3CApR
* Answering while closed becomes the default: a new connection asks once, and ↵Adam Soltys2026-08-131-2/+2
| | | | | | | anywhere permission is already granted it arms itself — only an explicit Off is honored forever Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_014tE4U5nEjGTQy8PwYqamfv
* Never let a losing device's error shout over a paid zapAdam Soltys2026-08-011-0/+1
| | | | | | | | | | | Loose leader election for pay_invoice across a user's open tabs: before handling, defer a beat and stay silent if any reply for the request already exists on the relays; and before sending an ERROR reply, re-check — a device that lost the race (mid-init, stale state) was replying 'superseded' after another device's success, and clients trusted the later error, marking paid zaps as failed. Co-Authored-By: Claude Fable 5 <[email protected]>
* Make NWC budgets accumulate and travel across devicesAdam Soltys2026-08-011-0/+14
| | | | | | | | | | | | | onRequest handled every request against the connection object captured at listen() time, so each spend was recorded against a stale snapshot — spentToday restarted from the old value on every payment and the daily budget never actually bound. The connection is now re-resolved from stored state per request, which also stops serving a connection revoked on another device mid-session. recordSpend now publishes the sync snapshot so the spend (and the payment's movement + vtxo change) reaches the user's other devices. Test: two payments must accumulate to 52. Co-Authored-By: Claude Fable 5 <[email protected]>
* Sync NWC connections across devicesAdam Soltys2026-08-011-0/+35
| | | | | | | | | | | | | | | | | | | A connection made on the phone existed only there, so only that device could answer it — and if that device was asleep, a client just spun. Connections now ride the encrypted device-sync snapshot, so whichever device is awake can serve them. The snapshot is nip44-encrypted to a key derived from the seed, the same channel that already carries ark vtxo bytes and gift secrets. The merge is a union by id with two rules that matter: - REVOCATION WINS, so a stale snapshot can never resurrect a revoked connection - the higher same-day spend wins, so a stale device cannot hand a connection back its daily budget Creating or revoking a connection publishes the snapshot immediately rather than waiting for the next state change. Co-Authored-By: Claude Fable 5 <[email protected]>
* NWC: own the relay set instead of borrowing the sync feature'sAdam Soltys2026-08-011-1/+6
| | | | | | | | | | | | | | | | | | | | | The connection URI advertised up to four relays (sync relays + profile relays) while the subscription only ever listened on the sync relay, so a client could publish its request somewhere hal wasn't and spin forever waiting for a reply. Amethyst did exactly that. NWC now has its own relay list used for all three things that must agree: the URI, the subscription, and the replies. It no longer depends on device sync being enabled, which would otherwise have made the whole wallet service a silent no-op. Also republish the kind 13194 capability event on every start — a client that can't find it just spins, and a single publish at creation time is easy for a relay to miss. The relay transport is now injectable so the protocol can be exercised without a live relay (tools/nwc-test.js). Co-Authored-By: Claude Fable 5 <[email protected]>
* Add Nostr Wallet Connect (NIP-47) — hal as a wallet serviceAdam Soltys2026-08-011-0/+112
Other apps can connect over nostr and pay from this wallet without touching its keys, within a per-payment and daily budget the user sets, revocable at any time. hal holds no native Lightning balance, so every operation runs over Ark: pay_invoice takes the swap bridge when configured and falls back to the ASP's lightning-send, make_invoice mints on the ASP. NWC availability therefore tracks Ark availability. - kinds 13194/23194/23195, nip44_v2 and nip04, replying in whichever scheme the request used; every request gets a reply so clients never hang - each connection gets its own service keypair, so revoking one can't affect the others and none is tied to the user's nostr identity - budgets are checked BEFORE spending, not reconciled after - requests are ignored unless signed by that connection's client key, and replayed/stale events (>120s) can't re-trigger a payment - Settings card to create, show (URI + QR) and revoke connections, and it says plainly that this only works while a tab is open tools/nwc-test.js drives it as a real client: both encryption schemes, budget refusal with a genuine over-limit invoice, unsupported-method errors, and a request from an unauthorized key being ignored. Co-Authored-By: Claude Fable 5 <[email protected]>