@aztec/ethereum 0.0.1-commit.e558bd1c → 0.0.1-commit.e57c76e

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.
Files changed (159) hide show
  1. package/dest/client.d.ts +25 -3
  2. package/dest/client.d.ts.map +1 -1
  3. package/dest/client.js +60 -8
  4. package/dest/config.d.ts +8 -6
  5. package/dest/config.d.ts.map +1 -1
  6. package/dest/config.js +15 -11
  7. package/dest/contracts/chain_state_override.d.ts +78 -0
  8. package/dest/contracts/chain_state_override.d.ts.map +1 -0
  9. package/dest/contracts/chain_state_override.js +137 -0
  10. package/dest/contracts/fee_asset_price_oracle.d.ts +105 -0
  11. package/dest/contracts/fee_asset_price_oracle.d.ts.map +1 -0
  12. package/dest/contracts/fee_asset_price_oracle.js +656 -0
  13. package/dest/contracts/governance.d.ts +108 -33
  14. package/dest/contracts/governance.d.ts.map +1 -1
  15. package/dest/contracts/governance.js +196 -15
  16. package/dest/contracts/governance_proposer.d.ts +33 -1
  17. package/dest/contracts/governance_proposer.d.ts.map +1 -1
  18. package/dest/contracts/governance_proposer.js +53 -2
  19. package/dest/contracts/inbox.d.ts +3 -3
  20. package/dest/contracts/inbox.d.ts.map +1 -1
  21. package/dest/contracts/inbox.js +12 -7
  22. package/dest/contracts/index.d.ts +4 -3
  23. package/dest/contracts/index.d.ts.map +1 -1
  24. package/dest/contracts/index.js +3 -2
  25. package/dest/contracts/multicall.d.ts +127 -12
  26. package/dest/contracts/multicall.d.ts.map +1 -1
  27. package/dest/contracts/multicall.js +201 -94
  28. package/dest/contracts/outbox.d.ts +27 -4
  29. package/dest/contracts/outbox.d.ts.map +1 -1
  30. package/dest/contracts/outbox.js +34 -7
  31. package/dest/contracts/registry.d.ts +3 -1
  32. package/dest/contracts/registry.d.ts.map +1 -1
  33. package/dest/contracts/registry.js +30 -1
  34. package/dest/contracts/rollup.d.ts +5259 -29
  35. package/dest/contracts/rollup.d.ts.map +1 -1
  36. package/dest/contracts/rollup.js +388 -71
  37. package/dest/contracts/{tally_slashing_proposer.d.ts → slashing_proposer.d.ts} +3 -4
  38. package/dest/contracts/slashing_proposer.d.ts.map +1 -0
  39. package/dest/contracts/{tally_slashing_proposer.js → slashing_proposer.js} +13 -15
  40. package/dest/deploy_aztec_l1_contracts.d.ts +4 -8
  41. package/dest/deploy_aztec_l1_contracts.d.ts.map +1 -1
  42. package/dest/deploy_aztec_l1_contracts.js +35 -23
  43. package/dest/deploy_l1_contract.js +3 -3
  44. package/dest/generated/l1-contracts-defaults.d.ts +2 -2
  45. package/dest/generated/l1-contracts-defaults.js +2 -2
  46. package/dest/l1_artifacts.d.ts +14259 -15724
  47. package/dest/l1_artifacts.d.ts.map +1 -1
  48. package/dest/l1_artifacts.js +9 -24
  49. package/dest/l1_contract_addresses.d.ts +61 -65
  50. package/dest/l1_contract_addresses.d.ts.map +1 -1
  51. package/dest/l1_contract_addresses.js +101 -82
  52. package/dest/l1_reader.d.ts +5 -5
  53. package/dest/l1_reader.d.ts.map +1 -1
  54. package/dest/l1_reader.js +9 -7
  55. package/dest/l1_tx_utils/config.d.ts +7 -1
  56. package/dest/l1_tx_utils/config.d.ts.map +1 -1
  57. package/dest/l1_tx_utils/config.js +14 -1
  58. package/dest/l1_tx_utils/factory.d.ts +18 -10
  59. package/dest/l1_tx_utils/factory.d.ts.map +1 -1
  60. package/dest/l1_tx_utils/factory.js +17 -7
  61. package/dest/l1_tx_utils/fee-strategies/p75_competitive.js +1 -1
  62. package/dest/l1_tx_utils/fee-strategies/p75_competitive_blob_txs_only.js +1 -1
  63. package/dest/l1_tx_utils/forwarder_l1_tx_utils.d.ts +15 -15
  64. package/dest/l1_tx_utils/forwarder_l1_tx_utils.d.ts.map +1 -1
  65. package/dest/l1_tx_utils/forwarder_l1_tx_utils.js +9 -15
  66. package/dest/l1_tx_utils/index-blobs.d.ts +3 -3
  67. package/dest/l1_tx_utils/index-blobs.d.ts.map +1 -1
  68. package/dest/l1_tx_utils/index-blobs.js +2 -2
  69. package/dest/l1_tx_utils/index.d.ts +2 -1
  70. package/dest/l1_tx_utils/index.d.ts.map +1 -1
  71. package/dest/l1_tx_utils/index.js +1 -0
  72. package/dest/l1_tx_utils/l1_tx_utils.d.ts +26 -7
  73. package/dest/l1_tx_utils/l1_tx_utils.d.ts.map +1 -1
  74. package/dest/l1_tx_utils/l1_tx_utils.js +95 -60
  75. package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts +1 -1
  76. package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts.map +1 -1
  77. package/dest/l1_tx_utils/readonly_l1_tx_utils.js +10 -5
  78. package/dest/l1_tx_utils/tx_delayer.d.ts +58 -0
  79. package/dest/l1_tx_utils/tx_delayer.d.ts.map +1 -0
  80. package/dest/{test → l1_tx_utils}/tx_delayer.js +67 -37
  81. package/dest/publisher_manager.d.ts +21 -7
  82. package/dest/publisher_manager.d.ts.map +1 -1
  83. package/dest/publisher_manager.js +81 -7
  84. package/dest/queries.d.ts +13 -2
  85. package/dest/queries.d.ts.map +1 -1
  86. package/dest/queries.js +62 -3
  87. package/dest/test/chain_monitor.d.ts +23 -3
  88. package/dest/test/chain_monitor.d.ts.map +1 -1
  89. package/dest/test/chain_monitor.js +38 -2
  90. package/dest/test/eth_cheat_codes.d.ts +12 -4
  91. package/dest/test/eth_cheat_codes.d.ts.map +1 -1
  92. package/dest/test/eth_cheat_codes.js +17 -4
  93. package/dest/test/index.d.ts +1 -3
  94. package/dest/test/index.d.ts.map +1 -1
  95. package/dest/test/index.js +0 -2
  96. package/dest/test/rollup_cheat_codes.d.ts +31 -3
  97. package/dest/test/rollup_cheat_codes.d.ts.map +1 -1
  98. package/dest/test/rollup_cheat_codes.js +59 -7
  99. package/dest/test/start_anvil.d.ts +23 -3
  100. package/dest/test/start_anvil.d.ts.map +1 -1
  101. package/dest/test/start_anvil.js +143 -29
  102. package/dest/test/upgrade_utils.js +2 -2
  103. package/dest/utils.d.ts +9 -2
  104. package/dest/utils.d.ts.map +1 -1
  105. package/dest/utils.js +48 -16
  106. package/package.json +6 -8
  107. package/src/client.ts +66 -2
  108. package/src/config.ts +24 -15
  109. package/src/contracts/chain_state_override.ts +200 -0
  110. package/src/contracts/fee_asset_price_oracle.ts +285 -0
  111. package/src/contracts/governance.ts +269 -12
  112. package/src/contracts/governance_proposer.ts +54 -1
  113. package/src/contracts/inbox.ts +13 -5
  114. package/src/contracts/index.ts +3 -2
  115. package/src/contracts/multicall.ts +243 -105
  116. package/src/contracts/outbox.ts +42 -8
  117. package/src/contracts/registry.ts +31 -1
  118. package/src/contracts/rollup.ts +439 -73
  119. package/src/contracts/{tally_slashing_proposer.ts → slashing_proposer.ts} +14 -16
  120. package/src/deploy_aztec_l1_contracts.ts +57 -34
  121. package/src/deploy_l1_contract.ts +3 -3
  122. package/src/generated/l1-contracts-defaults.ts +2 -2
  123. package/src/l1_artifacts.ts +12 -35
  124. package/src/l1_contract_addresses.ts +120 -94
  125. package/src/l1_reader.ts +17 -10
  126. package/src/l1_tx_utils/config.ts +20 -0
  127. package/src/l1_tx_utils/factory.ts +31 -31
  128. package/src/l1_tx_utils/fee-strategies/p75_competitive.ts +1 -1
  129. package/src/l1_tx_utils/fee-strategies/p75_competitive_blob_txs_only.ts +1 -1
  130. package/src/l1_tx_utils/forwarder_l1_tx_utils.ts +43 -54
  131. package/src/l1_tx_utils/index-blobs.ts +2 -2
  132. package/src/l1_tx_utils/index.ts +1 -0
  133. package/src/l1_tx_utils/l1_tx_utils.ts +93 -42
  134. package/src/l1_tx_utils/readonly_l1_tx_utils.ts +10 -5
  135. package/src/{test → l1_tx_utils}/tx_delayer.ts +93 -56
  136. package/src/publisher_manager.ts +105 -10
  137. package/src/queries.ts +70 -4
  138. package/src/test/chain_monitor.ts +65 -3
  139. package/src/test/eth_cheat_codes.ts +16 -4
  140. package/src/test/index.ts +0 -2
  141. package/src/test/rollup_cheat_codes.ts +64 -6
  142. package/src/test/start_anvil.ts +177 -29
  143. package/src/test/upgrade_utils.ts +2 -2
  144. package/src/utils.ts +45 -18
  145. package/dest/contracts/empire_slashing_proposer.d.ts +0 -67
  146. package/dest/contracts/empire_slashing_proposer.d.ts.map +0 -1
  147. package/dest/contracts/empire_slashing_proposer.js +0 -207
  148. package/dest/contracts/tally_slashing_proposer.d.ts.map +0 -1
  149. package/dest/l1_tx_utils/l1_tx_utils_with_blobs.d.ts +0 -26
  150. package/dest/l1_tx_utils/l1_tx_utils_with_blobs.d.ts.map +0 -1
  151. package/dest/l1_tx_utils/l1_tx_utils_with_blobs.js +0 -26
  152. package/dest/test/delayed_tx_utils.d.ts +0 -13
  153. package/dest/test/delayed_tx_utils.d.ts.map +0 -1
  154. package/dest/test/delayed_tx_utils.js +0 -28
  155. package/dest/test/tx_delayer.d.ts +0 -36
  156. package/dest/test/tx_delayer.d.ts.map +0 -1
  157. package/src/contracts/empire_slashing_proposer.ts +0 -259
  158. package/src/l1_tx_utils/l1_tx_utils_with_blobs.ts +0 -77
  159. package/src/test/delayed_tx_utils.ts +0 -52
