Commit 309a1957 authored by Mark Tyneway's avatar Mark Tyneway

op-node: update genesis command

Configures the ERC721 bridge

contracts-bedrock: port fixes

Ports fixes from https://github.com/ethereum-optimism/optimism/pull/3549

contracts-bedrock: remove ownable upgradable

contracts-bedrock: more fixes

contracts-bedrock: start tests

contracts-bedrock: port predeploy fixes
parent 8fc55444
......@@ -61,6 +61,7 @@ var Subcommands = cli.Commands{
ProxyAdmin: predeploys.DevProxyAdminAddr,
L1StandardBridgeProxy: predeploys.DevL1StandardBridgeAddr,
L1CrossDomainMessengerProxy: predeploys.DevL1CrossDomainMessengerAddr,
L1ERC721BridgeProxy: predeploys.DevL1ERC721BridgeAddr,
}
l2Genesis, err := genesis.BuildL2DeveloperGenesis(config, l1StartBlock, l2Addrs)
if err != nil {
......@@ -151,10 +152,16 @@ var Subcommands = cli.Commands{
if err != nil {
return err
}
l1ERC721BP, err := hh.GetDeployment("L1ERC721BridgeProxy")
if err != nil {
return err
}
l2Addrs := &genesis.L2Addresses{
ProxyAdmin: proxyAdmin.Address,
L1StandardBridgeProxy: l1SBP.Address,
L1CrossDomainMessengerProxy: l1XDMP.Address,
L1ERC721BridgeProxy: l1ERC721BP.Address,
}
l2Genesis, err := genesis.BuildL2DeveloperGenesis(config, l1StartBlock, l2Addrs)
if err != nil {
......
......@@ -10,7 +10,7 @@ import { Semver } from "../universal/Semver.sol";
* @title L1ERC721Bridge
* @notice The L1 ERC721 bridge is a contract which works together with the L2 ERC721 bridge to
* make it possible to transfer ERC721 tokens from Ethereum to Optimism. This contract
* acts as an escrow for ERC721 tokens deposted into L2.
* acts as an escrow for ERC721 tokens deposited into L2.
*/
contract L1ERC721Bridge is ERC721Bridge, Semver {
/**
......@@ -55,55 +55,6 @@ contract L1ERC721Bridge is ERC721Bridge, Semver {
uint256 _tokenId,
bytes calldata _extraData
) external onlyOtherBridge {
try this.completeOutboundTransfer(_localToken, _remoteToken, _to, _tokenId) {
// slither-disable-next-line reentrancy-events
emit ERC721BridgeFinalized(_localToken, _remoteToken, _from, _to, _tokenId, _extraData);
} catch {
// If the token ID for this L1/L2 NFT pair is not escrowed in the L1 Bridge or if
// another error occurred during finalization, we initiate a cross-domain message to
// send the NFT back to its original owner on L2. This can happen if an L2 native NFT is
// bridged to L1, or if a user mistakenly entered an incorrect L1 ERC721 address.
// In either case, we stop the process here and construct a withdrawal in which we
// flip the to and from addresses. This ensures that event-based accounting
// will indicate net-zero transfer to the recipient. The ERC721BridgeFailed event
// emitted below can also be used to identify this occurence.
bytes memory message = abi.encodeWithSelector(
L2ERC721Bridge.finalizeBridgeERC721.selector,
_remoteToken,
_localToken,
_to,
_from, // Refund the NFT to the original owner on the remote chain.
_tokenId,
_extraData
);
// Send the message to the L2 bridge.
// slither-disable-next-line reentrancy-events
messenger.sendMessage(otherBridge, message, 600_000);
// slither-disable-next-line reentrancy-events
emit ERC721BridgeFailed(_localToken, _remoteToken, _from, _to, _tokenId, _extraData);
}
}
/**
* @notice Completes an outbound token transfer. Public function, but can only be called by
* this contract. It's security critical that there be absolutely no way for anyone to
* trigger this function, except by explicit trigger within this contract. Used as a
* simple way to be able to try/catch any type of revert that could occur during an
* ERC721 mint/transfer.
*
* @param _localToken Address of the ERC721 on this chain.
* @param _remoteToken Address of the corresponding token on the remote chain.
* @param _to Address of the receiver.
* @param _tokenId ID of the token being deposited.
*/
function completeOutboundTransfer(
address _localToken,
address _remoteToken,
address _to,
uint256 _tokenId
) external onlySelf {
require(_localToken != address(this), "L1ERC721Bridge: local token cannot be self");
// Checks that the L1/L2 NFT pair has a token ID that is escrowed in the L1 Bridge.
......@@ -119,6 +70,9 @@ contract L1ERC721Bridge is ERC721Bridge, Semver {
// When a withdrawal is finalized on L1, the L1 Bridge transfers the NFT to the
// withdrawer.
IERC721(_localToken).safeTransferFrom(address(this), _to, _tokenId);
// slither-disable-next-line reentrancy-events
emit ERC721BridgeFinalized(_localToken, _remoteToken, _from, _to, _tokenId, _extraData);
}
/**
......
......@@ -51,65 +51,15 @@ contract L2ERC721Bridge is ERC721Bridge, Semver {
uint256 _tokenId,
bytes calldata _extraData
) external onlyOtherBridge {
try this.completeOutboundTransfer(_localToken, _remoteToken, _to, _tokenId) {
// slither-disable-next-line reentrancy-events
emit ERC721BridgeFinalized(_localToken, _remoteToken, _from, _to, _tokenId, _extraData);
} catch {
// Either the L2 token which is being deposited-into disagrees about the correct address
// of its L1 token, or does not support the correct interface.
// This should only happen if there is a malicious L2 token, or if a user somehow
// specified the wrong L2 token address to deposit into.
// There is no way to prevent malicious token contracts altogether, but this does limit
// user error and mitigate some forms of malicious contract behavior.
/// In either case, we stop the process here and construct a withdrawal in which we
// flip the to and from addresses. This ensures that event-based accounting
// will indicate net-zero transfer to the recipient. The ERC721BridgeFailed event
// emitted below can also be used to identify this occurence.
bytes memory message = abi.encodeWithSelector(
L1ERC721Bridge.finalizeBridgeERC721.selector,
_remoteToken,
_localToken,
_to,
_from, // Refund the NFT to the original owner on the remote chain.
_tokenId,
_extraData
);
// Send the message to the L1 bridge
// slither-disable-next-line reentrancy-events
messenger.sendMessage(otherBridge, message, 0);
// slither-disable-next-line reentrancy-events
emit ERC721BridgeFailed(_localToken, _remoteToken, _from, _to, _tokenId, _extraData);
}
}
/**
* @notice Completes an outbound token transfer. Public function, but can only be called by
* this contract. It's security critical that there be absolutely no way for anyone to
* trigger this function, except by explicit trigger within this contract. Used as a
* simple way to be able to try/catch any type of revert that could occur during an
* ERC721 mint/transfer.
*
* @param _localToken Address of the ERC721 on this chain.
* @param _remoteToken Address of the corresponding token on the remote chain.
* @param _to Address of the receiver.
* @param _tokenId ID of the token being deposited.
*/
function completeOutboundTransfer(
address _localToken,
address _remoteToken,
address _to,
uint256 _tokenId
) external onlySelf {
require(_localToken != address(this), "L2ERC721Bridge: local token cannot be self");
// Note that supportsInterface makes a callback to the _localToken address which is user
// provided.
require(
// Note that supportsInterface makes a callback to the _localToken address
// which is user provided.
ERC165Checker.supportsInterface(_localToken, type(IOptimismMintableERC721).interfaceId),
"L2ERC721Bridge: local token interface is not compliant"
);
require(
_remoteToken == IOptimismMintableERC721(_localToken).remoteToken(),
"L2ERC721Bridge: wrong remote token for Optimism Mintable ERC721 local token"
......@@ -118,6 +68,9 @@ contract L2ERC721Bridge is ERC721Bridge, Semver {
// When a deposit is finalized, we give the NFT with the same tokenId to the account
// on L2. Note that safeMint makes a callback to the _to address which is user provided.
IOptimismMintableERC721(_localToken).safeMint(_to, _tokenId);
// slither-disable-next-line reentrancy-events
emit ERC721BridgeFinalized(_localToken, _remoteToken, _from, _to, _tokenId, _extraData);
}
/**
......
......@@ -7,7 +7,10 @@ import { L2OutputOracle } from "../L1/L2OutputOracle.sol";
import { L2ToL1MessagePasser } from "../L2/L2ToL1MessagePasser.sol";
import { L1StandardBridge } from "../L1/L1StandardBridge.sol";
import { L2StandardBridge } from "../L2/L2StandardBridge.sol";
import { L1ERC721Bridge } from "../L1/L1ERC721Bridge.sol";
import { L2ERC721Bridge } from "../L2/L2ERC721Bridge.sol";
import { OptimismMintableERC20Factory } from "../universal/OptimismMintableERC20Factory.sol";
import { OptimismMintableERC721Factory } from "../universal/OptimismMintableERC721Factory.sol";
import { OptimismMintableERC20 } from "../universal/OptimismMintableERC20.sol";
import { OptimismPortal } from "../L1/OptimismPortal.sol";
import { L1CrossDomainMessenger } from "../L1/L1CrossDomainMessenger.sol";
......@@ -424,6 +427,40 @@ contract Bridge_Initializer is Messenger_Initializer {
}
}
contract ERC721Bridge_Initializer is Messenger_Initializer {
L1ERC721Bridge L1Bridge;
L2ERC721Bridge L2Bridge;
OptimismMintableERC721Factory factory;
function setUp() public virtual override {
super.setUp();
L1Bridge = new L1ERC721Bridge(
address(L1Messenger),
Predeploys.L2_ERC721_BRIDGE
);
L2ERC721Bridge l2b = new L2ERC721Bridge(
Predeploys.L2_CROSS_DOMAIN_MESSENGER,
address(L1Bridge)
);
vm.etch(Predeploys.L2_ERC721_BRIDGE, address(l2b).code);
L2Bridge = L2ERC721Bridge(Predeploys.L2_ERC721_BRIDGE);
OptimismMintableERC721Factory f = new OptimismMintableERC721Factory(
Predeploys.L2_ERC721_BRIDGE,
block.chainid
);
vm.etch(Predeploys.OPTIMISM_MINTABLE_ERC721_FACTORY, address(f).code);
factory = OptimismMintableERC721Factory(Predeploys.OPTIMISM_MINTABLE_ERC721_FACTORY);
vm.label(address(L1Bridge), "L1ERC721Bridge");
vm.label(Predeploys.L2_ERC721_BRIDGE, "L2ERC721Bridge");
vm.label(Predeploys.OPTIMISM_MINTABLE_ERC721_FACTORY, "OptimismMintableERC721Factory");
}
}
contract FFIInterface is Test {
function getFinalizeWithdrawalTransactionInputs(Types.WithdrawalTransaction memory _tx)
external
......
......@@ -28,24 +28,6 @@ abstract contract ERC721Bridge {
bytes extraData
);
/**
* @notice Emitted when an NFT is refunded to the owner after an ERC721 bridge from the other
* chain fails.
*
* @param localToken Address of the token on this domain.
* @param remoteToken Address of the token on the remote domain.
* @param to Address to receive the refunded token.
* @param tokenId ID of the specific token being refunded.
* @param extraData Extra data for use on the client-side.
*/
event ERC721Refunded(
address indexed localToken,
address indexed remoteToken,
address indexed to,
uint256 tokenId,
bytes extraData
);
/**
* @notice Emitted when an ERC721 bridge from the other network is finalized.
*
......@@ -65,25 +47,6 @@ abstract contract ERC721Bridge {
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 Messenger contract on this domain.
*/
......@@ -110,19 +73,14 @@ abstract contract ERC721Bridge {
_;
}
/**
* @notice Ensures that the caller is this contract.
*/
modifier onlySelf() {
require(msg.sender == address(this), "ERC721Bridge: function can only be called by self");
_;
}
/**
* @param _messenger Address of the CrossDomainMessenger on this network.
* @param _otherBridge Address of the ERC721 bridge on the other network.
*/
constructor(address _messenger, address _otherBridge) {
require(_messenger != address(0), "ERC721Bridge: messenger cannot be address(0)");
require(_otherBridge != address(0), "ERC721Bridge: other bridge cannot be address(0)");
messenger = CrossDomainMessenger(_messenger);
otherBridge = _otherBridge;
}
......
......@@ -46,7 +46,7 @@ contract OptimismMintableERC20Factory is Semver {
/**
* @param _bridge Address of the StandardBridge on this chain.
*/
constructor(address _bridge) Semver(0, 0, 1) {
constructor(address _bridge) Semver(1, 0, 0) {
bridge = _bridge;
}
......
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import {
OwnableUpgradeable
} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import { OptimismMintableERC721 } from "./OptimismMintableERC721.sol";
import { Semver } from "./Semver.sol";
......@@ -11,29 +8,34 @@ import { Semver } from "./Semver.sol";
* @title OptimismMintableERC721Factory
* @notice Factory contract for creating OptimismMintableERC721 contracts.
*/
contract OptimismMintableERC721Factory is Semver, OwnableUpgradeable {
contract OptimismMintableERC721Factory is Semver {
/**
* @notice Emitted whenever a new OptimismMintableERC721 contract is created.
*
* @param localToken Address of the token on the this domain.
* @param remoteToken Address of the token on the remote domain.
* @param deployer Address of the initiator of the deployment
*/
event OptimismMintableERC721Created(address indexed localToken, address indexed remoteToken);
event OptimismMintableERC721Created(
address indexed localToken,
address indexed remoteToken,
address deployer
);
/**
* @notice Address of the ERC721 bridge on this network.
*/
address public bridge;
address public immutable bridge;
/**
* @notice Chain ID for the remote network.
*/
uint256 public remoteChainId;
uint256 public immutable remoteChainId;
/**
* @notice Tracks addresses created by this factory.
*/
mapping(address => bool) public isStandardOptimismMintableERC721;
mapping(address => bool) public isOptimismMintableERC721;
/**
* @custom:semver 1.0.0
......@@ -41,15 +43,6 @@ contract OptimismMintableERC721Factory is Semver, OwnableUpgradeable {
* @param _bridge Address of the ERC721 bridge on this network.
*/
constructor(address _bridge, uint256 _remoteChainId) Semver(1, 0, 0) {
initialize(_bridge, _remoteChainId);
}
/**
* @notice Initializes the factory.
*
* @param _bridge Address of the ERC721 bridge on this network.
*/
function initialize(address _bridge, uint256 _remoteChainId) public initializer {
require(
_bridge != address(0),
"OptimismMintableERC721Factory: bridge cannot be address(0)"
......@@ -61,9 +54,6 @@ contract OptimismMintableERC721Factory is Semver, OwnableUpgradeable {
bridge = _bridge;
remoteChainId = _remoteChainId;
// Initialize upgradable OZ contracts
__Ownable_init();
}
/**
......@@ -73,30 +63,23 @@ contract OptimismMintableERC721Factory is Semver, OwnableUpgradeable {
* @param _name ERC721 name.
* @param _symbol ERC721 symbol.
*/
function createStandardOptimismMintableERC721(
function createOptimismMintableERC721(
address _remoteToken,
string memory _name,
string memory _symbol
) external {
) external returns (address) {
require(
_remoteToken != address(0),
"OptimismMintableERC721Factory: L1 token address cannot be address(0)"
);
require(
bridge != address(0),
"OptimismMintableERC721Factory: bridge address must be initialized"
address localToken = address(
new OptimismMintableERC721(bridge, remoteChainId, _remoteToken, _name, _symbol)
);
OptimismMintableERC721 localToken = new OptimismMintableERC721(
bridge,
remoteChainId,
_remoteToken,
_name,
_symbol
);
isOptimismMintableERC721[localToken] = true;
emit OptimismMintableERC721Created(localToken, _remoteToken, msg.sender);
isStandardOptimismMintableERC721[address(localToken)] = true;
emit OptimismMintableERC721Created(address(localToken), _remoteToken);
return localToken;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment