diff --git a/system-contracts/contracts/test-deps/l2-upgrades/Deps.sol b/system-contracts/contracts/test-deps/l2-upgrades/Deps.sol new file mode 100644 index 0000000000..9da0e78a71 --- /dev/null +++ b/system-contracts/contracts/test-deps/l2-upgrades/Deps.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: MIT +// We use a floating point pragma here so it can be used within other projects that interact with the ZKsync ecosystem without using our exact pragma version. +pragma solidity ^0.8.21; + +uint160 constant SYSTEM_CONTRACTS_OFFSET = 0x8000; // 2^15 +address constant L2_DEPLOYER_SYSTEM_CONTRACT_ADDR = address(SYSTEM_CONTRACTS_OFFSET + 0x06); +address constant L2_FORCE_DEPLOYER_ADDR = address(SYSTEM_CONTRACTS_OFFSET + 0x07); +address constant L2_SYSTEM_CONTEXT_SYSTEM_CONTRACT_ADDR = address(SYSTEM_CONTRACTS_OFFSET + 0x0b); + +address constant GW_ASSET_TRACKER_ADDR = address(BUILT_IN_CONTRACTS_OFFSET + 0x0f); +address constant L2_ASSET_TRACKER_ADDR = address(BUILT_IN_CONTRACTS_OFFSET + 0x0e); +address constant L2_ASSET_ROUTER_ADDR = address(BUILT_IN_CONTRACTS_OFFSET + 0x03); +address constant L2_BRIDGEHUB_ADDR = address(BUILT_IN_CONTRACTS_OFFSET + 0x02); +address constant L2_CHAIN_ASSET_HANDLER_ADDR = address(BUILT_IN_CONTRACTS_OFFSET + 0x0a); +address constant L2_DEPLOYER_SYSTEM_CONTRACT_ADDR = address(SYSTEM_CONTRACTS_OFFSET + 0x06); +address constant L2_MESSAGE_ROOT_ADDR = address(BUILT_IN_CONTRACTS_OFFSET + 0x05); +address constant L2_NATIVE_TOKEN_VAULT_ADDR = address(BUILT_IN_CONTRACTS_OFFSET + 0x04); +address constant L2_NTV_BEACON_DEPLOYER_ADDR = address(BUILT_IN_CONTRACTS_OFFSET + 0x0b); +address constant L2_WRAPPED_BASE_TOKEN_IMPL_ADDR = address(BUILT_IN_CONTRACTS_OFFSET + 0x07); + + +error Unauthorized(address sender); +error AddressHasNoCode(address addr); +error InvalidChainId(); diff --git a/system-contracts/contracts/test-deps/l2-upgrades/IZKOSContractDeployer.sol b/system-contracts/contracts/test-deps/l2-upgrades/IZKOSContractDeployer.sol new file mode 100644 index 0000000000..5c420fa14c --- /dev/null +++ b/system-contracts/contracts/test-deps/l2-upgrades/IZKOSContractDeployer.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.28; + +/// @notice Interface for contract deployer system hook on ZKsyncOS. +/// @dev Note, that the actual implementation of this interface is implemented in Rust +/// as a system hook. +interface IZKOSContractDeployer { + /// @notice Sets the bytecode details for a contract on ZKsyncOS. + /// @dev This function is used to set the bytecode details for a contract on ZKsyncOS, + /// it is an alternative to the `forceDeployOnAddresses` function from Era. + /// @param _addr The address of the contract. + /// @param _bytecodeHash The hash of the bytecode. + /// @param _bytecodeLength The length of the bytecode. + /// @param _observableBytecodeHash The hash of the observable bytecode. + function setBytecodeDetailsEVM( + address _addr, + bytes32 _bytecodeHash, + uint32 _bytecodeLength, + bytes32 _observableBytecodeHash + ) external; +} diff --git a/system-contracts/contracts/test-deps/l2-upgrades/L2ComplexUpgrader.sol b/system-contracts/contracts/test-deps/l2-upgrades/L2ComplexUpgrader.sol new file mode 100644 index 0000000000..c93f036d40 --- /dev/null +++ b/system-contracts/contracts/test-deps/l2-upgrades/L2ComplexUpgrader.sol @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.28; + +import {IContractDeployer as IL2ContractDeployer } from "../../interfaces/IContractDeployer.sol"; + +import {IComplexUpgrader} from "../../interfaces/IComplexUpgrader.sol"; + +import {L2GenesisForceDeploymentsHelper} from "./L2GenesisForceDeploymentsHelper.sol"; + +/** + * @author Matter Labs + * @custom:security-contact security@matterlabs.dev + * @notice Upgrader which should be used to perform complex multistep upgrades on L2. In case some custom logic for an upgrade is needed + * this logic should be deployed into the user space and then this contract will delegatecall to the deployed contract. + */ +contract L2ComplexUpgrader is IComplexUpgrader { + /// @notice Ensures that only the `FORCE_DEPLOYER` can call the function. + /// @dev Note that it is vital to put this modifier at the start of *each* function, + /// since even temporary anauthorized access can be dangerous. + modifier onlyForceDeployer() { + // Note, that it is not + if (msg.sender != L2_FORCE_DEPLOYER_ADDR) { + revert Unauthorized(msg.sender); + } + _; + } + + /// @notice Executes an upgrade process by delegating calls to another contract. + /// @dev This function allows only the `FORCE_DEPLOYER` to initiate the upgrade. + /// If the delegate call fails, the function will revert the transaction, returning the error message + /// provided by the delegated contract. + /// @dev Compatible with Era only. + /// @param _forceDeployments the list of initial deployments that should be performed before the upgrade. + /// They would typically, though not necessarily include the deployment of the upgrade implementation itself. + /// @param _delegateTo the address of the contract to which the calls will be delegated + /// @param _calldata the calldata to be delegate called in the `_delegateTo` contract + function forceDeployAndUpgrade( + IL2ContractDeployer.ForceDeployment[] calldata _forceDeployments, + address _delegateTo, + bytes calldata _calldata + ) external payable onlyForceDeployer { + IL2ContractDeployer(L2_DEPLOYER_SYSTEM_CONTRACT_ADDR).forceDeployOnAddresses(_forceDeployments); + + upgrade(_delegateTo, _calldata); + } + + /// @notice Executes an upgrade process by delegating calls to another contract. + /// @dev Similar to `forceDeployAndUpgrade`, but allows for universal force deployments, that + /// work for both ZKsyncOS and Era. + /// @param _forceDeployments the list of initial deployments that should be performed before the upgrade. + /// They would typically, though not necessarily include the deployment of the upgrade implementation itself. + /// @param _delegateTo the address of the contract to which the calls will be delegated + /// @param _calldata the calldata to be delegate called in the `_delegateTo` contract + function forceDeployAndUpgradeUniversal( + UniversalForceDeploymentInfo[] calldata _forceDeployments, + address _delegateTo, + bytes calldata _calldata + ) external payable onlyForceDeployer { + // solhint-disable-next-line gas-length-in-loops + for (uint256 i = 0; i < _forceDeployments.length; ++i) { + L2GenesisForceDeploymentsHelper.forceDeployOnAddress( + _forceDeployments[i].isZKsyncOS, + _forceDeployments[i].deployedBytecodeInfo, + _forceDeployments[i].newAddress + ); + } + + upgrade(_delegateTo, _calldata); + } + + /// @notice Executes an upgrade process by delegating calls to another contract. + /// @dev This function allows only the `FORCE_DEPLOYER` to initiate the upgrade. + /// If the delegate call fails, the function will revert the transaction, returning the error message + /// provided by the delegated contract. + /// @param _delegateTo the address of the contract to which the calls will be delegated + /// @param _calldata the calldata to be delegate called in the `_delegateTo` contract + function upgrade(address _delegateTo, bytes calldata _calldata) public payable onlyForceDeployer { + if (_delegateTo.code.length == 0) { + revert AddressHasNoCode(_delegateTo); + } + // slither-disable-next-line controlled-delegatecall + (bool success, bytes memory returnData) = _delegateTo.delegatecall(_calldata); + assembly { + if iszero(success) { + revert(add(returnData, 0x20), mload(returnData)) + } + } + } +} diff --git a/system-contracts/contracts/test-deps/l2-upgrades/L2GenesisForceDeploymentsHelper.sol b/system-contracts/contracts/test-deps/l2-upgrades/L2GenesisForceDeploymentsHelper.sol new file mode 100644 index 0000000000..ffd8f704a2 --- /dev/null +++ b/system-contracts/contracts/test-deps/l2-upgrades/L2GenesisForceDeploymentsHelper.sol @@ -0,0 +1,333 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.28; + +import {GW_ASSET_TRACKER_ADDR, L2_ASSET_TRACKER_ADDR, L2_ASSET_ROUTER_ADDR, L2_BRIDGEHUB_ADDR, L2_CHAIN_ASSET_HANDLER_ADDR, L2_DEPLOYER_SYSTEM_CONTRACT_ADDR, L2_MESSAGE_ROOT_ADDR, L2_NATIVE_TOKEN_VAULT_ADDR, L2_NTV_BEACON_DEPLOYER_ADDR, L2_WRAPPED_BASE_TOKEN_IMPL_ADDR} from "./Deps.sol"; +import {IContractDeployer as IL2ContractDeployer } from "../../interfaces/IContractDeployer.sol"; +import {FixedForceDeploymentsData, ZKChainSpecificForceDeploymentsData} from "../../interfaces/IL2GenesisUpgrade.sol"; +import {IL2WrappedBaseToken} from "../bridge/interfaces/IL2WrappedBaseToken.sol"; + +import {TransparentUpgradeableProxy} from "@openzeppelin/contracts-v4/proxy/transparent/TransparentUpgradeableProxy.sol"; + +import {IZKOSContractDeployer} from "./IZKOSContractDeployer.sol"; +import {L2NativeTokenVault} from "../bridge/ntv/L2NativeTokenVault.sol"; +import {L2MessageRoot} from "../bridgehub/L2MessageRoot.sol"; +import {L2Bridgehub} from "../bridgehub/L2Bridgehub.sol"; +import {L2AssetRouter} from "../bridge/asset-router/L2AssetRouter.sol"; +import {L2AssetTracker} from "../bridge/asset-tracker/L2AssetTracker.sol"; +import {GWAssetTracker} from "../bridge/asset-tracker/GWAssetTracker.sol"; +import {L2ChainAssetHandler} from "../bridgehub/L2ChainAssetHandler.sol"; +import {DeployFailed} from "../common/L1ContractErrors.sol"; + +import {L2NativeTokenVaultZKOS} from "../bridge/ntv/L2NativeTokenVaultZKOS.sol"; + +import {ICTMDeploymentTracker} from "../bridgehub/ICTMDeploymentTracker.sol"; +import {IMessageRoot} from "../bridgehub/IMessageRoot.sol"; + +import {UpgradeableBeaconDeployer} from "../bridge/ntv/UpgradeableBeaconDeployer.sol"; + +/// @title L2GenesisForceDeploymentsHelper +/// @author Matter Labs +/// @custom:security-contact security@matterlabs.dev +/// @notice A helper library for initializing and managing force-deployed contracts during either the L2 gateway upgrade or +/// the genesis after the gateway protocol upgrade. +library L2GenesisForceDeploymentsHelper { + function forceDeployEra(bytes memory _bytecodeInfo, address _newAddress) internal { + bytes32 bytecodeHash = abi.decode(_bytecodeInfo, (bytes32)); + IL2ContractDeployer.ForceDeployment[] memory forceDeployments = new IL2ContractDeployer.ForceDeployment[](1); + forceDeployments[0] = IL2ContractDeployer.ForceDeployment({ + bytecodeHash: bytecodeHash, + newAddress: _newAddress, + callConstructor: false, + value: 0, + input: hex"" + }); + + IL2ContractDeployer(L2_DEPLOYER_SYSTEM_CONTRACT_ADDR).forceDeployOnAddresses(forceDeployments); + } + + function forceDeployZKsyncOS(bytes memory _bytecodeInfo, address _newAddress) internal { + (bytes32 bytecodeHash, uint32 bytecodeLength, bytes32 observableBytecodeHash) = abi.decode( + _bytecodeInfo, + (bytes32, uint32, bytes32) + ); + + bytes memory data = abi.encodeCall( + IZKOSContractDeployer.setBytecodeDetailsEVM, + (_newAddress, bytecodeHash, bytecodeLength, observableBytecodeHash) + ); + + // Note, that we dont use interface, but raw call to avoid Solidity checking for empty bytecode + (bool success, ) = L2_DEPLOYER_SYSTEM_CONTRACT_ADDR.call(data); + if (!success) { + revert DeployFailed(); + } + } + + /// @notice Unified function to force deploy contracts based on whether it's ZKSyncOS or Era. + /// @param _isZKsyncOS Whether the deployment is for ZKSyncOS or Era. + /// @param _bytecodeInfo The bytecode information for deployment. + /// @param _newAddress The address where the contract should be deployed. + function forceDeployOnAddress(bool _isZKsyncOS, bytes memory _bytecodeInfo, address _newAddress) internal { + if (_isZKsyncOS) { + forceDeployZKsyncOS(_bytecodeInfo, _newAddress); + } else { + forceDeployEra(_bytecodeInfo, _newAddress); + } + } + + /// @notice Initializes force-deployed contracts. + /// @param _ctmDeployer Address of the CTM Deployer contract. + /// @param _fixedForceDeploymentsData Encoded data for forced deployment that + /// is the same for all the chains. + /// @param _additionalForceDeploymentsData Encoded data for force deployments that + /// is specific for each ZK Chain. + function performForceDeployedContractsInit( + bool _isZKsyncOS, + address _ctmDeployer, + bytes memory _fixedForceDeploymentsData, + bytes memory _additionalForceDeploymentsData, + bool _isGenesisUpgrade + ) internal { + // Decode the fixed and additional force deployments data. + FixedForceDeploymentsData memory fixedForceDeploymentsData = abi.decode( + _fixedForceDeploymentsData, + (FixedForceDeploymentsData) + ); + ZKChainSpecificForceDeploymentsData memory additionalForceDeploymentsData = abi.decode( + _additionalForceDeploymentsData, + (ZKChainSpecificForceDeploymentsData) + ); + + forceDeployOnAddress( + _isZKsyncOS, + fixedForceDeploymentsData.messageRootBytecodeInfo, + address(L2_MESSAGE_ROOT_ADDR) + ); + // If this is a genesis upgrade, we need to initialize the MessageRoot contract. + // We dont need to do anything for already deployed chains. + if (_isGenesisUpgrade) { + L2MessageRoot(L2_MESSAGE_ROOT_ADDR).initL2( + fixedForceDeploymentsData.l1ChainId, + fixedForceDeploymentsData.gatewayChainId + ); + } + + forceDeployOnAddress(_isZKsyncOS, fixedForceDeploymentsData.bridgehubBytecodeInfo, address(L2_BRIDGEHUB_ADDR)); + if (_isGenesisUpgrade) { + L2Bridgehub(L2_BRIDGEHUB_ADDR).initL2( + fixedForceDeploymentsData.l1ChainId, + fixedForceDeploymentsData.aliasedL1Governance, + fixedForceDeploymentsData.maxNumberOfZKChains + ); + } else { + L2Bridgehub(L2_BRIDGEHUB_ADDR).updateL2( + fixedForceDeploymentsData.l1ChainId, + fixedForceDeploymentsData.maxNumberOfZKChains + ); + } + + // For new chains, there is no legacy shared bridge, but the already existing ones, + // we should be able to query it. + address l2LegacySharedBridge = _isGenesisUpgrade + ? address(0) + : L2AssetRouter(L2_ASSET_ROUTER_ADDR).L2_LEGACY_SHARED_BRIDGE(); + + forceDeployOnAddress( + _isZKsyncOS, + fixedForceDeploymentsData.l2AssetRouterBytecodeInfo, + address(L2_ASSET_ROUTER_ADDR) + ); + if (_isGenesisUpgrade) { + // solhint-disable-next-line func-named-parameters + L2AssetRouter(L2_ASSET_ROUTER_ADDR).initL2( + fixedForceDeploymentsData.l1ChainId, + fixedForceDeploymentsData.eraChainId, + fixedForceDeploymentsData.l1AssetRouter, + l2LegacySharedBridge, + additionalForceDeploymentsData.baseTokenAssetId, + fixedForceDeploymentsData.aliasedL1Governance + ); + } else { + // solhint-disable-next-line func-named-parameters + L2AssetRouter(L2_ASSET_ROUTER_ADDR).updateL2( + fixedForceDeploymentsData.l1ChainId, + fixedForceDeploymentsData.eraChainId, + fixedForceDeploymentsData.l1AssetRouter, + l2LegacySharedBridge, + additionalForceDeploymentsData.baseTokenAssetId + ); + } + + address predeployedL2WethAddress = _isGenesisUpgrade + ? address(0) + : L2NativeTokenVault(L2_NATIVE_TOKEN_VAULT_ADDR).WETH_TOKEN(); + bytes32 previousL2TokenProxyBytecodeHash = _isGenesisUpgrade + ? bytes32(0) + : L2NativeTokenVault(L2_NATIVE_TOKEN_VAULT_ADDR).L2_TOKEN_PROXY_BYTECODE_HASH(); + + // Ensure the WETH token is deployed and retrieve its address. + address wrappedBaseTokenAddress = _ensureWethToken({ + _predeployedWethToken: predeployedL2WethAddress, + _aliasedL1Governance: fixedForceDeploymentsData.aliasedL1Governance, + _baseTokenL1Address: additionalForceDeploymentsData.baseTokenL1Address, + _baseTokenAssetId: additionalForceDeploymentsData.baseTokenAssetId, + _baseTokenName: additionalForceDeploymentsData.baseTokenName, + _baseTokenSymbol: additionalForceDeploymentsData.baseTokenSymbol + }); + + // Now initializing the upgradeable token beacon + forceDeployOnAddress(_isZKsyncOS, fixedForceDeploymentsData.l2NtvBytecodeInfo, L2_NATIVE_TOKEN_VAULT_ADDR); + + if (_isGenesisUpgrade) { + address deployedTokenBeacon; + // In production, the `fixedForceDeploymentsData.dangerousTestOnlyForcedBeacon` must always + // be equal to 0. It is only for simplifying testing. + if (fixedForceDeploymentsData.dangerousTestOnlyForcedBeacon == address(0)) { + // We need to deploy the beacon, we will use a separate contract for that to save + // up on size of this contract. + forceDeployOnAddress( + _isZKsyncOS, + fixedForceDeploymentsData.beaconDeployerInfo, + L2_NTV_BEACON_DEPLOYER_ADDR + ); + + deployedTokenBeacon = UpgradeableBeaconDeployer(L2_NTV_BEACON_DEPLOYER_ADDR).deployUpgradeableBeacon( + fixedForceDeploymentsData.aliasedL1Governance + ); + } else { + deployedTokenBeacon = fixedForceDeploymentsData.dangerousTestOnlyForcedBeacon; + } + + // solhint-disable-next-line func-named-parameters + L2NativeTokenVaultZKOS(L2_NATIVE_TOKEN_VAULT_ADDR).initL2( + fixedForceDeploymentsData.l1ChainId, + fixedForceDeploymentsData.aliasedL1Governance, + fixedForceDeploymentsData.l2TokenProxyBytecodeHash, + additionalForceDeploymentsData.l2LegacySharedBridge, + deployedTokenBeacon, + wrappedBaseTokenAddress, + additionalForceDeploymentsData.baseTokenAssetId, + additionalForceDeploymentsData.baseTokenOriginAddress + ); + } else { + // solhint-disable-next-line func-named-parameters + L2NativeTokenVault(L2_NATIVE_TOKEN_VAULT_ADDR).updateL2( + fixedForceDeploymentsData.l1ChainId, + previousL2TokenProxyBytecodeHash, + l2LegacySharedBridge, + wrappedBaseTokenAddress, + additionalForceDeploymentsData.baseTokenAssetId, + additionalForceDeploymentsData.baseTokenOriginAddress + ); + } + + forceDeployOnAddress( + _isZKsyncOS, + fixedForceDeploymentsData.chainAssetHandlerBytecodeInfo, + address(L2_CHAIN_ASSET_HANDLER_ADDR) + ); + if (_isGenesisUpgrade) { + // solhint-disable-next-line func-named-parameters + L2ChainAssetHandler(L2_CHAIN_ASSET_HANDLER_ADDR).initL2( + fixedForceDeploymentsData.l1ChainId, + fixedForceDeploymentsData.aliasedL1Governance, + L2_BRIDGEHUB_ADDR, + L2_ASSET_ROUTER_ADDR, + L2_MESSAGE_ROOT_ADDR + ); + } else { + L2ChainAssetHandler(L2_CHAIN_ASSET_HANDLER_ADDR).updateL2( + fixedForceDeploymentsData.l1ChainId, + L2_BRIDGEHUB_ADDR, + L2_ASSET_ROUTER_ADDR, + L2_MESSAGE_ROOT_ADDR + ); + } + + // It is expected that either through the force deployments above + // or upon initialization, both the L2 deployment of BridgeHub, AssetRouter, and MessageRoot are deployed. + // However, there is still some follow-up finalization that needs to be done. + L2Bridgehub(L2_BRIDGEHUB_ADDR).setAddresses({ + _assetRouter: L2_ASSET_ROUTER_ADDR, + _l1CtmDeployer: ICTMDeploymentTracker(_ctmDeployer), + _messageRoot: IMessageRoot(L2_MESSAGE_ROOT_ADDR), + _chainAssetHandler: L2_CHAIN_ASSET_HANDLER_ADDR, + _chainRegistrationSender: fixedForceDeploymentsData.aliasedChainRegistrationSender + }); + + L2AssetTracker(L2_ASSET_TRACKER_ADDR).setAddresses( + fixedForceDeploymentsData.l1ChainId, + additionalForceDeploymentsData.baseTokenAssetId + ); + + GWAssetTracker(GW_ASSET_TRACKER_ADDR).setAddresses(fixedForceDeploymentsData.l1ChainId); + + L2NativeTokenVault(L2_NATIVE_TOKEN_VAULT_ADDR).setAddresses( + additionalForceDeploymentsData.baseTokenOriginChainId + ); + } + + /// @notice Constructs the initialization calldata for the L2WrappedBaseToken. + /// @param _wrappedBaseTokenName The name of the wrapped base token. + /// @param _wrappedBaseTokenSymbol The symbol of the wrapped base token. + /// @param _baseTokenL1Address The L1 address of the base token. + /// @param _baseTokenAssetId The asset ID of the base token. + /// @return initData The encoded initialization calldata. + function getWethInitData( + string memory _wrappedBaseTokenName, + string memory _wrappedBaseTokenSymbol, + address _baseTokenL1Address, + bytes32 _baseTokenAssetId + ) internal pure returns (bytes memory initData) { + initData = abi.encodeCall( + IL2WrappedBaseToken.initializeV3, + ( + _wrappedBaseTokenName, + _wrappedBaseTokenSymbol, + L2_ASSET_ROUTER_ADDR, + _baseTokenL1Address, + _baseTokenAssetId + ) + ); + } + + /// @notice Ensures that the WETH token is deployed. If not predeployed, deploys it. + /// @param _predeployedWethToken The potential address of the predeployed WETH token. + /// @param _aliasedL1Governance Address of the aliased L1 governance. + /// @param _baseTokenL1Address L1 address of the base token. + /// @param _baseTokenAssetId Asset ID of the base token. + /// @param _baseTokenName Name of the base token. + /// @param _baseTokenSymbol Symbol of the base token. + /// @return The address of the ensured WETH token. + function _ensureWethToken( + address _predeployedWethToken, + address _aliasedL1Governance, + address _baseTokenL1Address, + bytes32 _baseTokenAssetId, + string memory _baseTokenName, + string memory _baseTokenSymbol + ) private returns (address) { + if (_predeployedWethToken != address(0)) { + return _predeployedWethToken; + } + + string memory wrappedBaseTokenName = string.concat("Wrapped ", _baseTokenName); + string memory wrappedBaseTokenSymbol = string.concat("W", _baseTokenSymbol); + + bytes memory initData = getWethInitData( + wrappedBaseTokenName, + wrappedBaseTokenSymbol, + _baseTokenL1Address, + _baseTokenAssetId + ); + + TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy{salt: bytes32(0)}( + L2_WRAPPED_BASE_TOKEN_IMPL_ADDR, + _aliasedL1Governance, + initData + ); + + return address(proxy); + } +} diff --git a/system-contracts/contracts/test-deps/l2-upgrades/L2GenesisUpgrade.sol b/system-contracts/contracts/test-deps/l2-upgrades/L2GenesisUpgrade.sol new file mode 100644 index 0000000000..358a4f5efd --- /dev/null +++ b/system-contracts/contracts/test-deps/l2-upgrades/L2GenesisUpgrade.sol @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.28; + +import {L2_SYSTEM_CONTEXT_SYSTEM_CONTRACT_ADDR} from "../common/l2-helpers/L2ContractAddresses.sol"; +import {ISystemContext} from "../common/interfaces/ISystemContext.sol"; +import {IL2GenesisUpgrade} from "../common/interfaces/IL2GenesisUpgrade.sol"; + +import {L2GenesisForceDeploymentsHelper} from "./L2GenesisForceDeploymentsHelper.sol"; + +import {InvalidChainId} from "./Deps.sol"; + +/// @custom:security-contact security@matterlabs.dev +/// @author Matter Labs +/// @notice The l2 component of the genesis upgrade. +contract L2GenesisUpgrade is IL2GenesisUpgrade { + /// @notice The function that is delegateCalled from the complex upgrader. + /// @dev It is used to set the chainId and to deploy the force deployments. + /// @param _chainId the chain id + /// @param _ctmDeployer the address of the ctm deployer + /// @param _fixedForceDeploymentsData the force deployments data + /// @param _additionalForceDeploymentsData the additional force deployments data + // slither-disable-next-line locked-ether + function genesisUpgrade( + bool _isZKsyncOS, + uint256 _chainId, + address _ctmDeployer, + bytes calldata _fixedForceDeploymentsData, + bytes calldata _additionalForceDeploymentsData + ) external { + if (_chainId == 0) { + revert InvalidChainId(); + } + + // On ZKsyncOS, the chain Id is a part of implicit block properties + // and so does not need to set inside the genesis upgrade. + if (!_isZKsyncOS) { + ISystemContext(L2_SYSTEM_CONTEXT_SYSTEM_CONTRACT_ADDR).setChainId(_chainId); + } + + // solhint-disable-next-line func-named-parameters + L2GenesisForceDeploymentsHelper.performForceDeployedContractsInit( + _isZKsyncOS, + _ctmDeployer, + _fixedForceDeploymentsData, + _additionalForceDeploymentsData, + true + ); + + emit UpgradeComplete(_chainId); + } +} diff --git a/system-contracts/contracts/test-deps/l2-upgrades/L2V30Upgrade.sol b/system-contracts/contracts/test-deps/l2-upgrades/L2V30Upgrade.sol new file mode 100644 index 0000000000..620d3dd882 --- /dev/null +++ b/system-contracts/contracts/test-deps/l2-upgrades/L2V30Upgrade.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.28; + +/// @dev Storage slot with the admin of the contract used for EIP‑1967 proxies (e.g., TUP, BeaconProxy, etc.). +bytes32 constant PROXY_ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; + +/// @custom:security-contact security@matterlabs.dev +/// @author Matter Labs +/// @title L2V30Upgrade, contains v30 upgrade fixes. +/// @dev This contract is neither predeployed nor a system contract. It resides in this folder to facilitate code reuse. +/// @dev This contract is called during the forceDeployAndUpgrade function of the ComplexUpgrader system contract. +contract L2V30Upgrade { + /// @notice Executes the one‑time migration/patch. + /// @dev Intended to be delegate‑called by the `ComplexUpgrader` contract. + /// @param _baseTokenOriginChainId The chainId of the origin chain of the base token. + /// @param _baseTokenOriginAddress The address of the base token on the origin chain. + function upgrade(uint256 _baseTokenOriginChainId, address _baseTokenOriginAddress) external { + // kl todo set baseTokenOriginChainId and baseTokenOriginAddress in some location. + // kl todo add all setAddresses, initL2 and updateL2s from genesis upgrade. + } +}