Using viem with ZKsync

Learn how to use the viem/zksync plugin to interact with ZKsync.

This guide outlines how to use the ZKsync viem plugin to interact with ZKsync and use specific actions like sending transcations via paymasters.

What you'll learn:

  • How to install viem and the ZKsync plugin
  • How to send RPC requests to ZKsync
  • How to use ZKsync-specific methods
GitHub

Tools:

  • - viem
guideviemfrontend
Last Updated: Jun 11, 2025

viem is a TypeScript interface for Ethereum that now includes support for ZKsync, offering low-level stateless primitives for interacting with both Ethereum and ZKsync. You can use viem to interact seamlessly with smart contracts deployed on ZKsync. For more information on ZKsync specific support please refer to the viem documentation.

You can use viem to interact with smart contracts deployed on ZKsync.

Installation

Start by adding Viem to your project. Open your terminal and execute the following command:

npm install viem

This command installs the latest version of Viem and adds it to your project's dependencies.

Initial Setup

Client Configuration

Before using viem, you need to setup a Client with a chosen Transport and Chain.

Example

src/App.tsx
import { createPublicClient, http, type Chain, type PublicClient } from 'viem';
import {
  // zksync,
  zksyncSepoliaTestnet,
  // zksyncInMemoryNode,
  // zksyncLocalNode
} from 'viem/chains';

// Initialize the Viem client
const client: PublicClient = createPublicClient({
  chain: zksyncSepoliaTestnet as Chain, // Specify the ZKsync network
  transport: http(), // Define the transport method
});
For ZKsync Era Mainnet, replace zksyncSepoliaTestnet with zksync.

Reading Data

You can access ZKsync data by invoking public actions that mirror Ethereum RPC methods using the viem client.

Fetch the Latest Block Number

Read.tsx
      const blockNumber = await client.getBlockNumber();
      console.log(`Current block number: ${blockNumber}`);

Read from a Contract

Read.tsx
      const messageResponse = await client.readContract({
        address: '0xe2e810a56672336C0Cc303E5f8156A5a56DBE150',
        abi: greeterAbi,
        functionName: 'getLastMessage',
      });

Writing Data

To write data, such as sending transactions, you need to set up a Wallet client.

Sending Transactions

Write.tsx
import { createWalletClient, custom, encodeFunctionData } from 'viem';
import {
  // zksync,
  zksyncSepoliaTestnet,
  // zksyncInMemoryNode,
  // zksyncLocalNode
} from 'viem/chains';
import greeterAbi from '../utils/Greeter.json';
import type { Dispatch, SetStateAction } from 'react';
import { publicClient } from '../utils/client';

const Write = ({ update }: { update: Dispatch<SetStateAction<number>> }) => {
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  const window: any = globalThis.window;

  async function writeToContract() {
    try {
      // Request account access from the Ethereum provider
      const [account] = await window.ethereum.request({ method: 'eth_requestAccounts' });
      if (!account) return;
      // Configure the wallet client
      const client = createWalletClient({
        account,
        chain: zksyncSepoliaTestnet,
        transport: custom(window.ethereum),
      });

      const data = encodeFunctionData({
        abi: greeterAbi,
        functionName: 'sendMessage',
        args: ['ZK is the endgame!'],
      });

      // Example transaction
      const hash = await client.sendTransaction({
        account,
        to: '0xe2e810a56672336C0Cc303E5f8156A5a56DBE150',
        data,
        chain: client.chain,
      });

      await publicClient.waitForTransactionReceipt({ hash });
      update((prev) => prev + 1);
    } catch (error) {
      console.error('Error writing to contract:', error);
    }
  }

Advanced Usage

Utilizing Paymasters

Paymasters cover transaction fees, facilitating a smoother user experience. To utilize ZKsync Era's native account abstraction and Paymasters, extend the Wallet client with eip712WalletActions:

Setup

Paymaster.tsx
import { createWalletClient, custom } from 'viem';
import {
  // zksync,
  zksyncSepoliaTestnet,
  // zksyncInMemoryNode,
  // zksyncLocalNode
} from 'viem/chains';
import { eip712WalletActions, getGeneralPaymasterInput } from 'viem/zksync';

      // Initialize and extend the wallet client
      const client = createWalletClient({
        chain: zksyncSepoliaTestnet,
        transport: custom(window.ethereum!),
      }).extend(eip712WalletActions());
Ensure your paymaster contract is set up and funded appropriately.

For a live example, check out this StackBlitz demo. Remember to replace PAYMASTER_CONTRACT_ADDRESS with your own!

Contract Interactions with Paymasters

Contract Function Call

Paymaster.tsx
      // Replace with your paymaster address
      const paymasterAddress = '0xdeAD0bD94D5975538B7678C0EaAd28d20FbFF8A7';

      // Call the contract function
      const hash = await client.writeContract({
        account,
        address: '0xe2e810a56672336C0Cc303E5f8156A5a56DBE150',
        abi: greeterAbi,
        functionName: 'sendMessage',
        args: ['ZKsync Paymaster'],
        paymaster: paymasterAddress,
        paymasterInput: getGeneralPaymasterInput({ innerInput: new Uint8Array() }),
      });

      await publicClient.waitForTransactionReceipt({ hash });
Ensure your paymaster contract is set up and funded appropriately.

For a live example, check out this StackBlitz demo. Remember to replace PAYMASTER_CONTRACT_ADDRESS with your own!

Smart Contract Interactions

Interact with smart contracts by creating a contract instance, providing ABI, address, and the client.

Example

ReadContract.tsx
import { getContract } from 'viem';
import greeterAbi from '../utils/Greeter.json'; // Your contract's ABI
import { useEffect, useState } from 'react';
import { publicClient } from '../utils/client'; // Your initialized Viem client

      // Initialize the contract instance
      const contract = getContract({
        address: '0xe2e810a56672336C0Cc303E5f8156A5a56DBE150',
        abi: greeterAbi,
        client: publicClient,
      });

      // Interact with your contract
      const result = await contract.read.getLastMessage();
      console.log(`Last Message: ${result}`);

Made with ❤️ by the ZKsync Community