Commit 52ef1b63 authored by Maurelian's avatar Maurelian Committed by GitHub

Merge pull request #8365 from ethereum-optimism/ctb/erc721-pause

contracts-bedrock: ERC721Bridge pausable
parents 18d5273e e521392e
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -757,11 +757,11 @@ func NewL2ImmutableConfig(config *DeployConfig, block *types.Block) (*immutables
GovernanceToken: struct{}{},
LegacyMessagePasser: struct{}{},
L2ERC721Bridge: struct {
Messenger common.Address
OtherBridge common.Address
Messenger common.Address
}{
Messenger: predeploys.L2CrossDomainMessengerAddr,
OtherBridge: config.L1ERC721BridgeProxy,
Messenger: predeploys.L2CrossDomainMessengerAddr,
},
OptimismMintableERC721Factory: struct {
Bridge common.Address
......@@ -834,6 +834,10 @@ func NewL2StorageConfig(config *DeployConfig, block *types.Block) (state.Storage
"_initialized": 1,
"_initializing": false,
}
storage["L2ERC721Bridge"] = state.StorageValues{
"_initialized": 1,
"_initializing": false,
}
storage["L1Block"] = state.StorageValues{
"number": block.Number(),
"timestamp": block.Time(),
......
......@@ -45,8 +45,8 @@ type PredeploysImmutableConfig struct {
GovernanceToken struct{}
LegacyMessagePasser struct{}
L2ERC721Bridge struct {
Messenger common.Address
OtherBridge common.Address
Messenger common.Address
}
OptimismMintableERC721Factory struct {
Bridge common.Address
......@@ -230,15 +230,11 @@ func l2ImmutableDeployer(backend *backends.SimulatedBackend, opts *bind.Transact
}
_, tx, _, err = bindings.DeployOptimismMintableERC20Factory(opts, backend, bridge)
case "L2ERC721Bridge":
messenger, ok := deployment.Args[0].(common.Address)
if !ok {
return nil, fmt.Errorf("invalid type for messenger")
}
otherBridge, ok := deployment.Args[1].(common.Address)
otherBridge, ok := deployment.Args[0].(common.Address)
if !ok {
return nil, fmt.Errorf("invalid type for otherBridge")
}
_, tx, _, err = bindings.DeployL2ERC721Bridge(opts, backend, messenger, otherBridge)
_, tx, _, err = bindings.DeployL2ERC721Bridge(opts, backend, otherBridge)
case "OptimismMintableERC721Factory":
bridge, ok := deployment.Args[0].(common.Address)
if !ok {
......
......@@ -43,11 +43,11 @@ func TestBuildOptimism(t *testing.T) {
GovernanceToken: struct{}{},
LegacyMessagePasser: struct{}{},
L2ERC721Bridge: struct {
Messenger common.Address
OtherBridge common.Address
Messenger common.Address
}{
Messenger: common.HexToAddress("0x1234567890123456789012345678901234567890"),
OtherBridge: common.HexToAddress("0x1234567890123456789012345678901234567890"),
Messenger: predeploys.L2CrossDomainMessengerAddr,
},
OptimismMintableERC721Factory: struct {
Bridge common.Address
......
......@@ -42,7 +42,7 @@ library ChainAssertions {
checkL1StandardBridge({ _contracts: _prox, _isProxy: true });
checkL2OutputOracle(_prox, _cfg, _l2OutputOracleStartingTimestamp, _l2OutputOracleStartingBlockNumber);
checkOptimismMintableERC20Factory(_prox);
checkL1ERC721Bridge(_prox);
checkL1ERC721Bridge({ _contracts: _prox, _isProxy: true });
checkOptimismPortal({ _contracts: _prox, _cfg: _cfg, _isProxy: true });
checkProtocolVersions({ _contracts: _prox, _cfg: _cfg, _isProxy: true });
}
......@@ -137,12 +137,17 @@ library ChainAssertions {
}
/// @notice Asserts that the L1ERC721Bridge is setup correctly
function checkL1ERC721Bridge(Types.ContractSet memory _contracts) internal view {
function checkL1ERC721Bridge(Types.ContractSet memory _contracts, bool _isProxy) internal view {
L1ERC721Bridge bridge = L1ERC721Bridge(_contracts.L1ERC721Bridge);
require(address(bridge.MESSENGER()) == _contracts.L1CrossDomainMessenger);
require(address(bridge.messenger()) == _contracts.L1CrossDomainMessenger);
require(bridge.OTHER_BRIDGE() == Predeploys.L2_ERC721_BRIDGE);
require(bridge.otherBridge() == Predeploys.L2_ERC721_BRIDGE);
if (_isProxy) {
require(address(bridge.superchainConfig()) == _contracts.SuperchainConfig);
} else {
require(address(bridge.superchainConfig()) == address(0));
}
}
/// @notice Asserts the OptimismPortal is setup correctly
......
......@@ -678,8 +678,7 @@ contract Deploy is Deployer {
console.log("Deploying L1ERC721Bridge implementation");
address l1CrossDomainMessengerProxy = mustGetAddress("L1CrossDomainMessengerProxy");
L1ERC721Bridge bridge = new L1ERC721Bridge{ salt: _implSalt() }({
_messenger: l1CrossDomainMessengerProxy,
_otherBridge: Predeploys.L2_ERC721_BRIDGE
_messenger: l1CrossDomainMessengerProxy
});
save("L1ERC721Bridge", address(bridge));
......@@ -690,7 +689,8 @@ contract Deploy is Deployer {
// are always proxies.
Types.ContractSet memory contracts = _proxiesUnstrict();
contracts.L1ERC721Bridge = address(bridge);
ChainAssertions.checkL1ERC721Bridge(contracts);
ChainAssertions.checkL1ERC721Bridge({ _contracts: contracts, _isProxy: false });
addr_ = address(bridge);
}
......@@ -811,17 +811,19 @@ contract Deploy is Deployer {
ProxyAdmin proxyAdmin = ProxyAdmin(mustGetAddress("ProxyAdmin"));
address l1ERC721BridgeProxy = mustGetAddress("L1ERC721BridgeProxy");
address l1ERC721Bridge = mustGetAddress("L1ERC721Bridge");
address superchainConfigProxy = mustGetAddress("SuperchainConfigProxy");
_callViaSafe({
_target: address(proxyAdmin),
_data: abi.encodeCall(ProxyAdmin.upgrade, (payable(l1ERC721BridgeProxy), l1ERC721Bridge))
_upgradeAndCallViaSafe({
_proxy: payable(l1ERC721BridgeProxy),
_implementation: l1ERC721Bridge,
_innerCallData: abi.encodeCall(L1ERC721Bridge.initialize, (SuperchainConfig(superchainConfigProxy)))
});
L1ERC721Bridge bridge = L1ERC721Bridge(l1ERC721BridgeProxy);
string memory version = bridge.version();
console.log("L1ERC721Bridge version: %s", version);
ChainAssertions.checkL1ERC721Bridge(_proxies());
ChainAssertions.checkL1ERC721Bridge({ _contracts: _proxies(), _isProxy: true });
}
/// @notice Ininitialize the OptimismMintableERC20Factory
......
......@@ -16,8 +16,8 @@
"sourceCodeHash": "0x508ac7c5628d226f5e3ec6593f6c28aab969f70223c678eb0e870155d24b1da4"
},
"src/L1/L1ERC721Bridge.sol": {
"initCodeHash": "0xb9d7955f46b634725a57269f7004ee638850a4d3ecd20ae2d6fdcd4694430a75",
"sourceCodeHash": "0x76ab62d44cf4efdc1db7042e75738b6d187682b0bdfb4160f5d0dc09eab55b4f"
"initCodeHash": "0xca27bfb4742b3b44e9adb6018b0917ae3392e67e5f8242ab24828dd37245af16",
"sourceCodeHash": "0x60f2d99be1211f6d64fb0ae4d0ac4819bdbaefd2ebf4a49e95cbad27cf691fac"
},
"src/L1/L1StandardBridge.sol": {
"initCodeHash": "0x56a7fbc6807e1c4fa818a9706a12545f86499ca4269b0dc84c328ec632933eaf",
......@@ -64,8 +64,8 @@
"sourceCodeHash": "0x167f90f8b75e6ae2d5326d5245384d1ec9dd28222e78388b9e9b11aea8da7348"
},
"src/L2/L2ERC721Bridge.sol": {
"initCodeHash": "0x8c7dc436d8150514bca8a322e3e45e53fb98a6c1f0f22d6a9ba6cf1f479e0e56",
"sourceCodeHash": "0x448b01aa8e9e79782766134290d54a3557135c9d39357bfbbf1d2672687518ac"
"initCodeHash": "0x3b7357a0972db8cde39f6583d87291fc64ef424737498cf6927f8fcb3d7e368d",
"sourceCodeHash": "0x2b9b106a924ac21d6e8f57219e7f7c72f6581ea1cfd5cff77d79a7f144f0ebd2"
},
"src/L2/L2StandardBridge.sol": {
"initCodeHash": "0x2d6b3e6fee86b9edbfe56bc4e201d1fe0cd70db424121f0059772cd9a46cdb16",
......
......@@ -5,11 +5,6 @@
"internalType": "address",
"name": "_messenger",
"type": "address"
},
{
"internalType": "address",
"name": "_otherBridge",
"type": "address"
}
],
"stateMutability": "nonpayable",
......@@ -101,6 +96,19 @@
"name": "ERC721BridgeInitiated",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint8",
"name": "version",
"type": "uint8"
}
],
"name": "Initialized",
"type": "event"
},
{
"inputs": [],
"name": "MESSENGER",
......@@ -265,6 +273,19 @@
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "contract SuperchainConfig",
"name": "_superchainConfig",
"type": "address"
}
],
"name": "initialize",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "messenger",
......@@ -291,6 +312,32 @@
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "paused",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "superchainConfig",
"outputs": [
{
"internalType": "contract SuperchainConfig",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "version",
......
[
{
"inputs": [
{
"internalType": "address",
"name": "_messenger",
"type": "address"
},
{
"internalType": "address",
"name": "_otherBridge",
......@@ -101,6 +96,19 @@
"name": "ERC721BridgeInitiated",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint8",
"name": "version",
"type": "uint8"
}
],
"name": "Initialized",
"type": "event"
},
{
"inputs": [],
"name": "MESSENGER",
......@@ -236,6 +244,13 @@
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "initialize",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "messenger",
......@@ -262,6 +277,19 @@
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "paused",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "version",
......
[
{
"bytes": "1568",
"label": "__gap",
"bytes": "1",
"label": "_initialized",
"offset": 0,
"slot": "0",
"type": "uint256[49]"
"type": "uint8"
},
{
"bytes": "1",
"label": "_initializing",
"offset": 1,
"slot": "0",
"type": "bool"
},
{
"bytes": "1536",
"label": "__gap",
"offset": 0,
"slot": "1",
"type": "uint256[48]"
},
{
"bytes": "32",
......@@ -12,5 +26,12 @@
"offset": 0,
"slot": "49",
"type": "mapping(address => mapping(address => mapping(uint256 => bool)))"
},
{
"bytes": "20",
"label": "superchainConfig",
"offset": 0,
"slot": "50",
"type": "contract SuperchainConfig"
}
]
\ No newline at end of file
[
{
"bytes": "1568",
"label": "__gap",
"bytes": "1",
"label": "_initialized",
"offset": 0,
"slot": "0",
"type": "uint256[49]"
"type": "uint8"
},
{
"bytes": "1",
"label": "_initializing",
"offset": 1,
"slot": "0",
"type": "bool"
},
{
"bytes": "1536",
"label": "__gap",
"offset": 0,
"slot": "1",
"type": "uint256[48]"
}
]
\ No newline at end of file
......@@ -8,6 +8,7 @@ import { ISemver } from "src/universal/ISemver.sol";
import { Predeploys } from "src/libraries/Predeploys.sol";
import { CrossDomainMessenger } from "src/universal/CrossDomainMessenger.sol";
import { Constants } from "src/libraries/Constants.sol";
import { SuperchainConfig } from "src/L1/SuperchainConfig.sol";
/// @title L1ERC721Bridge
/// @notice The L1 ERC721 bridge is a contract which works together with the L2 ERC721 bridge to
......@@ -18,14 +19,29 @@ contract L1ERC721Bridge is ERC721Bridge, ISemver {
/// by ID was deposited for a given L2 token.
mapping(address => mapping(address => mapping(uint256 => bool))) public deposits;
/// @notice Address of the SuperchainConfig contract.
SuperchainConfig public superchainConfig;
/// @notice Semantic version.
/// @custom:semver 1.5.0
string public constant version = "1.5.0";
/// @custom:semver 2.0.0
string public constant version = "2.0.0";
/// @notice Constructs the L1ERC721Bridge contract.
/// @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) ERC721Bridge(_messenger, _otherBridge) { }
constructor(address _messenger) ERC721Bridge(_messenger, Predeploys.L2_ERC721_BRIDGE) {
initialize(SuperchainConfig(address(0)));
}
/// @notice Initializes the contract.
/// @param _superchainConfig Address of the SuperchainConfig contract on this network.
function initialize(SuperchainConfig _superchainConfig) public initializer {
superchainConfig = _superchainConfig;
}
/// @inheritdoc ERC721Bridge
function paused() public view override returns (bool) {
return superchainConfig.paused();
}
/// @notice Completes an ERC721 bridge from the other domain and sends the ERC721 token to the
/// recipient on this domain.
......@@ -48,6 +64,7 @@ contract L1ERC721Bridge is ERC721Bridge, ISemver {
external
onlyOtherBridge
{
require(paused() == false, "L1ERC721Bridge: paused");
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.
......
......@@ -20,13 +20,18 @@ import { Predeploys } from "src/libraries/Predeploys.sol";
/// wait for the one-week challenge period to elapse before their Optimism-native NFT
/// can be refunded on L2.
contract L2ERC721Bridge is ERC721Bridge, ISemver {
/// @custom:semver 1.5.0
string public constant version = "1.5.0";
/// @custom:semver 1.6.0
string public constant version = "1.6.0";
/// @notice Constructs the L2ERC721Bridge contract.
/// @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) ERC721Bridge(_messenger, _otherBridge) { }
constructor(address _otherBridge) ERC721Bridge(Predeploys.L2_CROSS_DOMAIN_MESSENGER, _otherBridge) {
initialize();
}
/// @notice Initializes the contract. This is a noop in the implementation but included to ensure that
/// the contract cannot be initialized a second time.
function initialize() public initializer { }
/// @notice Completes an ERC721 bridge from the other domain and sends the ERC721 token to the
/// recipient on this domain.
......
......@@ -2,12 +2,13 @@
pragma solidity 0.8.15;
import { CrossDomainMessenger } from "src/universal/CrossDomainMessenger.sol";
import { SuperchainConfig } from "src/L1/SuperchainConfig.sol";
import { Address } from "@openzeppelin/contracts/utils/Address.sol";
import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol";
/// @title ERC721Bridge
/// @notice ERC721Bridge is a base contract for the L1 and L2 ERC721 bridges.
abstract contract ERC721Bridge {
abstract contract ERC721Bridge is Initializable {
/// @notice Messenger contract on this domain. This will be removed in the
/// future, use `messenger` instead.
/// @custom:legacy
......@@ -19,7 +20,7 @@ abstract contract ERC721Bridge {
address public immutable OTHER_BRIDGE;
/// @notice Reserve extra slots (to a total of 50) in the storage layout for future upgrades.
uint256[49] private __gap;
uint256[48] private __gap;
/// @notice Emitted when an ERC721 bridge to the other network is initiated.
/// @param localToken Address of the token on this domain.
......@@ -84,6 +85,14 @@ abstract contract ERC721Bridge {
return OTHER_BRIDGE;
}
/// @notice This function should return true if the contract is paused.
/// On L1 this function will check the SuperchainConfig for its paused status.
/// On L2 this function should be a no-op.
/// @return Whether or not the contract is paused.
function paused() public view virtual returns (bool) {
return false;
}
/// @notice Initiates a bridge of an NFT to the caller's account on the other chain. 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
......
......@@ -82,6 +82,9 @@ contract Authorization_Test is CommonTest {
_addSpec("L1ERC721Bridge", _getSel("messenger()"), false);
_addSpec("L1ERC721Bridge", _getSel("otherBridge()"), false);
_addSpec("L1ERC721Bridge", _getSel("version()"), false);
_addSpec("L1ERC721Bridge", _getSel("superchainConfig()"), false);
_addSpec("L1ERC721Bridge", _getSel("paused()"), false);
_addSpec("L1ERC721Bridge", _getSel("initialize(address)"), false);
// L1StandardBridge
_addSpec("L1StandardBridge", _getSel("MESSENGER()"), false);
......
......@@ -4,27 +4,37 @@ pragma solidity 0.8.15;
import { CommonTest } from "test/setup/CommonTest.sol";
import { SuperchainConfig } from "src/L1/SuperchainConfig.sol";
/// @dev These tests are somewhat redundant with tests in the SuperchainConfig and other pausable contracts, however
/// it is worthwhile to pull them into one location to ensure that the behavior is consistent.
contract ExtendedPause_Test is CommonTest {
/// @dev Tests that other contracts are paused when the superchain config is paused
/// This test is somewhat redundant with tests in the SuperchainConfig and other pausable contracts, however
/// it is worthwhile to pull them into one location to ensure that the behavior is consistent.
function test_pause_fullSystem_succeeds() external {
function test_pause_fullSystem_succeeds() public {
assertFalse(superchainConfig.paused());
assertEq(l1CrossDomainMessenger.paused(), superchainConfig.paused());
vm.prank(superchainConfig.guardian());
superchainConfig.pause("identifier");
// validate the paused state
assertTrue(superchainConfig.paused());
assertEq(l1CrossDomainMessenger.paused(), superchainConfig.paused());
assertTrue(optimismPortal.paused());
assertTrue(l1CrossDomainMessenger.paused());
assertTrue(l1StandardBridge.paused());
assertEq(l1StandardBridge.paused(), superchainConfig.paused());
assertTrue(l1ERC721Bridge.paused());
}
/// @dev Tests that other contracts are unpaused when the superchain config is paused and then unpaused.
function test_unpause_fullSystem_succeeds() external {
// first use the test above to pause the system
test_pause_fullSystem_succeeds();
try SuperchainConfig(address(l1ERC721Bridge)).paused() {
revert("The L1ERC721Bridge has a paused() function, but is not tested as part of the ExtendedPause");
} catch (bytes memory) {
assertTrue(true);
}
vm.prank(superchainConfig.guardian());
superchainConfig.unpause();
// validate the unpaused state
assertFalse(superchainConfig.paused());
assertFalse(optimismPortal.paused());
assertFalse(l1CrossDomainMessenger.paused());
assertFalse(l1StandardBridge.paused());
assertFalse(l1ERC721Bridge.paused());
}
}
......@@ -8,6 +8,8 @@ import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
// Target contract dependencies
import { L2ERC721Bridge } from "src/L2/L2ERC721Bridge.sol";
import { Predeploys } from "src/libraries/Predeploys.sol";
import { SuperchainConfig } from "src/L1/SuperchainConfig.sol";
import { CrossDomainMessenger } from "src/universal/CrossDomainMessenger.sol";
// Target contract
import { L1ERC721Bridge } from "src/L1/L1ERC721Bridge.sol";
......@@ -65,6 +67,7 @@ contract L1ERC721Bridge_Test is Bridge_Initializer {
assertEq(address(l1ERC721Bridge.OTHER_BRIDGE()), Predeploys.L2_ERC721_BRIDGE);
assertEq(address(l1ERC721Bridge.messenger()), address(l1CrossDomainMessenger));
assertEq(address(l1ERC721Bridge.otherBridge()), Predeploys.L2_ERC721_BRIDGE);
assertEq(address(l1ERC721Bridge.superchainConfig()), address(superchainConfig));
}
/// @dev Tests that the ERC721 can be bridged successfully.
......@@ -295,3 +298,62 @@ contract L1ERC721Bridge_Test is Bridge_Initializer {
l1ERC721Bridge.finalizeBridgeERC721(address(localToken), address(remoteToken), alice, alice, tokenId, hex"5678");
}
}
contract L1ERC721Bridge_Pause_Test is Bridge_Initializer {
/// @dev Verifies that the `paused` accessor returns the same value as the `paused` function of the
/// `superchainConfig`.
function test_paused_succeeds() external {
assertEq(l1ERC721Bridge.paused(), superchainConfig.paused());
}
/// @dev Ensures that the `paused` function of the bridge contract actually calls the `paused` function of the
/// `superchainConfig`.
function test_pause_callsSuperchainConfig_succeeds() external {
vm.expectCall(address(superchainConfig), abi.encodeWithSelector(SuperchainConfig.paused.selector));
l1ERC721Bridge.paused();
}
/// @dev Checks that the `paused` state of the bridge matches the `paused` state of the `superchainConfig` after
/// it's been changed.
function test_pause_matchesSuperchainConfig_succeeds() external {
assertFalse(l1StandardBridge.paused());
assertEq(l1StandardBridge.paused(), superchainConfig.paused());
vm.prank(superchainConfig.guardian());
superchainConfig.pause("identifier");
assertTrue(l1StandardBridge.paused());
assertEq(l1StandardBridge.paused(), superchainConfig.paused());
}
}
contract L1ERC721Bridge_Pause_TestFail is Bridge_Initializer {
/// @dev Sets up the test by pausing the bridge, giving ether to the bridge and mocking
/// the calls to the xDomainMessageSender so that it returns the correct value.
function setUp() public override {
super.setUp();
vm.prank(superchainConfig.guardian());
superchainConfig.pause("identifier");
assertTrue(l1ERC721Bridge.paused());
vm.mockCall(
address(l1ERC721Bridge.messenger()),
abi.encodeWithSelector(CrossDomainMessenger.xDomainMessageSender.selector),
abi.encode(address(l1ERC721Bridge.otherBridge()))
);
}
// @dev Ensures that the `bridgeERC721` function reverts when the bridge is paused.
function test_pause_finalizeBridgeERC721_reverts() external {
vm.prank(address(l1ERC721Bridge.messenger()));
vm.expectRevert("L1ERC721Bridge: paused");
l1ERC721Bridge.finalizeBridgeERC721({
_localToken: address(0),
_remoteToken: address(0),
_from: address(0),
_to: address(0),
_tokenId: 0,
_extraData: hex""
});
}
}
......@@ -79,6 +79,12 @@ contract L2ERC721Bridge_Test is Bridge_Initializer {
assertEq(address(l2ERC721Bridge.otherBridge()), address(l1ERC721Bridge));
}
/// @dev Ensures that the L2ERC721Bridge is always not paused. The pausability
/// happens on L1 and not L2.
function test_paused_succeeds() external {
assertFalse(l2ERC721Bridge.paused());
}
/// @dev Tests that `bridgeERC721` correctly bridges a token and
/// burns it on the origin chain.
function test_bridgeERC721_succeeds() public {
......
......@@ -119,6 +119,22 @@ contract Initializer_Test is Bridge_Initializer {
initializedSlotVal: deploy.loadInitializedSlot("L2CrossDomainMessenger", false)
})
);
// L1ERC721Bridge
contracts.push(
InitializeableContract({
target: address(l1ERC721Bridge),
initCalldata: abi.encodeCall(l1ERC721Bridge.initialize, (superchainConfig)),
initializedSlotVal: deploy.loadInitializedSlot("L1ERC721Bridge", true)
})
);
// L2ERC721Bridge
contracts.push(
InitializeableContract({
target: address(l2ERC721Bridge),
initCalldata: abi.encodeCall(l2ERC721Bridge.initialize, ()),
initializedSlotVal: deploy.loadInitializedSlot("L2ERC721Bridge", false)
})
);
}
/// @notice Tests that:
......
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