Before you begin
- A Virtual Environment. Copy its RPC URL from the dashboard.
- A funded deployer address on that Virtual Environment.
/verify appended.
Deploy and verify
- Foundry
- Hardhat
Configure foundry.toml
Tenderly’s verifier matches the metadata hash the Solidity compiler appends to deployed bytecode against the source you submit. Keep the metadata in the compiled output by setting cbor_metadata and bytecode_hash explicitly:foundry.toml
cbor_metadata = true and bytecode_hash = "ipfs" are Foundry’s defaults, but some templates strip them; bytecode_hash = "none" breaks verification. Pin solc_version and the optimizer settings so re-verification compiles the same bytecode.If your project uses libraries such as OpenZeppelin, declare the remappings explicitly in remappings.txt:remappings.txt
Set environment variables
showLineNumbers
cast chain-id should print the Virtual Environment’s chain ID, and the deployer balance should be non-zero:Deploy with forge create
Run forge create with the verify flags. Constructor arguments are passed as raw values; Foundry ABI-encodes them and submits the encoded form to the verifier:showLineNumbers
Deploy with a Foundry script
Useforge script to deploy multiple contracts in one run. Every new Foo(...) in the script is submitted for verification automatically, with the correct constructor arguments taken from the broadcast log. Imports are handled transparently: the verifier receives a standard-JSON payload containing every source file the compiler touched, so local imports and library imports (such as OpenZeppelin) verify without extra steps.Use --slow so transactions are sent one at a time. Without it, broadcast batching can submit a transaction before the previous one is confirmed, which is flaky against a hosted RPC and can race the verification step.showLineNumbers
Verify an existing contract
Already-deployed contracts can be verified withforge verify-contract. Unlike forge create, this command expects constructor arguments already ABI-encoded; use cast abi-encode to produce them:showLineNumbers
--watch polls the verifier until verification finishes and prints the result. For multi-contract deployments, run forge verify-contract once per deployed address.Check the dashboard
In the Tenderly Dashboard, open Contracts and select the Virtual Contracts tab. Your newly deployed contracts appear there, with verification status visible per contract.Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
unauthorized / -32004 response from the verifier | Wrong verifier URL. A common mistake is appending /verify/etherscan instead of /verify. | Make sure the verifier URL ends in exactly /verify. |
Bytecode does not match deployed contract | The submitted source compiled differently than what’s deployed: an optimizer_runs mismatch, a different solc_version, or a stripped metadata hash. | Pin solc_version, optimizer, optimizer_runs, and bytecode_hash in foundry.toml. Run forge clean && forge build before re-verifying. |
Failed to deserialize content | The verifier returned an error string Foundry can’t parse, usually wrapping an upstream auth or path error. | Re-check the URL and re-run with -vvvv to see the raw response. |
Dry run enabled, not broadcasting transaction on forge create | --broadcast not passed, or swallowed by --constructor-args. | Move --constructor-args to be the last flag. |
Only the first contract verifies on forge script | RPC race: the second transaction was submitted before the first receipt. | Add --slow. |
Verifier reports OK but the explorer still shows unverified | UI caching. | Hard-refresh the explorer page. The status in the API response is authoritative. |
Next steps
- Verify proxy contracts with Foundry: verify a UUPS implementation and its ERC1967 proxy, including upgrades.
- Stage contracts: share the RPC link and contract addresses with your team.
- Stage your dApp: point your frontend at the Virtual Environment.
- Fork a Virtual Environment: branch from a Virtual Environment state.