Contract verification on Defender V2 doesn't work with Foundry

Verification via Etherscan using openzeppelin-foundry-upgrades does not work.

:computer: Environment

Deploying using foundry on Mac OS 14.6.1

forge 0.2.0 (d75318c 2024-09-01T00:24:22.270134000Z)

openzeppelin-foundry-upgrades v0.3.6 (2024-09-24)

[submodule "lib/openzeppelin-contracts"]
	path = lib/openzeppelin-contracts
	url = https://github.com/openzeppelin/openzeppelin-contracts
	branch = release-v5.0
[submodule "lib/openzeppelin-contracts-upgradeable"]
	path = lib/openzeppelin-contracts-upgradeable
	url = https://github.com/openzeppelin/openzeppelin-contracts-upgradeable
	branch = release-v5.0

:memo:Details

Deploying both with and without the --verify flag does not work. The Defender console says the deployment is verified

But clearly the contract is not verified: https://sepolia.etherscan.io/address/0x450f5cB6c92e8DE215bC113D9cB34436a3c7957A#code

The Defender UI shows that the bytecode matches

And attempting to manually verify fails (it seems as though verification does not support foundry's build process).

:1234: Code to reproduce

Deployed using:

forge clean \
&& forge build \
&& forge script script/foundry/DefenderDeploySepolia.s.sol \
--force \
--rpc-url <rpc_url> -vvvv

and

forge clean \
&& forge build \
&& forge script script/foundry/DefenderDeploySepolia.s.sol \
--force \
--rpc-url <rpc_url> --broadcast --verify -vvvv

This particular contract is deployed using:

    function _deployRegistry(
        address protocolAdmin,
        string memory relayerId,
        bytes32 salt,
        address relayerAdmin,
        DeployGasConfig memory gasConfig
    ) internal {
        DefenderOptions memory opts;
        TxOverrides memory txOverrides;

        txOverrides.gasLimit = 1_250_000;
        txOverrides.gasPrice = gasConfig.gasPrice;
        txOverrides.maxFeePerGas = gasConfig.maxFeePerGas;
        txOverrides.maxPriorityFeePerGas = gasConfig.maxPriorityFeePerGas;

        opts.useDefenderDeploy = true;
        opts.relayerId = relayerId;
        opts.salt = salt;
        opts.txOverrides = txOverrides;

        ApprovalProcessResponse memory deploymentApprovalProcess = Defender.getDeployApprovalProcess();
        if (deploymentApprovalProcess.via == address(0)) {
            revert(
                string.concat(
                    "Upgrade approval process with id ",
                    deploymentApprovalProcess.approvalProcessId,
                    " has no assigned address"
                )
            );
        }
        console.log("Deploy approval process address", deploymentApprovalProcess.via);
        console.log("Deploy approval process ID", deploymentApprovalProcess.approvalProcessId);
        console.log("Deploy approval type", deploymentApprovalProcess.viaType);

        registry = Defender.deployContract("Cube3Registry.sol", abi.encode(relayerAdmin), opts);
        console.log("Deployed Registry contract to address", registry);
    }
1 Like