From 8d2fb4bb4c2673c74d4fb225ae8b674daf620ad2 Mon Sep 17 00:00:00 2001 From: kelemeno Date: Mon, 15 Jan 2024 20:30:56 +0000 Subject: [PATCH] merge from Bence's commits --- .../contracts/bridge/L1ERC20Bridge.sol | 65 ++++++++++++------- .../contracts/bridge/L1WethBridge.sol | 14 ++-- .../bridge/interfaces/IL2ERC20Bridge.sol | 2 +- .../bridge/interfaces/IL2WethBridge.sol | 2 +- .../IStateTransitionManager.sol | 2 + .../StateTransitionManager.sol | 5 ++ .../contracts/bridge/L2ERC20Bridge.sol | 8 +-- .../contracts/bridge/L2WethBridge.sol | 4 +- 8 files changed, 64 insertions(+), 38 deletions(-) diff --git a/l1-contracts/contracts/bridge/L1ERC20Bridge.sol b/l1-contracts/contracts/bridge/L1ERC20Bridge.sol index 93455407b..e445f9f06 100644 --- a/l1-contracts/contracts/bridge/L1ERC20Bridge.sol +++ b/l1-contracts/contracts/bridge/L1ERC20Bridge.sol @@ -8,7 +8,7 @@ import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol import {IL1BridgeDeprecated} from "./interfaces/IL1BridgeDeprecated.sol"; import {IL1BridgeLegacy} from "./interfaces/IL1BridgeLegacy.sol"; -import {IL1Bridge} from "./interfaces/IL1Bridge.sol"; +import {IL1Bridge, ConfirmL2TxStatus} from "./interfaces/IL1Bridge.sol"; import {IL2Bridge} from "./interfaces/IL2Bridge.sol"; import {IL2ERC20Bridge} from "./interfaces/IL2ERC20Bridge.sol"; import {ConfirmL2TxStatus} from "./interfaces/IL1Bridge.sol"; @@ -48,7 +48,7 @@ contract L1ERC20Bridge is /// @dev Used to indicate that L2 -> L1 message was already processed /// @dev this is just used for ERA for backwards compatibility reasons mapping(uint256 l2BatchNumber => mapping(uint256 l2ToL1MessageNumber => bool isFinalized)) - public isWithdrawalFinalizedEra; + internal isWithdrawalFinalizedEra; /// @dev A mapping account => L1 token address => L2 deposit transaction hash => amount /// @dev Used for saving the number of deposited funds, to claim them in case the deposit transaction will fail @@ -94,7 +94,7 @@ contract L1ERC20Bridge is /// @dev A mapping chainId => keccak256(account, tokenAddress, amount) => L2 deposit transaction hash => true /// @dev Used for saving the number of deposited funds, to claim them in case the deposit transaction will fail - mapping(uint256 => mapping(bytes32 => mapping(bytes32 => bool))) internal deposited; + mapping(uint256 => mapping(bytes32 => mapping(bytes32 => bool))) internal depositHappened; /// @dev used for extra security until hyperbridging is implemented. mapping(uint256 => mapping(address => uint256)) public chainBalance; @@ -227,16 +227,16 @@ contract L1ERC20Bridge is // Prepare the proxy constructor data bytes memory l2BridgeProxyConstructorData; { - address proxyAdmin = readProxyAdmin(); - address l2ProxyAdmin = AddressAliasHelper.applyL1ToL2Alias(proxyAdmin); + address owner = owner(); + address l2Owner = AddressAliasHelper.applyL1ToL2Alias(owner); // Data to be used in delegate call to initialize the proxy bytes memory proxyInitializationParams = abi.encodeCall( IL2ERC20Bridge.initialize, - (address(this), l2TokenProxyBytecodeHash, l2ProxyAdmin) + (address(this), l2TokenProxyBytecodeHash, owner) ); l2BridgeProxyConstructorData = abi.encode( bridgeImplementationAddr, - l2ProxyAdmin, + owner, proxyInitializationParams ); } @@ -284,6 +284,8 @@ contract L1ERC20Bridge is ), "L1EB: bridge impl tx not conf" // not confirmed ); + delete bridgeImplDeployOnL2TxHash[_chainId]; + require( bridgehub.proveL1ToL2TransactionStatus( _chainId, @@ -296,7 +298,6 @@ contract L1ERC20Bridge is ), "L1EB: bridge proxy tx not conf" // not confirmed ); - delete bridgeImplDeployOnL2TxHash[_chainId]; delete bridgeProxyDeployOnL2TxHash[_chainId]; if (_bridgeProxyTxStatus.succeeded) { l2BridgeAddress[_chainId] = l2BridgeStandardAddress; @@ -436,7 +437,7 @@ contract L1ERC20Bridge is // Save the deposited amount to claim funds on L1 if the deposit failed on L2 bytes32 txDataHash = keccak256(abi.encode(msg.sender, _l1Token, _amount)); - deposited[_chainId][txDataHash][l2TxHash] = true; + depositHappened[_chainId][txDataHash][l2TxHash] = true; emit DepositInitiatedSharedBridge(_chainId, txDataHash, msg.sender, _l2Receiver, _l1Token, _amount); if (_chainId == ERA_CHAIN_ID) { @@ -554,8 +555,8 @@ contract L1ERC20Bridge is bytes32 _txDataHash, bytes32 _txHash ) external override onlyBridgehub { - require(!deposited[_chainId][_txDataHash][_txHash], "L1EB: tx already happened"); - deposited[_chainId][_txDataHash][_txHash] = true; + require(!depositHappened[_chainId][_txDataHash][_txHash], "L1EB: tx already happened"); + depositHappened[_chainId][_txDataHash][_txHash] = true; emit BridgehubDepositFinalized(_chainId, _txDataHash, _txHash); } @@ -631,6 +632,7 @@ contract L1ERC20Bridge is uint16 _l2TxNumberInBatch, bytes32[] calldata _merkleProof ) public nonReentrant { + { bool proofValid = bridgehub.proveL1ToL2TransactionStatus( _chainId, @@ -642,24 +644,16 @@ contract L1ERC20Bridge is TxStatus.Failure ); require(proofValid, "yn"); + require(_amount > 0, "y1"); } bytes32 txDataHash = keccak256(abi.encode(msg.sender, _l1Token, _amount)); + bool usingLegacyDepositAmountStorageVar = _checkDeposited(_chainId, _depositSender, _l1Token, txDataHash, _l2TxHash, _amount); - if (_chainId == ERA_CHAIN_ID) { - uint256 amount = 0; - amount = depositAmountEra[_depositSender][_l1Token][_l2TxHash]; - require(_amount == amount, "L1EB: amount mismatch"); - } else { - bool depositHappened = deposited[_chainId][txDataHash][_l2TxHash]; - require(depositHappened, "L1EB: deposit did not happen"); - } - require(_amount > 0, "y1"); - - if (_chainId == ERA_CHAIN_ID) { + if ((_chainId == ERA_CHAIN_ID) && usingLegacyDepositAmountStorageVar) { delete depositAmountEra[_depositSender][_l1Token][_l2TxHash]; } else { - delete deposited[_chainId][txDataHash][_l2TxHash]; + delete depositHappened[_chainId][txDataHash][_l2TxHash]; } if (!hyperbridgingEnabled[_chainId]) { // check that the chain has sufficient balance @@ -675,6 +669,31 @@ contract L1ERC20Bridge is } } + function _checkDeposited( + uint256 _chainId, + address _depositSender, + address _l1Token, + bytes32 _txDataHash, + bytes32 _l2TxHash, + uint256 _amount + ) internal returns (bool usingLegacyDepositAmountStorageVar) { + uint256 amount = 0; + if (_chainId == ERA_CHAIN_ID) { + { amount = depositAmountEra[_depositSender][_l1Token][_l2TxHash];} + if (amount > 0){ + usingLegacyDepositAmountStorageVar = true; + require(_amount == amount, "L1EB: amount mismatch"); + } else { + bool deposited; + {deposited = depositHappened[_chainId][_txDataHash][_l2TxHash];} + require(deposited, "L1EB: deposit did not happen"); + } + } else { + bool deposited = depositHappened[_chainId][_txDataHash][_l2TxHash]; + require(deposited, "L1EB: deposit did not happen"); + } + } + /// @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 diff --git a/l1-contracts/contracts/bridge/L1WethBridge.sol b/l1-contracts/contracts/bridge/L1WethBridge.sol index 842ea595e..bd5f5473d 100644 --- a/l1-contracts/contracts/bridge/L1WethBridge.sol +++ b/l1-contracts/contracts/bridge/L1WethBridge.sol @@ -5,7 +5,7 @@ pragma solidity 0.8.20; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import {IL1Bridge} from "./interfaces/IL1Bridge.sol"; +import {IL1Bridge, ConfirmL2TxStatus} from "./interfaces/IL1Bridge.sol"; import {IL2WethBridge} from "./interfaces/IL2WethBridge.sol"; import {IL2Bridge} from "./interfaces/IL2Bridge.sol"; import {IWETH9} from "./interfaces/IWETH9.sol"; @@ -86,7 +86,7 @@ contract L1WethBridge is IL1Bridge, ReentrancyGuard, Initializable, Ownable2Step /// @dev A mapping chainId => keccak256(account, amount) => L2 deposit transaction hash => amount /// @dev Used for saving the number of deposited funds, to claim them in case the deposit transaction will fail /// @dev only used when it is not the base token, as then it is sent to refund recipient - mapping(uint256 => mapping(bytes32 => mapping(bytes32 => bool))) internal deposited; + mapping(uint256 => mapping(bytes32 => mapping(bytes32 => bool))) internal depositHappened; /// @dev A mapping L2 chainId => Batch number => message number => flag /// @dev Used to indicate that L2 -> L1 WETH message was already processed @@ -483,8 +483,8 @@ contract L1WethBridge is IL1Bridge, ReentrancyGuard, Initializable, Ownable2Step bytes32 _txDataHash, bytes32 _txHash ) external override onlyBridgehub { - require(!deposited[_chainId][_txDataHash][_txHash], "L1WETHBridge: tx already happened"); - deposited[_chainId][_txDataHash][_txHash] = true; + require(!depositHappened[_chainId][_txDataHash][_txHash], "L1WETHBridge: tx already happened"); + depositHappened[_chainId][_txDataHash][_txHash] = true; emit BridgehubDepositFinalized(_chainId, _txDataHash, _txHash); } @@ -526,13 +526,13 @@ contract L1WethBridge is IL1Bridge, ReentrancyGuard, Initializable, Ownable2Step ); require(proofValid, "L1WB: Invalid L2 transaction status proof"); - bool depositHappened = deposited[_chainId][keccak256(abi.encode(_depositSender, _amount))][_l2TxHash]; - require(((_amount > 0) && (depositHappened)), "L1WB: _amount is zero or deposit did not happen"); + bool deposited = depositHappened[_chainId][keccak256(abi.encode(_depositSender, _amount))][_l2TxHash]; + require(((_amount > 0) && (deposited)), "L1WB: _amount is zero or deposit did not happen"); if (!hyperbridgingEnabled[_chainId]) { require(chainBalance[_chainId] >= _amount, "L1WB: chainBalance is too low"); chainBalance[_chainId] -= _amount; } - delete deposited[_chainId][keccak256(abi.encode(_depositSender, _amount))][_l2TxHash]; + delete depositHappened[_chainId][keccak256(abi.encode(_depositSender, _amount))][_l2TxHash]; // Withdraw funds // Wrap ETH to WETH tokens (smart contract address receives the equivalent _amount of WETH) diff --git a/l1-contracts/contracts/bridge/interfaces/IL2ERC20Bridge.sol b/l1-contracts/contracts/bridge/interfaces/IL2ERC20Bridge.sol index 4831eb55a..c5416e7fd 100644 --- a/l1-contracts/contracts/bridge/interfaces/IL2ERC20Bridge.sol +++ b/l1-contracts/contracts/bridge/interfaces/IL2ERC20Bridge.sol @@ -4,5 +4,5 @@ pragma solidity 0.8.20; /// @author Matter Labs interface IL2ERC20Bridge { - function initialize(address _l1Bridge, bytes32 _l2TokenProxyBytecodeHash, address _proxyAdmin) external; + function initialize(address _l1Bridge, bytes32 _l2TokenProxyBytecodeHash, address _aliasedOwner) external; } diff --git a/l1-contracts/contracts/bridge/interfaces/IL2WethBridge.sol b/l1-contracts/contracts/bridge/interfaces/IL2WethBridge.sol index c30812e55..38a9d3b56 100644 --- a/l1-contracts/contracts/bridge/interfaces/IL2WethBridge.sol +++ b/l1-contracts/contracts/bridge/interfaces/IL2WethBridge.sol @@ -3,5 +3,5 @@ pragma solidity 0.8.20; interface IL2WethBridge { - function initialize(address _l1Bridge, address _l1WethAddress, address _proxyAdmin, bool _ethIsBaseToken) external; + function initialize(address _l1Bridge, address _l1WethAddress, address _aliasedOwner, bool _ethIsBaseToken) external; } diff --git a/l1-contracts/contracts/state-transition/IStateTransitionManager.sol b/l1-contracts/contracts/state-transition/IStateTransitionManager.sol index ebcb575e2..11bec2d84 100644 --- a/l1-contracts/contracts/state-transition/IStateTransitionManager.sol +++ b/l1-contracts/contracts/state-transition/IStateTransitionManager.sol @@ -64,4 +64,6 @@ interface IStateTransitionManager { ) external; function setUpgradeDiamondCut(Diamond.DiamondCutData calldata _cutData, uint256 _oldProtocolVersion) external; + + function freezeChain(uint256 _chainId) external; } diff --git a/l1-contracts/contracts/state-transition/StateTransitionManager.sol b/l1-contracts/contracts/state-transition/StateTransitionManager.sol index f11fea77f..0fe2338f5 100644 --- a/l1-contracts/contracts/state-transition/StateTransitionManager.sol +++ b/l1-contracts/contracts/state-transition/StateTransitionManager.sol @@ -121,6 +121,11 @@ contract StateTransitionManager is IStateTransitionManager, ReentrancyGuard, Own upgradeCutHash[_oldProtocolVersion] = keccak256(abi.encode(_cutData)); } + /// @dev freezes the specified chain + function freezeChain(uint256 _chainId) external onlyOwner { + IZkSyncStateTransition(stateTransition[_chainId]).freezeDiamond(); + } + /// registration /// @dev we have to set the chainId at genesis, as blockhashzero is the same for all chains with the same chainId diff --git a/l2-contracts/contracts/bridge/L2ERC20Bridge.sol b/l2-contracts/contracts/bridge/L2ERC20Bridge.sol index 610d75c2e..efe447b1c 100644 --- a/l2-contracts/contracts/bridge/L2ERC20Bridge.sol +++ b/l2-contracts/contracts/bridge/L2ERC20Bridge.sol @@ -42,22 +42,22 @@ contract L2ERC20Bridge is IL2Bridge, Initializable { /// @notice Initializes the bridge contract for later use. Expected to be used in the proxy. /// @param _l1Bridge The address of the L1 Bridge contract. /// @param _l2TokenProxyBytecodeHash The bytecode hash of the proxy for tokens deployed by the bridge. - /// @param _proxyAdmin The address of the governor contract. + /// @param _aliasedOwner The address of the governor contract. function initialize( address _l1Bridge, bytes32 _l2TokenProxyBytecodeHash, - address _proxyAdmin + address _aliasedOwner ) external initializer { require(_l1Bridge != address(0), "bf"); require(_l2TokenProxyBytecodeHash != bytes32(0), "df"); - require(_proxyAdmin != address(0), "sf"); + require(_aliasedOwner != address(0), "sf"); l1Bridge = _l1Bridge; l2TokenProxyBytecodeHash = _l2TokenProxyBytecodeHash; address l2StandardToken = address(new L2StandardERC20{salt: bytes32(0)}()); l2TokenBeacon = new UpgradeableBeacon{salt: bytes32(0)}(l2StandardToken); - l2TokenBeacon.transferOwnership(_proxyAdmin); + l2TokenBeacon.transferOwnership(_aliasedOwner); } /// @notice Finalize the deposit and mint funds diff --git a/l2-contracts/contracts/bridge/L2WethBridge.sol b/l2-contracts/contracts/bridge/L2WethBridge.sol index f00d3d73e..bd393f6c6 100644 --- a/l2-contracts/contracts/bridge/L2WethBridge.sol +++ b/l2-contracts/contracts/bridge/L2WethBridge.sol @@ -49,7 +49,7 @@ contract L2WethBridge is IL2Bridge, Initializable { function initialize( address _l1Bridge, address _l1WethAddress, - address _proxyAdmin, + address _aliasedOwner, bool _isEthBaseToken ) external initializer { require(_l1Bridge != address(0), "L1 WETH bridge address cannot be zero"); @@ -63,7 +63,7 @@ contract L2WethBridge is IL2Bridge, Initializable { bytes memory initData = abi.encodeWithSelector(L2Weth.initialize.selector, "Wrapped Ether", "WETH"); TransparentUpgradeableProxy l2Weth = new TransparentUpgradeableProxy{salt: bytes32(0)}( l2WethImplementation, - _proxyAdmin, + _aliasedOwner, initData ); L2Weth(payable(address(l2Weth))).initializeV2(address(this), l1WethAddress, _isEthBaseToken);