aboutsummaryrefslogtreecommitdiffstats
path: root/src/wallet.js
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-08-15 14:32:28 -0700
committerAdam Soltys <[email protected]>2026-08-15 14:32:28 -0700
commit643046319fa9a393374fc2348381d9505fe143eb (patch)
tree1f76cb3ace4ce821cca010def27e3b5ec7062fc9 /src/wallet.js
parent16ee8bff43e8733848ef1b944b9d9312ce4ec223 (diff)
downloadcoinosv3-643046319fa9a393374fc2348381d9505fe143eb.tar.gz
coinosv3-643046319fa9a393374fc2348381d9505fe143eb.zip
Wallets go flat: every Spending and Savings is a top-level wallet in the switcher, named for its purpose, renamable apart — and a new wallet can carry its own seed or live at the next BIP84 account of one you already back up
Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_014tE4U5nEjGTQy8PwYqamfv
Diffstat (limited to 'src/wallet.js')
-rw-r--r--src/wallet.js15
1 files changed, 10 insertions, 5 deletions
diff --git a/src/wallet.js b/src/wallet.js
index d94b56f..662ef0f 100644
--- a/src/wallet.js
+++ b/src/wallet.js
@@ -173,13 +173,17 @@ export class Wallet {
}
// --- setup --------------------------------------------------------------
- load({ mnemonic = '', passphrase = '', xpub = '', xprv = '', netName = 'mainnet', offline = false, spFresh = false }) {
+ load({ mnemonic = '', passphrase = '', xpub = '', xprv = '', netName = 'mainnet', offline = false, spFresh = false, accountIndex = 0 }) {
this.stopRealtime();
// A freshly-generated wallet can't have received silent payments before it
// existed, so we start its SP watermark at the current tip (no history scan).
this.mnemonic = (mnemonic || '').trim().replace(/\s+/g, ' ');
this.passphrase = passphrase;
+ // BIP84 account index: 0 for ordinary wallets; a wallet sharing another's
+ // seed lives at the next index, giving it its own coins, ark state, cache
+ // slot and nostr identity while backing up under one phrase.
+ this.accountIndex = accountIndex || 0;
this.xpub = xpub || '';
this.xprv = xprv || ''; // spending wallet imported from an extended private key
this.watchOnly = !!this.xpub && !this.mnemonic && !this.xprv; // view/receive only
@@ -261,7 +265,7 @@ export class Wallet {
// Cache key identifies the source so a reload rebuilds when it changes.
const key = this.watchOnly ? 'pub:' + this.xpub
: this.xprv ? 'prv:' + this.xprv
- : `${this.netName}|${this.mnemonic}|${this.passphrase}`;
+ : `${this.netName}|${this.mnemonic}|${this.passphrase}|${this.accountIndex || 0}`;
if (this._account && this._accountKey === key) return this._account;
let acct;
if (this.watchOnly) {
@@ -270,9 +274,9 @@ export class Wallet {
// A master xprv (depth 0) needs the BIP84 account path derived from it;
// an already account-level xprv is used as-is.
const node = HDKey.fromExtendedKey(this.xprv);
- acct = node.depth === 0 ? node.derive(`m/84'/${coin}'/0'`) : node;
+ acct = node.depth === 0 ? node.derive(`m/84'/${coin}'/${this.accountIndex || 0}'`) : node;
} else {
- acct = HDKey.fromMasterSeed(mnemonicToSeedSync(this.mnemonic, this.passphrase)).derive(`m/84'/${coin}'/0'`);
+ acct = HDKey.fromMasterSeed(mnemonicToSeedSync(this.mnemonic, this.passphrase)).derive(`m/84'/${coin}'/${this.accountIndex || 0}'`);
}
this._account = acct;
this._accountKey = key;
@@ -1772,7 +1776,8 @@ export class Wallet {
// then shows the last-known balance/history instantly while a fresh scan
// runs in the background.
_cacheKey() {
- const id = this.watchOnly ? this.xpub : this.xprv ? this.xprv : `${this.mnemonic}\n${this.passphrase}`;
+ let id = this.watchOnly ? this.xpub : this.xprv ? this.xprv : `${this.mnemonic}\n${this.passphrase}`;
+ if (this.accountIndex) id += `\n#${this.accountIndex}`; // derived siblings get their own slots
const bytes = new TextEncoder().encode(id);
return 'btc-wallet-cache:' + hex.encode(sha256(bytes)).slice(0, 32);
}