@aztec/ethereum 0.0.1-commit.b655e406 → 0.0.1-commit.b8a057fa

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 (240) hide show
  1. package/dest/account.d.ts +1 -1
  2. package/dest/chain.d.ts +1 -1
  3. package/dest/client.d.ts +25 -3
  4. package/dest/client.d.ts.map +1 -1
  5. package/dest/client.js +60 -4
  6. package/dest/config.d.ts +48 -73
  7. package/dest/config.d.ts.map +1 -1
  8. package/dest/config.js +120 -386
  9. package/dest/constants.d.ts +1 -1
  10. package/dest/contracts/chain_state_override.d.ts +78 -0
  11. package/dest/contracts/chain_state_override.d.ts.map +1 -0
  12. package/dest/contracts/chain_state_override.js +137 -0
  13. package/dest/contracts/empire_base.d.ts +7 -5
  14. package/dest/contracts/empire_base.d.ts.map +1 -1
  15. package/dest/contracts/empire_base.js +1 -1
  16. package/dest/contracts/errors.d.ts +1 -1
  17. package/dest/contracts/errors.d.ts.map +1 -1
  18. package/dest/contracts/fee_asset_handler.d.ts +6 -5
  19. package/dest/contracts/fee_asset_handler.d.ts.map +1 -1
  20. package/dest/contracts/fee_asset_handler.js +11 -9
  21. package/dest/contracts/fee_asset_price_oracle.d.ts +105 -0
  22. package/dest/contracts/fee_asset_price_oracle.d.ts.map +1 -0
  23. package/dest/contracts/fee_asset_price_oracle.js +656 -0
  24. package/dest/contracts/fee_juice.d.ts +1 -1
  25. package/dest/contracts/fee_juice.d.ts.map +1 -1
  26. package/dest/contracts/governance.d.ts +113 -36
  27. package/dest/contracts/governance.d.ts.map +1 -1
  28. package/dest/contracts/governance.js +207 -16
  29. package/dest/contracts/governance_proposer.d.ts +38 -4
  30. package/dest/contracts/governance_proposer.d.ts.map +1 -1
  31. package/dest/contracts/governance_proposer.js +455 -12
  32. package/dest/contracts/gse.d.ts +4 -2
  33. package/dest/contracts/gse.d.ts.map +1 -1
  34. package/dest/contracts/gse.js +2 -2
  35. package/dest/contracts/inbox.d.ts +24 -3
  36. package/dest/contracts/inbox.d.ts.map +1 -1
  37. package/dest/contracts/inbox.js +42 -2
  38. package/dest/contracts/index.d.ts +6 -3
  39. package/dest/contracts/index.d.ts.map +1 -1
  40. package/dest/contracts/index.js +5 -2
  41. package/dest/contracts/log.d.ts +13 -0
  42. package/dest/contracts/log.d.ts.map +1 -0
  43. package/dest/contracts/log.js +1 -0
  44. package/dest/contracts/multicall.d.ts +130 -13
  45. package/dest/contracts/multicall.d.ts.map +1 -1
  46. package/dest/contracts/multicall.js +203 -93
  47. package/dest/contracts/outbox.d.ts +64 -0
  48. package/dest/contracts/outbox.d.ts.map +1 -0
  49. package/dest/contracts/outbox.js +113 -0
  50. package/dest/contracts/registry.d.ts +3 -1
  51. package/dest/contracts/registry.d.ts.map +1 -1
  52. package/dest/contracts/registry.js +30 -1
  53. package/dest/contracts/rollup.d.ts +5591 -131
  54. package/dest/contracts/rollup.d.ts.map +1 -1
  55. package/dest/contracts/rollup.js +1125 -232
  56. package/dest/contracts/slasher_contract.d.ts +1 -1
  57. package/dest/contracts/slasher_contract.d.ts.map +1 -1
  58. package/dest/contracts/{tally_slashing_proposer.d.ts → slashing_proposer.d.ts} +11 -10
  59. package/dest/contracts/slashing_proposer.d.ts.map +1 -0
  60. package/dest/contracts/{tally_slashing_proposer.js → slashing_proposer.js} +21 -16
  61. package/dest/contracts/utils.d.ts +1 -1
  62. package/dest/deploy_aztec_l1_contracts.d.ts +267 -0
  63. package/dest/deploy_aztec_l1_contracts.d.ts.map +1 -0
  64. package/dest/deploy_aztec_l1_contracts.js +417 -0
  65. package/dest/deploy_l1_contract.d.ts +68 -0
  66. package/dest/deploy_l1_contract.d.ts.map +1 -0
  67. package/dest/deploy_l1_contract.js +312 -0
  68. package/dest/eth-signer/eth-signer.d.ts +1 -1
  69. package/dest/eth-signer/index.d.ts +1 -1
  70. package/dest/forwarder_proxy.d.ts +32 -0
  71. package/dest/forwarder_proxy.d.ts.map +1 -0
  72. package/dest/forwarder_proxy.js +93 -0
  73. package/dest/l1_artifacts.d.ts +21007 -16779
  74. package/dest/l1_artifacts.d.ts.map +1 -1
  75. package/dest/l1_artifacts.js +9 -24
  76. package/dest/l1_contract_addresses.d.ts +61 -65
  77. package/dest/l1_contract_addresses.d.ts.map +1 -1
  78. package/dest/l1_contract_addresses.js +99 -80
  79. package/dest/l1_reader.d.ts +7 -5
  80. package/dest/l1_reader.d.ts.map +1 -1
  81. package/dest/l1_reader.js +15 -7
  82. package/dest/l1_tx_utils/config.d.ts +11 -5
  83. package/dest/l1_tx_utils/config.d.ts.map +1 -1
  84. package/dest/l1_tx_utils/config.js +46 -10
  85. package/dest/l1_tx_utils/constants.d.ts +8 -2
  86. package/dest/l1_tx_utils/constants.d.ts.map +1 -1
  87. package/dest/l1_tx_utils/constants.js +27 -2
  88. package/dest/l1_tx_utils/factory.d.ts +18 -10
  89. package/dest/l1_tx_utils/factory.d.ts.map +1 -1
  90. package/dest/l1_tx_utils/factory.js +17 -7
  91. package/dest/l1_tx_utils/fee-strategies/index.d.ts +10 -0
  92. package/dest/l1_tx_utils/fee-strategies/index.d.ts.map +1 -0
  93. package/dest/l1_tx_utils/fee-strategies/index.js +12 -0
  94. package/dest/l1_tx_utils/fee-strategies/p75_competitive.d.ts +8 -0
  95. package/dest/l1_tx_utils/fee-strategies/p75_competitive.d.ts.map +1 -0
  96. package/dest/l1_tx_utils/fee-strategies/p75_competitive.js +129 -0
  97. package/dest/l1_tx_utils/fee-strategies/p75_competitive_blob_txs_only.d.ts +23 -0
  98. package/dest/l1_tx_utils/fee-strategies/p75_competitive_blob_txs_only.d.ts.map +1 -0
  99. package/dest/l1_tx_utils/fee-strategies/p75_competitive_blob_txs_only.js +191 -0
  100. package/dest/l1_tx_utils/fee-strategies/types.d.ts +51 -0
  101. package/dest/l1_tx_utils/fee-strategies/types.d.ts.map +1 -0
  102. package/dest/l1_tx_utils/fee-strategies/types.js +3 -0
  103. package/dest/l1_tx_utils/forwarder_l1_tx_utils.d.ts +41 -0
  104. package/dest/l1_tx_utils/forwarder_l1_tx_utils.d.ts.map +1 -0
  105. package/dest/l1_tx_utils/forwarder_l1_tx_utils.js +42 -0
  106. package/dest/l1_tx_utils/index-blobs.d.ts +3 -0
  107. package/dest/l1_tx_utils/index-blobs.d.ts.map +1 -0
  108. package/dest/l1_tx_utils/index-blobs.js +2 -0
  109. package/dest/l1_tx_utils/index.d.ts +4 -1
  110. package/dest/l1_tx_utils/index.d.ts.map +1 -1
  111. package/dest/l1_tx_utils/index.js +3 -0
  112. package/dest/l1_tx_utils/interfaces.d.ts +2 -2
  113. package/dest/l1_tx_utils/interfaces.d.ts.map +1 -1
  114. package/dest/l1_tx_utils/l1_fee_analyzer.d.ts +252 -0
  115. package/dest/l1_tx_utils/l1_fee_analyzer.d.ts.map +1 -0
  116. package/dest/l1_tx_utils/l1_fee_analyzer.js +568 -0
  117. package/dest/l1_tx_utils/l1_tx_utils.d.ts +26 -8
  118. package/dest/l1_tx_utils/l1_tx_utils.d.ts.map +1 -1
  119. package/dest/l1_tx_utils/l1_tx_utils.js +202 -92
  120. package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts +19 -30
  121. package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts.map +1 -1
  122. package/dest/l1_tx_utils/readonly_l1_tx_utils.js +65 -168
  123. package/dest/l1_tx_utils/signer.d.ts +1 -1
  124. package/dest/l1_tx_utils/tx_delayer.d.ts +58 -0
  125. package/dest/l1_tx_utils/tx_delayer.d.ts.map +1 -0
  126. package/dest/{test → l1_tx_utils}/tx_delayer.js +70 -39
  127. package/dest/l1_tx_utils/types.d.ts +27 -1
  128. package/dest/l1_tx_utils/types.d.ts.map +1 -1
  129. package/dest/l1_tx_utils/types.js +10 -0
  130. package/dest/l1_tx_utils/utils.d.ts +1 -1
  131. package/dest/l1_types.d.ts +1 -1
  132. package/dest/publisher_manager.d.ts +34 -7
  133. package/dest/publisher_manager.d.ts.map +1 -1
  134. package/dest/publisher_manager.js +119 -8
  135. package/dest/queries.d.ts +14 -3
  136. package/dest/queries.d.ts.map +1 -1
  137. package/dest/queries.js +84 -9
  138. package/dest/test/blob_kzg_warmup.d.ts +19 -0
  139. package/dest/test/blob_kzg_warmup.d.ts.map +1 -0
  140. package/dest/test/blob_kzg_warmup.js +64 -0
  141. package/dest/test/chain_monitor.d.ts +80 -26
  142. package/dest/test/chain_monitor.d.ts.map +1 -1
  143. package/dest/test/chain_monitor.js +137 -39
  144. package/dest/test/eth_cheat_codes.d.ts +47 -8
  145. package/dest/test/eth_cheat_codes.d.ts.map +1 -1
  146. package/dest/test/eth_cheat_codes.js +100 -44
  147. package/dest/test/eth_cheat_codes_with_state.d.ts +1 -1
  148. package/dest/test/eth_cheat_codes_with_state.d.ts.map +1 -1
  149. package/dest/test/index.d.ts +2 -3
  150. package/dest/test/index.d.ts.map +1 -1
  151. package/dest/test/index.js +1 -2
  152. package/dest/test/rollup_cheat_codes.d.ts +75 -14
  153. package/dest/test/rollup_cheat_codes.d.ts.map +1 -1
  154. package/dest/test/rollup_cheat_codes.js +145 -42
  155. package/dest/test/start_anvil.d.ts +25 -2
  156. package/dest/test/start_anvil.d.ts.map +1 -1
  157. package/dest/test/start_anvil.js +143 -28
  158. package/dest/test/upgrade_utils.d.ts +1 -1
  159. package/dest/test/upgrade_utils.js +2 -2
  160. package/dest/types.d.ts +57 -2
  161. package/dest/types.d.ts.map +1 -1
  162. package/dest/utils.d.ts +24 -4
  163. package/dest/utils.d.ts.map +1 -1
  164. package/dest/utils.js +112 -16
  165. package/dest/zkPassportVerifierAddress.d.ts +1 -1
  166. package/package.json +34 -17
  167. package/src/client.ts +66 -2
  168. package/src/config.ts +162 -470
  169. package/src/contracts/README.md +157 -0
  170. package/src/contracts/chain_state_override.ts +200 -0
  171. package/src/contracts/empire_base.ts +6 -5
  172. package/src/contracts/fee_asset_handler.ts +10 -7
  173. package/src/contracts/fee_asset_price_oracle.ts +285 -0
  174. package/src/contracts/governance.ts +279 -13
  175. package/src/contracts/governance_proposer.ts +74 -7
  176. package/src/contracts/gse.ts +7 -2
  177. package/src/contracts/inbox.ts +64 -4
  178. package/src/contracts/index.ts +5 -2
  179. package/src/contracts/log.ts +13 -0
  180. package/src/contracts/multicall.ts +247 -103
  181. package/src/contracts/outbox.ts +132 -0
  182. package/src/contracts/registry.ts +31 -1
  183. package/src/contracts/rollup.ts +830 -194
  184. package/src/contracts/{tally_slashing_proposer.ts → slashing_proposer.ts} +26 -21
  185. package/src/deploy_aztec_l1_contracts.ts +652 -0
  186. package/src/deploy_l1_contract.ts +362 -0
  187. package/src/forwarder_proxy.ts +108 -0
  188. package/src/l1_artifacts.ts +12 -35
  189. package/src/l1_contract_addresses.ts +120 -92
  190. package/src/l1_reader.ts +25 -10
  191. package/src/l1_tx_utils/config.ts +55 -14
  192. package/src/l1_tx_utils/constants.ts +13 -2
  193. package/src/l1_tx_utils/factory.ts +31 -31
  194. package/src/l1_tx_utils/fee-strategies/index.ts +22 -0
  195. package/src/l1_tx_utils/fee-strategies/p75_competitive.ts +163 -0
  196. package/src/l1_tx_utils/fee-strategies/p75_competitive_blob_txs_only.ts +245 -0
  197. package/src/l1_tx_utils/fee-strategies/types.ts +56 -0
  198. package/src/l1_tx_utils/forwarder_l1_tx_utils.ts +108 -0
  199. package/src/l1_tx_utils/index-blobs.ts +2 -0
  200. package/src/l1_tx_utils/index.ts +3 -0
  201. package/src/l1_tx_utils/interfaces.ts +1 -1
  202. package/src/l1_tx_utils/l1_fee_analyzer.ts +875 -0
  203. package/src/l1_tx_utils/l1_tx_utils.ts +194 -72
  204. package/src/l1_tx_utils/readonly_l1_tx_utils.ts +79 -214
  205. package/src/{test → l1_tx_utils}/tx_delayer.ts +97 -58
  206. package/src/l1_tx_utils/types.ts +32 -0
  207. package/src/publisher_manager.ts +145 -12
  208. package/src/queries.ts +93 -10
  209. package/src/test/blob_kzg_warmup.ts +65 -0
  210. package/src/test/chain_monitor.ts +198 -51
  211. package/src/test/eth_cheat_codes.ts +91 -48
  212. package/src/test/index.ts +1 -2
  213. package/src/test/rollup_cheat_codes.ts +174 -45
  214. package/src/test/start_anvil.ts +179 -27
  215. package/src/test/upgrade_utils.ts +2 -2
  216. package/src/types.ts +62 -0
  217. package/src/utils.ts +128 -19
  218. package/dest/contracts/empire_slashing_proposer.d.ts +0 -65
  219. package/dest/contracts/empire_slashing_proposer.d.ts.map +0 -1
  220. package/dest/contracts/empire_slashing_proposer.js +0 -194
  221. package/dest/contracts/tally_slashing_proposer.d.ts.map +0 -1
  222. package/dest/deploy_l1_contracts.d.ts +0 -226
  223. package/dest/deploy_l1_contracts.d.ts.map +0 -1
  224. package/dest/deploy_l1_contracts.js +0 -1473
  225. package/dest/index.d.ts +0 -18
  226. package/dest/index.d.ts.map +0 -1
  227. package/dest/index.js +0 -17
  228. package/dest/l1_tx_utils/l1_tx_utils_with_blobs.d.ts +0 -26
  229. package/dest/l1_tx_utils/l1_tx_utils_with_blobs.d.ts.map +0 -1
  230. package/dest/l1_tx_utils/l1_tx_utils_with_blobs.js +0 -26
  231. package/dest/test/delayed_tx_utils.d.ts +0 -13
  232. package/dest/test/delayed_tx_utils.d.ts.map +0 -1
  233. package/dest/test/delayed_tx_utils.js +0 -28
  234. package/dest/test/tx_delayer.d.ts +0 -36
  235. package/dest/test/tx_delayer.d.ts.map +0 -1
  236. package/src/contracts/empire_slashing_proposer.ts +0 -259
  237. package/src/deploy_l1_contracts.ts +0 -1849
  238. package/src/index.ts +0 -17
  239. package/src/l1_tx_utils/l1_tx_utils_with_blobs.ts +0 -77
  240. package/src/test/delayed_tx_utils.ts +0 -52
