hardhat-zksync-deploy
Description
The hardhat-zksync-deploy
plugin provides utilities for deploying smart contracts on ZKsync Era with
artifacts built by the @matterlabs/hardhat-zksync-solc
or @matterlabs/hardhat-zksync-vyper
plugins. Key features include:
- Deployment utilities for ZKsync Era smart contracts
- Support for both Solidity and Vyper contracts
- Caching mechanism for deployed contracts
- Library dependency management
- Script-based deployment with tags, dependencies, and priority
- Integration with Hardhat Runtime Environment (HRE)
Resources:
Version Compatibility Warning
Ensure you are using the correct version of the plugin with ethers:- For plugin version <1.0.0:
- Compatible with ethers v5.
- For plugin version ≥1.0.0:
- Compatible with ethers v6 (⭐ Recommended)
- For v6, the version should be 1.2.0 or higher.
- For v5, the version should be 0.8.0 or higher.
Installation
Prerequisites
- Node.js version 18 or higher
- Hardhat version 2.16.0 or higher
Setup
Install the plugin using your preferred package manager:
yarn add -D @matterlabs/hardhat-zksync-deploy ethers zksync-ethers
Import the package in your hardhat.config.ts
file:
import "@matterlabs/hardhat-zksync-deploy";
Configuration
Network Configuration
In the hardhat.config.ts
file, specify ZKsync Era and Ethereum networks in the networks
object:
networks: {
sepolia: {
url: "https://sepolia.infura.io/v3/<API_KEY>" // The Ethereum Web3 RPC URL (optional).
},
zkTestnet: {
url: "https://sepolia.era.zksync.dev", // The testnet RPC URL of ZKsync Era network.
ethNetwork: "sepolia", // The Ethereum Web3 RPC URL, or the identifier of the network (e.g. `mainnet` or `sepolia`)
zksync: true
}
},
// defaultNetwork: "zkTestnet", // optional (if not set, use '--network zkTestnet')
Network Configuration Options
Name | Type | Description | Default | Notes |
---|---|---|---|---|
networks.*.url | string | RPC URL for the network | - | Required for all networks |
networks.*.ethNetwork | string | Ethereum network URL or identifier | - | Required for ZKsync networks |
networks.*.zksync | boolean | Whether this is a ZKsync network | false | Set to true for ZKsync networks |
networks.*.accounts | string | {mnemonic: string} | Private keys or mnemonic for deployment | - | Optional |
networks.*.forceDeploy | boolean | Force deployment bypassing cache | true | Optional |
deployerAccounts.* | number | Default account index per network | 0 | Optional |
paths.deployPaths | string | string | Deployment script directories | "deploy" | Optional |
Account Configuration
You can configure accounts in two ways:
Using Private Keys:
const config: HardhatUserConfig = {
networks: {
zkTestnet: {
url: "https://sepolia.era.zksync.dev",
ethNetwork: "sepolia",
zksync: true,
accounts: ['0xac1e735be8536c6534bb4f17f06f6afc73b2b5ba84ac2cfb12f7461b20c0bbe3']
}
}
};
Using Mnemonics:
const config: HardhatUserConfig = {
networks: {
zkTestnet: {
url: "https://sepolia.era.zksync.dev",
ethNetwork: "sepolia",
zksync: true,
accounts: {
mnemonic: 'stuff slice staff easily soup parent arm payment cotton trade scatter struggle'
}
}
}
};
Deployer Account Configuration
Configure specific deployer accounts per network:
const config: HardhatUserConfig = {
deployerAccounts: {
zkTestnet: 1, // Use the second account (index 1) for deployments
mainnet: 0 // Use the first account (index 0) for mainnet
}
};
Account Management
The accounts
configuration represents a list of private keys or a mnemonic object used in the deployment process.
When using anvil-zksync or local-setup, the accounts
object will be automatically populated with rich accounts.
To establish a default index per network (which is 0 by default), you can include a deployerAccounts
section in your hardhat.config.ts
file.
This configuration will be used by hre.deployer
when accessing the deploy method.
Plugin Features
Deployer Class
The main export of this plugin is the Deployer
class. It wraps a zksync-ethers
Wallet
instance and provides a convenient interface to deploy smart contracts.
Constructor
constructor(hre: HardhatRuntimeEnvironment, zkWallet: zk.Wallet, deploymentType?: zk.types.DeploymentType)
Static Method
static fromEthWallet(hre: HardhatRuntimeEnvironment, ethWallet: ethers.Wallet, deploymentType?: zk.types.DeploymentType)
Main Methods
async loadArtifact(contractNameOrFullyQualifiedName: string): Promise<ZkSyncArtifact>
async estimateDeployFee(artifact: ZkSyncArtifact, constructorArguments: any[]): Promise<bigint>
async deploy(
contractNameOrArtifact: ZkSyncArtifact | string,
constructorArguments: any[],
overrides?: Overrides,
additionalFactoryDeps?: ethers.BytesLike[],
): Promise<zk.Contract>
HRE Extensions
The plugin adds the following extensions to the Hardhat Runtime Environment (HRE):
deployer
- Deployment utilities for ZKsync EraloadArtifact
- Loads contract artifactsestimateDeployFee
- Estimates deployment feesdeploy
- Deploys contractssetDeploymentType
- Sets deployment typesetWallet
- Sets deployment walletgetWallet
- Gets wallet instance
Detailed HRE Extensions Reference
Method | Description | Parameters & Return Type | Usage Example |
---|---|---|---|
loadArtifact | Loads contract artifacts for deployment | (contractNameOrFullyQualifiedName: string) => Promise<ZkSyncArtifact> | const artifact = await hre.deployer.loadArtifact("MyContract") |
estimateDeployFee | Estimates the deployment fee for a contract | (artifact: ZkSyncArtifact, constructorArguments: any[]) => Promise<bigint> | const fee = await hre.deployer.estimateDeployFee(artifact, []) |
deploy | Deploys a contract to the network | (contractNameOrArtifact: ZkSyncArtifact | string, constructorArguments: any[], overrides?: Overrides, additionalFactoryDeps?: ethers.BytesLike[]) => Promise<zk.Contract> | const contract = await hre.deployer.deploy(artifact, []) |
setDeploymentType | Sets the deployment type for subsequent deployments | (deploymentType: zk.types.DeploymentType) => void | hre.deployer.setDeploymentType("create2") |
setWallet | Sets the wallet to be used for deployments | (wallet: zk.Wallet) => void | hre.deployer.setWallet(newWallet) |
getWallet | Retrieves the current deployment wallet | (privateKeyOrAccountNumber?: string | number) => zk.Wallet | const wallet = hre.deployer.getWallet() |
Usage
Basic Deployment
// Using Deployer class
const wallet = new zk.Wallet("PRIVATE_KEY");
const deployer = new Deployer(hre, zkWallet);
const artifact = await deployer.loadArtifact("ContractName");
const contract = await deployer.deploy(artifact, []);
// Using hre.deployer
const artifact = await hre.deployer.loadArtifact("ContractName");
const contract = await hre.deployer.deploy(artifact, []);
Deployment Scripts
Script Organization
Deployment scripts are stored in the configured deployPaths
directory. You can specify multiple deployment directories:
const config: HardhatUserConfig = {
paths: {
deployPaths: ["deploy", "deploy-zksync"] // Multiple deployment directories
}
};
Script Structure
Each deployment script follows a standard structure:
// deploy/001_deploy.ts
import { HardhatRuntimeEnvironment } from "hardhat/types";
const deployScript = async function (hre: HardhatRuntimeEnvironment) {
// Deployment logic here
const artifact = await hre.deployer.loadArtifact("ContractName");
await hre.deployer.deploy(artifact, []);
};
export default deployScript;
Script Metadata
Scripts can be enhanced with metadata properties:
// Metadata properties
deployScript.priority = 800; // Execution priority
deployScript.tags = ["first"]; // Script categorization
deployScript.dependencies = ["second"]; // Required dependencies
Script Properties
Property | Type | Description | Default |
---|---|---|---|
priority | number | Execution order priority | 0 |
tags | string | Script categorization labels | "default" |
dependencies | string | Required script tags |
Execution Order
The plugin determines script execution order based on:
- Dependencies (highest priority)
- Priority value (higher values execute first)
Example of execution order:
// Script 001_deploy.ts
deployScript.priority = 800;
deployScript.tags = ["first"];
deployScript.dependencies = ["second"];
// Script 002_deploy.ts
deployScript.priority = 650;
deployScript.tags = ["second"];
// Script 003_deploy.ts
deployScript.priority = 1000;
Execution sequence:
003_deploy.ts
(highest priority, no dependencies)002_deploy.ts
(required by 001_deploy.ts)001_deploy.ts
(depends on 002_deploy.ts)
Caching Mechanism
Cache Structure
For each deployment, a new deployments-zk
folder is created with the following structure:
deployments-zk/
├── <network-name>/
│ ├── <contract-name>.json
│ ├── <contract-name>.json
│ └── .chainId
└── <another-network>/
├── <contract-name>.json
└── .chainId
Each contract JSON file contains:
- Contract address
- Deployment transaction hash
- Constructor arguments
- Contract bytecode
- Salt and deployment type
- Network information
How Caching Works
- Initial Deployment:
- Contract is deployed to the network
- Deployment information is stored in the cache
- Contract address and metadata are saved
- Subsequent Deployments:
- Plugin checks if contract bytecode, constructor arguments, salt and deployment type match
- If match is found, reuses the existing contract address
- If no match, performs a new deployment
- Cache Invalidation:
- Cache is invalidated when contract code changes
- Cache is invalidated when constructor arguments change
- Cache is invalidated when salt and deployment type change
- Cache is invalidated when
forceDeploy
is set totrue
Configuration
Control caching behavior through network configuration:
const config: HardhatUserConfig = {
networks: {
zkTestnet: {
url: "https://sepolia.era.zksync.dev",
ethNetwork: "sepolia",
zksync: true,
forceDeploy: true, // Force deployment bypassing cache
}
}
};
forceDeploy
is true
, meaning caching is disabled by default for all networks unless explicitly enabled.Library Dependencies
Library Detection
Starting from version 1.13.14, the zksolc compiler has been enhanced to identify missing libraries during compilation. The plugin leverages this feature to:
- Detect missing libraries during contract compilation
- Generate a dependency tree of library relationships
- Automatically handle library deployment order
Complex Dependency Trees
The plugin supports complex library dependency trees:
Consider three libraries where:
- Library A is dependent on Library B
- Library B is dependent on Library C
A
└── B
└── C
Deployment workflow:
1. Compile and deploy Library C
2. Compile and deploy Library B, referencing the deployed address of Library C
3. Compile and deploy Library A, referencing the deployed address of Library B
Automatic Configuration Updates
The plugin automatically updates the Hardhat configuration with deployed library addresses:
zksolc: {
compilerSource: 'binary',
settings: {
libraries: {
"contracts/LibraryA.sol": {
"LibraryA": "0x13706Afd344d905BB9Cb50752065a67Fa8d09c70"
},
"contracts/LibraryB.sol": {
"LibraryB": "0x4cf2E778D384746EaB115b914885e2bB18E893E2"
}
}
}
}
Commands
deploy-zksync
Runs all deployment scripts.
# Run all scripts
yarn hardhat deploy-zksync
# Run specific script
yarn hardhat deploy-zksync --script 001_deploy.ts
# Run scripts with specific tags
yarn hardhat deploy-zksync --tags all
# Run on specific network
yarn hardhat deploy-zksync --network zkTestnet
deploy-zksync:contract
Deploys a single contract.
# Basic deployment
yarn hardhat deploy-zksync:contract --contract-name ContractName
# With constructor arguments
yarn hardhat deploy-zksync:contract --contract-name Greeter 'Hello'
# Using external constructor arguments
yarn hardhat deploy-zksync:contract --contract-name ComplexContract --constructor-args args.js
Example of `args.js` file:
```javascript
// args.js
module.exports = [
"a string argument",
"0xabcdef",
"42",
{
property1: "one",
property2: 2,
},
];
# Skip compilation
yarn hardhat deploy-zksync:contract --contract-name Contract --no-compile
# Specify deployment type
yarn hardhat deploy-zksync:contract --contract-name Greeter 'Hello' --deployment-type create2
args.js
file should export an array of constructor arguments that will be passed to your contract's constructor.
This is particularly useful when you have complex constructor arguments that would be cumbersome to pass directly in the command line.deploy-zksync:libraries
Deploys missing libraries.
# Using default account
yarn hardhat deploy-zksync:libraries
# Using specific private key
yarn hardhat deploy-zksync:libraries --private-key-or-index 0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110
# Using account index
yarn hardhat deploy-zksync:libraries --private-key-or-index 2
# With external configuration
yarn hardhat deploy-zksync:libraries --exported-config-object someObject
Additional Options
--no-auto-populate-config
: Disable automatic configuration updates--external-config-object-path <path>
: Specify path to external configuration file--exported-config-object <name>
: Specify name of the configuration object--compile-all-contracts
: Compile all contracts with deployed libraries
--exported-config-object
argument is optional.
The plugin will attempt to resolve the configuration using the HardhatUserConfig
type.tags
: "default"priority
: 0dependencies
:
Troubleshooting
Version Compatibility Issues
Problem: Plugin version conflicts with ethers library.
Solution: Ensure you're using the correct version combination:
- For plugin version <1.0.0: Use ethers v5
- For plugin version ≥1.0.0: Use ethers v6 (Recommended)
Deployer Extension Features Not Working
Problem: New features like deployer extension, caching mechanism, and script support are not available.
Solution: Update to the correct plugin version:
- For v6: Use version 1.2.0 or higher
- For v5: Use version 0.8.0 or higher
Network Configuration Errors
Problem: Network configuration issues preventing proper deployment.
Solution: Verify your hardhat.config.ts
has all required fields:
url
: RPC URLethNetwork
: Ethereum network URL/identifierzksync
: Set to true for ZKsync networks
For more help, please refer to the GitHub issues.