diff --git a/packages/contracts-bedrock/scripts/deploy/DeployImplementations.s.sol b/packages/contracts-bedrock/scripts/deploy/DeployImplementations.s.sol index 9bd8181be6e8..b3660c92c2e8 100644 --- a/packages/contracts-bedrock/scripts/deploy/DeployImplementations.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/DeployImplementations.s.sol @@ -29,6 +29,7 @@ import { ISystemConfigInterop } from "interfaces/L1/ISystemConfigInterop.sol"; import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; import { Solarray } from "scripts/libraries/Solarray.sol"; import { BaseDeployIO } from "scripts/deploy/BaseDeployIO.sol"; +import { DeploySuperchainImplementations, DeploySuperchainOutput } from "scripts/deploy/DeploySuperchain.s.sol"; // See DeploySuperchain.s.sol for detailed comments on the script architecture used here. contract DeployImplementationsInput is BaseDeployIO { @@ -143,6 +144,8 @@ contract DeployImplementationsOutput is BaseDeployIO { IOptimismMintableERC20Factory internal _optimismMintableERC20FactoryImpl; IDisputeGameFactory internal _disputeGameFactoryImpl; IAnchorStateRegistry internal _anchorStateRegistryImpl; + ISuperchainConfig internal _superchainConfigImpl; + IProtocolVersions internal _protocolVersionsImpl; function set(bytes4 _sel, address _addr) public { require(_addr != address(0), "DeployImplementationsOutput: cannot set zero address"); @@ -160,6 +163,8 @@ contract DeployImplementationsOutput is BaseDeployIO { else if (_sel == this.optimismMintableERC20FactoryImpl.selector) _optimismMintableERC20FactoryImpl = IOptimismMintableERC20Factory(_addr); else if (_sel == this.disputeGameFactoryImpl.selector) _disputeGameFactoryImpl = IDisputeGameFactory(_addr); else if (_sel == this.anchorStateRegistryImpl.selector) _anchorStateRegistryImpl = IAnchorStateRegistry(_addr); + else if (_sel == this.superchainConfigImpl.selector) _superchainConfigImpl = ISuperchainConfig(_addr); + else if (_sel == this.protocolVersionsImpl.selector) _protocolVersionsImpl = IProtocolVersions(_addr); else revert("DeployImplementationsOutput: unknown selector"); // forgefmt: disable-end } @@ -250,6 +255,16 @@ contract DeployImplementationsOutput is BaseDeployIO { return _anchorStateRegistryImpl; } + function superchainConfigImpl() public view returns (ISuperchainConfig) { + DeployUtils.assertValidContractAddress(address(_superchainConfigImpl)); + return _superchainConfigImpl; + } + + function protocolVersionsImpl() public view returns (IProtocolVersions) { + DeployUtils.assertValidContractAddress(address(_protocolVersionsImpl)); + return _protocolVersionsImpl; + } + // -------- Deployment Assertions -------- function assertValidDeploy(DeployImplementationsInput _dii) public view { assertValidDelayedWETHImpl(_dii); @@ -411,6 +426,7 @@ contract DeployImplementations is Script { function run(DeployImplementationsInput _dii, DeployImplementationsOutput _dio) public { // Deploy the implementations. + DeploySuperchainImplementations.deploySuperchainImplementationContracts(DeploySuperchainOutput(address(_dio))); deploySystemConfigImpl(_dio); deployL1CrossDomainMessengerImpl(_dio); deployL1ERC721BridgeImpl(_dio); @@ -455,6 +471,8 @@ contract DeployImplementations is Script { l1StandardBridgeImpl: address(_dio.l1StandardBridgeImpl()), disputeGameFactoryImpl: address(_dio.disputeGameFactoryImpl()), anchorStateRegistryImpl: address(_dio.anchorStateRegistryImpl()), + superchainConfigImpl: address(_dio.superchainConfigImpl()), + protocolVersionsImpl: address(_dio.protocolVersionsImpl()), delayedWETHImpl: address(_dio.delayedWETHImpl()), mipsImpl: address(_dio.mipsSingleton()) }); @@ -790,6 +808,8 @@ contract DeployImplementationsInterop is DeployImplementations { IProtocolVersions protocolVersionsProxy = _dii.protocolVersionsProxy(); OPContractsManager.Implementations memory implementations = OPContractsManager.Implementations({ + superchainConfigImpl: address(_dio.superchainConfigImpl()), + protocolVersionsImpl: address(_dio.protocolVersionsImpl()), l1ERC721BridgeImpl: address(_dio.l1ERC721BridgeImpl()), optimismPortalImpl: address(_dio.optimismPortalImpl()), systemConfigImpl: address(_dio.systemConfigImpl()), diff --git a/packages/contracts-bedrock/scripts/deploy/DeployOPCM.s.sol b/packages/contracts-bedrock/scripts/deploy/DeployOPCM.s.sol index b676439bd7e7..775654feb021 100644 --- a/packages/contracts-bedrock/scripts/deploy/DeployOPCM.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/DeployOPCM.s.sol @@ -25,6 +25,8 @@ contract DeployOPCMInput is BaseDeployIO { address internal _permissionedDisputeGame1Blueprint; address internal _permissionedDisputeGame2Blueprint; + address internal _superchainConfigImpl; + address internal _protocolVersionsImpl; address internal _l1ERC721BridgeImpl; address internal _optimismPortalImpl; address internal _systemConfigImpl; @@ -160,6 +162,16 @@ contract DeployOPCMInput is BaseDeployIO { return _anchorStateRegistryImpl; } + function superchainConfigImpl() public view returns (address) { + require(_superchainConfigImpl != address(0), "DeployOPCMInput: not set"); + return _superchainConfigImpl; + } + + function protocolVersionsImpl() public view returns (address) { + require(_protocolVersionsImpl != address(0), "DeployOPCMInput: not set"); + return _protocolVersionsImpl; + } + function delayedWETHImpl() public view returns (address) { require(_delayedWETHImpl != address(0), "DeployOPCMInput: not set"); return _delayedWETHImpl; @@ -202,6 +214,8 @@ contract DeployOPCM is Script { permissionlessDisputeGame2: address(0) }); OPContractsManager.Implementations memory implementations = OPContractsManager.Implementations({ + superchainConfigImpl: address(_doi.superchainConfigImpl()), + protocolVersionsImpl: address(_doi.protocolVersionsImpl()), l1ERC721BridgeImpl: address(_doi.l1ERC721BridgeImpl()), optimismPortalImpl: address(_doi.optimismPortalImpl()), systemConfigImpl: address(_doi.systemConfigImpl()), diff --git a/packages/contracts-bedrock/scripts/deploy/DeploySuperchain.s.sol b/packages/contracts-bedrock/scripts/deploy/DeploySuperchain.s.sol index e4804aa867a3..99bb3b506a23 100644 --- a/packages/contracts-bedrock/scripts/deploy/DeploySuperchain.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/DeploySuperchain.s.sol @@ -3,7 +3,7 @@ pragma solidity 0.8.15; import { Script } from "forge-std/Script.sol"; import { stdToml } from "forge-std/StdToml.sol"; - +import { Vm } from "forge-std/Vm.sol"; import { ISuperchainConfig } from "interfaces/L1/ISuperchainConfig.sol"; import { IProtocolVersions, ProtocolVersion } from "interfaces/L1/IProtocolVersions.sol"; import { IProxyAdmin } from "interfaces/universal/IProxyAdmin.sol"; @@ -310,7 +310,7 @@ contract DeploySuperchain is Script { deploySuperchainProxyAdmin(_dsi, _dso); // Deploy and initialize the superchain contracts. - deploySuperchainImplementationContracts(_dsi, _dso); + DeploySuperchainImplementations.deploySuperchainImplementationContracts(_dso); deployAndInitializeSuperchainConfig(_dsi, _dso); deployAndInitializeProtocolVersions(_dsi, _dso); @@ -341,29 +341,7 @@ contract DeploySuperchain is Script { } function deploySuperchainImplementationContracts(DeploySuperchainInput, DeploySuperchainOutput _dso) public { - // Deploy implementation contracts. - vm.startBroadcast(msg.sender); - ISuperchainConfig superchainConfigImpl = ISuperchainConfig( - DeployUtils.createDeterministic({ - _name: "SuperchainConfig", - _args: DeployUtils.encodeConstructor(abi.encodeCall(ISuperchainConfig.__constructor__, ())), - _salt: _salt - }) - ); - IProtocolVersions protocolVersionsImpl = IProtocolVersions( - DeployUtils.createDeterministic({ - _name: "ProtocolVersions", - _args: DeployUtils.encodeConstructor(abi.encodeCall(IProtocolVersions.__constructor__, ())), - _salt: _salt - }) - ); - vm.stopBroadcast(); - - vm.label(address(superchainConfigImpl), "SuperchainConfigImpl"); - vm.label(address(protocolVersionsImpl), "ProtocolVersionsImpl"); - - _dso.set(_dso.superchainConfigImpl.selector, address(superchainConfigImpl)); - _dso.set(_dso.protocolVersionsImpl.selector, address(protocolVersionsImpl)); + DeploySuperchainImplementations.deploySuperchainImplementationContracts(_dso); } function deployAndInitializeSuperchainConfig(DeploySuperchainInput _dsi, DeploySuperchainOutput _dso) public { @@ -458,3 +436,34 @@ contract DeploySuperchain is Script { dso_ = DeploySuperchainOutput(DeployUtils.toIOAddress(msg.sender, "optimism.DeploySuperchainOutput")); } } + +library DeploySuperchainImplementations { + bytes32 internal constant _salt = keccak256("op-stack-contract-impls-salt-v0"); + Vm internal constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code"))))); + + function deploySuperchainImplementationContracts(DeploySuperchainOutput _dso) public { + // Deploy implementation contracts. + vm.startBroadcast(msg.sender); + ISuperchainConfig superchainConfigImpl = ISuperchainConfig( + DeployUtils.createDeterministic({ + _name: "SuperchainConfig", + _args: DeployUtils.encodeConstructor(abi.encodeCall(ISuperchainConfig.__constructor__, ())), + _salt: _salt + }) + ); + IProtocolVersions protocolVersionsImpl = IProtocolVersions( + DeployUtils.createDeterministic({ + _name: "ProtocolVersions", + _args: DeployUtils.encodeConstructor(abi.encodeCall(IProtocolVersions.__constructor__, ())), + _salt: _salt + }) + ); + vm.stopBroadcast(); + + vm.label(address(superchainConfigImpl), "SuperchainConfigImpl"); + vm.label(address(protocolVersionsImpl), "ProtocolVersionsImpl"); + + _dso.set(_dso.superchainConfigImpl.selector, address(superchainConfigImpl)); + _dso.set(_dso.protocolVersionsImpl.selector, address(protocolVersionsImpl)); + } +} diff --git a/packages/contracts-bedrock/src/L1/OPContractsManager.sol b/packages/contracts-bedrock/src/L1/OPContractsManager.sol index 576655bd6cde..d4cf39bbb184 100644 --- a/packages/contracts-bedrock/src/L1/OPContractsManager.sol +++ b/packages/contracts-bedrock/src/L1/OPContractsManager.sol @@ -100,6 +100,8 @@ contract OPContractsManager is ISemver { /// @notice The latest implementation contracts for the OP Stack. struct Implementations { + address superchainConfigImpl; + address protocolVersionsImpl; address l1ERC721BridgeImpl; address optimismPortalImpl; address systemConfigImpl; @@ -220,6 +222,9 @@ contract OPContractsManager is ISemver { /// @notice Thrown when the SuperchainConfig of the chain does not match the SuperchainConfig of this OPCM. error SuperchainConfigMismatch(ISystemConfig systemConfig); + /// @notice Thrown when the SuperchainProxyAdmin does not match the SuperchainConfig's admin. + error SuperchainProxyAdminMismatch(); + // -------- Methods -------- constructor( @@ -420,12 +425,22 @@ contract OPContractsManager is ISemver { /// @notice Upgrades a set of chains to the latest implementation contracts /// @param _opChains Array of OpChain structs, one per chain to upgrade /// @dev This function is intended to be called via DELEGATECALL from the Upgrade Controller Safe - function upgrade(OpChain[] memory _opChains, OutputRoot[] memory _startingAnchorRoots) external { + function upgrade( + IProxyAdmin _superchainProxyAdmin, + OpChain[] memory _opChains, + OutputRoot[] memory _startingAnchorRoots + ) + external + { if (address(this) == address(thisOPCM)) revert OnlyDelegatecall(); Implementations memory impls = thisOPCM.implementations(); - // TODO: upgrading the SuperchainConfig and ProtocolVersions (in a new function) + if (address(_superchainProxyAdmin) != address(0)) { + // Attempt to upgrade. If the ProxyAdmin is not the SuperchainConfig's admin, this will revert. + upgradeTo(_superchainProxyAdmin, address(superchainConfig), impls.superchainConfigImpl); + upgradeTo(_superchainProxyAdmin, address(protocolVersions), impls.protocolVersionsImpl); + } for (uint256 i = 0; i < _opChains.length; i++) { ISystemConfig systemConfig = _opChains[i].systemConfigProxy; diff --git a/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol b/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol index 2256c63d03a8..5f00685f41a9 100644 --- a/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol +++ b/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol @@ -15,8 +15,7 @@ import { EIP1967Helper } from "test/mocks/EIP1967Helper.sol"; import { Blueprint } from "src/libraries/Blueprint.sol"; import { ForgeArtifacts } from "scripts/libraries/ForgeArtifacts.sol"; -// Contracts -import { OPContractsManager } from "src/L1/OPContractsManager.sol"; +// Interfaces import { ISuperchainConfig } from "interfaces/L1/ISuperchainConfig.sol"; import { IProtocolVersions } from "interfaces/L1/IProtocolVersions.sol"; import { IProxyAdmin } from "interfaces/universal/IProxyAdmin.sol"; @@ -25,6 +24,11 @@ import { IPreimageOracle } from "interfaces/cannon/IPreimageOracle.sol"; import { IPermissionedDisputeGame } from "interfaces/dispute/IPermissionedDisputeGame.sol"; import { IDelayedWETH } from "interfaces/dispute/IDelayedWETH.sol"; import { IFaultDisputeGame } from "interfaces/dispute/IFaultDisputeGame.sol"; + +// Contracts +import { OPContractsManager } from "src/L1/OPContractsManager.sol"; +import { SuperchainConfig } from "src/L1/SuperchainConfig.sol"; +import { ProtocolVersions } from "src/L1/ProtocolVersions.sol"; import { DisputeGameFactory } from "src/dispute/DisputeGameFactory.sol"; import { L1ERC721Bridge } from "src/L1/L1ERC721Bridge.sol"; import { OptimismPortal2 } from "src/L1/OptimismPortal2.sol"; @@ -192,6 +196,7 @@ contract OPContractsManager_Upgrade_Harness is CommonTest { uint256 l2ChainId; IProxyAdmin proxyAdmin; + IProxyAdmin superchainProxyAdmin; address upgrader; OPContractsManager.OpChain[] opChains; OutputRoot[] startingAnchorRoots; @@ -210,6 +215,7 @@ contract OPContractsManager_Upgrade_Harness is CommonTest { } proxyAdmin = IProxyAdmin(EIP1967Helper.getAdmin(address(systemConfig))); + superchainProxyAdmin = IProxyAdmin(EIP1967Helper.getAdmin(address(superchainConfig))); upgrader = proxyAdmin.owner(); vm.label(upgrader, "ProxyAdmin Owner"); @@ -235,6 +241,8 @@ contract OPContractsManager_Upgrade_Test is OPContractsManager_Upgrade_Harness { OPContractsManager.Implementations memory impls = opcm.implementations(); address oldL1CrossDomainMessenger = addressManager.getAddress("OVM_L1CrossDomainMessenger"); + expectEmitUpgraded(impls.superchainConfigImpl, address(superchainConfig)); + expectEmitUpgraded(impls.protocolVersionsImpl, address(protocolVersions)); expectEmitUpgraded(impls.systemConfigImpl, address(systemConfig)); vm.expectEmit(address(addressManager)); emit AddressSet("OVM_L1CrossDomainMessenger", impls.l1CrossDomainMessengerImpl, oldL1CrossDomainMessenger); @@ -253,7 +261,8 @@ contract OPContractsManager_Upgrade_Test is OPContractsManager_Upgrade_Harness { emit Upgraded(l2ChainId, opChains[0].systemConfigProxy, address(upgrader)); DelegateCaller(upgrader).dcForward( - address(opcm), abi.encodeCall(OPContractsManager.upgrade, (opChains, startingAnchorRoots)) + address(opcm), + abi.encodeCall(OPContractsManager.upgrade, (superchainProxyAdmin, opChains, startingAnchorRoots)) ); assertEq(impls.systemConfigImpl, EIP1967Helper.getImplementation(address(systemConfig))); @@ -286,7 +295,7 @@ contract OPContractsManager_Upgrade_TestFails is OPContractsManager_Upgrade_Harn function test_upgrade_notDelegateCalled_reverts() public { vm.prank(upgrader); vm.expectRevert(OPContractsManager.OnlyDelegatecall.selector); - opcm.upgrade(opChains, startingAnchorRoots); + opcm.upgrade(superchainProxyAdmin, opChains, startingAnchorRoots); } function test_upgrade_superchainConfigMismatch_reverts() public { @@ -302,7 +311,8 @@ contract OPContractsManager_Upgrade_TestFails is OPContractsManager_Upgrade_Harn abi.encodeWithSelector(OPContractsManager.SuperchainConfigMismatch.selector, address(systemConfig)) ); DelegateCaller(upgrader).dcForward( - address(opcm), abi.encodeCall(OPContractsManager.upgrade, (opChains, startingAnchorRoots)) + address(opcm), + abi.encodeCall(OPContractsManager.upgrade, (superchainProxyAdmin, opChains, startingAnchorRoots)) ); } } @@ -330,6 +340,8 @@ contract OPContractsManager_AddGameType_Test is Test { IPreimageOracle oracle = IPreimageOracle(address(new PreimageOracle(126000, 86400))); OPContractsManager.Implementations memory impls = OPContractsManager.Implementations({ + superchainConfigImpl: address(new SuperchainConfig()), + protocolVersionsImpl: address(new ProtocolVersions()), l1ERC721BridgeImpl: address(new L1ERC721Bridge()), optimismPortalImpl: address(new OptimismPortal2(1, 1)), systemConfigImpl: address(new SystemConfig()), diff --git a/packages/contracts-bedrock/test/setup/ForkLive.s.sol b/packages/contracts-bedrock/test/setup/ForkLive.s.sol index acb178317995..2c3c493f965c 100644 --- a/packages/contracts-bedrock/test/setup/ForkLive.s.sol +++ b/packages/contracts-bedrock/test/setup/ForkLive.s.sol @@ -20,6 +20,7 @@ import { IFaultDisputeGame } from "interfaces/dispute/IFaultDisputeGame.sol"; import { IDisputeGameFactory } from "interfaces/dispute/IDisputeGameFactory.sol"; import { IAddressManager } from "interfaces/legacy/IAddressManager.sol"; import { ISystemConfig } from "interfaces/L1/ISystemConfig.sol"; +import { ISuperchainConfig } from "interfaces/L1/ISuperchainConfig.sol"; import { IProxyAdmin } from "interfaces/universal/IProxyAdmin.sol"; import { OPContractsManager } from "src/L1/OPContractsManager.sol"; @@ -146,6 +147,9 @@ contract ForkLive is Deployer { ISystemConfig systemConfig = ISystemConfig(artifacts.mustGetAddress("SystemConfigProxy")); IProxyAdmin proxyAdmin = IProxyAdmin(EIP1967Helper.getAdmin(address(systemConfig))); + ISuperchainConfig superchainConfig = ISuperchainConfig(artifacts.mustGetAddress("SuperchainConfig")); + IProxyAdmin superchainProxyAdmin = IProxyAdmin(EIP1967Helper.getAdmin(address(superchainConfig))); + address upgrader = proxyAdmin.owner(); vm.label(upgrader, "ProxyAdmin Owner"); @@ -161,7 +165,8 @@ contract ForkLive is Deployer { // reflecting the production system. vm.etch(upgrader, vm.getDeployedCode("test/mocks/Callers.sol:DelegateCaller")); DelegateCaller(upgrader).dcForward( - address(opcm), abi.encodeCall(OPContractsManager.upgrade, (opChains, startingAnchorRoots)) + address(opcm), + abi.encodeCall(OPContractsManager.upgrade, (superchainProxyAdmin, opChains, startingAnchorRoots)) ); }