@dusk-network/app-core 0.2.0 → 0.3.1

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/CHANGELOG.md CHANGED
@@ -2,6 +2,28 @@
2
2
 
3
3
  All notable changes to this package are listed here. It follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and [Semantic Versioning](https://semver.org/).
4
4
 
5
+ ## [0.3.1]
6
+
7
+ ### Added
8
+
9
+ - `/history`: `historyRange` reads on from a `page`
10
+
11
+ ### Fixed
12
+
13
+ - `/bridge`: each transfer reads only its own hours of its account's history, read on from its `scan` while a call can't read it all: it waits then, rather than staying unsure
14
+ - `/bridge`: a transfer the archive doesn't list in its account's history yet waits, rather than taking an earlier one's credit
15
+ - `/bridge`: a deposit whose call's arguments the archive doesn't give counts as one that could be equal, rather than none
16
+
17
+ ## [0.3.0]
18
+
19
+ ### Added
20
+
21
+ - `/duskevm-withdrawals`: a DuskEVM withdrawal's start, finding it again, its status and claim date, and its proof's and claim's calls, checked before they're handed over to sign
22
+
23
+ ### Fixed
24
+
25
+ - `/bridge`: a migration is paid by its own route's payout account also where one account pays out both routes (a payout of one not listed then has a null `route`), and a payout read from an entry leaves out a gas refund paid with it, which `/history` entries now tell apart as `refund`
26
+
5
27
  ## [0.2.0]
6
28
 
7
29
  ### Added
package/README.md CHANGED
@@ -31,8 +31,9 @@ The entry points are the same on both, under each registry's name: `@dusk-networ
31
31
  | `@dusk-network/app-core/shielded` | A profile's shielded history from the notes its shielded sync keeps, each block asked for among kept decoys (`shieldedHistory`); its entries as facts (`shieldedEntries`); the blocks at the notes' heights (`fetchShieldedBlocks`); the note tree's heights and the decoys drawn from them (`noteTreeHeights`, `readNoteHeights`, `createLeafHeightTap`, `pickHeights`, `leafInfo`) |
32
32
  | `@dusk-network/app-core/activity` | An account's activity as rows of facts, one per transaction, newest first, from its public and shielded history and the wallet's own sends (`activityRows`), and the sends the chain's rows now say all of (`settleSends`) |
33
33
  | `@dusk-network/app-core/bridge` | The other side of the bridge's routes: where a deposit to DuskEVM arrived (`depositArrivals`), a migration's payout on Dusk (`migrationPayouts`), and where a transfer out to BNB Smart Chain arrived (`serviceArrivals`); the history's entries as those transfers (`depositOf`, `migrationPayoutOf`, `serviceTransferOf`); and the arrivals as `/activity` takes them (`arrivalsOf`) |
34
+ | `@dusk-network/app-core/duskevm-withdrawals` | A DuskEVM withdrawal's life, signing aside: its start on DuskEVM (`withdrawalCall`), finding an address's withdrawals (`syncWithdrawals`, `explorerWithdrawals`, `withdrawalsByNonce`), and on Dusk where each is, its proof and its claim, ready for the app's wallet to sign (`createWithdrawals`) |
34
35
 
35
- Every entry point has its `.d.ts`. The one runtime dependency is `@dusk-network/networks`.
36
+ Every entry point has its `.d.ts`. The one runtime dependency is `@dusk-network/networks`. `/duskevm-withdrawals` also takes [viem](https://viem.sh) (`^2.55.2`), an optional peer: an app installs it to use that entry, and no other entry imports it.
36
37
 
37
38
  ## History
38
39
 
@@ -58,7 +59,7 @@ const { entries, complete } = await history.read({ limit: 50 });
58
59
  - `network` is the node's network or its chain ID, for the DuskEVM bridge's calls, the bridge's routes and Sozu. A node of a network there's no file for passes none.
59
60
  - `fillDetails(entries)` reads the details of entries shown before a run gave them any.
60
61
 
61
- An entry is facts, not text: `direction` ("in", "out", "self"), `kind` (`transfer`, `shield`, `unshield`, `contract_call`, `stake`, `unstake`, `withdraw_reward`), `counterparty`, `value` and `fee` in lux (a gas refund paid to another account counts in the sender's `value`), `memo`, and with its details its time, outcome, gas and call. A call to DuskEVM's bridge or Portal has `bridge`, what it did (`decodeBridgeCall`): a deposit's amount and DuskEVM address, or a withdrawal's proof or claim, its hash, whom it pays and its nonce. A transfer with one of the bridge's accounts has its `route` (`routeForAccount`), and a stake or an unstake with Sozu `sozu: true` (`isSozuCall`, with the pools the hub named in `read({ pools })`).
62
+ An entry is facts, not text: `direction` ("in", "out", "self"), `kind` (`transfer`, `shield`, `unshield`, `contract_call`, `stake`, `unstake`, `withdraw_reward`), `counterparty`, `value` and `fee` in lux (a gas refund paid to another account counts in the sender's `value` and in that account's, told apart as `refund`), `memo`, and with its details its time, outcome, gas and call. A call to DuskEVM's bridge or Portal has `bridge`, what it did (`decodeBridgeCall`): a deposit's amount and DuskEVM address, or a withdrawal's proof or claim, its hash, whom it pays and its nonce. A transfer with one of the bridge's accounts has its `route` (`routeForAccount`), and a stake or an unstake with Sozu `sozu: true` (`isSozuCall`, with the pools the hub named in `read({ pools })`).
62
63
 
63
64
  ### The store
64
65
 
@@ -97,7 +98,7 @@ const history = createHistory({
97
98
 
98
99
  ### Other reads
99
100
 
100
- - `historyRange(archive, { account, from, to, direction, pages })`: an account's entries between two heights, a page at a time.
101
+ - `historyRange(archive, { account, from, to, direction, pages, page })`: an account's entries between two heights, a page at a time, from `page` on.
101
102
  - `fetchTxDetails(archive, hashes, options)` and `fetchTxNonce(archive, hash)`: a transaction's time, outcome, fee and call, and its nonce.
102
103
  - `archive.query(text)`: one GraphQL request, its integers past 2^53 kept as text. A request answered 429, 502, 503 or 504 is sent again, twice at most.
103
104
 
@@ -200,9 +201,10 @@ arrivalsOf(arrivals); // Map { hash => { hash: "0x029e…", at: 1793080333000 }
200
201
  - Each credit pays one transfer. Equal transfers (the same route, recipient and payout) are paid in the order they were sent: the oldest takes the first credit after it, from 2 minutes before its block, as the chains' clocks differ. The app passes the transfers it still waits for, and the credits its others arrived with (`taken`): none of those pays a waiting one, though an earlier transfer read from the chain takes its own back, as one given but not looked for does.
201
202
  - A transfer's time is its Dusk block's, never the device's clock. One without it yet isn't looked for. One sent more than a week ago (`waitMs`) is unknown, and isn't asked about.
202
203
  - A deposit is followed on DuskEVM's explorer (`via: "explorer"`, the default): the recipient's incoming calls, newest first, read once for all its deposits, back past the oldest, 5 pages at most. Those calls don't say who deposited, so a payment is a deposit's only when it can't be another's: within 2 minutes either side of its block, once those minutes are over, with no other payment of its amount there (a failed one counts) that isn't another given deposit's. Nothing is asked before then.
203
- - On DuskEVM's RPC (`via: "rpc"`), a deposit is found by DuskEVM's bridge event from its sender's DuskEVM address. With `/history`'s archive, each sender's equal deposits in the hours before its own are read from its history and paid first; a history too long to read, or a deposit there whose call can't be read, leaves the order unclear. Without it, the RPC's credits are taken as the explorer's are. A deposit given without its sender takes no credit from the sender of another equal one given.
204
- - A migration is paid by a transfer to the account from its route's payout account, with the migration's transaction hash as the memo, of exactly its amount: from the account's entries, or from the archive's memo index, which gives each payout's own amount. One from that account of another amount leaves it unsure, not waiting: an entry's value holds any gas refund paid to the account. With `entries`, the payouts there of migrations the app didn't list come too (`unlisted`).
205
- - A transfer out is read from the chain: what the route's account received, the address its memo gives as the route's bridge reads it (0x and 40 hex digits, any case, nothing around them), and its block. It arrives with the DUSK token's `Transfer` from its route's payout address to that address, of what the account received less the fee. The account's history lists every transfer that named the same address, in any case: equal ones in the hours before (a given one's by its own fee) are paid first, and one that can't be read, or a history cut short, leaves the order unclear. One that pays nothing out is `no-payout`, with its `problem`. `serviceTransferOf` gives what left the account (`sent`), and what the account received (`amount`) only where no gas was left to refund: an entry's value holds any refund paid to another account.
204
+ - On DuskEVM's RPC (`via: "rpc"`), a deposit is found by DuskEVM's bridge event from its sender's DuskEVM address. With `/history`'s archive, each sender's equal deposits in the hours before its own are read from its history and paid first; a deposit there whose call can't be read leaves the order unclear. Without it, the RPC's credits are taken as the explorer's are. A deposit given without its sender takes no credit from the sender of another equal one given.
205
+ - A migration is paid by a transfer to the account from its route's payout account, with the migration's transaction hash as the memo, of exactly its amount: from the account's entries, less any `refund` paid to the account with it, or from the archive's memo index, which gives each payout's own amount. One from that account of another amount leaves it unsure, not waiting: the value of an entry an earlier version kept holds any gas refund paid to the account. With `entries`, the payouts there of migrations the app didn't list come too (`unlisted`), each with its `payer`; where one account pays out more than one route, a payout of a migration not listed doesn't say which: its `route` is null.
206
+ - A transfer out is read from the chain: what the route's account received, the address its memo gives as the route's bridge reads it (0x and 40 hex digits, any case, nothing around them), and its block. It arrives with the DUSK token's `Transfer` from its route's payout address to that address, of what the account received less the fee. The account's history lists every transfer that named the same address, in any case: equal ones in the hours before (a given one's by its own fee) are paid first, and one that can't be read leaves the order unclear. One that pays nothing out is `no-payout`, with its `problem`. `serviceTransferOf` gives what left the account (`sent`), and what the account received (`amount`) only where no gas was left to refund: an entry's value holds any refund paid to another account.
207
+ - With the archive, each transfer reads only its own hours of its account's history (its sender's for a deposit, the route's account's for a transfer out), further back while equal ones before it run on, and only once the archive lists the transfer there. A call reads 4 pages of it (`HISTORY_PAGES`) for each: a transfer whose history is longer waits, and the next call reads on from its `scan`.
206
208
  - A route without its payout account or address in the network's file pays out nothing that can be told apart: `no-route`.
207
209
  - An RPC search starts at a block from before the transfer, estimated from the chain's pace and checked by its time, and reads 8 windows of 5,000 blocks at most, narrower where the RPC asks for it. A result that isn't arrived says where the next search starts, with what it searched for and the credits found so far (`scan`): the app keeps it with the arrivals it was made with, and passes both back. A search for another sender or amount starts afresh.
208
210
 
@@ -210,6 +212,51 @@ A result is facts: `arrived`, with the other side's transaction, its block's tim
210
212
 
211
213
  `activityRows` of `/activity` takes the deposits that arrived as its `arrivals`: `arrivalsOf` gives them by the deposit's hash, the DuskEVM transaction that paid it and when, and the deposit's row says where it arrived. Its `claims` are DuskEVM withdrawals', which this entry point doesn't read.
212
214
 
215
+ ## DuskEVM withdrawals
216
+
217
+ ```js
218
+ import { createWithdrawals, withdrawalCall } from "@dusk-network/app-core/duskevm-withdrawals";
219
+
220
+ // The start, for the app's EVM wallet to send on DuskEVM.
221
+ const start = withdrawalCall(chainId, { amountWei, sender: evmAddress, to: duskAccount }); // { to, data, value }
222
+
223
+ const withdrawals = createWithdrawals({
224
+ network: chainId,
225
+ nodeUrl,
226
+ fetch: createMulticallFetch({ nodeUrl, multicallId }), // the reads one task starts go as one query
227
+ drivers: { optimismPortal: () => loadDriver(portalDriver), disputeGameFactory: () => loadDriver(factoryDriver) },
228
+ duskEvm: publicClient, // a viem client, or { url, fetch }
229
+ });
230
+
231
+ const status = await withdrawals.status(txHash, { submitter: account, proofGame });
232
+ // { stage: "proven_waiting", reason: "game-in-progress", readyAt: 1793589639n, readyAtExact: false, facts: { amountWei, recipient, … }, … }
233
+
234
+ const { call, claim } = await withdrawals.prepareProof(txHash, { submitter: account });
235
+ // claim: when it can be claimed if proved now; call: { contractId, fnName, fnArgs, deposit: 0n, gasLimit, decoded }
236
+ const claimCall = await withdrawals.claimCall(txHash, { submitter: account }); // once `status` says "ready_to_finalize"
237
+ ```
238
+
239
+ - Only DUSK withdrawals on the bridge's route count, as Dusk's L1 bridge pays them out (`decodeWithdrawal` of `@dusk-network/networks/duskevm`): to Dusk's messenger, relaying DuskEVM's bridge to Dusk's, paying a Dusk account, or a contract only whole lux at the address DuskEVM gives it, and no one past a u64 of lux. Anything else is refused, for its status, its proof and its claim (`WithdrawalError`, `code: "not-a-withdrawal"`).
240
+ - `stage` is where the withdrawal is for the account whose proof counts (`submitter`, a Dusk account or its Portal address; its recipient's account when none is given): "waiting_for_output", "ready_to_prove", "proven_waiting", "ready_to_finalize" or "finalized". `reason` says why it waits. A claim date is worked out as the Portal checks a claim (`claimFrom`), counted from Dusk's latest block, and is exact once the proof's game has resolved; `readyAt` is absent when the game can't be read now, so the date known so far stands. A claim is ready once the Portal takes it, its game has resolved for the defender, and its date has come: never while the game, the Portal's blacklist or the anchor state registry's word on it can't be read, nor on the Portal's own check alone, which may take just the proof and its game. A proof whose game the Portal refuses (blacklisted, retired, no longer respected, or won by the challenger) can't be claimed, whatever that check says (`proofRejected`, "proof-rejected"). `provenByOther` says another account holds a proof that stands, which this one can't claim with.
241
+ - `proofGame`, the game the last status found the proof against, puts that game's reads in the same query as the Portal's: a check is one query.
242
+ - A proof goes against a game that covers the withdrawal and has already resolved for its proposal, where the anchor state registry calls it proper and respected and it came after the respected game type last changed, so its claim date is exact when it's made; otherwise against the newest game that covers it, among the newest 64 (`gameSearchDepth`), that the Portal takes a proof against: a game the Portal blacklisted, the registry calls improper or not respected, or the challenger won is passed over for an older one, as is one whose standing can't be read. Where none of those is one the Portal takes, a status offers a proof against one that has resolved, as `prepareProof` finds it, to an account that never proved and to one whose proof the Portal refuses. A proof isn't prepared for a claimed withdrawal, nor over a proof of the account's that stands: it would cost its fee for nothing. One the Portal refuses is made again against another game it takes.
243
+ - `prepareProof` and `claimCall` refuse what would do nothing or can't be made: "claimed", "proven", "no-proof", "not-ready" (with the status), and "gas" for a withdrawal whose own gas takes the limit over the app's bound (`gasLimitUpper`, 1,000,000,000 by default). The gas limit is 50M and the withdrawal's own gas, at least 1M; the price is the app's.
244
+ - A call is encoded by the Portal's data driver the app loaded, as the network's file names and pins it, and read back before it's handed over: its arguments must say exactly the withdrawal, the game, the output root proof and the proof's nodes they were built from, as Dusk Wallet reads them (`decoded`), or it's refused ("bad-call"). It sends no DUSK along; the app signs `fnArgs` as given, with no amount and no one paid but the account itself.
245
+ - `prepareProof` gives `at`, when it was prepared: an app that shows a proof's claim date sends that proof, for as long as it chooses.
246
+ - `claims(hashes, { submitter })` reads when the withdrawals an account proved can be claimed, by their hashes: claimed, ready, a date, none, or to prove again. Its map is `/activity`'s `claims`.
247
+ - `timing()` is how long a withdrawal takes: the Portal's two delays, and how often outputs come.
248
+
249
+ ### Finding withdrawals
250
+
251
+ - `syncWithdrawals({ address, duskEvm, network, state })` reads an address's withdrawals from the bridge's own `ETHBridgeInitiated` events, the newest final blocks first, a window of 100,000 blocks at a time (narrower where the RPC asks for it), 8 requests a run. It gives the app the state to keep, and the next run carries on below and reads only what is new above. Under a final head lower than the last run's, what it covered and found above it is dropped, and read again.
252
+ - `explorerWithdrawals({ address, network, fetch })` lists the address's own calls of the bridge on DuskEVM's explorer, each with the explorer's word for it (`explorerStage`), and `withdrawalsByNonce({ nonces, network, fetch })` finds the DuskEVM transactions of withdrawals known by the nonces their proofs and claims on Dusk carry.
253
+
254
+ ### Reads and caches
255
+
256
+ - The Portal and the dispute game factory are read with the app's drivers; each game's hub and the anchor state registry raw. Reads go to the node's contracts through the app's `fetch` (its Multicall for Dusk batcher), sent again twice at most when the node answers 429, 502, 503 or 504; a refusal carries the contract's own words. `maxBatch` is how many reads one query holds (32 by default), each game checked taking 4: its status, the Portal's blacklist, and the registry's two answers. A game's status is read on its own hub, which the factory names: a game type's hub can change, and the games made before stay where they were.
257
+ - What a deployment doesn't change is read once: each game's hub, each game type's hub, each hub's clock limit. `cache`, a `Map` of text by default, is where they're kept: an app can keep them across sessions.
258
+ - Dusk's latest block comes from `latestBlock`, or the node's GraphQL. Without it, a proved withdrawal's status fails: its date can't be worked out, and no claim is built.
259
+
213
260
  ## License
214
261
 
215
262
  MPL-2.0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dusk-network/app-core",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
4
4
  "description": "Shared logic for the Dusk apps: public and shielded history, activity rows, bridge arrivals and DuskEVM withdrawals.",
5
5
  "license": "MPL-2.0",
6
6
  "type": "module",
@@ -25,6 +25,10 @@
25
25
  "types": "./src/bridge.d.ts",
26
26
  "default": "./src/bridge.js"
27
27
  },
28
+ "./duskevm-withdrawals": {
29
+ "types": "./src/duskevm-withdrawals.d.ts",
30
+ "default": "./src/duskevm-withdrawals.js"
31
+ },
28
32
  "./package.json": "./package.json"
29
33
  },
30
34
  "files": [
@@ -39,10 +43,23 @@
39
43
  "typecheck": "npx --yes --package=typescript@5.9.3 -- tsc -p test-types",
40
44
  "check:versions": "node scripts/check-versions.js",
41
45
  "check:jsr": "npx --yes jsr@0.14.3 publish --dry-run --allow-dirty",
46
+ "fetch:drivers": "node scripts/fetch-drivers.js",
42
47
  "smoke:history": "node scripts/smoke-history.js",
43
- "smoke:bridge": "node scripts/smoke-bridge.js"
48
+ "smoke:bridge": "node scripts/smoke-bridge.js",
49
+ "smoke:withdrawals": "node scripts/smoke-withdrawals.js"
44
50
  },
45
51
  "dependencies": {
46
52
  "@dusk-network/networks": "0.3.0"
53
+ },
54
+ "peerDependencies": {
55
+ "viem": "^2.55.2"
56
+ },
57
+ "peerDependenciesMeta": {
58
+ "viem": {
59
+ "optional": true
60
+ }
61
+ },
62
+ "devDependencies": {
63
+ "viem": "2.55.2"
47
64
  }
48
65
  }
