Verification publishes your Solidity source alongside the deployed bytecode and proves the two match — it does not change the contract, and it cannot be done wrong in a way that costs gas beyond a failed submission. What it does require is exactness: the same compiler version, the same optimizer settings, the same EVM version and correctly ABI-encoded constructor arguments as the deploy that produced the bytecode. Nearly every failed verification is a mismatch in one of those four.
This page covers Arc in its first weeks of mainnet. Where something is still settling we say so rather than guess, and we revise this page as the ecosystem fills in. Last reviewed: September 2026.
What does verification actually prove?
Verification recompiles your submitted source with the settings you declare and compares the resulting bytecode against what is deployed at the address. A match means the source shown on the explorer is the source that produced the contract. It does not mean the contract is safe or audited — a verified honeypot is still a honeypot. What verification buys is readable logic and a real ABI for tooling to decode calls and events against.
This page covers how a developer verifies a contract they deployed. If you are on the other side of that — assessing a contract someone else deployed — reading a token contract covers what to look for, and the Arcscan guide covers the explorer's general navigation and transaction views.
What do you need before you verify?
Five things, and you should record all five at deploy time rather than reconstructing them later:
The exact compiler version, including the commit hash, not just 0.8.26. The optimizer setting and run count exactly as compiled. The EVM version you pinned. The contract address and the network it is on. And the constructor arguments, ABI-encoded — this is the one people lose, because it lives in a deploy script rather than a config file.
If you deployed with Foundry using forge script --broadcast, all of this is preserved in broadcast/<Script>.s.sol/<chainId>/run-latest.json. With Hardhat, the arguments live in your deploy script — commit them to a file rather than relying on shell history.
# ABI-encode constructor args by hand if you need them explicitly
cast abi-encode "constructor(string,string,uint8)" "MyToken" "MTK" 18
Network parameters: Arc's testnet explorer is testnet.arcscan.app and its chain ID is 5042002. The mainnet explorer URL and chain ID are not published in this guide — every mainnet value below is written as MAINNET-TBD and you must substitute the real value from Circle's official documentation. Do not guess the chain ID; verification submitted against the wrong chain simply will not find your contract.
How do you verify with Foundry?
forge verify-contract takes the address, the fully qualified contract path, and the explorer target. On testnet:
# SOLC_VERSION must be the full string, e.g. v0.8.26+commit.<hash>
# Read it from your build output; do not copy one from a guide.
forge verify-contract \
0xYourDeployedAddress \
src/MyToken.sol:MyToken \
--chain 5042002 \
--verifier-url https://testnet.arcscan.app/api \
--etherscan-api-key "$ARCSCAN_API_KEY" \
--compiler-version "$SOLC_VERSION" \
--num-of-optimizations 200 \
--constructor-args "$(cast abi-encode 'constructor(string,string,uint8)' 'MyToken' 'MTK' 18)" \
--watch
# Mainnet — substitute the published explorer API endpoint and chain ID.
forge verify-contract \
0xYourDeployedAddress \
src/MyToken.sol:MyToken \
--chain "$ARC_MAINNET_CHAIN_ID" \
--verifier-url "$ARC_MAINNET_EXPLORER_API" \
--etherscan-api-key "$ARCSCAN_API_KEY" \
--compiler-version "$SOLC_VERSION" \
--num-of-optimizations 200 \
--watch
# MAINNET-TBD: ARC_MAINNET_CHAIN_ID and ARC_MAINNET_EXPLORER_API
Verifying at deploy time is the pattern worth standardising on, because it removes the record-the-settings problem entirely:
forge script script/Deploy.s.sol:Deploy \
--rpc-url arc_testnet \
--private-key "$DEPLOYER_KEY" \
--broadcast \
--verify \
--verifier-url https://testnet.arcscan.app/api \
--etherscan-api-key "$ARCSCAN_API_KEY"
Both commands assume Arcscan speaks the Etherscan verification API, which is what Foundry's --etherscan-api-key and --verifier-url pair targets. That is the right first attempt and it is not a settled fact: Arc mainnet launched on September 16, 2026, and whether the explorer's /api endpoint is Etherscan-shaped or Blockscout-shaped, and whether a key is required, is documented by Arcscan and Circle rather than by us. Read it from the explorer's own API documentation before scripting around it.
The cost of guessing wrong is low, because the backends fail differently. A Blockscout backend rejects an Etherscan-shaped submission with a request-shape or unsupported-endpoint error rather than a compilation result; swap --etherscan-api-key for --verifier blockscout and point --verifier-url at the Blockscout API path. An Etherscan-compatible explorer missing a key names the key. Neither failure costs gas.
With --watch, a successful run prints the submission GUID, polls, and ends on a line saying the contract was successfully verified, with the explorer URL for the published source. A report that compiled and deployed bytecode do not match means the explorer accepted and compiled your submission — you are past the transport question and into the settings-mismatch list below.
How do you verify with Hardhat?
Hardhat's verify plugin reads the compiler settings from your config, so the only thing you supply on the command line is the address and the constructor arguments in the same order the constructor declares them.
// hardhat.config.ts — verification block
etherscan: {
apiKey: {
arcTestnet: process.env.ARCSCAN_API_KEY ?? "",
arc: process.env.ARCSCAN_API_KEY ?? "",
},
customChains: [
{
network: "arcTestnet",
chainId: 5042002,
urls: {
apiURL: "https://testnet.arcscan.app/api",
browserURL: "https://testnet.arcscan.app",
},
},
{
network: "arc",
// MAINNET-TBD — substitute the published mainnet chain ID.
chainId: Number(process.env.ARC_MAINNET_CHAIN_ID),
urls: {
apiURL: process.env.ARC_MAINNET_EXPLORER_API ?? "MAINNET-TBD",
browserURL: process.env.ARC_MAINNET_EXPLORER ?? "MAINNET-TBD",
},
},
],
},
npx hardhat verify --network arcTestnet \
0xYourDeployedAddress "MyToken" "MTK" 18
# Arguments too complex for the command line — use a file
npx hardhat verify --network arcTestnet \
--constructor-args scripts/args.ts \
0xYourDeployedAddress
The customChains entry is the whole trick. Without it Hardhat does not know where to send the request for a chain it has never heard of, and fails with an unsupported-network error rather than a verification error. Substitute Circle's published mainnet chain ID and explorer URL into the arc entry before using it; the MAINNET-TBD strings above are placeholders, not values.
@nomicfoundation/hardhat-verify speaks the Etherscan API shape, so it reaches Arcscan on the same condition Foundry does — that the explorer answers Etherscan-shaped requests — which is a live question three weeks into the chain's existence. The plugin has no Blockscout mode. If Arcscan turns out to run one, verify through Foundry's --verifier blockscout against the same artifacts, or through the upload form below.
How do you verify manually through the Arcscan UI?
When the API path fails — no key available, a plugin version mismatch, linked libraries — the standard JSON input route almost always works, because it hands the explorer the exact compiler input rather than asking it to reconstruct one.
Generate the input from your build artifacts, then paste it into the explorer's verification form.
# Foundry: emit the standard JSON input for one contract
forge verify-contract 0xYourAddress src/MyToken.sol:MyToken \
--show-standard-json-input > standard-input.json
# Hardhat: the same document already exists in the build info
ls artifacts/build-info/*.json
On testnet.arcscan.app, search the contract address and open the Contract tab — on an unverified contract it shows raw creation bytecode and a prompt to verify and publish the source. That prompt is the entry point. The form asks for four things: a compiler type, where you choose Solidity (Standard JSON Input) rather than single file or multi-part; the compiler version, matching your build output's full v0.8.26+commit.<hash> string exactly; a license selector; and a file upload for the JSON. Below the upload sits a constructor arguments field — paste the ABI-encoded arguments there without the leading 0x, and leave it empty if your constructor takes none.
On success the page reloads with your source tree under the Contract tab and the Read and Write tabs populated from the ABI; if the Read tab lists your functions, you are done. The mainnet flow is expected to mirror this at the MAINNET-TBD URL — substitute Circle's published mainnet explorer address. Explorer interfaces get rearranged on a new chain, so treat those field names as what to look for rather than a fixed sequence of clicks.
Why does verification fail, and how do you fix it?
Bytecode mismatch is the near-universal error, and it has a short list of causes.
Optimizer settings differ. The most common single cause. Runs of 200 versus 999999 produce different bytecode from identical source. Check the value in the config you actually deployed with, not the one currently in your working tree.
Compiler version differs at the commit level. 0.8.26 compiled by two different builds is not necessarily identical. Supply the full v0.8.26+commit.<hash> string.
EVM version differs. If you deployed before changing evmVersion — or relied on a compiler default that shifted between toolchain updates — the opcodes differ. This is not hypothetical: Solidity's 0.8.30 release in May 2025 changed the compiler's default EVM version from cancun to prague, per the Solidity team's release announcement, so a project that never touched the setting can produce different bytecode either side of a routine compiler bump. Pin it explicitly in both config and verification.
Constructor arguments are wrong or misordered. Encode them with cast abi-encode against the real constructor signature and compare against the deployment transaction's input data, where they are appended after the creation bytecode.
Unlinked libraries. A contract with external libraries carries placeholder addresses that must be supplied at verification time via --libraries. Standard JSON input handles this more reliably than flat-file submission.
Immutable variables. Values written at construction appear in runtime bytecode, so a verifier that does not account for immutables reports a mismatch on otherwise-correct source. Standard JSON input is again the more reliable path.
Proxies. Verifying a proxy verifies the proxy, not the implementation. Verify the implementation at its own address first — that step is the same on every explorer. Linking the two, so the Read and Write tabs surface the implementation's ABI rather than the proxy's handful of functions, depends on the explorer offering a proxy-detection flow; look for a proxy option in the contract page's tools menu rather than assuming Arcscan has shipped one in Arc's first weeks. If it has not, both contracts still verify independently and the implementation's Read tab works at its own address — you lose only the convenience of reading through the proxy.
For the compiler and network configuration this page assumes, see Hardhat and Foundry on Arc. If you have not deployed yet, deploying a smart contract on Arc is the step before this one — and verifying immediately after deployment, while the settings are still in front of you, is materially easier than doing it a week later.
FAQ
Is verifying a contract on Arcscan free?
Yes. Verification is an off-chain submission to the explorer that recompiles and compares source against deployed bytecode. It costs no gas and modifies nothing on chain, so a failed attempt costs nothing but the retry. Explorers commonly require a free registered API key for the programmatic route while leaving the manual upload form open to anyone; whether Arcscan does that on mainnet is set out in the explorer's own API documentation, and Arc is new enough that this is worth reading rather than inferring from another chain's explorer.
Can I verify a contract someone else deployed?
Yes, if you have the exact source and compiler settings. Verification is a proof about bytecode, not a claim of ownership, so anyone holding matching source can submit it — which is how forks of well-known contracts often end up verified by third parties.
What is the difference between verification and an audit?
Verification proves the published source matches the deployed bytecode. An audit is a human review of whether that source is safe. A fully verified contract can still mint unlimited supply or block transfers — the source being readable is what lets you find that out.
Which compiler version string does Arcscan need?
The full version including the commit hash, in the form v0.8.26+commit.<hash>, read from your own build output. The short form is ambiguous across builds and is the second most common cause of bytecode mismatch after optimizer settings.
My contract deployed fine but verification says bytecode mismatch. Where do I start?
Optimizer runs first, then the full compiler version, then EVM version, then constructor arguments. Compare each against the config that produced the deployment rather than your current working tree, and if all four match, switch to standard JSON input, which removes the reconstruction step entirely.
Launching a token on Arc? Team Finance deploys audited, pre-verified token, lock and vesting contracts with flat USDC-quoted fees and no percentage cut of supply or liquidity — see /arc for the full Arc hub.Open Team Finance →Sources: Circle documentation and Arc developer docs (arc.io, developers.circle.com) for chain and fee parameters; Foundry Book (book.getfoundry.sh) for forge verify-contract flags and the Blockscout verifier option; Hardhat documentation (hardhat.org) for hardhat-verify and customChains configuration; Solidity documentation (docs.soliditylang.org) for standard JSON input and metadata behaviour, and the Solidity 0.8.30 release announcement (soliditylang.org, May 2025) for the change of default EVM version from cancun to prague; Arc testnet explorer testnet.arcscan.app.
Last verified: August 2026