diff --git a/interfaces/L1/IETHLockbox.sol b/interfaces/L1/IETHLockbox.sol deleted file mode 100644 index c5ee53d44..000000000 --- a/interfaces/L1/IETHLockbox.sol +++ /dev/null @@ -1,40 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -import { ISemver } from "interfaces/universal/ISemver.sol"; -import { ISystemConfig } from "interfaces/L1/ISystemConfig.sol"; -import { IProxyAdminOwnedBase } from "interfaces/L1/IProxyAdminOwnedBase.sol"; -import { IOptimismPortal2 } from "interfaces/L1/IOptimismPortal2.sol"; -import { ISuperchainConfig } from "interfaces/L1/ISuperchainConfig.sol"; -import { IReinitializableBase } from "interfaces/universal/IReinitializableBase.sol"; - -interface IETHLockbox is IProxyAdminOwnedBase, ISemver, IReinitializableBase { - error ETHLockbox_Unauthorized(); - error ETHLockbox_Paused(); - error ETHLockbox_InsufficientBalance(); - error ETHLockbox_NoWithdrawalTransactions(); - error ETHLockbox_DifferentSuperchainConfig(); - - event Initialized(uint8 version); - event ETHLocked(IOptimismPortal2 indexed portal, uint256 amount); - event ETHUnlocked(IOptimismPortal2 indexed portal, uint256 amount); - event PortalAuthorized(IOptimismPortal2 indexed portal); - event LockboxAuthorized(IETHLockbox indexed lockbox); - event LiquidityMigrated(IETHLockbox indexed lockbox, uint256 amount); - event LiquidityReceived(IETHLockbox indexed lockbox, uint256 amount); - - function initialize(ISystemConfig _systemConfig, IOptimismPortal2[] calldata _portals) external; - function systemConfig() external view returns (ISystemConfig); - function paused() external view returns (bool); - function authorizedPortals(IOptimismPortal2) external view returns (bool); - function authorizedLockboxes(IETHLockbox) external view returns (bool); - function receiveLiquidity() external payable; - function lockETH() external payable; - function unlockETH(uint256 _value) external; - function authorizePortal(IOptimismPortal2 _portal) external; - function authorizeLockbox(IETHLockbox _lockbox) external; - function migrateLiquidity(IETHLockbox _lockbox) external; - function superchainConfig() external view returns (ISuperchainConfig); - - function __constructor__() external; -} diff --git a/interfaces/L1/IOptimismPortal2.sol b/interfaces/L1/IOptimismPortal2.sol index f25237103..6bebf02bd 100644 --- a/interfaces/L1/IOptimismPortal2.sol +++ b/interfaces/L1/IOptimismPortal2.sol @@ -9,7 +9,6 @@ import { ISystemConfig } from "interfaces/L1/ISystemConfig.sol"; import { ISuperchainConfig } from "interfaces/L1/ISuperchainConfig.sol"; import { IAnchorStateRegistry } from "interfaces/L1/proofs/IAnchorStateRegistry.sol"; import { IProxyAdminOwnedBase } from "interfaces/L1/IProxyAdminOwnedBase.sol"; -import { IETHLockbox } from "interfaces/L1/IETHLockbox.sol"; interface IOptimismPortal2 is IProxyAdminOwnedBase { error ContentLengthMismatch(); @@ -33,7 +32,6 @@ interface IOptimismPortal2 is IProxyAdminOwnedBase { error OptimismPortal_NoReentrancy(); error OptimismPortal_ProofNotOldEnough(); error OptimismPortal_Unproven(); - error OptimismPortal_InvalidLockboxState(); error OutOfGas(); error UnexpectedList(); error UnexpectedString(); @@ -47,7 +45,6 @@ interface IOptimismPortal2 is IProxyAdminOwnedBase { receive() external payable; function anchorStateRegistry() external view returns (IAnchorStateRegistry); - function ethLockbox() external view returns (IETHLockbox); function checkWithdrawal(bytes32 _withdrawalHash, address _proofSubmitter) external view; function depositTransaction( address _to, diff --git a/scripts/deploy/SystemDeploy.s.sol b/scripts/deploy/SystemDeploy.s.sol index e8e1a19cf..00843e755 100644 --- a/scripts/deploy/SystemDeploy.s.sol +++ b/scripts/deploy/SystemDeploy.s.sol @@ -12,7 +12,6 @@ import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; import { StateDiff } from "scripts/libraries/StateDiff.sol"; import { Types } from "scripts/libraries/Types.sol"; -import { IETHLockbox } from "interfaces/L1/IETHLockbox.sol"; import { IL1CrossDomainMessenger } from "interfaces/L1/IL1CrossDomainMessenger.sol"; import { IL1ERC721Bridge } from "interfaces/L1/IL1ERC721Bridge.sol"; import { IL1StandardBridge } from "interfaces/L1/IL1StandardBridge.sol"; @@ -213,7 +212,6 @@ contract SystemDeploy is Script { superchainConfigImpl: artifacts.mustGetAddress("SuperchainConfigImpl"), l1ERC721BridgeImpl: artifacts.mustGetAddress("L1ERC721BridgeImpl"), optimismPortalImpl: artifacts.mustGetAddress("OptimismPortalImpl"), - ethLockboxImpl: artifacts.mustGetAddress("ETHLockboxImpl"), systemConfigImpl: artifacts.mustGetAddress("SystemConfigImpl"), optimismMintableERC20FactoryImpl: artifacts.mustGetAddress("OptimismMintableERC20FactoryImpl"), l1CrossDomainMessengerImpl: artifacts.mustGetAddress("L1CrossDomainMessengerImpl"), @@ -453,7 +451,6 @@ contract SystemDeploy is Script { output_.l1StandardBridgeImpl = address(_deployL1StandardBridgeImpl()); output_.optimismMintableERC20FactoryImpl = address(_deployOptimismMintableERC20FactoryImpl()); output_.optimismPortalImpl = address(_deployOptimismPortalImpl(_input)); - output_.ethLockboxImpl = address(_deployETHLockboxImpl()); output_.delayedWETHImpl = address(_deployDelayedWETHImpl(_input)); output_.disputeGameFactoryImpl = address(_deployDisputeGameFactoryImpl()); output_.anchorStateRegistryImpl = address(_deployAnchorStateRegistryImpl(_input)); @@ -488,7 +485,6 @@ contract SystemDeploy is Script { output_.l1ERC721BridgeProxy = IL1ERC721Bridge(_deployProxy(_input, output_.opChainProxyAdmin, "L1ERC721Bridge")); output_.optimismPortalProxy = IOptimismPortal(payable(_deployProxy(_input, output_.opChainProxyAdmin, "OptimismPortal"))); - output_.ethLockboxProxy = IETHLockbox(_deployProxy(_input, output_.opChainProxyAdmin, "ETHLockbox")); output_.systemConfigProxy = ISystemConfig(_deployProxy(_input, output_.opChainProxyAdmin, "SystemConfig")); output_.optimismMintableERC20FactoryProxy = IOptimismMintableERC20Factory( _deployProxy(_input, output_.opChainProxyAdmin, "OptimismMintableERC20Factory") @@ -587,15 +583,6 @@ contract SystemDeploy is Script { abi.encodeCall(IOptimismPortal.initialize, (_output.systemConfigProxy, _output.anchorStateRegistryProxy)) ); - IOptimismPortal[] memory portals = new IOptimismPortal[](1); - portals[0] = _output.optimismPortalProxy; - _upgradeToAndCall( - _output.opChainProxyAdmin, - address(_output.ethLockboxProxy), - _impls.ethLockboxImpl, - abi.encodeCall(IETHLockbox.initialize, (_output.systemConfigProxy, portals)) - ); - _upgradeToAndCall( _output.opChainProxyAdmin, address(_output.optimismMintableERC20FactoryProxy), @@ -933,16 +920,6 @@ contract SystemDeploy is Script { ); } - function _deployETHLockboxImpl() internal returns (IETHLockbox) { - return IETHLockbox( - DeployUtils.createDeterministic({ - _name: "ETHLockbox", - _args: DeployUtils.encodeConstructor(abi.encodeCall(IETHLockbox.__constructor__, ())), - _salt: DeployUtils.DEFAULT_SALT - }) - ); - } - function _deployDelayedWETHImpl(ImplementationInput memory _input) internal returns (IDelayedWETH) { return IDelayedWETH( DeployUtils.createDeterministic({ @@ -1134,7 +1111,6 @@ contract SystemDeploy is Script { DeployUtils.assertValidContractAddress(_impls.superchainConfigImpl); DeployUtils.assertValidContractAddress(_impls.l1ERC721BridgeImpl); DeployUtils.assertValidContractAddress(_impls.optimismPortalImpl); - DeployUtils.assertValidContractAddress(_impls.ethLockboxImpl); DeployUtils.assertValidContractAddress(_impls.systemConfigImpl); DeployUtils.assertValidContractAddress(_impls.optimismMintableERC20FactoryImpl); DeployUtils.assertValidContractAddress(_impls.l1CrossDomainMessengerImpl); @@ -1164,7 +1140,6 @@ contract SystemDeploy is Script { artifacts.save("OptimismMintableERC20FactoryProxy", address(chain.optimismMintableERC20FactoryProxy)); artifacts.save("L1StandardBridgeProxy", address(chain.l1StandardBridgeProxy)); artifacts.save("L1CrossDomainMessengerProxy", address(chain.l1CrossDomainMessengerProxy)); - artifacts.save("ETHLockboxProxy", address(chain.ethLockboxProxy)); artifacts.save("DisputeGameFactoryProxy", address(chain.disputeGameFactoryProxy)); artifacts.save("DelayedWETHProxy", address(chain.delayedWETHProxy)); artifacts.save("AnchorStateRegistryProxy", address(chain.anchorStateRegistryProxy)); @@ -1187,7 +1162,6 @@ contract SystemDeploy is Script { artifacts.save("SuperchainConfigImpl", _impls.superchainConfigImpl); artifacts.save("L1ERC721BridgeImpl", _impls.l1ERC721BridgeImpl); artifacts.save("OptimismPortalImpl", _impls.optimismPortalImpl); - artifacts.save("ETHLockboxImpl", _impls.ethLockboxImpl); artifacts.save("SystemConfigImpl", _impls.systemConfigImpl); artifacts.save("OptimismMintableERC20FactoryImpl", _impls.optimismMintableERC20FactoryImpl); artifacts.save("L1CrossDomainMessengerImpl", _impls.l1CrossDomainMessengerImpl); diff --git a/scripts/libraries/Types.sol b/scripts/libraries/Types.sol index 9f26a5e75..941784952 100644 --- a/scripts/libraries/Types.sol +++ b/scripts/libraries/Types.sol @@ -16,7 +16,6 @@ import { IL1CrossDomainMessenger } from "interfaces/L1/IL1CrossDomainMessenger.s import { IL1ERC721Bridge } from "interfaces/L1/IL1ERC721Bridge.sol"; import { IL1StandardBridge } from "interfaces/L1/IL1StandardBridge.sol"; import { IOptimismMintableERC20Factory } from "interfaces/universal/IOptimismMintableERC20Factory.sol"; -import { IETHLockbox } from "interfaces/L1/IETHLockbox.sol"; import { IProtocolVersions } from "interfaces/L1/IProtocolVersions.sol"; import { Proposal } from "src/libraries/bridge/Types.sol"; @@ -52,7 +51,6 @@ library Types { IOptimismMintableERC20Factory optimismMintableERC20FactoryProxy; IL1StandardBridge l1StandardBridgeProxy; IL1CrossDomainMessenger l1CrossDomainMessengerProxy; - IETHLockbox ethLockboxProxy; IOptimismPortal2 optimismPortalProxy; IDisputeGameFactory disputeGameFactoryProxy; IAnchorStateRegistry anchorStateRegistryProxy; @@ -71,7 +69,6 @@ library Types { address superchainConfigImpl; address l1ERC721BridgeImpl; address optimismPortalImpl; - address ethLockboxImpl; address systemConfigImpl; address optimismMintableERC20FactoryImpl; address l1CrossDomainMessengerImpl; diff --git a/snapshots/abi/ETHLockbox.json b/snapshots/abi/ETHLockbox.json deleted file mode 100644 index 2f09a99da..000000000 --- a/snapshots/abi/ETHLockbox.json +++ /dev/null @@ -1,395 +0,0 @@ -[ - { - "inputs": [], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "inputs": [ - { - "internalType": "contract IETHLockbox", - "name": "_lockbox", - "type": "address" - } - ], - "name": "authorizeLockbox", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IOptimismPortal2", - "name": "_portal", - "type": "address" - } - ], - "name": "authorizePortal", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IETHLockbox", - "name": "", - "type": "address" - } - ], - "name": "authorizedLockboxes", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IOptimismPortal2", - "name": "", - "type": "address" - } - ], - "name": "authorizedPortals", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "initVersion", - "outputs": [ - { - "internalType": "uint8", - "name": "", - "type": "uint8" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract ISystemConfig", - "name": "_systemConfig", - "type": "address" - }, - { - "internalType": "contract IOptimismPortal2[]", - "name": "_portals", - "type": "address[]" - } - ], - "name": "initialize", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "lockETH", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IETHLockbox", - "name": "_lockbox", - "type": "address" - } - ], - "name": "migrateLiquidity", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "paused", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "proxyAdmin", - "outputs": [ - { - "internalType": "contract IProxyAdmin", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "proxyAdminOwner", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "receiveLiquidity", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [], - "name": "superchainConfig", - "outputs": [ - { - "internalType": "contract ISuperchainConfig", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "systemConfig", - "outputs": [ - { - "internalType": "contract ISystemConfig", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "_value", - "type": "uint256" - } - ], - "name": "unlockETH", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "version", - "outputs": [ - { - "internalType": "string", - "name": "", - "type": "string" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "contract IOptimismPortal2", - "name": "portal", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "ETHLocked", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "contract IOptimismPortal2", - "name": "portal", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "ETHUnlocked", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint8", - "name": "version", - "type": "uint8" - } - ], - "name": "Initialized", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "contract IETHLockbox", - "name": "lockbox", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "LiquidityMigrated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "contract IETHLockbox", - "name": "lockbox", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "LiquidityReceived", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "contract IETHLockbox", - "name": "lockbox", - "type": "address" - } - ], - "name": "LockboxAuthorized", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "contract IOptimismPortal2", - "name": "portal", - "type": "address" - } - ], - "name": "PortalAuthorized", - "type": "event" - }, - { - "inputs": [], - "name": "ETHLockbox_DifferentSuperchainConfig", - "type": "error" - }, - { - "inputs": [], - "name": "ETHLockbox_InsufficientBalance", - "type": "error" - }, - { - "inputs": [], - "name": "ETHLockbox_NoWithdrawalTransactions", - "type": "error" - }, - { - "inputs": [], - "name": "ETHLockbox_Paused", - "type": "error" - }, - { - "inputs": [], - "name": "ETHLockbox_Unauthorized", - "type": "error" - }, - { - "inputs": [], - "name": "ProxyAdminOwnedBase_NotProxyAdmin", - "type": "error" - }, - { - "inputs": [], - "name": "ProxyAdminOwnedBase_NotProxyAdminOrProxyAdminOwner", - "type": "error" - }, - { - "inputs": [], - "name": "ProxyAdminOwnedBase_NotProxyAdminOwner", - "type": "error" - }, - { - "inputs": [], - "name": "ProxyAdminOwnedBase_NotResolvedDelegateProxy", - "type": "error" - }, - { - "inputs": [], - "name": "ProxyAdminOwnedBase_NotSharedProxyAdminOwner", - "type": "error" - }, - { - "inputs": [], - "name": "ProxyAdminOwnedBase_ProxyAdminNotFound", - "type": "error" - }, - { - "inputs": [], - "name": "ReinitializableBase_ZeroInitVersion", - "type": "error" - } -] \ No newline at end of file diff --git a/snapshots/abi/OptimismPortal2.json b/snapshots/abi/OptimismPortal2.json index 9d19d18db..1d5385756 100644 --- a/snapshots/abi/OptimismPortal2.json +++ b/snapshots/abi/OptimismPortal2.json @@ -130,19 +130,6 @@ "stateMutability": "payable", "type": "function" }, - { - "inputs": [], - "name": "ethLockbox", - "outputs": [ - { - "internalType": "contract IETHLockbox", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, { "inputs": [ { @@ -794,11 +781,6 @@ "name": "OptimismPortal_InvalidDisputeGame", "type": "error" }, - { - "inputs": [], - "name": "OptimismPortal_InvalidLockboxState", - "type": "error" - }, { "inputs": [], "name": "OptimismPortal_InvalidMerkleProof", diff --git a/snapshots/semver-lock.json b/snapshots/semver-lock.json index f597a206b..6575846fd 100644 --- a/snapshots/semver-lock.json +++ b/snapshots/semver-lock.json @@ -1,8 +1,4 @@ { - "src/L1/ETHLockbox.sol:ETHLockbox": { - "initCodeHash": "0x65db3aa3c2e3221065752f66016fa02b66688a01cc5c3066533b27fe620619c8", - "sourceCodeHash": "0x519c4606618c1d5fa489dc99437c895171b16179cbb74f602011ffe0b5902788" - }, "src/L1/L1CrossDomainMessenger.sol:L1CrossDomainMessenger": { "initCodeHash": "0x3dc659aafb03bd357f92abfc6794af89ee0ddd5212364551637422bf8d0b00f9", "sourceCodeHash": "0xb1467f925aaf0a834bc98cdfaffe8832bdf57d57a0e22087179d1c3b4bee2c53" @@ -16,8 +12,8 @@ "sourceCodeHash": "0xd9f576a79e97bc541b3d7a2ee928f34223edaaecb074eeb9e6e2ecee857ce6a0" }, "src/L1/OptimismPortal2.sol:OptimismPortal2": { - "initCodeHash": "0xdd5d13b03c015d45d0cdda512795c4fe8bbd60557e3848fed53260ee887b2df4", - "sourceCodeHash": "0xa1281f5da03fa0431eabad33da23f51a8d835b0dd04554603fd59e11efe9fa51" + "initCodeHash": "0xe8145d0c3bd0e35f84086cddd75c73915332c42b40046d52188d822119915c3d", + "sourceCodeHash": "0x811596e7486cab9ceeeb61405b9ae510d93fcbbdfa11949201a367506c53194a" }, "src/L1/ProtocolVersions.sol:ProtocolVersions": { "initCodeHash": "0x8ff4bbb0adb8115a3d77e1b706038b0758fdb3a0b183108a794b3cb6200acd83", @@ -28,8 +24,8 @@ "sourceCodeHash": "0x79f0c771fc5f6d222d89b05addedc08ce40a7a42423fbd66f7cbb6cde3c2e74f" }, "src/L1/SystemConfig.sol:SystemConfig": { - "initCodeHash": "0x3e8e52d96398a6de91d8922769cc5d0bc7acb2a692689ceb70f1de816e8d6b14", - "sourceCodeHash": "0xf122a50487efe9bd5a620262ba20ef4adbca14eeec2af7fd32e6e16739001596" + "initCodeHash": "0xb91123c7a4c051f2c9af220a5981301499d43c7740892db4ea8abce183437a8c", + "sourceCodeHash": "0x780ff372493ba9010bc0d13100ac896f2bf75730a9b17d4bb63aaf694dc3c634" }, "src/L1/proofs/AggregateVerifier.sol:AggregateVerifier": { "initCodeHash": "0x6f3c53245830b9c72c8199695e93959d5ed3b0cb8352e5b50b3dfa28183ac787", diff --git a/snapshots/storageLayout/ETHLockbox.json b/snapshots/storageLayout/ETHLockbox.json deleted file mode 100644 index 2af24fd9c..000000000 --- a/snapshots/storageLayout/ETHLockbox.json +++ /dev/null @@ -1,37 +0,0 @@ -[ - { - "bytes": "1", - "label": "_initialized", - "offset": 0, - "slot": "0", - "type": "uint8" - }, - { - "bytes": "1", - "label": "_initializing", - "offset": 1, - "slot": "0", - "type": "bool" - }, - { - "bytes": "20", - "label": "systemConfig", - "offset": 2, - "slot": "0", - "type": "contract ISystemConfig" - }, - { - "bytes": "32", - "label": "authorizedPortals", - "offset": 0, - "slot": "1", - "type": "mapping(contract IOptimismPortal2 => bool)" - }, - { - "bytes": "32", - "label": "authorizedLockboxes", - "offset": 0, - "slot": "2", - "type": "mapping(contract IETHLockbox => bool)" - } -] \ No newline at end of file diff --git a/snapshots/storageLayout/OptimismPortal2.json b/snapshots/storageLayout/OptimismPortal2.json index 649ad99cb..12b99bf49 100644 --- a/snapshots/storageLayout/OptimismPortal2.json +++ b/snapshots/storageLayout/OptimismPortal2.json @@ -134,10 +134,10 @@ }, { "bytes": "20", - "label": "ethLockbox", + "label": "spacer_63_0_20", "offset": 0, "slot": "63", - "type": "contract IETHLockbox" + "type": "address" }, { "bytes": "1", diff --git a/src/L1/ETHLockbox.sol b/src/L1/ETHLockbox.sol deleted file mode 100644 index a4d124cdf..000000000 --- a/src/L1/ETHLockbox.sol +++ /dev/null @@ -1,232 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.15; - -// Contracts -import { Initializable } from "lib/openzeppelin-contracts/contracts/proxy/utils/Initializable.sol"; -import { ProxyAdminOwnedBase } from "src/universal/ProxyAdminOwnedBase.sol"; -import { ReinitializableBase } from "src/universal/ReinitializableBase.sol"; - -// Libraries -import { Constants } from "src/libraries/Constants.sol"; - -// Interfaces -import { ISemver } from "interfaces/universal/ISemver.sol"; -import { IOptimismPortal2 as IOptimismPortal } from "interfaces/L1/IOptimismPortal2.sol"; -import { ISuperchainConfig } from "interfaces/L1/ISuperchainConfig.sol"; -import { IETHLockbox } from "interfaces/L1/IETHLockbox.sol"; -import { ISystemConfig } from "interfaces/L1/ISystemConfig.sol"; - -/// @custom:proxied true -/// @title ETHLockbox -/// @notice Manages ETH liquidity locking and unlocking for authorized OptimismPortals, enabling unified ETH liquidity -/// management across chains in the superchain cluster. -contract ETHLockbox is ProxyAdminOwnedBase, Initializable, ReinitializableBase, ISemver { - /// @notice Thrown when the lockbox is paused. - error ETHLockbox_Paused(); - - /// @notice Thrown when the caller is not authorized. - error ETHLockbox_Unauthorized(); - - /// @notice Thrown when the value to unlock is greater than the balance of the lockbox. - error ETHLockbox_InsufficientBalance(); - - /// @notice Thrown when attempting to unlock ETH from the lockbox through a withdrawal transaction. - error ETHLockbox_NoWithdrawalTransactions(); - - /// @notice Thrown when any authorized portal has a different SuperchainConfig. - error ETHLockbox_DifferentSuperchainConfig(); - - /// @notice Emitted when ETH is locked in the lockbox by an authorized portal. - /// @param portal The address of the portal that locked the ETH. - /// @param amount The amount of ETH locked. - event ETHLocked(IOptimismPortal indexed portal, uint256 amount); - - /// @notice Emitted when ETH is unlocked from the lockbox by an authorized portal. - /// @param portal The address of the portal that unlocked the ETH. - /// @param amount The amount of ETH unlocked. - event ETHUnlocked(IOptimismPortal indexed portal, uint256 amount); - - /// @notice Emitted when a portal is authorized to lock and unlock ETH. - /// @param portal The address of the portal that was authorized. - event PortalAuthorized(IOptimismPortal indexed portal); - - /// @notice Emitted when an ETH lockbox is authorized to migrate its liquidity to the current ETH lockbox. - /// @param lockbox The address of the ETH lockbox that was authorized. - event LockboxAuthorized(IETHLockbox indexed lockbox); - - /// @notice Emitted when ETH liquidity is migrated from the current ETH lockbox to another. - /// @param lockbox The address of the ETH lockbox that was migrated. - event LiquidityMigrated(IETHLockbox indexed lockbox, uint256 amount); - - /// @notice Emitted when ETH liquidity is received during an authorized lockbox migration. - /// @param lockbox The address of the ETH lockbox that received the liquidity. - /// @param amount The amount of ETH received. - event LiquidityReceived(IETHLockbox indexed lockbox, uint256 amount); - - /// @notice The address of the SystemConfig contract. - ISystemConfig public systemConfig; - - /// @notice Mapping of authorized portals. - mapping(IOptimismPortal => bool) public authorizedPortals; - - /// @notice Mapping of authorized lockboxes. - mapping(IETHLockbox => bool) public authorizedLockboxes; - - /// @notice Semantic version. - /// @custom:semver 1.2.0 - function version() public view virtual returns (string memory) { - return "1.2.0"; - } - - /// @notice Constructs the ETHLockbox contract. - constructor() ReinitializableBase(1) { - _disableInitializers(); - } - - /// @notice Initializer. - /// @param _systemConfig The address of the SystemConfig contract. - /// @param _portals The addresses of the portals to authorize. - /// @dev Note: Multiple chains can share an ETHLockbox contract. In this case, all SystemConfig - /// contracts will point to the same pause identifier (the lockbox itself). Therefore, it - /// doesn't matter which SystemConfig is used here as long as it belongs to one of the - /// chains that share the lockbox. - function initialize( - ISystemConfig _systemConfig, - IOptimismPortal[] calldata _portals - ) - external - reinitializer(initVersion()) - { - // Initialization transactions must come from the ProxyAdmin or its owner. - _assertOnlyProxyAdminOrProxyAdminOwner(); - - // Now perform initialization logic. - systemConfig = _systemConfig; - for (uint256 i; i < _portals.length; i++) { - _authorizePortal(_portals[i]); - } - } - - /// @notice Getter for the current paused status. - function paused() public view returns (bool) { - return systemConfig.paused(); - } - - /// @notice Returns the SuperchainConfig contract. - /// @return ISuperchainConfig The SuperchainConfig contract. - function superchainConfig() public view returns (ISuperchainConfig) { - return systemConfig.superchainConfig(); - } - - /// @notice Authorizes a portal to lock and unlock ETH. - /// @param _portal The address of the portal to authorize. - function authorizePortal(IOptimismPortal _portal) external { - // Check that this transaction is coming from the ProxyAdmin owner. - _assertOnlyProxyAdminOwner(); - - // Authorize the portal. - _authorizePortal(_portal); - } - - /// @notice Receives the ETH liquidity migrated from an authorized lockbox. - function receiveLiquidity() external payable { - // Check that the sender is authorized to trigger this function. - IETHLockbox sender = IETHLockbox(payable(msg.sender)); - if (!authorizedLockboxes[sender]) revert ETHLockbox_Unauthorized(); - - // Emit the event. - emit LiquidityReceived(sender, msg.value); - } - - /// @notice Locks ETH in the lockbox. - /// Called by an authorized portal on a deposit to lock the ETH value. - function lockETH() external payable { - // Check that the sender is authorized to trigger this function. - IOptimismPortal sender = IOptimismPortal(payable(msg.sender)); - if (!authorizedPortals[sender]) revert ETHLockbox_Unauthorized(); - - // Emit the event. - emit ETHLocked(sender, msg.value); - } - - /// @notice Unlocks ETH from the lockbox. - /// Called by an authorized portal when finalizing a withdrawal that requires ETH. - /// Cannot be called if the lockbox is paused. - /// @param _value The amount of ETH to unlock. - function unlockETH(uint256 _value) external { - // Unlocks are blocked when paused, locks are not. - if (paused()) revert ETHLockbox_Paused(); - - // Check that the sender is authorized to trigger this function. - IOptimismPortal sender = IOptimismPortal(payable(msg.sender)); - if (!authorizedPortals[sender]) revert ETHLockbox_Unauthorized(); - - // Check that we have enough balance to process the unlock. - if (_value > address(this).balance) revert ETHLockbox_InsufficientBalance(); - - // Check that the sender is not executing a withdrawal transaction. - if (sender.l2Sender() != Constants.DEFAULT_L2_SENDER) { - revert ETHLockbox_NoWithdrawalTransactions(); - } - - // Using donateETH to avoid triggering a deposit. - sender.donateETH{ value: _value }(); - - // Emit the event. - emit ETHUnlocked(sender, _value); - } - - /// @notice Authorizes an ETH lockbox to migrate its liquidity to the current ETH lockbox. We - /// allow this function to be called more than once for the same lockbox. A lockbox - /// cannot be removed from the authorized list once added. - /// @param _lockbox The address of the ETH lockbox to authorize. - function authorizeLockbox(IETHLockbox _lockbox) external { - // Check that this transaction is coming from the ProxyAdmin owner. - _assertOnlyProxyAdminOwner(); - - // Check that the lockbox has the same proxy admin owner. - _assertSharedProxyAdminOwner(address(_lockbox)); - - // Authorize the lockbox. - authorizedLockboxes[_lockbox] = true; - - // Emit the event. - emit LockboxAuthorized(_lockbox); - } - - /// @notice Migrates liquidity from the current ETH lockbox to another. - /// @dev Must be called atomically with `OptimismPortal.migrateToSuperRoots()` in the same - /// transaction batch, or otherwise the OptimismPortal may not be able to unlock ETH - /// from the ETHLockbox on finalized withdrawals. - /// @param _lockbox The address of the ETH lockbox to migrate liquidity to. - function migrateLiquidity(IETHLockbox _lockbox) external { - // Check that this transaction is coming from the ProxyAdmin owner. - _assertOnlyProxyAdminOwner(); - - // Check that the lockbox has the same proxy admin owner. - _assertSharedProxyAdminOwner(address(_lockbox)); - - // Receive the liquidity. - uint256 balance = address(this).balance; - IETHLockbox(_lockbox).receiveLiquidity{ value: balance }(); - - // Emit the event. - emit LiquidityMigrated(_lockbox, balance); - } - - /// @notice Authorizes a portal to lock and unlock ETH. - /// @param _portal The address of the portal to authorize. - function _authorizePortal(IOptimismPortal _portal) internal { - // Check that the portal has the same proxy admin owner. - _assertSharedProxyAdminOwner(address(_portal)); - - // Check that the portal has the same superchain config. - if (_portal.superchainConfig() != superchainConfig()) revert ETHLockbox_DifferentSuperchainConfig(); - - // Authorize the portal. - authorizedPortals[_portal] = true; - - // Emit the event. - emit PortalAuthorized(_portal); - } -} diff --git a/src/L1/OptimismPortal2.sol b/src/L1/OptimismPortal2.sol index e0bc4bf39..531f5a3c4 100644 --- a/src/L1/OptimismPortal2.sol +++ b/src/L1/OptimismPortal2.sol @@ -25,7 +25,6 @@ import { IResourceMetering } from "interfaces/L1/IResourceMetering.sol"; import { IDisputeGameFactory } from "interfaces/L1/proofs/IDisputeGameFactory.sol"; import { IDisputeGame } from "interfaces/L1/proofs/IDisputeGame.sol"; import { IAnchorStateRegistry } from "interfaces/L1/proofs/IAnchorStateRegistry.sol"; -import { IETHLockbox } from "interfaces/L1/IETHLockbox.sol"; import { ISuperchainConfig } from "interfaces/L1/ISuperchainConfig.sol"; /// @custom:proxied true @@ -120,11 +119,10 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase /// @notice Address of the AnchorStateRegistry contract. IAnchorStateRegistry public anchorStateRegistry; - /// @notice Address of the ETHLockbox contract. NOTE that as of v4.1.0 it is not possible to - /// set this value in storage and it is only possible for this value to be set if the - /// chain was first upgraded to v4.0.0. Chains that skip v4.0.0 will not have any - /// ETHLockbox set here. - IETHLockbox public ethLockbox; + /// @custom:legacy + /// @custom:spacer + /// @notice Spacer taking up a legacy address slot. + address private spacer_63_0_20; /// @custom:legacy /// @custom:spacer superRootsActive @@ -204,9 +202,6 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase /// @notice Thrown when a withdrawal has not been proven. error OptimismPortal_Unproven(); - /// @notice Thrown when ETHLockbox is set/unset incorrectly depending on the feature flag. - error OptimismPortal_InvalidLockboxState(); - /// @notice Semantic version. /// @custom:semver 5.2.0 function version() public pure virtual returns (string memory) { @@ -236,9 +231,6 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase systemConfig = _systemConfig; anchorStateRegistry = _anchorStateRegistry; - // Assert that the lockbox state is valid. - _assertValidLockboxState(); - // Set the l2Sender slot, only if it is currently empty. This signals the first // initialization of the contract. if (l2Sender == address(0)) { @@ -472,11 +464,6 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase // Mark the withdrawal as finalized so it can't be replayed. finalizedWithdrawals[withdrawalHash] = true; - // If using ETHLockbox, unlock the ETH from the ETHLockbox. - if (_isUsingLockbox()) { - if (_tx.value > 0) ethLockbox.unlockETH(_tx.value); - } - // Set the l2Sender so contracts know who triggered this withdrawal on L2. l2Sender = _tx.sender; @@ -496,14 +483,6 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase // be achieved through contracts built on top of this contract emit WithdrawalFinalized(withdrawalHash, success); - // If using ETHLockbox, send ETH back to the Lockbox in the case of a failed transaction or - // it'll get stuck here and would need to be moved back via admin action. - if (_isUsingLockbox()) { - if (!success && _tx.value > 0) { - ethLockbox.lockETH{ value: _tx.value }(); - } - } - // Reverting here is useful for determining the exact gas cost to successfully execute the // sub call to the target contract if the minimum gas limit specified by the user would not // be sufficient to execute the sub call. @@ -555,8 +534,8 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase /// deriving deposit transactions. Note that if a deposit is made by a contract, its /// address will be aliased when retrieved using `tx.origin` or `msg.sender`. Consider /// using the CrossDomainMessenger contracts for a simpler developer experience. - /// @dev The `msg.value` is locked on the ETHLockbox and minted as ETH when the deposit - /// arrives on L2, while `_value` specifies how much ETH to send to the target. + /// @dev The `msg.value` is minted as ETH when the deposit arrives on L2, while `_value` + /// specifies how much ETH to send to the target. /// @param _to Target address on L2. /// @param _value ETH value to send to the recipient. /// @param _gasLimit Amount of L2 gas to purchase by burning gas on L1. @@ -577,11 +556,6 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase if (msg.value > 0) revert OptimismPortal_NotAllowedOnCGTMode(); } - // If using ETHLockbox, lock the ETH in the ETHLockbox. - if (_isUsingLockbox()) { - if (msg.value > 0) ethLockbox.lockETH{ value: msg.value }(); - } - // Just to be safe, make sure that people specify address(0) as the target when doing // contract creations. if (_isCreation && _to != address(0)) { @@ -625,12 +599,6 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase return proofSubmitters[_withdrawalHash].length; } - /// @notice Checks if the ETHLockbox feature is enabled. - /// @return bool True if the ETHLockbox feature is enabled. - function _isUsingLockbox() internal view returns (bool) { - return systemConfig.isFeatureEnabled(Features.ETH_LOCKBOX) && address(ethLockbox) != address(0); - } - /// @notice Checks if the Custom Gas Token feature is enabled. /// @return bool True if the Custom Gas Token feature is enabled. function _isUsingCustomGasToken() internal view returns (bool) { @@ -646,20 +614,10 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase } } - /// @notice Asserts that the ETHLockbox is set/unset correctly depending on the feature flag. - function _assertValidLockboxState() internal view { - if ( - systemConfig.isFeatureEnabled(Features.ETH_LOCKBOX) && address(ethLockbox) == address(0) - || !systemConfig.isFeatureEnabled(Features.ETH_LOCKBOX) && address(ethLockbox) != address(0) - ) { - revert OptimismPortal_InvalidLockboxState(); - } - } - /// @notice Checks if a target address is unsafe. function _isUnsafeTarget(address _target) internal view virtual returns (bool) { // Prevent users from targeting an unsafe target address on a withdrawal transaction. - return _target == address(this) || _target == address(ethLockbox); + return _target == address(this); } /// @notice Getter for the resource config. Used internally by the ResourceMetering contract. diff --git a/src/L1/SystemConfig.sol b/src/L1/SystemConfig.sol index b5498da18..51c975dba 100644 --- a/src/L1/SystemConfig.sol +++ b/src/L1/SystemConfig.sol @@ -546,32 +546,6 @@ contract SystemConfig is ProxyAdminOwnedBase, OwnableUpgradeable, Reinitializabl revert SystemConfig_InvalidFeatureState(); } - // Handle feature-specific safety logic here. - if (_feature == Features.ETH_LOCKBOX) { - // It would probably better to check that the ETHLockbox contract is set inside the - // OptimismPortal2 contract before you're allowed to enable the feature here, but the - // portal checks that the feature is set before allowing you to set the lockbox, so - // these checks are good enough. - - // Lockbox shouldn't be unset if the ethLockbox address is still configured in the - // OptimismPortal2 contract. Doing so would cause the system to start keeping ETH in - // the portal. This check means there's no way to stop using ETHLockbox at the moment - // after it's been configured (which is expected). - if ( - isFeatureEnabled[_feature] && !_enabled - && address(IOptimismPortal2(payable(optimismPortal())).ethLockbox()) != address(0) - ) { - revert SystemConfig_InvalidFeatureState(); - } - - // Lockbox can't be set or unset if the system is currently paused because it would - // change the pause identifier which would potentially cause the system to become - // unpaused unexpectedly. - if (paused()) { - revert SystemConfig_InvalidFeatureState(); - } - } - // Set the feature. isFeatureEnabled[_feature] = _enabled; @@ -579,20 +553,10 @@ contract SystemConfig is ProxyAdminOwnedBase, OwnableUpgradeable, Reinitializabl emit FeatureSet(_feature, _enabled); } - /// @notice Returns the current pause state for this network. If the network is using - /// ETHLockbox, the system is paused if either the global pause is active or the pause - /// is active where the ETHLockbox address is used as the identifier. If the network is - /// not using ETHLockbox, the system is paused if either the global pause is active or - /// the pause is active where the OptimismPortal address is used as the identifier. + /// @notice Returns the current pause state for this network. /// @return bool True if the system is paused, false otherwise. function paused() public view returns (bool) { - // Determine the appropriate chain identifier based on the feature flags. - address identifier = isFeatureEnabled[Features.ETH_LOCKBOX] - ? address(IOptimismPortal2(payable(optimismPortal())).ethLockbox()) - : address(optimismPortal()); - - // Check if either global or local pause is active. - return superchainConfig.paused(address(0)) || superchainConfig.paused(identifier); + return superchainConfig.paused(address(0)) || superchainConfig.paused(optimismPortal()); } /// @notice Returns the guardian address of the SuperchainConfig. diff --git a/src/libraries/Features.sol b/src/libraries/Features.sol index ffe8b8c3e..c630885fb 100644 --- a/src/libraries/Features.sol +++ b/src/libraries/Features.sol @@ -5,12 +5,6 @@ pragma solidity ^0.8.0; /// feature flagging functionality in the SystemConfig contract to selectively enable or /// disable customizable features of the OP Stack. library Features { - /// @notice The ETH_LOCKBOX feature determines if the system is configured to use the - /// ETHLockbox contract in the OptimismPortal. When the ETH_LOCKBOX feature is active - /// and the ETHLockbox contract has been configured, the OptimismPortal will use the - /// ETHLockbox to store ETH instead of storing ETH directly in the portal itself. - bytes32 internal constant ETH_LOCKBOX = "ETH_LOCKBOX"; - /// @notice The CUSTOM_GAS_TOKEN feature determines if the system is configured to use a custom /// gas token in the OptimismPortal. When the CUSTOM_GAS_TOKEN feature is active, the /// deposits and withdrawals of native ETH are disabled. diff --git a/test/L1/ETHLockbox.t.sol b/test/L1/ETHLockbox.t.sol deleted file mode 100644 index 1f96d859d..000000000 --- a/test/L1/ETHLockbox.t.sol +++ /dev/null @@ -1,623 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.15; - -// Testing -import { CommonTest } from "test/setup/CommonTest.sol"; - -// Contracts -import { Proxy } from "src/universal/Proxy.sol"; - -// Libraries -import { Constants } from "src/libraries/Constants.sol"; -import { EIP1967Helper } from "test/mocks/EIP1967Helper.sol"; -import { ForgeArtifacts, StorageSlot } from "scripts/libraries/ForgeArtifacts.sol"; -import { Features } from "src/libraries/Features.sol"; - -// Interfaces -import { IETHLockbox } from "interfaces/L1/IETHLockbox.sol"; -import { IProxyAdminOwnedBase } from "interfaces/L1/IProxyAdminOwnedBase.sol"; -import { IOptimismPortal2 } from "interfaces/L1/IOptimismPortal2.sol"; - -/// @title ETHLockbox_TestInit -/// @notice Base contract that sets up the testing environment for ETHLockbox tests. -abstract contract ETHLockbox_TestInit is CommonTest { - event ETHLocked(IOptimismPortal2 indexed portal, uint256 amount); - event ETHUnlocked(IOptimismPortal2 indexed portal, uint256 amount); - event PortalAuthorized(IOptimismPortal2 indexed portal); - event LockboxAuthorized(IETHLockbox indexed lockbox); - event LiquidityMigrated(IETHLockbox indexed lockbox, uint256 amount); - event LiquidityReceived(IETHLockbox indexed lockbox, uint256 amount); - - function setUp() public virtual override { - super.setUp(); - - // If not on the last upgrade network, we skip the test since the `ETHLockbox` won't be yet - // deployed - // TODO(#14691): Remove this check once Upgrade 15 is deployed on Mainnet. - if (isForkTest() && !deploy.cfg().useUpgradedFork()) vm.skip(true); - - // If the ETHLockbox system feature is not enabled, skip these tests. - skipIfSysFeatureDisabled(Features.ETH_LOCKBOX); - } - - function _mockPortalSharedOwnerAndSuperchainConfig(IOptimismPortal2 _portal) internal { - vm.mockCall( - address(_portal), abi.encodeCall(IProxyAdminOwnedBase.proxyAdminOwner, ()), abi.encode(proxyAdminOwner) - ); - vm.mockCall( - address(_portal), abi.encodeCall(IOptimismPortal2.superchainConfig, ()), abi.encode(superchainConfig) - ); - } - - function _authorizePortalIfNeeded(IOptimismPortal2 _portal) internal { - if (!ethLockbox.authorizedPortals(_portal)) { - vm.prank(proxyAdminOwner); - ethLockbox.authorizePortal(_portal); - } - } - - function _mockLockboxSharedOwner(address _lockbox) internal { - vm.mockCall( - address(_lockbox), abi.encodeCall(IProxyAdminOwnedBase.proxyAdminOwner, ()), abi.encode(proxyAdminOwner) - ); - } -} - -/// @title ETHLockbox_Version_Test -/// @notice Test contract for the `version` function. -contract ETHLockbox_Version_Test is ETHLockbox_TestInit { - /// @notice Tests that the `version` function returns a valid string. We avoid testing the - /// specific value of the string as it changes frequently. - function test_version_succeeds() public view { - assert(bytes(ethLockbox.version()).length > 0); - } -} - -/// @title ETHLockbox_Initialize_Test -/// @notice Test contract for the initialize function. -contract ETHLockbox_Initialize_Test is ETHLockbox_TestInit { - StorageSlot internal initializedSlot; - - function setUp() public override { - super.setUp(); - - initializedSlot = ForgeArtifacts.getSlot("ETHLockbox", "_initialized"); - } - - /// @notice Tests the superchain config was correctly set during initialization. - function test_initialize_succeeds() public view { - assertEq(address(ethLockbox.systemConfig().superchainConfig()), address(superchainConfig)); - assertEq(ethLockbox.authorizedPortals(optimismPortal2), true); - assertEq(address(ethLockbox.superchainConfig()), address(superchainConfig)); - } - - /// @notice Tests that the initializer value is correct. Trivial test for normal initialization - /// but confirms that the initValue is not incremented incorrectly if an upgrade - /// function is not present. - function test_initialize_correctInitializerValue_succeeds() public view { - bytes32 slotVal = vm.load(address(ethLockbox), bytes32(initializedSlot.slot)); - uint8 val = uint8(uint256(slotVal) & 0xFF); - - assertEq(val, ethLockbox.initVersion()); - } - - /// @notice Tests that the `initialize` function reverts if called by a non-proxy admin or - /// owner. - /// @param _sender The address of the sender to test. - function testFuzz_initialize_notProxyAdminOrProxyAdminOwner_reverts(address _sender) public { - // Prank as the not ProxyAdmin or ProxyAdmin owner. - vm.assume(_sender != address(proxyAdmin) && _sender != proxyAdminOwner); - - // Set the initialized slot to 0. - vm.store(address(ethLockbox), bytes32(initializedSlot.slot), bytes32(0)); - - // Expect the revert with `ProxyAdminOwnedBase_NotProxyAdminOrProxyAdminOwner` selector - vm.expectRevert(IProxyAdminOwnedBase.ProxyAdminOwnedBase_NotProxyAdminOrProxyAdminOwner.selector); - - // Call the `initialize` function with the sender - vm.prank(_sender); - IOptimismPortal2[] memory _portals = new IOptimismPortal2[](1); - ethLockbox.initialize(systemConfig, _portals); - } - - /// @notice Tests it reverts when the contract is already initialized. - function test_initialize_alreadyInitialized_reverts() public { - vm.expectRevert("Initializable: contract is already initialized"); - IOptimismPortal2[] memory _portals = new IOptimismPortal2[](1); - ethLockbox.initialize(systemConfig, _portals); - } -} - -/// @title ETHLockbox_Paused_Test -/// @notice Test contract for the `paused` function. -contract ETHLockbox_Paused_Test is ETHLockbox_TestInit { - /// @notice Tests the `paused` status is correctly returned. - function test_paused_succeeds() public { - // Assert the paused status is false - assertEq(ethLockbox.paused(), false); - - // Mock the superchain config to return true for the paused status - // We use abi.encodeWithSignature because paused is overloaded. - // nosemgrep: sol-style-use-abi-encodecall - vm.mockCall(address(superchainConfig), abi.encodeWithSignature("paused(address)", address(0)), abi.encode(true)); - - // Assert the paused status is true - assertEq(ethLockbox.paused(), true); - } -} - -/// @title ETHLockbox_AuthorizePortal_Test -/// @notice Test contract for the authorizePortal function. -contract ETHLockbox_AuthorizePortal_Test is ETHLockbox_TestInit { - /// @notice Tests the `authorizePortal` function reverts when the caller is not the proxy - /// admin. - function testFuzz_authorizePortal_unauthorized_reverts(address _caller) public { - vm.assume(_caller != proxyAdminOwner); - - // Expect the revert with `ProxyAdminOwnedBase_NotProxyAdminOwner` selector - vm.expectRevert(IProxyAdminOwnedBase.ProxyAdminOwnedBase_NotProxyAdminOwner.selector); - - // Call the `authorizePortal` function with an unauthorized caller - vm.prank(_caller); - ethLockbox.authorizePortal(optimismPortal2); - } - - /// @notice Tests the `authorizePortal` function reverts when the proxy admin owner of the - /// portal is not the same as the one of the lockbox. - function testFuzz_authorizePortal_differentProxyAdminOwner_reverts(IOptimismPortal2 _portal) public { - assumeNotForgeAddress(address(_portal)); - vm.mockCall(address(_portal), abi.encodeCall(IProxyAdminOwnedBase.proxyAdminOwner, ()), abi.encode(address(0))); - - // Expect the revert with `DifferentOwner` selector - vm.expectRevert(IProxyAdminOwnedBase.ProxyAdminOwnedBase_NotSharedProxyAdminOwner.selector); - - // Call the `authorizePortal` function - vm.prank(proxyAdminOwner); - ethLockbox.authorizePortal(_portal); - } - - /// @notice Tests the `authorizePortal` function reverts when the portal has a different - /// SuperchainConfig than the one configured in the lockbox. - /// @param _portal The portal to authorize. - function testFuzz_authorizePortal_differentSuperchainConfig_reverts(IOptimismPortal2 _portal) public { - assumeNotForgeAddress(address(_portal)); - vm.assume(address(_portal) != address(systemConfig)); - vm.assume(address(_portal) != EIP1967Helper.getImplementation(address(systemConfig))); - - // Mock the portal to have the right proxyAdminOwner. - vm.mockCall( - address(_portal), abi.encodeCall(IProxyAdminOwnedBase.proxyAdminOwner, ()), abi.encode(proxyAdminOwner) - ); - - // Mock the portal to have the wrong SuperchainConfig. - vm.mockCall(address(_portal), abi.encodeCall(IOptimismPortal2.superchainConfig, ()), abi.encode(address(0))); - - // Expect the revert with `DifferentSuperchainConfig` selector - vm.expectRevert(IETHLockbox.ETHLockbox_DifferentSuperchainConfig.selector); - - // Call the `authorizePortal` function - vm.prank(proxyAdminOwner); - ethLockbox.authorizePortal(_portal); - } - - /// @notice Tests the `authorizePortal` function succeeds using the `optimismPortal2` address - /// as the portal. - function test_authorizePortal_succeeds() public { - StorageSlot memory authorizedPortalsSlot = ForgeArtifacts.getSlot("ETHLockbox", "authorizedPortals"); - address key = address(optimismPortal2); - bytes32 slot = keccak256(abi.encode(key, bytes32(authorizedPortalsSlot.slot))); - - // Reset the authorization status to false - vm.store(address(ethLockbox), slot, bytes32(0)); - - // Expect the `PortalAuthorized` event to be emitted - vm.expectEmit(address(ethLockbox)); - emit PortalAuthorized(optimismPortal2); - - // Call the `authorizePortal` function with the portal - vm.prank(proxyAdminOwner); - ethLockbox.authorizePortal(optimismPortal2); - - // Assert the portal is authorized - assertTrue(ethLockbox.authorizedPortals(optimismPortal2)); - } - - /// @notice Tests the `authorizePortal` function succeeds - function testFuzz_authorizePortal_succeeds(IOptimismPortal2 _portal) public { - assumeNotForgeAddress(address(_portal)); - - _mockPortalSharedOwnerAndSuperchainConfig(_portal); - - // Expect the `PortalAuthorized` event to be emitted - vm.expectEmit(address(ethLockbox)); - emit PortalAuthorized(_portal); - - // Call the `authorizePortal` function with the portal - vm.prank(proxyAdminOwner); - ethLockbox.authorizePortal(_portal); - - // Assert the portal is authorized - assertTrue(ethLockbox.authorizedPortals(_portal)); - } -} - -/// @title ETHLockbox_ReceiveLiquidity_Test -/// @notice Test contract for the receiveLiquidity function. -contract ETHLockbox_ReceiveLiquidity_Test is ETHLockbox_TestInit { - /// @notice Tests the liquidity is correctly received. - function testFuzz_receiveLiquidity_succeeds(address _lockbox, uint256 _value) public { - // Since on the fork the `_lockbox` fuzzed address doesn't exist, we skip the test - if (isForkTest()) vm.skip(true); - assumeNotForgeAddress(_lockbox); - vm.assume(address(_lockbox) != address(ethLockbox)); - - // Deal the value to the lockbox - deal(address(_lockbox), _value); - - _mockLockboxSharedOwner(_lockbox); - - // Authorize the lockbox if needed - if (!ethLockbox.authorizedLockboxes(IETHLockbox(_lockbox))) { - vm.prank(proxyAdminOwner); - ethLockbox.authorizeLockbox(IETHLockbox(_lockbox)); - } - - // Get the balance of the lockbox before the receive - uint256 ethLockboxBalanceBefore = address(ethLockbox).balance; - - // Expect the `LiquidityReceived` event to be emitted - vm.expectEmit(address(ethLockbox)); - emit LiquidityReceived(IETHLockbox(_lockbox), _value); - - // Call the `receiveLiquidity` function - vm.prank(address(_lockbox)); - ethLockbox.receiveLiquidity{ value: _value }(); - - // Assert the lockbox's balance increased by the amount received - assertEq(address(ethLockbox).balance, ethLockboxBalanceBefore + _value); - } -} - -/// @title ETHLockbox_LockETH_Test -/// @notice Test contract for the lockETH function. -contract ETHLockbox_LockETH_Test is ETHLockbox_TestInit { - /// @notice Tests it reverts when the caller is not an authorized portal. - function testFuzz_lockETH_unauthorizedPortal_reverts(address _caller) public { - vm.assume(!ethLockbox.authorizedPortals(IOptimismPortal2(payable(_caller)))); - - // Expect the revert with `Unauthorized` selector - vm.expectRevert(IETHLockbox.ETHLockbox_Unauthorized.selector); - - // Call the `lockETH` function with an unauthorized caller - vm.prank(_caller); - ethLockbox.lockETH(); - } - - /// @notice Tests the ETH is correctly locked when the caller is an authorized portal. - function testFuzz_lockETH_succeeds(uint256 _amount) public { - // Prevent overflow on an upgrade context - _amount = bound(_amount, 0, type(uint256).max - address(ethLockbox).balance); - - // Deal the ETH amount to the portal - vm.deal(address(optimismPortal2), _amount); - - // Get the balance of the portal and lockbox before the lock to compare later on the - // assertions - uint256 portalBalanceBefore = address(optimismPortal2).balance; - uint256 lockboxBalanceBefore = address(ethLockbox).balance; - - // Look for the emit of the `ETHLocked` event - vm.expectEmit(address(ethLockbox)); - emit ETHLocked(optimismPortal2, _amount); - - // Call the `lockETH` function with the portal - vm.prank(address(optimismPortal2)); - ethLockbox.lockETH{ value: _amount }(); - - // Assert the portal's balance decreased and the lockbox's balance increased by the - // amount locked - assertEq(address(optimismPortal2).balance, portalBalanceBefore - _amount); - assertEq(address(ethLockbox).balance, lockboxBalanceBefore + _amount); - } - - /// @notice Tests the ETH is correctly locked when the caller is an authorized portal with - /// different portals. - function testFuzz_lockETH_multiplePortals_succeeds(IOptimismPortal2 _portal, uint256 _amount) public { - // Since on the fork the `_portal` fuzzed address doesn't exist, we skip the test - if (isForkTest()) vm.skip(true); - assumeNotForgeAddress(address(_portal)); - vm.assume(address(_portal) != address(ethLockbox)); - - _mockPortalSharedOwnerAndSuperchainConfig(_portal); - - // Set the portal as an authorized portal if needed - _authorizePortalIfNeeded(_portal); - - // Deal the ETH amount to the portal - vm.deal(address(_portal), _amount); - - // Get the balance of the lockbox before the lock to compare later on the assertions - uint256 lockboxBalanceBefore = address(ethLockbox).balance; - - // Look for the emit of the `ETHLocked` event - vm.expectEmit(address(ethLockbox)); - emit ETHLocked(_portal, _amount); - - // Call the `lockETH` function with the portal - vm.prank(address(_portal)); - ethLockbox.lockETH{ value: _amount }(); - - // Assert the portal's balance decreased and the lockbox's balance increased by the - // amount locked - assertEq(address(ethLockbox).balance, lockboxBalanceBefore + _amount); - } -} - -/// @title ETHLockbox_UnlockETH_Test -/// @notice Test contract for the unlockETH function. -contract ETHLockbox_UnlockETH_Test is ETHLockbox_TestInit { - /// @notice Tests `unlockETH` reverts when the contract is paused. - function testFuzz_unlockETH_paused_reverts(address _caller, uint256 _value) public { - // Mock the superchain config to return true for the paused status - // We use abi.encodeWithSignature because paused is overloaded. - // nosemgrep: sol-style-use-abi-encodecall - vm.mockCall(address(superchainConfig), abi.encodeWithSignature("paused(address)", address(0)), abi.encode(true)); - - // Expect the revert with `Paused` selector - vm.expectRevert(IETHLockbox.ETHLockbox_Paused.selector); - - // Call the `unlockETH` function with the caller - vm.prank(_caller); - ethLockbox.unlockETH(_value); - } - - /// @notice Tests it reverts when the caller is not an authorized portal. - function testFuzz_unlockETH_unauthorizedPortal_reverts(address _caller, uint256 _value) public { - vm.assume(!ethLockbox.authorizedPortals(IOptimismPortal2(payable(_caller)))); - - // Expect the revert with `Unauthorized` selector - vm.expectRevert(IETHLockbox.ETHLockbox_Unauthorized.selector); - - // Call the `unlockETH` function with an unauthorized caller - vm.prank(_caller); - ethLockbox.unlockETH(_value); - } - - /// @notice Tests `unlockETH` reverts when the `_value` input is greater than the balance of - /// the lockbox. - function testFuzz_unlockETH_insufficientBalance_reverts(uint256 _value) public { - _value = bound(_value, address(ethLockbox).balance + 1, type(uint256).max); - - // Expect the revert with `InsufficientBalance` selector - vm.expectRevert(IETHLockbox.ETHLockbox_InsufficientBalance.selector); - - // Call the `unlockETH` function with the portal - vm.prank(address(optimismPortal2)); - ethLockbox.unlockETH(_value); - } - - /// @notice Tests `unlockETH` reverts when the portal is not the L2 sender to prevent - /// unlocking ETH from the lockbox through a withdrawal transaction. - function testFuzz_unlockETH_withdrawalTransaction_reverts(uint256 _value, address _l2Sender) public { - _value = bound(_value, 0, address(ethLockbox).balance); - vm.assume(_l2Sender != Constants.DEFAULT_L2_SENDER); - - // Mock the L2 sender - vm.mockCall(address(optimismPortal2), abi.encodeCall(IOptimismPortal2.l2Sender, ()), abi.encode(_l2Sender)); - - // Expect the revert with `NoWithdrawalTransactions` selector - vm.expectRevert(IETHLockbox.ETHLockbox_NoWithdrawalTransactions.selector); - - // Call the `unlockETH` function with the portal - vm.prank(address(optimismPortal2)); - ethLockbox.unlockETH(_value); - } - - /// @notice Tests the ETH is correctly unlocked when the caller is an authorized portal. - function testFuzz_unlockETH_succeeds(uint256 _value) public { - // Deal the ETH amount to the lockbox - vm.deal(address(ethLockbox), _value); - - // Get the balance of the portal and lockbox before the unlock to compare later on the - // assertions - uint256 portalBalanceBefore = address(optimismPortal2).balance; - uint256 lockboxBalanceBefore = address(ethLockbox).balance; - - // Expect `donateETH` function to be called on Portal - vm.expectCall(address(optimismPortal2), abi.encodeCall(IOptimismPortal2.donateETH, ())); - - // Look for the emit of the `ETHUnlocked` event - vm.expectEmit(address(ethLockbox)); - emit ETHUnlocked(optimismPortal2, _value); - - // Call the `unlockETH` function with the portal - vm.prank(address(optimismPortal2)); - ethLockbox.unlockETH(_value); - - // Assert the portal's balance increased and the lockbox's balance decreased by the amount - // unlocked - assertEq(address(optimismPortal2).balance, portalBalanceBefore + _value); - assertEq(address(ethLockbox).balance, lockboxBalanceBefore - _value); - } - - /// @notice Tests the ETH is correctly unlocked when the caller is an authorized portal. - function testFuzz_unlockETH_multiplePortals_succeeds(IOptimismPortal2 _portal, uint256 _value) public { - // Since on the fork the `_portal` fuzzed address doesn't exist, we skip the test - if (isForkTest()) vm.skip(true); - assumeNotForgeAddress(address(_portal)); - vm.assume(address(_portal) != address(ethLockbox)); - - _mockPortalSharedOwnerAndSuperchainConfig(_portal); - vm.mockCall( - address(_portal), abi.encodeCall(IOptimismPortal2.l2Sender, ()), abi.encode(Constants.DEFAULT_L2_SENDER) - ); - - // Set the portal as an authorized portal if needed - _authorizePortalIfNeeded(_portal); - - // Deal the ETH amount to the lockbox - vm.deal(address(ethLockbox), _value); - - // Get the balance of the portal and lockbox before the unlock to compare later on the - // assertions - uint256 portalBalanceBefore = address(_portal).balance; - uint256 lockboxBalanceBefore = address(ethLockbox).balance; - - // Expect `donateETH` function to be called on Portal - vm.expectCall(address(_portal), abi.encodeCall(IOptimismPortal2.donateETH, ())); - - // Look for the emit of the `ETHUnlocked` event - vm.expectEmit(address(ethLockbox)); - emit ETHUnlocked(_portal, _value); - - // Call the `unlockETH` function with the portal - vm.prank(address(_portal)); - ethLockbox.unlockETH(_value); - - // Assert the portal's balance increased and the lockbox's balance decreased by the amount - // unlocked - assertEq(address(_portal).balance, portalBalanceBefore + _value); - assertEq(address(ethLockbox).balance, lockboxBalanceBefore - _value); - } -} - -/// @title ETHLockbox_AuthorizeLockbox_Test -/// @notice Test contract for the authorizeLockbox function. -contract ETHLockbox_AuthorizeLockbox_Test is ETHLockbox_TestInit { - /// @notice Tests the `authorizeLockbox` function reverts when the caller is not the proxy - /// admin. - function testFuzz_authorizeLockbox_unauthorized_reverts(address _caller) public { - vm.assume(_caller != proxyAdminOwner); - - // Expect the revert with `ProxyAdminOwnedBase_NotProxyAdminOwner` selector - vm.expectRevert(IProxyAdminOwnedBase.ProxyAdminOwnedBase_NotProxyAdminOwner.selector); - - // Call the `authorizeLockbox` function with an unauthorized caller - vm.prank(_caller); - ethLockbox.authorizeLockbox(ethLockbox); - } - - /// @notice Tests the `authorizeLockbox` function reverts when the proxy admin owner of the - /// lockbox is not the same as the proxy admin owner of the proxy admin. - function testFuzz_authorizeLockbox_differentProxyAdminOwner_reverts(address _lockbox) public { - assumeNotForgeAddress(_lockbox); - - vm.mockCall(address(_lockbox), abi.encodeCall(IProxyAdminOwnedBase.proxyAdminOwner, ()), abi.encode(address(0))); - - // Expect the revert with `NotSharedProxyAdminOwner` selector - vm.expectRevert(IProxyAdminOwnedBase.ProxyAdminOwnedBase_NotSharedProxyAdminOwner.selector); - - // Call the `authorizeLockbox` function with the lockbox - vm.prank(proxyAdminOwner); - ethLockbox.authorizeLockbox(IETHLockbox(_lockbox)); - } - - /// @notice Tests the `authorizeLockbox` function succeeds - function testFuzz_authorizeLockbox_succeeds(address _lockbox) public { - assumeNotForgeAddress(_lockbox); - - _mockLockboxSharedOwner(_lockbox); - - // Expect the `LockboxAuthorized` event to be emitted - vm.expectEmit(address(ethLockbox)); - emit LockboxAuthorized(IETHLockbox(_lockbox)); - - // Authorize the lockbox - vm.prank(proxyAdminOwner); - ethLockbox.authorizeLockbox(IETHLockbox(_lockbox)); - - // Assert the lockbox is authorized - assertTrue(ethLockbox.authorizedLockboxes(IETHLockbox(_lockbox))); - } -} - -/// @title ETHLockbox_MigrateLiquidity_Test -/// @notice Test contract for the migrateLiquidity function. -contract ETHLockbox_MigrateLiquidity_Test is ETHLockbox_TestInit { - /// @notice Tests the `migrateLiquidity` function reverts when the caller is not the proxy - /// admin. - function testFuzz_migrateLiquidity_unauthorized_reverts(address _caller) public { - vm.assume(_caller != proxyAdminOwner); - - // Expect the revert with `ProxyAdminOwnedBase_NotProxyAdminOwner` selector - vm.expectRevert(IProxyAdminOwnedBase.ProxyAdminOwnedBase_NotProxyAdminOwner.selector); - - // Call the `migrateLiquidity` function with an unauthorized caller - vm.prank(_caller); - ethLockbox.migrateLiquidity(ethLockbox); - } - - /// @notice Tests the `migrateLiquidity` function reverts when the proxy admin owner of the - /// lockbox is not the same as the proxy admin owner of the proxy admin. - function testFuzz_migrateLiquidity_differentProxyAdminOwner_reverts(address _lockbox) public { - assumeNotForgeAddress(_lockbox); - - vm.mockCall(address(_lockbox), abi.encodeCall(IProxyAdminOwnedBase.proxyAdminOwner, ()), abi.encode(address(0))); - - // Expect the revert with `NotSharedProxyAdminOwner` selector - vm.expectRevert(IProxyAdminOwnedBase.ProxyAdminOwnedBase_NotSharedProxyAdminOwner.selector); - - // Call the `migrateLiquidity` function with the lockbox - vm.prank(proxyAdminOwner); - ethLockbox.migrateLiquidity(IETHLockbox(_lockbox)); - } - - /// @notice Tests the `migrateLiquidity` function succeeds - function testFuzz_migrateLiquidity_succeeds( - uint256 _originLockboxBalance, - uint256 _destinationLockboxBalance - ) - public - { - // Since on the fork the `_lockbox` fuzzed address doesn't exist, we skip the test - if (isForkTest()) vm.skip(true); - - // Bound balances to avoid overflow - _originLockboxBalance = bound(_originLockboxBalance, 0, type(uint256).max - address(ethLockbox).balance); - _destinationLockboxBalance = bound(_destinationLockboxBalance, 0, type(uint256).max - _originLockboxBalance); - - // Deploy a new Proxy for the destination lockbox - address destinationLockbox = address(new Proxy(address(proxyAdmin))); - - // Get the ETHLockbox implementation of the origin `ethLockbox` proxy - vm.prank(address(proxyAdmin)); - address implementation = Proxy(payable(address(ethLockbox))).implementation(); - - // Upgrade the destination lockbox proxy to the `ETHLockbox` implementation - vm.prank(address(proxyAdmin)); - Proxy(payable(destinationLockbox)).upgradeTo(implementation); - - // Authorize the origin lockbox on the destination lockbox - vm.prank(proxyAdminOwner); - IETHLockbox(destinationLockbox).authorizeLockbox(ethLockbox); - - // Deal the balance to both lockboxes - deal(address(ethLockbox), _originLockboxBalance); - deal(address(destinationLockbox), _destinationLockboxBalance); - - // Get balances before the migration - uint256 originLockboxBalanceBefore = address(ethLockbox).balance; - uint256 destLockboxBalanceBefore = address(destinationLockbox).balance; - - // Expect the `LiquidityMigrated` event to be emitted - vm.expectEmit(address(ethLockbox)); - emit LiquidityMigrated(IETHLockbox(destinationLockbox), originLockboxBalanceBefore); - - // Call the `migrateLiquidity` function with the lockbox - vm.prank(proxyAdminOwner); - ethLockbox.migrateLiquidity(IETHLockbox(destinationLockbox)); - - // Assert the liquidity was migrated - assertEq(address(ethLockbox).balance, 0); - assertEq(address(destinationLockbox).balance, destLockboxBalanceBefore + originLockboxBalanceBefore); - } -} - -/// @title ETHLockbox_Uncategorized_Test -/// @notice Contains uncategorized tests related to ETHLockbox. -contract ETHLockbox_Uncategorized_Test is ETHLockbox_TestInit { - /// @notice Tests the proxy admin owner is correctly returned. - function test_proxyProxyAdminOwner_succeeds() public view { - assertEq(ethLockbox.proxyAdminOwner(), proxyAdminOwner); - } -} diff --git a/test/L1/L1StandardBridge.t.sol b/test/L1/L1StandardBridge.t.sol index 206087776..c944b2d99 100644 --- a/test/L1/L1StandardBridge.t.sol +++ b/test/L1/L1StandardBridge.t.sol @@ -25,20 +25,8 @@ import { IProxyAdminOwnedBase } from "interfaces/L1/IProxyAdminOwnedBase.sol"; /// @title L1StandardBridge_TestInit /// @notice Reusable test initialization for `L1StandardBridge` tests. abstract contract L1StandardBridge_TestInit is CommonTest { - function _assertETHBridgeCustody( - uint256 _portalBalanceBefore, - uint256 _ethLockboxBalanceBefore, - uint256 _amount - ) - internal - view - { - if (isSysFeatureEnabled(Features.ETH_LOCKBOX)) { - assertEq(address(optimismPortal2).balance, _portalBalanceBefore); - assertEq(address(ethLockbox).balance, _ethLockboxBalanceBefore + _amount); - } else { - assertEq(address(optimismPortal2).balance, _portalBalanceBefore + _amount); - } + function _assertETHBridgeCustody(uint256 _portalBalanceBefore, uint256 _amount) internal view { + assertEq(address(optimismPortal2).balance, _portalBalanceBefore + _amount); } function _mockXDomainMessageSender(address _sender) internal { @@ -356,7 +344,6 @@ contract L1StandardBridge_Receive_Test is L1StandardBridge_TestInit { function test_receive_succeeds() external { skipIfSysFeatureEnabled(Features.CUSTOM_GAS_TOKEN); uint256 portalBalanceBefore = address(optimismPortal2).balance; - uint256 ethLockboxBalanceBefore = address(ethLockbox).balance; // The legacy event must be emitted for backwards compatibility vm.expectEmit(address(l1StandardBridge)); @@ -381,7 +368,7 @@ contract L1StandardBridge_Receive_Test is L1StandardBridge_TestInit { (bool success,) = address(l1StandardBridge).call{ value: 100 }(hex""); assertTrue(success); - _assertETHBridgeCustody(portalBalanceBefore, ethLockboxBalanceBefore, 100); + _assertETHBridgeCustody(portalBalanceBefore, 100); } /// @notice Verifies receive function reverts when called by contracts @@ -407,10 +394,9 @@ contract L1StandardBridge_DepositETH_Test is L1StandardBridge_TestInit { skipIfSysFeatureEnabled(Features.CUSTOM_GAS_TOKEN); _preBridgeETH({ isLegacy: true, value: 500 }); uint256 portalBalanceBefore = address(optimismPortal2).balance; - uint256 ethLockboxBalanceBefore = address(ethLockbox).balance; l1StandardBridge.depositETH{ value: 500 }(50000, hex"dead"); - _assertETHBridgeCustody(portalBalanceBefore, ethLockboxBalanceBefore, 500); + _assertETHBridgeCustody(portalBalanceBefore, 500); } /// @notice Tests that depositing ETH succeeds for an EOA using 7702 delegation. @@ -421,10 +407,9 @@ contract L1StandardBridge_DepositETH_Test is L1StandardBridge_TestInit { _preBridgeETH({ isLegacy: true, value: 500 }); uint256 portalBalanceBefore = address(optimismPortal2).balance; - uint256 ethLockboxBalanceBefore = address(ethLockbox).balance; l1StandardBridge.depositETH{ value: 500 }(50000, hex"dead"); - _assertETHBridgeCustody(portalBalanceBefore, ethLockboxBalanceBefore, 500); + _assertETHBridgeCustody(portalBalanceBefore, 500); } /// @notice Tests that depositing ETH reverts if the call is not from an EOA. @@ -448,10 +433,9 @@ contract L1StandardBridge_DepositETHTo_Test is L1StandardBridge_TestInit { skipIfSysFeatureEnabled(Features.CUSTOM_GAS_TOKEN); _preBridgeETHTo({ isLegacy: true, value: 600 }); uint256 portalBalanceBefore = address(optimismPortal2).balance; - uint256 ethLockboxBalanceBefore = address(ethLockbox).balance; l1StandardBridge.depositETHTo{ value: 600 }(bob, 60000, hex"dead"); - _assertETHBridgeCustody(portalBalanceBefore, ethLockboxBalanceBefore, 600); + _assertETHBridgeCustody(portalBalanceBefore, 600); } /// @notice Verifies depositETHTo succeeds with various recipients and amounts @@ -465,12 +449,11 @@ contract L1StandardBridge_DepositETHTo_Test is L1StandardBridge_TestInit { vm.deal(alice, _amount); uint256 portalBalanceBefore = address(optimismPortal2).balance; - uint256 ethLockboxBalanceBefore = address(ethLockbox).balance; vm.prank(alice); l1StandardBridge.depositETHTo{ value: _amount }(_to, 60000, hex"dead"); - _assertETHBridgeCustody(portalBalanceBefore, ethLockboxBalanceBefore, _amount); + _assertETHBridgeCustody(portalBalanceBefore, _amount); } } @@ -739,10 +722,9 @@ contract L1StandardBridge_Uncategorized_Test is L1StandardBridge_TestInit { skipIfSysFeatureEnabled(Features.CUSTOM_GAS_TOKEN); _preBridgeETH({ isLegacy: false, value: 500 }); uint256 portalBalanceBefore = address(optimismPortal2).balance; - uint256 ethLockboxBalanceBefore = address(ethLockbox).balance; l1StandardBridge.bridgeETH{ value: 500 }(50000, hex"dead"); - _assertETHBridgeCustody(portalBalanceBefore, ethLockboxBalanceBefore, 500); + _assertETHBridgeCustody(portalBalanceBefore, 500); } /// @notice Tests that bridging ETH to a different address succeeds. @@ -754,10 +736,9 @@ contract L1StandardBridge_Uncategorized_Test is L1StandardBridge_TestInit { skipIfSysFeatureEnabled(Features.CUSTOM_GAS_TOKEN); _preBridgeETHTo({ isLegacy: false, value: 600 }); uint256 portalBalanceBefore = address(optimismPortal2).balance; - uint256 ethLockboxBalanceBefore = address(ethLockbox).balance; l1StandardBridge.bridgeETHTo{ value: 600 }(bob, 60000, hex"dead"); - _assertETHBridgeCustody(portalBalanceBefore, ethLockboxBalanceBefore, 600); + _assertETHBridgeCustody(portalBalanceBefore, 600); } /// @notice Tests that finalizing bridged ETH succeeds. diff --git a/test/L1/OptimismPortal2.t.sol b/test/L1/OptimismPortal2.t.sol index 04e8d1df5..78d54dd60 100644 --- a/test/L1/OptimismPortal2.t.sol +++ b/test/L1/OptimismPortal2.t.sol @@ -114,9 +114,6 @@ abstract contract OptimismPortal2_TestInit is DisputeGameFactory_TestInit { // Fund the portal so that we can withdraw ETH. vm.deal(address(optimismPortal2), 0xFFFFFFFF); - if (isUsingLockbox()) { - vm.deal(address(ethLockbox), 0xFFFFFFFF); - } } function _createDisputeGame(Claim _rootClaim, uint256 _salt) internal returns (IAggregateVerifier game_) { @@ -189,9 +186,6 @@ abstract contract OptimismPortal2_TestInit is DisputeGameFactory_TestInit { { uint256 value = bound(_value, 0, 200_000_000 ether); vm.deal(address(optimismPortal2), value); - if (isUsingLockbox()) { - vm.deal(address(ethLockbox), value); - } uint256 gasLimit = bound(_gasLimit, 0, 50_000_000); withdrawalTx_ = Types.WithdrawalTransaction({ @@ -239,31 +233,6 @@ abstract contract OptimismPortal2_TestInit is DisputeGameFactory_TestInit { // Assert that the withdrawal was not finalized. assertFalse(optimismPortal2.finalizedWithdrawals(Hashing.hashWithdrawal(_defaultTx))); } - - /// @notice Checks if the ETHLockbox feature is enabled. - /// @return bool True if the ETHLockbox feature is enabled. - function isUsingLockbox() internal view returns (bool) { - return - systemConfig.isFeatureEnabled(Features.ETH_LOCKBOX) && address(optimismPortal2.ethLockbox()) != address(0); - } - - /// @notice Enables the ETHLockbox feature if not enabled. - /// @param _lockbox Address of the lockbox to enable. - function forceEnableLockbox(address _lockbox) internal { - if (!isSysFeatureEnabled(Features.ETH_LOCKBOX)) { - vm.prank(address(proxyAdmin)); - systemConfig.setFeature(Features.ETH_LOCKBOX, true); - } - - // Overwrite the lockbox either way. - StorageSlot memory slot = ForgeArtifacts.getSlot("OptimismPortal2", "ethLockbox"); - vm.store(address(optimismPortal2), bytes32(slot.slot), bytes32(uint256(uint160(address(_lockbox))))); - - // If the recipient address has no code, store STOP so we don't get reverts. - if (address(_lockbox).code.length == 0) { - vm.etch(address(_lockbox), hex"00"); - } - } } /// @title OptimismPortal2_Version_Test @@ -285,7 +254,6 @@ contract OptimismPortal2_Constructor_Test is OptimismPortal2_TestInit { assertEq(address(opImpl.anchorStateRegistry()), address(0)); assertEq(address(opImpl.systemConfig()), address(0)); assertEq(opImpl.l2Sender(), address(0)); - assertEq(address(opImpl.ethLockbox()), address(0)); } } @@ -301,16 +269,6 @@ contract OptimismPortal2_Initialize_Test is OptimismPortal2_TestInit { assertEq(optimismPortal2.paused(), false); assertEq(address(optimismPortal2.systemConfig()), address(systemConfig)); - if (isUsingLockbox()) { - assertEq(address(optimismPortal2.ethLockbox()), address(ethLockbox)); - } else { - assertEq(address(optimismPortal2.ethLockbox()), address(0)); - } - - if (!isUsingLockbox()) { - assertFalse(optimismPortal2.systemConfig().isFeatureEnabled(Features.CUSTOM_GAS_TOKEN)); - } - returnIfForkTest( "OptimismPortal2_Initialize_Test: Do not check guardian and respectedGameType on forked networks" ); @@ -338,32 +296,6 @@ contract OptimismPortal2_Initialize_Test is OptimismPortal2_TestInit { assertEq(val, optimismPortal2.initVersion()); } - /// @notice Tests that the initialize function reverts when lockbox state is invalid. - function test_initialize_invalidLockboxState_reverts() external { - // Get the slot for _initialized. - StorageSlot memory slot = ForgeArtifacts.getSlot("OptimismPortal2", "_initialized"); - - // Set the initialized slot to 0. - vm.store(address(optimismPortal2), bytes32(slot.slot), bytes32(0)); - - // Enable ETH_LOCKBOX feature but clear the lockbox address to create invalid state. - if (!systemConfig.isFeatureEnabled(Features.ETH_LOCKBOX)) { - vm.prank(address(proxyAdmin)); - systemConfig.setFeature(Features.ETH_LOCKBOX, true); - } - - // Clear the lockbox address. - StorageSlot memory lockboxSlot = ForgeArtifacts.getSlot("OptimismPortal2", "ethLockbox"); - vm.store(address(optimismPortal2), bytes32(lockboxSlot.slot), bytes32(0)); - - // Expect the revert with `OptimismPortal_InvalidLockboxState` selector. - vm.expectRevert(IOptimismPortal.OptimismPortal_InvalidLockboxState.selector); - - // Call the `initialize` function - vm.prank(address(proxyAdmin)); - optimismPortal2.initialize(systemConfig, anchorStateRegistry); - } - /// @notice Tests that the initialize function reverts if called by a non-proxy admin or owner. /// @param _sender The address of the sender to test. function testFuzz_initialize_notProxyAdminOrProxyAdminOwner_reverts(address _sender) public { @@ -546,9 +478,7 @@ contract OptimismPortal2_Receive_Test is OptimismPortal2_TestInit { function testFuzz_receive_succeeds(uint256 _value) external { skipIfSysFeatureEnabled(Features.CUSTOM_GAS_TOKEN); // Prevent overflow on an upgrade context - _value = bound(_value, 0, type(uint256).max - address(ethLockbox).balance); uint256 balanceBefore = address(optimismPortal2).balance; - uint256 lockboxBalanceBefore = address(ethLockbox).balance; _value = bound(_value, 0, type(uint256).max - balanceBefore); vm.expectEmit(address(optimismPortal2)); @@ -562,11 +492,6 @@ contract OptimismPortal2_Receive_Test is OptimismPortal2_TestInit { _data: hex"" }); - if (isUsingLockbox()) { - // Expect call to the ETHLockbox to lock the funds only if the value is greater than 0. - vm.expectCall(address(ethLockbox), _value, abi.encodeCall(ethLockbox.lockETH, ()), _value > 0 ? 1 : 0); - } - // give alice money and send as an eoa vm.deal(alice, _value); vm.prank(alice, alice); @@ -574,50 +499,7 @@ contract OptimismPortal2_Receive_Test is OptimismPortal2_TestInit { assertTrue(s); - if (isUsingLockbox()) { - assertEq(address(optimismPortal2).balance, balanceBefore); - assertEq(address(ethLockbox).balance, lockboxBalanceBefore + _value); - } else { - assertEq(address(optimismPortal2).balance, balanceBefore + _value); - } - } - - function testFuzz_receive_withLockbox_succeeds(uint256 _value) external { - skipIfSysFeatureEnabled(Features.CUSTOM_GAS_TOKEN); - // Prevent overflow on an upgrade context. - // We use a dummy lockbox here because the real one won't work for upgrade tests. - address dummyLockbox = address(0xdeadbeef); - _value = bound(_value, 0, type(uint256).max - address(dummyLockbox).balance); - uint256 balanceBefore = address(optimismPortal2).balance; - uint256 lockboxBalanceBefore = address(dummyLockbox).balance; - _value = bound(_value, 0, type(uint256).max - balanceBefore); - - // Enable the lockbox. - forceEnableLockbox(dummyLockbox); - - // Expect the transaction deposited event. - vm.expectEmit(address(optimismPortal2)); - emitTransactionDeposited({ - _from: alice, - _to: alice, - _value: _value, - _mint: _value, - _gasLimit: 100_000, - _isCreation: false, - _data: hex"" - }); - - // Expect call to the ETHLockbox to lock the funds only if the value is greater than 0. - vm.expectCall(address(dummyLockbox), _value, abi.encodeCall(ethLockbox.lockETH, ()), _value > 0 ? 1 : 0); - - // give alice money and send as an eoa - vm.deal(alice, _value); - vm.prank(alice, alice); - (bool s,) = address(optimismPortal2).call{ value: _value }(hex""); - - assertTrue(s); - assertEq(address(optimismPortal2).balance, balanceBefore); - assertEq(address(dummyLockbox).balance, lockboxBalanceBefore + _value); + assertEq(address(optimismPortal2).balance, balanceBefore + _value); } } @@ -630,7 +512,6 @@ contract OptimismPortal2_DonateETH_Test is OptimismPortal2_TestInit { vm.deal(alice, _amount); uint256 preBalance = address(optimismPortal2).balance; - uint256 lockboxBalanceBefore = address(ethLockbox).balance; _amount = bound(_amount, 0, type(uint256).max - preBalance); vm.startStateDiffRecording(); @@ -639,8 +520,6 @@ contract OptimismPortal2_DonateETH_Test is OptimismPortal2_TestInit { assertEq(address(optimismPortal2).balance, preBalance + _amount); - assertEq(address(ethLockbox).balance, lockboxBalanceBefore); - // 0 for extcodesize of proxy before being called by this test, // 1 for the call to the proxy by the pranked address // 2 for the delegate call to the impl by the proxy @@ -691,17 +570,6 @@ contract OptimismPortal2_ProveWithdrawalTransaction_Test is OptimismPortal2_Test _outputRootProof: _outputRootProof, _withdrawalProof: _withdrawalProof }); - - if (isUsingLockbox()) { - _defaultTx.target = address(ethLockbox); - vm.expectRevert(IOptimismPortal.OptimismPortal_BadTarget.selector); - optimismPortal2.proveWithdrawalTransaction({ - _tx: _defaultTx, - _disputeGameIndex: _proposedGameIndex, - _outputRootProof: _outputRootProof, - _withdrawalProof: _withdrawalProof - }); - } } /// @notice Tests that `proveWithdrawalTransaction` reverts when the current timestamp is less @@ -887,17 +755,11 @@ contract OptimismPortal2_ProveWithdrawalTransaction_Test is OptimismPortal2_Test /// @notice Test contract for OptimismPortal2 `finalizeWithdrawalTransaction` function. contract OptimismPortal2_FinalizeWithdrawalTransaction_Test is OptimismPortal2_TestInit { /// @notice Tests that `finalizeWithdrawalTransaction` reverts when the target is the portal - /// contract or the lockbox. + /// contract. function test_finalizeWithdrawalTransaction_badTarget_reverts() external { _defaultTx.target = address(optimismPortal2); vm.expectRevert(IOptimismPortal.OptimismPortal_BadTarget.selector); optimismPortal2.finalizeWithdrawalTransaction(_defaultTx); - - if (isUsingLockbox()) { - _defaultTx.target = address(ethLockbox); - vm.expectRevert(IOptimismPortal.OptimismPortal_BadTarget.selector); - optimismPortal2.finalizeWithdrawalTransaction(_defaultTx); - } } /// @notice Tests that `finalizeWithdrawalTransaction` reverts if the target reverts and caller @@ -946,9 +808,6 @@ contract OptimismPortal2_FinalizeWithdrawalTransaction_Test is OptimismPortal2_T vm.warp(gameNoData.expectedResolution().raw() + 1 seconds); vm.deal(address(optimismPortal2), 0xFFFFFFFF); - if (isUsingLockbox()) { - vm.deal(address(ethLockbox), 0xFFFFFFFF); - } uint256 bobBalanceBefore = bob.balance; @@ -1104,53 +963,20 @@ contract OptimismPortal2_FinalizeWithdrawalTransaction_Test is OptimismPortal2_T /// @notice Tests that `finalizeWithdrawalTransaction` reverts if the target reverts. function test_finalizeWithdrawalTransaction_targetFails_fails() external { - if (isSysFeatureEnabled(Features.ETH_LOCKBOX)) { - vm.deal(address(optimismPortal2), 0); // no balance - } - - uint256 bobBalanceBefore = address(bob).balance; - vm.etch(bob, hex"fe"); // Contract with just the invalid opcode. - - _proveDefaultWithdrawal(); - - _resolveGameAndWarpPastProofMaturity(game); - vm.expectEmit(true, true, true, true); - emit WithdrawalFinalized(_withdrawalHash, false); - optimismPortal2.finalizeWithdrawalTransaction(_defaultTx); - - // Bob's balance should not have changed. - assertEq(address(bob).balance, bobBalanceBefore); - - if (isSysFeatureEnabled(Features.ETH_LOCKBOX)) { - // OptimismPortal2 should not have any stuck ETH. - assertEq(address(optimismPortal2).balance, 0); - } - } - - /// @notice Tests that `finalizeWithdrawalTransaction` reverts if the target reverts when - /// using the ETHLockbox. - function test_finalizeWithdrawalTransaction_lockboxAndTargetFails_fails() external { - // Enable the ETHLockbox. - address dummyLockbox = address(0xdeadbeef); - forceEnableLockbox(dummyLockbox); - vm.deal(address(dummyLockbox), 0xFFFFFFFF); - vm.deal(address(optimismPortal2), _defaultTx.value); - uint256 bobBalanceBefore = address(bob).balance; vm.etch(bob, hex"fe"); // Contract with just the invalid opcode. _proveDefaultWithdrawal(); _resolveGameAndWarpPastProofMaturity(game); + uint256 portalBalanceBefore = address(optimismPortal2).balance; vm.expectEmit(true, true, true, true); emit WithdrawalFinalized(_withdrawalHash, false); optimismPortal2.finalizeWithdrawalTransaction(_defaultTx); // Bob's balance should not have changed. assertEq(address(bob).balance, bobBalanceBefore); - - // OptimismPortal2 should not have any stuck ETH. - assertEq(address(optimismPortal2).balance, 0); + assertEq(address(optimismPortal2).balance, portalBalanceBefore); } /// @notice Tests that `finalizeWithdrawalTransaction` reverts if the withdrawal has already @@ -1639,13 +1465,8 @@ contract OptimismPortal2_DepositTransaction_Test is OptimismPortal2_TestInit { external { // Prevent overflow on an upgrade context - // Since the value always goes through the portal _mint = bound(_mint, 0, type(uint256).max - address(optimismPortal2).balance); - if (isUsingLockbox() && address(optimismPortal2).balance > address(ethLockbox).balance) { - _mint = bound(_mint, 0, type(uint256).max - address(ethLockbox).balance); - } - _gasLimit = uint64( bound( _gasLimit, @@ -1656,7 +1477,6 @@ contract OptimismPortal2_DepositTransaction_Test is OptimismPortal2_TestInit { if (_isCreation) _to = address(0); uint256 balanceBefore = address(optimismPortal2).balance; - uint256 lockboxBalanceBefore = address(ethLockbox).balance; // EOA emulation vm.expectEmit(address(optimismPortal2)); @@ -1670,23 +1490,13 @@ contract OptimismPortal2_DepositTransaction_Test is OptimismPortal2_TestInit { _data: _data }); - if (isSysFeatureEnabled(Features.ETH_LOCKBOX)) { - // Expect call to the ETHLockbox to lock the funds only if the value is greater than 0. - vm.expectCall(address(ethLockbox), _mint, abi.encodeCall(ethLockbox.lockETH, ()), _mint > 0 ? 1 : 0); - } - vm.deal(depositor, _mint); vm.prank(depositor, depositor); optimismPortal2.depositTransaction{ value: _mint }({ _to: _to, _value: _value, _gasLimit: _gasLimit, _isCreation: _isCreation, _data: _data }); - if (isSysFeatureEnabled(Features.ETH_LOCKBOX)) { - assertEq(address(optimismPortal2).balance, balanceBefore); - assertEq(address(ethLockbox).balance, lockboxBalanceBefore + _mint); - } else { - assertEq(address(optimismPortal2).balance, balanceBefore + _mint); - } + assertEq(address(optimismPortal2).balance, balanceBefore + _mint); } /// @notice Tests that `depositTransaction` succeeds for an EOA using 7702 delegation. @@ -1705,7 +1515,7 @@ contract OptimismPortal2_DepositTransaction_Test is OptimismPortal2_TestInit { vm.assume(_7702Target != address(0)); // Prevent overflow on an upgrade context - _mint = bound(_mint, 0, type(uint256).max - address(ethLockbox).balance); + _mint = bound(_mint, 0, type(uint256).max - address(optimismPortal2).balance); _gasLimit = uint64( bound( @@ -1717,8 +1527,6 @@ contract OptimismPortal2_DepositTransaction_Test is OptimismPortal2_TestInit { if (_isCreation) _to = address(0); uint256 portalBalanceBefore = address(optimismPortal2).balance; - uint256 lockboxBalanceBefore = address(ethLockbox).balance; - _mint = bound(_mint, 0, type(uint256).max - portalBalanceBefore); // EOA emulation vm.expectEmit(address(optimismPortal2)); @@ -1741,12 +1549,7 @@ contract OptimismPortal2_DepositTransaction_Test is OptimismPortal2_TestInit { _to: _to, _value: _value, _gasLimit: _gasLimit, _isCreation: _isCreation, _data: _data }); - if (isSysFeatureEnabled(Features.ETH_LOCKBOX)) { - assertEq(address(optimismPortal2).balance, portalBalanceBefore); - assertEq(address(ethLockbox).balance, lockboxBalanceBefore + _mint); - } else { - assertEq(address(optimismPortal2).balance, portalBalanceBefore + _mint); - } + assertEq(address(optimismPortal2).balance, portalBalanceBefore + _mint); } /// @notice Tests that `depositTransaction` succeeds for a contract. @@ -1761,7 +1564,7 @@ contract OptimismPortal2_DepositTransaction_Test is OptimismPortal2_TestInit { external { // Prevent overflow on an upgrade context - _mint = bound(_mint, 0, type(uint256).max - address(ethLockbox).balance); + _mint = bound(_mint, 0, type(uint256).max - address(optimismPortal2).balance); _gasLimit = uint64( bound( _gasLimit, @@ -1772,8 +1575,6 @@ contract OptimismPortal2_DepositTransaction_Test is OptimismPortal2_TestInit { if (_isCreation) _to = address(0); uint256 balanceBefore = address(optimismPortal2).balance; - uint256 lockboxBalanceBefore = address(ethLockbox).balance; - _mint = bound(_mint, 0, type(uint256).max - balanceBefore); vm.expectEmit(address(optimismPortal2)); emitTransactionDeposited({ @@ -1786,23 +1587,13 @@ contract OptimismPortal2_DepositTransaction_Test is OptimismPortal2_TestInit { _data: _data }); - if (isSysFeatureEnabled(Features.ETH_LOCKBOX)) { - // Expect call to the ETHLockbox to lock the funds only if the value is greater than 0. - vm.expectCall(address(ethLockbox), _mint, abi.encodeCall(ethLockbox.lockETH, ()), _mint > 0 ? 1 : 0); - } - vm.deal(address(this), _mint); vm.prank(address(this)); optimismPortal2.depositTransaction{ value: _mint }({ _to: _to, _value: _value, _gasLimit: _gasLimit, _isCreation: _isCreation, _data: _data }); - if (isSysFeatureEnabled(Features.ETH_LOCKBOX)) { - assertEq(address(optimismPortal2).balance, balanceBefore); - assertEq(address(ethLockbox).balance, lockboxBalanceBefore + _mint); - } else { - assertEq(address(optimismPortal2).balance, balanceBefore + _mint); - } + assertEq(address(optimismPortal2).balance, balanceBefore + _mint); } } diff --git a/test/L1/SystemConfig.t.sol b/test/L1/SystemConfig.t.sol index 796edaedf..30d8cbab1 100644 --- a/test/L1/SystemConfig.t.sol +++ b/test/L1/SystemConfig.t.sol @@ -5,12 +5,10 @@ pragma solidity 0.8.15; import { CommonTest } from "test/setup/CommonTest.sol"; // Scripts -import { ForgeArtifacts, StorageSlot } from "scripts/libraries/ForgeArtifacts.sol"; // Libraries import { Constants } from "src/libraries/Constants.sol"; import { EIP1967Helper } from "test/mocks/EIP1967Helper.sol"; -import { Features } from "src/libraries/Features.sol"; // Interfaces import { IResourceMetering } from "interfaces/L1/IResourceMetering.sol"; @@ -563,11 +561,8 @@ contract SystemConfig_Paused_Test is SystemConfig_TestInit { assertTrue(systemConfig.paused()); } - /// @notice Tests that `paused()` returns true when OptimismPortal identifier is paused and - /// the ETH_LOCKBOX feature is disabled. + /// @notice Tests that `paused()` returns true when the OptimismPortal identifier is paused. function test_paused_optimismPortalIdentifier_succeeds() external { - skipIfSysFeatureEnabled(Features.ETH_LOCKBOX); - // Initially not paused assertFalse(systemConfig.paused()); @@ -579,22 +574,6 @@ contract SystemConfig_Paused_Test is SystemConfig_TestInit { assertTrue(systemConfig.paused()); } - /// @notice Tests that `paused()` returns true when ETHLockbox identifier is paused and - /// ETH_LOCKBOX feature is enabled. - function test_paused_ethLockboxIdentifier_succeeds() external { - skipIfSysFeatureDisabled(Features.ETH_LOCKBOX); - - // Initially not paused - assertFalse(systemConfig.paused()); - - // Pause the system with ETHLockbox identifier - vm.prank(superchainConfig.guardian()); - superchainConfig.pause(address(ethLockbox)); - - // Verify paused state - assertTrue(systemConfig.paused()); - } - /// @notice Tests that `paused()` returns true when both pauses are active. function test_paused_bothPausesActive_succeeds() external { assertFalse(systemConfig.paused()); @@ -614,7 +593,6 @@ contract SystemConfig_Paused_Test is SystemConfig_TestInit { function testFuzz_paused_otherAddress_succeeds(address _address) external { vm.assume(_address != address(0)); vm.assume(_address != address(optimismPortal2)); - vm.assume(_address != address(ethLockbox)); // Initially not paused assertFalse(systemConfig.paused()); @@ -633,15 +611,6 @@ contract SystemConfig_Paused_Test is SystemConfig_TestInit { contract SystemConfig_SetFeature_Test is SystemConfig_TestInit { event FeatureSet(bytes32 indexed feature, bool indexed enabled); - function _enableEthLockboxIfDisabled(address proxyAdmin) internal { - if (!systemConfig.isFeatureEnabled(Features.ETH_LOCKBOX)) { - vm.prank(proxyAdmin); - systemConfig.setFeature(Features.ETH_LOCKBOX, true); - } - - assertTrue(systemConfig.isFeatureEnabled(Features.ETH_LOCKBOX)); - } - /// @notice Tests that `setFeature` reverts if the caller is not ProxyAdmin or ProxyAdmin owner. /// @param _sender The address to test. function testFuzz_setFeature_notProxyAdminOrProxyAdminOwner_reverts(address _sender) external { @@ -733,55 +702,6 @@ contract SystemConfig_SetFeature_Test is SystemConfig_TestInit { vm.expectRevert(ISystemConfig.SystemConfig_InvalidFeatureState.selector); systemConfig.setFeature(EXAMPLE_FEATURE, false); } - - /// @notice Tests that disabling ETH_LOCKBOX reverts if the OptimismPortal has a non-zero - /// ETHLockbox configured. - function test_setFeature_ethLockboxDisableWhileConfigured_reverts() external { - address proxyAdmin = address(systemConfig.proxyAdmin()); - - _enableEthLockboxIfDisabled(proxyAdmin); - - // Force the portal to have a configured ETHLockbox address. - StorageSlot memory slot = ForgeArtifacts.getSlot("OptimismPortal2", "ethLockbox"); - vm.store(address(optimismPortal2), bytes32(slot.slot), bytes32(uint256(uint160(address(1))))); - - // Disabling should revert due to safety check while lockbox is configured. - vm.expectRevert(ISystemConfig.SystemConfig_InvalidFeatureState.selector); - vm.prank(proxyAdmin); - systemConfig.setFeature(Features.ETH_LOCKBOX, false); - } - - /// @notice Tests that enabling ETH_LOCKBOX while the system is paused (global) reverts. - function test_setFeature_ethLockboxEnableWhilePaused_reverts() external { - address proxyAdmin = address(systemConfig.proxyAdmin()); - - _enableEthLockboxIfDisabled(proxyAdmin); - - // Pause globally. - vm.prank(superchainConfig.guardian()); - superchainConfig.pause(address(0)); - - // Enabling while paused should revert. - vm.expectRevert(ISystemConfig.SystemConfig_InvalidFeatureState.selector); - vm.prank(proxyAdmin); - systemConfig.setFeature(Features.ETH_LOCKBOX, true); - } - - /// @notice Tests that disabling ETH_LOCKBOX while the system is paused (global) reverts. - function test_setFeature_ethLockboxDisableWhilePaused_reverts() external { - address proxyAdmin = address(systemConfig.proxyAdmin()); - - _enableEthLockboxIfDisabled(proxyAdmin); - - // Pause globally. - vm.prank(superchainConfig.guardian()); - superchainConfig.pause(address(0)); - - // Disabling while paused should revert. - vm.expectRevert(ISystemConfig.SystemConfig_InvalidFeatureState.selector); - vm.prank(proxyAdmin); - systemConfig.setFeature(Features.ETH_LOCKBOX, false); - } } /// @title SystemConfig_IsFeatureEnabled_Test diff --git a/test/deploy/SystemDeploy.t.sol b/test/deploy/SystemDeploy.t.sol index 5011d14f3..b7020e413 100644 --- a/test/deploy/SystemDeploy.t.sol +++ b/test/deploy/SystemDeploy.t.sol @@ -119,7 +119,6 @@ contract SystemDeploy_Test is Test, SystemDeployAssertions { assertNotEq(address(output.opChain.opChainProxyAdmin), address(0), "proxy admin"); assertNotEq(address(output.opChain.systemConfigProxy), address(0), "system config"); assertNotEq(address(output.opChain.optimismPortalProxy), address(0), "portal"); - assertNotEq(address(output.opChain.ethLockboxProxy), address(0), "lockbox"); assertNotEq(address(output.opChain.delayedWETHProxy), address(0), "delayed weth"); assertNotEq(address(output.opChain.protocolVersionsProxy), address(0), "protocol versions"); assertEq( @@ -338,7 +337,6 @@ contract SystemDeploy_Test is Test, SystemDeployAssertions { superchainConfig: _output.superchain.superchainConfigProxy, implementations: _output.impls, delayedWETH: _output.opChain.delayedWETHProxy, - ethLockbox: _output.opChain.ethLockboxProxy, proxyAdminOwner: _input.opChainInput.roles.opChainProxyAdminOwner, multiproofGameType: GameType.wrap(uint32(_input.implementationsInput.multiproofGameType)), teeImageHash: _input.implementationsInput.teeImageHash, diff --git a/test/deploy/SystemDeployAssertions.sol b/test/deploy/SystemDeployAssertions.sol index fb3aa1c1e..7392dba36 100644 --- a/test/deploy/SystemDeployAssertions.sol +++ b/test/deploy/SystemDeployAssertions.sol @@ -6,7 +6,6 @@ import { Test } from "lib/forge-std/src/Test.sol"; import { Types } from "scripts/libraries/Types.sol"; import { Constants } from "src/libraries/Constants.sol"; -import { Features } from "src/libraries/Features.sol"; import { Predeploys } from "src/libraries/Predeploys.sol"; import { GameType, Hash } from "src/libraries/bridge/Types.sol"; import { Claim } from "src/libraries/bridge/LibUDT.sol"; @@ -24,7 +23,6 @@ import { IProxyAdminOwnedBase } from "interfaces/L1/IProxyAdminOwnedBase.sol"; import { IResourceMetering } from "interfaces/L1/IResourceMetering.sol"; import { ISuperchainConfig } from "interfaces/L1/ISuperchainConfig.sol"; import { ISystemConfig } from "interfaces/L1/ISystemConfig.sol"; -import { IETHLockbox } from "interfaces/L1/IETHLockbox.sol"; import { IOptimismMintableERC20Factory } from "interfaces/universal/IOptimismMintableERC20Factory.sol"; import { IProxyAdmin } from "interfaces/universal/IProxyAdmin.sol"; import { ISemver } from "interfaces/universal/ISemver.sol"; @@ -36,7 +34,6 @@ abstract contract SystemDeployAssertions is Test { ISuperchainConfig superchainConfig; Types.Implementations implementations; IDelayedWETH delayedWETH; - IETHLockbox ethLockbox; address proxyAdminOwner; GameType multiproofGameType; bytes32 teeImageHash; @@ -58,7 +55,6 @@ abstract contract SystemDeployAssertions is Test { _assertBridgeAndPortalWiring(_expected, proxyAdmin); _assertDisputeGameFactory(_expected, proxyAdmin); _assertGame(_expected, proxyAdmin, _expected.multiproofGameType); - _assertETHLockbox(_expected, proxyAdmin); } function _assertSuperchainConfig(ExpectedSystemDeployState memory _expected) private view { @@ -300,24 +296,6 @@ abstract contract SystemDeployAssertions is Test { assertGt(_asr.retirementTimestamp(), 0, "AV-ANCHORP-60"); } - function _assertETHLockbox(ExpectedSystemDeployState memory _expected, IProxyAdmin _proxyAdmin) private view { - IOptimismPortal2 portal = IOptimismPortal2(payable(_expected.systemConfig.optimismPortal())); - IETHLockbox lockbox = _expected.ethLockbox; - - assertNotEq(address(lockbox), address(0), "LOCKBOX-05"); - assertEq(_version(address(lockbox)), _version(_expected.implementations.ethLockboxImpl), "LOCKBOX-10"); - assertEq( - _proxyAdmin.getProxyImplementation(address(lockbox)), _expected.implementations.ethLockboxImpl, "LOCKBOX-20" - ); - assertEq(address(_proxyAdminFor(address(lockbox))), address(_proxyAdmin), "LOCKBOX-30"); - assertEq(address(lockbox.systemConfig()), address(_expected.systemConfig), "LOCKBOX-40"); - assertTrue(lockbox.authorizedPortals(portal), "LOCKBOX-50"); - - if (_expected.systemConfig.isFeatureEnabled(Features.ETH_LOCKBOX)) { - assertEq(address(portal.ethLockbox()), address(lockbox), "LOCKBOX-60"); - } - } - function _proxyAdminFor(address _contract) private view returns (IProxyAdmin) { return IProxyAdminOwnedBase(_contract).proxyAdmin(); } diff --git a/test/setup/ForkLive.s.sol b/test/setup/ForkLive.s.sol index e606b5f64..6b58d140f 100644 --- a/test/setup/ForkLive.s.sol +++ b/test/setup/ForkLive.s.sol @@ -23,8 +23,6 @@ import { IAddressManager } from "interfaces/legacy/IAddressManager.sol"; import { ISystemConfig } from "interfaces/L1/ISystemConfig.sol"; import { IProtocolVersions } from "interfaces/L1/IProtocolVersions.sol"; import { IProxyAdmin } from "interfaces/universal/IProxyAdmin.sol"; -import { IETHLockbox } from "interfaces/L1/IETHLockbox.sol"; -import { IOptimismPortal2 } from "interfaces/L1/IOptimismPortal2.sol"; /// @title ForkLive /// @notice This script is called by Setup.sol as a preparation step for the foundry test suite, and is run as an @@ -143,15 +141,6 @@ contract ForkLive is Script { artifacts.save("OptimismPortalProxy", optimismPortal); artifacts.save("OptimismPortal2Impl", EIP1967Helper.getImplementation(optimismPortal)); - // Get the lockbox address from the portal, and save it - /// NOTE: Using try catch because this function could be called before or after the upgrade. - try IOptimismPortal2(payable(optimismPortal)).ethLockbox() returns (IETHLockbox ethLockbox_) { - console.log("ForkLive: ETHLockboxProxy found: %s", address(ethLockbox_)); - artifacts.save("ETHLockboxProxy", address(ethLockbox_)); - } catch { - console.log("ForkLive: ETHLockboxProxy not found"); - } - address l1CrossDomainMessenger = systemConfigAddresses.l1CrossDomainMessenger; address addressManager = _legacyAddressManager(l1CrossDomainMessenger); artifacts.save("AddressManager", addressManager); @@ -242,10 +231,6 @@ contract ForkLive is Script { IAggregateVerifier(address(disputeGameFactory.gameImpls(GameTypes.AGGREGATE_VERIFIER))); artifacts.save("AggregateVerifier", address(aggregateVerifier)); - IOptimismPortal2 portal = IOptimismPortal2(artifacts.mustGetAddress("OptimismPortalProxy")); - address lockboxAddress = address(portal.ethLockbox()); - artifacts.save("ETHLockboxProxy", lockboxAddress); - GameAddresses memory gameAddresses = _aggregateVerifierAddresses(aggregateVerifier); artifacts.save("AnchorStateRegistryProxy", gameAddresses.anchorStateRegistry); artifacts.save("DelayedWETHProxy", gameAddresses.weth); diff --git a/test/setup/Setup.sol b/test/setup/Setup.sol index df4a6d05e..7ee901cb4 100644 --- a/test/setup/Setup.sol +++ b/test/setup/Setup.sol @@ -25,7 +25,6 @@ import { AddressAliasHelper } from "src/vendor/AddressAliasHelper.sol"; // Interfaces import { IOptimismPortal2 as IOptimismPortal } from "interfaces/L1/IOptimismPortal2.sol"; -import { IETHLockbox } from "interfaces/L1/IETHLockbox.sol"; import { IL1CrossDomainMessenger } from "interfaces/L1/IL1CrossDomainMessenger.sol"; import { ISystemConfig } from "interfaces/L1/ISystemConfig.sol"; import { ISuperchainConfig } from "interfaces/L1/ISuperchainConfig.sol"; @@ -98,7 +97,6 @@ abstract contract Setup is FeatureFlags { address superchainProxyAdminOwner; IProxyAdmin superchainProxyAdmin; IOptimismPortal optimismPortal2; - IETHLockbox ethLockbox; ISystemConfig systemConfig; IL1StandardBridge l1StandardBridge; IL1CrossDomainMessenger l1CrossDomainMessenger; @@ -220,15 +218,6 @@ abstract contract Setup is FeatureFlags { optimismPortal2 = IOptimismPortal(artifacts.mustGetAddress("OptimismPortalProxy")); - // Only skip ETHLockbox assignment if we're in a fork test with non-upgraded fork - // TODO(#14691): Remove this check once Upgrade 15 is deployed on Mainnet. - if (!forkTest || deploy.cfg().useUpgradedFork()) { - // Here we use getAddress instead of mustGetAddress because some chains might not have - // the ETHLockbox proxy. Chains that don't have the ETHLockbox proxy will just return - // address(0) and cause a revert if we use mustGetAddress. - ethLockbox = IETHLockbox(artifacts.getAddress("ETHLockboxProxy")); - } - systemConfig = ISystemConfig(artifacts.mustGetAddress("SystemConfigProxy")); l1StandardBridge = IL1StandardBridge(artifacts.mustGetAddress("L1StandardBridgeProxy")); l1CrossDomainMessenger = IL1CrossDomainMessenger(artifacts.mustGetAddress("L1CrossDomainMessengerProxy")); diff --git a/test/vendor/Initializable.t.sol b/test/vendor/Initializable.t.sol index b1c762141..9adb1e132 100644 --- a/test/vendor/Initializable.t.sol +++ b/test/vendor/Initializable.t.sol @@ -18,7 +18,6 @@ import { ISuperchainConfig } from "interfaces/L1/ISuperchainConfig.sol"; import { IProtocolVersions } from "interfaces/L1/IProtocolVersions.sol"; import { IResourceMetering } from "interfaces/L1/IResourceMetering.sol"; import { IDisputeGameFactory } from "interfaces/L1/proofs/IDisputeGameFactory.sol"; -import { IOptimismPortal2 } from "interfaces/L1/IOptimismPortal2.sol"; import { TEEProverRegistry } from "src/L1/proofs/tee/TEEProverRegistry.sol"; /// @title Initializer_Test @@ -229,24 +228,6 @@ contract Initializer_Test is CommonTest { ); } - // ETHLockbox is only deployed when interop is enabled - if (address(ethLockbox) != address(0)) { - initCalldata = abi.encodeCall(ethLockbox.initialize, (ISystemConfig(address(0)), new IOptimismPortal2[](0))); - contracts.push( - InitializeableContract({ - name: "ETHLockboxImpl", - target: EIP1967Helper.getImplementation(address(ethLockbox)), - initCalldata: initCalldata - }) - ); - - contracts.push( - InitializeableContract({ - name: "ETHLockboxProxy", target: address(ethLockbox), initCalldata: initCalldata - }) - ); - } - // AggregateVerifier uses a custom `bool initialized` instead of OpenZeppelin's `_initialized` // uint8, so it cannot be tested by this framework. It is excluded below. @@ -279,10 +260,6 @@ contract Initializer_Test is CommonTest { excludes[j++] = "src/L1/BalanceTracker.sol"; // AggregateVerifier uses a custom `bool initialized` instead of OpenZeppelin's `_initialized` uint8. excludes[j++] = "src/L1/proofs/AggregateVerifier.sol"; - // ETHLockbox is only deployed when interop is enabled. - if (address(ethLockbox) == address(0)) { - excludes[j++] = "src/L1/ETHLockbox.sol"; - } // ProtocolVersions is not deployed on older forked chains. if (address(protocolVersions) == address(0)) { excludes[j++] = "src/L1/ProtocolVersions.sol";