@aztec/ethereum 3.0.0-canary.a9708bd → 3.0.0-devnet.20251212

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 (195) hide show
  1. package/dest/account.d.ts +1 -1
  2. package/dest/chain.d.ts +1 -1
  3. package/dest/client.d.ts +2 -2
  4. package/dest/client.d.ts.map +1 -1
  5. package/dest/config.d.ts +16 -8
  6. package/dest/config.d.ts.map +1 -1
  7. package/dest/config.js +160 -62
  8. package/dest/constants.d.ts +1 -1
  9. package/dest/contracts/empire_base.d.ts +7 -6
  10. package/dest/contracts/empire_base.d.ts.map +1 -1
  11. package/dest/contracts/empire_base.js +1 -1
  12. package/dest/contracts/empire_slashing_proposer.d.ts +7 -6
  13. package/dest/contracts/empire_slashing_proposer.d.ts.map +1 -1
  14. package/dest/contracts/empire_slashing_proposer.js +9 -3
  15. package/dest/contracts/errors.d.ts +1 -1
  16. package/dest/contracts/errors.d.ts.map +1 -1
  17. package/dest/contracts/fee_asset_handler.d.ts +4 -4
  18. package/dest/contracts/fee_asset_handler.d.ts.map +1 -1
  19. package/dest/contracts/fee_juice.d.ts +1 -1
  20. package/dest/contracts/fee_juice.d.ts.map +1 -1
  21. package/dest/contracts/governance.d.ts +16 -16
  22. package/dest/contracts/governance.d.ts.map +1 -1
  23. package/dest/contracts/governance.js +7 -3
  24. package/dest/contracts/governance_proposer.d.ts +6 -6
  25. package/dest/contracts/governance_proposer.d.ts.map +1 -1
  26. package/dest/contracts/governance_proposer.js +9 -4
  27. package/dest/contracts/gse.d.ts +1 -1
  28. package/dest/contracts/gse.d.ts.map +1 -1
  29. package/dest/contracts/inbox.d.ts +1 -1
  30. package/dest/contracts/inbox.d.ts.map +1 -1
  31. package/dest/contracts/index.d.ts +1 -1
  32. package/dest/contracts/multicall.d.ts +5 -7
  33. package/dest/contracts/multicall.d.ts.map +1 -1
  34. package/dest/contracts/multicall.js +6 -4
  35. package/dest/contracts/registry.d.ts +1 -1
  36. package/dest/contracts/registry.d.ts.map +1 -1
  37. package/dest/contracts/rollup.d.ts +83 -72
  38. package/dest/contracts/rollup.d.ts.map +1 -1
  39. package/dest/contracts/rollup.js +144 -139
  40. package/dest/contracts/slasher_contract.d.ts +11 -1
  41. package/dest/contracts/slasher_contract.d.ts.map +1 -1
  42. package/dest/contracts/slasher_contract.js +18 -0
  43. package/dest/contracts/tally_slashing_proposer.d.ts +30 -9
  44. package/dest/contracts/tally_slashing_proposer.d.ts.map +1 -1
  45. package/dest/contracts/tally_slashing_proposer.js +58 -8
  46. package/dest/contracts/utils.d.ts +1 -1
  47. package/dest/deploy_l1_contracts.d.ts +477 -15
  48. package/dest/deploy_l1_contracts.d.ts.map +1 -1
  49. package/dest/deploy_l1_contracts.js +610 -386
  50. package/dest/eth-signer/eth-signer.d.ts +1 -1
  51. package/dest/eth-signer/index.d.ts +1 -1
  52. package/dest/forwarder_proxy.d.ts +32 -0
  53. package/dest/forwarder_proxy.d.ts.map +1 -0
  54. package/dest/forwarder_proxy.js +93 -0
  55. package/dest/l1_artifacts.d.ts +14258 -6015
  56. package/dest/l1_artifacts.d.ts.map +1 -1
  57. package/dest/l1_artifacts.js +10 -5
  58. package/dest/l1_contract_addresses.d.ts +8 -4
  59. package/dest/l1_contract_addresses.d.ts.map +1 -1
  60. package/dest/l1_contract_addresses.js +16 -26
  61. package/dest/l1_reader.d.ts +4 -2
  62. package/dest/l1_reader.d.ts.map +1 -1
  63. package/dest/l1_reader.js +14 -8
  64. package/dest/l1_tx_utils/config.d.ts +59 -0
  65. package/dest/l1_tx_utils/config.d.ts.map +1 -0
  66. package/dest/l1_tx_utils/config.js +96 -0
  67. package/dest/l1_tx_utils/constants.d.ts +6 -0
  68. package/dest/l1_tx_utils/constants.d.ts.map +1 -0
  69. package/dest/l1_tx_utils/constants.js +14 -0
  70. package/dest/l1_tx_utils/factory.d.ts +24 -0
  71. package/dest/l1_tx_utils/factory.d.ts.map +1 -0
  72. package/dest/l1_tx_utils/factory.js +12 -0
  73. package/dest/l1_tx_utils/forwarder_l1_tx_utils.d.ts +41 -0
  74. package/dest/l1_tx_utils/forwarder_l1_tx_utils.d.ts.map +1 -0
  75. package/dest/l1_tx_utils/forwarder_l1_tx_utils.js +48 -0
  76. package/dest/l1_tx_utils/index-blobs.d.ts +3 -0
  77. package/dest/l1_tx_utils/index-blobs.d.ts.map +1 -0
  78. package/dest/l1_tx_utils/index-blobs.js +2 -0
  79. package/dest/l1_tx_utils/index.d.ts +10 -0
  80. package/dest/l1_tx_utils/index.d.ts.map +1 -0
  81. package/dest/l1_tx_utils/index.js +10 -0
  82. package/dest/l1_tx_utils/interfaces.d.ts +76 -0
  83. package/dest/l1_tx_utils/interfaces.d.ts.map +1 -0
  84. package/dest/l1_tx_utils/interfaces.js +4 -0
  85. package/dest/l1_tx_utils/l1_tx_utils.d.ts +94 -0
  86. package/dest/l1_tx_utils/l1_tx_utils.d.ts.map +1 -0
  87. package/dest/l1_tx_utils/l1_tx_utils.js +623 -0
  88. package/dest/l1_tx_utils/l1_tx_utils_with_blobs.d.ts +26 -0
  89. package/dest/l1_tx_utils/l1_tx_utils_with_blobs.d.ts.map +1 -0
  90. package/dest/l1_tx_utils/l1_tx_utils_with_blobs.js +26 -0
  91. package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts +94 -0
  92. package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts.map +1 -0
  93. package/dest/l1_tx_utils/readonly_l1_tx_utils.js +431 -0
  94. package/dest/l1_tx_utils/signer.d.ts +4 -0
  95. package/dest/l1_tx_utils/signer.d.ts.map +1 -0
  96. package/dest/l1_tx_utils/signer.js +16 -0
  97. package/dest/l1_tx_utils/types.d.ts +67 -0
  98. package/dest/l1_tx_utils/types.d.ts.map +1 -0
  99. package/dest/l1_tx_utils/types.js +26 -0
  100. package/dest/l1_tx_utils/utils.d.ts +4 -0
  101. package/dest/l1_tx_utils/utils.d.ts.map +1 -0
  102. package/dest/l1_tx_utils/utils.js +14 -0
  103. package/dest/l1_types.d.ts +1 -1
  104. package/dest/publisher_manager.d.ts +8 -3
  105. package/dest/publisher_manager.d.ts.map +1 -1
  106. package/dest/publisher_manager.js +36 -8
  107. package/dest/queries.d.ts +1 -1
  108. package/dest/queries.d.ts.map +1 -1
  109. package/dest/queries.js +13 -12
  110. package/dest/test/chain_monitor.d.ts +33 -19
  111. package/dest/test/chain_monitor.d.ts.map +1 -1
  112. package/dest/test/chain_monitor.js +100 -33
  113. package/dest/test/delayed_tx_utils.d.ts +3 -3
  114. package/dest/test/delayed_tx_utils.d.ts.map +1 -1
  115. package/dest/test/delayed_tx_utils.js +2 -2
  116. package/dest/test/eth_cheat_codes.d.ts +36 -14
  117. package/dest/test/eth_cheat_codes.d.ts.map +1 -1
  118. package/dest/test/eth_cheat_codes.js +124 -31
  119. package/dest/test/eth_cheat_codes_with_state.d.ts +1 -1
  120. package/dest/test/eth_cheat_codes_with_state.d.ts.map +1 -1
  121. package/dest/test/index.d.ts +1 -1
  122. package/dest/test/rollup_cheat_codes.d.ts +23 -20
  123. package/dest/test/rollup_cheat_codes.d.ts.map +1 -1
  124. package/dest/test/rollup_cheat_codes.js +79 -42
  125. package/dest/test/start_anvil.d.ts +2 -1
  126. package/dest/test/start_anvil.d.ts.map +1 -1
  127. package/dest/test/start_anvil.js +2 -1
  128. package/dest/test/tx_delayer.d.ts +1 -1
  129. package/dest/test/tx_delayer.d.ts.map +1 -1
  130. package/dest/test/tx_delayer.js +3 -2
  131. package/dest/test/upgrade_utils.d.ts +1 -1
  132. package/dest/test/upgrade_utils.d.ts.map +1 -1
  133. package/dest/test/upgrade_utils.js +3 -2
  134. package/dest/types.d.ts +57 -2
  135. package/dest/types.d.ts.map +1 -1
  136. package/dest/utils.d.ts +2 -2
  137. package/dest/utils.d.ts.map +1 -1
  138. package/dest/utils.js +10 -161
  139. package/dest/zkPassportVerifierAddress.d.ts +1 -1
  140. package/dest/zkPassportVerifierAddress.js +1 -1
  141. package/package.json +27 -13
  142. package/src/client.ts +1 -1
  143. package/src/config.ts +177 -65
  144. package/src/contracts/empire_base.ts +7 -6
  145. package/src/contracts/empire_slashing_proposer.ts +18 -8
  146. package/src/contracts/fee_asset_handler.ts +1 -1
  147. package/src/contracts/governance.ts +3 -3
  148. package/src/contracts/governance_proposer.ts +14 -9
  149. package/src/contracts/multicall.ts +12 -10
  150. package/src/contracts/rollup.ts +170 -171
  151. package/src/contracts/slasher_contract.ts +22 -0
  152. package/src/contracts/tally_slashing_proposer.ts +63 -12
  153. package/src/deploy_l1_contracts.ts +610 -337
  154. package/src/forwarder_proxy.ts +108 -0
  155. package/src/l1_artifacts.ts +14 -6
  156. package/src/l1_contract_addresses.ts +17 -26
  157. package/src/l1_reader.ts +17 -9
  158. package/src/l1_tx_utils/README.md +177 -0
  159. package/src/l1_tx_utils/config.ts +161 -0
  160. package/src/l1_tx_utils/constants.ts +18 -0
  161. package/src/l1_tx_utils/factory.ts +64 -0
  162. package/src/l1_tx_utils/forwarder_l1_tx_utils.ts +119 -0
  163. package/src/l1_tx_utils/index-blobs.ts +2 -0
  164. package/src/l1_tx_utils/index.ts +12 -0
  165. package/src/l1_tx_utils/interfaces.ts +86 -0
  166. package/src/l1_tx_utils/l1_tx_utils.ts +738 -0
  167. package/src/l1_tx_utils/l1_tx_utils_with_blobs.ts +77 -0
  168. package/src/l1_tx_utils/readonly_l1_tx_utils.ts +557 -0
  169. package/src/l1_tx_utils/signer.ts +28 -0
  170. package/src/l1_tx_utils/types.ts +85 -0
  171. package/src/l1_tx_utils/utils.ts +16 -0
  172. package/src/publisher_manager.ts +51 -9
  173. package/src/queries.ts +16 -8
  174. package/src/test/chain_monitor.ts +118 -36
  175. package/src/test/delayed_tx_utils.ts +2 -2
  176. package/src/test/eth_cheat_codes.ts +149 -30
  177. package/src/test/rollup_cheat_codes.ts +94 -52
  178. package/src/test/start_anvil.ts +2 -0
  179. package/src/test/tx_delayer.ts +4 -2
  180. package/src/test/upgrade_utils.ts +3 -2
  181. package/src/types.ts +62 -0
  182. package/src/utils.ts +12 -184
  183. package/src/zkPassportVerifierAddress.ts +1 -1
  184. package/dest/index.d.ts +0 -18
  185. package/dest/index.d.ts.map +0 -1
  186. package/dest/index.js +0 -17
  187. package/dest/l1_tx_utils.d.ts +0 -250
  188. package/dest/l1_tx_utils.d.ts.map +0 -1
  189. package/dest/l1_tx_utils.js +0 -826
  190. package/dest/l1_tx_utils_with_blobs.d.ts +0 -19
  191. package/dest/l1_tx_utils_with_blobs.d.ts.map +0 -1
  192. package/dest/l1_tx_utils_with_blobs.js +0 -85
  193. package/src/index.ts +0 -17
  194. package/src/l1_tx_utils.ts +0 -1105
  195. package/src/l1_tx_utils_with_blobs.ts +0 -144
