diff options
| author | Adam Soltys <[email protected]> | 2026-06-15 22:57:39 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-06-15 22:57:39 -0700 |
| commit | 5f478de7d90f9170b81b21af6492de1be2076696 (patch) | |
| tree | db1747b15ce78110a02ca921de32db61238d5f0b | |
| parent | b7e73b295311fcfcff3e247bb4451fbf757cefb9 (diff) | |
| download | coinosv3-5f478de7d90f9170b81b21af6492de1be2076696.tar.gz coinosv3-5f478de7d90f9170b81b21af6492de1be2076696.zip | |
Stop re-scanning coin addresses; frontier-only poll + manual rescan
Per request: an address is scanned once, then never auto-scanned again. The
routine poll now checks only the fresh frontier (next receive + change
address) for new deposits; coin-holding addresses are no longer polled and
the periodic full re-scan is removed. Spends on existing coins arrive via the
WebSocket while connected; a new Settings → Rescan button does a full scan to
catch anything missed (e.g. a payment to a reused old address).
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
| -rw-r--r-- | src/app.js | 11 | ||||
| -rw-r--r-- | src/wallet.js | 28 |
2 files changed, 25 insertions, 14 deletions
@@ -521,6 +521,17 @@ function settingsTab() { { class: 'card col' }, h('h3', {}, 'Offline transfer'), snapshotActions() + ), + h( + 'div', + { class: 'card col' }, + h('h3', {}, 'Rescan'), + h('p', { class: 'small muted', style: 'margin:0' }, + 'Your current address updates automatically. Rescan only if a payment was sent to an old (reused) address and isn’t showing.'), + wallet.offline + ? null + : h('button', { disabled: wallet.scanning, onClick: () => wallet.scan() }, + wallet.scanning ? 'Scanning…' : 'Rescan wallet') ) ); } diff --git a/src/wallet.js b/src/wallet.js index b3c6a25..440f8dc 100644 --- a/src/wallet.js +++ b/src/wallet.js @@ -229,21 +229,22 @@ export class Wallet { return arr.find((a) => a.index === index); } - // Light poll: only re-query the addresses that can actually change — ones - // holding coins (could be spent) and the next fresh receive/change address - // (could receive). Stable historical addresses stay cached and aren't - // re-fetched. If anything moved, fall back to a full scan to reconcile. + // Light poll: only check the fresh frontier — the next receive and change + // address — for new activity. Already-scanned addresses are never re-polled; + // changes to them (spends) arrive over the WebSocket while connected, and a + // manual rescan covers anything missed (e.g. address reuse). If the frontier + // moved, escalate to a full scan once to reconcile. async refreshLive() { if (this.offline || this._refreshing || this._polling) return; this._polling = true; try { - const targets = new Map(); - for (const u of this.utxos) targets.set(`${u.chain}/${u.index}`, { chain: u.chain, index: u.index }); - targets.set(`0/${this.nextReceiveIndex}`, { chain: 0, index: this.nextReceiveIndex }); - targets.set(`1/${this.nextChangeIndex}`, { chain: 1, index: this.nextChangeIndex }); + const targets = [ + { chain: 0, index: this.nextReceiveIndex }, + { chain: 1, index: this.nextChangeIndex }, + ]; let changed = false; - for (const { chain, index } of targets.values()) { + for (const { chain, index } of targets) { const { address } = this.derive(chain, index); const info = await this.api.addressInfo(address); const cs = info.chain_stats || {}; @@ -497,11 +498,10 @@ export class Wallet { startRealtime() { if (this.offline) return; this.stopRealtime(); - // Safety-net polling (the WebSocket gives instant updates, but its pushes - // can be missed): a light poll of just the live addresses every 15s, plus - // an occasional full scan to catch anything the frontier check can't. - this._pollTimer = setInterval(() => this.refreshLive(), 15000); - this._deepTimer = setInterval(() => this.scan({ silent: true }), 300000); + // Safety-net poll: only the fresh frontier (next receive/change address), + // in case the WebSocket misses a deposit. Already-scanned addresses are + // never re-polled. No periodic full scan — use the manual Rescan for that. + this._pollTimer = setInterval(() => this.refreshLive(), 20000); if (typeof WebSocket === 'undefined') return; this._wsWant = true; |
