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 }