@aztec/ethereum 3.0.0-devnet.2 → 3.0.0-devnet.20251212

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 (142) hide show
  1. package/dest/account.d.ts +1 -1
  2. package/dest/chain.d.ts +1 -1
  3. package/dest/client.d.ts +1 -1
  4. package/dest/config.d.ts +7 -4
  5. package/dest/config.d.ts.map +1 -1
  6. package/dest/config.js +55 -17
  7. package/dest/constants.d.ts +1 -1
  8. package/dest/contracts/empire_base.d.ts +6 -5
  9. package/dest/contracts/empire_base.d.ts.map +1 -1
  10. package/dest/contracts/empire_base.js +1 -1
  11. package/dest/contracts/empire_slashing_proposer.d.ts +5 -4
  12. package/dest/contracts/empire_slashing_proposer.d.ts.map +1 -1
  13. package/dest/contracts/empire_slashing_proposer.js +8 -2
  14. package/dest/contracts/errors.d.ts +1 -1
  15. package/dest/contracts/errors.d.ts.map +1 -1
  16. package/dest/contracts/fee_asset_handler.d.ts +1 -1
  17. package/dest/contracts/fee_asset_handler.d.ts.map +1 -1
  18. package/dest/contracts/fee_juice.d.ts +1 -1
  19. package/dest/contracts/fee_juice.d.ts.map +1 -1
  20. package/dest/contracts/governance.d.ts +16 -16
  21. package/dest/contracts/governance.d.ts.map +1 -1
  22. package/dest/contracts/governance_proposer.d.ts +5 -4
  23. package/dest/contracts/governance_proposer.d.ts.map +1 -1
  24. package/dest/contracts/governance_proposer.js +8 -2
  25. package/dest/contracts/gse.d.ts +1 -1
  26. package/dest/contracts/gse.d.ts.map +1 -1
  27. package/dest/contracts/inbox.d.ts +1 -1
  28. package/dest/contracts/inbox.d.ts.map +1 -1
  29. package/dest/contracts/index.d.ts +1 -1
  30. package/dest/contracts/multicall.d.ts +2 -2
  31. package/dest/contracts/multicall.d.ts.map +1 -1
  32. package/dest/contracts/registry.d.ts +1 -1
  33. package/dest/contracts/registry.d.ts.map +1 -1
  34. package/dest/contracts/rollup.d.ts +55 -64
  35. package/dest/contracts/rollup.d.ts.map +1 -1
  36. package/dest/contracts/rollup.js +76 -67
  37. package/dest/contracts/slasher_contract.d.ts +1 -1
  38. package/dest/contracts/slasher_contract.d.ts.map +1 -1
  39. package/dest/contracts/tally_slashing_proposer.d.ts +9 -7
  40. package/dest/contracts/tally_slashing_proposer.d.ts.map +1 -1
  41. package/dest/contracts/tally_slashing_proposer.js +4 -4
  42. package/dest/contracts/utils.d.ts +1 -1
  43. package/dest/deploy_l1_contracts.d.ts +456 -9
  44. package/dest/deploy_l1_contracts.d.ts.map +1 -1
  45. package/dest/deploy_l1_contracts.js +68 -37
  46. package/dest/eth-signer/eth-signer.d.ts +1 -1
  47. package/dest/eth-signer/index.d.ts +1 -1
  48. package/dest/forwarder_proxy.d.ts +32 -0
  49. package/dest/forwarder_proxy.d.ts.map +1 -0
  50. package/dest/forwarder_proxy.js +93 -0
  51. package/dest/l1_artifacts.d.ts +6400 -872
  52. package/dest/l1_artifacts.d.ts.map +1 -1
  53. package/dest/l1_contract_addresses.d.ts +3 -3
  54. package/dest/l1_reader.d.ts +3 -1
  55. package/dest/l1_reader.d.ts.map +1 -1
  56. package/dest/l1_reader.js +6 -0
  57. package/dest/l1_tx_utils/config.d.ts +5 -5
  58. package/dest/l1_tx_utils/config.d.ts.map +1 -1
  59. package/dest/l1_tx_utils/config.js +32 -9
  60. package/dest/l1_tx_utils/constants.d.ts +1 -1
  61. package/dest/l1_tx_utils/factory.d.ts +1 -1
  62. package/dest/l1_tx_utils/forwarder_l1_tx_utils.d.ts +41 -0
  63. package/dest/l1_tx_utils/forwarder_l1_tx_utils.d.ts.map +1 -0
  64. package/dest/l1_tx_utils/forwarder_l1_tx_utils.js +48 -0
  65. package/dest/l1_tx_utils/index-blobs.d.ts +3 -0
  66. package/dest/l1_tx_utils/index-blobs.d.ts.map +1 -0
  67. package/dest/l1_tx_utils/index-blobs.js +2 -0
  68. package/dest/l1_tx_utils/index.d.ts +1 -1
  69. package/dest/l1_tx_utils/interfaces.d.ts +2 -2
  70. package/dest/l1_tx_utils/interfaces.d.ts.map +1 -1
  71. package/dest/l1_tx_utils/l1_tx_utils.d.ts +1 -2
  72. package/dest/l1_tx_utils/l1_tx_utils.d.ts.map +1 -1
  73. package/dest/l1_tx_utils/l1_tx_utils.js +17 -4
  74. package/dest/l1_tx_utils/l1_tx_utils_with_blobs.d.ts +1 -1
  75. package/dest/l1_tx_utils/l1_tx_utils_with_blobs.d.ts.map +1 -1
  76. package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts +29 -16
  77. package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts.map +1 -1
  78. package/dest/l1_tx_utils/readonly_l1_tx_utils.js +171 -34
  79. package/dest/l1_tx_utils/signer.d.ts +1 -1
  80. package/dest/l1_tx_utils/types.d.ts +1 -1
  81. package/dest/l1_tx_utils/types.d.ts.map +1 -1
  82. package/dest/l1_tx_utils/utils.d.ts +1 -1
  83. package/dest/l1_types.d.ts +1 -1
  84. package/dest/publisher_manager.d.ts +1 -1
  85. package/dest/publisher_manager.d.ts.map +1 -1
  86. package/dest/queries.d.ts +1 -1
  87. package/dest/queries.d.ts.map +1 -1
  88. package/dest/queries.js +5 -3
  89. package/dest/test/chain_monitor.d.ts +27 -24
  90. package/dest/test/chain_monitor.d.ts.map +1 -1
  91. package/dest/test/chain_monitor.js +32 -34
  92. package/dest/test/delayed_tx_utils.d.ts +1 -1
  93. package/dest/test/delayed_tx_utils.d.ts.map +1 -1
  94. package/dest/test/eth_cheat_codes.d.ts +8 -12
  95. package/dest/test/eth_cheat_codes.d.ts.map +1 -1
  96. package/dest/test/eth_cheat_codes.js +9 -3
  97. package/dest/test/eth_cheat_codes_with_state.d.ts +1 -1
  98. package/dest/test/eth_cheat_codes_with_state.d.ts.map +1 -1
  99. package/dest/test/index.d.ts +1 -1
  100. package/dest/test/rollup_cheat_codes.d.ts +14 -13
  101. package/dest/test/rollup_cheat_codes.d.ts.map +1 -1
  102. package/dest/test/rollup_cheat_codes.js +43 -38
  103. package/dest/test/start_anvil.d.ts +2 -1
  104. package/dest/test/start_anvil.d.ts.map +1 -1
  105. package/dest/test/start_anvil.js +2 -1
  106. package/dest/test/tx_delayer.d.ts +1 -1
  107. package/dest/test/tx_delayer.d.ts.map +1 -1
  108. package/dest/test/tx_delayer.js +3 -2
  109. package/dest/test/upgrade_utils.d.ts +1 -1
  110. package/dest/types.d.ts +57 -2
  111. package/dest/types.d.ts.map +1 -1
  112. package/dest/utils.d.ts +2 -2
  113. package/dest/utils.d.ts.map +1 -1
  114. package/dest/zkPassportVerifierAddress.d.ts +1 -1
  115. package/package.json +27 -13
  116. package/src/config.ts +62 -18
  117. package/src/contracts/empire_base.ts +6 -5
  118. package/src/contracts/empire_slashing_proposer.ts +11 -5
  119. package/src/contracts/governance_proposer.ts +11 -5
  120. package/src/contracts/rollup.ts +81 -80
  121. package/src/contracts/tally_slashing_proposer.ts +11 -8
  122. package/src/deploy_l1_contracts.ts +63 -32
  123. package/src/forwarder_proxy.ts +108 -0
  124. package/src/l1_reader.ts +8 -0
  125. package/src/l1_tx_utils/config.ts +34 -13
  126. package/src/l1_tx_utils/forwarder_l1_tx_utils.ts +119 -0
  127. package/src/l1_tx_utils/index-blobs.ts +2 -0
  128. package/src/l1_tx_utils/interfaces.ts +1 -1
  129. package/src/l1_tx_utils/l1_tx_utils.ts +24 -4
  130. package/src/l1_tx_utils/readonly_l1_tx_utils.ts +223 -38
  131. package/src/queries.ts +6 -3
  132. package/src/test/chain_monitor.ts +50 -48
  133. package/src/test/eth_cheat_codes.ts +8 -2
  134. package/src/test/rollup_cheat_codes.ts +44 -42
  135. package/src/test/start_anvil.ts +2 -0
  136. package/src/test/tx_delayer.ts +4 -2
  137. package/src/types.ts +62 -0
  138. package/src/utils.ts +1 -1
  139. package/dest/index.d.ts +0 -18
  140. package/dest/index.d.ts.map +0 -1
  141. package/dest/index.js +0 -17
  142. package/src/index.ts +0 -17
