@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,3 +1,4 @@
1
+ import { L1_TO_L2_MSG_SUBTREE_HEIGHT } from '@aztec/constants';
1
2
  import { SecretValue, getActiveNetworkName } from '@aztec/foundation/config';
2
3
  import { keccak256String } from '@aztec/foundation/crypto';
3
4
  import { EthAddress } from '@aztec/foundation/eth-address';
@@ -8,6 +9,7 @@ import { DateProvider } from '@aztec/foundation/timer';
8
9
  import type { RollupAbi } from '@aztec/l1-artifacts/RollupAbi';
9
10
 
10
11
  import type { Abi, Narrow } from 'abitype';
12
+ import { mkdir, writeFile } from 'fs/promises';
11
13
  import {
12
14
  type Chain,
13
15
  type ContractConstructorArgs,
@@ -15,6 +17,7 @@ import {
15
17
  type Hex,
16
18
  type PrivateKeyAccount,
17
19
  concatHex,
20
+ encodeAbiParameters,
18
21
  encodeDeployData,
19
22
  encodeFunctionData,
20
23
  getAddress,
@@ -129,6 +132,19 @@ export interface ContractArtifacts<TAbi extends Abi | readonly unknown[] = Abi>
129
132
  libraries?: Libraries;
130
133
  }
131
134
 
135
+ export type VerificationLibraryEntry = {
136
+ file: string;
137
+ contract: string;
138
+ address: string;
139
+ };
140
+
141
+ export type VerificationRecord = {
142
+ name: string;
143
+ address: string;
144
+ constructorArgsHex: Hex;
145
+ libraries: VerificationLibraryEntry[];
146
+ };
147
+
132
148
  export interface DeployL1ContractsArgs extends Omit<L1ContractsConfig, keyof L1TxUtilsConfig> {
133
149
  /** The vk tree root. */
134
150
  vkTreeRoot: Fr;
@@ -247,36 +263,14 @@ export const deploySharedContracts = async (
247
263
 
248
264
  const coinIssuerAddress = await deployer.deploy(CoinIssuerArtifact, [
249
265
  feeAssetAddress.toString(),
250
- 1n * 10n ** 18n, // @todo #8084
251
- governanceAddress.toString(),
266
+ 1_000_000n * 10n ** 18n, // @todo #8084
267
+ l1Client.account.address,
252
268
  ]);
253
269
  logger.verbose(`Deployed CoinIssuer at ${coinIssuerAddress}`);
254
270
 
255
- const feeAsset = getContract({
256
- address: feeAssetAddress.toString(),
257
- abi: FeeAssetArtifact.contractAbi,
258
- client: l1Client,
259
- });
260
-
261
271
  logger.verbose(`Waiting for deployments to complete`);
262
272
  await deployer.waitForDeployments();
263
273
 
264
- if (args.acceleratedTestDeployments || !(await feeAsset.read.minters([coinIssuerAddress.toString()]))) {
265
- const { txHash } = await deployer.sendTransaction(
266
- {
267
- to: feeAssetAddress.toString(),
268
- data: encodeFunctionData({
269
- abi: FeeAssetArtifact.contractAbi,
270
- functionName: 'addMinter',
271
- args: [coinIssuerAddress.toString()],
272
- }),
273
- },
274
- { gasLimit: 100_000n },
275
- );
276
- logger.verbose(`Added coin issuer ${coinIssuerAddress} as minter on fee asset in ${txHash}`);
277
- txHashes.push(txHash);
278
- }
279
-
280
274
  // Registry ownership will be transferred to governance later, after rollup is added
281
275
 
282
276
  let feeAssetHandlerAddress: EthAddress | undefined = undefined;
@@ -292,20 +286,23 @@ export const deploySharedContracts = async (
292
286
  feeAssetHandlerAddress = await deployer.deploy(FeeAssetHandlerArtifact, [
293
287
  l1Client.account.address,
294
288
  feeAssetAddress.toString(),
295
- BigInt(1e18),
289
+ BigInt(1000n * 10n ** 18n),
296
290
  ]);
297
291
  logger.verbose(`Deployed FeeAssetHandler at ${feeAssetHandlerAddress}`);
298
292
 
299
- const { txHash } = await deployer.sendTransaction({
300
- to: feeAssetAddress.toString(),
301
- data: encodeFunctionData({
302
- abi: FeeAssetArtifact.contractAbi,
303
- functionName: 'addMinter',
304
- args: [feeAssetHandlerAddress.toString()],
305
- }),
306
- });
307
- logger.verbose(`Added fee asset handler ${feeAssetHandlerAddress} as minter on fee asset in ${txHash}`);
308
- 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
+ }
309
306
 
310
307
  // Only if on sepolia will we deploy the staking asset handler
311
308
  // Should not be deployed to devnet since it would cause caos with sequencers there etc.
@@ -500,7 +497,12 @@ export const deployRollup = async (
500
497
  >,
501
498
  addresses: Pick<
502
499
  L1ContractAddresses,
503
- 'feeJuiceAddress' | 'registryAddress' | 'rewardDistributorAddress' | 'stakingAssetAddress' | 'gseAddress'
500
+ | 'feeJuiceAddress'
501
+ | 'registryAddress'
502
+ | 'rewardDistributorAddress'
503
+ | 'stakingAssetAddress'
504
+ | 'gseAddress'
505
+ | 'governanceAddress'
504
506
  >,
505
507
  logger: Logger,
506
508
  ) => {
@@ -678,6 +680,24 @@ export const deployRollup = async (
678
680
  );
679
681
  }
680
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
+
681
701
  await deployer.waitForDeployments();
682
702
  await Promise.all(txHashes.map(txHash => extendedClient.waitForTransactionReceipt({ hash: txHash })));
683
703
  logger.verbose(`Rollup deployed`);
@@ -690,6 +710,8 @@ export const handoverToGovernance = async (
690
710
  deployer: L1Deployer,
691
711
  registryAddress: EthAddress,
692
712
  gseAddress: EthAddress,
713
+ coinIssuerAddress: EthAddress,
714
+ feeAssetAddress: EthAddress,
693
715
  governanceAddress: EthAddress,
694
716
  logger: Logger,
695
717
  acceleratedTestDeployments: boolean | undefined,
@@ -707,6 +729,18 @@ export const handoverToGovernance = async (
707
729
  client: extendedClient,
708
730
  });
709
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
+
710
744
  const txHashes: Hex[] = [];
711
745
 
712
746
  // If the owner is not the Governance contract, transfer ownership to the Governance contract
@@ -746,6 +780,54 @@ export const handoverToGovernance = async (
746
780
  txHashes.push(transferOwnershipTxHash);
747
781
  }
748
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
+
749
831
  // Wait for all actions to be mined
750
832
  await deployer.waitForDeployments();
751
833
  await Promise.all(txHashes.map(txHash => extendedClient.waitForTransactionReceipt({ hash: txHash })));
@@ -827,19 +909,51 @@ export const addMultipleValidators = async (
827
909
 
828
910
  logger.info(`Adding ${validators.length} validators to the rollup`);
829
911
 
830
- await deployer.l1TxUtils.sendAndMonitorTransaction(
831
- {
832
- to: multiAdder.toString(),
833
- data: encodeFunctionData({
834
- abi: MultiAdderArtifact.contractAbi,
835
- functionName: 'addValidators',
836
- args: [validatorsTuples],
837
- }),
838
- },
839
- {
840
- gasLimit: 45_000_000n,
841
- },
842
- );
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
+ }
843
957
 
844
958
  const entryQueueLengthAfter = await rollup.getEntryQueueLength();
845
959
  const validatorCountAfter = await rollup.getActiveAttesterCount();
@@ -914,7 +1028,9 @@ export const deployL1Contracts = async (
914
1028
  logger: Logger,
915
1029
  args: DeployL1ContractsArgs,
916
1030
  txUtilsConfig: L1TxUtilsConfig = getL1TxUtilsConfigEnvVars(),
1031
+ createVerificationJson: string | false = false,
917
1032
  ): Promise<DeployL1ContractsReturnType> => {
1033
+ logger.info(`Deploying L1 contracts with config: ${jsonStringify(args)}`);
918
1034
  validateConfig(args);
919
1035
 
920
1036
  const l1Client = createExtendedL1Client(rpcUrls, account, chain);
@@ -952,6 +1068,7 @@ export const deployL1Contracts = async (
952
1068
  args.acceleratedTestDeployments,
953
1069
  logger,
954
1070
  txUtilsConfig,
1071
+ !!createVerificationJson,
955
1072
  );
956
1073
 
957
1074
  const {
@@ -964,6 +1081,7 @@ export const deployL1Contracts = async (
964
1081
  governanceAddress,
965
1082
  rewardDistributorAddress,
966
1083
  zkPassportVerifierAddress,
1084
+ coinIssuerAddress,
967
1085
  } = await deploySharedContracts(l1Client, deployer, args, logger);
968
1086
  const { rollup, slashFactoryAddress } = await deployRollup(
969
1087
  l1Client,
@@ -975,6 +1093,7 @@ export const deployL1Contracts = async (
975
1093
  gseAddress,
976
1094
  rewardDistributorAddress,
977
1095
  stakingAssetAddress,
1096
+ governanceAddress,
978
1097
  },
979
1098
  logger,
980
1099
  );
@@ -988,6 +1107,8 @@ export const deployL1Contracts = async (
988
1107
  deployer,
989
1108
  registryAddress,
990
1109
  gseAddress,
1110
+ coinIssuerAddress,
1111
+ feeAssetAddress,
991
1112
  governanceAddress,
992
1113
  logger,
993
1114
  args.acceleratedTestDeployments,
@@ -1000,6 +1121,147 @@ export const deployL1Contracts = async (
1000
1121
 
1001
1122
  logger.info(`Aztec L1 contracts initialized`, l1Contracts);
1002
1123
 
1124
+ // Write verification data (constructor args + linked libraries) to file for later forge verify
1125
+ if (createVerificationJson) {
1126
+ try {
1127
+ // Add Inbox / Outbox verification records (constructor args are created inside RollupCore)
1128
+ const rollupAddr = l1Contracts.rollupAddress.toString();
1129
+ const inboxAddr = l1Contracts.inboxAddress.toString();
1130
+ const outboxAddr = l1Contracts.outboxAddress.toString();
1131
+ const feeAsset = l1Contracts.feeJuiceAddress.toString();
1132
+ const version = await rollup.getVersion();
1133
+
1134
+ const inboxCtor = encodeAbiParameters(
1135
+ [{ type: 'address' }, { type: 'address' }, { type: 'uint256' }, { type: 'uint256' }],
1136
+ [rollupAddr, feeAsset, version, BigInt(L1_TO_L2_MSG_SUBTREE_HEIGHT)],
1137
+ );
1138
+
1139
+ const outboxCtor = encodeAbiParameters([{ type: 'address' }, { type: 'uint256' }], [rollupAddr, version]);
1140
+
1141
+ deployer.verificationRecords.push(
1142
+ { name: 'Inbox', address: inboxAddr, constructorArgsHex: inboxCtor, libraries: [] },
1143
+ { name: 'Outbox', address: outboxAddr, constructorArgsHex: outboxCtor, libraries: [] },
1144
+ );
1145
+
1146
+ // Include Slasher and SlashingProposer (if deployed) in verification data
1147
+ try {
1148
+ const slasherAddrHex = await rollup.getSlasher();
1149
+ const slasherAddr = EthAddress.fromString(slasherAddrHex);
1150
+ if (!slasherAddr.isZero()) {
1151
+ // Slasher constructor: (address _vetoer, address _governance)
1152
+ const slasherCtor = encodeAbiParameters(
1153
+ [{ type: 'address' }, { type: 'address' }],
1154
+ [args.slashingVetoer.toString(), l1Client.account.address],
1155
+ );
1156
+ deployer.verificationRecords.push({
1157
+ name: 'Slasher',
1158
+ address: slasherAddr.toString(),
1159
+ constructorArgsHex: slasherCtor,
1160
+ libraries: [],
1161
+ });
1162
+
1163
+ // Proposer address is stored in Slasher.PROPOSER()
1164
+ const proposerAddr = (await rollup.getSlashingProposerAddress()).toString();
1165
+
1166
+ // Compute constructor args matching deployment path in RollupCore
1167
+ const computedRoundSize = BigInt(args.slashingRoundSizeInEpochs * args.aztecEpochDuration);
1168
+ const computedQuorum = BigInt(
1169
+ args.slashingQuorum ?? (args.slashingRoundSizeInEpochs * args.aztecEpochDuration) / 2 + 1,
1170
+ );
1171
+ const lifetimeInRounds = BigInt(args.slashingLifetimeInRounds);
1172
+ const executionDelayInRounds = BigInt(args.slashingExecutionDelayInRounds);
1173
+
1174
+ if (args.slasherFlavor === 'tally') {
1175
+ const slashAmounts: readonly [bigint, bigint, bigint] = [
1176
+ args.slashAmountSmall,
1177
+ args.slashAmountMedium,
1178
+ args.slashAmountLarge,
1179
+ ];
1180
+ const committeeSize = BigInt(args.aztecTargetCommitteeSize);
1181
+ const epochDuration = BigInt(args.aztecEpochDuration);
1182
+ const slashOffsetInRounds = BigInt(args.slashingOffsetInRounds);
1183
+
1184
+ const proposerCtor = encodeAbiParameters(
1185
+ [
1186
+ { type: 'address' },
1187
+ { type: 'address' },
1188
+ { type: 'uint256' },
1189
+ { type: 'uint256' },
1190
+ { type: 'uint256' },
1191
+ { type: 'uint256' },
1192
+ { type: 'uint256[3]' },
1193
+ { type: 'uint256' },
1194
+ { type: 'uint256' },
1195
+ { type: 'uint256' },
1196
+ ],
1197
+ [
1198
+ rollup.address,
1199
+ slasherAddr.toString(),
1200
+ computedQuorum,
1201
+ computedRoundSize,
1202
+ lifetimeInRounds,
1203
+ executionDelayInRounds,
1204
+ slashAmounts,
1205
+ committeeSize,
1206
+ epochDuration,
1207
+ slashOffsetInRounds,
1208
+ ],
1209
+ );
1210
+
1211
+ deployer.verificationRecords.push({
1212
+ name: 'TallySlashingProposer',
1213
+ address: proposerAddr,
1214
+ constructorArgsHex: proposerCtor,
1215
+ libraries: [],
1216
+ });
1217
+ } else if (args.slasherFlavor === 'empire') {
1218
+ const proposerCtor = encodeAbiParameters(
1219
+ [
1220
+ { type: 'address' },
1221
+ { type: 'address' },
1222
+ { type: 'uint256' },
1223
+ { type: 'uint256' },
1224
+ { type: 'uint256' },
1225
+ { type: 'uint256' },
1226
+ ],
1227
+ [
1228
+ rollup.address,
1229
+ slasherAddr.toString(),
1230
+ computedQuorum,
1231
+ computedRoundSize,
1232
+ lifetimeInRounds,
1233
+ executionDelayInRounds,
1234
+ ],
1235
+ );
1236
+
1237
+ deployer.verificationRecords.push({
1238
+ name: 'EmpireSlashingProposer',
1239
+ address: proposerAddr,
1240
+ constructorArgsHex: proposerCtor,
1241
+ libraries: [],
1242
+ });
1243
+ }
1244
+ }
1245
+ } catch (e) {
1246
+ logger.warn(`Failed to add Slasher/Proposer verification records: ${String(e)}`);
1247
+ }
1248
+ const date = new Date();
1249
+ const formattedDate = date.toISOString().slice(2, 19).replace(/[-T:]/g, '');
1250
+ // Ensure the verification output directory exists
1251
+ await mkdir(createVerificationJson, { recursive: true });
1252
+ const verificationOutputPath = `${createVerificationJson}/l1-verify-${chain.id}-${formattedDate.slice(0, 6)}-${formattedDate.slice(6)}.json`;
1253
+ const verificationData = {
1254
+ chainId: chain.id,
1255
+ network: networkName,
1256
+ records: deployer.verificationRecords,
1257
+ };
1258
+ await writeFile(verificationOutputPath, JSON.stringify(verificationData, null, 2));
1259
+ logger.info(`Wrote L1 verification data to ${verificationOutputPath}`);
1260
+ } catch (e) {
1261
+ logger.warn(`Failed to write L1 verification data file: ${String(e)}`);
1262
+ }
1263
+ }
1264
+
1003
1265
  if (isAnvilTestChain(chain.id)) {
1004
1266
  // @note We make a time jump PAST the very first slot to not have to deal with the edge case of the first slot.
1005
1267
  // The edge case being that the genesis block is already occupying slot 0, so we cannot have another block.
@@ -1032,6 +1294,7 @@ export const deployL1Contracts = async (
1032
1294
  feeAssetHandlerAddress,
1033
1295
  stakingAssetHandlerAddress,
1034
1296
  zkPassportVerifierAddress,
1297
+ coinIssuerAddress,
1035
1298
  },
1036
1299
  };
1037
1300
  };
@@ -1040,6 +1303,7 @@ export class L1Deployer {
1040
1303
  private salt: Hex | undefined;
1041
1304
  private txHashes: Hex[] = [];
1042
1305
  public readonly l1TxUtils: L1TxUtils;
1306
+ public readonly verificationRecords: VerificationRecord[] = [];
1043
1307
 
1044
1308
  constructor(
1045
1309
  public readonly client: ExtendedViemWalletClient,
@@ -1048,6 +1312,7 @@ export class L1Deployer {
1048
1312
  private acceleratedTestDeployments: boolean = false,
1049
1313
  private logger: Logger = createLogger('L1Deployer'),
1050
1314
  private txUtilsConfig?: L1TxUtilsConfig,
1315
+ private createVerificationJson: boolean = false,
1051
1316
  ) {
1052
1317
  this.salt = maybeSalt ? padHex(numberToHex(maybeSalt), { size: 32 }) : undefined;
1053
1318
  this.l1TxUtils = createL1TxUtilsFromViemWallet(
@@ -1066,7 +1331,7 @@ export class L1Deployer {
1066
1331
  ): Promise<EthAddress> {
1067
1332
  this.logger.debug(`Deploying ${params.name} contract`, { args });
1068
1333
  try {
1069
- const { txHash, address } = await deployL1Contract(
1334
+ const { txHash, address, deployedLibraries } = await deployL1Contract(
1070
1335
  this.client,
1071
1336
  params.contractAbi,
1072
1337
  params.contractBytecode,
@@ -1084,6 +1349,26 @@ export class L1Deployer {
1084
1349
  this.txHashes.push(txHash);
1085
1350
  }
1086
1351
  this.logger.debug(`Deployed ${params.name} at ${address}`, { args });
1352
+
1353
+ if (this.createVerificationJson) {
1354
+ // Encode constructor args for verification
1355
+ let constructorArgsHex: Hex = '0x';
1356
+ try {
1357
+ const abiItem: any = (params.contractAbi as any[]).find((x: any) => x && x.type === 'constructor');
1358
+ const inputDefs: any[] = abiItem && Array.isArray(abiItem.inputs) ? abiItem.inputs : [];
1359
+ constructorArgsHex =
1360
+ inputDefs.length > 0 ? (encodeAbiParameters(inputDefs as any, (args ?? []) as any) as Hex) : ('0x' as Hex);
1361
+ } catch {
1362
+ constructorArgsHex = '0x' as Hex;
1363
+ }
1364
+
1365
+ this.verificationRecords.push({
1366
+ name: params.name,
1367
+ address: address.toString(),
1368
+ constructorArgsHex,
1369
+ libraries: deployedLibraries ?? [],
1370
+ });
1371
+ }
1087
1372
  return address;
1088
1373
  } catch (error) {
1089
1374
  throw new Error(`Failed to deploy ${params.name}`, { cause: formatViemError(error) });
@@ -1142,9 +1427,10 @@ export async function deployL1Contract(
1142
1427
  gasLimit?: bigint;
1143
1428
  acceleratedTestDeployments?: boolean;
1144
1429
  } = {},
1145
- ): Promise<{ address: EthAddress; txHash: Hex | undefined }> {
1430
+ ): Promise<{ address: EthAddress; txHash: Hex | undefined; deployedLibraries?: VerificationLibraryEntry[] }> {
1146
1431
  let txHash: Hex | undefined = undefined;
1147
1432
  let resultingAddress: Hex | null | undefined = undefined;
1433
+ const deployedLibraries: VerificationLibraryEntry[] = [];
1148
1434
 
1149
1435
  const { salt: saltFromOpts, libraries, logger, gasLimit, acceleratedTestDeployments } = opts;
1150
1436
  let { l1TxUtils } = opts;
@@ -1179,10 +1465,29 @@ export async function deployL1Contract(
1179
1465
  optsWithoutLibraries,
1180
1466
  );
1181
1467
 
1468
+ // Log deployed library name and address for easier verification/triage
1469
+ logger?.verbose(`Linked library deployed`, { library: libraryName, address: address.toString(), txHash });
1470
+
1182
1471
  if (txHash) {
1183
1472
  libraryTxs.push(txHash);
1184
1473
  }
1185
1474
 
1475
+ // Try to find the source file for this library from linkReferences
1476
+ let fileNameForLibrary: string | undefined = undefined;
1477
+ for (const fileName in libraries.linkReferences) {
1478
+ if (libraries.linkReferences[fileName] && libraries.linkReferences[fileName][libraryName]) {
1479
+ fileNameForLibrary = fileName;
1480
+ break;
1481
+ }
1482
+ }
1483
+ if (fileNameForLibrary) {
1484
+ deployedLibraries.push({
1485
+ file: fileNameForLibrary,
1486
+ contract: libraryName,
1487
+ address: address.toString(),
1488
+ });
1489
+ }
1490
+
1186
1491
  for (const linkRef in libraries.linkReferences) {
1187
1492
  for (const contractName in libraries.linkReferences[linkRef]) {
1188
1493
  // If the library name matches the one we just deployed, we replace it.
@@ -1264,7 +1569,7 @@ export async function deployL1Contract(
1264
1569
  }
1265
1570
  }
1266
1571
 
1267
- return { address: EthAddress.fromString(resultingAddress!), txHash };
1572
+ return { address: EthAddress.fromString(resultingAddress!), txHash, deployedLibraries };
1268
1573
  }
1269
1574
 
1270
1575
  export function getExpectedAddress(
@@ -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
  }