Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 78 additions & 2 deletions packages/contracts-bedrock/src/L1/SuperchainConfig.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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.
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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 {
Expand Down Expand Up @@ -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));
}
}
32 changes: 1 addition & 31 deletions packages/contracts-bedrock/src/L1/SystemConfigInterop.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
{
Expand All @@ -65,7 +59,6 @@ contract SystemConfigInterop is SystemConfig {
_batchInbox: _batchInbox,
_addresses: _addresses
});
Storage.setAddress(DEPENDENCY_MANAGER_SLOT, _dependencyManager);
}

/// @custom:semver +interop-beta.7
Expand Down Expand Up @@ -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);
}
}
66 changes: 2 additions & 64 deletions packages/contracts-bedrock/src/L2/L1BlockInterop.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,45 +5,24 @@ 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
/// @custom:predeploy 0x4200000000000000000000000000000000000015
/// @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))
Expand All @@ -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.
Expand Down Expand Up @@ -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);
}
}

Expand All @@ -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);
}
}
14 changes: 1 addition & 13 deletions packages/contracts-bedrock/src/libraries/L1BlockErrors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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();