Skip to main content

Interacting with the blockchain with viem

viem a TypeScript interface for Ethereum that provides low-level stateless primitives for interacting with Ethereum.

You can use viem to interact with smart contracts deployed on Lisk. Viem provides first-class support for chains implemented on the OP stack, see viem.sh > OP stack.

Install

To install viem run the following command:

npm install --save viem

Setup

Before you can start using viem, you need to setup a Client with a desired Transport and Chain.

Public Client

In order to read data from Lisk, you need to create a Public client.

By extending the public client with publicActionsL2 or publicActionsL1, you have access to additional methods for Lisk and other chains built on top of the OP stack. See Layer 2 extensions for more information.

public-client.js
import { createPublicClient, http } from 'viem'
import { liskSepolia, sepolia } from 'viem/chains'
import { publicActionsL2, publicActionsL1 } from 'viem/op-stack'

export const publicClientL2 = createPublicClient({
chain: liskSepolia,
transport: http()
}).extend(publicActionsL2())

export const publicClientL1 = createPublicClient({
chain: sepolia,
transport: http()
}).extend(publicActionsL1())

Wallet Client

In order to write data to Lisk, you need to create a Wallet client (createWalletClient) and specify an Account to use.

By extending the wallet client with walletActionsL1 or walletActionsL2, you have access to additional methods for Lisk and other chains built on top of the OP stack. See Layer 2 extensions for more information.

wallet-client.js
import { createWalletClient, custom } from 'viem'
import { sepolia, liskSepolia } from 'viem/chains'
import { walletActionsL1, walletActionsL2 } from 'viem/op-stack'

// Retrieve Account from an EIP-1193 Provider.
const [account] = await window.ethereum.request({
method: 'eth_requestAccounts'
})

export const walletClientL1 = createWalletClient({
account,
chain: sepolia,
transport: custom(window.ethereum)
}).extend(walletActionsL1());

export const walletClientL2 = createWalletClient({
account,
chain: liskSepolia,
transport: custom(window.ethereum)
}).extend(walletActionsL2());
info

In addition to making a JSON-RPC request (eth_requestAccounts) to get an Account, viem provides various helper methods for creating an Account, including: privateKeyToAccount, mnemonicToAccount, and hdKeyToAccount.

Reading data from the blockchain

Create a public client and use it to read and access data from Lisk using Public Actions and OP stack public actions.

Public Actions are client methods that map one-to-one with a "public" Ethereum RPC method (eth_blockNumber, eth_getBalance, etc.).

For example, you can use the getBlockNumber action to get the latest block:

read-example.js
import { parseEther } from 'viem'
import { publicClientL2 } from './public-client.js'

const blockNumber = await publicClientL2.getBlockNumber();

export const l1Gas = await publicClientL2.estimateL1Gas({
account: '0x3C46A11471f285E36EE8d089473ce98269D1b081',
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('0.1')
})

console.log(blockNumber);
console.log(l1Gas);

Writing data to the blockchain

Create a wallet client and use it to read and access data from Lisk using Wallet Actions and OP stack wallet actions.

Wallet Actions are actions that map one-to-one with a "wallet" or "signable" Ethereum RPC method (eth_requestAccounts, eth_sendTransaction, etc).

For example, you can use the sendTransaction action to post a transaction:

write-example.js
import { walletClientL2 } from './wallet-client.js'

const hash = await walletClientL2.sendTransaction({
to: '0x...',
value: 1000000000000000000n
})

console.log(hash);

Interacting with smart contracts

You can use viem to interact with a smart contract on Lisk by creating a Contract instance using getContract and passing it the contract ABI, contract address, and and Public and/or Wallet Client:

contract-example.js
import { getContract } from 'viem'
import { wagmiAbi } from './abi.js'
import { publicClient, walletClient } from './client.js'

// 1. Create contract instance
const contract = getContract({
address: 'CONTRACT_ADDRESS',
abi: wagmiAbi,
// 1a. Insert a single client
//client: publicClient,
// 1b. Or public and/or wallet clients
client: { public: publicClient, wallet: walletClient }
})

// 2. Call contract methods, fetch events, listen to events, etc.
const result = await contract.read.totalSupply()
const logs = await contract.getEvents.Transfer()
const unwatch = contract.watchEvent.Transfer(
{ from: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e' },
{ onLogs(logs) { console.log(logs) } }
)
info

CONTRACT_ADDRESS is the address of the deployed contract.