From 06d4311dda56d0b0b7284bb045b2c80fa9b8f0aa Mon Sep 17 00:00:00 2001 From: Lyova Potyomkin Date: Mon, 22 Apr 2024 14:19:24 +0300 Subject: [PATCH] feat: allow using era's legacy interface for testing (#384) Co-authored-by: Stanislav Breadless --- .../dev-contracts/DummyL1ERC20Bridge.sol | 16 +++++ .../scripts/upgrade-shared-bridge-era.ts | 70 ++++++++++++++++++- l1-contracts/src.ts/deploy.ts | 8 ++- l1-contracts/test/unit_tests/utils.ts | 3 +- l1-contracts/tsconfig.json | 3 +- .../deploy-shared-bridge-on-l2-through-l1.ts | 23 ++++-- 6 files changed, 110 insertions(+), 13 deletions(-) create mode 100644 l1-contracts/contracts/dev-contracts/DummyL1ERC20Bridge.sol diff --git a/l1-contracts/contracts/dev-contracts/DummyL1ERC20Bridge.sol b/l1-contracts/contracts/dev-contracts/DummyL1ERC20Bridge.sol new file mode 100644 index 000000000000..8155ddf6b06a --- /dev/null +++ b/l1-contracts/contracts/dev-contracts/DummyL1ERC20Bridge.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.24; + +import {L1ERC20Bridge} from "../bridge/L1ERC20Bridge.sol"; +import {IL1SharedBridge} from "../bridge/interfaces/IL1SharedBridge.sol"; + +contract DummyL1ERC20Bridge is L1ERC20Bridge { + constructor(IL1SharedBridge _l1SharedBridge) L1ERC20Bridge(_l1SharedBridge) {} + + function setValues(address _l2Bridge, address _l2TokenBeacon, bytes32 _l2TokenProxyBytecodeHash) external { + l2Bridge = _l2Bridge; + l2TokenBeacon = _l2TokenBeacon; + l2TokenProxyBytecodeHash = _l2TokenProxyBytecodeHash; + } +} diff --git a/l1-contracts/scripts/upgrade-shared-bridge-era.ts b/l1-contracts/scripts/upgrade-shared-bridge-era.ts index e29c1172c448..659005b80300 100644 --- a/l1-contracts/scripts/upgrade-shared-bridge-era.ts +++ b/l1-contracts/scripts/upgrade-shared-bridge-era.ts @@ -7,7 +7,10 @@ import { Deployer } from "../src.ts/deploy"; import { formatUnits, parseUnits, Interface } from "ethers/lib/utils"; import { web3Provider, GAS_MULTIPLIER } from "./utils"; import { deployedAddressesFromEnv } from "../src.ts/deploy-utils"; -import { ethTestConfig } from "../src.ts/utils"; +import { ethTestConfig, getAddressFromEnv } from "../src.ts/utils"; +import { hashL2Bytecode } from "../../l2-contracts/src/utils"; +import { Provider } from "zksync-web3"; +import beaconProxy = require("../../l2-contracts/artifacts-zk/@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol/BeaconProxy.json"); const provider = web3Provider(); @@ -18,7 +21,6 @@ async function main() { program .option("--private-key ") - .option("--chain-id ") .option("--gas-price ") .option("--nonce ") .option("--owner-address ") @@ -57,12 +59,56 @@ async function main() { await deployer.deploySharedBridgeImplementation(create2Salt, { nonce }); const proxyAdminInterface = new Interface(hardhat.artifacts.readArtifactSync("ProxyAdmin").abi); - const calldata = proxyAdminInterface.encodeFunctionData("upgrade(address,address)", [ + let calldata = proxyAdminInterface.encodeFunctionData("upgrade(address,address)", [ deployer.addresses.Bridges.SharedBridgeProxy, deployer.addresses.Bridges.SharedBridgeImplementation, ]); await deployer.executeUpgrade(deployer.addresses.TransparentProxyAdmin, 0, calldata); + + // deploy a dummy erc20 bridge to set the storage values + await deployer.deployERC20BridgeImplementation(create2Salt, {}, true); + + // upgrade to dummy bridge + calldata = proxyAdminInterface.encodeFunctionData("upgrade(address,address)", [ + deployer.addresses.Bridges.ERC20BridgeProxy, + deployer.addresses.Bridges.ERC20BridgeImplementation, + ]); + + await deployer.executeUpgrade(deployer.addresses.TransparentProxyAdmin, 0, calldata); + console.log("Upgraded ERC20Bridge to 'dummy' implementation"); + + const dummyBridgeAbi = hardhat.artifacts.readArtifactSync("DummyL1ERC20Bridge").abi; + const dummyBridge = new ethers.Contract( + deployer.addresses.Bridges.ERC20BridgeProxy, + dummyBridgeAbi, + deployWallet + ); + + const l2SharedBridgeAddress = getAddressFromEnv("CONTRACTS_L2_SHARED_BRIDGE_ADDR"); + const l2TokenBytecodeHash = hashL2Bytecode(beaconProxy.bytecode); + const l2Provider = new Provider(process.env.API_WEB3_JSON_RPC_HTTP_URL); + // For the server to start up. + console.log("Waiting for server to start up"); + await waitForServer(l2Provider); + + const l2SharedBridge = new ethers.Contract( + l2SharedBridgeAddress, + ["function l2TokenBeacon() view returns (address)"], + l2Provider + ); + const l2TokenBeacon = await l2SharedBridge.l2TokenBeacon(); + + console.log("Retrieved storage values for TestERC20Bridge:"); + console.log("l2SharedBridgeAddress:", l2SharedBridgeAddress); + console.log("l2TokenBeacon:", l2TokenBeacon); + console.log("l2TokenBytecodeHash:", ethers.utils.hexlify(l2TokenBytecodeHash)); + + // set storage values + const tx = await dummyBridge.setValues(l2SharedBridgeAddress, l2TokenBeacon, l2TokenBytecodeHash); + await tx.wait(); + + console.log("Set storage values for TestERC20Bridge"); }); await program.parseAsync(process.argv); @@ -74,3 +120,21 @@ main() console.error("Error:", err); process.exit(1); }); + +async function waitForServer(provider: Provider) { + let iter = 0; + while (iter < 60) { + try { + await provider.getBlockNumber(); + return; + } catch (_) { + await sleep(2); + iter += 1; + } + } + throw new Error("Server didn't start up in time. Exiting."); +} + +async function sleep(seconds: number) { + return new Promise((resolve) => setTimeout(resolve, seconds * 1000)); +} diff --git a/l1-contracts/src.ts/deploy.ts b/l1-contracts/src.ts/deploy.ts index 78e148f5f6f8..3039e2c49267 100644 --- a/l1-contracts/src.ts/deploy.ts +++ b/l1-contracts/src.ts/deploy.ts @@ -401,10 +401,14 @@ export class Deployer { this.addresses.StateTransition.Verifier = contractAddress; } - public async deployERC20BridgeImplementation(create2Salt: string, ethTxOptions: ethers.providers.TransactionRequest) { + public async deployERC20BridgeImplementation( + create2Salt: string, + ethTxOptions: ethers.providers.TransactionRequest, + dummy: boolean = false + ) { ethTxOptions.gasLimit ??= 10_000_000; const contractAddress = await this.deployViaCreate2( - "L1ERC20Bridge", + dummy ? "DummyL1ERC20Bridge" : "L1ERC20Bridge", [this.addresses.Bridges.SharedBridgeProxy], create2Salt, ethTxOptions diff --git a/l1-contracts/test/unit_tests/utils.ts b/l1-contracts/test/unit_tests/utils.ts index 5edd03e81340..fd34a0e7fa58 100644 --- a/l1-contracts/test/unit_tests/utils.ts +++ b/l1-contracts/test/unit_tests/utils.ts @@ -380,7 +380,7 @@ export async function depositERC20( const neededValue = await bridgehubContract.l2TransactionBaseCost(chainId, gasPrice, l2GasLimit, gasPerPubdata); const ethIsBaseToken = (await bridgehubContract.baseToken(chainId)) == ADDRESS_ONE; - await bridge["deposit(address,address,uint256,uint256,uint256,address)"]( + const deposit = await bridge["deposit(address,address,uint256,uint256,uint256,address)"]( l2Receiver, l1Token, amount, @@ -391,6 +391,7 @@ export async function depositERC20( value: ethIsBaseToken ? neededValue : 0, } ); + await deposit.wait(); } export function buildL2CanonicalTransaction(tx: Partial): L2CanonicalTransaction { diff --git a/l1-contracts/tsconfig.json b/l1-contracts/tsconfig.json index 72a5bef45fb0..41d8e3fa873f 100644 --- a/l1-contracts/tsconfig.json +++ b/l1-contracts/tsconfig.json @@ -1,6 +1,7 @@ { "compilerOptions": { "types": ["node", "mocha"], - "downlevelIteration": true + "downlevelIteration": true, + "resolveJsonModule": true } } diff --git a/l2-contracts/src/deploy-shared-bridge-on-l2-through-l1.ts b/l2-contracts/src/deploy-shared-bridge-on-l2-through-l1.ts index e431bc344ca4..226f1a5058b9 100644 --- a/l2-contracts/src/deploy-shared-bridge-on-l2-through-l1.ts +++ b/l2-contracts/src/deploy-shared-bridge-on-l2-through-l1.ts @@ -50,7 +50,12 @@ export async function publishL2SharedBridgeDependencyBytecodesOnL2( } } -export async function deploySharedBridgeImplOnL2ThroughL1(deployer: Deployer, chainId: string, gasPrice: BigNumberish) { +export async function deploySharedBridgeImplOnL2ThroughL1( + deployer: Deployer, + chainId: string, + gasPrice: BigNumberish, + chainIdHack: boolean = false +) { if (deployer.verbose) { console.log("Deploying L2SharedBridge Implementation"); } @@ -66,7 +71,7 @@ export async function deploySharedBridgeImplOnL2ThroughL1(deployer: Deployer, ch const l2SharedBridgeImplAddress = computeL2Create2Address( deployer.deployWallet, L2_SHARED_BRIDGE_IMPLEMENTATION_BYTECODE, - defaultAbiCoder.encode(["uint256"], [eraChainId]), + defaultAbiCoder.encode(["uint256"], [chainIdHack ? 0 : eraChainId]), ethers.constants.HashZero ); deployer.addresses.Bridges.L2SharedBridgeImplementation = l2SharedBridgeImplAddress; @@ -84,7 +89,7 @@ export async function deploySharedBridgeImplOnL2ThroughL1(deployer: Deployer, ch chainId, deployer.deployWallet, L2_SHARED_BRIDGE_IMPLEMENTATION_BYTECODE, - defaultAbiCoder.encode(["uint256"], [eraChainId]), + defaultAbiCoder.encode(["uint256"], [chainIdHack ? 0 : eraChainId]), ethers.constants.HashZero, priorityTxMaxGasLimit, gasPrice, @@ -174,9 +179,14 @@ export async function initializeChainGovernance(deployer: Deployer, chainId: str } } -export async function deploySharedBridgeOnL2ThroughL1(deployer: Deployer, chainId: string, gasPrice: BigNumberish) { +export async function deploySharedBridgeOnL2ThroughL1( + deployer: Deployer, + chainId: string, + gasPrice: BigNumberish, + chainIdHack: boolean = false +) { await publishL2SharedBridgeDependencyBytecodesOnL2(deployer, chainId, gasPrice); - await deploySharedBridgeImplOnL2ThroughL1(deployer, chainId, gasPrice); + await deploySharedBridgeImplOnL2ThroughL1(deployer, chainId, gasPrice, chainIdHack); await deploySharedBridgeProxyOnL2ThroughL1(deployer, chainId, gasPrice); await initializeChainGovernance(deployer, chainId); } @@ -189,6 +199,7 @@ async function main() { program .option("--private-key ") .option("--chain-id ") + .option("--chain-id-hack") .option("--gas-price ") .option("--nonce ") .option("--erc20-bridge ") @@ -216,7 +227,7 @@ async function main() { : (await provider.getGasPrice()).mul(GAS_MULTIPLIER); console.log(`Using gas price: ${formatUnits(gasPrice, "gwei")} gwei`); - await deploySharedBridgeOnL2ThroughL1(deployer, chainId, gasPrice); + await deploySharedBridgeOnL2ThroughL1(deployer, chainId, gasPrice, cmd.chainIdHack); }); await program.parseAsync(process.argv);