Интеграция фронтенда с Viem

Проектируем и разрабатываем блокчейн-решения полного цикла: от архитектуры смарт-контрактов до запуска DeFi-протоколов, NFT-маркетплейсов и криптобирж. Аудит безопасности, токеномика, интеграция с существующей инфраструктурой.
Показано 1 из 1 услугВсе 1306 услуг
Интеграция фронтенда с Viem
Простая
~2-3 рабочих дня
Часто задаваемые вопросы
Направления блокчейн-разработки
Этапы блокчейн-разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1221
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1163
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    855
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1056
  • image_logo-advance_0.png
    Разработка логотипа компании B2B Advance
    561
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    828

Интеграция фронтенда с Viem

ethers.js версии 5 весил 285KB gzipped, имел mutable провайдеры и не был написан с tree-shaking в голове. viem — это переосмысление того, как TypeScript-клиент для EVM должен выглядеть в 2024 году: модульная архитектура, zero-dep ядро, полный TypeScript с выводом типов из ABI.

Ключевые концепции viem

Клиенты: Public, Wallet, Test

В viem нет единого «провайдера». Есть три типа клиентов с разными возможностями:

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

// Для чтения данных из блокчейна (не нужен кошелёк)
const publicClient = createPublicClient({
  chain: mainnet,
  transport: http("https://eth-mainnet.g.alchemy.com/v2/KEY")
})

// Для подписи и отправки транзакций (нужен кошелёк)
const walletClient = createWalletClient({
  chain: mainnet,
  transport: custom(window.ethereum)
})

transport может быть http(), webSocket(), fallback([http(...), http(...)]) — автоматический failover между RPC провайдерами. Это само по себе решает проблему надёжности, которую раньше решали вручную.

Типизация ABI — главное преимущество

Viem генерирует типы из ABI в compile time. Если передать неправильный аргумент — TypeScript ошибка, не runtime revert:

const abi = [
  {
    name: "transfer",
    type: "function",
    inputs: [
      { name: "to", type: "address" },
      { name: "amount", type: "uint256" }
    ],
    outputs: [{ type: "bool" }]
  }
] as const  // важно: as const для вывода типов

// TypeScript знает: первый аргумент - `0x${string}`, второй - bigint
const hash = await walletClient.writeContract({
  address: "0xTokenAddress",
  abi,
  functionName: "transfer",
  args: ["0xRecipient", parseEther("1.0")]
})

Без as const — теряется вся типизация. Это важный нюанс при работе с viem.

Encoding/decoding данных

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

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

// Encode calldata вручную
const calldata = encodeAbiParameters(
  [{ type: "address" }, { type: "uint256" }],
  ["0xRecipient", parseEther("1.0")]
)

parseUnits / formatUnits / parseEther / formatEther — утилиты для работы с BigInt значениями. Все числа в viem — нативный JavaScript BigInt, не ethers.BigNumber.

Интеграция с React через wagmi

viem — это transport layer. Для React-приложений — wagmi (hooks поверх 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 полностью построен на viem, кеширует результаты через TanStack Query. useReadContract — это фактически useQuery обёртка над publicClient.readContract.

Мультичейн конфигурация

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"),
  }
})

Кастомные сети (testnets, L2 без поддержки в viem/chains) определяются вручную:

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"] } }
})

Миграция с 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 })

Главное изменение в мышлении: вместо объектов контрактов (new ethers.Contract(...)) — функции с явной передачей abi и address. Verbose, но типобезопасно.

Сроки

Настройка viem + wagmi с мультичейн конфигурацией, подключением кошелька и базовыми read/write операциями — 1-2 дня. Миграция существующего проекта с ethers.js — зависит от объёма, обычно 2-5 дней.