diff --git a/packages/contracts-bedrock/src/L1/SuperchainConfig.sol b/packages/contracts-bedrock/src/L1/SuperchainConfig.sol index ddea4a36ec510..8eb93ee7e0aa4 100644 --- a/packages/contracts-bedrock/src/L1/SuperchainConfig.sol +++ b/packages/contracts-bedrock/src/L1/SuperchainConfig.sol @@ -6,6 +6,8 @@ import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable // Libraries import { Storage } from "src/libraries/Storage.sol"; +import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; +import { Unauthorized } from "src/libraries/errors/CommonErrors.sol"; // Interfaces import { ISemver } from "interfaces/universal/ISemver.sol"; @@ -15,10 +17,27 @@ import { ISemver } from "interfaces/universal/ISemver.sol"; /// @title SuperchainConfig /// @notice The SuperchainConfig contract is used to manage configuration of global superchain values. contract SuperchainConfig is Initializable, ISemver { + + error InvalidChainID(); + error DependencySetTooLarge(); + error InvalidDependency(); + + using EnumerableSet for EnumerableSet.UintSet; + + /// @notice Event emitted when a new dependency is added to the interop dependency set. + event DependencyAdded(uint256 indexed chainId); + + /// @notice Event emitted when a dependency is removed from the interop dependency set. + event DependencyRemoved(uint256 indexed chainId); + + /// @notice The interop dependency set, containing the chain IDs in it. + EnumerableSet.UintSet dependencySet; + /// @notice Enum representing different types of updates. /// @custom:value GUARDIAN Represents an update to the guardian. enum UpdateType { - GUARDIAN + GUARDIAN, + DEPENDENCY_MANAGER } /// @notice Whether or not the Superchain is paused. @@ -28,6 +47,10 @@ contract SuperchainConfig is Initializable, ISemver { /// It can only be modified by an upgrade. bytes32 public constant GUARDIAN_SLOT = bytes32(uint256(keccak256("superchainConfig.guardian")) - 1); + /// @notice Storage slot where the dependency manager address is stored + /// It can only be modified by an upgrade. + bytes32 internal constant DEPENDENCY_MANAGER_SLOT = bytes32(uint256(keccak256("superchainConfig.dependencymanager")) - 1); + /// @notice Emitted when the pause is triggered. /// @param identifier A string helping to identify provenance of the pause transaction. event Paused(string identifier); @@ -52,13 +75,28 @@ contract SuperchainConfig is Initializable, ISemver { /// @notice Initializer. /// @param _guardian Address of the guardian, can pause the OptimismPortal. /// @param _paused Initial paused status. - function initialize(address _guardian, bool _paused) external initializer { + function initialize(address _guardian, address _dependencyManager, bool _paused) external initializer { _setGuardian(_guardian); + _setDependencyManager(_dependencyManager); if (_paused) { _pause("Initializer paused"); } } + /// @notice Returns true if a chain ID is in the interop dependency set and false otherwise. + /// The chain's chain ID is always considered to be in the dependency set. + /// @param _chainId The chain ID to check. + /// @return True if the chain ID to check is in the interop dependency set. False otherwise. + function isInDependencySet(uint256 _chainId) public view returns (bool) { + return dependencySet.contains(_chainId); + } + + /// @notice Returns the size of the interop dependency set. + /// @return The size of the interop dependency set. + function dependencySetSize() external view returns (uint8) { + return uint8(dependencySet.length()); + } + /// @notice Getter for the guardian address. function guardian() public view returns (address guardian_) { guardian_ = Storage.getAddress(GUARDIAN_SLOT); @@ -69,6 +107,36 @@ contract SuperchainConfig is Initializable, ISemver { paused_ = Storage.getBool(PAUSED_SLOT); } + // @notice Getter for the current dependency manager + function dependencyManager() public view returns (address dependencyManager_) { + dependencyManager_ = Storage.getAddress(DEPENDENCY_MANAGER_SLOT); + } + + /// @notice Adds a chain to the interop dependency set. Can only be called by the dependency manager. + /// @param _chainId Chain ID of chain to add. + function addDependency(uint256 _chainId) external { + if (msg.sender != dependencyManager()) revert Unauthorized(); + + if (dependencySet.length() == type(uint8).max) revert DependencySetTooLarge(); + if (_chainId == block.chainid) revert InvalidChainID(); + + bool success = dependencySet.add(_chainId); + if (!success) revert InvalidDependency(); + + emit DependencyAdded(_chainId); + } + + /// @notice Removes a chain from the interop dependency set. Can only be called by the dependency manager + /// @param _chainId Chain ID of the chain to remove. + function removeDependency(uint256 _chainId) external { + if (msg.sender != dependencyManager()) revert Unauthorized(); + + bool success = dependencySet.remove(_chainId); + if (!success) revert InvalidDependency(); + + emit DependencyRemoved(_chainId); + } + /// @notice Pauses withdrawals. /// @param _identifier (Optional) A string to identify provenance of the pause transaction. function pause(string memory _identifier) external { @@ -97,4 +165,12 @@ contract SuperchainConfig is Initializable, ISemver { Storage.setAddress(GUARDIAN_SLOT, _guardian); emit ConfigUpdate(UpdateType.GUARDIAN, abi.encode(_guardian)); } + + /// @notice Sets the dependency manager address. This is only callable during initialization, + /// so an upgrade will be required to change the guardian. + /// @param _dependencyManager The new dependency manager address. + function _setDependencyManager(address _dependencyManager) internal { + Storage.setAddress(DEPENDENCY_MANAGER_SLOT, _dependencyManager); + emit ConfigUpdate(UpdateType.DEPENDENCY_MANAGER, abi.encode(_dependencyManager)); + } } diff --git a/packages/contracts-bedrock/src/L1/SystemConfigInterop.sol b/packages/contracts-bedrock/src/L1/SystemConfigInterop.sol index 94c4a37f3ff89..c38c2934d87b3 100644 --- a/packages/contracts-bedrock/src/L1/SystemConfigInterop.sol +++ b/packages/contracts-bedrock/src/L1/SystemConfigInterop.sol @@ -22,11 +22,6 @@ import { ConfigType } from "interfaces/L2/IL1BlockInterop.sol"; /// All configuration is stored on L1 and picked up by L2 as part of the derviation of /// the L2 chain. contract SystemConfigInterop is SystemConfig { - /// @notice Storage slot where the dependency manager address is stored - /// @dev Equal to bytes32(uint256(keccak256("systemconfig.dependencymanager")) - 1) - bytes32 internal constant DEPENDENCY_MANAGER_SLOT = - 0x1708e077affb93e89be2665fb0fb72581be66f84dc00d25fed755ae911905b1c; - /// @notice Initializer. /// @param _owner Initial owner of the contract. /// @param _basefeeScalar Initial basefee scalar value. @@ -48,8 +43,7 @@ contract SystemConfigInterop is SystemConfig { address _unsafeBlockSigner, IResourceMetering.ResourceConfig memory _config, address _batchInbox, - SystemConfig.Addresses memory _addresses, - address _dependencyManager + SystemConfig.Addresses memory _addresses ) external { @@ -65,7 +59,6 @@ contract SystemConfigInterop is SystemConfig { _batchInbox: _batchInbox, _addresses: _addresses }); - Storage.setAddress(DEPENDENCY_MANAGER_SLOT, _dependencyManager); } /// @custom:semver +interop-beta.7 @@ -101,27 +94,4 @@ contract SystemConfigInterop is SystemConfig { ); } } - - /// @notice Adds a chain to the interop dependency set. Can only be called by the dependency manager. - /// @param _chainId Chain ID of chain to add. - function addDependency(uint256 _chainId) external { - require(msg.sender == dependencyManager(), "SystemConfig: caller is not the dependency manager"); - IOptimismPortal(payable(optimismPortal())).setConfig( - ConfigType.ADD_DEPENDENCY, StaticConfig.encodeAddDependency(_chainId) - ); - } - - /// @notice Removes a chain from the interop dependency set. Can only be called by the dependency manager - /// @param _chainId Chain ID of the chain to remove. - function removeDependency(uint256 _chainId) external { - require(msg.sender == dependencyManager(), "SystemConfig: caller is not the dependency manager"); - IOptimismPortal(payable(optimismPortal())).setConfig( - ConfigType.REMOVE_DEPENDENCY, StaticConfig.encodeRemoveDependency(_chainId) - ); - } - - /// @notice getter for the dependency manager address - function dependencyManager() public view returns (address) { - return Storage.getAddress(DEPENDENCY_MANAGER_SLOT); - } } diff --git a/packages/contracts-bedrock/src/L2/L1BlockInterop.sol b/packages/contracts-bedrock/src/L2/L1BlockInterop.sol index 7b92b202a052d..1a8ddd10c1316 100644 --- a/packages/contracts-bedrock/src/L2/L1BlockInterop.sol +++ b/packages/contracts-bedrock/src/L2/L1BlockInterop.sol @@ -5,27 +5,17 @@ pragma solidity 0.8.15; import { L1Block } from "src/L2/L1Block.sol"; // Libraries -import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import { GasPayingToken } from "src/libraries/GasPayingToken.sol"; import { StaticConfig } from "src/libraries/StaticConfig.sol"; import { Predeploys } from "src/libraries/Predeploys.sol"; -import { - NotDepositor, - NotCrossL2Inbox, - NotDependency, - DependencySetSizeTooLarge, - AlreadyDependency, - CantRemovedDependency -} from "src/libraries/L1BlockErrors.sol"; +import { NotDepositor, NotCrossL2Inbox } from "src/libraries/L1BlockErrors.sol"; /// @notice Enum representing different types of configurations that can be set on L1BlockInterop. /// @custom:value SET_GAS_PAYING_TOKEN Represents the config type for setting the gas paying token. /// @custom:value ADD_DEPENDENCY Represents the config type for adding a chain to the interop dependency set. /// @custom:value REMOVE_DEPENDENCY Represents the config type for removing a chain from the interop dependency set. enum ConfigType { - SET_GAS_PAYING_TOKEN, - ADD_DEPENDENCY, - REMOVE_DEPENDENCY + SET_GAS_PAYING_TOKEN } /// @custom:proxied true @@ -33,17 +23,6 @@ enum ConfigType { /// @title L1BlockInterop /// @notice Interop extenstions of L1Block. contract L1BlockInterop is L1Block { - using EnumerableSet for EnumerableSet.UintSet; - - /// @notice Event emitted when a new dependency is added to the interop dependency set. - event DependencyAdded(uint256 indexed chainId); - - /// @notice Event emitted when a dependency is removed from the interop dependency set. - event DependencyRemoved(uint256 indexed chainId); - - /// @notice The interop dependency set, containing the chain IDs in it. - EnumerableSet.UintSet dependencySet; - /// @notice Storage slot that the isDeposit is stored at. /// This is a custom slot that is not part of the standard storage layout. /// keccak256(abi.encode(uint256(keccak256("l1Block.identifier.isDeposit")) - 1)) & ~bytes32(uint256(0xff)) @@ -63,19 +42,6 @@ contract L1BlockInterop is L1Block { } } - /// @notice Returns true if a chain ID is in the interop dependency set and false otherwise. - /// The chain's chain ID is always considered to be in the dependency set. - /// @param _chainId The chain ID to check. - /// @return True if the chain ID to check is in the interop dependency set. False otherwise. - function isInDependencySet(uint256 _chainId) public view returns (bool) { - return _chainId == block.chainid || dependencySet.contains(_chainId); - } - - /// @notice Returns the size of the interop dependency set. - /// @return The size of the interop dependency set. - function dependencySetSize() external view returns (uint8) { - return uint8(dependencySet.length()); - } /// @notice Updates the `isDeposit` flag and sets the L1 block values for an Interop upgraded chain. /// It updates the L1 block values through the `setL1BlockValuesEcotone` function. @@ -109,10 +75,6 @@ contract L1BlockInterop is L1Block { if (_type == ConfigType.SET_GAS_PAYING_TOKEN) { _setGasPayingToken(_value); - } else if (_type == ConfigType.ADD_DEPENDENCY) { - _addDependency(_value); - } else if (_type == ConfigType.REMOVE_DEPENDENCY) { - _removeDependency(_value); } } @@ -125,28 +87,4 @@ contract L1BlockInterop is L1Block { emit GasPayingTokenSet({ token: token, decimals: decimals, name: name, symbol: symbol }); } - - /// @notice Internal method to add a dependency to the interop dependency set. - /// @param _value The encoded value with which to add the dependency. - function _addDependency(bytes calldata _value) internal { - uint256 chainId = StaticConfig.decodeAddDependency(_value); - - if (dependencySet.length() == type(uint8).max) revert DependencySetSizeTooLarge(); - - if (chainId == block.chainid || !dependencySet.add(chainId)) revert AlreadyDependency(); - - emit DependencyAdded(chainId); - } - - /// @notice Internal method to remove a dependency from the interop dependency set. - /// @param _value The encoded value with which to remove the dependency. - function _removeDependency(bytes calldata _value) internal { - uint256 chainId = StaticConfig.decodeRemoveDependency(_value); - - if (chainId == block.chainid) revert CantRemovedDependency(); - - if (!dependencySet.remove(chainId)) revert NotDependency(); - - emit DependencyRemoved(chainId); - } } diff --git a/packages/contracts-bedrock/src/libraries/L1BlockErrors.sol b/packages/contracts-bedrock/src/libraries/L1BlockErrors.sol index 44e156e158c06..7c18d168c9c77 100644 --- a/packages/contracts-bedrock/src/libraries/L1BlockErrors.sol +++ b/packages/contracts-bedrock/src/libraries/L1BlockErrors.sol @@ -5,16 +5,4 @@ pragma solidity ^0.8.0; error NotDepositor(); /// @notice Error when a non-cross L2 Inbox sender tries to call the `isDeposit()` method. -error NotCrossL2Inbox(); - -/// @notice Error when a chain ID is not in the interop dependency set. -error NotDependency(); - -/// @notice Error when the interop dependency set size is too large. -error DependencySetSizeTooLarge(); - -/// @notice Error when a chain ID already in the interop dependency set is attempted to be added. -error AlreadyDependency(); - -/// @notice Error when the chain's chain ID is attempted to be removed from the interop dependency set. -error CantRemovedDependency(); +error NotCrossL2Inbox(); \ No newline at end of file