@aztec/ethereum 3.0.0-canary.a9708bd → 3.0.0-devnet.2

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 (144) hide show
  1. package/dest/client.d.ts +1 -1
  2. package/dest/client.d.ts.map +1 -1
  3. package/dest/config.d.ts +11 -6
  4. package/dest/config.d.ts.map +1 -1
  5. package/dest/config.js +124 -64
  6. package/dest/contracts/empire_base.d.ts +1 -1
  7. package/dest/contracts/empire_base.d.ts.map +1 -1
  8. package/dest/contracts/empire_slashing_proposer.d.ts +2 -2
  9. package/dest/contracts/empire_slashing_proposer.d.ts.map +1 -1
  10. package/dest/contracts/empire_slashing_proposer.js +1 -1
  11. package/dest/contracts/fee_asset_handler.d.ts +3 -3
  12. package/dest/contracts/fee_asset_handler.d.ts.map +1 -1
  13. package/dest/contracts/governance.js +7 -3
  14. package/dest/contracts/governance_proposer.d.ts +1 -2
  15. package/dest/contracts/governance_proposer.d.ts.map +1 -1
  16. package/dest/contracts/governance_proposer.js +1 -2
  17. package/dest/contracts/multicall.d.ts +3 -5
  18. package/dest/contracts/multicall.d.ts.map +1 -1
  19. package/dest/contracts/multicall.js +6 -4
  20. package/dest/contracts/rollup.d.ts +39 -19
  21. package/dest/contracts/rollup.d.ts.map +1 -1
  22. package/dest/contracts/rollup.js +84 -88
  23. package/dest/contracts/slasher_contract.d.ts +10 -0
  24. package/dest/contracts/slasher_contract.d.ts.map +1 -1
  25. package/dest/contracts/slasher_contract.js +18 -0
  26. package/dest/contracts/tally_slashing_proposer.d.ts +22 -3
  27. package/dest/contracts/tally_slashing_proposer.d.ts.map +1 -1
  28. package/dest/contracts/tally_slashing_proposer.js +55 -5
  29. package/dest/deploy_l1_contracts.d.ts +22 -7
  30. package/dest/deploy_l1_contracts.d.ts.map +1 -1
  31. package/dest/deploy_l1_contracts.js +555 -362
  32. package/dest/index.d.ts +1 -1
  33. package/dest/index.d.ts.map +1 -1
  34. package/dest/index.js +1 -1
  35. package/dest/l1_artifacts.d.ts +8729 -6014
  36. package/dest/l1_artifacts.d.ts.map +1 -1
  37. package/dest/l1_artifacts.js +10 -5
  38. package/dest/l1_contract_addresses.d.ts +5 -1
  39. package/dest/l1_contract_addresses.d.ts.map +1 -1
  40. package/dest/l1_contract_addresses.js +16 -26
  41. package/dest/l1_reader.d.ts +1 -1
  42. package/dest/l1_reader.d.ts.map +1 -1
  43. package/dest/l1_reader.js +8 -8
  44. package/dest/l1_tx_utils/config.d.ts +59 -0
  45. package/dest/l1_tx_utils/config.d.ts.map +1 -0
  46. package/dest/l1_tx_utils/config.js +73 -0
  47. package/dest/l1_tx_utils/constants.d.ts +6 -0
  48. package/dest/l1_tx_utils/constants.d.ts.map +1 -0
  49. package/dest/l1_tx_utils/constants.js +14 -0
  50. package/dest/l1_tx_utils/factory.d.ts +24 -0
  51. package/dest/l1_tx_utils/factory.d.ts.map +1 -0
  52. package/dest/l1_tx_utils/factory.js +12 -0
  53. package/dest/l1_tx_utils/index.d.ts +10 -0
  54. package/dest/l1_tx_utils/index.d.ts.map +1 -0
  55. package/dest/l1_tx_utils/index.js +10 -0
  56. package/dest/l1_tx_utils/interfaces.d.ts +76 -0
  57. package/dest/l1_tx_utils/interfaces.d.ts.map +1 -0
  58. package/dest/l1_tx_utils/interfaces.js +4 -0
  59. package/dest/l1_tx_utils/l1_tx_utils.d.ts +95 -0
  60. package/dest/l1_tx_utils/l1_tx_utils.d.ts.map +1 -0
  61. package/dest/l1_tx_utils/l1_tx_utils.js +610 -0
  62. package/dest/l1_tx_utils/l1_tx_utils_with_blobs.d.ts +26 -0
  63. package/dest/l1_tx_utils/l1_tx_utils_with_blobs.d.ts.map +1 -0
  64. package/dest/l1_tx_utils/l1_tx_utils_with_blobs.js +26 -0
  65. package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts +81 -0
  66. package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts.map +1 -0
  67. package/dest/l1_tx_utils/readonly_l1_tx_utils.js +294 -0
  68. package/dest/l1_tx_utils/signer.d.ts +4 -0
  69. package/dest/l1_tx_utils/signer.d.ts.map +1 -0
  70. package/dest/l1_tx_utils/signer.js +16 -0
  71. package/dest/l1_tx_utils/types.d.ts +67 -0
  72. package/dest/l1_tx_utils/types.d.ts.map +1 -0
  73. package/dest/l1_tx_utils/types.js +26 -0
  74. package/dest/l1_tx_utils/utils.d.ts +4 -0
  75. package/dest/l1_tx_utils/utils.d.ts.map +1 -0
  76. package/dest/l1_tx_utils/utils.js +14 -0
  77. package/dest/publisher_manager.d.ts +7 -2
  78. package/dest/publisher_manager.d.ts.map +1 -1
  79. package/dest/publisher_manager.js +36 -8
  80. package/dest/queries.d.ts.map +1 -1
  81. package/dest/queries.js +11 -12
  82. package/dest/test/chain_monitor.d.ts +11 -0
  83. package/dest/test/chain_monitor.d.ts.map +1 -1
  84. package/dest/test/chain_monitor.js +81 -12
  85. package/dest/test/delayed_tx_utils.d.ts +2 -2
  86. package/dest/test/delayed_tx_utils.d.ts.map +1 -1
  87. package/dest/test/delayed_tx_utils.js +2 -2
  88. package/dest/test/eth_cheat_codes.d.ts +32 -6
  89. package/dest/test/eth_cheat_codes.d.ts.map +1 -1
  90. package/dest/test/eth_cheat_codes.js +115 -28
  91. package/dest/test/rollup_cheat_codes.d.ts +11 -9
  92. package/dest/test/rollup_cheat_codes.d.ts.map +1 -1
  93. package/dest/test/rollup_cheat_codes.js +38 -6
  94. package/dest/test/upgrade_utils.d.ts.map +1 -1
  95. package/dest/test/upgrade_utils.js +3 -2
  96. package/dest/utils.d.ts.map +1 -1
  97. package/dest/utils.js +10 -161
  98. package/dest/zkPassportVerifierAddress.js +1 -1
  99. package/package.json +7 -7
  100. package/src/client.ts +1 -1
  101. package/src/config.ts +136 -68
  102. package/src/contracts/empire_base.ts +1 -1
  103. package/src/contracts/empire_slashing_proposer.ts +7 -3
  104. package/src/contracts/fee_asset_handler.ts +1 -1
  105. package/src/contracts/governance.ts +3 -3
  106. package/src/contracts/governance_proposer.ts +3 -4
  107. package/src/contracts/multicall.ts +12 -10
  108. package/src/contracts/rollup.ts +104 -106
  109. package/src/contracts/slasher_contract.ts +22 -0
  110. package/src/contracts/tally_slashing_proposer.ts +54 -6
  111. package/src/deploy_l1_contracts.ts +570 -328
  112. package/src/index.ts +1 -1
  113. package/src/l1_artifacts.ts +14 -6
  114. package/src/l1_contract_addresses.ts +17 -26
  115. package/src/l1_reader.ts +9 -9
  116. package/src/l1_tx_utils/README.md +177 -0
  117. package/src/l1_tx_utils/config.ts +140 -0
  118. package/src/l1_tx_utils/constants.ts +18 -0
  119. package/src/l1_tx_utils/factory.ts +64 -0
  120. package/src/l1_tx_utils/index.ts +12 -0
  121. package/src/l1_tx_utils/interfaces.ts +86 -0
  122. package/src/l1_tx_utils/l1_tx_utils.ts +718 -0
  123. package/src/l1_tx_utils/l1_tx_utils_with_blobs.ts +77 -0
  124. package/src/l1_tx_utils/readonly_l1_tx_utils.ts +372 -0
  125. package/src/l1_tx_utils/signer.ts +28 -0
  126. package/src/l1_tx_utils/types.ts +85 -0
  127. package/src/l1_tx_utils/utils.ts +16 -0
  128. package/src/publisher_manager.ts +51 -9
  129. package/src/queries.ts +13 -8
  130. package/src/test/chain_monitor.ts +89 -9
  131. package/src/test/delayed_tx_utils.ts +2 -2
  132. package/src/test/eth_cheat_codes.ts +142 -29
  133. package/src/test/rollup_cheat_codes.ts +54 -14
  134. package/src/test/upgrade_utils.ts +3 -2
  135. package/src/utils.ts +13 -185
  136. package/src/zkPassportVerifierAddress.ts +1 -1
  137. package/dest/l1_tx_utils.d.ts +0 -250
  138. package/dest/l1_tx_utils.d.ts.map +0 -1
  139. package/dest/l1_tx_utils.js +0 -826
  140. package/dest/l1_tx_utils_with_blobs.d.ts +0 -19
  141. package/dest/l1_tx_utils_with_blobs.d.ts.map +0 -1
  142. package/dest/l1_tx_utils_with_blobs.js +0 -85
  143. package/src/l1_tx_utils.ts +0 -1105
  144. package/src/l1_tx_utils_with_blobs.ts +0 -144
