@dusk-network/app-core 0.4.0 → 0.5.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/CHANGELOG.md +24 -0
- package/README.md +10 -6
- package/package.json +5 -1
- package/src/bridge-common.js +639 -0
- package/src/bridge-explorer.d.ts +34 -0
- package/src/bridge-explorer.js +186 -0
- package/src/bridge.d.ts +78 -20
- package/src/bridge.js +274 -708
- package/src/duskevm-withdrawals.d.ts +33 -4
- package/src/duskevm-withdrawals.js +218 -12
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,30 @@
|
|
|
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.5.0]
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- `/bridge-explorer`: `depositArrivalsFromExplorer`, deposits followed on DuskEVM's explorer without the RPC path and `/history`, which `/bridge` exports too
|
|
10
|
+
- `/bridge`: a network an app builds from its own settings, for a chain there's no network file for, its fields checked as a file's are
|
|
11
|
+
- `/bridge`: `depositArrivals` takes a `cache`, where the call of each deposit a sender's history lists is kept once its block is final and its arguments are read, and isn't asked for again
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- `/bridge`: on the RPC, a deposit with its sender is followed as without the archive while the archive can't be read, and is an `error` only when the RPC can't be read either
|
|
16
|
+
- `/duskevm-withdrawals`: a game type's hub is kept in `cache` only once the factory names one, so a proof goes against a resolved game once it does
|
|
17
|
+
- `/duskevm-withdrawals`: where the RPC has no finalized block, `syncWithdrawals` reads the blocks it read within 1,800 under the head it takes as final again on a later run, rather than once
|
|
18
|
+
|
|
19
|
+
## [0.4.1]
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
- `/duskevm-withdrawals`: a withdrawal's receipt is kept in `cache` once its DuskEVM block is final, and read once
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
|
|
27
|
+
- `/bridge`: a memo of bytes that aren't text (`memoBinary`) gives no payout address, and names no migration
|
|
28
|
+
|
|
5
29
|
## [0.4.0]
|
|
6
30
|
|
|
7
31
|
### Added
|
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ Shared logic for the Dusk apps: public and shielded history, activity rows, brid
|
|
|
4
4
|
|
|
5
5
|
It has no framework and runs in a browser extension's service worker. Each part is its own entry point, so an app loads only what it uses. Apps pass in their own fetch, batching and storage; the package never chooses storage keys.
|
|
6
6
|
|
|
7
|
-
Network data (endpoints, contracts, routes) comes from [`@dusk-network/networks`](https://github.com/dusk-network/networks).
|
|
7
|
+
Network data (endpoints, contracts, routes) comes from [`@dusk-network/networks`](https://github.com/dusk-network/networks). On a chain there's no network file for, `/bridge` takes a network the app builds from its own settings.
|
|
8
8
|
|
|
9
9
|
## Install
|
|
10
10
|
|
|
@@ -31,6 +31,7 @@ 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/bridge-explorer` | Where a deposit to DuskEVM arrived, on DuskEVM's explorer alone (`depositArrivalsFromExplorer`): `/bridge`'s explorer path, for an app that reads nothing else, without the RPC path and `/history` |
|
|
34
35
|
| `@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`) |
|
|
35
36
|
|
|
36
37
|
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.
|
|
@@ -202,12 +203,15 @@ const { arrivals } = await depositArrivals({
|
|
|
202
203
|
arrivalsOf(arrivals); // Map { hash => { hash: "0x029e…", at: 1793080333000 } }: `/activity`'s `arrivals`
|
|
203
204
|
```
|
|
204
205
|
|
|
206
|
+
An app that follows deposits on DuskEVM's explorer alone imports `depositArrivalsFromExplorer` from `@dusk-network/app-core/bridge-explorer`: the same reads and results as `depositArrivals` with `via: "explorer"`, without the RPC path and `/history`. `/bridge` exports it too.
|
|
207
|
+
|
|
205
208
|
- 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.
|
|
206
209
|
- 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.
|
|
207
210
|
- 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.
|
|
208
|
-
- 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.
|
|
209
|
-
-
|
|
210
|
-
- A
|
|
211
|
+
- 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, as they are while it can't be read and on a network that doesn't name DuskEVM's bridge and Portal on Dusk: a deposit is an `error` only when DuskEVM's RPC can't be read either. A deposit given without its sender takes no credit from the sender of another equal one given. With `cache`, the app's cache of what can't change any more (a `Map` of text does), the call of each deposit its sender's history lists is kept once its block is final and its answer says all a match reads of it (that it failed, that it's another call than a deposit, or a deposit's amount and DuskEVM address), and isn't asked for again; one whose arguments come cut short is asked for again.
|
|
212
|
+
- `network` is a network file or its chain ID, or, on a chain there's no file for, an object the app builds from its settings: `{ chainId, duskEvm: { rpcUrl, blockscoutApiUrl, predeploys: { l2StandardBridge }, contracts } }`, each as a network file has it, DuskEVM's `contracts` on Dusk only for its senders' histories. Its fields are checked as a file's are: an address that isn't 0x and 40 hex digits, a URL that isn't http(s) or ends with a slash, or a contract ID that isn't 64 lower-case hex digits is a TypeError. It has no routes to other chains unless it gives them as a file does: transfers out and migrations there are `no-route`.
|
|
213
|
+
- 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, as text, 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.
|
|
214
|
+
- 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, as text: a memo of bytes that aren't text gives none), 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.
|
|
211
215
|
- 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`.
|
|
212
216
|
- A route without its payout account or address in the network's file pays out nothing that can be told apart: `no-route`.
|
|
213
217
|
- An RPC search starts at a block from before the transfer, 10 minutes before it as the chain's pace says, checked by its time and stepped back until it is, and reads 8 windows at most: of 5,000 blocks, twice as many after each one the RPC answers whole, up to 100,000, and fewer where it asks for it, the width kept in the search's `scan` from call to call. A call reads a chain's head and pace once for all its searches. Once a deposit's windows are over and read, a search without its sender's history reads no further. It reads each block more than once: its window at the head reaches back 1,000 blocks, so the newest blocks are read again on each call, and once it has reached the head with a transfer not arrived, it reads its blocks again from the first, a pass at a time with the windows a call has left (one at least while a pass is under way, so 9 where the search ahead takes all 8): the first a minute later, each one after as long again as it has been since it reached the head. Where a later equal transfer, still without its credit, could have had the credit an earlier one takes, the earlier one waits until the blocks are read again 8 times since the credit was found, one pass after the other. 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. The block times the app keeps (`blockTimes`) are a chain's: for transfers out, a cache a chain by its chain ID, or one cache where the network's service routes are all on one chain.
|
|
@@ -252,13 +256,13 @@ const claimCall = await withdrawals.claimCall(txHash, { submitter: account }); /
|
|
|
252
256
|
|
|
253
257
|
### Finding withdrawals
|
|
254
258
|
|
|
255
|
-
- `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, the head's among them. It gives the app the state to keep, and the next run carries on below. Each run reads the newest 50,000 final blocks, the new ones first, in as many windows as they take, though in half its requests at most (while older blocks are still to be read, only once the final head has moved). Under a final head lower than the last run's, what it covered and found above it is dropped, and read again. It reads each final block more than once: once it has covered them all, it reads them again from the newest down, a pass at a time with the requests a run has left, the first an hour later and each one after as long again as it has been since. A withdrawal is found once.
|
|
259
|
+
- `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, the head's among them. It gives the app the state to keep, and the next run carries on below. Each run reads the newest 50,000 final blocks, the new ones first, in as many windows as they take, though in half its requests at most (while older blocks are still to be read, only once the final head has moved). Under a final head lower than the last run's, what it covered and found above it is dropped, and read again. Where the RPC has no finalized block, the latest less 1,800 blocks (`UNFINALIZED_MARGIN`, about an hour) is the final head, and the blocks a run reads within 1,800 under it are read again once they're under a later run's (`open`, in the state): among the newest blocks a run reads again, or, once all the blocks are covered, before its passes over them, with the requests it has left. A deeper reorg there is found, and older blocks are read as they would be without it. It reads each final block more than once: once it has covered them all, it reads them again from the newest down, a pass at a time with the requests a run has left, the first an hour later and each one after as long again as it has been since. A withdrawal is found once.
|
|
256
260
|
- `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.
|
|
257
261
|
|
|
258
262
|
### Reads and caches
|
|
259
263
|
|
|
260
264
|
- 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.
|
|
261
|
-
- What a deployment doesn't change is read once: each game's hub
|
|
265
|
+
- What a deployment doesn't change is read once: each game's hub and each game type's hub, once the factory names one, each hub's clock limit, and a withdrawal's receipt once its DuskEVM block is final. A receipt is read after DuskEVM's finalized block, one read for the receipts read at the same time, and kept when its block is at or under that one; one that isn't final yet, or isn't a withdrawal's, is read again each time. `cache`, a `Map` of text by default, is where they're kept: an app can keep them across sessions.
|
|
262
266
|
- 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.
|
|
263
267
|
|
|
264
268
|
## License
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dusk-network/app-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
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
|
+
"./bridge-explorer": {
|
|
29
|
+
"types": "./src/bridge-explorer.d.ts",
|
|
30
|
+
"default": "./src/bridge-explorer.js"
|
|
31
|
+
},
|
|
28
32
|
"./duskevm-withdrawals": {
|
|
29
33
|
"types": "./src/duskevm-withdrawals.d.ts",
|
|
30
34
|
"default": "./src/duskevm-withdrawals.js"
|