How to verify contracts with Hardhat
This how-to guide explains how to verify a smart contract on ZKsync using Hardhat.
What you'll learn:
- How to compile smart contracts.
- How to verify contracts.
- How to verify contracts with constructor arguments.
Tools:
- - hardhat-zksync-deploy
- - hardhat-zksync-verify
Contract source-code verification ensures that the code running on-chain matches your published code.
The verification process validates and authenticates contracts running on a blockchain network, and enhances transparency, security, and trust in your smart contracts.
This document shows you how to verify your contracts with the hardhat-zksync
plugin.
Common use cases
- Transparent contract deployment: using the plugin, developers can deploy their contracts on the ZKsync Era network with transparency so that users and other developers can independently verify the contract's source code, and ensure it behaves as expected.
- Cross-chain interoperability: for contracts operating across multiple chains, verifying contracts on each network assures users of the contract's consistency across different networks.
- Open source projects: for open-source projects, verifying contracts enhances trust and encourages more developers to contribute. It assures contributors that the deployed contracts match the source code.
Verifying contracts using hardhat-zksync
Project setup
- Scaffold a new project by running the command:
npx zksync-cli create verify-greeter-contract --template hardhat_solidity
This creates a new ZKsync Era project calledverify-greeter-contract
with a basicGreeter
contract and all the ZKsync plugins and configurations. - Proceed by moving into the project directory:
cd verify-greeter-contract
Configuration of hardhat.config.ts
The provided hardhat.config.ts
configuration already contains the verifyURL
for the zkSyncSepoliaTestnet
and zkSyncMainnet
networks:
zkSyncSepoliaTestnet: {
url: "https://sepolia.era.zksync.dev",
ethNetwork: "sepolia",
zksync: true,
verifyURL: "https://explorer.sepolia.era.zksync.dev/contract_verification",
},
zkSyncMainnet: {
url: "https://mainnet.era.zksync.io",
ethNetwork: "mainnet",
zksync: true,
verifyURL: "https://zksync2-mainnet-explorer.zksync.io/contract_verification",
},
- Feel free to assign an arbitrary ZKsync Era network name utilizing the
defaultNetwork
property. - The
verifyURL
attribute directs to the verification endpoint specific to the ZKsync network. - If you want to verify a smart contract in other supported block explorer you can set
verifyURL
to point to it's verification API URL. For example for L2scan on mainnet setverifyURL
tohttps://zksync-era.l2scan.co/api/zksync_contract_verification
. - If you intend to validate a smart contract on Ethereum within the same project, don't forget to include your Etherscan API key.
Greeter.sol
contract compilation
The ZKsync CLI provides a Greeter.sol
contract we will verify on ZKsync Era.
Compile the contract using this command:
npm hardhat compile
Deploy the Greeter.sol
contract
The ZKsync CLI provides a deploy/deploy-greeter.ts
script that we will use to deploy the Greeter contract.
To configure your private key, copy the .env.example
file, rename the copy to .env
, and add your wallet private key.
WALLET_PRIVATE_KEY=YourPrivateKeyHere....
Your private key will be used for paying the costs of deploying the smart contract.
Initiate contract deployment using this command:
npm hardhat deploy-zksync --script deploy-greeter.ts
Expect an output similar to this:
Running the deployment function for the Greeter contract
The deployment is estimated to cost 0.0265726735 ETH
constructor args:0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000094869207468657265210000000000000000000000000000000000000000000000
The Greeter contract got deployed at 0xE84774C41F096Ba5BafA1439cEE787D9dD1A6b72
Remember, you need the contract address to verify the contract on ZKsync Era.
Verify the contract
Run the following command to verify your contract on the specified network, replacing <contract address>
with your deployed contract's address.
npm hardhat verify --network <network> <contract address>
For example, to verify our Greeter contract on the zkSyncTestnet
network and our contract address is 0xE84774C41F096Ba5BafA1439cEE787D9dD1A6b72
,
use:
npm hardhat verify --network zkSyncTestnet 0xE84774C41F096Ba5BafA1439cEE787D9dD1A6b72 'Hi there!'
The verification task attempts to compare the compiled bytecode of all the contracts in your local environment with the deployed bytecode of the deployed contract you are seeking to verify. If there is no match, it reports an error.
Verify the contract with fully qualified name
Specify which contract from your local setup you want to verify using the --contract
parameter and providing its fully qualified name.
npm hardhat verify --network <network> <contract address> --contract <fully qualified name>
A fully qualified name is, for example, path/sourceName:contractName
.
For instance, if the source name is Greeter.sol
, the contract name is Greeter
,
and the contract lives in the contracts/
directory, the fully qualified contract name is: contracts/Greeter.sol:Greeter
.
Here's an example of command using the --contract
flag:
npm hardhat verify --network zkSyncTestnet 0xE84774C41F096Ba5BafA1439cEE787D9dD1A6b72 'Hi there!' --contract contracts/Greeter.sol:Greeter
Verify the contract with constructor arguments
If your contract was deployed with specific constructor arguments, you need to specify them when running the verify task.
npm hardhat verify --network testnet <contract address> '<constructor argument>'
For example, if you're verifying the Greeter contract on the zkSyncTestnet network,
your contract address is 0xE84774C41F096Ba5BafA1439cEE787D9dD1A6b72
, and your constructor argument was 'Hi there!', use:
npm hardhat verify --network zkSyncTestnet 0xE84774C41F096Ba5BafA1439cEE787D9dD1A6b72 'Hi there!'
Handle complex lists of constructor arguments
If your contract's constructor includes a complex list of arguments, create a separate JavaScript module to export these arguments. Here's how you can achieve this:
Prepare the JavaScript module with arguments
Start by creating an arguments.js
file. This file should export an array of arguments
that matches the order and types of the arguments in your contract's constructor.
For example, if your contract constructor has two string values, an address, and an integer, your arguments.js
might look like this:
module.exports = [
"string argument 1", // string
"string argument 2", // string
"0x1234abc...", // address
42, // integer
];
Verify the contract with the constructor arguments file
Once you have your arguments.js
file prepared, you can reference it when verifying your contract.
The --constructor-args
parameter allows you to specify a JavaScript module that exports your constructor arguments.
Execute the verify
command, substituting <contract address>
with your actual contract address:
npm hardhat verify --network zkSyncTestnet <contract address> --constructor-args arguments.js
For instance, if your contract address was 0xE84774C41F096Ba5BafA1439cEE787D9dD1A6b72
, you'd use:
npm hardhat verify --network zkSyncTestnet 0xE84774C41F096Ba5BafA1439cEE787D9dD1A6b72 --constructor-args arguments.js
This command verifies the contract on the zkSyncTestnet
network,
using the specific contract address and the constructor arguments defined in the arguments.js
file.
By following this procedure, you can handle contract verification for constructors with complex argument types and arrangements.
Check verification status
Once you've submitted your contract for verification, you may want to check the status of your request. This can be especially useful in cases where verification may take a long time, or if you wish to verify the successful receipt of your request.
Use the verify-status
command to check the status of your verification request.
Replace <your verification id>
with the verification ID you received when you submitted your contract for verification.
npm hardhat verify-status --verification-id <your verification id>
For instance, if your verification ID is 12345
, run the following:
npm hardhat verify-status --verification-id 12345
This command returns the current status of your verification request.
Verify smart contract programmatically
There may be cases where you need to verify your contracts programmatically, for instance, as part of your project's build or deployment scripts.
To achieve this, use Hardhat's task runner to call the verify:verify
task directly from your code. For example:
const contractAddress = "<your contract address>";
const contractFullyQualifiedName = "<your contract fully qualified name>";
const constructorArguments = [
/* your decoded constructor arguments */
];
const verificationId = await hre.run("verify:verify", {
address: contractAddress,
contract: contractFullyQualifiedName,
constructorArguments: constructorArguments,
});
console.log(`Verification ID: ${verificationId}`);
In this script:
contractAddress
is the address of the deployed contract you wish to verify.contractFullyQualifiedName
is the fully qualified name of your contract (including the path to your Solidity file and contract name, separated by a colon e.g.,"contracts/Greeter.sol:Greeter"
).constructorArguments
is an array containing the arguments used to deploy your contract (e.g. "Hi There").
Once this script runs, it prints the verification ID.
If the verification request is successful, you can use this ID to check the status of your verification request.
If the request was not successful, the return value and printed ID is -1
.
UI block explorer alternative
Contract verification in ZKsync Era ensures the integrity and trustworthiness of your contracts.
The Smart Contract Verification in ZKsync Era Block Explorer
is a manual UI process for doing the same, ideal for occasional use,
while the hardhat-zksync-verify
plugin facilitates an automated, flexible approach for developers.