From 9615d90278dc71201e17254e408d9a172b96b40a Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Tue, 10 Sep 2024 18:41:32 +0200 Subject: [PATCH 01/20] resolve most of the conflicts --- .../contracts/bridge/L1AssetRouter.sol | 8 +- .../contracts/bridge/L1ERC20Bridge.sol | 128 +-- .../contracts/bridge/L1NativeTokenVault.sol | 8 +- .../contracts/bridge/L1SharedBridge.sol | 918 ------------------ .../bridge/interfaces/IL1ERC20Bridge.sol | 4 - .../contracts/bridgehub/Bridgehub.sol | 171 +--- .../dev-contracts/test/DummyBridgehub.sol | 23 +- .../dev-contracts/test/DummySharedBridge.sol | 24 +- .../contracts/governance/ChainAdmin.sol | 27 - .../contracts/governance/IChainAdmin.sol | 7 - .../StateTransitionManager.sol | 32 - .../state-transition/ValidatorTimelock.sol | 3 - .../chain-deps/DiamondInit.sol | 3 - .../chain-deps/facets/Admin.sol | 3 - .../chain-deps/facets/Executor.sol | 229 +---- .../chain-deps/facets/Mailbox.sol | 67 +- .../IZkSyncHyperchainBase.sol | 4 - l1-contracts/deploy-scripts/AcceptAdmin.s.sol | 24 - l1-contracts/deploy-scripts/DeployErc20.s.sol | 12 - l1-contracts/deploy-scripts/DeployL1.s.sol | 18 - .../deploy-scripts/DeployL2Contracts.sol | 24 - .../deploy-scripts/RegisterHyperchain.s.sol | 10 - l1-contracts/deploy-scripts/Utils.sol | 6 +- l1-contracts/foundry.toml | 17 +- l1-contracts/scripts/register-hyperchain.ts | 8 - l1-contracts/src.ts/deploy-process.ts | 10 - l1-contracts/src.ts/deploy.ts | 45 +- l2-contracts/contracts/L2ContractHelper.sol | 2 +- .../contracts/bridge/L2AssetRouter.sol | 2 +- .../contracts/bridge/L2NativeTokenVault.sol | 2 +- .../contracts/bridge/L2StandardERC20.sol | 19 - .../contracts/bridge/L2WrappedBaseToken.sol | 10 - .../contracts/errors/L2ContractErrors.sol | 6 - l2-contracts/test/erc20.test.ts | 25 - system-contracts/SystemContractsHashes.json | 100 -- system-contracts/contracts/L1Messenger.sol | 43 - .../contracts/PubdataChunkPublisher.sol | 7 - .../contracts/interfaces/IComplexUpgrader.sol | 4 - system-contracts/package.json | 4 - system-contracts/scripts/utils.ts | 4 +- yarn.lock | 18 +- 41 files changed, 78 insertions(+), 2001 deletions(-) delete mode 100644 l1-contracts/contracts/bridge/L1SharedBridge.sol diff --git a/l1-contracts/contracts/bridge/L1AssetRouter.sol b/l1-contracts/contracts/bridge/L1AssetRouter.sol index b6845cba0..9a4a16bce 100644 --- a/l1-contracts/contracts/bridge/L1AssetRouter.sol +++ b/l1-contracts/contracts/bridge/L1AssetRouter.sol @@ -4,11 +4,11 @@ pragma solidity 0.8.24; // solhint-disable reason-string, gas-custom-errors -import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; -import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol"; +import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable-v4/access/Ownable2StepUpgradeable.sol"; +import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable-v4/security/PausableUpgradeable.sol"; -import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import {IERC20} from "@openzeppelin/contracts-v4/token/ERC20/IERC20.sol"; +import {SafeERC20} from "@openzeppelin/contracts-v4/token/ERC20/utils/SafeERC20.sol"; import {IL1ERC20Bridge} from "./interfaces/IL1ERC20Bridge.sol"; import {IL1AssetRouter} from "./interfaces/IL1AssetRouter.sol"; diff --git a/l1-contracts/contracts/bridge/L1ERC20Bridge.sol b/l1-contracts/contracts/bridge/L1ERC20Bridge.sol index fdc00acb4..39c8879b2 100644 --- a/l1-contracts/contracts/bridge/L1ERC20Bridge.sol +++ b/l1-contracts/contracts/bridge/L1ERC20Bridge.sol @@ -16,11 +16,7 @@ import {Unauthorized, EmptyDeposit, TokensWithFeesNotSupported, WithdrawalAlread /// @author Matter Labs /// @custom:security-contact security@matterlabs.dev -<<<<<<< HEAD /// @notice Smart contract that allows depositing ERC20 tokens from Ethereum to ZK chains -======= -/// @notice Smart contract that allows depositing ERC20 tokens from Ethereum to hyperchains ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe /// @dev It is a legacy bridge from ZKsync Era, that was deprecated in favour of shared bridge. /// It is needed for backward compatibility with already integrated projects. contract L1ERC20Bridge is IL1ERC20Bridge, ReentrancyGuard { @@ -46,11 +42,7 @@ contract L1ERC20Bridge is IL1ERC20Bridge, ReentrancyGuard { mapping(address account => mapping(address l1Token => mapping(bytes32 depositL2TxHash => uint256 amount))) public depositAmount; -<<<<<<< HEAD - /// @dev The address that is used as a L2 Shared Bridge in ZKsync Era. -======= /// @dev The address that is used as a L2 bridge counterpart in ZKsync Era. ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe // slither-disable-next-line uninitialized-state address public l2Bridge; @@ -86,17 +78,6 @@ contract L1ERC20Bridge is IL1ERC20Bridge, ReentrancyGuard { /// @dev Initializes the reentrancy guard. Expected to be used in the proxy. function initialize() external reentrancyGuardInitializer {} -<<<<<<< HEAD -======= - /// @dev transfer token to shared bridge as part of upgrade - function transferTokenToSharedBridge(address _token) external { - if (msg.sender != address(SHARED_BRIDGE)) { - revert Unauthorized(msg.sender); - } - uint256 amount = IERC20(_token).balanceOf(address(this)); - IERC20(_token).safeTransfer(address(SHARED_BRIDGE), amount); - } - /*////////////////////////////////////////////////////////////// ERA LEGACY GETTERS //////////////////////////////////////////////////////////////*/ @@ -186,7 +167,7 @@ contract L1ERC20Bridge is IL1ERC20Bridge, ReentrancyGuard { } l2TxHash = SHARED_BRIDGE.depositLegacyErc20Bridge{value: msg.value}({ - _msgSender: msg.sender, + _prevMsgSender: msg.sender, _l2Receiver: _l2Receiver, _l1Token: _l1Token, _amount: _amount, @@ -214,7 +195,6 @@ contract L1ERC20Bridge is IL1ERC20Bridge, ReentrancyGuard { return balanceAfter - balanceBefore; } ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe /// @dev Withdraw funds from the initiated deposit, that failed when finalizing on L2. /// @param _depositSender The address of the deposit initiator /// @param _l1Token The address of the deposited L1 ERC20 token @@ -257,35 +237,6 @@ contract L1ERC20Bridge is IL1ERC20Bridge, ReentrancyGuard { ERA LEGACY FUNCTIONS //////////////////////////////////////////////////////////////*/ - /// @notice Legacy deposit method with refunding the fee to the caller, use another `deposit` method instead. - /// @dev Initiates a deposit by locking funds on the contract and sending the request - /// of processing an L2 transaction where tokens would be minted. - /// @dev If the token is bridged for the first time, the L2 token contract will be deployed. Note however, that the - /// newly-deployed token does not support any custom logic, i.e. rebase tokens' functionality is not supported. - /// @param _l2Receiver The account address that should receive funds on L2 - /// @param _l1Token The L1 token address which is deposited - /// @param _amount The total amount of tokens to be bridged - /// @param _l2TxGasLimit The L2 gas limit to be used in the corresponding L2 transaction - /// @param _l2TxGasPerPubdataByte The gasPerPubdataByteLimit to be used in the corresponding L2 transaction - /// @return txHash The L2 transaction hash of deposit finalization - /// NOTE: the function doesn't use `nonreentrant` modifier, because the inner method does. - function deposit( - address _l2Receiver, - address _l1Token, - uint256 _amount, - uint256 _l2TxGasLimit, - uint256 _l2TxGasPerPubdataByte - ) external payable returns (bytes32 txHash) { - txHash = deposit({ - _l2Receiver: _l2Receiver, - _l1Token: _l1Token, - _amount: _amount, - _l2TxGasLimit: _l2TxGasLimit, - _l2TxGasPerPubdataByte: _l2TxGasPerPubdataByte, - _refundRecipient: address(0) - }); - } - /// @notice Finalize the withdrawal and release funds /// @param _l2BatchNumber The L2 batch number where the withdrawal was processed /// @param _l2MessageIndex The position in the L2 logs Merkle tree of the l2Log that was sent with the message @@ -313,81 +264,4 @@ contract L1ERC20Bridge is IL1ERC20Bridge, ReentrancyGuard { }); emit WithdrawalFinalized(l1Receiver, l1Token, amount); } - - /// @notice Initiates a deposit by locking funds on the contract and sending the request - /// @dev Initiates a deposit by locking funds on the contract and sending the request - /// of processing an L2 transaction where tokens would be minted - /// @dev If the token is bridged for the first time, the L2 token contract will be deployed. Note however, that the - /// newly-deployed token does not support any custom logic, i.e. rebase tokens' functionality is not supported. - /// @param _l2Receiver The account address that should receive funds on L2 - /// @param _l1Token The L1 token address which is deposited - /// @param _amount The total amount of tokens to be bridged - /// @param _l2TxGasLimit The L2 gas limit to be used in the corresponding L2 transaction - /// @param _l2TxGasPerPubdataByte The gasPerPubdataByteLimit to be used in the corresponding L2 transaction - /// @param _refundRecipient The address on L2 that will receive the refund for the transaction. - /// @dev If the L2 deposit finalization transaction fails, the `_refundRecipient` will receive the `_l2Value`. - /// Please note, the contract may change the refund recipient's address to eliminate sending funds to addresses - /// out of control. - /// - If `_refundRecipient` is a contract on L1, the refund will be sent to the aliased `_refundRecipient`. - /// - If `_refundRecipient` is set to `address(0)` and the sender has NO deployed bytecode on L1, the refund will - /// be sent to the `msg.sender` address. - /// - If `_refundRecipient` is set to `address(0)` and the sender has deployed bytecode on L1, the refund will be - /// sent to the aliased `msg.sender` address. - /// @dev The address aliasing of L1 contracts as refund recipient on L2 is necessary to guarantee that the funds - /// are controllable through the Mailbox, since the Mailbox applies address aliasing to the from address for the - /// L2 tx if the L1 msg.sender is a contract. Without address aliasing for L1 contracts as refund recipients they - /// would not be able to make proper L2 tx requests through the Mailbox to use or withdraw the funds from L2, and - /// the funds would be lost. - /// @return txHash The L2 transaction hash of deposit finalization - function deposit( - address _l2Receiver, - address _l1Token, - uint256 _amount, - uint256 _l2TxGasLimit, - uint256 _l2TxGasPerPubdataByte, - address _refundRecipient - ) public payable nonReentrant returns (bytes32 txHash) { - require(_amount != 0, "0T"); // empty deposit - uint256 amount = _depositFundsToSharedBridge(msg.sender, IERC20(_l1Token), _amount); - require(amount == _amount, "3T"); // The token has non-standard transfer logic - - txHash = SHARED_BRIDGE.depositLegacyErc20Bridge{value: msg.value}({ - _prevMsgSender: msg.sender, - _l2Receiver: _l2Receiver, - _l1Token: _l1Token, - _amount: _amount, - _l2TxGasLimit: _l2TxGasLimit, - _l2TxGasPerPubdataByte: _l2TxGasPerPubdataByte, - _refundRecipient: _refundRecipient - }); - depositAmount[msg.sender][_l1Token][txHash] = _amount; - emit DepositInitiated({ - l2DepositTxHash: txHash, - from: msg.sender, - to: _l2Receiver, - l1Token: _l1Token, - amount: _amount - }); - } - - /// @dev Transfers tokens from the depositor address to the shared bridge address. - /// @return The difference between the contract balance before and after the transferring of funds. - function _depositFundsToSharedBridge(address _from, IERC20 _token, uint256 _amount) internal returns (uint256) { - uint256 balanceBefore = _token.balanceOf(address(SHARED_BRIDGE)); - _token.safeTransferFrom(_from, address(SHARED_BRIDGE), _amount); - uint256 balanceAfter = _token.balanceOf(address(SHARED_BRIDGE)); - return balanceAfter - balanceBefore; - } - - /*////////////////////////////////////////////////////////////// - ERA LEGACY GETTERS - //////////////////////////////////////////////////////////////*/ - - /// @return The L2 token address that would be minted for deposit of the given L1 token on zkSync Era. - function l2TokenAddress(address _l1Token) external view returns (address) { - bytes32 constructorInputHash = keccak256(abi.encode(l2TokenBeacon, "")); - bytes32 salt = bytes32(uint256(uint160(_l1Token))); - - return L2ContractHelper.computeCreate2Address(l2Bridge, salt, l2TokenProxyBytecodeHash, constructorInputHash); - } } diff --git a/l1-contracts/contracts/bridge/L1NativeTokenVault.sol b/l1-contracts/contracts/bridge/L1NativeTokenVault.sol index 1d8cd0973..fba532597 100644 --- a/l1-contracts/contracts/bridge/L1NativeTokenVault.sol +++ b/l1-contracts/contracts/bridge/L1NativeTokenVault.sol @@ -4,11 +4,11 @@ pragma solidity 0.8.24; // solhint-disable reason-string, gas-custom-errors -import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; -import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol"; +import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable-v4/access/Ownable2StepUpgradeable.sol"; +import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable-v4/security/PausableUpgradeable.sol"; -import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import {IERC20} from "@openzeppelin/contracts-v4/token/ERC20/IERC20.sol"; +import {SafeERC20} from "@openzeppelin/contracts-v4/token/ERC20/utils/SafeERC20.sol"; import {IL1NativeTokenVault} from "./interfaces/IL1NativeTokenVault.sol"; import {IL1AssetHandler} from "./interfaces/IL1AssetHandler.sol"; diff --git a/l1-contracts/contracts/bridge/L1SharedBridge.sol b/l1-contracts/contracts/bridge/L1SharedBridge.sol deleted file mode 100644 index eec3c2e69..000000000 --- a/l1-contracts/contracts/bridge/L1SharedBridge.sol +++ /dev/null @@ -1,918 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity 0.8.24; - -import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable-v4/access/Ownable2StepUpgradeable.sol"; -import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable-v4/security/PausableUpgradeable.sol"; - -import {IERC20Metadata} from "@openzeppelin/contracts-v4/token/ERC20/extensions/IERC20Metadata.sol"; -import {IERC20} from "@openzeppelin/contracts-v4/token/ERC20/IERC20.sol"; -import {SafeERC20} from "@openzeppelin/contracts-v4/token/ERC20/utils/SafeERC20.sol"; - -import {IL1ERC20Bridge} from "./interfaces/IL1ERC20Bridge.sol"; -import {IL1SharedBridge} from "./interfaces/IL1SharedBridge.sol"; -import {IL2Bridge} from "./interfaces/IL2Bridge.sol"; - -import {IMailbox} from "../state-transition/chain-interfaces/IMailbox.sol"; -import {L2Message, TxStatus} from "../common/Messaging.sol"; -import {UnsafeBytes} from "../common/libraries/UnsafeBytes.sol"; -import {ReentrancyGuard} from "../common/ReentrancyGuard.sol"; -import {AddressAliasHelper} from "../vendor/AddressAliasHelper.sol"; -import {ETH_TOKEN_ADDRESS, TWO_BRIDGES_MAGIC_VALUE} from "../common/Config.sol"; -import {IBridgehub, L2TransactionRequestTwoBridgesInner, L2TransactionRequestDirect} from "../bridgehub/IBridgehub.sol"; -import {IGetters} from "../state-transition/chain-interfaces/IGetters.sol"; -import {L2_BASE_TOKEN_SYSTEM_CONTRACT_ADDR} from "../common/L2ContractAddresses.sol"; -import {Unauthorized, ZeroAddress, SharedBridgeValueAlreadySet, SharedBridgeKey, NoFundsTransferred, ZeroBalance, ValueMismatch, TokensWithFeesNotSupported, NonEmptyMsgValue, L2BridgeNotSet, TokenNotSupported, DepositIncorrectAmount, EmptyDeposit, DepositExists, AddressAlreadyUsed, InvalidProof, DepositDoesNotExist, InsufficientChainBalance, SharedBridgeValueNotSet, WithdrawalAlreadyFinalized, WithdrawFailed, L2WithdrawalMessageWrongLength, InvalidSelector, SharedBridgeBalanceMismatch, SharedBridgeValueNotSet} from "../common/L1ContractErrors.sol"; - -/// @author Matter Labs -/// @custom:security-contact security@matterlabs.dev -/// @dev Bridges assets between L1 and hyperchains, supporting both ETH and ERC20 tokens. -/// @dev Designed for use with a proxy for upgradability. -contract L1SharedBridge is IL1SharedBridge, ReentrancyGuard, Ownable2StepUpgradeable, PausableUpgradeable { - using SafeERC20 for IERC20; - - /// @dev The address of the WETH token on L1. - address public immutable override L1_WETH_TOKEN; - - /// @dev Bridgehub smart contract that is used to operate with L2 via asynchronous L2 <-> L1 communication. - IBridgehub public immutable override BRIDGE_HUB; - - /// @dev Era's chainID - uint256 internal immutable ERA_CHAIN_ID; - - /// @dev The address of ZKsync Era diamond proxy contract. - address internal immutable ERA_DIAMOND_PROXY; - - /// @dev Stores the first batch number on the ZKsync Era Diamond Proxy that was settled after Diamond proxy upgrade. - /// This variable is used to differentiate between pre-upgrade and post-upgrade Eth withdrawals. Withdrawals from batches older - /// than this value are considered to have been finalized prior to the upgrade and handled separately. - uint256 internal eraPostDiamondUpgradeFirstBatch; - - /// @dev Stores the first batch number on the ZKsync Era Diamond Proxy that was settled after L1ERC20 Bridge upgrade. - /// This variable is used to differentiate between pre-upgrade and post-upgrade ERC20 withdrawals. Withdrawals from batches older - /// than this value are considered to have been finalized prior to the upgrade and handled separately. - uint256 internal eraPostLegacyBridgeUpgradeFirstBatch; - - /// @dev Stores the ZKsync Era batch number that processes the last deposit tx initiated by the legacy bridge - /// This variable (together with eraLegacyBridgeLastDepositTxNumber) is used to differentiate between pre-upgrade and post-upgrade deposits. Deposits processed in older batches - /// than this value are considered to have been processed prior to the upgrade and handled separately. - /// We use this both for Eth and erc20 token deposits, so we need to update the diamond and bridge simultaneously. - uint256 internal eraLegacyBridgeLastDepositBatch; - - /// @dev The tx number in the _eraLegacyBridgeLastDepositBatch of the last deposit tx initiated by the legacy bridge - /// This variable (together with eraLegacyBridgeLastDepositBatch) is used to differentiate between pre-upgrade and post-upgrade deposits. Deposits processed in older txs - /// than this value are considered to have been processed prior to the upgrade and handled separately. - /// We use this both for Eth and erc20 token deposits, so we need to update the diamond and bridge simultaneously. - uint256 internal eraLegacyBridgeLastDepositTxNumber; - - /// @dev Legacy bridge smart contract that used to hold ERC20 tokens. - IL1ERC20Bridge public override legacyBridge; - - /// @dev A mapping chainId => bridgeProxy. Used to store the bridge proxy's address, and to see if it has been deployed yet. - mapping(uint256 chainId => address l2Bridge) public override l2BridgeAddress; - - /// @dev A mapping chainId => L2 deposit transaction hash => keccak256(abi.encode(account, tokenAddress, amount)) - /// @dev Tracks deposit transactions from L2 to enable users to claim their funds if a deposit fails. - mapping(uint256 chainId => mapping(bytes32 l2DepositTxHash => bytes32 depositDataHash)) - public - override depositHappened; - - /// @dev Tracks the processing status of L2 to L1 messages, indicating whether a message has already been finalized. - mapping(uint256 chainId => mapping(uint256 l2BatchNumber => mapping(uint256 l2ToL1MessageNumber => bool isFinalized))) - public isWithdrawalFinalized; - - /// @dev Indicates whether the hyperbridging is enabled for a given chain. - // slither-disable-next-line uninitialized-state - mapping(uint256 chainId => bool enabled) internal hyperbridgingEnabled; - - /// @dev Maps token balances for each chain to prevent unauthorized spending across hyperchains. - /// This serves as a security measure until hyperbridging is implemented. - /// NOTE: this function may be removed in the future, don't rely on it! - mapping(uint256 chainId => mapping(address l1Token => uint256 balance)) public chainBalance; - - /// @notice Checks that the message sender is the bridgehub. - modifier onlyBridgehub() { - if (msg.sender != address(BRIDGE_HUB)) { - revert Unauthorized(msg.sender); - } - _; - } - - /// @notice Checks that the message sender is the bridgehub or ZKsync Era Diamond Proxy. - modifier onlyBridgehubOrEra(uint256 _chainId) { - if (msg.sender != address(BRIDGE_HUB) && (_chainId != ERA_CHAIN_ID || msg.sender != ERA_DIAMOND_PROXY)) { - revert Unauthorized(msg.sender); - } - _; - } - - /// @notice Checks that the message sender is the legacy bridge. - modifier onlyLegacyBridge() { - if (msg.sender != address(legacyBridge)) { - revert Unauthorized(msg.sender); - } - _; - } - - /// @notice Checks that the message sender is the shared bridge itself. - modifier onlySelf() { - if (msg.sender != address(this)) { - revert Unauthorized(msg.sender); - } - _; - } - - /// @dev Contract is expected to be used as proxy implementation. - /// @dev Initialize the implementation to prevent Parity hack. - constructor( - address _l1WethAddress, - IBridgehub _bridgehub, - uint256 _eraChainId, - address _eraDiamondProxy - ) reentrancyGuardInitializer { - _disableInitializers(); - L1_WETH_TOKEN = _l1WethAddress; - BRIDGE_HUB = _bridgehub; - ERA_CHAIN_ID = _eraChainId; - ERA_DIAMOND_PROXY = _eraDiamondProxy; - } - - /// @dev Initializes a contract bridge for later use. Expected to be used in the proxy - /// @param _owner Address which can change L2 token implementation and upgrade the bridge - /// implementation. The owner is the Governor and separate from the ProxyAdmin from now on, so that the Governor can call the bridge. - function initialize(address _owner) external reentrancyGuardInitializer initializer { - if (_owner == address(0)) { - revert ZeroAddress(); - } - _transferOwnership(_owner); - } - - /// @dev This sets the first post diamond upgrade batch for era, used to check old eth withdrawals - /// @param _eraPostDiamondUpgradeFirstBatch The first batch number on the ZKsync Era Diamond Proxy that was settled after diamond proxy upgrade. - function setEraPostDiamondUpgradeFirstBatch(uint256 _eraPostDiamondUpgradeFirstBatch) external onlyOwner { - if (eraPostDiamondUpgradeFirstBatch != 0) { - revert SharedBridgeValueAlreadySet(SharedBridgeKey.PostUpgradeFirstBatch); - } - eraPostDiamondUpgradeFirstBatch = _eraPostDiamondUpgradeFirstBatch; - } - - /// @dev This sets the first post upgrade batch for era, used to check old token withdrawals - /// @param _eraPostLegacyBridgeUpgradeFirstBatch The first batch number on the ZKsync Era Diamond Proxy that was settled after legacy bridge upgrade. - function setEraPostLegacyBridgeUpgradeFirstBatch(uint256 _eraPostLegacyBridgeUpgradeFirstBatch) external onlyOwner { - if (eraPostLegacyBridgeUpgradeFirstBatch != 0) { - revert SharedBridgeValueAlreadySet(SharedBridgeKey.LegacyBridgeFirstBatch); - } - eraPostLegacyBridgeUpgradeFirstBatch = _eraPostLegacyBridgeUpgradeFirstBatch; - } - - /// @dev This sets the first post upgrade batch for era, used to check old withdrawals - /// @param _eraLegacyBridgeLastDepositBatch The the ZKsync Era batch number that processes the last deposit tx initiated by the legacy bridge - /// @param _eraLegacyBridgeLastDepositTxNumber The tx number in the _eraLegacyBridgeLastDepositBatch of the last deposit tx initiated by the legacy bridge - function setEraLegacyBridgeLastDepositTime( - uint256 _eraLegacyBridgeLastDepositBatch, - uint256 _eraLegacyBridgeLastDepositTxNumber - ) external onlyOwner { - if (eraLegacyBridgeLastDepositBatch != 0) { - revert SharedBridgeValueAlreadySet(SharedBridgeKey.LegacyBridgeLastDepositBatch); - } - if (eraLegacyBridgeLastDepositTxNumber != 0) { - revert SharedBridgeValueAlreadySet(SharedBridgeKey.LegacyBridgeLastDepositTxn); - } - eraLegacyBridgeLastDepositBatch = _eraLegacyBridgeLastDepositBatch; - eraLegacyBridgeLastDepositTxNumber = _eraLegacyBridgeLastDepositTxNumber; - } - - /// @dev Transfer tokens from legacy erc20 bridge or mailbox and set chainBalance as part of migration process. - /// @param _token The address of token to be transferred (address(1) for ether and contract address for ERC20). - /// @param _target The hyperchain or bridge contract address from where to transfer funds. - /// @param _targetChainId The chain ID of the corresponding hyperchain. - function transferFundsFromLegacy(address _token, address _target, uint256 _targetChainId) external onlySelf { - if (_token == ETH_TOKEN_ADDRESS) { - uint256 balanceBefore = address(this).balance; - IMailbox(_target).transferEthToSharedBridge(); - uint256 balanceAfter = address(this).balance; - if (balanceAfter <= balanceBefore) { - revert NoFundsTransferred(); - } - chainBalance[_targetChainId][ETH_TOKEN_ADDRESS] = - chainBalance[_targetChainId][ETH_TOKEN_ADDRESS] + - balanceAfter - - balanceBefore; - } else { - uint256 balanceBefore = IERC20(_token).balanceOf(address(this)); - uint256 legacyBridgeBalance = IERC20(_token).balanceOf(address(legacyBridge)); - if (legacyBridgeBalance == 0) { - revert ZeroBalance(); - } - IL1ERC20Bridge(_target).transferTokenToSharedBridge(_token); - uint256 balanceAfter = IERC20(_token).balanceOf(address(this)); - if (balanceAfter - balanceBefore < legacyBridgeBalance) { - revert SharedBridgeBalanceMismatch(); - } - chainBalance[_targetChainId][_token] = chainBalance[_targetChainId][_token] + legacyBridgeBalance; - } - } - - /// @dev transfer tokens from legacy erc20 bridge or mailbox and set chainBalance as part of migration process. - /// @dev Unlike `transferFundsFromLegacy` is provides a concrete limit on the gas used for the transfer and even if it will fail, it will not revert the whole transaction. - function safeTransferFundsFromLegacy( - address _token, - address _target, - uint256 _targetChainId, - uint256 _gasPerToken - ) external onlyOwner { - try this.transferFundsFromLegacy{gas: _gasPerToken}(_token, _target, _targetChainId) {} catch { - // A reasonable amount of gas will be provided to transfer the token. - // If the transfer fails, we don't want to revert the whole transaction. - } - } - - /// @dev Accepts ether only from the hyperchain associated with the specified chain ID. - /// @param _chainId The chain ID corresponding to the hyperchain allowed to send ether. - function receiveEth(uint256 _chainId) external payable { - if (BRIDGE_HUB.getHyperchain(_chainId) != msg.sender) { - revert Unauthorized(msg.sender); - } - } - - /// @dev Initializes the l2Bridge address by governance for a specific chain. - function initializeChainGovernance(uint256 _chainId, address _l2BridgeAddress) external onlyOwner { - l2BridgeAddress[_chainId] = _l2BridgeAddress; - } - - /// @notice Allows bridgehub to acquire mintValue for L1->L2 transactions. - /// @dev If the corresponding L2 transaction fails, refunds are issued to a refund recipient on L2. - /// @param _chainId The chain ID of the hyperchain to which deposit. - /// @param _prevMsgSender The `msg.sender` address from the external call that initiated current one. - /// @param _l1Token The L1 token address which is deposited. - /// @param _amount The total amount of tokens to be bridged. - function bridgehubDepositBaseToken( - uint256 _chainId, - address _prevMsgSender, - address _l1Token, - uint256 _amount - ) external payable virtual onlyBridgehubOrEra(_chainId) whenNotPaused { - if (_l1Token == ETH_TOKEN_ADDRESS) { - if (msg.value != _amount) { - revert ValueMismatch(_amount, msg.value); - } - } else { - // The Bridgehub also checks this, but we want to be sure - if (msg.value != 0) { - revert NonEmptyMsgValue(); - } - - uint256 amount = _depositFunds(_prevMsgSender, IERC20(_l1Token), _amount); // note if _prevMsgSender is this contract, this will return 0. This does not happen. - // The token has non-standard transfer logic - if (amount != _amount) { - revert TokensWithFeesNotSupported(); - } - } - - if (!hyperbridgingEnabled[_chainId]) { - chainBalance[_chainId][_l1Token] += _amount; - } - // Note that we don't save the deposited amount, as this is for the base token, which gets sent to the refundRecipient if the tx fails - emit BridgehubDepositBaseTokenInitiated(_chainId, _prevMsgSender, _l1Token, _amount); - } - - /// @dev Transfers tokens from the depositor address to the smart contract address. - /// @return The difference between the contract balance before and after the transferring of funds. - function _depositFunds(address _from, IERC20 _token, uint256 _amount) internal returns (uint256) { - uint256 balanceBefore = _token.balanceOf(address(this)); - // slither-disable-next-line arbitrary-send-erc20 - _token.safeTransferFrom(_from, address(this), _amount); - uint256 balanceAfter = _token.balanceOf(address(this)); - - return balanceAfter - balanceBefore; - } - - /// @notice Initiates a deposit transaction within Bridgehub, used by `requestL2TransactionTwoBridges`. - /// @param _chainId The chain ID of the hyperchain to which deposit. - /// @param _prevMsgSender The `msg.sender` address from the external call that initiated current one. - /// @param _l2Value The L2 `msg.value` from the L1 -> L2 deposit transaction. - /// @param _data The calldata for the second bridge deposit. - function bridgehubDeposit( - uint256 _chainId, - address _prevMsgSender, - // solhint-disable-next-line no-unused-vars - uint256 _l2Value, - bytes calldata _data - ) - external - payable - override - onlyBridgehub - whenNotPaused - returns (L2TransactionRequestTwoBridgesInner memory request) - { - if (l2BridgeAddress[_chainId] == address(0)) { - revert L2BridgeNotSet(_chainId); - } - - (address _l1Token, uint256 _depositAmount, address _l2Receiver) = abi.decode( - _data, - (address, uint256, address) - ); - if (_l1Token == L1_WETH_TOKEN) { - revert TokenNotSupported(L1_WETH_TOKEN); - } - if (BRIDGE_HUB.baseToken(_chainId) == _l1Token) { - revert TokenNotSupported(_l1Token); - } - - uint256 amount; - if (_l1Token == ETH_TOKEN_ADDRESS) { - amount = msg.value; - if (_depositAmount != 0) { - revert DepositIncorrectAmount(0, _depositAmount); - } - } else { - if (msg.value != 0) { - revert NonEmptyMsgValue(); - } - amount = _depositAmount; - - uint256 depAmount = _depositFunds(_prevMsgSender, IERC20(_l1Token), _depositAmount); - // The token has non-standard transfer logic - if (depAmount != _depositAmount) { - revert DepositIncorrectAmount(depAmount, _depositAmount); - } - } - // empty deposit amount - if (amount == 0) { - revert EmptyDeposit(); - } - - bytes32 txDataHash = keccak256(abi.encode(_prevMsgSender, _l1Token, amount)); - if (!hyperbridgingEnabled[_chainId]) { - chainBalance[_chainId][_l1Token] += amount; - } - - { - // Request the finalization of the deposit on the L2 side - bytes memory l2TxCalldata = _getDepositL2Calldata(_prevMsgSender, _l2Receiver, _l1Token, amount); - - request = L2TransactionRequestTwoBridgesInner({ - magicValue: TWO_BRIDGES_MAGIC_VALUE, - l2Contract: l2BridgeAddress[_chainId], - l2Calldata: l2TxCalldata, - factoryDeps: new bytes[](0), - txDataHash: txDataHash - }); - } - emit BridgehubDepositInitiated({ - chainId: _chainId, - txDataHash: txDataHash, - from: _prevMsgSender, - to: _l2Receiver, - l1Token: _l1Token, - amount: amount - }); - } - - /// @notice Confirms the acceptance of a transaction by the Mailbox, as part of the L2 transaction process within Bridgehub. - /// This function is utilized by `requestL2TransactionTwoBridges` to validate the execution of a transaction. - /// @param _chainId The chain ID of the hyperchain to which confirm the deposit. - /// @param _txDataHash The keccak256 hash of abi.encode(msgSender, l1Token, amount) - /// @param _txHash The hash of the L1->L2 transaction to confirm the deposit. - function bridgehubConfirmL2Transaction( - uint256 _chainId, - bytes32 _txDataHash, - bytes32 _txHash - ) external override onlyBridgehub whenNotPaused { - if (depositHappened[_chainId][_txHash] != 0x00) { - revert DepositExists(); - } - depositHappened[_chainId][_txHash] = _txDataHash; - emit BridgehubDepositFinalized(_chainId, _txDataHash, _txHash); - } - - /// @dev Sets the L1ERC20Bridge contract address. Should be called only once. - function setL1Erc20Bridge(address _legacyBridge) external onlyOwner { - if (address(legacyBridge) != address(0)) { - revert AddressAlreadyUsed(address(legacyBridge)); - } - if (_legacyBridge == address(0)) { - revert ZeroAddress(); - } - legacyBridge = IL1ERC20Bridge(_legacyBridge); - } - - /// @dev Generate a calldata for calling the deposit finalization on the L2 bridge contract - function _getDepositL2Calldata( - address _l1Sender, - address _l2Receiver, - address _l1Token, - uint256 _amount - ) internal view returns (bytes memory) { - bytes memory gettersData = _getERC20Getters(_l1Token); - return abi.encodeCall(IL2Bridge.finalizeDeposit, (_l1Sender, _l2Receiver, _l1Token, _amount, gettersData)); - } - - /// @dev Receives and parses (name, symbol, decimals) from the token contract - function _getERC20Getters(address _token) internal view returns (bytes memory) { - if (_token == ETH_TOKEN_ADDRESS) { - bytes memory name = bytes("Ether"); - bytes memory symbol = bytes("ETH"); - bytes memory decimals = abi.encode(uint8(18)); - return abi.encode(name, symbol, decimals); // when depositing eth to a non-eth based chain it is an ERC20 - } - - (, bytes memory data1) = _token.staticcall(abi.encodeCall(IERC20Metadata.name, ())); - (, bytes memory data2) = _token.staticcall(abi.encodeCall(IERC20Metadata.symbol, ())); - (, bytes memory data3) = _token.staticcall(abi.encodeCall(IERC20Metadata.decimals, ())); - return abi.encode(data1, data2, data3); - } - - /// @dev Withdraw funds from the initiated deposit, that failed when finalizing on L2 - /// @param _depositSender The address of the deposit initiator - /// @param _l1Token The address of the deposited L1 ERC20 token - /// @param _amount The amount of the deposit that failed. - /// @param _l2TxHash The L2 transaction hash of the failed deposit finalization - /// @param _l2BatchNumber The L2 batch number where the deposit finalization was processed - /// @param _l2MessageIndex The position in the L2 logs Merkle tree of the l2Log that was sent with the message - /// @param _l2TxNumberInBatch The L2 transaction number in a batch, in which the log was sent - /// @param _merkleProof The Merkle proof of the processing L1 -> L2 transaction with deposit finalization - function claimFailedDeposit( - uint256 _chainId, - address _depositSender, - address _l1Token, - uint256 _amount, - bytes32 _l2TxHash, - uint256 _l2BatchNumber, - uint256 _l2MessageIndex, - uint16 _l2TxNumberInBatch, - bytes32[] calldata _merkleProof - ) external override { - _claimFailedDeposit({ - _checkedInLegacyBridge: false, - _chainId: _chainId, - _depositSender: _depositSender, - _l1Token: _l1Token, - _amount: _amount, - _l2TxHash: _l2TxHash, - _l2BatchNumber: _l2BatchNumber, - _l2MessageIndex: _l2MessageIndex, - _l2TxNumberInBatch: _l2TxNumberInBatch, - _merkleProof: _merkleProof - }); - } - - /// @dev Processes claims of failed deposit, whether they originated from the legacy bridge or the current system. - function _claimFailedDeposit( - bool _checkedInLegacyBridge, - uint256 _chainId, - address _depositSender, - address _l1Token, - uint256 _amount, - bytes32 _l2TxHash, - uint256 _l2BatchNumber, - uint256 _l2MessageIndex, - uint16 _l2TxNumberInBatch, - bytes32[] calldata _merkleProof - ) internal nonReentrant whenNotPaused { - { - bool proofValid = BRIDGE_HUB.proveL1ToL2TransactionStatus({ - _chainId: _chainId, - _l2TxHash: _l2TxHash, - _l2BatchNumber: _l2BatchNumber, - _l2MessageIndex: _l2MessageIndex, - _l2TxNumberInBatch: _l2TxNumberInBatch, - _merkleProof: _merkleProof, - _status: TxStatus.Failure - }); - if (!proofValid) { - revert InvalidProof(); - } - } - if (_amount == 0) { - revert NoFundsTransferred(); - } - - { - bool notCheckedInLegacyBridgeOrWeCanCheckDeposit; - { - // Deposits that happened before the upgrade cannot be checked here, they have to be claimed and checked in the legacyBridge - bool weCanCheckDepositHere = !_isEraLegacyDeposit(_chainId, _l2BatchNumber, _l2TxNumberInBatch); - // Double claims are not possible, as depositHappened is checked here for all except legacy deposits (which have to happen through the legacy bridge) - // Funds claimed before the update will still be recorded in the legacy bridge - // Note we double check NEW deposits if they are called from the legacy bridge - notCheckedInLegacyBridgeOrWeCanCheckDeposit = (!_checkedInLegacyBridge) || weCanCheckDepositHere; - } - if (notCheckedInLegacyBridgeOrWeCanCheckDeposit) { - bytes32 dataHash = depositHappened[_chainId][_l2TxHash]; - bytes32 txDataHash = keccak256(abi.encode(_depositSender, _l1Token, _amount)); - if (dataHash != txDataHash) { - revert DepositDoesNotExist(); - } - delete depositHappened[_chainId][_l2TxHash]; - } - } - - if (!hyperbridgingEnabled[_chainId]) { - // check that the chain has sufficient balance - if (chainBalance[_chainId][_l1Token] < _amount) { - revert InsufficientChainBalance(); - } - chainBalance[_chainId][_l1Token] -= _amount; - } - - // Withdraw funds - if (_l1Token == ETH_TOKEN_ADDRESS) { - bool callSuccess; - // Low-level assembly call, to avoid any memory copying (save gas) - assembly { - callSuccess := call(gas(), _depositSender, _amount, 0, 0, 0, 0) - } - if (!callSuccess) { - revert WithdrawFailed(); - } - } else { - IERC20(_l1Token).safeTransfer(_depositSender, _amount); - // Note we don't allow weth deposits anymore, but there might be legacy weth deposits. - // until we add Weth bridging capabilities, we don't wrap/unwrap weth to ether. - } - - emit ClaimedFailedDepositSharedBridge(_chainId, _depositSender, _l1Token, _amount); - } - - /// @dev Determines if an eth withdrawal was initiated on ZKsync Era before the upgrade to the Shared Bridge. - /// @param _chainId The chain ID of the transaction to check. - /// @param _l2BatchNumber The L2 batch number for the withdrawal. - /// @return Whether withdrawal was initiated on ZKsync Era before diamond proxy upgrade. - function _isEraLegacyEthWithdrawal(uint256 _chainId, uint256 _l2BatchNumber) internal view returns (bool) { - if ((_chainId == ERA_CHAIN_ID) && eraPostDiamondUpgradeFirstBatch == 0) { - revert SharedBridgeValueNotSet(SharedBridgeKey.PostUpgradeFirstBatch); - } - return (_chainId == ERA_CHAIN_ID) && (_l2BatchNumber < eraPostDiamondUpgradeFirstBatch); - } - - /// @dev Determines if a token withdrawal was initiated on ZKsync Era before the upgrade to the Shared Bridge. - /// @param _chainId The chain ID of the transaction to check. - /// @param _l2BatchNumber The L2 batch number for the withdrawal. - /// @return Whether withdrawal was initiated on ZKsync Era before Legacy Bridge upgrade. - function _isEraLegacyTokenWithdrawal(uint256 _chainId, uint256 _l2BatchNumber) internal view returns (bool) { - if ((_chainId == ERA_CHAIN_ID) && eraPostLegacyBridgeUpgradeFirstBatch == 0) { - revert SharedBridgeValueNotSet(SharedBridgeKey.LegacyBridgeFirstBatch); - } - return (_chainId == ERA_CHAIN_ID) && (_l2BatchNumber < eraPostLegacyBridgeUpgradeFirstBatch); - } - - /// @dev Determines if a deposit was initiated on ZKsync Era before the upgrade to the Shared Bridge. - /// @param _chainId The chain ID of the transaction to check. - /// @param _l2BatchNumber The L2 batch number for the deposit where it was processed. - /// @param _l2TxNumberInBatch The L2 transaction number in the batch, in which the deposit was processed. - /// @return Whether deposit was initiated on ZKsync Era before Shared Bridge upgrade. - function _isEraLegacyDeposit( - uint256 _chainId, - uint256 _l2BatchNumber, - uint256 _l2TxNumberInBatch - ) internal view returns (bool) { - if ((_chainId == ERA_CHAIN_ID) && (eraLegacyBridgeLastDepositBatch == 0)) { - revert SharedBridgeValueNotSet(SharedBridgeKey.LegacyBridgeLastDepositBatch); - } - return - (_chainId == ERA_CHAIN_ID) && - (_l2BatchNumber < eraLegacyBridgeLastDepositBatch || - (_l2TxNumberInBatch < eraLegacyBridgeLastDepositTxNumber && - _l2BatchNumber == eraLegacyBridgeLastDepositBatch)); - } - - /// @notice Finalize the withdrawal and release funds - /// @param _chainId The chain ID of the transaction to check - /// @param _l2BatchNumber The L2 batch number where the withdrawal was processed - /// @param _l2MessageIndex The position in the L2 logs Merkle tree of the l2Log that was sent with the message - /// @param _l2TxNumberInBatch The L2 transaction number in the batch, in which the log was sent - /// @param _message The L2 withdraw data, stored in an L2 -> L1 message - /// @param _merkleProof The Merkle proof of the inclusion L2 -> L1 message about withdrawal initialization - function finalizeWithdrawal( - uint256 _chainId, - uint256 _l2BatchNumber, - uint256 _l2MessageIndex, - uint16 _l2TxNumberInBatch, - bytes calldata _message, - bytes32[] calldata _merkleProof - ) external override { - // To avoid rewithdrawing txs that have already happened on the legacy bridge. - // Note: new withdraws are all recorded here, so double withdrawing them is not possible. - if (_isEraLegacyTokenWithdrawal(_chainId, _l2BatchNumber)) { - if (legacyBridge.isWithdrawalFinalized(_l2BatchNumber, _l2MessageIndex)) { - revert WithdrawalAlreadyFinalized(); - } - } - _finalizeWithdrawal({ - _chainId: _chainId, - _l2BatchNumber: _l2BatchNumber, - _l2MessageIndex: _l2MessageIndex, - _l2TxNumberInBatch: _l2TxNumberInBatch, - _message: _message, - _merkleProof: _merkleProof - }); - } - - struct MessageParams { - uint256 l2BatchNumber; - uint256 l2MessageIndex; - uint16 l2TxNumberInBatch; - } - - /// @dev Internal function that handles the logic for finalizing withdrawals, - /// serving both the current bridge system and the legacy ERC20 bridge. - function _finalizeWithdrawal( - uint256 _chainId, - uint256 _l2BatchNumber, - uint256 _l2MessageIndex, - uint16 _l2TxNumberInBatch, - bytes calldata _message, - bytes32[] calldata _merkleProof - ) internal nonReentrant whenNotPaused returns (address l1Receiver, address l1Token, uint256 amount) { - if (isWithdrawalFinalized[_chainId][_l2BatchNumber][_l2MessageIndex]) { - revert WithdrawalAlreadyFinalized(); - } - isWithdrawalFinalized[_chainId][_l2BatchNumber][_l2MessageIndex] = true; - - // Handling special case for withdrawal from ZKsync Era initiated before Shared Bridge. - if (_isEraLegacyEthWithdrawal(_chainId, _l2BatchNumber)) { - // Checks that the withdrawal wasn't finalized already. - bool alreadyFinalized = IGetters(ERA_DIAMOND_PROXY).isEthWithdrawalFinalized( - _l2BatchNumber, - _l2MessageIndex - ); - if (alreadyFinalized) { - revert WithdrawalAlreadyFinalized(); - } - } - - MessageParams memory messageParams = MessageParams({ - l2BatchNumber: _l2BatchNumber, - l2MessageIndex: _l2MessageIndex, - l2TxNumberInBatch: _l2TxNumberInBatch - }); - (l1Receiver, l1Token, amount) = _checkWithdrawal(_chainId, messageParams, _message, _merkleProof); - - if (!hyperbridgingEnabled[_chainId]) { - // Check that the chain has sufficient balance - if (chainBalance[_chainId][l1Token] < amount) { - // not enough funds - revert InsufficientChainBalance(); - } - chainBalance[_chainId][l1Token] -= amount; - } - - if (l1Token == ETH_TOKEN_ADDRESS) { - bool callSuccess; - // Low-level assembly call, to avoid any memory copying (save gas) - assembly { - callSuccess := call(gas(), l1Receiver, amount, 0, 0, 0, 0) - } - if (!callSuccess) { - revert WithdrawFailed(); - } - } else { - // Withdraw funds - IERC20(l1Token).safeTransfer(l1Receiver, amount); - } - emit WithdrawalFinalizedSharedBridge(_chainId, l1Receiver, l1Token, amount); - } - - /// @dev Verifies the validity of a withdrawal message from L2 and returns details of the withdrawal. - function _checkWithdrawal( - uint256 _chainId, - MessageParams memory _messageParams, - bytes calldata _message, - bytes32[] calldata _merkleProof - ) internal view returns (address l1Receiver, address l1Token, uint256 amount) { - (l1Receiver, l1Token, amount) = _parseL2WithdrawalMessage(_chainId, _message); - L2Message memory l2ToL1Message; - { - bool baseTokenWithdrawal = (l1Token == BRIDGE_HUB.baseToken(_chainId)); - address l2Sender = baseTokenWithdrawal ? L2_BASE_TOKEN_SYSTEM_CONTRACT_ADDR : l2BridgeAddress[_chainId]; - - l2ToL1Message = L2Message({ - txNumberInBatch: _messageParams.l2TxNumberInBatch, - sender: l2Sender, - data: _message - }); - } - - bool success = BRIDGE_HUB.proveL2MessageInclusion({ - _chainId: _chainId, - _batchNumber: _messageParams.l2BatchNumber, - _index: _messageParams.l2MessageIndex, - _message: l2ToL1Message, - _proof: _merkleProof - }); - // withdrawal wrong proof - if (!success) { - revert InvalidProof(); - } - } - - function _parseL2WithdrawalMessage( - uint256 _chainId, - bytes memory _l2ToL1message - ) internal view returns (address l1Receiver, address l1Token, uint256 amount) { - // We check that the message is long enough to read the data. - // Please note that there are two versions of the message: - // 1. The message that is sent by `withdraw(address _l1Receiver)` - // It should be equal to the length of the bytes4 function signature + address l1Receiver + uint256 amount = 4 + 20 + 32 = 56 (bytes). - // 2. The message that is sent by `withdrawWithMessage(address _l1Receiver, bytes calldata _additionalData)` - // It should be equal to the length of the following: - // bytes4 function signature + address l1Receiver + uint256 amount + address l2Sender + bytes _additionalData = - // = 4 + 20 + 32 + 32 + _additionalData.length >= 68 (bytes). - - // So the data is expected to be at least 56 bytes long. - // wrong message length - if (_l2ToL1message.length < 56) { - revert L2WithdrawalMessageWrongLength(_l2ToL1message.length); - } - - (uint32 functionSignature, uint256 offset) = UnsafeBytes.readUint32(_l2ToL1message, 0); - if (bytes4(functionSignature) == IMailbox.finalizeEthWithdrawal.selector) { - // this message is a base token withdrawal - (l1Receiver, offset) = UnsafeBytes.readAddress(_l2ToL1message, offset); - (amount, offset) = UnsafeBytes.readUint256(_l2ToL1message, offset); - l1Token = BRIDGE_HUB.baseToken(_chainId); - } else if (bytes4(functionSignature) == IL1ERC20Bridge.finalizeWithdrawal.selector) { - // We use the IL1ERC20Bridge for backward compatibility with old withdrawals. - - // this message is a token withdrawal - - // Check that the message length is correct. - // It should be equal to the length of the function signature + address + address + uint256 = 4 + 20 + 20 + 32 = - // 76 (bytes). - if (_l2ToL1message.length != 76) { - revert L2WithdrawalMessageWrongLength(_l2ToL1message.length); - } - (l1Receiver, offset) = UnsafeBytes.readAddress(_l2ToL1message, offset); - (l1Token, offset) = UnsafeBytes.readAddress(_l2ToL1message, offset); - (amount, offset) = UnsafeBytes.readUint256(_l2ToL1message, offset); - } else { - revert InvalidSelector(bytes4(functionSignature)); - } - } - - /*////////////////////////////////////////////////////////////// - ERA LEGACY FUNCTIONS - //////////////////////////////////////////////////////////////*/ - - /// @notice Initiates a deposit by locking funds on the contract and sending the request - /// of processing an L2 transaction where tokens would be minted. - /// @dev If the token is bridged for the first time, the L2 token contract will be deployed. Note however, that the - /// newly-deployed token does not support any custom logic, i.e. rebase tokens' functionality is not supported. - /// @param _prevMsgSender The `msg.sender` address from the external call that initiated current one. - /// @param _l2Receiver The account address that should receive funds on L2 - /// @param _l1Token The L1 token address which is deposited - /// @param _amount The total amount of tokens to be bridged - /// @param _l2TxGasLimit The L2 gas limit to be used in the corresponding L2 transaction - /// @param _l2TxGasPerPubdataByte The gasPerPubdataByteLimit to be used in the corresponding L2 transaction - /// @param _refundRecipient The address on L2 that will receive the refund for the transaction. - /// @dev If the L2 deposit finalization transaction fails, the `_refundRecipient` will receive the `_l2Value`. - /// Please note, the contract may change the refund recipient's address to eliminate sending funds to addresses - /// out of control. - /// - If `_refundRecipient` is a contract on L1, the refund will be sent to the aliased `_refundRecipient`. - /// - If `_refundRecipient` is set to `address(0)` and the sender has NO deployed bytecode on L1, the refund will - /// be sent to the `msg.sender` address. - /// - If `_refundRecipient` is set to `address(0)` and the sender has deployed bytecode on L1, the refund will be - /// sent to the aliased `msg.sender` address. - /// @dev The address aliasing of L1 contracts as refund recipient on L2 is necessary to guarantee that the funds - /// are controllable through the Mailbox, since the Mailbox applies address aliasing to the from address for the - /// L2 tx if the L1 msg.sender is a contract. Without address aliasing for L1 contracts as refund recipients they - /// would not be able to make proper L2 tx requests through the Mailbox to use or withdraw the funds from L2, and - /// the funds would be lost. - /// @return l2TxHash The L2 transaction hash of deposit finalization. - function depositLegacyErc20Bridge( - address _prevMsgSender, - address _l2Receiver, - address _l1Token, - uint256 _amount, - uint256 _l2TxGasLimit, - uint256 _l2TxGasPerPubdataByte, - address _refundRecipient - ) external payable override onlyLegacyBridge nonReentrant whenNotPaused returns (bytes32 l2TxHash) { - if (l2BridgeAddress[ERA_CHAIN_ID] == address(0)) { - revert L2BridgeNotSet(ERA_CHAIN_ID); - } - if (_l1Token == L1_WETH_TOKEN) { - revert TokenNotSupported(L1_WETH_TOKEN); - } - - // Note that funds have been transferred to this contract in the legacy ERC20 bridge. - if (!hyperbridgingEnabled[ERA_CHAIN_ID]) { - chainBalance[ERA_CHAIN_ID][_l1Token] += _amount; - } - - bytes memory l2TxCalldata = _getDepositL2Calldata(_prevMsgSender, _l2Receiver, _l1Token, _amount); - - { - // If the refund recipient is not specified, the refund will be sent to the sender of the transaction. - // Otherwise, the refund will be sent to the specified address. - // If the recipient is a contract on L1, the address alias will be applied. - address refundRecipient = AddressAliasHelper.actualRefundRecipient(_refundRecipient, _prevMsgSender); - - L2TransactionRequestDirect memory request = L2TransactionRequestDirect({ - chainId: ERA_CHAIN_ID, - l2Contract: l2BridgeAddress[ERA_CHAIN_ID], - mintValue: msg.value, // l2 gas + l2 msg.Value the bridgehub will withdraw the mintValue from the base token bridge for gas - l2Value: 0, // L2 msg.value, this contract doesn't support base token deposits or wrapping functionality, for direct deposits use bridgehub - l2Calldata: l2TxCalldata, - l2GasLimit: _l2TxGasLimit, - l2GasPerPubdataByteLimit: _l2TxGasPerPubdataByte, - factoryDeps: new bytes[](0), - refundRecipient: refundRecipient - }); - l2TxHash = BRIDGE_HUB.requestL2TransactionDirect{value: msg.value}(request); - } - - bytes32 txDataHash = keccak256(abi.encode(_prevMsgSender, _l1Token, _amount)); - // Save the deposited amount to claim funds on L1 if the deposit failed on L2 - depositHappened[ERA_CHAIN_ID][l2TxHash] = txDataHash; - - emit LegacyDepositInitiated({ - chainId: ERA_CHAIN_ID, - l2DepositTxHash: l2TxHash, - from: _prevMsgSender, - to: _l2Receiver, - l1Token: _l1Token, - amount: _amount - }); - } - - /// @notice Finalizes the withdrawal for transactions initiated via the legacy ERC20 bridge. - /// @param _l2BatchNumber The L2 batch number where the withdrawal was processed - /// @param _l2MessageIndex The position in the L2 logs Merkle tree of the l2Log that was sent with the message - /// @param _l2TxNumberInBatch The L2 transaction number in the batch, in which the log was sent - /// @param _message The L2 withdraw data, stored in an L2 -> L1 message - /// @param _merkleProof The Merkle proof of the inclusion L2 -> L1 message about withdrawal initialization - /// - /// @return l1Receiver The address on L1 that will receive the withdrawn funds - /// @return l1Token The address of the L1 token being withdrawn - /// @return amount The amount of the token being withdrawn - function finalizeWithdrawalLegacyErc20Bridge( - uint256 _l2BatchNumber, - uint256 _l2MessageIndex, - uint16 _l2TxNumberInBatch, - bytes calldata _message, - bytes32[] calldata _merkleProof - ) external override onlyLegacyBridge returns (address l1Receiver, address l1Token, uint256 amount) { - (l1Receiver, l1Token, amount) = _finalizeWithdrawal({ - _chainId: ERA_CHAIN_ID, - _l2BatchNumber: _l2BatchNumber, - _l2MessageIndex: _l2MessageIndex, - _l2TxNumberInBatch: _l2TxNumberInBatch, - _message: _message, - _merkleProof: _merkleProof - }); - } - - /// @notice Withdraw funds from the initiated deposit, that failed when finalizing on ZKsync Era chain. - /// This function is specifically designed for maintaining backward-compatibility with legacy `claimFailedDeposit` - /// method in `L1ERC20Bridge`. - /// - /// @param _depositSender The address of the deposit initiator - /// @param _l1Token The address of the deposited L1 ERC20 token - /// @param _amount The amount of the deposit that failed. - /// @param _l2TxHash The L2 transaction hash of the failed deposit finalization - /// @param _l2BatchNumber The L2 batch number where the deposit finalization was processed - /// @param _l2MessageIndex The position in the L2 logs Merkle tree of the l2Log that was sent with the message - /// @param _l2TxNumberInBatch The L2 transaction number in a batch, in which the log was sent - /// @param _merkleProof The Merkle proof of the processing L1 -> L2 transaction with deposit finalization - function claimFailedDepositLegacyErc20Bridge( - address _depositSender, - address _l1Token, - uint256 _amount, - bytes32 _l2TxHash, - uint256 _l2BatchNumber, - uint256 _l2MessageIndex, - uint16 _l2TxNumberInBatch, - bytes32[] calldata _merkleProof - ) external override onlyLegacyBridge { - _claimFailedDeposit({ - _checkedInLegacyBridge: true, - _chainId: ERA_CHAIN_ID, - _depositSender: _depositSender, - _l1Token: _l1Token, - _amount: _amount, - _l2TxHash: _l2TxHash, - _l2BatchNumber: _l2BatchNumber, - _l2MessageIndex: _l2MessageIndex, - _l2TxNumberInBatch: _l2TxNumberInBatch, - _merkleProof: _merkleProof - }); - } - - /*////////////////////////////////////////////////////////////// - PAUSE - //////////////////////////////////////////////////////////////*/ - - /// @notice Pauses all functions marked with the `whenNotPaused` modifier. - function pause() external onlyOwner { - _pause(); - } - - /// @notice Unpauses the contract, allowing all functions marked with the `whenNotPaused` modifier to be called again. - function unpause() external onlyOwner { - _unpause(); - } -} diff --git a/l1-contracts/contracts/bridge/interfaces/IL1ERC20Bridge.sol b/l1-contracts/contracts/bridge/interfaces/IL1ERC20Bridge.sol index d4c13af8e..b9426f3e1 100644 --- a/l1-contracts/contracts/bridge/interfaces/IL1ERC20Bridge.sol +++ b/l1-contracts/contracts/bridge/interfaces/IL1ERC20Bridge.sol @@ -8,11 +8,7 @@ import {IL1NativeTokenVault} from "./IL1NativeTokenVault.sol"; /// @title L1 Bridge contract legacy interface /// @author Matter Labs /// @custom:security-contact security@matterlabs.dev -<<<<<<< HEAD /// @notice Legacy Bridge interface before ZK chain migration, used for backward compatibility with ZKsync Era -======= -/// @notice Legacy Bridge interface before hyperchain migration, used for backward compatibility with ZKsync Era ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe interface IL1ERC20Bridge { event DepositInitiated( bytes32 indexed l2DepositTxHash, diff --git a/l1-contracts/contracts/bridgehub/Bridgehub.sol b/l1-contracts/contracts/bridgehub/Bridgehub.sol index 12ff6cfc5..10495b489 100644 --- a/l1-contracts/contracts/bridgehub/Bridgehub.sol +++ b/l1-contracts/contracts/bridgehub/Bridgehub.sol @@ -15,13 +15,10 @@ import {IZkSyncHyperchain} from "../state-transition/chain-interfaces/IZkSyncHyp import {ETH_TOKEN_ADDRESS, TWO_BRIDGES_MAGIC_VALUE, BRIDGEHUB_MIN_SECOND_BRIDGE_ADDRESS, VIRTUAL_SENDER_ALIASED_ZERO_ADDRESS} from "../common/Config.sol"; import {BridgehubL2TransactionRequest, L2Message, L2Log, TxStatus} from "../common/Messaging.sol"; import {AddressAliasHelper} from "../vendor/AddressAliasHelper.sol"; -<<<<<<< HEAD import {IMessageRoot} from "./IMessageRoot.sol"; import {ISTMDeploymentTracker} from "./ISTMDeploymentTracker.sol"; import {L2CanonicalTransaction} from "../common/Messaging.sol"; -======= import {Unauthorized, STMAlreadyRegistered, STMNotRegistered, TokenAlreadyRegistered, TokenNotRegistered, ZeroChainId, ChainIdTooBig, SharedBridgeNotSet, BridgeHubAlreadyRegistered, AddressTooLow, MsgValueMismatch, WrongMagicValue, ZeroAddress} from "../common/L1ContractErrors.sol"; ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe /// @author Matter Labs /// @custom:security-contact security@matterlabs.dev @@ -79,7 +76,9 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus mapping(uint256 chainId => bool isWhitelistedSettlementLayer) public whitelistedSettlementLayers; modifier onlyOwnerOrAdmin() { - require(msg.sender == admin || msg.sender == owner(), "BH: not owner or admin"); + if (msg.sender != admin && msg.sender != owner()) { + revert Unauthorized(msg.sender); + } _; } @@ -103,16 +102,9 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus _transferOwnership(_owner); } -<<<<<<< HEAD modifier onlyAliasedZero() { /// There is no sender for the wrapping, we use a virtual address. require(msg.sender == VIRTUAL_SENDER_ALIASED_ZERO_ADDRESS, "BH: not aliased zero"); -======= - modifier onlyOwnerOrAdmin() { - if (msg.sender != admin && msg.sender != owner()) { - revert Unauthorized(msg.sender); - } ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe _; } @@ -173,19 +165,12 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus /// @notice State Transition can be any contract with the appropriate interface/functionality /// @param _stateTransitionManager the state transition manager address to be added function addStateTransitionManager(address _stateTransitionManager) external onlyOwner { -<<<<<<< HEAD - require( - !stateTransitionManagerIsRegistered[_stateTransitionManager], - "BH: state transition already registered" - ); -======= if (_stateTransitionManager == address(0)) { revert ZeroAddress(); } if (stateTransitionManagerIsRegistered[_stateTransitionManager]) { revert STMAlreadyRegistered(); } ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe stateTransitionManagerIsRegistered[_stateTransitionManager] = true; emit StateTransitionManagerAdded(_stateTransitionManager); @@ -195,16 +180,12 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus /// @notice this stops new Chains from using the STF, old chains are not affected /// @param _stateTransitionManager the state transition manager address to be removed function removeStateTransitionManager(address _stateTransitionManager) external onlyOwner { -<<<<<<< HEAD - require(stateTransitionManagerIsRegistered[_stateTransitionManager], "BH: state transition not registered yet"); -======= if (_stateTransitionManager == address(0)) { revert ZeroAddress(); } if (!stateTransitionManagerIsRegistered[_stateTransitionManager]) { revert STMNotRegistered(); } ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe stateTransitionManagerIsRegistered[_stateTransitionManager] = false; emit StateTransitionManagerRemoved(_stateTransitionManager); @@ -213,13 +194,9 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus /// @notice token can be any contract with the appropriate interface/functionality /// @param _token address of base token to be registered function addToken(address _token) external onlyOwner { -<<<<<<< HEAD - require(!tokenIsRegistered[_token], "BH: token already registered"); -======= if (tokenIsRegistered[_token]) { revert TokenAlreadyRegistered(_token); } ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe tokenIsRegistered[_token] = true; emit TokenRegistered(_token); @@ -228,16 +205,11 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus /// @notice To set shared bridge, only Owner. Not done in initialize, as /// the order of deployment is Bridgehub, Shared bridge, and then we call this function setSharedBridge(address _sharedBridge) external onlyOwner { -<<<<<<< HEAD - sharedBridge = IL1AssetRouter(_sharedBridge); - - emit SharedBridgeUpdated(_sharedBridge); -======= if (_sharedBridge == address(0)) { revert ZeroAddress(); } - sharedBridge = IL1SharedBridge(_sharedBridge); ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe + sharedBridge = IL1AssetRouter(_sharedBridge); + emit SharedBridgeUpdated(_sharedBridge); } /// @notice Used to register a chain as a settlement layer. @@ -285,16 +257,6 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus bytes calldata _initData, bytes[] calldata _factoryDeps ) external onlyOwnerOrAdmin nonReentrant whenNotPaused returns (uint256) { -<<<<<<< HEAD - require(_chainId != 0, "BH: chainId cannot be 0"); - require(_chainId <= type(uint48).max, "BH: chainId too large"); - - require(stateTransitionManagerIsRegistered[_stateTransitionManager], "BH: state transition not registered"); - require(tokenIsRegistered[_baseToken], "BH: token not registered"); - require(address(sharedBridge) != address(0), "BH: shared bridge not set"); - - require(stateTransitionManager[_chainId] == address(0), "BH: chainId already registered"); -======= if (_chainId == 0) { revert ZeroChainId(); } @@ -321,7 +283,6 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus if (stateTransitionManager[_chainId] != address(0)) { revert BridgeHubAlreadyRegistered(); } ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe stateTransitionManager[_chainId] = _stateTransitionManager; baseToken[_chainId] = _baseToken; @@ -378,9 +339,13 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus { bytes32 tokenAssetId = baseTokenAssetId[_request.chainId]; if (tokenAssetId == ETH_TOKEN_ASSET_ID) { - require(msg.value == _request.mintValue, "BH: msg.value mismatch 1"); + if (msg.value != _request.mintValue) { + revert MsgValueMismatch(_request.mintValue, msg.value); + } } else { - require(msg.value == 0, "BH: non-eth bridge with msg.value"); + if (msg.value != 0) { + revert MsgValueMismatch(0, msg.value); + } } // slither-disable-next-line arbitrary-send-eth @@ -432,10 +397,14 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus bytes32 tokenAssetId = baseTokenAssetId[_request.chainId]; uint256 baseTokenMsgValue; if (tokenAssetId == ETH_TOKEN_ASSET_ID) { - require(msg.value == _request.mintValue + _request.secondBridgeValue, "BH: msg.value mismatch 2"); + if (msg.value != _request.mintValue + _request.secondBridgeValue) { + revert MsgValueMismatch(_request.mintValue + _request.secondBridgeValue, msg.value); + } baseTokenMsgValue = _request.mintValue; } else { - require(msg.value == _request.secondBridgeValue, "BH: msg.value mismatch 3"); + if (msg.value != _request.secondBridgeValue) { + revert MsgValueMismatch(_request.secondBridgeValue, msg.value); + } baseTokenMsgValue = 0; } // slither-disable-next-line arbitrary-send-eth @@ -458,10 +427,15 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus _request.secondBridgeCalldata ); - require(outputRequest.magicValue == TWO_BRIDGES_MAGIC_VALUE, "BH: magic value mismatch"); + if (outputRequest.magicValue != TWO_BRIDGES_MAGIC_VALUE) { + revert WrongMagicValue(uint256(TWO_BRIDGES_MAGIC_VALUE), uint256(outputRequest.magicValue)); + } address refundRecipient = AddressAliasHelper.actualRefundRecipient(_request.refundRecipient, msg.sender); + if (_request.secondBridgeAddress <= BRIDGEHUB_MIN_SECOND_BRIDGE_ADDRESS) { + revert AddressTooLow(_request.secondBridgeAddress); + } canonicalTxHash = IZkSyncHyperchain(hyperchain).bridgehubRequestL2Transaction( BridgehubL2TransactionRequest({ sender: _request.secondBridgeAddress, @@ -584,30 +558,9 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus return IZkSyncHyperchain(hyperchain).l2TransactionBaseCost(_gasPrice, _l2GasLimit, _l2GasPerPubdataByteLimit); } -<<<<<<< HEAD /*////////////////////////////////////////////////////////////// Chain migration //////////////////////////////////////////////////////////////*/ -======= - /// @notice the mailbox is called directly after the sharedBridge received the deposit - /// this assumes that either ether is the base token or - /// the msg.sender has approved mintValue allowance for the sharedBridge. - /// This means this is not ideal for contract calls, as the contract would have to handle token allowance of the base Token - function requestL2TransactionDirect( - L2TransactionRequestDirect calldata _request - ) external payable override nonReentrant whenNotPaused returns (bytes32 canonicalTxHash) { - { - address token = baseToken[_request.chainId]; - if (token == ETH_TOKEN_ADDRESS) { - if (msg.value != _request.mintValue) { - revert MsgValueMismatch(_request.mintValue, msg.value); - } - } else { - if (msg.value != 0) { - revert MsgValueMismatch(0, msg.value); - } - } ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe /// @notice IL1AssetHandler interface, used to migrate (transfer) a chain to the settlement layer. /// @param _settlementChainId the chainId of the settlement chain, i.e. where the message and the migrating chain is sent. @@ -641,7 +594,6 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus // TODO: double check that get only returns when chain id is there. } -<<<<<<< HEAD /// @dev IL1AssetHandler interface, used to receive a chain on the settlement layer. /// @param _assetId the assetId of the chain's STM /// @param _bridgehubMintData the data for the mint @@ -668,83 +620,6 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus messageRoot.addNewChainIfNeeded(_chainId); IZkSyncHyperchain(hyperchain).forwardedBridgeMint(_chainMintData); return address(0); -======= - /// @notice After depositing funds to the sharedBridge, the secondBridge is called - /// to return the actual L2 message which is sent to the Mailbox. - /// This assumes that either ether is the base token or - /// the msg.sender has approved the sharedBridge with the mintValue, - /// and also the necessary approvals are given for the second bridge. - /// @notice The logic of this bridge is to allow easy depositing for bridges. - /// Each contract that handles the users ERC20 tokens needs approvals from the user, this contract allows - /// the user to approve for each token only its respective bridge - /// @notice This function is great for contract calls to L2, the secondBridge can be any contract. - function requestL2TransactionTwoBridges( - L2TransactionRequestTwoBridgesOuter calldata _request - ) external payable override nonReentrant whenNotPaused returns (bytes32 canonicalTxHash) { - { - address token = baseToken[_request.chainId]; - uint256 baseTokenMsgValue; - if (token == ETH_TOKEN_ADDRESS) { - if (msg.value != _request.mintValue + _request.secondBridgeValue) { - revert MsgValueMismatch(_request.mintValue + _request.secondBridgeValue, msg.value); - } - baseTokenMsgValue = _request.mintValue; - } else { - if (msg.value != _request.secondBridgeValue) { - revert MsgValueMismatch(_request.secondBridgeValue, msg.value); - } - baseTokenMsgValue = 0; - } - // slither-disable-next-line arbitrary-send-eth - sharedBridge.bridgehubDepositBaseToken{value: baseTokenMsgValue}( - _request.chainId, - msg.sender, - token, - _request.mintValue - ); - } - - address hyperchain = getHyperchain(_request.chainId); - - // slither-disable-next-line arbitrary-send-eth - L2TransactionRequestTwoBridgesInner memory outputRequest = IL1SharedBridge(_request.secondBridgeAddress) - .bridgehubDeposit{value: _request.secondBridgeValue}( - _request.chainId, - msg.sender, - _request.l2Value, - _request.secondBridgeCalldata - ); - - if (outputRequest.magicValue != TWO_BRIDGES_MAGIC_VALUE) { - revert WrongMagicValue(uint256(TWO_BRIDGES_MAGIC_VALUE), uint256(outputRequest.magicValue)); - } - - address refundRecipient = AddressAliasHelper.actualRefundRecipient(_request.refundRecipient, msg.sender); - - if (_request.secondBridgeAddress <= BRIDGEHUB_MIN_SECOND_BRIDGE_ADDRESS) { - revert AddressTooLow(_request.secondBridgeAddress); - } - // to avoid calls to precompiles - canonicalTxHash = IZkSyncHyperchain(hyperchain).bridgehubRequestL2Transaction( - BridgehubL2TransactionRequest({ - sender: _request.secondBridgeAddress, - contractL2: outputRequest.l2Contract, - mintValue: _request.mintValue, - l2Value: _request.l2Value, - l2Calldata: outputRequest.l2Calldata, - l2GasLimit: _request.l2GasLimit, - l2GasPerPubdataByteLimit: _request.l2GasPerPubdataByteLimit, - factoryDeps: outputRequest.factoryDeps, - refundRecipient: refundRecipient - }) - ); - - IL1SharedBridge(_request.secondBridgeAddress).bridgehubConfirmL2Transaction( - _request.chainId, - outputRequest.txDataHash, - canonicalTxHash - ); ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe } /// @dev IL1AssetHandler interface, used to undo a failed migration of a chain. diff --git a/l1-contracts/contracts/dev-contracts/test/DummyBridgehub.sol b/l1-contracts/contracts/dev-contracts/test/DummyBridgehub.sol index c2524bbdf..fc092c801 100644 --- a/l1-contracts/contracts/dev-contracts/test/DummyBridgehub.sol +++ b/l1-contracts/contracts/dev-contracts/test/DummyBridgehub.sol @@ -2,12 +2,13 @@ pragma solidity 0.8.24; -<<<<<<< HEAD import {ETH_TOKEN_ADDRESS} from "../../common/Config.sol"; import {L2_NATIVE_TOKEN_VAULT_ADDRESS} from "../../common/L2ContractAddresses.sol"; import {IMessageRoot} from "../../bridgehub/IMessageRoot.sol"; import {IGetters} from "../../state-transition/chain-interfaces/IGetters.sol"; +import {Bridgehub} from "../../bridgehub/Bridgehub.sol"; + contract DummyBridgehub { IMessageRoot public messageRoot; @@ -15,6 +16,8 @@ contract DummyBridgehub { // add this to be excluded from coverage report function test() internal virtual {} + constructor() {} + function baseTokenAssetId(uint256) external view returns (bytes32) { return keccak256( @@ -29,19 +32,9 @@ contract DummyBridgehub { function setMessageRoot(address _messageRoot) public { messageRoot = IMessageRoot(_messageRoot); -======= -import {Bridgehub} from "../../bridgehub/Bridgehub.sol"; - -/// @title DummyBridgehub -/// @notice A test smart contract that allows to set State Transition Manager for a given chain -contract DummyBridgehub is Bridgehub { - // add this to be excluded from coverage report - function test() internal virtual {} - - constructor() Bridgehub() {} - - function setStateTransitionManager(uint256 _chainId, address _stm) external { - stateTransitionManager[_chainId] = _stm; ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe } + + // function setStateTransitionManager(uint256 _chainId, address _stm) external { + // stateTransitionManager[_chainId] = _stm; + // } } diff --git a/l1-contracts/contracts/dev-contracts/test/DummySharedBridge.sol b/l1-contracts/contracts/dev-contracts/test/DummySharedBridge.sol index 6b49bae91..f0151b6df 100644 --- a/l1-contracts/contracts/dev-contracts/test/DummySharedBridge.sol +++ b/l1-contracts/contracts/dev-contracts/test/DummySharedBridge.sol @@ -5,24 +5,18 @@ pragma solidity 0.8.24; import {IERC20} from "@openzeppelin/contracts-v4/token/ERC20/IERC20.sol"; import {L2TransactionRequestTwoBridgesInner} from "../../bridgehub/IBridgehub.sol"; -<<<<<<< HEAD -import {TWO_BRIDGES_MAGIC_VALUE} from "../../common/Config.sol"; -import {IL1NativeTokenVault} from "../../bridge/L1NativeTokenVault.sol"; -import {L2_NATIVE_TOKEN_VAULT_ADDRESS} from "../../common/L2ContractAddresses.sol"; - -contract DummySharedBridge { - IL1NativeTokenVault public nativeTokenVault; - -======= import {TWO_BRIDGES_MAGIC_VALUE, ETH_TOKEN_ADDRESS} from "../../common/Config.sol"; +import {IL1NativeTokenVault} from "../../bridge/L1NativeTokenVault.sol"; import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable-v4/security/PausableUpgradeable.sol"; +import {L2_NATIVE_TOKEN_VAULT_ADDRESS} from "../../common/L2ContractAddresses.sol"; import {SafeERC20} from "@openzeppelin/contracts-v4/token/ERC20/utils/SafeERC20.sol"; import {IL2Bridge} from "../../bridge/interfaces/IL2Bridge.sol"; contract DummySharedBridge is PausableUpgradeable { using SafeERC20 for IERC20; ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe + IL1NativeTokenVault public nativeTokenVault; + event BridgehubDepositBaseTokenInitiated( uint256 indexed chainId, address indexed from, @@ -203,10 +197,12 @@ contract DummySharedBridge is PausableUpgradeable { require(withdrawAmount == _depositAmount, "5T"); // The token has non-standard transfer logic } - bytes memory l2TxCalldata = abi.encodeCall( - IL2Bridge.finalizeDeposit, - (_prevMsgSender, _l2Receiver, _l1Token, amount, new bytes(0)) - ); + // TODO: restore + bytes memory l2TxCalldata = hex""; + // abi.encodeCall( + // IL2Bridge.finalizeDeposit, + // (_prevMsgSender, _l2Receiver, _l1Token, amount, new bytes(0)) + // ); bytes32 txDataHash = keccak256(abi.encode(_prevMsgSender, _l1Token, amount)); request = L2TransactionRequestTwoBridgesInner({ diff --git a/l1-contracts/contracts/governance/ChainAdmin.sol b/l1-contracts/contracts/governance/ChainAdmin.sol index ba30e71f1..4d9ff858f 100644 --- a/l1-contracts/contracts/governance/ChainAdmin.sol +++ b/l1-contracts/contracts/governance/ChainAdmin.sol @@ -2,40 +2,22 @@ pragma solidity 0.8.24; -<<<<<<< HEAD -import {Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol"; -import {IChainAdmin} from "./IChainAdmin.sol"; -======= import {Ownable2Step} from "@openzeppelin/contracts-v4/access/Ownable2Step.sol"; import {IChainAdmin} from "./IChainAdmin.sol"; import {IAdmin} from "../state-transition/chain-interfaces/IAdmin.sol"; import {NoCallsProvided, Unauthorized, ZeroAddress} from "../common/L1ContractErrors.sol"; ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe /// @author Matter Labs /// @custom:security-contact security@matterlabs.dev /// @notice The contract is designed to hold the `admin` role in ZKSync Chain (State Transition) contracts. /// The owner of the contract can perform any external calls and also save the information needed for -<<<<<<< HEAD -/// the blockchain node to accept the protocol upgrade. -contract ChainAdmin is IChainAdmin, Ownable2Step { - constructor(address _initialOwner) { - // solhint-disable-next-line gas-custom-errors, reason-string - require(_initialOwner != address(0), "Initial owner should be non zero address"); - _transferOwnership(_initialOwner); - } - -======= /// the blockchain node to accept the protocol upgrade. Another role - `tokenMultiplierSetter` can be used in the contract /// to change the base token gas price in the Chain contract. contract ChainAdmin is IChainAdmin, Ownable2Step { ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe /// @notice Mapping of protocol versions to their expected upgrade timestamps. /// @dev Needed for the offchain node administration to know when to start building batches with the new protocol version. mapping(uint256 protocolVersion => uint256 upgradeTimestamp) public protocolVersionToUpgradeTimestamp; -<<<<<<< HEAD -======= /// @notice The address which can call `setTokenMultiplier` function to change the base token gas price in the Chain contract. /// @dev The token base price can be changed quite often, so the private key for this role is supposed to be stored in the node /// and used by the automated service in a way similar to the sequencer workflow. @@ -58,7 +40,6 @@ contract ChainAdmin is IChainAdmin, Ownable2Step { tokenMultiplierSetter = _tokenMultiplierSetter; } ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe /// @notice Set the expected upgrade timestamp for a specific protocol version. /// @param _protocolVersion The ZKsync chain protocol version. /// @param _upgradeTimestamp The timestamp at which the chain node should expect the upgrade to happen. @@ -72,14 +53,9 @@ contract ChainAdmin is IChainAdmin, Ownable2Step { /// @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. function multicall(Call[] calldata _calls, bool _requireSuccess) external payable onlyOwner { -<<<<<<< HEAD - // solhint-disable-next-line gas-custom-errors - require(_calls.length > 0, "No calls provided"); -======= if (_calls.length == 0) { revert NoCallsProvided(); } ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe // solhint-disable-next-line gas-length-in-loops for (uint256 i = 0; i < _calls.length; ++i) { // slither-disable-next-line arbitrary-send-eth @@ -94,8 +70,6 @@ contract ChainAdmin is IChainAdmin, Ownable2Step { } } -<<<<<<< HEAD -======= /// @notice Sets the token multiplier in the specified Chain contract. /// @param _chainContract The chain contract address where the token multiplier will be set. /// @param _nominator The numerator part of the token multiplier. @@ -107,7 +81,6 @@ contract ChainAdmin is IChainAdmin, Ownable2Step { _chainContract.setTokenMultiplier(_nominator, _denominator); } ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe /// @dev Contract might receive/hold ETH as part of the maintenance process. receive() external payable {} } diff --git a/l1-contracts/contracts/governance/IChainAdmin.sol b/l1-contracts/contracts/governance/IChainAdmin.sol index cea8d7bcd..d5d8f117c 100644 --- a/l1-contracts/contracts/governance/IChainAdmin.sol +++ b/l1-contracts/contracts/governance/IChainAdmin.sol @@ -2,11 +2,8 @@ pragma solidity 0.8.24; -<<<<<<< HEAD -======= import {IAdmin} from "../state-transition/chain-interfaces/IAdmin.sol"; ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe /// @title ChainAdmin contract interface /// @author Matter Labs /// @custom:security-contact security@matterlabs.dev @@ -25,9 +22,6 @@ interface IChainAdmin { event UpdateUpgradeTimestamp(uint256 indexed _protocolVersion, uint256 _upgradeTimestamp); /// @notice Emitted when the call is executed from the contract. -<<<<<<< HEAD - event CallExecuted(Call _call, bool success, bytes returnData); -======= event CallExecuted(Call _call, bool _success, bytes _returnData); /// @notice Emitted when the new token multiplier address is set. @@ -40,5 +34,4 @@ interface IChainAdmin { function multicall(Call[] calldata _calls, bool _requireSuccess) external payable; function setTokenMultiplier(IAdmin _chainContract, uint128 _nominator, uint128 _denominator) external; ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe } diff --git a/l1-contracts/contracts/state-transition/StateTransitionManager.sol b/l1-contracts/contracts/state-transition/StateTransitionManager.sol index f305031e9..9396458ab 100644 --- a/l1-contracts/contracts/state-transition/StateTransitionManager.sol +++ b/l1-contracts/contracts/state-transition/StateTransitionManager.sol @@ -13,11 +13,6 @@ import {IExecutor} from "./chain-interfaces/IExecutor.sol"; import {IStateTransitionManager, StateTransitionManagerInitializeData, ChainCreationParams} from "./IStateTransitionManager.sol"; import {IZkSyncHyperchain} from "./chain-interfaces/IZkSyncHyperchain.sol"; import {FeeParams} from "./chain-deps/ZkSyncHyperchainStorage.sol"; -<<<<<<< HEAD -import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; -import {ReentrancyGuard} from "../common/ReentrancyGuard.sol"; -import {L2_TO_L1_LOG_SERIALIZE_SIZE, DEFAULT_L2_LOGS_TREE_ROOT_HASH, EMPTY_STRING_KECCAK} from "../common/Config.sol"; -======= import {L2_SYSTEM_CONTEXT_SYSTEM_CONTRACT_ADDR, L2_FORCE_DEPLOYER_ADDR} from "../common/L2ContractAddresses.sol"; import {L2CanonicalTransaction} from "../common/Messaging.sol"; import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable-v4/access/Ownable2StepUpgradeable.sol"; @@ -26,7 +21,6 @@ import {ReentrancyGuard} from "../common/ReentrancyGuard.sol"; import {REQUIRED_L2_GAS_PRICE_PER_PUBDATA, L2_TO_L1_LOG_SERIALIZE_SIZE, DEFAULT_L2_LOGS_TREE_ROOT_HASH, EMPTY_STRING_KECCAK, SYSTEM_UPGRADE_L2_TX_TYPE, PRIORITY_TX_MAX_GAS_LIMIT} from "../common/Config.sol"; import {VerifierParams} from "./chain-interfaces/IVerifier.sol"; import {Unauthorized, ZeroAddress, HashMismatch, HyperchainLimitReached, GenesisUpgradeZero, GenesisBatchHashZero, GenesisIndexStorageZero, GenesisBatchCommitmentZero} from "../common/L1ContractErrors.sol"; ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe import {SemVer} from "../common/libraries/SemVer.sol"; import {IBridgehub} from "../bridgehub/IBridgehub.sol"; @@ -393,41 +387,16 @@ contract StateTransitionManager is IStateTransitionManager, ReentrancyGuard, Own // check not registered Diamond.DiamondCutData memory diamondCut = abi.decode(_diamondCut, (Diamond.DiamondCutData)); -<<<<<<< HEAD - { - // check input - bytes32 cutHashInput = keccak256(_diamondCut); - require(cutHashInput == initialCutHash, "STM: initial cutHash mismatch"); - } - bytes memory mandatoryInitData; - { - // solhint-disable-next-line func-named-parameters - mandatoryInitData = bytes.concat( - bytes32(_chainId), - bytes32(uint256(uint160(BRIDGE_HUB))), - bytes32(uint256(uint160(address(this)))), - bytes32(protocolVersion), - bytes32(uint256(uint160(_admin))), - bytes32(uint256(uint160(validatorTimelock))), - bytes32(uint256(uint160(_baseToken))), - bytes32(uint256(uint160(_sharedBridge))), - storedBatchZero - ); -======= // check input bytes32 cutHashInput = keccak256(_diamondCut); if (cutHashInput != initialCutHash) { revert HashMismatch(initialCutHash, cutHashInput); ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe } // construct init data bytes memory initData; /// all together 4+9*32=292 bytes for the selector + mandatory data // solhint-disable-next-line func-named-parameters -<<<<<<< HEAD - initData = bytes.concat(IDiamondInit.initialize.selector, mandatoryInitData, diamondCut.initCalldata); -======= initData = bytes.concat( IDiamondInit.initialize.selector, bytes32(_chainId), @@ -441,7 +410,6 @@ contract StateTransitionManager is IStateTransitionManager, ReentrancyGuard, Own storedBatchZero, diamondCut.initCalldata ); ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe diamondCut.initCalldata = initData; // deploy hyperchainContract diff --git a/l1-contracts/contracts/state-transition/ValidatorTimelock.sol b/l1-contracts/contracts/state-transition/ValidatorTimelock.sol index b8d219111..710eaa870 100644 --- a/l1-contracts/contracts/state-transition/ValidatorTimelock.sol +++ b/l1-contracts/contracts/state-transition/ValidatorTimelock.sol @@ -6,11 +6,8 @@ import {Ownable2Step} from "@openzeppelin/contracts-v4/access/Ownable2Step.sol"; import {LibMap} from "./libraries/LibMap.sol"; import {IExecutor} from "./chain-interfaces/IExecutor.sol"; import {IStateTransitionManager} from "./IStateTransitionManager.sol"; -<<<<<<< HEAD import {PriorityOpsBatchInfo} from "./libraries/PriorityTree.sol"; -======= import {Unauthorized, TimeNotReached, ZeroAddress} from "../common/L1ContractErrors.sol"; ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe /// @author Matter Labs /// @custom:security-contact security@matterlabs.dev diff --git a/l1-contracts/contracts/state-transition/chain-deps/DiamondInit.sol b/l1-contracts/contracts/state-transition/chain-deps/DiamondInit.sol index bf944e95c..a1812ca26 100644 --- a/l1-contracts/contracts/state-transition/chain-deps/DiamondInit.sol +++ b/l1-contracts/contracts/state-transition/chain-deps/DiamondInit.sol @@ -6,13 +6,10 @@ import {Diamond} from "../libraries/Diamond.sol"; import {ZkSyncHyperchainBase} from "./facets/ZkSyncHyperchainBase.sol"; import {L2_TO_L1_LOG_SERIALIZE_SIZE, MAX_GAS_PER_TRANSACTION} from "../../common/Config.sol"; import {InitializeData, IDiamondInit} from "../chain-interfaces/IDiamondInit.sol"; -<<<<<<< HEAD import {IBridgehub} from "../../bridgehub/IBridgehub.sol"; import {PriorityQueue} from "../libraries/PriorityQueue.sol"; import {PriorityTree} from "../libraries/PriorityTree.sol"; -======= import {ZeroAddress, TooMuchGas} from "../../common/L1ContractErrors.sol"; ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe /// @author Matter Labs /// @dev The contract is used only once to initialize the diamond proxy. diff --git a/l1-contracts/contracts/state-transition/chain-deps/facets/Admin.sol b/l1-contracts/contracts/state-transition/chain-deps/facets/Admin.sol index cd472126d..20ce1f5c9 100644 --- a/l1-contracts/contracts/state-transition/chain-deps/facets/Admin.sol +++ b/l1-contracts/contracts/state-transition/chain-deps/facets/Admin.sol @@ -10,11 +10,8 @@ import {PriorityTree} from "../../../state-transition/libraries/PriorityTree.sol import {PriorityQueue} from "../../../state-transition/libraries/PriorityQueue.sol"; import {ZkSyncHyperchainBase} from "./ZkSyncHyperchainBase.sol"; import {IStateTransitionManager} from "../../IStateTransitionManager.sol"; -<<<<<<< HEAD import {IL1GenesisUpgrade} from "../../../upgrades/IL1GenesisUpgrade.sol"; -======= import {Unauthorized, TooMuchGas, PriorityTxPubdataExceedsMaxPubDataPerBatch, InvalidPubdataPricingMode, ProtocolIdMismatch, ChainAlreadyLive, HashMismatch, ProtocolIdNotGreater, DenominatorIsZero, DiamondAlreadyFrozen, DiamondNotFrozen} from "../../../common/L1ContractErrors.sol"; ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe // While formally the following import is not used, it is needed to inherit documentation from it import {IZkSyncHyperchainBase} from "../../chain-interfaces/IZkSyncHyperchainBase.sol"; diff --git a/l1-contracts/contracts/state-transition/chain-deps/facets/Executor.sol b/l1-contracts/contracts/state-transition/chain-deps/facets/Executor.sol index 9406387e7..7fb021060 100644 --- a/l1-contracts/contracts/state-transition/chain-deps/facets/Executor.sol +++ b/l1-contracts/contracts/state-transition/chain-deps/facets/Executor.sol @@ -12,12 +12,9 @@ import {UncheckedMath} from "../../../common/libraries/UncheckedMath.sol"; import {UnsafeBytes} from "../../../common/libraries/UnsafeBytes.sol"; import {L2_BOOTLOADER_ADDRESS, L2_TO_L1_MESSENGER_SYSTEM_CONTRACT_ADDR, L2_SYSTEM_CONTEXT_SYSTEM_CONTRACT_ADDR} from "../../../common/L2ContractAddresses.sol"; import {IStateTransitionManager} from "../../IStateTransitionManager.sol"; -<<<<<<< HEAD import {PriorityTree, PriorityOpsBatchInfo} from "../../libraries/PriorityTree.sol"; import {IL1DAValidator, L1DAValidatorOutput} from "../../chain-interfaces/IL1DAValidator.sol"; -======= import {BatchNumberMismatch, TimeNotReached, TooManyBlobs, ValueMismatch, InvalidPubdataMode, InvalidPubdataLength, HashMismatch, NonIncreasingTimestamp, TimestampError, InvalidLogSender, TxHashMismatch, UnexpectedSystemLog, MissingSystemLogs, LogAlreadyProcessed, InvalidProtocolVersion, CanOnlyProcessOneBatch, BatchHashMismatch, UpgradeBatchNumberIsNotZero, NonSequentialBatch, CantExecuteUnprovenBatches, SystemLogsSizeTooBig, InvalidNumberOfBlobs, VerifiedBatchesExceedsCommittedBatches, InvalidProof, RevertedBatchNotAfterNewLastBatch, CantRevertExecutedBatch, PointEvalFailed, EmptyBlobVersionHash, NonEmptyBlobVersionHash, BlobHashCommitmentError, CalldataLengthTooBig, InvalidPubdataHash, L2TimestampTooBig, PriorityOperationsRollingHashMismatch, PubdataCommitmentsEmpty, PointEvalCallFailed, PubdataCommitmentsTooBig, InvalidPubdataCommitmentsSize} from "../../../common/L1ContractErrors.sol"; ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe // While formally the following import is not used, it is needed to inherit documentation from it import {IZkSyncHyperchainBase} from "../../chain-interfaces/IZkSyncHyperchainBase.sol"; @@ -40,9 +37,11 @@ contract ExecutorFacet is ZkSyncHyperchainBase, IExecutor { StoredBatchInfo memory _previousBatch, CommitBatchInfo calldata _newBatch, bytes32 _expectedSystemContractUpgradeTxHash -<<<<<<< HEAD ) internal returns (StoredBatchInfo memory) { - require(_newBatch.batchNumber == _previousBatch.batchNumber + 1, "f"); // only commit next batch + // only commit next batch + if (_newBatch.batchNumber != _previousBatch.batchNumber + 1) { + revert BatchNumberMismatch(_previousBatch.batchNumber + 1, _newBatch.batchNumber); + } // Check that batch contains all meta information for L2 logs. // Get the chained hash of priority transaction hashes. @@ -54,55 +53,6 @@ contract ExecutorFacet is ZkSyncHyperchainBase, IExecutor { _newBatch.operatorDAInput, TOTAL_BLOBS_IN_COMMITMENT ); -======= - ) internal view returns (StoredBatchInfo memory) { - // only commit next batch - if (_newBatch.batchNumber != _previousBatch.batchNumber + 1) { - revert BatchNumberMismatch(_previousBatch.batchNumber + 1, _newBatch.batchNumber); - } - - uint8 pubdataSource = uint8(bytes1(_newBatch.pubdataCommitments[0])); - PubdataPricingMode pricingMode = s.feeParams.pubdataPricingMode; - if ( - pricingMode != PubdataPricingMode.Validium && - pubdataSource != uint8(PubdataSource.Calldata) && - pubdataSource != uint8(PubdataSource.Blob) - ) { - revert InvalidPubdataMode(); - } - - // Check that batch contain all meta information for L2 logs. - // Get the chained hash of priority transaction hashes. - LogProcessingOutput memory logOutput = _processL2Logs(_newBatch, _expectedSystemContractUpgradeTxHash); - - bytes32[] memory blobCommitments = new bytes32[](MAX_NUMBER_OF_BLOBS); - if (pricingMode == PubdataPricingMode.Validium) { - // skipping data validation for validium, we just check that the data is empty - if (_newBatch.pubdataCommitments.length != 1) { - revert CalldataLengthTooBig(); - } - for (uint8 i = uint8(SystemLogKey.BLOB_ONE_HASH_KEY); i <= uint8(SystemLogKey.BLOB_SIX_HASH_KEY); ++i) { - logOutput.blobHashes[i - uint8(SystemLogKey.BLOB_ONE_HASH_KEY)] = bytes32(0); - } - } else if (pubdataSource == uint8(PubdataSource.Blob)) { - // In this scenario, pubdataCommitments is a list of: opening point (16 bytes) || claimed value (32 bytes) || commitment (48 bytes) || proof (48 bytes)) = 144 bytes - blobCommitments = _verifyBlobInformation(_newBatch.pubdataCommitments[1:], logOutput.blobHashes); - } else if (pubdataSource == uint8(PubdataSource.Calldata)) { - // In this scenario pubdataCommitments is actual pubdata consisting of l2 to l1 logs, l2 to l1 message, compressed smart contract bytecode, and compressed state diffs - if (_newBatch.pubdataCommitments.length > BLOB_SIZE_BYTES) { - revert InvalidPubdataLength(); - } - bytes32 pubdataHash = keccak256(_newBatch.pubdataCommitments[1:_newBatch.pubdataCommitments.length - 32]); - if (logOutput.pubdataHash != pubdataHash) { - revert InvalidPubdataHash(pubdataHash, logOutput.pubdataHash); - } - blobCommitments[0] = bytes32( - _newBatch.pubdataCommitments[_newBatch.pubdataCommitments.length - 32:_newBatch - .pubdataCommitments - .length] - ); - } ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe if (_previousBatch.batchHash != logOutput.previousBatchHash) { revert HashMismatch(logOutput.previousBatchHash, _previousBatch.batchHash); @@ -215,19 +165,6 @@ contract ExecutorFacet is ZkSyncHyperchainBase, IExecutor { revert InvalidLogSender(logSender, logKey); } logOutput.l2LogsTreeRoot = logValue; -<<<<<<< HEAD -======= - } else if (logKey == uint256(SystemLogKey.TOTAL_L2_TO_L1_PUBDATA_KEY)) { - if (logSender != L2_TO_L1_MESSENGER_SYSTEM_CONTRACT_ADDR) { - revert InvalidLogSender(logSender, logKey); - } - logOutput.pubdataHash = logValue; - } else if (logKey == uint256(SystemLogKey.STATE_DIFF_HASH_KEY)) { - if (logSender != L2_TO_L1_MESSENGER_SYSTEM_CONTRACT_ADDR) { - revert InvalidLogSender(logSender, logKey); - } - logOutput.stateDiffHash = logValue; ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe } else if (logKey == uint256(SystemLogKey.PACKED_BATCH_AND_L2_BLOCK_TIMESTAMP_KEY)) { if (logSender != L2_SYSTEM_CONTEXT_SYSTEM_CONTRACT_ADDR) { revert InvalidLogSender(logSender, logKey); @@ -248,28 +185,12 @@ contract ExecutorFacet is ZkSyncHyperchainBase, IExecutor { revert InvalidLogSender(logSender, logKey); } logOutput.numberOfLayer1Txs = uint256(logValue); -<<<<<<< HEAD } else if (logKey == uint256(SystemLogKey.USED_L2_DA_VALIDATOR_ADDRESS_KEY)) { require(logSender == L2_TO_L1_MESSENGER_SYSTEM_CONTRACT_ADDR, "vk"); require(s.l2DAValidator == address(uint160(uint256(logValue))), "lo"); } else if (logKey == uint256(SystemLogKey.L2_DA_VALIDATOR_OUTPUT_HASH_KEY)) { require(logSender == L2_TO_L1_MESSENGER_SYSTEM_CONTRACT_ADDR, "lp2"); logOutput.l2DAValidatorOutputHash = logValue; -======= - } else if ( - logKey >= uint256(SystemLogKey.BLOB_ONE_HASH_KEY) && logKey <= uint256(SystemLogKey.BLOB_SIX_HASH_KEY) - ) { - if (logSender != L2_PUBDATA_CHUNK_PUBLISHER_ADDR) { - revert InvalidLogSender(logSender, logKey); - } - uint8 blobNumber = uint8(logKey) - uint8(SystemLogKey.BLOB_ONE_HASH_KEY); - - if (blobNumber >= MAX_NUMBER_OF_BLOBS) { - revert TooManyBlobs(); - } - - logOutput.blobHashes[blobNumber] = logValue; ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe } else if (logKey == uint256(SystemLogKey.EXPECTED_SYSTEM_CONTRACT_UPGRADE_TX_HASH_KEY)) { if (logSender != L2_BOOTLOADER_ADDRESS) { revert InvalidLogSender(logSender, logKey); @@ -288,20 +209,10 @@ contract ExecutorFacet is ZkSyncHyperchainBase, IExecutor { // Without the protocol upgrade we expect 8 logs: 2^8 - 1 = 255 // With the protocol upgrade we expect 9 logs: 2^9 - 1 = 511 if (_expectedSystemContractUpgradeTxHash == bytes32(0)) { -<<<<<<< HEAD // require(processedLogs == 255, "b7"); } else { // FIXME: do restore this code to the one that was before require(_checkBit(processedLogs, uint8(SystemLogKey.EXPECTED_SYSTEM_CONTRACT_UPGRADE_TX_HASH_KEY)), "b8"); -======= - if (processedLogs != 8191) { - revert MissingSystemLogs(8191, processedLogs); - } - } else { - if (processedLogs != 16383) { - revert MissingSystemLogs(16383, processedLogs); - } ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe } } @@ -454,38 +365,26 @@ contract ExecutorFacet is ZkSyncHyperchainBase, IExecutor { bytes32 _priorityOperationsHash ) internal view { uint256 currentBatchNumber = _storedBatch.batchNumber; -<<<<<<< HEAD - require(currentBatchNumber == s.totalBatchesExecuted + _executedBatchIdx + 1, "k"); // Execute batches in order - require( - _hashStoredBatchInfo(_storedBatch) == s.storedBatchHashes[currentBatchNumber], - "exe10" // executing batch should be committed - ); - require(_priorityOperationsHash == _storedBatch.priorityOperationsHash, "x"); // priority operations hash does not match with expected - } -======= if (currentBatchNumber != s.totalBatchesExecuted + _executedBatchIdx + 1) { revert NonSequentialBatch(); } if (_hashStoredBatchInfo(_storedBatch) != s.storedBatchHashes[currentBatchNumber]) { revert BatchHashMismatch(s.storedBatchHashes[currentBatchNumber], _hashStoredBatchInfo(_storedBatch)); } ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe - + if (_priorityOperationsHash != _storedBatch.priorityOperationsHash) { + revert PriorityOperationsRollingHashMismatch(); + } + } + /// @dev Executes one batch /// @dev 1. Processes all pending operations (Complete priority requests) /// @dev 2. Finalizes batch on Ethereum /// @dev _executedBatchIdx is an index in the array of the batches that we want to execute together function _executeOneBatch(StoredBatchInfo memory _storedBatch, uint256 _executedBatchIdx) internal { bytes32 priorityOperationsHash = _collectOperationsFromPriorityQueue(_storedBatch.numberOfLayer1Txs); -<<<<<<< HEAD _checkBatchData(_storedBatch, _executedBatchIdx, priorityOperationsHash); uint256 currentBatchNumber = _storedBatch.batchNumber; -======= - if (priorityOperationsHash != _storedBatch.priorityOperationsHash) { - revert PriorityOperationsRollingHashMismatch(); - } ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe // Save root hash of L2 -> L1 logs tree s.l2LogsRootHashes[currentBatchNumber] = _storedBatch.l2LogsTreeRoot; @@ -764,14 +663,9 @@ contract ExecutorFacet is ZkSyncHyperchainBase, IExecutor { ) internal pure returns (bytes32[] memory blobAuxOutputWords) { // These invariants should be checked by the caller of this function, but we double check // just in case. -<<<<<<< HEAD - require(_blobCommitments.length == TOTAL_BLOBS_IN_COMMITMENT, "b10"); - require(_blobHashes.length == TOTAL_BLOBS_IN_COMMITMENT, "b11"); -======= - if (_blobCommitments.length != MAX_NUMBER_OF_BLOBS || _blobHashes.length != MAX_NUMBER_OF_BLOBS) { - revert InvalidNumberOfBlobs(MAX_NUMBER_OF_BLOBS, _blobCommitments.length, _blobHashes.length); + if (_blobCommitments.length != TOTAL_BLOBS_IN_COMMITMENT || _blobHashes.length != TOTAL_BLOBS_IN_COMMITMENT) { + revert InvalidNumberOfBlobs(TOTAL_BLOBS_IN_COMMITMENT, _blobCommitments.length, _blobHashes.length); } ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe // for each blob we have: // linear hash (hash of preimage from system logs) and @@ -803,105 +697,4 @@ contract ExecutorFacet is ZkSyncHyperchainBase, IExecutor { function _setBit(uint256 _bitMap, uint8 _index) internal pure returns (uint256) { return _bitMap | (1 << _index); } -<<<<<<< HEAD -======= - - /// @notice Calls the point evaluation precompile and verifies the output - /// Verify p(z) = y given commitment that corresponds to the polynomial p(x) and a KZG proof. - /// Also verify that the provided commitment matches the provided versioned_hash. - /// - function _pointEvaluationPrecompile( - bytes32 _versionedHash, - bytes32 _openingPoint, - bytes calldata _openingValueCommitmentProof - ) internal view { - bytes memory precompileInput = abi.encodePacked(_versionedHash, _openingPoint, _openingValueCommitmentProof); - - (bool success, bytes memory data) = POINT_EVALUATION_PRECOMPILE_ADDR.staticcall(precompileInput); - - // We verify that the point evaluation precompile call was successful by testing the latter 32 bytes of the - // response is equal to BLS_MODULUS as defined in https://eips.ethereum.org/EIPS/eip-4844#point-evaluation-precompile - if (!success) { - revert PointEvalCallFailed(precompileInput); - } - (, uint256 result) = abi.decode(data, (uint256, uint256)); - if (result != BLS_MODULUS) { - revert PointEvalFailed(abi.encode(result)); - } - } - - /// @dev Verifies that the blobs contain the correct data by calling the point evaluation precompile. For the precompile we need: - /// versioned hash || opening point || opening value || commitment || proof - /// the _pubdataCommitments will contain the last 4 values, the versioned hash is pulled from the BLOBHASH opcode - /// pubdataCommitments is a list of: opening point (16 bytes) || claimed value (32 bytes) || commitment (48 bytes) || proof (48 bytes)) = 144 bytes - function _verifyBlobInformation( - bytes calldata _pubdataCommitments, - bytes32[] memory _blobHashes - ) internal view returns (bytes32[] memory blobCommitments) { - uint256 versionedHashIndex = 0; - - if (_pubdataCommitments.length == 0) { - revert PubdataCommitmentsEmpty(); - } - if (_pubdataCommitments.length > PUBDATA_COMMITMENT_SIZE * MAX_NUMBER_OF_BLOBS) { - revert PubdataCommitmentsTooBig(); - } - if (_pubdataCommitments.length % PUBDATA_COMMITMENT_SIZE != 0) { - revert InvalidPubdataCommitmentsSize(); - } - blobCommitments = new bytes32[](MAX_NUMBER_OF_BLOBS); - - // We disable this check because calldata array length is cheap. - // solhint-disable-next-line gas-length-in-loops - for (uint256 i = 0; i < _pubdataCommitments.length; i += PUBDATA_COMMITMENT_SIZE) { - bytes32 blobVersionedHash = _getBlobVersionedHash(versionedHashIndex); - - if (blobVersionedHash == bytes32(0)) { - revert EmptyBlobVersionHash(versionedHashIndex); - } - - // First 16 bytes is the opening point. While we get the point as 16 bytes, the point evaluation precompile - // requires it to be 32 bytes. The blob commitment must use the opening point as 16 bytes though. - bytes32 openingPoint = bytes32( - uint256(uint128(bytes16(_pubdataCommitments[i:i + PUBDATA_COMMITMENT_CLAIMED_VALUE_OFFSET]))) - ); - - _pointEvaluationPrecompile( - blobVersionedHash, - openingPoint, - _pubdataCommitments[i + PUBDATA_COMMITMENT_CLAIMED_VALUE_OFFSET:i + PUBDATA_COMMITMENT_SIZE] - ); - - // Take the hash of the versioned hash || opening point || claimed value - blobCommitments[versionedHashIndex] = keccak256( - abi.encodePacked(blobVersionedHash, _pubdataCommitments[i:i + PUBDATA_COMMITMENT_COMMITMENT_OFFSET]) - ); - ++versionedHashIndex; - } - - // This check is required because we want to ensure that there aren't any extra blobs trying to be published. - // Calling the BLOBHASH opcode with an index > # blobs - 1 yields bytes32(0) - bytes32 versionedHash = _getBlobVersionedHash(versionedHashIndex); - if (versionedHash != bytes32(0)) { - revert NonEmptyBlobVersionHash(versionedHashIndex); - } - - // We verify that for each set of blobHash/blobCommitment are either both empty - // or there are values for both. - for (uint256 i = 0; i < MAX_NUMBER_OF_BLOBS; ++i) { - if ( - (_blobHashes[i] == bytes32(0) && blobCommitments[i] != bytes32(0)) || - (_blobHashes[i] != bytes32(0) && blobCommitments[i] == bytes32(0)) - ) { - revert BlobHashCommitmentError(i, _blobHashes[i] == bytes32(0), blobCommitments[i] == bytes32(0)); - } - } - } - - function _getBlobVersionedHash(uint256 _index) internal view virtual returns (bytes32 versionedHash) { - assembly { - versionedHash := blobhash(_index) - } - } ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe } diff --git a/l1-contracts/contracts/state-transition/chain-deps/facets/Mailbox.sol b/l1-contracts/contracts/state-transition/chain-deps/facets/Mailbox.sol index 3aa3bac79..18781078f 100644 --- a/l1-contracts/contracts/state-transition/chain-deps/facets/Mailbox.sol +++ b/l1-contracts/contracts/state-transition/chain-deps/facets/Mailbox.sol @@ -49,20 +49,6 @@ contract MailboxFacet is ZkSyncHyperchainBase, IMailbox { } /// @inheritdoc IMailbox -<<<<<<< HEAD -======= - function transferEthToSharedBridge() external onlyBaseTokenBridge { - if (s.chainId != ERA_CHAIN_ID) { - revert OnlyEraSupported(); - } - - uint256 amount = address(this).balance; - address baseTokenBridgeAddress = s.baseTokenBridge; - IL1SharedBridge(baseTokenBridgeAddress).receiveEth{value: amount}(ERA_CHAIN_ID); - } - - /// @notice when requesting transactions through the bridgehub ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe function bridgehubRequestL2Transaction( BridgehubL2TransactionRequest calldata _request ) external onlyBridgehub returns (bytes32 canonicalTxHash) { @@ -235,13 +221,7 @@ contract MailboxFacet is ZkSyncHyperchainBase, IMailbox { L2Log memory _log, bytes32[] calldata _proof ) internal view returns (bool) { -<<<<<<< HEAD // require(_batchNumber <= s.totalBatchesExecuted, "xx"); -======= - if (_batchNumber > s.totalBatchesExecuted) { - revert BatchNotExecuted(_batchNumber); - } ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe bytes32 hashedLog = keccak256( // solhint-disable-next-line func-named-parameters @@ -315,7 +295,6 @@ contract MailboxFacet is ZkSyncHyperchainBase, IMailbox { } /// @inheritdoc IMailbox -<<<<<<< HEAD function requestL2TransactionToGatewayMailbox( uint256 _chainId, L2CanonicalTransaction calldata _transaction, @@ -335,25 +314,6 @@ contract MailboxFacet is ZkSyncHyperchainBase, IMailbox { _factoryDeps: _factoryDeps, _canonicalTxHash: _canonicalTxHash, _expirationTimestamp: _expirationTimestamp -======= - function finalizeEthWithdrawal( - uint256 _l2BatchNumber, - uint256 _l2MessageIndex, - uint16 _l2TxNumberInBatch, - bytes calldata _message, - bytes32[] calldata _merkleProof - ) external nonReentrant { - if (s.chainId != ERA_CHAIN_ID) { - revert OnlyEraSupported(); - } - IL1SharedBridge(s.baseTokenBridge).finalizeWithdrawal({ - _chainId: ERA_CHAIN_ID, - _l2BatchNumber: _l2BatchNumber, - _l2MessageIndex: _l2MessageIndex, - _l2TxNumberInBatch: _l2TxNumberInBatch, - _message: _message, - _merkleProof: _merkleProof ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe }); canonicalTxHash = _requestL2TransactionToGatewayFree(wrappedRequest); } @@ -362,7 +322,6 @@ contract MailboxFacet is ZkSyncHyperchainBase, IMailbox { function bridgehubRequestL2TransactionOnGateway( L2CanonicalTransaction calldata _transaction, bytes[] calldata _factoryDeps, -<<<<<<< HEAD bytes32 _canonicalTxHash, uint64 _expirationTimestamp ) external override onlyBridgehub { @@ -382,14 +341,6 @@ contract MailboxFacet is ZkSyncHyperchainBase, IMailbox { (_chainId, _transaction, _factoryDeps, _canonicalTxHash, _expirationTimestamp) ); return -======= - address _refundRecipient - ) external payable returns (bytes32 canonicalTxHash) { - if (s.chainId != ERA_CHAIN_ID) { - revert OnlyEraSupported(); - } - canonicalTxHash = _requestL2TransactionSender( ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe BridgehubL2TransactionRequest({ /// There is no sender for the wrapping, we use a virtual address. sender: VIRTUAL_SENDER_ALIASED_ZERO_ADDRESS, @@ -443,15 +394,10 @@ contract MailboxFacet is ZkSyncHyperchainBase, IMailbox { function _requestL2Transaction(WritePriorityOpParams memory _params) internal returns (bytes32 canonicalTxHash) { BridgehubL2TransactionRequest memory request = _params.request; -<<<<<<< HEAD - require(request.factoryDeps.length <= MAX_NEW_FACTORY_DEPS, "uj"); - _params.txId = _nextPriorityTxId(); -======= if (request.factoryDeps.length > MAX_NEW_FACTORY_DEPS) { revert TooManyFactoryDeps(); } - _params.txId = s.priorityQueue.getTotalPriorityTxs(); ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe + _params.txId = _nextPriorityTxId(); // Checking that the user provided enough ether to pay for the transaction. _params.l2GasPrice = _deriveL2GasPrice(tx.gasprice, request.l2GasPerPubdataByteLimit); @@ -463,10 +409,7 @@ contract MailboxFacet is ZkSyncHyperchainBase, IMailbox { request.refundRecipient = AddressAliasHelper.actualRefundRecipient(request.refundRecipient, request.sender); // Change the sender address if it is a smart contract to prevent address collision between L1 and L2. // Please note, currently ZKsync address derivation is different from Ethereum one, but it may be changed in the future. -<<<<<<< HEAD -======= // solhint-disable avoid-tx-origin ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe // slither-disable-next-line tx-origin if (request.sender != tx.origin) { request.sender = AddressAliasHelper.applyL1ToL2Alias(request.sender); @@ -602,7 +545,9 @@ contract MailboxFacet is ZkSyncHyperchainBase, IMailbox { bytes calldata _message, bytes32[] calldata _merkleProof ) external nonReentrant { - require(s.chainId == ERA_CHAIN_ID, "Mailbox: finalizeEthWithdrawal only available for Era on mailbox"); + if (s.chainId != ERA_CHAIN_ID) { + revert OnlyEraSupported(); + } IL1AssetRouter(s.baseTokenBridge).finalizeWithdrawal({ _chainId: ERA_CHAIN_ID, _l2BatchNumber: _l2BatchNumber, @@ -623,7 +568,9 @@ contract MailboxFacet is ZkSyncHyperchainBase, IMailbox { bytes[] calldata _factoryDeps, address _refundRecipient ) external payable returns (bytes32 canonicalTxHash) { - require(s.chainId == ERA_CHAIN_ID, "Mailbox: legacy interface only available for Era"); + if (s.chainId != ERA_CHAIN_ID) { + revert OnlyEraSupported(); + } canonicalTxHash = _requestL2TransactionSender( BridgehubL2TransactionRequest({ sender: msg.sender, diff --git a/l1-contracts/contracts/state-transition/chain-interfaces/IZkSyncHyperchainBase.sol b/l1-contracts/contracts/state-transition/chain-interfaces/IZkSyncHyperchainBase.sol index 23466ce4a..3cd646cc9 100644 --- a/l1-contracts/contracts/state-transition/chain-interfaces/IZkSyncHyperchainBase.sol +++ b/l1-contracts/contracts/state-transition/chain-interfaces/IZkSyncHyperchainBase.sol @@ -1,10 +1,6 @@ // SPDX-License-Identifier: MIT -<<<<<<< HEAD -pragma solidity 0.8.24; -======= // 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; ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe /// @title The interface of the ZKsync contract, responsible for the main ZKsync logic. /// @author Matter Labs diff --git a/l1-contracts/deploy-scripts/AcceptAdmin.s.sol b/l1-contracts/deploy-scripts/AcceptAdmin.s.sol index c4b9827cd..9262a5cd8 100644 --- a/l1-contracts/deploy-scripts/AcceptAdmin.s.sol +++ b/l1-contracts/deploy-scripts/AcceptAdmin.s.sol @@ -1,9 +1,4 @@ -<<<<<<< HEAD -// SPDX-License-Identifier: UNLICENSED -// 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. -======= // SPDX-License-Identifier: MIT ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe pragma solidity ^0.8.21; import {Script} from "forge-std/Script.sol"; @@ -33,17 +28,11 @@ contract AcceptAdmin is Script { config.governor = toml.readAddress("$.governor"); } -<<<<<<< HEAD // This function should be called by the owner to accept the owner role function acceptOwner() public { initConfig(); Ownable2Step adminContract = Ownable2Step(config.admin); -======= - // This function should be called by the owner to accept the admin role - function governanceAcceptOwner(address governor, address target) public { - Ownable2Step adminContract = Ownable2Step(target); ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe Utils.executeUpgrade({ _governor: governor, _salt: bytes32(0), @@ -55,7 +44,6 @@ contract AcceptAdmin is Script { } // This function should be called by the owner to accept the admin role -<<<<<<< HEAD function acceptAdmin(address payable _admin, address _target) public { IZkSyncHyperchain hyperchain = IZkSyncHyperchain(_target); ChainAdmin chainAdmin = ChainAdmin(_admin); @@ -66,18 +54,6 @@ contract AcceptAdmin is Script { vm.startBroadcast(); chainAdmin.multicall(calls, true); vm.stopBroadcast(); -======= - function governanceAcceptAdmin(address governor, address target) public { - IZkSyncHyperchain adminContract = IZkSyncHyperchain(target); - Utils.executeUpgrade({ - _governor: governor, - _salt: bytes32(0), - _target: target, - _data: abi.encodeCall(adminContract.acceptAdmin, ()), - _value: 0, - _delay: 0 - }); ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe } // This function should be called by the owner to accept the admin role diff --git a/l1-contracts/deploy-scripts/DeployErc20.s.sol b/l1-contracts/deploy-scripts/DeployErc20.s.sol index 3ad89a8a4..6b58a2acb 100644 --- a/l1-contracts/deploy-scripts/DeployErc20.s.sol +++ b/l1-contracts/deploy-scripts/DeployErc20.s.sol @@ -128,7 +128,6 @@ contract DeployErc20Script is Script { if (mint > 0) { vm.broadcast(); additionalAddressesForMinting.push(config.deployerAddress); -<<<<<<< HEAD // solhint-disable-next-line gas-length-in-loops for (uint256 i = 0; i < additionalAddressesForMinting.length; ++i) { (bool success, ) = tokenAddress.call( @@ -138,17 +137,6 @@ contract DeployErc20Script is Script { if (!success) { revert MintFailed(); } -======= - uint256 addressMintListLength = additionalAddressesForMinting.length; - for (uint256 i = 0; i < addressMintListLength; ++i) { - (bool success, ) = tokenAddress.call( - abi.encodeWithSignature("mint(address,uint256)", additionalAddressesForMinting[i], mint) - ); - if (!success) { - revert MintFailed(); - } - console.log("Minting to:", additionalAddressesForMinting[i]); ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe } } diff --git a/l1-contracts/deploy-scripts/DeployL1.s.sol b/l1-contracts/deploy-scripts/DeployL1.s.sol index 0adb606b4..71813c016 100644 --- a/l1-contracts/deploy-scripts/DeployL1.s.sol +++ b/l1-contracts/deploy-scripts/DeployL1.s.sol @@ -5,14 +5,8 @@ pragma solidity 0.8.24; import {Script, console2 as console} from "forge-std/Script.sol"; import {stdToml} from "forge-std/StdToml.sol"; -<<<<<<< HEAD -import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; -import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; -// import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; -======= import {ProxyAdmin} from "@openzeppelin/contracts-v4/proxy/transparent/ProxyAdmin.sol"; import {TransparentUpgradeableProxy} from "@openzeppelin/contracts-v4/proxy/transparent/TransparentUpgradeableProxy.sol"; ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe import {Utils} from "./Utils.sol"; import {Multicall3} from "contracts/dev-contracts/Multicall3.sol"; @@ -21,13 +15,8 @@ import {TestnetVerifier} from "contracts/state-transition/TestnetVerifier.sol"; import {VerifierParams, IVerifier} from "contracts/state-transition/chain-interfaces/IVerifier.sol"; import {DefaultUpgrade} from "contracts/upgrades/DefaultUpgrade.sol"; import {Governance} from "contracts/governance/Governance.sol"; -<<<<<<< HEAD import {L1GenesisUpgrade} from "contracts/upgrades/L1GenesisUpgrade.sol"; import {ChainAdmin} from "contracts/governance/ChainAdmin.sol"; -======= -import {ChainAdmin} from "contracts/governance/ChainAdmin.sol"; -import {GenesisUpgrade} from "contracts/upgrades/GenesisUpgrade.sol"; ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe import {ValidatorTimelock} from "contracts/state-transition/ValidatorTimelock.sol"; import {Bridgehub} from "contracts/bridgehub/Bridgehub.sol"; import {MessageRoot} from "contracts/bridgehub/MessageRoot.sol"; @@ -361,14 +350,7 @@ contract DeployL1Script is Script { } function deployChainAdmin() internal { -<<<<<<< HEAD bytes memory bytecode = abi.encodePacked(type(ChainAdmin).creationCode, abi.encode(config.ownerAddress)); -======= - bytes memory bytecode = abi.encodePacked( - type(ChainAdmin).creationCode, - abi.encode(config.ownerAddress, address(0)) - ); ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe address contractAddress = deployViaCreate2(bytecode); console.log("ChainAdmin deployed at:", contractAddress); addresses.chainAdmin = contractAddress; diff --git a/l1-contracts/deploy-scripts/DeployL2Contracts.sol b/l1-contracts/deploy-scripts/DeployL2Contracts.sol index 9e3b3bdc6..cd647d7e9 100644 --- a/l1-contracts/deploy-scripts/DeployL2Contracts.sol +++ b/l1-contracts/deploy-scripts/DeployL2Contracts.sol @@ -45,10 +45,6 @@ contract DeployL2Script is Script { deployFactoryDeps(); deploySharedBridge(); deploySharedBridgeProxy(); -<<<<<<< HEAD -======= - initializeChain(); ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe deployForceDeployer(); saveOutput(); @@ -61,10 +57,6 @@ contract DeployL2Script is Script { deployFactoryDeps(); deploySharedBridge(); deploySharedBridgeProxy(); -<<<<<<< HEAD -======= - initializeChain(); ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe saveOutput(); } @@ -196,20 +188,4 @@ contract DeployL2Script is Script { l1SharedBridgeProxy: config.l1SharedBridgeProxy }); } -<<<<<<< HEAD -======= - - 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 - }); - } ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe } diff --git a/l1-contracts/deploy-scripts/RegisterHyperchain.s.sol b/l1-contracts/deploy-scripts/RegisterHyperchain.s.sol index 2d8e7b854..6aca2fefe 100644 --- a/l1-contracts/deploy-scripts/RegisterHyperchain.s.sol +++ b/l1-contracts/deploy-scripts/RegisterHyperchain.s.sol @@ -174,11 +174,7 @@ contract RegisterHyperchainScript is Script { function deployChainAdmin() internal { vm.broadcast(); -<<<<<<< HEAD - ChainAdmin chainAdmin = new ChainAdmin(config.ownerAddress); -======= ChainAdmin chainAdmin = new ChainAdmin(config.ownerAddress, address(0)); ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe console.log("ChainAdmin deployed at:", address(chainAdmin)); config.chainAdmin = address(chainAdmin); } @@ -258,14 +254,8 @@ contract RegisterHyperchainScript is Script { function setPendingAdmin() internal { IZkSyncHyperchain hyperchain = IZkSyncHyperchain(config.newDiamondProxy); -<<<<<<< HEAD - vm.startBroadcast(msg.sender); - hyperchain.setPendingAdmin(config.chainAdmin); - vm.stopBroadcast(); -======= vm.broadcast(); hyperchain.setPendingAdmin(config.chainAdmin); ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe console.log("Owner for ", config.newDiamondProxy, "set to", config.chainAdmin); } diff --git a/l1-contracts/deploy-scripts/Utils.sol b/l1-contracts/deploy-scripts/Utils.sol index 780645b21..c3ab22594 100644 --- a/l1-contracts/deploy-scripts/Utils.sol +++ b/l1-contracts/deploy-scripts/Utils.sol @@ -8,12 +8,8 @@ import {Vm} from "forge-std/Vm.sol"; import {Bridgehub} from "contracts/bridgehub/Bridgehub.sol"; import {L2TransactionRequestDirect} from "contracts/bridgehub/IBridgehub.sol"; import {IGovernance} from "contracts/governance/IGovernance.sol"; -<<<<<<< HEAD -import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; -======= import {IERC20} from "@openzeppelin/contracts-v4/token/ERC20/IERC20.sol"; ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe +import {Ownable} from "@openzeppelin/contracts-v4/access/Ownable.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"; diff --git a/l1-contracts/foundry.toml b/l1-contracts/foundry.toml index b8389b8b9..c640a4b27 100644 --- a/l1-contracts/foundry.toml +++ b/l1-contracts/foundry.toml @@ -1,15 +1,4 @@ [profile.default] -<<<<<<< HEAD -src = 'contracts' -out = 'out' -libs = ['node_modules', 'lib', '../da-contracts/'] -remappings = [ - "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/", - "@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/", - "l2-contracts/=../l2-contracts/contracts/", - "da-contracts/=../da-contracts/contracts/" -] -======= src = "contracts" out = "out" libs = ["lib"] @@ -17,18 +6,14 @@ cache_path = "cache-forge" test = "test/foundry" solc_version = "0.8.24" evm_version = "cancun" ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe allow_paths = ["../l2-contracts/contracts"] fs_permissions = [ { access = "read", path = "../system-contracts/bootloader/build/artifacts" }, { access = "read", path = "../system-contracts/artifacts-zk/contracts-preprocessed" }, { access = "read", path = "../l2-contracts/artifacts-zk/" }, -<<<<<<< HEAD { access = "read", path = "../da-contracts/" }, -======= { access = "read", path = "../l2-contracts/zkout/" }, { access = "read", path = "../system-contracts/zkout/" }, ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe { access = "read", path = "./script-config" }, { access = "read-write", path = "./script-out" }, { access = "read", path = "./out" }, @@ -43,4 +28,6 @@ remappings = [ "foundry-test/=test/foundry/", "@openzeppelin/contracts-v4/=lib/openzeppelin-contracts-v4/contracts/", "@openzeppelin/contracts-upgradeable-v4/=lib/openzeppelin-contracts-upgradeable-v4/contracts/", + "l2-contracts/=../l2-contracts/contracts/", + "da-contracts/=../da-contracts/contracts/" ] diff --git a/l1-contracts/scripts/register-hyperchain.ts b/l1-contracts/scripts/register-hyperchain.ts index 2d8bfa589..8a68c92df 100644 --- a/l1-contracts/scripts/register-hyperchain.ts +++ b/l1-contracts/scripts/register-hyperchain.ts @@ -66,12 +66,8 @@ async function main() { .option("--validium-mode") .option("--base-token-name ") .option("--base-token-address ") -<<<<<<< HEAD - .option("--use-governance") -======= .option("--use-governance ") .option("--token-multiplier-setter-address ") ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe .action(async (cmd) => { const deployWallet = cmd.privateKey ? new Wallet(cmd.privateKey, provider) @@ -105,7 +101,6 @@ async function main() { if (!(await deployer.bridgehubContract(deployWallet).tokenIsRegistered(baseTokenAddress))) { await deployer.registerTokenBridgehub(baseTokenAddress, cmd.useGovernance); } -<<<<<<< HEAD await deployer.registerTokenInNativeTokenVault(baseTokenAddress); await deployer.registerHyperchain( baseTokenAddress, @@ -117,16 +112,13 @@ async function main() { null, cmd.useGovernance ); -======= const tokenMultiplierSetterAddress = cmd.tokenMultiplierSetterAddress || ""; - await deployer.registerHyperchain(baseTokenAddress, cmd.validiumMode, null, gasPrice, useGovernance); if (tokenMultiplierSetterAddress != "") { console.log(`Using token multiplier setter address: ${tokenMultiplierSetterAddress}`); await deployer.setTokenMultiplierSetterAddress(tokenMultiplierSetterAddress); } ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe await deployer.transferAdminFromDeployerToChainAdmin(); }); diff --git a/l1-contracts/src.ts/deploy-process.ts b/l1-contracts/src.ts/deploy-process.ts index cb7ba5232..d197df699 100644 --- a/l1-contracts/src.ts/deploy-process.ts +++ b/l1-contracts/src.ts/deploy-process.ts @@ -61,7 +61,6 @@ export async function initialBridgehubDeployment( await deployer.deployChainAdmin(create2Salt, { gasPrice }); await deployer.deployValidatorTimelock(create2Salt, { gasPrice }); -<<<<<<< HEAD if (!deployer.isZkMode()) { // proxy admin is already deployed when SL's L2SharedBridge is registered await deployer.deployTransparentProxyAdmin(create2Salt, { gasPrice }); @@ -89,15 +88,6 @@ export async function initialBridgehubDeployment( } else { await deployer.deployBlobVersionedHashRetriever(create2Salt, { gasPrice }); } -======= - await deployer.deployGovernance(create2Salt, { gasPrice, nonce }); - nonce++; - - await deployer.deployChainAdmin(create2Salt, { gasPrice, nonce }); - await deployer.deployTransparentProxyAdmin(create2Salt, { gasPrice }); - await deployer.deployBridgehubContract(create2Salt, gasPrice); - await deployer.deployBlobVersionedHashRetriever(create2Salt, { gasPrice }); ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe await deployer.deployStateTransitionManagerContract(create2Salt, extraFacets, gasPrice); await deployer.setStateTransitionManagerInValidatorTimelock({ gasPrice }); } diff --git a/l1-contracts/src.ts/deploy.ts b/l1-contracts/src.ts/deploy.ts index e334bd0cd..afae0dbc2 100644 --- a/l1-contracts/src.ts/deploy.ts +++ b/l1-contracts/src.ts/deploy.ts @@ -63,7 +63,6 @@ import type { FacetCut } from "./diamondCut"; import { getCurrentFacetCutsForAdd } from "./diamondCut"; import { ChainAdminFactory, ERC20Factory, StateTransitionManagerFactory } from "../typechain"; -<<<<<<< HEAD import { IL1AssetRouterFactory } from "../typechain/IL1AssetRouterFactory"; import { IL1NativeTokenVaultFactory } from "../typechain/IL1NativeTokenVaultFactory"; @@ -73,9 +72,6 @@ import { TestnetERC20TokenFactory } from "../typechain/TestnetERC20TokenFactory" import { RollupL1DAValidatorFactory } from "../../da-contracts/typechain/RollupL1DAValidatorFactory"; import { ValidiumL1DAValidatorFactory } from "../../da-contracts/typechain/ValidiumL1DAValidatorFactory"; -======= -import type { Contract, Overrides } from "@ethersproject/contracts"; ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe let L2_BOOTLOADER_BYTECODE_HASH: string; let L2_DEFAULT_ACCOUNT_BYTECODE_HASH: string; @@ -356,8 +352,6 @@ export class Deployer { } public async deployChainAdmin(create2Salt: string, ethTxOptions: ethers.providers.TransactionRequest) { -<<<<<<< HEAD -======= ethTxOptions.gasLimit ??= 10_000_000; const contractAddress = await this.deployViaCreate2( "ChainAdmin", @@ -371,32 +365,13 @@ export class Deployer { this.addresses.ChainAdmin = contractAddress; } - public async deployBridgehubImplementation(create2Salt: string, ethTxOptions: ethers.providers.TransactionRequest) { ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe - ethTxOptions.gasLimit ??= 10_000_000; - const contractAddress = await this.deployViaCreate2("ChainAdmin", [this.ownerAddress], create2Salt, ethTxOptions); - if (this.verbose) { - console.log(`CONTRACTS_CHAIN_ADMIN_ADDR=${contractAddress}`); - } - this.addresses.ChainAdmin = contractAddress; - } - public async deployTransparentProxyAdmin(create2Salt: string, ethTxOptions: ethers.providers.TransactionRequest) { if (this.verbose) { console.log("Deploying Proxy Admin"); } // Note: we cannot deploy using Create2, as the owner of the ProxyAdmin is msg.sender -<<<<<<< HEAD let proxyAdmin; let rec; -======= - const contractFactory = await hardhat.ethers.getContractFactory( - "@openzeppelin/contracts-v4/proxy/transparent/ProxyAdmin.sol:ProxyAdmin", - { - signer: this.deployWallet, - } - ); ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe if (this.isZkMode()) { // @ts-ignore @@ -1327,7 +1302,6 @@ export class Deployer { } } -<<<<<<< HEAD public async executeChainAdminMulticall(calls: ChainAdminCall[], requireSuccess: boolean = true) { const chainAdmin = ChainAdminFactory.connect(this.addresses.ChainAdmin, this.deployWallet); @@ -1335,7 +1309,7 @@ export class Deployer { const multicallTx = await chainAdmin.multicall(calls, requireSuccess, { value: totalValue }); return await multicallTx.wait(); -======= + } public async setTokenMultiplierSetterAddress(tokenMultiplierSetterAddress: string) { const chainAdmin = ChainAdminFactory.connect(this.addresses.ChainAdmin, this.deployWallet); @@ -1345,7 +1319,6 @@ export class Deployer { `Token multiplier setter set as ${tokenMultiplierSetterAddress}, gas used: ${receipt.gasUsed.toString()}` ); } ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe } public async transferAdminFromDeployerToChainAdmin() { @@ -1358,7 +1331,6 @@ export class Deployer { console.log(`ChainAdmin set as pending admin, gas used: ${receipt.gasUsed.toString()}`); } -<<<<<<< HEAD // await this.executeUpgrade( // hyperchain.address, // 0, @@ -1374,21 +1346,6 @@ export class Deployer { data: acceptAdminData, }, ]); -======= - const acceptAdminData = hyperchain.interface.encodeFunctionData("acceptAdmin"); - const chainAdmin = ChainAdminFactory.connect(this.addresses.ChainAdmin, this.deployWallet); - const multicallTx = await chainAdmin.multicall( - [ - { - target: hyperchain.address, - value: 0, - data: acceptAdminData, - }, - ], - true - ); - await multicallTx.wait(); ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe if (this.verbose) { console.log("Pending admin successfully accepted"); diff --git a/l2-contracts/contracts/L2ContractHelper.sol b/l2-contracts/contracts/L2ContractHelper.sol index edeec071e..97bf6b55e 100644 --- a/l2-contracts/contracts/L2ContractHelper.sol +++ b/l2-contracts/contracts/L2ContractHelper.sol @@ -5,7 +5,7 @@ pragma solidity ^0.8.20; import {EfficientCall} from "@matterlabs/zksync-contracts/l2/system-contracts/libraries/EfficientCall.sol"; import {IL2AssetRouter} from "./bridge/interfaces/IL2AssetRouter.sol"; import {IL2NativeTokenVault} from "./bridge/interfaces/IL2NativeTokenVault.sol"; -import {MalformedBytecode, BytecodeError} from "./L2ContractErrors.sol"; +import {MalformedBytecode, BytecodeError} from "./errors/L2ContractErrors.sol"; /** * @author Matter Labs diff --git a/l2-contracts/contracts/bridge/L2AssetRouter.sol b/l2-contracts/contracts/bridge/L2AssetRouter.sol index 20be7d767..d143517b1 100644 --- a/l2-contracts/contracts/bridge/L2AssetRouter.sol +++ b/l2-contracts/contracts/bridge/L2AssetRouter.sol @@ -16,7 +16,7 @@ import {AddressAliasHelper} from "../vendor/AddressAliasHelper.sol"; import {L2ContractHelper, L2_NATIVE_TOKEN_VAULT} from "../L2ContractHelper.sol"; import {DataEncoding} from "../common/libraries/DataEncoding.sol"; -import {EmptyAddress, InvalidCaller} from "../L2ContractErrors.sol"; +import {EmptyAddress, InvalidCaller} from "../errors/L2ContractErrors.sol"; /// @author Matter Labs /// @custom:security-contact security@matterlabs.dev diff --git a/l2-contracts/contracts/bridge/L2NativeTokenVault.sol b/l2-contracts/contracts/bridge/L2NativeTokenVault.sol index 1cab56ae3..56c50d9a8 100644 --- a/l2-contracts/contracts/bridge/L2NativeTokenVault.sol +++ b/l2-contracts/contracts/bridge/L2NativeTokenVault.sol @@ -14,7 +14,7 @@ import {L2ContractHelper, DEPLOYER_SYSTEM_CONTRACT, L2_ASSET_ROUTER, IContractDe import {SystemContractsCaller} from "../SystemContractsCaller.sol"; import {DataEncoding} from "../common/libraries/DataEncoding.sol"; -import {EmptyAddress, EmptyBytes32, AddressMismatch, AssetIdMismatch, DeployFailed, AmountMustBeGreaterThanZero, InvalidCaller} from "../L2ContractErrors.sol"; +import {EmptyAddress, EmptyBytes32, AddressMismatch, AssetIdMismatch, DeployFailed, AmountMustBeGreaterThanZero, InvalidCaller} from "../errors/L2ContractErrors.sol"; /// @author Matter Labs /// @custom:security-contact security@matterlabs.dev diff --git a/l2-contracts/contracts/bridge/L2StandardERC20.sol b/l2-contracts/contracts/bridge/L2StandardERC20.sol index 60f68dde4..df81f542e 100644 --- a/l2-contracts/contracts/bridge/L2StandardERC20.sol +++ b/l2-contracts/contracts/bridge/L2StandardERC20.sol @@ -147,25 +147,6 @@ contract L2StandardERC20 is ERC20PermitUpgradeable, IL2StandardToken, ERC1967Upg emit BridgeInitialize(l1Address, _newName, _newSymbol, decimals_); } -<<<<<<< HEAD -======= - modifier onlyBridge() { - if (msg.sender != l2Bridge) { - revert Unauthorized(msg.sender); - } - _; - } - - modifier onlyNextVersion(uint8 _version) { - // The version should be incremented by 1. Otherwise, the governor risks disabling - // future reinitialization of the token by providing too large a version. - if (_version != _getInitializedVersion() + 1) { - revert NonSequentialVersion(); - } - _; - } - ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe /// @dev Mint tokens to a given account. /// @param _to The account that will receive the created tokens. /// @param _amount The amount that will be created. diff --git a/l2-contracts/contracts/bridge/L2WrappedBaseToken.sol b/l2-contracts/contracts/bridge/L2WrappedBaseToken.sol index f8566f01d..f7bda32b5 100644 --- a/l2-contracts/contracts/bridge/L2WrappedBaseToken.sol +++ b/l2-contracts/contracts/bridge/L2WrappedBaseToken.sol @@ -80,16 +80,6 @@ contract L2WrappedBaseToken is ERC20PermitUpgradeable, IL2WrappedBaseToken, IL2S emit Initialize(name_, symbol_, 18); } -<<<<<<< HEAD -======= - modifier onlyBridge() { - if (msg.sender != l2Bridge) { - revert Unauthorized(msg.sender); - } - _; - } - ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe /// @notice Function for minting tokens on L2, implemented only to be compatible with IL2StandardToken interface. /// Always reverts instead of minting anything! /// Note: Use `deposit`/`depositTo` methods instead. diff --git a/l2-contracts/contracts/errors/L2ContractErrors.sol b/l2-contracts/contracts/errors/L2ContractErrors.sol index 918d52d1d..c7d5deffe 100644 --- a/l2-contracts/contracts/errors/L2ContractErrors.sol +++ b/l2-contracts/contracts/errors/L2ContractErrors.sol @@ -4,11 +4,8 @@ pragma solidity ^0.8.20; // 0x1f73225f error AddressMismatch(address expected, address supplied); -<<<<<<< HEAD:l2-contracts/contracts/L2ContractErrors.sol error AssetIdMismatch(bytes32 expected, bytes32 supplied); -======= // 0x5e85ae73 ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe:l2-contracts/contracts/errors/L2ContractErrors.sol error AmountMustBeGreaterThanZero(); // 0xb4f54111 error DeployFailed(); @@ -36,7 +33,6 @@ error UnimplementedMessage(string message); error UnsupportedPaymasterFlow(); // 0x750b219c error WithdrawFailed(); -<<<<<<< HEAD:l2-contracts/contracts/L2ContractErrors.sol error MalformedBytecode(BytecodeError); enum BytecodeError { @@ -46,9 +42,7 @@ enum BytecodeError { WordsMustBeOdd, DictionaryLength } -======= // 0xd92e233d error ZeroAddress(); ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe:l2-contracts/contracts/errors/L2ContractErrors.sol string constant BRIDGE_MINT_NOT_IMPLEMENTED = "bridgeMint is not implemented! Use deposit/depositTo methods instead."; diff --git a/l2-contracts/test/erc20.test.ts b/l2-contracts/test/erc20.test.ts index e7ce20449..ec531f7aa 100644 --- a/l2-contracts/test/erc20.test.ts +++ b/l2-contracts/test/erc20.test.ts @@ -54,7 +54,6 @@ describe("ERC20Bridge", function () { // While we formally don't need to deploy the token and the beacon proxy, it is a neat way to have the bytecode published const l2TokenImplAddress = await deployer.deploy(await deployer.loadArtifact("L2StandardERC20")); -<<<<<<< HEAD const l2Erc20TokenBeacon = await deployer.deploy(await deployer.loadArtifact("UpgradeableBeacon"), [ l2TokenImplAddress.address, ]); @@ -71,19 +70,6 @@ describe("ERC20Bridge", function () { (await deployer.loadArtifact("L2AssetRouter")).bytecode, true, constructorArgs -======= - const l2Erc20TokenBeacon = await deployer.deploy( - await deployer.loadArtifact("@openzeppelin/contracts-v4/proxy/beacon/UpgradeableBeacon.sol:UpgradeableBeacon"), - [l2TokenImplAddress.address] - ); - await deployer.deploy( - await deployer.loadArtifact("@openzeppelin/contracts-v4/proxy/beacon/BeaconProxy.sol:BeaconProxy"), - [l2Erc20TokenBeacon.address, "0x"] - ); - - const beaconProxyBytecodeHash = hashBytecode( - (await deployer.loadArtifact("@openzeppelin/contracts-v4/proxy/beacon/BeaconProxy.sol:BeaconProxy")).bytecode ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe ); erc20Bridge = L2AssetRouterFactory.connect(L2_ASSET_ROUTER_ADDRESS, deployerWallet); @@ -101,20 +87,9 @@ describe("ERC20Bridge", function () { constructorArgs ); -<<<<<<< HEAD erc20NativeTokenVault = L2NativeTokenVaultFactory.connect(L2_NATIVE_TOKEN_VAULT_ADDRESS, l1BridgeWallet); const governorNTV = L2NativeTokenVaultFactory.connect(L2_NATIVE_TOKEN_VAULT_ADDRESS, governorWallet); await governorNTV.configureL2TokenBeacon(false, ethers.constants.AddressZero); -======= - const erc20BridgeProxy = await deployer.deploy( - await deployer.loadArtifact( - "@openzeppelin/contracts-v4/proxy/transparent/TransparentUpgradeableProxy.sol:TransparentUpgradeableProxy" - ), - [erc20BridgeImpl.address, governorWallet.address, bridgeInitializeData] - ); - - erc20Bridge = L2SharedBridgeFactory.connect(erc20BridgeProxy.address, deployerWallet); ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe }); it("Should finalize deposit ERC20 deposit", async function () { diff --git a/system-contracts/SystemContractsHashes.json b/system-contracts/SystemContractsHashes.json index 76598528b..abf7c87f1 100644 --- a/system-contracts/SystemContractsHashes.json +++ b/system-contracts/SystemContractsHashes.json @@ -3,85 +3,50 @@ "contractName": "AccountCodeStorage", "bytecodePath": "artifacts-zk/contracts-preprocessed/AccountCodeStorage.sol/AccountCodeStorage.json", "sourceCodePath": "contracts-preprocessed/AccountCodeStorage.sol", -<<<<<<< HEAD "bytecodeHash": "0x0100005d1aca0cbed0567a1c7e07a1da321386b1665e440da22053addd4639e1", "sourceCodeHash": "0xea3806fcaf7728463f559fe195d8acdc47a7659d58119e0a51efcf86a691b61b" -======= - "bytecodeHash": "0x0100005d05a277543946914759aa4a6c403604b828f80d00b900c669c3d224e1", - "sourceCodeHash": "0x2e0e09d57a04bd1e722d8bf8c6423fdf3f8bca44e5e8c4f6684f987794be066e" ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe }, { "contractName": "BootloaderUtilities", "bytecodePath": "artifacts-zk/contracts-preprocessed/BootloaderUtilities.sol/BootloaderUtilities.json", "sourceCodePath": "contracts-preprocessed/BootloaderUtilities.sol", -<<<<<<< HEAD "bytecodeHash": "0x010007c7149301f5f29fa1757a600b5088354d9e280a6d8f69bcce4f2dbce660", "sourceCodeHash": "0x9d2b7376c4cd9b143ddd5dfe001a9faae99b9125ccd45f2915c3ce0099643ed9" -======= - "bytecodeHash": "0x010007c7bb63f64649098bf75f4baa588db20f445b4d20b7cca972d5d8f973ce", - "sourceCodeHash": "0x0f1213c4b95acb71f4ab5d4082cc1aeb2bd5017e1cccd46afc66e53268609d85" ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe }, { "contractName": "ComplexUpgrader", "bytecodePath": "artifacts-zk/contracts-preprocessed/ComplexUpgrader.sol/ComplexUpgrader.json", "sourceCodePath": "contracts-preprocessed/ComplexUpgrader.sol", -<<<<<<< HEAD "bytecodeHash": "0x0100004deb11ca32277ab54e2d036f4d33b4a7e218ced1fee63b5b4713ff50ff", "sourceCodeHash": "0xdde7c49a94cc3cd34c3e7ced1b5ba45e4740df68d26243871edbe393e7298f7a" -======= - "bytecodeHash": "0x0100004da9f3aa5e4febcc53522cb7ee6949369fde25dd79e977752b82b9fd5d", - "sourceCodeHash": "0x796046a914fb676ba2bbd337b2924311ee2177ce54571c18a2c3945755c83614" ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe }, { "contractName": "Compressor", "bytecodePath": "artifacts-zk/contracts-preprocessed/Compressor.sol/Compressor.json", "sourceCodePath": "contracts-preprocessed/Compressor.sol", -<<<<<<< HEAD "bytecodeHash": "0x0100013fc989177bcb6ec29d851cd01f845de31963ea5817ea7a684767c36368", "sourceCodeHash": "0xb0cec0016f481ce023478f71727fbc0d82e967ddc0508e4d47f5c52292a3f790" -======= - "bytecodeHash": "0x0100014fb4f05ae09288cbcf4fa7a09ca456910f6e69be5ac2c2dfc8d71d1576", - "sourceCodeHash": "0xc6f7cd8b21aae52ed3dd5083c09b438a7af142a4ecda6067c586770e8be745a5" ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe }, { "contractName": "ContractDeployer", "bytecodePath": "artifacts-zk/contracts-preprocessed/ContractDeployer.sol/ContractDeployer.json", "sourceCodePath": "contracts-preprocessed/ContractDeployer.sol", -<<<<<<< HEAD "bytecodeHash": "0x010004e5711fff19f0048d745b0177b8b73952963b6de79ff4e16c902dbcc091", "sourceCodeHash": "0xea9627fd5e6e905c268ba801e87bf2d9022bea036982d2b54425f2388b27e6b1" -======= - "bytecodeHash": "0x010004e5d52d692822d5c54ac87de3297f39be0e4a6f72f2830ae5ac856684ee", - "sourceCodeHash": "0x82f81fbf5fb007a9cac97462d50907ca5d7a1af62d82d2645e093ed8647a5209" ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe }, { "contractName": "Create2Factory", "bytecodePath": "artifacts-zk/contracts-preprocessed/Create2Factory.sol/Create2Factory.json", "sourceCodePath": "contracts-preprocessed/Create2Factory.sol", -<<<<<<< HEAD "bytecodeHash": "0x0100004937dba13ac3e393def7fe6cf01da88bbe9b087c397e950301fe14377d", "sourceCodeHash": "0x217e65f55c8add77982171da65e0db8cc10141ba75159af582973b332a4e098a" -======= - "bytecodeHash": "0x010000495bd172e90725e6bfafe73e36a288d616d4673f5347eeb819a78bf546", - "sourceCodeHash": "0x114d9322a9ca654989f3e0b3b21f1311dbc4db84f443d054cd414f6414d84de3" ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe }, { "contractName": "DefaultAccount", "bytecodePath": "artifacts-zk/contracts-preprocessed/DefaultAccount.sol/DefaultAccount.json", "sourceCodePath": "contracts-preprocessed/DefaultAccount.sol", -<<<<<<< HEAD "bytecodeHash": "0x0100055d7adab6efac115df578d88bc113738dc6ad811329c7575c2af3d91756", "sourceCodeHash": "0xeb5ac8fc83e1c8619db058a9b6973958bd6ed1b6f4938f8f4541d702f12e085d" -======= - "bytecodeHash": "0x0100055dba11508480be023137563caec69debc85f826cb3a4b68246a7cabe30", - "sourceCodeHash": "0xebffe840ebbd9329edb1ebff8ca50f6935e7dabcc67194a896fcc2e968d46dfb" ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe }, { "contractName": "EmptyContract", @@ -94,49 +59,29 @@ "contractName": "ImmutableSimulator", "bytecodePath": "artifacts-zk/contracts-preprocessed/ImmutableSimulator.sol/ImmutableSimulator.json", "sourceCodePath": "contracts-preprocessed/ImmutableSimulator.sol", -<<<<<<< HEAD "bytecodeHash": "0x0100003bf60f81cb3074170af6420e8d74b710fea0b1fa04e291a080ec17f98a", "sourceCodeHash": "0x4212e99cbc1722887cfb5b4cb967f278ac8642834786f0e3c6f3b324a9316815" -======= - "bytecodeHash": "0x01000039785a8e0d342a49b6b6c6e156801b816434d93bee85d33f56d56b4f9a", - "sourceCodeHash": "0x9659e69f7db09e8f60a8bb95314b1ed26afcc689851665cf27f5408122f60c98" ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe }, { "contractName": "KnownCodesStorage", "bytecodePath": "artifacts-zk/contracts-preprocessed/KnownCodesStorage.sol/KnownCodesStorage.json", "sourceCodePath": "contracts-preprocessed/KnownCodesStorage.sol", -<<<<<<< HEAD "bytecodeHash": "0x0100006f5cf65a28234e0791927389664cced66dd3d600aefbe120d63e9debae", "sourceCodeHash": "0x8da495a9fc5aa0d7d20a165a4fc8bc77012bec29c472015ea5ecc0a2bd706137" -======= - "bytecodeHash": "0x0100006f0f209c9e6d06b1327db1257b15fa7a8b9864ee5ccd12cd3f8bc40ac9", - "sourceCodeHash": "0xb39b5b81168653e0c5062f7b8e1d6d15a4e186df3317f192f0cb2fc3a74f5448" ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe }, { "contractName": "L1Messenger", "bytecodePath": "artifacts-zk/contracts-preprocessed/L1Messenger.sol/L1Messenger.json", "sourceCodePath": "contracts-preprocessed/L1Messenger.sol", -<<<<<<< HEAD "bytecodeHash": "0x010001e9765b885f7e6422722e36e6d375beffc916047f5cec419d11d178baea", "sourceCodeHash": "0xd83b345b8633affb0bba2296fec9424b4fe9483b60c20ca407d755857a385d8e" -======= - "bytecodeHash": "0x010002955993e8ff8190e388e94a6bb791fbe9c6388e5011c52cb587a4ebf05e", - "sourceCodeHash": "0xa8768fdaac6d8804782f14e2a51bbe2b6be31dee9103b6d02d149ea8dc46eb6a" ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe }, { "contractName": "L2BaseToken", "bytecodePath": "artifacts-zk/contracts-preprocessed/L2BaseToken.sol/L2BaseToken.json", "sourceCodePath": "contracts-preprocessed/L2BaseToken.sol", -<<<<<<< HEAD "bytecodeHash": "0x0100010517992363aa510731a717db3c7740d1c31e69718090d07f73d47ba960", "sourceCodeHash": "0x4cdafafd4cfdf410b31641e14487ea657be3af25e5ec1754fcd7ad67ec23d8be" -======= - "bytecodeHash": "0x01000103174a90beadc2cffe3e81bdb7b8a576174e888809c1953175fd3015b4", - "sourceCodeHash": "0x8bdd2b4d0b53dba84c9f0af250bbaa2aad10b3de6747bba957f0bd3721090dfa" ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe }, { "contractName": "L2GenesisUpgrade", @@ -149,49 +94,29 @@ "contractName": "MsgValueSimulator", "bytecodePath": "artifacts-zk/contracts-preprocessed/MsgValueSimulator.sol/MsgValueSimulator.json", "sourceCodePath": "contracts-preprocessed/MsgValueSimulator.sol", -<<<<<<< HEAD "bytecodeHash": "0x0100005d3182a51477d7ee3488aafab351bb5f0560412e4df7e5a5e21ca87cd5", "sourceCodeHash": "0x4834adf62dbaefa1a1c15d36b5ad1bf2826e7d888a17be495f7ed4e4ea381aa8" -======= - "bytecodeHash": "0x0100005da36075675b98f85fe90df11c1d526f6b12925da3a55a8b9c02aaac5f", - "sourceCodeHash": "0x082f3dcbc2fe4d93706c86aae85faa683387097d1b676e7ebd00f71ee0f13b71" ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe }, { "contractName": "NonceHolder", "bytecodePath": "artifacts-zk/contracts-preprocessed/NonceHolder.sol/NonceHolder.json", "sourceCodePath": "contracts-preprocessed/NonceHolder.sol", -<<<<<<< HEAD "bytecodeHash": "0x010000db9e6c4608ca06cd3a69484b23f5e4ee196fa046cb2db8c0b56d3a2163", "sourceCodeHash": "0xaa2ed3a26af30032c00a612ac327e0cdf5288b7c932ae903462355f863f950cb" -======= - "bytecodeHash": "0x010000d97de8c14cd36b1ce06cd7f44a09f6093ec8eb4041629c0fc2116d0c73", - "sourceCodeHash": "0xcd0c0366effebf2c98c58cf96322cc242a2d1c675620ef5514b7ed1f0a869edc" ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe }, { "contractName": "PubdataChunkPublisher", "bytecodePath": "artifacts-zk/contracts-preprocessed/PubdataChunkPublisher.sol/PubdataChunkPublisher.json", "sourceCodePath": "contracts-preprocessed/PubdataChunkPublisher.sol", -<<<<<<< HEAD "bytecodeHash": "0x01000049825a39e7057700666867a2b2be806c15d9b2addb60d335bb61b405d9", "sourceCodeHash": "0x0da0d1279f906147a40e278f52bf3e4d5d4f24225935e4611cc04f4b387b5286" -======= - "bytecodeHash": "0x010000470e396f376539289b7975b6866914a8a0994008a02987edac8be81db7", - "sourceCodeHash": "0xd7161e2c8092cf57b43c6220bc605c0e7e540bddcde1af24e2d90f75633b098e" ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe }, { "contractName": "SystemContext", "bytecodePath": "artifacts-zk/contracts-preprocessed/SystemContext.sol/SystemContext.json", "sourceCodePath": "contracts-preprocessed/SystemContext.sol", -<<<<<<< HEAD "bytecodeHash": "0x010001a722bf92cd15264a662582a6806db24101493ad7a1f202428a2a10e7bc", "sourceCodeHash": "0x532a962209042f948e8a13e3f4cf12b6d53631e0fc5fa53083c7e2d8062771c0" -======= - "bytecodeHash": "0x010001a5eabf9e28288b7ab7e1db316148021347460cfb4314570956867d5af5", - "sourceCodeHash": "0xf308743981ef5cea2f7a3332b8e51695a5e47e811a63974437fc1cceee475e7a" ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe }, { "contractName": "EventWriter", @@ -260,60 +185,35 @@ "contractName": "bootloader_test", "bytecodePath": "bootloader/build/artifacts/bootloader_test.yul.zbin", "sourceCodePath": "bootloader/build/bootloader_test.yul", -<<<<<<< HEAD "bytecodeHash": "0x010003cb983fb1cded326ff4429b8a637a7b045233c427dc498373be58312969", "sourceCodeHash": "0xa4f83a28bcc3d3a79c197a77de03dce464b2141c3aaf970ad3f3487f41ae5690" -======= - "bytecodeHash": "0x010003cbe67434b2848054322cbc311385851bbfbf70d17f92cd5f1f9836b25e", - "sourceCodeHash": "0x006fdf461899dec5fdb34301c23e6819eb93e275907cbfc67d73fccfb47cae68" ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe }, { "contractName": "fee_estimate", "bytecodePath": "bootloader/build/artifacts/fee_estimate.yul.zbin", "sourceCodePath": "bootloader/build/fee_estimate.yul", -<<<<<<< HEAD "bytecodeHash": "0x010009559ef1268a8b83687828c5dfc804c58a018028694fc931df712fb67f58", "sourceCodeHash": "0xe7970c1738f2817b50bfcd16038227c5c059f12309407977df453bc6d365d31e" -======= - "bytecodeHash": "0x0100092d9b8ae575bca569f68fe60fbef1dc7d4aad6aa02cc4836f56afcf0a38", - "sourceCodeHash": "0x8a858319bac2924a3dee778218a7fe5e23898db0d87b02d7b783f94c5a02d257" ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe }, { "contractName": "gas_test", "bytecodePath": "bootloader/build/artifacts/gas_test.yul.zbin", "sourceCodePath": "bootloader/build/gas_test.yul", -<<<<<<< HEAD "bytecodeHash": "0x010008db4c71130d6a96d77f2d3bf3573a5cddc72ecee030ecc9c3bc22764039", "sourceCodeHash": "0x1b6ef61d0dbbbaa049946b95dc6d12d9335baed03b8c3364a0cbdb404495c045" -======= - "bytecodeHash": "0x010008b3e1b8bdd393f2f8d6f5c994c8b9c287df7310dee75d0c52a245fc7cb1", - "sourceCodeHash": "0x89f5ad470f10e755fa57b82507518e571c24409a328bc33aeba26e9518ad1c3e" ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe }, { "contractName": "playground_batch", "bytecodePath": "bootloader/build/artifacts/playground_batch.yul.zbin", "sourceCodePath": "bootloader/build/playground_batch.yul", -<<<<<<< HEAD "bytecodeHash": "0x0100095b01a95cb5caa633702442aeeac8ea64bf4dab31d534e0bb815b5c2820", "sourceCodeHash": "0xb5a2f9d7d8990f9a1296f699573a5dffe3a1d2ed53d9d3c60b313cd9443221ab" -======= - "bytecodeHash": "0x01000933061c23d700f3f647c45068e22f5506ff33bb516ac13f11069b163986", - "sourceCodeHash": "0x769448c4fd2b65c43d758ca5f34dd29d9b9dd3000fd0ec89cffcaf8d365a64fd" ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe }, { "contractName": "proved_batch", "bytecodePath": "bootloader/build/artifacts/proved_batch.yul.zbin", "sourceCodePath": "bootloader/build/proved_batch.yul", -<<<<<<< HEAD "bytecodeHash": "0x010008eba57f69c88344eced34109c75d34b4bf84db66f47b47b4579b930355e", "sourceCodeHash": "0xb7697ee1c00b1b8af52c166e3a6deb862281616ca8861ab3aa0b51e34aed8715" -======= - "bytecodeHash": "0x010008c3be57ae5800e077b6c2056d9d75ad1a7b4f0ce583407961cc6fe0b678", - "sourceCodeHash": "0x908bc6ddb34ef89b125e9637239a1149deacacd91255781d82a65a542a39036e" ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe } ] diff --git a/system-contracts/contracts/L1Messenger.sol b/system-contracts/contracts/L1Messenger.sol index f3deb0fa2..d79b129f7 100644 --- a/system-contracts/contracts/L1Messenger.sol +++ b/system-contracts/contracts/L1Messenger.sol @@ -2,14 +2,9 @@ pragma solidity 0.8.24; -<<<<<<< HEAD import {IL1Messenger, L2ToL1Log, L2_L1_LOGS_TREE_DEFAULT_LEAF_HASH, L2_TO_L1_LOG_SERIALIZE_SIZE} from "./interfaces/IL1Messenger.sol"; -import {ISystemContract} from "./interfaces/ISystemContract.sol"; -======= -import {IL1Messenger, L2ToL1Log, L2_L1_LOGS_TREE_DEFAULT_LEAF_HASH, L2_TO_L1_LOG_SERIALIZE_SIZE, STATE_DIFF_COMPRESSION_VERSION_NUMBER} from "./interfaces/IL1Messenger.sol"; import {SystemContractBase} from "./abstract/SystemContractBase.sol"; ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe import {SystemContractHelper} from "./libraries/SystemContractHelper.sol"; import {EfficientCall} from "./libraries/EfficientCall.sol"; import {Utils} from "./libraries/Utils.sol"; @@ -291,7 +286,6 @@ contract L1Messenger is IL1Messenger, SystemContractBase { } bytes32 localLogsRootHash = l2ToL1LogsTreeArray[0]; -<<<<<<< HEAD bytes32 aggregatedRootHash = L2_MESSAGE_ROOT.getAggregatedRoot(); bytes32 fullRootHash = keccak256(bytes.concat(localLogsRootHash, aggregatedRootHash)); @@ -310,43 +304,6 @@ contract L1Messenger is IL1Messenger, SystemContractBase { }); l2DAValidatorOutputhash = abi.decode(returnData, (bytes32)); -======= - /// Check messages - uint32 numberOfMessages = uint32(bytes4(_totalL2ToL1PubdataAndStateDiffs[calldataPtr:calldataPtr + 4])); - calldataPtr += 4; - bytes32 reconstructedChainedMessagesHash = bytes32(0); - for (uint256 i = 0; i < numberOfMessages; ++i) { - uint32 currentMessageLength = uint32(bytes4(_totalL2ToL1PubdataAndStateDiffs[calldataPtr:calldataPtr + 4])); - calldataPtr += 4; - bytes32 hashedMessage = EfficientCall.keccak( - _totalL2ToL1PubdataAndStateDiffs[calldataPtr:calldataPtr + currentMessageLength] - ); - calldataPtr += currentMessageLength; - reconstructedChainedMessagesHash = keccak256(abi.encode(reconstructedChainedMessagesHash, hashedMessage)); - } - if (reconstructedChainedMessagesHash != chainedMessagesHash) { - revert ReconstructionMismatch(PubdataField.MsgHash, chainedMessagesHash, reconstructedChainedMessagesHash); - } - - /// Check bytecodes - uint32 numberOfBytecodes = uint32(bytes4(_totalL2ToL1PubdataAndStateDiffs[calldataPtr:calldataPtr + 4])); - calldataPtr += 4; - bytes32 reconstructedChainedL1BytecodesRevealDataHash = bytes32(0); - for (uint256 i = 0; i < numberOfBytecodes; ++i) { - uint32 currentBytecodeLength = uint32( - bytes4(_totalL2ToL1PubdataAndStateDiffs[calldataPtr:calldataPtr + 4]) - ); - calldataPtr += 4; - reconstructedChainedL1BytecodesRevealDataHash = keccak256( - abi.encode( - reconstructedChainedL1BytecodesRevealDataHash, - Utils.hashL2Bytecode( - _totalL2ToL1PubdataAndStateDiffs[calldataPtr:calldataPtr + currentBytecodeLength] - ) - ) - ); - calldataPtr += currentBytecodeLength; ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe } /// Native (VM) L2 to L1 log diff --git a/system-contracts/contracts/PubdataChunkPublisher.sol b/system-contracts/contracts/PubdataChunkPublisher.sol index 535667d68..2f48a9626 100644 --- a/system-contracts/contracts/PubdataChunkPublisher.sol +++ b/system-contracts/contracts/PubdataChunkPublisher.sol @@ -2,14 +2,7 @@ pragma solidity 0.8.24; import {IPubdataChunkPublisher} from "./interfaces/IPubdataChunkPublisher.sol"; -<<<<<<< HEAD -import {ISystemContract} from "./interfaces/ISystemContract.sol"; import {BLOB_SIZE_BYTES, MAX_NUMBER_OF_BLOBS} from "./Constants.sol"; -======= -import {SystemContractBase} from "./abstract/SystemContractBase.sol"; -import {L1_MESSENGER_CONTRACT, BLOB_SIZE_BYTES, MAX_NUMBER_OF_BLOBS, SystemLogKey} from "./Constants.sol"; -import {SystemContractHelper} from "./libraries/SystemContractHelper.sol"; ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe import {TooMuchPubdata} from "./SystemContractErrors.sol"; /** diff --git a/system-contracts/contracts/interfaces/IComplexUpgrader.sol b/system-contracts/contracts/interfaces/IComplexUpgrader.sol index 6e3624b89..3b1468417 100644 --- a/system-contracts/contracts/interfaces/IComplexUpgrader.sol +++ b/system-contracts/contracts/interfaces/IComplexUpgrader.sol @@ -1,9 +1,5 @@ // SPDX-License-Identifier: MIT -<<<<<<< HEAD -// 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. -======= // 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. ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe pragma solidity ^0.8.20; /** diff --git a/system-contracts/package.json b/system-contracts/package.json index 20d4aed40..70e7208b7 100644 --- a/system-contracts/package.json +++ b/system-contracts/package.json @@ -63,12 +63,8 @@ "deploy-preimages": "ts-node scripts/deploy-preimages.ts", "copy:typechain": "mkdir -p ../l2-contracts/typechain && cp ./typechain/ContractDeployerFactory.ts ../l2-contracts/typechain/", "preprocess:bootloader": "rm -rf ./bootloader/build && yarn ts-node scripts/preprocess-bootloader.ts", -<<<<<<< HEAD - "preprocess:system-contracts": "ts-node scripts/preprocess-system-contracts.ts", -======= "preprocess:system-contracts": "rm -rf ./contracts-preprocessed && ts-node scripts/preprocess-system-contracts.ts", "verify-on-explorer": "hardhat run scripts/verify-on-explorer.ts", ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe "test": "yarn build:test-system-contracts && hardhat test --network zkSyncTestNode", "test-node": "hardhat node-zksync --tag v0.0.1-vm1.5.0", "test:bootloader": "cd bootloader/test_infra && cargo run" diff --git a/system-contracts/scripts/utils.ts b/system-contracts/scripts/utils.ts index 93ce6f515..8f63cd004 100644 --- a/system-contracts/scripts/utils.ts +++ b/system-contracts/scripts/utils.ts @@ -16,6 +16,7 @@ import path from "path"; import { spawn as _spawn } from "child_process"; import { createHash } from "crypto"; import { CompilerDownloader } from "hardhat/internal/solidity/compiler/downloader"; +import fetch from 'node-fetch'; export type HttpMethod = "POST" | "GET"; @@ -326,8 +327,6 @@ export async function isFolderEmpty(folderPath: string): Promise { } catch (error) { console.error("No target folder with artifacts."); return true; // Return true if an error, as folder doesn't exist. -<<<<<<< HEAD -======= } } /** @@ -371,6 +370,5 @@ export async function query( error: "Could not decode JSON in response", status: `${response.status} ${response.statusText}`, }; ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe } } diff --git a/yarn.lock b/yarn.lock index a353889cb..c550f2d1e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -763,7 +763,6 @@ sinon-chai "^3.7.0" undici "^5.14.0" -<<<<<<< HEAD "@matterlabs/hardhat-zksync-solc@^0.3.15": version "0.3.17" resolved "https://registry.yarnpkg.com/@matterlabs/hardhat-zksync-solc/-/hardhat-zksync-solc-0.3.17.tgz#72f199544dc89b268d7bfc06d022a311042752fd" @@ -773,8 +772,6 @@ chalk "4.1.2" dockerode "^3.3.4" -======= ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe "@matterlabs/hardhat-zksync-verify@0.6.1": version "0.6.1" resolved "https://registry.yarnpkg.com/@matterlabs/hardhat-zksync-verify/-/hardhat-zksync-verify-0.6.1.tgz#3fd83f4177ac0b138656ed93d4756ec27f1d329d" @@ -1219,12 +1216,12 @@ resolved "https://registry.yarnpkg.com/@nomiclabs/hardhat-waffle/-/hardhat-waffle-2.0.6.tgz#d11cb063a5f61a77806053e54009c40ddee49a54" integrity sha512-+Wz0hwmJGSI17B+BhU/qFRZ1l6/xMW82QGXE/Gi+WTmwgJrQefuBs1lIf7hzQ1hLk6hpkvb/zwcNkpVKRYTQYg== -"@openzeppelin/contracts-upgradeable-v4@npm:@openzeppelin/contracts-upgradeable@4.9.5": +"@openzeppelin/contracts-upgradeable-v4@npm:@openzeppelin/contracts-upgradeable@4.9.5", "@openzeppelin/contracts-upgradeable@4.9.5": 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-v4@npm:@openzeppelin/contracts@4.9.5", "@openzeppelin/contracts@4.9.5": version "4.9.5" resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.9.5.tgz#1eed23d4844c861a1835b5d33507c1017fa98de8" integrity sha512-ZK+W5mVhRppff9BE6YdR8CC52C8zAvsVAiWhEtQ5+oNxFE6h1WdeWo+FJSF8KKvtxxVYZ7MTP/5KoVpAU3aSWg== @@ -4444,9 +4441,6 @@ hardhat@=2.22.2: uuid "^8.3.2" ws "^7.4.6" -<<<<<<< HEAD -hardhat@^2.14.0, hardhat@^2.19.4: -======= hardhat@^2.14.0: version "2.22.7" resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.22.7.tgz#3de0ce5074063cf468876c5e62f84c66d2408e8e" @@ -4497,7 +4491,6 @@ hardhat@^2.14.0: ws "^7.4.6" hardhat@^2.19.4: ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe version "2.22.5" resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.22.5.tgz#7e1a4311fa9e34a1cfe337784eae06706f6469a5" integrity sha512-9Zq+HonbXCSy6/a13GY1cgHglQRfh4qkzmj1tpPlhxJDwNVnhxlReV6K7hCWFKlOrV13EQwsdcD0rjcaQKWRZw== @@ -7925,7 +7918,6 @@ yocto-queue@^1.0.0: resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.0.0.tgz#7f816433fb2cbc511ec8bf7d263c3b58a1a3c251" integrity sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g== -<<<<<<< HEAD zksync-ethers@5.8.0-beta.5: version "5.8.0-beta.5" resolved "https://registry.yarnpkg.com/zksync-ethers/-/zksync-ethers-5.8.0-beta.5.tgz#4f70193a86bd1e41b25b0aa5aa32f6d41d52f7c6" @@ -7933,11 +7925,6 @@ zksync-ethers@5.8.0-beta.5: dependencies: ethers "~5.7.0" -zksync-ethers@^5.0.0: - version "5.7.2" - resolved "https://registry.yarnpkg.com/zksync-ethers/-/zksync-ethers-5.7.2.tgz#e965a9926e6f8168963ab565dd6ad0d38c4f7f18" - integrity sha512-D+wn4nkGixUOek9ZsVvIZ/MHponQ5xvw74FSbDJDv6SLCI4LZALOAc8lF3b1ml8nOkpeE2pGV0VKmHTSquRNJg== -======= zksync-ethers@^5.0.0: version "5.8.0" resolved "https://registry.yarnpkg.com/zksync-ethers/-/zksync-ethers-5.8.0.tgz#ff054345048f851c33cb6efcf2094f40d4da6063" @@ -7949,6 +7936,5 @@ zksync-ethers@^5.9.0: version "5.9.0" resolved "https://registry.yarnpkg.com/zksync-ethers/-/zksync-ethers-5.9.0.tgz#96dc29e4eaaf0aa70d927886fd6e1e4c545786e3" integrity sha512-VnRUesrBcPBmiTYTAp+WreIazK2qCIJEHE7j8BiK+cDApHzjAfIXX+x8SXXJpG1npGJANxiJKnPwA5wjGZtCRg== ->>>>>>> 874bc6ba940de9d37b474d1e3dda2fe4e869dfbe dependencies: ethers "~5.7.0" From eb3583a28067a15e70c7076e3feb5686688f78bf Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Wed, 11 Sep 2024 17:50:23 +0200 Subject: [PATCH 02/20] delete unneeded files --- .../contracts/bridge/L1AssetRouter.sol | 1030 ----------------- .../contracts/bridge/L1NativeTokenVault.sol | 259 ----- .../state-transition/libraries/Merkle.sol | 54 - .../StateTransitionManager/FreezeChain.t.sol | 26 - .../Getters/PriorityQueueFrontOperation.t.sol | 30 - .../libraries/Merkle/Merkle.t.sol | 67 -- .../contracts/bridge/L2AssetRouter.sol | 198 ---- .../contracts/bridge/L2NativeTokenVault.sol | 234 ---- .../dev-contracts/DevL2SharedBridge.sol | 33 - .../deploy-shared-bridge-on-l2-through-l1.ts | 154 --- l2-contracts/test/erc20.test.ts | 169 --- 11 files changed, 2254 deletions(-) delete mode 100644 l1-contracts/contracts/bridge/L1AssetRouter.sol delete mode 100644 l1-contracts/contracts/bridge/L1NativeTokenVault.sol delete mode 100644 l1-contracts/contracts/state-transition/libraries/Merkle.sol delete mode 100644 l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/FreezeChain.t.sol delete mode 100644 l1-contracts/test/foundry/unit/concrete/state-transition/chain-deps/facets/Getters/PriorityQueueFrontOperation.t.sol delete mode 100644 l1-contracts/test/foundry/unit/concrete/state-transition/libraries/Merkle/Merkle.t.sol delete mode 100644 l2-contracts/contracts/bridge/L2AssetRouter.sol delete mode 100644 l2-contracts/contracts/bridge/L2NativeTokenVault.sol delete mode 100644 l2-contracts/contracts/dev-contracts/DevL2SharedBridge.sol delete mode 100644 l2-contracts/src/deploy-shared-bridge-on-l2-through-l1.ts delete mode 100644 l2-contracts/test/erc20.test.ts diff --git a/l1-contracts/contracts/bridge/L1AssetRouter.sol b/l1-contracts/contracts/bridge/L1AssetRouter.sol deleted file mode 100644 index 9a4a16bce..000000000 --- a/l1-contracts/contracts/bridge/L1AssetRouter.sol +++ /dev/null @@ -1,1030 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity 0.8.24; - -// solhint-disable reason-string, gas-custom-errors - -import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable-v4/access/Ownable2StepUpgradeable.sol"; -import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable-v4/security/PausableUpgradeable.sol"; - -import {IERC20} from "@openzeppelin/contracts-v4/token/ERC20/IERC20.sol"; -import {SafeERC20} from "@openzeppelin/contracts-v4/token/ERC20/utils/SafeERC20.sol"; - -import {IL1ERC20Bridge} from "./interfaces/IL1ERC20Bridge.sol"; -import {IL1AssetRouter} from "./interfaces/IL1AssetRouter.sol"; -import {IL2Bridge} from "./interfaces/IL2Bridge.sol"; -import {IL2BridgeLegacy} from "./interfaces/IL2BridgeLegacy.sol"; -import {IL1AssetHandler} from "./interfaces/IL1AssetHandler.sol"; -import {IL1NativeTokenVault} from "./interfaces/IL1NativeTokenVault.sol"; - -import {IMailbox} from "../state-transition/chain-interfaces/IMailbox.sol"; -import {L2Message, TxStatus} from "../common/Messaging.sol"; -import {UnsafeBytes} from "../common/libraries/UnsafeBytes.sol"; -import {ReentrancyGuard} from "../common/ReentrancyGuard.sol"; -import {DataEncoding} from "../common/libraries/DataEncoding.sol"; -import {AddressAliasHelper} from "../vendor/AddressAliasHelper.sol"; -import {TWO_BRIDGES_MAGIC_VALUE, ETH_TOKEN_ADDRESS} from "../common/Config.sol"; -import {L2_NATIVE_TOKEN_VAULT_ADDRESS} from "../common/L2ContractAddresses.sol"; - -import {IBridgehub, L2TransactionRequestTwoBridgesInner, L2TransactionRequestDirect} from "../bridgehub/IBridgehub.sol"; -import {L2_BASE_TOKEN_SYSTEM_CONTRACT_ADDR, L2_ASSET_ROUTER_ADDR} from "../common/L2ContractAddresses.sol"; - -import {BridgeHelper} from "./BridgeHelper.sol"; - -/// @author Matter Labs -/// @custom:security-contact security@matterlabs.dev -/// @dev Bridges assets between L1 and ZK chain, supporting both ETH and ERC20 tokens. -/// @dev Designed for use with a proxy for upgradability. -contract L1AssetRouter is IL1AssetRouter, ReentrancyGuard, Ownable2StepUpgradeable, PausableUpgradeable { - using SafeERC20 for IERC20; - - /// @dev The address of the WETH token on L1. - address public immutable override L1_WETH_TOKEN; - - /// @dev Bridgehub smart contract that is used to operate with L2 via asynchronous L2 <-> L1 communication. - IBridgehub public immutable override BRIDGE_HUB; - - /// @dev Era's chainID - uint256 internal immutable ERA_CHAIN_ID; - - /// @dev The address of ZKsync Era diamond proxy contract. - address internal immutable ERA_DIAMOND_PROXY; - - /// @dev Stores the first batch number on the ZKsync Era Diamond Proxy that was settled after Diamond proxy upgrade. - /// This variable is used to differentiate between pre-upgrade and post-upgrade Eth withdrawals. Withdrawals from batches older - /// than this value are considered to have been finalized prior to the upgrade and handled separately. - uint256 internal eraPostDiamondUpgradeFirstBatch; - - /// @dev Stores the first batch number on the ZKsync Era Diamond Proxy that was settled after L1ERC20 Bridge upgrade. - /// This variable is used to differentiate between pre-upgrade and post-upgrade ERC20 withdrawals. Withdrawals from batches older - /// than this value are considered to have been finalized prior to the upgrade and handled separately. - uint256 internal eraPostLegacyBridgeUpgradeFirstBatch; - - /// @dev Stores the ZKsync Era batch number that processes the last deposit tx initiated by the legacy bridge - /// This variable (together with eraLegacyBridgeLastDepositTxNumber) is used to differentiate between pre-upgrade and post-upgrade deposits. Deposits processed in older batches - /// than this value are considered to have been processed prior to the upgrade and handled separately. - /// We use this both for Eth and erc20 token deposits, so we need to update the diamond and bridge simultaneously. - uint256 internal eraLegacyBridgeLastDepositBatch; - - /// @dev The tx number in the _eraLegacyBridgeLastDepositBatch of the last deposit tx initiated by the legacy bridge. - /// This variable (together with eraLegacyBridgeLastDepositBatch) is used to differentiate between pre-upgrade and post-upgrade deposits. Deposits processed in older txs - /// than this value are considered to have been processed prior to the upgrade and handled separately. - /// We use this both for Eth and erc20 token deposits, so we need to update the diamond and bridge simultaneously. - uint256 internal eraLegacyBridgeLastDepositTxNumber; - - /// @dev Legacy bridge smart contract that used to hold ERC20 tokens. - IL1ERC20Bridge public override legacyBridge; - - /// @dev A mapping chainId => bridgeProxy. Used to store the bridge proxy's address, and to see if it has been deployed yet. - mapping(uint256 chainId => address l2Bridge) public __DEPRECATED_l2BridgeAddress; - - /// @dev A mapping chainId => L2 deposit transaction hash => dataHash - // keccak256(abi.encode(account, tokenAddress, amount)) for legacy transfers - // keccak256(abi.encode(_prevMsgSender, assetId, transferData)) for new transfers - /// @dev Tracks deposit transactions to L2 to enable users to claim their funds if a deposit fails. - mapping(uint256 chainId => mapping(bytes32 l2DepositTxHash => bytes32 depositDataHash)) - public - override depositHappened; - - /// @dev Tracks the processing status of L2 to L1 messages, indicating whether a message has already been finalized. - mapping(uint256 chainId => mapping(uint256 l2BatchNumber => mapping(uint256 l2ToL1MessageNumber => bool isFinalized))) - public isWithdrawalFinalized; - - /// @notice Deprecated. Kept for backwards compatibility. - /// @dev Indicates whether the hyperbridging is enabled for a given chain. - // slither-disable-next-line uninitialized-state - mapping(uint256 chainId => bool enabled) public hyperbridgingEnabled; - - /// @dev Maps token balances for each chain to prevent unauthorized spending across ZK chain. - /// This serves as a security measure until hyperbridging is implemented. - /// NOTE: this function may be removed in the future, don't rely on it! - mapping(uint256 chainId => mapping(address l1Token => uint256 balance)) public chainBalance; - - /// @dev Maps asset ID to address of corresponding asset handler. - /// @dev Tracks the address of Asset Handler contracts, where bridged funds are locked for each asset. - /// @dev P.S. this liquidity was locked directly in SharedBridge before. - mapping(bytes32 assetId => address assetHandlerAddress) public assetHandlerAddress; - - /// @dev Maps asset ID to the asset deployment tracker address. - /// @dev Tracks the address of Deployment Tracker contract on L1, which sets Asset Handlers on L2s (ZK chain). - /// @dev For the asset and stores respective addresses. - mapping(bytes32 assetId => address assetDeploymentTracker) public assetDeploymentTracker; - - /// @dev Address of native token vault. - IL1NativeTokenVault public nativeTokenVault; - - /// @notice Checks that the message sender is the bridgehub. - modifier onlyBridgehub() { - require(msg.sender == address(BRIDGE_HUB), "L1AR: not BH"); - _; - } - - /// @notice Checks that the message sender is the bridgehub or zkSync Era Diamond Proxy. - modifier onlyBridgehubOrEra(uint256 _chainId) { - require( - msg.sender == address(BRIDGE_HUB) || (_chainId == ERA_CHAIN_ID && msg.sender == ERA_DIAMOND_PROXY), - "L1AR: msg.sender not equal to bridgehub or era chain" - ); - _; - } - - /// @notice Checks that the message sender is the legacy bridge. - modifier onlyLegacyBridge() { - require(msg.sender == address(legacyBridge), "L1AR: not legacy bridge"); - _; - } - - /// @dev Contract is expected to be used as proxy implementation. - /// @dev Initialize the implementation to prevent Parity hack. - constructor( - address _l1WethAddress, - IBridgehub _bridgehub, - uint256 _eraChainId, - address _eraDiamondProxy - ) reentrancyGuardInitializer { - _disableInitializers(); - L1_WETH_TOKEN = _l1WethAddress; - BRIDGE_HUB = _bridgehub; - ERA_CHAIN_ID = _eraChainId; - ERA_DIAMOND_PROXY = _eraDiamondProxy; - } - - /// @dev Initializes a contract bridge for later use. Expected to be used in the proxy. - /// @dev Used for testing purposes only, as the contract has been initialized on mainnet. - /// @param _owner The address which can change L2 token implementation and upgrade the bridge implementation. - /// The owner is the Governor and separate from the ProxyAdmin from now on, so that the Governor can call the bridge. - /// @param _eraPostDiamondUpgradeFirstBatch The first batch number on the zkSync Era Diamond Proxy that was settled after diamond proxy upgrade. - /// @param _eraPostLegacyBridgeUpgradeFirstBatch The first batch number on the zkSync Era Diamond Proxy that was settled after legacy bridge upgrade. - /// @param _eraLegacyBridgeLastDepositBatch The the zkSync Era batch number that processes the last deposit tx initiated by the legacy bridge. - /// @param _eraLegacyBridgeLastDepositTxNumber The tx number in the _eraLegacyBridgeLastDepositBatch of the last deposit tx initiated by the legacy bridge. - function initialize( - address _owner, - uint256 _eraPostDiamondUpgradeFirstBatch, - uint256 _eraPostLegacyBridgeUpgradeFirstBatch, - uint256 _eraLegacyBridgeLastDepositBatch, - uint256 _eraLegacyBridgeLastDepositTxNumber - ) external reentrancyGuardInitializer initializer { - require(_owner != address(0), "L1AR: owner 0"); - _transferOwnership(_owner); - if (eraPostDiamondUpgradeFirstBatch == 0) { - eraPostDiamondUpgradeFirstBatch = _eraPostDiamondUpgradeFirstBatch; - eraPostLegacyBridgeUpgradeFirstBatch = _eraPostLegacyBridgeUpgradeFirstBatch; - eraLegacyBridgeLastDepositBatch = _eraLegacyBridgeLastDepositBatch; - eraLegacyBridgeLastDepositTxNumber = _eraLegacyBridgeLastDepositTxNumber; - } - } - - /// @notice Transfers tokens from shared bridge to native token vault. - /// @dev This function is part of the upgrade process used to transfer liquidity. - /// @param _token The address of the token to be transferred to NTV. - function transferTokenToNTV(address _token) external { - address ntvAddress = address(nativeTokenVault); - require(msg.sender == ntvAddress, "L1AR: not NTV"); - if (ETH_TOKEN_ADDRESS == _token) { - uint256 amount = address(this).balance; - bool callSuccess; - // Low-level assembly call, to avoid any memory copying (save gas) - assembly { - callSuccess := call(gas(), ntvAddress, amount, 0, 0, 0, 0) - } - require(callSuccess, "L1AR: eth transfer failed"); - } else { - IERC20(_token).safeTransfer(ntvAddress, IERC20(_token).balanceOf(address(this))); - } - } - - /// @notice Clears chain balance for specific token. - /// @dev This function is part of the upgrade process used to nullify chain balances once they are credited to NTV. - /// @param _chainId The ID of the ZK chain. - /// @param _token The address of the token which was previously deposit to shared bridge. - function nullifyChainBalanceByNTV(uint256 _chainId, address _token) external { - require(msg.sender == address(nativeTokenVault), "L1AR: not NTV"); - chainBalance[_chainId][_token] = 0; - } - - /// @notice Sets the L1ERC20Bridge contract address. - /// @dev Should be called only once by the owner. - /// @param _legacyBridge The address of the legacy bridge. - function setL1Erc20Bridge(address _legacyBridge) external onlyOwner { - require(address(legacyBridge) == address(0), "L1AR: legacy bridge already set"); - require(_legacyBridge != address(0), "L1AR: legacy bridge 0"); - legacyBridge = IL1ERC20Bridge(_legacyBridge); - } - - /// @notice Sets the nativeTokenVault contract address. - /// @dev Should be called only once by the owner. - /// @param _nativeTokenVault The address of the native token vault. - function setNativeTokenVault(IL1NativeTokenVault _nativeTokenVault) external onlyOwner { - require(address(nativeTokenVault) == address(0), "L1AR: native token vault already set"); - require(address(_nativeTokenVault) != address(0), "L1AR: native token vault 0"); - nativeTokenVault = _nativeTokenVault; - } - - /// @notice Used to set the assed deployment tracker address for given asset data. - /// @param _assetRegistrationData The asset data which may include the asset address and any additional required data or encodings. - /// @param _assetDeploymentTracker The whitelisted address of asset deployment tracker for provided asset. - function setAssetDeploymentTracker( - bytes32 _assetRegistrationData, - address _assetDeploymentTracker - ) external onlyOwner { - bytes32 assetId = keccak256( - abi.encode(uint256(block.chainid), _assetDeploymentTracker, _assetRegistrationData) - ); - assetDeploymentTracker[assetId] = _assetDeploymentTracker; - emit AssetDeploymentTrackerSet(assetId, _assetDeploymentTracker, _assetRegistrationData); - } - - /// @notice Sets the asset handler address for a specified asset ID on the chain of the asset deployment tracker. - /// @dev The caller of this function is encoded within the `assetId`, therefore, it should be invoked by the asset deployment tracker contract. - /// @dev Typically, for most tokens, ADT is the native token vault. However, custom tokens may have their own specific asset deployment trackers. - /// @dev `setAssetHandlerAddressOnCounterPart` should be called on L1 to set asset handlers on L2 chains for a specific asset ID. - /// @param _assetRegistrationData The asset data which may include the asset address and any additional required data or encodings. - /// @param _assetHandlerAddress The address of the asset handler to be set for the provided asset. - function setAssetHandlerAddressInitial(bytes32 _assetRegistrationData, address _assetHandlerAddress) external { - bool senderIsNTV = msg.sender == address(nativeTokenVault); - address sender = senderIsNTV ? L2_NATIVE_TOKEN_VAULT_ADDRESS : msg.sender; - bytes32 assetId = DataEncoding.encodeAssetId(block.chainid, _assetRegistrationData, sender); - require(senderIsNTV || msg.sender == assetDeploymentTracker[assetId], "ShB: not NTV or ADT"); - assetHandlerAddress[assetId] = _assetHandlerAddress; - if (senderIsNTV) { - assetDeploymentTracker[assetId] = msg.sender; - } - emit AssetHandlerRegisteredInitial(assetId, _assetHandlerAddress, _assetRegistrationData, sender); - } - - /// @notice Used to set the asset handler address for a given asset ID on a remote ZK chain - /// @dev No access control on the caller, as msg.sender is encoded in the assetId. - /// @param _chainId The ZK chain ID. - /// @param _mintValue The value withdrawn by base token bridge to cover for l2 gas and l2 msg.value costs. - /// @param _l2TxGasLimit The L2 gas limit to be used in the corresponding L2 transaction. - /// @param _l2TxGasPerPubdataByte The gasPerPubdataByteLimit to be used in the corresponding L2 transaction. - /// @param _refundRecipient The address on L2 that will receive the refund for the transaction. - /// @param _assetId The encoding of asset ID. - /// @param _assetHandlerAddressOnCounterPart The address of the asset handler, which will hold the token of interest. - /// @return txHash The L2 transaction hash of setting asset handler on remote chain. - function setAssetHandlerAddressOnCounterPart( - uint256 _chainId, - uint256 _mintValue, - uint256 _l2TxGasLimit, - uint256 _l2TxGasPerPubdataByte, - address _refundRecipient, - bytes32 _assetId, - address _assetHandlerAddressOnCounterPart - ) external payable returns (bytes32 txHash) { - require(msg.sender == assetDeploymentTracker[_assetId] || msg.sender == owner(), "L1AR: only ADT or owner"); - - bytes memory l2Calldata = abi.encodeCall( - IL2Bridge.setAssetHandlerAddress, - (_assetId, _assetHandlerAddressOnCounterPart) - ); - - L2TransactionRequestDirect memory request = L2TransactionRequestDirect({ - chainId: _chainId, - l2Contract: L2_ASSET_ROUTER_ADDR, - mintValue: _mintValue, // l2 gas + l2 msg.value the bridgehub will withdraw the mintValue from the base token bridge for gas - l2Value: 0, // For base token deposits, there is no msg.value during the call, as the base token is minted to the recipient address - l2Calldata: l2Calldata, - l2GasLimit: _l2TxGasLimit, - l2GasPerPubdataByteLimit: _l2TxGasPerPubdataByte, - factoryDeps: new bytes[](0), - refundRecipient: _refundRecipient - }); - txHash = BRIDGE_HUB.requestL2TransactionDirect{value: msg.value}(request); - } - - /// @notice Allows bridgehub to acquire mintValue for L1->L2 transactions. - /// @dev If the corresponding L2 transaction fails, refunds are issued to a refund recipient on L2. - /// @param _chainId The chain ID of the ZK chain to which deposit. - /// @param _assetId The deposited asset ID. - /// @param _prevMsgSender The `msg.sender` address from the external call that initiated current one. - /// @param _amount The total amount of tokens to be bridged. - function bridgehubDepositBaseToken( - uint256 _chainId, - bytes32 _assetId, - address _prevMsgSender, - uint256 _amount - ) external payable onlyBridgehubOrEra(_chainId) whenNotPaused { - address l1AssetHandler = assetHandlerAddress[_assetId]; - require(l1AssetHandler != address(0), "ShB: asset handler not set"); - - _transferAllowanceToNTV(_assetId, _amount, _prevMsgSender); - // slither-disable-next-line unused-return - IL1AssetHandler(l1AssetHandler).bridgeBurn{value: msg.value}({ - _chainId: _chainId, - _l2Value: 0, - _assetId: _assetId, - _prevMsgSender: _prevMsgSender, - _data: abi.encode(_amount, address(0)) - }); - - // Note that we don't save the deposited amount, as this is for the base token, which gets sent to the refundRecipient if the tx fails - emit BridgehubDepositBaseTokenInitiated(_chainId, _prevMsgSender, _assetId, _amount); - } - - /// @notice Initiates a deposit transaction within Bridgehub, used by `requestL2TransactionTwoBridges`. - /// @param _chainId The chain ID of the ZK chain to which deposit. - /// @param _prevMsgSender The `msg.sender` address from the external call that initiated current one. - /// @param _l2Value The L2 `msg.value` from the L1 -> L2 deposit transaction. - /// @param _data The calldata for the second bridge deposit. - /// @return request The data used by the bridgehub to create L2 transaction request to specific ZK chain. - function bridgehubDeposit( - uint256 _chainId, - address _prevMsgSender, - uint256 _l2Value, - bytes calldata _data - ) - external - payable - override - onlyBridgehub - whenNotPaused - returns (L2TransactionRequestTwoBridgesInner memory request) - { - bytes32 assetId; - bytes memory transferData; - bool legacyDeposit = false; - bytes1 encodingVersion = _data[0]; - - // The new encoding ensures that the calldata is collision-resistant with respect to the legacy format. - // In the legacy calldata, the first input was the address, meaning the most significant byte was always `0x00`. - if (encodingVersion == 0x01) { - (assetId, transferData) = abi.decode(_data[1:], (bytes32, bytes)); - require( - assetHandlerAddress[assetId] != address(nativeTokenVault), - "ShB: new encoding format not yet supported for NTV" - ); - } else { - (assetId, transferData) = _handleLegacyData(_data, _prevMsgSender); - legacyDeposit = true; - } - - require(BRIDGE_HUB.baseTokenAssetId(_chainId) != assetId, "L1AR: baseToken deposit not supported"); - - bytes memory bridgeMintCalldata = _burn({ - _chainId: _chainId, - _l2Value: _l2Value, - _assetId: assetId, - _prevMsgSender: _prevMsgSender, - _transferData: transferData, - _passValue: true - }); - bytes32 txDataHash = this.encodeTxDataHash(legacyDeposit, _prevMsgSender, assetId, transferData); - - request = _requestToBridge({ - _prevMsgSender: _prevMsgSender, - _assetId: assetId, - _bridgeMintCalldata: bridgeMintCalldata, - _txDataHash: txDataHash - }); - - emit BridgehubDepositInitiated({ - chainId: _chainId, - txDataHash: txDataHash, - from: _prevMsgSender, - assetId: assetId, - bridgeMintCalldata: bridgeMintCalldata - }); - } - - /// @notice Confirms the acceptance of a transaction by the Mailbox, as part of the L2 transaction process within Bridgehub. - /// This function is utilized by `requestL2TransactionTwoBridges` to validate the execution of a transaction. - /// @param _chainId The chain ID of the ZK chain to which confirm the deposit. - /// @param _txDataHash The keccak256 hash of 0x01 || abi.encode(bytes32, bytes) to identify deposits. - /// @param _txHash The hash of the L1->L2 transaction to confirm the deposit. - function bridgehubConfirmL2Transaction( - uint256 _chainId, - bytes32 _txDataHash, - bytes32 _txHash - ) external override onlyBridgehub whenNotPaused { - require(depositHappened[_chainId][_txHash] == 0x00, "L1AR: tx hap"); - depositHappened[_chainId][_txHash] = _txDataHash; - emit BridgehubDepositFinalized(_chainId, _txDataHash, _txHash); - } - - /// @notice Finalize the withdrawal and release funds - /// @param _chainId The chain ID of the transaction to check - /// @param _l2BatchNumber The L2 batch number where the withdrawal was processed - /// @param _l2MessageIndex The position in the L2 logs Merkle tree of the l2Log that was sent with the message - /// @param _l2TxNumberInBatch The L2 transaction number in the batch, in which the log was sent - /// @param _message The L2 withdraw data, stored in an L2 -> L1 message - /// @param _merkleProof The Merkle proof of the inclusion L2 -> L1 message about withdrawal initialization - function finalizeWithdrawal( - uint256 _chainId, - uint256 _l2BatchNumber, - uint256 _l2MessageIndex, - uint16 _l2TxNumberInBatch, - bytes calldata _message, - bytes32[] calldata _merkleProof - ) external override { - _finalizeWithdrawal({ - _chainId: _chainId, - _l2BatchNumber: _l2BatchNumber, - _l2MessageIndex: _l2MessageIndex, - _l2TxNumberInBatch: _l2TxNumberInBatch, - _message: _message, - _merkleProof: _merkleProof - }); - } - - /// @dev Calls the internal `_encodeTxDataHash`. Used as a wrapped for try / catch case. - /// @param _isLegacyEncoding Boolean flag indicating whether to use the legacy encoding standard (true) or the latest encoding standard (false). - /// @param _prevMsgSender The address of the entity that initiated the deposit. - /// @param _assetId The unique identifier of the deposited L1 token. - /// @param _transferData The encoded transfer data, which includes both the deposit amount and the address of the L2 receiver. - /// @return txDataHash The resulting encoded transaction data hash. - function encodeTxDataHash( - bool _isLegacyEncoding, - address _prevMsgSender, - bytes32 _assetId, - bytes calldata _transferData - ) external view returns (bytes32 txDataHash) { - return _encodeTxDataHash(_isLegacyEncoding, _prevMsgSender, _assetId, _transferData); - } - - /// @dev Withdraw funds from the initiated deposit, that failed when finalizing on L2. - /// @param _chainId The ZK chain id to which deposit was initiated. - /// @param _depositSender The address of the entity that initiated the deposit. - /// @param _assetId The unique identifier of the deposited L1 token. - /// @param _assetData The encoded transfer data, which includes both the deposit amount and the address of the L2 receiver. Might include extra information. - /// @param _l2TxHash The L2 transaction hash of the failed deposit finalization. - /// @param _l2BatchNumber The L2 batch number where the deposit finalization was processed. - /// @param _l2MessageIndex The position in the L2 logs Merkle tree of the l2Log that was sent with the message. - /// @param _l2TxNumberInBatch The L2 transaction number in a batch, in which the log was sent. - /// @param _merkleProof The Merkle proof of the processing L1 -> L2 transaction with deposit finalization. - /// @dev Processes claims of failed deposit, whether they originated from the legacy bridge or the current system. - function bridgeRecoverFailedTransfer( - uint256 _chainId, - address _depositSender, - bytes32 _assetId, - bytes memory _assetData, - bytes32 _l2TxHash, - uint256 _l2BatchNumber, - uint256 _l2MessageIndex, - uint16 _l2TxNumberInBatch, - bytes32[] calldata _merkleProof - ) public nonReentrant whenNotPaused { - { - bool proofValid = BRIDGE_HUB.proveL1ToL2TransactionStatus({ - _chainId: _chainId, - _l2TxHash: _l2TxHash, - _l2BatchNumber: _l2BatchNumber, - _l2MessageIndex: _l2MessageIndex, - _l2TxNumberInBatch: _l2TxNumberInBatch, - _merkleProof: _merkleProof, - _status: TxStatus.Failure - }); - require(proofValid, "yn"); - } - - require(!_isEraLegacyDeposit(_chainId, _l2BatchNumber, _l2TxNumberInBatch), "L1AR: legacy cFD"); - { - bytes32 dataHash = depositHappened[_chainId][_l2TxHash]; - // Determine if the given dataHash matches the calculated legacy transaction hash. - bool isLegacyTxDataHash = _isLegacyTxDataHash(_depositSender, _assetId, _assetData, dataHash); - // If the dataHash matches the legacy transaction hash, skip the next step. - // Otherwise, perform the check using the new transaction data hash encoding. - if (!isLegacyTxDataHash) { - bytes32 txDataHash = _encodeTxDataHash(false, _depositSender, _assetId, _assetData); - require(dataHash == txDataHash, "L1AR: d.it not hap"); - } - } - delete depositHappened[_chainId][_l2TxHash]; - - IL1AssetHandler(assetHandlerAddress[_assetId]).bridgeRecoverFailedTransfer( - _chainId, - _assetId, - _depositSender, - _assetData - ); - - emit ClaimedFailedDepositSharedBridge(_chainId, _depositSender, _assetId, _assetData); - } - - /// @dev Receives and parses (name, symbol, decimals) from the token contract - function getERC20Getters(address _token) public view returns (bytes memory) { - return BridgeHelper.getERC20Getters(_token, ETH_TOKEN_ADDRESS); - } - - /// @dev send the burn message to the asset - /// @notice Forwards the burn request for specific asset to respective asset handler - /// @param _chainId The chain ID of the ZK chain to which deposit. - /// @param _l2Value The L2 `msg.value` from the L1 -> L2 deposit transaction. - /// @param _assetId The deposited asset ID. - /// @param _prevMsgSender The `msg.sender` address from the external call that initiated current one. - /// @param _transferData The encoded data, which is used by the asset handler to determine L2 recipient and amount. Might include extra information. - /// @param _passValue Boolean indicating whether to pass msg.value in the call. - /// @return bridgeMintCalldata The calldata used by remote asset handler to mint tokens for recipient. - function _burn( - uint256 _chainId, - uint256 _l2Value, - bytes32 _assetId, - address _prevMsgSender, - bytes memory _transferData, - bool _passValue - ) internal returns (bytes memory bridgeMintCalldata) { - address l1AssetHandler = assetHandlerAddress[_assetId]; - require(l1AssetHandler != address(0), "ShB: asset handler does not exist for assetId"); - - uint256 msgValue = _passValue ? msg.value : 0; - bridgeMintCalldata = IL1AssetHandler(l1AssetHandler).bridgeBurn{value: msgValue}({ - _chainId: _chainId, - _l2Value: _l2Value, - _assetId: _assetId, - _prevMsgSender: _prevMsgSender, - _data: _transferData - }); - } - - struct MessageParams { - uint256 l2BatchNumber; - uint256 l2MessageIndex; - uint16 l2TxNumberInBatch; - } - - /// @notice Internal function that handles the logic for finalizing withdrawals, supporting both the current bridge system and the legacy ERC20 bridge. - /// @param _chainId The chain ID of the transaction to check. - /// @param _l2BatchNumber The L2 batch number where the withdrawal was processed. - /// @param _l2MessageIndex The position in the L2 logs Merkle tree of the l2Log that was sent with the message. - /// @param _l2TxNumberInBatch The L2 transaction number in the batch, in which the log was sent. - /// @param _message The L2 withdraw data, stored in an L2 -> L1 message. - /// @param _merkleProof The Merkle proof of the inclusion L2 -> L1 message about withdrawal initialization. - /// @return l1Receiver The address to receive bridged assets. - /// @return assetId The bridged asset ID. - /// @return amount The amount of asset bridged. - function _finalizeWithdrawal( - uint256 _chainId, - uint256 _l2BatchNumber, - uint256 _l2MessageIndex, - uint16 _l2TxNumberInBatch, - bytes calldata _message, - bytes32[] calldata _merkleProof - ) internal nonReentrant whenNotPaused returns (address l1Receiver, bytes32 assetId, uint256 amount) { - require( - !isWithdrawalFinalized[_chainId][_l2BatchNumber][_l2MessageIndex], - "L1AR: Withdrawal is already finalized" - ); - isWithdrawalFinalized[_chainId][_l2BatchNumber][_l2MessageIndex] = true; - - // Handling special case for withdrawal from ZKsync Era initiated before Shared Bridge. - require(!_isEraLegacyEthWithdrawal(_chainId, _l2BatchNumber), "L1AR: legacy eth withdrawal"); - require(!_isEraLegacyTokenWithdrawal(_chainId, _l2BatchNumber), "L1AR: legacy token withdrawal"); - - bytes memory transferData; - { - MessageParams memory messageParams = MessageParams({ - l2BatchNumber: _l2BatchNumber, - l2MessageIndex: _l2MessageIndex, - l2TxNumberInBatch: _l2TxNumberInBatch - }); - (assetId, transferData) = _checkWithdrawal(_chainId, messageParams, _message, _merkleProof); - } - address l1AssetHandler = assetHandlerAddress[assetId]; - // slither-disable-next-line unused-return - IL1AssetHandler(l1AssetHandler).bridgeMint(_chainId, assetId, transferData); - (amount, l1Receiver) = abi.decode(transferData, (uint256, address)); - - emit WithdrawalFinalizedSharedBridge(_chainId, l1Receiver, assetId, amount); - } - - /// @notice Decodes the transfer input for legacy data and transfers allowance to NTV - /// @dev Is not applicable for custom asset handlers - /// @param _data encoded transfer data (address _l1Token, uint256 _depositAmount, address _l2Receiver) - /// @param _prevMsgSender address of the deposit initiator - function _handleLegacyData(bytes calldata _data, address _prevMsgSender) internal returns (bytes32, bytes memory) { - (address _l1Token, uint256 _depositAmount, address _l2Receiver) = abi.decode( - _data, - (address, uint256, address) - ); - bytes32 assetId = _ensureTokenRegisteredWithNTV(_l1Token); - _transferAllowanceToNTV(assetId, _depositAmount, _prevMsgSender); - return (assetId, abi.encode(_depositAmount, _l2Receiver)); - } - - function _ensureTokenRegisteredWithNTV(address _l1Token) internal returns (bytes32 assetId) { - assetId = DataEncoding.encodeNTVAssetId(block.chainid, _l1Token); - if (nativeTokenVault.tokenAddress(assetId) == address(0)) { - nativeTokenVault.registerToken(_l1Token); - } - } - - /// @notice Transfers allowance to Native Token Vault, if the asset is registered with it. Does nothing for ETH or non-registered tokens. - /// @dev assetId is not the padded address, but the correct encoded id (NTV stores respective format for IDs) - function _transferAllowanceToNTV(bytes32 _assetId, uint256 _amount, address _prevMsgSender) internal { - address l1TokenAddress = nativeTokenVault.tokenAddress(_assetId); - if (l1TokenAddress == address(0) || l1TokenAddress == ETH_TOKEN_ADDRESS) { - return; - } - IERC20 l1Token = IERC20(l1TokenAddress); - - // Do the transfer if allowance to Shared bridge is bigger than amount - // And if there is not enough allowance for the NTV - if ( - l1Token.allowance(_prevMsgSender, address(this)) >= _amount && - l1Token.allowance(_prevMsgSender, address(nativeTokenVault)) < _amount - ) { - // slither-disable-next-line arbitrary-send-erc20 - l1Token.safeTransferFrom(_prevMsgSender, address(this), _amount); - l1Token.forceApprove(address(nativeTokenVault), _amount); - } - } - - /// @dev The request data that is passed to the bridgehub - function _requestToBridge( - address _prevMsgSender, - bytes32 _assetId, - bytes memory _bridgeMintCalldata, - bytes32 _txDataHash - ) internal view returns (L2TransactionRequestTwoBridgesInner memory request) { - // Request the finalization of the deposit on the L2 side - bytes memory l2TxCalldata = _getDepositL2Calldata(_prevMsgSender, _assetId, _bridgeMintCalldata); - - request = L2TransactionRequestTwoBridgesInner({ - magicValue: TWO_BRIDGES_MAGIC_VALUE, - l2Contract: L2_ASSET_ROUTER_ADDR, - l2Calldata: l2TxCalldata, - factoryDeps: new bytes[](0), - txDataHash: _txDataHash - }); - } - - /// @dev Generate a calldata for calling the deposit finalization on the L2 bridge contract - function _getDepositL2Calldata( - address _l1Sender, - bytes32 _assetId, - bytes memory _assetData - ) internal view returns (bytes memory) { - // First branch covers the case when asset is not registered with NTV (custom asset handler) - // Second branch handles tokens registered with NTV and uses legacy calldata encoding - if (nativeTokenVault.tokenAddress(_assetId) == address(0)) { - return abi.encodeCall(IL2Bridge.finalizeDeposit, (_assetId, _assetData)); - } else { - // slither-disable-next-line unused-return - (, address _l2Receiver, address _parsedL1Token, uint256 _amount, bytes memory _gettersData) = DataEncoding - .decodeBridgeMintData(_assetData); - return - abi.encodeCall( - IL2BridgeLegacy.finalizeDeposit, - (_l1Sender, _l2Receiver, _parsedL1Token, _amount, _gettersData) - ); - } - } - - /// @dev Determines if an eth withdrawal was initiated on zkSync Era before the upgrade to the Shared Bridge. - /// @param _chainId The chain ID of the transaction to check. - /// @param _l2BatchNumber The L2 batch number for the withdrawal. - /// @return Whether withdrawal was initiated on ZKsync Era before diamond proxy upgrade. - function _isEraLegacyEthWithdrawal(uint256 _chainId, uint256 _l2BatchNumber) internal view returns (bool) { - require((_chainId != ERA_CHAIN_ID) || eraPostDiamondUpgradeFirstBatch != 0, "L1AR: diamondUFB not set for Era"); - return (_chainId == ERA_CHAIN_ID) && (_l2BatchNumber < eraPostDiamondUpgradeFirstBatch); - } - - /// @dev Determines if a token withdrawal was initiated on ZKsync Era before the upgrade to the Shared Bridge. - /// @param _chainId The chain ID of the transaction to check. - /// @param _l2BatchNumber The L2 batch number for the withdrawal. - /// @return Whether withdrawal was initiated on ZKsync Era before Legacy Bridge upgrade. - function _isEraLegacyTokenWithdrawal(uint256 _chainId, uint256 _l2BatchNumber) internal view returns (bool) { - require( - (_chainId != ERA_CHAIN_ID) || eraPostLegacyBridgeUpgradeFirstBatch != 0, - "L1AR: LegacyUFB not set for Era" - ); - return (_chainId == ERA_CHAIN_ID) && (_l2BatchNumber < eraPostLegacyBridgeUpgradeFirstBatch); - } - - /// @dev Determines if the provided data for a failed deposit corresponds to a legacy failed deposit. - /// @param _prevMsgSender The address of the entity that initiated the deposit. - /// @param _assetId The unique identifier of the deposited L1 token. - /// @param _transferData The encoded transfer data, which includes both the deposit amount and the address of the L2 receiver. - /// @param _expectedTxDataHash The nullifier data hash stored for the failed deposit. - /// @return isLegacyTxDataHash True if the transaction is legacy, false otherwise. - function _isLegacyTxDataHash( - address _prevMsgSender, - bytes32 _assetId, - bytes memory _transferData, - bytes32 _expectedTxDataHash - ) internal view returns (bool isLegacyTxDataHash) { - try this.encodeTxDataHash(true, _prevMsgSender, _assetId, _transferData) returns (bytes32 txDataHash) { - return txDataHash == _expectedTxDataHash; - } catch { - return false; - } - } - - /// @dev Encodes the transaction data hash using either the latest encoding standard or the legacy standard. - /// @param _isLegacyEncoding Boolean flag indicating whether to use the legacy encoding standard (true) or the latest encoding standard (false). - /// @param _prevMsgSender The address of the entity that initiated the deposit. - /// @param _assetId The unique identifier of the deposited L1 token. - /// @param _transferData The encoded transfer data, which includes both the deposit amount and the address of the L2 receiver. - /// @return txDataHash The resulting encoded transaction data hash. - function _encodeTxDataHash( - bool _isLegacyEncoding, - address _prevMsgSender, - bytes32 _assetId, - bytes memory _transferData - ) internal view returns (bytes32 txDataHash) { - if (_isLegacyEncoding) { - (uint256 depositAmount, ) = abi.decode(_transferData, (uint256, address)); - txDataHash = keccak256(abi.encode(_prevMsgSender, nativeTokenVault.tokenAddress(_assetId), depositAmount)); - } else { - // Similarly to calldata, the txDataHash is collision-resistant. - // In the legacy data hash, the first encoded variable was the address, which is padded with zeros during `abi.encode`. - txDataHash = keccak256(bytes.concat(bytes1(0x01), abi.encode(_prevMsgSender, _assetId, _transferData))); - } - } - - /// @dev Determines if a deposit was initiated on zkSync Era before the upgrade to the Shared Bridge. - /// @param _chainId The chain ID of the transaction to check. - /// @param _l2BatchNumber The L2 batch number for the deposit where it was processed. - /// @param _l2TxNumberInBatch The L2 transaction number in the batch, in which the deposit was processed. - /// @return Whether deposit was initiated on ZKsync Era before Shared Bridge upgrade. - function _isEraLegacyDeposit( - uint256 _chainId, - uint256 _l2BatchNumber, - uint256 _l2TxNumberInBatch - ) internal view returns (bool) { - require( - (_chainId != ERA_CHAIN_ID) || (eraLegacyBridgeLastDepositBatch != 0), - "L1AR: last deposit time not set for Era" - ); - return - (_chainId == ERA_CHAIN_ID) && - (_l2BatchNumber < eraLegacyBridgeLastDepositBatch || - (_l2TxNumberInBatch <= eraLegacyBridgeLastDepositTxNumber && - _l2BatchNumber == eraLegacyBridgeLastDepositBatch)); - } - - /// @notice Verifies the validity of a withdrawal message from L2 and returns withdrawal details. - /// @param _chainId The chain ID of the transaction to check. - /// @param _messageParams The message params, which include batch number, message index, and L2 tx number in batch. - /// @param _message The L2 withdraw data, stored in an L2 -> L1 message. - /// @param _merkleProof The Merkle proof of the inclusion L2 -> L1 message about withdrawal initialization. - /// @return assetId The ID of the bridged asset. - /// @return transferData The transfer data used to finalize withdawal. - function _checkWithdrawal( - uint256 _chainId, - MessageParams memory _messageParams, - bytes calldata _message, - bytes32[] calldata _merkleProof - ) internal view returns (bytes32 assetId, bytes memory transferData) { - (assetId, transferData) = _parseL2WithdrawalMessage(_chainId, _message); - L2Message memory l2ToL1Message; - { - bool baseTokenWithdrawal = (assetId == BRIDGE_HUB.baseTokenAssetId(_chainId)); - address l2Sender = baseTokenWithdrawal ? L2_BASE_TOKEN_SYSTEM_CONTRACT_ADDR : L2_ASSET_ROUTER_ADDR; - - l2ToL1Message = L2Message({ - txNumberInBatch: _messageParams.l2TxNumberInBatch, - sender: l2Sender, - data: _message - }); - } - - bool success = BRIDGE_HUB.proveL2MessageInclusion({ - _chainId: _chainId, - _batchNumber: _messageParams.l2BatchNumber, - _index: _messageParams.l2MessageIndex, - _message: l2ToL1Message, - _proof: _merkleProof - }); - require(success, "L1AR: withd w proof"); // withdrawal wrong proof - } - - /// @notice Parses the withdrawal message and returns withdrawal details. - /// @dev Currently, 3 different encoding versions are supported: legacy mailbox withdrawal, ERC20 bridge withdrawal, - /// @dev and the latest version supported by shared bridge. Selectors are used for versioning. - /// @param _chainId The ZK chain ID. - /// @param _l2ToL1message The encoded L2 -> L1 message. - /// @return assetId The ID of the bridged asset. - /// @return transferData The transfer data used to finalize withdawal. - function _parseL2WithdrawalMessage( - uint256 _chainId, - bytes memory _l2ToL1message - ) internal view returns (bytes32 assetId, bytes memory transferData) { - // We check that the message is long enough to read the data. - // Please note that there are three versions of the message: - // 1. The message that is sent by `withdraw(address _l1Receiver)` or `withdrawWithMessage`. In the second case, this function ignores the extra data - // It should be equal to the length of the bytes4 function signature + address l1Receiver + uint256 amount = 4 + 20 + 32 = 56 (bytes). - // 2. The legacy `getL1WithdrawMessage`, the length of the data is known. - // 3. The message that is encoded by `getL1WithdrawMessage(bytes32 _assetId, bytes memory _bridgeMintData)` - // No length is assumed. The assetId is decoded and the mintData is passed to respective assetHandler - - (uint32 functionSignature, uint256 offset) = UnsafeBytes.readUint32(_l2ToL1message, 0); - if (bytes4(functionSignature) == IMailbox.finalizeEthWithdrawal.selector) { - uint256 amount; - address l1Receiver; - - // The data is expected to be at least 56 bytes long. - require(_l2ToL1message.length >= 56, "L1AR: wrong msg len"); // wrong message length - // this message is a base token withdrawal - (l1Receiver, offset) = UnsafeBytes.readAddress(_l2ToL1message, offset); - // slither-disable-next-line unused-return - (amount, ) = UnsafeBytes.readUint256(_l2ToL1message, offset); - assetId = BRIDGE_HUB.baseTokenAssetId(_chainId); - transferData = abi.encode(amount, l1Receiver); - } else if (bytes4(functionSignature) == IL1ERC20Bridge.finalizeWithdrawal.selector) { - address l1Token; - uint256 amount; - address l1Receiver; - // We use the IL1ERC20Bridge for backward compatibility with old withdrawals. - // This message is a token withdrawal - - // Check that the message length is correct. - // It should be equal to the length of the function signature + address + address + uint256 = 4 + 20 + 20 + 32 = - // 76 (bytes). - require(_l2ToL1message.length == 76, "L1AR: wrong msg len 2"); - (l1Receiver, offset) = UnsafeBytes.readAddress(_l2ToL1message, offset); - (l1Token, offset) = UnsafeBytes.readAddress(_l2ToL1message, offset); - // slither-disable-next-line unused-return - (amount, ) = UnsafeBytes.readUint256(_l2ToL1message, offset); - - assetId = DataEncoding.encodeNTVAssetId(block.chainid, l1Token); - transferData = abi.encode(amount, l1Receiver); - } else if (bytes4(functionSignature) == this.finalizeWithdrawal.selector) { - // The data is expected to be at least 36 bytes long to contain assetId. - require(_l2ToL1message.length >= 36, "L1AR: wrong msg len"); // wrong message length - (assetId, offset) = UnsafeBytes.readBytes32(_l2ToL1message, offset); - transferData = UnsafeBytes.readRemainingBytes(_l2ToL1message, offset); - } else { - revert("L1AR: Incorrect message function selector"); - } - } - - /*////////////////////////////////////////////////////////////// - SHARED BRIDGE TOKEN BRIDGING LEGACY FUNCTIONS - //////////////////////////////////////////////////////////////*/ - - /// @notice Withdraw funds from the initiated deposit, that failed when finalizing on L2. - /// @dev Cannot be used to claim deposits made with new encoding. - /// @param _chainId The ZK chain id to which deposit was initiated. - /// @param _depositSender The address of the deposit initiator. - /// @param _l1Asset The address of the deposited L1 ERC20 token. - /// @param _amount The amount of the deposit that failed. - /// @param _l2TxHash The L2 transaction hash of the failed deposit finalization. - /// @param _l2BatchNumber The L2 batch number where the deposit finalization was processed. - /// @param _l2MessageIndex The position in the L2 logs Merkle tree of the l2Log that was sent with the message. - /// @param _l2TxNumberInBatch The L2 transaction number in a batch, in which the log was sent. - /// @param _merkleProof The Merkle proof of the processing L1 -> L2 transaction with deposit finalization. - function claimFailedDeposit( - uint256 _chainId, - address _depositSender, - address _l1Asset, - uint256 _amount, - bytes32 _l2TxHash, - uint256 _l2BatchNumber, - uint256 _l2MessageIndex, - uint16 _l2TxNumberInBatch, - bytes32[] calldata _merkleProof - ) external override { - bytes32 assetId = DataEncoding.encodeNTVAssetId(block.chainid, _l1Asset); - // For legacy deposits, the l2 receiver is not required to check tx data hash - bytes memory transferData = abi.encode(_amount, address(0)); - bridgeRecoverFailedTransfer({ - _chainId: _chainId, - _depositSender: _depositSender, - _assetId: assetId, - _assetData: transferData, - _l2TxHash: _l2TxHash, - _l2BatchNumber: _l2BatchNumber, - _l2MessageIndex: _l2MessageIndex, - _l2TxNumberInBatch: _l2TxNumberInBatch, - _merkleProof: _merkleProof - }); - } - - /*////////////////////////////////////////////////////////////// - ERA ERC20 LEGACY FUNCTIONS - //////////////////////////////////////////////////////////////*/ - - /// @notice Initiates a deposit by locking funds on the contract and sending the request - /// of processing an L2 transaction where tokens would be minted. - /// @dev If the token is bridged for the first time, the L2 token contract will be deployed. Note however, that the - /// newly-deployed token does not support any custom logic, i.e. rebase tokens' functionality is not supported. - /// @param _prevMsgSender The `msg.sender` address from the external call that initiated current one. - /// @param _l2Receiver The account address that should receive funds on L2. - /// @param _l1Token The L1 token address which is deposited. - /// @param _amount The total amount of tokens to be bridged. - /// @param _l2TxGasLimit The L2 gas limit to be used in the corresponding L2 transaction. - /// @param _l2TxGasPerPubdataByte The gasPerPubdataByteLimit to be used in the corresponding L2 transaction. - /// @param _refundRecipient The address on L2 that will receive the refund for the transaction. - /// @dev If the L2 deposit finalization transaction fails, the `_refundRecipient` will receive the `_l2Value`. - /// Please note, the contract may change the refund recipient's address to eliminate sending funds to addresses - /// out of control. - /// - If `_refundRecipient` is a contract on L1, the refund will be sent to the aliased `_refundRecipient`. - /// - If `_refundRecipient` is set to `address(0)` and the sender has NO deployed bytecode on L1, the refund will - /// be sent to the `msg.sender` address. - /// - If `_refundRecipient` is set to `address(0)` and the sender has deployed bytecode on L1, the refund will be - /// sent to the aliased `msg.sender` address. - /// @dev The address aliasing of L1 contracts as refund recipient on L2 is necessary to guarantee that the funds - /// are controllable through the Mailbox, since the Mailbox applies address aliasing to the from address for the - /// L2 tx if the L1 msg.sender is a contract. Without address aliasing for L1 contracts as refund recipients they - /// would not be able to make proper L2 tx requests through the Mailbox to use or withdraw the funds from L2, and - /// the funds would be lost. - /// @return txHash The L2 transaction hash of deposit finalization. - function depositLegacyErc20Bridge( - address _prevMsgSender, - address _l2Receiver, - address _l1Token, - uint256 _amount, - uint256 _l2TxGasLimit, - uint256 _l2TxGasPerPubdataByte, - address _refundRecipient - ) external payable override onlyLegacyBridge nonReentrant whenNotPaused returns (bytes32 txHash) { - require(_l1Token != L1_WETH_TOKEN, "L1AR: WETH deposit not supported 2"); - - bytes32 _assetId; - bytes memory bridgeMintCalldata; - - { - // Inner call to encode data to decrease local var numbers - _assetId = _ensureTokenRegisteredWithNTV(_l1Token); - IERC20(_l1Token).forceApprove(address(nativeTokenVault), _amount); - } - - { - bridgeMintCalldata = _burn({ - _chainId: ERA_CHAIN_ID, - _l2Value: 0, - _assetId: _assetId, - _prevMsgSender: _prevMsgSender, - _transferData: abi.encode(_amount, _l2Receiver), - _passValue: false - }); - } - - { - bytes memory l2TxCalldata = _getDepositL2Calldata(_prevMsgSender, _assetId, bridgeMintCalldata); - - // If the refund recipient is not specified, the refund will be sent to the sender of the transaction. - // Otherwise, the refund will be sent to the specified address. - // If the recipient is a contract on L1, the address alias will be applied. - address refundRecipient = AddressAliasHelper.actualRefundRecipient(_refundRecipient, _prevMsgSender); - - L2TransactionRequestDirect memory request = L2TransactionRequestDirect({ - chainId: ERA_CHAIN_ID, - l2Contract: L2_ASSET_ROUTER_ADDR, - mintValue: msg.value, // l2 gas + l2 msg.Value the bridgehub will withdraw the mintValue from the shared bridge (base token bridge) for gas - l2Value: 0, // L2 msg.value, this contract doesn't support base token deposits or wrapping functionality, for direct deposits use bridgehub - l2Calldata: l2TxCalldata, - l2GasLimit: _l2TxGasLimit, - l2GasPerPubdataByteLimit: _l2TxGasPerPubdataByte, - factoryDeps: new bytes[](0), - refundRecipient: refundRecipient - }); - txHash = BRIDGE_HUB.requestL2TransactionDirect{value: msg.value}(request); - } - - // Save the deposited amount to claim funds on L1 if the deposit failed on L2 - depositHappened[ERA_CHAIN_ID][txHash] = keccak256(abi.encode(_prevMsgSender, _l1Token, _amount)); - - emit LegacyDepositInitiated({ - chainId: ERA_CHAIN_ID, - l2DepositTxHash: txHash, - from: _prevMsgSender, - to: _l2Receiver, - l1Asset: _l1Token, - amount: _amount - }); - } - - /// @notice Finalizes the withdrawal for transactions initiated via the legacy ERC20 bridge. - /// @param _l2BatchNumber The L2 batch number where the withdrawal was processed. - /// @param _l2MessageIndex The position in the L2 logs Merkle tree of the l2Log that was sent with the message. - /// @param _l2TxNumberInBatch The L2 transaction number in the batch, in which the log was sent. - /// @param _message The L2 withdraw data, stored in an L2 -> L1 message. - /// @param _merkleProof The Merkle proof of the inclusion L2 -> L1 message about withdrawal initialization. - /// - /// @return l1Receiver The address on L1 that will receive the withdrawn funds. - /// @return l1Asset The address of the L1 token being withdrawn. - /// @return amount The amount of the token being withdrawn. - function finalizeWithdrawalLegacyErc20Bridge( - uint256 _l2BatchNumber, - uint256 _l2MessageIndex, - uint16 _l2TxNumberInBatch, - bytes calldata _message, - bytes32[] calldata _merkleProof - ) external override onlyLegacyBridge returns (address l1Receiver, address l1Asset, uint256 amount) { - bytes32 assetId; - (l1Receiver, assetId, amount) = _finalizeWithdrawal({ - _chainId: ERA_CHAIN_ID, - _l2BatchNumber: _l2BatchNumber, - _l2MessageIndex: _l2MessageIndex, - _l2TxNumberInBatch: _l2TxNumberInBatch, - _message: _message, - _merkleProof: _merkleProof - }); - l1Asset = nativeTokenVault.tokenAddress(assetId); - } - - /*////////////////////////////////////////////////////////////// - PAUSE - //////////////////////////////////////////////////////////////*/ - - /// @notice Pauses all functions marked with the `whenNotPaused` modifier. - function pause() external onlyOwner { - _pause(); - } - - /// @notice Unpauses the contract, allowing all functions marked with the `whenNotPaused` modifier to be called again. - function unpause() external onlyOwner { - _unpause(); - } -} diff --git a/l1-contracts/contracts/bridge/L1NativeTokenVault.sol b/l1-contracts/contracts/bridge/L1NativeTokenVault.sol deleted file mode 100644 index fba532597..000000000 --- a/l1-contracts/contracts/bridge/L1NativeTokenVault.sol +++ /dev/null @@ -1,259 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity 0.8.24; - -// solhint-disable reason-string, gas-custom-errors - -import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable-v4/access/Ownable2StepUpgradeable.sol"; -import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable-v4/security/PausableUpgradeable.sol"; - -import {IERC20} from "@openzeppelin/contracts-v4/token/ERC20/IERC20.sol"; -import {SafeERC20} from "@openzeppelin/contracts-v4/token/ERC20/utils/SafeERC20.sol"; - -import {IL1NativeTokenVault} from "./interfaces/IL1NativeTokenVault.sol"; -import {IL1AssetHandler} from "./interfaces/IL1AssetHandler.sol"; - -import {IL1AssetRouter} from "./interfaces/IL1AssetRouter.sol"; -import {ETH_TOKEN_ADDRESS} from "../common/Config.sol"; -import {DataEncoding} from "../common/libraries/DataEncoding.sol"; - -import {BridgeHelper} from "./BridgeHelper.sol"; - -/// @author Matter Labs -/// @custom:security-contact security@matterlabs.dev -/// @dev Vault holding L1 native ETH and ERC20 tokens bridged into the ZK chains. -/// @dev Designed for use with a proxy for upgradability. -contract L1NativeTokenVault is IL1NativeTokenVault, IL1AssetHandler, Ownable2StepUpgradeable, PausableUpgradeable { - using SafeERC20 for IERC20; - - /// @dev The address of the WETH token on L1. - address public immutable override L1_WETH_TOKEN; - - /// @dev L1 Shared Bridge smart contract that handles communication with its counterparts on L2s - IL1AssetRouter public immutable override L1_SHARED_BRIDGE; - - /// @dev Maps token balances for each chain to prevent unauthorized spending across ZK chains. - /// This serves as a security measure until hyperbridging is implemented. - /// NOTE: this function may be removed in the future, don't rely on it! - mapping(uint256 chainId => mapping(address l1Token => uint256 balance)) public chainBalance; - - /// @dev A mapping assetId => tokenAddress - mapping(bytes32 assetId => address tokenAddress) public tokenAddress; - - /// @notice Checks that the message sender is the bridge. - modifier onlyBridge() { - require(msg.sender == address(L1_SHARED_BRIDGE), "NTV not ShB"); - _; - } - - /// @dev Contract is expected to be used as proxy implementation. - /// @dev Initialize the implementation to prevent Parity hack. - constructor(address _l1WethAddress, IL1AssetRouter _l1SharedBridge) { - _disableInitializers(); - L1_WETH_TOKEN = _l1WethAddress; - L1_SHARED_BRIDGE = _l1SharedBridge; - } - - /// @dev Accepts ether only from the Shared Bridge. - receive() external payable { - require(address(L1_SHARED_BRIDGE) == msg.sender, "NTV: ETH only accepted from Shared Bridge"); - } - - /// @dev Initializes a contract for later use. Expected to be used in the proxy - /// @param _owner Address which can change pause / unpause the NTV - /// implementation. The owner is the Governor and separate from the ProxyAdmin from now on, so that the Governor can call the bridge. - function initialize(address _owner) external initializer { - require(_owner != address(0), "NTV owner 0"); - _transferOwnership(_owner); - } - - /// @notice Transfers tokens from shared bridge as part of the migration process. - /// @dev Both ETH and ERC20 tokens can be transferred. Exhausts balance of shared bridge after the first call. - /// @dev Calling second time for the same token will revert. - /// @param _token The address of token to be transferred (address(1) for ether and contract address for ERC20). - function transferFundsFromSharedBridge(address _token) external { - if (_token == ETH_TOKEN_ADDRESS) { - uint256 balanceBefore = address(this).balance; - L1_SHARED_BRIDGE.transferTokenToNTV(_token); - uint256 balanceAfter = address(this).balance; - require(balanceAfter > balanceBefore, "NTV: 0 eth transferred"); - } else { - uint256 balanceBefore = IERC20(_token).balanceOf(address(this)); - uint256 sharedBridgeChainBalance = IERC20(_token).balanceOf(address(L1_SHARED_BRIDGE)); - require(sharedBridgeChainBalance > 0, "NTV: 0 amount to transfer"); - L1_SHARED_BRIDGE.transferTokenToNTV(_token); - uint256 balanceAfter = IERC20(_token).balanceOf(address(this)); - require(balanceAfter - balanceBefore >= sharedBridgeChainBalance, "NTV: wrong amount transferred"); - } - } - - /// @notice Updates chain token balance within NTV to account for tokens transferred from the shared bridge (part of the migration process). - /// @dev Clears chain balance on the shared bridge after the first call. Subsequent calls will not affect the state. - /// @param _token The address of token to be transferred (address(1) for ether and contract address for ERC20). - /// @param _targetChainId The chain ID of the corresponding ZK chain. - function updateChainBalancesFromSharedBridge(address _token, uint256 _targetChainId) external { - uint256 sharedBridgeChainBalance = L1_SHARED_BRIDGE.chainBalance(_targetChainId, _token); - chainBalance[_targetChainId][_token] = chainBalance[_targetChainId][_token] + sharedBridgeChainBalance; - L1_SHARED_BRIDGE.nullifyChainBalanceByNTV(_targetChainId, _token); - } - - /// @notice Registers tokens within the NTV. - /// @dev The goal was to allow bridging L1 native tokens automatically, by registering them on the fly. - /// @notice Allows the bridge to register a token address for the vault. - /// @notice No access control is ok, since the bridging of tokens should be permissionless. This requires permissionless registration. - function registerToken(address _l1Token) external { - require(_l1Token != L1_WETH_TOKEN, "NTV: WETH deposit not supported"); - require(_l1Token == ETH_TOKEN_ADDRESS || _l1Token.code.length > 0, "NTV: empty token"); - bytes32 assetId = DataEncoding.encodeNTVAssetId(block.chainid, _l1Token); - L1_SHARED_BRIDGE.setAssetHandlerAddressInitial(bytes32(uint256(uint160(_l1Token))), address(this)); - tokenAddress[assetId] = _l1Token; - } - - /// @inheritdoc IL1AssetHandler - function bridgeMint( - uint256 _chainId, - bytes32 _assetId, - bytes calldata _data - ) external payable override onlyBridge whenNotPaused returns (address l1Receiver) { - // here we are minting the tokens after the bridgeBurn has happened on an L2, so we can assume the l1Token is not zero - address l1Token = tokenAddress[_assetId]; - uint256 amount; - (amount, l1Receiver) = abi.decode(_data, (uint256, address)); - // Check that the chain has sufficient balance - require(chainBalance[_chainId][l1Token] >= amount, "NTV: not enough funds"); // not enough funds - chainBalance[_chainId][l1Token] -= amount; - - if (l1Token == ETH_TOKEN_ADDRESS) { - bool callSuccess; - // Low-level assembly call, to avoid any memory copying (save gas) - assembly { - callSuccess := call(gas(), l1Receiver, amount, 0, 0, 0, 0) - } - require(callSuccess, "NTV: withdrawal failed, no funds or cannot transfer to receiver"); - } else { - // Withdraw funds - IERC20(l1Token).safeTransfer(l1Receiver, amount); - } - emit BridgeMint(_chainId, _assetId, l1Receiver, amount); - } - - /// @inheritdoc IL1AssetHandler - /// @notice Allows bridgehub to acquire mintValue for L1->L2 transactions. - /// @dev In case of native token vault _data is the tuple of _depositAmount and _l2Receiver. - function bridgeBurn( - uint256 _chainId, - uint256, - bytes32 _assetId, - address _prevMsgSender, - bytes calldata _data - ) external payable override onlyBridge whenNotPaused returns (bytes memory _bridgeMintData) { - (uint256 _depositAmount, address _l2Receiver) = abi.decode(_data, (uint256, address)); - - uint256 amount; - address l1Token = tokenAddress[_assetId]; - if (l1Token == ETH_TOKEN_ADDRESS) { - amount = msg.value; - - // In the old SDK/contracts the user had to always provide `0` as the deposit amount for ETH token, while - // ultimately the provided `msg.value` was used as the deposit amount. This check is needed for backwards compatibility. - if (_depositAmount == 0) { - _depositAmount = amount; - } - - require(_depositAmount == amount, "L1NTV: msg.value not equal to amount"); - } else { - // The Bridgehub also checks this, but we want to be sure - require(msg.value == 0, "NTV m.v > 0 b d.it"); - amount = _depositAmount; - - uint256 expectedDepositAmount = _depositFunds(_prevMsgSender, IERC20(l1Token), _depositAmount); // note if _prevMsgSender is this contract, this will return 0. This does not happen. - require(expectedDepositAmount == _depositAmount, "5T"); // The token has non-standard transfer logic - } - require(amount != 0, "6T"); // empty deposit amount - - chainBalance[_chainId][l1Token] += amount; - - _bridgeMintData = DataEncoding.encodeBridgeMintData({ - _prevMsgSender: _prevMsgSender, - _l2Receiver: _l2Receiver, - _l1Token: l1Token, - _amount: amount, - _erc20Metadata: getERC20Getters(l1Token) - }); - - emit BridgeBurn({ - chainId: _chainId, - assetId: _assetId, - l1Sender: _prevMsgSender, - l2receiver: _l2Receiver, - amount: amount - }); - } - - /// @inheritdoc IL1AssetHandler - function bridgeRecoverFailedTransfer( - uint256 _chainId, - bytes32 _assetId, - address _depositSender, - bytes calldata _data - ) external payable override onlyBridge whenNotPaused { - (uint256 _amount, ) = abi.decode(_data, (uint256, address)); - address l1Token = tokenAddress[_assetId]; - require(_amount > 0, "y1"); - - // check that the chain has sufficient balance - require(chainBalance[_chainId][l1Token] >= _amount, "NTV: not enough funds 2"); - chainBalance[_chainId][l1Token] -= _amount; - - if (l1Token == ETH_TOKEN_ADDRESS) { - bool callSuccess; - // Low-level assembly call, to avoid any memory copying (save gas) - assembly { - callSuccess := call(gas(), _depositSender, _amount, 0, 0, 0, 0) - } - require(callSuccess, "NTV: claimFailedDeposit failed, no funds or cannot transfer to receiver"); - } else { - IERC20(l1Token).safeTransfer(_depositSender, _amount); - // Note we don't allow weth deposits anymore, but there might be legacy weth deposits. - // until we add Weth bridging capabilities, we don't wrap/unwrap weth to ether. - } - } - - /// @dev Receives and parses (name, symbol, decimals) from the token contract - function getERC20Getters(address _token) public view returns (bytes memory) { - return BridgeHelper.getERC20Getters(_token, ETH_TOKEN_ADDRESS); - } - - /// @dev Transfers tokens from the depositor address to the smart contract address. - /// @return The difference between the contract balance before and after the transferring of funds. - function _depositFunds(address _from, IERC20 _token, uint256 _amount) internal returns (uint256) { - uint256 balanceBefore = _token.balanceOf(address(this)); - address from = _from; - // in the legacy scenario the SharedBridge was granting the allowance, we have to transfer from them instead of the user - if ( - _token.allowance(address(L1_SHARED_BRIDGE), address(this)) >= _amount && - _token.allowance(_from, address(this)) < _amount - ) { - from = address(L1_SHARED_BRIDGE); - } - // slither-disable-next-line arbitrary-send-erc20 - _token.safeTransferFrom(from, address(this), _amount); - uint256 balanceAfter = _token.balanceOf(address(this)); - - return balanceAfter - balanceBefore; - } - - /*////////////////////////////////////////////////////////////// - PAUSE - //////////////////////////////////////////////////////////////*/ - - /// @notice Pauses all functions marked with the `whenNotPaused` modifier. - function pause() external onlyOwner { - _pause(); - } - - /// @notice Unpauses the contract, allowing all functions marked with the `whenNotPaused` modifier to be called again. - function unpause() external onlyOwner { - _unpause(); - } -} diff --git a/l1-contracts/contracts/state-transition/libraries/Merkle.sol b/l1-contracts/contracts/state-transition/libraries/Merkle.sol deleted file mode 100644 index 57701f338..000000000 --- a/l1-contracts/contracts/state-transition/libraries/Merkle.sol +++ /dev/null @@ -1,54 +0,0 @@ -// 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; - -import {UncheckedMath} from "../../common/libraries/UncheckedMath.sol"; -import {MerklePathEmpty, MerklePathOutOfBounds, MerkleIndexOutOfBounds} from "../../common/L1ContractErrors.sol"; - -/// @author Matter Labs -/// @custom:security-contact security@matterlabs.dev -library Merkle { - using UncheckedMath for uint256; - - /// @dev Calculate Merkle root by the provided Merkle proof. - /// NOTE: When using this function, check that the _path length is equal to the tree height to prevent shorter/longer paths attack - /// @param _path Merkle path from the leaf to the root - /// @param _index Leaf index in the tree - /// @param _itemHash Hash of leaf content - /// @return The Merkle root - function calculateRoot( - bytes32[] calldata _path, - uint256 _index, - bytes32 _itemHash - ) internal pure returns (bytes32) { - uint256 pathLength = _path.length; - if (pathLength == 0) { - revert MerklePathEmpty(); - } - if (pathLength >= 256) { - revert MerklePathOutOfBounds(); - } - if (_index >= (1 << pathLength)) { - revert MerkleIndexOutOfBounds(); - } - - bytes32 currentHash = _itemHash; - for (uint256 i; i < pathLength; i = i.uncheckedInc()) { - currentHash = (_index % 2 == 0) - ? _efficientHash(currentHash, _path[i]) - : _efficientHash(_path[i], currentHash); - _index /= 2; - } - - return currentHash; - } - - /// @dev Keccak hash of the concatenation of two 32-byte words - function _efficientHash(bytes32 _lhs, bytes32 _rhs) private pure returns (bytes32 result) { - assembly { - mstore(0x00, _lhs) - mstore(0x20, _rhs) - result := keccak256(0x00, 0x40) - } - } -} diff --git a/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/FreezeChain.t.sol b/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/FreezeChain.t.sol deleted file mode 100644 index 20dd04e92..000000000 --- a/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/FreezeChain.t.sol +++ /dev/null @@ -1,26 +0,0 @@ -// // SPDX-License-Identifier: MIT -pragma solidity 0.8.24; - -import {StateTransitionManagerTest} from "./_StateTransitionManager_Shared.t.sol"; -import {GettersFacet} from "contracts/state-transition/chain-deps/facets/Getters.sol"; -import {IAdmin} from "contracts/state-transition/chain-interfaces/IAdmin.sol"; -import {FacetIsFrozen} from "contracts/common/L1ContractErrors.sol"; - -contract freezeChainTest is StateTransitionManagerTest { - // function test_FreezingChain() public { - // createNewChain(getDiamondCutData(diamondInit)); - // address newChainAddress = chainContractAddress.getHyperchain(chainId); - // GettersFacet gettersFacet = GettersFacet(newChainAddress); - // bool isChainFrozen = gettersFacet.isDiamondStorageFrozen(); - // assertEq(isChainFrozen, false); - // vm.stopPrank(); - // vm.startPrank(governor); - // chainContractAddress.freezeChain(block.chainid); - // // Repeated call should revert - // vm.expectRevert(bytes.concat("q1")); // storage frozen - // chainContractAddress.freezeChain(block.chainid); - // // Call fails as storage is frozen - // vm.expectRevert(bytes.concat("q1")); - // isChainFrozen = gettersFacet.isDiamondStorageFrozen(); - // } -} diff --git a/l1-contracts/test/foundry/unit/concrete/state-transition/chain-deps/facets/Getters/PriorityQueueFrontOperation.t.sol b/l1-contracts/test/foundry/unit/concrete/state-transition/chain-deps/facets/Getters/PriorityQueueFrontOperation.t.sol deleted file mode 100644 index ac8ccfeaa..000000000 --- a/l1-contracts/test/foundry/unit/concrete/state-transition/chain-deps/facets/Getters/PriorityQueueFrontOperation.t.sol +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity 0.8.24; - -import {GettersFacetTest} from "./_Getters_Shared.t.sol"; -import {PriorityOperation} from "contracts/state-transition/libraries/PriorityQueue.sol"; -import {QueueIsEmpty} from "contracts/common/L1ContractErrors.sol"; - -contract GetPriorityQueueFrontOperationTest is GettersFacetTest { - function test_revertWhen_queueIsEmpty() public { - vm.expectRevert(QueueIsEmpty.selector); - gettersFacet.priorityQueueFrontOperation(); - } - - function test() public { - PriorityOperation memory expected = PriorityOperation({ - canonicalTxHash: bytes32(uint256(1)), - expirationTimestamp: uint64(2), - layer2Tip: uint192(3) - }); - - gettersFacetWrapper.util_setPriorityQueueFrontOperation(expected); - - PriorityOperation memory received = gettersFacet.priorityQueueFrontOperation(); - - bytes32 expectedHash = keccak256(abi.encode(expected)); - bytes32 receivedHash = keccak256(abi.encode(received)); - assertEq(expectedHash, receivedHash, "Priority queue front operation is incorrect"); - } -} diff --git a/l1-contracts/test/foundry/unit/concrete/state-transition/libraries/Merkle/Merkle.t.sol b/l1-contracts/test/foundry/unit/concrete/state-transition/libraries/Merkle/Merkle.t.sol deleted file mode 100644 index 89514fc99..000000000 --- a/l1-contracts/test/foundry/unit/concrete/state-transition/libraries/Merkle/Merkle.t.sol +++ /dev/null @@ -1,67 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.24; - -import {Test} from "forge-std/Test.sol"; -import {MerkleTest} from "contracts/dev-contracts/test/MerkleTest.sol"; -import {MerkleTreeNoSort} from "./MerkleTreeNoSort.sol"; -import {MerklePathEmpty, MerkleIndexOutOfBounds, MerklePathOutOfBounds} from "contracts/common/L1ContractErrors.sol"; - -contract MerkleTestTest is Test { - MerkleTreeNoSort merkleTree; - MerkleTest merkleTest; - bytes32[] elements; - bytes32 root; - - function setUp() public { - merkleTree = new MerkleTreeNoSort(); - merkleTest = new MerkleTest(); - - for (uint256 i = 0; i < 65; i++) { - elements.push(keccak256(abi.encodePacked(i))); - } - - root = merkleTree.getRoot(elements); - } - - function testElements(uint256 i) public { - vm.assume(i < elements.length); - bytes32 leaf = elements[i]; - bytes32[] memory proof = merkleTree.getProof(elements, i); - - bytes32 rootFromContract = merkleTest.calculateRoot(proof, i, leaf); - - assertEq(rootFromContract, root); - } - - function testFirstElement() public { - testElements(0); - } - - function testLastElement() public { - testElements(elements.length - 1); - } - - function testEmptyProof_shouldRevert() public { - bytes32 leaf = elements[0]; - bytes32[] memory proof; - - vm.expectRevert(MerklePathEmpty.selector); - merkleTest.calculateRoot(proof, 0, leaf); - } - - function testLeafIndexTooBig_shouldRevert() public { - bytes32 leaf = elements[0]; - bytes32[] memory proof = merkleTree.getProof(elements, 0); - - vm.expectRevert(MerkleIndexOutOfBounds.selector); - merkleTest.calculateRoot(proof, 2 ** 255, leaf); - } - - function testProofLengthTooLarge_shouldRevert() public { - bytes32 leaf = elements[0]; - bytes32[] memory proof = new bytes32[](256); - - vm.expectRevert(MerklePathOutOfBounds.selector); - merkleTest.calculateRoot(proof, 0, leaf); - } -} diff --git a/l2-contracts/contracts/bridge/L2AssetRouter.sol b/l2-contracts/contracts/bridge/L2AssetRouter.sol deleted file mode 100644 index d143517b1..000000000 --- a/l2-contracts/contracts/bridge/L2AssetRouter.sol +++ /dev/null @@ -1,198 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity 0.8.20; - -import {Initializable} from "@openzeppelin/contracts/proxy/utils/Initializable.sol"; -import {UpgradeableBeacon} from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; - -import {IL2AssetRouter} from "./interfaces/IL2AssetRouter.sol"; -import {IL1AssetRouter} from "./interfaces/IL1AssetRouter.sol"; -import {ILegacyL2SharedBridge} from "./interfaces/ILegacyL2SharedBridge.sol"; -import {IL2AssetHandler} from "./interfaces/IL2AssetHandler.sol"; -import {IL2StandardToken} from "./interfaces/IL2StandardToken.sol"; -import {IL2NativeTokenVault} from "./interfaces/IL2NativeTokenVault.sol"; - -import {AddressAliasHelper} from "../vendor/AddressAliasHelper.sol"; -import {L2ContractHelper, L2_NATIVE_TOKEN_VAULT} from "../L2ContractHelper.sol"; -import {DataEncoding} from "../common/libraries/DataEncoding.sol"; - -import {EmptyAddress, InvalidCaller} from "../errors/L2ContractErrors.sol"; - -/// @author Matter Labs -/// @custom:security-contact security@matterlabs.dev -/// @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 L2AssetRouter is IL2AssetRouter, ILegacyL2SharedBridge, Initializable { - /// @dev Chain ID of Era for legacy reasons - uint256 public immutable ERA_CHAIN_ID; - - /// @dev Chain ID of L1 for bridging reasons - uint256 public immutable L1_CHAIN_ID; - - /// @dev The address of the L1 shared bridge counterpart. - address public override l1SharedBridge; - - /// @dev Contract that stores the implementation address for token. - /// @dev For more details see https://docs.openzeppelin.com/contracts/3.x/api/proxy#UpgradeableBeacon. - UpgradeableBeacon public DEPRECATED_l2TokenBeacon; - - /// @dev Bytecode hash of the proxy for tokens deployed by the bridge. - bytes32 internal DEPRECATED_l2TokenProxyBytecodeHash; - - /// @notice Deprecated. Kept for backwards compatibility. - /// @dev A mapping l2 token address => l1 token address - mapping(address l2Token => address l1Token) public override l1TokenAddress; - - /// @notice Obsolete, as all calls are performed via L1 Shared Bridge. Kept for backwards compatibility. - /// @dev The address of the legacy L1 erc20 bridge counterpart. - /// This is non-zero only on Era, and should not be renamed for backward compatibility with the SDKs. - address public override l1Bridge; - - /// @dev The contract responsible for handling tokens native to a single chain. - IL2NativeTokenVault public nativeTokenVault; - - /// @dev A mapping of asset ID to asset handler address - mapping(bytes32 assetId => address assetHandlerAddress) public override assetHandlerAddress; - - /// @notice Checks that the message sender is the legacy bridge. - modifier onlyL1Bridge() { - // Only the L1 bridge counterpart can initiate and finalize the deposit. - if ( - AddressAliasHelper.undoL1ToL2Alias(msg.sender) != l1Bridge && - AddressAliasHelper.undoL1ToL2Alias(msg.sender) != l1SharedBridge - ) { - revert InvalidCaller(msg.sender); - } - _; - } - - /// @dev Contract is expected to be used as proxy implementation. - /// @dev Disable the initialization to prevent Parity hack. - /// @param _l1SharedBridge The address of the L1 Bridge contract. - /// @param _l1Bridge The address of the legacy L1 Bridge contract. - constructor(uint256 _eraChainId, uint256 _l1ChainId, address _l1SharedBridge, address _l1Bridge) { - ERA_CHAIN_ID = _eraChainId; - L1_CHAIN_ID = _l1ChainId; - if (_l1SharedBridge == address(0)) { - revert EmptyAddress(); - } - - l1SharedBridge = _l1SharedBridge; - if (block.chainid == ERA_CHAIN_ID) { - if (_l1Bridge == address(0)) { - revert EmptyAddress(); - } - if (l1Bridge == address(0)) { - l1Bridge = _l1Bridge; - } - } - _disableInitializers(); - } - - /// @dev Used to set the assedAddress for a given assetId. - /// @dev Will be used by ZK Gateway - function setAssetHandlerAddress(bytes32 _assetId, address _assetAddress) external onlyL1Bridge { - assetHandlerAddress[_assetId] = _assetAddress; - emit AssetHandlerRegistered(_assetId, _assetAddress); - } - - /// @notice Finalize the deposit and mint funds - /// @param _assetId The encoding of the asset on L2 - /// @param _transferData The encoded data required for deposit (address _l1Sender, uint256 _amount, address _l2Receiver, bytes memory erc20Data, address originToken) - function finalizeDeposit(bytes32 _assetId, bytes memory _transferData) public override onlyL1Bridge { - address assetHandler = assetHandlerAddress[_assetId]; - if (assetHandler != address(0)) { - IL2AssetHandler(assetHandler).bridgeMint(L1_CHAIN_ID, _assetId, _transferData); - } else { - L2_NATIVE_TOKEN_VAULT.bridgeMint(L1_CHAIN_ID, _assetId, _transferData); - assetHandlerAddress[_assetId] = address(L2_NATIVE_TOKEN_VAULT); - } - - emit FinalizeDepositSharedBridge(L1_CHAIN_ID, _assetId, _transferData); - } - - /// @notice Initiates a withdrawal by burning funds on the contract and sending the message to L1 - /// where tokens would be unlocked - /// @param _assetId The asset id of the withdrawn asset - /// @param _assetData The data that is passed to the asset handler contract - function withdraw(bytes32 _assetId, bytes memory _assetData) public override { - address assetHandler = assetHandlerAddress[_assetId]; - bytes memory _l1bridgeMintData = IL2AssetHandler(assetHandler).bridgeBurn({ - _chainId: L1_CHAIN_ID, - _mintValue: 0, - _assetId: _assetId, - _prevMsgSender: msg.sender, - _data: _assetData - }); - - bytes memory message = _getL1WithdrawMessage(_assetId, _l1bridgeMintData); - L2ContractHelper.sendMessageToL1(message); - - emit WithdrawalInitiatedSharedBridge(L1_CHAIN_ID, msg.sender, _assetId, _assetData); - } - - /// @notice Encodes the message for l2ToL1log sent during withdraw initialization. - /// @param _assetId The encoding of the asset on L2 which is withdrawn. - /// @param _l1bridgeMintData The calldata used by l1 asset handler to unlock tokens for recipient. - function _getL1WithdrawMessage( - bytes32 _assetId, - bytes memory _l1bridgeMintData - ) internal pure returns (bytes memory) { - // note we use the IL1SharedBridge.finalizeWithdrawal function selector to specify the selector for L1<>L2 messages, - // and we use this interface so that when the switch happened the old messages could be processed - // solhint-disable-next-line func-named-parameters - return abi.encodePacked(IL1AssetRouter.finalizeWithdrawal.selector, _assetId, _l1bridgeMintData); - } - - /*////////////////////////////////////////////////////////////// - LEGACY FUNCTIONS - //////////////////////////////////////////////////////////////*/ - - /// @notice Legacy finalizeDeposit. - /// @dev Finalizes the deposit and mint funds. - /// @param _l1Sender The address of token sender on L1. - /// @param _l2Receiver The address of token receiver on L2. - /// @param _l1Token The address of the token transferred. - /// @param _amount The amount of the token transferred. - /// @param _data The metadata of the token transferred. - function finalizeDeposit( - address _l1Sender, - address _l2Receiver, - address _l1Token, - uint256 _amount, - bytes calldata _data - ) external override { - bytes32 assetId = DataEncoding.encodeNTVAssetId(L1_CHAIN_ID, _l1Token); - // solhint-disable-next-line func-named-parameters - bytes memory data = DataEncoding.encodeBridgeMintData(_l1Sender, _l2Receiver, _l1Token, _amount, _data); - finalizeDeposit(assetId, data); - } - - /// @notice Initiates a withdrawal by burning funds on the contract and sending the message to L1 - /// where tokens would be unlocked. - /// @param _l1Receiver The address of token receiver on L1. - /// @param _l2Token The address of the token transferred. - /// @param _amount The amount of the token transferred. - function withdraw(address _l1Receiver, address _l2Token, uint256 _amount) external { - bytes32 assetId = DataEncoding.encodeNTVAssetId(L1_CHAIN_ID, getL1TokenAddress(_l2Token)); - bytes memory data = abi.encode(_amount, _l1Receiver); - withdraw(assetId, data); - } - - /// @notice Legacy getL1TokenAddress. - /// @param _l2Token The address of token on L2. - /// @return The address of token on L1. - function getL1TokenAddress(address _l2Token) public view returns (address) { - return IL2StandardToken(_l2Token).l1Address(); - } - - /// @notice Legacy function used for backward compatibility to return L2 wrapped token - /// @notice address corresponding to provided L1 token address and deployed through NTV. - /// @dev However, the shared bridge can use custom asset handlers such that L2 addresses differ, - /// @dev or an L1 token may not have an L2 counterpart. - /// @param _l1Token The address of token on L1. - /// @return Address of an L2 token counterpart - function l2TokenAddress(address _l1Token) public view returns (address) { - return L2_NATIVE_TOKEN_VAULT.l2TokenAddress(_l1Token); - } -} diff --git a/l2-contracts/contracts/bridge/L2NativeTokenVault.sol b/l2-contracts/contracts/bridge/L2NativeTokenVault.sol deleted file mode 100644 index 56c50d9a8..000000000 --- a/l2-contracts/contracts/bridge/L2NativeTokenVault.sol +++ /dev/null @@ -1,234 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity 0.8.20; - -import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; -import {BeaconProxy} from "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol"; -import {UpgradeableBeacon} from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; - -import {IL2StandardToken} from "./interfaces/IL2StandardToken.sol"; -import {IL2NativeTokenVault} from "./interfaces/IL2NativeTokenVault.sol"; - -import {L2StandardERC20} from "./L2StandardERC20.sol"; -import {L2ContractHelper, DEPLOYER_SYSTEM_CONTRACT, L2_ASSET_ROUTER, IContractDeployer} from "../L2ContractHelper.sol"; -import {SystemContractsCaller} from "../SystemContractsCaller.sol"; -import {DataEncoding} from "../common/libraries/DataEncoding.sol"; - -import {EmptyAddress, EmptyBytes32, AddressMismatch, AssetIdMismatch, DeployFailed, AmountMustBeGreaterThanZero, InvalidCaller} from "../errors/L2ContractErrors.sol"; - -/// @author Matter Labs -/// @custom:security-contact security@matterlabs.dev -/// @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 L2NativeTokenVault is IL2NativeTokenVault, Ownable2StepUpgradeable { - /// @dev Chain ID of L1 for bridging reasons. - uint256 public immutable L1_CHAIN_ID; - - bytes32 internal l2TokenProxyBytecodeHash; - - /// @dev Contract that stores the implementation address for token. - /// @dev For more details see https://docs.openzeppelin.com/contracts/3.x/api/proxy#UpgradeableBeacon. - UpgradeableBeacon public l2TokenBeacon; - - mapping(bytes32 assetId => address tokenAddress) public override tokenAddress; - - /// @dev Bytecode hash of the proxy for tokens deployed by the bridge. - - modifier onlyBridge() { - if (msg.sender != address(L2_ASSET_ROUTER)) { - revert InvalidCaller(msg.sender); - // Only L2 bridge can call this method - } - _; - } - - /// @notice Initializes the bridge contract for later use. Expected to be used in the proxy. - /// @param _l1ChainId The L1 chain id differs between mainnet and testnets. - /// @param _l2TokenProxyBytecodeHash The bytecode hash of the proxy for tokens deployed by the bridge. - /// @param _aliasedOwner The address of the governor contract. - constructor(uint256 _l1ChainId, bytes32 _l2TokenProxyBytecodeHash, address _aliasedOwner) { - L1_CHAIN_ID = _l1ChainId; - - _disableInitializers(); - if (_l2TokenProxyBytecodeHash == bytes32(0)) { - revert EmptyBytes32(); - } - if (_aliasedOwner == address(0)) { - revert EmptyAddress(); - } - - l2TokenProxyBytecodeHash = _l2TokenProxyBytecodeHash; - _transferOwnership(_aliasedOwner); - } - - /// @notice Sets L2 token beacon used by wrapped ERC20 tokens deployed by NTV. - /// @dev Sets the l2TokenBeacon, called after initialize. - /// @param _l2TokenBeacon The address of L2 token beacon implementation. - /// @param _l2TokenProxyBytecodeHash The bytecode hash of the L2 token proxy that will be deployed for wrapped tokens. - function setL2TokenBeacon(address _l2TokenBeacon, bytes32 _l2TokenProxyBytecodeHash) external onlyOwner { - l2TokenBeacon = UpgradeableBeacon(_l2TokenBeacon); - l2TokenProxyBytecodeHash = _l2TokenProxyBytecodeHash; - emit L2TokenBeaconUpdated(_l2TokenBeacon, _l2TokenProxyBytecodeHash); - } - - /// @notice Configure L2 token beacon used by wrapped ERC20 tokens deployed by NTV. - /// @dev we don't call this in the constructor, as we need to provide factory deps. - /// @param _contractsDeployedAlready Ensures beacon proxy for standard ERC20 has not been deployed. - function configureL2TokenBeacon(bool _contractsDeployedAlready, address _l2TokenBeacon) external { - if (address(l2TokenBeacon) != address(0)) { - revert AddressMismatch(address(l2TokenBeacon), address(0)); - } - if (_contractsDeployedAlready) { - if (_l2TokenBeacon == address(0)) { - revert EmptyAddress(); - } - l2TokenBeacon = UpgradeableBeacon(_l2TokenBeacon); - } else { - address l2StandardToken = address(new L2StandardERC20{salt: bytes32(0)}()); - l2TokenBeacon = new UpgradeableBeacon{salt: bytes32(0)}(l2StandardToken); - l2TokenBeacon.transferOwnership(owner()); - } - } - - /// @notice Used when the chain receives a transfer from L1 Shared Bridge and correspondingly mints the asset. - /// @param _chainId The chainId that the message is from. - /// @param _assetId The assetId of the asset being bridged. - /// @param _data The abi.encoded transfer data. - function bridgeMint(uint256 _chainId, bytes32 _assetId, bytes calldata _data) external payable override onlyBridge { - address token = tokenAddress[_assetId]; - ( - address _l1Sender, - address _l2Receiver, - address originToken, - uint256 _amount, - bytes memory erc20Data - ) = DataEncoding.decodeBridgeMintData(_data); - - if (token == address(0)) { - address expectedToken = _calculateCreate2TokenAddress(originToken); - bytes32 expectedAssetId = DataEncoding.encodeNTVAssetId(L1_CHAIN_ID, originToken); - if (_assetId != expectedAssetId) { - // Make sure that a NativeTokenVault sent the message - revert AssetIdMismatch(expectedAssetId, _assetId); - } - address deployedToken = _deployL2Token(originToken, erc20Data); - if (deployedToken != expectedToken) { - revert AddressMismatch(expectedToken, deployedToken); - } - tokenAddress[_assetId] = expectedToken; - token = expectedToken; - } - - IL2StandardToken(token).bridgeMint(_l2Receiver, _amount); - /// backwards compatible event - emit FinalizeDeposit(_l1Sender, _l2Receiver, token, _amount); - emit BridgeMint({ - chainId: _chainId, - assetId: _assetId, - sender: _l1Sender, - l2Receiver: _l2Receiver, - amount: _amount - }); - } - - /// @notice Burns wrapped tokens and returns the calldata for L2 -> L1 message. - /// @dev In case of native token vault _data is the tuple of _depositAmount and _l2Receiver. - /// @param _chainId The chainId that the message will be sent to. - /// @param _mintValue The L1 base token value bridged. - /// @param _assetId The L2 assetId of the asset being bridged. - /// @param _prevMsgSender The original caller of the shared bridge. - /// @param _data The abi.encoded transfer data. - /// @return l1BridgeMintData The calldata used by l1 asset handler to unlock tokens for recipient. - function bridgeBurn( - uint256 _chainId, - uint256 _mintValue, - bytes32 _assetId, - address _prevMsgSender, - bytes calldata _data - ) external payable override onlyBridge returns (bytes memory l1BridgeMintData) { - (uint256 _amount, address _l1Receiver) = abi.decode(_data, (uint256, address)); - if (_amount == 0) { - // "Amount cannot be zero"); - revert AmountMustBeGreaterThanZero(); - } - - address l2Token = tokenAddress[_assetId]; - IL2StandardToken(l2Token).bridgeBurn(_prevMsgSender, _amount); - - /// backwards compatible event - emit WithdrawalInitiated(_prevMsgSender, _l1Receiver, l2Token, _amount); - emit BridgeBurn({ - chainId: _chainId, - assetId: _assetId, - l2Sender: _prevMsgSender, - receiver: _l1Receiver, - mintValue: _mintValue, - amount: _amount - }); - l1BridgeMintData = _data; - } - - /// @notice Calculates L2 wrapped token address corresponding to L1 token counterpart. - /// @param _l1Token The address of token on L1. - /// @return expectedToken The address of token on L2. - function l2TokenAddress(address _l1Token) public view override returns (address expectedToken) { - bytes32 expectedAssetId = DataEncoding.encodeNTVAssetId(L1_CHAIN_ID, _l1Token); - expectedToken = tokenAddress[expectedAssetId]; - if (expectedToken == address(0)) { - expectedToken = _calculateCreate2TokenAddress(_l1Token); - } - } - - /// @notice Deploys and initializes the L2 token for the L1 counterpart. - /// @param _l1Token The address of token on L1. - /// @param _erc20Data The ERC20 metadata of the token deployed. - /// @return The address of the beacon proxy (L2 wrapped / bridged token). - function _deployL2Token(address _l1Token, bytes memory _erc20Data) internal returns (address) { - bytes32 salt = _getCreate2Salt(_l1Token); - - BeaconProxy l2Token = _deployBeaconProxy(salt); - L2StandardERC20(address(l2Token)).bridgeInitialize(_l1Token, _erc20Data); - - return address(l2Token); - } - - /// @notice Deploys the beacon proxy for the L2 token, while using ContractDeployer system contract. - /// @dev This function uses raw call to ContractDeployer to make sure that exactly `l2TokenProxyBytecodeHash` is used - /// for the code of the proxy. - /// @param salt The salt used for beacon proxy deployment of L2 wrapped token. - /// @return proxy The beacon proxy, i.e. L2 wrapped / bridged token. - function _deployBeaconProxy(bytes32 salt) internal returns (BeaconProxy proxy) { - (bool success, bytes memory returndata) = SystemContractsCaller.systemCallWithReturndata( - uint32(gasleft()), - DEPLOYER_SYSTEM_CONTRACT, - 0, - abi.encodeCall( - IContractDeployer.create2, - (salt, l2TokenProxyBytecodeHash, abi.encode(address(l2TokenBeacon), "")) - ) - ); - - // The deployment should be successful and return the address of the proxy - if (!success) { - revert DeployFailed(); - } - proxy = BeaconProxy(abi.decode(returndata, (address))); - } - - /// @notice Calculates L2 wrapped token address given the currently stored beacon proxy bytecode hash and beacon address. - /// @param _l1Token The address of token on L1. - /// @return Address of an L2 token counterpart. - function _calculateCreate2TokenAddress(address _l1Token) internal view returns (address) { - bytes32 constructorInputHash = keccak256(abi.encode(address(l2TokenBeacon), "")); - bytes32 salt = _getCreate2Salt(_l1Token); - return - L2ContractHelper.computeCreate2Address(address(this), salt, l2TokenProxyBytecodeHash, constructorInputHash); - } - - /// @notice Converts the L1 token address to the create2 salt of deployed L2 token. - /// @param _l1Token The address of token on L1. - /// @return salt The salt used to compute address of wrapped token on L2 and for beacon proxy deployment. - function _getCreate2Salt(address _l1Token) internal pure returns (bytes32 salt) { - salt = bytes32(uint256(uint160(_l1Token))); - } -} diff --git a/l2-contracts/contracts/dev-contracts/DevL2SharedBridge.sol b/l2-contracts/contracts/dev-contracts/DevL2SharedBridge.sol deleted file mode 100644 index e93d5c987..000000000 --- a/l2-contracts/contracts/dev-contracts/DevL2SharedBridge.sol +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity 0.8.24; - -import {L2SharedBridge} from "../bridge/L2SharedBridge.sol"; -import {L2StandardERC20} from "../bridge/L2StandardERC20.sol"; -import {UpgradeableBeacon} from "@openzeppelin/contracts-v4/proxy/beacon/UpgradeableBeacon.sol"; - -/// @author Matter Labs -/// @notice The implementation of the shared bridge that allows setting legacy bridge. Must only be used in local testing environments. -contract DevL2SharedBridge is L2SharedBridge { - constructor(uint256 _eraChainId) L2SharedBridge(_eraChainId) {} - - function initializeDevBridge( - address _l1SharedBridge, - address _l1Bridge, - bytes32 _l2TokenProxyBytecodeHash, - address _aliasedOwner - ) external reinitializer(2) { - l1SharedBridge = _l1SharedBridge; - - address l2StandardToken = address(new L2StandardERC20{salt: bytes32(0)}()); - l2TokenBeacon = new UpgradeableBeacon{salt: bytes32(0)}(l2StandardToken); - l2TokenProxyBytecodeHash = _l2TokenProxyBytecodeHash; - l2TokenBeacon.transferOwnership(_aliasedOwner); - - // Unfortunately the `l1Bridge` is not an internal variable in the parent contract. - // To keep the changes to the production code minimal, we'll just manually set the variable here. - assembly { - sstore(4, _l1Bridge) - } - } -} diff --git a/l2-contracts/src/deploy-shared-bridge-on-l2-through-l1.ts b/l2-contracts/src/deploy-shared-bridge-on-l2-through-l1.ts deleted file mode 100644 index 433365064..000000000 --- a/l2-contracts/src/deploy-shared-bridge-on-l2-through-l1.ts +++ /dev/null @@ -1,154 +0,0 @@ -import { Command } from "commander"; -import type { BigNumberish } from "ethers"; -import { Wallet, ethers } from "ethers"; -import { formatUnits, parseUnits } from "ethers/lib/utils"; -import { provider, publishBytecodeFromL1, priorityTxMaxGasLimit } from "./utils"; - -import { ethTestConfig } from "./deploy-utils"; - -import { Deployer } from "../../l1-contracts/src.ts/deploy"; -import { GAS_MULTIPLIER } from "../../l1-contracts/scripts/utils"; -import * as hre from "hardhat"; -import { - ADDRESS_ONE, - L2_ASSET_ROUTER_ADDRESS, - L2_BRIDGEHUB_ADDRESS, - L2_MESSAGE_ROOT_ADDRESS, - L2_NATIVE_TOKEN_VAULT_ADDRESS, -} from "../../l1-contracts/src.ts/utils"; - -import { L2NativeTokenVaultFactory } from "../typechain"; -import { BridgehubFactory } from "../../l1-contracts/typechain"; - -export const L2_SHARED_BRIDGE_ABI = hre.artifacts.readArtifactSync("L2SharedBridge").abi; -export const L2_STANDARD_TOKEN_PROXY_BYTECODE = hre.artifacts.readArtifactSync("BeaconProxy").bytecode; - -export async function publishL2NativeTokenVaultDependencyBytecodesOnL2( - deployer: Deployer, - chainId: string, - gasPrice: BigNumberish -) { - if (deployer.verbose) { - console.log("Providing necessary L2 bytecodes"); - } - - const L2_STANDARD_ERC20_PROXY_FACTORY_BYTECODE = hre.artifacts.readArtifactSync("UpgradeableBeacon").bytecode; - const L2_STANDARD_ERC20_IMPLEMENTATION_BYTECODE = hre.artifacts.readArtifactSync("L2StandardERC20").bytecode; - - const receipt = await ( - await publishBytecodeFromL1( - chainId, - deployer.deployWallet, - [ - L2_STANDARD_ERC20_PROXY_FACTORY_BYTECODE, - L2_STANDARD_ERC20_IMPLEMENTATION_BYTECODE, - L2_STANDARD_TOKEN_PROXY_BYTECODE, - ], - gasPrice - ) - ).wait(); - - if (deployer.verbose) { - console.log("Bytecodes published on L2, hash: ", receipt.transactionHash); - } -} - -async function setL2TokenBeacon(deployer: Deployer, chainId: string, gasPrice: BigNumberish) { - if (deployer.verbose) { - console.log("Setting L2 token beacon"); - } - const l2NTV = L2NativeTokenVaultFactory.connect(L2_NATIVE_TOKEN_VAULT_ADDRESS, deployer.deployWallet); - - const receipt = await deployer.executeUpgradeOnL2( - chainId, - L2_NATIVE_TOKEN_VAULT_ADDRESS, - gasPrice, - l2NTV.interface.encodeFunctionData("setL2TokenBeacon", [false, ethers.constants.AddressZero]), - priorityTxMaxGasLimit - ); - if (deployer.verbose) { - console.log("Set L2Token Beacon, upgrade hash", receipt.transactionHash); - } - const bridgehub = BridgehubFactory.connect(L2_BRIDGEHUB_ADDRESS, deployer.deployWallet); - const receipt2 = await deployer.executeUpgradeOnL2( - chainId, - L2_BRIDGEHUB_ADDRESS, - gasPrice, - bridgehub.interface.encodeFunctionData("setAddresses", [ - L2_ASSET_ROUTER_ADDRESS, - ADDRESS_ONE, - L2_MESSAGE_ROOT_ADDRESS, - ]), - priorityTxMaxGasLimit - ); - if (deployer.verbose) { - console.log("Set addresses in BH, upgrade hash", receipt2.transactionHash); - } -} - -export async function deploySharedBridgeOnL2ThroughL1(deployer: Deployer, chainId: string, gasPrice: BigNumberish) { - await publishL2NativeTokenVaultDependencyBytecodesOnL2(deployer, chainId, gasPrice); - await setL2TokenBeacon(deployer, chainId, gasPrice); - if (deployer.verbose) { - console.log(`CONTRACTS_L2_NATIVE_TOKEN_VAULT_IMPL_ADDR=${L2_NATIVE_TOKEN_VAULT_ADDRESS}`); - console.log(`CONTRACTS_L2_NATIVE_TOKEN_VAULT_PROXY_ADDR=${L2_NATIVE_TOKEN_VAULT_ADDRESS}`); - console.log(`CONTRACTS_L2_SHARED_BRIDGE_IMPL_ADDR=${L2_ASSET_ROUTER_ADDRESS}`); - console.log(`CONTRACTS_L2_SHARED_BRIDGE_ADDR=${L2_ASSET_ROUTER_ADDRESS}`); - } -} - -async function main() { - const program = new Command(); - - program.version("0.1.0").name("deploy-shared-bridge-on-l2-through-l1"); - - program - .option("--private-key ") - .option("--chain-id ") - .option("--local-legacy-bridge-testing") - .option("--gas-price ") - .option("--nonce ") - .option("--erc20-bridge ") - .option("--skip-initialize-chain-governance ") - .action(async (cmd) => { - const chainId: string = cmd.chainId ? cmd.chainId : process.env.CHAIN_ETH_ZKSYNC_NETWORK_ID; - const deployWallet = cmd.privateKey - ? new Wallet(cmd.privateKey, provider) - : Wallet.fromMnemonic( - process.env.MNEMONIC ? process.env.MNEMONIC : ethTestConfig.mnemonic, - "m/44'/60'/0'/0/1" - ).connect(provider); - console.log(`Using deployer wallet: ${deployWallet.address}`); - - const deployer = new Deployer({ - deployWallet, - ownerAddress: deployWallet.address, - verbose: true, - }); - - const nonce = cmd.nonce ? parseInt(cmd.nonce) : await deployer.deployWallet.getTransactionCount(); - console.log(`Using nonce: ${nonce}`); - - const gasPrice = cmd.gasPrice - ? parseUnits(cmd.gasPrice, "gwei") - : (await provider.getGasPrice()).mul(GAS_MULTIPLIER); - console.log(`Using gas price: ${formatUnits(gasPrice, "gwei")} gwei`); - - const skipInitializeChainGovernance = - !!cmd.skipInitializeChainGovernance && cmd.skipInitializeChainGovernance === "true"; - if (skipInitializeChainGovernance) { - console.log("Initialization of the chain governance will be skipped"); - } - - await deploySharedBridgeOnL2ThroughL1(deployer, chainId, gasPrice); - }); - - await program.parseAsync(process.argv); -} - -main() - .then(() => process.exit(0)) - .catch((err) => { - console.error("Error:", err); - process.exit(1); - }); diff --git a/l2-contracts/test/erc20.test.ts b/l2-contracts/test/erc20.test.ts deleted file mode 100644 index ec531f7aa..000000000 --- a/l2-contracts/test/erc20.test.ts +++ /dev/null @@ -1,169 +0,0 @@ -import { Deployer } from "@matterlabs/hardhat-zksync-deploy"; -import { expect } from "chai"; -import { ethers } from "ethers"; -import * as hre from "hardhat"; -import { Provider, Wallet } from "zksync-ethers"; -import { hashBytecode } from "zksync-ethers/build/utils"; -import { unapplyL1ToL2Alias, setCode } from "./test-utils"; -import type { L2AssetRouter, L2NativeTokenVault, L2StandardERC20 } from "../typechain"; -import { L2AssetRouterFactory, L2NativeTokenVaultFactory, L2StandardERC20Factory } from "../typechain"; - -const richAccount = [ - { - address: "0x36615Cf349d7F6344891B1e7CA7C72883F5dc049", - privateKey: "0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110", - }, - { - address: "0xa61464658AfeAf65CccaaFD3a512b69A83B77618", - privateKey: "0xac1e735be8536c6534bb4f17f06f6afc73b2b5ba84ac2cfb12f7461b20c0bbe3", - }, - { - address: "0x0D43eB5B8a47bA8900d84AA36656c92024e9772e", - privateKey: "0xd293c684d884d56f8d6abd64fc76757d3664904e309a0645baf8522ab6366d9e", - }, - { - address: "0xA13c10C0D5bd6f79041B9835c63f91de35A15883", - privateKey: "0x850683b40d4a740aa6e745f889a6fdc8327be76e122f5aba645a5b02d0248db8", - }, -]; - -describe("ERC20Bridge", function () { - const provider = new Provider(hre.config.networks.localhost.url); - const deployerWallet = new Wallet(richAccount[0].privateKey, provider); - const governorWallet = new Wallet(richAccount[1].privateKey, provider); - const proxyAdminWallet = new Wallet(richAccount[3].privateKey, provider); - - // We need to emulate a L1->L2 transaction from the L1 bridge to L2 counterpart. - // It is a bit easier to use EOA and it is sufficient for the tests. - const l1BridgeWallet = new Wallet(richAccount[2].privateKey, provider); - - // We won't actually deploy an L1 token in these tests, but we need some address for it. - const L1_TOKEN_ADDRESS = "0x1111000000000000000000000000000000001111"; - const L2_ASSET_ROUTER_ADDRESS = "0x0000000000000000000000000000000000010003"; - const L2_NATIVE_TOKEN_VAULT_ADDRESS = "0x0000000000000000000000000000000000010004"; - - const testChainId = 9; - - let erc20Bridge: L2AssetRouter; - let erc20NativeTokenVault: L2NativeTokenVault; - let erc20Token: L2StandardERC20; - const contractsDeployedAlready: boolean = false; - - before("Deploy token and bridge", async function () { - const deployer = new Deployer(hre, deployerWallet); - - // While we formally don't need to deploy the token and the beacon proxy, it is a neat way to have the bytecode published - const l2TokenImplAddress = await deployer.deploy(await deployer.loadArtifact("L2StandardERC20")); - const l2Erc20TokenBeacon = await deployer.deploy(await deployer.loadArtifact("UpgradeableBeacon"), [ - l2TokenImplAddress.address, - ]); - await deployer.deploy(await deployer.loadArtifact("BeaconProxy"), [l2Erc20TokenBeacon.address, "0x"]); - const beaconProxyBytecodeHash = hashBytecode((await deployer.loadArtifact("BeaconProxy")).bytecode); - let constructorArgs = ethers.utils.defaultAbiCoder.encode( - ["uint256", "uint256", "address", "address"], - /// note in real deployment we have to transfer ownership of standard deployer here - [testChainId, 1, unapplyL1ToL2Alias(l1BridgeWallet.address), unapplyL1ToL2Alias(l1BridgeWallet.address)] - ); - await setCode( - deployerWallet, - L2_ASSET_ROUTER_ADDRESS, - (await deployer.loadArtifact("L2AssetRouter")).bytecode, - true, - constructorArgs - ); - - erc20Bridge = L2AssetRouterFactory.connect(L2_ASSET_ROUTER_ADDRESS, deployerWallet); - const l2NativeTokenVaultArtifact = await deployer.loadArtifact("L2NativeTokenVault"); - constructorArgs = ethers.utils.defaultAbiCoder.encode( - ["uint256", "bytes32", "address", "bool"], - /// note in real deployment we have to transfer ownership of standard deployer here - [1, beaconProxyBytecodeHash, governorWallet.address, contractsDeployedAlready] - ); - await setCode( - deployerWallet, - L2_NATIVE_TOKEN_VAULT_ADDRESS, - l2NativeTokenVaultArtifact.bytecode, - true, - constructorArgs - ); - - erc20NativeTokenVault = L2NativeTokenVaultFactory.connect(L2_NATIVE_TOKEN_VAULT_ADDRESS, l1BridgeWallet); - const governorNTV = L2NativeTokenVaultFactory.connect(L2_NATIVE_TOKEN_VAULT_ADDRESS, governorWallet); - await governorNTV.configureL2TokenBeacon(false, ethers.constants.AddressZero); - }); - - it("Should finalize deposit ERC20 deposit", async function () { - const erc20BridgeWithL1BridgeWallet = L2AssetRouterFactory.connect(erc20Bridge.address, proxyAdminWallet); - const l1Depositor = ethers.Wallet.createRandom(); - const l2Receiver = ethers.Wallet.createRandom(); - const l1Bridge = await hre.ethers.getImpersonatedSigner(l1BridgeWallet.address); - const tx = await ( - await erc20BridgeWithL1BridgeWallet.connect(l1Bridge)["finalizeDeposit(address,address,address,uint256,bytes)"]( - // Depositor and l2Receiver can be any here - l1Depositor.address, - l2Receiver.address, - L1_TOKEN_ADDRESS, - 100, - encodedTokenData("TestToken", "TT", 18) - ) - ).wait(); - const l2TokenInfo = tx.events.find((event) => event.event === "FinalizeDepositSharedBridge").args.assetId; - const l2TokenAddress = await erc20NativeTokenVault.tokenAddress(l2TokenInfo); - // Checking the correctness of the balance: - erc20Token = L2StandardERC20Factory.connect(l2TokenAddress, deployerWallet); - expect(await erc20Token.balanceOf(l2Receiver.address)).to.equal(100); - expect(await erc20Token.totalSupply()).to.equal(100); - expect(await erc20Token.name()).to.equal("TestToken"); - expect(await erc20Token.symbol()).to.equal("TT"); - expect(await erc20Token.decimals()).to.equal(18); - }); - - it("Governance should be able to reinitialize the token", async () => { - const erc20TokenWithGovernor = L2StandardERC20Factory.connect(erc20Token.address, governorWallet); - - await ( - await erc20TokenWithGovernor.reinitializeToken( - { - ignoreName: false, - ignoreSymbol: false, - ignoreDecimals: false, - }, - "TestTokenNewName", - "TTN", - 2 - ) - ).wait(); - - expect(await erc20Token.name()).to.equal("TestTokenNewName"); - expect(await erc20Token.symbol()).to.equal("TTN"); - // The decimals should stay the same - expect(await erc20Token.decimals()).to.equal(18); - }); - - it("Governance should not be able to skip initializer versions", async () => { - const erc20TokenWithGovernor = L2StandardERC20Factory.connect(erc20Token.address, governorWallet); - - await expect( - erc20TokenWithGovernor.reinitializeToken( - { - ignoreName: false, - ignoreSymbol: false, - ignoreDecimals: false, - }, - "TestTokenNewName", - "TTN", - 20, - { gasLimit: 10000000 } - ) - ).to.be.reverted; - }); -}); - -function encodedTokenData(name: string, symbol: string, decimals: number) { - const abiCoder = ethers.utils.defaultAbiCoder; - const encodedName = abiCoder.encode(["string"], [name]); - const encodedSymbol = abiCoder.encode(["string"], [symbol]); - const encodedDecimals = abiCoder.encode(["uint8"], [decimals]); - - return abiCoder.encode(["bytes", "bytes", "bytes"], [encodedName, encodedSymbol, encodedDecimals]); -} From e5727a01e6db06743d77f85521afa12f07e3d257 Mon Sep 17 00:00:00 2001 From: Stanislav Bezkorovainyi Date: Wed, 11 Sep 2024 17:52:01 +0200 Subject: [PATCH 03/20] Sync audit head with base (#797) From 9aa740122a29b5ff4974c566856811d686468b65 Mon Sep 17 00:00:00 2001 From: Raid5594 <52794079+Raid5594@users.noreply.github.com> Date: Fri, 20 Sep 2024 16:30:41 +0400 Subject: [PATCH 04/20] (feat): comment out unit tests and fix, small bug in shared bridge (#812) Co-authored-by: Raid Ateir --- .../bridge/ntv/IL1NativeTokenVault.sol | 3 +- .../bridge/ntv/L1NativeTokenVault.sol | 12 + .../contracts/bridge/ntv/NativeTokenVault.sol | 2 +- .../contracts/common/L1ContractErrors.sol | 2 - .../L1SharedBridge/L1SharedBridgeFails.t.sol | 133 ++++--- .../concrete/Executor/ExecutorProof.t.sol | 103 +++--- .../foundry/l1/unit/concrete/Utils/Utils.sol | 39 +- .../ChainTypeManager/CreateNewChain.t.sol | 10 + .../ChainTypeManager/FreezeChain.t.sol | 41 ++- .../ChainTypeManager/RevertBatches.t.sol | 345 +++++++++++------- .../_ChainTypeManager_Shared.t.sol | 19 + 11 files changed, 441 insertions(+), 268 deletions(-) diff --git a/l1-contracts/contracts/bridge/ntv/IL1NativeTokenVault.sol b/l1-contracts/contracts/bridge/ntv/IL1NativeTokenVault.sol index 1d16f48fb..a3fcbe917 100644 --- a/l1-contracts/contracts/bridge/ntv/IL1NativeTokenVault.sol +++ b/l1-contracts/contracts/bridge/ntv/IL1NativeTokenVault.sol @@ -4,13 +4,14 @@ pragma solidity 0.8.24; import {IL1Nullifier} from "../interfaces/IL1Nullifier.sol"; import {INativeTokenVault} from "./INativeTokenVault.sol"; +import {IL1AssetDeploymentTracker} from "../interfaces/IL1AssetDeploymentTracker.sol"; /// @title L1 Native token vault contract interface /// @author Matter Labs /// @custom:security-contact security@matterlabs.dev /// @notice The NTV is an Asset Handler for the L1AssetRouter to handle native tokens // is IL1AssetHandler, IL1BaseTokenAssetHandler { -interface IL1NativeTokenVault is INativeTokenVault { +interface IL1NativeTokenVault is INativeTokenVault, IL1AssetDeploymentTracker { /// @notice The L1Nullifier contract function L1_NULLIFIER() external view returns (IL1Nullifier); diff --git a/l1-contracts/contracts/bridge/ntv/L1NativeTokenVault.sol b/l1-contracts/contracts/bridge/ntv/L1NativeTokenVault.sol index f1d14834d..9fe19b2f4 100644 --- a/l1-contracts/contracts/bridge/ntv/L1NativeTokenVault.sol +++ b/l1-contracts/contracts/bridge/ntv/L1NativeTokenVault.sol @@ -20,6 +20,7 @@ import {IL1Nullifier} from "../interfaces/IL1Nullifier.sol"; import {IL1AssetRouter} from "../asset-router/IL1AssetRouter.sol"; import {ETH_TOKEN_ADDRESS} from "../../common/Config.sol"; +import {L2_NATIVE_TOKEN_VAULT_ADDR} from "../../common/L2ContractAddresses.sol"; import {DataEncoding} from "../../common/libraries/DataEncoding.sol"; import {Unauthorized, ZeroAddress, NoFundsTransferred, InsufficientChainBalance, WithdrawFailed} from "../../common/L1ContractErrors.sol"; @@ -123,6 +124,17 @@ contract L1NativeTokenVault is IL1NativeTokenVault, IL1AssetHandler, NativeToken L1_NULLIFIER.nullifyChainBalanceByNTV(_targetChainId, _token); } + /// @notice Used to register the Asset Handler asset in L2 AssetRouter. + /// @param _assetHandlerAddressOnCounterpart the address of the asset handler on the counterpart chain. + function bridgeCheckCounterpartAddress( + uint256, + bytes32, + address, + address _assetHandlerAddressOnCounterpart + ) external view override onlyAssetRouter { + require(_assetHandlerAddressOnCounterpart == L2_NATIVE_TOKEN_VAULT_ADDR, "NTV: wrong counterpart"); + } + /*////////////////////////////////////////////////////////////// Start transaction Functions //////////////////////////////////////////////////////////////*/ diff --git a/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol b/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol index 02e865d5d..c636f1a91 100644 --- a/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol +++ b/l1-contracts/contracts/bridge/ntv/NativeTokenVault.sol @@ -252,7 +252,7 @@ abstract contract NativeTokenVault is INativeTokenVault, IAssetHandler, Ownable2 } _handleChainBalanceIncrease(_chainId, _assetId, amount, true); if (_depositAmount != amount) { - revert ValueMismatch(amount, msg.value); + revert ValueMismatch(_depositAmount, amount); } } else { // The Bridgehub also checks this, but we want to be sure diff --git a/l1-contracts/contracts/common/L1ContractErrors.sol b/l1-contracts/contracts/common/L1ContractErrors.sol index 18766497f..f5a1b7631 100644 --- a/l1-contracts/contracts/common/L1ContractErrors.sol +++ b/l1-contracts/contracts/common/L1ContractErrors.sol @@ -95,8 +95,6 @@ error DepositDoesNotExist(); error DepositExists(); // 0x79cacff1 error DepositFailed(); -// 0xae08e4af -error DepositIncorrectAmount(uint256 expectedAmt, uint256 providedAmt); // 0x0e7ee319 error DiamondAlreadyFrozen(); // 0x682dabb4 diff --git a/l1-contracts/test/foundry/l1/unit/concrete/Bridges/L1SharedBridge/L1SharedBridgeFails.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/Bridges/L1SharedBridge/L1SharedBridgeFails.t.sol index 0131721a0..0603a34dc 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/Bridges/L1SharedBridge/L1SharedBridgeFails.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/Bridges/L1SharedBridge/L1SharedBridgeFails.t.sol @@ -8,6 +8,7 @@ import {L1AssetRouterTest} from "./_L1SharedBridge_Shared.t.sol"; import {TransparentUpgradeableProxy} from "@openzeppelin/contracts-v4/proxy/transparent/TransparentUpgradeableProxy.sol"; import {IERC20} from "@openzeppelin/contracts-v4/token/ERC20/IERC20.sol"; +import {SET_ASSET_HANDLER_COUNTERPART_ENCODING_VERSION} from "contracts/bridge/asset-router/IAssetRouterBase.sol"; import {L1AssetRouter} from "contracts/bridge/asset-router/L1AssetRouter.sol"; import {L1NativeTokenVault} from "contracts/bridge/ntv/L1NativeTokenVault.sol"; import {ETH_TOKEN_ADDRESS} from "contracts/common/Config.sol"; @@ -20,7 +21,7 @@ import {INativeTokenVault} from "contracts/bridge/ntv/INativeTokenVault.sol"; import {L1NativeTokenVault} from "contracts/bridge/ntv/L1NativeTokenVault.sol"; import {L2_BASE_TOKEN_SYSTEM_CONTRACT_ADDR} from "contracts/common/L2ContractAddresses.sol"; import {IGetters} from "contracts/state-transition/chain-interfaces/IGetters.sol"; -import {AddressAlreadyUsed, WithdrawFailed, Unauthorized, AssetIdNotSupported, SharedBridgeKey, SharedBridgeValueNotSet, L2WithdrawalMessageWrongLength, InsufficientChainBalance, ZeroAddress, ValueMismatch, NonEmptyMsgValue, DepositExists, ValueMismatch, NonEmptyMsgValue, TokenNotSupported, EmptyDeposit, L2BridgeNotDeployed, DepositIncorrectAmount, InvalidProof, NoFundsTransferred, InsufficientFunds, DepositDoesNotExist, WithdrawalAlreadyFinalized, InsufficientFunds, MalformedMessage, InvalidSelector, TokensWithFeesNotSupported} from "contracts/common/L1ContractErrors.sol"; +import {AddressAlreadyUsed, WithdrawFailed, Unauthorized, AssetIdNotSupported, SharedBridgeKey, SharedBridgeValueNotSet, L2WithdrawalMessageWrongLength, InsufficientChainBalance, ZeroAddress, ValueMismatch, NonEmptyMsgValue, DepositExists, ValueMismatch, NonEmptyMsgValue, TokenNotSupported, EmptyDeposit, L2BridgeNotDeployed, InvalidProof, NoFundsTransferred, InsufficientFunds, DepositDoesNotExist, WithdrawalAlreadyFinalized, InsufficientFunds, MalformedMessage, InvalidSelector, TokensWithFeesNotSupported} from "contracts/common/L1ContractErrors.sol"; import {StdStorage, stdStorage} from "forge-std/Test.sol"; /// We are testing all the specified revert and require cases. @@ -96,37 +97,23 @@ contract L1AssetRouterFailTest is L1AssetRouterTest { sharedBridge.setNativeTokenVault(INativeTokenVault(address(0))); } - // function test_setAssetHandlerAddressOnCounterpart_notOwnerOrADT() public { - // uint256 l2TxGasLimit = 100000; - // uint256 l2TxGasPerPubdataByte = 100; - // address refundRecipient = address(0); - - // vm.prank(alice); - // vm.expectRevert("L1N: only ADT or owner"); - // sharedBridge.setAssetHandlerAddressOnCounterpart( - // eraChainId, - // mintValue, - // l2TxGasLimit, - // l2TxGasPerPubdataByte, - // refundRecipient, - // tokenAssetId, - // address(token) - // ); - // } - - // function test_transferFundsToSharedBridge_Eth_CallFailed() public { - // vm.mockCall(address(nativeTokenVault), "0x", abi.encode("")); - // vm.prank(address(nativeTokenVault)); - // vm.expectRevert("L1N: eth transfer failed"); - // nativeTokenVault.transferFundsFromSharedBridge(ETH_TOKEN_ADDRESS); - // } - - // function test_transferFundsToSharedBridge_Eth_CallFailed() public { - // vm.mockCall(address(nativeTokenVault), "0x", abi.encode("")); - // vm.prank(address(nativeTokenVault)); - // vm.expectRevert("L1N: eth transfer failed"); - // nativeTokenVault.transferFundsFromSharedBridge(ETH_TOKEN_ADDRESS); - // } + function test_setAssetHandlerAddressOnCounterpart_wrongCounterPartAddress() public { + bytes memory data = bytes.concat( + SET_ASSET_HANDLER_COUNTERPART_ENCODING_VERSION, + abi.encode(tokenAssetId, address(token)) + ); + + vm.prank(bridgehubAddress); + vm.expectRevert("NTV: wrong counterpart"); + sharedBridge.bridgehubDeposit(eraChainId, owner, 0, data); + } + + function test_transferFundsToSharedBridge_Eth_CallFailed() public { + vm.mockCallRevert(address(nativeTokenVault), "", "eth transfer failed"); + vm.prank(address(nativeTokenVault)); + vm.expectRevert("L1N: eth transfer failed"); + l1Nullifier.transferTokenToNTV(ETH_TOKEN_ADDRESS); + } function test_transferFundsToSharedBridge_Eth_0_AmountTransferred() public { vm.deal(address(l1Nullifier), 0); @@ -155,12 +142,12 @@ contract L1AssetRouterFailTest is L1AssetRouterTest { sharedBridge.bridgehubDepositBaseToken{value: amount}(chainId, ETH_TOKEN_ASSET_ID, alice, amount); } - // function test_bridgehubDepositBaseToken_EthwrongMsgValue() public { - // vm.deal(bridgehubAddress, amount); - // vm.prank(bridgehubAddress); - // vm.expectRevert(abi.encodeWithSelector(ValueMismatch.selector, amount, uint256(1))); - // sharedBridge.bridgehubDepositBaseToken(chainId, ETH_TOKEN_ASSET_ID, alice, amount); - // } + function test_bridgehubDepositBaseToken_EthwrongMsgValue() public { + vm.deal(bridgehubAddress, amount); + vm.prank(bridgehubAddress); + vm.expectRevert(abi.encodeWithSelector(ValueMismatch.selector, amount, uint256(1))); + sharedBridge.bridgehubDepositBaseToken{value: 1}(chainId, ETH_TOKEN_ASSET_ID, alice, amount); + } function test_bridgehubDepositBaseToken_ErcWrongMsgValue() public { vm.deal(bridgehubAddress, amount); @@ -199,18 +186,18 @@ contract L1AssetRouterFailTest is L1AssetRouterTest { sharedBridge.bridgehubDeposit(chainId, alice, 0, abi.encode(ETH_TOKEN_ADDRESS, 0, bob)); } - // function test_bridgehubDeposit_Eth_wrongDepositAmount() public { - // _setBaseTokenAssetId(tokenAssetId); - // vm.prank(bridgehubAddress); - // vm.mockCall( - // bridgehubAddress, - // abi.encodeWithSelector(IBridgehub.baseTokenAssetId.selector), - // abi.encode(tokenAssetId) - // ); - // vm.expectRevert(abi.encodeWithSelector(DepositIncorrectAmount.selector, 0, amount)); - // // solhint-disable-next-line func-named-parameters - // sharedBridge.bridgehubDeposit(chainId, alice, 0, abi.encode(ETH_TOKEN_ADDRESS, amount, bob)); - // } + function test_bridgehubDeposit_Eth_wrongDepositAmount() public { + _setBaseTokenAssetId(tokenAssetId); + vm.prank(bridgehubAddress); + vm.mockCall( + bridgehubAddress, + abi.encodeWithSelector(IBridgehub.baseTokenAssetId.selector), + abi.encode(tokenAssetId) + ); + vm.expectRevert(abi.encodeWithSelector(ValueMismatch.selector, amount, 0)); + // solhint-disable-next-line func-named-parameters + sharedBridge.bridgehubDeposit(chainId, alice, 0, abi.encode(ETH_TOKEN_ADDRESS, amount, bob)); + } function test_bridgehubDeposit_Erc_msgValue() public { vm.prank(bridgehubAddress); @@ -623,27 +610,31 @@ contract L1AssetRouterFailTest is L1AssetRouterTest { }); } - // function test_finalizeWithdrawal_TokenOnEth_legacyUpgradeFirstBatchNotSet() public { - // vm.store(address(sharedBridge), bytes32(isWithdrawalFinalizedStorageLocation - 6), bytes32(uint256(0))); - // vm.deal(address(sharedBridge), amount); - - // bytes memory message = abi.encodePacked( - // IL1ERC20Bridge.finalizeWithdrawal.selector, - // alice, - // address(token), - // amount - // ); - - // vm.expectRevert(abi.encodeWithSelector(SharedBridgeValueNotSet.selector, SharedBridgeKey.PostUpgradeFirstBatch)); - // sharedBridge.finalizeWithdrawal({ - // _chainId: eraChainId, - // _l2BatchNumber: l2BatchNumber, - // _l2MessageIndex: l2MessageIndex, - // _l2TxNumberInBatch: l2TxNumberInBatch, - // _message: message, - // _merkleProof: merkleProof - // }); - // } + function test_finalizeWithdrawal_TokenOnEth_legacyUpgradeFirstBatchNotSet() public { + vm.store(address(l1Nullifier), bytes32(isWithdrawalFinalizedStorageLocation - 7), bytes32(uint256(0))); + vm.deal(address(nativeTokenVault), amount); + + bytes memory message = abi.encodePacked( + IL1ERC20Bridge.finalizeWithdrawal.selector, + alice, + address(token), + amount + ); + + vm.mockCall(bridgehubAddress, abi.encode(IBridgehub.proveL2MessageInclusion.selector), abi.encode(true)); + + vm.expectRevert( + abi.encodeWithSelector(SharedBridgeValueNotSet.selector, SharedBridgeKey.PostUpgradeFirstBatch) + ); + sharedBridge.finalizeWithdrawal({ + _chainId: eraChainId, + _l2BatchNumber: l2BatchNumber, + _l2MessageIndex: l2MessageIndex, + _l2TxNumberInBatch: l2TxNumberInBatch, + _message: message, + _merkleProof: merkleProof + }); + } function test_finalizeWithdrawal_chainBalance() public { bytes memory message = abi.encodePacked(IMailbox.finalizeEthWithdrawal.selector, alice, amount); diff --git a/l1-contracts/test/foundry/l1/unit/concrete/Executor/ExecutorProof.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/Executor/ExecutorProof.t.sol index af6e9f3a5..9f4530cc4 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/Executor/ExecutorProof.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/Executor/ExecutorProof.t.sol @@ -72,60 +72,55 @@ contract ExecutorProofTest is Test { utilsFacet = UtilsFacet(diamondProxy); } // todo - /// This test is based on a block generated in a local system. - // function test_Hashes() public { - // utilsFacet.util_setL2DefaultAccountBytecodeHash( - // 0x0100065d134a862a777e50059f5e0fbe68b583f3617a67820f7edda0d7f253a0 - // ); - // utilsFacet.util_setL2BootloaderBytecodeHash(0x010009416e909e0819593a9806bbc841d25c5cdfed3f4a1523497c6814e5194a); - // utilsFacet.util_setZkPorterAvailability(false); - - // IExecutor.CommitBatchInfo memory nextBatch = IExecutor.CommitBatchInfo({ - // // ignored - // batchNumber: 1, - // // ignored - // timestamp: 100, - // indexRepeatedStorageChanges: 84, - // newStateRoot: 0x9cf7bb72401a56039ca097cabed20a72221c944ed9b0e515c083c04663ab45a6, - // // ignored - // numberOfLayer1Txs: 10, - // // ignored - // priorityOperationsHash: 0x167f4ca80269c9520ad951eeeda28dd3deb0715e9e2917461e81a60120a14183, - // bootloaderHeapInitialContentsHash: 0x540442e48142fa061a81822184f7790e7b69dea92153d38ef623802c6f0411c0, - // eventsQueueStateHash: 0xda42ab7994d4695a25f4ea8a9a485a592b7a31c20d5dae6363828de86d8826ea, - // systemLogs: abi.encodePacked( - // hex"00000000000000000000000000000000000000000000800b000000000000000000000000000000000000000000000000000000000000000416914ac26bb9cafa0f1dfaeaab10745a9094e1b60c7076fedf21651d6a25b5740000000a000000000000000000000000000000000000800b0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000651bcde0000000000000000000000000651bcde20001000a00000000000000000000000000000000000080010000000000000000000000000000000000000000000000000000000000000005167f4ca80269c9520ad951eeeda28dd3deb0715e9e2917461e81a60120a141830001000a00000000000000000000000000000000000080010000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0001000a00000000000000000000000000000000000080080000000000000000000000000000000000000000000000000000000000000000ee6ee8f50659bd8be3d86c32efb02baa5571cf3b46dd7ea3db733ae181747b8b0001000a0000000000000000000000000000000000008008000000000000000000000000000000000000000000000000000000000000000160fc5fb513ca8e6f6232a7410797954dcb6edbf9081768da24b483aca91c54db0001000a000000000000000000000000000000000000800800000000000000000000000000000000000000000000000000000000000000029a67073c2df8f53087fcfc32d82c98bba591da35df6ce1fb55a23b677d37f9fc000000000000000000000000000000000000000000008011000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080110000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000801100000000000000000000000000000000000000000000000000000000000000090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008011000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008011000000000000000000000000000000000000000000000000000000000000000b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008011000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000" - // ), - // operatorDAInput: abi.encodePacked( - // hex"000000000a000100000000000000000000000000000000000000008001760f6100ddbd86c4d5a58532923e7424d33ffb44145a26171d9b2595a349450b0000000000000000000000000000000000000000000000000000000000000001000100010000000000000000000000000000000000008001a789fe4e2a955eee45d44f408f86203c8f643910bf4888d1fd1465cdbc6376d800000000000000000000000000000000000000000000000000000000000000010001000200000000000000000000000000000000000080016ba43e7c7df11e5a655f22c9bce1b37434afd2bf8fcdb10100a460e6a2c0cc83000000000000000000000000000000000000000000000000000000000000000100010003000000000000000000000000000000000000800156e569838658c17c756aa9f6e40de8f1c41b1a67fea5214ec47869882ecda9bd0000000000000000000000000000000000000000000000000000000000000001000100040000000000000000000000000000000000008001ab5d064ba75c02635fd6e4de7fd8420eda54c4bda05bd61edabe201f2066d38f00000000000000000000000000000000000000000000000000000000000000010001000500000000000000000000000000000000000080015bcb6d7c735023e0884297db5016a6c704e3490ed0671417639313ecea86795b00000000000000000000000000000000000000000000000000000000000000010001000600000000000000000000000000000000000080015ee51b5b7d47fae5811a9f777174bb08d81d78098c8bd9430a7618756a0ceb8b00000000000000000000000000000000000000000000000000000000000000010001000700000000000000000000000000000000000080011ea63171021b9ab0846efbe0a06f7882d76e24a4900c74c14fa1e0bdf313ed560000000000000000000000000000000000000000000000000000000000000001000100080000000000000000000000000000000000008001574537f1665cd9c894d8d9834d32ed291f49ae1165a0e12a79a4937f2425bf70000000000000000000000000000000000000000000000000000000000000000100010009000000000000000000000000000000000000800190558033c8a3f7c20c81e613e00a9d0e678a7a14923e94e7cb99c8621c7918090000000000000000000000000000000000000000000000000000000000000001000000000000000001000c3104003d1291725c657fe486d0e626f562842175a705a9704c0980b40e3d716b95bbf9e8000100005dd96deb789fbc05264165795bf652190645bfae1ce253ce1db17087a898fb1e240ebf0d53563011198fddab33312923ba20f3c56cf1ba18ca5be9c053000100022bd65a924da61271d1dd5080fc640601185125830805e0ceb42f4185e5118fb454a12a3d9e0c1fbb89230f67044cc191e4f18459261233f659c9e2ba5e000100008b9feb52993729436da78b2863dd56d8d757e19c01a2cdcf1940e45ca9979941fa93f5f699afeab75e8b25cfea22004a8d2ea49f057741c2f2b910996d00010001bdf9205fb9bd185829f2c6bec2a6f100b86eff579da4fc2a8f1a15ea4afee3cea48e96b9bddb544b4569e60736a1f1fe919e223fcc08f74acf3513be1200010001bdf9205fb9bd185829f2c6bec2a6f100b86eff579da4fc2a8f1a15ea4a8755061217b6a78f5d5f8af6e326e482ebdc57f7144108662d122252ddcc27e7000100045dddc527887dc39b9cd189d6f183f16217393a5d3d3165fead2daeaf4f2d6916280c572561a809555de4a87d7a56d5bcca2c246a389dbb2a24c5639bdb0001000153c0f36532563ba2a10f52b865e558cd1a5eef9a9edd01c1cb23b74aa772beb4f3e3b784609f4e205a09863c0587e63b4b47664022cb34896a1711416b00010003e7842b0b4f4fd8e665883fe9c158ba8d38347840f1da0a75aca1fc284ce2428454b48df9f5551500fc50b63af4741b1cd21d4cfddc69aa46cb78eff45b00010000f183703a165afed04326ad5786316f6fc65b27f1cf17459a52bd1f57f27f896b7429e070ca76e3e33165ec75f6c9f439ee37f3b58822494b1251c8247500010001bdf9205fb9bd185829f2c6bec2a6f100b86eff579da4fc2a8f1a15ea4a05ea3d0bb218598c42b2e25ae5f6cbc9369b273ee6610450cade89775646b2a08902000000000000000000000000000000008b71d4a184058d07fccac4348ae02a1f663403231b0a40fa2c8c0ff73bdca092890200000000000000000000000000000000ab63c4cebbd508a7d7184f0b9134453eea7a09ca749610d5576f8046241b9cde890200000000000000000000000000000000e58af14be53d8ac56f58ff3e5b07c239bfb549149f067597e9d028f35e3c2b77890200000000000000000000000000000000b78e94980fec3a5f68aa25d0d934084907688e537e82c2942af905aab21413ab890200000000000000000000000000000000c4db460819691e825328b532024bbecdc40394c74307a00bd245fc658b1bd34f0901908827f2052a14b24a10cae1f9e259ead06a89a1d74ff736a54f54ebcf05eeb30901d32d07305b87debd25698d4dfac4c2f986693a4e9d9baff7da37a7b5ca8d01cb0901e73042e5dacff2ce20a720c9c6d694576e4afa7bbbafdc4d409c63b7ca8027b70901760a7405795441aceea3be649a53d02785cb3487b7bd23e3b4888a935cee010d09011f3acf5d6d7bfeab8a7112771866e28c3714e0c315a81ec6a58ab4ad1c3d6eb10901c207b49d14deb3af9bc960d57074e27386285c73248abc5fa1d72aa6e8664fa40901644f0c4e15446d7e5ff363c944b55bd6801a1f38afd984c3427569530cb663210901743be0243628b8e7e8f04c00fc4f88efae001250a7482b31e6a0ec87ee3598e7090171e91721f9918576d760f02f03cac47c6f4003316031848e3c1d99e6e83a47434102d84e69f2f480002d5a6962cccee5d4adb48a36bbbf443a531721484381125937f3001ac5ff875b41022f496efbbdb2007b727eb806c926fb20c8ad087c57422977cebd06373e26d19b640e5fe32c85ff39a904faf736ce00a25420c1d9d705358c134cc601d9d184cb4dfdde7e1cac2bc3d4d38bf9ec44e6210ee6b280123bafc586f77764488cd24c6a77546e5a0fe8bdfb4fa203cfaffc36cce4dd5b8901000000000000000000000000651bcde08e7dd06ac5b73b473be6bc5a51030f4c7437657cb7b29bf376c564b8d1675a5e8903000000000000000000000000651bcde24ba84e1f37d041bc6e55ba396826cc494e84d4815b6db52690422eea7386314f00e8e77626586f73b955364c7b4bbf0bb7f7685ebd40e852b164633a4acbd3244c3de2202ccb626ad387d70722e64fbe44562e2f231a290c08532b8d6aba402ff50025fe002039e87b424de2772b82d935f14e2b657429a1bcc04612391ea0330c90ebddefdda48eb2aa7f66ecf7940a280e9ef3fb2e95db0995538440a62af79861004434720529e816fd2e40f8031a8d7471ebcd00351db0787346bcfe8dfad8d2b479093588d0e847efa73a10ce20e4799fb1e46642d65617c7e5213fa04989d92d8903000000000000000000000000651bcde287ded247e1660f827071c7f1371934589751085384fc9f4462c1f1897c5c3eef890100000000000000000000000000000001911dd2ad743ff237d411648a0fe32c6d74eec060716a2a74352f6b1c435b5d670016914ac26bb9cafa0f1dfaeaab10745a9094e1b60c7076fedf21651d6a25b574686a068c708f1bdbefd9e6e454ac2b520fd41c8dcf23ecd4cee978c22f1c1f5f09ff974fe8b575175cefa919a5ba1c0ddf4409be4b16695dc7bd12f6701b99bd2e70a152312ad6f01657413b2eae9287f6b9adad93d5fed1a0dd5e13ec74ce1163146509bfe426f2315a69cb452bf388cccd321eca2746a1adf793b489e5c8f61c40688b7ef3e53defc56c78facf513e511f9f5ba0eb50dbcc745afea3b860da75b394d2d1627b6e2ef54fb7b187d0af61e4532c238f387ecf9f0b466f1d54414100018e519b65c8901b344a480638beadb923fbd3462e475d39acebe559d65ed5cb11a1b25279f1918477c35eec1332ff07001d3f85cf854b70d7552f93ba8e88d581064ca4c0df6ac456c00a0e83898ccd464c63e5008aa1a498cc0646b78eb216d9eeeec76ed0eb0ee6c352f35ca5f0b2edc2ca17d211cc5cb905ba10142f042a6ac836d9cef9a6916635c9a1c1d2dc62a9fe83e2230b506b98e0fded46249008fe28b813907a05ae0d773d8f31e330200e9336e0159034c137ed645fb67ccca8a152312ad6f01657413b2eae9287f6b9adad93d5fee5d8f810abde496ccbeb45a4f3c06af828975163a006257cbf18cefebbfb4cd409025f40404a3d37bba024799ce32d7c2a833aec8474288a26b246afa32b07b4a3ce00577261707065642045746865720000000000000000000000000000000000001a09cf14f266dfe87c4b33e6d934de01f8f7242199fa8783178117218fa033f7ab005745544800000000000000000000000000000000000000000000000000000008289026c5fa173652bd62774824698a6848c63031f853d0e275174552f35df33000577261707065642045746865720000000000000000000000000000000000001a1e59309944cbc900ae848855e10bc929f78e86c2179d6e96cf52bfd520f039200031000000000000000000000000000000000000000000000000000000000000021653a735395136e5494c5426ba972b45e34d36ebcb86ac104c724ab375fcce90a18580ba6aeebc6e6b89d226c79be8927257a436ad11d9c0305b18e9d78cab8f75a3aec2096302b67e3815939e29476fb36a0d8299a1b25279f1918477c35eec1332ff07001d3f85cf85688525f98e4859a9c6939f2d2f92e6b1950ed57e56137d717aca1ccf9754f719a1c7ebe9226d26524400a8959a08f411a727ae7bb68f8febecd89ffe9d84708d24544d452de3e22e62b3b2b872e430839a15115818a152312ad6f01657413b2eae9287f6b9adad93d5fe3fb60af355125687beeb90c066ace76c442b0f963a6afd0e3316fcdd673ad22c09ff30c8a03ec44e5337a1f9d66763cf1b319fdc6d8bc4981e1f47edbd86210614b909ff0cbdceb634b81192417b64d114d535ad3bdba97d6d7e90ee2a79bf1c132d3c2d09ff5cd85060f4ff26eb5b68a6687aee76c1b7a77575fdc86ba49b4faf5041377a79b14de8989f2385a6e23f6bd05a80e0d9231870c15a000142e50adc0d84bff439d0086d9fbab9984f8b27aa208935238a60cc62e7c9bb2ea1709e94c96366b3c40ea4854837c18733e5ac1193b8d8e4070d2eca4441b0378b572bd949ab764fd71c002b759613c3e29d425cf4000100012730c940a81021004e899c6ee4bec02f0667757b9d75a8f0714ce6c157f5940b7664e4f69f01fc530db36965e33599a1348629f07ae2d724007ac36a71a16baac84db583d88e0f3a8c082e3632fcc0e15757f0dcf5234b87af41fdee4c0999c4fe698a8d824415979ab839e6913a975a3055a152312ad6f01657413b2eae9287f6b9adad93d5fe00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - // ) - // }); - // LogProcessingOutput memory logOutput = executor.processL2Logs( - // nextBatch, - // 0x0000000000000000000000000000000000000000000000000000000000000000 - // ); - // assertEq( - // logOutput.stateDiffHash, - // 0x9a67073c2df8f53087fcfc32d82c98bba591da35df6ce1fb55a23b677d37f9fc, - // "stateDiffHash computation failed" - // ); - - // bytes32 nextCommitment = executor.createBatchCommitment( - // nextBatch, - // logOutput.stateDiffHash, - // new bytes32[](6), - // new bytes32[](6) - // ); - // assertEq( - // nextCommitment, - // 0xa1dcde434352cda8e331e721232ff2d457d4074efae1e3d06ef5b10ffada0c9a, - // "nextCommitment computation failed" - // ); - - // bytes32 prevCommitment = 0x6ebf945305689a8c3ac993df7f002d41d311a762cd6bf39bb054ead8d1f54404; - // uint256 result = executor.getBatchProofPublicInput(prevCommitment, nextCommitment); - // assertEq(result, 0xAC7931F2C11013FC24963E41B86E5325A79F1150350CB41E4F0876A7, "getBatchProofPublicInput"); - // } + // This test is based on a block generated in a local system. + function test_Hashes() public { + utilsFacet.util_setL2DefaultAccountBytecodeHash( + 0x0100065d134a862a777e50059f5e0fbe68b583f3617a67820f7edda0d7f253a0 + ); + utilsFacet.util_setL2BootloaderBytecodeHash(0x010009416e909e0819593a9806bbc841d25c5cdfed3f4a1523497c6814e5194a); + utilsFacet.util_setZkPorterAvailability(false); + + bytes[] memory mockSystemLogs = Utils.createSystemLogsWithEmptyDAValidator(); + + IExecutor.CommitBatchInfo memory nextBatch = IExecutor.CommitBatchInfo({ + // ignored + batchNumber: 1, + // ignored + timestamp: 100, + indexRepeatedStorageChanges: 84, + newStateRoot: 0x9cf7bb72401a56039ca097cabed20a72221c944ed9b0e515c083c04663ab45a6, + // ignored + numberOfLayer1Txs: 10, + // ignored + priorityOperationsHash: 0x167f4ca80269c9520ad951eeeda28dd3deb0715e9e2917461e81a60120a14183, + bootloaderHeapInitialContentsHash: 0x540442e48142fa061a81822184f7790e7b69dea92153d38ef623802c6f0411c0, + eventsQueueStateHash: 0xda42ab7994d4695a25f4ea8a9a485a592b7a31c20d5dae6363828de86d8826ea, + systemLogs: Utils.encodePacked(mockSystemLogs), + operatorDAInput: abi.encodePacked( + hex"000000000a000100000000000000000000000000000000000000008001760f6100ddbd86c4d5a58532923e7424d33ffb44145a26171d9b2595a349450b0000000000000000000000000000000000000000000000000000000000000001000100010000000000000000000000000000000000008001a789fe4e2a955eee45d44f408f86203c8f643910bf4888d1fd1465cdbc6376d800000000000000000000000000000000000000000000000000000000000000010001000200000000000000000000000000000000000080016ba43e7c7df11e5a655f22c9bce1b37434afd2bf8fcdb10100a460e6a2c0cc83000000000000000000000000000000000000000000000000000000000000000100010003000000000000000000000000000000000000800156e569838658c17c756aa9f6e40de8f1c41b1a67fea5214ec47869882ecda9bd0000000000000000000000000000000000000000000000000000000000000001000100040000000000000000000000000000000000008001ab5d064ba75c02635fd6e4de7fd8420eda54c4bda05bd61edabe201f2066d38f00000000000000000000000000000000000000000000000000000000000000010001000500000000000000000000000000000000000080015bcb6d7c735023e0884297db5016a6c704e3490ed0671417639313ecea86795b00000000000000000000000000000000000000000000000000000000000000010001000600000000000000000000000000000000000080015ee51b5b7d47fae5811a9f777174bb08d81d78098c8bd9430a7618756a0ceb8b00000000000000000000000000000000000000000000000000000000000000010001000700000000000000000000000000000000000080011ea63171021b9ab0846efbe0a06f7882d76e24a4900c74c14fa1e0bdf313ed560000000000000000000000000000000000000000000000000000000000000001000100080000000000000000000000000000000000008001574537f1665cd9c894d8d9834d32ed291f49ae1165a0e12a79a4937f2425bf70000000000000000000000000000000000000000000000000000000000000000100010009000000000000000000000000000000000000800190558033c8a3f7c20c81e613e00a9d0e678a7a14923e94e7cb99c8621c7918090000000000000000000000000000000000000000000000000000000000000001000000000000000001000c3104003d1291725c657fe486d0e626f562842175a705a9704c0980b40e3d716b95bbf9e8000100005dd96deb789fbc05264165795bf652190645bfae1ce253ce1db17087a898fb1e240ebf0d53563011198fddab33312923ba20f3c56cf1ba18ca5be9c053000100022bd65a924da61271d1dd5080fc640601185125830805e0ceb42f4185e5118fb454a12a3d9e0c1fbb89230f67044cc191e4f18459261233f659c9e2ba5e000100008b9feb52993729436da78b2863dd56d8d757e19c01a2cdcf1940e45ca9979941fa93f5f699afeab75e8b25cfea22004a8d2ea49f057741c2f2b910996d00010001bdf9205fb9bd185829f2c6bec2a6f100b86eff579da4fc2a8f1a15ea4afee3cea48e96b9bddb544b4569e60736a1f1fe919e223fcc08f74acf3513be1200010001bdf9205fb9bd185829f2c6bec2a6f100b86eff579da4fc2a8f1a15ea4a8755061217b6a78f5d5f8af6e326e482ebdc57f7144108662d122252ddcc27e7000100045dddc527887dc39b9cd189d6f183f16217393a5d3d3165fead2daeaf4f2d6916280c572561a809555de4a87d7a56d5bcca2c246a389dbb2a24c5639bdb0001000153c0f36532563ba2a10f52b865e558cd1a5eef9a9edd01c1cb23b74aa772beb4f3e3b784609f4e205a09863c0587e63b4b47664022cb34896a1711416b00010003e7842b0b4f4fd8e665883fe9c158ba8d38347840f1da0a75aca1fc284ce2428454b48df9f5551500fc50b63af4741b1cd21d4cfddc69aa46cb78eff45b00010000f183703a165afed04326ad5786316f6fc65b27f1cf17459a52bd1f57f27f896b7429e070ca76e3e33165ec75f6c9f439ee37f3b58822494b1251c8247500010001bdf9205fb9bd185829f2c6bec2a6f100b86eff579da4fc2a8f1a15ea4a05ea3d0bb218598c42b2e25ae5f6cbc9369b273ee6610450cade89775646b2a08902000000000000000000000000000000008b71d4a184058d07fccac4348ae02a1f663403231b0a40fa2c8c0ff73bdca092890200000000000000000000000000000000ab63c4cebbd508a7d7184f0b9134453eea7a09ca749610d5576f8046241b9cde890200000000000000000000000000000000e58af14be53d8ac56f58ff3e5b07c239bfb549149f067597e9d028f35e3c2b77890200000000000000000000000000000000b78e94980fec3a5f68aa25d0d934084907688e537e82c2942af905aab21413ab890200000000000000000000000000000000c4db460819691e825328b532024bbecdc40394c74307a00bd245fc658b1bd34f0901908827f2052a14b24a10cae1f9e259ead06a89a1d74ff736a54f54ebcf05eeb30901d32d07305b87debd25698d4dfac4c2f986693a4e9d9baff7da37a7b5ca8d01cb0901e73042e5dacff2ce20a720c9c6d694576e4afa7bbbafdc4d409c63b7ca8027b70901760a7405795441aceea3be649a53d02785cb3487b7bd23e3b4888a935cee010d09011f3acf5d6d7bfeab8a7112771866e28c3714e0c315a81ec6a58ab4ad1c3d6eb10901c207b49d14deb3af9bc960d57074e27386285c73248abc5fa1d72aa6e8664fa40901644f0c4e15446d7e5ff363c944b55bd6801a1f38afd984c3427569530cb663210901743be0243628b8e7e8f04c00fc4f88efae001250a7482b31e6a0ec87ee3598e7090171e91721f9918576d760f02f03cac47c6f4003316031848e3c1d99e6e83a47434102d84e69f2f480002d5a6962cccee5d4adb48a36bbbf443a531721484381125937f3001ac5ff875b41022f496efbbdb2007b727eb806c926fb20c8ad087c57422977cebd06373e26d19b640e5fe32c85ff39a904faf736ce00a25420c1d9d705358c134cc601d9d184cb4dfdde7e1cac2bc3d4d38bf9ec44e6210ee6b280123bafc586f77764488cd24c6a77546e5a0fe8bdfb4fa203cfaffc36cce4dd5b8901000000000000000000000000651bcde08e7dd06ac5b73b473be6bc5a51030f4c7437657cb7b29bf376c564b8d1675a5e8903000000000000000000000000651bcde24ba84e1f37d041bc6e55ba396826cc494e84d4815b6db52690422eea7386314f00e8e77626586f73b955364c7b4bbf0bb7f7685ebd40e852b164633a4acbd3244c3de2202ccb626ad387d70722e64fbe44562e2f231a290c08532b8d6aba402ff50025fe002039e87b424de2772b82d935f14e2b657429a1bcc04612391ea0330c90ebddefdda48eb2aa7f66ecf7940a280e9ef3fb2e95db0995538440a62af79861004434720529e816fd2e40f8031a8d7471ebcd00351db0787346bcfe8dfad8d2b479093588d0e847efa73a10ce20e4799fb1e46642d65617c7e5213fa04989d92d8903000000000000000000000000651bcde287ded247e1660f827071c7f1371934589751085384fc9f4462c1f1897c5c3eef890100000000000000000000000000000001911dd2ad743ff237d411648a0fe32c6d74eec060716a2a74352f6b1c435b5d670016914ac26bb9cafa0f1dfaeaab10745a9094e1b60c7076fedf21651d6a25b574686a068c708f1bdbefd9e6e454ac2b520fd41c8dcf23ecd4cee978c22f1c1f5f09ff974fe8b575175cefa919a5ba1c0ddf4409be4b16695dc7bd12f6701b99bd2e70a152312ad6f01657413b2eae9287f6b9adad93d5fed1a0dd5e13ec74ce1163146509bfe426f2315a69cb452bf388cccd321eca2746a1adf793b489e5c8f61c40688b7ef3e53defc56c78facf513e511f9f5ba0eb50dbcc745afea3b860da75b394d2d1627b6e2ef54fb7b187d0af61e4532c238f387ecf9f0b466f1d54414100018e519b65c8901b344a480638beadb923fbd3462e475d39acebe559d65ed5cb11a1b25279f1918477c35eec1332ff07001d3f85cf854b70d7552f93ba8e88d581064ca4c0df6ac456c00a0e83898ccd464c63e5008aa1a498cc0646b78eb216d9eeeec76ed0eb0ee6c352f35ca5f0b2edc2ca17d211cc5cb905ba10142f042a6ac836d9cef9a6916635c9a1c1d2dc62a9fe83e2230b506b98e0fded46249008fe28b813907a05ae0d773d8f31e330200e9336e0159034c137ed645fb67ccca8a152312ad6f01657413b2eae9287f6b9adad93d5fee5d8f810abde496ccbeb45a4f3c06af828975163a006257cbf18cefebbfb4cd409025f40404a3d37bba024799ce32d7c2a833aec8474288a26b246afa32b07b4a3ce00577261707065642045746865720000000000000000000000000000000000001a09cf14f266dfe87c4b33e6d934de01f8f7242199fa8783178117218fa033f7ab005745544800000000000000000000000000000000000000000000000000000008289026c5fa173652bd62774824698a6848c63031f853d0e275174552f35df33000577261707065642045746865720000000000000000000000000000000000001a1e59309944cbc900ae848855e10bc929f78e86c2179d6e96cf52bfd520f039200031000000000000000000000000000000000000000000000000000000000000021653a735395136e5494c5426ba972b45e34d36ebcb86ac104c724ab375fcce90a18580ba6aeebc6e6b89d226c79be8927257a436ad11d9c0305b18e9d78cab8f75a3aec2096302b67e3815939e29476fb36a0d8299a1b25279f1918477c35eec1332ff07001d3f85cf85688525f98e4859a9c6939f2d2f92e6b1950ed57e56137d717aca1ccf9754f719a1c7ebe9226d26524400a8959a08f411a727ae7bb68f8febecd89ffe9d84708d24544d452de3e22e62b3b2b872e430839a15115818a152312ad6f01657413b2eae9287f6b9adad93d5fe3fb60af355125687beeb90c066ace76c442b0f963a6afd0e3316fcdd673ad22c09ff30c8a03ec44e5337a1f9d66763cf1b319fdc6d8bc4981e1f47edbd86210614b909ff0cbdceb634b81192417b64d114d535ad3bdba97d6d7e90ee2a79bf1c132d3c2d09ff5cd85060f4ff26eb5b68a6687aee76c1b7a77575fdc86ba49b4faf5041377a79b14de8989f2385a6e23f6bd05a80e0d9231870c15a000142e50adc0d84bff439d0086d9fbab9984f8b27aa208935238a60cc62e7c9bb2ea1709e94c96366b3c40ea4854837c18733e5ac1193b8d8e4070d2eca4441b0378b572bd949ab764fd71c002b759613c3e29d425cf4000100012730c940a81021004e899c6ee4bec02f0667757b9d75a8f0714ce6c157f5940b7664e4f69f01fc530db36965e33599a1348629f07ae2d724007ac36a71a16baac84db583d88e0f3a8c082e3632fcc0e15757f0dcf5234b87af41fdee4c0999c4fe698a8d824415979ab839e6913a975a3055a152312ad6f01657413b2eae9287f6b9adad93d5fe00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + ) + }); + LogProcessingOutput memory logOutput = executor.processL2Logs( + nextBatch, + 0x0000000000000000000000000000000000000000000000000000000000000000 + ); + + bytes32 nextCommitment = executor.createBatchCommitment( + nextBatch, + logOutput.stateDiffHash, + new bytes32[](16), + new bytes32[](16) + ); + assertEq( + nextCommitment, + 0x81e46ea22cdb4a0a6cb30b6c02170394703e9bdd101275d542a7c6c23c789898, + "nextCommitment computation failed" + ); + + bytes32 prevCommitment = 0x6ebf945305689a8c3ac993df7f002d41d311a762cd6bf39bb054ead8d1f54404; + uint256 result = executor.getBatchProofPublicInput(prevCommitment, nextCommitment); + assertEq(result, 0x7C854720CBA105B9E34DA6A28770B93AD384C1BF98C497CCBFA4DADB, "getBatchProofPublicInput"); + } // add this to be excluded from coverage report function test() internal {} diff --git a/l1-contracts/test/foundry/l1/unit/concrete/Utils/Utils.sol b/l1-contracts/test/foundry/l1/unit/concrete/Utils/Utils.sol index 8ab52c976..b19dcb3b3 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/Utils/Utils.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/Utils/Utils.sol @@ -108,6 +108,18 @@ library Utils { return logs; } + function createSystemLogsWithEmptyDAValidator() public returns (bytes[] memory) { + bytes[] memory systemLogs = createSystemLogs(bytes32(0)); + systemLogs[uint256(SystemLogKey.USED_L2_DA_VALIDATOR_ADDRESS_KEY)] = constructL2Log( + true, + L2_TO_L1_MESSENGER, + uint256(SystemLogKey.USED_L2_DA_VALIDATOR_ADDRESS_KEY), + bytes32(uint256(0)) + ); + + return systemLogs; + } + function createSystemLogsWithUpgradeTransaction( bytes32 _expectedSystemContractUpgradeTxHash ) public returns (bytes[] memory) { @@ -125,6 +137,30 @@ library Utils { return logs; } + function createSystemLogsWithUpgradeTransactionForCTM( + bytes32 _expectedSystemContractUpgradeTxHash, + bytes32 _outputHash + ) public returns (bytes[] memory) { + bytes[] memory logsWithoutUpgradeTx = createSystemLogs(_outputHash); + bytes[] memory logs = new bytes[](logsWithoutUpgradeTx.length + 1); + for (uint256 i = 0; i < logsWithoutUpgradeTx.length; i++) { + logs[i] = logsWithoutUpgradeTx[i]; + } + logs[uint256(SystemLogKey.PREV_BATCH_HASH_KEY)] = constructL2Log( + true, + L2_SYSTEM_CONTEXT_ADDRESS, + uint256(SystemLogKey.PREV_BATCH_HASH_KEY), + bytes32(uint256(0x01)) + ); + logs[logsWithoutUpgradeTx.length] = constructL2Log( + true, + L2_BOOTLOADER_ADDRESS, + uint256(SystemLogKey.EXPECTED_SYSTEM_CONTRACT_UPGRADE_TX_HASH_KEY), + _expectedSystemContractUpgradeTxHash + ); + return logs; + } + function createStoredBatchInfo() public pure returns (IExecutor.StoredBatchInfo memory) { return IExecutor.StoredBatchInfo({ @@ -198,7 +234,7 @@ library Utils { } function getAdminSelectors() public pure returns (bytes4[] memory) { - bytes4[] memory selectors = new bytes4[](12); + bytes4[] memory selectors = new bytes4[](13); selectors[0] = AdminFacet.setPendingAdmin.selector; selectors[1] = AdminFacet.acceptAdmin.selector; selectors[2] = AdminFacet.setValidator.selector; @@ -211,6 +247,7 @@ library Utils { selectors[9] = AdminFacet.freezeDiamond.selector; selectors[10] = AdminFacet.unfreezeDiamond.selector; selectors[11] = AdminFacet.genesisUpgrade.selector; + selectors[12] = AdminFacet.setDAValidatorPair.selector; return selectors; } diff --git a/l1-contracts/test/foundry/l1/unit/concrete/state-transition/ChainTypeManager/CreateNewChain.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/state-transition/ChainTypeManager/CreateNewChain.t.sol index 81659b682..51a4bb521 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/state-transition/ChainTypeManager/CreateNewChain.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/state-transition/ChainTypeManager/CreateNewChain.t.sol @@ -5,6 +5,7 @@ import {ChainTypeManagerTest} from "./_ChainTypeManager_Shared.t.sol"; import {Diamond} from "contracts/state-transition/libraries/Diamond.sol"; import {DataEncoding} from "contracts/common/libraries/DataEncoding.sol"; import {Unauthorized, HashMismatch} from "contracts/common/L1ContractErrors.sol"; +import {IZKChain} from "contracts/state-transition/chain-interfaces/IZKChain.sol"; contract createNewChainTest is ChainTypeManagerTest { function setUp() public { @@ -38,4 +39,13 @@ contract createNewChainTest is ChainTypeManagerTest { _factoryDeps: new bytes[](0) }); } + + function test_SuccessfulCreationOfNewChain() public { + address newChainAddress = createNewChain(getDiamondCutData(diamondInit)); + + address admin = IZKChain(newChainAddress).getAdmin(); + + assertEq(newChainAdmin, admin); + assertNotEq(newChainAddress, address(0)); + } } diff --git a/l1-contracts/test/foundry/l1/unit/concrete/state-transition/ChainTypeManager/FreezeChain.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/state-transition/ChainTypeManager/FreezeChain.t.sol index d92349a61..73a2dc498 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/state-transition/ChainTypeManager/FreezeChain.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/state-transition/ChainTypeManager/FreezeChain.t.sol @@ -5,22 +5,31 @@ import {ChainTypeManagerTest} from "./_ChainTypeManager_Shared.t.sol"; import {GettersFacet} from "contracts/state-transition/chain-deps/facets/Getters.sol"; import {IAdmin} from "contracts/state-transition/chain-interfaces/IAdmin.sol"; import {FacetIsFrozen} from "contracts/common/L1ContractErrors.sol"; +import {IBridgehub} from "contracts/bridgehub/IBridgehub.sol"; contract freezeChainTest is ChainTypeManagerTest { - // function test_FreezingChain() public { - // createNewChain(getDiamondCutData(diamondInit)); - // address newChainAddress = chainContractAddress.getZKChain(chainId); - // GettersFacet gettersFacet = GettersFacet(newChainAddress); - // bool isChainFrozen = gettersFacet.isDiamondStorageFrozen(); - // assertEq(isChainFrozen, false); - // vm.stopPrank(); - // vm.startPrank(governor); - // chainContractAddress.freezeChain(block.chainid); - // // Repeated call should revert - // vm.expectRevert(bytes("q1")); // storage frozen - // chainContractAddress.freezeChain(block.chainid); - // // Call fails as storage is frozen - // vm.expectRevert(bytes("q1")); - // isChainFrozen = gettersFacet.isDiamondStorageFrozen(); - // } + function setUp() public { + deploy(); + } + + function test_FreezingChain() public { + address newChainAddress = createNewChain(getDiamondCutData(diamondInit)); + vm.mockCall( + address(bridgehub), + abi.encodeWithSelector(IBridgehub.getZKChain.selector), + abi.encode(newChainAddress) + ); + GettersFacet gettersFacet = GettersFacet(newChainAddress); + bool isChainFrozen = gettersFacet.isDiamondStorageFrozen(); + assertEq(isChainFrozen, false); + vm.stopPrank(); + vm.startPrank(governor); + chainContractAddress.freezeChain(block.chainid); + // Repeated call should revert + vm.expectRevert(bytes("q1")); // storage frozen + chainContractAddress.freezeChain(block.chainid); + // Call fails as storage is frozen + vm.expectRevert(bytes("q1")); + isChainFrozen = gettersFacet.isDiamondStorageFrozen(); + } } diff --git a/l1-contracts/test/foundry/l1/unit/concrete/state-transition/ChainTypeManager/RevertBatches.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/state-transition/ChainTypeManager/RevertBatches.t.sol index cdac3e776..610c6c1a3 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/state-transition/ChainTypeManager/RevertBatches.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/state-transition/ChainTypeManager/RevertBatches.t.sol @@ -3,15 +3,24 @@ pragma solidity 0.8.24; import {Vm} from "forge-std/Test.sol"; -import {Utils, L2_SYSTEM_CONTEXT_ADDRESS} from "../../Utils/Utils.sol"; +import {SafeCast} from "@openzeppelin/contracts-v4/utils/math/SafeCast.sol"; + +import {Utils, L2_SYSTEM_CONTEXT_ADDRESS, L2_DA_VALIDATOR_ADDRESS} from "../../Utils/Utils.sol"; import {ChainTypeManagerTest} from "./_ChainTypeManager_Shared.t.sol"; -import {COMMIT_TIMESTAMP_NOT_OLDER, DEFAULT_L2_LOGS_TREE_ROOT_HASH, EMPTY_STRING_KECCAK} from "contracts/common/Config.sol"; -import {IExecutor, SystemLogKey} from "contracts/state-transition/chain-interfaces/IExecutor.sol"; +import {COMMIT_TIMESTAMP_NOT_OLDER, DEFAULT_L2_LOGS_TREE_ROOT_HASH, EMPTY_STRING_KECCAK, POINT_EVALUATION_PRECOMPILE_ADDR, REQUIRED_L2_GAS_PRICE_PER_PUBDATA, SYSTEM_UPGRADE_L2_TX_TYPE, PRIORITY_TX_MAX_GAS_LIMIT} from "contracts/common/Config.sol"; +import {L2_FORCE_DEPLOYER_ADDR, L2_COMPLEX_UPGRADER_ADDR, L2_GENESIS_UPGRADE_ADDR} from "contracts/common/L2ContractAddresses.sol"; //, COMPLEX_UPGRADER_ADDR, GENESIS_UPGRADE_ADDR +import {SemVer} from "contracts/common/libraries/SemVer.sol"; +import {L2ContractHelper} from "contracts/common/libraries/L2ContractHelper.sol"; +import {L2CanonicalTransaction} from "contracts/common/Messaging.sol"; +import {IExecutor, SystemLogKey, TOTAL_BLOBS_IN_COMMITMENT} from "contracts/state-transition/chain-interfaces/IExecutor.sol"; import {GettersFacet} from "contracts/state-transition/chain-deps/facets/Getters.sol"; import {AdminFacet} from "contracts/state-transition/chain-deps/facets/Admin.sol"; import {ExecutorFacet} from "contracts/state-transition/chain-deps/facets/Executor.sol"; import {IExecutor} from "contracts/state-transition/chain-interfaces/IExecutor.sol"; +import {IL2GenesisUpgrade} from "contracts/state-transition/l2-deps/IL2GenesisUpgrade.sol"; +import {IComplexUpgrader} from "contracts/state-transition/l2-deps/IComplexUpgrader.sol"; +import {IBridgehub} from "contracts/bridgehub/IBridgehub.sol"; contract revertBatchesTest is ChainTypeManagerTest { // Items for logs & commits @@ -20,129 +29,221 @@ contract revertBatchesTest is ChainTypeManagerTest { IExecutor.StoredBatchInfo internal newStoredBatchInfo; IExecutor.StoredBatchInfo internal genesisStoredBatchInfo; uint256[] internal proofInput; + bytes32 l2DAValidatorOutputHash; + bytes operatorDAInput; + bytes defaultBlobCommitment; + bytes32[] defaultBlobVersionedHashes; + bytes16 defaultBlobOpeningPoint = 0x7142c5851421a2dc03dde0aabdb0ffdb; + bytes32 defaultBlobClaimedValue = 0x1e5eea3bbb85517461c1d1c7b84c7c2cec050662a5e81a71d5d7e2766eaff2f0; + bytes l2Logs; + address newChainAddress; + + bytes32 constant EMPTY_PREPUBLISHED_COMMITMENT = 0x0000000000000000000000000000000000000000000000000000000000000000; + bytes constant POINT_EVALUATION_PRECOMPILE_RESULT = + hex"000000000000000000000000000000000000000000000000000000000000100073eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001"; // Facets exposing the diamond AdminFacet internal adminFacet; ExecutorFacet internal executorFacet; GettersFacet internal gettersFacet; - // function test_SuccessfulBatchReverting() public { - // createNewChain(getDiamondCutData(diamondInit)); - - // address newChainAddress = chainContractAddress.getZKChain(chainId); - - // executorFacet = ExecutorFacet(address(newChainAddress)); - // gettersFacet = GettersFacet(address(newChainAddress)); - // adminFacet = AdminFacet(address(newChainAddress)); - - // // Initial setup for logs & commits - // vm.stopPrank(); - // vm.startPrank(newChainAdmin); - - // genesisStoredBatchInfo = IExecutor.StoredBatchInfo({ - // batchNumber: 0, - // batchHash: bytes32(uint256(0x01)), - // indexRepeatedStorageChanges: 1, - // numberOfLayer1Txs: 0, - // priorityOperationsHash: EMPTY_STRING_KECCAK, - // l2LogsTreeRoot: DEFAULT_L2_LOGS_TREE_ROOT_HASH, - // timestamp: 0, - // commitment: bytes32(uint256(0x01)) - // }); - - // adminFacet.setTokenMultiplier(1, 1); - - // uint256[] memory recursiveAggregationInput; - // uint256[] memory serializedProof; - // proofInput = IExecutor.ProofInput(recursiveAggregationInput, serializedProof); - - // // foundry's default value is 1 for the block's timestamp, it is expected - // // that block.timestamp > COMMIT_TIMESTAMP_NOT_OLDER + 1 - // vm.warp(COMMIT_TIMESTAMP_NOT_OLDER + 1 + 1); - // currentTimestamp = block.timestamp; - - // bytes memory l2Logs = Utils.encodePacked(Utils.createSystemLogs()); - // newCommitBatchInfo = IExecutor.CommitBatchInfo({ - // batchNumber: 1, - // timestamp: uint64(currentTimestamp), - // indexRepeatedStorageChanges: 1, - // newStateRoot: Utils.randomBytes32("newStateRoot"), - // numberOfLayer1Txs: 0, - // priorityOperationsHash: keccak256(""), - // bootloaderHeapInitialContentsHash: Utils.randomBytes32("bootloaderHeapInitialContentsHash"), - // eventsQueueStateHash: Utils.randomBytes32("eventsQueueStateHash"), - // systemLogs: l2Logs, - // pubdataCommitments: "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - // }); - - // // Commit & prove batches - // vm.warp(COMMIT_TIMESTAMP_NOT_OLDER + 1); - // currentTimestamp = block.timestamp; - - // bytes32 expectedSystemContractUpgradeTxHash = gettersFacet.getL2SystemContractsUpgradeTxHash(); - // bytes[] memory correctL2Logs = Utils.createSystemLogsWithUpgradeTransaction( - // expectedSystemContractUpgradeTxHash - // ); - - // correctL2Logs[uint256(uint256(SystemLogKey.PACKED_BATCH_AND_L2_BLOCK_TIMESTAMP_KEY))] = Utils.constructL2Log( - // true, - // L2_SYSTEM_CONTEXT_ADDRESS, - // uint256(SystemLogKey.PACKED_BATCH_AND_L2_BLOCK_TIMESTAMP_KEY), - // Utils.packBatchTimestampAndBlockTimestamp(currentTimestamp, currentTimestamp) - // ); - - // correctL2Logs[uint256(uint256(SystemLogKey.PREV_BATCH_HASH_KEY))] = Utils.constructL2Log( - // true, - // L2_SYSTEM_CONTEXT_ADDRESS, - // uint256(SystemLogKey.PREV_BATCH_HASH_KEY), - // bytes32(uint256(0x01)) - // ); - - // l2Logs = Utils.encodePacked(correctL2Logs); - // newCommitBatchInfo.timestamp = uint64(currentTimestamp); - // newCommitBatchInfo.systemLogs = l2Logs; - - // IExecutor.CommitBatchInfo[] memory commitBatchInfoArray = new IExecutor.CommitBatchInfo[](1); - // commitBatchInfoArray[0] = newCommitBatchInfo; - - // vm.stopPrank(); - // vm.startPrank(validator); - // vm.recordLogs(); - // executorFacet.commitBatchesSharedBridge(uint256(0), genesisStoredBatchInfo, commitBatchInfoArray); - // Vm.Log[] memory entries = vm.getRecordedLogs(); - - // newStoredBatchInfo = IExecutor.StoredBatchInfo({ - // batchNumber: 1, - // batchHash: entries[0].topics[2], - // indexRepeatedStorageChanges: 1, - // numberOfLayer1Txs: 0, - // priorityOperationsHash: keccak256(""), - // l2LogsTreeRoot: 0, - // timestamp: currentTimestamp, - // commitment: entries[0].topics[3] - // }); - - // IExecutor.StoredBatchInfo[] memory storedBatchInfoArray = new IExecutor.StoredBatchInfo[](1); - // storedBatchInfoArray[0] = newStoredBatchInfo; - - // executorFacet.proveBatches(genesisStoredBatchInfo, storedBatchInfoArray, proofInput); - - // // Test batch revert triggered from CTM - // vm.stopPrank(); - // vm.startPrank(governor); - - // uint256 totalBlocksCommittedBefore = gettersFacet.getTotalBlocksCommitted(); - // assertEq(totalBlocksCommittedBefore, 1, "totalBlocksCommittedBefore"); - - // uint256 totalBlocksVerifiedBefore = gettersFacet.getTotalBlocksVerified(); - // assertEq(totalBlocksVerifiedBefore, 1, "totalBlocksVerifiedBefore"); - - // chainContractAddress.revertBatches(chainId, 0); - - // uint256 totalBlocksCommitted = gettersFacet.getTotalBlocksCommitted(); - // assertEq(totalBlocksCommitted, 0, "totalBlocksCommitted"); - - // uint256 totalBlocksVerified = gettersFacet.getTotalBlocksVerified(); - // assertEq(totalBlocksVerified, 0, "totalBlocksVerified"); - // } + function setUp() public { + deploy(); + + defaultBlobCommitment = Utils.getDefaultBlobCommitment(); + defaultBlobVersionedHashes = new bytes32[](1); + defaultBlobVersionedHashes[0] = 0x01c024b4740620a5849f95930cefe298933bdf588123ea897cdf0f2462f6d2d5; + + bytes memory precompileInput = Utils.defaultPointEvaluationPrecompileInput(defaultBlobVersionedHashes[0]); + vm.mockCall(POINT_EVALUATION_PRECOMPILE_ADDR, precompileInput, POINT_EVALUATION_PRECOMPILE_RESULT); + + l2Logs = Utils.encodePacked(Utils.createSystemLogs(bytes32(0))); + genesisStoredBatchInfo = IExecutor.StoredBatchInfo({ + batchNumber: 0, + batchHash: bytes32(uint256(0x01)), + indexRepeatedStorageChanges: 0x01, + numberOfLayer1Txs: 0, + priorityOperationsHash: keccak256(""), + l2LogsTreeRoot: DEFAULT_L2_LOGS_TREE_ROOT_HASH, + timestamp: 0, + commitment: bytes32(uint256(0x01)) + }); + vm.warp(COMMIT_TIMESTAMP_NOT_OLDER + 1 + 1); + currentTimestamp = block.timestamp; + newCommitBatchInfo = IExecutor.CommitBatchInfo({ + batchNumber: 1, + timestamp: uint64(currentTimestamp), + indexRepeatedStorageChanges: 0, + newStateRoot: Utils.randomBytes32("newStateRoot"), + numberOfLayer1Txs: 0, + priorityOperationsHash: keccak256(""), + bootloaderHeapInitialContentsHash: Utils.randomBytes32("bootloaderHeapInitialContentsHash"), + eventsQueueStateHash: Utils.randomBytes32("eventsQueueStateHash"), + systemLogs: l2Logs, + operatorDAInput: "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + }); + + { + bytes memory complexUpgraderCalldata; + address l1CtmDeployer = address(bridgehub.l1CtmDeployer()); + { + bytes memory l2GenesisUpgradeCalldata = abi.encodeCall( + IL2GenesisUpgrade.genesisUpgrade, + (chainId, l1CtmDeployer, forceDeploymentsData, "0x") + ); + complexUpgraderCalldata = abi.encodeCall( + IComplexUpgrader.upgrade, + (L2_GENESIS_UPGRADE_ADDR, l2GenesisUpgradeCalldata) + ); + } + + // slither-disable-next-line unused-return + (, uint32 minorVersion, ) = SemVer.unpackSemVer(SafeCast.toUint96(0)); + } + + newChainAddress = createNewChain(getDiamondCutData(diamondInit)); + vm.mockCall( + address(bridgehub), + abi.encodeWithSelector(IBridgehub.getZKChain.selector), + abi.encode(newChainAddress) + ); + + executorFacet = ExecutorFacet(address(newChainAddress)); + gettersFacet = GettersFacet(address(newChainAddress)); + adminFacet = AdminFacet(address(newChainAddress)); + + vm.stopPrank(); + vm.prank(newChainAdmin); + adminFacet.setDAValidatorPair(address(rollupL1DAValidator), L2_DA_VALIDATOR_ADDRESS); + } + + function test_SuccessfulBatchReverting() public { + vm.startPrank(governor); + + bytes32 uncompressedStateDiffHash = Utils.randomBytes32("uncompressedStateDiffHash"); + bytes32 totalL2PubdataHash = Utils.randomBytes32("totalL2PubdataHash"); + uint8 numberOfBlobs = 1; + bytes32[] memory blobsLinearHashes = new bytes32[](1); + blobsLinearHashes[0] = Utils.randomBytes32("blobsLinearHashes"); + + operatorDAInput = abi.encodePacked( + uncompressedStateDiffHash, + totalL2PubdataHash, + numberOfBlobs, + blobsLinearHashes, + bytes1(0x01), + defaultBlobCommitment, + EMPTY_PREPUBLISHED_COMMITMENT + ); + + l2DAValidatorOutputHash = Utils.constructRollupL2DAValidatorOutputHash( + uncompressedStateDiffHash, + totalL2PubdataHash, + uint8(numberOfBlobs), + blobsLinearHashes + ); + + vm.warp(COMMIT_TIMESTAMP_NOT_OLDER + 1); + currentTimestamp = block.timestamp; + bytes32 expectedSystemContractUpgradeTxHash = gettersFacet.getL2SystemContractsUpgradeTxHash(); + bytes[] memory correctL2Logs = Utils.createSystemLogsWithUpgradeTransactionForCTM( + expectedSystemContractUpgradeTxHash, + l2DAValidatorOutputHash + ); + correctL2Logs[uint256(SystemLogKey.PACKED_BATCH_AND_L2_BLOCK_TIMESTAMP_KEY)] = Utils.constructL2Log( + true, + L2_SYSTEM_CONTEXT_ADDRESS, + uint256(SystemLogKey.PACKED_BATCH_AND_L2_BLOCK_TIMESTAMP_KEY), + Utils.packBatchTimestampAndBlockTimestamp(currentTimestamp, currentTimestamp) + ); + + IExecutor.CommitBatchInfo memory correctNewCommitBatchInfo = newCommitBatchInfo; + correctNewCommitBatchInfo.timestamp = uint64(currentTimestamp); + correctNewCommitBatchInfo.systemLogs = Utils.encodePacked(correctL2Logs); + correctNewCommitBatchInfo.operatorDAInput = operatorDAInput; + + bytes32[] memory blobHashes = new bytes32[](TOTAL_BLOBS_IN_COMMITMENT); + blobHashes[0] = blobsLinearHashes[0]; + + bytes32[] memory blobCommitments = new bytes32[](TOTAL_BLOBS_IN_COMMITMENT); + blobCommitments[0] = keccak256( + abi.encodePacked( + defaultBlobVersionedHashes[0], + abi.encodePacked(defaultBlobOpeningPoint, defaultBlobClaimedValue) + ) + ); + + bytes32 expectedBatchCommitment = Utils.createBatchCommitment( + correctNewCommitBatchInfo, + uncompressedStateDiffHash, + blobCommitments, + blobHashes + ); + + IExecutor.CommitBatchInfo[] memory correctCommitBatchInfoArray = new IExecutor.CommitBatchInfo[](1); + correctCommitBatchInfoArray[0] = correctNewCommitBatchInfo; + correctCommitBatchInfoArray[0].operatorDAInput = operatorDAInput; + + vm.stopPrank(); + vm.startPrank(validator); + vm.blobhashes(defaultBlobVersionedHashes); + vm.recordLogs(); + (uint256 commitBatchFrom, uint256 commitBatchTo, bytes memory commitData) = Utils.encodeCommitBatchesData( + genesisStoredBatchInfo, + correctCommitBatchInfoArray + ); + executorFacet.commitBatchesSharedBridge(uint256(0), commitBatchFrom, commitBatchTo, commitData); + + Vm.Log[] memory entries = vm.getRecordedLogs(); + + assertEq(entries.length, 1); + assertEq(entries[0].topics[0], keccak256("BlockCommit(uint256,bytes32,bytes32)")); + assertEq(entries[0].topics[1], bytes32(uint256(1))); // batchNumber + assertEq(entries[0].topics[2], correctNewCommitBatchInfo.newStateRoot); // batchHash + + uint256 totalBatchesCommitted = gettersFacet.getTotalBatchesCommitted(); + assertEq(totalBatchesCommitted, 1); + + newStoredBatchInfo = IExecutor.StoredBatchInfo({ + batchNumber: 1, + batchHash: entries[0].topics[2], + indexRepeatedStorageChanges: 0, + numberOfLayer1Txs: 0, + priorityOperationsHash: keccak256(""), + l2LogsTreeRoot: DEFAULT_L2_LOGS_TREE_ROOT_HASH, + timestamp: currentTimestamp, + commitment: entries[0].topics[3] + }); + + IExecutor.StoredBatchInfo[] memory storedBatchInfoArray = new IExecutor.StoredBatchInfo[](1); + storedBatchInfoArray[0] = newStoredBatchInfo; + + (uint256 proveBatchFrom, uint256 proveBatchTo, bytes memory proveData) = Utils.encodeProveBatchesData( + genesisStoredBatchInfo, + storedBatchInfoArray, + proofInput + ); + + executorFacet.proveBatchesSharedBridge(uint256(0), proveBatchFrom, proveBatchTo, proveData); + + // Test batch revert triggered from CTM + vm.stopPrank(); + vm.prank(address(chainContractAddress)); + adminFacet.setValidator(address(chainContractAddress), true); + vm.startPrank(governor); + + uint256 totalBlocksCommittedBefore = gettersFacet.getTotalBlocksCommitted(); + assertEq(totalBlocksCommittedBefore, 1, "totalBlocksCommittedBefore"); + + uint256 totalBlocksVerifiedBefore = gettersFacet.getTotalBlocksVerified(); + assertEq(totalBlocksVerifiedBefore, 1, "totalBlocksVerifiedBefore"); + + chainContractAddress.revertBatches(chainId, 0); + + uint256 totalBlocksCommitted = gettersFacet.getTotalBlocksCommitted(); + assertEq(totalBlocksCommitted, 0, "totalBlocksCommitted"); + + uint256 totalBlocksVerified = gettersFacet.getTotalBlocksVerified(); + assertEq(totalBlocksVerified, 0, "totalBlocksVerified"); + } } diff --git a/l1-contracts/test/foundry/l1/unit/concrete/state-transition/ChainTypeManager/_ChainTypeManager_Shared.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/state-transition/ChainTypeManager/_ChainTypeManager_Shared.t.sol index 99d8c9859..6f8f73b4a 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/state-transition/ChainTypeManager/_ChainTypeManager_Shared.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/state-transition/ChainTypeManager/_ChainTypeManager_Shared.t.sol @@ -10,6 +10,8 @@ import {IBridgehub} from "contracts/bridgehub/IBridgehub.sol"; import {Utils} from "foundry-test/l1/unit/concrete/Utils/Utils.sol"; import {Bridgehub} from "contracts/bridgehub/Bridgehub.sol"; +import {IL1AssetRouter} from "contracts/bridge/asset-router/IL1AssetRouter.sol"; +import {IL1Nullifier} from "contracts/bridge/interfaces/IL1Nullifier.sol"; import {UtilsFacet} from "foundry-test/l1/unit/concrete/Utils/UtilsFacet.sol"; import {AdminFacet} from "contracts/state-transition/chain-deps/facets/Admin.sol"; import {ExecutorFacet} from "contracts/state-transition/chain-deps/facets/Executor.sol"; @@ -24,18 +26,21 @@ import {TestnetVerifier} from "contracts/state-transition/TestnetVerifier.sol"; import {DummyBridgehub} from "contracts/dev-contracts/test/DummyBridgehub.sol"; import {DataEncoding} from "contracts/common/libraries/DataEncoding.sol"; import {ZeroAddress} from "contracts/common/L1ContractErrors.sol"; +import {RollupL1DAValidator} from "da-contracts/RollupL1DAValidator.sol"; contract ChainTypeManagerTest is Test { ChainTypeManager internal chainTypeManager; ChainTypeManager internal chainContractAddress; L1GenesisUpgrade internal genesisUpgradeContract; Bridgehub internal bridgehub; + RollupL1DAValidator internal rollupL1DAValidator; address internal diamondInit; address internal constant governor = address(0x1010101); address internal constant admin = address(0x2020202); address internal constant baseToken = address(0x3030303); address internal constant sharedBridge = address(0x4040404); address internal constant validator = address(0x5050505); + address internal constant l1Nullifier = address(0x6060606); address internal newChainAdmin; uint256 chainId = 112; address internal testnetVerifier = address(new TestnetVerifier()); @@ -123,6 +128,8 @@ contract ChainTypeManagerTest is Test { ); chainContractAddress = ChainTypeManager(address(transparentUpgradeableProxy)); + rollupL1DAValidator = new RollupL1DAValidator(); + vm.stopPrank(); vm.startPrank(governor); } @@ -143,6 +150,18 @@ contract ChainTypeManagerTest is Test { vm.stopPrank(); vm.startPrank(address(bridgehub)); + vm.mockCall( + address(sharedBridge), + abi.encodeWithSelector(IL1AssetRouter.L1_NULLIFIER.selector), + abi.encode(l1Nullifier) + ); + + vm.mockCall( + address(l1Nullifier), + abi.encodeWithSelector(IL1Nullifier.l2BridgeAddress.selector), + abi.encode(l1Nullifier) + ); + return chainContractAddress.createNewChain({ _chainId: chainId, From 6a8dc33be18ce8c75518a2494410289afbfba263 Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Wed, 25 Sep 2024 12:28:16 +0200 Subject: [PATCH 05/20] better governance protection --- .../contracts/common/L1ContractErrors.sol | 4 + .../governance/IPermanentRestriction.sol | 3 + .../contracts/governance/L2AdminFactory.sol | 43 +++++++ .../governance/PermanentRestriction.sol | 113 +++++++++++++++++- 4 files changed, 159 insertions(+), 4 deletions(-) create mode 100644 l1-contracts/contracts/governance/L2AdminFactory.sol diff --git a/l1-contracts/contracts/common/L1ContractErrors.sol b/l1-contracts/contracts/common/L1ContractErrors.sol index 18766497f..4e4a71ee7 100644 --- a/l1-contracts/contracts/common/L1ContractErrors.sol +++ b/l1-contracts/contracts/common/L1ContractErrors.sol @@ -407,6 +407,10 @@ error IncorrectBatchBounds( ); // 0x64107968 error AssetHandlerNotRegistered(bytes32 assetId); +// 0x10f30e75 +error NotBridgehub(address addr); +// 0x2554babc +error InvalidAddress(address expected, address actual); enum SharedBridgeKey { PostUpgradeFirstBatch, diff --git a/l1-contracts/contracts/governance/IPermanentRestriction.sol b/l1-contracts/contracts/governance/IPermanentRestriction.sol index 548866b9f..7169ddf14 100644 --- a/l1-contracts/contracts/governance/IPermanentRestriction.sol +++ b/l1-contracts/contracts/governance/IPermanentRestriction.sol @@ -14,4 +14,7 @@ interface IPermanentRestriction { /// @notice Emitted when the selector is labeled as validated or not. event SelectorValidationChanged(bytes4 indexed selector, bool isValidated); + + /// @notice Emitted when the L2 admin is whitelisted or not. + event WhitelistL2Admin(address indexed adminAddress, bool isWhitelisted); } diff --git a/l1-contracts/contracts/governance/L2AdminFactory.sol b/l1-contracts/contracts/governance/L2AdminFactory.sol new file mode 100644 index 000000000..f3dba098e --- /dev/null +++ b/l1-contracts/contracts/governance/L2AdminFactory.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.24; + +import {ChainAdmin} from "./ChainAdmin.sol"; + +/// @author Matter Labs +/// @custom:security-contact security@matterlabs.dev +/// @dev Contract used to deploy ChainAdmin contracts on L2. +/// @dev It can be used to ensure that certain L2 admins are deployed with +/// predefined restrictions. E.g. it can be used to deploy admins that ensure that +/// a chain is a permanent rollup. +/// @dev This contract is expected to be deployed in zkEVM (L2) environment. +/// @dev The contract is immutable, in case the restrictions need to be changed, +/// a new contract should be deployed. +contract L2AdminFactory { + event AdminDeployed(address admin); + + /// @dev We use storage instead of immutable variables due to the + /// specifics of the zkEVM environment, where storage is actually cheaper. + address[] public requiredRestrictions; + + constructor(address[] memory _requiredRestrictions) { + requiredRestrictions = _requiredRestrictions; + } + + /// @notice Deploys a new L2 admin contract. + /// @return admin The address of the deployed admin contract. + function deployAdmin( + address[] memory _additionalRestrictions, + bytes32 _salt + ) external returns (address admin) { + address[] memory restrictions = new address[](requiredRestrictions.length + _additionalRestrictions.length); + for (uint256 i = 0; i < requiredRestrictions.length; i++) { + restrictions[i] = requiredRestrictions[i]; + } + for (uint256 i = 0; i < _additionalRestrictions.length; i++) { + restrictions[requiredRestrictions.length + i] = _additionalRestrictions[i]; + } + + admin = address(new ChainAdmin{salt: _salt}(restrictions)); + } +} diff --git a/l1-contracts/contracts/governance/PermanentRestriction.sol b/l1-contracts/contracts/governance/PermanentRestriction.sol index d013a4de6..650a289a8 100644 --- a/l1-contracts/contracts/governance/PermanentRestriction.sol +++ b/l1-contracts/contracts/governance/PermanentRestriction.sol @@ -2,9 +2,12 @@ pragma solidity 0.8.24; -import {CallNotAllowed, ChainZeroAddress, NotAHyperchain, NotAnAdmin, RemovingPermanentRestriction, ZeroAddress, UnallowedImplementation} from "../common/L1ContractErrors.sol"; +import {UnsupportedEncodingVersion, CallNotAllowed, ChainZeroAddress, NotAHyperchain, NotAnAdmin, RemovingPermanentRestriction, ZeroAddress, UnallowedImplementation, AlreadyWhitelisted, NotWhitelisted, NotBridgehub, InvalidSelector, InvalidAddress} from "../common/L1ContractErrors.sol"; -import {Ownable2Step} from "@openzeppelin/contracts-v4/access/Ownable2Step.sol"; +import {L2TransactionRequestTwoBridgesOuter, BridgehubBurnCTMAssetData} from "../bridgehub/IBridgehub.sol"; +import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable-v4/access/Ownable2StepUpgradeable.sol"; +import {L2ContractHelper} from "../common/libraries/L2ContractHelper.sol"; +import {NEW_ENCODING_VERSION} from "../bridge/asset-router/IAssetRouterBase.sol"; import {Call} from "./Common.sol"; import {IRestriction} from "./IRestriction.sol"; @@ -22,10 +25,19 @@ import {IPermanentRestriction} from "./IPermanentRestriction.sol"; /// properties are preserved forever. /// @dev To be deployed as a transparent upgradable proxy, owned by a trusted decentralized governance. /// @dev Once of the instances of such contract is to ensure that a ZkSyncHyperchain is a rollup forever. -contract PermanentRestriction is IRestriction, IPermanentRestriction, Ownable2Step { +contract PermanentRestriction is IRestriction, IPermanentRestriction, Ownable2StepUpgradeable { /// @notice The address of the Bridgehub contract. IBridgehub public immutable BRIDGE_HUB; + /// @notice The address of the L2 admin factory that should be used to deploy the chain admins + /// for chains that migrated on top of an L2 settlement layer. + /// @dev If this contract is deployed on L2, this address is 0. + /// @dev This address is expected to be the same on all L2 chains. + address public immutable L2_ADMIN_FACTORY; + + /// @notice The bytecode hash of the L2 chain admin contract. + bytes32 public immutable L2_CHAIN_ADMIN_BYTECODE_HASH; + /// @notice The mapping of the allowed admin implementations. mapping(bytes32 implementationCodeHash => bool isAllowed) public allowedAdminImplementations; @@ -35,9 +47,20 @@ contract PermanentRestriction is IRestriction, IPermanentRestriction, Ownable2St /// @notice The mapping of the validated selectors. mapping(bytes4 selector => bool isValidated) public validatedSelectors; - constructor(address _initialOwner, IBridgehub _bridgehub) { + /// @notice The mapping of whitelisted L2 admins. + mapping(address adminAddress => bool isWhitelisted) public whitelistedL2Admins; + + constructor( + IBridgehub _bridgehub, + address _l2AdminFactory, + bytes32 _l2ChainAdminBytecodeHash + ) { BRIDGE_HUB = _bridgehub; + L2_ADMIN_FACTORY = _l2AdminFactory; + L2_CHAIN_ADMIN_BYTECODE_HASH = _l2ChainAdminBytecodeHash; + } + function initialize(address _initialOwner) initializer external { // solhint-disable-next-line gas-custom-errors, reason-string if (_initialOwner == address(0)) { revert ZeroAddress(); @@ -71,6 +94,27 @@ contract PermanentRestriction is IRestriction, IPermanentRestriction, Ownable2St emit SelectorValidationChanged(_selector, _isValidated); } + + /// @notice Whitelists a certain L2 admin. + /// @param deploymentSalt The salt for the deployment. + /// @param constructorInputHash The hash of the constructor data for the deployment. + function whitelistL2Admin(bytes32 deploymentSalt, bytes32 constructorInputHash) external { + // We do not do any additional validations for constructor data, + // we expect that only admins of the allowed format are to be deployed. + address expectedAddress = L2ContractHelper.computeCreate2Address( + L2_ADMIN_FACTORY, + deploymentSalt, + L2_CHAIN_ADMIN_BYTECODE_HASH, + constructorInputHash + ); + + if (whitelistedL2Admins[expectedAddress]) { + revert AlreadyWhitelisted(expectedAddress); + } + + whitelistedL2Admins[expectedAddress] = true; + emit WhitelistL2Admin(expectedAddress, true); + } /// @inheritdoc IRestriction function validateCall( @@ -78,9 +122,26 @@ contract PermanentRestriction is IRestriction, IPermanentRestriction, Ownable2St address // _invoker ) external view override { _validateAsChainAdmin(_call); + _validateMigrationToL2(_call); _validateRemoveRestriction(_call); } + /// @notice Validates the migration to an L2 settlement layer. + /// @param _call The call data. + /// @dev Note that we do not need to validate the migration to the L1 layer as the admin + /// is not changed in this case. + function _validateMigrationToL2( + Call calldata _call + ) internal view { + try this.tryGetNewAdminFromMigration(_call) returns (address admin) { + if(!whitelistedL2Admins[admin]) { + revert NotWhitelisted(admin); + } + } catch { + // It was not the migration call, so we do nothing + } + } + /// @notice Validates the call as the chain admin /// @param _call The call data. function _validateAsChainAdmin(Call calldata _call) internal view { @@ -183,4 +244,48 @@ contract PermanentRestriction is IRestriction, IPermanentRestriction, Ownable2St revert NotAnAdmin(admin, _potentialAdmin); } } + + /// @notice Tries to get the new admin from the migration. + /// @param _call The call data. + /// @dev This function reverts if the provided call was not a migration call. + function tryGetNewAdminFromMigration(Call calldata _call) external view returns (address) { + if (_call.target != address(BRIDGE_HUB)) { + revert NotBridgehub(_call.target); + } + + if (bytes4(_call.data[:4]) != IBridgehub.requestL2TransactionTwoBridges.selector) { + revert InvalidSelector(bytes4(_call.data[:4])); + } + + address sharedBridge = BRIDGE_HUB.sharedBridge(); + + L2TransactionRequestTwoBridgesOuter memory request = abi.decode(_call.data[4:], (L2TransactionRequestTwoBridgesOuter)); + + if (request.secondBridgeAddress != sharedBridge) { + revert InvalidAddress(request.secondBridgeAddress, sharedBridge); + } + + bytes memory secondBridgeData = request.secondBridgeCalldata; + if (secondBridgeData[0] != NEW_ENCODING_VERSION) { + revert UnsupportedEncodingVersion(); + } + bytes memory encodedData = new bytes(secondBridgeData.length - 1); + assembly { + mcopy(add(encodedData, 0x20), add(secondBridgeData, 0x21), mload(encodedData)) + } + + (bytes32 chainAssetId, bytes memory bridgehubData) = abi.decode(encodedData, (bytes32, bytes)); + // We will just check that the chainAssetId is a valid chainAssetId. + // For now, for simplicity, we do not check that the admin is exactly the admin + // of this chain. + address ctmAddress = BRIDGE_HUB.ctmAssetIdToAddress(chainAssetId); + if (ctmAddress == address(0)) { + revert ZeroAddress(); + } + + BridgehubBurnCTMAssetData memory burnData = abi.decode(bridgehubData, (BridgehubBurnCTMAssetData)); + (address l2Admin, ) = abi.decode(burnData.ctmData, (address, bytes)); + + return l2Admin; + } } From 82a2639c1500ad2dafccd3efc04902f2b7df409c Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Wed, 25 Sep 2024 12:43:01 +0200 Subject: [PATCH 06/20] fix old tests --- .../governance/PermanentRestriction.sol | 18 +++++++------- .../Governance/PermanentRestriction.t.sol | 24 +++++++++++++++++-- 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/l1-contracts/contracts/governance/PermanentRestriction.sol b/l1-contracts/contracts/governance/PermanentRestriction.sol index 650a289a8..12e2e5d4c 100644 --- a/l1-contracts/contracts/governance/PermanentRestriction.sol +++ b/l1-contracts/contracts/governance/PermanentRestriction.sol @@ -35,9 +35,6 @@ contract PermanentRestriction is IRestriction, IPermanentRestriction, Ownable2St /// @dev This address is expected to be the same on all L2 chains. address public immutable L2_ADMIN_FACTORY; - /// @notice The bytecode hash of the L2 chain admin contract. - bytes32 public immutable L2_CHAIN_ADMIN_BYTECODE_HASH; - /// @notice The mapping of the allowed admin implementations. mapping(bytes32 implementationCodeHash => bool isAllowed) public allowedAdminImplementations; @@ -52,12 +49,10 @@ contract PermanentRestriction is IRestriction, IPermanentRestriction, Ownable2St constructor( IBridgehub _bridgehub, - address _l2AdminFactory, - bytes32 _l2ChainAdminBytecodeHash + address _l2AdminFactory ) { BRIDGE_HUB = _bridgehub; L2_ADMIN_FACTORY = _l2AdminFactory; - L2_CHAIN_ADMIN_BYTECODE_HASH = _l2ChainAdminBytecodeHash; } function initialize(address _initialOwner) initializer external { @@ -97,14 +92,19 @@ contract PermanentRestriction is IRestriction, IPermanentRestriction, Ownable2St /// @notice Whitelists a certain L2 admin. /// @param deploymentSalt The salt for the deployment. + /// @param l2BytecodeHash The hash of the L2 bytecode. /// @param constructorInputHash The hash of the constructor data for the deployment. - function whitelistL2Admin(bytes32 deploymentSalt, bytes32 constructorInputHash) external { - // We do not do any additional validations for constructor data, + function whitelistL2Admin( + bytes32 deploymentSalt, + bytes32 l2BytecodeHash, + bytes32 constructorInputHash + ) external { + // We do not do any additional validations for constructor data or the bytecode, // we expect that only admins of the allowed format are to be deployed. address expectedAddress = L2ContractHelper.computeCreate2Address( L2_ADMIN_FACTORY, deploymentSalt, - L2_CHAIN_ADMIN_BYTECODE_HASH, + l2BytecodeHash, constructorInputHash ); diff --git a/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol index f97ecc08a..c8da87080 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol @@ -1,6 +1,7 @@ pragma solidity 0.8.24; import "@openzeppelin/contracts-v4/utils/Strings.sol"; +import {TransparentUpgradeableProxy} from "@openzeppelin/contracts-v4/proxy/transparent/TransparentUpgradeableProxy.sol"; import {Bridgehub} from "contracts/bridgehub/Bridgehub.sol"; import {Diamond} from "contracts/state-transition/libraries/Diamond.sol"; import {ChainTypeManager} from "contracts/state-transition/ChainTypeManager.sol"; @@ -22,12 +23,15 @@ import {IMessageRoot} from "contracts/bridgehub/IMessageRoot.sol"; import {MessageRoot} from "contracts/bridgehub/MessageRoot.sol"; import {IL1AssetRouter} from "contracts/bridge/asset-router/IL1AssetRouter.sol"; import {IL1Nullifier} from "contracts/bridge/interfaces/IL1Nullifier.sol"; +import {IBridgehub} from "contracts/bridgehub/IBridgehub.sol"; contract PermanentRestrictionTest is ChainTypeManagerTest { ChainAdmin internal chainAdmin; AccessControlRestriction internal restriction; PermanentRestriction internal permRestriction; + address constant L2_FACTORY_ADDR = address(0); + address internal owner; address internal hyperchain; @@ -40,16 +44,32 @@ contract PermanentRestrictionTest is ChainTypeManagerTest { owner = makeAddr("owner"); hyperchain = chainContractAddress.getHyperchain(chainId); - permRestriction = new PermanentRestriction(owner, bridgehub); + (permRestriction, ) = _deployPermRestriction( + bridgehub, + L2_FACTORY_ADDR, + owner + ); restriction = new AccessControlRestriction(0, owner); address[] memory restrictions = new address[](1); restrictions[0] = address(restriction); chainAdmin = new ChainAdmin(restrictions); } + function _deployPermRestriction( + IBridgehub _bridgehub, + address _l2AdminFactory, + address _owner + ) internal returns (PermanentRestriction proxy, PermanentRestriction impl) { + impl = new PermanentRestriction(_bridgehub, _l2AdminFactory); + TransparentUpgradeableProxy tup = new TransparentUpgradeableProxy(address(impl), address(uint160(1)), abi.encodeCall(PermanentRestriction.initialize, (_owner))); + + proxy = PermanentRestriction(address(tup)); + } + function test_ownerAsAddressZero() public { + PermanentRestriction impl = new PermanentRestriction(bridgehub, L2_FACTORY_ADDR); vm.expectRevert(ZeroAddress.selector); - permRestriction = new PermanentRestriction(address(0), bridgehub); + new TransparentUpgradeableProxy(address(impl), address(uint160(1)), abi.encodeCall(PermanentRestriction.initialize, (address(0)))); } function test_allowAdminImplementation(bytes32 implementationHash) public { From 54f3c8b626cff87570b718c2a99af03fe1cdf433 Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Wed, 25 Sep 2024 12:45:48 +0200 Subject: [PATCH 07/20] fix lint --- .../contracts/governance/L2AdminFactory.sol | 19 ++++---- .../governance/PermanentRestriction.sol | 44 ++++++++----------- .../Governance/PermanentRestriction.t.sol | 18 +++++--- 3 files changed, 39 insertions(+), 42 deletions(-) diff --git a/l1-contracts/contracts/governance/L2AdminFactory.sol b/l1-contracts/contracts/governance/L2AdminFactory.sol index f3dba098e..d4fe4637c 100644 --- a/l1-contracts/contracts/governance/L2AdminFactory.sol +++ b/l1-contracts/contracts/governance/L2AdminFactory.sol @@ -7,16 +7,16 @@ import {ChainAdmin} from "./ChainAdmin.sol"; /// @author Matter Labs /// @custom:security-contact security@matterlabs.dev /// @dev Contract used to deploy ChainAdmin contracts on L2. -/// @dev It can be used to ensure that certain L2 admins are deployed with -/// predefined restrictions. E.g. it can be used to deploy admins that ensure that -/// a chain is a permanent rollup. +/// @dev It can be used to ensure that certain L2 admins are deployed with +/// predefined restrictions. E.g. it can be used to deploy admins that ensure that +/// a chain is a permanent rollup. /// @dev This contract is expected to be deployed in zkEVM (L2) environment. /// @dev The contract is immutable, in case the restrictions need to be changed, /// a new contract should be deployed. contract L2AdminFactory { event AdminDeployed(address admin); - /// @dev We use storage instead of immutable variables due to the + /// @dev We use storage instead of immutable variables due to the /// specifics of the zkEVM environment, where storage is actually cheaper. address[] public requiredRestrictions; @@ -26,15 +26,14 @@ contract L2AdminFactory { /// @notice Deploys a new L2 admin contract. /// @return admin The address of the deployed admin contract. - function deployAdmin( - address[] memory _additionalRestrictions, - bytes32 _salt - ) external returns (address admin) { + function deployAdmin(address[] calldata _additionalRestrictions, bytes32 _salt) external returns (address admin) { address[] memory restrictions = new address[](requiredRestrictions.length + _additionalRestrictions.length); - for (uint256 i = 0; i < requiredRestrictions.length; i++) { + uint256 cachedRequired = requiredRestrictions.length; + for (uint256 i = 0; i < cachedRequired; ++i) { restrictions[i] = requiredRestrictions[i]; } - for (uint256 i = 0; i < _additionalRestrictions.length; i++) { + uint256 cachedAdditional = _additionalRestrictions.length; + for (uint256 i = 0; i < cachedAdditional; ++i) { restrictions[requiredRestrictions.length + i] = _additionalRestrictions[i]; } diff --git a/l1-contracts/contracts/governance/PermanentRestriction.sol b/l1-contracts/contracts/governance/PermanentRestriction.sol index 12e2e5d4c..83745de5e 100644 --- a/l1-contracts/contracts/governance/PermanentRestriction.sol +++ b/l1-contracts/contracts/governance/PermanentRestriction.sol @@ -29,7 +29,7 @@ contract PermanentRestriction is IRestriction, IPermanentRestriction, Ownable2St /// @notice The address of the Bridgehub contract. IBridgehub public immutable BRIDGE_HUB; - /// @notice The address of the L2 admin factory that should be used to deploy the chain admins + /// @notice The address of the L2 admin factory that should be used to deploy the chain admins /// for chains that migrated on top of an L2 settlement layer. /// @dev If this contract is deployed on L2, this address is 0. /// @dev This address is expected to be the same on all L2 chains. @@ -47,15 +47,12 @@ contract PermanentRestriction is IRestriction, IPermanentRestriction, Ownable2St /// @notice The mapping of whitelisted L2 admins. mapping(address adminAddress => bool isWhitelisted) public whitelistedL2Admins; - constructor( - IBridgehub _bridgehub, - address _l2AdminFactory - ) { + constructor(IBridgehub _bridgehub, address _l2AdminFactory) { BRIDGE_HUB = _bridgehub; L2_ADMIN_FACTORY = _l2AdminFactory; } - function initialize(address _initialOwner) initializer external { + function initialize(address _initialOwner) external initializer { // solhint-disable-next-line gas-custom-errors, reason-string if (_initialOwner == address(0)) { revert ZeroAddress(); @@ -89,25 +86,21 @@ contract PermanentRestriction is IRestriction, IPermanentRestriction, Ownable2St emit SelectorValidationChanged(_selector, _isValidated); } - + /// @notice Whitelists a certain L2 admin. /// @param deploymentSalt The salt for the deployment. /// @param l2BytecodeHash The hash of the L2 bytecode. /// @param constructorInputHash The hash of the constructor data for the deployment. - function whitelistL2Admin( - bytes32 deploymentSalt, - bytes32 l2BytecodeHash, - bytes32 constructorInputHash - ) external { - // We do not do any additional validations for constructor data or the bytecode, + function whitelistL2Admin(bytes32 deploymentSalt, bytes32 l2BytecodeHash, bytes32 constructorInputHash) external { + // We do not do any additional validations for constructor data or the bytecode, // we expect that only admins of the allowed format are to be deployed. address expectedAddress = L2ContractHelper.computeCreate2Address( - L2_ADMIN_FACTORY, - deploymentSalt, - l2BytecodeHash, + L2_ADMIN_FACTORY, + deploymentSalt, + l2BytecodeHash, constructorInputHash ); - + if (whitelistedL2Admins[expectedAddress]) { revert AlreadyWhitelisted(expectedAddress); } @@ -128,13 +121,11 @@ contract PermanentRestriction is IRestriction, IPermanentRestriction, Ownable2St /// @notice Validates the migration to an L2 settlement layer. /// @param _call The call data. - /// @dev Note that we do not need to validate the migration to the L1 layer as the admin + /// @dev Note that we do not need to validate the migration to the L1 layer as the admin /// is not changed in this case. - function _validateMigrationToL2( - Call calldata _call - ) internal view { + function _validateMigrationToL2(Call calldata _call) internal view { try this.tryGetNewAdminFromMigration(_call) returns (address admin) { - if(!whitelistedL2Admins[admin]) { + if (!whitelistedL2Admins[admin]) { revert NotWhitelisted(admin); } } catch { @@ -259,7 +250,10 @@ contract PermanentRestriction is IRestriction, IPermanentRestriction, Ownable2St address sharedBridge = BRIDGE_HUB.sharedBridge(); - L2TransactionRequestTwoBridgesOuter memory request = abi.decode(_call.data[4:], (L2TransactionRequestTwoBridgesOuter)); + L2TransactionRequestTwoBridgesOuter memory request = abi.decode( + _call.data[4:], + (L2TransactionRequestTwoBridgesOuter) + ); if (request.secondBridgeAddress != sharedBridge) { revert InvalidAddress(request.secondBridgeAddress, sharedBridge); @@ -285,7 +279,7 @@ contract PermanentRestriction is IRestriction, IPermanentRestriction, Ownable2St BridgehubBurnCTMAssetData memory burnData = abi.decode(bridgehubData, (BridgehubBurnCTMAssetData)); (address l2Admin, ) = abi.decode(burnData.ctmData, (address, bytes)); - + return l2Admin; - } + } } diff --git a/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol index c8da87080..245028235 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol @@ -44,11 +44,7 @@ contract PermanentRestrictionTest is ChainTypeManagerTest { owner = makeAddr("owner"); hyperchain = chainContractAddress.getHyperchain(chainId); - (permRestriction, ) = _deployPermRestriction( - bridgehub, - L2_FACTORY_ADDR, - owner - ); + (permRestriction, ) = _deployPermRestriction(bridgehub, L2_FACTORY_ADDR, owner); restriction = new AccessControlRestriction(0, owner); address[] memory restrictions = new address[](1); restrictions[0] = address(restriction); @@ -61,7 +57,11 @@ contract PermanentRestrictionTest is ChainTypeManagerTest { address _owner ) internal returns (PermanentRestriction proxy, PermanentRestriction impl) { impl = new PermanentRestriction(_bridgehub, _l2AdminFactory); - TransparentUpgradeableProxy tup = new TransparentUpgradeableProxy(address(impl), address(uint160(1)), abi.encodeCall(PermanentRestriction.initialize, (_owner))); + TransparentUpgradeableProxy tup = new TransparentUpgradeableProxy( + address(impl), + address(uint160(1)), + abi.encodeCall(PermanentRestriction.initialize, (_owner)) + ); proxy = PermanentRestriction(address(tup)); } @@ -69,7 +69,11 @@ contract PermanentRestrictionTest is ChainTypeManagerTest { function test_ownerAsAddressZero() public { PermanentRestriction impl = new PermanentRestriction(bridgehub, L2_FACTORY_ADDR); vm.expectRevert(ZeroAddress.selector); - new TransparentUpgradeableProxy(address(impl), address(uint160(1)), abi.encodeCall(PermanentRestriction.initialize, (address(0)))); + new TransparentUpgradeableProxy( + address(impl), + address(uint160(1)), + abi.encodeCall(PermanentRestriction.initialize, (address(0))) + ); } function test_allowAdminImplementation(bytes32 implementationHash) public { From c2c3dbe61f17675acf6ff755499c6c6ddeb84c1c Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Wed, 25 Sep 2024 13:50:33 +0200 Subject: [PATCH 08/20] tests wip --- .../governance/PermanentRestriction.sol | 2 +- .../Governance/PermanentRestriction.t.sol | 116 +++++++++++++++++- 2 files changed, 116 insertions(+), 2 deletions(-) diff --git a/l1-contracts/contracts/governance/PermanentRestriction.sol b/l1-contracts/contracts/governance/PermanentRestriction.sol index 83745de5e..976036b91 100644 --- a/l1-contracts/contracts/governance/PermanentRestriction.sol +++ b/l1-contracts/contracts/governance/PermanentRestriction.sol @@ -256,7 +256,7 @@ contract PermanentRestriction is IRestriction, IPermanentRestriction, Ownable2St ); if (request.secondBridgeAddress != sharedBridge) { - revert InvalidAddress(request.secondBridgeAddress, sharedBridge); + revert InvalidAddress(sharedBridge, request.secondBridgeAddress); } bytes memory secondBridgeData = request.secondBridgeCalldata; diff --git a/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol index 245028235..99c03cd27 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol @@ -3,12 +3,13 @@ pragma solidity 0.8.24; import "@openzeppelin/contracts-v4/utils/Strings.sol"; import {TransparentUpgradeableProxy} from "@openzeppelin/contracts-v4/proxy/transparent/TransparentUpgradeableProxy.sol"; import {Bridgehub} from "contracts/bridgehub/Bridgehub.sol"; +import {L2TransactionRequestTwoBridgesOuter, BridgehubBurnCTMAssetData} from "contracts/bridgehub/IBridgehub.sol"; import {Diamond} from "contracts/state-transition/libraries/Diamond.sol"; import {ChainTypeManager} from "contracts/state-transition/ChainTypeManager.sol"; import {DiamondInit} from "contracts/state-transition/chain-deps/DiamondInit.sol"; import {PermanentRestriction} from "contracts/governance/PermanentRestriction.sol"; import {IPermanentRestriction} from "contracts/governance/IPermanentRestriction.sol"; -import {ZeroAddress, ChainZeroAddress, NotAnAdmin, UnallowedImplementation, RemovingPermanentRestriction, CallNotAllowed} from "contracts/common/L1ContractErrors.sol"; +import {InvalidAddress, UnsupportedEncodingVersion, InvalidSelector, NotBridgehub, ZeroAddress, ChainZeroAddress, NotAnAdmin, UnallowedImplementation, RemovingPermanentRestriction, CallNotAllowed} from "contracts/common/L1ContractErrors.sol"; import {Call} from "contracts/governance/Common.sol"; import {IZKChain} from "contracts/state-transition/chain-interfaces/IZKChain.sol"; import {VerifierParams, FeeParams, PubdataPricingMode} from "contracts/state-transition/chain-deps/ZKChainStorage.sol"; @@ -221,6 +222,119 @@ contract PermanentRestrictionTest is ChainTypeManagerTest { vm.stopPrank(); } + function _encodeMigraationCall( + bool correctTarget, + bool correctSelector, + bool correctSecondBridge, + bool correctEncodingVersion, + bool correctAssetId, + address l2Admin + ) internal returns (Call memory call) { + if (!correctTarget) { + call.target = address(0); + return call; + } + call.target = address(bridgehub); + + if (!correctSelector) { + call.data = hex"00000000"; + return call; + } + + L2TransactionRequestTwoBridgesOuter memory outer = L2TransactionRequestTwoBridgesOuter({ + chainId: chainId, + mintValue: 0, + l2Value: 0, + l2GasLimit: 0, + l2GasPerPubdataByteLimit: 0, + refundRecipient: address(0), + secondBridgeAddress: address(0), + secondBridgeValue: 0, + secondBridgeCalldata: hex"" + }); + if (!correctSecondBridge) { + call.data = abi.encodeCall(Bridgehub.requestL2TransactionTwoBridges, (outer)); + // 0 is not correct second bridge + return call; + } + outer.secondBridgeAddress = sharedBridge; + + + uint8 encoding = correctEncodingVersion ? 1 : 12; + + bytes32 chainAssetId = correctAssetId ? bridgehub.ctmAssetIdFromChainId(chainId) : bytes32(0); + + bytes memory bridgehubData = abi.encode( + BridgehubBurnCTMAssetData({ + // Gateway chain id, we do not need it + chainId: 0, + ctmData: abi.encode(l2Admin, hex""), + chainData: abi.encode(IZKChain(IBridgehub(bridgehub).getZKChain(chainId)).getProtocolVersion()) + }) + ); + outer.secondBridgeCalldata = abi.encodePacked(bytes1(encoding), abi.encode(chainAssetId, bridgehubData)); + + call.data = abi.encodeCall(Bridgehub.requestL2TransactionTwoBridges, (outer)); + } + + function test_tryGetNewAdminFromMigrationRevertWhenInvalidSelector() public { + Call memory call = _encodeMigraationCall( + false, + true, + true, + true, + true, + address(0) + ); + + vm.expectRevert(abi.encodeWithSelector(NotBridgehub.selector, address(0))); + permRestriction.tryGetNewAdminFromMigration(call); + } + + function test_tryGetNewAdminFromMigrationRevertWhenNotBridgehub() public { + Call memory call = _encodeMigraationCall( + true, + false, + true, + true, + true, + address(0) + ); + + vm.expectRevert(abi.encodeWithSelector(InvalidSelector.selector, bytes4(0))); + permRestriction.tryGetNewAdminFromMigration(call); + } + + + function test_tryGetNewAdminFromMigrationRevertWhenNotSharedBridge() public { + Call memory call = _encodeMigraationCall( + true, + true, + false, + true, + true, + address(0) + ); + + vm.expectRevert(abi.encodeWithSelector(InvalidAddress.selector, address(sharedBridge), address(0))); + permRestriction.tryGetNewAdminFromMigration(call); + } + + function test_tryGetNewAdminFromMigrationRevertWhenIncorrectEncoding() public { + Call memory call = _encodeMigraationCall( + true, + true, + true, + false, + true, + address(0) + ); + + vm.expectRevert(abi.encodeWithSelector(UnsupportedEncodingVersion.selector)); + permRestriction.tryGetNewAdminFromMigration(call); + } + + function createNewChainBridgehub() internal { bytes[] memory factoryDeps = new bytes[](0); vm.stopPrank(); From 6ea2aed59df00eb76008c9fb71cd9605b1270c29 Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Wed, 25 Sep 2024 14:04:23 +0200 Subject: [PATCH 09/20] min gas for fallable call --- .../contracts/governance/PermanentRestriction.sol | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/l1-contracts/contracts/governance/PermanentRestriction.sol b/l1-contracts/contracts/governance/PermanentRestriction.sol index 976036b91..748ae675a 100644 --- a/l1-contracts/contracts/governance/PermanentRestriction.sol +++ b/l1-contracts/contracts/governance/PermanentRestriction.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.24; -import {UnsupportedEncodingVersion, CallNotAllowed, ChainZeroAddress, NotAHyperchain, NotAnAdmin, RemovingPermanentRestriction, ZeroAddress, UnallowedImplementation, AlreadyWhitelisted, NotWhitelisted, NotBridgehub, InvalidSelector, InvalidAddress} from "../common/L1ContractErrors.sol"; +import {UnsupportedEncodingVersion, CallNotAllowed, ChainZeroAddress, NotAHyperchain, NotAnAdmin, RemovingPermanentRestriction, ZeroAddress, UnallowedImplementation, AlreadyWhitelisted, NotWhitelisted, NotBridgehub, InvalidSelector, InvalidAddress, NotEnoughGas} from "../common/L1ContractErrors.sol"; import {L2TransactionRequestTwoBridgesOuter, BridgehubBurnCTMAssetData} from "../bridgehub/IBridgehub.sol"; import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable-v4/access/Ownable2StepUpgradeable.sol"; @@ -26,6 +26,11 @@ import {IPermanentRestriction} from "./IPermanentRestriction.sol"; /// @dev To be deployed as a transparent upgradable proxy, owned by a trusted decentralized governance. /// @dev Once of the instances of such contract is to ensure that a ZkSyncHyperchain is a rollup forever. contract PermanentRestriction is IRestriction, IPermanentRestriction, Ownable2StepUpgradeable { + /// @notice We use try-catch to test whether some of the conditions should be checked. + /// To avoid attacks based on teh 63/64 gas limitations, we ensure that each such call + /// has at least this amount. + uint256 constant MIN_GAS_FOR_FALLABLE_CALL = 5_000_000; + /// @notice The address of the Bridgehub contract. IBridgehub public immutable BRIDGE_HUB; @@ -124,6 +129,7 @@ contract PermanentRestriction is IRestriction, IPermanentRestriction, Ownable2St /// @dev Note that we do not need to validate the migration to the L1 layer as the admin /// is not changed in this case. function _validateMigrationToL2(Call calldata _call) internal view { + _ensureEnoughGas(); try this.tryGetNewAdminFromMigration(_call) returns (address admin) { if (!whitelistedL2Admins[admin]) { revert NotWhitelisted(admin); @@ -205,6 +211,7 @@ contract PermanentRestriction is IRestriction, IPermanentRestriction, Ownable2St /// @notice Checks if the `msg.sender` is an admin of a certain ZkSyncHyperchain. /// @param _chain The address of the chain. function _isAdminOfAChain(address _chain) internal view returns (bool) { + _ensureEnoughGas(); (bool success, ) = address(this).staticcall(abi.encodeCall(this.tryCompareAdminOfAChain, (_chain, msg.sender))); return success; } @@ -282,4 +289,10 @@ contract PermanentRestriction is IRestriction, IPermanentRestriction, Ownable2St return l2Admin; } + + function _ensureEnoughGas() internal view { + if (gasleft() < MIN_GAS_FOR_FALLABLE_CALL) { + revert NotEnoughGas(); + } + } } From 4be4c29174e8a541e3769d1a54fdab359398bfb8 Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Wed, 25 Sep 2024 15:09:49 +0200 Subject: [PATCH 10/20] full test cover for tryGetNewAdminFromMigration --- .../Governance/PermanentRestriction.t.sol | 39 ++++++++++++++++++- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol index 99c03cd27..59899da0b 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol @@ -41,8 +41,6 @@ contract PermanentRestrictionTest is ChainTypeManagerTest { createNewChainBridgehub(); - vm.stopPrank(); - owner = makeAddr("owner"); hyperchain = chainContractAddress.getHyperchain(chainId); (permRestriction, ) = _deployPermRestriction(bridgehub, L2_FACTORY_ADDR, owner); @@ -334,6 +332,34 @@ contract PermanentRestrictionTest is ChainTypeManagerTest { permRestriction.tryGetNewAdminFromMigration(call); } + function test_tryGetNewAdminFromMigrationRevertWhenIncorrectAssetId() public { + Call memory call = _encodeMigraationCall( + true, + true, + true, + true, + false, + address(0) + ); + + vm.expectRevert(abi.encodeWithSelector(ZeroAddress.selector)); + permRestriction.tryGetNewAdminFromMigration(call); + } + + function test_tryGetNewAdminFromMigrationShouldWorkCorrectly() public { + address l2Addr = makeAddr("l2Addr"); + Call memory call = _encodeMigraationCall( + true, + true, + true, + true, + true, + l2Addr + ); + + address result = permRestriction.tryGetNewAdminFromMigration(call); + assertEq(result, l2Addr); + } function createNewChainBridgehub() internal { bytes[] memory factoryDeps = new bytes[](0); @@ -342,6 +368,13 @@ contract PermanentRestrictionTest is ChainTypeManagerTest { bridgehub.addChainTypeManager(address(chainContractAddress)); bridgehub.addTokenAssetId(DataEncoding.encodeNTVAssetId(block.chainid, baseToken)); bridgehub.setAddresses(sharedBridge, ICTMDeploymentTracker(address(0)), new MessageRoot(bridgehub)); + vm.stopPrank(); + + // ctm deployer address is 0 in this test + vm.startPrank(address(0)); + bridgehub.setAssetHandlerAddress(bytes32(uint256(uint160(address(chainContractAddress)))), address(chainContractAddress)); + vm.stopPrank(); + address l1Nullifier = makeAddr("l1Nullifier"); address l2LegacySharedBridge = makeAddr("l2LegacySharedBridge"); vm.mockCall( @@ -354,6 +387,7 @@ contract PermanentRestrictionTest is ChainTypeManagerTest { abi.encodeWithSelector(IL1Nullifier.l2BridgeAddress.selector), abi.encode(l2LegacySharedBridge) ); + vm.startPrank(governor); bridgehub.createNewChain({ _chainId: chainId, _chainTypeManager: address(chainContractAddress), @@ -363,5 +397,6 @@ contract PermanentRestrictionTest is ChainTypeManagerTest { _initData: getCTMInitData(), _factoryDeps: factoryDeps }); + vm.stopPrank(); } } From c77fd57b3d974b80d3cbe33961e5b95e1d766b91 Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Wed, 25 Sep 2024 15:31:25 +0200 Subject: [PATCH 11/20] some rename + better coverage --- .../contracts/common/L1ContractErrors.sol | 2 + .../governance/IPermanentRestriction.sol | 2 +- .../governance/PermanentRestriction.sol | 26 ++++---- .../Governance/PermanentRestriction.t.sol | 63 ++++++++++++++++++- 4 files changed, 77 insertions(+), 16 deletions(-) diff --git a/l1-contracts/contracts/common/L1ContractErrors.sol b/l1-contracts/contracts/common/L1ContractErrors.sol index 4e4a71ee7..a0c470a67 100644 --- a/l1-contracts/contracts/common/L1ContractErrors.sol +++ b/l1-contracts/contracts/common/L1ContractErrors.sol @@ -411,6 +411,8 @@ error AssetHandlerNotRegistered(bytes32 assetId); error NotBridgehub(address addr); // 0x2554babc error InvalidAddress(address expected, address actual); +// 0xfa5cd00f +error NotAllowed(address addr); enum SharedBridgeKey { PostUpgradeFirstBatch, diff --git a/l1-contracts/contracts/governance/IPermanentRestriction.sol b/l1-contracts/contracts/governance/IPermanentRestriction.sol index 7169ddf14..5fb015e33 100644 --- a/l1-contracts/contracts/governance/IPermanentRestriction.sol +++ b/l1-contracts/contracts/governance/IPermanentRestriction.sol @@ -16,5 +16,5 @@ interface IPermanentRestriction { event SelectorValidationChanged(bytes4 indexed selector, bool isValidated); /// @notice Emitted when the L2 admin is whitelisted or not. - event WhitelistL2Admin(address indexed adminAddress, bool isWhitelisted); + event AllowL2Admin(address indexed adminAddress); } diff --git a/l1-contracts/contracts/governance/PermanentRestriction.sol b/l1-contracts/contracts/governance/PermanentRestriction.sol index 748ae675a..5dda4f071 100644 --- a/l1-contracts/contracts/governance/PermanentRestriction.sol +++ b/l1-contracts/contracts/governance/PermanentRestriction.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.24; -import {UnsupportedEncodingVersion, CallNotAllowed, ChainZeroAddress, NotAHyperchain, NotAnAdmin, RemovingPermanentRestriction, ZeroAddress, UnallowedImplementation, AlreadyWhitelisted, NotWhitelisted, NotBridgehub, InvalidSelector, InvalidAddress, NotEnoughGas} from "../common/L1ContractErrors.sol"; +import {UnsupportedEncodingVersion, CallNotAllowed, ChainZeroAddress, NotAHyperchain, NotAnAdmin, RemovingPermanentRestriction, ZeroAddress, UnallowedImplementation, AlreadyWhitelisted, NotAllowed, NotBridgehub, InvalidSelector, InvalidAddress, NotEnoughGas} from "../common/L1ContractErrors.sol"; import {L2TransactionRequestTwoBridgesOuter, BridgehubBurnCTMAssetData} from "../bridgehub/IBridgehub.sol"; import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable-v4/access/Ownable2StepUpgradeable.sol"; @@ -18,6 +18,11 @@ import {IAdmin} from "../state-transition/chain-interfaces/IAdmin.sol"; import {IPermanentRestriction} from "./IPermanentRestriction.sol"; +/// @dev We use try-catch to test whether some of the conditions should be checked. +/// To avoid attacks based on teh 63/64 gas limitations, we ensure that each such call +/// has at least this amount. +uint256 constant MIN_GAS_FOR_FALLABLE_CALL = 5_000_000; + /// @title PermanentRestriction contract /// @author Matter Labs /// @custom:security-contact security@matterlabs.dev @@ -26,11 +31,6 @@ import {IPermanentRestriction} from "./IPermanentRestriction.sol"; /// @dev To be deployed as a transparent upgradable proxy, owned by a trusted decentralized governance. /// @dev Once of the instances of such contract is to ensure that a ZkSyncHyperchain is a rollup forever. contract PermanentRestriction is IRestriction, IPermanentRestriction, Ownable2StepUpgradeable { - /// @notice We use try-catch to test whether some of the conditions should be checked. - /// To avoid attacks based on teh 63/64 gas limitations, we ensure that each such call - /// has at least this amount. - uint256 constant MIN_GAS_FOR_FALLABLE_CALL = 5_000_000; - /// @notice The address of the Bridgehub contract. IBridgehub public immutable BRIDGE_HUB; @@ -50,7 +50,7 @@ contract PermanentRestriction is IRestriction, IPermanentRestriction, Ownable2St mapping(bytes4 selector => bool isValidated) public validatedSelectors; /// @notice The mapping of whitelisted L2 admins. - mapping(address adminAddress => bool isWhitelisted) public whitelistedL2Admins; + mapping(address adminAddress => bool isWhitelisted) public allowedL2Admins; constructor(IBridgehub _bridgehub, address _l2AdminFactory) { BRIDGE_HUB = _bridgehub; @@ -96,7 +96,7 @@ contract PermanentRestriction is IRestriction, IPermanentRestriction, Ownable2St /// @param deploymentSalt The salt for the deployment. /// @param l2BytecodeHash The hash of the L2 bytecode. /// @param constructorInputHash The hash of the constructor data for the deployment. - function whitelistL2Admin(bytes32 deploymentSalt, bytes32 l2BytecodeHash, bytes32 constructorInputHash) external { + function allowL2Admin(bytes32 deploymentSalt, bytes32 l2BytecodeHash, bytes32 constructorInputHash) external { // We do not do any additional validations for constructor data or the bytecode, // we expect that only admins of the allowed format are to be deployed. address expectedAddress = L2ContractHelper.computeCreate2Address( @@ -106,12 +106,12 @@ contract PermanentRestriction is IRestriction, IPermanentRestriction, Ownable2St constructorInputHash ); - if (whitelistedL2Admins[expectedAddress]) { + if (allowedL2Admins[expectedAddress]) { revert AlreadyWhitelisted(expectedAddress); } - whitelistedL2Admins[expectedAddress] = true; - emit WhitelistL2Admin(expectedAddress, true); + allowedL2Admins[expectedAddress] = true; + emit AllowL2Admin(expectedAddress); } /// @inheritdoc IRestriction @@ -131,8 +131,8 @@ contract PermanentRestriction is IRestriction, IPermanentRestriction, Ownable2St function _validateMigrationToL2(Call calldata _call) internal view { _ensureEnoughGas(); try this.tryGetNewAdminFromMigration(_call) returns (address admin) { - if (!whitelistedL2Admins[admin]) { - revert NotWhitelisted(admin); + if (!allowedL2Admins[admin]) { + revert NotAllowed(admin); } } catch { // It was not the migration call, so we do nothing diff --git a/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol index 59899da0b..84e4d4793 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol @@ -7,9 +7,9 @@ import {L2TransactionRequestTwoBridgesOuter, BridgehubBurnCTMAssetData} from "co import {Diamond} from "contracts/state-transition/libraries/Diamond.sol"; import {ChainTypeManager} from "contracts/state-transition/ChainTypeManager.sol"; import {DiamondInit} from "contracts/state-transition/chain-deps/DiamondInit.sol"; -import {PermanentRestriction} from "contracts/governance/PermanentRestriction.sol"; +import {PermanentRestriction, MIN_GAS_FOR_FALLABLE_CALL} from "contracts/governance/PermanentRestriction.sol"; import {IPermanentRestriction} from "contracts/governance/IPermanentRestriction.sol"; -import {InvalidAddress, UnsupportedEncodingVersion, InvalidSelector, NotBridgehub, ZeroAddress, ChainZeroAddress, NotAnAdmin, UnallowedImplementation, RemovingPermanentRestriction, CallNotAllowed} from "contracts/common/L1ContractErrors.sol"; +import {NotAllowed, NotEnoughGas, InvalidAddress, UnsupportedEncodingVersion, InvalidSelector, NotBridgehub, ZeroAddress, ChainZeroAddress, NotAnAdmin, UnallowedImplementation, RemovingPermanentRestriction, CallNotAllowed} from "contracts/common/L1ContractErrors.sol"; import {Call} from "contracts/governance/Common.sol"; import {IZKChain} from "contracts/state-transition/chain-interfaces/IZKChain.sol"; import {VerifierParams, FeeParams, PubdataPricingMode} from "contracts/state-transition/chain-deps/ZKChainStorage.sol"; @@ -25,6 +25,7 @@ import {MessageRoot} from "contracts/bridgehub/MessageRoot.sol"; import {IL1AssetRouter} from "contracts/bridge/asset-router/IL1AssetRouter.sol"; import {IL1Nullifier} from "contracts/bridge/interfaces/IL1Nullifier.sol"; import {IBridgehub} from "contracts/bridgehub/IBridgehub.sol"; +import {L2ContractHelper} from "contracts/common/libraries/L2ContractHelper.sol"; contract PermanentRestrictionTest is ChainTypeManagerTest { ChainAdmin internal chainAdmin; @@ -361,6 +362,64 @@ contract PermanentRestrictionTest is ChainTypeManagerTest { assertEq(result, l2Addr); } + function test_validateMigrationToL2RevertNotAllowed() public { + Call memory call = _encodeMigraationCall( + true, + true, + true, + true, + true, + address(0) + ); + + vm.expectRevert(abi.encodeWithSelector(NotAllowed.selector, address(0))); + permRestriction.validateCall(call, owner); + } + + function test_validateMigrationToL2() public { + address expectedAddress = L2ContractHelper.computeCreate2Address( + L2_FACTORY_ADDR, + bytes32(0), + bytes32(0), + bytes32(0) + ); + + vm.expectEmit(true, false, false, true); + emit IPermanentRestriction.AllowL2Admin(expectedAddress); + permRestriction.allowL2Admin( + bytes32(0), + bytes32(0), + bytes32(0) + ); + + Call memory call = _encodeMigraationCall( + true, + true, + true, + true, + true, + expectedAddress + ); + + // Should not fail + permRestriction.validateCall(call, owner); + } + + function test_validateNotEnoughGas() public { + address l2Addr = makeAddr("l2Addr"); + Call memory call = _encodeMigraationCall( + true, + true, + true, + true, + true, + l2Addr + ); + + vm.expectRevert(abi.encodeWithSelector(NotEnoughGas.selector)); + permRestriction.validateCall{gas: MIN_GAS_FOR_FALLABLE_CALL}(call, address(0)); + } + function createNewChainBridgehub() internal { bytes[] memory factoryDeps = new bytes[](0); vm.stopPrank(); From 950862e5852b375753bfecd014dd4847819e76c8 Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Wed, 25 Sep 2024 15:32:29 +0200 Subject: [PATCH 12/20] lint fix --- .../governance/PermanentRestriction.sol | 4 +- .../Governance/PermanentRestriction.t.sol | 110 ++++-------------- 2 files changed, 24 insertions(+), 90 deletions(-) diff --git a/l1-contracts/contracts/governance/PermanentRestriction.sol b/l1-contracts/contracts/governance/PermanentRestriction.sol index 5dda4f071..6e34e3ba3 100644 --- a/l1-contracts/contracts/governance/PermanentRestriction.sol +++ b/l1-contracts/contracts/governance/PermanentRestriction.sol @@ -19,9 +19,9 @@ import {IAdmin} from "../state-transition/chain-interfaces/IAdmin.sol"; import {IPermanentRestriction} from "./IPermanentRestriction.sol"; /// @dev We use try-catch to test whether some of the conditions should be checked. -/// To avoid attacks based on teh 63/64 gas limitations, we ensure that each such call +/// To avoid attacks based on teh 63/64 gas limitations, we ensure that each such call /// has at least this amount. -uint256 constant MIN_GAS_FOR_FALLABLE_CALL = 5_000_000; +uint256 constant MIN_GAS_FOR_FALLABLE_CALL = 5_000_000; /// @title PermanentRestriction contract /// @author Matter Labs diff --git a/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol index 84e4d4793..c7eede8de 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol @@ -222,7 +222,7 @@ contract PermanentRestrictionTest is ChainTypeManagerTest { } function _encodeMigraationCall( - bool correctTarget, + bool correctTarget, bool correctSelector, bool correctSecondBridge, bool correctEncodingVersion, @@ -258,9 +258,8 @@ contract PermanentRestrictionTest is ChainTypeManagerTest { } outer.secondBridgeAddress = sharedBridge; - uint8 encoding = correctEncodingVersion ? 1 : 12; - + bytes32 chainAssetId = correctAssetId ? bridgehub.ctmAssetIdFromChainId(chainId) : bytes32(0); bytes memory bridgehubData = abi.encode( @@ -277,100 +276,50 @@ contract PermanentRestrictionTest is ChainTypeManagerTest { } function test_tryGetNewAdminFromMigrationRevertWhenInvalidSelector() public { - Call memory call = _encodeMigraationCall( - false, - true, - true, - true, - true, - address(0) - ); + Call memory call = _encodeMigraationCall(false, true, true, true, true, address(0)); vm.expectRevert(abi.encodeWithSelector(NotBridgehub.selector, address(0))); - permRestriction.tryGetNewAdminFromMigration(call); + permRestriction.tryGetNewAdminFromMigration(call); } function test_tryGetNewAdminFromMigrationRevertWhenNotBridgehub() public { - Call memory call = _encodeMigraationCall( - true, - false, - true, - true, - true, - address(0) - ); + Call memory call = _encodeMigraationCall(true, false, true, true, true, address(0)); vm.expectRevert(abi.encodeWithSelector(InvalidSelector.selector, bytes4(0))); - permRestriction.tryGetNewAdminFromMigration(call); + permRestriction.tryGetNewAdminFromMigration(call); } - function test_tryGetNewAdminFromMigrationRevertWhenNotSharedBridge() public { - Call memory call = _encodeMigraationCall( - true, - true, - false, - true, - true, - address(0) - ); + Call memory call = _encodeMigraationCall(true, true, false, true, true, address(0)); vm.expectRevert(abi.encodeWithSelector(InvalidAddress.selector, address(sharedBridge), address(0))); - permRestriction.tryGetNewAdminFromMigration(call); + permRestriction.tryGetNewAdminFromMigration(call); } function test_tryGetNewAdminFromMigrationRevertWhenIncorrectEncoding() public { - Call memory call = _encodeMigraationCall( - true, - true, - true, - false, - true, - address(0) - ); + Call memory call = _encodeMigraationCall(true, true, true, false, true, address(0)); vm.expectRevert(abi.encodeWithSelector(UnsupportedEncodingVersion.selector)); - permRestriction.tryGetNewAdminFromMigration(call); + permRestriction.tryGetNewAdminFromMigration(call); } function test_tryGetNewAdminFromMigrationRevertWhenIncorrectAssetId() public { - Call memory call = _encodeMigraationCall( - true, - true, - true, - true, - false, - address(0) - ); + Call memory call = _encodeMigraationCall(true, true, true, true, false, address(0)); vm.expectRevert(abi.encodeWithSelector(ZeroAddress.selector)); - permRestriction.tryGetNewAdminFromMigration(call); + permRestriction.tryGetNewAdminFromMigration(call); } function test_tryGetNewAdminFromMigrationShouldWorkCorrectly() public { address l2Addr = makeAddr("l2Addr"); - Call memory call = _encodeMigraationCall( - true, - true, - true, - true, - true, - l2Addr - ); + Call memory call = _encodeMigraationCall(true, true, true, true, true, l2Addr); address result = permRestriction.tryGetNewAdminFromMigration(call); - assertEq(result, l2Addr); + assertEq(result, l2Addr); } function test_validateMigrationToL2RevertNotAllowed() public { - Call memory call = _encodeMigraationCall( - true, - true, - true, - true, - true, - address(0) - ); + Call memory call = _encodeMigraationCall(true, true, true, true, true, address(0)); vm.expectRevert(abi.encodeWithSelector(NotAllowed.selector, address(0))); permRestriction.validateCall(call, owner); @@ -386,20 +335,9 @@ contract PermanentRestrictionTest is ChainTypeManagerTest { vm.expectEmit(true, false, false, true); emit IPermanentRestriction.AllowL2Admin(expectedAddress); - permRestriction.allowL2Admin( - bytes32(0), - bytes32(0), - bytes32(0) - ); + permRestriction.allowL2Admin(bytes32(0), bytes32(0), bytes32(0)); - Call memory call = _encodeMigraationCall( - true, - true, - true, - true, - true, - expectedAddress - ); + Call memory call = _encodeMigraationCall(true, true, true, true, true, expectedAddress); // Should not fail permRestriction.validateCall(call, owner); @@ -407,14 +345,7 @@ contract PermanentRestrictionTest is ChainTypeManagerTest { function test_validateNotEnoughGas() public { address l2Addr = makeAddr("l2Addr"); - Call memory call = _encodeMigraationCall( - true, - true, - true, - true, - true, - l2Addr - ); + Call memory call = _encodeMigraationCall(true, true, true, true, true, l2Addr); vm.expectRevert(abi.encodeWithSelector(NotEnoughGas.selector)); permRestriction.validateCall{gas: MIN_GAS_FOR_FALLABLE_CALL}(call, address(0)); @@ -431,7 +362,10 @@ contract PermanentRestrictionTest is ChainTypeManagerTest { // ctm deployer address is 0 in this test vm.startPrank(address(0)); - bridgehub.setAssetHandlerAddress(bytes32(uint256(uint160(address(chainContractAddress)))), address(chainContractAddress)); + bridgehub.setAssetHandlerAddress( + bytes32(uint256(uint160(address(chainContractAddress)))), + address(chainContractAddress) + ); vm.stopPrank(); address l1Nullifier = makeAddr("l1Nullifier"); From 16a26bc5c357f62a588daae7a073f101e0b68342 Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Wed, 25 Sep 2024 15:34:04 +0200 Subject: [PATCH 13/20] comment --- l1-contracts/contracts/governance/PermanentRestriction.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/l1-contracts/contracts/governance/PermanentRestriction.sol b/l1-contracts/contracts/governance/PermanentRestriction.sol index 6e34e3ba3..81b472fee 100644 --- a/l1-contracts/contracts/governance/PermanentRestriction.sol +++ b/l1-contracts/contracts/governance/PermanentRestriction.sol @@ -19,7 +19,7 @@ import {IAdmin} from "../state-transition/chain-interfaces/IAdmin.sol"; import {IPermanentRestriction} from "./IPermanentRestriction.sol"; /// @dev We use try-catch to test whether some of the conditions should be checked. -/// To avoid attacks based on teh 63/64 gas limitations, we ensure that each such call +/// To avoid attacks based on the 63/64 gas limitations, we ensure that each such call /// has at least this amount. uint256 constant MIN_GAS_FOR_FALLABLE_CALL = 5_000_000; From 30f1e549b377adbb551d1db993bb8f4cedf708ae Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Wed, 25 Sep 2024 16:02:49 +0200 Subject: [PATCH 14/20] add tests for l2 admin factory --- .../unit/L2AdminFactory/L2AdminFactory.t.sol | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 l1-contracts/test/foundry/l2/unit/L2AdminFactory/L2AdminFactory.t.sol diff --git a/l1-contracts/test/foundry/l2/unit/L2AdminFactory/L2AdminFactory.t.sol b/l1-contracts/test/foundry/l2/unit/L2AdminFactory/L2AdminFactory.t.sol new file mode 100644 index 000000000..23dbf5486 --- /dev/null +++ b/l1-contracts/test/foundry/l2/unit/L2AdminFactory/L2AdminFactory.t.sol @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.20; + +import {Test} from "forge-std/Test.sol"; + +import {IBridgehub} from "contracts/bridgehub/IBridgehub.sol"; +import { L2AdminFactory } from "contracts/governance/L2AdminFactory.sol"; +import { PermanentRestriction } from "contracts/governance/PermanentRestriction.sol"; +import { IPermanentRestriction } from "contracts/governance/IPermanentRestriction.sol"; + +contract L2AdminFactoryTest is Test { + function testL2AdminFactory() public { + address[] memory requiredRestrictions = new address[](1); + requiredRestrictions[0] = makeAddr("required"); + + L2AdminFactory factory = new L2AdminFactory(requiredRestrictions); + + address[] memory additionalRestrictions = new address[](1); + additionalRestrictions[0] = makeAddr("additional"); + + address[] memory allRestrictions = new address[](2); + allRestrictions[0] = requiredRestrictions[0]; + allRestrictions[1] = additionalRestrictions[0]; + + + bytes32 salt = keccak256("salt"); + + address admin = factory.deployAdmin(additionalRestrictions, salt); + + // Now, we need to check whether it would be able to accept such an admin + PermanentRestriction restriction = new PermanentRestriction( + IBridgehub(address(0)), + address(factory) + ); + + bytes32 codeHash; + assembly { + codeHash := extcodehash(admin) + } + + vm.expectEmit(true, false, false, true); + emit IPermanentRestriction.AllowL2Admin(admin); + restriction.allowL2Admin( + salt, + codeHash, + keccak256(abi.encode(allRestrictions)) + ); + } +} From 2c6d11637b0c3a24063ed1014862785b72b379c1 Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Wed, 25 Sep 2024 16:05:44 +0200 Subject: [PATCH 15/20] fix lint --- .../unit/L2AdminFactory/L2AdminFactory.t.sol | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/l1-contracts/test/foundry/l2/unit/L2AdminFactory/L2AdminFactory.t.sol b/l1-contracts/test/foundry/l2/unit/L2AdminFactory/L2AdminFactory.t.sol index 23dbf5486..7b85a8c54 100644 --- a/l1-contracts/test/foundry/l2/unit/L2AdminFactory/L2AdminFactory.t.sol +++ b/l1-contracts/test/foundry/l2/unit/L2AdminFactory/L2AdminFactory.t.sol @@ -5,9 +5,9 @@ pragma solidity ^0.8.20; import {Test} from "forge-std/Test.sol"; import {IBridgehub} from "contracts/bridgehub/IBridgehub.sol"; -import { L2AdminFactory } from "contracts/governance/L2AdminFactory.sol"; -import { PermanentRestriction } from "contracts/governance/PermanentRestriction.sol"; -import { IPermanentRestriction } from "contracts/governance/IPermanentRestriction.sol"; +import {L2AdminFactory} from "contracts/governance/L2AdminFactory.sol"; +import {PermanentRestriction} from "contracts/governance/PermanentRestriction.sol"; +import {IPermanentRestriction} from "contracts/governance/IPermanentRestriction.sol"; contract L2AdminFactoryTest is Test { function testL2AdminFactory() public { @@ -23,16 +23,12 @@ contract L2AdminFactoryTest is Test { allRestrictions[0] = requiredRestrictions[0]; allRestrictions[1] = additionalRestrictions[0]; - bytes32 salt = keccak256("salt"); address admin = factory.deployAdmin(additionalRestrictions, salt); // Now, we need to check whether it would be able to accept such an admin - PermanentRestriction restriction = new PermanentRestriction( - IBridgehub(address(0)), - address(factory) - ); + PermanentRestriction restriction = new PermanentRestriction(IBridgehub(address(0)), address(factory)); bytes32 codeHash; assembly { @@ -41,10 +37,6 @@ contract L2AdminFactoryTest is Test { vm.expectEmit(true, false, false, true); emit IPermanentRestriction.AllowL2Admin(admin); - restriction.allowL2Admin( - salt, - codeHash, - keccak256(abi.encode(allRestrictions)) - ); + restriction.allowL2Admin(salt, codeHash, keccak256(abi.encode(allRestrictions))); } } From 5bd14e18291115b841d5651683e105415822e886 Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Wed, 25 Sep 2024 16:22:07 +0200 Subject: [PATCH 16/20] exclude l2adminfactory as it is covered by l2 tests --- l1-contracts/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/l1-contracts/package.json b/l1-contracts/package.json index 6d68729e2..0c955188e 100644 --- a/l1-contracts/package.json +++ b/l1-contracts/package.json @@ -61,7 +61,7 @@ "test:foundry": "forge test --ffi --match-path 'test/foundry/l1/*'", "test:zkfoundry": "forge test --zksync --match-path 'test/foundry/l2/*'", "test:fork": "TEST_CONTRACTS_FORK=1 yarn run hardhat test test/unit_tests/*.fork.ts --network hardhat", - "coverage:foundry": "forge coverage --ffi --match-path 'test/foundry/l1/*' --no-match-coverage 'contracts/bridge/.*L2.*.sol'", + "coverage:foundry": "forge coverage --ffi --match-path 'test/foundry/l1/*' --no-match-coverage 'contracts/(bridge/.*L2.*\\.sol|governance/L2AdminFactory\\.sol)'", "deploy-no-build": "ts-node scripts/deploy.ts", "register-zk-chain": "ts-node scripts/register-zk-chain.ts", "deploy-weth-bridges": "ts-node scripts/deploy-weth-bridges.ts", From 61e8cda4b304a57c4d495b3a2c53e75b7a015dd8 Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Wed, 25 Sep 2024 17:48:28 +0200 Subject: [PATCH 17/20] use legacy functions --- l1-contracts/contracts/bridgehub/IBridgehub.sol | 4 ++++ l1-contracts/contracts/governance/PermanentRestriction.sol | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/l1-contracts/contracts/bridgehub/IBridgehub.sol b/l1-contracts/contracts/bridgehub/IBridgehub.sol index 5e2be03fc..dc6c23e7c 100644 --- a/l1-contracts/contracts/bridgehub/IBridgehub.sol +++ b/l1-contracts/contracts/bridgehub/IBridgehub.sol @@ -232,4 +232,8 @@ interface IBridgehub is IAssetHandler, IL1AssetHandler { function registerAlreadyDeployedZKChain(uint256 _chainId, address _hyperchain) external; function setLegacyChainAddress(uint256 _chainId) external; + + /// @notice return the ZK chain contract for a chainId + /// @dev It is a legacy method. Do not use! + function getHyperchain(uint256 _chainId) public view returns (address); } diff --git a/l1-contracts/contracts/governance/PermanentRestriction.sol b/l1-contracts/contracts/governance/PermanentRestriction.sol index 81b472fee..7d5c41c91 100644 --- a/l1-contracts/contracts/governance/PermanentRestriction.sol +++ b/l1-contracts/contracts/governance/PermanentRestriction.sol @@ -232,7 +232,9 @@ contract PermanentRestriction is IRestriction, IPermanentRestriction, Ownable2St // - Query the Bridgehub for the Hyperchain with the given `chainId`. // - We compare the corresponding addresses uint256 chainId = IZKChain(_chain).getChainId(); - if (BRIDGE_HUB.getZKChain(chainId) != _chain) { + // Note, that here it is important to use the legacy `getHyperchain` function, so that the contract + // is compatible with the legacy ones. + if (BRIDGE_HUB.getHyperchain(chainId) != _chain) { revert NotAHyperchain(_chain); } From a27fdeac7404394c7bc254e98b7dd43d630bc653 Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Wed, 25 Sep 2024 17:51:16 +0200 Subject: [PATCH 18/20] ensure that the function never panics --- .../contracts/governance/PermanentRestriction.sol | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/l1-contracts/contracts/governance/PermanentRestriction.sol b/l1-contracts/contracts/governance/PermanentRestriction.sol index 7d5c41c91..ee12b5ff0 100644 --- a/l1-contracts/contracts/governance/PermanentRestriction.sol +++ b/l1-contracts/contracts/governance/PermanentRestriction.sol @@ -231,7 +231,17 @@ contract PermanentRestriction is IRestriction, IPermanentRestriction, Ownable2St // - Query it for `chainId`. If it reverts, it is not a ZkSyncHyperchain. // - Query the Bridgehub for the Hyperchain with the given `chainId`. // - We compare the corresponding addresses - uint256 chainId = IZKChain(_chain).getChainId(); + + // Note, that we do not use an explicit call here to ensure that the function does not panic in case of + // incorrect `_chain` address. + (bool success, bytes memory data) = _chain.staticcall(abi.encodeWithSelector(IZKChain.getChainId.selector)); + if (!success || data.length < 32) { + revert NotAHyperchain(_chain); + } + + // Can not fail + uint256 chainId = abi.decode(data, (uint256)); + // Note, that here it is important to use the legacy `getHyperchain` function, so that the contract // is compatible with the legacy ones. if (BRIDGE_HUB.getHyperchain(chainId) != _chain) { From 8e4c1fc78406b87eafde5213627ee8ec0d374f62 Mon Sep 17 00:00:00 2001 From: Stanislav Breadless Date: Wed, 25 Sep 2024 17:55:23 +0200 Subject: [PATCH 19/20] fix things --- l1-contracts/contracts/bridgehub/IBridgehub.sol | 2 +- l1-contracts/contracts/governance/PermanentRestriction.sol | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/l1-contracts/contracts/bridgehub/IBridgehub.sol b/l1-contracts/contracts/bridgehub/IBridgehub.sol index dc6c23e7c..4e4931345 100644 --- a/l1-contracts/contracts/bridgehub/IBridgehub.sol +++ b/l1-contracts/contracts/bridgehub/IBridgehub.sol @@ -235,5 +235,5 @@ interface IBridgehub is IAssetHandler, IL1AssetHandler { /// @notice return the ZK chain contract for a chainId /// @dev It is a legacy method. Do not use! - function getHyperchain(uint256 _chainId) public view returns (address); + function getHyperchain(uint256 _chainId) external view returns (address); } diff --git a/l1-contracts/contracts/governance/PermanentRestriction.sol b/l1-contracts/contracts/governance/PermanentRestriction.sol index ee12b5ff0..153ce369e 100644 --- a/l1-contracts/contracts/governance/PermanentRestriction.sol +++ b/l1-contracts/contracts/governance/PermanentRestriction.sol @@ -14,6 +14,7 @@ import {IRestriction} from "./IRestriction.sol"; import {IChainAdmin} from "./IChainAdmin.sol"; import {IBridgehub} from "../bridgehub/IBridgehub.sol"; import {IZKChain} from "../state-transition/chain-interfaces/IZKChain.sol"; +import {IGetters} from "../state-transition/chain-interfaces/IGetters.sol"; import {IAdmin} from "../state-transition/chain-interfaces/IAdmin.sol"; import {IPermanentRestriction} from "./IPermanentRestriction.sol"; @@ -234,7 +235,7 @@ contract PermanentRestriction is IRestriction, IPermanentRestriction, Ownable2St // Note, that we do not use an explicit call here to ensure that the function does not panic in case of // incorrect `_chain` address. - (bool success, bytes memory data) = _chain.staticcall(abi.encodeWithSelector(IZKChain.getChainId.selector)); + (bool success, bytes memory data) = _chain.staticcall(abi.encodeWithSelector(IGetters.getChainId.selector)); if (!success || data.length < 32) { revert NotAHyperchain(_chain); } From c3030f58e6c13fb523cdc2fa5c095fa2f49c0666 Mon Sep 17 00:00:00 2001 From: Stanislav Bezkorovainyi Date: Mon, 30 Sep 2024 14:42:31 +0200 Subject: [PATCH 20/20] First iteration on testing the gateway upgrade (#826) --- .gitignore | 1 + l1-contracts/contracts/bridge/L1Nullifier.sol | 6 + .../contracts/bridge/L2SharedBridgeLegacy.sol | 44 +- .../bridge/asset-router/IL1AssetRouter.sol | 3 +- .../bridge/asset-router/IL2AssetRouter.sol | 8 + .../bridge/asset-router/L1AssetRouter.sol | 7 + .../bridge/asset-router/L2AssetRouter.sol | 32 + .../interfaces/IL1SharedBridgeLegacy.sol | 9 - .../interfaces/IL2SharedBridgeLegacy.sol | 11 + .../contracts/bridgehub/Bridgehub.sol | 15 +- .../dev-contracts/test/DummyBridgehub.sol | 6 + .../governance/L2ProxyAdminDeployer.sol | 22 + .../governance/TransitionaryOwner.sol | 25 + .../state-transition/ChainTypeManager.sol | 8 +- .../state-transition/IChainTypeManager.sol | 1 - .../chain-deps/DiamondInit.sol | 4 - .../chain-deps/ZKChainStorage.sol | 2 +- .../chain-deps/facets/Getters.sol | 5 - .../chain-deps/facets/Mailbox.sol | 6 +- .../chain-deps/facets/ZKChainBase.sol | 7 - .../chain-interfaces/IDiamondInit.sol | 2 - .../chain-interfaces/IGetters.sol | 3 - .../l2-deps/IL2GatewayUpgrade.sol | 13 + .../contracts/upgrades/GatewayHelper.sol | 24 + .../contracts/upgrades/GatewayUpgrade.sol | 56 +- .../contracts/upgrades/L1GenesisUpgrade.sol | 13 +- l1-contracts/deploy-scripts/DeployL1.s.sol | 11 +- .../L2ContractsBytecodesLib.sol | 43 + l1-contracts/deploy-scripts/Utils.sol | 83 +- .../deploy-scripts/upgrade/ChainUpgrade.s.sol | 212 +++ .../upgrade/EcosystemUpgrade.s.sol | 1348 +++++++++++++++++ l1-contracts/foundry.toml | 4 +- l1-contracts/package.json | 5 +- l1-contracts/src.ts/deploy.ts | 4 + l1-contracts/src.ts/utils.ts | 5 +- .../foundry/l1/integration/GatewayTests.t.sol | 2 - .../foundry/l1/integration/UpgradeTest.t.sol | 105 ++ .../integration/_SharedZKChainDeployer.t.sol | 2 - .../script-config/mainnet-era.toml | 7 + .../upgrade-envs/script-config/mainnet.toml | 37 + .../upgrade-envs/script-out/.gitkeep | 0 .../Bridgehub/experimental_bridge.t.sol | 4 +- .../concrete/DiamondCut/UpgradeLogic.t.sol | 1 - .../concrete/Executor/_Executor_Shared.t.sol | 3 +- .../Governance/PermanentRestriction.t.sol | 6 - .../foundry/l1/unit/concrete/Utils/Utils.sol | 72 +- .../l1/unit/concrete/Utils/UtilsFacet.sol | 8 - .../ChainTypeManager/CreateNewChain.t.sol | 1 - .../_ChainTypeManager_Shared.t.sol | 14 +- .../chain-deps/DiamondInit/Initialize.t.sol | 1 - .../facets/Getters/GetBaseTokenBridge.t.sol | 16 - .../facets/Getters/_Getters_Shared.t.sol | 4 - .../facets/Mailbox/FinalizeWithdrawal.t.sol | 5 +- .../facets/Mailbox/RequestL2Transaction.t.sol | 4 +- .../facets/Mailbox/_Mailbox_Shared.t.sol | 4 + system-contracts/SystemContractsHashes.json | 48 +- .../contracts/L2GatewayUpgrade.sol | 78 + .../contracts/L2GatewayUpgradeHelper.sol | 117 ++ .../contracts/L2GenesisUpgrade.sol | 108 +- .../interfaces/IL2GenesisUpgrade.sol | 5 + .../interfaces/IL2SharedBridgeLegacy.sol | 32 + .../libraries/SystemContractHelper.sol | 15 + system-contracts/package.json | 4 +- .../test/L2GenesisUpgrade.spec.ts | 7 +- 64 files changed, 2461 insertions(+), 297 deletions(-) create mode 100644 l1-contracts/contracts/governance/L2ProxyAdminDeployer.sol create mode 100644 l1-contracts/contracts/governance/TransitionaryOwner.sol create mode 100644 l1-contracts/contracts/state-transition/l2-deps/IL2GatewayUpgrade.sol create mode 100644 l1-contracts/contracts/upgrades/GatewayHelper.sol create mode 100644 l1-contracts/deploy-scripts/upgrade/ChainUpgrade.s.sol create mode 100644 l1-contracts/deploy-scripts/upgrade/EcosystemUpgrade.s.sol create mode 100644 l1-contracts/test/foundry/l1/integration/UpgradeTest.t.sol create mode 100644 l1-contracts/test/foundry/l1/integration/upgrade-envs/script-config/mainnet-era.toml create mode 100644 l1-contracts/test/foundry/l1/integration/upgrade-envs/script-config/mainnet.toml create mode 100644 l1-contracts/test/foundry/l1/integration/upgrade-envs/script-out/.gitkeep delete mode 100644 l1-contracts/test/foundry/l1/unit/concrete/state-transition/chain-deps/facets/Getters/GetBaseTokenBridge.t.sol create mode 100644 system-contracts/contracts/L2GatewayUpgrade.sol create mode 100644 system-contracts/contracts/L2GatewayUpgradeHelper.sol create mode 100644 system-contracts/contracts/interfaces/IL2SharedBridgeLegacy.sol diff --git a/.gitignore b/.gitignore index a2a7a4d05..2128686e0 100644 --- a/.gitignore +++ b/.gitignore @@ -33,4 +33,5 @@ l1-contracts/test/foundry/l1/integration/deploy-scripts/script-out/*.toml 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/test/foundry/l1/integration/upgrade-envs/script-out/*.toml l1-contracts/zkout/* diff --git a/l1-contracts/contracts/bridge/L1Nullifier.sol b/l1-contracts/contracts/bridge/L1Nullifier.sol index 6dda093c9..a4447e085 100644 --- a/l1-contracts/contracts/bridge/L1Nullifier.sol +++ b/l1-contracts/contracts/bridge/L1Nullifier.sol @@ -100,6 +100,12 @@ contract L1Nullifier is IL1Nullifier, ReentrancyGuard, Ownable2StepUpgradeable, /// NOTE: this function may be removed in the future, don't rely on it! mapping(uint256 chainId => mapping(address l1Token => uint256 balance)) public __DEPRECATED_chainBalance; + /// @dev Admin has the ability to register new chains within the shared bridge. + address public __DEPRECATED_admin; + + /// @dev The pending admin, i.e. the candidate to the admin role. + address public __DEPRECATED_pendingAdmin; + /// @dev Address of L1 asset router. IL1AssetRouter public l1AssetRouter; diff --git a/l1-contracts/contracts/bridge/L2SharedBridgeLegacy.sol b/l1-contracts/contracts/bridge/L2SharedBridgeLegacy.sol index 9ea29cf07..df5bda67c 100644 --- a/l1-contracts/contracts/bridge/L2SharedBridgeLegacy.sol +++ b/l1-contracts/contracts/bridge/L2SharedBridgeLegacy.sol @@ -10,17 +10,20 @@ import {BridgedStandardERC20} from "./BridgedStandardERC20.sol"; import {DEPLOYER_SYSTEM_CONTRACT, L2_ASSET_ROUTER_ADDR, L2_NATIVE_TOKEN_VAULT_ADDR} from "../common/L2ContractAddresses.sol"; import {SystemContractsCaller} from "../common/libraries/SystemContractsCaller.sol"; import {L2ContractHelper, IContractDeployer} from "../common/libraries/L2ContractHelper.sol"; +import {AddressAliasHelper} from "../vendor/AddressAliasHelper.sol"; import {IL2AssetRouter} from "./asset-router/IL2AssetRouter.sol"; import {IL2NativeTokenVault} from "./ntv/IL2NativeTokenVault.sol"; import {IL2SharedBridgeLegacy} from "./interfaces/IL2SharedBridgeLegacy.sol"; -import {ZeroAddress, EmptyBytes32, Unauthorized, AmountMustBeGreaterThanZero, DeployFailed} from "../common/L1ContractErrors.sol"; +import {InvalidCaller, ZeroAddress, EmptyBytes32, Unauthorized, AmountMustBeGreaterThanZero, DeployFailed} from "../common/L1ContractErrors.sol"; /// @author Matter Labs /// @custom:security-contact security@matterlabs.dev /// @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. +/// @dev Note, that this contract should be compatible with its previous version as it will be +/// the primary bridge to be used during migration. contract L2SharedBridgeLegacy is IL2SharedBridgeLegacy, Initializable { /// @dev The address of the L1 shared bridge counterpart. address public override l1SharedBridge; @@ -37,6 +40,7 @@ contract L2SharedBridgeLegacy is IL2SharedBridgeLegacy, Initializable { /// @dev The address of the legacy L1 erc20 bridge counterpart. /// This is non-zero only on Era, and should not be renamed for backward compatibility with the SDKs. + // slither-disable-next-line uninitialized-state address public override l1Bridge; modifier onlyNTV() { @@ -103,6 +107,44 @@ contract L2SharedBridgeLegacy is IL2SharedBridgeLegacy, Initializable { IL2AssetRouter(L2_ASSET_ROUTER_ADDR).withdrawLegacyBridge(_l1Receiver, _l2Token, _amount, msg.sender); } + /// @notice Finalize the deposit and mint funds + /// @param _l1Sender The account address that initiated the deposit on L1 + /// @param _l2Receiver The account address that would receive minted ether + /// @param _l1Token The address of the token that was locked on the L1 + /// @param _amount Total amount of tokens deposited from L1 + /// @param _data The additional data that user can pass with the deposit + function finalizeDeposit( + address _l1Sender, + address _l2Receiver, + address _l1Token, + uint256 _amount, + bytes calldata _data + ) external { + // Only the L1 bridge counterpart can initiate and finalize the deposit. + if ( + AddressAliasHelper.undoL1ToL2Alias(msg.sender) != l1Bridge && + AddressAliasHelper.undoL1ToL2Alias(msg.sender) != l1SharedBridge + ) { + revert InvalidCaller(msg.sender); + } + + IL2AssetRouter(L2_ASSET_ROUTER_ADDR).finalizeDepositLegacyBridge({ + _l1Sender: _l1Sender, + _l2Receiver: _l2Receiver, + _l1Token: _l1Token, + _amount: _amount, + _data: _data + }); + + address l2Token = IL2NativeTokenVault(L2_NATIVE_TOKEN_VAULT_ADDR).l2TokenAddress(_l1Token); + + if (l1TokenAddress[l2Token] == address(0)) { + l1TokenAddress[l2Token] = _l1Token; + } + + emit FinalizeDeposit(_l1Sender, _l2Receiver, l2Token, _amount); + } + /// @return Address of an L2 token counterpart function l2TokenAddress(address _l1Token) public view override returns (address) { address token = IL2NativeTokenVault(L2_NATIVE_TOKEN_VAULT_ADDR).l2TokenAddress(_l1Token); diff --git a/l1-contracts/contracts/bridge/asset-router/IL1AssetRouter.sol b/l1-contracts/contracts/bridge/asset-router/IL1AssetRouter.sol index 55b5b9560..8c02ea00e 100644 --- a/l1-contracts/contracts/bridge/asset-router/IL1AssetRouter.sol +++ b/l1-contracts/contracts/bridge/asset-router/IL1AssetRouter.sol @@ -6,11 +6,12 @@ import {IL1Nullifier} from "../interfaces/IL1Nullifier.sol"; import {INativeTokenVault} from "../ntv/INativeTokenVault.sol"; import {IAssetRouterBase} from "./IAssetRouterBase.sol"; import {L2TransactionRequestTwoBridgesInner} from "../../bridgehub/IBridgehub.sol"; +import {IL1SharedBridgeLegacy} from "../interfaces/IL1SharedBridgeLegacy.sol"; /// @title L1 Bridge contract interface /// @author Matter Labs /// @custom:security-contact security@matterlabs.dev -interface IL1AssetRouter is IAssetRouterBase { +interface IL1AssetRouter is IAssetRouterBase, IL1SharedBridgeLegacy { event BridgehubMintData(bytes bridgeMintData); event BridgehubDepositFinalized( diff --git a/l1-contracts/contracts/bridge/asset-router/IL2AssetRouter.sol b/l1-contracts/contracts/bridge/asset-router/IL2AssetRouter.sol index 34ce2ecd1..2613a75eb 100644 --- a/l1-contracts/contracts/bridge/asset-router/IL2AssetRouter.sol +++ b/l1-contracts/contracts/bridge/asset-router/IL2AssetRouter.sol @@ -18,6 +18,14 @@ interface IL2AssetRouter { function withdrawLegacyBridge(address _l1Receiver, address _l2Token, uint256 _amount, address _sender) external; + function finalizeDepositLegacyBridge( + address _l1Sender, + address _l2Receiver, + address _l1Token, + uint256 _amount, + bytes calldata _data + ) external; + /// @dev Used to set the assedAddress for a given assetId. /// @dev Will be used by ZK Gateway function setAssetHandlerAddress(uint256 _originChainId, bytes32 _assetId, address _assetAddress) external; diff --git a/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol b/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol index 6e44f4cae..16b14f2ea 100644 --- a/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol +++ b/l1-contracts/contracts/bridge/asset-router/L1AssetRouter.sol @@ -585,4 +585,11 @@ contract L1AssetRouter is AssetRouterBase, IL1AssetRouter, ReentrancyGuard { _merkleProof: _merkleProof }); } + + /// @notice Legacy function to get the L2 shared bridge address for a chain. + /// @dev In case the chain has been deployed after the gateway release, + /// the returned value is 0. + function l2BridgeAddress(uint256 _chainId) external view override returns (address) { + return L1_NULLIFIER.l2BridgeAddress(_chainId); + } } diff --git a/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol b/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol index b75eb58e8..b22b3f5b9 100644 --- a/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol +++ b/l1-contracts/contracts/bridge/asset-router/L2AssetRouter.sol @@ -215,6 +215,38 @@ contract L2AssetRouter is AssetRouterBase, IL2AssetRouter { uint256 _amount, bytes calldata _data ) external onlyAssetRouterCounterpart(L1_CHAIN_ID) { + _translateLegacyFinalizeDeposit({ + _l1Sender: _l1Sender, + _l2Receiver: _l2Receiver, + _l1Token: _l1Token, + _amount: _amount, + _data: _data + }); + } + + function finalizeDepositLegacyBridge( + address _l1Sender, + address _l2Receiver, + address _l1Token, + uint256 _amount, + bytes calldata _data + ) external onlyLegacyBridge { + _translateLegacyFinalizeDeposit({ + _l1Sender: _l1Sender, + _l2Receiver: _l2Receiver, + _l1Token: _l1Token, + _amount: _amount, + _data: _data + }); + } + + function _translateLegacyFinalizeDeposit( + address _l1Sender, + address _l2Receiver, + address _l1Token, + uint256 _amount, + bytes calldata _data + ) internal { bytes32 assetId = DataEncoding.encodeNTVAssetId(L1_CHAIN_ID, _l1Token); // solhint-disable-next-line func-named-parameters bytes memory data = DataEncoding.encodeBridgeMintData(_l1Sender, _l2Receiver, _l1Token, _amount, _data); diff --git a/l1-contracts/contracts/bridge/interfaces/IL1SharedBridgeLegacy.sol b/l1-contracts/contracts/bridge/interfaces/IL1SharedBridgeLegacy.sol index 627048f75..43fca83a3 100644 --- a/l1-contracts/contracts/bridge/interfaces/IL1SharedBridgeLegacy.sol +++ b/l1-contracts/contracts/bridge/interfaces/IL1SharedBridgeLegacy.sol @@ -7,13 +7,4 @@ pragma solidity 0.8.24; /// @custom:security-contact security@matterlabs.dev interface IL1SharedBridgeLegacy { function l2BridgeAddress(uint256 _chainId) external view returns (address); - - event LegacyDepositInitiated( - uint256 indexed chainId, - bytes32 indexed l2DepositTxHash, - address indexed from, - address to, - address l1Asset, - uint256 amount - ); } diff --git a/l1-contracts/contracts/bridge/interfaces/IL2SharedBridgeLegacy.sol b/l1-contracts/contracts/bridge/interfaces/IL2SharedBridgeLegacy.sol index 00a762447..05d86757e 100644 --- a/l1-contracts/contracts/bridge/interfaces/IL2SharedBridgeLegacy.sol +++ b/l1-contracts/contracts/bridge/interfaces/IL2SharedBridgeLegacy.sol @@ -2,9 +2,20 @@ pragma solidity ^0.8.20; +import {UpgradeableBeacon} from "@openzeppelin/contracts-v4/proxy/beacon/UpgradeableBeacon.sol"; + /// @author Matter Labs /// @custom:security-contact security@matterlabs.dev interface IL2SharedBridgeLegacy { + event FinalizeDeposit( + address indexed l1Sender, + address indexed l2Receiver, + address indexed l2Token, + uint256 amount + ); + + function l2TokenBeacon() external returns (UpgradeableBeacon); + function withdraw(address _l1Receiver, address _l2Token, uint256 _amount) external; function l1TokenAddress(address _l2Token) external view returns (address); diff --git a/l1-contracts/contracts/bridgehub/Bridgehub.sol b/l1-contracts/contracts/bridgehub/Bridgehub.sol index 8e23a9f3d..4df126f9b 100644 --- a/l1-contracts/contracts/bridgehub/Bridgehub.sol +++ b/l1-contracts/contracts/bridgehub/Bridgehub.sol @@ -154,14 +154,24 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus // We will change this with interop. ETH_TOKEN_ASSET_ID = DataEncoding.encodeNTVAssetId(L1_CHAIN_ID, ETH_TOKEN_ADDRESS); _transferOwnership(_owner); - whitelistedSettlementLayers[_l1ChainId] = true; + _initializeInner(); } /// @notice used to initialize the contract /// @notice this contract is also deployed on L2 as a system contract there the owner and the related functions will not be used /// @param _owner the owner of the contract - function initialize(address _owner) external reentrancyGuardInitializer { + function initialize(address _owner) external reentrancyGuardInitializer onlyL1 { _transferOwnership(_owner); + _initializeInner(); + } + + /// @notice Used to initialize the contract on L1 + function initializeV2() external initializer onlyL1 { + _initializeInner(); + } + + /// @notice Initializes the contract + function _initializeInner() internal { assetIdIsRegistered[ETH_TOKEN_ASSET_ID] = true; whitelistedSettlementLayers[L1_CHAIN_ID] = true; } @@ -356,7 +366,6 @@ contract Bridgehub is IBridgehub, ReentrancyGuard, Ownable2StepUpgradeable, Paus address chainAddress = IChainTypeManager(_chainTypeManager).createNewChain({ _chainId: _chainId, _baseTokenAssetId: _baseTokenAssetId, - _assetRouter: assetRouter, _admin: _admin, _initData: _initData, _factoryDeps: _factoryDeps diff --git a/l1-contracts/contracts/dev-contracts/test/DummyBridgehub.sol b/l1-contracts/contracts/dev-contracts/test/DummyBridgehub.sol index 5038f5f66..f178fc0ed 100644 --- a/l1-contracts/contracts/dev-contracts/test/DummyBridgehub.sol +++ b/l1-contracts/contracts/dev-contracts/test/DummyBridgehub.sol @@ -15,6 +15,8 @@ contract DummyBridgehub { address public zkChain; + address public sharedBridge; + // add this to be excluded from coverage report function test() internal virtual {} @@ -41,4 +43,8 @@ contract DummyBridgehub { function getZKChain(uint256) external view returns (address) { return address(0); } + + function setSharedBridge(address addr) external { + sharedBridge = addr; + } } diff --git a/l1-contracts/contracts/governance/L2ProxyAdminDeployer.sol b/l1-contracts/contracts/governance/L2ProxyAdminDeployer.sol new file mode 100644 index 000000000..144f951bf --- /dev/null +++ b/l1-contracts/contracts/governance/L2ProxyAdminDeployer.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.24; + +// solhint-disable gas-length-in-loops + +import {ProxyAdmin} from "@openzeppelin/contracts-v4/proxy/transparent/ProxyAdmin.sol"; + +/// @author Matter Labs +/// @custom:security-contact security@matterlabs.dev +/// @notice The contract that deterministically deploys a ProxyAdmin, while +/// ensuring that its owner is the aliased governance contract +contract L2ProxyAdminDeployer { + address public immutable PROXY_ADMIN_ADDRESS; + + constructor(address _aliasedGovernance) { + ProxyAdmin admin = new ProxyAdmin{salt: bytes32(0)}(); + admin.transferOwnership(_aliasedGovernance); + + PROXY_ADMIN_ADDRESS = address(admin); + } +} diff --git a/l1-contracts/contracts/governance/TransitionaryOwner.sol b/l1-contracts/contracts/governance/TransitionaryOwner.sol new file mode 100644 index 000000000..9248204bf --- /dev/null +++ b/l1-contracts/contracts/governance/TransitionaryOwner.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.24; + +// solhint-disable gas-length-in-loops + +import {Ownable2Step} from "@openzeppelin/contracts-v4/access/Ownable2Step.sol"; + +/// @author Matter Labs +/// @custom:security-contact security@matterlabs.dev +/// @notice The contract that is used a temporary owner for Ownable2Step contracts until the +/// governance can accept the ownership +contract TransitionaryOwner { + address public immutable GOVERNANCE_ADDRESS; + + constructor(address _governanceAddress) { + GOVERNANCE_ADDRESS = _governanceAddress; + } + + /// @notice Claims that ownership of a contract and transfers it to the governance + function claimOwnershipAndGiveToGovernance(address target) external { + Ownable2Step(target).acceptOwnership(); + Ownable2Step(target).transferOwnership(GOVERNANCE_ADDRESS); + } +} diff --git a/l1-contracts/contracts/state-transition/ChainTypeManager.sol b/l1-contracts/contracts/state-transition/ChainTypeManager.sol index 62df92419..2760e36c3 100644 --- a/l1-contracts/contracts/state-transition/ChainTypeManager.sol +++ b/l1-contracts/contracts/state-transition/ChainTypeManager.sol @@ -344,13 +344,11 @@ contract ChainTypeManager is IChainTypeManager, ReentrancyGuard, Ownable2StepUpg /// @notice deploys a full set of chains contracts /// @param _chainId the chain's id /// @param _baseTokenAssetId the base token asset id used to pay for gas fees - /// @param _sharedBridge the shared bridge address, used as base token bridge /// @param _admin the chain's admin address /// @param _diamondCut the diamond cut data that initializes the chains Diamond Proxy function _deployNewChain( uint256 _chainId, bytes32 _baseTokenAssetId, - address _sharedBridge, address _admin, bytes memory _diamondCut ) internal returns (address zkChainAddress) { @@ -383,7 +381,6 @@ contract ChainTypeManager is IChainTypeManager, ReentrancyGuard, Ownable2StepUpg bytes32(uint256(uint160(_admin))), bytes32(uint256(uint160(validatorTimelock))), _baseTokenAssetId, - bytes32(uint256(uint160(_sharedBridge))), storedBatchZero, diamondCut.initCalldata ); @@ -400,7 +397,6 @@ contract ChainTypeManager is IChainTypeManager, ReentrancyGuard, Ownable2StepUpg /// @notice called by Bridgehub when a chain registers /// @param _chainId the chain's id /// @param _baseTokenAssetId the base token asset id used to pay for gas fees - /// @param _assetRouter the shared bridge address, used as base token bridge /// @param _admin the chain's admin address /// @param _initData the diamond cut data, force deployments and factoryDeps encoded /// @param _factoryDeps the factory dependencies used for the genesis upgrade @@ -408,7 +404,6 @@ contract ChainTypeManager is IChainTypeManager, ReentrancyGuard, Ownable2StepUpg function createNewChain( uint256 _chainId, bytes32 _baseTokenAssetId, - address _assetRouter, address _admin, bytes calldata _initData, bytes[] calldata _factoryDeps @@ -416,7 +411,7 @@ contract ChainTypeManager is IChainTypeManager, ReentrancyGuard, Ownable2StepUpg (bytes memory _diamondCut, bytes memory _forceDeploymentData) = abi.decode(_initData, (bytes, bytes)); // solhint-disable-next-line func-named-parameters - zkChainAddress = _deployNewChain(_chainId, _baseTokenAssetId, _assetRouter, _admin, _diamondCut); + zkChainAddress = _deployNewChain(_chainId, _baseTokenAssetId, _admin, _diamondCut); { // check input @@ -492,7 +487,6 @@ contract ChainTypeManager is IChainTypeManager, ReentrancyGuard, Ownable2StepUpg chainAddress = _deployNewChain({ _chainId: _chainId, _baseTokenAssetId: _baseTokenAssetId, - _sharedBridge: address(IBridgehub(BRIDGE_HUB).sharedBridge()), _admin: _admin, _diamondCut: _diamondCut }); diff --git a/l1-contracts/contracts/state-transition/IChainTypeManager.sol b/l1-contracts/contracts/state-transition/IChainTypeManager.sol index b5202e975..90b500b28 100644 --- a/l1-contracts/contracts/state-transition/IChainTypeManager.sol +++ b/l1-contracts/contracts/state-transition/IChainTypeManager.sol @@ -115,7 +115,6 @@ interface IChainTypeManager { function createNewChain( uint256 _chainId, bytes32 _baseTokenAssetId, - address _assetRouter, address _admin, bytes calldata _initData, bytes[] calldata _factoryDeps diff --git a/l1-contracts/contracts/state-transition/chain-deps/DiamondInit.sol b/l1-contracts/contracts/state-transition/chain-deps/DiamondInit.sol index 3be7dc2b1..69a646308 100644 --- a/l1-contracts/contracts/state-transition/chain-deps/DiamondInit.sol +++ b/l1-contracts/contracts/state-transition/chain-deps/DiamondInit.sol @@ -45,9 +45,6 @@ contract DiamondInit is ZKChainBase, IDiamondInit { if (_initializeData.baseTokenAssetId == bytes32(0)) { revert ZeroAddress(); } - if (_initializeData.baseTokenBridge == address(0)) { - revert ZeroAddress(); - } if (_initializeData.blobVersionedHashRetriever == address(0)) { revert ZeroAddress(); } @@ -56,7 +53,6 @@ contract DiamondInit is ZKChainBase, IDiamondInit { s.bridgehub = _initializeData.bridgehub; s.chainTypeManager = _initializeData.chainTypeManager; s.baseTokenAssetId = _initializeData.baseTokenAssetId; - s.baseTokenBridge = _initializeData.baseTokenBridge; s.protocolVersion = _initializeData.protocolVersion; s.verifier = _initializeData.verifier; diff --git a/l1-contracts/contracts/state-transition/chain-deps/ZKChainStorage.sol b/l1-contracts/contracts/state-transition/chain-deps/ZKChainStorage.sol index 3205a229e..5f19aecd4 100644 --- a/l1-contracts/contracts/state-transition/chain-deps/ZKChainStorage.sol +++ b/l1-contracts/contracts/state-transition/chain-deps/ZKChainStorage.sol @@ -149,7 +149,7 @@ struct ZKChainStorage { /// @dev The address of the baseToken contract. Eth is address(1) address __DEPRECATED_baseToken; /// @dev The address of the baseTokenbridge. Eth also uses the shared bridge - address baseTokenBridge; + address __DEPRECATED_baseTokenBridge; /// @notice gasPriceMultiplier for each baseToken, so that each L1->L2 transaction pays for its transaction on the destination /// we multiply by the nominator, and divide by the denominator uint128 baseTokenGasPriceMultiplierNominator; diff --git a/l1-contracts/contracts/state-transition/chain-deps/facets/Getters.sol b/l1-contracts/contracts/state-transition/chain-deps/facets/Getters.sol index 1ffdb5b0c..b5eda6d19 100644 --- a/l1-contracts/contracts/state-transition/chain-deps/facets/Getters.sol +++ b/l1-contracts/contracts/state-transition/chain-deps/facets/Getters.sol @@ -75,11 +75,6 @@ contract GettersFacet is ZKChainBase, IGetters, ILegacyGetters { return s.baseTokenAssetId; } - /// @inheritdoc IGetters - function getBaseTokenBridge() external view returns (address) { - return s.baseTokenBridge; - } - /// @inheritdoc IGetters function baseTokenGasPriceMultiplierNominator() external view returns (uint128) { return s.baseTokenGasPriceMultiplierNominator; diff --git a/l1-contracts/contracts/state-transition/chain-deps/facets/Mailbox.sol b/l1-contracts/contracts/state-transition/chain-deps/facets/Mailbox.sol index 48b6dd76d..d230a04b9 100644 --- a/l1-contracts/contracts/state-transition/chain-deps/facets/Mailbox.sol +++ b/l1-contracts/contracts/state-transition/chain-deps/facets/Mailbox.sol @@ -610,7 +610,8 @@ contract MailboxFacet is ZKChainBase, IMailbox { if (s.chainId != ERA_CHAIN_ID) { revert OnlyEraSupported(); } - IL1AssetRouter(s.baseTokenBridge).finalizeWithdrawal({ + address sharedBridge = IBridgehub(s.bridgehub).sharedBridge(); + IL1AssetRouter(sharedBridge).finalizeWithdrawal({ _chainId: ERA_CHAIN_ID, _l2BatchNumber: _l2BatchNumber, _l2MessageIndex: _l2MessageIndex, @@ -646,7 +647,8 @@ contract MailboxFacet is ZKChainBase, IMailbox { refundRecipient: _refundRecipient }) ); - IL1AssetRouter(s.baseTokenBridge).bridgehubDepositBaseToken{value: msg.value}( + address sharedBridge = IBridgehub(s.bridgehub).sharedBridge(); + IL1AssetRouter(sharedBridge).bridgehubDepositBaseToken{value: msg.value}( s.chainId, s.baseTokenAssetId, msg.sender, diff --git a/l1-contracts/contracts/state-transition/chain-deps/facets/ZKChainBase.sol b/l1-contracts/contracts/state-transition/chain-deps/facets/ZKChainBase.sol index 45c360197..6c8a08657 100644 --- a/l1-contracts/contracts/state-transition/chain-deps/facets/ZKChainBase.sol +++ b/l1-contracts/contracts/state-transition/chain-deps/facets/ZKChainBase.sol @@ -62,13 +62,6 @@ contract ZKChainBase is ReentrancyGuard { _; } - modifier onlyBaseTokenBridge() { - if (msg.sender != s.baseTokenBridge) { - revert Unauthorized(msg.sender); - } - _; - } - function _getTotalPriorityTxs() internal view returns (uint256) { if (s.priorityQueue.getFirstUnprocessedPriorityTx() >= s.priorityTree.startIndex) { return s.priorityTree.getTotalPriorityTxs(); diff --git a/l1-contracts/contracts/state-transition/chain-interfaces/IDiamondInit.sol b/l1-contracts/contracts/state-transition/chain-interfaces/IDiamondInit.sol index c5f2bbc90..e175ac91f 100644 --- a/l1-contracts/contracts/state-transition/chain-interfaces/IDiamondInit.sol +++ b/l1-contracts/contracts/state-transition/chain-interfaces/IDiamondInit.sol @@ -12,7 +12,6 @@ import {FeeParams} from "../chain-deps/ZKChainStorage.sol"; /// @param validatorTimelock address of the validator timelock that delays execution /// @param admin address who can manage the contract /// @param baseTokenAssetId asset id of the base token of the chain -/// @param baseTokenBridge address of the L1 shared bridge contract /// @param storedBatchZero hash of the initial genesis batch /// @param verifier address of Verifier contract /// @param verifierParams Verifier config parameters that describes the circuit to be verified @@ -30,7 +29,6 @@ struct InitializeData { address admin; address validatorTimelock; bytes32 baseTokenAssetId; - address baseTokenBridge; bytes32 storedBatchZero; IVerifier verifier; VerifierParams verifierParams; diff --git a/l1-contracts/contracts/state-transition/chain-interfaces/IGetters.sol b/l1-contracts/contracts/state-transition/chain-interfaces/IGetters.sol index 5dfd600ca..d2ee2b3d0 100644 --- a/l1-contracts/contracts/state-transition/chain-interfaces/IGetters.sol +++ b/l1-contracts/contracts/state-transition/chain-interfaces/IGetters.sol @@ -41,9 +41,6 @@ interface IGetters is IZKChainBase { /// @return The address of the base token function getBaseTokenAssetId() external view returns (bytes32); - /// @return The address of the base token bridge - function getBaseTokenBridge() external view returns (address); - /// @return The total number of batches that were committed function getTotalBatchesCommitted() external view returns (uint256); diff --git a/l1-contracts/contracts/state-transition/l2-deps/IL2GatewayUpgrade.sol b/l1-contracts/contracts/state-transition/l2-deps/IL2GatewayUpgrade.sol new file mode 100644 index 000000000..fdafe2807 --- /dev/null +++ b/l1-contracts/contracts/state-transition/l2-deps/IL2GatewayUpgrade.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.24; + +import {IL2ContractDeployer} from "../../common/interfaces/IL2ContractDeployer.sol"; + +interface IL2GatewayUpgrade { + function upgrade( + IL2ContractDeployer.ForceDeployment[] calldata _forceDeployments, + address _ctmDeployer, + bytes calldata _fixedForceDeploymentsData, + bytes calldata _additionalForceDeploymentsData + ) external payable; +} diff --git a/l1-contracts/contracts/upgrades/GatewayHelper.sol b/l1-contracts/contracts/upgrades/GatewayHelper.sol new file mode 100644 index 000000000..5ae02b9a0 --- /dev/null +++ b/l1-contracts/contracts/upgrades/GatewayHelper.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.24; + +import {IL1SharedBridgeLegacy} from "../bridge/interfaces/IL1SharedBridgeLegacy.sol"; +import {IBridgehub} from "../bridgehub/IBridgehub.sol"; + +import {ZKChainSpecificForceDeploymentsData} from "../state-transition/l2-deps/IL2GenesisUpgrade.sol"; + +import {ZKChainStorage} from "../state-transition/chain-deps/ZKChainStorage.sol"; + +library GatewayHelper { + function getZKChainSpecificForceDeploymentsData(ZKChainStorage storage s) internal view returns (bytes memory) { + address sharedBridge = IBridgehub(s.bridgehub).sharedBridge(); + address legacySharedBridge = IL1SharedBridgeLegacy(sharedBridge).l2BridgeAddress(s.chainId); + ZKChainSpecificForceDeploymentsData + memory additionalForceDeploymentsData = ZKChainSpecificForceDeploymentsData({ + baseTokenAssetId: s.baseTokenAssetId, + l2LegacySharedBridge: legacySharedBridge, + l2Weth: address(0) // kl todo + }); + return abi.encode(additionalForceDeploymentsData); + } +} diff --git a/l1-contracts/contracts/upgrades/GatewayUpgrade.sol b/l1-contracts/contracts/upgrades/GatewayUpgrade.sol index 08d05989e..3420d81ae 100644 --- a/l1-contracts/contracts/upgrades/GatewayUpgrade.sol +++ b/l1-contracts/contracts/upgrades/GatewayUpgrade.sol @@ -2,8 +2,6 @@ pragma solidity 0.8.24; -import {Initializable} from "@openzeppelin/contracts-upgradeable-v4/proxy/utils/Initializable.sol"; - import {BaseZkSyncUpgrade, ProposedUpgrade} from "./BaseZkSyncUpgrade.sol"; import {DataEncoding} from "../common/libraries/DataEncoding.sol"; @@ -13,14 +11,27 @@ import {PriorityQueue} from "../state-transition/libraries/PriorityQueue.sol"; import {PriorityTree} from "../state-transition/libraries/PriorityTree.sol"; import {IGatewayUpgrade} from "./IGatewayUpgrade.sol"; -import {IL1SharedBridgeLegacy} from "../bridge/interfaces/IL1SharedBridgeLegacy.sol"; +import {IComplexUpgrader} from "../state-transition/l2-deps/IComplexUpgrader.sol"; +import {IL2GatewayUpgrade} from "../state-transition/l2-deps/IL2GatewayUpgrade.sol"; + +import {IL2ContractDeployer} from "../common/interfaces/IL2ContractDeployer.sol"; -import {IBridgehub} from "../bridgehub/IBridgehub.sol"; +import {GatewayHelper} from "./GatewayHelper.sol"; + +// solhint-disable-next-line gas-struct-packing +struct GatewayUpgradeEncodedInput { + IL2ContractDeployer.ForceDeployment[] baseForceDeployments; + bytes fixedForceDeploymentsData; + address ctmDeployer; + address l2GatewayUpgrade; + address oldValidatorTimelock; + address newValidatorTimelock; +} /// @author Matter Labs /// @custom:security-contact security@matterlabs.dev /// @notice This upgrade will be used to migrate Era to be part of the ZK chain ecosystem contracts. -contract GatewayUpgrade is BaseZkSyncUpgrade, Initializable { +contract GatewayUpgrade is BaseZkSyncUpgrade { using PriorityQueue for PriorityQueue.Queue; using PriorityTree for PriorityTree.Tree; @@ -33,23 +44,34 @@ contract GatewayUpgrade is BaseZkSyncUpgrade, Initializable { /// @notice The main function that will be called by the upgrade proxy. /// @param _proposedUpgrade The upgrade to be executed. function upgrade(ProposedUpgrade calldata _proposedUpgrade) public override returns (bytes32) { - (bytes memory l2TxDataStart, bytes memory l2TxDataFinish) = abi.decode( + GatewayUpgradeEncodedInput memory encodedInput = abi.decode( _proposedUpgrade.postUpgradeCalldata, - (bytes, bytes) + (GatewayUpgradeEncodedInput) ); - s.baseTokenAssetId = DataEncoding.encodeNTVAssetId(block.chainid, s.__DEPRECATED_baseToken); + bytes32 baseTokenAssetId = DataEncoding.encodeNTVAssetId(block.chainid, s.__DEPRECATED_baseToken); + + s.baseTokenAssetId = baseTokenAssetId; s.priorityTree.setup(s.priorityQueue.getTotalPriorityTxs()); - IBridgehub bridgehub = IBridgehub(s.bridgehub); - s.baseTokenBridge = bridgehub.sharedBridge(); // we change the assetRouter - bridgehub.setLegacyBaseTokenAssetId(s.chainId); + s.validators[encodedInput.oldValidatorTimelock] = false; + s.validators[encodedInput.newValidatorTimelock] = true; ProposedUpgrade memory proposedUpgrade = _proposedUpgrade; - address l2LegacyBridge = IL1SharedBridgeLegacy(s.baseTokenBridge).l2BridgeAddress(s.chainId); - proposedUpgrade.l2ProtocolUpgradeTx.data = bytes.concat( - l2TxDataStart, - bytes32(uint256(uint160(l2LegacyBridge))), - l2TxDataFinish + + bytes memory gatewayUpgradeCalldata = abi.encodeCall( + IL2GatewayUpgrade.upgrade, + ( + encodedInput.baseForceDeployments, + encodedInput.ctmDeployer, + encodedInput.fixedForceDeploymentsData, + GatewayHelper.getZKChainSpecificForceDeploymentsData(s) + ) + ); + + proposedUpgrade.l2ProtocolUpgradeTx.data = abi.encodeCall( + IComplexUpgrader.upgrade, + (encodedInput.l2GatewayUpgrade, gatewayUpgradeCalldata) ); + // slither-disable-next-line controlled-delegatecall (bool success, ) = THIS_ADDRESS.delegatecall( abi.encodeWithSelector(IGatewayUpgrade.upgradeExternal.selector, proposedUpgrade) @@ -61,8 +83,6 @@ contract GatewayUpgrade is BaseZkSyncUpgrade, Initializable { /// @notice The function that will be called from this same contract, we need an external call to be able to modify _proposedUpgrade (memory/calldata). function upgradeExternal(ProposedUpgrade calldata _proposedUpgrade) external { - // solhint-disable-next-line gas-custom-errors - require(msg.sender == address(this), "GatewayUpgrade: upgradeExternal"); super.upgrade(_proposedUpgrade); } } diff --git a/l1-contracts/contracts/upgrades/L1GenesisUpgrade.sol b/l1-contracts/contracts/upgrades/L1GenesisUpgrade.sol index d6cb769c0..a6475f59a 100644 --- a/l1-contracts/contracts/upgrades/L1GenesisUpgrade.sol +++ b/l1-contracts/contracts/upgrades/L1GenesisUpgrade.sol @@ -8,15 +8,18 @@ import {Diamond} from "../state-transition/libraries/Diamond.sol"; import {BaseZkSyncUpgradeGenesis} from "./BaseZkSyncUpgradeGenesis.sol"; import {ProposedUpgrade} from "./IDefaultUpgrade.sol"; import {L2CanonicalTransaction} from "../common/Messaging.sol"; -import {IL2GenesisUpgrade, ZKChainSpecificForceDeploymentsData} from "../state-transition/l2-deps/IL2GenesisUpgrade.sol"; +import {IL2GenesisUpgrade} from "../state-transition/l2-deps/IL2GenesisUpgrade.sol"; import {IL1GenesisUpgrade} from "./IL1GenesisUpgrade.sol"; -import {IL1Nullifier} from "../bridge/interfaces/IL1Nullifier.sol"; -import {IL1AssetRouter} from "../bridge/asset-router/IL1AssetRouter.sol"; import {IComplexUpgrader} from "../state-transition/l2-deps/IComplexUpgrader.sol"; import {L2_FORCE_DEPLOYER_ADDR, L2_COMPLEX_UPGRADER_ADDR, L2_GENESIS_UPGRADE_ADDR} from "../common/L2ContractAddresses.sol"; //, COMPLEX_UPGRADER_ADDR, GENESIS_UPGRADE_ADDR import {REQUIRED_L2_GAS_PRICE_PER_PUBDATA, SYSTEM_UPGRADE_L2_TX_TYPE, PRIORITY_TX_MAX_GAS_LIMIT} from "../common/Config.sol"; import {SemVer} from "../common/libraries/SemVer.sol"; +import {IL1SharedBridgeLegacy} from "../bridge/interfaces/IL1SharedBridgeLegacy.sol"; +import {IBridgehub} from "../bridgehub/IBridgehub.sol"; + +import {ZKChainSpecificForceDeploymentsData} from "../state-transition/l2-deps/IL2GenesisUpgrade.sol"; + import {VerifierParams} from "../state-transition/chain-interfaces/IVerifier.sol"; import {L2ContractHelper} from "../common/libraries/L2ContractHelper.sol"; @@ -106,8 +109,8 @@ contract L1GenesisUpgrade is IL1GenesisUpgrade, BaseZkSyncUpgradeGenesis { } function _getZKChainSpecificForceDeploymentsData() internal view returns (bytes memory) { - IL1Nullifier l1Nullifier = IL1AssetRouter(s.baseTokenBridge).L1_NULLIFIER(); - address legacySharedBridge = l1Nullifier.l2BridgeAddress(s.chainId); + address sharedBridge = IBridgehub(s.bridgehub).sharedBridge(); + address legacySharedBridge = IL1SharedBridgeLegacy(sharedBridge).l2BridgeAddress(s.chainId); ZKChainSpecificForceDeploymentsData memory additionalForceDeploymentsData = ZKChainSpecificForceDeploymentsData({ baseTokenAssetId: s.baseTokenAssetId, diff --git a/l1-contracts/deploy-scripts/DeployL1.s.sol b/l1-contracts/deploy-scripts/DeployL1.s.sol index 1112d917e..26bd82396 100644 --- a/l1-contracts/deploy-scripts/DeployL1.s.sol +++ b/l1-contracts/deploy-scripts/DeployL1.s.sol @@ -67,6 +67,10 @@ struct FixedForceDeploymentsData { bytes32 l2AssetRouterBytecodeHash; bytes32 l2NtvBytecodeHash; bytes32 messageRootBytecodeHash; + address l2SharedBridgeLegacyImpl; + address l2BridgedStandardERC20Impl; + address l2BridgeProxyOwnerAddress; + address l2BridgedStandardERC20ProxyOwnerAddress; } contract DeployL1Script is Script { @@ -1102,7 +1106,12 @@ contract DeployL1Script is Script { l2NtvBytecodeHash: L2ContractHelper.hashL2Bytecode( L2ContractsBytecodesLib.readL2NativeTokenVaultBytecode() ), - messageRootBytecodeHash: L2ContractHelper.hashL2Bytecode(L2ContractsBytecodesLib.readMessageRootBytecode()) + messageRootBytecodeHash: L2ContractHelper.hashL2Bytecode(L2ContractsBytecodesLib.readMessageRootBytecode()), + // For newly created chains it it is expected that the following bridges are not present + l2SharedBridgeLegacyImpl: address(0), + l2BridgedStandardERC20Impl: address(0), + l2BridgeProxyOwnerAddress: address(0), + l2BridgedStandardERC20ProxyOwnerAddress: address(0) }); return abi.encode(data); diff --git a/l1-contracts/deploy-scripts/L2ContractsBytecodesLib.sol b/l1-contracts/deploy-scripts/L2ContractsBytecodesLib.sol index b9481c578..9b672deb2 100644 --- a/l1-contracts/deploy-scripts/L2ContractsBytecodesLib.sol +++ b/l1-contracts/deploy-scripts/L2ContractsBytecodesLib.sol @@ -259,4 +259,47 @@ library L2ContractsBytecodesLib { "/../l1-contracts/artifacts-zk/contracts/bridge/L2SharedBridgeLegacy.sol/L2SharedBridgeLegacy.json" ); } + + /// @notice Reads the bytecode of the L2GatewayUpgrade contract. + /// @return The bytecode of the L2GatewayUpgrade contract. + function readGatewayUpgradeBytecode() internal view returns (bytes memory) { + return + Utils.readHardhatBytecode( + "/../system-contracts/artifacts-zk/contracts-preprocessed/L2GatewayUpgrade.sol/L2GatewayUpgrade.json" + ); + } + + /// @notice Reads the bytecode of the L2GatewayUpgrade contract. + /// @return The bytecode of the L2GatewayUpgrade contract. + function readL2AdminFactoryBytecode() internal view returns (bytes memory) { + return + Utils.readHardhatBytecode( + "/../l1-contracts/artifacts-zk/contracts/governance/L2AdminFactory.sol/L2AdminFactory.json" + ); + } + + function readProxyAdminBytecode() internal view returns (bytes memory) { + return + Utils.readHardhatBytecode( + "/../l1-contracts/artifacts-zk/@openzeppelin/contracts-v4/proxy/transparent/ProxyAdmin.sol/ProxyAdmin.json" + ); + } + + /// @notice Reads the bytecode of the L2GatewayUpgrade contract. + /// @return The bytecode of the L2GatewayUpgrade contract. + function readPermanentRestrictionBytecode() internal view returns (bytes memory) { + return + Utils.readHardhatBytecode( + "/../l1-contracts/artifacts-zk/contracts/governance/PermanentRestriction.sol/PermanentRestriction.json" + ); + } + + /// @notice Reads the bytecode of the L2ProxyAdminDeployer contract. + /// @return The bytecode of the L2ProxyAdminDeployer contract. + function readProxyAdminDeployerBytecode() internal view returns (bytes memory) { + return + Utils.readHardhatBytecode( + "/../l1-contracts/artifacts-zk/contracts/governance/L2ProxyAdminDeployer.sol/L2ProxyAdminDeployer.json" + ); + } } diff --git a/l1-contracts/deploy-scripts/Utils.sol b/l1-contracts/deploy-scripts/Utils.sol index b2d71bab7..9b8a9e7da 100644 --- a/l1-contracts/deploy-scripts/Utils.sol +++ b/l1-contracts/deploy-scripts/Utils.sol @@ -17,7 +17,6 @@ import {L2_DEPLOYER_SYSTEM_CONTRACT_ADDR} from "contracts/common/L2ContractAddre 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 @@ -28,6 +27,8 @@ 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); +address constant L2_CREATE2_FACTORY_ADDRESS = address(USER_CONTRACTS_OFFSET); + // solhint-disable-next-line gas-struct-packing struct StateTransitionDeployedAddresses { address chainTypeManagerProxy; @@ -244,14 +245,7 @@ library Utils { address bridgehubAddress, address l1SharedBridgeProxy ) internal returns (address) { - bytes32 bytecodeHash = L2ContractHelper.hashL2Bytecode(bytecode); - - bytes memory deployData = abi.encodeWithSignature( - "create2(bytes32,bytes32,bytes)", - create2salt, - bytecodeHash, - constructorargs - ); + (bytes32 bytecodeHash, bytes memory deployData) = getDeploymentCalldata(create2salt, bytecode, constructorargs); address contractAddress = L2ContractHelper.computeCreate2Address( msg.sender, @@ -260,21 +254,80 @@ library Utils { keccak256(constructorargs) ); - uint256 factoryDepsLength = factoryDeps.length; + bytes[] memory _factoryDeps = appendArray(factoryDeps, bytecode); - bytes[] memory _factoryDeps = new bytes[](factoryDepsLength + 1); + runL1L2Transaction({ + l2Calldata: deployData, + l2GasLimit: l2GasLimit, + l2Value: 0, + factoryDeps: _factoryDeps, + dstAddress: L2_DEPLOYER_SYSTEM_CONTRACT_ADDR, + chainId: chainId, + bridgehubAddress: bridgehubAddress, + l1SharedBridgeProxy: l1SharedBridgeProxy + }); + return contractAddress; + } + + function getL2AddressViaCreate2Factory( + bytes32 create2Salt, + bytes32 bytecodeHash, + bytes memory constructorArgs + ) internal view returns (address) { + return + L2ContractHelper.computeCreate2Address( + L2_CREATE2_FACTORY_ADDRESS, + create2Salt, + bytecodeHash, + keccak256(constructorArgs) + ); + } - for (uint256 i = 0; i < factoryDepsLength; ++i) { - _factoryDeps[i] = factoryDeps[i]; + function getDeploymentCalldata( + bytes32 create2Salt, + bytes memory bytecode, + bytes memory constructorArgs + ) internal view returns (bytes32 bytecodeHash, bytes memory data) { + bytecodeHash = L2ContractHelper.hashL2Bytecode(bytecode); + + data = abi.encodeWithSignature("create2(bytes32,bytes32,bytes)", create2Salt, bytecodeHash, constructorArgs); + } + + function appendArray(bytes[] memory array, bytes memory element) internal pure returns (bytes[] memory) { + uint256 arrayLength = array.length; + bytes[] memory newArray = new bytes[](arrayLength + 1); + for (uint256 i = 0; i < arrayLength; ++i) { + newArray[i] = array[i]; } - _factoryDeps[factoryDepsLength] = bytecode; + newArray[arrayLength] = element; + return newArray; + } + + /** + * @dev Deploy l2 contracts through l1, while using built-in L2 Create2Factory contract. + */ + function deployThroughL1Deterministic( + bytes memory bytecode, + bytes memory constructorargs, + bytes32 create2salt, + uint256 l2GasLimit, + bytes[] memory factoryDeps, + uint256 chainId, + address bridgehubAddress, + address l1SharedBridgeProxy + ) internal returns (address) { + (bytes32 bytecodeHash, bytes memory deployData) = getDeploymentCalldata(create2salt, bytecode, constructorargs); + + address contractAddress = getL2AddressViaCreate2Factory(create2salt, bytecodeHash, constructorargs); + + bytes[] memory _factoryDeps = appendArray(factoryDeps, bytecode); runL1L2Transaction({ l2Calldata: deployData, l2GasLimit: l2GasLimit, l2Value: 0, factoryDeps: _factoryDeps, - dstAddress: L2_DEPLOYER_SYSTEM_CONTRACT_ADDR, + dstAddress: L2_CREATE2_FACTORY_ADDRESS, chainId: chainId, bridgehubAddress: bridgehubAddress, l1SharedBridgeProxy: l1SharedBridgeProxy diff --git a/l1-contracts/deploy-scripts/upgrade/ChainUpgrade.s.sol b/l1-contracts/deploy-scripts/upgrade/ChainUpgrade.s.sol new file mode 100644 index 000000000..7df6863d3 --- /dev/null +++ b/l1-contracts/deploy-scripts/upgrade/ChainUpgrade.s.sol @@ -0,0 +1,212 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.24; + +// 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 {Utils, L2_BRIDGEHUB_ADDRESS, L2_ASSET_ROUTER_ADDRESS, L2_NATIVE_TOKEN_VAULT_ADDRESS, L2_MESSAGE_ROOT_ADDRESS} from "../Utils.sol"; +import {L2ContractHelper} from "contracts/common/libraries/L2ContractHelper.sol"; +import {L2ContractsBytecodesLib} from "../L2ContractsBytecodesLib.sol"; +import {IZKChain} from "contracts/state-transition/chain-interfaces/IZKChain.sol"; +import {IAdmin} from "contracts/state-transition/chain-interfaces/IAdmin.sol"; +import {AccessControlRestriction} from "contracts/governance/AccessControlRestriction.sol"; +import {ChainAdmin} from "contracts/governance/ChainAdmin.sol"; +import {Call} from "contracts/governance/Common.sol"; +import {Diamond} from "contracts/state-transition/libraries/Diamond.sol"; + +interface LegacyChainAdmin { + function owner() external view returns (address); +} + +contract ChainUpgrade is Script { + using stdToml for string; + + struct ChainConfig { + address deployerAddress; + address ownerAddress; + uint256 chainChainId; + address chainDiamondProxyAddress; + bool validiumMode; + bool permanentRollup; + // FIXME: From ecosystem, maybe move to a different struct + address expectedRollupL2DAValidator; + address expectedL2GatewayUpgrade; + address expectedValidiumL2DAValidator; + address permanentRollupRestriction; + address bridgehubProxyAddress; + address oldSharedBridgeProxyAddress; + } + + struct Output { + address l2DAValidator; + address accessControlRestriction; + address chainAdmin; + } + + address currentChainAdmin; + ChainConfig config; + Output output; + + function prepareChain( + string memory ecosystemInputPath, + string memory ecosystemOutputPath, + string memory configPath, + string memory outputPath + ) public { + string memory root = vm.projectRoot(); + ecosystemInputPath = string.concat(root, ecosystemInputPath); + ecosystemOutputPath = string.concat(root, ecosystemOutputPath); + configPath = string.concat(root, configPath); + outputPath = string.concat(root, outputPath); + + initializeConfig(configPath, ecosystemInputPath, ecosystemOutputPath); + + checkCorrectOwnerAddress(); + // Preparation of chain consists of two parts: + // - Deploying l2 da validator + // - Deploying new chain admin + + deployNewL2DAValidator(); + deployL2GatewayUpgrade(); + deployNewChainAdmin(); + governanceMoveToNewChainAdmin(); + + saveOutput(outputPath); + } + + function upgradeChain(uint256 oldProtocolVersion, Diamond.DiamondCutData memory upgradeCutData) public { + Utils.adminExecute( + output.chainAdmin, + output.accessControlRestriction, + config.chainDiamondProxyAddress, + abi.encodeCall(IAdmin.upgradeChainFromVersion, (oldProtocolVersion, upgradeCutData)), + 0 + ); + } + + function initializeConfig( + string memory configPath, + string memory ecosystemInputPath, + string memory ecosystemOutputPath + ) internal { + config.deployerAddress = msg.sender; + + // Grab config from output of l1 deployment + string memory toml = vm.readFile(configPath); + + // 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.ownerAddress = toml.readAddress("$.owner_address"); + config.chainChainId = toml.readUint("$.chain.chain_id"); + config.validiumMode = toml.readBool("$.chain.validium_mode"); + config.chainDiamondProxyAddress = toml.readAddress("$.chain.diamond_proxy_address"); + config.permanentRollup = toml.readBool("$.chain.permanent_rollup"); + + toml = vm.readFile(ecosystemOutputPath); + + config.expectedRollupL2DAValidator = toml.readAddress("$.contracts_config.expected_rollup_l2_da_validator"); + config.expectedValidiumL2DAValidator = toml.readAddress("$.contracts_config.expected_validium_l2_da_validator"); + config.expectedL2GatewayUpgrade = toml.readAddress("$.contracts_config.expected_l2_gateway_upgrade"); + config.permanentRollupRestriction = toml.readAddress("$.deployed_addresses.permanent_rollup_restriction"); + + toml = vm.readFile(ecosystemInputPath); + + config.bridgehubProxyAddress = toml.readAddress("$.contracts.bridgehub_proxy_address"); + config.oldSharedBridgeProxyAddress = toml.readAddress("$.contracts.old_shared_bridge_proxy_address"); + } + + function checkCorrectOwnerAddress() internal { + currentChainAdmin = address(IZKChain(config.chainDiamondProxyAddress).getAdmin()); + address currentAdminOwner = LegacyChainAdmin(currentChainAdmin).owner(); + + require(currentAdminOwner == config.ownerAddress, "Only the owner of the chain admin can call this function"); + } + + function deployNewL2DAValidator() internal { + address expectedL2DAValidator = Utils.deployThroughL1Deterministic({ + // FIXME: for now this script only works with rollup chains + bytecode: L2ContractsBytecodesLib.readRollupL2DAValidatorBytecode(), + constructorargs: hex"", + create2salt: bytes32(0), + l2GasLimit: Utils.MAX_PRIORITY_TX_GAS, + factoryDeps: new bytes[](0), + chainId: config.chainChainId, + bridgehubAddress: config.bridgehubProxyAddress, + l1SharedBridgeProxy: config.oldSharedBridgeProxyAddress + }); + // FIXME: for now this script only works with rollup chains + require(expectedL2DAValidator == config.expectedRollupL2DAValidator, "Invalid L2DAValidator address"); + + output.l2DAValidator = expectedL2DAValidator; + } + + function deployL2GatewayUpgrade() internal { + address expectedGatewayUpgrade = Utils.deployThroughL1Deterministic({ + bytecode: L2ContractsBytecodesLib.readGatewayUpgradeBytecode(), + constructorargs: hex"", + create2salt: bytes32(0), + l2GasLimit: Utils.MAX_PRIORITY_TX_GAS, + factoryDeps: new bytes[](0), + chainId: config.chainChainId, + bridgehubAddress: config.bridgehubProxyAddress, + l1SharedBridgeProxy: config.oldSharedBridgeProxyAddress + }); + require(expectedGatewayUpgrade == config.expectedL2GatewayUpgrade, "Invalid L2Gateway address"); + } + + function deployNewChainAdmin() internal { + AccessControlRestriction accessControlRestriction = new AccessControlRestriction(0, config.ownerAddress); + + address[] memory restrictions; + if (config.permanentRollup) { + restrictions = new address[](2); + restrictions[0] = address(accessControlRestriction); + restrictions[1] = config.permanentRollupRestriction; + } else { + restrictions = new address[](1); + restrictions[0] = address(accessControlRestriction); + } + + ChainAdmin newChainAdmin = new ChainAdmin(restrictions); + output.chainAdmin = address(newChainAdmin); + output.accessControlRestriction = address(accessControlRestriction); + } + + /// @dev The caller of this function needs to be the owner of the chain admin + /// of the + function governanceMoveToNewChainAdmin() internal { + // Firstly, we need to call the legacy chain admin to transfer the ownership to the new chain admin + Call[] memory calls = new Call[](1); + calls[0] = Call({ + target: config.chainDiamondProxyAddress, + value: 0, + data: abi.encodeCall(IAdmin.setPendingAdmin, (output.chainAdmin)) + }); + + vm.startBroadcast(config.ownerAddress); + ChainAdmin(payable(currentChainAdmin)).multicall(calls, true); + vm.stopBroadcast(); + + // Now we need to accept the adminship + Utils.adminExecute({ + _admin: output.chainAdmin, + _accessControlRestriction: output.accessControlRestriction, + _target: config.chainDiamondProxyAddress, + _data: abi.encodeCall(IAdmin.acceptAdmin, ()), + _value: 0 + }); + } + + function saveOutput(string memory outputPath) internal { + vm.serializeAddress("root", "l2_da_validator_addr", output.l2DAValidator); + vm.serializeAddress("root", "chain_admin_addr", output.chainAdmin); + + string memory toml = vm.serializeAddress("root", "access_control_restriction", output.accessControlRestriction); + string memory root = vm.projectRoot(); + vm.writeToml(toml, outputPath); + console.log("Output saved at:", outputPath); + } +} diff --git a/l1-contracts/deploy-scripts/upgrade/EcosystemUpgrade.s.sol b/l1-contracts/deploy-scripts/upgrade/EcosystemUpgrade.s.sol new file mode 100644 index 000000000..124d33294 --- /dev/null +++ b/l1-contracts/deploy-scripts/upgrade/EcosystemUpgrade.s.sol @@ -0,0 +1,1348 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.24; + +// 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, ITransparentUpgradeableProxy} from "@openzeppelin/contracts-v4/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {UpgradeableBeacon} from "@openzeppelin/contracts-v4/proxy/beacon/UpgradeableBeacon.sol"; +import {Utils, 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"; +import {VerifierParams, IVerifier} from "contracts/state-transition/chain-interfaces/IVerifier.sol"; +import {DefaultUpgrade} from "contracts/upgrades/DefaultUpgrade.sol"; +import {Governance} from "contracts/governance/Governance.sol"; +import {L1GenesisUpgrade} from "contracts/upgrades/L1GenesisUpgrade.sol"; +import {GatewayUpgrade} from "contracts/upgrades/GatewayUpgrade.sol"; +import {ChainAdmin} from "contracts/governance/ChainAdmin.sol"; +import {ValidatorTimelock} from "contracts/state-transition/ValidatorTimelock.sol"; +import {Bridgehub} from "contracts/bridgehub/Bridgehub.sol"; +import {MessageRoot} from "contracts/bridgehub/MessageRoot.sol"; +import {CTMDeploymentTracker} from "contracts/bridgehub/CTMDeploymentTracker.sol"; +import {L1NativeTokenVault} from "contracts/bridge/ntv/L1NativeTokenVault.sol"; +import {ExecutorFacet} from "contracts/state-transition/chain-deps/facets/Executor.sol"; +import {AdminFacet} from "contracts/state-transition/chain-deps/facets/Admin.sol"; +import {MailboxFacet} from "contracts/state-transition/chain-deps/facets/Mailbox.sol"; +import {GettersFacet} from "contracts/state-transition/chain-deps/facets/Getters.sol"; +import {DiamondInit} from "contracts/state-transition/chain-deps/DiamondInit.sol"; +import {ChainTypeManager} from "contracts/state-transition/ChainTypeManager.sol"; +import {ChainTypeManagerInitializeData, ChainCreationParams} from "contracts/state-transition/IChainTypeManager.sol"; +import {IChainTypeManager} from "contracts/state-transition/IChainTypeManager.sol"; +import {Diamond} from "contracts/state-transition/libraries/Diamond.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 {L1AssetRouter} from "contracts/bridge/asset-router/L1AssetRouter.sol"; +import {L1ERC20Bridge} from "contracts/bridge/L1ERC20Bridge.sol"; +import {L1Nullifier} from "contracts/bridge/L1Nullifier.sol"; +import {DiamondProxy} from "contracts/state-transition/chain-deps/DiamondProxy.sol"; +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 {PermanentRestriction} from "contracts/governance/PermanentRestriction.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"; +import {ValidiumL1DAValidator} from "contracts/state-transition/data-availability/ValidiumL1DAValidator.sol"; +import {Call} from "contracts/governance/Common.sol"; +import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable-v4/access/Ownable2StepUpgradeable.sol"; +import {IZKChain} from "contracts/state-transition/chain-interfaces/IZKChain.sol"; +import {ProposedUpgrade} from "contracts/upgrades/BaseZkSyncUpgrade.sol"; + +import {L2CanonicalTransaction} from "contracts/common/Messaging.sol"; + +import {L2_FORCE_DEPLOYER_ADDR, L2_COMPLEX_UPGRADER_ADDR} from "contracts/common/L2ContractAddresses.sol"; +import {IComplexUpgrader} from "contracts/state-transition/l2-deps/IComplexUpgrader.sol"; +import {GatewayUpgradeEncodedInput} from "contracts/upgrades/GatewayUpgrade.sol"; +import {TransitionaryOwner} from "contracts/governance/TransitionaryOwner.sol"; + +struct FixedForceDeploymentsData { + uint256 l1ChainId; + uint256 eraChainId; + address l1AssetRouter; + bytes32 l2TokenProxyBytecodeHash; + address aliasedL1Governance; + uint256 maxNumberOfZKChains; + bytes32 bridgehubBytecodeHash; + bytes32 l2AssetRouterBytecodeHash; + bytes32 l2NtvBytecodeHash; + bytes32 messageRootBytecodeHash; + address l2SharedBridgeLegacyImpl; + address l2BridgedStandardERC20Impl; + address l2BridgeProxyOwnerAddress; + address l2BridgedStandardERC20ProxyOwnerAddress; +} + +// A subset of the ones used for tests +struct StateTransitionDeployedAddresses { + address chainTypeManagerImplementation; + address verifier; + address adminFacet; + address mailboxFacet; + address executorFacet; + address gettersFacet; + address diamondInit; + address genesisUpgrade; + address defaultUpgrade; + address validatorTimelock; +} + +contract EcosystemUpgrade is Script { + using stdToml for string; + + address internal constant ADDRESS_ONE = 0x0000000000000000000000000000000000000001; + address internal constant DETERMINISTIC_CREATE2_ADDRESS = 0x4e59b44847b379578588920cA78FbF26c0B4956C; + + // solhint-disable-next-line gas-struct-packing + struct DeployedAddresses { + BridgehubDeployedAddresses bridgehub; + StateTransitionDeployedAddresses stateTransition; + BridgesDeployedAddresses bridges; + L1NativeTokenVaultAddresses vaults; + DataAvailabilityDeployedAddresses daAddresses; + ExpectedL2Addresses expectedL2Addresses; + address chainAdmin; + address accessControlRestrictionAddress; + address permanentRollupRestriction; + address validatorTimelock; + address gatewayUpgrade; + address create2Factory; + address transitionaryOwner; + } + + struct ExpectedL2Addresses { + address expectedRollupL2DAValidator; + address expectedValidiumL2DAValidator; + address expectedL2GatewayUpgrade; + address l2SharedBridgeLegacyImpl; + address l2BridgedStandardERC20Impl; + // In reality, the following addresses need to be + // deployed only on a settlement layer, i.e. the Gateway. + address expectedL2ProxyAdminDeployer; + address expectedL2ProxyAdmin; + address expectedL2AdminFactory; + address expectedL2PermanentRestrictionImpl; + address expectedL2PermanentRestrictionProxy; + } + + // solhint-disable-next-line gas-struct-packing + struct L1NativeTokenVaultAddresses { + address l1NativeTokenVaultImplementation; + address l1NativeTokenVaultProxy; + } + + struct DataAvailabilityDeployedAddresses { + address l1RollupDAValidator; + address l1ValidiumDAValidator; + } + + // solhint-disable-next-line gas-struct-packing + struct BridgehubDeployedAddresses { + address bridgehubImplementation; + address ctmDeploymentTrackerImplementation; + address ctmDeploymentTrackerProxy; + address messageRootImplementation; + address messageRootProxy; + } + + // solhint-disable-next-line gas-struct-packing + struct BridgesDeployedAddresses { + address erc20BridgeImplementation; + address sharedBridgeProxy; + address sharedBridgeImplementation; + address l1NullifierImplementation; + address bridgedStandardERC20Implementation; + address bridgedTokenBeacon; + } + + // solhint-disable-next-line gas-struct-packing + struct Config { + uint256 l1ChainId; + address deployerAddress; + uint256 eraChainId; + address ownerAddress; + bool testnetVerifier; + ContractsConfig contracts; + TokensConfig tokens; + } + + // solhint-disable-next-line gas-struct-packing + struct GeneratedData { + bytes forceDeploymentsData; + bytes diamondCutData; + } + + // solhint-disable-next-line gas-struct-packing + struct ContractsConfig { + bytes32 create2FactorySalt; + address create2FactoryAddr; + uint256 validatorTimelockExecutionDelay; + bytes32 genesisRoot; + uint256 genesisRollupLeafIndex; + bytes32 genesisBatchCommitment; + uint256 latestProtocolVersion; + bytes32 recursionNodeLevelVkHash; + bytes32 recursionLeafLevelVkHash; + bytes32 recursionCircuitsSetVksHash; + uint256 priorityTxMaxGasLimit; + PubdataPricingMode diamondInitPubdataPricingMode; + uint256 diamondInitBatchOverheadL1Gas; + uint256 diamondInitMaxPubdataPerBatch; + uint256 diamondInitMaxL2GasPerBatch; + uint256 diamondInitPriorityTxMaxPubdata; + uint256 diamondInitMinimalL2GasPrice; + uint256 maxNumberOfChains; + bytes32 bootloaderHash; + bytes32 defaultAAHash; + address oldValidatorTimelock; + address legacyErc20BridgeAddress; + address bridgehubProxyAddress; + address oldSharedBridgeProxyAddress; + address stateTransitionManagerAddress; + address transparentProxyAdmin; + address eraDiamondProxy; + address blobVersionedHashRetriever; + address l2BridgeProxyOwnerAddress; + address l2BridgedStandardERC20ProxyOwnerAddress; + } + + struct TokensConfig { + address tokenWethAddress; + } + + Config internal config; + GeneratedData internal generatedData; + DeployedAddresses internal addresses; + + function prepareEcosystemContracts(string memory configPath, string memory outputPath) public { + string memory root = vm.projectRoot(); + configPath = string.concat(root, configPath); + outputPath = string.concat(root, outputPath); + + initializeConfig(configPath); + + instantiateCreate2Factory(); + + deployVerifier(); + deployDefaultUpgrade(); + deployGenesisUpgrade(); + deployGatewayUpgrade(); + + deployDAValidators(); + deployValidatorTimelock(); + + // TODO: restore + // deployChainAdmin(); + deployBridgehubImplementation(); + deployMessageRootContract(); + + deployL1NullifierContracts(); + deploySharedBridgeContracts(); + deployBridgedStandardERC20Implementation(); + deployBridgedTokenBeacon(); + deployL1NativeTokenVaultImplementation(); + deployL1NativeTokenVaultProxy(); + deployErc20BridgeImplementation(); + + deployCTMDeploymentTracker(); + + initializeGeneratedData(); + initializeExpectedL2Addresses(); + + deployChainTypeManagerContract(); + setChainTypeManagerInValidatorTimelock(); + + deployPermanentRollupRestriction(); + + deployTransitionaryOwner(); + + updateOwners(); + + saveOutput(outputPath); + } + + function run() public { + console.log("Deploying L1 contracts"); + } + + function provideAcceptOwnershipCalls() public returns (Call[] memory calls) { + console.log("Providing accept ownership calls"); + + calls = new Call[](4); + calls[0] = Call({ + target: addresses.permanentRollupRestriction, + data: abi.encodeCall(Ownable2StepUpgradeable.acceptOwnership, ()), + value: 0 + }); + calls[1] = Call({ + target: addresses.validatorTimelock, + data: abi.encodeCall(Ownable2StepUpgradeable.acceptOwnership, ()), + value: 0 + }); + calls[2] = Call({ + target: addresses.bridges.sharedBridgeProxy, + data: abi.encodeCall(Ownable2StepUpgradeable.acceptOwnership, ()), + value: 0 + }); + calls[3] = Call({ + target: addresses.bridgehub.ctmDeploymentTrackerProxy, + data: abi.encodeCall(Ownable2StepUpgradeable.acceptOwnership, ()), + value: 0 + }); + } + + function getOwnerAddress() public returns (address) { + return config.ownerAddress; + } + + function _getFacetCutsForDeletion() internal returns (Diamond.FacetCut[] memory facetCuts) { + IZKChain.Facet[] memory facets = IZKChain(config.contracts.eraDiamondProxy).facets(); + + // Freezability does not matter when deleting, so we just put false everywhere + facetCuts = new Diamond.FacetCut[](facets.length); + for (uint i = 0; i < facets.length; i++) { + facetCuts[i] = Diamond.FacetCut({ + facet: address(0), + action: Diamond.Action.Remove, + isFreezable: false, + selectors: facets[i].selectors + }); + } + } + + function _composeUpgradeTx() internal returns (L2CanonicalTransaction memory transaction) { + transaction = L2CanonicalTransaction({ + // FIXME: dont use hardcoded values + txType: 254, + from: uint256(uint160(L2_FORCE_DEPLOYER_ADDR)), + to: uint256(uint160(address(L2_COMPLEX_UPGRADER_ADDR))), + gasLimit: 72_000_000, + gasPerPubdataByteLimit: 800, + maxFeePerGas: 0, + maxPriorityFeePerGas: 0, + paymaster: uint256(uint160(address(0))), + nonce: 25, + value: 0, + reserved: [uint256(0), uint256(0), uint256(0), uint256(0)], + // Note, that the data is empty, it will be fully composed inside the `GatewayUpgrade` contract + data: new bytes(0), + signature: new bytes(0), + // All factory deps should've been published before + factoryDeps: new uint256[](0), + paymasterInput: new bytes(0), + // Reserved dynamic type for the future use-case. Using it should be avoided, + // But it is still here, just in case we want to enable some additional functionality + reservedDynamic: new bytes(0) + }); + } + + function getNewProtocolVersion() public returns (uint256) { + return 0x1900000000; + } + + function getOldProtocolDeadline() public returns (uint256) { + return 7 days; + } + + function getOldProtocolVersion() public returns (uint256) { + return 0x1800000002; + } + + function provideSetNewVersionUpgradeCall() public returns (Call[] memory calls) { + // Just retrieved it from the contract + uint256 PREVIOUS_PROTOCOL_VERSION = getOldProtocolVersion(); + uint256 DEADLINE = getOldProtocolDeadline(); + uint256 NEW_PROTOCOL_VERSION = getNewProtocolVersion(); + Call memory call = Call({ + target: config.contracts.stateTransitionManagerAddress, + data: abi.encodeCall( + ChainTypeManager.setNewVersionUpgrade, + (getChainUpgradeInfo(), PREVIOUS_PROTOCOL_VERSION, DEADLINE, NEW_PROTOCOL_VERSION) + ), + value: 0 + }); + + calls = new Call[](1); + calls[0] = call; + } + + function getChainUpgradeInfo() public returns (Diamond.DiamondCutData memory upgradeCutData) { + Diamond.FacetCut[] memory deletedFacets = _getFacetCutsForDeletion(); + + Diamond.FacetCut[] memory facetCuts = new Diamond.FacetCut[](deletedFacets.length + 4); + for (uint i = 0; i < deletedFacets.length; i++) { + facetCuts[i] = deletedFacets[i]; + } + facetCuts[deletedFacets.length] = Diamond.FacetCut({ + facet: addresses.stateTransition.adminFacet, + action: Diamond.Action.Add, + isFreezable: false, + selectors: Utils.getAllSelectors(addresses.stateTransition.adminFacet.code) + }); + facetCuts[deletedFacets.length + 1] = Diamond.FacetCut({ + facet: addresses.stateTransition.gettersFacet, + action: Diamond.Action.Add, + isFreezable: false, + selectors: Utils.getAllSelectors(addresses.stateTransition.gettersFacet.code) + }); + facetCuts[deletedFacets.length + 2] = Diamond.FacetCut({ + facet: addresses.stateTransition.mailboxFacet, + action: Diamond.Action.Add, + isFreezable: true, + selectors: Utils.getAllSelectors(addresses.stateTransition.mailboxFacet.code) + }); + facetCuts[deletedFacets.length + 3] = Diamond.FacetCut({ + facet: addresses.stateTransition.executorFacet, + action: Diamond.Action.Add, + isFreezable: true, + selectors: Utils.getAllSelectors(addresses.stateTransition.executorFacet.code) + }); + + VerifierParams memory verifierParams = VerifierParams({ + recursionNodeLevelVkHash: config.contracts.recursionNodeLevelVkHash, + recursionLeafLevelVkHash: config.contracts.recursionLeafLevelVkHash, + recursionCircuitsSetVksHash: config.contracts.recursionCircuitsSetVksHash + }); + + // TODO: we should fill this one up completely, but it is straightforward + IL2ContractDeployer.ForceDeployment[] memory baseForceDeployments = new IL2ContractDeployer.ForceDeployment[]( + 0 + ); + address ctmDeployer = addresses.bridgehub.ctmDeploymentTrackerProxy; + + GatewayUpgradeEncodedInput memory gateUpgradeInput = GatewayUpgradeEncodedInput({ + baseForceDeployments: baseForceDeployments, + ctmDeployer: ctmDeployer, + fixedForceDeploymentsData: generatedData.forceDeploymentsData, + l2GatewayUpgrade: addresses.expectedL2Addresses.expectedL2GatewayUpgrade, + oldValidatorTimelock: config.contracts.oldValidatorTimelock, + newValidatorTimelock: addresses.validatorTimelock + }); + + bytes memory postUpgradeCalldata = abi.encode(gateUpgradeInput); + + ProposedUpgrade memory proposedUpgrade = ProposedUpgrade({ + l2ProtocolUpgradeTx: _composeUpgradeTx(), + factoryDeps: new bytes[](0), + bootloaderHash: config.contracts.bootloaderHash, + defaultAccountHash: config.contracts.defaultAAHash, + verifier: addresses.stateTransition.verifier, + verifierParams: verifierParams, + l1ContractsUpgradeCalldata: new bytes(0), + postUpgradeCalldata: postUpgradeCalldata, + // FIXME: TBH, I am not sure if even should even put any time there, + // but we may + upgradeTimestamp: 0, + newProtocolVersion: getNewProtocolVersion() + }); + + upgradeCutData = Diamond.DiamondCutData({ + facetCuts: facetCuts, + initAddress: addresses.gatewayUpgrade, + initCalldata: abi.encodeCall(GatewayUpgrade.upgrade, (proposedUpgrade)) + }); + } + + function getStage2UpgradeCalls() public returns (Call[] memory calls) { + calls = new Call[](9); + + // We need to firstly update all the contracts + calls[0] = Call({ + target: config.contracts.transparentProxyAdmin, + data: abi.encodeCall( + ProxyAdmin.upgrade, + ( + ITransparentUpgradeableProxy(payable(config.contracts.stateTransitionManagerAddress)), + addresses.stateTransition.chainTypeManagerImplementation + ) + ), + value: 0 + }); + calls[1] = Call({ + target: config.contracts.transparentProxyAdmin, + data: abi.encodeCall( + ProxyAdmin.upgradeAndCall, + ( + ITransparentUpgradeableProxy(payable(config.contracts.bridgehubProxyAddress)), + addresses.bridgehub.bridgehubImplementation, + abi.encodeCall(Bridgehub.initializeV2, ()) + ) + ), + value: 0 + }); + calls[2] = Call({ + target: config.contracts.transparentProxyAdmin, + data: abi.encodeCall( + ProxyAdmin.upgrade, + ( + ITransparentUpgradeableProxy(payable(config.contracts.oldSharedBridgeProxyAddress)), + addresses.bridges.l1NullifierImplementation + ) + ), + value: 0 + }); + calls[3] = Call({ + target: config.contracts.transparentProxyAdmin, + data: abi.encodeCall( + ProxyAdmin.upgrade, + ( + ITransparentUpgradeableProxy(payable(config.contracts.legacyErc20BridgeAddress)), + addresses.bridges.erc20BridgeImplementation + ) + ), + value: 0 + }); + + // Now, updating chain creation params + calls[4] = Call({ + target: config.contracts.stateTransitionManagerAddress, + data: abi.encodeCall(ChainTypeManager.setChainCreationParams, (prepareNewChainCreationParams())), + value: 0 + }); + calls[5] = Call({ + target: config.contracts.stateTransitionManagerAddress, + data: abi.encodeCall(ChainTypeManager.setValidatorTimelock, (addresses.validatorTimelock)), + value: 0 + }); + + // Now, we need to update the bridgehub + calls[6] = Call({ + target: config.contracts.bridgehubProxyAddress, + data: abi.encodeCall( + Bridgehub.setAddresses, + ( + addresses.bridges.sharedBridgeProxy, + CTMDeploymentTracker(addresses.bridgehub.ctmDeploymentTrackerProxy), + MessageRoot(addresses.bridgehub.messageRootProxy) + ) + ), + value: 0 + }); + + // Setting the necessary params for the L1Nullifier contract + calls[7] = Call({ + target: config.contracts.oldSharedBridgeProxyAddress, + data: abi.encodeCall( + L1Nullifier.setL1NativeTokenVault, + (L1NativeTokenVault(payable(addresses.vaults.l1NativeTokenVaultProxy))) + ), + value: 0 + }); + calls[8] = Call({ + target: config.contracts.oldSharedBridgeProxyAddress, + data: abi.encodeCall(L1Nullifier.setL1AssetRouter, (addresses.bridges.sharedBridgeProxy)), + value: 0 + }); + } + + function initializeConfig(string memory configPath) internal { + string memory toml = vm.readFile(configPath); + + config.l1ChainId = block.chainid; + 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.eraChainId = toml.readUint("$.era_chain_id"); + config.ownerAddress = toml.readAddress("$.owner_address"); + config.testnetVerifier = toml.readBool("$.testnet_verifier"); + + config.contracts.maxNumberOfChains = toml.readUint("$.contracts.max_number_of_chains"); + config.contracts.create2FactorySalt = toml.readBytes32("$.contracts.create2_factory_salt"); + if (vm.keyExistsToml(toml, "$.contracts.create2_factory_addr")) { + config.contracts.create2FactoryAddr = toml.readAddress("$.contracts.create2_factory_addr"); + } + config.contracts.validatorTimelockExecutionDelay = toml.readUint( + "$.contracts.validator_timelock_execution_delay" + ); + config.contracts.genesisRoot = toml.readBytes32("$.contracts.genesis_root"); + config.contracts.genesisRollupLeafIndex = toml.readUint("$.contracts.genesis_rollup_leaf_index"); + config.contracts.genesisBatchCommitment = toml.readBytes32("$.contracts.genesis_batch_commitment"); + config.contracts.latestProtocolVersion = toml.readUint("$.contracts.latest_protocol_version"); + config.contracts.recursionNodeLevelVkHash = toml.readBytes32("$.contracts.recursion_node_level_vk_hash"); + config.contracts.recursionLeafLevelVkHash = toml.readBytes32("$.contracts.recursion_leaf_level_vk_hash"); + config.contracts.recursionCircuitsSetVksHash = toml.readBytes32("$.contracts.recursion_circuits_set_vks_hash"); + config.contracts.priorityTxMaxGasLimit = toml.readUint("$.contracts.priority_tx_max_gas_limit"); + config.contracts.diamondInitPubdataPricingMode = PubdataPricingMode( + toml.readUint("$.contracts.diamond_init_pubdata_pricing_mode") + ); + config.contracts.diamondInitBatchOverheadL1Gas = toml.readUint( + "$.contracts.diamond_init_batch_overhead_l1_gas" + ); + config.contracts.diamondInitMaxPubdataPerBatch = toml.readUint( + "$.contracts.diamond_init_max_pubdata_per_batch" + ); + config.contracts.diamondInitMaxL2GasPerBatch = toml.readUint("$.contracts.diamond_init_max_l2_gas_per_batch"); + config.contracts.diamondInitPriorityTxMaxPubdata = toml.readUint( + "$.contracts.diamond_init_priority_tx_max_pubdata" + ); + config.contracts.diamondInitMinimalL2GasPrice = toml.readUint("$.contracts.diamond_init_minimal_l2_gas_price"); + config.contracts.defaultAAHash = toml.readBytes32("$.contracts.default_aa_hash"); + config.contracts.bootloaderHash = toml.readBytes32("$.contracts.bootloader_hash"); + + config.contracts.stateTransitionManagerAddress = toml.readAddress( + "$.contracts.state_transition_manager_address" + ); + config.contracts.bridgehubProxyAddress = toml.readAddress("$.contracts.bridgehub_proxy_address"); + config.contracts.oldSharedBridgeProxyAddress = toml.readAddress("$.contracts.old_shared_bridge_proxy_address"); + config.contracts.transparentProxyAdmin = toml.readAddress("$.contracts.transparent_proxy_admin"); + config.contracts.eraDiamondProxy = toml.readAddress("$.contracts.era_diamond_proxy"); + config.contracts.legacyErc20BridgeAddress = toml.readAddress("$.contracts.legacy_erc20_bridge_address"); + config.contracts.oldValidatorTimelock = toml.readAddress("$.contracts.old_validator_timelock"); + // FIXME: value stored there is incorrect at the moment, figure out the correct value + config.contracts.blobVersionedHashRetriever = toml.readAddress("$.contracts.blob_versioned_hash_retriever"); + config.contracts.l2BridgeProxyOwnerAddress = toml.readAddress("$.contracts.l2_bridge_proxy_owner_address"); + config.contracts.l2BridgedStandardERC20ProxyOwnerAddress = toml.readAddress( + "$.contracts.l2_bridged_standard_erc20_proxy_owner_address" + ); + + config.tokens.tokenWethAddress = toml.readAddress("$.tokens.token_weth_address"); + } + + function initializeGeneratedData() internal { + generatedData.forceDeploymentsData = prepareForceDeploymentsData(); + } + + function initializeExpectedL2Addresses() internal { + address aliasedGovernance = AddressAliasHelper.applyL1ToL2Alias(config.ownerAddress); + + address expectedL2ProxyAdminDeployer = Utils.getL2AddressViaCreate2Factory( + bytes32(0), + L2ContractHelper.hashL2Bytecode(L2ContractsBytecodesLib.readProxyAdminDeployerBytecode()), + abi.encode(aliasedGovernance) + ); + address expectedL2ProxyAdmin = L2ContractHelper.computeCreate2Address( + expectedL2ProxyAdminDeployer, + bytes32(0), + L2ContractHelper.hashL2Bytecode(L2ContractsBytecodesLib.readProxyAdminBytecode()), + keccak256(hex"") + ); + + address permanentRestrictionImpl = Utils.getL2AddressViaCreate2Factory( + bytes32(0), + L2ContractHelper.hashL2Bytecode(L2ContractsBytecodesLib.readPermanentRestrictionBytecode()), + // Note that for L2 deployments the L2AdminFactory is 0. + abi.encode(L2_BRIDGEHUB_ADDRESS, address(0)) + ); + + address permanentRestrictionProxy = Utils.getL2AddressViaCreate2Factory( + bytes32(0), + L2ContractHelper.hashL2Bytecode(L2ContractsBytecodesLib.readTransparentUpgradeableProxyBytecode()), + abi.encode( + permanentRestrictionImpl, + expectedL2ProxyAdmin, + abi.encodeCall(PermanentRestriction.initialize, (aliasedGovernance)) + ) + ); + + address[] memory requiredL2Restrictions = new address[](1); + requiredL2Restrictions[0] = permanentRestrictionProxy; + + addresses.expectedL2Addresses = ExpectedL2Addresses({ + expectedRollupL2DAValidator: Utils.getL2AddressViaCreate2Factory( + bytes32(0), + L2ContractHelper.hashL2Bytecode(L2ContractsBytecodesLib.readRollupL2DAValidatorBytecode()), + hex"" + ), + expectedValidiumL2DAValidator: Utils.getL2AddressViaCreate2Factory( + bytes32(0), + L2ContractHelper.hashL2Bytecode(L2ContractsBytecodesLib.readValidiumL2DAValidatorBytecode()), + hex"" + ), + expectedL2GatewayUpgrade: Utils.getL2AddressViaCreate2Factory( + bytes32(0), + L2ContractHelper.hashL2Bytecode(L2ContractsBytecodesLib.readGatewayUpgradeBytecode()), + hex"" + ), + l2SharedBridgeLegacyImpl: Utils.getL2AddressViaCreate2Factory( + bytes32(0), + L2ContractHelper.hashL2Bytecode(L2ContractsBytecodesLib.readL2LegacySharedBridgeBytecode()), + hex"" + ), + l2BridgedStandardERC20Impl: Utils.getL2AddressViaCreate2Factory( + bytes32(0), + L2ContractHelper.hashL2Bytecode(L2ContractsBytecodesLib.readStandardERC20Bytecode()), + hex"" + ), + expectedL2ProxyAdminDeployer: expectedL2ProxyAdminDeployer, + expectedL2ProxyAdmin: expectedL2ProxyAdmin, + expectedL2AdminFactory: Utils.getL2AddressViaCreate2Factory( + bytes32(0), + L2ContractHelper.hashL2Bytecode(L2ContractsBytecodesLib.readL2AdminFactoryBytecode()), + abi.encode(requiredL2Restrictions) + ), + expectedL2PermanentRestrictionImpl: permanentRestrictionImpl, + expectedL2PermanentRestrictionProxy: permanentRestrictionProxy + }); + } + + function instantiateCreate2Factory() internal { + address contractAddress; + + bool isDeterministicDeployed = DETERMINISTIC_CREATE2_ADDRESS.code.length > 0; + bool isConfigured = config.contracts.create2FactoryAddr != address(0); + + if (isConfigured) { + if (config.contracts.create2FactoryAddr.code.length == 0) { + revert AddressHasNoCode(config.contracts.create2FactoryAddr); + } + contractAddress = config.contracts.create2FactoryAddr; + console.log("Using configured Create2Factory address:", contractAddress); + } else if (isDeterministicDeployed) { + contractAddress = DETERMINISTIC_CREATE2_ADDRESS; + console.log("Using deterministic Create2Factory address:", contractAddress); + } else { + contractAddress = Utils.deployCreate2Factory(); + console.log("Create2Factory deployed at:", contractAddress); + } + + addresses.create2Factory = contractAddress; + } + + function deployVerifier() internal { + bytes memory code; + if (config.testnetVerifier) { + code = type(TestnetVerifier).creationCode; + } else { + code = type(Verifier).creationCode; + } + address contractAddress = deployViaCreate2(code); + console.log("Verifier deployed at:", contractAddress); + addresses.stateTransition.verifier = contractAddress; + } + + function deployDefaultUpgrade() internal { + address contractAddress = deployViaCreate2(type(DefaultUpgrade).creationCode); + console.log("DefaultUpgrade deployed at:", contractAddress); + addresses.stateTransition.defaultUpgrade = contractAddress; + } + + function deployGenesisUpgrade() internal { + bytes memory bytecode = abi.encodePacked(type(L1GenesisUpgrade).creationCode); + address contractAddress = deployViaCreate2(bytecode); + console.log("GenesisUpgrade deployed at:", contractAddress); + addresses.stateTransition.genesisUpgrade = contractAddress; + } + + function deployGatewayUpgrade() internal { + bytes memory bytecode = abi.encodePacked(type(GatewayUpgrade).creationCode); + address contractAddress = deployViaCreate2(bytecode); + console.log("GatewayUpgrade deployed at:", contractAddress); + addresses.gatewayUpgrade = contractAddress; + } + + function deployDAValidators() internal { + address contractAddress = deployViaCreate2(Utils.readRollupDAValidatorBytecode()); + console.log("L1RollupDAValidator deployed at:", contractAddress); + addresses.daAddresses.l1RollupDAValidator = contractAddress; + + contractAddress = deployViaCreate2(type(ValidiumL1DAValidator).creationCode); + console.log("L1ValidiumDAValidator deployed at:", contractAddress); + addresses.daAddresses.l1ValidiumDAValidator = contractAddress; + } + + function deployPermanentRollupRestriction() internal { + bytes memory bytecode = abi.encodePacked( + type(PermanentRestriction).creationCode, + abi.encode(config.contracts.bridgehubProxyAddress, addresses.expectedL2Addresses.expectedL2AdminFactory) + ); + address implementationAddress = deployViaCreate2(bytecode); + + bytes memory proxyBytecode = abi.encodePacked( + type(TransparentUpgradeableProxy).creationCode, + abi.encode( + implementationAddress, + config.contracts.transparentProxyAdmin, + abi.encodeCall(PermanentRestriction.initialize, (config.deployerAddress)) + ) + ); + + address proxyAddress = deployViaCreate2(proxyBytecode); + addresses.permanentRollupRestriction = proxyAddress; + // FIXME: supply restrictions + } + + function deployValidatorTimelock() internal { + uint32 executionDelay = uint32(config.contracts.validatorTimelockExecutionDelay); + bytes memory bytecode = abi.encodePacked( + type(ValidatorTimelock).creationCode, + abi.encode(config.deployerAddress, executionDelay, config.eraChainId) + ); + address contractAddress = deployViaCreate2(bytecode); + console.log("ValidatorTimelock deployed at:", contractAddress); + addresses.validatorTimelock = contractAddress; + } + + function deployChainAdmin() internal { + bytes memory accessControlRestrictionBytecode = abi.encodePacked( + type(AccessControlRestriction).creationCode, + abi.encode(uint256(0), config.ownerAddress) + ); + + address accessControlRestriction = deployViaCreate2(accessControlRestrictionBytecode); + 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); + console.log("ChainAdmin deployed at:", contractAddress); + addresses.chainAdmin = contractAddress; + } + + function deployBridgehubImplementation() internal { + bytes memory bridgeHubBytecode = abi.encodePacked( + type(Bridgehub).creationCode, + abi.encode(config.l1ChainId, config.ownerAddress, (config.contracts.maxNumberOfChains)) + ); + address bridgehubImplementation = deployViaCreate2(bridgeHubBytecode); + console.log("Bridgehub Implementation deployed at:", bridgehubImplementation); + addresses.bridgehub.bridgehubImplementation = bridgehubImplementation; + } + + function deployMessageRootContract() internal { + bytes memory messageRootBytecode = abi.encodePacked( + type(MessageRoot).creationCode, + abi.encode(config.contracts.bridgehubProxyAddress) + ); + address messageRootImplementation = deployViaCreate2(messageRootBytecode); + console.log("MessageRoot Implementation deployed at:", messageRootImplementation); + addresses.bridgehub.messageRootImplementation = messageRootImplementation; + + bytes memory bytecode = abi.encodePacked( + type(TransparentUpgradeableProxy).creationCode, + abi.encode( + messageRootImplementation, + config.contracts.transparentProxyAdmin, + abi.encodeCall(MessageRoot.initialize, ()) + ) + ); + address messageRootProxy = deployViaCreate2(bytecode); + console.log("Message Root Proxy deployed at:", messageRootProxy); + addresses.bridgehub.messageRootProxy = messageRootProxy; + } + + function deployCTMDeploymentTracker() internal { + bytes memory ctmDTBytecode = abi.encodePacked( + type(CTMDeploymentTracker).creationCode, + abi.encode(config.contracts.bridgehubProxyAddress, addresses.bridges.sharedBridgeProxy) + ); + address ctmDTImplementation = deployViaCreate2(ctmDTBytecode); + console.log("CTM Deployment Tracker Implementation deployed at:", ctmDTImplementation); + addresses.bridgehub.ctmDeploymentTrackerImplementation = ctmDTImplementation; + + bytes memory bytecode = abi.encodePacked( + type(TransparentUpgradeableProxy).creationCode, + abi.encode( + ctmDTImplementation, + config.contracts.transparentProxyAdmin, + abi.encodeCall(CTMDeploymentTracker.initialize, (config.deployerAddress)) + ) + ); + address ctmDTProxy = deployViaCreate2(bytecode); + console.log("CTM Deployment Tracker Proxy deployed at:", ctmDTProxy); + addresses.bridgehub.ctmDeploymentTrackerProxy = ctmDTProxy; + } + + function deployChainTypeManagerContract() internal { + deployStateTransitionDiamondFacets(); + deployChainTypeManagerImplementation(); + // registerChainTypeManager(); + } + + function deployStateTransitionDiamondFacets() internal { + address executorFacet = deployViaCreate2(type(ExecutorFacet).creationCode); + console.log("ExecutorFacet deployed at:", executorFacet); + addresses.stateTransition.executorFacet = executorFacet; + + address adminFacet = deployViaCreate2( + abi.encodePacked(type(AdminFacet).creationCode, abi.encode(config.l1ChainId)) + ); + console.log("AdminFacet deployed at:", adminFacet); + addresses.stateTransition.adminFacet = adminFacet; + + address mailboxFacet = deployViaCreate2( + abi.encodePacked(type(MailboxFacet).creationCode, abi.encode(config.eraChainId, config.l1ChainId)) + ); + console.log("MailboxFacet deployed at:", mailboxFacet); + addresses.stateTransition.mailboxFacet = mailboxFacet; + + address gettersFacet = deployViaCreate2(type(GettersFacet).creationCode); + console.log("GettersFacet deployed at:", gettersFacet); + addresses.stateTransition.gettersFacet = gettersFacet; + + address diamondInit = deployViaCreate2(type(DiamondInit).creationCode); + console.log("DiamondInit deployed at:", diamondInit); + addresses.stateTransition.diamondInit = diamondInit; + } + + function deployChainTypeManagerImplementation() internal { + bytes memory bytecode = abi.encodePacked( + type(ChainTypeManager).creationCode, + abi.encode(config.contracts.bridgehubProxyAddress) + ); + address contractAddress = deployViaCreate2(bytecode); + console.log("ChainTypeManagerImplementation deployed at:", contractAddress); + addresses.stateTransition.chainTypeManagerImplementation = contractAddress; + } + + function setChainTypeManagerInValidatorTimelock() internal { + ValidatorTimelock validatorTimelock = ValidatorTimelock(addresses.validatorTimelock); + vm.broadcast(msg.sender); + validatorTimelock.setChainTypeManager(IChainTypeManager(config.contracts.stateTransitionManagerAddress)); + console.log("ChainTypeManager set in ValidatorTimelock"); + } + + function deploySharedBridgeContracts() internal { + deploySharedBridgeImplementation(); + deploySharedBridgeProxy(); + setL1LegacyBridge(); + } + + function deployL1NullifierContracts() internal { + deployL1NullifierImplementation(); + } + + function deployL1NullifierImplementation() internal { + // TODO(EVM-743): allow non-dev nullifier in the local deployment + bytes memory bytecode = abi.encodePacked( + type(L1NullifierDev).creationCode, + // solhint-disable-next-line func-named-parameters + abi.encode(config.contracts.bridgehubProxyAddress, config.eraChainId, config.contracts.eraDiamondProxy) + ); + address contractAddress = deployViaCreate2(bytecode); + console.log("L1NullifierImplementation deployed at:", contractAddress); + addresses.bridges.l1NullifierImplementation = contractAddress; + } + + function deploySharedBridgeImplementation() internal { + bytes memory bytecode = abi.encodePacked( + type(L1AssetRouter).creationCode, + // solhint-disable-next-line func-named-parameters + abi.encode( + config.tokens.tokenWethAddress, + config.contracts.bridgehubProxyAddress, + config.contracts.oldSharedBridgeProxyAddress, + config.eraChainId, + config.contracts.eraDiamondProxy + ) + ); + address contractAddress = deployViaCreate2(bytecode); + console.log("SharedBridgeImplementation deployed at:", contractAddress); + addresses.bridges.sharedBridgeImplementation = contractAddress; + } + + function deploySharedBridgeProxy() internal { + bytes memory initCalldata = abi.encodeCall(L1AssetRouter.initialize, (config.deployerAddress)); + bytes memory bytecode = abi.encodePacked( + type(TransparentUpgradeableProxy).creationCode, + abi.encode( + addresses.bridges.sharedBridgeImplementation, + config.contracts.transparentProxyAdmin, + initCalldata + ) + ); + address contractAddress = deployViaCreate2(bytecode); + console.log("SharedBridgeProxy deployed at:", contractAddress); + addresses.bridges.sharedBridgeProxy = contractAddress; + } + + function setL1LegacyBridge() internal { + vm.broadcast(msg.sender); + L1AssetRouter(addresses.bridges.sharedBridgeProxy).setL1Erc20Bridge( + L1ERC20Bridge(config.contracts.legacyErc20BridgeAddress) + ); + } + + function deployErc20BridgeImplementation() internal { + bytes memory bytecode = abi.encodePacked( + type(L1ERC20Bridge).creationCode, + abi.encode( + config.contracts.oldSharedBridgeProxyAddress, + addresses.bridges.sharedBridgeProxy, + addresses.vaults.l1NativeTokenVaultProxy, + config.eraChainId + ) + ); + address contractAddress = deployViaCreate2(bytecode); + console.log("Erc20BridgeImplementation deployed at:", contractAddress); + addresses.bridges.erc20BridgeImplementation = contractAddress; + } + + function deployBridgedStandardERC20Implementation() internal { + bytes memory bytecode = abi.encodePacked( + type(BridgedStandardERC20).creationCode, + // solhint-disable-next-line func-named-parameters + abi.encode() + ); + address contractAddress = deployViaCreate2(bytecode); + console.log("BridgedStandardERC20Implementation deployed at:", contractAddress); + addresses.bridges.bridgedStandardERC20Implementation = contractAddress; + } + + function deployBridgedTokenBeacon() internal { + bytes memory bytecode = abi.encodePacked( + type(UpgradeableBeacon).creationCode, + // solhint-disable-next-line func-named-parameters + abi.encode(addresses.bridges.bridgedStandardERC20Implementation) + ); + UpgradeableBeacon beacon = new UpgradeableBeacon(addresses.bridges.bridgedStandardERC20Implementation); + address contractAddress = address(beacon); + beacon.transferOwnership(config.ownerAddress); + console.log("BridgedTokenBeacon deployed at:", contractAddress); + addresses.bridges.bridgedTokenBeacon = contractAddress; + } + + function deployL1NativeTokenVaultImplementation() internal { + bytes memory bytecode = abi.encodePacked( + type(L1NativeTokenVault).creationCode, + // solhint-disable-next-line func-named-parameters + abi.encode( + config.tokens.tokenWethAddress, + addresses.bridges.sharedBridgeProxy, + config.eraChainId, + config.contracts.oldSharedBridgeProxyAddress + ) + ); + address contractAddress = deployViaCreate2(bytecode); + console.log("L1NativeTokenVaultImplementation deployed at:", contractAddress); + addresses.vaults.l1NativeTokenVaultImplementation = contractAddress; + } + + function deployL1NativeTokenVaultProxy() internal { + bytes memory initCalldata = abi.encodeCall( + L1NativeTokenVault.initialize, + (config.ownerAddress, addresses.bridges.bridgedTokenBeacon) + ); + bytes memory bytecode = abi.encodePacked( + type(TransparentUpgradeableProxy).creationCode, + abi.encode( + addresses.vaults.l1NativeTokenVaultImplementation, + config.contracts.transparentProxyAdmin, + initCalldata + ) + ); + address contractAddress = deployViaCreate2(bytecode); + console.log("L1NativeTokenVaultProxy deployed at:", contractAddress); + addresses.vaults.l1NativeTokenVaultProxy = contractAddress; + + IL1AssetRouter sharedBridge = IL1AssetRouter(addresses.bridges.sharedBridgeProxy); + IL1Nullifier l1Nullifier = IL1Nullifier(config.contracts.oldSharedBridgeProxyAddress); + // Ownable ownable = Ownable(addresses.bridges.sharedBridgeProxy); + + vm.broadcast(msg.sender); + sharedBridge.setNativeTokenVault(INativeTokenVault(addresses.vaults.l1NativeTokenVaultProxy)); + vm.broadcast(msg.sender); + IL1NativeTokenVault(addresses.vaults.l1NativeTokenVaultProxy).registerEthToken(); + } + + function deployTransitionaryOwner() internal { + bytes memory bytecode = abi.encodePacked( + type(TransitionaryOwner).creationCode, + abi.encode(config.ownerAddress) + ); + + addresses.transitionaryOwner = deployViaCreate2(bytecode); + } + + function _moveGovernanceToOwner(address target) internal { + Ownable2StepUpgradeable(target).transferOwnership(addresses.transitionaryOwner); + TransitionaryOwner(addresses.transitionaryOwner).claimOwnershipAndGiveToGovernance(target); + } + + function updateOwners() internal { + vm.startBroadcast(msg.sender); + + // Note, that it will take some time for the governance to sign the "acceptOwnership" transaction, + // in order to avoid any possibility of the front-run, we will temporarily give the ownership to the + // contract that can only transfer ownership to the governance. + _moveGovernanceToOwner(addresses.validatorTimelock); + _moveGovernanceToOwner(addresses.bridges.sharedBridgeProxy); + _moveGovernanceToOwner(addresses.bridgehub.ctmDeploymentTrackerProxy); + _moveGovernanceToOwner(addresses.permanentRollupRestriction); + + vm.stopBroadcast(); + console.log("Owners updated"); + } + + function prepareNewChainCreationParams() internal returns (ChainCreationParams memory chainCreationParams) { + Diamond.FacetCut[] memory facetCuts = new Diamond.FacetCut[](4); + facetCuts[0] = Diamond.FacetCut({ + facet: addresses.stateTransition.adminFacet, + action: Diamond.Action.Add, + isFreezable: false, + selectors: Utils.getAllSelectors(addresses.stateTransition.adminFacet.code) + }); + facetCuts[1] = Diamond.FacetCut({ + facet: addresses.stateTransition.gettersFacet, + action: Diamond.Action.Add, + isFreezable: false, + selectors: Utils.getAllSelectors(addresses.stateTransition.gettersFacet.code) + }); + facetCuts[2] = Diamond.FacetCut({ + facet: addresses.stateTransition.mailboxFacet, + action: Diamond.Action.Add, + isFreezable: true, + selectors: Utils.getAllSelectors(addresses.stateTransition.mailboxFacet.code) + }); + facetCuts[3] = Diamond.FacetCut({ + facet: addresses.stateTransition.executorFacet, + action: Diamond.Action.Add, + isFreezable: true, + selectors: Utils.getAllSelectors(addresses.stateTransition.executorFacet.code) + }); + + VerifierParams memory verifierParams = VerifierParams({ + recursionNodeLevelVkHash: config.contracts.recursionNodeLevelVkHash, + recursionLeafLevelVkHash: config.contracts.recursionLeafLevelVkHash, + recursionCircuitsSetVksHash: config.contracts.recursionCircuitsSetVksHash + }); + + FeeParams memory feeParams = FeeParams({ + pubdataPricingMode: config.contracts.diamondInitPubdataPricingMode, + batchOverheadL1Gas: uint32(config.contracts.diamondInitBatchOverheadL1Gas), + maxPubdataPerBatch: uint32(config.contracts.diamondInitMaxPubdataPerBatch), + maxL2GasPerBatch: uint32(config.contracts.diamondInitMaxL2GasPerBatch), + priorityTxMaxPubdata: uint32(config.contracts.diamondInitPriorityTxMaxPubdata), + minimalL2GasPrice: uint64(config.contracts.diamondInitMinimalL2GasPrice) + }); + + DiamondInitializeDataNewChain memory initializeData = DiamondInitializeDataNewChain({ + verifier: IVerifier(addresses.stateTransition.verifier), + verifierParams: verifierParams, + l2BootloaderBytecodeHash: config.contracts.bootloaderHash, + l2DefaultAccountBytecodeHash: config.contracts.defaultAAHash, + priorityTxMaxGasLimit: config.contracts.priorityTxMaxGasLimit, + feeParams: feeParams, + blobVersionedHashRetriever: config.contracts.blobVersionedHashRetriever + }); + + Diamond.DiamondCutData memory diamondCut = Diamond.DiamondCutData({ + facetCuts: facetCuts, + initAddress: addresses.stateTransition.diamondInit, + initCalldata: abi.encode(initializeData) + }); + + chainCreationParams = ChainCreationParams({ + genesisUpgrade: addresses.stateTransition.genesisUpgrade, + genesisBatchHash: config.contracts.genesisRoot, + genesisIndexRepeatedStorageChanges: uint64(config.contracts.genesisRollupLeafIndex), + genesisBatchCommitment: config.contracts.genesisBatchCommitment, + diamondCut: diamondCut, + forceDeploymentsData: generatedData.forceDeploymentsData + }); + } + + function saveOutput(string memory outputPath) internal { + vm.serializeAddress("bridgehub", "bridgehub_implementation_addr", addresses.bridgehub.bridgehubImplementation); + vm.serializeAddress( + "bridgehub", + "ctm_deployment_tracker_proxy_addr", + addresses.bridgehub.ctmDeploymentTrackerProxy + ); + vm.serializeAddress( + "bridgehub", + "ctm_deployment_tracker_implementation_addr", + addresses.bridgehub.ctmDeploymentTrackerImplementation + ); + vm.serializeAddress("bridgehub", "message_root_proxy_addr", addresses.bridgehub.messageRootProxy); + string memory bridgehub = vm.serializeAddress( + "bridgehub", + "message_root_implementation_addr", + addresses.bridgehub.messageRootImplementation + ); + + // TODO(EVM-744): this has to be renamed to chain type manager + vm.serializeAddress( + "state_transition", + "state_transition_implementation_addr", + addresses.stateTransition.chainTypeManagerImplementation + ); + vm.serializeAddress("state_transition", "verifier_addr", addresses.stateTransition.verifier); + vm.serializeAddress("state_transition", "admin_facet_addr", addresses.stateTransition.adminFacet); + vm.serializeAddress("state_transition", "mailbox_facet_addr", addresses.stateTransition.mailboxFacet); + vm.serializeAddress("state_transition", "executor_facet_addr", addresses.stateTransition.executorFacet); + vm.serializeAddress("state_transition", "getters_facet_addr", addresses.stateTransition.gettersFacet); + vm.serializeAddress("state_transition", "diamond_init_addr", addresses.stateTransition.diamondInit); + vm.serializeAddress("state_transition", "genesis_upgrade_addr", addresses.stateTransition.genesisUpgrade); + string memory stateTransition = vm.serializeAddress( + "state_transition", + "default_upgrade_addr", + addresses.stateTransition.defaultUpgrade + ); + + vm.serializeAddress("bridges", "erc20_bridge_implementation_addr", addresses.bridges.erc20BridgeImplementation); + vm.serializeAddress("bridges", "l1_nullifier_implementation_addr", addresses.bridges.l1NullifierImplementation); + vm.serializeAddress( + "bridges", + "shared_bridge_implementation_addr", + addresses.bridges.sharedBridgeImplementation + ); + string memory bridges = vm.serializeAddress( + "bridges", + "shared_bridge_proxy_addr", + addresses.bridges.sharedBridgeProxy + ); + + vm.serializeUint( + "contracts_config", + "diamond_init_max_l2_gas_per_batch", + config.contracts.diamondInitMaxL2GasPerBatch + ); + vm.serializeUint( + "contracts_config", + "diamond_init_batch_overhead_l1_gas", + config.contracts.diamondInitBatchOverheadL1Gas + ); + vm.serializeUint( + "contracts_config", + "diamond_init_max_pubdata_per_batch", + config.contracts.diamondInitMaxPubdataPerBatch + ); + vm.serializeUint( + "contracts_config", + "diamond_init_minimal_l2_gas_price", + config.contracts.diamondInitMinimalL2GasPrice + ); + vm.serializeUint( + "contracts_config", + "diamond_init_priority_tx_max_pubdata", + config.contracts.diamondInitPriorityTxMaxPubdata + ); + vm.serializeUint( + "contracts_config", + "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_circuits_set_vks_hash", + config.contracts.recursionCircuitsSetVksHash + ); + vm.serializeBytes32( + "contracts_config", + "recursion_leaf_level_vk_hash", + config.contracts.recursionLeafLevelVkHash + ); + vm.serializeBytes32( + "contracts_config", + "recursion_node_level_vk_hash", + config.contracts.recursionNodeLevelVkHash + ); + + vm.serializeAddress( + "contracts_config", + "expected_rollup_l2_da_validator", + addresses.expectedL2Addresses.expectedRollupL2DAValidator + ); + vm.serializeAddress( + "contracts_config", + "expected_validium_l2_da_validator", + addresses.expectedL2Addresses.expectedValidiumL2DAValidator + ); + vm.serializeAddress( + "contracts_config", + "expected_l2_gateway_upgrade", + addresses.expectedL2Addresses.expectedL2GatewayUpgrade + ); + vm.serializeBytes("contracts_config", "diamond_cut_data", generatedData.diamondCutData); + + string memory contractsConfig = vm.serializeBytes( + "contracts_config", + "force_deployments_data", + generatedData.forceDeploymentsData + ); + + vm.serializeAddress("deployed_addresses", "validator_timelock_addr", addresses.validatorTimelock); + vm.serializeAddress("deployed_addresses", "chain_admin", addresses.chainAdmin); + vm.serializeAddress( + "deployed_addresses", + "access_control_restriction_addr", + addresses.accessControlRestrictionAddress + ); + vm.serializeAddress("deployed_addresses", "permanent_rollup_restriction", addresses.permanentRollupRestriction); + vm.serializeString("deployed_addresses", "bridgehub", bridgehub); + vm.serializeString("deployed_addresses", "bridges", bridges); + vm.serializeString("deployed_addresses", "state_transition", stateTransition); + + vm.serializeAddress( + "deployed_addresses", + "rollup_l1_da_validator_addr", + addresses.daAddresses.l1RollupDAValidator + ); + vm.serializeAddress( + "deployed_addresses", + "validium_l1_da_validator_addr", + addresses.daAddresses.l1ValidiumDAValidator + ); + + 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); + vm.serializeUint("root", "l1_chain_id", config.l1ChainId); + vm.serializeUint("root", "era_chain_id", config.eraChainId); + vm.serializeAddress("root", "deployer_addr", config.deployerAddress); + vm.serializeString("root", "deployed_addresses", deployedAddresses); + vm.serializeString("root", "contracts_config", contractsConfig); + string memory toml = vm.serializeAddress("root", "owner_address", config.ownerAddress); + + 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(config.ownerAddress != address(0), "owner not set"); + + FixedForceDeploymentsData memory data = FixedForceDeploymentsData({ + l1ChainId: config.l1ChainId, + eraChainId: config.eraChainId, + l1AssetRouter: addresses.bridges.sharedBridgeProxy, + l2TokenProxyBytecodeHash: L2ContractHelper.hashL2Bytecode( + L2ContractsBytecodesLib.readBeaconProxyBytecode() + ), + aliasedL1Governance: AddressAliasHelper.applyL1ToL2Alias(config.ownerAddress), + maxNumberOfZKChains: config.contracts.maxNumberOfChains, + bridgehubBytecodeHash: L2ContractHelper.hashL2Bytecode(L2ContractsBytecodesLib.readBridgehubBytecode()), + l2AssetRouterBytecodeHash: L2ContractHelper.hashL2Bytecode( + L2ContractsBytecodesLib.readL2AssetRouterBytecode() + ), + l2NtvBytecodeHash: L2ContractHelper.hashL2Bytecode( + L2ContractsBytecodesLib.readL2NativeTokenVaultBytecode() + ), + messageRootBytecodeHash: L2ContractHelper.hashL2Bytecode(L2ContractsBytecodesLib.readMessageRootBytecode()), + l2SharedBridgeLegacyImpl: addresses.expectedL2Addresses.l2SharedBridgeLegacyImpl, + l2BridgedStandardERC20Impl: addresses.expectedL2Addresses.l2BridgedStandardERC20Impl, + l2BridgeProxyOwnerAddress: config.contracts.l2BridgeProxyOwnerAddress, + l2BridgedStandardERC20ProxyOwnerAddress: config.contracts.l2BridgedStandardERC20ProxyOwnerAddress + }); + + return abi.encode(data); + } + + // add this to be excluded from coverage report + function test() internal {} +} diff --git a/l1-contracts/foundry.toml b/l1-contracts/foundry.toml index c9a299f88..b4dc2ca3b 100644 --- a/l1-contracts/foundry.toml +++ b/l1-contracts/foundry.toml @@ -19,6 +19,8 @@ fs_permissions = [ { access = "read", path = "./out" }, { 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-write", path = "./test/foundry/l1/integration/upgrade-envs/script-config/" }, + { access = "read-write", path = "./test/foundry/l1/integration/upgrade-envs/script-out/" }, { access = "read", path = "zkout" }, ] ignored_error_codes = ["missing-receive-ether", "code-size"] @@ -34,7 +36,7 @@ remappings = [ "@openzeppelin/contracts-upgradeable-v4/=lib/openzeppelin-contracts-upgradeable-v4/contracts/", ] optimizer = true -optimizer_runs = 9999999 +optimizer_runs = 200 [profile.default.zksync] enable_eravm_extensions = true zksolc = "1.5.3" diff --git a/l1-contracts/package.json b/l1-contracts/package.json index 0c955188e..effe54841 100644 --- a/l1-contracts/package.json +++ b/l1-contracts/package.json @@ -58,10 +58,11 @@ "clean": "hardhat clean && CONTRACTS_BASE_NETWORK_ZKSYNC=true hardhat clean", "clean:foundry": "forge clean", "test": "yarn workspace da-contracts build && hardhat test test/unit_tests/*.spec.ts --network hardhat", - "test:foundry": "forge test --ffi --match-path 'test/foundry/l1/*'", + "test:foundry": "forge test --ffi --match-path 'test/foundry/l1/*' --no-match-test 'test_MainnetFork'", "test:zkfoundry": "forge test --zksync --match-path 'test/foundry/l2/*'", + "test:mainnet-upgrade-fork": "forge test --match-test test_MainnetFork --ffi --rpc-url $INFURA_MAINNET", "test:fork": "TEST_CONTRACTS_FORK=1 yarn run hardhat test test/unit_tests/*.fork.ts --network hardhat", - "coverage:foundry": "forge coverage --ffi --match-path 'test/foundry/l1/*' --no-match-coverage 'contracts/(bridge/.*L2.*\\.sol|governance/L2AdminFactory\\.sol)'", + "coverage:foundry": "forge coverage --ffi --match-path 'test/foundry/l1/*' --no-match-coverage 'contracts/(bridge/.*L2.*\\.sol|governance/L2AdminFactory\\.sol)' --no-match-test test_MainnetFork", "deploy-no-build": "ts-node scripts/deploy.ts", "register-zk-chain": "ts-node scripts/register-zk-chain.ts", "deploy-weth-bridges": "ts-node scripts/deploy-weth-bridges.ts", diff --git a/l1-contracts/src.ts/deploy.ts b/l1-contracts/src.ts/deploy.ts index b798378aa..7083bbb24 100644 --- a/l1-contracts/src.ts/deploy.ts +++ b/l1-contracts/src.ts/deploy.ts @@ -209,6 +209,10 @@ export class Deployer { l2AssetRouterBytecodeHash: ethers.utils.hexlify(hashL2Bytecode(assetRouterZKBytecode)), l2NtvBytecodeHash: ethers.utils.hexlify(hashL2Bytecode(nativeTokenVaultZKBytecode)), messageRootBytecodeHash: ethers.utils.hexlify(hashL2Bytecode(messageRootZKBytecode)), + l2SharedBridgeLegacyImpl: ethers.constants.AddressZero, + l2BridgedStandardERC20Impl: ethers.constants.AddressZero, + l2BridgeProxyOwnerAddress: ethers.constants.AddressZero, + l2BridgedStandardERC20ProxyOwnerAddress: ethers.constants.AddressZero, }; return ethers.utils.defaultAbiCoder.encode([FIXED_FORCE_DEPLOYMENTS_DATA_ABI_STRING], [fixedForceDeploymentsData]); diff --git a/l1-contracts/src.ts/utils.ts b/l1-contracts/src.ts/utils.ts index ce57958e0..190a5743c 100644 --- a/l1-contracts/src.ts/utils.ts +++ b/l1-contracts/src.ts/utils.ts @@ -47,7 +47,7 @@ export const FORCE_DEPLOYMENT_ABI_STRING = "tuple(bytes32 bytecodeHash, address newAddress, bool callConstructor, uint256 value, bytes input)[]"; export const BRIDGEHUB_CTM_ASSET_DATA_ABI_STRING = "tuple(uint256 chainId, bytes ctmData, bytes chainData)"; export const FIXED_FORCE_DEPLOYMENTS_DATA_ABI_STRING = - "tuple(uint256 l1ChainId, uint256 eraChainId, address l1AssetRouter, bytes32 l2TokenProxyBytecodeHash, address aliasedL1Governance, uint256 maxNumberOfZKChains, bytes32 bridgehubBytecodeHash, bytes32 l2AssetRouterBytecodeHash, bytes32 l2NtvBytecodeHash, bytes32 messageRootBytecodeHash)"; + "tuple(uint256 l1ChainId, uint256 eraChainId, address l1AssetRouter, bytes32 l2TokenProxyBytecodeHash, address aliasedL1Governance, uint256 maxNumberOfZKChains, bytes32 bridgehubBytecodeHash, bytes32 l2AssetRouterBytecodeHash, bytes32 l2NtvBytecodeHash, bytes32 messageRootBytecodeHash, address l2SharedBridgeLegacyImpl, address l2BridgedStandardERC20Impl, address l2BridgeProxyOwnerAddress, address l2BridgedStandardERC20ProxyOwnerAddress)"; export const ADDITIONAL_FORCE_DEPLOYMENTS_DATA_ABI_STRING = "tuple(bytes32 baseTokenAssetId, address l2Weth)"; export function applyL1ToL2Alias(address: string): string { @@ -321,7 +321,6 @@ export function compileInitialCutHash( admin: "0x0000000000000000000000000000000000003234", validatorTimelock: "0x0000000000000000000000000000000000004234", baseTokenAssetId: "0x0000000000000000000000000000000000000000000000000000000000004234", - baseTokenBridge: "0x0000000000000000000000000000000000004234", storedBatchZero: "0x0000000000000000000000000000000000000000000000000000000000005432", verifier, verifierParams, @@ -333,7 +332,7 @@ export function compileInitialCutHash( }, ]); - return diamondCut(facetCuts, diamondInit, "0x" + diamondInitCalldata.slice(2 + (4 + 9 * 32) * 2)); + return diamondCut(facetCuts, diamondInit, "0x" + diamondInitCalldata.slice(2 + (4 + 8 * 32) * 2)); } export enum PubdataSource { diff --git a/l1-contracts/test/foundry/l1/integration/GatewayTests.t.sol b/l1-contracts/test/foundry/l1/integration/GatewayTests.t.sol index 0cfcbf049..d4aa1ba0a 100644 --- a/l1-contracts/test/foundry/l1/integration/GatewayTests.t.sol +++ b/l1-contracts/test/foundry/l1/integration/GatewayTests.t.sol @@ -258,7 +258,6 @@ contract GatewayTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, L2T address chain = _deployZkChain( chainId, baseTokenAssetId, - address(bridgehub.sharedBridge()), owner, stm.protocolVersion(), stm.storedBatchZero(), @@ -290,7 +289,6 @@ contract GatewayTests is L1ContractDeployer, ZKChainDeployer, TokenDeployer, L2T address chain = _deployZkChain( chainId, baseTokenAssetId, - address(bridgehub.sharedBridge()), owner, stm.protocolVersion(), stm.storedBatchZero(), diff --git a/l1-contracts/test/foundry/l1/integration/UpgradeTest.t.sol b/l1-contracts/test/foundry/l1/integration/UpgradeTest.t.sol new file mode 100644 index 000000000..3cbcf454b --- /dev/null +++ b/l1-contracts/test/foundry/l1/integration/UpgradeTest.t.sol @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.24; + +// 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 {EcosystemUpgrade} from "deploy-scripts/upgrade/EcosystemUpgrade.s.sol"; +import {ChainUpgrade} from "deploy-scripts/upgrade/ChainUpgrade.s.sol"; +import {Call} from "contracts/governance/Common.sol"; +import {Test} from "forge-std/Test.sol"; + +string constant ECOSYSTEM_INPUT = "/test/foundry/l1/integration/upgrade-envs/script-config/mainnet.toml"; +string constant ECOSYSTEM_OUTPUT = "/test/foundry/l1/integration/upgrade-envs/script-out/mainnet.toml"; +string constant CHAIN_INPUT = "/test/foundry/l1/integration/upgrade-envs/script-config/mainnet-era.toml"; +string constant CHAIN_OUTPUT = "/test/foundry/l1/integration/upgrade-envs/script-out/mainnet-era.toml"; + +contract UpgradeTest is Test { + EcosystemUpgrade generateUpgradeData; + ChainUpgrade chainUpgrade; + + function setUp() public { + generateUpgradeData = new EcosystemUpgrade(); + chainUpgrade = new ChainUpgrade(); + } + + function test_MainnetFork() public { + console.log("Preparing ecosystem contracts"); + // Firstly, we deploy all the contracts. + generateUpgradeData.prepareEcosystemContracts(ECOSYSTEM_INPUT, ECOSYSTEM_OUTPUT); + + // For chain, we have deployed the DA validator contracts + // and also updated the chain admin. + // IMPORTANT: for erc20-based chains with token multiplier setter + // this should be coordinated with the server. + console.log("Preparing chain for the upgrade"); + chainUpgrade.prepareChain(ECOSYSTEM_INPUT, ECOSYSTEM_OUTPUT, CHAIN_INPUT, CHAIN_OUTPUT); + + console.log("Starting stage1 of the upgrade!"); + // Now, some time has passed and we are ready to start the upgrade of the + // ecosystem. + // Stage 1 of the upgrade: + // - accept all the ownerships of the contracts + // - set the new upgrade data for chains + update validator timelock. + Call[] memory stage1Calls = mergeCalls( + generateUpgradeData.provideAcceptOwnershipCalls(), + generateUpgradeData.provideSetNewVersionUpgradeCall() + ); + + governanceMulticall(generateUpgradeData.getOwnerAddress(), stage1Calls); + + console.log("Stage1 is done, now all the chains have to upgrade to the new version"); + + console.log("Upgrading Era"); + + // Now, the admin of the Era needs to call the upgrade function. + // Note, that the step below also updated ValidatorTimelock so the server needs to be ready for that. + // TODO: We do not include calls that ensure that the server is ready for the sake of brevity. + chainUpgrade.upgradeChain( + generateUpgradeData.getOldProtocolVersion(), + generateUpgradeData.getChainUpgradeInfo() + ); + + // TODO: here we should include tests that depoists work for upgraded chains + // including era specific deposit/withdraw functions + // We also may need to test that normal flow of block commit / verify / execute works (but it is hard) + + vm.warp(generateUpgradeData.getOldProtocolDeadline()); + + console.log("Starting stage2 of the upgrade!"); + governanceMulticall(generateUpgradeData.getOwnerAddress(), generateUpgradeData.getStage2UpgradeCalls()); + + // TODO: here we should have tests that the bridging works for the previously deployed chains + // and that it does not work for those that did not upgrade. + // TODO: test that creation of new chains works under new conditions. + // TODO: if not hard, include test for deploying a gateway and migrating Era to it. + } + + /// @dev This is a contract that is used for additional visibility of transactions + /// that the decentralized governance should do. + function governanceMulticall(address governanceAddr, Call[] memory calls) internal { + // How the governance is implemented is out of scope here + vm.startBroadcast(governanceAddr); + + for (uint256 i = 0; i < calls.length; i++) { + Call memory call = calls[i]; + + (bool success, bytes memory data) = payable(call.target).call{value: call.value}(call.data); + require(success, "Multicall failed"); + } + + vm.stopBroadcast(); + } + + function mergeCalls(Call[] memory a, Call[] memory b) internal pure returns (Call[] memory result) { + result = new Call[](a.length + b.length); + for (uint256 i = 0; i < a.length; i++) { + result[i] = a[i]; + } + for (uint256 i = 0; i < b.length; i++) { + result[a.length + i] = b[i]; + } + } +} diff --git a/l1-contracts/test/foundry/l1/integration/_SharedZKChainDeployer.t.sol b/l1-contracts/test/foundry/l1/integration/_SharedZKChainDeployer.t.sol index 6220818ef..74a2f86b6 100644 --- a/l1-contracts/test/foundry/l1/integration/_SharedZKChainDeployer.t.sol +++ b/l1-contracts/test/foundry/l1/integration/_SharedZKChainDeployer.t.sol @@ -156,7 +156,6 @@ contract ZKChainDeployer is L1ContractDeployer { function _deployZkChain( uint256 _chainId, bytes32 _baseTokenAssetId, - address _sharedBridge, address _admin, uint256 _protocolVersion, bytes32 _storedBatchZero, @@ -178,7 +177,6 @@ contract ZKChainDeployer is L1ContractDeployer { bytes32(uint256(uint160(_admin))), bytes32(uint256(uint160(address(0x1337)))), _baseTokenAssetId, - bytes32(uint256(uint160(_sharedBridge))), _storedBatchZero, diamondCut.initCalldata ); diff --git a/l1-contracts/test/foundry/l1/integration/upgrade-envs/script-config/mainnet-era.toml b/l1-contracts/test/foundry/l1/integration/upgrade-envs/script-config/mainnet-era.toml new file mode 100644 index 000000000..d48013384 --- /dev/null +++ b/l1-contracts/test/foundry/l1/integration/upgrade-envs/script-config/mainnet-era.toml @@ -0,0 +1,7 @@ +owner_address = "0x4e4943346848c4867f81dfb37c4ca9c5715a7828" + +[chain] +chain_id = 324 +diamond_proxy_address = "0x32400084c286cf3e17e7b677ea9583e60a000324" +validium_mode = false +permanent_rollup = true diff --git a/l1-contracts/test/foundry/l1/integration/upgrade-envs/script-config/mainnet.toml b/l1-contracts/test/foundry/l1/integration/upgrade-envs/script-config/mainnet.toml new file mode 100644 index 000000000..abf681e37 --- /dev/null +++ b/l1-contracts/test/foundry/l1/integration/upgrade-envs/script-config/mainnet.toml @@ -0,0 +1,37 @@ +era_chain_id = 324 +owner_address = "8f7a9912416e8adc4d9c21fae1415d3318a11897" +testnet_verifier = false + +[contracts] +max_number_of_chains = 100 +create2_factory_salt = "0xde6b9c610417de5c775c1601c947f482e4f4e30c0f7b848c6d2b0554d76f607e" +validator_timelock_execution_delay = 0 +genesis_root = "0xf9030b78c5bf5ac997a76962aa32c90a6d8e8ebce9838c8eeb388d73e1f7659a" +genesis_rollup_leaf_index = 64 +genesis_batch_commitment = "0x34c1b220363e0cde7eaf10fe95754d61de097e0f9d9a1dc56c8026562e395259" +latest_protocol_version = "0x1900000000" +recursion_node_level_vk_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" +recursion_leaf_level_vk_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" +recursion_circuits_set_vks_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" +priority_tx_max_gas_limit = 72000000 +diamond_init_pubdata_pricing_mode = 0 +diamond_init_batch_overhead_l1_gas = 1000000 +diamond_init_max_pubdata_per_batch = 120000 +diamond_init_max_l2_gas_per_batch = 80000000 +diamond_init_priority_tx_max_pubdata = 99000 +diamond_init_minimal_l2_gas_price = 250000000 +bootloader_hash = "0x010008c753336bc8d1ddca235602b9f31d346412b2d463cd342899f7bfb73baf" +default_aa_hash = "0x0100055d760f11a3d737e7fd1816e600a4cd874a9f17f7a225d1f1c537c51a1e" +bridgehub_proxy_address = "0x303a465B659cBB0ab36eE643eA362c509EEb5213" +old_shared_bridge_proxy_address = "0xD7f9f54194C633F36CCD5F3da84ad4a1c38cB2cB" +state_transition_manager_address = "0xc2eE6b6af7d616f6e27ce7F4A451Aedc2b0F5f5C" +transparent_proxy_admin = "0xC2a36181fB524a6bEfE639aFEd37A67e77d62cf1" +era_diamond_proxy = "0x32400084c286cf3e17e7b677ea9583e60a000324" +blob_versioned_hash_retriever = "0x0000000000000000000000000000000000000001" +legacy_erc20_bridge_address = "0x57891966931eb4bb6fb81430e6ce0a03aabde063" +old_validator_timelock = "0x5D8ba173Dc6C3c90C8f7C04C9288BeF5FDbAd06E" +l2_bridge_proxy_owner_address = "0x0000000000000000000000000000000000000001" +l2_bridged_standard_erc20_proxy_owner_address = "0x0000000000000000000000000000000000000001" + +[tokens] +token_weth_address = "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2" diff --git a/l1-contracts/test/foundry/l1/integration/upgrade-envs/script-out/.gitkeep b/l1-contracts/test/foundry/l1/integration/upgrade-envs/script-out/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/l1-contracts/test/foundry/l1/unit/concrete/Bridgehub/experimental_bridge.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/Bridgehub/experimental_bridge.t.sol index d9675912a..6ffbbc0e9 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/Bridgehub/experimental_bridge.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/Bridgehub/experimental_bridge.t.sol @@ -439,9 +439,10 @@ contract ExperimentalBridgeTest is Test { ); if (randomAddress != address(testTokenAddress)) { + assetId = DataEncoding.encodeNTVAssetId(block.chainid, address(randomAddress)); + vm.assume(!bridgeHub.assetIdIsRegistered(assetId)); // Testing to see if a random address can also be added or not vm.prank(bridgeOwner); - assetId = DataEncoding.encodeNTVAssetId(block.chainid, address(randomAddress)); bridgeHub.addTokenAssetId(assetId); assertTrue(bridgeHub.assetIdIsRegistered(assetId)); } @@ -787,7 +788,6 @@ contract ExperimentalBridgeTest is Test { mockCTM.createNewChain.selector, chainId, tokenAssetId, - sharedBridgeAddress, admin, mockInitCalldata, factoryDeps diff --git a/l1-contracts/test/foundry/l1/unit/concrete/DiamondCut/UpgradeLogic.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/DiamondCut/UpgradeLogic.t.sol index 4645bcb2b..8823f20b9 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/DiamondCut/UpgradeLogic.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/DiamondCut/UpgradeLogic.t.sol @@ -84,7 +84,6 @@ contract UpgradeLogicTest is DiamondCutTest { admin: admin, validatorTimelock: makeAddr("validatorTimelock"), baseTokenAssetId: DataEncoding.encodeNTVAssetId(1, (makeAddr("baseToken"))), - baseTokenBridge: makeAddr("baseTokenBridge"), storedBatchZero: bytes32(0), // genesisBatchHash: 0x02c775f0a90abf7a0e8043f2fdc38f0580ca9f9996a895d05a501bfeaa3b2e21, // genesisIndexRepeatedStorageChanges: 0, diff --git a/l1-contracts/test/foundry/l1/unit/concrete/Executor/_Executor_Shared.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/Executor/_Executor_Shared.t.sol index b4d46e842..f44a93e72 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/Executor/_Executor_Shared.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/Executor/_Executor_Shared.t.sol @@ -150,6 +150,8 @@ contract ExecutorTest is Test { dummyBridgehub.setMessageRoot(address(messageRoot)); sharedBridge = new DummyEraBaseTokenBridge(); + dummyBridgehub.setSharedBridge(address(sharedBridge)); + vm.mockCall( address(messageRoot), abi.encodeWithSelector(MessageRoot.addChainBatchRoot.selector, 9, 1, bytes32(0)), @@ -197,7 +199,6 @@ contract ExecutorTest is Test { admin: owner, validatorTimelock: validator, baseTokenAssetId: DataEncoding.encodeNTVAssetId(block.chainid, ETH_TOKEN_ADDRESS), - baseTokenBridge: address(sharedBridge), storedBatchZero: keccak256(abi.encode(genesisStoredBatchInfo)), verifier: IVerifier(testnetVerifier), // verifier verifierParams: VerifierParams({ diff --git a/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol index c7eede8de..bcfe6ae2c 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/Governance/PermanentRestriction.t.sol @@ -369,17 +369,11 @@ contract PermanentRestrictionTest is ChainTypeManagerTest { vm.stopPrank(); address l1Nullifier = makeAddr("l1Nullifier"); - address l2LegacySharedBridge = makeAddr("l2LegacySharedBridge"); vm.mockCall( address(sharedBridge), abi.encodeWithSelector(IL1AssetRouter.L1_NULLIFIER.selector), abi.encode(l1Nullifier) ); - vm.mockCall( - address(l1Nullifier), - abi.encodeWithSelector(IL1Nullifier.l2BridgeAddress.selector), - abi.encode(l2LegacySharedBridge) - ); vm.startPrank(governor); bridgehub.createNewChain({ _chainId: chainId, diff --git a/l1-contracts/test/foundry/l1/unit/concrete/Utils/Utils.sol b/l1-contracts/test/foundry/l1/unit/concrete/Utils/Utils.sol index b19dcb3b3..3347e19ef 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/Utils/Utils.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/Utils/Utils.sol @@ -309,48 +309,47 @@ library Utils { } function getUtilsFacetSelectors() public pure returns (bytes4[] memory) { - bytes4[] memory selectors = new bytes4[](41); + bytes4[] memory selectors = new bytes4[](39); selectors[0] = UtilsFacet.util_setChainId.selector; selectors[1] = UtilsFacet.util_getChainId.selector; selectors[2] = UtilsFacet.util_setBridgehub.selector; selectors[3] = UtilsFacet.util_getBridgehub.selector; selectors[4] = UtilsFacet.util_setBaseToken.selector; selectors[5] = UtilsFacet.util_getBaseTokenAssetId.selector; - selectors[6] = UtilsFacet.util_setBaseTokenBridge.selector; - selectors[7] = UtilsFacet.util_getBaseTokenBridge.selector; - selectors[8] = UtilsFacet.util_setVerifier.selector; - selectors[9] = UtilsFacet.util_getVerifier.selector; - selectors[10] = UtilsFacet.util_setStoredBatchHashes.selector; - selectors[11] = UtilsFacet.util_getStoredBatchHashes.selector; - selectors[12] = UtilsFacet.util_setVerifierParams.selector; - selectors[13] = UtilsFacet.util_getVerifierParams.selector; - selectors[14] = UtilsFacet.util_setL2BootloaderBytecodeHash.selector; - selectors[15] = UtilsFacet.util_getL2BootloaderBytecodeHash.selector; - selectors[16] = UtilsFacet.util_setL2DefaultAccountBytecodeHash.selector; - selectors[17] = UtilsFacet.util_getL2DefaultAccountBytecodeHash.selector; - selectors[18] = UtilsFacet.util_setPendingAdmin.selector; - selectors[19] = UtilsFacet.util_getPendingAdmin.selector; - selectors[20] = UtilsFacet.util_setAdmin.selector; - selectors[21] = UtilsFacet.util_getAdmin.selector; - selectors[22] = UtilsFacet.util_setValidator.selector; - selectors[23] = UtilsFacet.util_getValidator.selector; - selectors[24] = UtilsFacet.util_setZkPorterAvailability.selector; - selectors[25] = UtilsFacet.util_getZkPorterAvailability.selector; - selectors[26] = UtilsFacet.util_setChainTypeManager.selector; - selectors[27] = UtilsFacet.util_getChainTypeManager.selector; - selectors[28] = UtilsFacet.util_setPriorityTxMaxGasLimit.selector; - selectors[29] = UtilsFacet.util_getPriorityTxMaxGasLimit.selector; - selectors[30] = UtilsFacet.util_setFeeParams.selector; - selectors[31] = UtilsFacet.util_getFeeParams.selector; - selectors[32] = UtilsFacet.util_setProtocolVersion.selector; - selectors[33] = UtilsFacet.util_getProtocolVersion.selector; - selectors[34] = UtilsFacet.util_setIsFrozen.selector; - selectors[35] = UtilsFacet.util_getIsFrozen.selector; - selectors[36] = UtilsFacet.util_setTransactionFilterer.selector; - selectors[37] = UtilsFacet.util_setBaseTokenGasPriceMultiplierDenominator.selector; - selectors[38] = UtilsFacet.util_setTotalBatchesExecuted.selector; - selectors[39] = UtilsFacet.util_setL2LogsRootHash.selector; - selectors[40] = UtilsFacet.util_setBaseTokenGasPriceMultiplierNominator.selector; + selectors[6] = UtilsFacet.util_setVerifier.selector; + selectors[7] = UtilsFacet.util_getVerifier.selector; + selectors[8] = UtilsFacet.util_setStoredBatchHashes.selector; + selectors[9] = UtilsFacet.util_getStoredBatchHashes.selector; + selectors[10] = UtilsFacet.util_setVerifierParams.selector; + selectors[11] = UtilsFacet.util_getVerifierParams.selector; + selectors[12] = UtilsFacet.util_setL2BootloaderBytecodeHash.selector; + selectors[13] = UtilsFacet.util_getL2BootloaderBytecodeHash.selector; + selectors[14] = UtilsFacet.util_setL2DefaultAccountBytecodeHash.selector; + selectors[15] = UtilsFacet.util_getL2DefaultAccountBytecodeHash.selector; + selectors[16] = UtilsFacet.util_setPendingAdmin.selector; + selectors[17] = UtilsFacet.util_getPendingAdmin.selector; + selectors[18] = UtilsFacet.util_setAdmin.selector; + selectors[19] = UtilsFacet.util_getAdmin.selector; + selectors[20] = UtilsFacet.util_setValidator.selector; + selectors[21] = UtilsFacet.util_getValidator.selector; + selectors[22] = UtilsFacet.util_setZkPorterAvailability.selector; + selectors[23] = UtilsFacet.util_getZkPorterAvailability.selector; + selectors[24] = UtilsFacet.util_setChainTypeManager.selector; + selectors[25] = UtilsFacet.util_getChainTypeManager.selector; + selectors[26] = UtilsFacet.util_setPriorityTxMaxGasLimit.selector; + selectors[27] = UtilsFacet.util_getPriorityTxMaxGasLimit.selector; + selectors[28] = UtilsFacet.util_setFeeParams.selector; + selectors[29] = UtilsFacet.util_getFeeParams.selector; + selectors[30] = UtilsFacet.util_setProtocolVersion.selector; + selectors[31] = UtilsFacet.util_getProtocolVersion.selector; + selectors[32] = UtilsFacet.util_setIsFrozen.selector; + selectors[33] = UtilsFacet.util_getIsFrozen.selector; + selectors[34] = UtilsFacet.util_setTransactionFilterer.selector; + selectors[35] = UtilsFacet.util_setBaseTokenGasPriceMultiplierDenominator.selector; + selectors[36] = UtilsFacet.util_setTotalBatchesExecuted.selector; + selectors[37] = UtilsFacet.util_setL2LogsRootHash.selector; + selectors[38] = UtilsFacet.util_setBaseTokenGasPriceMultiplierNominator.selector; + return selectors; } @@ -387,7 +386,6 @@ library Utils { admin: address(0x32149872498357874258787), validatorTimelock: address(0x85430237648403822345345), baseTokenAssetId: bytes32(uint256(0x923645439232223445)), - baseTokenBridge: address(0x23746765237749923040872834), storedBatchZero: bytes32(0), verifier: makeVerifier(testnetVerifier), verifierParams: makeVerifierParams(), diff --git a/l1-contracts/test/foundry/l1/unit/concrete/Utils/UtilsFacet.sol b/l1-contracts/test/foundry/l1/unit/concrete/Utils/UtilsFacet.sol index 0d141ce1f..08afae8c9 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/Utils/UtilsFacet.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/Utils/UtilsFacet.sol @@ -32,14 +32,6 @@ contract UtilsFacet is ZKChainBase { return s.baseTokenAssetId; } - function util_setBaseTokenBridge(address _baseTokenBridge) external { - s.baseTokenBridge = _baseTokenBridge; - } - - function util_getBaseTokenBridge() external view returns (address) { - return s.baseTokenBridge; - } - function util_setVerifier(IVerifier _verifier) external { s.verifier = _verifier; } diff --git a/l1-contracts/test/foundry/l1/unit/concrete/state-transition/ChainTypeManager/CreateNewChain.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/state-transition/ChainTypeManager/CreateNewChain.t.sol index 51a4bb521..c422dca99 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/state-transition/ChainTypeManager/CreateNewChain.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/state-transition/ChainTypeManager/CreateNewChain.t.sol @@ -33,7 +33,6 @@ contract createNewChainTest is ChainTypeManagerTest { chainContractAddress.createNewChain({ _chainId: chainId, _baseTokenAssetId: DataEncoding.encodeNTVAssetId(block.chainid, baseToken), - _assetRouter: sharedBridge, _admin: admin, _initData: abi.encode(abi.encode(initialDiamondCutData), bytes("")), _factoryDeps: new bytes[](0) diff --git a/l1-contracts/test/foundry/l1/unit/concrete/state-transition/ChainTypeManager/_ChainTypeManager_Shared.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/state-transition/ChainTypeManager/_ChainTypeManager_Shared.t.sol index 6f8f73b4a..5ecaa7407 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/state-transition/ChainTypeManager/_ChainTypeManager_Shared.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/state-transition/ChainTypeManager/_ChainTypeManager_Shared.t.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.21; import {Test} from "forge-std/Test.sol"; +import {console2 as console} from "forge-std/Script.sol"; import {TransparentUpgradeableProxy} from "@openzeppelin/contracts-v4/proxy/transparent/TransparentUpgradeableProxy.sol"; @@ -26,6 +27,9 @@ import {TestnetVerifier} from "contracts/state-transition/TestnetVerifier.sol"; import {DummyBridgehub} from "contracts/dev-contracts/test/DummyBridgehub.sol"; import {DataEncoding} from "contracts/common/libraries/DataEncoding.sol"; import {ZeroAddress} from "contracts/common/L1ContractErrors.sol"; +import {ICTMDeploymentTracker} from "contracts/bridgehub/ICTMDeploymentTracker.sol"; +import {IMessageRoot} from "contracts/bridgehub/IMessageRoot.sol"; +import {L1AssetRouter} from "contracts/bridge/asset-router/L1AssetRouter.sol"; import {RollupL1DAValidator} from "da-contracts/RollupL1DAValidator.sol"; contract ChainTypeManagerTest is Test { @@ -51,6 +55,15 @@ contract ChainTypeManagerTest is Test { function deploy() public { bridgehub = new Bridgehub(block.chainid, governor, type(uint256).max); + vm.prank(governor); + bridgehub.setAddresses(sharedBridge, ICTMDeploymentTracker(address(0)), IMessageRoot(address(0))); + + vm.mockCall( + address(sharedBridge), + abi.encodeCall(L1AssetRouter.l2BridgeAddress, (chainId)), + abi.encode(makeAddr("l2BridgeAddress")) + ); + newChainAdmin = makeAddr("chainadmin"); vm.startPrank(address(bridgehub)); @@ -166,7 +179,6 @@ contract ChainTypeManagerTest is Test { chainContractAddress.createNewChain({ _chainId: chainId, _baseTokenAssetId: DataEncoding.encodeNTVAssetId(block.chainid, baseToken), - _assetRouter: sharedBridge, _admin: newChainAdmin, _initData: abi.encode(abi.encode(_diamondCut), bytes("")), _factoryDeps: new bytes[](0) diff --git a/l1-contracts/test/foundry/l1/unit/concrete/state-transition/chain-deps/DiamondInit/Initialize.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/state-transition/chain-deps/DiamondInit/Initialize.t.sol index cfc826fa5..0b7dfbbe9 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/state-transition/chain-deps/DiamondInit/Initialize.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/state-transition/chain-deps/DiamondInit/Initialize.t.sol @@ -86,7 +86,6 @@ contract InitializeTest is DiamondInitTest { assertEq(utilsFacet.util_getBridgehub(), initializeData.bridgehub); assertEq(utilsFacet.util_getChainTypeManager(), initializeData.chainTypeManager); assertEq(utilsFacet.util_getBaseTokenAssetId(), initializeData.baseTokenAssetId); - assertEq(utilsFacet.util_getBaseTokenBridge(), initializeData.baseTokenBridge); assertEq(utilsFacet.util_getProtocolVersion(), initializeData.protocolVersion); assertEq(address(utilsFacet.util_getVerifier()), address(initializeData.verifier)); diff --git a/l1-contracts/test/foundry/l1/unit/concrete/state-transition/chain-deps/facets/Getters/GetBaseTokenBridge.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/state-transition/chain-deps/facets/Getters/GetBaseTokenBridge.t.sol deleted file mode 100644 index db32ca6bd..000000000 --- a/l1-contracts/test/foundry/l1/unit/concrete/state-transition/chain-deps/facets/Getters/GetBaseTokenBridge.t.sol +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity 0.8.24; - -import {GettersFacetTest} from "./_Getters_Shared.t.sol"; - -contract GetBaseTokenBridgeTest is GettersFacetTest { - function test() public { - address expected = makeAddr("baseTokenBride"); - gettersFacetWrapper.util_setBaseTokenBridge(expected); - - address received = gettersFacet.getBaseTokenBridge(); - - assertEq(expected, received, "BaseTokenBridge address is incorrect"); - } -} diff --git a/l1-contracts/test/foundry/l1/unit/concrete/state-transition/chain-deps/facets/Getters/_Getters_Shared.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/state-transition/chain-deps/facets/Getters/_Getters_Shared.t.sol index 557378c63..9f66926e7 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/state-transition/chain-deps/facets/Getters/_Getters_Shared.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/state-transition/chain-deps/facets/Getters/_Getters_Shared.t.sol @@ -36,10 +36,6 @@ contract GettersFacetWrapper is GettersFacet { s.baseTokenAssetId = _baseTokenAssetId; } - function util_setBaseTokenBridge(address _baseTokenBridge) external { - s.baseTokenBridge = _baseTokenBridge; - } - function util_setTotalBatchesCommitted(uint256 _totalBatchesCommitted) external { s.totalBatchesCommitted = _totalBatchesCommitted; } diff --git a/l1-contracts/test/foundry/l1/unit/concrete/state-transition/chain-deps/facets/Mailbox/FinalizeWithdrawal.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/state-transition/chain-deps/facets/Mailbox/FinalizeWithdrawal.t.sol index c71721c79..5e7fa27f6 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/state-transition/chain-deps/facets/Mailbox/FinalizeWithdrawal.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/state-transition/chain-deps/facets/Mailbox/FinalizeWithdrawal.t.sol @@ -9,6 +9,7 @@ import {IBridgehub} from "contracts/bridgehub/IBridgehub.sol"; import {IL1AssetRouter} from "contracts/bridge/asset-router/IL1AssetRouter.sol"; import {DummySharedBridge} from "contracts/dev-contracts/test/DummySharedBridge.sol"; import {OnlyEraSupported} from "contracts/common/L1ContractErrors.sol"; +import {Bridgehub} from "contracts/bridgehub/Bridgehub.sol"; contract MailboxFinalizeWithdrawal is MailboxTest { bytes32[] proof; @@ -22,6 +23,8 @@ contract MailboxFinalizeWithdrawal is MailboxTest { L1AssetRouter = new DummySharedBridge(keccak256("dummyDepositHash")); baseTokenBridgeAddress = address(L1AssetRouter); + vm.mockCall(bridgehub, abi.encodeCall(Bridgehub.sharedBridge, ()), abi.encode(baseTokenBridgeAddress)); + proof = new bytes32[](0); message = "message"; } @@ -40,9 +43,7 @@ contract MailboxFinalizeWithdrawal is MailboxTest { } function test_success_withdrawal(uint256 amount) public { - address baseTokenBridge = makeAddr("baseTokenBridge"); utilsFacet.util_setChainId(eraChainId); - utilsFacet.util_setBaseTokenBridge(baseTokenBridgeAddress); address l1Receiver = makeAddr("receiver"); address l1Token = address(1); diff --git a/l1-contracts/test/foundry/l1/unit/concrete/state-transition/chain-deps/facets/Mailbox/RequestL2Transaction.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/state-transition/chain-deps/facets/Mailbox/RequestL2Transaction.t.sol index 85bcd8be8..0ea16b46c 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/state-transition/chain-deps/facets/Mailbox/RequestL2Transaction.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/state-transition/chain-deps/facets/Mailbox/RequestL2Transaction.t.sol @@ -11,6 +11,7 @@ import {FeeParams, PubdataPricingMode} from "contracts/state-transition/chain-de import {IL1AssetRouter} from "contracts/bridge/asset-router/IL1AssetRouter.sol"; import {DummySharedBridge} from "contracts/dev-contracts/test/DummySharedBridge.sol"; import {OnlyEraSupported, TooManyFactoryDeps, MsgValueTooLow, GasPerPubdataMismatch} from "contracts/common/L1ContractErrors.sol"; +import {Bridgehub} from "contracts/bridgehub/Bridgehub.sol"; contract MailboxRequestL2TransactionTest is MailboxTest { address tempAddress; @@ -24,6 +25,7 @@ contract MailboxRequestL2TransactionTest is MailboxTest { l1SharedBridge = new DummySharedBridge(keccak256("dummyDepositHash")); baseTokenBridgeAddress = address(l1SharedBridge); + vm.mockCall(bridgehub, abi.encodeCall(Bridgehub.sharedBridge, ()), abi.encode(baseTokenBridgeAddress)); tempAddress = makeAddr("temp"); tempBytesArr = new bytes[](0); @@ -122,7 +124,6 @@ contract MailboxRequestL2TransactionTest is MailboxTest { function test_RevertWhen_bridgePaused(uint256 randomValue) public { utilsFacet.util_setBaseTokenGasPriceMultiplierDenominator(1); utilsFacet.util_setPriorityTxMaxGasLimit(100000000); - utilsFacet.util_setBaseTokenBridge(baseTokenBridgeAddress); uint256 l2GasLimit = 1000000; uint256 baseCost = mailboxFacet.l2TransactionBaseCost(10000000, l2GasLimit, REQUIRED_L2_GAS_PRICE_PER_PUBDATA); @@ -137,7 +138,6 @@ contract MailboxRequestL2TransactionTest is MailboxTest { function test_success_requestL2Transaction(uint256 randomValue) public { utilsFacet.util_setBaseTokenGasPriceMultiplierDenominator(1); utilsFacet.util_setPriorityTxMaxGasLimit(100000000); - utilsFacet.util_setBaseTokenBridge(baseTokenBridgeAddress); uint256 l2GasLimit = 1000000; uint256 baseCost = mailboxFacet.l2TransactionBaseCost(10000000, l2GasLimit, REQUIRED_L2_GAS_PRICE_PER_PUBDATA); diff --git a/l1-contracts/test/foundry/l1/unit/concrete/state-transition/chain-deps/facets/Mailbox/_Mailbox_Shared.t.sol b/l1-contracts/test/foundry/l1/unit/concrete/state-transition/chain-deps/facets/Mailbox/_Mailbox_Shared.t.sol index b1ef215d8..37755b08e 100644 --- a/l1-contracts/test/foundry/l1/unit/concrete/state-transition/chain-deps/facets/Mailbox/_Mailbox_Shared.t.sol +++ b/l1-contracts/test/foundry/l1/unit/concrete/state-transition/chain-deps/facets/Mailbox/_Mailbox_Shared.t.sol @@ -21,9 +21,11 @@ contract MailboxTest is Test { uint256 constant eraChainId = 9; address internal testnetVerifier = address(new TestnetVerifier()); address diamondProxy; + address bridgehub; function setupDiamondProxy() public virtual { sender = makeAddr("sender"); + bridgehub = makeAddr("bridgehub"); vm.deal(sender, 100 ether); Diamond.FacetCut[] memory facetCuts = new Diamond.FacetCut[](3); @@ -50,6 +52,8 @@ contract MailboxTest is Test { mailboxFacet = IMailbox(diamondProxy); utilsFacet = UtilsFacet(diamondProxy); gettersFacet = IGetters(diamondProxy); + + utilsFacet.util_setBridgehub(bridgehub); } // add this to be excluded from coverage report diff --git a/system-contracts/SystemContractsHashes.json b/system-contracts/SystemContractsHashes.json index 8b0feaf78..3f351ca43 100644 --- a/system-contracts/SystemContractsHashes.json +++ b/system-contracts/SystemContractsHashes.json @@ -3,49 +3,49 @@ "contractName": "AccountCodeStorage", "bytecodePath": "artifacts-zk/contracts-preprocessed/AccountCodeStorage.sol/AccountCodeStorage.json", "sourceCodePath": "contracts-preprocessed/AccountCodeStorage.sol", - "bytecodeHash": "0x0100005d7a6c264bffa97a66a5ba8daff43b8a34e5ab7fab4e2ddf74f0a10e4c", + "bytecodeHash": "0x0100005d3ae95fb62791ed4693e614755bd780011ffc3d2dea8344fb1284f9df", "sourceCodeHash": "0x2e0e09d57a04bd1e722d8bf8c6423fdf3f8bca44e5e8c4f6684f987794be066e" }, { "contractName": "BootloaderUtilities", "bytecodePath": "artifacts-zk/contracts-preprocessed/BootloaderUtilities.sol/BootloaderUtilities.json", "sourceCodePath": "contracts-preprocessed/BootloaderUtilities.sol", - "bytecodeHash": "0x010007c7b6bd43d607e55f594e743394b7ae6288ac7f6caad8a7904b6c990e32", + "bytecodeHash": "0x010007c7daac9a547e1e20ed650b09b21668c3fb49e23cea5113dd8cb224b9ac", "sourceCodeHash": "0x0f1213c4b95acb71f4ab5d4082cc1aeb2bd5017e1cccd46afc66e53268609d85" }, { "contractName": "ComplexUpgrader", "bytecodePath": "artifacts-zk/contracts-preprocessed/ComplexUpgrader.sol/ComplexUpgrader.json", "sourceCodePath": "contracts-preprocessed/ComplexUpgrader.sol", - "bytecodeHash": "0x0100004d8c1520bc212778de21b5df751968cbbf1690ddd362ea7ab844ec0b1d", + "bytecodeHash": "0x0100004de3ddc8c5296fed145323ba1d6874af80469e40657bf0ff79d113ccb5", "sourceCodeHash": "0x796046a914fb676ba2bbd337b2924311ee2177ce54571c18a2c3945755c83614" }, { "contractName": "Compressor", "bytecodePath": "artifacts-zk/contracts-preprocessed/Compressor.sol/Compressor.json", "sourceCodePath": "contracts-preprocessed/Compressor.sol", - "bytecodeHash": "0x0100014ba4bf3056bc1d10e64986c71972db9056c879eed689163f7c91bb596f", + "bytecodeHash": "0x0100014b3784efd0fbc6825fa84f3dcf9fc1dcbed37a681c57098c347527ba21", "sourceCodeHash": "0x7240b5fb2ea8e184522e731fb14f764ebae52b8a69d1870a55daedac9a3ed617" }, { "contractName": "ContractDeployer", "bytecodePath": "artifacts-zk/contracts-preprocessed/ContractDeployer.sol/ContractDeployer.json", "sourceCodePath": "contracts-preprocessed/ContractDeployer.sol", - "bytecodeHash": "0x010004e5ad1de716de961c9e52e3b1d6219709891024e5b28d8cde96fe7fdc69", + "bytecodeHash": "0x010004e522c95733920e0a1f072b5dc36dd3d6a1b30515de48423575c10a8f7e", "sourceCodeHash": "0x92bc09da23ed9d86ba7a84f0dbf48503c99582ae58cdbebbdcc5f14ea1fcf014" }, { "contractName": "Create2Factory", "bytecodePath": "artifacts-zk/contracts-preprocessed/Create2Factory.sol/Create2Factory.json", "sourceCodePath": "contracts-preprocessed/Create2Factory.sol", - "bytecodeHash": "0x01000049bae223f356480d01ad05099bad8cfc3e0a91e206ae5dd72abb187cb1", + "bytecodeHash": "0x01000049a4e4beb07895adcdcf34186af3d28a9f3c1d9b56c72c8464730755f1", "sourceCodeHash": "0x114d9322a9ca654989f3e0b3b21f1311dbc4db84f443d054cd414f6414d84de3" }, { "contractName": "DefaultAccount", "bytecodePath": "artifacts-zk/contracts-preprocessed/DefaultAccount.sol/DefaultAccount.json", "sourceCodePath": "contracts-preprocessed/DefaultAccount.sol", - "bytecodeHash": "0x0100055d760f11a3d737e7fd1816e600a4cd874a9f17f7a225d1f1c537c51a1e", + "bytecodeHash": "0x0100055d3993e14104994ca4d8cfa91beb9b544ee86894b45708b4824d832ff2", "sourceCodeHash": "0xebffe840ebbd9329edb1ebff8ca50f6935e7dabcc67194a896fcc2e968d46dfb" }, { @@ -59,63 +59,77 @@ "contractName": "ImmutableSimulator", "bytecodePath": "artifacts-zk/contracts-preprocessed/ImmutableSimulator.sol/ImmutableSimulator.json", "sourceCodePath": "contracts-preprocessed/ImmutableSimulator.sol", - "bytecodeHash": "0x01000039fcb0afdc4480b9a83e0a5cfc2dbc55dce4f6f1d6363778b4e9371ca9", + "bytecodeHash": "0x010000394846da43b9adfe72f0820c19d39daaf861e2eae55d6fe248840f641e", "sourceCodeHash": "0x9659e69f7db09e8f60a8bb95314b1ed26afcc689851665cf27f5408122f60c98" }, { "contractName": "KnownCodesStorage", "bytecodePath": "artifacts-zk/contracts-preprocessed/KnownCodesStorage.sol/KnownCodesStorage.json", "sourceCodePath": "contracts-preprocessed/KnownCodesStorage.sol", - "bytecodeHash": "0x0100006f2889f3200b41f87f8e0835f970e47e513548bbf68239577f8bd97816", + "bytecodeHash": "0x0100006f1fa761c40d5b3325482c8bc9a577ac65278b624523b67eb99cf7e51c", "sourceCodeHash": "0xb39b5b81168653e0c5062f7b8e1d6d15a4e186df3317f192f0cb2fc3a74f5448" }, { "contractName": "L1Messenger", "bytecodePath": "artifacts-zk/contracts-preprocessed/L1Messenger.sol/L1Messenger.json", "sourceCodePath": "contracts-preprocessed/L1Messenger.sol", - "bytecodeHash": "0x010001f735b209b666e9e3d377cf7b5c0483a57dff7b1cd035b58ac10c2e0771", + "bytecodeHash": "0x010001f74edfe69d83816cc586cbb42b2a37d2649dcd1cab88052a37dffaadf3", "sourceCodeHash": "0x8d22a4019347a45cb0c27bed9e98f7033637a7bdcd90fafb1922caa48f2b05de" }, { "contractName": "L2BaseToken", "bytecodePath": "artifacts-zk/contracts-preprocessed/L2BaseToken.sol/L2BaseToken.json", "sourceCodePath": "contracts-preprocessed/L2BaseToken.sol", - "bytecodeHash": "0x010001036ff04ddfde50fac4cf41bf9a34df472373e8b2769938cb35d293f7a7", + "bytecodeHash": "0x0100010395d69e52583bf981c6eb16a7f45a4e930671c69df04e24c58dba3648", "sourceCodeHash": "0x8bdd2b4d0b53dba84c9f0af250bbaa2aad10b3de6747bba957f0bd3721090dfa" }, + { + "contractName": "L2GatewayUpgrade", + "bytecodePath": "artifacts-zk/contracts-preprocessed/L2GatewayUpgrade.sol/L2GatewayUpgrade.json", + "sourceCodePath": "contracts-preprocessed/L2GatewayUpgrade.sol", + "bytecodeHash": "0x0100019d6f999ca8393f86be0b24f494bf647cc3ec56e01eed6aa233c6a0e6c0", + "sourceCodeHash": "0x3b85c44fc4fdc422c43d8bf8a2b8229665698996a034bf37c895e963f6c839bf" + }, + { + "contractName": "L2GatewayUpgradeHelper", + "bytecodePath": "artifacts-zk/contracts-preprocessed/L2GatewayUpgradeHelper.sol/L2GatewayUpgradeHelper.json", + "sourceCodePath": "contracts-preprocessed/L2GatewayUpgradeHelper.sol", + "bytecodeHash": "0x01000007a010cd0e6cd1a9fcb802ccc92eb7a1acfb6566864ff6429a2c0ddf0a", + "sourceCodeHash": "0xe7bdc91f70cd5c395435423c25dd9cd27a99e19e12965854106cdc1385bcf14d" + }, { "contractName": "L2GenesisUpgrade", "bytecodePath": "artifacts-zk/contracts-preprocessed/L2GenesisUpgrade.sol/L2GenesisUpgrade.json", "sourceCodePath": "contracts-preprocessed/L2GenesisUpgrade.sol", - "bytecodeHash": "0x01000109946aaf60780aa88277365bdd6bc2719f5a2592079a91aae7058cee58", - "sourceCodeHash": "0xbfe430d992d5740c4befdc7adbac2bb9a33c25a45c30ed9fe86c2b4e0263778a" + "bytecodeHash": "0x010000fb0d40ab01a12ce7bad289cb44ab49563e51e6e1d889d431492a4b320f", + "sourceCodeHash": "0xdd7a89c11b624282aed07ef9d985d2fd006015a0aa0b11aec8e445695f7dd689" }, { "contractName": "MsgValueSimulator", "bytecodePath": "artifacts-zk/contracts-preprocessed/MsgValueSimulator.sol/MsgValueSimulator.json", "sourceCodePath": "contracts-preprocessed/MsgValueSimulator.sol", - "bytecodeHash": "0x0100005dc9890cf9aca1c56e823e2147619ea5058c70e123a6a76e51bcee8956", + "bytecodeHash": "0x0100005d895eb5ad625c93a99c3796c18e8fda2e34e9af6997c5208aea197cc2", "sourceCodeHash": "0x082f3dcbc2fe4d93706c86aae85faa683387097d1b676e7ebd00f71ee0f13b71" }, { "contractName": "NonceHolder", "bytecodePath": "artifacts-zk/contracts-preprocessed/NonceHolder.sol/NonceHolder.json", "sourceCodePath": "contracts-preprocessed/NonceHolder.sol", - "bytecodeHash": "0x010000d9ff72782330c259f50fc3ff63e5f8b0a2277e6d652c2a60c56e60efec", + "bytecodeHash": "0x010000d9515266d6a161d41fe0789fe8e75a31a3d8c0ce915ed09fa4ffcd7c61", "sourceCodeHash": "0xcd0c0366effebf2c98c58cf96322cc242a2d1c675620ef5514b7ed1f0a869edc" }, { "contractName": "PubdataChunkPublisher", "bytecodePath": "artifacts-zk/contracts-preprocessed/PubdataChunkPublisher.sol/PubdataChunkPublisher.json", "sourceCodePath": "contracts-preprocessed/PubdataChunkPublisher.sol", - "bytecodeHash": "0x0100004994ca7f560b82e531240c2bac414d02180c33f75363de4edc00796c15", + "bytecodeHash": "0x010000491ab9335e1a112a136580f1f1b2c2929d11be12aed00fd94925bc3fc1", "sourceCodeHash": "0x04d3d2e4019081c87aae5c22a060d84ae2e9d631ebce59801ecce37b9c87e4c7" }, { "contractName": "SystemContext", "bytecodePath": "artifacts-zk/contracts-preprocessed/SystemContext.sol/SystemContext.json", "sourceCodePath": "contracts-preprocessed/SystemContext.sol", - "bytecodeHash": "0x010001a7e10cfa64a3b326897067f582f992db9fbb9bf50304d5db6525de961d", + "bytecodeHash": "0x010001a7baaabeaf80186c0dec134f606186fa9f73f91e753806b424ca8f171f", "sourceCodeHash": "0xb3b8c1f57928938ac590984442bc96c2c888282793014845d5ce2f90bbf2677f" }, { diff --git a/system-contracts/contracts/L2GatewayUpgrade.sol b/system-contracts/contracts/L2GatewayUpgrade.sol new file mode 100644 index 000000000..59d96102d --- /dev/null +++ b/system-contracts/contracts/L2GatewayUpgrade.sol @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.24; + +import {DEPLOYER_SYSTEM_CONTRACT} from "./Constants.sol"; +import {IContractDeployer, ForceDeployment} from "./interfaces/IContractDeployer.sol"; +import {SystemContractHelper} from "./libraries/SystemContractHelper.sol"; +import {FixedForceDeploymentsData, ZKChainSpecificForceDeploymentsData} from "./interfaces/IL2GenesisUpgrade.sol"; + +import {L2GatewayUpgradeHelper} from "./L2GatewayUpgradeHelper.sol"; +import {ITransparentUpgradeableProxy} from "@openzeppelin/contracts-v4/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {IL2SharedBridgeLegacy} from "./interfaces/IL2SharedBridgeLegacy.sol"; +import {UpgradeableBeacon} from "@openzeppelin/contracts-v4/proxy/beacon/UpgradeableBeacon.sol"; + +/// @custom:security-contact security@matterlabs.dev +/// @author Matter Labs +/// @notice The contract that is used for facilitating the upgrade of the L2 +/// to the protocol version that supports gateway +/// @dev This contract is neither predeployed nor a system contract. It is located +/// in this folder due to very overlapping functionality with `L2GenesisUpgrade` and +/// facilitating reusage of the code. +/// @dev During the upgrade, it will be delegate-called by the `ComplexUpgrader` contract. +contract L2GatewayUpgrade { + function upgrade( + ForceDeployment[] calldata _forceDeployments, + address _ctmDeployer, + bytes calldata _fixedForceDeploymentsData, + bytes calldata _additionalForceDeploymentsData + ) external payable { + // Firstly, we force deploy the main set of contracts. + // Those will be deployed without any contract invocation. + IContractDeployer(DEPLOYER_SYSTEM_CONTRACT).forceDeployOnAddresses{value: msg.value}(_forceDeployments); + + // Secondly, we perform the more complex deployment of the gateway contracts. + L2GatewayUpgradeHelper.performGatewayContractsInit( + _ctmDeployer, + _fixedForceDeploymentsData, + _additionalForceDeploymentsData + ); + + ZKChainSpecificForceDeploymentsData memory additionalForceDeploymentsData = abi.decode( + _additionalForceDeploymentsData, + (ZKChainSpecificForceDeploymentsData) + ); + + address l2LegacyBridgeAddress = additionalForceDeploymentsData.l2LegacySharedBridge; + + if (l2LegacyBridgeAddress != address(0)) { + FixedForceDeploymentsData memory fixedForceDeploymentsData = abi.decode( + _fixedForceDeploymentsData, + (FixedForceDeploymentsData) + ); + + // Firstly, upgrade the legacy L2SharedBridge + bytes memory bridgeUpgradeData = abi.encodeCall( + ITransparentUpgradeableProxy.upgradeTo, + (fixedForceDeploymentsData.l2SharedBridgeLegacyImpl) + ); + SystemContractHelper.mimicCallWithPropagatedRevert( + l2LegacyBridgeAddress, + fixedForceDeploymentsData.l2BridgeProxyOwnerAddress, + bridgeUpgradeData + ); + + // Secondly, upgrade the tokens + UpgradeableBeacon upgradableBeacon = IL2SharedBridgeLegacy(l2LegacyBridgeAddress).l2TokenBeacon(); + bytes memory beaconUpgradeData = abi.encodeCall( + UpgradeableBeacon.upgradeTo, + (fixedForceDeploymentsData.l2BridgedStandardERC20Impl) + ); + SystemContractHelper.mimicCallWithPropagatedRevert( + address(upgradableBeacon), + fixedForceDeploymentsData.l2BridgedStandardERC20ProxyOwnerAddress, + beaconUpgradeData + ); + } + } +} diff --git a/system-contracts/contracts/L2GatewayUpgradeHelper.sol b/system-contracts/contracts/L2GatewayUpgradeHelper.sol new file mode 100644 index 000000000..e5004bc8a --- /dev/null +++ b/system-contracts/contracts/L2GatewayUpgradeHelper.sol @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.24; + +import {DEPLOYER_SYSTEM_CONTRACT, L2_BRIDGE_HUB, L2_ASSET_ROUTER, L2_MESSAGE_ROOT, L2_NATIVE_TOKEN_VAULT_ADDR} from "./Constants.sol"; +import {IContractDeployer, ForceDeployment} from "./interfaces/IContractDeployer.sol"; +import {SystemContractHelper} from "./libraries/SystemContractHelper.sol"; +import {FixedForceDeploymentsData, ZKChainSpecificForceDeploymentsData} from "./interfaces/IL2GenesisUpgrade.sol"; + +library L2GatewayUpgradeHelper { + function performGatewayContractsInit( + address _ctmDeployer, + bytes calldata _fixedForceDeploymentsData, + bytes calldata _additionalForceDeploymentsData + ) internal { + ForceDeployment[] memory forceDeployments = _getForceDeploymentsData( + _fixedForceDeploymentsData, + _additionalForceDeploymentsData + ); + IContractDeployer(DEPLOYER_SYSTEM_CONTRACT).forceDeployOnAddresses{value: msg.value}(forceDeployments); + + // It is expected that either via to the force deployments above + // or upon init both the L2 deployment of Bridgehub, AssetRouter and MessageRoot are deployed. + // (The comment does not mention the exact order in case it changes) + // However, there is still some follow up finalization that needs to be done. + + address bridgehubOwner = L2_BRIDGE_HUB.owner(); + + bytes memory data = abi.encodeCall( + L2_BRIDGE_HUB.setAddresses, + (L2_ASSET_ROUTER, _ctmDeployer, address(L2_MESSAGE_ROOT)) + ); + + (bool success, bytes memory returnData) = SystemContractHelper.mimicCall( + address(L2_BRIDGE_HUB), + bridgehubOwner, + data + ); + if (!success) { + // Progapatate revert reason + assembly { + revert(add(returnData, 0x20), returndatasize()) + } + } + } + + function _getForceDeploymentsData( + bytes calldata _fixedForceDeploymentsData, + bytes calldata _additionalForceDeploymentsData + ) internal view returns (ForceDeployment[] memory forceDeployments) { + FixedForceDeploymentsData memory fixedForceDeploymentsData = abi.decode( + _fixedForceDeploymentsData, + (FixedForceDeploymentsData) + ); + ZKChainSpecificForceDeploymentsData memory additionalForceDeploymentsData = abi.decode( + _additionalForceDeploymentsData, + (ZKChainSpecificForceDeploymentsData) + ); + + forceDeployments = new ForceDeployment[](4); + + forceDeployments[0] = ForceDeployment({ + bytecodeHash: fixedForceDeploymentsData.messageRootBytecodeHash, + newAddress: address(L2_MESSAGE_ROOT), + callConstructor: true, + value: 0, + // solhint-disable-next-line func-named-parameters + input: abi.encode(address(L2_BRIDGE_HUB)) + }); + + forceDeployments[1] = ForceDeployment({ + bytecodeHash: fixedForceDeploymentsData.bridgehubBytecodeHash, + newAddress: address(L2_BRIDGE_HUB), + callConstructor: true, + value: 0, + input: abi.encode( + fixedForceDeploymentsData.l1ChainId, + fixedForceDeploymentsData.aliasedL1Governance, + fixedForceDeploymentsData.maxNumberOfZKChains + ) + }); + + forceDeployments[2] = ForceDeployment({ + bytecodeHash: fixedForceDeploymentsData.l2AssetRouterBytecodeHash, + newAddress: address(L2_ASSET_ROUTER), + callConstructor: true, + value: 0, + // solhint-disable-next-line func-named-parameters + input: abi.encode( + fixedForceDeploymentsData.l1ChainId, + fixedForceDeploymentsData.eraChainId, + fixedForceDeploymentsData.l1AssetRouter, + additionalForceDeploymentsData.l2LegacySharedBridge, + additionalForceDeploymentsData.baseTokenAssetId, + fixedForceDeploymentsData.aliasedL1Governance + ) + }); + + forceDeployments[3] = ForceDeployment({ + bytecodeHash: fixedForceDeploymentsData.l2NtvBytecodeHash, + newAddress: L2_NATIVE_TOKEN_VAULT_ADDR, + callConstructor: true, + value: 0, + // solhint-disable-next-line func-named-parameters + input: abi.encode( + fixedForceDeploymentsData.l1ChainId, + fixedForceDeploymentsData.aliasedL1Governance, + fixedForceDeploymentsData.l2TokenProxyBytecodeHash, + additionalForceDeploymentsData.l2LegacySharedBridge, + address(0), // this is used if the contract were already deployed, so for the migration of Era. + false, + additionalForceDeploymentsData.l2Weth, + additionalForceDeploymentsData.baseTokenAssetId + ) + }); + } +} diff --git a/system-contracts/contracts/L2GenesisUpgrade.sol b/system-contracts/contracts/L2GenesisUpgrade.sol index 35d03b648..4d5592529 100644 --- a/system-contracts/contracts/L2GenesisUpgrade.sol +++ b/system-contracts/contracts/L2GenesisUpgrade.sol @@ -2,11 +2,11 @@ pragma solidity 0.8.24; -import {DEPLOYER_SYSTEM_CONTRACT, SYSTEM_CONTEXT_CONTRACT, L2_BRIDGE_HUB, L2_ASSET_ROUTER, L2_MESSAGE_ROOT, L2_NATIVE_TOKEN_VAULT_ADDR} from "./Constants.sol"; -import {IContractDeployer, ForceDeployment} from "./interfaces/IContractDeployer.sol"; -import {SystemContractHelper} from "./libraries/SystemContractHelper.sol"; +import {SYSTEM_CONTEXT_CONTRACT} from "./Constants.sol"; import {ISystemContext} from "./interfaces/ISystemContext.sol"; -import {IL2GenesisUpgrade, FixedForceDeploymentsData, ZKChainSpecificForceDeploymentsData} from "./interfaces/IL2GenesisUpgrade.sol"; +import {IL2GenesisUpgrade} from "./interfaces/IL2GenesisUpgrade.sol"; + +import {L2GatewayUpgradeHelper} from "./L2GatewayUpgradeHelper.sol"; /// @custom:security-contact security@matterlabs.dev /// @author Matter Labs @@ -21,107 +21,13 @@ contract L2GenesisUpgrade is IL2GenesisUpgrade { // solhint-disable-next-line gas-custom-errors require(_chainId != 0, "Invalid chainId"); ISystemContext(SYSTEM_CONTEXT_CONTRACT).setChainId(_chainId); - ForceDeployment[] memory forceDeployments = _getForceDeploymentsData( + + L2GatewayUpgradeHelper.performGatewayContractsInit( + _ctmDeployer, _fixedForceDeploymentsData, _additionalForceDeploymentsData ); - IContractDeployer(DEPLOYER_SYSTEM_CONTRACT).forceDeployOnAddresses{value: msg.value}(forceDeployments); - - // It is expected that either via to the force deployments above - // or upon init both the L2 deployment of Bridgehub, AssetRouter and MessageRoot are deployed. - // (The comment does not mention the exact order in case it changes) - // However, there is still some follow up finalization that needs to be done. - - address bridgehubOwner = L2_BRIDGE_HUB.owner(); - - bytes memory data = abi.encodeCall( - L2_BRIDGE_HUB.setAddresses, - (L2_ASSET_ROUTER, _ctmDeployer, address(L2_MESSAGE_ROOT)) - ); - - (bool success, bytes memory returnData) = SystemContractHelper.mimicCall( - address(L2_BRIDGE_HUB), - bridgehubOwner, - data - ); - if (!success) { - // Progapatate revert reason - assembly { - revert(add(returnData, 0x20), returndatasize()) - } - } emit UpgradeComplete(_chainId); } - - function _getForceDeploymentsData( - bytes calldata _fixedForceDeploymentsData, - bytes calldata _additionalForceDeploymentsData - ) internal view returns (ForceDeployment[] memory forceDeployments) { - FixedForceDeploymentsData memory fixedForceDeploymentsData = abi.decode( - _fixedForceDeploymentsData, - (FixedForceDeploymentsData) - ); - ZKChainSpecificForceDeploymentsData memory additionalForceDeploymentsData = abi.decode( - _additionalForceDeploymentsData, - (ZKChainSpecificForceDeploymentsData) - ); - - forceDeployments = new ForceDeployment[](4); - - forceDeployments[0] = ForceDeployment({ - bytecodeHash: fixedForceDeploymentsData.messageRootBytecodeHash, - newAddress: address(L2_MESSAGE_ROOT), - callConstructor: true, - value: 0, - // solhint-disable-next-line func-named-parameters - input: abi.encode(address(L2_BRIDGE_HUB)) - }); - - forceDeployments[1] = ForceDeployment({ - bytecodeHash: fixedForceDeploymentsData.bridgehubBytecodeHash, - newAddress: address(L2_BRIDGE_HUB), - callConstructor: true, - value: 0, - input: abi.encode( - fixedForceDeploymentsData.l1ChainId, - fixedForceDeploymentsData.aliasedL1Governance, - fixedForceDeploymentsData.maxNumberOfZKChains - ) - }); - - forceDeployments[2] = ForceDeployment({ - bytecodeHash: fixedForceDeploymentsData.l2AssetRouterBytecodeHash, - newAddress: address(L2_ASSET_ROUTER), - callConstructor: true, - value: 0, - // solhint-disable-next-line func-named-parameters - input: abi.encode( - fixedForceDeploymentsData.l1ChainId, - fixedForceDeploymentsData.eraChainId, - fixedForceDeploymentsData.l1AssetRouter, - additionalForceDeploymentsData.l2LegacySharedBridge, - additionalForceDeploymentsData.baseTokenAssetId, - fixedForceDeploymentsData.aliasedL1Governance - ) - }); - - forceDeployments[3] = ForceDeployment({ - bytecodeHash: fixedForceDeploymentsData.l2NtvBytecodeHash, - newAddress: L2_NATIVE_TOKEN_VAULT_ADDR, - callConstructor: true, - value: 0, - // solhint-disable-next-line func-named-parameters - input: abi.encode( - fixedForceDeploymentsData.l1ChainId, - fixedForceDeploymentsData.aliasedL1Governance, - fixedForceDeploymentsData.l2TokenProxyBytecodeHash, - additionalForceDeploymentsData.l2LegacySharedBridge, - address(0), // this is used if the contract were already deployed, so for the migration of Era. - false, - additionalForceDeploymentsData.l2Weth, - additionalForceDeploymentsData.baseTokenAssetId - ) - }); - } } diff --git a/system-contracts/contracts/interfaces/IL2GenesisUpgrade.sol b/system-contracts/contracts/interfaces/IL2GenesisUpgrade.sol index 88566d5d8..8752202a5 100644 --- a/system-contracts/contracts/interfaces/IL2GenesisUpgrade.sol +++ b/system-contracts/contracts/interfaces/IL2GenesisUpgrade.sol @@ -8,6 +8,7 @@ struct ZKChainSpecificForceDeploymentsData { address l2Weth; } +// solhint-disable-next-line gas-struct-packing struct FixedForceDeploymentsData { uint256 l1ChainId; uint256 eraChainId; @@ -19,6 +20,10 @@ struct FixedForceDeploymentsData { bytes32 l2AssetRouterBytecodeHash; bytes32 l2NtvBytecodeHash; bytes32 messageRootBytecodeHash; + address l2SharedBridgeLegacyImpl; + address l2BridgedStandardERC20Impl; + address l2BridgeProxyOwnerAddress; + address l2BridgedStandardERC20ProxyOwnerAddress; } interface IL2GenesisUpgrade { diff --git a/system-contracts/contracts/interfaces/IL2SharedBridgeLegacy.sol b/system-contracts/contracts/interfaces/IL2SharedBridgeLegacy.sol new file mode 100644 index 000000000..05d86757e --- /dev/null +++ b/system-contracts/contracts/interfaces/IL2SharedBridgeLegacy.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.20; + +import {UpgradeableBeacon} from "@openzeppelin/contracts-v4/proxy/beacon/UpgradeableBeacon.sol"; + +/// @author Matter Labs +/// @custom:security-contact security@matterlabs.dev +interface IL2SharedBridgeLegacy { + event FinalizeDeposit( + address indexed l1Sender, + address indexed l2Receiver, + address indexed l2Token, + uint256 amount + ); + + function l2TokenBeacon() external returns (UpgradeableBeacon); + + function withdraw(address _l1Receiver, address _l2Token, uint256 _amount) external; + + function l1TokenAddress(address _l2Token) external view returns (address); + + function l2TokenAddress(address _l1Token) external view returns (address); + + function l1Bridge() external view returns (address); + + function l1SharedBridge() external view returns (address); + + function deployBeaconProxy(bytes32 _salt) external returns (address); + + function sendMessageToL1(bytes calldata _message) external; +} diff --git a/system-contracts/contracts/libraries/SystemContractHelper.sol b/system-contracts/contracts/libraries/SystemContractHelper.sol index 77407b2cd..d3d5e7536 100644 --- a/system-contracts/contracts/libraries/SystemContractHelper.sol +++ b/system-contracts/contracts/libraries/SystemContractHelper.sol @@ -401,4 +401,19 @@ library SystemContractHelper { returndatacopy(add(returndata, 0x20), 0, rtSize) } } + + /// @notice Performs a `mimicCall` to an address, while ensuring that the call + /// was successful + /// @param _to The address to call. + /// @param _whoToMimic The address to mimic. + /// @param _data The data to pass to the call. + function mimicCallWithPropagatedRevert(address _to, address _whoToMimic, bytes memory _data) internal { + (bool success, bytes memory returnData) = mimicCall(_to, _whoToMimic, _data); + if (!success) { + // Propagate revert reason + assembly { + revert(add(returnData, 0x20), returndatasize()) + } + } + } } diff --git a/system-contracts/package.json b/system-contracts/package.json index ab9f8d60e..b1010c7ab 100644 --- a/system-contracts/package.json +++ b/system-contracts/package.json @@ -15,7 +15,9 @@ "fast-glob": "^3.3.2", "hardhat": "=2.22.2", "preprocess": "^3.2.0", - "zksync-ethers": "^5.9.0" + "zksync-ethers": "^5.9.0", + "@openzeppelin/contracts-upgradeable-v4": "npm:@openzeppelin/contracts-upgradeable@4.9.5", + "@openzeppelin/contracts-v4": "npm:@openzeppelin/contracts@4.9.5" }, "devDependencies": { "@matterlabs/hardhat-zksync-chai-matchers": "^0.2.0", diff --git a/system-contracts/test/L2GenesisUpgrade.spec.ts b/system-contracts/test/L2GenesisUpgrade.spec.ts index 916807c99..a7914f705 100644 --- a/system-contracts/test/L2GenesisUpgrade.spec.ts +++ b/system-contracts/test/L2GenesisUpgrade.spec.ts @@ -90,7 +90,7 @@ describe("L2GenesisUpgrade tests", function () { fixedForceDeploymentsData = ethers.utils.defaultAbiCoder.encode( [ - "tuple(uint256 l1ChainId, uint256 eraChainId, address l1AssetRouter, bytes32 l2TokenProxyBytecodeHash, address aliasedL1Governance, uint256 maxNumberOfZKChains, bytes32 bridgehubBytecodeHash, bytes32 l2AssetRouterBytecodeHash, bytes32 l2NtvBytecodeHash, bytes32 messageRootBytecodeHash)", + "tuple(uint256 l1ChainId, uint256 eraChainId, address l1AssetRouter, bytes32 l2TokenProxyBytecodeHash, address aliasedL1Governance, uint256 maxNumberOfZKChains, bytes32 bridgehubBytecodeHash, bytes32 l2AssetRouterBytecodeHash, bytes32 l2NtvBytecodeHash, bytes32 messageRootBytecodeHash, address l2SharedBridgeLegacyImpl, address l2BridgedStandardERC20Impl, address l2BridgeProxyOwnerAddress, address l2BridgedStandardERC20ProxyOwnerAddress)", ], [ { @@ -104,6 +104,11 @@ describe("L2GenesisUpgrade tests", function () { l2AssetRouterBytecodeHash: l2AssetRouterBytecodeHash, l2NtvBytecodeHash: ntvBytecodeHash, messageRootBytecodeHash: messageRootBytecodeHash, + // For genesis upgrade these values will always be zero + l2SharedBridgeLegacyImpl: ethers.constants.AddressZero, + l2BridgedStandardERC20Impl: ethers.constants.AddressZero, + l2BridgeProxyOwnerAddress: ethers.constants.AddressZero, + l2BridgedStandardERC20ProxyOwnerAddress: ethers.constants.AddressZero, }, ] );