@aztec/ethereum 0.86.0 → 0.87.0-nightly.20250521

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 (70) hide show
  1. package/dest/client.d.ts +1 -1
  2. package/dest/client.d.ts.map +1 -1
  3. package/dest/client.js +1 -1
  4. package/dest/contracts/errors.d.ts +4 -0
  5. package/dest/contracts/errors.d.ts.map +1 -0
  6. package/dest/contracts/errors.js +6 -0
  7. package/dest/contracts/fee_asset_handler.d.ts.map +1 -1
  8. package/dest/contracts/fee_juice.d.ts.map +1 -1
  9. package/dest/contracts/forwarder.d.ts.map +1 -1
  10. package/dest/contracts/governance.d.ts.map +1 -1
  11. package/dest/contracts/governance.js +1 -0
  12. package/dest/contracts/governance_proposer.d.ts.map +1 -1
  13. package/dest/contracts/inbox.d.ts +26 -0
  14. package/dest/contracts/inbox.d.ts.map +1 -0
  15. package/dest/contracts/inbox.js +45 -0
  16. package/dest/contracts/index.d.ts +2 -0
  17. package/dest/contracts/index.d.ts.map +1 -1
  18. package/dest/contracts/index.js +2 -0
  19. package/dest/contracts/registry.d.ts.map +1 -1
  20. package/dest/contracts/registry.js +2 -2
  21. package/dest/contracts/rollup.d.ts +6 -2
  22. package/dest/contracts/rollup.d.ts.map +1 -1
  23. package/dest/contracts/rollup.js +13 -2
  24. package/dest/contracts/slashing_proposer.d.ts.map +1 -1
  25. package/dest/contracts/utils.d.ts +3 -0
  26. package/dest/contracts/utils.d.ts.map +1 -0
  27. package/dest/contracts/utils.js +11 -0
  28. package/dest/deploy_l1_contracts.d.ts +1275 -634
  29. package/dest/deploy_l1_contracts.d.ts.map +1 -1
  30. package/dest/deploy_l1_contracts.js +22 -25
  31. package/dest/eth_cheat_codes.d.ts.map +1 -1
  32. package/dest/eth_cheat_codes.js +8 -1
  33. package/dest/index.d.ts +1 -0
  34. package/dest/index.d.ts.map +1 -1
  35. package/dest/index.js +1 -0
  36. package/dest/l1_tx_utils.d.ts +12 -11
  37. package/dest/l1_tx_utils.d.ts.map +1 -1
  38. package/dest/l1_tx_utils.js +1 -1
  39. package/dest/l1_types.d.ts +6 -0
  40. package/dest/l1_types.d.ts.map +1 -0
  41. package/dest/l1_types.js +1 -0
  42. package/dest/test/chain_monitor.d.ts +6 -3
  43. package/dest/test/chain_monitor.d.ts.map +1 -1
  44. package/dest/test/chain_monitor.js +27 -5
  45. package/dest/test/tx_delayer.d.ts +6 -2
  46. package/dest/test/tx_delayer.d.ts.map +1 -1
  47. package/dest/test/tx_delayer.js +41 -9
  48. package/dest/utils.js +2 -2
  49. package/package.json +7 -7
  50. package/src/client.ts +2 -2
  51. package/src/contracts/errors.ts +6 -0
  52. package/src/contracts/fee_asset_handler.ts +4 -1
  53. package/src/contracts/fee_juice.ts +4 -1
  54. package/src/contracts/forwarder.ts +5 -1
  55. package/src/contracts/governance.ts +9 -2
  56. package/src/contracts/governance_proposer.ts +4 -1
  57. package/src/contracts/inbox.ts +63 -0
  58. package/src/contracts/index.ts +2 -0
  59. package/src/contracts/registry.ts +6 -3
  60. package/src/contracts/rollup.ts +17 -3
  61. package/src/contracts/slashing_proposer.ts +4 -1
  62. package/src/contracts/utils.ts +14 -0
  63. package/src/deploy_l1_contracts.ts +23 -19
  64. package/src/eth_cheat_codes.ts +8 -1
  65. package/src/index.ts +1 -0
  66. package/src/l1_tx_utils.ts +2 -1
  67. package/src/l1_types.ts +6 -0
  68. package/src/test/chain_monitor.ts +26 -4
  69. package/src/test/tx_delayer.ts +55 -13
  70. package/src/utils.ts +2 -2
