Using viem 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
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
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
});
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
const blockNumber = await client.getBlockNumber();
console.log(`Current block number: ${blockNumber}`);
Read from a Contract
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
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
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());
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
// 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 });
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
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}`);