From eb8e49ae7fd817dc0aa42b823a8e3a3589704400 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Migone?= Date: Fri, 15 Jul 2022 11:16:00 +0200 Subject: [PATCH] chore: add hardhat task to verify all contracts with etherscan MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Migone --- DEPLOYMENT.md | 34 +++++++++++++++++++++ tasks/verify/verify.ts | 69 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 102 insertions(+), 1 deletion(-) diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index f7c11977c..d3222acaa 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -105,3 +105,37 @@ Some contracts require the address from previously deployed contracts. For that 4. Merge this update into master, branch off and save for whatever version of the testnet is going on, and then tag this on the github repo, pointing to your branch (ex. at `testnet-phase-1` branch). This way we can always get the contract code for testnet, while continuing to do work on mainnet. 5. Pull the updated package into the subgraph, and other apps that depend on the package.json. 6. Send tokens to the whole team using `./cli/cli.ts airdrop` + +## Verifying the deployed smart contracts + +Deployed smart contracts can be verified on etherscan and sourcify using built-in commands. + +### Etherscan + +[Etherscan](https://etherscan.io/) verification can be performed by using the [hardhat-etherscan](https://hardhat.org/hardhat-runner/plugins/nomiclabs-hardhat-etherscan) plugin. __Note__: ensure you have set a valid `ETHERSCAN_API_KEY` in the `.env` file. + +- To verify a single contract, run: + + ```bash + npx hardhat verify --network {networkName} --contract {FullyQualifiedContractName} {contractAddress} {constructorInitParams} + ``` + +- To verify all contracts on the address book, run: + ```bash + npx hardhat verifyAll --network {networkName} --graph-config {graphConfigFile} + ``` + +### Sourcify + +Additionally you can verify contracts on [Sourcify](https://sourcify.dev/). + +- To verify a single contract, run: + + ```bash + npx hardhat sourcify --network {networkName} --contract {FullyQualifiedContractName} {contractAddress} + ``` + +- To verify all contracts on the address book, run: + ```bash + npx hardhat sourcifyAll --network {networkName} + ``` diff --git a/tasks/verify/verify.ts b/tasks/verify/verify.ts index f3639d3c4..8169df624 100644 --- a/tasks/verify/verify.ts +++ b/tasks/verify/verify.ts @@ -3,10 +3,15 @@ import * as types from 'hardhat/internal/core/params/argumentTypes' import { submitSourcesToSourcify } from './sourcify' import { isFullyQualifiedName, parseFullyQualifiedName } from 'hardhat/utils/contract-names' import { TASK_COMPILE } from 'hardhat/builtin-tasks/task-names' -import { getAddressBook } from '../../cli/address-book' +import { loadEnv } from '../../cli/env' import { cliOpts } from '../../cli/defaults' +import { getAddressBook } from '../../cli/address-book' +import { getContractConfig, readConfig } from '../../cli/config' +import { Wallet } from 'ethers' +import { NomicLabsHardhatPluginError } from 'hardhat/plugins' import fs from 'fs' import path from 'path' +import { HardhatRuntimeEnvironment } from 'hardhat/types' task('sourcify', 'Verifies contract on sourcify') .addPositionalParam('address', 'Address of the smart contract to verify', undefined, types.string) @@ -69,6 +74,68 @@ task('sourcifyAll', 'Verifies all contracts on sourcify') } }) +task('verifyAll', 'Verifies all contracts on etherscan') + .addParam('addressBook', cliOpts.addressBook.description, cliOpts.addressBook.default) + .addParam('graphConfig', cliOpts.graphConfig.description, cliOpts.graphConfig.default) + .setAction(async (args, hre) => { + const chainId = hre.network.config.chainId + const chainName = hre.network.name + + if (!chainId || !chainName) { + throw new Error('Cannot verify contracts without a network') + } + + console.log(`> Verifying all contracts on chain ${chainName}[${chainId}]...`) + const addressBook = getAddressBook(args.addressBook, chainId.toString()) + const graphConfig = readConfig(args.graphConfig) + + const accounts = await hre.ethers.getSigners() + const env = await loadEnv(args, accounts[0] as unknown as Wallet) + + for (const contractName of addressBook.listEntries()) { + console.log(`\n> Verifying contract ${contractName}...`) + + const contractConfig = getContractConfig(graphConfig, addressBook, contractName, env) + const contractPath = getContractPath(contractName) + const constructorParams = contractConfig.params.map((p) => p.value.toString()) + + if (contractPath) { + const contract = addressBook.getEntry(contractName) + + if (contract.implementation) { + console.log('Contract is upgradeable, verifying proxy...') + const proxyAdmin = addressBook.getEntry('GraphProxyAdmin') + + // Verify proxy + await safeVerify(hre, { + address: contract.address, + contract: 'contracts/upgrades/GraphProxy.sol:GraphProxy', + constructorArgsParams: [contract.implementation.address, proxyAdmin.address], + }) + } + + // Verify implementation + console.log('Verifying implementation...') + await safeVerify(hre, { + address: contract.implementation?.address ?? contract.address, + contract: `${contractPath}:${contractName}`, + constructorArgsParams: contract.implementation ? [] : constructorParams, + }) + } else { + console.log(`Contract ${contractName} not found.`) + } + } + }) + +// etherscan API throws errors if the contract is already verified +async function safeVerify(hre: HardhatRuntimeEnvironment, taskArguments: any): Promise { + try { + await hre.run('verify', taskArguments) + } catch (error) { + console.log(error.message) + } +} + function getContractPath(contract: string): string | undefined { const files = readDirRecursive('contracts/') return files.find((f) => path.basename(f) === `${contract}.sol`)