Frontend Integration with Viem

We design and develop full-cycle blockchain solutions: from smart contract architecture to launching DeFi protocols, NFT marketplaces and crypto exchanges. Security audits, tokenomics, integration with existing infrastructure.
Showing 1 of 1 servicesAll 1306 services
Frontend Integration with Viem
Simple
~2-3 business days
FAQ
Blockchain Development Services
Blockchain Development Stages
Latest works
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1051
  • image_logo-advance_0.png
    B2B Advance company logo design
    561
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    827
  • image_logo-aider_0.jpg
    AIDER company logo development
    762
  • image_crm_chasseurs_493_0.webp
    CRM development for Chasseurs
    850

Viem Frontend Integration

ethers.js v5 weighed 285KB gzipped, had mutable providers, and wasn't written with tree-shaking in mind. viem is a rethinking of how a TypeScript client for EVM should look in 2024: modular architecture, zero-dep core, full TypeScript with type inference from ABI.

Key Concepts of Viem

Clients: Public, Wallet, Test

In viem there's no single "provider". There are three types of clients with different capabilities:

import { createPublicClient, createWalletClient, http, custom } from "viem"
import { mainnet } from "viem/chains"

// For reading data from blockchain (wallet not needed)
const publicClient = createPublicClient({
  chain: mainnet,
  transport: http("https://eth-mainnet.g.alchemy.com/v2/KEY")
})

// For signing and sending transactions (wallet needed)
const walletClient = createWalletClient({
  chain: mainnet,
  transport: custom(window.ethereum)
})

transport can be http(), webSocket(), fallback([http(...), http(...)]) — automatic failover between RPC providers. This alone solves reliability problems that used to be solved manually.

ABI Typing — Main Advantage

Viem generates types from ABI at compile time. If you pass a wrong argument — TypeScript error, not runtime revert:

const abi = [
  {
    name: "transfer",
    type: "function",
    inputs: [
      { name: "to", type: "address" },
      { name: "amount", type: "uint256" }
    ],
    outputs: [{ type: "bool" }]
  }
] as const  // important: as const for type inference

// TypeScript knows: first argument is `0x${string}`, second is bigint
const hash = await walletClient.writeContract({
  address: "0xTokenAddress",
  abi,
  functionName: "transfer",
  args: ["0xRecipient", parseEther("1.0")]
})

Without as const — all typing is lost. This is an important detail when working with viem.

Encoding/Decoding Data

import { encodeAbiParameters, decodeAbiParameters, parseAbi } from "viem"

// Decode transaction data
const decoded = decodeAbiParameters(
  parseAbi(["function transfer(address to, uint256 amount)"]),
  txData
)

// Encode calldata manually
const calldata = encodeAbiParameters(
  [{ type: "address" }, { type: "uint256" }],
  ["0xRecipient", parseEther("1.0")]
)

parseUnits / formatUnits / parseEther / formatEther — utilities for working with BigInt values. All numbers in viem are native JavaScript BigInt, not ethers.BigNumber.

Integration with React via Wagmi

viem is a transport layer. For React applications — wagmi (hooks over viem):

import { useReadContract, useWriteContract, useAccount } from "wagmi"

function TokenBalance({ tokenAddress }: { tokenAddress: `0x${string}` }) {
  const { address } = useAccount()

  const { data: balance } = useReadContract({
    address: tokenAddress,
    abi: erc20Abi,
    functionName: "balanceOf",
    args: [address!],
    query: { enabled: !!address }
  })

  const { writeContract, isPending } = useWriteContract()

  const transfer = (to: `0x${string}`, amount: bigint) =>
    writeContract({
      address: tokenAddress,
      abi: erc20Abi,
      functionName: "transfer",
      args: [to, amount]
    })

  return <div>Balance: {balance ? formatEther(balance) : "..."}</div>
}

wagmi v2 is fully built on viem, caches results via TanStack Query. useReadContract is essentially a useQuery wrapper over publicClient.readContract.

Multichain Configuration

import { createConfig, http } from "wagmi"
import { mainnet, polygon, arbitrum, base } from "wagmi/chains"

export const config = createConfig({
  chains: [mainnet, polygon, arbitrum, base],
  transports: {
    [mainnet.id]: http("https://eth-mainnet.g.alchemy.com/v2/KEY"),
    [polygon.id]: http("https://polygon-mainnet.g.alchemy.com/v2/KEY"),
    [arbitrum.id]: http("https://arb-mainnet.g.alchemy.com/v2/KEY"),
    [base.id]: http("https://base-mainnet.g.alchemy.com/v2/KEY"),
  }
})

Custom networks (testnets, L2 without support in viem/chains) are defined manually:

import { defineChain } from "viem"

const customChain = defineChain({
  id: 1234,
  name: "Custom Network",
  nativeCurrency: { name: "ETH", symbol: "ETH", decimals: 18 },
  rpcUrls: { default: { http: ["https://rpc.custom.network"] } }
})

Migration from ethers.js v5

ethers.js v5 viem
new ethers.providers.Web3Provider(window.ethereum) createWalletClient({ transport: custom(window.ethereum) })
provider.getBalance(address) publicClient.getBalance({ address })
contract.balanceOf(address) publicClient.readContract({ abi, functionName: 'balanceOf', args: [address] })
ethers.utils.parseEther("1.0") parseEther("1.0")
ethers.BigNumber.from("100") BigInt("100")
signer.signMessage(message) walletClient.signMessage({ message })

The main mindset change: instead of contract objects (new ethers.Contract(...)) — functions with explicit abi and address. Verbose, but type-safe.

Timeline

Setting up viem + wagmi with multichain configuration, wallet connection and basic read/write operations — 1-2 days. Migrating existing project with ethers.js — depends on volume, usually 2-5 days.