hardhat-zksync-ethers
Description
The hardhat-zksync-ethers
plugin integrates zksync-ethers into the Hardhat Runtime Environment, providing:
- L1 and L2 providers for interacting with both networks
- Contract deployment helpers with factory dependency support
- Wallet management capabilities including rich accounts support
- Contract interaction utilities
Resources:
- For plugin version <1.0.0:
- Compatible with ethers v5
- For plugin version ≥1.0.0:
- Compatible with ethers v6 (⭐ Recommended)
Installation
Prerequisites
- Node.js version 18 or higher
- Hardhat version 2.16.0 or higher
Setup
Install the plugin and its dependencies:
yarn add -D @matterlabs/hardhat-zksync-ethers zksync-ethers ethers
Import the package in your hardhat.config.ts
file:
import "@matterlabs/hardhat-zksync-ethers";
Configuration
The plugin doesn't require any additional configuration options.
import "@matterlabs/hardhat-zksync-ethers";
import { HardhatUserConfig } from "hardhat/config";
const config: HardhatUserConfig = {
// Your existing hardhat config
};
export default config;
Additions
Added Tasks
This plugin does not add any additional Hardhat tasks.
Added HRE Extensions
The plugin adds a zksyncEthers
and ethers
object to the Hardhat Runtime Environment with the following helpers:
Helper | Description |
---|---|
providerL2() | Returns a zk.Provider for L2, automatically connected to the selected network |
providerL1() | Returns an ethers.Provider for L1, automatically connected to the selected network |
getWallet(privateKeyOrIndex?: string | number) | Returns zk.Wallet for the given private key or index |
getWallets() | Returns all wallets of type zk.Wallet |
getContractFactory(name: string, wallet?: zk.Wallet, deploymentType?: DeploymentType) | Returns a zk.ContractFactory for provided artifact name |
getContractFactoryFromArtifact(artifact: ZkSyncArtifact, wallet?: zk.Wallet, deploymentType?: DeploymentType) | Returns a zk.ContractFactory for provided artifact |
getContractAt(nameOrAbi: string | any[], address: string | Address, wallet?: zk.Wallet) | Returns zk.Contract for provided artifact name or abi and address |
getContractAtFromArtifact(artifact: ZkSyncArtifact, address: string, wallet?: zk.Wallet) | Returns zk.Contract for provided artifact and address |
getImpersonatedSigner(address: string) | Impersonates zk.Signer from address |
loadArtifact(name: string) | Loads ZkSyncArtifact from contract name |
extractFactoryDeps(artifact: ZkSyncArtifact) | Extracts factory deps from artifact |
deployContract(artifact: ZkSyncArtifact, constructorArguments: any[], wallet?: zk.Wallet, overrides?: ethers.Overrides, additionalFactoryDeps?: ethers.BytesLike[]) | Deploys contract with factory dependencies support |
- When wallet?: zk.Wallet is omitted and the network is set to local, the default wallet is assigned to the first rich account.
- If the Hardhat configuration specifies an accounts object for the selected network, the default wallet is chosen from that instead.
- The default value for deploymentType?: DeploymentType is create unless explicitly set otherwise.
hre.ethers Object
The plugin also populates the hre.ethers
object, which dynamically switches between zksync-ethers and regular ethers based on the selected network:
- When a ZKSync network is selected,
hre.ethers
will use zksync-ethers and its helpers - When a regular Ethereum network is selected,
hre.ethers
will use standard ethers and hardhat-ethers helpers
This allows you to write network-agnostic code that automatically uses the appropriate implementation:
// This will use zksync-ethers on ZKSync networks and regular ethers on other networks
const provider = hre.ethers.provider;
const wallet = hre.ethers.getWallet();
const contract = await hre.ethers.getContractFactory("MyContract");
hardhat-zksync-ethers
and not hardhat-ethers
to avoid conflicts in the hre.ethers
object population.Interfaces
interface FactoryDeps {
[contractHash: string]: string;
}
interface ZkSyncArtifact extends Artifact {
factoryDeps: FactoryDeps;
sourceMapping: string;
}
interface FactoryOptions {
wallet?: zk.Wallet;
}
Usage
The plugin can be used in tasks, scripts, and tests. Here are some examples:
Tasks
task("getFeeData", "Returns a fee data.").setAction(async (hre) => {
const feeDataL2 = await hre.zksyncEthers.providerL2.getFeeData();
const feeDataL1 = await this.env.zksyncEthers.providerL1.getFeeData();
return { feeDataL2, feeDataL1 };
});
Scripts
export default async function (hre: HardhatRuntimeEnvironment) {
console.info(chalk.yellow(`Running deploy`));
// automatically connected to the selected network
const gasPrice = await hre.zksyncEthers.providerL2.send("eth_gasPrice", []);
// getContractFactory with default wallet, deploy contract and set new greeting message
const greeterFactory = await hre.zksyncEthers.getContractFactory("Greeter");
const greeter = await greeterFactory.deploy("Hello, world!");
console.info(chalk.green(`Greeter deployed to: ${await greeter.getAddress()}`));
console.info(chalk.green(`Greeter greeting set to: ${await greeter.greet()}`));
const tx = await greeter.setGreeting("Hello, world again!");
await tx.wait();
console.info(chalk.green(`Greeter greeting set to: ${await greeter.greet()}`));
// deploy with provided wallet using loadArtifact and deployContract
const wallet = await hre.zksyncEthers.getWallet("0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110");
// deposit ETH from L1 to L2 to cover costs of deployment
const depositHandle = await wallet.deposit({
to: wallet.address,
token: utils.ETH_ADDRESS,
amount: ethers.parseEther("0.001"),
});
await depositHandle.wait();
const artifact = await hre.zksyncEthers.loadArtifact("Greeter");
const greets = await hre.zksyncEthers.deployContract(artifact, ["Hello, world!"], wallet);
console.info(chalk.green(`Greeter deployed to: ${await greets.getAddress()}`));
console.info(chalk.green(`Greeter greeting set to: ${await greets.greet()}`));
}
Tests
describe('Deploy Greeter', async function () {
it('deploy greeter and validate greet method ', async function () {
const greets = await hre.ethers.deployContract('Greeter', ["Hello, world!"]);
expect(await greets.greet()).to.equal("Hello, world!");
});
});
Troubleshooting
Connection Issues
Problem: Provider Connection Issues Solution:
- Ensure your network configuration in
hardhat.config.ts
is correct - Check if you have the correct RPC URLs set up
- Verify network connectivity
Contract Deployment Failures
Problem: Deployment of contract fails Solution:
- Make sure you have sufficient funds in your wallet
- Verify contract bytecode and ABI are correct
- Check if all required factory dependencies are included
TypeScript Configuration
Problem: TypeScript compilation errors or type issues Solution:
- Ensure you're using the correct version of ethers (v6 for plugin version ≥1.0.0)
- Check if all type definitions are properly imported
- Update
tsconfig.json
with required compiler options
Plugin Conflicts
Problem: Multiple ethers implementations or conflicting providers Solution:
- Make sure that only
hardhat-zksync-ethers
are imported in the hardhat config - Use the appropriate provider based on network:
// For L2 operations const providerL2 = hre.zksyncEthers.providerL2(); // For L1 operations const providerL1 = hre.zksyncEthers.providerL1();
For more help, please refer to the GitHub issues.