Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

hardhat-upgrades: Use actual build info when verifiying contracts on explorers #1054

Open
SKYBITDev3 opened this issue Jul 30, 2024 · 1 comment

Comments

@SKYBITDev3
Copy link

SKYBITDev3 commented Jul 30, 2024

When verifying deployed contracts it's better practice to use the artifacts and source code that were actually used for the deployment instead of using old saved ones in hardhat-upgrades.

I don't use upgrades.deployProxy because I choose to deploy the proxy via the CREATE3 method in order to reliably get the same address on multiple blockchains. So after deploying the implementation and proxy, verification fails because of bytecode mismatch with the old saved bytecode of the proxy in hardhat-upgrades.

A solution that has worked for me is to use hre.artifacts.getBuildInfo which gives everything needed for verification.
e.g. I replaced

const constructorArguments = inferConstructorArgs(tx.input, contractInfo.artifact.bytecode);

with

    const buildInfo = await hre.artifacts.getBuildInfo(`${contractInfo.artifact.sourceName}:${contractInfo.artifact.contractName}`);
    const bytecode = `0x${buildInfo.output.contracts[contractInfo.artifact.sourceName][contractInfo.artifact.contractName].evm.bytecode.object}`;

    const constructorArguments = inferConstructorArgs(tx.input, bytecode);

and

const params = {
contractAddress: address,
sourceCode: JSON.stringify(artifactsBuildInfo.input),
contractName: `${artifact.sourceName}:${artifact.contractName}`,
compilerVersion: `v${artifactsBuildInfo.solcLongVersion}`,

with

    const buildInfo = await hre.artifacts.getBuildInfo(`${artifact.sourceName}:${artifact.contractName}`);

    const buildInfoInput = Object.assign({}, buildInfo.input); // copies language, sources, settings
    buildInfoInput.sources = {}; // replace sources as otherwise it includes all solidity files under contracts directory
    [ // include only those needed by proxy
        "@openzeppelin/contracts/access/Ownable.sol",
        "@openzeppelin/contracts/interfaces/IERC1967.sol",
        "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol",
        "@openzeppelin/contracts/proxy/beacon/IBeacon.sol",
        "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol",
        "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol",
        "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol",
        "@openzeppelin/contracts/proxy/Proxy.sol",
        "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol",
        "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol",
        "@openzeppelin/contracts/utils/Address.sol",
        "@openzeppelin/contracts/utils/Context.sol",
        "@openzeppelin/contracts/utils/StorageSlot.sol"
    ].forEach(path => buildInfoInput.sources[path] = buildInfo.input.sources[path]);

    const params = {
        contractAddress: address,
        sourceCode: JSON.stringify(buildInfoInput),
        contractName: `${artifact.sourceName}:${artifact.contractName}`,
        compilerVersion: `v${buildInfo.solcLongVersion}`,

Also, I've found that the bytecode can have extra data at the beginning (e.g. salt), so startsWith fails in such cases.
So I replaced

if (txInput.startsWith(creationCode)) {
return txInput.substring(creationCode.length);

with

    const creationCodeWithout0x = creationCode.slice(2)

    if (txInput.includes(creationCodeWithout0x)) {
        return txInput.substring(txInput.indexOf(creationCodeWithout0x) + creationCodeWithout0x.length);

Verification on explorers worked after these changes.

Please update the code in your repository so that verification on explorers will still work for upgradeable contracts even if we deploy a newer version of the proxy.

@SKYBITDev3
Copy link
Author

SKYBITDev3 commented Aug 1, 2024

I've updated my code to replace sources with only those that are needed for a proxy contract (I just followed the list in @openzeppelin/upgrades-core/artifacts/build-info-v5.json), as otherwise all source files under contracts directory would be included, which may not be good for privacy. Verification still works after this change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant