Розробка користувацького індексатора блокчейну

Проєктуємо та розробляємо блокчейн-рішення повного циклу: від архітектури смарт-контрактів до запуску DeFi-протоколів, NFT-маркетплейсів та криптобірж. Аудит безпеки, токеноміка, інтеграція з наявною інфраструктурою.
Показано 1 з 1Усі 1306 послуг
Розробка користувацького індексатора блокчейну
Складний
~1-2 тижні
Часті запитання

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

Етапи блокчейн-розробки

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

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1306
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1218
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    920
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1147
  • image_logo-advance_0.webp
    Розробка логотипу компанії B2B Advance
    610
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    885

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

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

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

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

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

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

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

Компоненти системи

Шар ингестии даних (Data Ingestion Layer)

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

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

WebSocket subscriptionseth_subscribe("newHeads") та eth_subscribe("logs", filter). Задержка близька до часу блоку. Проблема: при реконнекті можна пропустити блоки, потрібна логіка 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 eventos — немає topic[0], matching тільки по адресі
  • Proxy контракти — ABI implementation, а не proxy. Потрібно резолвити через implementation() slot (EIP-1967: slot 0x360894...)
  • Upgrade события — після апгрейду ABI змінюється, потрібна версіонованість

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

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

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

-- Обнаруження реорга
SELECT block_hash, block_number 
FROM processed_blocks 
WHERE block_number = $1 AND block_hash != $2;
-- Якщо є рядки — це реорг

При обнаруженні реорга:

  1. Знайти точку розходження (спільний предок)
  2. Відкатити всі записи від точки розходження до поточного блоку (DELETE WHERE block_number >= fork_point)
  3. Переобробити канонічні блоки

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

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;

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

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

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

Для більшості 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.

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

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

Паралельна обробка блоків. Блоки незалежні якщо немає 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 (ethers-rs/alloy)
Черга Redis Streams, Apache Kafka (при > 10k eventos/сек)
База даних 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, або sharding.
# Приклад 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 тижнів.