Розробка кастомного індексатора блокчейну
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"
Як ми розробляємо кастомний індексатор?
- Проектування (3-5 днів). Визначення джерел даних, схеми БД, вимог до реалтайму. Вибір між кастомною розробкою та розширенням існуючих рішень (Ponder, Substreams).
- Розробка ядра (5-10 днів). Інгестія + декодування + обробка реорганізацій + зберігання. Це критичний шлях, тестується на історичних даних.
- API та інтеграції (3-5 днів). GraphQL/REST схема, subscriptions, документація.
- Навантажувальне тестування та оптимізація (2-3 дні). Синхронізація з genesis, навантажувальне тестування API, налаштування пулів з'єднань, індексів.
- Деплой та моніторинг (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% за рахунок готових архітектурних шаблонів. Замовте розробку під ключ — зв'яжіться, щоб оцінити ваш проект.







