Интеграция фронтенда с Wagmi
Wagmi v2 — де-факто стандарт для React + EVM. Переход с Wagmi v1 к v2 сломал много API (переход на Viem вместо ethers.js, изменение hooks), поэтому если проект написан под старую версию — интеграция включает и миграцию. Если с нуля — ставим v2 сразу.
Настройка и конфигурация
// config.ts
import { createConfig, http } from 'wagmi';
import { mainnet, polygon, arbitrum, base } from 'wagmi/chains';
import { injected, coinbaseWallet, walletConnect } from 'wagmi/connectors';
export const config = createConfig({
chains: [mainnet, polygon, arbitrum, base],
transports: {
[mainnet.id]: http('https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY'),
[polygon.id]: http('https://polygon-mainnet.g.alchemy.com/v2/YOUR_KEY'),
[arbitrum.id]: http('https://arb-mainnet.g.alchemy.com/v2/YOUR_KEY'),
[base.id]: http('https://base-mainnet.g.alchemy.com/v2/YOUR_KEY'),
},
connectors: [
injected(),
coinbaseWallet({ appName: 'AppName' }),
walletConnect({ projectId: process.env.VITE_WC_PROJECT_ID! }),
],
});
WagmiProvider оборачивает приложение; QueryClientProvider (TanStack Query) — обязателен, Wagmi использует его для кэширования.
Основные паттерны
Чтение данных
useReadContract для одного вызова, useReadContracts для батча через Multicall3:
const { data: balance } = useReadContract({
address: TOKEN_ADDRESS,
abi: erc20Abi,
functionName: 'balanceOf',
args: [address],
query: { enabled: !!address },
});
query.enabled — критичная опция: без неё хук пытается читать до того, как address определён. staleTime и gcTime контролируют как часто данные перечитываются — для балансов разумно 30 секунд, для медленно меняющихся параметров контракта — 5 минут.
Запись (транзакции)
const { writeContractAsync } = useWriteContract();
const { isLoading: isConfirming } = useWaitForTransactionReceipt({ hash });
const handleStake = async () => {
const hash = await writeContractAsync({
address: STAKING_ADDRESS,
abi: stakingAbi,
functionName: 'stake',
args: [parseEther(amount)],
});
// hash получен — транзакция отправлена, ждём подтверждения
};
Подписи
Для SIWE и permit-подписей — useSignMessage и useSignTypedData:
const { signTypedDataAsync } = useSignTypedData();
// EIP-712 типизированные данные для permit
const signature = await signTypedDataAsync({
domain, types, primaryType: 'Permit', message: permitMessage,
});
Типичные проблемы при интеграции
SSR/гидрация: Next.js App Router + Wagmi требуют 'use client' на компонентах с хуками и аккуратного разделения серверной и клиентской логики. useAccount() на сервере всегда возвращает disconnected — это нормально.
ABI типизация: Wagmi + Viem генерируют типы из ABI через as const. Без as const теряется type inference для аргументов и возвращаемых значений.
ENS resolution: useEnsName и useEnsAddress работают только на mainnet. Для других сетей — явно передавать chainId: mainnet.id.
Stale данные после транзакции: после успешной транзакции нужно инвалидировать кэш:
const queryClient = useQueryClient();
// после успешного writeContract:
queryClient.invalidateQueries({ queryKey: ['readContract', ...] });
Или использовать useWaitForTransactionReceipt с onSuccess callback для автоматической инвалидации.
Ориентиры по срокам
Настройка с нуля (мультичейн, wallet UI, базовые read/write хуки): 1 день. Интеграция существующего React-приложения с несколькими смарт-контрактами и migrate с v1: 2-3 дня.







