UNPKG

@imikailoby/sats

Version:

Tiny non-custodial Bitcoin SDK (TS) — keys, addresses, PSBT, provider chain

55 lines (54 loc) 2.05 kB
import { withTimeout } from "../utils/http"; import { ProviderError, TimeoutError } from "../core/errors"; /** * Create a provider that tries the given providers in order with simple backoff and timeout. * Backoff schedule: 0ms, 250ms, 500ms. */ export function createProviderChain(...providersOrOptions) { // Options can be passed as the last argument let timeoutMs = 4000; let backoffMs = [0, 250, 500]; const providers = []; providersOrOptions.forEach((item, idx) => { if (typeof item.getAddressUtxos === "function") { providers.push(item); } else if (idx === providersOrOptions.length - 1) { const opts = item; if (typeof opts.timeoutMs === "number") timeoutMs = opts.timeoutMs; if (Array.isArray(opts.backoffMs) && opts.backoffMs.length > 0) backoffMs = opts.backoffMs; } }); async function tryAll(fn) { let lastErr; for (let i = 0; i < providers.length; i++) { const provider = providers[i]; const delay = backoffMs[Math.min(i, backoffMs.length - 1)] ?? 0; if (delay > 0) await new Promise((r) => { const t = setTimeout(r, delay); t?.unref?.(); }); try { return await withTimeout(fn(provider), timeoutMs); } catch (e) { lastErr = e instanceof TimeoutError ? e : new ProviderError(`provider[${i}] failed`, e); } } throw lastErr ?? new ProviderError("no providers available"); } return { getAddressUtxos(addr) { return tryAll((p) => p.getAddressUtxos(addr)); }, getAddressBalance(addr) { return tryAll((p) => p.getAddressBalance(addr)); }, broadcast(rawHex) { return tryAll((p) => (p.broadcast ? p.broadcast(rawHex) : Promise.reject(new ProviderError("broadcast not supported")))); }, }; }