diff --git a/.github/workflows/l1-contracts-ci.yaml b/.github/workflows/l1-contracts-ci.yaml index fe3c28379..964efaea8 100644 --- a/.github/workflows/l1-contracts-ci.yaml +++ b/.github/workflows/l1-contracts-ci.yaml @@ -22,6 +22,9 @@ jobs: node-version: 18.18.0 cache: yarn + - name: Use Foundry + uses: foundry-rs/foundry-toolchain@v1 + - name: Install dependencies run: yarn @@ -39,11 +42,15 @@ jobs: - name: Build l1 artifacts run: yarn l1 build + - name: Build da-contracts artifacts + run: yarn da build:foundry + - name: Create cache uses: actions/cache/save@v3 with: key: artifacts-l1-${{ github.sha }} path: | + da-contracts/out l1-contracts/artifacts l1-contracts/artifacts-zk l1-contracts/cache @@ -51,7 +58,6 @@ jobs: l2-contracts/artifacts-zk l2-contracts/cache-zk l2-contracts/typechain - l1-contracts/lib lint: runs-on: ubuntu-latest @@ -100,6 +106,7 @@ jobs: fail-on-cache-miss: true key: artifacts-l1-${{ github.sha }} path: | + da-contracts/out l1-contracts/artifacts l1-contracts/artifacts-zk l1-contracts/cache @@ -107,7 +114,6 @@ jobs: l2-contracts/artifacts-zk l2-contracts/cache-zk l2-contracts/typechain - l1-contracts/lib - name: Run tests working-directory: ./l1-contracts @@ -141,6 +147,7 @@ jobs: fail-on-cache-miss: true key: artifacts-l1-${{ github.sha }} path: | + da-contracts/out l1-contracts/artifacts l1-contracts/artifacts-zk l1-contracts/cache @@ -148,7 +155,6 @@ jobs: l2-contracts/artifacts-zk l2-contracts/cache-zk l2-contracts/typechain - l1-contracts/lib - name: Install foundry zksync run: | @@ -191,6 +197,7 @@ jobs: fail-on-cache-miss: true key: artifacts-l1-${{ github.sha }} path: | + da-contracts/out l1-contracts/artifacts l1-contracts/artifacts-zk l1-contracts/cache @@ -198,7 +205,6 @@ jobs: l2-contracts/artifacts-zk l2-contracts/cache-zk l2-contracts/typechain - l1-contracts/lib - name: Build L2 contracts run: yarn l2 build @@ -258,6 +264,7 @@ jobs: fail-on-cache-miss: true key: artifacts-l1-${{ github.sha }} path: | + da-contracts/out l1-contracts/artifacts l1-contracts/artifacts-zk l1-contracts/cache @@ -265,7 +272,6 @@ jobs: l2-contracts/artifacts-zk l2-contracts/cache-zk l2-contracts/typechain - l1-contracts/lib - name: Run coverage run: FOUNDRY_PROFILE=default yarn test:foundry && FOUNDRY_PROFILE=default yarn coverage:foundry --report summary --report lcov @@ -280,7 +286,7 @@ jobs: - name: Filter directories run: | sudo apt update && sudo apt install -y lcov - lcov --remove lcov.info 'test/*' 'contracts/dev-contracts/*' '../lib/forge-std/*' '../lib/murky/*' 'lib/*' '../lib/*' 'lib/' --output-file lcov.info --rc lcov_branch_coverage=1 + lcov --remove lcov.info 'test/*' 'contracts/dev-contracts/*' '../lib/forge-std/*' '../lib/murky/*' 'lib/*' '../lib/*' 'lib/' 'deploy-scripts/*' --output-file lcov.info --rc lcov_branch_coverage=1 # This step posts a detailed coverage report as a comment and deletes previous comments on # each push. The below step is used to fail coverage if the specified coverage threshold is diff --git a/.github/workflows/l1-contracts-foundry-ci.yaml b/.github/workflows/l1-contracts-foundry-ci.yaml index 5205d092c..4d90553c9 100644 --- a/.github/workflows/l1-contracts-foundry-ci.yaml +++ b/.github/workflows/l1-contracts-foundry-ci.yaml @@ -41,11 +41,15 @@ jobs: - name: Build l2 artifacts run: yarn l2 build + - name: Build da-contracts artifacts + run: yarn da build:foundry + - name: Create cache uses: actions/cache/save@v3 with: key: artifacts-l1-contracts-foudry-${{ github.sha }} path: | + da-contracts/out l1-contracts/cache l1-contracts/out l1-contracts/artifacts-zk @@ -72,6 +76,7 @@ jobs: fail-on-cache-miss: true key: artifacts-l1-contracts-foudry-${{ github.sha }} path: | + da-contracts/out l1-contracts/cache l1-contracts/out l1-contracts/artifacts-zk diff --git a/.gitignore b/.gitignore index 780fb4e9d..a2a7a4d05 100644 --- a/.gitignore +++ b/.gitignore @@ -31,4 +31,6 @@ l1-contracts/test/foundry/l1/integration/deploy-scripts/script-out/*.toml !l1-contracts/script-out/.gitkeep *.timestamp l1-contracts/test/foundry/l1/integration/deploy-scripts/script-out/* +l1-contracts/test/foundry/l1/integration/deploy-scripts/script-config/config-deploy-zk-chain-*.toml +l1-contracts/test/foundry/integration/deploy-scripts/script-out/* l1-contracts/zkout/* diff --git a/da-contracts/package.json b/da-contracts/package.json index 1f68dcdab..ec4ca3b45 100644 --- a/da-contracts/package.json +++ b/da-contracts/package.json @@ -54,7 +54,7 @@ }, "scripts": { "build": "hardhat compile ", - "build-l1": "harhdat compile", + "build:foundry": "forge build", "clean": "hardhat clean", "clean:foundry": "forge clean", "verify": "hardhat run --network env scripts/verify.ts" diff --git a/l1-contracts/contracts/bridge/L2SharedBridgeLegacy.sol b/l1-contracts/contracts/bridge/L2SharedBridgeLegacy.sol index 61e6141c2..9ea29cf07 100644 --- a/l1-contracts/contracts/bridge/L2SharedBridgeLegacy.sol +++ b/l1-contracts/contracts/bridge/L2SharedBridgeLegacy.sol @@ -22,10 +22,6 @@ import {ZeroAddress, EmptyBytes32, Unauthorized, AmountMustBeGreaterThanZero, De /// @notice The "default" bridge implementation for the ERC20 tokens. Note, that it does not /// support any custom token logic, i.e. rebase tokens' functionality is not supported. contract L2SharedBridgeLegacy is IL2SharedBridgeLegacy, Initializable { - /// @dev Contract is expected to be used as proxy implementation. - /// @dev Disable the initialization to prevent Parity hack. - uint256 public immutable ERA_CHAIN_ID; - /// @dev The address of the L1 shared bridge counterpart. address public override l1SharedBridge; @@ -57,19 +53,16 @@ contract L2SharedBridgeLegacy is IL2SharedBridgeLegacy, Initializable { _; } - constructor(uint256 _eraChainId) { - ERA_CHAIN_ID = _eraChainId; + constructor() { _disableInitializers(); } /// @notice Initializes the bridge contract for later use. Expected to be used in the proxy. /// @param _l1SharedBridge The address of the L1 Bridge contract. - /// @param _l1Bridge The address of the legacy L1 Bridge contract. /// @param _l2TokenProxyBytecodeHash The bytecode hash of the proxy for tokens deployed by the bridge. /// @param _aliasedOwner The address of the governor contract. function initialize( address _l1SharedBridge, - address _l1Bridge, bytes32 _l2TokenProxyBytecodeHash, address _aliasedOwner ) external reinitializer(2) { @@ -87,17 +80,14 @@ contract L2SharedBridgeLegacy is IL2SharedBridgeLegacy, Initializable { l1SharedBridge = _l1SharedBridge; - if (block.chainid != ERA_CHAIN_ID) { + // The following statement is true only in freshly deployed environments. However, + // for those environments we do not need to deploy this contract at all. + // This check is primarily for local testing purposes. + if (l2TokenProxyBytecodeHash == bytes32(0) && address(l2TokenBeacon) == address(0)) { address l2StandardToken = address(new BridgedStandardERC20{salt: bytes32(0)}()); l2TokenBeacon = new UpgradeableBeacon{salt: bytes32(0)}(l2StandardToken); l2TokenProxyBytecodeHash = _l2TokenProxyBytecodeHash; l2TokenBeacon.transferOwnership(_aliasedOwner); - } else { - if (_l1Bridge == address(0)) { - revert ZeroAddress(); - } - l1Bridge = _l1Bridge; - // l2StandardToken and l2TokenBeacon are already deployed on ERA, and stored in the proxy } } diff --git a/l1-contracts/contracts/bridgehub/CTMDeploymentTracker.sol b/l1-contracts/contracts/bridgehub/CTMDeploymentTracker.sol index 6ffee2482..6a7717ed5 100644 --- a/l1-contracts/contracts/bridgehub/CTMDeploymentTracker.sol +++ b/l1-contracts/contracts/bridgehub/CTMDeploymentTracker.sol @@ -15,6 +15,9 @@ import {ReentrancyGuard} from "../common/ReentrancyGuard.sol"; import {TWO_BRIDGES_MAGIC_VALUE} from "../common/Config.sol"; import {L2_BRIDGEHUB_ADDR} from "../common/L2ContractAddresses.sol"; +/// @dev The encoding version of the data. +bytes1 constant CTM_DEPLOYMENT_TRACKER_ENCODING_VERSION = 0x01; + /// @author Matter Labs /// @custom:security-contact security@matterlabs.dev /// @dev Contract to be deployed on L1, can link together other contracts based on AssetInfo. @@ -25,9 +28,6 @@ contract CTMDeploymentTracker is ICTMDeploymentTracker, ReentrancyGuard, Ownable /// @dev Bridgehub smart contract that is used to operate with L2 via asynchronous L2 <-> L1 communication. IAssetRouterBase public immutable override L1_ASSET_ROUTER; - /// @dev The encoding version of the data. - bytes1 internal constant ENCODING_VERSION = 0x01; - /// @notice Checks that the message sender is the bridgehub. modifier onlyBridgehub() { // solhint-disable-next-line gas-custom-errors @@ -93,7 +93,7 @@ contract CTMDeploymentTracker is ICTMDeploymentTracker, ReentrancyGuard, Ownable require(_originalCaller == owner(), "CTMDT: not owner"); bytes1 encodingVersion = _data[0]; - require(encodingVersion == ENCODING_VERSION, "CTMDT: wrong encoding version"); + require(encodingVersion == CTM_DEPLOYMENT_TRACKER_ENCODING_VERSION, "CTMDT: wrong encoding version"); (address _ctmL1Address, address _ctmL2Address) = abi.decode(_data[1:], (address, address)); request = _registerCTMAssetOnL2Bridgehub(_chainId, _ctmL1Address, _ctmL2Address); diff --git a/l1-contracts/contracts/bridgehub/IBridgehub.sol b/l1-contracts/contracts/bridgehub/IBridgehub.sol index 53adf37b8..5e2be03fc 100644 --- a/l1-contracts/contracts/bridgehub/IBridgehub.sol +++ b/l1-contracts/contracts/bridgehub/IBridgehub.sol @@ -197,6 +197,8 @@ interface IBridgehub is IAssetHandler, IL1AssetHandler { function registerSettlementLayer(uint256 _newSettlementLayerChainId, bool _isWhitelisted) external; + function settlementLayer(uint256 _chainId) external view returns (uint256); + // function finalizeMigrationToGateway( // uint256 _chainId, // address _baseToken, diff --git a/l1-contracts/contracts/governance/IChainAdmin.sol b/l1-contracts/contracts/governance/IChainAdmin.sol index 1ef3144c2..6dba4dfa8 100644 --- a/l1-contracts/contracts/governance/IChainAdmin.sol +++ b/l1-contracts/contracts/governance/IChainAdmin.sol @@ -35,4 +35,13 @@ interface IChainAdmin { /// @param _restriction The address of the restriction contract. /// @dev Sometimes restrictions might need to enforce their permanence (e.g. if a chain should be a rollup forever). function removeRestriction(address _restriction) external; + + /// @notice Execute multiple calls as part of contract administration. + /// @param _calls Array of Call structures defining target, value, and data for each call. + /// @param _requireSuccess If true, reverts transaction on any call failure. + /// @dev Intended for batch processing of contract interactions, managing gas efficiency and atomicity of operations. + /// @dev Note, that this function lacks access control. It is expected that the access control is implemented in a separate restriction contract. + /// @dev Even though all the validation from external modules is executed via `staticcall`, the function + /// is marked as `nonReentrant` to prevent reentrancy attacks in case the staticcall restriction is lifted in the future. + function multicall(Call[] calldata _calls, bool _requireSuccess) external payable; } diff --git a/l1-contracts/contracts/transactionFilterer/GatewayTransactionFilterer.sol b/l1-contracts/contracts/transactionFilterer/GatewayTransactionFilterer.sol index 81556f221..ebcb37ff8 100644 --- a/l1-contracts/contracts/transactionFilterer/GatewayTransactionFilterer.sol +++ b/l1-contracts/contracts/transactionFilterer/GatewayTransactionFilterer.sol @@ -10,6 +10,7 @@ import {ITransactionFilterer} from "../state-transition/chain-interfaces/ITransa import {IBridgehub} from "../bridgehub/IBridgehub.sol"; import {IL2Bridge} from "../bridge/interfaces/IL2Bridge.sol"; import {IAssetRouterBase} from "../bridge/asset-router/IAssetRouterBase.sol"; +import {IL2AssetRouter} from "../bridge/asset-router/IL2AssetRouter.sol"; /// @author Matter Labs /// @custom:security-contact security@matterlabs.dev @@ -82,18 +83,28 @@ contract GatewayTransactionFilterer is ITransactionFilterer, ReentrancyGuard, Ow ) external view returns (bool) { if (sender == L1_ASSET_ROUTER) { bytes4 l2TxSelector = bytes4(l2Calldata[:4]); + + if (IL2AssetRouter.setAssetHandlerAddress.selector == l2TxSelector) { + (, bytes32 decodedAssetId, ) = abi.decode(l2Calldata[4:], (uint256, bytes32, address)); + return _checkSTMAssetId(decodedAssetId); + } + if ( - (IAssetRouterBase.finalizeDeposit.selector != l2TxSelector) && - (IL2Bridge.finalizeDeposit.selector != l2TxSelector) + IAssetRouterBase.finalizeDeposit.selector != l2TxSelector && + IL2Bridge.finalizeDeposit.selector != l2TxSelector ) { revert InvalidSelector(l2TxSelector); } (, bytes32 decodedAssetId, ) = abi.decode(l2Calldata[4:], (uint256, bytes32, bytes)); - address stmAddress = BRIDGE_HUB.ctmAssetIdToAddress(decodedAssetId); - return (stmAddress != address(0)); + return _checkSTMAssetId(decodedAssetId); } return whitelistedSenders[sender]; } + + function _checkSTMAssetId(bytes32 assetId) internal view returns (bool) { + address stmAddress = BRIDGE_HUB.ctmAssetIdToAddress(assetId); + return stmAddress != address(0); + } } diff --git a/l1-contracts/deploy-scripts/AcceptAdmin.s.sol b/l1-contracts/deploy-scripts/AcceptAdmin.s.sol index 2eff6dc07..690832a53 100644 --- a/l1-contracts/deploy-scripts/AcceptAdmin.s.sol +++ b/l1-contracts/deploy-scripts/AcceptAdmin.s.sol @@ -5,11 +5,16 @@ import {Script} from "forge-std/Script.sol"; import {Ownable2Step} from "@openzeppelin/contracts-v4/access/Ownable2Step.sol"; import {IZKChain} from "contracts/state-transition/chain-interfaces/IZKChain.sol"; +import {IAdmin} from "contracts/state-transition/chain-interfaces/IAdmin.sol"; import {ChainAdmin} from "contracts/governance/ChainAdmin.sol"; +import {AccessControlRestriction} from "contracts/governance/AccessControlRestriction.sol"; import {IChainAdmin} from "contracts/governance/IChainAdmin.sol"; +import {Call} from "contracts/governance/Common.sol"; import {Utils} from "./Utils.sol"; import {stdToml} from "forge-std/StdToml.sol"; +bytes32 constant SET_TOKEN_MULTIPLIER_SETTER_ROLE = keccak256("SET_TOKEN_MULTIPLIER_SETTER_ROLE"); + contract AcceptAdmin is Script { using stdToml for string; @@ -58,8 +63,8 @@ contract AcceptAdmin is Script { function chainAdminAcceptAdmin(ChainAdmin chainAdmin, address target) public { IZKChain adminContract = IZKChain(target); - IChainAdmin.Call[] memory calls = new IChainAdmin.Call[](1); - calls[0] = IChainAdmin.Call({target: target, value: 0, data: abi.encodeCall(adminContract.acceptAdmin, ())}); + Call[] memory calls = new Call[](1); + calls[0] = Call({target: target, value: 0, data: abi.encodeCall(adminContract.acceptAdmin, ())}); vm.startBroadcast(); chainAdmin.multicall(calls, true); @@ -67,11 +72,50 @@ contract AcceptAdmin is Script { } // This function should be called by the owner to update token multiplier setter role - function chainSetTokenMultiplierSetter(address chainAdmin, address target) public { - IChainAdmin admin = IChainAdmin(chainAdmin); + function chainSetTokenMultiplierSetter( + address accessControlRestriction, + address diamondProxyAddress, + address setter + ) public { + AccessControlRestriction restriction = AccessControlRestriction(accessControlRestriction); + + if ( + restriction.requiredRoles(diamondProxyAddress, IAdmin.setTokenMultiplier.selector) != + SET_TOKEN_MULTIPLIER_SETTER_ROLE + ) { + vm.startBroadcast(); + restriction.setRequiredRoleForCall( + diamondProxyAddress, + IAdmin.setTokenMultiplier.selector, + SET_TOKEN_MULTIPLIER_SETTER_ROLE + ); + vm.stopBroadcast(); + } + + if (!restriction.hasRole(SET_TOKEN_MULTIPLIER_SETTER_ROLE, setter)) { + vm.startBroadcast(); + restriction.grantRole(SET_TOKEN_MULTIPLIER_SETTER_ROLE, setter); + vm.stopBroadcast(); + } + } + + function setDAValidatorPair( + ChainAdmin chainAdmin, + address target, + address l1DaValidator, + address l2DaValidator + ) public { + IZKChain adminContract = IZKChain(target); + + Call[] memory calls = new Call[](1); + calls[0] = Call({ + target: target, + value: 0, + data: abi.encodeCall(adminContract.setDAValidatorPair, (l1DaValidator, l2DaValidator)) + }); vm.startBroadcast(); - admin.setTokenMultiplierSetter(target); + chainAdmin.multicall(calls, true); vm.stopBroadcast(); } } diff --git a/l1-contracts/deploy-scripts/DeployL1.s.sol b/l1-contracts/deploy-scripts/DeployL1.s.sol index 20ec3cbc7..0f86201dd 100644 --- a/l1-contracts/deploy-scripts/DeployL1.s.sol +++ b/l1-contracts/deploy-scripts/DeployL1.s.sol @@ -1,14 +1,14 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.24; -// solhint-disable no-console +// solhint-disable no-console, gas-custom-errors import {Script, console2 as console} from "forge-std/Script.sol"; import {stdToml} from "forge-std/StdToml.sol"; import {ProxyAdmin} from "@openzeppelin/contracts-v4/proxy/transparent/ProxyAdmin.sol"; import {TransparentUpgradeableProxy} from "@openzeppelin/contracts-v4/proxy/transparent/TransparentUpgradeableProxy.sol"; import {UpgradeableBeacon} from "@openzeppelin/contracts-v4/proxy/beacon/UpgradeableBeacon.sol"; -import {Utils} from "./Utils.sol"; +import {StateTransitionDeployedAddresses, Utils, DAContractBytecodes, L2_BRIDGEHUB_ADDRESS, L2_ASSET_ROUTER_ADDRESS, L2_NATIVE_TOKEN_VAULT_ADDRESS, L2_MESSAGE_ROOT_ADDRESS} 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"; @@ -41,12 +41,32 @@ import {IL1AssetRouter} from "contracts/bridge/asset-router/IL1AssetRouter.sol"; import {INativeTokenVault} from "contracts/bridge/ntv/INativeTokenVault.sol"; import {BridgedStandardERC20} from "contracts/bridge/BridgedStandardERC20.sol"; import {AddressHasNoCode} from "./ZkSyncScriptErrors.sol"; +import {ICTMDeploymentTracker} from "contracts/bridgehub/ICTMDeploymentTracker.sol"; +import {IMessageRoot} from "contracts/bridgehub/IMessageRoot.sol"; +import {IL2ContractDeployer} from "contracts/common/interfaces/IL2ContractDeployer.sol"; +import {L2ContractHelper} from "contracts/common/libraries/L2ContractHelper.sol"; +import {AddressAliasHelper} from "contracts/vendor/AddressAliasHelper.sol"; import {IL1Nullifier} from "contracts/bridge/L1Nullifier.sol"; import {IL1NativeTokenVault} from "contracts/bridge/ntv/IL1NativeTokenVault.sol"; - +import {L1NullifierDev} from "contracts/dev-contracts/L1NullifierDev.sol"; +import {AccessControlRestriction} from "contracts/governance/AccessControlRestriction.sol"; import {ICTMDeploymentTracker} from "contracts/bridgehub/ICTMDeploymentTracker.sol"; import {IMessageRoot} from "contracts/bridgehub/IMessageRoot.sol"; import {IAssetRouterBase} from "contracts/bridge/asset-router/IAssetRouterBase.sol"; +import {L2ContractsBytecodesLib} from "./L2ContractsBytecodesLib.sol"; + +struct FixedForceDeploymentsData { + uint256 l1ChainId; + uint256 eraChainId; + address l1AssetRouter; + bytes32 l2TokenProxyBytecodeHash; + address aliasedL1Governance; + uint256 maxNumberOfZKChains; + bytes32 bridgehubBytecodeHash; + bytes32 l2AssetRouterBytecodeHash; + bytes32 l2NtvBytecodeHash; + bytes32 messageRootBytecodeHash; +} contract DeployL1Script is Script { using stdToml for string; @@ -60,9 +80,11 @@ contract DeployL1Script is Script { StateTransitionDeployedAddresses stateTransition; BridgesDeployedAddresses bridges; L1NativeTokenVaultAddresses vaults; + DataAvailabilityDeployedAddresses daAddresses; address transparentProxyAdmin; address governance; address chainAdmin; + address accessControlRestrictionAddress; address blobVersionedHashRetriever; address validatorTimelock; address create2Factory; @@ -74,6 +96,11 @@ contract DeployL1Script is Script { address l1NativeTokenVaultProxy; } + struct DataAvailabilityDeployedAddresses { + address l1RollupDAValidator; + address l1ValidiumDAValidator; + } + // solhint-disable-next-line gas-struct-packing struct BridgehubDeployedAddresses { address bridgehubImplementation; @@ -84,21 +111,6 @@ contract DeployL1Script is Script { address messageRootProxy; } - // solhint-disable-next-line gas-struct-packing - struct StateTransitionDeployedAddresses { - address stateTransitionProxy; - address stateTransitionImplementation; - address verifier; - address adminFacet; - address mailboxFacet; - address executorFacet; - address gettersFacet; - address diamondInit; - address genesisUpgrade; - address defaultUpgrade; - address diamondProxy; - } - // solhint-disable-next-line gas-struct-packing struct BridgesDeployedAddresses { address erc20BridgeImplementation; @@ -114,14 +126,19 @@ contract DeployL1Script is Script { // solhint-disable-next-line gas-struct-packing struct Config { uint256 l1ChainId; - uint256 eraChainId; address deployerAddress; + uint256 eraChainId; address ownerAddress; bool testnetVerifier; ContractsConfig contracts; TokensConfig tokens; } + // solhint-disable-next-line gas-struct-packing + struct GeneratedData { + bytes forceDeploymentsData; + } + // solhint-disable-next-line gas-struct-packing struct ContractsConfig { bytes32 create2FactorySalt; @@ -148,7 +165,6 @@ contract DeployL1Script is Script { bytes diamondCutData; bytes32 bootloaderHash; bytes32 defaultAAHash; - bytes forceDeploymentsData; } struct TokensConfig { @@ -156,12 +172,25 @@ contract DeployL1Script is Script { } Config internal config; + GeneratedData internal generatedData; DeployedAddresses internal addresses; function run() public { console.log("Deploying L1 contracts"); - initializeConfig(); + runInner("/script-config/config-deploy-l1.toml", "/script-out/output-deploy-l1.toml"); + } + + function runForTest() public { + runInner(vm.envString("L1_CONFIG"), vm.envString("L1_OUTPUT")); + } + + function runInner(string memory inputPath, string memory outputPath) internal { + string memory root = vm.projectRoot(); + inputPath = string.concat(root, inputPath); + outputPath = string.concat(root, outputPath); + + initializeConfig(inputPath); instantiateCreate2Factory(); deployIfNeededMulticall3(); @@ -170,6 +199,7 @@ contract DeployL1Script is Script { deployDefaultUpgrade(); deployGenesisUpgrade(); + deployDAValidators(); deployValidatorTimelock(); deployGovernance(); @@ -188,17 +218,17 @@ contract DeployL1Script is Script { deployErc20BridgeProxy(); updateSharedBridge(); deployCTMDeploymentTracker(); - registerSharedBridge(); + setBridgehubParams(); + + initializeGeneratedData(); deployBlobVersionedHashRetriever(); deployChainTypeManagerContract(); setChainTypeManagerInValidatorTimelock(); - // deployDiamondProxy(); - updateOwners(); - saveOutput(); + saveOutput(outputPath); } function getBridgehubProxyAddress() public view returns (address) { @@ -222,17 +252,19 @@ contract DeployL1Script is Script { } function getCTM() public view returns (address) { - return addresses.stateTransition.stateTransitionProxy; + return addresses.stateTransition.chainTypeManagerProxy; } function getInitialDiamondCutData() public view returns (bytes memory) { return config.contracts.diamondCutData; } - function initializeConfig() internal { - string memory root = vm.projectRoot(); - string memory path = string.concat(root, vm.envString("L1_CONFIG")); - string memory toml = vm.readFile(path); + function getCTMDeploymentTrackerAddress() public view returns (address) { + return addresses.bridgehub.ctmDeploymentTrackerProxy; + } + + function initializeConfig(string memory configPath) internal { + string memory toml = vm.readFile(configPath); config.l1ChainId = block.chainid; config.deployerAddress = msg.sender; @@ -282,8 +314,10 @@ contract DeployL1Script is Script { config.contracts.bootloaderHash = toml.readBytes32("$.contracts.bootloader_hash"); config.tokens.tokenWethAddress = toml.readAddress("$.tokens.token_weth_address"); + } - config.contracts.forceDeploymentsData = toml.readBytes("$.contracts.force_deployments_data"); + function initializeGeneratedData() internal { + generatedData.forceDeploymentsData = prepareForceDeploymentsData(); } function instantiateCreate2Factory() internal { @@ -345,6 +379,18 @@ contract DeployL1Script is Script { addresses.stateTransition.genesisUpgrade = contractAddress; } + function deployDAValidators() internal { + DAContractBytecodes memory daBytecodes = Utils.readDAContractBytecodes(); + + address contractAddress = deployViaCreate2(daBytecodes.rollupL1DAValidator); + console.log("L1RollupDAValidator deployed at:", contractAddress); + addresses.daAddresses.l1RollupDAValidator = contractAddress; + + contractAddress = deployViaCreate2(daBytecodes.validiumL1DAValidator); + console.log("L1ValidiumDAValidator deployed at:", contractAddress); + addresses.daAddresses.l1ValidiumDAValidator = contractAddress; + } + function deployValidatorTimelock() internal { uint32 executionDelay = uint32(config.contracts.validatorTimelockExecutionDelay); bytes memory bytecode = abi.encodePacked( @@ -372,7 +418,7 @@ contract DeployL1Script is Script { function deployChainAdmin() internal { bytes memory accessControlRestrictionBytecode = abi.encodePacked( - type(ChainAdmin).creationCode, + type(AccessControlRestriction).creationCode, abi.encode(uint256(0), config.ownerAddress) ); @@ -380,6 +426,7 @@ contract DeployL1Script is Script { console.log("Access control restriction deployed at:", accessControlRestriction); address[] memory restrictions = new address[](1); restrictions[0] = accessControlRestriction; + addresses.accessControlRestrictionAddress = accessControlRestriction; bytes memory bytecode = abi.encodePacked(type(ChainAdmin).creationCode, abi.encode(restrictions)); address contractAddress = deployViaCreate2(bytecode); @@ -510,7 +557,7 @@ contract DeployL1Script is Script { ); address contractAddress = deployViaCreate2(bytecode); console.log("ChainTypeManagerImplementation deployed at:", contractAddress); - addresses.stateTransition.stateTransitionImplementation = contractAddress; + addresses.stateTransition.chainTypeManagerImplementation = contractAddress; } function deployChainTypeManagerProxy() internal { @@ -579,7 +626,7 @@ contract DeployL1Script is Script { genesisIndexRepeatedStorageChanges: uint64(config.contracts.genesisRollupLeafIndex), genesisBatchCommitment: config.contracts.genesisBatchCommitment, diamondCut: diamondCut, - forceDeploymentsData: config.contracts.forceDeploymentsData + forceDeploymentsData: generatedData.forceDeploymentsData }); ChainTypeManagerInitializeData memory diamondInitData = ChainTypeManagerInitializeData({ @@ -593,35 +640,35 @@ contract DeployL1Script is Script { abi.encodePacked( type(TransparentUpgradeableProxy).creationCode, abi.encode( - addresses.stateTransition.stateTransitionImplementation, + addresses.stateTransition.chainTypeManagerImplementation, addresses.transparentProxyAdmin, abi.encodeCall(ChainTypeManager.initialize, (diamondInitData)) ) ) ); console.log("ChainTypeManagerProxy deployed at:", contractAddress); - addresses.stateTransition.stateTransitionProxy = contractAddress; + addresses.stateTransition.chainTypeManagerProxy = contractAddress; } function registerChainTypeManager() internal { Bridgehub bridgehub = Bridgehub(addresses.bridgehub.bridgehubProxy); vm.startBroadcast(msg.sender); - bridgehub.addChainTypeManager(addresses.stateTransition.stateTransitionProxy); + bridgehub.addChainTypeManager(addresses.stateTransition.chainTypeManagerProxy); console.log("ChainTypeManager registered"); CTMDeploymentTracker ctmDT = CTMDeploymentTracker(addresses.bridgehub.ctmDeploymentTrackerProxy); // vm.startBroadcast(msg.sender); L1AssetRouter sharedBridge = L1AssetRouter(addresses.bridges.sharedBridgeProxy); sharedBridge.setAssetDeploymentTracker( - bytes32(uint256(uint160(addresses.stateTransition.stateTransitionProxy))), + bytes32(uint256(uint160(addresses.stateTransition.chainTypeManagerProxy))), address(ctmDT) ); console.log("CTM DT whitelisted"); - ctmDT.registerCTMAssetOnL1(addresses.stateTransition.stateTransitionProxy); + ctmDT.registerCTMAssetOnL1(addresses.stateTransition.chainTypeManagerProxy); vm.stopBroadcast(); console.log("CTM registered in CTMDeploymentTracker"); - bytes32 assetId = bridgehub.ctmAssetId(addresses.stateTransition.stateTransitionProxy); + bytes32 assetId = bridgehub.ctmAssetId(addresses.stateTransition.chainTypeManagerProxy); // console.log(address(bridgehub.ctmDeployer()), addresses.bridgehub.ctmDeploymentTrackerProxy); // console.log(address(bridgehub.ctmDeployer().BRIDGE_HUB()), addresses.bridgehub.bridgehubProxy); console.log( @@ -634,7 +681,7 @@ contract DeployL1Script is Script { function setChainTypeManagerInValidatorTimelock() internal { ValidatorTimelock validatorTimelock = ValidatorTimelock(addresses.validatorTimelock); vm.broadcast(msg.sender); - validatorTimelock.setChainTypeManager(IChainTypeManager(addresses.stateTransition.stateTransitionProxy)); + validatorTimelock.setChainTypeManager(IChainTypeManager(addresses.stateTransition.chainTypeManagerProxy)); console.log("ChainTypeManager set in ValidatorTimelock"); } @@ -671,8 +718,9 @@ contract DeployL1Script is Script { } function deployL1NullifierImplementation() internal { + // TODO(EVM-743): allow non-dev nullifier in the local deployment bytes memory bytecode = abi.encodePacked( - type(L1Nullifier).creationCode, + type(L1NullifierDev).creationCode, // solhint-disable-next-line func-named-parameters abi.encode(addresses.bridgehub.bridgehubProxy, config.eraChainId, addresses.stateTransition.diamondProxy) ); @@ -720,7 +768,7 @@ contract DeployL1Script is Script { addresses.bridges.sharedBridgeProxy = contractAddress; } - function registerSharedBridge() internal { + function setBridgehubParams() internal { Bridgehub bridgehub = Bridgehub(addresses.bridgehub.bridgehubProxy); vm.startBroadcast(msg.sender); bridgehub.addTokenAssetId(bridgehub.baseTokenAssetId(config.eraChainId)); @@ -853,19 +901,25 @@ contract DeployL1Script is Script { Bridgehub bridgehub = Bridgehub(addresses.bridgehub.bridgehubProxy); bridgehub.transferOwnership(addresses.governance); + bridgehub.setPendingAdmin(addresses.chainAdmin); L1AssetRouter sharedBridge = L1AssetRouter(addresses.bridges.sharedBridgeProxy); sharedBridge.transferOwnership(addresses.governance); - ChainTypeManager ctm = ChainTypeManager(addresses.stateTransition.stateTransitionProxy); + ChainTypeManager ctm = ChainTypeManager(addresses.stateTransition.chainTypeManagerProxy); ctm.transferOwnership(addresses.governance); + ctm.setPendingAdmin(addresses.chainAdmin); + + CTMDeploymentTracker ctmDeploymentTracker = CTMDeploymentTracker(addresses.bridgehub.ctmDeploymentTrackerProxy); + ctmDeploymentTracker.transferOwnership(addresses.governance); vm.stopBroadcast(); console.log("Owners updated"); } - function saveOutput() internal { + function saveOutput(string memory outputPath) internal { vm.serializeAddress("bridgehub", "bridgehub_proxy_addr", addresses.bridgehub.bridgehubProxy); + vm.serializeAddress("bridgehub", "bridgehub_implementation_addr", addresses.bridgehub.bridgehubImplementation); vm.serializeAddress( "bridgehub", "ctm_deployment_tracker_proxy_addr", @@ -877,26 +931,22 @@ contract DeployL1Script is Script { addresses.bridgehub.ctmDeploymentTrackerImplementation ); vm.serializeAddress("bridgehub", "message_root_proxy_addr", addresses.bridgehub.messageRootProxy); - vm.serializeAddress( + string memory bridgehub = vm.serializeAddress( "bridgehub", "message_root_implementation_addr", addresses.bridgehub.messageRootImplementation ); - string memory bridgehub = vm.serializeAddress( - "bridgehub", - "bridgehub_implementation_addr", - addresses.bridgehub.bridgehubImplementation - ); + // TODO(EVM-744): this has to be renamed to chain type manager vm.serializeAddress( "state_transition", "state_transition_proxy_addr", - addresses.stateTransition.stateTransitionProxy + addresses.stateTransition.chainTypeManagerProxy ); vm.serializeAddress( "state_transition", "state_transition_implementation_addr", - addresses.stateTransition.stateTransitionImplementation + addresses.stateTransition.chainTypeManagerImplementation ); vm.serializeAddress("state_transition", "verifier_addr", addresses.stateTransition.verifier); vm.serializeAddress("state_transition", "admin_facet_addr", addresses.stateTransition.adminFacet); @@ -914,6 +964,8 @@ contract DeployL1Script is Script { vm.serializeAddress("bridges", "erc20_bridge_implementation_addr", addresses.bridges.erc20BridgeImplementation); vm.serializeAddress("bridges", "erc20_bridge_proxy_addr", addresses.bridges.erc20BridgeProxy); + vm.serializeAddress("bridges", "l1_nullifier_implementation_addr", addresses.bridges.l1NullifierImplementation); + vm.serializeAddress("bridges", "l1_nullifier_proxy_addr", addresses.bridges.l1NullifierProxy); vm.serializeAddress( "bridges", "shared_bridge_implementation_addr", @@ -927,8 +979,8 @@ contract DeployL1Script is Script { vm.serializeUint( "contracts_config", - "diamond_init_pubdata_pricing_mode", - uint256(config.contracts.diamondInitPubdataPricingMode) + "diamond_init_max_l2_gas_per_batch", + config.contracts.diamondInitMaxL2GasPerBatch ); vm.serializeUint( "contracts_config", @@ -942,8 +994,8 @@ contract DeployL1Script is Script { ); vm.serializeUint( "contracts_config", - "diamond_init_max_l2_gas_per_batch", - config.contracts.diamondInitMaxL2GasPerBatch + "diamond_init_minimal_l2_gas_price", + config.contracts.diamondInitMinimalL2GasPrice ); vm.serializeUint( "contracts_config", @@ -952,13 +1004,14 @@ contract DeployL1Script is Script { ); vm.serializeUint( "contracts_config", - "diamond_init_minimal_l2_gas_price", - config.contracts.diamondInitMinimalL2GasPrice + "diamond_init_pubdata_pricing_mode", + uint256(config.contracts.diamondInitPubdataPricingMode) ); + vm.serializeUint("contracts_config", "priority_tx_max_gas_limit", config.contracts.priorityTxMaxGasLimit); vm.serializeBytes32( "contracts_config", - "recursion_node_level_vk_hash", - config.contracts.recursionNodeLevelVkHash + "recursion_circuits_set_vks_hash", + config.contracts.recursionCircuitsSetVksHash ); vm.serializeBytes32( "contracts_config", @@ -967,29 +1020,52 @@ contract DeployL1Script is Script { ); vm.serializeBytes32( "contracts_config", - "recursion_circuits_set_vks_hash", - config.contracts.recursionCircuitsSetVksHash + "recursion_node_level_vk_hash", + config.contracts.recursionNodeLevelVkHash ); - vm.serializeUint("contracts_config", "priority_tx_max_gas_limit", config.contracts.priorityTxMaxGasLimit); - vm.serializeBytes("contracts_config", "force_deployments_data", config.contracts.forceDeploymentsData); + vm.serializeBytes("contracts_config", "diamond_cut_data", config.contracts.diamondCutData); + string memory contractsConfig = vm.serializeBytes( "contracts_config", - "diamond_cut_data", - config.contracts.diamondCutData + "force_deployments_data", + generatedData.forceDeploymentsData ); - vm.serializeAddress("deployed_addresses", "transparent_proxy_admin_addr", addresses.transparentProxyAdmin); - vm.serializeAddress("deployed_addresses", "governance_addr", addresses.governance); vm.serializeAddress( "deployed_addresses", "blob_versioned_hash_retriever_addr", addresses.blobVersionedHashRetriever ); + vm.serializeAddress("deployed_addresses", "governance_addr", addresses.governance); + vm.serializeAddress("deployed_addresses", "transparent_proxy_admin_addr", addresses.transparentProxyAdmin); + vm.serializeAddress("deployed_addresses", "validator_timelock_addr", addresses.validatorTimelock); - vm.serializeAddress("deployed_addresses", "native_token_vault_addr", addresses.vaults.l1NativeTokenVaultProxy); + vm.serializeAddress("deployed_addresses", "chain_admin", addresses.chainAdmin); + vm.serializeAddress( + "deployed_addresses", + "access_control_restriction_addr", + addresses.accessControlRestrictionAddress + ); vm.serializeString("deployed_addresses", "bridgehub", bridgehub); + vm.serializeString("deployed_addresses", "bridges", bridges); vm.serializeString("deployed_addresses", "state_transition", stateTransition); - string memory deployedAddresses = vm.serializeString("deployed_addresses", "bridges", bridges); + + vm.serializeAddress( + "deployed_addresses", + "rollup_l1_da_validator_addr", + addresses.daAddresses.l1RollupDAValidator + ); + vm.serializeAddress( + "deployed_addresses", + "validium_l1_da_validator_addr", + addresses.daAddresses.l1RollupDAValidator + ); + + string memory deployedAddresses = vm.serializeAddress( + "deployed_addresses", + "native_token_vault_addr", + addresses.vaults.l1NativeTokenVaultProxy + ); vm.serializeAddress("root", "create2_factory_addr", addresses.create2Factory); vm.serializeBytes32("root", "create2_factory_salt", config.contracts.create2FactorySalt); @@ -1001,14 +1077,38 @@ contract DeployL1Script is Script { vm.serializeString("root", "contracts_config", contractsConfig); string memory toml = vm.serializeAddress("root", "owner_address", config.ownerAddress); - string memory path = string.concat(vm.projectRoot(), vm.envString("L1_OUTPUT")); - vm.writeToml(toml, path); + vm.writeToml(toml, outputPath); } function deployViaCreate2(bytes memory _bytecode) internal returns (address) { return Utils.deployViaCreate2(_bytecode, config.contracts.create2FactorySalt, addresses.create2Factory); } + function prepareForceDeploymentsData() internal view returns (bytes memory) { + require(addresses.governance != address(0), "Governance address is not set"); + + FixedForceDeploymentsData memory data = FixedForceDeploymentsData({ + l1ChainId: config.l1ChainId, + eraChainId: config.eraChainId, + l1AssetRouter: addresses.bridges.sharedBridgeProxy, + l2TokenProxyBytecodeHash: L2ContractHelper.hashL2Bytecode( + L2ContractsBytecodesLib.readBeaconProxyBytecode() + ), + aliasedL1Governance: AddressAliasHelper.applyL1ToL2Alias(addresses.governance), + maxNumberOfZKChains: config.contracts.maxNumberOfChains, + bridgehubBytecodeHash: L2ContractHelper.hashL2Bytecode(L2ContractsBytecodesLib.readBridgehubBytecode()), + l2AssetRouterBytecodeHash: L2ContractHelper.hashL2Bytecode( + L2ContractsBytecodesLib.readL2AssetRouterBytecode() + ), + l2NtvBytecodeHash: L2ContractHelper.hashL2Bytecode( + L2ContractsBytecodesLib.readL2NativeTokenVaultBytecode() + ), + messageRootBytecodeHash: L2ContractHelper.hashL2Bytecode(L2ContractsBytecodesLib.readMessageRootBytecode()) + }); + + return abi.encode(data); + } + // add this to be excluded from coverage report function test() internal {} } diff --git a/l1-contracts/deploy-scripts/DeployL2Contracts.sol b/l1-contracts/deploy-scripts/DeployL2Contracts.sol index 072840fc9..8c281e4cc 100644 --- a/l1-contracts/deploy-scripts/DeployL2Contracts.sol +++ b/l1-contracts/deploy-scripts/DeployL2Contracts.sol @@ -8,41 +8,32 @@ 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 {L2ContractsBytecodesLib} from "./L2ContractsBytecodesLib.sol"; // import {L1AssetRouter} from "contracts/bridge/asset-router/L1AssetRouter.sol"; contract DeployL2Script is Script { using stdToml for string; Config internal config; - ContractsBytecodes internal contracts; + DeployedContrats internal deployed; // solhint-disable-next-line gas-struct-packing struct Config { - address bridgehubAddress; + uint256 eraChainId; + uint256 chainId; address l1SharedBridgeProxy; + address bridgehubAddress; address governance; address erc20BridgeProxy; - // The owner of the contract sets the validator/attester weights. - // Can be the developer multisig wallet on mainnet. + bool validiumMode; address consensusRegistryOwner; - uint256 chainId; - uint256 eraChainId; - address l2SharedBridgeImplementation; - address l2SharedBridgeProxy; - address consensusRegistryImplementation; - address consensusRegistryProxy; - address forceDeployUpgraderAddress; } - struct ContractsBytecodes { - bytes l2StandardErc20FactoryBytecode; - bytes beaconProxy; - bytes l2StandardErc20Bytecode; - bytes l2SharedBridgeBytecode; - bytes l2SharedBridgeProxyBytecode; - bytes consensusRegistryBytecode; - bytes consensusRegistryProxyBytecode; - bytes forceDeployUpgrader; + struct DeployedContrats { + address l2DaValidatorAddress; + address forceDeployUpgraderAddress; + address consensusRegistryImplementation; + address consensusRegistryProxy; } function run() public { @@ -55,12 +46,10 @@ contract DeployL2Script is Script { function deploy(bool legacyBridge) public { initializeConfig(); - loadContracts(legacyBridge); - deployFactoryDeps(); - deploySharedBridge(); - deploySharedBridgeProxy(legacyBridge); - initializeChain(); + // Note, that it is important that the first transaction is for setting the L2 DA validator + deployL2DaValidator(); + deployForceDeployer(); deployConsensusRegistry(); deployConsensusRegistryProxy(); @@ -76,21 +65,15 @@ contract DeployL2Script is Script { deploySharedBridge(false); } + // TODO(EVM-745): port legacy contract tests to new contracts function deploySharedBridge(bool legacyBridge) internal { initializeConfig(); - loadContracts(legacyBridge); - - deployFactoryDeps(); - deploySharedBridge(); - deploySharedBridgeProxy(legacyBridge); - initializeChain(); saveOutput(); } function runDefaultUpgrader() public { initializeConfig(); - loadContracts(false); deployForceDeployer(); @@ -99,7 +82,6 @@ contract DeployL2Script is Script { function runDeployConsensusRegistry() public { initializeConfig(); - loadContracts(false); deployConsensusRegistry(); deployConsensusRegistryProxy(); @@ -107,44 +89,6 @@ contract DeployL2Script is Script { saveOutput(); } - function loadContracts(bool legacyBridge) internal { - //HACK: Meanwhile we are not integrated foundry zksync we use contracts that has been built using hardhat - contracts.l2StandardErc20FactoryBytecode = Utils.readHardhatBytecode( - "/artifacts-zk/@openzeppelin/contracts-v4/proxy/beacon/UpgradeableBeacon.sol/UpgradeableBeacon.json" - ); - contracts.beaconProxy = Utils.readHardhatBytecode( - "/artifacts-zk/@openzeppelin/contracts-v4/proxy/beacon/BeaconProxy.sol/BeaconProxy.json" - ); - contracts.l2StandardErc20Bytecode = Utils.readHardhatBytecode( - "/artifacts-zk/contracts/bridge/BridgedStandardERC20.sol/BridgedStandardERC20.json" - ); - - if (legacyBridge) { - contracts.l2SharedBridgeBytecode = Utils.readHardhatBytecode( - "/../l2-contracts/artifacts-zk/contracts/dev-contracts/DevL2SharedBridge.sol/DevL2SharedBridge.json" - ); - } else { - contracts.l2SharedBridgeBytecode = Utils.readHardhatBytecode( - "/../l2-contracts/zkout/L2SharedBridge.sol/L2SharedBridge.json" - ); - } - - contracts.l2SharedBridgeProxyBytecode = Utils.readHardhatBytecode( - "/artifacts-zk/@openzeppelin/contracts-v4/proxy/transparent/TransparentUpgradeableProxy.sol/TransparentUpgradeableProxy.json" - ); - - contracts.consensusRegistryBytecode = Utils.readHardhatBytecode( - "/../l2-contracts/zkout/ConsensusRegistry.sol/ConsensusRegistry.json" - ); - contracts.consensusRegistryProxyBytecode = Utils.readHardhatBytecode( - "/../l2-contracts/zkout/TransparentUpgradeableProxy.sol/TransparentUpgradeableProxy.json" - ); - - contracts.forceDeployUpgrader = Utils.readHardhatBytecode( - "/../l2-contracts/zkout/ForceDeployUpgrader.sol/ForceDeployUpgrader.json" - ); - } - function initializeConfig() internal { string memory root = vm.projectRoot(); string memory path = string.concat(root, "/script-config/config-deploy-l2-contracts.toml"); @@ -159,36 +103,31 @@ contract DeployL2Script is Script { } function saveOutput() internal { - vm.serializeAddress("root", "l2_shared_bridge_implementation", config.l2SharedBridgeImplementation); - vm.serializeAddress("root", "l2_shared_bridge_proxy", config.l2SharedBridgeProxy); - vm.serializeAddress("root", "consensus_registry_implementation", config.consensusRegistryImplementation); - vm.serializeAddress("root", "consensus_registry_proxy", config.consensusRegistryProxy); - string memory toml = vm.serializeAddress("root", "l2_default_upgrader", config.forceDeployUpgraderAddress); + vm.serializeAddress("root", "l2_da_validator_address", deployed.l2DaValidatorAddress); + + vm.serializeAddress("root", "consensus_registry_implementation", deployed.consensusRegistryImplementation); + vm.serializeAddress("root", "consensus_registry_proxy", deployed.consensusRegistryProxy); + string memory toml = vm.serializeAddress("root", "l2_default_upgrader", deployed.forceDeployUpgraderAddress); + string memory root = vm.projectRoot(); string memory path = string.concat(root, "/script-out/output-deploy-l2-contracts.toml"); vm.writeToml(toml, path); } - function deployFactoryDeps() internal { - 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() internal { - bytes[] memory factoryDeps = new bytes[](1); - factoryDeps[0] = contracts.beaconProxy; - - bytes memory constructorData = abi.encode(config.eraChainId); + function deployL2DaValidator() internal { + bytes memory bytecode; + if (config.validiumMode) { + bytecode = L2ContractsBytecodesLib.readValidiumL2DAValidatorBytecode(); + } else { + bytecode = L2ContractsBytecodesLib.readRollupL2DAValidatorBytecode(); + } - config.l2SharedBridgeImplementation = Utils.deployThroughL1({ - bytecode: contracts.l2SharedBridgeBytecode, - constructorargs: constructorData, + deployed.l2DaValidatorAddress = Utils.deployThroughL1({ + bytecode: bytecode, + constructorargs: bytes(""), create2salt: "", l2GasLimit: Utils.MAX_PRIORITY_TX_GAS, - factoryDeps: factoryDeps, + factoryDeps: new bytes[](0), chainId: config.chainId, bridgehubAddress: config.bridgehubAddress, l1SharedBridgeProxy: config.l1SharedBridgeProxy @@ -197,8 +136,8 @@ contract DeployL2Script is Script { function deployForceDeployer() internal { bytes[] memory factoryDeps = new bytes[](0); - config.forceDeployUpgraderAddress = Utils.deployThroughL1({ - bytecode: contracts.forceDeployUpgrader, + deployed.forceDeployUpgraderAddress = Utils.deployThroughL1({ + bytecode: L2ContractsBytecodesLib.readForceDeployUpgraderBytecode(), constructorargs: "", create2salt: "", l2GasLimit: Utils.MAX_PRIORITY_TX_GAS, @@ -209,51 +148,13 @@ contract DeployL2Script is Script { }); } - function deploySharedBridgeProxy(bool legacyBridge) internal { - address l2GovernorAddress = AddressAliasHelper.applyL1ToL2Alias(config.governance); - bytes32 l2StandardErc20BytecodeHash = L2ContractHelper.hashL2Bytecode(contracts.beaconProxy); - - string memory functionSignature; - - if (legacyBridge) { - functionSignature = "initializeDevBridge(address,address,bytes32,address)"; - } else { - functionSignature = "initialize(address,address,bytes32,address)"; - } - // solhint-disable-next-line func-named-parameters - bytes memory proxyInitializationParams = abi.encodeWithSignature( - functionSignature, - 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 - }); - } - // Deploy the ConsensusRegistry implementation and save its address into the config. function deployConsensusRegistry() internal { // ConsensusRegistry.sol doesn't have a constructor, just an initializer. bytes memory constructorData = ""; - config.consensusRegistryImplementation = Utils.deployThroughL1({ - bytecode: contracts.consensusRegistryBytecode, + deployed.consensusRegistryImplementation = Utils.deployThroughL1({ + bytecode: L2ContractsBytecodesLib.readConsensusRegistryBytecode(), constructorargs: constructorData, create2salt: "", l2GasLimit: Utils.MAX_PRIORITY_TX_GAS, @@ -278,13 +179,13 @@ contract DeployL2Script is Script { ); bytes memory consensusRegistryProxyConstructorData = abi.encode( - config.consensusRegistryImplementation, // _logic + deployed.consensusRegistryImplementation, // _logic l2GovernorAddress, // admin_ proxyInitializationParams // _data ); - config.consensusRegistryProxy = Utils.deployThroughL1({ - bytecode: contracts.consensusRegistryProxyBytecode, + deployed.consensusRegistryProxy = Utils.deployThroughL1({ + bytecode: L2ContractsBytecodesLib.readTransparentUpgradeableProxyBytecode(), constructorargs: consensusRegistryProxyConstructorData, create2salt: "", l2GasLimit: Utils.MAX_PRIORITY_TX_GAS, @@ -294,17 +195,4 @@ contract DeployL2Script is Script { l1SharedBridgeProxy: config.l1SharedBridgeProxy }); } - - function initializeChain() internal { - 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/deploy-scripts/DeployPaymaster.s.sol b/l1-contracts/deploy-scripts/DeployPaymaster.s.sol index eec87fbb0..e2b12b319 100644 --- a/l1-contracts/deploy-scripts/DeployPaymaster.s.sol +++ b/l1-contracts/deploy-scripts/DeployPaymaster.s.sol @@ -44,8 +44,8 @@ contract DeployPaymaster is Script { } function deploy() internal { - bytes memory testnetPaymasterBytecode = Utils.readFoundryBytecode( - "/../l2-contracts/zkout/TestnetPaymaster.sol/TestnetPaymaster.json" + bytes memory testnetPaymasterBytecode = Utils.readHardhatBytecode( + "/../l2-contracts/artifacts-zk/contracts/TestnetPaymaster.sol/TestnetPaymaster.json" ); config.paymaster = Utils.deployThroughL1({ diff --git a/l1-contracts/deploy-scripts/Gateway.s.sol b/l1-contracts/deploy-scripts/Gateway.s.sol deleted file mode 100644 index d5e4c78ea..000000000 --- a/l1-contracts/deploy-scripts/Gateway.s.sol +++ /dev/null @@ -1,255 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.24; - -// solhint-disable no-console, gas-custom-errors, reason-string - -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 {TransparentUpgradeableProxy} from "@openzeppelin/contracts-v4/proxy/transparent/TransparentUpgradeableProxy.sol"; - -import {Ownable} from "@openzeppelin/contracts-v4/access/Ownable.sol"; -import {IBridgehub, BridgehubBurnCTMAssetData} from "contracts/bridgehub/IBridgehub.sol"; -import {IZKChain} from "contracts/state-transition/chain-interfaces/IZKChain.sol"; -import {GatewayTransactionFilterer} from "contracts/transactionFilterer/GatewayTransactionFilterer.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/ZKChainStorage.sol"; -// import {IL1NativeTokenVault} from "contracts/bridge/ntv/IL1NativeTokenVault.sol"; -import {REQUIRED_L2_GAS_PRICE_PER_PUBDATA} from "contracts/common/Config.sol"; -import {L2TransactionRequestTwoBridgesOuter} from "contracts/bridgehub/IBridgehub.sol"; -import {L2_BRIDGEHUB_ADDR} from "contracts/common/L2ContractAddresses.sol"; - -// import {IL1AssetRouter} from "contracts/bridge/asset-router/IL1AssetRouter.sol"; - -contract GatewayScript is Script { - using stdToml for string; - - address internal constant ADDRESS_ONE = 0x0000000000000000000000000000000000000001; - bytes32 internal constant STATE_TRANSITION_NEW_CHAIN_HASH = keccak256("NewZKChain(uint256,address)"); - - // solhint-disable-next-line gas-struct-packing - struct Config { - address deployerAddress; - address ownerAddress; - uint256 chainChainId; - bool validiumMode; - uint256 bridgehubCreateNewChainSalt; - address validatorSenderOperatorCommitEth; - address validatorSenderOperatorBlobsEth; - address baseToken; - uint128 baseTokenGasPriceMultiplierNominator; - uint128 baseTokenGasPriceMultiplierDenominator; - address bridgehub; - address ctmDeploymentTracker; - address nativeTokenVault; - address stateTransitionProxy; - address sharedBridgeProxy; - address validatorTimelock; - bytes diamondCutData; - bytes forceDeployments; - address governanceSecurityCouncilAddress; - uint256 governanceMinDelay; - address newDiamondProxy; - address governance; - uint256 gatewayChainId; - } - - Config internal config; - - function run() public { - console.log("Setting up the Gateway script"); - - initializeConfig(); - } - - function initializeConfig() internal { - // Grab config from output of l1 deployment - string memory root = vm.projectRoot(); - string memory path = string.concat(root, vm.envString("L1_OUTPUT")); //"/script-config/register-zkChain.toml"); - string memory toml = vm.readFile(path); - - config.deployerAddress = msg.sender; - - // Config file must be parsed key by key, otherwise values returned - // are parsed alfabetically and not by key. - // https://book.getfoundry.sh/cheatcodes/parse-toml - - config.bridgehub = toml.readAddress("$.deployed_addresses.bridgehub.bridgehub_proxy_addr"); - config.stateTransitionProxy = toml.readAddress( - "$.deployed_addresses.state_transition.state_transition_proxy_addr" - ); - config.sharedBridgeProxy = toml.readAddress("$.deployed_addresses.bridges.shared_bridge_proxy_addr"); - config.validatorTimelock = toml.readAddress("$.deployed_addresses.validator_timelock_addr"); - // config.bridgehubGovernance = toml.readAddress("$.deployed_addresses.governance_addr"); - config.nativeTokenVault = toml.readAddress("$.deployed_addresses.native_token_vault_addr"); - config.diamondCutData = toml.readBytes("$.contracts_config.diamond_cut_data"); - config.forceDeployments = toml.readBytes("$.contracts_config.force_deployments_data"); - config.ctmDeploymentTracker = toml.readAddress( - "$.deployed_addresses.bridgehub.ctm_deployment_tracker_proxy_addr" - ); - path = string.concat(root, vm.envString("ZK_CHAIN_CONFIG")); - toml = vm.readFile(path); - - config.ownerAddress = toml.readAddress("$.owner_address"); - - config.chainChainId = toml.readUint("$.chain.chain_chain_id"); - config.bridgehubCreateNewChainSalt = toml.readUint("$.chain.bridgehub_create_new_chain_salt"); - config.baseToken = toml.readAddress("$.chain.base_token_addr"); - config.validiumMode = toml.readBool("$.chain.validium_mode"); - config.validatorSenderOperatorCommitEth = toml.readAddress("$.chain.validator_sender_operator_commit_eth"); - config.validatorSenderOperatorBlobsEth = toml.readAddress("$.chain.validator_sender_operator_blobs_eth"); - config.baseTokenGasPriceMultiplierNominator = uint128( - toml.readUint("$.chain.base_token_gas_price_multiplier_nominator") - ); - config.baseTokenGasPriceMultiplierDenominator = uint128( - toml.readUint("$.chain.base_token_gas_price_multiplier_denominator") - ); - config.governanceMinDelay = uint256(toml.readUint("$.chain.governance_min_delay")); - config.governanceSecurityCouncilAddress = toml.readAddress("$.chain.governance_security_council_address"); - - path = string.concat(root, vm.envString("GATEWAY_CONFIG")); - toml = vm.readFile(path); - - config.gatewayChainId = toml.readUint("$.chain.chain_chain_id"); - } - - function registerGateway() public { - IBridgehub bridgehub = IBridgehub(config.bridgehub); - Ownable ownable = Ownable(config.bridgehub); - Ownable ownableStmDT = Ownable(config.ctmDeploymentTracker); - IZKChain chainL2 = IZKChain(bridgehub.getZKChain(config.chainChainId)); - IZKChain chain = IZKChain(bridgehub.getZKChain(config.gatewayChainId)); - vm.startPrank(chain.getAdmin()); - GatewayTransactionFilterer transactionFiltererImplementation = new GatewayTransactionFilterer( - IBridgehub(config.bridgehub), - config.sharedBridgeProxy - ); - address transactionFiltererProxy = address( - new TransparentUpgradeableProxy( - address(transactionFiltererImplementation), - chain.getAdmin(), - abi.encodeCall(GatewayTransactionFilterer.initialize, ownable.owner()) - ) - ); - chain.setTransactionFilterer(transactionFiltererProxy); - vm.stopPrank(); - - vm.startPrank(ownable.owner()); - GatewayTransactionFilterer(transactionFiltererProxy).grantWhitelist(ownableStmDT.owner()); - GatewayTransactionFilterer(transactionFiltererProxy).grantWhitelist(chainL2.getAdmin()); - GatewayTransactionFilterer(transactionFiltererProxy).grantWhitelist(config.sharedBridgeProxy); - bridgehub.registerSettlementLayer(config.gatewayChainId, true); - - vm.stopPrank(); - // bytes memory data = abi.encodeCall(stm.registerSettlementLayer, (config.chainChainId, true)); - // Utils.executeUpgrade({ - // _governor: ownable.owner(), - // _salt: bytes32(config.bridgehubCreateNewChainSalt), - // _target: config.stateTransitionProxy, - // _data: data, - // _value: 0, - // _delay: 0 - // }); - console.log("Gateway registered on CTM"); - } - - function moveChainToGateway() public { - IBridgehub bridgehub = IBridgehub(config.bridgehub); - // IL1AssetRouter router = IL1AssetRouter(config.sharedBridgeProxy); - Ownable ownable = Ownable(config.bridgehub); - - uint256 gasPrice = 10; //Utils.bytesToUint256(vm.rpc("eth_gasPrice", "[]")); - uint256 l2GasLimit = 72000000; - - uint256 expectedCost = bridgehub.l2TransactionBaseCost( - config.gatewayChainId, - gasPrice, - l2GasLimit, - REQUIRED_L2_GAS_PRICE_PER_PUBDATA - ) * 2; - - address newAdmin = ownable.owner(); - console.log("newAdmin", newAdmin); - IZKChain chain = IZKChain(bridgehub.getZKChain(config.chainChainId)); - console.log("chainAdmin", bridgehub.getZKChain(config.chainChainId), chain.getAdmin()); - bytes32 ctmAssetId = bridgehub.ctmAssetIdFromChainId(config.chainChainId); - bytes memory diamondCutData = config.diamondCutData; // todo replace with config.zkDiamondCutData; - bytes memory ctmData = abi.encode(newAdmin, diamondCutData); - bytes memory chainData = abi.encode(chain.getProtocolVersion()); - BridgehubBurnCTMAssetData memory ctmAssetData = BridgehubBurnCTMAssetData({ - chainId: config.chainChainId, - ctmData: ctmData, - chainData: chainData - }); - bytes memory bridgehubData = abi.encode(ctmAssetData); - bytes memory routerData = bytes.concat(bytes1(0x01), abi.encode(ctmAssetId, bridgehubData)); - - vm.startBroadcast(chain.getAdmin()); - L2TransactionRequestTwoBridgesOuter memory request = L2TransactionRequestTwoBridgesOuter({ - chainId: config.gatewayChainId, - mintValue: expectedCost, - l2Value: 0, - l2GasLimit: l2GasLimit, - l2GasPerPubdataByteLimit: REQUIRED_L2_GAS_PRICE_PER_PUBDATA, - refundRecipient: newAdmin, - secondBridgeAddress: config.sharedBridgeProxy, - secondBridgeValue: 0, - secondBridgeCalldata: routerData - }); - bridgehub.requestL2TransactionTwoBridges{value: expectedCost}(request); - vm.stopBroadcast(); - console.log("Chain moved to Gateway"); - } - - function registerL2Contracts() public { - IBridgehub bridgehub = IBridgehub(config.bridgehub); - Ownable ownable = Ownable(config.ctmDeploymentTracker); - // IStateTransitionManager stm = IStateTransitionManager(config.stateTransitionProxy); - - uint256 gasPrice = 10; - uint256 l2GasLimit = 72000000; - - uint256 expectedCost = bridgehub.l2TransactionBaseCost( - config.chainChainId, - gasPrice, - l2GasLimit, - REQUIRED_L2_GAS_PRICE_PER_PUBDATA - ) * 2; - bytes32 assetId = bridgehub.ctmAssetIdFromChainId(config.chainChainId); - bytes memory routerData = bytes.concat(bytes1(0x02), abi.encode(assetId, L2_BRIDGEHUB_ADDR)); - L2TransactionRequestTwoBridgesOuter - memory assetRouterRegistrationRequest = L2TransactionRequestTwoBridgesOuter({ - chainId: config.chainChainId, - mintValue: expectedCost, - l2Value: 0, - l2GasLimit: l2GasLimit, - l2GasPerPubdataByteLimit: REQUIRED_L2_GAS_PRICE_PER_PUBDATA, - refundRecipient: ownable.owner(), - secondBridgeAddress: config.sharedBridgeProxy, - secondBridgeValue: 0, - secondBridgeCalldata: routerData - }); - - L2TransactionRequestTwoBridgesOuter memory bridehubRegistrationRequest = L2TransactionRequestTwoBridgesOuter({ - chainId: config.chainChainId, - mintValue: expectedCost, - l2Value: 0, - l2GasLimit: l2GasLimit, - l2GasPerPubdataByteLimit: REQUIRED_L2_GAS_PRICE_PER_PUBDATA, - refundRecipient: ownable.owner(), - secondBridgeAddress: config.ctmDeploymentTracker, - secondBridgeValue: 0, - secondBridgeCalldata: bytes.concat( - bytes1(0x01), - abi.encode(config.stateTransitionProxy, config.stateTransitionProxy) - ) - }); - vm.startBroadcast(ownable.owner()); - bridgehub.requestL2TransactionTwoBridges{value: expectedCost}(assetRouterRegistrationRequest); - bridgehub.requestL2TransactionTwoBridges{value: expectedCost}(bridehubRegistrationRequest); - vm.stopBroadcast(); - } -} diff --git a/l1-contracts/deploy-scripts/GatewayCTMFromL1.s.sol b/l1-contracts/deploy-scripts/GatewayCTMFromL1.s.sol new file mode 100644 index 000000000..66fb96ed9 --- /dev/null +++ b/l1-contracts/deploy-scripts/GatewayCTMFromL1.s.sol @@ -0,0 +1,423 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.24; + +// solhint-disable no-console, gas-custom-errors, reason-string + +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-v4/access/Ownable.sol"; +import {IBridgehub} from "contracts/bridgehub/IBridgehub.sol"; +import {IZKChain} from "contracts/state-transition/chain-interfaces/IZKChain.sol"; +import {REQUIRED_L2_GAS_PRICE_PER_PUBDATA} from "contracts/common/Config.sol"; +import {L2TransactionRequestTwoBridgesOuter} from "contracts/bridgehub/IBridgehub.sol"; +import {IZKChain} from "contracts/state-transition/chain-interfaces/IZKChain.sol"; +import {StateTransitionDeployedAddresses, Utils, L2_BRIDGEHUB_ADDRESS} from "./Utils.sol"; +import {AddressAliasHelper} from "contracts/vendor/AddressAliasHelper.sol"; +import {L2ContractsBytecodesLib} from "./L2ContractsBytecodesLib.sol"; + +import {AdminFacet} from "contracts/state-transition/chain-deps/facets/Admin.sol"; +import {ExecutorFacet} from "contracts/state-transition/chain-deps/facets/Executor.sol"; +import {GettersFacet} from "contracts/state-transition/chain-deps/facets/Getters.sol"; +import {MailboxFacet} from "contracts/state-transition/chain-deps/facets/Mailbox.sol"; +import {DiamondProxy} from "contracts/state-transition/chain-deps/DiamondProxy.sol"; +import {DiamondInit} from "contracts/state-transition/chain-deps/DiamondInit.sol"; + +import {TestnetVerifier} from "contracts/state-transition/TestnetVerifier.sol"; +import {Verifier} from "contracts/state-transition/Verifier.sol"; +import {VerifierParams, IVerifier} from "contracts/state-transition/chain-interfaces/IVerifier.sol"; +import {ValidatorTimelock} from "contracts/state-transition/ValidatorTimelock.sol"; +import {L1GenesisUpgrade} from "contracts/upgrades/L1GenesisUpgrade.sol"; +import {DefaultUpgrade} from "contracts/upgrades/DefaultUpgrade.sol"; + +import {ChainTypeManager} from "contracts/state-transition/ChainTypeManager.sol"; +import {TransparentUpgradeableProxy} from "@openzeppelin/contracts-v4/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {InitializeDataNewChain as DiamondInitializeDataNewChain} from "contracts/state-transition/chain-interfaces/IDiamondInit.sol"; +import {FeeParams, PubdataPricingMode} from "contracts/state-transition/chain-deps/ZKChainStorage.sol"; +import {Diamond} from "contracts/state-transition/libraries/Diamond.sol"; +import {ChainTypeManagerInitializeData, ChainCreationParams, IChainTypeManager} from "contracts/state-transition/IChainTypeManager.sol"; + +/// @notice Scripts that is responsible for preparing the chain to become a gateway +contract GatewayCTMFromL1 is Script { + using stdToml for string; + + address internal constant ADDRESS_ONE = 0x0000000000000000000000000000000000000001; + bytes32 internal constant STATE_TRANSITION_NEW_CHAIN_HASH = keccak256("NewHyperchain(uint256,address)"); + + address deployerAddress; + + // solhint-disable-next-line gas-struct-packing + struct Config { + address bridgehub; + address ctmDeploymentTracker; + address nativeTokenVault; + address chainTypeManagerProxy; + address sharedBridgeProxy; + address governance; + uint256 chainChainId; + uint256 eraChainId; + uint256 l1ChainId; + bool testnetVerifier; + bytes32 recursionNodeLevelVkHash; + bytes32 recursionLeafLevelVkHash; + bytes32 recursionCircuitsSetVksHash; + PubdataPricingMode diamondInitPubdataPricingMode; + uint256 diamondInitBatchOverheadL1Gas; + uint256 diamondInitMaxPubdataPerBatch; + uint256 diamondInitMaxL2GasPerBatch; + uint256 diamondInitPriorityTxMaxPubdata; + uint256 diamondInitMinimalL2GasPrice; + bytes32 bootloaderHash; + bytes32 defaultAAHash; + uint256 priorityTxMaxGasLimit; + bytes32 genesisRoot; + uint256 genesisRollupLeafIndex; + bytes32 genesisBatchCommitment; + uint256 latestProtocolVersion; + bytes forceDeploymentsData; + } + + struct Output { + StateTransitionDeployedAddresses gatewayStateTransition; + address multicall3; + bytes diamondCutData; + address relayedSLDAValidator; + // TODO(EVM-747): for now zero, since the contract structure is not adapted to it + address validiumDAValidator; + } + + Config internal config; + Output internal output; + + function run() public { + console.log("Setting up the Gateway script"); + + initializeConfig(); + deployGatewayContracts(); + + saveOutput(); + } + + function initializeConfig() internal { + deployerAddress = msg.sender; + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/script-config/config-deploy-gateway-ctm.toml"); + string memory toml = vm.readFile(path); + + // Config file must be parsed key by key, otherwise values returned + // are parsed alfabetically and not by key. + // https://book.getfoundry.sh/cheatcodes/parse-toml + + // Initializing all values at once is preferableo ensure type safety of + // the fact that all values are initialized + config = Config({ + bridgehub: toml.readAddress("$.bridgehub_proxy_addr"), + ctmDeploymentTracker: toml.readAddress("$.ctm_deployment_tracker_proxy_addr"), + nativeTokenVault: toml.readAddress("$.native_token_vault_addr"), + chainTypeManagerProxy: toml.readAddress("$.chain_type_manager_proxy_addr"), + sharedBridgeProxy: toml.readAddress("$.shared_bridge_proxy_addr"), + chainChainId: toml.readUint("$.chain_chain_id"), + governance: toml.readAddress("$.governance"), + l1ChainId: toml.readUint("$.l1_chain_id"), + eraChainId: toml.readUint("$.era_chain_id"), + testnetVerifier: toml.readBool("$.testnet_verifier"), + recursionNodeLevelVkHash: toml.readBytes32("$.recursion_node_level_vk_hash"), + recursionLeafLevelVkHash: toml.readBytes32("$.recursion_leaf_level_vk_hash"), + recursionCircuitsSetVksHash: toml.readBytes32("$.recursion_circuits_set_vks_hash"), + diamondInitPubdataPricingMode: PubdataPricingMode(toml.readUint("$.diamond_init_pubdata_pricing_mode")), + diamondInitBatchOverheadL1Gas: toml.readUint("$.diamond_init_batch_overhead_l1_gas"), + diamondInitMaxPubdataPerBatch: toml.readUint("$.diamond_init_max_pubdata_per_batch"), + diamondInitMaxL2GasPerBatch: toml.readUint("$.diamond_init_max_l2_gas_per_batch"), + diamondInitPriorityTxMaxPubdata: toml.readUint("$.diamond_init_priority_tx_max_pubdata"), + diamondInitMinimalL2GasPrice: toml.readUint("$.diamond_init_minimal_l2_gas_price"), + bootloaderHash: toml.readBytes32("$.bootloader_hash"), + defaultAAHash: toml.readBytes32("$.default_aa_hash"), + priorityTxMaxGasLimit: toml.readUint("$.priority_tx_max_gas_limit"), + genesisRoot: toml.readBytes32("$.genesis_root"), + genesisRollupLeafIndex: toml.readUint("$.genesis_rollup_leaf_index"), + genesisBatchCommitment: toml.readBytes32("$.genesis_batch_commitment"), + latestProtocolVersion: toml.readUint("$.latest_protocol_version"), + forceDeploymentsData: toml.readBytes("$.force_deployments_data") + }); + } + + function saveOutput() internal { + vm.serializeAddress( + "gateway_state_transition", + "chain_type_manager_proxy_addr", + output.gatewayStateTransition.chainTypeManagerProxy + ); + vm.serializeAddress( + "gateway_state_transition", + "chain_type_manager_implementation_addr", + output.gatewayStateTransition.chainTypeManagerImplementation + ); + vm.serializeAddress("gateway_state_transition", "verifier_addr", output.gatewayStateTransition.verifier); + vm.serializeAddress("gateway_state_transition", "admin_facet_addr", output.gatewayStateTransition.adminFacet); + vm.serializeAddress( + "gateway_state_transition", + "mailbox_facet_addr", + output.gatewayStateTransition.mailboxFacet + ); + vm.serializeAddress( + "gateway_state_transition", + "executor_facet_addr", + output.gatewayStateTransition.executorFacet + ); + vm.serializeAddress( + "gateway_state_transition", + "getters_facet_addr", + output.gatewayStateTransition.gettersFacet + ); + vm.serializeAddress("gateway_state_transition", "diamond_init_addr", output.gatewayStateTransition.diamondInit); + vm.serializeAddress( + "gateway_state_transition", + "genesis_upgrade_addr", + output.gatewayStateTransition.genesisUpgrade + ); + vm.serializeAddress( + "gateway_state_transition", + "default_upgrade_addr", + output.gatewayStateTransition.defaultUpgrade + ); + vm.serializeAddress( + "gateway_state_transition", + "validator_timelock_addr", + output.gatewayStateTransition.validatorTimelock + ); + string memory gatewayStateTransition = vm.serializeAddress( + "gateway_state_transition", + "diamond_proxy_addr", + output.gatewayStateTransition.diamondProxy + ); + vm.serializeString("root", "gateway_state_transition", gatewayStateTransition); + vm.serializeAddress("root", "multicall3_addr", output.multicall3); + vm.serializeAddress("root", "relayed_sl_da_validator", output.relayedSLDAValidator); + vm.serializeAddress("root", "validium_da_validator", output.validiumDAValidator); + + string memory toml = vm.serializeBytes("root", "diamond_cut_data", output.diamondCutData); + string memory path = string.concat(vm.projectRoot(), "/script-out/output-deploy-gateway-ctm.toml"); + vm.writeToml(toml, path); + } + + /// @dev The sender may not have any privileges + function deployGatewayContracts() public { + output.multicall3 = _deployInternal(L2ContractsBytecodesLib.readMulticall3Bytecode(), hex""); + + deployGatewayFacets(); + + output.gatewayStateTransition.verifier = deployGatewayVerifier(); + output.gatewayStateTransition.validatorTimelock = deployValidatorTimelock(); + output.gatewayStateTransition.genesisUpgrade = address( + _deployInternal(L2ContractsBytecodesLib.readL1GenesisUpgradeBytecode(), hex"") + ); + console.log("Genesis upgrade deployed at", output.gatewayStateTransition.genesisUpgrade); + output.gatewayStateTransition.defaultUpgrade = address( + _deployInternal(L2ContractsBytecodesLib.readDefaultUpgradeBytecode(), hex"") + ); + console.log("Default upgrade deployed at", output.gatewayStateTransition.defaultUpgrade); + output.gatewayStateTransition.diamondInit = address( + _deployInternal(L2ContractsBytecodesLib.readDiamondInitBytecode(), hex"") + ); + console.log("Diamond init deployed at", output.gatewayStateTransition.diamondInit); + + deployGatewayChainTypeManager(); + setChainTypeManagerInValidatorTimelock(); + + output.relayedSLDAValidator = _deployInternal( + L2ContractsBytecodesLib.readRelayedSLDAValidatorBytecode(), + hex"" + ); + } + + function _deployInternal(bytes memory bytecode, bytes memory constructorargs) internal returns (address) { + return + Utils.deployThroughL1({ + bytecode: bytecode, + constructorargs: constructorargs, + create2salt: bytes32(0), + l2GasLimit: Utils.MAX_PRIORITY_TX_GAS, + factoryDeps: new bytes[](0), + chainId: config.chainChainId, + bridgehubAddress: config.bridgehub, + l1SharedBridgeProxy: config.sharedBridgeProxy + }); + } + + function deployGatewayFacets() internal { + address adminFacet = address( + _deployInternal(L2ContractsBytecodesLib.readAdminFacetBytecode(), abi.encode(config.l1ChainId)) + ); + console.log("Admin facet deployed at", adminFacet); + + address mailboxFacet = address( + _deployInternal( + L2ContractsBytecodesLib.readMailboxFacetBytecode(), + abi.encode(config.l1ChainId, config.eraChainId) + ) + ); + console.log("Mailbox facet deployed at", mailboxFacet); + + address executorFacet = address(_deployInternal(L2ContractsBytecodesLib.readExecutorFacetBytecode(), hex"")); + console.log("ExecutorFacet facet deployed at", executorFacet); + + address gettersFacet = address(_deployInternal(L2ContractsBytecodesLib.readGettersFacetBytecode(), hex"")); + console.log("Getters facet deployed at", gettersFacet); + + output.gatewayStateTransition.adminFacet = adminFacet; + output.gatewayStateTransition.mailboxFacet = mailboxFacet; + output.gatewayStateTransition.executorFacet = executorFacet; + output.gatewayStateTransition.gettersFacet = gettersFacet; + } + + function deployGatewayVerifier() internal returns (address verifier) { + if (config.testnetVerifier) { + verifier = address(_deployInternal(L2ContractsBytecodesLib.readTestnetVerifierBytecode(), hex"")); + } else { + verifier = address(_deployInternal(L2ContractsBytecodesLib.readVerifierBytecode(), hex"")); + } + + console.log("Verifier deployed at", verifier); + } + + function deployValidatorTimelock() internal returns (address validatorTimelock) { + // address aliasedGovernor = AddressAliasHelper.applyL1ToL2Alias(config.governance); + // TODO(EVM-745): eventually the governance should be moved to the governance contract + // Note: we do not apply alias because the deployer is an EOA. + validatorTimelock = address( + _deployInternal( + L2ContractsBytecodesLib.readValidatorTimelockBytecode(), + abi.encode(deployerAddress, 0, config.eraChainId) + ) + ); + console.log("Validator timelock deployed at", validatorTimelock); + } + + function deployGatewayChainTypeManager() internal { + // We need to publish the bytecode of the diamdon proxy contract, + // we can only do it via deploying its dummy version. + // We could've published the dependency separately, but we just repeated the code that would be + // used for pure L2 execution. + address dp = address(_deployInternal(L2ContractsBytecodesLib.readDiamondProxyBytecode(), hex"")); + console.log("Dummy diamond proxy deployed at", dp); + + output.gatewayStateTransition.chainTypeManagerImplementation = address( + _deployInternal(L2ContractsBytecodesLib.readChainTypeManagerBytecode(), abi.encode(L2_BRIDGEHUB_ADDRESS)) + ); + console.log( + "StateTransitionImplementation deployed at", + output.gatewayStateTransition.chainTypeManagerImplementation + ); + + // TODO(EVM-745): eventually a proxy admin or something should be deplyoed here + Diamond.FacetCut[] memory facetCuts = new Diamond.FacetCut[](4); + facetCuts[0] = Diamond.FacetCut({ + facet: output.gatewayStateTransition.adminFacet, + action: Diamond.Action.Add, + isFreezable: false, + selectors: Utils.getAllSelectorsForFacet("Admin") + }); + facetCuts[1] = Diamond.FacetCut({ + facet: output.gatewayStateTransition.gettersFacet, + action: Diamond.Action.Add, + isFreezable: false, + selectors: Utils.getAllSelectorsForFacet("Getters") + }); + facetCuts[2] = Diamond.FacetCut({ + facet: output.gatewayStateTransition.mailboxFacet, + action: Diamond.Action.Add, + isFreezable: true, + selectors: Utils.getAllSelectorsForFacet("Mailbox") + }); + facetCuts[3] = Diamond.FacetCut({ + facet: output.gatewayStateTransition.executorFacet, + action: Diamond.Action.Add, + isFreezable: true, + selectors: Utils.getAllSelectorsForFacet("Executor") + }); + + VerifierParams memory verifierParams = VerifierParams({ + recursionNodeLevelVkHash: config.recursionNodeLevelVkHash, + recursionLeafLevelVkHash: config.recursionLeafLevelVkHash, + recursionCircuitsSetVksHash: config.recursionCircuitsSetVksHash + }); + + FeeParams memory feeParams = FeeParams({ + pubdataPricingMode: config.diamondInitPubdataPricingMode, + batchOverheadL1Gas: uint32(config.diamondInitBatchOverheadL1Gas), + maxPubdataPerBatch: uint32(config.diamondInitMaxPubdataPerBatch), + maxL2GasPerBatch: uint32(config.diamondInitMaxL2GasPerBatch), + priorityTxMaxPubdata: uint32(config.diamondInitPriorityTxMaxPubdata), + minimalL2GasPrice: uint64(config.diamondInitMinimalL2GasPrice) + }); + + DiamondInitializeDataNewChain memory initializeData = DiamondInitializeDataNewChain({ + verifier: IVerifier(output.gatewayStateTransition.verifier), + verifierParams: verifierParams, + l2BootloaderBytecodeHash: config.bootloaderHash, + l2DefaultAccountBytecodeHash: config.defaultAAHash, + priorityTxMaxGasLimit: config.priorityTxMaxGasLimit, + feeParams: feeParams, + // We can not provide zero value there. At the same time, there is no such contract on gateway + blobVersionedHashRetriever: ADDRESS_ONE + }); + + Diamond.DiamondCutData memory diamondCut = Diamond.DiamondCutData({ + facetCuts: facetCuts, + initAddress: output.gatewayStateTransition.diamondInit, + initCalldata: abi.encode(initializeData) + }); + + output.diamondCutData = abi.encode(diamondCut); + + ChainCreationParams memory chainCreationParams = ChainCreationParams({ + genesisUpgrade: output.gatewayStateTransition.genesisUpgrade, + genesisBatchHash: config.genesisRoot, + genesisIndexRepeatedStorageChanges: uint64(config.genesisRollupLeafIndex), + genesisBatchCommitment: config.genesisBatchCommitment, + diamondCut: diamondCut, + // Note, it is the same as for contracts that are based on L2 + forceDeploymentsData: config.forceDeploymentsData + }); + + ChainTypeManagerInitializeData memory diamondInitData = ChainTypeManagerInitializeData({ + owner: msg.sender, + validatorTimelock: output.gatewayStateTransition.validatorTimelock, + chainCreationParams: chainCreationParams, + protocolVersion: config.latestProtocolVersion + }); + + output.gatewayStateTransition.chainTypeManagerProxy = _deployInternal( + L2ContractsBytecodesLib.readTransparentUpgradeableProxyBytecode(), + abi.encode( + output.gatewayStateTransition.chainTypeManagerImplementation, + deployerAddress, + abi.encodeCall(ChainTypeManager.initialize, (diamondInitData)) + ) + ); + + console.log("ChainTypeManagerProxy deployed at:", output.gatewayStateTransition.chainTypeManagerProxy); + output.gatewayStateTransition.chainTypeManagerProxy = output.gatewayStateTransition.chainTypeManagerProxy; + } + + function setChainTypeManagerInValidatorTimelock() internal { + bytes memory data = abi.encodeCall( + ValidatorTimelock.setChainTypeManager, + (IChainTypeManager(output.gatewayStateTransition.chainTypeManagerProxy)) + ); + + Utils.runL1L2Transaction({ + l2Calldata: data, + l2GasLimit: Utils.MAX_PRIORITY_TX_GAS, + l2Value: 0, + factoryDeps: new bytes[](0), + dstAddress: output.gatewayStateTransition.validatorTimelock, + chainId: config.chainChainId, + bridgehubAddress: config.bridgehub, + l1SharedBridgeProxy: config.sharedBridgeProxy + }); + + console.log("ChainTypeManager set in ValidatorTimelock"); + } +} diff --git a/l1-contracts/deploy-scripts/GatewayPreparation.s.sol b/l1-contracts/deploy-scripts/GatewayPreparation.s.sol new file mode 100644 index 000000000..91601d9bd --- /dev/null +++ b/l1-contracts/deploy-scripts/GatewayPreparation.s.sol @@ -0,0 +1,410 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.24; + +// solhint-disable no-console, gas-custom-errors, reason-string + +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-v4/access/Ownable.sol"; +import {IBridgehub, BridgehubBurnCTMAssetData} from "contracts/bridgehub/IBridgehub.sol"; +import {IZKChain} from "contracts/state-transition/chain-interfaces/IZKChain.sol"; +import {REQUIRED_L2_GAS_PRICE_PER_PUBDATA} from "contracts/common/Config.sol"; +import {L2TransactionRequestTwoBridgesOuter} from "contracts/bridgehub/IBridgehub.sol"; +import {L2_BRIDGEHUB_ADDR} from "contracts/common/L2ContractAddresses.sol"; +import {IZKChain} from "contracts/state-transition/chain-interfaces/IZKChain.sol"; +import {StateTransitionDeployedAddresses, Utils, L2_BRIDGEHUB_ADDRESS} from "./Utils.sol"; +import {AddressAliasHelper} from "contracts/vendor/AddressAliasHelper.sol"; +import {ValidatorTimelock} from "contracts/state-transition/ValidatorTimelock.sol"; +import {IAdmin} from "contracts/state-transition/chain-interfaces/IAdmin.sol"; +import {GatewayTransactionFilterer} from "contracts/transactionFilterer/GatewayTransactionFilterer.sol"; +import {TransparentUpgradeableProxy} from "@openzeppelin/contracts-v4/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {SET_ASSET_HANDLER_COUNTERPART_ENCODING_VERSION} from "contracts/bridge/asset-router/IAssetRouterBase.sol"; +import {CTM_DEPLOYMENT_TRACKER_ENCODING_VERSION} from "contracts/bridgehub/CTMDeploymentTracker.sol"; + +/// @notice Scripts that is responsible for preparing the chain to become a gateway +contract GatewayPreparation is Script { + using stdToml for string; + + address internal constant ADDRESS_ONE = 0x0000000000000000000000000000000000000001; + bytes32 internal constant STATE_TRANSITION_NEW_CHAIN_HASH = keccak256("NewHyperchain(uint256,address)"); + + address deployerAddress; + uint256 l1ChainId; + + // solhint-disable-next-line gas-struct-packing + struct Config { + address bridgehub; + address ctmDeploymentTracker; + address chainTypeManagerProxy; + address sharedBridgeProxy; + address governance; + uint256 gatewayChainId; + address gatewayChainAdmin; + address gatewayAccessControlRestriction; + address gatewayChainProxyAdmin; + bytes gatewayDiamondCutData; + } + + struct Output { + bytes32 governanceL2TxHash; + address gatewayTransactionFiltererImplementation; + address gatewayTransactionFiltererProxy; + } + + Config internal config; + + function run() public { + console.log("Setting up the Gateway script"); + + initializeConfig(); + } + + function _getL1GasPrice() internal virtual returns (uint256) { + return Utils.bytesToUint256(vm.rpc("eth_gasPrice", "[]")); + } + + function initializeConfig() internal virtual { + deployerAddress = msg.sender; + l1ChainId = block.chainid; + + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/script-config/gateway-preparation-l1.toml"); + string memory toml = vm.readFile(path); + + // Config file must be parsed key by key, otherwise values returned + // are parsed alfabetically and not by key. + // https://book.getfoundry.sh/cheatcodes/parse-toml + + // Initializing all values at once is preferable to ensure type safety of + // the fact that all values are initialized + config = Config({ + bridgehub: toml.readAddress("$.bridgehub_proxy_addr"), + ctmDeploymentTracker: toml.readAddress("$.ctm_deployment_tracker_proxy_addr"), + chainTypeManagerProxy: toml.readAddress("$.chain_type_manager_proxy_addr"), + sharedBridgeProxy: toml.readAddress("$.shared_bridge_proxy_addr"), + gatewayChainId: toml.readUint("$.chain_chain_id"), + governance: toml.readAddress("$.governance"), + gatewayDiamondCutData: toml.readBytes("$.gateway_diamond_cut_data"), + gatewayChainAdmin: toml.readAddress("$.chain_admin"), + gatewayAccessControlRestriction: toml.readAddress("$.access_control_restriction"), + gatewayChainProxyAdmin: toml.readAddress("$.chain_proxy_admin") + }); + } + + function saveOutput(Output memory output) internal { + vm.serializeAddress( + "root", + "gateway_transaction_filterer_implementation", + output.gatewayTransactionFiltererImplementation + ); + vm.serializeAddress("root", "gateway_transaction_filterer_proxy", output.gatewayTransactionFiltererProxy); + string memory toml = vm.serializeBytes32("root", "governance_l2_tx_hash", output.governanceL2TxHash); + string memory path = string.concat(vm.projectRoot(), "/script-out/output-gateway-preparation-l1.toml"); + vm.writeToml(toml, path); + } + + function saveOutput(bytes32 governanceL2TxHash) internal { + Output memory output = Output({ + governanceL2TxHash: governanceL2TxHash, + gatewayTransactionFiltererImplementation: address(0), + gatewayTransactionFiltererProxy: address(0) + }); + + saveOutput(output); + } + + function saveOutput( + address gatewayTransactionFiltererImplementation, + address gatewayTransactionFiltererProxy + ) internal { + Output memory output = Output({ + governanceL2TxHash: bytes32(0), + gatewayTransactionFiltererImplementation: gatewayTransactionFiltererImplementation, + gatewayTransactionFiltererProxy: gatewayTransactionFiltererProxy + }); + + saveOutput(output); + } + + /// @dev Requires the sender to be the owner of the contract + function governanceRegisterGateway() public { + initializeConfig(); + + IBridgehub bridgehub = IBridgehub(config.bridgehub); + + if (bridgehub.whitelistedSettlementLayers(config.gatewayChainId)) { + console.log("Chain already whitelisted as settlement layer"); + } else { + bytes memory data = abi.encodeCall(bridgehub.registerSettlementLayer, (config.gatewayChainId, true)); + Utils.executeUpgrade({ + _governor: config.governance, + _salt: bytes32(0), + _target: address(bridgehub), + _data: data, + _value: 0, + _delay: 0 + }); + console.log("Gateway whitelisted as settlement layer"); + } + // No tx has been executed, so we save an empty hash + saveOutput(bytes32(0)); + } + + /// @dev Requires the sender to be the owner of the contract + function governanceWhitelistGatewayCTM(address gatewayCTMAddress, bytes32 governanoceOperationSalt) public { + initializeConfig(); + + bytes memory data = abi.encodeCall(IBridgehub.addChainTypeManager, (gatewayCTMAddress)); + + bytes32 l2TxHash = Utils.runGovernanceL1L2DirectTransaction( + _getL1GasPrice(), + config.governance, + governanoceOperationSalt, + data, + Utils.MAX_PRIORITY_TX_GAS, + new bytes[](0), + L2_BRIDGEHUB_ADDRESS, + config.gatewayChainId, + config.bridgehub, + config.sharedBridgeProxy + ); + + saveOutput(l2TxHash); + } + + function governanceSetCTMAssetHandler(bytes32 governanoceOperationSalt) public { + initializeConfig(); + + bytes32 assetId = IBridgehub(config.bridgehub).ctmAssetId(config.chainTypeManagerProxy); + + // This should be equivalent to `config.chainTypeManagerProxy`, but we just double checking to ensure that + // bridgehub was initialized correctly + address ctmAddress = IBridgehub(config.bridgehub).ctmAssetIdToAddress(assetId); + require(ctmAddress == config.chainTypeManagerProxy, "CTM asset id does not match the expected CTM address"); + + bytes memory secondBridgeData = abi.encodePacked( + SET_ASSET_HANDLER_COUNTERPART_ENCODING_VERSION, + abi.encode(assetId, L2_BRIDGEHUB_ADDRESS) + ); + + bytes32 l2TxHash = Utils.runGovernanceL1L2TwoBridgesTransaction( + _getL1GasPrice(), + config.governance, + governanoceOperationSalt, + Utils.MAX_PRIORITY_TX_GAS, + config.gatewayChainId, + config.bridgehub, + config.sharedBridgeProxy, + config.sharedBridgeProxy, + 0, + secondBridgeData + ); + + saveOutput(l2TxHash); + } + + function registerAssetIdInBridgehub(address gatewayCTMAddress, bytes32 governanoceOperationSalt) public { + initializeConfig(); + + bytes memory secondBridgeData = abi.encodePacked( + bytes1(0x01), + abi.encode(config.chainTypeManagerProxy, gatewayCTMAddress) + ); + + bytes32 l2TxHash = Utils.runGovernanceL1L2TwoBridgesTransaction( + _getL1GasPrice(), + config.governance, + governanoceOperationSalt, + Utils.MAX_PRIORITY_TX_GAS, + config.gatewayChainId, + config.bridgehub, + config.sharedBridgeProxy, + config.ctmDeploymentTracker, + 0, + secondBridgeData + ); + + saveOutput(l2TxHash); + } + + /// @dev Calling this function requires private key to the admin of the chain + function migrateChainToGateway(address chainAdmin, address accessControlRestriction, uint256 chainId) public { + initializeConfig(); + + // TODO(EVM-746): Use L2-based chain admin contract + address l2ChainAdmin = AddressAliasHelper.applyL1ToL2Alias(chainAdmin); + + bytes32 chainAssetId = IBridgehub(config.bridgehub).ctmAssetIdFromChainId(chainId); + + uint256 currentSettlementLayer = IBridgehub(config.bridgehub).settlementLayer(chainId); + if (currentSettlementLayer == config.gatewayChainId) { + console.log("Chain already whitelisted as settlement layer"); + saveOutput(bytes32(0)); + return; + } + + bytes memory bridgehubData = abi.encode( + BridgehubBurnCTMAssetData({ + chainId: chainId, + ctmData: abi.encode(l2ChainAdmin, config.gatewayDiamondCutData), + chainData: abi.encode(IZKChain(IBridgehub(config.bridgehub).getZKChain(chainId)).getProtocolVersion()) + }) + ); + + // TODO: use constant for the 0x01 + bytes memory secondBridgeData = abi.encodePacked(bytes1(0x01), abi.encode(chainAssetId, bridgehubData)); + + bytes32 l2TxHash = Utils.runAdminL1L2TwoBridgesTransaction( + _getL1GasPrice(), + chainAdmin, + accessControlRestriction, + Utils.MAX_PRIORITY_TX_GAS, + config.gatewayChainId, + config.bridgehub, + config.sharedBridgeProxy, + config.sharedBridgeProxy, + 0, + secondBridgeData + ); + + saveOutput(l2TxHash); + } + + /// @dev Calling this function requires private key to the admin of the chain + function setDAValidatorPair( + address chainAdmin, + address accessControlRestriction, + uint256 chainId, + address l1DAValidator, + address l2DAValidator, + address chainDiamondProxyOnGateway + ) public { + initializeConfig(); + + bytes memory data = abi.encodeCall(IAdmin.setDAValidatorPair, (l1DAValidator, l2DAValidator)); + + bytes32 l2TxHash = Utils.runAdminL1L2DirectTransaction( + _getL1GasPrice(), + chainAdmin, + accessControlRestriction, + data, + Utils.MAX_PRIORITY_TX_GAS, + new bytes[](0), + chainDiamondProxyOnGateway, + config.gatewayChainId, + config.bridgehub, + config.sharedBridgeProxy + ); + + saveOutput(l2TxHash); + } + + function enableValidator( + address chainAdmin, + address accessControlRestriction, + uint256 chainId, + address validatorAddress, + address gatewayValidatorTimelock + ) public { + initializeConfig(); + + bytes memory data = abi.encodeCall(ValidatorTimelock.addValidator, (chainId, validatorAddress)); + + bytes32 l2TxHash = Utils.runAdminL1L2DirectTransaction( + _getL1GasPrice(), + chainAdmin, + accessControlRestriction, + data, + Utils.MAX_PRIORITY_TX_GAS, + new bytes[](0), + gatewayValidatorTimelock, + config.gatewayChainId, + config.bridgehub, + config.sharedBridgeProxy + ); + + saveOutput(l2TxHash); + } + + /// TODO(EVM-748): make that function support non-ETH based chains + function supplyGatewayWallet(address addr, uint256 amount) public { + initializeConfig(); + + Utils.runL1L2Transaction( + hex"", + Utils.MAX_PRIORITY_TX_GAS, + amount, + new bytes[](0), + addr, + config.gatewayChainId, + config.bridgehub, + config.sharedBridgeProxy + ); + + // We record L2 tx hash only for governance operations + saveOutput(bytes32(0)); + } + + /// The caller of this function should have private key of the admin of the *gateway* + function deployAndSetGatewayTransactionFilterer() public { + initializeConfig(); + + vm.broadcast(); + GatewayTransactionFilterer impl = new GatewayTransactionFilterer( + IBridgehub(config.bridgehub), + config.sharedBridgeProxy + ); + + vm.broadcast(); + TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( + address(impl), + config.gatewayChainProxyAdmin, + abi.encodeCall(GatewayTransactionFilterer.initialize, (config.gatewayChainAdmin)) + ); + + GatewayTransactionFilterer proxyAsFilterer = GatewayTransactionFilterer(address(proxy)); + + IZKChain chain = IZKChain(IBridgehub(config.bridgehub).getZKChain(config.gatewayChainId)); + + // Firstly, we set the filterer + Utils.adminExecute({ + _admin: config.gatewayChainAdmin, + _accessControlRestriction: config.gatewayAccessControlRestriction, + _target: address(chain), + _data: abi.encodeCall(IAdmin.setTransactionFilterer, (address(proxyAsFilterer))), + _value: 0 + }); + + _grantWhitelist(address(proxy), config.gatewayChainAdmin); + _grantWhitelist(address(proxy), config.sharedBridgeProxy); + _grantWhitelist(address(proxy), config.ctmDeploymentTracker); + + // Then, we grant the whitelist to a few addresses + + saveOutput(address(impl), address(proxy)); + } + + function grantWhitelist(address filtererProxy, address[] memory addresses) public { + initializeConfig(); + + for (uint256 i = 0; i < addresses.length; i++) { + if (GatewayTransactionFilterer(filtererProxy).whitelistedSenders(addresses[i])) { + console.log("Address already whitelisted: ", addresses[i]); + } else { + _grantWhitelist(filtererProxy, addresses[i]); + } + } + } + + function _grantWhitelist(address filtererProxy, address addr) internal { + Utils.adminExecute({ + _admin: config.gatewayChainAdmin, + _accessControlRestriction: config.gatewayAccessControlRestriction, + _target: address(filtererProxy), + _data: abi.encodeCall(GatewayTransactionFilterer.grantWhitelist, (addr)), + _value: 0 + }); + } +} diff --git a/l1-contracts/deploy-scripts/GenerateForceDeploymentsData.s.sol b/l1-contracts/deploy-scripts/GenerateForceDeploymentsData.s.sol deleted file mode 100644 index 1c2db4d7d..000000000 --- a/l1-contracts/deploy-scripts/GenerateForceDeploymentsData.s.sol +++ /dev/null @@ -1,100 +0,0 @@ -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 {L2_BRIDGEHUB_ADDR, L2_ASSET_ROUTER_ADDR, L2_NATIVE_TOKEN_VAULT_ADDR, L2_MESSAGE_ROOT_ADDR} from "contracts/common/L2ContractAddresses.sol"; -import {AddressAliasHelper} from "contracts/vendor/AddressAliasHelper.sol"; -import {ForceDeployment} from "contracts/state-transition/l2-deps/IL2GenesisUpgrade.sol"; - -contract GenerateForceDeploymentsData is Script { - using stdToml for string; - - Config internal config; - ContractsBytecodes internal contracts; - - // solhint-disable-next-line gas-struct-packing - struct Config { - address l1AssetRouterProxy; - address governance; - uint256 chainId; - uint256 eraChainId; - bytes forceDeploymentsData; - address l2LegacySharedBridge; - address l2TokenBeacon; - bool contractsDeployedAlready; - } - - struct ContractsBytecodes { - bytes bridgehubBytecode; - bytes l2AssetRouterBytecode; - bytes l2NtvBytecode; - bytes l2StandardErc20FactoryBytecode; - bytes l2TokenProxyBytecode; - bytes l2StandardErc20Bytecode; - bytes messageRootBytecode; - } - - function run() public { - initializeConfig(); - loadContracts(); - - genesisForceDeploymentsData(); - - 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( - "/artifacts-zk/@openzeppelin/contracts-v4/proxy/beacon/UpgradeableBeacon.sol/UpgradeableBeacon.json" - ); - contracts.l2TokenProxyBytecode = Utils.readHardhatBytecode( - "/artifacts-zk/@openzeppelin/contracts-v4/proxy/beacon/BeaconProxy.sol/BeaconProxy.json" - ); - contracts.l2StandardErc20Bytecode = Utils.readHardhatBytecode( - "/artifacts-zk/contracts/bridge/BridgedStandardERC20.sol/BridgedStandardERC20.json" - ); - - contracts.l2AssetRouterBytecode = Utils.readHardhatBytecode( - "/artifacts-zk/contracts/bridge/asset-router/L2AssetRouter.sol/L2AssetRouter.json" - ); - contracts.bridgehubBytecode = Utils.readHardhatBytecode( - "/../l1-contracts/artifacts-zk/contracts/bridgehub/Bridgehub.sol/Bridgehub.json" - ); - contracts.messageRootBytecode = Utils.readHardhatBytecode( - "/../l1-contracts/artifacts-zk/contracts/bridgehub/MessageRoot.sol/MessageRoot.json" - ); - contracts.l2NtvBytecode = Utils.readHardhatBytecode( - "/artifacts-zk/contracts/bridge/ntv/L2NativeTokenVault.sol/L2NativeTokenVault.json" - ); - } - - function initializeConfig() internal { - string memory root = vm.projectRoot(); - string memory path = string.concat(root, vm.envString("FORCE_DEPLOYMENTS_CONFIG")); - string memory toml = vm.readFile(path); - config.governance = toml.readAddress("$.governance"); - config.l1AssetRouterProxy = toml.readAddress("$.l1_shared_bridge"); - config.chainId = toml.readUint("$.chain_id"); - config.eraChainId = toml.readUint("$.era_chain_id"); - config.l2LegacySharedBridge = toml.readAddress("$.l2_legacy_shared_bridge"); - config.l2TokenBeacon = toml.readAddress("$.l2_token_beacon"); - config.contractsDeployedAlready = toml.readBool("$.l2_contracts_deployed_already"); - } - - function saveOutput() internal { - string memory toml = vm.serializeBytes("root", "force_deployments_data", config.forceDeploymentsData); - string memory root = vm.projectRoot(); - string memory path = string.concat(root, "/script-out/output-force-deployments-data.toml"); - vm.writeToml(toml, path); - } - - function genesisForceDeploymentsData() internal { - address aliasedGovernance = AddressAliasHelper.applyL1ToL2Alias(config.governance); - ForceDeployment[] memory forceDeployments = new ForceDeployment[](4); - - config.forceDeploymentsData = abi.encode(forceDeployments); - } -} diff --git a/l1-contracts/deploy-scripts/L2ContractsBytecodesLib.sol b/l1-contracts/deploy-scripts/L2ContractsBytecodesLib.sol new file mode 100644 index 000000000..5249d28d0 --- /dev/null +++ b/l1-contracts/deploy-scripts/L2ContractsBytecodesLib.sol @@ -0,0 +1,250 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "./Utils.sol"; + +/// @title L2ContractsBytecodesLib +/// @notice Library providing functions to read bytecodes of L2 contracts individually. +library L2ContractsBytecodesLib { + /// @notice Reads the bytecode of the Bridgehub contract. + /// @return The bytecode of the Bridgehub contract. + function readBridgehubBytecode() internal view returns (bytes memory) { + return + Utils.readHardhatBytecode("/../l1-contracts/artifacts-zk/contracts/bridgehub/Bridgehub.sol/Bridgehub.json"); + } + + /// @notice Reads the bytecode of the L2NativeTokenVault contract. + /// @return The bytecode of the L2NativeTokenVault contract. + function readL2NativeTokenVaultBytecode() internal view returns (bytes memory) { + return + Utils.readHardhatBytecode( + "/../l1-contracts/artifacts-zk/contracts/bridge/ntv/L2NativeTokenVault.sol/L2NativeTokenVault.json" + ); + } + + /// @notice Reads the bytecode of the L2AssetRouter contract. + /// @return The bytecode of the L2AssetRouter contract. + function readL2AssetRouterBytecode() internal view returns (bytes memory) { + return + Utils.readHardhatBytecode( + "/../l1-contracts/artifacts-zk/contracts/bridge/asset-router/L2AssetRouter.sol/L2AssetRouter.json" + ); + } + + /// @notice Reads the bytecode of the MessageRoot contract. + /// @return The bytecode of the MessageRoot contract. + function readMessageRootBytecode() internal view returns (bytes memory) { + return + Utils.readHardhatBytecode( + "/../l1-contracts/artifacts-zk/contracts/bridgehub/MessageRoot.sol/MessageRoot.json" + ); + } + + /// @notice Reads the bytecode of the UpgradeableBeacon contract. + /// @return The bytecode of the UpgradeableBeacon contract. + function readUpgradeableBeaconBytecode() internal view returns (bytes memory) { + return + Utils.readHardhatBytecode( + "/../l1-contracts/artifacts-zk/@openzeppelin/contracts-v4/proxy/beacon/UpgradeableBeacon.sol/UpgradeableBeacon.json" + ); + } + + /// @notice Reads the bytecode of the BeaconProxy contract. + /// @return The bytecode of the BeaconProxy contract. + function readBeaconProxyBytecode() internal view returns (bytes memory) { + return + Utils.readHardhatBytecode( + "/../l1-contracts/artifacts-zk/@openzeppelin/contracts-v4/proxy/beacon/BeaconProxy.sol/BeaconProxy.json" + ); + } + + /// @notice Reads the bytecode of the BridgedStandardERC20 contract. + /// @return The bytecode of the BridgedStandardERC20 contract. + function readStandardERC20Bytecode() internal view returns (bytes memory) { + return + Utils.readHardhatBytecode( + "/../l1-contracts/artifacts-zk/contracts/bridge/BridgedStandardERC20.sol/BridgedStandardERC20.json" + ); + } + + /// @notice Reads the bytecode of the TransparentUpgradeableProxy contract. + /// @return The bytecode of the TransparentUpgradeableProxy contract. + function readTransparentUpgradeableProxyBytecode() internal view returns (bytes memory) { + return + Utils.readHardhatBytecode( + "/../l1-contracts/artifacts-zk/@openzeppelin/contracts-v4/proxy/transparent/TransparentUpgradeableProxy.sol/TransparentUpgradeableProxy.json" + ); + } + + /// @notice Reads the bytecode of the ForceDeployUpgrader contract. + /// @return The bytecode of the ForceDeployUpgrader contract. + function readForceDeployUpgraderBytecode() internal view returns (bytes memory) { + return + Utils.readHardhatBytecode( + "/../l2-contracts/artifacts-zk/contracts/ForceDeployUpgrader.sol/ForceDeployUpgrader.json" + ); + } + + /// @notice Reads the bytecode of the RollupL2DAValidator contract. + /// @return The bytecode of the RollupL2DAValidator contract. + function readRollupL2DAValidatorBytecode() internal view returns (bytes memory) { + return + Utils.readHardhatBytecode( + "/../l2-contracts/artifacts-zk/contracts/data-availability/RollupL2DAValidator.sol/RollupL2DAValidator.json" + ); + } + + /// @notice Reads the bytecode of the ValidiumL2DAValidator contract. + /// @return The bytecode of the ValidiumL2DAValidator contract. + function readValidiumL2DAValidatorBytecode() internal view returns (bytes memory) { + return + Utils.readHardhatBytecode( + "/../l2-contracts/artifacts-zk/contracts/data-availability/ValidiumL2DAValidator.sol/ValidiumL2DAValidator.json" + ); + } + + /// @notice Reads the bytecode of the ChainTypeManager contract. + /// @return The bytecode of the ChainTypeManager contract. + function readChainTypeManagerBytecode() internal view returns (bytes memory) { + return + Utils.readHardhatBytecode( + "/../l1-contracts/artifacts-zk/contracts/state-transition/ChainTypeManager.sol/ChainTypeManager.json" + ); + } + + /// @notice Reads the bytecode of the AdminFacet contract. + /// @return The bytecode of the AdminFacet contract. + function readAdminFacetBytecode() internal view returns (bytes memory) { + return + Utils.readHardhatBytecode( + "/../l1-contracts/artifacts-zk/contracts/state-transition/chain-deps/facets/Admin.sol/AdminFacet.json" + ); + } + + /// @notice Reads the bytecode of the MailboxFacet contract. + /// @return The bytecode of the MailboxFacet contract. + function readMailboxFacetBytecode() internal view returns (bytes memory) { + return + Utils.readHardhatBytecode( + "/../l1-contracts/artifacts-zk/contracts/state-transition/chain-deps/facets/Mailbox.sol/MailboxFacet.json" + ); + } + + /// @notice Reads the bytecode of the ExecutorFacet contract. + /// @return The bytecode of the ExecutorFacet contract. + function readExecutorFacetBytecode() internal view returns (bytes memory) { + return + Utils.readHardhatBytecode( + "/../l1-contracts/artifacts-zk/contracts/state-transition/chain-deps/facets/Executor.sol/ExecutorFacet.json" + ); + } + + /// @notice Reads the bytecode of the GettersFacet contract. + /// @return The bytecode of the GettersFacet contract. + function readGettersFacetBytecode() internal view returns (bytes memory) { + return + Utils.readHardhatBytecode( + "/../l1-contracts/artifacts-zk/contracts/state-transition/chain-deps/facets/Getters.sol/GettersFacet.json" + ); + } + + // FIXME: replace it with L2 verifier + /// @notice Reads the bytecode of the Verifier contract. + /// @return The bytecode of the Verifier contract. + function readVerifierBytecode() internal view returns (bytes memory) { + return + Utils.readHardhatBytecode( + "/../l1-contracts/artifacts-zk/contracts/state-transition/Verifier.sol/Verifier.json" + ); + } + + /// @notice Reads the bytecode of the Verifier contract. + /// @return The bytecode of the Verifier contract. + function readConsensusRegistryBytecode() internal view returns (bytes memory) { + return + Utils.readHardhatBytecode( + "/../l2-contracts/artifacts-zk/contracts/ConsensusRegistry.sol/ConsensusRegistry.json" + ); + } + + /// @notice Reads the bytecode of the TestnetVerifier contract. + /// @return The bytecode of the TestnetVerifier contract. + function readTestnetVerifierBytecode() internal view returns (bytes memory) { + return + Utils.readHardhatBytecode( + "/../l1-contracts/artifacts-zk/contracts/state-transition/TestnetVerifier.sol/TestnetVerifier.json" + ); + } + + /// @notice Reads the bytecode of the ValidatorTimelock contract. + /// @return The bytecode of the ValidatorTimelock contract. + function readValidatorTimelockBytecode() internal view returns (bytes memory) { + return + Utils.readHardhatBytecode( + "/../l1-contracts/artifacts-zk/contracts/state-transition/ValidatorTimelock.sol/ValidatorTimelock.json" + ); + } + + /// @notice Reads the bytecode of the DiamondInit contract. + /// @return The bytecode of the DiamondInit contract. + function readDiamondInitBytecode() internal view returns (bytes memory) { + return + Utils.readHardhatBytecode( + "/../l1-contracts/artifacts-zk/contracts/state-transition/chain-deps/DiamondInit.sol/DiamondInit.json" + ); + } + + /// @notice Reads the bytecode of the DiamondProxy contract. + /// @return The bytecode of the DiamondProxy contract. + function readDiamondProxyBytecode() internal view returns (bytes memory) { + return + Utils.readHardhatBytecode( + "/../l1-contracts/artifacts-zk/contracts/state-transition/chain-deps/DiamondProxy.sol/DiamondProxy.json" + ); + } + + /// @notice Reads the bytecode of the L1GenesisUpgrade contract. + /// @return The bytecode of the L1GenesisUpgrade contract. + function readL1GenesisUpgradeBytecode() internal view returns (bytes memory) { + return + Utils.readHardhatBytecode( + "/../l1-contracts/artifacts-zk/contracts/upgrades/L1GenesisUpgrade.sol/L1GenesisUpgrade.json" + ); + } + + /// @notice Reads the bytecode of the DefaultUpgrade contract. + /// @return The bytecode of the DefaultUpgrade contract. + function readDefaultUpgradeBytecode() internal view returns (bytes memory) { + return + Utils.readHardhatBytecode( + "/../l1-contracts/artifacts-zk/contracts/upgrades/DefaultUpgrade.sol/DefaultUpgrade.json" + ); + } + + /// @notice Reads the bytecode of the Multicall3 contract. + /// @return The bytecode of the Multicall3 contract. + function readMulticall3Bytecode() internal view returns (bytes memory) { + return + Utils.readHardhatBytecode( + "/../l1-contracts/artifacts-zk/contracts/dev-contracts/Multicall3.sol/Multicall3.json" + ); + } + + /// @notice Reads the bytecode of the RelayedSLDAValidator contract. + /// @return The bytecode of the RelayedSLDAValidator contract. + function readRelayedSLDAValidatorBytecode() internal view returns (bytes memory) { + return + Utils.readHardhatBytecode( + "/../l1-contracts/artifacts-zk/contracts/state-transition/data-availability/RelayedSLDAValidator.sol/RelayedSLDAValidator.json" + ); + } + + /// @notice Reads the bytecode of the L2SharedBridgeLegacy contract. + /// @return The bytecode of the L2SharedBridgeLegacy contract. + function readL2LegacySharedBridgeBytecode() internal view returns (bytes memory) { + return + Utils.readHardhatBytecode( + "/../l1-contracts/artifacts-zk/contracts/bridge/L2SharedBridgeLegacy.sol/L2SharedBridgeLegacy.json" + ); + } +} diff --git a/l1-contracts/deploy-scripts/RegisterZKChain.s.sol b/l1-contracts/deploy-scripts/RegisterZKChain.s.sol index 63c230741..3a374e674 100644 --- a/l1-contracts/deploy-scripts/RegisterZKChain.s.sol +++ b/l1-contracts/deploy-scripts/RegisterZKChain.s.sol @@ -7,6 +7,7 @@ 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 {ProxyAdmin} from "@openzeppelin/contracts-v4/proxy/transparent/ProxyAdmin.sol"; import {Ownable} from "@openzeppelin/contracts-v4/access/Ownable.sol"; import {IBridgehub} from "contracts/bridgehub/IBridgehub.sol"; import {IZKChain} from "contracts/state-transition/chain-interfaces/IZKChain.sol"; @@ -15,9 +16,15 @@ import {Governance} from "contracts/governance/Governance.sol"; import {ChainAdmin} from "contracts/governance/ChainAdmin.sol"; import {AccessControlRestriction} from "contracts/governance/AccessControlRestriction.sol"; import {Utils} from "./Utils.sol"; +import {L2ContractsBytecodesLib} from "./L2ContractsBytecodesLib.sol"; import {PubdataPricingMode} from "contracts/state-transition/chain-deps/ZKChainStorage.sol"; +import {IL1NativeTokenVault} from "contracts/bridge/ntv/IL1NativeTokenVault.sol"; import {INativeTokenVault} from "contracts/bridge/ntv/INativeTokenVault.sol"; import {DataEncoding} from "contracts/common/libraries/DataEncoding.sol"; +import {L2ContractHelper} from "contracts/common/libraries/L2ContractHelper.sol"; +import {L1NullifierDev} from "contracts/dev-contracts/L1NullifierDev.sol"; +import {L2SharedBridgeLegacy} from "contracts/bridge/L2SharedBridgeLegacy.sol"; +import {AddressAliasHelper} from "contracts/vendor/AddressAliasHelper.sol"; import {ETH_TOKEN_ADDRESS} from "contracts/common/Config.sol"; @@ -41,27 +48,67 @@ contract RegisterZKChainScript is Script { uint128 baseTokenGasPriceMultiplierNominator; uint128 baseTokenGasPriceMultiplierDenominator; address bridgehub; + // TODO(EVM-744): maybe rename to asset router + address sharedBridgeProxy; address nativeTokenVault; - address stateTransitionProxy; + address chainTypeManagerProxy; address validatorTimelock; bytes diamondCutData; bytes forceDeployments; address governanceSecurityCouncilAddress; uint256 governanceMinDelay; - address newDiamondProxy; + address l1Nullifier; + } + + struct Output { address governance; + address diamondProxy; address chainAdmin; + address l2LegacySharedBridge; + address accessControlRestrictionAddress; + address chainProxyAdmin; + } + + struct LegacySharedBridgeParams { + bytes implementationConstructorParams; + address implementationAddress; + bytes proxyConstructorParams; + address proxyAddress; } + LegacySharedBridgeParams internal legacySharedBridgeParams; + Config internal config; + Output internal output; function run() public { console.log("Deploying ZKChain"); initializeConfig(); + // TODO: some chains may not want to have a legacy shared bridge + runInner("/script-out/output-register-zk-chain.toml", true); + } + + function runForTest() public { + console.log("Deploying ZKChain"); + + initializeConfigTest(); + // TODO: Yes, it is the same as for prod since it is never read from down the line + runInner(vm.envString("ZK_CHAIN_OUT"), false); + } + + function runInner(string memory outputPath, bool initializeL2LegacyBridge) internal { + string memory root = vm.projectRoot(); + outputPath = string.concat(root, outputPath); + + if (initializeL2LegacyBridge) { + // This must be run before the chain is deployed + setUpLegacySharedBridgeParams(); + } deployGovernance(); deployChainAdmin(); + deployChainProxyAddress(); checkTokenAddress(); registerAssetIdOnBridgehub(); registerTokenOnNTV(); @@ -70,10 +117,57 @@ contract RegisterZKChainScript is Script { configureZkSyncStateTransition(); setPendingAdmin(); - saveOutput(); + if (initializeL2LegacyBridge) { + deployLegacySharedBridge(); + } + + saveOutput(outputPath); } function initializeConfig() internal { + // Grab config from output of l1 deployment + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/script-config/register-zk-chain.toml"); + string memory toml = vm.readFile(path); + + config.deployerAddress = msg.sender; + + // Config file must be parsed key by key, otherwise values returned + // are parsed alfabetically and not by key. + // https://book.getfoundry.sh/cheatcodes/parse-toml + + config.bridgehub = toml.readAddress("$.deployed_addresses.bridgehub.bridgehub_proxy_addr"); + config.chainTypeManagerProxy = toml.readAddress( + "$.deployed_addresses.state_transition.chain_type_manager_proxy_addr" + ); + config.validatorTimelock = toml.readAddress("$.deployed_addresses.validator_timelock_addr"); + // config.bridgehubGovernance = toml.readAddress("$.deployed_addresses.governance_addr"); + config.nativeTokenVault = toml.readAddress("$.deployed_addresses.native_token_vault_addr"); + config.sharedBridgeProxy = toml.readAddress("$.deployed_addresses.bridges.shared_bridge_proxy_addr"); + config.l1Nullifier = toml.readAddress("$.deployed_addresses.bridges.l1_nullifier_proxy_addr"); + + config.diamondCutData = toml.readBytes("$.contracts_config.diamond_cut_data"); + config.forceDeployments = toml.readBytes("$.contracts_config.force_deployments_data"); + + config.ownerAddress = toml.readAddress("$.owner_address"); + + config.chainChainId = toml.readUint("$.chain.chain_chain_id"); + config.baseTokenGasPriceMultiplierNominator = uint128( + toml.readUint("$.chain.base_token_gas_price_multiplier_nominator") + ); + config.baseTokenGasPriceMultiplierDenominator = uint128( + toml.readUint("$.chain.base_token_gas_price_multiplier_denominator") + ); + config.baseToken = toml.readAddress("$.chain.base_token_addr"); + config.governanceSecurityCouncilAddress = toml.readAddress("$.chain.governance_security_council_address"); + config.governanceMinDelay = uint256(toml.readUint("$.chain.governance_min_delay")); + config.bridgehubCreateNewChainSalt = toml.readUint("$.chain.bridgehub_create_new_chain_salt"); + config.validiumMode = toml.readBool("$.chain.validium_mode"); + config.validatorSenderOperatorCommitEth = toml.readAddress("$.chain.validator_sender_operator_commit_eth"); + config.validatorSenderOperatorBlobsEth = toml.readAddress("$.chain.validator_sender_operator_blobs_eth"); + } + + function initializeConfigTest() internal { // Grab config from output of l1 deployment string memory root = vm.projectRoot(); string memory path = string.concat(root, vm.envString("L1_OUTPUT")); //"/script-config/register-zkChain.toml"); @@ -86,14 +180,19 @@ contract RegisterZKChainScript is Script { // https://book.getfoundry.sh/cheatcodes/parse-toml config.bridgehub = toml.readAddress("$.deployed_addresses.bridgehub.bridgehub_proxy_addr"); - config.stateTransitionProxy = toml.readAddress( + // TODO(EVM-744): name of the key is a bit inconsistent + config.chainTypeManagerProxy = toml.readAddress( "$.deployed_addresses.state_transition.state_transition_proxy_addr" ); config.validatorTimelock = toml.readAddress("$.deployed_addresses.validator_timelock_addr"); // config.bridgehubGovernance = toml.readAddress("$.deployed_addresses.governance_addr"); config.nativeTokenVault = toml.readAddress("$.deployed_addresses.native_token_vault_addr"); + config.sharedBridgeProxy = toml.readAddress("$.deployed_addresses.bridges.shared_bridge_proxy_addr"); + config.l1Nullifier = toml.readAddress("$.deployed_addresses.bridges.l1_nullifier_proxy_addr"); + config.diamondCutData = toml.readBytes("$.contracts_config.diamond_cut_data"); config.forceDeployments = toml.readBytes("$.contracts_config.force_deployments_data"); + path = string.concat(root, vm.envString("ZK_CHAIN_CONFIG")); toml = vm.readFile(path); @@ -115,6 +214,10 @@ contract RegisterZKChainScript is Script { config.governanceSecurityCouncilAddress = toml.readAddress("$.chain.governance_security_council_address"); } + function getOwnerAddress() public view returns (address) { + return config.ownerAddress; + } + function checkTokenAddress() internal view { if (config.baseToken == address(0)) { revert("Token address is not set"); @@ -132,6 +235,52 @@ contract RegisterZKChainScript is Script { console.log("Using base token address:", config.baseToken); } + function setUpLegacySharedBridgeParams() internal { + bytes memory implementationConstructorParams = hex""; + + address legacyBridgeImplementationAddress = L2ContractHelper.computeCreate2Address( + msg.sender, + "", + L2ContractHelper.hashL2Bytecode(L2ContractsBytecodesLib.readL2LegacySharedBridgeBytecode()), + keccak256(implementationConstructorParams) + ); + + bytes memory proxyInitializationParams = abi.encodeCall( + L2SharedBridgeLegacy.initialize, + ( + config.sharedBridgeProxy, + L2ContractHelper.hashL2Bytecode(L2ContractsBytecodesLib.readBeaconProxyBytecode()), + // This is not exactly correct, this should be ecosystem governance and not chain governance + msg.sender + ) + ); + + bytes memory proxyConstructorParams = abi.encode( + legacyBridgeImplementationAddress, + // In real production, this would be aliased ecosystem governance. + // But in real production we also do not initialize legacy shared bridge + msg.sender, + proxyInitializationParams + ); + + address proxyAddress = L2ContractHelper.computeCreate2Address( + msg.sender, + "", + L2ContractHelper.hashL2Bytecode(L2ContractsBytecodesLib.readTransparentUpgradeableProxyBytecode()), + keccak256(proxyConstructorParams) + ); + + vm.broadcast(); + L1NullifierDev(config.l1Nullifier).setL2LegacySharedBridge(config.chainChainId, proxyAddress); + + legacySharedBridgeParams = LegacySharedBridgeParams({ + implementationConstructorParams: implementationConstructorParams, + implementationAddress: legacyBridgeImplementationAddress, + proxyConstructorParams: proxyConstructorParams, + proxyAddress: proxyAddress + }); + } + function registerAssetIdOnBridgehub() internal { IBridgehub bridgehub = IBridgehub(config.bridgehub); Ownable ownable = Ownable(config.bridgehub); @@ -162,6 +311,7 @@ contract RegisterZKChainScript is Script { console.log("Token already registered on NTV"); } else { // bytes memory data = abi.encodeCall(ntv.registerToken, (config.baseToken)); + vm.broadcast(); ntv.registerToken(config.baseToken); console.log("Token registered on NTV"); } @@ -175,19 +325,20 @@ contract RegisterZKChainScript is Script { config.governanceMinDelay ); console.log("Governance deployed at:", address(governance)); - config.governance = address(governance); + output.governance = address(governance); } function deployChainAdmin() internal { vm.broadcast(); AccessControlRestriction restriction = new AccessControlRestriction(0, config.ownerAddress); + output.accessControlRestrictionAddress = address(restriction); address[] memory restrictions = new address[](1); restrictions[0] = address(restriction); vm.broadcast(); ChainAdmin chainAdmin = new ChainAdmin(restrictions); - config.chainAdmin = address(chainAdmin); + output.chainAdmin = address(chainAdmin); } function registerZKChain() internal { @@ -199,12 +350,12 @@ contract RegisterZKChainScript is Script { bridgehub.createNewChain, ( config.chainChainId, - config.stateTransitionProxy, + config.chainTypeManagerProxy, config.baseTokenAssetId, config.bridgehubCreateNewChainSalt, msg.sender, abi.encode(config.diamondCutData, config.forceDeployments), - new bytes[](0) + getFactoryDeps() ) ); Utils.executeUpgrade({ @@ -230,7 +381,7 @@ contract RegisterZKChainScript is Script { if (diamondProxyAddress == address(0)) { revert("Diamond proxy address not found"); } - config.newDiamondProxy = diamondProxyAddress; + output.diamondProxy = diamondProxyAddress; console.log("ZKChain diamond proxy deployed at:", diamondProxyAddress); } @@ -246,7 +397,7 @@ contract RegisterZKChainScript is Script { } function configureZkSyncStateTransition() internal { - IZKChain zkChain = IZKChain(config.newDiamondProxy); + IZKChain zkChain = IZKChain(output.diamondProxy); vm.startBroadcast(msg.sender); zkChain.setTokenMultiplier( @@ -263,21 +414,74 @@ contract RegisterZKChainScript is Script { } function setPendingAdmin() internal { - IZKChain zkChain = IZKChain(config.newDiamondProxy); + IZKChain zkChain = IZKChain(output.diamondProxy); vm.startBroadcast(msg.sender); - zkChain.setPendingAdmin(config.chainAdmin); + zkChain.setPendingAdmin(output.chainAdmin); vm.stopBroadcast(); - console.log("Owner for ", config.newDiamondProxy, "set to", config.chainAdmin); + console.log("Owner for ", output.diamondProxy, "set to", output.chainAdmin); } - function saveOutput() internal { - vm.serializeAddress("root", "diamond_proxy_addr", config.newDiamondProxy); - vm.serializeAddress("root", "chain_admin_addr", config.chainAdmin); - string memory toml = vm.serializeAddress("root", "governance_addr", config.governance); + function deployChainProxyAddress() internal { + vm.startBroadcast(); + ProxyAdmin proxyAdmin = new ProxyAdmin(); + proxyAdmin.transferOwnership(output.chainAdmin); + vm.stopBroadcast(); + console.log("Transparent Proxy Admin deployed at:", address(proxyAdmin)); + output.chainProxyAdmin = address(proxyAdmin); + } + + function deployLegacySharedBridge() internal { + bytes[] memory emptyDeps = new bytes[](0); + address correctLegacyBridgeImplAddr = Utils.deployThroughL1({ + bytecode: L2ContractsBytecodesLib.readL2LegacySharedBridgeBytecode(), + constructorargs: legacySharedBridgeParams.implementationConstructorParams, + create2salt: "", + l2GasLimit: Utils.MAX_PRIORITY_TX_GAS, + factoryDeps: emptyDeps, + chainId: config.chainChainId, + bridgehubAddress: config.bridgehub, + l1SharedBridgeProxy: config.sharedBridgeProxy + }); + + address correctProxyAddress = Utils.deployThroughL1({ + bytecode: L2ContractsBytecodesLib.readTransparentUpgradeableProxyBytecode(), + constructorargs: legacySharedBridgeParams.proxyConstructorParams, + create2salt: "", + l2GasLimit: Utils.MAX_PRIORITY_TX_GAS, + factoryDeps: emptyDeps, + chainId: config.chainChainId, + bridgehubAddress: config.bridgehub, + l1SharedBridgeProxy: config.sharedBridgeProxy + }); + + require( + correctLegacyBridgeImplAddr == legacySharedBridgeParams.implementationAddress, + "Legacy bridge implementation address mismatch" + ); + require(correctProxyAddress == legacySharedBridgeParams.proxyAddress, "Legacy bridge proxy address mismatch"); + + output.l2LegacySharedBridge = correctProxyAddress; + } + + function getFactoryDeps() internal view returns (bytes[] memory) { + bytes[] memory factoryDeps = new bytes[](3); + factoryDeps[0] = L2ContractsBytecodesLib.readBeaconProxyBytecode(); + factoryDeps[1] = L2ContractsBytecodesLib.readStandardERC20Bytecode(); + factoryDeps[2] = L2ContractsBytecodesLib.readUpgradeableBeaconBytecode(); + return factoryDeps; + } + + function saveOutput(string memory outputPath) internal { + vm.serializeAddress("root", "diamond_proxy_addr", output.diamondProxy); + vm.serializeAddress("root", "chain_admin_addr", output.chainAdmin); + vm.serializeAddress("root", "l2_legacy_shared_bridge_addr", output.l2LegacySharedBridge); + vm.serializeAddress("root", "access_control_restriction_addr", output.accessControlRestrictionAddress); + vm.serializeAddress("root", "chain_proxy_admin_addr", output.chainProxyAdmin); + + string memory toml = vm.serializeAddress("root", "governance_addr", output.governance); string memory root = vm.projectRoot(); - string memory path = string.concat(root, "/script-out/output-register-zkChain.toml"); - vm.writeToml(toml, path); - console.log("Output saved at:", path); + vm.writeToml(toml, outputPath); + console.log("Output saved at:", outputPath); } } diff --git a/l1-contracts/deploy-scripts/Utils.sol b/l1-contracts/deploy-scripts/Utils.sol index 7c387ac5f..37a871571 100644 --- a/l1-contracts/deploy-scripts/Utils.sol +++ b/l1-contracts/deploy-scripts/Utils.sol @@ -4,8 +4,10 @@ pragma solidity 0.8.24; // solhint-disable gas-custom-errors, reason-string import {Vm} from "forge-std/Vm.sol"; +import {console2 as console} from "forge-std/Script.sol"; -import {L2TransactionRequestDirect, IBridgehub} from "contracts/bridgehub/IBridgehub.sol"; +import {Bridgehub} from "contracts/bridgehub/Bridgehub.sol"; +import {L2TransactionRequestDirect, L2TransactionRequestTwoBridgesOuter} from "contracts/bridgehub/IBridgehub.sol"; import {IGovernance} from "contracts/governance/IGovernance.sol"; import {IERC20} from "@openzeppelin/contracts-v4/token/ERC20/IERC20.sol"; import {Ownable} from "@openzeppelin/contracts-v4/access/Ownable.sol"; @@ -13,6 +15,52 @@ import {Call} from "contracts/governance/Common.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"; +import {IChainAdmin} from "contracts/governance/IChainAdmin.sol"; +import {AccessControlRestriction} from "contracts/governance/AccessControlRestriction.sol"; +import {Call} from "contracts/governance/Common.sol"; + +/// @dev The offset from which the built-in, but user space contracts are located. +uint160 constant USER_CONTRACTS_OFFSET = 0x10000; // 2^16 + +// address constant +address constant L2_BRIDGEHUB_ADDRESS = address(USER_CONTRACTS_OFFSET + 0x02); +address constant L2_ASSET_ROUTER_ADDRESS = address(USER_CONTRACTS_OFFSET + 0x03); +address constant L2_NATIVE_TOKEN_VAULT_ADDRESS = address(USER_CONTRACTS_OFFSET + 0x04); +address constant L2_MESSAGE_ROOT_ADDRESS = address(USER_CONTRACTS_OFFSET + 0x05); + +struct DAContractBytecodes { + bytes rollupL1DAValidator; + bytes validiumL1DAValidator; +} + +// solhint-disable-next-line gas-struct-packing +struct StateTransitionDeployedAddresses { + address chainTypeManagerProxy; + address chainTypeManagerImplementation; + address verifier; + address adminFacet; + address mailboxFacet; + address executorFacet; + address gettersFacet; + address diamondInit; + address genesisUpgrade; + address defaultUpgrade; + address validatorTimelock; + address diamondProxy; +} + +/// @dev We need to use a struct instead of list of params to prevent stack too deep error +struct PrepareL1L2TransactionParams { + uint256 l1GasPrice; + bytes l2Calldata; + uint256 l2GasLimit; + uint256 l2Value; + bytes[] factoryDeps; + address dstAddress; + uint256 chainId; + address bridgehubAddress; + address l1SharedBridgeProxy; +} library Utils { // Cheatcodes address, 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D. @@ -77,6 +125,13 @@ library Utils { return selectors; } + function getAllSelectorsForFacet(string memory facetName) internal returns (bytes4[] memory) { + // TODO(EVM-746): use forge to read the bytecode + string memory path = string.concat("/../l1-contracts/out/", facetName, ".sol/", facetName, "Facet.json"); + bytes memory bytecode = readFoundryDeployedBytecode(path); + return getAllSelectors(bytecode); + } + /** * @dev Extract an address from bytes. */ @@ -112,6 +167,18 @@ library Utils { return vm.readFileBinary("../system-contracts/bootloader/build/artifacts/proved_batch.yul.zbin"); } + /** + * @dev Read hardhat bytecodes + */ + function readHardhatBytecode(string memory artifactPath) internal view returns (bytes memory) { + string memory root = vm.projectRoot(); + string memory path = string.concat(root, artifactPath); + console.log(path); + string memory json = vm.readFile(path); + bytes memory bytecode = vm.parseJsonBytes(json, ".bytecode"); + return bytecode; + } + /** * @dev Returns the bytecode of a given system contract. */ @@ -210,6 +277,7 @@ library Utils { runL1L2Transaction({ l2Calldata: deployData, l2GasLimit: l2GasLimit, + l2Value: 0, factoryDeps: _factoryDeps, dstAddress: L2_DEPLOYER_SYSTEM_CONTRACT_ADDR, chainId: chainId, @@ -219,39 +287,95 @@ library Utils { return contractAddress; } + function prepareL1L2Transaction( + PrepareL1L2TransactionParams memory params + ) internal returns (L2TransactionRequestDirect memory l2TransactionRequestDirect, uint256 requiredValueToDeploy) { + Bridgehub bridgehub = Bridgehub(params.bridgehubAddress); + + requiredValueToDeploy = + bridgehub.l2TransactionBaseCost( + params.chainId, + params.l1GasPrice, + params.l2GasLimit, + REQUIRED_L2_GAS_PRICE_PER_PUBDATA + ) * + 2 + + params.l2Value; + + l2TransactionRequestDirect = L2TransactionRequestDirect({ + chainId: params.chainId, + mintValue: requiredValueToDeploy, + l2Contract: params.dstAddress, + l2Value: params.l2Value, + l2Calldata: params.l2Calldata, + l2GasLimit: params.l2GasLimit, + l2GasPerPubdataByteLimit: REQUIRED_L2_GAS_PRICE_PER_PUBDATA, + factoryDeps: params.factoryDeps, + refundRecipient: msg.sender + }); + } + + function prepareL1L2TransactionTwoBridges( + uint256 l1GasPrice, + uint256 l2GasLimit, + uint256 chainId, + address bridgehubAddress, + address secondBridgeAddress, + uint256 secondBridgeValue, + bytes memory secondBridgeCalldata + ) + internal + returns (L2TransactionRequestTwoBridgesOuter memory l2TransactionRequest, uint256 requiredValueToDeploy) + { + Bridgehub bridgehub = Bridgehub(bridgehubAddress); + + requiredValueToDeploy = + bridgehub.l2TransactionBaseCost(chainId, l1GasPrice, l2GasLimit, REQUIRED_L2_GAS_PRICE_PER_PUBDATA) * + 2; + + l2TransactionRequest = L2TransactionRequestTwoBridgesOuter({ + chainId: chainId, + mintValue: requiredValueToDeploy, + l2Value: 0, + l2GasLimit: l2GasLimit, + l2GasPerPubdataByteLimit: REQUIRED_L2_GAS_PRICE_PER_PUBDATA, + refundRecipient: msg.sender, + secondBridgeAddress: secondBridgeAddress, + secondBridgeValue: secondBridgeValue, + secondBridgeCalldata: secondBridgeCalldata + }); + } + /** * @dev Run the l2 l1 transaction */ function runL1L2Transaction( bytes memory l2Calldata, uint256 l2GasLimit, + uint256 l2Value, bytes[] memory factoryDeps, address dstAddress, uint256 chainId, address bridgehubAddress, address l1SharedBridgeProxy ) internal { - IBridgehub bridgehub = IBridgehub(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 - }); + Bridgehub bridgehub = Bridgehub(bridgehubAddress); + ( + L2TransactionRequestDirect memory l2TransactionRequestDirect, + uint256 requiredValueToDeploy + ) = prepareL1L2Transaction( + PrepareL1L2TransactionParams({ + l1GasPrice: bytesToUint256(vm.rpc("eth_gasPrice", "[]")), + l2Calldata: l2Calldata, + l2GasLimit: l2GasLimit, + l2Value: l2Value, + factoryDeps: factoryDeps, + dstAddress: dstAddress, + chainId: chainId, + bridgehubAddress: bridgehubAddress, + l1SharedBridgeProxy: l1SharedBridgeProxy + }) + ); address baseTokenAddress = bridgehub.baseToken(chainId); if (ADDRESS_ONE != baseTokenAddress) { @@ -265,6 +389,312 @@ library Utils { bridgehub.requestL2TransactionDirect{value: requiredValueToDeploy}(l2TransactionRequestDirect); } + function runGovernanceL1L2DirectTransaction( + uint256 l1GasPrice, + address governor, + bytes32 salt, + bytes memory l2Calldata, + uint256 l2GasLimit, + bytes[] memory factoryDeps, + address dstAddress, + uint256 chainId, + address bridgehubAddress, + address l1SharedBridgeProxy + ) internal returns (bytes32 txHash) { + ( + L2TransactionRequestDirect memory l2TransactionRequestDirect, + uint256 requiredValueToDeploy + ) = prepareL1L2Transaction( + PrepareL1L2TransactionParams({ + l1GasPrice: l1GasPrice, + l2Calldata: l2Calldata, + l2GasLimit: l2GasLimit, + l2Value: 0, + factoryDeps: factoryDeps, + dstAddress: dstAddress, + chainId: chainId, + bridgehubAddress: bridgehubAddress, + l1SharedBridgeProxy: l1SharedBridgeProxy + }) + ); + + requiredValueToDeploy = approveBaseTokenGovernance( + Bridgehub(bridgehubAddress), + l1SharedBridgeProxy, + governor, + salt, + chainId, + requiredValueToDeploy + ); + + bytes memory l2TransactionRequestDirectCalldata = abi.encodeCall( + Bridgehub.requestL2TransactionDirect, + (l2TransactionRequestDirect) + ); + + console.log("Executing transaction"); + vm.recordLogs(); + executeUpgrade(governor, salt, bridgehubAddress, l2TransactionRequestDirectCalldata, requiredValueToDeploy, 0); + Vm.Log[] memory logs = vm.getRecordedLogs(); + console.log("Transaction executed succeassfully! Extracting logs..."); + + address expectedDiamondProxyAddress = Bridgehub(bridgehubAddress).getHyperchain(chainId); + + txHash = extractPriorityOpFromLogs(expectedDiamondProxyAddress, logs); + + console.log("L2 Transaction hash is "); + console.logBytes32(txHash); + } + + function runGovernanceL1L2TwoBridgesTransaction( + uint256 l1GasPrice, + address governor, + bytes32 salt, + uint256 l2GasLimit, + uint256 chainId, + address bridgehubAddress, + address l1SharedBridgeProxy, + address secondBridgeAddress, + uint256 secondBridgeValue, + bytes memory secondBridgeCalldata + ) internal returns (bytes32 txHash) { + ( + L2TransactionRequestTwoBridgesOuter memory l2TransactionRequest, + uint256 requiredValueToDeploy + ) = prepareL1L2TransactionTwoBridges( + l1GasPrice, + l2GasLimit, + chainId, + bridgehubAddress, + secondBridgeAddress, + secondBridgeValue, + secondBridgeCalldata + ); + + requiredValueToDeploy = approveBaseTokenGovernance( + Bridgehub(bridgehubAddress), + l1SharedBridgeProxy, + governor, + salt, + chainId, + requiredValueToDeploy + ); + + bytes memory l2TransactionRequestCalldata = abi.encodeCall( + Bridgehub.requestL2TransactionTwoBridges, + (l2TransactionRequest) + ); + + console.log("Executing transaction"); + vm.recordLogs(); + executeUpgrade(governor, salt, bridgehubAddress, l2TransactionRequestCalldata, requiredValueToDeploy, 0); + Vm.Log[] memory logs = vm.getRecordedLogs(); + console.log("Transaction executed succeassfully! Extracting logs..."); + + address expectedDiamondProxyAddress = Bridgehub(bridgehubAddress).getHyperchain(chainId); + + txHash = extractPriorityOpFromLogs(expectedDiamondProxyAddress, logs); + + console.log("L2 Transaction hash is "); + console.logBytes32(txHash); + } + + function approveBaseTokenGovernance( + Bridgehub bridgehub, + address l1SharedBridgeProxy, + address governor, + bytes32 salt, + uint256 chainId, + uint256 amountToApprove + ) internal returns (uint256 ethAmountToPass) { + address baseTokenAddress = bridgehub.baseToken(chainId); + if (ADDRESS_ONE != baseTokenAddress) { + console.log("Base token not ETH, approving"); + IERC20 baseToken = IERC20(baseTokenAddress); + + bytes memory approvalCalldata = abi.encodeCall(baseToken.approve, (l1SharedBridgeProxy, amountToApprove)); + + executeUpgrade(governor, salt, address(baseToken), approvalCalldata, 0, 0); + + ethAmountToPass = 0; + } else { + console.log("Base token is ETH, no need to approve"); + ethAmountToPass = amountToApprove; + } + } + + function runAdminL1L2DirectTransaction( + uint256 gasPrice, + address admin, + address accessControlRestriction, + bytes memory l2Calldata, + uint256 l2GasLimit, + bytes[] memory factoryDeps, + address dstAddress, + uint256 chainId, + address bridgehubAddress, + address l1SharedBridgeProxy + ) internal returns (bytes32 txHash) { + ( + L2TransactionRequestDirect memory l2TransactionRequestDirect, + uint256 requiredValueToDeploy + ) = prepareL1L2Transaction( + PrepareL1L2TransactionParams({ + l1GasPrice: gasPrice, + l2Calldata: l2Calldata, + l2GasLimit: l2GasLimit, + l2Value: 0, + factoryDeps: factoryDeps, + dstAddress: dstAddress, + chainId: chainId, + bridgehubAddress: bridgehubAddress, + l1SharedBridgeProxy: l1SharedBridgeProxy + }) + ); + + requiredValueToDeploy = approveBaseTokenAdmin( + Bridgehub(bridgehubAddress), + l1SharedBridgeProxy, + admin, + accessControlRestriction, + chainId, + requiredValueToDeploy + ); + + bytes memory l2TransactionRequestDirectCalldata = abi.encodeCall( + Bridgehub.requestL2TransactionDirect, + (l2TransactionRequestDirect) + ); + + console.log("Executing transaction"); + vm.recordLogs(); + adminExecute( + admin, + accessControlRestriction, + bridgehubAddress, + l2TransactionRequestDirectCalldata, + requiredValueToDeploy + ); + Vm.Log[] memory logs = vm.getRecordedLogs(); + console.log("Transaction executed succeassfully! Extracting logs..."); + + address expectedDiamondProxyAddress = Bridgehub(bridgehubAddress).getHyperchain(chainId); + + txHash = extractPriorityOpFromLogs(expectedDiamondProxyAddress, logs); + + console.log("L2 Transaction hash is "); + console.logBytes32(txHash); + } + + function runAdminL1L2TwoBridgesTransaction( + uint256 l1GasPrice, + address admin, + address accessControlRestriction, + uint256 l2GasLimit, + uint256 chainId, + address bridgehubAddress, + address l1SharedBridgeProxy, + address secondBridgeAddress, + uint256 secondBridgeValue, + bytes memory secondBridgeCalldata + ) internal returns (bytes32 txHash) { + ( + L2TransactionRequestTwoBridgesOuter memory l2TransactionRequest, + uint256 requiredValueToDeploy + ) = prepareL1L2TransactionTwoBridges( + l1GasPrice, + l2GasLimit, + chainId, + bridgehubAddress, + secondBridgeAddress, + secondBridgeValue, + secondBridgeCalldata + ); + + requiredValueToDeploy = approveBaseTokenAdmin( + Bridgehub(bridgehubAddress), + l1SharedBridgeProxy, + admin, + accessControlRestriction, + chainId, + requiredValueToDeploy + ); + + bytes memory l2TransactionRequestCalldata = abi.encodeCall( + Bridgehub.requestL2TransactionTwoBridges, + (l2TransactionRequest) + ); + + console.log("Executing transaction"); + vm.recordLogs(); + adminExecute( + admin, + accessControlRestriction, + bridgehubAddress, + l2TransactionRequestCalldata, + requiredValueToDeploy + ); + Vm.Log[] memory logs = vm.getRecordedLogs(); + console.log("Transaction executed succeassfully! Extracting logs..."); + + address expectedDiamondProxyAddress = Bridgehub(bridgehubAddress).getHyperchain(chainId); + + txHash = extractPriorityOpFromLogs(expectedDiamondProxyAddress, logs); + + console.log("L2 Transaction hash is "); + console.logBytes32(txHash); + } + + function approveBaseTokenAdmin( + Bridgehub bridgehub, + address l1SharedBridgeProxy, + address admin, + address accessControlRestriction, + uint256 chainId, + uint256 amountToApprove + ) internal returns (uint256 ethAmountToPass) { + address baseTokenAddress = bridgehub.baseToken(chainId); + if (ADDRESS_ONE != baseTokenAddress) { + console.log("Base token not ETH, approving"); + IERC20 baseToken = IERC20(baseTokenAddress); + + bytes memory approvalCalldata = abi.encodeCall(baseToken.approve, (l1SharedBridgeProxy, amountToApprove)); + + adminExecute(admin, accessControlRestriction, address(baseToken), approvalCalldata, 0); + + ethAmountToPass = 0; + } else { + console.log("Base token is ETH, no need to approve"); + ethAmountToPass = amountToApprove; + } + } + + function extractPriorityOpFromLogs( + address expectedDiamondProxyAddress, + Vm.Log[] memory logs + ) internal pure returns (bytes32 txHash) { + // TODO(EVM-749): cleanup the constant and automate its derivation + bytes32 topic0 = bytes32(uint256(0x4531cd5795773d7101c17bdeb9f5ab7f47d7056017506f937083be5d6e77a382)); + + for (uint256 i = 0; i < logs.length; i++) { + if (logs[i].emitter == expectedDiamondProxyAddress && logs[i].topics[0] == topic0) { + if (txHash != bytes32(0)) { + revert("Multiple priority ops"); + } + + bytes memory data = logs[i].data; + assembly { + // Skip length + tx id + txHash := mload(add(data, 0x40)) + } + } + } + + if (txHash == bytes32(0)) { + revert("No priority op found"); + } + } + /** * @dev Publish bytecodes to l2 through l1 */ @@ -277,6 +707,7 @@ library Utils { runL1L2Transaction({ l2Calldata: "", l2GasLimit: MAX_PRIORITY_TX_GAS, + l2Value: 0, factoryDeps: factoryDeps, dstAddress: 0x0000000000000000000000000000000000000000, chainId: chainId, @@ -299,11 +730,11 @@ library Utils { /** * @dev Read hardhat bytecodes */ - function readHardhatBytecode(string memory artifactPath) internal view returns (bytes memory) { + function readFoundryDeployedBytecode(string memory artifactPath) internal 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"); + bytes memory bytecode = vm.parseJsonBytes(json, ".deployedBytecode.object"); return bytecode; } @@ -335,6 +766,34 @@ library Utils { vm.stopBroadcast(); } + function adminExecute( + address _admin, + address _accessControlRestriction, + address _target, + bytes memory _data, + uint256 _value + ) internal { + address defaultAdmin = AccessControlRestriction(_accessControlRestriction).defaultAdmin(); + + Call[] memory calls = new Call[](1); + calls[0] = Call({target: _target, value: _value, data: _data}); + + vm.startBroadcast(defaultAdmin); + IChainAdmin(_admin).multicall{value: _value}(calls, true); + vm.stopBroadcast(); + } + + function readDAContractBytecodes() internal view returns (DAContractBytecodes memory bytecodes) { + bytecodes = DAContractBytecodes({ + rollupL1DAValidator: readFoundryBytecode( + "/../da-contracts/out/RollupL1DAValidator.sol/RollupL1DAValidator.json" + ), + validiumL1DAValidator: readFoundryBytecode( + "/../da-contracts/out/ValidiumL1DAValidator.sol/ValidiumL1DAValidator.json" + ) + }); + } + // add this to be excluded from coverage report function test() internal {} } diff --git a/l1-contracts/foundry.toml b/l1-contracts/foundry.toml index c2e651106..c9a299f88 100644 --- a/l1-contracts/foundry.toml +++ b/l1-contracts/foundry.toml @@ -17,7 +17,7 @@ fs_permissions = [ { access = "read", path = "./script-config" }, { access = "read-write", path = "./script-out" }, { access = "read", path = "./out" }, - { access = "read", path = "./test/foundry/l1/integration/deploy-scripts/script-config/" }, + { access = "read-write", path = "./test/foundry/l1/integration/deploy-scripts/script-config/" }, { access = "read-write", path = "./test/foundry/l1/integration/deploy-scripts/script-out/" }, { access = "read", path = "zkout" }, ] diff --git a/l1-contracts/hardhat.config.ts b/l1-contracts/hardhat.config.ts index f19968b51..fc3ae18f1 100644 --- a/l1-contracts/hardhat.config.ts +++ b/l1-contracts/hardhat.config.ts @@ -41,11 +41,12 @@ export default { }, evmVersion: "cancun", }, + eraVersion: "1.0.1", }, zksolc: { compilerSource: "binary", + version: "1.5.3", settings: { - // compilerPath: getZksolcUrl(), isSystem: true, }, }, diff --git a/l1-contracts/src.ts/deploy.ts b/l1-contracts/src.ts/deploy.ts index cd3e03baf..3eef1cf9f 100644 --- a/l1-contracts/src.ts/deploy.ts +++ b/l1-contracts/src.ts/deploy.ts @@ -1486,14 +1486,12 @@ export class Deployer { const l1SharedBridge = this.defaultSharedBridge(this.deployWallet); if (isCurrentNetworkLocal()) { - const eraChainId = getNumberFromEnv("CONTRACTS_ERA_CHAIN_ID"); - const l2SharedBridgeImplementationBytecode = L2_SHARED_BRIDGE_IMPLEMENTATION.bytecode; const l2SharedBridgeImplAddress = computeL2Create2Address( this.deployWallet.address, l2SharedBridgeImplementationBytecode, - ethers.utils.defaultAbiCoder.encode(["uint256"], [eraChainId]), + "0x", ethers.constants.HashZero ); @@ -1502,7 +1500,6 @@ export class Deployer { const l2SharedBridgeInterface = new Interface(L2_SHARED_BRIDGE_IMPLEMENTATION.abi); const proxyInitializationParams = l2SharedBridgeInterface.encodeFunctionData("initialize", [ l1SharedBridge.address, - this.addresses.Bridges.ERC20BridgeProxy, hashL2Bytecode(L2_STANDARD_TOKEN_PROXY.bytecode), l2GovernorAddress, ]); @@ -1561,7 +1558,7 @@ export class Deployer { const l2SharedBridgeImplAddress = computeL2Create2Address( this.deployWallet.address, l2SharedBridgeImplementationBytecode, - ethers.utils.defaultAbiCoder.encode(["uint256"], [eraChainId]), + "0x", ethers.constants.HashZero ); this.addresses.Bridges.L2LegacySharedBridgeImplementation = l2SharedBridgeImplAddress; @@ -1602,7 +1599,6 @@ export class Deployer { const l2SharedBridgeInterface = new Interface(L2_SHARED_BRIDGE_IMPLEMENTATION.abi); const proxyInitializationParams = l2SharedBridgeInterface.encodeFunctionData("initialize", [ l1SharedBridge.address, - this.addresses.Bridges.ERC20BridgeProxy, hashL2Bytecode(L2_STANDARD_TOKEN_PROXY.bytecode), l2GovernorAddress, ]); diff --git a/l1-contracts/test/foundry/l1/integration/GatewayPreparationForTests.sol b/l1-contracts/test/foundry/l1/integration/GatewayPreparationForTests.sol new file mode 100644 index 000000000..271dfdf0c --- /dev/null +++ b/l1-contracts/test/foundry/l1/integration/GatewayPreparationForTests.sol @@ -0,0 +1,48 @@ +import {stdToml} from "forge-std/StdToml.sol"; +import {Script, console2 as console} from "forge-std/Script.sol"; + +import {GatewayPreparation} from "deploy-scripts/GatewayPreparation.s.sol"; + +contract GatewayPreparationForTests is GatewayPreparation { + using stdToml for string; + + function initializeConfig() internal override { + // Grab config from output of l1 deployment + string memory root = vm.projectRoot(); + string memory path = string.concat(root, vm.envString("L1_OUTPUT")); + string memory toml = vm.readFile(path); + + config.bridgehub = toml.readAddress("$.deployed_addresses.bridgehub.bridgehub_proxy_addr"); + config.chainTypeManagerProxy = toml.readAddress( + "$.deployed_addresses.state_transition.state_transition_proxy_addr" + ); + config.sharedBridgeProxy = toml.readAddress("$.deployed_addresses.bridges.shared_bridge_proxy_addr"); + config.ctmDeploymentTracker = toml.readAddress( + "$.deployed_addresses.bridgehub.ctm_deployment_tracker_proxy_addr" + ); + config.governance = toml.readAddress("$.deployed_addresses.governance_addr"); + + path = string.concat(root, vm.envString("GATEWAY_CONFIG")); + toml = vm.readFile(path); + + config.gatewayChainId = toml.readUint("$.chain.chain_chain_id"); + + path = string.concat(root, vm.envString("GATEWAY_OUTPUT")); + toml = vm.readFile(path); + + config.gatewayChainAdmin = toml.readAddress("$.chain_admin_addr"); + config.gatewayChainProxyAdmin = toml.readAddress("$.chain_proxy_admin_addr"); + config.gatewayAccessControlRestriction = toml.readAddress( + "$.deployed_addresses.access_control_restriction_addr" + ); + + console.log("chain chain id = ", config.gatewayChainId); + + // This value is never checked in the integration tests + config.gatewayDiamondCutData = hex""; + } + + function _getL1GasPrice() internal view override returns (uint256) { + return 10; + } +} diff --git a/l1-contracts/test/foundry/l1/integration/GatewayTests.t.sol b/l1-contracts/test/foundry/l1/integration/GatewayTests.t.sol index e4e14e10f..0cfcbf049 100644 --- a/l1-contracts/test/foundry/l1/integration/GatewayTests.t.sol +++ b/l1-contracts/test/foundry/l1/integration/GatewayTests.t.sol @@ -33,6 +33,7 @@ import {AddressAliasHelper} from "contracts/vendor/AddressAliasHelper.sol"; import {TxStatus} from "contracts/common/Messaging.sol"; import {DataEncoding} from "contracts/common/libraries/DataEncoding.sol"; import {IncorrectBridgeHubAddress} from "contracts/common/L1ContractErrors.sol"; +import {ChainAdmin} from "contracts/governance/ChainAdmin.sol"; contract GatewayTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, L2TxMocker, GatewayDeployer { uint256 constant TEST_USERS_COUNT = 10; @@ -40,7 +41,11 @@ contract GatewayTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, L2T address[] public l2ContractAddresses; uint256 migratingChainId = 10; + IZKChain migratingChain; + uint256 gatewayChainId = 11; + IZKChain gatewayChain; + uint256 mintChainId = 12; // generate MAX_USERS addresses and append it to users array @@ -82,35 +87,55 @@ contract GatewayTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, L2T vm.deal(Ownable(l1Script.getBridgehubProxyAddress()).owner(), 100000000000000000000000000000000000); vm.deal(l1Script.getOwnerAddress(), 100000000000000000000000000000000000); - IZKChain chain = IZKChain(IBridgehub(l1Script.getBridgehubProxyAddress()).getZKChain(migratingChainId)); - IZKChain chain2 = IZKChain(IBridgehub(l1Script.getBridgehubProxyAddress()).getZKChain(gatewayChainId)); - vm.deal(chain.getAdmin(), 100000000000000000000000000000000000); - vm.deal(chain2.getAdmin(), 100000000000000000000000000000000000); + migratingChain = IZKChain(IBridgehub(l1Script.getBridgehubProxyAddress()).getZKChain(migratingChainId)); + gatewayChain = IZKChain(IBridgehub(l1Script.getBridgehubProxyAddress()).getZKChain(gatewayChainId)); + vm.deal(migratingChain.getAdmin(), 100000000000000000000000000000000000); + vm.deal(gatewayChain.getAdmin(), 100000000000000000000000000000000000); // vm.deal(msg.sender, 100000000000000000000000000000000000); // vm.deal(l1Script.getBridgehubProxyAddress(), 100000000000000000000000000000000000); } + // This is a method to simplify porting the tests for now. + // Here we rely that the first restriction is the AccessControlRestriction + function _extractAccessControlRestriction(address admin) internal returns (address) { + return ChainAdmin(payable(admin)).getRestrictions()[0]; + } + function setUp() public { prepare(); } + function _setUpGatewayWithFilterer() internal { + gatewayScript.governanceRegisterGateway(); + gatewayScript.deployAndSetGatewayTransactionFilterer(); + } + // function test_registerGateway() public { - gatewayScript.registerGateway(); + _setUpGatewayWithFilterer(); } // function test_moveChainToGateway() public { - gatewayScript.registerGateway(); - gatewayScript.moveChainToGateway(); + _setUpGatewayWithFilterer(); + gatewayScript.migrateChainToGateway( + migratingChain.getAdmin(), + _extractAccessControlRestriction(migratingChain.getAdmin()), + migratingChainId + ); // require(bridgehub.settlementLayer()) } function test_l2Registration() public { - gatewayScript.registerGateway(); - gatewayScript.moveChainToGateway(); - gatewayScript.registerL2Contracts(); + _setUpGatewayWithFilterer(); + gatewayScript.migrateChainToGateway( + migratingChain.getAdmin(), + _extractAccessControlRestriction(migratingChain.getAdmin()), + migratingChainId + ); + gatewayScript.governanceSetCTMAssetHandler(bytes32(0)); + gatewayScript.registerAssetIdInBridgehub(address(0x01), bytes32(0)); } function test_finishMoveChain() public { @@ -144,8 +169,12 @@ contract GatewayTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, L2T } function test_recoverFromFailedChainMigration() public { - gatewayScript.registerGateway(); - gatewayScript.moveChainToGateway(); + _setUpGatewayWithFilterer(); + gatewayScript.migrateChainToGateway( + migratingChain.getAdmin(), + _extractAccessControlRestriction(migratingChain.getAdmin()), + migratingChainId + ); // Setup IBridgehub bridgehub = IBridgehub(l1Script.getBridgehubProxyAddress()); @@ -216,7 +245,8 @@ contract GatewayTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, L2T } function test_registerAlreadyDeployedZKChain() public { - gatewayScript.registerGateway(); + gatewayScript.governanceRegisterGateway(); + IChainTypeManager stm = IChainTypeManager(l1Script.getCTM()); IBridgehub bridgehub = IBridgehub(l1Script.getBridgehubProxyAddress()); address owner = Ownable(address(bridgeHub)).owner(); diff --git a/l1-contracts/test/foundry/l1/integration/_SharedGatewayDeployer.t.sol b/l1-contracts/test/foundry/l1/integration/_SharedGatewayDeployer.t.sol index 11b320367..66e0aa628 100644 --- a/l1-contracts/test/foundry/l1/integration/_SharedGatewayDeployer.t.sol +++ b/l1-contracts/test/foundry/l1/integration/_SharedGatewayDeployer.t.sol @@ -2,26 +2,30 @@ pragma solidity 0.8.24; import {L1ContractDeployer} from "./_SharedL1ContractDeployer.t.sol"; -import {GatewayScript} from "deploy-scripts/Gateway.s.sol"; +import {GatewayPreparationForTests} from "./GatewayPreparationForTests.sol"; import {ETH_TOKEN_ADDRESS} from "contracts/common/Config.sol"; import "@openzeppelin/contracts-v4/utils/Strings.sol"; contract GatewayDeployer is L1ContractDeployer { - GatewayScript gatewayScript; + GatewayPreparationForTests gatewayScript; function _initializeGatewayScript() internal { vm.setEnv("L1_CONFIG", "/test/foundry/l1/integration/deploy-scripts/script-config/config-deploy-l1.toml"); vm.setEnv("L1_OUTPUT", "/test/foundry/l1/integration/deploy-scripts/script-out/output-deploy-l1.toml"); vm.setEnv( "ZK_CHAIN_CONFIG", - "/test/foundry/l1/integration/deploy-scripts/script-out/output-deploy-zk-chain-10.toml" + "/test/foundry/l1/integration/deploy-scripts/script-config/config-deploy-zk-chain-10.toml" ); vm.setEnv( "GATEWAY_CONFIG", + "/test/foundry/l1/integration/deploy-scripts/script-config/config-deploy-zk-chain-11.toml" + ); + vm.setEnv( + "GATEWAY_OUTPUT", "/test/foundry/l1/integration/deploy-scripts/script-out/output-deploy-zk-chain-11.toml" ); - gatewayScript = new GatewayScript(); + gatewayScript = new GatewayPreparationForTests(); gatewayScript.run(); } } diff --git a/l1-contracts/test/foundry/l1/integration/_SharedL1ContractDeployer.t.sol b/l1-contracts/test/foundry/l1/integration/_SharedL1ContractDeployer.t.sol index 20d0840be..b180f6090 100644 --- a/l1-contracts/test/foundry/l1/integration/_SharedL1ContractDeployer.t.sol +++ b/l1-contracts/test/foundry/l1/integration/_SharedL1ContractDeployer.t.sol @@ -5,12 +5,12 @@ import {Test} from "forge-std/Test.sol"; import {StdStorage, stdStorage} from "forge-std/Test.sol"; import {DeployL1Script} from "deploy-scripts/DeployL1.s.sol"; -import {GenerateForceDeploymentsData} from "deploy-scripts/GenerateForceDeploymentsData.s.sol"; import {Bridgehub} from "contracts/bridgehub/Bridgehub.sol"; import {L1AssetRouter} from "contracts/bridge/asset-router/L1AssetRouter.sol"; import {L1Nullifier} from "contracts/bridge/L1Nullifier.sol"; import {L1NativeTokenVault} from "contracts/bridge/ntv/L1NativeTokenVault.sol"; import {DataEncoding} from "contracts/common/libraries/DataEncoding.sol"; +import {CTMDeploymentTracker} from "contracts/bridgehub/CTMDeploymentTracker.sol"; contract L1ContractDeployer is Test { using stdStorage for StdStorage; @@ -19,28 +19,28 @@ contract L1ContractDeployer is Test { address bridgehubOwnerAddress; Bridgehub bridgeHub; + CTMDeploymentTracker ctmDeploymentTracker; + L1AssetRouter public sharedBridge; L1Nullifier public l1Nullifier; L1NativeTokenVault public l1NativeTokenVault; DeployL1Script l1Script; - GenerateForceDeploymentsData forceDeploymentsScript; function _deployL1Contracts() internal { vm.setEnv("L1_CONFIG", "/test/foundry/l1/integration/deploy-scripts/script-config/config-deploy-l1.toml"); vm.setEnv("L1_OUTPUT", "/test/foundry/l1/integration/deploy-scripts/script-out/output-deploy-l1.toml"); vm.setEnv( "ZK_CHAIN_CONFIG", - "/test/foundry/l1/integration/deploy-scripts/script-out/output-deploy-zk-chain-era.toml" + "/test/foundry/l1/integration/deploy-scripts/script-config/config-deploy-zk-chain-era.toml" ); vm.setEnv( - "FORCE_DEPLOYMENTS_CONFIG", - "/test/foundry/l1/integration/deploy-scripts/script-config/generate-force-deployments-data.toml" + "ZK_CHAIN_OUT", + "/test/foundry/l1/integration/deploy-scripts/script-out/output-deploy-zk-chain-era.toml" ); - forceDeploymentsScript = new GenerateForceDeploymentsData(); + l1Script = new DeployL1Script(); - forceDeploymentsScript.run(); - l1Script.run(); + l1Script.runForTest(); bridgehubProxyAddress = l1Script.getBridgehubProxyAddress(); bridgeHub = Bridgehub(bridgehubProxyAddress); @@ -54,6 +54,8 @@ contract L1ContractDeployer is Test { address l1NativeTokenVaultProxyAddress = l1Script.getNativeTokenVaultProxyAddress(); l1NativeTokenVault = L1NativeTokenVault(payable(l1NativeTokenVaultProxyAddress)); + ctmDeploymentTracker = CTMDeploymentTracker(l1Script.getCTMDeploymentTrackerAddress()); + _acceptOwnership(); _setEraBatch(); @@ -64,6 +66,7 @@ contract L1ContractDeployer is Test { vm.startPrank(bridgeHub.pendingOwner()); bridgeHub.acceptOwnership(); sharedBridge.acceptOwnership(); + ctmDeploymentTracker.acceptOwnership(); vm.stopPrank(); } diff --git a/l1-contracts/test/foundry/l1/integration/_SharedZKChainDeployer.t.sol b/l1-contracts/test/foundry/l1/integration/_SharedZKChainDeployer.t.sol index 747a6c311..6220818ef 100644 --- a/l1-contracts/test/foundry/l1/integration/_SharedZKChainDeployer.t.sol +++ b/l1-contracts/test/foundry/l1/integration/_SharedZKChainDeployer.t.sol @@ -36,19 +36,30 @@ contract ZKChainDeployer is L1ContractDeployer { function _deployEra() internal { vm.setEnv( "ZK_CHAIN_CONFIG", + "/test/foundry/l1/integration/deploy-scripts/script-config/config-deploy-zk-chain-era.toml" + ); + vm.setEnv( + "ZK_CHAIN_OUT", "/test/foundry/l1/integration/deploy-scripts/script-out/output-deploy-zk-chain-era.toml" ); - deployScript = new RegisterZKChainScript(); saveZKChainConfig(_getDefaultDescription(eraZKChainId, ETH_TOKEN_ADDRESS, eraZKChainId)); vm.warp(100); - deployScript.run(); + deployScript.runForTest(); zkChainIds.push(eraZKChainId); } function _deployZKChain(address _baseToken) internal { vm.setEnv( "ZK_CHAIN_CONFIG", + string.concat( + "/test/foundry/l1/integration/deploy-scripts/script-config/config-deploy-zk-chain-", + Strings.toString(currentZKChainId), + ".toml" + ) + ); + vm.setEnv( + "ZK_CHAIN_OUT", string.concat( "/test/foundry/l1/integration/deploy-scripts/script-out/output-deploy-zk-chain-", Strings.toString(currentZKChainId), @@ -58,7 +69,7 @@ contract ZKChainDeployer is L1ContractDeployer { zkChainIds.push(currentZKChainId); saveZKChainConfig(_getDefaultDescription(currentZKChainId, _baseToken, currentZKChainId)); currentZKChainId++; - deployScript.run(); + deployScript.runForTest(); } function _getDefaultDescription( diff --git a/l1-contracts/test/test_config/constant/hardhat.json b/l1-contracts/test/test_config/constant/hardhat.json index 60af027f7..7e18d5adf 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": "0x3577F97253469b560CD6442AB37A262a292003f3" + "address": "0x2733174391e451C1708050dE4442f2AaF197759C" }, { "name": "wBTC", "symbol": "wBTC", "decimals": 8, - "address": "0x4A9D48Db0008F8778160dDF142b28a858c427B48" + "address": "0x5195459c6a59dA8f5bED6a9E7692d5b5b40A2928" }, { "name": "BAT", "symbol": "BAT", "decimals": 18, - "address": "0x8ce06E5aF9A1221a88282A5Ce65D750BE16b0079" + "address": "0x18fb101C1f3ab450498fD0D4400e4D8a3c1B9F6c" }, { "name": "GNT", "symbol": "GNT", "decimals": 18, - "address": "0xF1286aD858DeE56B79D5F23f14040849fA3631dA" + "address": "0x20bABCb488aad652e713fB70BACCD0c8e72de4EF" }, { "name": "MLTT", "symbol": "MLTT", "decimals": 18, - "address": "0x9267631d42C7D2747f8e5573169BdceAE87535b8" + "address": "0xFF9d5a057d09802c371582D1166df728D3b019A4" }, { "name": "DAIK", "symbol": "DAIK", "decimals": 18, - "address": "0xea21B9a6C6D13d1C6AbAEc73c6F330D601779e15" + "address": "0xAC5d1395Dd3bA956bB8Ba0e0E8ffe247404fd9c5" }, { "name": "wBTCK", "symbol": "wBTCK", "decimals": 8, - "address": "0x389f272Ae7D1061608Af3E2203d24c8e654FcEd5" + "address": "0x9614c0F8e657eAb74e95B87cA819C6ae1F9d5fe1" }, { "name": "BATK", "symbol": "BATS", "decimals": 18, - "address": "0x6890D8DB20db3A5d06eC6DE69F7DB1d5A183922C" + "address": "0x175F95E3c6a30c3D4DDBA32f82427C5d371f67B8" }, { "name": "GNTK", "symbol": "GNTS", "decimals": 18, - "address": "0x037f096F289dF1c0dBf3C89Dd6CAbc07599dD150" + "address": "0x205725BE39c54574e64771C3c71c44829E3031dC" }, { "name": "MLTTK", "symbol": "MLTTS", "decimals": 18, - "address": "0x23886B9856326226A5de9368C3781843b58Bd2bE" + "address": "0xADDb1960aCAAC7db90E3d20b9621D8b8C0b97405" }, { "name": "DAIL", "symbol": "DAIL", "decimals": 18, - "address": "0x039D76D9b98e856da082ddf5Ab504352BB2096E0" + "address": "0x5F0A3258CF075F828a01bEAf63cCea32159B4EcD" }, { "name": "wBTCL", "symbol": "wBTCP", "decimals": 8, - "address": "0x341a1D5df70E56DCA0bCe2892F70A9e83bFA7958" + "address": "0x09405DfB0C61959daA219e9E9907223e7F091587" }, { "name": "BATL", "symbol": "BATW", "decimals": 18, - "address": "0x1268Cf85f3D4306059A3fa7aDE2a9a49467E0E0C" + "address": "0xA318F029bc204EaF55A6f480f9Dc6Ef0C235d2D6" }, { "name": "GNTL", "symbol": "GNTW", "decimals": 18, - "address": "0x75fe8be7615e5b8b116AF4ffD67993E03b7568b5" + "address": "0x9910Ed28A31C2b4EE8A7C1F8559Cf1a874e374F2" }, { "name": "MLTTL", "symbol": "MLTTW", "decimals": 18, - "address": "0x35e4ba9B6913426C15410DeD184Ba642E858f3Ef" + "address": "0x3bC2c8eF2f110750bfa9aA89D30F3D66c2a03bb9" }, { "name": "Wrapped Ether", "symbol": "WETH", "decimals": 18, - "address": "0x8ed463C98Ba3A08d1263D785Ac74CD93bDBbcFD4" + "address": "0x6930c5c3421a666d6642FafBe750B1D0B42197c6" } ] diff --git a/yarn.lock b/yarn.lock index acf71f3ce..2677c503c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1217,11 +1217,13 @@ integrity sha512-+Wz0hwmJGSI17B+BhU/qFRZ1l6/xMW82QGXE/Gi+WTmwgJrQefuBs1lIf7hzQ1hLk6hpkvb/zwcNkpVKRYTQYg== "@openzeppelin/contracts-upgradeable-v4@npm:@openzeppelin/contracts-upgradeable@4.9.5", "@openzeppelin/contracts-upgradeable@4.9.5": + name "@openzeppelin/contracts-upgradeable-v4" version "4.9.5" resolved "https://registry.yarnpkg.com/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-4.9.5.tgz#572b5da102fc9be1d73f34968e0ca56765969812" integrity sha512-f7L1//4sLlflAN7fVzJLoRedrf5Na3Oal5PZfIq55NFcVZ90EpV1q5xOvL4lFvg3MNICSDr2hH0JUBxwlxcoPg== "@openzeppelin/contracts-v4@npm:@openzeppelin/contracts@4.9.5", "@openzeppelin/contracts@4.9.5": + name "@openzeppelin/contracts-v4" version "4.9.5" resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.9.5.tgz#1eed23d4844c861a1835b5d33507c1017fa98de8" integrity sha512-ZK+W5mVhRppff9BE6YdR8CC52C8zAvsVAiWhEtQ5+oNxFE6h1WdeWo+FJSF8KKvtxxVYZ7MTP/5KoVpAU3aSWg== @@ -7938,10 +7940,3 @@ zksync-ethers@^5.9.0: integrity sha512-Y2Mx6ovvxO6UdC2dePLguVzvNToOY8iLWeq5ne+jgGSJxAi/f4He/NF6FNsf6x1aWX0o8dy4Df8RcOQXAkj5qw== dependencies: ethers "~5.7.0" - -zksync-web3@^0.15.4: - version "0.15.5" - resolved "https://registry.yarnpkg.com/zksync-web3/-/zksync-web3-0.15.5.tgz#aabe379464963ab573e15948660a709f409b5316" - integrity sha512-97gB7OKJL4spegl8fGO54g6cvTd/75G6yFWZWEa2J09zhjTrfqabbwE/GwiUJkFQ5BbzoH4JaTlVz1hoYZI+DQ== - dependencies: - ethers "~5.7.0"