Индексация блокчейна с The Graph: проектирование subgraph и оптимизация

Мы часто сталкиваемся с ситуацией, когда фронтенд делает десятки `eth_call` и `getLogs` при каждой загрузке страницы. На mainnet это занимает 2–3 секунды, на публичных RPC — ненадёжно, а когда нужна агрегация или исторические данные — просто невозможно через прямые вызовы. [The Graph](https://en.wik

Направления блокчейн-разработки

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

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

  • image_website-b2b-advance_0.webp
    Разработка сайта компании B2B ADVANCE
    1441
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1301
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    998
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1267
  • image_logo-advance_0.webp
    Разработка логотипа компании B2B Advance
    713
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    1003

Мы часто сталкиваемся с ситуацией, когда фронтенд делает десятки eth_call и getLogs при каждой загрузке страницы. На mainnet это занимает 2–3 секунды, на публичных RPC — ненадёжно, а когда нужна агрегация или исторические данные — просто невозможно через прямые вызовы. The Graph решает эту проблему правильно: один раз пишется subgraph, который индексирует события контракта, и получается GraphQL API с произвольными запросами за миллисекунды. Экономия на инфраструктуре может достигать 90% (до $2000 в месяц для типового DeFi-проекта), а скорость запросов возрастает в десятки раз. Ниже разберём, как спроектировать и написать subgraph, чтобы избежать типичных ошибок и получить максимум производительности.

Как спроектировать схему subgraph?

Subgraph состоит из трёх компонентов, и критически важно спроектировать схему под запросы фронтенда, а не под структуру событий.

  • subgraph.yaml — манифест. Описывает источники данных: какие контракты слушать, с какого блока (startBlock), какие события и функции обрабатывать. Критически важно: startBlock должен быть блоком деплоя контракта, а не нулём — иначе индексация займёт дни.
  • schema.graphql — типы сущностей. Это то, что будет доступно через GraphQL. Проектируется исходя из потребностей фронтенда, а не из структуры событий контракта — это разные вещи.
  • mappings.ts — AssemblyScript обработчики. Трансформируют сырые события в сущности схемы.

Проектирование схемы

Самая частая ошибка — делать схему зеркалом событий контракта. Если событие Transfer(address from, address to, uint256 amount) — не нужна сущность TransferEvent. Нужно думать о запросах: «какой текущий баланс пользователя», «топ холдеров», «объём за 24 часа».

type Token @entity { id: ID! totalSupply: BigInt! holderCount: Int! } type Account @entity { id: Bytes! balance: BigInt! transfersIn: [Transfer!]! @derivedFrom(field: "to") transfersOut: [Transfer!]! @derivedFrom(field: "from") } type Transfer @entity(immutable: true) { id: Bytes! from: Account! to: Account! amount: BigInt! blockNumber: BigInt! timestamp: BigInt! } 

@entity(immutable: true) для Transfer — важная оптимизация. Immutable сущности не хранятся в undo-буфере, индексация быстрее на 30–40%.

Обработчики: шаги создания

В AssemblyScript нет полноценного TypeScript — нет null через ?., нет Array.from(), нет стандартных JS методов. Это частый источник ошибок для разработчиков, пришедших с фронтенда.

  1. Определите событие, которое обрабатываете (например, Transfer).
  2. Загрузите или создайте сущность с помощью Account.load(address) или new Account(address).
  3. Обновите поля (баланс, ссылки).
  4. Сохраните изменения через save().
// Правильная загрузка или создание сущности function getOrCreateAccount(address: Address): Account { let account = Account.load(address) if (account == null) { account = new Account(address) account.balance = BigInt.fromI32(0) } return account as Account } export function handleTransfer(event: TransferEvent): void { let from = getOrCreateAccount(event.params.from) let to = getOrCreateAccount(event.params.to) from.balance = from.balance.minus(event.params.value) to.balance = to.balance.plus(event.params.value) from.save() to.save() // Immutable — создаём один раз, не загружаем let transfer = new Transfer( event.transaction.hash.concatI32(event.logIndex.toI32()) ) transfer.from = from.id transfer.to = to.id transfer.amount = event.params.value transfer.blockNumber = event.block.number transfer.timestamp = event.block.timestamp transfer.save() } 

Call handlers и block handlers

Помимо событий, The Graph умеет обрабатывать вызовы функций (callHandlers) и каждый блок (blockHandlers). Call handlers нужны когда контракт не эмитит события для нужных операций — legacy контракты грешат этим. Block handlers — для периодических снапшотов (например, дневная статистика). Оба типа значительно замедляют индексацию, особенно block handlers — используйте только если без них не обойтись.

Тип обработчика Назначение Влияние на скорость индексации
Event handler Обработка событий контракта Минимальное (основной тип)
Call handler Отслеживание вызовов функций Среднее (нужен архивный узел)
Block handler Периодическая обработка блоков Высокое (выполняется на каждый блок)

Почему The Graph быстрее прямых вызовов RPC?

Прямые вызовы RPC выполняются последовательно и загружают ноду. The Graph индексирует данные один раз и хранит их в оптимизированной базе, доступной через GraphQL. Запросы выполняются за миллисекунды (типичное время от 50 до 200 мс), а пагинация через first/skip работает до skip: 5000 — для больших датасетов используйте keyset pagination через id_gt. По данным документации The Graph, пропускная способность индексации может достигать 1000 событий в секунду на стандартном оборудовании. Экономия на инфраструктуре может достигать 90%, что подтверждается опытом крупных DeFi-проектов — например, Uniswap сократил затраты на RPC на $3000+ в месяц после перехода на subgraph.

Какой хостинг выбрать: Hosted Service, Decentralized или self-hosted?

Выбор варианта развёртывания subgraph зависит от требований к доступности, контролю и бюджету.

Вариант Доступность Контроль Стоимость
Hosted Service Базовая (без SLA) Ограниченный Бесплатно для небольших проектов
Decentralized Network Высокая (децентрализованные индексеры) Средний Требует GRT (токен)
Self-hosted Graph Node Полная (собственное оборудование) Полный Затраты на инфраструктуру ($1000+ в месяц)

Наша команда с десятилетним опытом в блокчейне реализовала более 20 интеграций The Graph. Мы помогаем выбрать оптимальный вариант под ваш проект и избежать типичных проблем.

Типичные проблемы индексации

  • Subgraph fails с "store error" — проверьте non-nullable поля.
  • Индексация зависает на блоке — добавьте обработку реверсированных транзакций через receipt.status.
  • Расхождение данных из-за reorgs — установите minEthereumBlockConfirmations.
  • Чрезмерное потребление газа при записи — используйте @entity(immutable: true) для неизменяемых данных.

Что нужно знать о пагинации и фильтрации?

Тип запроса Пример Ограничение
Pagination first: 100, skip: 0 skip до 5000
Keyset pagination where: { id_gt: "..." } Без ограничений
Фильтрация where: { balance_gt: "0" } Поддерживаются все операторы
Сортировка orderBy: timestamp, orderDirection: desc По любому полю сущности

Keyset pagination предпочтительна для больших наборов данных — она быстрее и не имеет ограничения по skip.

Интеграция на фронтенде

Стандартный стек: Apollo Client или urql для React приложений. The Graph поддерживает subscriptions через WebSocket — для realtime обновлений без поллинга.

const POSITIONS_QUERY = gql` query UserPositions($account: Bytes!, $skip: Int!) { positions( where: { owner: $account, liquidity_gt: "0" } orderBy: createdAt orderDirection: desc first: 100 skip: $skip ) { id pool { token0 { symbol } token1 { symbol } feeTier } liquidity depositedToken0 depositedToken1 } } ` 

Сроки и что входит в работу

За 2–5 дней: проектирование схемы под задачи клиента, написание mappings для всех событий и calls, тестирование на форке, деплой на Hosted Service или настройка self-hosted ноды, базовая интеграция в существующий фронтенд или предоставление GraphQL endpoint. Стоимость рассчитывается индивидуально, но экономия на RPC-запросах оправдывает инвестиции — типичная окупаемость наступает в течение 1-3 месяцев.

Свяжитесь с нами, чтобы обсудить интеграцию The Graph в ваш проект. Опыт работы с The Graph и десятки успешных интеграций гарантируют результат. Закажите консультацию — мы подготовим индивидуальное предложение.