@aztec/ethereum 0.0.1-commit.b655e406 → 0.0.1-commit.b8a057fa

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 (240) hide show
  1. package/dest/account.d.ts +1 -1
  2. package/dest/chain.d.ts +1 -1
  3. package/dest/client.d.ts +25 -3
  4. package/dest/client.d.ts.map +1 -1
  5. package/dest/client.js +60 -4
  6. package/dest/config.d.ts +48 -73
  7. package/dest/config.d.ts.map +1 -1
  8. package/dest/config.js +120 -386
  9. package/dest/constants.d.ts +1 -1
  10. package/dest/contracts/chain_state_override.d.ts +78 -0
  11. package/dest/contracts/chain_state_override.d.ts.map +1 -0
  12. package/dest/contracts/chain_state_override.js +137 -0
  13. package/dest/contracts/empire_base.d.ts +7 -5
  14. package/dest/contracts/empire_base.d.ts.map +1 -1
  15. package/dest/contracts/empire_base.js +1 -1
  16. package/dest/contracts/errors.d.ts +1 -1
  17. package/dest/contracts/errors.d.ts.map +1 -1
  18. package/dest/contracts/fee_asset_handler.d.ts +6 -5
  19. package/dest/contracts/fee_asset_handler.d.ts.map +1 -1
  20. package/dest/contracts/fee_asset_handler.js +11 -9
  21. package/dest/contracts/fee_asset_price_oracle.d.ts +105 -0
  22. package/dest/contracts/fee_asset_price_oracle.d.ts.map +1 -0
  23. package/dest/contracts/fee_asset_price_oracle.js +656 -0
  24. package/dest/contracts/fee_juice.d.ts +1 -1
  25. package/dest/contracts/fee_juice.d.ts.map +1 -1
  26. package/dest/contracts/governance.d.ts +113 -36
  27. package/dest/contracts/governance.d.ts.map +1 -1
  28. package/dest/contracts/governance.js +207 -16
  29. package/dest/contracts/governance_proposer.d.ts +38 -4
  30. package/dest/contracts/governance_proposer.d.ts.map +1 -1
  31. package/dest/contracts/governance_proposer.js +455 -12
  32. package/dest/contracts/gse.d.ts +4 -2
  33. package/dest/contracts/gse.d.ts.map +1 -1
  34. package/dest/contracts/gse.js +2 -2
  35. package/dest/contracts/inbox.d.ts +24 -3
  36. package/dest/contracts/inbox.d.ts.map +1 -1
  37. package/dest/contracts/inbox.js +42 -2
  38. package/dest/contracts/index.d.ts +6 -3
  39. package/dest/contracts/index.d.ts.map +1 -1
  40. package/dest/contracts/index.js +5 -2
  41. package/dest/contracts/log.d.ts +13 -0
  42. package/dest/contracts/log.d.ts.map +1 -0
  43. package/dest/contracts/log.js +1 -0
  44. package/dest/contracts/multicall.d.ts +130 -13
  45. package/dest/contracts/multicall.d.ts.map +1 -1
  46. package/dest/contracts/multicall.js +203 -93
  47. package/dest/contracts/outbox.d.ts +64 -0
  48. package/dest/contracts/outbox.d.ts.map +1 -0
  49. package/dest/contracts/outbox.js +113 -0
  50. package/dest/contracts/registry.d.ts +3 -1
  51. package/dest/contracts/registry.d.ts.map +1 -1
  52. package/dest/contracts/registry.js +30 -1
  53. package/dest/contracts/rollup.d.ts +5591 -131
  54. package/dest/contracts/rollup.d.ts.map +1 -1
  55. package/dest/contracts/rollup.js +1125 -232
  56. package/dest/contracts/slasher_contract.d.ts +1 -1
  57. package/dest/contracts/slasher_contract.d.ts.map +1 -1
  58. package/dest/contracts/{tally_slashing_proposer.d.ts → slashing_proposer.d.ts} +11 -10
  59. package/dest/contracts/slashing_proposer.d.ts.map +1 -0
  60. package/dest/contracts/{tally_slashing_proposer.js → slashing_proposer.js} +21 -16
  61. package/dest/contracts/utils.d.ts +1 -1
  62. package/dest/deploy_aztec_l1_contracts.d.ts +267 -0
  63. package/dest/deploy_aztec_l1_contracts.d.ts.map +1 -0
  64. package/dest/deploy_aztec_l1_contracts.js +417 -0
  65. package/dest/deploy_l1_contract.d.ts +68 -0
  66. package/dest/deploy_l1_contract.d.ts.map +1 -0
  67. package/dest/deploy_l1_contract.js +312 -0
  68. package/dest/eth-signer/eth-signer.d.ts +1 -1
  69. package/dest/eth-signer/index.d.ts +1 -1
  70. package/dest/forwarder_proxy.d.ts +32 -0
  71. package/dest/forwarder_proxy.d.ts.map +1 -0
  72. package/dest/forwarder_proxy.js +93 -0
  73. package/dest/l1_artifacts.d.ts +21007 -16779
  74. package/dest/l1_artifacts.d.ts.map +1 -1
  75. package/dest/l1_artifacts.js +9 -24
  76. package/dest/l1_contract_addresses.d.ts +61 -65
  77. package/dest/l1_contract_addresses.d.ts.map +1 -1
  78. package/dest/l1_contract_addresses.js +99 -80
  79. package/dest/l1_reader.d.ts +7 -5
  80. package/dest/l1_reader.d.ts.map +1 -1
  81. package/dest/l1_reader.js +15 -7
  82. package/dest/l1_tx_utils/config.d.ts +11 -5
  83. package/dest/l1_tx_utils/config.d.ts.map +1 -1
  84. package/dest/l1_tx_utils/config.js +46 -10
  85. package/dest/l1_tx_utils/constants.d.ts +8 -2
  86. package/dest/l1_tx_utils/constants.d.ts.map +1 -1
  87. package/dest/l1_tx_utils/constants.js +27 -2
  88. package/dest/l1_tx_utils/factory.d.ts +18 -10
  89. package/dest/l1_tx_utils/factory.d.ts.map +1 -1
  90. package/dest/l1_tx_utils/factory.js +17 -7
  91. package/dest/l1_tx_utils/fee-strategies/index.d.ts +10 -0
  92. package/dest/l1_tx_utils/fee-strategies/index.d.ts.map +1 -0
  93. package/dest/l1_tx_utils/fee-strategies/index.js +12 -0
  94. package/dest/l1_tx_utils/fee-strategies/p75_competitive.d.ts +8 -0
  95. package/dest/l1_tx_utils/fee-strategies/p75_competitive.d.ts.map +1 -0
  96. package/dest/l1_tx_utils/fee-strategies/p75_competitive.js +129 -0
  97. package/dest/l1_tx_utils/fee-strategies/p75_competitive_blob_txs_only.d.ts +23 -0
  98. package/dest/l1_tx_utils/fee-strategies/p75_competitive_blob_txs_only.d.ts.map +1 -0
  99. package/dest/l1_tx_utils/fee-strategies/p75_competitive_blob_txs_only.js +191 -0
  100. package/dest/l1_tx_utils/fee-strategies/types.d.ts +51 -0
  101. package/dest/l1_tx_utils/fee-strategies/types.d.ts.map +1 -0
  102. package/dest/l1_tx_utils/fee-strategies/types.js +3 -0
  103. package/dest/l1_tx_utils/forwarder_l1_tx_utils.d.ts +41 -0
  104. package/dest/l1_tx_utils/forwarder_l1_tx_utils.d.ts.map +1 -0
  105. package/dest/l1_tx_utils/forwarder_l1_tx_utils.js +42 -0
  106. package/dest/l1_tx_utils/index-blobs.d.ts +3 -0
  107. package/dest/l1_tx_utils/index-blobs.d.ts.map +1 -0
  108. package/dest/l1_tx_utils/index-blobs.js +2 -0
  109. package/dest/l1_tx_utils/index.d.ts +4 -1
  110. package/dest/l1_tx_utils/index.d.ts.map +1 -1
  111. package/dest/l1_tx_utils/index.js +3 -0
  112. package/dest/l1_tx_utils/interfaces.d.ts +2 -2
  113. package/dest/l1_tx_utils/interfaces.d.ts.map +1 -1
  114. package/dest/l1_tx_utils/l1_fee_analyzer.d.ts +252 -0
  115. package/dest/l1_tx_utils/l1_fee_analyzer.d.ts.map +1 -0
  116. package/dest/l1_tx_utils/l1_fee_analyzer.js +568 -0
  117. package/dest/l1_tx_utils/l1_tx_utils.d.ts +26 -8
  118. package/dest/l1_tx_utils/l1_tx_utils.d.ts.map +1 -1
  119. package/dest/l1_tx_utils/l1_tx_utils.js +202 -92
  120. package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts +19 -30
  121. package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts.map +1 -1
  122. package/dest/l1_tx_utils/readonly_l1_tx_utils.js +65 -168
  123. package/dest/l1_tx_utils/signer.d.ts +1 -1
  124. package/dest/l1_tx_utils/tx_delayer.d.ts +58 -0
  125. package/dest/l1_tx_utils/tx_delayer.d.ts.map +1 -0
  126. package/dest/{test → l1_tx_utils}/tx_delayer.js +70 -39
  127. package/dest/l1_tx_utils/types.d.ts +27 -1
  128. package/dest/l1_tx_utils/types.d.ts.map +1 -1
  129. package/dest/l1_tx_utils/types.js +10 -0
  130. package/dest/l1_tx_utils/utils.d.ts +1 -1
  131. package/dest/l1_types.d.ts +1 -1
  132. package/dest/publisher_manager.d.ts +34 -7
  133. package/dest/publisher_manager.d.ts.map +1 -1
  134. package/dest/publisher_manager.js +119 -8
  135. package/dest/queries.d.ts +14 -3
  136. package/dest/queries.d.ts.map +1 -1
  137. package/dest/queries.js +84 -9
  138. package/dest/test/blob_kzg_warmup.d.ts +19 -0
  139. package/dest/test/blob_kzg_warmup.d.ts.map +1 -0
  140. package/dest/test/blob_kzg_warmup.js +64 -0
  141. package/dest/test/chain_monitor.d.ts +80 -26
  142. package/dest/test/chain_monitor.d.ts.map +1 -1
  143. package/dest/test/chain_monitor.js +137 -39
  144. package/dest/test/eth_cheat_codes.d.ts +47 -8
  145. package/dest/test/eth_cheat_codes.d.ts.map +1 -1
  146. package/dest/test/eth_cheat_codes.js +100 -44
  147. package/dest/test/eth_cheat_codes_with_state.d.ts +1 -1
  148. package/dest/test/eth_cheat_codes_with_state.d.ts.map +1 -1
  149. package/dest/test/index.d.ts +2 -3
  150. package/dest/test/index.d.ts.map +1 -1
  151. package/dest/test/index.js +1 -2
  152. package/dest/test/rollup_cheat_codes.d.ts +75 -14
  153. package/dest/test/rollup_cheat_codes.d.ts.map +1 -1
  154. package/dest/test/rollup_cheat_codes.js +145 -42
  155. package/dest/test/start_anvil.d.ts +25 -2
  156. package/dest/test/start_anvil.d.ts.map +1 -1
  157. package/dest/test/start_anvil.js +143 -28
  158. package/dest/test/upgrade_utils.d.ts +1 -1
  159. package/dest/test/upgrade_utils.js +2 -2
  160. package/dest/types.d.ts +57 -2
  161. package/dest/types.d.ts.map +1 -1
  162. package/dest/utils.d.ts +24 -4
  163. package/dest/utils.d.ts.map +1 -1
  164. package/dest/utils.js +112 -16
  165. package/dest/zkPassportVerifierAddress.d.ts +1 -1
  166. package/package.json +34 -17
  167. package/src/client.ts +66 -2
  168. package/src/config.ts +162 -470
  169. package/src/contracts/README.md +157 -0
  170. package/src/contracts/chain_state_override.ts +200 -0
  171. package/src/contracts/empire_base.ts +6 -5
  172. package/src/contracts/fee_asset_handler.ts +10 -7
  173. package/src/contracts/fee_asset_price_oracle.ts +285 -0
  174. package/src/contracts/governance.ts +279 -13
  175. package/src/contracts/governance_proposer.ts +74 -7
  176. package/src/contracts/gse.ts +7 -2
  177. package/src/contracts/inbox.ts +64 -4
  178. package/src/contracts/index.ts +5 -2
  179. package/src/contracts/log.ts +13 -0
  180. package/src/contracts/multicall.ts +247 -103
  181. package/src/contracts/outbox.ts +132 -0
  182. package/src/contracts/registry.ts +31 -1
  183. package/src/contracts/rollup.ts +830 -194
  184. package/src/contracts/{tally_slashing_proposer.ts → slashing_proposer.ts} +26 -21
  185. package/src/deploy_aztec_l1_contracts.ts +652 -0
  186. package/src/deploy_l1_contract.ts +362 -0
  187. package/src/forwarder_proxy.ts +108 -0
  188. package/src/l1_artifacts.ts +12 -35
  189. package/src/l1_contract_addresses.ts +120 -92
  190. package/src/l1_reader.ts +25 -10
  191. package/src/l1_tx_utils/config.ts +55 -14
  192. package/src/l1_tx_utils/constants.ts +13 -2
  193. package/src/l1_tx_utils/factory.ts +31 -31
  194. package/src/l1_tx_utils/fee-strategies/index.ts +22 -0
  195. package/src/l1_tx_utils/fee-strategies/p75_competitive.ts +163 -0
  196. package/src/l1_tx_utils/fee-strategies/p75_competitive_blob_txs_only.ts +245 -0
  197. package/src/l1_tx_utils/fee-strategies/types.ts +56 -0
  198. package/src/l1_tx_utils/forwarder_l1_tx_utils.ts +108 -0
  199. package/src/l1_tx_utils/index-blobs.ts +2 -0
  200. package/src/l1_tx_utils/index.ts +3 -0
  201. package/src/l1_tx_utils/interfaces.ts +1 -1
  202. package/src/l1_tx_utils/l1_fee_analyzer.ts +875 -0
  203. package/src/l1_tx_utils/l1_tx_utils.ts +194 -72
  204. package/src/l1_tx_utils/readonly_l1_tx_utils.ts +79 -214
  205. package/src/{test → l1_tx_utils}/tx_delayer.ts +97 -58
  206. package/src/l1_tx_utils/types.ts +32 -0
  207. package/src/publisher_manager.ts +145 -12
  208. package/src/queries.ts +93 -10
  209. package/src/test/blob_kzg_warmup.ts +65 -0
  210. package/src/test/chain_monitor.ts +198 -51
  211. package/src/test/eth_cheat_codes.ts +91 -48
  212. package/src/test/index.ts +1 -2
  213. package/src/test/rollup_cheat_codes.ts +174 -45
  214. package/src/test/start_anvil.ts +179 -27
  215. package/src/test/upgrade_utils.ts +2 -2
  216. package/src/types.ts +62 -0
  217. package/src/utils.ts +128 -19
  218. package/dest/contracts/empire_slashing_proposer.d.ts +0 -65
  219. package/dest/contracts/empire_slashing_proposer.d.ts.map +0 -1
  220. package/dest/contracts/empire_slashing_proposer.js +0 -194
  221. package/dest/contracts/tally_slashing_proposer.d.ts.map +0 -1
  222. package/dest/deploy_l1_contracts.d.ts +0 -226
  223. package/dest/deploy_l1_contracts.d.ts.map +0 -1
  224. package/dest/deploy_l1_contracts.js +0 -1473
  225. package/dest/index.d.ts +0 -18
  226. package/dest/index.d.ts.map +0 -1
  227. package/dest/index.js +0 -17
  228. package/dest/l1_tx_utils/l1_tx_utils_with_blobs.d.ts +0 -26
  229. package/dest/l1_tx_utils/l1_tx_utils_with_blobs.d.ts.map +0 -1
  230. package/dest/l1_tx_utils/l1_tx_utils_with_blobs.js +0 -26
  231. package/dest/test/delayed_tx_utils.d.ts +0 -13
  232. package/dest/test/delayed_tx_utils.d.ts.map +0 -1
  233. package/dest/test/delayed_tx_utils.js +0 -28
  234. package/dest/test/tx_delayer.d.ts +0 -36
  235. package/dest/test/tx_delayer.d.ts.map +0 -1
  236. package/src/contracts/empire_slashing_proposer.ts +0 -259
  237. package/src/deploy_l1_contracts.ts +0 -1849
  238. package/src/index.ts +0 -17
  239. package/src/l1_tx_utils/l1_tx_utils_with_blobs.ts +0 -77
  240. package/src/test/delayed_tx_utils.ts +0 -52
