@aztec/ethereum 2.0.0-nightly.20250903 → 3.0.0-canary.a9708bd

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 (41) hide show
  1. package/dest/config.d.ts.map +1 -1
  2. package/dest/config.js +61 -54
  3. package/dest/contracts/fee_asset_handler.d.ts +3 -2
  4. package/dest/contracts/fee_asset_handler.d.ts.map +1 -1
  5. package/dest/contracts/fee_asset_handler.js +14 -0
  6. package/dest/contracts/fee_juice.d.ts +2 -1
  7. package/dest/contracts/fee_juice.d.ts.map +1 -1
  8. package/dest/contracts/fee_juice.js +6 -0
  9. package/dest/contracts/governance.d.ts +1 -1
  10. package/dest/contracts/governance.d.ts.map +1 -1
  11. package/dest/contracts/governance.js +3 -0
  12. package/dest/contracts/governance_proposer.d.ts +1 -1
  13. package/dest/contracts/governance_proposer.d.ts.map +1 -1
  14. package/dest/contracts/governance_proposer.js +3 -0
  15. package/dest/contracts/gse.d.ts +2 -0
  16. package/dest/contracts/gse.d.ts.map +1 -1
  17. package/dest/contracts/gse.js +6 -0
  18. package/dest/deploy_l1_contracts.d.ts +2 -2
  19. package/dest/deploy_l1_contracts.d.ts.map +1 -1
  20. package/dest/deploy_l1_contracts.js +133 -53
  21. package/dest/l1_artifacts.d.ts +39210 -39196
  22. package/dest/l1_artifacts.d.ts.map +1 -1
  23. package/dest/test/eth_cheat_codes.d.ts +3 -2
  24. package/dest/test/eth_cheat_codes.d.ts.map +1 -1
  25. package/dest/test/eth_cheat_codes.js +11 -0
  26. package/dest/test/rollup_cheat_codes.d.ts.map +1 -1
  27. package/dest/test/rollup_cheat_codes.js +2 -1
  28. package/dest/test/tx_delayer.d.ts +2 -2
  29. package/dest/test/tx_delayer.d.ts.map +1 -1
  30. package/dest/test/tx_delayer.js +4 -4
  31. package/package.json +5 -5
  32. package/src/config.ts +81 -72
  33. package/src/contracts/fee_asset_handler.ts +17 -2
  34. package/src/contracts/fee_juice.ts +8 -1
  35. package/src/contracts/governance.ts +4 -1
  36. package/src/contracts/governance_proposer.ts +4 -1
  37. package/src/contracts/gse.ts +8 -0
  38. package/src/deploy_l1_contracts.ts +151 -50
  39. package/src/test/eth_cheat_codes.ts +12 -2
  40. package/src/test/rollup_cheat_codes.ts +5 -1
  41. package/src/test/tx_delayer.ts +14 -4
