Commit 09e49577 authored by smartcontracts's avatar smartcontracts Committed by GitHub

feat(ctb): put dictator behind Proxy (#4206)

Puts the SystemDictator behind a standard Proxy contract. By putting the
SystemDictator behind a proxy, we can work our way out of pretty much
any error type. The owner of the proxy will be the controller as defined
in the deploy config.
parent 79a57977
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.15; 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 { L2OutputOracle } from "../L1/L2OutputOracle.sol";
import { OptimismPortal } from "../L1/OptimismPortal.sol"; import { OptimismPortal } from "../L1/OptimismPortal.sol";
import { L1CrossDomainMessenger } from "../L1/L1CrossDomainMessenger.sol"; import { L1CrossDomainMessenger } from "../L1/L1CrossDomainMessenger.sol";
...@@ -21,7 +23,7 @@ import { SystemConfig } from "../L1/SystemConfig.sol"; ...@@ -21,7 +23,7 @@ import { SystemConfig } from "../L1/SystemConfig.sol";
* system. The SystemDictator is designed to support both fresh network deployments and * system. The SystemDictator is designed to support both fresh network deployments and
* upgrades to existing pre-Bedrock systems. * upgrades to existing pre-Bedrock systems.
*/ */
contract SystemDictator is Ownable { contract SystemDictator is OwnableUpgradeable {
/** /**
* @notice Basic system configuration. * @notice Basic system configuration.
*/ */
...@@ -111,7 +113,7 @@ contract SystemDictator is Ownable { ...@@ -111,7 +113,7 @@ contract SystemDictator is Ownable {
/** /**
* @notice Current step; * @notice Current step;
*/ */
uint8 public currentStep = 1; uint8 public currentStep;
/** /**
* @notice Whether or not dynamic config has been set. * @notice Whether or not dynamic config has been set.
...@@ -142,8 +144,10 @@ contract SystemDictator is Ownable { ...@@ -142,8 +144,10 @@ contract SystemDictator is Ownable {
/** /**
* @param _config System configuration. * @param _config System configuration.
*/ */
constructor(DeployConfig memory _config) Ownable() { function initialize(DeployConfig memory _config) public initializer {
config = _config; config = _config;
currentStep = 1;
__Ownable_init();
_transferOwnership(config.globalConfig.controller); _transferOwnership(config.globalConfig.controller);
} }
......
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
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
import { ethers } from 'ethers' import { ethers } from 'ethers'
import { DeployFunction } from 'hardhat-deploy/dist/types' import { DeployFunction } from 'hardhat-deploy/dist/types'
import { awaitCondition } from '@eth-optimism/core-utils'
import '@eth-optimism/hardhat-deploy-config' import '@eth-optimism/hardhat-deploy-config'
import 'hardhat-deploy' import 'hardhat-deploy'
import { import {
deployAndVerifyAndThen,
assertDictatorConfig, assertDictatorConfig,
makeDictatorConfig, makeDictatorConfig,
getContractsFromArtifacts,
} from '../src/deploy-utils' } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => { const deployFn: DeployFunction = async (hre) => {
...@@ -46,17 +47,91 @@ const deployFn: DeployFunction = async (hre) => { ...@@ -46,17 +47,91 @@ const deployFn: DeployFunction = async (hre) => {
} }
} }
const config = await makeDictatorConfig(hre, controller, finalOwner, false) // Load the contracts we need to interact with.
await deployAndVerifyAndThen({ const [
hre, SystemDictator,
name: 'SystemDictator', SystemDictatorProxy,
args: [config], SystemDictatorProxyWithSigner,
postDeployAction: async (contract) => { SystemDictatorImpl,
await assertDictatorConfig(contract, config) ] = 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 = ['SystemDictator'] deployFn.tags = ['SystemDictatorImpl']
export default deployFn export default deployFn
...@@ -70,7 +70,8 @@ const deployFn: DeployFunction = async (hre) => { ...@@ -70,7 +70,8 @@ const deployFn: DeployFunction = async (hre) => {
L1ERC721Bridge, L1ERC721Bridge,
] = await getContractsFromArtifacts(hre, [ ] = await getContractsFromArtifacts(hre, [
{ {
name: 'SystemDictator', name: 'SystemDictatorProxy',
iface: 'SystemDictator',
signerOrProvider: deployer, signerOrProvider: deployer,
}, },
{ {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment