diff --git a/packages/contracts-bedrock/interfaces/L1/ISuperchainConfig.sol b/packages/contracts-bedrock/interfaces/L1/ISuperchainConfig.sol index df11d6c5cfa7c..df63c6888873a 100644 --- a/packages/contracts-bedrock/interfaces/L1/ISuperchainConfig.sol +++ b/packages/contracts-bedrock/interfaces/L1/ISuperchainConfig.sol @@ -24,6 +24,7 @@ interface ISuperchainConfig is IProxyAdminOwnedBase { function pause(address _identifier) external; function unpause(address _identifier) external; function pausable(address _identifier) external view returns (bool); + function paused() external view returns (bool); function paused(address _identifier) external view returns (bool); function expiration(address _identifier) external view returns (uint256); function extend(address _identifier) external; diff --git a/packages/contracts-bedrock/snapshots/abi/SuperchainConfig.json b/packages/contracts-bedrock/snapshots/abi/SuperchainConfig.json index af7b20286b3eb..ed7ad5c10f978 100644 --- a/packages/contracts-bedrock/snapshots/abi/SuperchainConfig.json +++ b/packages/contracts-bedrock/snapshots/abi/SuperchainConfig.json @@ -158,6 +158,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "paused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "proxyAdmin", diff --git a/packages/contracts-bedrock/snapshots/semver-lock.json b/packages/contracts-bedrock/snapshots/semver-lock.json index 632df042114c9..008ae4fc8c04c 100644 --- a/packages/contracts-bedrock/snapshots/semver-lock.json +++ b/packages/contracts-bedrock/snapshots/semver-lock.json @@ -32,8 +32,8 @@ "sourceCodeHash": "0xb3e32b18c95d4940980333e1e99b4dcf42d8a8bfce78139db4dc3fb06e9349d0" }, "src/L1/SuperchainConfig.sol:SuperchainConfig": { - "initCodeHash": "0x5c6d81d8c998a4fbe2b97737584197930a54c0bfc198ef62eae049b163e893aa", - "sourceCodeHash": "0x2a2b109a66b0025f118b64e2a81ad88fd097ce3cb2adc370b5bb3d6bd2029ddd" + "initCodeHash": "0x0ea921059d71fd19ac9c6e29c05b9724ad584eb27f74231de6df9551e9b13084", + "sourceCodeHash": "0xad12c20a00dc20683bd3f68e6ee254f968da6cc2d98930be6534107ee5cb11d9" }, "src/L1/SystemConfig.sol:SystemConfig": { "initCodeHash": "0x19e352bb99cab389e0eead6178746c49b14d061bea89c444f26e768f601bd6d8", diff --git a/packages/contracts-bedrock/src/L1/SuperchainConfig.sol b/packages/contracts-bedrock/src/L1/SuperchainConfig.sol index c3562e9ad4318..870a35e134428 100644 --- a/packages/contracts-bedrock/src/L1/SuperchainConfig.sol +++ b/packages/contracts-bedrock/src/L1/SuperchainConfig.sol @@ -59,8 +59,8 @@ contract SuperchainConfig is ProxyAdminOwnedBase, Initializable, Reinitializable event ConfigUpdate(UpdateType indexed updateType, bytes data); /// @notice Semantic version. - /// @custom:semver 2.2.0 - string public constant version = "2.2.0"; + /// @custom:semver 2.3.0 + string public constant version = "2.3.0"; /// @notice Constructs the SuperchainConfig contract. constructor() ReinitializableBase(2) { @@ -156,10 +156,19 @@ contract SuperchainConfig is ProxyAdminOwnedBase, Initializable, Reinitializable return pauseTimestamps[_identifier] == 0; } + /// @custom:legacy + /// @notice Checks if the global superchain system is paused. NOTE that this is a legacy + /// function that provides support for systems that still rely on the older interface. + /// Contracts should use paused(address) instead when possible. + /// @return True if the global superchain system is paused. + function paused() external view returns (bool) { + return paused(address(0)); + } + /// @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) external view returns (bool) { + function paused(address _identifier) public view returns (bool) { uint256 timestamp = pauseTimestamps[_identifier]; if (timestamp == 0) return false; return block.timestamp < timestamp + PAUSE_EXPIRY; diff --git a/packages/contracts-bedrock/test/L1/ETHLockbox.t.sol b/packages/contracts-bedrock/test/L1/ETHLockbox.t.sol index 4317c8e447de9..b34d24e4a48d7 100644 --- a/packages/contracts-bedrock/test/L1/ETHLockbox.t.sol +++ b/packages/contracts-bedrock/test/L1/ETHLockbox.t.sol @@ -14,7 +14,6 @@ import { ForgeArtifacts, StorageSlot } from "scripts/libraries/ForgeArtifacts.so // Interfaces import { IETHLockbox } from "interfaces/L1/IETHLockbox.sol"; -import { ISuperchainConfig } from "interfaces/L1/ISuperchainConfig.sol"; import { IProxyAdminOwnedBase } from "interfaces/L1/IProxyAdminOwnedBase.sol"; import { IOptimismPortal2 } from "interfaces/L1/IOptimismPortal2.sol"; @@ -97,7 +96,9 @@ contract ETHLockboxTest is CommonTest { assertEq(ethLockbox.paused(), false); // Mock the superchain config to return true for the paused status - vm.mockCall(address(superchainConfig), abi.encodeCall(ISuperchainConfig.paused, (address(0))), abi.encode(true)); + // 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); @@ -227,7 +228,9 @@ contract ETHLockboxTest is CommonTest { /// @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 - vm.mockCall(address(superchainConfig), abi.encodeCall(ISuperchainConfig.paused, (address(0))), abi.encode(true)); + // 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); diff --git a/packages/contracts-bedrock/test/L1/L1CrossDomainMessenger.t.sol b/packages/contracts-bedrock/test/L1/L1CrossDomainMessenger.t.sol index c59b7dc987bda..2425e3f675607 100644 --- a/packages/contracts-bedrock/test/L1/L1CrossDomainMessenger.t.sol +++ b/packages/contracts-bedrock/test/L1/L1CrossDomainMessenger.t.sol @@ -17,7 +17,6 @@ import { ForgeArtifacts, StorageSlot } from "scripts/libraries/ForgeArtifacts.so // Target contract dependencies import { IL1CrossDomainMessenger } from "interfaces/L1/IL1CrossDomainMessenger.sol"; import { IOptimismPortal2 } from "interfaces/L1/IOptimismPortal2.sol"; -import { ISuperchainConfig } from "interfaces/L1/ISuperchainConfig.sol"; import { ISystemConfig } from "interfaces/L1/ISystemConfig.sol"; import { IProxyAdminOwnedBase } from "interfaces/L1/IProxyAdminOwnedBase.sol"; @@ -892,7 +891,9 @@ contract L1CrossDomainMessenger_Test is CommonTest { /// @dev Tests that the superchain config is called by the messengers paused function function test_pause_callsSuperchainConfig_succeeds() external { - vm.expectCall(address(superchainConfig), abi.encodeCall(ISuperchainConfig.paused, (address(0)))); + // We use abi.encodeWithSignature because paused is overloaded. + // nosemgrep: sol-style-use-abi-encodecall + vm.expectCall(address(superchainConfig), abi.encodeWithSignature("paused(address)", address(0))); l1CrossDomainMessenger.paused(); } diff --git a/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol b/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol index ea2ec0dfb473e..926a976b32dd7 100644 --- a/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol +++ b/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol @@ -1044,7 +1044,9 @@ contract OPContractsManager_TestInit is Test { // Mock the SuperchainConfig.paused function to return false. // Otherwise migration will fail! - vm.mockCall(address(superchainConfigProxy), ISuperchainConfig.paused.selector, abi.encode(false)); + // We use abi.encodeWithSignature because paused is overloaded. + // nosemgrep: sol-style-use-abi-encodecall + vm.mockCall(address(superchainConfigProxy), abi.encodeWithSignature("paused(address)"), abi.encode(false)); // Fund the lockboxes for testing. vm.deal(address(chainDeployOutput1.ethLockboxProxy), 100 ether); diff --git a/packages/contracts-bedrock/test/L1/StandardValidator.t.sol b/packages/contracts-bedrock/test/L1/StandardValidator.t.sol index c42d13375c152..066e040c041f8 100644 --- a/packages/contracts-bedrock/test/L1/StandardValidator.t.sol +++ b/packages/contracts-bedrock/test/L1/StandardValidator.t.sol @@ -16,7 +16,6 @@ import { IPermissionedDisputeGame } from "interfaces/dispute/IPermissionedDisput import { IDisputeGame } from "interfaces/dispute/IDisputeGame.sol"; import { IDelayedWETH } from "interfaces/dispute/IDelayedWETH.sol"; import { IAnchorStateRegistry } from "interfaces/dispute/IAnchorStateRegistry.sol"; -import { ISuperchainConfig } from "interfaces/L1/ISuperchainConfig.sol"; import { IProxyAdmin } from "interfaces/universal/IProxyAdmin.sol"; import { ISemver } from "interfaces/universal/ISemver.sol"; import { ISystemConfig } from "interfaces/L1/ISystemConfig.sol"; @@ -264,7 +263,11 @@ contract StandardValidator_validate_Test is StandardValidator_TestInit { /// @notice Tests that the validate function successfully returns the right error when the /// SuperchainConfig contract is paused. function test_validate_superchainConfigPaused_succeeds() public { - vm.mockCall(address(superchainConfig), abi.encodeCall(ISuperchainConfig.paused, (address(0))), abi.encode(true)); + // 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) + ); assertEq("SPRCFG-10", _validate(true)); } diff --git a/packages/contracts-bedrock/test/L1/SuperchainConfig.t.sol b/packages/contracts-bedrock/test/L1/SuperchainConfig.t.sol index ae8ce8552a40f..9890273036fb3 100644 --- a/packages/contracts-bedrock/test/L1/SuperchainConfig.t.sol +++ b/packages/contracts-bedrock/test/L1/SuperchainConfig.t.sol @@ -174,6 +174,53 @@ contract SuperchainConfig_PauseExpiry_Test is SuperchainConfig_TestInit { } } +/// @title SuperchainConfig_Paused_Test +/// @notice Test contract for SuperchainConfig `paused` function. +contract SuperchainConfig_Paused_Test is SuperchainConfig_TestInit { + /// @notice Tests that `paused` returns true when the specific identifier is paused. + /// @param _identifier The identifier to test. + function testFuzz_paused_specificIdentifier_succeeds(address _identifier) external { + // Assume the identifier is not the zero address. + vm.assume(_identifier != address(0)); + + // Pause with the specific identifier. + vm.prank(superchainConfig.guardian()); + superchainConfig.pause(_identifier); + + // Assert that the specific identifier is paused. + assertTrue(superchainConfig.paused(_identifier)); + + // Pick a random address that is not the identifier. + address other = vm.randomAddress(); + while (other == _identifier) { + other = vm.randomAddress(); + } + + // Assert that the other address is not paused. + assertFalse(superchainConfig.paused(other)); + } + + /// @notice Tests that `paused` returns true when the global superchain system is paused. + function test_paused_global_succeeds() external { + // Pause the global superchain system. + vm.prank(superchainConfig.guardian()); + superchainConfig.pause(address(0)); + + // Assert that the global superchain system is paused. + assertTrue(superchainConfig.paused()); + assertTrue(superchainConfig.paused(address(0))); + + // Pick a random address that is not the zero address. + address other = vm.randomAddress(); + while (other == address(0)) { + other = vm.randomAddress(); + } + + // Assert that the other address is not paused. + assertFalse(superchainConfig.paused(other)); + } +} + /// @title SuperchainConfig_Pause_Test /// @notice Test contract for SuperchainConfig `pause` function. contract SuperchainConfig_Pause_Test is SuperchainConfig_TestInit { diff --git a/packages/contracts-bedrock/test/safe/DeputyPauseModule.t.sol b/packages/contracts-bedrock/test/safe/DeputyPauseModule.t.sol index 658c7956d22d3..7120fea280e3f 100644 --- a/packages/contracts-bedrock/test/safe/DeputyPauseModule.t.sol +++ b/packages/contracts-bedrock/test/safe/DeputyPauseModule.t.sol @@ -575,7 +575,11 @@ contract DeputyPauseModule_Pause_TestFail is DeputyPauseModule_TestInit { /// transaction is sent. function test_pause_superchainPauseFails_reverts() external { // Make sure that the SuperchainConfig paused() returns false. - vm.mockCall(address(superchainConfig), abi.encodePacked(superchainConfig.paused.selector), abi.encode(false)); + // 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(false) + ); // Expect a revert. vm.expectRevert(IDeputyPauseModule.DeputyPauseModule_SuperchainNotPaused.selector); diff --git a/packages/contracts-bedrock/test/universal/Specs.t.sol b/packages/contracts-bedrock/test/universal/Specs.t.sol index 456292bc890f8..b1e5b9d3e71de 100644 --- a/packages/contracts-bedrock/test/universal/Specs.t.sol +++ b/packages/contracts-bedrock/test/universal/Specs.t.sol @@ -330,6 +330,7 @@ contract Specification_Test is CommonTest { _addSpec({ _name: "SuperchainConfig", _sel: _getSel("upgrade()") }); _addSpec({ _name: "SuperchainConfig", _sel: _getSel("pause(address)"), _auth: Role.GUARDIAN }); _addSpec({ _name: "SuperchainConfig", _sel: _getSel("paused(address)") }); + _addSpec({ _name: "SuperchainConfig", _sel: _getSel("paused()") }); _addSpec({ _name: "SuperchainConfig", _sel: _getSel("unpause(address)"), _auth: Role.GUARDIAN }); _addSpec({ _name: "SuperchainConfig", _sel: _getSel("version()") }); _addSpec({ _name: "SuperchainConfig", _sel: _getSel("pausable(address)") });