Commit 56e2b726 authored by Mark Tyneway's avatar Mark Tyneway Committed by GitHub

Merge pull request #4232 from ethereum-optimism/feat/check-l2

contracts-bedrock: add post check L2 hh task
parents 1807c229 4eea5e6b
......@@ -29,6 +29,13 @@ const config: HardhatUserConfig = {
'ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80',
],
},
devnetL2: {
live: false,
url: process.env.RPC_URL || 'http://localhost:9545',
accounts: [
'ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80',
],
},
hivenet: {
chainId: Number(process.env.CHAIN_ID),
url: process.env.L1_RPC || '',
......
import assert from 'assert'
import { task } from 'hardhat/config'
import '@nomiclabs/hardhat-ethers'
import 'hardhat-deploy'
import { HardhatRuntimeEnvironment } from 'hardhat/types'
import { Contract } from 'ethers'
import { predeploys } from '../src'
// expectedSemver is the semver version of the contracts
// deployed at bedrock deployment
const expectedSemver = '0.0.1'
const implSlot =
'0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc'
const adminSlot =
'0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103'
const prefix = '0x420000000000000000000000000000000000'
const logLoud = () => {
console.log(' !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!')
}
const yell = (msg: string) => {
logLoud()
console.log(msg)
logLoud()
}
// checkPredeploys will ensure that all of the predeploys are set
const checkPredeploys = async (hre: HardhatRuntimeEnvironment) => {
console.log('Checking predeploys are configured correctly')
for (let i = 0; i < 2048; i++) {
const num = hre.ethers.utils.hexZeroPad('0x' + i.toString(16), 2)
const addr = hre.ethers.utils.getAddress(
hre.ethers.utils.hexConcat([prefix, num])
)
const code = await hre.ethers.provider.getCode(addr)
if (code === '0x') {
throw new Error(`no code found at ${addr}`)
}
if (addr === predeploys.GovernanceToken || addr === predeploys.ProxyAdmin) {
continue
}
const slot = await hre.ethers.provider.getStorageAt(addr, adminSlot)
const admin = hre.ethers.utils.hexConcat([
'0x000000000000000000000000',
predeploys.ProxyAdmin,
])
if (admin !== slot) {
throw new Error(`incorrect admin slot in ${addr}`)
}
}
}
// assertSemver will ensure that the semver is the correct version
const assertSemver = async (
contract: Contract,
name: string,
override?: string
) => {
const version = await contract.version()
let target = expectedSemver
if (override) {
target = override
}
if (version !== target) {
throw new Error(
`${name}: version mismatch. Got ${version}, expected ${target}`
)
}
console.log(` - version: ${version}`)
}
// checkProxy will print out the proxy slots
const checkProxy = async (hre: HardhatRuntimeEnvironment, name: string) => {
const address = predeploys[name]
if (!address) {
throw new Error(`unknown contract name: ${name}`)
}
const impl = await hre.ethers.provider.getStorageAt(address, implSlot)
const admin = await hre.ethers.provider.getStorageAt(address, adminSlot)
console.log(` - EIP-1967 implementation slot: ${impl}`)
console.log(` - EIP-1967 admin slot: ${admin}`)
}
// assertProxy will require the proxy is set
const assertProxy = async (hre: HardhatRuntimeEnvironment, name: string) => {
const address = predeploys[name]
if (!address) {
throw new Error(`unknown contract name: ${name}`)
}
const code = await hre.ethers.provider.getCode(address)
const deployInfo = await hre.artifacts.readArtifact('Proxy')
if (code !== deployInfo.deployedBytecode) {
throw new Error(`${address}: code mismatch`)
}
const impl = await hre.ethers.provider.getStorageAt(address, implSlot)
const implAddress = '0x' + impl.slice(26)
const implCode = await hre.ethers.provider.getCode(implAddress)
if (implCode === '0x') {
throw new Error('No code at implementation')
}
}
const check = {
// LegacyMessagePasser
// - check version
// - is behind a proxy
LegacyMessagePasser: async (hre: HardhatRuntimeEnvironment) => {
const LegacyMessagePasser = await hre.ethers.getContractAt(
'LegacyMessagePasser',
predeploys.LegacyMessagePasser
)
await assertSemver(LegacyMessagePasser, 'LegacyMessagePasser')
await checkProxy(hre, 'LegacyMessagePasser')
await assertProxy(hre, 'LegacyMessagePasser')
},
// DeployerWhitelist
// - check version
// - is behind a proxy
// - owner is `address(0)`
DeployerWhitelist: async (hre: HardhatRuntimeEnvironment) => {
const DeployerWhitelist = await hre.ethers.getContractAt(
'DeployerWhitelist',
predeploys.DeployerWhitelist
)
await assertSemver(DeployerWhitelist, 'DeployerWhitelist')
const owner = await DeployerWhitelist.owner()
assert(owner === hre.ethers.constants.AddressZero)
console.log(` - owner: ${owner}`)
await checkProxy(hre, 'DeployerWhitelist')
await assertProxy(hre, 'DeployerWhitelist')
},
// L2CrossDomainMessenger
// - check version
// - check OTHER_MESSENGER
// - check l1CrossDomainMessenger (legacy)
// - is behind a proxy
// - check owner
// - check initialized
L2CrossDomainMessenger: async (hre: HardhatRuntimeEnvironment) => {
const L2CrossDomainMessenger = await hre.ethers.getContractAt(
'L2CrossDomainMessenger',
predeploys.L2CrossDomainMessenger
)
await assertSemver(L2CrossDomainMessenger, 'L2CrossDomainMessenger')
const xDomainMessageSenderSlot = await hre.ethers.provider.getStorageAt(
predeploys.L2CrossDomainMessenger,
204
)
const xDomainMessageSender = '0x' + xDomainMessageSenderSlot.slice(26)
assert(
xDomainMessageSender === '0x000000000000000000000000000000000000dead'
)
const otherMessenger = await L2CrossDomainMessenger.OTHER_MESSENGER()
assert(otherMessenger !== hre.ethers.constants.AddressZero)
yell(` - OTHER_MESSENGER: ${otherMessenger}`)
const l1CrossDomainMessenger =
await L2CrossDomainMessenger.l1CrossDomainMessenger()
yell(` - l1CrossDomainMessenger: ${l1CrossDomainMessenger}`)
await checkProxy(hre, 'L2CrossDomainMessenger')
await assertProxy(hre, 'L2CrossDomainMessenger')
const owner = await L2CrossDomainMessenger.owner()
assert(owner !== hre.ethers.constants.AddressZero)
yell(` - owner: ${owner}`)
const MESSAGE_VERSION = await L2CrossDomainMessenger.MESSAGE_VERSION()
console.log(` - MESSAGE_VERSION: ${MESSAGE_VERSION}`)
const MIN_GAS_CALLDATA_OVERHEAD =
await L2CrossDomainMessenger.MIN_GAS_CALLDATA_OVERHEAD()
console.log(` - MIN_GAS_CALLDATA_OVERHEAD: ${MIN_GAS_CALLDATA_OVERHEAD}`)
const MIN_GAS_CONSTANT_OVERHEAD =
await L2CrossDomainMessenger.MIN_GAS_CONSTANT_OVERHEAD()
console.log(` - MIN_GAS_CONSTANT_OVERHEAD: ${MIN_GAS_CONSTANT_OVERHEAD}`)
const MIN_GAS_DYNAMIC_OVERHEAD_DENOMINATOR =
await L2CrossDomainMessenger.MIN_GAS_DYNAMIC_OVERHEAD_DENOMINATOR()
console.log(
` - MIN_GAS_DYNAMIC_OVERHEAD_DENOMINATOR: ${MIN_GAS_DYNAMIC_OVERHEAD_DENOMINATOR}`
)
const MIN_GAS_DYNAMIC_OVERHEAD_NUMERATOR =
await L2CrossDomainMessenger.MIN_GAS_DYNAMIC_OVERHEAD_NUMERATOR()
console.log(
` - MIN_GAS_DYNAMIC_OVERHEAD_NUMERATOR: ${MIN_GAS_DYNAMIC_OVERHEAD_NUMERATOR}`
)
const slot = await hre.ethers.provider.getStorageAt(
predeploys.L2CrossDomainMessenger,
0
)
const spacer = '0x' + slot.slice(26)
console.log(` - legacy spacer: ${spacer}`)
const initialized = '0x' + slot.slice(24, 26)
assert(initialized === '0x01')
console.log(` - initialized: ${initialized}`)
},
// GasPriceOracle
// - check version
// - check decimals
GasPriceOracle: async (hre: HardhatRuntimeEnvironment) => {
const GasPriceOracle = await hre.ethers.getContractAt(
'GasPriceOracle',
predeploys.GasPriceOracle
)
await assertSemver(GasPriceOracle, 'GasPriceOracle')
const decimals = await GasPriceOracle.decimals()
assert(decimals.eq(6))
console.log(` - decimals: ${decimals.toNumber()}`)
await checkProxy(hre, 'GasPriceOracle')
await assertProxy(hre, 'GasPriceOracle')
},
// L2StandardBridge
// - check version
L2StandardBridge: async (hre: HardhatRuntimeEnvironment) => {
const L2StandardBridge = await hre.ethers.getContractAt(
'L2StandardBridge',
predeploys.L2StandardBridge
)
await assertSemver(L2StandardBridge, 'L2StandardBridge', '0.0.2')
const OTHER_BRIDGE = await L2StandardBridge.OTHER_BRIDGE()
assert(OTHER_BRIDGE !== hre.ethers.constants.AddressZero)
yell(` - OTHER_BRIDGE: ${OTHER_BRIDGE}`)
const MESSENGER = await L2StandardBridge.MESSENGER()
assert(MESSENGER === predeploys.L2CrossDomainMessenger)
await checkProxy(hre, 'L2StandardBridge')
await assertProxy(hre, 'L2StandardBridge')
},
// SequencerFeeVault
// - check version
// - check RECIPIENT
// - check l1FeeWallet (legacy)
SequencerFeeVault: async (hre: HardhatRuntimeEnvironment) => {
const SequencerFeeVault = await hre.ethers.getContractAt(
'SequencerFeeVault',
predeploys.SequencerFeeVault
)
await assertSemver(SequencerFeeVault, 'SequencerFeeVault')
const RECIPIENT = await SequencerFeeVault.RECIPIENT()
assert(RECIPIENT !== hre.ethers.constants.AddressZero)
yell(` - RECIPIENT: ${RECIPIENT}`)
const l1FeeWallet = await SequencerFeeVault.l1FeeWallet()
assert(l1FeeWallet !== hre.ethers.constants.AddressZero)
console.log(` - l1FeeWallet: ${l1FeeWallet}`)
const MIN_WITHDRAWAL_AMOUNT =
await SequencerFeeVault.MIN_WITHDRAWAL_AMOUNT()
console.log(` - MIN_WITHDRAWAL_AMOUNT: ${MIN_WITHDRAWAL_AMOUNT}`)
await checkProxy(hre, 'SequencerFeeVault')
await assertProxy(hre, 'SequencerFeeVault')
},
// OptimismMintableERC20Factory
// - check version
OptimismMintableERC20Factory: async (hre: HardhatRuntimeEnvironment) => {
const OptimismMintableERC20Factory = await hre.ethers.getContractAt(
'OptimismMintableERC20Factory',
predeploys.OptimismMintableERC20Factory
)
await assertSemver(
OptimismMintableERC20Factory,
'OptimismMintableERC20Factory',
'1.0.0'
)
const BRIDGE = await OptimismMintableERC20Factory.BRIDGE()
assert(BRIDGE !== hre.ethers.constants.AddressZero)
await checkProxy(hre, 'OptimismMintableERC20Factory')
await assertProxy(hre, 'OptimismMintableERC20Factory')
},
// L1BlockNumber
// - check version
L1BlockNumber: async (hre: HardhatRuntimeEnvironment) => {
const L1BlockNumber = await hre.ethers.getContractAt(
'L1BlockNumber',
predeploys.L1BlockNumber
)
await assertSemver(L1BlockNumber, 'L1BlockNumber')
await checkProxy(hre, 'L1BlockNumber')
await assertProxy(hre, 'L1BlockNumber')
},
// L1Block
// - check version
L1Block: async (hre: HardhatRuntimeEnvironment) => {
const L1Block = await hre.ethers.getContractAt(
'L1Block',
predeploys.L1Block
)
await assertSemver(L1Block, 'L1Block')
await checkProxy(hre, 'L1Block')
await assertProxy(hre, 'L1Block')
},
// LegacyERC20ETH
// - not behind a proxy
// - check name
// - check symbol
// - check decimals
// - check BRIDGE
// - check REMOTE_TOKEN
// - totalSupply should be set to 0
LegacyERC20ETH: async (hre: HardhatRuntimeEnvironment) => {
const LegacyERC20ETH = await hre.ethers.getContractAt(
'LegacyERC20ETH',
predeploys.LegacyERC20ETH
)
const name = await LegacyERC20ETH.name()
assert(name === 'Ether')
console.log(` - name: ${name}`)
const symbol = await LegacyERC20ETH.symbol()
assert(symbol === 'ETH')
console.log(` - symbol: ${symbol}`)
const decimals = await LegacyERC20ETH.decimals()
assert(decimals === 18)
console.log(` - decimals: ${decimals}`)
const BRIDGE = await LegacyERC20ETH.BRIDGE()
assert(BRIDGE === predeploys.L2StandardBridge)
const REMOTE_TOKEN = await LegacyERC20ETH.REMOTE_TOKEN()
assert(REMOTE_TOKEN === hre.ethers.constants.AddressZero)
const totalSupply = await LegacyERC20ETH.totalSupply()
assert(totalSupply.eq(0))
console.log(` - totalSupply: ${totalSupply}`)
await checkProxy(hre, 'LegacyERC20ETH')
// No proxy at this address, don't call assertProxy
},
// WETH9
// - check name
// - check symbol
// - check decimals
// - is behind a proxy
WETH9: async (hre: HardhatRuntimeEnvironment) => {
const WETH9 = await hre.ethers.getContractAt('WETH9', predeploys.WETH9)
const name = await WETH9.name()
assert(name === 'Wrapped Ether')
console.log(` - name: ${name}`)
const symbol = await WETH9.symbol()
assert(symbol === 'WETH')
console.log(` - symbol: ${symbol}`)
const decimals = await WETH9.decimals()
assert(decimals === 18)
console.log(` - decimals: ${decimals}`)
await checkProxy(hre, 'WETH9')
await assertProxy(hre, 'WETH9')
},
// GovernanceToken
// - not behind a proxy
// - check name
// - check symbol
// - check owner
GovernanceToken: async (hre: HardhatRuntimeEnvironment) => {
const GovernanceToken = await hre.ethers.getContractAt(
'GovernanceToken',
predeploys.GovernanceToken
)
const name = await GovernanceToken.name()
assert(name === 'Optimism')
console.log(` - name: ${name}`)
const symbol = await GovernanceToken.symbol()
assert(symbol === 'OP')
console.log(` - symbol: ${symbol}`)
const owner = await GovernanceToken.owner()
yell(` - owner: ${owner}`)
const totalSupply = await GovernanceToken.totalSupply()
console.log(` - totalSupply: ${totalSupply}`)
await checkProxy(hre, 'GovernanceToken')
// No proxy at this address, don't call assertProxy
},
// L2ERC721Bridge
// - check version
L2ERC721Bridge: async (hre: HardhatRuntimeEnvironment) => {
const L2ERC721Bridge = await hre.ethers.getContractAt(
'L2ERC721Bridge',
predeploys.L2ERC721Bridge
)
await assertSemver(L2ERC721Bridge, 'L2ERC721Bridge')
const MESSENGER = await L2ERC721Bridge.MESSENGER()
assert(MESSENGER !== hre.ethers.constants.AddressZero)
console.log(` - MESSENGER: ${MESSENGER}`)
const OTHER_BRIDGE = await L2ERC721Bridge.OTHER_BRIDGE()
assert(OTHER_BRIDGE !== hre.ethers.constants.AddressZero)
yell(` - OTHER_BRIDGE: ${OTHER_BRIDGE}`)
await checkProxy(hre, 'L2ERC721Bridge')
await assertProxy(hre, 'L2ERC721Bridge')
},
// OptimismMintableERC721Factory
// - check version
OptimismMintableERC721Factory: async (hre: HardhatRuntimeEnvironment) => {
const OptimismMintableERC721Factory = await hre.ethers.getContractAt(
'OptimismMintableERC721Factory',
predeploys.OptimismMintableERC721Factory
)
await assertSemver(
OptimismMintableERC721Factory,
'OptimismMintableERC721Factory',
'1.0.0'
)
const BRIDGE = await OptimismMintableERC721Factory.BRIDGE()
assert(BRIDGE !== hre.ethers.constants.AddressZero)
console.log(` - BRIDGE: ${BRIDGE}`)
const REMOTE_CHAIN_ID =
await OptimismMintableERC721Factory.REMOTE_CHAIN_ID()
assert(REMOTE_CHAIN_ID !== 0)
console.log(` - REMOTE_CHAIN_ID: ${REMOTE_CHAIN_ID}`)
await checkProxy(hre, 'OptimismMintableERC721Factory')
await assertProxy(hre, 'OptimismMintableERC721Factory')
},
// ProxyAdmin
// - check owner
ProxyAdmin: async (hre: HardhatRuntimeEnvironment) => {
const ProxyAdmin = await hre.ethers.getContractAt(
'ProxyAdmin',
predeploys.ProxyAdmin
)
const owner = await ProxyAdmin.owner()
assert(owner !== hre.ethers.constants.AddressZero)
yell(` - owner: ${owner}`)
const addressManager = await ProxyAdmin.addressManager()
console.log(` - addressManager: ${addressManager}`)
},
// BaseFeeVault
// - check version
// - check MIN_WITHDRAWAL_AMOUNT
// - check RECIPIENT
BaseFeeVault: async (hre: HardhatRuntimeEnvironment) => {
const BaseFeeVault = await hre.ethers.getContractAt(
'BaseFeeVault',
predeploys.BaseFeeVault
)
await assertSemver(BaseFeeVault, 'BaseFeeVault')
const MIN_WITHDRAWAL_AMOUNT = await BaseFeeVault.MIN_WITHDRAWAL_AMOUNT()
console.log(` - MIN_WITHDRAWAL_AMOUNT: ${MIN_WITHDRAWAL_AMOUNT}`)
const RECIPIENT = await BaseFeeVault.RECIPIENT()
assert(RECIPIENT !== hre.ethers.constants.AddressZero)
yell(` - RECIPIENT: ${RECIPIENT}`)
await checkProxy(hre, 'BaseFeeVault')
await assertProxy(hre, 'BaseFeeVault')
},
// L1FeeVault
// - check version
// - check MIN_WITHDRAWAL_AMOUNT
// - check RECIPIENT
L1FeeVault: async (hre: HardhatRuntimeEnvironment) => {
const L1FeeVault = await hre.ethers.getContractAt(
'L1FeeVault',
predeploys.L1FeeVault
)
await assertSemver(L1FeeVault, 'L1FeeVault')
const MIN_WITHDRAWAL_AMOUNT = await L1FeeVault.MIN_WITHDRAWAL_AMOUNT()
console.log(` - MIN_WITHDRAWAL_AMOUNT: ${MIN_WITHDRAWAL_AMOUNT}`)
const RECIPIENT = await L1FeeVault.RECIPIENT()
assert(RECIPIENT !== hre.ethers.constants.AddressZero)
yell(` - RECIPIENT: ${RECIPIENT}`)
await checkProxy(hre, 'L1FeeVault')
await assertProxy(hre, 'L1FeeVault')
},
// L2ToL1MessagePasser
// - check version
L2ToL1MessagePasser: async (hre: HardhatRuntimeEnvironment) => {
const L2ToL1MessagePasser = await hre.ethers.getContractAt(
'L2ToL1MessagePasser',
predeploys.L2ToL1MessagePasser
)
await assertSemver(L2ToL1MessagePasser, 'L2ToL1MessagePasser')
const MESSAGE_VERSION = await L2ToL1MessagePasser.MESSAGE_VERSION()
console.log(` - MESSAGE_VERSION: ${MESSAGE_VERSION}`)
const messageNonce = await L2ToL1MessagePasser.messageNonce()
console.log(` - messageNonce: ${messageNonce}`)
await checkProxy(hre, 'L2ToL1MessagePasser')
await assertProxy(hre, 'L2ToL1MessagePasser')
},
}
task(
'check-l2',
'Checks a freshly migrated L2 system for correct migration'
).setAction(async (_, hre: HardhatRuntimeEnvironment) => {
yell('Manually check values wrapped in !!!!')
console.log()
// Ensure that all the predeploys exist, including the not
// currently configured ones
await checkPredeploys(hre)
console.log()
// Check the currently configured predeploys
for (const [name, fn] of Object.entries(check)) {
const address = predeploys[name]
console.log(`${name}: ${address}`)
await fn(hre)
}
})
......@@ -6,3 +6,4 @@ import './forge-verify'
import './validate-spacers'
import './solidity'
import './accounts'
import './check-l2'
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