package/dest/utils.js CHANGED
@@ -101,7 +101,7 @@ function getNestedErrorData(error) {
101
101
  return new FormattedViemError(`${errorName}${args}`, error?.metaMessages);
102
102
  }
103
103
  }
104
- } catch (decodeErr) {
104
+ } catch {
105
105
  // If decoding fails, we fall back to the original formatting
106
106
  }
107
107
  // Strip ABI from the error object before formatting
@@ -309,7 +309,7 @@ export function tryGetCustomErrorName(err) {
309
309
  return revertError.data?.errorName;
310
310
  }
311
311
  }
312
- } catch (_e) {
312
+ } catch {
313
313
  return undefined;
314
314
  }
315
315
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aztec/ethereum",
3
- "version": "0.86.0",
3
+ "version": "0.87.0-nightly.20250521",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./dest/index.js",
@@ -32,9 +32,9 @@
32
32
  "../package.common.json"
33
33
  ],
34
34
  "dependencies": {
35
- "@aztec/blob-lib": "0.86.0",
36
- "@aztec/foundation": "0.86.0",
37
- "@aztec/l1-artifacts": "0.86.0",
35
+ "@aztec/blob-lib": "0.87.0-nightly.20250521",
36
+ "@aztec/foundation": "0.87.0-nightly.20250521",
37
+ "@aztec/l1-artifacts": "0.87.0-nightly.20250521",
38
38
  "@viem/anvil": "^0.0.10",
39
39
  "dotenv": "^16.0.3",
40
40
  "tslib": "^2.4.0",
@@ -44,13 +44,13 @@
44
44
  "devDependencies": {
45
45
  "@jest/globals": "^29.5.0",
46
46
  "@types/jest": "^29.5.0",
47
- "@types/node": "^18.14.6",
47
+ "@types/node": "^22.15.17",
48
48
  "@viem/anvil": "^0.0.10",
49
49
  "get-port": "^7.1.0",
50
50
  "jest": "^29.5.0",
51
51
  "lodash.omit": "^4.5.0",
52
52
  "ts-node": "^10.9.1",
53
- "typescript": "^5.0.4"
53
+ "typescript": "^5.3.3"
54
54
  },
55
55
  "files": [
56
56
  "dest",
@@ -92,6 +92,6 @@
92
92
  ]
93
93
  },
94
94
  "engines": {
95
- "node": ">=18"
95
+ "node": ">=20.10"
96
96
  }
97
97
  }
