From 3dd7f8425ecf339332cc44b6f9734b51472d22c1 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Mon, 15 Jun 2026 13:17:26 -0700 Subject: Broadcast sends to all explorers in parallel Post the signed tx to both mempool.space and Blockstream at once (outside the throttle, since a send is urgent) so it's visible regardless of which explorer the recipient watches. Succeeds if any accept; only fails if all do. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/api.js | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/api.js b/src/api.js index 8ceb870..1c70640 100644 --- a/src/api.js +++ b/src/api.js @@ -147,22 +147,22 @@ export class Api { return { fastestFee: 20, halfHourFee: 10, hourFee: 5, economyFee: 2, minimumFee: 1 }; } + // Broadcast to ALL explorers in parallel (not via the throttle — a send is + // urgent) so the tx is visible regardless of which explorer the recipient + // watches. Resolves with the txid if any accept it; rejects only if all fail. async broadcast(hexTx) { - let lastErr; - for (const base of this._hosts) { - try { - const res = await this.#run(base + '/tx', { method: 'POST', body: hexTx }); - const body = await res.text(); - if (!res.ok) { - lastErr = new Error(body || `${res.status} broadcast failed`); - continue; - } - return body.trim(); // txid - } catch (e) { - lastErr = e; - } - } - throw lastErr; + if (this.offline) throw new Error('offline'); + const attempts = this._hosts.map(async (base) => { + const res = await fetch(base + '/tx', { method: 'POST', body: hexTx }); + const body = await res.text(); + if (res.status === 429) this.#penalize(); + if (!res.ok) throw new Error(body || `${res.status} broadcast failed`); + return body.trim(); // txid + }); + const results = await Promise.allSettled(attempts); + const ok = results.find((r) => r.status === 'fulfilled'); + if (ok) return ok.value; + throw (results.find((r) => r.status === 'rejected') || {}).reason || new Error('broadcast failed'); } } -- cgit v1.2.3