diff --git a/ethereum/contracts/common/L2ContractHelper.sol b/ethereum/contracts/common/L2ContractHelper.sol index c78135908..d4c61892b 100644 --- a/ethereum/contracts/common/L2ContractHelper.sol +++ b/ethereum/contracts/common/L2ContractHelper.sol @@ -10,6 +10,7 @@ interface IContractDeployer { struct ForceDeployment { bytes32 bytecodeHash; address newAddress; + bool callConstructor; uint256 value; bytes input; } diff --git a/ethereum/contracts/zksync/DIamondUpgradeInit2.sol b/ethereum/contracts/zksync/upgrade-initializers/DIamondUpgradeInit2.sol similarity index 90% rename from ethereum/contracts/zksync/DIamondUpgradeInit2.sol rename to ethereum/contracts/zksync/upgrade-initializers/DIamondUpgradeInit2.sol index c4c2ab320..b7a26536e 100644 --- a/ethereum/contracts/zksync/DIamondUpgradeInit2.sol +++ b/ethereum/contracts/zksync/upgrade-initializers/DIamondUpgradeInit2.sol @@ -2,10 +2,10 @@ pragma solidity ^0.8.0; -import "./Config.sol"; -import "./facets/Mailbox.sol"; -import "./libraries/Diamond.sol"; -import "../common/L2ContractHelper.sol"; +import "../Config.sol"; +import "../facets/Mailbox.sol"; +import "../libraries/Diamond.sol"; +import "../../common/L2ContractHelper.sol"; interface IOldContractDeployer { function forceDeployOnAddress( diff --git a/ethereum/contracts/zksync/DiamondUpgradeInit.sol b/ethereum/contracts/zksync/upgrade-initializers/DiamondUpgradeInit.sol similarity index 87% rename from ethereum/contracts/zksync/DiamondUpgradeInit.sol rename to ethereum/contracts/zksync/upgrade-initializers/DiamondUpgradeInit.sol index 7d3831819..7f33c8917 100644 --- a/ethereum/contracts/zksync/DiamondUpgradeInit.sol +++ b/ethereum/contracts/zksync/upgrade-initializers/DiamondUpgradeInit.sol @@ -2,9 +2,9 @@ pragma solidity ^0.8.0; -import "./facets/Mailbox.sol"; -import "./libraries/Diamond.sol"; -import "../common/L2ContractHelper.sol"; +import "../facets/Mailbox.sol"; +import "../libraries/Diamond.sol"; +import "../../common/L2ContractHelper.sol"; /// @author Matter Labs contract DiamondUpgradeInit is MailboxFacet { diff --git a/ethereum/contracts/zksync/upgrade-initializers/DiamondUpgradeInit3.sol b/ethereum/contracts/zksync/upgrade-initializers/DiamondUpgradeInit3.sol new file mode 100644 index 000000000..816584701 --- /dev/null +++ b/ethereum/contracts/zksync/upgrade-initializers/DiamondUpgradeInit3.sol @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../libraries/Diamond.sol"; +import "../facets/Base.sol"; + +interface IOldDiamondCut { + function proposeDiamondCut(Diamond.FacetCut[] calldata _facetCuts, address _initAddress) external; + + function cancelDiamondCutProposal() external; + + function executeDiamondCutProposal(Diamond.DiamondCutData calldata _diamondCut) external; + + function emergencyFreezeDiamond() external; + + function unfreezeDiamond() external; + + function approveEmergencyDiamondCutAsSecurityCouncilMember(bytes32 _diamondCutHash) external; + + // FIXME: token holders should have the ability to cancel the upgrade + + event DiamondCutProposal(Diamond.FacetCut[] _facetCuts, address _initAddress); + + event DiamondCutProposalCancelation(uint256 currentProposalId, bytes32 indexed proposedDiamondCutHash); + + event DiamondCutProposalExecution(Diamond.DiamondCutData _diamondCut); + + event EmergencyFreeze(); + + event Unfreeze(uint256 lastDiamondFreezeTimestamp); + + event EmergencyDiamondCutApproved( + address indexed _address, + uint256 currentProposalId, + uint256 securityCouncilEmergencyApprovals, + bytes32 indexed proposedDiamondCutHash + ); +} + +/// @author Matter Labs +contract DiamondUpgradeInit3 is Base { + function upgrade( + uint256 _priorityTxMaxErgsLimit, + IAllowList _allowList, + Verifier _verifier + ) external payable returns (bytes32) { + // Zero out the deprecated storage slots + delete s.__DEPRECATED_diamondCutStorage; + + s.priorityTxMaxErgsLimit = _priorityTxMaxErgsLimit; + s.allowList = _allowList; + s.verifier = _verifier; + + return Diamond.DIAMOND_INIT_SUCCESS_RETURN_VALUE; + } +} diff --git a/ethereum/contracts/zksync/upgrade-initializers/DiamondUpgradeInit4.sol b/ethereum/contracts/zksync/upgrade-initializers/DiamondUpgradeInit4.sol new file mode 100644 index 000000000..2cb006cba --- /dev/null +++ b/ethereum/contracts/zksync/upgrade-initializers/DiamondUpgradeInit4.sol @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../Config.sol"; +import "../facets/Mailbox.sol"; +import "../libraries/Diamond.sol"; +import "../../common/L2ContractHelper.sol"; + +interface IOldContractDeployer { + struct ForceDeployment { + bytes32 bytecodeHash; + address newAddress; + uint256 value; + bytes input; + } + + function forceDeployOnAddresses(ForceDeployment[] calldata _deployParams) external; +} + +/// @author Matter Labs +contract DiamondUpgradeInit4 is MailboxFacet { + function forceDeploy2( + bytes calldata _upgradeDeployerCalldata, + bytes calldata _upgradeSystemContractsCalldata, + bytes[] calldata _factoryDeps + ) external payable returns (bytes32) { + // 1. Update bytecode for the deployer smart contract + _requestL2Transaction( + FORCE_DEPLOYER, + DEPLOYER_SYSTEM_CONTRACT_ADDRESS, + 0, + _upgradeDeployerCalldata, + $(PRIORITY_TX_MAX_ERGS_LIMIT), + _factoryDeps + ); + + // 2. Redeploy other contracts by one transaction + _requestL2Transaction( + FORCE_DEPLOYER, + DEPLOYER_SYSTEM_CONTRACT_ADDRESS, + 0, + _upgradeSystemContractsCalldata, + $(PRIORITY_TX_MAX_ERGS_LIMIT), + _factoryDeps + ); + + return Diamond.DIAMOND_INIT_SUCCESS_RETURN_VALUE; + } +} diff --git a/ethereum/package.json b/ethereum/package.json index eec709fae..100511eaf 100644 --- a/ethereum/package.json +++ b/ethereum/package.json @@ -58,8 +58,10 @@ "read-variable": "ts-node scripts/read-variable.ts", "initialize-bridges": "zk f ts-node scripts/initialize-bridges.ts", "initialize-allow-list": "zk f ts-node scripts/initialize-l1-allow-list.ts", - "upgrade-l2-force-deploy": "zk f ts-node scripts/upgrade-l2-force-deploy.ts", - "force-deploy-upgrade-2": "zk f ts-node scripts/force-deploy-upgrade-2.ts" + "upgrade-1": "zk f ts-node scripts/upgrades/upgrade-1.ts", + "upgrade-2": "zk f ts-node scripts/upgrades/upgrade-2.ts", + "upgrade-3": "zk f ts-node scripts/upgrades/upgrade-3.ts", + "upgrade-4": "zk f ts-node scripts/upgrades/upgrade-4.ts" }, "dependencies": { "dotenv": "^16.0.3" diff --git a/ethereum/scripts/deploy.ts b/ethereum/scripts/deploy.ts index 66261e426..d199a639c 100644 --- a/ethereum/scripts/deploy.ts +++ b/ethereum/scripts/deploy.ts @@ -21,6 +21,7 @@ async function main() { .option('--nonce ') .option('--governor-address ') .option('--create2-salt ') + .option('--diamond-upgrade-init ') .action(async (cmd) => { const deployWallet = cmd.privateKey ? new Wallet(cmd.privateKey, provider) @@ -52,10 +53,16 @@ async function main() { await deployer.deployCreate2Factory({ gasPrice, nonce }); nonce++; } + + // Deploy diamond upgrade init contract if needed + const diamondUpgradeContractVersion = cmd.diamondUpgradeInit; + if (diamondUpgradeContractVersion) { + await deployer.deployDiamondUpgradeInit(create2Salt, diamondUpgradeContractVersion, { gasPrice, nonce }); + nonce++; + } - await deployer.deployDiamondInit2(create2Salt, { gasPrice, nonce }); - await deployer.deployAllowList(create2Salt, { gasPrice, nonce: nonce + 1 }); - await deployer.deployZkSyncContract(create2Salt, gasPrice, nonce + 2); + await deployer.deployAllowList(create2Salt, { gasPrice, nonce }); + await deployer.deployZkSyncContract(create2Salt, gasPrice, nonce + 1); await deployer.deployBridgeContracts(create2Salt, gasPrice); // Do not pass nonce, since it was increment after deploying zkSync contracts }); diff --git a/ethereum/scripts/diamond-upgrade.ts b/ethereum/scripts/diamond-upgrade.ts index 72c98fb32..5866c4a23 100644 --- a/ethereum/scripts/diamond-upgrade.ts +++ b/ethereum/scripts/diamond-upgrade.ts @@ -9,6 +9,14 @@ import { FacetCut, getAllSelectors } from '../src.ts/diamondCut'; const provider = web3Provider(); const ZERO_ADDRESS = ethers.constants.AddressZero; +function getZkSyncContract() { + // Create the dummy wallet with provider to get contracts from `Deployer` + const dummyWallet = ethers.Wallet.createRandom().connect(provider); + const deployer = new Deployer({ deployWallet: dummyWallet }); + + return deployer.zkSyncContract(dummyWallet); +} + async function main() { const program = new Command(); @@ -22,20 +30,46 @@ async function main() { }); program.command('diamond-loupe-view').action(async () => { - // Create the dummy wallet with provider to get contracts from `Deployer` - const dummyWallet = ethers.Wallet.createRandom().connect(provider); - const deployer = new Deployer({ deployWallet: dummyWallet }); - - const zkSync = deployer.zkSyncContract(dummyWallet); - const facets = await zkSync.facets(); + const facets = await getZkSyncContract().facets(); print('Facets', facets); }); + program.command('legacy-prepare-upgrade-calldata ') + .option('--init-address ') + .option('--init-data ') + .action(async (facetCutsData: string, cmd) => { + const diamondCutFacet = await hardhat.ethers.getContractAt('IOldDiamondCut', ZERO_ADDRESS); + + // Encode data for the upgrade call + const facetCuts: Array = JSON.parse(facetCutsData); + + const initAddress = cmd.initAddress ? cmd.initAddress : ZERO_ADDRESS; + const initData = cmd.initData ? cmd.initData : '0x'; + + const upgradeParam = diamondCut(facetCuts, initAddress, initData); + print('DiamondCutData', upgradeParam); + + // Get transaction data of the `proposeDiamondCut` + const proposeUpgrade = await diamondCutFacet.interface.encodeFunctionData('proposeDiamondCut', [ + upgradeParam.facetCuts, + upgradeParam.initAddress + ]); + + // Get transaction data of the `executeDiamondCutProposal` + const executeUpgrade = await diamondCutFacet.interface.encodeFunctionData('executeDiamondCutProposal', [ + upgradeParam + ]); + + print('proposeUpgrade', proposeUpgrade); + print('executeUpgrade', executeUpgrade); + }); + program .command('prepare-upgrade-calldata ') .option('--init-address ') .option('--init-data ') + .option('--proposal-id ') .action(async (facetCutsData: string, cmd) => { const diamondCutFacet = await hardhat.ethers.getContractAt('DiamondCutFacet', ZERO_ADDRESS); @@ -44,24 +78,25 @@ async function main() { const initAddress = cmd.initAddress ? cmd.initAddress : ZERO_ADDRESS; const initData = cmd.initData ? cmd.initData : '0x'; + const proposalId = cmd.proposalId ? cmd.proposalId : await getZkSyncContract().getCurrentProposalId(); const upgradeParam = diamondCut(facetCuts, initAddress, initData); print('DiamondCut', upgradeParam); - // Get transaction data of the `proposeDiamondCut` - const proposeDiamondCut = await diamondCutFacet.interface.encodeFunctionData('proposeDiamondCut', [ - upgradeParam.facetCuts, - upgradeParam.initAddress + // Get transaction data of the `proposeTransparentUpgrade` + const proposeUpgrade = await diamondCutFacet.interface.encodeFunctionData('proposeTransparentUpgrade', [ + upgradeParam, + proposalId ]); // Get transaction data of the `executeDiamondCutProposal` - const executeDiamondCutProposal = await diamondCutFacet.interface.encodeFunctionData( - 'executeDiamondCutProposal', - [upgradeParam] - ); + const executeUpgrade = await diamondCutFacet.interface.encodeFunctionData('executeUpgrade', [ + upgradeParam, + ethers.constants.HashZero + ]); - print('proposeDiamondCut', proposeDiamondCut); - print('executeDiamondCutProposal', executeDiamondCutProposal); + print('proposeUpgrade', proposeUpgrade); + print('executeUpgrade', executeUpgrade); }); await program.parseAsync(process.argv); diff --git a/ethereum/scripts/upgrade-l2-force-deploy.ts b/ethereum/scripts/upgrades/upgrade-1.ts similarity index 100% rename from ethereum/scripts/upgrade-l2-force-deploy.ts rename to ethereum/scripts/upgrades/upgrade-1.ts diff --git a/ethereum/scripts/force-deploy-upgrade-2.ts b/ethereum/scripts/upgrades/upgrade-2.ts similarity index 100% rename from ethereum/scripts/force-deploy-upgrade-2.ts rename to ethereum/scripts/upgrades/upgrade-2.ts diff --git a/ethereum/scripts/upgrades/upgrade-3.ts b/ethereum/scripts/upgrades/upgrade-3.ts new file mode 100644 index 000000000..51c53738c --- /dev/null +++ b/ethereum/scripts/upgrades/upgrade-3.ts @@ -0,0 +1,135 @@ +import { Command } from 'commander'; +import { diamondCut } from '../../src.ts/diamondCut'; +import { Wallet } from 'ethers'; +import { ethers } from 'hardhat'; +import { Provider } from 'zksync-web3'; +import '@nomiclabs/hardhat-ethers'; +import { web3Provider } from '../utils'; +import { Deployer } from '../../src.ts/deploy'; +import * as fs from 'fs'; +import * as path from 'path'; +import { IOldDiamondCutFactory } from '../../typechain/IOldDiamondCutFactory'; + +function sleep(millis: number) { + return new Promise((resolve) => setTimeout(resolve, millis)); +} + +async function prepareCalldata( + diamondUpgradeAddress: string, + allowlist: string, + verifier: string, + prioirityTxMaxErgsLimit: string, +) { + const DiamondUpgradeInit3 = await ethers.getContractAt('DiamondUpgradeInit3', ZERO_ADDRESS); + + // Prepare the diamond cut data + const upgradeInitData = await DiamondUpgradeInit3.interface.encodeFunctionData('upgrade', [ + allowlist, + verifier, + prioirityTxMaxErgsLimit, + ]); + + return diamondCut([], diamondUpgradeAddress, upgradeInitData); +} + +const provider = web3Provider(); +const testConfigPath = path.join(process.env.ZKSYNC_HOME as string, `etc/test_config/constant`); +const ethTestConfig = JSON.parse(fs.readFileSync(`${testConfigPath}/eth.json`, { encoding: 'utf-8' })); + +const ZERO_ADDRESS = ethers.constants.AddressZero; + +async function main() { + const program = new Command(); + + program.version('0.1.0').name('force-deploy-upgrade-2'); + + program + .command('prepare-calldata') + .requiredOption('--diamond-upgrade-address ') + .requiredOption('--allowlist-address ') + .requiredOption('--verifier-address ') + .requiredOption('--prioirity-tx-max-ergs-limit ') + .action(async (cmd) => { + // Get address of the diamond init contract + const diamondUpgradeAddress = cmd.diamondUpgradeAddress; + // Get address of the allowlist contract + const allowlist = cmd.allowlistAddress; + // Get address of the verifier contract + const verifier = cmd.verifierAddress; + // Get the prioirity tx max ergs limit + const prioirityTxMaxErgsLimit = cmd.prioirityTxMaxErgsLimit; + // Get diamond cut data + const calldata = await prepareCalldata(diamondUpgradeAddress, allowlist, verifier, prioirityTxMaxErgsLimit); + console.log(calldata); + }); + + program + .command('force-upgrade') + .option('--private-key ') + .option('--proposal-id ') + .requiredOption('--diamond-upgrade-address ') + .requiredOption('--allowlist-address ') + .requiredOption('--verifier-address ') + .requiredOption('--prioirity-tx-max-ergs-limit ') + .action(async (cmd) => { + const zksProvider = new Provider(process.env.API_WEB3_JSON_RPC_HTTP_URL); + const deployWallet = cmd.privateKey + ? new Wallet(cmd.privateKey, provider) + : Wallet.fromMnemonic( + process.env.MNEMONIC ? process.env.MNEMONIC : ethTestConfig.mnemonic, + "m/44'/60'/0'/0/1" + ).connect(provider); + + const deployer = new Deployer({ + deployWallet, + governorAddress: ZERO_ADDRESS, + verbose: true + }); + + const zkSyncContract = IOldDiamondCutFactory.connect(deployer.addresses.ZkSync.DiamondProxy, deployWallet); + + // Get address of the diamond init contract + const diamondUpgradeAddress = cmd.diamondUpgradeAddress; + // Get address of the allowlist contract + const allowlist = cmd.allowlistAddress; + // Get address of the verifier contract + const verifier = cmd.verifierAddress; + // Get the prioirity tx max ergs limit + const prioirityTxMaxErgsLimit = cmd.prioirityTxMaxErgsLimit; + // Get diamond cut data + const upgradeParam = await prepareCalldata(diamondUpgradeAddress, allowlist, verifier, prioirityTxMaxErgsLimit); + + const proposeUpgradeTx = await zkSyncContract.proposeDiamondCut( + upgradeParam.facetCuts, + upgradeParam.initAddress + ); + await proposeUpgradeTx.wait(); + + const executeUpgradeTx = await zkSyncContract.executeDiamondCutProposal(upgradeParam); + const executeUpgradeRec = await executeUpgradeTx.wait(); + const deployL2TxHashes = executeUpgradeRec.events + .filter((event) => event.event === 'NewPriorityRequest') + .map((event) => event.args[1]); + for (const txHash of deployL2TxHashes) { + console.log(txHash); + let receipt = null; + while (receipt == null) { + receipt = await zksProvider.getTransactionReceipt(txHash); + await sleep(100); + } + + if (receipt.status != 1) { + throw new Error('Failed to process L2 tx'); + } + } + }); + + await program.parseAsync(process.argv); +} + +main() + .then(() => process.exit(0)) + .catch((err) => { + console.error('Error:', err); + process.exit(1); + }); diff --git a/ethereum/scripts/upgrades/upgrade-4.ts b/ethereum/scripts/upgrades/upgrade-4.ts new file mode 100644 index 000000000..9cbfc38ec --- /dev/null +++ b/ethereum/scripts/upgrades/upgrade-4.ts @@ -0,0 +1,160 @@ +import { Command } from 'commander'; +import { diamondCut } from '../../src.ts/diamondCut'; +import { BigNumberish, Wallet } from 'ethers'; +import { ethers } from 'hardhat'; +import { Provider } from 'zksync-web3'; +import '@nomiclabs/hardhat-ethers'; +import { web3Provider } from '../utils'; +import { Deployer } from '../../src.ts/deploy'; +import * as fs from 'fs'; +import * as path from 'path'; + +type ForceDeploymentOld = { + bytecodeHash: string; + newAddress: string; + value: BigNumberish; + input: string; +}; + +type ForceDeployment = { + bytecodeHash: string; + newAddress: string; + callConstructor: boolean; + value: BigNumberish; + input: string; +}; + +function sleep(millis: number) { + return new Promise((resolve) => setTimeout(resolve, millis)); +} + +async function prepareCalldata( + diamondUpgradeAddress: string, + deployerBytecodeHash: string, + params: Array +) { + const DiamondUpgradeInit4 = await ethers.getContractAt('DiamondUpgradeInit4', ZERO_ADDRESS); + const oldDeployerSystemContract = await ethers.getContractAt('cache/solpp-generated-contracts/zksync/upgrade-initializers/DiamondUpgradeInit4.sol:IOldContractDeployer', ZERO_ADDRESS); + const newDeployerSystemContract = await ethers.getContractAt('IContractDeployer', ZERO_ADDRESS); + + const upgradeDeployerParams: ForceDeploymentOld = { + bytecodeHash: deployerBytecodeHash, + newAddress: DEPLOYER_SYSTEM_CONTRACT_ADDRESS, + value: 0, + input: '0x' + }; + const upgradeDeployerCalldata = await oldDeployerSystemContract.interface.encodeFunctionData( + 'forceDeployOnAddresses', + [[upgradeDeployerParams]] + ); + const upgradeSystemContractsCalldata = await newDeployerSystemContract.interface.encodeFunctionData( + 'forceDeployOnAddresses', + [params] + ); + + // Prepare the diamond cut data + const upgradeInitData = await DiamondUpgradeInit4.interface.encodeFunctionData('forceDeploy2', [ + upgradeDeployerCalldata, + upgradeSystemContractsCalldata, + [] // Empty factory deps + ]); + + return diamondCut([], diamondUpgradeAddress, upgradeInitData); +} + +const provider = web3Provider(); +const testConfigPath = path.join(process.env.ZKSYNC_HOME as string, `etc/test_config/constant`); +const ethTestConfig = JSON.parse(fs.readFileSync(`${testConfigPath}/eth.json`, { encoding: 'utf-8' })); + +const ZERO_ADDRESS = ethers.constants.AddressZero; +const DEPLOYER_SYSTEM_CONTRACT_ADDRESS = '0x0000000000000000000000000000000000008006'; + +async function main() { + const program = new Command(); + + program.version('0.1.0').name('force-deploy-upgrade-2'); + + program + .command('prepare-calldata') + .requiredOption('--diamond-upgrade-address ') + .requiredOption('--new-deployer-bytecodehash ') + .requiredOption('--deployment-params ') + .action(async (cmd) => { + // Get address of the diamond init contract + const diamondUpgradeAddress = cmd.diamondUpgradeAddress; + // Get new deployer bytecodehash + const deployerBytecodeHash = cmd.newDeployerBytecodehash; + // Encode data for the upgrade call + const params: Array = JSON.parse(cmd.deploymentParams); + // Get diamond cut data + const calldata = await prepareCalldata(diamondUpgradeAddress, deployerBytecodeHash, params); + console.log(calldata); + }); + + program + .command('force-upgrade') + .option('--private-key ') + .option('--proposal-id ') + .requiredOption('--diamond-upgrade-address ') + .requiredOption('--new-deployer-bytecodehash ') + .requiredOption('--deployment-params ') + .action(async (cmd) => { + const zksProvider = new Provider(process.env.API_WEB3_JSON_RPC_HTTP_URL); + const deployWallet = cmd.privateKey + ? new Wallet(cmd.privateKey, provider) + : Wallet.fromMnemonic( + process.env.MNEMONIC ? process.env.MNEMONIC : ethTestConfig.mnemonic, + "m/44'/60'/0'/0/1" + ).connect(provider); + + const deployer = new Deployer({ + deployWallet, + governorAddress: ZERO_ADDRESS, + verbose: true + }); + const zkSyncContract = deployer.zkSyncContract(deployWallet); + + // Get address of the diamond init contract + const diamondUpgradeAddress = cmd.diamondUpgradeAddress; + // Get new deployer bytecodehash + const deployerBytecodeHash = cmd.newDeployerBytecodehash; + // Encode data for the upgrade call + const params: Array = JSON.parse(cmd.deploymentParams); + // Get diamond cut data + const upgradeParam = await prepareCalldata(diamondUpgradeAddress, deployerBytecodeHash, params); + + const proposalId = cmd.proposalId ? cmd.proposalId : await zkSyncContract.getCurrentProposalId(); + const proposeUpgradeTx = await zkSyncContract.proposeTransparentUpgrade( + upgradeParam, + proposalId + ); + await proposeUpgradeTx.wait(); + + const executeUpgradeTx = await zkSyncContract.executeUpgrade(upgradeParam, ethers.constants.HashZero); + const executeUpgradeRec = await executeUpgradeTx.wait(); + const deployL2TxHashes = executeUpgradeRec.events + .filter((event) => event.event === 'NewPriorityRequest') + .map((event) => event.args[1]); + for (const txHash of deployL2TxHashes) { + console.log(txHash); + let receipt = null; + while (receipt == null) { + receipt = await zksProvider.getTransactionReceipt(txHash); + await sleep(100); + } + + if (receipt.status != 1) { + throw new Error('Failed to process L2 tx'); + } + } + }); + + await program.parseAsync(process.argv); +} + +main() + .then(() => process.exit(0)) + .catch((err) => { + console.error('Error:', err); + process.exit(1); + }); diff --git a/ethereum/src.ts/deploy.ts b/ethereum/src.ts/deploy.ts index bdb885159..680a260f1 100644 --- a/ethereum/src.ts/deploy.ts +++ b/ethereum/src.ts/deploy.ts @@ -213,15 +213,6 @@ export class Deployer { return expectedAddress; } - public async deployDiamondInit2(create2Salt: string, ethTxOptions: ethers.providers.TransactionRequest) { - ethTxOptions.gasLimit ??= 10_000_000; - const contractAddress = await this.deployViaCreate2('DiamondUpgradeInit2', [], create2Salt, ethTxOptions); - - if (this.verbose) { - console.log(`CONTRACTS_DIAMOND_UPGRADE_INIT2=${contractAddress}`); - } - } - public async deployAllowList(create2Salt: string, ethTxOptions: ethers.providers.TransactionRequest) { ethTxOptions.gasLimit ??= 10_000_000; const contractAddress = await this.deployViaCreate2( @@ -382,9 +373,9 @@ export class Deployer { this.addresses.ZkSync.DiamondInit = contractAddress; } - public async deployDiamondUpgradeInit(create2Salt: string, ethTxOptions: ethers.providers.TransactionRequest) { + public async deployDiamondUpgradeInit(create2Salt: string, contractVersion: number, ethTxOptions: ethers.providers.TransactionRequest) { ethTxOptions.gasLimit ??= 10_000_000; - const contractAddress = await this.deployViaCreate2('DiamondUpgradeInit', [], create2Salt, ethTxOptions); + const contractAddress = await this.deployViaCreate2(`DiamondUpgradeInit${contractVersion}`, [], create2Salt, ethTxOptions); if (this.verbose) { console.log(`CONTRACTS_DIAMOND_UPGRADE_INIT_ADDR=${contractAddress}`); @@ -424,7 +415,6 @@ export class Deployer { this.deployGettersFacet(create2Salt, { gasPrice, nonce: nonce + 4 }), this.deployVerifier(create2Salt, { gasPrice, nonce: nonce + 5 }), this.deployDiamondInit(create2Salt, { gasPrice, nonce: nonce + 6 }), - this.deployDiamondUpgradeInit(create2Salt, { gasPrice, nonce: nonce + 7 }) ]; await Promise.all(independentZkSyncDeployPromises); nonce += 8; diff --git a/zksync/package.json b/zksync/package.json index 17ee592f4..8c6d6dd79 100644 --- a/zksync/package.json +++ b/zksync/package.json @@ -28,7 +28,8 @@ "build": "hardhat compile", "clean": "hardhat clean", "compile-and-deploy-libs": "zk f hardhat run src/compileAndDeployLibs.ts", - "deploy-testnet-paymaster": "zk f hardhat run src/deployTestnetPaymaster.ts" + "deploy-testnet-paymaster": "zk f hardhat run src/deployTestnetPaymaster.ts", + "publish-bridge-preimages": "zk f hardhat run src/publish-bridge-preimages.ts" }, "dependencies": { "@matterlabs/hardhat-zksync-solc": "^0.3.9", diff --git a/zksync/src/publish-bridge-preimages.ts b/zksync/src/publish-bridge-preimages.ts new file mode 100644 index 000000000..1161f2a5b --- /dev/null +++ b/zksync/src/publish-bridge-preimages.ts @@ -0,0 +1,60 @@ +import { Command } from 'commander'; +import { Wallet, ethers } from 'ethers'; +import * as fs from 'fs'; +import { Deployer } from '../../ethereum/src.ts/deploy'; +import * as path from 'path'; +import { getNumberFromEnv, web3Provider } from '../../ethereum/scripts/utils'; +import * as hre from 'hardhat'; + +const PRIORITY_TX_MAX_ERGS_LIMIT = getNumberFromEnv('CONTRACTS_PRIORITY_TX_MAX_ERGS_LIMIT'); +const provider = web3Provider(); +const testConfigPath = path.join(process.env.ZKSYNC_HOME as string, `etc/test_config/constant`); +const ethTestConfig = JSON.parse(fs.readFileSync(`${testConfigPath}/eth.json`, { encoding: 'utf-8' })); + +function getContractBytecode(contractName: string) { + return hre.artifacts.readArtifactSync(contractName).bytecode; +} + +async function main() { + const program = new Command(); + + program.version('0.1.0').name('publish-bridge-preimages'); + + program + .option('--private-key ') + .option('--nonce ') + .option('--gas-price ') + .action(async (cmd) => { + const wallet = cmd.privateKey + ? new Wallet(cmd.privateKey, provider) + : Wallet.fromMnemonic( + process.env.MNEMONIC ? process.env.MNEMONIC : ethTestConfig.mnemonic, + "m/44'/60'/0'/0/1" + ).connect(provider); + console.log(`Using wallet: ${wallet.address}`); + + const nonce = cmd.nonce ? parseInt(cmd.nonce) : await wallet.getTransactionCount(); + console.log(`Using nonce: ${nonce}`); + + const gasPrice = cmd.gasPrice ? parseInt(cmd.gasPrice) : await wallet.getGasPrice(); + console.log(`Using gas price: ${gasPrice}`); + + const deployer = new Deployer({ deployWallet: wallet }); + const zkSync = deployer.zkSyncContract(wallet); + + const publishL2EthBridgeTx = await zkSync.requestL2Transaction(ethers.constants.AddressZero, 0, "0x", PRIORITY_TX_MAX_ERGS_LIMIT, [getContractBytecode("L2ETHBridge")], {nonce, gasPrice}); + await publishL2EthBridgeTx.wait(); + + const publishL2ERC20BridgeTx = await zkSync.requestL2Transaction(ethers.constants.AddressZero, 0, "0x", PRIORITY_TX_MAX_ERGS_LIMIT, [getContractBytecode("L2ERC20Bridge")], {nonce: nonce+1, gasPrice}); + await publishL2ERC20BridgeTx.wait(); + }); + + await program.parseAsync(process.argv); +} + +main() + .then(() => process.exit(0)) + .catch((err) => { + console.error('Error:', err); + process.exit(1); + });