package/src/test/index.ts CHANGED
@@ -1,8 +1,6 @@
1
- export * from './delayed_tx_utils.js';
2
1
  export * from './eth_cheat_codes.js';
3
2
  export * from './eth_cheat_codes_with_state.js';
4
3
  export * from './start_anvil.js';
5
- export * from './tx_delayer.js';
6
4
  export * from './upgrade_utils.js';
7
5
  export * from './chain_monitor.js';
8
6
  export * from './rollup_cheat_codes.js';
@@ -15,6 +15,7 @@ import {
15
15
  getContract,
16
16
  hexToBigInt,
17
17
  http,
18
+ keccak256,
18
19
  } from 'viem';
19
20
 
20
21
  import { EthCheatCodes } from './eth_cheat_codes.js';
@@ -53,7 +54,17 @@ export class RollupCheatCodes {
53
54
  /** Returns the current slot */
54
55
  public async getSlot(): Promise<SlotNumber> {
55
56
  const ts = BigInt((await this.client.getBlock()).timestamp);
56
- return SlotNumber.fromBigInt(await this.rollup.read.getSlotAt([ts]));
57
+ return this.getSlotAt(ts);
58
+ }
59
+
60
+ /** Returns the slot number at a given timestamp. */
61
+ public async getSlotAt(timestamp: bigint): Promise<SlotNumber> {
62
+ return SlotNumber.fromBigInt(await this.rollup.read.getSlotAt([timestamp]));
63
+ }
64
+
65
+ /** Returns the timestamp for the start of a given slot. */
66
+ public async getTimestampForSlot(slot: SlotNumber): Promise<bigint> {
67
+ return await this.rollup.read.getTimestampForSlot([BigInt(slot)]);
57
68
  }
58
69
 
59
70
  /** Returns the number of seconds until the start of the given slot based on L1 block timestamp. */
@@ -168,6 +179,21 @@ export class RollupCheatCodes {
168
179
  return [timestamp, nextSlot];
169
180
  }
170
181
 
182
+ /**
183
+ * Warps L1 time to the start of an explicit absolute slot. Unlike `advanceSlots(N)` which is
184
+ * relative to the current L1 timestamp at call time and therefore races with real-time
185
+ * progression between query and warp, this lands at the slot regardless of latency.
186
+ *
187
+ * Throws if the requested slot is in the past relative to current L1 time (anvil cannot
188
+ * rewind).
189
+ */
190
+ public async advanceToSlot(slot: SlotNumber) {
191
+ const timestamp = await this.rollup.read.getTimestampForSlot([BigInt(slot)]);
192
+ await this.ethCheatCodes.warp(Number(timestamp), { silent: true, resetBlockInterval: true });
193
+ this.logger.warn(`Advanced to slot ${slot}`, { timestamp });
194
+ return timestamp;
195
+ }
196
+
171
197
  /**
172
198
  * Warps time in L1 equivalent to however many slots.
173
199
  * @param howMany - The number of slots to advance.
@@ -259,12 +285,14 @@ export class RollupCheatCodes {
259
285
  });
260
286
  }
261
287
 
262
- public insertOutbox(epoch: EpochNumber, outHash: bigint) {
288
+ public insertOutbox(epoch: EpochNumber, numCheckpointsInEpoch: number, outHash: bigint) {
263
289
  return this.ethCheatCodes.execWithPausedAnvil(async () => {
264
290
  const outboxAddress = await this.rollup.read.getOutbox();
265
- const epochRootSlot = OutboxContract.getEpochRootStorageSlot(epoch);
291
+ const epochRootSlot = OutboxContract.getEpochRootStorageSlot(epoch, numCheckpointsInEpoch);
266
292
  await this.ethCheatCodes.store(EthAddress.fromString(outboxAddress), epochRootSlot, outHash);
267
- this.logger.warn(`Advanced outbox to epoch ${epoch} with out hash ${outHash}`);
293
+ this.logger.warn(
294
+ `Advanced outbox to epoch ${epoch} numCheckpointsInEpoch ${numCheckpointsInEpoch} with out hash ${outHash}`,
295
+ );
268
296
  });
269
297
  }
270
298
 
@@ -313,7 +341,8 @@ export class RollupCheatCodes {
313
341
  }
314
342
 
315
343
  /**
316
- * Directly updates proving cost per mana.
344
+ * Directly updates proving cost per mana. Throws if the on-chain tx reverts
345
+ * (e.g. rate-limit cooldown, step cap, or floor) instead of silently succeeding.
317
346
  * @param ethValue - The new proving cost per mana in ETH
318
347
  */
319
348
  public async setProvingCostPerMana(ethValue: bigint) {
@@ -323,8 +352,37 @@ export class RollupCheatCodes {
323
352
  chain: this.client.chain,
324
353
  gasLimit: 1000000n,
325
354
  });
326
- await this.client.waitForTransactionReceipt({ hash });
355
+ const receipt = await this.client.waitForTransactionReceipt({ hash });
356
+ if (receipt.status !== 'success') {
357
+ throw new Error(
358
+ `setProvingCostPerMana(${ethValue}) reverted on L1 (tx ${hash}). ` +
359
+ `Likely FeeLib rate-limit (30-day cooldown or 1.5x step cap); ` +
360
+ `use clearProvingCostCooldown() between successive updates.`,
361
+ );
362
+ }
327
363
  this.logger.warn(`Updated proving cost per mana to ${ethValue}`);
328
364
  });