package/src/bridge.d.ts CHANGED
@@ -85,7 +85,7 @@ export declare const DEPOSIT_HORIZON_MS: 7200000;
85
85
  /** Equal transfers out sent within this of each other are paid in their order, or 4 times the route's usual time: 12 hours, in milliseconds. */
86
86
  export declare const SERVICE_HORIZON_MS: 43200000;
87
87
 
88
- /** Pages of a sender's history read for its earlier equal deposits. */
88
+ /** Pages of its account's history one call reads for a transfer's earlier equal ones; a longer history is read on in the next call, from its `scan`. */
89
89
  export declare const HISTORY_PAGES: 4;
90
90
 
91
91
  /** `Transfer(address,address,uint256)`: the DUSK token's transfer that pays a transfer out. */
@@ -94,15 +94,37 @@ export declare const TRANSFER_TOPIC: "0xddf252ad1be2c89b69c2b068fc378daa952ba7f1
94
94
  /** `ETHBridgeFinalized(address,address,uint256,bytes)`: DuskEVM's bridge paying a deposit, from the depositor's DuskEVM address. */
95
95
  export declare const ETH_BRIDGE_FINALIZED_TOPIC: "0x31b2166ff604fc5672ea5df08a78081d2bc6d746cadce880747f3643d819e83d";