package/src/config.ts CHANGED
@@ -423,78 +423,7 @@ export function validateConfig(config: Omit<L1ContractsConfig, keyof L1TxUtilsCo
423
423
 
424
424
  // TallySlashingProposer constructor validations
425
425
  if (config.slasherFlavor === 'tally') {
426
- // From: require(SLASH_OFFSET_IN_ROUNDS > 0, Errors.TallySlashingProposer__SlashOffsetMustBeGreaterThanZero(...));
427
- if (config.slashingOffsetInRounds <= 0) {
428
- errors.push(`slashingOffsetInRounds (${config.slashingOffsetInRounds}) must be greater than 0`);
429
- }
430
-
431
- // From: require(ROUND_SIZE_IN_EPOCHS * _epochDuration == ROUND_SIZE, Errors.TallySlashingProposer__RoundSizeMustBeMultipleOfEpochDuration(...));
432
- const roundSizeInSlots = config.slashingRoundSizeInEpochs * config.aztecEpochDuration;
433
-
434
- // From: require(QUORUM > 0, Errors.TallySlashingProposer__QuorumMustBeGreaterThanZero());
435
- if (slashingQuorum !== undefined && slashingQuorum <= 0) {
436
- errors.push(`slashingQuorum (${slashingQuorum}) must be greater than 0`);
437
- }
438
-
439
- // From: require(ROUND_SIZE > 1, Errors.TallySlashingProposer__InvalidQuorumAndRoundSize(QUORUM, ROUND_SIZE));
440
- if (roundSizeInSlots <= 1) {
441
- errors.push(`slashing round size in slots (${roundSizeInSlots}) must be greater than 1`);
442
- }
443
-
444
- // From: require(_slashAmounts[0] <= _slashAmounts[1], Errors.TallySlashingProposer__InvalidSlashAmounts(_slashAmounts));
445
- if (config.slashAmountSmall > config.slashAmountMedium) {
446
- errors.push(
447
- `slashAmountSmall (${config.slashAmountSmall}) must be less than or equal to slashAmountMedium (${config.slashAmountMedium})`,
448
- );
449
- }
450
-
451
- // From: require(_slashAmounts[1] <= _slashAmounts[2], Errors.TallySlashingProposer__InvalidSlashAmounts(_slashAmounts));
452
- if (config.slashAmountMedium > config.slashAmountLarge) {
453
- errors.push(
454
- `slashAmountMedium (${config.slashAmountMedium}) must be less than or equal to slashAmountLarge (${config.slashAmountLarge})`,
455
- );
456
- }
457
-
458
- // From: require(LIFETIME_IN_ROUNDS < ROUNDABOUT_SIZE, Errors.TallySlashingProposer__LifetimeMustBeLessThanRoundabout(...));
459
- const ROUNDABOUT_SIZE = 128; // Constant from TallySlashingProposer
460
- if (config.slashingLifetimeInRounds >= ROUNDABOUT_SIZE) {
461
- errors.push(`slashingLifetimeInRounds (${config.slashingLifetimeInRounds}) must be less than ${ROUNDABOUT_SIZE}`);
462
- }
463
-
464
- // From: require(ROUND_SIZE_IN_EPOCHS > 0, Errors.TallySlashingProposer__RoundSizeInEpochsMustBeGreaterThanZero(...));
465
- if (config.slashingRoundSizeInEpochs <= 0) {
466
- errors.push(`slashingRoundSizeInEpochs (${config.slashingRoundSizeInEpochs}) must be greater than 0`);
467
- }
468
-
469
- // From: require(ROUND_SIZE < MAX_ROUND_SIZE, Errors.TallySlashingProposer__RoundSizeTooLarge(ROUND_SIZE, MAX_ROUND_SIZE));
470
- const MAX_ROUND_SIZE = 1024; // Constant from TallySlashingProposer
471
- if (roundSizeInSlots >= MAX_ROUND_SIZE) {
472
- errors.push(`slashing round size in slots (${roundSizeInSlots}) must be less than ${MAX_ROUND_SIZE}`);
473
- }
474
-
475
- // From: require(COMMITTEE_SIZE > 0, Errors.TallySlashingProposer__CommitteeSizeMustBeGreaterThanZero(COMMITTEE_SIZE));
476
- if (config.aztecTargetCommitteeSize <= 0) {
477
- errors.push(`aztecTargetCommitteeSize (${config.aztecTargetCommitteeSize}) must be greater than 0`);
478
- }
479
-
480
- // From: require(voteSize <= 128, Errors.TallySlashingProposer__VoteSizeTooBig(voteSize, 128));
481
- // voteSize = COMMITTEE_SIZE * ROUND_SIZE_IN_EPOCHS / 4
482
- const voteSize = (config.aztecTargetCommitteeSize * config.slashingRoundSizeInEpochs) / 4;
483
- if (voteSize > 128) {
484
- errors.push(`vote size (${voteSize}) must be <= 128 (committee size * round size in epochs / 4)`);
485
- }
486
-
487
- // From: require(COMMITTEE_SIZE * ROUND_SIZE_IN_EPOCHS % 4 == 0, Errors.TallySlashingProposer__InvalidCommitteeAndRoundSize(...));
488
- if ((config.aztecTargetCommitteeSize * config.slashingRoundSizeInEpochs) % 4 !== 0) {
489
- errors.push(
490
- `aztecTargetCommitteeSize * slashingRoundSizeInEpochs (${config.aztecTargetCommitteeSize * config.slashingRoundSizeInEpochs}) must be divisible by 4`,
491
- );
492
- }
493
-
494
- // Slashing offset validation: should be positive to allow proper slashing timing
495
- if (config.slashingOffsetInRounds < 0) {
496
- errors.push('slashingOffsetInRounds cannot be negative');
497
- }
426
+ validateTallySlasherConfig(config, errors);
498
427
  }
499
428
 
500
429
  // Epoch and slot duration validations
@@ -541,3 +470,83 @@ export function validateConfig(config: Omit<L1ContractsConfig, keyof L1TxUtilsCo
541
470
  );
542
471
  }
543
472
  }
