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:
-
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.
-
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
Component
Forge
Describe the feature you would like
forge verify-contractdoes not expose any way to passlicenseTypeto Etherscan'sverifysourcecodeendpoint, so contracts verified via Foundry end up with an emptyLicenseTypefield on Etherscan.The pre-existing direct-API verification flow (e.g. older
verify.pyscripts) used to POSTlicenseType=13forAGPL-3.0-or-later(or5forGPL-3.0-or-later, etc., per Etherscan's supported license types). After migrating toforge verify-contract,result[0].LicenseTypefrom the v2getsourcecodeAPI comes back as"".Repro:
forge verify-contract \ <ADDR> src/MyContract.sol:MyContract \ --verifier etherscan \ --flatten \ --watch \ --etherscan-api-key $ETHERSCAN_API_KEYThen:
Expected:
LicenseTypeis 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 onLicenseType(e.g. license-policy CI checks).Root cause in the codebase:
crates/block-explorers/src/verify.rsinfoundry-rs/block-explorers— theVerifyContractstruct has nolicense_typefield. It only exposesaddress,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.rsinfoundry-rs/foundry— the function that buildsVerifyContractfromVerifyArgspopulates the typed fields but never writes alicenseTypekey intoother.A grep of either repo for
licenseType/license_typein the verify modules turns up no hits.Proposed solutions
Two reasonable shapes, in order of preference:
Dedicated
--license-type <NAME|NUMBER>flag onforge 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 onVerifyContractinfoundry-rs/block-explorers, serialized aslicenseTypeto match Etherscan's parameter name.Generic
--extra <key>=<value>(repeatable) passthrough that populates the existingother: HashMap<String, String>onVerifyContract. 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-mainnetafter migrating from a direct-API verifier to aforge verify-contractwrapper.Foundry version where this was reproduced: