@qbit-oss/api 0.0.0-stage → 0.1.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/lib/index.d.ts ADDED
@@ -0,0 +1,551 @@
1
+ interface EsploraChainStats {
2
+ funded_txo_count: number;
3
+ funded_txo_sum: number;
4
+ spent_txo_count: number;
5
+ spent_txo_sum: number;
6
+ tx_count: number;
7
+ }
8
+ interface EsploraAddressStats {
9
+ address: string;
10
+ chain_stats: EsploraChainStats;
11
+ mempool_stats: EsploraChainStats;
12
+ }
13
+ interface EsploraTxStatus {
14
+ confirmed: boolean;
15
+ block_height?: number;
16
+ block_hash?: string;
17
+ block_time?: number;
18
+ }
19
+ interface EsploraUtxo {
20
+ txid: string;
21
+ vout: number;
22
+ value: number;
23
+ status: EsploraTxStatus;
24
+ }
25
+ interface EsploraVin {
26
+ txid: string;
27
+ vout: number;
28
+ prevout: EsploraVout | null;
29
+ scriptsig: string;
30
+ scriptsig_asm: string;
31
+ witness?: string[];
32
+ is_coinbase: boolean;
33
+ sequence: number;
34
+ }
35
+ interface EsploraVout {
36
+ scriptpubkey: string;
37
+ scriptpubkey_asm: string;
38
+ scriptpubkey_type: string;
39
+ scriptpubkey_address?: string;
40
+ value: number;
41
+ }
42
+ interface EsploraTx {
43
+ txid: string;
44
+ version: number;
45
+ locktime: number;
46
+ vin: EsploraVin[];
47
+ vout: EsploraVout[];
48
+ size: number;
49
+ weight: number;
50
+ fee: number;
51
+ status: EsploraTxStatus;
52
+ }
53
+ /** esplora returns `{ "<target_blocks>": <sat/vB> }`, e.g. `{ "1": 12.3, "6": 4.1 }`. */
54
+ type EsploraFeeEstimates = Record<string, number>;
55
+ /**
56
+ * mempool-qbit retains mempool's field names, but the first three values come
57
+ * from projected blocks 1, 2, and 3. Economy and minimum are policy floors,
58
+ * not confirmation-target estimates.
59
+ */
60
+ interface MempoolRecommendedFees {
61
+ fastestFee: number;
62
+ halfHourFee: number;
63
+ hourFee: number;
64
+ economyFee: number;
65
+ minimumFee: number;
66
+ }
67
+ interface AddressHistory {
68
+ confirmed: EsploraTx[];
69
+ mempool: EsploraTx[];
70
+ }
71
+ /**
72
+ * Per-call cancellation and time budget. Both cover every dependent request of
73
+ * the call — pagination, retries, backoff, and response-body reads.
74
+ */
75
+ interface RequestOptions {
76
+ /**
77
+ * Aborting rejects the call with an `aborted` `QbitApiError` and stops further
78
+ * requests (`unknown_outcome` from `broadcastTx` once its POST may have been sent).
79
+ */
80
+ signal?: AbortSignal;
81
+ /**
82
+ * Overall budget for the call in ms; exceeding it rejects with a `timeout`
83
+ * `QbitApiError` (`unknown_outcome` from `broadcastTx` once its POST may have been sent).
84
+ */
85
+ deadlineMs?: number;
86
+ }
87
+ interface AddressHistoryOptions extends RequestOptions {
88
+ /**
89
+ * Maximum history page fetches for the whole call, including the terminal
90
+ * empty response and a rescan (default 256). The address-stats reads are not
91
+ * page fetches, nor is `EsploraClient`'s `/txs/mempool` fetch.
92
+ */
93
+ maxPages?: number;
94
+ /**
95
+ * Maximum raw transaction rows accepted for the whole call, a rescan
96
+ * included, before failing closed (default 6,400).
97
+ */
98
+ maxTransactions?: number;
99
+ }
100
+ interface BroadcastTxOptions extends RequestOptions {
101
+ /**
102
+ * Transaction id (64 hex characters) of `rawTxHex`. Derived from the hex when
103
+ * omitted; when both are available they must agree.
104
+ */
105
+ txid?: string;
106
+ }
107
+ type QbitApiErrorKind = 'rate_limit' | 'payload_too_large' | 'not_found' | 'http' | 'network' | 'timeout' | 'aborted' | 'parse' | 'pagination_limit' | 'invalid_cursor' | 'unknown_outcome';
108
+ interface QbitApiErrorOptions {
109
+ status?: number;
110
+ url?: string;
111
+ cause?: unknown;
112
+ /**
113
+ * The underlying error before redaction. It is kept off the error object:
114
+ * read it with `QbitApiError.unredactedCause(error)`.
115
+ */
116
+ unredactedCause?: unknown;
117
+ retryAfterMs?: number;
118
+ /**
119
+ * Trimmed response body preview, capped at 200 characters. Errors the client
120
+ * creates redact the base URL from it as they do in `message`.
121
+ */
122
+ responseBody?: string;
123
+ /** Transaction id of the submission an `unknown_outcome` error refers to, when known. */
124
+ txid?: string;
125
+ }
126
+ declare class QbitApiError extends Error {
127
+ readonly kind: QbitApiErrorKind;
128
+ readonly status?: number;
129
+ readonly url?: string;
130
+ readonly retryAfterMs?: number;
131
+ /**
132
+ * The underlying error. Errors the client creates attach a redacted copy,
133
+ * safe to log: see `QbitApiError.unredactedCause`.
134
+ */
135
+ readonly cause?: unknown;
136
+ readonly responseBody?: string;
137
+ readonly txid?: string;
138
+ /**
139
+ * The underlying error of `error` as the fetch implementation threw it, or
140
+ * undefined. Its message, stack and properties can contain the full request
141
+ * URL, base-URL secrets included, so it is kept off the error object, where
142
+ * no logger, error reporter or serializer can reach it. Do not log it where
143
+ * the base URL is secret.
144
+ */
145
+ static unredactedCause(error: unknown): unknown;
146
+ /**
147
+ * Whether `value` is a `QbitApiError` (or a subclass instance) from any copy
148
+ * of `@qbit-oss/api`. `instanceof` only recognizes errors from the same copy,
149
+ * so it is false when an app loads both the CommonJS and the ESM build (or
150
+ * two installed versions) and the error comes from the other one. This checks
151
+ * a brand, `Symbol.for('qbit-js.QbitApiError')`, set on the class prototype
152
+ * instead. It identifies the class, not where the error came from: any code
153
+ * can set the brand. An error from another version can carry a `kind` this
154
+ * version does not list, so keep a default branch when switching on it.
155
+ */
156
+ static is(value: unknown): value is QbitApiError;
157
+ constructor(kind: QbitApiErrorKind, message: string, options?: QbitApiErrorOptions);
158
+ }
159
+ /**
160
+ * Fetch credential policy, the values of the DOM's `RequestCredentials`.
161
+ * Declared here so the published types compile without the DOM lib, which
162
+ * `@types/node` does not provide this name from.
163
+ */
164
+ type QbitFetchCredentials = 'omit' | 'same-origin' | 'include';
165
+ interface EsploraClientOptions {
166
+ /** Base URL for the esplora endpoint, e.g. `https://blockstream.info/testnet/api`. */
167
+ baseUrl: string;
168
+ /**
169
+ * Accept a plain `http:` baseUrl on a non-loopback host (default false).
170
+ * Without TLS, request headers such as API keys and every response are
171
+ * exposed to the network. `localhost`, `127.0.0.0/8` and `[::1]` are always
172
+ * accepted.
173
+ */
174
+ allowInsecureHttp?: boolean;
175
+ /**
176
+ * Custom fetch (defaults to global fetch). It should honour
177
+ * `redirect: 'manual'`; a reply it reports as `redirected`, or whose `url`
178
+ * is not the requested URL (another scheme, host, port, path or query), is
179
+ * refused, but only after the redirected request was sent. A `QbitApiError`
180
+ * it throws, from any copy of this package (see `QbitApiError.is`),
181
+ * propagates as it is, or as the `cause` of an `unknown_outcome` from a
182
+ * submission that may have been sent. One from another copy is judged by its
183
+ * public fields only, since that copy keeps the original cause and response
184
+ * body to itself: on a submission, its `network` error never proves that
185
+ * nothing was sent, and its response body never triggers the `/tx/push`
186
+ * fallback.
187
+ */
188
+ fetchFn?: typeof fetch;
189
+ /** Fetch credential policy (default `omit`). */
190
+ credentials?: QbitFetchCredentials;
191
+ /** Per-request timeout in ms (default 15000). */
192
+ timeoutMs?: number;
193
+ /**
194
+ * Retry attempts for transient failures — network/timeout/5xx/429 on reads
195
+ * (default 2). Submissions only repeat attempts that provably never reached
196
+ * the server; see `broadcastTx`.
197
+ */
198
+ retries?: number;
199
+ /** Base backoff in ms; grows exponentially per attempt (default 300). */
200
+ retryBaseDelayMs?: number;
201
+ /**
202
+ * Longest server-requested `Retry-After` delay the client will sleep before
203
+ * retrying, in ms (default 30000, at most 2147483647). A longer request is not
204
+ * slept on: a read's 429/5xx (`rate_limit` / `http`) or a submission's 429
205
+ * (`rate_limit`) is thrown at once with `retryAfterMs` set. A submission's
206
+ * 5xx is never retried, so the cap does not apply to it; see `broadcastTx`.
207
+ */
208
+ maxRetryAfterMs?: number;
209
+ /** Extra request headers. UDID/telemetry headers are intentionally unsupported. */
210
+ headers?: Record<string, string>;
211
+ /**
212
+ * Sleep function (injectable for tests); defaults to an abortable setTimeout.
213
+ * Receives the call's signal; the client stops waiting on abort either way.
214
+ */
215
+ sleepFn?: (ms: number, signal?: AbortSignal) => Promise<void>;
216
+ /** Cancels every in-flight and future call on this client when aborted. */
217
+ signal?: AbortSignal;
218
+ }
219
+ interface MempoolQbitClientOptions extends EsploraClientOptions {
220
+ /** Maximum combined-history requests fetched per call (default 256). */
221
+ maxAddressHistoryPages?: number;
222
+ }
223
+ declare class EsploraClient {
224
+ /** As configured, without trailing slashes. It can carry secrets: errors show `errorUrl(path)`. */
225
+ protected readonly baseUrl: string;
226
+ private readonly fetchFn;
227
+ private readonly timeoutMs;
228
+ private readonly retries;
229
+ private readonly retryBaseDelayMs;
230
+ private readonly maxRetryAfterMs;
231
+ private readonly headers;
232
+ private readonly credentials;
233
+ private readonly sleepFn;
234
+ private readonly signal;
235
+ /** In-flight GET de-dup: concurrent identical GETs share one request. */
236
+ private readonly inflight;
237
+ constructor(options: EsploraClientOptions);
238
+ getAddressStats(address: string, options?: RequestOptions): Promise<EsploraAddressStats>;
239
+ getAddressUtxos(address: string, options?: RequestOptions): Promise<EsploraUtxo[]>;
240
+ /**
241
+ * Confirmed transaction history for an address, newest first, 25 per page.
242
+ * Pass the last txid of the previous page as `afterTxid` to paginate.
243
+ */
244
+ getAddressTxs(address: string, afterTxid?: string, options?: RequestOptions): Promise<EsploraTx[]>;
245
+ /**
246
+ * Unconfirmed (mempool) transactions touching an address. electrs lists at
247
+ * most `--rest-default-max-mempool-txs` (default 50) of them, with no way to
248
+ * page further; `getAddressHistory` detects that cap.
249
+ */
250
+ getAddressMempoolTxs(address: string, options?: RequestOptions): Promise<EsploraTx[]>;
251
+ /**
252
+ * Complete address history from a bounded, page-one scan. The confirmed
253
+ * chain route is exhausted explicitly; short pages are not assumed final.
254
+ *
255
+ * electrs answers a chain cursor it does not know (a lagging node behind a
256
+ * load balancer, or a reorg between pages) with HTTP 200 and `[]`, and
257
+ * `/txs/mempool` stops at 50 rows, so the scan alone cannot tell a short
258
+ * history from a complete one. The address stats are therefore read before
259
+ * and after the scan, each in a request of its own, and the history is
260
+ * returned only if both reads agree and it holds exactly
261
+ * `chain_stats.tx_count` confirmed and `mempool_stats.tx_count` mempool
262
+ * transactions. If the stats changed during the scan, the address is scanned
263
+ * once more; if they change again, or the counts differ, the call rejects
264
+ * with `invalid_cursor`. Stats without a non-negative safe-integer
265
+ * `tx_count` reject with `parse`. `maxPages` and `maxTransactions` cover
266
+ * both scans.
267
+ */
268
+ getAddressHistory(address: string, options?: AddressHistoryOptions): Promise<AddressHistory>;
269
+ private scanAddressHistory;
270
+ private scanAddressHistoryOnce;
271
+ getTx(txid: string, options?: RequestOptions): Promise<EsploraTx>;
272
+ /**
273
+ * Raw transaction hex as the backend serializes it (with witness data when
274
+ * the transaction has any), in lowercase. The reply is checked before it is
275
+ * returned: it must be non-empty, even-length hex that decodes to one
276
+ * complete transaction whose txid (the double SHA-256 of its
277
+ * witness-stripped serialization, as qbit Core computes it) is `txid`,
278
+ * compared case-insensitively. The transaction must be encoded as qbit
279
+ * Core's decoder requires: every count and length in its shortest
280
+ * CompactSize form and at most 0x02000000, and no witness flag when every
281
+ * witness stack is empty. Anything else, including the empty `200`
282
+ * reply mempool.qbit.org serves, throws a `parse` `QbitApiError`, so a
283
+ * backend cannot hand back an empty string or another transaction. Spaces,
284
+ * tabs and line breaks around the hex are ignored. The txid does not commit
285
+ * to witness data, so a reply with altered or stripped witnesses still
286
+ * passes; compare the wtxid if you need them.
287
+ */
288
+ getTxHex(txid: string, options?: RequestOptions): Promise<string>;
289
+ getTxStatus(txid: string, options?: RequestOptions): Promise<EsploraTxStatus>;
290
+ getFeeEstimates(options?: RequestOptions): Promise<EsploraFeeEstimates>;
291
+ getTipHeight(options?: RequestOptions): Promise<number>;
292
+ getTipHash(options?: RequestOptions): Promise<string>;
293
+ /**
294
+ * Broadcast a raw transaction hex. Resolves with the txid once the node has
295
+ * the transaction, including when it reports the transaction as already in
296
+ * its mempool or chain. A submission is never re-sent after an attempt that
297
+ * may have reached the node: a timeout, an ambiguous network error, or a
298
+ * 5xx reply whose body carries no verdict from the node rejects with
299
+ * `unknown_outcome` carrying the txid so the caller can check
300
+ * `getTxStatus(txid)` before resubmitting the same hex. Only 429 replies and
301
+ * connection failures that happened before anything was sent are retried; a
302
+ * 429 whose `Retry-After` exceeds `maxRetryAfterMs` is not waited on and
303
+ * rejects at once as `rate_limit` with `retryAfterMs`, without a re-send.
304
+ * A redirect is never followed. A refused one (a 3xx, a browser opaque
305
+ * redirect, or a reply from a redirect the runtime followed) rejects with
306
+ * `unknown_outcome` too, with the `http` refusal as `cause`: the POST
307
+ * reached the backend, which may have accepted it before redirecting, and
308
+ * a runtime that followed the redirect may have re-sent it.
309
+ * Coded node rejections fail closed as `http` errors whatever the HTTP
310
+ * status, since qbit Core's own JSON-RPC server answers 500 for every RPC
311
+ * error and a backend or proxy may relay that status. When no txid
312
+ * can be derived from the hex and none is supplied, a 2xx reply resolves only
313
+ * if its body is itself a 64-hex txid; any other 2xx body is `unknown_outcome`.
314
+ * Hex `txidFromRawTx` cannot read, including encodings qbit Core refuses to
315
+ * decode, is still sent as it is, and the node gives the verdict.
316
+ * A malformed `rawTxHex` argument or an `options.txid` that is not a 64-hex
317
+ * transaction id matching the hex throws a `TypeError` before anything is sent.
318
+ *
319
+ * `options.signal` / `options.deadlineMs` bound the whole call. An abort or
320
+ * expired deadline before the POST is sent rejects with `aborted` /
321
+ * `timeout`; once the POST may have been sent (in flight or while its reply
322
+ * is read) it rejects with `unknown_outcome`, with that error as `cause`.
323
+ * A reply whose body stalls past `timeoutMs` or cannot be read is
324
+ * `unknown_outcome` too. A 429 reply is the exception throughout: it proves
325
+ * nothing was submitted, so one whose body is lost is retried (then
326
+ * `rate_limit`), and an abort or deadline during its body is definite.
327
+ */
328
+ broadcastTx(rawTxHex: string, options?: BroadcastTxOptions): Promise<string>;
329
+ /** Confirmed + mempool balance for an address, in satoshis. */
330
+ getAddressBalance(address: string, options?: RequestOptions): Promise<AddressBalance>;
331
+ /** Fee-rate tiers (sat/vB), ascending by confirmation target. */
332
+ getFeeTiers(options?: RequestOptions): Promise<FeeRateTier[]>;
333
+ /**
334
+ * Run one public call under a single signal that combines the client's
335
+ * signal, the caller's signal, and the caller's deadline. Pass `{ signal }`
336
+ * to dependent calls so they share the same cancellation and budget.
337
+ */
338
+ protected runOperation<T>(options: RequestOptions | undefined, run: (signal: AbortSignal) => Promise<T>): Promise<T>;
339
+ /**
340
+ * GET and parse JSON. With `dedup` false the request is sent on its own
341
+ * instead of joining an identical one already in flight, whose reply may
342
+ * predate requests this call has made since.
343
+ */
344
+ protected getJson<T>(path: string, options?: RequestOptions, dedup?: boolean): Promise<T>;
345
+ protected getText(path: string, options?: RequestOptions): Promise<string>;
346
+ /**
347
+ * POST a transaction submission under the submission retry policy and
348
+ * classify the node's reply. `txid` identifies the submission in
349
+ * `unknown_outcome` errors and is the result for already-known replies.
350
+ * An abort or deadline once the POST may have been sent, including while
351
+ * the reply body is read, is an `unknown_outcome` too, unless the reply is
352
+ * a 429.
353
+ */
354
+ protected submitTx(path: string, body: string, txid: string | undefined, options?: RequestOptions): Promise<string>;
355
+ protected request(path: string, init: {
356
+ method: string;
357
+ body?: string;
358
+ headers?: Record<string, string>;
359
+ dedup: boolean;
360
+ }, options?: RequestOptions): Promise<Response>;
361
+ /**
362
+ * Concurrent identical GETs share one request. One caller giving up only
363
+ * stops waiting; the shared request is aborted once every caller has left.
364
+ */
365
+ private requestShared;
366
+ /**
367
+ * Idempotent requests (GET/HEAD) retry on network errors, timeouts, and
368
+ * transient statuses. Any other method is a submission: an attempt is only
369
+ * repeated when it provably never reached the server (429, or a connection
370
+ * failure before anything was sent), and an attempt that may have been
371
+ * processed fails with `unknown_outcome` instead of being re-sent; that
372
+ * includes an abort or deadline that fires while a submission is in flight,
373
+ * and a refused redirect. `txid` labels that error when the submission is a
374
+ * transaction.
375
+ */
376
+ private requestWithRetry;
377
+ /** Backoff that rejects as soon as `signal` aborts, even if `sleepFn` ignores it. */
378
+ private sleep;
379
+ /**
380
+ * One attempt under one timeout that spans the headers and the whole body,
381
+ * also bounded by the call's signal until the body is buffered. Every
382
+ * response it returns is fully buffered, whatever its status, so no caller
383
+ * (retry loop, de-duplicated clone, or error path) can block on the network
384
+ * after the timer is cleared. A body that misses the attempt timeout fails
385
+ * the attempt as `timeout`, even for a status that would otherwise be
386
+ * retried; an abort or call deadline, before or after the headers, rejects
387
+ * with the call's `aborted` / `timeout` error. `reply.status` is set once
388
+ * the headers arrive. A redirect is refused before its body is read, which
389
+ * is cancelled instead, and `reply.refusedRedirect` is set; see
390
+ * `refuseRedirect`.
391
+ */
392
+ private fetchOnce;
393
+ /** `status` is set when the headers arrived and the body missed the deadline. */
394
+ private timeoutError;
395
+ private backoffMs;
396
+ /** The request URL for `path` as errors show it: see `redactedBaseUrl`. */
397
+ protected errorUrl(path: string): string;
398
+ }
399
+ /**
400
+ * Adapter for mempool-qbit's public Node API (`/api/v1` prefix). Most routes
401
+ * match Esplora, but address pagination and fees use mempool-style shapes.
402
+ */
403
+ declare class MempoolQbitClient extends EsploraClient {
404
+ private readonly maxAddressHistoryPages;
405
+ constructor(options: MempoolQbitClientOptions);
406
+ /**
407
+ * Complete address history from one bounded traversal of mempool-qbit's
408
+ * combined endpoint. Page size is intentionally not assumed.
409
+ *
410
+ * The result is checked against the address stats, read before and after
411
+ * the traversal, as `EsploraClient.getAddressHistory` describes, and a
412
+ * traversal during which they changed is repeated once. qbit-electrs
413
+ * answers a cursor that is the last mempool row with `[]` instead of the
414
+ * confirmed rows that follow, so an address whose mempool rows fill whole
415
+ * pages (50 each by default) rejects with `invalid_cursor` while it has
416
+ * confirmed history, rather than returning none.
417
+ */
418
+ getAddressHistory(address: string, options?: AddressHistoryOptions): Promise<AddressHistory>;
419
+ private scanCombinedAddressHistory;
420
+ private scanCombinedAddressHistoryOnce;
421
+ /**
422
+ * Confirmed transaction history for an address, newest first.
423
+ * mempool-qbit paginates combined history with `?after_txid=...` and sorts
424
+ * mempool entries first, so full mempool-only pages must still advance.
425
+ * Without `afterTxid`, an empty page right after a mempool row is checked
426
+ * against the address stats: qbit-electrs answers that cursor with `[]`
427
+ * instead of the confirmed rows that follow, so the call rejects with
428
+ * `invalid_cursor` if the stats count more confirmed transactions than it
429
+ * found.
430
+ */
431
+ getAddressTxs(address: string, afterTxid?: string, options?: RequestOptions): Promise<EsploraTx[]>;
432
+ private scanAddressTxs;
433
+ /**
434
+ * qbit-electrs's combined route answers an `after_txid` that is the last
435
+ * mempool row with `[]`, not the confirmed rows that follow: it passes the
436
+ * mempool txid on as the chain cursor, which `history()` never finds
437
+ * (`src/rest.rs:992-998` in Qbit-Org/qbit-electrs@53102e9). Reject that
438
+ * empty page when the address stats count confirmed transactions the scan
439
+ * has not seen.
440
+ */
441
+ private assertConfirmedHistoryReached;
442
+ /**
443
+ * mempool-qbit does not expose `/txs/mempool`. Page through the combined
444
+ * history until confirmed entries begin, collecting the leading mempool set.
445
+ */
446
+ getAddressMempoolTxs(address: string, options?: RequestOptions): Promise<EsploraTx[]>;
447
+ private scanAddressMempoolTxs;
448
+ getMempoolRecommendedFees(options?: RequestOptions): Promise<MempoolRecommendedFees>;
449
+ getFeeEstimates(options?: RequestOptions): Promise<EsploraFeeEstimates>;
450
+ /**
451
+ * Broadcast via `/tx`, falling back to the `/tx/push` form route only when
452
+ * `/tx` reported that it did not submit the transaction. The fallback is a
453
+ * second submission, so it follows the same policy: an already-known reply
454
+ * resolves with the txid and an uncertain attempt rejects with
455
+ * `unknown_outcome`. An uncertain `/tx` attempt never falls back.
456
+ *
457
+ * One signal and deadline cover both requests, with the same abort rules as
458
+ * `EsploraClient.broadcastTx`. An abort or expired deadline during `/tx`
459
+ * never sends the `/tx/push` fallback.
460
+ */
461
+ broadcastTx(rawTxHex: string, options?: BroadcastTxOptions): Promise<string>;
462
+ private addressHistoryPaginationLimitError;
463
+ private invalidAddressHistoryCursorError;
464
+ private nonAdvancingAddressHistoryCursorError;
465
+ }
466
+ interface AddressBalance {
467
+ address: string;
468
+ confirmedSats: number;
469
+ mempoolSats: number;
470
+ totalSats: number;
471
+ }
472
+ /**
473
+ * Confirmed, mempool and total balance in satoshis from esplora address stats.
474
+ *
475
+ * Throws a `parse` {@link QbitApiError} unless `stats` has a string `address`
476
+ * and `chain_stats` / `mempool_stats` objects whose `funded_txo_sum` and
477
+ * `spent_txo_sum` are non-negative safe integers, the confirmed balance is not
478
+ * negative, and `totalSats` is a non-negative safe integer. `mempoolSats` may
479
+ * be negative while mempool transactions spend confirmed outputs.
480
+ *
481
+ * `funded_txo_sum` and `spent_txo_sum` are lifetime totals of everything the
482
+ * address has received and spent, not balances, and `MAX_MONEY` does not
483
+ * bound them. `JSON.parse` cannot represent integers above
484
+ * `Number.MAX_SAFE_INTEGER` (2^53 - 1 sats, about 90M QBT) exactly, so it
485
+ * rounds them, and they fail closed rather than yield a plausible but wrong
486
+ * balance. A reused address that has received more than that over its life
487
+ * therefore fails on every call, however little it holds now. For such an
488
+ * address, sum the `value` of each output from
489
+ * {@link EsploraClient.getAddressUtxos} instead; that raw endpoint is not
490
+ * validated, so check that each value is a non-negative safe integer.
491
+ *
492
+ * The `parse` error for a negative total, here and from
493
+ * {@link EsploraClient.getAddressBalance}, can be transient right after a
494
+ * block on electrs: it applies the block's headers before indexing it and
495
+ * does not deduplicate mempool stats against the chain, so a spend that was
496
+ * just confirmed can count in both `chain_stats` and `mempool_stats` for a
497
+ * moment. Retry it on the next poll.
498
+ */
499
+ declare function addressBalanceFromStats(stats: EsploraAddressStats): AddressBalance;
500
+ /**
501
+ * Default `maxFeeRate` for {@link feeTiersFromEstimates}, in sat/vB. qbit Core's
502
+ * `sendrawtransaction` refuses fee rates above `DEFAULT_MAX_RAW_TX_FEE_RATE{COIN / 10}`
503
+ * per kvB by default (`src/node/transaction.h:27`), which is 10,000 sat/vB.
504
+ */
505
+ declare const DEFAULT_MAX_FEE_RATE_SAT_VB = 10000;
506
+ interface FeeRateTier {
507
+ /** Confirmation target in blocks. */
508
+ target: number;
509
+ /** Fee rate in sat/vB. */
510
+ feeRate: number;
511
+ }
512
+ /**
513
+ * Map mempool-qbit's projected blocks, leaving named policy floors unlabelled.
514
+ *
515
+ * Throws a `parse` {@link QbitApiError} unless `fees` is an object whose
516
+ * `fastestFee`, `halfHourFee` and `hourFee` are finite numbers above 0. The
517
+ * fee-rate ceiling is applied downstream by {@link feeTiersFromEstimates}.
518
+ */
519
+ declare function feeEstimatesFromMempoolRecommendedFees(fees: MempoolRecommendedFees): EsploraFeeEstimates;
520
+ /**
521
+ * Fee-rate tiers (sat/vB) from an esplora fee-estimate map, ascending by
522
+ * confirmation target. `{}` yields `[]`.
523
+ *
524
+ * Fails closed with a `parse` {@link QbitApiError} unless `estimates` is an
525
+ * object whose every key is a canonical decimal target from 1 to 1008 blocks
526
+ * and whose every value is a finite fee rate above 0 and at most `maxFeeRate`
527
+ * (default {@link DEFAULT_MAX_FEE_RATE_SAT_VB}). The default is a ceiling, not
528
+ * a sanity check: it only rejects rates above Core's default broadcast limit,
529
+ * so a hostile or broken backend can still return 9,999 sat/vB. Set a lower
530
+ * ceiling with `feeTiersFromEstimates(await client.getFeeEstimates(), { maxFeeRate })`,
531
+ * cap the total fee before signing, or both.
532
+ * Previously, malformed entries were silently dropped and no ceiling applied.
533
+ *
534
+ * @throws TypeError when `options.maxFeeRate` is not a finite number above 0.
535
+ */
536
+ declare function feeTiersFromEstimates(estimates: EsploraFeeEstimates, options?: {
537
+ maxFeeRate?: number;
538
+ }): FeeRateTier[];
539
+ /**
540
+ * Transaction id of a raw transaction hex in legacy or BIP144 (witness)
541
+ * encoding: the double SHA-256 of the witness-stripped serialization,
542
+ * displayed byte-reversed. Throws a `parse` `QbitApiError` when the hex is not
543
+ * a structurally complete transaction, or uses an encoding qbit Core refuses
544
+ * to decode even where the txid would not change: a count or length (witness
545
+ * section included) in a longer CompactSize form than its value needs or above
546
+ * 0x02000000 (Core's MAX_SIZE), or a witness flag with every witness stack
547
+ * empty.
548
+ */
549
+ declare function txidFromRawTx(rawTxHex: string): string;
550
+
551
+ export { type AddressBalance, type AddressHistory, type AddressHistoryOptions, type BroadcastTxOptions, DEFAULT_MAX_FEE_RATE_SAT_VB, type EsploraAddressStats, type EsploraChainStats, EsploraClient, type EsploraClientOptions, type EsploraFeeEstimates, type EsploraTx, type EsploraTxStatus, type EsploraUtxo, type EsploraVin, type EsploraVout, type FeeRateTier, MempoolQbitClient, type MempoolQbitClientOptions, type MempoolRecommendedFees, QbitApiError, type QbitApiErrorKind, type QbitApiErrorOptions, type QbitFetchCredentials, type RequestOptions, addressBalanceFromStats, feeEstimatesFromMempoolRecommendedFees, feeTiersFromEstimates, txidFromRawTx };