@dusk-network/app-core 0.1.0 → 0.3.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.
@@ -0,0 +1,535 @@
1
+ /**
2
+ * A DuskEVM → Dusk withdrawal's whole life, for the Dusk apps, signing
3
+ * aside: the call that starts it on DuskEVM, finding an account's
4
+ * withdrawals again, where each one is (waiting for a dispute game,
5
+ * provable, proved, claimable from a date, claimed), the game a proof goes
6
+ * against and the proof itself, and the proof's and the claim's calls on
7
+ * Dusk, ready for the app's wallet to sign.
8
+ *
9
+ * - Only DUSK withdrawals on the bridge's route count, as Dusk's L1 bridge
10
+ * pays them out (`decodeWithdrawal` of `@dusk-network/networks/duskevm`):
11
+ * from DuskEVM's messenger to Dusk's, relaying DuskEVM's bridge to
12
+ * Dusk's, paying a Dusk account, or a contract only whole lux at the
13
+ * address DuskEVM gives it, and no one past a u64 of lux. Any other
14
+ * message is refused, for its status, its proof and its claim.
15
+ * - A proof goes against a game that covers the withdrawal and has already
16
+ * resolved for its proposal, where the Portal would take it (the anchor
17
+ * state registry calls it proper and respected), so its claim date is
18
+ * exact when it's made; otherwise against the newest game that covers
19
+ * it and that the Portal takes a proof against. A claim date is worked
20
+ * out as the Portal checks a claim (`claimFrom`), and a claim is ready
21
+ * once the Portal takes it, its game has resolved for the defender, and
22
+ * its date has come: all of it read, never on the Portal's check alone.
23
+ * - A proof or a claim is encoded by the Portal's own data driver, which
24
+ * the app loads, and read back before it's handed over: its arguments
25
+ * must say exactly the withdrawal, the game, the output root proof and
26
+ * the proof's nodes they were built from, as Dusk Wallet reads them, or
27
+ * nothing is given to sign. It sends no DUSK along, and its gas limit is
28
+ * 50M and the withdrawal's own gas, at least 1M (`withdrawalGasLimit`),
29
+ * refused over the app's bound.
30
+ * - The app owns all I/O, storage, wallets and words: it passes its own
31
+ * clients (DuskEVM's RPC as a viem client or a URL and `fetch`, the Dusk
32
+ * node's URL with its `fetch`, its Multicall for Dusk batcher), the data
33
+ * drivers it loaded, and its caches. Results are facts; the words are
34
+ * the app's.
35
+ *
36
+ * @example
37
+ * ```js
38
+ * import { createWithdrawals } from "@dusk/app-core/duskevm-withdrawals";
39
+ *
40
+ * const withdrawals = createWithdrawals({
41
+ * network: chainId,
42
+ * nodeUrl,
43
+ * fetch: createMulticallFetch({ nodeUrl, multicallId }), // the reads one task starts, in one query
44
+ * drivers: { optimismPortal: () => loadDriver(portalUrl), disputeGameFactory: () => loadDriver(factoryUrl) },
45
+ * duskEvm: publicClient, // or { url: rpcUrl, fetch }
46
+ * });
47
+ *
48
+ * const status = await withdrawals.status(txHash, { submitter: account });
49
+ * // { stage: "proven_waiting", reason: "game-in-progress", readyAt: 1793589639n, readyAtExact: false, … }
50
+ * const prepared = await withdrawals.prepareProof(txHash, { submitter: account });
51
+ * prepared.claim; // when it can be claimed if proved now: { at, exact, overdue }
52
+ * prepared.call; // { contractId, fnName: "proveWithdrawalTransaction", fnArgs, deposit: 0n, gasLimit: 51_000_000n, … }
53
+ * const claim = await withdrawals.claimCall(txHash, { submitter: account }); // once it's ready
54
+ * ```
55
+ *
56
+ * @module
57
+ */
58
+
59
+ import type { Account, ContractId, EvmAddress, Network, NetworkRef } from "@dusk-network/networks";
60
+ import type { ClaimCall, ClaimDate, DuskTip, ProveCall, WithdrawalFacts } from "@dusk-network/networks/duskevm";
61
+
62
+ /** How many of the newest games a proof is looked for among, by default. */
63
+ export declare const GAME_SEARCH_DEPTH: 64;
64
+
65
+ /** Games one search for a resolved game reads at once. */
66
+ export declare const RESOLVED_PAGE: 32;
67
+
68
+ /** Pages of games a search for a resolved game reads at most. */
69
+ export declare const RESOLVED_PAGES: 3;
70
+
71
+ /**
72
+ * How long past its clock running out a game's resolver is usually done:
73
+ * an hour, in seconds. A game is old enough to have resolved once both
74
+ * have passed.
75
+ */
76
+ export declare const RESOLVER_LAG_SECONDS: 3600;
77
+
78
+ /** Reads one query holds by default, as Multicall for Dusk's batcher sends them; each game checked takes 4. */
79
+ export declare const MAX_BATCH: 32;
80
+
81
+ /** The gas limit a proof or a claim is refused over by default: 1,000,000,000, the apps' bound. */
82
+ export declare const GAS_LIMIT_UPPER: 1000000000n;
83
+
84
+ /** The gas a withdrawal's start leaves its message on Dusk: 200,000, as OP Stack bridges use. */
85
+ export declare const WITHDRAWAL_MIN_GAS_LIMIT: 200000;
86
+
87
+ /** DuskEVM blocks one log search asks for at once: 100,000, about 2.3 days. */
88
+ export declare const L2_WINDOW: 100000;
89
+
90
+ /** Log searches one run of `syncWithdrawals` makes, the head's included, by default. */
91
+ export declare const L2_RUN_REQUESTS: 8;
92
+
93
+ /** DuskEVM blocks under the head counted as final where the RPC has no "finalized" block: about an hour. */
94
+ export declare const UNFINALIZED_MARGIN: 1800;
95
+
96
+ /** Pages of an address's transactions `explorerWithdrawals` reads at most, by default. */
97
+ export declare const EXPLORER_PAGES: 10;
98
+
99
+ /** Withdrawals `explorerWithdrawals` gives at most, by default. */
100
+ export declare const EXPLORER_ITEMS: 12;
101
+
102
+ /** `MessagePassed(uint256,address,address,uint256,uint256,bytes,bytes32)`: DuskEVM's message passer recording a withdrawal. */
103
+ export declare const MESSAGE_PASSED_TOPIC: "0x02a52367d10742d8032712c1bb8e0144ff1ec5ffda1ed7d70bb05a2744955054";
104
+
105
+ /** `ETHBridgeInitiated(address,address,uint256,bytes)`: DuskEVM's bridge starting a DUSK withdrawal, from its sender. */
106
+ export declare const ETH_BRIDGE_INITIATED_TOPIC: "0x2849b43074093a05396b6f2a937dee8565b15a48a7b3d4bffb732a5017380af5";
107
+
108
+ /**
109
+ * Where a withdrawal is on Dusk, for the account whose proof counts:
110
+ * waiting for a dispute game to cover it, ready to prove, proved and
111
+ * waiting for its claim date, ready to claim, or claimed.
112
+ */
113
+ export type WithdrawalStage = "waiting_for_output" | "ready_to_prove" | "proven_waiting" | "ready_to_finalize" | "finalized";
114
+
115
+ /** The stages, by name. */
116
+ export declare const STAGE: Readonly<{
117
+ WAITING_FOR_OUTPUT: "waiting_for_output";
118
+ READY_TO_PROVE: "ready_to_prove";
119
+ PROVEN_WAITING: "proven_waiting";
120
+ READY_TO_FINALIZE: "ready_to_finalize";
121
+ FINALIZED: "finalized";
122
+ }>;
123
+
124
+ /**
125
+ * Why a withdrawal waits, or must be proved again:
126
+ * - "no-games": no dispute game has been proposed yet;
127
+ * - "no-output": no game the Portal takes a proof against covers it yet (`error` says what couldn't be read while
128
+ * looking, if anything);
129
+ * - "proof-rejected": the account's proof can't be claimed, as the Portal refuses its game (blacklisted, retired, no
130
+ * longer respected, or won by the challenger), whatever its own check says: it's ready to prove again against
131
+ * another game the Portal takes, or waits for one;
132
+ * - "game-unreadable": the proof's game, or whether the Portal still takes it, can't be read now: no date, and nothing
133
+ * to claim;
134
+ * - "portal-refuses": the claim's date has passed and the Portal still refuses the claim;
135
+ * - "game-in-progress": the proof's game hasn't resolved;
136
+ * - "game-not-final": its game has resolved, and the proof or the game isn't final yet.
137
+ */
138
+ export type WaitReason =
139
+ | "no-games"
140
+ | "no-output"
141
+ | "proof-rejected"
142
+ | "game-unreadable"
143
+ | "portal-refuses"
144
+ | "game-in-progress"
145
+ | "game-not-final";
146
+
147
+ /**
148
+ * Why the package refuses:
149
+ * - "bad-hash": not a DuskEVM transaction hash;
150
+ * - "not-found": DuskEVM has no receipt of it (yet);
151
+ * - "failed": it failed on DuskEVM;
152
+ * - "not-a-withdrawal": it records no DUSK withdrawal on the bridge's route that Dusk's L1 bridge pays out;
153
+ * - "gas": its proof or claim needs more gas than the bound;
154
+ * - "claimed": it's claimed already, so a proof does nothing;
155
+ * - "proven": the account has a proof of it that stands, and another would cost its fee for nothing;
156
+ * - "no-proof": no game covers it yet (`reason`);
157
+ * - "not-ready": it isn't ready to claim for the account (`status`);
158
+ * - "bad-call": the call its driver encoded doesn't read back as what it was built from.
159
+ */
160
+ export type WithdrawalErrorCode =
161
+ | "bad-hash"
162
+ | "not-found"
163
+ | "failed"
164
+ | "not-a-withdrawal"
165
+ | "gas"
166
+ | "claimed"
167
+ | "proven"
168
+ | "no-proof"
169
+ | "not-ready"
170
+ | "bad-call";
171
+
172
+ /** A refusal, by its code, with the withdrawal's status where one was read. */
173
+ export declare class WithdrawalError extends Error {
174
+ constructor(
175
+ code: WithdrawalErrorCode,
176
+ message?: string,
177
+ options?: { status?: WithdrawalStatus | null; reason?: WaitReason | null; cause?: unknown }
178
+ );
179
+ readonly code: WithdrawalErrorCode;
180
+ /** The withdrawal's status, for "claimed", "proven" and "not-ready". */
181
+ readonly status: WithdrawalStatus | null;
182
+ /** Why it waits: the status's, or for "no-proof", "no-games" or "no-output". */
183
+ readonly reason: WaitReason | null;
184
+ }
185
+
186
+ /** A withdrawal as DuskEVM's message passer recorded it. */
187
+ export type L2Withdrawal = {
188
+ nonce: bigint;
189
+ /** DuskEVM's messenger, with its checksum. */
190
+ sender: EvmAddress;
191
+ /** Dusk's messenger, as DuskEVM names it, with its checksum. */
192
+ target: EvmAddress;
193
+ /** In DuskEVM's 18 decimals. */
194
+ value: bigint;
195
+ /** The gas its own call asks for, which a proof's and a claim's limit add. */
196
+ gasLimit: bigint;
197
+ /** The relayed message, lower-case hex. */
198
+ data: `0x${string}`;
199
+ };
200
+
201
+ /** A DuskEVM transaction's withdrawal: where DuskEVM recorded it, and what it does. */
202
+ export type ReceiptWithdrawal = {
203
+ /** The DuskEVM transaction, lower case. */
204
+ transactionHash: `0x${string}`;
205
+ /** Its DuskEVM block: a game must cover it. */
206
+ blockNumber: bigint;
207
+ /** The hash the Portal keys its proofs and its claim by, lower case. */
208
+ withdrawalHash: `0x${string}`;
209
+ withdrawal: L2Withdrawal;
210
+ /** What it pays, whom, from whom: `decodeWithdrawal`'s. */
211
+ facts: WithdrawalFacts;
212
+ };
213
+
214
+ /** Where a withdrawal is, as the chain says, for the account whose proof counts (`proofSubmitter`). */
215
+ export type WithdrawalStatus = ReceiptWithdrawal & {
216
+ stage: WithdrawalStage;
217
+ /** Why it waits; null when it doesn't. */
218
+ reason: WaitReason | null;
219
+ /** For "no-output": the last read that failed while a game was looked for. */
220
+ error?: Error;
221
+ /**
222
+ * The Portal's address of the account whose proof the stage is, lower
223
+ * case: the one asked for, else the recipient's own when it's an
224
+ * account; null for neither.
225
+ */
226
+ proofSubmitter: `0x${string}` | null;
227
+ /** How many accounts have proved it. */
228
+ proofSubmitterCount: bigint;
229
+ /** Whether the account's proof can't be claimed and must be made again. */
230
+ proofRejected: boolean;
231
+ /** For "ready_to_prove": another account holds a proof that stands, which this account can't claim with. */
232
+ provenByOther?: boolean;
233
+ /**
234
+ * For "ready_to_prove": the game a proof would go against now, the newest
235
+ * that covers it and that the Portal takes a proof against, else one
236
+ * that has resolved, as `prepareProof` finds it; and the DuskEVM block it
237
+ * covers.
238
+ */
239
+ disputeGameIndex?: bigint;
240
+ proofL2BlockNumber?: bigint;
241
+ /** Once proved: the game the account's proof went against, lower case. */
242
+ disputeGameProxy?: `0x${string}`;
243
+ /** Once proved: when, in seconds of wall-clock time, from the contracts' own clock. */
244
+ provenAt?: bigint;
245
+ /**
246
+ * When it can be claimed, in seconds; null when there's no date (a proof
247
+ * to make again, or a date past while the Portal still refuses). Absent
248
+ * when it couldn't be worked out now, as when the proof's game can't be
249
+ * read: the date known so far stands.
250
+ */
251
+ readyAt?: bigint | null;
252
+ /** Whether `readyAt` is exact: its game has resolved. */
253
+ readyAtExact?: boolean;
254
+ /** Whether its game is more than an hour late to resolve. */
255
+ claimOverdue?: boolean;
256
+ };
257
+
258
+ /**
259
+ * When a proved withdrawal can be claimed (`ClaimDate`), whether it's
260
+ * claimed, whether it can be claimed now (its game read and resolved for
261
+ * the defender, the Portal taking the claim, its date come), and whether
262
+ * its proof must be made again (the Portal refuses its game: blacklisted,
263
+ * retired, no longer respected, or won by the challenger), with no date
264
+ * then. Fits `/activity`'s `claims`.
265
+ */
266
+ export type WithdrawalClaim = ClaimDate & { claimed: boolean; ready: boolean; reprove: boolean };
267
+
268
+ /** How long a withdrawal takes on the network, as the Portal and the games say. */
269
+ export type WithdrawalTiming = {
270
+ /** The Portal's proof maturity delay, in seconds. */
271
+ maturitySeconds: number;
272
+ /** The Portal's dispute game finality delay, in seconds. */
273
+ finalitySeconds: number;
274
+ /** How often outputs come: the middle gap between the latest games, in the contracts' seconds; null without two games. */
275
+ outputSeconds: number | null;
276
+ };
277
+
278
+ /** The game a proof goes against, and the proof. */
279
+ export type OutputProof = {
280
+ /** The game's index in the factory. */
281
+ disputeGameIndex: bigint;
282
+ /** The game's address, lower case. */
283
+ disputeGameProxy: `0x${string}`;
284
+ /** The DuskEVM block the game's output is of. */
285
+ l2BlockNumber: bigint;
286
+ /** The output's root, the game's root claim. */
287
+ outputRoot: `0x${string}`;
288
+ outputRootProof: {
289
+ version: `0x${string}`;
290
+ stateRoot: `0x${string}`;
291
+ messagePasserStorageRoot: `0x${string}`;
292
+ latestBlockhash: `0x${string}`;
293
+ };
294
+ /** The message passer's storage proof of the withdrawal, its nodes as RLP. */
295
+ withdrawalProof: `0x${string}`[];
296
+ };
297
+
298
+ /** A proof's or a claim's call on Dusk, for the app's wallet to sign. */
299
+ export type PortalCall = {
300
+ /** The network's Portal. */
301
+ contractId: ContractId;
302
+ fnName: "proveWithdrawalTransaction" | "finalizeWithdrawalTransaction";
303
+ /** The arguments, as the Portal's data driver encoded `args`. */
304
+ fnArgs: Uint8Array;
305
+ /** The driver's input, as JSON takes it: bytes as arrays of numbers. */
306
+ args: unknown;
307
+ /** DUSK sent along: none. */
308
+ deposit: 0n;
309
+ /** 50M and the withdrawal's own gas, at least 1M. */
310
+ gasLimit: bigint;
311
+ /** What the call does, as `decodeBridgeCall` reads its `fnArgs`: what Dusk Wallet shows. */
312
+ decoded: ProveCall | ClaimCall;
313
+ };
314
+
315
+ /**
316
+ * A proof, ready to sign: the withdrawal, the game and its proof, when it
317
+ * can be claimed if proved now (null when that can't be read), and the
318
+ * call. `at` is when it was prepared: the app decides how long it sends it
319
+ * as is.
320
+ */
321
+ export type PreparedProof = {
322
+ at: number;
323
+ receipt: ReceiptWithdrawal;
324
+ proof: OutputProof;
325
+ claim: ClaimDate | null;
326
+ call: PortalCall;
327
+ };
328
+
329
+ /** A data driver, as the app loaded it: JSON in, rkyv out, and back. */
330
+ export type DataDriver = {
331
+ encodeInputFn(fnName: string, json: string): Uint8Array;
332
+ decodeOutputFn(fnName: string, bytes: Uint8Array): unknown;
333
+ };
334
+
335
+ /** A driver, a promise of one, or what loads one, once, and again after it failed. */
336
+ export type DriverSource = DataDriver | PromiseLike<DataDriver> | (() => DataDriver | PromiseLike<DataDriver>);
337
+
338
+ /** DuskEVM's JSON-RPC: a viem client (its `request`), or a URL and the `fetch` to post to it. */
339
+ export type DuskEvmClient = { request(args: { method: string; params?: unknown }): Promise<unknown> } | { url: string; fetch?: typeof fetch };
340
+
341
+ /** The app's cache of what a deployment doesn't change, as text: a Map does. */
342
+ export type FactCache = { get(key: string): string | null | undefined; set(key: string, value: string): unknown };
343
+
344
+ /** What `createWithdrawals` takes. */
345
+ export type WithdrawalsOptions = {
346
+ /** The network, or its chain ID: its file names the Portal, the factory, the registry and the bridge's route. */
347
+ network: NetworkRef;
348
+ /** The Dusk node the Portal and the games are read on. */
349
+ nodeUrl: string;
350
+ /** The `fetch` the reads go through: the app's Multicall for Dusk batcher sends those one task starts in one query. */
351
+ fetch?: typeof fetch;
352
+ /** The Portal's and the dispute game factory's data drivers, as the network's file names and pins them. */
353
+ drivers: { optimismPortal: DriverSource; disputeGameFactory: DriverSource };
354
+ /** DuskEVM's RPC; the network file's, by `fetch`, by default. */
355
+ duskEvm?: DuskEvmClient;
356
+ /** Dusk's latest block, which contract times count from; read from the node by default. */
357
+ latestBlock?: () => Promise<DuskTip>;
358
+ /** Reads one query holds: 32 by default. */
359
+ maxBatch?: number;
360
+ /** How many of the newest games a proof is looked for among: 64 by default, and for anything but a positive whole number. */
361
+ gameSearchDepth?: number | bigint;
362
+ /** Where the deployment's fixed facts are kept: each game's hub, each game type's hub, each hub's clock limit. */
363
+ cache?: FactCache;
364
+ /** The clock, in milliseconds. */
365
+ now?: () => number;
366
+ };
367
+
368
+ /** A network's withdrawals, read on its Dusk node and DuskEVM. */
369
+ export type Withdrawals = {
370
+ readonly network: Network;
371
+ /** A DuskEVM transaction's withdrawal, from its receipt; refuses as `parseWithdrawalReceipt` does. */
372
+ receipt(txHash: string): Promise<ReceiptWithdrawal>;
373
+ /**
374
+ * Where a withdrawal is, for `submitter` (a Dusk account, or its
375
+ * Portal address), or else its recipient's account. `proofGame`, the game
376
+ * the last check found the proof against, puts that game's reads in the
377
+ * same query as the Portal's.
378
+ */
379
+ status(txHash: string, options?: { submitter?: string | null; proofGame?: string | null }): Promise<WithdrawalStatus>;
380
+ /**
381
+ * When the withdrawals `submitter` proved can be claimed, by their hashes
382
+ * (lower case keys): claimed, ready, a date, none, or to prove again.
383
+ * Each one's record and the Portal's check go in one query, their games
384
+ * in the next.
385
+ */
386
+ claims(hashes: ReadonlyArray<string>, options: { submitter: string }): Promise<Map<string, WithdrawalClaim>>;
387
+ /** How long a withdrawal takes: the Portal's delays with the game count in one query, then the latest games. */
388
+ timing(): Promise<WithdrawalTiming>;
389
+ /**
390
+ * The proof `submitter` signs: against a resolved game where one covers
391
+ * the withdrawal, else the newest that covers it that the Portal takes a
392
+ * proof against. Refused ("claimed",
393
+ * "proven", "gas", "no-proof", "not-a-withdrawal") where it would do
394
+ * nothing or can't be made.
395
+ */
396
+ prepareProof(txHash: string, options: { submitter: string; gasLimitUpper?: bigint | number }): Promise<PreparedProof>;
397
+ /** The claim `submitter` signs, once its status is ready to claim; refused ("not-ready", "gas") before. */
398
+ claimCall(
399
+ txHash: string,
400
+ options: { submitter: string; gasLimitUpper?: bigint | number; proofGame?: string | null }
401
+ ): Promise<PortalCall>;
402
+ };
403
+
404
+ /** A network's withdrawals: the reads go through the app's clients, and nothing is kept outside the object. */
405
+ export declare function createWithdrawals(options: WithdrawalsOptions): Withdrawals;
406
+
407
+ /**
408
+ * The withdrawal a DuskEVM receipt records: its message passer's
409
+ * `MessagePassed`, whose hash must be its own, on the network's bridge
410
+ * route, as Dusk's L1 bridge pays it out. Throws a WithdrawalError:
411
+ * "not-found" without a receipt, "failed" for a reverted one,
412
+ * "not-a-withdrawal" for anything else.
413
+ */
414
+ export declare function parseWithdrawalReceipt(network: NetworkRef, receipt: unknown): ReceiptWithdrawal;
415
+
416
+ /** What `withdrawalCall` takes. */
417
+ export type WithdrawalCallInput = {
418
+ /** What it moves, in DuskEVM's 18 decimals: whole lux, more than 0, at most a u64 of lux. */
419
+ amountWei: bigint | number | string;
420
+ /** The Dusk account it pays. */
421
+ to: Account;
422
+ /** The DuskEVM address that sends it, which its message names as its recipient address. */
423
+ sender: string;
424
+ /** The gas it leaves its message on Dusk: `WITHDRAWAL_MIN_GAS_LIMIT` by default. */
425
+ minGasLimit?: number | bigint;
426
+ };
427
+
428
+ /**
429
+ * A DUSK withdrawal's start on DuskEVM, for the app's EVM wallet to send:
430
+ * DuskEVM's bridge's `bridgeETHTo`, the Dusk account in its extra data,
431
+ * the amount as its value. Throws a RangeError for an amount that isn't
432
+ * whole lux, is 0, or is past a u64 of lux, and a TypeError for a
433
+ * recipient that isn't a Dusk account or a sender that isn't an address.
434
+ */
435
+ export declare function withdrawalCall(
436
+ network: NetworkRef,
437
+ withdrawal: WithdrawalCallInput
438
+ ): { to: EvmAddress; data: `0x${string}`; value: bigint };
439
+
440
+ /** A withdrawal found in DuskEVM's bridge's logs. */
441
+ export type FoundWithdrawal = {
442
+ /** Its DuskEVM transaction, lower case. */
443
+ hash: `0x${string}`;
444
+ blockNumber: number;
445
+ /** What it moves, in DuskEVM's 18 decimals, as text. */
446
+ amountWei: string;
447
+ /** Whom it pays on Dusk, as its extra data says: an account, a contract's ID, or null for neither. */
448
+ destination: string | null;
449
+ };
450
+
451
+ /** Where `syncWithdrawals` got to, kept by the app and passed back as it is. */
452
+ export type WithdrawalScan = {
453
+ /** The final blocks covered, as ranges, highest first. */
454
+ covered: number[][];
455
+ /** What was found, by hash. */
456
+ entries: Record<string, FoundWithdrawal>;
457
+ /** The final head the last run read under. */
458
+ target: number | null;
459
+ /** The blocks one search asks for. */
460
+ window: number;
461
+ updatedAt?: number;
462
+ };
463
+
464
+ /**
465
+ * One run over an address's withdrawals, from the bridge's own event
466
+ * (`ETHBridgeInitiated`, by its sender), the newest final blocks first, a
467
+ * window at a time, narrower where the RPC asks for it, within
468
+ * `maxRequests`. Only final blocks count as covered: those above can still
469
+ * be reorged away. The next run carries on below and reads only what is
470
+ * new above; under a final head lower than the last run's, what was
471
+ * covered and found above it is dropped, and read again.
472
+ */
473
+ export declare function syncWithdrawals(options: {
474
+ /** The DuskEVM address that sent them. */
475
+ address: string;
476
+ duskEvm: DuskEvmClient;
477
+ /** The network, for its bridge on DuskEVM; or the bridge's address. */
478
+ network?: NetworkRef;
479
+ bridge?: string;
480
+ state?: WithdrawalScan | null;
481
+ maxRequests?: number;
482
+ now?: () => number;
483
+ }): Promise<{ found: FoundWithdrawal[]; requests: number; state: WithdrawalScan }>;
484
+
485
+ /** A withdrawal as DuskEVM's explorer lists it. */
486
+ export type ExplorerWithdrawal = {
487
+ /** Its DuskEVM transaction, lower case. */
488
+ hash: `0x${string}`;
489
+ /** Who sent it, lower case. */
490
+ from: `0x${string}` | null;
491
+ /** Its DuskEVM block, as text; null where the list doesn't say. */
492
+ blockNumber: string | null;
493
+ /** In DuskEVM's 18 decimals, as text. */
494
+ amountWei: string | null;
495
+ /** Whom it pays on Dusk, as its call says: an account, a contract's ID, or null. */
496
+ destination: string | null;
497
+ /** When it was sent, ISO; null where the explorer doesn't say. */
498
+ timestamp: string | null;
499
+ /** The explorer's own word for where it is ("Ready to prove"); see `explorerStage`. */
500
+ explorerStatus: string | null;
501
+ /** Its claim's transaction, where the explorer knows it. */
502
+ l1TransactionHash: `0x${string}` | null;
503
+ };
504
+
505
+ /**
506
+ * An address's withdrawals, from DuskEVM's explorer: its own calls to the
507
+ * bridge's `bridgeETHTo` that didn't fail, newest first, up to `maxItems`,
508
+ * each with what the explorer says of it.
509
+ */
510
+ export declare function explorerWithdrawals(options: {
511
+ network: NetworkRef;
512
+ address: string;
513
+ fetch?: typeof fetch;
514
+ /** The explorer's API: the network file's by default. */
515
+ url?: string;
516
+ maxPages?: number;
517
+ maxItems?: number;
518
+ }): Promise<ExplorerWithdrawal[]>;
519
+
520
+ /**
521
+ * The DuskEVM transactions of withdrawals known by their nonces, as their
522
+ * proofs and claims on Dusk carry them, from the explorer's list of all
523
+ * withdrawals: newest first, never past the oldest asked for, by nonce as
524
+ * decimal text. However a withdrawal was sent, the list has it.
525
+ */
526
+ export declare function withdrawalsByNonce(options: {
527
+ network: NetworkRef;
528
+ nonces: ReadonlyArray<bigint>;
529
+ fetch?: typeof fetch;
530
+ url?: string;
531
+ maxPages?: number;
532
+ }): Promise<Map<string, ExplorerWithdrawal>>;
533
+
534
+ /** The stage the explorer's word for a withdrawal stands for; null for one it doesn't have yet. */
535
+ export declare function explorerStage(status: string | null | undefined): WithdrawalStage | null;