Works on: Virtual Environments and public networks (mainnets and testnets). The same plugin handles both; point it at the right network in
hardhat.config.ts.@tenderly/hardhat-tenderly.
Requirements
Automatic proxy verification works out of the box on@tenderly/hardhat-tenderly:
>= 1.10.0(Ethers 5 line)>= 2.1.0(Ethers 6 line)
@openzeppelin/hardhat-upgrades. Hardhat Ignition is not supported for proxy verification yet.
If you’re on a lower plugin version and can’t upgrade, use the manual workaround below.
Automatic verification
The plugin verifies three proxy patterns:TransparentUpgradeableProxy, UUPSUpgradeableProxy, and BeaconProxy.
Under the hood the plugin drives
@nomicfoundation/hardhat-verify with the @openzeppelin/hardhat-upgrades extension. Setting TENDERLY_AUTOMATIC_POPULATE_HARDHAT_VERIFY_CONFIG=true lets the plugin populate @nomicfoundation/hardhat-verify’s verification URL for you.For a clearer view, you can check out this GitHub repo and go to
scripts/proxy/ to see the full example. Automatic
Deploy the proxy as usual using
deployProxy from the @openzeppelin/hardhat-upgrades extension.Under the hood, the automatic verification is implemented by wrapping the deployProxy, and waiting for completion of the deployment. Manual
Verifying the proxy with the manual verification method is done after the proxy contract has been deployed, by calling the
tenderly.verify() function.Workaround for lower versions
When using@tenderly/hardhat-tenderly at versions < 1.10.0 and < 2.1.0, this workaround will enable automatic verification.
You need to verify the following:
- The proxy contract (e.g. OpenZeppelin’s proxies)
- Implementation behind the proxy
- Any dependencies the implementation has
- New implementation instances deployed with upgrades
Overview
In this guide, we’ll use an example Hardhat project and the@tenderly/hardhat-tenderly plugin to demonstrate the verification of OpenZeppelin’s UUPSUpgradeable, TransparentUpgradeableProxy, and BeaconProxy alternatives.
To obtain the address of the deployed implementation, use the @openzeppelin/upgrades-core package and getImplementationAddress function.
Verifying the proxy implementation is usually straightforward; verify it just like any other contract.
example.ts
- Load the exact smart contract of the proxy depending on the type of proxy you’re using, so it gets compiled. You’ll need to import the proxy contracts through the compiler by creating a dummy .sol file.
- Modify
hardhat.config.tsto specify the settings OpenZepplin contracts were compiled with.
example.ts
See the Tenderly CLI repo for non-Homebrew install options.
In
hardhat.config.ts, set tenderly.username and tenderly.project to your project and username slugs.The fastest way to deploy and verify contracts is on a Virtual Environment.
In the Tenderly Dashboard, open Virtual Environments and create a new one. Pick the base network to fork from and a Chain ID, then copy the Admin RPC URL from the Virtual Environment’s details page.
networks: {
virtualMainnet: {
url: process.env.TENDERLY_VIRTUAL_TESTNET_RPC!,
chainId: 73571, // the Chain ID you set when creating the Virtual Environment
},
},
For a full walkthrough see the Virtual Environment quickstart.
When redeploying contracts, remove the
.openzeppelin folder first. It caches information about proxies and their implementations.To verify the proxy contract, create a
DummyProxy.sol file and import the OpenZepplin proxy contracts you’re working with. In doing so, these contracts are loaded and have passed through the compiler, enabling you to reference the exact contract source of the proxy during verification.// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
abstract contract ERC1967ProxyAccess is ERC1967Proxy {}
abstract contract UpgradableBeaconAccess is UpgradeableBeacon {}
abstract contract BeaconProxyAccess is BeaconProxy {}
abstract contract TransparentUpgradeableProxyAccess is TransparentUpgradeableProxy {}
The following overrides map was derived for
@openzeppelin/contracts-upgradeable version 4.9.1.
They may differ for other versions of the package.The
hardhat-tenderly plugin uses both the source code of smart contracts and compiler settings
for verification. If either of these settings is incorrect, the verification will fail.Add the following
overrides map to the config.solidity section of your Hardhat User config object.const config: HardhatUserConfig = {
solidity: {
compilers: [{ version: '0.8.18' } /* OTHER COMPILER VERSIONS*/],
overrides: {
'@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol': {
version: '0.8.9',
settings: {
optimizer: {
enabled: true,
runs: 200,
},
},
},
'@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol': {
version: '0.8.9',
settings: {
optimizer: {
enabled: true,
runs: 200,
},
},
},
'@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol': {
version: '0.8.9',
settings: {
optimizer: {
enabled: true,
runs: 200,
},
},
},
'@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol': {
version: '0.8.9',
settings: {
optimizer: {
enabled: true,
runs: 200,
},
},
},
'contracts/proxy.sol': {
version: '0.8.9',
settings: {
optimizer: {
enabled: true,
runs: 200,
},
},
},
},
},
/* OTHER CONFIG */
};
Verify by proxy type
These code samples show how to verify the implementation and the proxy for OpenZeppelin’s three proxy patterns. The examples use a proxiedVault contract that references an ERC-20 token (TToken).
UUPS proxy
To verify the UUPS proxy and the underlying information, call thehardhat-tenderly plugin twice:
- To verify the implementation, you need to provide the following:
nameof your proxied contract (in our caseVault)- Address where the contract was deployed using the
getImplementationAddressmethod from@openzeppelin/upgrades-core.
- To verify the proxy, provide the following:
ERC1967Proxyas the proxy contractname- Address of the proxy
proxy.address
example.ts
Complete Code Sample
Here’s a complete Hardhat test that does the following:- Deploys the
TToken(needed for the vault) - Deploys
Vaultas a proxy, initialized with theTTokencontract - Verifies the proxy (
ERC1967Proxy) instance deployed atawait proxy.getAddress() - Verifies the implementation instance
Vault, deployed atgetImplementationAddress(ethers.provider, await proxy.getAddress()) - Upgrades the proxy to
VaultV2
example.ts
Transparent proxy
To verify the UUPS proxy and the underlying information, callhardhat-tenderly while passing two contracts: Vault for the implementation, and TransparentUpgradeableProxy for the proxy itself.
example.ts
- To verify the implementation, provide the following:
nameof your proxied contract (in our caseVault)- Address where the contract was deployed, using the
getImplementationAddressmethod from@openzeppelin/upgrades-core.
- To verify the proxy, provide the following:
TransparentUpgradeableProxyas the proxy contractname- Address of the proxy
await proxy.getAddress()
Complete Code Sample
example.ts
Beacon proxy
To verify the Beacon proxy and the underlying information, you have to verify two contracts: theVault (implementation) and OpenZepplin’s UpgradableBeacon:
example.ts
Complete Code Sample
example.ts