@ninsei-labs/sdk 0.52.0 → 0.54.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/src/lock.mjs CHANGED
@@ -99,12 +99,10 @@ export async function send(request, wallet, deps = {}) {
99
99
  send: (tx) => wallet.send(tx),
100
100
  // NORMALISING THE RECEIPT ARGUMENT'S SHAPE - HERE, AT THE CORE'S BOUNDARY, NOT AT EVERY CALLER.
101
101
  //
102
- // www/js/evm/funding.js calls the receipt with an OBJECT { hash } (waitReceipt), while the adapter contract
103
- // declares receipt(txHash: string). A strict adapter (sdk/src/adapters/evm-wallet.mjs) answers bad-input to
104
- // an object, the refusal is SWALLOWED by the receipt wait, and the escrow address is silently replaced with
105
- // the PREDICTED one - that is, the "address from the receipt" check stops working without saying so. We
106
- // normalise the argument to a string here: funding.js is foreign code and must not be edited, and every
107
- // adapter should not repeat the normalisation.
102
+ // funding.js calls the receipt with an OBJECT { hash } (waitReceipt), while the adapter contract declares
103
+ // receipt(txHash: string). A strict adapter (sdk/src/adapters/evm-wallet.mjs) answers bad-input to an object,
104
+ // the refusal is SWALLOWED by the receipt wait, and the address would silently fall back. We normalise the
105
+ // argument to a string at this boundary so every adapter sees one shape.
108
106
  receipt: (arg) => wallet.receipt(typeof arg === "string" ? arg : (arg && typeof arg.hash === "string" ? arg.hash : arg)),
109
107
  });
