@aztec/ethereum 0.0.0-test.1 → 0.0.1-commit.21caa21
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.
- package/dest/account.d.ts +2 -0
- package/dest/account.d.ts.map +1 -0
- package/dest/account.js +4 -0
- package/dest/chain.d.ts +1 -1
- package/dest/client.d.ts +6 -4
- package/dest/client.d.ts.map +1 -1
- package/dest/client.js +16 -2
- package/dest/config.d.ts +111 -17
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +462 -22
- package/dest/constants.d.ts +1 -1
- package/dest/contracts/empire_base.d.ts +24 -8
- package/dest/contracts/empire_base.d.ts.map +1 -1
- package/dest/contracts/empire_base.js +75 -2
- package/dest/contracts/empire_slashing_proposer.d.ts +66 -0
- package/dest/contracts/empire_slashing_proposer.d.ts.map +1 -0
- package/dest/contracts/empire_slashing_proposer.js +200 -0
- package/dest/contracts/errors.d.ts +7 -0
- package/dest/contracts/errors.d.ts.map +1 -0
- package/dest/contracts/errors.js +12 -0
- package/dest/contracts/fee_asset_handler.d.ts +19 -0
- package/dest/contracts/fee_asset_handler.d.ts.map +1 -0
- package/dest/contracts/fee_asset_handler.js +57 -0
- package/dest/contracts/fee_juice.d.ts +6 -7
- package/dest/contracts/fee_juice.d.ts.map +1 -1
- package/dest/contracts/fee_juice.js +27 -20
- package/dest/contracts/governance.d.ts +43 -32
- package/dest/contracts/governance.d.ts.map +1 -1
- package/dest/contracts/governance.js +87 -84
- package/dest/contracts/governance_proposer.d.ts +16 -13
- package/dest/contracts/governance_proposer.d.ts.map +1 -1
- package/dest/contracts/governance_proposer.js +37 -17
- package/dest/contracts/gse.d.ts +32 -0
- package/dest/contracts/gse.d.ts.map +1 -0
- package/dest/contracts/gse.js +72 -0
- package/dest/contracts/inbox.d.ts +26 -0
- package/dest/contracts/inbox.d.ts.map +1 -0
- package/dest/contracts/inbox.js +45 -0
- package/dest/contracts/index.d.ts +9 -3
- package/dest/contracts/index.d.ts.map +1 -1
- package/dest/contracts/index.js +8 -2
- package/dest/contracts/multicall.d.ts +21 -0
- package/dest/contracts/multicall.d.ts.map +1 -0
- package/dest/contracts/multicall.js +156 -0
- package/dest/contracts/registry.d.ts +10 -5
- package/dest/contracts/registry.d.ts.map +1 -1
- package/dest/contracts/registry.js +44 -16
- package/dest/contracts/rollup.d.ts +204 -40
- package/dest/contracts/rollup.d.ts.map +1 -1
- package/dest/contracts/rollup.js +529 -79
- package/dest/contracts/slasher_contract.d.ts +44 -0
- package/dest/contracts/slasher_contract.d.ts.map +1 -0
- package/dest/contracts/slasher_contract.js +75 -0
- package/dest/contracts/tally_slashing_proposer.d.ts +139 -0
- package/dest/contracts/tally_slashing_proposer.d.ts.map +1 -0
- package/dest/contracts/tally_slashing_proposer.js +313 -0
- package/dest/contracts/utils.d.ts +3 -0
- package/dest/contracts/utils.d.ts.map +1 -0
- package/dest/contracts/utils.js +11 -0
- package/dest/deploy_l1_contracts.d.ts +577 -21114
- package/dest/deploy_l1_contracts.d.ts.map +1 -1
- package/dest/deploy_l1_contracts.js +1225 -421
- package/dest/eth-signer/eth-signer.d.ts +21 -0
- package/dest/eth-signer/eth-signer.d.ts.map +1 -0
- package/dest/eth-signer/eth-signer.js +5 -0
- package/dest/eth-signer/index.d.ts +2 -0
- package/dest/eth-signer/index.d.ts.map +1 -0
- package/dest/eth-signer/index.js +1 -0
- package/dest/index.d.ts +7 -3
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +6 -2
- package/dest/l1_artifacts.d.ts +77344 -0
- package/dest/l1_artifacts.d.ts.map +1 -0
- package/dest/l1_artifacts.js +166 -0
- package/dest/l1_contract_addresses.d.ts +24 -4
- package/dest/l1_contract_addresses.d.ts.map +1 -1
- package/dest/l1_contract_addresses.js +22 -18
- package/dest/l1_reader.d.ts +2 -2
- package/dest/l1_reader.d.ts.map +1 -1
- package/dest/l1_reader.js +8 -8
- package/dest/l1_tx_utils/config.d.ts +59 -0
- package/dest/l1_tx_utils/config.d.ts.map +1 -0
- package/dest/l1_tx_utils/config.js +82 -0
- package/dest/l1_tx_utils/constants.d.ts +6 -0
- package/dest/l1_tx_utils/constants.d.ts.map +1 -0
- package/dest/l1_tx_utils/constants.js +14 -0
- package/dest/l1_tx_utils/factory.d.ts +24 -0
- package/dest/l1_tx_utils/factory.d.ts.map +1 -0
- package/dest/l1_tx_utils/factory.js +12 -0
- package/dest/l1_tx_utils/index.d.ts +10 -0
- package/dest/l1_tx_utils/index.d.ts.map +1 -0
- package/dest/l1_tx_utils/index.js +10 -0
- package/dest/l1_tx_utils/interfaces.d.ts +76 -0
- package/dest/l1_tx_utils/interfaces.d.ts.map +1 -0
- package/dest/l1_tx_utils/interfaces.js +4 -0
- package/dest/l1_tx_utils/l1_tx_utils.d.ts +94 -0
- package/dest/l1_tx_utils/l1_tx_utils.d.ts.map +1 -0
- package/dest/l1_tx_utils/l1_tx_utils.js +610 -0
- package/dest/l1_tx_utils/l1_tx_utils_with_blobs.d.ts +26 -0
- package/dest/l1_tx_utils/l1_tx_utils_with_blobs.d.ts.map +1 -0
- package/dest/l1_tx_utils/l1_tx_utils_with_blobs.js +26 -0
- package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts +94 -0
- package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts.map +1 -0
- package/dest/l1_tx_utils/readonly_l1_tx_utils.js +430 -0
- package/dest/l1_tx_utils/signer.d.ts +4 -0
- package/dest/l1_tx_utils/signer.d.ts.map +1 -0
- package/dest/l1_tx_utils/signer.js +16 -0
- package/dest/l1_tx_utils/types.d.ts +67 -0
- package/dest/l1_tx_utils/types.d.ts.map +1 -0
- package/dest/l1_tx_utils/types.js +26 -0
- package/dest/l1_tx_utils/utils.d.ts +4 -0
- package/dest/l1_tx_utils/utils.d.ts.map +1 -0
- package/dest/l1_tx_utils/utils.js +14 -0
- package/dest/l1_types.d.ts +6 -0
- package/dest/l1_types.d.ts.map +1 -0
- package/dest/l1_types.js +1 -0
- package/dest/publisher_manager.d.ts +15 -0
- package/dest/publisher_manager.d.ts.map +1 -0
- package/dest/publisher_manager.js +88 -0
- package/dest/queries.d.ts +4 -2
- package/dest/queries.d.ts.map +1 -1
- package/dest/queries.js +53 -12
- package/dest/test/chain_monitor.d.ts +73 -0
- package/dest/test/chain_monitor.d.ts.map +1 -0
- package/dest/test/chain_monitor.js +215 -0
- package/dest/test/delayed_tx_utils.d.ts +8 -3
- package/dest/test/delayed_tx_utils.d.ts.map +1 -1
- package/dest/test/delayed_tx_utils.js +13 -6
- package/dest/test/eth_cheat_codes.d.ts +217 -0
- package/dest/test/eth_cheat_codes.d.ts.map +1 -0
- package/dest/test/eth_cheat_codes.js +558 -0
- package/dest/test/eth_cheat_codes_with_state.d.ts +2 -2
- package/dest/test/eth_cheat_codes_with_state.d.ts.map +1 -1
- package/dest/test/eth_cheat_codes_with_state.js +1 -1
- package/dest/test/index.d.ts +4 -1
- package/dest/test/index.d.ts.map +1 -1
- package/dest/test/index.js +3 -0
- package/dest/test/rollup_cheat_codes.d.ts +87 -0
- package/dest/test/rollup_cheat_codes.d.ts.map +1 -0
- package/dest/test/rollup_cheat_codes.js +266 -0
- package/dest/test/start_anvil.d.ts +7 -1
- package/dest/test/start_anvil.d.ts.map +1 -1
- package/dest/test/start_anvil.js +16 -7
- package/dest/test/tx_delayer.d.ts +18 -7
- package/dest/test/tx_delayer.d.ts.map +1 -1
- package/dest/test/tx_delayer.js +95 -19
- package/dest/test/upgrade_utils.d.ts +6 -5
- package/dest/test/upgrade_utils.d.ts.map +1 -1
- package/dest/test/upgrade_utils.js +23 -16
- package/dest/types.d.ts +7 -8
- package/dest/types.d.ts.map +1 -1
- package/dest/types.js +3 -1
- package/dest/utils.d.ts +2 -1
- package/dest/utils.d.ts.map +1 -1
- package/dest/utils.js +43 -88
- package/dest/zkPassportVerifierAddress.d.ts +15 -0
- package/dest/zkPassportVerifierAddress.d.ts.map +1 -0
- package/dest/zkPassportVerifierAddress.js +11 -0
- package/package.json +28 -19
- package/src/account.ts +5 -0
- package/src/client.ts +42 -4
- package/src/config.ts +592 -31
- package/src/contracts/empire_base.ts +77 -7
- package/src/contracts/empire_slashing_proposer.ts +265 -0
- package/src/contracts/errors.ts +13 -0
- package/src/contracts/fee_asset_handler.ts +63 -0
- package/src/contracts/fee_juice.ts +29 -15
- package/src/contracts/governance.ts +80 -77
- package/src/contracts/governance_proposer.ts +66 -24
- package/src/contracts/gse.ts +88 -0
- package/src/contracts/inbox.ts +63 -0
- package/src/contracts/index.ts +8 -2
- package/src/contracts/multicall.ts +155 -0
- package/src/contracts/registry.ts +51 -26
- package/src/contracts/rollup.ts +596 -74
- package/src/contracts/slasher_contract.ts +89 -0
- package/src/contracts/tally_slashing_proposer.ts +316 -0
- package/src/contracts/utils.ts +14 -0
- package/src/deploy_l1_contracts.ts +1459 -538
- package/src/eth-signer/eth-signer.ts +25 -0
- package/src/eth-signer/index.ts +1 -0
- package/src/index.ts +6 -2
- package/src/l1_artifacts.ts +254 -0
- package/src/l1_contract_addresses.ts +32 -19
- package/src/l1_reader.ts +9 -9
- package/src/l1_tx_utils/README.md +177 -0
- package/src/l1_tx_utils/config.ts +143 -0
- package/src/l1_tx_utils/constants.ts +18 -0
- package/src/l1_tx_utils/factory.ts +64 -0
- package/src/l1_tx_utils/index.ts +12 -0
- package/src/l1_tx_utils/interfaces.ts +86 -0
- package/src/l1_tx_utils/l1_tx_utils.ts +718 -0
- package/src/l1_tx_utils/l1_tx_utils_with_blobs.ts +77 -0
- package/src/l1_tx_utils/readonly_l1_tx_utils.ts +558 -0
- package/src/l1_tx_utils/signer.ts +28 -0
- package/src/l1_tx_utils/types.ts +85 -0
- package/src/l1_tx_utils/utils.ts +16 -0
- package/src/l1_types.ts +6 -0
- package/src/publisher_manager.ts +106 -0
- package/src/queries.ts +73 -15
- package/src/test/chain_monitor.ts +243 -0
- package/src/test/delayed_tx_utils.ts +34 -6
- package/src/test/eth_cheat_codes.ts +588 -0
- package/src/test/eth_cheat_codes_with_state.ts +1 -1
- package/src/test/index.ts +3 -0
- package/src/test/rollup_cheat_codes.ts +307 -0
- package/src/test/start_anvil.ts +22 -5
- package/src/test/tx_delayer.ts +127 -26
- package/src/test/upgrade_utils.ts +30 -21
- package/src/types.ts +10 -8
- package/src/utils.ts +49 -90
- package/src/zkPassportVerifierAddress.ts +15 -0
- package/dest/contracts/forwarder.d.ts +0 -24
- package/dest/contracts/forwarder.d.ts.map +0 -1
- package/dest/contracts/forwarder.js +0 -101
- package/dest/contracts/slashing_proposer.d.ts +0 -21
- package/dest/contracts/slashing_proposer.d.ts.map +0 -1
- package/dest/contracts/slashing_proposer.js +0 -47
- package/dest/eth_cheat_codes.d.ts +0 -147
- package/dest/eth_cheat_codes.d.ts.map +0 -1
- package/dest/eth_cheat_codes.js +0 -303
- package/dest/l1_tx_utils.d.ts +0 -192
- package/dest/l1_tx_utils.d.ts.map +0 -1
- package/dest/l1_tx_utils.js +0 -641
- package/dest/l1_tx_utils_with_blobs.d.ts +0 -12
- package/dest/l1_tx_utils_with_blobs.d.ts.map +0 -1
- package/dest/l1_tx_utils_with_blobs.js +0 -64
- package/src/contracts/forwarder.ts +0 -132
- package/src/contracts/slashing_proposer.ts +0 -51
- package/src/eth_cheat_codes.ts +0 -314
- package/src/l1_tx_utils.ts +0 -847
- package/src/l1_tx_utils_with_blobs.ts +0 -86
|
@@ -1,93 +1,99 @@
|
|
|
1
|
+
import { L1_TO_L2_MSG_SUBTREE_HEIGHT } from '@aztec/constants';
|
|
2
|
+
import { SlotNumber } from '@aztec/foundation/branded-types';
|
|
3
|
+
import { SecretValue, getActiveNetworkName } from '@aztec/foundation/config';
|
|
4
|
+
import { keccak256String } from '@aztec/foundation/crypto';
|
|
1
5
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
2
6
|
import type { Fr } from '@aztec/foundation/fields';
|
|
7
|
+
import { jsonStringify } from '@aztec/foundation/json-rpc';
|
|
3
8
|
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
CoinIssuerBytecode,
|
|
7
|
-
ExtRollupLibAbi,
|
|
8
|
-
ExtRollupLibBytecode,
|
|
9
|
-
FeeJuicePortalAbi,
|
|
10
|
-
FeeJuicePortalBytecode,
|
|
11
|
-
ForwarderAbi,
|
|
12
|
-
ForwarderBytecode,
|
|
13
|
-
GovernanceAbi,
|
|
14
|
-
GovernanceBytecode,
|
|
15
|
-
GovernanceProposerAbi,
|
|
16
|
-
GovernanceProposerBytecode,
|
|
17
|
-
InboxAbi,
|
|
18
|
-
InboxBytecode,
|
|
19
|
-
OutboxAbi,
|
|
20
|
-
OutboxBytecode,
|
|
21
|
-
RegisterNewRollupVersionPayloadAbi,
|
|
22
|
-
RegisterNewRollupVersionPayloadBytecode,
|
|
23
|
-
RegistryAbi,
|
|
24
|
-
RegistryBytecode,
|
|
25
|
-
RewardDistributorAbi,
|
|
26
|
-
RewardDistributorBytecode,
|
|
27
|
-
RollupAbi,
|
|
28
|
-
RollupBytecode,
|
|
29
|
-
RollupLinkReferences,
|
|
30
|
-
SlashFactoryAbi,
|
|
31
|
-
SlashFactoryBytecode,
|
|
32
|
-
TestERC20Abi,
|
|
33
|
-
TestERC20Bytecode,
|
|
34
|
-
ValidatorSelectionLibAbi,
|
|
35
|
-
ValidatorSelectionLibBytecode,
|
|
36
|
-
} from '@aztec/l1-artifacts';
|
|
9
|
+
import { DateProvider } from '@aztec/foundation/timer';
|
|
10
|
+
import { RollupAbi } from '@aztec/l1-artifacts/RollupAbi';
|
|
37
11
|
|
|
38
12
|
import type { Abi, Narrow } from 'abitype';
|
|
13
|
+
import { mkdir, writeFile } from 'fs/promises';
|
|
14
|
+
import chunk from 'lodash.chunk';
|
|
39
15
|
import {
|
|
40
16
|
type Chain,
|
|
17
|
+
type ContractConstructorArgs,
|
|
18
|
+
type HDAccount,
|
|
41
19
|
type Hex,
|
|
20
|
+
type PrivateKeyAccount,
|
|
42
21
|
concatHex,
|
|
43
|
-
|
|
44
|
-
createWalletClient,
|
|
22
|
+
encodeAbiParameters,
|
|
45
23
|
encodeDeployData,
|
|
46
24
|
encodeFunctionData,
|
|
47
|
-
fallback,
|
|
48
25
|
getAddress,
|
|
49
26
|
getContract,
|
|
50
27
|
getContractAddress,
|
|
51
|
-
http,
|
|
52
28
|
numberToHex,
|
|
53
29
|
padHex,
|
|
54
|
-
publicActions,
|
|
55
30
|
} from 'viem';
|
|
56
|
-
import { type HDAccount, type PrivateKeyAccount, mnemonicToAccount, privateKeyToAccount } from 'viem/accounts';
|
|
57
31
|
import { foundry } from 'viem/chains';
|
|
58
32
|
|
|
59
33
|
import { isAnvilTestChain } from './chain.js';
|
|
60
|
-
import
|
|
34
|
+
import { createExtendedL1Client } from './client.js';
|
|
35
|
+
import {
|
|
36
|
+
type L1ContractsConfig,
|
|
37
|
+
getEntryQueueConfig,
|
|
38
|
+
getGovernanceConfiguration,
|
|
39
|
+
getRewardBoostConfig,
|
|
40
|
+
getRewardConfig,
|
|
41
|
+
validateConfig,
|
|
42
|
+
} from './config.js';
|
|
43
|
+
import { GSEContract } from './contracts/gse.js';
|
|
44
|
+
import { deployMulticall3 } from './contracts/multicall.js';
|
|
61
45
|
import { RegistryContract } from './contracts/registry.js';
|
|
62
|
-
import { RollupContract } from './contracts/rollup.js';
|
|
46
|
+
import { RollupContract, SlashingProposerType } from './contracts/rollup.js';
|
|
47
|
+
import {
|
|
48
|
+
CoinIssuerArtifact,
|
|
49
|
+
DateGatedRelayerArtifact,
|
|
50
|
+
FeeAssetArtifact,
|
|
51
|
+
FeeAssetHandlerArtifact,
|
|
52
|
+
GSEArtifact,
|
|
53
|
+
GovernanceArtifact,
|
|
54
|
+
GovernanceProposerArtifact,
|
|
55
|
+
MultiAdderArtifact,
|
|
56
|
+
RegisterNewRollupVersionPayloadArtifact,
|
|
57
|
+
RegistryArtifact,
|
|
58
|
+
RollupArtifact,
|
|
59
|
+
SlashFactoryArtifact,
|
|
60
|
+
StakingAssetArtifact,
|
|
61
|
+
StakingAssetHandlerArtifact,
|
|
62
|
+
l1ArtifactsVerifiers,
|
|
63
|
+
mockVerifiers,
|
|
64
|
+
} from './l1_artifacts.js';
|
|
63
65
|
import type { L1ContractAddresses } from './l1_contract_addresses.js';
|
|
64
66
|
import {
|
|
65
67
|
type GasPrice,
|
|
68
|
+
type L1TxConfig,
|
|
66
69
|
type L1TxRequest,
|
|
67
70
|
L1TxUtils,
|
|
68
71
|
type L1TxUtilsConfig,
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
+
createL1TxUtilsFromViemWallet,
|
|
73
|
+
getL1TxUtilsConfigEnvVars,
|
|
74
|
+
} from './l1_tx_utils/index.js';
|
|
75
|
+
import type { ExtendedViemWalletClient } from './types.js';
|
|
76
|
+
import { formatViemError } from './utils.js';
|
|
77
|
+
import { ZK_PASSPORT_DOMAIN, ZK_PASSPORT_SCOPE, ZK_PASSPORT_VERIFIER_ADDRESS } from './zkPassportVerifierAddress.js';
|
|
72
78
|
|
|
73
79
|
export const DEPLOYER_ADDRESS: Hex = '0x4e59b44847b379578588920cA78FbF26c0B4956C';
|
|
74
80
|
|
|
81
|
+
export type Operator = {
|
|
82
|
+
attester: EthAddress;
|
|
83
|
+
withdrawer: EthAddress;
|
|
84
|
+
bn254SecretKey: SecretValue<bigint>;
|
|
85
|
+
};
|
|
86
|
+
|
|
75
87
|
/**
|
|
76
88
|
* Return type of the deployL1Contract function.
|
|
77
89
|
*/
|
|
78
90
|
export type DeployL1ContractsReturnType = {
|
|
79
|
-
/**
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
walletClient: ViemWalletClient;
|
|
83
|
-
/**
|
|
84
|
-
* Public Client Type.
|
|
85
|
-
*/
|
|
86
|
-
publicClient: ViemPublicClient;
|
|
87
|
-
/**
|
|
88
|
-
* The currently deployed l1 contract addresses
|
|
89
|
-
*/
|
|
91
|
+
/** Extended Wallet Client Type. */
|
|
92
|
+
l1Client: ExtendedViemWalletClient;
|
|
93
|
+
/** The currently deployed l1 contract addresses */
|
|
90
94
|
l1ContractAddresses: L1ContractAddresses;
|
|
95
|
+
/** Version of the current rollup contract. */
|
|
96
|
+
rollupVersion: number;
|
|
91
97
|
};
|
|
92
98
|
|
|
93
99
|
export interface LinkReferences {
|
|
@@ -107,11 +113,15 @@ export interface Libraries {
|
|
|
107
113
|
/**
|
|
108
114
|
* Contract artifacts
|
|
109
115
|
*/
|
|
110
|
-
export interface ContractArtifacts {
|
|
116
|
+
export interface ContractArtifacts<TAbi extends Abi | readonly unknown[] = Abi> {
|
|
117
|
+
/**
|
|
118
|
+
* The contract name.
|
|
119
|
+
*/
|
|
120
|
+
name: string;
|
|
111
121
|
/**
|
|
112
122
|
* The contract abi.
|
|
113
123
|
*/
|
|
114
|
-
contractAbi: Narrow<
|
|
124
|
+
contractAbi: Narrow<TAbi>;
|
|
115
125
|
/**
|
|
116
126
|
* The contract bytecode
|
|
117
127
|
*/
|
|
@@ -122,173 +132,656 @@ export interface ContractArtifacts {
|
|
|
122
132
|
libraries?: Libraries;
|
|
123
133
|
}
|
|
124
134
|
|
|
125
|
-
export
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
},
|
|
130
|
-
inbox: {
|
|
131
|
-
contractAbi: InboxAbi,
|
|
132
|
-
contractBytecode: InboxBytecode as Hex,
|
|
133
|
-
},
|
|
134
|
-
outbox: {
|
|
135
|
-
contractAbi: OutboxAbi,
|
|
136
|
-
contractBytecode: OutboxBytecode as Hex,
|
|
137
|
-
},
|
|
138
|
-
rollup: {
|
|
139
|
-
contractAbi: RollupAbi,
|
|
140
|
-
contractBytecode: RollupBytecode as Hex,
|
|
141
|
-
libraries: {
|
|
142
|
-
linkReferences: RollupLinkReferences,
|
|
143
|
-
libraryCode: {
|
|
144
|
-
ValidatorSelectionLib: {
|
|
145
|
-
contractAbi: ValidatorSelectionLibAbi,
|
|
146
|
-
contractBytecode: ValidatorSelectionLibBytecode as Hex,
|
|
147
|
-
},
|
|
148
|
-
ExtRollupLib: {
|
|
149
|
-
contractAbi: ExtRollupLibAbi,
|
|
150
|
-
contractBytecode: ExtRollupLibBytecode as Hex,
|
|
151
|
-
},
|
|
152
|
-
},
|
|
153
|
-
},
|
|
154
|
-
},
|
|
155
|
-
stakingAsset: {
|
|
156
|
-
contractAbi: TestERC20Abi,
|
|
157
|
-
contractBytecode: TestERC20Bytecode as Hex,
|
|
158
|
-
},
|
|
159
|
-
feeAsset: {
|
|
160
|
-
contractAbi: TestERC20Abi,
|
|
161
|
-
contractBytecode: TestERC20Bytecode as Hex,
|
|
162
|
-
},
|
|
163
|
-
feeJuicePortal: {
|
|
164
|
-
contractAbi: FeeJuicePortalAbi,
|
|
165
|
-
contractBytecode: FeeJuicePortalBytecode as Hex,
|
|
166
|
-
},
|
|
167
|
-
rewardDistributor: {
|
|
168
|
-
contractAbi: RewardDistributorAbi,
|
|
169
|
-
contractBytecode: RewardDistributorBytecode as Hex,
|
|
170
|
-
},
|
|
171
|
-
coinIssuer: {
|
|
172
|
-
contractAbi: CoinIssuerAbi,
|
|
173
|
-
contractBytecode: CoinIssuerBytecode as Hex,
|
|
174
|
-
},
|
|
175
|
-
governanceProposer: {
|
|
176
|
-
contractAbi: GovernanceProposerAbi,
|
|
177
|
-
contractBytecode: GovernanceProposerBytecode as Hex,
|
|
178
|
-
},
|
|
179
|
-
governance: {
|
|
180
|
-
contractAbi: GovernanceAbi,
|
|
181
|
-
contractBytecode: GovernanceBytecode as Hex,
|
|
182
|
-
},
|
|
183
|
-
slashFactory: {
|
|
184
|
-
contractAbi: SlashFactoryAbi,
|
|
185
|
-
contractBytecode: SlashFactoryBytecode as Hex,
|
|
186
|
-
},
|
|
187
|
-
registerNewRollupVersionPayload: {
|
|
188
|
-
contractAbi: RegisterNewRollupVersionPayloadAbi,
|
|
189
|
-
contractBytecode: RegisterNewRollupVersionPayloadBytecode as Hex,
|
|
190
|
-
},
|
|
135
|
+
export type VerificationLibraryEntry = {
|
|
136
|
+
file: string;
|
|
137
|
+
contract: string;
|
|
138
|
+
address: string;
|
|
191
139
|
};
|
|
192
140
|
|
|
193
|
-
export
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
141
|
+
export type VerificationRecord = {
|
|
142
|
+
name: string;
|
|
143
|
+
address: string;
|
|
144
|
+
constructorArgsHex: Hex;
|
|
145
|
+
libraries: VerificationLibraryEntry[];
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
export interface DeployL1ContractsArgs extends Omit<L1ContractsConfig, keyof L1TxUtilsConfig> {
|
|
200
149
|
/** The vk tree root. */
|
|
201
150
|
vkTreeRoot: Fr;
|
|
202
|
-
/** The
|
|
203
|
-
|
|
151
|
+
/** The hash of the protocol contracts. */
|
|
152
|
+
protocolContractsHash: Fr;
|
|
204
153
|
/** The genesis root of the archive tree. */
|
|
205
154
|
genesisArchiveRoot: Fr;
|
|
206
|
-
/** The hash of the genesis block header. */
|
|
207
|
-
genesisBlockHash: Fr;
|
|
208
155
|
/** The salt for CREATE2 deployment. */
|
|
209
156
|
salt: number | undefined;
|
|
210
157
|
/** The initial validators for the rollup contract. */
|
|
211
|
-
initialValidators?:
|
|
158
|
+
initialValidators?: Operator[];
|
|
212
159
|
/** Configuration for the L1 tx utils module. */
|
|
213
160
|
l1TxConfig?: Partial<L1TxUtilsConfig>;
|
|
214
161
|
/** Enable fast mode for deployments (fire and forget transactions) */
|
|
215
162
|
acceleratedTestDeployments?: boolean;
|
|
163
|
+
/** The initial balance of the fee juice portal. This is the amount of fee juice that is prefunded to accounts */
|
|
164
|
+
feeJuicePortalInitialBalance?: bigint;
|
|
165
|
+
/** Whether to deploy the real verifier or the mock verifier */
|
|
166
|
+
realVerifier: boolean;
|
|
167
|
+
/** The zk passport args */
|
|
168
|
+
zkPassportArgs?: ZKPassportArgs;
|
|
169
|
+
/** If provided, use this token for BOTH fee and staking assets (skip deployments) */
|
|
170
|
+
existingTokenAddress?: EthAddress;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export interface ZKPassportArgs {
|
|
174
|
+
/** Whether to use the mock zk passport verifier */
|
|
175
|
+
mockZkPassportVerifier?: boolean;
|
|
176
|
+
/** The domain of the zk passport (url) */
|
|
177
|
+
zkPassportDomain?: string;
|
|
178
|
+
/** The scope of the zk passport (personhood, etc) */
|
|
179
|
+
zkPassportScope?: string;
|
|
216
180
|
}
|
|
217
181
|
|
|
182
|
+
// Minimal ERC20 ABI for validation purposes. We only read view methods.
|
|
183
|
+
const ERC20_VALIDATION_ABI = [
|
|
184
|
+
{
|
|
185
|
+
type: 'function',
|
|
186
|
+
name: 'totalSupply',
|
|
187
|
+
stateMutability: 'view',
|
|
188
|
+
inputs: [],
|
|
189
|
+
outputs: [{ name: '', type: 'uint256' }],
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
type: 'function',
|
|
193
|
+
name: 'name',
|
|
194
|
+
stateMutability: 'view',
|
|
195
|
+
inputs: [],
|
|
196
|
+
outputs: [{ name: '', type: 'string' }],
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
type: 'function',
|
|
200
|
+
name: 'symbol',
|
|
201
|
+
stateMutability: 'view',
|
|
202
|
+
inputs: [],
|
|
203
|
+
outputs: [{ name: '', type: 'string' }],
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
type: 'function',
|
|
207
|
+
name: 'decimals',
|
|
208
|
+
stateMutability: 'view',
|
|
209
|
+
inputs: [],
|
|
210
|
+
outputs: [{ name: '', type: 'uint8' }],
|
|
211
|
+
},
|
|
212
|
+
] as const;
|
|
213
|
+
|
|
218
214
|
/**
|
|
219
|
-
*
|
|
220
|
-
*
|
|
221
|
-
*
|
|
222
|
-
* @param chain - Optional chain spec (defaults to local foundry).
|
|
223
|
-
* @param addressIndex - Optional index of the address to use from the mnemonic.
|
|
224
|
-
* @returns - A wallet and a public client.
|
|
215
|
+
* Validates that the provided address points to a contract that resembles an ERC20 token.
|
|
216
|
+
* Checks for contract code and attempts common ERC20 view calls.
|
|
217
|
+
* Throws an error if validation fails.
|
|
225
218
|
*/
|
|
226
|
-
export function
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
// From what I can see, this is the difference between the HDAccount and the PrivateKeyAccount
|
|
240
|
-
// and we don't need it for anything. This lets us use the same type for both.
|
|
241
|
-
// eslint-disable-next-line camelcase
|
|
242
|
-
hdAccount.experimental_signAuthorization ??= () => {
|
|
243
|
-
throw new Error('experimental_signAuthorization not implemented for HDAccount');
|
|
244
|
-
};
|
|
219
|
+
export async function validateExistingErc20TokenAddress(
|
|
220
|
+
l1Client: ExtendedViemWalletClient,
|
|
221
|
+
tokenAddress: EthAddress,
|
|
222
|
+
logger: Logger,
|
|
223
|
+
): Promise<void> {
|
|
224
|
+
const addressString = tokenAddress.toString();
|
|
225
|
+
|
|
226
|
+
// Ensure there is contract code at the address
|
|
227
|
+
const code = await l1Client.getCode({ address: addressString });
|
|
228
|
+
if (!code || code === '0x') {
|
|
229
|
+
throw new Error(`No contract code found at provided token address ${addressString}`);
|
|
230
|
+
}
|
|
245
231
|
|
|
246
|
-
const
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
}).extend(publicActions);
|
|
251
|
-
const publicClient = createPublicClient({
|
|
252
|
-
chain,
|
|
253
|
-
transport: fallback(rpcUrls.map(url => http(url))),
|
|
254
|
-
pollingInterval: 100,
|
|
232
|
+
const contract = getContract({
|
|
233
|
+
address: getAddress(addressString),
|
|
234
|
+
abi: ERC20_VALIDATION_ABI,
|
|
235
|
+
client: l1Client,
|
|
255
236
|
});
|
|
256
237
|
|
|
257
|
-
|
|
238
|
+
// Validate all required ERC20 methods in parallel
|
|
239
|
+
const checks = [
|
|
240
|
+
contract.read.totalSupply().then(total => typeof total === 'bigint'),
|
|
241
|
+
contract.read.name().then(() => true),
|
|
242
|
+
contract.read.symbol().then(() => true),
|
|
243
|
+
contract.read.decimals().then(dec => typeof dec === 'number' || typeof dec === 'bigint'),
|
|
244
|
+
];
|
|
245
|
+
|
|
246
|
+
const results = await Promise.allSettled(checks);
|
|
247
|
+
const failedChecks = results.filter(result => result.status === 'rejected' || result.value !== true);
|
|
248
|
+
|
|
249
|
+
if (failedChecks.length > 0) {
|
|
250
|
+
throw new Error(`Address ${addressString} does not appear to implement ERC20 view methods`);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
logger.verbose(`Validated existing token at ${addressString} appears to be ERC20-compatible`);
|
|
258
254
|
}
|
|
259
255
|
|
|
260
|
-
export const
|
|
261
|
-
|
|
256
|
+
export const deploySharedContracts = async (
|
|
257
|
+
l1Client: ExtendedViemWalletClient,
|
|
258
|
+
deployer: L1Deployer,
|
|
262
259
|
args: DeployL1ContractsArgs,
|
|
260
|
+
logger: Logger,
|
|
261
|
+
) => {
|
|
262
|
+
const networkName = getActiveNetworkName();
|
|
263
|
+
|
|
264
|
+
logger.info(`Deploying shared contracts for network configuration: ${networkName}`);
|
|
265
|
+
|
|
266
|
+
const txHashes: Hex[] = [];
|
|
267
|
+
|
|
268
|
+
let feeAssetAddress: EthAddress;
|
|
269
|
+
let stakingAssetAddress: EthAddress;
|
|
270
|
+
if (args.existingTokenAddress) {
|
|
271
|
+
await validateExistingErc20TokenAddress(l1Client, args.existingTokenAddress, logger);
|
|
272
|
+
feeAssetAddress = args.existingTokenAddress;
|
|
273
|
+
stakingAssetAddress = args.existingTokenAddress;
|
|
274
|
+
logger.verbose(`Using existing token for fee and staking assets at ${args.existingTokenAddress}`);
|
|
275
|
+
} else {
|
|
276
|
+
const deployedFee = await deployer.deploy(FeeAssetArtifact, ['FeeJuice', 'FEE', l1Client.account.address]);
|
|
277
|
+
feeAssetAddress = deployedFee.address;
|
|
278
|
+
logger.verbose(`Deployed Fee Asset at ${feeAssetAddress}`);
|
|
279
|
+
|
|
280
|
+
// Mint a tiny bit of tokens to satisfy coin-issuer constraints
|
|
281
|
+
const { txHash } = await deployer.sendTransaction(
|
|
282
|
+
{
|
|
283
|
+
to: feeAssetAddress.toString(),
|
|
284
|
+
data: encodeFunctionData({
|
|
285
|
+
abi: FeeAssetArtifact.contractAbi,
|
|
286
|
+
functionName: 'mint',
|
|
287
|
+
args: [l1Client.account.address, 1n * 10n ** 18n],
|
|
288
|
+
}),
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
// contract may not have been deployed yet (CREATE2 returns address before mining),
|
|
292
|
+
// which causes gas estimation to fail. Hardcode to 100k which is plenty for ERC20 mint.
|
|
293
|
+
gasLimit: 100_000n,
|
|
294
|
+
},
|
|
295
|
+
);
|
|
296
|
+
await l1Client.waitForTransactionReceipt({ hash: txHash });
|
|
297
|
+
logger.verbose(`Minted tiny bit of tokens to satisfy coin-issuer constraints in ${txHash}`);
|
|
298
|
+
|
|
299
|
+
const deployedStaking = await deployer.deploy(StakingAssetArtifact, ['Staking', 'STK', l1Client.account.address]);
|
|
300
|
+
stakingAssetAddress = deployedStaking.address;
|
|
301
|
+
logger.verbose(`Deployed Staking Asset at ${stakingAssetAddress}`);
|
|
302
|
+
|
|
303
|
+
await deployer.waitForDeployments();
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
const gseAddress = (
|
|
307
|
+
await deployer.deploy(GSEArtifact, [
|
|
308
|
+
l1Client.account.address,
|
|
309
|
+
stakingAssetAddress.toString(),
|
|
310
|
+
args.activationThreshold,
|
|
311
|
+
args.ejectionThreshold,
|
|
312
|
+
])
|
|
313
|
+
).address;
|
|
314
|
+
logger.verbose(`Deployed GSE at ${gseAddress}`);
|
|
315
|
+
|
|
316
|
+
const { address: registryAddress } = await deployer.deploy(RegistryArtifact, [
|
|
317
|
+
l1Client.account.address,
|
|
318
|
+
feeAssetAddress.toString(),
|
|
319
|
+
]);
|
|
320
|
+
logger.verbose(`Deployed Registry at ${registryAddress}`);
|
|
321
|
+
|
|
322
|
+
const { address: governanceProposerAddress } = await deployer.deploy(GovernanceProposerArtifact, [
|
|
323
|
+
registryAddress.toString(),
|
|
324
|
+
gseAddress.toString(),
|
|
325
|
+
BigInt(args.governanceProposerQuorum ?? args.governanceProposerRoundSize / 2 + 1),
|
|
326
|
+
BigInt(args.governanceProposerRoundSize),
|
|
327
|
+
]);
|
|
328
|
+
logger.verbose(`Deployed GovernanceProposer at ${governanceProposerAddress}`);
|
|
329
|
+
|
|
330
|
+
// @note @LHerskind the assets are expected to be the same at some point, but for better
|
|
331
|
+
// configurability they are different for now.
|
|
332
|
+
const { address: governanceAddress } = await deployer.deploy(GovernanceArtifact, [
|
|
333
|
+
stakingAssetAddress.toString(),
|
|
334
|
+
governanceProposerAddress.toString(),
|
|
335
|
+
gseAddress.toString(),
|
|
336
|
+
getGovernanceConfiguration(networkName),
|
|
337
|
+
]);
|
|
338
|
+
logger.verbose(`Deployed Governance at ${governanceAddress}`);
|
|
339
|
+
|
|
340
|
+
let needToSetGovernance = false;
|
|
341
|
+
|
|
342
|
+
const existingCode = await l1Client.getCode({ address: gseAddress.toString() });
|
|
343
|
+
if (!existingCode || existingCode === '0x') {
|
|
344
|
+
needToSetGovernance = true;
|
|
345
|
+
} else {
|
|
346
|
+
const gseContract = getContract({
|
|
347
|
+
address: getAddress(gseAddress.toString()),
|
|
348
|
+
abi: GSEArtifact.contractAbi,
|
|
349
|
+
client: l1Client,
|
|
350
|
+
});
|
|
351
|
+
const existingGovernance = await gseContract.read.getGovernance();
|
|
352
|
+
if (EthAddress.fromString(existingGovernance).equals(EthAddress.ZERO)) {
|
|
353
|
+
needToSetGovernance = true;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
if (needToSetGovernance) {
|
|
358
|
+
const { txHash } = await deployer.sendTransaction(
|
|
359
|
+
{
|
|
360
|
+
to: gseAddress.toString(),
|
|
361
|
+
data: encodeFunctionData({
|
|
362
|
+
abi: GSEArtifact.contractAbi,
|
|
363
|
+
functionName: 'setGovernance',
|
|
364
|
+
args: [governanceAddress.toString()],
|
|
365
|
+
}),
|
|
366
|
+
},
|
|
367
|
+
{ gasLimit: 100_000n },
|
|
368
|
+
);
|
|
369
|
+
|
|
370
|
+
logger.verbose(`Set governance on GSE in ${txHash}`);
|
|
371
|
+
txHashes.push(txHash);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
logger.verbose(`Waiting for deployments to complete`);
|
|
375
|
+
await deployer.waitForDeployments();
|
|
376
|
+
|
|
377
|
+
const coinIssuerAddress = (
|
|
378
|
+
await deployer.deploy(
|
|
379
|
+
CoinIssuerArtifact,
|
|
380
|
+
[
|
|
381
|
+
feeAssetAddress.toString(),
|
|
382
|
+
2n * 10n ** 17n, // hard cap of 20% per year
|
|
383
|
+
l1Client.account.address,
|
|
384
|
+
],
|
|
385
|
+
{ gasLimit: 1_000_000n, noSimulation: true },
|
|
386
|
+
)
|
|
387
|
+
).address;
|
|
388
|
+
logger.verbose(`Deployed CoinIssuer at ${coinIssuerAddress}`);
|
|
389
|
+
|
|
390
|
+
logger.verbose(`Waiting for deployments to complete`);
|
|
391
|
+
await deployer.waitForDeployments();
|
|
392
|
+
|
|
393
|
+
// Registry ownership will be transferred to governance later, after rollup is added
|
|
394
|
+
|
|
395
|
+
let feeAssetHandlerAddress: EthAddress | undefined = undefined;
|
|
396
|
+
let stakingAssetHandlerAddress: EthAddress | undefined = undefined;
|
|
397
|
+
let zkPassportVerifierAddress: EthAddress | undefined = undefined;
|
|
398
|
+
|
|
399
|
+
// Only if not on mainnet will we deploy the handlers, and only when we control the token
|
|
400
|
+
if (l1Client.chain.id !== 1 && !args.existingTokenAddress) {
|
|
401
|
+
/* -------------------------------------------------------------------------- */
|
|
402
|
+
/* CHEAT CODES START HERE */
|
|
403
|
+
/* -------------------------------------------------------------------------- */
|
|
404
|
+
|
|
405
|
+
const deployedFeeAssetHandler = await deployer.deploy(FeeAssetHandlerArtifact, [
|
|
406
|
+
l1Client.account.address,
|
|
407
|
+
feeAssetAddress.toString(),
|
|
408
|
+
BigInt(1000n * 10n ** 18n),
|
|
409
|
+
]);
|
|
410
|
+
feeAssetHandlerAddress = deployedFeeAssetHandler.address;
|
|
411
|
+
logger.verbose(`Deployed FeeAssetHandler at ${feeAssetHandlerAddress}`);
|
|
412
|
+
|
|
413
|
+
// Only add as minter if this is a new deployment (not reusing existing handler from failed previous run)
|
|
414
|
+
if (!deployedFeeAssetHandler.existed) {
|
|
415
|
+
const { txHash } = await deployer.sendTransaction({
|
|
416
|
+
to: feeAssetAddress.toString(),
|
|
417
|
+
data: encodeFunctionData({
|
|
418
|
+
abi: FeeAssetArtifact.contractAbi,
|
|
419
|
+
functionName: 'addMinter',
|
|
420
|
+
args: [feeAssetHandlerAddress.toString()],
|
|
421
|
+
}),
|
|
422
|
+
});
|
|
423
|
+
logger.verbose(`Added fee asset handler ${feeAssetHandlerAddress} as minter on fee asset in ${txHash}`);
|
|
424
|
+
txHashes.push(txHash);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// Only if on sepolia will we deploy the staking asset handler
|
|
428
|
+
// Should not be deployed to devnet since it would cause caos with sequencers there etc.
|
|
429
|
+
if ([11155111, foundry.id].includes(l1Client.chain.id)) {
|
|
430
|
+
const AMIN = EthAddress.fromString('0x3b218d0F26d15B36C715cB06c949210a0d630637');
|
|
431
|
+
zkPassportVerifierAddress = await getZkPassportVerifierAddress(deployer, args);
|
|
432
|
+
const [domain, scope] = getZkPassportScopes(args);
|
|
433
|
+
|
|
434
|
+
const stakingAssetHandlerDeployArgs = {
|
|
435
|
+
owner: l1Client.account.address,
|
|
436
|
+
stakingAsset: stakingAssetAddress.toString(),
|
|
437
|
+
registry: registryAddress.toString(),
|
|
438
|
+
withdrawer: AMIN.toString(),
|
|
439
|
+
validatorsToFlush: 16n,
|
|
440
|
+
mintInterval: BigInt(60 * 60 * 24),
|
|
441
|
+
depositsPerMint: BigInt(10),
|
|
442
|
+
depositMerkleRoot: '0x0000000000000000000000000000000000000000000000000000000000000000',
|
|
443
|
+
zkPassportVerifier: zkPassportVerifierAddress.toString(),
|
|
444
|
+
unhinged: [AMIN.toString()], // isUnhinged,
|
|
445
|
+
// Scopes
|
|
446
|
+
domain: domain,
|
|
447
|
+
scope: scope,
|
|
448
|
+
// Skip checks
|
|
449
|
+
skipBindCheck: args.zkPassportArgs?.mockZkPassportVerifier ?? false,
|
|
450
|
+
skipMerkleCheck: true, // skip merkle check - needed for testing without generating proofs
|
|
451
|
+
} as const;
|
|
452
|
+
|
|
453
|
+
stakingAssetHandlerAddress = (await deployer.deploy(StakingAssetHandlerArtifact, [stakingAssetHandlerDeployArgs]))
|
|
454
|
+
.address;
|
|
455
|
+
logger.verbose(`Deployed StakingAssetHandler at ${stakingAssetHandlerAddress}`);
|
|
456
|
+
|
|
457
|
+
const { txHash: stakingMinterTxHash } = await deployer.sendTransaction({
|
|
458
|
+
to: stakingAssetAddress.toString(),
|
|
459
|
+
data: encodeFunctionData({
|
|
460
|
+
abi: StakingAssetArtifact.contractAbi,
|
|
461
|
+
functionName: 'addMinter',
|
|
462
|
+
args: [stakingAssetHandlerAddress.toString()],
|
|
463
|
+
}),
|
|
464
|
+
});
|
|
465
|
+
logger.verbose(
|
|
466
|
+
`Added staking asset handler ${stakingAssetHandlerAddress} as minter on staking asset in ${stakingMinterTxHash}`,
|
|
467
|
+
);
|
|
468
|
+
txHashes.push(stakingMinterTxHash);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/* -------------------------------------------------------------------------- */
|
|
473
|
+
/* CHEAT CODES END HERE */
|
|
474
|
+
/* -------------------------------------------------------------------------- */
|
|
475
|
+
|
|
476
|
+
logger.verbose(`Waiting for deployments to complete`);
|
|
477
|
+
await deployer.waitForDeployments();
|
|
478
|
+
await Promise.all(txHashes.map(txHash => l1Client.waitForTransactionReceipt({ hash: txHash })));
|
|
479
|
+
|
|
480
|
+
logger.verbose(`Deployed shared contracts`);
|
|
481
|
+
|
|
482
|
+
const registry = new RegistryContract(l1Client, registryAddress);
|
|
483
|
+
|
|
484
|
+
/* -------------------------------------------------------------------------- */
|
|
485
|
+
/* FUND REWARD DISTRIBUTOR START */
|
|
486
|
+
/* -------------------------------------------------------------------------- */
|
|
487
|
+
|
|
488
|
+
const rewardDistributorAddress = await registry.getRewardDistributor();
|
|
489
|
+
|
|
490
|
+
if (!args.existingTokenAddress) {
|
|
491
|
+
const checkpointReward = getRewardConfig(networkName).checkpointReward;
|
|
492
|
+
|
|
493
|
+
const funding = checkpointReward * 200000n;
|
|
494
|
+
const { txHash: fundRewardDistributorTxHash } = await deployer.sendTransaction({
|
|
495
|
+
to: feeAssetAddress.toString(),
|
|
496
|
+
data: encodeFunctionData({
|
|
497
|
+
abi: FeeAssetArtifact.contractAbi,
|
|
498
|
+
functionName: 'mint',
|
|
499
|
+
args: [rewardDistributorAddress.toString(), funding],
|
|
500
|
+
}),
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
logger.verbose(`Funded reward distributor with ${funding} fee asset in ${fundRewardDistributorTxHash}`);
|
|
504
|
+
} else {
|
|
505
|
+
logger.verbose(`Skipping reward distributor funding as existing token is provided`);
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/* -------------------------------------------------------------------------- */
|
|
509
|
+
/* FUND REWARD DISTRIBUTOR STOP */
|
|
510
|
+
/* -------------------------------------------------------------------------- */
|
|
511
|
+
|
|
512
|
+
return {
|
|
513
|
+
feeAssetAddress,
|
|
514
|
+
feeAssetHandlerAddress,
|
|
515
|
+
stakingAssetAddress,
|
|
516
|
+
stakingAssetHandlerAddress,
|
|
517
|
+
zkPassportVerifierAddress,
|
|
518
|
+
registryAddress,
|
|
519
|
+
gseAddress,
|
|
520
|
+
governanceAddress,
|
|
521
|
+
governanceProposerAddress,
|
|
522
|
+
coinIssuerAddress,
|
|
523
|
+
rewardDistributorAddress: await registry.getRewardDistributor(),
|
|
524
|
+
};
|
|
525
|
+
};
|
|
526
|
+
|
|
527
|
+
const getZkPassportVerifierAddress = async (deployer: L1Deployer, args: DeployL1ContractsArgs): Promise<EthAddress> => {
|
|
528
|
+
if (args.zkPassportArgs?.mockZkPassportVerifier) {
|
|
529
|
+
return (await deployer.deploy(mockVerifiers.mockZkPassportVerifier)).address;
|
|
530
|
+
}
|
|
531
|
+
return ZK_PASSPORT_VERIFIER_ADDRESS;
|
|
532
|
+
};
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* Get the zk passport scopes - default to testnet values if not provided
|
|
536
|
+
* @param args - The deployment arguments
|
|
537
|
+
* @returns The zk passport scopes
|
|
538
|
+
*/
|
|
539
|
+
const getZkPassportScopes = (args: DeployL1ContractsArgs): [string, string] => {
|
|
540
|
+
const domain = args.zkPassportArgs?.zkPassportDomain ?? ZK_PASSPORT_DOMAIN;
|
|
541
|
+
const scope = args.zkPassportArgs?.zkPassportScope ?? ZK_PASSPORT_SCOPE;
|
|
542
|
+
return [domain, scope];
|
|
543
|
+
};
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* Generates verification records for a deployed rollup and its associated contracts (Inbox, Outbox, Slasher, etc).
|
|
547
|
+
* @param rollup - The deployed rollup contract.
|
|
548
|
+
* @param deployer - The L1 deployer instance.
|
|
549
|
+
* @param args - The deployment arguments used for the rollup.
|
|
550
|
+
* @param addresses - The L1 contract addresses.
|
|
551
|
+
* @param extendedClient - The extended viem wallet client.
|
|
552
|
+
* @param logger - The logger.
|
|
553
|
+
*/
|
|
554
|
+
async function generateRollupVerificationRecords(
|
|
555
|
+
rollup: RollupContract,
|
|
556
|
+
deployer: L1Deployer,
|
|
557
|
+
args: {
|
|
558
|
+
slashingVetoer: EthAddress;
|
|
559
|
+
slashingRoundSizeInEpochs: number;
|
|
560
|
+
aztecEpochDuration: number;
|
|
561
|
+
slashingQuorum?: number;
|
|
562
|
+
slashingLifetimeInRounds: number;
|
|
563
|
+
slashingExecutionDelayInRounds: number;
|
|
564
|
+
slasherFlavor: 'none' | 'tally' | 'empire';
|
|
565
|
+
slashAmountSmall: bigint;
|
|
566
|
+
slashAmountMedium: bigint;
|
|
567
|
+
slashAmountLarge: bigint;
|
|
568
|
+
aztecTargetCommitteeSize: number;
|
|
569
|
+
slashingOffsetInRounds: number;
|
|
570
|
+
},
|
|
571
|
+
addresses: Pick<L1ContractAddresses, 'feeJuiceAddress'>,
|
|
572
|
+
extendedClient: ExtendedViemWalletClient,
|
|
573
|
+
logger: Logger,
|
|
574
|
+
): Promise<void> {
|
|
575
|
+
try {
|
|
576
|
+
// Add Inbox / Outbox verification records (constructor args are created inside RollupCore)
|
|
577
|
+
const rollupAddr = rollup.address;
|
|
578
|
+
const rollupAddresses = await rollup.getRollupAddresses();
|
|
579
|
+
const inboxAddr = rollupAddresses.inboxAddress.toString();
|
|
580
|
+
const outboxAddr = rollupAddresses.outboxAddress.toString();
|
|
581
|
+
const feeAsset = rollupAddresses.feeJuiceAddress.toString();
|
|
582
|
+
const version = await rollup.getVersion();
|
|
583
|
+
|
|
584
|
+
const inboxCtor = encodeAbiParameters(
|
|
585
|
+
[{ type: 'address' }, { type: 'address' }, { type: 'uint256' }, { type: 'uint256' }],
|
|
586
|
+
[rollupAddr, feeAsset, version, BigInt(L1_TO_L2_MSG_SUBTREE_HEIGHT)],
|
|
587
|
+
);
|
|
588
|
+
|
|
589
|
+
const outboxCtor = encodeAbiParameters([{ type: 'address' }, { type: 'uint256' }], [rollupAddr, version]);
|
|
590
|
+
|
|
591
|
+
deployer.verificationRecords.push(
|
|
592
|
+
{ name: 'Inbox', address: inboxAddr, constructorArgsHex: inboxCtor, libraries: [] },
|
|
593
|
+
{ name: 'Outbox', address: outboxAddr, constructorArgsHex: outboxCtor, libraries: [] },
|
|
594
|
+
);
|
|
595
|
+
|
|
596
|
+
// Include Slasher and SlashingProposer (if deployed) in verification data
|
|
597
|
+
try {
|
|
598
|
+
const slasherAddrHex = await rollup.getSlasherAddress();
|
|
599
|
+
const slasherAddr = EthAddress.fromString(slasherAddrHex);
|
|
600
|
+
if (!slasherAddr.isZero()) {
|
|
601
|
+
// Slasher constructor: (address _vetoer, address _governance)
|
|
602
|
+
const slasherCtor = encodeAbiParameters(
|
|
603
|
+
[{ type: 'address' }, { type: 'address' }],
|
|
604
|
+
[args.slashingVetoer.toString(), extendedClient.account.address],
|
|
605
|
+
);
|
|
606
|
+
deployer.verificationRecords.push({
|
|
607
|
+
name: 'Slasher',
|
|
608
|
+
address: slasherAddr.toString(),
|
|
609
|
+
constructorArgsHex: slasherCtor,
|
|
610
|
+
libraries: [],
|
|
611
|
+
});
|
|
612
|
+
|
|
613
|
+
// Proposer address is stored in Slasher.PROPOSER()
|
|
614
|
+
const proposerAddr = (await rollup.getSlashingProposerAddress()).toString();
|
|
615
|
+
|
|
616
|
+
// Compute constructor args matching deployment path in RollupCore
|
|
617
|
+
const computedRoundSize = BigInt(args.slashingRoundSizeInEpochs * args.aztecEpochDuration);
|
|
618
|
+
const computedQuorum = BigInt(
|
|
619
|
+
args.slashingQuorum ?? (args.slashingRoundSizeInEpochs * args.aztecEpochDuration) / 2 + 1,
|
|
620
|
+
);
|
|
621
|
+
const lifetimeInRounds = BigInt(args.slashingLifetimeInRounds);
|
|
622
|
+
const executionDelayInRounds = BigInt(args.slashingExecutionDelayInRounds);
|
|
623
|
+
|
|
624
|
+
if (args.slasherFlavor === 'tally') {
|
|
625
|
+
const slashAmounts: readonly [bigint, bigint, bigint] = [
|
|
626
|
+
args.slashAmountSmall,
|
|
627
|
+
args.slashAmountMedium,
|
|
628
|
+
args.slashAmountLarge,
|
|
629
|
+
];
|
|
630
|
+
const committeeSize = BigInt(args.aztecTargetCommitteeSize);
|
|
631
|
+
const epochDuration = BigInt(args.aztecEpochDuration);
|
|
632
|
+
const slashOffsetInRounds = BigInt(args.slashingOffsetInRounds);
|
|
633
|
+
|
|
634
|
+
const proposerCtor = encodeAbiParameters(
|
|
635
|
+
[
|
|
636
|
+
{ type: 'address' },
|
|
637
|
+
{ type: 'address' },
|
|
638
|
+
{ type: 'uint256' },
|
|
639
|
+
{ type: 'uint256' },
|
|
640
|
+
{ type: 'uint256' },
|
|
641
|
+
{ type: 'uint256' },
|
|
642
|
+
{ type: 'uint256[3]' },
|
|
643
|
+
{ type: 'uint256' },
|
|
644
|
+
{ type: 'uint256' },
|
|
645
|
+
{ type: 'uint256' },
|
|
646
|
+
],
|
|
647
|
+
[
|
|
648
|
+
rollup.address,
|
|
649
|
+
slasherAddr.toString(),
|
|
650
|
+
computedQuorum,
|
|
651
|
+
computedRoundSize,
|
|
652
|
+
lifetimeInRounds,
|
|
653
|
+
executionDelayInRounds,
|
|
654
|
+
slashAmounts,
|
|
655
|
+
committeeSize,
|
|
656
|
+
epochDuration,
|
|
657
|
+
slashOffsetInRounds,
|
|
658
|
+
],
|
|
659
|
+
);
|
|
660
|
+
|
|
661
|
+
deployer.verificationRecords.push({
|
|
662
|
+
name: 'TallySlashingProposer',
|
|
663
|
+
address: proposerAddr,
|
|
664
|
+
constructorArgsHex: proposerCtor,
|
|
665
|
+
libraries: [],
|
|
666
|
+
});
|
|
667
|
+
} else if (args.slasherFlavor === 'empire') {
|
|
668
|
+
const proposerCtor = encodeAbiParameters(
|
|
669
|
+
[
|
|
670
|
+
{ type: 'address' },
|
|
671
|
+
{ type: 'address' },
|
|
672
|
+
{ type: 'uint256' },
|
|
673
|
+
{ type: 'uint256' },
|
|
674
|
+
{ type: 'uint256' },
|
|
675
|
+
{ type: 'uint256' },
|
|
676
|
+
],
|
|
677
|
+
[
|
|
678
|
+
rollup.address,
|
|
679
|
+
slasherAddr.toString(),
|
|
680
|
+
computedQuorum,
|
|
681
|
+
computedRoundSize,
|
|
682
|
+
lifetimeInRounds,
|
|
683
|
+
executionDelayInRounds,
|
|
684
|
+
],
|
|
685
|
+
);
|
|
686
|
+
|
|
687
|
+
deployer.verificationRecords.push({
|
|
688
|
+
name: 'EmpireSlashingProposer',
|
|
689
|
+
address: proposerAddr,
|
|
690
|
+
constructorArgsHex: proposerCtor,
|
|
691
|
+
libraries: [],
|
|
692
|
+
});
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
} catch (e) {
|
|
696
|
+
logger.warn(`Failed to add Slasher/Proposer verification records: ${String(e)}`);
|
|
697
|
+
}
|
|
698
|
+
} catch (e) {
|
|
699
|
+
throw new Error(`Failed to generate rollup verification records: ${String(e)}`);
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
/**
|
|
704
|
+
* Writes verification records to a JSON file for later forge verify.
|
|
705
|
+
* @param deployer - The L1 deployer containing verification records.
|
|
706
|
+
* @param outputDirectory - The directory to write the verification file to.
|
|
707
|
+
* @param chainId - The chain ID.
|
|
708
|
+
* @param filenameSuffix - Optional suffix for the filename (e.g., 'upgrade').
|
|
709
|
+
* @param logger - The logger.
|
|
710
|
+
*/
|
|
711
|
+
async function writeVerificationJson(
|
|
712
|
+
deployer: L1Deployer,
|
|
713
|
+
outputDirectory: string,
|
|
714
|
+
chainId: number,
|
|
715
|
+
filenameSuffix: string = '',
|
|
716
|
+
logger: Logger,
|
|
717
|
+
): Promise<void> {
|
|
718
|
+
try {
|
|
719
|
+
const date = new Date();
|
|
720
|
+
const formattedDate = date.toISOString().slice(2, 19).replace(/[-T:]/g, '');
|
|
721
|
+
// Ensure the verification output directory exists
|
|
722
|
+
await mkdir(outputDirectory, { recursive: true });
|
|
723
|
+
const suffix = filenameSuffix ? `-${filenameSuffix}` : '';
|
|
724
|
+
const verificationOutputPath = `${outputDirectory}/l1-verify${suffix}-${chainId}-${formattedDate.slice(0, 6)}-${formattedDate.slice(6)}.json`;
|
|
725
|
+
const networkName = getActiveNetworkName();
|
|
726
|
+
const verificationData = {
|
|
727
|
+
chainId: chainId,
|
|
728
|
+
network: networkName,
|
|
729
|
+
records: deployer.verificationRecords,
|
|
730
|
+
};
|
|
731
|
+
await writeFile(verificationOutputPath, JSON.stringify(verificationData, null, 2));
|
|
732
|
+
logger.info(`Wrote L1 verification data to ${verificationOutputPath}`);
|
|
733
|
+
} catch (e) {
|
|
734
|
+
logger.warn(`Failed to write L1 verification data file: ${String(e)}`);
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
/**
|
|
739
|
+
* Deploys a new rollup, using the existing canonical version to derive certain values (addresses of assets etc).
|
|
740
|
+
* @param clients - The L1 clients.
|
|
741
|
+
* @param args - The deployment arguments.
|
|
742
|
+
* @param registryAddress - The address of the registry.
|
|
743
|
+
* @param logger - The logger.
|
|
744
|
+
* @param txUtilsConfig - The L1 tx utils config.
|
|
745
|
+
* @param createVerificationJson - Optional path to write verification data for forge verify.
|
|
746
|
+
*/
|
|
747
|
+
export const deployRollupForUpgrade = async (
|
|
748
|
+
extendedClient: ExtendedViemWalletClient,
|
|
749
|
+
args: Omit<
|
|
750
|
+
DeployL1ContractsArgs,
|
|
751
|
+
'governanceProposerQuorum' | 'governanceProposerRoundSize' | 'ejectionThreshold' | 'activationThreshold'
|
|
752
|
+
>,
|
|
263
753
|
registryAddress: EthAddress,
|
|
264
754
|
logger: Logger,
|
|
265
755
|
txUtilsConfig: L1TxUtilsConfig,
|
|
756
|
+
createVerificationJson: string | false = false,
|
|
266
757
|
) => {
|
|
267
758
|
const deployer = new L1Deployer(
|
|
268
|
-
|
|
269
|
-
clients.publicClient,
|
|
759
|
+
extendedClient,
|
|
270
760
|
args.salt,
|
|
761
|
+
undefined,
|
|
271
762
|
args.acceleratedTestDeployments,
|
|
272
763
|
logger,
|
|
273
764
|
txUtilsConfig,
|
|
765
|
+
!!createVerificationJson,
|
|
274
766
|
);
|
|
275
767
|
|
|
276
|
-
const addresses = await RegistryContract.collectAddresses(
|
|
768
|
+
const addresses = await RegistryContract.collectAddresses(extendedClient, registryAddress, 'canonical');
|
|
277
769
|
|
|
278
|
-
const rollup = await deployRollup(
|
|
279
|
-
const payloadAddress = await deployUpgradePayload(deployer, {
|
|
280
|
-
registryAddress: addresses.registryAddress,
|
|
281
|
-
rollupAddress: EthAddress.fromString(rollup.address),
|
|
282
|
-
});
|
|
283
|
-
const slashFactoryAddress = await deploySlashFactory(deployer, rollup.address, logger);
|
|
770
|
+
const { rollup, slashFactoryAddress } = await deployRollup(extendedClient, deployer, args, addresses, logger);
|
|
284
771
|
|
|
285
772
|
await deployer.waitForDeployments();
|
|
286
773
|
|
|
287
|
-
|
|
774
|
+
// Write verification data (constructor args + linked libraries) to file for later forge verify
|
|
775
|
+
if (createVerificationJson) {
|
|
776
|
+
await generateRollupVerificationRecords(rollup, deployer, args, addresses, extendedClient, logger);
|
|
777
|
+
await writeVerificationJson(deployer, createVerificationJson, extendedClient.chain.id, 'upgrade', logger);
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
return { rollup, slashFactoryAddress };
|
|
288
781
|
};
|
|
289
782
|
|
|
290
783
|
export const deploySlashFactory = async (deployer: L1Deployer, rollupAddress: Hex, logger: Logger) => {
|
|
291
|
-
const slashFactoryAddress = await deployer.deploy(
|
|
784
|
+
const slashFactoryAddress = (await deployer.deploy(SlashFactoryArtifact, [rollupAddress])).address;
|
|
292
785
|
logger.verbose(`Deployed SlashFactory at ${slashFactoryAddress}`);
|
|
293
786
|
return slashFactoryAddress;
|
|
294
787
|
};
|
|
@@ -297,130 +790,604 @@ export const deployUpgradePayload = async (
|
|
|
297
790
|
deployer: L1Deployer,
|
|
298
791
|
addresses: Pick<L1ContractAddresses, 'registryAddress' | 'rollupAddress'>,
|
|
299
792
|
) => {
|
|
300
|
-
const payloadAddress =
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
793
|
+
const payloadAddress = (
|
|
794
|
+
await deployer.deploy(RegisterNewRollupVersionPayloadArtifact, [
|
|
795
|
+
addresses.registryAddress.toString(),
|
|
796
|
+
addresses.rollupAddress.toString(),
|
|
797
|
+
])
|
|
798
|
+
).address;
|
|
304
799
|
|
|
305
800
|
return payloadAddress;
|
|
306
801
|
};
|
|
307
802
|
|
|
803
|
+
function slasherFlavorToSolidityEnum(flavor: DeployL1ContractsArgs['slasherFlavor']): number {
|
|
804
|
+
switch (flavor) {
|
|
805
|
+
case 'none':
|
|
806
|
+
return SlashingProposerType.None.valueOf();
|
|
807
|
+
case 'tally':
|
|
808
|
+
return SlashingProposerType.Tally.valueOf();
|
|
809
|
+
case 'empire':
|
|
810
|
+
return SlashingProposerType.Empire.valueOf();
|
|
811
|
+
default: {
|
|
812
|
+
const _: never = flavor;
|
|
813
|
+
throw new Error(`Unexpected slasher flavor ${flavor}`);
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
/**
|
|
819
|
+
* Deploys a new rollup contract, funds and initializes the fee juice portal, and initializes the validator set.
|
|
820
|
+
*/
|
|
308
821
|
export const deployRollup = async (
|
|
309
|
-
|
|
822
|
+
extendedClient: ExtendedViemWalletClient,
|
|
310
823
|
deployer: L1Deployer,
|
|
311
|
-
args:
|
|
312
|
-
|
|
824
|
+
args: Omit<
|
|
825
|
+
DeployL1ContractsArgs,
|
|
826
|
+
'governanceProposerQuorum' | 'governanceProposerRoundSize' | 'ejectionThreshold' | 'activationThreshold'
|
|
827
|
+
>,
|
|
828
|
+
addresses: Pick<
|
|
829
|
+
L1ContractAddresses,
|
|
830
|
+
| 'feeJuiceAddress'
|
|
831
|
+
| 'registryAddress'
|
|
832
|
+
| 'rewardDistributorAddress'
|
|
833
|
+
| 'stakingAssetAddress'
|
|
834
|
+
| 'gseAddress'
|
|
835
|
+
| 'governanceAddress'
|
|
836
|
+
>,
|
|
313
837
|
logger: Logger,
|
|
314
|
-
)
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
838
|
+
) => {
|
|
839
|
+
if (!addresses.gseAddress) {
|
|
840
|
+
throw new Error('GSE address is required when deploying');
|
|
841
|
+
}
|
|
842
|
+
const networkName = getActiveNetworkName();
|
|
843
|
+
|
|
844
|
+
logger.info(`Deploying rollup using network configuration: ${networkName}`);
|
|
845
|
+
|
|
846
|
+
const txHashes: Hex[] = [];
|
|
847
|
+
|
|
848
|
+
let epochProofVerifier = EthAddress.ZERO;
|
|
849
|
+
|
|
850
|
+
if (args.realVerifier) {
|
|
851
|
+
epochProofVerifier = (await deployer.deploy(l1ArtifactsVerifiers.honkVerifier)).address;
|
|
852
|
+
logger.verbose(`Rollup will use the real verifier at ${epochProofVerifier}`);
|
|
853
|
+
} else {
|
|
854
|
+
epochProofVerifier = (await deployer.deploy(mockVerifiers.mockVerifier)).address;
|
|
855
|
+
logger.verbose(`Rollup will use the mock verifier at ${epochProofVerifier}`);
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
const rewardConfig = {
|
|
859
|
+
...getRewardConfig(networkName),
|
|
860
|
+
rewardDistributor: addresses.rewardDistributorAddress.toString(),
|
|
323
861
|
};
|
|
862
|
+
|
|
863
|
+
const rollupConfigArgs: ContractConstructorArgs<typeof RollupAbi>[6] = {
|
|
864
|
+
aztecSlotDuration: BigInt(args.aztecSlotDuration),
|
|
865
|
+
aztecEpochDuration: BigInt(args.aztecEpochDuration),
|
|
866
|
+
targetCommitteeSize: BigInt(args.aztecTargetCommitteeSize),
|
|
867
|
+
lagInEpochsForValidatorSet: BigInt(args.lagInEpochsForValidatorSet),
|
|
868
|
+
lagInEpochsForRandao: BigInt(args.lagInEpochsForRandao),
|
|
869
|
+
aztecProofSubmissionEpochs: BigInt(args.aztecProofSubmissionEpochs),
|
|
870
|
+
slashingQuorum: BigInt(args.slashingQuorum ?? (args.slashingRoundSizeInEpochs * args.aztecEpochDuration) / 2 + 1),
|
|
871
|
+
slashingRoundSize: BigInt(args.slashingRoundSizeInEpochs * args.aztecEpochDuration),
|
|
872
|
+
slashingLifetimeInRounds: BigInt(args.slashingLifetimeInRounds),
|
|
873
|
+
slashingExecutionDelayInRounds: BigInt(args.slashingExecutionDelayInRounds),
|
|
874
|
+
slashingVetoer: args.slashingVetoer.toString(),
|
|
875
|
+
manaTarget: args.manaTarget,
|
|
876
|
+
provingCostPerMana: args.provingCostPerMana,
|
|
877
|
+
rewardConfig: rewardConfig,
|
|
878
|
+
version: 0,
|
|
879
|
+
rewardBoostConfig: getRewardBoostConfig(),
|
|
880
|
+
stakingQueueConfig: getEntryQueueConfig(networkName),
|
|
881
|
+
exitDelaySeconds: BigInt(args.exitDelaySeconds),
|
|
882
|
+
slasherFlavor: slasherFlavorToSolidityEnum(args.slasherFlavor),
|
|
883
|
+
slashingOffsetInRounds: BigInt(args.slashingOffsetInRounds),
|
|
884
|
+
slashAmounts: [args.slashAmountSmall, args.slashAmountMedium, args.slashAmountLarge],
|
|
885
|
+
localEjectionThreshold: args.localEjectionThreshold,
|
|
886
|
+
slashingDisableDuration: BigInt(args.slashingDisableDuration ?? 0n),
|
|
887
|
+
earliestRewardsClaimableTimestamp: 0n,
|
|
888
|
+
};
|
|
889
|
+
|
|
324
890
|
const genesisStateArgs = {
|
|
325
891
|
vkTreeRoot: args.vkTreeRoot.toString(),
|
|
326
|
-
|
|
892
|
+
protocolContractsHash: args.protocolContractsHash.toString(),
|
|
327
893
|
genesisArchiveRoot: args.genesisArchiveRoot.toString(),
|
|
328
|
-
genesisBlockHash: args.genesisBlockHash.toString(),
|
|
329
894
|
};
|
|
895
|
+
|
|
896
|
+
// Until there is an actual chain-id for the version, we will just draw a random value.
|
|
897
|
+
// TODO(https://linear.app/aztec-labs/issue/TMNT-139/version-at-deployment)
|
|
898
|
+
rollupConfigArgs.version = Buffer.from(
|
|
899
|
+
keccak256String(
|
|
900
|
+
jsonStringify({
|
|
901
|
+
rollupConfigArgs,
|
|
902
|
+
genesisStateArgs,
|
|
903
|
+
}),
|
|
904
|
+
),
|
|
905
|
+
).readUint32BE(0);
|
|
330
906
|
logger.verbose(`Rollup config args`, rollupConfigArgs);
|
|
907
|
+
|
|
331
908
|
const rollupArgs = [
|
|
332
|
-
addresses.
|
|
333
|
-
addresses.rewardDistributorAddress.toString(),
|
|
909
|
+
addresses.feeJuiceAddress.toString(),
|
|
334
910
|
addresses.stakingAssetAddress.toString(),
|
|
335
|
-
|
|
911
|
+
addresses.gseAddress.toString(),
|
|
912
|
+
epochProofVerifier.toString(),
|
|
913
|
+
extendedClient.account.address,
|
|
336
914
|
genesisStateArgs,
|
|
337
915
|
rollupConfigArgs,
|
|
338
|
-
];
|
|
916
|
+
] as const;
|
|
917
|
+
|
|
918
|
+
const { address: rollupAddress, existed: rollupExisted } = await deployer.deploy(RollupArtifact, rollupArgs, {
|
|
919
|
+
gasLimit: 15_000_000n,
|
|
920
|
+
});
|
|
921
|
+
logger.verbose(`Deployed Rollup at ${rollupAddress}, already existed: ${rollupExisted}`, rollupConfigArgs);
|
|
339
922
|
|
|
340
|
-
const
|
|
341
|
-
logger.verbose(`Deployed Rollup at ${rollupAddress}`, rollupConfigArgs);
|
|
923
|
+
const rollupContract = new RollupContract(extendedClient, rollupAddress);
|
|
342
924
|
|
|
343
925
|
await deployer.waitForDeployments();
|
|
344
926
|
logger.verbose(`All core contracts have been deployed`);
|
|
345
927
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
928
|
+
if (args.feeJuicePortalInitialBalance && args.feeJuicePortalInitialBalance > 0n) {
|
|
929
|
+
// Skip funding when using an external token, as we likely don't have mint permissions
|
|
930
|
+
if (!('existingTokenAddress' in args) || !args.existingTokenAddress) {
|
|
931
|
+
const feeJuicePortalAddress = await rollupContract.getFeeJuicePortal();
|
|
932
|
+
|
|
933
|
+
// In fast mode, use the L1TxUtils to send transactions with nonce management
|
|
934
|
+
const { txHash: mintTxHash } = await deployer.sendTransaction({
|
|
935
|
+
to: addresses.feeJuiceAddress.toString(),
|
|
936
|
+
data: encodeFunctionData({
|
|
937
|
+
abi: FeeAssetArtifact.contractAbi,
|
|
938
|
+
functionName: 'mint',
|
|
939
|
+
args: [feeJuicePortalAddress.toString(), args.feeJuicePortalInitialBalance],
|
|
940
|
+
}),
|
|
941
|
+
});
|
|
942
|
+
logger.verbose(
|
|
943
|
+
`Funding fee juice portal with ${args.feeJuicePortalInitialBalance} fee juice in ${mintTxHash} (accelerated test deployments)`,
|
|
944
|
+
);
|
|
945
|
+
txHashes.push(mintTxHash);
|
|
946
|
+
} else {
|
|
947
|
+
logger.verbose('Skipping fee juice portal funding due to external token usage');
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
const slashFactoryAddress = (await deployer.deploy(SlashFactoryArtifact, [rollupAddress.toString()])).address;
|
|
952
|
+
logger.verbose(`Deployed SlashFactory at ${slashFactoryAddress}`);
|
|
953
|
+
|
|
954
|
+
// We need to call a function on the registry to set the various contract addresses.
|
|
955
|
+
const registryContract = getContract({
|
|
956
|
+
address: getAddress(addresses.registryAddress.toString()),
|
|
957
|
+
abi: RegistryArtifact.contractAbi,
|
|
958
|
+
client: extendedClient,
|
|
959
|
+
});
|
|
960
|
+
|
|
961
|
+
// Only if we are the owner will we be sending these transactions
|
|
962
|
+
if ((await registryContract.read.owner()) === getAddress(extendedClient.account.address)) {
|
|
963
|
+
const version = await rollupContract.getVersion();
|
|
964
|
+
try {
|
|
965
|
+
const retrievedRollupAddress = await registryContract.read.getRollup([version]);
|
|
966
|
+
logger.verbose(`Rollup ${retrievedRollupAddress} already exists in registry`);
|
|
967
|
+
} catch {
|
|
968
|
+
const { txHash: addRollupTxHash } = await deployer.sendTransaction({
|
|
969
|
+
to: addresses.registryAddress.toString(),
|
|
970
|
+
data: encodeFunctionData({
|
|
971
|
+
abi: RegistryArtifact.contractAbi,
|
|
972
|
+
functionName: 'addRollup',
|
|
973
|
+
args: [getAddress(rollupContract.address)],
|
|
974
|
+
}),
|
|
975
|
+
});
|
|
976
|
+
logger.verbose(
|
|
977
|
+
`Adding rollup ${rollupContract.address} to registry ${addresses.registryAddress} in tx ${addRollupTxHash}`,
|
|
978
|
+
);
|
|
979
|
+
|
|
980
|
+
txHashes.push(addRollupTxHash);
|
|
981
|
+
}
|
|
982
|
+
} else {
|
|
983
|
+
logger.verbose(`Not the owner of the registry, skipping rollup addition`);
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
// We need to call a function on the registry to set the various contract addresses.
|
|
987
|
+
const gseContract = getContract({
|
|
988
|
+
address: getAddress(addresses.gseAddress.toString()),
|
|
989
|
+
abi: GSEArtifact.contractAbi,
|
|
990
|
+
client: extendedClient,
|
|
991
|
+
});
|
|
992
|
+
if ((await gseContract.read.owner()) === getAddress(extendedClient.account.address)) {
|
|
993
|
+
if (!(await gseContract.read.isRollupRegistered([rollupContract.address]))) {
|
|
994
|
+
const { txHash: addRollupTxHash } = await deployer.sendTransaction({
|
|
995
|
+
to: addresses.gseAddress.toString(),
|
|
996
|
+
data: encodeFunctionData({
|
|
997
|
+
abi: GSEArtifact.contractAbi,
|
|
998
|
+
functionName: 'addRollup',
|
|
999
|
+
args: [getAddress(rollupContract.address)],
|
|
1000
|
+
}),
|
|
1001
|
+
});
|
|
1002
|
+
logger.verbose(`Adding rollup ${rollupContract.address} to GSE ${addresses.gseAddress} in tx ${addRollupTxHash}`);
|
|
1003
|
+
|
|
1004
|
+
// wait for this tx to land in case we have to register initialValidators
|
|
1005
|
+
await extendedClient.waitForTransactionReceipt({ hash: addRollupTxHash });
|
|
1006
|
+
} else {
|
|
1007
|
+
logger.verbose(`Rollup ${rollupContract.address} is already registered in GSE ${addresses.gseAddress}`);
|
|
1008
|
+
}
|
|
1009
|
+
} else {
|
|
1010
|
+
logger.verbose(`Not the owner of the gse, skipping rollup addition`);
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
const activeAttestorCount = await rollupContract.getActiveAttesterCount();
|
|
1014
|
+
const queuedAttestorCount = await rollupContract.getEntryQueueLength();
|
|
1015
|
+
logger.info(`Rollup has ${activeAttestorCount} active attestors and ${queuedAttestorCount} queued attestors`);
|
|
1016
|
+
|
|
1017
|
+
const shouldAddValidators = activeAttestorCount === 0n && queuedAttestorCount === 0n;
|
|
1018
|
+
|
|
1019
|
+
if (
|
|
1020
|
+
args.initialValidators &&
|
|
1021
|
+
shouldAddValidators &&
|
|
1022
|
+
(await gseContract.read.isRollupRegistered([rollupContract.address]))
|
|
1023
|
+
) {
|
|
1024
|
+
await addMultipleValidators(
|
|
1025
|
+
extendedClient,
|
|
1026
|
+
deployer,
|
|
1027
|
+
addresses.gseAddress.toString(),
|
|
1028
|
+
rollupAddress.toString(),
|
|
1029
|
+
addresses.stakingAssetAddress.toString(),
|
|
1030
|
+
args.initialValidators,
|
|
1031
|
+
args.acceleratedTestDeployments,
|
|
1032
|
+
logger,
|
|
1033
|
+
);
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
// If the owner is not the Governance contract, transfer ownership to the Governance contract
|
|
1037
|
+
logger.verbose(addresses.governanceAddress.toString());
|
|
1038
|
+
if (getAddress(await rollupContract.getOwner()) !== getAddress(addresses.governanceAddress.toString())) {
|
|
1039
|
+
// TODO(md): add send transaction to the deployer such that we do not need to manage tx hashes here
|
|
1040
|
+
const { txHash: transferOwnershipTxHash } = await deployer.sendTransaction({
|
|
1041
|
+
to: rollupContract.address,
|
|
1042
|
+
data: encodeFunctionData({
|
|
1043
|
+
abi: RegistryArtifact.contractAbi,
|
|
1044
|
+
functionName: 'transferOwnership',
|
|
1045
|
+
args: [getAddress(addresses.governanceAddress.toString())],
|
|
1046
|
+
}),
|
|
1047
|
+
});
|
|
1048
|
+
logger.verbose(
|
|
1049
|
+
`Transferring the ownership of the rollup contract at ${rollupContract.address} to the Governance ${addresses.governanceAddress} in tx ${transferOwnershipTxHash}`,
|
|
1050
|
+
);
|
|
1051
|
+
txHashes.push(transferOwnershipTxHash);
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
await deployer.waitForDeployments();
|
|
1055
|
+
await Promise.all(txHashes.map(txHash => extendedClient.waitForTransactionReceipt({ hash: txHash })));
|
|
1056
|
+
logger.verbose(`Rollup deployed`);
|
|
1057
|
+
|
|
1058
|
+
return { rollup: rollupContract, slashFactoryAddress };
|
|
1059
|
+
};
|
|
1060
|
+
|
|
1061
|
+
export const handoverToGovernance = async (
|
|
1062
|
+
extendedClient: ExtendedViemWalletClient,
|
|
1063
|
+
deployer: L1Deployer,
|
|
1064
|
+
registryAddress: EthAddress,
|
|
1065
|
+
gseAddress: EthAddress,
|
|
1066
|
+
coinIssuerAddress: EthAddress,
|
|
1067
|
+
feeAssetAddress: EthAddress,
|
|
1068
|
+
governanceAddress: EthAddress,
|
|
1069
|
+
logger: Logger,
|
|
1070
|
+
acceleratedTestDeployments: boolean | undefined,
|
|
1071
|
+
useExternalToken: boolean = false,
|
|
1072
|
+
) => {
|
|
1073
|
+
// We need to call a function on the registry to set the various contract addresses.
|
|
1074
|
+
const registryContract = getContract({
|
|
1075
|
+
address: getAddress(registryAddress.toString()),
|
|
1076
|
+
abi: RegistryArtifact.contractAbi,
|
|
1077
|
+
client: extendedClient,
|
|
1078
|
+
});
|
|
1079
|
+
|
|
1080
|
+
const gseContract = getContract({
|
|
1081
|
+
address: getAddress(gseAddress.toString()),
|
|
1082
|
+
abi: GSEArtifact.contractAbi,
|
|
1083
|
+
client: extendedClient,
|
|
1084
|
+
});
|
|
1085
|
+
|
|
1086
|
+
const coinIssuerContract = getContract({
|
|
1087
|
+
address: getAddress(coinIssuerAddress.toString()),
|
|
1088
|
+
abi: CoinIssuerArtifact.contractAbi,
|
|
1089
|
+
client: extendedClient,
|
|
1090
|
+
});
|
|
1091
|
+
|
|
1092
|
+
const feeAsset = getContract({
|
|
1093
|
+
address: getAddress(feeAssetAddress.toString()),
|
|
1094
|
+
abi: FeeAssetArtifact.contractAbi,
|
|
1095
|
+
client: extendedClient,
|
|
350
1096
|
});
|
|
351
1097
|
|
|
352
1098
|
const txHashes: Hex[] = [];
|
|
353
1099
|
|
|
354
|
-
|
|
1100
|
+
// If the owner is not the Governance contract, transfer ownership to the Governance contract
|
|
1101
|
+
if (
|
|
1102
|
+
acceleratedTestDeployments ||
|
|
1103
|
+
(await registryContract.read.owner()) !== getAddress(governanceAddress.toString())
|
|
1104
|
+
) {
|
|
1105
|
+
// TODO(md): add send transaction to the deployer such that we do not need to manage tx hashes here
|
|
1106
|
+
const { txHash: transferOwnershipTxHash } = await deployer.sendTransaction({
|
|
1107
|
+
to: registryAddress.toString(),
|
|
1108
|
+
data: encodeFunctionData({
|
|
1109
|
+
abi: RegistryArtifact.contractAbi,
|
|
1110
|
+
functionName: 'transferOwnership',
|
|
1111
|
+
args: [getAddress(governanceAddress.toString())],
|
|
1112
|
+
}),
|
|
1113
|
+
});
|
|
1114
|
+
logger.verbose(
|
|
1115
|
+
`Transferring the ownership of the registry contract at ${registryAddress} to the Governance ${governanceAddress} in tx ${transferOwnershipTxHash}`,
|
|
1116
|
+
);
|
|
1117
|
+
txHashes.push(transferOwnershipTxHash);
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
// If the owner is not the Governance contract, transfer ownership to the Governance contract
|
|
1121
|
+
if (acceleratedTestDeployments || (await gseContract.read.owner()) !== getAddress(governanceAddress.toString())) {
|
|
1122
|
+
// TODO(md): add send transaction to the deployer such that we do not need to manage tx hashes here
|
|
1123
|
+
const { txHash: transferOwnershipTxHash } = await deployer.sendTransaction({
|
|
1124
|
+
to: gseContract.address,
|
|
1125
|
+
data: encodeFunctionData({
|
|
1126
|
+
abi: GSEArtifact.contractAbi,
|
|
1127
|
+
functionName: 'transferOwnership',
|
|
1128
|
+
args: [getAddress(governanceAddress.toString())],
|
|
1129
|
+
}),
|
|
1130
|
+
});
|
|
1131
|
+
logger.verbose(
|
|
1132
|
+
`Transferring the ownership of the gse contract at ${gseAddress} to the Governance ${governanceAddress} in tx ${transferOwnershipTxHash}`,
|
|
1133
|
+
);
|
|
1134
|
+
txHashes.push(transferOwnershipTxHash);
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
if (
|
|
1138
|
+
!useExternalToken &&
|
|
1139
|
+
(acceleratedTestDeployments || (await feeAsset.read.owner()) !== coinIssuerAddress.toString())
|
|
1140
|
+
) {
|
|
1141
|
+
const { txHash } = await deployer.sendTransaction(
|
|
1142
|
+
{
|
|
1143
|
+
to: feeAssetAddress.toString(),
|
|
1144
|
+
data: encodeFunctionData({
|
|
1145
|
+
abi: FeeAssetArtifact.contractAbi,
|
|
1146
|
+
functionName: 'transferOwnership',
|
|
1147
|
+
args: [coinIssuerAddress.toString()],
|
|
1148
|
+
}),
|
|
1149
|
+
},
|
|
1150
|
+
{ gasLimit: 500_000n },
|
|
1151
|
+
);
|
|
1152
|
+
logger.verbose(`Transfer ownership of fee asset to coin issuer ${coinIssuerAddress} in ${txHash}`);
|
|
1153
|
+
txHashes.push(txHash);
|
|
1154
|
+
|
|
1155
|
+
const { txHash: acceptTokenOwnershipTxHash } = await deployer.sendTransaction(
|
|
1156
|
+
{
|
|
1157
|
+
to: coinIssuerAddress.toString(),
|
|
1158
|
+
data: encodeFunctionData({
|
|
1159
|
+
abi: CoinIssuerArtifact.contractAbi,
|
|
1160
|
+
functionName: 'acceptTokenOwnership',
|
|
1161
|
+
}),
|
|
1162
|
+
},
|
|
1163
|
+
{ gasLimit: 500_000n },
|
|
1164
|
+
);
|
|
1165
|
+
logger.verbose(`Accept ownership of fee asset in ${acceptTokenOwnershipTxHash}`);
|
|
1166
|
+
txHashes.push(acceptTokenOwnershipTxHash);
|
|
1167
|
+
} else if (useExternalToken) {
|
|
1168
|
+
logger.verbose('Skipping fee asset ownership transfer due to external token usage');
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
// Either deploy or at least predict the address of the date gated relayer
|
|
1172
|
+
const dateGatedRelayer = await deployer.deploy(DateGatedRelayerArtifact, [
|
|
1173
|
+
governanceAddress.toString(),
|
|
1174
|
+
1798761600n, // 2027-01-01 00:00:00 UTC
|
|
1175
|
+
]);
|
|
1176
|
+
|
|
1177
|
+
// If the owner is not the Governance contract, transfer ownership to the Governance contract
|
|
1178
|
+
if (acceleratedTestDeployments || (await coinIssuerContract.read.owner()) === deployer.client.account.address) {
|
|
1179
|
+
const { txHash: transferOwnershipTxHash } = await deployer.sendTransaction({
|
|
1180
|
+
to: coinIssuerContract.address,
|
|
1181
|
+
data: encodeFunctionData({
|
|
1182
|
+
abi: CoinIssuerArtifact.contractAbi,
|
|
1183
|
+
functionName: 'transferOwnership',
|
|
1184
|
+
args: [getAddress(dateGatedRelayer.address.toString())],
|
|
1185
|
+
}),
|
|
1186
|
+
});
|
|
1187
|
+
logger.verbose(
|
|
1188
|
+
`Transferring the ownership of the coin issuer contract at ${coinIssuerAddress} to the DateGatedRelayer ${dateGatedRelayer.address} in tx ${transferOwnershipTxHash}`,
|
|
1189
|
+
);
|
|
1190
|
+
txHashes.push(transferOwnershipTxHash);
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
// Wait for all actions to be mined
|
|
1194
|
+
await deployer.waitForDeployments();
|
|
1195
|
+
await Promise.all(txHashes.map(txHash => extendedClient.waitForTransactionReceipt({ hash: txHash })));
|
|
1196
|
+
|
|
1197
|
+
return { dateGatedRelayerAddress: dateGatedRelayer.address };
|
|
1198
|
+
};
|
|
1199
|
+
|
|
1200
|
+
/*
|
|
1201
|
+
* Adds multiple validators to the rollup
|
|
1202
|
+
*
|
|
1203
|
+
* @param extendedClient - The L1 clients.
|
|
1204
|
+
* @param deployer - The L1 deployer.
|
|
1205
|
+
* @param rollupAddress - The address of the rollup.
|
|
1206
|
+
* @param stakingAssetAddress - The address of the staking asset.
|
|
1207
|
+
* @param validators - The validators to initialize.
|
|
1208
|
+
* @param acceleratedTestDeployments - Whether to use accelerated test deployments.
|
|
1209
|
+
* @param logger - The logger.
|
|
1210
|
+
*/
|
|
1211
|
+
export const addMultipleValidators = async (
|
|
1212
|
+
extendedClient: ExtendedViemWalletClient,
|
|
1213
|
+
deployer: L1Deployer,
|
|
1214
|
+
gseAddress: Hex,
|
|
1215
|
+
rollupAddress: Hex,
|
|
1216
|
+
stakingAssetAddress: Hex,
|
|
1217
|
+
validators: Operator[],
|
|
1218
|
+
acceleratedTestDeployments: boolean | undefined,
|
|
1219
|
+
logger: Logger,
|
|
1220
|
+
) => {
|
|
1221
|
+
const rollup = new RollupContract(extendedClient, rollupAddress);
|
|
1222
|
+
const activationThreshold = await rollup.getActivationThreshold();
|
|
1223
|
+
if (validators && validators.length > 0) {
|
|
355
1224
|
// Check if some of the initial validators are already registered, so we support idempotent deployments
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
...(await rollup.read.getInfo([address.toString()])),
|
|
1225
|
+
if (!acceleratedTestDeployments) {
|
|
1226
|
+
const enrichedValidators = await Promise.all(
|
|
1227
|
+
validators.map(async operator => ({
|
|
1228
|
+
operator,
|
|
1229
|
+
status: await rollup.getStatus(operator.attester),
|
|
362
1230
|
})),
|
|
363
1231
|
);
|
|
364
|
-
const existingValidators =
|
|
1232
|
+
const existingValidators = enrichedValidators.filter(v => v.status !== 0);
|
|
365
1233
|
if (existingValidators.length > 0) {
|
|
366
1234
|
logger.warn(
|
|
367
1235
|
`Validators ${existingValidators
|
|
368
|
-
.map(v => v.
|
|
1236
|
+
.map(v => v.operator.attester)
|
|
369
1237
|
.join(', ')} already exist. Skipping from initialization.`,
|
|
370
1238
|
);
|
|
371
1239
|
}
|
|
372
1240
|
|
|
373
|
-
|
|
1241
|
+
validators = enrichedValidators.filter(v => v.status === 0).map(v => v.operator);
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
if (validators.length === 0) {
|
|
1245
|
+
logger.warn('No validators to add. Skipping.');
|
|
1246
|
+
return;
|
|
374
1247
|
}
|
|
375
1248
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
1249
|
+
const gseContract = new GSEContract(extendedClient, gseAddress);
|
|
1250
|
+
const multiAdder = (await deployer.deploy(MultiAdderArtifact, [rollupAddress, deployer.client.account.address]))
|
|
1251
|
+
.address;
|
|
1252
|
+
|
|
1253
|
+
const makeValidatorTuples = async (validator: Operator) => {
|
|
1254
|
+
const registrationTuple = await gseContract.makeRegistrationTuple(validator.bn254SecretKey.getValue());
|
|
1255
|
+
return {
|
|
1256
|
+
attester: getAddress(validator.attester.toString()),
|
|
1257
|
+
withdrawer: getAddress(validator.withdrawer.toString()),
|
|
1258
|
+
...registrationTuple,
|
|
1259
|
+
};
|
|
1260
|
+
};
|
|
1261
|
+
|
|
1262
|
+
const validatorsTuples = await Promise.all(validators.map(makeValidatorTuples));
|
|
1263
|
+
|
|
1264
|
+
// Mint tokens, approve them, use cheat code to initialize validator set without setting up the epoch.
|
|
1265
|
+
const stakeNeeded = activationThreshold * BigInt(validators.length);
|
|
1266
|
+
|
|
1267
|
+
await deployer.l1TxUtils.sendAndMonitorTransaction({
|
|
1268
|
+
to: stakingAssetAddress,
|
|
1269
|
+
data: encodeFunctionData({
|
|
1270
|
+
abi: StakingAssetArtifact.contractAbi,
|
|
1271
|
+
functionName: 'mint',
|
|
1272
|
+
args: [multiAdder.toString(), stakeNeeded],
|
|
1273
|
+
}),
|
|
1274
|
+
});
|
|
1275
|
+
|
|
1276
|
+
const entryQueueLengthBefore = await rollup.getEntryQueueLength();
|
|
1277
|
+
const validatorCountBefore = await rollup.getActiveAttesterCount();
|
|
1278
|
+
|
|
1279
|
+
logger.info(`Adding ${validators.length} validators to the rollup`);
|
|
1280
|
+
|
|
1281
|
+
const chunkSize = 16;
|
|
1282
|
+
|
|
1283
|
+
// We will add `chunkSize` validators to the queue until we have covered all of our validators.
|
|
1284
|
+
// The `chunkSize` needs to be small enough to fit inside a single tx, therefore 16.
|
|
1285
|
+
for (const c of chunk(validatorsTuples, chunkSize)) {
|
|
1286
|
+
await deployer.l1TxUtils.sendAndMonitorTransaction(
|
|
1287
|
+
{
|
|
1288
|
+
to: multiAdder.toString(),
|
|
1289
|
+
data: encodeFunctionData({
|
|
1290
|
+
abi: MultiAdderArtifact.contractAbi,
|
|
1291
|
+
functionName: 'addValidators',
|
|
1292
|
+
args: [c, BigInt(0)],
|
|
388
1293
|
}),
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
1294
|
+
},
|
|
1295
|
+
{
|
|
1296
|
+
gasLimit: 16_000_000n,
|
|
1297
|
+
},
|
|
1298
|
+
);
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
// After adding to the queue, we will now try to flush from it.
|
|
1302
|
+
// We are explicitly doing this as a second step instead of as part of adding to benefit
|
|
1303
|
+
// from the accounting used to speed the process up.
|
|
1304
|
+
// As the queue computes the amount of possible flushes in an epoch when told to flush,
|
|
1305
|
+
// waiting until we have added all we want allows us to benefit in the case were we added
|
|
1306
|
+
// enough to pass the bootstrap set size without needing to wait another epoch.
|
|
1307
|
+
// This is useful when we are testing as it speeds up the tests slightly.
|
|
1308
|
+
while (true) {
|
|
1309
|
+
// If the queue is empty, we can break
|
|
1310
|
+
if ((await rollup.getEntryQueueLength()) == 0n) {
|
|
1311
|
+
break;
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
// If there are no available validator flushes, no need to even try
|
|
1315
|
+
if ((await rollup.getAvailableValidatorFlushes()) == 0n) {
|
|
1316
|
+
break;
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
// Note that we are flushing at most `chunkSize` at each call
|
|
1320
|
+
await deployer.l1TxUtils.sendAndMonitorTransaction(
|
|
1321
|
+
{
|
|
1322
|
+
to: rollup.address,
|
|
1323
|
+
data: encodeFunctionData({
|
|
1324
|
+
abi: RollupArtifact.contractAbi,
|
|
1325
|
+
functionName: 'flushEntryQueue',
|
|
1326
|
+
args: [BigInt(chunkSize)],
|
|
396
1327
|
}),
|
|
397
|
-
|
|
1328
|
+
},
|
|
1329
|
+
{
|
|
1330
|
+
gasLimit: 16_000_000n,
|
|
1331
|
+
},
|
|
398
1332
|
);
|
|
1333
|
+
}
|
|
399
1334
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
functionName: 'cheat__InitialiseValidatorSet',
|
|
411
|
-
args: [validators],
|
|
412
|
-
});
|
|
413
|
-
txHashes.push(initiateValidatorSetTxHash);
|
|
414
|
-
logger.info(`Initialized validator set`, {
|
|
415
|
-
validators,
|
|
416
|
-
txHash: initiateValidatorSetTxHash,
|
|
417
|
-
});
|
|
1335
|
+
const entryQueueLengthAfter = await rollup.getEntryQueueLength();
|
|
1336
|
+
const validatorCountAfter = await rollup.getActiveAttesterCount();
|
|
1337
|
+
|
|
1338
|
+
if (
|
|
1339
|
+
entryQueueLengthAfter + validatorCountAfter <
|
|
1340
|
+
entryQueueLengthBefore + validatorCountBefore + BigInt(validators.length)
|
|
1341
|
+
) {
|
|
1342
|
+
throw new Error(
|
|
1343
|
+
`Failed to add ${validators.length} validators. Active validators: ${validatorCountBefore} -> ${validatorCountAfter}. Queue: ${entryQueueLengthBefore} -> ${entryQueueLengthAfter}. A likely issue is the bootstrap size.`,
|
|
1344
|
+
);
|
|
418
1345
|
}
|
|
419
|
-
}
|
|
420
1346
|
|
|
421
|
-
|
|
1347
|
+
logger.info(
|
|
1348
|
+
`Added ${validators.length} validators. Active validators: ${validatorCountBefore} -> ${validatorCountAfter}. Queue: ${entryQueueLengthBefore} -> ${entryQueueLengthAfter}`,
|
|
1349
|
+
);
|
|
1350
|
+
}
|
|
1351
|
+
};
|
|
422
1352
|
|
|
423
|
-
|
|
1353
|
+
/**
|
|
1354
|
+
* Initialize the fee asset handler and make it a minter on the fee asset.
|
|
1355
|
+
* @note This function will only be used for testing purposes.
|
|
1356
|
+
*
|
|
1357
|
+
* @param extendedClient - The L1 clients.
|
|
1358
|
+
* @param deployer - The L1 deployer.
|
|
1359
|
+
* @param feeAssetAddress - The address of the fee asset.
|
|
1360
|
+
* @param logger - The logger.
|
|
1361
|
+
*/
|
|
1362
|
+
// eslint-disable-next-line camelcase
|
|
1363
|
+
export const cheat_initializeFeeAssetHandler = async (
|
|
1364
|
+
extendedClient: ExtendedViemWalletClient,
|
|
1365
|
+
deployer: L1Deployer,
|
|
1366
|
+
feeAssetAddress: EthAddress,
|
|
1367
|
+
logger: Logger,
|
|
1368
|
+
): Promise<{
|
|
1369
|
+
feeAssetHandlerAddress: EthAddress;
|
|
1370
|
+
txHash: Hex;
|
|
1371
|
+
}> => {
|
|
1372
|
+
const feeAssetHandlerAddress = (
|
|
1373
|
+
await deployer.deploy(FeeAssetHandlerArtifact, [
|
|
1374
|
+
extendedClient.account.address,
|
|
1375
|
+
feeAssetAddress.toString(),
|
|
1376
|
+
BigInt(1e18),
|
|
1377
|
+
])
|
|
1378
|
+
).address;
|
|
1379
|
+
logger.verbose(`Deployed FeeAssetHandler at ${feeAssetHandlerAddress}`);
|
|
1380
|
+
|
|
1381
|
+
const { txHash } = await deployer.sendTransaction({
|
|
1382
|
+
to: feeAssetAddress.toString(),
|
|
1383
|
+
data: encodeFunctionData({
|
|
1384
|
+
abi: FeeAssetArtifact.contractAbi,
|
|
1385
|
+
functionName: 'addMinter',
|
|
1386
|
+
args: [feeAssetHandlerAddress.toString()],
|
|
1387
|
+
}),
|
|
1388
|
+
});
|
|
1389
|
+
logger.verbose(`Added fee asset handler ${feeAssetHandlerAddress} as minter on fee asset in ${txHash}`);
|
|
1390
|
+
return { feeAssetHandlerAddress, txHash };
|
|
424
1391
|
};
|
|
425
1392
|
|
|
426
1393
|
/**
|
|
@@ -438,17 +1405,30 @@ export const deployL1Contracts = async (
|
|
|
438
1405
|
chain: Chain,
|
|
439
1406
|
logger: Logger,
|
|
440
1407
|
args: DeployL1ContractsArgs,
|
|
441
|
-
txUtilsConfig: L1TxUtilsConfig =
|
|
1408
|
+
txUtilsConfig: L1TxUtilsConfig = getL1TxUtilsConfigEnvVars(),
|
|
1409
|
+
createVerificationJson: string | false = false,
|
|
442
1410
|
): Promise<DeployL1ContractsReturnType> => {
|
|
443
|
-
|
|
444
|
-
|
|
1411
|
+
logger.info(`Deploying L1 contracts with config: ${jsonStringify(args)}`);
|
|
1412
|
+
validateConfig(args);
|
|
1413
|
+
|
|
1414
|
+
if (args.initialValidators && args.initialValidators.length > 0 && args.existingTokenAddress) {
|
|
1415
|
+
throw new Error(
|
|
1416
|
+
'Cannot deploy with both initialValidators and existingTokenAddress. ' +
|
|
1417
|
+
'Initial validator funding requires minting tokens, which is not possible with an external token.',
|
|
1418
|
+
);
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
const l1Client = createExtendedL1Client(rpcUrls, account, chain);
|
|
1422
|
+
|
|
1423
|
+
// Deploy multicall3 if it does not exist in this network
|
|
1424
|
+
await deployMulticall3(l1Client, logger);
|
|
445
1425
|
|
|
446
1426
|
// We are assuming that you are running this on a local anvil node which have 1s block times
|
|
447
1427
|
// To align better with actual deployment, we update the block interval to 12s
|
|
448
1428
|
|
|
449
1429
|
const rpcCall = async (method: string, params: any[]) => {
|
|
450
1430
|
logger.info(`Calling ${method} with params: ${JSON.stringify(params)}`);
|
|
451
|
-
return (await
|
|
1431
|
+
return (await l1Client.transport.request({
|
|
452
1432
|
method,
|
|
453
1433
|
params,
|
|
454
1434
|
})) as any;
|
|
@@ -465,229 +1445,74 @@ export const deployL1Contracts = async (
|
|
|
465
1445
|
|
|
466
1446
|
logger.verbose(`Deploying contracts from ${account.address.toString()}`);
|
|
467
1447
|
|
|
468
|
-
|
|
1448
|
+
const dateProvider = new DateProvider();
|
|
469
1449
|
const deployer = new L1Deployer(
|
|
470
|
-
|
|
471
|
-
publicClient,
|
|
1450
|
+
l1Client,
|
|
472
1451
|
args.salt,
|
|
1452
|
+
dateProvider,
|
|
473
1453
|
args.acceleratedTestDeployments,
|
|
474
1454
|
logger,
|
|
475
1455
|
txUtilsConfig,
|
|
1456
|
+
!!createVerificationJson,
|
|
476
1457
|
);
|
|
477
1458
|
|
|
478
|
-
const
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
registryAddress.toString(),
|
|
493
|
-
args.governanceProposerQuorum,
|
|
494
|
-
args.governanceProposerRoundSize,
|
|
495
|
-
]);
|
|
496
|
-
logger.verbose(`Deployed GovernanceProposer at ${governanceProposerAddress}`);
|
|
497
|
-
|
|
498
|
-
// @note @LHerskind the assets are expected to be the same at some point, but for better
|
|
499
|
-
// configurability they are different for now.
|
|
500
|
-
const governanceAddress = await deployer.deploy(l1Artifacts.governance, [
|
|
501
|
-
feeAssetAddress.toString(),
|
|
502
|
-
governanceProposerAddress.toString(),
|
|
503
|
-
]);
|
|
504
|
-
logger.verbose(`Deployed Governance at ${governanceAddress}`);
|
|
505
|
-
|
|
506
|
-
const coinIssuerAddress = await deployer.deploy(l1Artifacts.coinIssuer, [
|
|
507
|
-
feeAssetAddress.toString(),
|
|
508
|
-
1n * 10n ** 18n, // @todo #8084
|
|
509
|
-
governanceAddress.toString(),
|
|
510
|
-
]);
|
|
511
|
-
logger.verbose(`Deployed CoinIssuer at ${coinIssuerAddress}`);
|
|
512
|
-
|
|
513
|
-
const rewardDistributorAddress = await deployer.deploy(l1Artifacts.rewardDistributor, [
|
|
514
|
-
feeAssetAddress.toString(),
|
|
515
|
-
registryAddress.toString(),
|
|
516
|
-
governanceAddress.toString(),
|
|
517
|
-
]);
|
|
518
|
-
logger.verbose(`Deployed RewardDistributor at ${rewardDistributorAddress}`);
|
|
519
|
-
|
|
520
|
-
const feeJuicePortalAddress = await deployer.deploy(l1Artifacts.feeJuicePortal, [
|
|
521
|
-
registryAddress.toString(),
|
|
522
|
-
feeAssetAddress.toString(),
|
|
523
|
-
args.l2FeeJuiceAddress.toString(),
|
|
524
|
-
]);
|
|
525
|
-
logger.verbose(`Deployed Fee Juice Portal at ${feeJuicePortalAddress}`);
|
|
526
|
-
|
|
527
|
-
logger.verbose(`Waiting for governance contracts to be deployed`);
|
|
528
|
-
await deployer.waitForDeployments();
|
|
529
|
-
logger.verbose(`All governance contracts deployed`);
|
|
530
|
-
|
|
531
|
-
const feeJuicePortal = getContract({
|
|
532
|
-
address: feeJuicePortalAddress.toString(),
|
|
533
|
-
abi: l1Artifacts.feeJuicePortal.contractAbi,
|
|
534
|
-
client: walletClient,
|
|
535
|
-
});
|
|
536
|
-
|
|
537
|
-
const feeAsset = getContract({
|
|
538
|
-
address: feeAssetAddress.toString(),
|
|
539
|
-
abi: l1Artifacts.feeAsset.contractAbi,
|
|
540
|
-
client: walletClient,
|
|
541
|
-
});
|
|
542
|
-
// Transaction hashes to await
|
|
543
|
-
const txHashes: Hex[] = [];
|
|
544
|
-
|
|
545
|
-
if (args.acceleratedTestDeployments || !(await feeAsset.read.freeForAll())) {
|
|
546
|
-
const { txHash } = await deployer.sendTransaction({
|
|
547
|
-
to: feeAssetAddress.toString(),
|
|
548
|
-
data: encodeFunctionData({
|
|
549
|
-
abi: l1Artifacts.feeAsset.contractAbi,
|
|
550
|
-
functionName: 'setFreeForAll',
|
|
551
|
-
args: [true],
|
|
552
|
-
}),
|
|
553
|
-
});
|
|
554
|
-
logger.verbose(`Fee asset set to free for all in ${txHash}`);
|
|
555
|
-
txHashes.push(txHash);
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
if (args.acceleratedTestDeployments || (await feeAsset.read.owner()) !== getAddress(coinIssuerAddress.toString())) {
|
|
559
|
-
const { txHash } = await deployer.sendTransaction({
|
|
560
|
-
to: feeAssetAddress.toString(),
|
|
561
|
-
data: encodeFunctionData({
|
|
562
|
-
abi: l1Artifacts.feeAsset.contractAbi,
|
|
563
|
-
functionName: 'transferOwnership',
|
|
564
|
-
args: [coinIssuerAddress.toString()],
|
|
565
|
-
}),
|
|
566
|
-
});
|
|
567
|
-
logger.verbose(`Fee asset transferred ownership to coin issuer in ${txHash}`);
|
|
568
|
-
txHashes.push(txHash);
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
// @note This value MUST match what is in `constants.nr`. It is currently specified here instead of just importing
|
|
572
|
-
// because there is circular dependency hell. This is a temporary solution. #3342
|
|
573
|
-
// @todo #8084
|
|
574
|
-
// fund the portal contract with Fee Juice
|
|
575
|
-
const FEE_JUICE_INITIAL_MINT = 200000000000000000000000n;
|
|
576
|
-
|
|
577
|
-
// In fast mode, use the L1TxUtils to send transactions with nonce management
|
|
578
|
-
const { txHash: mintTxHash } = await deployer.sendTransaction({
|
|
579
|
-
to: feeAssetAddress.toString(),
|
|
580
|
-
data: encodeFunctionData({
|
|
581
|
-
abi: l1Artifacts.feeAsset.contractAbi,
|
|
582
|
-
functionName: 'mint',
|
|
583
|
-
args: [feeJuicePortalAddress.toString(), FEE_JUICE_INITIAL_MINT],
|
|
584
|
-
}),
|
|
585
|
-
});
|
|
586
|
-
logger.verbose(`Funding fee juice portal contract with fee juice in ${mintTxHash} (accelerated test deployments)`);
|
|
587
|
-
txHashes.push(mintTxHash);
|
|
588
|
-
|
|
589
|
-
// @note This is used to ensure we fully wait for the transaction when running against a real chain
|
|
590
|
-
// otherwise we execute subsequent transactions too soon
|
|
591
|
-
if (!args.acceleratedTestDeployments) {
|
|
592
|
-
await publicClient.waitForTransactionReceipt({ hash: mintTxHash });
|
|
593
|
-
logger.verbose(`Funding fee juice portal contract with fee juice in ${mintTxHash}`);
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
// Check if portal needs initialization
|
|
597
|
-
let needsInitialization = args.acceleratedTestDeployments;
|
|
598
|
-
if (!args.acceleratedTestDeployments) {
|
|
599
|
-
// Only check if not in fast mode and not already known to need initialization
|
|
600
|
-
needsInitialization = !(await feeJuicePortal.read.initialized());
|
|
601
|
-
}
|
|
602
|
-
if (needsInitialization) {
|
|
603
|
-
const { txHash: initPortalTxHash } = await deployer.sendTransaction({
|
|
604
|
-
to: feeJuicePortalAddress.toString(),
|
|
605
|
-
data: encodeFunctionData({
|
|
606
|
-
abi: l1Artifacts.feeJuicePortal.contractAbi,
|
|
607
|
-
functionName: 'initialize',
|
|
608
|
-
args: [],
|
|
609
|
-
}),
|
|
610
|
-
});
|
|
611
|
-
|
|
612
|
-
txHashes.push(initPortalTxHash);
|
|
613
|
-
logger.verbose(`Fee juice portal initializing in tx ${initPortalTxHash}`);
|
|
614
|
-
} else {
|
|
615
|
-
logger.verbose(`Fee juice portal is already initialized`);
|
|
616
|
-
}
|
|
617
|
-
logger.verbose(
|
|
618
|
-
`Initialized Fee Juice Portal at ${feeJuicePortalAddress} to bridge between L1 ${feeAssetAddress} to L2 ${args.l2FeeJuiceAddress}`,
|
|
619
|
-
);
|
|
620
|
-
|
|
621
|
-
const rollup = await deployRollup(
|
|
622
|
-
{
|
|
623
|
-
walletClient,
|
|
624
|
-
publicClient,
|
|
625
|
-
},
|
|
1459
|
+
const {
|
|
1460
|
+
feeAssetAddress,
|
|
1461
|
+
feeAssetHandlerAddress,
|
|
1462
|
+
stakingAssetAddress,
|
|
1463
|
+
stakingAssetHandlerAddress,
|
|
1464
|
+
registryAddress,
|
|
1465
|
+
gseAddress,
|
|
1466
|
+
governanceAddress,
|
|
1467
|
+
rewardDistributorAddress,
|
|
1468
|
+
zkPassportVerifierAddress,
|
|
1469
|
+
coinIssuerAddress,
|
|
1470
|
+
} = await deploySharedContracts(l1Client, deployer, args, logger);
|
|
1471
|
+
const { rollup, slashFactoryAddress } = await deployRollup(
|
|
1472
|
+
l1Client,
|
|
626
1473
|
deployer,
|
|
627
1474
|
args,
|
|
628
|
-
{
|
|
1475
|
+
{
|
|
1476
|
+
feeJuiceAddress: feeAssetAddress,
|
|
1477
|
+
registryAddress,
|
|
1478
|
+
gseAddress,
|
|
1479
|
+
rewardDistributorAddress,
|
|
1480
|
+
stakingAssetAddress,
|
|
1481
|
+
governanceAddress,
|
|
1482
|
+
},
|
|
629
1483
|
logger,
|
|
630
1484
|
);
|
|
631
|
-
const slashFactoryAddress = await deploySlashFactory(deployer, rollup.address, logger);
|
|
632
1485
|
|
|
633
1486
|
logger.verbose('Waiting for rollup and slash factory to be deployed');
|
|
634
1487
|
await deployer.waitForDeployments();
|
|
635
|
-
logger.verbose(`Rollup and slash factory deployed`);
|
|
636
|
-
|
|
637
|
-
// We need to call a function on the registry to set the various contract addresses.
|
|
638
|
-
const registryContract = getContract({
|
|
639
|
-
address: getAddress(registryAddress.toString()),
|
|
640
|
-
abi: l1Artifacts.registry.contractAbi,
|
|
641
|
-
client: walletClient,
|
|
642
|
-
});
|
|
643
1488
|
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
`Upgrading registry contract at ${registryAddress} to rollup ${rollup.address} in tx ${upgradeTxHash}`,
|
|
658
|
-
);
|
|
659
|
-
txHashes.push(upgradeTxHash);
|
|
660
|
-
} else {
|
|
661
|
-
logger.verbose(`Registry ${registryAddress} has already registered rollup ${rollup.address}`);
|
|
662
|
-
}
|
|
1489
|
+
// Now that the rollup has been deployed and added to the registry, transfer ownership to governance
|
|
1490
|
+
const { dateGatedRelayerAddress } = await handoverToGovernance(
|
|
1491
|
+
l1Client,
|
|
1492
|
+
deployer,
|
|
1493
|
+
registryAddress,
|
|
1494
|
+
gseAddress,
|
|
1495
|
+
coinIssuerAddress,
|
|
1496
|
+
feeAssetAddress,
|
|
1497
|
+
governanceAddress,
|
|
1498
|
+
logger,
|
|
1499
|
+
args.acceleratedTestDeployments,
|
|
1500
|
+
!!args.existingTokenAddress,
|
|
1501
|
+
);
|
|
663
1502
|
|
|
664
|
-
|
|
665
|
-
if (
|
|
666
|
-
args.acceleratedTestDeployments ||
|
|
667
|
-
(await registryContract.read.owner()) !== getAddress(governanceAddress.toString())
|
|
668
|
-
) {
|
|
669
|
-
// TODO(md): add send transaction to the deployer such that we do not need to manage tx hashes here
|
|
670
|
-
const { txHash: transferOwnershipTxHash } = await deployer.sendTransaction({
|
|
671
|
-
to: registryAddress.toString(),
|
|
672
|
-
data: encodeFunctionData({
|
|
673
|
-
abi: l1Artifacts.registry.contractAbi,
|
|
674
|
-
functionName: 'transferOwnership',
|
|
675
|
-
args: [getAddress(governanceAddress.toString())],
|
|
676
|
-
}),
|
|
677
|
-
});
|
|
678
|
-
logger.verbose(
|
|
679
|
-
`Transferring the ownership of the registry contract at ${registryAddress} to the Governance ${governanceAddress} in tx ${transferOwnershipTxHash}`,
|
|
680
|
-
);
|
|
681
|
-
txHashes.push(transferOwnershipTxHash);
|
|
682
|
-
}
|
|
1503
|
+
logger.info(`Handing over to governance complete`);
|
|
683
1504
|
|
|
684
|
-
// Wait for all actions to be mined
|
|
685
|
-
await Promise.all(txHashes.map(txHash => publicClient.waitForTransactionReceipt({ hash: txHash })));
|
|
686
1505
|
logger.verbose(`All transactions for L1 deployment have been mined`);
|
|
687
|
-
const l1Contracts = await RegistryContract.collectAddresses(
|
|
1506
|
+
const l1Contracts = await RegistryContract.collectAddresses(l1Client, registryAddress, 'canonical');
|
|
688
1507
|
|
|
689
1508
|
logger.info(`Aztec L1 contracts initialized`, l1Contracts);
|
|
690
1509
|
|
|
1510
|
+
// Write verification data (constructor args + linked libraries) to file for later forge verify
|
|
1511
|
+
if (createVerificationJson) {
|
|
1512
|
+
await generateRollupVerificationRecords(rollup, deployer, args, l1Contracts, l1Client, logger);
|
|
1513
|
+
await writeVerificationJson(deployer, createVerificationJson, chain.id, '', logger);
|
|
1514
|
+
}
|
|
1515
|
+
|
|
691
1516
|
if (isAnvilTestChain(chain.id)) {
|
|
692
1517
|
// @note We make a time jump PAST the very first slot to not have to deal with the edge case of the first slot.
|
|
693
1518
|
// The edge case being that the genesis block is already occupying slot 0, so we cannot have another block.
|
|
@@ -695,13 +1520,13 @@ export const deployL1Contracts = async (
|
|
|
695
1520
|
// Need to get the time
|
|
696
1521
|
const currentSlot = await rollup.getSlotNumber();
|
|
697
1522
|
|
|
698
|
-
if (
|
|
699
|
-
const ts = Number(await rollup.getTimestampForSlot(
|
|
1523
|
+
if (currentSlot === 0) {
|
|
1524
|
+
const ts = Number(await rollup.getTimestampForSlot(SlotNumber(1)));
|
|
700
1525
|
await rpcCall('evm_setNextBlockTimestamp', [ts]);
|
|
701
1526
|
await rpcCall('hardhat_mine', [1]);
|
|
702
1527
|
const currentSlot = await rollup.getSlotNumber();
|
|
703
1528
|
|
|
704
|
-
if (
|
|
1529
|
+
if (currentSlot !== 1) {
|
|
705
1530
|
throw new Error(`Error jumping time: current slot is ${currentSlot}`);
|
|
706
1531
|
}
|
|
707
1532
|
logger.info(`Jumped to slot 1`);
|
|
@@ -712,55 +1537,96 @@ export const deployL1Contracts = async (
|
|
|
712
1537
|
}
|
|
713
1538
|
|
|
714
1539
|
return {
|
|
715
|
-
|
|
716
|
-
|
|
1540
|
+
rollupVersion: Number(await rollup.getVersion()),
|
|
1541
|
+
l1Client: l1Client,
|
|
717
1542
|
l1ContractAddresses: {
|
|
718
1543
|
...l1Contracts,
|
|
719
1544
|
slashFactoryAddress,
|
|
1545
|
+
feeAssetHandlerAddress,
|
|
1546
|
+
stakingAssetHandlerAddress,
|
|
1547
|
+
zkPassportVerifierAddress,
|
|
1548
|
+
coinIssuerAddress,
|
|
1549
|
+
dateGatedRelayerAddress,
|
|
720
1550
|
},
|
|
721
1551
|
};
|
|
722
1552
|
};
|
|
723
1553
|
|
|
724
|
-
class L1Deployer {
|
|
1554
|
+
export class L1Deployer {
|
|
725
1555
|
private salt: Hex | undefined;
|
|
726
1556
|
private txHashes: Hex[] = [];
|
|
727
1557
|
public readonly l1TxUtils: L1TxUtils;
|
|
1558
|
+
public readonly verificationRecords: VerificationRecord[] = [];
|
|
728
1559
|
|
|
729
1560
|
constructor(
|
|
730
|
-
public readonly
|
|
731
|
-
private publicClient: ViemPublicClient,
|
|
1561
|
+
public readonly client: ExtendedViemWalletClient,
|
|
732
1562
|
maybeSalt: number | undefined,
|
|
1563
|
+
dateProvider: DateProvider = new DateProvider(),
|
|
733
1564
|
private acceleratedTestDeployments: boolean = false,
|
|
734
1565
|
private logger: Logger = createLogger('L1Deployer'),
|
|
735
1566
|
private txUtilsConfig?: L1TxUtilsConfig,
|
|
1567
|
+
private createVerificationJson: boolean = false,
|
|
736
1568
|
) {
|
|
737
1569
|
this.salt = maybeSalt ? padHex(numberToHex(maybeSalt), { size: 32 }) : undefined;
|
|
738
|
-
this.l1TxUtils =
|
|
739
|
-
this.
|
|
740
|
-
this.
|
|
741
|
-
this.
|
|
742
|
-
this.txUtilsConfig,
|
|
743
|
-
this.acceleratedTestDeployments,
|
|
1570
|
+
this.l1TxUtils = createL1TxUtilsFromViemWallet(
|
|
1571
|
+
this.client,
|
|
1572
|
+
{ logger: this.logger, dateProvider },
|
|
1573
|
+
{ ...this.txUtilsConfig, debugMaxGasLimit: acceleratedTestDeployments },
|
|
744
1574
|
);
|
|
745
1575
|
}
|
|
746
1576
|
|
|
747
|
-
async deploy
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
1577
|
+
async deploy<const TAbi extends Abi>(
|
|
1578
|
+
params: ContractArtifacts<TAbi>,
|
|
1579
|
+
args?: ContractConstructorArgs<TAbi>,
|
|
1580
|
+
opts: { gasLimit?: bigint; noSimulation?: boolean } = {},
|
|
1581
|
+
): Promise<{ address: EthAddress; existed: boolean }> {
|
|
1582
|
+
this.logger.debug(`Deploying ${params.name} contract`, { args });
|
|
1583
|
+
try {
|
|
1584
|
+
const { txHash, address, deployedLibraries, existed } = await deployL1Contract(
|
|
1585
|
+
this.client,
|
|
1586
|
+
params.contractAbi,
|
|
1587
|
+
params.contractBytecode,
|
|
1588
|
+
(args ?? []) as readonly unknown[],
|
|
1589
|
+
{
|
|
1590
|
+
salt: this.salt,
|
|
1591
|
+
libraries: params.libraries,
|
|
1592
|
+
logger: this.logger,
|
|
1593
|
+
l1TxUtils: this.l1TxUtils,
|
|
1594
|
+
acceleratedTestDeployments: this.acceleratedTestDeployments,
|
|
1595
|
+
gasLimit: opts.gasLimit,
|
|
1596
|
+
noSimulation: opts.noSimulation,
|
|
1597
|
+
},
|
|
1598
|
+
);
|
|
1599
|
+
if (txHash) {
|
|
1600
|
+
this.txHashes.push(txHash);
|
|
1601
|
+
}
|
|
1602
|
+
this.logger.debug(`Deployed ${params.name} at ${address}`, { args });
|
|
1603
|
+
|
|
1604
|
+
if (this.createVerificationJson) {
|
|
1605
|
+
// Encode constructor args for verification
|
|
1606
|
+
let constructorArgsHex: Hex = '0x';
|
|
1607
|
+
try {
|
|
1608
|
+
const abiItem: any = (params.contractAbi as any[]).find((x: any) => x && x.type === 'constructor');
|
|
1609
|
+
const inputDefs: any[] = abiItem && Array.isArray(abiItem.inputs) ? abiItem.inputs : [];
|
|
1610
|
+
constructorArgsHex =
|
|
1611
|
+
inputDefs.length > 0 ? (encodeAbiParameters(inputDefs as any, (args ?? []) as any) as Hex) : ('0x' as Hex);
|
|
1612
|
+
} catch {
|
|
1613
|
+
constructorArgsHex = '0x' as Hex;
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
this.verificationRecords.push({
|
|
1617
|
+
name: params.name,
|
|
1618
|
+
address: address.toString(),
|
|
1619
|
+
constructorArgsHex,
|
|
1620
|
+
libraries: deployedLibraries ?? [],
|
|
1621
|
+
});
|
|
1622
|
+
}
|
|
1623
|
+
return {
|
|
1624
|
+
address,
|
|
1625
|
+
existed,
|
|
1626
|
+
};
|
|
1627
|
+
} catch (error) {
|
|
1628
|
+
throw new Error(`Failed to deploy ${params.name}`, { cause: formatViemError(error) });
|
|
762
1629
|
}
|
|
763
|
-
return address;
|
|
764
1630
|
}
|
|
765
1631
|
|
|
766
1632
|
async waitForDeployments(): Promise<void> {
|
|
@@ -772,17 +1638,29 @@ class L1Deployer {
|
|
|
772
1638
|
return;
|
|
773
1639
|
}
|
|
774
1640
|
|
|
775
|
-
this.logger.
|
|
776
|
-
await Promise.all(
|
|
777
|
-
|
|
1641
|
+
this.logger.verbose(`Waiting for ${this.txHashes.length} transactions to be mined`, { txHashes: this.txHashes });
|
|
1642
|
+
const receipts = await Promise.all(
|
|
1643
|
+
this.txHashes.map(txHash => this.client.waitForTransactionReceipt({ hash: txHash })),
|
|
1644
|
+
);
|
|
1645
|
+
const failed = receipts.filter(r => r.status !== 'success');
|
|
1646
|
+
if (failed.length > 0) {
|
|
1647
|
+
throw new Error(`Some deployment txs have failed: ${failed.map(f => f.transactionHash).join(', ')}`);
|
|
1648
|
+
}
|
|
1649
|
+
this.logger.info('All transactions mined successfully', { txHashes: this.txHashes });
|
|
778
1650
|
}
|
|
779
1651
|
|
|
780
|
-
sendTransaction(
|
|
781
|
-
|
|
1652
|
+
sendTransaction(
|
|
1653
|
+
tx: L1TxRequest,
|
|
1654
|
+
options?: L1TxConfig,
|
|
1655
|
+
): Promise<{ txHash: Hex; gasLimit: bigint; gasPrice: GasPrice }> {
|
|
1656
|
+
return this.l1TxUtils.sendTransaction(tx, options).then(({ txHash, state }) => ({
|
|
1657
|
+
txHash,
|
|
1658
|
+
gasLimit: state.gasLimit,
|
|
1659
|
+
gasPrice: state.gasPrice,
|
|
1660
|
+
}));
|
|
782
1661
|
}
|
|
783
1662
|
}
|
|
784
1663
|
|
|
785
|
-
// docs:start:deployL1Contract
|
|
786
1664
|
/**
|
|
787
1665
|
* Helper function to deploy ETH contracts.
|
|
788
1666
|
* @param walletClient - A viem WalletClient.
|
|
@@ -790,26 +1668,43 @@ class L1Deployer {
|
|
|
790
1668
|
* @param abi - The ETH contract's ABI (as abitype's Abi).
|
|
791
1669
|
* @param bytecode - The ETH contract's bytecode.
|
|
792
1670
|
* @param args - Constructor arguments for the contract.
|
|
793
|
-
* @param
|
|
1671
|
+
* @param salt - Optional salt for CREATE2 deployment (does not wait for deployment tx to be mined if set, does not send tx if contract already exists).
|
|
794
1672
|
* @returns The ETH address the contract was deployed to.
|
|
795
1673
|
*/
|
|
796
1674
|
export async function deployL1Contract(
|
|
797
|
-
|
|
798
|
-
publicClient: ViemPublicClient,
|
|
1675
|
+
extendedClient: ExtendedViemWalletClient,
|
|
799
1676
|
abi: Narrow<Abi | readonly unknown[]>,
|
|
800
1677
|
bytecode: Hex,
|
|
801
1678
|
args: readonly unknown[] = [],
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
1679
|
+
opts: {
|
|
1680
|
+
salt?: Hex;
|
|
1681
|
+
libraries?: Libraries;
|
|
1682
|
+
logger?: Logger;
|
|
1683
|
+
l1TxUtils?: L1TxUtils;
|
|
1684
|
+
gasLimit?: bigint;
|
|
1685
|
+
acceleratedTestDeployments?: boolean;
|
|
1686
|
+
noSimulation?: boolean;
|
|
1687
|
+
} = {},
|
|
1688
|
+
): Promise<{
|
|
1689
|
+
address: EthAddress;
|
|
1690
|
+
txHash: Hex | undefined;
|
|
1691
|
+
deployedLibraries?: VerificationLibraryEntry[];
|
|
1692
|
+
existed: boolean;
|
|
1693
|
+
}> {
|
|
808
1694
|
let txHash: Hex | undefined = undefined;
|
|
809
1695
|
let resultingAddress: Hex | null | undefined = undefined;
|
|
1696
|
+
const deployedLibraries: VerificationLibraryEntry[] = [];
|
|
1697
|
+
|
|
1698
|
+
const { salt: saltFromOpts, libraries, logger, gasLimit, acceleratedTestDeployments, noSimulation } = opts;
|
|
1699
|
+
let { l1TxUtils } = opts;
|
|
810
1700
|
|
|
811
1701
|
if (!l1TxUtils) {
|
|
812
|
-
|
|
1702
|
+
const config = getL1TxUtilsConfigEnvVars();
|
|
1703
|
+
l1TxUtils = createL1TxUtilsFromViemWallet(
|
|
1704
|
+
extendedClient,
|
|
1705
|
+
{ logger },
|
|
1706
|
+
{ ...config, debugMaxGasLimit: acceleratedTestDeployments },
|
|
1707
|
+
);
|
|
813
1708
|
}
|
|
814
1709
|
|
|
815
1710
|
if (libraries) {
|
|
@@ -828,24 +1723,38 @@ export async function deployL1Contract(
|
|
|
828
1723
|
const libraryTxs: Hex[] = [];
|
|
829
1724
|
for (const libraryName in libraries?.libraryCode) {
|
|
830
1725
|
const lib = libraries.libraryCode[libraryName];
|
|
831
|
-
|
|
1726
|
+
const { libraries: _libraries, ...optsWithoutLibraries } = opts;
|
|
832
1727
|
const { address, txHash } = await deployL1Contract(
|
|
833
|
-
|
|
834
|
-
publicClient,
|
|
1728
|
+
extendedClient,
|
|
835
1729
|
lib.contractAbi,
|
|
836
1730
|
lib.contractBytecode,
|
|
837
1731
|
[],
|
|
838
|
-
|
|
839
|
-
undefined,
|
|
840
|
-
logger,
|
|
841
|
-
l1TxUtils,
|
|
842
|
-
acceleratedTestDeployments,
|
|
1732
|
+
optsWithoutLibraries,
|
|
843
1733
|
);
|
|
844
1734
|
|
|
1735
|
+
// Log deployed library name and address for easier verification/triage
|
|
1736
|
+
logger?.verbose(`Linked library deployed`, { library: libraryName, address: address.toString(), txHash });
|
|
1737
|
+
|
|
845
1738
|
if (txHash) {
|
|
846
1739
|
libraryTxs.push(txHash);
|
|
847
1740
|
}
|
|
848
1741
|
|
|
1742
|
+
// Try to find the source file for this library from linkReferences
|
|
1743
|
+
let fileNameForLibrary: string | undefined = undefined;
|
|
1744
|
+
for (const fileName in libraries.linkReferences) {
|
|
1745
|
+
if (libraries.linkReferences[fileName] && libraries.linkReferences[fileName][libraryName]) {
|
|
1746
|
+
fileNameForLibrary = fileName;
|
|
1747
|
+
break;
|
|
1748
|
+
}
|
|
1749
|
+
}
|
|
1750
|
+
if (fileNameForLibrary) {
|
|
1751
|
+
deployedLibraries.push({
|
|
1752
|
+
file: fileNameForLibrary,
|
|
1753
|
+
contract: libraryName,
|
|
1754
|
+
address: address.toString(),
|
|
1755
|
+
});
|
|
1756
|
+
}
|
|
1757
|
+
|
|
849
1758
|
for (const linkRef in libraries.linkReferences) {
|
|
850
1759
|
for (const contractName in libraries.linkReferences[linkRef]) {
|
|
851
1760
|
// If the library name matches the one we just deployed, we replace it.
|
|
@@ -877,7 +1786,7 @@ export async function deployL1Contract(
|
|
|
877
1786
|
// However, if we are in fast mode or using debugMaxGasLimit, we will skip simulation, so we can skip waiting.
|
|
878
1787
|
if (libraryTxs.length > 0 && !acceleratedTestDeployments) {
|
|
879
1788
|
logger?.verbose(`Awaiting for linked libraries to be deployed`);
|
|
880
|
-
await Promise.all(libraryTxs.map(txHash =>
|
|
1789
|
+
await Promise.all(libraryTxs.map(txHash => extendedClient.waitForTransactionReceipt({ hash: txHash })));
|
|
881
1790
|
} else {
|
|
882
1791
|
logger?.verbose(
|
|
883
1792
|
`Skipping waiting for linked libraries to be deployed ${
|
|
@@ -887,28 +1796,42 @@ export async function deployL1Contract(
|
|
|
887
1796
|
}
|
|
888
1797
|
}
|
|
889
1798
|
|
|
890
|
-
|
|
891
|
-
|
|
1799
|
+
let existed = false;
|
|
1800
|
+
|
|
1801
|
+
if (saltFromOpts) {
|
|
1802
|
+
logger?.info(`Deploying contract with salt ${saltFromOpts}`);
|
|
1803
|
+
const { address, paddedSalt: salt, calldata } = getExpectedAddress(abi, bytecode, args, saltFromOpts);
|
|
892
1804
|
resultingAddress = address;
|
|
893
|
-
const existing = await
|
|
1805
|
+
const existing = await extendedClient.getCode({ address: resultingAddress });
|
|
894
1806
|
if (existing === undefined || existing === '0x') {
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
1807
|
+
if (!noSimulation) {
|
|
1808
|
+
try {
|
|
1809
|
+
await l1TxUtils.simulate({ to: DEPLOYER_ADDRESS, data: concatHex([salt, calldata]), gas: gasLimit });
|
|
1810
|
+
} catch (err) {
|
|
1811
|
+
logger?.error(`Failed to simulate deployment tx using universal deployer`, err);
|
|
1812
|
+
await l1TxUtils.simulate({ to: null, data: encodeDeployData({ abi, bytecode, args }), gas: gasLimit });
|
|
1813
|
+
}
|
|
1814
|
+
}
|
|
1815
|
+
const res = await l1TxUtils.sendTransaction(
|
|
1816
|
+
{ to: DEPLOYER_ADDRESS, data: concatHex([salt, calldata]) },
|
|
1817
|
+
{ gasLimit },
|
|
1818
|
+
);
|
|
899
1819
|
txHash = res.txHash;
|
|
900
1820
|
|
|
901
1821
|
logger?.verbose(`Deployed contract with salt ${salt} to address ${resultingAddress} in tx ${txHash}.`);
|
|
902
1822
|
} else {
|
|
903
1823
|
logger?.verbose(`Skipping existing deployment of contract with salt ${salt} to address ${resultingAddress}`);
|
|
1824
|
+
existed = true;
|
|
904
1825
|
}
|
|
905
1826
|
} else {
|
|
906
|
-
// Regular deployment path
|
|
907
1827
|
const deployData = encodeDeployData({ abi, bytecode, args });
|
|
908
|
-
const { receipt } = await l1TxUtils.sendAndMonitorTransaction(
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
1828
|
+
const { receipt } = await l1TxUtils.sendAndMonitorTransaction(
|
|
1829
|
+
{
|
|
1830
|
+
to: null,
|
|
1831
|
+
data: deployData,
|
|
1832
|
+
},
|
|
1833
|
+
{ gasLimit },
|
|
1834
|
+
);
|
|
912
1835
|
|
|
913
1836
|
txHash = receipt.transactionHash;
|
|
914
1837
|
resultingAddress = receipt.contractAddress;
|
|
@@ -921,7 +1844,7 @@ export async function deployL1Contract(
|
|
|
921
1844
|
}
|
|
922
1845
|
}
|
|
923
1846
|
|
|
924
|
-
return { address: EthAddress.fromString(resultingAddress!), txHash };
|
|
1847
|
+
return { address: EthAddress.fromString(resultingAddress!), txHash, deployedLibraries, existed };
|
|
925
1848
|
}
|
|
926
1849
|
|
|
927
1850
|
export function getExpectedAddress(
|
|
@@ -944,5 +1867,3 @@ export function getExpectedAddress(
|
|
|
944
1867
|
calldata,
|
|
945
1868
|
};
|
|
946
1869
|
}
|
|
947
|
-
|
|
948
|
-
// docs:end:deployL1Contract
|