aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-06-15 12:56:36 -0700
committerAdam Soltys <[email protected]>2026-06-15 12:56:36 -0700
commit3ecb137cc4300e60f00fbb575441f34e01839a8e (patch)
tree16169e758e281bd565600ed4ec12f183941aec3c
parent769589f431725a275751bfc84a0d7e6bacee6d0d (diff)
downloadcoinosv3-3ecb137cc4300e60f00fbb575441f34e01839a8e.tar.gz
coinosv3-3ecb137cc4300e60f00fbb575441f34e01839a8e.zip
Apply WS tx payload instantly; faster reconcile
The mempool.space WebSocket push already contains the transaction, so parse it on arrival to add the incoming UTXO and bump pending immediately, instead of waiting on a debounced re-scan. The scan still follows (400ms) to reconcile spends/confirmations. Removes the ~3s our handler added on top of mempool.space's own push latency. (Measured: mempool.space itself takes ~8s to see a coinos-broadcast tx due to p2p propagation — the real fix for incoming-payment latency is the sender pushing directly to the explorer.) Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
-rw-r--r--src/wallet.js42
1 files changed, 41 insertions, 1 deletions
diff --git a/src/wallet.js b/src/wallet.js
index 2f9fed7..0d24d61 100644
--- a/src/wallet.js
+++ b/src/wallet.js
@@ -490,6 +490,8 @@ export class Wallet {
msg['block-transactions'] ||
msg['txConfirmed']
) {
+ // Update instantly from the pushed tx data, then reconcile via a scan.
+ this._ingestWs(msg);
this._scheduleRefresh();
}
};
@@ -514,6 +516,44 @@ export class Wallet {
}
}
+ // Optimistically apply transactions from a WS push so an incoming deposit
+ // shows the instant mempool.space pushes it — no extra round-trip. The
+ // debounced scan that follows reconciles everything (spends, confirmations).
+ _ingestWs(msg) {
+ const txs = [];
+ const multi = msg['multi-address-transactions'];
+ if (multi && typeof multi === 'object') {
+ for (const addr of Object.keys(multi)) {
+ const g = multi[addr] || {};
+ for (const t of g.mempool || []) txs.push(t);
+ for (const t of g.confirmed || []) txs.push(t);
+ }
+ }
+ const single = msg['address-transactions'];
+ if (Array.isArray(single)) txs.push(...single);
+
+ let changed = false;
+ const have = new Set(this.utxos.map(utxoId));
+ for (const tx of txs) {
+ if (!tx || !Array.isArray(tx.vout)) continue;
+ tx.vout.forEach((vo, i) => {
+ const a = vo && vo.scriptpubkey_address;
+ if (!a || !this.addrMap.has(a)) return;
+ const id = `${tx.txid}:${i}`;
+ if (have.has(id)) return;
+ const p = this.addrMap.get(a);
+ const confirmed = !!(tx.status && tx.status.confirmed);
+ this.utxos.push({ txid: tx.txid, vout: i, value: vo.value, address: a, chain: p.chain, index: p.index, confirmed });
+ have.add(id);
+ if (confirmed) this.confirmed += vo.value;
+ else this.pending += vo.value;
+ changed = true;
+ });
+ }
+ if (changed) this.emit();
+ return changed;
+ }
+
_scheduleReconnect() {
if (!this._wsWant) return;
clearTimeout(this._wsRetry);
@@ -528,7 +568,7 @@ export class Wallet {
await this.scan({ silent: true });
this.retrack();
} catch {}
- }, 800);
+ }, 400);
}
stopRealtime() {