110
108
  } catch (error) {
package/src/order.mjs CHANGED
@@ -1,15 +1,18 @@
1
1
  // THE COUNTERPARTY'S SIDE AND THE JOINT ADDRESS.
2
2
  //
3
- // TWO CHECKS, BOTH MANDATORY BEFORE SIGNING - after it there is nothing to fix:
3
+ // THREE CHECKS, ALL MANDATORY BEFORE SIGNING - after it there is nothing to fix:
4
4
  //
5
5
  // 1) the DLEQ proof stands in the CONTEXT OF THIS order: the counterparty proved for our terms, not for others.
6
6
  // The context is the same one the quote signature is bound to (see quotes.firm);
7
7
  // 2) the proven ed25519 point is THE VERY ONE that will go into the contract and from which the address is
8
8
  // assembled. Without this check the counterparty could prove one thing and substitute another: on our side
9
9
  // everything would look sound, and the discrepancy would surface when claiming the XMR, with nothing left to
10
- // fix.
10
+ // fix;
11
+ // 3) the proven secp256k1 point matches `commitHalfClaimer`, the commitment the contract compares on claim and
12
+ // refund. A proof without this comparison binds the wrong half and the escrow rejects it after the other side
13
+ // has paid.
11
14
  //
12
- // Both are done by the engine (order.js: the verifying code is the same for us and for the counterparty). The
15
+ // All three are done by the engine (order.js: the verifying code is the same for us and for the counterparty). The
13
16
  // core has no cryptography of its own and should not: a copy would silently drift from the original.
14
17
  import { SdkError } from "./errors.mjs";
15
18
  import { primitives } from "./primitives.mjs";
@@ -86,6 +89,43 @@ const sameFormAsPoint = (proof) => (proof && typeof proof === "object" && proof.
86
89
  // "no point", not "point in another form".
87
90
  const pointForm = (v) => (typeof v === "string" && v ? norm(v) : v);
88
91
 
92
+ // THE COUNTERPARTY'S VIEW MATERIAL HAS TWO SPELLINGS, AND THEY MUST AGREE. A quote carries `viewHalfClaimer`
93
+ // (and sometimes `viewHalf`), a point as `viewPoint` or `viewPub`. The guard and the builder used to prefer
94
+ // different names: the guard took the half first and ignored the point, the builder took the point first. A
95
+ // counterparty could then supply a half and a different point, the address was built from one and the recovery
96
+ // words from the other. Normalise once, refuse conflicting aliases, and derive the point from the half when only
97
+ // the half is present.
98
+ export function normalizeCounterpartyView(counterparty) {
99
+ const cp = counterparty && typeof counterparty === "object" ? counterparty : {};
100
+ const halves = [cp.viewHalf, cp.viewHalfClaimer].filter((v) => v !== undefined && v !== null && v !== "");
101
+ let half = null;
102
+ if (halves.length) {
103
+ try { half = BigInt(String(halves[0])); } catch { throw new SdkError("bad-input", { field: "counterparty", why: "view-half" }); }
104
+ for (const other of halves.slice(1)) {
105
+ let n = null;
106
+ try { n = BigInt(String(other)); } catch { n = null; }
107
+ if (n === null || n !== half) throw new SdkError("bad-input", { field: "counterparty", why: "conflicting-view-half" });
108
+ }
109
+ }
110
+ const points = [cp.viewPoint, cp.viewPub].filter((v) => v !== undefined && v !== null && v !== "");
111
+ let point = null;
112
+ if (points.length) {
113
+ point = pointForm(points[0]);
114
+ if (!isPoint(point)) throw new SdkError("bad-input", { field: "counterparty", why: "view-point" });
115
+ for (const other of points.slice(1)) {
116
+ if (pointForm(other) !== point) throw new SdkError("bad-input", { field: "counterparty", why: "conflicting-view-point" });
117
+ }
118
+ }
119
+ if (half !== null) {
120
+ let derived = null;
121
+ try { derived = pointForm(builder().pointOf(half)); } catch { derived = null; }
122
+ if (!derived) throw new SdkError("bad-input", { field: "counterparty", why: "view-half" });
123
+ if (point !== null && point !== derived) throw new SdkError("bad-input", { field: "counterparty", why: "view-half-point-mismatch" });
124
+ point = derived;
125
+ }
126
+ return { half: half === null ? null : halves[0], point };
127
+ }
128
+
89
129
  /**
90
130
  * Our side of the order. It is built by the engine's builder: two halves, their points, the DLEQ PROOF (it is
91
131
  * bound to the order terms and so is not computed without the context) and the side's encryption key.
@@ -136,11 +176,12 @@ export function acceptCounterparty({ context, side = null, counterparty = null,
136
176
  // (rfq/orderQuote.mjs). While only `edPub`/`viewPub` were accepted, the facade's own side was NOT accepted:
137
177
  // start answered bad-input { field: "counterparty", missing: ["edPub"] } to its own answer. The engine knew
138
178
  // this mapping (sdk/src/swap-flow.mjs: edPub: quote.edPointClaimer). We accept BOTH names.
179
+ const view = normalizeCounterpartyView(counterparty);
139
180
  accepted = {
140
181
  proof: sameFormAsPoint(counterparty.proof),
141
182
  edPub: pointForm(counterparty.edPub || counterparty.edPoint || counterparty.edPointClaimer || pub.ed || null),
142
- viewPoint: pointForm(counterparty.viewPoint || counterparty.viewPub || null),
143
- viewHalf: counterparty.viewHalf || counterparty.viewHalfClaimer || null,
183
+ viewPoint: view.point,
184
+ viewHalf: view.half,
144
185
  // THE COUNTERPARTY'S SPEND HALF: HIS commitment (commitHalfClaimer) is computed from it where it did not
145
186
  // arrive ready-made. Without the half the commitment cannot be obtained - and that is named null, not invented.
146
187
  half: counterparty.half !== undefined ? counterparty.half : null,
@@ -155,13 +196,18 @@ export function acceptCounterparty({ context, side = null, counterparty = null,
155
196
  // not what the page runs on.
156
197
  const mock = builder().newSide(context, true);
157
198
  accepted = { proof: sameFormAsPoint(mock.proof), edPub: norm(mock.pub.ed), viewPoint: norm(mock.viewPub),
158
- viewHalf: mock.viewHalf, half: mock.half, commitHalfClaimer: null, sealed: null, providerId: null, standIn: true, mock };
199
+ viewHalf: mock.viewHalf, half: mock.half,
200
+ // THE STAND-IN DOES CARRY A COMMITMENT: it is derived from the half it just built, by the same formula
201
+ // the contract uses. The proof check below is therefore the real one, not a path that skips it.
202
+ commitHalfClaimer: norm(builder().halfCommitment(mock.half)), sealed: null, providerId: null, standIn: true, mock };
159
203
  } else {
160
204
  throw new SdkError("bad-input", { field: "counterparty" });
161
205
  }
162
206
  const missing = [];
163
207
  if (!accepted.proof) missing.push("proof");
164
208
  if (!accepted.edPub) missing.push("edPub");
209
+ // THE COMMITMENT IS NOT OPTIONAL. Without it the proof says nothing about the half the contract will compare.
210
+ if (!accepted.commitHalfClaimer) missing.push("commitHalfClaimer");
165
211
  if (missing.length) throw new SdkError("bad-input", { field: "counterparty", missing });
166
212
  // OPENING THE SEALED PACKAGE - BY THE SAME ENGINE CALL, and the key is OURS (the package is addressed to us).
167
213
  // The envelope's composition is locked separately (www/js/atomic/halfenc.js, HALFENC_LOCK_REASON): in the live
@@ -169,7 +215,7 @@ export function acceptCounterparty({ context, side = null, counterparty = null,
169
215
  // with a refusal rather than passing itself off as a working key handover.
170
216
  const ownEncPriv = accepted.sealed && side && side.enc ? side.enc.priv : null;
171
217
  let verdict = null;
172
- try { verdict = builder().verifySide({ proof: accepted.proof, pub: { ed: accepted.edPub } }, context, accepted.sealed, ownEncPriv); }
218
+ try { verdict = builder().verifySide({ proof: accepted.proof, pub: { ed: accepted.edPub }, commitHalfClaimer: accepted.commitHalfClaimer }, context, accepted.sealed, ownEncPriv); }
173
219
  catch { throw new SdkError("bad-input", { field: "counterparty" }); }
174
220
  if (!verdict || verdict.ok !== true) {
175
221
  throw new SdkError("quote-refused", { step: "proof", why: verdict && verdict.reason ? "rejected" : "unknown" });
@@ -21,10 +21,23 @@ export interface ChainUniswap {
21
21
  [field: string]: unknown;
22
22
  }
23
23
 
24
+ /** What a settlement side looks like: address format, account/UTXO settlement and reference shape. Metadata, not a
25
+ * claim that a driver exists. */
26
+ export interface SettlementProfile {
27
+ vm: string;
28
+ addressFormat: string;
29
+ settlementKind: "contract" | "program" | "utxo-script" | string;
30
+ refKind: "address" | "pda" | "script" | string;
31
+ txRef: "hash" | "signature" | "txid" | string;
32
+ }
33
+
24
34
  export interface Chain {
25
35
  id: string;
26
36
  name: string;
27
- vm: string;
37
+ /** A registry chain names its VM; the type allows null so an unknown network is never read as EVM. */
38
+ vm: string | null;
39
+ /** Present when the VM has a profile; unknown VMs are null, never silently EVM. */
40
+ settlementProfile?: SettlementProfile | null;
28
41
  chainId: number;
29
42
  caip2?: string;
30
43
  testnet?: boolean;
@@ -269,12 +269,16 @@ export declare function recoveryFileName(swapId?: string | null): string;
269
269
  export declare const RECOVERY_KEY_VERSION: number;
270
270
  /** Twelve words, by the BIP39 rules. */
271
271
  export declare const RECOVERY_KEY_WORDS: number;
272
+ /** The default number of v1 indices one `findSwaps` call scans. */
273
+ export declare const RECOVERY_SCAN_DEFAULT: number;
272
274
  /** The store key a browser adapter keeps the key under. */
273
275
  export declare const RECOVERY_KEY_STORE: string;
274
276
  /** The first line of a plain backup file. */
275
277
  export declare const RECOVERY_KEY_FILE_MAGIC: string;
276
278
  /** The domain half derivation binds to; the chain id and swap number join it. */
277
279
  export declare const RECOVERY_KEY_DOMAIN: string;
280
+ /** The salt-based derivation domain; the chain, factory, role and order salt join it. */
281
+ export declare const RECOVERY_KEY_DOMAIN_V2: string;
278
282
  /** The domain of the counterparty's view half, derived from the same one key. */
279
283
  export declare const MAKER_VIEW_HALF_DOMAIN: string;
280
284
  /** The encrypted backup envelope version. */
@@ -290,6 +294,8 @@ export interface RecoveryKey {
290
294
  readonly mnemonic: string;
291
295
  readonly fingerprint: string;
292
296
  readonly createdAt: string;
297
+ /** True only after the person confirmed that the key is saved; a key in storage is not proof of that. */
298
+ readonly backupConfirmed?: boolean;
293
299
  }
294
300
 
295
301
  /** A derived swap number: the halves cross JSON as hex scalars. */
@@ -305,6 +311,15 @@ export interface TakerHalves {
305
311
  readonly spendCommitment: string;
306
312
  }
307
313
 
314
+ /** A salt-based derivation: no number is reserved, and the public order salt is the input. */
315
+ export interface TakerHalvesV2 extends Omit<TakerHalves, "index"> {
316
+ readonly index: null;
317
+ readonly derivation: "salt-v2";
318
+ readonly factory: string;
319
+ readonly role: string;
320
+ readonly salt: string;
321
+ }
322
+
308
323
  /** One order found by the key's commitments. */
309
324
  export interface FoundSwap {
310
325
  readonly escrow: string;
@@ -338,12 +353,17 @@ export interface FoundSwap {
338
353
  * the expected clone. */
339
354
  readonly xmrAmount: string | null;
340
355
  readonly xmrAmountReason: "event" | "no-code-reader" | "code" | "not-clone" | null;
341
- readonly index: number;
356
+ /** The v1 swap number; null for a salt-based v2 order. */
357
+ readonly index: number | null;
358
+ /** Which derivation found this order. v1 orders keep recovering through the index path. */
359
+ readonly derivation: "index-v1" | "salt-v2";
360
+ /** The factory the order was read from; the v2 binding needs it. */
361
+ readonly factory: string;
342
362
  readonly side: "locker" | "claimer";
343
363
  /** The Monero height a withdrawal may start from, derived from the order's block time. Null when the caller did
344
364
  * not pass the readers; `startHeightReason` then names why. */
345
365
  readonly startHeight: number | null;
346
- readonly startHeightReason: "no-block-number" | "no-height-readers" | "monero-nodes-unreachable" | "monero-network-mismatch" | "no-block-time" | "monero-header" | null;
366
+ readonly startHeightReason: "no-block-number" | "no-height-readers" | "monero-nodes-unreachable" | "monero-network-mismatch" | "no-block-time" | "block-time-read" | "monero-header" | null;
347
367
  }
348
368
 
349
369
  /** A storage adapter: the same shape the SDK store takes. */
@@ -361,8 +381,16 @@ export declare function importRecoveryKey(mnemonic: string, options?: { createdA
361
381
  export declare function fingerprintOf(mnemonic: string): string;
362
382
  /** The binding for a chain and swap number: `ninsei-taker-v1|<chainId>|<index>`. */
363
383
  export declare function recoveryBinding(chainId: number, index: number): string;
384
+ /** The salt-based binding: `ninsei-taker-v2|<chainId>|<factory>|<role>|<salt>`. */
385
+ export declare function recoveryBindingV2(request: { chainId: number; factory: string; role?: string; salt: string }): string;
386
+ /** Normalises a 32-byte salt from an event word, a hex string, a bigint or a safe integer. */
387
+ export declare function recoverySaltOf(value: string | number | bigint): string;
388
+ /** A fresh 32-byte public salt for one salt-based order. */
389
+ export declare function newRecoverySalt(): string;
364
390
  /** Derives the taker spend and view halves for one swap number. */
365
391
  export declare function takerHalves(request: { mnemonic: string; chainId: number; index: number }): TakerHalves;
392
+ /** Derives the taker halves from a public per-order salt; no index is reserved. */
393
+ export declare function takerHalvesV2(request: { mnemonic: string; chainId: number; factory: string; role?: string; salt: string }): TakerHalvesV2;
366
394
  /** The stable backup name, with `DO-NOT-DELETE` in it on purpose. */
367
395
  export declare function recoveryKeyFileName(request: { mnemonic?: string; fingerprint?: string; createdAt?: string | Date }): string;
368
396
  /** A plain backup file: readable header above the words. */
@@ -372,9 +400,9 @@ export declare function createRecoveryKeyFile(request: { mnemonic: string; creat
372
400
  /** Reads a plain or encrypted backup file and validates the words. */
373
401
  export declare function openRecoveryKeyFile(contents: string, passphrase?: string): Promise<RecoveryKey>;
374
402
  /** Saves the key through a storage adapter. */
375
- export declare function saveRecoveryKey(storage: RecoveryKeyStorage, key: RecoveryKey & { nextIndex?: number; reserved?: Record<string, unknown>; retired?: Record<string, unknown> }): RecoveryKey;
403
+ export declare function saveRecoveryKey(storage: RecoveryKeyStorage, key: RecoveryKey & { nextIndex?: number; reserved?: Record<string, unknown>; retired?: Record<string, unknown>; backupConfirmed?: boolean }): RecoveryKey;
376
404
  /** Reads the key from a storage adapter, or null. */
377
- export declare function loadRecoveryKey(storage: RecoveryKeyStorage): (RecoveryKey & { nextIndex: number; reserved: Record<string, unknown>; retired: Record<string, unknown> }) | null;
405
+ export declare function loadRecoveryKey(storage: RecoveryKeyStorage): (RecoveryKey & { nextIndex: number; reserved: Record<string, unknown>; retired: Record<string, unknown>; backupConfirmed?: boolean }) | null;
378
406
  /** Reserves a swap number; the caller releases it if the request is abandoned. */
379
407
  export declare function reserveRecoveryIndex(storage: RecoveryKeyStorage, request: { chainId: number; swapId?: string | null }): number;
380
408
  /** True when an order already holds this number's commitment on chain. A page that starts swaps without the SDK's
@@ -403,8 +431,17 @@ export declare function findSwaps(request: {
403
431
  /** The factory's deployment block from the chain registry; used when `fromBlock` is omitted. */
404
432
  deploymentBlock?: number | null;
405
433
  toBlock?: number | string;
434
+ /** Deprecated and ignored: discovery no longer stops after a run of empty numbers. */
406
435
  gap?: number;
436
+ /** The first index to try. Defaults to 0; a continuation passes the previous `nextIndex`. */
437
+ fromIndex?: number;
438
+ /** How many indices this call may try. Defaults to `RECOVERY_SCAN_DEFAULT` (1000), not the allocator ceiling. */
407
439
  maxIndex?: number;
440
+ /** Stop the scan between chunks. An aborted scan returns `complete: false`, `incompleteReason: "aborted"` and
441
+ * the `nextIndex` to resume from. */
442
+ signal?: { readonly aborted: boolean } | null;
443
+ /** Called after every chunk and after the last index, with the count and the index just scanned. */
444
+ onProgress?: ((value: { readonly scanned: number; readonly index: number }) => void) | null;
408
445
  /** Reads the settlement block timestamp, in seconds: (blockNumber) -> seconds. */
409
446
  evmBlockTime?: (blockNumber: number) => Promise<number>;
410
447
  /** Reads contract code: (address) -> 0x-hex. Used to read the XMR amount from an order clone. */
@@ -420,4 +457,15 @@ export declare function findSwaps(request: {
420
457
  moneroNetwork?: string | null;
421
458
  /** The fetch used for those node requests; absent - the global fetch. */
422
459
  fetchImpl?: typeof fetch;
423
- }): Promise<{ swaps: FoundSwap[]; scanned: number }>;
460
+ }): Promise<{
461
+ swaps: FoundSwap[];
462
+ scanned: number;
463
+ /** The last index tried, or null when no index was scanned. */
464
+ searchedUpTo: number | null;
465
+ /** True when every order in the logs was matched, or when the allocator range was searched to its end. */
466
+ complete: boolean;
467
+ /** `max-index` or `aborted` when a bound or the caller stopped the search before every order was matched. */
468
+ incompleteReason?: string;
469
+ /** The next index to pass as `fromIndex`; absent when the search is complete. */
470
+ nextIndex?: number;
471
+ }>;
@@ -25,6 +25,7 @@ export {
25
25
  export {
26
26
  RECOVERY_KEY_ALG,
27
27
  RECOVERY_KEY_DOMAIN,
28
+ RECOVERY_KEY_DOMAIN_V2,
28
29
  RECOVERY_KEY_ENVELOPE_VERSION,
29
30
  RECOVERY_KEY_FILE_MAGIC,
30
31
  RECOVERY_KEY_ITER,
@@ -32,6 +33,7 @@ export {
32
33
  RECOVERY_KEY_VERSION,
33
34
  MAKER_VIEW_HALF_DOMAIN,
34
35
  RECOVERY_KEY_WORDS,
36
+ RECOVERY_SCAN_DEFAULT,
35
37
  commitRecoveryIndex,
36
38
  createRecoveryKey,
37
39
  createRecoveryKeyFile,
@@ -39,13 +41,17 @@ export {
39
41
  fingerprintOf,
40
42
  importRecoveryKey,
41
43
  loadRecoveryKey,
44
+ newRecoverySalt,
42
45
  openRecoveryKeyFile,
43
46
  recoveryBinding,
47
+ recoveryBindingV2,
44
48
  recoveryIndexTaken,
49
+ recoverySaltOf,
45
50
  recoveryKeyFileName,
46
51
  recoveryKeyText,
47
52
  releaseRecoveryIndex,
48
53
  reserveRecoveryIndex,
49
54
  saveRecoveryKey,
50
55
  takerHalves,
56
+ takerHalvesV2,
51
57
  } from "../recovery-key.mjs";
@@ -407,17 +407,18 @@ export declare function judgeDaemonAddress(
407
407
  options?: { network?: string; allowPlainLoopback?: boolean },
408
408
  ): DaemonAddressJudgement;
409
409
 
410
- /** A per-KB price -> the fee for a transaction of `bytes`, rounded UP. Throws on a non-integer or a negative input. */
411
- export declare function feeForBytes(feePerKB: bigint | number | string, bytes: bigint | number | string): string;
410
+ /** A per-byte price -> the fee for a transaction of `bytes`/weight. Exact integer multiplication. */
411
+ export declare function feeForBytes(feePerByte: bigint | number | string, bytes: bigint | number | string): string;
412
412
 
413
413
  export interface FeeEstimateOk {
414
414
  readonly ok: true;
415
415
  readonly url: string;
416
- /** The price per kilobyte for the next block, in atomic units, or null when the daemon did not send it. */
417
- readonly feePerKB: string | null;
418
- readonly medianFeePerKB: string | null;
419
- /** The daemon's own priority list, in its order, or null. */
416
+ /** The price per byte for the next block, in atomic units, or null when the daemon did not send it. */
417
+ readonly feePerByte: string | null;
418
+ /** The daemon's own priority list, per byte and in its order, or null. */
420
419
  readonly perPriority: readonly string[] | null;
420
+ /** The unit the final fee is rounded to, or null when the daemon did not send it. */
421
+ readonly quantizationMask: string | null;
421
422
  }
422
423
 
423
424
  export interface FeeEstimateRefusal {
@@ -430,7 +431,7 @@ export interface FeeEstimateRefusal {
430
431
 
431
432
  /**
432
433
  * ASK A NODE WHAT A TRANSACTION WILL COST (`get_fee_estimate`). The exact sum needs the assembled transaction, so
433
- * this is a price per kilobyte; `feeForBytes` is the only arithmetic that turns it into a fee. Never throws.
434
+ * this is a price per byte; `feeForBytes` is the only arithmetic that turns it into a fee. Never throws.
434
435
  */
435
436
  export declare function moneroFeeEstimate(options: {
436
437
  url: string;
@@ -483,6 +484,19 @@ export interface ScannedMatch {
483
484
  [field: string]: unknown;
484
485
  }
485
486
 
487
+ /** A range scan's answer: the per-address totals and whether every block was read. */
488
+ export interface ScanRangeResult {
489
+ readonly fromHeight: number;
490
+ readonly toHeight: number;
491
+ readonly results: ScannedMatch[];
492
+ /** False when at least one block or its transactions could not be read. */
493
+ readonly complete: boolean;
494
+ /** The heights that failed; empty when `complete` is true. */
495
+ readonly missingHeights: number[];
496
+ /** `blocks-unreadable` when `complete` is false. */
497
+ readonly incompleteReason?: "blocks-unreadable";
498
+ }
499
+
486
500
  /** Walk a block range and return the transactions that pay a watched address. */
487
501
  export declare function scanRange(options: {
488
502
  daemonUrl: string;
@@ -492,4 +506,8 @@ export declare function scanRange(options: {
492
506
  log?: (event: unknown) => void;
493
507
  concurrency?: number;
494
508
  nowSec?: number | null;
495
- }): Promise<ScannedMatch[]>;
509
+ /** The chain tip used to judge a height lock; defaults to `toHeight`. Inclusion height still drives confirmations. */
510
+ tipHeight?: number | null;
511
+ /** The fetch used for the daemon requests; absent - the global fetch. */
512
+ fetchImpl?: typeof fetch;
513
+ }): Promise<ScanRangeResult>;