Skip to content
Merged
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
21 changes: 21 additions & 0 deletions packages/contracts-bedrock/interfaces/L2/IL2ContractsManager.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// Interfaces
import { ISemver } from "interfaces/universal/ISemver.sol";

// Libraries
import { L2ContractsManagerTypes } from "src/libraries/L2ContractsManagerTypes.sol";

/// @title IL2ContractsManager
/// @notice Interface for the L2ContractsManager contract.
interface IL2ContractsManager is ISemver {
/// @notice Executes the upgrade for all predeploys.
/// @dev This function MUST be called via DELEGATECALL from the L2ProxyAdmin.
function upgrade() external;

/// @notice Constructor for the L2ContractsManager contract.
/// @param _implementations The implementation struct containing the new implementation addresses for the L2
/// predeploys.
function __constructor__(L2ContractsManagerTypes.Implementations memory _implementations) external;
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ import { IL1Block } from "interfaces/L2/IL1Block.sol";

// Libraries
import { Predeploys } from "src/libraries/Predeploys.sol";
import { XForkL2CMTypes } from "src/libraries/XForkL2CMTypes.sol";
import { L2ContractsManagerTypes } from "src/libraries/L2ContractsManagerTypes.sol";
import { L2ContractsManagerUtils } from "src/libraries/L2ContractsManagerUtils.sol";

/// @title XForkL2ContractsManager
/// @notice Manages the upgrade of the L2 predeploys for the XFork upgrade.
contract XForkL2ContractsManager is ISemver {
/// @title L2ContractsManager
/// @notice Manages the upgrade of the L2 predeploys.
contract L2ContractsManager is ISemver {
/// @notice Thrown when the upgrade function is called outside of a DELEGATECALL context.
error XForkL2ContractsManager_OnlyDelegatecall();
error L2ContractsManager_OnlyDelegatecall();

/// @notice The semantic version of the L2ContractsManager contract.
/// @custom:semver 1.0.0
Expand Down Expand Up @@ -103,10 +103,10 @@ contract XForkL2ContractsManager is ISemver {
/// @notice FeeSplitter implementation.
address internal immutable FEE_SPLITTER_IMPL;

/// @notice Constructor for the XForkL2ContractsManager contract.
/// @notice Constructor for the L2ContractsManager contract.
/// @param _implementations The implementation struct containing the new implementation addresses for the L2
/// predeploys.
constructor(XForkL2CMTypes.Implementations memory _implementations) {
constructor(L2ContractsManagerTypes.Implementations memory _implementations) {
// Store the address of this contract for DELEGATECALL enforcement.
THIS_L2CM = address(this);

Expand Down Expand Up @@ -145,33 +145,38 @@ contract XForkL2ContractsManager is ISemver {
/// @notice Executes the upgrade for all predeploys.
/// @dev This function MUST be called via DELEGATECALL from the L2ProxyAdmin.
function upgrade() external {
if (address(this) == THIS_L2CM) revert XForkL2ContractsManager_OnlyDelegatecall();
if (address(this) == THIS_L2CM) revert L2ContractsManager_OnlyDelegatecall();

XForkL2CMTypes.FullConfig memory fullConfig = _loadFullConfig();
L2ContractsManagerTypes.FullConfig memory fullConfig = _loadFullConfig();
_apply(fullConfig);
}

/// @notice Loads the full configuration for the L2 Predeploys.
/// @return fullConfig_ The full configuration.
function _loadFullConfig() internal view returns (XForkL2CMTypes.FullConfig memory fullConfig_) {
function _loadFullConfig() internal view returns (L2ContractsManagerTypes.FullConfig memory fullConfig_) {
// Note: Currently, this is the only way to determine if the network is a custom gas token network.
// We need our upgrades be able to determine if the network is a custom gas token network so that we can
// apply the appropriate configuration to the LiquidityController predeploy. In networks without custom gas
// tokens, the LiquidityController predeploy is not used and points to address(0).
bool isCustomGasToken = IL1Block(Predeploys.L1_BLOCK_ATTRIBUTES).isCustomGasToken();

// L2CrossDomainMessenger
fullConfig_.crossDomainMessenger = XForkL2CMTypes.CrossDomainMessengerConfig({
fullConfig_.crossDomainMessenger = L2ContractsManagerTypes.CrossDomainMessengerConfig({
otherMessenger: ICrossDomainMessenger(Predeploys.L2_CROSS_DOMAIN_MESSENGER).otherMessenger()
});

// L2StandardBridge
fullConfig_.standardBridge = XForkL2CMTypes.StandardBridgeConfig({
fullConfig_.standardBridge = L2ContractsManagerTypes.StandardBridgeConfig({
otherBridge: IStandardBridge(payable(Predeploys.L2_STANDARD_BRIDGE)).otherBridge()
});

// L2ERC721Bridge
fullConfig_.erc721Bridge =
XForkL2CMTypes.ERC721BridgeConfig({ otherBridge: IERC721Bridge(Predeploys.L2_ERC721_BRIDGE).otherBridge() });
fullConfig_.erc721Bridge = L2ContractsManagerTypes.ERC721BridgeConfig({
otherBridge: IERC721Bridge(Predeploys.L2_ERC721_BRIDGE).otherBridge()
});

// OptimismMintableERC20Factory
fullConfig_.mintableERC20Factory = XForkL2CMTypes.MintableERC20FactoryConfig({
fullConfig_.mintableERC20Factory = L2ContractsManagerTypes.MintableERC20FactoryConfig({
bridge: IOptimismMintableERC20Factory(Predeploys.OPTIMISM_MINTABLE_ERC20_FACTORY).bridge()
});

Expand All @@ -190,25 +195,25 @@ contract XForkL2ContractsManager is ISemver {
// LiquidityController
if (isCustomGasToken) {
ILiquidityController liquidityController = ILiquidityController(Predeploys.LIQUIDITY_CONTROLLER);
fullConfig_.liquidityController = XForkL2CMTypes.LiquidityControllerConfig({
fullConfig_.liquidityController = L2ContractsManagerTypes.LiquidityControllerConfig({
owner: liquidityController.owner(),
gasPayingTokenName: liquidityController.gasPayingTokenName(),
gasPayingTokenSymbol: liquidityController.gasPayingTokenSymbol()
});
}

// FeeSplitter
fullConfig_.feeSplitter = XForkL2CMTypes.FeeSplitterConfig({
fullConfig_.feeSplitter = L2ContractsManagerTypes.FeeSplitterConfig({
sharesCalculator: IFeeSplitter(payable(Predeploys.FEE_SPLITTER)).sharesCalculator()
});

fullConfig_.isCustomGasToken = isCustomGasToken;
}

/// @notice Upgrades each of the predeploys to its corresponding new implementation. Applies the appropriate
/// configuration to each predeploy.
/// @param _config The full configuration for the L2 Predeploys.
function _apply(XForkL2CMTypes.FullConfig memory _config) internal {
bool isCustomGasToken = IL1Block(Predeploys.L1_BLOCK_ATTRIBUTES).isCustomGasToken();

function _apply(L2ContractsManagerTypes.FullConfig memory _config) internal {
// Initializable predeploys.

// L2CrossDomainMessenger
Expand Down Expand Up @@ -252,7 +257,7 @@ contract XForkL2ContractsManager is ISemver {
);

// LiquidityController (only on custom gas token networks)
if (isCustomGasToken) {
if (_config.isCustomGasToken) {
L2ContractsManagerUtils.upgradeToAndCall(
Predeploys.LIQUIDITY_CONTROLLER,
LIQUIDITY_CONTROLLER_IMPL,
Expand All @@ -268,6 +273,9 @@ contract XForkL2ContractsManager is ISemver {
INITIALIZABLE_SLOT_OZ_V4,
0
);

// NativeAssetLiquidity
L2ContractsManagerUtils.upgradeTo(Predeploys.NATIVE_ASSET_LIQUIDITY, NATIVE_ASSET_LIQUIDITY_IMPL);
}

// FeeSplitter
Expand Down Expand Up @@ -352,11 +360,12 @@ contract XForkL2ContractsManager is ISemver {
L2ContractsManagerUtils.upgradeTo(Predeploys.GAS_PRICE_ORACLE, GAS_PRICE_ORACLE_IMPL);
// L1BlockAttributes and L2ToL1MessagePasser have different implementations for custom gas token networks.
L2ContractsManagerUtils.upgradeTo(
Predeploys.L1_BLOCK_ATTRIBUTES, isCustomGasToken ? L1_BLOCK_ATTRIBUTES_CGT_IMPL : L1_BLOCK_ATTRIBUTES_IMPL
Predeploys.L1_BLOCK_ATTRIBUTES,
_config.isCustomGasToken ? L1_BLOCK_ATTRIBUTES_CGT_IMPL : L1_BLOCK_ATTRIBUTES_IMPL
);
L2ContractsManagerUtils.upgradeTo(
Predeploys.L2_TO_L1_MESSAGE_PASSER,
isCustomGasToken ? L2_TO_L1_MESSAGE_PASSER_CGT_IMPL : L2_TO_L1_MESSAGE_PASSER_IMPL
_config.isCustomGasToken ? L2_TO_L1_MESSAGE_PASSER_CGT_IMPL : L2_TO_L1_MESSAGE_PASSER_IMPL
);
L2ContractsManagerUtils.upgradeTo(
Predeploys.OPTIMISM_MINTABLE_ERC721_FACTORY, OPTIMISM_MINTABLE_ERC721_FACTORY_IMPL
Expand All @@ -375,10 +384,6 @@ contract XForkL2ContractsManager is ISemver {
Predeploys.OPTIMISM_SUPERCHAIN_ERC20_BEACON, OPTIMISM_SUPERCHAIN_ERC20_BEACON_IMPL
);
L2ContractsManagerUtils.upgradeTo(Predeploys.SUPERCHAIN_TOKEN_BRIDGE, SUPERCHAIN_TOKEN_BRIDGE_IMPL);
// NativeAssetLiquidity
if (isCustomGasToken) {
L2ContractsManagerUtils.upgradeTo(Predeploys.NATIVE_ASSET_LIQUIDITY, NATIVE_ASSET_LIQUIDITY_IMPL);
}
L2ContractsManagerUtils.upgradeTo(Predeploys.SCHEMA_REGISTRY, SCHEMA_REGISTRY_IMPL);
L2ContractsManagerUtils.upgradeTo(Predeploys.EAS, EAS_IMPL);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import { IStandardBridge } from "interfaces/universal/IStandardBridge.sol";
import { IERC721Bridge } from "interfaces/universal/IERC721Bridge.sol";
import { ISharesCalculator } from "interfaces/L2/ISharesCalculator.sol";

/// @title XForkL2CMTypes
/// @notice Type definitions for XForkL2ContractsManager upgrade operations.
library XForkL2CMTypes {
/// @title L2ContractsManagerTypes
/// @notice Type definitions for L2ContractsManager upgrade operations.
library L2ContractsManagerTypes {
/// @notice Configuration for L2CrossDomainMessenger.
struct CrossDomainMessengerConfig {
ICrossDomainMessenger otherMessenger;
Expand Down Expand Up @@ -62,9 +62,10 @@ library XForkL2CMTypes {
FeeVaultConfig operatorFeeVault;
LiquidityControllerConfig liquidityController;
FeeSplitterConfig feeSplitter;
bool isCustomGasToken;
}

/// @notice The implementation addresses to manage the XFork upgrade.
/// @notice The current implementation addresses for the L2 predeploys.
struct Implementations {
address storageSetterImpl;
address l2CrossDomainMessengerImpl;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,38 @@
pragma solidity ^0.8.0;

// Libraries
import { XForkL2CMTypes } from "src/libraries/XForkL2CMTypes.sol";
import { IProxy } from "interfaces/universal/IProxy.sol";
import { L2ContractsManagerTypes } from "src/libraries/L2ContractsManagerTypes.sol";
import { SemverComp } from "src/libraries/SemverComp.sol";
import { ProxyAdmin } from "src/universal/ProxyAdmin.sol";
import { Predeploys } from "src/libraries/Predeploys.sol";

// Interfaces
import { IStorageSetter } from "interfaces/universal/IStorageSetter.sol";
import { IFeeVault } from "interfaces/L2/IFeeVault.sol";
import { ISemver } from "interfaces/universal/ISemver.sol";
import { IProxy } from "interfaces/universal/IProxy.sol";

/// @title L2ContractsManagerUtils
/// @notice L2ContractsManagerUtils is a library that provides utility functions for the L2ContractsManager system.
library L2ContractsManagerUtils {
/// @notice Thrown when a user attempts to downgrade a contract.
/// @param _target The address of the contract that was attempted to be downgraded.
error L2ContractsManager_DowngradeNotAllowed(address _target);

/// @notice Upgrades a predeploy to a new implementation without calling an initializer.
/// @param _proxy The proxy address of the predeploy.
/// @param _implementation The new implementation address.
function upgradeTo(address _proxy, address _implementation) internal {
// We avoid downgrading Predeploys
if (
// Predploys.PROXY_ADMIN is not checked for downgrades because it has no version number.
_proxy != Predeploys.PROXY_ADMIN
&& ProxyAdmin(Predeploys.PROXY_ADMIN).getProxyImplementation(_proxy) != address(0)
&& SemverComp.gt(ISemver(_proxy).version(), ISemver(_implementation).version())
) {
revert L2ContractsManager_DowngradeNotAllowed(address(_proxy));
}

IProxy(payable(_proxy)).upgradeTo(_implementation);
}

Expand All @@ -23,13 +43,16 @@ library L2ContractsManagerUtils {
function readFeeVaultConfig(address _feeVault)
internal
view
returns (XForkL2CMTypes.FeeVaultConfig memory config_)
returns (L2ContractsManagerTypes.FeeVaultConfig memory config_)
{
// Note: We are intentionally using legacy deprecated getters for this 1.0.0 version of the L2ContractsManager.
// Subsequent versions should use the new getters as L2ContractsManager should ensure that the new current
// version of the FeeVault is used.
IFeeVault feeVault = IFeeVault(payable(_feeVault));
config_ = XForkL2CMTypes.FeeVaultConfig({
recipient: feeVault.recipient(),
minWithdrawalAmount: feeVault.minWithdrawalAmount(),
withdrawalNetwork: feeVault.withdrawalNetwork()
config_ = L2ContractsManagerTypes.FeeVaultConfig({
recipient: feeVault.RECIPIENT(),
minWithdrawalAmount: feeVault.MIN_WITHDRAWAL_AMOUNT(),
withdrawalNetwork: feeVault.WITHDRAWAL_NETWORK()
});
}

Expand All @@ -51,6 +74,16 @@ library L2ContractsManagerUtils {
)
internal
{
if (
// Predeploys.PROXY_ADMIN is not checked for downgrades because it has no version number.
// This should never be the case, if you're trying to initialize the ProxyAdmin, it's probably a mistake.
_proxy != Predeploys.PROXY_ADMIN
&& ProxyAdmin(Predeploys.PROXY_ADMIN).getProxyImplementation(_proxy) != address(0)
&& SemverComp.gt(ISemver(_proxy).version(), ISemver(_implementation).version())
) {
revert L2ContractsManager_DowngradeNotAllowed(address(_proxy));
}

// Upgrade to StorageSetter.
IProxy(payable(_proxy)).upgradeTo(_storageSetterImpl);

Expand Down
Loading