@@ -1,7 +1,10 @@
1
- import { RollupContract, type ViemPublicClient } from '@aztec/ethereum';
1
+ import { OutboxContract, RollupContract } from '@aztec/ethereum/contracts';
2
2
  import type { L1ContractAddresses } from '@aztec/ethereum/l1-contract-addresses';
3
+ import type { ViemPublicClient } from '@aztec/ethereum/types';
4
+ import { CheckpointNumber, EpochNumber, SlotNumber } from '@aztec/foundation/branded-types';
3
5
  import { EthAddress } from '@aztec/foundation/eth-address';
4
6
  import { createLogger } from '@aztec/foundation/log';
7
+ import { retryUntil } from '@aztec/foundation/retry';
5
8
  import type { DateProvider } from '@aztec/foundation/timer';
6
9
  import { RollupAbi } from '@aztec/l1-artifacts/RollupAbi';
7
10
 
@@ -13,8 +16,8 @@ import {
13
16
  getContract,
14
17
  hexToBigInt,
15
18
  http,
19
+ keccak256,
16
20
  } from 'viem';
17
- import { foundry } from 'viem/chains';
18
21
 
19
22
  import { EthCheatCodes } from './eth_cheat_codes.js';
20
23
 
@@ -30,8 +33,8 @@ export class RollupCheatCodes {
30
33
  addresses: Pick<L1ContractAddresses, 'rollupAddress'>,
31
34
  ) {
32
35
  this.client = createPublicClient({
33
- chain: foundry,
34
- transport: fallback(ethCheatCodes.rpcUrls.map(url => http(url))),
36
+ chain: ethCheatCodes.chain,
37
+ transport: fallback(ethCheatCodes.rpcUrls.map(url => http(url, { batch: false }))),
35
38
  });
36
39
  this.rollup = getContract({
37
40
  abi: RollupAbi,
@@ -50,15 +53,34 @@ export class RollupCheatCodes {
50
53
  }
51
54
 
52
55
  /** Returns the current slot */
53
- public async getSlot() {
56
+ public async getSlot(): Promise<SlotNumber> {
54
57
  const ts = BigInt((await this.client.getBlock()).timestamp);
55
- return await this.rollup.read.getSlotAt([ts]);
58
+ return this.getSlotAt(ts);
59
+ }
60
+
61
+ /** Returns the slot number at a given timestamp. */
62
+ public async getSlotAt(timestamp: bigint): Promise<SlotNumber> {
63
+ return SlotNumber.fromBigInt(await this.rollup.read.getSlotAt([timestamp]));
64
+ }
65
+
66
+ /** Returns the timestamp for the start of a given slot. */
67
+ public async getTimestampForSlot(slot: SlotNumber): Promise<bigint> {
68
+ return await this.rollup.read.getTimestampForSlot([BigInt(slot)]);
69
+ }
70
+
71
+ /** Returns the number of seconds until the start of the given slot based on L1 block timestamp. */
72
+ public async getSecondsUntilSlot(slot: SlotNumber): Promise<number> {
73
+ const [currentTimestamp, targetTimestamp] = await Promise.all([
74
+ this.client.getBlock().then(b => BigInt(b.timestamp)),
75
+ this.rollup.read.getTimestampForSlot([BigInt(slot)]),
76
+ ]);
77
+ return Math.max(0, Number(targetTimestamp - currentTimestamp));
56
78
  }
57
79
 
58
80
  /** Returns the current epoch */
59
- public async getEpoch() {
81
+ public async getEpoch(): Promise<EpochNumber> {
60
82
  const slotNumber = await this.getSlot();
61
- return await this.rollup.read.getEpochAtSlot([slotNumber]);
83
+ return EpochNumber.fromBigInt(await this.rollup.read.getEpochAtSlot([BigInt(slotNumber)]));
62
84
  }
63
85
 
64
86
  /**
@@ -66,13 +88,13 @@ export class RollupCheatCodes {
66
88
  * @returns The pending and proven chain tips
67
89
  */
68
90
  public async getTips(): Promise<{
69
- /** The pending chain tip */ pending: bigint;
70
- /** The proven chain tip */ proven: bigint;
91
+ /** The pending chain tip */ pending: CheckpointNumber;
92
+ /** The proven chain tip */ proven: CheckpointNumber;
71
93
  }> {
72
- const res = await this.rollup.read.getTips();
94
+ const { pending, proven } = await this.rollup.read.getTips();
73
95
  return {
74
- pending: res.pendingBlockNumber,
75
- proven: res.provenBlockNumber,
96
+ pending: CheckpointNumber.fromBigInt(pending),
97
+ proven: CheckpointNumber.fromBigInt(proven),
76
98
  };
77
99
  }
78
100
 
@@ -81,16 +103,16 @@ export class RollupCheatCodes {
81
103
  */
82
104
  public async debugRollup() {
83
105
  const rollup = new RollupContract(this.client, this.rollup.address);
84
- const pendingNum = await rollup.getBlockNumber();
85
- const provenNum = await rollup.getProvenBlockNumber();
106
+ const pendingNum = await rollup.getCheckpointNumber();
107
+ const provenNum = await rollup.getProvenCheckpointNumber();
86
108
  const validators = await rollup.getAttesters();
87
109
  const committee = await rollup.getCurrentEpochCommittee();
88
110
  const archive = await rollup.archive();
89
111
  const slot = await this.getSlot();
90
112
  const epochNum = await rollup.getEpochNumberForSlotNumber(slot);
91
113
 
92
- this.logger.info(`Pending block num: ${pendingNum}`);
93
- this.logger.info(`Proven block num: ${provenNum}`);
114
+ this.logger.info(`Pending checkpoint num: ${pendingNum}`);
115
+ this.logger.info(`Proven checkpoint num: ${provenNum}`);
94
116
  this.logger.info(`Validators: ${validators.map(v => v.toString()).join(', ')}`);
95
117
  this.logger.info(`Committee: ${committee?.map(v => v.toString()).join(', ')}`);
96
118
  this.logger.info(`Archive: ${archive}`);
@@ -101,13 +123,13 @@ export class RollupCheatCodes {
101
123
  /** Fetches the epoch and slot duration config from the rollup contract */
102
124
  public async getConfig(): Promise<{
103
125
  /** Epoch duration */ epochDuration: bigint;
104
- /** Slot duration */ slotDuration: bigint;
126
+ /** Slot duration */ slotDuration: number;
105
127
  }> {
106
128
  const [epochDuration, slotDuration] = await Promise.all([
107
129
  this.rollup.read.getEpochDuration(),
108
130
  this.rollup.read.getSlotDuration(),
109
131
  ]);
110
- return { epochDuration, slotDuration };
132
+ return { epochDuration, slotDuration: Number(slotDuration) };
111
133
  }
112
134
 
113
135
  /**
@@ -116,18 +138,18 @@ export class RollupCheatCodes {
116
138
  * @param opts - Options
117
139
  */
118
140
  public async advanceToEpoch(
119
- epoch: bigint | number,
141
+ epoch: EpochNumber,
120
142
  opts: {
121
143
  /** Offset in seconds */
122
144
  offset?: number;
123
145
  } = {},
124
146
  ) {
125
147
  const { epochDuration: slotsInEpoch } = await this.getConfig();
126
- const timestamp =
127
- (await this.rollup.read.getTimestampForSlot([BigInt(epoch) * slotsInEpoch])) + BigInt(opts.offset ?? 0);
148
+ const slotNumber = SlotNumber(Number(epoch) * Number(slotsInEpoch));
149
+ const timestamp = (await this.rollup.read.getTimestampForSlot([BigInt(slotNumber)])) + BigInt(opts.offset ?? 0);
128
150
  try {
129
151
  await this.ethCheatCodes.warp(Number(timestamp), { ...opts, silent: true, resetBlockInterval: true });
130
- this.logger.warn(`Warped to epoch ${epoch}`);
152
+ this.logger.warn(`Warped to epoch ${epoch}`, { offset: opts.offset, timestamp });
131
153
  } catch (err) {
132
154
  this.logger.warn(`Warp to epoch ${epoch} failed: ${err}`);
133
155
  }
@@ -138,8 +160,8 @@ export class RollupCheatCodes {
138
160
  public async advanceToNextEpoch() {
139
161
  const slot = await this.getSlot();
140
162
  const { epochDuration, slotDuration } = await this.getConfig();
141
- const slotsUntilNextEpoch = epochDuration - (slot % epochDuration) + 1n;
142
- const timeToNextEpoch = slotsUntilNextEpoch * slotDuration;
163
+ const slotsUntilNextEpoch = epochDuration - (BigInt(slot) % epochDuration) + 1n;
164
+ const timeToNextEpoch = slotsUntilNextEpoch * BigInt(slotDuration);
143
165
  const l1Timestamp = BigInt((await this.client.getBlock()).timestamp);
144
166
  await this.ethCheatCodes.warp(Number(l1Timestamp + timeToNextEpoch), {
145
167
  silent: true,
@@ -151,10 +173,26 @@ export class RollupCheatCodes {
151
173
  /** Warps time in L1 until the beginning of the next slot. */
152
174
  public async advanceToNextSlot() {
153
175
  const currentSlot = await this.getSlot();
154
- const timestamp = await this.rollup.read.getTimestampForSlot([currentSlot + 1n]);
176
+ const nextSlot = SlotNumber(currentSlot + 1);
177
+ const timestamp = await this.rollup.read.getTimestampForSlot([BigInt(nextSlot)]);
155
178
  await this.ethCheatCodes.warp(Number(timestamp), { silent: true, resetBlockInterval: true });
156
- this.logger.warn(`Advanced to slot ${currentSlot + 1n}`);
157
- return [timestamp, currentSlot + 1n];
179
+ this.logger.warn(`Advanced to slot ${nextSlot}`);
180
+ return [timestamp, nextSlot];
181
+ }
182
+
183
+ /**
184
+ * Warps L1 time to the start of an explicit absolute slot. Unlike `advanceSlots(N)` which is
185
+ * relative to the current L1 timestamp at call time and therefore races with real-time
186
+ * progression between query and warp, this lands at the slot regardless of latency.
187
+ *
188
+ * Throws if the requested slot is in the past relative to current L1 time (anvil cannot
189
+ * rewind).
190
+ */
191
+ public async advanceToSlot(slot: SlotNumber) {
192
+ const timestamp = await this.rollup.read.getTimestampForSlot([BigInt(slot)]);
193
+ await this.ethCheatCodes.warp(Number(timestamp), { silent: true, resetBlockInterval: true });
194
+ this.logger.warn(`Advanced to slot ${slot}`, { timestamp });
195
+ return timestamp;
158
196
  }
159
197
 
160
198
  /**
@@ -163,42 +201,42 @@ export class RollupCheatCodes {
163
201
  */
164
202
  public async advanceSlots(howMany: number) {
165
203
  const l1Timestamp = (await this.client.getBlock()).timestamp;
166
- const slotDuration = await this.rollup.read.getSlotDuration();
167
- const timeToWarp = BigInt(howMany) * slotDuration;
204
+ const slotDuration = Number(await this.rollup.read.getSlotDuration());
205
+ const timeToWarp = BigInt(howMany) * BigInt(slotDuration);
168
206
  await this.ethCheatCodes.warp(l1Timestamp + timeToWarp, { silent: true, resetBlockInterval: true });
169
207
  const [slot, epoch] = await Promise.all([this.getSlot(), this.getEpoch()]);
170
208
  this.logger.warn(`Advanced ${howMany} slots up to slot ${slot} in epoch ${epoch}`);
171
209
  }
172
210
 
173
211
  /**
174
- * Marks the specified block (or latest if none) as proven
175
- * @param maybeBlockNumber - The block number to mark as proven (defaults to latest pending)
212
+ * Marks the specified checkpoint (or latest if none) as proven
213
+ * @param maybeCheckpointNumber - The checkpoint number to mark as proven (defaults to latest pending)
176
214
  */
177
- public markAsProven(maybeBlockNumber?: number | bigint) {
215
+ public markAsProven(maybeCheckpointNumber?: CheckpointNumber) {
178
216
  return this.ethCheatCodes.execWithPausedAnvil(async () => {
179
217
  const tipsBefore = await this.getTips();
180
218
  const { pending, proven } = tipsBefore;
181
219
 
182
- let blockNumber = maybeBlockNumber;
183
- if (blockNumber === undefined || blockNumber > pending) {
184
- blockNumber = pending;
220
+ let checkpointNumber = maybeCheckpointNumber;
221
+ if (checkpointNumber === undefined || checkpointNumber > pending) {
222
+ checkpointNumber = pending;
185
223
  }
186
- if (blockNumber <= proven) {
187
- this.logger.debug(`Block ${blockNumber} is already proven`);
224
+ if (checkpointNumber <= proven) {
225
+ this.logger.debug(`Checkpoint ${checkpointNumber} is already proven`);
188
226
  return;
189
227
  }
190
228
 
191
229
  // @note @LHerskind this is heavily dependent on the storage layout and size of values
192
230
  // The rollupStore is a struct and if the size of elements or the struct changes, this can break
193
- const provenBlockNumberSlot = hexToBigInt(RollupContract.stfStorageSlot);
231
+ const provenCheckpointNumberSlot = hexToBigInt(RollupContract.stfStorageSlot);
194
232
 
195
233
  // Need to pack it as a single 32 byte word
196
- const newValue = (BigInt(tipsBefore.pending) << 128n) | BigInt(blockNumber);
197
- await this.ethCheatCodes.store(EthAddress.fromString(this.rollup.address), provenBlockNumberSlot, newValue);
234
+ const newValue = (BigInt(tipsBefore.pending) << 128n) | BigInt(checkpointNumber);
235
+ await this.ethCheatCodes.store(EthAddress.fromString(this.rollup.address), provenCheckpointNumberSlot, newValue);
198
236
 
199
237
  const tipsAfter = await this.getTips();
200
238
  if (tipsAfter.pending < tipsAfter.proven) {
201
- throw new Error('Overwrote pending tip to a block in the past');
239
+ throw new Error('Overwrote pending tip to a checkpoint in the past');
202
240
  }
203
241
 
204
242
  this.logger.info(
@@ -207,9 +245,59 @@ export class RollupCheatCodes {
207
245
  });
208
246
  }
209
247
 
248
+ /**
249
+ * Polls the rollup until its current epoch reaches `epoch`. Unlike {@link advanceToEpoch} this does
250
+ * not warp the L1 clock; it only waits for the chain to reach `epoch` through external activity.
251
+ */
252
+ public async waitForEpoch(epoch: EpochNumber, opts: { timeout?: number; interval?: number } = {}): Promise<void> {
253
+ await retryUntil(
254
+ async () => (await this.getEpoch()) >= epoch || undefined,
255
+ `rollup epoch >= ${epoch}`,
256
+ opts.timeout ?? 60,
257
+ opts.interval ?? 1,
258
+ );
259
+ }
260
+
261
+ /**
262
+ * Polls the rollup until its current slot reaches `slot`. Unlike {@link advanceToSlot} this does not
263
+ * warp the L1 clock; it only waits for the chain to reach `slot` through external activity.
264
+ */
265
+ public async waitForSlot(slot: SlotNumber, opts: { timeout?: number; interval?: number } = {}): Promise<void> {
266
+ await retryUntil(
267
+ async () => (await this.getSlot()) >= slot || undefined,
268
+ `rollup slot >= ${slot}`,
269
+ opts.timeout ?? 60,
270
+ opts.interval ?? 1,
271
+ );
272
+ }
273
+
274
+ /**
275
+ * Polls the rollup until its pending checkpoint settles below `checkpoint` on a freshly mined, non-zero
276
+ * checkpoint, and returns that new pending checkpoint number. Reads the L1 rollup contract directly
277
+ * rather than a node, since a rollback lands on L1 first.
278
+ *
279
+ * A prune can momentarily drop the pending checkpoint to 0 before the post-deadline propose mines its
280
+ * replacement, so a caller detecting a rollback wants the new lower checkpoint, not that transient
281
+ * empty state — hence the non-zero guard.
282
+ */
283
+ public async waitForCheckpointBelow(
284
+ checkpoint: CheckpointNumber,
285
+ opts: { timeout?: number; interval?: number } = {},
286
+ ): Promise<CheckpointNumber> {
287
+ return await retryUntil(
288
+ async () => {
289
+ const { pending } = await this.getTips();
290
+ return pending > 0 && pending < checkpoint ? pending : undefined;
291
+ },
292
+ `rollup checkpoint in (0, ${checkpoint})`,
293
+ opts.timeout ?? 60,
294
+ opts.interval ?? 1,
295
+ );
296
+ }
297
+
210
298
  /**
211
299
  * Overrides the inProgress field of the Inbox contract state
212
- * @param howMuch - How many blocks to move it forward
300
+ * @param howMuch - How many checkpoints to move it forward
213
301
  */
214
302
  public advanceInboxInProgress(howMuch: number | bigint): Promise<bigint> {
215
303
  return this.ethCheatCodes.execWithPausedAnvil(async () => {
@@ -248,6 +336,17 @@ export class RollupCheatCodes {
248
336
  });
249
337
  }
250
338
 
339
+ public insertOutbox(epoch: EpochNumber, numCheckpointsInEpoch: number, outHash: bigint) {
340
+ return this.ethCheatCodes.execWithPausedAnvil(async () => {
341
+ const outboxAddress = await this.rollup.read.getOutbox();
342
+ const epochRootSlot = OutboxContract.getEpochRootStorageSlot(epoch, numCheckpointsInEpoch);
343
+ await this.ethCheatCodes.store(EthAddress.fromString(outboxAddress), epochRootSlot, outHash);
344
+ this.logger.warn(
345
+ `Advanced outbox to epoch ${epoch} numCheckpointsInEpoch ${numCheckpointsInEpoch} with out hash ${outHash}`,
346
+ );
347
+ });
348
+ }
349
+
251
350
  /**
252
351
  * Executes an action impersonated as the owner of the Rollup contract.
253
352
  * @param action - The action to execute
@@ -293,7 +392,8 @@ export class RollupCheatCodes {
293
392
  }
294
393
 
295
394
  /**
296
- * Directly updates proving cost per mana.
395
+ * Directly updates proving cost per mana. Throws if the on-chain tx reverts
396
+ * (e.g. rate-limit cooldown, step cap, or floor) instead of silently succeeding.
297
397
  * @param ethValue - The new proving cost per mana in ETH
298
398
  */
299
399
  public async setProvingCostPerMana(ethValue: bigint) {
@@ -303,8 +403,37 @@ export class RollupCheatCodes {
303
403
  chain: this.client.chain,
304
404
  gasLimit: 1000000n,
305
405
  });
306
- await this.client.waitForTransactionReceipt({ hash });
406
+ const receipt = await this.client.waitForTransactionReceipt({ hash });
407
+ if (receipt.status !== 'success') {
408
+ throw new Error(
409
+ `setProvingCostPerMana(${ethValue}) reverted on L1 (tx ${hash}). ` +
410
+ `Likely FeeLib rate-limit (30-day cooldown or 1.5x step cap); ` +
411
+ `use clearProvingCostCooldown() between successive updates.`,
412
+ );
413
+ }
307
414
  this.logger.warn(`Updated proving cost per mana to ${ethValue}`);
308
415
  });
309
416
  }
417
+
418
+ /**
419
+ * Resets the 30-day proving-cost update cooldown enforced by FeeLib.updateProvingCostPerMana
420
+ * by zeroing `FeeStore.provingCostLastUpdate` directly in contract storage. Use between
421
+ * successive setProvingCostPerMana / bumpProvingCostPerMana calls so the later update can
422
+ * land instead of reverting. Does not touch L1 time, so PXE/tx-expiration state stays intact.
423
+ *
424
+ * @note This is tightly coupled to the `FeeStore` layout in
425
+ * l1-contracts/src/core/libraries/rollup/FeeLib.sol:
426
+ * slot + 0: CompressedFeeConfig config (uint256)
427
+ * slot + 1: L1GasOracleValues l1GasOracleValues (14+14+4 bytes, packed)
428
+ * slot + 2: uint64 provingCostLastUpdate (only member — zeroing the slot is safe)
429
+ * If the struct layout changes, update the offset below.
430
+ */
431
+ public async clearProvingCostCooldown() {
432
+ const feeStoreBaseSlot = hexToBigInt(keccak256(Buffer.from('aztec.fee.storage', 'utf-8')));
433
+ const provingCostLastUpdateSlot = feeStoreBaseSlot + 2n;
434
+ await this.ethCheatCodes.store(EthAddress.fromString(this.rollup.address), provingCostLastUpdateSlot, 0n, {
435
+ silent: true,
436
+ });
437
+ this.logger.warn(`Cleared proving-cost update cooldown`);
438
+ }
310
439
  }
@@ -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
  */
@@ -15,53 +24,196 @@ export async function startAnvil(
15
24
  log?: boolean;
16
25
  captureMethodCalls?: boolean;
17
26
  accounts?: number;
27
+ chainId?: number;
28
+ /** The hardfork to use (e.g. 'cancun', 'latest'). */
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;
18
43
  } = {},
19
44
  ): Promise<{ anvil: Anvil; methodCalls?: string[]; rpcUrl: string; stop: () => Promise<void> }> {
20
45
  const anvilBinary = resolve(dirname(fileURLToPath(import.meta.url)), '../../', 'scripts/anvil_kill_wrapper.sh');
21
46
  const logger = opts.log ? createLogger('ethereum:anvil') : undefined;
22
47
  const methodCalls = opts.captureMethodCalls ? ([] as string[]) : undefined;
23
48
 
24
- let port: number | undefined;
49
+ let detectedPort: number | undefined;
25
50
 
26
- // Start anvil.
27
- // We go via a wrapper script to ensure if the parent dies, anvil dies.
28
51
  const anvil = await retry(
29
52
  async () => {
30
- const anvil = createAnvil({
31
- anvilBinary,
32
- host: '127.0.0.1',
33
- port: opts.port ?? 8545,
34
- blockTime: opts.l1BlockTime,
35
- stopTimeout: 1000,
36
- accounts: opts.accounts ?? 20,
37
- gasLimit: 45_000_000n,
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' },
38
77
  });
39
78
 
40
- // Listen to the anvil output to get the port.
41
- const removeHandler = anvil.on('message', (message: string) => {
42
- 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
+ };
43
101
 
44
- methodCalls?.push(...(message.match(/eth_[^\s]+/g) || []));
45
- if (port === undefined && message.includes('Listening on')) {
46
- port = parseInt(message.match(/Listening on ([^:]+):(\d+)/)![2]);
47
- }
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);
48
116
  });
49
- await anvil.start();
50
- if (!logger && !opts.captureMethodCalls) {
51
- 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();
52
135
  }
53
136
 
54
- return anvil;
137
+ return child;
55
138
  },
56
139
  'Start anvil',
57
140
  makeBackoff([5, 5, 5]),
58
141
  );
59
142
 
60
- if (!port) {
143
+ if (!detectedPort) {
61
144
  throw new Error('Failed to start anvil');
62
145
  }
63
146
 
64
- // Monkeypatch the anvil instance to include the actually assigned port
65
- // Object.defineProperty(anvil, 'port', { value: port, writable: false });
66
- 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
+ });
67
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({