aboutsummaryrefslogtreecommitdiffstats
path: root/src/nostr.js
Commit message (Collapse)AuthorAgeFilesLines
* Send gift DMs as NIP-17 only (drop NIP-04)Adam Soltys2026-06-281-11/+7
| | | | | | | Stop also sending the legacy NIP-04 kind-4 DM — clients that support both (e.g. Amethyst) were showing the claim code twice. NIP-17 alone covers current clients. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Nostr: use SimplePool (fixes WS console errors) + send DMs as NIP-04 AND NIP-17Adam Soltys2026-06-281-121/+33
| | | | | | | | | | | | | | | | - Migrated all relay I/O (profile/inbox fetch, DM publish, state sync) to a shared SimplePool. The old code opened a relay per request and closed it manually, which raced nostr-tools' CLOSE-after-EOSE and spammed 'WebSocket is already in CLOSING or CLOSED state'. SimplePool owns the connection lifecycle, so that's gone. Also reuses connections instead of churning one per fetch. - Gift DMs now go out as BOTH NIP-04 (kind 4) and NIP-17 (kind 1059 gift wrap). Older clients (Amethyst) read NIP-04; newer ones (Jumble) only support NIP-17 and ignore NIP-04 — sending both means the claim code shows up either way. Verified: profile/inbox fetch + DM still work via the pool; the NIP-17 wrap is published and unwraps to the original message; the NIP-04 kind-4 still lands too. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Locked gifts: let NIP-07 extension users decrypt in-place, skip the codeAdam Soltys2026-06-281-7/+14
| | | | | | | | | | | | | | The gift payload is now encrypted two ways in the link: under the one-time code (the DM / manual path) AND to the recipient's nostr pubkey via an ephemeral key. When a NIP-07 browser extension (window.nostr) is present, the claim view offers 'Claim with nostr extension' — it checks the extension's pubkey matches the gift, decrypts ctKey via window.nostr.nip44.decrypt, and claims into a fresh wallet with no code. The pasted-code path remains as the fallback for everyone else. Verified: both decrypt paths recover the payload from the blob; a different extension account is rejected. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Route the gift DM to the recipient's published inbox relaysAdam Soltys2026-06-281-4/+32
| | | | | | | | | | | | Discover where a recipient reads DMs before sending the claim code: prefer their NIP-17 DM relay list (kind 10050), else their NIP-65 read relays (kind 10002), unioned with the broad fallback set (so users without a list still get it). This makes the locked-gift DM far more likely to land in the recipient's client. Verified: resolves fiatjaf's real inbox relays; a key with no list returns none and falls back to defaults. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Lock a gift to a nostr key: public link, claim code DM'd to the recipientAdam Soltys2026-06-281-0/+70
| | | | | | | | | | | | | | | | | | | | | | | | A gift can now be locked to a recipient's nostr npub. The shareable link (/lg/<blob>) carries the amount + recipient in the clear (anyone can see what it is and who it's for), but the claim payload is encrypted under a one-time code. That code is delivered as an encrypted nostr DM (NIP-04) to the recipient — so only they can read it. They paste the code to decrypt, then claim into a fresh wallet exactly like a normal bearer gift. - nostr.js: parseNostrPubkey/npubOf, encryptWithCode/decryptWithCode (random 32-byte code), fetchNostrProfile, NostrSync.sendDM (broad relays). - wallet.js: lockGift → { blob, claimCode }, previewLockedGift, nostr identity + sendNostrDM. - app.js: gift form gains an optional 'Lock to npub' field that shows the recipient's avatar + name (nostr kind-0 profile); create encrypts + DMs the code (with sent/failed status + a copyable fallback); /lg/ links open a claim view showing the gift + recipient where you paste the code; account settings shows your own npub to share. Verified: code lock/unlock round-trip, a real nostr DM published and read back from relay.damus.io, and the UI loads + renders with no errors. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Configurable Nostr sync; fully reconcile wallet state on loadAdam Soltys2026-06-241-40/+98
| | | | | | | | | | | | | | | | | | | | | Device sync: - Add a "Device sync" settings card: toggle (on by default) + editable relay list (defaults to wss://relay.coinos.io). When off, the wallet never contacts a relay. nostr.js is now multi-relay and reads an enabled/relays preference from localStorage. Reconciliation (fixes a stray "payment received" + stale history on import, especially via the Nostr/cache load path): - Always full-scan on load instead of a frontier-only refreshLive, so balance, history confirmations and spends are reconciled and the celebration baselines against everything that already exists (cache/ Nostr still shows instantly first). - Actually start the periodic full-scan safety net (_deepTimer, 2 min); it was declared but never wired up. - WS pushes now refresh an existing tx's pending→confirmed status, not only add new transactions. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Sync wallet state across devices via encrypted Nostr eventsAdam Soltys2026-06-151-0/+94
Persist wallet state (addresses, utxos, history, indices, fees) as a NIP-44 encrypted, parameterized-replaceable event (kind 30078) addressed to self on relay.coinos.io. The Nostr identity is derived from the same seed (NIP-06), so no extra secret. On load we pull the latest state from Nostr — a device with no local cache gets full state without re-scanning, and another device's updates propagate — using savedAt to keep the newest copy. localStorage stays as the instant/offline cache; state is published (debounced) on every change. (Relay note: added kind 30078 to the strfry write-policy allowlist on prod.) Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>