@aztec/ethereum 2.0.0-nightly.20250902 → 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 +18 -4
  19. package/dest/deploy_l1_contracts.d.ts.map +1 -1
  20. package/dest/deploy_l1_contracts.js +383 -61
  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 -4
  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 +357 -52
  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
@@ -1,10 +1,12 @@
1
+ import { L1_TO_L2_MSG_SUBTREE_HEIGHT } from '@aztec/constants';
1
2
  import { getActiveNetworkName } from '@aztec/foundation/config';
2
3
  import { keccak256String } from '@aztec/foundation/crypto';
3
4
  import { EthAddress } from '@aztec/foundation/eth-address';
4
5
  import { jsonStringify } from '@aztec/foundation/json-rpc';
5
6
  import { createLogger } from '@aztec/foundation/log';
6
7
  import { DateProvider } from '@aztec/foundation/timer';
7
- import { concatHex, encodeDeployData, encodeFunctionData, getAddress, getContract, getContractAddress, numberToHex, padHex } from 'viem';
8
+ import { mkdir, writeFile } from 'fs/promises';
9
+ import { concatHex, encodeAbiParameters, encodeDeployData, encodeFunctionData, getAddress, getContract, getContractAddress, numberToHex, padHex } from 'viem';
8
10
  import { foundry } from 'viem/chains';
9
11
  import { isAnvilTestChain } from './chain.js';
10
12
  import { createExtendedL1Client } from './client.js';
@@ -98,35 +100,12 @@ export const deploySharedContracts = async (l1Client, deployer, args, logger)=>{
98
100
  }
99
101
  const coinIssuerAddress = await deployer.deploy(CoinIssuerArtifact, [
100
102
  feeAssetAddress.toString(),
101
- 1n * 10n ** 18n,
102
- governanceAddress.toString()
103
+ 1_000_000n * 10n ** 18n,
104
+ l1Client.account.address
103
105
  ]);
104
106
  logger.verbose(`Deployed CoinIssuer at ${coinIssuerAddress}`);
105
- const feeAsset = getContract({
106
- address: feeAssetAddress.toString(),
107
- abi: FeeAssetArtifact.contractAbi,
108
- client: l1Client
109
- });
110
107
  logger.verbose(`Waiting for deployments to complete`);
111
108
  await deployer.waitForDeployments();
112
- if (args.acceleratedTestDeployments || !await feeAsset.read.minters([
113
- coinIssuerAddress.toString()
114
- ])) {
115
- const { txHash } = await deployer.sendTransaction({
116
- to: feeAssetAddress.toString(),
117
- data: encodeFunctionData({
118
- abi: FeeAssetArtifact.contractAbi,
119
- functionName: 'addMinter',
120
- args: [
121
- coinIssuerAddress.toString()
122
- ]
123
- })
124
- }, {
125
- gasLimit: 100_000n
126
- });
127
- logger.verbose(`Added coin issuer ${coinIssuerAddress} as minter on fee asset in ${txHash}`);
128
- txHashes.push(txHash);
129
- }
130
109
  // Registry ownership will be transferred to governance later, after rollup is added
131
110
  let feeAssetHandlerAddress = undefined;
132
111
  let stakingAssetHandlerAddress = undefined;
