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

     1  // SPDX-License-Identifier: MIT
     2  pragma solidity 0.8.15;
     3  
     4  import { ERC721Bridge } from "src/universal/ERC721Bridge.sol";
     5  import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
     6  import { L2ERC721Bridge } from "src/L2/L2ERC721Bridge.sol";
     7  import { ISemver } from "src/universal/ISemver.sol";
     8  import { Predeploys } from "src/libraries/Predeploys.sol";
     9  import { CrossDomainMessenger } from "src/universal/CrossDomainMessenger.sol";
    10  import { StandardBridge } from "src/universal/StandardBridge.sol";
    11  import { Constants } from "src/libraries/Constants.sol";
    12  import { SuperchainConfig } from "src/L1/SuperchainConfig.sol";
    13  
    14  /// @title L1ERC721Bridge
    15  /// @notice The L1 ERC721 bridge is a contract which works together with the L2 ERC721 bridge to
    16  ///         make it possible to transfer ERC721 tokens from Ethereum to Optimism. This contract
    17  ///         acts as an escrow for ERC721 tokens deposited into L2.
    18  contract L1ERC721Bridge is ERC721Bridge, ISemver {
    19      /// @notice Mapping of L1 token to L2 token to ID to boolean, indicating if the given L1 token
    20      ///         by ID was deposited for a given L2 token.
    21      mapping(address => mapping(address => mapping(uint256 => bool))) public deposits;
    22  
    23      /// @notice Address of the SuperchainConfig contract.
    24      SuperchainConfig public superchainConfig;
    25  
    26      /// @notice Semantic version.
    27      /// @custom:semver 2.1.0
    28      string public constant version = "2.1.0";
    29  
    30      /// @notice Constructs the L1ERC721Bridge contract.
    31      constructor() ERC721Bridge() {
    32          initialize({ _messenger: CrossDomainMessenger(address(0)), _superchainConfig: SuperchainConfig(address(0)) });
    33      }
    34  
    35      /// @notice Initializes the contract.
    36      /// @param _messenger   Contract of the CrossDomainMessenger on this network.
    37      /// @param _superchainConfig Contract of the SuperchainConfig contract on this network.
    38      function initialize(CrossDomainMessenger _messenger, SuperchainConfig _superchainConfig) public initializer {
    39          superchainConfig = _superchainConfig;
    40          __ERC721Bridge_init({
    41              _messenger: _messenger,
    42              _otherBridge: StandardBridge(payable(Predeploys.L2_ERC721_BRIDGE))
    43          });
    44      }
    45  
    46      /// @inheritdoc ERC721Bridge
    47      function paused() public view override returns (bool) {
    48          return superchainConfig.paused();
    49      }
    50  
    51      /// @notice Completes an ERC721 bridge from the other domain and sends the ERC721 token to the
    52      ///         recipient on this domain.
    53      /// @param _localToken  Address of the ERC721 token on this domain.
    54      /// @param _remoteToken Address of the ERC721 token on the other domain.
    55      /// @param _from        Address that triggered the bridge on the other domain.
    56      /// @param _to          Address to receive the token on this domain.
    57      /// @param _tokenId     ID of the token being deposited.
    58      /// @param _extraData   Optional data to forward to L2.
    59      ///                     Data supplied here will not be used to execute any code on L2 and is
    60      ///                     only emitted as extra data for the convenience of off-chain tooling.
    61      function finalizeBridgeERC721(
    62          address _localToken,
    63          address _remoteToken,
    64          address _from,
    65          address _to,
    66          uint256 _tokenId,
    67          bytes calldata _extraData
    68      )
    69          external
    70          onlyOtherBridge
    71      {
    72          require(paused() == false, "L1ERC721Bridge: paused");
    73          require(_localToken != address(this), "L1ERC721Bridge: local token cannot be self");
    74  
    75          // Checks that the L1/L2 NFT pair has a token ID that is escrowed in the L1 Bridge.
    76          require(
    77              deposits[_localToken][_remoteToken][_tokenId] == true,
    78              "L1ERC721Bridge: Token ID is not escrowed in the L1 Bridge"
    79          );
    80  
    81          // Mark that the token ID for this L1/L2 token pair is no longer escrowed in the L1
    82          // Bridge.
    83          deposits[_localToken][_remoteToken][_tokenId] = false;
    84  
    85          // When a withdrawal is finalized on L1, the L1 Bridge transfers the NFT to the
    86          // withdrawer.
    87          IERC721(_localToken).safeTransferFrom({ from: address(this), to: _to, tokenId: _tokenId });
    88  
    89          // slither-disable-next-line reentrancy-events
    90          emit ERC721BridgeFinalized(_localToken, _remoteToken, _from, _to, _tokenId, _extraData);
    91      }
    92  
    93      /// @inheritdoc ERC721Bridge
    94      function _initiateBridgeERC721(
    95          address _localToken,
    96          address _remoteToken,
    97          address _from,
    98          address _to,
    99          uint256 _tokenId,
   100          uint32 _minGasLimit,
   101          bytes calldata _extraData
   102      )
   103          internal
   104          override
   105      {
   106          require(_remoteToken != address(0), "L1ERC721Bridge: remote token cannot be address(0)");
   107  
   108          // Construct calldata for _l2Token.finalizeBridgeERC721(_to, _tokenId)
   109          bytes memory message = abi.encodeWithSelector(
   110              L2ERC721Bridge.finalizeBridgeERC721.selector, _remoteToken, _localToken, _from, _to, _tokenId, _extraData
   111          );
   112  
   113          // Lock token into bridge
   114          deposits[_localToken][_remoteToken][_tokenId] = true;
   115          IERC721(_localToken).transferFrom({ from: _from, to: address(this), tokenId: _tokenId });
   116  
   117          // Send calldata into L2
   118          messenger.sendMessage({ _target: address(otherBridge), _message: message, _minGasLimit: _minGasLimit });
   119          emit ERC721BridgeInitiated(_localToken, _remoteToken, _from, _to, _tokenId, _extraData);
   120      }
   121  }