@@ -1,55 +1,171 @@
1
1
  import { L1_TO_L2_MSG_SUBTREE_HEIGHT } from '@aztec/constants';
2
+ import { SlotNumber } from '@aztec/foundation/branded-types';
2
3
  import { getActiveNetworkName } from '@aztec/foundation/config';
3
- import { keccak256String } from '@aztec/foundation/crypto';
4
+ import { keccak256String } from '@aztec/foundation/crypto/keccak';
4
5
  import { EthAddress } from '@aztec/foundation/eth-address';
5
6
  import { jsonStringify } from '@aztec/foundation/json-rpc';
6
7
  import { createLogger } from '@aztec/foundation/log';
7
8
  import { DateProvider } from '@aztec/foundation/timer';
8
- import { mkdir, writeFile } from 'fs/promises';
9
+ import fs from 'fs';
10
+ import chunk from 'lodash.chunk';
9
11
  import { concatHex, encodeAbiParameters, encodeDeployData, encodeFunctionData, getAddress, getContract, getContractAddress, numberToHex, padHex } from 'viem';
10
12
  import { foundry } from 'viem/chains';
11
13
  import { isAnvilTestChain } from './chain.js';
12
14
  import { createExtendedL1Client } from './client.js';
13
- import { getEntryQueueConfig, getGSEConfiguration, getGovernanceConfiguration, getRewardBoostConfig, getRewardConfig, validateConfig } from './config.js';
15
+ import { getEntryQueueConfig, getGovernanceConfiguration, getRewardBoostConfig, getRewardConfig, validateConfig } from './config.js';
14
16
  import { GSEContract } from './contracts/gse.js';
15
17
  import { deployMulticall3 } from './contracts/multicall.js';
16
18
  import { RegistryContract } from './contracts/registry.js';
17
19
  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';
20
+ import { CoinIssuerArtifact, DateGatedRelayerArtifact, FeeAssetArtifact, FeeAssetHandlerArtifact, GSEArtifact, GovernanceArtifact, GovernanceProposerArtifact, MultiAdderArtifact, RegisterNewRollupVersionPayloadArtifact, RegistryArtifact, RollupArtifact, SlashFactoryArtifact, StakingAssetArtifact, StakingAssetHandlerArtifact, l1ArtifactsVerifiers, mockVerifiers } from './l1_artifacts.js';
21
+ import { createL1TxUtilsFromViemWallet, getL1TxUtilsConfigEnvVars } from './l1_tx_utils/index.js';
20
22
  import { formatViemError } from './utils.js';
21
23
  import { ZK_PASSPORT_DOMAIN, ZK_PASSPORT_SCOPE, ZK_PASSPORT_VERIFIER_ADDRESS } from './zkPassportVerifierAddress.js';
22
24
  export const DEPLOYER_ADDRESS = '0x4e59b44847b379578588920cA78FbF26c0B4956C';
