github.com/ethereum-optimism/optimism@v1.7.2/packages/contracts-bedrock/src/universal/StandardBridge.sol (about)

     1  // SPDX-License-Identifier: MIT
     2  pragma solidity 0.8.15;
     3  
     4  import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
     5  import { ERC165Checker } from "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol";
     6  import { Address } from "@openzeppelin/contracts/utils/Address.sol";
     7  import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
     8  import { SafeCall } from "src/libraries/SafeCall.sol";
     9  import { IOptimismMintableERC20, ILegacyMintableERC20 } from "src/universal/IOptimismMintableERC20.sol";
    10  import { CrossDomainMessenger } from "src/universal/CrossDomainMessenger.sol";
    11  import { OptimismMintableERC20 } from "src/universal/OptimismMintableERC20.sol";
    12  import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol";
    13  
    14  /// @custom:upgradeable
    15  /// @title StandardBridge
    16  /// @notice StandardBridge is a base contract for the L1 and L2 standard ERC20 bridges. It handles
    17  ///         the core bridging logic, including escrowing tokens that are native to the local chain
    18  ///         and minting/burning tokens that are native to the remote chain.
    19  abstract contract StandardBridge is Initializable {
    20      using SafeERC20 for IERC20;
    21  
    22      /// @notice The L2 gas limit set when eth is depoisited using the receive() function.
    23      uint32 internal constant RECEIVE_DEFAULT_GAS_LIMIT = 200_000;
    24  
    25      /// @custom:legacy
    26      /// @custom:spacer messenger
    27      /// @notice Spacer for backwards compatibility.
    28      bytes30 private spacer_0_2_30;
    29  
    30      /// @custom:legacy
    31      /// @custom:spacer l2TokenBridge
    32      /// @notice Spacer for backwards compatibility.
    33      address private spacer_1_0_20;
    34  
    35      /// @notice Mapping that stores deposits for a given pair of local and remote tokens.
    36      mapping(address => mapping(address => uint256)) public deposits;
    37  
    38      /// @notice Messenger contract on this domain.
    39      /// @custom:network-specific
    40      CrossDomainMessenger public messenger;
    41  
    42      /// @notice Corresponding bridge on the other domain.
    43      /// @custom:network-specific
    44      StandardBridge public otherBridge;
    45  
    46      /// @notice Reserve extra slots (to a total of 50) in the storage layout for future upgrades.
    47      ///         A gap size of 45 was chosen here, so that the first slot used in a child contract
    48      ///         would be a multiple of 50.
    49      uint256[45] private __gap;
    50  
    51      /// @notice Emitted when an ETH bridge is initiated to the other chain.
    52      /// @param from      Address of the sender.
    53      /// @param to        Address of the receiver.
    54      /// @param amount    Amount of ETH sent.
    55      /// @param extraData Extra data sent with the transaction.
    56      event ETHBridgeInitiated(address indexed from, address indexed to, uint256 amount, bytes extraData);
    57  
    58      /// @notice Emitted when an ETH bridge is finalized on this chain.
    59      /// @param from      Address of the sender.
    60      /// @param to        Address of the receiver.
    61      /// @param amount    Amount of ETH sent.
    62      /// @param extraData Extra data sent with the transaction.
    63      event ETHBridgeFinalized(address indexed from, address indexed to, uint256 amount, bytes extraData);
    64  
    65      /// @notice Emitted when an ERC20 bridge is initiated to the other chain.
    66      /// @param localToken  Address of the ERC20 on this chain.
    67      /// @param remoteToken Address of the ERC20 on the remote chain.
    68      /// @param from        Address of the sender.
    69      /// @param to          Address of the receiver.
    70      /// @param amount      Amount of the ERC20 sent.
    71      /// @param extraData   Extra data sent with the transaction.
    72      event ERC20BridgeInitiated(
    73          address indexed localToken,
    74          address indexed remoteToken,
    75          address indexed from,
    76          address to,
    77          uint256 amount,
    78          bytes extraData
    79      );
    80  
    81      /// @notice Emitted when an ERC20 bridge is finalized on this chain.
    82      /// @param localToken  Address of the ERC20 on this chain.
    83      /// @param remoteToken Address of the ERC20 on the remote chain.
    84      /// @param from        Address of the sender.
    85      /// @param to          Address of the receiver.
    86      /// @param amount      Amount of the ERC20 sent.
    87      /// @param extraData   Extra data sent with the transaction.
    88      event ERC20BridgeFinalized(
    89          address indexed localToken,
    90          address indexed remoteToken,
    91          address indexed from,
    92          address to,
    93          uint256 amount,
    94          bytes extraData
    95      );
    96  
    97      /// @notice Only allow EOAs to call the functions. Note that this is not safe against contracts
    98      ///         calling code within their constructors, but also doesn't really matter since we're
    99      ///         just trying to prevent users accidentally depositing with smart contract wallets.
   100      modifier onlyEOA() {
   101          require(!Address.isContract(msg.sender), "StandardBridge: function can only be called from an EOA");
   102          _;
   103      }
   104  
   105      /// @notice Ensures that the caller is a cross-chain message from the other bridge.
   106      modifier onlyOtherBridge() {
   107          require(
   108              msg.sender == address(messenger) && messenger.xDomainMessageSender() == address(otherBridge),
   109              "StandardBridge: function can only be called from the other bridge"
   110          );
   111          _;
   112      }
   113  
   114      /// @notice Initializer.
   115      /// @param _messenger   Contract for CrossDomainMessenger on this network.
   116      /// @param _otherBridge Contract for the other StandardBridge contract.
   117      function __StandardBridge_init(
   118          CrossDomainMessenger _messenger,
   119          StandardBridge _otherBridge
   120      )
   121          internal
   122          onlyInitializing
   123      {
   124          messenger = _messenger;
   125          otherBridge = _otherBridge;
   126      }
   127  
   128      /// @notice Allows EOAs to bridge ETH by sending directly to the bridge.
   129      ///         Must be implemented by contracts that inherit.
   130      receive() external payable virtual;
   131  
   132      /// @notice Getter for messenger contract.
   133      ///         Public getter is legacy and will be removed in the future. Use `messenger` instead.
   134      /// @return Contract of the messenger on this domain.
   135      /// @custom:legacy
   136      function MESSENGER() external view returns (CrossDomainMessenger) {
   137          return messenger;
   138      }
   139  
   140      /// @notice Getter for the other bridge contract.
   141      ///         Public getter is legacy and will be removed in the future. Use `otherBridge` instead.
   142      /// @return Contract of the bridge on the other network.
   143      /// @custom:legacy
   144      function OTHER_BRIDGE() external view returns (StandardBridge) {
   145          return otherBridge;
   146      }
   147  
   148      /// @notice This function should return true if the contract is paused.
   149      ///         On L1 this function will check the SuperchainConfig for its paused status.
   150      ///         On L2 this function should be a no-op.
   151      /// @return Whether or not the contract is paused.
   152      function paused() public view virtual returns (bool) {
   153          return false;
   154      }
   155  
   156      /// @notice Sends ETH to the sender's address on the other chain.
   157      /// @param _minGasLimit Minimum amount of gas that the bridge can be relayed with.
   158      /// @param _extraData   Extra data to be sent with the transaction. Note that the recipient will
   159      ///                     not be triggered with this data, but it will be emitted and can be used
   160      ///                     to identify the transaction.
   161      function bridgeETH(uint32 _minGasLimit, bytes calldata _extraData) public payable onlyEOA {
   162          _initiateBridgeETH(msg.sender, msg.sender, msg.value, _minGasLimit, _extraData);
   163      }
   164  
   165      /// @notice Sends ETH to a receiver's address on the other chain. Note that if ETH is sent to a
   166      ///         smart contract and the call fails, the ETH will be temporarily locked in the
   167      ///         StandardBridge on the other chain until the call is replayed. If the call cannot be
   168      ///         replayed with any amount of gas (call always reverts), then the ETH will be
   169      ///         permanently locked in the StandardBridge on the other chain. ETH will also
   170      ///         be locked if the receiver is the other bridge, because finalizeBridgeETH will revert
   171      ///         in that case.
   172      /// @param _to          Address of the receiver.
   173      /// @param _minGasLimit Minimum amount of gas that the bridge can be relayed with.
   174      /// @param _extraData   Extra data to be sent with the transaction. Note that the recipient will
   175      ///                     not be triggered with this data, but it will be emitted and can be used
   176      ///                     to identify the transaction.
   177      function bridgeETHTo(address _to, uint32 _minGasLimit, bytes calldata _extraData) public payable {
   178          _initiateBridgeETH(msg.sender, _to, msg.value, _minGasLimit, _extraData);
   179      }
   180  
   181      /// @notice Sends ERC20 tokens to the sender's address on the other chain. Note that if the
   182      ///         ERC20 token on the other chain does not recognize the local token as the correct
   183      ///         pair token, the ERC20 bridge will fail and the tokens will be returned to sender on
   184      ///         this chain.
   185      /// @param _localToken  Address of the ERC20 on this chain.
   186      /// @param _remoteToken Address of the corresponding token on the remote chain.
   187      /// @param _amount      Amount of local tokens to deposit.
   188      /// @param _minGasLimit Minimum amount of gas that the bridge can be relayed with.
   189      /// @param _extraData   Extra data to be sent with the transaction. Note that the recipient will
   190      ///                     not be triggered with this data, but it will be emitted and can be used
   191      ///                     to identify the transaction.
   192      function bridgeERC20(
   193          address _localToken,
   194          address _remoteToken,
   195          uint256 _amount,
   196          uint32 _minGasLimit,
   197          bytes calldata _extraData
   198      )
   199          public
   200          virtual
   201          onlyEOA
   202      {
   203          _initiateBridgeERC20(_localToken, _remoteToken, msg.sender, msg.sender, _amount, _minGasLimit, _extraData);
   204      }
   205  
   206      /// @notice Sends ERC20 tokens to a receiver's address on the other chain. Note that if the
   207      ///         ERC20 token on the other chain does not recognize the local token as the correct
   208      ///         pair token, the ERC20 bridge will fail and the tokens will be returned to sender on
   209      ///         this chain.
   210      /// @param _localToken  Address of the ERC20 on this chain.
   211      /// @param _remoteToken Address of the corresponding token on the remote chain.
   212      /// @param _to          Address of the receiver.
   213      /// @param _amount      Amount of local tokens to deposit.
   214      /// @param _minGasLimit Minimum amount of gas that the bridge can be relayed with.
   215      /// @param _extraData   Extra data to be sent with the transaction. Note that the recipient will
   216      ///                     not be triggered with this data, but it will be emitted and can be used
   217      ///                     to identify the transaction.
   218      function bridgeERC20To(
   219          address _localToken,
   220          address _remoteToken,
   221          address _to,
   222          uint256 _amount,
   223          uint32 _minGasLimit,
   224          bytes calldata _extraData
   225      )
   226          public
   227          virtual
   228      {
   229          _initiateBridgeERC20(_localToken, _remoteToken, msg.sender, _to, _amount, _minGasLimit, _extraData);
   230      }
   231  
   232      /// @notice Finalizes an ETH bridge on this chain. Can only be triggered by the other
   233      ///         StandardBridge contract on the remote chain.
   234      /// @param _from      Address of the sender.
   235      /// @param _to        Address of the receiver.
   236      /// @param _amount    Amount of ETH being bridged.
   237      /// @param _extraData Extra data to be sent with the transaction. Note that the recipient will
   238      ///                   not be triggered with this data, but it will be emitted and can be used
   239      ///                   to identify the transaction.
   240      function finalizeBridgeETH(
   241          address _from,
   242          address _to,
   243          uint256 _amount,
   244          bytes calldata _extraData
   245      )
   246          public
   247          payable
   248          onlyOtherBridge
   249      {
   250          require(paused() == false, "StandardBridge: paused");
   251          require(msg.value == _amount, "StandardBridge: amount sent does not match amount required");
   252          require(_to != address(this), "StandardBridge: cannot send to self");
   253          require(_to != address(messenger), "StandardBridge: cannot send to messenger");
   254  
   255          // Emit the correct events. By default this will be _amount, but child
   256          // contracts may override this function in order to emit legacy events as well.
   257          _emitETHBridgeFinalized(_from, _to, _amount, _extraData);
   258  
   259          bool success = SafeCall.call(_to, gasleft(), _amount, hex"");
   260          require(success, "StandardBridge: ETH transfer failed");
   261      }
   262  
   263      /// @notice Finalizes an ERC20 bridge on this chain. Can only be triggered by the other
   264      ///         StandardBridge contract on the remote chain.
   265      /// @param _localToken  Address of the ERC20 on this chain.
   266      /// @param _remoteToken Address of the corresponding token on the remote chain.
   267      /// @param _from        Address of the sender.
   268      /// @param _to          Address of the receiver.
   269      /// @param _amount      Amount of the ERC20 being bridged.
   270      /// @param _extraData   Extra data to be sent with the transaction. Note that the recipient will
   271      ///                     not be triggered with this data, but it will be emitted and can be used
   272      ///                     to identify the transaction.
   273      function finalizeBridgeERC20(
   274          address _localToken,
   275          address _remoteToken,
   276          address _from,
   277          address _to,
   278          uint256 _amount,
   279          bytes calldata _extraData
   280      )
   281          public
   282          onlyOtherBridge
   283      {
   284          require(paused() == false, "StandardBridge: paused");
   285          if (_isOptimismMintableERC20(_localToken)) {
   286              require(
   287                  _isCorrectTokenPair(_localToken, _remoteToken),
   288                  "StandardBridge: wrong remote token for Optimism Mintable ERC20 local token"
   289              );
   290  
   291              OptimismMintableERC20(_localToken).mint(_to, _amount);
   292          } else {
   293              deposits[_localToken][_remoteToken] = deposits[_localToken][_remoteToken] - _amount;
   294              IERC20(_localToken).safeTransfer(_to, _amount);
   295          }
   296  
   297          // Emit the correct events. By default this will be ERC20BridgeFinalized, but child
   298          // contracts may override this function in order to emit legacy events as well.
   299          _emitERC20BridgeFinalized(_localToken, _remoteToken, _from, _to, _amount, _extraData);
   300      }
   301  
   302      /// @notice Initiates a bridge of ETH through the CrossDomainMessenger.
   303      /// @param _from        Address of the sender.
   304      /// @param _to          Address of the receiver.
   305      /// @param _amount      Amount of ETH being bridged.
   306      /// @param _minGasLimit Minimum amount of gas that the bridge can be relayed with.
   307      /// @param _extraData   Extra data to be sent with the transaction. Note that the recipient will
   308      ///                     not be triggered with this data, but it will be emitted and can be used
   309      ///                     to identify the transaction.
   310      function _initiateBridgeETH(
   311          address _from,
   312          address _to,
   313          uint256 _amount,
   314          uint32 _minGasLimit,
   315          bytes memory _extraData
   316      )
   317          internal
   318      {
   319          require(msg.value == _amount, "StandardBridge: bridging ETH must include sufficient ETH value");
   320  
   321          // Emit the correct events. By default this will be _amount, but child
   322          // contracts may override this function in order to emit legacy events as well.
   323          _emitETHBridgeInitiated(_from, _to, _amount, _extraData);
   324  
   325          messenger.sendMessage{ value: _amount }({
   326              _target: address(otherBridge),
   327              _message: abi.encodeWithSelector(this.finalizeBridgeETH.selector, _from, _to, _amount, _extraData),
   328              _minGasLimit: _minGasLimit
   329          });
   330      }
   331  
   332      /// @notice Sends ERC20 tokens to a receiver's address on the other chain.
   333      /// @param _localToken  Address of the ERC20 on this chain.
   334      /// @param _remoteToken Address of the corresponding token on the remote chain.
   335      /// @param _to          Address of the receiver.
   336      /// @param _amount      Amount of local tokens to deposit.
   337      /// @param _minGasLimit Minimum amount of gas that the bridge can be relayed with.
   338      /// @param _extraData   Extra data to be sent with the transaction. Note that the recipient will
   339      ///                     not be triggered with this data, but it will be emitted and can be used
   340      ///                     to identify the transaction.
   341      function _initiateBridgeERC20(
   342          address _localToken,
   343          address _remoteToken,
   344          address _from,
   345          address _to,
   346          uint256 _amount,
   347          uint32 _minGasLimit,
   348          bytes memory _extraData
   349      )
   350          internal
   351      {
   352          if (_isOptimismMintableERC20(_localToken)) {
   353              require(
   354                  _isCorrectTokenPair(_localToken, _remoteToken),
   355                  "StandardBridge: wrong remote token for Optimism Mintable ERC20 local token"
   356              );
   357  
   358              OptimismMintableERC20(_localToken).burn(_from, _amount);
   359          } else {
   360              IERC20(_localToken).safeTransferFrom(_from, address(this), _amount);
   361              deposits[_localToken][_remoteToken] = deposits[_localToken][_remoteToken] + _amount;
   362          }
   363  
   364          // Emit the correct events. By default this will be ERC20BridgeInitiated, but child
   365          // contracts may override this function in order to emit legacy events as well.
   366          _emitERC20BridgeInitiated(_localToken, _remoteToken, _from, _to, _amount, _extraData);
   367  
   368          messenger.sendMessage({
   369              _target: address(otherBridge),
   370              _message: abi.encodeWithSelector(
   371                  this.finalizeBridgeERC20.selector,
   372                  // Because this call will be executed on the remote chain, we reverse the order of
   373                  // the remote and local token addresses relative to their order in the
   374                  // finalizeBridgeERC20 function.
   375                  _remoteToken,
   376                  _localToken,
   377                  _from,
   378                  _to,
   379                  _amount,
   380                  _extraData
   381                  ),
   382              _minGasLimit: _minGasLimit
   383          });
   384      }
   385  
   386      /// @notice Checks if a given address is an OptimismMintableERC20. Not perfect, but good enough.
   387      ///         Just the way we like it.
   388      /// @param _token Address of the token to check.
   389      /// @return True if the token is an OptimismMintableERC20.
   390      function _isOptimismMintableERC20(address _token) internal view returns (bool) {
   391          return ERC165Checker.supportsInterface(_token, type(ILegacyMintableERC20).interfaceId)
   392              || ERC165Checker.supportsInterface(_token, type(IOptimismMintableERC20).interfaceId);
   393      }
   394  
   395      /// @notice Checks if the "other token" is the correct pair token for the OptimismMintableERC20.
   396      ///         Calls can be saved in the future by combining this logic with
   397      ///         `_isOptimismMintableERC20`.
   398      /// @param _mintableToken OptimismMintableERC20 to check against.
   399      /// @param _otherToken    Pair token to check.
   400      /// @return True if the other token is the correct pair token for the OptimismMintableERC20.
   401      function _isCorrectTokenPair(address _mintableToken, address _otherToken) internal view returns (bool) {
   402          if (ERC165Checker.supportsInterface(_mintableToken, type(ILegacyMintableERC20).interfaceId)) {
   403              return _otherToken == ILegacyMintableERC20(_mintableToken).l1Token();
   404          } else {
   405              return _otherToken == IOptimismMintableERC20(_mintableToken).remoteToken();
   406          }
   407      }
   408  
   409      /// @notice Emits the ETHBridgeInitiated event and if necessary the appropriate legacy event
   410      ///         when an ETH bridge is finalized on this chain.
   411      /// @param _from      Address of the sender.
   412      /// @param _to        Address of the receiver.
   413      /// @param _amount    Amount of ETH sent.
   414      /// @param _extraData Extra data sent with the transaction.
   415      function _emitETHBridgeInitiated(
   416          address _from,
   417          address _to,
   418          uint256 _amount,
   419          bytes memory _extraData
   420      )
   421          internal
   422          virtual
   423      {
   424          emit ETHBridgeInitiated(_from, _to, _amount, _extraData);
   425      }
   426  
   427      /// @notice Emits the ETHBridgeFinalized and if necessary the appropriate legacy event when an
   428      ///         ETH bridge is finalized on this chain.
   429      /// @param _from      Address of the sender.
   430      /// @param _to        Address of the receiver.
   431      /// @param _amount    Amount of ETH sent.
   432      /// @param _extraData Extra data sent with the transaction.
   433      function _emitETHBridgeFinalized(
   434          address _from,
   435          address _to,
   436          uint256 _amount,
   437          bytes memory _extraData
   438      )
   439          internal
   440          virtual
   441      {
   442          emit ETHBridgeFinalized(_from, _to, _amount, _extraData);
   443      }
   444  
   445      /// @notice Emits the ERC20BridgeInitiated event and if necessary the appropriate legacy
   446      ///         event when an ERC20 bridge is initiated to the other chain.
   447      /// @param _localToken  Address of the ERC20 on this chain.
   448      /// @param _remoteToken Address of the ERC20 on the remote chain.
   449      /// @param _from        Address of the sender.
   450      /// @param _to          Address of the receiver.
   451      /// @param _amount      Amount of the ERC20 sent.
   452      /// @param _extraData   Extra data sent with the transaction.
   453      function _emitERC20BridgeInitiated(
   454          address _localToken,
   455          address _remoteToken,
   456          address _from,
   457          address _to,
   458          uint256 _amount,
   459          bytes memory _extraData
   460      )
   461          internal
   462          virtual
   463      {
   464          emit ERC20BridgeInitiated(_localToken, _remoteToken, _from, _to, _amount, _extraData);
   465      }
   466  
   467      /// @notice Emits the ERC20BridgeFinalized event and if necessary the appropriate legacy
   468      ///         event when an ERC20 bridge is initiated to the other chain.
   469      /// @param _localToken  Address of the ERC20 on this chain.
   470      /// @param _remoteToken Address of the ERC20 on the remote chain.
   471      /// @param _from        Address of the sender.
   472      /// @param _to          Address of the receiver.
   473      /// @param _amount      Amount of the ERC20 sent.
   474      /// @param _extraData   Extra data sent with the transaction.
   475      function _emitERC20BridgeFinalized(
   476          address _localToken,
   477          address _remoteToken,
   478          address _from,
   479          address _to,
   480          uint256 _amount,
   481          bytes memory _extraData
   482      )
   483          internal
   484          virtual
   485      {
   486          emit ERC20BridgeFinalized(_localToken, _remoteToken, _from, _to, _amount, _extraData);
   487      }
   488  }