329
365
  }
366
+
367
+ /**
368
+ * Resets the 30-day proving-cost update cooldown enforced by FeeLib.updateProvingCostPerMana
369
+ * by zeroing `FeeStore.provingCostLastUpdate` directly in contract storage. Use between
370
+ * successive setProvingCostPerMana / bumpProvingCostPerMana calls so the later update can
371
+ * land instead of reverting. Does not touch L1 time, so PXE/tx-expiration state stays intact.
372
+ *
373
+ * @note This is tightly coupled to the `FeeStore` layout in
374
+ * l1-contracts/src/core/libraries/rollup/FeeLib.sol:
375
+ * slot + 0: CompressedFeeConfig config (uint256)
376
+ * slot + 1: L1GasOracleValues l1GasOracleValues (14+14+4 bytes, packed)
377
+ * slot + 2: uint64 provingCostLastUpdate (only member — zeroing the slot is safe)
378
+ * If the struct layout changes, update the offset below.
379
+ */
380
+ public async clearProvingCostCooldown() {
381
+ const feeStoreBaseSlot = hexToBigInt(keccak256(Buffer.from('aztec.fee.storage', 'utf-8')));
382
+ const provingCostLastUpdateSlot = feeStoreBaseSlot + 2n;
383
+ await this.ethCheatCodes.store(EthAddress.fromString(this.rollup.address), provingCostLastUpdateSlot, 0n, {
384
+ silent: true,
385
+ });
386
+ this.logger.warn(`Cleared proving-cost update cooldown`);
387
+ }
330
388
  }
