diff --git a/content/00.build/40.tooling/20.hardhat/20.guides/20.migrating-to-zksync.md b/content/00.build/40.tooling/20.hardhat/20.guides/20.migrating-to-zksync.md index 3abf8f1d..0865c674 100644 --- a/content/00.build/40.tooling/20.hardhat/20.guides/20.migrating-to-zksync.md +++ b/content/00.build/40.tooling/20.hardhat/20.guides/20.migrating-to-zksync.md @@ -1,276 +1,558 @@ --- -title: Migrating to ZKsync -description: Learn how to migrate an existing Hardhat project deploy scripts to ZKsync Era. -tags: ["migration", "hardhat", "zksync", "plugins", "ethereum"] +title: Migrating Hardhat project to ZKsync Era +description: Learn how to migrate an existing Hardhat project to ZKsync Era. --- -::callout{icon="i-heroicons-exclamation-triangle" color="amber"} -If you are using Windows, we strongly recommend you use Windows Subsystem for Linux (also known as WSL 2). -You can use `Hardhat` and `Hardhat ZKsync plugins` without it, but it will work better if you use it. +## Project setup + +The `@matterlabs/hardhat-zksync` plugin includes all the necessary tools to compile, test, deploy, and verify contracts on ZKsync. -To install Node.js using WSL 2, please read this [guide](https://learn.microsoft.com/en-us/windows/dev-environment/javascript/nodejs-on-wsl). +::callout{icon="i-heroicons-light-bulb"} +Under the hood, this plugin bundles together several plugins that focus on specific features like compilation, deployment, and verification. +You can learn more about each plugin in the [Getting started section](/build/tooling/hardhat/guides/getting-started). :: -This guide shows you how to migrate an existing Hardhat project to ZKsync Era. +1. Install the `@matterlabs/hardhat-zksync` plugin with: + + ::code-group + + ```bash [npm] + npm i -D @matterlabs/hardhat-zksync + ``` + + ```bash [yarn] + yarn add -D @matterlabs/hardhat-zksync + ``` + + ```bash [pnpm] + pnpm i -D @matterlabs/hardhat-zksync + ``` + + ```bash [bun] + bun add -D @matterlabs/hardhat-zksync + ``` + + :: + +1. Import the plugin at the top of the `hardhat.config.ts` file: + + ```ts + import "@matterlabs/hardhat-zksync"; + ``` + +1. Add the preferred ZKsync networks to the `hardhat.config.ts` file: + + ```js + 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", + }, + dockerizedNode: { + url: "http://localhost:3050", + ethNetwork: "http://localhost:8545", + zksync: true, + }, + inMemoryNode: { + url: "http://127.0.0.1:8011", + ethNetwork: "localhost", + zksync: true, + }, + // Other networks -## Overview + } + ``` -ZKsync Era offers [multiple Hardhat plugins](/build/tooling/hardhat) with different features. -This guide details the one you need to migrate your project to ZKsync Era. +::callout{icon="i-heroicons-light-bulb"} +You can also add the `zksync:true` flag to the `hardhat` network. +:: -::callout{icon="i-heroicons-exclamation-triangle" color="amber"} -#### Non-default paths are not supported yet +## Compilation -- Contract files must be included in the `contracts` folder and deployment scripts must be included in the `deploy` folder. -- Support for custom paths will be included in the future. -:: +ZKsync Era (as well as other chains built with ZK Stack) is operated by the EraVM, +which executes a specific bytecode that differs from the EVM. +This bytecode is generated by the `zksolc` (for Solidity contracts) +and `zkvyper` (for Vyper contracts) compilers. -## Install dependencies +To compile your contracts with these compilers, follow these steps: -Although ZKsync Era is compatible with Solidity and Vyper, -the deployed bytecode and the deployment process is different from Ethereum or other EVM blockchains. -So the first step is to install the compiler and deployer Hardhat plugins: +1. Run the compilation task targeting one of the ZKsync networks, which contain `zksync: true`: -If you're using Vyper, replace `@matterlabs/hardhat-zksync-solc` with `@matterlabs/hardhat-zksync-vyper` + ::code-group -::code-group + ```bash [npm] + npx hardhat compile --network zkSyncSepoliaTestnet + ``` + + ```bash [yarn] + yarn hardhat compile --network zkSyncSepoliaTestnet + ``` + + ```bash [pnpm] + pnpx hardhat compile --network zkSyncSepoliaTestnet + ``` + + ```bash [bun] + bunx hardhat compile --network zkSyncSepoliaTestnet + ``` + + :: + +1. The following output indicates the contracts are being compiled with the `zksolc` compiler: + + ```bash + Compiling contracts for ZKsync Era with zksolc v1.5.1 and zkvm-solc v0.8.17-1.0.1 + Compiling 42 Solidity files + ``` + +1. The compiler generates the `/artifacts-zk` and `/cache-zk` folders containing the smart contract correspondent artifacts, +which follow the same structure as the ones generated by the `solc` compiler. + +### Compiler settings + +You can modify different compiler settings in the `zksolc` or `zkvyper` property inside the `hardhat.config.ts` file. + +- Check the [available `zksolc` settings here](/build/tooling/hardhat/plugins/hardhat-zksync-solc#configuration). +- Check the [available `zkvyper` settings here](/build/tooling/hardhat/plugins/hardhat-zksync-vyper#configuration). + +### Non-inline Libraries + +Deploying non-inline libraries on ZKsync differs from Ethereum. -```bash [npm] -npm i -D @matterlabs/hardhat-zksync +On Ethereum, non-inlineable libraries must be deployed beforehand, and then referenced in the deployment transaction of the contract that imports them: + +```typescript [Ethereum non-inlineable libraries] +const firstLibrary = await hre.ethers.deployContract("LibraryA"); +await firstLibrary.waitForDeployment(); +const firstLibraryAddress = await firstLibrary.getAddress() + +const secondLibrary = await hre.ethers.deployContract("LibraryB"); +await secondLibrary.waitForDeployment(); +const secondLibraryAddress = await l2.getAddress(); + +const mainContract = await hre.ethers.deployContract("MainContract",{ + libraries:{ + LibraryA:firstLibraryAddress, + LibraryB:secondLibraryAddress + } +}); + +await mainContract.waitForDeployment(); ``` -```bash [yarn] -yarn add -D @matterlabs/hardhat-zksync +On ZKsync, if your project contains non-inlineable libraries, +the compilation command will throw an error. + +To automatically deploy all non-inlineable libraries, follow these steps: + +1. Configure a deployer account in the ZKsync network you want to deploy by adding the `accounts:[]` property. +1. Run the following command to deploy the libraries and auto-generate the libraries configuration in the `hardhat.config.ts` file: + + ::code-group + + ```bash [npm] + npx hardhat deploy-zksync:libraries + ``` + + ```bash [yarn] + yarn hardhat deploy-zksync:libraries + ``` + + ```bash [pnpm] + pnpx hardhat deploy-zksync:libraries + ``` + + ```bash [bun] + bunx hardhat deploy-zksync:libraries + ``` + + :: + +The command will add the libraries and their addresses to the `hardhat.config.ts` file. + +1. Now you can compile the main contract that imports the libraries and deploy the contract without the need to reference the libraries: + +```typescript [ZKsync non-inlineable libraries] +const mainContract = await hre.ethers.deployContract("MainContract") +await mainContract.waitForDeployment(); ``` -```bash [pnpm] -pnpm i -D @matterlabs/hardhat-zksync +### Troubleshoting + +**Use of unsupported opcodes like SELFDESTRUCT or EXTCODECOPY.** The compiler will throw an error if any unsupported opcodes is used in one of the contracts. +See differences with EVM opcodes in [EVM instructions](/build/developer-reference/ethereum-differences/evm-instructions#coinbase). + +## Testing + +ZKSync provides different EraVM node implementations to test smart contracts locally: + +- In-Memory Node: fast L2 node with non-persistent state. +- Dockerized setup: L1 and L2 nodes with persistent state but slower performance. + +::callout{icon="i-heroicons-information-circle" color="blue"} +Unless your project contains L1-L2 features, testing with the In-Memory Node is recommended, which is included in the `@matterlabs/hardhat-zksync` plugin. +:: + +You can read more about each node in the [Testing section of the docs](/build/test-and-debug). + +### Running unit tests on In-Memory Node + +To run tests using In-Memory Node, follow these steps: + +1. Add the `zksync:true` flag to the `hardhat` network in the `hardhat.config.ts` file to override Hardhat's default node with ZKsync In-Memory Node. +1. Run the test task with `npx hardhat test --network hardhat` (or make `hardhat` the default network). + +::callout{icon="i-heroicons-exclamation-triangle" color="amber"} +Methods from `hardhat-network-helpers` like `loadFixture` currently do not support testing with the In-Memory Node. +:: + +You can find more info about testing with the In-Memory-Node in [Hardhat-ZKsync node](/build/tooling/hardhat/plugins/hardhat-zksync-node#running-hardhats-test-task-with-hardhat-zksync-node). + +### Running tests on Dockerized setup + +To run tests on the Dockerized local setup, follow these steps: + +1. Run `npx zksync-cli dev config` and select the “Dockerized node” option. +1. Run `npx zksync-cli dev start` to start the L1 and L2 nodes. +1. Add the Dockerized nodes to the list of networks in the `hardhat.config.ts` file: + + ```bash + networks: { + dockerizedNode: { + url: "http://localhost:3050", + ethNetwork: "http://localhost:8545", + zksync: true, + }, + // Other networks + + } + ``` + +1. Make sure the providers in your test files target the correct url. +1. Run the test task with `npx hardhat test --network dockerizedNode`. + +## Deployment + +Smart contract deployment on ZKsync Era (and chains built with ZK Stack) differ from Ethereum +as they are handled by the `ContractDeployer` system contract +(see [Ethereum differences](/build/developer-reference/ethereum-differences/contract-deployment)). + +There are different approaches for contract deployment: + +### Deployment with `hardhat-ethers` + +The `@matterlabs/hardhat-zksync` includes `@matterlabs/hardhat-zksync-ethers`, a package that extends `@nomiclabs/hardhat-ethers` +with all the necessary helper methods to deploy contracts on both ZKsync and EVM networks. The injected `hre.ethers` +object provides methods like `deployContract`, `getContractFactory` or `getContractAt` so deployment scripts work out of the box. + +::callout{icon="i-heroicons-exclamation-triangle" color="amber"} +To avoid typescript collision errors between `@nomiclabs/hardhat-ethers` and `@matterlabs/hardhat-zksync-ethers` make sure that only the latter +(or the wrapper plugin `@matterlabs/hardhat-zksync`) is imported in the `hardhat.config.ts` file. +:: + +See below examples for `hardhat-ethers` and `hardhat-zksync-ethers`: + +::code-group + +```typescript [hardhat-ethers] +const greeter = await hre.ethers.deployContract('Greeter', ['Hi there!']); +await greeter.waitForDeployment(); ``` -```bash [bun] -bun add @matterlabs/hardhat-zksync --dev +```typescript [hardhat-zksync-ethers] +const greeter = await hre.ethers.deployContract('Greeter', ['Hi there!']); +await greeter.waitForDeployment(); ``` :: -## Configuration changes +When a custom deployment is needed, use `ContractFactory`. -In your `hardhat.config.ts` file import the installed dependencies: +::code-group -```ts -import "@matterlabs/hardhat-zksync"; +```typescript [hardhat-ethers] +const GreeterFactory = await hre.ethers.getContractFactory('Greeter'); +const greeterContract = GreeterFactory.deploy(); // if any, pass constructor arguments in deploy arguments +await greeter.waitForDeployment(); ``` -Networks on ZKsync Era require two different URL endpoints: one for layer 1 (Ethereum or Sepolia), and one for layer 2 (ZKsync Era). -This is how you add the %%zk_testnet_name%% to your list of networks in the `hardhat.config.ts`: - -```typescript -const config: HardhatUserConfig = { - networks: { - hardhat: { - zksync: false, - }, - zkSyncTestnet: { - url: "%%zk_testnet_rpc_url%%", - ethNetwork: "%%zk_testnet_identifier%%", // or a Sepolia RPC endpoint from Infura/Alchemy/Chainstack etc. - zksync: true, - }, - }, - defaultNetwork: "zkSyncTestnet", - // configuration continues .... -}; +```typescript [hardhat-zksync-ethers] +const GreeterFactory = await hre.ethers.getContractFactory('Greeter'); +const greeterContract = GreeterFactory.deploy(); // if any, pass constructor arguments in deploy arguments +await greeter.waitForDeployment(); ``` -::callout{icon="i-heroicons-information-circle" color="blue"} -Remember to add `zksync: false` to any other networks. :: -Finally, add the compiler options inside a `zksolc` or `zkvyper` property. Here is the minimal configuration for a Solidity project: +### Deployment with `hardhat-deploy` -```ts -zksolc: { - version: "latest", - settings: {}, -}, -``` +The newest versions of the `hardhat-deploy` plugin (beginning with `0.11.26`) now support ZKsync +deployments out of the box. This means you don't need to modify your deployment scripts and methods like `getNamedAccounts` can be used on ZKsync. -For more advanced settings, check out the [Solidity](/build/tooling/hardhat/plugins/hardhat-zksync-solc) -or [Vyper](/build/tooling/hardhat/plugins/hardhat-zksync-vyper) plugins. +### Deployment with `hardhat-viem` -### How to configure multiple compilation targets + +To be included. -To configure the `hardhat.config.ts` file to target both ZKsync Era and other networks, do the following: +### Deployment with Hardhat ignition -1. In your `hardhat.config.ts`, configure the ZKsync Era network with `zksync: true`. -2. Configure all other networks with `zksync: false`. -3. Run the compilation or deployment scripts with the network flag: `yarn hardhat compile --network zkSyncTestnet` for ZKsync Era network -or `yarn hardhat compile --network sepolia` for other networks, e.g sepolia. +Hardhat ignition scripts do not support deployments to ZKsync Era yet. +Please use other options like [hardhat-deploy](#deployment-with-hardhat-deploy) or [hardhat-ethers](#deployment-with-hardhat-ethers) -```typescript -networks: { - sepolia: { - url: "https://sepolia.infura.io/v3/", // The Ethereum Web3 RPC URL. - zksync: false, // Set to false to target other networks. - }, - zkSyncTestnet: { - url: "%%zk_testnet_rpc_url%%", // The testnet RPC URL of ZKsync Era network. - ethNetwork: "%%zk_testnet_identifier%%", // The identifier of the network (e.g. `mainnet` or `sepolia`) - zksync: true, // Set to true to target ZKsync Era. - } -}, +### Deploying proxy contracts + +The `@matterlabs/hardhat-zksync` includes `@matterlabs/hardhat-zksync-upgradable`, +which extends `@openzeppelin/hardhat-upgrades` with all the necessary methods to deploy and upgrade proxy contracts on ZKsync and EVM networks. + +The injected `hre.upgrades` +object provides methods like `deployProxy`or `deployBeacon` so deployment scripts work out of the box. + +::callout{icon="i-heroicons-exclamation-triangle" color="amber"} +To avoid typescript collision errors between `@openzeppelin/hardhat-upgrades` and `@matterlabs/hardhat-zksync-upgradable` make sure that only the latter +(or the wrapper plugin `@matterlabs/hardhat-zksync`) is imported in the `hardhat.config.ts` file. +:: + +See examples below: + +#### Transparent proxy (deployment) + +::code-group +```typescript [@openzeppelin/contracts-upgradable] +const constructorArguments = [...]; +const initializerFunctionName = 'initialize' +const boxFactory = await hre.ethers.getContractFactory("Box"); +const box = await hre.upgrades.deployProxy(boxFactory, constructorArguments, { + initializer: initializerFunctionName, +}); +await box.waitForDeployment(); ``` -### Full configuration +```typescript [hardhat-zksync-upgradeable] +const constructorArguments = [...]; +const initializerFunctionName = 'initialize' +const boxFactory = await hre.ethers.getContractFactory("Box"); +const box = await hre.upgrades.deployProxy(boxFactory, constructorArguments, { + initializer: initializerFunctionName +}); +await box.waitForDeployment(); +``` -Here is an example config file: +:: -```ts -import { HardhatUserConfig } from "hardhat/config"; +#### Transparent proxy (upgrade) -import "@matterlabs/hardhat-zksync"; +::code-group -const config: HardhatUserConfig = { - zksolc: { - version: "latest", // Uses latest available in %%zk_git_repo_zksolc-bin%% - settings: {}, - }, - defaultNetwork: "zkSyncTestnet", - networks: { - hardhat: { - zksync: false, - }, - sepolia: { - url: "https://sepolia.com/api/abcdef12345", - zksync: false, - }, - mainnet: { - url: "https://ethereum.mainnet.com/api/abcdef12345", - zksync: false, - }, - zkSyncTestnet: { - url: "%%zk_testnet_rpc_url%%", - ethNetwork: "%%zk_testnet_identifier%%", // or a Sepolia RPC endpoint from Infura/Alchemy/Chainstack etc. - zksync: true, - }, - }, - solidity: { - version: "0.8.13", - }, - // OTHER SETTINGS... -}; +```typescript [@openzeppelin/hardhat-upgrades] +const constructorArguments = [...]; +const upgradableProxyAddress = "UPGRADEABLE_PROXY_ADDRESS"; +const boxV2ContractFactory = await hre.ethers.getContractFactory("BoxV2"); +const boxV2 = await hre.upgrades.upgradeProxy(upgradableProxyAddress,boxV2ContractFactory); +await boxV2.waitForDeployment(); +``` -export default config; +```typescript [hardhat-zksync-upgradeable] +const constructorArguments = [...]; +const upgradableProxyAddress = "UPGRADEABLE_PROXY_ADDRESS"; +const boxV2ContractFactory = await hre.ethers.getContractFactory("BoxV2"); +const boxV2 = await hre.upgrades.upgradeProxy(upgradableProxyAddress,boxV2ContractFactory); +await boxV2.waitForDeployment(); ``` -## Compile contracts +:: -To compile your contracts for ZKsync Era, run: +#### Beacon proxy (deployment) ::code-group -```bash [yarn] -yarn hardhat compile --network zkSyncTestnet +```typescript [@openzeppelin/contracts-upgradable] +const constructorArguments = [...]; +const boxFactory = await hre.ethers.getContractFactory("Box"); +const box = await hre.upgrades.deployBeacon(boxFactory, constructorArguments); +await box.waitForDeployment(); ``` -```bash [npm] -npm hardhat compile --network zkSyncTestnet +```typescript [hardhat-zksync-upgradeable] +const constructorArguments = [...]; +const boxFactory = await hre.ethers.getContractFactory("Box"); +const box = await hre.upgrades.deployBeacon(boxFactory, constructorArguments); +await box.waitForDeployment(); ``` :: -Passing the `--network` flag we make sure Hardhat will use the zksolc compiler (or zkvyper). -This command will compile all contracts in the `/contracts` folder and create the folders `artifacts-zk` and `cache-zk`. +#### Beacon proxy (upgrade) + +::code-group -If your contracts import any non-inlineable libraries, you need to configure them in the `hardhat.config.ts` file. -Find more info and examples about [compiling libraries here](/build/tooling/hardhat/guides/compiling-libraries). +```typescript [@openzeppelin/hardhat-upgrades] +const upgradableProxyAddress = "UPGRADEABLE_PROXY_ADDRESS"; +const boxV2ContractFactory = await hre.ethers.getContractFactory("BoxV2"); +const boxV2 = await hre.upgrades.upgradeBeacon(upgradableProxyAddress, boxV2ContractFactory); +await boxV2.waitForDeployment(); +``` -## Deploy contracts +```typescript [hardhat-zksync-upgradeable] +const upgradableProxyAddress = "UPGRADEABLE_PROXY_ADDRESS"; +const boxV2ContractFactory = await hre.ethers.getContractFactory("BoxV2"); +const boxV2 = await hre.upgrades.upgradeProxy(upgradableProxyAddress, boxV2ContractFactory); +await boxV2.waitForDeployment(); +``` -::callout{icon="i-heroicons-exclamation-triangle" color="amber"} -`hardhat-deploy` version `^0.11.26` supports deployments on ZKsync Era. :: -To deploy your contracts you need to use the `Deployer` class from the `hardhat-zksync` plugin. +#### UUPS proxy (deployment) + +::code-group -Here is a basic deployment script for a `Greeter` contract: +```typescript [@openzeppelin/contracts-upgradable] +const constructorArguments = [...]; +const initializerFunctionName = 'initialize' +const boxFactory = await hre.ethers.getContractFactory("Box"); +const box = await hre.upgrades.deployProxy(boxFactory, constructorArguments, { + initializer: initializerFunctionName, +}); +await box.waitForDeployment(); +``` -```ts -import { utils, Wallet } from "zksync-ethers"; -import * as ethers from "ethers"; -import { HardhatRuntimeEnvironment } from "hardhat/types"; -import { Deployer } from "@matterlabs/hardhat-zksync"; - -// An example of a deploy script that will deploy and call a simple contract. -export default async function (hre: HardhatRuntimeEnvironment) { - console.log(`Running deploy script`); - - // Initialize the wallet. - const wallet = new Wallet(""); - - // Create deployer object and load the artifact of the contract we want to deploy. - const deployer = new Deployer(hre, wallet); - // Load contract - const artifact = await deployer.loadArtifact("Greeter"); - - // Deploy this contract. The returned object will be of a `Contract` type, - // similar to the ones in `ethers`. - const greeting = "Hi there!"; - // `greeting` is an argument for contract constructor. - const greeterContract = await deployer.deploy(artifact, [greeting]); - - // Show the contract info. - console.log(`${artifact.contractName} was deployed to ${await greeterContract.getAddress()}`); -} +```typescript [hardhat-zksync-upgradeable] +const constructorArguments = [...]; +const initializerFunctionName = 'initialize' +const boxFactory = await hre.ethers.getContractFactory("Box"); +const box = await hre.upgrades.deployProxy(boxFactory, constructorArguments, { + initializer: initializerFunctionName +}); +await box.waitForDeployment(); ``` -::callout{icon="i-heroicons-information-circle" color="blue"} -To obtain Sepolia ETH please refer to the [network faucets page](/ecosystem/network-faucets) for more info. -
-To transfer Sepolia ETH to %%zk_testnet_name%% use [bridges](https://zksync.io/explore#bridges). :: -Include your deployment script in the `deploy` folder and execute it running: +#### UUPS proxy (upgrade) ::code-group -```bash [yarn] -yarn hardhat deploy-zksync --script SCRIPT_FILENAME.ts --network zkSyncTestnet +```typescript [@openzeppelin/hardhat-upgrades] +const constructorArguments = [...]; +const upgradableProxyAddress = "UPGRADEABLE_PROXY_ADDRESS"; +const boxV2ContractFactory = await hre.ethers.getContractFactory("BoxV2"); +const boxV2 = await hre.upgrades.upgradeProxy(upgradableProxyAddress,boxV2ContractFactory); +await boxV2.waitForDeployment(); ``` -```bash [npm] -npm hardhat deploy-zksync --script SCRIPT_FILENAME.ts --network zkSyncTestnet +```typescript [hardhat-zksync-upgradeable] +const constructorArguments = [...]; +const upgradableProxyAddress = "UPGRADEABLE_PROXY_ADDRESS"; +const boxV2ContractFactory = await hre.ethers.getContractFactory("BoxV2"); +const boxV2 = await hre.upgrades.upgradeProxy(upgradableProxyAddress,boxV2ContractFactory); +await boxV2.waitForDeployment(); ``` :: -If you don't include the `--script` option, all script files inside the `deploy` folder will be executed in alphabetical order. +### Custom deployment scripts with `hardhat-zksync` -Check out a detailed [approach](/build/tooling/hardhat/plugins/hardhat-zksync-deploy) on how to use `hardhat-zksync-deploy` plugin. +Additionally, you can write custom deployment scripts for ZKsync leveraging the `hre.deployer` object which is injected automatically by `@matterlabs/hardhat-zksync-deploy`. +The `Deployer` class provides helper methods to deploy smart contracts to ZKsync. Learn more on [`hardhat-zksync-deploy` methods](/build/tooling/hardhat/plugins/hardhat-zksync-deploy#methods). -## Frontend integration +### Troubleshoting -You can interact with your contracts using the `zksync-ethers` Javascript library. -This SDK has been built on top of ethers and uses the same classes (`Provider`, `Contract`, `Wallet`) so in a lot of cases, -you just need to import these classes from `zksync-ethers` instead of `ethers`: +- **Contract size too large:** if the size of the generated bytecode is too large and can not be deployed, try compiling +the contract with the `mode: 3` flag in the `hardhat.config.ts` file to +optimize the bytecode size on compilation. Learn more on [`hardhat-zksync-solc` configuration](/build/tooling/hardhat/plugins/hardhat-zksync-solc#configuration). -```ts -//import { utils, Provider, Contract, Wallet } from "ethers"; -import { utils, Provider, Contract, Wallet } from "zksync-ethers"; +## Smart contract verification + + +There are no differences in the verification of smart contracts on ZKsync. + +By installing `@matterlabs/hardhat-zksync`, a verification plugin is provided. + +You will have to add a `verifyURL` on the ZKsync networks in the `hardhat.config.ts` file: + +```typescript + 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", + }, ``` -You also need to use the `contract ABI` from the `artifacts-zk` folder to instantiate contracts. +You can run the verification task programmatically inside a script as follows: -Apart from the same classes and methods provided by ethers, zksync-ethers includes additional methods for zksync-specific features. +```js +const verificationId = await hre.run("verify:verify", { + address: contractAddress, + contract: contractFullyQualifedName, + constructorArguments: [...] +}); +``` -You can read more in the [`zksync-ethers` documentation](https://sdk.zksync.io/sdk/js/ethers). +Alternatively you can execute the verification task from your terminal: -## Verify contracts +```sh +npx hardhat verify --network testnet 0x7cf08341524AAF292255F3ecD435f8EE1a910AbF "Hi there!" +``` -To verify your contracts you have two options: +Find more info in [`hardhat-zksync-verify`](/build/tooling/hardhat/plugins/hardhat-zksync-verify). + +## Scripting + +Most scripts will work out of the box as interacting with smart contracts deployed on ZKsync is exactly the same as on any EVM chain. + +For ZKsync-specific features like native account abstraction or paymaster transactions, there are plugins or extensions for the most popular libraries: + +- Ethers: [zksync-ethers](https://sdk.zksync.io/js/ethers/installation). +- Web3.js: [web3-plugin-zksync](https://sdk.zksync.io/js/web3js). +- Viem: [ZKsync extension](https://viem.sh/zksync). + +For other programming languages, please refer to the SDK documentation](https://sdk.zksync.io/). + +## Multichain projects + +The [following example project](https://github.com/uF4No/hardhat-evm-zksync-example) can be used as a reference to target both EVM and ZKsync chains. + +Here are some recommendations for projects that target multiple chains: + +- Add the desired ZKsync networks with the `zksync:true` flag to the `hardhat.config.ts`. +- Make sure to run the compilation task with the `--network` flag when targeting ZKsync networks to use the custom compiler. +- When targeting ZKsync chains, the `@matterlabs/hardhat-zksync` plugin overrides the following plugins: `@nomiclabs/hardhat-ethers`, `@openzeppelin/hardhat-upgrades`. +- To avoid typescript collision errors between `@nomiclabs/hardhat-ethers` and +`@openzeppelin/hardhat-upgrades` with `@matterlabs/hardhat-zksync` make sure that only the latter is imported in the `hardhat.config.ts` file. +- Your deployment scripts should not require any changes if you're using `hardhat-deploy` or `hardhat-ethers`. +- If you have a separate directory for ZKsync deployment scripts, you can indicate the custom +deployment folder for the ZKsync network using [the `deployPaths` property](/build/tooling/hardhat/plugins/hardhat-zksync-deploy#network-specific-deployment-paths): + +```ts +const config: HardhatUserConfig = { + networks: { + zksyncTestnet: { + url: "https://sepolia.era.zksync.dev", + ethNetwork: "sepolia", + zksync: true, + // ADDITION + deployPaths: "deploy-zksync", //single deployment directory + deployPaths: ["deploy", "deploy-zksync"], //multiple deployment directories + }, + }, +}; +``` - - -- Plugin: verify your contracts programmatically using the [Hardhat verify plugin](/build/tooling/hardhat/plugins/hardhat-zksync-verify) +## Support -If you have any problems migrating your project, [send us a message on Discord](https://join.zksync.dev/). +If you're having issues migrating a Hardhat project to ZKsync, please [reach out to us by creating a GitHub discussion](%%zk_git_repo_zksync-developers%%/discussions/). diff --git a/content/00.build/40.tooling/20.hardhat/20.guides/30.migrating-to-zksync_old.md b/content/00.build/40.tooling/20.hardhat/20.guides/30.migrating-to-zksync_old.md deleted file mode 100644 index cc9accfa..00000000 --- a/content/00.build/40.tooling/20.hardhat/20.guides/30.migrating-to-zksync_old.md +++ /dev/null @@ -1,277 +0,0 @@ ---- -title: OLD - Migrating to ZKsync -description: Learn how to migrate an existing Hardhat project deploy scripts to ZKsync Era. -tags: ["migration", "hardhat", "zksync", "plugins", "ethereum"] -navigation: false ---- - -::callout{icon="i-heroicons-exclamation-triangle" color="amber"} -If you are using Windows, we strongly recommend you use Windows Subsystem for Linux (also known as WSL 2). -You can use `Hardhat` and `Hardhat ZKsync plugins` without it, but it will work better if you use it. - -To install Node.js using WSL 2, please read this [guide](https://learn.microsoft.com/en-us/windows/dev-environment/javascript/nodejs-on-wsl). -:: - -This guide shows you how to migrate an existing Hardhat project to ZKsync Era. - -## Overview - -ZKsync Era offers [multiple Hardhat plugins](/build/tooling/hardhat) with different features. -This guide details the one you need to migrate your project to ZKsync Era. - -::callout{icon="i-heroicons-exclamation-triangle" color="amber"} -#### Non-default paths are not supported yet - -- Contract files must be included in the `contracts` folder and deployment scripts must be included in the `deploy` folder. -- Support for custom paths will be included in the future. -:: - -## Install dependencies - -Although ZKsync Era is compatible with Solidity and Vyper, -the deployed bytecode and the deployment process is different from Ethereum or other EVM blockchains. -So the first step is to install the compiler and deployer Hardhat plugins: - -If you're using Vyper, replace `@matterlabs/hardhat-zksync-solc` with `@matterlabs/hardhat-zksync-vyper` - -::code-group - -```bash [npm] -npm i -D @matterlabs/hardhat-zksync -``` - -```bash [yarn] -yarn add -D @matterlabs/hardhat-zksync -``` - -```bash [pnpm] -pnpm i -D @matterlabs/hardhat-zksync -``` - -```bash [bun] -bun add @matterlabs/hardhat-zksync --dev -``` - -:: - -## Configuration changes - -In your `hardhat.config.ts` file import the installed dependencies: - -```ts -import "@matterlabs/hardhat-zksync"; -``` - -Networks on ZKsync Era require two different URL endpoints: one for layer 1 (Ethereum or Sepolia), and one for layer 2 (ZKsync Era). -This is how you add the %%zk_testnet_name%% to your list of networks in the `hardhat.config.ts`: - -```typescript -const config: HardhatUserConfig = { - networks: { - hardhat: { - zksync: false, - }, - zkSyncTestnet: { - url: "%%zk_testnet_rpc_url%%", - ethNetwork: "%%zk_testnet_identifier%%", // or a Sepolia RPC endpoint from Infura/Alchemy/Chainstack etc. - zksync: true, - }, - }, - defaultNetwork: "zkSyncTestnet", - // configuration continues .... -}; -``` - -::callout{icon="i-heroicons-information-circle" color="blue"} -Remember to add `zksync: false` to any other networks. -:: - -Finally, add the compiler options inside a `zksolc` or `zkvyper` property. Here is the minimal configuration for a Solidity project: - -```ts -zksolc: { - version: "latest", - settings: {}, -}, -``` - -For more advanced settings, check out the [Solidity](/build/tooling/hardhat/plugins/hardhat-zksync-solc) -or [Vyper](/build/tooling/hardhat/plugins/hardhat-zksync-vyper) plugins. - -### How to configure multiple compilation targets - -To configure the `hardhat.config.ts` file to target both ZKsync Era and other networks, do the following: - -1. In your `hardhat.config.ts`, configure the ZKsync Era network with `zksync: true`. -2. Configure all other networks with `zksync: false`. -3. Run the compilation or deployment scripts with the network flag: `yarn hardhat compile --network zkSyncTestnet` for ZKsync Era network -or `yarn hardhat compile --network sepolia` for other networks, e.g sepolia. - -```typescript -networks: { - sepolia: { - url: "https://sepolia.infura.io/v3/", // The Ethereum Web3 RPC URL. - zksync: false, // Set to false to target other networks. - }, - zkSyncTestnet: { - url: "%%zk_testnet_rpc_url%%", // The testnet RPC URL of ZKsync Era network. - ethNetwork: "%%zk_testnet_identifier%%", // The identifier of the network (e.g. `mainnet` or `sepolia`) - zksync: true, // Set to true to target ZKsync Era. - } -}, - -``` - -### Full configuration - -Here is an example config file: - -```ts -import { HardhatUserConfig } from "hardhat/config"; - -import "@matterlabs/hardhat-zksync"; - -const config: HardhatUserConfig = { - zksolc: { - version: "latest", // Uses latest available in %%zk_git_repo_zksolc-bin%% - settings: {}, - }, - defaultNetwork: "zkSyncTestnet", - networks: { - hardhat: { - zksync: false, - }, - sepolia: { - url: "https://sepolia.com/api/abcdef12345", - zksync: false, - }, - mainnet: { - url: "https://ethereum.mainnet.com/api/abcdef12345", - zksync: false, - }, - zkSyncTestnet: { - url: "%%zk_testnet_rpc_url%%", - ethNetwork: "%%zk_testnet_identifier%%", // or a Sepolia RPC endpoint from Infura/Alchemy/Chainstack etc. - zksync: true, - }, - }, - solidity: { - version: "0.8.13", - }, - // OTHER SETTINGS... -}; - -export default config; -``` - -## Compile contracts - -To compile your contracts for ZKsync Era, run: - -::code-group - -```bash [yarn] -yarn hardhat compile --network zkSyncTestnet -``` - -```bash [npm] -npm hardhat compile --network zkSyncTestnet -``` - -:: - -Passing the `--network` flag we make sure Hardhat will use the zksolc compiler (or zkvyper). -This command will compile all contracts in the `/contracts` folder and create the folders `artifacts-zk` and `cache-zk`. - -If your contracts import any non-inlineable libraries, you need to configure them in the `hardhat.config.ts` file. -Find more info and examples about [compiling libraries here](/build/tooling/hardhat/guides/compiling-libraries). - -## Deploy contracts - -::callout{icon="i-heroicons-exclamation-triangle" color="amber"} -`hardhat-deploy` version `^0.11.26` supports deployments on ZKsync Era. -:: - -To deploy your contracts you need to use the `Deployer` class from the `hardhat-zksync` plugin. - -Here is a basic deployment script for a `Greeter` contract: - -```ts -import { utils, Wallet } from "zksync-ethers"; -import * as ethers from "ethers"; -import { HardhatRuntimeEnvironment } from "hardhat/types"; -import { Deployer } from "@matterlabs/hardhat-zksync"; - -// An example of a deploy script that will deploy and call a simple contract. -export default async function (hre: HardhatRuntimeEnvironment) { - console.log(`Running deploy script`); - - // Initialize the wallet. - const wallet = new Wallet(""); - - // Create deployer object and load the artifact of the contract we want to deploy. - const deployer = new Deployer(hre, wallet); - // Load contract - const artifact = await deployer.loadArtifact("Greeter"); - - // Deploy this contract. The returned object will be of a `Contract` type, - // similar to the ones in `ethers`. - const greeting = "Hi there!"; - // `greeting` is an argument for contract constructor. - const greeterContract = await deployer.deploy(artifact, [greeting]); - - // Show the contract info. - console.log(`${artifact.contractName} was deployed to ${await greeterContract.getAddress()}`); -} -``` - -::callout{icon="i-heroicons-information-circle" color="blue"} -To obtain Sepolia ETH please refer to the [network faucets page](/ecosystem/network-faucets) for more info. -
-To transfer Sepolia ETH to %%zk_testnet_name%% use [bridges](https://zksync.io/explore#bridges). -:: - -Include your deployment script in the `deploy` folder and execute it running: - -::code-group - -```bash [yarn] -yarn hardhat deploy-zksync --script SCRIPT_FILENAME.ts --network zkSyncTestnet -``` - -```bash [npm] -npm hardhat deploy-zksync --script SCRIPT_FILENAME.ts --network zkSyncTestnet -``` - -:: - -If you don't include the `--script` option, all script files inside the `deploy` folder will be executed in alphabetical order. - -Check out a detailed [approach](/build/tooling/hardhat/plugins/hardhat-zksync-deploy) on how to use `hardhat-zksync-deploy` plugin. - -## Frontend integration - -You can interact with your contracts using the `zksync-ethers` Javascript library. -This SDK has been built on top of ethers and uses the same classes (`Provider`, `Contract`, `Wallet`) so in a lot of cases, -you just need to import these classes from `zksync-ethers` instead of `ethers`: - -```ts -//import { utils, Provider, Contract, Wallet } from "ethers"; -import { utils, Provider, Contract, Wallet } from "zksync-ethers"; -``` - -You also need to use the `contract ABI` from the `artifacts-zk` folder to instantiate contracts. - -Apart from the same classes and methods provided by ethers, zksync-ethers includes additional methods for zksync-specific features. - -You can read more in the [`zksync-ethers` documentation](https://sdk.zksync.io/sdk/js/ethers). - -## Verify contracts - -To verify your contracts you have two options: - - - -- Plugin: verify your contracts programmatically using the [Hardhat verify plugin](/build/tooling/hardhat/plugins/hardhat-zksync-verify) - -If you have any problems migrating your project, [send us a message on Discord](https://join.zksync.dev/). diff --git a/content/00.build/40.tooling/20.hardhat/20.migrating-to-zksync.md b/content/00.build/40.tooling/20.hardhat/20.migrating-to-zksync.md new file mode 100644 index 00000000..cde9101a --- /dev/null +++ b/content/00.build/40.tooling/20.hardhat/20.migrating-to-zksync.md @@ -0,0 +1,559 @@ +--- +title: Migrate a Hardhat project to ZKsync Era +description: Learn how to migrate an existing Hardhat project to ZKsync Era. +tags: ["migration", "hardhat", "zksync", "plugins", "ethereum"] +--- + +## Project setup + +The `@matterlabs/hardhat-zksync` plugin includes all the necessary tools to compile, test, deploy, and verify contracts on ZKsync. + +::callout{icon="i-heroicons-light-bulb"} +Under the hood, this plugin bundles together several plugins that focus on specific features like compilation, deployment, and verification. +You can learn more about each plugin in the [Getting started section](/build/tooling/hardhat/guides/getting-started). +:: + +1. Install the `@matterlabs/hardhat-zksync` plugin with: + + ::code-group + + ```bash [npm] + npm i -D @matterlabs/hardhat-zksync + ``` + + ```bash [yarn] + yarn add -D @matterlabs/hardhat-zksync + ``` + + ```bash [pnpm] + pnpm i -D @matterlabs/hardhat-zksync + ``` + + ```bash [bun] + bun add -D @matterlabs/hardhat-zksync + ``` + + :: + +1. Import the plugin at the top of the `hardhat.config.ts` file: + + ```ts + import "@matterlabs/hardhat-zksync"; + ``` + +1. Add the preferred ZKsync networks to the `hardhat.config.ts` file: + + ```js + 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", + }, + dockerizedNode: { + url: "http://localhost:3050", + ethNetwork: "http://localhost:8545", + zksync: true, + }, + inMemoryNode: { + url: "http://127.0.0.1:8011", + ethNetwork: "localhost", + zksync: true, + }, + // Other networks + + } + ``` + +::callout{icon="i-heroicons-light-bulb"} +You can also add the `zksync:true` flag to the `hardhat` network. +:: + +## Compilation + +ZKsync Era (as well as other chains built with ZK Stack) is operated by the EraVM, +which executes a specific bytecode that differs from the EVM. +This bytecode is generated by the `zksolc` (for Solidity contracts) +and `zkvyper` (for Vyper contracts) compilers. + +To compile your contracts with these compilers, follow these steps: + +1. Run the compilation task targeting one of the ZKsync networks, which contain `zksync: true`: + + ::code-group + + ```bash [npm] + npx hardhat compile --network zkSyncSepoliaTestnet + ``` + + ```bash [yarn] + yarn hardhat compile --network zkSyncSepoliaTestnet + ``` + + ```bash [pnpm] + pnpx hardhat compile --network zkSyncSepoliaTestnet + ``` + + ```bash [bun] + bunx hardhat compile --network zkSyncSepoliaTestnet + ``` + + :: + +1. The following output indicates the contracts are being compiled with the `zksolc` compiler: + + ```bash + Compiling contracts for ZKsync Era with zksolc v1.5.1 and zkvm-solc v0.8.17-1.0.1 + Compiling 42 Solidity files + ``` + +1. The compiler generates the `/artifacts-zk` and `/cache-zk` folders containing the smart contract correspondent artifacts, +which follow the same structure as the ones generated by the `solc` compiler. + +### Compiler settings + +You can modify different compiler settings in the `zksolc` or `zkvyper` property inside the `hardhat.config.ts` file. + +- Check the [available `zksolc` settings here](/build/tooling/hardhat/plugins/hardhat-zksync-solc#configuration). +- Check the [available `zkvyper` settings here](/build/tooling/hardhat/plugins/hardhat-zksync-vyper#configuration). + +### Non-inline Libraries + +Deploying non-inline libraries on ZKsync differs from Ethereum. + +On Ethereum, non-inlineable libraries must be deployed beforehand, and then referenced in the deployment transaction of the contract that imports them: + +```typescript [Ethereum non-inlineable libraries] +const firstLibrary = await hre.ethers.deployContract("LibraryA"); +await firstLibrary.waitForDeployment(); +const firstLibraryAddress = await firstLibrary.getAddress() + +const secondLibrary = await hre.ethers.deployContract("LibraryB"); +await secondLibrary.waitForDeployment(); +const secondLibraryAddress = await l2.getAddress(); + +const mainContract = await hre.ethers.deployContract("MainContract",{ + libraries:{ + LibraryA:firstLibraryAddress, + LibraryB:secondLibraryAddress + } +}); + +await mainContract.waitForDeployment(); +``` + +On ZKsync, if your project contains non-inlineable libraries, +the compilation command will throw an error. + +To automatically deploy all non-inlineable libraries, follow these steps: + +1. Configure a deployer account in the ZKsync network you want to deploy by adding the `accounts:[]` property. +1. Run the following command to deploy the libraries and auto-generate the libraries configuration in the `hardhat.config.ts` file: + + ::code-group + + ```bash [npm] + npx hardhat deploy-zksync:libraries + ``` + + ```bash [yarn] + yarn hardhat deploy-zksync:libraries + ``` + + ```bash [pnpm] + pnpx hardhat deploy-zksync:libraries + ``` + + ```bash [bun] + bunx hardhat deploy-zksync:libraries + ``` + + :: + +The command will add the libraries and their addresses to the `hardhat.config.ts` file. + +1. Now you can compile the main contract that imports the libraries and deploy the contract without the need to reference the libraries: + +```typescript [ZKsync non-inlineable libraries] +const mainContract = await hre.ethers.deployContract("MainContract") +await mainContract.waitForDeployment(); +``` + +### Troubleshoting + +**Use of unsupported opcodes like SELFDESTRUCT or EXTCODECOPY.** The compiler will throw an error if any unsupported opcodes is used in one of the contracts. +See differences with EVM opcodes in [EVM instructions](/build/developer-reference/ethereum-differences/evm-instructions#coinbase). + +## Testing + +ZKSync provides different EraVM node implementations to test smart contracts locally: + +- In-Memory Node: fast L2 node with non-persistent state. +- Dockerized setup: L1 and L2 nodes with persistent state but slower performance. + +::callout{icon="i-heroicons-information-circle" color="blue"} +Unless your project contains L1-L2 features, testing with the In-Memory Node is recommended, which is included in the `@matterlabs/hardhat-zksync` plugin. +:: + +You can read more about each node in the [Testing section of the docs](/build/test-and-debug). + +### Running unit tests on In-Memory Node + +To run tests using In-Memory Node, follow these steps: + +1. Add the `zksync:true` flag to the `hardhat` network in the `hardhat.config.ts` file to override Hardhat's default node with ZKsync In-Memory Node. +1. Run the test task with `npx hardhat test --network hardhat` (or make `hardhat` the default network). + +::callout{icon="i-heroicons-exclamation-triangle" color="amber"} +Methods from `hardhat-network-helpers` like `loadFixture` currently do not support testing with the In-Memory Node. +:: + +You can find more info about testing with the In-Memory-Node in [Hardhat-ZKsync node](/build/tooling/hardhat/plugins/hardhat-zksync-node#running-hardhats-test-task-with-hardhat-zksync-node). + +### Running tests on Dockerized setup + +To run tests on the Dockerized local setup, follow these steps: + +1. Run `npx zksync-cli dev config` and select the “Dockerized node” option. +1. Run `npx zksync-cli dev start` to start the L1 and L2 nodes. +1. Add the Dockerized nodes to the list of networks in the `hardhat.config.ts` file: + + ```bash + networks: { + dockerizedNode: { + url: "http://localhost:3050", + ethNetwork: "http://localhost:8545", + zksync: true, + }, + // Other networks + + } + ``` + +1. Make sure the providers in your test files target the correct url. +1. Run the test task with `npx hardhat test --network dockerizedNode`. + +## Deployment + +Smart contract deployment on ZKsync Era (and chains built with ZK Stack) differ from Ethereum +as they are handled by the `ContractDeployer` system contract +(see [Ethereum differences](https://docs.zksync.io/build/developer-reference/ethereum-differences/contract-deployment)). + +There are different approaches for contract deployment: + +### Deployment with `hardhat-ethers` + +The `@matterlabs/hardhat-zksync` includes `@matterlabs/hardhat-zksync-ethers`, a package that extends `@nomiclabs/hardhat-ethers` +with all the necessary helper methods to deploy contracts on both ZKsync and EVM networks. The injected `hre.ethers` +object provides methods like `deployContract`, `getContractFactory` or `getContractAt` so deployment scripts work out of the box. + +::callout{icon="i-heroicons-exclamation-triangle" color="amber"} +To avoid typescript collision errors between `@nomiclabs/hardhat-ethers` and `@matterlabs/hardhat-zksync-ethers` make sure that only the latter +(or the wrapper plugin `@matterlabs/hardhat-zksync`) is imported in the `hardhat.config.ts` file. +:: + +See below examples for `hardhat-ethers` and `hardhat-zksync-ethers`: + +::code-group + +```typescript [hardhat-ethers] +const greeter = await hre.ethers.deployContract('Greeter', ['Hi there!']); +await greeter.waitForDeployment(); +``` + +```typescript [hardhat-zksync-ethers] +const greeter = await hre.ethers.deployContract('Greeter', ['Hi there!']); +await greeter.waitForDeployment(); +``` + +:: + +When a custom deployment is needed, use `ContractFactory`. + +::code-group + +```typescript [hardhat-ethers] +const GreeterFactory = await hre.ethers.getContractFactory('Greeter'); +const greeterContract = GreeterFactory.deploy(); // if any, pass constructor arguments in deploy arguments +await greeter.waitForDeployment(); +``` + +```typescript [hardhat-zksync-ethers] +const GreeterFactory = await hre.ethers.getContractFactory('Greeter'); +const greeterContract = GreeterFactory.deploy(); // if any, pass constructor arguments in deploy arguments +await greeter.waitForDeployment(); +``` + +:: + +### Deployment with `hardhat-deploy` + +The newest versions of the `hardhat-deploy` plugin (beginning with `0.11.26`) now support ZKsync +deployments out of the box. This means you don't need to modify your deployment scripts and methods like `getNamedAccounts` can be used on ZKsync. + +### Deployment with `hardhat-viem` + + +To be included. + +### Deployment with Hardhat ignition + +Hardhat ignition scripts do not support deployments to ZKsync Era yet. +Please use other options like [hardhat-deploy](#deployment-with-hardhat-deploy) or [hardhat-ethers](#deployment-with-hardhat-ethers) + +### Deploying proxy contracts + +The `@matterlabs/hardhat-zksync` includes `@matterlabs/hardhat-zksync-upgradable`, +which extends `@openzeppelin/hardhat-upgrades` with all the necessary methods to deploy and upgrade proxy contracts on ZKsync and EVM networks. + +The injected `hre.upgrades` +object provides methods like `deployProxy`or `deployBeacon` so deployment scripts work out of the box. + +::callout{icon="i-heroicons-exclamation-triangle" color="amber"} +To avoid typescript collision errors between `@openzeppelin/hardhat-upgrades` and `@matterlabs/hardhat-zksync-upgradable` make sure that only the latter +(or the wrapper plugin `@matterlabs/hardhat-zksync`) is imported in the `hardhat.config.ts` file. +:: + +See examples below: + +#### Transparent proxy (deployment) + +::code-group + +```typescript [@openzeppelin/contracts-upgradable] +const constructorArguments = [...]; +const initializerFunctionName = 'initialize' +const boxFactory = await hre.ethers.getContractFactory("Box"); +const box = await hre.upgrades.deployProxy(boxFactory, constructorArguments, { + initializer: initializerFunctionName, +}); +await box.waitForDeployment(); +``` + +```typescript [hardhat-zksync-upgradeable] +const constructorArguments = [...]; +const initializerFunctionName = 'initialize' +const boxFactory = await hre.ethers.getContractFactory("Box"); +const box = await hre.upgrades.deployProxy(boxFactory, constructorArguments, { + initializer: initializerFunctionName +}); +await box.waitForDeployment(); +``` + +:: + +#### Transparent proxy (upgrade) + +::code-group + +```typescript [@openzeppelin/hardhat-upgrades] +const constructorArguments = [...]; +const upgradableProxyAddress = "UPGRADEABLE_PROXY_ADDRESS"; +const boxV2ContractFactory = await hre.ethers.getContractFactory("BoxV2"); +const boxV2 = await hre.upgrades.upgradeProxy(upgradableProxyAddress,boxV2ContractFactory); +await boxV2.waitForDeployment(); +``` + +```typescript [hardhat-zksync-upgradeable] +const constructorArguments = [...]; +const upgradableProxyAddress = "UPGRADEABLE_PROXY_ADDRESS"; +const boxV2ContractFactory = await hre.ethers.getContractFactory("BoxV2"); +const boxV2 = await hre.upgrades.upgradeProxy(upgradableProxyAddress,boxV2ContractFactory); +await boxV2.waitForDeployment(); +``` + +:: + +#### Beacon proxy (deployment) + +::code-group + +```typescript [@openzeppelin/contracts-upgradable] +const constructorArguments = [...]; +const boxFactory = await hre.ethers.getContractFactory("Box"); +const box = await hre.upgrades.deployBeacon(boxFactory, constructorArguments); +await box.waitForDeployment(); +``` + +```typescript [hardhat-zksync-upgradeable] +const constructorArguments = [...]; +const boxFactory = await hre.ethers.getContractFactory("Box"); +const box = await hre.upgrades.deployBeacon(boxFactory, constructorArguments); +await box.waitForDeployment(); +``` + +:: + +#### Beacon proxy (upgrade) + +::code-group + +```typescript [@openzeppelin/hardhat-upgrades] +const upgradableProxyAddress = "UPGRADEABLE_PROXY_ADDRESS"; +const boxV2ContractFactory = await hre.ethers.getContractFactory("BoxV2"); +const boxV2 = await hre.upgrades.upgradeBeacon(upgradableProxyAddress, boxV2ContractFactory); +await boxV2.waitForDeployment(); +``` + +```typescript [hardhat-zksync-upgradeable] +const upgradableProxyAddress = "UPGRADEABLE_PROXY_ADDRESS"; +const boxV2ContractFactory = await hre.ethers.getContractFactory("BoxV2"); +const boxV2 = await hre.upgrades.upgradeProxy(upgradableProxyAddress, boxV2ContractFactory); +await boxV2.waitForDeployment(); +``` + +:: + +#### UUPS proxy (deployment) + +::code-group + +```typescript [@openzeppelin/contracts-upgradable] +const constructorArguments = [...]; +const initializerFunctionName = 'initialize' +const boxFactory = await hre.ethers.getContractFactory("Box"); +const box = await hre.upgrades.deployProxy(boxFactory, constructorArguments, { + initializer: initializerFunctionName, +}); +await box.waitForDeployment(); +``` + +```typescript [hardhat-zksync-upgradeable] +const constructorArguments = [...]; +const initializerFunctionName = 'initialize' +const boxFactory = await hre.ethers.getContractFactory("Box"); +const box = await hre.upgrades.deployProxy(boxFactory, constructorArguments, { + initializer: initializerFunctionName +}); +await box.waitForDeployment(); +``` + +:: + +#### UUPS proxy (upgrade) + +::code-group + +```typescript [@openzeppelin/hardhat-upgrades] +const constructorArguments = [...]; +const upgradableProxyAddress = "UPGRADEABLE_PROXY_ADDRESS"; +const boxV2ContractFactory = await hre.ethers.getContractFactory("BoxV2"); +const boxV2 = await hre.upgrades.upgradeProxy(upgradableProxyAddress,boxV2ContractFactory); +await boxV2.waitForDeployment(); +``` + +```typescript [hardhat-zksync-upgradeable] +const constructorArguments = [...]; +const upgradableProxyAddress = "UPGRADEABLE_PROXY_ADDRESS"; +const boxV2ContractFactory = await hre.ethers.getContractFactory("BoxV2"); +const boxV2 = await hre.upgrades.upgradeProxy(upgradableProxyAddress,boxV2ContractFactory); +await boxV2.waitForDeployment(); +``` + +:: + +### Custom deployment scripts with `hardhat-zksync` + +Additionally, you can write custom deployment scripts for ZKsync leveraging the `hre.deployer` object which is injected automatically by `@matterlabs/hardhat-zksync-deploy`. +The `Deployer` class provides helper methods to deploy smart contracts to ZKsync. Learn more on [`hardhat-zksync-deploy` methods](/build/tooling/hardhat/plugins/hardhat-zksync-deploy#methods). + +### Troubleshoting + +- **Contract size too large:** if the size of the generated bytecode is too large and can not be deployed, try compiling +the contract with the `mode: 3` flag in the `hardhat.config.ts` file to +optimize the bytecode size on compilation. Learn more on [`hardhat-zksync-solc` configuration](/build/tooling/hardhat/plugins/hardhat-zksync-solc#configuration). + +## Smart contract verification + + +There are no differences in the verification of smart contracts on ZKsync. + +By installing `@matterlabs/hardhat-zksync`, a verification plugin is provided. + +You will have to add a `verifyURL` on the ZKsync networks in the `hardhat.config.ts` file: + +```typescript + 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", + }, +``` + +You can run the verification task programmatically inside a script as follows: + +```js +const verificationId = await hre.run("verify:verify", { + address: contractAddress, + contract: contractFullyQualifedName, + constructorArguments: [...] +}); +``` + +Alternatively you can execute the verification task from your terminal: + +```sh +npx hardhat verify --network testnet 0x7cf08341524AAF292255F3ecD435f8EE1a910AbF "Hi there!" +``` + +Find more info in [`hardhat-zksync-verify`](/build/tooling/hardhat/plugins/hardhat-zksync-verify). + +## Scripting + +Most scripts will work out of the box as interacting with smart contracts deployed on ZKsync is exactly the same as on any EVM chain. + +For ZKsync-specific features like native account abstraction or paymaster transactions, there are plugins or extensions for the most popular libraries: + +- Ethers: [zksync-ethers](https://sdk.zksync.io/js/ethers/installation). +- Web3.js: [web3-plugin-zksync](https://sdk.zksync.io/js/web3js). +- Viem: [ZKsync extension](https://viem.sh/zksync). + +For other programming languages, please refer to the [SDK documentation](https://sdk.zksync.io/). + +## Multichain projects + +The [following example project](https://github.com/uF4No/hardhat-evm-zksync-example) can be used as a reference to target both EVM and ZKsync chains. + +Here are some recommendations for projects that target multiple chains: + +- Add the desired ZKsync networks with the `zksync:true` flag to the `hardhat.config.ts`. +- Make sure to run the compilation task with the `--network` flag when targeting ZKsync networks to use the custom compiler. +- When targeting ZKsync chains, the `@matterlabs/hardhat-zksync` plugin overrides the following plugins: `@nomiclabs/hardhat-ethers`, `@openzeppelin/hardhat-upgrades`. +- To avoid typescript collision errors between `@nomiclabs/hardhat-ethers` and +`@openzeppelin/hardhat-upgrades` with `@matterlabs/hardhat-zksync` make sure that only the latter is imported in the `hardhat.config.ts` file. +- Your deployment scripts should not require any changes if you're using `hardhat-deploy` or `hardhat-ethers`. +- If you have a separate directory for ZKsync deployment scripts, you can indicate the custom +deployment folder for the ZKsync network using [the `deployPaths` property](/build/tooling/hardhat/plugins/hardhat-zksync-deploy#network-specific-deployment-paths): + +```ts +const config: HardhatUserConfig = { + networks: { + zksyncTestnet: { + url: "https://sepolia.era.zksync.dev", + ethNetwork: "sepolia", + zksync: true, + // ADDITION + deployPaths: "deploy-zksync", //single deployment directory + deployPaths: ["deploy", "deploy-zksync"], //multiple deployment directories + }, + }, +}; +``` + +## Support + +If you're having issues migrating a Hardhat project to ZKsync, please [reach out to us by creating a GitHub discussion](%%zk_git_repo_zksync-developers%%/discussions/). diff --git a/cspell-config/cspell-zksync.txt b/cspell-config/cspell-zksync.txt index 31a35bdb..f3191afd 100644 --- a/cspell-config/cspell-zksync.txt +++ b/cspell-config/cspell-zksync.txt @@ -25,3 +25,4 @@ zkvyper !ZkSync ZKsync ZKP +zkvm