diff --git a/packages/contracts-bedrock/interfaces/L1/IOptimismPortal2.sol b/packages/contracts-bedrock/interfaces/L1/IOptimismPortal2.sol index dba9b1bd927b8..eb73b2956cc24 100644 --- a/packages/contracts-bedrock/interfaces/L1/IOptimismPortal2.sol +++ b/packages/contracts-bedrock/interfaces/L1/IOptimismPortal2.sol @@ -121,7 +121,7 @@ interface IOptimismPortal2 is IProxyAdminOwnedBase { function respectedGameTypeUpdatedAt() external view returns (uint64); function superRootsActive() external view returns (bool); function systemConfig() external view returns (ISystemConfig); - function upgrade(IAnchorStateRegistry _anchorStateRegistry, IETHLockbox _ethLockbox, ISystemConfig _systemConfig) external; + function upgrade(IAnchorStateRegistry _anchorStateRegistry, IETHLockbox _ethLockbox) external; function version() external pure returns (string memory); function migrateLiquidity() external; diff --git a/packages/contracts-bedrock/interfaces/L1/ISuperchainConfig.sol b/packages/contracts-bedrock/interfaces/L1/ISuperchainConfig.sol index 0c58015092089..df11d6c5cfa7c 100644 --- a/packages/contracts-bedrock/interfaces/L1/ISuperchainConfig.sol +++ b/packages/contracts-bedrock/interfaces/L1/ISuperchainConfig.sol @@ -15,6 +15,7 @@ interface ISuperchainConfig is IProxyAdminOwnedBase { error SuperchainConfig_OnlyGuardian(); error SuperchainConfig_AlreadyPaused(address identifier); + error SuperchainConfig_NotAlreadyPaused(address identifier); error ReinitializableBase_ZeroInitVersion(); function guardian() external view returns (address); diff --git a/packages/contracts-bedrock/snapshots/abi/OptimismPortal2.json b/packages/contracts-bedrock/snapshots/abi/OptimismPortal2.json index e80a8914b2f86..39d02adf5ec39 100644 --- a/packages/contracts-bedrock/snapshots/abi/OptimismPortal2.json +++ b/packages/contracts-bedrock/snapshots/abi/OptimismPortal2.json @@ -789,11 +789,6 @@ "internalType": "contract IETHLockbox", "name": "_ethLockbox", "type": "address" - }, - { - "internalType": "contract ISystemConfig", - "name": "_systemConfig", - "type": "address" } ], "name": "upgrade", diff --git a/packages/contracts-bedrock/snapshots/abi/SuperchainConfig.json b/packages/contracts-bedrock/snapshots/abi/SuperchainConfig.json index b7e462bbae669..af7b20286b3eb 100644 --- a/packages/contracts-bedrock/snapshots/abi/SuperchainConfig.json +++ b/packages/contracts-bedrock/snapshots/abi/SuperchainConfig.json @@ -321,6 +321,17 @@ "name": "SuperchainConfig_AlreadyPaused", "type": "error" }, + { + "inputs": [ + { + "internalType": "address", + "name": "identifier", + "type": "address" + } + ], + "name": "SuperchainConfig_NotAlreadyPaused", + "type": "error" + }, { "inputs": [], "name": "SuperchainConfig_OnlyGuardian", diff --git a/packages/contracts-bedrock/snapshots/semver-lock.json b/packages/contracts-bedrock/snapshots/semver-lock.json index 541a397c1a22d..221429a407c6d 100644 --- a/packages/contracts-bedrock/snapshots/semver-lock.json +++ b/packages/contracts-bedrock/snapshots/semver-lock.json @@ -8,36 +8,36 @@ "sourceCodeHash": "0x6c9d3e2dee44c234d59ab93b6564536dfd807f1c4a02a82d5393bc53cb15b8b7" }, "src/L1/L1CrossDomainMessenger.sol:L1CrossDomainMessenger": { - "initCodeHash": "0x08cddeb55338b0783cd4dc75d7c62ff2fc02589c47bd5e4ed1de08cb58d4decf", - "sourceCodeHash": "0xd1bb05ad9cdee4d62c6ec7e70a4679d95278c308e73cb4954ccdfee304fa9449" + "initCodeHash": "0x117e4126f2accbcd0c4de00b8d19f522e76396dd39145b4c2e2b4f9dfa1b03ef", + "sourceCodeHash": "0x66b6e4d41c40efcc50b644d22d736408e28a73a6b55b18fcbb89a83bd3230d53" }, "src/L1/L1ERC721Bridge.sol:L1ERC721Bridge": { - "initCodeHash": "0xf05ae41b99405e74ecb84ee39b74c5655eced21ab02af0b475615396060fc267", - "sourceCodeHash": "0xfcfc27b034cf2898d8795d60a410c66eba13ded52fc5f4ac71c2f9160903c36e" + "initCodeHash": "0xf1eaecec5e9c9c3d143bc9980d15e4671e97cb840f044bc2189a9d42ea7a1ef7", + "sourceCodeHash": "0x24e870fc3620d07ef9e336bd56e0df0604df69a2909c1aaf709f2c253ad16c78" }, "src/L1/L1StandardBridge.sol:L1StandardBridge": { - "initCodeHash": "0x65c7197a676d93990f6c26d5e2cc5188bda9161633223c339588ee293b298157", - "sourceCodeHash": "0x86d7db4348cc8d0329da82ffef0d9cde1666e47d1443aca6154d6e03a64a0854" + "initCodeHash": "0x11e28569436e16691f03820e0fd5252492706f5855b350439f695c7e4cd331c3", + "sourceCodeHash": "0x11b35ee81f797b30ee834e2ffad52686d2100d7ee139db4299b7d854dba25550" }, "src/L1/OPContractsManager.sol:OPContractsManager": { - "initCodeHash": "0xe3a452b02dfbd68c747788454e97244e52d46d1d1468dfa0590252824630ca58", - "sourceCodeHash": "0x8b806713bfd96b9a84b3b3738988fe47c187b0ebc69829d42f3d4394f15f64fb" + "initCodeHash": "0xab0bd49534411b7f57a20d897ea495dfea419421d4caec392371a4670bfa1ae3", + "sourceCodeHash": "0xafa3ebc3061831c4503664dff2e290934a2ba0ec7be354e1a11afcf9d130962e" }, "src/L1/OptimismPortal2.sol:OptimismPortal2": { - "initCodeHash": "0x8bcea71af244b56d0615f0ff63ce381549575a605422c634a1afd5c6bcb442bb", - "sourceCodeHash": "0x0f1ae4b7a9ec316ab12aacce38933ce23775a59249f8b433f152d6df7184cb56" + "initCodeHash": "0x6f0c9089e4cbe2360473c95235115a4eb31462cd06799cd7ff288fc85efdbf91", + "sourceCodeHash": "0x2fa10a8e28dac549ec29270aa0f3ddd8963873762fd8dcd19a0fc8bc14e8d1fe" }, "src/L1/ProtocolVersions.sol:ProtocolVersions": { "initCodeHash": "0x5a76c8530cb24cf23d3baacc6eefaac226382af13f1e2a35535d2ec2b0573b29", "sourceCodeHash": "0xb3e32b18c95d4940980333e1e99b4dcf42d8a8bfce78139db4dc3fb06e9349d0" }, "src/L1/SuperchainConfig.sol:SuperchainConfig": { - "initCodeHash": "0x29db031af64093ea8439b7a3cdeb30ae209ff34e946e026f1c7c4a969a07d9a9", - "sourceCodeHash": "0xa53695ff8493be42eb4b7cfdc50ed165fef293075df3c38a29e0695095299c78" + "initCodeHash": "0x5c6d81d8c998a4fbe2b97737584197930a54c0bfc198ef62eae049b163e893aa", + "sourceCodeHash": "0x2a2b109a66b0025f118b64e2a81ad88fd097ce3cb2adc370b5bb3d6bd2029ddd" }, "src/L1/SystemConfig.sol:SystemConfig": { - "initCodeHash": "0x8be717b2c665e36e0c619f072b9d3a80591d42d58f69815782d077a348a02f34", - "sourceCodeHash": "0x83add011a0950ab0b3aecb62b6b7a47bfaf3176a095f81491d1871b897023fc3" + "initCodeHash": "0x19e352bb99cab389e0eead6178746c49b14d061bea89c444f26e768f601bd6d8", + "sourceCodeHash": "0x5ab5f6a63e40b3c491167afa4a207daebc87286829e3cf07cc2a8ce04abe70e9" }, "src/L2/BaseFeeVault.sol:BaseFeeVault": { "initCodeHash": "0x9b664e3d84ad510091337b4aacaa494b142512e2f6f7fbcdb6210ed62ca9b885", @@ -140,24 +140,24 @@ "sourceCodeHash": "0x03c160168986ffc8d26a90c37366e7ad6da03f49d83449e1f8b3de0f4b590f6f" }, "src/dispute/AnchorStateRegistry.sol:AnchorStateRegistry": { - "initCodeHash": "0x963778e4ab7198b74d250ca3f08d928a614db7760372a9e889956ebfad520af2", - "sourceCodeHash": "0x944c0d0c611fcaeff3f93516a76d3380cc1d98c28f035e1eebfed22616b6c3e6" + "initCodeHash": "0x9bb9cd78ac1d15844fbff2e7c759f2c949f3aa1a8d950d54ddb4b1ed88863239", + "sourceCodeHash": "0xf2715ff5393244742428454e1661aae7a56433867ee7bc563f44ad572c492d82" }, "src/dispute/DelayedWETH.sol:DelayedWETH": { "initCodeHash": "0xa8f60e142108b33675a8f6b6979c73b96eea247884842d796f9f878904c0a906", "sourceCodeHash": "0xdebf2ab3af4d5549c40e9dd9db6b2458af286f323b6891f3b0c4e89f3c8928db" }, "src/dispute/DisputeGameFactory.sol:DisputeGameFactory": { - "initCodeHash": "0x7b7e20657b57920938f6bb97b2cb80c6e99a575394f0fb53818b5c162a8e97c9", - "sourceCodeHash": "0x4037e9b492045bd7c630aa20ae4687a32551965f96d579093670beece0ddce3b" + "initCodeHash": "0xa3e6a7466e16e6b7a8ce7a257ec543c1bf675e24f53565080d826404654b9262", + "sourceCodeHash": "0x1871aaeba0658f17270190cc95ffff172d92dca795d698401ec34a7462bf5242" }, "src/dispute/FaultDisputeGame.sol:FaultDisputeGame": { - "initCodeHash": "0x143ba465e9d37cf169430322b1f8e4fc3520968a8696ceb20c864a259ce899b1", - "sourceCodeHash": "0x55179ce163dacd3470fee90b203dc552b3f1e394c70c6c3e9c072f7d8cb119e1" + "initCodeHash": "0x9748700f873b6fe0599f9674a4c2dfbc9e35bbc918ebd2f7c54f709b1480df36", + "sourceCodeHash": "0xe6d4bdbfb05491164f203f1c5542a7ba961a20727a5b706b393f4f886ba5f901" }, "src/dispute/PermissionedDisputeGame.sol:PermissionedDisputeGame": { - "initCodeHash": "0x2a16858f30102c70a291edd92a27af30ff64b822254a1e83db3ec547ee7db3b5", - "sourceCodeHash": "0xa47741a34aaa6adec158032da19a98ca3dae696858ceede361c6dda9c6aa3f31" + "initCodeHash": "0x1018dcbe7714a80a33dd8ad09bcc533dc6cbe1e97d2a17d3780887d406fc46a8", + "sourceCodeHash": "0x09455fe79619e63a08244647dca734fa58e96352fe21aeb289cc467437389125" }, "src/dispute/SuperFaultDisputeGame.sol:SuperFaultDisputeGame": { "initCodeHash": "0x687bde7b8632b47dc16530cc523946e4109e023f0d32c9bf0281b51f412f0f0d", @@ -180,8 +180,8 @@ "sourceCodeHash": "0x62c9a6182d82692fb9c173ddb0d7978bcff2d1d4dc8cd2f10625e1e65bda6888" }, "src/safe/DeputyPauseModule.sol:DeputyPauseModule": { - "initCodeHash": "0xf5d06ae8d238d428216931738fd01cccb9933835347d174143f8bba069593b00", - "sourceCodeHash": "0x18a5c7268ce9f43e3a58681aec606c40ddfdc0d59a1668b93716517b0ce29361" + "initCodeHash": "0x4685af7d7c54b3bc5614afb735f34ae311d1d86d5112b9d28d931bc372b94ea8", + "sourceCodeHash": "0x2dc7c513be25e1350ae1caa71adad91a7cde91125540699ce83489dd772330ad" }, "src/safe/LivenessGuard.sol:LivenessGuard": { "initCodeHash": "0xc8e29e8b12f423c8cd229a38bc731240dd815d96f1b0ab96c71494dde63f6a81", diff --git a/packages/contracts-bedrock/src/L1/L1CrossDomainMessenger.sol b/packages/contracts-bedrock/src/L1/L1CrossDomainMessenger.sol index 4fd718d2fb5c4..cee1d3765eb8e 100644 --- a/packages/contracts-bedrock/src/L1/L1CrossDomainMessenger.sol +++ b/packages/contracts-bedrock/src/L1/L1CrossDomainMessenger.sol @@ -36,8 +36,8 @@ contract L1CrossDomainMessenger is CrossDomainMessenger, ProxyAdminOwnedBase, Re address private spacer_253_0_20; /// @notice Semantic version. - /// @custom:semver 2.8.0 - string public constant version = "2.8.0"; + /// @custom:semver 2.9.0 + string public constant version = "2.9.0"; /// @notice Contract of the SystemConfig. ISystemConfig public systemConfig; @@ -68,7 +68,6 @@ contract L1CrossDomainMessenger is CrossDomainMessenger, ProxyAdminOwnedBase, Re // Now perform upgrade logic. systemConfig = _systemConfig; - spacer_251_0_20 = address(0); } /// @inheritdoc CrossDomainMessenger diff --git a/packages/contracts-bedrock/src/L1/L1ERC721Bridge.sol b/packages/contracts-bedrock/src/L1/L1ERC721Bridge.sol index 8a72570cd846e..9fe0ce893331e 100644 --- a/packages/contracts-bedrock/src/L1/L1ERC721Bridge.sol +++ b/packages/contracts-bedrock/src/L1/L1ERC721Bridge.sol @@ -33,8 +33,8 @@ contract L1ERC721Bridge is ERC721Bridge, ProxyAdminOwnedBase, ReinitializableBas address private spacer_50_0_20; /// @notice Semantic version. - /// @custom:semver 2.6.0 - string public constant version = "2.6.0"; + /// @custom:semver 2.7.0 + string public constant version = "2.7.0"; /// @notice Address of the SystemConfig contract. ISystemConfig public systemConfig; @@ -70,7 +70,6 @@ contract L1ERC721Bridge is ERC721Bridge, ProxyAdminOwnedBase, ReinitializableBas // Now perform upgrade logic. systemConfig = _systemConfig; - spacer_50_0_20 = address(0); } /// @inheritdoc ERC721Bridge diff --git a/packages/contracts-bedrock/src/L1/L1StandardBridge.sol b/packages/contracts-bedrock/src/L1/L1StandardBridge.sol index fcc9c08632af1..504ba854fb5ba 100644 --- a/packages/contracts-bedrock/src/L1/L1StandardBridge.sol +++ b/packages/contracts-bedrock/src/L1/L1StandardBridge.sol @@ -77,8 +77,8 @@ contract L1StandardBridge is StandardBridge, ProxyAdminOwnedBase, Reinitializabl ); /// @notice Semantic version. - /// @custom:semver 2.5.0 - string public constant version = "2.5.0"; + /// @custom:semver 2.6.0 + string public constant version = "2.6.0"; /// @custom:legacy /// @custom:spacer superchainConfig @@ -127,7 +127,6 @@ contract L1StandardBridge is StandardBridge, ProxyAdminOwnedBase, Reinitializabl // Now perform upgrade logic. systemConfig = _systemConfig; - spacer_50_0_20 = address(0); } /// @inheritdoc StandardBridge diff --git a/packages/contracts-bedrock/src/L1/OPContractsManager.sol b/packages/contracts-bedrock/src/L1/OPContractsManager.sol index 7e3058a7454ce..f36516d6dd70d 100644 --- a/packages/contracts-bedrock/src/L1/OPContractsManager.sol +++ b/packages/contracts-bedrock/src/L1/OPContractsManager.sol @@ -672,7 +672,7 @@ contract OPContractsManagerUpgrader is OPContractsManagerBase { _l2ChainId: l2ChainId, _proxyAdmin: _opChainConfigs[i].proxyAdmin, _saltMixer: reusableSaltMixer(_opChainConfigs[i]), - _contractName: "AnchorStateRegistry-SOT" + _contractName: "AnchorStateRegistry-U16" }) ); @@ -714,13 +714,13 @@ contract OPContractsManagerUpgrader is OPContractsManagerBase { _l2ChainId: l2ChainId, _proxyAdmin: _opChainConfigs[i].proxyAdmin, _saltMixer: reusableSaltMixer(_opChainConfigs[i]), - _contractName: "ETHLockbox" + _contractName: "ETHLockbox-U16" }) ); // Upgrade the OptimismPortal contract first so that the SystemConfig will have // the SuperchainConfig reference required in the ETHLockbox. - optimismPortal.upgrade(newAnchorStateRegistryProxy, ethLockbox, _opChainConfigs[i].systemConfigProxy); + optimismPortal.upgrade(newAnchorStateRegistryProxy, ethLockbox); // Initialize the ETHLockbox setting the OptimismPortal as an authorized portal. IOptimismPortal[] memory portals = new IOptimismPortal[](1); @@ -1754,9 +1754,9 @@ contract OPContractsManager is ISemver { // -------- Constants and Variables -------- - /// @custom:semver 2.3.0 + /// @custom:semver 2.4.0 function version() public pure virtual returns (string memory) { - return "2.3.0"; + return "2.4.0"; } OPContractsManagerGameTypeAdder public immutable opcmGameTypeAdder; @@ -1917,7 +1917,8 @@ contract OPContractsManager is ISemver { _performDelegateCall(address(opcmGameTypeAdder), data); } - /// @notice Migrates the Optimism contracts to the latest version. This is a stub for now. + /// @notice Migrates the Optimism contracts to the latest version. + /// @param _input Input parameters for the migration. function migrate(OPContractsManagerInteropMigrator.MigrateInput calldata _input) external virtual { if (address(this) == address(thisOPCM)) revert OnlyDelegatecall(); diff --git a/packages/contracts-bedrock/src/L1/OptimismPortal2.sol b/packages/contracts-bedrock/src/L1/OptimismPortal2.sol index 01a395e363d7c..e68b022594299 100644 --- a/packages/contracts-bedrock/src/L1/OptimismPortal2.sol +++ b/packages/contracts-bedrock/src/L1/OptimismPortal2.sol @@ -231,16 +231,10 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase /// @notice Thrown when trying to migrate to the same AnchorStateRegistry. error OptimismPortal_MigratingToSameRegistry(); - /// @notice Reverts when paused. - modifier whenNotPaused() { - if (paused()) revert OptimismPortal_CallPaused(); - _; - } - /// @notice Semantic version. - /// @custom:semver 4.4.0 + /// @custom:semver 4.5.0 function version() public pure virtual returns (string memory) { - return "4.4.0"; + return "4.5.0"; } /// @param _proofMaturityDelaySeconds The proof maturity delay in seconds. @@ -281,11 +275,9 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase /// @notice Upgrades the OptimismPortal contract to have a reference to the AnchorStateRegistry and SystemConfig /// @param _anchorStateRegistry AnchorStateRegistry contract. /// @param _ethLockbox ETHLockbox contract. - /// @param _systemConfig SystemConfig contract. function upgrade( IAnchorStateRegistry _anchorStateRegistry, - IETHLockbox _ethLockbox, - ISystemConfig _systemConfig + IETHLockbox _ethLockbox ) external reinitializer(initVersion()) @@ -296,8 +288,6 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase // Now perform upgrade logic. anchorStateRegistry = _anchorStateRegistry; ethLockbox = _ethLockbox; - systemConfig = _systemConfig; - spacer_53_1_20 = address(0); } /// @notice Getter for the current paused status. @@ -317,13 +307,13 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase /// @notice Returns the SuperchainConfig contract. /// @return ISuperchainConfig The SuperchainConfig contract. - function superchainConfig() public view returns (ISuperchainConfig) { + function superchainConfig() external view returns (ISuperchainConfig) { return systemConfig.superchainConfig(); } /// @custom:legacy /// @notice Getter function for the address of the guardian. - function guardian() public view returns (address) { + function guardian() external view returns (address) { return systemConfig.guardian(); } @@ -403,6 +393,10 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase /// @param _newLockbox The address of the new ETHLockbox contract. /// @param _newAnchorStateRegistry The address of the new AnchorStateRegistry contract. function migrateToSuperRoots(IETHLockbox _newLockbox, IAnchorStateRegistry _newAnchorStateRegistry) external { + // Migration can only be triggered when the system is not paused because the migration can + // potentially unpause the system as a result of the modified ETHLockbox address. + _assertNotPaused(); + // Migration can only be triggered by the ProxyAdmin owner. _assertOnlyProxyAdminOwner(); @@ -450,8 +444,10 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase bytes[] calldata _withdrawalProof ) external - whenNotPaused { + // Cannot prove withdrawal transactions while the system is paused. + _assertNotPaused(); + // Make sure that the OptimismPortal is using Super Roots. if (!superRootsActive) { revert OptimismPortal_WrongProofMethod(); @@ -476,8 +472,10 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase bytes[] calldata _withdrawalProof ) external - whenNotPaused { + // Cannot prove withdrawal transactions while the system is paused. + _assertNotPaused(); + // Make sure that the OptimismPortal is using Output Roots. if (superRootsActive) { revert OptimismPortal_WrongProofMethod(); @@ -615,7 +613,7 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase /// @notice Finalizes a withdrawal transaction. /// @param _tx Withdrawal transaction to finalize. - function finalizeWithdrawalTransaction(Types.WithdrawalTransaction memory _tx) external whenNotPaused { + function finalizeWithdrawalTransaction(Types.WithdrawalTransaction memory _tx) external { finalizeWithdrawalTransactionExternalProof(_tx, msg.sender); } @@ -627,8 +625,10 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase address _proofSubmitter ) public - whenNotPaused { + // Cannot finalize withdrawal transactions while the system is paused. + _assertNotPaused(); + // Make sure that the l2Sender has not yet been set. The l2Sender is set to a value other // than the default value when a withdrawal transaction is being finalized. This check is // a defacto reentrancy guard. @@ -672,8 +672,9 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase // be achieved through contracts built on top of this contract emit WithdrawalFinalized(withdrawalHash, success); - // Send ETH back to the Lockbox or it'll get stuck here. - if (!success) { + // Send ETH back to the Lockbox or it'll get stuck here and would need to be moved back via + // the migrateLiquidity function. + if (!success && _tx.value > 0) { ethLockbox.lockETH{ value: _tx.value }(); } @@ -792,6 +793,13 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ReinitializableBase return proofSubmitters[_withdrawalHash].length; } + /// @notice Asserts that the contract is not paused. + function _assertNotPaused() internal view { + if (paused()) { + revert OptimismPortal_CallPaused(); + } + } + /// @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. diff --git a/packages/contracts-bedrock/src/L1/ProxyAdminOwnedBase.sol b/packages/contracts-bedrock/src/L1/ProxyAdminOwnedBase.sol index dd1ebe992c24f..b23ce019c367a 100644 --- a/packages/contracts-bedrock/src/L1/ProxyAdminOwnedBase.sol +++ b/packages/contracts-bedrock/src/L1/ProxyAdminOwnedBase.sol @@ -13,6 +13,10 @@ import { IAddressManager } from "interfaces/legacy/IAddressManager.sol"; /// compatible Proxy contracts so that their ProxyAdmin and ProxyAdmin owner addresses can /// be retrieved onchain. Existing Proxy contracts don't have these getters, so we need a /// base contract instead. +/// @dev WARNING: This contract is ONLY designed to be used with either the Optimism Proxy +/// implementation or the Optimism ResolvedDelegateProxy implementation. It is not safe to use +/// this contract with any other proxy implementation. +/// WARNING: Multiple OP Stack chains may share the same ProxyAdmin owner address. abstract contract ProxyAdminOwnedBase { /// @notice Thrown when the ProxyAdmin owner of the current contract is not the same as the /// ProxyAdmin owner of the other Proxy address provided. diff --git a/packages/contracts-bedrock/src/L1/StandardValidator.sol b/packages/contracts-bedrock/src/L1/StandardValidator.sol index e899b548c3854..891355aa8d9e1 100644 --- a/packages/contracts-bedrock/src/L1/StandardValidator.sol +++ b/packages/contracts-bedrock/src/L1/StandardValidator.sol @@ -122,23 +122,23 @@ contract StandardValidator { } function systemConfigVersion() public pure returns (string memory) { - return "3.2.0"; + return "3.3.0"; } function optimismPortalVersion() public pure returns (string memory) { - return "4.4.0"; + return "4.5.0"; } function l1CrossDomainMessengerVersion() public pure returns (string memory) { - return "2.8.0"; + return "2.9.0"; } function l1ERC721BridgeVersion() public pure returns (string memory) { - return "2.6.0"; + return "2.7.0"; } function l1StandardBridgeVersion() public pure returns (string memory) { - return "2.5.0"; + return "2.6.0"; } function mipsVersion() public pure returns (string memory) { @@ -150,11 +150,11 @@ contract StandardValidator { } function disputeGameFactoryVersion() public pure returns (string memory) { - return "1.1.0"; + return "1.2.0"; } function anchorStateRegistryVersion() public pure returns (string memory) { - return "3.4.0"; + return "3.5.0"; } function delayedWETHVersion() public pure returns (string memory) { @@ -162,7 +162,7 @@ contract StandardValidator { } function permissionedDisputeGameVersion() public pure returns (string memory) { - return "1.6.0"; + return "1.7.0"; } function preimageOracleVersion() public pure returns (string memory) { diff --git a/packages/contracts-bedrock/src/L1/SuperchainConfig.sol b/packages/contracts-bedrock/src/L1/SuperchainConfig.sol index 9d23d5bc99fb5..c3562e9ad4318 100644 --- a/packages/contracts-bedrock/src/L1/SuperchainConfig.sol +++ b/packages/contracts-bedrock/src/L1/SuperchainConfig.sol @@ -26,6 +26,9 @@ contract SuperchainConfig is ProxyAdminOwnedBase, Initializable, Reinitializable /// @notice Thrown when attempting to pause an identifier that is already paused error SuperchainConfig_AlreadyPaused(address identifier); + /// @notice Thrown when attempting to extend a pause that is not already paused. + error SuperchainConfig_NotAlreadyPaused(address identifier); + /// @notice Enum representing different types of updates. /// @custom:value GUARDIAN Represents an update to the guardian. enum UpdateType { @@ -56,8 +59,8 @@ contract SuperchainConfig is ProxyAdminOwnedBase, Initializable, Reinitializable event ConfigUpdate(UpdateType indexed updateType, bytes data); /// @notice Semantic version. - /// @custom:semver 2.1.0 - string public constant version = "2.1.0"; + /// @custom:semver 2.2.0 + string public constant version = "2.2.0"; /// @notice Constructs the SuperchainConfig contract. constructor() ReinitializableBase(2) { @@ -81,6 +84,9 @@ contract SuperchainConfig is ProxyAdminOwnedBase, Initializable, Reinitializable // Now perform upgrade logic. // Transfer the guardian into the new variable and clear the old storage slot. + // We generally do not clear out old storage slots but in the case of the SuperchainConfig + // these are the only spacer slots, they aren't cleanly represented by spacer variables, + // and we can get rid of them now and never think about them again later. bytes32 guardianSlot = bytes32(uint256(keccak256("superchainConfig.guardian")) - 1); _setGuardian(Storage.getAddress(guardianSlot)); Storage.setBytes32(guardianSlot, bytes32(0)); @@ -103,9 +109,7 @@ contract SuperchainConfig is ProxyAdminOwnedBase, Initializable, Reinitializable /// @param _identifier The address identifier for the pause. function pause(address _identifier) external { // Only the Guardian can pause the system. - if (msg.sender != guardian) { - revert SuperchainConfig_OnlyGuardian(); - } + _assertOnlyGuardian(); // Cannot pause if the identifier is already paused to prevent re-pausing without either // unpausing, extending, or resetting the pause timestamp. @@ -122,9 +126,7 @@ contract SuperchainConfig is ProxyAdminOwnedBase, Initializable, Reinitializable /// @param _identifier The address identifier to unpause. function unpause(address _identifier) external { // Only the Guardian can unpause the system. - if (msg.sender != guardian) { - revert SuperchainConfig_OnlyGuardian(); - } + _assertOnlyGuardian(); // Unpause the system. pauseTimestamps[_identifier] = 0; @@ -135,8 +137,11 @@ contract SuperchainConfig is ProxyAdminOwnedBase, Initializable, Reinitializable /// @param _identifier The address identifier to extend. function extend(address _identifier) external { // Only the Guardian can extend the pause. - if (msg.sender != guardian) { - revert SuperchainConfig_OnlyGuardian(); + _assertOnlyGuardian(); + + // Cannot extend the pause if not already paused. + if (pauseTimestamps[_identifier] == 0) { + revert SuperchainConfig_NotAlreadyPaused(_identifier); } // Reset the pause timestamp. @@ -154,7 +159,7 @@ contract SuperchainConfig is ProxyAdminOwnedBase, Initializable, Reinitializable /// @notice Checks if the system is currently paused for a specific identifier. /// @param _identifier The address identifier to check. /// @return True if the system is paused for this identifier and not expired. - function paused(address _identifier) public view returns (bool) { + function paused(address _identifier) external view returns (bool) { uint256 timestamp = pauseTimestamps[_identifier]; if (timestamp == 0) return false; return block.timestamp < timestamp + PAUSE_EXPIRY; @@ -176,4 +181,11 @@ contract SuperchainConfig is ProxyAdminOwnedBase, Initializable, Reinitializable guardian = _guardian; emit ConfigUpdate(UpdateType.GUARDIAN, abi.encode(_guardian)); } + + /// @notice Asserts that the caller is the guardian. + function _assertOnlyGuardian() internal view { + if (msg.sender != guardian) { + revert SuperchainConfig_OnlyGuardian(); + } + } } diff --git a/packages/contracts-bedrock/src/L1/SystemConfig.sol b/packages/contracts-bedrock/src/L1/SystemConfig.sol index d35646dded4a6..4aaf0ab7c9c45 100644 --- a/packages/contracts-bedrock/src/L1/SystemConfig.sol +++ b/packages/contracts-bedrock/src/L1/SystemConfig.sol @@ -142,9 +142,9 @@ contract SystemConfig is ProxyAdminOwnedBase, OwnableUpgradeable, Reinitializabl event ConfigUpdate(uint256 indexed version, UpdateType indexed updateType, bytes data); /// @notice Semantic version. - /// @custom:semver 3.2.0 + /// @custom:semver 3.3.0 function version() public pure virtual returns (string memory) { - return "3.2.0"; + return "3.3.0"; } /// @notice Constructs the SystemConfig contract. @@ -210,7 +210,7 @@ contract SystemConfig is ProxyAdminOwnedBase, OwnableUpgradeable, Reinitializabl _setResourceConfig(_config); l2ChainId = _l2ChainId; - superchainConfig = ISuperchainConfig(_superchainConfig); + superchainConfig = _superchainConfig; } /// @notice Upgrades the SystemConfig by adding a reference to the SuperchainConfig. @@ -227,9 +227,11 @@ contract SystemConfig is ProxyAdminOwnedBase, OwnableUpgradeable, Reinitializabl // Set the SuperchainConfig contract. superchainConfig = _superchainConfig; - // Clear out the old dispute game factory address, it's derived now. + // Clear out the old dispute game factory address, it's derived now. We get rid of this + // storage slot because it doesn't use structured storage and we can't use a spacer + // variable to block it off. bytes32 disputeGameFactorySlot = bytes32(uint256(keccak256("systemconfig.disputegamefactory")) - 1); - Storage.setAddress(disputeGameFactorySlot, address(0)); + Storage.setBytes32(disputeGameFactorySlot, bytes32(0)); } /// @notice Returns the minimum L2 gas limit that can be safely set for the system to @@ -275,7 +277,7 @@ contract SystemConfig is ProxyAdminOwnedBase, OwnableUpgradeable, Reinitializabl /// @notice Getter for the DisputeGameFactory address. function disputeGameFactory() public view returns (address addr_) { - IOptimismPortal2 portal = IOptimismPortal2(payable(Storage.getAddress(OPTIMISM_PORTAL_SLOT))); + IOptimismPortal2 portal = IOptimismPortal2(payable(optimismPortal())); addr_ = address(portal.disputeGameFactory()); } diff --git a/packages/contracts-bedrock/src/dispute/AnchorStateRegistry.sol b/packages/contracts-bedrock/src/dispute/AnchorStateRegistry.sol index 1af5a5d969f69..87f74740048e6 100644 --- a/packages/contracts-bedrock/src/dispute/AnchorStateRegistry.sol +++ b/packages/contracts-bedrock/src/dispute/AnchorStateRegistry.sol @@ -25,8 +25,8 @@ import { ISuperchainConfig } from "interfaces/L1/ISuperchainConfig.sol"; /// be initialized with a more recent starting state which reduces the amount of required offchain computation. contract AnchorStateRegistry is ProxyAdminOwnedBase, Initializable, ReinitializableBase, ISemver { /// @notice Semantic version. - /// @custom:semver 3.4.0 - string public constant version = "3.4.0"; + /// @custom:semver 3.5.0 + string public constant version = "3.5.0"; /// @notice The dispute game finality delay in seconds. uint256 internal immutable DISPUTE_GAME_FINALITY_DELAY_SECONDS; @@ -125,14 +125,20 @@ contract AnchorStateRegistry is ProxyAdminOwnedBase, Initializable, Reinitializa /// @notice Allows the Guardian to set the respected game type. /// @param _gameType The new respected game type. function setRespectedGameType(GameType _gameType) external { - if (msg.sender != systemConfig.guardian()) revert AnchorStateRegistry_Unauthorized(); + // Only the Guardian can set the respected game type. + _assertOnlyGuardian(); + + // Set the respected game type. respectedGameType = _gameType; emit RespectedGameTypeSet(_gameType); } /// @notice Allows the Guardian to update the retirement timestamp. function updateRetirementTimestamp() external { - if (msg.sender != systemConfig.guardian()) revert AnchorStateRegistry_Unauthorized(); + // Only the Guardian can update the retirement timestamp. + _assertOnlyGuardian(); + + // Update the retirement timestamp. retirementTimestamp = uint64(block.timestamp); emit RetirementTimestampSet(block.timestamp); } @@ -140,7 +146,10 @@ contract AnchorStateRegistry is ProxyAdminOwnedBase, Initializable, Reinitializa /// @notice Allows the Guardian to blacklist a dispute game. /// @param _disputeGame Dispute game to blacklist. function blacklistDisputeGame(IDisputeGame _disputeGame) external { - if (msg.sender != systemConfig.guardian()) revert AnchorStateRegistry_Unauthorized(); + // Only the Guardian can blacklist a dispute game. + _assertOnlyGuardian(); + + // Blacklist the dispute game. disputeGameBlacklist[_disputeGame] = true; emit DisputeGameBlacklisted(_disputeGame); } @@ -331,4 +340,11 @@ contract AnchorStateRegistry is ProxyAdminOwnedBase, Initializable, Reinitializa anchorGame = game; emit AnchorUpdated(game); } + + /// @notice Asserts that the caller is the Guardian. + function _assertOnlyGuardian() internal view { + if (msg.sender != systemConfig.guardian()) { + revert AnchorStateRegistry_Unauthorized(); + } + } } diff --git a/packages/contracts-bedrock/src/dispute/DisputeGameFactory.sol b/packages/contracts-bedrock/src/dispute/DisputeGameFactory.sol index 758858f1270f2..3453d752c4461 100644 --- a/packages/contracts-bedrock/src/dispute/DisputeGameFactory.sol +++ b/packages/contracts-bedrock/src/dispute/DisputeGameFactory.sol @@ -51,8 +51,8 @@ contract DisputeGameFactory is ProxyAdminOwnedBase, ReinitializableBase, Ownable } /// @notice Semantic version. - /// @custom:semver 1.1.0 - string public constant version = "1.1.0"; + /// @custom:semver 1.2.0 + string public constant version = "1.2.0"; /// @notice `gameImpls` is a mapping that maps `GameType`s to their respective /// `IDisputeGame` implementations. @@ -116,11 +116,11 @@ contract DisputeGameFactory is ProxyAdminOwnedBase, ReinitializableBase, Ownable /// @notice `gameAtIndex` returns the dispute game contract address and its creation timestamp /// at the given index. Each created dispute game increments the underlying index. + /// Reverts if the provided index does not correspond to an existing dispute game. /// @param _index The index of the dispute game. /// @return gameType_ The type of the DisputeGame - used to decide the proxy implementation. /// @return timestamp_ The timestamp of the creation of the dispute game. /// @return proxy_ The clone of the `DisputeGame` created with the given parameters. - /// Returns `address(0)` if nonexistent. function gameAtIndex(uint256 _index) external view diff --git a/packages/contracts-bedrock/src/dispute/FaultDisputeGame.sol b/packages/contracts-bedrock/src/dispute/FaultDisputeGame.sol index b7ee016c050ce..bedb826f97f37 100644 --- a/packages/contracts-bedrock/src/dispute/FaultDisputeGame.sol +++ b/packages/contracts-bedrock/src/dispute/FaultDisputeGame.sol @@ -171,9 +171,9 @@ contract FaultDisputeGame is Clone, ISemver { uint256 internal constant HEADER_BLOCK_NUMBER_INDEX = 8; /// @notice Semantic version. - /// @custom:semver 1.6.0 + /// @custom:semver 1.7.0 function version() public pure virtual returns (string memory) { - return "1.6.0"; + return "1.7.0"; } /// @notice The starting timestamp of the game @@ -997,15 +997,6 @@ contract FaultDisputeGame is Clone, ISemver { /// @notice Closes out the game, determines the bond distribution mode, attempts to register /// the game as the anchor game, and emits an event. function closeGame() public { - // We won't close the game if the system is currently paused. Paused games are temporarily - // invalid which would cause the game to go into refund mode and potentially cause some - // confusion for honest challengers. By blocking the game from being closed while the - // system is paused, the game will only go into refund mode if it ends up being explicitly - // invalidated in the AnchorStateRegistry. - if (ANCHOR_STATE_REGISTRY.paused()) { - revert GamePaused(); - } - // If the bond distribution mode has already been determined, we can return early. if (bondDistributionMode == BondDistributionMode.REFUND || bondDistributionMode == BondDistributionMode.NORMAL) { @@ -1016,6 +1007,16 @@ contract FaultDisputeGame is Clone, ISemver { revert InvalidBondDistributionMode(); } + // We won't close the game if the system is currently paused. Paused games are temporarily + // invalid which would cause the game to go into refund mode and potentially cause some + // confusion for honest challengers. By blocking the game from being closed while the + // system is paused, the game will only go into refund mode if it ends up being explicitly + // invalidated in the AnchorStateRegistry. If the game has already been closed and a refund + // mode has been selected, we'll already have returned and we won't hit this revert. + if (ANCHOR_STATE_REGISTRY.paused()) { + revert GamePaused(); + } + // Make sure that the game is resolved. // AnchorStateRegistry should be checking this but we're being defensive here. if (resolvedAt.raw() == 0) { diff --git a/packages/contracts-bedrock/src/dispute/PermissionedDisputeGame.sol b/packages/contracts-bedrock/src/dispute/PermissionedDisputeGame.sol index 814b9c8732173..8e5574512dd11 100644 --- a/packages/contracts-bedrock/src/dispute/PermissionedDisputeGame.sol +++ b/packages/contracts-bedrock/src/dispute/PermissionedDisputeGame.sol @@ -32,9 +32,9 @@ contract PermissionedDisputeGame is FaultDisputeGame { } /// @notice Semantic version. - /// @custom:semver 1.6.0 + /// @custom:semver 1.7.0 function version() public pure override returns (string memory) { - return "1.6.0"; + return "1.7.0"; } /// @param _params Parameters for creating a new FaultDisputeGame. diff --git a/packages/contracts-bedrock/src/libraries/Encoding.sol b/packages/contracts-bedrock/src/libraries/Encoding.sol index 3e6ebaa471a43..ec8c5623e7e75 100644 --- a/packages/contracts-bedrock/src/libraries/Encoding.sol +++ b/packages/contracts-bedrock/src/libraries/Encoding.sol @@ -244,7 +244,7 @@ library Encoding { } // Start with version byte and timestamp. - bytes memory encoded = bytes.concat(bytes1(0x01), bytes8(_superRootProof.timestamp)); + bytes memory encoded = bytes.concat(bytes1(_superRootProof.version), bytes8(_superRootProof.timestamp)); // Add each output root (chainId + root) for (uint256 i = 0; i < _superRootProof.outputRoots.length; i++) { diff --git a/packages/contracts-bedrock/src/safe/DeputyPauseModule.sol b/packages/contracts-bedrock/src/safe/DeputyPauseModule.sol index 9cf1da8ec1caa..967561e8bbe5a 100644 --- a/packages/contracts-bedrock/src/safe/DeputyPauseModule.sol +++ b/packages/contracts-bedrock/src/safe/DeputyPauseModule.sol @@ -80,8 +80,8 @@ contract DeputyPauseModule is ISemver, EIP712 { mapping(bytes32 => bool) public usedNonces; /// @notice Semantic version. - /// @custom:semver 2.0.0 - string public constant version = "2.0.0"; + /// @custom:semver 2.1.0 + string public constant version = "2.1.0"; /// @param _guardianSafe Address of the Guardian Safe. /// @param _foundationSafe Address of the Foundation Safe. @@ -146,7 +146,7 @@ contract DeputyPauseModule is ISemver, EIP712 { _setDeputy(_deputy, _deputySignature); } - /// @notice Calls the Foundation Safe's `execTransactionFromModuleReturnData()` function with + /// @notice Calls the Guardian Safe's `execTransactionFromModuleReturnData()` function with /// the arguments necessary to call `pause()` on the SuperchainConfig. /// Front-running this function is completely safe, it'll pause either way. /// @param _nonce Signature nonce. diff --git a/packages/contracts-bedrock/test/L1/L1CrossDomainMessenger.t.sol b/packages/contracts-bedrock/test/L1/L1CrossDomainMessenger.t.sol index 699f8c21569da..c59b7dc987bda 100644 --- a/packages/contracts-bedrock/test/L1/L1CrossDomainMessenger.t.sol +++ b/packages/contracts-bedrock/test/L1/L1CrossDomainMessenger.t.sol @@ -1022,10 +1022,6 @@ contract L1CrossDomainMessenger_Upgrade_Test is CommonTest { // Verify that the systemConfig was updated. assertEq(address(l1CrossDomainMessenger.systemConfig()), address(newSystemConfig)); - - // Verify that the spacer was cleared. - StorageSlot memory spacerSlot = ForgeArtifacts.getSlot("L1CrossDomainMessenger", "spacer_251_0_20"); - assertEq(vm.load(address(l1CrossDomainMessenger), bytes32(spacerSlot.slot)), bytes32(0)); } /// @notice Tests that the upgrade() function reverts if called a second time. diff --git a/packages/contracts-bedrock/test/L1/L1ERC721Bridge.t.sol b/packages/contracts-bedrock/test/L1/L1ERC721Bridge.t.sol index 4e2de8b0ca829..ad79e2c23fae8 100644 --- a/packages/contracts-bedrock/test/L1/L1ERC721Bridge.t.sol +++ b/packages/contracts-bedrock/test/L1/L1ERC721Bridge.t.sol @@ -488,10 +488,6 @@ contract L1ERC721Bridge_Upgrade_Test is CommonTest { // Verify that the systemConfig was updated. assertEq(address(l1ERC721Bridge.systemConfig()), address(newSystemConfig)); - - // Verify that the spacer was cleared. - StorageSlot memory spacerSlot = ForgeArtifacts.getSlot("L1ERC721Bridge", "spacer_50_0_20"); - assertEq(vm.load(address(l1ERC721Bridge), bytes32(spacerSlot.slot)), bytes32(0)); } /// @notice Tests that the upgrade() function reverts if called a second time. diff --git a/packages/contracts-bedrock/test/L1/L1StandardBridge.t.sol b/packages/contracts-bedrock/test/L1/L1StandardBridge.t.sol index fecee31c7ded7..921bd105ad872 100644 --- a/packages/contracts-bedrock/test/L1/L1StandardBridge.t.sol +++ b/packages/contracts-bedrock/test/L1/L1StandardBridge.t.sol @@ -800,10 +800,6 @@ contract L1StandardBridge_Upgrade_Test is CommonTest { // Verify that the systemConfig was updated. assertEq(address(l1StandardBridge.systemConfig()), address(newSystemConfig)); - - // Verify that the spacer was cleared. - StorageSlot memory spacerSlot = ForgeArtifacts.getSlot("L1StandardBridge", "spacer_50_0_20"); - assertEq(vm.load(address(l1StandardBridge), bytes32(spacerSlot.slot)), bytes32(0)); } /// @notice Tests that the upgrade() function reverts if called a second time. diff --git a/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol b/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol index a9837d574fa23..f48a2c9bdbf7c 100644 --- a/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol +++ b/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol @@ -524,7 +524,7 @@ contract OPContractsManager_Upgrade_Harness is CommonTest { abi.encode( l2ChainId, string.concat(string(bytes.concat(bytes32(uint256(uint160(address(systemConfig))))))), - "AnchorStateRegistry-SOT" + "AnchorStateRegistry-U16" ) ); @@ -589,27 +589,27 @@ contract OPContractsManager_Upgrade_Harness is CommonTest { // Check that the PermissionedDisputeGame is upgraded to the expected version, references // the correct anchor state and has the mipsImpl. Although Upgrade 15 doesn't actually // change any of this, we might as well check it again. - assertEq(ISemver(address(pdg)).version(), "1.6.0"); + assertEq(ISemver(address(pdg)).version(), "1.7.0"); assertEq(address(pdg.vm()), impls.mipsImpl); assertEq(pdg.l2ChainId(), oldPDG.l2ChainId()); // If the old FaultDisputeGame exists, we expect it to be upgraded. Check same as above. if (address(oldFDG) != address(0)) { - assertEq(ISemver(address(fdg)).version(), "1.6.0"); + assertEq(ISemver(address(fdg)).version(), "1.7.0"); assertEq(address(fdg.vm()), impls.mipsImpl); assertEq(fdg.l2ChainId(), oldFDG.l2ChainId()); } // Make sure that the SystemConfig is upgraded to the right version. It must also have the // right l2ChainId and must be properly initialized. - assertEq(ISemver(address(systemConfig)).version(), "3.2.0"); + assertEq(ISemver(address(systemConfig)).version(), "3.3.0"); assertEq(impls.systemConfigImpl, EIP1967Helper.getImplementation(address(systemConfig))); assertEq(systemConfig.l2ChainId(), l2ChainId); DeployUtils.assertInitialized({ _contractAddress: address(systemConfig), _isProxy: true, _slot: 0, _offset: 0 }); // Make sure that the OptimismPortal is upgraded to the right version. It must also have a // reference to the new AnchorStateRegistry. - assertEq(ISemver(address(optimismPortal2)).version(), "4.4.0"); + assertEq(ISemver(address(optimismPortal2)).version(), "4.5.0"); assertEq(impls.optimismPortalImpl, EIP1967Helper.getImplementation(address(optimismPortal2))); assertEq(address(optimismPortal2.anchorStateRegistry()), address(newAsrProxy)); DeployUtils.assertInitialized({ @@ -620,7 +620,7 @@ contract OPContractsManager_Upgrade_Harness is CommonTest { }); // Make sure the new AnchorStateRegistry has the right version and is initialized. - assertEq(ISemver(address(newAsrProxy)).version(), "3.4.0"); + assertEq(ISemver(address(newAsrProxy)).version(), "3.5.0"); vm.prank(address(proxyAdmin)); assertEq(IProxy(payable(newAsrProxy)).admin(), address(proxyAdmin)); DeployUtils.assertInitialized({ _contractAddress: address(newAsrProxy), _isProxy: true, _slot: 0, _offset: 0 }); @@ -1027,6 +1027,10 @@ contract OPContractsManager_TestInit is Test { chainDeployOutput1 = createChainContracts(100); chainDeployOutput2 = createChainContracts(101); + // Mock the SuperchainConfig.paused function to return false. + // Otherwise migration will fail! + vm.mockCall(address(superchainConfigProxy), ISuperchainConfig.paused.selector, abi.encode(false)); + // Fund the lockboxes for testing. vm.deal(address(chainDeployOutput1.ethLockboxProxy), 100 ether); vm.deal(address(chainDeployOutput2.ethLockboxProxy), 100 ether); diff --git a/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol b/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol index c0afc78973618..ff5c981cf71da 100644 --- a/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol +++ b/packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol @@ -30,7 +30,6 @@ import { IFaultDisputeGame } from "interfaces/dispute/IFaultDisputeGame.sol"; import { IProxy } from "interfaces/universal/IProxy.sol"; import { IAnchorStateRegistry } from "interfaces/dispute/IAnchorStateRegistry.sol"; import { IETHLockbox } from "interfaces/L1/IETHLockbox.sol"; -import { ISystemConfig } from "interfaces/L1/ISystemConfig.sol"; import { IProxyAdminOwnedBase } from "interfaces/L1/IProxyAdminOwnedBase.sol"; contract OptimismPortal2_Test is CommonTest { @@ -415,6 +414,18 @@ contract OptimismPortal2_Test is CommonTest { assertEq(accountAccesses[2].storageAccesses.length, 0); } + /// @notice Tests that `migrateToSuperRoots` reverts when the system is paused. + function test_migrateToSuperRoots_paused_reverts() external { + vm.startPrank(optimismPortal2.guardian()); + systemConfig.superchainConfig().pause(address(0)); + vm.stopPrank(); + + address caller = optimismPortal2.proxyAdminOwner(); + vm.expectRevert(IOptimismPortal.OptimismPortal_CallPaused.selector); + vm.prank(caller); + optimismPortal2.migrateToSuperRoots(IETHLockbox(address(1)), IAnchorStateRegistry(address(1))); + } + /// @dev Tests that `migrateToSuperRoots` reverts if the caller is not the proxy admin owner. function testFuzz_migrateToSuperRoots_notProxyAdminOwner_reverts(address _caller) external { vm.assume(_caller != optimismPortal2.proxyAdminOwner()); @@ -2107,13 +2118,7 @@ contract OptimismPortal2_upgrade_Test is CommonTest { } /// @notice Tests that the upgrade() function succeeds. - function testFuzz_upgrade_succeeds( - address _newAnchorStateRegistry, - uint256 _balance, - address _newSystemConfig - ) - external - { + function testFuzz_upgrade_succeeds(address _newAnchorStateRegistry, uint256 _balance) external { // Prevent overflow on an upgrade context _balance = bound(_balance, 0, type(uint256).max - address(ethLockbox).balance); @@ -2132,9 +2137,7 @@ contract OptimismPortal2_upgrade_Test is CommonTest { // Call the upgrade function. vm.prank(address(optimismPortal2.proxyAdmin())); - optimismPortal2.upgrade( - IAnchorStateRegistry(_newAnchorStateRegistry), IETHLockbox(ethLockbox), ISystemConfig(_newSystemConfig) - ); + optimismPortal2.upgrade(IAnchorStateRegistry(_newAnchorStateRegistry), IETHLockbox(ethLockbox)); // Verify that the initialized slot was updated. bytes32 initializedSlotAfter = vm.load(address(optimismPortal2), bytes32(slot.slot)); @@ -2143,7 +2146,6 @@ contract OptimismPortal2_upgrade_Test is CommonTest { // Assert the portal is properly upgraded. assertEq(address(optimismPortal2.ethLockbox()), address(ethLockbox)); assertEq(address(optimismPortal2.anchorStateRegistry()), _newAnchorStateRegistry); - assertEq(address(optimismPortal2.systemConfig()), _newSystemConfig); // Balance has not updated. assertEq(address(optimismPortal2).balance, _balance); @@ -2168,16 +2170,12 @@ contract OptimismPortal2_upgrade_Test is CommonTest { // Trigger first upgrade. vm.prank(address(optimismPortal2.proxyAdmin())); - optimismPortal2.upgrade( - IAnchorStateRegistry(address(0xdeadbeef)), IETHLockbox(ethLockbox), ISystemConfig(address(0xdeadbeef)) - ); + optimismPortal2.upgrade(IAnchorStateRegistry(address(0xdeadbeef)), IETHLockbox(ethLockbox)); // Try to trigger second upgrade. vm.prank(address(optimismPortal2.proxyAdmin())); vm.expectRevert("Initializable: contract is already initialized"); - optimismPortal2.upgrade( - IAnchorStateRegistry(address(0xdeadbeef)), IETHLockbox(ethLockbox), ISystemConfig(address(0xdeadbeef)) - ); + optimismPortal2.upgrade(IAnchorStateRegistry(address(0xdeadbeef)), IETHLockbox(ethLockbox)); } /// @notice Tests that the upgrade() function reverts if called after initialization. @@ -2197,9 +2195,7 @@ contract OptimismPortal2_upgrade_Test is CommonTest { // Try to trigger upgrade(). vm.expectRevert("Initializable: contract is already initialized"); - optimismPortal2.upgrade( - IAnchorStateRegistry(address(0xdeadbeef)), IETHLockbox(ethLockbox), ISystemConfig(address(0xdeadbeef)) - ); + optimismPortal2.upgrade(IAnchorStateRegistry(address(0xdeadbeef)), IETHLockbox(ethLockbox)); } /// @notice Tests that the upgrade() function reverts if called by a non-proxy admin or owner. @@ -2219,9 +2215,7 @@ contract OptimismPortal2_upgrade_Test is CommonTest { // Call the `upgrade` function with the sender vm.prank(_sender); - optimismPortal2.upgrade( - IAnchorStateRegistry(address(0xdeadbeef)), IETHLockbox(ethLockbox), ISystemConfig(address(0xdeadbeef)) - ); + optimismPortal2.upgrade(IAnchorStateRegistry(address(0xdeadbeef)), IETHLockbox(ethLockbox)); } } diff --git a/packages/contracts-bedrock/test/L1/SuperchainConfig.t.sol b/packages/contracts-bedrock/test/L1/SuperchainConfig.t.sol index 31d5f2e6dc7d5..ae8ce8552a40f 100644 --- a/packages/contracts-bedrock/test/L1/SuperchainConfig.t.sol +++ b/packages/contracts-bedrock/test/L1/SuperchainConfig.t.sol @@ -271,6 +271,15 @@ contract SuperchainConfig_Extend_Test is SuperchainConfig_TestInit { vm.expectRevert(ISuperchainConfig.SuperchainConfig_OnlyGuardian.selector); superchainConfig.extend(address(this)); } + + /// @notice Tests that `extend` reverts when the identifier is not already paused. + function test_extend_notAlreadyPaused_reverts() external { + vm.prank(superchainConfig.guardian()); + vm.expectRevert( + abi.encodeWithSelector(ISuperchainConfig.SuperchainConfig_NotAlreadyPaused.selector, address(this)) + ); + superchainConfig.extend(address(this)); + } } /// @title SuperchainConfig_Pausable_Test diff --git a/packages/contracts-bedrock/test/universal/Specs.t.sol b/packages/contracts-bedrock/test/universal/Specs.t.sol index 0be2bbeb4acbb..456292bc890f8 100644 --- a/packages/contracts-bedrock/test/universal/Specs.t.sol +++ b/packages/contracts-bedrock/test/universal/Specs.t.sol @@ -271,7 +271,7 @@ contract Specification_Test is CommonTest { _addSpec({ _name: "OptimismPortal2", _sel: _getSel("respectedGameTypeUpdatedAt()") }); _addSpec({ _name: "OptimismPortal2", _sel: _getSel("proofSubmitters(bytes32,uint256)") }); _addSpec({ _name: "OptimismPortal2", _sel: _getSel("numProofSubmitters(bytes32)") }); - _addSpec({ _name: "OptimismPortal2", _sel: _getSel("upgrade(address,address,address)") }); + _addSpec({ _name: "OptimismPortal2", _sel: _getSel("upgrade(address,address)") }); _addSpec({ _name: "OptimismPortal2", _sel: _getSel("ethLockbox()") }); _addSpec({ _name: "OptimismPortal2", _sel: _getSel("migrateLiquidity()") }); _addSpec({ _name: "OptimismPortal2", _sel: _getSel("proxyAdminOwner()") });