@aztec/ethereum 2.0.3 → 2.1.0-rc.2

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 (124) hide show
  1. package/dest/config.d.ts +10 -5
  2. package/dest/config.d.ts.map +1 -1
  3. package/dest/config.js +27 -39
  4. package/dest/contracts/empire_base.d.ts +1 -1
  5. package/dest/contracts/empire_base.d.ts.map +1 -1
  6. package/dest/contracts/empire_slashing_proposer.d.ts +1 -1
  7. package/dest/contracts/empire_slashing_proposer.d.ts.map +1 -1
  8. package/dest/contracts/fee_asset_handler.d.ts +3 -3
  9. package/dest/contracts/fee_asset_handler.d.ts.map +1 -1
  10. package/dest/contracts/governance.js +7 -3
  11. package/dest/contracts/governance_proposer.d.ts +1 -2
  12. package/dest/contracts/governance_proposer.d.ts.map +1 -1
  13. package/dest/contracts/governance_proposer.js +1 -2
  14. package/dest/contracts/multicall.d.ts +3 -5
  15. package/dest/contracts/multicall.d.ts.map +1 -1
  16. package/dest/contracts/multicall.js +6 -4
  17. package/dest/contracts/rollup.d.ts +13 -14
  18. package/dest/contracts/rollup.d.ts.map +1 -1
  19. package/dest/contracts/rollup.js +25 -67
  20. package/dest/contracts/slasher_contract.d.ts +10 -0
  21. package/dest/contracts/slasher_contract.d.ts.map +1 -1
  22. package/dest/contracts/slasher_contract.js +18 -0
  23. package/dest/deploy_l1_contracts.d.ts +18 -4
  24. package/dest/deploy_l1_contracts.d.ts.map +1 -1
  25. package/dest/deploy_l1_contracts.js +316 -159
  26. package/dest/index.d.ts +1 -1
  27. package/dest/index.d.ts.map +1 -1
  28. package/dest/index.js +1 -1
  29. package/dest/l1_artifacts.d.ts +8019 -5948
  30. package/dest/l1_artifacts.d.ts.map +1 -1
  31. package/dest/l1_artifacts.js +6 -1
  32. package/dest/l1_contract_addresses.d.ts +5 -1
  33. package/dest/l1_contract_addresses.d.ts.map +1 -1
  34. package/dest/l1_contract_addresses.js +2 -1
  35. package/dest/l1_tx_utils/config.d.ts +59 -0
  36. package/dest/l1_tx_utils/config.d.ts.map +1 -0
  37. package/dest/l1_tx_utils/config.js +73 -0
  38. package/dest/l1_tx_utils/constants.d.ts +6 -0
  39. package/dest/l1_tx_utils/constants.d.ts.map +1 -0
  40. package/dest/l1_tx_utils/constants.js +14 -0
  41. package/dest/l1_tx_utils/factory.d.ts +24 -0
  42. package/dest/l1_tx_utils/factory.d.ts.map +1 -0
  43. package/dest/l1_tx_utils/factory.js +12 -0
  44. package/dest/l1_tx_utils/index.d.ts +10 -0
  45. package/dest/l1_tx_utils/index.d.ts.map +1 -0
  46. package/dest/l1_tx_utils/index.js +10 -0
  47. package/dest/l1_tx_utils/interfaces.d.ts +76 -0
  48. package/dest/l1_tx_utils/interfaces.d.ts.map +1 -0
  49. package/dest/l1_tx_utils/interfaces.js +4 -0
  50. package/dest/l1_tx_utils/l1_tx_utils.d.ts +95 -0
  51. package/dest/l1_tx_utils/l1_tx_utils.d.ts.map +1 -0
  52. package/dest/l1_tx_utils/l1_tx_utils.js +610 -0
  53. package/dest/l1_tx_utils/l1_tx_utils_with_blobs.d.ts +26 -0
  54. package/dest/l1_tx_utils/l1_tx_utils_with_blobs.d.ts.map +1 -0
  55. package/dest/l1_tx_utils/l1_tx_utils_with_blobs.js +26 -0
  56. package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts +81 -0
  57. package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts.map +1 -0
  58. package/dest/l1_tx_utils/readonly_l1_tx_utils.js +294 -0
  59. package/dest/l1_tx_utils/signer.d.ts +4 -0
  60. package/dest/l1_tx_utils/signer.d.ts.map +1 -0
  61. package/dest/l1_tx_utils/signer.js +16 -0
  62. package/dest/l1_tx_utils/types.d.ts +67 -0
  63. package/dest/l1_tx_utils/types.d.ts.map +1 -0
  64. package/dest/l1_tx_utils/types.js +26 -0
  65. package/dest/l1_tx_utils/utils.d.ts +4 -0
  66. package/dest/l1_tx_utils/utils.d.ts.map +1 -0
  67. package/dest/l1_tx_utils/utils.js +14 -0
  68. package/dest/publisher_manager.d.ts +7 -2
  69. package/dest/publisher_manager.d.ts.map +1 -1
  70. package/dest/publisher_manager.js +36 -8
  71. package/dest/queries.d.ts.map +1 -1
  72. package/dest/queries.js +8 -2
  73. package/dest/test/chain_monitor.js +2 -2
  74. package/dest/test/delayed_tx_utils.d.ts +2 -2
  75. package/dest/test/delayed_tx_utils.d.ts.map +1 -1
  76. package/dest/test/delayed_tx_utils.js +2 -2
  77. package/dest/test/eth_cheat_codes.d.ts +18 -1
  78. package/dest/test/eth_cheat_codes.d.ts.map +1 -1
  79. package/dest/test/eth_cheat_codes.js +101 -22
  80. package/dest/test/rollup_cheat_codes.d.ts +3 -1
  81. package/dest/test/rollup_cheat_codes.d.ts.map +1 -1
  82. package/dest/test/rollup_cheat_codes.js +2 -2
  83. package/dest/test/upgrade_utils.js +1 -1
  84. package/package.json +6 -6
  85. package/src/config.ts +34 -44
  86. package/src/contracts/empire_base.ts +1 -1
  87. package/src/contracts/empire_slashing_proposer.ts +1 -1
  88. package/src/contracts/fee_asset_handler.ts +1 -1
  89. package/src/contracts/governance.ts +3 -3
  90. package/src/contracts/governance_proposer.ts +3 -4
  91. package/src/contracts/multicall.ts +12 -10
  92. package/src/contracts/rollup.ts +31 -86
  93. package/src/contracts/slasher_contract.ts +22 -0
  94. package/src/deploy_l1_contracts.ts +351 -178
  95. package/src/index.ts +1 -1
  96. package/src/l1_artifacts.ts +8 -0
  97. package/src/l1_contract_addresses.ts +3 -1
  98. package/src/l1_tx_utils/README.md +177 -0
  99. package/src/l1_tx_utils/config.ts +140 -0
  100. package/src/l1_tx_utils/constants.ts +18 -0
  101. package/src/l1_tx_utils/factory.ts +64 -0
  102. package/src/l1_tx_utils/index.ts +12 -0
  103. package/src/l1_tx_utils/interfaces.ts +86 -0
  104. package/src/l1_tx_utils/l1_tx_utils.ts +718 -0
  105. package/src/l1_tx_utils/l1_tx_utils_with_blobs.ts +77 -0
  106. package/src/l1_tx_utils/readonly_l1_tx_utils.ts +372 -0
  107. package/src/l1_tx_utils/signer.ts +28 -0
  108. package/src/l1_tx_utils/types.ts +85 -0
  109. package/src/l1_tx_utils/utils.ts +16 -0
  110. package/src/publisher_manager.ts +51 -9
  111. package/src/queries.ts +10 -1
  112. package/src/test/chain_monitor.ts +2 -2
  113. package/src/test/delayed_tx_utils.ts +2 -2
  114. package/src/test/eth_cheat_codes.ts +120 -20
  115. package/src/test/rollup_cheat_codes.ts +5 -2
  116. package/src/test/upgrade_utils.ts +1 -1
  117. package/dest/l1_tx_utils.d.ts +0 -252
  118. package/dest/l1_tx_utils.d.ts.map +0 -1
  119. package/dest/l1_tx_utils.js +0 -834
  120. package/dest/l1_tx_utils_with_blobs.d.ts +0 -20
  121. package/dest/l1_tx_utils_with_blobs.d.ts.map +0 -1
  122. package/dest/l1_tx_utils_with_blobs.js +0 -87
  123. package/src/l1_tx_utils.ts +0 -1124
  124. package/src/l1_tx_utils_with_blobs.ts +0 -150
