@aztec/ethereum 2.1.0-rc.9 → 2.1.1-rc.1

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.
@@ -276,9 +276,22 @@ export const deploySharedContracts = async (
276
276
  feeAssetAddress = deployedFee.address;
277
277
  logger.verbose(`Deployed Fee Asset at ${feeAssetAddress}`);
278
278
 
279
+ // Mint a tiny bit of tokens to satisfy coin-issuer constraints
280
+ const { txHash } = await deployer.sendTransaction({
281
+ to: feeAssetAddress.toString(),
282
+ data: encodeFunctionData({
283
+ abi: FeeAssetArtifact.contractAbi,
284
+ functionName: 'mint',
285
+ args: [l1Client.account.address, 1n * 10n ** 18n],
286
+ }),
287
+ });
288
+ logger.verbose(`Minted tiny bit of tokens to satisfy coin-issuer constraints in ${txHash}`);
289
+
279
290
  const deployedStaking = await deployer.deploy(StakingAssetArtifact, ['Staking', 'STK', l1Client.account.address]);
280
291
  stakingAssetAddress = deployedStaking.address;
281
292
  logger.verbose(`Deployed Staking Asset at ${stakingAssetAddress}`);
293
+
294
+ await deployer.waitForDeployments();
282
295
  }
283
296
 
