diff options
| author | Adam Soltys <[email protected]> | 2026-06-27 14:46:02 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-06-27 14:46:02 -0700 |
| commit | eeceebb09878a544b18926a3cd164e581b0c9869 (patch) | |
| tree | ded51a2f259dbe878d52334ca57e6e89416491c7 /src/wallet.js | |
| parent | 1beae75ebd34142cc63342fbf7c8a798af45bb5a (diff) | |
| download | coinosv3-eeceebb09878a544b18926a3cd164e581b0c9869.tar.gz coinosv3-eeceebb09878a544b18926a3cd164e581b0c9869.zip | |
Wire SP foundations into the wallet: configurable indexer + keys/address
api.js: per-network SP indexer config (presets + custom, stored per network) with
getSpIndexerConfig/spIndexerUrl/spReceiveEnabled — regtest defaults to the local
indexer, others to custom; a hosted mainnet default slots in once we run one.
wallet.js: silentPaymentKeys() derives BIP-352 scan/spend keys from the master
seed (needs the mnemonic / depth-0 xprv), and silentPaymentAddress() returns the
sp1…/tsp1… address. Verified it matches the vector-validated e2e address.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Diffstat (limited to 'src/wallet.js')
| -rw-r--r-- | src/wallet.js | 24 |
1 files changed, 23 insertions, 1 deletions
diff --git a/src/wallet.js b/src/wallet.js index 46b9f5c..219b0f5 100644 --- a/src/wallet.js +++ b/src/wallet.js @@ -24,7 +24,7 @@ import { concatBytes } from '@scure/btc-signer/utils.js'; import { Api, pool, getBackend, explorerWeb, electrumCandidates } from './api.js'; import { ElectrumApi } from './electrum.js'; import { NostrSync, getSyncConfig } from './nostr.js'; -import { isSilentPaymentAddress, decodeSilentPaymentAddress, silentPaymentScripts, silentPaymentPlaceholder } from './silentpay.js'; +import { isSilentPaymentAddress, decodeSilentPaymentAddress, silentPaymentScripts, silentPaymentPlaceholder, deriveSilentPaymentKeys, encodeSilentPaymentAddress, silentPaymentScan } from './silentpay.js'; // No look-ahead: stop scanning a chain at the first unused address. This wallet // only ever exposes ONE unused address at a time (freshReceive = first unused; @@ -255,6 +255,28 @@ export class Wallet { return this.account().publicExtendedKey; } + // BIP-352 scan/spend keys, derived from the master seed (m/352' paths) — so + // they need the mnemonic (or a depth-0 master xprv); watch-only and account- + // level imported keys can't do silent payments. Cached per account. + silentPaymentKeys() { + if (this.watchOnly) return null; + const coin = NETS[this.netName].coin; + const ck = `sp|${this.netName}|${this.mnemonic}|${this.passphrase}|${this.xprv}`; + if (this._spKeys && this._spKeysKey === ck) return this._spKeys; + let master = null; + if (this.mnemonic) master = HDKey.fromMasterSeed(mnemonicToSeedSync(this.mnemonic, this.passphrase)); + else if (this.xprv) { const n = HDKey.fromExtendedKey(this.xprv); if (n.depth === 0) master = n; } + this._spKeys = master ? deriveSilentPaymentKeys(master, coin) : null; + this._spKeysKey = ck; + return this._spKeys; + } + + // Our silent-payment address (sp1…/tsp1…), or null if this wallet can't do SP. + silentPaymentAddress() { + const k = this.silentPaymentKeys(); + return k ? encodeSilentPaymentAddress(k.scanPub, k.spendPub, { testnet: this.netName !== 'mainnet' }) : null; + } + node(chain, index) { // Relative derivation from the account node (chain/index are non-hardened). return this.account().deriveChild(chain).deriveChild(index); |
