validate-address-dictator.ts 5.24 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
'use strict'

import { ethers } from 'ethers'
import { task } from 'hardhat/config'
import * as types from 'hardhat/internal/core/params/argumentTypes'
import { hexStringEquals } from '@eth-optimism/core-utils'
import { getContractFactory } from '../src/contract-defs'

import {
  getInput,
  color as c,
  getArtifact,
  getEtherscanUrl,
  printComparison,
} from '../src/validation-utils'

task('validate:address-dictator')
  .addParam(
    'dictator',
    'Address of the AddressDictator to validate.',
    undefined,
    types.string
  )
  .addParam(
    'manager',
    'Address of the Address Manager contract which would be updated by the Dictator.',
    undefined,
    types.string
  )
  .addParam(
    'multisig',
    'Address of the multisig contract which should be the final owner',
    undefined,
    types.string
  )
  .addOptionalParam(
    'contractsRpcUrl',
    'RPC Endpoint to query for data',
    process.env.CONTRACTS_RPC_URL,
    types.string
  )
  .setAction(async (args) => {
    if (!args.contractsRpcUrl) {
      throw new Error(
        c.red('RPC URL must be set in your env, or passed as an argument.')
      )
    }
    const provider = new ethers.providers.JsonRpcProvider(args.contractsRpcUrl)

    const network = await provider.getNetwork()
    console.log()
    console.log(c.cyan("First make sure you're on the right chain:"))
    console.log(
      `Reading from the ${c.red(network.name)} network (Chain ID: ${c.red(
        '' + network.chainId
      )})`
    )
    await getInput(c.yellow('OK? Hit enter to continue.'))

    // eslint-disable-next-line @typescript-eslint/no-var-requires
    const dictatorArtifact = require('../artifacts/contracts/L1/deployment/AddressDictator.sol/AddressDictator.json')
    const dictatorCode = await provider.getCode(args.dictator)
    console.log(
      c.cyan(`
Now validating the Address Dictator deployment at\n${getEtherscanUrl(
        network,
        args.dictator
      )}`)
    )
    printComparison(
      'Comparing deployed AddressDictator bytecode against local build artifacts',
      'Deployed AddressDictator code',
      { name: 'Compiled bytecode', value: dictatorArtifact.deployedBytecode },
      { name: 'Deployed bytecode', value: dictatorCode }
    )

    // Connect to the deployed AddressDictator.
    const dictatorContract = getContractFactory('AddressDictator')
      .attach(args.dictator)
      .connect(provider)

    const finalOwner = await dictatorContract.finalOwner()
    printComparison(
      'Comparing the finalOwner address in the AddressDictator to the multisig address',
      'finalOwner',
      { name: 'multisig address', value: args.multisig },
      { name: 'finalOwner      ', value: finalOwner }
    )

    const manager = await dictatorContract.manager()
    printComparison(
      'Validating the AddressManager address in the AddressDictator',
      'addressManager',
      { name: 'manager', value: args.manager },
      { name: 'Address Manager', value: manager }
    )
    await getInput(c.yellow('OK? Hit enter to continue.'))

    // Get names and addresses from the Dictator.
    const namedAddresses = await dictatorContract.getNamedAddresses()

    // In order to reduce noise for the user, we query the AddressManager identify addresses that
    // will not be changed, and skip over them in this block.
    const managerContract = getContractFactory('Lib_AddressManager')
      .attach(args.manager)
      .connect(provider)

    // Now we loop over those and compare the addresses/deployedBytecode to deployment artifacts.
    for (const pair of namedAddresses) {
      const currentAddress = await managerContract.getAddress(pair.name)
      const artifact = getArtifact(pair.name)
      const addressChanged = !hexStringEquals(currentAddress, pair.addr)
      if (addressChanged) {
        console.log(
          c.cyan(`
Now validating the ${pair.name} deployment.
Current address: ${getEtherscanUrl(network, currentAddress)}
Upgraded address ${getEtherscanUrl(network, pair.addr)}`)
        )

        const code = await provider.getCode(pair.addr)
        printComparison(
          `Verifying ${pair.name} source code against local deployment artifacts`,
          `Deployed ${pair.name} code`,
          {
            name: 'artifact.deployedBytecode',
            value: artifact.deployedBytecode,
          },
          { name: 'Deployed bytecode        ', value: code }
        )

        // Identify contracts which inherit from Lib_AddressResolver, and check that they
        // have the right manager address.
        if (Object.keys(artifact)) {
          if (artifact.abi.some((el) => el.name === 'libAddressManager')) {
            const libAddressManager = await getContractFactory(
              'Lib_AddressResolver'
            )
              .attach(pair.addr)
              .connect(provider)
              .libAddressManager()

            printComparison(
              `Verifying ${pair.name} has the correct AddressManager address`,
              `The AddressManager address in ${pair.name}`,
              { name: 'Deployed value', value: libAddressManager },
              { name: 'Expected value', value: manager }
            )
          }
        }
      }
      await getInput(c.yellow('OK? Hit enter to continue.'))
    }
    console.log(c.green('AddressManager Validation complete!'))
  })