contracts:verify
Submit Solidity Standard JSON Input source code to Etherscan V2 for verification at
. Mirror an existing verification with --mirror-chain / --mirror-address, or supply source explicitly with --source. Inside sim:fork this becomes a local dry-run: the source is compiled and checked against the fork's deployed bytecode instead of being sent to Etherscan.Syntax
Section titled “Syntax”contracts:verify <address>Arguments
Section titled “Arguments”| Name | Type | Description |
|---|---|---|
address | address | Deployed contract address on the current chain to verify. |
Options
Section titled “Options”| Name | Type | Description |
|---|---|---|
--mirror-chain | chain | Chain (id or viem name like optimism) to mirror an existing verification from. Defaults to the current chain when only --mirror-address is set. |
--mirror-address | address | Existing verified contract to mirror. Defaults to when only --mirror-chain is set. |
--source | string | Solidity Standard JSON Input text including language, sources, and settings. Required for explicit (non-mirror) mode. |
--contract-name | string | Qualified contract name path/File.sol:ContractName. Required for explicit mode. |
--compiler | string | Solidity compiler version, e.g. 0.8.20+commit.a1b79de6. Required for explicit mode. |
--license | string | SPDX license identifier (e.g. MIT, Apache-2.0). Defaults to None in explicit mode; mirrored in mirror mode. |
--constructor | string | Constructor signature like constructor(uint256,address). Requires --constructor-args. |
--constructor-args | array | Constructor arguments as an array literal, e.g. [100e18 @me]. Requires --constructor. |
--constructor-args-hex | bytes | Pre-encoded ABI constructor arguments as hex. Mutually exclusive with --constructor / --constructor-args. |
--timeout | number | Maximum time to wait for verification to complete, in time units (default 60s). |
--poll-interval | number | Time between status polls, in time units (default 3s). |
Simulation dry-run
Section titled “Simulation dry-run”Inside sim:fork, verify never talks to Etherscan. It performs a local
dry-run instead: the Standard JSON Input is compiled with the pinned
compiler and diffed against the bytecode deployed on the fork (metadata
hashes and immutable values are ignored, exactly like a verifier would).
A match logs would verify on Etherscan; a mismatch aborts the simulation
naming the reason (wrong optimizer runs and a missing via-ir are the
usual suspects). No VITE_ETHERSCAN_API_KEY is needed for the dry-run —
mirror mode still needs one, since it reads the source from Etherscan.
load simload contractssim:fork ( set $src <<<SOLpragma solidity 0.8.26;contract Counter { uint256 public n; function inc() public { n++; }}SOL contracts:deploy $counter @contracts:solidity($src) contracts:verify $counter --source @contracts:solidity.standardJson($src) --contract-name @contracts:solidity.contract($src) --compiler @contracts:solidity.compiler($src))verify cannot be used inside batch — verification is not a transaction.
Examples
Section titled “Examples”load contracts
# Mirror SAME address from a DIFFERENT chain (the canonical cross-chain re-verify)contracts:verify 0x44fA8E6f47987339850636F88629646662444217 --mirror-chain 1
# Mirror a DIFFERENT address on the SAME chain# (e.g. clone an already-verified implementation onto a freshly deployed copy)set $newDeploy 0x0102030405060708090a0b0c0d0e0f1011121314set $alreadyVerified 0xf8D1677c8a0c961938bf2f9aDc3F3CFDA759A9d9contracts:verify $newDeploy --mirror-address $alreadyVerified
# Mirror a DIFFERENT address on a DIFFERENT chainset $mainnetSibling 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2contracts:verify $newDeploy --mirror-chain 1 --mirror-address $mainnetSibling
# Mirror with overridden constructor arguments# (typical when an immutable like `owner` differs across chains)set $myToken 0x9C58BAcC331c9aa871AFD802DB6379a98e80CEdbcontracts:verify $myToken --mirror-chain 1 --constructor "constructor(address)" --constructor-args [@me]
# Pair with deploy: same source, deterministic CREATE2 address on a new chaincontracts:deploy $token 0x6080604052348015600f57600080fd5b50603f80601d6000396000f3fe --create2 0x0000000000000000000000000000000000000000000000000000000000000001 --constructor "constructor(string,uint8)" --constructor-args ["MyToken" 18]contracts:verify $token --mirror-chain 1 --constructor "constructor(string,uint8)" --constructor-args ["MyToken" 18]
# Explicit Standard JSON Input fetched from a URLload http [@fetch]set $src @fetch("https://gist.githubusercontent.com/me/abc/raw/input.json")contracts:verify 0x44fA8E6f47987339850636F88629646662444217 --source $src --contract-name "src/MyToken.sol:MyToken" --compiler "0.8.20+commit.a1b79de6"- Mirror mode is activated by either
--mirror-chainor--mirror-address. The command pulls the verified source from Etherscan viagetsourcecodeand resubmits it on the current chain for<address>. The other selector defaults so all three combinations are reachable:- same address, different chain → only
--mirror-chain - different address, same chain → only
--mirror-address - different address, different chain → both
Settings (optimizer, evmVersion, libraries) and the original constructor
arguments are preserved by default; supply
--constructor/--constructor-argsto override per-deployment differences (e.g. immutableownerset to@meon the new chain).
- same address, different chain → only
- Explicit mode is the default when no mirror selector is set. It accepts
a Solidity Standard JSON Input via
--sourceplus the qualified--contract-nameand--compiler. The JSON itself encodes optimizer, evmVersion, and libraries — there are intentionally no separate--evm-versionor--optimizer-runsopts.
Source format
Section titled “Source format”verify only supports Solidity Standard JSON Input for explicit-mode
submissions. The JSON encodes everything Etherscan needs to compile:
{ "language": "Solidity", "sources": { "src/Foo.sol": { "content": "// SPDX ..." } }, "settings": { "optimizer": { "enabled": true, "runs": 200 }, "evmVersion": "paris" }}This is exactly what foundry and hardhat emit out of the box (foundry:
metadata.solcInput in out/<Contract>.sol/<Contract>.json; hardhat:
input in artifacts/build-info/*.json). For mirror mode the source is
fetched from the source chain — no source format choice is exposed because
the command always submits solidity-standard-json-input after
normalising whatever shape Etherscan returns.
Delivering --source
Section titled “Delivering --source”DSL string literals come in two flavors and neither supports escapes:
"..." cannot contain ", and '...' cannot contain '. The two
practical patterns:
- Fetch via
@fetchfrom thehttpmodule — recommended for any non-trivial input:
load contractsload http [@fetch]set $src @fetch("https://gist.githubusercontent.com/me/abc/raw/input.json")contracts:verify 0x44fA8E6f47987339850636F88629646662444217 --source $src --contract-name "src/Foo.sol:Foo" --compiler "0.8.20+commit.a1b79de6"- Inline single-quoted multi-line literal — fine for tiny payloads,
provided no
'appears anywhere inside the JSON:
load contractscontracts:verify 0x44fA8E6f47987339850636F88629646662444217 --source '{ "language": "Solidity", "sources": { "Foo.sol": { "content": "// SPDX ..." } }, "settings": { "optimizer": { "enabled": true, "runs": 200 } }}' --contract-name "Foo.sol:Foo" --compiler "0.8.20+commit.a1b79de6"Caveats
Section titled “Caveats”- A command and all of its options must be written on a single line — EVML
has no
\line continuation. (Quoted strings may span lines, as in the inline--sourceexample above.) - Requires the
VITE_ETHERSCAN_API_KEYenvironment variable. The same key is used by hover-card lookups; Etherscan V2's free tier covers 60+ chains under one key. - The
deploytransaction must be mined beforeverifyruns — Etherscan reads the deployed runtime bytecode at<address>. In practice this meansverifyshould not appear inside abatch (...)block alongside the deploy; run it as a follow-up command after the deploy confirms. - Verification is opaque to the deployment method. CREATE, Arachnid CREATE2, and CreateX CREATE3 deployments all verify identically because Etherscan compares deployed runtime bytecode rather than replaying the deployment transaction.
- For deterministic cross-chain re-deploys (CREATE2 / CREATE3), compile identically on every chain (same Standard JSON, same compiler version). Any divergence in metadata-affecting settings changes the metadata hash embedded in the deployed bytecode — and therefore the contract address.
- The poll loop waits up to
--timeoutseconds (default 60) and polls every--poll-intervalseconds (default 3). Increase--timeoutfor large standard JSONs that take Etherscan longer to compile.