Інтеграція фронтенду з Viem

Проєктуємо та розробляємо блокчейн-рішення повного циклу: від архітектури смарт-контрактів до запуску DeFi-протоколів, NFT-маркетплейсів та криптобірж. Аудит безпеки, токеноміка, інтеграція з наявною інфраструктурою.
Показано 1 з 1Усі 1306 послуг
Інтеграція фронтенду з Viem
Простий
~2-3 дні
Часті запитання

Напрямки блокчейн-розробки

Етапи блокчейн-розробки

Останні роботи

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1306
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1218
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    920
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1147
  • image_logo-advance_0.webp
    Розробка логотипу компанії B2B Advance
    610
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    885

Інтеграція фронтенда з 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 днів.