@aztec/ethereum 0.0.1-commit.3469e52 → 0.0.1-commit.381b1a9

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 (135) hide show
  1. package/dest/config.d.ts +5 -1
  2. package/dest/config.d.ts.map +1 -1
  3. package/dest/config.js +16 -3
  4. package/dest/contracts/empire_base.d.ts +3 -1
  5. package/dest/contracts/empire_base.d.ts.map +1 -1
  6. package/dest/contracts/empire_slashing_proposer.d.ts +3 -1
  7. package/dest/contracts/empire_slashing_proposer.d.ts.map +1 -1
  8. package/dest/contracts/empire_slashing_proposer.js +22 -15
  9. package/dest/contracts/fee_asset_handler.d.ts +1 -1
  10. package/dest/contracts/fee_asset_handler.d.ts.map +1 -1
  11. package/dest/contracts/fee_asset_handler.js +2 -0
  12. package/dest/contracts/fee_asset_price_oracle.d.ts +101 -0
  13. package/dest/contracts/fee_asset_price_oracle.d.ts.map +1 -0
  14. package/dest/contracts/fee_asset_price_oracle.js +651 -0
  15. package/dest/contracts/governance.d.ts +3 -1
  16. package/dest/contracts/governance.d.ts.map +1 -1
  17. package/dest/contracts/governance.js +14 -4
  18. package/dest/contracts/governance_proposer.d.ts +3 -1
  19. package/dest/contracts/governance_proposer.d.ts.map +1 -1
  20. package/dest/contracts/governance_proposer.js +13 -1
  21. package/dest/contracts/index.d.ts +2 -1
  22. package/dest/contracts/index.d.ts.map +1 -1
  23. package/dest/contracts/index.js +1 -0
  24. package/dest/contracts/multicall.d.ts +1 -1
  25. package/dest/contracts/multicall.d.ts.map +1 -1
  26. package/dest/contracts/multicall.js +2 -1
  27. package/dest/contracts/registry.d.ts +3 -1
  28. package/dest/contracts/registry.d.ts.map +1 -1
  29. package/dest/contracts/registry.js +30 -1
  30. package/dest/contracts/rollup.d.ts +14 -8
  31. package/dest/contracts/rollup.d.ts.map +1 -1
  32. package/dest/contracts/rollup.js +55 -10
  33. package/dest/contracts/tally_slashing_proposer.d.ts +1 -1
  34. package/dest/contracts/tally_slashing_proposer.d.ts.map +1 -1
  35. package/dest/contracts/tally_slashing_proposer.js +8 -1
  36. package/dest/deploy_aztec_l1_contracts.d.ts +4 -3
  37. package/dest/deploy_aztec_l1_contracts.d.ts.map +1 -1
  38. package/dest/deploy_aztec_l1_contracts.js +38 -22
  39. package/dest/deploy_l1_contract.js +3 -3
  40. package/dest/generated/l1-contracts-defaults.d.ts +4 -3
  41. package/dest/generated/l1-contracts-defaults.d.ts.map +1 -1
  42. package/dest/generated/l1-contracts-defaults.js +3 -2
  43. package/dest/l1_artifacts.d.ts +1857 -52
  44. package/dest/l1_artifacts.d.ts.map +1 -1
  45. package/dest/l1_tx_utils/config.d.ts +7 -1
  46. package/dest/l1_tx_utils/config.d.ts.map +1 -1
  47. package/dest/l1_tx_utils/config.js +14 -1
  48. package/dest/l1_tx_utils/constants.d.ts +1 -1
  49. package/dest/l1_tx_utils/constants.js +2 -2
  50. package/dest/l1_tx_utils/factory.d.ts +18 -10
  51. package/dest/l1_tx_utils/factory.d.ts.map +1 -1
  52. package/dest/l1_tx_utils/factory.js +17 -7
  53. package/dest/l1_tx_utils/fee-strategies/p75_competitive.js +1 -1
  54. package/dest/l1_tx_utils/fee-strategies/p75_competitive_blob_txs_only.js +1 -1
  55. package/dest/l1_tx_utils/forwarder_l1_tx_utils.d.ts +15 -15
  56. package/dest/l1_tx_utils/forwarder_l1_tx_utils.d.ts.map +1 -1
  57. package/dest/l1_tx_utils/forwarder_l1_tx_utils.js +9 -15
  58. package/dest/l1_tx_utils/index-blobs.d.ts +3 -3
  59. package/dest/l1_tx_utils/index-blobs.d.ts.map +1 -1
  60. package/dest/l1_tx_utils/index-blobs.js +2 -2
  61. package/dest/l1_tx_utils/index.d.ts +2 -1
  62. package/dest/l1_tx_utils/index.d.ts.map +1 -1
  63. package/dest/l1_tx_utils/index.js +1 -0
  64. package/dest/l1_tx_utils/l1_tx_utils.d.ts +18 -7
  65. package/dest/l1_tx_utils/l1_tx_utils.d.ts.map +1 -1
  66. package/dest/l1_tx_utils/l1_tx_utils.js +58 -42
  67. package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts +1 -1
  68. package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts.map +1 -1
  69. package/dest/l1_tx_utils/readonly_l1_tx_utils.js +11 -7
  70. package/dest/l1_tx_utils/tx_delayer.d.ts +56 -0
  71. package/dest/l1_tx_utils/tx_delayer.d.ts.map +1 -0
  72. package/dest/{test → l1_tx_utils}/tx_delayer.js +62 -34
  73. package/dest/publisher_manager.d.ts +3 -2
  74. package/dest/publisher_manager.d.ts.map +1 -1
  75. package/dest/publisher_manager.js +2 -2
  76. package/dest/queries.d.ts +1 -1
  77. package/dest/queries.d.ts.map +1 -1
  78. package/dest/queries.js +2 -1
  79. package/dest/test/index.d.ts +1 -3
  80. package/dest/test/index.d.ts.map +1 -1
  81. package/dest/test/index.js +0 -2
  82. package/dest/test/rollup_cheat_codes.d.ts +4 -2
  83. package/dest/test/rollup_cheat_codes.d.ts.map +1 -1
  84. package/dest/test/rollup_cheat_codes.js +10 -1
  85. package/dest/test/start_anvil.d.ts +16 -3
  86. package/dest/test/start_anvil.d.ts.map +1 -1
  87. package/dest/test/start_anvil.js +129 -29
  88. package/dest/test/upgrade_utils.js +2 -2
  89. package/dest/utils.d.ts +2 -1
  90. package/dest/utils.d.ts.map +1 -1
  91. package/dest/utils.js +46 -0
  92. package/package.json +5 -5
  93. package/src/config.ts +22 -2
  94. package/src/contracts/empire_base.ts +2 -0
  95. package/src/contracts/empire_slashing_proposer.ts +22 -27
  96. package/src/contracts/fee_asset_handler.ts +2 -0
  97. package/src/contracts/fee_asset_price_oracle.ts +280 -0
  98. package/src/contracts/governance.ts +13 -4
  99. package/src/contracts/governance_proposer.ts +10 -1
  100. package/src/contracts/index.ts +1 -0
  101. package/src/contracts/multicall.ts +5 -2
  102. package/src/contracts/registry.ts +31 -1
  103. package/src/contracts/rollup.ts +60 -13
  104. package/src/contracts/tally_slashing_proposer.ts +5 -1
  105. package/src/deploy_aztec_l1_contracts.ts +61 -29
  106. package/src/deploy_l1_contract.ts +3 -3
  107. package/src/generated/l1-contracts-defaults.ts +3 -2
  108. package/src/l1_tx_utils/config.ts +20 -0
  109. package/src/l1_tx_utils/constants.ts +2 -2
  110. package/src/l1_tx_utils/factory.ts +31 -31
  111. package/src/l1_tx_utils/fee-strategies/p75_competitive.ts +1 -1
  112. package/src/l1_tx_utils/fee-strategies/p75_competitive_blob_txs_only.ts +1 -1
  113. package/src/l1_tx_utils/forwarder_l1_tx_utils.ts +43 -54
  114. package/src/l1_tx_utils/index-blobs.ts +2 -2
  115. package/src/l1_tx_utils/index.ts +1 -0
  116. package/src/l1_tx_utils/l1_tx_utils.ts +60 -32
  117. package/src/l1_tx_utils/readonly_l1_tx_utils.ts +11 -7
  118. package/src/{test → l1_tx_utils}/tx_delayer.ts +78 -50
  119. package/src/publisher_manager.ts +4 -2
  120. package/src/queries.ts +1 -0
  121. package/src/test/index.ts +0 -2
  122. package/src/test/rollup_cheat_codes.ts +11 -2
  123. package/src/test/start_anvil.ts +154 -29
  124. package/src/test/upgrade_utils.ts +2 -2
  125. package/src/utils.ts +53 -0
  126. package/dest/l1_tx_utils/l1_tx_utils_with_blobs.d.ts +0 -26
  127. package/dest/l1_tx_utils/l1_tx_utils_with_blobs.d.ts.map +0 -1
  128. package/dest/l1_tx_utils/l1_tx_utils_with_blobs.js +0 -26
  129. package/dest/test/delayed_tx_utils.d.ts +0 -13
  130. package/dest/test/delayed_tx_utils.d.ts.map +0 -1
  131. package/dest/test/delayed_tx_utils.js +0 -28
  132. package/dest/test/tx_delayer.d.ts +0 -36
  133. package/dest/test/tx_delayer.d.ts.map +0 -1
  134. package/src/l1_tx_utils/l1_tx_utils_with_blobs.ts +0 -77
  135. package/src/test/delayed_tx_utils.ts +0 -52