473
+
474
+ function validateTallySlasherConfig(config: L1ContractsConfig, errors: string[]) {
475
+ if (config.slasherFlavor !== 'tally') {
476
+ return;
477
+ }
478
+
479
+ // From: require(SLASH_OFFSET_IN_ROUNDS > 0, Errors.TallySlashingProposer__SlashOffsetMustBeGreaterThanZero(...));
480
+ if (config.slashingOffsetInRounds <= 0) {
481
+ errors.push(`slashingOffsetInRounds (${config.slashingOffsetInRounds}) must be greater than 0`);
482
+ }
483
+
484
+ // From: require(ROUND_SIZE_IN_EPOCHS * _epochDuration == ROUND_SIZE, Errors.TallySlashingProposer__RoundSizeMustBeMultipleOfEpochDuration(...));
485
+ const roundSizeInSlots = config.slashingRoundSizeInEpochs * config.aztecEpochDuration;
486
+
487
+ // From: require(QUORUM > 0, Errors.TallySlashingProposer__QuorumMustBeGreaterThanZero());
488
+ const { slashingQuorum } = config;
489
+ if (slashingQuorum !== undefined && slashingQuorum <= 0) {
490
+ errors.push(`slashingQuorum (${slashingQuorum}) must be greater than 0`);
491
+ }
492
+
493
+ // From: require(ROUND_SIZE > 1, Errors.TallySlashingProposer__InvalidQuorumAndRoundSize(QUORUM, ROUND_SIZE));
494
+ if (roundSizeInSlots <= 1) {
495
+ errors.push(`slashing round size in slots (${roundSizeInSlots}) must be greater than 1`);
496
+ }
497
+
498
+ // From: require(_slashAmounts[0] <= _slashAmounts[1], Errors.TallySlashingProposer__InvalidSlashAmounts(_slashAmounts));
499
+ if (config.slashAmountSmall > config.slashAmountMedium) {
500
+ errors.push(
501
+ `slashAmountSmall (${config.slashAmountSmall}) must be less than or equal to slashAmountMedium (${config.slashAmountMedium})`,
502
+ );
503
+ }
504
+
505
+ // From: require(_slashAmounts[1] <= _slashAmounts[2], Errors.TallySlashingProposer__InvalidSlashAmounts(_slashAmounts));
506
+ if (config.slashAmountMedium > config.slashAmountLarge) {
507
+ errors.push(
508
+ `slashAmountMedium (${config.slashAmountMedium}) must be less than or equal to slashAmountLarge (${config.slashAmountLarge})`,
509
+ );
510
+ }
511
+
512
+ // From: require(LIFETIME_IN_ROUNDS < ROUNDABOUT_SIZE, Errors.TallySlashingProposer__LifetimeMustBeLessThanRoundabout(...));
513
+ const ROUNDABOUT_SIZE = 128; // Constant from TallySlashingProposer
514
+ if (config.slashingLifetimeInRounds >= ROUNDABOUT_SIZE) {
515
+ errors.push(`slashingLifetimeInRounds (${config.slashingLifetimeInRounds}) must be less than ${ROUNDABOUT_SIZE}`);
516
+ }
517
+
518
+ // From: require(ROUND_SIZE_IN_EPOCHS > 0, Errors.TallySlashingProposer__RoundSizeInEpochsMustBeGreaterThanZero(...));
519
+ if (config.slashingRoundSizeInEpochs <= 0) {
520
+ errors.push(`slashingRoundSizeInEpochs (${config.slashingRoundSizeInEpochs}) must be greater than 0`);
521
+ }
522
+
523
+ // From: require(ROUND_SIZE < MAX_ROUND_SIZE, Errors.TallySlashingProposer__RoundSizeTooLarge(ROUND_SIZE, MAX_ROUND_SIZE));
524
+ const MAX_ROUND_SIZE = 1024; // Constant from TallySlashingProposer
525
+ if (roundSizeInSlots >= MAX_ROUND_SIZE) {
526
+ errors.push(`slashing round size in slots (${roundSizeInSlots}) must be less than ${MAX_ROUND_SIZE}`);
527
+ }
528
+
529
+ // From: require(COMMITTEE_SIZE > 0, Errors.TallySlashingProposer__CommitteeSizeMustBeGreaterThanZero(COMMITTEE_SIZE));
530
+ if (config.aztecTargetCommitteeSize <= 0) {
531
+ errors.push(`aztecTargetCommitteeSize (${config.aztecTargetCommitteeSize}) must be greater than 0`);
532
+ }
533
+
534
+ // From: require(voteSize <= 128, Errors.TallySlashingProposer__VoteSizeTooBig(voteSize, 128));
535
+ // voteSize = COMMITTEE_SIZE * ROUND_SIZE_IN_EPOCHS / 4
536
+ const voteSize = (config.aztecTargetCommitteeSize * config.slashingRoundSizeInEpochs) / 4;
537
+ if (voteSize > 128) {
538
+ errors.push(`vote size (${voteSize}) must be <= 128 (committee size * round size in epochs / 4)`);
539
+ }
540
+
541
+ // From: require(COMMITTEE_SIZE * ROUND_SIZE_IN_EPOCHS % 4 == 0, Errors.TallySlashingProposer__InvalidCommitteeAndRoundSize(...));
542
+ if ((config.aztecTargetCommitteeSize * config.slashingRoundSizeInEpochs) % 4 !== 0) {
543
+ errors.push(
544
+ `aztecTargetCommitteeSize * slashingRoundSizeInEpochs (${config.aztecTargetCommitteeSize * config.slashingRoundSizeInEpochs}) must be divisible by 4`,
545
+ );
546
+ }
547
+
548
+ // Slashing offset validation: should be positive to allow proper slashing timing
549
+ if (config.slashingOffsetInRounds < 0) {
550
+ errors.push('slashingOffsetInRounds cannot be negative');
551
+ }
552
+ }
@@ -9,12 +9,24 @@ export class FeeAssetHandlerContract {
9
9
  public address: EthAddress;
10
10
 
11
11
  constructor(
12
- address: Hex,
12
+ address: Hex | EthAddress,
13
13
  public readonly txUtils: L1TxUtils,
14
14
  ) {
15
+ if (address instanceof EthAddress) {
16
+ address = address.toString();
17
+ }
15
18
  this.address = EthAddress.fromString(address);
16
19
  }
17
20
 
21
+ public async getOwner(): Promise<EthAddress> {
22
+ const contract = getContract({
23
+ abi: FeeAssetHandlerAbi,
24
+ address: this.address.toString(),
25
+ client: this.txUtils.client,
26
+ });
27
+ return EthAddress.fromString(await contract.read.owner());
28
+ }
29
+
18
30
  public getMintAmount() {
19
31
  const contract = getContract({
20
32
  abi: FeeAssetHandlerAbi,
@@ -24,7 +36,10 @@ export class FeeAssetHandlerContract {
24
36
  return contract.read.mintAmount();
25
37
  }
26
38
 
27
- public mint(recipient: Hex) {
39
+ public mint(recipient: Hex | EthAddress) {
40
+ if (recipient instanceof EthAddress) {
41
+ recipient = recipient.toString();
42
+ }
28
43
  return this.txUtils.sendAndMonitorTransaction({
29
44
  to: this.address.toString(),
30
45
  data: encodeFunctionData({
@@ -9,9 +9,12 @@ export class FeeJuiceContract {
9
9
  private readonly feeJuiceContract: GetContractReturnType<typeof FeeJuiceAbi, ViemClient>;
10
10
 
11
11
  constructor(
12
- address: Hex,
12
+ address: Hex | EthAddress,
13
13
  public readonly client: ViemClient,
14
14
  ) {
15
+ if (address instanceof EthAddress) {
16
+ address = address.toString();
17
+ }
15
18
  this.feeJuiceContract = getContract({ address, abi: FeeJuiceAbi, client });
16
19
  }
17
20
 
@@ -19,6 +22,10 @@ export class FeeJuiceContract {
19
22
  return EthAddress.fromString(this.feeJuiceContract.address);
20
23
  }
21
24
 
25
+ public async getOwner(): Promise<EthAddress> {
26
+ return EthAddress.fromString(await this.feeJuiceContract.read.owner());
27
+ }
28
+
22
29
  private assertWalletFeeJuice(): GetContractReturnType<typeof FeeJuiceAbi, ExtendedViemWalletClient> {
23
30
  if (!isExtendedClient(this.client)) {
24
31
  throw new Error('Wallet client is required for this operation');
@@ -136,9 +136,12 @@ export class GovernanceContract extends ReadOnlyGovernanceContract {
136
136
  protected override readonly governanceContract: GetContractReturnType<typeof GovernanceAbi, ExtendedViemWalletClient>;
137
137
 
138
138
  constructor(
139
- address: Hex,
139
+ address: Hex | EthAddress,
140
140
  public override readonly client: ExtendedViemWalletClient,
141
141
  ) {
142
+ if (address instanceof EthAddress) {
143
+ address = address.toString();
144
+ }
142
145
  super(address, client);
143
146
  if (!isExtendedClient(client)) {
144
147
  throw new Error('GovernanceContract has to be instantiated with a wallet client.');
@@ -21,8 +21,11 @@ export class GovernanceProposerContract implements IEmpireBase {
21
21
 
22
22
  constructor(
23
23
  public readonly client: ViemClient,
24
- address: Hex,
24
+ address: Hex | EthAddress,
25
25
  ) {
26
+ if (address instanceof EthAddress) {
27
+ address = address.toString();
28
+ }
26
29
  this.proposer = getContract({ address, abi: GovernanceProposerAbi, client });
27
30
  }
28
31
 
@@ -39,6 +39,14 @@ export class GSEContract {
39
39
  this.gse = getContract({ address, abi: GSEAbi, client });
40
40
  }
41
41
 
42
+ public async getOwner(): Promise<EthAddress> {
43
+ return EthAddress.fromString(await this.gse.read.owner());
44
+ }
45
+
46
+ public async getGovernance(): Promise<EthAddress> {
47
+ return EthAddress.fromString(await this.gse.read.getGovernance());
48
+ }
49
+
42
50
  getAttestersFromIndicesAtTime(instance: Hex | EthAddress, ts: bigint, indices: bigint[]) {
43
51
  if (instance instanceof EthAddress) {
44
52
  instance = instance.toString();
@@ -263,37 +263,14 @@ export const deploySharedContracts = async (
263
263
 
264
264
  const coinIssuerAddress = await deployer.deploy(CoinIssuerArtifact, [
265
265
  feeAssetAddress.toString(),
266
- 1n * 10n ** 18n, // @todo #8084
267
- governanceAddress.toString(),
266
+ 1_000_000n * 10n ** 18n, // @todo #8084
267
+ l1Client.account.address,
268
268
  ]);
269
269
  logger.verbose(`Deployed CoinIssuer at ${coinIssuerAddress}`);
270
270
 
271
- const feeAsset = getContract({
272
- address: feeAssetAddress.toString(),
273
- abi: FeeAssetArtifact.contractAbi,
274
- client: l1Client,
275
- });
276
-
277
271
  logger.verbose(`Waiting for deployments to complete`);
278
272
  await deployer.waitForDeployments();
279
273
 
280
- if (args.acceleratedTestDeployments || !(await feeAsset.read.minters([coinIssuerAddress.toString()]))) {
281
- const { txHash } = await deployer.sendTransaction(
282
- {
283
- to: feeAssetAddress.toString(),
284
- data: encodeFunctionData({
285
- abi: FeeAssetArtifact.contractAbi,
286
- functionName: 'addMinter',
287
- args: [coinIssuerAddress.toString()],
288
- }),
289
- },
290
- { gasLimit: 100_000n },
291
- );
292
-
293
- logger.verbose(`Added coin issuer ${coinIssuerAddress} as minter on fee asset in ${txHash}`);
294
- txHashes.push(txHash);
295
- }
296
-
297
274
  // Registry ownership will be transferred to governance later, after rollup is added
298
275
 
299
276
  let feeAssetHandlerAddress: EthAddress | undefined = undefined;
@@ -309,20 +286,23 @@ export const deploySharedContracts = async (
309
286
  feeAssetHandlerAddress = await deployer.deploy(FeeAssetHandlerArtifact, [
310
287
  l1Client.account.address,
311
288
  feeAssetAddress.toString(),
312
- BigInt(1e18),
289
+ BigInt(1000n * 10n ** 18n),
313
290
  ]);
314
291
  logger.verbose(`Deployed FeeAssetHandler at ${feeAssetHandlerAddress}`);
315
292
 
316
- const { txHash } = await deployer.sendTransaction({
317
- to: feeAssetAddress.toString(),
318
- data: encodeFunctionData({
319
- abi: FeeAssetArtifact.contractAbi,
320
- functionName: 'addMinter',
321
- args: [feeAssetHandlerAddress.toString()],
322
- }),
323
- });
324
- logger.verbose(`Added fee asset handler ${feeAssetHandlerAddress} as minter on fee asset in ${txHash}`);
325
- txHashes.push(txHash);
293
+ // Only if we are "fresh" will we be adding as a minter, otherwise above will simply get same address
294
+ if (needToSetGovernance) {
295
+ const { txHash } = await deployer.sendTransaction({
296
+ to: feeAssetAddress.toString(),
297
+ data: encodeFunctionData({
298
+ abi: FeeAssetArtifact.contractAbi,
299
+ functionName: 'addMinter',
300
+ args: [feeAssetHandlerAddress.toString()],
301
+ }),
302
+ });
303
+ logger.verbose(`Added fee asset handler ${feeAssetHandlerAddress} as minter on fee asset in ${txHash}`);
304
+ txHashes.push(txHash);
305
+ }
326
306
 
327
307
  // Only if on sepolia will we deploy the staking asset handler
328
308
  // Should not be deployed to devnet since it would cause caos with sequencers there etc.
@@ -517,7 +497,12 @@ export const deployRollup = async (
517
497
  >,
518
498
  addresses: Pick<
519
499
  L1ContractAddresses,
520
- 'feeJuiceAddress' | 'registryAddress' | 'rewardDistributorAddress' | 'stakingAssetAddress' | 'gseAddress'
500
+ | 'feeJuiceAddress'
501
+ | 'registryAddress'
502
+ | 'rewardDistributorAddress'
503
+ | 'stakingAssetAddress'
504
+ | 'gseAddress'
505
+ | 'governanceAddress'
521
506
  >,
522
507
  logger: Logger,
523
508
  ) => {
@@ -695,6 +680,24 @@ export const deployRollup = async (
695
680
  );
696
681
  }
697
682
 
683
+ // If the owner is not the Governance contract, transfer ownership to the Governance contract
684
+ logger.verbose(addresses.governanceAddress.toString());
685
+ if (getAddress(await rollupContract.getOwner()) !== getAddress(addresses.governanceAddress.toString())) {
686
+ // TODO(md): add send transaction to the deployer such that we do not need to manage tx hashes here
687
+ const { txHash: transferOwnershipTxHash } = await deployer.sendTransaction({
688
+ to: rollupContract.address,
689
+ data: encodeFunctionData({
690
+ abi: RegistryArtifact.contractAbi,
691
+ functionName: 'transferOwnership',
692
+ args: [getAddress(addresses.governanceAddress.toString())],
693
+ }),
694
+ });
695
+ logger.verbose(
696
+ `Transferring the ownership of the rollup contract at ${rollupContract.address} to the Governance ${addresses.governanceAddress} in tx ${transferOwnershipTxHash}`,
697
+ );
698
+ txHashes.push(transferOwnershipTxHash);
699
+ }
700
+
698
701
  await deployer.waitForDeployments();
699
702
  await Promise.all(txHashes.map(txHash => extendedClient.waitForTransactionReceipt({ hash: txHash })));
700
703
  logger.verbose(`Rollup deployed`);
@@ -707,6 +710,8 @@ export const handoverToGovernance = async (
707
710
  deployer: L1Deployer,
708
711
  registryAddress: EthAddress,
709
712
  gseAddress: EthAddress,
713
+ coinIssuerAddress: EthAddress,
714
+ feeAssetAddress: EthAddress,
710
715
  governanceAddress: EthAddress,
711
716
  logger: Logger,
712
717
  acceleratedTestDeployments: boolean | undefined,
@@ -724,6 +729,18 @@ export const handoverToGovernance = async (
724
729
  client: extendedClient,
725
730
  });
726
731
 
732
+ const coinIssuerContract = getContract({
733
+ address: getAddress(coinIssuerAddress.toString()),
734
+ abi: CoinIssuerArtifact.contractAbi,
735
+ client: extendedClient,
736
+ });
737
+
738
+ const feeAsset = getContract({
739
+ address: getAddress(feeAssetAddress.toString()),
740
+ abi: FeeAssetArtifact.contractAbi,
741
+ client: extendedClient,
742
+ });
743
+
727
744
  const txHashes: Hex[] = [];
728
745
 
729
746
  // If the owner is not the Governance contract, transfer ownership to the Governance contract
@@ -763,6 +780,54 @@ export const handoverToGovernance = async (
763
780
  txHashes.push(transferOwnershipTxHash);
764
781
  }
765
782
 
783
+ if (acceleratedTestDeployments || (await feeAsset.read.owner()) !== coinIssuerAddress.toString()) {
784
+ const { txHash } = await deployer.sendTransaction(
785
+ {
786
+ to: feeAssetAddress.toString(),
787
+ data: encodeFunctionData({
788
+ abi: FeeAssetArtifact.contractAbi,
789
+ functionName: 'transferOwnership',
790
+ args: [coinIssuerAddress.toString()],
791
+ }),
792
+ },
793
+ { gasLimit: 500_000n },
794
+ );
795
+ logger.verbose(`Transfer ownership of fee asset to coin issuer ${coinIssuerAddress} in ${txHash}`);
796
+ txHashes.push(txHash);
797
+
798
+ const { txHash: acceptTokenOwnershipTxHash } = await deployer.sendTransaction(
799
+ {
800
+ to: coinIssuerAddress.toString(),
801
+ data: encodeFunctionData({
802
+ abi: CoinIssuerArtifact.contractAbi,
803
+ functionName: 'acceptTokenOwnership',
804
+ }),
805
+ },
806
+ { gasLimit: 500_000n },
807
+ );
808
+ logger.verbose(`Accept ownership of fee asset in ${acceptTokenOwnershipTxHash}`);
809
+ txHashes.push(acceptTokenOwnershipTxHash);
810
+ }
811
+
812
+ // If the owner is not the Governance contract, transfer ownership to the Governance contract
813
+ if (
814
+ acceleratedTestDeployments ||
815
+ (await coinIssuerContract.read.owner()) !== getAddress(governanceAddress.toString())
816
+ ) {
817
+ const { txHash: transferOwnershipTxHash } = await deployer.sendTransaction({
818
+ to: coinIssuerContract.address,
819
+ data: encodeFunctionData({
820
+ abi: CoinIssuerArtifact.contractAbi,
821
+ functionName: 'transferOwnership',
822
+ args: [getAddress(governanceAddress.toString())],
823
+ }),
824
+ });
825
+ logger.verbose(
826
+ `Transferring the ownership of the coin issuer contract at ${coinIssuerAddress} to the Governance ${governanceAddress} in tx ${transferOwnershipTxHash}`,
827
+ );
828
+ txHashes.push(transferOwnershipTxHash);
829
+ }
830
+
766
831
  // Wait for all actions to be mined
767
832
  await deployer.waitForDeployments();
768
833
  await Promise.all(txHashes.map(txHash => extendedClient.waitForTransactionReceipt({ hash: txHash })));
@@ -844,19 +909,51 @@ export const addMultipleValidators = async (
844
909
 
845
910
  logger.info(`Adding ${validators.length} validators to the rollup`);
846
911
 
847
- await deployer.l1TxUtils.sendAndMonitorTransaction(
848
- {
849
- to: multiAdder.toString(),
850
- data: encodeFunctionData({
851
- abi: MultiAdderArtifact.contractAbi,
852
- functionName: 'addValidators',
853
- args: [validatorsTuples],
854
- }),
855
- },
856
- {
857
- gasLimit: 45_000_000n,
858
- },
859
- );
912
+ // Adding to the queue and flushing need to be done in two transactions
913
+ // if we are adding many validators.
914
+ if (validatorsTuples.length > 10) {
915
+ await deployer.l1TxUtils.sendAndMonitorTransaction(
916
+ {
917
+ to: multiAdder.toString(),
918
+ data: encodeFunctionData({
919
+ abi: MultiAdderArtifact.contractAbi,
920
+ functionName: 'addValidators',
921
+ args: [validatorsTuples, true],
922
+ }),
923
+ },
924
+ {
925
+ gasLimit: 40_000_000n,
926
+ },
927
+ );
928
+
929
+ await deployer.l1TxUtils.sendAndMonitorTransaction(
930
+ {
931
+ to: rollupAddress,
932
+ data: encodeFunctionData({
933
+ abi: RollupArtifact.contractAbi,
934
+ functionName: 'flushEntryQueue',
935
+ args: [],
936
+ }),
937
+ },
938
+ {
939
+ gasLimit: 40_000_000n,
940
+ },
941
+ );
942
+ } else {
943
+ await deployer.l1TxUtils.sendAndMonitorTransaction(
944
+ {
945
+ to: multiAdder.toString(),
946
+ data: encodeFunctionData({
947
+ abi: MultiAdderArtifact.contractAbi,
948
+ functionName: 'addValidators',
949
+ args: [validatorsTuples, false],
950
+ }),
951
+ },
952
+ {
953
+ gasLimit: 45_000_000n,
954
+ },
955
+ );
956
+ }
860
957
 
861
958
  const entryQueueLengthAfter = await rollup.getEntryQueueLength();
862
959
  const validatorCountAfter = await rollup.getActiveAttesterCount();
@@ -933,6 +1030,7 @@ export const deployL1Contracts = async (
933
1030
  txUtilsConfig: L1TxUtilsConfig = getL1TxUtilsConfigEnvVars(),
934
1031
  createVerificationJson: string | false = false,
935
1032
  ): Promise<DeployL1ContractsReturnType> => {
1033
+ logger.info(`Deploying L1 contracts with config: ${jsonStringify(args)}`);
936
1034
  validateConfig(args);
937
1035
 
938
1036
  const l1Client = createExtendedL1Client(rpcUrls, account, chain);
@@ -995,6 +1093,7 @@ export const deployL1Contracts = async (
995
1093
  gseAddress,
996
1094
  rewardDistributorAddress,
997
1095
  stakingAssetAddress,
1096
+ governanceAddress,
998
1097
  },
999
1098
  logger,
1000
1099
  );
@@ -1008,6 +1107,8 @@ export const deployL1Contracts = async (
1008
1107
  deployer,
1009
1108
  registryAddress,
1010
1109
  gseAddress,
1110
+ coinIssuerAddress,
1111
+ feeAssetAddress,
1011
1112
  governanceAddress,
1012
1113
  logger,
1013
1114
  args.acceleratedTestDeployments,
@@ -1,6 +1,6 @@
1
1
  import { toBigIntBE, toHex } from '@aztec/foundation/bigint-buffer';
2
2
  import { keccak256 } from '@aztec/foundation/crypto';
3
- import type { EthAddress } from '@aztec/foundation/eth-address';
3
+ import { EthAddress } from '@aztec/foundation/eth-address';
4
4
  import { jsonStringify } from '@aztec/foundation/json-rpc';
5
5
  import { createLogger } from '@aztec/foundation/log';
6
6
  import type { TestDateProvider } from '@aztec/foundation/timer';
@@ -112,7 +112,7 @@ export class EthCheatCodes {
112
112
  * @param account - The account to set the balance for
113
113
  * @param balance - The balance to set
114
114
  */
115
- public async setBalance(account: EthAddress, balance: bigint): Promise<void> {
115
+ public async setBalance(account: EthAddress | Hex, balance: bigint): Promise<void> {
116
116
  try {
117
117
  await this.rpcCall('anvil_setBalance', [account.toString(), toHex(balance)]);
118
118
  } catch (err) {
@@ -121,6 +121,11 @@ export class EthCheatCodes {
121
121
  this.logger.warn(`Set balance for ${account} to ${balance}`);
122
122
  }
123
123
 
124
+ public async getBalance(account: EthAddress | Hex): Promise<bigint> {
125
+ const res = await this.rpcCall('eth_getBalance', [account.toString(), 'latest']);
126
+ return BigInt(res);
127
+ }
128
+
124
129
  /**
125
130
  * Set the interval between successive blocks (block time). This does NOT enable interval mining.
126
131
  * @param interval - The interval to use between blocks
@@ -300,6 +305,11 @@ export class EthCheatCodes {
300
305
  */
301
306
  public async startImpersonating(who: EthAddress | Hex): Promise<void> {
302
307
  try {
308
+ // Since the `who` impersonated will sometimes be a contract without funds, we fund it if needed.
309
+ if ((await this.getBalance(who)) === 0n) {
310
+ await this.setBalance(who, 10n * 10n ** 18n);
311
+ }
312
+
303
313
  await this.rpcCall('hardhat_impersonateAccount', [who.toString()]);
304
314
  } catch (err) {
305
315
  throw new Error(`Error impersonating ${who}: ${err}`);
@@ -258,7 +258,11 @@ export class RollupCheatCodes {
258
258
  */
259
259
  public async setProvingCostPerMana(ethValue: bigint) {
260
260
  await this.asOwner(async (account, rollup) => {
261
- const hash = await rollup.write.setProvingCostPerMana([ethValue], { account, chain: this.client.chain });
261
+ const hash = await rollup.write.setProvingCostPerMana([ethValue], {
262
+ account,
263
+ chain: this.client.chain,
264
+ gasLimit: 1000000n,
265
+ });
262
266
  await this.client.waitForTransactionReceipt({ hash });
263
267
  this.logger.warn(`Updated proving cost per mana to ${ethValue}`);
264
268
  });
@@ -18,7 +18,12 @@ import {
18
18
 
19
19
  import { type ViemClient, isExtendedClient } from '../types.js';
20
20
 
21
- export function waitUntilBlock<T extends Client>(client: T, blockNumber: number | bigint, logger?: Logger) {
21
+ export function waitUntilBlock<T extends Client>(
22
+ client: T,
23
+ blockNumber: number | bigint,
24
+ logger?: Logger,
25
+ timeout?: number,
26
+ ) {
22
27
  const publicClient =
23
28
  'getBlockNumber' in client && typeof client.getBlockNumber === 'function'
24
29
  ? (client as unknown as PublicClient)
@@ -31,12 +36,17 @@ export function waitUntilBlock<T extends Client>(client: T, blockNumber: number
31
36
  return currentBlockNumber >= BigInt(blockNumber);
32
37
  },
33
38
  `Wait until L1 block ${blockNumber}`,
34
- 120,
39
+ timeout ?? 120,
35
40
  0.1,
36
41
  );
37
42
  }
38
43
 
39
- export function waitUntilL1Timestamp<T extends Client>(client: T, timestamp: number | bigint, logger?: Logger) {
44
+ export function waitUntilL1Timestamp<T extends Client>(
45
+ client: T,
46
+ timestamp: number | bigint,
47
+ logger?: Logger,
48
+ timeout?: number,
49
+ ) {
40
50
  const publicClient =
41
51
  'getBlockNumber' in client && typeof client.getBlockNumber === 'function'
42
52
  ? (client as unknown as PublicClient)
@@ -56,7 +66,7 @@ export function waitUntilL1Timestamp<T extends Client>(client: T, timestamp: num
56
66
  return currentTs >= BigInt(timestamp);
57
67
  },
58
68
  `Wait until L1 timestamp ${timestamp}`,
59
- 120,
69
+ timeout ?? 120,
60
70
  0.1,
61
71
  );
62
72
  }