@scure/btc-signer 2.2.0 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/net.d.ts ADDED
@@ -0,0 +1,355 @@
1
+ import type * as psbt from './psbt.ts';
2
+ import { type BTC_NETWORK } from './utils.ts';
3
+ /** Subclass for all EsploraProvider related errors */
4
+ export declare class EsploraError extends Error {
5
+ readonly status?: number;
6
+ readonly path?: string;
7
+ constructor(message: string, opts?: {
8
+ status?: number;
9
+ path?: string;
10
+ });
11
+ }
12
+ type FetchOpts = {
13
+ method?: string;
14
+ headers?: Record<string, string>;
15
+ body?: string;
16
+ signal?: AbortSignal;
17
+ };
18
+ type FetchResponse = {
19
+ ok: boolean;
20
+ status: number;
21
+ statusText?: string;
22
+ json(): Promise<unknown>;
23
+ text(): Promise<string>;
24
+ };
25
+ /** Minimal fetch-compatible transport accepted by {@link EsploraProvider}. */
26
+ export type FetchFn = (url: string, opts?: FetchOpts) => Promise<FetchResponse>;
27
+ /** Confirmation metadata from Esplora-compatible transaction responses. */
28
+ export type TxStatus = {
29
+ /** Whether the transaction is confirmed in a block. */
30
+ confirmed: boolean;
31
+ /** Confirming block height, absent for mempool transactions. */
32
+ block?: number;
33
+ /** Confirming block hash, absent for mempool transactions. */
34
+ blockHash?: string;
35
+ /** Confirming block timestamp in milliseconds, absent for mempool transactions. */
36
+ timestamp?: number;
37
+ };
38
+ /** Transaction output as reported by Esplora-compatible APIs. */
39
+ export type TxOutput = {
40
+ /** Hex-encoded scriptPubKey. */
41
+ scriptPubKey: string;
42
+ /** Decoded address when the script has a known address form. */
43
+ scriptPubKeyAddress?: string;
44
+ /** Output value in satoshis. */
45
+ value: bigint;
46
+ };
47
+ /** Transaction input as reported by Esplora-compatible APIs. */
48
+ export type TxInput = {
49
+ /** Previous transaction id for non-coinbase inputs. */
50
+ txid?: string;
51
+ /** Previous output index for non-coinbase inputs. */
52
+ index?: number;
53
+ /** Previous output details when supplied by the backend. */
54
+ prevout?: TxOutput;
55
+ /** Input sequence number. */
56
+ sequence?: number;
57
+ /** Whether this input is a coinbase input. */
58
+ isCoinbase?: boolean;
59
+ };
60
+ /** Full Bitcoin transaction metadata returned by {@link EsploraProvider.txInfo}. */
61
+ export type TxInfo = {
62
+ /** Transaction id. */
63
+ txid: string;
64
+ /** Transaction version. */
65
+ version: number;
66
+ /** Transaction locktime. */
67
+ lockTime: number;
68
+ /** Serialized transaction size in bytes. */
69
+ size: number;
70
+ /** Transaction weight units. */
71
+ weight: number;
72
+ /** Fee paid by the transaction in satoshis. */
73
+ fee: bigint;
74
+ /** Transaction inputs. */
75
+ inputs: TxInput[];
76
+ /** Transaction outputs. */
77
+ outputs: TxOutput[];
78
+ /** Confirmation status. */
79
+ status: TxStatus;
80
+ /** Hex-encoded raw transaction. */
81
+ raw: string;
82
+ };
83
+ /** Block metadata returned by Esplora-compatible APIs. */
84
+ export type BlockInfo = {
85
+ /** Block hash. */
86
+ hash: string;
87
+ /** Block number / height. */
88
+ number: number;
89
+ /** Block version. */
90
+ version: number;
91
+ /** Block timestamp in milliseconds. */
92
+ timestamp: number;
93
+ /** Serialized block size in bytes. */
94
+ size: number;
95
+ /** Block weight units. */
96
+ weight: number;
97
+ /** Merkle root hash. */
98
+ merkleRoot: string;
99
+ /** Previous block hash, absent for genesis. */
100
+ parentHash?: string;
101
+ /** Median block timestamp in milliseconds when the backend returns it. */
102
+ medianTime?: number;
103
+ /** Block nonce. */
104
+ nonce?: number;
105
+ /** Compact difficulty bits. */
106
+ bits?: number;
107
+ /** Floating-point network difficulty. */
108
+ difficulty?: number;
109
+ /** Transaction hashes in block order. */
110
+ transactions: string[];
111
+ };
112
+ /** Value movement entry derived from Bitcoin transaction inputs or outputs. */
113
+ export type Transfer = {
114
+ /** Source address for input-side movements, absent when the backend cannot decode one. */
115
+ from?: string;
116
+ /** Destination address for output-side movements, absent for scripts without address form. */
117
+ to?: string;
118
+ /** Value in satoshis. */
119
+ value: bigint;
120
+ };
121
+ /** Address-history transaction with transfer records and compact transaction metadata. */
122
+ export type TxTransfers = {
123
+ /** Transaction id. */
124
+ txid: string;
125
+ /** Confirming block timestamp in milliseconds, absent for mempool transactions. */
126
+ timestamp?: number;
127
+ /** Confirming block height, absent for mempool transactions. */
128
+ block?: number;
129
+ /** Input and output value movements for the whole transaction. */
130
+ transfers: Transfer[];
131
+ /** Compact transaction metadata useful for wallet history. */
132
+ info: {
133
+ /** Transaction version. */
134
+ version: number;
135
+ /** Transaction locktime. */
136
+ lockTime: number;
137
+ /** Serialized transaction size in bytes. */
138
+ size: number;
139
+ /** Transaction weight units. */
140
+ weight: number;
141
+ /** Transaction fee in satoshis. */
142
+ fee: bigint;
143
+ /** Confirming block hash, absent for mempool transactions. */
144
+ blockHash?: string;
145
+ /** Hex-encoded raw transaction. */
146
+ raw: string;
147
+ };
148
+ };
149
+ /** Merged multi-address history row from {@link EsploraProvider.historyMulti}. */
150
+ export type MultiTxTransfers = TxTransfers & {
151
+ /** Watched addresses participating in this transaction's transfers. */
152
+ addresses: string[];
153
+ };
154
+ /** UTXO set for an address in a shape accepted by transaction builders. */
155
+ export type Unspent = {
156
+ /** Asset symbol. */
157
+ symbol: 'BTC';
158
+ /** Decimal precision for BTC amounts. */
159
+ decimals: number;
160
+ /** Sum of returned spendable outputs in satoshis. */
161
+ balance: bigint;
162
+ /** Input updates that can be passed to `Transaction.addInput` or `selectUTXO`. */
163
+ utxo: psbt.TransactionInputUpdate[];
164
+ };
165
+ /** Lightweight address balance from Esplora stats, without enumerating UTXOs. */
166
+ export type Balance = {
167
+ /** Asset symbol. */
168
+ symbol: 'BTC';
169
+ /** Decimal precision for BTC amounts. */
170
+ decimals: number;
171
+ /** Current address balance in satoshis. */
172
+ balance: bigint;
173
+ /** Confirmed plus mempool transaction count. */
174
+ txCount: number;
175
+ };
176
+ /** Scan progress reported by {@link EsploraProvider.history} while metadata pages arrive. */
177
+ export type ScanProgress = {
178
+ /** Transactions scanned so far, before block-range and limit filters. */
179
+ scannedTxs: number;
180
+ /** Confirmed plus mempool transaction count for the address, from address stats. */
181
+ totalTxs: number;
182
+ /** Share of the address history scanned so far, 0-100. */
183
+ percent: number;
184
+ /** Height of the most recently scanned confirmed transaction, absent for mempool rows. */
185
+ currentBlock?: number;
186
+ };
187
+ /** Address-history pagination and filtering options. */
188
+ export type TransfersOpts = {
189
+ /** Inclusive lower block bound. */
190
+ fromBlock?: number;
191
+ /** Inclusive upper block bound. */
192
+ toBlock?: number;
193
+ /** Maximum number of matching transactions to return. */
194
+ limit?: number;
195
+ /** Return transactions older than this address-history cursor txid. */
196
+ afterTxid?: string;
197
+ /** Aborts the scan; checked between requests and passed to the transport. */
198
+ signal?: AbortSignal;
199
+ /** Progress listener; costs one extra address-stats request to size the scan. */
200
+ onProgress?: (progress: ScanProgress) => void;
201
+ /** Maximum concurrent raw-transaction fetches (default 8). */
202
+ concurrency?: number;
203
+ };
204
+ /** {@link EsploraProvider.history} options: transfers filters plus yield direction. */
205
+ export type HistoryOpts = TransfersOpts & {
206
+ /**
207
+ * Yield direction. `newest` (default) streams rows while pages arrive, so
208
+ * stopping early also stops fetching. `oldest` (the transfers() order) must
209
+ * buffer transaction metadata first, since Esplora only pages newest-first.
210
+ */
211
+ order?: 'newest' | 'oldest';
212
+ };
213
+ /** {@link EsploraProvider.unspent} scan options. */
214
+ export type UnspentOpts = {
215
+ /** Aborts the scan; checked between requests and passed to the transport. */
216
+ signal?: AbortSignal;
217
+ /** Maximum concurrent raw-transaction fetches (default 8). */
218
+ concurrency?: number;
219
+ };
220
+ /** {@link EsploraProvider.waitForTx} options. */
221
+ export type WaitTxOpts = {
222
+ /** Blocks on top of the inclusion block, default 1 (just included). */
223
+ confirmations?: number;
224
+ /** Delay between status polls in milliseconds, default 5000. */
225
+ pollIntervalMs?: number;
226
+ /** Give up (reject) after this long; default is to wait forever. */
227
+ timeoutMs?: number;
228
+ /** Aborts the wait; checked between polls and passed to the transport. */
229
+ signal?: AbortSignal;
230
+ };
231
+ /** Running balance snapshot attached by {@link calcTransfersDiff}. */
232
+ export type Balances = {
233
+ /** Running satoshi balance by address after this transaction. */
234
+ balances: Record<string, bigint>;
235
+ };
236
+ /**
237
+ * Esplora-compatible Bitcoin HTTP provider.
238
+ *
239
+ * Runtime transport is caller-provided `fetch`. The repository `test/proxy.ts`
240
+ * bridge is test/dev tooling for serving the wallet/history HTTP subset from Electrum TCP.
241
+ * Transient backend failures (429/5xx, dropped connections) are retried with
242
+ * exponential backoff on GET requests; long-running scans accept `AbortSignal`.
243
+ * @param fetch - Fetch-compatible HTTP transport.
244
+ * @param url - Base URL of an Esplora-compatible HTTP API.
245
+ * @param network - Bitcoin address network parameters.
246
+ * @example
247
+ * Create a provider with a caller-owned transport.
248
+ * ```ts
249
+ * import { EsploraProvider } from '@scure/btc-signer/net.js';
250
+ * const httpFetch = async () => ({
251
+ * ok: true,
252
+ * status: 200,
253
+ * text: async () => '1',
254
+ * json: async () => ({ '2': 1 }),
255
+ * });
256
+ * const net = new EsploraProvider(httpFetch, 'http://127.0.0.1:3000');
257
+ * await net.height();
258
+ * ```
259
+ */
260
+ export declare class EsploraProvider {
261
+ private fetch;
262
+ private url;
263
+ private address;
264
+ constructor(fetch: FetchFn, url: string, network?: BTC_NETWORK);
265
+ private request;
266
+ private requestBody;
267
+ private getJson;
268
+ private getText;
269
+ private addressPath;
270
+ private canonicalAddress;
271
+ private txHex;
272
+ /**
273
+ * Fetches raw tx hex and verifies it is actually the transaction the txid
274
+ * names, otherwise balances would be computed from whatever transaction the
275
+ * backend chose to serve. `memo` dedupes fetches within one scan: a tx
276
+ * shared by several watched addresses must cost one request, not one per
277
+ * address stream.
278
+ */
279
+ private fetchRawTx;
280
+ /**
281
+ * Pages through Esplora address history newest-first, one transaction at a
282
+ * time. The single owner of cursor pagination: the mempool-aware afterTxid
283
+ * jump, the full-page heuristic and the cursor-loop guard live here.
284
+ */
285
+ private addressTxs;
286
+ private historyInner;
287
+ private historyMultiInner;
288
+ height(opts?: {
289
+ signal?: AbortSignal;
290
+ }): Promise<number>;
291
+ blockInfo(block: number): Promise<BlockInfo>;
292
+ fee(target?: number): Promise<bigint>;
293
+ /** Lightweight method to receive the "unspent" amount without getting the full UTXO list. */
294
+ balance(address: string, opts?: {
295
+ signal?: AbortSignal;
296
+ }): Promise<Balance>;
297
+ txCount(address: string, opts?: {
298
+ signal?: AbortSignal;
299
+ }): Promise<number>;
300
+ sendTx(tx: string): Promise<string>;
301
+ /**
302
+ * Polls transaction status until it confirms (plus optional extra
303
+ * confirmations). A just-broadcast transaction may briefly be unknown to the
304
+ * backend, so 404 responses keep polling instead of failing.
305
+ */
306
+ waitForTx(txid: string, opts?: WaitTxOpts): Promise<TxStatus>;
307
+ txInfo(txid: string): Promise<TxInfo>;
308
+ unspent(address: string, opts?: UnspentOpts): Promise<Unspent>;
309
+ /**
310
+ * Streaming address history. Yields the same {@link TxTransfers} rows as
311
+ * {@link EsploraProvider.transfers}, one at a time, so callers can render or
312
+ * persist rows without waiting for the whole scan.
313
+ *
314
+ * `order: 'newest'` (default) follows Esplora pagination from the mempool
315
+ * backward and streams genuinely: stopping early (break / `limit`) also
316
+ * stops fetching. `order: 'oldest'` yields in transfers() order and must
317
+ * buffer transaction metadata first, since Esplora only pages newest-first;
318
+ * raw transactions still stream in bounded batches.
319
+ * @example
320
+ * ```ts
321
+ * for await (const tx of net.history(address, { limit: 10 })) console.log(tx.txid);
322
+ * ```
323
+ */
324
+ history(address: string, opts?: HistoryOpts): AsyncGenerator<TxTransfers, void>;
325
+ /**
326
+ * Merged history across several addresses (HD wallets, watch lists): one
327
+ * txid-deduplicated stream in `order`, k-way merged from per-address
328
+ * {@link EsploraProvider.history} streams. A transaction moving funds
329
+ * between two watched addresses appears once; its `addresses` field lists
330
+ * the watched participants. All options apply to each underlying stream, so
331
+ * `limit` caps rows per address, not the merged total; `afterTxid` is
332
+ * rejected because a chain cursor only exists in one address's history.
333
+ * Same-block rows from different addresses have no canonical order.
334
+ */
335
+ historyMulti(addresses: string[], opts?: HistoryOpts): AsyncGenerator<MultiTxTransfers, void>;
336
+ /**
337
+ * Address history as chronological transfer rows, oldest first. Buffered
338
+ * variant of {@link EsploraProvider.history}; use that to stream rows.
339
+ */
340
+ transfers(address: string, opts?: TransfersOpts): Promise<TxTransfers[]>;
341
+ }
342
+ /**
343
+ * Calculates balances at specific point in time after tx.
344
+ * Info from multiple addresses can be merged when transactions are already sorted.
345
+ * @param transfers - Transaction transfer records.
346
+ * @returns Same transfer records with running balance snapshots attached.
347
+ * @example
348
+ * Fold an address history into running balances.
349
+ * ```ts
350
+ * import { calcTransfersDiff } from '@scure/btc-signer/net.js';
351
+ * calcTransfersDiff([]);
352
+ * ```
353
+ */
354
+ export declare function calcTransfersDiff(transfers: TxTransfers[]): (TxTransfers & Balances)[];
355
+ export {};