Skip to content

Commit

Permalink
contracts-bedrock: add forge contract verification support (#3141)
Browse files Browse the repository at this point in the history
* contracts-bedrock: add forge contract verification support

Add etherscan contract verification for the hh/foundry
compiler toolchain. This will eventually be upstreamed,
but is implemented here to ensure that it works thoroughly
first.

To run the task:

```
export ETHERSCAN_API_KEY=$(cat api-key.txt)
$ npx hardhat forge-verify --network goerli
```

In the future, other contract verification backends
will be added to forge and this task will be updated
to support them.

Example contract that was verified: https://goerli.etherscan.io/address/0x1234662682c85fa6fb375416d14db965eba222ba#code

An individual contract can be targeted instead of all of them
with the `--contract` flag. If hardhat deploy has configured
external deployments, forge will not be able to verify them.
It may be possible to implement that in the future.

* contracts-bedrock: modularize tasks
  • Loading branch information
tynes committed Aug 2, 2022
1 parent 357c770 commit 28649d6
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 8 deletions.
5 changes: 5 additions & 0 deletions .changeset/pretty-boxes-thank.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@eth-optimism/contracts-bedrock': patch
---

Add harhdat forge contract verification support
9 changes: 1 addition & 8 deletions packages/contracts-bedrock/hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,7 @@ import '@nomiclabs/hardhat-ethers'
import 'hardhat-deploy'

// Hardhat tasks
import './tasks/genesis-l1'
import './tasks/genesis-l2'
import './tasks/deposits'
import './tasks/rekey'
import './tasks/rollup-config'
import './tasks/check-op-node'
import './tasks/check-l2-config'
import './tasks/watch'
import './tasks'

subtask(TASK_COMPILE_SOLIDITY_GET_SOURCE_PATHS).setAction(
async (_, __, runSuper) => {
Expand Down
1 change: 1 addition & 0 deletions packages/contracts-bedrock/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"ethereumjs-wallet": "^1.0.2",
"@defi-wonderland/smock": "^2.0.2",
"@foundry-rs/hardhat-forge": "^0.1.16",
"@foundry-rs/easy-foundryup": "^0.1.3",
"@nomiclabs/hardhat-ethers": "^2.0.0",
"@nomiclabs/hardhat-waffle": "^2.0.0",
"@typechain/ethers-v5": "^10.1.0",
Expand Down
128 changes: 128 additions & 0 deletions packages/contracts-bedrock/tasks/forge-verify.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import { spawn as spawn } from 'child_process'

import { task, types } from 'hardhat/config'
import * as foundryup from '@foundry-rs/easy-foundryup'
import 'hardhat-deploy'
import { ethers } from 'ethers'

interface ForgeVerifyArgs {
chainId: string
compilerVersion: string
constructorArgs: string
optimizerRuns: number
contractAddress: string
contractName: string
etherscanApiKey: string
}

const verifyArgs = (opts: ForgeVerifyArgs): string[] => {
const allArgs: string[] = []

if (!opts.chainId) {
throw new Error(`No chain-id provided`)
}
allArgs.push(`--chain`, opts.chainId)

if (opts.compilerVersion) {
allArgs.push('--compiler-version', opts.compilerVersion)
}
if (opts.constructorArgs) {
allArgs.push('--constructor-args', opts.constructorArgs)
}
if (typeof opts.optimizerRuns === 'number') {
allArgs.push('--num-of-optimizations', opts.optimizerRuns.toString())
}
allArgs.push('--watch')

if (!opts.contractAddress) {
throw new Error('No contract address provided')
}
allArgs.push(opts.contractAddress)
if (!opts.contractName) {
throw new Error('No contract name provided')
}
allArgs.push(opts.contractName)
if (!opts.etherscanApiKey) {
throw new Error('No Etherscan API key provided')
}
allArgs.push(opts.etherscanApiKey)
return allArgs
}

const spawnVerify = async (opts: ForgeVerifyArgs): Promise<boolean> => {
const args = ['verify-contract', ...verifyArgs(opts)]
const forgeCmd = await foundryup.getForgeCommand()
return new Promise((resolve) => {
const process = spawn(forgeCmd, args, {
stdio: 'inherit',
})
process.on('exit', (code) => {
resolve(code === 0)
})
})
}

task('forge-contract-verify', 'Verify contracts using forge')
.addOptionalParam(
'contract',
'Name of the contract to verify',
'',
types.string
)
.addOptionalParam(
'etherscanApiKey',
'Etherscan API key',
process.env.ETHERSCAN_API_KEY,
types.string
)
.setAction(async (args, hre) => {
const deployments = await hre.deployments.all()
if (args.contract !== '') {
if (!deployments[args.contract]) {
throw new Error(
`Contract ${args.contract} not found in ${hre.network} deployments`
)
}
}

for (const [contract, deployment] of Object.entries(deployments)) {
if (args.contract !== '' && args.contract !== contract) {
continue
}

const chainId = await hre.getChainId()
const contractAddress = deployment.address
const etherscanApiKey = args.etherscanApiKey

let metadata = deployment.metadata as any
// Handle double nested JSON stringify
while (typeof metadata === 'string') {
metadata = JSON.parse(metadata) as any
}

const contractName = Object.values(
metadata.settings.compilationTarget
)[0].toString()
const compilerVersion = metadata.compiler.version

const iface = new ethers.utils.Interface(deployment.abi)
const constructorArgs = iface.encodeDeploy(deployment.args)
const optimizerRuns = metadata.settings.optimizer

const success = await spawnVerify({
chainId,
compilerVersion,
constructorArgs,
optimizerRuns,
contractAddress,
contractName,
etherscanApiKey,
})

if (success) {
console.log(`Contract verification successful for ${contractName}`)
} else {
console.log(`Contract verification unsuccesful for ${contractName}`)
}
}
})
9 changes: 9 additions & 0 deletions packages/contracts-bedrock/tasks/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import './genesis-l1'
import './genesis-l2'
import './deposits'
import './rekey'
import './rollup-config'
import './check-op-node'
import './check-l2-config'
import './watch'
import './forge-verify'

0 comments on commit 28649d6

Please sign in to comment.