@@ -136,21 +115,24 @@ export const deploySharedContracts = async (l1Client, deployer, args, logger)=>{
136
115
  /* -------------------------------------------------------------------------- */ /* CHEAT CODES START HERE */ /* -------------------------------------------------------------------------- */ feeAssetHandlerAddress = await deployer.deploy(FeeAssetHandlerArtifact, [
137
116
  l1Client.account.address,
138
117
  feeAssetAddress.toString(),
139
- BigInt(1e18)
118
+ BigInt(1000n * 10n ** 18n)
140
119
  ]);
141
120
  logger.verbose(`Deployed FeeAssetHandler at ${feeAssetHandlerAddress}`);
142
- const { txHash } = await deployer.sendTransaction({
143
- to: feeAssetAddress.toString(),
144
- data: encodeFunctionData({
145
- abi: FeeAssetArtifact.contractAbi,
146
- functionName: 'addMinter',
147
- args: [
148
- feeAssetHandlerAddress.toString()
149
- ]
150
- })
151
- });
152
- logger.verbose(`Added fee asset handler ${feeAssetHandlerAddress} as minter on fee asset in ${txHash}`);
153
- txHashes.push(txHash);
121
+ // Only if we are "fresh" will we be adding as a minter, otherwise above will simply get same address
122
+ if (needToSetGovernance) {
123
+ const { txHash } = await deployer.sendTransaction({
124
+ to: feeAssetAddress.toString(),
125
+ data: encodeFunctionData({
126
+ abi: FeeAssetArtifact.contractAbi,
127
+ functionName: 'addMinter',
128
+ args: [
129
+ feeAssetHandlerAddress.toString()
130
+ ]
131
+ })
132
+ });
133
+ logger.verbose(`Added fee asset handler ${feeAssetHandlerAddress} as minter on fee asset in ${txHash}`);
134
+ txHashes.push(txHash);
135
+ }
154
136
  // Only if on sepolia will we deploy the staking asset handler
155
137
  // Should not be deployed to devnet since it would cause caos with sequencers there etc.
156
138
  if ([
@@ -458,6 +440,23 @@ function slasherFlavorToSolidityEnum(flavor) {
458
440
  ])) {
459
441
  await addMultipleValidators(extendedClient, deployer, addresses.gseAddress.toString(), rollupAddress.toString(), addresses.stakingAssetAddress.toString(), args.initialValidators, args.acceleratedTestDeployments, logger);
460
442
  }
443
+ // If the owner is not the Governance contract, transfer ownership to the Governance contract
444
+ logger.verbose(addresses.governanceAddress.toString());
445
+ if (getAddress(await rollupContract.getOwner()) !== getAddress(addresses.governanceAddress.toString())) {
446
+ // TODO(md): add send transaction to the deployer such that we do not need to manage tx hashes here
447
+ const { txHash: transferOwnershipTxHash } = await deployer.sendTransaction({
448
+ to: rollupContract.address,
449
+ data: encodeFunctionData({
450
+ abi: RegistryArtifact.contractAbi,
451
+ functionName: 'transferOwnership',
452
+ args: [
453
+ getAddress(addresses.governanceAddress.toString())
454
+ ]
455
+ })
456
+ });
457
+ logger.verbose(`Transferring the ownership of the rollup contract at ${rollupContract.address} to the Governance ${addresses.governanceAddress} in tx ${transferOwnershipTxHash}`);
458
+ txHashes.push(transferOwnershipTxHash);
459
+ }
461
460
  await deployer.waitForDeployments();
462
461
  await Promise.all(txHashes.map((txHash)=>extendedClient.waitForTransactionReceipt({
463
462
  hash: txHash
@@ -468,7 +467,7 @@ function slasherFlavorToSolidityEnum(flavor) {
468
467
  slashFactoryAddress
469
468
  };
470
469
  };
471
- export const handoverToGovernance = async (extendedClient, deployer, registryAddress, gseAddress, governanceAddress, logger, acceleratedTestDeployments)=>{
470
+ export const handoverToGovernance = async (extendedClient, deployer, registryAddress, gseAddress, coinIssuerAddress, feeAssetAddress, governanceAddress, logger, acceleratedTestDeployments)=>{
472
471
  // We need to call a function on the registry to set the various contract addresses.
473
472
  const registryContract = getContract({
474
473
  address: getAddress(registryAddress.toString()),
@@ -480,6 +479,16 @@ export const handoverToGovernance = async (extendedClient, deployer, registryAdd
480
479
  abi: GSEArtifact.contractAbi,
481
480
  client: extendedClient
482
481
  });
482
+ const coinIssuerContract = getContract({
483
+ address: getAddress(coinIssuerAddress.toString()),
484
+ abi: CoinIssuerArtifact.contractAbi,
485
+ client: extendedClient
486
+ });
487
+ const feeAsset = getContract({
488
+ address: getAddress(feeAssetAddress.toString()),
489
+ abi: FeeAssetArtifact.contractAbi,
490
+ client: extendedClient
491
+ });
483
492
  const txHashes = [];
484
493
  // If the owner is not the Governance contract, transfer ownership to the Governance contract
485
494
  if (acceleratedTestDeployments || await registryContract.read.owner() !== getAddress(governanceAddress.toString())) {
@@ -513,6 +522,48 @@ export const handoverToGovernance = async (extendedClient, deployer, registryAdd
513
522
  logger.verbose(`Transferring the ownership of the gse contract at ${gseAddress} to the Governance ${governanceAddress} in tx ${transferOwnershipTxHash}`);
514
523
  txHashes.push(transferOwnershipTxHash);
515
524
  }
525
+ if (acceleratedTestDeployments || await feeAsset.read.owner() !== coinIssuerAddress.toString()) {
526
+ const { txHash } = await deployer.sendTransaction({
527
+ to: feeAssetAddress.toString(),
528
+ data: encodeFunctionData({
529
+ abi: FeeAssetArtifact.contractAbi,
530
+ functionName: 'transferOwnership',
531
+ args: [
532
+ coinIssuerAddress.toString()
533
+ ]
534
+ })
535
+ }, {
536
+ gasLimit: 500_000n
537
+ });
538
+ logger.verbose(`Transfer ownership of fee asset to coin issuer ${coinIssuerAddress} in ${txHash}`);
539
+ txHashes.push(txHash);
540
+ const { txHash: acceptTokenOwnershipTxHash } = await deployer.sendTransaction({
541
+ to: coinIssuerAddress.toString(),
542
+ data: encodeFunctionData({
543
+ abi: CoinIssuerArtifact.contractAbi,
544
+ functionName: 'acceptTokenOwnership'
545
+ })
546
+ }, {
547
+ gasLimit: 500_000n
548
+ });
549
+ logger.verbose(`Accept ownership of fee asset in ${acceptTokenOwnershipTxHash}`);
550
+ txHashes.push(acceptTokenOwnershipTxHash);
551
+ }
552
+ // If the owner is not the Governance contract, transfer ownership to the Governance contract
553
+ if (acceleratedTestDeployments || await coinIssuerContract.read.owner() !== getAddress(governanceAddress.toString())) {
554
+ const { txHash: transferOwnershipTxHash } = await deployer.sendTransaction({
555
+ to: coinIssuerContract.address,
556
+ data: encodeFunctionData({
557
+ abi: CoinIssuerArtifact.contractAbi,
558
+ functionName: 'transferOwnership',
559
+ args: [
560
+ getAddress(governanceAddress.toString())
561
+ ]
562
+ })
563
+ });
564
+ logger.verbose(`Transferring the ownership of the coin issuer contract at ${coinIssuerAddress} to the Governance ${governanceAddress} in tx ${transferOwnershipTxHash}`);
565
+ txHashes.push(transferOwnershipTxHash);
566
+ }
516
567
  // Wait for all actions to be mined
517
568
  await deployer.waitForDeployments();
518
569
  await Promise.all(txHashes.map((txHash)=>extendedClient.waitForTransactionReceipt({
@@ -576,18 +627,47 @@ export const handoverToGovernance = async (extendedClient, deployer, registryAdd
576
627
  const entryQueueLengthBefore = await rollup.getEntryQueueLength();
577
628
  const validatorCountBefore = await rollup.getActiveAttesterCount();
578
629
  logger.info(`Adding ${validators.length} validators to the rollup`);
579
- await deployer.l1TxUtils.sendAndMonitorTransaction({
580
- to: multiAdder.toString(),
581
- data: encodeFunctionData({
582
- abi: MultiAdderArtifact.contractAbi,
583
- functionName: 'addValidators',
584
- args: [
585
- validatorsTuples
586
- ]
587
- })
588
- }, {
589
- gasLimit: 45_000_000n
590
- });
630
+ // Adding to the queue and flushing need to be done in two transactions
631
+ // if we are adding many validators.
632
+ if (validatorsTuples.length > 10) {
633
+ await deployer.l1TxUtils.sendAndMonitorTransaction({
634
+ to: multiAdder.toString(),
635
+ data: encodeFunctionData({
636
+ abi: MultiAdderArtifact.contractAbi,
637
+ functionName: 'addValidators',
638
+ args: [
639
+ validatorsTuples,
640
+ true
641
+ ]
642
+ })
643
+ }, {
644
+ gasLimit: 40_000_000n
645
+ });
646
+ await deployer.l1TxUtils.sendAndMonitorTransaction({
647
+ to: rollupAddress,
648
+ data: encodeFunctionData({
649
+ abi: RollupArtifact.contractAbi,
650
+ functionName: 'flushEntryQueue',
651
+ args: []
652
+ })
653
+ }, {
654
+ gasLimit: 40_000_000n
655
+ });
656
+ } else {
657
+ await deployer.l1TxUtils.sendAndMonitorTransaction({
658
+ to: multiAdder.toString(),
659
+ data: encodeFunctionData({
660
+ abi: MultiAdderArtifact.contractAbi,
661
+ functionName: 'addValidators',
662
+ args: [
663
+ validatorsTuples,
664
+ false
665
+ ]
666
+ })
667
+ }, {
668
+ gasLimit: 45_000_000n
669
+ });
670
+ }
591
671
  const entryQueueLengthAfter = await rollup.getEntryQueueLength();
592
672
  const validatorCountAfter = await rollup.getActiveAttesterCount();
593
673
  if (entryQueueLengthAfter + validatorCountAfter < entryQueueLengthBefore + validatorCountBefore + BigInt(validators.length)) {
@@ -637,7 +717,8 @@ export const cheat_initializeFeeAssetHandler = async (extendedClient, deployer,
637
717
  * @param logger - A logger object.
638
718
  * @param args - Arguments for initialization of L1 contracts
639
719
  * @returns A list of ETH addresses of the deployed contracts.
640
- */ export const deployL1Contracts = async (rpcUrls, account, chain, logger, args, txUtilsConfig = getL1TxUtilsConfigEnvVars())=>{
720
+ */ export const deployL1Contracts = async (rpcUrls, account, chain, logger, args, txUtilsConfig = getL1TxUtilsConfigEnvVars(), createVerificationJson = false)=>{
721
+ logger.info(`Deploying L1 contracts with config: ${jsonStringify(args)}`);
641
722
  validateConfig(args);
642
723
  const l1Client = createExtendedL1Client(rpcUrls, account, chain);
643
724
  // Deploy multicall3 if it does not exist in this network
@@ -663,23 +744,219 @@ export const cheat_initializeFeeAssetHandler = async (extendedClient, deployer,
663
744
  }
664
745
  logger.verbose(`Deploying contracts from ${account.address.toString()}`);
665
746
  const dateProvider = new DateProvider();
666
- const deployer = new L1Deployer(l1Client, args.salt, dateProvider, args.acceleratedTestDeployments, logger, txUtilsConfig);
667
- const { feeAssetAddress, feeAssetHandlerAddress, stakingAssetAddress, stakingAssetHandlerAddress, registryAddress, gseAddress, governanceAddress, rewardDistributorAddress, zkPassportVerifierAddress } = await deploySharedContracts(l1Client, deployer, args, logger);
747
+ const deployer = new L1Deployer(l1Client, args.salt, dateProvider, args.acceleratedTestDeployments, logger, txUtilsConfig, !!createVerificationJson);
748
+ const { feeAssetAddress, feeAssetHandlerAddress, stakingAssetAddress, stakingAssetHandlerAddress, registryAddress, gseAddress, governanceAddress, rewardDistributorAddress, zkPassportVerifierAddress, coinIssuerAddress } = await deploySharedContracts(l1Client, deployer, args, logger);
668
749
  const { rollup, slashFactoryAddress } = await deployRollup(l1Client, deployer, args, {
669
750
  feeJuiceAddress: feeAssetAddress,
670
751
  registryAddress,
671
752
  gseAddress,
672
753
  rewardDistributorAddress,
673
- stakingAssetAddress
754
+ stakingAssetAddress,
755
+ governanceAddress
674
756
  }, logger);
675
757
  logger.verbose('Waiting for rollup and slash factory to be deployed');
676
758
  await deployer.waitForDeployments();
677
759
  // Now that the rollup has been deployed and added to the registry, transfer ownership to governance
678
- await handoverToGovernance(l1Client, deployer, registryAddress, gseAddress, governanceAddress, logger, args.acceleratedTestDeployments);
760
+ await handoverToGovernance(l1Client, deployer, registryAddress, gseAddress, coinIssuerAddress, feeAssetAddress, governanceAddress, logger, args.acceleratedTestDeployments);
679
761
  logger.info(`Handing over to governance complete`);
680
762
  logger.verbose(`All transactions for L1 deployment have been mined`);
681
763
  const l1Contracts = await RegistryContract.collectAddresses(l1Client, registryAddress, 'canonical');
682
764
  logger.info(`Aztec L1 contracts initialized`, l1Contracts);
765
+ // Write verification data (constructor args + linked libraries) to file for later forge verify
766
+ if (createVerificationJson) {
767
+ try {
768
+ // Add Inbox / Outbox verification records (constructor args are created inside RollupCore)
769
+ const rollupAddr = l1Contracts.rollupAddress.toString();
770
+ const inboxAddr = l1Contracts.inboxAddress.toString();
771
+ const outboxAddr = l1Contracts.outboxAddress.toString();
772
+ const feeAsset = l1Contracts.feeJuiceAddress.toString();
773
+ const version = await rollup.getVersion();
774
+ const inboxCtor = encodeAbiParameters([
775
+ {
776
+ type: 'address'
777
+ },
778
+ {
779
+ type: 'address'
780
+ },
781
+ {
782
+ type: 'uint256'
783
+ },
784
+ {
785
+ type: 'uint256'
786
+ }
787
+ ], [
788
+ rollupAddr,
789
+ feeAsset,
790
+ version,
791
+ BigInt(L1_TO_L2_MSG_SUBTREE_HEIGHT)
792
+ ]);
793
+ const outboxCtor = encodeAbiParameters([
794
+ {
795
+ type: 'address'
796
+ },
797
+ {
798
+ type: 'uint256'
799
+ }
800
+ ], [
801
+ rollupAddr,
802
+ version
803
+ ]);
804
+ deployer.verificationRecords.push({
805
+ name: 'Inbox',
806
+ address: inboxAddr,
807
+ constructorArgsHex: inboxCtor,
808
+ libraries: []
809
+ }, {
810
+ name: 'Outbox',
811
+ address: outboxAddr,
812
+ constructorArgsHex: outboxCtor,
813
+ libraries: []
814
+ });
815
+ // Include Slasher and SlashingProposer (if deployed) in verification data
816
+ try {
817
+ const slasherAddrHex = await rollup.getSlasher();
818
+ const slasherAddr = EthAddress.fromString(slasherAddrHex);
819
+ if (!slasherAddr.isZero()) {
820
+ // Slasher constructor: (address _vetoer, address _governance)
821
+ const slasherCtor = encodeAbiParameters([
822
+ {
823
+ type: 'address'
824
+ },
825
+ {
826
+ type: 'address'
827
+ }
828
+ ], [
829
+ args.slashingVetoer.toString(),
830
+ l1Client.account.address
831
+ ]);
832
+ deployer.verificationRecords.push({
833
+ name: 'Slasher',
834
+ address: slasherAddr.toString(),
835
+ constructorArgsHex: slasherCtor,
836
+ libraries: []
837
+ });
838
+ // Proposer address is stored in Slasher.PROPOSER()
839
+ const proposerAddr = (await rollup.getSlashingProposerAddress()).toString();
840
+ // Compute constructor args matching deployment path in RollupCore
841
+ const computedRoundSize = BigInt(args.slashingRoundSizeInEpochs * args.aztecEpochDuration);
842
+ const computedQuorum = BigInt(args.slashingQuorum ?? args.slashingRoundSizeInEpochs * args.aztecEpochDuration / 2 + 1);
843
+ const lifetimeInRounds = BigInt(args.slashingLifetimeInRounds);
844
+ const executionDelayInRounds = BigInt(args.slashingExecutionDelayInRounds);
845
+ if (args.slasherFlavor === 'tally') {
846
+ const slashAmounts = [
847
+ args.slashAmountSmall,
848
+ args.slashAmountMedium,
849
+ args.slashAmountLarge
850
+ ];
851
+ const committeeSize = BigInt(args.aztecTargetCommitteeSize);
852
+ const epochDuration = BigInt(args.aztecEpochDuration);
853
+ const slashOffsetInRounds = BigInt(args.slashingOffsetInRounds);
854
+ const proposerCtor = encodeAbiParameters([
855
+ {
856
+ type: 'address'
857
+ },
858
+ {
859
+ type: 'address'
860
+ },
861
+ {
862
+ type: 'uint256'
863
+ },
864
+ {
865
+ type: 'uint256'
866
+ },
867
+ {
868
+ type: 'uint256'
869
+ },
870
+ {
871
+ type: 'uint256'
872
+ },
873
+ {
874
+ type: 'uint256[3]'
875
+ },
876
+ {
877
+ type: 'uint256'
878
+ },
879
+ {
880
+ type: 'uint256'
881
+ },
882
+ {
883
+ type: 'uint256'
884
+ }
885
+ ], [
886
+ rollup.address,
887
+ slasherAddr.toString(),
888
+ computedQuorum,
889
+ computedRoundSize,
890
+ lifetimeInRounds,
891
+ executionDelayInRounds,
892
+ slashAmounts,
893
+ committeeSize,
894
+ epochDuration,
895
+ slashOffsetInRounds
896
+ ]);
897
+ deployer.verificationRecords.push({
898
+ name: 'TallySlashingProposer',
899
+ address: proposerAddr,
900
+ constructorArgsHex: proposerCtor,
901
+ libraries: []
902
+ });
903
+ } else if (args.slasherFlavor === 'empire') {
904
+ const proposerCtor = encodeAbiParameters([
905
+ {
906
+ type: 'address'
907
+ },
908
+ {
909
+ type: 'address'
910
+ },
911
+ {
912
+ type: 'uint256'
913
+ },
914
+ {
915
+ type: 'uint256'
916
+ },
917
+ {
918
+ type: 'uint256'
919
+ },
920
+ {
921
+ type: 'uint256'
922
+ }
923
+ ], [
924
+ rollup.address,
925
+ slasherAddr.toString(),
926
+ computedQuorum,
927
+ computedRoundSize,
928
+ lifetimeInRounds,
929
+ executionDelayInRounds
930
+ ]);
931
+ deployer.verificationRecords.push({
932
+ name: 'EmpireSlashingProposer',
933
+ address: proposerAddr,
934
+ constructorArgsHex: proposerCtor,
935
+ libraries: []
936
+ });
937
+ }
938
+ }
939
+ } catch (e) {
940
+ logger.warn(`Failed to add Slasher/Proposer verification records: ${String(e)}`);
941
+ }
942
+ const date = new Date();
943
+ const formattedDate = date.toISOString().slice(2, 19).replace(/[-T:]/g, '');
944
+ // Ensure the verification output directory exists
945
+ await mkdir(createVerificationJson, {
946
+ recursive: true
947
+ });
948
+ const verificationOutputPath = `${createVerificationJson}/l1-verify-${chain.id}-${formattedDate.slice(0, 6)}-${formattedDate.slice(6)}.json`;
949
+ const verificationData = {
950
+ chainId: chain.id,
951
+ network: networkName,
952
+ records: deployer.verificationRecords
953
+ };
954
+ await writeFile(verificationOutputPath, JSON.stringify(verificationData, null, 2));
955
+ logger.info(`Wrote L1 verification data to ${verificationOutputPath}`);
956
+ } catch (e) {
957
+ logger.warn(`Failed to write L1 verification data file: ${String(e)}`);
958
+ }
959
+ }
683
960
  if (isAnvilTestChain(chain.id)) {
684
961
  // @note We make a time jump PAST the very first slot to not have to deal with the edge case of the first slot.
685
962
  // The edge case being that the genesis block is already occupying slot 0, so we cannot have another block.
@@ -712,7 +989,8 @@ export const cheat_initializeFeeAssetHandler = async (extendedClient, deployer,
712
989
  slashFactoryAddress,
713
990
  feeAssetHandlerAddress,
714
991
  stakingAssetHandlerAddress,
715
- zkPassportVerifierAddress
992
+ zkPassportVerifierAddress,
993
+ coinIssuerAddress
716
994
  }
717
995
  };
718
996
  };
@@ -721,15 +999,19 @@ export class L1Deployer {
721
999
  acceleratedTestDeployments;
722
1000
  logger;
723
1001
  txUtilsConfig;
1002
+ createVerificationJson;
724
1003
  salt;
725
1004
  txHashes;
726
1005
  l1TxUtils;
727
- constructor(client, maybeSalt, dateProvider = new DateProvider(), acceleratedTestDeployments = false, logger = createLogger('L1Deployer'), txUtilsConfig){
1006
+ verificationRecords;
1007
+ constructor(client, maybeSalt, dateProvider = new DateProvider(), acceleratedTestDeployments = false, logger = createLogger('L1Deployer'), txUtilsConfig, createVerificationJson = false){
728
1008
  this.client = client;
729
1009
  this.acceleratedTestDeployments = acceleratedTestDeployments;
730
1010
  this.logger = logger;
731
1011
  this.txUtilsConfig = txUtilsConfig;
1012
+ this.createVerificationJson = createVerificationJson;
732
1013
  this.txHashes = [];
1014
+ this.verificationRecords = [];
733
1015
  this.salt = maybeSalt ? padHex(numberToHex(maybeSalt), {
734
1016
  size: 32
735
1017
  }) : undefined;
@@ -740,7 +1022,7 @@ export class L1Deployer {
740
1022
  args
741
1023
  });
742
1024
  try {
743
- const { txHash, address } = await deployL1Contract(this.client, params.contractAbi, params.contractBytecode, args ?? [], {
1025
+ const { txHash, address, deployedLibraries } = await deployL1Contract(this.client, params.contractAbi, params.contractBytecode, args ?? [], {
744
1026
  salt: this.salt,
745
1027
  libraries: params.libraries,
746
1028
  logger: this.logger,
@@ -754,6 +1036,23 @@ export class L1Deployer {
754
1036
  this.logger.debug(`Deployed ${params.name} at ${address}`, {
755
1037
  args
756
1038
  });
1039
+ if (this.createVerificationJson) {
1040
+ // Encode constructor args for verification
1041
+ let constructorArgsHex = '0x';
1042
+ try {
1043
+ const abiItem = params.contractAbi.find((x)=>x && x.type === 'constructor');
1044
+ const inputDefs = abiItem && Array.isArray(abiItem.inputs) ? abiItem.inputs : [];
1045
+ constructorArgsHex = inputDefs.length > 0 ? encodeAbiParameters(inputDefs, args ?? []) : '0x';
1046
+ } catch {
1047
+ constructorArgsHex = '0x';
1048
+ }
1049
+ this.verificationRecords.push({
1050
+ name: params.name,
1051
+ address: address.toString(),
1052
+ constructorArgsHex,
1053
+ libraries: deployedLibraries ?? []
1054
+ });
1055
+ }
757
1056
  return address;
758
1057
  } catch (error) {
759
1058
  throw new Error(`Failed to deploy ${params.name}`, {
@@ -800,6 +1099,7 @@ export class L1Deployer {
800
1099
  */ export async function deployL1Contract(extendedClient, abi, bytecode, args = [], opts = {}) {
801
1100
  let txHash = undefined;
802
1101
  let resultingAddress = undefined;
1102
+ const deployedLibraries = [];
803
1103
  const { salt: saltFromOpts, libraries, logger, gasLimit, acceleratedTestDeployments } = opts;
804
1104
  let { l1TxUtils } = opts;
805
1105
  if (!l1TxUtils) {
@@ -822,9 +1122,30 @@ export class L1Deployer {
822
1122
  const lib = libraries.libraryCode[libraryName];
823
1123
  const { libraries: _libraries, ...optsWithoutLibraries } = opts;
824
1124
  const { address, txHash } = await deployL1Contract(extendedClient, lib.contractAbi, lib.contractBytecode, [], optsWithoutLibraries);
1125
+ // Log deployed library name and address for easier verification/triage
1126
+ logger?.verbose(`Linked library deployed`, {
1127
+ library: libraryName,
1128
+ address: address.toString(),
1129
+ txHash
1130
+ });
825
1131
  if (txHash) {
826
1132
  libraryTxs.push(txHash);
827
1133
  }
1134
+ // Try to find the source file for this library from linkReferences
1135
+ let fileNameForLibrary = undefined;
1136
+ for(const fileName in libraries.linkReferences){
1137
+ if (libraries.linkReferences[fileName] && libraries.linkReferences[fileName][libraryName]) {
1138
+ fileNameForLibrary = fileName;
1139
+ break;
1140
+ }
1141
+ }
1142
+ if (fileNameForLibrary) {
1143
+ deployedLibraries.push({
1144
+ file: fileNameForLibrary,
1145
+ contract: libraryName,
1146
+ address: address.toString()
1147
+ });
1148
+ }
828
1149
  for(const linkRef in libraries.linkReferences){
829
1150
  for(const contractName in libraries.linkReferences[linkRef]){
830
1151
  // If the library name matches the one we just deployed, we replace it.
@@ -919,7 +1240,8 @@ export class L1Deployer {
919
1240
  }
920
1241
  return {
921
1242
  address: EthAddress.fromString(resultingAddress),
922
- txHash
1243
+ txHash,
1244
+ deployedLibraries
923
1245
  };
924
1246
  }
925
1247
  export function getExpectedAddress(abi, bytecode, args, salt) {