diff options
| author | Adam Soltys <[email protected]> | 2026-06-24 23:23:50 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-06-24 23:23:50 -0700 |
| commit | 7af3c430d49d7817e9a0cf28ab49143edba4c00f (patch) | |
| tree | 33843b3dde033b0506db84d689906967ca9812b2 /src/wallet.js | |
| parent | 4d1a9aea0f6b234dfe8eb36d398002ccea6a5680 (diff) | |
| download | coinosv3-7af3c430d49d7817e9a0cf28ab49143edba4c00f.tar.gz coinosv3-7af3c430d49d7817e9a0cf28ab49143edba4c00f.zip | |
Make pending receives fast: cut REST load, surface live/polling state
Incoming deposits show instantly when the mempool.space WebSocket is healthy
(_ingestWs updates immediately). The lag came from the socket being starved:
a 20s frontier poll plus a full wallet re-scan every 120s generated enough
REST volume to trip mempool's per-IP rate limit (the 429/CORS errors), which
in turn degraded the socket — a feedback loop.
- While the socket is live, skip the periodic full scan entirely (the WS pushes
confirmations and block events) and poll the frontier only every ~25s as a
half-open-socket safety net — near-zero recurring REST.
- While the socket is down, poll the frontier every ~6s (was 20s) so receives
still show quickly, and keep the 120s reconcile scan.
- Refresh the frontier on WS (re)connect to catch deposits that landed while
disconnected.
- Surface a Live/Polling badge on the balance card so a stuck socket is visible.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Diffstat (limited to 'src/wallet.js')
| -rw-r--r-- | src/wallet.js | 31 |
1 files changed, 24 insertions, 7 deletions
diff --git a/src/wallet.js b/src/wallet.js index af827f3..f52efdf 100644 --- a/src/wallet.js +++ b/src/wallet.js @@ -92,6 +92,7 @@ export class Wallet { this._pollTimer = null; this._deepTimer = null; // periodic full-scan safety net this._polling = false; // a light poll is in flight + this._lastPoll = 0; // last frontier poll (throttles the adaptive poll timer) this._hbTimer = null; // heartbeat / liveness watchdog this._lastMsg = 0; // time of last WS message (incl. pong) this._wakeHooked = false; @@ -1066,13 +1067,26 @@ export class Wallet { startRealtime() { if (this.offline) return; this.stopRealtime(); - // 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 — the deep scan below covers spends/confirmations. - this._pollTimer = setInterval(() => this.refreshLive(), 20000); - // Periodic full scan: reconciles everything refreshLive can't see on its own - // — pending→confirmed history, spends, and stale cache/relay state. - this._deepTimer = setInterval(() => this.scan({ silent: true }).catch(() => {}), 120000); + // Frontier poll (next receive/change address only). While the socket is live + // it pushes deposits instantly, so we poll just as a slow half-open-socket + // safety net (~25s). When the socket is down, poll fast (~6s) so receives + // still show quickly. The 3s tick is a cheap time check — it issues no + // request unless one is due, which keeps REST volume (and 429s) low. + this._pollTimer = setInterval(() => { + if (this.offline) return; + const due = this.live ? 25000 : 6000; + if (Date.now() - (this._lastPoll || 0) < due) return; + this._lastPoll = Date.now(); + this.refreshLive(); + }, 3000); + // Periodic full scan: reconciles pending→confirmed history, spends, and + // stale cache/relay state — but only when the socket is down. While live the + // WS already pushes confirmations and block events, so a full re-scan would + // just burn the rate limit (and risk starving the very socket we rely on). + this._deepTimer = setInterval(() => { + if (this.live) return; + this.scan({ silent: true }).catch(() => {}); + }, 120000); // Only mempool.space has a live socket; other explorers rely on the poll // and deep scan above (wsUrl returns null → no socket). if (typeof WebSocket === 'undefined' || !this.wsUrl()) return; @@ -1136,6 +1150,9 @@ export class Wallet { this._lastMsg = Date.now(); this.retrack(); this.emit(); + // Catch any deposit that landed while we were disconnected — mempool only + // pushes new activity after tracking starts, not what's already there. + this.refreshLive().catch(() => {}); }; ws.onmessage = (ev) => { this._lastMsg = Date.now(); |
