Оптимизация производительности dApp

Проектируем и разрабатываем блокчейн-решения полного цикла: от архитектуры смарт-контрактов до запуска DeFi-протоколов, NFT-маркетплейсов и криптобирж. Аудит безопасности, токеномика, интеграция с существующей инфраструктурой.
Показано 1 из 1 услугВсе 1306 услуг
Оптимизация производительности dApp
Средняя
~3-5 рабочих дней
Часто задаваемые вопросы
Направления блокчейн-разработки
Этапы блокчейн-разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1221
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1163
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    855
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1056
  • image_logo-advance_0.png
    Разработка логотипа компании B2B Advance
    561
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    828

Оптимизация производительности dApp

Большинство dApp медленные по одной причине: каждый компонент делает свой RPC вызов, и они не координируются. Страница с 10 компонентами — 10 параллельных запросов к Infura, каждый с latency 100–300ms. Пользователь видит постепенно появляющийся UI с loading state везде. Исправление этого — главная задача оптимизации.

RPC оптимизация

Multicall и батчинг

multicall3 — контракт, задеплоенный на большинстве EVM сетей по адресу 0xcA11bde05977b3631167028862bE2a173976CA11. Позволяет выполнить N view вызовов в одном RPC запросе:

import { useReadContracts } from 'wagmi'

// Вместо 3 отдельных useReadContract:
const { data } = useReadContracts({
  contracts: [
    { address: tokenA, abi: erc20Abi, functionName: 'balanceOf', args: [userAddress] },
    { address: tokenB, abi: erc20Abi, functionName: 'balanceOf', args: [userAddress] },
    { address: tokenC, abi: erc20Abi, functionName: 'balanceOf', args: [userAddress] },
  ],
})

wagmi v2 автоматически батчит вызовы через multicall3 если включена опция batch: { multicall: true } в transport конфиге. Проверьте что она включена — по умолчанию да, но иногда отключают при дебаггинге и забывают вернуть.

Кэширование с TanStack Query

wagmi построен поверх TanStack Query, и staleTime / gcTime напрямую влияют на количество RPC запросов:

const config = createConfig({
  // ...
  // Настройка QueryClient:
})

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 12_000,    // данные свежие 12 секунд — нет повторного запроса
      gcTime: 5 * 60_000,  // кэш живёт 5 минут
      retry: 2,
      retryDelay: attemptIndex => Math.min(1000 * 2 ** attemptIndex, 30_000),
    },
  },
})

Для balance данных staleTime: 12_000 разумен — новый блок каждые ~12 секунд. Для статичных данных (token metadata, contract addresses) — staleTime: Infinity.

WebSocket вместо polling

Для real-time данных (цены, события) watchContractEvent из wagmi использует eth_subscribe через WebSocket. Это кардинально лучше polling каждые N секунд:

useWatchContractEvent({
  address: poolAddress,
  abi: poolAbi,
  eventName: 'Swap',
  onLogs: (logs) => {
    // обновляем цену при каждом свапе
    updatePrice(logs)
  },
})

Требует WebSocket-совместимого RPC провайдера — Alchemy, QuickNode, Infura (WSS endpoint).

Bundle size и загрузка

Tree shaking viem vs ethers.js

viem спроектирован с tree shaking в уме — импортируешь только то, что используешь. ethers v5 при полном импорте добавляет ~200KB gzipped. viem при типичном использовании — ~40–60KB.

Проверяем bundle analyzer:

ANALYZE=true next build
# или
npx vite-bundle-analyzer

Типичные находки: случайный импорт всего ethers вместо конкретных функций, дублирование bigint polyfills, несколько версий одного пакета в node_modules.

Code splitting по маршрутам

Компоненты работы с кошельком — под dynamic import:

const WalletModal = dynamic(() => import('@/components/WalletModal'), {
  ssr: false, // обязательно для Web3 компонентов
  loading: () => <Skeleton className="h-10 w-32" />,
})

ssr: false критично — wallet adapter и wagmi обращаются к window.ethereum при инициализации.

React rendering оптимизация

Изоляция re-renders

Главный паттерн: не читать весь account объект там, где нужен только адрес.

// Плохо — компонент перерендерится при любом изменении аккаунта:
const { address, isConnected, chain, connector } = useAccount()

// Хорошо — перерендер только при изменении address:
const { address } = useAccount({ select: (a) => a.address })

select в wagmi хуках работает как selector в Redux — мемоизирует производное значение.

Memoization тяжёлых вычислений

// Форматирование большого списка позиций
const formattedPositions = useMemo(() =>
  rawPositions.map(pos => ({
    ...pos,
    valueUsd: formatUnits(pos.amount * pos.price, 18),
    pnlPercent: ((pos.currentPrice - pos.entryPrice) / pos.entryPrice * 100).toFixed(2),
  })),
  [rawPositions] // только при изменении данных
)

Виртуализация длинных списков

Таблица с 1000+ транзакций без виртуализации — гарантированный лаг. @tanstack/react-virtual или react-window:

import { useVirtualizer } from '@tanstack/react-virtual'

const rowVirtualizer = useVirtualizer({
  count: transactions.length,
  getScrollElement: () => parentRef.current,
  estimateSize: () => 56, // высота строки
  overscan: 5,
})

Метрики и профилирование

Перед оптимизацией — измерения. React DevTools Profiler показывает какие компоненты рендерятся и сколько. Chrome DevTools Network tab — сколько RPC запросов и их timing. Lighthouse для общих метрик загрузки (LCP, TTI).

Типичные результаты после оптимизации: количество RPC запросов уменьшается в 5–10x через multicall батчинг, TTI снижается на 30–50% через code splitting, re-render бюджет уменьшается в 2–3x через правильные selectors.

Работа занимает 3–5 дней: аудит текущих узких мест, настройка multicall, TanStack Query параметров, bundle анализ и устранение лишних зависимостей, профилирование React rendering.