Introduction

Public, typed API surface for ZKsyncOSIncorruptible Financial Infrastructure.

What Is This?

The zksync-js provides lightweight adapters for ethers and viem to build L1 ↔ L2 flows — deposits and withdrawals — with a small, focused API. You’ll work with:

  • Adapter-level Clients (providers/wallets, resolved addresses, convenience contracts)
  • High-level SDKs (resources for deposits/withdrawals plus tokens and contracts)
  • ZKsync-specific RPC helpers (client.zks.*)
  • A consistent, typed Error model (ZKsyncError, try* results)

Quick Start

Ethers Example
import { JsonRpcProvider, Wallet, parseEther } from 'ethers';
import { ETH_ADDRESS } from '@matterlabs/zksync-js/core';
import { createEthersClient, createEthersSdk } from '@matterlabs/zksync-js/ethers';

const l1 = new JsonRpcProvider(process.env.L1_RPC!);
const l2 = new JsonRpcProvider(process.env.L2_RPC!);
const signer = new Wallet(process.env.PRIVATE_KEY!, l1);

const client = createEthersClient({ l1, l2, signer });
const sdk = createEthersSdk(client);

const tokenAddress = '0xTokenL1...';

// Example: deposit 0.05 ETH L1 → L2 and wait for L2 execution
const handle = await sdk.deposits.create({
  token: ETH_ADDRESS, // 0x…00 sentinel for ETH supported
  amount: parseEther('0.05'),
  to: await signer.getAddress() as `0x${string}`,
});

await sdk.deposits.wait(handle, { for: 'l2' });

// Example: resolve core contracts
const { l1NativeTokenVault } = await sdk.contracts.instances();

// Example: map a token L1 → L2
const token = await sdk.tokens.resolve(tokenAddress);
console.log(token.l2);
Viem Example
import { createViemClient, createViemSdk } from '@matterlabs/zksync-js/viem';
import { createPublicClient, createWalletClient, http } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { ETH_ADDRESS } from '@matterlabs/zksync-js/core';

const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);

const l1 = createPublicClient({ transport: http(process.env.L1_RPC!) });
const l2 = createPublicClient({ transport: http(process.env.L2_RPC!) });
const l1Wallet = createWalletClient({ chain: l1Chain, account, transport: http(process.env.L1_RPC!) });
const l2Wallet = createWalletClient({ chain: l2Chain, account, transport: http(process.env.L2_RPC!) });

const client = createViemClient({ l1, l2, l1Wallet, l2Wallet });
const sdk = createViemSdk(client);

const tokenAddress = '0xYourToken';

// Example: deposit 0.05 ETH L1 → L2, wait for L2 execution
const handle = await sdk.deposits.create({
  token: ETH_ADDRESS,               // 0x…00 sentinel for ETH
  amount: 50_000_000_000_000_000n,  // 0.05 ETH in wei
  to: account.address,
});
await sdk.deposits.wait(handle, { for: 'l2' });

// Example: resolve contracts and map an L1 token to its L2 address
const { l1NativeTokenVault } = await sdk.contracts.instances();
const token = await sdk.tokens.resolve(tokenAddress);
console.log(token.l2);

What's Documented Here

AreaDescription
Ethers · ClientLow-level handle: providers/signer, resolved addresses, convenience contracts, ZK RPC access.
Ethers · SDKHigh-level deposits/withdrawals plus token and contract resources.
Ethers · ContractsResolved addresses and connected core contracts.
Ethers · DepositsL1 → L2 flow with quote, prepare, create, status, and wait.
Ethers · WithdrawalsL2 → L1 flow with quote, prepare, create, status, wait, and finalize.
Viem · ClientPublicClient / WalletClient integration, resolved addresses, contracts, ZK RPC access.
Viem · SDKSame high-level surface as ethers, typed to viem contracts.
Viem · ContractsResolved addresses and connected core contracts.
Viem · DepositsL1 → L2 flow with quote, prepare, create, status, and wait.
Viem · WithdrawalsL2 → L1 flow with quote, prepare, create, status, wait, and finalize.
Core · ZK RPCZKsync-specific RPC: getBridgehubAddress, getBytecodeSupplierAddress, getBlockMetadataByNumber, getL2ToL1LogProof.
Core · Error modelTyped ZKsyncError envelope and try* result helpers.

Notes & Conventions

[!NOTE] Standard eth_* RPC should always be performed through your chosen base library (ethers or viem). The SDK only adds ZKsync-specific RPC methods via client.zks.* (e.g. getBridgehubAddress, getBytecodeSupplierAddress, getBlockMetadataByNumber, getGenesis).

  • Every resource method has a try* variant (e.g. tryCreate) that returns a result object instead of throwing. When errors occur, the SDK throws ZKsyncError with a stable, structured envelope (see Error model).
  • Address resolution comes from on-chain lookups and well-known constants, but can be overridden in the client constructor for forks/tests.