hardhat-zksync-deploy

Guide on using the hardhat-zksync-deploy plugin.

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)
Deployer extension inside Hardhat Runtime Environment (HRE): To use new features like the deployer extension inside Hardhat Runtime Environment (HRE), caching mechanism, and support for script paths, tags, dependencies, and priority, the plugin versions should be as follows:
  • 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

NameTypeDescriptionDefaultNotes
networks.*.urlstringRPC URL for the network-Required for all networks
networks.*.ethNetworkstringEthereum network URL or identifier-Required for ZKsync networks
networks.*.zksyncbooleanWhether this is a ZKsync networkfalseSet to true for ZKsync networks
networks.*.accountsstring | {mnemonic: string}Private keys or mnemonic for deployment-Optional
networks.*.forceDeploybooleanForce deployment bypassing cachetrueOptional
deployerAccounts.*numberDefault account index per network0Optional
paths.deployPathsstring | stringDeployment 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 Era
    • loadArtifact - Loads contract artifacts
    • estimateDeployFee - Estimates deployment fees
    • deploy - Deploys contracts
    • setDeploymentType - Sets deployment type
    • setWallet - Sets deployment wallet
    • getWallet - Gets wallet instance

Detailed HRE Extensions Reference

MethodDescriptionParameters & Return TypeUsage Example
loadArtifactLoads contract artifacts for deployment(contractNameOrFullyQualifiedName: string) => Promise<ZkSyncArtifact>const artifact = await hre.deployer.loadArtifact("MyContract")
estimateDeployFeeEstimates the deployment fee for a contract(artifact: ZkSyncArtifact, constructorArguments: any[]) => Promise<bigint>const fee = await hre.deployer.estimateDeployFee(artifact, [])
deployDeploys 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, [])
setDeploymentTypeSets the deployment type for subsequent deployments(deploymentType: zk.types.DeploymentType) => voidhre.deployer.setDeploymentType("create2")
setWalletSets the wallet to be used for deployments(wallet: zk.Wallet) => voidhre.deployer.setWallet(newWallet)
getWalletRetrieves the current deployment wallet(privateKeyOrAccountNumber?: string | number) => zk.Walletconst 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

PropertyTypeDescriptionDefault
prioritynumberExecution order priority0
tagsstringScript categorization labels"default"
dependenciesstringRequired script tags

Execution Order

The plugin determines script execution order based on:

  1. Dependencies (highest priority)
  2. 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:

  1. 003_deploy.ts (highest priority, no dependencies)
  2. 002_deploy.ts (required by 001_deploy.ts)
  3. 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

  1. Initial Deployment:
    • Contract is deployed to the network
    • Deployment information is stored in the cache
    • Contract address and metadata are saved
  2. 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
  3. 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 to true

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
    }
  }
};
The default value for 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
Starting from version 1.13.14, the zksolc compiler has been enhanced to identify missing libraries.

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
The 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
In TypeScript projects, the --exported-config-object argument is optional. The plugin will attempt to resolve the configuration using the HardhatUserConfig type.
The default values for script properties are:
  • tags: "default"
  • priority: 0
  • dependencies:
When using dependencies, ensure there are no circular dependencies between scripts, as this will prevent deployment from completing.

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 URL
  • ethNetwork: Ethereum network URL/identifier
  • zksync: Set to true for ZKsync networks

For more help, please refer to the GitHub issues.


Made with ❤️ by the ZKsync Community