@@ -6,50 +6,144 @@ import { jsonStringify } from '@aztec/foundation/json-rpc';
6
6
  import { createLogger } from '@aztec/foundation/log';
7
7
  import { DateProvider } from '@aztec/foundation/timer';
8
8
  import { mkdir, writeFile } from 'fs/promises';
9
+ import chunk from 'lodash.chunk';
9
10
  import { concatHex, encodeAbiParameters, encodeDeployData, encodeFunctionData, getAddress, getContract, getContractAddress, numberToHex, padHex } from 'viem';
10
11
  import { foundry } from 'viem/chains';
11
12
  import { isAnvilTestChain } from './chain.js';
12
13
  import { createExtendedL1Client } from './client.js';
13
- import { getEntryQueueConfig, getGSEConfiguration, getGovernanceConfiguration, getRewardBoostConfig, getRewardConfig, validateConfig } from './config.js';
14
+ import { getEntryQueueConfig, getGovernanceConfiguration, getRewardBoostConfig, getRewardConfig, validateConfig } from './config.js';
14
15
  import { GSEContract } from './contracts/gse.js';
15
16
  import { deployMulticall3 } from './contracts/multicall.js';
16
17
  import { RegistryContract } from './contracts/registry.js';
17
18
  import { RollupContract, SlashingProposerType } from './contracts/rollup.js';
18
- import { CoinIssuerArtifact, FeeAssetArtifact, FeeAssetHandlerArtifact, GSEArtifact, GovernanceArtifact, GovernanceProposerArtifact, MultiAdderArtifact, RegisterNewRollupVersionPayloadArtifact, RegistryArtifact, RollupArtifact, SlashFactoryArtifact, StakingAssetArtifact, StakingAssetHandlerArtifact, l1ArtifactsVerifiers, mockVerifiers } from './l1_artifacts.js';
19
- import { createL1TxUtilsFromViemWallet, getL1TxUtilsConfigEnvVars } from './l1_tx_utils.js';
19
+ import { CoinIssuerArtifact, DateGatedRelayerArtifact, FeeAssetArtifact, FeeAssetHandlerArtifact, GSEArtifact, GovernanceArtifact, GovernanceProposerArtifact, MultiAdderArtifact, RegisterNewRollupVersionPayloadArtifact, RegistryArtifact, RollupArtifact, SlashFactoryArtifact, StakingAssetArtifact, StakingAssetHandlerArtifact, l1ArtifactsVerifiers, mockVerifiers } from './l1_artifacts.js';
20
+ import { createL1TxUtilsFromViemWallet, getL1TxUtilsConfigEnvVars } from './l1_tx_utils/index.js';
20
21
  import { formatViemError } from './utils.js';
21
22
  import { ZK_PASSPORT_DOMAIN, ZK_PASSPORT_SCOPE, ZK_PASSPORT_VERIFIER_ADDRESS } from './zkPassportVerifierAddress.js';
22
23
  export const DEPLOYER_ADDRESS = '0x4e59b44847b379578588920cA78FbF26c0B4956C';
