DEV Community

Byaigo
Byaigo

Posted on

How to Programmatically Verify Smart Contracts on Etherscan with Python

Why Verify Smart Contracts?

When you deploy a smart contract to Ethereum, the bytecode on-chain is opaque. Users and other developers can't easily audit what the contract does without the original source code. Verifying your contract on Etherscan bridges that gap — it links your human-readable Solidity (or Vyper) source to the bytecode on-chain, so anyone can read and trust the logic.

But doing this manually through Etherscan's web UI for every deployment is tedious. Let's automate it.

The Etherscan API

Etherscan provides a Contract Verification API endpoint. You POST the source code, compiler version, optimization settings, and constructor arguments, and Etherscan does the hard work of checking that the compiled bytecode matches what's on-chain.

Here's the endpoint:

POST https://api.etherscan.io/api
  ?module=contract
  &action=verifysourcecode
  &apikey=YOUR_API_KEY
Enter fullscreen mode Exit fullscreen mode

Python Implementation

Let's build a reusable verify_contract() function using requests:

import requests

def verify_contract(
    api_key: str,
    contract_address: str,
    source_code: str,
    contract_name: str,
    compiler_version: str,
    optimization_used: int = 0,
    runs: int = 200,
    constructor_args: str = "",
    license_type: int = 3,  # MIT
) -> dict:
    """
    Submit a smart contract for verification on Etherscan.

    Args:
        api_key: Your Etherscan API key
        contract_address: Deployed contract address (0x...)
        source_code: Full Solidity source code as a string
        contract_name: Name of the main contract to verify
        compiler_version: Solidity compiler version, e.g. v0.8.24+commit.e11b9ed9
        optimization_used: 1 if optimizer was enabled, else 0
        runs: Number of optimizer runs (default 200)
        constructor_args: ABI-encoded constructor arguments (hex, without 0x)
        license_type: SPDX license type code (1=None, 2=Unlicense, 3=MIT, ...)

    Returns:
        API response as a dict with verification GUID
    """
    url = "https://api.etherscan.io/api"

    payload = {
        "apikey": api_key,
        "module": "contract",
        "action": "verifysourcecode",
        "contractaddress": contract_address,
        "sourceCode": source_code,
        "codeformat": "solidity-single-file",
        "contractname": contract_name,
        "compilerversion": compiler_version,
        "optimizationUsed": optimization_used,
        "runs": runs,
        "constructorArguements": constructor_args,
        "licenseType": license_type,
    }

    resp = requests.post(url, data=payload, timeout=30)
    resp.raise_for_status()
    return resp.json()
Enter fullscreen mode Exit fullscreen mode

Checking Verification Status

Etherscan verification is asynchronous — you get a GUID back and need to poll for the result:

import time

def check_verification_status(api_key: str, guid: str) -> dict:
    """Poll until verification completes or fails."""
    url = "https://api.etherscan.io/api"
    params = {
        "apikey": api_key,
        "module": "contract",
        "action": "checkverifystatus",
        "guid": guid,
    }

    for attempt in range(20):
        resp = requests.get(url, params=params, timeout=10)
        data = resp.json()
        status = data.get("result", "")

        if status == "Pass - Verified":
            print(f"✅ Contract verified: {data}")
            return data
        elif status.startswith("Fail"):
            print(f"❌ Verification failed: {data}")
            return data

        print(f"⏳ Attempt {attempt+1}/20 — still pending...")
        time.sleep(5)

    return {"result": "Timeout — check manually"}
Enter fullscreen mode Exit fullscreen mode

Usage Example

def main():
    API_KEY = "your-etherscan-api-key"
    contract_addr = "0xYourDeployedContractAddress"

    # Read your Solidity source
    with open("MyToken.sol", "r") as f:
        source = f.read()

    result = verify_contract(
        api_key=API_KEY,
        contract_address=contract_addr,
        source_code=source,
        contract_name="MyToken",
        compiler_version="v0.8.24+commit.e11b9ed9",
        optimization_used=1,
        runs=200,
    )

    guid = result.get("result")
    if guid and guid != "Max rate limit reached":
        check_verification_status(API_KEY, guid)
    else:
        print(f"Submission failed: {result}")

if __name__ == "__main__":
    main()
Enter fullscreen mode Exit fullscreen mode

Multi-Chain Support

Etherscan's API also works across other EVM chains. Swap the base URL for:

Chain API URL
Ethereum Mainnet api.etherscan.io
Polygon api.polygonscan.com
BSC api.bscscan.com
Arbitrum api.arbiscan.io
Optimism api-optimistic.etherscan.io
Base api.basescan.org

Why It Matters

Verified contracts build trust. Users can inspect the code before interacting. Tools like Dune Analytics and The Graph can index verified contracts more reliably. And it's a professional standard — serious projects don't skip verification.

Automation ensures every deployment — whether from CI, Hardhat, or Foundry — gets verified without manual steps. That's one less thing to forget after a late-night deploy.


Useful Resources:

  • 🔗 GitHub: github.com/Byaigo
  • 💰 Support: 0x18da907cb9d981bc798acb87ac27b03a2dc3cbb7

Happy building! 🚀

Top comments (0)