Available on Whitechain Sepolia today. The steps use the Blockscout explorer at
https://explorer.testnet.whitechain.io. For a plain, non-proxy contract, see Deploy with Hardhat or Deploy with Foundry.How proxy verification works
An upgradeable contract is two contracts: a proxy that holds the state and a fixed address, and an implementation (also called the logic contract) that holds the code. The proxy forwards every call to the implementation withdelegatecall, so users always interact with the proxy address while the logic can be swapped.
Proxy verification is therefore a two-part job. You verify the implementation so its source and ABI are known, and you verify the proxy so its own source is known. Blockscout links the two by reading the EIP-1967 implementation slot from the proxy’s storage. Once both are verified and linked, the proxy address gains a Read/Write proxy tab that exposes the implementation’s functions.
Whitechain runs standard OP Stack Blockscout, which reads these slots automatically:
Transparent and UUPS proxies share one slot, so Blockscout cannot tell them apart. It labels every EIP-1967 detection “Transparent Proxy”, including UUPS. The label is cosmetic and does not affect the linkage or the proxy tab.
Before you start
- A proxy and its implementation deployed on Whitechain Sepolia. See Deploy with Hardhat or Deploy with Foundry.
- Three values recorded at deploy time: the implementation address, the proxy address, and the ABI-encoded initializer calldata passed as the proxy’s second constructor argument. You cannot verify the proxy without the calldata.
- The OpenZeppelin contracts installed, because the proxy source comes from that library. Run
npm install @openzeppelin/contractsfor Hardhat, orforge install OpenZeppelin/openzeppelin-contractsfor Foundry. - The Blockscout verifier configured for chain
1874. Hardhat needs thechainDescriptorsentry shown on the Deploy with Hardhat page. Foundry takes the explorer URL on the command line.
Hardhat 3 needs the proxy named as a build root
Hardhat 3 only emits artifacts for contracts declared in files undercontracts/. ERC1967Proxy lives in the OpenZeppelin package, so importing it from a local file is not enough: a file that declares no contract of its own produces no artifact. Name it in solidity.npmFilesToBuild instead.
hardhat.config.ts
hardhat compile succeeds but no ERC1967Proxy artifact exists, and both deployment and verification fail with an artifact-not-found error.
1. Verify the implementation contract
Verify the implementation exactly like any standalone contract. It takes no constructor arguments in most upgradeable patterns, because upgradeable contracts use aninitialize function instead of a constructor.
- Hardhat
- Foundry
Terminal
Foundry’s
--verifier-url ends with /api/. Hardhat’s chainDescriptors apiUrl ends with /api and no trailing slash. The two tools differ; each form is correct for its own tool.2. Verify the proxy contract
The proxy isERC1967Proxy, TransparentUpgradeableProxy, or BeaconProxy, and it was deployed with two constructor arguments: the implementation address and the initializer calldata. Pass both when you verify.
- Hardhat
- Foundry
Terminal
--force is required here. Hardhat checks whether an address is already verified before submitting, and that check uses Blockscout’s legacy getsourcecode endpoint. For a detected proxy, that endpoint resolves through to the implementation and returns the implementation’s name and source. Hardhat reads that as “the proxy is already verified”, skips the submission, and exits successfully while the proxy’s own source stays unverified. --force submits anyway.3. Confirm the result
Check the API rather than the page, because the page can look correct while the proxy itself is unverified.Terminal
Then open the proxy on the explorer. Under
Contract there are three tabs:
The implementation address has only
Code and Read/Write contract, and no proxy tab. That is correct: it is not a proxy.
Open Read/Write proxy and check that the functions listed are the implementation’s, not the proxy’s. The proxy’s own ABI has 8 entries and no state getters. A UUPS implementation adds its own functions alongside the inherited upgradeToAndCall, proxiableUUID, owner, transferOwnership, renounceOwnership, and UPGRADE_INTERFACE_VERSION.
/api/v2/smart-contracts/<address>/methods-read and methods-write return 404 on this Blockscout build. To check the proxy state from a script, read proxy_type and implementations from /api/v2/smart-contracts/<address>.4. Verify again after an upgrade
An upgrade points the proxy at new code, so the verification you already did no longer covers it. Two things happen when you callupgradeToAndCall.
The proxy stays verified. Its own source and constructor arguments do not change, so you never re-verify the proxy itself. Blockscout re-reads the EIP-1967 slot on each page load and follows the new implementation automatically.
The new implementation is unverified. Until you verify it, the API returns implementations with a name of null, and the Read/Write proxy tab cannot show the new functions. Verify the new implementation exactly as in step 1, then confirm the linkage as in step 3.
Terminal
Every implementation you deploy stays on chain at its own address, verified independently. Only the address in the EIP-1967 slot is live. Earlier implementations remain verified but unreferenced, which is expected and needs no cleanup.
Worked example on Whitechain Sepolia
A UUPS pair on Whitechain Sepolia, verified with the steps above and then upgraded once, for comparison against your own output.
The proxy was verified once, before any upgrade, and is still verified now. Every contract reports
is_fully_verified: true and verified_via_sourcify: false, so Blockscout verified them natively.
Two earlier implementations remain on chain, verified and no longer referenced by the slot: BoxV1 0xc6d4397ba81e5f74e49d77f52b1d6c364a7a060b and an earlier BoxV2 0x92b00d7db1aef4801f81167ffe58b2d35f0b9b4c.
Troubleshooting
`hardhat verify` reports the proxy is already verified, but its source is missing
`hardhat verify` reports the proxy is already verified, but its source is missing
Blockscout’s legacy
getsourcecode resolves through the proxy to the implementation, satisfying Hardhat’s pre-check. Re-run with --force, then confirm name and is_fully_verified on the proxy through the API.Artifact for `ERC1967Proxy` not found
Artifact for `ERC1967Proxy` not found
Hardhat 3 emits artifacts only for contracts declared under
contracts/. Add the proxy to solidity.npmFilesToBuild, as shown in “Before you start”.`hardhat verify` exits with code 1 although Blockscout succeeded
`hardhat verify` exits with code 1 although Blockscout succeeded
The toolbox enables several verifiers. A different verifier in the same run failed, for example Etherscan without an API key. Read the per-verifier output. A Blockscout success is independent of the others.
Verification reports a compiler mismatch
Verification reports a compiler mismatch
The solc version, EVM version, or optimizer setting differs from the deployed bytecode. Compare
artifacts/build-info against the values the explorer reports, then verify again with the matching settings.No `Read/Write proxy` tab
No `Read/Write proxy` tab
The implementation is not verified, or the slot is non-standard. Verify the implementation first, then reload the proxy page. Blockscout re-reads the slot on load.
Hardhat rejects the network configuration at startup
Hardhat rejects the network configuration at startup
The private key in
.env has no 0x prefix. Add the prefix, or normalize it in hardhat.config.ts before passing it to accounts.
