From accecc4526c95fccbf190d7c2f97f174d1c5fce0 Mon Sep 17 00:00:00 2001 From: kelemeno <34402761+kelemeno@users.noreply.github.com> Date: Wed, 8 May 2024 15:49:35 +0100 Subject: [PATCH] fix: hyperchain upgrade fix2 (don't merge) (#401) Co-authored-by: Stanislav Bezkorovainyi --- .github/workflows/slither.yaml | 4 +- .../scripts/governance-accept-ownership.ts | 23 +- l1-contracts/scripts/hyperchain-upgrade-1.ts | 6 +- l1-contracts/scripts/hyperchain-upgrade-2.ts | 3 +- l1-contracts/scripts/hyperchain-upgrade-3.ts | 5 +- l1-contracts/scripts/token-migration.ts | 4 +- .../scripts/upgrade-consistency-checker.ts | 122 ++--- l1-contracts/scripts/verify.ts | 146 +++++- l1-contracts/src.ts/deploy-utils.ts | 4 +- l1-contracts/src.ts/deploy.ts | 36 +- l1-contracts/src.ts/hyperchain-upgrade.ts | 457 +++++++----------- .../test/test_config/constant/hardhat.json | 32 +- l2-contracts/hardhat.config.ts | 2 +- .../src/upgrade-consistency-checker.ts | 4 +- l2-contracts/src/verify.ts | 3 +- system-contracts/hardhat.config.ts | 8 + system-contracts/scripts/deploy-preimages.ts | 2 +- 17 files changed, 438 insertions(+), 423 deletions(-) diff --git a/.github/workflows/slither.yaml b/.github/workflows/slither.yaml index ea278b2a3..fa253e159 100644 --- a/.github/workflows/slither.yaml +++ b/.github/workflows/slither.yaml @@ -44,5 +44,5 @@ jobs: - name: Run Slither run: | - cd l1-contracts - slither --config ./slither.config.json . + cd l1-contracts + slither --config ./slither.config.json . diff --git a/l1-contracts/scripts/governance-accept-ownership.ts b/l1-contracts/scripts/governance-accept-ownership.ts index e111f0ba3..1c366f684 100644 --- a/l1-contracts/scripts/governance-accept-ownership.ts +++ b/l1-contracts/scripts/governance-accept-ownership.ts @@ -63,11 +63,11 @@ async function main() { const proxyAdminAddr = ethers.utils.getAddress(cmd.proxyAdminAddr); console.log("Using Proxy Admin address: ", proxyAdminAddr); - await transferOwnership1StepTo(deployWallet, validatorTimelockAddr, ownerAddress); - await transferOwnership1StepTo(deployWallet, stmAddr, ownerAddress); - await transferOwnership1StepTo(deployWallet, l1SharedBridgeAddr, ownerAddress); - await transferOwnership1StepTo(deployWallet, bridgehubAddr, ownerAddress); - await transferOwnership1StepTo(deployWallet, proxyAdminAddr, ownerAddress); + await transferOwnership1StepTo(deployWallet, validatorTimelockAddr, ownerAddress, true); + await transferOwnership1StepTo(deployWallet, stmAddr, ownerAddress, true); + await transferOwnership1StepTo(deployWallet, l1SharedBridgeAddr, ownerAddress, true); + await transferOwnership1StepTo(deployWallet, bridgehubAddr, ownerAddress, true); + await transferOwnership1StepTo(deployWallet, proxyAdminAddr, ownerAddress, false); }); program @@ -114,14 +114,21 @@ main() process.exit(1); }); -async function transferOwnership1StepTo(wallet: ethers.Wallet, contractAddress: string, newOwner: string) { +async function transferOwnership1StepTo( + wallet: ethers.Wallet, + contractAddress: string, + newOwner: string, + printPendingOwner: boolean = true +) { const contract = new ethers.Contract(contractAddress, ownable2StepInterface, wallet); console.log("Transferring ownership of contract: ", contractAddress, " to: ", newOwner); const tx = await contract.transferOwnership(newOwner); console.log("Tx hash", tx.hash); await tx.wait(); - const newPendingOwner = await contract.pendingOwner(); - console.log("New pending owner: ", newPendingOwner); + if (printPendingOwner) { + const newPendingOwner = await contract.pendingOwner(); + console.log("New pending owner: ", newPendingOwner); + } } function acceptOwnershipCall(target: string) { diff --git a/l1-contracts/scripts/hyperchain-upgrade-1.ts b/l1-contracts/scripts/hyperchain-upgrade-1.ts index e99d84a8c..90a398ee3 100644 --- a/l1-contracts/scripts/hyperchain-upgrade-1.ts +++ b/l1-contracts/scripts/hyperchain-upgrade-1.ts @@ -2,7 +2,7 @@ // eslint-disable-next-line @typescript-eslint/no-unused-vars import * as hardhat from "hardhat"; import { Command } from "commander"; -import { Wallet, ethers } from "ethers"; +import { Wallet } from "ethers"; import { Deployer } from "../src.ts/deploy"; import { formatUnits, parseUnits } from "ethers/lib/utils"; import { web3Provider, GAS_MULTIPLIER } from "./utils"; @@ -46,7 +46,9 @@ async function main() { const nonce = cmd.nonce ? parseInt(cmd.nonce) : await deployWallet.getTransactionCount(); console.log(`Using nonce: ${nonce}`); - const create2Salt = cmd.create2Salt ? cmd.create2Salt : ethers.utils.hexlify(ethers.utils.randomBytes(32)); + const create2Salt = cmd.create2Salt + ? cmd.create2Salt + : "0x0000000000000000000000000000000000000000000000000000000000000000"; const deployer = new Deployer({ deployWallet, diff --git a/l1-contracts/scripts/hyperchain-upgrade-2.ts b/l1-contracts/scripts/hyperchain-upgrade-2.ts index edef79bf0..ae8fd93ad 100644 --- a/l1-contracts/scripts/hyperchain-upgrade-2.ts +++ b/l1-contracts/scripts/hyperchain-upgrade-2.ts @@ -24,6 +24,7 @@ async function main() { .option("--nonce ") .option("--owner-address ") .option("--create2-salt ") + .option("--print-file-path ") .option("--diamond-upgrade-init ") .option("--only-verifier") .action(async (cmd) => { @@ -53,7 +54,7 @@ async function main() { verbose: true, }); - await upgradeToHyperchains2(deployer, gasPrice); + await upgradeToHyperchains2(deployer, gasPrice, cmd.printFilePath); }); await program.parseAsync(process.argv); diff --git a/l1-contracts/scripts/hyperchain-upgrade-3.ts b/l1-contracts/scripts/hyperchain-upgrade-3.ts index d232a80d8..e66065d94 100644 --- a/l1-contracts/scripts/hyperchain-upgrade-3.ts +++ b/l1-contracts/scripts/hyperchain-upgrade-3.ts @@ -24,6 +24,7 @@ async function main() { .option("--nonce ") .option("--owner-address ") .option("--create2-salt ") + .option("--print-file-path ") .option("--diamond-upgrade-init ") .option("--only-verifier") .action(async (cmd) => { @@ -33,7 +34,7 @@ async function main() { process.env.MNEMONIC ? process.env.MNEMONIC : ethTestConfig.mnemonic, "m/44'/60'/0'/0/1" ).connect(provider); - console.log(`Using deployer wallet: ${deployWallet.address}, ${deployWallet.privateKey}`); + console.log(`Using deployer wallet: ${deployWallet.address}`); const ownerAddress = cmd.ownerAddress ? cmd.ownerAddress : deployWallet.address; console.log(`Using owner address: ${ownerAddress}`); @@ -53,7 +54,7 @@ async function main() { verbose: true, }); - await upgradeToHyperchains3(deployer); + await upgradeToHyperchains3(deployer, cmd.printFilePath); }); await program.parseAsync(process.argv); diff --git a/l1-contracts/scripts/token-migration.ts b/l1-contracts/scripts/token-migration.ts index 09413b529..d9ec8757a 100644 --- a/l1-contracts/scripts/token-migration.ts +++ b/l1-contracts/scripts/token-migration.ts @@ -86,7 +86,7 @@ async function main() { .option("--tokens-per-signature ") .option("--shared-bridge-addr ") .option("--legacy-bridge-addr ") - .option("--era-chain-address ") + .option("--era-chain-addr ") .option("--era-chain-id ") .option("--delay ") @@ -106,7 +106,7 @@ async function main() { tokens, cmd.sharedBridgeAddr, cmd.legacyBridgeAddr, - cmd.eraChainAddress, + cmd.eraChainAddr, cmd.eraChainId, +cmd.gasPerToken, +cmd.delay diff --git a/l1-contracts/scripts/upgrade-consistency-checker.ts b/l1-contracts/scripts/upgrade-consistency-checker.ts index ae2d994d1..3972c8695 100644 --- a/l1-contracts/scripts/upgrade-consistency-checker.ts +++ b/l1-contracts/scripts/upgrade-consistency-checker.ts @@ -16,61 +16,60 @@ import { getCurrentFacetCutsForAdd } from "../src.ts/diamondCut"; // 2. Getter methods in STM. // List the contracts that should become the upgrade targets -const genesisUpgrade = "0xDdc72e56A3b90793271FF0EA9a762294f163F992"; -const validatorTimelockDeployTx = "0x1ada4121db6e83bfe38f1f92e31c0931e2f0f2b830429841a7d264c56cceb8b0"; -const validatorTimelock = "0xc47CBbc601dbB65439e7b02B0d19bbA9Dba57442"; -const upgradeHyperchains = "0xc029cE1EB5C61C4a3B2a6EE920bb3B7b026bc00b"; +const genesisUpgrade = "0xc6aB8b3b93f3E47fb4163eB9Dc7A61E1a5D86369"; +const validatorTimelockDeployTx = "0x420e0dddae4a1565fee430ecafa8f5ddbc3eebee2666d0c91f97a47bf054eeb4"; +const validatorTimelock = "0xc2d7a7Bd59a548249e64C1a587220c0E4F6F439E"; +const upgradeHyperchains = "0xb2963DDc6694a989B527AED0B1E19f9F0675AE4d"; -const verifier = "0x82856fED36d36e1d4db24398bC2056C440cB45FC"; -const proxyAdmin = "0xCb7F8e556Ef02771eA32F54e767D6F9742ED31c2"; +const verifier = "0x9D6c59D9A234F585B367b4ba3C62e5Ec7A6179FD"; +const proxyAdmin = "0xf2c1d17441074FFb18E9A918db81A17dB1752146"; -const bridgeHubImpl = "0x22c456Cb8E657bD48e14E9a54CE20169d78CB0F7"; -const bridgeHub = "0x236D1c3Ff32Bd0Ca26b72Af287E895627c0478cE"; +const bridgeHubImpl = "0xF9D2E98Ed518eC6Daac0579a9707d83da55D5f89"; +const bridgeHub = "0x5B5c82f4Da996e118B127880492a23391376F65c"; -const executorFacet = "0xd56f4696ecbE9ADc2e1539F5311ae6C92F4B2BAd"; -const adminFacet = "0x21924127192db478faDf6Ae07f57df928EBCA6AE"; -const mailboxFacetDeployTx = "0xad8028a8a1c7fe71e40fb6e32b80f5893b6b26af5475d9a014b9510faf460090"; -const mailboxFacet = "0x445aD49fC6d1845ec774783659aA5351381b0c49"; -const gettersFacet = "0xbF4C2dfBe9E722F0A87E104c3af5780d49872745"; +const executorFacet = "0x1a451d9bFBd176321966e9bc540596Ca9d39B4B1"; +const adminFacet = "0x342a09385E9BAD4AD32a6220765A6c333552e565"; +const mailboxFacetDeployTx = "0x2fa6af6e9317089be2734ffae73771c8099382d390d4edbb6c35e2db7f73b152"; +const mailboxFacet = "0x7814399116C17F2750Ca99cBFD2b75bA9a0793d7"; +const gettersFacet = "0x345c6ca2F3E08445614f4299001418F125AD330a"; -const diamondInit = "0x17384Fd6Cc64468b69df514A940caC89B602d01c"; +const diamondInit = "0x05D865AE297d236Bc5C7988328d02A00b3D38a4F"; -const stmImplDeployTx = "0x6dacf003368a922b9f916393f3c11c869c1f614c16345667cabd1d8b890ec0cb"; -const stmImpl = "0x91E088D2F36500c4826E5623c9C14Dd90912c23E"; -const stmDeployTx = "0x11ceebf3d0b95a4a49f798c937fd3e0085dc01a4e5d497b60b5072b13e58235a"; -const stm = "0x6F03861D12E6401623854E494beACd66BC46e6F0"; +const stmImplDeployTx = "0x7a077accd4ee39d14b6c23ef31ece4a84c87aff41cd64fd4d2ac23a3885dd4f8"; +const stmImpl = "0x3060D61538fC91B6580e34C5b5D09651CBB9c609"; +const stmDeployTx = "0x30138b826e8f8f855e7fe9e6153d49376b53bce71c34cb2a78e186b12156c966"; +const stm = "0x280372beAAf440C52a2ed893daa14CDACc0422b8"; -const sharedBridgeImplDeployTx = "0x6dacf003368a922b9f916393f3c11c869c1f614c16345667cabd1d8b890ec0cb"; -const sharedBridgeImpl = "0x91E088D2F36500c4826E5623c9C14Dd90912c23E"; -const sharedBridgeProxy = "0x6F03861D12E6401623854E494beACd66BC46e6F0"; +const sharedBridgeImplDeployTx = "0xd24d38cab0beb62f6de9a83cd0a5d7e339e985ba84ac6ef07a336efd79ae333a"; +const sharedBridgeImpl = "0x3819200C978d8A589a1e28A2e8fEb9a0CAD700F7"; +const sharedBridgeProxy = "0x241F19eA8CcD04515b309f1C9953A322F51891FC"; -const legacyBridgeImplDeployTx = "0xc0640213aa843f812c44d63723b5dc03064d8e5a32d85e94689e3273df6c3ef5"; -const legacyBridgeImpl = "0x8fE595B3f92AA34962d7A8aF106Fa50A3e4FC6fA"; +const legacyBridgeImplDeployTx = "0xd8cca5843318ca176afd1075ca6fbb941837a641324300d3719f9189e49fd62c"; +const legacyBridgeImpl = "0xbf3d4109D65A66c629D1999fb630bE2eE16d7038"; -const expectedL1WethAddress = "0x7b79995e5f793A07Bc00c21412e50Ecae098E7f9"; -const initialOwner = "0x343Ee72DdD8CCD80cd43D6Adbc6c463a2DE433a7"; -const expectedOwner = "0x343Ee72DdD8CCD80cd43D6Adbc6c463a2DE433a7"; +const expectedL1WethAddress = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"; +const initialOwner = "0x71d84c3404a6ae258E6471d4934B96a2033F9438"; +const expectedOwner = "0x71d84c3404a6ae258E6471d4934B96a2033F9438"; const expectedDelay = 0; -const eraChainId = 270; -const expectedSalt = "0x0000000000000000000000000000000000000000000000000000000000000005"; -const expectedHyperchainAddr = "0x6d6e010A2680E2E5a3b097ce411528b36d880EF6"; +const eraChainId = 324; +const expectedSalt = "0x0000000000000000000000000000000000000000000000000000000000000000"; +const expectedHyperchainAddr = "0x32400084c286cf3e17e7b677ea9583e60a000324"; const maxNumberOfHyperchains = 100; -const expectedStoredBatchHashZero = "0x53dc316f108d1b64412be840e0ab89193e94ba6c4af8b9ca57d39ad4d782e0f4"; -const expectedL2BridgeAddress = "0xCEB8d4888d2025aEaAD0272175281e0CaFC33152"; -const expectedL1LegacyBridge = "0x7303B5Ce64f1ADB0558572611a0b90620b6dd5F4"; -const expectedGenesisBatchCommitment = "0x49276362411c40c07ab01d3dfa9428abca95e361d8c980cd39f1ab6a9c561c0c"; +const expectedStoredBatchHashZero = "0x1574fa776dec8da2071e5f20d71840bfcbd82c2bca9ad68680edfedde1710bc4"; +const expectedL2BridgeAddress = "0x11f943b2c77b743AB90f4A0Ae7d5A4e7FCA3E102"; +const expectedL1LegacyBridge = "0x57891966931Eb4Bb6FB81430E6cE0A03AAbDe063"; +const expectedGenesisBatchCommitment = "0x2d00e5f8d77afcebf58a6b82ae56ba967566fe7dfbcb6760319fb0d215d18ffd"; const expectedIndexRepeatedStorageChanges = BigNumber.from(54); -const expectedProtocolVersion = 23; +const expectedProtocolVersion = 24; const expectedGenesisRoot = "0xabdb766b18a479a5c783a4b80e12686bc8ea3cc2d8a3050491b701d72370ebb5"; const expectedRecursionNodeLevelVkHash = "0xf520cd5b37e74e19fdb369c8d676a04dce8a19457497ac6686d2bb95d94109c8"; const expectedRecursionLeafLevelVkHash = "0x435202d277dd06ef3c64ddd99fda043fc27c2bd8b7c66882966840202c27f4f6"; const expectedRecursionCircuitsSetVksHash = "0x0000000000000000000000000000000000000000000000000000000000000000"; -const expectedBootloaderHash = "0x010008e7f0f15ed191392960117f88fe371348982b28a033c7207ed2c09bc0f4"; +const expectedBootloaderHash = "0x010008e742608b21bf7eb23c1a9d0602047e3618b464c9b59c0fba3b3d7ab66e"; const expectedDefaultAccountHash = "0x01000563374c277a2c1e34659a2a1e87371bb6d852ce142022d497bfb50b9e32"; -const expectedGovernance = "0xEE73438083629026FAfA1f5F5bBE2bBD6Bad6331"; -const validatorOne = "0x0000000000000000000000000000000000000000"; // to do find -const validatorTwo = "0x0000000000000000000000000000000000000000"; // to do find +const validatorOne = "0x0D3250c3D5FAcb74Ac15834096397a3Ef790ec99"; +const validatorTwo = "0x3527439923a63F8C13CF72b8Fe80a77f6e572092"; const l1Provider = new ethers.providers.JsonRpcProvider(web3Url()); @@ -213,12 +212,10 @@ async function extractProxyInitializationData(contract: ethers.Contract, data: s const [ usedVerifier, - // We just unpack verifier params here recursionNodeLevelVkHash, recursionLeafLevelVkHash, recursionCircuitsSetVksHash, - l2BootloaderBytecodeHash, l2DefaultAccountBytecodeHash, // priorityTxMaxGasLimit, @@ -362,46 +359,6 @@ async function checkMailbox() { console.log("Mailbox is correct!"); } -async function checkUpgradeHyperchainParams() { - const artifact = await hardhat.artifacts.readArtifact("GettersFacet"); - const contract = new ethers.Contract(expectedHyperchainAddr, artifact.abi, l1Provider); - - // Note: there is no getters for chainId - const setBridgehub = await contract.getBridgehub(); - if (setBridgehub != bridgeHub) { - throw new Error("Bridgehub is not set in Era correctly"); - } - const setStateTransitionManager = await contract.getStateTransitionManager(); - if (setStateTransitionManager != stm) { - throw new Error("Bridgehub is not set in Era correctly"); - } - const setBaseTokenBridge = await contract.getBaseTokenBridge(); - if (setBaseTokenBridge != sharedBridgeProxy) { - throw new Error("Bridgehub is not set in Era correctly"); - } - const setBaseToken = await contract.getBaseToken(); - if (setBaseToken != utils.ETH_ADDRESS_IN_CONTRACTS) { - throw new Error("Bridgehub is not set in Era correctly"); - } - const baseTokenGasPriceMultiplierNominator = await contract.baseTokenGasPriceMultiplierNominator(); - if (baseTokenGasPriceMultiplierNominator != 1) { - throw new Error("baseTokenGasPriceMultiplierNominator is not set in Era correctly"); - } - const baseTokenGasPriceMultiplierDenominator = await contract.baseTokenGasPriceMultiplierDenominator(); - if (baseTokenGasPriceMultiplierDenominator != 1) { - throw new Error("baseTokenGasPriceMultiplierDenominator is not set in Era correctly"); - } - const admin = await contract.getAdmin(); - if (admin != expectedGovernance) { - throw new Error("admin is not set in Era correctly"); - } - const validatorTimelockIsRegistered = await contract.isValidator(validatorTimelock); - if (!validatorTimelockIsRegistered) { - throw new Error("Bridgehub is not set in Era correctly"); - } - console.log("Validator timelock and admin is set correctly in Era!"); -} - async function checkSTMImpl() { const artifact = await hardhat.artifacts.readArtifact("StateTransitionManager"); const contract = new ethers.Contract(stmImpl, artifact.abi, l1Provider); @@ -498,7 +455,7 @@ async function checkProxyAdmin() { const currentOwner = await contract.owner(); if (currentOwner.toLowerCase() != expectedOwner.toLowerCase()) { - throw new Error("ProxyAdmin owner is not correct"); + throw new Error(`ProxyAdmin owner is not correct ${currentOwner}, ${expectedOwner}`); } console.log("ProxyAdmin is correct!"); @@ -519,11 +476,10 @@ async function main() { await checkIdenticalBytecode(gettersFacet, "GettersFacet"); await checkIdenticalBytecode(adminFacet, "AdminFacet"); await checkIdenticalBytecode(bridgeHubImpl, "Bridgehub"); - await checkIdenticalBytecode(verifier, "TestnetVerifier"); + await checkIdenticalBytecode(verifier, eraChainId == 324 ? "Verifier" : "TestnetVerifier"); await checkIdenticalBytecode(diamondInit, "DiamondInit"); await checkMailbox(); - await checkUpgradeHyperchainParams(); await checkProxyAdmin(); diff --git a/l1-contracts/scripts/verify.ts b/l1-contracts/scripts/verify.ts index d70c34663..36c0980b5 100644 --- a/l1-contracts/scripts/verify.ts +++ b/l1-contracts/scripts/verify.ts @@ -1,17 +1,31 @@ // hardhat import should be the first import in the file import * as hardhat from "hardhat"; import { deployedAddressesFromEnv } from "../src.ts/deploy-utils"; +import { getNumberFromEnv, getHashFromEnv, getAddressFromEnv, ethTestConfig } from "../src.ts/utils"; + +import { Interface } from "ethers/lib/utils"; +import { Deployer } from "../src.ts/deploy"; +import { Wallet } from "ethers"; +import { web3Provider } from "./utils"; +import { getTokens } from "../src.ts/deploy-token"; + +const provider = web3Provider(); // eslint-disable-next-line @typescript-eslint/no-explicit-any function verifyPromise(address: string, constructorArguments?: Array, libraries?: object): Promise { return new Promise((resolve, reject) => { hardhat - .run("verify:verify", { address, constructorArguments, libraries }) + .run("verify:verify", { + address, + constructorArguments, + libraries, + }) .then(() => resolve(`Successfully verified ${address}`)) .catch((e) => reject(`Failed to verify ${address}\nError: ${e.message}`)); }); } +// Note: running all verifications in parallel might be too much for etherscan, comment out some of them if needed async function main() { if (process.env.CHAIN_ETH_NETWORK == "localhost") { console.log("Skip contract verification on localhost"); @@ -24,25 +38,21 @@ async function main() { const addresses = deployedAddressesFromEnv(); const promises = []; - // Contracts without constructor parameters - for (const address of [ - addresses.StateTransition.GettersFacet, - addresses.StateTransition.DiamondInit, - addresses.StateTransition.AdminFacet, - addresses.StateTransition.MailboxFacet, - addresses.StateTransition.ExecutorFacet, - addresses.StateTransition.Verifier, - ]) { - const promise = verifyPromise(address); - promises.push(promise); - } + const deployWalletAddress = "0x71d84c3404a6ae258E6471d4934B96a2033F9438"; + const deployWallet = Wallet.fromMnemonic(ethTestConfig.mnemonic, "m/44'/60'/0'/0/1").connect(provider); + const deployer = new Deployer({ + deployWallet, + addresses: deployedAddressesFromEnv(), + ownerAddress: deployWalletAddress, + verbose: true, + }); // TODO: Restore after switching to hardhat tasks (SMA-1711). // promises.push(verifyPromise(addresses.AllowList, [governor])); - // // Proxy + // Proxy // { - // // Create dummy deployer to get constructor parameters for diamond proxy + // Create dummy deployer to get constructor parameters for diamond proxy // const deployer = new Deployer({ // deployWallet: ethers.Wallet.createRandom(), // governorAddress: governor @@ -54,10 +64,112 @@ async function main() { // promises.push(promise); // } - // Bridges - const promise = verifyPromise(addresses.Bridges.ERC20BridgeImplementation, [addresses.StateTransition.DiamondProxy]); + const promise1 = verifyPromise(addresses.StateTransition.GenesisUpgrade); + promises.push(promise1); + + const executionDelay = getNumberFromEnv("CONTRACTS_VALIDATOR_TIMELOCK_EXECUTION_DELAY"); + const eraChainId = getNumberFromEnv("CONTRACTS_ERA_CHAIN_ID"); + const promise2 = verifyPromise(addresses.ValidatorTimeLock, [deployWalletAddress, executionDelay, eraChainId]); + promises.push(promise2); + + console.log("CONTRACTS_HYPERCHAIN_UPGRADE_ADDR", process.env.CONTRACTS_HYPERCHAIN_UPGRADE_ADDR); + const promise3 = verifyPromise(process.env.CONTRACTS_HYPERCHAIN_UPGRADE_ADDR); + promises.push(promise3); + + const promise5 = verifyPromise(addresses.TransparentProxyAdmin); + promises.push(promise5); + + // bridgehub + + const promise6 = verifyPromise(addresses.Bridgehub.BridgehubImplementation); + promises.push(promise6); + + const bridgehub = new Interface(hardhat.artifacts.readArtifactSync("Bridgehub").abi); + const initCalldata1 = bridgehub.encodeFunctionData("initialize", [deployWalletAddress]); + const promise7 = verifyPromise(addresses.Bridgehub.BridgehubProxy, [ + addresses.Bridgehub.BridgehubImplementation, + addresses.TransparentProxyAdmin, + initCalldata1, + ]); + promises.push(promise7); + + // stm + + // Contracts without constructor parameters + for (const address of [ + addresses.StateTransition.GettersFacet, + addresses.StateTransition.DiamondInit, + addresses.StateTransition.AdminFacet, + addresses.StateTransition.ExecutorFacet, + addresses.StateTransition.Verifier, + ]) { + const promise = verifyPromise(address); + promises.push(promise); + } + + const promise = verifyPromise(addresses.StateTransition.MailboxFacet, [eraChainId]); promises.push(promise); + const promise8 = verifyPromise(addresses.StateTransition.StateTransitionImplementation, [ + addresses.Bridgehub.BridgehubProxy, + getNumberFromEnv("CONTRACTS_MAX_NUMBER_OF_HYPERCHAINS"), + ]); + promises.push(promise8); + + const stateTransitionManager = new Interface(hardhat.artifacts.readArtifactSync("StateTransitionManager").abi); + const genesisBatchHash = getHashFromEnv("CONTRACTS_GENESIS_ROOT"); // TODO: confusing name + const genesisRollupLeafIndex = getNumberFromEnv("CONTRACTS_GENESIS_ROLLUP_LEAF_INDEX"); + const genesisBatchCommitment = getHashFromEnv("CONTRACTS_GENESIS_BATCH_COMMITMENT"); + const diamondCut = await deployer.initialZkSyncHyperchainDiamondCut([]); + const protocolVersion = getNumberFromEnv("CONTRACTS_GENESIS_PROTOCOL_VERSION"); + + const initCalldata2 = stateTransitionManager.encodeFunctionData("initialize", [ + { + owner: addresses.Governance, + validatorTimelock: addresses.ValidatorTimeLock, + genesisUpgrade: addresses.StateTransition.GenesisUpgrade, + genesisBatchHash, + genesisIndexRepeatedStorageChanges: genesisRollupLeafIndex, + genesisBatchCommitment, + diamondCut, + protocolVersion, + }, + ]); + + const promise9 = verifyPromise(addresses.StateTransition.StateTransitionProxy, [ + addresses.StateTransition.StateTransitionImplementation, + addresses.TransparentProxyAdmin, + initCalldata2, + ]); + promises.push(promise9); + + // bridges + // Note: do this manually and pass in to verify:verify the following: contract:"contracts/bridge/L1ERC20Bridge.sol:L1ERC20Bridge" + const promise10 = verifyPromise(addresses.Bridges.ERC20BridgeImplementation, [addresses.Bridges.SharedBridgeProxy]); + promises.push(promise10); + + const eraDiamondProxy = getAddressFromEnv("CONTRACTS_ERA_DIAMOND_PROXY_ADDR"); + const tokens = getTokens(); + const l1WethToken = tokens.find((token: { symbol: string }) => token.symbol == "WETH")!.address; + + const promise12 = verifyPromise(addresses.Bridges.SharedBridgeImplementation, [ + l1WethToken, + addresses.Bridgehub.BridgehubProxy, + eraChainId, + eraDiamondProxy, + ]); + promises.push(promise12); + const initCalldata4 = new Interface(hardhat.artifacts.readArtifactSync("L1SharedBridge").abi).encodeFunctionData( + "initialize", + [deployWalletAddress] + ); + const promise13 = verifyPromise(addresses.Bridges.SharedBridgeProxy, [ + addresses.Bridges.SharedBridgeImplementation, + addresses.TransparentProxyAdmin, + initCalldata4, + ]); + promises.push(promise13); + const messages = await Promise.allSettled(promises); for (const message of messages) { console.log(message.status == "fulfilled" ? message.value : message.reason); diff --git a/l1-contracts/src.ts/deploy-utils.ts b/l1-contracts/src.ts/deploy-utils.ts index 8e810e652..80c41b195 100644 --- a/l1-contracts/src.ts/deploy-utils.ts +++ b/l1-contracts/src.ts/deploy-utils.ts @@ -71,7 +71,9 @@ export async function deployBytecodeViaCreate2( const receipt = await tx.wait(); const gasUsed = receipt.gasUsed; - log(`${contractName} deployed, gasUsed: ${gasUsed.toString()}`); + log( + `${contractName} deployed, gasUsed: ${gasUsed.toString()}, tx hash: ${tx.hash}, expected address: ${expectedAddress}` + ); const deployedBytecodeAfter = await deployWallet.provider.getCode(expectedAddress); if (ethers.utils.hexDataLength(deployedBytecodeAfter) == 0) { diff --git a/l1-contracts/src.ts/deploy.ts b/l1-contracts/src.ts/deploy.ts index 7a1760762..a7009e868 100644 --- a/l1-contracts/src.ts/deploy.ts +++ b/l1-contracts/src.ts/deploy.ts @@ -45,6 +45,14 @@ export interface DeployerConfig { defaultAccountBytecodeHash?: string; } +export interface Operation { + calls: { target: string; value: BigNumberish; data: string }[]; + predecessor: string; + salt: string; +} + +export type OperationOrString = Operation | string; + export class Deployer { public addresses: DeployedAddresses; public deployWallet: Wallet; @@ -215,9 +223,9 @@ export class Deployer { public async deployTransparentProxyAdmin(create2Salt: string, ethTxOptions: ethers.providers.TransactionRequest) { ethTxOptions.gasLimit ??= 10_000_000; if (this.verbose) { - console.log("Deploying Proxy Admin factory"); + console.log("Deploying Proxy Admin"); } - + // Note: we cannot deploy using Create2, as the owner of the ProxyAdmin is msg.sender const contractFactory = await hardhat.ethers.getContractFactory("ProxyAdmin", { signer: this.deployWallet, }); @@ -443,13 +451,25 @@ export class Deployer { } /// this should be only use for local testing - public async executeUpgrade(targetAddress: string, value: BigNumberish, callData: string) { + public async executeUpgrade(targetAddress: string, value: BigNumberish, callData: string, printFileName?: string) { const governance = IGovernanceFactory.connect(this.addresses.Governance, this.deployWallet); const operation = { calls: [{ target: targetAddress, value: value, data: callData }], predecessor: ethers.constants.HashZero, salt: ethers.utils.hexlify(ethers.utils.randomBytes(32)), }; + if (printFileName) { + console.log("Operation:", operation); + console.log( + "Schedule operation: ", + governance.interface.encodeFunctionData("scheduleTransparent", [operation, 0]) + ); + console.log( + `Execute operation value: ${value}, calldata`, + governance.interface.encodeFunctionData("execute", [operation]) + ); + return; + } const scheduleTx = await governance.scheduleTransparent(operation, 0); await scheduleTx.wait(); if (this.verbose) { @@ -640,11 +660,13 @@ export class Deployer { public async registerStateTransitionManager() { const bridgehub = this.bridgehubContract(this.deployWallet); - const tx = await bridgehub.addStateTransitionManager(this.addresses.StateTransition.StateTransitionProxy); + if (!(await bridgehub.stateTransitionManagerIsRegistered(this.addresses.StateTransition.StateTransitionProxy))) { + const tx = await bridgehub.addStateTransitionManager(this.addresses.StateTransition.StateTransitionProxy); - const receipt = await tx.wait(); - if (this.verbose) { - console.log(`StateTransition System registered, gas used: ${receipt.gasUsed.toString()}`); + const receipt = await tx.wait(); + if (this.verbose) { + console.log(`StateTransition System registered, gas used: ${receipt.gasUsed.toString()}`); + } } } diff --git a/l1-contracts/src.ts/hyperchain-upgrade.ts b/l1-contracts/src.ts/hyperchain-upgrade.ts index 578ed92f0..56e8573a9 100644 --- a/l1-contracts/src.ts/hyperchain-upgrade.ts +++ b/l1-contracts/src.ts/hyperchain-upgrade.ts @@ -1,27 +1,21 @@ // hardhat import should be the first import in the file // eslint-disable-next-line @typescript-eslint/no-unused-vars import * as hardhat from "hardhat"; +import * as path from "path"; import "@nomiclabs/hardhat-ethers"; -// import * as path from "path"; import type { BigNumberish } from "ethers"; -import { BigNumber, ethers } from "ethers"; +import { ethers } from "ethers"; -import type { DiamondCut } from "./diamondCut"; -import { getFacetCutsForUpgrade } from "./diamondCut"; - -import { getTokens } from "./deploy-token"; import type { Deployer } from "./deploy"; import type { ITransparentUpgradeableProxy } from "../typechain/ITransparentUpgradeableProxy"; import { ITransparentUpgradeableProxyFactory } from "../typechain/ITransparentUpgradeableProxyFactory"; - -import { L1SharedBridgeFactory, StateTransitionManagerFactory } from "../typechain"; +import { StateTransitionManagerFactory, L1SharedBridgeFactory, ValidatorTimelockFactory } from "../typechain"; import { Interface } from "ethers/lib/utils"; -import { ADDRESS_ONE, getAddressFromEnv } from "./utils"; -import type { L2CanonicalTransaction, ProposedUpgrade, VerifierParams } from "./utils"; +import { ADDRESS_ONE, getAddressFromEnv, readBytecode } from "./utils"; import { REQUIRED_L2_GAS_PRICE_PER_PUBDATA, @@ -31,22 +25,22 @@ import { } from "../../l2-contracts/src/utils"; import { ETH_ADDRESS_IN_CONTRACTS } from "zksync-ethers/build/src/utils"; -const SYSTEM_UPGRADE_TX_TYPE = 254; -const FORCE_DEPLOYER_ADDRESS = "0x0000000000000000000000000000000000008007"; - -const BEACON_PROXY_BYTECODE = ethers.constants.HashZero; +const contractArtifactsPath = path.join(process.env.ZKSYNC_HOME as string, "contracts/l2-contracts/artifacts-zk/"); +const openzeppelinBeaconProxyArtifactsPath = path.join(contractArtifactsPath, "@openzeppelin/contracts/proxy/beacon"); +export const BEACON_PROXY_BYTECODE = readBytecode(openzeppelinBeaconProxyArtifactsPath, "BeaconProxy"); /// In the hardhat tests we do the upgrade all at once. /// On localhost/stage/.. we will call the components and send the calldata to Governance manually export async function upgradeToHyperchains( deployer: Deployer, gasPrice: BigNumberish, + printFileName?: string, create2Salt?: string, nonce?: number ) { await upgradeToHyperchains1(deployer, gasPrice, create2Salt, nonce); - await upgradeToHyperchains2(deployer, gasPrice); - await upgradeToHyperchains3(deployer); + await upgradeToHyperchains2(deployer, gasPrice, printFileName); + await upgradeToHyperchains3(deployer, printFileName); } /// this just deploys the contract ( we do it here instead of using the protocol-upgrade tool, since we are deploying more than just facets, the Bridgehub, STM, etc.) @@ -56,67 +50,163 @@ export async function upgradeToHyperchains1( create2Salt?: string, nonce?: number ) { + /// we manually override the governance address so that we can set the variables + deployer.addresses.Governance = deployer.deployWallet.address; // does not interfere with existing system // note other contract were already deployed if (deployer.verbose) { console.log("Deploying new contracts"); } await deployNewContracts(deployer, gasPrice, create2Salt, nonce); + + // register Era in Bridgehub, STM + const stateTransitionManager = deployer.stateTransitionManagerContract(deployer.deployWallet); + + if (deployer.verbose) { + console.log("Registering Era in stateTransitionManager"); + } + const txRegister = await stateTransitionManager.registerAlreadyDeployedHyperchain( + deployer.chainId, + deployer.addresses.StateTransition.DiamondProxy + ); + + await txRegister.wait(); + + const bridgehub = deployer.bridgehubContract(deployer.deployWallet); + if (deployer.verbose) { + console.log("Registering Era in Bridgehub"); + } + + const tx = await bridgehub.createNewChain( + deployer.chainId, + deployer.addresses.StateTransition.StateTransitionProxy, + ETH_ADDRESS_IN_CONTRACTS, + ethers.constants.HashZero, + deployer.addresses.Governance, + ethers.constants.HashZero, + { gasPrice } + ); + await tx.wait(); + + if (deployer.verbose) { + console.log("Setting L1Erc20Bridge data in shared bridge"); + } + const sharedBridge = L1SharedBridgeFactory.connect( + deployer.addresses.Bridges.SharedBridgeProxy, + deployer.deployWallet + ); + const tx1 = await sharedBridge.setL1Erc20Bridge(deployer.addresses.Bridges.ERC20BridgeProxy); + await tx1.wait(); + + if (deployer.verbose) { + console.log("Initializing l2 bridge in shared bridge", deployer.addresses.Bridges.L2SharedBridgeProxy); + } + const tx2 = await sharedBridge.initializeChainGovernance( + deployer.chainId, + deployer.addresses.Bridges.L2SharedBridgeProxy + ); + await tx2.wait(); + + if (deployer.verbose) { + console.log("Setting Validator timelock in STM"); + } + const stm = StateTransitionManagerFactory.connect( + deployer.addresses.StateTransition.StateTransitionProxy, + deployer.deployWallet + ); + const tx3 = await stm.setValidatorTimelock(deployer.addresses.ValidatorTimeLock); + await tx3.wait(); + + if (deployer.verbose) { + console.log("Setting dummy STM in Validator timelock"); + } + + const ethTxOptions: ethers.providers.TransactionRequest = {}; + ethTxOptions.gasLimit ??= 10_000_000; + const migrationSTMAddress = await deployer.deployViaCreate2( + "MigrationSTM", + [deployer.deployWallet.address], + create2Salt, + ethTxOptions + ); + console.log("Migration STM address", migrationSTMAddress); + + const validatorTimelock = ValidatorTimelockFactory.connect( + deployer.addresses.ValidatorTimeLock, + deployer.deployWallet + ); + const tx4 = await validatorTimelock.setStateTransitionManager(migrationSTMAddress); + await tx4.wait(); + + const validatorOneAddress = getAddressFromEnv("ETH_SENDER_SENDER_OPERATOR_COMMIT_ETH_ADDR"); + const validatorTwoAddress = getAddressFromEnv("ETH_SENDER_SENDER_OPERATOR_BLOBS_ETH_ADDR"); + const tx5 = await validatorTimelock.addValidator(deployer.chainId, validatorOneAddress, { gasPrice }); + const receipt5 = await tx5.wait(); + const tx6 = await validatorTimelock.addValidator(deployer.chainId, validatorTwoAddress, { gasPrice }); + const receipt6 = await tx6.wait(); + + const tx7 = await validatorTimelock.setStateTransitionManager( + deployer.addresses.StateTransition.StateTransitionProxy + ); + const receipt7 = await tx7.wait(); + if (deployer.verbose) { + console.log( + "Validators added, stm transferred back", + receipt5.transactionHash, + receipt6.transactionHash, + receipt7.transactionHash + ); + } } -// this simulates the main part of the upgrade, the diamond cut, registration into the Bridgehub and STM, and the bridge upgrade -// before we call this we need to generate the facet cuts using the protocol upgrade tool, on hardhat we test the dummy diamondCut -export async function upgradeToHyperchains2(deployer: Deployer, gasPrice: BigNumberish) { +// this should be called after the diamond cut has been proposed and executed +// this simulates the main part of the upgrade, registration into the Bridgehub and STM, and the bridge upgrade +export async function upgradeToHyperchains2(deployer: Deployer, gasPrice: BigNumberish, printFileName?: string) { // upgrading system contracts on Era only adds setChainId in systemContext, does not interfere with anything // we first upgrade the DiamondProxy. the Mailbox is backwards compatible, so the L1ERC20 and other bridges should still work. // this requires the sharedBridge to be deployed. // In theory, the L1SharedBridge deposits should be disabled until the L2Bridge is upgraded. // However, without the Portal, UI being upgraded it does not matter (nobody will call it, they will call the legacy bridge) - if (deployer.verbose) { - console.log("Integrating Era into Bridgehub and upgrading L2 system contract"); - } - await integrateEraIntoBridgehubAndUpgradeL2SystemContract(deployer, gasPrice); // details for L2 system contract upgrade are part of the infrastructure/protocol_upgrade tool // the L2Bridge and L1ERC20Bridge should be updated relatively in sync, as new messages might not be parsed correctly by the old bridge. // however new bridges can parse old messages. L1->L2 messages are faster, so L2 side is upgraded first. if (deployer.verbose) { console.log("Upgrading L2 bridge"); } - await upgradeL2Bridge(deployer); + await upgradeL2Bridge(deployer, gasPrice, printFileName); + + if (deployer.verbose) { + console.log("Transferring L1 ERC20 bridge to proxy admin"); + } + await transferERC20BridgeToProxyAdmin(deployer, gasPrice, printFileName); - if (process.env.CHAIN_ETH_NETWORK === "localhost") { + if (process.env.CHAIN_ETH_NETWORK != "hardhat") { if (deployer.verbose) { console.log("Upgrading L1 ERC20 bridge"); } - await upgradeL1ERC20Bridge(deployer); + await upgradeL1ERC20Bridge(deployer, gasPrice, printFileName); } - - // note, withdrawals will not work until this step, but deposits will - if (deployer.verbose) { - console.log("Migrating assets from L1 ERC20 bridge and ChainBalance"); - } - await migrateAssets(deployer); } // This sets the Shared Bridge parameters. We need to do this separately, as these params will be known after the upgrade -export async function upgradeToHyperchains3(deployer: Deployer) { +export async function upgradeToHyperchains3(deployer: Deployer, printFileName?: string) { const sharedBridge = L1SharedBridgeFactory.connect( deployer.addresses.Bridges.SharedBridgeProxy, deployer.deployWallet ); const data2 = sharedBridge.interface.encodeFunctionData("setEraPostDiamondUpgradeFirstBatch", [ - process.env.CONTRACTS_ERA_POST_DIAMOND_UPGRADE_FIRST_BATCH ?? 1, + process.env.CONTRACTS_ERA_POST_DIAMOND_UPGRADE_FIRST_BATCH, ]); const data3 = sharedBridge.interface.encodeFunctionData("setEraPostLegacyBridgeUpgradeFirstBatch", [ - process.env.CONTRACTS_ERA_POST_LEGACY_BRIDGE_UPGRADE_FIRST_BATCH ?? 1, + process.env.CONTRACTS_ERA_POST_LEGACY_BRIDGE_UPGRADE_FIRST_BATCH, ]); const data4 = sharedBridge.interface.encodeFunctionData("setEraLegacyBridgeLastDepositTime", [ - process.env.CONTRACTS_ERA_LEGACY_UPGRADE_LAST_DEPOSIT_BATCH ?? 1, - process.env.CONTRACTS_ERA_LEGACY_UPGRADE_LAST_DEPOSIT_TX_NUMBER ?? 0, + process.env.CONTRACTS_ERA_LEGACY_UPGRADE_LAST_DEPOSIT_BATCH, + process.env.CONTRACTS_ERA_LEGACY_UPGRADE_LAST_DEPOSIT_TX_NUMBER, ]); - await deployer.executeUpgrade(deployer.addresses.Bridges.SharedBridgeProxy, 0, data2); - await deployer.executeUpgrade(deployer.addresses.Bridges.SharedBridgeProxy, 0, data3); - await deployer.executeUpgrade(deployer.addresses.Bridges.SharedBridgeProxy, 0, data4); + await deployer.executeUpgrade(deployer.addresses.Bridges.SharedBridgeProxy, 0, data2, printFileName); + await deployer.executeUpgrade(deployer.addresses.Bridges.SharedBridgeProxy, 0, data3, printFileName); + await deployer.executeUpgrade(deployer.addresses.Bridges.SharedBridgeProxy, 0, data4, printFileName); } async function deployNewContracts(deployer: Deployer, gasPrice: BigNumberish, create2Salt?: string, nonce?: number) { @@ -129,21 +219,19 @@ async function deployNewContracts(deployer: Deployer, gasPrice: BigNumberish, cr gasPrice, nonce, }); - nonce++; - await deployer.deployValidatorTimelock(create2Salt, { gasPrice, nonce }); - nonce++; + await deployer.deployValidatorTimelock(create2Salt, { gasPrice }); await deployer.deployHyperchainsUpgrade(create2Salt, { gasPrice, - nonce, }); - nonce++; - await deployer.deployVerifier(create2Salt, { gasPrice, nonce }); + await deployer.deployVerifier(create2Salt, { gasPrice }); if (process.env.CHAIN_ETH_NETWORK != "hardhat") { await deployer.deployTransparentProxyAdmin(create2Salt, { gasPrice }); } + // console.log("Proxy admin is already deployed (not via Create2)", deployer.addresses.TransparentProxyAdmin); + // console.log("CONTRACTS_TRANSPARENT_PROXY_ADMIN_ADDR=0xf2c1d17441074FFb18E9A918db81A17dB1752146"); await deployer.deployBridgehubContract(create2Salt, gasPrice); await deployer.deployStateTransitionManagerContract(create2Salt, [], gasPrice); @@ -151,180 +239,16 @@ async function deployNewContracts(deployer: Deployer, gasPrice: BigNumberish, cr await deployer.deploySharedBridgeContracts(create2Salt, gasPrice); await deployer.deployERC20BridgeImplementation(create2Salt, { gasPrice }); - await deployer.deployERC20BridgeProxy(create2Salt, { gasPrice }); -} - -async function integrateEraIntoBridgehubAndUpgradeL2SystemContract(deployer: Deployer, gasPrice: BigNumberish) { - // publish L2 system contracts - if (process.env.CHAIN_ETH_NETWORK === "hardhat") { - // era facet cut - const newProtocolVersion = 24; - const toAddress: string = ethers.constants.AddressZero; - const calldata: string = ethers.constants.HashZero; - const l2ProtocolUpgradeTx: L2CanonicalTransaction = { - txType: SYSTEM_UPGRADE_TX_TYPE, - from: FORCE_DEPLOYER_ADDRESS, - to: toAddress, - gasLimit: 72_000_000, - gasPerPubdataByteLimit: REQUIRED_L2_GAS_PRICE_PER_PUBDATA, - maxFeePerGas: 0, - maxPriorityFeePerGas: 0, - paymaster: 0, - nonce: newProtocolVersion, - value: 0, - reserved: [0, 0, 0, 0], - data: calldata, - signature: "0x", - factoryDeps: [], - paymasterInput: "0x", - reservedDynamic: "0x", - }; - const upgradeTimestamp = BigNumber.from(100); - const verifierParams: VerifierParams = { - recursionNodeLevelVkHash: ethers.constants.HashZero, - recursionLeafLevelVkHash: ethers.constants.HashZero, - recursionCircuitsSetVksHash: ethers.constants.HashZero, - }; - const postUpgradeCalldata = new ethers.utils.AbiCoder().encode( - ["uint256", "address", "address", "address"], - [ - deployer.chainId, - deployer.addresses.Bridgehub.BridgehubProxy, - deployer.addresses.StateTransition.StateTransitionProxy, - deployer.addresses.Bridges.SharedBridgeProxy, - ] - ); - const proposedUpgrade: ProposedUpgrade = { - l2ProtocolUpgradeTx, - factoryDeps: [], - bootloaderHash: ethers.constants.HashZero, - defaultAccountHash: ethers.constants.HashZero, - verifier: ethers.constants.AddressZero, - verifierParams: verifierParams, - l1ContractsUpgradeCalldata: ethers.constants.HashZero, - postUpgradeCalldata: postUpgradeCalldata, - upgradeTimestamp: upgradeTimestamp, - newProtocolVersion: 24, - }; - const upgradeHyperchains = new Interface(hardhat.artifacts.readArtifactSync("UpgradeHyperchains").abi); - const defaultUpgradeData = upgradeHyperchains.encodeFunctionData("upgrade", [proposedUpgrade]); - - const facetCuts = await getFacetCutsForUpgrade( - deployer.deployWallet, - deployer.addresses.StateTransition.DiamondProxy, - deployer.addresses.StateTransition.AdminFacet, - deployer.addresses.StateTransition.GettersFacet, - deployer.addresses.StateTransition.MailboxFacet, - deployer.addresses.StateTransition.ExecutorFacet - ); - const diamondCut: DiamondCut = { - facetCuts, - initAddress: deployer.addresses.StateTransition.DefaultUpgrade, - initCalldata: defaultUpgradeData, - }; - const adminFacet = new Interface(hardhat.artifacts.readArtifactSync("DummyAdminFacetNoOverlap").abi); - - const data = adminFacet.encodeFunctionData("executeUpgradeNoOverlap", [diamondCut]); - await deployer.executeUpgrade(deployer.addresses.StateTransition.DiamondProxy, 0, data); - } - // register Era in Bridgehub, STM - const stateTransitionManager = deployer.stateTransitionManagerContract(deployer.deployWallet); - - if (deployer.verbose) { - console.log("Registering Era in stateTransitionManager"); - } - const registerData = stateTransitionManager.interface.encodeFunctionData("registerAlreadyDeployedHyperchain", [ - deployer.chainId, - deployer.addresses.StateTransition.DiamondProxy, - ]); - await deployer.executeUpgrade(deployer.addresses.StateTransition.StateTransitionProxy, 0, registerData); - const bridgehub = deployer.bridgehubContract(deployer.deployWallet); - if (deployer.verbose) { - console.log("Registering Era in Bridgehub"); - } - const tx = await bridgehub.createNewChain( - deployer.chainId, - deployer.addresses.StateTransition.StateTransitionProxy, - ETH_ADDRESS_IN_CONTRACTS, - ethers.constants.HashZero, - deployer.addresses.Governance, - ethers.constants.HashZero, - { gasPrice } - ); - - await tx.wait(); - if (deployer.verbose) { - console.log("Setting L1Erc20Bridge data in shared bridge"); - } - const sharedBridge = L1SharedBridgeFactory.connect( - deployer.addresses.Bridges.SharedBridgeProxy, - deployer.deployWallet - ); - const data1 = sharedBridge.interface.encodeFunctionData("setL1Erc20Bridge", [ - deployer.addresses.Bridges.ERC20BridgeProxy, - ]); - await deployer.executeUpgrade(deployer.addresses.Bridges.SharedBridgeProxy, 0, data1); - if (process.env.CHAIN_ETH_NETWORK != "hardhat") { - if (deployer.verbose) { - console.log("Initializing l2 bridge in shared bridge"); - } - const data2 = sharedBridge.interface.encodeFunctionData("initializeChainGovernance", [ - deployer.chainId, - deployer.addresses.Bridges.L2SharedBridgeProxy, - ]); - await deployer.executeUpgrade(deployer.addresses.Bridges.SharedBridgeProxy, 0, data2); - } - if (deployer.verbose) { - console.log("Setting validators in hyperchain"); - } - // we have to set it via the STM - const stm = StateTransitionManagerFactory.connect( - deployer.addresses.StateTransition.DiamondProxy, - deployer.deployWallet - ); - const data3 = stm.interface.encodeFunctionData("setValidator", [ - deployer.chainId, - deployer.addresses.ValidatorTimeLock, - true, - ]); - await deployer.executeUpgrade(deployer.addresses.StateTransition.StateTransitionProxy, 0, data3); - - if (deployer.verbose) { - console.log("Setting validators in validator timelock"); - } - - // adding to validator timelock - const validatorOneAddress = getAddressFromEnv("ETH_SENDER_SENDER_OPERATOR_COMMIT_ETH_ADDR"); - const validatorTwoAddress = getAddressFromEnv("ETH_SENDER_SENDER_OPERATOR_BLOBS_ETH_ADDR"); - const validatorTimelock = deployer.validatorTimelock(deployer.deployWallet); - const txRegisterValidator = await validatorTimelock.addValidator(deployer.chainId, validatorOneAddress, { - gasPrice, - }); - const receiptRegisterValidator = await txRegisterValidator.wait(); - if (deployer.verbose) { - console.log( - `Validator registered, gas used: ${receiptRegisterValidator.gasUsed.toString()}, tx hash: ${ - txRegisterValidator.hash - }` - ); - } - - const tx3 = await validatorTimelock.addValidator(deployer.chainId, validatorTwoAddress, { - gasPrice, - }); - const receipt3 = await tx3.wait(); - if (deployer.verbose) { - console.log(`Validator 2 registered, gas used: ${receipt3.gasUsed.toString()}`); - } } -async function upgradeL2Bridge(deployer: Deployer) { +async function upgradeL2Bridge(deployer: Deployer, gasPrice: BigNumberish, printFileName?: string) { const l2BridgeImplementationAddress = process.env.CONTRACTS_L2_SHARED_BRIDGE_IMPL_ADDR!; // upgrade from L1 governance. This has to come from governacne on L1. const l2BridgeAbi = ["function initialize(address, address, bytes32, address)"]; const l2BridgeContract = new ethers.Contract(ADDRESS_ONE, l2BridgeAbi, deployer.deployWallet); const l2Bridge = l2BridgeContract.interface; //L2_SHARED_BRIDGE_INTERFACE; + const l2BridgeCalldata = l2Bridge.encodeFunctionData("initialize", [ deployer.addresses.Bridges.SharedBridgeProxy, deployer.addresses.Bridges.ERC20BridgeProxy, @@ -341,48 +265,33 @@ async function upgradeL2Bridge(deployer: Deployer) { l2BridgeImplementationAddress, l2BridgeCalldata, ]); + // console.log("kl todo", l2BridgeImplementationAddress, l2BridgeCalldata) const factoryDeps = []; - const gasPrice = await deployer.deployWallet.getGasPrice(); const requiredValueForL2Tx = await deployer .bridgehubContract(deployer.deployWallet) .l2TransactionBaseCost(deployer.chainId, gasPrice, priorityTxMaxGasLimit, REQUIRED_L2_GAS_PRICE_PER_PUBDATA); //"1000000000000000000"; - if (process.env.CHAIN_ETH_NETWORK === "localhost") { - // on the main branch the l2SharedBridge governor is incorrectly set to deploy wallet, so we can just make the call - const hyperchain = deployer.stateTransitionContract(deployer.deployWallet); - const tx = await hyperchain.requestL2Transaction( - process.env.CONTRACTS_L2_ERC20_BRIDGE_ADDR, - 0, - l2ProxyCalldata, - priorityTxMaxGasLimit, - REQUIRED_L2_GAS_PRICE_PER_PUBDATA, - factoryDeps, - deployer.deployWallet.address, - { value: requiredValueForL2Tx.mul(10) } - ); - await tx.wait(); - } else { - const mailboxFacet = new Interface(hardhat.artifacts.readArtifactSync("MailboxFacet").abi); - const mailboxCalldata = mailboxFacet.encodeFunctionData("requestL2Transaction", [ - process.env.CONTRACTS_L2_ERC20_BRIDGE_ADDR, - 0, - l2ProxyCalldata, - priorityTxMaxGasLimit, - REQUIRED_L2_GAS_PRICE_PER_PUBDATA, - factoryDeps, - deployer.deployWallet.address, - ]); + const mailboxFacet = new Interface(hardhat.artifacts.readArtifactSync("MailboxFacet").abi); + const mailboxCalldata = mailboxFacet.encodeFunctionData("requestL2Transaction", [ + process.env.CONTRACTS_L2_ERC20_BRIDGE_ADDR, + 0, + l2ProxyCalldata, + priorityTxMaxGasLimit, + REQUIRED_L2_GAS_PRICE_PER_PUBDATA, + factoryDeps, + deployer.deployWallet.address, + ]); - await deployer.executeUpgrade( - deployer.addresses.StateTransition.DiamondProxy, - requiredValueForL2Tx.mul(10), - mailboxCalldata - ); - } + await deployer.executeUpgrade( + deployer.addresses.StateTransition.DiamondProxy, + requiredValueForL2Tx, + mailboxCalldata, + printFileName + ); } -async function upgradeL1ERC20Bridge(deployer: Deployer) { - if (process.env.CHAIN_ETH_NETWORK === "localhost") { +async function upgradeL1ERC20Bridge(deployer: Deployer, gasPrice: BigNumberish, printFileName?: string) { + if (process.env.CHAIN_ETH_NETWORK != "hardhat") { // we need to wait here for a new block await new Promise((resolve) => setTimeout(resolve, 5000)); // upgrade ERC20. @@ -397,7 +306,7 @@ async function upgradeL1ERC20Bridge(deployer: Deployer) { deployer.addresses.Bridges.ERC20BridgeImplementation, ]); - await deployer.executeUpgrade(deployer.addresses.TransparentProxyAdmin, 0, data1); + await deployer.executeUpgrade(deployer.addresses.TransparentProxyAdmin, 0, data1, printFileName); if (deployer.verbose) { console.log("L1ERC20Bridge upgrade sent"); @@ -405,41 +314,35 @@ async function upgradeL1ERC20Bridge(deployer: Deployer) { } } -async function migrateAssets(deployer: Deployer) { - // migrate assets from L1 ERC20 bridge - if (deployer.verbose) { - console.log("transferring Eth"); - } - const sharedBridge = deployer.defaultSharedBridge(deployer.deployWallet); - const ethTransferData = sharedBridge.interface.encodeFunctionData("safeTransferFundsFromLegacy", [ - ADDRESS_ONE, - deployer.addresses.StateTransition.DiamondProxy, - deployer.chainId, - 300_000, +export async function transferERC20BridgeToProxyAdmin( + deployer: Deployer, + gasPrice: BigNumberish, + printFileName?: string +) { + const bridgeProxy: ITransparentUpgradeableProxy = ITransparentUpgradeableProxyFactory.connect( + deployer.addresses.Bridges.ERC20BridgeProxy, + deployer.deployWallet + ); + const data1 = await bridgeProxy.interface.encodeFunctionData("changeAdmin", [ + deployer.addresses.TransparentProxyAdmin, ]); - await deployer.executeUpgrade(deployer.addresses.Bridges.SharedBridgeProxy, 0, ethTransferData); - const tokens = getTokens(); - const altTokenAddress = tokens.find((token: { symbol: string }) => token.symbol == "DAI")!.address; + await deployer.executeUpgrade(deployer.addresses.Bridges.ERC20BridgeProxy, 0, data1, printFileName); + if (deployer.verbose) { - console.log("transferring Dai, ", altTokenAddress); + console.log("ERC20Bridge ownership transfer sent"); } +} - // Mint some tokens - const l1Erc20ABI = ["function mint(address to, uint256 amount)"]; - const l1Erc20Contract = new ethers.Contract(altTokenAddress, l1Erc20ABI, deployer.deployWallet); - const mintTx = await l1Erc20Contract.mint( +export async function transferTokens(deployer: Deployer, token: string) { + const sharedBridge = deployer.defaultSharedBridge(deployer.deployWallet); + const tx = await sharedBridge.safeTransferFundsFromLegacy( + token, deployer.addresses.Bridges.ERC20BridgeProxy, - ethers.utils.parseEther("10000.0") + "324", + "300000", + { gasLimit: 25_000_000 } ); - await mintTx.wait(); - - const daiTransferData = sharedBridge.interface.encodeFunctionData("safeTransferFundsFromLegacy", [ - altTokenAddress, - deployer.addresses.Bridges.ERC20BridgeProxy, - deployer.chainId, - 300_000, - ]); - // daiTransferData; - await deployer.executeUpgrade(deployer.addresses.Bridges.SharedBridgeProxy, 0, daiTransferData); + await tx.wait(); + console.log("Receipt", tx.hash); } diff --git a/l1-contracts/test/test_config/constant/hardhat.json b/l1-contracts/test/test_config/constant/hardhat.json index b9bd70c61..0e63431f0 100644 --- a/l1-contracts/test/test_config/constant/hardhat.json +++ b/l1-contracts/test/test_config/constant/hardhat.json @@ -3,96 +3,96 @@ "name": "DAI", "symbol": "DAI", "decimals": 18, - "address": "0xfCdA09988fC2433BFE27BBe9e95E0C007ee272Fd" + "address": "0xD6E49dd4fb0CA1549566869725d1820aDEb92Ae9" }, { "name": "wBTC", "symbol": "wBTC", "decimals": 8, - "address": "0x3b78cDD294f0b34755c8a9bA8f124Cb6c217b60a" + "address": "0xcee1f75F30B6908286Cd003C4228A5D9a2851FA4" }, { "name": "BAT", "symbol": "BAT", "decimals": 18, - "address": "0xd4a9D970EF600289dC96b67505027D17604FBfe1" + "address": "0x0Bc76A4EfE0748f1697F237fB100741ea6Ceda2d" }, { "name": "GNT", "symbol": "GNT", "decimals": 18, - "address": "0x4fE0EE70D78132fBb5C68C1d5e0020f3c05542f2" + "address": "0x51ae50BcCEE10ac5BEFFA1E4a64106a5f83bc3F8" }, { "name": "MLTT", "symbol": "MLTT", "decimals": 18, - "address": "0x3202935CA01eADd2F0B4e7aA23EFA52315121172" + "address": "0xa9c7fEEf8586E17D93A05f873BA65f28f48ED259" }, { "name": "DAIK", "symbol": "DAIK", "decimals": 18, - "address": "0xe73a1c05498e492f182f9B3E3cd65b2F9f52eE94" + "address": "0x99Efb27598804Aa408A1066550e9d01c45f21b05" }, { "name": "wBTCK", "symbol": "wBTCK", "decimals": 8, - "address": "0x7aAE2ee8317b384aDE246664933A147411b60045" + "address": "0x4B701928Da6B3e72775b462A15b8b76ba2d16BbD" }, { "name": "BATK", "symbol": "BATS", "decimals": 18, - "address": "0x02a344d1e31e92e39c2681937645F1d668C37d4e" + "address": "0xf7B03c921dfefB4286b13075BA0335099708368D" }, { "name": "GNTK", "symbol": "GNTS", "decimals": 18, - "address": "0x28033f8EdB2F43747E55401C4a3E3b4b2cF5146C" + "address": "0xc0581Ee28c519533B06cc0aAC1ace98cF63C817b" }, { "name": "MLTTK", "symbol": "MLTTS", "decimals": 18, - "address": "0x147dCc3a8E99794C2Ec79EaF1a142324D6778255" + "address": "0xeB6394F2E8DA607b94dBa2Cf345A965d6D9b3aCD" }, { "name": "DAIL", "symbol": "DAIL", "decimals": 18, - "address": "0x74cA8715E29196Bfbcd7444B09203d22dDaF7d1a" + "address": "0x4311643C5eD7cD0813B4E3Ff5428de71c7d7b8bB" }, { "name": "wBTCL", "symbol": "wBTCP", "decimals": 8, - "address": "0x2eAf3eac597d23db3A8427329482aE47935141d9" + "address": "0x6b3fbfC9Bb89Ab5F11BE782a1f67c1615c2A5fc3" }, { "name": "BATL", "symbol": "BATW", "decimals": 18, - "address": "0x2B1b32d23f2be391280bFbD2B51daB8Ad2a69B9e" + "address": "0xE003698b7831829843B69D3fB4f9a3133d97b257" }, { "name": "GNTL", "symbol": "GNTW", "decimals": 18, - "address": "0xc2Ca10940Ad80Cd98512B767457bd44713232B5a" + "address": "0x2417626170675Ccf6022d9db1eFC8f3c59836368" }, { "name": "MLTTL", "symbol": "MLTTW", "decimals": 18, - "address": "0xd6b4CDa9F0Ef6d9F6b35Ab5424df0dD95E6a6D1b" + "address": "0x28106C39BE5E51C31D9a289313361D86C9bb7C8E" }, { "name": "Wrapped Ether", "symbol": "WETH", "decimals": 18, - "address": "0x15CD4a1C10AE2D3727Dad680a1966947931588C9" + "address": "0x51E83b811930bb4a3aAb3494894ec237Cb6cEc49" } ] diff --git a/l2-contracts/hardhat.config.ts b/l2-contracts/hardhat.config.ts index 155611b6a..c0aaca03e 100644 --- a/l2-contracts/hardhat.config.ts +++ b/l2-contracts/hardhat.config.ts @@ -43,7 +43,7 @@ export default { // contract verification endpoint verifyURL: "https://explorer.sepolia.era.zksync.dev/contract_verification", }, - zksyncMainnet: { + zkSyncMainnet: { url: "https://mainnet.era.zksync.io", ethNetwork: "mainnet", zksync: true, diff --git a/l2-contracts/src/upgrade-consistency-checker.ts b/l2-contracts/src/upgrade-consistency-checker.ts index 74f286eae..8bebe197d 100644 --- a/l2-contracts/src/upgrade-consistency-checker.ts +++ b/l2-contracts/src/upgrade-consistency-checker.ts @@ -13,9 +13,9 @@ import { Provider } from "zksync-ethers"; // 2. Getter methods in STM. // List the contracts that should become the upgrade targets -const l2BridgeImplAddr = "0xce0a8c005a73e35d95cec41a9e8b75668470fb8f"; +const l2BridgeImplAddr = "0x470afaacce2acdaefcc662419b74c79d76c914ae"; -const eraChainId = 270; +const eraChainId = 324; const l2Provider = new Provider(process.env.API_WEB3_JSON_RPC_HTTP_URL); diff --git a/l2-contracts/src/verify.ts b/l2-contracts/src/verify.ts index 53fc28f33..a45c83795 100644 --- a/l2-contracts/src/verify.ts +++ b/l2-contracts/src/verify.ts @@ -22,13 +22,14 @@ async function main() { // Contracts without constructor parameters for (const address of [ process.env.CONTRACTS_L2_WETH_TOKEN_IMPL_ADDR, - process.env.CONTRACTS_L2_ERC20_BRIDGE_IMPL_ADDR, process.env.CONTRACTS_L2_ERC20_BRIDGE_TOKEN_IMPL_ADDR, ]) { const promise = verifyPromise(address); promises.push(promise); } + promises.push(verifyPromise(process.env.CONTRACTS_L2_SHARED_BRIDGE_IMPL_ADDR, [process.env.CONTRACTS_ERA_CHAIN_ID])); + const messages = await Promise.allSettled(promises); for (const message of messages) { console.log(message.status == "fulfilled" ? message.value : message.reason); diff --git a/system-contracts/hardhat.config.ts b/system-contracts/hardhat.config.ts index cc0a5e0b4..2adf0c440 100644 --- a/system-contracts/hardhat.config.ts +++ b/system-contracts/hardhat.config.ts @@ -55,6 +55,14 @@ export default { ethNetwork: "localhost", zksync: true, }, + stage: { + url: "https://z2-dev-api.zksync.dev/", + ethNetwork: "sepolia", + zksync: true, + }, + sepolia: { + url: "", // add your sepolia node url + }, }, paths: { sources: "./contracts-preprocessed", diff --git a/system-contracts/scripts/deploy-preimages.ts b/system-contracts/scripts/deploy-preimages.ts index 035b98e88..6803f9a53 100644 --- a/system-contracts/scripts/deploy-preimages.ts +++ b/system-contracts/scripts/deploy-preimages.ts @@ -19,7 +19,7 @@ const testConfigPath = path.join(process.env.ZKSYNC_HOME as string, "etc/test_co const ethTestConfig = JSON.parse(fs.readFileSync(`${testConfigPath}/eth.json`, { encoding: "utf-8" })); // Maximum length of the combined length of dependencies -const MAX_COMBINED_LENGTH = 90000; +const MAX_COMBINED_LENGTH = 125000; const DEFAULT_ACCOUNT_CONTRACT_NAME = "DefaultAccount"; const BOOTLOADER_CONTRACT_NAME = "Bootloader";