diff options
| author | Adam Soltys <[email protected]> | 2026-09-10 21:05:58 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-09-10 21:05:58 -0700 |
| commit | 3cda220c2432181b5f2dfc0c47e28c3e3cb54f18 (patch) | |
| tree | e727d022556f63d2a6383360fb59a466e58de537 /src/app.js | |
| parent | 1c34bb343c47f6e200575bac967959656ae7becb (diff) | |
| download | coinosv3-3cda220c2432181b5f2dfc0c47e28c3e3cb54f18.tar.gz coinosv3-3cda220c2432181b5f2dfc0c47e28c3e3cb54f18.zip | |
Each account in the switcher wears its own face, and its own renewal warning.
Two accounts could show the same name and avatar: the identity list read a
key stamped onto the account record, and the stamp was taken during the gap
in a wallet switch where the new account is already loaded and saved but the
features haven't re-inited — so the PREVIOUS account's signer was still
answering "who are you". The live signer now re-checks on every read that it
still belongs to the open wallet, not just at init(); and the switcher reads
each account's own persisted login (or its seed's nostr key) instead of the
stamped mirror, so a record stamped wrong before this still lists right.
The Spending renewal warning belonged to the wallet that computed it and
stayed on screen after switching — the old wallet's amount and deadline over
the new wallet's balance, with a 30-minute throttle keeping it there. It now
resets with the ark manager, and the next connect re-decides.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_019mrt228Upn9nNCwGixzSLx
Diffstat (limited to 'src/app.js')
| -rw-r--r-- | src/app.js | 27 |
1 files changed, 21 insertions, 6 deletions
@@ -7,7 +7,7 @@ import { Wallet, newMnemonic, isValidMnemonic, accountXpubFor, cacheKeyFor, utxoId, parseExtendedKey, xpubToZpub, encryptVault, decryptVault } from './wallet.js'; import { qrSvg } from './qr.js'; import { makeSearcher, resultRows, searchable, punkUrl, warmSearch } from './recipient-search.js'; -import { npubOf } from './nostr.js'; +import { npubOf, seedPubkey } from './nostr.js'; import { nip98Header } from './nip98.js'; import { NOSTR_MARK } from './features/nostrlogin.js'; import { scanQr } from './scan.js'; @@ -1327,17 +1327,32 @@ function signInAnother() { render(); } +// The nostr login persisted against an account's OWN wallet state. Feature +// state lives in localStorage keyed by the seed, so this answers "who is that +// account?" for every account on the device, not just the open one — no +// stamping, nothing to go stale. +function loginPkOf(a) { + try { + const base = a.type === 'watch' ? a.xpub : a.xprv || (a.mnemonic ? `${a.mnemonic}\n${a.passphrase || ''}` : null); + if (!base) return null; + const key = cacheKeyFor(a.deriveIndex ? `${base}\n#${a.deriveIndex}` : base); + return (JSON.parse(localStorage.getItem(key + ':nostrlogin') || 'null') || {}).pubkey || null; + } catch { return null; } +} + // The identities on this device — one per seed (a seed's Spending and // Savings faces are the same person), each with the nostr key its header // avatar wears: the nostr login when there is one, else the seed's own key. -// A key is remembered on the account when it's active (saveDirectory) and in -// the durable directory by xpub, so accounts restored from the vault still -// show a face; one never opened on this device has no key yet and falls back -// to its label. +// Both are read from the account itself; the key remembered on the record +// (saveDirectory) and in the durable directory by xpub is only the fallback, +// for a watch-only entry whose seed isn't here to ask. function identities() { let dir = []; try { dir = JSON.parse(localStorage.getItem(WATCH_KEY) || '[]'); } catch {} const dirPk = (a) => (a.xpub && (dir.find((d) => d.xpub === a.xpub) || {}).nostrPk) || null; + const pkOf = (a) => loginPkOf(a) + || (a.mnemonic ? seedPubkey(a.mnemonic, a.passphrase || '', a.deriveIndex || 0) : null) + || a.nostrPk || dirPk(a); const bySeed = new Map(); for (const a of accounts) { if (a.provisional) continue; @@ -1345,7 +1360,7 @@ function identities() { : a.mnemonic ? 'm:' + a.mnemonic + '\n' + (a.passphrase || '') + '\n' + (a.deriveIndex || 0) : 'x:' + (a.xprv || a.xpub || a.id); const active = a.id === activeId; - const pk = a.nostrPk || dirPk(a); + const pk = pkOf(a); const cur = bySeed.get(key); if (!cur) bySeed.set(key, { id: a.id, pk, active, label: seedName(a), network: a.network, watch: a.type === 'watch' }); else { |