23
- const networkName = getActiveNetworkName();
25
+ // Minimal ERC20 ABI for validation purposes. We only read view methods.
26
+ const ERC20_VALIDATION_ABI = [
27
+ {
28
+ type: 'function',
29
+ name: 'totalSupply',
30
+ stateMutability: 'view',
31
+ inputs: [],
32
+ outputs: [
33
+ {
34
+ name: '',
35
+ type: 'uint256'
36
+ }
37
+ ]
38
+ },
39
+ {
40
+ type: 'function',
41
+ name: 'name',
42
+ stateMutability: 'view',
43
+ inputs: [],
44
+ outputs: [
45
+ {
46
+ name: '',
47
+ type: 'string'
48
+ }
49
+ ]
50
+ },
51
+ {
52
+ type: 'function',
53
+ name: 'symbol',
54
+ stateMutability: 'view',
55
+ inputs: [],
56
+ outputs: [
57
+ {
58
+ name: '',
59
+ type: 'string'
60
+ }
61
+ ]
62
+ },
63
+ {
64
+ type: 'function',
65
+ name: 'decimals',
66
+ stateMutability: 'view',
67
+ inputs: [],
68
+ outputs: [
69
+ {
70
+ name: '',
71
+ type: 'uint8'
72
+ }
73
+ ]
74
+ }
75
+ ];
76
+ /**
77
+ * Validates that the provided address points to a contract that resembles an ERC20 token.
78
+ * Checks for contract code and attempts common ERC20 view calls.
79
+ * Throws an error if validation fails.
80
+ */ export async function validateExistingErc20TokenAddress(l1Client, tokenAddress, logger) {
81
+ const addressString = tokenAddress.toString();
82
+ // Ensure there is contract code at the address
83
+ const code = await l1Client.getCode({
84
+ address: addressString
85
+ });
86
+ if (!code || code === '0x') {
87
+ throw new Error(`No contract code found at provided token address ${addressString}`);
88
+ }
89
+ const contract = getContract({
90
+ address: getAddress(addressString),
91
+ abi: ERC20_VALIDATION_ABI,
92
+ client: l1Client
93
+ });
94
+ // Validate all required ERC20 methods in parallel
95
+ const checks = [
96
+ contract.read.totalSupply().then((total)=>typeof total === 'bigint'),
97
+ contract.read.name().then(()=>true),
98
+ contract.read.symbol().then(()=>true),
99
+ contract.read.decimals().then((dec)=>typeof dec === 'number' || typeof dec === 'bigint')
100
+ ];
101
+ const results = await Promise.allSettled(checks);
102
+ const failedChecks = results.filter((result)=>result.status === 'rejected' || result.value !== true);
103
+ if (failedChecks.length > 0) {
104
+ throw new Error(`Address ${addressString} does not appear to implement ERC20 view methods`);
105
+ }
106
+ logger.verbose(`Validated existing token at ${addressString} appears to be ERC20-compatible`);
107
+ }
24
108
  export const deploySharedContracts = async (l1Client, deployer, args, logger)=>{
25
- logger.info(`Deploying shared contracts for network configration: ${networkName}`);
109
+ const networkName = getActiveNetworkName();
110
+ logger.info(`Deploying shared contracts for network configuration: ${networkName}`);
26
111
  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, [
112
+ let feeAssetAddress;
113
+ let stakingAssetAddress;
114
+ if (args.existingTokenAddress) {
115
+ await validateExistingErc20TokenAddress(l1Client, args.existingTokenAddress, logger);
116
+ feeAssetAddress = args.existingTokenAddress;
117
+ stakingAssetAddress = args.existingTokenAddress;
118
+ logger.verbose(`Using existing token for fee and staking assets at ${args.existingTokenAddress}`);
119
+ } else {
120
+ const deployedFee = await deployer.deploy(FeeAssetArtifact, [
121
+ 'FeeJuice',
122
+ 'FEE',
123
+ l1Client.account.address
124
+ ]);
125
+ feeAssetAddress = deployedFee.address;
126
+ logger.verbose(`Deployed Fee Asset at ${feeAssetAddress}`);
127
+ // Mint a tiny bit of tokens to satisfy coin-issuer constraints
128
+ const { txHash } = await deployer.sendTransaction({
129
+ to: feeAssetAddress.toString(),
130
+ data: encodeFunctionData({
131
+ abi: FeeAssetArtifact.contractAbi,
132
+ functionName: 'mint',
133
+ args: [
134
+ l1Client.account.address,
135
+ 1n * 10n ** 18n
136
+ ]
137
+ })
138
+ }, {
139
+ // contract may not have been deployed yet (CREATE2 returns address before mining),
140
+ // which causes gas estimation to fail. Hardcode to 100k which is plenty for ERC20 mint.
141
+ gasLimit: 100_000n
142
+ });
143
+ await l1Client.waitForTransactionReceipt({
144
+ hash: txHash
145
+ });
146
+ logger.verbose(`Minted tiny bit of tokens to satisfy coin-issuer constraints in ${txHash}`);
147
+ const deployedStaking = await deployer.deploy(StakingAssetArtifact, [
148
+ 'Staking',
149
+ 'STK',
150
+ l1Client.account.address
151
+ ]);
152
+ stakingAssetAddress = deployedStaking.address;
153
+ logger.verbose(`Deployed Staking Asset at ${stakingAssetAddress}`);
154
+ await deployer.waitForDeployments();
155
+ }
156
+ const gseAddress = (await deployer.deploy(GSEArtifact, [
41
157
  l1Client.account.address,
42
158
  stakingAssetAddress.toString(),
43
- gseConfiguration.activationThreshold,
44
- gseConfiguration.ejectionThreshold
45
- ]);
159
+ args.activationThreshold,
160
+ args.ejectionThreshold
161
+ ])).address;
46
162
  logger.verbose(`Deployed GSE at ${gseAddress}`);
