From 32c44ca0ad9dea27980a52f47c5971e66bb5bac7 Mon Sep 17 00:00:00 2001 From: tak Date: Mon, 25 Mar 2024 12:53:00 +0900 Subject: [PATCH 1/9] integrate with system director --- .../scripts/oasys/L1/build/Build.s.sol | 27 +- .../src/L1/L1ERC721Bridge.sol | 8 +- .../src/oasys/L1/build/BuildProxy.sol | 28 -- .../src/oasys/L1/build/L1BuildAgent.sol | 310 +++++++++++------- .../src/oasys/L1/build/PortalSender.sol | 31 ++ .../oasys/L1/build/interfaces/IBuildProxy.sol | 21 -- .../L1/build/interfaces/IL1BuildAgent.sol | 26 +- .../OasysL1ERC721BridgeLegacySpacer.sol | 28 ++ .../oasys/L1/rollup/OasysL2OutputOracle.sol | 20 ++ .../src/universal/ERC721Bridge.sol | 3 +- .../contracts-bedrock/test/L1BuildAgent.t.sol | 40 +-- .../test/setup/oasys/SetupL1BuildAgent.sol | 33 +- 12 files changed, 342 insertions(+), 233 deletions(-) create mode 100644 packages/contracts-bedrock/src/oasys/L1/build/PortalSender.sol create mode 100644 packages/contracts-bedrock/src/oasys/L1/messaging/OasysL1ERC721BridgeLegacySpacer.sol diff --git a/packages/contracts-bedrock/scripts/oasys/L1/build/Build.s.sol b/packages/contracts-bedrock/scripts/oasys/L1/build/Build.s.sol index 0161a50a9..18ad6c0bb 100644 --- a/packages/contracts-bedrock/scripts/oasys/L1/build/Build.s.sol +++ b/packages/contracts-bedrock/scripts/oasys/L1/build/Build.s.sol @@ -302,6 +302,7 @@ contract Build is Script { // construct a build configuration. buildCfg = IL1BuildAgent.BuildConfig({ + legacyAddressManager: address(0), finalSystemOwner: deployCfg.finalSystemOwner, l2OutputOracleProposer: deployCfg.l2OutputOracleProposer, l2OutputOracleChallenger: deployCfg.l2OutputOracleChallenger, @@ -334,24 +335,26 @@ contract Build is Script { deposit.deposit{ value: 1 ether }(msg.sender); // build L2. - (address proxyAdmin, address[7] memory proxys,, address batchInbox, address addressManager) = - agent.build(deployCfg.l2ChainID, buildCfg); + (IL1BuildAgent.BuiltAddressList memory results,) = agent.build(deployCfg.l2ChainID, buildCfg); vm.stopBroadcast(); // set deployed addresses - deployCfg.optimismPortalProxy = proxys[0]; - deployCfg.systemConfigProxy = proxys[2]; - deployCfg.l1CrossDomainMessengerProxy = proxys[3]; - deployCfg.l1StandardBridgeProxy = proxys[4]; - deployCfg.l1ERC721BridgeProxy = proxys[5]; - deployCfg.batchInboxAddress = batchInbox; - address protocolVersions = proxys[6]; - address l2OutputOracleProxy = proxys[1]; + deployCfg.optimismPortalProxy = results.oasysPortal; + deployCfg.systemConfigProxy = results.systemConfig; + deployCfg.l1CrossDomainMessengerProxy = results.l1CrossDomainMessenger; + deployCfg.l1StandardBridgeProxy = results.l1StandardBridge; + deployCfg.l1ERC721BridgeProxy = results.l1ERC721Bridge; + deployCfg.batchInboxAddress = results.batchInbox; // output opstack configuration files. string memory deployCfgJson = _deployConfigJson("DeployConfig"); - string memory addressesJson = - _addressesJson("deployed", proxyAdmin, l2OutputOracleProxy, addressManager, protocolVersions); + string memory addressesJson = _addressesJson( + "deployed", + results.proxyAdmin, + results.oasysL2OutputOracle, + address(0), + results.protocolVersions + ); // output to the `./tmp/L1BuildAgent/Build/latest` directory _writeJson(deployCfgJson, Path.buildLatestOutDir(), "/deploy-config.json"); diff --git a/packages/contracts-bedrock/src/L1/L1ERC721Bridge.sol b/packages/contracts-bedrock/src/L1/L1ERC721Bridge.sol index 68e7d29d8..5f3453281 100644 --- a/packages/contracts-bedrock/src/L1/L1ERC721Bridge.sol +++ b/packages/contracts-bedrock/src/L1/L1ERC721Bridge.sol @@ -13,10 +13,16 @@ import { Constants } from "src/libraries/Constants.sol"; /// @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 deposited into L2. +/// ------------------------------ +/// Oasys made a change to this contract +// The change is comment out in the mapping of deposits contract L1ERC721Bridge is ERC721Bridge, ISemver { /// @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. - mapping(address => mapping(address => mapping(uint256 => bool))) public deposits; + /// ------------------------- + // Move this mapping to the `L1ERC721BridgeLegacySpacer` contract + // To follow the storage layout of Oasys Legacy L1ERC721Bridge + // mapping(address => mapping(address => mapping(uint256 => bool))) public deposits; /// @notice Semantic version. /// @custom:semver 1.5.0 diff --git a/packages/contracts-bedrock/src/oasys/L1/build/BuildProxy.sol b/packages/contracts-bedrock/src/oasys/L1/build/BuildProxy.sol index 4c31b3455..2de53e385 100644 --- a/packages/contracts-bedrock/src/oasys/L1/build/BuildProxy.sol +++ b/packages/contracts-bedrock/src/oasys/L1/build/BuildProxy.sol @@ -5,9 +5,6 @@ import { ProxyAdmin } from "src/universal/ProxyAdmin.sol"; import { ISemver } from "src/universal/ISemver.sol"; import { IBuildProxy } from "src/oasys/L1/build/interfaces/IBuildProxy.sol"; import { Proxy } from "src/universal/Proxy.sol"; -import { L1ChugSplashProxy } from "src/legacy/L1ChugSplashProxy.sol"; -import { ResolvedDelegateProxy } from "src/legacy/ResolvedDelegateProxy.sol"; -import { AddressManager } from "src/legacy/AddressManager.sol"; /// @notice Hold the deployment bytecode /// Separate from build contract to avoid bytecode size limitations @@ -16,12 +13,6 @@ contract BuildProxy is IBuildProxy, ISemver { /// @custom:semver 1.0.0 string public constant version = "1.0.0"; - /// @inheritdoc IBuildProxy - function deployAddressManager(address owner) external returns (AddressManager addressManager) { - addressManager = new AddressManager(); - addressManager.transferOwnership(owner); - } - /// @inheritdoc IBuildProxy function deployProxyAdmin(address owner) external returns (ProxyAdmin proxyAdmin) { proxyAdmin = new ProxyAdmin({ _owner: owner }); @@ -31,23 +22,4 @@ contract BuildProxy is IBuildProxy, ISemver { function deployERC1967Proxy(address admin) external returns (Proxy proxy) { proxy = new Proxy({ _admin: admin }); } - - /// @inheritdoc IBuildProxy - function deployChugProxy(address owner) external returns (L1ChugSplashProxy proxy) { - proxy = new L1ChugSplashProxy({ _owner: owner }); - } - - /// @inheritdoc IBuildProxy - function deployResolvedProxy( - address addressManager, - string memory implementationName - ) - external - returns (ResolvedDelegateProxy proxy) - { - proxy = new ResolvedDelegateProxy({ - _addressManager: AddressManager(addressManager), - _implementationName: implementationName - }); - } } diff --git a/packages/contracts-bedrock/src/oasys/L1/build/L1BuildAgent.sol b/packages/contracts-bedrock/src/oasys/L1/build/L1BuildAgent.sol index 923453d04..9e05283ce 100644 --- a/packages/contracts-bedrock/src/oasys/L1/build/L1BuildAgent.sol +++ b/packages/contracts-bedrock/src/oasys/L1/build/L1BuildAgent.sol @@ -23,6 +23,8 @@ import { IBuildL1ERC721Bridge } from "src/oasys/L1/build/interfaces/IBuildL1ERC7 import { IBuildProtocolVersions } from "src/oasys/L1/build/interfaces/IBuildProtocolVersions.sol"; import { ILegacyL1BuildAgent } from "src/oasys/L1/build/interfaces/ILegacyL1BuildAgent.sol"; import { IOasysL2OutputOracleVerifier } from "src/oasys/L1/interfaces/IOasysL2OutputOracleVerifier.sol"; +import { PortalSender } from "src/oasys/L1/build/PortalSender.sol"; +import { OptimismPortal } from "src/L1/OptimismPortal.sol"; /// @notice The 2nd version of L1BuildAgent /// Regarding the build step, referred to the build script of Opstack @@ -106,77 +108,73 @@ contract L1BuildAgent is IL1BuildAgent, ISemver { BuildConfig calldata _cfg ) external - returns (address, address[7] memory, address[7] memory, address, address) + returns (BuiltAddressList memory, address[7] memory) { + // Only the builder can build the L2 + // The builder is the person who deposits the required amount + address builder = msg.sender; + // Not require to be globally unique, as the pre built L2 needs to be upgraded require(_isInternallyUniqueChainId(_chainId), "L1BuildAgent: already deployed"); if (_requiresDepositCheck(_chainId)) { require( - L1_BUILD_DEPOSIT.getDepositTotal(msg.sender) >= L1_BUILD_DEPOSIT.requiredAmount(), + L1_BUILD_DEPOSIT.getDepositTotal(builder) >= L1_BUILD_DEPOSIT.requiredAmount(), "deposit amount shortage" ); } // build the deposit. // Mark this builder as built. - L1_BUILD_DEPOSIT.build(msg.sender); + L1_BUILD_DEPOSIT.build(builder); // register the builder // Mark this chainId as built - builders[_chainId] = msg.sender; + builders[_chainId] = builder; + + // check if the L2 is upgrading the existing L2 + // If so, the existing address manager is set to the legacyAddressManager + // otherwise, legacyAddressManager is empty + bool isUpgradingExistingL2 = _cfg.legacyAddressManager != address(0); // temporarily set the admin to this contract // transfer ownership to the final system owner at the end of building address admin = address(this); - // deploy the AddressManager. - // TODO: Not required for new L2. - address addressManager = address(BUILD_PROXY.deployAddressManager({ owner: admin })); - // deploy proxy contracts for each verse - (ProxyAdmin proxyAdmin, address[7] memory proxys) = _deployProxies(admin, addressManager); - - // transfer ownership of the address manager to the ProxyAdmin - _transferAddressManagerOwnership(proxyAdmin, addressManager); + ProxyAdmin proxyAdmin = _deployProxies(_chainId, admin, _cfg.legacyAddressManager); + + if (isUpgradingExistingL2) { + // Pause the legacy L1CrossDomainMessenger + _pauseLegacyL1CrossDomainMessenger(_cfg.legacyAddressManager); + // Set the address of the AddressManager. + proxyAdmin.setAddressManager(AddressManager(_cfg.legacyAddressManager)); + require(proxyAdmin.addressManager() == AddressManager(_cfg.legacyAddressManager)); + // transfer ownership of the address manager to the ProxyAdmin + AddressManager(_cfg.legacyAddressManager).transferOwnership(address(proxyAdmin)); + } // don't deploy the implementation contracts every time // to save gas, reuse the same implementation contract for each proxy - address[7] memory impls = _deployImplementations(_cfg, proxys); - - // compute the batch inbox address from chainId - // L2 tx bathch is sent to this address - address batchInbox = computeInboxAddress(_chainId); - - emit Deployed(_chainId, _cfg.finalSystemOwner, address(proxyAdmin), proxys, impls, batchInbox, addressManager); + address[7] memory impls = _deployImplementations(_chainId, _cfg); - // register built addresses to the builtLists - builtLists[_chainId].proxyAdmin = address(proxyAdmin); - builtLists[_chainId].systemConfig = proxys[2]; - builtLists[_chainId].l1StandardBridge = proxys[4]; - builtLists[_chainId].l1ERC721Bridge = proxys[5]; - builtLists[_chainId].l1CrossDomainMessenger = proxys[3]; - builtLists[_chainId].oasysL2OutputOracle = proxys[1]; - builtLists[_chainId].oasysPortal = proxys[0]; - builtLists[_chainId].protocolVersions = proxys[6]; - builtLists[_chainId].batchInbox = batchInbox; - builtLists[_chainId].addressManager = addressManager; + emit Deployed(_chainId, _cfg.finalSystemOwner, _cfg.legacyAddressManager, builtLists[_chainId], impls); // append the chainId to the list chainIds.push(_chainId); // initialize each contracts by calling `initialize` functions through proxys - _initializeSystemConfig(_cfg, proxyAdmin, impls[2], proxys); - _initializeL1StandardBridge(proxyAdmin, impls[4], proxys); - _initializeL1ERC721Bridge(proxyAdmin, impls[5], proxys); - _initializeL1CrossDomainMessenger(proxyAdmin, impls[3], proxys); - _initializeOasysL2OutputOracle(_cfg, proxyAdmin, impls[1], proxys); - _initializeOasysPortal(proxyAdmin, impls[0], proxys); - _initializeProtocolVersions(_cfg, proxyAdmin, impls[6], proxys); + _initializeSystemConfig(_chainId, _cfg, proxyAdmin, impls[2]); + _initializeL1StandardBridge(_chainId, proxyAdmin, impls[4], isUpgradingExistingL2); + _initializeL1ERC721Bridge(_chainId, proxyAdmin, impls[5], isUpgradingExistingL2); + _initializeL1CrossDomainMessenger(_chainId, proxyAdmin, impls[3], isUpgradingExistingL2); + _initializeOasysL2OutputOracle(_chainId, _cfg, proxyAdmin, impls[1]); + _initializeOasysPortal(_chainId, proxyAdmin, impls[0]); + _initializeProtocolVersions(_chainId, _cfg, proxyAdmin, impls[6]); // transfer ownership of the proxy admin to the final system owner _transferProxyAdminOwnership(_cfg, proxyAdmin); - return (address(proxyAdmin), proxys, impls, batchInbox, addressManager); + return (builtLists[_chainId], impls); } /// @notice Compute inbox address from chainId @@ -217,25 +215,28 @@ contract L1BuildAgent is IL1BuildAgent, ISemver { } function _deployProxies( + uint256 _chainId, address admin, address addressManager ) internal - returns (ProxyAdmin proxyAdmin, address[7] memory proxys) + returns (ProxyAdmin proxyAdmin) { proxyAdmin = BUILD_PROXY.deployProxyAdmin({ owner: admin }); - proxys[0] = _deployProxy(address(proxyAdmin)); // OasysPortalProxy - proxys[1] = _deployProxy(address(proxyAdmin)); // OasysL2OutputOracleProxy - proxys[2] = _deployProxy(address(proxyAdmin)); // SystemConfigProxy - proxys[3] = _deployL1CrossDomainMessengerProxy(addressManager); // L1CrossDomainMessengerProxy - proxys[4] = _deployL1StandardBridgeProxy(address(proxyAdmin)); // L1StandardBridgeProxy - proxys[5] = _deployProxy(address(proxyAdmin)); // L1ERC721BridgeProxy - proxys[6] = _deployProxy(address(proxyAdmin)); // ProtocolVersionsProxy - - // Set the address of the AddressManager. - // TODO: Not required for new L2. - proxyAdmin.setAddressManager(AddressManager(addressManager)); - require(proxyAdmin.addressManager() == AddressManager(addressManager)); + + // register built addresses to the builtLists + builtLists[_chainId].proxyAdmin = address(proxyAdmin); + builtLists[_chainId].oasysPortal = _deployProxy(address(proxyAdmin)); + builtLists[_chainId].oasysL2OutputOracle = _deployProxy(address(proxyAdmin)); + builtLists[_chainId].systemConfig = _deployProxy(address(proxyAdmin)); + builtLists[_chainId].l1CrossDomainMessenger = _deployL1CrossDomainMessengerProxy(address(proxyAdmin), addressManager); + builtLists[_chainId].l1StandardBridge = _deployL1StandardBridgeProxy(address(proxyAdmin), addressManager); + builtLists[_chainId].l1ERC721Bridge = _deployL1ERC721BridgeProxy(address(proxyAdmin), addressManager); + builtLists[_chainId].protocolVersions = _deployProxy(address(proxyAdmin)); + + // compute the batch inbox address from chainId + // L2 tx bathch is sent to this address + builtLists[_chainId].batchInbox = computeInboxAddress(_chainId); } /// @notice Deploy the Proxy @@ -244,42 +245,61 @@ contract L1BuildAgent is IL1BuildAgent, ISemver { } /// @notice Deploy the L1CrossDomainMessengerProxy using a ResolvedDelegateProxy - function _deployL1CrossDomainMessengerProxy(address _addressManager) internal returns (address addr) { - AddressManager addressManager = AddressManager(_addressManager); - - string memory contractName = "OVM_L1CrossDomainMessenger"; - // TODO: Not required for new L2. - ResolvedDelegateProxy proxy = - BUILD_PROXY.deployResolvedProxy({ addressManager: _addressManager, implementationName: contractName }); - - address contractAddr = addressManager.getAddress(contractName); - if (contractAddr != address(proxy)) { - addressManager.setAddress(contractName, address(proxy)); + function _deployL1CrossDomainMessengerProxy(address proxyAdmin, address addressManager) internal returns (address addr) { + if (addressManager != address(0)) { + // upgrading existing L2 + // Don't deply proxy, as the existing L2 already has the proxy(RelolvedDelegateProxy) + string memory contractName = "OVM_L1CrossDomainMessenger"; + addr = AddressManager(addressManager).getAddress(contractName); + require(addr != address(0), "L1BuildAgent: failed to find L1CrossDomainMessengerProxy from AddressManager"); + // Trasfer ownership to ProxyAdmin + // ResolvedDelegateProxy(payable(addr)).setOwner(address(proxyAdmin)); + } else { + addr = _deployProxy(address(proxyAdmin)); } + } - require(addressManager.getAddress(contractName) == address(proxy)); - - addr = address(proxy); + function _deployL1StandardBridgeProxy(address proxyAdmin, address addressManager) internal returns (address addr) { + if (addressManager != address(0)) { + // upgrading existing L2 + // Don't deply proxy, as the existing L2 already has the proxy(RelolvedDelegateProxy) + string memory contractName = "Proxy__OVM_L1StandardBridge"; + addr = AddressManager(addressManager).getAddress(contractName); + require(addr != address(0), "L1BuildAgent: failed to find L1StandardBridgeProxy from AddressManager"); + // Trasfer ownership to ProxyAdmin + L1ChugSplashProxy(payable(addr)).setOwner(address(proxyAdmin)); + } else { + addr = _deployProxy(address(proxyAdmin)); + } } - function _deployL1StandardBridgeProxy(address admin) internal returns (address addr) { - // TODO: Not required for new L2. - addr = address(BUILD_PROXY.deployChugProxy({ owner: admin })); + function _deployL1ERC721BridgeProxy(address proxyAdmin, address addressManager) internal returns (address addr) { + if (addressManager != address(0)) { + // upgrading existing L2 + // Don't deply proxy, as the existing L2 already has the proxy(RelolvedDelegateProxy) + string memory contractName = "Proxy__OVM_L1ERC721Bridge"; + addr = AddressManager(addressManager).getAddress(contractName); + require(addr != address(0), "L1BuildAgent: failed to find L1ERC721BridgeProxy from AddressManager"); + // Trasfer ownership to ProxyAdmin + L1ChugSplashProxy(payable(addr)).setOwner(address(proxyAdmin)); + } else { + addr = _deployProxy(address(proxyAdmin)); + } } /// @notice Deploy all of the implementations function _deployImplementations( - BuildConfig calldata _cfg, - address[7] memory proxys + uint256 _chainId, + BuildConfig calldata _cfg ) internal returns (address[7] memory impls) { impls[0] = _deployImplementation( BUILD_OASYS_PORTAL.deployBytecode({ - _l2Oracle: proxys[1], // OasysL2OutputOracleProxy + _l2Oracle: builtLists[_chainId].oasysL2OutputOracle, _guardian: _cfg.finalSystemOwner, - _systemConfig: proxys[2] // SystemConfigProxy + _systemConfig: builtLists[_chainId].systemConfig }) ); @@ -302,19 +322,19 @@ contract L1BuildAgent is IL1BuildAgent, ISemver { impls[3] = _deployImplementation( BUILD_L1CROSS_DOMAIN_MESSENGER.deployBytecode({ - _portal: payable(proxys[0]) // OasysPortalProxy + _portal: payable(builtLists[_chainId].oasysPortal) // OasysPortalProxy }) ); impls[4] = _deployImplementation( BUILD_L1_STANDARD_BRIDGE.deployBytecode({ - _messenger: payable(proxys[3]) // L1CrossDomainMessengerProxy + _messenger: payable(builtLists[_chainId].l1CrossDomainMessenger) // L1CrossDomainMessengerProxy }) ); impls[5] = _deployImplementation( BUILD_L1_ERC721_BRIDGE.deployBytecode({ - _messenger: proxys[3], // L1CrossDomainMessengerProxy + _messenger: builtLists[_chainId].l1CrossDomainMessenger, // L1CrossDomainMessengerProxy _otherBridge: L2PredeployAddresses.L2_ERC721_BRIDGE }) ); @@ -332,14 +352,14 @@ contract L1BuildAgent is IL1BuildAgent, ISemver { /// @notice Initialize the SystemConfig function _initializeSystemConfig( + uint256 _chainId, BuildConfig calldata _cfg, ProxyAdmin proxyAdmin, - address impl, - address[7] memory proxys + address impl ) internal { - address systemConfigProxy = proxys[2]; + address systemConfigProxy = builtLists[_chainId].systemConfig; proxyAdmin.upgradeAndCall({ _proxy: payable(systemConfigProxy), @@ -366,54 +386,74 @@ contract L1BuildAgent is IL1BuildAgent, ISemver { } /// @notice Initialize the L1StandardBridge - function _initializeL1StandardBridge(ProxyAdmin proxyAdmin, address impl, address[7] memory proxys) internal { - address l1StandardBridgeProxy = proxys[4]; + function _initializeL1StandardBridge( + uint256 _chainId, + ProxyAdmin proxyAdmin, + address impl, + bool isUpgradingExistingL2 + ) internal { + address l1StandardBridgeProxy = builtLists[_chainId].l1StandardBridge; - uint256 proxyType = uint256(proxyAdmin.proxyType(l1StandardBridgeProxy)); - if (proxyType != uint256(ProxyAdmin.ProxyType.CHUGSPLASH)) { + if (isUpgradingExistingL2) { + // The proxy of Legacy L2 is L1ChugSplashProxy, so need to set type proxyAdmin.setProxyType(l1StandardBridgeProxy, ProxyAdmin.ProxyType.CHUGSPLASH); + require(uint256(proxyAdmin.proxyType(l1StandardBridgeProxy)) == uint256(ProxyAdmin.ProxyType.CHUGSPLASH)); + + // Transfer ETH from the L1StandardBridge to the OptimismPortal. + PortalSender portalSender = new PortalSender(OptimismPortal(payable(builtLists[_chainId].oasysPortal))); + proxyAdmin.upgradeAndCall( + payable(l1StandardBridgeProxy), + address(portalSender), + abi.encodeCall(PortalSender.donate, ()) + ); } - require(uint256(proxyAdmin.proxyType(l1StandardBridgeProxy)) == uint256(ProxyAdmin.ProxyType.CHUGSPLASH)); - // TODO: Built L2 uses L1ChugSplashProxy and requires special handling. proxyAdmin.upgrade({ _proxy: payable(l1StandardBridgeProxy), _implementation: impl }); } /// @notice Initialize the L1ERC721Bridge - function _initializeL1ERC721Bridge(ProxyAdmin proxyAdmin, address impl, address[7] memory proxys) internal { - address l1ERC721BridgeProxy = proxys[5]; + function _initializeL1ERC721Bridge( + uint256 _chainId, + ProxyAdmin proxyAdmin, + address impl, + bool isUpgradingExistingL2 + ) internal { + address l1ERC721BridgeProxy = builtLists[_chainId].l1ERC721Bridge; + + if (isUpgradingExistingL2) { + // The proxy of Legacy L2 is L1ChugSplashProxy, so need to set type + proxyAdmin.setProxyType(l1ERC721BridgeProxy, ProxyAdmin.ProxyType.CHUGSPLASH); + require(uint256(proxyAdmin.proxyType(l1ERC721BridgeProxy)) == uint256(ProxyAdmin.ProxyType.CHUGSPLASH)); + } - // TODO: Built L2 uses L1ChugSplashProxy and requires special handling. proxyAdmin.upgrade({ _proxy: payable(l1ERC721BridgeProxy), _implementation: impl }); } /// @notice Initialize the L1CrossDomainMessenger function _initializeL1CrossDomainMessenger( + uint256 _chainId, ProxyAdmin proxyAdmin, address impl, - address[7] memory proxys + bool isUpgradingExistingL2 ) internal { - address l1CrossDomainMessengerProxy = proxys[3]; + address l1CrossDomainMessengerProxy = builtLists[_chainId].l1CrossDomainMessenger; - uint256 proxyType = uint256(proxyAdmin.proxyType(l1CrossDomainMessengerProxy)); - if (proxyType != uint256(ProxyAdmin.ProxyType.RESOLVED)) { + if (isUpgradingExistingL2) { + // The proxy of Legacy L2 is ResolvedDelegateProxy, so need to set type and implementation name + // Set proxy type to RESOLVED proxyAdmin.setProxyType(l1CrossDomainMessengerProxy, ProxyAdmin.ProxyType.RESOLVED); - } - require(uint256(proxyAdmin.proxyType(l1CrossDomainMessengerProxy)) == uint256(ProxyAdmin.ProxyType.RESOLVED)); - - string memory contractName = "OVM_L1CrossDomainMessenger"; - string memory implName = proxyAdmin.implementationName(impl); - if (keccak256(bytes(contractName)) != keccak256(bytes(implName))) { + require(uint256(proxyAdmin.proxyType(l1CrossDomainMessengerProxy)) == uint256(ProxyAdmin.ProxyType.RESOLVED)); + // Set the implementation name to OVM_L1CrossDomainMessenger + string memory contractName = "OVM_L1CrossDomainMessenger"; proxyAdmin.setImplementationName(l1CrossDomainMessengerProxy, contractName); + require( + keccak256(bytes(proxyAdmin.implementationName(l1CrossDomainMessengerProxy))) + == keccak256(bytes(contractName)) + ); } - require( - keccak256(bytes(proxyAdmin.implementationName(l1CrossDomainMessengerProxy))) - == keccak256(bytes(contractName)) - ); - // TODO: Built L2 uses OVM_L1CrossDomainMessengerProxy and requires special handling. proxyAdmin.upgradeAndCall({ _proxy: payable(l1CrossDomainMessengerProxy), _implementation: impl, @@ -423,14 +463,14 @@ contract L1BuildAgent is IL1BuildAgent, ISemver { /// @notice Initialize the OasysL2OutputOracle function _initializeOasysL2OutputOracle( + uint256 _chainId, BuildConfig calldata _cfg, ProxyAdmin proxyAdmin, - address impl, - address[7] memory proxys + address impl ) internal { - address l2OutputOracleProxy = proxys[1]; + address l2OutputOracleProxy = builtLists[_chainId].oasysL2OutputOracle; proxyAdmin.upgradeAndCall({ _proxy: payable(l2OutputOracleProxy), @@ -443,8 +483,8 @@ contract L1BuildAgent is IL1BuildAgent, ISemver { } /// @notice Initialize the OasysPortal - function _initializeOasysPortal(ProxyAdmin proxyAdmin, address impl, address[7] memory proxys) internal { - address oasysPortalProxy = proxys[0]; + function _initializeOasysPortal(uint256 _chainId, ProxyAdmin proxyAdmin, address impl) internal { + address oasysPortalProxy = builtLists[_chainId].oasysPortal; proxyAdmin.upgradeAndCall({ _proxy: payable(oasysPortalProxy), @@ -454,14 +494,14 @@ contract L1BuildAgent is IL1BuildAgent, ISemver { } function _initializeProtocolVersions( + uint256 _chainId, BuildConfig calldata _cfg, ProxyAdmin proxyAdmin, - address impl, - address[7] memory proxys + address impl ) internal { - address protocolVersionsProxy = proxys[6]; + address protocolVersionsProxy = builtLists[_chainId].protocolVersions; uint256 requiredProtocolVersion = uint256(0x0); uint256 recommendedProtocolVersion = uint256(0x0); @@ -477,6 +517,48 @@ contract L1BuildAgent is IL1BuildAgent, ISemver { }); } + // Ref: step2, 3, and 4 of `SystemDictator` + // https://github.com/oasysgames/oasys-opstack/blob/cd7c58349542f9f1ce9fd42c9054aeed1325e02c/packages/contracts-bedrock/contracts/deployment/SystemDictator.sol + function _pauseLegacyL1CrossDomainMessenger(address addressManager) internal { + // Temporarily brick the L1CrossDomainMessenger by setting its implementation address to + // address(0) which will cause the ResolvedDelegateProxy to revert. Better than pausing + // the L1CrossDomainMessenger via pause() because it can be easily reverted. + AddressManager(addressManager).setAddress("OVM_L1CrossDomainMessenger", address(0)); + + // Set the DTL shutoff block, which will tell the DTL to stop syncing new deposits from the + // CanonicalTransactionChain. We do this by setting an address in the AddressManager + // because the DTL already has a reference to the AddressManager and this way we don't also + // need to give it a reference to the SystemDictator. + AddressManager(addressManager).setAddress( + "DTL_SHUTOFF_BLOCK", + address(uint160(block.number)) + ); + + // Remove all deprecated addresses from the AddressManager + string[17] memory deprecated = [ + "OVM_CanonicalTransactionChain", + "OVM_L2CrossDomainMessenger", + "OVM_DecompressionPrecompileAddress", + "OVM_Sequencer", + "OVM_Proposer", + "OVM_ChainStorageContainer-CTC-batches", + "OVM_ChainStorageContainer-CTC-queue", + "OVM_CanonicalTransactionChain", + "OVM_StateCommitmentChain", + "OVM_BondManager", + "OVM_ExecutionManager", + "OVM_FraudVerifier", + "OVM_StateManagerFactory", + "OVM_StateTransitionerFactory", + "OVM_SafetyChecker", + "OVM_L1MultiMessageRelayer", + "BondManager" + ]; + for (uint256 i = 0; i < deprecated.length; i++) { + AddressManager(addressManager).setAddress(deprecated[i], address(0)); + } + } + /// @notice Transfer ownership of the ProxyAdmin contract to the final system owner function _transferProxyAdminOwnership(BuildConfig calldata _cfg, ProxyAdmin proxyAdmin) internal { address owner = proxyAdmin.owner(); @@ -485,12 +567,4 @@ contract L1BuildAgent is IL1BuildAgent, ISemver { proxyAdmin.transferOwnership(finalSystemOwner); } } - - /// @notice Transfer ownership of the address manager to the ProxyAdmin - function _transferAddressManagerOwnership(ProxyAdmin proxyAdmin, address _addressManager) internal { - AddressManager addressManager = AddressManager(_addressManager); - if (addressManager.owner() != address(proxyAdmin)) { - addressManager.transferOwnership(address(proxyAdmin)); - } - } } diff --git a/packages/contracts-bedrock/src/oasys/L1/build/PortalSender.sol b/packages/contracts-bedrock/src/oasys/L1/build/PortalSender.sol new file mode 100644 index 000000000..2366edf06 --- /dev/null +++ b/packages/contracts-bedrock/src/oasys/L1/build/PortalSender.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +import { OptimismPortal } from "src/L1/OptimismPortal.sol"; + +/** + * @title PortalSender + * @notice The PortalSender is a simple intermediate contract that will transfer the balance of the + * L1StandardBridge to the OptimismPortal during the Bedrock migration. + */ +// Copied from https://github.com/oasysgames/oasys-opstack/blob/cd7c58349542f9f1ce9fd42c9054aeed1325e02c/packages/contracts-bedrock/contracts/deployment/PortalSender.sol +contract PortalSender { + /** + * @notice Address of the OptimismPortal contract. + */ + OptimismPortal public immutable PORTAL; + + /** + * @param _portal Address of the OptimismPortal contract. + */ + constructor(OptimismPortal _portal) { + PORTAL = _portal; + } + + /** + * @notice Sends balance of this contract to the OptimismPortal. + */ + function donate() public { + PORTAL.donateETH{ value: address(this).balance }(); + } +} \ No newline at end of file diff --git a/packages/contracts-bedrock/src/oasys/L1/build/interfaces/IBuildProxy.sol b/packages/contracts-bedrock/src/oasys/L1/build/interfaces/IBuildProxy.sol index 1bde78df1..b36f8c290 100644 --- a/packages/contracts-bedrock/src/oasys/L1/build/interfaces/IBuildProxy.sol +++ b/packages/contracts-bedrock/src/oasys/L1/build/interfaces/IBuildProxy.sol @@ -1,17 +1,10 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; -import { AddressManager } from "src/legacy/AddressManager.sol"; import { ProxyAdmin } from "src/universal/ProxyAdmin.sol"; import { Proxy } from "src/universal/Proxy.sol"; -import { L1ChugSplashProxy } from "src/legacy/L1ChugSplashProxy.sol"; -import { ResolvedDelegateProxy } from "src/legacy/ResolvedDelegateProxy.sol"; interface IBuildProxy { - /// @notice Deploy the AddressManager. - /// @param owner Initial owner of the contract. - function deployAddressManager(address owner) external returns (AddressManager addressManager); - /// @notice Deploy the proxyAdmin. /// @param owner Initial owner of the contract. function deployProxyAdmin(address owner) external returns (ProxyAdmin proxyAdmin); @@ -19,18 +12,4 @@ interface IBuildProxy { /// @notice Deploy the Proxy. /// @param admin Initial admin of the contract. function deployERC1967Proxy(address admin) external returns (Proxy proxy); - - /// @notice Deploy the L1ChugSplashProxy. - /// @param owner Initial owner of the contract. - function deployChugProxy(address owner) external returns (L1ChugSplashProxy proxy); - - /// @notice Deploy the ResolvedDelegateProxy. - /// @param addressManager Address of the AddressManager. - /// @param implementationName implementationName of the contract to proxy to. - function deployResolvedProxy( - address addressManager, - string memory implementationName - ) - external - returns (ResolvedDelegateProxy proxy); } diff --git a/packages/contracts-bedrock/src/oasys/L1/build/interfaces/IL1BuildAgent.sol b/packages/contracts-bedrock/src/oasys/L1/build/interfaces/IL1BuildAgent.sol index 746aa0b0d..9ce72cb55 100644 --- a/packages/contracts-bedrock/src/oasys/L1/build/interfaces/IL1BuildAgent.sol +++ b/packages/contracts-bedrock/src/oasys/L1/build/interfaces/IL1BuildAgent.sol @@ -3,6 +3,11 @@ pragma solidity 0.8.15; interface IL1BuildAgent { struct BuildConfig { + // The address of `Lib_AddressManager`. + // Value: + // - for new chain : address(0) + // - for existing chain : pre-deployed address + address legacyAddressManager; // The owner of L1 contract set. Any L1 contract that is ownable has this account set as its owner // Value: depending on each verse address finalSystemOwner; @@ -58,24 +63,21 @@ interface IL1BuildAgent { address oasysPortal; address protocolVersions; address batchInbox; - address addressManager; } /// @notice Event emitted when the L1 contract set is deployed event Deployed( uint256 indexed chainId, - address owner, - address proxyAdmin, - address[7] proxys, - address[7] impls, - address batchInbox, - address addressManager + address finalSystemOwner, + address legacyAddressManager, + BuiltAddressList results, + address[7] impls ); function builtLists(uint256 chainId) external view - returns (address, address, address, address, address, address, address, address, address, address); + returns (address, address, address, address, address, address, address, address, address); function chainIds(uint256 index) external view returns (uint256 chainId); @@ -88,11 +90,5 @@ interface IL1BuildAgent { BuildConfig calldata cfg ) external - returns ( - address proxyAdmin, - address[7] memory proxys, - address[7] memory impls, - address batchInbox, - address addressManager - ); + returns (BuiltAddressList memory, address[7] memory); } diff --git a/packages/contracts-bedrock/src/oasys/L1/messaging/OasysL1ERC721BridgeLegacySpacer.sol b/packages/contracts-bedrock/src/oasys/L1/messaging/OasysL1ERC721BridgeLegacySpacer.sol new file mode 100644 index 000000000..8df18deac --- /dev/null +++ b/packages/contracts-bedrock/src/oasys/L1/messaging/OasysL1ERC721BridgeLegacySpacer.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +import { L2OutputOracle } from "src/L1/L2OutputOracle.sol"; + +/// @title OasysL1ERC721BridgeLegacySpacer +/// @notice The LegacyOasysStorageLayout_L1ERC721Bridge is a contract that defines the storage layout of Oasys Legacy L1ERC721Bridge. +/// Ref: https://github.com/oasysgames/oasys-optimism/blob/4d667a169296f37422ffaa4901e8d149e84abe5a/packages/contracts/contracts/oasys/L1/messaging/L1ERC721BridgeV2.sol +contract OasysL1ERC721BridgeLegacySpacer { + /// @custom:legacy + /// @custom:spacer messenger + /// @notice Spacer for backwards compatibility. + address private spacer_0_0_20; + + /// @custom:legacy + /// @custom:spacer l2ERC721Bridge + /// @notice Spacer for backwards compatibility. + address private spacer_1_0_20; + + /// @notice Maps the deposit status of L1 token to L2 token + /// This mapping is commonly used in Oasy's L1ERC721Bridge and Optimism's L1ERC721Bridge + mapping(address => mapping(address => mapping(uint256 => bool))) public deposits; + + /// @notice Reserve extra slots (to a total of 50) in the storage layout for future upgrades. + /// A gap size of 47 was chosen here, so that the first slot used in a child contract + /// would be a multiple of 50. + uint256[47] private __gap; +} diff --git a/packages/contracts-bedrock/src/oasys/L1/rollup/OasysL2OutputOracle.sol b/packages/contracts-bedrock/src/oasys/L1/rollup/OasysL2OutputOracle.sol index 7cdc1aad8..d3b3ee8cf 100644 --- a/packages/contracts-bedrock/src/oasys/L1/rollup/OasysL2OutputOracle.sol +++ b/packages/contracts-bedrock/src/oasys/L1/rollup/OasysL2OutputOracle.sol @@ -51,6 +51,26 @@ contract OasysL2OutputOracle is IOasysL2OutputOracle, L2OutputOracle { super.initialize(_startingBlockNumber, _startingTimestamp); } + /// @notice Update the starting block number and timestamp. + /// Anyone can call, until the first output is recorded or deleted all the outputs. + /// This function for the purpose of attempting the L2 upgrade again, + /// after the L2 Upgrade fails and rollback operations are conducted. + /// @param _startingBlockNumber Block number for the first recoded L2 block. + /// @param _startingTimestamp Timestamp for the first recoded L2 block. + function updateStartingBlock(uint256 _startingBlockNumber, uint256 _startingTimestamp) public { + require( + _startingTimestamp <= block.timestamp, + "L2OutputOracle: starting L2 timestamp must be less than current time" + ); + require( + l2Outputs.length == 0, + "L2OutputOracle: cannot update starting block after outputs have been recorded" + ); + + startingTimestamp = _startingTimestamp; + startingBlockNumber = _startingBlockNumber; + } + /// @notice Getter function for the address of the OasysL2OutputOracleVerifier on this chain. /// @notice Address of the OasysL2OutputOracleVerifier on this chain. function l2OracleVerifier() public view returns (IOasysL2OutputOracleVerifier) { diff --git a/packages/contracts-bedrock/src/universal/ERC721Bridge.sol b/packages/contracts-bedrock/src/universal/ERC721Bridge.sol index 948704c2f..78fab17ab 100644 --- a/packages/contracts-bedrock/src/universal/ERC721Bridge.sol +++ b/packages/contracts-bedrock/src/universal/ERC721Bridge.sol @@ -4,10 +4,11 @@ pragma solidity 0.8.15; import { CrossDomainMessenger } from "src/universal/CrossDomainMessenger.sol"; import { Address } from "@openzeppelin/contracts/utils/Address.sol"; import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol"; +import { OasysL1ERC721BridgeLegacySpacer } from "src/oasys/L1/messaging/OasysL1ERC721BridgeLegacySpacer.sol"; /// @title ERC721Bridge /// @notice ERC721Bridge is a base contract for the L1 and L2 ERC721 bridges. -abstract contract ERC721Bridge { +abstract contract ERC721Bridge is OasysL1ERC721BridgeLegacySpacer { /// @notice Messenger contract on this domain. This will be removed in the /// future, use `messenger` instead. /// @custom:legacy diff --git a/packages/contracts-bedrock/test/L1BuildAgent.t.sol b/packages/contracts-bedrock/test/L1BuildAgent.t.sol index d3294f4ac..4d7dab6b7 100644 --- a/packages/contracts-bedrock/test/L1BuildAgent.t.sol +++ b/packages/contracts-bedrock/test/L1BuildAgent.t.sol @@ -36,39 +36,39 @@ contract L1BuildAgentTest is SetupL1BuildAgent { assert(deployment.proxyAdmin.owner() == deployment.buildCfg.finalSystemOwner); } - function test_ProxyAdmin_addressManager() external view { - assert(address(deployment.proxyAdmin.addressManager()) == address(deployment.addressManager)); - } + // function test_ProxyAdmin_addressManager() external view { + // assert(address(deployment.proxyAdmin.addressManager()) == address(deployment.addressManager)); + // } function test_ProxyAdmin_proxyTypes() external view { assert(deployment.proxyAdmin.proxyType(address(deployment.portal)) == ProxyAdmin.ProxyType.ERC1967); assert(deployment.proxyAdmin.proxyType(address(deployment.l2Oracle)) == ProxyAdmin.ProxyType.ERC1967); assert(deployment.proxyAdmin.proxyType(address(deployment.systemConfig)) == ProxyAdmin.ProxyType.ERC1967); - assert(deployment.proxyAdmin.proxyType(address(deployment.l1Messenger)) == ProxyAdmin.ProxyType.RESOLVED); - assert(deployment.proxyAdmin.proxyType(address(deployment.l1ERC20Bridge)) == ProxyAdmin.ProxyType.CHUGSPLASH); + assert(deployment.proxyAdmin.proxyType(address(deployment.l1Messenger)) == ProxyAdmin.ProxyType.ERC1967); + assert(deployment.proxyAdmin.proxyType(address(deployment.l1ERC20Bridge)) == ProxyAdmin.ProxyType.ERC1967); assert(deployment.proxyAdmin.proxyType(address(deployment.l1ERC721Bridge)) == ProxyAdmin.ProxyType.ERC1967); assert(deployment.proxyAdmin.proxyType(address(deployment.protocolVersions)) == ProxyAdmin.ProxyType.ERC1967); } - function test_ProxyAdmin_implementationNames() external view { - assert( - keccak256(abi.encode(deployment.proxyAdmin.implementationName(address(deployment.l1Messenger)))) - == keccak256(abi.encode("OVM_L1CrossDomainMessenger")) - ); - } + // function test_ProxyAdmin_implementationNames() external view { + // assert( + // keccak256(abi.encode(deployment.proxyAdmin.implementationName(address(deployment.l1Messenger)))) + // == keccak256(abi.encode("OVM_L1CrossDomainMessenger")) + // ); + // } /** * @dev Tests for `AddressManager` */ - function test_AddressManager_owner() external view { - assert(deployment.addressManager.owner() == address(deployment.proxyAdmin)); - } - - function test_AddressManager_getAddress_OVM_L1CrossDomainMessenger() external view { - assert( - deployment.addressManager.getAddress("OVM_L1CrossDomainMessenger") == address(deployment.l1MessengerImpl) - ); - } + // function test_AddressManager_owner() external view { + // assert(deployment.addressManager.owner() == address(deployment.proxyAdmin)); + // } + + // function test_AddressManager_getAddress_OVM_L1CrossDomainMessenger() external view { + // assert( + // deployment.addressManager.getAddress("OVM_L1CrossDomainMessenger") == address(deployment.l1MessengerImpl) + // ); + // } /** * @dev Tests for `OasysPortal` diff --git a/packages/contracts-bedrock/test/setup/oasys/SetupL1BuildAgent.sol b/packages/contracts-bedrock/test/setup/oasys/SetupL1BuildAgent.sol index ac2a782c7..54160b1de 100644 --- a/packages/contracts-bedrock/test/setup/oasys/SetupL1BuildAgent.sol +++ b/packages/contracts-bedrock/test/setup/oasys/SetupL1BuildAgent.sol @@ -86,6 +86,8 @@ contract SetupL1BuildAgent is Test { /// @dev Default deployment L2 Deployment deployment; + address legacyAddressManager; + function setUp() public virtual { _addBalanceToTestWallets(); @@ -96,10 +98,13 @@ contract SetupL1BuildAgent is Test { vm.prank(depositor); l1Deposit.deposit{ value: 1 ether }(builder); + legacyAddressManager = address(0); + vm.prank(builder); deployment = _runL1BuildAgent( 5555, IL1BuildAgent.BuildConfig({ + legacyAddressManager: legacyAddressManager, finalSystemOwner: finalOwner, l2OutputOracleProposer: proposer, l2OutputOracleChallenger: challenger, @@ -207,29 +212,23 @@ contract SetupL1BuildAgent is Test { internal returns (Deployment memory) { - ( - address proxyAdmin, - address[7] memory proxys, - address[7] memory impls, - address batchInbox, - address addressManager - ) = l1Agent.build(chainId, cfg); + (IL1BuildAgent.BuiltAddressList memory results, address[7] memory impls) = l1Agent.build(chainId, cfg); return Deployment({ // Build config chainId: chainId, buildCfg: cfg, // Deployed proxies - portal: OasysPortal(payable(proxys[0])), - l2Oracle: OasysL2OutputOracle(proxys[1]), - systemConfig: SystemConfig(proxys[2]), - l1Messenger: L1CrossDomainMessenger(proxys[3]), - l1ERC20Bridge: L1StandardBridge(payable(proxys[4])), - l1ERC721Bridge: L1ERC721Bridge(proxys[5]), - protocolVersions: ProtocolVersions(proxys[6]), + portal: OasysPortal(payable(results.oasysPortal)), + l2Oracle: OasysL2OutputOracle(results.oasysL2OutputOracle), + systemConfig: SystemConfig(results.systemConfig), + l1Messenger: L1CrossDomainMessenger(results.l1CrossDomainMessenger), + l1ERC20Bridge: L1StandardBridge(payable(results.l1StandardBridge)), + l1ERC721Bridge: L1ERC721Bridge(results.l1ERC721Bridge), + protocolVersions: ProtocolVersions(results.protocolVersions), // Deployed implementations - proxyAdmin: ProxyAdmin(proxyAdmin), - addressManager: AddressManager(addressManager), + proxyAdmin: ProxyAdmin(results.proxyAdmin), + addressManager: AddressManager(legacyAddressManager), portalImpl: OasysPortal(payable(impls[0])), l2OracleImpl: OasysL2OutputOracle(impls[1]), systemConfigImpl: SystemConfig(impls[2]), @@ -238,7 +237,7 @@ contract SetupL1BuildAgent is Test { l1ERC721BridgeImpl: L1ERC721Bridge(impls[5]), protocolVersionsImpl: ProtocolVersions(impls[6]), // Misc - batchInbox: batchInbox + batchInbox: results.batchInbox }); } } From 7db9478ddaeae380c704948b0721571a51325ef1 Mon Sep 17 00:00:00 2001 From: tak Date: Wed, 27 Mar 2024 15:24:21 +0900 Subject: [PATCH 2/9] add data migrate testcase --- .../src/oasys/L1/build/L1BuildAgent.sol | 97 ++-- .../contracts-bedrock/test/L1BuildAgent.t.sol | 463 +++++++++--------- .../setup/oasys/MockLegacyL1ERC721Bridge.sol | 20 + .../oasys/MockLegacyL1StandardBridge.sol | 21 + .../test/setup/oasys/SetupL1BuildAgent.sol | 329 ++++++++++--- 5 files changed, 606 insertions(+), 324 deletions(-) create mode 100644 packages/contracts-bedrock/test/setup/oasys/MockLegacyL1ERC721Bridge.sol create mode 100644 packages/contracts-bedrock/test/setup/oasys/MockLegacyL1StandardBridge.sol diff --git a/packages/contracts-bedrock/src/oasys/L1/build/L1BuildAgent.sol b/packages/contracts-bedrock/src/oasys/L1/build/L1BuildAgent.sol index 9e05283ce..77e72b2e2 100644 --- a/packages/contracts-bedrock/src/oasys/L1/build/L1BuildAgent.sol +++ b/packages/contracts-bedrock/src/oasys/L1/build/L1BuildAgent.sol @@ -73,6 +73,9 @@ contract L1BuildAgent is IL1BuildAgent, ISemver { /// https://betterprogramming.pub/issues-of-returning-arrays-of-dynamic-size-in-solidity-smart-contracts-dd1e54424235 uint256[] public chainIds; + /// @notice The flag to pause the L1CrossDomainMessenger + bool public messengerPaused; + constructor( IBuildProxy _bProxy, IBuildOasysL2OutputOracle _bOasysL2OO, @@ -100,6 +103,50 @@ contract L1BuildAgent is IL1BuildAgent, ISemver { L2OO_VERIFIER = _l2ooVerifier; } + + /// @notice Pause the legacy L1CrossDomainMessenger + /// This is used when upgrading the existing L2 + /// Ref: step2 of `SystemDictator` + /// https://github.com/oasysgames/oasys-opstack/blob/cd7c58349542f9f1ce9fd42c9054aeed1325e02c/packages/contracts-bedrock/contracts/deployment/SystemDictator.sol + /// @param addressManager The address manager of the existing L2 + function pauseLegacyL1CrossDomainMessenger(address addressManager) public { + require(!messengerPaused, "L1BuildAgent: already paused"); + + messengerPaused = true; + + // Temporarily brick the L1CrossDomainMessenger by setting its implementation address to + // address(0) which will cause the ResolvedDelegateProxy to revert. Better than pausing + // the L1CrossDomainMessenger via pause() because it can be easily reverted. + AddressManager(addressManager).setAddress("OVM_L1CrossDomainMessenger", address(0)); + + // Set the DTL shutoff block, which will tell the DTL to stop syncing new deposits from the + // CanonicalTransactionChain. We do this by setting an address in the AddressManager + // because the DTL already has a reference to the AddressManager and this way we don't also + // need to give it a reference to the SystemDictator. + AddressManager(addressManager).setAddress( + "DTL_SHUTOFF_BLOCK", + address(uint160(block.number)) + ); + } + + /// @notice Unpause the legacy L1CrossDomainMessenger + /// @param addressManager The address manager of the existing L2 + /// @param oldL1CrossDomainMessenger The address of the old L1CrossDomainMessenger + function unpauseLegacyL1CrossDomainMessenger(address addressManager, address oldL1CrossDomainMessenger) public { + require(messengerPaused, "L1BuildAgent: not paused"); + + messengerPaused = false; + + // Reset the L1CrossDomainMessenger to the old implementation. + AddressManager(addressManager).setAddress( + "OVM_L1CrossDomainMessenger", + oldL1CrossDomainMessenger + ); + + // Unset the DTL shutoff block which will allow the DTL to sync again. + AddressManager(addressManager).setAddress("DTL_SHUTOFF_BLOCK", address(0)); + } + /// @notice Deploy the L1 contract set to build Verse, This is th main function. /// @param _chainId The chainId of Verse /// @param _cfg The configuration of the L1 contract set @@ -110,6 +157,7 @@ contract L1BuildAgent is IL1BuildAgent, ISemver { external returns (BuiltAddressList memory, address[7] memory) { + // Only the builder can build the L2 // The builder is the person who deposits the required amount address builder = msg.sender; @@ -144,8 +192,12 @@ contract L1BuildAgent is IL1BuildAgent, ISemver { ProxyAdmin proxyAdmin = _deployProxies(_chainId, admin, _cfg.legacyAddressManager); if (isUpgradingExistingL2) { - // Pause the legacy L1CrossDomainMessenger - _pauseLegacyL1CrossDomainMessenger(_cfg.legacyAddressManager); + if (!messengerPaused) { + // Pause the legacy L1CrossDomainMessenger + pauseLegacyL1CrossDomainMessenger(_cfg.legacyAddressManager); + } + // Remove deprecated addresses from the AddressManager + _removeDeprecatedAddresses(_cfg.legacyAddressManager); // Set the address of the AddressManager. proxyAdmin.setAddressManager(AddressManager(_cfg.legacyAddressManager)); require(proxyAdmin.addressManager() == AddressManager(_cfg.legacyAddressManager)); @@ -164,11 +216,13 @@ contract L1BuildAgent is IL1BuildAgent, ISemver { // initialize each contracts by calling `initialize` functions through proxys _initializeSystemConfig(_chainId, _cfg, proxyAdmin, impls[2]); + // OasysPortal should be initialized before L1StandardBridge, + // because L1StandardBridge uses OasysPortal as a recipient of the ETH + _initializeOasysPortal(_chainId, proxyAdmin, impls[0]); _initializeL1StandardBridge(_chainId, proxyAdmin, impls[4], isUpgradingExistingL2); _initializeL1ERC721Bridge(_chainId, proxyAdmin, impls[5], isUpgradingExistingL2); _initializeL1CrossDomainMessenger(_chainId, proxyAdmin, impls[3], isUpgradingExistingL2); _initializeOasysL2OutputOracle(_chainId, _cfg, proxyAdmin, impls[1]); - _initializeOasysPortal(_chainId, proxyAdmin, impls[0]); _initializeProtocolVersions(_chainId, _cfg, proxyAdmin, impls[6]); // transfer ownership of the proxy admin to the final system owner @@ -249,11 +303,9 @@ contract L1BuildAgent is IL1BuildAgent, ISemver { if (addressManager != address(0)) { // upgrading existing L2 // Don't deply proxy, as the existing L2 already has the proxy(RelolvedDelegateProxy) - string memory contractName = "OVM_L1CrossDomainMessenger"; + string memory contractName = "Proxy__OVM_L1CrossDomainMessenger"; addr = AddressManager(addressManager).getAddress(contractName); require(addr != address(0), "L1BuildAgent: failed to find L1CrossDomainMessengerProxy from AddressManager"); - // Trasfer ownership to ProxyAdmin - // ResolvedDelegateProxy(payable(addr)).setOwner(address(proxyAdmin)); } else { addr = _deployProxy(address(proxyAdmin)); } @@ -445,15 +497,16 @@ contract L1BuildAgent is IL1BuildAgent, ISemver { // Set proxy type to RESOLVED proxyAdmin.setProxyType(l1CrossDomainMessengerProxy, ProxyAdmin.ProxyType.RESOLVED); require(uint256(proxyAdmin.proxyType(l1CrossDomainMessengerProxy)) == uint256(ProxyAdmin.ProxyType.RESOLVED)); - // Set the implementation name to OVM_L1CrossDomainMessenger - string memory contractName = "OVM_L1CrossDomainMessenger"; - proxyAdmin.setImplementationName(l1CrossDomainMessengerProxy, contractName); - require( - keccak256(bytes(proxyAdmin.implementationName(l1CrossDomainMessengerProxy))) - == keccak256(bytes(contractName)) - ); } + // Set the implementation name to OVM_L1CrossDomainMessenger + string memory contractName = "OVM_L1CrossDomainMessenger"; + proxyAdmin.setImplementationName(l1CrossDomainMessengerProxy, contractName); + require( + keccak256(bytes(proxyAdmin.implementationName(l1CrossDomainMessengerProxy))) + == keccak256(bytes(contractName)) + ); + proxyAdmin.upgradeAndCall({ _proxy: payable(l1CrossDomainMessengerProxy), _implementation: impl, @@ -517,23 +570,9 @@ contract L1BuildAgent is IL1BuildAgent, ISemver { }); } - // Ref: step2, 3, and 4 of `SystemDictator` + // Ref: step3 of `SystemDictator` // https://github.com/oasysgames/oasys-opstack/blob/cd7c58349542f9f1ce9fd42c9054aeed1325e02c/packages/contracts-bedrock/contracts/deployment/SystemDictator.sol - function _pauseLegacyL1CrossDomainMessenger(address addressManager) internal { - // Temporarily brick the L1CrossDomainMessenger by setting its implementation address to - // address(0) which will cause the ResolvedDelegateProxy to revert. Better than pausing - // the L1CrossDomainMessenger via pause() because it can be easily reverted. - AddressManager(addressManager).setAddress("OVM_L1CrossDomainMessenger", address(0)); - - // Set the DTL shutoff block, which will tell the DTL to stop syncing new deposits from the - // CanonicalTransactionChain. We do this by setting an address in the AddressManager - // because the DTL already has a reference to the AddressManager and this way we don't also - // need to give it a reference to the SystemDictator. - AddressManager(addressManager).setAddress( - "DTL_SHUTOFF_BLOCK", - address(uint160(block.number)) - ); - + function _removeDeprecatedAddresses(address addressManager) internal { // Remove all deprecated addresses from the AddressManager string[17] memory deprecated = [ "OVM_CanonicalTransactionChain", diff --git a/packages/contracts-bedrock/test/L1BuildAgent.t.sol b/packages/contracts-bedrock/test/L1BuildAgent.t.sol index 4d7dab6b7..62fa7a15b 100644 --- a/packages/contracts-bedrock/test/L1BuildAgent.t.sol +++ b/packages/contracts-bedrock/test/L1BuildAgent.t.sol @@ -2,44 +2,92 @@ pragma solidity 0.8.15; -import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; - import { console2 as console } from "forge-std/console2.sol"; import { stdStorage, StdStorage } from "forge-std/Test.sol"; +import { TestERC20 } from "test/mocks/TestERC20.sol"; +import { TestERC721 } from "test/mocks/TestERC721.sol"; import { Constants } from "src/libraries/Constants.sol"; import { ProxyAdmin } from "src/universal/ProxyAdmin.sol"; -import { ResourceMetering } from "src/L1/ResourceMetering.sol"; -import { ProtocolVersion } from "src/L1/ProtocolVersions.sol"; - -import { SetupL1BuildAgent } from "test/setup/oasys/SetupL1BuildAgent.sol"; - -contract TestERC721 is ERC721 { - constructor() ERC721("Test", "TST") { } - - function mint(address to, uint256 tokenId) public { - _mint(to, tokenId); - } -} - -contract L1BuildAgentTest is SetupL1BuildAgent { +import { AddressManager } from "src/legacy/AddressManager.sol"; +import { L1CrossDomainMessenger } from "src/L1/L1CrossDomainMessenger.sol"; +import { IL1BuildAgent } from "src/oasys/L1/build/interfaces/IL1BuildAgent.sol"; +import { ResolvedDelegateProxy } from "src/legacy/ResolvedDelegateProxy.sol"; +import { L1ChugSplashProxy } from "src/legacy/L1ChugSplashProxy.sol"; +import { OptimismPortal } from "src/L1/OptimismPortal.sol"; +import { L1StandardBridge } from "src/L1/L1StandardBridge.sol"; +import { L1ERC721Bridge } from "src/L1/L1ERC721Bridge.sol"; + +import { L1BuildAgentTestCommon } from "test/setup/oasys/SetupL1BuildAgent.sol"; +import { MockLegacyL1StandardBridge } from "test/setup/oasys/MockLegacyL1StandardBridge.sol"; +import { MockLegacyL1ERC721Bridge } from "test/setup/oasys/MockLegacyL1ERC721Bridge.sol"; + +contract L1BuildAgentTest is L1BuildAgentTestCommon { using stdStorage for StdStorage; - function test_batchInbox() external view { - assert(deployment.batchInbox == 0xfF00000000000000000000000000000015B3FF00); + function setUp() public override { + super.setUp(); + + vm.prank(builder); + deployment = _runL1BuildAgent( + 5555, + IL1BuildAgent.BuildConfig({ + legacyAddressManager: address(0), + finalSystemOwner: finalOwner, + l2OutputOracleProposer: proposer, + l2OutputOracleChallenger: challenger, + batchSenderAddress: batcher, + p2pSequencerAddress: p2pSequencer, + l2BlockTime: 5, + l2GasLimit: 50_000_000, + l2OutputOracleSubmissionInterval: 50, + finalizationPeriodSeconds: 5 days, + l2OutputOracleStartingBlockNumber: 500, + l2OutputOracleStartingTimestamp: block.timestamp + }) + ); + + console.log("\nWallets"); + console.log("alice : %s", alice); + console.log("bob : %s", bob); + console.log("depositor : %s", depositor); + console.log("builder : %s", builder); + console.log("finalOwner : %s", finalOwner); + console.log("proposer : %s", proposer); + console.log("challenger : %s", challenger); + console.log("batcher : %s", batcher); + console.log("p2pSequencer : %s", p2pSequencer); + + console.log("\nDependency contracts"); + console.log("PermissionedContractFactory : %s", address(permissionedFactory)); + console.log("OasysL2OutputOracleVerifier : %s", address(l2OracleVerifier)); + + console.log("\nL1 Build contracts"); + console.log("L1BuildAgent : %s", address(l1Agent)); + console.log("L1BuildDeposit : %s", address(l1Deposit)); + + console.log("\nProxies"); + console.log("OasysPortal : %s", address(deployment.portal)); + console.log("OasysL2OutputOracle : %s", address(deployment.l2Oracle)); + console.log("SystemConfig : %s", address(deployment.systemConfig)); + console.log("L1CrossDomainMessenger : %s", address(deployment.l1Messenger)); + console.log("L1StandardBridge : %s", address(deployment.l1ERC20Bridge)); + console.log("L1ERC721Bridge : %s", address(deployment.l1ERC721Bridge)); + console.log("ProtocolVersions : %s", address(deployment.protocolVersions)); + + console.log("\nImplementations"); + console.log("OasysPortal : %s", address(deployment.portalImpl)); + console.log("OasysL2OutputOracle : %s", address(deployment.l2OracleImpl)); + console.log("SystemConfig : %s", address(deployment.systemConfigImpl)); + console.log("L1CrossDomainMessenger : %s", address(deployment.l1MessengerImpl)); + console.log("L1StandardBridge : %s", address(deployment.l1ERC20BridgeImpl)); + console.log("L1ERC721Bridge : %s", address(deployment.l1ERC721BridgeImpl)); + console.log("ProtocolVersions : %s", address(deployment.protocolVersionsImpl)); } /** * @dev Tests for `ProxyAdmin` */ - function test_ProxyAdmin_owner() external view { - assert(deployment.proxyAdmin.owner() == deployment.buildCfg.finalSystemOwner); - } - - // function test_ProxyAdmin_addressManager() external view { - // assert(address(deployment.proxyAdmin.addressManager()) == address(deployment.addressManager)); - // } - function test_ProxyAdmin_proxyTypes() external view { assert(deployment.proxyAdmin.proxyType(address(deployment.portal)) == ProxyAdmin.ProxyType.ERC1967); assert(deployment.proxyAdmin.proxyType(address(deployment.l2Oracle)) == ProxyAdmin.ProxyType.ERC1967); @@ -50,247 +98,194 @@ contract L1BuildAgentTest is SetupL1BuildAgent { assert(deployment.proxyAdmin.proxyType(address(deployment.protocolVersions)) == ProxyAdmin.ProxyType.ERC1967); } - // function test_ProxyAdmin_implementationNames() external view { - // assert( - // keccak256(abi.encode(deployment.proxyAdmin.implementationName(address(deployment.l1Messenger)))) - // == keccak256(abi.encode("OVM_L1CrossDomainMessenger")) - // ); - // } - - /** - * @dev Tests for `AddressManager` - */ - // function test_AddressManager_owner() external view { - // assert(deployment.addressManager.owner() == address(deployment.proxyAdmin)); - // } - - // function test_AddressManager_getAddress_OVM_L1CrossDomainMessenger() external view { - // assert( - // deployment.addressManager.getAddress("OVM_L1CrossDomainMessenger") == address(deployment.l1MessengerImpl) - // ); - // } - - /** - * @dev Tests for `OasysPortal` - */ - function test_OasysPortal_initialized() external { - vm.expectRevert("Initializable: contract is already initialized"); - deployment.portal.initialize(false); - } - - function test_OasysPortal_L2_ORACLE() external view { - assert(address(deployment.portal.L2_ORACLE()) == address(deployment.l2Oracle)); - assert(address(deployment.portalImpl.L2_ORACLE()) == address(deployment.l2Oracle)); - } - - function test_OasysPortal_SYSTEM_CONFIG() external view { - assert(address(deployment.portal.SYSTEM_CONFIG()) == address(deployment.systemConfig)); - assert(address(deployment.portalImpl.SYSTEM_CONFIG()) == address(deployment.systemConfig)); - } - - function test_OasysPortal_GUARDIAN() external view { - assert(address(deployment.portal.GUARDIAN()) == deployment.buildCfg.finalSystemOwner); - assert(address(deployment.portalImpl.GUARDIAN()) == deployment.buildCfg.finalSystemOwner); - } - - function test_OasysPortal_l2Sender() external view { - assert(deployment.portal.l2Sender() == 0x000000000000000000000000000000000000dEaD); - assert(deployment.portalImpl.l2Sender() == 0x000000000000000000000000000000000000dEaD); - } - - function test_OasysPortal_paused() external view { - assert(deployment.portal.paused() == false); - assert(deployment.portalImpl.paused() == true); - } - - function test_OasysPortal_messageRelayer() external view { - assert(deployment.portal.messageRelayer() == address(0)); - assert(deployment.portalImpl.messageRelayer() == address(0)); - } - - /** - * @dev Tests for `OasysL2OutputOracle` - */ - function test_OasysL2OutputOracle_initialized() external { - vm.expectRevert("Initializable: contract is already initialized"); - deployment.l2Oracle.initialize(0, 0); - } - - function test_OasysL2OutputOracle_SUBMISSION_INTERVAL() external view { - assert(deployment.l2Oracle.SUBMISSION_INTERVAL() == deployment.buildCfg.l2OutputOracleSubmissionInterval); - assert(deployment.l2OracleImpl.SUBMISSION_INTERVAL() == deployment.buildCfg.l2OutputOracleSubmissionInterval); - } - - function test_OasysL2OutputOracle_L2_BLOCK_TIME() external view { - assert(deployment.l2Oracle.L2_BLOCK_TIME() == deployment.buildCfg.l2BlockTime); - assert(deployment.l2OracleImpl.L2_BLOCK_TIME() == deployment.buildCfg.l2BlockTime); - } - - function test_OasysL2OutputOracle_CHALLENGER() external view { - assert(deployment.l2Oracle.CHALLENGER() == deployment.buildCfg.l2OutputOracleChallenger); - assert(deployment.l2OracleImpl.CHALLENGER() == deployment.buildCfg.l2OutputOracleChallenger); - } - - function testOasysL2OutputOracle__PROPOSER() external view { - assert(deployment.l2Oracle.PROPOSER() == deployment.buildCfg.l2OutputOracleProposer); - assert(deployment.l2OracleImpl.PROPOSER() == deployment.buildCfg.l2OutputOracleProposer); - } - - function test_OasysL2OutputOracle_FINALIZATION_PERIOD_SECONDS() external view { - assert(deployment.l2Oracle.FINALIZATION_PERIOD_SECONDS() == deployment.buildCfg.finalizationPeriodSeconds); - assert(deployment.l2OracleImpl.FINALIZATION_PERIOD_SECONDS() == deployment.buildCfg.finalizationPeriodSeconds); - } - - function test_OasysL2OutputOracle_VERIFIER() external view { - assert(address(deployment.l2Oracle.VERIFIER()) == address(l2OracleVerifier)); - assert(address(deployment.l2OracleImpl.VERIFIER()) == address(l2OracleVerifier)); + function test_ProxyAdmin_implementationNames() external view { + assert( + keccak256(abi.encode(deployment.proxyAdmin.implementationName(address(deployment.l1Messenger)))) + == keccak256(abi.encode("OVM_L1CrossDomainMessenger")) + ); } +} - function test_OasysL2OutputOracle_startingBlockNumber() external view { - assert(deployment.l2Oracle.startingBlockNumber() == deployment.buildCfg.l2OutputOracleStartingBlockNumber); - assert(deployment.l2OracleImpl.startingBlockNumber() == 0); +contract L1BuildAgentUpgradeTest is L1BuildAgentTestCommon { + TestERC20 l1ERC20; + TestERC20 l2ERC20; + TestERC721 l1ERC721; + TestERC721 l2ERC721; + + uint256 depositedAmount; + uint256 depositedTokenId; + + function setUp() public override { + super.setUp(); + + // Deploy legacy address manager, cross, standard bridge, erc721 bridge + (address addressManager,, address l1StandardBridgeProxy, address l1ERC721BridgeProxy) = _deployLegacies(); + + // Deploy ERC20 and ERC721 tokens + _deployTokens(); + + // Deposit ERC20 and ERC721 tokens to each bridges + _depositTokensToBridge(l1StandardBridgeProxy, l1ERC721BridgeProxy); + + // Transfer ownership of the legacy contracts to the builder + _transferOwnerships(addressManager, l1StandardBridgeProxy, l1ERC721BridgeProxy, address(l1Agent)); + + vm.prank(builder); + deployment = _runL1BuildAgent( + 5555, + IL1BuildAgent.BuildConfig({ + legacyAddressManager: addressManager, + finalSystemOwner: finalOwner, + l2OutputOracleProposer: proposer, + l2OutputOracleChallenger: challenger, + batchSenderAddress: batcher, + p2pSequencerAddress: p2pSequencer, + l2BlockTime: 5, + l2GasLimit: 50_000_000, + l2OutputOracleSubmissionInterval: 50, + finalizationPeriodSeconds: 5 days, + l2OutputOracleStartingBlockNumber: 500, + l2OutputOracleStartingTimestamp: block.timestamp + }) + ); + } + + function _deployLegacies() internal returns (address, address, address, address) { + // Deploy address manager + AddressManager addressManager = new AddressManager(); + // Deploy proxies + ResolvedDelegateProxy l1CrossDomainMessengerProxy = new ResolvedDelegateProxy(addressManager, "OVM_L1CrossDomainMessenger"); + L1ChugSplashProxy l1StandardBridgeProxy = new L1ChugSplashProxy(address(this)); + L1ChugSplashProxy l1ERC721BridgeProxy = new L1ChugSplashProxy(address(this)); + // Deploy implementations + L1CrossDomainMessenger l1CrossDomainMessenger = new L1CrossDomainMessenger(OptimismPortal(payable(0))); + MockLegacyL1StandardBridge legacyStandardBridge = new MockLegacyL1StandardBridge(); + MockLegacyL1ERC721Bridge legacyERC721Bridge = new MockLegacyL1ERC721Bridge(); + // Set addresses to address manager + addressManager.setAddress("Proxy__OVM_L1CrossDomainMessenger", address(l1CrossDomainMessengerProxy)); + addressManager.setAddress("Proxy__OVM_L1StandardBridge", address(l1StandardBridgeProxy)); + addressManager.setAddress("Proxy__OVM_L1ERC721Bridge", address(l1ERC721BridgeProxy)); + addressManager.setAddress("OVM_L1CrossDomainMessenger", address(l1CrossDomainMessenger)); + addressManager.setAddress("OVM_CanonicalTransactionChain", address(l1CrossDomainMessenger)); // dummy, expected to be set zero value after build + // Transfer ownership of the address manager to the builder + addressManager.transferOwnership(builder); + // Set implementations to proxies + l1StandardBridgeProxy.setStorage(Constants.PROXY_IMPLEMENTATION_ADDRESS, bytes32(uint256(uint160(address(legacyStandardBridge))))); + l1StandardBridgeProxy.setOwner(builder); + l1ERC721BridgeProxy.setStorage(Constants.PROXY_IMPLEMENTATION_ADDRESS, bytes32(uint256(uint160(address(legacyERC721Bridge))))); + l1ERC721BridgeProxy.setOwner(builder); + return ( + address(addressManager), + address(l1CrossDomainMessengerProxy), + address(l1StandardBridgeProxy), + address(l1ERC721BridgeProxy) + ); + } + + function _deployTokens() internal { + l1ERC20 = new TestERC20(); + l2ERC20 = new TestERC20(); + l1ERC721 = new TestERC721(); + l2ERC721 = new TestERC721(); + } + + function _depositTokensToBridge(address l1StandardBridgeProxy, address l1ERC721BridgeProxy) internal { + // Deposit OAS + depositedAmount = 1 ether; + MockLegacyL1StandardBridge(l1StandardBridgeProxy).depositETH{value: depositedAmount}(50000, hex""); + // Deposit ERC20 + deal(address(l1ERC20), address(alice), depositedAmount); + vm.prank(alice); + l1ERC20.approve(l1StandardBridgeProxy, depositedAmount); + vm.prank(alice); + MockLegacyL1StandardBridge(l1StandardBridgeProxy).depositERC20(address(l1ERC20), address(l2ERC20), depositedAmount, 50000, hex""); + // Deposit ERC721 + depositedTokenId = 1243; + l1ERC721.mint(alice, depositedTokenId); + vm.prank(alice); + l1ERC721.approve(l1ERC721BridgeProxy, depositedTokenId); + vm.prank(alice); + MockLegacyL1ERC721Bridge(l1ERC721BridgeProxy).depositERC721(address(l1ERC721), address(l2ERC721), depositedTokenId, 50000, hex""); } - function test_OasysL2OutputOracle_startingTimestamp() external view { - assert(deployment.l2Oracle.startingTimestamp() == deployment.buildCfg.l2OutputOracleStartingTimestamp); - assert(deployment.l2OracleImpl.startingTimestamp() == 0); + function _transferOwnerships(address manager, address chugProxy1, address chugProxy2, address newOwner) internal { + vm.prank(builder); + AddressManager(manager).transferOwnership(newOwner); + vm.prank(builder); + L1ChugSplashProxy(payable(chugProxy1)).setOwner(newOwner); + vm.prank(builder); + L1ChugSplashProxy(payable(chugProxy2)).setOwner(newOwner); } /** - * @dev Tests for `SystemConfig` + * @dev Tests for `ProxyAdmin` */ - function test_SystemConfig_initialized() external { - ResourceMetering.ResourceConfig memory cfg; - vm.expectRevert("Initializable: contract is already initialized"); - deployment.systemConfig.initialize(address(0), 0, 0, bytes32(0), 0, address(0), cfg); - } - - function test_SystemConfig_owner() external view { - assert(deployment.systemConfig.owner() == deployment.buildCfg.finalSystemOwner); - assert(deployment.systemConfigImpl.owner() == address(0xdEaD)); - } - - function test_SystemConfig_overhead() external view { - assert(deployment.systemConfig.overhead() == 188); - assert(deployment.systemConfigImpl.overhead() == 0); - } - - function test_SystemConfig_scalar() external view { - assert(deployment.systemConfig.scalar() == 684_000); - assert(deployment.systemConfigImpl.scalar() == 0); - } - - function test_SystemConfig_batcherHash() external view { - bytes32 expect = bytes32(uint256(uint160(deployment.buildCfg.batchSenderAddress))); - assert(deployment.systemConfig.batcherHash() == expect); - assert(deployment.systemConfigImpl.batcherHash() == bytes32(0)); - } - - function test_SystemConfig_gasLimit() external view { - assert(deployment.systemConfig.gasLimit() == deployment.buildCfg.l2GasLimit); - assert(deployment.systemConfigImpl.gasLimit() == 20_000_000 + 1_000_000); - } - - function test_SystemConfig_resourceConfig() external view { - ResourceMetering.ResourceConfig memory expect = Constants.DEFAULT_RESOURCE_CONFIG(); - ResourceMetering.ResourceConfig memory actual = deployment.systemConfig.resourceConfig(); - - assert(actual.maxResourceLimit == expect.maxResourceLimit); - assert(actual.elasticityMultiplier == expect.elasticityMultiplier); - assert(actual.baseFeeMaxChangeDenominator == expect.baseFeeMaxChangeDenominator); - assert(actual.minimumBaseFee == expect.minimumBaseFee); - assert(actual.systemTxMaxGas == expect.systemTxMaxGas); - assert(actual.maximumBaseFee == expect.maximumBaseFee); + function test_ProxyAdmin_addressManager() external view { + assert(address(deployment.proxyAdmin.addressManager()) == address(deployment.addressManager)); } - function test_SystemConfig_unsafeBlockSigner() external view { - assert(deployment.systemConfig.unsafeBlockSigner() == deployment.buildCfg.p2pSequencerAddress); - assert(deployment.systemConfigImpl.unsafeBlockSigner() == address(0)); + function test_ProxyAdmin_proxyTypes_succeeds() external view { + assert(deployment.proxyAdmin.proxyType(address(deployment.portal)) == ProxyAdmin.ProxyType.ERC1967); + assert(deployment.proxyAdmin.proxyType(address(deployment.l2Oracle)) == ProxyAdmin.ProxyType.ERC1967); + assert(deployment.proxyAdmin.proxyType(address(deployment.systemConfig)) == ProxyAdmin.ProxyType.ERC1967); + assert(deployment.proxyAdmin.proxyType(address(deployment.l1Messenger)) == ProxyAdmin.ProxyType.RESOLVED); + assert(deployment.proxyAdmin.proxyType(address(deployment.l1ERC20Bridge)) == ProxyAdmin.ProxyType.CHUGSPLASH); + assert(deployment.proxyAdmin.proxyType(address(deployment.l1ERC721Bridge)) == ProxyAdmin.ProxyType.CHUGSPLASH); + assert(deployment.proxyAdmin.proxyType(address(deployment.protocolVersions)) == ProxyAdmin.ProxyType.ERC1967); } - /** - * @dev Tests for `L1CrossDomainMessenger` - */ - function test_L1CrossDomainMessenger_initialized() external { - vm.expectRevert("Initializable: contract is already initialized"); - deployment.l1Messenger.initialize(); + function test_ProxyAdmin_implementationNames() external view { + assert( + keccak256(abi.encode(deployment.proxyAdmin.implementationName(address(deployment.l1Messenger)))) + == keccak256(abi.encode("OVM_L1CrossDomainMessenger")) + ); } - function test_L1CrossDomainMessenger_OTHER_MESSENGER() external view { - assert(deployment.l1Messenger.OTHER_MESSENGER() == 0x4200000000000000000000000000000000000007); - assert(deployment.l1MessengerImpl.OTHER_MESSENGER() == 0x4200000000000000000000000000000000000007); + function test_ProxyAdmin_addressManager_succeeds() external { + assertEq(address(deployment.proxyAdmin.addressManager()), address(deployment.addressManager)); } - function test_L1CrossDomainMessenger_PORTAL() external view { - assert(address(deployment.l1Messenger.PORTAL()) == address(deployment.portal)); - assert(address(deployment.l1MessengerImpl.PORTAL()) == address(deployment.portal)); + function test_ProxyAdmin_AddressManager_owner_succeeds() external { + assertEq(deployment.addressManager.owner(), address(deployment.proxyAdmin)); } /** - * @dev Tests for `L1StandardBridge` + * @dev Tests for `AddressManager` */ - function test_L1StandardBridge_MESSENGER() external view { - assert(address(deployment.l1ERC20Bridge.MESSENGER()) == address(deployment.l1Messenger)); - assert(address(deployment.l1ERC20BridgeImpl.MESSENGER()) == address(deployment.l1Messenger)); + function test_AddressManager_owner() external view { + assert(deployment.addressManager.owner() == address(deployment.proxyAdmin)); } - function test_L1StandardBridge_OTHER_BRIDGE() external view { - assert(address(deployment.l1ERC20Bridge.OTHER_BRIDGE()) == 0x4200000000000000000000000000000000000010); - assert(address(deployment.l1ERC20BridgeImpl.OTHER_BRIDGE()) == 0x4200000000000000000000000000000000000010); + function test_AddressManager_getAddress_OVM_L1CrossDomainMessenger() external view { + assert( + deployment.addressManager.getAddress("OVM_L1CrossDomainMessenger") == address(deployment.l1MessengerImpl) + ); } - function test_L1StandardBridge_depositETH() external { - vm.prank(alice); - deployment.l1ERC20Bridge.depositETH{ value: 1 ether }(50000, hex""); + function test_AddressManager_getAddress_DTL_SHUTOFF_BLOCK_succeeds() external { + assertEq(deployment.addressManager.getAddress("DTL_SHUTOFF_BLOCK"), address(uint160(block.number))); } - /** - * @dev Tests for `L1ERC721Bridge` - */ - function test_L1ERC721Bridge_MESSENGER() external view { - assert(address(deployment.l1ERC721Bridge.MESSENGER()) == address(deployment.l1Messenger)); - assert(address(deployment.l1ERC721BridgeImpl.MESSENGER()) == address(deployment.l1Messenger)); - } - - function test_L1ERC721Bridge_OTHER_BRIDGE() external view { - assert(address(deployment.l1ERC721Bridge.OTHER_BRIDGE()) == 0x6200000000000000000000000000000000000001); - assert(address(deployment.l1ERC721BridgeImpl.OTHER_BRIDGE()) == 0x6200000000000000000000000000000000000001); - } - - function test_L1ERC721Bridge_bridgeERC721() external { - TestERC721 local = new TestERC721(); - TestERC721 remote = new TestERC721(); - uint256 tokenId = 1337; - - local.mint(alice, tokenId); - - vm.prank(alice); - local.approve(address(deployment.l1ERC721Bridge), tokenId); - - vm.prank(alice); - deployment.l1ERC721Bridge.bridgeERC721(address(local), address(remote), tokenId, 50000, hex""); + function test_AddressManager_getAddress_OVM_CanonicalTransactionChain_succeeds() external { + // legacy addresses are expected to be set zero value + assertEq(deployment.addressManager.getAddress("OVM_CanonicalTransactionChain"), address(0)); } /** - * @dev Tests for `ProtocolVersions` + * @dev Tests OAS/ERC20/ERC721 balance migration */ - function test_ProtocolVersions_initialized() external { - vm.expectRevert("Initializable: contract is already initialized"); - deployment.protocolVersions.initialize(address(0), ProtocolVersion.wrap(0), ProtocolVersion.wrap(0)); - } - - function test_ProtocolVersions_owner() external view { - assert(deployment.protocolVersions.owner() == deployment.buildCfg.finalSystemOwner); - assert(deployment.protocolVersionsImpl.owner() == address(0xdEaD)); + function test_OAS_migration_succeeds() external { + // OAS expected to be transferred to OasysPortal + assertEq(address(deployment.portal).balance, depositedAmount); } - function test_ProtocolVersions_required() external view { - assert(ProtocolVersion.unwrap(deployment.protocolVersions.required()) == 0); - assert(ProtocolVersion.unwrap(deployment.protocolVersionsImpl.required()) == 0); + function test_ERC20_migration_succeeds() external { + assertEq(l1ERC20.balanceOf(address(deployment.l1ERC20Bridge)), depositedAmount); + assertEq(L1StandardBridge(deployment.l1ERC20Bridge).deposits(address(l1ERC20), address(l2ERC20)), depositedAmount); } - function test_ProtocolVersions_recommended() external view { - assert(ProtocolVersion.unwrap(deployment.protocolVersions.recommended()) == 0); - assert(ProtocolVersion.unwrap(deployment.protocolVersionsImpl.recommended()) == 0); + function test_ERC721_migration_succeeds() external { + assertEq(l1ERC721.ownerOf(depositedTokenId), address(deployment.l1ERC721Bridge)); + assertEq(L1ERC721Bridge(deployment.l1ERC721Bridge).deposits(address(l1ERC721), address(l2ERC721), depositedTokenId), true); } } diff --git a/packages/contracts-bedrock/test/setup/oasys/MockLegacyL1ERC721Bridge.sol b/packages/contracts-bedrock/test/setup/oasys/MockLegacyL1ERC721Bridge.sol new file mode 100644 index 000000000..3052bf3d5 --- /dev/null +++ b/packages/contracts-bedrock/test/setup/oasys/MockLegacyL1ERC721Bridge.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.9; + +import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; + +contract MockLegacyL1ERC721Bridge { + address public messenger; + address public l2ERC721Bridge; + mapping(address => mapping(address => mapping(uint256 => bool))) public deposits; + function depositERC721( + address _l1Token, + address _l2Token, + uint256 _tokenId, + uint32 /*_l2Gas*/, + bytes calldata /*_data*/ + ) external { + deposits[_l1Token][_l2Token][_tokenId] = true; + IERC721(_l1Token).transferFrom(msg.sender, address(this), _tokenId); + } +} \ No newline at end of file diff --git a/packages/contracts-bedrock/test/setup/oasys/MockLegacyL1StandardBridge.sol b/packages/contracts-bedrock/test/setup/oasys/MockLegacyL1StandardBridge.sol new file mode 100644 index 000000000..59e0b7228 --- /dev/null +++ b/packages/contracts-bedrock/test/setup/oasys/MockLegacyL1StandardBridge.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +contract MockLegacyL1StandardBridge { + address public messenger; + address public l2TokenBridge; + mapping(address => mapping(address => uint256)) public deposits; + function depositETH(uint32 /*_l2Gas*/, bytes calldata /*_data*/) external payable {} + function depositERC20( + address _l1Token, + address _l2Token, + uint256 _amount, + uint32 /*_l2Gas*/, + bytes calldata /*_data*/ + ) external { + deposits[_l1Token][_l2Token] = deposits[_l1Token][_l2Token] + _amount; + IERC20(_l1Token).transferFrom(msg.sender, address(this), _amount); + } +} \ No newline at end of file diff --git a/packages/contracts-bedrock/test/setup/oasys/SetupL1BuildAgent.sol b/packages/contracts-bedrock/test/setup/oasys/SetupL1BuildAgent.sol index 54160b1de..e3a798233 100644 --- a/packages/contracts-bedrock/test/setup/oasys/SetupL1BuildAgent.sol +++ b/packages/contracts-bedrock/test/setup/oasys/SetupL1BuildAgent.sol @@ -14,6 +14,7 @@ import { IL1BuildAgent } from "src/oasys/L1/build/interfaces/IL1BuildAgent.sol"; import { L1BuildAgent } from "src/oasys/L1/build/L1BuildAgent.sol"; import { L1BuildDeposit } from "src/oasys/L1/build/L1BuildDeposit.sol"; +import { TestERC721 } from "test/mocks/TestERC721.sol"; import { Constants } from "src/libraries/Constants.sol"; import { ProxyAdmin } from "src/universal/ProxyAdmin.sol"; import { OasysPortal } from "src/oasys/L1/messaging/OasysPortal.sol"; @@ -23,6 +24,7 @@ import { ResourceMetering } from "src/L1/ResourceMetering.sol"; import { L1CrossDomainMessenger } from "src/L1/L1CrossDomainMessenger.sol"; import { L1StandardBridge } from "src/L1/L1StandardBridge.sol"; import { L1ERC721Bridge } from "src/L1/L1ERC721Bridge.sol"; +import { ProtocolVersion } from "src/L1/ProtocolVersions.sol"; import { ProtocolVersions } from "src/L1/ProtocolVersions.sol"; import { AddressManager } from "src/legacy/AddressManager.sol"; import { OasysL2OutputOracleVerifier } from "src/oasys/L1/rollup/OasysL2OutputOracleVerifier.sol"; @@ -86,8 +88,6 @@ contract SetupL1BuildAgent is Test { /// @dev Default deployment L2 Deployment deployment; - address legacyAddressManager; - function setUp() public virtual { _addBalanceToTestWallets(); @@ -97,64 +97,6 @@ contract SetupL1BuildAgent is Test { vm.prank(depositor); l1Deposit.deposit{ value: 1 ether }(builder); - - legacyAddressManager = address(0); - - vm.prank(builder); - deployment = _runL1BuildAgent( - 5555, - IL1BuildAgent.BuildConfig({ - legacyAddressManager: legacyAddressManager, - finalSystemOwner: finalOwner, - l2OutputOracleProposer: proposer, - l2OutputOracleChallenger: challenger, - batchSenderAddress: batcher, - p2pSequencerAddress: p2pSequencer, - l2BlockTime: 5, - l2GasLimit: 50_000_000, - l2OutputOracleSubmissionInterval: 50, - finalizationPeriodSeconds: 5 days, - l2OutputOracleStartingBlockNumber: 500, - l2OutputOracleStartingTimestamp: block.timestamp - }) - ); - - console.log("\nWallets"); - console.log("alice : %s", alice); - console.log("bob : %s", bob); - console.log("depositor : %s", depositor); - console.log("builder : %s", builder); - console.log("finalOwner : %s", finalOwner); - console.log("proposer : %s", proposer); - console.log("challenger : %s", challenger); - console.log("batcher : %s", batcher); - console.log("p2pSequencer : %s", p2pSequencer); - - console.log("\nDependency contracts"); - console.log("PermissionedContractFactory : %s", address(permissionedFactory)); - console.log("OasysL2OutputOracleVerifier : %s", address(l2OracleVerifier)); - - console.log("\nL1 Build contracts"); - console.log("L1BuildAgent : %s", address(l1Agent)); - console.log("L1BuildDeposit : %s", address(l1Deposit)); - - console.log("\nProxies"); - console.log("OasysPortal : %s", address(deployment.portal)); - console.log("OasysL2OutputOracle : %s", address(deployment.l2Oracle)); - console.log("SystemConfig : %s", address(deployment.systemConfig)); - console.log("L1CrossDomainMessenger : %s", address(deployment.l1Messenger)); - console.log("L1StandardBridge : %s", address(deployment.l1ERC20Bridge)); - console.log("L1ERC721Bridge : %s", address(deployment.l1ERC721Bridge)); - console.log("ProtocolVersions : %s", address(deployment.protocolVersions)); - - console.log("\nImplementations"); - console.log("OasysPortal : %s", address(deployment.portalImpl)); - console.log("OasysL2OutputOracle : %s", address(deployment.l2OracleImpl)); - console.log("SystemConfig : %s", address(deployment.systemConfigImpl)); - console.log("L1CrossDomainMessenger : %s", address(deployment.l1MessengerImpl)); - console.log("L1StandardBridge : %s", address(deployment.l1ERC20BridgeImpl)); - console.log("L1ERC721Bridge : %s", address(deployment.l1ERC721BridgeImpl)); - console.log("ProtocolVersions : %s", address(deployment.protocolVersionsImpl)); } function _addBalanceToTestWallets() internal { @@ -228,7 +170,7 @@ contract SetupL1BuildAgent is Test { protocolVersions: ProtocolVersions(results.protocolVersions), // Deployed implementations proxyAdmin: ProxyAdmin(results.proxyAdmin), - addressManager: AddressManager(legacyAddressManager), + addressManager: AddressManager(cfg.legacyAddressManager), portalImpl: OasysPortal(payable(impls[0])), l2OracleImpl: OasysL2OutputOracle(impls[1]), systemConfigImpl: SystemConfig(impls[2]), @@ -241,3 +183,268 @@ contract SetupL1BuildAgent is Test { }); } } + +contract L1BuildAgentTestCommon is SetupL1BuildAgent { + function test_batchInbox() external view { + assert(deployment.batchInbox == 0xfF00000000000000000000000000000015B3FF00); + } + + /** + * @dev Tests for `ProxyAdmin` + */ + function test_ProxyAdmin_owner() external view { + assert(deployment.proxyAdmin.owner() == deployment.buildCfg.finalSystemOwner); + } + + function test_ProxyAdmin_getProxyAdmin_SystemConfig_succeeds() external { + assertEq(deployment.proxyAdmin.getProxyAdmin(payable(address(deployment.systemConfig))), address(deployment.proxyAdmin)); + } + + function test_ProxyAdmin_getProxyAdmin_OasysPortal_succeeds() external { + assertEq(deployment.proxyAdmin.getProxyAdmin(payable(address(deployment.portal))), address(deployment.proxyAdmin)); + } + + function test_ProxyAdmin_getProxyAdmin_OasysL2OutputOracle_succeeds() external { + assertEq(deployment.proxyAdmin.getProxyAdmin(payable(address(deployment.l2Oracle))), address(deployment.proxyAdmin)); + } + + function test_ProxyAdmin_getProxyAdmin_L1CrossDomainMessenger_succeeds() external { + assertEq(deployment.proxyAdmin.getProxyAdmin(payable(address(deployment.l1Messenger))), address(deployment.proxyAdmin)); + } + + function test_ProxyAdmin_getProxyAdmin_L1StandardBridge_succeeds() external { + assertEq(deployment.proxyAdmin.getProxyAdmin(payable(address(deployment.l1ERC20Bridge))), address(deployment.proxyAdmin)); + } + + function test_ProxyAdmin_getProxyAdmin_L1ERC721Bridge_succeeds() external { + assertEq(deployment.proxyAdmin.getProxyAdmin(payable(address(deployment.l1ERC721Bridge))), address(deployment.proxyAdmin)); + } + + function test_ProxyAdmin_getProxyAdmin_ProtocolVersions_succeeds() external { + assertEq(deployment.proxyAdmin.getProxyAdmin(payable(address(deployment.protocolVersions))), address(deployment.proxyAdmin)); + } + + /** + * @dev Tests for `OasysPortal` + */ + function test_OasysPortal_initialized() external { + vm.expectRevert("Initializable: contract is already initialized"); + deployment.portal.initialize(false); + } + + function test_OasysPortal_L2_ORACLE() external view { + assert(address(deployment.portal.L2_ORACLE()) == address(deployment.l2Oracle)); + assert(address(deployment.portalImpl.L2_ORACLE()) == address(deployment.l2Oracle)); + } + + function test_OasysPortal_SYSTEM_CONFIG() external view { + assert(address(deployment.portal.SYSTEM_CONFIG()) == address(deployment.systemConfig)); + assert(address(deployment.portalImpl.SYSTEM_CONFIG()) == address(deployment.systemConfig)); + } + + function test_OasysPortal_GUARDIAN() external view { + assert(address(deployment.portal.GUARDIAN()) == deployment.buildCfg.finalSystemOwner); + assert(address(deployment.portalImpl.GUARDIAN()) == deployment.buildCfg.finalSystemOwner); + } + + function test_OasysPortal_l2Sender() external view { + assert(deployment.portal.l2Sender() == 0x000000000000000000000000000000000000dEaD); + assert(deployment.portalImpl.l2Sender() == 0x000000000000000000000000000000000000dEaD); + } + + function test_OasysPortal_paused() external view { + assert(deployment.portal.paused() == false); + assert(deployment.portalImpl.paused() == true); + } + + function test_OasysPortal_messageRelayer() external view { + assert(deployment.portal.messageRelayer() == address(0)); + assert(deployment.portalImpl.messageRelayer() == address(0)); + } + + /** + * @dev Tests for `OasysL2OutputOracle` + */ + function test_OasysL2OutputOracle_initialized() external { + vm.expectRevert("Initializable: contract is already initialized"); + deployment.l2Oracle.initialize(0, 0); + } + + function test_OasysL2OutputOracle_SUBMISSION_INTERVAL() external view { + assert(deployment.l2Oracle.SUBMISSION_INTERVAL() == deployment.buildCfg.l2OutputOracleSubmissionInterval); + assert(deployment.l2OracleImpl.SUBMISSION_INTERVAL() == deployment.buildCfg.l2OutputOracleSubmissionInterval); + } + + function test_OasysL2OutputOracle_L2_BLOCK_TIME() external view { + assert(deployment.l2Oracle.L2_BLOCK_TIME() == deployment.buildCfg.l2BlockTime); + assert(deployment.l2OracleImpl.L2_BLOCK_TIME() == deployment.buildCfg.l2BlockTime); + } + + function test_OasysL2OutputOracle_CHALLENGER() external view { + assert(deployment.l2Oracle.CHALLENGER() == deployment.buildCfg.l2OutputOracleChallenger); + assert(deployment.l2OracleImpl.CHALLENGER() == deployment.buildCfg.l2OutputOracleChallenger); + } + + function testOasysL2OutputOracle__PROPOSER() external view { + assert(deployment.l2Oracle.PROPOSER() == deployment.buildCfg.l2OutputOracleProposer); + assert(deployment.l2OracleImpl.PROPOSER() == deployment.buildCfg.l2OutputOracleProposer); + } + + function test_OasysL2OutputOracle_FINALIZATION_PERIOD_SECONDS() external view { + assert(deployment.l2Oracle.FINALIZATION_PERIOD_SECONDS() == deployment.buildCfg.finalizationPeriodSeconds); + assert(deployment.l2OracleImpl.FINALIZATION_PERIOD_SECONDS() == deployment.buildCfg.finalizationPeriodSeconds); + } + + function test_OasysL2OutputOracle_VERIFIER() external view { + assert(address(deployment.l2Oracle.VERIFIER()) == address(l2OracleVerifier)); + assert(address(deployment.l2OracleImpl.VERIFIER()) == address(l2OracleVerifier)); + } + + function test_OasysL2OutputOracle_startingBlockNumber() external view { + assert(deployment.l2Oracle.startingBlockNumber() == deployment.buildCfg.l2OutputOracleStartingBlockNumber); + assert(deployment.l2OracleImpl.startingBlockNumber() == 0); + } + + function test_OasysL2OutputOracle_startingTimestamp() external view { + assert(deployment.l2Oracle.startingTimestamp() == deployment.buildCfg.l2OutputOracleStartingTimestamp); + assert(deployment.l2OracleImpl.startingTimestamp() == 0); + } + + /** + * @dev Tests for `SystemConfig` + */ + function test_SystemConfig_initialized() external { + ResourceMetering.ResourceConfig memory cfg; + vm.expectRevert("Initializable: contract is already initialized"); + deployment.systemConfig.initialize(address(0), 0, 0, bytes32(0), 0, address(0), cfg); + } + + function test_SystemConfig_owner() external view { + assert(deployment.systemConfig.owner() == deployment.buildCfg.finalSystemOwner); + assert(deployment.systemConfigImpl.owner() == address(0xdEaD)); + } + + function test_SystemConfig_overhead() external view { + assert(deployment.systemConfig.overhead() == 188); + assert(deployment.systemConfigImpl.overhead() == 0); + } + + function test_SystemConfig_scalar() external view { + assert(deployment.systemConfig.scalar() == 684_000); + assert(deployment.systemConfigImpl.scalar() == 0); + } + + function test_SystemConfig_batcherHash() external view { + bytes32 expect = bytes32(uint256(uint160(deployment.buildCfg.batchSenderAddress))); + assert(deployment.systemConfig.batcherHash() == expect); + assert(deployment.systemConfigImpl.batcherHash() == bytes32(0)); + } + + function test_SystemConfig_gasLimit() external view { + assert(deployment.systemConfig.gasLimit() == deployment.buildCfg.l2GasLimit); + assert(deployment.systemConfigImpl.gasLimit() == 20_000_000 + 1_000_000); + } + + function test_SystemConfig_resourceConfig() external view { + ResourceMetering.ResourceConfig memory expect = Constants.DEFAULT_RESOURCE_CONFIG(); + ResourceMetering.ResourceConfig memory actual = deployment.systemConfig.resourceConfig(); + + assert(actual.maxResourceLimit == expect.maxResourceLimit); + assert(actual.elasticityMultiplier == expect.elasticityMultiplier); + assert(actual.baseFeeMaxChangeDenominator == expect.baseFeeMaxChangeDenominator); + assert(actual.minimumBaseFee == expect.minimumBaseFee); + assert(actual.systemTxMaxGas == expect.systemTxMaxGas); + assert(actual.maximumBaseFee == expect.maximumBaseFee); + } + + function test_SystemConfig_unsafeBlockSigner() external view { + assert(deployment.systemConfig.unsafeBlockSigner() == deployment.buildCfg.p2pSequencerAddress); + assert(deployment.systemConfigImpl.unsafeBlockSigner() == address(0)); + } + + /** + * @dev Tests for `L1CrossDomainMessenger` + */ + function test_L1CrossDomainMessenger_initialized() external { + vm.expectRevert("Initializable: contract is already initialized"); + deployment.l1Messenger.initialize(); + } + + function test_L1CrossDomainMessenger_OTHER_MESSENGER() external view { + assert(deployment.l1Messenger.OTHER_MESSENGER() == 0x4200000000000000000000000000000000000007); + assert(deployment.l1MessengerImpl.OTHER_MESSENGER() == 0x4200000000000000000000000000000000000007); + } + + function test_L1CrossDomainMessenger_PORTAL() external view { + assert(address(deployment.l1Messenger.PORTAL()) == address(deployment.portal)); + assert(address(deployment.l1MessengerImpl.PORTAL()) == address(deployment.portal)); + } + + /** + * @dev Tests for `L1StandardBridge` + */ + function test_L1StandardBridge_MESSENGER() external view { + assert(address(deployment.l1ERC20Bridge.MESSENGER()) == address(deployment.l1Messenger)); + assert(address(deployment.l1ERC20BridgeImpl.MESSENGER()) == address(deployment.l1Messenger)); + } + + function test_L1StandardBridge_OTHER_BRIDGE() external view { + assert(address(deployment.l1ERC20Bridge.OTHER_BRIDGE()) == 0x4200000000000000000000000000000000000010); + assert(address(deployment.l1ERC20BridgeImpl.OTHER_BRIDGE()) == 0x4200000000000000000000000000000000000010); + } + + function test_L1StandardBridge_depositETH() external { + vm.prank(alice); + deployment.l1ERC20Bridge.depositETH{ value: 1 ether }(50000, hex""); + } + + /** + * @dev Tests for `L1ERC721Bridge` + */ + function test_L1ERC721Bridge_MESSENGER() external view { + assert(address(deployment.l1ERC721Bridge.MESSENGER()) == address(deployment.l1Messenger)); + assert(address(deployment.l1ERC721BridgeImpl.MESSENGER()) == address(deployment.l1Messenger)); + } + + function test_L1ERC721Bridge_OTHER_BRIDGE() external view { + assert(address(deployment.l1ERC721Bridge.OTHER_BRIDGE()) == 0x6200000000000000000000000000000000000001); + assert(address(deployment.l1ERC721BridgeImpl.OTHER_BRIDGE()) == 0x6200000000000000000000000000000000000001); + } + + function test_L1ERC721Bridge_bridgeERC721() external { + TestERC721 local = new TestERC721(); + TestERC721 remote = new TestERC721(); + uint256 tokenId = 1337; + + local.mint(alice, tokenId); + + vm.prank(alice); + local.approve(address(deployment.l1ERC721Bridge), tokenId); + + vm.prank(alice); + deployment.l1ERC721Bridge.bridgeERC721(address(local), address(remote), tokenId, 50000, hex""); + } + + /** + * @dev Tests for `ProtocolVersions` + */ + function test_ProtocolVersions_initialized() external { + vm.expectRevert("Initializable: contract is already initialized"); + deployment.protocolVersions.initialize(address(0), ProtocolVersion.wrap(0), ProtocolVersion.wrap(0)); + } + + function test_ProtocolVersions_owner() external view { + assert(deployment.protocolVersions.owner() == deployment.buildCfg.finalSystemOwner); + assert(deployment.protocolVersionsImpl.owner() == address(0xdEaD)); + } + + function test_ProtocolVersions_required() external view { + assert(ProtocolVersion.unwrap(deployment.protocolVersions.required()) == 0); + assert(ProtocolVersion.unwrap(deployment.protocolVersionsImpl.required()) == 0); + } + + function test_ProtocolVersions_recommended() external view { + assert(ProtocolVersion.unwrap(deployment.protocolVersions.recommended()) == 0); + assert(ProtocolVersion.unwrap(deployment.protocolVersionsImpl.recommended()) == 0); + } +} From 29d1c0a54f8ada31b31fd835189e85492579d208 Mon Sep 17 00:00:00 2001 From: tak Date: Sun, 31 Mar 2024 12:45:44 +0900 Subject: [PATCH 3/9] respond feedback from ironbeer --- .../scripts/oasys/L1/build/Build.s.sol | 1 - .../src/L2/L2ERC721Bridge.sol | 2 +- .../src/oasys/L1/build/L1BuildAgent.sol | 105 +++++++++++------- .../src/oasys/L1/build/L1BuildDeposit.sol | 21 ++++ .../L1/build/interfaces/IL1BuildAgent.sol | 10 +- .../L1/build/interfaces/IL1BuildDeposit.sol | 4 + .../build/interfaces/ILegacyL1BuildAgent.sol | 1 + .../L1/build/legacy/LegacyL1BuildDeposit.sol | 2 +- ....sol => OasysERC721BridgeLegacySpacer.sol} | 9 +- .../src/universal/ERC721Bridge.sol | 4 +- .../contracts-bedrock/test/L1BuildAgent.t.sol | 28 +++-- .../setup/oasys/MockLegacyL1BuildAgent.sol | 39 +++++++ .../setup/oasys/MockLegacyL1BuildDeposit.sol | 12 ++ .../test/setup/oasys/SetupL1BuildAgent.sol | 18 ++- 14 files changed, 188 insertions(+), 68 deletions(-) rename packages/contracts-bedrock/src/oasys/L1/messaging/{OasysL1ERC721BridgeLegacySpacer.sol => OasysERC721BridgeLegacySpacer.sol} (73%) create mode 100644 packages/contracts-bedrock/test/setup/oasys/MockLegacyL1BuildAgent.sol create mode 100644 packages/contracts-bedrock/test/setup/oasys/MockLegacyL1BuildDeposit.sol diff --git a/packages/contracts-bedrock/scripts/oasys/L1/build/Build.s.sol b/packages/contracts-bedrock/scripts/oasys/L1/build/Build.s.sol index 18ad6c0bb..a5a16365c 100644 --- a/packages/contracts-bedrock/scripts/oasys/L1/build/Build.s.sol +++ b/packages/contracts-bedrock/scripts/oasys/L1/build/Build.s.sol @@ -302,7 +302,6 @@ contract Build is Script { // construct a build configuration. buildCfg = IL1BuildAgent.BuildConfig({ - legacyAddressManager: address(0), finalSystemOwner: deployCfg.finalSystemOwner, l2OutputOracleProposer: deployCfg.l2OutputOracleProposer, l2OutputOracleChallenger: deployCfg.l2OutputOracleChallenger, diff --git a/packages/contracts-bedrock/src/L2/L2ERC721Bridge.sol b/packages/contracts-bedrock/src/L2/L2ERC721Bridge.sol index b30dba280..0ae09ade2 100644 --- a/packages/contracts-bedrock/src/L2/L2ERC721Bridge.sol +++ b/packages/contracts-bedrock/src/L2/L2ERC721Bridge.sol @@ -46,7 +46,7 @@ contract L2ERC721Bridge is ERC721Bridge, ISemver { uint256 _tokenId, bytes calldata _extraData ) - external + public virtual onlyOtherBridge { diff --git a/packages/contracts-bedrock/src/oasys/L1/build/L1BuildAgent.sol b/packages/contracts-bedrock/src/oasys/L1/build/L1BuildAgent.sol index 25baf9669..e0b7de3f6 100644 --- a/packages/contracts-bedrock/src/oasys/L1/build/L1BuildAgent.sol +++ b/packages/contracts-bedrock/src/oasys/L1/build/L1BuildAgent.sol @@ -62,7 +62,7 @@ contract L1BuildAgent is IL1BuildAgent, ISemver { string public constant version = "2.0.0"; /// @notice The map of chainId => builder - mapping(uint256 => address) public builders; + mapping(uint256 => address) private builders; /// @notice The map of chainId => BuiltAddressList mapping(uint256 => BuiltAddressList) public builtLists; @@ -74,7 +74,7 @@ contract L1BuildAgent is IL1BuildAgent, ISemver { uint256[] public chainIds; /// @notice The flag to pause the L1CrossDomainMessenger - bool public messengerPaused; + mapping(uint256 => bool) public messengerPauseds; constructor( IBuildProxy _bProxy, @@ -109,10 +109,11 @@ contract L1BuildAgent is IL1BuildAgent, ISemver { /// Ref: step2 of `SystemDictator` /// https://github.com/oasysgames/oasys-opstack/blob/cd7c58349542f9f1ce9fd42c9054aeed1325e02c/packages/contracts-bedrock/contracts/deployment/SystemDictator.sol /// @param addressManager The address manager of the existing L2 - function pauseLegacyL1CrossDomainMessenger(address addressManager) public { - require(!messengerPaused, "L1BuildAgent: already paused"); + function pauseLegacyL1CrossDomainMessenger(uint256 _chainId, address addressManager) public { + require(getBuilderGlobally(_chainId) == msg.sender, "L1BuildAgent: inconsistent builder"); + require(!messengerPauseds[_chainId], "L1BuildAgent: already paused"); - messengerPaused = true; + messengerPauseds[_chainId] = true; // Temporarily brick the L1CrossDomainMessenger by setting its implementation address to // address(0) which will cause the ResolvedDelegateProxy to revert. Better than pausing @@ -132,10 +133,11 @@ contract L1BuildAgent is IL1BuildAgent, ISemver { /// @notice Unpause the legacy L1CrossDomainMessenger /// @param addressManager The address manager of the existing L2 /// @param oldL1CrossDomainMessenger The address of the old L1CrossDomainMessenger - function unpauseLegacyL1CrossDomainMessenger(address addressManager, address oldL1CrossDomainMessenger) public { - require(messengerPaused, "L1BuildAgent: not paused"); + function unpauseLegacyL1CrossDomainMessenger(uint256 _chainId, address addressManager, address oldL1CrossDomainMessenger) public { + require(getBuilderGlobally(_chainId) == msg.sender, "L1BuildAgent: inconsistent builder"); + require(messengerPauseds[_chainId], "L1BuildAgent: not paused"); - messengerPaused = false; + messengerPauseds[_chainId] = false; // Reset the L1CrossDomainMessenger to the old implementation. AddressManager(addressManager).setAddress( @@ -157,7 +159,6 @@ contract L1BuildAgent is IL1BuildAgent, ISemver { external returns (BuiltAddressList memory, address[7] memory) { - // Only the builder can build the L2 // The builder is the person who deposits the required amount address builder = msg.sender; @@ -180,36 +181,37 @@ contract L1BuildAgent is IL1BuildAgent, ISemver { builders[_chainId] = builder; // check if the L2 is upgrading the existing L2 - // If so, the existing address manager is set to the legacyAddressManager - // otherwise, legacyAddressManager is empty - bool isUpgradingExistingL2 = _cfg.legacyAddressManager != address(0); + (bool isUpgrading, address addressManager) = isUpgradingExistingL2(_chainId); // temporarily set the admin to this contract // transfer ownership to the final system owner at the end of building address admin = address(this); // deploy proxy contracts for each verse - ProxyAdmin proxyAdmin = _deployProxies(_chainId, admin, _cfg.legacyAddressManager); + ProxyAdmin proxyAdmin = _deployProxies(_chainId, admin, addressManager); + + if (isUpgrading) { + // Verify the builder is consistent with chain id; + require(getBuilderGlobally(_chainId) == builder, "L1BuildAgent: inconsistent builder"); - if (isUpgradingExistingL2) { - if (!messengerPaused) { + if (!messengerPauseds[_chainId]) { // Pause the legacy L1CrossDomainMessenger - pauseLegacyL1CrossDomainMessenger(_cfg.legacyAddressManager); + pauseLegacyL1CrossDomainMessenger(_chainId, addressManager); } // Remove deprecated addresses from the AddressManager - _removeDeprecatedAddresses(_cfg.legacyAddressManager); + _removeDeprecatedAddresses(addressManager); // Set the address of the AddressManager. - proxyAdmin.setAddressManager(AddressManager(_cfg.legacyAddressManager)); - require(proxyAdmin.addressManager() == AddressManager(_cfg.legacyAddressManager)); + proxyAdmin.setAddressManager(AddressManager(addressManager)); + require(proxyAdmin.addressManager() == AddressManager(addressManager)); // transfer ownership of the address manager to the ProxyAdmin - AddressManager(_cfg.legacyAddressManager).transferOwnership(address(proxyAdmin)); + AddressManager(addressManager).transferOwnership(address(proxyAdmin)); } // don't deploy the implementation contracts every time // to save gas, reuse the same implementation contract for each proxy address[7] memory impls = _deployImplementations(_chainId, _cfg); - emit Deployed(_chainId, _cfg.finalSystemOwner, _cfg.legacyAddressManager, builtLists[_chainId], impls); + emit Deployed(_chainId, _cfg.finalSystemOwner, builtLists[_chainId], impls); // append the chainId to the list chainIds.push(_chainId); @@ -219,9 +221,9 @@ contract L1BuildAgent is IL1BuildAgent, ISemver { // OasysPortal should be initialized before L1StandardBridge, // because L1StandardBridge uses OasysPortal as a recipient of the ETH _initializeOasysPortal(_chainId, proxyAdmin, impls[0]); - _initializeL1StandardBridge(_chainId, proxyAdmin, impls[4], isUpgradingExistingL2); - _initializeL1ERC721Bridge(_chainId, proxyAdmin, impls[5], isUpgradingExistingL2); - _initializeL1CrossDomainMessenger(_chainId, proxyAdmin, impls[3], isUpgradingExistingL2); + _initializeL1StandardBridge(_chainId, proxyAdmin, impls[4], isUpgrading); + _initializeL1ERC721Bridge(_chainId, proxyAdmin, impls[5], isUpgrading); + _initializeL1CrossDomainMessenger(_chainId, proxyAdmin, impls[3], isUpgrading); _initializeOasysL2OutputOracle(_chainId, _cfg, proxyAdmin, impls[1]); _initializeProtocolVersions(_chainId, _cfg, proxyAdmin, impls[6]); @@ -231,6 +233,34 @@ contract L1BuildAgent is IL1BuildAgent, ISemver { return (builtLists[_chainId], impls); } + /// @notice Return builder address corresponding to the chainId. traverse the legacy build agent + /// @param _chainId The chainId of Verse + function getBuilderGlobally(uint256 _chainId) public view returns (address builder) { + if (LEGACY_L1_BUILD_AGENT != ILegacyL1BuildAgent(address(0))) { + uint256 howMany = 255; // type(uint256).max; -> this leads memory allocation error + (address[] memory _builders, uint256[] memory _chainIds,) = LEGACY_L1_BUILD_AGENT.getBuilts(0, howMany); + for (uint256 i = 0; i < _chainIds.length; i++) { + if (_chainIds[i] == _chainId) { + return _builders[i]; + } + } + } + return getBuilderInternally(_chainId); + } + + /// @notice Return builder address corresponding to the chainId within this contract + /// @param _chainId The chainId of Verse + function getBuilderInternally(uint256 _chainId) public view returns (address) { + return builders[_chainId]; + } + + /// @notice Check if the L2 is upgrading the existing L2 + /// @param _chainId The chainId of Verse + function isUpgradingExistingL2(uint256 _chainId) public view returns(bool, address) { + address addressManager = LEGACY_L1_BUILD_AGENT.getAddressManager(_chainId); + return (addressManager != address(0), addressManager); + } + /// @notice Compute inbox address from chainId /// @param _chainId The chainId of Verse function computeInboxAddress(uint256 _chainId) public pure returns (address) { @@ -442,11 +472,11 @@ contract L1BuildAgent is IL1BuildAgent, ISemver { uint256 _chainId, ProxyAdmin proxyAdmin, address impl, - bool isUpgradingExistingL2 + bool isUpgrading ) internal { address l1StandardBridgeProxy = builtLists[_chainId].l1StandardBridge; - if (isUpgradingExistingL2) { + if (isUpgrading) { // The proxy of Legacy L2 is L1ChugSplashProxy, so need to set type proxyAdmin.setProxyType(l1StandardBridgeProxy, ProxyAdmin.ProxyType.CHUGSPLASH); require(uint256(proxyAdmin.proxyType(l1StandardBridgeProxy)) == uint256(ProxyAdmin.ProxyType.CHUGSPLASH)); @@ -468,11 +498,11 @@ contract L1BuildAgent is IL1BuildAgent, ISemver { uint256 _chainId, ProxyAdmin proxyAdmin, address impl, - bool isUpgradingExistingL2 + bool isUpgrading ) internal { address l1ERC721BridgeProxy = builtLists[_chainId].l1ERC721Bridge; - if (isUpgradingExistingL2) { + if (isUpgrading) { // The proxy of Legacy L2 is L1ChugSplashProxy, so need to set type proxyAdmin.setProxyType(l1ERC721BridgeProxy, ProxyAdmin.ProxyType.CHUGSPLASH); require(uint256(proxyAdmin.proxyType(l1ERC721BridgeProxy)) == uint256(ProxyAdmin.ProxyType.CHUGSPLASH)); @@ -486,27 +516,26 @@ contract L1BuildAgent is IL1BuildAgent, ISemver { uint256 _chainId, ProxyAdmin proxyAdmin, address impl, - bool isUpgradingExistingL2 + bool isUpgrading ) internal { address l1CrossDomainMessengerProxy = builtLists[_chainId].l1CrossDomainMessenger; - if (isUpgradingExistingL2) { + if (isUpgrading) { // The proxy of Legacy L2 is ResolvedDelegateProxy, so need to set type and implementation name // Set proxy type to RESOLVED proxyAdmin.setProxyType(l1CrossDomainMessengerProxy, ProxyAdmin.ProxyType.RESOLVED); require(uint256(proxyAdmin.proxyType(l1CrossDomainMessengerProxy)) == uint256(ProxyAdmin.ProxyType.RESOLVED)); + // Set the implementation name to OVM_L1CrossDomainMessenger + string memory contractName = "OVM_L1CrossDomainMessenger"; + proxyAdmin.setImplementationName(l1CrossDomainMessengerProxy, contractName); + require( + keccak256(bytes(proxyAdmin.implementationName(l1CrossDomainMessengerProxy))) + == keccak256(bytes(contractName)) + ); } - // Set the implementation name to OVM_L1CrossDomainMessenger - string memory contractName = "OVM_L1CrossDomainMessenger"; - proxyAdmin.setImplementationName(l1CrossDomainMessengerProxy, contractName); - require( - keccak256(bytes(proxyAdmin.implementationName(l1CrossDomainMessengerProxy))) - == keccak256(bytes(contractName)) - ); - proxyAdmin.upgradeAndCall({ _proxy: payable(l1CrossDomainMessengerProxy), _implementation: impl, diff --git a/packages/contracts-bedrock/src/oasys/L1/build/L1BuildDeposit.sol b/packages/contracts-bedrock/src/oasys/L1/build/L1BuildDeposit.sol index c763bfc6b..c4641dfd6 100644 --- a/packages/contracts-bedrock/src/oasys/L1/build/L1BuildDeposit.sol +++ b/packages/contracts-bedrock/src/oasys/L1/build/L1BuildDeposit.sol @@ -34,4 +34,25 @@ contract L1BuildDeposit is ISemver, LegacyL1BuildDeposit { function getDepositTotalIncludeLegacy(address _builder) public view returns (uint256) { return getDepositTotal(_builder) + legacyL1BuildDeposit.getDepositTotal(_builder); } + + + /** + * Returns the whether the address is the builder globally. + * @param _builder Address of the Verse-Builder. + */ + function isBuilderGlobally(address _builder) public view returns(bool) { + bool builtLegacy; + if (legacyL1BuildDeposit != IL1BuildDeposit(address(0))) { + builtLegacy = legacyL1BuildDeposit.getBuildBlock(_builder) > 0; + } + return builtLegacy || isBuilderInternally(_builder); + } + + /** + * Returns the whether the address is the internal builder. + * @param _builder Address of the Verse-Builder. + */ + function isBuilderInternally(address _builder) public view returns(bool) { + return getBuildBlock(_builder) > 0; + } } diff --git a/packages/contracts-bedrock/src/oasys/L1/build/interfaces/IL1BuildAgent.sol b/packages/contracts-bedrock/src/oasys/L1/build/interfaces/IL1BuildAgent.sol index 9ce72cb55..6c559dafd 100644 --- a/packages/contracts-bedrock/src/oasys/L1/build/interfaces/IL1BuildAgent.sol +++ b/packages/contracts-bedrock/src/oasys/L1/build/interfaces/IL1BuildAgent.sol @@ -3,11 +3,6 @@ pragma solidity 0.8.15; interface IL1BuildAgent { struct BuildConfig { - // The address of `Lib_AddressManager`. - // Value: - // - for new chain : address(0) - // - for existing chain : pre-deployed address - address legacyAddressManager; // The owner of L1 contract set. Any L1 contract that is ownable has this account set as its owner // Value: depending on each verse address finalSystemOwner; @@ -69,7 +64,6 @@ interface IL1BuildAgent { event Deployed( uint256 indexed chainId, address finalSystemOwner, - address legacyAddressManager, BuiltAddressList results, address[7] impls ); @@ -81,10 +75,14 @@ interface IL1BuildAgent { function chainIds(uint256 index) external view returns (uint256 chainId); + function getBuilderGlobally(uint256 chainId) external view returns (address builder); + function computeInboxAddress(uint256 chainId) external view returns (address batchInbox); function isUniqueChainId(uint256 chainId) external view returns (bool); + function isUpgradingExistingL2(uint256 _chainId) external returns(bool, address); + function build( uint256 chainId, BuildConfig calldata cfg diff --git a/packages/contracts-bedrock/src/oasys/L1/build/interfaces/IL1BuildDeposit.sol b/packages/contracts-bedrock/src/oasys/L1/build/interfaces/IL1BuildDeposit.sol index cc14f1644..bb0cdb367 100644 --- a/packages/contracts-bedrock/src/oasys/L1/build/interfaces/IL1BuildDeposit.sol +++ b/packages/contracts-bedrock/src/oasys/L1/build/interfaces/IL1BuildDeposit.sol @@ -11,4 +11,8 @@ interface IL1BuildDeposit { function deposit(address _builder) external payable; function withdraw(address _builder, uint256 _amount) external; + + function getBuildBlock(address _builder) external view returns (uint256); + + function isBuilderGlobally(address _builder) external view returns(bool); } diff --git a/packages/contracts-bedrock/src/oasys/L1/build/interfaces/ILegacyL1BuildAgent.sol b/packages/contracts-bedrock/src/oasys/L1/build/interfaces/ILegacyL1BuildAgent.sol index 422a173d2..bb1b62ee8 100644 --- a/packages/contracts-bedrock/src/oasys/L1/build/interfaces/ILegacyL1BuildAgent.sol +++ b/packages/contracts-bedrock/src/oasys/L1/build/interfaces/ILegacyL1BuildAgent.sol @@ -3,4 +3,5 @@ pragma solidity 0.8.15; interface ILegacyL1BuildAgent { function getAddressManager(uint256 _chainId) external view returns (address); + function getBuilts(uint256 cursor, uint256 howMany) external view returns (address[] memory, uint256[] memory, uint256); } diff --git a/packages/contracts-bedrock/src/oasys/L1/build/legacy/LegacyL1BuildDeposit.sol b/packages/contracts-bedrock/src/oasys/L1/build/legacy/LegacyL1BuildDeposit.sol index 39f036e1e..8c8cc177f 100644 --- a/packages/contracts-bedrock/src/oasys/L1/build/legacy/LegacyL1BuildDeposit.sol +++ b/packages/contracts-bedrock/src/oasys/L1/build/legacy/LegacyL1BuildDeposit.sol @@ -198,7 +198,7 @@ contract LegacyL1BuildDeposit { * @param _builder Address of the Verse-Builder. * @return block Block number. */ - function getBuildBlock(address _builder) external view returns (uint256) { + function getBuildBlock(address _builder) public view returns (uint256) { return _buildBlock[_builder]; } diff --git a/packages/contracts-bedrock/src/oasys/L1/messaging/OasysL1ERC721BridgeLegacySpacer.sol b/packages/contracts-bedrock/src/oasys/L1/messaging/OasysERC721BridgeLegacySpacer.sol similarity index 73% rename from packages/contracts-bedrock/src/oasys/L1/messaging/OasysL1ERC721BridgeLegacySpacer.sol rename to packages/contracts-bedrock/src/oasys/L1/messaging/OasysERC721BridgeLegacySpacer.sol index 8df18deac..0ac9d4bb6 100644 --- a/packages/contracts-bedrock/src/oasys/L1/messaging/OasysL1ERC721BridgeLegacySpacer.sol +++ b/packages/contracts-bedrock/src/oasys/L1/messaging/OasysERC721BridgeLegacySpacer.sol @@ -3,17 +3,18 @@ pragma solidity 0.8.15; import { L2OutputOracle } from "src/L1/L2OutputOracle.sol"; -/// @title OasysL1ERC721BridgeLegacySpacer -/// @notice The LegacyOasysStorageLayout_L1ERC721Bridge is a contract that defines the storage layout of Oasys Legacy L1ERC721Bridge. +/// @title OasysERC721BridgeLegacySpacer +/// @notice Defines the storage layout of Oasys Legacy ERC721Bridge. /// Ref: https://github.com/oasysgames/oasys-optimism/blob/4d667a169296f37422ffaa4901e8d149e84abe5a/packages/contracts/contracts/oasys/L1/messaging/L1ERC721BridgeV2.sol -contract OasysL1ERC721BridgeLegacySpacer { +/// Ref: https://github.com/oasysgames/oasys-optimism/blob/4d667a169296f37422ffaa4901e8d149e84abe5a/packages/contracts/contracts/oasys/L2/messaging/L2ERC721Bridge.sol +contract OasysERC721BridgeLegacySpacer { /// @custom:legacy /// @custom:spacer messenger /// @notice Spacer for backwards compatibility. address private spacer_0_0_20; /// @custom:legacy - /// @custom:spacer l2ERC721Bridge + /// @custom:spacer l1ERC721Bridge,l2ERC721Bridge /// @notice Spacer for backwards compatibility. address private spacer_1_0_20; diff --git a/packages/contracts-bedrock/src/universal/ERC721Bridge.sol b/packages/contracts-bedrock/src/universal/ERC721Bridge.sol index 78fab17ab..00a8666e8 100644 --- a/packages/contracts-bedrock/src/universal/ERC721Bridge.sol +++ b/packages/contracts-bedrock/src/universal/ERC721Bridge.sol @@ -4,11 +4,11 @@ pragma solidity 0.8.15; import { CrossDomainMessenger } from "src/universal/CrossDomainMessenger.sol"; import { Address } from "@openzeppelin/contracts/utils/Address.sol"; import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol"; -import { OasysL1ERC721BridgeLegacySpacer } from "src/oasys/L1/messaging/OasysL1ERC721BridgeLegacySpacer.sol"; +import { OasysERC721BridgeLegacySpacer } from "src/oasys/L1/messaging/OasysERC721BridgeLegacySpacer.sol"; /// @title ERC721Bridge /// @notice ERC721Bridge is a base contract for the L1 and L2 ERC721 bridges. -abstract contract ERC721Bridge is OasysL1ERC721BridgeLegacySpacer { +abstract contract ERC721Bridge is OasysERC721BridgeLegacySpacer { /// @notice Messenger contract on this domain. This will be removed in the /// future, use `messenger` instead. /// @custom:legacy diff --git a/packages/contracts-bedrock/test/L1BuildAgent.t.sol b/packages/contracts-bedrock/test/L1BuildAgent.t.sol index 62fa7a15b..f47be68aa 100644 --- a/packages/contracts-bedrock/test/L1BuildAgent.t.sol +++ b/packages/contracts-bedrock/test/L1BuildAgent.t.sol @@ -19,6 +19,7 @@ import { L1StandardBridge } from "src/L1/L1StandardBridge.sol"; import { L1ERC721Bridge } from "src/L1/L1ERC721Bridge.sol"; import { L1BuildAgentTestCommon } from "test/setup/oasys/SetupL1BuildAgent.sol"; +import { MockLegacyL1BuildAgent } from "test/setup/oasys/MockLegacyL1BuildAgent.sol"; import { MockLegacyL1StandardBridge } from "test/setup/oasys/MockLegacyL1StandardBridge.sol"; import { MockLegacyL1ERC721Bridge } from "test/setup/oasys/MockLegacyL1ERC721Bridge.sol"; @@ -31,8 +32,8 @@ contract L1BuildAgentTest is L1BuildAgentTestCommon { vm.prank(builder); deployment = _runL1BuildAgent( 5555, + address(0), IL1BuildAgent.BuildConfig({ - legacyAddressManager: address(0), finalSystemOwner: finalOwner, l2OutputOracleProposer: proposer, l2OutputOracleChallenger: challenger, @@ -97,13 +98,6 @@ contract L1BuildAgentTest is L1BuildAgentTestCommon { assert(deployment.proxyAdmin.proxyType(address(deployment.l1ERC721Bridge)) == ProxyAdmin.ProxyType.ERC1967); assert(deployment.proxyAdmin.proxyType(address(deployment.protocolVersions)) == ProxyAdmin.ProxyType.ERC1967); } - - function test_ProxyAdmin_implementationNames() external view { - assert( - keccak256(abi.encode(deployment.proxyAdmin.implementationName(address(deployment.l1Messenger)))) - == keccak256(abi.encode("OVM_L1CrossDomainMessenger")) - ); - } } contract L1BuildAgentUpgradeTest is L1BuildAgentTestCommon { @@ -130,11 +124,17 @@ contract L1BuildAgentUpgradeTest is L1BuildAgentTestCommon { // Transfer ownership of the legacy contracts to the builder _transferOwnerships(addressManager, l1StandardBridgeProxy, l1ERC721BridgeProxy, address(l1Agent)); + // Mark the following chainId as built + uint256 chainId = 5555; + legacyAgent.setBuilder(builder, chainId); + legacyAgent.setAddressManager(chainId, addressManager); + legacyDeposit.setBuildBlock(builder, block.number); + vm.prank(builder); deployment = _runL1BuildAgent( - 5555, + chainId, + addressManager, IL1BuildAgent.BuildConfig({ - legacyAddressManager: addressManager, finalSystemOwner: finalOwner, l2OutputOracleProposer: proposer, l2OutputOracleChallenger: challenger, @@ -148,6 +148,11 @@ contract L1BuildAgentUpgradeTest is L1BuildAgentTestCommon { l2OutputOracleStartingTimestamp: block.timestamp }) ); + + // Clear the cross domain messenger address to prevent early deposits + vm.prank(finalOwner); + string memory contractName = "OVM_L1CrossDomainMessenger"; + deployment.proxyAdmin.setImplementationName(address(0), contractName); } function _deployLegacies() internal returns (address, address, address, address) { @@ -236,7 +241,8 @@ contract L1BuildAgentUpgradeTest is L1BuildAgentTestCommon { function test_ProxyAdmin_implementationNames() external view { assert( - keccak256(abi.encode(deployment.proxyAdmin.implementationName(address(deployment.l1Messenger)))) + // keccak256(abi.encode(deployment.proxyAdmin.implementationName(address(deployment.l1Messenger)))) + keccak256(abi.encode(deployment.proxyAdmin.implementationName(address(0)))) == keccak256(abi.encode("OVM_L1CrossDomainMessenger")) ); } diff --git a/packages/contracts-bedrock/test/setup/oasys/MockLegacyL1BuildAgent.sol b/packages/contracts-bedrock/test/setup/oasys/MockLegacyL1BuildAgent.sol new file mode 100644 index 000000000..55a7c176a --- /dev/null +++ b/packages/contracts-bedrock/test/setup/oasys/MockLegacyL1BuildAgent.sol @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +contract MockLegacyL1BuildAgent { + mapping(uint256 => address) public addressManagers; + address[] private _builders; + uint256[] private _chainIds; + function setAddressManager(uint256 _chainId, address _addressManager) public { + addressManagers[_chainId] = _addressManager; + } + function getAddressManager(uint256 _chainId) public view returns (address) { + return addressManagers[_chainId]; + } + function setBuilder(address _builder, uint256 _chainId) public { + _builders.push(_builder); + _chainIds.push(_chainId); + } + function getBuilts(uint256 cursor, uint256 howMany) + external + view + returns ( + address[] memory, + uint256[] memory, + uint256 + ) + { + uint256 length = _builders.length; + if (cursor + howMany >= length) { + howMany = length - cursor; + } + address[] memory builders = new address[](howMany); + uint256[] memory chainIds = new uint256[](howMany); + for (uint256 i = 0; i < howMany; i++) { + builders[i] = _builders[cursor + i]; + chainIds[i] = _chainIds[cursor + i]; + } + return (builders, chainIds, cursor + howMany); + } +} \ No newline at end of file diff --git a/packages/contracts-bedrock/test/setup/oasys/MockLegacyL1BuildDeposit.sol b/packages/contracts-bedrock/test/setup/oasys/MockLegacyL1BuildDeposit.sol new file mode 100644 index 000000000..447116566 --- /dev/null +++ b/packages/contracts-bedrock/test/setup/oasys/MockLegacyL1BuildDeposit.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +contract MockLegacyL1BuildDeposit { + mapping(address => uint256) public _buildBlock; + function setBuildBlock(address _builder, uint256 blockNumber) public { + _buildBlock[_builder] = blockNumber; + } + function getBuildBlock(address _builder) public view returns (uint256) { + return _buildBlock[_builder]; + } +} \ No newline at end of file diff --git a/packages/contracts-bedrock/test/setup/oasys/SetupL1BuildAgent.sol b/packages/contracts-bedrock/test/setup/oasys/SetupL1BuildAgent.sol index e3a798233..f670e14ac 100644 --- a/packages/contracts-bedrock/test/setup/oasys/SetupL1BuildAgent.sol +++ b/packages/contracts-bedrock/test/setup/oasys/SetupL1BuildAgent.sol @@ -33,6 +33,8 @@ import { Path } from "scripts/oasys/L1/build/_path.sol"; import { Deploy } from "scripts/oasys/L1/build/Deploy.s.sol"; import { BuiltInContracts } from "test/setup/oasys/BuiltInContracts.sol"; +import { MockLegacyL1BuildAgent } from "test/setup/oasys/MockLegacyL1BuildAgent.sol"; +import { MockLegacyL1BuildDeposit } from "test/setup/oasys/MockLegacyL1BuildDeposit.sol"; contract SetupL1BuildAgent is Test { using stdJson for string; @@ -40,6 +42,7 @@ contract SetupL1BuildAgent is Test { struct Deployment { // Build config uint256 chainId; + AddressManager addressManager; IL1BuildAgent.BuildConfig buildCfg; // Deployed proxies OasysPortal portal; @@ -51,7 +54,6 @@ contract SetupL1BuildAgent is Test { ProtocolVersions protocolVersions; // Deployed implementations ProxyAdmin proxyAdmin; - AddressManager addressManager; OasysPortal portalImpl; OasysL2OutputOracle l2OracleImpl; SystemConfig systemConfigImpl; @@ -88,6 +90,10 @@ contract SetupL1BuildAgent is Test { /// @dev Default deployment L2 Deployment deployment; + /// Legacy contracts + MockLegacyL1BuildAgent legacyAgent; + MockLegacyL1BuildDeposit legacyDeposit; + function setUp() public virtual { _addBalanceToTestWallets(); @@ -119,10 +125,13 @@ contract SetupL1BuildAgent is Test { internal returns (L1BuildAgent, L1BuildDeposit, OasysL2OutputOracleVerifier) { + legacyAgent = new MockLegacyL1BuildAgent(); + legacyDeposit = new MockLegacyL1BuildDeposit(); + vm.setEnv("SALT", salt); vm.setEnv("PERMISSIONED_FACTORY", vm.toString(address(factory))); - vm.setEnv("LEGACY_AGENT", vm.toString(address(0))); - vm.setEnv("LEGACY_DEPOSIT", vm.toString(address(0))); + vm.setEnv("LEGACY_AGENT", vm.toString(address(legacyAgent))); + vm.setEnv("LEGACY_DEPOSIT", vm.toString(address(legacyDeposit))); string memory jsonPath = string.concat(Path.deployOutDir(), "/test.json"); @@ -149,6 +158,7 @@ contract SetupL1BuildAgent is Test { /// @dev Run `L1BuildAgent.build()` method. function _runL1BuildAgent( uint256 chainId, + address addressManager, IL1BuildAgent.BuildConfig memory cfg ) internal @@ -159,6 +169,7 @@ contract SetupL1BuildAgent is Test { return Deployment({ // Build config chainId: chainId, + addressManager: AddressManager(addressManager), buildCfg: cfg, // Deployed proxies portal: OasysPortal(payable(results.oasysPortal)), @@ -170,7 +181,6 @@ contract SetupL1BuildAgent is Test { protocolVersions: ProtocolVersions(results.protocolVersions), // Deployed implementations proxyAdmin: ProxyAdmin(results.proxyAdmin), - addressManager: AddressManager(cfg.legacyAddressManager), portalImpl: OasysPortal(payable(impls[0])), l2OracleImpl: OasysL2OutputOracle(impls[1]), systemConfigImpl: SystemConfig(impls[2]), From 5ebc4574504ef40a2a8b680b4fcd3a2e55f983b6 Mon Sep 17 00:00:00 2001 From: tak Date: Sun, 31 Mar 2024 12:49:11 +0900 Subject: [PATCH 4/9] fmt --- .../scripts/oasys/L1/build/Build.s.sol | 6 +-- .../src/oasys/L1/build/L1BuildAgent.sol | 50 +++++++++++-------- .../src/oasys/L1/build/L1BuildDeposit.sol | 5 +- .../src/oasys/L1/build/PortalSender.sol | 5 +- .../L1/build/interfaces/IL1BuildAgent.sol | 9 +--- .../L1/build/interfaces/IL1BuildDeposit.sol | 2 +- .../build/interfaces/ILegacyL1BuildAgent.sol | 8 ++- .../OasysERC721BridgeLegacySpacer.sol | 6 ++- .../L1/messaging/OasysL1ERC721Bridge.sol | 6 ++- .../oasys/L1/rollup/OasysL2OutputOracle.sol | 5 +- .../L2/messaging/OasysL2ERC721Bridge.sol | 15 +++--- .../setup/oasys/MockLegacyL1BuildAgent.sol | 17 ++++--- .../setup/oasys/MockLegacyL1BuildDeposit.sol | 4 +- .../setup/oasys/MockLegacyL1ERC721Bridge.sol | 9 ++-- .../oasys/MockLegacyL1StandardBridge.sol | 12 +++-- .../test/setup/oasys/SetupL1BuildAgent.sol | 33 +++++++++--- 16 files changed, 116 insertions(+), 76 deletions(-) diff --git a/packages/contracts-bedrock/scripts/oasys/L1/build/Build.s.sol b/packages/contracts-bedrock/scripts/oasys/L1/build/Build.s.sol index a5a16365c..3c4f244c9 100644 --- a/packages/contracts-bedrock/scripts/oasys/L1/build/Build.s.sol +++ b/packages/contracts-bedrock/scripts/oasys/L1/build/Build.s.sol @@ -348,11 +348,7 @@ contract Build is Script { // output opstack configuration files. string memory deployCfgJson = _deployConfigJson("DeployConfig"); string memory addressesJson = _addressesJson( - "deployed", - results.proxyAdmin, - results.oasysL2OutputOracle, - address(0), - results.protocolVersions + "deployed", results.proxyAdmin, results.oasysL2OutputOracle, address(0), results.protocolVersions ); // output to the `./tmp/L1BuildAgent/Build/latest` directory diff --git a/packages/contracts-bedrock/src/oasys/L1/build/L1BuildAgent.sol b/packages/contracts-bedrock/src/oasys/L1/build/L1BuildAgent.sol index e0b7de3f6..9c22f5c68 100644 --- a/packages/contracts-bedrock/src/oasys/L1/build/L1BuildAgent.sol +++ b/packages/contracts-bedrock/src/oasys/L1/build/L1BuildAgent.sol @@ -103,7 +103,6 @@ contract L1BuildAgent is IL1BuildAgent, ISemver { L2OO_VERIFIER = _l2ooVerifier; } - /// @notice Pause the legacy L1CrossDomainMessenger /// This is used when upgrading the existing L2 /// Ref: step2 of `SystemDictator` @@ -124,26 +123,26 @@ contract L1BuildAgent is IL1BuildAgent, ISemver { // CanonicalTransactionChain. We do this by setting an address in the AddressManager // because the DTL already has a reference to the AddressManager and this way we don't also // need to give it a reference to the SystemDictator. - AddressManager(addressManager).setAddress( - "DTL_SHUTOFF_BLOCK", - address(uint160(block.number)) - ); + AddressManager(addressManager).setAddress("DTL_SHUTOFF_BLOCK", address(uint160(block.number))); } /// @notice Unpause the legacy L1CrossDomainMessenger /// @param addressManager The address manager of the existing L2 /// @param oldL1CrossDomainMessenger The address of the old L1CrossDomainMessenger - function unpauseLegacyL1CrossDomainMessenger(uint256 _chainId, address addressManager, address oldL1CrossDomainMessenger) public { + function unpauseLegacyL1CrossDomainMessenger( + uint256 _chainId, + address addressManager, + address oldL1CrossDomainMessenger + ) + public + { require(getBuilderGlobally(_chainId) == msg.sender, "L1BuildAgent: inconsistent builder"); require(messengerPauseds[_chainId], "L1BuildAgent: not paused"); messengerPauseds[_chainId] = false; // Reset the L1CrossDomainMessenger to the old implementation. - AddressManager(addressManager).setAddress( - "OVM_L1CrossDomainMessenger", - oldL1CrossDomainMessenger - ); + AddressManager(addressManager).setAddress("OVM_L1CrossDomainMessenger", oldL1CrossDomainMessenger); // Unset the DTL shutoff block which will allow the DTL to sync again. AddressManager(addressManager).setAddress("DTL_SHUTOFF_BLOCK", address(0)); @@ -256,7 +255,7 @@ contract L1BuildAgent is IL1BuildAgent, ISemver { /// @notice Check if the L2 is upgrading the existing L2 /// @param _chainId The chainId of Verse - function isUpgradingExistingL2(uint256 _chainId) public view returns(bool, address) { + function isUpgradingExistingL2(uint256 _chainId) public view returns (bool, address) { address addressManager = LEGACY_L1_BUILD_AGENT.getAddressManager(_chainId); return (addressManager != address(0), addressManager); } @@ -313,7 +312,8 @@ contract L1BuildAgent is IL1BuildAgent, ISemver { builtLists[_chainId].oasysPortal = _deployProxy(address(proxyAdmin)); builtLists[_chainId].oasysL2OutputOracle = _deployProxy(address(proxyAdmin)); builtLists[_chainId].systemConfig = _deployProxy(address(proxyAdmin)); - builtLists[_chainId].l1CrossDomainMessenger = _deployL1CrossDomainMessengerProxy(address(proxyAdmin), addressManager); + builtLists[_chainId].l1CrossDomainMessenger = + _deployL1CrossDomainMessengerProxy(address(proxyAdmin), addressManager); builtLists[_chainId].l1StandardBridge = _deployL1StandardBridgeProxy(address(proxyAdmin), addressManager); builtLists[_chainId].l1ERC721Bridge = _deployL1ERC721BridgeProxy(address(proxyAdmin), addressManager); builtLists[_chainId].protocolVersions = _deployProxy(address(proxyAdmin)); @@ -329,7 +329,13 @@ contract L1BuildAgent is IL1BuildAgent, ISemver { } /// @notice Deploy the L1CrossDomainMessengerProxy using a ResolvedDelegateProxy - function _deployL1CrossDomainMessengerProxy(address proxyAdmin, address addressManager) internal returns (address addr) { + function _deployL1CrossDomainMessengerProxy( + address proxyAdmin, + address addressManager + ) + internal + returns (address addr) + { if (addressManager != address(0)) { // upgrading existing L2 // Don't deply proxy, as the existing L2 already has the proxy(RelolvedDelegateProxy) @@ -382,7 +388,7 @@ contract L1BuildAgent is IL1BuildAgent, ISemver { _l2Oracle: builtLists[_chainId].oasysL2OutputOracle, _guardian: _cfg.finalSystemOwner, _systemConfig: builtLists[_chainId].systemConfig - }) + }) ); address _challenger = _cfg.l2OutputOracleChallenger; @@ -473,7 +479,9 @@ contract L1BuildAgent is IL1BuildAgent, ISemver { ProxyAdmin proxyAdmin, address impl, bool isUpgrading - ) internal { + ) + internal + { address l1StandardBridgeProxy = builtLists[_chainId].l1StandardBridge; if (isUpgrading) { @@ -484,9 +492,7 @@ contract L1BuildAgent is IL1BuildAgent, ISemver { // Transfer ETH from the L1StandardBridge to the OptimismPortal. PortalSender portalSender = new PortalSender(OptimismPortal(payable(builtLists[_chainId].oasysPortal))); proxyAdmin.upgradeAndCall( - payable(l1StandardBridgeProxy), - address(portalSender), - abi.encodeCall(PortalSender.donate, ()) + payable(l1StandardBridgeProxy), address(portalSender), abi.encodeCall(PortalSender.donate, ()) ); } @@ -499,7 +505,9 @@ contract L1BuildAgent is IL1BuildAgent, ISemver { ProxyAdmin proxyAdmin, address impl, bool isUpgrading - ) internal { + ) + internal + { address l1ERC721BridgeProxy = builtLists[_chainId].l1ERC721Bridge; if (isUpgrading) { @@ -526,7 +534,9 @@ contract L1BuildAgent is IL1BuildAgent, ISemver { // The proxy of Legacy L2 is ResolvedDelegateProxy, so need to set type and implementation name // Set proxy type to RESOLVED proxyAdmin.setProxyType(l1CrossDomainMessengerProxy, ProxyAdmin.ProxyType.RESOLVED); - require(uint256(proxyAdmin.proxyType(l1CrossDomainMessengerProxy)) == uint256(ProxyAdmin.ProxyType.RESOLVED)); + require( + uint256(proxyAdmin.proxyType(l1CrossDomainMessengerProxy)) == uint256(ProxyAdmin.ProxyType.RESOLVED) + ); // Set the implementation name to OVM_L1CrossDomainMessenger string memory contractName = "OVM_L1CrossDomainMessenger"; proxyAdmin.setImplementationName(l1CrossDomainMessengerProxy, contractName); diff --git a/packages/contracts-bedrock/src/oasys/L1/build/L1BuildDeposit.sol b/packages/contracts-bedrock/src/oasys/L1/build/L1BuildDeposit.sol index c4641dfd6..d3c5fe79f 100644 --- a/packages/contracts-bedrock/src/oasys/L1/build/L1BuildDeposit.sol +++ b/packages/contracts-bedrock/src/oasys/L1/build/L1BuildDeposit.sol @@ -35,12 +35,11 @@ contract L1BuildDeposit is ISemver, LegacyL1BuildDeposit { return getDepositTotal(_builder) + legacyL1BuildDeposit.getDepositTotal(_builder); } - /** * Returns the whether the address is the builder globally. * @param _builder Address of the Verse-Builder. */ - function isBuilderGlobally(address _builder) public view returns(bool) { + function isBuilderGlobally(address _builder) public view returns (bool) { bool builtLegacy; if (legacyL1BuildDeposit != IL1BuildDeposit(address(0))) { builtLegacy = legacyL1BuildDeposit.getBuildBlock(_builder) > 0; @@ -52,7 +51,7 @@ contract L1BuildDeposit is ISemver, LegacyL1BuildDeposit { * Returns the whether the address is the internal builder. * @param _builder Address of the Verse-Builder. */ - function isBuilderInternally(address _builder) public view returns(bool) { + function isBuilderInternally(address _builder) public view returns (bool) { return getBuildBlock(_builder) > 0; } } diff --git a/packages/contracts-bedrock/src/oasys/L1/build/PortalSender.sol b/packages/contracts-bedrock/src/oasys/L1/build/PortalSender.sol index 2366edf06..110c34b86 100644 --- a/packages/contracts-bedrock/src/oasys/L1/build/PortalSender.sol +++ b/packages/contracts-bedrock/src/oasys/L1/build/PortalSender.sol @@ -8,7 +8,8 @@ import { OptimismPortal } from "src/L1/OptimismPortal.sol"; * @notice The PortalSender is a simple intermediate contract that will transfer the balance of the * L1StandardBridge to the OptimismPortal during the Bedrock migration. */ -// Copied from https://github.com/oasysgames/oasys-opstack/blob/cd7c58349542f9f1ce9fd42c9054aeed1325e02c/packages/contracts-bedrock/contracts/deployment/PortalSender.sol +// Copied from +// https://github.com/oasysgames/oasys-opstack/blob/cd7c58349542f9f1ce9fd42c9054aeed1325e02c/packages/contracts-bedrock/contracts/deployment/PortalSender.sol contract PortalSender { /** * @notice Address of the OptimismPortal contract. @@ -28,4 +29,4 @@ contract PortalSender { function donate() public { PORTAL.donateETH{ value: address(this).balance }(); } -} \ No newline at end of file +} diff --git a/packages/contracts-bedrock/src/oasys/L1/build/interfaces/IL1BuildAgent.sol b/packages/contracts-bedrock/src/oasys/L1/build/interfaces/IL1BuildAgent.sol index 6c559dafd..94e7facb6 100644 --- a/packages/contracts-bedrock/src/oasys/L1/build/interfaces/IL1BuildAgent.sol +++ b/packages/contracts-bedrock/src/oasys/L1/build/interfaces/IL1BuildAgent.sol @@ -61,12 +61,7 @@ interface IL1BuildAgent { } /// @notice Event emitted when the L1 contract set is deployed - event Deployed( - uint256 indexed chainId, - address finalSystemOwner, - BuiltAddressList results, - address[7] impls - ); + event Deployed(uint256 indexed chainId, address finalSystemOwner, BuiltAddressList results, address[7] impls); function builtLists(uint256 chainId) external @@ -81,7 +76,7 @@ interface IL1BuildAgent { function isUniqueChainId(uint256 chainId) external view returns (bool); - function isUpgradingExistingL2(uint256 _chainId) external returns(bool, address); + function isUpgradingExistingL2(uint256 _chainId) external returns (bool, address); function build( uint256 chainId, diff --git a/packages/contracts-bedrock/src/oasys/L1/build/interfaces/IL1BuildDeposit.sol b/packages/contracts-bedrock/src/oasys/L1/build/interfaces/IL1BuildDeposit.sol index bb0cdb367..2b391e8a7 100644 --- a/packages/contracts-bedrock/src/oasys/L1/build/interfaces/IL1BuildDeposit.sol +++ b/packages/contracts-bedrock/src/oasys/L1/build/interfaces/IL1BuildDeposit.sol @@ -14,5 +14,5 @@ interface IL1BuildDeposit { function getBuildBlock(address _builder) external view returns (uint256); - function isBuilderGlobally(address _builder) external view returns(bool); + function isBuilderGlobally(address _builder) external view returns (bool); } diff --git a/packages/contracts-bedrock/src/oasys/L1/build/interfaces/ILegacyL1BuildAgent.sol b/packages/contracts-bedrock/src/oasys/L1/build/interfaces/ILegacyL1BuildAgent.sol index bb1b62ee8..69646104a 100644 --- a/packages/contracts-bedrock/src/oasys/L1/build/interfaces/ILegacyL1BuildAgent.sol +++ b/packages/contracts-bedrock/src/oasys/L1/build/interfaces/ILegacyL1BuildAgent.sol @@ -3,5 +3,11 @@ pragma solidity 0.8.15; interface ILegacyL1BuildAgent { function getAddressManager(uint256 _chainId) external view returns (address); - function getBuilts(uint256 cursor, uint256 howMany) external view returns (address[] memory, uint256[] memory, uint256); + function getBuilts( + uint256 cursor, + uint256 howMany + ) + external + view + returns (address[] memory, uint256[] memory, uint256); } diff --git a/packages/contracts-bedrock/src/oasys/L1/messaging/OasysERC721BridgeLegacySpacer.sol b/packages/contracts-bedrock/src/oasys/L1/messaging/OasysERC721BridgeLegacySpacer.sol index 0ac9d4bb6..ee98eb4a7 100644 --- a/packages/contracts-bedrock/src/oasys/L1/messaging/OasysERC721BridgeLegacySpacer.sol +++ b/packages/contracts-bedrock/src/oasys/L1/messaging/OasysERC721BridgeLegacySpacer.sol @@ -5,8 +5,10 @@ import { L2OutputOracle } from "src/L1/L2OutputOracle.sol"; /// @title OasysERC721BridgeLegacySpacer /// @notice Defines the storage layout of Oasys Legacy ERC721Bridge. -/// Ref: https://github.com/oasysgames/oasys-optimism/blob/4d667a169296f37422ffaa4901e8d149e84abe5a/packages/contracts/contracts/oasys/L1/messaging/L1ERC721BridgeV2.sol -/// Ref: https://github.com/oasysgames/oasys-optimism/blob/4d667a169296f37422ffaa4901e8d149e84abe5a/packages/contracts/contracts/oasys/L2/messaging/L2ERC721Bridge.sol +/// Ref: +/// https://github.com/oasysgames/oasys-optimism/blob/4d667a169296f37422ffaa4901e8d149e84abe5a/packages/contracts/contracts/oasys/L1/messaging/L1ERC721BridgeV2.sol +/// Ref: +/// https://github.com/oasysgames/oasys-optimism/blob/4d667a169296f37422ffaa4901e8d149e84abe5a/packages/contracts/contracts/oasys/L2/messaging/L2ERC721Bridge.sol contract OasysERC721BridgeLegacySpacer { /// @custom:legacy /// @custom:spacer messenger diff --git a/packages/contracts-bedrock/src/oasys/L1/messaging/OasysL1ERC721Bridge.sol b/packages/contracts-bedrock/src/oasys/L1/messaging/OasysL1ERC721Bridge.sol index 093e09f03..cb4e3afbf 100644 --- a/packages/contracts-bedrock/src/oasys/L1/messaging/OasysL1ERC721Bridge.sol +++ b/packages/contracts-bedrock/src/oasys/L1/messaging/OasysL1ERC721Bridge.sol @@ -95,7 +95,8 @@ contract OasysL1ERC721Bridge is L1ERC721Bridge, ILegacyL1ERC721Bridge { { super.finalizeBridgeERC721(_localToken, _remoteToken, _from, _to, _tokenId, _extraData); - // Ref: https://github.com/oasysgames/oasys-optimism/blob/4d667a169296f37422ffaa4901e8d149e84abe5a/packages/contracts/contracts/oasys/L1/messaging/IL1ERC721Bridge.sol#L21-L28 + // Ref: + // https://github.com/oasysgames/oasys-optimism/blob/4d667a169296f37422ffaa4901e8d149e84abe5a/packages/contracts/contracts/oasys/L1/messaging/IL1ERC721Bridge.sol#L21-L28 // slither-disable-next-line reentrancy-events emit ERC721WithdrawalFinalized(_localToken, _remoteToken, _from, _to, _tokenId, _extraData); } @@ -116,7 +117,8 @@ contract OasysL1ERC721Bridge is L1ERC721Bridge, ILegacyL1ERC721Bridge { { super._initiateBridgeERC721(_localToken, _remoteToken, _from, _to, _tokenId, _minGasLimit, _extraData); - // Ref: https://github.com/oasysgames/oasys-optimism/blob/4d667a169296f37422ffaa4901e8d149e84abe5a/packages/contracts/contracts/oasys/L1/messaging/IL1ERC721Bridge.sol#L12-L19 + // Ref: + // https://github.com/oasysgames/oasys-optimism/blob/4d667a169296f37422ffaa4901e8d149e84abe5a/packages/contracts/contracts/oasys/L1/messaging/IL1ERC721Bridge.sol#L12-L19 // slither-disable-next-line reentrancy-events emit ERC721DepositInitiated(_localToken, _remoteToken, _from, _to, _tokenId, _extraData); } diff --git a/packages/contracts-bedrock/src/oasys/L1/rollup/OasysL2OutputOracle.sol b/packages/contracts-bedrock/src/oasys/L1/rollup/OasysL2OutputOracle.sol index d3b3ee8cf..e52f34389 100644 --- a/packages/contracts-bedrock/src/oasys/L1/rollup/OasysL2OutputOracle.sol +++ b/packages/contracts-bedrock/src/oasys/L1/rollup/OasysL2OutputOracle.sol @@ -62,10 +62,7 @@ contract OasysL2OutputOracle is IOasysL2OutputOracle, L2OutputOracle { _startingTimestamp <= block.timestamp, "L2OutputOracle: starting L2 timestamp must be less than current time" ); - require( - l2Outputs.length == 0, - "L2OutputOracle: cannot update starting block after outputs have been recorded" - ); + require(l2Outputs.length == 0, "L2OutputOracle: cannot update starting block after outputs have been recorded"); startingTimestamp = _startingTimestamp; startingBlockNumber = _startingBlockNumber; diff --git a/packages/contracts-bedrock/src/oasys/L2/messaging/OasysL2ERC721Bridge.sol b/packages/contracts-bedrock/src/oasys/L2/messaging/OasysL2ERC721Bridge.sol index 3ff6a7a46..d76d21837 100644 --- a/packages/contracts-bedrock/src/oasys/L2/messaging/OasysL2ERC721Bridge.sol +++ b/packages/contracts-bedrock/src/oasys/L2/messaging/OasysL2ERC721Bridge.sol @@ -22,14 +22,16 @@ contract OasysL2ERC721Bridge is L2ERC721Bridge, ILegacyL2ERC721Bridge { /// @custom:legacy /// @inheritdoc ILegacyL2ERC721Bridge - /// @dev Ref: https://github.com/oasysgames/oasys-optimism/blob/4d667a169296f37422ffaa4901e8d149e84abe5a/packages/contracts/contracts/oasys/L2/messaging/L2ERC721Bridge.sol#L30 + /// @dev Ref: + /// https://github.com/oasysgames/oasys-optimism/blob/4d667a169296f37422ffaa4901e8d149e84abe5a/packages/contracts/contracts/oasys/L2/messaging/L2ERC721Bridge.sol#L30 function l1ERC721Bridge() external view returns (address) { return OTHER_BRIDGE; } /// @custom:legacy /// @inheritdoc ILegacyL2ERC721Bridge - /// @dev Ref: https://github.com/oasysgames/oasys-optimism/blob/4d667a169296f37422ffaa4901e8d149e84abe5a/packages/contracts/contracts/oasys/L2/messaging/L2ERC721Bridge.sol#L53-L58 + /// @dev Ref: + /// https://github.com/oasysgames/oasys-optimism/blob/4d667a169296f37422ffaa4901e8d149e84abe5a/packages/contracts/contracts/oasys/L2/messaging/L2ERC721Bridge.sol#L53-L58 function withdraw(address _l2Token, uint256 _tokenId, uint32 _l1Gas, bytes calldata _data) external { // Copied from ERC721Bridge.bridgeERC721 // start ---------------------------- @@ -47,7 +49,8 @@ contract OasysL2ERC721Bridge is L2ERC721Bridge, ILegacyL2ERC721Bridge { /// @custom:legacy /// @inheritdoc ILegacyL2ERC721Bridge - /// @dev Ref: https://github.com/oasysgames/oasys-optimism/blob/4d667a169296f37422ffaa4901e8d149e84abe5a/packages/contracts/contracts/oasys/L2/messaging/L2ERC721Bridge.sol#L65-L71 + /// @dev Ref: + /// https://github.com/oasysgames/oasys-optimism/blob/4d667a169296f37422ffaa4901e8d149e84abe5a/packages/contracts/contracts/oasys/L2/messaging/L2ERC721Bridge.sol#L65-L71 function withdrawTo( address _l2Token, address _to, @@ -67,7 +70,8 @@ contract OasysL2ERC721Bridge is L2ERC721Bridge, ILegacyL2ERC721Bridge { /// @custom:legacy /// @inheritdoc ILegacyL2ERC721Bridge - /// @dev Ref: https://github.com/oasysgames/oasys-optimism/blob/4d667a169296f37422ffaa4901e8d149e84abe5a/packages/contracts/contracts/oasys/L2/messaging/L2ERC721Bridge.sol#L130-L137 + /// @dev Ref: + /// https://github.com/oasysgames/oasys-optimism/blob/4d667a169296f37422ffaa4901e8d149e84abe5a/packages/contracts/contracts/oasys/L2/messaging/L2ERC721Bridge.sol#L130-L137 function finalizeDeposit( address _l1Token, address _l2Token, @@ -146,12 +150,9 @@ contract OasysL2ERC721Bridge is L2ERC721Bridge, ILegacyL2ERC721Bridge { // Proceed with the original implementation if the local token is optimism mintable super._initiateBridgeERC721(_localToken, _remoteToken, _from, _to, _tokenId, _minGasLimit, _extraData); } else { - // Following implementation is for legacy L2StandardERC721 // Mostly copied from the original implementation - - require(_remoteToken != address(0), "L2ERC721Bridge: remote token cannot be address(0)"); // Check that the withdrawal is being initiated by the NFT owner diff --git a/packages/contracts-bedrock/test/setup/oasys/MockLegacyL1BuildAgent.sol b/packages/contracts-bedrock/test/setup/oasys/MockLegacyL1BuildAgent.sol index 55a7c176a..8d1101e51 100644 --- a/packages/contracts-bedrock/test/setup/oasys/MockLegacyL1BuildAgent.sol +++ b/packages/contracts-bedrock/test/setup/oasys/MockLegacyL1BuildAgent.sol @@ -5,24 +5,27 @@ contract MockLegacyL1BuildAgent { mapping(uint256 => address) public addressManagers; address[] private _builders; uint256[] private _chainIds; + function setAddressManager(uint256 _chainId, address _addressManager) public { addressManagers[_chainId] = _addressManager; } + function getAddressManager(uint256 _chainId) public view returns (address) { return addressManagers[_chainId]; } + function setBuilder(address _builder, uint256 _chainId) public { _builders.push(_builder); _chainIds.push(_chainId); } - function getBuilts(uint256 cursor, uint256 howMany) + + function getBuilts( + uint256 cursor, + uint256 howMany + ) external view - returns ( - address[] memory, - uint256[] memory, - uint256 - ) + returns (address[] memory, uint256[] memory, uint256) { uint256 length = _builders.length; if (cursor + howMany >= length) { @@ -36,4 +39,4 @@ contract MockLegacyL1BuildAgent { } return (builders, chainIds, cursor + howMany); } -} \ No newline at end of file +} diff --git a/packages/contracts-bedrock/test/setup/oasys/MockLegacyL1BuildDeposit.sol b/packages/contracts-bedrock/test/setup/oasys/MockLegacyL1BuildDeposit.sol index 447116566..3bfb4d069 100644 --- a/packages/contracts-bedrock/test/setup/oasys/MockLegacyL1BuildDeposit.sol +++ b/packages/contracts-bedrock/test/setup/oasys/MockLegacyL1BuildDeposit.sol @@ -3,10 +3,12 @@ pragma solidity 0.8.15; contract MockLegacyL1BuildDeposit { mapping(address => uint256) public _buildBlock; + function setBuildBlock(address _builder, uint256 blockNumber) public { _buildBlock[_builder] = blockNumber; } + function getBuildBlock(address _builder) public view returns (uint256) { return _buildBlock[_builder]; } -} \ No newline at end of file +} diff --git a/packages/contracts-bedrock/test/setup/oasys/MockLegacyL1ERC721Bridge.sol b/packages/contracts-bedrock/test/setup/oasys/MockLegacyL1ERC721Bridge.sol index 3052bf3d5..0926e7c8f 100644 --- a/packages/contracts-bedrock/test/setup/oasys/MockLegacyL1ERC721Bridge.sol +++ b/packages/contracts-bedrock/test/setup/oasys/MockLegacyL1ERC721Bridge.sol @@ -7,14 +7,17 @@ contract MockLegacyL1ERC721Bridge { address public messenger; address public l2ERC721Bridge; mapping(address => mapping(address => mapping(uint256 => bool))) public deposits; + function depositERC721( address _l1Token, address _l2Token, uint256 _tokenId, - uint32 /*_l2Gas*/, + uint32, /*_l2Gas*/ bytes calldata /*_data*/ - ) external { + ) + external + { deposits[_l1Token][_l2Token][_tokenId] = true; IERC721(_l1Token).transferFrom(msg.sender, address(this), _tokenId); } -} \ No newline at end of file +} diff --git a/packages/contracts-bedrock/test/setup/oasys/MockLegacyL1StandardBridge.sol b/packages/contracts-bedrock/test/setup/oasys/MockLegacyL1StandardBridge.sol index 59e0b7228..4f801c2fa 100644 --- a/packages/contracts-bedrock/test/setup/oasys/MockLegacyL1StandardBridge.sol +++ b/packages/contracts-bedrock/test/setup/oasys/MockLegacyL1StandardBridge.sol @@ -7,15 +7,19 @@ contract MockLegacyL1StandardBridge { address public messenger; address public l2TokenBridge; mapping(address => mapping(address => uint256)) public deposits; - function depositETH(uint32 /*_l2Gas*/, bytes calldata /*_data*/) external payable {} + + function depositETH(uint32, /*_l2Gas*/ bytes calldata /*_data*/ ) external payable { } + function depositERC20( address _l1Token, address _l2Token, uint256 _amount, - uint32 /*_l2Gas*/, + uint32, /*_l2Gas*/ bytes calldata /*_data*/ - ) external { + ) + external + { deposits[_l1Token][_l2Token] = deposits[_l1Token][_l2Token] + _amount; IERC20(_l1Token).transferFrom(msg.sender, address(this), _amount); } -} \ No newline at end of file +} diff --git a/packages/contracts-bedrock/test/setup/oasys/SetupL1BuildAgent.sol b/packages/contracts-bedrock/test/setup/oasys/SetupL1BuildAgent.sol index f670e14ac..2a2cc0f73 100644 --- a/packages/contracts-bedrock/test/setup/oasys/SetupL1BuildAgent.sol +++ b/packages/contracts-bedrock/test/setup/oasys/SetupL1BuildAgent.sol @@ -207,31 +207,50 @@ contract L1BuildAgentTestCommon is SetupL1BuildAgent { } function test_ProxyAdmin_getProxyAdmin_SystemConfig_succeeds() external { - assertEq(deployment.proxyAdmin.getProxyAdmin(payable(address(deployment.systemConfig))), address(deployment.proxyAdmin)); + assertEq( + deployment.proxyAdmin.getProxyAdmin(payable(address(deployment.systemConfig))), + address(deployment.proxyAdmin) + ); } function test_ProxyAdmin_getProxyAdmin_OasysPortal_succeeds() external { - assertEq(deployment.proxyAdmin.getProxyAdmin(payable(address(deployment.portal))), address(deployment.proxyAdmin)); + assertEq( + deployment.proxyAdmin.getProxyAdmin(payable(address(deployment.portal))), address(deployment.proxyAdmin) + ); } function test_ProxyAdmin_getProxyAdmin_OasysL2OutputOracle_succeeds() external { - assertEq(deployment.proxyAdmin.getProxyAdmin(payable(address(deployment.l2Oracle))), address(deployment.proxyAdmin)); + assertEq( + deployment.proxyAdmin.getProxyAdmin(payable(address(deployment.l2Oracle))), address(deployment.proxyAdmin) + ); } function test_ProxyAdmin_getProxyAdmin_L1CrossDomainMessenger_succeeds() external { - assertEq(deployment.proxyAdmin.getProxyAdmin(payable(address(deployment.l1Messenger))), address(deployment.proxyAdmin)); + assertEq( + deployment.proxyAdmin.getProxyAdmin(payable(address(deployment.l1Messenger))), + address(deployment.proxyAdmin) + ); } function test_ProxyAdmin_getProxyAdmin_L1StandardBridge_succeeds() external { - assertEq(deployment.proxyAdmin.getProxyAdmin(payable(address(deployment.l1ERC20Bridge))), address(deployment.proxyAdmin)); + assertEq( + deployment.proxyAdmin.getProxyAdmin(payable(address(deployment.l1ERC20Bridge))), + address(deployment.proxyAdmin) + ); } function test_ProxyAdmin_getProxyAdmin_L1ERC721Bridge_succeeds() external { - assertEq(deployment.proxyAdmin.getProxyAdmin(payable(address(deployment.l1ERC721Bridge))), address(deployment.proxyAdmin)); + assertEq( + deployment.proxyAdmin.getProxyAdmin(payable(address(deployment.l1ERC721Bridge))), + address(deployment.proxyAdmin) + ); } function test_ProxyAdmin_getProxyAdmin_ProtocolVersions_succeeds() external { - assertEq(deployment.proxyAdmin.getProxyAdmin(payable(address(deployment.protocolVersions))), address(deployment.proxyAdmin)); + assertEq( + deployment.proxyAdmin.getProxyAdmin(payable(address(deployment.protocolVersions))), + address(deployment.proxyAdmin) + ); } /** From 4f2f04d9cb8e8e7dec74b1a6e45142850039fe4c Mon Sep 17 00:00:00 2001 From: tak Date: Tue, 2 Apr 2024 22:23:37 +0900 Subject: [PATCH 5/9] set message relayer during building --- .../scripts/oasys/L1/build/Build.s.sol | 23 +++++++++++-------- .../src/oasys/L1/build/BuildOasysPortal.sol | 4 ++-- .../src/oasys/L1/build/L1BuildAgent.sol | 6 ++--- .../L1/build/interfaces/IBuildOasysPortal.sol | 3 ++- .../L1/build/interfaces/IL1BuildAgent.sol | 3 +++ .../src/oasys/L1/messaging/OasysPortal.sol | 6 +++++ .../contracts-bedrock/test/L1BuildAgent.t.sol | 3 +++ .../test/setup/oasys/SetupL1BuildAgent.sol | 3 ++- 8 files changed, 34 insertions(+), 17 deletions(-) diff --git a/packages/contracts-bedrock/scripts/oasys/L1/build/Build.s.sol b/packages/contracts-bedrock/scripts/oasys/L1/build/Build.s.sol index 3c4f244c9..f3cac9b33 100644 --- a/packages/contracts-bedrock/scripts/oasys/L1/build/Build.s.sol +++ b/packages/contracts-bedrock/scripts/oasys/L1/build/Build.s.sol @@ -80,6 +80,8 @@ contract Build is Script { address l2OutputOracleProposer; // L2OutputOracleChallenger is the address of the account that challenges L2 outputs. address l2OutputOracleChallenger; + // MessageRelayer is the address of message-relayer finalizer. + address messageRelayer; // FinalizationPeriodSeconds represents the number of seconds before an output is considered // finalized. This impacts the amount of time that withdrawals take to finalize and is // generally set to 1 week. @@ -219,15 +221,11 @@ contract Build is Script { address l2ooChallenger = vm.envAddress("L2OO_CHALLENGER"); address l2ooProposer = vm.envAddress("L2OO_PROPOSER"); address batchSender = vm.envAddress("BATCH_SENDER"); + address messageRelayer = vm.envAddress("MESSAGE_RELAYER"); uint256 l2ChainId = vm.envUint("L2_CHAIN_ID"); uint256 l1BlockTime = vm.envUint("L1_BLOCK_TIME"); uint256 l2BlockTime = vm.envUint("L2_BLOCK_TIME"); uint256 l2GasLimit = vm.envUint("L2_GAS_LIMIT"); - uint256 finalizationPeriodSeconds = vm.envUint("FINALIZATION_PERIOD_SECONDS"); - uint256 outputOracleSubmissionInterval = vm.envUint("OUTPUT_ORACLE_SUBMISSION_INTERVAL"); - uint256 outputOracleStartingBlockNumber = vm.envUint("OUTPUT_ORACLE_STARTING_BLOCK_NUMBER"); - uint256 outputOracleStartingTimestamp = vm.envUint("OUTPUT_ORACLE_STARTING_TIMESTAMP"); - uint256 l2ZeroFeeTime = vm.envOr("ENABLE_L2_ZERO_FEE", false) ? block.timestamp : 0; // construct a deployment configuration. deployCfg = DeployConfig({ @@ -248,14 +246,16 @@ contract Build is Script { p2pSequencerAddress: p2pSequencer, batchSenderAddress: batchSender, // ---- - l2OutputOracleSubmissionInterval: outputOracleSubmissionInterval, - l2OutputOracleStartingBlockNumber: outputOracleStartingBlockNumber, - l2OutputOracleStartingTimestamp: outputOracleStartingTimestamp, + l2OutputOracleSubmissionInterval: vm.envUint("OUTPUT_ORACLE_SUBMISSION_INTERVAL"), + l2OutputOracleStartingBlockNumber: vm.envUint("OUTPUT_ORACLE_STARTING_BLOCK_NUMBER"), + l2OutputOracleStartingTimestamp: vm.envUint("OUTPUT_ORACLE_STARTING_TIMESTAMP"), // ---- l2OutputOracleProposer: l2ooProposer, l2OutputOracleChallenger: l2ooChallenger, // ---- - finalizationPeriodSeconds: finalizationPeriodSeconds, + messageRelayer: messageRelayer, + + finalizationPeriodSeconds: vm.envUint("FINALIZATION_PERIOD_SECONDS"), // ---- proxyAdminOwner: finalSystemOwner, baseFeeVaultRecipient: finalSystemOwner, @@ -290,7 +290,7 @@ contract Build is Script { requiredProtocolVersion: bytes32(0), recommendedProtocolVersion: bytes32(0), // ---- - l2ZeroFeeTime: l2ZeroFeeTime, + l2ZeroFeeTime: vm.envOr("ENABLE_L2_ZERO_FEE", false) ? block.timestamp : 0, // set later. batchInboxAddress: address(0), l1StandardBridgeProxy: address(0), @@ -307,6 +307,7 @@ contract Build is Script { l2OutputOracleChallenger: deployCfg.l2OutputOracleChallenger, batchSenderAddress: deployCfg.batchSenderAddress, p2pSequencerAddress: deployCfg.p2pSequencerAddress, + messageRelayer: deployCfg.messageRelayer, l2BlockTime: deployCfg.l2BlockTime, l2GasLimit: uint64(deployCfg.l2GenesisBlockGasLimit), l2OutputOracleSubmissionInterval: deployCfg.l2OutputOracleSubmissionInterval, @@ -422,6 +423,8 @@ contract Build is Script { json.serialize("l2OutputOracleProposer", deployCfg.l2OutputOracleProposer); json.serialize("l2OutputOracleChallenger", deployCfg.l2OutputOracleChallenger); + json.serialize("messageRelayer", deployCfg.messageRelayer); + json.serialize("finalizationPeriodSeconds", deployCfg.finalizationPeriodSeconds); json.serialize("proxyAdminOwner", deployCfg.proxyAdminOwner); diff --git a/packages/contracts-bedrock/src/oasys/L1/build/BuildOasysPortal.sol b/packages/contracts-bedrock/src/oasys/L1/build/BuildOasysPortal.sol index d4de13fe5..8e539bf4c 100644 --- a/packages/contracts-bedrock/src/oasys/L1/build/BuildOasysPortal.sol +++ b/packages/contracts-bedrock/src/oasys/L1/build/BuildOasysPortal.sol @@ -34,7 +34,7 @@ contract BuildOasysPortal is IBuildOasysPortal, ISemver { } /// @inheritdoc IBuildOasysPortal - function initializeData(bool _paused) external pure returns (bytes memory) { - return abi.encodeCall(OasysPortal.initialize, (_paused)); + function initializeData(bool _paused, address relayer) external pure returns (bytes memory) { + return abi.encodeCall(OasysPortal.initializeWithRelayer, (_paused, relayer)); } } diff --git a/packages/contracts-bedrock/src/oasys/L1/build/L1BuildAgent.sol b/packages/contracts-bedrock/src/oasys/L1/build/L1BuildAgent.sol index 9c22f5c68..5af271b51 100644 --- a/packages/contracts-bedrock/src/oasys/L1/build/L1BuildAgent.sol +++ b/packages/contracts-bedrock/src/oasys/L1/build/L1BuildAgent.sol @@ -219,7 +219,7 @@ contract L1BuildAgent is IL1BuildAgent, ISemver { _initializeSystemConfig(_chainId, _cfg, proxyAdmin, impls[2]); // OasysPortal should be initialized before L1StandardBridge, // because L1StandardBridge uses OasysPortal as a recipient of the ETH - _initializeOasysPortal(_chainId, proxyAdmin, impls[0]); + _initializeOasysPortal(_chainId, _cfg, proxyAdmin, impls[0]); _initializeL1StandardBridge(_chainId, proxyAdmin, impls[4], isUpgrading); _initializeL1ERC721Bridge(_chainId, proxyAdmin, impls[5], isUpgrading); _initializeL1CrossDomainMessenger(_chainId, proxyAdmin, impls[3], isUpgrading); @@ -575,13 +575,13 @@ contract L1BuildAgent is IL1BuildAgent, ISemver { } /// @notice Initialize the OasysPortal - function _initializeOasysPortal(uint256 _chainId, ProxyAdmin proxyAdmin, address impl) internal { + function _initializeOasysPortal(uint256 _chainId, BuildConfig calldata _cfg, ProxyAdmin proxyAdmin, address impl) internal { address oasysPortalProxy = builtLists[_chainId].oasysPortal; proxyAdmin.upgradeAndCall({ _proxy: payable(oasysPortalProxy), _implementation: impl, - _data: BUILD_OASYS_PORTAL.initializeData({ _paused: false }) + _data: BUILD_OASYS_PORTAL.initializeData({ _paused: false, relayer: _cfg.messageRelayer }) }); } diff --git a/packages/contracts-bedrock/src/oasys/L1/build/interfaces/IBuildOasysPortal.sol b/packages/contracts-bedrock/src/oasys/L1/build/interfaces/IBuildOasysPortal.sol index 6262a9f39..bf571fce8 100644 --- a/packages/contracts-bedrock/src/oasys/L1/build/interfaces/IBuildOasysPortal.sol +++ b/packages/contracts-bedrock/src/oasys/L1/build/interfaces/IBuildOasysPortal.sol @@ -17,5 +17,6 @@ interface IBuildOasysPortal { /// @notice Return data for initializer. /// @param _paused Sets the contract's pausability state. - function initializeData(bool _paused) external pure returns (bytes memory); + /// @param relayer Sets the messager relayer + function initializeData(bool _paused, address relayer) external pure returns (bytes memory); } diff --git a/packages/contracts-bedrock/src/oasys/L1/build/interfaces/IL1BuildAgent.sol b/packages/contracts-bedrock/src/oasys/L1/build/interfaces/IL1BuildAgent.sol index 94e7facb6..2138e287d 100644 --- a/packages/contracts-bedrock/src/oasys/L1/build/interfaces/IL1BuildAgent.sol +++ b/packages/contracts-bedrock/src/oasys/L1/build/interfaces/IL1BuildAgent.sol @@ -19,6 +19,9 @@ interface IL1BuildAgent { // This address sign the block for p2p sync. // Value: depending on each verse address p2pSequencerAddress; + /// The address of messager relayer. + // Value: depending on each verse + address messageRelayer; // the block time of l2 chain // Value: 2s uint256 l2BlockTime; diff --git a/packages/contracts-bedrock/src/oasys/L1/messaging/OasysPortal.sol b/packages/contracts-bedrock/src/oasys/L1/messaging/OasysPortal.sol index 7c43581af..114d23397 100644 --- a/packages/contracts-bedrock/src/oasys/L1/messaging/OasysPortal.sol +++ b/packages/contracts-bedrock/src/oasys/L1/messaging/OasysPortal.sol @@ -36,6 +36,12 @@ contract OasysPortal is OptimismPortal { super.initialize(_paused); } + /// @notice Initalize with setting messager relayer + function initializeWithRelayer(bool _paused, address _messageRelayer) public { + messageRelayer = _messageRelayer; + initialize(_paused); + } + /// @notice Set a new message relayer address. /// If the zero address is set, no immediate relay of withdrawal messages. function setMessageRelayer(address newRelayer) external { diff --git a/packages/contracts-bedrock/test/L1BuildAgent.t.sol b/packages/contracts-bedrock/test/L1BuildAgent.t.sol index f47be68aa..4c5c9281a 100644 --- a/packages/contracts-bedrock/test/L1BuildAgent.t.sol +++ b/packages/contracts-bedrock/test/L1BuildAgent.t.sol @@ -39,6 +39,7 @@ contract L1BuildAgentTest is L1BuildAgentTestCommon { l2OutputOracleChallenger: challenger, batchSenderAddress: batcher, p2pSequencerAddress: p2pSequencer, + messageRelayer: relayer, l2BlockTime: 5, l2GasLimit: 50_000_000, l2OutputOracleSubmissionInterval: 50, @@ -58,6 +59,7 @@ contract L1BuildAgentTest is L1BuildAgentTestCommon { console.log("challenger : %s", challenger); console.log("batcher : %s", batcher); console.log("p2pSequencer : %s", p2pSequencer); + console.log("relayer : %s", relayer); console.log("\nDependency contracts"); console.log("PermissionedContractFactory : %s", address(permissionedFactory)); @@ -140,6 +142,7 @@ contract L1BuildAgentUpgradeTest is L1BuildAgentTestCommon { l2OutputOracleChallenger: challenger, batchSenderAddress: batcher, p2pSequencerAddress: p2pSequencer, + messageRelayer: relayer, l2BlockTime: 5, l2GasLimit: 50_000_000, l2OutputOracleSubmissionInterval: 50, diff --git a/packages/contracts-bedrock/test/setup/oasys/SetupL1BuildAgent.sol b/packages/contracts-bedrock/test/setup/oasys/SetupL1BuildAgent.sol index 2a2cc0f73..2f8c366ce 100644 --- a/packages/contracts-bedrock/test/setup/oasys/SetupL1BuildAgent.sol +++ b/packages/contracts-bedrock/test/setup/oasys/SetupL1BuildAgent.sol @@ -78,6 +78,7 @@ contract SetupL1BuildAgent is Test { address challenger = makeAddr("challenger"); address batcher = makeAddr("batcher"); address p2pSequencer = makeAddr("p2pSequencer"); + address relayer = makeAddr("messageRelayer"); // Dependency contracts IPermissionedContractFactory permissionedFactory; @@ -287,7 +288,7 @@ contract L1BuildAgentTestCommon is SetupL1BuildAgent { } function test_OasysPortal_messageRelayer() external view { - assert(deployment.portal.messageRelayer() == address(0)); + assert(deployment.portal.messageRelayer() == address(deployment.buildCfg.messageRelayer)); assert(deployment.portalImpl.messageRelayer() == address(0)); } From 375110e5d6418d9401303414b0e996ceae6c1cd8 Mon Sep 17 00:00:00 2001 From: tak Date: Tue, 2 Apr 2024 22:27:08 +0900 Subject: [PATCH 6/9] fmt --- .../scripts/oasys/L1/build/Build.s.sol | 2 +- .../src/oasys/L1/build/L1BuildAgent.sol | 9 ++++- .../contracts-bedrock/test/L1BuildAgent.t.sol | 33 ++++++++++++++----- 3 files changed, 33 insertions(+), 11 deletions(-) diff --git a/packages/contracts-bedrock/scripts/oasys/L1/build/Build.s.sol b/packages/contracts-bedrock/scripts/oasys/L1/build/Build.s.sol index f3cac9b33..9ffeb5af0 100644 --- a/packages/contracts-bedrock/scripts/oasys/L1/build/Build.s.sol +++ b/packages/contracts-bedrock/scripts/oasys/L1/build/Build.s.sol @@ -254,7 +254,7 @@ contract Build is Script { l2OutputOracleChallenger: l2ooChallenger, // ---- messageRelayer: messageRelayer, - + // ---- finalizationPeriodSeconds: vm.envUint("FINALIZATION_PERIOD_SECONDS"), // ---- proxyAdminOwner: finalSystemOwner, diff --git a/packages/contracts-bedrock/src/oasys/L1/build/L1BuildAgent.sol b/packages/contracts-bedrock/src/oasys/L1/build/L1BuildAgent.sol index 5af271b51..6b5f7a038 100644 --- a/packages/contracts-bedrock/src/oasys/L1/build/L1BuildAgent.sol +++ b/packages/contracts-bedrock/src/oasys/L1/build/L1BuildAgent.sol @@ -575,7 +575,14 @@ contract L1BuildAgent is IL1BuildAgent, ISemver { } /// @notice Initialize the OasysPortal - function _initializeOasysPortal(uint256 _chainId, BuildConfig calldata _cfg, ProxyAdmin proxyAdmin, address impl) internal { + function _initializeOasysPortal( + uint256 _chainId, + BuildConfig calldata _cfg, + ProxyAdmin proxyAdmin, + address impl + ) + internal + { address oasysPortalProxy = builtLists[_chainId].oasysPortal; proxyAdmin.upgradeAndCall({ diff --git a/packages/contracts-bedrock/test/L1BuildAgent.t.sol b/packages/contracts-bedrock/test/L1BuildAgent.t.sol index 4c5c9281a..ed10159b8 100644 --- a/packages/contracts-bedrock/test/L1BuildAgent.t.sol +++ b/packages/contracts-bedrock/test/L1BuildAgent.t.sol @@ -162,7 +162,8 @@ contract L1BuildAgentUpgradeTest is L1BuildAgentTestCommon { // Deploy address manager AddressManager addressManager = new AddressManager(); // Deploy proxies - ResolvedDelegateProxy l1CrossDomainMessengerProxy = new ResolvedDelegateProxy(addressManager, "OVM_L1CrossDomainMessenger"); + ResolvedDelegateProxy l1CrossDomainMessengerProxy = + new ResolvedDelegateProxy(addressManager, "OVM_L1CrossDomainMessenger"); L1ChugSplashProxy l1StandardBridgeProxy = new L1ChugSplashProxy(address(this)); L1ChugSplashProxy l1ERC721BridgeProxy = new L1ChugSplashProxy(address(this)); // Deploy implementations @@ -174,13 +175,18 @@ contract L1BuildAgentUpgradeTest is L1BuildAgentTestCommon { addressManager.setAddress("Proxy__OVM_L1StandardBridge", address(l1StandardBridgeProxy)); addressManager.setAddress("Proxy__OVM_L1ERC721Bridge", address(l1ERC721BridgeProxy)); addressManager.setAddress("OVM_L1CrossDomainMessenger", address(l1CrossDomainMessenger)); - addressManager.setAddress("OVM_CanonicalTransactionChain", address(l1CrossDomainMessenger)); // dummy, expected to be set zero value after build + addressManager.setAddress("OVM_CanonicalTransactionChain", address(l1CrossDomainMessenger)); // dummy, expected + // to be set zero value after build // Transfer ownership of the address manager to the builder addressManager.transferOwnership(builder); // Set implementations to proxies - l1StandardBridgeProxy.setStorage(Constants.PROXY_IMPLEMENTATION_ADDRESS, bytes32(uint256(uint160(address(legacyStandardBridge))))); + l1StandardBridgeProxy.setStorage( + Constants.PROXY_IMPLEMENTATION_ADDRESS, bytes32(uint256(uint160(address(legacyStandardBridge)))) + ); l1StandardBridgeProxy.setOwner(builder); - l1ERC721BridgeProxy.setStorage(Constants.PROXY_IMPLEMENTATION_ADDRESS, bytes32(uint256(uint160(address(legacyERC721Bridge))))); + l1ERC721BridgeProxy.setStorage( + Constants.PROXY_IMPLEMENTATION_ADDRESS, bytes32(uint256(uint160(address(legacyERC721Bridge)))) + ); l1ERC721BridgeProxy.setOwner(builder); return ( address(addressManager), @@ -200,20 +206,24 @@ contract L1BuildAgentUpgradeTest is L1BuildAgentTestCommon { function _depositTokensToBridge(address l1StandardBridgeProxy, address l1ERC721BridgeProxy) internal { // Deposit OAS depositedAmount = 1 ether; - MockLegacyL1StandardBridge(l1StandardBridgeProxy).depositETH{value: depositedAmount}(50000, hex""); + MockLegacyL1StandardBridge(l1StandardBridgeProxy).depositETH{ value: depositedAmount }(50000, hex""); // Deposit ERC20 deal(address(l1ERC20), address(alice), depositedAmount); vm.prank(alice); l1ERC20.approve(l1StandardBridgeProxy, depositedAmount); vm.prank(alice); - MockLegacyL1StandardBridge(l1StandardBridgeProxy).depositERC20(address(l1ERC20), address(l2ERC20), depositedAmount, 50000, hex""); + MockLegacyL1StandardBridge(l1StandardBridgeProxy).depositERC20( + address(l1ERC20), address(l2ERC20), depositedAmount, 50000, hex"" + ); // Deposit ERC721 depositedTokenId = 1243; l1ERC721.mint(alice, depositedTokenId); vm.prank(alice); l1ERC721.approve(l1ERC721BridgeProxy, depositedTokenId); vm.prank(alice); - MockLegacyL1ERC721Bridge(l1ERC721BridgeProxy).depositERC721(address(l1ERC721), address(l2ERC721), depositedTokenId, 50000, hex""); + MockLegacyL1ERC721Bridge(l1ERC721BridgeProxy).depositERC721( + address(l1ERC721), address(l2ERC721), depositedTokenId, 50000, hex"" + ); } function _transferOwnerships(address manager, address chugProxy1, address chugProxy2, address newOwner) internal { @@ -290,11 +300,16 @@ contract L1BuildAgentUpgradeTest is L1BuildAgentTestCommon { function test_ERC20_migration_succeeds() external { assertEq(l1ERC20.balanceOf(address(deployment.l1ERC20Bridge)), depositedAmount); - assertEq(L1StandardBridge(deployment.l1ERC20Bridge).deposits(address(l1ERC20), address(l2ERC20)), depositedAmount); + assertEq( + L1StandardBridge(deployment.l1ERC20Bridge).deposits(address(l1ERC20), address(l2ERC20)), depositedAmount + ); } function test_ERC721_migration_succeeds() external { assertEq(l1ERC721.ownerOf(depositedTokenId), address(deployment.l1ERC721Bridge)); - assertEq(L1ERC721Bridge(deployment.l1ERC721Bridge).deposits(address(l1ERC721), address(l2ERC721), depositedTokenId), true); + assertEq( + L1ERC721Bridge(deployment.l1ERC721Bridge).deposits(address(l1ERC721), address(l2ERC721), depositedTokenId), + true + ); } } From d846a868b7c156c97cc3b97b8cd9fa86c2036bb0 Mon Sep 17 00:00:00 2001 From: tak Date: Wed, 3 Apr 2024 10:53:32 +0700 Subject: [PATCH 7/9] respond feedback from ironbeer --- .../src/oasys/L1/build/L1BuildAgent.sol | 3 +++ .../src/oasys/L2/messaging/OasysL2ERC721Bridge.sol | 12 +++++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/packages/contracts-bedrock/src/oasys/L1/build/L1BuildAgent.sol b/packages/contracts-bedrock/src/oasys/L1/build/L1BuildAgent.sol index 6b5f7a038..8d1520e5f 100644 --- a/packages/contracts-bedrock/src/oasys/L1/build/L1BuildAgent.sol +++ b/packages/contracts-bedrock/src/oasys/L1/build/L1BuildAgent.sol @@ -256,6 +256,9 @@ contract L1BuildAgent is IL1BuildAgent, ISemver { /// @notice Check if the L2 is upgrading the existing L2 /// @param _chainId The chainId of Verse function isUpgradingExistingL2(uint256 _chainId) public view returns (bool, address) { + if (LEGACY_L1_BUILD_AGENT == ILegacyL1BuildAgent(address(0))) { + return (false, address(0)); + } address addressManager = LEGACY_L1_BUILD_AGENT.getAddressManager(_chainId); return (addressManager != address(0), addressManager); } diff --git a/packages/contracts-bedrock/src/oasys/L2/messaging/OasysL2ERC721Bridge.sol b/packages/contracts-bedrock/src/oasys/L2/messaging/OasysL2ERC721Bridge.sol index d76d21837..043ee6c9a 100644 --- a/packages/contracts-bedrock/src/oasys/L2/messaging/OasysL2ERC721Bridge.sol +++ b/packages/contracts-bedrock/src/oasys/L2/messaging/OasysL2ERC721Bridge.sol @@ -97,6 +97,7 @@ contract OasysL2ERC721Bridge is L2ERC721Bridge, ILegacyL2ERC721Bridge { ) public override + onlyOtherBridge { if (_isOptimismMintableToken(_localToken)) { // Proceed with the original implementation if the local token is optimism mintable @@ -146,6 +147,8 @@ contract OasysL2ERC721Bridge is L2ERC721Bridge, ILegacyL2ERC721Bridge { internal override { + address remoteToken; + if (_isOptimismMintableToken(_localToken)) { // Proceed with the original implementation if the local token is optimism mintable super._initiateBridgeERC721(_localToken, _remoteToken, _from, _to, _tokenId, _minGasLimit, _extraData); @@ -164,8 +167,11 @@ contract OasysL2ERC721Bridge is L2ERC721Bridge, ILegacyL2ERC721Bridge { // Construct calldata for l1ERC721Bridge.finalizeBridgeERC721(_to, _tokenId) // Legacy token references the remote token as `l1Token` // slither-disable-next-line reentrancy-events - address remoteToken = ILegacyL2StandardERC721(_localToken).l1Token(); - require(remoteToken == _remoteToken, "L2ERC721Bridge: remote token does not match given value"); + remoteToken = ILegacyL2StandardERC721(_localToken).l1Token(); + // Skip the following check because the legacy interfaces (withdraw, withdrawTo) do not specify the correct + // remote token, + // resulting in failures. + // require(remoteToken == _remoteToken, "L2ERC721Bridge: remote token does not match given value"); // When a withdrawal is initiated, we burn the withdrawer's NFT to prevent subsequent L2 // usage @@ -186,7 +192,7 @@ contract OasysL2ERC721Bridge is L2ERC721Bridge, ILegacyL2ERC721Bridge { // Emit Legacy event for backward compatibility // slither-disable-next-line reentrancy-events - emit WithdrawalInitiated(_remoteToken, _localToken, _from, _to, _tokenId, _extraData); + emit WithdrawalInitiated(remoteToken, _localToken, _from, _to, _tokenId, _extraData); } /// @notice Determine if the local token is an ILegacyL2StandardERC721. From ec0f57380719cd0b68e4270cf93590dd6e36dd42 Mon Sep 17 00:00:00 2001 From: tak Date: Wed, 3 Apr 2024 12:47:34 +0700 Subject: [PATCH 8/9] fix zero address emit of mintable case --- .../src/oasys/L2/messaging/OasysL2ERC721Bridge.sol | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/contracts-bedrock/src/oasys/L2/messaging/OasysL2ERC721Bridge.sol b/packages/contracts-bedrock/src/oasys/L2/messaging/OasysL2ERC721Bridge.sol index 043ee6c9a..2b0fa540d 100644 --- a/packages/contracts-bedrock/src/oasys/L2/messaging/OasysL2ERC721Bridge.sol +++ b/packages/contracts-bedrock/src/oasys/L2/messaging/OasysL2ERC721Bridge.sol @@ -147,11 +147,14 @@ contract OasysL2ERC721Bridge is L2ERC721Bridge, ILegacyL2ERC721Bridge { internal override { + // Keep remote token for the legacy address remoteToken; if (_isOptimismMintableToken(_localToken)) { + address remoteToken = _remoteToken; + // Proceed with the original implementation if the local token is optimism mintable - super._initiateBridgeERC721(_localToken, _remoteToken, _from, _to, _tokenId, _minGasLimit, _extraData); + super._initiateBridgeERC721(_localToken, remoteToken, _from, _to, _tokenId, _minGasLimit, _extraData); } else { // Following implementation is for legacy L2StandardERC721 // Mostly copied from the original implementation From 2383cc1e2c82506731f4ae7313dde9bb14f9d88e Mon Sep 17 00:00:00 2001 From: tak Date: Wed, 3 Apr 2024 17:03:40 +0700 Subject: [PATCH 9/9] fix local variable shardowing --- .../src/oasys/L2/messaging/OasysL2ERC721Bridge.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/contracts-bedrock/src/oasys/L2/messaging/OasysL2ERC721Bridge.sol b/packages/contracts-bedrock/src/oasys/L2/messaging/OasysL2ERC721Bridge.sol index 2b0fa540d..091de3742 100644 --- a/packages/contracts-bedrock/src/oasys/L2/messaging/OasysL2ERC721Bridge.sol +++ b/packages/contracts-bedrock/src/oasys/L2/messaging/OasysL2ERC721Bridge.sol @@ -151,7 +151,7 @@ contract OasysL2ERC721Bridge is L2ERC721Bridge, ILegacyL2ERC721Bridge { address remoteToken; if (_isOptimismMintableToken(_localToken)) { - address remoteToken = _remoteToken; + remoteToken = _remoteToken; // Proceed with the original implementation if the local token is optimism mintable super._initiateBridgeERC721(_localToken, remoteToken, _from, _to, _tokenId, _minGasLimit, _extraData);