Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

contracts-bedrock: add forge contract verification support #3141

Merged
merged 2 commits into from
Aug 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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
)
protolambda marked this conversation as resolved.
Show resolved Hide resolved
.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'