ByAUJay
Smart Contract Verification: Source Code, Bytecode, and On‑Chain Metadata
Startups and enterprises can only treat a deployed contract as a “known good” binary when its source, compiler settings, and resulting bytecode are provably linked. This guide turns that principle into an actionable playbook for decision‑makers, with 2025‑ready practices for EVM chains.
Summary (meta description)
A practical, 2025‑ready playbook for enterprise‑grade smart contract verification across Etherscan, Sourcify, and Blockscout—covering source/bytecode linkage, CBOR metadata, proxies, immutables, constructor args, and emerging practices like ethdebug, cross‑chain sharing, and attested build provenance. (hardhat.org)
Why verification matters more in 2025
- Ethereum’s Pectra upgrade went live on May 7, 2025 (epoch 364,032), enabling EIP‑7702 “EOA code,” which lets externally owned accounts temporarily delegate execution to deployed code. This expands the surface where “what code am I authorizing?” must be crystal clear. (ethereum.org)
- The EVM Object Format (EOF) is not on mainnet yet, but Solidity 0.8.29 added experimental EOF support, and core devs signal EOF for future hard forks. Verification tooling and processes should anticipate EOF’s code/data separation and validation rules. (soliditylang.org)
Takeaway: verification is no longer a “nice to have” checkbox. It’s an operational control that underpins safe wallets, upgrade processes, and cross‑chain deployments.
The three pillars: Source code, bytecode, on‑chain metadata
- Source code and compiler settings
- The Solidity compiler emits a metadata JSON that captures sources, compiler version, optimizer runs, EVM version, libraries, and ABI. Make this the canonical build artifact in your CI. (docs.soliditylang.org)
- Bytecode (creation vs runtime)
- The creation (init) code runs once to deploy; the runtime code stays on chain and is what users call. Verification compares locally recompiled bytecode to on‑chain runtime and (sometimes) init code. (docs.sourcify.dev)
- On‑chain metadata (CBOR auxdata)
- By default, Solidity appends a CBOR‑encoded trailer to bytecode containing the IPFS (or Swarm) content hash of the metadata and the solc version. The last two bytes of the deployed bytecode encode the CBOR length, enabling robust parsing. (docs.soliditylang.org)
Why this matters: when the runtime bytecode includes the metadata hash and it matches your metadata.json, you get a cryptographic link from chain → metadata → exact sources/settings used to build. (docs.soliditylang.org)
“Exact match” vs “match”: what your status really means
- Sourcify defines two verification levels:
- exact_match: runtime/creation bytecode and metadata hash all align with your sources and settings (formerly “full/perfect match”).
- match: bytecodes align ignoring differences in metadata hashes (useful when metadata is missing or altered).
- Vyper contracts can’t currently reach exact_match because Vyper doesn’t embed metadata hashes in bytecode. (docs.sourcify.dev)
Blockscout and other explorers tap Sourcify data and expose verification state in the UI and APIs; Blockscout’s verification pipeline and EBD microservice further help share verified artifacts cross‑chain. (docs.blockscout.com)
What’s inside Solidity’s CBOR metadata section (and how to read it)
Solidity’s default trailer encodes keys like ipfs, bzzr1, experimental, and solc. It’s CBOR‑encoded; don’t hardcode the prefix—read the last two bytes to get the CBOR length and decode from there. Example values you’ll see in practice (from Sourcify repository pages) include the raw trailer hex and the decoded IPFS multihash. (docs.soliditylang.org)
Minimal decoding approach:
// Given deployed runtime bytecode as a hex string "0x...": const hex = deployedBytecode.slice(2); const len = parseInt(hex.slice(-4), 16); // last 2 bytes = CBOR length (big-endian) const cborHex = hex.slice(hex.length - (len*2 + 4), hex.length - 4); // Decode cborHex with a CBOR lib, e.g. 'cbor-x', to get { ipfs, bzzr1, solc, experimental }
If you need a reference implementation that converts the “ipfs” multihash into a CIDv0/v1 and fetches metadata.json, see community tooling like solc‑metadata. (docs.soliditylang.org)
Practical implications:
- Ensure settings.metadata.bytecodeHash is “ipfs” (default) and keep settings.metadata.appendCBOR true to preserve exact_match potential. Solidity 0.8.18 added flags to omit CBOR; don’t use those in production unless you fully understand the trade‑offs. (docs.soliditylang.org)
Constructor arguments, immutables, and libraries: the usual verification pain points
- Constructor arguments are ABI‑encoded and appended to the end of the creation code. Explorers like Etherscan require you to submit these for a successful verification. Plan to capture and store them at deploy time. (info.etherscan.com)
- Immutable variables are inlined into the deployed runtime bytecode at fixed offsets. Verifiers need the compiler’s immutableReferences map to zero out those offsets before comparing bytecode. Expect exact_match to succeed only when these positions and values reconcile. (docs.solidity.org)
- Libraries must be linked deterministically; mismatched or unlinked library addresses produce different bytecode. Your Standard JSON must include libraries mapping or your framework must inject them. (docs.soliditylang.org)
Decision‑maker checklist:
- Require your deploy scripts to persist: constructor args (ABI‑encoded), solc version, optimizer runs, evmVersion, libraries used, and build hash (commit SHA, artifact digest). Keep them alongside metadata.json. (docs.soliditylang.org)
Proxy and upgradeable systems: verify both proxy and logic
- A proxy’s address is not the logic you’re calling. EIP‑1967 standardizes storage slots for implementation, admin, and beacon addresses; explorers and tools read those slots to display the implementation. Your process must verify the proxy, the implementation, and (if used) the beacon. (eips.ethereum.org)
- Etherscan and Blockscout detect common patterns (EIP‑1967, Beacon, UUPS, Diamond) and show both proxy and implementation views. Don’t rely on “proxy is verified” alone—ensure the current implementation contract is verified. (medium.com)
Operational pitfalls:
- Attackers can hide malicious logic behind chains of proxies or custom slots. Institutional policy should require reading the standardized storage slots and verifying the current implementation’s source/bytecode link before interacting. (eips.ethereum.org)
Cross‑chain and cross‑explorer verification
- Contracts often deploy the same runtime across EVM chains via CREATE2 or factory patterns. Blockscout’s Ethereum Bytecode Database (EBD) addresses the fragmentation by sharing verified contract info across chains; adopt tools that consult shared datasets. (docs.blockscout.com)
- CREATE2 deterministically derives addresses from salt, deployer, and init code. Your verification pipeline should store the salt and init code hash for reproducibility and audit. (eips.ethereum.org)
- Etherscan offers “Cross‑Chain Similar Match Verification” and a unified API key across its explorers—helpful for enterprises running multi‑chain deployments. (info.etherscan.com)
Practical, step‑by‑step verification flows
A) Hardhat (multi‑provider programmatic verification)
- Use @nomicfoundation/hardhat‑verify. It supports Etherscan, Sourcify, and Blockscout. Match your build profile at compile and verify time to avoid subtle mismatches. (hardhat.org)
// hardhat.config.ts import { defineConfig } from "hardhat/config"; import hardhatVerify from "@nomicfoundation/hardhat-verify"; export default defineConfig({ plugins: [hardhatVerify], etherscan: { apiKey: process.env.ETHERSCAN_API_KEY }, sourcify: { enabled: true }, // optional }); // Verify a deployment npx hardhat build --build-profile production npx hardhat verify --network mainnet DEPLOYED_ADDRESS "arg1" "arg2"
B) Foundry (one‑shot deploy + verify)
- With forge create, pass --verify and choose verifier etherscan/sourcify/blockscout. For existing contracts, forge verify‑contract supports chains and custom verifier URLs. Automate this from CI/CD. (docs.etherscan.io)
forge create --broadcast \ --rpc-url $RPC_URL --private-key $PK \ src/MyContract.sol:MyContract \ --constructor-args "Name" "SYM" 18 \ --verify --verifier etherscan --etherscan-api-key $ETHERSCAN_API_KEY
C) Sourcify API v2 and UI
- New API v2 exposes “exact_match” vs “match,” ABIs, and per‑contract details; the verify.sourcify.dev UI supports importing from Etherscan (API key local in browser), upload of Hardhat/Foundry build-info, and bytecode diff views on failures. (docs.sourcify.dev)
D) Blockscout
- Blockscout proxies verification through Sourcify, showing match status and sources/ABI when successful; it also notes constructor args aren’t in metadata (you may need creation input to reconstruct). (docs.blockscout.com)
Recommended compiler settings for verifiability
In Standard JSON input (or framework equivalents), set:
- settings.metadata.bytecodeHash: "ipfs" (default)
- settings.metadata.appendCBOR: true (Solidity ≥0.8.18 lets you disable; don’t unless necessary)
- settings.metadata.useLiteralContent: true (prevents opaque URLs in metadata)
- Explicit evmVersion to your target fork (e.g., “paris”, “shanghai”, “cancun”). For EOF experiments, “Osaka” is used in 0.8.29 but is not on mainnet—use only in sandboxes. (docs.soliditylang.org)
Security note: don’t flatten sources for verification unless a verifier absolutely requires it. Prefer Standard JSON or build‑info to preserve exact compiler inputs. (docs.sourcify.dev)
Metadata hosting and availability
- Solidity docs historically required publishing metadata.json to IPFS/Swarm so others can retrieve it by hash. Sourcify’s repository mirrors verified contracts and metadata, easing long‑term availability even if IPFS pins disappear. (docs.soliditylang.org)
Enterprise practice:
- Pin metadata.json and all sources to IPFS as part of the build, record the CID(s) in release notes, and verify on Sourcify so the repo also retains a copy. (docs.sourcify.dev)
EOF is coming: what will change for verification?
EOF (EIP‑3540 et al., grouped under EIP‑7692) introduces code/data sections and deploy‑time validation. Some introspection opcodes behave differently against EOF contracts (e.g., EXTCODE* semantics), and relative jump families replace legacy flows. Your explorers and verifiers must parse EOF containers and surface per‑section metadata once it ships to mainnet. Start testing in devnets or compiler experimental backends. (eips.ethereum.org)
Governance and compliance: institutionalizing verification
- Adopt the OWASP Smart Contract Security Verification Standard (SCSVS) as a process baseline, and extend it with “exact_match or fail” gates for production deploys. (owasp.org)
- Track the Verifier Alliance shared database exports for cross‑ecosystem provenance, and prefer explorers that contribute to/consume these shared datasets. (verifieralliance.org)
- If you relied on OpenZeppelin Defender for post‑deploy ops/monitoring, plan migration by July 1, 2026; align your verification/monitoring pipelines with their open‑source successors. (blog.openzeppelin.com)
Optional provenance layer:
- Publish an attestation (e.g., via EAS) that binds contract address → metadata CID → commit SHA → build system fingerprint. Auditors can then verify provenance without centralized records. (github.com)
Emerging best practices that materially reduce risk
- Require exact_match where possible
- For Solidity, aim for exact_match (runtime bytecode + metadata hash proven). If only match is possible (e.g., Vyper), document why, and compensate with stronger review. (docs.sourcify.dev)
- Verify proxy + implementation + beacon
- Enforce verification of all three and require reading EIP‑1967 slots during risk checks. Automate this in your explorer tooling and dashboards. (eips.ethereum.org)
- Treat constructor args as first‑class artifacts
- Persist ABI‑encoded constructor args and the transaction that created the contract; store them alongside build artifacts so verification never blocks on reverse‑engineering. (info.etherscan.com)
- Lock compiler/toolchain versions
- Pin solc (exact build, not “latest”), optimizer settings, and evmVersion; archive the Standard JSON and build‑info. Mixed or floating versions cause spurious mismatches. (docs.soliditylang.org)
- Use multi‑provider verification in CI
- Run Hardhat/Foundry verification to Etherscan + Sourcify + Blockscout during deployments. Fail the pipeline if any provider can’t reach exact_match (for Solidity). (hardhat.org)
- Prepare for EOF
- Add tests that compile with the solc EOF backend (Osaka) in non‑prod profiles to discover tooling gaps early. Track EOF meta EIP progress. (soliditylang.org)
- Cross‑chain reproducibility
- For CREATE2 deployments, persist the salt and keccak256(init_code). For factory deploys, persist determinism rules. This is crucial when regulators or partners ask you to reproduce addresses. (eips.ethereum.org)
Example: end‑to‑end verification with proxies and immutables
Scenario: You deploy a Transparent (EIP‑1967) proxy pointing to Implementation v1 that uses immutables and libraries.
- Build with Standard JSON, metadata.bytecodeHash=ipfs, appendCBOR=true, useLiteralContent=true. Archive metadata.json, ABI, build‑info, constructor args, library addresses. (docs.soliditylang.org)
- Verify the implementation on Sourcify (exact_match expected) and Etherscan. If the implementation uses immutables, ensure the verifier zeroes immutableReferences before comparing. (docs.sourcify.dev)
- Verify the proxy (constructor args typically include implementation address and init calldata). Confirm explorer detects the proxy and displays implementation/admin slots. (eips.ethereum.org)
- Post‑deploy, assert exact_match via Sourcify API v2 and snapshot the ABI from metadata.json (output.abi) for downstream systems. (docs.sourcify.dev)
Byte‑level sanity check: recognize the metadata trailer
You’ll often see deployed bytecode end with a pattern like:
... a2646970667358 2212 <34 bytes> 64 736f6c63 4300 08xx 0033
That’s the CBOR map (“a2” or “a4” etc.) with “ipfs” and “solc” keys, followed by a 2‑byte length. Don’t rely on a fixed prefix; always read the 2‑byte length at the end to locate and decode the CBOR. (docs.soliditylang.org)
What about “verification = safety”?
- Verification proves “code equivalence,” not “code correctness.” A verified proxy can still point to malicious logic; a verified implementation can still contain harmful functions. Educate users and internal approvers accordingly. (medium.com)
A procurement‑grade checklist you can enforce today
- Compiler and build
- Pin exact solc version; archive Standard JSON and build‑info.
- metadata.bytecodeHash=ipfs; appendCBOR=true; useLiteralContent=true. (docs.soliditylang.org)
- Arguments and libraries
- Persist ABI‑encoded constructor args; pin library addresses and names. (info.etherscan.com)
- Verification gates
- Require exact_match on Sourcify for Solidity; fail deploy otherwise (waiver if Vyper). (docs.sourcify.dev)
- Verify on at least two independent verifiers (e.g., Etherscan + Sourcify).
- Proxies
- Verify proxy, implementation, and beacon (if any). Read EIP‑1967 slots; record admin and implementation at deploy/upgrade. (eips.ethereum.org)
- Cross‑chain
- Persist CREATE2 salts and init code hashes; track cross‑explorer verification (use Etherscan’s cross‑chain similar match when applicable; monitor Blockscout EBD). (info.etherscan.com)
- Attest provenance
- Publish EAS attestations binding address → metadata CID → commit SHA for consumer‑verifiable provenance. (github.com)
- Compliance
- Align with OWASP SCSVS and record verification evidence per release. (owasp.org)
Closing thought
Verification is your cryptographic chain of custody—from Git commit to on‑chain runtime. In 2025, with Pectra live and EOF on the horizon, the teams that standardize exact_match verification, proxy/implementation parity, and provenance attestations will reduce operational risk and speed audits across every EVM they touch. (ethereum.org)
References and further reading
- Solidity metadata and CBOR auxdata; compiler options for metadata hash and CBOR appendix. (docs.soliditylang.org)
- Sourcify exact_match vs match, API v2 endpoints, verification UI. (docs.sourcify.dev)
- Etherscan verification docs and constructor arguments. (info.etherscan.com)
- Blockscout verification pipeline and Ethereum Bytecode Database. (docs.blockscout.com)
- Proxy standards: EIP‑1967. (eips.ethereum.org)
- CREATE2 determinism: EIP‑1014. (eips.ethereum.org)
- Solidity immutables and verification implications. (docs.solidity.org)
- Pectra mainnet activation and EIP‑7702. (ethereum.org)
- EOF meta/EIPs and experimental compiler support. (eips.ethereum.org)
- OWASP SCSVS baseline. (owasp.org)
7Block Labs helps teams implement these controls—from build pipelines to cross‑explorer verification and provenance attestations—across Ethereum and major L2s. If you want a short readiness assessment for your verification posture, we’re happy to help.
Like what you're reading? Let's build together.
Get a free 30‑minute consultation with our engineering team.

