diff --git a/packages/contracts-bedrock/contracts/deployment/SystemDictator.sol b/packages/contracts-bedrock/contracts/deployment/SystemDictator.sol index f54b4e9ca3383..1f2bbf82b44d3 100644 --- a/packages/contracts-bedrock/contracts/deployment/SystemDictator.sol +++ b/packages/contracts-bedrock/contracts/deployment/SystemDictator.sol @@ -1,7 +1,9 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; -import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; +import { + OwnableUpgradeable +} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import { L2OutputOracle } from "../L1/L2OutputOracle.sol"; import { OptimismPortal } from "../L1/OptimismPortal.sol"; import { L1CrossDomainMessenger } from "../L1/L1CrossDomainMessenger.sol"; @@ -21,7 +23,7 @@ import { SystemConfig } from "../L1/SystemConfig.sol"; * system. The SystemDictator is designed to support both fresh network deployments and * upgrades to existing pre-Bedrock systems. */ -contract SystemDictator is Ownable { +contract SystemDictator is OwnableUpgradeable { /** * @notice Basic system configuration. */ @@ -111,7 +113,7 @@ contract SystemDictator is Ownable { /** * @notice Current step; */ - uint8 public currentStep = 1; + uint8 public currentStep; /** * @notice Whether or not dynamic config has been set. @@ -142,8 +144,10 @@ contract SystemDictator is Ownable { /** * @param _config System configuration. */ - constructor(DeployConfig memory _config) Ownable() { + function initialize(DeployConfig memory _config) public initializer { config = _config; + currentStep = 1; + __Ownable_init(); _transferOwnership(config.globalConfig.controller); } diff --git a/packages/contracts-bedrock/deploy/009-SystemDictatorProxy.ts b/packages/contracts-bedrock/deploy/009-SystemDictatorProxy.ts new file mode 100644 index 0000000000000..75d3c841c4d12 --- /dev/null +++ b/packages/contracts-bedrock/deploy/009-SystemDictatorProxy.ts @@ -0,0 +1,23 @@ +import { DeployFunction } from 'hardhat-deploy/dist/types' + +import { + assertContractVariable, + deployAndVerifyAndThen, +} from '../src/deploy-utils' + +const deployFn: DeployFunction = async (hre) => { + const { deployer } = await hre.getNamedAccounts() + await deployAndVerifyAndThen({ + hre, + name: 'SystemDictatorProxy', + contract: 'Proxy', + args: [deployer], + postDeployAction: async (contract) => { + await assertContractVariable(contract, 'admin', deployer) + }, + }) +} + +deployFn.tags = ['SystemDictatorProxy'] + +export default deployFn diff --git a/packages/contracts-bedrock/deploy/009-L1CrossDomainMessengerImpl.ts b/packages/contracts-bedrock/deploy/010-L1CrossDomainMessengerImpl.ts similarity index 100% rename from packages/contracts-bedrock/deploy/009-L1CrossDomainMessengerImpl.ts rename to packages/contracts-bedrock/deploy/010-L1CrossDomainMessengerImpl.ts diff --git a/packages/contracts-bedrock/deploy/010-L1StandardBridgeImpl.ts b/packages/contracts-bedrock/deploy/011-L1StandardBridgeImpl.ts similarity index 100% rename from packages/contracts-bedrock/deploy/010-L1StandardBridgeImpl.ts rename to packages/contracts-bedrock/deploy/011-L1StandardBridgeImpl.ts diff --git a/packages/contracts-bedrock/deploy/011-L2OutputOracleImpl.ts b/packages/contracts-bedrock/deploy/012-L2OutputOracleImpl.ts similarity index 100% rename from packages/contracts-bedrock/deploy/011-L2OutputOracleImpl.ts rename to packages/contracts-bedrock/deploy/012-L2OutputOracleImpl.ts diff --git a/packages/contracts-bedrock/deploy/012-OptimismPortalImpl.ts b/packages/contracts-bedrock/deploy/013-OptimismPortalImpl.ts similarity index 100% rename from packages/contracts-bedrock/deploy/012-OptimismPortalImpl.ts rename to packages/contracts-bedrock/deploy/013-OptimismPortalImpl.ts diff --git a/packages/contracts-bedrock/deploy/013-OptimismMintableERC20FactoryImpl.ts b/packages/contracts-bedrock/deploy/014-OptimismMintableERC20FactoryImpl.ts similarity index 100% rename from packages/contracts-bedrock/deploy/013-OptimismMintableERC20FactoryImpl.ts rename to packages/contracts-bedrock/deploy/014-OptimismMintableERC20FactoryImpl.ts diff --git a/packages/contracts-bedrock/deploy/014-L1ERC721BridgeImpl.ts b/packages/contracts-bedrock/deploy/015-L1ERC721BridgeImpl.ts similarity index 100% rename from packages/contracts-bedrock/deploy/014-L1ERC721BridgeImpl.ts rename to packages/contracts-bedrock/deploy/015-L1ERC721BridgeImpl.ts diff --git a/packages/contracts-bedrock/deploy/015-PortalSenderImpl.ts b/packages/contracts-bedrock/deploy/016-PortalSenderImpl.ts similarity index 100% rename from packages/contracts-bedrock/deploy/015-PortalSenderImpl.ts rename to packages/contracts-bedrock/deploy/016-PortalSenderImpl.ts diff --git a/packages/contracts-bedrock/deploy/016-SystemConfigImpl.ts b/packages/contracts-bedrock/deploy/017-SystemConfigImpl.ts similarity index 100% rename from packages/contracts-bedrock/deploy/016-SystemConfigImpl.ts rename to packages/contracts-bedrock/deploy/017-SystemConfigImpl.ts diff --git a/packages/contracts-bedrock/deploy/017-SystemDictator.ts b/packages/contracts-bedrock/deploy/017-SystemDictator.ts deleted file mode 100644 index d3064b4ae1dfd..0000000000000 --- a/packages/contracts-bedrock/deploy/017-SystemDictator.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { ethers } from 'ethers' -import { DeployFunction } from 'hardhat-deploy/dist/types' -import '@eth-optimism/hardhat-deploy-config' -import 'hardhat-deploy' - -import { - deployAndVerifyAndThen, - assertDictatorConfig, - makeDictatorConfig, -} from '../src/deploy-utils' - -const deployFn: DeployFunction = async (hre) => { - const { deployer } = await hre.getNamedAccounts() - - let controller = hre.deployConfig.controller - if (controller === ethers.constants.AddressZero) { - if (hre.network.config.live === false) { - console.log(`WARNING!!!`) - console.log(`WARNING!!!`) - console.log(`WARNING!!!`) - console.log(`WARNING!!! A controller address was not provided.`) - console.log( - `WARNING!!! Make sure you are ONLY doing this on a test network.` - ) - controller = deployer - } else { - throw new Error( - `controller address MUST NOT be the deployer on live networks` - ) - } - } - - let finalOwner = hre.deployConfig.finalSystemOwner - if (finalOwner === ethers.constants.AddressZero) { - if (hre.network.config.live === false) { - console.log(`WARNING!!!`) - console.log(`WARNING!!!`) - console.log(`WARNING!!!`) - console.log(`WARNING!!! A proxy admin owner address was not provided.`) - console.log( - `WARNING!!! Make sure you are ONLY doing this on a test network.` - ) - finalOwner = deployer - } else { - throw new Error(`must specify the finalSystemOwner on live networks`) - } - } - - const config = await makeDictatorConfig(hre, controller, finalOwner, false) - await deployAndVerifyAndThen({ - hre, - name: 'SystemDictator', - args: [config], - postDeployAction: async (contract) => { - await assertDictatorConfig(contract, config) - }, - }) -} - -deployFn.tags = ['SystemDictator'] - -export default deployFn diff --git a/packages/contracts-bedrock/deploy/018-SystemDictatorImpl.ts b/packages/contracts-bedrock/deploy/018-SystemDictatorImpl.ts new file mode 100644 index 0000000000000..a9d96e9f159be --- /dev/null +++ b/packages/contracts-bedrock/deploy/018-SystemDictatorImpl.ts @@ -0,0 +1,17 @@ +import { DeployFunction } from 'hardhat-deploy/dist/types' +import '@eth-optimism/hardhat-deploy-config' +import 'hardhat-deploy' + +import { deployAndVerifyAndThen } from '../src/deploy-utils' + +const deployFn: DeployFunction = async (hre) => { + await deployAndVerifyAndThen({ + hre, + name: 'SystemDictator', + args: [], + }) +} + +deployFn.tags = ['SystemDictatorImpl'] + +export default deployFn diff --git a/packages/contracts-bedrock/deploy/019-SystemDictatorInit.ts b/packages/contracts-bedrock/deploy/019-SystemDictatorInit.ts new file mode 100644 index 0000000000000..e5287476db090 --- /dev/null +++ b/packages/contracts-bedrock/deploy/019-SystemDictatorInit.ts @@ -0,0 +1,137 @@ +import { ethers } from 'ethers' +import { DeployFunction } from 'hardhat-deploy/dist/types' +import { awaitCondition } from '@eth-optimism/core-utils' +import '@eth-optimism/hardhat-deploy-config' +import 'hardhat-deploy' + +import { + assertDictatorConfig, + makeDictatorConfig, + getContractsFromArtifacts, +} from '../src/deploy-utils' + +const deployFn: DeployFunction = async (hre) => { + const { deployer } = await hre.getNamedAccounts() + + let controller = hre.deployConfig.controller + if (controller === ethers.constants.AddressZero) { + if (hre.network.config.live === false) { + console.log(`WARNING!!!`) + console.log(`WARNING!!!`) + console.log(`WARNING!!!`) + console.log(`WARNING!!! A controller address was not provided.`) + console.log( + `WARNING!!! Make sure you are ONLY doing this on a test network.` + ) + controller = deployer + } else { + throw new Error( + `controller address MUST NOT be the deployer on live networks` + ) + } + } + + let finalOwner = hre.deployConfig.finalSystemOwner + if (finalOwner === ethers.constants.AddressZero) { + if (hre.network.config.live === false) { + console.log(`WARNING!!!`) + console.log(`WARNING!!!`) + console.log(`WARNING!!!`) + console.log(`WARNING!!! A proxy admin owner address was not provided.`) + console.log( + `WARNING!!! Make sure you are ONLY doing this on a test network.` + ) + finalOwner = deployer + } else { + throw new Error(`must specify the finalSystemOwner on live networks`) + } + } + + // Load the contracts we need to interact with. + const [ + SystemDictator, + SystemDictatorProxy, + SystemDictatorProxyWithSigner, + SystemDictatorImpl, + ] = await getContractsFromArtifacts(hre, [ + { + name: 'SystemDictatorProxy', + iface: 'SystemDictator', + signerOrProvider: deployer, + }, + { + name: 'SystemDictatorProxy', + }, + { + name: 'SystemDictatorProxy', + signerOrProvider: deployer, + }, + { + name: 'SystemDictator', + signerOrProvider: deployer, + }, + ]) + + // Load the dictator configuration. + const config = await makeDictatorConfig(hre, controller, finalOwner, false) + + // Update the implementation if necessary. + if ( + (await SystemDictatorProxy.callStatic.implementation({ + from: ethers.constants.AddressZero, + })) !== SystemDictatorImpl.address + ) { + console.log('Upgrading the SystemDictator proxy...') + + // Upgrade and initialize the proxy. + await SystemDictatorProxyWithSigner.upgradeToAndCall( + SystemDictatorImpl.address, + SystemDictatorImpl.interface.encodeFunctionData('initialize', [config]) + ) + + // Wait for the transaction to execute properly. + await awaitCondition( + async () => { + return ( + (await SystemDictatorProxy.callStatic.implementation({ + from: ethers.constants.AddressZero, + })) === SystemDictatorImpl.address + ) + }, + 30000, + 1000 + ) + + // Verify that the contract was initialized correctly. + await assertDictatorConfig(SystemDictator, config) + } + + // Update the owner if necessary. + if ( + (await SystemDictatorProxy.callStatic.admin({ + from: ethers.constants.AddressZero, + })) !== controller + ) { + console.log('Transferring ownership of the SystemDictator proxy...') + + // Transfer ownership to the controller address. + await SystemDictatorProxyWithSigner.transferOwnership(controller) + + // Wait for the transaction to execute properly. + await awaitCondition( + async () => { + return ( + (await SystemDictatorProxy.callStatic.admin({ + from: ethers.constants.AddressZero, + })) === controller + ) + }, + 30000, + 1000 + ) + } +} + +deployFn.tags = ['SystemDictatorImpl'] + +export default deployFn diff --git a/packages/contracts-bedrock/deploy/018-SystemDictatorSteps.ts b/packages/contracts-bedrock/deploy/020-SystemDictatorSteps.ts similarity index 99% rename from packages/contracts-bedrock/deploy/018-SystemDictatorSteps.ts rename to packages/contracts-bedrock/deploy/020-SystemDictatorSteps.ts index ec702763fb33f..451a3af24d5e8 100644 --- a/packages/contracts-bedrock/deploy/018-SystemDictatorSteps.ts +++ b/packages/contracts-bedrock/deploy/020-SystemDictatorSteps.ts @@ -70,7 +70,8 @@ const deployFn: DeployFunction = async (hre) => { L1ERC721Bridge, ] = await getContractsFromArtifacts(hre, [ { - name: 'SystemDictator', + name: 'SystemDictatorProxy', + iface: 'SystemDictator', signerOrProvider: deployer, }, {