@@ -34,10 +34,13 @@ export class Multicall3 {
34
34
  };
35
35
 
36
36
  const encodedForwarderData = encodeFunctionData(forwarderFunctionData);
37
-
38
37
  try {
39
38
  const { receipt, state } = await l1TxUtils.sendAndMonitorTransaction(
40
- { to: MULTI_CALL_3_ADDRESS, data: encodedForwarderData },
39
+ {
40
+ to: MULTI_CALL_3_ADDRESS,
41
+ data: encodedForwarderData,
42
+ abi: multicall3Abi,
43
+ },
41
44
  gasConfig,
42
45
  blobConfig,
43
46
  );
@@ -3,7 +3,7 @@ import { createLogger } from '@aztec/foundation/log';
3
3
  import { RegistryAbi } from '@aztec/l1-artifacts/RegistryAbi';
4
4
  import { TestERC20Abi } from '@aztec/l1-artifacts/TestERC20Abi';
5
5
 
6
- import { type GetContractReturnType, type Hex, getContract } from 'viem';
6
+ import { type GetContractReturnType, type Hex, getAbiItem, getContract } from 'viem';
7
7
 
8
8
  import type { L1ContractAddresses } from '../l1_contract_addresses.js';
9
9
  import type { ViemClient } from '../types.js';
@@ -128,4 +128,34 @@ export class RegistryContract {
128
128
  public async getRewardDistributor(): Promise<EthAddress> {
129
129
  return EthAddress.fromString(await this.registry.read.getRewardDistributor());
130
130
  }
131
+
132
+ /** Returns the L1 timestamp at which the given rollup was registered via addRollup(). */
133
+ public async getCanonicalRollupRegistrationTimestamp(
134
+ rollupAddress: EthAddress,
135
+ fromBlock?: bigint,
136
+ ): Promise<bigint | undefined> {
137
+ const event = getAbiItem({ abi: RegistryAbi, name: 'CanonicalRollupUpdated' });
138
+ const start = fromBlock ?? 0n;
139
+ const latestBlock = await this.client.getBlockNumber();
140
+ const chunkSize = 1_000n;
141
+
142
+ for (let from = start; from <= latestBlock; from += chunkSize) {
143
+ const to = from + chunkSize - 1n > latestBlock ? latestBlock : from + chunkSize - 1n;
144
+ const logs = await this.client.getLogs({
145
+ address: this.address.toString(),
146
+ fromBlock: from,
147
+ toBlock: to,
148
+ strict: true,
149
+ event,
150
+ args: { instance: rollupAddress.toString() },
151
+ });
152
+
153
+ if (logs.length > 0) {
154
+ const block = await this.client.getBlock({ blockNumber: logs[0].blockNumber });
155
+ return block.timestamp;
156
+ }
157
+ }
158
+
159
+ return undefined;
160
+ }
131
161
  }
@@ -108,7 +108,7 @@ export enum AttesterStatus {
108
108
  export type FeeHeader = {
109
109
  excessMana: bigint;
110
110
  manaUsed: bigint;
111
- feeAssetPriceNumerator: bigint;
111
+ ethPerFeeAsset: bigint;
112
112
  congestionCost: bigint;
113
113
  proverCost: bigint;
114
114
  };
@@ -193,10 +193,10 @@ export type CheckpointProposedArgs = {
193
193
  checkpointNumber: CheckpointNumber;
194
194
  archive: Fr;
195
195
  versionedBlobHashes: Buffer[];
196
- /** Hash of attestations. Undefined for older events (backwards compatibility). */
197
- attestationsHash?: Buffer32;
198
- /** Digest of the payload. Undefined for older events (backwards compatibility). */
199
- payloadDigest?: Buffer32;
196
+ /** Hash of attestations emitted in the CheckpointProposed event. */
197
+ attestationsHash: Buffer32;
198
+ /** Digest of the payload emitted in the CheckpointProposed event. */
199
+ payloadDigest: Buffer32;
200
200
  };
201
201
 
202
202
  /** Log type for CheckpointProposed events. */
@@ -379,6 +379,20 @@ export class RollupContract {
379
379
  return Fr.fromString(await this.rollup.read.archiveAt([0n]));
380
380
  }
381
381
 
382
+ @memoize
383
+ async getVkTreeRoot(): Promise<Fr> {
384
+ const slot = BigInt(RollupContract.stfStorageSlot) + 3n;
385
+ const value = await this.client.getStorageAt({ address: this.address, slot: `0x${slot.toString(16)}` });
386
+ return Fr.fromString(value ?? '0x0');
387
+ }
388
+
389
+ @memoize
390
+ async getProtocolContractsHash(): Promise<Fr> {
391
+ const slot = BigInt(RollupContract.stfStorageSlot) + 4n;
392
+ const value = await this.client.getStorageAt({ address: this.address, slot: `0x${slot.toString(16)}` });
393
+ return Fr.fromString(value ?? '0x0');
394
+ }
395
+
382
396
  /**
383
397
  * Returns rollup constants used for epoch queries.
384
398
  * Return type is `L1RollupConstants` which is defined in stdlib,
@@ -391,13 +405,25 @@ export class RollupContract {
391
405
  slotDuration: number;
392
406
  epochDuration: number;
393
407
  proofSubmissionEpochs: number;
408
+ targetCommitteeSize: number;
409
+ rollupManaLimit: number;
394
410
  }> {
395
- const [l1StartBlock, l1GenesisTime, slotDuration, epochDuration, proofSubmissionEpochs] = await Promise.all([
411
+ const [
412
+ l1StartBlock,
413
+ l1GenesisTime,
414
+ slotDuration,
415
+ epochDuration,
416
+ proofSubmissionEpochs,
417
+ targetCommitteeSize,
418
+ rollupManaLimit,
419
+ ] = await Promise.all([
396
420
  this.getL1StartBlock(),
397
421
  this.getL1GenesisTime(),
398
422
  this.getSlotDuration(),
399
423
  this.getEpochDuration(),
400
424
  this.getProofSubmissionEpochs(),
425
+ this.getTargetCommitteeSize(),
426
+ this.getManaLimit(),
401
427
  ]);
402
428
  return {
403
429
  l1StartBlock,
@@ -405,6 +431,8 @@ export class RollupContract {
405
431
  slotDuration,
406
432
  epochDuration: Number(epochDuration),
407
433
  proofSubmissionEpochs: Number(proofSubmissionEpochs),
434
+ targetCommitteeSize,
435
+ rollupManaLimit: Number(rollupManaLimit),
408
436
  };
409
437
  }
410
438
 
@@ -499,8 +527,9 @@ export class RollupContract {
499
527
  return CheckpointNumber.fromBigInt(await this.rollup.read.getPendingCheckpointNumber());
500
528
  }
501
529
 
502
- async getProvenCheckpointNumber(): Promise<CheckpointNumber> {
503
- return CheckpointNumber.fromBigInt(await this.rollup.read.getProvenCheckpointNumber());
530
+ async getProvenCheckpointNumber(options?: { blockNumber?: bigint }): Promise<CheckpointNumber> {
531
+ await checkBlockTag(options?.blockNumber, this.client);
532
+ return CheckpointNumber.fromBigInt(await this.rollup.read.getProvenCheckpointNumber(options));
504
533
  }
505
534
 
506
535
  async getSlotNumber(): Promise<SlotNumber> {
@@ -515,8 +544,8 @@ export class RollupContract {
515
544
  };
516
545
  }
517
546
 
518
- getFeeAssetPerEth(): Promise<bigint> {
519
- return this.rollup.read.getFeeAssetPerEth();
547
+ getEthPerFeeAsset(): Promise<bigint> {
548
+ return this.rollup.read.getEthPerFeeAsset();
520
549
  }
521
550
 
522
551
  async getCommitteeAt(timestamp: bigint): Promise<EthAddress[] | undefined> {
@@ -601,7 +630,7 @@ export class RollupContract {
601
630
  feeHeader: {
602
631
  excessMana: result.feeHeader.excessMana,
603
632
  manaUsed: result.feeHeader.manaUsed,
604
- feeAssetPriceNumerator: result.feeHeader.feeAssetPriceNumerator,
633
+ ethPerFeeAsset: result.feeHeader.ethPerFeeAsset,
605
634
  congestionCost: result.feeHeader.congestionCost,
606
635
  proverCost: result.feeHeader.proverCost,
607
636
  },
@@ -805,6 +834,7 @@ export class RollupContract {
805
834
  ): L1TxRequest {
806
835
  return {
807
836
  to: this.address,
837
+ abi: RollupAbi,
808
838
  data: encodeFunctionData({
809
839
  abi: RollupAbi,
810
840
  functionName: 'invalidateBadAttestation',
@@ -826,6 +856,7 @@ export class RollupContract {
826
856
  ): L1TxRequest {
827
857
  return {
828
858
  to: this.address,
859
+ abi: RollupAbi,
829
860
  data: encodeFunctionData({
830
861
  abi: RollupAbi,
831
862
  functionName: 'invalidateInsufficientAttestations',
@@ -959,6 +990,7 @@ export class RollupContract {
959
990
  setupEpoch(l1TxUtils: L1TxUtils) {
960
991
  return l1TxUtils.sendAndMonitorTransaction({
961
992
  to: this.address,
993
+ abi: RollupAbi,
962
994
  data: encodeFunctionData({
963
995
  abi: RollupAbi,
964
996
  functionName: 'setupEpoch',
@@ -970,6 +1002,7 @@ export class RollupContract {
970
1002
  vote(l1TxUtils: L1TxUtils, proposalId: bigint) {
971
1003
  return l1TxUtils.sendAndMonitorTransaction({
972
1004
  to: this.address,
1005
+ abi: RollupAbi,
973
1006
  data: encodeFunctionData({
974
1007
  abi: RollupAbi,
975
1008
  functionName: 'vote',
@@ -1052,8 +1085,22 @@ export class RollupContract {
1052
1085
  checkpointNumber: CheckpointNumber.fromBigInt(log.args.checkpointNumber!),
1053
1086
  archive: Fr.fromString(log.args.archive!),
1054
1087
  versionedBlobHashes: log.args.versionedBlobHashes!.map(h => Buffer.from(h.slice(2), 'hex')),
1055
- attestationsHash: log.args.attestationsHash ? Buffer32.fromString(log.args.attestationsHash) : undefined,
1056
- payloadDigest: log.args.payloadDigest ? Buffer32.fromString(log.args.payloadDigest) : undefined,
1088
+ attestationsHash: (() => {
1089
+ if (!log.args.attestationsHash) {
1090
+ throw new Error(
1091
+ `CheckpointProposed event missing attestationsHash for checkpoint ${log.args.checkpointNumber}`,
1092
+ );
1093
+ }
1094
+ return Buffer32.fromString(log.args.attestationsHash);
1095
+ })(),
1096
+ payloadDigest: (() => {
1097
+ if (!log.args.payloadDigest) {
1098
+ throw new Error(
1099
+ `CheckpointProposed event missing payloadDigest for checkpoint ${log.args.checkpointNumber}`,
1100
+ );
1101
+ }
1102
+ return Buffer32.fromString(log.args.payloadDigest);
1103
+ })(),
1057
1104
  },
1058
1105
  }));
1059
1106
  }
@@ -1,11 +1,12 @@
1
1
  import type { L1TxRequest } from '@aztec/ethereum/l1-tx-utils';
2
2
  import type { ViemClient } from '@aztec/ethereum/types';
3
- import { tryExtractEvent } from '@aztec/ethereum/utils';
3
+ import { mergeAbis, tryExtractEvent } from '@aztec/ethereum/utils';
4
4
  import { SlotNumber } from '@aztec/foundation/branded-types';
5
5
  import { Buffer32 } from '@aztec/foundation/buffer';
6
6
  import { EthAddress } from '@aztec/foundation/eth-address';
7
7
  import { Signature } from '@aztec/foundation/eth-signature';
8
8
  import { hexToBuffer } from '@aztec/foundation/string';
9
+ import { SlasherAbi } from '@aztec/l1-artifacts/SlasherAbi';
9
10
  import { TallySlashingProposerAbi } from '@aztec/l1-artifacts/TallySlashingProposerAbi';
10
11
 
11
12
  import {
@@ -160,6 +161,7 @@ export class TallySlashingProposerContract {
160
161
 
161
162
  return {
162
163
  to: this.contract.address,
164
+ abi: TallySlashingProposerAbi,
163
165
  data: encodeFunctionData({
164
166
  abi: TallySlashingProposerAbi,
165
167
  functionName: 'vote',
@@ -207,6 +209,7 @@ export class TallySlashingProposerContract {
207
209
  public buildVoteRequestWithSignature(votes: Hex, signature: { v: number; r: Hex; s: Hex }): L1TxRequest {
208
210
  return {
209
211
  to: this.contract.address,
212
+ abi: TallySlashingProposerAbi,
210
213
  data: encodeFunctionData({
211
214
  abi: TallySlashingProposerAbi,
212
215
  functionName: 'vote',
@@ -224,6 +227,7 @@ export class TallySlashingProposerContract {
224
227
  public buildExecuteRoundRequest(round: bigint, committees: EthAddress[][]): L1TxRequest {
225
228
  return {
226
229
  to: this.contract.address,
230
+ abi: mergeAbis([TallySlashingProposerAbi, SlasherAbi]),
227
231
  data: encodeFunctionData({
228
232
  abi: TallySlashingProposerAbi,
229
233
  functionName: 'executeRound',
@@ -15,7 +15,7 @@ import { tmpdir } from 'os';
15
15
  import { dirname, join, resolve } from 'path';
16
16
  import readline from 'readline';
17
17
  import type { Hex } from 'viem';
18
- import { foundry, mainnet, sepolia } from 'viem/chains';
18
+ import { mainnet, sepolia } from 'viem/chains';
19
19
 
20
20
  import { createEthereumChain, isAnvilTestChain } from './chain.js';
21
21
  import { createExtendedL1Client } from './client.js';
@@ -23,7 +23,6 @@ import type { L1ContractsConfig } from './config.js';
23
23
  import { deployMulticall3 } from './contracts/multicall.js';
24
24
  import { RollupContract } from './contracts/rollup.js';
25
25
  import type { L1ContractAddresses } from './l1_contract_addresses.js';
26
- import type { L1TxUtilsConfig } from './l1_tx_utils/config.js';
27
26
  import type { ExtendedViemWalletClient } from './types.js';
28
27
 
29
28
  const logger = createLogger('ethereum:deploy_aztec_l1_contracts');
@@ -31,9 +30,9 @@ const logger = createLogger('ethereum:deploy_aztec_l1_contracts');
31
30
  const JSON_DEPLOY_RESULT_PREFIX = 'JSON DEPLOY RESULT:';
32
31
 
33
32
  /**
34
- * Runs a process with the given command, arguments, and environment.
35
- * If the process outputs a line starting with JSON_DEPLOY_RESULT_PREFIX,
36
- * the JSON is parsed and returned.
33
+ * Runs a process and parses JSON deploy results from stdout.
34
+ * Lines starting with JSON_DEPLOY_RESULT_PREFIX are parsed and returned.
35
+ * All other stdout goes to logger.info, stderr goes to logger.warn.
37
36
  */
38
37
  function runProcess<T>(
39
38
  command: string,
@@ -49,26 +48,41 @@ function runProcess<T>(
49
48
  });
50
49
 
51
50
  let result: T | undefined;
51
+ let parseError: Error | undefined;
52
+ let settled = false;
52
53
 
53
54
  readline.createInterface({ input: proc.stdout }).on('line', line => {
54
55
  const trimmedLine = line.trim();
55
56
  if (trimmedLine.startsWith(JSON_DEPLOY_RESULT_PREFIX)) {
56
57
  const jsonStr = trimmedLine.slice(JSON_DEPLOY_RESULT_PREFIX.length).trim();
57
- // TODO(AD): should this be a zod parse?
58
- result = JSON.parse(jsonStr);
58
+ try {
59
+ result = JSON.parse(jsonStr);
60
+ } catch {
61
+ parseError = new Error(`Failed to parse deploy result JSON: ${jsonStr.slice(0, 200)}`);
62
+ }
59
63
  } else {
60
64
  logger.info(line);
61
65
  }
62
66
  });
63
- readline.createInterface({ input: proc.stderr }).on('line', logger.error.bind(logger));
67
+ readline.createInterface({ input: proc.stderr }).on('line', logger.warn.bind(logger));
64
68
 
65
69
  proc.on('error', error => {
70
+ if (settled) {
71
+ return;
72
+ }
73
+ settled = true;
66
74
  reject(new Error(`Failed to spawn ${command}: ${error.message}`));
67
75
  });
68
76
 
69
77
  proc.on('close', code => {
78
+ if (settled) {
79
+ return;
80
+ }
81
+ settled = true;
70
82
  if (code !== 0) {
71
- reject(new Error(`${command} exited with code ${code}. See logs for details.\n`));
83
+ reject(new Error(`${command} exited with code ${code}`));
84
+ } else if (parseError) {
85
+ reject(parseError);
72
86
  } else {
73
87
  resolve(result);
74
88
  }
@@ -168,6 +182,9 @@ export function prepareL1ContractsForDeployment(): string {
168
182
  const foundryTomlPath = join(basePath, 'foundry.toml');
169
183
  let foundryToml = readFileSync(foundryTomlPath, 'utf-8');
170
184
  const solcPathMatch = foundryToml.match(/solc\s*=\s*"\.\/solc-([^"]+)"/);
185
+ // Did we find a hardcoded solc path that we need to make absolute?
186
+ // This code path happens in CI currently as we bundle solc there to avoid race conditions when
187
+ // downloading solc.
171
188
  if (solcPathMatch) {
172
189
  const solcVersion = solcPathMatch[1];
173
190
  const absoluteSolcPath = join(basePath, `solc-${solcVersion}`);
@@ -318,11 +335,8 @@ export async function deployAztecL1Contracts(
318
335
  );
319
336
  }
320
337
 
321
- // From heuristic testing. More caused issues with anvil.
322
- const MAGIC_ANVIL_BATCH_SIZE = 8;
323
- // Anvil seems to stall with unbounded batch size. Otherwise no max batch size is desirable.
338
+ const scriptPath = join(getL1ContractsPath(), 'scripts', 'forge_broadcast.js');
324
339
  const forgeArgs = [
325
- 'script',
326
340
  FORGE_SCRIPT,
327
341
  '--sig',
328
342
  'run()',
@@ -330,8 +344,6 @@ export async function deployAztecL1Contracts(
330
344
  privateKey,
331
345
  '--rpc-url',
332
346
  rpcUrl,
333
- '--broadcast',
334
- ...(chainId === foundry.id ? ['--batch-size', MAGIC_ANVIL_BATCH_SIZE.toString()] : []),
335
347
  ...(shouldVerify ? ['--verify'] : []),
336
348
  ];
337
349
  const forgeEnv = {
@@ -340,7 +352,12 @@ export async function deployAztecL1Contracts(
340
352
  FOUNDRY_PROFILE: chainId === mainnet.id ? 'production' : undefined,
341
353
  ...getDeployAztecL1ContractsEnvVars(args),
342
354
  };
343
- const result = await runProcess<ForgeL1ContractsDeployResult>('forge', forgeArgs, forgeEnv, l1ContractsPath);
355
+ const result = await runProcess<ForgeL1ContractsDeployResult>(
356
+ process.execPath,
357
+ [scriptPath, ...forgeArgs],
358
+ forgeEnv,
359
+ l1ContractsPath,
360
+ );
344
361
  if (!result) {
345
362
  throw new Error('Forge script did not output deployment result');
346
363
  }
@@ -473,7 +490,25 @@ export type VerificationRecord = {
473
490
  libraries: VerificationLibraryEntry[];
474
491
  };
475
492
 
476
- export interface DeployAztecL1ContractsArgs extends Omit<L1ContractsConfig, keyof L1TxUtilsConfig> {
493
+ export interface DeployAztecL1ContractsArgs
494
+ extends Omit<
495
+ L1ContractsConfig,
496
+ | 'gasLimitBufferPercentage'
497
+ | 'maxGwei'
498
+ | 'maxBlobGwei'
499
+ | 'priorityFeeBumpPercentage'
500
+ | 'priorityFeeRetryBumpPercentage'
501
+ | 'minimumPriorityFeePerGas'
502
+ | 'maxSpeedUpAttempts'
503
+ | 'checkIntervalMs'
504
+ | 'stallTimeMs'
505
+ | 'txTimeoutMs'
506
+ | 'cancelTxOnTimeout'
507
+ | 'txCancellationFinalTimeoutMs'
508
+ | 'txUnseenConsideredDroppedMs'
509
+ | 'enableDelayer'
510
+ | 'txDelayerMaxInclusionTimeIntoSlot'
511
+ > {
477
512
  /** The vk tree root. */
478
513
  vkTreeRoot: Fr;
479
514
  /** The hash of the protocol contracts. */
@@ -549,6 +584,7 @@ export function getDeployRollupForUpgradeEnvVars(
549
584
  AZTEC_MANA_TARGET: args.manaTarget.toString(),
550
585
  AZTEC_EXIT_DELAY_SECONDS: args.exitDelaySeconds.toString(),
551
586
  AZTEC_PROVING_COST_PER_MANA: args.provingCostPerMana.toString(),
587
+ AZTEC_INITIAL_ETH_PER_FEE_ASSET: args.initialEthPerFeeAsset.toString(),
552
588
  AZTEC_SLASHER_FLAVOR: args.slasherFlavor,
553
589
  AZTEC_SLASHING_ROUND_SIZE_IN_EPOCHS: args.slashingRoundSizeInEpochs.toString(),
554
590
  AZTEC_SLASHING_QUORUM: args.slashingQuorum?.toString(),
@@ -582,17 +618,8 @@ export const deployRollupForUpgrade = async (
582
618
  const FORGE_SCRIPT = 'script/deploy/DeployRollupForUpgrade.s.sol';
583
619
  await maybeForgeForceProductionBuild(l1ContractsPath, FORGE_SCRIPT, chainId);
584
620
 
585
- const forgeArgs = [
586
- 'script',
587
- FORGE_SCRIPT,
588
- '--sig',
589
- 'run()',
590
- '--private-key',
591
- privateKey,
592
- '--rpc-url',
593
- rpcUrl,
594
- '--broadcast',
595
- ];
621
+ const scriptPath = join(getL1ContractsPath(), 'scripts', 'forge_broadcast.js');
622
+ const forgeArgs = [FORGE_SCRIPT, '--sig', 'run()', '--private-key', privateKey, '--rpc-url', rpcUrl];
596
623
  const forgeEnv = {
597
624
  FOUNDRY_PROFILE: chainId === mainnet.id ? 'production' : undefined,
598
625
  // Env vars required by l1-contracts/script/deploy/RollupConfiguration.sol.
@@ -601,7 +628,12 @@ export const deployRollupForUpgrade = async (
601
628
  ...getDeployRollupForUpgradeEnvVars(args),
602
629
  };
603
630
 
604
- const result = await runProcess<ForgeRollupUpgradeResult>('forge', forgeArgs, forgeEnv, l1ContractsPath);
631
+ const result = await runProcess<ForgeRollupUpgradeResult>(
632
+ process.execPath,
633
+ [scriptPath, ...forgeArgs],
634
+ forgeEnv,
635
+ l1ContractsPath,
636
+ );
605
637
  if (!result) {
606
638
  throw new Error('Forge script did not output deployment result');
607
639
  }
@@ -24,7 +24,7 @@ import {
24
24
  } from './deploy_aztec_l1_contracts.js';
25
25
  import { RegisterNewRollupVersionPayloadArtifact } from './l1_artifacts.js';
26
26
  import { type L1TxUtilsConfig, getL1TxUtilsConfigEnvVars } from './l1_tx_utils/config.js';
27
- import { createL1TxUtilsFromViemWallet } from './l1_tx_utils/factory.js';
27
+ import { createL1TxUtils } from './l1_tx_utils/factory.js';
28
28
  import type { L1TxUtils } from './l1_tx_utils/l1_tx_utils.js';
29
29
  import type { GasPrice, L1TxConfig, L1TxRequest } from './l1_tx_utils/types.js';
30
30
  import type { ExtendedViemWalletClient } from './types.js';
@@ -46,7 +46,7 @@ export class L1Deployer {
46
46
  private createVerificationJson: boolean = false,
47
47
  ) {
48
48
  this.salt = maybeSalt ? padHex(numberToHex(maybeSalt), { size: 32 }) : undefined;
49
- this.l1TxUtils = createL1TxUtilsFromViemWallet(
49
+ this.l1TxUtils = createL1TxUtils(
50
50
  this.client,
51
51
  { logger: this.logger, dateProvider },
52
52
  { ...this.txUtilsConfig, debugMaxGasLimit: acceleratedTestDeployments },
@@ -179,7 +179,7 @@ export async function deployL1Contract(
179
179
 
180
180
  if (!l1TxUtils) {
181
181
  const config = getL1TxUtilsConfigEnvVars();
182
- l1TxUtils = createL1TxUtilsFromViemWallet(
182
+ l1TxUtils = createL1TxUtils(
183
183
  extendedClient,
184
184
  { logger },
185
185
  { ...config, debugMaxGasLimit: acceleratedTestDeployments },
@@ -4,11 +4,11 @@
4
4
  /** Default L1 contracts configuration values from network-defaults.yml */
5
5
  export const l1ContractsDefaultEnv = {
6
6
  ETHEREUM_SLOT_DURATION: 12,
7
- AZTEC_SLOT_DURATION: 36,
7
+ AZTEC_SLOT_DURATION: 72,
8
8
  AZTEC_EPOCH_DURATION: 32,
9
9
  AZTEC_TARGET_COMMITTEE_SIZE: 48,
10
10
  AZTEC_LAG_IN_EPOCHS_FOR_VALIDATOR_SET: 2,
11
- AZTEC_LAG_IN_EPOCHS_FOR_RANDAO: 2,
11
+ AZTEC_LAG_IN_EPOCHS_FOR_RANDAO: 1,
12
12
  AZTEC_ACTIVATION_THRESHOLD: 100000000000000000000,
13
13
  AZTEC_EJECTION_THRESHOLD: 50000000000000000000,
14
14
  AZTEC_LOCAL_EJECTION_THRESHOLD: 98000000000000000000,
@@ -17,6 +17,7 @@ export const l1ContractsDefaultEnv = {
17
17
  AZTEC_PROOF_SUBMISSION_EPOCHS: 1,
18
18
  AZTEC_MANA_TARGET: 100000000,
19
19
  AZTEC_PROVING_COST_PER_MANA: 100,
20
+ AZTEC_INITIAL_ETH_PER_FEE_ASSET: 10000000,
20
21
  AZTEC_SLASHER_FLAVOR: 'tally',
21
22
  AZTEC_SLASHING_ROUND_SIZE_IN_EPOCHS: 4,
22
23
  AZTEC_SLASHING_LIFETIME_IN_ROUNDS: 5,
@@ -5,6 +5,7 @@ import {
5
5
  getConfigFromMappings,
6
6
  getDefaultConfig,
7
7
  numberConfigHelper,
8
+ optionalNumberConfigHelper,
8
9
  } from '@aztec/foundation/config';
9
10
 
10
11
  export interface L1TxUtilsConfig {
@@ -60,6 +61,12 @@ export interface L1TxUtilsConfig {
60
61
  * How long a tx nonce can be unseen in the mempool before considering it dropped
61
62
  */
62
63
  txUnseenConsideredDroppedMs?: number;
64
+ /** Enable tx delayer. When true, wraps the viem client to intercept and delay txs. Test-only. */
65
+ enableDelayer?: boolean;
66
+ /** Max seconds into an L1 slot for tx inclusion. Txs sent later are deferred to next slot. Only used when enableDelayer is true. */
67
+ txDelayerMaxInclusionTimeIntoSlot?: number;
68
+ /** How many seconds an L1 slot lasts. */
69
+ ethereumSlotDuration?: number;
63
70
  }
64
71
 
65
72
  export const l1TxUtilsConfigMappings: ConfigMappingsType<L1TxUtilsConfig> = {
@@ -142,6 +149,19 @@ export const l1TxUtilsConfigMappings: ConfigMappingsType<L1TxUtilsConfig> = {
142
149
  env: 'L1_TX_MONITOR_TX_UNSEEN_CONSIDERED_DROPPED_MS',
143
150
  ...numberConfigHelper(6 * 12 * 1000), // 6 L1 blocks
144
151
  },
152
+ enableDelayer: {
153
+ description: 'Enable tx delayer for testing.',
154
+ ...booleanConfigHelper(false),
155
+ },
156
+ txDelayerMaxInclusionTimeIntoSlot: {
157
+ description: 'Max seconds into L1 slot for tx inclusion when delayer is enabled.',
158
+ ...optionalNumberConfigHelper(),
159
+ },
160
+ ethereumSlotDuration: {
161
+ env: 'ETHEREUM_SLOT_DURATION',
162
+ description: 'How many seconds an L1 slot lasts.',
163
+ ...numberConfigHelper(12),
164
+ },
145
165
  };
146
166
 
147
167
  // We abuse the fact that all mappings above have a non null default value and force-type this to Required
@@ -3,8 +3,8 @@
3
3
  // 1_000_000_000_000_000_000 Wei = 1 ETH
4
4
  export const WEI_CONST = 1_000_000_000n;
5
5
 
6
- // @note using this large gas limit to avoid the issue of `gas limit too low` when estimating gas in reth
7
- export const LARGE_GAS_LIMIT = 12_000_000n;
6
+ // EIP-7825: protocol-level cap on tx gas limit (2^24). Clients reject above this.
7
+ export const MAX_L1_TX_LIMIT = 16_777_216n;
8
8
 
9
9
  // setting a minimum bump percentage to 10% due to geth's implementation
10
10
  // https://github.com/ethereum/go-ethereum/blob/e3d61e6db028c412f74bc4d4c7e117a9e29d0de0/core/txpool/legacypool/list.go#L298
@@ -1,64 +1,64 @@
1
+ import type { BlobKzgInstance } from '@aztec/blob-lib/types';
1
2
  import { EthAddress } from '@aztec/foundation/eth-address';
2
3
  import type { Logger } from '@aztec/foundation/log';
3
4
  import { DateProvider } from '@aztec/foundation/timer';
4
5
 
5
- import type { TransactionSerializable } from 'viem';
6
-
7
6
  import type { EthSigner } from '../eth-signer/eth-signer.js';
8
7
  import type { ExtendedViemWalletClient, ViemClient } from '../types.js';
9
8
  import type { L1TxUtilsConfig } from './config.js';
10
9
  import type { IL1TxMetrics, IL1TxStore } from './interfaces.js';
11
10
  import { L1TxUtils } from './l1_tx_utils.js';
12
11
  import { createViemSigner } from './signer.js';
12
+ import { Delayer } from './tx_delayer.js';
13
13
  import type { SigningCallback } from './types.js';
14
14
 
15
- export function createL1TxUtilsFromViemWallet(
16
- client: ExtendedViemWalletClient,
17
- deps?: {
18
- logger?: Logger;
19
- dateProvider?: DateProvider;
20
- store?: IL1TxStore;
21
- metrics?: IL1TxMetrics;
22
- },
23
- config?: Partial<L1TxUtilsConfig> & { debugMaxGasLimit?: boolean },
24
- ): L1TxUtils {
25
- return new L1TxUtils(
15
+ /** Source of signing capability: either a wallet client or a separate client + signer. */
16
+ export type L1SignerSource = ExtendedViemWalletClient | { client: ViemClient; signer: EthSigner };
17
+
18
+ export function resolveSignerSource(source: L1SignerSource): {
19
+ client: ViemClient;
20
+ address: EthAddress;
21
+ signingCallback: SigningCallback;
22
+ } {
23
+ if ('account' in source && source.account) {
24
+ return {
25
+ client: source as ExtendedViemWalletClient,
26
+ address: EthAddress.fromString((source as ExtendedViemWalletClient).account.address),
27
+ signingCallback: createViemSigner(source as ExtendedViemWalletClient),
28
+ };
29
+ }
30
+ const { client, signer } = source as { client: ViemClient; signer: EthSigner };
31
+ return {
26
32
  client,
27
- EthAddress.fromString(client.account.address),
28
- createViemSigner(client),
29
- deps?.logger,
30
- deps?.dateProvider,
31
- config,
32
- config?.debugMaxGasLimit ?? false,
33
- deps?.store,
34
- deps?.metrics,
35
- );
33
+ address: signer.address,
34
+ signingCallback: async (tx, _addr) => (await signer.signTransaction(tx)).toViemTransactionSignature(),
35
+ };
36
36
  }
37
37
 
38
- export function createL1TxUtilsFromEthSigner(
39
- client: ViemClient,
40
- signer: EthSigner,
38
+ export function createL1TxUtils(
39
+ source: L1SignerSource,
41
40
  deps?: {
42
41
  logger?: Logger;
43
42
  dateProvider?: DateProvider;
44
43
  store?: IL1TxStore;
45
44
  metrics?: IL1TxMetrics;
45
+ kzg?: BlobKzgInstance;
46
+ delayer?: Delayer;
46
47
  },
47
48
  config?: Partial<L1TxUtilsConfig> & { debugMaxGasLimit?: boolean },
48
49
  ): L1TxUtils {
49
- const callback: SigningCallback = async (transaction: TransactionSerializable, _signingAddress) => {
50
- return (await signer.signTransaction(transaction)).toViemTransactionSignature();
51
- };
52
-
50
+ const { client, address, signingCallback } = resolveSignerSource(source);
53
51
  return new L1TxUtils(
54
52
  client,
55
- signer.address,
56
- callback,
53
+ address,
54
+ signingCallback,
57
55
  deps?.logger,
58
56
  deps?.dateProvider,
59
57
  config,
60
58
  config?.debugMaxGasLimit ?? false,
61
59
  deps?.store,
62
60
  deps?.metrics,
61
+ deps?.kzg,
62
+ deps?.delayer,
63
63
  );
64
64
  }
@@ -134,7 +134,7 @@ export const P75AllTxsPriorityFeeStrategy: PriorityFeeStrategy = {
134
134
  // Sanity check: cap competitive fee at 100x network estimate to avoid using unrealistic fees
135
135
  const maxReasonableFee = networkEstimate * 100n;
136
136
  if (competitiveFee > maxReasonableFee && networkEstimate > 0n) {
137
- logger?.warn('Competitive fee exceeds sanity cap, using capped value', {
137
+ logger?.debug('Competitive fee exceeds sanity cap, using capped value', {
138
138
  competitiveFee: formatGwei(competitiveFee),
139
139
  networkEstimate: formatGwei(networkEstimate),
140
140
  cappedTo: formatGwei(maxReasonableFee),
@@ -207,7 +207,7 @@ export const P75BlobTxsOnlyPriorityFeeStrategy: PriorityFeeStrategy = {
207
207
 
208
208
  // Debug: Log suspicious fees from history
209
209
  if (medianHistoricalFee > 100n * WEI_CONST) {
210
- logger?.warn('Suspicious high fee in history', {
210
+ logger?.debug('Suspicious high fee in history', {
211
211
  historicalMedian: formatGwei(medianHistoricalFee),
212
212
  allP75Fees: percentile75Fees.map(f => formatGwei(f)),
213
213
  });