@@ -1,10 +1,19 @@
1
1
  import { createLogger } from '@aztec/foundation/log';
2
2
  import { makeBackoff, retry } from '@aztec/foundation/retry';
3
+ import type { TestDateProvider } from '@aztec/foundation/timer';
3
4
  import { fileURLToPath } from '@aztec/foundation/url';
4
5
 
5
- import { type Anvil, createAnvil } from '@viem/anvil';
6
+ import { type ChildProcess, spawn } from 'child_process';
6
7
  import { dirname, resolve } from 'path';
7
8
 
9
+ /** Minimal interface matching the @viem/anvil Anvil shape used by callers. */
10
+ export interface Anvil {
11
+ readonly port: number;
12
+ readonly host: string;
13
+ readonly status: 'listening' | 'idle';
14
+ stop(): Promise<void>;
15
+ }
16
+
8
17
  /**
9
18
  * Ensures there's a running Anvil instance and returns the RPC URL.
10
19
  */
@@ -16,56 +25,195 @@ export async function startAnvil(
16
25
  captureMethodCalls?: boolean;
17
26
  accounts?: number;
18
27
  chainId?: number;
19
- /** The hardfork to use - note: @viem/anvil types are out of date but 'cancun' and 'latest' work */
28
+ /** The hardfork to use (e.g. 'cancun', 'latest'). */
20
29
  hardfork?: string;
30
+ /**
31
+ * Number of slots per epoch used by anvil to compute the 'finalized' and 'safe' block tags.
32
+ * Anvil reports `finalized = latest - slotsInAnEpoch * 2`.
33
+ * Defaults to 1 so the finalized block advances immediately, making tests that check
34
+ * L1-finality-based logic work without needing hundreds of mined blocks.
35
+ */
36
+ slotsInAnEpoch?: number;
37
+ /**
38
+ * If provided, the date provider will be synced to anvil's block time on every mined block.
39
+ * This keeps the dateProvider in lockstep with anvil's chain time, avoiding drift between
40
+ * the wall clock and the L1 chain when computing L1 slot timestamps.
41
+ */
42
+ dateProvider?: TestDateProvider;
21
43
  } = {},