package/src/client.ts CHANGED
@@ -54,7 +54,7 @@ async function waitForRpc(client: ViemPublicClient, config: Config, logger?: Log
54
54
  let chainId = 0;
55
55
  try {
56
56
  chainId = await client.getChainId();
57
- } catch (err) {
57
+ } catch {
58
58
  logger?.warn(`Failed to connect to Ethereum node at ${config.l1RpcUrls.join(', ')}. Retrying...`);
59
59
  }
60
60
  return chainId;
@@ -73,7 +73,7 @@ async function waitForRpc(client: ViemPublicClient, config: Config, logger?: Log
73
73
 
74
74
  export function createExtendedL1Client(
75
75
  rpcUrls: string[],
76
- mnemonicOrPrivateKeyOrHdAccount: string | `0x${string}` | HDAccount | PrivateKeyAccount | LocalAccount,
76
+ mnemonicOrPrivateKeyOrHdAccount: string | HDAccount | PrivateKeyAccount | LocalAccount,
77
77
  chain: Chain = foundry,
78
78
  pollingIntervalMS?: number,
79
79
  addressIndex?: number,
@@ -0,0 +1,6 @@
1
+ export class BlockTagTooOldError extends Error {
2
+ constructor(blockTag: bigint | number, latestBlock: bigint | number) {
3
+ super(`Block tag ${blockTag} is more than 128 blocks behind the latest block ${latestBlock}`);
4
+ this.name = 'BlockTagTooOldError';
5
+ }
6
+ }
@@ -8,7 +8,10 @@ import type { L1TxUtils } from '../l1_tx_utils.js';
8
8
  export class FeeAssetHandlerContract {
9
9
  public address: EthAddress;
10
10
 
11
- constructor(address: Hex, public readonly txUtils: L1TxUtils) {
11
+ constructor(
12
+ address: Hex,
13
+ public readonly txUtils: L1TxUtils,
14
+ ) {
12
15
  this.address = EthAddress.fromString(address);
13
16
  }
14
17
 
@@ -8,7 +8,10 @@ import { type ExtendedViemWalletClient, type ViemClient, isExtendedClient } from
8
8
  export class FeeJuiceContract {
9
9
  private readonly feeJuiceContract: GetContractReturnType<typeof FeeJuiceAbi, ViemClient>;
10
10
 
11
- constructor(address: Hex, public readonly client: ViemClient) {
11
+ constructor(
12
+ address: Hex,
13
+ public readonly client: ViemClient,
14
+ ) {
12
15
  this.feeJuiceContract = getContract({ address, abi: FeeJuiceAbi, client });
13
16
  }
14
17
 
@@ -19,7 +19,11 @@ import { RollupContract } from './rollup.js';
19
19
  export class ForwarderContract {
20
20
  private readonly forwarder: GetContractReturnType<typeof ForwarderAbi, ViemClient>;
21
21
 
22
- constructor(public readonly client: ExtendedViemWalletClient, address: Hex, public readonly rollupAddress: Hex) {
22
+ constructor(
23
+ public readonly client: ExtendedViemWalletClient,
24
+ address: Hex,
25
+ public readonly rollupAddress: Hex,
26
+ ) {
23
27
  this.forwarder = getContract({ address, abi: ForwarderAbi, client });
24
28
  }
25
29
 
@@ -50,7 +50,10 @@ export function extractProposalIdFromLogs(logs: Log[]): bigint {
50
50
  export class ReadOnlyGovernanceContract {
51
51
  protected readonly governanceContract: GetContractReturnType<typeof GovernanceAbi, ViemClient>;
52
52
 
53
- constructor(address: Hex, public readonly client: ViemClient) {
53
+ constructor(
54
+ address: Hex,
55
+ public readonly client: ViemClient,
56
+ ) {
54
57
  this.governanceContract = getContract({ address, abi: GovernanceAbi, client: client });
55
58
  }
56
59
 
@@ -72,6 +75,7 @@ export class ReadOnlyGovernanceContract {
72
75
 
73
76
  public async getProposalState(proposalId: bigint): Promise<ProposalState> {
74
77
  const state = await this.governanceContract.read.getProposalState([proposalId]);
78
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison
75
79
  if (state < 0 || state > ProposalState.Expired) {
76
80
  throw new Error(`Invalid proposal state: ${state}`);
77
81
  }
@@ -131,7 +135,10 @@ export class ReadOnlyGovernanceContract {
131
135
  export class GovernanceContract extends ReadOnlyGovernanceContract {
132
136
  protected override readonly governanceContract: GetContractReturnType<typeof GovernanceAbi, ExtendedViemWalletClient>;
133
137
 
134
- constructor(address: Hex, public override readonly client: ExtendedViemWalletClient) {
138
+ constructor(
139
+ address: Hex,
140
+ public override readonly client: ExtendedViemWalletClient,
141
+ ) {
135
142
  super(address, client);
136
143
  if (!isExtendedClient(client)) {
137
144
  throw new Error('GovernanceContract has to be instantiated with a wallet client.');
@@ -12,7 +12,10 @@ import { extractProposalIdFromLogs } from './governance.js';
12
12
  export class GovernanceProposerContract implements IEmpireBase {
13
13
  private readonly proposer: GetContractReturnType<typeof GovernanceProposerAbi, ViemClient>;
14
14
 
15
- constructor(public readonly client: ViemClient, address: Hex) {
15
+ constructor(
16
+ public readonly client: ViemClient,
17
+ address: Hex,
18
+ ) {
16
19
  this.proposer = getContract({ address, abi: GovernanceProposerAbi, client });
17
20
  }
18
21
 
@@ -0,0 +1,63 @@
1
+ import { Buffer16 } from '@aztec/foundation/buffer';
2
+ import { EthAddress } from '@aztec/foundation/eth-address';
3
+ import { InboxAbi } from '@aztec/l1-artifacts/InboxAbi';
4
+
5
+ import { type BlockTag, type GetContractReturnType, type Hex, getContract } from 'viem';
6
+
7
+ import { getPublicClient } from '../client.js';
8
+ import type { DeployL1ContractsReturnType } from '../deploy_l1_contracts.js';
9
+ import type { L1ReaderConfig } from '../l1_reader.js';
10
+ import type { ViemClient } from '../types.js';
11
+ import { checkBlockTag } from './utils.js';
12
+
13
+ export class InboxContract {
14
+ private readonly inbox: GetContractReturnType<typeof InboxAbi, ViemClient>;
15
+
16
+ static getFromL1ContractsValues(deployL1ContractsValues: DeployL1ContractsReturnType) {
17
+ const {
18
+ l1Client,
19
+ l1ContractAddresses: { inboxAddress },
20
+ } = deployL1ContractsValues;
21
+ return new InboxContract(l1Client, inboxAddress.toString());
22
+ }
23
+
24
+ static getFromConfig(config: L1ReaderConfig) {
25
+ const client = getPublicClient(config);
26
+ const address = config.l1Contracts.inboxAddress.toString();
27
+ return new InboxContract(client, address);
28
+ }
29
+
30
+ constructor(
31
+ public readonly client: ViemClient,
32
+ address: Hex | EthAddress,
33
+ ) {
34
+ if (address instanceof EthAddress) {
35
+ address = address.toString();
36
+ }
37
+ this.inbox = getContract({ address, abi: InboxAbi, client });
38
+ }
39
+
40
+ public get address() {
41
+ return this.inbox.address;
42
+ }
43
+
44
+ public getContract(): GetContractReturnType<typeof InboxAbi, ViemClient> {
45
+ return this.inbox;
46
+ }
47
+
48
+ public async getState(opts: { blockTag?: BlockTag; blockNumber?: bigint } = {}): Promise<InboxContractState> {
49
+ await checkBlockTag(opts.blockNumber, this.client);
50
+ const state = await this.inbox.read.getState(opts);
51
+ return {
52
+ totalMessagesInserted: state.totalMessagesInserted,
53
+ messagesRollingHash: Buffer16.fromString(state.rollingHash),
54
+ treeInProgress: state.inProgress,
55
+ };
56
+ }
57
+ }
58
+
59
+ export type InboxContractState = {
60
+ totalMessagesInserted: bigint;
61
+ messagesRollingHash: Buffer16;
62
+ treeInProgress: bigint;
63
+ };
@@ -4,6 +4,8 @@ export * from './fee_juice.js';
4
4
  export * from './forwarder.js';
5
5
  export * from './governance.js';
6
6
  export * from './governance_proposer.js';
7
+ export * from './inbox.js';
7
8
  export * from './registry.js';
8
9
  export * from './rollup.js';
9
10
  export * from './slashing_proposer.js';
11
+ export * from './errors.js';
@@ -16,7 +16,10 @@ export class RegistryContract {
16
16
  private readonly log = createLogger('ethereum:contracts:registry');
17
17
  private readonly registry: GetContractReturnType<typeof RegistryAbi, ViemClient>;
18
18
 
19
- constructor(public readonly client: ViemClient, address: Hex | EthAddress) {
19
+ constructor(
20
+ public readonly client: ViemClient,
21
+ address: Hex | EthAddress,
22
+ ) {
20
23
  if (address instanceof EthAddress) {
21
24
  address = address.toString();
22
25
  }
@@ -40,7 +43,7 @@ export class RegistryContract {
40
43
 
41
44
  try {
42
45
  return EthAddress.fromString(await this.registry.read.getRollup([version]));
43
- } catch (e) {
46
+ } catch {
44
47
  this.log.warn(`Failed fetching rollup address for version ${version}. Retrying as index.`);
45
48
  }
46
49
 
@@ -48,7 +51,7 @@ export class RegistryContract {
48
51
  const actualVersion = await this.registry.read.getVersion([version]);
49
52
  const rollupAddress = await this.registry.read.getRollup([actualVersion]);
50
53
  return EthAddress.fromString(rollupAddress);
51
- } catch (e) {
54
+ } catch {
52
55
  throw new Error('Rollup address is undefined');
53
56
  }
54
57
  }
@@ -14,6 +14,7 @@ import type { L1ReaderConfig } from '../l1_reader.js';
14
14
  import type { ViemClient } from '../types.js';
15
15
  import { formatViemError } from '../utils.js';
16
16
  import { SlashingProposerContract } from './slashing_proposer.js';
17
+ import { checkBlockTag } from './utils.js';
17
18
 
18
19
  export type L1RollupContractAddresses = Pick<
19
20
  L1ContractAddresses,
@@ -60,7 +61,10 @@ export class RollupContract {
60
61
  return new RollupContract(client, address);
61
62
  }
62
63
 
63
- constructor(public readonly client: ViemClient, address: Hex | EthAddress) {
64
+ constructor(
65
+ public readonly client: ViemClient,
66
+ address: Hex | EthAddress,
67
+ ) {
64
68
  if (address instanceof EthAddress) {
65
69
  address = address.toString();
66
70
  }
@@ -175,6 +179,14 @@ export class RollupContract {
175
179
  return this.rollup.read.getCurrentSlot();
176
180
  }
177
181
 
182
+ getL1FeesAt(timestamp: bigint) {
183
+ return this.rollup.read.getL1FeesAt([timestamp]);
184
+ }
185
+
186
+ getFeeAssetPerEth() {
187
+ return this.rollup.read.getFeeAssetPerEth();
188
+ }
189
+
178
190
  async getCommitteeAt(timestamp: bigint) {
179
191
  const { result } = await this.client.simulateContract({
180
192
  address: this.address,
@@ -381,11 +393,13 @@ export class RollupContract {
381
393
  return this.rollup.read.getSlotAt([timestamp]);
382
394
  }
383
395
 
384
- status(blockNumber: bigint, options?: { blockNumber?: bigint }) {
396
+ async status(blockNumber: bigint, options?: { blockNumber?: bigint }) {
397
+ await checkBlockTag(options?.blockNumber, this.client);
385
398
  return this.rollup.read.status([blockNumber], options);
386
399
  }
387
400
 
388
- canPruneAtTime(timestamp: bigint, options?: { blockNumber?: bigint }) {
401
+ async canPruneAtTime(timestamp: bigint, options?: { blockNumber?: bigint }) {
402
+ await checkBlockTag(options?.blockNumber, this.client);
389
403
  return this.rollup.read.canPruneAtTime([timestamp], options);
390
404
  }
391
405
 
@@ -10,7 +10,10 @@ import { type IEmpireBase, encodeVote } from './empire_base.js';
10
10
  export class SlashingProposerContract implements IEmpireBase {
11
11
  private readonly proposer: GetContractReturnType<typeof SlashingProposerAbi, ViemClient>;
12
12
 
13
- constructor(public readonly client: ViemClient, address: Hex) {
13
+ constructor(
14
+ public readonly client: ViemClient,
15
+ address: Hex,
16
+ ) {
14
17
  this.proposer = getContract({ address, abi: SlashingProposerAbi, client });
15
18
  }
16
19
 
@@ -0,0 +1,14 @@
1
+ import type { ViemClient } from '../types.js';
2
+ import { BlockTagTooOldError } from './errors.js';
3
+
4
+ const L1_NON_ARCHIVE_BLOCK_HISTORY_LENGTH = 128n;
5
+
6
+ export async function checkBlockTag(block: bigint | undefined, publicClient: ViemClient) {
7
+ if (block === undefined) {
8
+ return;
9
+ }
10
+ const latestBlock = await publicClient.getBlockNumber();
11
+ if (block < latestBlock - L1_NON_ARCHIVE_BLOCK_HISTORY_LENGTH) {
12
+ throw new BlockTagTooOldError(block, latestBlock);
13
+ }
14
+ }
@@ -18,6 +18,8 @@ import {
18
18
  GovernanceProposerBytecode,
19
19
  InboxAbi,
20
20
  InboxBytecode,
21
+ MultiAdderAbi,
22
+ MultiAdderBytecode,
21
23
  OutboxAbi,
22
24
  OutboxBytecode,
23
25
  RegisterNewRollupVersionPayloadAbi,
@@ -193,6 +195,10 @@ export const l1Artifacts = {
193
195
  contractAbi: StakingAssetHandlerAbi,
194
196
  contractBytecode: StakingAssetHandlerBytecode as Hex,
195
197
  },
198
+ multiAdder: {
199
+ contractAbi: MultiAdderAbi,
200
+ contractBytecode: MultiAdderBytecode as Hex,
201
+ },
196
202
  };
197
203
 
198
204
  export interface DeployL1ContractsArgs extends L1ContractsConfig {
@@ -550,7 +556,7 @@ export const deployRollup = async (
550
556
  try {
551
557
  const retrievedRollupAddress = await registryContract.read.getRollup([version]);
552
558
  logger.verbose(`Rollup ${retrievedRollupAddress} already exists in registry`);
553
- } catch (e) {
559
+ } catch {
554
560
  const { txHash: addRollupTxHash } = await deployer.sendTransaction({
555
561
  to: addresses.registryAddress.toString(),
556
562
  data: encodeFunctionData({
@@ -664,6 +670,18 @@ export const cheat_initializeValidatorSet = async (
664
670
  }
665
671
 
666
672
  if (validators.length > 0) {
673
+ const multiAdder = await deployer.deploy(l1Artifacts.multiAdder, [
674
+ rollupAddress,
675
+ deployer.client.account.address,
676
+ ]);
677
+
678
+ const validatorsTuples = validators.map(v => ({
679
+ attester: v,
680
+ proposer: getExpectedAddress(ForwarderAbi, ForwarderBytecode, [v], v).address,
681
+ withdrawer: v,
682
+ amount: minimumStake,
683
+ }));
684
+
667
685
  // Mint tokens, approve them, use cheat code to initialise validator set without setting up the epoch.
668
686
  const stakeNeeded = minimumStake * BigInt(validators.length);
669
687
  await Promise.all(
@@ -673,30 +691,16 @@ export const cheat_initializeValidatorSet = async (
673
691
  data: encodeFunctionData({
674
692
  abi: l1Artifacts.stakingAsset.contractAbi,
675
693
  functionName: 'mint',
676
- args: [extendedClient.account.address, stakeNeeded],
677
- }),
678
- }),
679
- await deployer.sendTransaction({
680
- to: stakingAssetAddress,
681
- data: encodeFunctionData({
682
- abi: l1Artifacts.stakingAsset.contractAbi,
683
- functionName: 'approve',
684
- args: [rollupAddress, stakeNeeded],
694
+ args: [multiAdder.toString(), stakeNeeded],
685
695
  }),
686
696
  }),
687
697
  ].map(tx => extendedClient.waitForTransactionReceipt({ hash: tx.txHash })),
688
698
  );
689
699
 
690
- const validatorsTuples = validators.map(v => ({
691
- attester: v,
692
- proposer: getExpectedAddress(ForwarderAbi, ForwarderBytecode, [v], v).address,
693
- withdrawer: v,
694
- amount: minimumStake,
695
- }));
696
700
  const initiateValidatorSetTxHash = await deployer.client.writeContract({
697
- address: rollupAddress,
698
- abi: l1Artifacts.rollup.contractAbi,
699
- functionName: 'cheat__InitialiseValidatorSet',
701
+ address: multiAdder.toString(),
702
+ abi: l1Artifacts.multiAdder.contractAbi,
703
+ functionName: 'addValidators',
700
704
  args: [validatorsTuples],
701
705
  });
702
706
  await extendedClient.waitForTransactionReceipt({ hash: initiateValidatorSetTxHash });
@@ -4,7 +4,7 @@ import type { EthAddress } from '@aztec/foundation/eth-address';
4
4
  import { jsonStringify } from '@aztec/foundation/json-rpc';
5
5
  import { createLogger } from '@aztec/foundation/log';
6
6
 
7
- import { type Hex, createPublicClient, fallback, http } from 'viem';
7
+ import { type Hex, createPublicClient, fallback, http, parseTransaction } from 'viem';
8
8
 
9
9
  import type { ViemPublicClient } from './types.js';
10
10
 
@@ -336,6 +336,13 @@ export class EthCheatCodes {
336
336
  depth: number,
337
337
  newBlocks: (Hex | { to: EthAddress | Hex; input?: Hex; from?: EthAddress | Hex; value?: number | bigint })[][] = [],
338
338
  ): Promise<void> {
339
+ this.logger.verbose(`Preparing L1 reorg with depth ${depth}`);
340
+ for (const tx of newBlocks.flat()) {
341
+ const isBlobTx = typeof tx === 'string' ? parseTransaction(tx).type === 'eip4844' : 'blobVersionedHashes' in tx;
342
+ if (isBlobTx) {
343
+ throw new Error(`Anvil does not support blob transactions in anvil_reorg`);
344
+ }
345
+ }
339
346
  try {
340
347
  await this.rpcCall('anvil_reorg', [
341
348
  depth,
package/src/index.ts CHANGED
@@ -12,3 +12,4 @@ export * from './contracts/index.js';
12
12
  export * from './queries.js';
13
13
  export * from './client.js';
14
14
  export * from './account.js';
15
+ export * from './l1_types.js';
@@ -167,6 +167,7 @@ export interface L1TxRequest {
167
167
  to: Address | null;
168
168
  data?: Hex;
169
169
  value?: bigint;
170
+ abi?: Abi;
170
171
  }
171
172
 
172
173
  export type L1GasConfig = Partial<L1TxUtilsConfig> & { gasLimit?: bigint; txTimeoutAt?: Date };
@@ -595,7 +596,7 @@ export class L1TxUtils extends ReadOnlyL1TxUtils {
595
596
 
596
597
  return { txHash, gasLimit, gasPrice };
597
598
  } catch (err: any) {
598
- const viemError = formatViemError(err);
599
+ const viemError = formatViemError(err, request.abi);
599
600
  this.logger?.error(`Failed to send L1 transaction`, viemError.message, {
600
601
  metaMessages: viemError.metaMessages,
601
602
  });
@@ -0,0 +1,6 @@
1
+ import type { Buffer32 } from '@aztec/foundation/buffer';
2
+
3
+ export type L1BlockId = {
4
+ l1BlockNumber: bigint;
5
+ l1BlockHash: Buffer32;
6
+ };
@@ -1,4 +1,4 @@
1
- import type { RollupContract } from '@aztec/ethereum/contracts';
1
+ import { InboxContract, type RollupContract } from '@aztec/ethereum/contracts';
2
2
  import { createLogger } from '@aztec/foundation/log';
3
3
 
4
4
  import { EventEmitter } from 'events';
@@ -8,6 +8,7 @@ import type { ViemClient } from '../types.js';
8
8
  /** Utility class that polls the chain on quick intervals and logs new L1 blocks, L2 blocks, and L2 proofs. */
9
9
  export class ChainMonitor extends EventEmitter {
10
10
  private readonly l1Client: ViemClient;
11
+ private inbox: InboxContract | undefined;
11
12
  private handle: NodeJS.Timeout | undefined;
12
13
 
13
14
  /** Current L1 block number */
@@ -20,6 +21,8 @@ export class ChainMonitor extends EventEmitter {
20
21
  public l2BlockTimestamp!: bigint;
21
22
  /** L1 timestamp for the proven L2 block */
22
23
  public l2ProvenBlockTimestamp!: bigint;
24
+ /** Total number of L2 messages pushed into the Inbox */
25
+ public totalL2Messages: number = 0;
23
26
 
24
27
  constructor(
25
28
  private readonly rollup: RollupContract,
@@ -46,16 +49,24 @@ export class ChainMonitor extends EventEmitter {
46
49
  }
47
50
  }
48
51
 
52
+ private async getInbox() {
53
+ if (!this.inbox) {
54
+ const { inboxAddress } = await this.rollup.getRollupAddresses();
55
+ this.inbox = new InboxContract(this.l1Client, inboxAddress);
56
+ }
57
+ return this.inbox;
58
+ }
59
+
49
60
  private safeRun() {
50
61
  void this.run().catch(error => {
51
62
  this.logger.error('Error in chain monitor loop', error);
52
63
  });
53
64
  }
54
65
 
55
- async run() {
66
+ async run(force = false) {
56
67
  const newL1BlockNumber = Number(await this.l1Client.getBlockNumber({ cacheTime: 0 }));
57
- if (this.l1BlockNumber === newL1BlockNumber) {
58
- return;
68
+ if (!force && this.l1BlockNumber === newL1BlockNumber) {
69
+ return this;
59
70
  }
60
71
  this.l1BlockNumber = newL1BlockNumber;
61
72
 
@@ -88,12 +99,23 @@ export class ChainMonitor extends EventEmitter {
88
99
  });
89
100
  }
90
101
 
102
+ const inbox = await this.getInbox();
103
+ const newTotalL2Messages = await inbox.getState().then(s => Number(s.totalMessagesInserted));
104
+ if (this.totalL2Messages !== newTotalL2Messages) {
105
+ msg += ` with ${newTotalL2Messages - this.totalL2Messages} new L2 messages (total ${newTotalL2Messages})`;
106
+ this.totalL2Messages = newTotalL2Messages;
107
+ this.emit('l2-messages', { totalL2Messages: newTotalL2Messages, l1BlockNumber: newL1BlockNumber });
108
+ }
109
+
91
110
  this.logger.info(msg, {
92
111
  l1Timestamp: timestamp,
93
112
  l1BlockNumber: this.l1BlockNumber,
94
113
  l2SlotNumber: await this.rollup.getSlotNumber(),
95
114
  l2BlockNumber: this.l2BlockNumber,
96
115
  l2ProvenBlockNumber: this.l2ProvenBlockNumber,
116
+ totalL2Messages: this.totalL2Messages,
97
117
  });
118
+
119
+ return this;
98
120
  }
99
121
  }