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 }