-
Notifications
You must be signed in to change notification settings - Fork 3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
contracts-bedrock: add forge contract verification support (#3141)
* 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
Showing
5 changed files
with
144 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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}`) | ||
} | ||
} | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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' |