23
- const networkName = getActiveNetworkName();
24
+ // Minimal ERC20 ABI for validation purposes. We only read view methods.
25
+ const ERC20_VALIDATION_ABI = [
26
+ {
27
+ type: 'function',
28
+ name: 'totalSupply',
29
+ stateMutability: 'view',
30
+ inputs: [],
31
+ outputs: [
32
+ {
33
+ name: '',
34
+ type: 'uint256'
35
+ }
36
+ ]
37
+ },
38
+ {
39
+ type: 'function',
40
+ name: 'name',
41
+ stateMutability: 'view',
42
+ inputs: [],
43
+ outputs: [
44
+ {
45
+ name: '',
46
+ type: 'string'
47
+ }
48
+ ]
49
+ },
50
+ {
51
+ type: 'function',
52
+ name: 'symbol',
53
+ stateMutability: 'view',
54
+ inputs: [],
55
+ outputs: [
56
+ {
57
+ name: '',
58
+ type: 'string'
59
+ }
60
+ ]
61
+ },
62
+ {
63
+ type: 'function',
64
+ name: 'decimals',
65
+ stateMutability: 'view',
66
+ inputs: [],
67
+ outputs: [
68
+ {
69
+ name: '',
70
+ type: 'uint8'
71
+ }
72
+ ]
73
+ }
74
+ ];
75
+ /**
76
+ * Validates that the provided address points to a contract that resembles an ERC20 token.
77
+ * Checks for contract code and attempts common ERC20 view calls.
78
+ * Throws an error if validation fails.
79
+ */ export async function validateExistingErc20TokenAddress(l1Client, tokenAddress, logger) {
80
+ const addressString = tokenAddress.toString();
81
+ // Ensure there is contract code at the address
82
+ const code = await l1Client.getCode({
83
+ address: addressString
84
+ });
85
+ if (!code || code === '0x') {
86
+ throw new Error(`No contract code found at provided token address ${addressString}`);
87
+ }
88
+ const contract = getContract({
89
+ address: getAddress(addressString),
90
+ abi: ERC20_VALIDATION_ABI,
91
+ client: l1Client
92
+ });
93
+ // Validate all required ERC20 methods in parallel
94
+ const checks = [
95
+ contract.read.totalSupply().then((total)=>typeof total === 'bigint'),
96
+ contract.read.name().then(()=>true),
97
+ contract.read.symbol().then(()=>true),
98
+ contract.read.decimals().then((dec)=>typeof dec === 'number' || typeof dec === 'bigint')
99
+ ];
100
+ const results = await Promise.allSettled(checks);
101
+ const failedChecks = results.filter((result)=>result.status === 'rejected' || result.value !== true);
102
+ if (failedChecks.length > 0) {
103
+ throw new Error(`Address ${addressString} does not appear to implement ERC20 view methods`);
104
+ }
105
+ logger.verbose(`Validated existing token at ${addressString} appears to be ERC20-compatible`);
106
+ }
24
107
  export const deploySharedContracts = async (l1Client, deployer, args, logger)=>{
108
+ const networkName = getActiveNetworkName();
25
109
  logger.info(`Deploying shared contracts for network configuration: ${networkName}`);
26
110
  const txHashes = [];
27
- const feeAssetAddress = await deployer.deploy(FeeAssetArtifact, [
28
- 'FeeJuice',
29
- 'FEE',
30
- l1Client.account.address
31
- ]);
32
- logger.verbose(`Deployed Fee Asset at ${feeAssetAddress}`);
33
- const stakingAssetAddress = await deployer.deploy(StakingAssetArtifact, [
34
- 'Staking',
35
- 'STK',
36
- l1Client.account.address
37
- ]);
38
- logger.verbose(`Deployed Staking Asset at ${stakingAssetAddress}`);
39
- const gseConfiguration = getGSEConfiguration(networkName);
40
- const gseAddress = await deployer.deploy(GSEArtifact, [
111
+ let feeAssetAddress;
112
+ let stakingAssetAddress;
113
+ if (args.existingTokenAddress) {
114
+ await validateExistingErc20TokenAddress(l1Client, args.existingTokenAddress, logger);
115
+ feeAssetAddress = args.existingTokenAddress;
116
+ stakingAssetAddress = args.existingTokenAddress;
117
+ logger.verbose(`Using existing token for fee and staking assets at ${args.existingTokenAddress}`);
118
+ } else {
119
+ const deployedFee = await deployer.deploy(FeeAssetArtifact, [
120
+ 'FeeJuice',
121
+ 'FEE',
122
+ l1Client.account.address
123
+ ]);
124
+ feeAssetAddress = deployedFee.address;
125
+ logger.verbose(`Deployed Fee Asset at ${feeAssetAddress}`);
126
+ const deployedStaking = await deployer.deploy(StakingAssetArtifact, [
127
+ 'Staking',
128
+ 'STK',
129
+ l1Client.account.address
130
+ ]);
131
+ stakingAssetAddress = deployedStaking.address;
132
+ logger.verbose(`Deployed Staking Asset at ${stakingAssetAddress}`);
133
+ }
134
+ const gseAddress = (await deployer.deploy(GSEArtifact, [
41
135
  l1Client.account.address,
42
136
  stakingAssetAddress.toString(),
43
- gseConfiguration.activationThreshold,
44
- gseConfiguration.ejectionThreshold
45
- ]);
137
+ args.activationThreshold,
138
+ args.ejectionThreshold
139
+ ])).address;
46
140
  logger.verbose(`Deployed GSE at ${gseAddress}`);
47
- const registryAddress = await deployer.deploy(RegistryArtifact, [
141
+ const { address: registryAddress } = await deployer.deploy(RegistryArtifact, [
48
142
  l1Client.account.address,
49
143
  feeAssetAddress.toString()
50
144
  ]);
51
145
  logger.verbose(`Deployed Registry at ${registryAddress}`);
52
- const governanceProposerAddress = await deployer.deploy(GovernanceProposerArtifact, [
146
+ const { address: governanceProposerAddress } = await deployer.deploy(GovernanceProposerArtifact, [
53
147
  registryAddress.toString(),
54
148
  gseAddress.toString(),
55
149
  BigInt(args.governanceProposerQuorum ?? args.governanceProposerRoundSize / 2 + 1),
@@ -58,7 +152,7 @@ export const deploySharedContracts = async (l1Client, deployer, args, logger)=>{
58
152
  logger.verbose(`Deployed GovernanceProposer at ${governanceProposerAddress}`);
59
153
  // @note @LHerskind the assets are expected to be the same at some point, but for better
60
154
  // configurability they are different for now.
61
- const governanceAddress = await deployer.deploy(GovernanceArtifact, [
155
+ const { address: governanceAddress } = await deployer.deploy(GovernanceArtifact, [
62
156
  stakingAssetAddress.toString(),
63
157
  governanceProposerAddress.toString(),
64
158
  gseAddress.toString(),
@@ -98,11 +192,11 @@ export const deploySharedContracts = async (l1Client, deployer, args, logger)=>{
98
192
  logger.verbose(`Set governance on GSE in ${txHash}`);
99
193
  txHashes.push(txHash);
100
194
  }
101
- const coinIssuerAddress = await deployer.deploy(CoinIssuerArtifact, [
195
+ const coinIssuerAddress = (await deployer.deploy(CoinIssuerArtifact, [
102
196
  feeAssetAddress.toString(),
103
- 1_000_000n * 10n ** 18n,
197
+ 25_000_000_000n * 10n ** 18n / (60n * 60n * 24n * 365n),
104
198
  l1Client.account.address
105
- ]);
199
+ ])).address;
106
200
  logger.verbose(`Deployed CoinIssuer at ${coinIssuerAddress}`);
107
201
  logger.verbose(`Waiting for deployments to complete`);
108
202
  await deployer.waitForDeployments();
@@ -110,13 +204,13 @@ export const deploySharedContracts = async (l1Client, deployer, args, logger)=>{
110
204
  let feeAssetHandlerAddress = undefined;
111
205
  let stakingAssetHandlerAddress = undefined;
112
206
  let zkPassportVerifierAddress = undefined;
113
- // Only if not on mainnet will we deploy the handlers
114
- if (l1Client.chain.id !== 1) {
115
- /* -------------------------------------------------------------------------- */ /* CHEAT CODES START HERE */ /* -------------------------------------------------------------------------- */ feeAssetHandlerAddress = await deployer.deploy(FeeAssetHandlerArtifact, [
207
+ // Only if not on mainnet will we deploy the handlers, and only when we control the token
208
+ if (l1Client.chain.id !== 1 && !args.existingTokenAddress) {
209
+ /* -------------------------------------------------------------------------- */ /* CHEAT CODES START HERE */ /* -------------------------------------------------------------------------- */ feeAssetHandlerAddress = (await deployer.deploy(FeeAssetHandlerArtifact, [
116
210
  l1Client.account.address,
117
211
  feeAssetAddress.toString(),
118
212
  BigInt(1000n * 10n ** 18n)
119
- ]);
213
+ ])).address;
120
214
  logger.verbose(`Deployed FeeAssetHandler at ${feeAssetHandlerAddress}`);
121
215
  // Only if we are "fresh" will we be adding as a minter, otherwise above will simply get same address
122
216
  if (needToSetGovernance) {
@@ -147,6 +241,7 @@ export const deploySharedContracts = async (l1Client, deployer, args, logger)=>{
147
241
  stakingAsset: stakingAssetAddress.toString(),
148
242
  registry: registryAddress.toString(),
149
243
  withdrawer: AMIN.toString(),
244
+ validatorsToFlush: 16n,
150
245
  mintInterval: BigInt(60 * 60 * 24),
151
246
  depositsPerMint: BigInt(10),
152
247
  depositMerkleRoot: '0x0000000000000000000000000000000000000000000000000000000000000000',
@@ -161,9 +256,9 @@ export const deploySharedContracts = async (l1Client, deployer, args, logger)=>{
161
256
  skipBindCheck: args.zkPassportArgs?.mockZkPassportVerifier ?? false,
162
257
  skipMerkleCheck: true
163
258
  };
164
- stakingAssetHandlerAddress = await deployer.deploy(StakingAssetHandlerArtifact, [
259
+ stakingAssetHandlerAddress = (await deployer.deploy(StakingAssetHandlerArtifact, [
165
260
  stakingAssetHandlerDeployArgs
166
- ]);
261
+ ])).address;
167
262
  logger.verbose(`Deployed StakingAssetHandler at ${stakingAssetHandlerAddress}`);
168
263
  const { txHash: stakingMinterTxHash } = await deployer.sendTransaction({
169
264
  to: stakingAssetAddress.toString(),
@@ -187,20 +282,24 @@ export const deploySharedContracts = async (l1Client, deployer, args, logger)=>{
187
282
  logger.verbose(`Deployed shared contracts`);
188
283
  const registry = new RegistryContract(l1Client, registryAddress);
189
284
  /* -------------------------------------------------------------------------- */ /* FUND REWARD DISTRIBUTOR START */ /* -------------------------------------------------------------------------- */ const rewardDistributorAddress = await registry.getRewardDistributor();
190
- const blockReward = getRewardConfig(networkName).blockReward;
191
- const funding = blockReward * 200000n;
192
- const { txHash: fundRewardDistributorTxHash } = await deployer.sendTransaction({
193
- to: feeAssetAddress.toString(),
194
- data: encodeFunctionData({
195
- abi: FeeAssetArtifact.contractAbi,
196
- functionName: 'mint',
197
- args: [
198
- rewardDistributorAddress.toString(),
199
- funding
200
- ]
201
- })
202
- });
203
- logger.verbose(`Funded reward distributor with ${funding} fee asset in ${fundRewardDistributorTxHash}`);
285
+ if (!args.existingTokenAddress) {
286
+ const blockReward = getRewardConfig(networkName).blockReward;
287
+ const funding = blockReward * 200000n;
288
+ const { txHash: fundRewardDistributorTxHash } = await deployer.sendTransaction({
289
+ to: feeAssetAddress.toString(),
290
+ data: encodeFunctionData({
291
+ abi: FeeAssetArtifact.contractAbi,
292
+ functionName: 'mint',
293
+ args: [
294
+ rewardDistributorAddress.toString(),
295
+ funding
296
+ ]
297
+ })
298
+ });
299
+ logger.verbose(`Funded reward distributor with ${funding} fee asset in ${fundRewardDistributorTxHash}`);
300
+ } else {
301
+ logger.verbose(`Skipping reward distributor funding as existing token is provided`);
302
+ }
204
303
  /* -------------------------------------------------------------------------- */ /* FUND REWARD DISTRIBUTOR STOP */ /* -------------------------------------------------------------------------- */ return {
205
304
  feeAssetAddress,
206
305
  feeAssetHandlerAddress,
@@ -217,7 +316,7 @@ export const deploySharedContracts = async (l1Client, deployer, args, logger)=>{
217
316
  };
218
317
  const getZkPassportVerifierAddress = async (deployer, args)=>{
219
318
  if (args.zkPassportArgs?.mockZkPassportVerifier) {
220
- return await deployer.deploy(mockVerifiers.mockZkPassportVerifier);
319
+ return (await deployer.deploy(mockVerifiers.mockZkPassportVerifier)).address;
221
320
  }
222
321
  return ZK_PASSPORT_VERIFIER_ADDRESS;
223
322
  };
@@ -251,17 +350,17 @@ const getZkPassportVerifierAddress = async (deployer, args)=>{
251
350
  };
252
351
  };
253
352
  export const deploySlashFactory = async (deployer, rollupAddress, logger)=>{
254
- const slashFactoryAddress = await deployer.deploy(SlashFactoryArtifact, [
353
+ const slashFactoryAddress = (await deployer.deploy(SlashFactoryArtifact, [
255
354
  rollupAddress
256
- ]);
355
+ ])).address;
257
356
  logger.verbose(`Deployed SlashFactory at ${slashFactoryAddress}`);
258
357
  return slashFactoryAddress;
259
358
  };
260
359
  export const deployUpgradePayload = async (deployer, addresses)=>{
261
- const payloadAddress = await deployer.deploy(RegisterNewRollupVersionPayloadArtifact, [
360
+ const payloadAddress = (await deployer.deploy(RegisterNewRollupVersionPayloadArtifact, [
262
361
  addresses.registryAddress.toString(),
263
362
  addresses.rollupAddress.toString()
264
- ]);
363
+ ])).address;
265
364
  return payloadAddress;
266
365
  };
267
366
  function slasherFlavorToSolidityEnum(flavor) {
@@ -285,14 +384,15 @@ function slasherFlavorToSolidityEnum(flavor) {
285
384
  if (!addresses.gseAddress) {
286
385
  throw new Error('GSE address is required when deploying');
287
386
  }
387
+ const networkName = getActiveNetworkName();
288
388
  logger.info(`Deploying rollup using network configuration: ${networkName}`);
289
389
  const txHashes = [];
290
390
  let epochProofVerifier = EthAddress.ZERO;
291
391
  if (args.realVerifier) {
292
- epochProofVerifier = await deployer.deploy(l1ArtifactsVerifiers.honkVerifier);
392
+ epochProofVerifier = (await deployer.deploy(l1ArtifactsVerifiers.honkVerifier)).address;
293
393
  logger.verbose(`Rollup will use the real verifier at ${epochProofVerifier}`);
294
394
  } else {
295
- epochProofVerifier = await deployer.deploy(mockVerifiers.mockVerifier);
395
+ epochProofVerifier = (await deployer.deploy(mockVerifiers.mockVerifier)).address;
296
396
  logger.verbose(`Rollup will use the mock verifier at ${epochProofVerifier}`);
297
397
  }
298
398
  const rewardConfig = {
@@ -303,6 +403,7 @@ function slasherFlavorToSolidityEnum(flavor) {
303
403
  aztecSlotDuration: BigInt(args.aztecSlotDuration),
304
404
  aztecEpochDuration: BigInt(args.aztecEpochDuration),
305
405
  targetCommitteeSize: BigInt(args.aztecTargetCommitteeSize),
406
+ lagInEpochs: BigInt(args.lagInEpochs),
306
407
  aztecProofSubmissionEpochs: BigInt(args.aztecProofSubmissionEpochs),
307
408
  slashingQuorum: BigInt(args.slashingQuorum ?? args.slashingRoundSizeInEpochs * args.aztecEpochDuration / 2 + 1),
308
409
  slashingRoundSize: BigInt(args.slashingRoundSizeInEpochs * args.aztecEpochDuration),
@@ -322,7 +423,9 @@ function slasherFlavorToSolidityEnum(flavor) {
322
423
  args.slashAmountSmall,
323
424
  args.slashAmountMedium,
324
425
  args.slashAmountLarge
325
- ]
426
+ ],
427
+ localEjectionThreshold: args.localEjectionThreshold,
428
+ slashingDisableDuration: BigInt(args.slashingDisableDuration ?? 0n)
326
429
  };
327
430
  const genesisStateArgs = {
328
431
  vkTreeRoot: args.vkTreeRoot.toString(),
@@ -345,33 +448,38 @@ function slasherFlavorToSolidityEnum(flavor) {
345
448
  genesisStateArgs,
346
449
  rollupConfigArgs
347
450
  ];
348
- const rollupAddress = await deployer.deploy(RollupArtifact, rollupArgs, {
451
+ const { address: rollupAddress, existed: rollupExisted } = await deployer.deploy(RollupArtifact, rollupArgs, {
349
452
  gasLimit: 15_000_000n
350
453
  });
351
- logger.verbose(`Deployed Rollup at ${rollupAddress}`, rollupConfigArgs);
454
+ logger.verbose(`Deployed Rollup at ${rollupAddress}, already existed: ${rollupExisted}`, rollupConfigArgs);
352
455
  const rollupContract = new RollupContract(extendedClient, rollupAddress);
353
456
  await deployer.waitForDeployments();
354
457
  logger.verbose(`All core contracts have been deployed`);
355
458
  if (args.feeJuicePortalInitialBalance && args.feeJuicePortalInitialBalance > 0n) {
356
- const feeJuicePortalAddress = await rollupContract.getFeeJuicePortal();
357
- // In fast mode, use the L1TxUtils to send transactions with nonce management
358
- const { txHash: mintTxHash } = await deployer.sendTransaction({
359
- to: addresses.feeJuiceAddress.toString(),
360
- data: encodeFunctionData({
361
- abi: FeeAssetArtifact.contractAbi,
362
- functionName: 'mint',
363
- args: [
364
- feeJuicePortalAddress.toString(),
365
- args.feeJuicePortalInitialBalance
366
- ]
367
- })
368
- });
369
- logger.verbose(`Funding fee juice portal with ${args.feeJuicePortalInitialBalance} fee juice in ${mintTxHash} (accelerated test deployments)`);
370
- txHashes.push(mintTxHash);
459
+ // Skip funding when using an external token, as we likely don't have mint permissions
460
+ if (!('existingTokenAddress' in args) || !args.existingTokenAddress) {
461
+ const feeJuicePortalAddress = await rollupContract.getFeeJuicePortal();
462
+ // In fast mode, use the L1TxUtils to send transactions with nonce management
463
+ const { txHash: mintTxHash } = await deployer.sendTransaction({
464
+ to: addresses.feeJuiceAddress.toString(),
465
+ data: encodeFunctionData({
466
+ abi: FeeAssetArtifact.contractAbi,
467
+ functionName: 'mint',
468
+ args: [
469
+ feeJuicePortalAddress.toString(),
470
+ args.feeJuicePortalInitialBalance
471
+ ]
472
+ })
473
+ });
474
+ logger.verbose(`Funding fee juice portal with ${args.feeJuicePortalInitialBalance} fee juice in ${mintTxHash} (accelerated test deployments)`);
475
+ txHashes.push(mintTxHash);
476
+ } else {
477
+ logger.verbose('Skipping fee juice portal funding due to external token usage');
478
+ }
371
479
  }
372
- const slashFactoryAddress = await deployer.deploy(SlashFactoryArtifact, [
480
+ const slashFactoryAddress = (await deployer.deploy(SlashFactoryArtifact, [
373
481
  rollupAddress.toString()
374
- ]);
482
+ ])).address;
375
483
  logger.verbose(`Deployed SlashFactory at ${slashFactoryAddress}`);
376
484
  // We need to call a function on the registry to set the various contract addresses.
377
485
  const registryContract = getContract({
@@ -435,7 +543,11 @@ function slasherFlavorToSolidityEnum(flavor) {
435
543
  } else {
436
544
  logger.verbose(`Not the owner of the gse, skipping rollup addition`);
437
545
  }
438
- if (args.initialValidators && await gseContract.read.isRollupRegistered([
546
+ const activeAttestorCount = await rollupContract.getActiveAttesterCount();
547
+ const queuedAttestorCount = await rollupContract.getEntryQueueLength();
548
+ logger.info(`Rollup has ${activeAttestorCount} active attestors and ${queuedAttestorCount} queued attestors`);
549
+ const shouldAddValidators = activeAttestorCount === 0n && queuedAttestorCount === 0n;
550
+ if (args.initialValidators && shouldAddValidators && await gseContract.read.isRollupRegistered([
439
551
  rollupContract.address
440
552
  ])) {
441
553
  await addMultipleValidators(extendedClient, deployer, addresses.gseAddress.toString(), rollupAddress.toString(), addresses.stakingAssetAddress.toString(), args.initialValidators, args.acceleratedTestDeployments, logger);
@@ -467,7 +579,7 @@ function slasherFlavorToSolidityEnum(flavor) {
467
579
  slashFactoryAddress
468
580
  };
469
581
  };
470
- export const handoverToGovernance = async (extendedClient, deployer, registryAddress, gseAddress, coinIssuerAddress, feeAssetAddress, governanceAddress, logger, acceleratedTestDeployments)=>{
582
+ export const handoverToGovernance = async (extendedClient, deployer, registryAddress, gseAddress, coinIssuerAddress, feeAssetAddress, governanceAddress, logger, acceleratedTestDeployments, useExternalToken = false)=>{
471
583
  // We need to call a function on the registry to set the various contract addresses.
472
584
  const registryContract = getContract({
473
585
  address: getAddress(registryAddress.toString()),
@@ -522,7 +634,7 @@ export const handoverToGovernance = async (extendedClient, deployer, registryAdd
522
634
  logger.verbose(`Transferring the ownership of the gse contract at ${gseAddress} to the Governance ${governanceAddress} in tx ${transferOwnershipTxHash}`);
523
635
  txHashes.push(transferOwnershipTxHash);
524
636
  }
525
- if (acceleratedTestDeployments || await feeAsset.read.owner() !== coinIssuerAddress.toString()) {
637
+ if (!useExternalToken && (acceleratedTestDeployments || await feeAsset.read.owner() !== coinIssuerAddress.toString())) {
526
638
  const { txHash } = await deployer.sendTransaction({
527
639
  to: feeAssetAddress.toString(),
528
640
  data: encodeFunctionData({
@@ -548,20 +660,27 @@ export const handoverToGovernance = async (extendedClient, deployer, registryAdd
548
660
  });
549
661
  logger.verbose(`Accept ownership of fee asset in ${acceptTokenOwnershipTxHash}`);
550
662
  txHashes.push(acceptTokenOwnershipTxHash);
663
+ } else if (useExternalToken) {
664
+ logger.verbose('Skipping fee asset ownership transfer due to external token usage');
551
665
  }
666
+ // Either deploy or at least predict the address of the date gated relayer
667
+ const dateGatedRelayer = await deployer.deploy(DateGatedRelayerArtifact, [
668
+ governanceAddress.toString(),
669
+ 1798761600n
670
+ ]);
552
671
  // If the owner is not the Governance contract, transfer ownership to the Governance contract
553
- if (acceleratedTestDeployments || await coinIssuerContract.read.owner() !== getAddress(governanceAddress.toString())) {
672
+ if (acceleratedTestDeployments || await coinIssuerContract.read.owner() === deployer.client.account.address) {
554
673
  const { txHash: transferOwnershipTxHash } = await deployer.sendTransaction({
555
674
  to: coinIssuerContract.address,
556
675
  data: encodeFunctionData({
557
676
  abi: CoinIssuerArtifact.contractAbi,
558
677
  functionName: 'transferOwnership',
559
678
  args: [
560
- getAddress(governanceAddress.toString())
679
+ getAddress(dateGatedRelayer.address.toString())
561
680
  ]
562
681
  })
563
682
  });
564
- logger.verbose(`Transferring the ownership of the coin issuer contract at ${coinIssuerAddress} to the Governance ${governanceAddress} in tx ${transferOwnershipTxHash}`);
683
+ logger.verbose(`Transferring the ownership of the coin issuer contract at ${coinIssuerAddress} to the DateGatedRelayer ${dateGatedRelayer.address} in tx ${transferOwnershipTxHash}`);
565
684
  txHashes.push(transferOwnershipTxHash);
566
685
  }
567
686
  // Wait for all actions to be mined
@@ -569,6 +688,9 @@ export const handoverToGovernance = async (extendedClient, deployer, registryAdd
569
688
  await Promise.all(txHashes.map((txHash)=>extendedClient.waitForTransactionReceipt({
570
689
  hash: txHash
571
690
  })));
691
+ return {
692
+ dateGatedRelayerAddress: dateGatedRelayer.address
693
+ };
572
694
  };
573
695
  /*
574
696
  * Adds multiple validators to the rollup
@@ -596,85 +718,94 @@ export const handoverToGovernance = async (extendedClient, deployer, registryAdd
596
718
  }
597
719
  validators = enrichedValidators.filter((v)=>v.status === 0).map((v)=>v.operator);
598
720
  }
599
- if (validators.length > 0) {
600
- const gseContract = new GSEContract(extendedClient, gseAddress);
601
- const multiAdder = await deployer.deploy(MultiAdderArtifact, [
602
- rollupAddress,
603
- deployer.client.account.address
604
- ]);
605
- const makeValidatorTuples = async (validator)=>{
606
- const registrationTuple = await gseContract.makeRegistrationTuple(validator.bn254SecretKey.getValue());
607
- return {
608
- attester: getAddress(validator.attester.toString()),
609
- withdrawer: getAddress(validator.withdrawer.toString()),
610
- ...registrationTuple
611
- };
721
+ if (validators.length === 0) {
722
+ logger.warn('No validators to add. Skipping.');
723
+ return;
724
+ }
725
+ const gseContract = new GSEContract(extendedClient, gseAddress);
726
+ const multiAdder = (await deployer.deploy(MultiAdderArtifact, [
727
+ rollupAddress,
728
+ deployer.client.account.address
729
+ ])).address;
730
+ const makeValidatorTuples = async (validator)=>{
731
+ const registrationTuple = await gseContract.makeRegistrationTuple(validator.bn254SecretKey.getValue());
732
+ return {
733
+ attester: getAddress(validator.attester.toString()),
734
+ withdrawer: getAddress(validator.withdrawer.toString()),
735
+ ...registrationTuple
612
736
  };
613
- const validatorsTuples = await Promise.all(validators.map(makeValidatorTuples));
614
- // Mint tokens, approve them, use cheat code to initialize validator set without setting up the epoch.
615
- const stakeNeeded = activationThreshold * BigInt(validators.length);
737
+ };
738
+ const validatorsTuples = await Promise.all(validators.map(makeValidatorTuples));
739
+ // Mint tokens, approve them, use cheat code to initialize validator set without setting up the epoch.
740
+ const stakeNeeded = activationThreshold * BigInt(validators.length);
741
+ await deployer.l1TxUtils.sendAndMonitorTransaction({
742
+ to: stakingAssetAddress,
743
+ data: encodeFunctionData({
744
+ abi: StakingAssetArtifact.contractAbi,
745
+ functionName: 'mint',
746
+ args: [
747
+ multiAdder.toString(),
748
+ stakeNeeded
749
+ ]
750
+ })
751
+ });
752
+ const entryQueueLengthBefore = await rollup.getEntryQueueLength();
753
+ const validatorCountBefore = await rollup.getActiveAttesterCount();
754
+ logger.info(`Adding ${validators.length} validators to the rollup`);
755
+ const chunkSize = 16;
756
+ // We will add `chunkSize` validators to the queue until we have covered all of our validators.
757
+ // The `chunkSize` needs to be small enough to fit inside a single tx, therefore 16.
758
+ for (const c of chunk(validatorsTuples, chunkSize)){
616
759
  await deployer.l1TxUtils.sendAndMonitorTransaction({
617
- to: stakingAssetAddress,
760
+ to: multiAdder.toString(),
618
761
  data: encodeFunctionData({
619
- abi: StakingAssetArtifact.contractAbi,
620
- functionName: 'mint',
762
+ abi: MultiAdderArtifact.contractAbi,
763
+ functionName: 'addValidators',
621
764
  args: [
622
- multiAdder.toString(),
623
- stakeNeeded
765
+ c,
766
+ BigInt(chunkSize)
624
767
  ]
625
768
  })
769
+ }, {
770
+ gasLimit: 16_000_000n
626
771
  });
627
- const entryQueueLengthBefore = await rollup.getEntryQueueLength();
628
- const validatorCountBefore = await rollup.getActiveAttesterCount();
629
- logger.info(`Adding ${validators.length} validators to the rollup`);
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
- });
772
+ }
773
+ // After adding to the queue, we will now try to flush from it.
774
+ // We are explicitly doing this as a second step instead of as part of adding to benefit
775
+ // from the accounting used to speed the process up.
776
+ // As the queue computes the amount of possible flushes in an epoch when told to flush,
777
+ // waiting until we have added all we want allows us to benefit in the case were we added
778
+ // enough to pass the bootstrap set size without needing to wait another epoch.
779
+ // This is useful when we are testing as it speeds up the tests slightly.
780
+ while(true){
781
+ // If the queue is empty, we can break
782
+ if (await rollup.getEntryQueueLength() == 0n) {
783
+ break;
670
784
  }
671
- const entryQueueLengthAfter = await rollup.getEntryQueueLength();
672
- const validatorCountAfter = await rollup.getActiveAttesterCount();
673
- if (entryQueueLengthAfter + validatorCountAfter < entryQueueLengthBefore + validatorCountBefore + BigInt(validators.length)) {
674
- throw new Error(`Failed to add ${validators.length} validators. Active validators: ${validatorCountBefore} -> ${validatorCountAfter}. Queue: ${entryQueueLengthBefore} -> ${entryQueueLengthAfter}`);
785
+ // If there are no available validator flushes, no need to even try
786
+ if (await rollup.getAvailableValidatorFlushes() == 0n) {
787
+ break;
675
788
  }
676
- logger.info(`Added ${validators.length} validators. Active validators: ${validatorCountBefore} -> ${validatorCountAfter}. Queue: ${entryQueueLengthBefore} -> ${entryQueueLengthAfter}`);
789
+ // Note that we are flushing at most `chunkSize` at each call
790
+ await deployer.l1TxUtils.sendAndMonitorTransaction({
791
+ to: rollup.address,
792
+ data: encodeFunctionData({
793
+ abi: RollupArtifact.contractAbi,
794
+ functionName: 'flushEntryQueue',
795
+ args: [
796
+ BigInt(chunkSize)
797
+ ]
798
+ })
799
+ }, {
800
+ gasLimit: 16_000_000n
801
+ });
802
+ }
803
+ const entryQueueLengthAfter = await rollup.getEntryQueueLength();
804
+ const validatorCountAfter = await rollup.getActiveAttesterCount();
805
+ if (entryQueueLengthAfter + validatorCountAfter < entryQueueLengthBefore + validatorCountBefore + BigInt(validators.length)) {
806
+ throw new Error(`Failed to add ${validators.length} validators. Active validators: ${validatorCountBefore} -> ${validatorCountAfter}. Queue: ${entryQueueLengthBefore} -> ${entryQueueLengthAfter}. A likely issue is the bootstrap size.`);
677
807
  }
808
+ logger.info(`Added ${validators.length} validators. Active validators: ${validatorCountBefore} -> ${validatorCountAfter}. Queue: ${entryQueueLengthBefore} -> ${entryQueueLengthAfter}`);
678
809
  }
679
810
  };
680
811
  /**
@@ -687,11 +818,11 @@ export const handoverToGovernance = async (extendedClient, deployer, registryAdd
687
818
  * @param logger - The logger.
688
819
  */ // eslint-disable-next-line camelcase
689
820
  export const cheat_initializeFeeAssetHandler = async (extendedClient, deployer, feeAssetAddress, logger)=>{
690
- const feeAssetHandlerAddress = await deployer.deploy(FeeAssetHandlerArtifact, [
821
+ const feeAssetHandlerAddress = (await deployer.deploy(FeeAssetHandlerArtifact, [
691
822
  extendedClient.account.address,
692
823
  feeAssetAddress.toString(),
693
824
  BigInt(1e18)
694
- ]);
825
+ ])).address;
695
826
  logger.verbose(`Deployed FeeAssetHandler at ${feeAssetHandlerAddress}`);
696
827
  const { txHash } = await deployer.sendTransaction({
697
828
  to: feeAssetAddress.toString(),
@@ -720,6 +851,9 @@ export const cheat_initializeFeeAssetHandler = async (extendedClient, deployer,
720
851
  */ export const deployL1Contracts = async (rpcUrls, account, chain, logger, args, txUtilsConfig = getL1TxUtilsConfigEnvVars(), createVerificationJson = false)=>{
721
852
  logger.info(`Deploying L1 contracts with config: ${jsonStringify(args)}`);
722
853
  validateConfig(args);
854
+ if (args.initialValidators && args.initialValidators.length > 0 && args.existingTokenAddress) {
855
+ throw new Error('Cannot deploy with both initialValidators and existingTokenAddress. ' + 'Initial validator funding requires minting tokens, which is not possible with an external token.');
856
+ }
723
857
  const l1Client = createExtendedL1Client(rpcUrls, account, chain);
724
858
  // Deploy multicall3 if it does not exist in this network
725
859
  await deployMulticall3(l1Client, logger);
@@ -757,7 +891,7 @@ export const cheat_initializeFeeAssetHandler = async (extendedClient, deployer,
757
891
  logger.verbose('Waiting for rollup and slash factory to be deployed');
758
892
  await deployer.waitForDeployments();
759
893
  // Now that the rollup has been deployed and added to the registry, transfer ownership to governance
760
- await handoverToGovernance(l1Client, deployer, registryAddress, gseAddress, coinIssuerAddress, feeAssetAddress, governanceAddress, logger, args.acceleratedTestDeployments);
894
+ const { dateGatedRelayerAddress } = await handoverToGovernance(l1Client, deployer, registryAddress, gseAddress, coinIssuerAddress, feeAssetAddress, governanceAddress, logger, args.acceleratedTestDeployments, !!args.existingTokenAddress);
761
895
  logger.info(`Handing over to governance complete`);
762
896
  logger.verbose(`All transactions for L1 deployment have been mined`);
763
897
  const l1Contracts = await RegistryContract.collectAddresses(l1Client, registryAddress, 'canonical');
@@ -946,6 +1080,7 @@ export const cheat_initializeFeeAssetHandler = async (extendedClient, deployer,
946
1080
  recursive: true
947
1081
  });
948
1082
  const verificationOutputPath = `${createVerificationJson}/l1-verify-${chain.id}-${formattedDate.slice(0, 6)}-${formattedDate.slice(6)}.json`;
1083
+ const networkName = getActiveNetworkName();
949
1084
  const verificationData = {
950
1085
  chainId: chain.id,
951
1086
  network: networkName,
@@ -990,7 +1125,8 @@ export const cheat_initializeFeeAssetHandler = async (extendedClient, deployer,
990
1125
  feeAssetHandlerAddress,
991
1126
  stakingAssetHandlerAddress,
992
1127
  zkPassportVerifierAddress,
993
- coinIssuerAddress
1128
+ coinIssuerAddress,
1129
+ dateGatedRelayerAddress
994
1130
  }
995
1131
  };
996
1132
  };
@@ -1015,14 +1151,20 @@ export class L1Deployer {
1015
1151
  this.salt = maybeSalt ? padHex(numberToHex(maybeSalt), {
1016
1152
  size: 32
1017
1153
  }) : undefined;
1018
- this.l1TxUtils = createL1TxUtilsFromViemWallet(this.client, this.logger, dateProvider, this.txUtilsConfig, this.acceleratedTestDeployments);
1154
+ this.l1TxUtils = createL1TxUtilsFromViemWallet(this.client, {
1155
+ logger: this.logger,
1156
+ dateProvider
1157
+ }, {
1158
+ ...this.txUtilsConfig,
1159
+ debugMaxGasLimit: acceleratedTestDeployments
1160
+ });
1019
1161
  }
1020
1162
  async deploy(params, args, opts = {}) {
1021
1163
  this.logger.debug(`Deploying ${params.name} contract`, {
1022
1164
  args
1023
1165
  });
1024
1166
  try {
1025
- const { txHash, address, deployedLibraries } = await deployL1Contract(this.client, params.contractAbi, params.contractBytecode, args ?? [], {
1167
+ const { txHash, address, deployedLibraries, existed } = await deployL1Contract(this.client, params.contractAbi, params.contractBytecode, args ?? [], {
1026
1168
  salt: this.salt,
1027
1169
  libraries: params.libraries,
1028
1170
  logger: this.logger,
@@ -1053,7 +1195,10 @@ export class L1Deployer {
1053
1195
  libraries: deployedLibraries ?? []
1054
1196
  });
1055
1197
  }
1056
- return address;
1198
+ return {
1199
+ address,
1200
+ existed
1201
+ };
1057
1202
  } catch (error) {
1058
1203
  throw new Error(`Failed to deploy ${params.name}`, {
1059
1204
  cause: formatViemError(error)
@@ -1083,7 +1228,11 @@ export class L1Deployer {
1083
1228
  });
1084
1229
  }
1085
1230
  sendTransaction(tx, options) {
1086
- return this.l1TxUtils.sendTransaction(tx, options);
1231
+ return this.l1TxUtils.sendTransaction(tx, options).then(({ txHash, state })=>({
1232
+ txHash,
1233
+ gasLimit: state.gasLimit,
1234
+ gasPrice: state.gasPrice
1235
+ }));
1087
1236
  }
1088
1237
  }
1089
1238
  // docs:start:deployL1Contract
@@ -1104,7 +1253,12 @@ export class L1Deployer {
1104
1253
  let { l1TxUtils } = opts;
1105
1254
  if (!l1TxUtils) {
1106
1255
  const config = getL1TxUtilsConfigEnvVars();
1107
- l1TxUtils = createL1TxUtilsFromViemWallet(extendedClient, logger, undefined, config, acceleratedTestDeployments);
1256
+ l1TxUtils = createL1TxUtilsFromViemWallet(extendedClient, {
1257
+ logger
1258
+ }, {
1259
+ ...config,
1260
+ debugMaxGasLimit: acceleratedTestDeployments
1261
+ });
1108
1262
  }
1109
1263
  if (libraries) {
1110
1264
  // Note that this does NOT work well for linked libraries having linked libraries.
@@ -1179,6 +1333,7 @@ export class L1Deployer {
1179
1333
  logger?.verbose(`Skipping waiting for linked libraries to be deployed ${acceleratedTestDeployments ? '(accelerated test deployments)' : ''}`);
1180
1334
  }
1181
1335
  }
1336
+ let existed = false;
1182
1337
  if (saltFromOpts) {
1183
1338
  logger?.info(`Deploying contract with salt ${saltFromOpts}`);
1184
1339
  const { address, paddedSalt: salt, calldata } = getExpectedAddress(abi, bytecode, args, saltFromOpts);
@@ -1221,6 +1376,7 @@ export class L1Deployer {
1221
1376
  logger?.verbose(`Deployed contract with salt ${salt} to address ${resultingAddress} in tx ${txHash}.`);
1222
1377
  } else {
1223
1378
  logger?.verbose(`Skipping existing deployment of contract with salt ${salt} to address ${resultingAddress}`);
1379
+ existed = true;
1224
1380
  }
1225
1381
  } else {
1226
1382
  const deployData = encodeDeployData({
@@ -1241,7 +1397,8 @@ export class L1Deployer {
1241
1397
  return {
1242
1398
  address: EthAddress.fromString(resultingAddress),
1243
1399
  txHash,
1244
- deployedLibraries
1400
+ deployedLibraries,
1401
+ existed
1245
1402
  };
1246
1403
  }
1247
1404
  export function getExpectedAddress(abi, bytecode, args, salt) {