Getting started

Getting started


  • Please note that with the system update released in Feb 2023, the ergs concept is only used by the VM while the API layer operates with gas.
  • For more information, read the changelog.


While most of the existing SDKs should work out of the box, deploying smart contracts or using unique zkSync features, like account abstraction, requires providing additional fields to those that Ethereum transactions have by default.

To provide easy access to all of the features of zkSync Era, the zksync-web3 JavaScript SDK was created, which is made in a way that has an interface very similar to those of ethersopen in new window. In fact, ethers is a peer dependency of our library and most of the objects exported by zksync-web3 (e.g. Wallet, Provider etc.) inherit from the corresponding ethers objects and override only the fields that need to be changed.

The library is made in such a way that after replacing ethers with zksync-web3 most client apps will work out of box.

Adding dependencies

yarn add zksync-web3
yarn add ethers@5 # ethers is a peer dependency of zksync-web3

Then you can import all the content of the ethers library and the zksync-web3 library with the following statement:

import * as zksync from "zksync-web3";
import * as ethers from "ethers";

Connecting to zkSync

To interact with the zkSync network users need to know the endpoint of the operator node.

// Currently, only one environment is supported.
import { Wallet, Provider } from "zksync-web3";

const provider = new Provider("");
// Private key of the account to connect
const wallet = new Wallet("<WALLET-PRIVATE-KEY>").connect(provider);

Note: Currently, only goerli network is supported.

Some operations require access to the Ethereum network. ethers library should be used to interact with Ethereum.

const ethProvider = ethers.getDefaultProvider("goerli");

Creating a wallet

To control your account in zkSync, use the zksync.Wallet object. It can sign transactions with keys stored in ethers.Wallet and send transaction to the zkSync network using zksync.Provider.

// Derive zksync.Wallet from ethereum private key.
// zkSync's wallets support all of the methods of ethers' wallets.
// Also, both providers are optional and can be connected to later via `connect` and `connectToL1`.
const zkSyncWallet = new zksync.Wallet(PRIVATE_KEY, zkSyncProvider, ethProvider);

Depositing funds

Let's deposit 1.0 ETH to our zkSync account.

const deposit = await zkSyncWallet.deposit({
  token: zksync.utils.ETH_ADDRESS,
  amount: ethers.utils.parseEther("1.0"),

NOTE: Each token inside zkSync has an address. If ERC-20 tokens are being bridged, you should supply the token's L1 address in the deposit function, or zero address (0x0000000000000000000000000000000000000000) if you want to deposit ETH. Note, that for the ERC-20 tokens the address of their corresponding L2 token will be different from the one on Ethereum.

After the transaction is submitted to the Ethereum node, its status can be tracked using the transaction handle:

// Await processing of the deposit on L1
const ethereumTxReceipt = await deposit.waitL1Commit();

// Await processing the deposit on zkSync
const depositReceipt = await deposit.wait();

Checking zkSync account balance

// Retrieving the current (committed) zkSync ETH balance of an account
const committedEthBalance = await zkSyncWallet.getBalance(zksync.utils.ETH_ADDRESS);

// Retrieving the ETH balance of an account in the last finalized zkSync block.
const finalizedEthBalance = await zkSyncWallet.getBalance(zksync.utils.ETH_ADDRESS, "finalized");

You can read more about what committed and finalized blocks are here.

Performing a transfer

Now, let's create a second wallet and transfer some funds into it. Note that it is possible to send assets to any fresh Ethereum account, without preliminary registration!

const zkSyncWallet2 = new zksync.Wallet(PRIVATE_KEY2, zkSyncProvider, ethProvider);

Let's transfer 1 ETH to another account:

The transfer method is a helper method that enables transferring ETH or any ERC-20 token within a single interface.

const amount = ethers.utils.parseEther("1.0");

const transfer = await zkSyncWallet.transfer({
  to: zkSyncWallet2.address,
  token: zksync.utils.ETH_ADDRESS,

To track the status of this transaction:

// Await commitment
const committedTxReceipt = await transfer.wait();

// Await finalization on L1
const finalizedTxReceipt = await transfer.waitFinalize();

Withdrawing funds

There are two ways to withdraw funds from zkSync to Ethereum, calling the operation through L2 or L1. If the withdrawal operation is called through L1, then the operator has a period of time during which he must process the transaction, otherwise PriorityMode will be turned on. This ensures that the operator cannot stage the transaction. But in most cases, a call via L2 is sufficient.

const withdrawL2 = await zkSyncWallet.withdraw({
  token: zksync.utils.ETH_ADDRESS,
  amount: ethers.utils.parseEther("0.5"),

Assets will be withdrawn to the target wallet(if do not define the to address in the withdraw method's argument - the sender address will be chosen as a destination) after the validity proof of the zkSync block with this transaction is generated and verified by the mainnet contract.

It is possible to wait until the validity proof verification is complete:

await withdrawL2.waitFinalize();

Deploying a contract

A guide on deploying smart contracts using our hardhat plugin is available here.

Adding tokens to the standard bridge

Adding tokens to the zkSync standard bridge can be done in a permissionless way. After adding a token to zkSync, it can be used in all types of transactions.

The documentation on adding tokens to zkSync can be found here.

Last update:
Contributors: Blessing Krofegha,Antonio,Stanislav Bezkorovainyi,Dustin Brickwood,niramisa,AnastasiiaVashchuk,Dimitris Apostolou,Roman Brodetski,barakshani,botdad,frosh-li,omahs