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