284
297
  const gseAddress = (
@@ -350,11 +363,15 @@ export const deploySharedContracts = async (
350
363
  }
351
364
 
352
365
  const coinIssuerAddress = (
353
- await deployer.deploy(CoinIssuerArtifact, [
354
- feeAssetAddress.toString(),
355
- (25_000_000_000n * 10n ** 18n) / (60n * 60n * 24n * 365n),
356
- l1Client.account.address,
357
- ])
366
+ await deployer.deploy(
367
+ CoinIssuerArtifact,
368
+ [
369
+ feeAssetAddress.toString(),
370
+ 2n * 10n ** 17n, // hard cap of 20% per year
371
+ l1Client.account.address,
372
+ ],
373
+ { gasLimit: 1_000_000n },
374
+ )
358
375
  ).address;
359
376
  logger.verbose(`Deployed CoinIssuer at ${coinIssuerAddress}`);
360
377
 
@@ -514,6 +531,199 @@ const getZkPassportScopes = (args: DeployL1ContractsArgs): [string, string] => {
514
531
  return [domain, scope];
515
532
  };
516
533
 
534
+ /**
535
+ * Generates verification records for a deployed rollup and its associated contracts (Inbox, Outbox, Slasher, etc).
536
+ * @param rollup - The deployed rollup contract.
537
+ * @param deployer - The L1 deployer instance.
538
+ * @param args - The deployment arguments used for the rollup.
539
+ * @param addresses - The L1 contract addresses.
540
+ * @param extendedClient - The extended viem wallet client.
541
+ * @param logger - The logger.
542
+ */
543
+ async function generateRollupVerificationRecords(
544
+ rollup: RollupContract,
545
+ deployer: L1Deployer,
546
+ args: {
547
+ slashingVetoer: EthAddress;
548
+ slashingRoundSizeInEpochs: number;
549
+ aztecEpochDuration: number;
550
+ slashingQuorum?: number;
551
+ slashingLifetimeInRounds: number;
552
+ slashingExecutionDelayInRounds: number;
553
+ slasherFlavor: 'none' | 'tally' | 'empire';
554
+ slashAmountSmall: bigint;
555
+ slashAmountMedium: bigint;
556
+ slashAmountLarge: bigint;
557
+ aztecTargetCommitteeSize: number;
558
+ slashingOffsetInRounds: number;
559
+ },
560
+ addresses: Pick<L1ContractAddresses, 'feeJuiceAddress'>,
561
+ extendedClient: ExtendedViemWalletClient,
562
+ logger: Logger,
563
+ ): Promise<void> {
564
+ try {
565
+ // Add Inbox / Outbox verification records (constructor args are created inside RollupCore)
566
+ const rollupAddr = rollup.address;
567
+ const rollupAddresses = await rollup.getRollupAddresses();
568
+ const inboxAddr = rollupAddresses.inboxAddress.toString();
569
+ const outboxAddr = rollupAddresses.outboxAddress.toString();
570
+ const feeAsset = rollupAddresses.feeJuiceAddress.toString();
571
+ const version = await rollup.getVersion();
572
+
573
+ const inboxCtor = encodeAbiParameters(
574
+ [{ type: 'address' }, { type: 'address' }, { type: 'uint256' }, { type: 'uint256' }],
575
+ [rollupAddr, feeAsset, version, BigInt(L1_TO_L2_MSG_SUBTREE_HEIGHT)],
576
+ );
577
+
578
+ const outboxCtor = encodeAbiParameters([{ type: 'address' }, { type: 'uint256' }], [rollupAddr, version]);
579
+
580
+ deployer.verificationRecords.push(
581
+ { name: 'Inbox', address: inboxAddr, constructorArgsHex: inboxCtor, libraries: [] },
582
+ { name: 'Outbox', address: outboxAddr, constructorArgsHex: outboxCtor, libraries: [] },
583
+ );
584
+
585
+ // Include Slasher and SlashingProposer (if deployed) in verification data
586
+ try {
587
+ const slasherAddrHex = await rollup.getSlasherAddress();
588
+ const slasherAddr = EthAddress.fromString(slasherAddrHex);
589
+ if (!slasherAddr.isZero()) {
590
+ // Slasher constructor: (address _vetoer, address _governance)
591
+ const slasherCtor = encodeAbiParameters(
592
+ [{ type: 'address' }, { type: 'address' }],
593
+ [args.slashingVetoer.toString(), extendedClient.account.address],
594
+ );
595
+ deployer.verificationRecords.push({
596
+ name: 'Slasher',
597
+ address: slasherAddr.toString(),
598
+ constructorArgsHex: slasherCtor,
599
+ libraries: [],
600
+ });
601
+
602
+ // Proposer address is stored in Slasher.PROPOSER()
603
+ const proposerAddr = (await rollup.getSlashingProposerAddress()).toString();
604
+
605
+ // Compute constructor args matching deployment path in RollupCore
606
+ const computedRoundSize = BigInt(args.slashingRoundSizeInEpochs * args.aztecEpochDuration);
607
+ const computedQuorum = BigInt(
608
+ args.slashingQuorum ?? (args.slashingRoundSizeInEpochs * args.aztecEpochDuration) / 2 + 1,
609
+ );
610
+ const lifetimeInRounds = BigInt(args.slashingLifetimeInRounds);
611
+ const executionDelayInRounds = BigInt(args.slashingExecutionDelayInRounds);
612
+
613
+ if (args.slasherFlavor === 'tally') {
614
+ const slashAmounts: readonly [bigint, bigint, bigint] = [
615
+ args.slashAmountSmall,
616
+ args.slashAmountMedium,
617
+ args.slashAmountLarge,
618
+ ];
619
+ const committeeSize = BigInt(args.aztecTargetCommitteeSize);
620
+ const epochDuration = BigInt(args.aztecEpochDuration);
621
+ const slashOffsetInRounds = BigInt(args.slashingOffsetInRounds);
622
+
623
+ const proposerCtor = encodeAbiParameters(
624
+ [
625
+ { type: 'address' },
626
+ { type: 'address' },
627
+ { type: 'uint256' },
628
+ { type: 'uint256' },
629
+ { type: 'uint256' },
630
+ { type: 'uint256' },
631
+ { type: 'uint256[3]' },
632
+ { type: 'uint256' },
633
+ { type: 'uint256' },
634
+ { type: 'uint256' },
635
+ ],
636
+ [
637
+ rollup.address,
638
+ slasherAddr.toString(),
639
+ computedQuorum,
640
+ computedRoundSize,
641
+ lifetimeInRounds,
642
+ executionDelayInRounds,
643
+ slashAmounts,
644
+ committeeSize,
645
+ epochDuration,
646
+ slashOffsetInRounds,
647
+ ],
648
+ );
649
+
650
+ deployer.verificationRecords.push({
651
+ name: 'TallySlashingProposer',
652
+ address: proposerAddr,
653
+ constructorArgsHex: proposerCtor,
654
+ libraries: [],
655
+ });
656
+ } else if (args.slasherFlavor === 'empire') {
657
+ const proposerCtor = encodeAbiParameters(
658
+ [
659
+ { type: 'address' },
660
+ { type: 'address' },
661
+ { type: 'uint256' },
662
+ { type: 'uint256' },
663
+ { type: 'uint256' },
664
+ { type: 'uint256' },
665
+ ],
666
+ [
667
+ rollup.address,
668
+ slasherAddr.toString(),
669
+ computedQuorum,
670
+ computedRoundSize,
671
+ lifetimeInRounds,
672
+ executionDelayInRounds,
673
+ ],
674
+ );
675
+
676
+ deployer.verificationRecords.push({
677
+ name: 'EmpireSlashingProposer',
678
+ address: proposerAddr,
679
+ constructorArgsHex: proposerCtor,
680
+ libraries: [],
681
+ });
682
+ }
683
+ }
684
+ } catch (e) {
685
+ logger.warn(`Failed to add Slasher/Proposer verification records: ${String(e)}`);
686
+ }
687
+ } catch (e) {
688
+ throw new Error(`Failed to generate rollup verification records: ${String(e)}`);
689
+ }
690
+ }
691
+
692
+ /**
693
+ * Writes verification records to a JSON file for later forge verify.
694
+ * @param deployer - The L1 deployer containing verification records.
695
+ * @param outputDirectory - The directory to write the verification file to.
696
+ * @param chainId - The chain ID.
697
+ * @param filenameSuffix - Optional suffix for the filename (e.g., 'upgrade').
698
+ * @param logger - The logger.
699
+ */
700
+ async function writeVerificationJson(
701
+ deployer: L1Deployer,
702
+ outputDirectory: string,
703
+ chainId: number,
704
+ filenameSuffix: string = '',
705
+ logger: Logger,
706
+ ): Promise<void> {
707
+ try {
708
+ const date = new Date();
709
+ const formattedDate = date.toISOString().slice(2, 19).replace(/[-T:]/g, '');
710
+ // Ensure the verification output directory exists
711
+ await mkdir(outputDirectory, { recursive: true });
712
+ const suffix = filenameSuffix ? `-${filenameSuffix}` : '';
713
+ const verificationOutputPath = `${outputDirectory}/l1-verify${suffix}-${chainId}-${formattedDate.slice(0, 6)}-${formattedDate.slice(6)}.json`;
714
+ const networkName = getActiveNetworkName();
715
+ const verificationData = {
716
+ chainId: chainId,
717
+ network: networkName,
718
+ records: deployer.verificationRecords,
719
+ };
720
+ await writeFile(verificationOutputPath, JSON.stringify(verificationData, null, 2));
721
+ logger.info(`Wrote L1 verification data to ${verificationOutputPath}`);
722
+ } catch (e) {
723
+ logger.warn(`Failed to write L1 verification data file: ${String(e)}`);
724
+ }
725
+ }
726
+
517
727
  /**
518
728
  * Deploys a new rollup, using the existing canonical version to derive certain values (addresses of assets etc).
519
729
  * @param clients - The L1 clients.
@@ -521,6 +731,7 @@ const getZkPassportScopes = (args: DeployL1ContractsArgs): [string, string] => {
521
731
  * @param registryAddress - The address of the registry.
522
732
  * @param logger - The logger.
523
733
  * @param txUtilsConfig - The L1 tx utils config.
734
+ * @param createVerificationJson - Optional path to write verification data for forge verify.
524
735
  */
525
736
  export const deployRollupForUpgrade = async (
526
737
  extendedClient: ExtendedViemWalletClient,
@@ -531,6 +742,7 @@ export const deployRollupForUpgrade = async (
531
742
  registryAddress: EthAddress,
532
743
  logger: Logger,
533
744
  txUtilsConfig: L1TxUtilsConfig,
745
+ createVerificationJson: string | false = false,
534
746
  ) => {
535
747
  const deployer = new L1Deployer(
536
748
  extendedClient,
@@ -539,6 +751,7 @@ export const deployRollupForUpgrade = async (
539
751
  args.acceleratedTestDeployments,
540
752
  logger,
541
753
  txUtilsConfig,
754
+ !!createVerificationJson,
542
755
  );
543
756
 
544
757
  const addresses = await RegistryContract.collectAddresses(extendedClient, registryAddress, 'canonical');
@@ -547,6 +760,12 @@ export const deployRollupForUpgrade = async (
547
760
 
548
761
  await deployer.waitForDeployments();
549
762
 
763
+ // Write verification data (constructor args + linked libraries) to file for later forge verify
764
+ if (createVerificationJson) {
765
+ await generateRollupVerificationRecords(rollup, deployer, args, addresses, extendedClient, logger);
766
+ await writeVerificationJson(deployer, createVerificationJson, extendedClient.chain.id, 'upgrade', logger);
767
+ }
768
+
550
769
  return { rollup, slashFactoryAddress };
551
770
  };
552
771
 
@@ -653,6 +872,7 @@ export const deployRollup = async (
653
872
  slashAmounts: [args.slashAmountSmall, args.slashAmountMedium, args.slashAmountLarge],
654
873
  localEjectionThreshold: args.localEjectionThreshold,
655
874
  slashingDisableDuration: BigInt(args.slashingDisableDuration ?? 0n),
875
+ earliestRewardsClaimableTimestamp: 0n,
656
876
  };
657
877
 
658
878
  const genesisStateArgs = {
@@ -1057,7 +1277,7 @@ export const addMultipleValidators = async (
1057
1277
  data: encodeFunctionData({
1058
1278
  abi: MultiAdderArtifact.contractAbi,
1059
1279
  functionName: 'addValidators',
1060
- args: [c, BigInt(chunkSize)],
1280
+ args: [c, BigInt(0)],
1061
1281
  }),
1062
1282
  },
1063
1283
  {
@@ -1076,15 +1296,18 @@ export const addMultipleValidators = async (
1076
1296
  while (true) {
1077
1297
  // If the queue is empty, we can break
1078
1298
  if ((await rollup.getEntryQueueLength()) == 0n) {
1299
+ logger.debug('Entry queue is empty, stopping flush attempts');
1079
1300
  break;
1080
1301
  }
1081
1302
 
1082
1303
  // If there are no available validator flushes, no need to even try
1083
1304
  if ((await rollup.getAvailableValidatorFlushes()) == 0n) {
1305
+ logger.debug('No available validator flushes, stopping flush attempts');
1084
1306
  break;
1085
1307
  }
1086
1308
 
1087
1309
  // Note that we are flushing at most `chunkSize` at each call
1310
+ logger.debug(`Flushing entry queue for ${chunkSize} validators`);
1088
1311
  await deployer.l1TxUtils.sendAndMonitorTransaction(
1089
1312
  {
1090
1313
  to: rollup.address,
@@ -1277,144 +1500,8 @@ export const deployL1Contracts = async (
1277
1500
 
1278
1501
  // Write verification data (constructor args + linked libraries) to file for later forge verify
1279
1502
  if (createVerificationJson) {
1280
- try {
1281
- // Add Inbox / Outbox verification records (constructor args are created inside RollupCore)
1282
- const rollupAddr = l1Contracts.rollupAddress.toString();
1283
- const inboxAddr = l1Contracts.inboxAddress.toString();
1284
- const outboxAddr = l1Contracts.outboxAddress.toString();
1285
- const feeAsset = l1Contracts.feeJuiceAddress.toString();
1286
- const version = await rollup.getVersion();
1287
-
1288
- const inboxCtor = encodeAbiParameters(
1289
- [{ type: 'address' }, { type: 'address' }, { type: 'uint256' }, { type: 'uint256' }],
1290
- [rollupAddr, feeAsset, version, BigInt(L1_TO_L2_MSG_SUBTREE_HEIGHT)],
1291
- );
1292
-
1293
- const outboxCtor = encodeAbiParameters([{ type: 'address' }, { type: 'uint256' }], [rollupAddr, version]);
1294
-
1295
- deployer.verificationRecords.push(
1296
- { name: 'Inbox', address: inboxAddr, constructorArgsHex: inboxCtor, libraries: [] },
1297
- { name: 'Outbox', address: outboxAddr, constructorArgsHex: outboxCtor, libraries: [] },
1298
- );
1299
-
1300
- // Include Slasher and SlashingProposer (if deployed) in verification data
1301
- try {
1302
- const slasherAddrHex = await rollup.getSlasherAddress();
1303
- const slasherAddr = EthAddress.fromString(slasherAddrHex);
1304
- if (!slasherAddr.isZero()) {
1305
- // Slasher constructor: (address _vetoer, address _governance)
1306
- const slasherCtor = encodeAbiParameters(
1307
- [{ type: 'address' }, { type: 'address' }],
1308
- [args.slashingVetoer.toString(), l1Client.account.address],
1309
- );
1310
- deployer.verificationRecords.push({
1311
- name: 'Slasher',
1312
- address: slasherAddr.toString(),
1313
- constructorArgsHex: slasherCtor,
1314
- libraries: [],
1315
- });
1316
-
1317
- // Proposer address is stored in Slasher.PROPOSER()
1318
- const proposerAddr = (await rollup.getSlashingProposerAddress()).toString();
1319
-
1320
- // Compute constructor args matching deployment path in RollupCore
1321
- const computedRoundSize = BigInt(args.slashingRoundSizeInEpochs * args.aztecEpochDuration);
1322
- const computedQuorum = BigInt(
1323
- args.slashingQuorum ?? (args.slashingRoundSizeInEpochs * args.aztecEpochDuration) / 2 + 1,
1324
- );
1325
- const lifetimeInRounds = BigInt(args.slashingLifetimeInRounds);
1326
- const executionDelayInRounds = BigInt(args.slashingExecutionDelayInRounds);
1327
-
1328
- if (args.slasherFlavor === 'tally') {
1329
- const slashAmounts: readonly [bigint, bigint, bigint] = [
1330
- args.slashAmountSmall,
1331
- args.slashAmountMedium,
1332
- args.slashAmountLarge,
1333
- ];
1334
- const committeeSize = BigInt(args.aztecTargetCommitteeSize);
1335
- const epochDuration = BigInt(args.aztecEpochDuration);
1336
- const slashOffsetInRounds = BigInt(args.slashingOffsetInRounds);
1337
-
1338
- const proposerCtor = encodeAbiParameters(
1339
- [
1340
- { type: 'address' },
1341
- { type: 'address' },
1342
- { type: 'uint256' },
1343
- { type: 'uint256' },
1344
- { type: 'uint256' },
1345
- { type: 'uint256' },
1346
- { type: 'uint256[3]' },
1347
- { type: 'uint256' },
1348
- { type: 'uint256' },
1349
- { type: 'uint256' },
1350
- ],
1351
- [
1352
- rollup.address,
1353
- slasherAddr.toString(),
1354
- computedQuorum,
1355
- computedRoundSize,
1356
- lifetimeInRounds,
1357
- executionDelayInRounds,
1358
- slashAmounts,
1359
- committeeSize,
1360
- epochDuration,
1361
- slashOffsetInRounds,
1362
- ],
1363
- );
1364
-
1365
- deployer.verificationRecords.push({
1366
- name: 'TallySlashingProposer',
1367
- address: proposerAddr,
1368
- constructorArgsHex: proposerCtor,
1369
- libraries: [],
1370
- });
1371
- } else if (args.slasherFlavor === 'empire') {
1372
- const proposerCtor = encodeAbiParameters(
1373
- [
1374
- { type: 'address' },
1375
- { type: 'address' },
1376
- { type: 'uint256' },
1377
- { type: 'uint256' },
1378
- { type: 'uint256' },
1379
- { type: 'uint256' },
1380
- ],
1381
- [
1382
- rollup.address,
1383
- slasherAddr.toString(),
1384
- computedQuorum,
1385
- computedRoundSize,
1386
- lifetimeInRounds,
1387
- executionDelayInRounds,
1388
- ],
1389
- );
1390
-
1391
- deployer.verificationRecords.push({
1392
- name: 'EmpireSlashingProposer',
1393
- address: proposerAddr,
1394
- constructorArgsHex: proposerCtor,
1395
- libraries: [],
1396
- });
1397
- }
1398
- }
1399
- } catch (e) {
1400
- logger.warn(`Failed to add Slasher/Proposer verification records: ${String(e)}`);
1401
- }
1402
- const date = new Date();
1403
- const formattedDate = date.toISOString().slice(2, 19).replace(/[-T:]/g, '');
1404
- // Ensure the verification output directory exists
1405
- await mkdir(createVerificationJson, { recursive: true });
1406
- const verificationOutputPath = `${createVerificationJson}/l1-verify-${chain.id}-${formattedDate.slice(0, 6)}-${formattedDate.slice(6)}.json`;
1407
- const networkName = getActiveNetworkName();
1408
- const verificationData = {
1409
- chainId: chain.id,
1410
- network: networkName,
1411
- records: deployer.verificationRecords,
1412
- };
1413
- await writeFile(verificationOutputPath, JSON.stringify(verificationData, null, 2));
1414
- logger.info(`Wrote L1 verification data to ${verificationOutputPath}`);
1415
- } catch (e) {
1416
- logger.warn(`Failed to write L1 verification data file: ${String(e)}`);
1417
- }
1503
+ await generateRollupVerificationRecords(rollup, deployer, args, l1Contracts, l1Client, logger);
1504
+ await writeVerificationJson(deployer, createVerificationJson, chain.id, '', logger);
1418
1505
  }
1419
1506
 
1420
1507
  if (isAnvilTestChain(chain.id)) {
@@ -33,10 +33,10 @@ import {
33
33
  RegisterNewRollupVersionPayloadBytecode,
34
34
  RegistryAbi,
35
35
  RegistryBytecode,
36
- RewardDeploymentExtLibAbi,
37
- RewardDeploymentExtLibBytecode,
38
36
  RewardDistributorAbi,
39
37
  RewardDistributorBytecode,
38
+ RewardExtLibAbi,
39
+ RewardExtLibBytecode,
40
40
  RollupAbi,
41
41
  RollupBytecode,
42
42
  RollupLinkReferences,
@@ -102,10 +102,10 @@ export const RollupArtifact = {
102
102
  contractAbi: ValidatorOperationsExtLibAbi,
103
103
  contractBytecode: ValidatorOperationsExtLibBytecode as Hex,
104
104
  },
105
- RewardDeploymentExtLib: {
106
- name: 'RewardDeploymentExtLib',
107
- contractAbi: RewardDeploymentExtLibAbi,
108
- contractBytecode: RewardDeploymentExtLibBytecode as Hex,
105
+ RewardExtLib: {
106
+ name: 'RewardExtLib',
107
+ contractAbi: RewardExtLibAbi,
108
+ contractBytecode: RewardExtLibBytecode as Hex,
109
109
  },
110
110
  TallySlasherDeploymentExtLib: {
111
111
  name: 'TallySlasherDeploymentExtLib',
@@ -69,14 +69,14 @@ 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
+ ...bigintConfigHelper(2000n),
75
75
  },
76
76
  maxBlobGwei: {
77
77
  description: 'Maximum blob fee per gas in gwei',
78
78
  env: 'L1_BLOB_FEE_PER_GAS_MAX',
79
- ...bigintConfigHelper(1_500n),
79
+ ...bigintConfigHelper(3000n),
80
80
  },
81
81
  priorityFeeBumpPercentage: {
82
82
  description: 'How much to increase priority fee by each attempt (percentage)',
@@ -106,7 +106,7 @@ export const l1TxUtilsConfigMappings: ConfigMappingsType<L1TxUtilsConfig> = {
106
106
  stallTimeMs: {
107
107
  description: 'How long before considering tx stalled',
108
108
  env: 'L1_TX_MONITOR_STALL_TIME_MS',
109
- ...numberConfigHelper(24_000), // 24s, 2 ethereum slots
109
+ ...numberConfigHelper(12_000), // 12s, 1 ethereum slot
110
110
  },
111
111
  txTimeoutMs: {
112
112
  description: 'How long to wait for a tx to be mined before giving up. Set to 0 to disable.',