Skip to main content
Works on: public networks (mainnets and testnets). To verify contracts on a Virtual Environment, see Deploy and verify contracts and Verify proxy contracts with Foundry.
Tenderly verifies smart contracts deployed with Foundry’s forge create, forge script, and forge verify-contract commands through its Etherscan-compatible verification API. Verification is either private (the source is visible only inside your Tenderly project) or public (visible to anyone with the link).

Before you begin

You need your Tenderly account and project slugs, a Tenderly access key (Dashboard → Account Settings → Authorization), and a funded account on the target network. 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, and pin the compiler settings so they can’t drift between deploy time and verify time:
foundry.toml
cbor_metadata = true and bytecode_hash = "ipfs" are Foundry’s defaults, but some templates strip them; bytecode_hash = "none" breaks verification. Compiler-setting drift between deploy and verify causes Bytecode does not match deployed contract failures. The examples below use Base Sepolia (chain ID 84532). Set up the environment:
.env

Verifier URL

Every Foundry verification command takes --verifier-url pointing at Tenderly’s verification API, and --etherscan-api-key $TENDERLY_ACCESS_KEY for authentication:
Each URL segment maps to a value you can read off the Dashboard:

Private verification

Privately verified contracts are visible only to your project’s members, under Contracts in the Dashboard.
showLineNumbers
forge verify-contract expects constructor arguments already ABI-encoded; use cast abi-encode to produce them. --watch polls the verifier until verification finishes and prints the result.
Foundry’s output prints a URL: https://etherscan.io/address/... line on success. This is a display quirk of the Etherscan-compatible flow; the contract was verified at Tenderly, not Etherscan. Confirm in the Dashboard under Contracts.

Public verification

Swap the verifier URL for the /public-suffixed variant; nothing else changes. The verified source page becomes reachable by anyone with the link, without a Tenderly login.
showLineNumbers
Public verification is irreversible. A contract verified publicly stays public. Private and public verifications are independent records: to make a privately verified contract public, re-verify it against the /public URL.

Deploy and verify in one step

forge create and forge script accept the same flags inline, so deployment and verification run as one command. Unlike forge verify-contract, both take constructor arguments as raw values and ABI-encode them for you:
showLineNumbers
Put --constructor-args last. It greedily consumes the rest of the command line, so any flag placed after it is treated as another constructor argument.
For multi-contract deployments, use forge script with --slow; every contract the script deploys is verified automatically with its constructor arguments taken from the broadcast log. Without --slow, broadcast batching can submit a transaction before the previous one is confirmed, which can race the verification step:
showLineNumbers

Verify a contract you didn’t deploy

When a contract is verified on a public explorer (Etherscan, Basescan) but not in your Tenderly project, clone the verified source locally with forge clone and re-verify it through Tenderly’s API:
showLineNumbers
forge clone downloads the verified source from the source chain’s Etherscan-compatible API (use the matching explorer’s key for each chain), reconstructs the project layout, and pins the compiler settings the original deployer used. Run forge build in the cloned directory to confirm it compiles; import-resolution errors usually trace back to remappings.txt. Then verify against Tenderly:
showLineNumbers
If verification fails with Bytecode does not match deployed contract, pass the original compiler settings explicitly. All of them are listed on the explorer page where the contract is already verified, under the contract source code section:
showLineNumbers

Contracts that live in the lib directory

forge build only compiles what’s reachable from src/. A contract that exists solely inside a lib/ dependency (a proxy, a standard ERC implementation) never enters the build cache, and forge verify-contract fails with:
Create a one-line src/Imports.sol that imports the contract, then rebuild:
src/Imports.sol
Imports.sol is never sent to the verifier; it only forces the compiler to cache the contract and its dependencies. Then verify using the contract’s full lib/ path, since that’s where the source physically lives. For a TransparentUpgradeableProxy you didn’t deploy, the constructor is (address _logic, address initialOwner, bytes _data), and most of it can be reconstructed from chain state:
showLineNumbers
Compiler version, optimizer runs, and EVM version come from the explorer page of the already-verified implementation. If the implementation isn’t verified anywhere, fall back to the deploying project’s foundry.toml or deployment scripts.

Troubleshooting