Розробка кастомного індексатора блокчейну під ключ

Розробка кастомного індексатора блокчейну The Graph вирішує 80% задач індексації. Решта 20% — це коли потрібна складна агрегація даних на льоту, крос-чейн індексація з об'єднанням стану, доступ до даних, які не потрапили в події (storage slots, trace calls), субсекундна латентність або повний кон

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

Часті запитання

Останні роботи

  • 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

Розробка кастомного індексатора блокчейну

The Graph вирішує 80% задач індексації. Решта 20% — це коли потрібна складна агрегація даних на льоту, крос-чейн індексація з об'єднанням стану, доступ до даних, які не потрапили в події (storage slots, trace calls), субсекундна латентність або повний контроль над інфраструктурою без vendor lock-in. Саме для цих випадків будується кастомний індексатор блокчейну. Якщо ваш DeFi-протокол обробляє тисячі транзакцій на годину і потребує миттєвої консистентності — типові рішення не справляються. Реорганізації глибиною 1–2 блоки трапляються щодня на Ethereum mainnet, і без їх коректної обробки баланси в UI будуть невірними.

Ми — команда блокчейн-інженерів з досвідом у розробці смарт-контрактів та індексаторів. За цей час реалізували понад 10 кастомних індексаторів для DeFi-протоколів. Гарантуємо продуктивність та відмовостійкість. Отримайте консультацію — обговоримо деталі вашого проекту.

Сценарії для кастомного індексатора блокчейну

Стандартні інструменти на зразок The Graph не справляються, коли потрібно:

  • складна агрегація з об'єднанням даних з кількох контрактів та ланцюжків;
  • доступ до storage slots або внутрішніх транзакцій (traces);
  • субсекундна латентність для торгових ботів або DeFi-протоколів;
  • повний контроль над інфраструктурою та відсутність vendor lock-in.

У цих випадках кастомний індексатор блокчейну — єдине рішення.

Архітектурні рішення перед початком

Перш ніж писати код, потрібно відповісти на три питання:

Джерело даних. Logs/events — найдешевший спосіб, але лише те, що контракт явно емітує. Traces (internal transactions) — потрібен archive node з trace_ namespace або Erigon з --tracing. Storage proofs — для стану, який ніколи не емітувався в події. Вибір джерела визначає вимоги до ноди та складність парсингу.

Модель консистентності. Чи потрібна точна консистентність (обробка реорганізацій) чи eventual достатньо? Для фінансових даних реорганізація — не рідкість. На Ethereum mainnet реорги глибиною 1-2 блоки трапляються кілька разів на день. Глибина фінальності для safe confirmation — 12-15 блоків на PoS Ethereum.

Вимоги до латентності. Реалтайм (< 1 сек від блоку) — потрібен WebSocket + streaming. Аналітика (хвилини/години) — batch processing достатній.

Як влаштований кастомний індексатор?

Інгестія даних (Data Ingestion Layer)

Три патерни отримання даних з ноди:

JSON-RPC polling — найпростіший варіант. eth_getLogs з фільтром по address і topics, eth_getBlockByNumber. Затримка = polling interval (зазвичай 500мс-2сек). Проблема: при високому RPS нода починає throttle.

WebSocket subscriptions — eth_subscribe("newHeads") і eth_subscribe("logs", filter). Latency близька до часу блоку. Проблема: при реконнекті можна пропустити блоки, потрібна логіка catch-up.

Direct P2P — підключення до Ethereum P2P мережі через devp2p/libp2p, отримання блоків безпосередньо без RPC. Мінімальна латентність, але висока складність реалізації. Практично тільки якщо індексатор фізично близько до валідаторів.

Для production рекомендую WebSocket + catch-up механізм:

async def subscribe_blocks(ws_url: str, from_block: int): # Спочатку доганяємо до поточного блоку current = await rpc.eth_block_number() for block_num in range(from_block, current): block = await rpc.eth_get_block_by_number(block_num) await process_block(block) # Потім підписуємося на нові async with websockets.connect(ws_url) as ws: await ws.send(json.dumps({ "method": "eth_subscribe", "params": ["newHeads"] })) async for message in ws: head = json.loads(message) await process_new_head(head) 

