Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
181 changes: 7 additions & 174 deletions packages/contracts-periphery/contracts/L1/L1ERC721Bridge.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,9 @@
pragma solidity 0.8.15;

import { ERC721Bridge } from "../universal/op-erc721/ERC721Bridge.sol";
import {
CrossDomainEnabled
} from "@eth-optimism/contracts/libraries/bridge/CrossDomainEnabled.sol";
import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import { Address } from "@openzeppelin/contracts/utils/Address.sol";
import { L2ERC721Bridge } from "../L2/L2ERC721Bridge.sol";
import { Semver } from "@eth-optimism/contracts-bedrock/contracts/universal/Semver.sol";
import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol";

/**
* @title L1ERC721Bridge
Expand All @@ -18,68 +13,6 @@ import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable
* acts as an escrow for ERC721 tokens deposted into L2.
*/
contract L1ERC721Bridge is ERC721Bridge, Semver {
/**
* @notice Emitted when an ERC721 bridge to the other network is initiated.
*
* @param localToken Address of the token on this domain.
* @param remoteToken Address of the token on the remote domain.
* @param from Address that initiated bridging action.
* @param to Address to receive the token.
* @param tokenId ID of the specific token deposited.
* @param extraData Extra data for use on the client-side.
*/
event ERC721BridgeInitiated(
address indexed localToken,
address indexed remoteToken,
address indexed from,
address to,
uint256 tokenId,
bytes extraData
);

/**
* @notice Emitted when an ERC721 bridge from the other network is finalized.
*
* @param localToken Address of the token on this domain.
* @param remoteToken Address of the token on the remote domain.
* @param from Address that initiated bridging action.
* @param to Address to receive the token.
* @param tokenId ID of the specific token deposited.
* @param extraData Extra data for use on the client-side.
*/
event ERC721BridgeFinalized(
address indexed localToken,
address indexed remoteToken,
address indexed from,
address to,
uint256 tokenId,
bytes extraData
);

/**
* @notice Emitted when an ERC721 bridge from the other network fails.
*
* @param localToken Address of the token on this domain.
* @param remoteToken Address of the token on the remote domain.
* @param from Address that initiated bridging action.
* @param to Address to receive the token.
* @param tokenId ID of the specific token deposited.
* @param extraData Extra data for use on the client-side.
*/
event ERC721BridgeFailed(
address indexed localToken,
address indexed remoteToken,
address indexed from,
address to,
uint256 tokenId,
bytes extraData
);

/**
* @notice Address of the bridge on the other network.
*/
address public otherBridge;

/**
* @notice Mapping of L1 token to L2 token to ID to boolean, indicating if the given L1 token
* by ID was deposited for a given L2 token.
Expand All @@ -94,98 +27,8 @@ contract L1ERC721Bridge is ERC721Bridge, Semver {
*/
constructor(address _messenger, address _otherBridge)
Semver(1, 0, 0)
CrossDomainEnabled(address(0))
{
initialize(_messenger, _otherBridge);
}

/**
* @param _messenger Address of the CrossDomainMessenger on this network.
* @param _otherBridge Address of the ERC721 bridge on the other network.
*/
function initialize(address _messenger, address _otherBridge) public initializer {
require(_messenger != address(0), "ERC721Bridge: messenger cannot be address(0)");
require(_otherBridge != address(0), "ERC721Bridge: other bridge cannot be address(0)");

messenger = _messenger;
otherBridge = _otherBridge;
}

/**
* @notice Initiates a bridge of an NFT to the caller's account on L2. Note that this function
* can only be called by EOAs. Smart contract wallets should use the `bridgeERC721To`
* function after ensuring that the recipient address on the remote chain exists. Also
* note that the current owner of the token on this chain must approve this contract to
* operate the NFT before it can be bridged.
*
* @param _localToken Address of the ERC721 on this domain.
* @param _remoteToken Address of the ERC721 on the remote domain.
* @param _tokenId Token ID to bridge.
* @param _minGasLimit Minimum gas limit for the bridge message on the other domain.
* @param _extraData Optional data to forward to L2. Data supplied here will not be used to
* execute any code on L2 and is only emitted as extra data for the
* convenience of off-chain tooling.
*/
function bridgeERC721(
address _localToken,
address _remoteToken,
uint256 _tokenId,
uint32 _minGasLimit,
bytes calldata _extraData
) external {
// Modifier requiring sender to be EOA. This prevents against a user error that would occur
// if the sender is a smart contract wallet that has a different address on the remote chain
// (or doesn't have an address on the remote chain at all). The user would fail to receive
// the NFT if they use this function because it sends the NFT to the same address as the
// caller. This check could be bypassed by a malicious contract via initcode, but it takes
// care of the user error we want to avoid.
require(!Address.isContract(msg.sender), "L1ERC721Bridge: account is not externally owned");

_initiateBridgeERC721(
_localToken,
_remoteToken,
msg.sender,
msg.sender,
_tokenId,
_minGasLimit,
_extraData
);
}

/**
* @notice Initiates a bridge of an NFT to some recipient's account on L2. Note that the current
* owner of the token on this chain must approve this contract to operate the NFT before
* it can be bridged.
*
* @param _localToken Address of the ERC721 on this domain.
* @param _remoteToken Address of the ERC721 on the remote domain.
* @param _to Address to receive the token on the other domain.
* @param _tokenId Token ID to bridge.
* @param _minGasLimit Minimum gas limit for the bridge message on the other domain.
* @param _extraData Optional data to forward to L2. Data supplied here will not be used to
* execute any code on L2 and is only emitted as extra data for the
* convenience of off-chain tooling.
*/
function bridgeERC721To(
address _localToken,
address _remoteToken,
address _to,
uint256 _tokenId,
uint32 _minGasLimit,
bytes calldata _extraData
) external {
require(_to != address(0), "ERC721Bridge: nft recipient cannot be address(0)");

_initiateBridgeERC721(
_localToken,
_remoteToken,
msg.sender,
_to,
_tokenId,
_minGasLimit,
_extraData
);
}
ERC721Bridge(_messenger, _otherBridge)
{}

/*************************
* Cross-chain Functions *
Expand All @@ -211,7 +54,7 @@ contract L1ERC721Bridge is ERC721Bridge, Semver {
address _to,
uint256 _tokenId,
bytes calldata _extraData
) external onlyFromCrossDomainAccount(otherBridge) {
) external onlyOtherBridge {
try this.completeOutboundTransfer(_localToken, _remoteToken, _to, _tokenId) {
if (_from == otherBridge) {
// The _from address is the address of the remote bridge if a transfer fails to be
Expand Down Expand Up @@ -247,7 +90,7 @@ contract L1ERC721Bridge is ERC721Bridge, Semver {

// Send the message to the L2 bridge.
// slither-disable-next-line reentrancy-events
sendCrossDomainMessage(otherBridge, 600_000, message);
messenger.sendMessage(otherBridge, message, 600_000);

// slither-disable-next-line reentrancy-events
emit ERC721BridgeFailed(_localToken, _remoteToken, _from, _to, _tokenId, _extraData);
Expand Down Expand Up @@ -291,17 +134,7 @@ contract L1ERC721Bridge is ERC721Bridge, Semver {
}

/**
* @notice Internal function for initiating a token bridge to the other domain.
*
* @param _localToken Address of the ERC721 on this domain.
* @param _remoteToken Address of the ERC721 on the remote domain.
* @param _from Address of the sender on this domain.
* @param _to Address to receive the token on the other domain.
* @param _tokenId Token ID to bridge.
* @param _minGasLimit Minimum gas limit for the bridge message on the other domain.
* @param _extraData Optional data to forward to L2. Data supplied here will not be used to
* execute any code on L2 and is only emitted as extra data for the
* convenience of off-chain tooling.
* @inheritdoc ERC721Bridge
*/
function _initiateBridgeERC721(
address _localToken,
Expand All @@ -311,7 +144,7 @@ contract L1ERC721Bridge is ERC721Bridge, Semver {
uint256 _tokenId,
uint32 _minGasLimit,
bytes calldata _extraData
) internal {
) internal override {
require(_remoteToken != address(0), "ERC721Bridge: remote token cannot be address(0)");

// Construct calldata for _l2Token.finalizeBridgeERC721(_to, _tokenId)
Expand All @@ -330,7 +163,7 @@ contract L1ERC721Bridge is ERC721Bridge, Semver {
IERC721(_localToken).transferFrom(_from, address(this), _tokenId);

// Send calldata into L2
sendCrossDomainMessage(otherBridge, _minGasLimit, message);
messenger.sendMessage(otherBridge, message, _minGasLimit);
emit ERC721BridgeInitiated(_localToken, _remoteToken, _from, _to, _tokenId, _extraData);
}
}
Loading