96
96
 
97
+ /**
98
+ * How far a transfer's own history is read: what it was read for (`of`),
99
+ * all of it from `lo` up to the transfer's block once `lo` is set, the
100
+ * range read next (`next`: its first block, its last and the page it
101
+ * carries on from), and the transfers found in it that could be equal to
102
+ * it, each with what it paid, in lux (`value`).
103
+ */
104
+ export type HistoryScan = {
105
+ of: string;
106
+ lo: number | null;
107
+ next: { from: number; to: number; page: number } | null;
108
+ found: ReadonlyArray<{ hash: string; height: number; value: string }>;
109
+ };
110
+
97
111
  /**
98
112
  * Where a search stopped: the block the next one starts at, the time of the
99
113
  * transfer it began from, the logs it searched for (`search`), and the
100
114
  * credits it found before (`seen`), which the next one counts again. A
101
115
  * search for other logs, as for another sender or amount, starts afresh.
102
- * Kept by the app with the credits it was made with, and passed back as it
103
- * is.
116
+ * With the archive, how far the transfer's own history is read
117
+ * (`history`): a history longer than one call reads is read on from there,
118
+ * before any search. Kept by the app with the credits it was made with,
119
+ * and passed back as it is.
104
120
  */
105
- export type Scan = { from: string; basis: number; search: string; seen?: ReadonlyArray<{ hash: string; block: string; at: number; sender: string | null }> };
121
+ export type Scan = {
122
+ from?: string;
123
+ basis?: number;
124
+ search?: string;
125
+ seen?: ReadonlyArray<{ hash: string; block: string; at: number; sender: string | null }>;
126
+ history?: HistoryScan;
127
+ };
106
128
 