Декодування ABI

Raw log — це масив topics (bytes32) і data (bytes). Декодування через ABI:

import { decodeEventLog, parseAbi } from 'viem'; const abi = parseAbi([ 'event Transfer(address indexed from, address indexed to, uint256 value)' ]); const decoded = decodeEventLog({ abi, data: log.data, topics: log.topics, }); // decoded.args.from, decoded.args.to, decoded.args.value 

Indexed параметри кодуються в topics (topic[0] = keccak256 сигнатури, topic[1..3] = indexed args). Non-indexed — в data через ABI encoding.

Проблеми при декодуванні:

  • Anonymous events — нема topic[0], matching тільки по address
  • Proxy контракти — ABI implementation, а не proxy. Потрібно резолвити через implementation() slot (EIP-1967)
  • Upgrade події — після апгрейду ABI змінюється, потрібна версіонованість

Обробка реорганізацій

Це найнеприємніша частина кастомного індексатора. Реорг означає, що блоки, які ви вже обробили, більше не канонічні.

Патерн: кожен запис у базі містить block_hash і block_number. При обробці нового блоку перевіряємо батька:

-- Виявлення реоргу SELECT block_hash, block_number FROM processed_blocks WHERE block_number = $1 AND block_hash != $2; -- Атомарна обробка блоку BEGIN; DELETE FROM events WHERE block_hash = $orphaned_hash; DELETE FROM processed_blocks WHERE block_hash = $orphaned_hash; INSERT INTO processed_blocks (block_number, block_hash, ...) VALUES (...); INSERT INTO events (...) VALUES (...); COMMIT; 

Для цього потрібна атомарна обробка блоку — всі зміни від одного блоку застосовуються в одній транзакції БД з block_hash як ідентифікатором.

Шар зберігання

Вибір БД визначається патернами запитів:

