@aztec/ethereum 0.0.1-commit.f504929 → 0.0.1-commit.f5d02921e
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/dest/client.d.ts +10 -2
- package/dest/client.d.ts.map +1 -1
- package/dest/client.js +13 -7
- package/dest/contracts/multicall.d.ts +51 -2
- package/dest/contracts/multicall.d.ts.map +1 -1
- package/dest/contracts/multicall.js +85 -0
- package/dest/contracts/rollup.d.ts +41 -5
- package/dest/contracts/rollup.d.ts.map +1 -1
- package/dest/contracts/rollup.js +197 -15
- package/dest/l1_artifacts.d.ts +69 -69
- package/dest/l1_reader.d.ts +3 -1
- package/dest/l1_reader.d.ts.map +1 -1
- package/dest/l1_reader.js +6 -1
- package/dest/l1_tx_utils/l1_tx_utils.d.ts +3 -1
- package/dest/l1_tx_utils/l1_tx_utils.d.ts.map +1 -1
- package/dest/l1_tx_utils/l1_tx_utils.js +33 -25
- package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts +1 -1
- package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts.map +1 -1
- package/dest/l1_tx_utils/readonly_l1_tx_utils.js +8 -4
- package/dest/publisher_manager.d.ts +21 -7
- package/dest/publisher_manager.d.ts.map +1 -1
- package/dest/publisher_manager.js +81 -7
- package/dest/test/chain_monitor.d.ts +22 -3
- package/dest/test/chain_monitor.d.ts.map +1 -1
- package/dest/test/chain_monitor.js +33 -2
- package/dest/test/eth_cheat_codes.d.ts +6 -4
- package/dest/test/eth_cheat_codes.d.ts.map +1 -1
- package/dest/test/eth_cheat_codes.js +6 -4
- package/dest/test/start_anvil.d.ts +15 -1
- package/dest/test/start_anvil.d.ts.map +1 -1
- package/dest/test/start_anvil.js +17 -2
- package/dest/utils.d.ts +1 -1
- package/dest/utils.d.ts.map +1 -1
- package/dest/utils.js +16 -12
- package/package.json +5 -5
- package/src/client.ts +10 -2
- package/src/contracts/multicall.ts +65 -1
- package/src/contracts/rollup.ts +224 -23
- package/src/l1_reader.ts +13 -1
- package/src/l1_tx_utils/l1_tx_utils.ts +23 -13
- package/src/l1_tx_utils/readonly_l1_tx_utils.ts +8 -4
- package/src/publisher_manager.ts +105 -10
- package/src/test/chain_monitor.ts +60 -3
- package/src/test/eth_cheat_codes.ts +6 -4
- package/src/test/start_anvil.ts +33 -2
- package/src/utils.ts +17 -14
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { TestDateProvider } from '@aztec/foundation/timer';
|
|
1
2
|
/** Minimal interface matching the @viem/anvil Anvil shape used by callers. */
|
|
2
3
|
export interface Anvil {
|
|
3
4
|
readonly port: number;
|
|
@@ -17,10 +18,23 @@ export declare function startAnvil(opts?: {
|
|
|
17
18
|
chainId?: number;
|
|
18
19
|
/** The hardfork to use (e.g. 'cancun', 'latest'). */
|
|
19
20
|
hardfork?: string;
|
|
21
|
+
/**
|
|
22
|
+
* Number of slots per epoch used by anvil to compute the 'finalized' and 'safe' block tags.
|
|
23
|
+
* Anvil reports `finalized = latest - slotsInAnEpoch * 2`.
|
|
24
|
+
* Defaults to 1 so the finalized block advances immediately, making tests that check
|
|
25
|
+
* L1-finality-based logic work without needing hundreds of mined blocks.
|
|
26
|
+
*/
|
|
27
|
+
slotsInAnEpoch?: number;
|
|
28
|
+
/**
|
|
29
|
+
* If provided, the date provider will be synced to anvil's block time on every mined block.
|
|
30
|
+
* This keeps the dateProvider in lockstep with anvil's chain time, avoiding drift between
|
|
31
|
+
* the wall clock and the L1 chain when computing L1 slot timestamps.
|
|
32
|
+
*/
|
|
33
|
+
dateProvider?: TestDateProvider;
|
|
20
34
|
}): Promise<{
|
|
21
35
|
anvil: Anvil;
|
|
22
36
|
methodCalls?: string[];
|
|
23
37
|
rpcUrl: string;
|
|
24
38
|
stop: () => Promise<void>;
|
|
25
39
|
}>;
|
|
26
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
40
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RhcnRfYW52aWwuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy90ZXN0L3N0YXJ0X2FudmlsLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUVBLE9BQU8sS0FBSyxFQUFFLGdCQUFnQixFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFNaEUsOEVBQThFO0FBQzlFLE1BQU0sV0FBVyxLQUFLO0lBQ3BCLFFBQVEsQ0FBQyxJQUFJLEVBQUUsTUFBTSxDQUFDO0lBQ3RCLFFBQVEsQ0FBQyxJQUFJLEVBQUUsTUFBTSxDQUFDO0lBQ3RCLFFBQVEsQ0FBQyxNQUFNLEVBQUUsV0FBVyxHQUFHLE1BQU0sQ0FBQztJQUN0QyxJQUFJLElBQUksT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDO0NBQ3ZCO0FBRUQ7O0dBRUc7QUFDSCx3QkFBc0IsVUFBVSxDQUM5QixJQUFJLEdBQUU7SUFDSixJQUFJLENBQUMsRUFBRSxNQUFNLENBQUM7SUFDZCxXQUFXLENBQUMsRUFBRSxNQUFNLENBQUM7SUFDckIsR0FBRyxDQUFDLEVBQUUsT0FBTyxDQUFDO0lBQ2Qsa0JBQWtCLENBQUMsRUFBRSxPQUFPLENBQUM7SUFDN0IsUUFBUSxDQUFDLEVBQUUsTUFBTSxDQUFDO0lBQ2xCLE9BQU8sQ0FBQyxFQUFFLE1BQU0sQ0FBQztJQUNqQixxREFBcUQ7SUFDckQsUUFBUSxDQUFDLEVBQUUsTUFBTSxDQUFDO0lBQ2xCOzs7OztPQUtHO0lBQ0gsY0FBYyxDQUFDLEVBQUUsTUFBTSxDQUFDO0lBQ3hCOzs7O09BSUc7SUFDSCxZQUFZLENBQUMsRUFBRSxnQkFBZ0IsQ0FBQztDQUM1QixHQUNMLE9BQU8sQ0FBQztJQUFFLEtBQUssRUFBRSxLQUFLLENBQUM7SUFBQyxXQUFXLENBQUMsRUFBRSxNQUFNLEVBQUUsQ0FBQztJQUFDLE1BQU0sRUFBRSxNQUFNLENBQUM7SUFBQyxJQUFJLEVBQUUsTUFBTSxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUE7Q0FBRSxDQUFDLENBK0g5RiJ9
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"start_anvil.d.ts","sourceRoot":"","sources":["../../src/test/start_anvil.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"start_anvil.d.ts","sourceRoot":"","sources":["../../src/test/start_anvil.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAMhE,8EAA8E;AAC9E,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAAC;IACtC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAED;;GAEG;AACH,wBAAsB,UAAU,CAC9B,IAAI,GAAE;IACJ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qDAAqD;IACrD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;OAIG;IACH,YAAY,CAAC,EAAE,gBAAgB,CAAC;CAC5B,GACL,OAAO,CAAC;IAAE,KAAK,EAAE,KAAK,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAC,CA+H9F"}
|
package/dest/test/start_anvil.js
CHANGED
|
@@ -30,6 +30,7 @@ import { dirname, resolve } from 'path';
|
|
|
30
30
|
if (opts.hardfork !== undefined) {
|
|
31
31
|
args.push('--hardfork', opts.hardfork);
|
|
32
32
|
}
|
|
33
|
+
args.push('--slots-in-an-epoch', String(opts.slotsInAnEpoch ?? 1));
|
|
33
34
|
const child = spawn(anvilBinary, args, {
|
|
34
35
|
stdio: [
|
|
35
36
|
'ignore',
|
|
@@ -74,12 +75,15 @@ import { dirname, resolve } from 'path';
|
|
|
74
75
|
child.stderr?.on('data', onStderr);
|
|
75
76
|
child.once('close', onClose);
|
|
76
77
|
});
|
|
77
|
-
// Continue piping for logging
|
|
78
|
-
if (logger || opts.captureMethodCalls) {
|
|
78
|
+
// Continue piping for logging, method-call capture, and/or dateProvider sync after startup.
|
|
79
|
+
if (logger || opts.captureMethodCalls || opts.dateProvider) {
|
|
79
80
|
child.stdout?.on('data', (data)=>{
|
|
80
81
|
const text = data.toString();
|
|
81
82
|
logger?.debug(text.trim());
|
|
82
83
|
methodCalls?.push(...text.match(/eth_[^\s]+/g) || []);
|
|
84
|
+
if (opts.dateProvider) {
|
|
85
|
+
syncDateProviderFromAnvilOutput(text, opts.dateProvider);
|
|
86
|
+
}
|
|
83
87
|
});
|
|
84
88
|
child.stderr?.on('data', (data)=>{
|
|
85
89
|
logger?.debug(data.toString().trim());
|
|
@@ -124,6 +128,17 @@ import { dirname, resolve } from 'path';
|
|
|
124
128
|
rpcUrl: `http://127.0.0.1:${port}`
|
|
125
129
|
};
|
|
126
130
|
}
|
|
131
|
+
/** Extracts block time from anvil stdout and syncs the dateProvider. */ function syncDateProviderFromAnvilOutput(text, dateProvider) {
|
|
132
|
+
// Anvil logs mined blocks as:
|
|
133
|
+
// Block Time: "Fri, 20 Mar 2026 02:10:46 +0000"
|
|
134
|
+
const match = text.match(/Block Time:\s*"([^"]+)"/);
|
|
135
|
+
if (match) {
|
|
136
|
+
const blockTimeMs = new Date(match[1]).getTime();
|
|
137
|
+
if (!isNaN(blockTimeMs)) {
|
|
138
|
+
dateProvider.setTime(blockTimeMs);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
127
142
|
/** Send SIGTERM, wait up to 5 s, then SIGKILL. All timers are always cleared. */ function killChild(child) {
|
|
128
143
|
return new Promise((resolve)=>{
|
|
129
144
|
if (child.exitCode !== null || child.killed) {
|
package/dest/utils.d.ts
CHANGED
|
@@ -35,4 +35,4 @@ export declare function isBlobTransaction(tx: FormattedTransaction): tx is Forma
|
|
|
35
35
|
* Calculates a percentile from an array of bigints
|
|
36
36
|
*/
|
|
37
37
|
export declare function calculatePercentile(values: bigint[], percentile: number): bigint;
|
|
38
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
38
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXRpbHMuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy91dGlscy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssRUFBRSxFQUFFLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUN6RCxPQUFPLEtBQUssRUFBRSxNQUFNLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUdwRCxPQUFPLEVBQ0wsS0FBSyxHQUFHLEVBR1IsS0FBSyxpQkFBaUIsRUFFdEIsS0FBSyx3QkFBd0IsRUFDN0IsS0FBSyxvQkFBb0IsRUFDekIsS0FBSyxHQUFHLEVBQ1IsS0FBSyxHQUFHLEVBR1QsTUFBTSxNQUFNLENBQUM7QUFHZCxNQUFNLFdBQVcsT0FBTztJQUN0QixXQUFXLEVBQUUsRUFBRSxDQUFDO0lBQ2hCLFdBQVcsRUFBRSxFQUFFLENBQUM7SUFDaEIsV0FBVyxFQUFFLEdBQUcsQ0FBQztJQUNqQixnQkFBZ0IsRUFBRSxNQUFNLENBQUM7Q0FDMUI7QUFFRCxxQkFBYSxrQkFBbUIsU0FBUSxLQUFLO0lBQzNDLFlBQVksQ0FBQyxFQUFFLEdBQUcsRUFBRSxDQUFDO0lBRXJCLFlBQVksT0FBTyxFQUFFLE1BQU0sRUFBRSxZQUFZLENBQUMsRUFBRSxHQUFHLEVBQUUsRUFJaEQ7Q0FDRjtBQUVELHdCQUFnQixZQUFZLENBQzFCLEtBQUssQ0FBQyxJQUFJLFNBQVMsR0FBRyxHQUFHLFNBQVMsT0FBTyxFQUFFLEVBQzNDLFVBQVUsU0FBUyxpQkFBaUIsQ0FBQyxJQUFJLENBQUMsRUFDMUMsVUFBVSxHQUFHLHdCQUF3QixDQUFDLElBQUksRUFBRSxVQUFVLEVBQUUsR0FBRyxFQUFFLEVBQUUsU0FBUyxFQUFFLElBQUksQ0FBQyxFQUUvRSxJQUFJLEVBQUUsR0FBRyxFQUFFLEVBQ1gsT0FBTyxFQUFFLEdBQUcsRUFDWixHQUFHLEVBQUUsSUFBSSxFQUNULFNBQVMsRUFBRSxVQUFVLEVBQ3JCLE1BQU0sQ0FBQyxFQUFFLENBQUMsR0FBRyxFQUFFLFVBQVUsS0FBSyxPQUFPLEVBQ3JDLE1BQU0sQ0FBQyxFQUFFLE1BQU0sR0FDZCxVQUFVLENBTVo7QUFFRCx3QkFBZ0IsZUFBZSxDQUM3QixLQUFLLENBQUMsSUFBSSxTQUFTLEdBQUcsR0FBRyxTQUFTLE9BQU8sRUFBRSxFQUMzQyxVQUFVLFNBQVMsaUJBQWlCLENBQUMsSUFBSSxDQUFDLEVBQzFDLFVBQVUsR0FBRyx3QkFBd0IsQ0FBQyxJQUFJLEVBQUUsVUFBVSxFQUFFLEdBQUcsRUFBRSxFQUFFLFNBQVMsRUFBRSxJQUFJLENBQUMsRUFFL0UsSUFBSSxFQUFFLEdBQUcsRUFBRSxFQUNYLE9BQU8sRUFBRSxHQUFHLEVBQ1osR0FBRyxFQUFFLElBQUksRUFDVCxTQUFTLEVBQUUsVUFBVSxFQUNyQixNQUFNLENBQUMsRUFBRSxDQUFDLEdBQUcsRUFBRSxVQUFVLEtBQUssT0FBTyxFQUNyQyxNQUFNLENBQUMsRUFBRSxNQUFNLEdBQ2QsVUFBVSxHQUFHLFNBQVMsQ0FnQnhCO0FBRUQsd0JBQWdCLHFCQUFxQixDQUFDLEdBQUcsRUFBRSxHQUFHLE9BVzdDO0FBRUQsd0JBQWdCLFNBQVMsQ0FBQyxJQUFJLEVBQUUsR0FBRyxFQUFFLEdBQUcsR0FBRyxDQWUxQztBQTJFRDs7Ozs7R0FLRztBQUNILHdCQUFnQixlQUFlLENBQUMsS0FBSyxFQUFFLEdBQUcsRUFBRSxHQUFHLEdBQUUsR0FBZSxHQUFHLGtCQUFrQixDQTREcEY7QUF5QkQsd0JBQWdCLHFCQUFxQixDQUFDLEdBQUcsRUFBRSxHQUFHLHNCQWE3QztBQUVEOzs7R0FHRztBQUNILHdCQUFnQixpQkFBaUIsQ0FBQyxFQUFFLEVBQUUsb0JBQW9CLEdBQUcsRUFBRSxJQUFJLG9CQUFvQixHQUFHO0lBQ3hGLGdCQUFnQixFQUFFLE1BQU0sQ0FBQztJQUN6QixtQkFBbUIsRUFBRSxTQUFTLEdBQUcsRUFBRSxDQUFDO0NBQ3JDLENBT0E7QUFFRDs7R0FFRztBQUNILHdCQUFnQixtQkFBbUIsQ0FBQyxNQUFNLEVBQUUsTUFBTSxFQUFFLEVBQUUsVUFBVSxFQUFFLE1BQU0sR0FBRyxNQUFNLENBT2hGIn0=
|
package/dest/utils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,gCAAgC,CAAC;AACzD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAGpD,OAAO,EACL,KAAK,GAAG,EAGR,KAAK,iBAAiB,EAEtB,KAAK,wBAAwB,EAC7B,KAAK,oBAAoB,EACzB,KAAK,GAAG,EACR,KAAK,GAAG,EAGT,MAAM,MAAM,CAAC;AAGd,MAAM,WAAW,OAAO;IACtB,WAAW,EAAE,EAAE,CAAC;IAChB,WAAW,EAAE,EAAE,CAAC;IAChB,WAAW,EAAE,GAAG,CAAC;IACjB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,YAAY,CAAC,EAAE,GAAG,EAAE,CAAC;IAErB,YAAY,OAAO,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,GAAG,EAAE,EAIhD;CACF;AAED,wBAAgB,YAAY,CAC1B,KAAK,CAAC,IAAI,SAAS,GAAG,GAAG,SAAS,OAAO,EAAE,EAC3C,UAAU,SAAS,iBAAiB,CAAC,IAAI,CAAC,EAC1C,UAAU,GAAG,wBAAwB,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,EAE/E,IAAI,EAAE,GAAG,EAAE,EACX,OAAO,EAAE,GAAG,EACZ,GAAG,EAAE,IAAI,EACT,SAAS,EAAE,UAAU,EACrB,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,UAAU,KAAK,OAAO,EACrC,MAAM,CAAC,EAAE,MAAM,GACd,UAAU,CAMZ;AAED,wBAAgB,eAAe,CAC7B,KAAK,CAAC,IAAI,SAAS,GAAG,GAAG,SAAS,OAAO,EAAE,EAC3C,UAAU,SAAS,iBAAiB,CAAC,IAAI,CAAC,EAC1C,UAAU,GAAG,wBAAwB,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,EAE/E,IAAI,EAAE,GAAG,EAAE,EACX,OAAO,EAAE,GAAG,EACZ,GAAG,EAAE,IAAI,EACT,SAAS,EAAE,UAAU,EACrB,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,UAAU,KAAK,OAAO,EACrC,MAAM,CAAC,EAAE,MAAM,GACd,UAAU,GAAG,SAAS,CAgBxB;AAED,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,GAAG,OAW7C;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,GAAG,CAe1C;
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,gCAAgC,CAAC;AACzD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAGpD,OAAO,EACL,KAAK,GAAG,EAGR,KAAK,iBAAiB,EAEtB,KAAK,wBAAwB,EAC7B,KAAK,oBAAoB,EACzB,KAAK,GAAG,EACR,KAAK,GAAG,EAGT,MAAM,MAAM,CAAC;AAGd,MAAM,WAAW,OAAO;IACtB,WAAW,EAAE,EAAE,CAAC;IAChB,WAAW,EAAE,EAAE,CAAC;IAChB,WAAW,EAAE,GAAG,CAAC;IACjB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,YAAY,CAAC,EAAE,GAAG,EAAE,CAAC;IAErB,YAAY,OAAO,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,GAAG,EAAE,EAIhD;CACF;AAED,wBAAgB,YAAY,CAC1B,KAAK,CAAC,IAAI,SAAS,GAAG,GAAG,SAAS,OAAO,EAAE,EAC3C,UAAU,SAAS,iBAAiB,CAAC,IAAI,CAAC,EAC1C,UAAU,GAAG,wBAAwB,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,EAE/E,IAAI,EAAE,GAAG,EAAE,EACX,OAAO,EAAE,GAAG,EACZ,GAAG,EAAE,IAAI,EACT,SAAS,EAAE,UAAU,EACrB,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,UAAU,KAAK,OAAO,EACrC,MAAM,CAAC,EAAE,MAAM,GACd,UAAU,CAMZ;AAED,wBAAgB,eAAe,CAC7B,KAAK,CAAC,IAAI,SAAS,GAAG,GAAG,SAAS,OAAO,EAAE,EAC3C,UAAU,SAAS,iBAAiB,CAAC,IAAI,CAAC,EAC1C,UAAU,GAAG,wBAAwB,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,EAE/E,IAAI,EAAE,GAAG,EAAE,EACX,OAAO,EAAE,GAAG,EACZ,GAAG,EAAE,IAAI,EACT,SAAS,EAAE,UAAU,EACrB,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,UAAU,KAAK,OAAO,EACrC,MAAM,CAAC,EAAE,MAAM,GACd,UAAU,GAAG,SAAS,CAgBxB;AAED,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,GAAG,OAW7C;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,GAAG,CAe1C;AA2ED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,GAAE,GAAe,GAAG,kBAAkB,CA4DpF;AAyBD,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,GAAG,sBAa7C;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,oBAAoB,GAAG,EAAE,IAAI,oBAAoB,GAAG;IACxF,gBAAgB,EAAE,MAAM,CAAC;IACzB,mBAAmB,EAAE,SAAS,GAAG,EAAE,CAAC;CACrC,CAOA;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAOhF"}
|
package/dest/utils.js
CHANGED
|
@@ -112,6 +112,19 @@ function getNestedErrorData(error) {
|
|
|
112
112
|
// Not found
|
|
113
113
|
return undefined;
|
|
114
114
|
}
|
|
115
|
+
/**
|
|
116
|
+
* Truncates an error message to a safe length for log renderers.
|
|
117
|
+
* LogExplorer can only render up to 2500 characters in its summary view.
|
|
118
|
+
* We cap at 2000 to leave room for decorating context added by callers.
|
|
119
|
+
*/ function truncateErrorMessage(message) {
|
|
120
|
+
const MAX = 2000;
|
|
121
|
+
const CHUNK = 950;
|
|
122
|
+
if (message.length <= MAX) {
|
|
123
|
+
return message;
|
|
124
|
+
}
|
|
125
|
+
const truncated = message.length - 2 * CHUNK;
|
|
126
|
+
return message.slice(0, CHUNK) + `...${truncated} characters truncated...` + message.slice(-CHUNK);
|
|
127
|
+
}
|
|
115
128
|
/**
|
|
116
129
|
* Formats a Viem error into a FormattedViemError instance.
|
|
117
130
|
* @param error - The error to format.
|
|
@@ -162,18 +175,9 @@ function getNestedErrorData(error) {
|
|
|
162
175
|
}
|
|
163
176
|
// If it's a regular Error instance, return it with its message
|
|
164
177
|
if (error instanceof Error) {
|
|
165
|
-
return new FormattedViemError(error.message, error?.metaMessages);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
const length = body.length;
|
|
169
|
-
// LogExplorer can only render up to 2500 characters in it's summary view. Try to keep the whole message below this number
|
|
170
|
-
// Limit the error to 2000 chacaters in order to allow code higher up to decorate this error with extra details (up to 500 characters)
|
|
171
|
-
if (length > 2000) {
|
|
172
|
-
const chunk = 950;
|
|
173
|
-
const truncated = length - 2 * chunk;
|
|
174
|
-
return new FormattedViemError(body.slice(0, chunk) + `...${truncated} characters truncated...` + body.slice(-1 * chunk));
|
|
175
|
-
}
|
|
176
|
-
return new FormattedViemError(body);
|
|
178
|
+
return new FormattedViemError(truncateErrorMessage(error.message), error?.metaMessages);
|
|
179
|
+
}
|
|
180
|
+
return new FormattedViemError(truncateErrorMessage(String(error)));
|
|
177
181
|
}
|
|
178
182
|
function stripAbis(obj) {
|
|
179
183
|
if (!obj || typeof obj !== 'object') {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aztec/ethereum",
|
|
3
|
-
"version": "0.0.1-commit.
|
|
3
|
+
"version": "0.0.1-commit.f5d02921e",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
"./account": "./dest/account.js",
|
|
@@ -50,10 +50,10 @@
|
|
|
50
50
|
"../package.common.json"
|
|
51
51
|
],
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"@aztec/blob-lib": "0.0.1-commit.
|
|
54
|
-
"@aztec/constants": "0.0.1-commit.
|
|
55
|
-
"@aztec/foundation": "0.0.1-commit.
|
|
56
|
-
"@aztec/l1-artifacts": "0.0.1-commit.
|
|
53
|
+
"@aztec/blob-lib": "0.0.1-commit.f5d02921e",
|
|
54
|
+
"@aztec/constants": "0.0.1-commit.f5d02921e",
|
|
55
|
+
"@aztec/foundation": "0.0.1-commit.f5d02921e",
|
|
56
|
+
"@aztec/l1-artifacts": "0.0.1-commit.f5d02921e",
|
|
57
57
|
"dotenv": "^16.0.3",
|
|
58
58
|
"lodash.chunk": "^4.2.0",
|
|
59
59
|
"lodash.pickby": "^4.5.0",
|
package/src/client.ts
CHANGED
|
@@ -25,10 +25,17 @@ type Config = {
|
|
|
25
25
|
l1ChainId: number;
|
|
26
26
|
/** The polling interval viem uses in ms */
|
|
27
27
|
viemPollingIntervalMS?: number;
|
|
28
|
+
/** Timeout for HTTP requests to the L1 RPC node in ms. */
|
|
29
|
+
l1HttpTimeoutMS?: number;
|
|
28
30
|
};
|
|
29
31
|
|
|
30
32
|
export type { Config as EthereumClientConfig };
|
|
31
33
|
|
|
34
|
+
/** Creates a viem fallback HTTP transport for the given L1 RPC URLs. */
|
|
35
|
+
export function makeL1HttpTransport(rpcUrls: string[], opts?: { timeout?: number }) {
|
|
36
|
+
return fallback(rpcUrls.map(url => http(url, { batch: false, timeout: opts?.timeout })));
|
|
37
|
+
}
|
|
38
|
+
|
|
32
39
|
// TODO: Use these methods to abstract the creation of viem clients.
|
|
33
40
|
|
|
34
41
|
/** Returns a viem public client given the L1 config. */
|
|
@@ -36,7 +43,7 @@ export function getPublicClient(config: Config): ViemPublicClient {
|
|
|
36
43
|
const chain = createEthereumChain(config.l1RpcUrls, config.l1ChainId);
|
|
37
44
|
return createPublicClient({
|
|
38
45
|
chain: chain.chainInfo,
|
|
39
|
-
transport:
|
|
46
|
+
transport: makeL1HttpTransport(config.l1RpcUrls, { timeout: config.l1HttpTimeoutMS }),
|
|
40
47
|
pollingInterval: config.viemPollingIntervalMS,
|
|
41
48
|
});
|
|
42
49
|
}
|
|
@@ -77,6 +84,7 @@ export function createExtendedL1Client(
|
|
|
77
84
|
chain: Chain = foundry,
|
|
78
85
|
pollingIntervalMS?: number,
|
|
79
86
|
addressIndex?: number,
|
|
87
|
+
opts?: { httpTimeoutMS?: number },
|
|
80
88
|
): ExtendedViemWalletClient {
|
|
81
89
|
const hdAccount =
|
|
82
90
|
typeof mnemonicOrPrivateKeyOrHdAccount === 'string'
|
|
@@ -88,7 +96,7 @@ export function createExtendedL1Client(
|
|
|
88
96
|
const extendedClient = createWalletClient({
|
|
89
97
|
account: hdAccount,
|
|
90
98
|
chain,
|
|
91
|
-
transport:
|
|
99
|
+
transport: makeL1HttpTransport(rpcUrls, { timeout: opts?.httpTimeoutMS }),
|
|
92
100
|
pollingInterval: pollingIntervalMS,
|
|
93
101
|
}).extend(publicActions);
|
|
94
102
|
|
|
@@ -2,7 +2,7 @@ import { toHex as toPaddedHex } from '@aztec/foundation/bigint-buffer';
|
|
|
2
2
|
import { TimeoutError } from '@aztec/foundation/error';
|
|
3
3
|
import type { Logger } from '@aztec/foundation/log';
|
|
4
4
|
|
|
5
|
-
import { type EncodeFunctionDataParameters, type Hex, encodeFunctionData, multicall3Abi } from 'viem';
|
|
5
|
+
import { type Address, type EncodeFunctionDataParameters, type Hex, encodeFunctionData, multicall3Abi } from 'viem';
|
|
6
6
|
|
|
7
7
|
import type { L1BlobInputs, L1TxConfig, L1TxRequest, L1TxUtils } from '../l1_tx_utils/index.js';
|
|
8
8
|
import type { ExtendedViemWalletClient } from '../types.js';
|
|
@@ -11,6 +11,39 @@ import { RollupContract } from './rollup.js';
|
|
|
11
11
|
|
|
12
12
|
export const MULTI_CALL_3_ADDRESS = '0xcA11bde05977b3631167028862bE2a173976CA11' as const;
|
|
13
13
|
|
|
14
|
+
/** ABI fragment for aggregate3Value — not included in viem's multicall3Abi. */
|
|
15
|
+
export const aggregate3ValueAbi = [
|
|
16
|
+
{
|
|
17
|
+
inputs: [
|
|
18
|
+
{
|
|
19
|
+
components: [
|
|
20
|
+
{ internalType: 'address', name: 'target', type: 'address' },
|
|
21
|
+
{ internalType: 'bool', name: 'allowFailure', type: 'bool' },
|
|
22
|
+
{ internalType: 'uint256', name: 'value', type: 'uint256' },
|
|
23
|
+
{ internalType: 'bytes', name: 'callData', type: 'bytes' },
|
|
24
|
+
],
|
|
25
|
+
internalType: 'struct Multicall3.Call3Value[]',
|
|
26
|
+
name: 'calls',
|
|
27
|
+
type: 'tuple[]',
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
name: 'aggregate3Value',
|
|
31
|
+
outputs: [
|
|
32
|
+
{
|
|
33
|
+
components: [
|
|
34
|
+
{ internalType: 'bool', name: 'success', type: 'bool' },
|
|
35
|
+
{ internalType: 'bytes', name: 'returnData', type: 'bytes' },
|
|
36
|
+
],
|
|
37
|
+
internalType: 'struct Multicall3.Result[]',
|
|
38
|
+
name: 'returnData',
|
|
39
|
+
type: 'tuple[]',
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
stateMutability: 'payable',
|
|
43
|
+
type: 'function',
|
|
44
|
+
},
|
|
45
|
+
] as const;
|
|
46
|
+
|
|
14
47
|
export class Multicall3 {
|
|
15
48
|
static async forward(
|
|
16
49
|
requests: L1TxRequest[],
|
|
@@ -122,6 +155,37 @@ export class Multicall3 {
|
|
|
122
155
|
throw err;
|
|
123
156
|
}
|
|
124
157
|
}
|
|
158
|
+
|
|
159
|
+
/** Batch multiple value transfers into a single aggregate3Value call on Multicall3. */
|
|
160
|
+
static async forwardValue(calls: { to: Address; value: bigint }[], l1TxUtils: L1TxUtils, logger: Logger) {
|
|
161
|
+
const args = calls.map(c => ({
|
|
162
|
+
target: c.to,
|
|
163
|
+
allowFailure: false,
|
|
164
|
+
value: c.value,
|
|
165
|
+
callData: '0x' as Hex,
|
|
166
|
+
}));
|
|
167
|
+
|
|
168
|
+
const data = encodeFunctionData({
|
|
169
|
+
abi: aggregate3ValueAbi,
|
|
170
|
+
functionName: 'aggregate3Value',
|
|
171
|
+
args: [args],
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
const totalValue = calls.reduce((sum, c) => sum + c.value, 0n);
|
|
175
|
+
|
|
176
|
+
logger.info(`Sending aggregate3Value with ${calls.length} calls`, { totalValue });
|
|
177
|
+
const { receipt } = await l1TxUtils.sendAndMonitorTransaction({
|
|
178
|
+
to: MULTI_CALL_3_ADDRESS,
|
|
179
|
+
data,
|
|
180
|
+
value: totalValue,
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
if (receipt.status !== 'success') {
|
|
184
|
+
throw new Error(`aggregate3Value transaction reverted: ${receipt.transactionHash}`);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return { receipt };
|
|
188
|
+
}
|
|
125
189
|
}
|
|
126
190
|
|
|
127
191
|
export async function deployMulticall3(l1Client: ExtendedViemWalletClient, logger: Logger) {
|
package/src/contracts/rollup.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { Fr } from '@aztec/foundation/curves/bn254';
|
|
|
4
4
|
import { memoize } from '@aztec/foundation/decorators';
|
|
5
5
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
6
6
|
import type { ViemSignature } from '@aztec/foundation/eth-signature';
|
|
7
|
+
import { createLogger } from '@aztec/foundation/log';
|
|
7
8
|
import { makeBackoff, retry } from '@aztec/foundation/retry';
|
|
8
9
|
import { EscapeHatchAbi } from '@aztec/l1-artifacts/EscapeHatchAbi';
|
|
9
10
|
import { RollupAbi } from '@aztec/l1-artifacts/RollupAbi';
|
|
@@ -16,6 +17,7 @@ import {
|
|
|
16
17
|
type Hex,
|
|
17
18
|
type StateOverride,
|
|
18
19
|
type WatchContractEventReturnType,
|
|
20
|
+
encodeAbiParameters,
|
|
19
21
|
encodeFunctionData,
|
|
20
22
|
getContract,
|
|
21
23
|
hexToBigInt,
|
|
@@ -134,6 +136,14 @@ export type L1FeeData = {
|
|
|
134
136
|
blobFee: bigint;
|
|
135
137
|
};
|
|
136
138
|
|
|
139
|
+
/** Components of the minimum fee per mana, as returned by the L1 rollup contract. */
|
|
140
|
+
export type ManaMinFeeComponents = {
|
|
141
|
+
sequencerCost: bigint;
|
|
142
|
+
proverCost: bigint;
|
|
143
|
+
congestionCost: bigint;
|
|
144
|
+
congestionMultiplier: bigint;
|
|
145
|
+
};
|
|
146
|
+
|
|
137
147
|
/**
|
|
138
148
|
* Reward configuration for the rollup
|
|
139
149
|
*/
|
|
@@ -204,6 +214,7 @@ export type CheckpointProposedLog = L1EventLog<CheckpointProposedArgs>;
|
|
|
204
214
|
|
|
205
215
|
export class RollupContract {
|
|
206
216
|
private readonly rollup: GetContractReturnType<typeof RollupAbi, ViemClient>;
|
|
217
|
+
private readonly logger = createLogger('ethereum:rollup');
|
|
207
218
|
|
|
208
219
|
private static cachedStfStorageSlot: Hex | undefined;
|
|
209
220
|
private cachedEscapeHatch?: {
|
|
@@ -379,6 +390,20 @@ export class RollupContract {
|
|
|
379
390
|
return Fr.fromString(await this.rollup.read.archiveAt([0n]));
|
|
380
391
|
}
|
|
381
392
|
|
|
393
|
+
@memoize
|
|
394
|
+
async getVkTreeRoot(): Promise<Fr> {
|
|
395
|
+
const slot = BigInt(RollupContract.stfStorageSlot) + 3n;
|
|
396
|
+
const value = await this.client.getStorageAt({ address: this.address, slot: `0x${slot.toString(16)}` });
|
|
397
|
+
return Fr.fromString(value ?? '0x0');
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
@memoize
|
|
401
|
+
async getProtocolContractsHash(): Promise<Fr> {
|
|
402
|
+
const slot = BigInt(RollupContract.stfStorageSlot) + 4n;
|
|
403
|
+
const value = await this.client.getStorageAt({ address: this.address, slot: `0x${slot.toString(16)}` });
|
|
404
|
+
return Fr.fromString(value ?? '0x0');
|
|
405
|
+
}
|
|
406
|
+
|
|
382
407
|
/**
|
|
383
408
|
* Returns rollup constants used for epoch queries.
|
|
384
409
|
* Return type is `L1RollupConstants` which is defined in stdlib,
|
|
@@ -392,16 +417,25 @@ export class RollupContract {
|
|
|
392
417
|
epochDuration: number;
|
|
393
418
|
proofSubmissionEpochs: number;
|
|
394
419
|
targetCommitteeSize: number;
|
|
420
|
+
rollupManaLimit: number;
|
|
395
421
|
}> {
|
|
396
|
-
const [
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
422
|
+
const [
|
|
423
|
+
l1StartBlock,
|
|
424
|
+
l1GenesisTime,
|
|
425
|
+
slotDuration,
|
|
426
|
+
epochDuration,
|
|
427
|
+
proofSubmissionEpochs,
|
|
428
|
+
targetCommitteeSize,
|
|
429
|
+
rollupManaLimit,
|
|
430
|
+
] = await Promise.all([
|
|
431
|
+
this.getL1StartBlock(),
|
|
432
|
+
this.getL1GenesisTime(),
|
|
433
|
+
this.getSlotDuration(),
|
|
434
|
+
this.getEpochDuration(),
|
|
435
|
+
this.getProofSubmissionEpochs(),
|
|
436
|
+
this.getTargetCommitteeSize(),
|
|
437
|
+
this.getManaLimit(),
|
|
438
|
+
]);
|
|
405
439
|
return {
|
|
406
440
|
l1StartBlock,
|
|
407
441
|
l1GenesisTime,
|
|
@@ -409,6 +443,7 @@ export class RollupContract {
|
|
|
409
443
|
epochDuration: Number(epochDuration),
|
|
410
444
|
proofSubmissionEpochs: Number(proofSubmissionEpochs),
|
|
411
445
|
targetCommitteeSize,
|
|
446
|
+
rollupManaLimit: Number(rollupManaLimit),
|
|
412
447
|
};
|
|
413
448
|
}
|
|
414
449
|
|
|
@@ -463,7 +498,11 @@ export class RollupContract {
|
|
|
463
498
|
|
|
464
499
|
const [isOpen] = await escapeHatch.read.isHatchOpen([BigInt(epoch)]);
|
|
465
500
|
return isOpen;
|
|
466
|
-
} catch {
|
|
501
|
+
} catch (err) {
|
|
502
|
+
this.logger.warn('isEscapeHatchOpen failed (treating as closed); RPC or contract error may cause liveness risk', {
|
|
503
|
+
epoch: Number(epoch),
|
|
504
|
+
error: err,
|
|
505
|
+
});
|
|
467
506
|
return false;
|
|
468
507
|
}
|
|
469
508
|
}
|
|
@@ -503,8 +542,9 @@ export class RollupContract {
|
|
|
503
542
|
return CheckpointNumber.fromBigInt(await this.rollup.read.getPendingCheckpointNumber());
|
|
504
543
|
}
|
|
505
544
|
|
|
506
|
-
async getProvenCheckpointNumber(): Promise<CheckpointNumber> {
|
|
507
|
-
|
|
545
|
+
async getProvenCheckpointNumber(options?: { blockNumber?: bigint }): Promise<CheckpointNumber> {
|
|
546
|
+
await checkBlockTag(options?.blockNumber, this.client);
|
|
547
|
+
return CheckpointNumber.fromBigInt(await this.rollup.read.getProvenCheckpointNumber(options));
|
|
508
548
|
}
|
|
509
549
|
|
|
510
550
|
async getSlotNumber(): Promise<SlotNumber> {
|
|
@@ -745,16 +785,23 @@ export class RollupContract {
|
|
|
745
785
|
* timestamp of the next L1 block
|
|
746
786
|
* @throws otherwise
|
|
747
787
|
*/
|
|
748
|
-
public async
|
|
788
|
+
public async canProposeAt(
|
|
749
789
|
archive: Buffer,
|
|
750
790
|
account: `0x${string}` | Account,
|
|
751
|
-
|
|
752
|
-
opts: {
|
|
791
|
+
timestamp: bigint,
|
|
792
|
+
opts: {
|
|
793
|
+
forcePendingCheckpointNumber?: CheckpointNumber;
|
|
794
|
+
forceArchive?: { checkpointNumber: CheckpointNumber; archive: Fr };
|
|
795
|
+
} = {},
|
|
753
796
|
): Promise<{ slot: SlotNumber; checkpointNumber: CheckpointNumber; timeOfNextL1Slot: bigint }> {
|
|
754
|
-
const
|
|
755
|
-
const timeOfNextL1Slot = latestBlock.timestamp + BigInt(slotDuration);
|
|
797
|
+
const timeOfNextL1Slot = timestamp;
|
|
756
798
|
const who = typeof account === 'string' ? account : account.address;
|
|
757
799
|
|
|
800
|
+
const stateOverride = RollupContract.mergeStateOverrides(
|
|
801
|
+
await this.makePendingCheckpointNumberOverride(opts.forcePendingCheckpointNumber),
|
|
802
|
+
opts.forceArchive ? this.makeArchiveOverride(opts.forceArchive.checkpointNumber, opts.forceArchive.archive) : [],
|
|
803
|
+
);
|
|
804
|
+
|
|
758
805
|
try {
|
|
759
806
|
const {
|
|
760
807
|
result: [slot, checkpointNumber],
|
|
@@ -764,7 +811,7 @@ export class RollupContract {
|
|
|
764
811
|
functionName: 'canProposeAtTime',
|
|
765
812
|
args: [timeOfNextL1Slot, `0x${archive.toString('hex')}`, who],
|
|
766
813
|
account,
|
|
767
|
-
stateOverride
|
|
814
|
+
stateOverride,
|
|
768
815
|
});
|
|
769
816
|
|
|
770
817
|
return {
|
|
@@ -800,6 +847,151 @@ export class RollupContract {
|
|
|
800
847
|
];
|
|
801
848
|
}
|
|
802
849
|
|
|
850
|
+
/**
|
|
851
|
+
* Returns a state override that sets tempCheckpointLogs[checkpointNumber].feeHeader to the compressed fee header.
|
|
852
|
+
* Used when simulating a propose call where the parent checkpoint hasn't landed on L1 yet (pipelining).
|
|
853
|
+
*/
|
|
854
|
+
public async makeFeeHeaderOverride(checkpointNumber: CheckpointNumber, feeHeader: FeeHeader): Promise<StateOverride> {
|
|
855
|
+
const { epochDuration, proofSubmissionEpochs } = await this.getRollupConstants();
|
|
856
|
+
const roundaboutSize = BigInt(epochDuration * (proofSubmissionEpochs + 1) + 1);
|
|
857
|
+
const circularIndex = BigInt(checkpointNumber) % roundaboutSize;
|
|
858
|
+
|
|
859
|
+
// tempCheckpointLogs is at offset 2 in RollupStore
|
|
860
|
+
const tempCheckpointLogsMappingBase = hexToBigInt(RollupContract.stfStorageSlot) + 2n;
|
|
861
|
+
|
|
862
|
+
// Solidity mapping slot: keccak256(abi.encode(key, baseSlot))
|
|
863
|
+
const structBaseSlot = hexToBigInt(
|
|
864
|
+
keccak256(
|
|
865
|
+
encodeAbiParameters([{ type: 'uint256' }, { type: 'uint256' }], [circularIndex, tempCheckpointLogsMappingBase]),
|
|
866
|
+
),
|
|
867
|
+
);
|
|
868
|
+
|
|
869
|
+
// feeHeader is the 7th field (offset 6) in CompressedTempCheckpointLog
|
|
870
|
+
const feeHeaderSlot = structBaseSlot + 6n;
|
|
871
|
+
const compressed = RollupContract.compressFeeHeader(feeHeader);
|
|
872
|
+
|
|
873
|
+
return [
|
|
874
|
+
{
|
|
875
|
+
address: this.address,
|
|
876
|
+
stateDiff: [
|
|
877
|
+
{
|
|
878
|
+
slot: `0x${feeHeaderSlot.toString(16).padStart(64, '0')}`,
|
|
879
|
+
value: `0x${compressed.toString(16).padStart(64, '0')}`,
|
|
880
|
+
},
|
|
881
|
+
],
|
|
882
|
+
},
|
|
883
|
+
];
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
/**
|
|
887
|
+
* Returns a state override that sets archives[checkpointNumber] to the given archive value.
|
|
888
|
+
* Used when simulating a canProposeAtTime call where the local archive differs from L1
|
|
889
|
+
* (e.g. pipelining where the parent checkpoint hasn't landed on L1 yet).
|
|
890
|
+
*/
|
|
891
|
+
public makeArchiveOverride(checkpointNumber: CheckpointNumber, archive: Fr): StateOverride {
|
|
892
|
+
const archivesMappingBase = hexToBigInt(RollupContract.stfStorageSlot) + 1n;
|
|
893
|
+
const archiveSlot = hexToBigInt(
|
|
894
|
+
keccak256(
|
|
895
|
+
encodeAbiParameters(
|
|
896
|
+
[{ type: 'uint256' }, { type: 'uint256' }],
|
|
897
|
+
[BigInt(checkpointNumber), archivesMappingBase],
|
|
898
|
+
),
|
|
899
|
+
),
|
|
900
|
+
);
|
|
901
|
+
return [
|
|
902
|
+
{
|
|
903
|
+
address: this.address,
|
|
904
|
+
stateDiff: [
|
|
905
|
+
{
|
|
906
|
+
slot: `0x${archiveSlot.toString(16).padStart(64, '0')}`,
|
|
907
|
+
value: archive.toString(),
|
|
908
|
+
},
|
|
909
|
+
],
|
|
910
|
+
},
|
|
911
|
+
];
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
/** Merges multiple StateOverride arrays, combining stateDiff entries for the same address. */
|
|
915
|
+
public static mergeStateOverrides(...overrides: StateOverride[]): StateOverride {
|
|
916
|
+
type StateDiffEntry = { slot: `0x${string}`; value: `0x${string}` };
|
|
917
|
+
const byAddress = new Map<string, { address: `0x${string}`; balance?: bigint; stateDiff: StateDiffEntry[] }>();
|
|
918
|
+
for (const override of overrides) {
|
|
919
|
+
for (const entry of override) {
|
|
920
|
+
const key = entry.address.toLowerCase();
|
|
921
|
+
const existing = byAddress.get(key);
|
|
922
|
+
if (existing) {
|
|
923
|
+
existing.stateDiff.push(...(entry.stateDiff ?? []));
|
|
924
|
+
if (entry.balance !== undefined) {
|
|
925
|
+
existing.balance = entry.balance;
|
|
926
|
+
}
|
|
927
|
+
} else {
|
|
928
|
+
byAddress.set(key, {
|
|
929
|
+
address: entry.address,
|
|
930
|
+
balance: entry.balance,
|
|
931
|
+
stateDiff: [...(entry.stateDiff ?? [])],
|
|
932
|
+
});
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
return [...byAddress.values()];
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
/** Compresses a FeeHeader into a uint256 matching FeeHeaderLib.compress() in FeeStructs.sol. */
|
|
940
|
+
public static compressFeeHeader(feeHeader: FeeHeader): bigint {
|
|
941
|
+
const MASK_48_BITS = (1n << 48n) - 1n;
|
|
942
|
+
const MASK_64_BITS = (1n << 64n) - 1n;
|
|
943
|
+
const MASK_63_BITS = (1n << 63n) - 1n;
|
|
944
|
+
|
|
945
|
+
let value = BigInt(feeHeader.manaUsed) & ((1n << 32n) - 1n); // bits [0:31]
|
|
946
|
+
value |= (feeHeader.excessMana < MASK_48_BITS ? feeHeader.excessMana : MASK_48_BITS) << 32n; // bits [32:79]
|
|
947
|
+
value |= (BigInt(feeHeader.ethPerFeeAsset) & MASK_48_BITS) << 80n; // bits [80:127]
|
|
948
|
+
value |= (feeHeader.congestionCost < MASK_64_BITS ? feeHeader.congestionCost : MASK_64_BITS) << 128n; // bits [128:191]
|
|
949
|
+
value |= (feeHeader.proverCost < MASK_63_BITS ? feeHeader.proverCost : MASK_63_BITS) << 192n; // bits [192:254]
|
|
950
|
+
value |= 1n << 255n; // preheat flag
|
|
951
|
+
return value;
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
/** Computes the fee header for a child checkpoint given parent fee header and child data.
|
|
955
|
+
* Must stay in sync with Solidity FeeLib.sol (computeNewEthPerFeeAsset, clampedAdd). */
|
|
956
|
+
public static computeChildFeeHeader(
|
|
957
|
+
parentFeeHeader: FeeHeader,
|
|
958
|
+
childManaUsed: bigint,
|
|
959
|
+
feeAssetPriceModifier: bigint,
|
|
960
|
+
manaTarget: bigint,
|
|
961
|
+
): FeeHeader {
|
|
962
|
+
const MIN_ETH_PER_FEE_ASSET = 100n;
|
|
963
|
+
const MAX_ETH_PER_FEE_ASSET = 100_000_000_000_000n; // 1e14, matches FeeLib.sol
|
|
964
|
+
|
|
965
|
+
// excessMana = clampedAdd(parent.excessMana + parent.manaUsed, -manaTarget)
|
|
966
|
+
const sum = parentFeeHeader.excessMana + parentFeeHeader.manaUsed;
|
|
967
|
+
const excessMana = sum > manaTarget ? sum - manaTarget : 0n;
|
|
968
|
+
|
|
969
|
+
// ethPerFeeAsset = computeNewEthPerFeeAsset(max(parent.ethPerFeeAsset, MIN), modifier)
|
|
970
|
+
const parentPrice =
|
|
971
|
+
parentFeeHeader.ethPerFeeAsset > MIN_ETH_PER_FEE_ASSET ? parentFeeHeader.ethPerFeeAsset : MIN_ETH_PER_FEE_ASSET;
|
|
972
|
+
let newPrice: bigint;
|
|
973
|
+
if (feeAssetPriceModifier >= 0n) {
|
|
974
|
+
newPrice = (parentPrice * (10_000n + feeAssetPriceModifier)) / 10_000n;
|
|
975
|
+
} else {
|
|
976
|
+
const absMod = -feeAssetPriceModifier;
|
|
977
|
+
newPrice = (parentPrice * (10_000n - absMod)) / 10_000n;
|
|
978
|
+
}
|
|
979
|
+
if (newPrice < MIN_ETH_PER_FEE_ASSET) {
|
|
980
|
+
newPrice = MIN_ETH_PER_FEE_ASSET;
|
|
981
|
+
}
|
|
982
|
+
if (newPrice > MAX_ETH_PER_FEE_ASSET) {
|
|
983
|
+
newPrice = MAX_ETH_PER_FEE_ASSET;
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
return {
|
|
987
|
+
excessMana,
|
|
988
|
+
manaUsed: childManaUsed,
|
|
989
|
+
ethPerFeeAsset: newPrice,
|
|
990
|
+
congestionCost: 0n,
|
|
991
|
+
proverCost: 0n,
|
|
992
|
+
};
|
|
993
|
+
}
|
|
994
|
+
|
|
803
995
|
/** Creates a request to Rollup#invalidateBadAttestation to be simulated or sent */
|
|
804
996
|
public buildInvalidateBadAttestationRequest(
|
|
805
997
|
checkpointNumber: CheckpointNumber,
|
|
@@ -848,8 +1040,18 @@ export class RollupContract {
|
|
|
848
1040
|
return this.rollup.read.getHasSubmitted([BigInt(epochNumber), BigInt(numberOfCheckpointsInEpoch), prover]);
|
|
849
1041
|
}
|
|
850
1042
|
|
|
851
|
-
getManaMinFeeAt(timestamp: bigint, inFeeAsset: boolean): Promise<bigint> {
|
|
852
|
-
return this.rollup.read.getManaMinFeeAt([timestamp, inFeeAsset]);
|
|
1043
|
+
getManaMinFeeAt(timestamp: bigint, inFeeAsset: boolean, stateOverride?: StateOverride): Promise<bigint> {
|
|
1044
|
+
return this.rollup.read.getManaMinFeeAt([timestamp, inFeeAsset], { stateOverride });
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
async getManaMinFeeComponentsAt(timestamp: bigint, inFeeAsset: boolean): Promise<ManaMinFeeComponents> {
|
|
1048
|
+
const result = await this.rollup.read.getManaMinFeeComponentsAt([timestamp, inFeeAsset]);
|
|
1049
|
+
return {
|
|
1050
|
+
sequencerCost: result.sequencerCost,
|
|
1051
|
+
proverCost: result.proverCost,
|
|
1052
|
+
congestionCost: result.congestionCost,
|
|
1053
|
+
congestionMultiplier: result.congestionMultiplier,
|
|
1054
|
+
};
|
|
853
1055
|
}
|
|
854
1056
|
|
|
855
1057
|
async getSlotAt(timestamp: bigint): Promise<SlotNumber> {
|
|
@@ -895,11 +1097,10 @@ export class RollupContract {
|
|
|
895
1097
|
return this.rollup.read.getSpecificProverRewardsForEpoch([epoch, prover]);
|
|
896
1098
|
}
|
|
897
1099
|
|
|
898
|
-
async getAttesters(): Promise<EthAddress[]> {
|
|
1100
|
+
async getAttesters(timestamp?: bigint): Promise<EthAddress[]> {
|
|
899
1101
|
const attesterSize = await this.getActiveAttesterCount();
|
|
900
1102
|
const gse = new GSEContract(this.client, await this.getGSE());
|
|
901
|
-
const ts = (await this.client.getBlock()).timestamp;
|
|
902
|
-
|
|
1103
|
+
const ts = timestamp ?? (await this.client.getBlock()).timestamp;
|
|
903
1104
|
const indices = Array.from({ length: attesterSize }, (_, i) => BigInt(i));
|
|
904
1105
|
const chunks = chunk(indices, 1000);
|
|
905
1106
|
|