107
129
  /**
108
130
  * Why a transfer's credit is unknown:
@@ -190,7 +212,9 @@ export type DepositArrivalOptions = {
190
212
  /**
191
213
  * `/history`'s archive, for "rpc": each sender's equal deposits in the
192
214
  * hours before its own are read from its history, those given but not
193
- * looked for too, and paid first.
215
+ * looked for too, and paid first. Each deposit reads its own hours, once
216
+ * the archive lists it there, `HISTORY_PAGES` pages a call: a deposit
217
+ * whose history is read on in the next call waits, with its `scan`.
194
218
  * Without it, or for a deposit without its sender and height, its credit
195
219
  * is a deposit's only as where they can't be told apart.
196
220
  */
@@ -279,9 +303,12 @@ export type ServiceArrivalOptions = {
279
303
  * route's account received in the hours before that named the same address,
280
304
  * in any case, and pay out the same are paid first: its history lists them,
281
305
  * and their own transactions say what each received (one given but not
282
- * looked for pays out what its own fee leaves). One that can't be
283
- * read, or a history cut short, leaves the order unclear: unsure. Throws a
284
- * TypeError without an archive, and for a network there's no file for.
306
+ * looked for pays out what its own fee leaves). One that can't be read
307
+ * leaves the order unclear: unsure. Each transfer reads its own hours of
308
+ * that history, once the archive lists it there, `HISTORY_PAGES` pages a
309
+ * call: one whose history is read on in the next call waits, with its
310
+ * `scan`. Throws a TypeError without an archive, and for a network there's
311
+ * no file for.
285
312
  */