@@ -0,0 +1,652 @@
1
+ import { SlotNumber } from '@aztec/foundation/branded-types';
2
+ import { SecretValue, getActiveNetworkName } from '@aztec/foundation/config';
3
+ import { EthAddress } from '@aztec/foundation/eth-address';
4
+ import { jsonStringify } from '@aztec/foundation/json-rpc';
5
+ import { createLogger } from '@aztec/foundation/log';
6
+ import { promiseWithResolvers } from '@aztec/foundation/promise';
7
+ import type { Fr } from '@aztec/foundation/schemas';
8
+
9
+ import { bn254 } from '@noble/curves/bn254';
10
+ import type { Abi, Narrow } from 'abitype';
11
+ import { spawn } from 'child_process';
12
+ import { cpSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'fs';
13
+ import { createRequire } from 'node:module';
14
+ import { tmpdir } from 'os';
15
+ import { dirname, join, resolve } from 'path';
16
+ import readline from 'readline';
17
+ import type { Hex } from 'viem';
18
+ import { mainnet, sepolia } from 'viem/chains';
19
+
20
+ import { createEthereumChain, isAnvilTestChain } from './chain.js';
21
+ import { createExtendedL1Client } from './client.js';
22
+ import { type L1ContractsConfig, assertValidSlotDurations } from './config.js';
23
+ import { deployMulticall3 } from './contracts/multicall.js';
24
+ import { RollupContract } from './contracts/rollup.js';
25
+ import type { L1ContractAddresses } from './l1_contract_addresses.js';
26
+ import type { ExtendedViemWalletClient } from './types.js';
27
+
28
+ const logger = createLogger('ethereum:deploy_aztec_l1_contracts');
29
+
30
+ const require = createRequire(import.meta.url);
31
+
32
+ const JSON_DEPLOY_RESULT_PREFIX = 'JSON DEPLOY RESULT:';
33
+
34
+ /**
35
+ * Runs a process and parses JSON deploy results from stdout.
36
+ * Lines starting with JSON_DEPLOY_RESULT_PREFIX are parsed and returned.
37
+ * All other stdout goes to logger.info, stderr goes to logger.warn.
38
+ */
39
+ function runProcess<T>(
40
+ command: string,
41
+ args: string[],
42
+ env: Record<string, string | undefined>,
43
+ cwd: string,
44
+ ): Promise<T | undefined> {
45
+ const { promise, resolve, reject } = promiseWithResolvers<T | undefined>();
46
+ const proc = spawn(command, args, {
47
+ cwd,
48
+ env: { ...process.env, ...env },
49
+ stdio: ['ignore', 'pipe', 'pipe'],
50
+ });
51
+
52
+ let result: T | undefined;
53
+ let parseError: Error | undefined;
54
+ let settled = false;
55
+
56
+ readline.createInterface({ input: proc.stdout }).on('line', line => {
57
+ const trimmedLine = line.trim();
58
+ if (trimmedLine.startsWith(JSON_DEPLOY_RESULT_PREFIX)) {
59
+ const jsonStr = trimmedLine.slice(JSON_DEPLOY_RESULT_PREFIX.length).trim();
60
+ try {
61
+ result = JSON.parse(jsonStr);
62
+ } catch {
63
+ parseError = new Error(`Failed to parse deploy result JSON: ${jsonStr.slice(0, 200)}`);
64
+ }
65
+ } else {
66
+ logger.info(line);
67
+ }
68
+ });
69
+ readline.createInterface({ input: proc.stderr }).on('line', logger.warn.bind(logger));
70
+
71
+ proc.on('error', error => {
72
+ if (settled) {
73
+ return;
74
+ }
75
+ settled = true;
76
+ reject(new Error(`Failed to spawn ${command}: ${error.message}`));
77
+ });
78
+
79
+ proc.on('close', code => {
80
+ if (settled) {
81
+ return;
82
+ }
83
+ settled = true;
84
+ if (code !== 0) {
85
+ reject(new Error(`${command} exited with code ${code}`));
86
+ } else if (parseError) {
87
+ reject(parseError);
88
+ } else {
89
+ resolve(result);
90
+ }
91
+ });
92
+
93
+ return promise;
94
+ }
95
+
96
+ // Covers an edge where where we may have a cached BlobLib that is not meant for production.
97
+ // Despite the profile apparently sometimes cached code remains (so says Lasse after his ignition-monorepo arc).
98
+ async function maybeForgeForceProductionBuild(l1ContractsPath: string, script: string, chainId: number) {
99
+ if (chainId === mainnet.id) {
100
+ logger.info(`Recompiling ${script} with production profile for mainnet deployment`);
101
+ logger.info('This may take a minute but ensures production BlobLib is used.');
102
+ await runProcess('forge', ['build', script, '--force'], { FOUNDRY_PROFILE: 'production' }, l1ContractsPath);
103
+ }
104
+ }
105
+
106
+ // Validator types for initial validator setup
107
+ export interface G2PointJson {
108
+ x0: string;
109
+ x1: string;
110
+ y0: string;
111
+ y1: string;
112
+ }
113
+
114
+ /**
115
+ * Validator data passed to Solidity for registration.
116
+ * Solidity will derive publicKeyG1 and proofOfPossession from the privateKey.
117
+ */
118
+ export interface ValidatorJson {
119
+ attester: string;
120
+ withdrawer: string;
121
+ /** BN254 secret key (private key) */
122
+ privateKey: string;
123
+ /** Pre-computed G2 public key (cannot be computed in Solidity) */
124
+ publicKeyInG2: G2PointJson;
125
+ }
126
+
127
+ /**
128
+ * Gets the path to the l1-contracts foundry artifacts directory bundled inside @aztec/l1-artifacts.
129
+ * Resolved through the package (its "." export -> dest/index.js) so it works whether the package is
130
+ * linked via portal (monorepo) or installed under node_modules (published npm) — resolution follows
131
+ * the symlink in the portal case. The bundled foundry subtree sits alongside dest/, at <pkg>/l1-contracts.
132
+ */
133
+ export function getL1ContractsPath(): string {
134
+ return resolve(dirname(require.resolve('@aztec/l1-artifacts')), '..', 'l1-contracts');
135
+ }
136
+
137
+ // Cached deployment directory
138
+ let preparedDeployDir: string | undefined;
139
+
140
+ function cleanupDeployDir() {
141
+ if (preparedDeployDir) {
142
+ try {
143
+ rmSync(preparedDeployDir, { recursive: true, force: true });
144
+ } catch {
145
+ // ignore cleanup errors
146
+ }
147
+ preparedDeployDir = undefined;
148
+ }
149
+ }
150
+
151
+ /**
152
+ * Prepares a temp directory for forge deployment.
153
+ * Copies all artifacts with preserved timestamps (required for forge cache validity).
154
+ * A fresh broadcast/ directory is created for deployment outputs.
155
+ */
156
+ export function prepareL1ContractsForDeployment(): string {
157
+ if (preparedDeployDir && existsSync(preparedDeployDir)) {
158
+ logger.verbose(`Using cached deployment directory: ${preparedDeployDir}`);
159
+ return preparedDeployDir;
160
+ }
161
+
162
+ const basePath = getL1ContractsPath();
163
+ logger.verbose(`Preparing L1 contracts from: ${basePath}`);
164
+ const tempDir = mkdtempSync(join(tmpdir(), '.foundry-deploy-'));
165
+ logger.verbose(`Created temp directory for deployment: ${tempDir}`);
166
+ preparedDeployDir = tempDir;
167
+ process.on('exit', cleanupDeployDir);
168
+
169
+ // Copy all dirs with preserved timestamps (required for forge cache validity)
170
+ const copyOpts = { recursive: true, preserveTimestamps: true };
171
+ cpSync(join(basePath, 'out'), join(tempDir, 'out'), copyOpts);
172
+ cpSync(join(basePath, 'lib'), join(tempDir, 'lib'), copyOpts);
173
+ cpSync(join(basePath, 'cache'), join(tempDir, 'cache'), copyOpts);
174
+ cpSync(join(basePath, 'src'), join(tempDir, 'src'), copyOpts);
175
+ cpSync(join(basePath, 'script'), join(tempDir, 'script'), copyOpts);
176
+ cpSync(join(basePath, 'generated'), join(tempDir, 'generated'), copyOpts);
177
+ // Kludge: copy test/ to appease forge cache which references test/shouting.t.sol
178
+ cpSync(join(basePath, 'test'), join(tempDir, 'test'), copyOpts);
179
+ cpSync(join(basePath, 'foundry.lock'), join(tempDir, 'foundry.lock'));
180
+
181
+ // Update foundry.toml to use absolute path to solc binary (avoids copying to noexec tmpfs)
182
+ const foundryTomlPath = join(basePath, 'foundry.toml');
183
+ let foundryToml = readFileSync(foundryTomlPath, 'utf-8');
184
+ const solcPathMatch = foundryToml.match(/solc\s*=\s*"\.\/solc-([^"]+)"/);
185
+ // Did we find a hardcoded solc path that we need to make absolute?
186
+ // This code path happens in CI currently as we bundle solc there to avoid race conditions when
187
+ // downloading solc.
188
+ if (solcPathMatch) {
189
+ const solcVersion = solcPathMatch[1];
190
+ const absoluteSolcPath = join(basePath, `solc-${solcVersion}`);
191
+ foundryToml = foundryToml.replace(/solc\s*=\s*"\.\/solc-[^"]+"/, `solc = "${absoluteSolcPath}"`);
192
+ logger.verbose(`Updated solc path in foundry.toml to: ${absoluteSolcPath}`);
193
+ }
194
+ writeFileSync(join(tempDir, 'foundry.toml'), foundryToml);
195
+
196
+ mkdirSync(join(tempDir, 'broadcast'));
197
+ return tempDir;
198
+ }
199
+
200
+ /**
201
+ * Computes the validator data for passing to Solidity.
202
+ * Only computes the G2 public key (which requires scalar multiplication on G2, not available in EVM).
203
+ * Solidity will derive G1 public key and proof of possession from the private key.
204
+ */
205
+ export function computeValidatorData(operator: Operator): ValidatorJson {
206
+ const privateKey = operator.bn254SecretKey.getValue();
207
+
208
+ // Compute G2 public key: pk2 = privateKey * G2
209
+ // This is the only computation we need to do in TypeScript since G2 scalar mul
210
+ // is not available as an EVM precompile
211
+ const publicKeyG2 = bn254.G2.ProjectivePoint.BASE.multiply(privateKey);
212
+ const publicKeyG2Affine = publicKeyG2.toAffine();
213
+
214
+ return {
215
+ attester: operator.attester.toString(),
216
+ withdrawer: operator.withdrawer.toString(),
217
+ privateKey: privateKey.toString(),
218
+ publicKeyInG2: {
219
+ x0: publicKeyG2Affine.x.c0.toString(),
220
+ x1: publicKeyG2Affine.x.c1.toString(),
221
+ y0: publicKeyG2Affine.y.c0.toString(),
222
+ y1: publicKeyG2Affine.y.c1.toString(),
223
+ },
224
+ };
225
+ }
226
+
227
+ /**
228
+ * Deployed addresses from the rollup upgrade deployment.
229
+ */
230
+ export interface RollupUpgradeAddresses {
231
+ rollupAddress: string;
232
+ verifierAddress: string;
233
+ inboxAddress: string;
234
+ outboxAddress: string;
235
+ feeJuicePortalAddress: string;
236
+ rollupVersion: number;
237
+ }
238
+
239
+ /**
240
+ * Return type for rollup upgrade via forge.
241
+ */
242
+ export interface ForgeRollupUpgradeResult {
243
+ rollupAddress: Hex;
244
+ verifierAddress: Hex;
245
+ inboxAddress: Hex;
246
+ outboxAddress: Hex;
247
+ feeJuicePortalAddress: Hex;
248
+ rollupVersion: number;
249
+ }
250
+
251
+ export interface ForgeL1ContractsDeployResult extends ForgeRollupUpgradeResult {
252
+ registryAddress: Hex;
253
+ feeAssetAddress: Hex;
254
+ stakingAssetAddress: Hex;
255
+ gseAddress?: Hex;
256
+ rewardDistributorAddress: Hex;
257
+ coinIssuerAddress: Hex;
258
+ governanceProposerAddress: Hex;
259
+ governanceAddress: Hex;
260
+ dateGatedRelayerAddress?: Hex;
261
+ feeAssetHandlerAddress?: Hex;
262
+ stakingAssetHandlerAddress?: Hex;
263
+ zkPassportVerifierAddress?: Hex;
264
+ }
265
+
266
+ /**
267
+ * Deploys L1 contracts using forge and returns a result compatible with the TypeScript deployAztecL1Contracts function.
268
+ * This queries the Rollup contract to get the inbox, outbox, and feeJuicePortal addresses.
269
+ *
270
+ * All configuration is passed via environment variables to the forge script. The DeploymentConfiguration.sol
271
+ * contract reads these values and applies defaults for any unspecified parameters.
272
+ *
273
+ * @param rpcUrl - The RPC URL to use
274
+ * @param privateKey - The private key for the deployer (with 0x prefix)
275
+ * @param options - Additional deployment options (all optional with sensible defaults)
276
+ * @returns The deployment result with all contract addresses and an l1Client
277
+ */
278
+ export async function deployAztecL1Contracts(
279
+ rpcUrl: string,
280
+ privateKey: `0x${string}`,
281
+ chainId: number,
282
+ args: DeployAztecL1ContractsArgs,
283
+ ): Promise<DeployAztecL1ContractsReturnType> {
284
+ logger.info(`Deploying L1 contracts with config: ${jsonStringify(args)}`);
285
+ assertValidSlotDurations(args);
286
+ if (args.initialValidators && args.initialValidators.length > 0 && args.existingTokenAddress) {
287
+ throw new Error(
288
+ 'Cannot deploy with both initialValidators and existingTokenAddress. ' +
289
+ 'Initial validator funding requires minting tokens, which is not possible with an external token.',
290
+ );
291
+ }
292
+ const chain = createEthereumChain([rpcUrl], chainId);
293
+
294
+ const l1Client = createExtendedL1Client([rpcUrl], privateKey, chain.chainInfo);
295
+ const rpcCall = async (method: string, params: any[]) => {
296
+ logger.info(`Calling ${method} with params: ${JSON.stringify(params)}`);
297
+ return (await l1Client.transport.request({
298
+ method,
299
+ params,
300
+ })) as any;
301
+ };
302
+
303
+ logger.verbose(`Deploying contracts from ${l1Client.account.address.toString()}`);
304
+
305
+ // Deploy multicall3 if it does not exist in this network
306
+ // Sepolia and mainnet will have this.
307
+ await deployMulticall3(l1Client, logger);
308
+
309
+ if (isAnvilTestChain(chainId)) {
310
+ try {
311
+ // We are assuming that you are running this on a local anvil node which have 1s block times
312
+ // To align better with actual deployment, we update the block interval to 12s
313
+ await rpcCall('anvil_setBlockTimestampInterval', [args.ethereumSlotDuration]);
314
+ logger.warn(`Set block interval to ${args.ethereumSlotDuration}`);
315
+ } catch (e) {
316
+ logger.error(`Error setting block interval: ${e}`);
317
+ }
318
+ }
319
+
320
+ // Use foundry-artifacts from l1-artifacts package
321
+ const l1ContractsPath = prepareL1ContractsForDeployment();
322
+
323
+ const FORGE_SCRIPT = 'script/deploy/DeployAztecL1Contracts.s.sol';
324
+ await maybeForgeForceProductionBuild(l1ContractsPath, FORGE_SCRIPT, chainId);
325
+
326
+ // Verify contracts on Etherscan when on mainnet/sepolia and ETHERSCAN_API_KEY is available.
327
+ const isVerifiableChain = chainId === mainnet.id || chainId === sepolia.id;
328
+ const shouldVerify = isVerifiableChain && !!process.env.ETHERSCAN_API_KEY;
329
+
330
+ if (isVerifiableChain && !process.env.ETHERSCAN_API_KEY) {
331
+ logger.warn(
332
+ `Deploying to chain ${chainId} (${chainId === mainnet.id ? 'mainnet' : 'sepolia'}) without ETHERSCAN_API_KEY. ` +
333
+ `Contracts will NOT be verified on Etherscan. Set ETHERSCAN_API_KEY environment variable to enable verification.`,
334
+ );
335
+ }
336
+
337
+ const scriptPath = join(getL1ContractsPath(), 'scripts', 'forge_broadcast.js');
338
+ const forgeArgs = [
339
+ FORGE_SCRIPT,
340
+ '--sig',
341
+ 'run()',
342
+ '--private-key',
343
+ privateKey,
344
+ '--rpc-url',
345
+ rpcUrl,
346
+ ...(shouldVerify ? ['--verify'] : []),
347
+ ];
348
+ const forgeEnv = {
349
+ // Env vars required by l1-contracts/script/deploy/DeploymentConfiguration.sol.
350
+ NETWORK: getActiveNetworkName(),
351
+ FOUNDRY_PROFILE: chainId === mainnet.id ? 'production' : undefined,
352
+ ...getDeployAztecL1ContractsEnvVars(args),
353
+ };
354
+ const result = await runProcess<ForgeL1ContractsDeployResult>(
355
+ process.execPath,
356
+ [scriptPath, ...forgeArgs],
357
+ forgeEnv,
358
+ l1ContractsPath,
359
+ );
360
+ if (!result) {
361
+ throw new Error('Forge script did not output deployment result');
362
+ }
363
+ logger.info(`Deployed L1 contracts with L1 addresses: ${jsonStringify(result)}`);
364
+
365
+ const rollup = new RollupContract(l1Client, result.rollupAddress);
366
+
367
+ if (isAnvilTestChain(chainId)) {
368
+ // @note We make a time jump PAST the very first slot to not have to deal with the edge case of the first slot.
369
+ // The edge case being that the genesis block is already occupying slot 0, so we cannot have another block.
370
+ try {
371
+ // Need to get the time
372
+ const currentSlot = await rollup.getSlotNumber();
373
+
374
+ if (currentSlot === 0) {
375
+ const ts = Number(await rollup.getTimestampForSlot(SlotNumber(1)));
376
+ await rpcCall('evm_setNextBlockTimestamp', [ts]);
377
+ await rpcCall('hardhat_mine', [1]);
378
+ const currentSlot = await rollup.getSlotNumber();
379
+
380
+ if (currentSlot !== 1) {
381
+ throw new Error(`Error jumping time: current slot is ${currentSlot}`);
382
+ }
383
+ logger.info(`Jumped to slot 1`);
384
+ }
385
+ } catch (e) {
386
+ throw new Error(`Error jumping time: ${e}`);
387
+ }
388
+ }
389
+
390
+ return {
391
+ l1Client,
392
+ rollupVersion: result.rollupVersion,
393
+ l1ContractAddresses: {
394
+ rollupAddress: EthAddress.fromString(result.rollupAddress),
395
+ registryAddress: EthAddress.fromString(result.registryAddress),
396
+ inboxAddress: EthAddress.fromString(result.inboxAddress),
397
+ outboxAddress: EthAddress.fromString(result.outboxAddress),
398
+ feeJuiceAddress: EthAddress.fromString(result.feeAssetAddress),
399
+ feeJuicePortalAddress: EthAddress.fromString(result.feeJuicePortalAddress),
400
+ coinIssuerAddress: EthAddress.fromString(result.coinIssuerAddress),
401
+ rewardDistributorAddress: EthAddress.fromString(result.rewardDistributorAddress),
402
+ governanceProposerAddress: EthAddress.fromString(result.governanceProposerAddress),
403
+ governanceAddress: EthAddress.fromString(result.governanceAddress),
404
+ stakingAssetAddress: EthAddress.fromString(result.stakingAssetAddress),
405
+ feeAssetHandlerAddress: result.feeAssetHandlerAddress
406
+ ? EthAddress.fromString(result.feeAssetHandlerAddress)
407
+ : undefined,
408
+ stakingAssetHandlerAddress: result.stakingAssetHandlerAddress
409
+ ? EthAddress.fromString(result.stakingAssetHandlerAddress)
410
+ : undefined,
411
+ zkPassportVerifierAddress: result.zkPassportVerifierAddress
412
+ ? EthAddress.fromString(result.zkPassportVerifierAddress)
413
+ : undefined,
414
+ gseAddress: result.gseAddress ? EthAddress.fromString(result.gseAddress) : undefined,
415
+ dateGatedRelayerAddress: result.dateGatedRelayerAddress
416
+ ? EthAddress.fromString(result.dateGatedRelayerAddress)
417
+ : undefined,
418
+ },
419
+ };
420
+ }
421
+
422
+ export const DEPLOYER_ADDRESS: Hex = '0x4e59b44847b379578588920cA78FbF26c0B4956C';
423
+
424
+ export type Operator = {
425
+ attester: EthAddress;
426
+ withdrawer: EthAddress;
427
+ bn254SecretKey: SecretValue<bigint>;
428
+ };
429
+
430
+ /**
431
+ * Return type of the deployAztecL1Contracts function.
432
+ */
433
+ export type DeployAztecL1ContractsReturnType = {
434
+ /** Extended Wallet Client Type. */
435
+ l1Client: ExtendedViemWalletClient;
436
+ /** The currently deployed l1 contract addresses */
437
+ l1ContractAddresses: L1ContractAddresses;
438
+ /** Version of the current rollup contract. */
439
+ rollupVersion: number;
440
+ };
441
+
442
+ export interface LinkReferences {
443
+ [fileName: string]: {
444
+ [contractName: string]: ReadonlyArray<{
445
+ start: number;
446
+ length: number;
447
+ }>;
448
+ };
449
+ }
450
+
451
+ export interface Libraries {
452
+ linkReferences: LinkReferences;
453
+ libraryCode: Record<string, ContractArtifacts>;
454
+ }
455
+
456
+ /**
457
+ * Contract artifacts
458
+ */
459
+ export interface ContractArtifacts<TAbi extends Abi | readonly unknown[] = Abi> {
460
+ /**
461
+ * The contract name.
462
+ */
463
+ name: string;
464
+ /**
465
+ * The contract abi.
466
+ */
467
+ contractAbi: Narrow<TAbi>;
468
+ /**
469
+ * The contract bytecode
470
+ */
471
+ contractBytecode: Hex;
472
+ /**
473
+ * The contract libraries
474
+ */
475
+ libraries?: Libraries;
476
+ }
477
+
478
+ export type VerificationLibraryEntry = {
479
+ file: string;
480
+ contract: string;
481
+ address: string;
482
+ };
483
+
484
+ export type VerificationRecord = {
485
+ name: string;
486
+ address: string;
487
+ constructorArgsHex: Hex;
488
+ libraries: VerificationLibraryEntry[];
489
+ };
490
+
491
+ export interface DeployAztecL1ContractsArgs
492
+ extends Omit<
493
+ L1ContractsConfig,
494
+ | 'gasLimitBufferPercentage'
495
+ | 'maxGwei'
496
+ | 'maxBlobGwei'
497
+ | 'priorityFeeBumpPercentage'
498
+ | 'priorityFeeRetryBumpPercentage'
499
+ | 'minimumPriorityFeePerGas'
500
+ | 'maxSpeedUpAttempts'
501
+ | 'checkIntervalMs'
502
+ | 'stallTimeMs'
503
+ | 'txTimeoutMs'
504
+ | 'cancelTxOnTimeout'
505
+ | 'txCancellationFinalTimeoutMs'
506
+ | 'txUnseenConsideredDroppedMs'
507
+ | 'enableDelayer'
508
+ | 'txDelayerMaxInclusionTimeIntoSlot'
509
+ > {
510
+ /** The vk tree root. */
511
+ vkTreeRoot: Fr;
512
+ /** The hash of the protocol contracts. */
513
+ protocolContractsHash: Fr;
514
+ /** The genesis root of the archive tree. */
515
+ genesisArchiveRoot: Fr;
516
+ /** The initial validators for the rollup contract. */
517
+ initialValidators?: Operator[];
518
+ /** The initial balance of the fee juice portal. This is the amount of fee juice that is prefunded to accounts */
519
+ feeJuicePortalInitialBalance?: bigint;
520
+ /** Whether to deploy the real verifier or the mock verifier */
521
+ realVerifier?: boolean;
522
+ /** The zk passport args */
523
+ zkPassportArgs?: ZKPassportArgs;
524
+ /** If provided, use this token for BOTH fee and staking assets (skip deployments) */
525
+ existingTokenAddress?: EthAddress;
526
+ }
527
+
528
+ export interface ZKPassportArgs {
529
+ /** The domain of the zk passport (url) */
530
+ zkPassportDomain?: string;
531
+ /** The scope of the zk passport (personhood, etc) */
532
+ zkPassportScope?: string;
533
+ }
534
+
535
+ // picked up by l1-contracts DeploymentConfiguration.sol
536
+ export function getDeployAztecL1ContractsEnvVars(args: DeployAztecL1ContractsArgs) {
537
+ return {
538
+ ...getDeployRollupForUpgradeEnvVars(args), // parsed by RollupConfiguration.sol
539
+ EXISTING_TOKEN_ADDRESS: args.existingTokenAddress?.toString(),
540
+ AZTEC_ACTIVATION_THRESHOLD: args.activationThreshold?.toString(),
541
+ AZTEC_EJECTION_THRESHOLD: args.ejectionThreshold?.toString(),
542
+ AZTEC_GOVERNANCE_PROPOSER_ROUND_SIZE: args.governanceProposerRoundSize?.toString(),
543
+ AZTEC_GOVERNANCE_PROPOSER_QUORUM: args.governanceProposerQuorum?.toString(),
544
+ AZTEC_GOVERNANCE_VOTING_DURATION: args.governanceVotingDuration?.toString(),
545
+ ZKPASSPORT_DOMAIN: args.zkPassportArgs?.zkPassportDomain,
546
+ ZKPASSPORT_SCOPE: args.zkPassportArgs?.zkPassportScope,
547
+ } as const;
548
+ }
549
+
550
+ // picked up by l1-contracts RollupConfiguration.sol
551
+ export function getDeployRollupForUpgradeEnvVars(
552
+ args: Omit<
553
+ DeployAztecL1ContractsArgs,
554
+ | 'governanceProposerQuorum'
555
+ | 'governanceProposerRoundSize'
556
+ | 'ejectionThreshold'
557
+ | 'activationThreshold'
558
+ | 'getZkPassportArgs'
559
+ >,
560
+ ) {
561
+ return {
562
+ INITIAL_VALIDATORS: JSON.stringify((args.initialValidators ?? []).map(computeValidatorData)),
563
+ REAL_VERIFIER: args.realVerifier ? 'true' : 'false',
564
+ FEE_JUICE_PORTAL_INITIAL_BALANCE: (args.feeJuicePortalInitialBalance ?? 0n).toString(),
565
+ // Genesis state
566
+ VK_TREE_ROOT: args.vkTreeRoot.toString(),
567
+ PROTOCOL_CONTRACTS_HASH: args.protocolContractsHash.toString(),
568
+ GENESIS_ARCHIVE_ROOT: args.genesisArchiveRoot.toString(),
569
+ // Rollup config
570
+ AZTEC_SLOT_DURATION: args.aztecSlotDuration.toString(),
571
+ AZTEC_EPOCH_DURATION: args.aztecEpochDuration.toString(),
572
+ AZTEC_TARGET_COMMITTEE_SIZE: args.aztecTargetCommitteeSize.toString(),
573
+ AZTEC_LAG_IN_EPOCHS_FOR_VALIDATOR_SET: args.lagInEpochsForValidatorSet.toString(),
574
+ AZTEC_LAG_IN_EPOCHS_FOR_RANDAO: args.lagInEpochsForRandao.toString(),
575
+ AZTEC_INBOX_LAG: args.inboxLag?.toString(),
576
+ AZTEC_PROOF_SUBMISSION_EPOCHS: args.aztecProofSubmissionEpochs.toString(),
577
+ AZTEC_LOCAL_EJECTION_THRESHOLD: args.localEjectionThreshold.toString(),
578
+ AZTEC_SLASHING_LIFETIME_IN_ROUNDS: args.slashingLifetimeInRounds.toString(),
579
+ AZTEC_SLASHING_EXECUTION_DELAY_IN_ROUNDS: args.slashingExecutionDelayInRounds.toString(),
580
+ AZTEC_SLASHING_VETOER: args.slashingVetoer.toString(),
581
+ AZTEC_SLASHING_DISABLE_DURATION: args.slashingDisableDuration.toString(),
582
+ AZTEC_MANA_TARGET: args.manaTarget.toString(),
583
+ AZTEC_EXIT_DELAY_SECONDS: args.exitDelaySeconds.toString(),
584
+ AZTEC_PROVING_COST_PER_MANA: args.provingCostPerMana.toString(),
585
+ AZTEC_INITIAL_ETH_PER_FEE_ASSET: args.initialEthPerFeeAsset.toString(),
586
+ AZTEC_SLASHER_ENABLED: args.slasherEnabled ? 'true' : 'false',
587
+ AZTEC_SLASHING_ROUND_SIZE_IN_EPOCHS: args.slashingRoundSizeInEpochs.toString(),
588
+ AZTEC_SLASHING_QUORUM: args.slashingQuorum?.toString(),
589
+ AZTEC_SLASHING_OFFSET_IN_ROUNDS: args.slashingOffsetInRounds.toString(),
590
+ AZTEC_SLASH_AMOUNT_SMALL: args.slashAmountSmall.toString(),
591
+ AZTEC_SLASH_AMOUNT_MEDIUM: args.slashAmountMedium.toString(),
592
+ AZTEC_SLASH_AMOUNT_LARGE: args.slashAmountLarge.toString(),
593
+ AZTEC_ENTRY_QUEUE_BOOTSTRAP_VALIDATOR_SET_SIZE: args.entryQueueBootstrapValidatorSetSize.toString(),
594
+ AZTEC_ENTRY_QUEUE_BOOTSTRAP_FLUSH_SIZE: args.entryQueueBootstrapFlushSize.toString(),
595
+ AZTEC_ENTRY_QUEUE_FLUSH_SIZE_MIN: args.entryQueueFlushSizeMin.toString(),
596
+ AZTEC_ENTRY_QUEUE_FLUSH_SIZE_QUOTIENT: args.entryQueueFlushSizeQuotient.toString(),
597
+ AZTEC_ENTRY_QUEUE_MAX_FLUSH_SIZE: args.entryQueueMaxFlushSize.toString(),
598
+ } as const;
599
+ }
600
+
601
+ /**
602
+ * Deploys a new rollup, using the existing canonical version to derive certain values (addresses of assets etc).
603
+ */
604
+ export const deployRollupForUpgrade = async (
605
+ privateKey: `0x${string}`,
606
+ rpcUrl: string,
607
+ chainId: number,
608
+ registryAddress: EthAddress,
609
+ args: Omit<
610
+ DeployAztecL1ContractsArgs,
611
+ | 'governanceProposerQuorum'
612
+ | 'governanceProposerRoundSize'
613
+ | 'ejectionThreshold'
614
+ | 'activationThreshold'
615
+ | 'zkPassportArgs'
616
+ >,
617
+ ) => {
618
+ // Use foundry-artifacts from l1-artifacts package
619
+ const l1ContractsPath = prepareL1ContractsForDeployment();
620
+
621
+ const FORGE_SCRIPT = 'script/deploy/DeployRollupForUpgrade.s.sol';
622
+ await maybeForgeForceProductionBuild(l1ContractsPath, FORGE_SCRIPT, chainId);
623
+
624
+ const scriptPath = join(getL1ContractsPath(), 'scripts', 'forge_broadcast.js');
625
+ const forgeArgs = [FORGE_SCRIPT, '--sig', 'run()', '--private-key', privateKey, '--rpc-url', rpcUrl];
626
+ const forgeEnv = {
627
+ FOUNDRY_PROFILE: chainId === mainnet.id ? 'production' : undefined,
628
+ // Env vars required by l1-contracts/script/deploy/RollupConfiguration.sol.
629
+ REGISTRY_ADDRESS: registryAddress.toString(),
630
+ NETWORK: getActiveNetworkName(),
631
+ ...getDeployRollupForUpgradeEnvVars(args),
632
+ };
633
+
634
+ const result = await runProcess<ForgeRollupUpgradeResult>(
635
+ process.execPath,
636
+ [scriptPath, ...forgeArgs],
637
+ forgeEnv,
638
+ l1ContractsPath,
639
+ );
640
+ if (!result) {
641
+ throw new Error('Forge script did not output deployment result');
642
+ }
643
+
644
+ const extendedClient = createExtendedL1Client([rpcUrl], privateKey);
645
+
646
+ // Create RollupContract wrapper for the deployed rollup
647
+ const rollup = new RollupContract(extendedClient, result.rollupAddress);
648
+
649
+ return {
650
+ rollup,
651
+ };
652
+ };