@@ -1,4 +1,7 @@
1
- import { type L1TxRequest, type ViemClient, tryExtractEvent } from '@aztec/ethereum';
1
+ import type { L1TxRequest } from '@aztec/ethereum/l1-tx-utils';
2
+ import type { ViemClient } from '@aztec/ethereum/types';
3
+ import { tryExtractEvent } from '@aztec/ethereum/utils';
4
+ import { SlotNumber } from '@aztec/foundation/branded-types';
2
5
  import { Buffer32 } from '@aztec/foundation/buffer';
3
6
  import { EthAddress } from '@aztec/foundation/eth-address';
4
7
  import { Signature } from '@aztec/foundation/eth-signature';
@@ -97,8 +100,8 @@ export class TallySlashingProposerContract {
97
100
  * @param slot - The slot number to check at
98
101
  * @returns Whether the round is ready to execute
99
102
  */
100
- public async isRoundReadyToExecute(round: bigint, slot: bigint): Promise<boolean> {
101
- return await this.contract.read.isRoundReadyToExecute([round, slot]);
103
+ public async isRoundReadyToExecute(round: bigint, slot: SlotNumber): Promise<boolean> {
104
+ return await this.contract.read.isRoundReadyToExecute([round, BigInt(slot)]);
102
105
  }
103
106
 
104
107
  /** Returns the slash actions and payload address for a given round (zero if no slash actions) */
@@ -149,7 +152,7 @@ export class TallySlashingProposerContract {
149
152
  */
150
153
  public async buildVoteRequestFromSigner(
151
154
  votes: Hex,
152
- slot: bigint,
155
+ slot: SlotNumber,
153
156
  signer: (msg: TypedDataDefinition) => Promise<Hex>,
154
157
  ): Promise<L1TxRequest> {
155
158
  const typedData = this.buildVoteTypedData(votes, slot);
@@ -166,7 +169,7 @@ export class TallySlashingProposerContract {
166
169
  }
167
170
 
168
171
  /** Returns the typed data definition to EIP712-sign for voting */
169
- public buildVoteTypedData(votes: Hex, slot: bigint): TypedDataDefinition {
172
+ public buildVoteTypedData(votes: Hex, slot: SlotNumber): TypedDataDefinition {
170
173
  const domain = {
171
174
  name: 'TallySlashingProposer',
172
175
  version: '1',
@@ -187,12 +190,12 @@ export class TallySlashingProposerContract {
187
190
  ],
188
191
  };
189
192
 
190
- return { domain, types, primaryType: 'Vote', message: { votes, slot } };
193
+ return { domain, types, primaryType: 'Vote', message: { votes, slot: BigInt(slot) } };
191
194
  }
192
195
 
193
196
  /** Gets the digest to sign for voting directly from the contract */
194
- public async getVoteDataDigest(votes: Hex, slot: bigint): Promise<Buffer32> {
195
- return Buffer32.fromString(await this.contract.read.getVoteSignatureDigest([votes, slot]));
197
+ public async getVoteDataDigest(votes: Hex, slot: SlotNumber): Promise<Buffer32> {
198
+ return Buffer32.fromString(await this.contract.read.getVoteSignatureDigest([votes, BigInt(slot)]));
196
199
  }
197
200
 
198
201
  /**
@@ -1,15 +1,16 @@
1
1
  import { L1_TO_L2_MSG_SUBTREE_HEIGHT } from '@aztec/constants';
2
+ import { SlotNumber } from '@aztec/foundation/branded-types';
2
3
  import { SecretValue, getActiveNetworkName } from '@aztec/foundation/config';
3
- import { keccak256String } from '@aztec/foundation/crypto';
4
+ import { keccak256String } from '@aztec/foundation/crypto/keccak';
5
+ import type { Fr } from '@aztec/foundation/curves/bn254';
4
6
  import { EthAddress } from '@aztec/foundation/eth-address';
5
- import type { Fr } from '@aztec/foundation/fields';
6
7
  import { jsonStringify } from '@aztec/foundation/json-rpc';
7
8
  import { type Logger, createLogger } from '@aztec/foundation/log';
8
9
  import { DateProvider } from '@aztec/foundation/timer';
9
10
  import { RollupAbi } from '@aztec/l1-artifacts/RollupAbi';
10
11
 
11
12
  import type { Abi, Narrow } from 'abitype';
12
- import { mkdir, writeFile } from 'fs/promises';
13
+ import fs from 'fs';
13
14
  import chunk from 'lodash.chunk';
14
15
  import {
15
16
  type Chain,
@@ -276,6 +277,25 @@ export const deploySharedContracts = async (
276
277
  feeAssetAddress = deployedFee.address;
277
278
  logger.verbose(`Deployed Fee Asset at ${feeAssetAddress}`);
278
279
 
280
+ // Mint a tiny bit of tokens to satisfy coin-issuer constraints
281
+ const { txHash } = await deployer.sendTransaction(
282
+ {
283
+ to: feeAssetAddress.toString(),
284
+ data: encodeFunctionData({
285
+ abi: FeeAssetArtifact.contractAbi,
286
+ functionName: 'mint',
287
+ args: [l1Client.account.address, 1n * 10n ** 18n],
288
+ }),
289
+ },
290
+ {
291
+ // contract may not have been deployed yet (CREATE2 returns address before mining),
292
+ // which causes gas estimation to fail. Hardcode to 100k which is plenty for ERC20 mint.
293
+ gasLimit: 100_000n,
294
+ },
295
+ );
296
+ await l1Client.waitForTransactionReceipt({ hash: txHash });
297
+ logger.verbose(`Minted tiny bit of tokens to satisfy coin-issuer constraints in ${txHash}`);
298
+
279
299
  const deployedStaking = await deployer.deploy(StakingAssetArtifact, ['Staking', 'STK', l1Client.account.address]);
280
300
  stakingAssetAddress = deployedStaking.address;
281
301
  logger.verbose(`Deployed Staking Asset at ${stakingAssetAddress}`);
@@ -351,12 +371,19 @@ export const deploySharedContracts = async (
351
371
  txHashes.push(txHash);
352
372
  }
353
373
 
374
+ logger.verbose(`Waiting for deployments to complete`);
375
+ await deployer.waitForDeployments();
376
+
354
377
  const coinIssuerAddress = (
355
- await deployer.deploy(CoinIssuerArtifact, [
356
- feeAssetAddress.toString(),
357
- 2n * 10n ** 17n, // hard cap of 20% per year
358
- l1Client.account.address,
359
- ])
378
+ await deployer.deploy(
379
+ CoinIssuerArtifact,
380
+ [
381
+ feeAssetAddress.toString(),
382
+ 2n * 10n ** 17n, // hard cap of 20% per year
383
+ l1Client.account.address,
384
+ ],
385
+ { gasLimit: 1_000_000n, noSimulation: true },
386
+ )
360
387
  ).address;
361
388
  logger.verbose(`Deployed CoinIssuer at ${coinIssuerAddress}`);
362
389
 
@@ -375,17 +402,16 @@ export const deploySharedContracts = async (
375
402
  /* CHEAT CODES START HERE */
376
403
  /* -------------------------------------------------------------------------- */
377
404
 
378
- feeAssetHandlerAddress = (
379
- await deployer.deploy(FeeAssetHandlerArtifact, [
380
- l1Client.account.address,
381
- feeAssetAddress.toString(),
382
- BigInt(1000n * 10n ** 18n),
383
- ])
384
- ).address;
405
+ const deployedFeeAssetHandler = await deployer.deploy(FeeAssetHandlerArtifact, [
406
+ l1Client.account.address,
407
+ feeAssetAddress.toString(),
408
+ BigInt(1000n * 10n ** 18n),
409
+ ]);
410
+ feeAssetHandlerAddress = deployedFeeAssetHandler.address;
385
411
  logger.verbose(`Deployed FeeAssetHandler at ${feeAssetHandlerAddress}`);
386
412
 
387
- // Only if we are "fresh" will we be adding as a minter, otherwise above will simply get same address
388
- if (needToSetGovernance) {
413
+ // Only add as minter if this is a new deployment (not reusing existing handler from failed previous run)
414
+ if (!deployedFeeAssetHandler.existed) {
389
415
  const { txHash } = await deployer.sendTransaction({
390
416
  to: feeAssetAddress.toString(),
391
417
  data: encodeFunctionData({
@@ -462,9 +488,9 @@ export const deploySharedContracts = async (
462
488
  const rewardDistributorAddress = await registry.getRewardDistributor();
463
489
 
464
490
  if (!args.existingTokenAddress) {
465
- const blockReward = getRewardConfig(networkName).blockReward;
491
+ const checkpointReward = getRewardConfig(networkName).checkpointReward;
466
492
 
467
- const funding = blockReward * 200000n;
493
+ const funding = checkpointReward * 200000n;
468
494
  const { txHash: fundRewardDistributorTxHash } = await deployer.sendTransaction({
469
495
  to: feeAssetAddress.toString(),
470
496
  data: encodeFunctionData({
@@ -693,7 +719,7 @@ async function writeVerificationJson(
693
719
  const date = new Date();
694
720
  const formattedDate = date.toISOString().slice(2, 19).replace(/[-T:]/g, '');
695
721
  // Ensure the verification output directory exists
696
- await mkdir(outputDirectory, { recursive: true });
722
+ await fs.promises.mkdir(outputDirectory, { recursive: true });
697
723
  const suffix = filenameSuffix ? `-${filenameSuffix}` : '';
698
724
  const verificationOutputPath = `${outputDirectory}/l1-verify${suffix}-${chainId}-${formattedDate.slice(0, 6)}-${formattedDate.slice(6)}.json`;
699
725
  const networkName = getActiveNetworkName();
@@ -702,7 +728,7 @@ async function writeVerificationJson(
702
728
  network: networkName,
703
729
  records: deployer.verificationRecords,
704
730
  };
705
- await writeFile(verificationOutputPath, JSON.stringify(verificationData, null, 2));
731
+ await fs.promises.writeFile(verificationOutputPath, JSON.stringify(verificationData, null, 2));
706
732
  logger.info(`Wrote L1 verification data to ${verificationOutputPath}`);
707
733
  } catch (e) {
708
734
  logger.warn(`Failed to write L1 verification data file: ${String(e)}`);
@@ -838,7 +864,8 @@ export const deployRollup = async (
838
864
  aztecSlotDuration: BigInt(args.aztecSlotDuration),
839
865
  aztecEpochDuration: BigInt(args.aztecEpochDuration),
840
866
  targetCommitteeSize: BigInt(args.aztecTargetCommitteeSize),
841
- lagInEpochs: BigInt(args.lagInEpochs),
867
+ lagInEpochsForValidatorSet: BigInt(args.lagInEpochsForValidatorSet),
868
+ lagInEpochsForRandao: BigInt(args.lagInEpochsForRandao),
842
869
  aztecProofSubmissionEpochs: BigInt(args.aztecProofSubmissionEpochs),
843
870
  slashingQuorum: BigInt(args.slashingQuorum ?? (args.slashingRoundSizeInEpochs * args.aztecEpochDuration) / 2 + 1),
844
871
  slashingRoundSize: BigInt(args.slashingRoundSizeInEpochs * args.aztecEpochDuration),
@@ -1493,13 +1520,13 @@ export const deployL1Contracts = async (
1493
1520
  // Need to get the time
1494
1521
  const currentSlot = await rollup.getSlotNumber();
1495
1522
 
1496
- if (BigInt(currentSlot) === 0n) {
1497
- const ts = Number(await rollup.getTimestampForSlot(1n));
1523
+ if (currentSlot === 0) {
1524
+ const ts = Number(await rollup.getTimestampForSlot(SlotNumber(1)));
1498
1525
  await rpcCall('evm_setNextBlockTimestamp', [ts]);
1499
1526
  await rpcCall('hardhat_mine', [1]);
1500
1527
  const currentSlot = await rollup.getSlotNumber();
1501
1528
 
1502
- if (BigInt(currentSlot) !== 1n) {
1529
+ if (currentSlot !== 1) {
1503
1530
  throw new Error(`Error jumping time: current slot is ${currentSlot}`);
1504
1531
  }
1505
1532
  logger.info(`Jumped to slot 1`);
@@ -1550,7 +1577,7 @@ export class L1Deployer {
1550
1577
  async deploy<const TAbi extends Abi>(
1551
1578
  params: ContractArtifacts<TAbi>,
1552
1579
  args?: ContractConstructorArgs<TAbi>,
1553
- opts: { gasLimit?: bigint } = {},
1580
+ opts: { gasLimit?: bigint; noSimulation?: boolean } = {},
1554
1581
  ): Promise<{ address: EthAddress; existed: boolean }> {
1555
1582
  this.logger.debug(`Deploying ${params.name} contract`, { args });
1556
1583
  try {
@@ -1566,6 +1593,7 @@ export class L1Deployer {
1566
1593
  l1TxUtils: this.l1TxUtils,
1567
1594
  acceleratedTestDeployments: this.acceleratedTestDeployments,
1568
1595
  gasLimit: opts.gasLimit,
1596
+ noSimulation: opts.noSimulation,
1569
1597
  },
1570
1598
  );
1571
1599
  if (txHash) {
@@ -1655,6 +1683,7 @@ export async function deployL1Contract(
1655
1683
  l1TxUtils?: L1TxUtils;
1656
1684
  gasLimit?: bigint;
1657
1685
  acceleratedTestDeployments?: boolean;
1686
+ noSimulation?: boolean;
1658
1687
  } = {},
1659
1688
  ): Promise<{
1660
1689
  address: EthAddress;
@@ -1666,7 +1695,7 @@ export async function deployL1Contract(
1666
1695
  let resultingAddress: Hex | null | undefined = undefined;
1667
1696
  const deployedLibraries: VerificationLibraryEntry[] = [];
1668
1697
 
1669
- const { salt: saltFromOpts, libraries, logger, gasLimit, acceleratedTestDeployments } = opts;
1698
+ const { salt: saltFromOpts, libraries, logger, gasLimit, acceleratedTestDeployments, noSimulation } = opts;
1670
1699
  let { l1TxUtils } = opts;
1671
1700
 
1672
1701
  if (!l1TxUtils) {
@@ -1775,11 +1804,13 @@ export async function deployL1Contract(
1775
1804
  resultingAddress = address;
1776
1805
  const existing = await extendedClient.getCode({ address: resultingAddress });
1777
1806
  if (existing === undefined || existing === '0x') {
1778
- try {
1779
- await l1TxUtils.simulate({ to: DEPLOYER_ADDRESS, data: concatHex([salt, calldata]), gas: gasLimit });
1780
- } catch (err) {
1781
- logger?.error(`Failed to simulate deployment tx using universal deployer`, err);
1782
- await l1TxUtils.simulate({ to: null, data: encodeDeployData({ abi, bytecode, args }), gas: gasLimit });
1807
+ if (!noSimulation) {
1808
+ try {
1809
+ await l1TxUtils.simulate({ to: DEPLOYER_ADDRESS, data: concatHex([salt, calldata]), gas: gasLimit });
1810
+ } catch (err) {
1811
+ logger?.error(`Failed to simulate deployment tx using universal deployer`, err);
1812
+ await l1TxUtils.simulate({ to: null, data: encodeDeployData({ abi, bytecode, args }), gas: gasLimit });
1813
+ }
1783
1814
  }
1784
1815
  const res = await l1TxUtils.sendTransaction(
1785
1816
  { to: DEPLOYER_ADDRESS, data: concatHex([salt, calldata]) },
@@ -0,0 +1,108 @@
1
+ /* eslint-disable no-console */
2
+ import { EthAddress } from '@aztec/foundation/eth-address';
3
+ import type { Logger } from '@aztec/foundation/log';
4
+ import { createLogger } from '@aztec/foundation/log';
5
+ import { DateProvider } from '@aztec/foundation/timer';
6
+
7
+ import { type Hex, extractChain } from 'viem';
8
+ import { anvil, mainnet, sepolia } from 'viem/chains';
9
+
10
+ import { L1Deployer } from './deploy_l1_contracts.js';
11
+ import type { ExtendedViemWalletClient } from './types.js';
12
+
13
+ export const FORWARDER_SOLIDITY_SOURCE = `
14
+ contract ForwarderProxy {
15
+ function forward(address target, bytes calldata data) external payable returns (bytes memory) {
16
+ (bool success, bytes memory result) = target.call{value: msg.value}(data);
17
+ require(success, "call failed");
18
+ return result;
19
+ }
20
+ }`;
21
+
22
+ export const FORWARDER_ABI = [
23
+ {
24
+ inputs: [
25
+ {
26
+ internalType: 'address',
27
+ name: 'target',
28
+ type: 'address',
29
+ },
30
+ {
31
+ internalType: 'bytes',
32
+ name: 'data',
33
+ type: 'bytes',
34
+ },
35
+ ],
36
+ name: 'forward',
37
+ outputs: [
38
+ {
39
+ internalType: 'bytes',
40
+ name: '',
41
+ type: 'bytes',
42
+ },
43
+ ],
44
+ stateMutability: 'payable',
45
+ type: 'function',
46
+ },
47
+ ] as const;
48
+
49
+ export const FORWARDER_BYTECODE =
50
+ '0x6080604052348015600e575f5ffd5b506103bf8061001c5f395ff3fe60806040526004361061001d575f3560e01c80636fadcf7214610021575b5f5ffd5b61003b600480360381019061003691906101d0565b610051565b604051610048919061029d565b60405180910390f35b60605f5f8573ffffffffffffffffffffffffffffffffffffffff1634868660405161007d9291906102f9565b5f6040518083038185875af1925050503d805f81146100b7576040519150601f19603f3d011682016040523d82523d5f602084013e6100bc565b606091505b509150915081610101576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100f89061036b565b60405180910390fd5b80925050509392505050565b5f5ffd5b5f5ffd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f61013e82610115565b9050919050565b61014e81610134565b8114610158575f5ffd5b50565b5f8135905061016981610145565b92915050565b5f5ffd5b5f5ffd5b5f5ffd5b5f5f83601f8401126101905761018f61016f565b5b8235905067ffffffffffffffff8111156101ad576101ac610173565b5b6020830191508360018202830111156101c9576101c8610177565b5b9250929050565b5f5f5f604084860312156101e7576101e661010d565b5b5f6101f48682870161015b565b935050602084013567ffffffffffffffff81111561021557610214610111565b5b6102218682870161017b565b92509250509250925092565b5f81519050919050565b5f82825260208201905092915050565b8281835e5f83830152505050565b5f601f19601f8301169050919050565b5f61026f8261022d565b6102798185610237565b9350610289818560208601610247565b61029281610255565b840191505092915050565b5f6020820190508181035f8301526102b58184610265565b905092915050565b5f81905092915050565b828183375f83830152505050565b5f6102e083856102bd565b93506102ed8385846102c7565b82840190509392505050565b5f6103058284866102d5565b91508190509392505050565b5f82825260208201905092915050565b7f63616c6c206661696c65640000000000000000000000000000000000000000005f82015250565b5f610355600b83610311565b915061036082610321565b602082019050919050565b5f6020820190508181035f83015261038281610349565b905091905056fea26469706673582212209a1c8cf638cf1569450a731ef9457b862f9e153b0a46e5555429bcf4dffd999564736f6c634300081e0033' as Hex;
51
+
52
+ const FORWARDER_ARTIFACT = {
53
+ name: 'Forwarder',
54
+ contractAbi: FORWARDER_ABI,
55
+ contractBytecode: FORWARDER_BYTECODE,
56
+ };
57
+
58
+ /**
59
+ * Deploys the forwarder proxy contract to L1.
60
+ * @param client - The L1 client to use for deployment
61
+ * @param logger - Optional logger
62
+ * @returns The deployed forwarder contract address
63
+ */
64
+ export async function deployForwarderProxy(client: ExtendedViemWalletClient, logger?: Logger): Promise<EthAddress> {
65
+ const log = logger ?? createLogger('ethereum:forwarder');
66
+ const nonce = await client.getTransactionCount({ address: client.account.address });
67
+ const deployer = new L1Deployer(client, nonce, new DateProvider(), false, log, undefined, false);
68
+
69
+ log.info('Deploying forwarder proxy contract');
70
+ const deployment = await deployer.deploy(FORWARDER_ARTIFACT, []);
71
+ log.info(`Forwarder proxy deployed at ${deployment.address.toString()}`);
72
+
73
+ return deployment.address;
74
+ }
75
+
76
+ /**
77
+ * Main function for deploying the forwarder proxy from command line.
78
+ * Usage: node forwarder_proxy.js <private_key> <rpc_url>
79
+ */
80
+ async function main() {
81
+ const args = process.argv.slice(2);
82
+ if (args.length < 3) {
83
+ console.error('Usage: node forwarder_proxy.js <private_key> <rpc_url> <chain_id>');
84
+ process.exit(1);
85
+ }
86
+
87
+ const [privateKey, rpcUrl, chainId] = args;
88
+
89
+ // Dynamic import to avoid pulling in dependencies at module load time
90
+ const { createExtendedL1Client } = await import('./client.js');
91
+
92
+ const client = createExtendedL1Client(
93
+ [rpcUrl],
94
+ privateKey.startsWith('0x') ? privateKey : `0x${privateKey}`,
95
+ extractChain({ chains: [mainnet, sepolia, anvil], id: parseInt(chainId) as any }),
96
+ );
97
+ const address = await deployForwarderProxy(client);
98
+
99
+ console.log(`Forwarder proxy deployed at: ${address.toString()}`);
100
+ }
101
+
102
+ // Only run main if this is the entry point
103
+ if (import.meta.url === `file://${process.argv[1]}`) {
104
+ main().catch(err => {
105
+ console.error('Failed to deploy forwarder proxy:', err);
106
+ process.exit(1);
107
+ });
108
+ }
package/src/l1_reader.ts CHANGED
@@ -6,6 +6,8 @@ import { type L1ContractAddresses, l1ContractAddressesMapping } from './l1_contr
6
6
  export interface L1ReaderConfig {
7
7
  /** List of URLs of Ethereum RPC nodes that services will connect to (comma separated). */
8
8
  l1RpcUrls: string[];
9
+ /** The RPC Url of the ethereum debug host for trace and debug methods. */
10
+ l1DebugRpcUrls: string[];
9
11
  /** The chain ID of the ethereum host. */
10
12
  l1ChainId: number;
11
13
  /** The deployed l1 contract addresses */
@@ -30,6 +32,12 @@ export const l1ReaderConfigMappings: ConfigMappingsType<L1ReaderConfig> = {
30
32
  parseEnv: (val: string) => val.split(',').map(url => url.trim()),
31
33
  defaultValue: [],
32
34
  },
35
+ l1DebugRpcUrls: {
36
+ env: 'ETHEREUM_DEBUG_HOSTS',
37
+ description: 'The RPC Url of the ethereum debug host for trace and debug methods.',
38
+ parseEnv: (val: string) => val.split(',').map(url => url.trim()),
39
+ defaultValue: [],
40
+ },
33
41
  viemPollingIntervalMS: {
34
42
  env: 'L1_READER_VIEM_POLLING_INTERVAL_MS',
35
43
  description: 'The polling interval viem uses in ms',
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  type ConfigMappingsType,
3
- bigintConfigHelper,
4
3
  booleanConfigHelper,
4
+ floatConfigHelper,
5
5
  getConfigFromMappings,
6
6
  getDefaultConfig,
7
7
  numberConfigHelper,
@@ -15,11 +15,11 @@ export interface L1TxUtilsConfig {
15
15
  /**
16
16
  * Maximum gas price in gwei
17
17
  */
18
- maxGwei?: bigint;
18
+ maxGwei?: number;
19
19
  /**
20
20
  * Maximum blob fee per gas in gwei
21
21
  */
22
- maxBlobGwei?: bigint;
22
+ maxBlobGwei?: number;
23
23
  /**
24
24
  * Priority fee bump percentage
25
25
  */
@@ -29,9 +29,9 @@ export interface L1TxUtilsConfig {
29
29
  */
30
30
  priorityFeeRetryBumpPercentage?: number;
31
31
  /**
32
- * Fixed priority fee per gas in Gwei. Overrides any priority fee bump percentage config
32
+ * Minimum priority fee per gas in Gwei. Acts as a floor for the computed priority fee.
33
33
  */
34
- fixedPriorityFeePerGas?: number;
34
+ minimumPriorityFeePerGas?: number;
35
35
  /**
36
36
  * Maximum number of speed-up attempts
37
37
  */
@@ -69,14 +69,16 @@ export const l1TxUtilsConfigMappings: ConfigMappingsType<L1TxUtilsConfig> = {
69
69
  ...numberConfigHelper(20),
70
70
  },
71
71
  maxGwei: {
72
- description: 'Maximum gas price in gwei',
72
+ description: 'Maximum gas price in gwei to be used for transactions.',
73
73
  env: 'L1_GAS_PRICE_MAX',
74
- ...bigintConfigHelper(500n),
74
+ fallback: ['L1_FEE_PER_GAS_GWEI_MAX'],
75
+ ...floatConfigHelper(2000),
75
76
  },
76
77
  maxBlobGwei: {
77
78
  description: 'Maximum blob fee per gas in gwei',
78
79
  env: 'L1_BLOB_FEE_PER_GAS_MAX',
79
- ...bigintConfigHelper(1_500n),
80
+ fallback: ['L1_BLOB_FEE_PER_GAS_GWEI_MAX'],
81
+ ...floatConfigHelper(3000),
80
82
  },
81
83
  priorityFeeBumpPercentage: {
82
84
  description: 'How much to increase priority fee by each attempt (percentage)',
@@ -88,10 +90,22 @@ export const l1TxUtilsConfigMappings: ConfigMappingsType<L1TxUtilsConfig> = {
88
90
  env: 'L1_PRIORITY_FEE_RETRY_BUMP_PERCENTAGE',
89
91
  ...numberConfigHelper(50),
90
92
  },
91
- fixedPriorityFeePerGas: {
92
- description: 'Fixed priority fee per gas in Gwei. Overrides any priority fee bump percentage',
93
- env: 'L1_FIXED_PRIORITY_FEE_PER_GAS',
94
- ...numberConfigHelper(0),
93
+ minimumPriorityFeePerGas: {
94
+ description:
95
+ 'Minimum priority fee per gas in Gwei. Acts as a floor for the computed priority fee. If network conditions require a higher fee, the higher fee will be used.',
96
+ env: 'L1_MINIMUM_PRIORITY_FEE_PER_GAS_GWEI',
97
+ fallback: ['L1_FIXED_PRIORITY_FEE_PER_GAS', 'L1_FIXED_PRIORITY_FEE_PER_GAS_GWEI'],
98
+ deprecatedFallback: [
99
+ {
100
+ env: 'L1_FIXED_PRIORITY_FEE_PER_GAS',
101
+ message: deprecatedFixedFeeMessage('L1_FIXED_PRIORITY_FEE_PER_GAS'),
102
+ },
103
+ {
104
+ env: 'L1_FIXED_PRIORITY_FEE_PER_GAS_GWEI',
105
+ message: deprecatedFixedFeeMessage('L1_FIXED_PRIORITY_FEE_PER_GAS_GWEI'),
106
+ },
107
+ ],
108
+ ...floatConfigHelper(0),
95
109
  },
96
110
  maxSpeedUpAttempts: {
97
111
  description: 'Maximum number of speed-up attempts',
@@ -106,7 +120,7 @@ export const l1TxUtilsConfigMappings: ConfigMappingsType<L1TxUtilsConfig> = {
106
120
  stallTimeMs: {
107
121
  description: 'How long before considering tx stalled',
108
122
  env: 'L1_TX_MONITOR_STALL_TIME_MS',
109
- ...numberConfigHelper(24_000), // 24s, 2 ethereum slots
123
+ ...numberConfigHelper(12_000), // 12s, 1 ethereum slot
110
124
  },
111
125
  txTimeoutMs: {
112
126
  description: 'How long to wait for a tx to be mined before giving up. Set to 0 to disable.',
@@ -138,3 +152,10 @@ export const defaultL1TxUtilsConfig = getDefaultConfig<L1TxUtilsConfig>(
138
152
  export function getL1TxUtilsConfigEnvVars(): L1TxUtilsConfig {
139
153
  return getConfigFromMappings(l1TxUtilsConfigMappings);
140
154
  }
155
+
156
+ function deprecatedFixedFeeMessage(envVar: string): string {
157
+ return (
158
+ `Environment variable ${envVar} is deprecated. It is now used as a MINIMUM priority fee rather than a fixed value. ` +
159
+ 'Please use L1_MINIMUM_PRIORITY_FEE_PER_GAS_GWEI instead. If network conditions require a higher fee, the higher fee will be used.'
160
+ );
161
+ }
@@ -0,0 +1,119 @@
1
+ import { EthAddress } from '@aztec/foundation/eth-address';
2
+ import type { Logger } from '@aztec/foundation/log';
3
+ import type { DateProvider } from '@aztec/foundation/timer';
4
+
5
+ import { type Hex, encodeFunctionData } from 'viem';
6
+
7
+ import type { EthSigner } from '../eth-signer/eth-signer.js';
8
+ import { FORWARDER_ABI } from '../forwarder_proxy.js';
9
+ import type { ExtendedViemWalletClient, ViemClient } from '../types.js';
10
+ import type { L1TxUtilsConfig } from './config.js';
11
+ import type { IL1TxMetrics, IL1TxStore } from './interfaces.js';
12
+ import { L1TxUtilsWithBlobs } from './l1_tx_utils_with_blobs.js';
13
+ import { createViemSigner } from './signer.js';
14
+ import type { L1BlobInputs, L1TxConfig, L1TxRequest, SigningCallback } from './types.js';
15
+
16
+ /**
17
+ * Extends L1TxUtilsWithBlobs to wrap all transactions through a forwarder contract.
18
+ * This is mainly used for testing the archiver's ability to decode transactions that go through proxies.
19
+ */
20
+ export class ForwarderL1TxUtils extends L1TxUtilsWithBlobs {
21
+ constructor(
22
+ client: ViemClient | ExtendedViemWalletClient,
23
+ senderAddress: EthAddress,
24
+ signingCallback: SigningCallback,
25
+ logger: Logger | undefined,
26
+ dateProvider: DateProvider | undefined,
27
+ config: Partial<L1TxUtilsConfig>,
28
+ debugMaxGasLimit: boolean,
29
+ store: IL1TxStore | undefined,
30
+ metrics: IL1TxMetrics | undefined,
31
+ private readonly forwarderAddress: EthAddress,
32
+ ) {
33
+ super(client, senderAddress, signingCallback, logger, dateProvider, config, debugMaxGasLimit, store, metrics);
34
+ }
35
+
36
+ /**
37
+ * Wraps the transaction request in a call to the forwarder contract.
38
+ */
39
+ private wrapInForwarder(request: L1TxRequest): L1TxRequest {
40
+ const forwarderCalldata = encodeFunctionData({
41
+ abi: FORWARDER_ABI,
42
+ functionName: 'forward',
43
+ args: [request.to as Hex, request.data as Hex],
44
+ });
45
+
46
+ return {
47
+ to: this.forwarderAddress.toString() as Hex,
48
+ data: forwarderCalldata,
49
+ value: request.value,
50
+ abi: request.abi, // Preserve the original ABI for error decoding
51
+ };
52
+ }
53
+
54
+ /**
55
+ * Override sendAndMonitorTransaction to wrap the request in a forwarder call.
56
+ */
57
+ public override sendAndMonitorTransaction(request: L1TxRequest, gasConfig?: L1TxConfig, blobInputs?: L1BlobInputs) {
58
+ this.logger.debug(`Wrapping transaction to ${request.to} in forwarder at ${this.forwarderAddress.toString()}`);
59
+ const wrappedRequest = this.wrapInForwarder(request);
60
+ return super.sendAndMonitorTransaction(wrappedRequest, gasConfig, blobInputs);
61
+ }
62
+ }
63
+
64
+ export function createForwarderL1TxUtilsFromViemWallet(
65
+ client: ExtendedViemWalletClient,
66
+ forwarderAddress: EthAddress,
67
+ deps: {
68
+ logger?: Logger;
69
+ dateProvider?: DateProvider;
70
+ store?: IL1TxStore;
71
+ metrics?: IL1TxMetrics;
72
+ } = {},
73
+ config: Partial<L1TxUtilsConfig> = {},
74
+ debugMaxGasLimit: boolean = false,
75
+ ) {
76
+ return new ForwarderL1TxUtils(
77
+ client,
78
+ EthAddress.fromString(client.account.address),
79
+ createViemSigner(client),
80
+ deps.logger,
81
+ deps.dateProvider,
82
+ config,
83
+ debugMaxGasLimit,
84
+ deps.store,
85
+ deps.metrics,
86
+ forwarderAddress,
87
+ );
88
+ }
89
+
90
+ export function createForwarderL1TxUtilsFromEthSigner(
91
+ client: ViemClient,
92
+ signer: EthSigner,
93
+ forwarderAddress: EthAddress,
94
+ deps: {
95
+ logger?: Logger;
96
+ dateProvider?: DateProvider;
97
+ store?: IL1TxStore;
98
+ metrics?: IL1TxMetrics;
99
+ } = {},
100
+ config: Partial<L1TxUtilsConfig> = {},
101
+ debugMaxGasLimit: boolean = false,
102
+ ) {
103
+ const callback: SigningCallback = async (transaction, _signingAddress) => {
104
+ return (await signer.signTransaction(transaction)).toViemTransactionSignature();
105
+ };
106
+
107
+ return new ForwarderL1TxUtils(
108
+ client,
109
+ signer.address,
110
+ callback,
111
+ deps.logger,
112
+ deps.dateProvider,
113
+ config,
114
+ debugMaxGasLimit,
115
+ deps.store,
116
+ deps.metrics,
117
+ forwarderAddress,
118
+ );
119
+ }
@@ -0,0 +1,2 @@
1
+ export * from './forwarder_l1_tx_utils.js';
2
+ export * from './l1_tx_utils_with_blobs.js';
@@ -46,7 +46,7 @@ export interface IL1TxStore {
46
46
  * @param account - The sender account address
47
47
  * @param stateId - The state ID to delete
48
48
  */
49
- deleteState(account: string, stateId: number): Promise<void>;
49
+ deleteState(account: string, ...stateId: number[]): Promise<void>;
50
50
 
51
51
  /**
52
52
  * Clears all transaction states for a specific account.
@@ -130,12 +130,32 @@ export class L1TxUtils extends ReadOnlyL1TxUtils {
130
130
  return;
131
131
  }
132
132
 
133
- // Convert loaded states (which have id) to the txs format
134
- this.txs = loadedStates;
135
- this.logger.info(`Rehydrated ${loadedStates.length} tx states for account ${account}`);
133
+ // Clean up excess states if we have more than MAX_L1_TX_STATES
134
+ if (loadedStates.length > MAX_L1_TX_STATES) {
135
+ this.logger.warn(
136
+ `Found ${loadedStates.length} tx states for account ${account}, pruning to most recent ${MAX_L1_TX_STATES}`,
137
+ );
138
+
139
+ // Keep only the most recent MAX_L1_TX_STATES
140
+ const statesToKeep = loadedStates.slice(-MAX_L1_TX_STATES);
141
+ const statesToDelete = loadedStates.slice(0, -MAX_L1_TX_STATES);
142
+
143
+ // Batch delete old states in a transaction for efficiency
144
+ const idsToDelete = statesToDelete.map(s => s.id);
145
+ await this.store.deleteState(account, ...idsToDelete);
146
+
147
+ this.txs = statesToKeep;
148
+ this.logger.info(
149
+ `Cleaned up ${statesToDelete.length} old tx states, kept ${statesToKeep.length} for account ${account}`,
150
+ );
151
+ } else {
152
+ // Convert loaded states (which have id) to the txs format
153
+ this.txs = loadedStates;
154
+ this.logger.info(`Rehydrated ${loadedStates.length} tx states for account ${account}`);
155
+ }
136
156
 
137
157
  // Find all pending states and resume monitoring
138
- const pendingStates = loadedStates.filter(state => !TerminalTxUtilsState.includes(state.status));
158
+ const pendingStates = this.txs.filter(state => !TerminalTxUtilsState.includes(state.status));
139
159
  if (pendingStates.length === 0) {
140
160
  return;
141
161
  }