# Getting started

In this tutorial we will demonstrate how to:

  1. Connect to the zkSync network.
  2. Deposit assets from Ethereum into zkSync.
  3. Make transfers.
  4. Withdraw funds back to Ethereum mainnet.

# Adding dependencies

yarn add zksync
yarn add ethers # ethers is a peer dependency of zksync

See Appendix A for how to add library to web project directly from https://unpkg.com (opens new window) CDN.

# Adding imports

You can import all the content of the zkSync library with the following statement:

import * as zksync from 'zksync';

Note, that it is not actually required to import all of the library. For instance, if you only need the Wallet class, you can safely do

import { Wallet } from 'zksync';

But in the rest of the book we will assume that the library was imported the first way to differentiate content imported from the zksync and ethers libraries.

# Connecting to zkSync network

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

const syncProvider = await zksync.getDefaultProvider('goerli');

Most operations require some read-only access to the Ethereum network. We use ethers library to interact with Ethereum.

const ethersProvider = 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 zksync.Signer and send transaction to zkSync network using zksync.Provider.

zksync.Wallet is a wrapper around 2 objects:

  • ethers.Signer to sign Ethereum transactions.
  • zksync.Signer to sign native zkSync transactions.

The private key used by zksync.Signer is implicitly derived from Ethereum signature of a special message.

// Create ethereum wallet using ethers.js
const ethWallet = ethers.Wallet.fromMnemonic(MNEMONIC).connect(ethersProvider);

// Derive zksync.Signer from ethereum wallet.
const syncWallet = await zksync.Wallet.fromEthSigner(ethWallet, syncProvider);

# Depositing assets from Ethereum into zkSync

We are going to deposit 1.0 ETH to our zkSync account.

const deposit = await syncWallet.depositToSyncFromEthereum({
  depositTo: syncWallet.address(),
  token: 'ETH',
  amount: ethers.utils.parseEther('1.0')

"ETH" stands for native Ether. To transfer supported ERC20 token use ERC20 address or ERC20 symbol instead of "ETH".

After the tx is submitted to the Ethereum node, we can track its status using the returned object:

// Await confirmation from the zkSync operator
// Completes when a promise is issued to process the tx
const depositReceipt = await deposit.awaitReceipt();

// Await verification
// Completes when the tx reaches finality on Ethereum
const depositReceipt = await deposit.awaitVerifyReceipt();

# Unlocking zkSync account

To control assets in zkSync network, an account must register a separate public key once.

if (!(await syncWallet.isSigningKeySet())) {
  if ((await syncWallet.getAccountId()) == undefined) {
    throw new Error('Unknown account');

  // As any other kind of transaction, `ChangePubKey` transaction requires fee.
  // User doesn't have (but can) to specify the fee amount. If omitted, library will query zkSync node for
  // the lowest possible amount.
  const changePubkey = await syncWallet.setSigningKey({
    feeToken: 'ETH',
    ethAuthType: 'ECDSA'

  // Wait until the tx is committed
  await changePubkey.awaitReceipt();

# Checking zkSync account balance

// Committed state is not final yet
const committedETHBalance = await syncWallet.getBalance('ETH');

// Verified state is final
const verifiedETHBalance = await syncWallet.getBalance('ETH', 'verified');

To list all tokens of this account at once, use getAccountState:

const state = await syncWallet.getAccountState();

const committedBalances = state.committed.balances;
const committedETHBalance = committedBalances['ETH'];

const verifiedBalances = state.verified.balances;
const committedETHBalance = verifiedBalances['ETH'];

# Making a transfer in zkSync

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

const ethWallet2 = ethers.Wallet.fromMnemonic(MNEMONIC2).connect(ethersProvider);
const syncWallet2 = await zksync.SyncWallet.fromEthSigner(ethWallet2, syncProvider);

We are going to transfer 0.999 ETH to another account and pay 0.001 ETH as a fee to the operator (zkSync account balance of the sender is going to be decreased by 0.999 + 0.001 ETH). The use of closestPackableTransactionAmount() and closestPackableTransactionFee() is necessary because the precision of transfer in zkSync is limited (see docs below).

const amount = zksync.utils.closestPackableTransactionAmount(ethers.utils.parseEther('0.999'));
const fee = zksync.utils.closestPackableTransactionFee(ethers.utils.parseEther('0.001'));

const transfer = await syncWallet.syncTransfer({
  to: syncWallet2.address(),
  token: 'ETH',

Note that setting fee manually is not required. If fee field is omitted, SDK will choose the lowest possible fee acceptable by server:

const amount = zksync.utils.closestPackableTransactionAmount(ethers.utils.parseEther('0.999'));

const transfer = await syncWallet.syncTransfer({
  to: syncWallet2.address(),
  token: 'ETH',

To track the status of this transaction:

const transferReceipt = await transfer.awaitReceipt();

# Withdrawing funds back to Ethereum

const withdraw = await syncWallet2.withdrawFromSyncToEthereum({
  ethAddress: ethWallet2.address,
  token: 'ETH',
  amount: ethers.utils.parseEther('0.998')

Assets will be withdrawn to the target wallet after the zero-knowledge proof of zkSync block with this operation is generated and verified by the mainnet contract.

We can wait until ZKP verification is complete:

await withdraw.awaitVerifyReceipt();