Commit 06da1613 authored by Matthew Slipper's avatar Matthew Slipper

ctb: Live migration utilities

Adds some utilities to make it easier to perform a live migration:

- If a live deployer isn't configured, the SystemDictator will populate transactions for each step and output a JSON containing the transaction's `to`, `data`, `value`, and `chainId` fields. This gives us the opportunity to simulate these transactions prior to executing them, and makes it easier to generate multisignature transactions.
- The dynamic oracle config step requires user input. To make this easier, I added a Hardhat task that configures the dynamic oracle config. It operates in two modes. In send mode it will send the transaction directly. This is useful for the Goerli migration. In no send mode it outputs the raw transaction hex for simulation/multisig purposes. The task will confirm all input before sending any transactions.
parent 36245294
...@@ -11,6 +11,7 @@ import { ...@@ -11,6 +11,7 @@ import {
assertContractVariable, assertContractVariable,
getContractsFromArtifacts, getContractsFromArtifacts,
getDeploymentAddress, getDeploymentAddress,
jsonifyTransaction,
} from '../src/deploy-utils' } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => { const deployFn: DeployFunction = async (hre) => {
...@@ -123,8 +124,14 @@ const deployFn: DeployFunction = async (hre) => { ...@@ -123,8 +124,14 @@ const deployFn: DeployFunction = async (hre) => {
console.log(`Setting AddressManager owner to MSD`) console.log(`Setting AddressManager owner to MSD`)
await AddressManager.transferOwnership(SystemDictator.address) await AddressManager.transferOwnership(SystemDictator.address)
} else { } else {
const tx = await AddressManager.populateTransaction.transferOwnership(
SystemDictator.address
)
console.log(`Please transfer AddressManager owner to MSD`) console.log(`Please transfer AddressManager owner to MSD`)
console.log(`AddressManager address: ${AddressManager.address}`)
console.log(`MSD address: ${SystemDictator.address}`) console.log(`MSD address: ${SystemDictator.address}`)
console.log(`JSON:`)
console.log(jsonifyTransaction(tx))
} }
// Wait for the ownership transfer to complete. // Wait for the ownership transfer to complete.
...@@ -151,8 +158,15 @@ const deployFn: DeployFunction = async (hre) => { ...@@ -151,8 +158,15 @@ const deployFn: DeployFunction = async (hre) => {
console.log(`Setting L1CrossDomainMessenger owner to MSD`) console.log(`Setting L1CrossDomainMessenger owner to MSD`)
await L1CrossDomainMessenger.transferOwnership(SystemDictator.address) await L1CrossDomainMessenger.transferOwnership(SystemDictator.address)
} else { } else {
const tx =
await L1CrossDomainMessenger.populateTransaction.transferOwnership(
SystemDictator.address
)
console.log(`Please transfer L1CrossDomainMessenger owner to MSD`) console.log(`Please transfer L1CrossDomainMessenger owner to MSD`)
console.log(`L1XDM address: ${L1CrossDomainMessenger.address}`)
console.log(`MSD address: ${SystemDictator.address}`) console.log(`MSD address: ${SystemDictator.address}`)
console.log(`JSON:`)
console.log(jsonifyTransaction(tx))
} }
// Wait for the ownership transfer to complete. // Wait for the ownership transfer to complete.
...@@ -179,8 +193,16 @@ const deployFn: DeployFunction = async (hre) => { ...@@ -179,8 +193,16 @@ const deployFn: DeployFunction = async (hre) => {
console.log(`Setting L1StandardBridge owner to MSD`) console.log(`Setting L1StandardBridge owner to MSD`)
await L1StandardBridgeProxyWithSigner.setOwner(SystemDictator.address) await L1StandardBridgeProxyWithSigner.setOwner(SystemDictator.address)
} else { } else {
const tx = await L1StandardBridgeProxy.populateTransaction.setOwner(
SystemDictator.address
)
console.log(`Please transfer L1StandardBridge (proxy) owner to MSD`) console.log(`Please transfer L1StandardBridge (proxy) owner to MSD`)
console.log(
`L1StandardBridgeProxy address: ${L1StandardBridgeProxy.address}`
)
console.log(`MSD address: ${SystemDictator.address}`) console.log(`MSD address: ${SystemDictator.address}`)
console.log(`JSON:`)
console.log(jsonifyTransaction(tx))
} }
// Wait for the ownership transfer to complete. // Wait for the ownership transfer to complete.
...@@ -209,8 +231,14 @@ const deployFn: DeployFunction = async (hre) => { ...@@ -209,8 +231,14 @@ const deployFn: DeployFunction = async (hre) => {
console.log(`Setting L1ERC721Bridge owner to MSD`) console.log(`Setting L1ERC721Bridge owner to MSD`)
await L1ERC721BridgeProxyWithSigner.changeAdmin(SystemDictator.address) await L1ERC721BridgeProxyWithSigner.changeAdmin(SystemDictator.address)
} else { } else {
const tx = await L1ERC721BridgeProxy.populateTransaction.changeAdmin(
SystemDictator.address
)
console.log(`Please transfer L1ERC721Bridge (proxy) owner to MSD`) console.log(`Please transfer L1ERC721Bridge (proxy) owner to MSD`)
console.log(`L1ERC721BridgeProxy address: ${L1ERC721BridgeProxy.address}`)
console.log(`MSD address: ${SystemDictator.address}`) console.log(`MSD address: ${SystemDictator.address}`)
console.log(`JSON:`)
console.log(jsonifyTransaction(tx))
} }
// Wait for the ownership transfer to complete. // Wait for the ownership transfer to complete.
...@@ -264,7 +292,11 @@ const deployFn: DeployFunction = async (hre) => { ...@@ -264,7 +292,11 @@ const deployFn: DeployFunction = async (hre) => {
console.log(`Executing step ${opts.step}...`) console.log(`Executing step ${opts.step}...`)
await SystemDictator[`step${opts.step}`]() await SystemDictator[`step${opts.step}`]()
} else { } else {
const tx = await SystemDictator.populateTransaction[`step${opts.step}`]()
console.log(`Please execute step ${opts.step}...`) console.log(`Please execute step ${opts.step}...`)
console.log(`MSD address: ${SystemDictator.address}`)
console.log(`JSON:`)
console.log(jsonifyTransaction(tx))
} }
// Wait for the step to complete. // Wait for the step to complete.
...@@ -566,7 +598,11 @@ const deployFn: DeployFunction = async (hre) => { ...@@ -566,7 +598,11 @@ const deployFn: DeployFunction = async (hre) => {
console.log(`Finalizing deployment...`) console.log(`Finalizing deployment...`)
await SystemDictator.finalize() await SystemDictator.finalize()
} else { } else {
const tx = await SystemDictator.populateTransaction.finalize()
console.log(`Please finalize deployment...`) console.log(`Please finalize deployment...`)
console.log(`MSD address: ${SystemDictator.address}`)
console.log(`JSON:`)
console.log(jsonifyTransaction(tx))
} }
await awaitCondition( await awaitCondition(
......
...@@ -276,3 +276,12 @@ export const getDeploymentAddress = async ( ...@@ -276,3 +276,12 @@ export const getDeploymentAddress = async (
const deployment = await hre.deployments.get(name) const deployment = await hre.deployments.get(name)
return deployment.address return deployment.address
} }
export const jsonifyTransaction = (tx: ethers.PopulatedTransaction): string => {
return JSON.stringify({
to: tx.to,
data: tx.data,
value: tx.value,
chainId: tx.chainId,
}, null, 2)
}
...@@ -7,3 +7,4 @@ import './validate-spacers' ...@@ -7,3 +7,4 @@ import './validate-spacers'
import './solidity' import './solidity'
import './accounts' import './accounts'
import './check-l2' import './check-l2'
import './update-dynamic-oracle-config'
import readline from 'readline'
import { task, types } from 'hardhat/config'
import { ethers, Wallet } from 'ethers'
import { getContractsFromArtifacts } from '../src/deploy-utils'
task('update-dynamic-oracle-config', 'Updates the dynamic oracle config.')
.addParam(
'l2OutputOracleStartingTimestamp',
'Starting timestamp for the L2 output oracle.',
null,
types.int
)
.addParam('noSend', 'Do not send the transaction.', true, types.boolean)
.addOptionalParam(
'privateKey',
'Private key to send transaction',
process.env.PRIVATE_KEY,
types.string
)
.setAction(async (args, hre) => {
const { l2OutputOracleStartingTimestamp, noSend, privateKey } = args
const wallet = new Wallet(privateKey, hre.ethers.provider)
const [SystemDictator] = await getContractsFromArtifacts(hre, [
{
name: 'SystemDictatorProxy',
iface: 'SystemDictator',
signerOrProvider: wallet,
},
])
const currStep = await SystemDictator.currentStep()
if (currStep !== 5) {
throw new Error(`Current step is ${currStep}, expected 5`)
}
if (await SystemDictator.dynamicConfigSet()) {
throw new Error('Dynamic config already set')
}
const l2OutputOracleStartingBlockNumber =
hre.deployConfig.l2OutputOracleStartingBlockNumber
console.log(
`This task will set the L2 output oracle's starting timestamp and block number.`
)
console.log(
`It can only be run once. Please carefully check the values below:`
)
console.log(
`L2OO starting block number: ${l2OutputOracleStartingBlockNumber}`
)
console.log(
`L2OO starting block timestamp: ${l2OutputOracleStartingTimestamp}`
)
await prompt('Press enter to continue...')
if (noSend) {
const tx =
await SystemDictator.populateTransaction.updateL2OutputOracleDynamicConfig(
{
l2OutputOracleStartingBlockNumber,
l2OutputOracleStartingTimestamp,
}
)
console.log(`Sending is disabled. Transaction data:`)
// Need to delete tx.from for Ethers to properly serialize the tx
delete tx.from
console.log(ethers.utils.serializeTransaction(tx))
console.log(`Calldata (for multisigs):`)
console.log(tx.data)
} else {
console.log(`Sending transaction...`)
const tx = await SystemDictator.updateL2OutputOracleDynamicConfig({
l2OutputOracleStartingBlockNumber,
l2OutputOracleStartingTimestamp,
})
console.log(
`Transaction sent with hash ${tx.hash}. Waiting for receipt...`
)
const receipt = await tx.wait(1)
console.log(`Transaction included in block ${receipt.blockNumber}`)
}
})
const prompt = async (question: string) => {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
})
return new Promise<void>((resolve) => {
rl.question(question, () => {
rl.close()
resolve()
})
})
}
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