diff options
| author | Adam Soltys <[email protected]> | 2026-06-25 00:30:03 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-06-25 00:30:03 -0700 |
| commit | 554716b916c5b4c4ca39bf6344f840c62a0da54a (patch) | |
| tree | 3d5879ecb50b9dca00a0e4d32a661d3a765ac00e /src | |
| parent | a1c20acc78df310e1d805e14c649bc1731e60e9d (diff) | |
| download | coinosv3-554716b916c5b4c4ca39bf6344f840c62a0da54a.tar.gz coinosv3-554716b916c5b4c4ca39bf6344f840c62a0da54a.zip | |
Fast-poll the receive address while watching for a payment
The WS badge reads Live but mempool.space's free address tracking doesn't
reliably push incoming mempool txs, so a deposit could take ~18s to appear (the
25s safety poll catching it). While the user is on the Receive tab (online), now
poll just the fresh receive address every ~3s regardless of Live state — one
cheap request that guarantees near-instant arrival. Toggled idempotently from
render() based on the active tab; stops when they navigate away.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Diffstat (limited to 'src')
| -rw-r--r-- | src/app.js | 3 | ||||
| -rw-r--r-- | src/wallet.js | 39 |
2 files changed, 41 insertions, 1 deletions
@@ -233,6 +233,9 @@ function render() { ? howItWorksScreen() : unlockScreen(); root.replaceChildren(screen, footer()); + // Fast-poll the receive address only while the user is actually watching for a + // payment (wallet screen, Receive tab, online). Idempotent — safe each render. + wallet.setWatchReceive(ui.screen === 'wallet' && ui.tab === 'receive' && !wallet.offline); } wallet.subscribe(render); diff --git a/src/wallet.js b/src/wallet.js index ec1141e..569c0bd 100644 --- a/src/wallet.js +++ b/src/wallet.js @@ -379,6 +379,34 @@ export class Wallet { } } + // Enable/disable fast polling of the fresh receive address — set while the + // Receive tab is open so a deposit shows near-instantly even if the WS doesn't + // push it. Toggling on triggers an immediate check. + setWatchReceive(on) { + on = !!on; + if (on === !!this._watchReceive) return; // no change — avoid re-triggering + this._watchReceive = on; + if (on) { this._lastReceivePoll = 0; this.pollReceiveFrontier(); } + } + + // Cheap single-address check of the next receive address. If it has activity, + // hand off to the full refreshLive (which pulls the coin + history and advances + // the frontier); otherwise do nothing. One request per call. + async pollReceiveFrontier() { + if (this.offline || this._polling || this._refreshing) return; + const idx = this.nextReceiveIndex; + const { address } = this.derive(0, idx); + try { + const info = await this.api.addressInfo(address); + const cs = info.chain_stats || {}; + const ms = info.mempool_stats || {}; + const used = (cs.tx_count || 0) > 0 || (ms.tx_count || 0) > 0; + if (used) await this.refreshLive(); + } catch { + /* transient; next tick retries */ + } + } + // Re-check every address that currently holds a UTXO: refresh its balance from // chain/mempool stats and rebuild its UTXOs. This is what reconciles spends // (a held coin that's now gone) — confirmed or still in the mempool, regardless @@ -1162,11 +1190,20 @@ export class Wallet { // (purged, too-low-fee) txs don't keep us in the fast lane. const stuck = this._stuckTxids(); const pending = this.utxos.some((u) => !u.confirmed && !stuck.has(u.txid)); + // While the user sits on the Receive tab we poll the fresh receive address + // fast (~3s) regardless of Live — mempool's address push is unreliable, so + // this guarantees a near-instant arrival. One address, cheap. + if (this._watchReceive) { + if (Date.now() - (this._lastReceivePoll || 0) < 3000) return; + this._lastReceivePoll = Date.now(); + this.pollReceiveFrontier(); + return; + } const due = this.live && !pending ? 25000 : 6000; if (Date.now() - (this._lastPoll || 0) < due) return; this._lastPoll = Date.now(); this.refreshLive(); - }, 3000); + }, 1000); // No periodic full re-scan: the WS (and the frontier poll above) keep the // balance current, and refreshLive's _reconcileHeld catches spends of held // coins. The only thing neither covers is a deposit to a reused old address, |