Сценарій Технологія Причина
Time-series дані (ціни, об'єми) TimescaleDB Гіпертаблиці, автокомпресія, continuous aggregates
Граф-запити (зв'язки між акаунтами) PostgreSQL + ltree або Neo4j Рекурсивні запити або граф-нативна БД
Повнотекстовий пошук по метаданих NFT PostgreSQL + GIN index jsonb + GIN індекси для JSON-полів
OLAP аналітика ClickHouse Колонкове зберігання, векторизоване виконання
Кеш актуального стану Redis Hash-структури для балансів, pub/sub для стримінгу

Для більшості DeFi індексаторів: PostgreSQL для основних даних + Redis для hot cache.

API шар

GraphQL через Hasura (автогенерація з PostgreSQL схеми) або вручну через Apollo Server. REST для простих випадків.

Критична оптимізація: DataLoader для батчінгу запитів. Якщо GraphQL-запит просить transfers { from { balance } } — без DataLoader отримаємо N+1 запитів до БД. DataLoader групує запити за один tick event loop.

Subscriptions для реалтайм даних: PostgreSQL LISTEN/NOTIFY → WebSocket → GraphQL subscription.

Чому кастомний індексатор швидше The Graph?

The Graph використовує WASM-обробку в Subgraphs, що дає затримки при підкачуванні станів. Кастомний індексатор працює напряму з нодою через WebSocket, обробляє блоки пулом воркерів і пише дані батчами — це знижує latency до субсекунд і збільшує throughput в 10-50x. Крім того, ви контролюєте індекси та схеми БД, що дозволяє оптимізувати запити під конкретні дані.

Продуктивність та масштабування

Вузькі місця в порядку частоти зустрічальності:

Паралельна обробка блоків. Блоки незалежні, якщо немає cross-block стану (зазвичай немає). Worker pool по N потоків, кожен обробляє свій діапазон блоків. Обережно: порядок запису в БД має бути детермінованим.

Batch insert. Не INSERT кожну подію окремо. PostgreSQL COPY або INSERT...VALUES — різниця в 10-50x по throughput.

# Погано: N окремих INSERT for event in events: await db.execute("INSERT INTO events VALUES ($1, $2, ...)", event) # Добре: один batch INSERT await db.executemany( "INSERT INTO events VALUES ($1, $2, ...)", [(e.block, e.tx_hash, ...) for e in events] ) 

Індекси vs insert speed. Кожен індекс уповільнює INSERT. Для історичної синхронізації: створити таблицю без індексів, завантажити дані, потім CREATE INDEX CONCURRENTLY. Прискорення в 3-10x порівняно з індексами під час завантаження.

Технологічний стек

Компонент Варіанти
Мова інгестії TypeScript/Node.js (viem/ethers), Python (web3.py), Rust (alloy)
Черга Redis Streams, Apache Kafka (при > 10k подій/сек)
База даних PostgreSQL 16 + TimescaleDB
API Hasura (швидкий старт) або custom GraphQL
Моніторинг Prometheus + Grafana, alerting по lag метриці
Деплой Docker Compose (dev), Kubernetes (prod)

Rust (alloy crate) дає найкращу продуктивність для високонавантажених індексаторів: парсинг ABI, десеріалізація блоків, робота з bytes — все це швидше ніж в Node.js в 5-20x.

Моніторинг та операційна робота

Ключові метрики:

  • Indexer lag — різниця між latest_block в БД та eth_blockNumber. Алерт при > 10 блоків.
  • Reorg count — кількість реорганізацій за період. Різке зростання = проблеми з нодою або RPC.
  • Events per block — аномалії вказують на нестандартну активність або баги в парсингу.
  • DB write latency — деградація означає потребу у vacuum, bloat, або шардуванні.
# Приклад Prometheus alert - alert: IndexerLagHigh expr: eth_latest_block - indexer_processed_block > 50 for: 2m annotations: summary: "Indexer is falling behind by {{ $value }} blocks" 

Як ми розробляємо кастомний індексатор?

  1. Проектування (3-5 днів). Визначення джерел даних, схеми БД, вимог до реалтайму. Вибір між кастомною розробкою та розширенням існуючих рішень (Ponder, Substreams).
  2. Розробка ядра (5-10 днів). Інгестія + декодування + обробка реорганізацій + зберігання. Це критичний шлях, тестується на історичних даних.
  3. API та інтеграції (3-5 днів). GraphQL/REST схема, subscriptions, документація.
  4. Навантажувальне тестування та оптимізація (2-3 дні). Синхронізація з genesis, навантажувальне тестування API, налаштування пулів з'єднань, індексів.
  5. Деплой та моніторинг (1-2 дні). Docker Compose / Kubernetes, налаштування алертів, runbook для чергових.

Разом: 1-2 тижні для індексатора одного протоколу на одній мережі. Мультичейн з агрегацією — ближче до 3-4 тижнів. Зв'яжіться з нами для точної оцінки термінів вашого проекту.

Як забезпечити нульову втрату подій при реорганізаціях?

Ключ — атомарність запису з block_hash. Якщо реорг виявлено, всі дані від точки розходження відкочуються в одній транзакції і потім перезаписуються канонічними блоками. Додатково ми використовуємо confirmations: чекаємо 12-15 блоків перед записом в основний шар. Для реалтайм-даних застосовується eventual consistency з гарантією, що фінальний запис завжди коректний.

Що входить в розробку

  • Архітектурна документація
  • Вихідний код індексатора (з коментарями)
  • GraphQL/REST API з документацією (Swagger/GraphiQL)
  • Налаштування моніторингу (Prometheus + Grafana, дашборди)
  • Інструкція з розгортання (Docker Compose/Kubernetes)
  • 1 місяць підтримки після запуску (виправлення багів, консультації)

Вартість розробки розраховується індивідуально після оцінки проекту. Економія на інфраструктурі при використанні нашого рішення може досягати 40% за рахунок оптимізації запитів та кешування. Скорочення часу на розробку порівняно з самостійною реалізацією — до 50% за рахунок готових архітектурних шаблонів. Замовте розробку під ключ — зв'яжіться, щоб оцінити ваш проект.