47
- const registryAddress = await deployer.deploy(RegistryArtifact, [
163
+ const { address: registryAddress } = await deployer.deploy(RegistryArtifact, [
48
164
  l1Client.account.address,
49
165
  feeAssetAddress.toString()
50
166
  ]);
51
167
  logger.verbose(`Deployed Registry at ${registryAddress}`);
52
- const governanceProposerAddress = await deployer.deploy(GovernanceProposerArtifact, [
168
+ const { address: governanceProposerAddress } = await deployer.deploy(GovernanceProposerArtifact, [
53
169
  registryAddress.toString(),
54
170
  gseAddress.toString(),
55
171
  BigInt(args.governanceProposerQuorum ?? args.governanceProposerRoundSize / 2 + 1),
@@ -58,7 +174,7 @@ export const deploySharedContracts = async (l1Client, deployer, args, logger)=>{
58
174
  logger.verbose(`Deployed GovernanceProposer at ${governanceProposerAddress}`);
59
175
  // @note @LHerskind the assets are expected to be the same at some point, but for better
60
176
  // configurability they are different for now.
61
- const governanceAddress = await deployer.deploy(GovernanceArtifact, [
177
+ const { address: governanceAddress } = await deployer.deploy(GovernanceArtifact, [
62
178
  stakingAssetAddress.toString(),
63
179
  governanceProposerAddress.toString(),
64
180
  gseAddress.toString(),
@@ -98,11 +214,16 @@ export const deploySharedContracts = async (l1Client, deployer, args, logger)=>{
98
214
  logger.verbose(`Set governance on GSE in ${txHash}`);
99
215
  txHashes.push(txHash);
100
216
  }
101
- const coinIssuerAddress = await deployer.deploy(CoinIssuerArtifact, [
217
+ logger.verbose(`Waiting for deployments to complete`);
218
+ await deployer.waitForDeployments();
219
+ const coinIssuerAddress = (await deployer.deploy(CoinIssuerArtifact, [
102
220
  feeAssetAddress.toString(),
103
- 1_000_000n * 10n ** 18n,
221
+ 2n * 10n ** 17n,
104
222
  l1Client.account.address
105
- ]);
223
+ ], {
224
+ gasLimit: 1_000_000n,
225
+ noSimulation: true
226
+ })).address;
106
227
  logger.verbose(`Deployed CoinIssuer at ${coinIssuerAddress}`);
107
228
  logger.verbose(`Waiting for deployments to complete`);
108
229
  await deployer.waitForDeployments();
@@ -110,16 +231,17 @@ export const deploySharedContracts = async (l1Client, deployer, args, logger)=>{
110
231
  let feeAssetHandlerAddress = undefined;
111
232
  let stakingAssetHandlerAddress = undefined;
112
233
  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, [
234
+ // Only if not on mainnet will we deploy the handlers, and only when we control the token
235
+ if (l1Client.chain.id !== 1 && !args.existingTokenAddress) {
236
+ /* -------------------------------------------------------------------------- */ /* CHEAT CODES START HERE */ /* -------------------------------------------------------------------------- */ const deployedFeeAssetHandler = await deployer.deploy(FeeAssetHandlerArtifact, [
116
237
  l1Client.account.address,
117
238
  feeAssetAddress.toString(),
118
239
  BigInt(1000n * 10n ** 18n)
119
240
  ]);
241
+ feeAssetHandlerAddress = deployedFeeAssetHandler.address;
120
242
  logger.verbose(`Deployed FeeAssetHandler at ${feeAssetHandlerAddress}`);
121
- // Only if we are "fresh" will we be adding as a minter, otherwise above will simply get same address
122
- if (needToSetGovernance) {
243
+ // Only add as minter if this is a new deployment (not reusing existing handler from failed previous run)
244
+ if (!deployedFeeAssetHandler.existed) {
123
245
  const { txHash } = await deployer.sendTransaction({
124
246
  to: feeAssetAddress.toString(),
125
247
  data: encodeFunctionData({
@@ -147,6 +269,7 @@ export const deploySharedContracts = async (l1Client, deployer, args, logger)=>{
147
269
  stakingAsset: stakingAssetAddress.toString(),
148
270
  registry: registryAddress.toString(),
149
271
  withdrawer: AMIN.toString(),
272
+ validatorsToFlush: 16n,
150
273
  mintInterval: BigInt(60 * 60 * 24),
151
274
  depositsPerMint: BigInt(10),
152
275
  depositMerkleRoot: '0x0000000000000000000000000000000000000000000000000000000000000000',
@@ -161,9 +284,9 @@ export const deploySharedContracts = async (l1Client, deployer, args, logger)=>{
161
284
  skipBindCheck: args.zkPassportArgs?.mockZkPassportVerifier ?? false,
162
285
  skipMerkleCheck: true
163
286
  };
164
- stakingAssetHandlerAddress = await deployer.deploy(StakingAssetHandlerArtifact, [
287
+ stakingAssetHandlerAddress = (await deployer.deploy(StakingAssetHandlerArtifact, [
165
288
  stakingAssetHandlerDeployArgs
166
- ]);
289
+ ])).address;
167
290
  logger.verbose(`Deployed StakingAssetHandler at ${stakingAssetHandlerAddress}`);
168
291
  const { txHash: stakingMinterTxHash } = await deployer.sendTransaction({
169
292
  to: stakingAssetAddress.toString(),
@@ -187,20 +310,24 @@ export const deploySharedContracts = async (l1Client, deployer, args, logger)=>{
187
310
  logger.verbose(`Deployed shared contracts`);
188
311
  const registry = new RegistryContract(l1Client, registryAddress);
189
312
  /* -------------------------------------------------------------------------- */ /* 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}`);
313
+ if (!args.existingTokenAddress) {
314
+ const checkpointReward = getRewardConfig(networkName).checkpointReward;
315
+ const funding = checkpointReward * 200000n;
316
+ const { txHash: fundRewardDistributorTxHash } = await deployer.sendTransaction({
317
+ to: feeAssetAddress.toString(),
318
+ data: encodeFunctionData({
319
+ abi: FeeAssetArtifact.contractAbi,
320
+ functionName: 'mint',
321
+ args: [
322
+ rewardDistributorAddress.toString(),
323
+ funding
324
+ ]
325
+ })
326
+ });
327
+ logger.verbose(`Funded reward distributor with ${funding} fee asset in ${fundRewardDistributorTxHash}`);
328
+ } else {
329
+ logger.verbose(`Skipping reward distributor funding as existing token is provided`);
330
+ }
204
331
  /* -------------------------------------------------------------------------- */ /* FUND REWARD DISTRIBUTOR STOP */ /* -------------------------------------------------------------------------- */ return {
205
332
  feeAssetAddress,
206
333
  feeAssetHandlerAddress,
@@ -217,7 +344,7 @@ export const deploySharedContracts = async (l1Client, deployer, args, logger)=>{
217
344
  };
218
345
  const getZkPassportVerifierAddress = async (deployer, args)=>{
219
346
  if (args.zkPassportArgs?.mockZkPassportVerifier) {
220
- return await deployer.deploy(mockVerifiers.mockZkPassportVerifier);
347
+ return (await deployer.deploy(mockVerifiers.mockZkPassportVerifier)).address;
221
348
  }
222
349
  return ZK_PASSPORT_VERIFIER_ADDRESS;
223
350
  };
@@ -233,6 +360,224 @@ const getZkPassportVerifierAddress = async (deployer, args)=>{
233
360
  scope
234
361
  ];
235
362
  };
363
+ /**
364
+ * Generates verification records for a deployed rollup and its associated contracts (Inbox, Outbox, Slasher, etc).
365
+ * @param rollup - The deployed rollup contract.
366
+ * @param deployer - The L1 deployer instance.
367
+ * @param args - The deployment arguments used for the rollup.
368
+ * @param addresses - The L1 contract addresses.
369
+ * @param extendedClient - The extended viem wallet client.
370
+ * @param logger - The logger.
371
+ */ async function generateRollupVerificationRecords(rollup, deployer, args, addresses, extendedClient, logger) {
372
+ try {
373
+ // Add Inbox / Outbox verification records (constructor args are created inside RollupCore)
374
+ const rollupAddr = rollup.address;
375
+ const rollupAddresses = await rollup.getRollupAddresses();
376
+ const inboxAddr = rollupAddresses.inboxAddress.toString();
377
+ const outboxAddr = rollupAddresses.outboxAddress.toString();
378
+ const feeAsset = rollupAddresses.feeJuiceAddress.toString();
379
+ const version = await rollup.getVersion();
380
+ const inboxCtor = encodeAbiParameters([
381
+ {
382
+ type: 'address'
383
+ },
384
+ {
385
+ type: 'address'
386
+ },
387
+ {
388
+ type: 'uint256'
389
+ },
390
+ {
391
+ type: 'uint256'
392
+ }
393
+ ], [
394
+ rollupAddr,
395
+ feeAsset,
396
+ version,
397
+ BigInt(L1_TO_L2_MSG_SUBTREE_HEIGHT)
398
+ ]);
399
+ const outboxCtor = encodeAbiParameters([
400
+ {
401
+ type: 'address'
402
+ },
403
+ {
404
+ type: 'uint256'
405
+ }
406
+ ], [
407
+ rollupAddr,
408
+ version
409
+ ]);
410
+ deployer.verificationRecords.push({
411
+ name: 'Inbox',
412
+ address: inboxAddr,
413
+ constructorArgsHex: inboxCtor,
414
+ libraries: []
415
+ }, {
416
+ name: 'Outbox',
417
+ address: outboxAddr,
418
+ constructorArgsHex: outboxCtor,
419
+ libraries: []
420
+ });
421
+ // Include Slasher and SlashingProposer (if deployed) in verification data
422
+ try {
423
+ const slasherAddrHex = await rollup.getSlasherAddress();
424
+ const slasherAddr = EthAddress.fromString(slasherAddrHex);
425
+ if (!slasherAddr.isZero()) {
426
+ // Slasher constructor: (address _vetoer, address _governance)
427
+ const slasherCtor = encodeAbiParameters([
428
+ {
429
+ type: 'address'
430
+ },
431
+ {
432
+ type: 'address'
433
+ }
434
+ ], [
435
+ args.slashingVetoer.toString(),
436
+ extendedClient.account.address
437
+ ]);
438
+ deployer.verificationRecords.push({
439
+ name: 'Slasher',
440
+ address: slasherAddr.toString(),
441
+ constructorArgsHex: slasherCtor,
442
+ libraries: []
443
+ });
444
+ // Proposer address is stored in Slasher.PROPOSER()
445
+ const proposerAddr = (await rollup.getSlashingProposerAddress()).toString();
446
+ // Compute constructor args matching deployment path in RollupCore
447
+ const computedRoundSize = BigInt(args.slashingRoundSizeInEpochs * args.aztecEpochDuration);
448
+ const computedQuorum = BigInt(args.slashingQuorum ?? args.slashingRoundSizeInEpochs * args.aztecEpochDuration / 2 + 1);
449
+ const lifetimeInRounds = BigInt(args.slashingLifetimeInRounds);
450
+ const executionDelayInRounds = BigInt(args.slashingExecutionDelayInRounds);
451
+ if (args.slasherFlavor === 'tally') {
452
+ const slashAmounts = [
453
+ args.slashAmountSmall,
454
+ args.slashAmountMedium,
455
+ args.slashAmountLarge
456
+ ];
457
+ const committeeSize = BigInt(args.aztecTargetCommitteeSize);
458
+ const epochDuration = BigInt(args.aztecEpochDuration);
459
+ const slashOffsetInRounds = BigInt(args.slashingOffsetInRounds);
460
+ const proposerCtor = encodeAbiParameters([
461
+ {
462
+ type: 'address'
463
+ },
464
+ {
465
+ type: 'address'
466
+ },
467
+ {
468
+ type: 'uint256'
469
+ },
470
+ {
471
+ type: 'uint256'
472
+ },
473
+ {
474
+ type: 'uint256'
475
+ },
476
+ {
477
+ type: 'uint256'
478
+ },
479
+ {
480
+ type: 'uint256[3]'
481
+ },
482
+ {
483
+ type: 'uint256'
484
+ },
485
+ {
486
+ type: 'uint256'
487
+ },
488
+ {
489
+ type: 'uint256'
490
+ }
491
+ ], [
492
+ rollup.address,
493
+ slasherAddr.toString(),
494
+ computedQuorum,
495
+ computedRoundSize,
496
+ lifetimeInRounds,
497
+ executionDelayInRounds,
498
+ slashAmounts,
499
+ committeeSize,
500
+ epochDuration,
501
+ slashOffsetInRounds
502
+ ]);
503
+ deployer.verificationRecords.push({
504
+ name: 'TallySlashingProposer',
505
+ address: proposerAddr,
506
+ constructorArgsHex: proposerCtor,
507
+ libraries: []
508
+ });
509
+ } else if (args.slasherFlavor === 'empire') {
510
+ const proposerCtor = encodeAbiParameters([
511
+ {
512
+ type: 'address'
513
+ },
514
+ {
515
+ type: 'address'
516
+ },
517
+ {
518
+ type: 'uint256'
519
+ },
520
+ {
521
+ type: 'uint256'
522
+ },
523
+ {
524
+ type: 'uint256'
525
+ },
526
+ {
527
+ type: 'uint256'
528
+ }
529
+ ], [
530
+ rollup.address,
531
+ slasherAddr.toString(),
532
+ computedQuorum,
533
+ computedRoundSize,
534
+ lifetimeInRounds,
535
+ executionDelayInRounds
536
+ ]);
537
+ deployer.verificationRecords.push({
538
+ name: 'EmpireSlashingProposer',
539
+ address: proposerAddr,
540
+ constructorArgsHex: proposerCtor,
541
+ libraries: []
542
+ });
543
+ }
544
+ }
545
+ } catch (e) {
546
+ logger.warn(`Failed to add Slasher/Proposer verification records: ${String(e)}`);
547
+ }
548
+ } catch (e) {
549
+ throw new Error(`Failed to generate rollup verification records: ${String(e)}`);
550
+ }
551
+ }
552
+ /**
553
+ * Writes verification records to a JSON file for later forge verify.
554
+ * @param deployer - The L1 deployer containing verification records.
555
+ * @param outputDirectory - The directory to write the verification file to.
556
+ * @param chainId - The chain ID.
557
+ * @param filenameSuffix - Optional suffix for the filename (e.g., 'upgrade').
558
+ * @param logger - The logger.
559
+ */ async function writeVerificationJson(deployer, outputDirectory, chainId, filenameSuffix = '', logger) {
560
+ try {
561
+ const date = new Date();
562
+ const formattedDate = date.toISOString().slice(2, 19).replace(/[-T:]/g, '');
563
+ // Ensure the verification output directory exists
564
+ await fs.promises.mkdir(outputDirectory, {
565
+ recursive: true
566
+ });
567
+ const suffix = filenameSuffix ? `-${filenameSuffix}` : '';
568
+ const verificationOutputPath = `${outputDirectory}/l1-verify${suffix}-${chainId}-${formattedDate.slice(0, 6)}-${formattedDate.slice(6)}.json`;
569
+ const networkName = getActiveNetworkName();
570
+ const verificationData = {
571
+ chainId: chainId,
572
+ network: networkName,
573
+ records: deployer.verificationRecords
574
+ };
575
+ await fs.promises.writeFile(verificationOutputPath, JSON.stringify(verificationData, null, 2));
576
+ logger.info(`Wrote L1 verification data to ${verificationOutputPath}`);
577
+ } catch (e) {
578
+ logger.warn(`Failed to write L1 verification data file: ${String(e)}`);
579
+ }
580
+ }
236
581
  /**
237
582
  * Deploys a new rollup, using the existing canonical version to derive certain values (addresses of assets etc).
238
583
  * @param clients - The L1 clients.
@@ -240,28 +585,34 @@ const getZkPassportVerifierAddress = async (deployer, args)=>{
240
585
  * @param registryAddress - The address of the registry.
241
586
  * @param logger - The logger.
242
587
  * @param txUtilsConfig - The L1 tx utils config.
243
- */ export const deployRollupForUpgrade = async (extendedClient, args, registryAddress, logger, txUtilsConfig)=>{
244
- const deployer = new L1Deployer(extendedClient, args.salt, undefined, args.acceleratedTestDeployments, logger, txUtilsConfig);
588
+ * @param createVerificationJson - Optional path to write verification data for forge verify.
589
+ */ export const deployRollupForUpgrade = async (extendedClient, args, registryAddress, logger, txUtilsConfig, createVerificationJson = false)=>{
590
+ const deployer = new L1Deployer(extendedClient, args.salt, undefined, args.acceleratedTestDeployments, logger, txUtilsConfig, !!createVerificationJson);
245
591
  const addresses = await RegistryContract.collectAddresses(extendedClient, registryAddress, 'canonical');
246
592
  const { rollup, slashFactoryAddress } = await deployRollup(extendedClient, deployer, args, addresses, logger);
247
593
  await deployer.waitForDeployments();
594
+ // Write verification data (constructor args + linked libraries) to file for later forge verify
595
+ if (createVerificationJson) {
596
+ await generateRollupVerificationRecords(rollup, deployer, args, addresses, extendedClient, logger);
597
+ await writeVerificationJson(deployer, createVerificationJson, extendedClient.chain.id, 'upgrade', logger);
598
+ }
248
599
  return {
249
600
  rollup,
250
601
  slashFactoryAddress
251
602
  };
252
603
  };
253
604
  export const deploySlashFactory = async (deployer, rollupAddress, logger)=>{
254
- const slashFactoryAddress = await deployer.deploy(SlashFactoryArtifact, [
605
+ const slashFactoryAddress = (await deployer.deploy(SlashFactoryArtifact, [
255
606
  rollupAddress
256
- ]);
607
+ ])).address;
257
608
  logger.verbose(`Deployed SlashFactory at ${slashFactoryAddress}`);
258
609
  return slashFactoryAddress;
259
610
  };
260
611
  export const deployUpgradePayload = async (deployer, addresses)=>{
261
- const payloadAddress = await deployer.deploy(RegisterNewRollupVersionPayloadArtifact, [
612
+ const payloadAddress = (await deployer.deploy(RegisterNewRollupVersionPayloadArtifact, [
262
613
  addresses.registryAddress.toString(),
263
614
  addresses.rollupAddress.toString()
264
- ]);
615
+ ])).address;
265
616
  return payloadAddress;
266
617
  };
267
618
  function slasherFlavorToSolidityEnum(flavor) {
@@ -285,14 +636,15 @@ function slasherFlavorToSolidityEnum(flavor) {
285
636
  if (!addresses.gseAddress) {
286
637
  throw new Error('GSE address is required when deploying');
287
638
  }
639
+ const networkName = getActiveNetworkName();
288
640
  logger.info(`Deploying rollup using network configuration: ${networkName}`);
289
641
  const txHashes = [];
290
642
  let epochProofVerifier = EthAddress.ZERO;
291
643
  if (args.realVerifier) {
292
- epochProofVerifier = await deployer.deploy(l1ArtifactsVerifiers.honkVerifier);
644
+ epochProofVerifier = (await deployer.deploy(l1ArtifactsVerifiers.honkVerifier)).address;
293
645
  logger.verbose(`Rollup will use the real verifier at ${epochProofVerifier}`);
294
646
  } else {
295
- epochProofVerifier = await deployer.deploy(mockVerifiers.mockVerifier);
647
+ epochProofVerifier = (await deployer.deploy(mockVerifiers.mockVerifier)).address;
296
648
  logger.verbose(`Rollup will use the mock verifier at ${epochProofVerifier}`);
297
649
  }
298
650
  const rewardConfig = {
@@ -303,6 +655,8 @@ function slasherFlavorToSolidityEnum(flavor) {
303
655
  aztecSlotDuration: BigInt(args.aztecSlotDuration),
304
656
  aztecEpochDuration: BigInt(args.aztecEpochDuration),
305
657
  targetCommitteeSize: BigInt(args.aztecTargetCommitteeSize),
658
+ lagInEpochsForValidatorSet: BigInt(args.lagInEpochsForValidatorSet),
659
+ lagInEpochsForRandao: BigInt(args.lagInEpochsForRandao),
306
660
  aztecProofSubmissionEpochs: BigInt(args.aztecProofSubmissionEpochs),
307
661
  slashingQuorum: BigInt(args.slashingQuorum ?? args.slashingRoundSizeInEpochs * args.aztecEpochDuration / 2 + 1),
308
662
  slashingRoundSize: BigInt(args.slashingRoundSizeInEpochs * args.aztecEpochDuration),
@@ -313,7 +667,7 @@ function slasherFlavorToSolidityEnum(flavor) {
313
667
  provingCostPerMana: args.provingCostPerMana,
314
668
  rewardConfig: rewardConfig,
315
669
  version: 0,
316
- rewardBoostConfig: getRewardBoostConfig(networkName),
670
+ rewardBoostConfig: getRewardBoostConfig(),
317
671
  stakingQueueConfig: getEntryQueueConfig(networkName),
318
672
  exitDelaySeconds: BigInt(args.exitDelaySeconds),
319
673
  slasherFlavor: slasherFlavorToSolidityEnum(args.slasherFlavor),
@@ -322,11 +676,14 @@ function slasherFlavorToSolidityEnum(flavor) {
322
676
  args.slashAmountSmall,
323
677
  args.slashAmountMedium,
324
678
  args.slashAmountLarge
325
- ]
679
+ ],
680
+ localEjectionThreshold: args.localEjectionThreshold,
681
+ slashingDisableDuration: BigInt(args.slashingDisableDuration ?? 0n),
682
+ earliestRewardsClaimableTimestamp: 0n
326
683
  };
327
684
  const genesisStateArgs = {
328
685
  vkTreeRoot: args.vkTreeRoot.toString(),
329
- protocolContractTreeRoot: args.protocolContractTreeRoot.toString(),
686
+ protocolContractsHash: args.protocolContractsHash.toString(),
330
687
  genesisArchiveRoot: args.genesisArchiveRoot.toString()
331
688
  };
332
689
  // Until there is an actual chain-id for the version, we will just draw a random value.
@@ -345,33 +702,38 @@ function slasherFlavorToSolidityEnum(flavor) {
345
702
  genesisStateArgs,
346
703
  rollupConfigArgs
347
704
  ];
348
- const rollupAddress = await deployer.deploy(RollupArtifact, rollupArgs, {
705
+ const { address: rollupAddress, existed: rollupExisted } = await deployer.deploy(RollupArtifact, rollupArgs, {
349
706
  gasLimit: 15_000_000n
350
707
  });
351
- logger.verbose(`Deployed Rollup at ${rollupAddress}`, rollupConfigArgs);
708
+ logger.verbose(`Deployed Rollup at ${rollupAddress}, already existed: ${rollupExisted}`, rollupConfigArgs);
352
709
  const rollupContract = new RollupContract(extendedClient, rollupAddress);
353
710
  await deployer.waitForDeployments();
354
711
  logger.verbose(`All core contracts have been deployed`);
355
712
  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);
713
+ // Skip funding when using an external token, as we likely don't have mint permissions
714
+ if (!('existingTokenAddress' in args) || !args.existingTokenAddress) {
715
+ const feeJuicePortalAddress = await rollupContract.getFeeJuicePortal();
716
+ // In fast mode, use the L1TxUtils to send transactions with nonce management
717
+ const { txHash: mintTxHash } = await deployer.sendTransaction({
718
+ to: addresses.feeJuiceAddress.toString(),
719
+ data: encodeFunctionData({
720
+ abi: FeeAssetArtifact.contractAbi,
721
+ functionName: 'mint',
722
+ args: [
723
+ feeJuicePortalAddress.toString(),
724
+ args.feeJuicePortalInitialBalance
725
+ ]
726
+ })
727
+ });
728
+ logger.verbose(`Funding fee juice portal with ${args.feeJuicePortalInitialBalance} fee juice in ${mintTxHash} (accelerated test deployments)`);
729
+ txHashes.push(mintTxHash);
730
+ } else {
731
+ logger.verbose('Skipping fee juice portal funding due to external token usage');
732
+ }
371
733
  }
372
- const slashFactoryAddress = await deployer.deploy(SlashFactoryArtifact, [
734
+ const slashFactoryAddress = (await deployer.deploy(SlashFactoryArtifact, [
373
735
  rollupAddress.toString()
374
- ]);
736
+ ])).address;
375
737
  logger.verbose(`Deployed SlashFactory at ${slashFactoryAddress}`);
376
738
  // We need to call a function on the registry to set the various contract addresses.
377
739
  const registryContract = getContract({
@@ -435,7 +797,11 @@ function slasherFlavorToSolidityEnum(flavor) {
435
797
  } else {
436
798
  logger.verbose(`Not the owner of the gse, skipping rollup addition`);
437
799
  }
438
- if (args.initialValidators && await gseContract.read.isRollupRegistered([
800
+ const activeAttestorCount = await rollupContract.getActiveAttesterCount();
801
+ const queuedAttestorCount = await rollupContract.getEntryQueueLength();
802
+ logger.info(`Rollup has ${activeAttestorCount} active attestors and ${queuedAttestorCount} queued attestors`);
803
+ const shouldAddValidators = activeAttestorCount === 0n && queuedAttestorCount === 0n;
804
+ if (args.initialValidators && shouldAddValidators && await gseContract.read.isRollupRegistered([
439
805
  rollupContract.address
440
806
  ])) {
441
807
  await addMultipleValidators(extendedClient, deployer, addresses.gseAddress.toString(), rollupAddress.toString(), addresses.stakingAssetAddress.toString(), args.initialValidators, args.acceleratedTestDeployments, logger);
@@ -467,7 +833,7 @@ function slasherFlavorToSolidityEnum(flavor) {
467
833
  slashFactoryAddress
468
834
  };
469
835
  };
470
- export const handoverToGovernance = async (extendedClient, deployer, registryAddress, gseAddress, coinIssuerAddress, feeAssetAddress, governanceAddress, logger, acceleratedTestDeployments)=>{
836
+ export const handoverToGovernance = async (extendedClient, deployer, registryAddress, gseAddress, coinIssuerAddress, feeAssetAddress, governanceAddress, logger, acceleratedTestDeployments, useExternalToken = false)=>{
471
837
  // We need to call a function on the registry to set the various contract addresses.
472
838
  const registryContract = getContract({
473
839
  address: getAddress(registryAddress.toString()),
@@ -522,7 +888,7 @@ export const handoverToGovernance = async (extendedClient, deployer, registryAdd
522
888
  logger.verbose(`Transferring the ownership of the gse contract at ${gseAddress} to the Governance ${governanceAddress} in tx ${transferOwnershipTxHash}`);
523
889
  txHashes.push(transferOwnershipTxHash);
524
890
  }
525
- if (acceleratedTestDeployments || await feeAsset.read.owner() !== coinIssuerAddress.toString()) {
891
+ if (!useExternalToken && (acceleratedTestDeployments || await feeAsset.read.owner() !== coinIssuerAddress.toString())) {
526
892
  const { txHash } = await deployer.sendTransaction({
527
893
  to: feeAssetAddress.toString(),
528
894
  data: encodeFunctionData({
@@ -548,20 +914,27 @@ export const handoverToGovernance = async (extendedClient, deployer, registryAdd
548
914
  });
549
915
  logger.verbose(`Accept ownership of fee asset in ${acceptTokenOwnershipTxHash}`);
550
916
  txHashes.push(acceptTokenOwnershipTxHash);
917
+ } else if (useExternalToken) {
918
+ logger.verbose('Skipping fee asset ownership transfer due to external token usage');
551
919
  }
920
+ // Either deploy or at least predict the address of the date gated relayer
921
+ const dateGatedRelayer = await deployer.deploy(DateGatedRelayerArtifact, [
922
+ governanceAddress.toString(),
923
+ 1798761600n
924
+ ]);
552
925
  // If the owner is not the Governance contract, transfer ownership to the Governance contract
553
- if (acceleratedTestDeployments || await coinIssuerContract.read.owner() !== getAddress(governanceAddress.toString())) {
926
+ if (acceleratedTestDeployments || await coinIssuerContract.read.owner() === deployer.client.account.address) {
554
927
  const { txHash: transferOwnershipTxHash } = await deployer.sendTransaction({
555
928
  to: coinIssuerContract.address,
556
929
  data: encodeFunctionData({
557
930
  abi: CoinIssuerArtifact.contractAbi,
558
931
  functionName: 'transferOwnership',
559
932
  args: [
560
- getAddress(governanceAddress.toString())
933
+ getAddress(dateGatedRelayer.address.toString())
561
934
  ]
562
935
  })
563
936
  });
564
- logger.verbose(`Transferring the ownership of the coin issuer contract at ${coinIssuerAddress} to the Governance ${governanceAddress} in tx ${transferOwnershipTxHash}`);
937
+ logger.verbose(`Transferring the ownership of the coin issuer contract at ${coinIssuerAddress} to the DateGatedRelayer ${dateGatedRelayer.address} in tx ${transferOwnershipTxHash}`);
565
938
  txHashes.push(transferOwnershipTxHash);
566
939
  }
567
940
  // Wait for all actions to be mined
@@ -569,6 +942,9 @@ export const handoverToGovernance = async (extendedClient, deployer, registryAdd
569
942
  await Promise.all(txHashes.map((txHash)=>extendedClient.waitForTransactionReceipt({
570
943
  hash: txHash
571
944
  })));
945
+ return {
946
+ dateGatedRelayerAddress: dateGatedRelayer.address
947
+ };
572
948
  };
573
949
  /*
574
950
  * Adds multiple validators to the rollup
@@ -596,85 +972,94 @@ export const handoverToGovernance = async (extendedClient, deployer, registryAdd
596
972
  }
597
973
  validators = enrichedValidators.filter((v)=>v.status === 0).map((v)=>v.operator);
598
974
  }
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
- };
975
+ if (validators.length === 0) {
976
+ logger.warn('No validators to add. Skipping.');
977
+ return;
978
+ }
979
+ const gseContract = new GSEContract(extendedClient, gseAddress);
980
+ const multiAdder = (await deployer.deploy(MultiAdderArtifact, [
981
+ rollupAddress,
982
+ deployer.client.account.address
983
+ ])).address;
984
+ const makeValidatorTuples = async (validator)=>{
985
+ const registrationTuple = await gseContract.makeRegistrationTuple(validator.bn254SecretKey.getValue());
986
+ return {
987
+ attester: getAddress(validator.attester.toString()),
988
+ withdrawer: getAddress(validator.withdrawer.toString()),
989
+ ...registrationTuple
612
990
  };
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);
991
+ };
992
+ const validatorsTuples = await Promise.all(validators.map(makeValidatorTuples));
993
+ // Mint tokens, approve them, use cheat code to initialize validator set without setting up the epoch.
994
+ const stakeNeeded = activationThreshold * BigInt(validators.length);
995
+ await deployer.l1TxUtils.sendAndMonitorTransaction({
996
+ to: stakingAssetAddress,
997
+ data: encodeFunctionData({
998
+ abi: StakingAssetArtifact.contractAbi,
999
+ functionName: 'mint',
1000
+ args: [
1001
+ multiAdder.toString(),
1002
+ stakeNeeded
1003
+ ]
1004
+ })
1005
+ });
1006
+ const entryQueueLengthBefore = await rollup.getEntryQueueLength();
1007
+ const validatorCountBefore = await rollup.getActiveAttesterCount();
1008
+ logger.info(`Adding ${validators.length} validators to the rollup`);
1009
+ const chunkSize = 16;
1010
+ // We will add `chunkSize` validators to the queue until we have covered all of our validators.
1011
+ // The `chunkSize` needs to be small enough to fit inside a single tx, therefore 16.
1012
+ for (const c of chunk(validatorsTuples, chunkSize)){
616
1013
  await deployer.l1TxUtils.sendAndMonitorTransaction({
617
- to: stakingAssetAddress,
1014
+ to: multiAdder.toString(),
618
1015
  data: encodeFunctionData({
619
- abi: StakingAssetArtifact.contractAbi,
620
- functionName: 'mint',
1016
+ abi: MultiAdderArtifact.contractAbi,
1017
+ functionName: 'addValidators',
621
1018
  args: [
622
- multiAdder.toString(),
623
- stakeNeeded
1019
+ c,
1020
+ BigInt(0)
624
1021
  ]
625
1022
  })
1023
+ }, {
1024
+ gasLimit: 16_000_000n
626
1025
  });
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
- });
1026
+ }
1027
+ // After adding to the queue, we will now try to flush from it.
1028
+ // We are explicitly doing this as a second step instead of as part of adding to benefit
1029
+ // from the accounting used to speed the process up.
1030
+ // As the queue computes the amount of possible flushes in an epoch when told to flush,
1031
+ // waiting until we have added all we want allows us to benefit in the case were we added
1032
+ // enough to pass the bootstrap set size without needing to wait another epoch.
1033
+ // This is useful when we are testing as it speeds up the tests slightly.
1034
+ while(true){
1035
+ // If the queue is empty, we can break
1036
+ if (await rollup.getEntryQueueLength() == 0n) {
1037
+ break;
670
1038
  }
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}`);
1039
+ // If there are no available validator flushes, no need to even try
1040
+ if (await rollup.getAvailableValidatorFlushes() == 0n) {
1041
+ break;
675
1042
  }
676
- logger.info(`Added ${validators.length} validators. Active validators: ${validatorCountBefore} -> ${validatorCountAfter}. Queue: ${entryQueueLengthBefore} -> ${entryQueueLengthAfter}`);
1043
+ // Note that we are flushing at most `chunkSize` at each call
1044
+ await deployer.l1TxUtils.sendAndMonitorTransaction({
1045
+ to: rollup.address,
1046
+ data: encodeFunctionData({
1047
+ abi: RollupArtifact.contractAbi,
1048
+ functionName: 'flushEntryQueue',
1049
+ args: [
1050
+ BigInt(chunkSize)
1051
+ ]
1052
+ })
1053
+ }, {
1054
+ gasLimit: 16_000_000n
1055
+ });
1056
+ }
1057
+ const entryQueueLengthAfter = await rollup.getEntryQueueLength();
1058
+ const validatorCountAfter = await rollup.getActiveAttesterCount();
1059
+ if (entryQueueLengthAfter + validatorCountAfter < entryQueueLengthBefore + validatorCountBefore + BigInt(validators.length)) {
1060
+ throw new Error(`Failed to add ${validators.length} validators. Active validators: ${validatorCountBefore} -> ${validatorCountAfter}. Queue: ${entryQueueLengthBefore} -> ${entryQueueLengthAfter}. A likely issue is the bootstrap size.`);
677
1061
  }
1062
+ logger.info(`Added ${validators.length} validators. Active validators: ${validatorCountBefore} -> ${validatorCountAfter}. Queue: ${entryQueueLengthBefore} -> ${entryQueueLengthAfter}`);
678
1063
  }
679
1064
  };
680
1065
  /**
@@ -687,11 +1072,11 @@ export const handoverToGovernance = async (extendedClient, deployer, registryAdd
687
1072
  * @param logger - The logger.
688
1073
  */ // eslint-disable-next-line camelcase
689
1074
  export const cheat_initializeFeeAssetHandler = async (extendedClient, deployer, feeAssetAddress, logger)=>{
690
- const feeAssetHandlerAddress = await deployer.deploy(FeeAssetHandlerArtifact, [
1075
+ const feeAssetHandlerAddress = (await deployer.deploy(FeeAssetHandlerArtifact, [
691
1076
  extendedClient.account.address,
692
1077
  feeAssetAddress.toString(),
693
1078
  BigInt(1e18)
694
- ]);
1079
+ ])).address;
695
1080
  logger.verbose(`Deployed FeeAssetHandler at ${feeAssetHandlerAddress}`);
696
1081
  const { txHash } = await deployer.sendTransaction({
697
1082
  to: feeAssetAddress.toString(),
@@ -720,6 +1105,9 @@ export const cheat_initializeFeeAssetHandler = async (extendedClient, deployer,
720
1105
  */ export const deployL1Contracts = async (rpcUrls, account, chain, logger, args, txUtilsConfig = getL1TxUtilsConfigEnvVars(), createVerificationJson = false)=>{
721
1106
  logger.info(`Deploying L1 contracts with config: ${jsonStringify(args)}`);
722
1107
  validateConfig(args);
1108
+ if (args.initialValidators && args.initialValidators.length > 0 && args.existingTokenAddress) {
1109
+ throw new Error('Cannot deploy with both initialValidators and existingTokenAddress. ' + 'Initial validator funding requires minting tokens, which is not possible with an external token.');
1110
+ }
723
1111
  const l1Client = createExtendedL1Client(rpcUrls, account, chain);
724
1112
  // Deploy multicall3 if it does not exist in this network
725
1113
  await deployMulticall3(l1Client, logger);
@@ -757,205 +1145,15 @@ export const cheat_initializeFeeAssetHandler = async (extendedClient, deployer,
757
1145
  logger.verbose('Waiting for rollup and slash factory to be deployed');
758
1146
  await deployer.waitForDeployments();
759
1147
  // 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);
1148
+ const { dateGatedRelayerAddress } = await handoverToGovernance(l1Client, deployer, registryAddress, gseAddress, coinIssuerAddress, feeAssetAddress, governanceAddress, logger, args.acceleratedTestDeployments, !!args.existingTokenAddress);
761
1149
  logger.info(`Handing over to governance complete`);
762
1150
  logger.verbose(`All transactions for L1 deployment have been mined`);
763
1151
  const l1Contracts = await RegistryContract.collectAddresses(l1Client, registryAddress, 'canonical');
764
1152
  logger.info(`Aztec L1 contracts initialized`, l1Contracts);
765
1153
  // Write verification data (constructor args + linked libraries) to file for later forge verify
766
1154
  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
- }
1155
+ await generateRollupVerificationRecords(rollup, deployer, args, l1Contracts, l1Client, logger);
1156
+ await writeVerificationJson(deployer, createVerificationJson, chain.id, '', logger);
959
1157
  }
960
1158
  if (isAnvilTestChain(chain.id)) {
961
1159
  // @note We make a time jump PAST the very first slot to not have to deal with the edge case of the first slot.
@@ -963,8 +1161,8 @@ export const cheat_initializeFeeAssetHandler = async (extendedClient, deployer,
963
1161
  try {
964
1162
  // Need to get the time
965
1163
  const currentSlot = await rollup.getSlotNumber();
966
- if (BigInt(currentSlot) === 0n) {
967
- const ts = Number(await rollup.getTimestampForSlot(1n));
1164
+ if (currentSlot === 0) {
1165
+ const ts = Number(await rollup.getTimestampForSlot(SlotNumber(1)));
968
1166
  await rpcCall('evm_setNextBlockTimestamp', [
969
1167
  ts
970
1168
  ]);
@@ -972,7 +1170,7 @@ export const cheat_initializeFeeAssetHandler = async (extendedClient, deployer,
972
1170
  1
973
1171
  ]);
974
1172
  const currentSlot = await rollup.getSlotNumber();
975
- if (BigInt(currentSlot) !== 1n) {
1173
+ if (currentSlot !== 1) {
976
1174
  throw new Error(`Error jumping time: current slot is ${currentSlot}`);
977
1175
  }
978
1176
  logger.info(`Jumped to slot 1`);
@@ -990,7 +1188,8 @@ export const cheat_initializeFeeAssetHandler = async (extendedClient, deployer,
990
1188
  feeAssetHandlerAddress,
991
1189
  stakingAssetHandlerAddress,
992
1190
  zkPassportVerifierAddress,
993
- coinIssuerAddress
1191
+ coinIssuerAddress,
1192
+ dateGatedRelayerAddress
994
1193
  }
995
1194
  };
996
1195
  };
@@ -1015,20 +1214,27 @@ export class L1Deployer {
1015
1214
  this.salt = maybeSalt ? padHex(numberToHex(maybeSalt), {
1016
1215
  size: 32
1017
1216
  }) : undefined;
1018
- this.l1TxUtils = createL1TxUtilsFromViemWallet(this.client, this.logger, dateProvider, this.txUtilsConfig, this.acceleratedTestDeployments);
1217
+ this.l1TxUtils = createL1TxUtilsFromViemWallet(this.client, {
1218
+ logger: this.logger,
1219
+ dateProvider
1220
+ }, {
1221
+ ...this.txUtilsConfig,
1222
+ debugMaxGasLimit: acceleratedTestDeployments
1223
+ });
1019
1224
  }
1020
1225
  async deploy(params, args, opts = {}) {
1021
1226
  this.logger.debug(`Deploying ${params.name} contract`, {
1022
1227
  args
1023
1228
  });
1024
1229
  try {
1025
- const { txHash, address, deployedLibraries } = await deployL1Contract(this.client, params.contractAbi, params.contractBytecode, args ?? [], {
1230
+ const { txHash, address, deployedLibraries, existed } = await deployL1Contract(this.client, params.contractAbi, params.contractBytecode, args ?? [], {
1026
1231
  salt: this.salt,
1027
1232
  libraries: params.libraries,
1028
1233
  logger: this.logger,
1029
1234
  l1TxUtils: this.l1TxUtils,
1030
1235
  acceleratedTestDeployments: this.acceleratedTestDeployments,
1031
- gasLimit: opts.gasLimit
1236
+ gasLimit: opts.gasLimit,
1237
+ noSimulation: opts.noSimulation
1032
1238
  });
1033
1239
  if (txHash) {
1034
1240
  this.txHashes.push(txHash);
@@ -1053,7 +1259,10 @@ export class L1Deployer {
1053
1259
  libraries: deployedLibraries ?? []
1054
1260
  });
1055
1261
  }
1056
- return address;
1262
+ return {
1263
+ address,
1264
+ existed
1265
+ };
1057
1266
  } catch (error) {
1058
1267
  throw new Error(`Failed to deploy ${params.name}`, {
1059
1268
  cause: formatViemError(error)
@@ -1083,10 +1292,13 @@ export class L1Deployer {
1083
1292
  });
1084
1293
  }
1085
1294
  sendTransaction(tx, options) {
1086
- return this.l1TxUtils.sendTransaction(tx, options);
1295
+ return this.l1TxUtils.sendTransaction(tx, options).then(({ txHash, state })=>({
1296
+ txHash,
1297
+ gasLimit: state.gasLimit,
1298
+ gasPrice: state.gasPrice
1299
+ }));
1087
1300
  }
1088
1301
  }
1089
- // docs:start:deployL1Contract
1090
1302
  /**
1091
1303
  * Helper function to deploy ETH contracts.
1092
1304
  * @param walletClient - A viem WalletClient.
@@ -1100,11 +1312,16 @@ export class L1Deployer {
1100
1312
  let txHash = undefined;
1101
1313
  let resultingAddress = undefined;
1102
1314
  const deployedLibraries = [];
1103
- const { salt: saltFromOpts, libraries, logger, gasLimit, acceleratedTestDeployments } = opts;
1315
+ const { salt: saltFromOpts, libraries, logger, gasLimit, acceleratedTestDeployments, noSimulation } = opts;
1104
1316
  let { l1TxUtils } = opts;
1105
1317
  if (!l1TxUtils) {
1106
1318
  const config = getL1TxUtilsConfigEnvVars();
1107
- l1TxUtils = createL1TxUtilsFromViemWallet(extendedClient, logger, undefined, config, acceleratedTestDeployments);
1319
+ l1TxUtils = createL1TxUtilsFromViemWallet(extendedClient, {
1320
+ logger
1321
+ }, {
1322
+ ...config,
1323
+ debugMaxGasLimit: acceleratedTestDeployments
1324
+ });
1108
1325
  }
1109
1326
  if (libraries) {
1110
1327
  // Note that this does NOT work well for linked libraries having linked libraries.
@@ -1179,6 +1396,7 @@ export class L1Deployer {
1179
1396
  logger?.verbose(`Skipping waiting for linked libraries to be deployed ${acceleratedTestDeployments ? '(accelerated test deployments)' : ''}`);
1180
1397
  }
1181
1398
  }
1399
+ let existed = false;
1182
1400
  if (saltFromOpts) {
1183
1401
  logger?.info(`Deploying contract with salt ${saltFromOpts}`);
1184
1402
  const { address, paddedSalt: salt, calldata } = getExpectedAddress(abi, bytecode, args, saltFromOpts);
@@ -1187,26 +1405,28 @@ export class L1Deployer {
1187
1405
  address: resultingAddress
1188
1406
  });
1189
1407
  if (existing === undefined || existing === '0x') {
1190
- try {
1191
- await l1TxUtils.simulate({
1192
- to: DEPLOYER_ADDRESS,
1193
- data: concatHex([
1194
- salt,
1195
- calldata
1196
- ])
1197
- }, {
1198
- gasLimit
1199
- });
1200
- } catch (err) {
1201
- logger?.error(`Failed to simulate deployment tx using universal deployer`, err);
1202
- await l1TxUtils.simulate({
1203
- to: null,
1204
- data: encodeDeployData({
1205
- abi,
1206
- bytecode,
1207
- args
1208
- })
1209
- });
1408
+ if (!noSimulation) {
1409
+ try {
1410
+ await l1TxUtils.simulate({
1411
+ to: DEPLOYER_ADDRESS,
1412
+ data: concatHex([
1413
+ salt,
1414
+ calldata
1415
+ ]),
1416
+ gas: gasLimit
1417
+ });
1418
+ } catch (err) {
1419
+ logger?.error(`Failed to simulate deployment tx using universal deployer`, err);
1420
+ await l1TxUtils.simulate({
1421
+ to: null,
1422
+ data: encodeDeployData({
1423
+ abi,
1424
+ bytecode,
1425
+ args
1426
+ }),
1427
+ gas: gasLimit
1428
+ });
1429
+ }
1210
1430
  }
1211
1431
  const res = await l1TxUtils.sendTransaction({
1212
1432
  to: DEPLOYER_ADDRESS,
@@ -1221,6 +1441,7 @@ export class L1Deployer {
1221
1441
  logger?.verbose(`Deployed contract with salt ${salt} to address ${resultingAddress} in tx ${txHash}.`);
1222
1442
  } else {
1223
1443
  logger?.verbose(`Skipping existing deployment of contract with salt ${salt} to address ${resultingAddress}`);
1444
+ existed = true;
1224
1445
  }
1225
1446
  } else {
1226
1447
  const deployData = encodeDeployData({
@@ -1231,6 +1452,8 @@ export class L1Deployer {
1231
1452
  const { receipt } = await l1TxUtils.sendAndMonitorTransaction({
1232
1453
  to: null,
1233
1454
  data: deployData
1455
+ }, {
1456
+ gasLimit
1234
1457
  });
1235
1458
  txHash = receipt.transactionHash;
1236
1459
  resultingAddress = receipt.contractAddress;
@@ -1241,7 +1464,8 @@ export class L1Deployer {
1241
1464
  return {
1242
1465
  address: EthAddress.fromString(resultingAddress),
1243
1466
  txHash,
1244
- deployedLibraries
1467
+ deployedLibraries,
1468
+ existed
1245
1469
  };
1246
1470
  }
1247
1471
  export function getExpectedAddress(abi, bytecode, args, salt) {
@@ -1264,4 +1488,4 @@ export function getExpectedAddress(abi, bytecode, args, salt) {
1264
1488
  paddedSalt,
1265
1489
  calldata
1266
1490
  };
1267
- } // docs:end:deployL1Contract
1491
+ }