Skip to content

feat(forge verify-contract): expose licenseType so Etherscan-verified contracts don't end up with empty LicenseType #14475

Description

@0xLaz3r

Component

Forge

Describe the feature you would like

forge verify-contract does not expose any way to pass licenseType to Etherscan's verifysourcecode endpoint, so contracts verified via Foundry end up with an empty LicenseType field on Etherscan.

The pre-existing direct-API verification flow (e.g. older verify.py scripts) used to POST licenseType=13 for AGPL-3.0-or-later (or 5 for GPL-3.0-or-later, etc., per Etherscan's supported license types). After migrating to forge verify-contract, result[0].LicenseType from the v2 getsourcecode API comes back as "".

Repro:

forge verify-contract \
    <ADDR> src/MyContract.sol:MyContract \
    --verifier etherscan \
    --flatten \
    --watch \
    --etherscan-api-key $ETHERSCAN_API_KEY

Then:

curl -s "https://api.etherscan.io/v2/api?chainid=1&module=contract&action=getsourcecode&address=<ADDR>&apikey=$ETHERSCAN_API_KEY" \
  | jq '.result[0].LicenseType'
# → ""

Expected: LicenseType is set to whatever SPDX license is declared in the source (e.g. "GNU AGPLv3").

Actual: empty string. Etherscan's UI sometimes back-fills it from the // SPDX-License-Identifier: … comment via a heuristic, but the API field stays blank, which breaks downstream automation that asserts on LicenseType (e.g. license-policy CI checks).

Root cause in the codebase:

  • crates/block-explorers/src/verify.rs in foundry-rs/block-explorers — the VerifyContract struct has no license_type field. It only exposes address, source, code_format, contract_name, compiler_version, optimization_used, runs, constructor_arguments, evm_version, via_ir, plus a #[serde(flatten)] other: HashMap<String, String> catch-all.
  • crates/verify/src/etherscan/mod.rs in foundry-rs/foundry — the function that builds VerifyContract from VerifyArgs populates the typed fields but never writes a licenseType key into other.

A grep of either repo for licenseType / license_type in the verify modules turns up no hits.

Proposed solutions

Two reasonable shapes, in order of preference:

  1. Dedicated --license-type <NAME|NUMBER> flag on forge verify-contract. Accept either an SPDX identifier (AGPL-3.0-or-later, GPL-3.0-or-later, MIT, …) — mapped internally to Etherscan's numeric codes — or the raw numeric code. Default could continue to be unset (current behaviour) so this is non-breaking. If the flag isn't passed, optionally auto-detect from the // SPDX-License-Identifier: line in the contract source.

    This would also need a license_type: Option<String> field on VerifyContract in foundry-rs/block-explorers, serialized as licenseType to match Etherscan's parameter name.

  2. Generic --extra <key>=<value> (repeatable) passthrough that populates the existing other: HashMap<String, String> on VerifyContract. More flexible (covers other Etherscan parameters that may be added in the future without a Foundry release), at the cost of being less ergonomic and less self-documenting than option 1.

Option 1 is more user-friendly for the most common case; option 2 is a smaller diff. Either unblocks the use case.

Additional context

Surfaced while debugging a verification regression in sky-ecosystem/spells-mainnet after migrating from a direct-API verifier to a forge verify-contract wrapper.

Foundry version where this was reproduced:

forge Version: 1.5.1-stable
Commit SHA: b0a9dd9ceda36f63e2326ce530c10e6916f4b8a2
Build Timestamp: 2025-12-22T11:41:09.812070000Z

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    Status
    Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions