@aztec/ethereum 0.0.1-commit.993d240 → 0.0.1-commit.9a89641
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 +11 -3
- package/dest/client.d.ts.map +1 -1
- package/dest/client.js +35 -3
- package/dest/config.d.ts +22 -3
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +54 -3
- package/dest/contracts/governance.d.ts +36 -11
- package/dest/contracts/governance.d.ts.map +1 -1
- package/dest/contracts/governance.js +54 -24
- package/dest/contracts/governance_proposer.d.ts +7 -8
- package/dest/contracts/governance_proposer.d.ts.map +1 -1
- package/dest/contracts/governance_proposer.js +7 -10
- package/dest/contracts/gse.d.ts +8 -2
- package/dest/contracts/gse.d.ts.map +1 -1
- package/dest/contracts/gse.js +11 -2
- package/dest/contracts/multicall.d.ts +5 -3
- package/dest/contracts/multicall.d.ts.map +1 -1
- package/dest/contracts/multicall.js +7 -5
- package/dest/contracts/outbox.d.ts +8 -2
- package/dest/contracts/outbox.d.ts.map +1 -1
- package/dest/contracts/outbox.js +6 -2
- package/dest/contracts/rollup.d.ts +176 -9
- package/dest/contracts/rollup.d.ts.map +1 -1
- package/dest/contracts/rollup.js +48 -8
- package/dest/contracts/slashing_proposer.d.ts +37 -6
- package/dest/contracts/slashing_proposer.d.ts.map +1 -1
- package/dest/contracts/slashing_proposer.js +447 -17
- package/dest/deploy_aztec_l1_contracts.d.ts +16 -5
- package/dest/deploy_aztec_l1_contracts.d.ts.map +1 -1
- package/dest/deploy_aztec_l1_contracts.js +26 -13
- package/dest/foundry_binary.d.ts +13 -0
- package/dest/foundry_binary.d.ts.map +1 -0
- package/dest/foundry_binary.js +52 -0
- package/dest/l1_artifacts.d.ts +1242 -95
- package/dest/l1_artifacts.d.ts.map +1 -1
- package/dest/l1_tx_utils/config.js +3 -3
- package/dest/l1_tx_utils/l1_fee_analyzer.d.ts +23 -4
- package/dest/l1_tx_utils/l1_fee_analyzer.d.ts.map +1 -1
- package/dest/l1_tx_utils/l1_fee_analyzer.js +65 -3
- package/dest/l1_tx_utils/l1_tx_utils.d.ts +1 -1
- package/dest/l1_tx_utils/l1_tx_utils.d.ts.map +1 -1
- package/dest/l1_tx_utils/l1_tx_utils.js +84 -22
- 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 +2 -1
- package/dest/l1_tx_utils/types.d.ts +27 -1
- package/dest/l1_tx_utils/types.d.ts.map +1 -1
- package/dest/l1_tx_utils/types.js +10 -0
- package/dest/publisher_manager.d.ts +18 -6
- package/dest/publisher_manager.d.ts.map +1 -1
- package/dest/publisher_manager.js +44 -7
- package/dest/queries.d.ts +1 -1
- package/dest/queries.d.ts.map +1 -1
- package/dest/queries.js +7 -1
- package/dest/test/blob_kzg_warmup.d.ts +19 -0
- package/dest/test/blob_kzg_warmup.d.ts.map +1 -0
- package/dest/test/blob_kzg_warmup.js +64 -0
- package/dest/test/chain_monitor.d.ts +33 -2
- package/dest/test/chain_monitor.d.ts.map +1 -1
- package/dest/test/chain_monitor.js +71 -6
- package/dest/test/eth_cheat_codes.d.ts +26 -3
- package/dest/test/eth_cheat_codes.d.ts.map +1 -1
- package/dest/test/eth_cheat_codes.js +72 -37
- package/dest/test/index.d.ts +2 -1
- package/dest/test/index.d.ts.map +1 -1
- package/dest/test/index.js +1 -0
- package/dest/test/rollup_cheat_codes.d.ts +30 -1
- package/dest/test/rollup_cheat_codes.d.ts.map +1 -1
- package/dest/test/rollup_cheat_codes.js +27 -0
- package/dest/test/start_anvil.d.ts +1 -1
- package/dest/test/start_anvil.d.ts.map +1 -1
- package/dest/test/start_anvil.js +41 -5
- package/dest/utils.d.ts +2 -2
- package/dest/utils.d.ts.map +1 -1
- package/dest/utils.js +15 -6
- package/package.json +5 -6
- package/src/client.ts +39 -1
- package/src/config.ts +73 -3
- package/src/contracts/governance.ts +63 -20
- package/src/contracts/governance_proposer.ts +11 -15
- package/src/contracts/gse.ts +19 -2
- package/src/contracts/multicall.ts +8 -5
- package/src/contracts/outbox.ts +6 -2
- package/src/contracts/rollup.ts +57 -8
- package/src/contracts/slashing_proposer.ts +78 -15
- package/src/deploy_aztec_l1_contracts.ts +31 -13
- package/src/foundry_binary.ts +57 -0
- package/src/l1_tx_utils/config.ts +3 -3
- package/src/l1_tx_utils/l1_fee_analyzer.ts +75 -3
- package/src/l1_tx_utils/l1_tx_utils.ts +71 -20
- package/src/l1_tx_utils/readonly_l1_tx_utils.ts +2 -1
- package/src/l1_tx_utils/types.ts +32 -0
- package/src/publisher_manager.ts +54 -18
- package/src/queries.ts +6 -0
- package/src/test/blob_kzg_warmup.ts +65 -0
- package/src/test/chain_monitor.ts +88 -5
- package/src/test/eth_cheat_codes.ts +66 -41
- package/src/test/index.ts +1 -0
- package/src/test/rollup_cheat_codes.ts +51 -0
- package/src/test/start_anvil.ts +38 -6
- package/src/utils.ts +12 -6
- package/dest/generated/l1-contracts-defaults.d.ts +0 -30
- package/dest/generated/l1-contracts-defaults.d.ts.map +0 -1
- package/dest/generated/l1-contracts-defaults.js +0 -30
- package/src/generated/l1-contracts-defaults.ts +0 -32
package/dest/test/start_anvil.js
CHANGED
|
@@ -1,16 +1,41 @@
|
|
|
1
1
|
import { createLogger } from '@aztec/foundation/log';
|
|
2
2
|
import { makeBackoff, retry } from '@aztec/foundation/retry';
|
|
3
|
-
import { fileURLToPath } from '@aztec/foundation/url';
|
|
4
3
|
import { spawn } from 'child_process';
|
|
5
|
-
import {
|
|
4
|
+
import { resolveFoundryBinary } from '../foundry_binary.js';
|
|
5
|
+
// Watchdog wrapper: instead of spawning anvil directly, we spawn a small bash supervisor that runs
|
|
6
|
+
// anvil as a background child and polls its own parent (this node process). If the parent dies for
|
|
7
|
+
// ANY reason — including SIGKILL / crash / OOM, where node's own exit handlers never run — the poll
|
|
8
|
+
// loop ends and the EXIT trap reaps anvil. The script is inlined (rather than shipped as a `.sh`) so
|
|
9
|
+
// it works from the published npm tarball too, and the resolved anvil binary is passed via
|
|
10
|
+
// `$ANVIL_BIN` so it works without `anvil` on PATH.
|
|
11
|
+
//
|
|
12
|
+
// `$@` is the anvil argv; `bash -c <script> bash <...args>` puts the args in `$@` and `$0` = 'bash'.
|
|
13
|
+
//
|
|
14
|
+
// The EXIT trap reaps anvil; INT/TERM just `exit` (which fires the EXIT trap) so a signal terminates
|
|
15
|
+
// the supervisor promptly instead of being swallowed — a trapped TERM does NOT terminate the shell,
|
|
16
|
+
// so trapping the kill directly on TERM would leave the poll loop running and the caller's teardown
|
|
17
|
+
// hanging until its SIGKILL escalation. `sleep & wait` makes the poll interruptible, so INT/TERM are
|
|
18
|
+
// handled immediately rather than after the current `sleep` returns.
|
|
19
|
+
const ANVIL_WATCHDOG = `
|
|
20
|
+
set -u
|
|
21
|
+
parent=$PPID
|
|
22
|
+
"$ANVIL_BIN" "$@" &
|
|
23
|
+
anvil_pid=$!
|
|
24
|
+
trap 'kill "$anvil_pid" 2>/dev/null' EXIT
|
|
25
|
+
trap 'exit 0' INT TERM
|
|
26
|
+
while kill -0 "$parent" 2>/dev/null; do sleep 1 & wait $!; done
|
|
27
|
+
`;
|
|
6
28
|
/**
|
|
7
29
|
* Ensures there's a running Anvil instance and returns the RPC URL.
|
|
8
30
|
*/ export async function startAnvil(opts = {}) {
|
|
9
|
-
const anvilBinary =
|
|
31
|
+
const anvilBinary = resolveFoundryBinary('anvil');
|
|
10
32
|
const logger = opts.log ? createLogger('ethereum:anvil') : undefined;
|
|
11
33
|
const methodCalls = opts.captureMethodCalls ? [] : undefined;
|
|
12
34
|
let detectedPort;
|
|
13
35
|
const anvil = await retry(async ()=>{
|
|
36
|
+
// `--port 0` lets anvil bind an OS-assigned ephemeral port; the actual port is read back from
|
|
37
|
+
// its "Listening on host:port" stdout below, so independent suites can spawn their own anvil
|
|
38
|
+
// in parallel without fighting over a fixed port.
|
|
14
39
|
const port = opts.port ?? (process.env.ANVIL_PORT ? parseInt(process.env.ANVIL_PORT) : 8545);
|
|
15
40
|
const args = [
|
|
16
41
|
'--host',
|
|
@@ -31,7 +56,14 @@ import { dirname, resolve } from 'path';
|
|
|
31
56
|
args.push('--hardfork', opts.hardfork);
|
|
32
57
|
}
|
|
33
58
|
args.push('--slots-in-an-epoch', String(opts.slotsInAnEpoch ?? 1));
|
|
34
|
-
|
|
59
|
+
// Spawn the watchdog (see ANVIL_WATCHDOG). It launches anvil with these args and reaps it if we
|
|
60
|
+
// die; `$0` is 'bash' and `$@` is the anvil argv.
|
|
61
|
+
const child = spawn('bash', [
|
|
62
|
+
'-c',
|
|
63
|
+
ANVIL_WATCHDOG,
|
|
64
|
+
'bash',
|
|
65
|
+
...args
|
|
66
|
+
], {
|
|
35
67
|
stdio: [
|
|
36
68
|
'ignore',
|
|
37
69
|
'pipe',
|
|
@@ -39,6 +71,7 @@ import { dirname, resolve } from 'path';
|
|
|
39
71
|
],
|
|
40
72
|
env: {
|
|
41
73
|
...process.env,
|
|
74
|
+
ANVIL_BIN: anvilBinary,
|
|
42
75
|
RAYON_NUM_THREADS: '1'
|
|
43
76
|
}
|
|
44
77
|
});
|
|
@@ -139,7 +172,10 @@ import { dirname, resolve } from 'path';
|
|
|
139
172
|
}
|
|
140
173
|
}
|
|
141
174
|
}
|
|
142
|
-
/**
|
|
175
|
+
/**
|
|
176
|
+
* Send SIGTERM to the watchdog, wait up to 5 s, then SIGKILL. The watchdog's trap forwards the
|
|
177
|
+
* signal to anvil, so terminating it tears down anvil too. All timers are always cleared.
|
|
178
|
+
*/ function killChild(child) {
|
|
143
179
|
return new Promise((resolve)=>{
|
|
144
180
|
if (child.exitCode !== null || child.killed) {
|
|
145
181
|
child.stdout?.destroy();
|
package/dest/utils.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export interface L2Claim {
|
|
|
9
9
|
}
|
|
10
10
|
export declare class FormattedViemError extends Error {
|
|
11
11
|
metaMessages?: any[];
|
|
12
|
-
constructor(message: string, metaMessages?: any[]);
|
|
12
|
+
constructor(message: string, metaMessages?: any[], options?: ErrorOptions);
|
|
13
13
|
}
|
|
14
14
|
export declare function extractEvent<const TAbi extends Abi | readonly unknown[], TEventName extends ContractEventName<TAbi>, TEventType = DecodeEventLogReturnType<TAbi, TEventName, Hex[], undefined, true>>(logs: Log[], address: Hex, abi: TAbi, eventName: TEventName, filter?: (log: TEventType) => boolean, logger?: Logger): TEventType;
|
|
15
15
|
export declare function tryExtractEvent<const TAbi extends Abi | readonly unknown[], TEventName extends ContractEventName<TAbi>, TEventType = DecodeEventLogReturnType<TAbi, TEventName, Hex[], undefined, true>>(logs: Log[], address: Hex, abi: TAbi, eventName: TEventName, filter?: (log: TEventType) => boolean, logger?: Logger): TEventType | undefined;
|
|
@@ -42,4 +42,4 @@ export declare function isBlobTransaction(tx: FormattedTransaction): tx is Forma
|
|
|
42
42
|
* Calculates a percentile from an array of bigints
|
|
43
43
|
*/
|
|
44
44
|
export declare function calculatePercentile(values: bigint[], percentile: number): bigint;
|
|
45
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
45
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXRpbHMuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy91dGlscy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssRUFBRSxFQUFFLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUN6RCxPQUFPLEtBQUssRUFBRSxNQUFNLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUdwRCxPQUFPLEVBQ0wsS0FBSyxHQUFHLEVBR1IsS0FBSyxpQkFBaUIsRUFFdEIsS0FBSyx3QkFBd0IsRUFDN0IsS0FBSyxvQkFBb0IsRUFDekIsS0FBSyxHQUFHLEVBQ1IsS0FBSyxHQUFHLEVBR1QsTUFBTSxNQUFNLENBQUM7QUFHZCxNQUFNLFdBQVcsT0FBTztJQUN0QixXQUFXLEVBQUUsRUFBRSxDQUFDO0lBQ2hCLFdBQVcsRUFBRSxFQUFFLENBQUM7SUFDaEIsV0FBVyxFQUFFLEdBQUcsQ0FBQztJQUNqQixnQkFBZ0IsRUFBRSxNQUFNLENBQUM7Q0FDMUI7QUFFRCxxQkFBYSxrQkFBbUIsU0FBUSxLQUFLO0lBQzNDLFlBQVksQ0FBQyxFQUFFLEdBQUcsRUFBRSxDQUFDO0lBRXJCLFlBQVksT0FBTyxFQUFFLE1BQU0sRUFBRSxZQUFZLENBQUMsRUFBRSxHQUFHLEVBQUUsRUFBRSxPQUFPLENBQUMsRUFBRSxZQUFZLEVBSXhFO0NBQ0Y7QUFFRCx3QkFBZ0IsWUFBWSxDQUMxQixLQUFLLENBQUMsSUFBSSxTQUFTLEdBQUcsR0FBRyxTQUFTLE9BQU8sRUFBRSxFQUMzQyxVQUFVLFNBQVMsaUJBQWlCLENBQUMsSUFBSSxDQUFDLEVBQzFDLFVBQVUsR0FBRyx3QkFBd0IsQ0FBQyxJQUFJLEVBQUUsVUFBVSxFQUFFLEdBQUcsRUFBRSxFQUFFLFNBQVMsRUFBRSxJQUFJLENBQUMsRUFFL0UsSUFBSSxFQUFFLEdBQUcsRUFBRSxFQUNYLE9BQU8sRUFBRSxHQUFHLEVBQ1osR0FBRyxFQUFFLElBQUksRUFDVCxTQUFTLEVBQUUsVUFBVSxFQUNyQixNQUFNLENBQUMsRUFBRSxDQUFDLEdBQUcsRUFBRSxVQUFVLEtBQUssT0FBTyxFQUNyQyxNQUFNLENBQUMsRUFBRSxNQUFNLEdBQ2QsVUFBVSxDQU1aO0FBRUQsd0JBQWdCLGVBQWUsQ0FDN0IsS0FBSyxDQUFDLElBQUksU0FBUyxHQUFHLEdBQUcsU0FBUyxPQUFPLEVBQUUsRUFDM0MsVUFBVSxTQUFTLGlCQUFpQixDQUFDLElBQUksQ0FBQyxFQUMxQyxVQUFVLEdBQUcsd0JBQXdCLENBQUMsSUFBSSxFQUFFLFVBQVUsRUFBRSxHQUFHLEVBQUUsRUFBRSxTQUFTLEVBQUUsSUFBSSxDQUFDLEVBRS9FLElBQUksRUFBRSxHQUFHLEVBQUUsRUFDWCxPQUFPLEVBQUUsR0FBRyxFQUNaLEdBQUcsRUFBRSxJQUFJLEVBQ1QsU0FBUyxFQUFFLFVBQVUsRUFDckIsTUFBTSxDQUFDLEVBQUUsQ0FBQyxHQUFHLEVBQUUsVUFBVSxLQUFLLE9BQU8sRUFDckMsTUFBTSxDQUFDLEVBQUUsTUFBTSxHQUNkLFVBQVUsR0FBRyxTQUFTLENBZ0J4QjtBQUVELHdCQUFnQixxQkFBcUIsQ0FBQyxHQUFHLEVBQUUsR0FBRyxPQVc3QztBQUVELHdCQUFnQixTQUFTLENBQUMsSUFBSSxFQUFFLEdBQUcsRUFBRSxHQUFHLEdBQUcsQ0FlMUM7QUEyRUQ7Ozs7O0dBS0c7QUFDSCx3QkFBZ0IsZUFBZSxDQUFDLEtBQUssRUFBRSxHQUFHLEVBQUUsR0FBRyxHQUFFLEdBQWUsR0FBRyxrQkFBa0IsQ0FrRXBGO0FBeUJEOzs7OztHQUtHO0FBQ0gsd0JBQWdCLHFCQUFxQixDQUFDLElBQUksRUFBRSxHQUFHLEdBQUcsU0FBUyxFQUFFLEdBQUcsRUFBRSxHQUFHLEdBQUcsTUFBTSxHQUFHLFNBQVMsQ0FVekY7QUFFRCx3QkFBZ0IscUJBQXFCLENBQUMsR0FBRyxFQUFFLEdBQUcsc0JBYTdDO0FBRUQ7OztHQUdHO0FBQ0gsd0JBQWdCLGlCQUFpQixDQUFDLEVBQUUsRUFBRSxvQkFBb0IsR0FBRyxFQUFFLElBQUksb0JBQW9CLEdBQUc7SUFDeEYsZ0JBQWdCLEVBQUUsTUFBTSxDQUFDO0lBQ3pCLG1CQUFtQixFQUFFLFNBQVMsR0FBRyxFQUFFLENBQUM7Q0FDckMsQ0FPQTtBQUVEOztHQUVHO0FBQ0gsd0JBQWdCLG1CQUFtQixDQUFDLE1BQU0sRUFBRSxNQUFNLEVBQUUsRUFBRSxVQUFVLEVBQUUsTUFBTSxHQUFHLE1BQU0sQ0FPaEYifQ==
|
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,
|
|
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,EAAE,OAAO,CAAC,EAAE,YAAY,EAIxE;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,CAkEpF;AAyBD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,GAAG,GAAG,SAAS,EAAE,GAAG,EAAE,GAAG,GAAG,MAAM,GAAG,SAAS,CAUzF;AAED,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
|
@@ -3,8 +3,8 @@ import { BaseError, ContractFunctionRevertedError, decodeErrorResult, decodeEven
|
|
|
3
3
|
import { formatAbiItem, formatAbiParams } from 'viem/utils';
|
|
4
4
|
export class FormattedViemError extends Error {
|
|
5
5
|
metaMessages;
|
|
6
|
-
constructor(message, metaMessages){
|
|
7
|
-
super(message);
|
|
6
|
+
constructor(message, metaMessages, options){
|
|
7
|
+
super(message, options);
|
|
8
8
|
this.name = 'FormattedViemError';
|
|
9
9
|
this.metaMessages = metaMessages;
|
|
10
10
|
}
|
|
@@ -135,6 +135,7 @@ function getNestedErrorData(error) {
|
|
|
135
135
|
if (error instanceof FormattedViemError) {
|
|
136
136
|
return error;
|
|
137
137
|
}
|
|
138
|
+
const originalError = error;
|
|
138
139
|
// First try to decode as a custom error using the ABI
|
|
139
140
|
try {
|
|
140
141
|
const data = getNestedErrorData(error);
|
|
@@ -145,7 +146,9 @@ function getNestedErrorData(error) {
|
|
|
145
146
|
data: data
|
|
146
147
|
});
|
|
147
148
|
if (decoded) {
|
|
148
|
-
return new FormattedViemError(`${decoded.errorName}(${decoded.args?.join(', ') ?? ''})`, error?.metaMessages
|
|
149
|
+
return new FormattedViemError(`${decoded.errorName}(${decoded.args?.join(', ') ?? ''})`, error?.metaMessages, {
|
|
150
|
+
cause: originalError
|
|
151
|
+
});
|
|
149
152
|
}
|
|
150
153
|
}
|
|
151
154
|
// If it's a BaseError, try to get the custom error through ContractFunctionRevertedError
|
|
@@ -157,7 +160,9 @@ function getNestedErrorData(error) {
|
|
|
157
160
|
errorName = revertError.signature ?? '';
|
|
158
161
|
}
|
|
159
162
|
const args = revertError.metaMessages && revertError.metaMessages?.length > 1 ? revertError.metaMessages[1].trimStart() : '';
|
|
160
|
-
return new FormattedViemError(`${errorName}${args}`, error?.metaMessages
|
|
163
|
+
return new FormattedViemError(`${errorName}${args}`, error?.metaMessages, {
|
|
164
|
+
cause: originalError
|
|
165
|
+
});
|
|
161
166
|
}
|
|
162
167
|
}
|
|
163
168
|
} catch {
|
|
@@ -175,9 +180,13 @@ function getNestedErrorData(error) {
|
|
|
175
180
|
}
|
|
176
181
|
// If it's a regular Error instance, return it with its message
|
|
177
182
|
if (error instanceof Error) {
|
|
178
|
-
return new FormattedViemError(truncateErrorMessage(error.message), error?.metaMessages
|
|
183
|
+
return new FormattedViemError(truncateErrorMessage(error.message), error?.metaMessages, {
|
|
184
|
+
cause: originalError
|
|
185
|
+
});
|
|
179
186
|
}
|
|
180
|
-
return new FormattedViemError(truncateErrorMessage(String(error))
|
|
187
|
+
return new FormattedViemError(truncateErrorMessage(String(error)), undefined, {
|
|
188
|
+
cause: originalError
|
|
189
|
+
});
|
|
181
190
|
}
|
|
182
191
|
function stripAbis(obj) {
|
|
183
192
|
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.9a89641",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
"./account": "./dest/account.js",
|
|
@@ -40,7 +40,6 @@
|
|
|
40
40
|
"build": "yarn clean && ../scripts/tsc.sh",
|
|
41
41
|
"build:dev": "../scripts/tsc.sh --watch",
|
|
42
42
|
"clean": "rm -rf ./dest .tsbuildinfo",
|
|
43
|
-
"generate": "./scripts/generate.sh",
|
|
44
43
|
"start:dev": "concurrently -k \"../scripts/tsc.sh --watch\" \"nodemon --watch dest --exec yarn start\"",
|
|
45
44
|
"start": "node ./dest/index.js",
|
|
46
45
|
"test": "NODE_NO_WARNINGS=1 node --experimental-vm-modules ../node_modules/.bin/jest --passWithNoTests --maxWorkers=${JEST_MAX_WORKERS:-8}",
|
|
@@ -50,10 +49,10 @@
|
|
|
50
49
|
"../package.common.json"
|
|
51
50
|
],
|
|
52
51
|
"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
|
|
52
|
+
"@aztec/blob-lib": "0.0.1-commit.9a89641",
|
|
53
|
+
"@aztec/constants": "0.0.1-commit.9a89641",
|
|
54
|
+
"@aztec/foundation": "0.0.1-commit.9a89641",
|
|
55
|
+
"@aztec/l1-artifacts": "6.0.0-nightly.20260807",
|
|
57
56
|
"dotenv": "^16.0.3",
|
|
58
57
|
"lodash.chunk": "^4.2.0",
|
|
59
58
|
"lodash.pickby": "^4.5.0",
|
package/src/client.ts
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import type { Logger } from '@aztec/foundation/log';
|
|
2
2
|
import { retryUntil } from '@aztec/foundation/retry';
|
|
3
|
+
import { getErrorCause } from '@aztec/foundation/types';
|
|
3
4
|
|
|
4
5
|
import {
|
|
5
6
|
type Chain,
|
|
7
|
+
type FallbackTransport,
|
|
6
8
|
type HDAccount,
|
|
9
|
+
HttpRequestError,
|
|
10
|
+
type HttpTransport,
|
|
7
11
|
type LocalAccount,
|
|
8
12
|
type PrivateKeyAccount,
|
|
9
13
|
createPublicClient,
|
|
@@ -31,9 +35,43 @@ type Config = {
|
|
|
31
35
|
|
|
32
36
|
export type { Config as EthereumClientConfig };
|
|
33
37
|
|
|
38
|
+
/** Error exposed by L1 RPC transports without including provider URLs in its message. */
|
|
39
|
+
export class L1RpcError extends Error {
|
|
40
|
+
constructor(message: string, options?: ErrorOptions) {
|
|
41
|
+
super(message, options);
|
|
42
|
+
this.name = 'L1RpcError';
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
34
46
|
/** Creates a viem fallback HTTP transport for the given L1 RPC URLs. */
|
|
35
47
|
export function makeL1HttpTransport(rpcUrls: string[], opts?: { timeout?: number }) {
|
|
36
|
-
return fallback(rpcUrls.map(url => http(url, { batch: false, timeout: opts?.timeout })));
|
|
48
|
+
return wrapL1RpcTransport(fallback(rpcUrls.map(url => http(url, { batch: false, timeout: opts?.timeout }))));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Returns the HTTP status from an L1 RPC error's cause chain, if one is available. */
|
|
52
|
+
export function getL1RpcHttpStatus(err: unknown): number | undefined {
|
|
53
|
+
return getErrorCause(err, HttpRequestError)?.status;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Returns true when an L1 RPC error's cause chain contains the given HTTP status. */
|
|
57
|
+
export function isL1RpcHttpStatus(err: unknown, status: number): boolean {
|
|
58
|
+
return getL1RpcHttpStatus(err) === status;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function wrapL1RpcTransport(transport: FallbackTransport<HttpTransport[]>): FallbackTransport<HttpTransport[]> {
|
|
62
|
+
const wrappedTransport: FallbackTransport<HttpTransport[]> = parameters => {
|
|
63
|
+
const fallbackTransport = transport(parameters);
|
|
64
|
+
const request: typeof fallbackTransport.request = async args => {
|
|
65
|
+
try {
|
|
66
|
+
return await fallbackTransport.request(args);
|
|
67
|
+
} catch (err) {
|
|
68
|
+
throw err instanceof L1RpcError ? err : new L1RpcError('L1 RPC request failed', { cause: err });
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
return { ...fallbackTransport, request };
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
return wrappedTransport;
|
|
37
75
|
}
|
|
38
76
|
|
|
39
77
|
/**
|
package/src/config.ts
CHANGED
|
@@ -9,8 +9,8 @@ import {
|
|
|
9
9
|
optionalNumberConfigHelper,
|
|
10
10
|
} from '@aztec/foundation/config';
|
|
11
11
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
12
|
+
import l1ContractsDefaultEnv from '@aztec/l1-artifacts/network-defaults.json' with { type: 'json' };
|
|
12
13
|
|
|
13
|
-
import { l1ContractsDefaultEnv } from './generated/l1-contracts-defaults.js';
|
|
14
14
|
import { type L1TxUtilsConfig, l1TxUtilsConfigMappings } from './l1_tx_utils/config.js';
|
|
15
15
|
|
|
16
16
|
export type GenesisStateConfig = {
|
|
@@ -81,11 +81,21 @@ export type L1ContractsConfig = {
|
|
|
81
81
|
initialEthPerFeeAsset: bigint;
|
|
82
82
|
/** The number of seconds to wait for an exit */
|
|
83
83
|
exitDelaySeconds: number;
|
|
84
|
+
/** Validator set size at or below which the entry queue uses the bootstrap flush size. */
|
|
85
|
+
entryQueueBootstrapValidatorSetSize: number;
|
|
86
|
+
/** Number of validators admitted from the entry queue per flush during the bootstrap phase. */
|
|
87
|
+
entryQueueBootstrapFlushSize: number;
|
|
88
|
+
/** Minimum number of validators admitted from the entry queue per flush. */
|
|
89
|
+
entryQueueFlushSizeMin: number;
|
|
90
|
+
/** Divisor applied to the queue size to derive the normal per-flush admission count. */
|
|
91
|
+
entryQueueFlushSizeQuotient: number;
|
|
92
|
+
/** Maximum number of validators admitted from the entry queue per flush. */
|
|
93
|
+
entryQueueMaxFlushSize: number;
|
|
84
94
|
} & L1TxUtilsConfig;
|
|
85
95
|
|
|
86
96
|
/**
|
|
87
97
|
* Config mappings for L1ContractsConfig.
|
|
88
|
-
* Default values come from
|
|
98
|
+
* Default values come from l1-contracts/scripts/network-defaults.json (published via @aztec/l1-artifacts).
|
|
89
99
|
* Real deployments use forge scripts which require explicit env vars (vm.envUint).
|
|
90
100
|
*/
|
|
91
101
|
export const l1ContractsConfigMappings: ConfigMappingsType<L1ContractsConfig> = {
|
|
@@ -237,15 +247,75 @@ export const l1ContractsConfigMappings: ConfigMappingsType<L1ContractsConfig> =
|
|
|
237
247
|
description: 'The delay before a validator can exit the set',
|
|
238
248
|
...numberConfigHelper(l1ContractsDefaultEnv.AZTEC_EXIT_DELAY_SECONDS),
|
|
239
249
|
},
|
|
250
|
+
entryQueueBootstrapValidatorSetSize: {
|
|
251
|
+
env: 'AZTEC_ENTRY_QUEUE_BOOTSTRAP_VALIDATOR_SET_SIZE',
|
|
252
|
+
description: 'Validator set size at or below which the entry queue uses the bootstrap flush size.',
|
|
253
|
+
...numberConfigHelper(l1ContractsDefaultEnv.AZTEC_ENTRY_QUEUE_BOOTSTRAP_VALIDATOR_SET_SIZE),
|
|
254
|
+
},
|
|
255
|
+
entryQueueBootstrapFlushSize: {
|
|
256
|
+
env: 'AZTEC_ENTRY_QUEUE_BOOTSTRAP_FLUSH_SIZE',
|
|
257
|
+
description: 'Number of validators admitted from the entry queue per flush during the bootstrap phase.',
|
|
258
|
+
...numberConfigHelper(l1ContractsDefaultEnv.AZTEC_ENTRY_QUEUE_BOOTSTRAP_FLUSH_SIZE),
|
|
259
|
+
},
|
|
260
|
+
entryQueueFlushSizeMin: {
|
|
261
|
+
env: 'AZTEC_ENTRY_QUEUE_FLUSH_SIZE_MIN',
|
|
262
|
+
description: 'Minimum number of validators admitted from the entry queue per flush.',
|
|
263
|
+
...numberConfigHelper(l1ContractsDefaultEnv.AZTEC_ENTRY_QUEUE_FLUSH_SIZE_MIN),
|
|
264
|
+
},
|
|
265
|
+
entryQueueFlushSizeQuotient: {
|
|
266
|
+
env: 'AZTEC_ENTRY_QUEUE_FLUSH_SIZE_QUOTIENT',
|
|
267
|
+
description: 'Divisor applied to the queue size to derive the normal per-flush admission count.',
|
|
268
|
+
...numberConfigHelper(l1ContractsDefaultEnv.AZTEC_ENTRY_QUEUE_FLUSH_SIZE_QUOTIENT),
|
|
269
|
+
},
|
|
270
|
+
entryQueueMaxFlushSize: {
|
|
271
|
+
env: 'AZTEC_ENTRY_QUEUE_MAX_FLUSH_SIZE',
|
|
272
|
+
description: 'Maximum number of validators admitted from the entry queue per flush.',
|
|
273
|
+
...numberConfigHelper(l1ContractsDefaultEnv.AZTEC_ENTRY_QUEUE_MAX_FLUSH_SIZE),
|
|
274
|
+
},
|
|
240
275
|
...omitConfigMappings(l1TxUtilsConfigMappings, ['ethereumSlotDuration']),
|
|
241
276
|
};
|
|
242
277
|
|
|
243
278
|
/**
|
|
244
279
|
* Default L1 contracts configuration derived from l1ContractsConfigMappings.
|
|
245
|
-
* Source of truth:
|
|
280
|
+
* Source of truth: l1-contracts/scripts/network-defaults.json (published via @aztec/l1-artifacts).
|
|
246
281
|
*/
|
|
247
282
|
export const DefaultL1ContractsConfig = getDefaultConfig(l1ContractsConfigMappings);
|
|
248
283
|
|
|
284
|
+
/**
|
|
285
|
+
* Validates that `ethereumSlotDuration` and `aztecSlotDuration` are positive and that the L2 slot is an exact
|
|
286
|
+
* multiple of the L1 slot. Every L2 slot boundary must land on an L1 slot boundary; a non-multiple pairing
|
|
287
|
+
* desyncs the epoch/checkpoint timing math throughout the sequencer, validator client, and timetables. Returns
|
|
288
|
+
* a list of error messages (empty when valid).
|
|
289
|
+
*/
|
|
290
|
+
export function validateSlotDurations(
|
|
291
|
+
config: Pick<L1ContractsConfig, 'ethereumSlotDuration' | 'aztecSlotDuration'>,
|
|
292
|
+
): string[] {
|
|
293
|
+
const errors: string[] = [];
|
|
294
|
+
if (config.ethereumSlotDuration <= 0) {
|
|
295
|
+
errors.push(`ethereumSlotDuration must be positive (got ${config.ethereumSlotDuration})`);
|
|
296
|
+
}
|
|
297
|
+
if (config.aztecSlotDuration <= 0) {
|
|
298
|
+
errors.push(`aztecSlotDuration must be positive (got ${config.aztecSlotDuration})`);
|
|
299
|
+
}
|
|
300
|
+
if (config.ethereumSlotDuration > 0 && config.aztecSlotDuration % config.ethereumSlotDuration !== 0) {
|
|
301
|
+
errors.push(
|
|
302
|
+
`aztecSlotDuration (${config.aztecSlotDuration}s) must be a multiple of ethereumSlotDuration ` +
|
|
303
|
+
`(${config.ethereumSlotDuration}s)`,
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
return errors;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/** Throws if {@link validateSlotDurations} reports any errors. */
|
|
310
|
+
export function assertValidSlotDurations(
|
|
311
|
+
config: Pick<L1ContractsConfig, 'ethereumSlotDuration' | 'aztecSlotDuration'>,
|
|
312
|
+
): void {
|
|
313
|
+
const errors = validateSlotDurations(config);
|
|
314
|
+
if (errors.length > 0) {
|
|
315
|
+
throw new Error(`Invalid slot duration configuration:\n${errors.map(e => ` - ${e}`).join('\n')}`);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
249
319
|
export const genesisStateConfigMappings: ConfigMappingsType<GenesisStateConfig> = {
|
|
250
320
|
testAccounts: {
|
|
251
321
|
env: 'TEST_ACCOUNTS',
|
|
@@ -37,7 +37,7 @@ export type L1GovernanceContractAddresses = Pick<
|
|
|
37
37
|
'governanceAddress' | 'rollupAddress' | 'registryAddress' | 'governanceProposerAddress'
|
|
38
38
|
>;
|
|
39
39
|
|
|
40
|
-
// NOTE: Must be kept in sync with
|
|
40
|
+
// NOTE: Must be kept in sync with IGovernance.ProposalState in l1-contracts
|
|
41
41
|
export enum ProposalState {
|
|
42
42
|
Pending,
|
|
43
43
|
Active,
|
|
@@ -45,10 +45,21 @@ export enum ProposalState {
|
|
|
45
45
|
Executable,
|
|
46
46
|
Rejected,
|
|
47
47
|
Executed,
|
|
48
|
+
Droppable,
|
|
48
49
|
Dropped,
|
|
49
50
|
Expired,
|
|
50
51
|
}
|
|
51
52
|
|
|
53
|
+
/**
|
|
54
|
+
* Outcome of {@link ReadOnlyGovernanceContract.getPayloadProposalStatus} for a queried payload.
|
|
55
|
+
* - `'live'`: a proposal referencing the payload is still progressing (Pending/Active/Queued/
|
|
56
|
+
* Executable) or is `Droppable`, so signalling for it again is redundant or premature.
|
|
57
|
+
* - `'executed'`: no live proposal references the payload, but one was already executed within the
|
|
58
|
+
* bounded lookback (or a prior sweep observed the execution and memoized it).
|
|
59
|
+
* - `'none'`: no live or executed proposal references the payload within the bounded lookback.
|
|
60
|
+
*/
|
|
61
|
+
export type PayloadProposalStatus = 'live' | 'executed' | 'none';
|
|
62
|
+
|
|
52
63
|
/** Vote tallies on a single proposal. Both fields are mutated by `Governance.vote`. */
|
|
53
64
|
export interface Ballot {
|
|
54
65
|
yea: bigint;
|
|
@@ -119,6 +130,17 @@ const TERMINAL_PROPOSAL_STATES: ReadonlySet<ProposalState> = new Set([
|
|
|
119
130
|
ProposalState.Expired,
|
|
120
131
|
]);
|
|
121
132
|
|
|
133
|
+
// Set of `ProposalState` values in which a proposal is still progressing towards execution.
|
|
134
|
+
// `Droppable` is deliberately excluded: it is neither live (it cannot progress on its own) nor
|
|
135
|
+
// terminal (it can resume its lifecycle if the governanceProposer is restored), so it is handled
|
|
136
|
+
// separately and is never memoized as immutable.
|
|
137
|
+
const LIVE_PROPOSAL_STATES: ReadonlySet<ProposalState> = new Set([
|
|
138
|
+
ProposalState.Pending,
|
|
139
|
+
ProposalState.Active,
|
|
140
|
+
ProposalState.Queued,
|
|
141
|
+
ProposalState.Executable,
|
|
142
|
+
]);
|
|
143
|
+
|
|
122
144
|
// Hard upper bound on the wall-clock lifetime of any Governance proposal, in seconds.
|
|
123
145
|
// Each proposal stores its own snapshot of `ProposalConfiguration` at creation time and progresses
|
|
124
146
|
// through Pending -> Active -> Queued -> Executable using those frozen durations
|
|
@@ -173,6 +195,14 @@ export class ReadOnlyGovernanceContract {
|
|
|
173
195
|
*/
|
|
174
196
|
private readonly originalPayloadCache: Map<Hex, Hex | undefined> = new Map();
|
|
175
197
|
|
|
198
|
+
/**
|
|
199
|
+
* Payloads (lowercased hex) observed as the subject of an `Executed` Governance proposal. Execution
|
|
200
|
+
* is immutable on-chain, so this verdict never expires within a process. It restores the
|
|
201
|
+
* `'executed'` classification for payloads whose executed proposal has since aged past the bounded
|
|
202
|
+
* lookback below. Lost on restart, at which point the bounded-lookback limitation applies again.
|
|
203
|
+
*/
|
|
204
|
+
private readonly executedPayloads: Set<string> = new Set();
|
|
205
|
+
|
|
176
206
|
constructor(
|
|
177
207
|
address: Hex,
|
|
178
208
|
public readonly client: ViemClient,
|
|
@@ -263,30 +293,38 @@ export class ReadOnlyGovernanceContract {
|
|
|
263
293
|
}
|
|
264
294
|
|
|
265
295
|
/**
|
|
266
|
-
*
|
|
267
|
-
*
|
|
268
|
-
*
|
|
269
|
-
*
|
|
270
|
-
*
|
|
271
|
-
*
|
|
296
|
+
* Classifies the given original payload against the Governance proposal history. Distinguishes a
|
|
297
|
+
* payload that is still the subject of a live proposal (`'live'`) from one whose proposal was
|
|
298
|
+
* already executed (`'executed'`) from one that has no relevant proposal (`'none'`), so callers can
|
|
299
|
+
* stop re-signalling an executed payload while still re-signalling one whose proposal was merely
|
|
300
|
+
* rejected/dropped/expired (each GovernanceProposer round is independent and there is no
|
|
301
|
+
* payload-level uniqueness check on-chain).
|
|
302
|
+
*
|
|
303
|
+
* A proposal matches the payload either directly (its stored `payload` equals the target, as for
|
|
304
|
+
* `proposeWithLock` proposals) or via its `GSEPayload` wrapper unwrapping to the target. `'live'`
|
|
305
|
+
* (including `Droppable`) takes precedence over `'executed'`, so a payload re-submitted while a
|
|
306
|
+
* prior execution is still in the lookback window reads as `'live'`.
|
|
272
307
|
*
|
|
273
308
|
* Implemented as a bounded view-call sweep over `Governance.proposals` rather than an event scan,
|
|
274
309
|
* because `eth_getLogs` over the full deployment history of a long-lived rollup exceeds typical
|
|
275
310
|
* RPC block-range caps. The number of proposals (`proposalCount`) is small in practice, and we
|
|
276
|
-
* walk newest -> oldest with a hard early-stop on the protocol-wide proposal lifetime cap.
|
|
311
|
+
* walk newest -> oldest with a hard early-stop on the protocol-wide proposal lifetime cap. This
|
|
312
|
+
* makes the `'executed'` verdict a *bounded lookback* rather than permanent suppression: a proposal
|
|
313
|
+
* executed longer ago than the lifetime cap is only reported once it has been observed and
|
|
314
|
+
* memoized in-process (memo is lost on restart).
|
|
277
315
|
*/
|
|
278
|
-
public async
|
|
316
|
+
public async getPayloadProposalStatus(payload: Hex): Promise<PayloadProposalStatus> {
|
|
317
|
+
const target = payload.toLowerCase() as Hex;
|
|
318
|
+
|
|
279
319
|
const proposalCount = await this.getProposalCount();
|
|
280
320
|
if (proposalCount === 0n) {
|
|
281
|
-
return
|
|
321
|
+
return this.executedPayloads.has(target) ? 'executed' : 'none';
|
|
282
322
|
}
|
|
283
323
|
|
|
284
324
|
// Anything created before this cutoff is guaranteed terminal regardless of its frozen config.
|
|
285
325
|
const block = await this.client.getBlock();
|
|
286
326
|
const hardCutoff = block.timestamp - MAX_PROPOSAL_LIFETIME_SECONDS;
|
|
287
327
|
|
|
288
|
-
const target = payload.toLowerCase() as Hex;
|
|
289
|
-
|
|
290
328
|
// Proposals are append-only with monotonically non-decreasing creation timestamps, so iterating
|
|
291
329
|
// from newest -> oldest lets us early-stop as soon as we cross the lifetime cutoff.
|
|
292
330
|
for (let id = proposalCount - 1n; id >= 0n; id--) {
|
|
@@ -294,24 +332,29 @@ export class ReadOnlyGovernanceContract {
|
|
|
294
332
|
|
|
295
333
|
// Hard early-stop: every older proposal is also older than the cutoff and therefore terminal.
|
|
296
334
|
if (proposal.creation < hardCutoff) {
|
|
297
|
-
|
|
335
|
+
break;
|
|
298
336
|
}
|
|
299
337
|
|
|
338
|
+
const proposalPayload = proposal.payload.toString().toLowerCase();
|
|
300
339
|
const original = await this.getOriginalPayload(proposal.payload);
|
|
301
|
-
|
|
340
|
+
const matches = proposalPayload === target || (original !== undefined && original.toLowerCase() === target);
|
|
341
|
+
if (!matches) {
|
|
302
342
|
continue;
|
|
303
343
|
}
|
|
304
344
|
|
|
305
|
-
//
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
345
|
+
// A live or Droppable proposal blocks re-signalling and wins over any executed one.
|
|
346
|
+
if (LIVE_PROPOSAL_STATES.has(proposal.state) || proposal.state === ProposalState.Droppable) {
|
|
347
|
+
return 'live';
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
if (proposal.state === ProposalState.Executed) {
|
|
351
|
+
this.executedPayloads.add(target);
|
|
309
352
|
}
|
|
310
353
|
|
|
311
|
-
|
|
354
|
+
// Rejected/Dropped/Expired proposals allow re-proposing the same payload; keep scanning.
|
|
312
355
|
}
|
|
313
356
|
|
|
314
|
-
return
|
|
357
|
+
return this.executedPayloads.has(target) ? 'executed' : 'none';
|
|
315
358
|
}
|
|
316
359
|
|
|
317
360
|
/**
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
import type { L1TxRequest, L1TxUtils } from '../l1_tx_utils/index.js';
|
|
16
16
|
import type { ViemClient } from '../types.js';
|
|
17
17
|
import { type IEmpireBase, encodeSignal, encodeSignalWithSignature, signSignalWithSig } from './empire_base.js';
|
|
18
|
-
import { ReadOnlyGovernanceContract, extractProposalIdFromLogs } from './governance.js';
|
|
18
|
+
import { type PayloadProposalStatus, ReadOnlyGovernanceContract, extractProposalIdFromLogs } from './governance.js';
|
|
19
19
|
|
|
20
20
|
export class GovernanceProposerContract implements IEmpireBase {
|
|
21
21
|
private readonly proposer: GetContractReturnType<typeof GovernanceProposerAbi, ViemClient>;
|
|
@@ -38,16 +38,16 @@ export class GovernanceProposerContract implements IEmpireBase {
|
|
|
38
38
|
this.proposer = getContract({ address, abi: GovernanceProposerAbi, client });
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
public get address() {
|
|
41
|
+
public get address(): EthAddress {
|
|
42
42
|
return EthAddress.fromString(this.proposer.address);
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
public async getRollupAddress() {
|
|
45
|
+
public async getRollupAddress(): Promise<EthAddress> {
|
|
46
46
|
return EthAddress.fromString(await this.proposer.read.getInstance());
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
@memoize
|
|
50
|
-
public async getRegistryAddress() {
|
|
50
|
+
public async getRegistryAddress(): Promise<EthAddress> {
|
|
51
51
|
return EthAddress.fromString(await this.proposer.read.REGISTRY());
|
|
52
52
|
}
|
|
53
53
|
|
|
@@ -59,10 +59,6 @@ export class GovernanceProposerContract implements IEmpireBase {
|
|
|
59
59
|
return this.proposer.read.ROUND_SIZE();
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
public getInstance() {
|
|
63
|
-
return this.proposer.read.getInstance();
|
|
64
|
-
}
|
|
65
|
-
|
|
66
62
|
public computeRound(slot: SlotNumber): Promise<bigint> {
|
|
67
63
|
return this.proposer.read.computeRound([BigInt(slot)]);
|
|
68
64
|
}
|
|
@@ -107,7 +103,7 @@ export class GovernanceProposerContract implements IEmpireBase {
|
|
|
107
103
|
signer,
|
|
108
104
|
payload,
|
|
109
105
|
slot,
|
|
110
|
-
await this.
|
|
106
|
+
(await this.getRollupAddress()).toString(),
|
|
111
107
|
this.address.toString(),
|
|
112
108
|
chainId,
|
|
113
109
|
);
|
|
@@ -130,15 +126,15 @@ export class GovernanceProposerContract implements IEmpireBase {
|
|
|
130
126
|
}
|
|
131
127
|
|
|
132
128
|
/**
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
* implements the actual sweep against the Governance contract -- this method exists only as
|
|
136
|
-
* convenience wrapper so callers that already hold a GovernanceProposer reference don't have to
|
|
129
|
+
* Classifies the given original payload against the Governance proposal history (`'live'` /
|
|
130
|
+
* `'executed'` / `'none'`). Delegates to `ReadOnlyGovernanceContract.getPayloadProposalStatus`,
|
|
131
|
+
* which implements the actual sweep against the Governance contract -- this method exists only as
|
|
132
|
+
* a convenience wrapper so callers that already hold a GovernanceProposer reference don't have to
|
|
137
133
|
* resolve the Governance address themselves.
|
|
138
134
|
*/
|
|
139
|
-
public async
|
|
135
|
+
public async getPayloadProposalStatus(payload: Hex): Promise<PayloadProposalStatus> {
|
|
140
136
|
const governance = await this.getGovernance();
|
|
141
|
-
return governance.
|
|
137
|
+
return governance.getPayloadProposalStatus(payload);
|
|
142
138
|
}
|
|
143
139
|
|
|
144
140
|
/**
|
package/src/contracts/gse.ts
CHANGED
|
@@ -47,11 +47,28 @@ export class GSEContract {
|
|
|
47
47
|
return EthAddress.fromString(await this.gse.read.getGovernance());
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
getAttestersFromIndicesAtTime(
|
|
50
|
+
getAttestersFromIndicesAtTime(
|
|
51
|
+
instance: Hex | EthAddress,
|
|
52
|
+
ts: bigint,
|
|
53
|
+
indices: bigint[],
|
|
54
|
+
options?: { blockNumber?: bigint },
|
|
55
|
+
) {
|
|
56
|
+
if (instance instanceof EthAddress) {
|
|
57
|
+
instance = instance.toString();
|
|
58
|
+
}
|
|
59
|
+
return this.gse.read.getAttestersFromIndicesAtTime([instance, ts, indices], options);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Number of attesters registered to `instance` as of the given L1 timestamp. */
|
|
63
|
+
public async getAttesterCountAtTime(
|
|
64
|
+
instance: Hex | EthAddress,
|
|
65
|
+
ts: bigint,
|
|
66
|
+
options?: { blockNumber?: bigint },
|
|
67
|
+
): Promise<number> {
|
|
51
68
|
if (instance instanceof EthAddress) {
|
|
52
69
|
instance = instance.toString();
|
|
53
70
|
}
|
|
54
|
-
return this.gse.read.
|
|
71
|
+
return Number(await this.gse.read.getAttesterCountAtTime([instance, ts], options));
|
|
55
72
|
}
|
|
56
73
|
|
|
57
74
|
public async getRegistrationDigest(publicKey: ProjPointType<bigint>): Promise<ProjPointType<bigint>> {
|