22
44
  ): Promise<{ anvil: Anvil; methodCalls?: string[]; rpcUrl: string; stop: () => Promise<void> }> {
23
45
  const anvilBinary = resolve(dirname(fileURLToPath(import.meta.url)), '../../', 'scripts/anvil_kill_wrapper.sh');
24
46
  const logger = opts.log ? createLogger('ethereum:anvil') : undefined;
25
47
  const methodCalls = opts.captureMethodCalls ? ([] as string[]) : undefined;
26
48
 
27
- let port: number | undefined;
49
+ let detectedPort: number | undefined;
28
50
 
29
- // Start anvil.
30
- // We go via a wrapper script to ensure if the parent dies, anvil dies.
31
51
  const anvil = await retry(
32
52
  async () => {
33
- const anvil = createAnvil({
34
- anvilBinary,
35
- host: '127.0.0.1',
36
- port: opts.port ?? (process.env.ANVIL_PORT ? parseInt(process.env.ANVIL_PORT) : 8545),
37
- blockTime: opts.l1BlockTime,
38
- stopTimeout: 1000,
39
- accounts: opts.accounts ?? 20,
40
- gasLimit: 45_000_000n,
41
- chainId: opts.chainId ?? 31337,
53
+ const port = opts.port ?? (process.env.ANVIL_PORT ? parseInt(process.env.ANVIL_PORT) : 8545);
54
+ const args: string[] = [
55
+ '--host',
56
+ '127.0.0.1',
57
+ '--port',
58
+ String(port),
59
+ '--accounts',
60
+ String(opts.accounts ?? 20),
61
+ '--gas-limit',
62
+ String(45_000_000),
63
+ '--chain-id',
64
+ String(opts.chainId ?? 31337),
65
+ ];
66
+ if (opts.l1BlockTime !== undefined) {
67
+ args.push('--block-time', String(opts.l1BlockTime));
68
+ }
69
+ if (opts.hardfork !== undefined) {
70
+ args.push('--hardfork', opts.hardfork);
71
+ }
72
+ args.push('--slots-in-an-epoch', String(opts.slotsInAnEpoch ?? 1));
73
+
74
+ const child = spawn(anvilBinary, args, {
75
+ stdio: ['ignore', 'pipe', 'pipe'],
76
+ env: { ...process.env, RAYON_NUM_THREADS: '1' },
42
77
  });
43
78
 
44
- // Listen to the anvil output to get the port.
45
- const removeHandler = anvil.on('message', (message: string) => {
46
- logger?.debug(message.trim());
79
+ // Wait for "Listening on" or an early exit.
80
+ await new Promise<void>((resolve, reject) => {
81
+ let stderr = '';
82
+
83
+ const onStdout = (data: Buffer) => {
84
+ const text = data.toString();
85
+ logger?.debug(text.trim());
86
+ methodCalls?.push(...(text.match(/eth_[^\s]+/g) || []));
87
+
88
+ if (detectedPort === undefined && text.includes('Listening on')) {
89
+ const match = text.match(/Listening on ([^:]+):(\d+)/);
90
+ if (match) {
91
+ detectedPort = parseInt(match[2]);
92
+ }
93
+ }
94
+ if (detectedPort !== undefined) {
95
+ child.stdout?.removeListener('data', onStdout);
96
+ child.stderr?.removeListener('data', onStderr);
97
+ child.removeListener('close', onClose);
98
+ resolve();
99
+ }
100
+ };
47
101
 
48
- methodCalls?.push(...(message.match(/eth_[^\s]+/g) || []));
49
- if (port === undefined && message.includes('Listening on')) {
50
- port = parseInt(message.match(/Listening on ([^:]+):(\d+)/)![2]);
51
- }
102
+ const onStderr = (data: Buffer) => {
103
+ stderr += data.toString();
104
+ logger?.debug(data.toString().trim());
105
+ };
106
+
107
+ const onClose = (code: number | null) => {
108
+ child.stdout?.removeListener('data', onStdout);
109
+ child.stderr?.removeListener('data', onStderr);
110
+ reject(new Error(`Anvil exited with code ${code} before listening. stderr: ${stderr}`));
111
+ };
112
+
113
+ child.stdout?.on('data', onStdout);
114
+ child.stderr?.on('data', onStderr);
115
+ child.once('close', onClose);
52
116
  });
53
- await anvil.start();
54
- if (!logger && !opts.captureMethodCalls) {
55
- removeHandler();
117
+
118
+ // Continue piping for logging, method-call capture, and/or dateProvider sync after startup.
119
+ if (logger || opts.captureMethodCalls || opts.dateProvider) {
120
+ child.stdout?.on('data', (data: Buffer) => {
121
+ const text = data.toString();
122
+ logger?.debug(text.trim());
123
+ methodCalls?.push(...(text.match(/eth_[^\s]+/g) || []));
124
+ if (opts.dateProvider) {
125
+ syncDateProviderFromAnvilOutput(text, opts.dateProvider);
126
+ }
127
+ });
128
+ child.stderr?.on('data', (data: Buffer) => {
129
+ logger?.debug(data.toString().trim());
130
+ });
131
+ } else {
132
+ // Consume streams so the child process doesn't block on full pipe buffers.
133
+ child.stdout?.resume();
134
+ child.stderr?.resume();
56
135
  }
57
136
 
58
- return anvil;
137
+ return child;
59
138
  },
60
139
  'Start anvil',
61
140
  makeBackoff([5, 5, 5]),
62
141
  );
63
142
 
64
- if (!port) {
143
+ if (!detectedPort) {
65
144
  throw new Error('Failed to start anvil');
66
145
  }
67
146
 
68
- // Monkeypatch the anvil instance to include the actually assigned port
69
- // Object.defineProperty(anvil, 'port', { value: port, writable: false });
70
- return { anvil, methodCalls, stop: () => anvil.stop(), rpcUrl: `http://127.0.0.1:${port}` };
147
+ const port = detectedPort;
148
+ let status: 'listening' | 'idle' = 'listening';
149
+
150
+ anvil.once('close', () => {
151
+ status = 'idle';
152
+ });
153
+
154
+ const stop = async () => {
155
+ if (status === 'idle') {
156
+ return;
157
+ }
158
+ await killChild(anvil);
159
+ };
160
+
161
+ const anvilObj: Anvil = {
162
+ port,
163
+ host: '127.0.0.1',
164
+ get status() {
165
+ return status;
166
+ },
167
+ stop,
168
+ };
169
+
170
+ return { anvil: anvilObj, methodCalls, stop, rpcUrl: `http://127.0.0.1:${port}` };
171
+ }
172
+
173
+ /** Extracts block time from anvil stdout and syncs the dateProvider. */
174
+ function syncDateProviderFromAnvilOutput(text: string, dateProvider: TestDateProvider): void {
175
+ // Anvil logs mined blocks as:
176
+ // Block Time: "Fri, 20 Mar 2026 02:10:46 +0000"
177
+ const match = text.match(/Block Time:\s*"([^"]+)"/);
178
+ if (match) {
179
+ const blockTimeMs = new Date(match[1]).getTime();
180
+ if (!isNaN(blockTimeMs)) {
181
+ dateProvider.setTime(blockTimeMs);
182
+ }
183
+ }
184
+ }
185
+
186
+ /** Send SIGTERM, wait up to 5 s, then SIGKILL. All timers are always cleared. */
187
+ function killChild(child: ChildProcess): Promise<void> {
188
+ return new Promise<void>(resolve => {
189
+ if (child.exitCode !== null || child.killed) {
190
+ child.stdout?.destroy();
191
+ child.stderr?.destroy();
192
+ resolve();
193
+ return;
194
+ }
195
+
196
+ let killTimer: NodeJS.Timeout | undefined;
197
+
198
+ const onClose = () => {
199
+ if (killTimer !== undefined) {
200
+ clearTimeout(killTimer);
201
+ }
202
+ // Destroy stdio streams so their PipeWrap handles don't keep the event loop alive.
203
+ child.stdout?.destroy();
204
+ child.stderr?.destroy();
205
+ resolve();
206
+ };
207
+
208
+ child.once('close', onClose);
209
+ child.kill('SIGTERM');
210
+
211
+ killTimer = setTimeout(() => {
212
+ killTimer = undefined;
213
+ child.kill('SIGKILL');
214
+ }, 5000);
215
+
216
+ // Ensure the timer does not prevent Node from exiting.
217
+ killTimer.unref();
218
+ });
71
219
  }
@@ -7,7 +7,7 @@ import { type GetContractReturnType, type PrivateKeyAccount, getContract } from
7
7
 
8
8
  import { extractProposalIdFromLogs } from '../contracts/governance.js';
9
9
  import type { L1ContractAddresses } from '../l1_contract_addresses.js';
10
- import { createL1TxUtilsFromViemWallet } from '../l1_tx_utils/index.js';
10
+ import { createL1TxUtils } from '../l1_tx_utils/index.js';
11
11
  import type { ExtendedViemWalletClient, ViemPublicClient } from '../types.js';
12
12
  import { EthCheatCodes } from './eth_cheat_codes.js';
13
13
 
@@ -22,7 +22,7 @@ export async function executeGovernanceProposal(
22
22
  ) {
23
23
  const proposal = await governance.read.getProposal([proposalId]);
24
24
 
25
- const l1TxUtils = createL1TxUtilsFromViemWallet(l1Client);
25
+ const l1TxUtils = createL1TxUtils(l1Client);
26
26
 
27
27
  const waitL1Block = async () => {
28
28
  await l1TxUtils.sendAndMonitorTransaction({
package/src/utils.ts CHANGED
@@ -27,8 +27,8 @@ export interface L2Claim {
27
27
  export class FormattedViemError extends Error {
28
28
  metaMessages?: any[];
29
29
 
30
- constructor(message: string, metaMessages?: any[]) {
31
- super(message);
30
+ constructor(message: string, metaMessages?: any[], options?: ErrorOptions) {
31
+ super(message, options);
32
32
  this.name = 'FormattedViemError';
33
33
  this.metaMessages = metaMessages;
34
34
  }
@@ -170,6 +170,21 @@ function getNestedErrorData(error: unknown): string | undefined {
170
170
  return undefined;
171
171
  }
172
172
 
173
+ /**
174
+ * Truncates an error message to a safe length for log renderers.
175
+ * LogExplorer can only render up to 2500 characters in its summary view.
176
+ * We cap at 2000 to leave room for decorating context added by callers.
177
+ */
178
+ function truncateErrorMessage(message: string): string {
179
+ const MAX = 2000;
180
+ const CHUNK = 950;
181
+ if (message.length <= MAX) {
182
+ return message;
183
+ }
184
+ const truncated = message.length - 2 * CHUNK;
185
+ return message.slice(0, CHUNK) + `...${truncated} characters truncated...` + message.slice(-CHUNK);
186
+ }
187
+
173
188
  /**
174
189
  * Formats a Viem error into a FormattedViemError instance.
175
190
  * @param error - The error to format.
@@ -182,6 +197,8 @@ export function formatViemError(error: any, abi: Abi = ErrorsAbi): FormattedViem
182
197
  return error;
183
198
  }
184
199
 
200
+ const originalError = error;
201
+
185
202
  // First try to decode as a custom error using the ABI
186
203
  try {
187
204
  const data = getNestedErrorData(error);
@@ -192,7 +209,9 @@ export function formatViemError(error: any, abi: Abi = ErrorsAbi): FormattedViem
192
209
  data: data as Hex,
193
210
  });
194
211
  if (decoded) {
195
- return new FormattedViemError(`${decoded.errorName}(${decoded.args?.join(', ') ?? ''})`, error?.metaMessages);
212
+ return new FormattedViemError(`${decoded.errorName}(${decoded.args?.join(', ') ?? ''})`, error?.metaMessages, {
213
+ cause: originalError,
214
+ });
196
215
  }
197
216
  }
198
217
 
@@ -209,7 +228,7 @@ export function formatViemError(error: any, abi: Abi = ErrorsAbi): FormattedViem
209
228
  revertError.metaMessages && revertError.metaMessages?.length > 1
210
229
  ? revertError.metaMessages[1].trimStart()
211
230
  : '';
212
- return new FormattedViemError(`${errorName}${args}`, error?.metaMessages);
231
+ return new FormattedViemError(`${errorName}${args}`, error?.metaMessages, { cause: originalError });
213
232
  }
214
233
  }
215
234
  } catch {
@@ -232,22 +251,12 @@ export function formatViemError(error: any, abi: Abi = ErrorsAbi): FormattedViem
232
251
 
233
252
  // If it's a regular Error instance, return it with its message
234
253
  if (error instanceof Error) {
235
- return new FormattedViemError(error.message, (error as any)?.metaMessages);
236
- }
237
-
238
- const body = String(error);
239
- const length = body.length;
240
- // LogExplorer can only render up to 2500 characters in it's summary view. Try to keep the whole message below this number
241
- // Limit the error to 2000 chacaters in order to allow code higher up to decorate this error with extra details (up to 500 characters)
242
- if (length > 2000) {
243
- const chunk = 950;
244
- const truncated = length - 2 * chunk;
245
- return new FormattedViemError(
246
- body.slice(0, chunk) + `...${truncated} characters truncated...` + body.slice(-1 * chunk),
247
- );
254
+ return new FormattedViemError(truncateErrorMessage(error.message), (error as any)?.metaMessages, {
255
+ cause: originalError,
256
+ });
248
257
  }
249
258
 
250
- return new FormattedViemError(body);
259
+ return new FormattedViemError(truncateErrorMessage(String(error)), undefined, { cause: originalError });
251
260
  }
252
261
 
253
262
  function stripAbis(obj: any) {
@@ -273,6 +282,24 @@ function stripAbis(obj: any) {
273
282
  });
274
283
  }
275
284
 
285
+ /**
286
+ * Best-effort decode of a raw revert payload (`0x...`) against an ABI.
287
+ * Returns a human-readable `ErrorName(arg1, arg2, ...)` string, or `undefined` if the selector
288
+ * is unknown or the payload is empty. Use to surface decoded error names alongside the raw
289
+ * payload in log lines for operators.
290
+ */
291
+ export function tryDecodeRevertReason(data: Hex | undefined, abi: Abi): string | undefined {
292
+ if (!data || data === '0x') {
293
+ return undefined;
294
+ }
295
+ try {
296
+ const decoded = decodeErrorResult({ abi, data });
297
+ return `${decoded.errorName}(${decoded.args?.join(', ') ?? ''})`;
298
+ } catch {
299
+ return undefined;
300
+ }
301
+ }
302
+
276
303
  export function tryGetCustomErrorName(err: any) {
277
304
  try {
278
305
  // See https://viem.sh/docs/contract/simulateContract#handling-custom-errors
@@ -1,67 +0,0 @@
1
- import { SlotNumber } from '@aztec/foundation/branded-types';
2
- import { EthAddress } from '@aztec/foundation/eth-address';
3
- import EventEmitter from 'events';
4
- import { type Hex, type Log, type TypedDataDefinition } from 'viem';
5
- import type { L1TxRequest, L1TxUtils } from '../l1_tx_utils/index.js';
6
- import type { ViemClient } from '../types.js';
7
- import { type IEmpireBase } from './empire_base.js';
8
- export declare class ProposalAlreadyExecutedError extends Error {
9
- constructor(round: bigint);
10
- }
11
- export declare class EmpireSlashingProposerContract extends EventEmitter implements IEmpireBase {
12
- readonly client: ViemClient;
13
- private readonly logger;
14
- private readonly proposer;
15
- readonly type: "empire";
16
- constructor(client: ViemClient, address: Hex | EthAddress);
17
- get address(): EthAddress;
18
- getQuorumSize(): Promise<bigint>;
19
- getRoundSize(): Promise<bigint>;
20
- getLifetimeInRounds(): Promise<bigint>;
21
- getExecutionDelayInRounds(): Promise<bigint>;
22
- getCurrentRound(): Promise<bigint>;
23
- computeRound(slot: SlotNumber): Promise<bigint>;
24
- getInstance(): Promise<`0x${string}`>;
25
- getRoundInfo(rollupAddress: Hex, round: bigint): Promise<{
26
- lastSignalSlot: SlotNumber;
27
- payloadWithMostSignals: Hex;
28
- quorumReached: boolean;
29
- executed: boolean;
30
- }>;
31
- getPayloadSignals(rollupAddress: Hex, round: bigint, payload: Hex): Promise<bigint>;
32
- createSignalRequest(payload: Hex): L1TxRequest;
33
- createSignalRequestWithSignature(payload: Hex, slot: SlotNumber, chainId: number, signerAddress: Hex, signer: (msg: TypedDataDefinition) => Promise<Hex>): Promise<L1TxRequest>;
34
- listenToSubmittablePayloads(callback: (args: {
35
- payload: `0x${string}`;
36
- round: bigint;
37
- }) => unknown): import("viem").WatchContractEventReturnType;
38
- listenToPayloadSubmitted(callback: (args: {
39
- round: bigint;
40
- payload: `0x${string}`;
41
- }) => unknown): import("viem").WatchContractEventReturnType;
42
- listenToSignalCasted(callback: (args: {
43
- round: bigint;
44
- payload: `0x${string}`;
45
- signaler: `0x${string}`;
46
- }) => unknown): import("viem").WatchContractEventReturnType;
47
- /** Creates an L1TxRequest to submit the round winner for the given round. */
48
- buildExecuteRoundRequest(round: bigint): L1TxRequest;
49
- /** Tries to extract a PayloadSubmitted event from the given logs. */
50
- tryExtractPayloadSubmittedEvent(logs: Log[]): {
51
- eventName: "PayloadSubmitted";
52
- args: {
53
- payload: `0x${string}`;
54
- round: bigint;
55
- };
56
- } | undefined;
57
- /**
58
- * Wait for a round to be reached.
59
- *
60
- * @param round - The round to wait for.
61
- * @param pollingIntervalSeconds - The interval in seconds to poll for the round.
62
- * @returns True if the round was reached, false otherwise.
63
- */
64
- waitForRound(round: bigint, pollingIntervalSeconds?: number): Promise<boolean>;
65
- executeRound(txUtils: L1TxUtils, round: bigint | number): ReturnType<typeof txUtils.sendAndMonitorTransaction>;
66
- }
67
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZW1waXJlX3NsYXNoaW5nX3Byb3Bvc2VyLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvY29udHJhY3RzL2VtcGlyZV9zbGFzaGluZ19wcm9wb3Nlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFDN0QsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLCtCQUErQixDQUFDO0FBSzNELE9BQU8sWUFBWSxNQUFNLFFBQVEsQ0FBQztBQUNsQyxPQUFPLEVBRUwsS0FBSyxHQUFHLEVBQ1IsS0FBSyxHQUFHLEVBQ1IsS0FBSyxtQkFBbUIsRUFHekIsTUFBTSxNQUFNLENBQUM7QUFFZCxPQUFPLEtBQUssRUFBRSxXQUFXLEVBQUUsU0FBUyxFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDdEUsT0FBTyxLQUFLLEVBQUUsVUFBVSxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBRTlDLE9BQU8sRUFBRSxLQUFLLFdBQVcsRUFBOEQsTUFBTSxrQkFBa0IsQ0FBQztBQUVoSCxxQkFBYSw0QkFBNkIsU0FBUSxLQUFLO0lBQ3JELFlBQVksS0FBSyxFQUFFLE1BQU0sRUFFeEI7Q0FDRjtBQUVELHFCQUFhLDhCQUErQixTQUFRLFlBQWEsWUFBVyxXQUFXO2FBT25FLE1BQU0sRUFBRSxVQUFVO0lBTnBDLE9BQU8sQ0FBQyxRQUFRLENBQUMsTUFBTSxDQUErRDtJQUN0RixPQUFPLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBc0U7SUFFL0YsU0FBZ0IsSUFBSSxXQUFxQjtJQUV6QyxZQUNrQixNQUFNLEVBQUUsVUFBVSxFQUNsQyxPQUFPLEVBQUUsR0FBRyxHQUFHLFVBQVUsRUFRMUI7SUFFRCxJQUFXLE9BQU8sZUFFakI7SUFFTSxhQUFhLG9CQUVuQjtJQUVNLFlBQVksb0JBRWxCO0lBRU0sbUJBQW1CLG9CQUV6QjtJQUVNLHlCQUF5QixvQkFFL0I7SUFFTSxlQUFlLG9CQUVyQjtJQUVNLFlBQVksQ0FBQyxJQUFJLEVBQUUsVUFBVSxHQUFHLE9BQU8sQ0FBQyxNQUFNLENBQUMsQ0FFckQ7SUFFTSxXQUFXLDJCQUVqQjtJQUVZLFlBQVksQ0FDdkIsYUFBYSxFQUFFLEdBQUcsRUFDbEIsS0FBSyxFQUFFLE1BQU0sR0FDWixPQUFPLENBQUM7UUFBRSxjQUFjLEVBQUUsVUFBVSxDQUFDO1FBQUMsc0JBQXNCLEVBQUUsR0FBRyxDQUFDO1FBQUMsYUFBYSxFQUFFLE9BQU8sQ0FBQztRQUFDLFFBQVEsRUFBRSxPQUFPLENBQUE7S0FBRSxDQUFDLENBWWpIO0lBRU0saUJBQWlCLENBQUMsYUFBYSxFQUFFLEdBQUcsRUFBRSxLQUFLLEVBQUUsTUFBTSxFQUFFLE9BQU8sRUFBRSxHQUFHLEdBQUcsT0FBTyxDQUFDLE1BQU0sQ0FBQyxDQUV6RjtJQUVNLG1CQUFtQixDQUFDLE9BQU8sRUFBRSxHQUFHLEdBQUcsV0FBVyxDQU1wRDtJQUVZLGdDQUFnQyxDQUMzQyxPQUFPLEVBQUUsR0FBRyxFQUNaLElBQUksRUFBRSxVQUFVLEVBQ2hCLE9BQU8sRUFBRSxNQUFNLEVBQ2YsYUFBYSxFQUFFLEdBQUcsRUFDbEIsTUFBTSxFQUFFLENBQUMsR0FBRyxFQUFFLG1CQUFtQixLQUFLLE9BQU8sQ0FBQyxHQUFHLENBQUMsR0FDakQsT0FBTyxDQUFDLFdBQVcsQ0FBQyxDQWN0QjtJQUVNLDJCQUEyQixDQUFDLFFBQVEsRUFBRSxDQUFDLElBQUksRUFBRTtRQUFFLE9BQU8sRUFBRSxLQUFLLE1BQU0sRUFBRSxDQUFDO1FBQUMsS0FBSyxFQUFFLE1BQU0sQ0FBQTtLQUFFLEtBQUssT0FBTywrQ0FleEc7SUFFTSx3QkFBd0IsQ0FBQyxRQUFRLEVBQUUsQ0FBQyxJQUFJLEVBQUU7UUFBRSxLQUFLLEVBQUUsTUFBTSxDQUFDO1FBQUMsT0FBTyxFQUFFLEtBQUssTUFBTSxFQUFFLENBQUE7S0FBRSxLQUFLLE9BQU8sK0NBY3JHO0lBRU0sb0JBQW9CLENBQ3pCLFFBQVEsRUFBRSxDQUFDLElBQUksRUFBRTtRQUFFLEtBQUssRUFBRSxNQUFNLENBQUM7UUFBQyxPQUFPLEVBQUUsS0FBSyxNQUFNLEVBQUUsQ0FBQztRQUFDLFFBQVEsRUFBRSxLQUFLLE1BQU0sRUFBRSxDQUFBO0tBQUUsS0FBSyxPQUFPLCtDQWVoRztJQUVELDZFQUE2RTtJQUN0RSx3QkFBd0IsQ0FBQyxLQUFLLEVBQUUsTUFBTSxHQUFHLFdBQVcsQ0FVMUQ7SUFFRCxxRUFBcUU7SUFDOUQsK0JBQStCLENBQUMsSUFBSSxFQUFFLEdBQUcsRUFBRTs7Ozs7O2tCQUVqRDtJQUVEOzs7Ozs7T0FNRztJQUNJLFlBQVksQ0FBQyxLQUFLLEVBQUUsTUFBTSxFQUFFLHNCQUFzQixHQUFFLE1BQVUsR0FBRyxPQUFPLENBQUMsT0FBTyxDQUFDLENBYXZGO0lBRVksWUFBWSxDQUN2QixPQUFPLEVBQUUsU0FBUyxFQUNsQixLQUFLLEVBQUUsTUFBTSxHQUFHLE1BQU0sR0FDckIsVUFBVSxDQUFDLE9BQU8sT0FBTyxDQUFDLHlCQUF5QixDQUFDLENBbUN0RDtDQUNGIn0=
@@ -1 +0,0 @@
1
- {"version":3,"file":"empire_slashing_proposer.d.ts","sourceRoot":"","sources":["../../src/contracts/empire_slashing_proposer.ts"],"names":[],"mappings":"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"}