Представьте: ваш DeFi-дашборд загружается 5 секунд из-за 20 отдельных RPC-запросов. Каждый запрос к блокчейну — это задержка. Мы решаем это через multicall, объединяя все вызовы в один. Итог — загрузка падает до 0.5 секунды. Это типичная задача, которую мы решаем каждый день для клиентов. За 5 лет работы над 50+ dApp мы выработали подход, который гарантирует быструю загрузку при сложной Web3-логике. В этой статье — конкретные решения по интеграции, оптимизации и разделению компонентов.
Next.js для Web3 — это прежде всего решение конкретного противоречия: блокчейн данные требуют client-side execution (wallet connection, подпись транзакций), а SEO и начальная загрузка требуют серверного рендеринга. Неправильная граница между server и client компонентами ломает либо wallet integration, либо производительность. Next.js документация рекомендует использовать Server Components для данных, не требующих интерактивности.
Как правильно разделить Server и Client компоненты в dApp?
Ошибка на этом этапе приводит к hydration mismatch или падению приложения. Рассмотрим решение.
Проблема гидрации с wagmi/viem
wagmi 2.x использует localStorage и window.ethereum — оба объекта недоступны на сервере. Наивный импорт useAccount в Server Component вызывает ошибку. Ещё хуже — hydration mismatch: сервер рендерит «не подключён», клиент после гидрации показывает «подключён к MetaMask», и React выдаёт warning или ломает UI.
Правильная структура:
// app/providers.tsx — CLIENT компонент, оборачивает всё приложение
'use client';
import { WagmiProvider, createConfig, http } from 'wagmi';
import { mainnet, base, arbitrum } from 'wagmi/chains';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ConnectKitProvider } from 'connectkit';
const config = createConfig({
chains: [mainnet, base, arbitrum],
transports: {
[mainnet.id]: http(process.env.NEXT_PUBLIC_RPC_MAINNET),
[base.id]: http(process.env.NEXT_PUBLIC_RPC_BASE),
[arbitrum.id]: http(process.env.NEXT_PUBLIC_RPC_ARBITRUM),
},
});
const queryClient = new QueryClient();
export function Providers({ children }: { children: React.ReactNode }) {
return (
<WagmiProvider config={config}>
<QueryClientProvider client={queryClient}>
<ConnectKitProvider>{children}</ConnectKitProvider>
</QueryClientProvider>
</WagmiProvider>
);
}
// app/layout.tsx — SERVER компонент, импортирует Providers
import { Providers } from './providers';
export default function RootLayout({ children }) {
return (
<html>
<body>
<Providers>{children}</Providers>
</body>
</html>
);
}
Статичный контент (navbar, footer, landing text) — Server Components. Кошелёк, балансы, транзакционные кнопки — Client Components с 'use client'.
SSR для on-chain данных
Публичные данные блокчейна (TVL протокола, список токенов, цены) можно загружать на сервере. Server Components в Next.js (App Router) + fetch с caching:
// app/protocol/page.tsx — Server Component
async function getProtocolStats() {
const client = createPublicClient({
chain: mainnet,
transport: http(process.env.RPC_URL), // приватная переменная, не NEXT_PUBLIC_
});
const [tvl, totalUsers] = await Promise.all([
client.readContract({ address: PROTOCOL, abi, functionName: 'getTVL' }),
client.readContract({ address: PROTOCOL, abi, functionName: 'userCount' }),
]);
return { tvl, totalUsers };
}
export default async function ProtocolPage() {
const stats = await getProtocolStats(); // выполняется на сервере
return <StatsDisplay stats={stats} />;
}
fetchв Next.js кэшируется по умолчанию. Для on-chain данных через viem нужно явное управление: revalidate: 60 в export const revalidate или ручная инвалидация через Route Handlers.
Почему multicall ускоряет загрузку в 10 раз?
Управление состоянием транзакций
Lifecycle транзакции в UI: idle → preparing → signing → pending → confirming → success/error. Каждое состояние требует отдельного UI feedback. wagmi предоставляет хуки для каждого этапа:
function TransactionButton({ tokenId }: { tokenId: bigint }) {
const { writeContract, data: hash, isPending, error } = useWriteContract();
const { isLoading: isConfirming, isSuccess } = useWaitForTransactionReceipt({ hash });
if (isPending) return <Button disabled>Подтвердите в кошельке...</Button>;
if (isConfirming) return <Button disabled>Ожидание подтверждения ({hash?.slice(0, 8)}...)</Button>;
if (isSuccess) return <Button variant="success">Готово ✓</Button>;
return (
<Button
onClick={() => writeContract({ address: CONTRACT, abi, functionName: 'mint', args: [tokenId] })}
>
Минт
</Button>
);
}
Оптимистичные обновления
Для операций с предсказуемым результатом (like, follow, simple toggle) — оптимистичный UI через @tanstack/react-query useMutation с onMutate/onError/onSettled. Пользователь видит изменение немедленно, откат происходит только при ошибке.
Multicall и batch запросы
Для dashboard с множеством on-chain данных — никаких параллельных одиночных RPC вызовов. Multicall3 собирает все запросы в один:
const results = await publicClient.multicall({
contracts: tokenIds.map(id => ({
address: NFT_CONTRACT,
abi: erc721Abi,
functionName: 'tokenURI',
args: [id],
})),
});
viem поддерживает multicall нативно. Для 100 токенов — 1 RPC запрос вместо 100. Это разница между 2 секундами и 200 миллисекундами загрузки dashboard. Server Components для статичных данных рендерятся в 3 раза быстрее Client Components, так как не включают JavaScript бандл для гидрации.
Алгоритм разработки dApp: от архитектуры до деплоя
- Анализ смарт-контракта и дизайн-макетов, определение точек интеграции.
- Проектирование компонентной архитектуры: Server vs Client, layout, роутинг.
- Реализация провайдера Web3 (wagmi + ConnectKit/RainbowKit).
- Написание серверных компонент для публичных on-chain данных с кэшированием.
- Реализация клиентских компонент: подключение кошелька, балансы, транзакции.
- Оптимизация запросов через multicall и React Query.
- Тестирование: unit (Vitest), e2e (Playwright), покрытие edge-кейсов транзакций.
- Деплой на Vercel или Docker, настройка CI/CD.
Важные детали окружения
.env.local для локальной разработки, .env.production для прода. Переменные без NEXT_PUBLIC_ не попадают в клиентский бандл — RPC URLs с API ключами должны быть без префикса (использовать только в Server Components или Route Handlers).
RPC провайдеры: Alchemy, Infura, QuickNode. Для продакшена — несколько провайдеров с fallback через wagmi fallback transport. Публичные RPC (вроде https://eth.llamarpc.com) имеют rate limits — не использовать в production без fallback. Средняя экономия на RPC-запросах существенна благодаря multicall и кэшированию.
Сравнение: Server Components vs Client Components для типичных блоков dApp
| Блок | Рекомендуемый тип | Причина |
|---|---|---|
| Navbar, Footer, SEO-текст | Server Component | Нет зависимости от кошелька, быстрая загрузка |
| Кнопка подключения кошелька | Client Component | Требует window.ethereum |
| Баланс токенов | Client Component | Динамические данные, подписка |
| TVL протокола, список токенов | Server Component (с кэшем) | Публичные данные, улучшает SEO |
| Форма транзакции | Client Component | Взаимодействие с кошельком |
Что входит в работу
- Архитектура: выбор стратегии рендеринга (SSR/SSG/CSR), разбивка на Server/Client Components.
- Интеграция кошельков: MetaMask, WalletConnect, Coinbase Wallet, Ledger.
- Работа со смарт-контрактами: чтение и запись через viem, обработка lifecycle транзакций.
- Оптимизация: multicall, кэширование, gas estimation, fallback RPC.
- Тестирование: unit (Vitest), e2e (Playwright), покрытие edge-кейсов.
- Документация: README, комментарии в коде, схема компонентов.
- Поддержка: настройка CI/CD (Vercel/Docker), мониторинг через Tenderly.
Ориентиры по срокам
| Этап | Длительность |
|---|---|
| Базовый фронтенд (wallet + транзакции) | от 1 недели |
| + SSR + оптимизация + полный lifecycle | от 2 недель |
| + Кастомные UI-компоненты, анимации | от 3 недель |
Сроки уточняются после анализа вашего смарт-контракта и дизайн-макетов. Гарантируем прозрачность на каждом этапе. Свяжитесь с нами для консультации — обсудим ваш проект и предложим оптимальное решение.
Стек
Next.js (App Router), wagmi 2.x, viem 2.x, ConnectKit или RainbowKit, @tanstack/react-query 5.x, TypeScript, Tailwind CSS. Тестирование: Vitest + Playwright для e2e. Деплой: Vercel или Docker.
Закажите разработку прямо сейчас — получите консультацию по вашему проекту. Оценим объём и сложность, предложим оптимальное решение.