286
313
  export declare function serviceArrivals(options: ServiceArrivalOptions): Promise<{ arrivals: Map<string, Arrival>; requests: number }>;
287
314
 
@@ -301,8 +328,15 @@ export type Migration = {
301
328
  export type MigrationPayout = {
302
329
  /** The migration's transaction hash, from the memo, lower case. */
303
330
  hash: `0x${string}`;
304
- route: string;
305
- /** What it paid, in lux. */
331
+ /**
332
+ * Its route's ID; null where its payer pays out more than one route's
333
+ * migrations, as one account set for both chains does: its transfer
334
+ * doesn't say which.
335
+ */
336
+ route: string | null;
337
+ /** The Dusk account that paid it: its route's payout account. */
338
+ payer: string;
339
+ /** What it paid, in lux: a gas refund paid to the account with it aside. */
306
340
  amount: bigint;
307
341
  /** The payout's Dusk transaction. */
308
342
  payout: string;
@@ -332,9 +366,12 @@ export type MigrationPayoutOptions = {
332
366
  * Each migration's payout on Dusk, by the migration's hash (lower case), in
333
367
  * the order given; with `entries`, the payouts there of migrations not given
334
368
  * (`unlisted`: sent from another device, or before); and the requests it
335
- * took. Each payout pays one migration. A payout of the migration of another
336
- * amount leaves it unsure, not waiting: an entry's value holds any gas
337
- * refund paid to the account; the memo index gives each payout's own.
369
+ * took. Each payout pays one migration, of the route it was given with:
370
+ * from that route's payout account, even where that account pays out
371
+ * another route's too. A payout of the migration of another amount leaves
372
+ * it unsure, not waiting: the value of an entry an earlier version kept
373
+ * holds any gas refund paid to the account (`refund` tells it apart now);
374
+ * the memo index gives each payout's own.
338
375
  */
339
376
  export declare function migrationPayouts(
340
377
  options: MigrationPayoutOptions
@@ -348,6 +385,8 @@ export type HistoryLike = {
348
385
  kind: EntryKind;
349
386
  counterparty: string | null;
350
387
  value: bigint | string;
388
+ /** Of `value`, a gas refund paid to another account than the sender. */
389
+ refund?: bigint | string | null;
351
390
  memo: string;
352
391
  payer: boolean;
353
392
  timestamp?: number | null;
@@ -391,7 +430,9 @@ export declare function serviceTransferOf(entry: HistoryLike, network: NetworkRe
391
430
  /**
392
431
  * A payout of a migration, from its entry: a transfer the account received
393
432
  * from a migration route's payout account, with a transaction hash as the
394
- * memo. Null for any other entry, and on a route without its payout account.
433
+ * memo, of its value less any gas `refund` paid to the account with it. Its
434
+ * route is null where that account pays out more than one route. Null for
435
+ * any other entry, and on a route without its payout account.
395
436
  */
396
437
  export declare function migrationPayoutOf(entry: HistoryLike, network: NetworkRef): MigrationPayout | null;
397
438