package/src/queries.ts CHANGED
@@ -1,7 +1,4 @@
1
1
  import { EthAddress } from '@aztec/foundation/eth-address';
2
- import { SlasherAbi } from '@aztec/l1-artifacts/SlasherAbi';
3
-
4
- import { getContract } from 'viem';
5
2
 
6
3
  import type { L1ContractsConfig } from './config.js';
7
4
  import { ReadOnlyGovernanceContract } from './contracts/governance.js';
@@ -27,8 +24,7 @@ export async function getL1ContractsConfig(
27
24
  const rollupAddress = addresses.rollupAddress ?? (await governanceProposer.getRollupAddress());
28
25
  const rollup = new RollupContract(publicClient, rollupAddress.toString());
29
26
  const slasherProposer = await rollup.getSlashingProposer();
30
- const slasherAddress = await rollup.getSlasher();
31
- const slasher = getContract({ address: slasherAddress, abi: SlasherAbi, client: publicClient });
27
+ const slasher = await rollup.getSlasherContract();
32
28
 
33
29
  const [
34
30
  l1StartBlock,
@@ -37,8 +33,10 @@ export async function getL1ContractsConfig(
37
33
  aztecSlotDuration,
38
34
  aztecProofSubmissionEpochs,
39
35
  aztecTargetCommitteeSize,
36
+ lagInEpochs,
40
37
  activationThreshold,
41
38
  ejectionThreshold,
39
+ localEjectionThreshold,
42
40
  governanceProposerQuorum,
43
41
  governanceProposerRoundSize,
44
42
  slashingQuorum,
@@ -48,6 +46,7 @@ export async function getL1ContractsConfig(
48
46
  slashingOffsetInRounds,
49
47
  slashingAmounts,
50
48
  slashingVetoer,
49
+ slashingDisableDuration,
51
50
  manaTarget,
52
51
  provingCostPerMana,
53
52
  rollupVersion,
@@ -60,8 +59,10 @@ export async function getL1ContractsConfig(
60
59
  rollup.getSlotDuration(),
61
60
  rollup.getProofSubmissionEpochs(),
62
61
  rollup.getTargetCommitteeSize(),
62
+ rollup.getLagInEpochs(),
63
63
  rollup.getActivationThreshold(),
64
64
  rollup.getEjectionThreshold(),
65
+ rollup.getLocalEjectionThreshold(),
65
66
  governanceProposer.getQuorumSize(),
66
67
  governanceProposer.getRoundSize(),
67
68
  slasherProposer?.getQuorumSize() ?? 0n,
@@ -70,7 +71,8 @@ export async function getL1ContractsConfig(
70
71
  slasherProposer?.getExecutionDelayInRounds() ?? 0n,
71
72
  slasherProposer?.type === 'tally' ? slasherProposer.getSlashOffsetInRounds() : 0n,
72
73
  slasherProposer?.type === 'tally' ? slasherProposer.getSlashingAmounts() : [0n, 0n, 0n],
73
- slasher.read.VETOER(),
74
+ slasher?.getVetoer() ?? EthAddress.ZERO,
75
+ slasher?.getSlashingDisableDuration() ?? 0,
74
76
  rollup.getManaTarget(),
75
77
  rollup.getProvingCostPerMana(),
76
78
  rollup.getVersion(),
@@ -85,16 +87,19 @@ export async function getL1ContractsConfig(
85
87
  aztecSlotDuration: Number(aztecSlotDuration),
86
88
  aztecProofSubmissionEpochs: Number(aztecProofSubmissionEpochs),
87
89
  aztecTargetCommitteeSize: Number(aztecTargetCommitteeSize),
90
+ lagInEpochs: Number(lagInEpochs),
88
91
  governanceProposerQuorum: Number(governanceProposerQuorum),
89
92
  governanceProposerRoundSize: Number(governanceProposerRoundSize),
90
93
  activationThreshold,
91
94
  ejectionThreshold,
95
+ localEjectionThreshold,
92
96
  slashingQuorum: Number(slashingQuorum),
93
97
  slashingRoundSizeInEpochs: Number(slashingRoundSize / aztecEpochDuration),
94
98
  slashingLifetimeInRounds: Number(slashingLifetimeInRounds),
95
99
  slashingExecutionDelayInRounds: Number(slashingExecutionDelayInRounds),
96
- slashingVetoer: EthAddress.fromString(slashingVetoer),
97
- manaTarget: manaTarget,
100
+ slashingVetoer,
101
+ slashingDisableDuration,
102
+ manaTarget,
98
103
  provingCostPerMana: provingCostPerMana,
99
104
  rollupVersion: Number(rollupVersion),
100
105
  genesisArchiveTreeRoot,
@@ -10,10 +10,11 @@ import type { ViemClient } from '../types.js';
10
10
 
11
11
  export type ChainMonitorEventMap = {
12
12
  'l1-block': [{ l1BlockNumber: number; timestamp: bigint }];
13
- 'l2-block': [{ l2BlockNumber: number; l1BlockNumber: number; timestamp: bigint }];
13
+ 'l2-block': [{ l2BlockNumber: number; l1BlockNumber: number; l2SlotNumber: number; timestamp: bigint }];
14
14
  'l2-block-proven': [{ l2ProvenBlockNumber: number; l1BlockNumber: number; timestamp: bigint }];
15
15
  'l2-messages': [{ totalL2Messages: number; l1BlockNumber: number }];
16
16
  'l2-epoch': [{ l2EpochNumber: number; timestamp: bigint; committee: EthAddress[] | undefined }];
17
+ 'l2-slot': [{ l2SlotNumber: number; timestamp: bigint }];
17
18
  };
18
19
 
19
20
  /** Utility class that polls the chain on quick intervals and logs new L1 blocks, L2 blocks, and L2 proofs. */
@@ -38,6 +39,8 @@ export class ChainMonitor extends EventEmitter<ChainMonitorEventMap> {
38
39
  public totalL2Messages: number = 0;
39
40
  /** Current L2 epoch number */
40
41
  public l2EpochNumber!: bigint;
42
+ /** Current L2 slot number */
43
+ public l2SlotNumber!: bigint;
41
44
 
42
45
  constructor(
43
46
  private readonly rollup: RollupContract,
@@ -99,8 +102,13 @@ export class ChainMonitor extends EventEmitter<ChainMonitorEventMap> {
99
102
  }
100
103
  this.l1BlockNumber = newL1BlockNumber;
101
104
 
102
- const block = await this.l1Client.getBlock({ blockNumber: BigInt(newL1BlockNumber), includeTransactions: false });
103
- const timestamp = block.timestamp;
105
+ const [l2SlotNumber, l2Epoch, l1block] = await Promise.all([
106
+ this.rollup.getSlotNumber(),
107
+ this.rollup.getCurrentEpoch(),
108
+ this.l1Client.getBlock({ blockNumber: BigInt(newL1BlockNumber), includeTransactions: false }),
109
+ ]);
110
+
111
+ const timestamp = l1block.timestamp;
104
112
  const timestampString = new Date(Number(timestamp) * 1000).toTimeString().split(' ')[0];
105
113
 
106
114
  this.emit('l1-block', { l1BlockNumber: newL1BlockNumber, timestamp });
@@ -108,16 +116,21 @@ export class ChainMonitor extends EventEmitter<ChainMonitorEventMap> {
108
116
 
109
117
  const newL2BlockNumber = Number(await this.rollup.getBlockNumber());
110
118
  if (this.l2BlockNumber !== newL2BlockNumber) {
111
- const epochNumber = await this.rollup.getEpochNumber(BigInt(newL2BlockNumber));
119
+ const epochNumber = await this.rollup.getEpochNumberForBlock(BigInt(newL2BlockNumber));
112
120
  msg += ` with new L2 block ${newL2BlockNumber} for epoch ${epochNumber}`;
113
121
  this.l2BlockNumber = newL2BlockNumber;
114
122
  this.l2BlockTimestamp = timestamp;
115
- this.emit('l2-block', { l2BlockNumber: newL2BlockNumber, l1BlockNumber: newL1BlockNumber, timestamp });
123
+ this.emit('l2-block', {
124
+ l2BlockNumber: newL2BlockNumber,
125
+ l1BlockNumber: newL1BlockNumber,
126
+ l2SlotNumber: Number(l2SlotNumber),
127
+ timestamp,
128
+ });
116
129
  }
117
130
 
118
131
  const newL2ProvenBlockNumber = Number(await this.rollup.getProvenBlockNumber());
119
132
  if (this.l2ProvenBlockNumber !== newL2ProvenBlockNumber) {
120
- const epochNumber = await this.rollup.getEpochNumber(BigInt(newL2ProvenBlockNumber));
133
+ const epochNumber = await this.rollup.getEpochNumberForBlock(BigInt(newL2ProvenBlockNumber));
121
134
  msg += ` with proof up to L2 block ${newL2ProvenBlockNumber} for epoch ${epochNumber}`;
122
135
  this.l2ProvenBlockNumber = newL2ProvenBlockNumber;
123
136
  this.l2ProvenBlockTimestamp = timestamp;
@@ -136,14 +149,17 @@ export class ChainMonitor extends EventEmitter<ChainMonitorEventMap> {
136
149
  this.emit('l2-messages', { totalL2Messages: newTotalL2Messages, l1BlockNumber: newL1BlockNumber });
137
150
  }
138
151
 
139
- const [l2SlotNumber, l2Epoch] = await Promise.all([this.rollup.getSlotNumber(), this.rollup.getCurrentEpoch()]);
140
-
141
152
  let committee: EthAddress[] | undefined;
142
153
  if (l2Epoch !== this.l2EpochNumber) {
143
154
  this.l2EpochNumber = l2Epoch;
144
155
  committee = (await this.rollup.getCurrentEpochCommittee())?.map(addr => EthAddress.fromString(addr));
145
156
  this.emit('l2-epoch', { l2EpochNumber: Number(l2Epoch), timestamp, committee });
146
- msg += ` starting new epoch ${this.l2EpochNumber} with committee ${committee?.join(', ') ?? 'undefined'}`;
157
+ msg += ` starting new epoch ${this.l2EpochNumber} `;
158
+ }
159
+
160
+ if (l2SlotNumber !== this.l2SlotNumber) {
161
+ this.l2SlotNumber = l2SlotNumber;
162
+ this.emit('l2-slot', { l2SlotNumber: Number(l2SlotNumber), timestamp });
147
163
  }
148
164
 
149
165
  this.logger.info(msg, {
@@ -160,4 +176,68 @@ export class ChainMonitor extends EventEmitter<ChainMonitorEventMap> {
160
176
 
161
177
  return this;
162
178
  }
179
+
180
+ public waitUntilL2Slot(slot: number | bigint): Promise<void> {
181
+ const targetSlot = typeof slot === 'bigint' ? slot.valueOf() : slot;
182
+ if (this.l2SlotNumber >= targetSlot) {
183
+ return Promise.resolve();
184
+ }
185
+ return new Promise(resolve => {
186
+ const listener = (data: { l2SlotNumber: number; timestamp: bigint }) => {
187
+ if (data.l2SlotNumber >= targetSlot) {
188
+ this.off('l2-slot', listener);
189
+ resolve();
190
+ }
191
+ };
192
+ this.on('l2-slot', listener);
193
+ });
194
+ }
195
+
196
+ public waitUntilL1Block(block: number | bigint): Promise<void> {
197
+ const targetBlock = typeof block === 'bigint' ? block.valueOf() : block;
198
+ if (this.l1BlockNumber >= targetBlock) {
199
+ return Promise.resolve();
200
+ }
201
+ return new Promise(resolve => {
202
+ const listener = (data: { l1BlockNumber: number; timestamp: bigint }) => {
203
+ if (data.l1BlockNumber >= targetBlock) {
204
+ this.off('l1-block', listener);
205
+ resolve();
206
+ }
207
+ };
208
+ this.on('l1-block', listener);
209
+ });
210
+ }
211
+
212
+ public waitUntilL1Timestamp(timestamp: number | bigint): Promise<void> {
213
+ const targetTimestamp = typeof timestamp === 'bigint' ? timestamp.valueOf() : timestamp;
214
+ if (this.l1BlockNumber >= targetTimestamp) {
215
+ return Promise.resolve();
216
+ }
217
+ return new Promise(resolve => {
218
+ const listener = (data: { l1BlockNumber: number; timestamp: bigint }) => {
219
+ if (data.timestamp >= targetTimestamp) {
220
+ this.off('l1-block', listener);
221
+ resolve();
222
+ }
223
+ };
224
+ this.on('l1-block', listener);
225
+ });
226
+ }
227
+
228
+ public waitUntilL2Block(l2BlockNumber: number | bigint): Promise<void> {
229
+ const targetBlock = typeof l2BlockNumber === 'bigint' ? l2BlockNumber.valueOf() : l2BlockNumber;
230
+ if (this.l2BlockNumber >= targetBlock) {
231
+ return Promise.resolve();
232
+ }
233
+ return new Promise(resolve => {
234
+ const listener = (data: { l2BlockNumber: number; timestamp: bigint }) => {
235
+ if (data.l2BlockNumber >= targetBlock) {
236
+ this.off('l2-block', listener);
237
+ resolve();
238
+ }
239
+ };
240
+ this.on('l2-block', listener);
241
+ });
242
+ }
163
243
  }
@@ -2,8 +2,8 @@ import { EthAddress } from '@aztec/foundation/eth-address';
2
2
  import { type Logger, createLogger } from '@aztec/foundation/log';
3
3
  import { DateProvider } from '@aztec/foundation/timer';
4
4
 
5
- import { type L1TxUtilsConfig, createViemSigner } from '../l1_tx_utils.js';
6
- import { L1TxUtilsWithBlobs } from '../l1_tx_utils_with_blobs.js';
5
+ import { type L1TxUtilsConfig, createViemSigner } from '../l1_tx_utils/index.js';
6
+ import { L1TxUtilsWithBlobs } from '../l1_tx_utils/l1_tx_utils_with_blobs.js';
7
7
  import type { ExtendedViemWalletClient } from '../types.js';
8
8
  import { type Delayer, withDelayer } from './tx_delayer.js';
9
9
 
@@ -3,9 +3,10 @@ import { keccak256 } from '@aztec/foundation/crypto';
3
3
  import { EthAddress } from '@aztec/foundation/eth-address';
4
4
  import { jsonStringify } from '@aztec/foundation/json-rpc';
5
5
  import { createLogger } from '@aztec/foundation/log';
6
- import type { TestDateProvider } from '@aztec/foundation/timer';
6
+ import { pluralize } from '@aztec/foundation/string';
7
+ import type { DateProvider, TestDateProvider } from '@aztec/foundation/timer';
7
8
 
8
- import { type Hex, createPublicClient, fallback, http } from 'viem';
9
+ import { type Hex, type Transaction, createPublicClient, fallback, hexToNumber, http } from 'viem';
9
10
 
10
11
  import type { ViemPublicClient } from '../types.js';
11
12
 
@@ -19,6 +20,10 @@ export class EthCheatCodes {
19
20
  * The RPC URL to use for interacting with the chain
20
21
  */
21
22
  public rpcUrls: string[],
23
+ /**
24
+ * The date provider to use for time operations
25
+ */
26
+ public dateProvider: DateProvider | TestDateProvider,
22
27
  /**
23
28
  * The logger to use for the eth cheatcodes
24
29
  */
@@ -29,9 +34,12 @@ export class EthCheatCodes {
29
34
  });
30
35
  }
31
36
 
32
- async rpcCall(method: string, params: any[]) {
33
- const paramsString = jsonStringify(params);
34
- this.logger.debug(`Calling ${method} with params: ${paramsString} on ${this.rpcUrls.join(', ')}`);
37
+ public rpcCall(method: string, params: any[]) {
38
+ this.logger.debug(`Calling ${method} with params: ${jsonStringify(params)} on ${this.rpcUrls.join(', ')}`);
39
+ return this.doRpcCall(method, params);
40
+ }
41
+
42
+ private async doRpcCall(method: string, params: any[]) {
35
43
  return (await this.publicClient.transport.request({
36
44
  method,
37
45
  params,
@@ -44,7 +52,7 @@ export class EthCheatCodes {
44
52
  */
45
53
  public async isAutoMining(): Promise<boolean> {
46
54
  try {
47
- const res = await this.rpcCall('anvil_getAutomine', []);
55
+ const res = await this.doRpcCall('anvil_getAutomine', []);
48
56
  return res;
49
57
  } catch (err) {
50
58
  this.logger.error(`Calling "anvil_getAutomine" failed with:`, err);
@@ -57,7 +65,7 @@ export class EthCheatCodes {
57
65
  * @returns The current block number
58
66
  */
59
67
  public async blockNumber(): Promise<number> {
60
- const res = await this.rpcCall('eth_blockNumber', []);
68
+ const res = await this.doRpcCall('eth_blockNumber', []);
61
69
  return parseInt(res, 16);
62
70
  }
63
71
 
@@ -66,7 +74,7 @@ export class EthCheatCodes {
66
74
  * @returns The current chainId
67
75
  */
68
76
  public async chainId(): Promise<number> {
69
- const res = await this.rpcCall('eth_chainId', []);
77
+ const res = await this.doRpcCall('eth_chainId', []);
70
78
  return parseInt(res, 16);
71
79
  }
72
80
 
@@ -75,7 +83,7 @@ export class EthCheatCodes {
75
83
  * @returns The current timestamp
76
84
  */
77
85
  public async timestamp(): Promise<number> {
78
- const res = await this.rpcCall('eth_getBlockByNumber', ['latest', true]);
86
+ const res = await this.doRpcCall('eth_getBlockByNumber', ['latest', true]);
79
87
  return parseInt(res.timestamp, 16);
80
88
  }
81
89
 
@@ -90,7 +98,7 @@ export class EthCheatCodes {
90
98
 
91
99
  private async doMine(numberOfBlocks = 1): Promise<void> {
92
100
  try {
93
- await this.rpcCall('hardhat_mine', [numberOfBlocks]);
101
+ await this.doRpcCall('hardhat_mine', [numberOfBlocks]);
94
102
  } catch (err) {
95
103
  throw new Error(`Error mining: ${err}`);
96
104
  }
@@ -101,7 +109,8 @@ export class EthCheatCodes {
101
109
  */
102
110
  public async evmMine(): Promise<void> {
103
111
  try {
104
- await this.rpcCall('evm_mine', []);
112
+ await this.doRpcCall('evm_mine', []);
113
+ this.logger.warn(`Mined 1 L1 block with evm_mine`);
105
114
  } catch (err) {
106
115
  throw new Error(`Error mining: ${err}`);
107
116
  }
@@ -122,7 +131,7 @@ export class EthCheatCodes {
122
131
  }
123
132
 
124
133
  public async getBalance(account: EthAddress | Hex): Promise<bigint> {
125
- const res = await this.rpcCall('eth_getBalance', [account.toString(), 'latest']);
134
+ const res = await this.doRpcCall('eth_getBalance', [account.toString(), 'latest']);
126
135
  return BigInt(res);
127
136
  }
128
137
 
@@ -158,7 +167,7 @@ export class EthCheatCodes {
158
167
  */
159
168
  public getIntervalMining(): Promise<number | null> {
160
169
  try {
161
- return this.rpcCall('anvil_getIntervalMining', []);
170
+ return this.doRpcCall('anvil_getIntervalMining', []);
162
171
  } catch (err) {
163
172
  throw new Error(`Error getting interval mining: ${err}`);
164
173
  }
@@ -225,12 +234,12 @@ export class EthCheatCodes {
225
234
  /**
226
235
  * Set the next block timestamp and mines the block.
227
236
  * Optionally resets interval mining so the next block is mined in `blockInterval` seconds from now.
228
- * Optionally updates a provided date provider to follow L1 time.
237
+ * Always updates the injected date provider to follow L1 time.
229
238
  * @param timestamp - The timestamp to set the next block to
230
239
  */
231
240
  public async warp(
232
241
  timestamp: number | bigint,
233
- opts: { silent?: boolean; resetBlockInterval?: boolean; updateDateProvider?: TestDateProvider } = {},
242
+ opts: { silent?: boolean; resetBlockInterval?: boolean } = {},
234
243
  ): Promise<void> {
235
244
  let blockInterval: number | null = null;
236
245
  try {
@@ -245,8 +254,10 @@ export class EthCheatCodes {
245
254
  await this.rpcCall('evm_setNextBlockTimestamp', [Number(timestamp)]);
246
255
  // And mine a block so the timestamp goes into effect now
247
256
  await this.doMine();
248
- // Update the date provider if provided so it follows L1 time
249
- opts.updateDateProvider?.setTime(Number(timestamp) * 1000);
257
+ // Update the injected date provider so it follows L1 time
258
+ if ('setTime' in this.dateProvider) {
259
+ this.dateProvider.setTime(Number(timestamp) * 1000);
260
+ }
250
261
  } catch (err) {
251
262
  throw new Error(`Error warping: ${err}`);
252
263
  } finally {
@@ -277,14 +288,21 @@ export class EthCheatCodes {
277
288
  * @param slot - The storage slot
278
289
  * @param value - The value to set the storage slot to
279
290
  */
280
- public async store(contract: EthAddress, slot: bigint, value: bigint): Promise<void> {
291
+ public async store(
292
+ contract: EthAddress,
293
+ slot: bigint,
294
+ value: bigint,
295
+ opts: { silent?: boolean } = {},
296
+ ): Promise<void> {
281
297
  // for the rpc call, we need to change value to be a 32 byte hex string.
282
298
  try {
283
299
  await this.rpcCall('hardhat_setStorageAt', [contract.toString(), toHex(slot), toHex(value, true)]);
284
300
  } catch (err) {
285
301
  throw new Error(`Error setting storage for contract ${contract} at ${slot}: ${err}`);
286
302
  }
287
- this.logger.warn(`Set L1 storage for contract ${contract} at ${slot} to ${value}`);
303
+ if (!opts.silent) {
304
+ this.logger.warn(`Set L1 storage for contract ${contract} at ${slot} to ${value}`);
305
+ }
288
306
  }
289
307
 
290
308
  /**
@@ -350,8 +368,7 @@ export class EthCheatCodes {
350
368
  * @returns The bytecode for the contract
351
369
  */
352
370
  public async getBytecode(contract: EthAddress): Promise<`0x${string}`> {
353
- const res = await this.rpcCall('eth_getCode', [contract.toString(), 'latest']);
354
- return res;
371
+ return await this.doRpcCall('eth_getCode', [contract.toString(), 'latest']);
355
372
  }
356
373
 
357
374
  /**
@@ -360,8 +377,7 @@ export class EthCheatCodes {
360
377
  * @returns The raw transaction
361
378
  */
362
379
  public async getRawTransaction(txHash: Hex): Promise<`0x${string}`> {
363
- const res = await this.rpcCall('debug_getRawTransaction', [txHash]);
364
- return res;
380
+ return await this.doRpcCall('debug_getRawTransaction', [txHash]);
365
381
  }
366
382
 
367
383
  /**
@@ -370,8 +386,7 @@ export class EthCheatCodes {
370
386
  * @returns The trace
371
387
  */
372
388
  public async debugTraceTransaction(txHash: Hex): Promise<any> {
373
- const res = await this.rpcCall('debug_traceTransaction', [txHash]);
374
- return res;
389
+ return await this.doRpcCall('debug_traceTransaction', [txHash]);
375
390
  }
376
391
 
377
392
  /**
@@ -389,7 +404,6 @@ export class EthCheatCodes {
389
404
  * @param blockNumber - The block number that's going to be the new tip
390
405
  */
391
406
  public reorgTo(blockNumber: number): Promise<void> {
392
- this.logger.info('reorgTo', { blockNumber });
393
407
  if (blockNumber <= 0) {
394
408
  throw new Error(`Can't reorg to block before genesis: ${blockNumber}`);
395
409
  }
@@ -405,6 +419,7 @@ export class EthCheatCodes {
405
419
 
406
420
  const depth = Number(currentTip - BigInt(blockNumber) + 1n);
407
421
  await this.rpcCall('anvil_rollback', [depth]);
422
+ this.logger.warn(`Reorged L1 chain to block number ${blockNumber} (depth ${depth})`);
408
423
  });
409
424
  }
410
425
 
@@ -431,10 +446,73 @@ export class EthCheatCodes {
431
446
  }
432
447
 
433
448
  public traceTransaction(txHash: Hex): Promise<any> {
434
- return this.rpcCall('trace_transaction', [txHash]);
449
+ return this.doRpcCall('trace_transaction', [txHash]);
450
+ }
451
+
452
+ public async getTxPoolStatus(): Promise<{ pending: number; queued: number }> {
453
+ const { pending, queued } = await this.doRpcCall('txpool_status', []);
454
+ return { pending: hexToNumber(pending), queued: hexToNumber(queued) };
455
+ }
456
+
457
+ public async getTxPoolContents(): Promise<TxPoolTransaction[]> {
458
+ const txpoolContent = await this.doRpcCall('txpool_content', []);
459
+ return mapTxPoolContent(txpoolContent);
460
+ }
461
+
462
+ /**
463
+ * Mines an empty block by temporarily removing all pending transactions from the mempool,
464
+ * mining a block, and then re-adding the transactions back to the pool.
465
+ */
466
+ public async mineEmptyBlock(blockCount: number = 1): Promise<void> {
467
+ await this.execWithPausedAnvil(async () => {
468
+ // Get all pending and queued transactions from the pool
469
+ const txs = await this.getTxPoolContents();
470
+
471
+ this.logger.debug(`Found ${txs.length} transactions in pool`);
472
+
473
+ // Get raw transactions before dropping them
474
+ const rawTxs: Hex[] = [];
475
+ for (const tx of txs) {
476
+ try {
477
+ const rawTx = await this.doRpcCall('debug_getRawTransaction', [tx.hash]);
478
+ if (rawTx) {
479
+ rawTxs.push(rawTx);
480
+ this.logger.debug(`Got raw tx for ${tx.hash}`);
481
+ } else {
482
+ this.logger.warn(`No raw tx found for ${tx.hash}`);
483
+ }
484
+ } catch {
485
+ this.logger.warn(`Failed to get raw transaction for ${tx.hash}`);
486
+ }
487
+ }
488
+
489
+ this.logger.debug(`Retrieved ${rawTxs.length} raw transactions`);
490
+
491
+ // Drop all transactions from the mempool
492
+ await this.doRpcCall('anvil_dropAllTransactions', []);
493
+
494
+ // Mine an empty block
495
+ await this.doMine(blockCount);
496
+
497
+ // Re-add the transactions to the pool
498
+ for (const rawTx of rawTxs) {
499
+ try {
500
+ const txHash = await this.doRpcCall('eth_sendRawTransaction', [rawTx]);
501
+ this.logger.debug(`Re-added transaction ${txHash}`);
502
+ } catch (err) {
503
+ this.logger.warn(`Failed to re-add transaction: ${err}`);
504
+ }
505
+ }
506
+
507
+ if (rawTxs.length !== txs.length) {
508
+ this.logger.warn(`Failed to add all txs back: had ${txs.length} but re-added ${rawTxs.length}`);
509
+ }
510
+ });
511
+
512
+ this.logger.warn(`Mined ${blockCount} empty L1 ${pluralize('block', blockCount)}`);
435
513
  }
436
514
 
437
- public async execWithPausedAnvil(fn: () => Promise<void>): Promise<void> {
515
+ public async execWithPausedAnvil<T>(fn: () => Promise<T>): Promise<T> {
438
516
  const [blockInterval, wasAutoMining] = await Promise.all([this.getIntervalMining(), this.isAutoMining()]);
439
517
  try {
440
518
  if (blockInterval !== null) {
@@ -445,7 +523,7 @@ export class EthCheatCodes {
445
523
  await this.setAutomine(false, { silent: true });
446
524
  }
447
525
 
448
- await fn();
526
+ return await fn();
449
527
  } finally {
450
528
  try {
451
529
  // restore automine if necessary
@@ -466,4 +544,39 @@ export class EthCheatCodes {
466
544
  }
467
545
  }
468
546
  }
547
+
548
+ public async syncDateProvider() {
549
+ const timestamp = await this.timestamp();
550
+ if ('setTime' in this.dateProvider) {
551
+ this.dateProvider.setTime(timestamp * 1000);
552
+ }
553
+ }
554
+ }
555
+
556
+ type TxPoolState = 'pending' | 'queued';
557
+
558
+ interface TxPoolContent {
559
+ pending: Record<Hex, Record<string, Transaction>>;
560
+ queued: Record<Hex, Record<string, Transaction>>;
561
+ }
562
+
563
+ export type TxPoolTransaction = Transaction & {
564
+ poolState: TxPoolState;
565
+ };
566
+
567
+ function mapTxPoolContent(content: TxPoolContent): TxPoolTransaction[] {
568
+ const result: TxPoolTransaction[] = [];
569
+
570
+ const processPool = (pool: Record<Hex, Record<string, Transaction>>, poolState: TxPoolState) => {
571
+ for (const txsByNonce of Object.values(pool)) {
572
+ for (const tx of Object.values(txsByNonce)) {
573
+ result.push({ ...tx, poolState });
574
+ }
575
+ }
576
+ };
577
+
578
+ processPool(content.pending, 'pending');
579
+ processPool(content.queued, 'queued');
580
+
581
+ return result;
469
582
  }
@@ -2,7 +2,7 @@ import { RollupContract, type ViemPublicClient } from '@aztec/ethereum';
2
2
  import type { L1ContractAddresses } from '@aztec/ethereum/l1-contract-addresses';
3
3
  import { EthAddress } from '@aztec/foundation/eth-address';
4
4
  import { createLogger } from '@aztec/foundation/log';
5
- import type { TestDateProvider } from '@aztec/foundation/timer';
5
+ import type { DateProvider } from '@aztec/foundation/timer';
6
6
  import { RollupAbi } from '@aztec/l1-artifacts/RollupAbi';
7
7
 
8
8
  import {
@@ -40,8 +40,12 @@ export class RollupCheatCodes {
40
40
  });
41
41
  }
42
42
 
43
- static create(rpcUrls: string[], addresses: Pick<L1ContractAddresses, 'rollupAddress'>): RollupCheatCodes {
44
- const ethCheatCodes = new EthCheatCodes(rpcUrls);
43
+ static create(
44
+ rpcUrls: string[],
45
+ addresses: Pick<L1ContractAddresses, 'rollupAddress'>,
46
+ dateProvider: DateProvider,
47
+ ): RollupCheatCodes {
48
+ const ethCheatCodes = new EthCheatCodes(rpcUrls, dateProvider);
45
49
  return new RollupCheatCodes(ethCheatCodes, addresses);
46
50
  }
47
51
 
@@ -112,14 +116,15 @@ export class RollupCheatCodes {
112
116
  * @param opts - Options
113
117
  */
114
118
  public async advanceToEpoch(
115
- epoch: bigint,
119
+ epoch: bigint | number,
116
120
  opts: {
117
- /** Optional test date provider to update with the epoch timestamp */
118
- updateDateProvider?: TestDateProvider;
121
+ /** Offset in seconds */
122
+ offset?: number;
119
123
  } = {},
120
124
  ) {
121
125
  const { epochDuration: slotsInEpoch } = await this.getConfig();
122
- const timestamp = await this.rollup.read.getTimestampForSlot([epoch * slotsInEpoch]);
126
+ const timestamp =
127
+ (await this.rollup.read.getTimestampForSlot([BigInt(epoch) * slotsInEpoch])) + BigInt(opts.offset ?? 0);
123
128
  try {
124
129
  await this.ethCheatCodes.warp(Number(timestamp), { ...opts, silent: true, resetBlockInterval: true });
125
130
  this.logger.warn(`Warped to epoch ${epoch}`);
@@ -130,19 +135,13 @@ export class RollupCheatCodes {
130
135
  }
131
136
 
132
137
  /** Warps time in L1 until the next epoch */
133
- public async advanceToNextEpoch(
134
- opts: {
135
- /** Optional test date provider to update with the epoch timestamp */
136
- updateDateProvider?: TestDateProvider;
137
- } = {},
138
- ) {
138
+ public async advanceToNextEpoch() {
139
139
  const slot = await this.getSlot();
140
140
  const { epochDuration, slotDuration } = await this.getConfig();
141
141
  const slotsUntilNextEpoch = epochDuration - (slot % epochDuration) + 1n;
142
142
  const timeToNextEpoch = slotsUntilNextEpoch * slotDuration;
143
143
  const l1Timestamp = BigInt((await this.client.getBlock()).timestamp);
144
144
  await this.ethCheatCodes.warp(Number(l1Timestamp + timeToNextEpoch), {
145
- ...opts,
146
145
  silent: true,
147
146
  resetBlockInterval: true,
148
147
  });
@@ -208,6 +207,47 @@ export class RollupCheatCodes {
208
207
  });
209
208
  }
210
209
 
210
+ /**
211
+ * Overrides the inProgress field of the Inbox contract state
212
+ * @param howMuch - How many blocks to move it forward
213
+ */
214
+ public advanceInboxInProgress(howMuch: number | bigint): Promise<bigint> {
215
+ return this.ethCheatCodes.execWithPausedAnvil(async () => {
216
+ // Storage slot 2 contains the InboxState struct
217
+ const inboxStateSlot = 2n;
218
+
219
+ // Get inbox and its current state values
220
+ const inboxAddress = await this.rollup.read.getInbox();
221
+ const currentStateValue = await this.ethCheatCodes.load(EthAddress.fromString(inboxAddress), inboxStateSlot);
222
+
223
+ // Extract current values from the packed storage slot
224
+ // Storage layout: rollingHash (128 bits) | totalMessagesInserted (64 bits) | inProgress (64 bits)
225
+ const currentRollingHash = currentStateValue & ((1n << 128n) - 1n);
226
+ const currentTotalMessages = (currentStateValue >> 128n) & ((1n << 64n) - 1n);
227
+ const currentInProgress = currentStateValue >> 192n;
228
+ const newInProgress = currentInProgress + BigInt(howMuch);
229
+
230
+ // Pack new values: rollingHash (low 128 bits) | totalMessages (middle 64 bits) | inProgress (high 64 bits)
231
+ const newValue = (BigInt(newInProgress) << 192n) | (currentTotalMessages << 128n) | currentRollingHash;
232
+
233
+ await this.ethCheatCodes.store(EthAddress.fromString(inboxAddress), inboxStateSlot, newValue, {
234
+ silent: true,
235
+ });
236
+
237
+ this.logger.warn(`Inbox inProgress advanced from ${currentInProgress} to ${newInProgress}`, {
238
+ inbox: inboxAddress,
239
+ oldValue: '0x' + currentStateValue.toString(16),
240
+ newValue: '0x' + newValue.toString(16),
241
+ rollingHash: currentRollingHash,
242
+ totalMessages: currentTotalMessages,
243
+ oldInProgress: currentInProgress,
244
+ newInProgress,
245
+ });
246
+
247
+ return newInProgress;
248
+ });
249
+ }
250
+
211
251
  /**
212
252
  * Executes an action impersonated as the owner of the Rollup contract.
213
253
  * @param action - The action to execute