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

     1  // SPDX-License-Identifier: MIT
     2  pragma solidity 0.8.15;
     3  
     4  import { CrossDomainMessenger } from "src/universal/CrossDomainMessenger.sol";
     5  import { StandardBridge } from "src/universal/StandardBridge.sol";
     6  import { SuperchainConfig } from "src/L1/SuperchainConfig.sol";
     7  import { Address } from "@openzeppelin/contracts/utils/Address.sol";
     8  import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol";
     9  
    10  /// @title ERC721Bridge
    11  /// @notice ERC721Bridge is a base contract for the L1 and L2 ERC721 bridges.
    12  abstract contract ERC721Bridge is Initializable {
    13      /// @custom:spacer ERC721Bridge's initializer slot spacing
    14      /// @notice Spacer to avoid packing into the initializer slot
    15      bytes30 private spacer_0_2_30;
    16  
    17      /// @notice Messenger contract on this domain.
    18      /// @custom:network-specific
    19      CrossDomainMessenger public messenger;
    20  
    21      /// @notice Contract of the bridge on the other network.
    22      /// @custom:network-specific
    23      StandardBridge public otherBridge;
    24  
    25      /// @notice Reserve extra slots (to a total of 50) in the storage layout for future upgrades.
    26      uint256[46] private __gap;
    27  
    28      /// @notice Emitted when an ERC721 bridge to the other network is initiated.
    29      /// @param localToken  Address of the token on this domain.
    30      /// @param remoteToken Address of the token on the remote domain.
    31      /// @param from        Address that initiated bridging action.
    32      /// @param to          Address to receive the token.
    33      /// @param tokenId     ID of the specific token deposited.
    34      /// @param extraData   Extra data for use on the client-side.
    35      event ERC721BridgeInitiated(
    36          address indexed localToken,
    37          address indexed remoteToken,
    38          address indexed from,
    39          address to,
    40          uint256 tokenId,
    41          bytes extraData
    42      );
    43  
    44      /// @notice Emitted when an ERC721 bridge from the other network is finalized.
    45      /// @param localToken  Address of the token on this domain.
    46      /// @param remoteToken Address of the token on the remote domain.
    47      /// @param from        Address that initiated bridging action.
    48      /// @param to          Address to receive the token.
    49      /// @param tokenId     ID of the specific token deposited.
    50      /// @param extraData   Extra data for use on the client-side.
    51      event ERC721BridgeFinalized(
    52          address indexed localToken,
    53          address indexed remoteToken,
    54          address indexed from,
    55          address to,
    56          uint256 tokenId,
    57          bytes extraData
    58      );
    59  
    60      /// @notice Ensures that the caller is a cross-chain message from the other bridge.
    61      modifier onlyOtherBridge() {
    62          require(
    63              msg.sender == address(messenger) && messenger.xDomainMessageSender() == address(otherBridge),
    64              "ERC721Bridge: function can only be called from the other bridge"
    65          );
    66          _;
    67      }
    68  
    69      /// @notice Initializer.
    70      /// @param _messenger   Contract of the CrossDomainMessenger on this network.
    71      /// @param _otherBridge Contract of the ERC721 bridge on the other network.
    72      function __ERC721Bridge_init(
    73          CrossDomainMessenger _messenger,
    74          StandardBridge _otherBridge
    75      )
    76          internal
    77          onlyInitializing
    78      {
    79          messenger = _messenger;
    80          otherBridge = _otherBridge;
    81      }
    82  
    83      /// @notice Legacy getter for messenger contract.
    84      ///         Public getter is legacy and will be removed in the future. Use `messenger` instead.
    85      /// @return Messenger contract on this domain.
    86      /// @custom:legacy
    87      function MESSENGER() external view returns (CrossDomainMessenger) {
    88          return messenger;
    89      }
    90  
    91      /// @notice Legacy getter for other bridge address.
    92      ///         Public getter is legacy and will be removed in the future. Use `otherBridge` instead.
    93      /// @return Contract of the bridge on the other network.
    94      /// @custom:legacy
    95      function OTHER_BRIDGE() external view returns (StandardBridge) {
    96          return otherBridge;
    97      }
    98  
    99      /// @notice This function should return true if the contract is paused.
   100      ///         On L1 this function will check the SuperchainConfig for its paused status.
   101      ///         On L2 this function should be a no-op.
   102      /// @return Whether or not the contract is paused.
   103      function paused() public view virtual returns (bool) {
   104          return false;
   105      }
   106  
   107      /// @notice Initiates a bridge of an NFT to the caller's account on the other chain. Note that
   108      ///         this function can only be called by EOAs. Smart contract wallets should use the
   109      ///         `bridgeERC721To` function after ensuring that the recipient address on the remote
   110      ///         chain exists. Also note that the current owner of the token on this chain must
   111      ///         approve this contract to operate the NFT before it can be bridged.
   112      ///         **WARNING**: Do not bridge an ERC721 that was originally deployed on Optimism. This
   113      ///         bridge only supports ERC721s originally deployed on Ethereum. Users will need to
   114      ///         wait for the one-week challenge period to elapse before their Optimism-native NFT
   115      ///         can be refunded on L2.
   116      /// @param _localToken  Address of the ERC721 on this domain.
   117      /// @param _remoteToken Address of the ERC721 on the remote domain.
   118      /// @param _tokenId     Token ID to bridge.
   119      /// @param _minGasLimit Minimum gas limit for the bridge message on the other domain.
   120      /// @param _extraData   Optional data to forward to the other chain. Data supplied here will not
   121      ///                     be used to execute any code on the other chain and is only emitted as
   122      ///                     extra data for the convenience of off-chain tooling.
   123      function bridgeERC721(
   124          address _localToken,
   125          address _remoteToken,
   126          uint256 _tokenId,
   127          uint32 _minGasLimit,
   128          bytes calldata _extraData
   129      )
   130          external
   131      {
   132          // Modifier requiring sender to be EOA. This prevents against a user error that would occur
   133          // if the sender is a smart contract wallet that has a different address on the remote chain
   134          // (or doesn't have an address on the remote chain at all). The user would fail to receive
   135          // the NFT if they use this function because it sends the NFT to the same address as the
   136          // caller. This check could be bypassed by a malicious contract via initcode, but it takes
   137          // care of the user error we want to avoid.
   138          require(!Address.isContract(msg.sender), "ERC721Bridge: account is not externally owned");
   139  
   140          _initiateBridgeERC721(_localToken, _remoteToken, msg.sender, msg.sender, _tokenId, _minGasLimit, _extraData);
   141      }
   142  
   143      /// @notice Initiates a bridge of an NFT to some recipient's account on the other chain. Note
   144      ///         that the current owner of the token on this chain must approve this contract to
   145      ///         operate the NFT before it can be bridged.
   146      ///         **WARNING**: Do not bridge an ERC721 that was originally deployed on Optimism. This
   147      ///         bridge only supports ERC721s originally deployed on Ethereum. Users will need to
   148      ///         wait for the one-week challenge period to elapse before their Optimism-native NFT
   149      ///         can be refunded on L2.
   150      /// @param _localToken  Address of the ERC721 on this domain.
   151      /// @param _remoteToken Address of the ERC721 on the remote domain.
   152      /// @param _to          Address to receive the token on the other domain.
   153      /// @param _tokenId     Token ID to bridge.
   154      /// @param _minGasLimit Minimum gas limit for the bridge message on the other domain.
   155      /// @param _extraData   Optional data to forward to the other chain. Data supplied here will not
   156      ///                     be used to execute any code on the other chain and is only emitted as
   157      ///                     extra data for the convenience of off-chain tooling.
   158      function bridgeERC721To(
   159          address _localToken,
   160          address _remoteToken,
   161          address _to,
   162          uint256 _tokenId,
   163          uint32 _minGasLimit,
   164          bytes calldata _extraData
   165      )
   166          external
   167      {
   168          require(_to != address(0), "ERC721Bridge: nft recipient cannot be address(0)");
   169  
   170          _initiateBridgeERC721(_localToken, _remoteToken, msg.sender, _to, _tokenId, _minGasLimit, _extraData);
   171      }
   172  
   173      /// @notice Internal function for initiating a token bridge to the other domain.
   174      /// @param _localToken  Address of the ERC721 on this domain.
   175      /// @param _remoteToken Address of the ERC721 on the remote domain.
   176      /// @param _from        Address of the sender on this domain.
   177      /// @param _to          Address to receive the token on the other domain.
   178      /// @param _tokenId     Token ID to bridge.
   179      /// @param _minGasLimit Minimum gas limit for the bridge message on the other domain.
   180      /// @param _extraData   Optional data to forward to the other domain. Data supplied here will
   181      ///                     not be used to execute any code on the other domain and is only emitted
   182      ///                     as extra data for the convenience of off-chain tooling.
   183      function _initiateBridgeERC721(
   184          address _localToken,
   185          address _remoteToken,
   186          address _from,
   187          address _to,
   188          uint256 _tokenId,
   189          uint32 _minGasLimit,
   190          bytes calldata _extraData
   191      )
   192          internal
   193          virtual;
   194  }