Commit fb46010e authored by Andreas Bigger's avatar Andreas Bigger

Remove migration from deployment process.

parent c7949cb3
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import { OptimismPortal } from "../L1/OptimismPortal.sol";
/**
* @title PortalSender
* @notice The PortalSender is a simple intermediate contract that will transfer the balance of the
* L1StandardBridge to the OptimismPortal during the Bedrock migration.
*/
contract PortalSender {
/**
* @notice Address of the OptimismPortal contract.
*/
OptimismPortal public immutable PORTAL;
/**
* @param _portal Address of the OptimismPortal contract.
*/
constructor(OptimismPortal _portal) {
PORTAL = _portal;
}
/**
* @notice Sends balance of this contract to the OptimismPortal.
*/
function donate() public {
PORTAL.donateETH{ value: address(this).balance }();
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
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";
import { L1ERC721Bridge } from "../L1/L1ERC721Bridge.sol";
import { L1StandardBridge } from "../L1/L1StandardBridge.sol";
import { L1ChugSplashProxy } from "../legacy/L1ChugSplashProxy.sol";
import { AddressManager } from "../legacy/AddressManager.sol";
import { Proxy } from "../universal/Proxy.sol";
import { ProxyAdmin } from "../universal/ProxyAdmin.sol";
import { OptimismMintableERC20Factory } from "../universal/OptimismMintableERC20Factory.sol";
import { PortalSender } from "./PortalSender.sol";
import { SystemConfig } from "../L1/SystemConfig.sol";
import { ResourceMetering } from "../L1/ResourceMetering.sol";
import { Constants } from "../libraries/Constants.sol";
/**
* @title SystemDictator
* @notice The SystemDictator is responsible for coordinating the deployment of a full Bedrock
* system. The SystemDictator is designed to support both fresh network deployments and
* upgrades to existing pre-Bedrock systems.
*/
contract SystemDictator is OwnableUpgradeable {
/**
* @notice Basic system configuration.
*/
struct GlobalConfig {
AddressManager addressManager;
ProxyAdmin proxyAdmin;
address controller;
address finalOwner;
}
/**
* @notice Set of proxy addresses.
*/
struct ProxyAddressConfig {
address l2OutputOracleProxy;
address optimismPortalProxy;
address l1CrossDomainMessengerProxy;
address l1StandardBridgeProxy;
address optimismMintableERC20FactoryProxy;
address l1ERC721BridgeProxy;
address systemConfigProxy;
}
/**
* @notice Set of implementation addresses.
*/
struct ImplementationAddressConfig {
L2OutputOracle l2OutputOracleImpl;
OptimismPortal optimismPortalImpl;
L1CrossDomainMessenger l1CrossDomainMessengerImpl;
L1StandardBridge l1StandardBridgeImpl;
OptimismMintableERC20Factory optimismMintableERC20FactoryImpl;
L1ERC721Bridge l1ERC721BridgeImpl;
PortalSender portalSenderImpl;
SystemConfig systemConfigImpl;
}
/**
* @notice Dynamic L2OutputOracle config.
*/
struct L2OutputOracleDynamicConfig {
uint256 l2OutputOracleStartingBlockNumber;
uint256 l2OutputOracleStartingTimestamp;
}
/**
* @notice Values for the system config contract.
*/
struct SystemConfigConfig {
address owner;
uint256 overhead;
uint256 scalar;
bytes32 batcherHash;
uint64 gasLimit;
address unsafeBlockSigner;
ResourceMetering.ResourceConfig resourceConfig;
}
/**
* @notice Combined system configuration.
*/
struct DeployConfig {
GlobalConfig globalConfig;
ProxyAddressConfig proxyAddressConfig;
ImplementationAddressConfig implementationAddressConfig;
SystemConfigConfig systemConfigConfig;
}
/**
* @notice Step after which exit 1 can no longer be used.
*/
uint8 public constant EXIT_1_NO_RETURN_STEP = 3;
/**
* @notice Step where proxy ownership is transferred.
*/
uint8 public constant PROXY_TRANSFER_STEP = 4;
/**
* @notice System configuration.
*/
DeployConfig public config;
/**
* @notice Dynamic configuration for the L2OutputOracle.
*/
L2OutputOracleDynamicConfig public l2OutputOracleDynamicConfig;
/**
* @notice Dynamic configuration for the OptimismPortal. Determines
* if the system should be paused when initialized.
*/
bool public optimismPortalDynamicConfig;
/**
* @notice Current step;
*/
uint8 public currentStep;
/**
* @notice Whether or not dynamic config has been set.
*/
bool public dynamicConfigSet;
/**
* @notice Whether or not the deployment is finalized.
*/
bool public finalized;
/**
* @notice Whether or not the deployment has been exited.
*/
bool public exited;
/**
* @notice Address of the old L1CrossDomainMessenger implementation.
*/
address public oldL1CrossDomainMessenger;
/**
* @notice Checks that the current step is the expected step, then bumps the current step.
*
* @param _step Current step.
*/
modifier step(uint8 _step) {
require(!finalized, "SystemDictator: already finalized");
require(!exited, "SystemDictator: already exited");
require(currentStep == _step, "SystemDictator: incorrect step");
_;
currentStep++;
}
/**
* @notice Constructor required to ensure that the implementation of the SystemDictator is
* initialized upon deployment.
*/
constructor() {
ResourceMetering.ResourceConfig memory rcfg = Constants.DEFAULT_RESOURCE_CONFIG();
// Using this shorter variable as an alias for address(0) just prevents us from having to
// to use a new line for every single parameter.
address zero = address(0);
initialize(
DeployConfig(
GlobalConfig(AddressManager(zero), ProxyAdmin(zero), zero, zero),
ProxyAddressConfig(zero, zero, zero, zero, zero, zero, zero),
ImplementationAddressConfig(
L2OutputOracle(zero),
OptimismPortal(payable(zero)),
L1CrossDomainMessenger(zero),
L1StandardBridge(payable(zero)),
OptimismMintableERC20Factory(zero),
L1ERC721Bridge(zero),
PortalSender(zero),
SystemConfig(zero)
),
SystemConfigConfig(zero, 0, 0, bytes32(0), 0, zero, rcfg)
)
);
}
/**
* @param _config System configuration.
*/
function initialize(DeployConfig memory _config) public initializer {
config = _config;
currentStep = 1;
__Ownable_init();
_transferOwnership(config.globalConfig.controller);
}
/**
* @notice Allows the owner to update dynamic config.
*
* @param _l2OutputOracleDynamicConfig Dynamic L2OutputOracle config.
* @param _optimismPortalDynamicConfig Dynamic OptimismPortal config.
*/
function updateDynamicConfig(
L2OutputOracleDynamicConfig memory _l2OutputOracleDynamicConfig,
bool _optimismPortalDynamicConfig
) external onlyOwner {
l2OutputOracleDynamicConfig = _l2OutputOracleDynamicConfig;
optimismPortalDynamicConfig = _optimismPortalDynamicConfig;
dynamicConfigSet = true;
}
/**
* @notice Configures the ProxyAdmin contract.
*/
function step1() public onlyOwner step(1) {
// Set the AddressManager in the ProxyAdmin.
config.globalConfig.proxyAdmin.setAddressManager(config.globalConfig.addressManager);
// Set the L1CrossDomainMessenger to the RESOLVED proxy type.
config.globalConfig.proxyAdmin.setProxyType(
config.proxyAddressConfig.l1CrossDomainMessengerProxy,
ProxyAdmin.ProxyType.RESOLVED
);
// Set the implementation name for the L1CrossDomainMessenger.
config.globalConfig.proxyAdmin.setImplementationName(
config.proxyAddressConfig.l1CrossDomainMessengerProxy,
"OVM_L1CrossDomainMessenger"
);
// Set the L1StandardBridge to the CHUGSPLASH proxy type.
config.globalConfig.proxyAdmin.setProxyType(
config.proxyAddressConfig.l1StandardBridgeProxy,
ProxyAdmin.ProxyType.CHUGSPLASH
);
// Upgrade and initialize the SystemConfig so the Sequencer can start up.
config.globalConfig.proxyAdmin.upgradeAndCall(
payable(config.proxyAddressConfig.systemConfigProxy),
address(config.implementationAddressConfig.systemConfigImpl),
abi.encodeCall(
SystemConfig.initialize,
(
config.systemConfigConfig.owner,
config.systemConfigConfig.overhead,
config.systemConfigConfig.scalar,
config.systemConfigConfig.batcherHash,
config.systemConfigConfig.gasLimit,
config.systemConfigConfig.unsafeBlockSigner,
config.systemConfigConfig.resourceConfig
)
)
);
}
/**
* @notice Pauses the system by shutting down the L1CrossDomainMessenger and setting the
* deposit halt flag to tell the Sequencer's DTL to stop accepting deposits.
*/
function step2() public onlyOwner step(2) {
// Store the address of the old L1CrossDomainMessenger implementation. We will need this
// address in the case that we have to exit early.
oldL1CrossDomainMessenger = config.globalConfig.addressManager.getAddress(
"OVM_L1CrossDomainMessenger"
);
// Temporarily brick the L1CrossDomainMessenger by setting its implementation address to
// address(0) which will cause the ResolvedDelegateProxy to revert. Better than pausing
// the L1CrossDomainMessenger via pause() because it can be easily reverted.
config.globalConfig.addressManager.setAddress("OVM_L1CrossDomainMessenger", address(0));
// Set the DTL shutoff block, which will tell the DTL to stop syncing new deposits from the
// CanonicalTransactionChain. We do this by setting an address in the AddressManager
// because the DTL already has a reference to the AddressManager and this way we don't also
// need to give it a reference to the SystemDictator.
config.globalConfig.addressManager.setAddress(
"DTL_SHUTOFF_BLOCK",
address(uint160(block.number))
);
}
/**
* @notice Removes deprecated addresses from the AddressManager.
*/
function step3() public onlyOwner step(EXIT_1_NO_RETURN_STEP) {
// Remove all deprecated addresses from the AddressManager
string[17] memory deprecated = [
"OVM_CanonicalTransactionChain",
"OVM_L2CrossDomainMessenger",
"OVM_DecompressionPrecompileAddress",
"OVM_Sequencer",
"OVM_Proposer",
"OVM_ChainStorageContainer-CTC-batches",
"OVM_ChainStorageContainer-CTC-queue",
"OVM_CanonicalTransactionChain",
"OVM_StateCommitmentChain",
"OVM_BondManager",
"OVM_ExecutionManager",
"OVM_FraudVerifier",
"OVM_StateManagerFactory",
"OVM_StateTransitionerFactory",
"OVM_SafetyChecker",
"OVM_L1MultiMessageRelayer",
"BondManager"
];
for (uint256 i = 0; i < deprecated.length; i++) {
config.globalConfig.addressManager.setAddress(deprecated[i], address(0));
}
}
/**
* @notice Transfers system ownership to the ProxyAdmin.
*/
function step4() public onlyOwner step(PROXY_TRANSFER_STEP) {
// Transfer ownership of the AddressManager to the ProxyAdmin.
config.globalConfig.addressManager.transferOwnership(
address(config.globalConfig.proxyAdmin)
);
// Transfer ownership of the L1StandardBridge to the ProxyAdmin.
L1ChugSplashProxy(payable(config.proxyAddressConfig.l1StandardBridgeProxy)).setOwner(
address(config.globalConfig.proxyAdmin)
);
// Transfer ownership of the L1ERC721Bridge to the ProxyAdmin.
Proxy(payable(config.proxyAddressConfig.l1ERC721BridgeProxy)).changeAdmin(
address(config.globalConfig.proxyAdmin)
);
}
/**
* @notice Upgrades and initializes proxy contracts.
*/
function step5() public onlyOwner step(5) {
// Dynamic config must be set before we can initialize the L2OutputOracle.
require(dynamicConfigSet, "SystemDictator: dynamic oracle config is not yet initialized");
// Upgrade and initialize the L2OutputOracle.
config.globalConfig.proxyAdmin.upgradeAndCall(
payable(config.proxyAddressConfig.l2OutputOracleProxy),
address(config.implementationAddressConfig.l2OutputOracleImpl),
abi.encodeCall(
L2OutputOracle.initialize,
(
l2OutputOracleDynamicConfig.l2OutputOracleStartingBlockNumber,
l2OutputOracleDynamicConfig.l2OutputOracleStartingTimestamp
)
)
);
// Upgrade and initialize the OptimismPortal.
config.globalConfig.proxyAdmin.upgradeAndCall(
payable(config.proxyAddressConfig.optimismPortalProxy),
address(config.implementationAddressConfig.optimismPortalImpl),
abi.encodeCall(OptimismPortal.initialize, (optimismPortalDynamicConfig))
);
// Upgrade the L1CrossDomainMessenger.
config.globalConfig.proxyAdmin.upgrade(
payable(config.proxyAddressConfig.l1CrossDomainMessengerProxy),
address(config.implementationAddressConfig.l1CrossDomainMessengerImpl)
);
// Try to initialize the L1CrossDomainMessenger, only fail if it's already been initialized.
try
L1CrossDomainMessenger(config.proxyAddressConfig.l1CrossDomainMessengerProxy)
.initialize()
{
// L1CrossDomainMessenger is the one annoying edge case difference between existing
// networks and fresh networks because in existing networks it'll already be
// initialized but in fresh networks it won't be. Try/catch is the easiest and most
// consistent way to handle this because initialized() is not exposed publicly.
} catch Error(string memory reason) {
require(
keccak256(abi.encodePacked(reason)) ==
keccak256("Initializable: contract is already initialized"),
string.concat("SystemDictator: unexpected error initializing L1XDM: ", reason)
);
} catch {
revert("SystemDictator: unexpected error initializing L1XDM (no reason)");
}
// Transfer ETH from the L1StandardBridge to the OptimismPortal.
config.globalConfig.proxyAdmin.upgradeAndCall(
payable(config.proxyAddressConfig.l1StandardBridgeProxy),
address(config.implementationAddressConfig.portalSenderImpl),
abi.encodeCall(PortalSender.donate, ())
);
// Upgrade the L1StandardBridge (no initializer).
config.globalConfig.proxyAdmin.upgrade(
payable(config.proxyAddressConfig.l1StandardBridgeProxy),
address(config.implementationAddressConfig.l1StandardBridgeImpl)
);
// Upgrade the OptimismMintableERC20Factory (no initializer).
config.globalConfig.proxyAdmin.upgrade(
payable(config.proxyAddressConfig.optimismMintableERC20FactoryProxy),
address(config.implementationAddressConfig.optimismMintableERC20FactoryImpl)
);
// Upgrade the L1ERC721Bridge (no initializer).
config.globalConfig.proxyAdmin.upgrade(
payable(config.proxyAddressConfig.l1ERC721BridgeProxy),
address(config.implementationAddressConfig.l1ERC721BridgeImpl)
);
}
/**
* @notice Calls the first 2 steps of the migration process.
*/
function phase1() external onlyOwner {
step1();
step2();
}
/**
* @notice Calls the remaining steps of the migration process, and finalizes.
*/
function phase2() external onlyOwner {
step3();
step4();
step5();
finalize();
}
/**
* @notice Tranfers admin ownership to the final owner.
*/
function finalize() public onlyOwner {
// Transfer ownership of the ProxyAdmin to the final owner.
config.globalConfig.proxyAdmin.transferOwnership(config.globalConfig.finalOwner);
// Optionally also transfer AddressManager and L1StandardBridge if we still own it. Might
// happen if we're exiting early.
if (currentStep <= PROXY_TRANSFER_STEP) {
// Transfer ownership of the AddressManager to the final owner.
config.globalConfig.addressManager.transferOwnership(
address(config.globalConfig.finalOwner)
);
// Transfer ownership of the L1StandardBridge to the final owner.
L1ChugSplashProxy(payable(config.proxyAddressConfig.l1StandardBridgeProxy)).setOwner(
address(config.globalConfig.finalOwner)
);
// Transfer ownership of the L1ERC721Bridge to the final owner.
Proxy(payable(config.proxyAddressConfig.l1ERC721BridgeProxy)).changeAdmin(
address(config.globalConfig.finalOwner)
);
}
// Mark the deployment as finalized.
finalized = true;
}
/**
* @notice First exit point, can only be called before step 3 is executed.
*/
function exit1() external onlyOwner {
require(
currentStep == EXIT_1_NO_RETURN_STEP,
"SystemDictator: can only exit1 before step 3 is executed"
);
// Reset the L1CrossDomainMessenger to the old implementation.
config.globalConfig.addressManager.setAddress(
"OVM_L1CrossDomainMessenger",
oldL1CrossDomainMessenger
);
// Unset the DTL shutoff block which will allow the DTL to sync again.
config.globalConfig.addressManager.setAddress("DTL_SHUTOFF_BLOCK", address(0));
// Mark the deployment as exited.
exited = true;
}
}
...@@ -40,7 +40,7 @@ ...@@ -40,7 +40,7 @@
"governanceTokenOwner": "0xBcd4042DE499D14e55001CcbB24a551F3b954096", "governanceTokenOwner": "0xBcd4042DE499D14e55001CcbB24a551F3b954096",
"eip1559Denominator": 8, "eip1559Denominator": 8,
"eip1559Elasticity": 2, "eip1559Elasticity": 2,
"l1GenesisBlockTimestamp": "0x638a4554", "l1GenesisBlockTimestamp": "0x648a0943",
"l1StartingBlockTag": "earliest", "l1StartingBlockTag": "earliest",
"l2GenesisRegolithTimeOffset": "0x0" "l2GenesisRegolithTimeOffset": "0x0"
} }
\ No newline at end of file
...@@ -11,7 +11,6 @@ const deployFn: DeployFunction = async (hre) => { ...@@ -11,7 +11,6 @@ const deployFn: DeployFunction = async (hre) => {
contract: 'AddressManager', contract: 'AddressManager',
args: [], args: [],
postDeployAction: async (contract) => { postDeployAction: async (contract) => {
// Owner is temporarily set to the deployer.
await assertContractVariable(contract, 'owner', deployer) await assertContractVariable(contract, 'owner', deployer)
}, },
}) })
......
import { DeployFunction } from 'hardhat-deploy/dist/types'
import { assertContractVariable, deploy } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts()
await deploy({
hre,
name: 'ProxyAdmin',
args: [deployer],
postDeployAction: async (contract) => {
// Owner is temporarily set to the deployer. We transfer ownership of the ProxyAdmin to the
// SystemDictator before we trigger the dictator steps.
await assertContractVariable(contract, 'owner', deployer)
},
})
}
deployFn.tags = ['ProxyAdmin', 'setup', 'l1']
export default deployFn
import assert from 'assert'
import { DeployFunction } from 'hardhat-deploy/dist/types'
import {
assertContractVariable,
getContractsFromArtifacts,
deploy,
} from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts()
const [addressManager] = await getContractsFromArtifacts(hre, [
{
name: 'Lib_AddressManager',
signerOrProvider: deployer,
},
])
const proxyAdmin = await deploy({
hre,
name: 'ProxyAdmin',
args: [deployer],
postDeployAction: async (contract) => {
// Owner is temporarily set to the deployer.
await assertContractVariable(contract, 'owner', deployer)
},
})
let addressManagerOnProxy = await proxyAdmin.callStatic.addressManager()
if (addressManagerOnProxy !== addressManager.address) {
// Set the address manager on the proxy admin
console.log(
`ProxyAdmin(${proxyAdmin.address}).setAddressManager(${addressManager.address})`
)
const tx = await proxyAdmin.setAddressManager(addressManager.address)
await tx.wait()
}
// Validate the address manager was set correctly.
addressManagerOnProxy = await proxyAdmin.callStatic.addressManager()
assert(
addressManagerOnProxy === addressManager.address,
'AddressManager not set on ProxyAdmin'
)
}
deployFn.tags = ['ProxyAdmin', 'setup', 'l1']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types' import { DeployFunction } from 'hardhat-deploy/dist/types'
import { assertContractVariable, deploy } from '../src/deploy-utils' import {
assertContractVariable,
getDeploymentAddress,
deploy,
} from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => { const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts() const proxyAdmin = await getDeploymentAddress(hre, 'ProxyAdmin')
await deploy({ await deploy({
hre, hre,
name: 'Proxy__OVM_L1StandardBridge', name: 'Proxy__OVM_L1StandardBridge',
contract: 'L1ChugSplashProxy', contract: 'L1ChugSplashProxy',
args: [deployer], args: [proxyAdmin],
postDeployAction: async (contract) => { postDeployAction: async (contract) => {
await assertContractVariable(contract, 'getOwner', deployer) await assertContractVariable(contract, 'getOwner', proxyAdmin)
}, },
}) })
} }
......
import assert from 'assert'
import { DeployFunction } from 'hardhat-deploy/dist/types' import { DeployFunction } from 'hardhat-deploy/dist/types'
import { deploy, getDeploymentAddress } from '../src/deploy-utils' import { deploy, getContractsFromArtifacts } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => { const deployFn: DeployFunction = async (hre) => {
const addressManager = await getDeploymentAddress(hre, 'Lib_AddressManager') const { deployer } = await hre.getNamedAccounts()
const [addressManager] = await getContractsFromArtifacts(hre, [
{
name: 'Lib_AddressManager',
signerOrProvider: deployer,
},
])
// The name in the address manager for this contract
const name = 'OVM_L1CrossDomainMessenger'
console.log(
`Setting up ResolvedDelegateProxy with AddressManager(${addressManager.address})`
)
await deploy({ const l1CrossDomainMessengerProxy = await deploy({
hre, hre,
name: 'Proxy__OVM_L1CrossDomainMessenger', name: 'Proxy__OVM_L1CrossDomainMessenger',
contract: 'ResolvedDelegateProxy', contract: 'ResolvedDelegateProxy',
args: [addressManager, 'OVM_L1CrossDomainMessenger'], args: [addressManager.address, name],
}) })
let addr = await addressManager.getAddress(name)
if (addr !== l1CrossDomainMessengerProxy.address) {
console.log(
`AddressManager(${addressManager.address}).setAddress(${name}, ${l1CrossDomainMessengerProxy.address})`
)
const tx = await addressManager.setAddress(
name,
l1CrossDomainMessengerProxy.address
)
await tx.wait()
}
addr = await addressManager.getAddress(name)
assert(
addr === l1CrossDomainMessengerProxy.address,
`${name} not set correctly`
)
} }
deployFn.tags = ['L1CrossDomainMessengerProxy', 'setup', 'l1'] deployFn.tags = ['L1CrossDomainMessengerProxy', 'setup', 'l1']
......
import { DeployFunction } from 'hardhat-deploy/dist/types' import { DeployFunction } from 'hardhat-deploy/dist/types'
import { assertContractVariable, deploy } from '../src/deploy-utils' import {
assertContractVariable,
getDeploymentAddress,
deploy,
} from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => { const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts() const proxyAdmin = await getDeploymentAddress(hre, 'ProxyAdmin')
await deploy({ await deploy({
hre, hre,
name: 'L1ERC721BridgeProxy', name: 'L1ERC721BridgeProxy',
contract: 'Proxy', contract: 'Proxy',
args: [deployer], args: [proxyAdmin],
postDeployAction: async (contract) => { postDeployAction: async (contract) => {
await assertContractVariable(contract, 'admin', deployer) await assertContractVariable(contract, 'admin', proxyAdmin)
}, },
}) })
} }
......
import { DeployFunction } from 'hardhat-deploy/dist/types'
import { assertContractVariable, deploy } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts()
await deploy({
hre,
name: 'SystemDictatorProxy',
contract: 'Proxy',
args: [deployer],
postDeployAction: async (contract) => {
await assertContractVariable(contract, 'admin', deployer)
},
})
}
deployFn.tags = ['SystemDictatorProxy', 'setup', 'l1']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import {
assertContractVariable,
deploy,
getContractFromArtifact,
} from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const OptimismPortalProxy = await getContractFromArtifact(
hre,
'OptimismPortalProxy'
)
await deploy({
hre,
name: 'PortalSender',
args: [OptimismPortalProxy.address],
postDeployAction: async (contract) => {
await assertContractVariable(
contract,
'PORTAL',
OptimismPortalProxy.address
)
},
})
}
deployFn.tags = ['PortalSenderImpl', 'setup', 'l1']
export default deployFn
import assert from 'assert'
import { DeployFunction } from 'hardhat-deploy/dist/types'
import { getContractsFromArtifacts } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts()
const [proxyAdmin, addressManager] = await getContractsFromArtifacts(hre, [
{
name: 'ProxyAdmin',
signerOrProvider: deployer,
},
{
name: 'Lib_AddressManager',
signerOrProvider: deployer,
},
])
let addressManagerOwner = await addressManager.callStatic.owner()
if (addressManagerOwner !== proxyAdmin.address) {
console.log(
`AddressManager(${addressManager.address}).transferOwnership(${proxyAdmin.address})`
)
const tx = await addressManager.transferOwnership(proxyAdmin.address)
await tx.wait()
}
addressManagerOwner = await addressManager.callStatic.owner()
assert(
addressManagerOwner === proxyAdmin.address,
'AddressManager owner not set correctly'
)
}
deployFn.tags = ['AddressManager', 'l1']
export default deployFn
import assert from 'assert'
import { DeployFunction } from 'hardhat-deploy/dist/types'
import '@eth-optimism/hardhat-deploy-config'
import 'hardhat-deploy'
import { getContractsFromArtifacts } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts()
const [proxyAdmin, l2OutputOracleProxy, l2OutputOracleImpl] =
await getContractsFromArtifacts(hre, [
{
name: 'ProxyAdmin',
signerOrProvider: deployer,
},
{
name: 'L2OutputOracleProxy',
iface: 'L2OutputOracle',
signerOrProvider: deployer,
},
{
name: 'L2OutputOracle',
},
])
const startingBlockNumber = hre.deployConfig.l2OutputOracleStartingBlockNumber
let startingTimestamp = hre.deployConfig.l2OutputOracleStartingTimestamp
if (startingTimestamp < 0) {
const l1StartingBlock = await hre.ethers.provider.getBlock(
hre.deployConfig.l1StartingBlockTag
)
if (l1StartingBlock === null) {
throw new Error(
`Cannot fetch block tag ${hre.deployConfig.l1StartingBlockTag}`
)
}
startingTimestamp = l1StartingBlock.timestamp
}
try {
const tx = await proxyAdmin.upgradeAndCall(
l2OutputOracleProxy.address,
l2OutputOracleImpl.address,
l2OutputOracleProxy.interface.encodeFunctionData('initialize', [
startingBlockNumber,
startingTimestamp,
])
)
await tx.wait()
} catch (e) {
console.log('L2OutputOracle already initialized')
}
const fetchedStartingBlockNumber =
await l2OutputOracleProxy.callStatic.startingBlockNumber()
const fetchedStartingTimestamp =
await l2OutputOracleProxy.callStatic.startingTimestamp()
assert(fetchedStartingBlockNumber.toNumber() === startingBlockNumber)
assert(fetchedStartingTimestamp.toNumber() === startingTimestamp)
console.log('Updgraded and initialized L2OutputOracle')
const version = await l2OutputOracleProxy.callStatic.version()
console.log(`L2OutputOracle version: ${version}`)
}
deployFn.tags = ['L2OutputOracleInitialize', 'l1']
export default deployFn
import assert from 'assert'
import { DeployFunction } from 'hardhat-deploy/dist/types'
import '@eth-optimism/hardhat-deploy-config'
import 'hardhat-deploy'
import { getContractsFromArtifacts } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts()
const [proxyAdmin, optimismPortalProxy, optimismPortalImpl] =
await getContractsFromArtifacts(hre, [
{
name: 'ProxyAdmin',
signerOrProvider: deployer,
},
{
name: 'OptimismPortalProxy',
iface: 'OptimismPortal',
signerOrProvider: deployer,
},
{
name: 'OptimismPortal',
},
])
// Initialize the portal, setting paused to false
try {
const tx = await proxyAdmin.upgradeAndCall(
optimismPortalProxy.address,
optimismPortalImpl.address,
optimismPortalProxy.interface.encodeFunctionData('initialize', [false])
)
await tx.wait()
} catch (e) {
console.log('OptimismPortal already initialized')
}
const isPaused = await optimismPortalProxy.callStatic.paused()
assert(isPaused === false)
console.log('Upgraded and initialized OptimismPortal')
const version = await optimismPortalProxy.callStatic.version()
console.log(`OptimismPortal version: ${version}`)
}
deployFn.tags = ['OptimismPortalInitialize', 'l1']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import '@eth-optimism/hardhat-deploy-config'
import 'hardhat-deploy'
import { deploy } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
await deploy({
hre,
name: 'SystemDictator',
args: [],
})
}
deployFn.tags = ['SystemDictatorImpl', 'setup', 'l1']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import '@eth-optimism/hardhat-deploy-config'
import 'hardhat-deploy'
import { getContractsFromArtifacts } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts()
const [proxyAdmin, l1CrossDomainMessengerProxy, l1CrossDomainMessengerImpl] =
await getContractsFromArtifacts(hre, [
{
name: 'ProxyAdmin',
signerOrProvider: deployer,
},
{
name: 'Proxy__OVM_L1CrossDomainMessenger',
iface: 'L1CrossDomainMessenger',
signerOrProvider: deployer,
},
{
name: 'L1CrossDomainMessenger',
},
])
const proxyType = await proxyAdmin.callStatic.proxyType(
l1CrossDomainMessengerProxy.address
)
if (proxyType !== 2) {
console.log(
`ProxyAdmin(${proxyAdmin.address}).setProxyType(${l1CrossDomainMessengerProxy.address}, 2)`
)
// Set the L1CrossDomainMessenger to the RESOLVED proxy type.
const tx = await proxyAdmin.setProxyType(
l1CrossDomainMessengerProxy.address,
2
)
await tx.wait()
}
const name = 'OVM_L1CrossDomainMessenger'
const implementationName = proxyAdmin.implementationName(
l1CrossDomainMessengerImpl.address
)
if (implementationName !== name) {
console.log(
`ProxyAdmin(${proxyAdmin.address}).setImplementationName(${l1CrossDomainMessengerImpl.address}, 'OVM_L1CrossDomainMessenger')`
)
const tx = await proxyAdmin.setImplementationName(
l1CrossDomainMessengerProxy.address,
name
)
await tx.wait()
}
try {
const tx = await proxyAdmin.upgradeAndCall(
l1CrossDomainMessengerProxy.address,
l1CrossDomainMessengerImpl.address,
l1CrossDomainMessengerImpl.interface.encodeFunctionData('initialize')
)
await tx.wait()
} catch (e) {
console.log('L1CrossDomainMessenger already initialized')
}
const version = await l1CrossDomainMessengerProxy.callStatic.version()
console.log(`L1CrossDomainMessenger version: ${version}`)
console.log('Upgraded L1CrossDomainMessenger')
}
deployFn.tags = ['L1CrossDomainMessengerInitialize', 'l1']
export default deployFn
import assert from 'assert'
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 {
getContractsFromArtifacts,
getDeploymentAddress,
} from '../src/deploy-utils'
import { defaultResourceConfig } from '../src/constants'
const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts()
// 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 = {
globalConfig: {
proxyAdmin: await getDeploymentAddress(hre, 'ProxyAdmin'),
controller: hre.deployConfig.controller,
finalOwner: hre.deployConfig.finalSystemOwner,
addressManager: await getDeploymentAddress(hre, 'Lib_AddressManager'),
},
proxyAddressConfig: {
l2OutputOracleProxy: await getDeploymentAddress(
hre,
'L2OutputOracleProxy'
),
optimismPortalProxy: await getDeploymentAddress(
hre,
'OptimismPortalProxy'
),
l1CrossDomainMessengerProxy: await getDeploymentAddress(
hre,
'Proxy__OVM_L1CrossDomainMessenger'
),
l1StandardBridgeProxy: await getDeploymentAddress(
hre,
'Proxy__OVM_L1StandardBridge'
),
optimismMintableERC20FactoryProxy: await getDeploymentAddress(
hre,
'OptimismMintableERC20FactoryProxy'
),
l1ERC721BridgeProxy: await getDeploymentAddress(
hre,
'L1ERC721BridgeProxy'
),
systemConfigProxy: await getDeploymentAddress(hre, 'SystemConfigProxy'),
},
implementationAddressConfig: {
l2OutputOracleImpl: await getDeploymentAddress(hre, 'L2OutputOracle'),
optimismPortalImpl: await getDeploymentAddress(hre, 'OptimismPortal'),
l1CrossDomainMessengerImpl: await getDeploymentAddress(
hre,
'L1CrossDomainMessenger'
),
l1StandardBridgeImpl: await getDeploymentAddress(hre, 'L1StandardBridge'),
optimismMintableERC20FactoryImpl: await getDeploymentAddress(
hre,
'OptimismMintableERC20Factory'
),
l1ERC721BridgeImpl: await getDeploymentAddress(hre, 'L1ERC721Bridge'),
portalSenderImpl: await getDeploymentAddress(hre, 'PortalSender'),
systemConfigImpl: await getDeploymentAddress(hre, 'SystemConfig'),
},
systemConfigConfig: {
owner: hre.deployConfig.finalSystemOwner,
overhead: hre.deployConfig.gasPriceOracleOverhead,
scalar: hre.deployConfig.gasPriceOracleScalar,
batcherHash: hre.ethers.utils.hexZeroPad(
hre.deployConfig.batchSenderAddress,
32
),
gasLimit: hre.deployConfig.l2GenesisBlockGasLimit,
unsafeBlockSigner: hre.deployConfig.p2pSequencerAddress,
// The resource config is not exposed to the end user
// to simplify deploy config. It may be introduced in the future.
resourceConfig: defaultResourceConfig,
},
}
// 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.
const dictatorConfig = await SystemDictator.config()
for (const [outerConfigKey, outerConfigValue] of Object.entries(config)) {
for (const [innerConfigKey, innerConfigValue] of Object.entries(
outerConfigValue
)) {
let have = dictatorConfig[outerConfigKey][innerConfigKey]
let want = innerConfigValue as any
if (ethers.utils.isAddress(want)) {
want = want.toLowerCase()
have = have.toLowerCase()
} else if (typeof want === 'number') {
want = ethers.BigNumber.from(want)
have = ethers.BigNumber.from(have)
assert(
want.eq(have),
`incorrect config for ${outerConfigKey}.${innerConfigKey}. Want: ${want}, have: ${have}`
)
return
}
assert(
want === have,
`incorrect config for ${outerConfigKey}.${innerConfigKey}. Want: ${want}, have: ${have}`
)
}
}
}
// Update the owner if necessary.
if (
(await SystemDictatorProxy.callStatic.admin({
from: ethers.constants.AddressZero,
})) !== hre.deployConfig.controller
) {
console.log('Transferring ownership of the SystemDictator proxy...')
// Transfer ownership to the controller address.
await SystemDictatorProxyWithSigner.changeAdmin(hre.deployConfig.controller)
// Wait for the transaction to execute properly.
await awaitCondition(
async () => {
return (
(await SystemDictatorProxy.callStatic.admin({
from: ethers.constants.AddressZero,
})) === hre.deployConfig.controller
)
},
30000,
1000
)
}
}
deployFn.tags = ['SystemDictatorImpl', 'setup', 'l1']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import { BigNumber } from 'ethers'
import '@eth-optimism/hardhat-deploy-config'
import 'hardhat-deploy'
import { defaultResourceConfig } from '../src/constants'
import { getContractsFromArtifacts } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts()
const [proxyAdmin, systemConfigProxy, systemConfigImpl] =
await getContractsFromArtifacts(hre, [
{
name: 'ProxyAdmin',
signerOrProvider: deployer,
},
{
name: 'SystemConfigProxy',
iface: 'SystemConfig',
signerOrProvider: deployer,
},
{
name: 'SystemConfig',
},
])
const batcherHash = hre.ethers.utils
.hexZeroPad(hre.deployConfig.batchSenderAddress, 32)
.toLowerCase()
const l2GenesisBlockGasLimit = BigNumber.from(
hre.deployConfig.l2GenesisBlockGasLimit
)
const l2GasLimitLowerBound = BigNumber.from(
defaultResourceConfig.systemTxMaxGas +
defaultResourceConfig.maxResourceLimit
)
if (l2GenesisBlockGasLimit.lt(l2GasLimitLowerBound)) {
throw new Error(
`L2 genesis block gas limit must be at least ${l2GasLimitLowerBound}`
)
}
try {
const tx = await proxyAdmin.upgradeAndCall(
systemConfigProxy.address,
systemConfigImpl.address,
systemConfigImpl.interface.encodeFunctionData('initialize', [
hre.deployConfig.finalSystemOwner,
hre.deployConfig.gasPriceOracleOverhead,
hre.deployConfig.gasPriceOracleScalar,
batcherHash,
l2GenesisBlockGasLimit,
hre.deployConfig.p2pSequencerAddress,
defaultResourceConfig,
])
)
await tx.wait()
} catch (e) {
console.log('SystemConfig already initialized')
console.log(e)
}
const version = await systemConfigProxy.callStatic.version()
console.log(`SystemConfig version: ${version}`)
console.log('Upgraded SystemConfig')
}
deployFn.tags = ['SystemConfigInitialize', 'l1']
export default deployFn
import assert from 'assert'
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 '@nomiclabs/hardhat-ethers'
import {
assertContractVariable,
getContractsFromArtifacts,
getDeploymentAddress,
doOwnershipTransfer,
doPhase,
liveDeployer,
} from '../src/deploy-utils'
const uint128Max = ethers.BigNumber.from('0xffffffffffffffffffffffffffffffff')
const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts()
// Set up required contract references.
const [
SystemDictator,
ProxyAdmin,
AddressManager,
L1StandardBridgeProxy,
L1StandardBridgeProxyWithSigner,
L1ERC721BridgeProxy,
L1ERC721BridgeProxyWithSigner,
SystemConfigProxy,
] = await getContractsFromArtifacts(hre, [
{
name: 'SystemDictatorProxy',
iface: 'SystemDictator',
signerOrProvider: deployer,
},
{
name: 'ProxyAdmin',
signerOrProvider: deployer,
},
{
name: 'Lib_AddressManager',
signerOrProvider: deployer,
},
{
name: 'Proxy__OVM_L1StandardBridge',
},
{
name: 'Proxy__OVM_L1StandardBridge',
signerOrProvider: deployer,
},
{
name: 'L1ERC721BridgeProxy',
},
{
name: 'L1ERC721BridgeProxy',
signerOrProvider: deployer,
},
{
name: 'SystemConfigProxy',
iface: 'SystemConfig',
signerOrProvider: deployer,
},
])
// If we have the key for the controller then we don't need to wait for external txns.
// Set the DISABLE_LIVE_DEPLOYER=true in the env to ensure the script will pause to simulate scenarios
// where the controller is not the deployer.
const isLiveDeployer = await liveDeployer({
hre,
disabled: process.env.DISABLE_LIVE_DEPLOYER,
})
// Transfer ownership of the ProxyAdmin to the SystemDictator.
if ((await ProxyAdmin.owner()) !== SystemDictator.address) {
await doOwnershipTransfer({
isLiveDeployer,
proxy: ProxyAdmin,
name: 'ProxyAdmin',
transferFunc: 'transferOwnership',
dictator: SystemDictator,
})
}
// We don't need to transfer proxy addresses if we're already beyond the proxy transfer step.
const needsProxyTransfer =
(await SystemDictator.currentStep()) <=
(await SystemDictator.PROXY_TRANSFER_STEP())
// Transfer ownership of the AddressManager to SystemDictator.
if (
needsProxyTransfer &&
(await AddressManager.owner()) !== SystemDictator.address
) {
await doOwnershipTransfer({
isLiveDeployer,
proxy: AddressManager,
name: 'AddressManager',
transferFunc: 'transferOwnership',
dictator: SystemDictator,
})
} else {
console.log(`AddressManager already owned by the SystemDictator`)
}
// Transfer ownership of the L1StandardBridge (proxy) to SystemDictator.
if (
needsProxyTransfer &&
(await L1StandardBridgeProxy.callStatic.getOwner({
from: ethers.constants.AddressZero,
})) !== SystemDictator.address
) {
await doOwnershipTransfer({
isLiveDeployer,
proxy: L1StandardBridgeProxyWithSigner,
name: 'L1StandardBridgeProxy',
transferFunc: 'setOwner',
dictator: SystemDictator,
})
} else {
console.log(`L1StandardBridge already owned by MSD`)
}
// Transfer ownership of the L1ERC721Bridge (proxy) to SystemDictator.
if (
needsProxyTransfer &&
(await L1ERC721BridgeProxy.callStatic.admin({
from: ethers.constants.AddressZero,
})) !== SystemDictator.address
) {
await doOwnershipTransfer({
isLiveDeployer,
proxy: L1ERC721BridgeProxyWithSigner,
name: 'L1ERC721BridgeProxy',
transferFunc: 'changeAdmin',
dictator: SystemDictator,
})
} else {
console.log(`L1ERC721Bridge already owned by MSD`)
}
// Wait for the ownership transfers to complete before continuing.
await awaitCondition(
async (): Promise<boolean> => {
const proxyAdminOwner = await ProxyAdmin.owner()
const addressManagerOwner = await AddressManager.owner()
const l1StandardBridgeOwner =
await L1StandardBridgeProxy.callStatic.getOwner({
from: ethers.constants.AddressZero,
})
const l1Erc721BridgeOwner = await L1ERC721BridgeProxy.callStatic.admin({
from: ethers.constants.AddressZero,
})
return (
proxyAdminOwner === SystemDictator.address &&
addressManagerOwner === SystemDictator.address &&
l1StandardBridgeOwner === SystemDictator.address &&
l1Erc721BridgeOwner === SystemDictator.address
)
},
5000,
1000
)
await doPhase({
isLiveDeployer,
SystemDictator,
phase: 1,
message: `
Phase 1 includes the following steps:
Step 1 will configure the ProxyAdmin contract, you can safely execute this step at any time
without impacting the functionality of the rest of the system.
Step 2 will stop deposits and withdrawals via the L1CrossDomainMessenger and will stop the
DTL from syncing new deposits via the CTC, effectively shutting down the legacy system. Once
this step has been executed, you should immediately begin the L2 migration process. If you
need to restart the system, run exit1() followed by finalize().
`,
checks: async () => {
// Step 1 checks
await assertContractVariable(
ProxyAdmin,
'addressManager',
AddressManager.address
)
assert(
(await ProxyAdmin.implementationName(
getDeploymentAddress(hre, 'Proxy__OVM_L1CrossDomainMessenger')
)) === 'OVM_L1CrossDomainMessenger'
)
assert(
(await ProxyAdmin.proxyType(
getDeploymentAddress(hre, 'Proxy__OVM_L1CrossDomainMessenger')
)) === 2
)
assert(
(await ProxyAdmin.proxyType(
getDeploymentAddress(hre, 'Proxy__OVM_L1StandardBridge')
)) === 1
)
// Check the SystemConfig was initialized properly.
await assertContractVariable(
SystemConfigProxy,
'owner',
hre.deployConfig.finalSystemOwner
)
await assertContractVariable(
SystemConfigProxy,
'overhead',
hre.deployConfig.gasPriceOracleOverhead
)
await assertContractVariable(
SystemConfigProxy,
'scalar',
hre.deployConfig.gasPriceOracleScalar
)
await assertContractVariable(
SystemConfigProxy,
'batcherHash',
ethers.utils.hexZeroPad(
hre.deployConfig.batchSenderAddress.toLowerCase(),
32
)
)
await assertContractVariable(
SystemConfigProxy,
'gasLimit',
hre.deployConfig.l2GenesisBlockGasLimit
)
const config = await SystemConfigProxy.resourceConfig()
assert(config.maxResourceLimit === 20_000_000)
assert(config.elasticityMultiplier === 10)
assert(config.baseFeeMaxChangeDenominator === 8)
assert(config.systemTxMaxGas === 1_000_000)
assert(ethers.utils.parseUnits('1', 'gwei').eq(config.minimumBaseFee))
assert(config.maximumBaseFee.eq(uint128Max))
// Step 2 checks
const messenger = await AddressManager.getAddress(
'OVM_L1CrossDomainMessenger'
)
assert(messenger === ethers.constants.AddressZero)
},
})
}
deployFn.tags = ['SystemDictatorSteps', 'phase1', 'l1']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import '@eth-optimism/hardhat-deploy-config'
import 'hardhat-deploy'
import { getContractsFromArtifacts } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts()
const [proxyAdmin, l1StandardBridgeProxy, l1StandardBridgeImpl] =
await getContractsFromArtifacts(hre, [
{
name: 'ProxyAdmin',
signerOrProvider: deployer,
},
{
name: 'Proxy__OVM_L1StandardBridge',
iface: 'L1StandardBridge',
signerOrProvider: deployer,
},
{
name: 'L1StandardBridge',
},
])
const proxyType = await proxyAdmin.callStatic.proxyType(
l1StandardBridgeProxy.address
)
if (proxyType !== 1) {
console.log(
`ProxyAdmin(${proxyAdmin.address}).setProxyType(${l1StandardBridgeProxy.address}, 1)`
)
// Set the L1StandardBridge to the UPGRADEABLE proxy type.
const tx = await proxyAdmin.setProxyType(l1StandardBridgeProxy.address, 1)
await tx.wait()
}
try {
const tx = await proxyAdmin.upgrade(
l1StandardBridgeProxy.address,
l1StandardBridgeImpl.address
)
await tx.wait()
} catch (e) {
console.log('L1StandardBridge already initialized')
}
const version = await l1StandardBridgeProxy.callStatic.version()
console.log(`L1StandardBridge version: ${version}`)
console.log('Upgraded L1StandardBridge')
}
deployFn.tags = ['L1StandardBridgeInitialize', 'l1']
export default deployFn
import assert from 'assert'
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 '@nomiclabs/hardhat-ethers'
import {
assertContractVariable,
getContractsFromArtifacts,
printJsonTransaction,
isStep,
printTenderlySimulationLink,
printCastCommand,
liveDeployer,
doPhase,
isStartOfPhase,
} from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts()
// Set up required contract references.
const [
SystemDictator,
ProxyAdmin,
AddressManager,
L1CrossDomainMessenger,
L1StandardBridgeProxy,
L1StandardBridge,
L2OutputOracle,
OptimismPortal,
OptimismMintableERC20Factory,
L1ERC721BridgeProxy,
L1ERC721Bridge,
] = await getContractsFromArtifacts(hre, [
{
name: 'SystemDictatorProxy',
iface: 'SystemDictator',
signerOrProvider: deployer,
},
{
name: 'ProxyAdmin',
signerOrProvider: deployer,
},
{
name: 'Lib_AddressManager',
signerOrProvider: deployer,
},
{
name: 'Proxy__OVM_L1CrossDomainMessenger',
iface: 'L1CrossDomainMessenger',
signerOrProvider: deployer,
},
{
name: 'Proxy__OVM_L1StandardBridge',
},
{
name: 'Proxy__OVM_L1StandardBridge',
iface: 'L1StandardBridge',
signerOrProvider: deployer,
},
{
name: 'L2OutputOracleProxy',
iface: 'L2OutputOracle',
signerOrProvider: deployer,
},
{
name: 'OptimismPortalProxy',
iface: 'OptimismPortal',
signerOrProvider: deployer,
},
{
name: 'OptimismMintableERC20FactoryProxy',
iface: 'OptimismMintableERC20Factory',
signerOrProvider: deployer,
},
{
name: 'L1ERC721BridgeProxy',
},
{
name: 'L1ERC721BridgeProxy',
iface: 'L1ERC721Bridge',
signerOrProvider: deployer,
},
])
// If we have the key for the controller then we don't need to wait for external txns.
// Set the DISABLE_LIVE_DEPLOYER=true in the env to ensure the script will pause to simulate scenarios
// where the controller is not the deployer.
const isLiveDeployer = await liveDeployer({
hre,
disabled: process.env.DISABLE_LIVE_DEPLOYER,
})
// Make sure the dynamic system configuration has been set.
if (
(await isStartOfPhase(SystemDictator, 2)) &&
!(await SystemDictator.dynamicConfigSet())
) {
console.log(`
You must now set the dynamic L2OutputOracle configuration by calling the function
updateL2OutputOracleDynamicConfig. You will need to provide the
l2OutputOracleStartingBlockNumber and the l2OutputOracleStartingTimestamp which can both be
found by querying the last finalized block in the L2 node.
`)
if (isLiveDeployer) {
console.log(`Updating dynamic oracle config...`)
// Use default starting time if not provided
let deployL2StartingTimestamp =
hre.deployConfig.l2OutputOracleStartingTimestamp
if (deployL2StartingTimestamp < 0) {
const l1StartingBlock = await hre.ethers.provider.getBlock(
hre.deployConfig.l1StartingBlockTag
)
if (l1StartingBlock === null) {
throw new Error(
`Cannot fetch block tag ${hre.deployConfig.l1StartingBlockTag}`
)
}
deployL2StartingTimestamp = l1StartingBlock.timestamp
}
await SystemDictator.updateDynamicConfig(
{
l2OutputOracleStartingBlockNumber:
hre.deployConfig.l2OutputOracleStartingBlockNumber,
l2OutputOracleStartingTimestamp: deployL2StartingTimestamp,
},
false // do not pause the the OptimismPortal when initializing
)
} else {
// pause the OptimismPortal when initializing
const optimismPortalPaused = true
const tx = await SystemDictator.populateTransaction.updateDynamicConfig(
{
l2OutputOracleStartingBlockNumber:
hre.deployConfig.l2OutputOracleStartingBlockNumber,
l2OutputOracleStartingTimestamp:
hre.deployConfig.l2OutputOracleStartingTimestamp,
},
optimismPortalPaused
)
console.log(`Please update dynamic oracle config...`)
console.log(
JSON.stringify(
{
l2OutputOracleStartingBlockNumber:
hre.deployConfig.l2OutputOracleStartingBlockNumber,
l2OutputOracleStartingTimestamp:
hre.deployConfig.l2OutputOracleStartingTimestamp,
optimismPortalPaused,
},
null,
2
)
)
console.log(`MSD address: ${SystemDictator.address}`)
printJsonTransaction(tx)
printCastCommand(tx)
await printTenderlySimulationLink(SystemDictator.provider, tx)
}
await awaitCondition(
async () => {
return SystemDictator.dynamicConfigSet()
},
5000,
1000
)
}
await doPhase({
isLiveDeployer,
SystemDictator,
phase: 2,
message: `
Phase 2 includes the following steps:
Step 3 will clear out some legacy state from the AddressManager. Once you execute this step,
you WILL NOT BE ABLE TO RESTART THE SYSTEM using exit1(). You should confirm that the L2
system is entirely operational before executing this step.
Step 4 will transfer ownership of the AddressManager and L1StandardBridge to the ProxyAdmin.
Step 5 will initialize all Bedrock contracts. After this step is executed, the OptimismPortal
will be open for deposits but withdrawals will be paused if deploying a production network.
The Proposer will also be able to submit L2 outputs to the L2OutputOracle.
Lastly the finalize step will be executed. This will transfer ownership of the ProxyAdmin to
the final system owner as specified in the deployment configuration.
`,
checks: async () => {
// Step 3 checks
const deads = [
'OVM_CanonicalTransactionChain',
'OVM_L2CrossDomainMessenger',
'OVM_DecompressionPrecompileAddress',
'OVM_Sequencer',
'OVM_Proposer',
'OVM_ChainStorageContainer-CTC-batches',
'OVM_ChainStorageContainer-CTC-queue',
'OVM_CanonicalTransactionChain',
'OVM_StateCommitmentChain',
'OVM_BondManager',
'OVM_ExecutionManager',
'OVM_FraudVerifier',
'OVM_StateManagerFactory',
'OVM_StateTransitionerFactory',
'OVM_SafetyChecker',
'OVM_L1MultiMessageRelayer',
'BondManager',
]
for (const dead of deads) {
const addr = await AddressManager.getAddress(dead)
assert(addr === ethers.constants.AddressZero)
}
// Step 4 checks
await assertContractVariable(AddressManager, 'owner', ProxyAdmin.address)
assert(
(await L1StandardBridgeProxy.callStatic.getOwner({
from: ethers.constants.AddressZero,
})) === ProxyAdmin.address
)
assert(
(await L1ERC721BridgeProxy.callStatic.admin({
from: ProxyAdmin.address,
})) === ProxyAdmin.address
)
// Step 5 checks
// Check L2OutputOracle was initialized properly.
await assertContractVariable(
L2OutputOracle,
'latestBlockNumber',
hre.deployConfig.l2OutputOracleStartingBlockNumber
)
// Check OptimismPortal was initialized properly.
await assertContractVariable(
OptimismPortal,
'l2Sender',
'0x000000000000000000000000000000000000dEaD'
)
const resourceParams = await OptimismPortal.params()
assert(
resourceParams.prevBaseFee.eq(ethers.utils.parseUnits('1', 'gwei')),
`OptimismPortal was not initialized with the correct initial base fee`
)
assert(
resourceParams.prevBoughtGas.eq(0),
`OptimismPortal was not initialized with the correct initial bought gas`
)
assert(
!resourceParams.prevBlockNum.eq(0),
`OptimismPortal was not initialized with the correct initial block number`
)
assert(
(await hre.ethers.provider.getBalance(L1StandardBridge.address)).eq(0)
)
if (isLiveDeployer) {
await assertContractVariable(OptimismPortal, 'paused', false)
} else {
await assertContractVariable(OptimismPortal, 'paused', true)
}
// Check L1CrossDomainMessenger was initialized properly.
try {
await L1CrossDomainMessenger.xDomainMessageSender()
assert(false, `L1CrossDomainMessenger was not initialized properly`)
} catch (err) {
// Expected.
}
// Check L1StandardBridge was initialized properly.
await assertContractVariable(
L1StandardBridge,
'messenger',
L1CrossDomainMessenger.address
)
assert(
(await hre.ethers.provider.getBalance(L1StandardBridge.address)).eq(0)
)
// Check OptimismMintableERC20Factory was initialized properly.
await assertContractVariable(
OptimismMintableERC20Factory,
'BRIDGE',
L1StandardBridge.address
)
// Check L1ERC721Bridge was initialized properly.
await assertContractVariable(
L1ERC721Bridge,
'messenger',
L1CrossDomainMessenger.address
)
// finalize checks
await assertContractVariable(
ProxyAdmin,
'owner',
hre.deployConfig.finalSystemOwner
)
},
})
// Step 6 unpauses the OptimismPortal.
if (await isStep(SystemDictator, 6)) {
console.log(`
Unpause the OptimismPortal. The GUARDIAN account should be used. In practice
this is the multisig. In test networks, the OptimismPortal is initialized
without being paused.
`)
if (isLiveDeployer) {
console.log('WARNING: OptimismPortal configured to not be paused')
console.log('This should only happen for test environments')
await assertContractVariable(OptimismPortal, 'paused', false)
} else {
const tx = await OptimismPortal.populateTransaction.unpause()
console.log(`Please unpause the OptimismPortal...`)
console.log(`OptimismPortal address: ${OptimismPortal.address}`)
printJsonTransaction(tx)
printCastCommand(tx)
await printTenderlySimulationLink(SystemDictator.provider, tx)
}
await awaitCondition(
async () => {
const paused = await OptimismPortal.paused()
return !paused
},
5000,
1000
)
await assertContractVariable(OptimismPortal, 'paused', false)
await awaitCondition(
async () => {
return SystemDictator.finalized()
},
5000,
1000
)
}
}
deployFn.tags = ['SystemDictatorSteps', 'phase2', 'l1']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import '@eth-optimism/hardhat-deploy-config'
import 'hardhat-deploy'
import { getContractsFromArtifacts } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts()
const [proxyAdmin, l1ERC721BridgeProxy, l1ERC721BridgeImpl] =
await getContractsFromArtifacts(hre, [
{
name: 'ProxyAdmin',
signerOrProvider: deployer,
},
{
name: 'L1ERC721BridgeProxy',
iface: 'L1ERC721Bridge',
signerOrProvider: deployer,
},
{
name: 'L1ERC721Bridge',
},
])
try {
const tx = await proxyAdmin.upgrade(
l1ERC721BridgeProxy.address,
l1ERC721BridgeImpl.address
)
await tx.wait()
} catch (e) {
console.log('L1ERC721Bridge already initialized')
}
const version = await l1ERC721BridgeProxy.callStatic.version()
console.log(`L1ERC721Bridge version: ${version}`)
console.log('Upgraded L1ERC721Bridge')
}
deployFn.tags = ['L1ERC721BridgeInitialize', 'l1']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import '@eth-optimism/hardhat-deploy-config'
import 'hardhat-deploy'
import { getContractsFromArtifacts } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts()
const [proxyAdmin, mintableERC20FactoryProxy, mintableERC20FactoryImpl] =
await getContractsFromArtifacts(hre, [
{
name: 'ProxyAdmin',
signerOrProvider: deployer,
},
{
name: 'OptimismMintableERC20FactoryProxy',
iface: 'OptimismMintableERC20Factory',
signerOrProvider: deployer,
},
{
name: 'OptimismMintableERC20Factory',
},
])
try {
const tx = await proxyAdmin.upgrade(
mintableERC20FactoryProxy.address,
mintableERC20FactoryImpl.address
)
await tx.wait()
} catch (e) {
console.log('OptimismMintableERC20Factory already initialized')
}
const version = await mintableERC20FactoryProxy.callStatic.version()
console.log(`OptimismMintableERC20Factory version: ${version}`)
console.log('Upgraded OptimismMintableERC20Factory')
}
deployFn.tags = ['OptimismMintableERC20FactoryInitialize', 'l1']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import { awaitCondition } from '@eth-optimism/core-utils'
import '@eth-optimism/hardhat-deploy-config'
import 'hardhat-deploy'
import { getContractsFromArtifacts } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts()
const [proxyAdmin] = await getContractsFromArtifacts(hre, [
{
name: 'ProxyAdmin',
signerOrProvider: deployer,
},
])
const finalOwner = hre.deployConfig.finalSystemOwner
const proxyAdminOwner = await proxyAdmin.callStatic.owner()
if (proxyAdminOwner !== finalOwner) {
const tx = await proxyAdmin.transferOwnership(finalOwner)
await tx.wait()
await awaitCondition(
async () => {
return (await proxyAdmin.callStatic.owner()) === finalOwner
},
30000,
1000
)
}
}
deployFn.tags = ['ProxyAdmin', 'transferOwnership', 'l1']
export default deployFn
...@@ -37,7 +37,7 @@ export const deploy = async ({ ...@@ -37,7 +37,7 @@ export const deploy = async ({
contract?: string contract?: string
iface?: string iface?: string
postDeployAction?: (contract: Contract) => Promise<void> postDeployAction?: (contract: Contract) => Promise<void>
}) => { }): Promise<Contract> => {
const { deployer } = await hre.getNamedAccounts() const { deployer } = await hre.getNamedAccounts()
// Hardhat deploy will usually do this check for us, but currently doesn't also consider // Hardhat deploy will usually do this check for us, but currently doesn't also consider
......
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