Introduction
Public, typed API surface for ZKsyncOS — Incorruptible 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
| Area | Description |
|---|---|
| Ethers · Client | Low-level handle: providers/signer, resolved addresses, convenience contracts, ZK RPC access. |
| Ethers · SDK | High-level deposits/withdrawals plus token and contract resources. |
| Ethers · Contracts | Resolved addresses and connected core contracts. |
| Ethers · Deposits | L1 → L2 flow with quote, prepare, create, status, and wait. |
| Ethers · Withdrawals | L2 → L1 flow with quote, prepare, create, status, wait, and finalize. |
| Viem · Client | PublicClient / WalletClient integration, resolved addresses, contracts, ZK RPC access. |
| Viem · SDK | Same high-level surface as ethers, typed to viem contracts. |
| Viem · Contracts | Resolved addresses and connected core contracts. |
| Viem · Deposits | L1 → L2 flow with quote, prepare, create, status, and wait. |
| Viem · Withdrawals | L2 → L1 flow with quote, prepare, create, status, wait, and finalize. |
| Core · ZK RPC | ZKsync-specific RPC: getBridgehubAddress, getBytecodeSupplierAddress, getBlockMetadataByNumber, getL2ToL1LogProof. |
| Core · Error model | Typed 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 viaclient.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 throwsZKsyncErrorwith 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.