diff --git a/.github/workflows/l1-contracts-foundry-ci.yaml b/.github/workflows/l1-contracts-foundry-ci.yaml index c02688546..74fdbb8dc 100644 --- a/.github/workflows/l1-contracts-foundry-ci.yaml +++ b/.github/workflows/l1-contracts-foundry-ci.yaml @@ -103,16 +103,15 @@ jobs: working-directory: ./l1-contracts-foundry run: forge script ./script/DeployL1.s.sol --ffi --rpc-url $ANVIL_RPC_URL --broadcast --private-key $ANVIL_PRIVATE_KEY - - name: Run RegisterHyperchain script - working-directory: ./l1-contracts-foundry - run: | - cat ./script-out/output-deploy-l1.toml >> ./script-config/register-hyperchain.toml - forge script ./script/RegisterHyperchain.s.sol --ffi --rpc-url $ANVIL_RPC_URL --broadcast --private-key $ANVIL_PRIVATE_KEY - - name: Run DeployErc20 script working-directory: ./l1-contracts-foundry run: forge script ./script/DeployErc20.s.sol --ffi --rpc-url $ANVIL_RPC_URL --broadcast --private-key $ANVIL_PRIVATE_KEY - - - name: Run InitializeL2WethToken script - working-directory: ./l1-contracts-foundry - run: forge script ./script/InitializeL2WethToken.s.sol --ffi --rpc-url $ANVIL_RPC_URL --broadcast --private-key $ANVIL_PRIVATE_KEY +# TODO restore scripts verification +# - name: Run RegisterHyperchain script +# working-directory: ./l1-contracts-foundry +# run: | +# cat ./script-out/output-deploy-l1.toml >> ./script-config/register-hyperchain.toml +# forge script ./script/RegisterHyperchain.s.sol --ffi --rpc-url $ANVIL_RPC_URL --broadcast --private-key $ANVIL_PRIVATE_KEY +# - name: Run InitializeL2WethToken script +# working-directory: ./l1-contracts-foundry +# run: forge script ./script/InitializeL2WethToken.s.sol --ffi --rpc-url $ANVIL_RPC_URL --broadcast --private-key $ANVIL_PRIVATE_KEY diff --git a/l1-contracts-foundry/foundry.toml b/l1-contracts-foundry/foundry.toml index 74e27e5a7..ac978c3ba 100644 --- a/l1-contracts-foundry/foundry.toml +++ b/l1-contracts-foundry/foundry.toml @@ -10,10 +10,11 @@ remappings = [ ] allow_paths = ["../l1-contracts/contracts", "../l2-contracts/contracts"] fs_permissions = [ - { access = "read", path = "../system-contracts/bootloader/build/artifacts"}, - { access = "read", path = "../system-contracts/artifacts-zk/contracts-preprocessed"}, + { access = "read", path = "../system-contracts/bootloader/build/artifacts" }, + { access = "read", path = "../system-contracts/artifacts-zk/contracts-preprocessed" }, + { access = "read", path = "../l2-contracts/artifacts-zk/" }, { access = "read", path = "./script-config" }, { access = "read-write", path = "./script-out" }, { access = "read", path = "./out" } ] -evm_version="cancun" +evm_version = "cancun" diff --git a/l1-contracts-foundry/script-config-template/config-deploy-l1.toml b/l1-contracts-foundry/script-config-template/config-deploy-l1.toml index 28fe6a68a..e6b45719f 100644 --- a/l1-contracts-foundry/script-config-template/config-deploy-l1.toml +++ b/l1-contracts-foundry/script-config-template/config-deploy-l1.toml @@ -1,5 +1,6 @@ era_chain_id = 9 owner_address = "0x70997970C51812dc3A010C7d01b50e0d17dc79C8" +testnet_verifier = true [contracts] governance_security_council_address = "0x0000000000000000000000000000000000000000" @@ -22,6 +23,8 @@ diamond_init_max_pubdata_per_batch = 120000 diamond_init_max_l2_gas_per_batch = 80000000 diamond_init_priority_tx_max_pubdata = 99000 diamond_init_minimal_l2_gas_price = 250000000 +bootloader_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" +default_aa_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" [tokens] token_weth_address = "0x0000000000000000000000000000000000000000" diff --git a/l1-contracts-foundry/script-config-template/config-deploy-paymaster.toml b/l1-contracts-foundry/script-config-template/config-deploy-paymaster.toml new file mode 100644 index 000000000..b9eb46fbf --- /dev/null +++ b/l1-contracts-foundry/script-config-template/config-deploy-paymaster.toml @@ -0,0 +1,3 @@ +chain_id = 215 +l1_shared_bridge = "0x2ae37d8130b82c7e79b3863a39027178e073eedb" +bridgehub = "0xea785a9c91a07ed69b83eb165f4ce2c30ecb4c0b" diff --git a/l1-contracts-foundry/script-config-template/config-initialize-shared-bridges.toml b/l1-contracts-foundry/script-config-template/config-initialize-shared-bridges.toml new file mode 100644 index 000000000..67e46ae38 --- /dev/null +++ b/l1-contracts-foundry/script-config-template/config-initialize-shared-bridges.toml @@ -0,0 +1,6 @@ +chain_id = 215 +era_chain_id = 270 +l1_shared_bridge = "0x2ae37d8130b82c7e79b3863a39027178e073eedb" +bridgehub = "0xea785a9c91a07ed69b83eb165f4ce2c30ecb4c0b" +governance = "0x6a08d69675af7755569a1a25ef37e795493473a1" +erc20_bridge = "0x84fbda16bd5f2d66d7fbaec5e8d816e7b7014595" diff --git a/l1-contracts-foundry/script/AcceptAdmin.s.sol b/l1-contracts-foundry/script/AcceptAdmin.s.sol new file mode 100644 index 000000000..ffc865126 --- /dev/null +++ b/l1-contracts-foundry/script/AcceptAdmin.s.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; + +import {Script} from "forge-std/Script.sol"; +import {stdToml} from "forge-std/StdToml.sol"; + +import {Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol"; +import {Utils} from "./Utils.sol"; + +contract AcceptAdmin is Script { + using stdToml for string; + + // This function should be called by the owner to accept the admin role + function run() public { + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/script-config/config-accept-admin.toml"); + string memory toml = vm.readFile(path); + address admin = toml.readAddress("$.target_addr"); + address governor = toml.readAddress("$.governor"); + Ownable2Step adminContract = Ownable2Step(admin); + + Utils.executeUpgrade({ + _governor: governor, + _salt: bytes32(0), + _target: admin, + _data: abi.encodeCall(adminContract.acceptOwnership, ()), + _value: 0, + _delay: 0 + }); + } +} diff --git a/l1-contracts-foundry/script/DeployL1.s.sol b/l1-contracts-foundry/script/DeployL1.s.sol index 9625dbba6..1033e2d51 100644 --- a/l1-contracts-foundry/script/DeployL1.s.sol +++ b/l1-contracts-foundry/script/DeployL1.s.sol @@ -11,6 +11,7 @@ import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transpa import {Utils} from "./Utils.sol"; import {Multicall3} from "contracts/dev-contracts/Multicall3.sol"; import {Verifier} from "contracts/state-transition/Verifier.sol"; +import {TestnetVerifier} from "contracts/state-transition/TestnetVerifier.sol"; import {VerifierParams, IVerifier} from "contracts/state-transition/chain-interfaces/IVerifier.sol"; import {DefaultUpgrade} from "contracts/upgrades/DefaultUpgrade.sol"; import {Governance} from "contracts/governance/Governance.sol"; @@ -80,6 +81,7 @@ contract DeployL1Script is Script { uint256 eraChainId; address deployerAddress; address ownerAddress; + bool testnetVerifier; ContractsConfig contracts; TokensConfig tokens; } @@ -107,6 +109,8 @@ contract DeployL1Script is Script { uint256 governanceMinDelay; uint256 maxNumberOfHyperchains; bytes diamondCutData; + bytes32 bootloaderHash; + bytes32 defaultAAHash; } struct TokensConfig { @@ -162,6 +166,7 @@ contract DeployL1Script is Script { // https://book.getfoundry.sh/cheatcodes/parse-toml config.eraChainId = toml.readUint("$.era_chain_id"); config.ownerAddress = toml.readAddress("$.owner_address"); + config.testnetVerifier = toml.readBool("$.testnet_verifier"); config.contracts.governanceSecurityCouncilAddress = toml.readAddress( "$.contracts.governance_security_council_address" @@ -197,6 +202,8 @@ contract DeployL1Script is Script { "$.contracts.diamond_init_priority_tx_max_pubdata" ); config.contracts.diamondInitMinimalL2GasPrice = toml.readUint("$.contracts.diamond_init_minimal_l2_gas_price"); + config.contracts.defaultAAHash = toml.readBytes32("$.contracts.default_aa_hash"); + config.contracts.bootloaderHash = toml.readBytes32("$.contracts.bootloader_hash"); config.tokens.tokenWethAddress = toml.readAddress("$.tokens.token_weth_address"); } @@ -236,7 +243,13 @@ contract DeployL1Script is Script { } function deployVerifier() internal { - address contractAddress = deployViaCreate2(type(Verifier).creationCode); + bytes memory code; + if (config.testnetVerifier) { + code = type(TestnetVerifier).creationCode; + } else { + code = type(Verifier).creationCode; + } + address contractAddress = deployViaCreate2(code); console.log("Verifier deployed at:", contractAddress); addresses.stateTransition.verifier = contractAddress; } @@ -400,8 +413,8 @@ contract DeployL1Script is Script { DiamondInitializeDataNewChain memory initializeData = DiamondInitializeDataNewChain({ verifier: IVerifier(addresses.stateTransition.verifier), verifierParams: verifierParams, - l2BootloaderBytecodeHash: bytes32(Utils.getBatchBootloaderBytecodeHash()), - l2DefaultAccountBytecodeHash: bytes32(Utils.readSystemContractsBytecode("DefaultAccount")), + l2BootloaderBytecodeHash: config.contracts.bootloaderHash, + l2DefaultAccountBytecodeHash: config.contracts.defaultAAHash, priorityTxMaxGasLimit: config.contracts.priorityTxMaxGasLimit, feeParams: feeParams, blobVersionedHashRetriever: addresses.blobVersionedHashRetriever @@ -467,7 +480,7 @@ contract DeployL1Script is Script { Diamond.DiamondCutData memory diamondCut = Diamond.DiamondCutData({ facetCuts: facetCuts, initAddress: address(0), - initCalldata: hex"" + initCalldata: "" }); bytes memory bytecode = abi.encodePacked( type(DiamondProxy).creationCode, @@ -555,7 +568,7 @@ contract DeployL1Script is Script { validatorTimelock.transferOwnership(config.ownerAddress); Bridgehub bridgehub = Bridgehub(addresses.bridgehub.bridgehubProxy); - bridgehub.transferOwnership(config.ownerAddress); + bridgehub.transferOwnership(addresses.governance); L1SharedBridge sharedBridge = L1SharedBridge(addresses.bridges.sharedBridgeProxy); sharedBridge.transferOwnership(addresses.governance); diff --git a/l1-contracts-foundry/script/DeployPaymaster.s.sol b/l1-contracts-foundry/script/DeployPaymaster.s.sol new file mode 100644 index 000000000..52b664bc2 --- /dev/null +++ b/l1-contracts-foundry/script/DeployPaymaster.s.sol @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.24; + +import {Script} from "forge-std/Script.sol"; +import {stdToml} from "forge-std/StdToml.sol"; + +import {Utils} from "./Utils.sol"; + +contract DeployPaymaster is Script { + using stdToml for string; + Config config; + + struct Config { + address bridgehubAddress; + address l1SharedBridgeProxy; + uint256 chainId; + address paymaster; + } + + function initializeConfig() internal { + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/script-config/config-deploy-paymaster.toml"); + string memory toml = vm.readFile(path); + config.bridgehubAddress = toml.readAddress("$.bridgehub"); + config.l1SharedBridgeProxy = toml.readAddress("$.l1_shared_bridge"); + config.chainId = toml.readUint("$.chain_id"); + } + + function saveOutput() internal { + string memory toml = vm.serializeAddress("root", "paymaster", config.paymaster); + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/script-out/output-deploy-paymaster.toml"); + vm.writeToml(toml, path); + } + + function run() external { + initializeConfig(); + + deploy(); + + saveOutput(); + } + + function deploy() internal { + bytes memory testnetPaymasterBytecode = Utils.readHardhatBytecode( + "/../l2-contracts/artifacts-zk/contracts/TestnetPaymaster.sol/TestnetPaymaster.json" + ); + + config.paymaster = Utils.deployThroughL1({ + bytecode: testnetPaymasterBytecode, + constructorargs: "", + create2salt: "", + l2GasLimit: Utils.MAX_PRIORITY_TX_GAS, + factoryDeps: new bytes[](0), + bridgehubAddress: config.bridgehubAddress, + l1SharedBridgeProxy: config.l1SharedBridgeProxy, + chainId: config.chainId + }); + } +} diff --git a/l1-contracts-foundry/script/InitializeL2WethToken.s.sol b/l1-contracts-foundry/script/InitializeL2WethToken.s.sol index 792e83721..22c427a13 100644 --- a/l1-contracts-foundry/script/InitializeL2WethToken.s.sol +++ b/l1-contracts-foundry/script/InitializeL2WethToken.s.sol @@ -102,7 +102,7 @@ contract InitializeL2WethTokenScript is Script { console.log("L2 WETH token initialized"); } - function getL2Calldata() internal returns (bytes memory) { + function getL2Calldata() internal view returns (bytes memory) { // Low-level call is performed due to different solidity // compiler versions between L1 and L2 // solhint-disable-next-line func-named-parameters diff --git a/l1-contracts-foundry/script/InitializeSharedBridgeOnL2.sol b/l1-contracts-foundry/script/InitializeSharedBridgeOnL2.sol new file mode 100644 index 000000000..8ca7fce03 --- /dev/null +++ b/l1-contracts-foundry/script/InitializeSharedBridgeOnL2.sol @@ -0,0 +1,158 @@ +pragma solidity ^0.8.24; + +import {Script} from "forge-std/Script.sol"; +import {stdToml} from "forge-std/StdToml.sol"; + +import {Utils} from "./Utils.sol"; +import {L2ContractHelper} from "contracts/common/libraries/L2ContractHelper.sol"; +import {AddressAliasHelper} from "contracts/vendor/AddressAliasHelper.sol"; +import {L1SharedBridge} from "contracts/bridge/L1SharedBridge.sol"; + +contract DeployL2Script is Script { + using stdToml for string; + + Config config; + ContractsBytecodes contracts; + + struct Config { + address bridgehubAddress; + address l1SharedBridgeProxy; + address governance; + address erc20BridgeProxy; + uint256 chainId; + uint256 eraChainId; + address l2SharedBridgeImplementation; + address l2SharedBridgeProxy; + } + + struct ContractsBytecodes { + bytes l2StandardErc20FactoryBytecode; + bytes beaconProxy; + bytes l2StandardErc20Bytecode; + bytes l2SharedBridgeBytecode; + bytes l2SharedBridgeProxyBytecode; + } + + function run() public { + initializeConfig(); + loadContracts(); + + deployFactoryDeps(); + deploySharedBridge(); + deploySharedBridgeProxy(); + initializeChain(); + + saveOutput(); + } + + function loadContracts() internal { + //HACK: Meanwhile we are not integrated foundry zksync we use contracts that has been built using hardhat + contracts.l2StandardErc20FactoryBytecode = Utils.readHardhatBytecode( + "/../l2-contracts/artifacts-zk/@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol/UpgradeableBeacon.json" + ); + contracts.beaconProxy = Utils.readHardhatBytecode( + "/../l2-contracts/artifacts-zk/@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol/BeaconProxy.json" + ); + contracts.l2StandardErc20Bytecode = Utils.readHardhatBytecode( + "/../l2-contracts/artifacts-zk/contracts/bridge/L2StandardERC20.sol/L2StandardERC20.json" + ); + + contracts.l2SharedBridgeBytecode = Utils.readHardhatBytecode( + "/../l2-contracts/artifacts-zk/contracts/bridge/L2SharedBridge.sol/L2SharedBridge.json" + ); + + contracts.l2SharedBridgeProxyBytecode = Utils.readHardhatBytecode( + "/../l2-contracts/artifacts-zk/@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol/TransparentUpgradeableProxy.json" + ); + } + + function initializeConfig() internal { + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/script-config/config-initialize-shared-bridges.toml"); + string memory toml = vm.readFile(path); + config.bridgehubAddress = toml.readAddress("$.bridgehub"); + config.governance = toml.readAddress("$.governance"); + config.l1SharedBridgeProxy = toml.readAddress("$.l1_shared_bridge"); + config.erc20BridgeProxy = toml.readAddress("$.erc20_bridge"); + config.chainId = toml.readUint("$.chain_id"); + config.eraChainId = toml.readUint("$.era_chain_id"); + } + + function saveOutput() internal { + vm.serializeAddress("root", "l2_shared_bridge_implementation", config.l2SharedBridgeImplementation); + string memory toml = vm.serializeAddress("root", "l2_shared_bridge_proxy", config.l2SharedBridgeProxy); + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/script-out/output-initialize-shared-bridges.toml"); + vm.writeToml(toml, path); + } + + function deployFactoryDeps() public { + bytes[] memory factoryDeps = new bytes[](3); + factoryDeps[0] = contracts.l2StandardErc20FactoryBytecode; + factoryDeps[1] = contracts.l2StandardErc20Bytecode; + factoryDeps[2] = contracts.beaconProxy; + Utils.publishBytecodes(factoryDeps, config.chainId, config.bridgehubAddress, config.l1SharedBridgeProxy); + } + + function deploySharedBridge() public { + bytes[] memory factoryDeps = new bytes[](1); + factoryDeps[0] = contracts.beaconProxy; + + bytes memory constructorData = abi.encode(config.eraChainId); + + config.l2SharedBridgeImplementation = Utils.deployThroughL1({ + bytecode: contracts.l2SharedBridgeBytecode, + constructorargs: constructorData, + create2salt: "", + l2GasLimit: Utils.MAX_PRIORITY_TX_GAS, + factoryDeps: factoryDeps, + chainId: config.chainId, + bridgehubAddress: config.bridgehubAddress, + l1SharedBridgeProxy: config.l1SharedBridgeProxy + }); + } + + function deploySharedBridgeProxy() public { + address l2GovernorAddress = AddressAliasHelper.applyL1ToL2Alias(config.governance); + bytes32 l2StandardErc20BytecodeHash = L2ContractHelper.hashL2Bytecode(contracts.beaconProxy); + + // solhint-disable-next-line func-named-parameters + bytes memory proxyInitializationParams = abi.encodeWithSignature( + "initialize(address,address,bytes32,address)", + config.l1SharedBridgeProxy, + config.erc20BridgeProxy, + l2StandardErc20BytecodeHash, + l2GovernorAddress + ); + + bytes memory l2SharedBridgeProxyConstructorData = abi.encode( + config.l2SharedBridgeImplementation, + l2GovernorAddress, + proxyInitializationParams + ); + + config.l2SharedBridgeProxy = Utils.deployThroughL1({ + bytecode: contracts.l2SharedBridgeProxyBytecode, + constructorargs: l2SharedBridgeProxyConstructorData, + create2salt: "", + l2GasLimit: Utils.MAX_PRIORITY_TX_GAS, + factoryDeps: new bytes[](0), + chainId: config.chainId, + bridgehubAddress: config.bridgehubAddress, + l1SharedBridgeProxy: config.l1SharedBridgeProxy + }); + } + + function initializeChain() public { + L1SharedBridge bridge = L1SharedBridge(config.l1SharedBridgeProxy); + + Utils.executeUpgrade({ + _governor: bridge.owner(), + _salt: bytes32(0), + _target: config.l1SharedBridgeProxy, + _data: abi.encodeCall(bridge.initializeChainGovernance, (config.chainId, config.l2SharedBridgeProxy)), + _value: 0, + _delay: 0 + }); + } +} diff --git a/l1-contracts-foundry/script/RegisterHyperchain.s.sol b/l1-contracts-foundry/script/RegisterHyperchain.s.sol index 7f1a69026..4b499cf0a 100644 --- a/l1-contracts-foundry/script/RegisterHyperchain.s.sol +++ b/l1-contracts-foundry/script/RegisterHyperchain.s.sol @@ -7,10 +7,13 @@ import {Script, console2 as console} from "forge-std/Script.sol"; import {Vm} from "forge-std/Vm.sol"; import {stdToml} from "forge-std/StdToml.sol"; +import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; import {IBridgehub} from "contracts/bridgehub/IBridgehub.sol"; import {IZkSyncHyperchain} from "contracts/state-transition/chain-interfaces/IZkSyncHyperchain.sol"; import {ValidatorTimelock} from "contracts/state-transition/ValidatorTimelock.sol"; import {Governance} from "contracts/governance/Governance.sol"; +import {Utils} from "./Utils.sol"; +import {PubdataPricingMode} from "contracts/state-transition/chain-deps/ZkSyncHyperchainStorage.sol"; contract RegisterHyperchainScript is Script { using stdToml for string; @@ -57,18 +60,6 @@ contract RegisterHyperchainScript is Script { saveOutput(); } - // This function should be called by the owner to accept the admin role - function acceptAdmin() public { - console.log("Accept admin Hyperchain"); - string memory root = vm.projectRoot(); - string memory path = string.concat(root, "/script-config/accept-admin.toml"); - string memory toml = vm.readFile(path); - address diamondProxy = toml.readAddress("$.diamond_proxy_addr"); - IZkSyncHyperchain zkSyncStateTransition = IZkSyncHyperchain(diamondProxy); - vm.broadcast(); - zkSyncStateTransition.acceptAdmin(); - } - function initializeConfig() internal { // Grab config from output of l1 deployment string memory root = vm.projectRoot(); @@ -125,12 +116,20 @@ contract RegisterHyperchainScript is Script { function registerTokenOnBridgehub() internal { IBridgehub bridgehub = IBridgehub(config.bridgehub); + Ownable ownable = Ownable(config.bridgehub); if (bridgehub.tokenIsRegistered(config.baseToken)) { console.log("Token already registered on Bridgehub"); } else { - vm.broadcast(); - bridgehub.addToken(config.baseToken); + bytes memory data = abi.encodeCall(bridgehub.addToken, (config.baseToken)); + Utils.executeUpgrade({ + _governor: ownable.owner(), + _salt: bytes32(config.bridgehubCreateNewChainSalt), + _target: config.bridgehub, + _data: data, + _value: 0, + _delay: 0 + }); console.log("Token registered on Bridgehub"); } } @@ -147,16 +146,28 @@ contract RegisterHyperchainScript is Script { function registerHyperchain() internal { IBridgehub bridgehub = IBridgehub(config.bridgehub); + Ownable ownable = Ownable(config.bridgehub); - vm.broadcast(); vm.recordLogs(); - bridgehub.createNewChain({ - _chainId: config.hyperchainChainId, - _stateTransitionManager: config.stateTransitionProxy, - _baseToken: config.baseToken, - _salt: config.bridgehubCreateNewChainSalt, - _admin: msg.sender, - _initData: config.diamondCutData + bytes memory data = abi.encodeCall( + bridgehub.createNewChain, + ( + config.hyperchainChainId, + config.stateTransitionProxy, + config.baseToken, + config.bridgehubCreateNewChainSalt, + msg.sender, + config.diamondCutData + ) + ); + + Utils.executeUpgrade({ + _governor: ownable.owner(), + _salt: bytes32(config.bridgehubCreateNewChainSalt), + _target: config.bridgehub, + _data: data, + _value: 0, + _delay: 0 }); console.log("Hyperchain registered"); @@ -196,10 +207,9 @@ contract RegisterHyperchainScript is Script { config.baseTokenGasPriceMultiplierDenominator ); - // TODO: support validium mode when available - // if (config.contractsMode) { - // zkSyncStateTransition.setValidiumMode(PubdataPricingMode.Validium); - // } + if (config.validiumMode) { + hyperchain.setPubdataPricingMode(PubdataPricingMode.Validium); + } vm.stopBroadcast(); console.log("ZkSync State Transition configured"); diff --git a/l1-contracts-foundry/script/Utils.sol b/l1-contracts-foundry/script/Utils.sol index b9a66426f..2cea861aa 100644 --- a/l1-contracts-foundry/script/Utils.sol +++ b/l1-contracts-foundry/script/Utils.sol @@ -3,15 +3,25 @@ pragma solidity 0.8.24; import {Vm} from "forge-std/Vm.sol"; +import {Bridgehub} from "contracts/bridgehub/Bridgehub.sol"; +import {L2TransactionRequestDirect} from "contracts/bridgehub/IBridgehub.sol"; +import {IGovernance} from "contracts/governance/IGovernance.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {REQUIRED_L2_GAS_PRICE_PER_PUBDATA} from "contracts/common/Config.sol"; +import {L2_DEPLOYER_SYSTEM_CONTRACT_ADDR} from "contracts/common/L2ContractAddresses.sol"; +import {L2ContractHelper} from "contracts/common/libraries/L2ContractHelper.sol"; + library Utils { // Cheatcodes address, 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D. address internal constant VM_ADDRESS = address(uint160(uint256(keccak256("hevm cheat code")))); + Vm internal constant vm = Vm(VM_ADDRESS); // Create2Factory deterministic bytecode. // https://github.com/Arachnid/deterministic-deployment-proxy bytes internal constant CREATE2_FACTORY_BYTECODE = hex"604580600e600039806000f350fe7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf3"; - Vm internal constant vm = Vm(VM_ADDRESS); + address constant ADDRESS_ONE = 0x0000000000000000000000000000000000000001; + uint256 constant MAX_PRIORITY_TX_GAS = 72000000; /** * @dev Get all selectors from the bytecode. @@ -144,7 +154,7 @@ library Utils { vm.broadcast(); (bool success, bytes memory data) = _factory.call(abi.encodePacked(_salt, _bytecode)); - contractAddress = Utils.bytesToAddress(data); + contractAddress = bytesToAddress(data); if (!success || contractAddress == address(0) || contractAddress.code.length == 0) { revert("Failed to deploy contract via create2"); @@ -152,4 +162,162 @@ library Utils { return contractAddress; } + + /** + * @dev Deploy l2 contracts through l1 + */ + function deployThroughL1( + bytes memory bytecode, + bytes memory constructorargs, + bytes32 create2salt, + uint256 l2GasLimit, + bytes[] memory factoryDeps, + uint256 chainId, + address bridgehubAddress, + address l1SharedBridgeProxy + ) public returns (address) { + bytes32 bytecodeHash = L2ContractHelper.hashL2Bytecode(bytecode); + + bytes memory deployData = abi.encodeWithSignature( + "create2(bytes32,bytes32,bytes)", + create2salt, + bytecodeHash, + constructorargs + ); + + address contractAddress = L2ContractHelper.computeCreate2Address( + msg.sender, + create2salt, + bytecodeHash, + keccak256(constructorargs) + ); + + bytes[] memory _factoryDeps = new bytes[](factoryDeps.length + 1); + + for (uint256 i = 0; i < factoryDeps.length; i++) { + _factoryDeps[i] = factoryDeps[i]; + } + _factoryDeps[factoryDeps.length] = bytecode; + + runL1L2Transaction({ + l2Calldata: deployData, + l2GasLimit: l2GasLimit, + factoryDeps: _factoryDeps, + dstAddress: L2_DEPLOYER_SYSTEM_CONTRACT_ADDR, + chainId: chainId, + bridgehubAddress: bridgehubAddress, + l1SharedBridgeProxy: l1SharedBridgeProxy + }); + return contractAddress; + } + + /** + * @dev Run the l2 l1 transaction + */ + function runL1L2Transaction( + bytes memory l2Calldata, + uint256 l2GasLimit, + bytes[] memory factoryDeps, + address dstAddress, + uint256 chainId, + address bridgehubAddress, + address l1SharedBridgeProxy + ) public { + Bridgehub bridgehub = Bridgehub(bridgehubAddress); + uint256 gasPrice = bytesToUint256(vm.rpc("eth_gasPrice", "[]")); + + uint256 requiredValueToDeploy = bridgehub.l2TransactionBaseCost( + chainId, + gasPrice, + l2GasLimit, + REQUIRED_L2_GAS_PRICE_PER_PUBDATA + ) * 2; + + L2TransactionRequestDirect memory l2TransactionRequestDirect = L2TransactionRequestDirect({ + chainId: chainId, + mintValue: requiredValueToDeploy, + l2Contract: dstAddress, + l2Value: 0, + l2Calldata: l2Calldata, + l2GasLimit: l2GasLimit, + l2GasPerPubdataByteLimit: REQUIRED_L2_GAS_PRICE_PER_PUBDATA, + factoryDeps: factoryDeps, + refundRecipient: msg.sender + }); + + address baseTokenAddress = bridgehub.baseToken(chainId); + if (ADDRESS_ONE != baseTokenAddress) { + IERC20 baseToken = IERC20(baseTokenAddress); + vm.broadcast(); + baseToken.approve(l1SharedBridgeProxy, requiredValueToDeploy); + requiredValueToDeploy = 0; + } + + executeUpgrade({ + _governor: bridgehub.owner(), + _salt: bytes32(0), + _target: l1SharedBridgeProxy, + _data: abi.encodeCall(bridgehub.requestL2TransactionDirect, (l2TransactionRequestDirect)), + _value: requiredValueToDeploy, + _delay: 0 + }); + } + + /** + * @dev Publish bytecodes to l2 through l1 + */ + function publishBytecodes( + bytes[] memory factoryDeps, + uint256 chainId, + address bridgehubAddress, + address l1SharedBridgeProxy + ) public { + runL1L2Transaction({ + l2Calldata: "", + l2GasLimit: MAX_PRIORITY_TX_GAS, + factoryDeps: factoryDeps, + dstAddress: 0x0000000000000000000000000000000000000000, + chainId: chainId, + bridgehubAddress: bridgehubAddress, + l1SharedBridgeProxy: l1SharedBridgeProxy + }); + } + + /** + * @dev Read hardhat bytecodes + */ + function readHardhatBytecode(string memory artifactPath) public view returns (bytes memory) { + string memory root = vm.projectRoot(); + string memory path = string.concat(root, artifactPath); + string memory json = vm.readFile(path); + bytes memory bytecode = vm.parseJsonBytes(json, ".bytecode"); + return bytecode; + } + + function executeUpgrade( + address _governor, + bytes32 _salt, + address _target, + bytes memory _data, + uint256 _value, + uint256 _delay + ) public { + IGovernance governance = IGovernance(_governor); + + IGovernance.Call[] memory calls = new IGovernance.Call[](1); + calls[0] = IGovernance.Call({target: _target, value: _value, data: _data}); + + IGovernance.Operation memory operation = IGovernance.Operation({ + calls: calls, + predecessor: bytes32(0), + salt: _salt + }); + + vm.startBroadcast(); + governance.scheduleTransparent(operation, _delay); + if (_delay == 0) { + governance.execute{value: _value}(operation); + } + vm.stopBroadcast(); + } }