Wagmi и viem: быстрая интеграция блокчейна в React-сайт

Представьте: ваше React-приложение использует ethers.js для взаимодействия с контрактами. При смене сети пользователем данные не обновляются, а N+1 запросы к RPC душат приложение. Мы сталкивались с этим не раз — hydration mismatch в Next.js, утечки памяти из-за мутабельного состояния ethers.js. Реше

Разработка и обслуживание любых видов сайтов:

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

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Услуги, которые мы предлагаем
Показано 1 из 1Все 2062 услуг
Wagmi и viem: быстрая интеграция блокчейна в React-сайт
Средний
~3-5 дней

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

Часто задаваемые вопросы

Последние работы

  • image_website-b2b-advance_0.webp
    Разработка сайта компании B2B ADVANCE
    1422
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1287
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    984
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1250
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    986
  • image_bitrix-bitrix-24-1c_fixper_448_0.webp
    Разработка веб-сайта для компании ФИКСПЕР
    1000

Представьте: ваше React-приложение использует ethers.js для взаимодействия с контрактами. При смене сети пользователем данные не обновляются, а N+1 запросы к RPC душат приложение. Мы сталкивались с этим не раз — hydration mismatch в Next.js, утечки памяти из-за мутабельного состояния ethers.js. Решение — связка wagmi + viem. Наш опыт в Web3-разработке — более 5 лет, внедрили решение на 10+ проектах DeFi. Переход на wagmi/viem сокращает объём кода в 2-3 раза и уменьшает TTFB на 40%, а также снижает затраты на RPC-запросы благодаря автоматическому кэшированию.

Почему ethers.js тормозит работу?

ethers.js — класс-ориентированная библиотека с мутабельным состоянием. Каждый new Contract(...) создаёт объект, привязанный к провайдеру. В React это проблема: при смене сети или аккаунта весь объект нужно пересоздавать, иначе данные не обновляются. Viem строится на функциях и иммутабельных клиентах. Wagmi управляет жизненным циклом клиентов автоматически — хуки обновляются при любых изменениях в кошельке. По нашей статистике, переход на wagmi/viem сокращает объём кода в 2-3 раза и уменьшает TTFB на 40%.

Сравнение: ethers.js vs wagmi/viem

Критерий ethers.js wagmi + viem
Состояние Мутабельное, объекты живут в контексте Иммутабельное, пересоздание не требуется
React-интеграция Ручная через useState/useEffect Встроенные хуки с кэшированием
Обновление при смене сети Нужно подписаться на события Автоматический refetch через watch
Типизация Слабая, ручные касты Полная типизация через @wagmi/cli
Производительность Выше latency из-за класса Ниже TTFB за счёт ленивых вызовов

Как wagmi/viem решают проблемы ethers.js?

Wagmi-хуки автоматически подписываются на события кошелька (chainChanged, accountsChanged). Это устраняет проблему устаревших данных. Viem-клиенты иммутабельны — каждый вызов возвращает свежий результат. Вместе они обеспечивают реактивность без утечек памяти. Например, useReadContract автоматически перезапрашивает данные при смене сети, а useWatchContractEvent фильтрует события без создания новых подписок при ререндере. Мы используем этот подход в production-проектах, что снижает нагрузку на RPC и улучшает пользовательский опыт. Как отмечается в официальной документации viem, "viem is designed to be modular, tree-shakeable, and type-safe out of the box".

Что даёт типогенерация ABI?

Без типогенерации каждый вызов контракта — это ручное указание ABI и кастинг результатов. Wagmi CLI генерирует типы из ваших ABI-файлов за одну команду. После генерации вы получаете хуки с полной типизацией аргументов и возвращаемых значений. Это исключает ошибки в адресах, аргументах и позволяет редактору подсказывать возможные значения. Проще говоря, вы просто импортируете useReadErc20BalanceOf и передаёте нужный адрес — остальное проверяется на этапе компиляции.

npm i -D @wagmi/cli npx wagmi generate 

Как мы внедряем wagmi/viem: пошагово

Настройка конфигурации

// 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!), }, }); 

Чтение данных из контракта

// 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-клиент

// 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!), }); export const serverWallet = createWalletClient({ account: privateKeyToAccount(process.env.RELAYER_PRIVATE_KEY as `0x${string}`), chain: mainnet, transport: http(process.env.ETH_RPC_URL!), }); 

Типогенерация из ABI

npm i -D @wagmi/cli npx wagmi generate 

После генерации появляются хуки вида useReadErc20BalanceOf(...) с полной типизацией аргументов.

Пример использования в Next.js App Router

В серверных компонентах используйте viem напрямую, так как wagmi работает только на клиенте. Создайте publicClient и используйте его для чтения данных. Например, в serverComponent.tsx импортируйте publicClient из lib/publicClient.ts и вызывайте методы без React-хуков.

Процесс работы

  • Аудит — анализ текущей реализации, выявление ошибок (утечки, необновления данных).
  • Проектирование — проектируем слой взаимодействия: конфиги, хуки, серверные клиенты.
  • Реализация — пишем код с типогенерацией, покрываем крайние случаи.
  • Тестирование — симуляции, тесты сети, ручные проверки с разными кошельками.
  • Деплой — развёртывание, мониторинг, документация.

Что входит в работу

  • Код интеграции wagmi/viem с типизированными хуками.
  • Документация по использованию и доработке.
  • Настройка CI/CD для автообновления ABI.
  • Обучение команды (1–2 сессии).
  • Гарантия совместимости с MetaMask, WalletConnect, Coinbase Wallet.

Сроки

Базовая интеграция (чтение/запись 1–2 контракта) — 1–2 дня. Полный слой с типогенерацией, серверными клиентами и event-подпиской — 2–3 дня. Сроки уточняются после аудита вашего проекта.

Закажите аудит вашей интеграции — мы найдём узкие места за 1 день.

Сравнение подходов: ethers.js vs wagmi/viem

Параметр ethers.js wagmi/viem
Boilerplate Высокий (ручное управление провайдерами) Низкий (автоматическая конфигурация)
Обновление данных Вручную через useEffect Автоматически через watch
Типобезопасность Нет Полная (via @wagmi/cli)
Производительность ~2 мс за вызов <1 мс за вызов (ленивая оценка)
Поддержка server-side Сложно (нужен ethers.providers) Простая (viem createPublicClient)

Свяжитесь с нами — обсудим детали вашего проекта и подберём оптимальную архитектуру.