Реалізація взаємодії з блокчейном через wagmi/viem на сайті

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Реалізація взаємодії з блокчейном через wagmi/viem на сайті
Середня
~3-5 робочих днів
Часті питання

Наші компетенції:

Етапи розробки

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

  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Реалізація взаємодії з блокчейном через wagmi/viem на веб-сайті

wagmi + viem — сучасний стандарт для Web3-фронтенду на React. wagmi надає hook'и (useAccount, useReadContract, useWriteContract), viem — низькорівневий типізований клієнт. Ця комбінація замінює ethers.js + react-query: менше boilerplate, вбудований кеш, автоматичний refetch по блокам.

Як це відрізняється від ethers.js

ethers.js — бібліотека, орієнтована на класи, з мінливим станом. new Contract(...) створює об'єкт, який тримає провайдер всередині себе. У React це проблема: коли мережа або рахунок змінюються, об'єкт потрібно пересоздавати.

viem побудований на функціях та незмінних клієнтах. wagmi управляє життєвим циклом клієнтів автоматично — коли рахунок змінюється, hook'и автоматично оновлюють дані.

// підхід ethers.js
const provider = new BrowserProvider(window.ethereum);
const contract = new Contract(address, abi, provider);
const balance = await contract.balanceOf(walletAddress);

// підхід viem/wagmi
const balance = await readContract(publicClient, {
  address,
  abi,
  functionName: 'balanceOf',
  args: [walletAddress],
});

Конфігурація

// lib/wagmi.ts
import { createConfig, http } from 'wagmi';
import { mainnet, arbitrum, base } from 'wagmi/chains';
import { injected, walletConnect } from 'wagmi/connectors';

export const config = createConfig({
  chains: [mainnet, arbitrum, base],
  connectors: [
    injected(),
    walletConnect({ projectId: process.env.NEXT_PUBLIC_WC_PROJECT_ID! }),
  ],
  transports: {
    [mainnet.id]: http(process.env.ETH_RPC_URL!),
    [arbitrum.id]: http(process.env.ARBITRUM_RPC_URL!),
    [base.id]: http(process.env.BASE_RPC_URL!),
  },
});
// app/providers.tsx
'use client';
import { WagmiProvider } from 'wagmi';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { config } from '@/lib/wagmi';

const queryClient = new QueryClient();

export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <WagmiProvider config={config}>
      <QueryClientProvider client={queryClient}>
        {children}
      </QueryClientProvider>
    </WagmiProvider>
  );
}

Читання даних з контракту

// hooks/useTokenData.ts
import { useReadContracts } from 'wagmi';
import { erc20Abi, formatUnits } from 'viem';

export function useTokenData(tokenAddress: `0x${string}`, userAddress?: `0x${string}`) {
  const { data, isLoading } = useReadContracts({
    contracts: [
      { address: tokenAddress, abi: erc20Abi, functionName: 'name' },
      { address: tokenAddress, abi: erc20Abi, functionName: 'symbol' },
      { address: tokenAddress, abi: erc20Abi, functionName: 'decimals' },
      { address: tokenAddress, abi: erc20Abi, functionName: 'totalSupply' },
      ...(userAddress ? [{
        address: tokenAddress,
        abi: erc20Abi,
        functionName: 'balanceOf' as const,
        args: [userAddress] as [`0x${string}`],
      }] : []),
    ],
    query: {
      refetchInterval: 30_000,
      staleTime: 10_000,
    },
  });

  const decimals = (data?.[2].result as number) ?? 18;

  return {
    isLoading,
    name: data?.[0].result as string | undefined,
    symbol: data?.[1].result as string | undefined,
    decimals,
    totalSupply: data?.[3].result
      ? formatUnits(data[3].result as bigint, decimals)
      : undefined,
    userBalance: userAddress && data?.[4]?.result
      ? formatUnits(data[4].result as bigint, decimals)
      : undefined,
  };
}

Запис до контракту

// hooks/useTokenTransfer.ts
import { useWriteContract, useWaitForTransactionReceipt, useSimulateContract } from 'wagmi';
import { erc20Abi, parseUnits } from 'viem';
import { useState } from 'react';

export function useTokenTransfer(tokenAddress: `0x${string}`, decimals: number) {
  const [recipient, setRecipient] = useState<`0x${string}` | undefined>();
  const [amount, setAmount] = useState('');

  const amountWei = amount ? parseUnits(amount, decimals) : 0n;

  // Симуляція до підпису — виявляємо помилки без газу
  const { error: simError } = useSimulateContract({
    address: tokenAddress,
    abi: erc20Abi,
    functionName: 'transfer',
    args: [recipient!, amountWei],
    query: { enabled: !!recipient && amountWei > 0n },
  });

  const { writeContract, data: txHash, isPending } = useWriteContract();
  const { isLoading: isConfirming, isSuccess } = useWaitForTransactionReceipt({ hash: txHash });

  const transfer = () => {
    if (!recipient || amountWei === 0n) return;
    writeContract({
      address: tokenAddress,
      abi: erc20Abi,
      functionName: 'transfer',
      args: [recipient, amountWei],
    });
  };

  return {
    recipient, setRecipient,
    amount, setAmount,
    simError,
    transfer,
    txHash,
    isPending,
    isConfirming,
    isSuccess,
  };
}

Прямий viem-клієнт для серверного використання

wagmi працює лише в React-контексті. Для API routes, server components, cron — прямий viem:

// lib/publicClient.ts
import { createPublicClient, http, createWalletClient } from 'viem';
import { mainnet } from 'viem/chains';
import { privateKeyToAccount } from 'viem/accounts';

export const publicClient = createPublicClient({
  chain: mainnet,
  transport: http(process.env.ETH_RPC_URL!),
});

// Серверний wallet-клієнт (для автоматичних транзакцій, relayer)
export const serverWallet = createWalletClient({
  account: privateKeyToAccount(process.env.RELAYER_PRIVATE_KEY as `0x${string}`),
  chain: mainnet,
  transport: http(process.env.ETH_RPC_URL!),
});

Підписка на події через viem

// Polling для HTTP транспорту
import { watchContractEvent } from 'viem/actions';

const unwatch = watchContractEvent(publicClient, {
  address: contractAddress,
  abi: contractAbi,
  eventName: 'Transfer',
  onLogs: (logs) => {
    for (const log of logs) {
      console.log(log.args);
    }
  },
  poll: true,
  pollingInterval: 4_000,
});

// Для WebSocket транспорту — push, не polling
import { webSocket } from 'viem';
const wsClient = createPublicClient({
  chain: mainnet,
  transport: webSocket(process.env.ETH_WS_URL!),
});

Генерація типів з ABI

# Встановлення wagmi CLI
npm i -D @wagmi/cli

# Автогенерація типізованих hook'ів
npx wagmi generate

Після генерації з'являються hook'и вигляду useReadErc20BalanceOf(...) з повною типізацією аргументів — помилка передачі неправильного типу буде виявлена на етапі компіляції.

Часові рамки: налаштування wagmi/viem у проекті, підключення гаманця, читання та запис до 1–2 контрактів — 1–2 дні. Повний шар взаємодії з типогенерацією, серверними клієнтами та event-підпискою — 2–3 дні.