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) } })