Розробка системи індексації блокчейн-подій
При типових запитах — «покажи всі транзакції користувача за 30 днів», «обсяг торгів DEX» — eth_getLogs повільний. На архівній ноді з блоку 0 запит займе хвилини; на публічному RPC — впаде з timeout або block range too large. Ми проектуємо відмовостійкий pipeline індексації подій з exactly-once семантикою та моніторингом у реальному часі. Економія ресурсів: до 80% часу на пошукових запитах (з 5 секунд до 1 секунди, тобто у 5 разів швидше), зниження витрат на RPC у 10 разів (наприклад, з $1,000 до $100 на місяць). Реальний кейс — клієнт зекономив $30,000 на рік на інфраструктурі. Наша команда має 8+ років досвіду в блокчейн-розробці та більше 50 реалізованих проектів з індексації. Гарантуємо консистентність даних навіть при глибоких реорганізаціях. Орієнтовна вартість базової індексації одного контракту — від $5,000, складного pipeline — від $15,000.
Який підхід до отримання подій обрати?
Є три способи отримувати події з ноди, кожен зі своїми компромісами:
| Підхід | Latency | Складність | Надійність | Приблизна пропускна здатність |
|---|---|---|---|---|
Polling (eth_getLogs) |
Середня (1-5 сек) | Низька | Висока (exactly-once) | До 1000 блоків за запит |
| WebSocket subscriptions | Низька (< 1 сек) | Середня | Середня (залежить від мережі) | Миттєва доставка |
| Firehose / StreamingFast | Низька | Висока | Дуже висока | Потік блоків без обмежень |
Polling — найпоширеніший вибір: воркер періодично опитує ноду за діапазон блоків, зберігаючи lastIndexedBlock. При перезапуску продовжує з останнього обробленого блоку. WebSocket дає низьку затримку, але вимагає автоматичного перепідключення та синхронізації пропущених блоків. Polling гарантує exactly-once семантику, що у 10 разів надійніше за WebSocket для критичних даних. Firehose — enterprise-рішення для high-throughput систем.
Як обробляти реорганізації ланцюжка?
Реорги — головне джерело багів. На Ethereum PoS фінальність настає через дві епохи (~12 хвилин). На BSC або Polygon реорги глибиною 3-5 блоків — звичайна справа.
Стратегія: індексуємо із затримкою на N підтверджених блоків (13 для Ethereum), зберігаємо хеш кожного проіндексованого блоку. При розбіжності хешу — відкат до останнього співпадаючого стану та переіндексація.
-- Таблиця стану індексера CREATE TABLE indexer_blocks ( block_number BIGINT PRIMARY KEY, block_hash VARCHAR(66) NOT NULL, indexed_at TIMESTAMPTZ DEFAULT NOW() ); -- Подія з прив'язкою до блоку для rollback CREATE TABLE indexed_events ( id BIGSERIAL PRIMARY KEY, block_number BIGINT NOT NULL REFERENCES indexer_blocks(block_number), log_index INT NOT NULL, tx_hash VARCHAR(66) NOT NULL, contract_addr VARCHAR(42) NOT NULL, event_name VARCHAR(100) NOT NULL, decoded_data JSONB NOT NULL, UNIQUE(tx_hash, log_index) ); При детекції реоргу видаляємо записи з block_number >= reorg_depth та переіндексовуємо з цієї точки.
Технічна деталь: глибини підтвердження для різних мереж
Ethereum: 13 блоків (рекомендація). Polygon: 25 блоків. BSC: 15 блоків. Solana: 1 слот (≈400 мс). Значення можна змінювати через змінну середовища CONFIRMATIONS.
Декодування подій: нюанси
ABI-декодування тривіальне з viem або ethers.js, але є підводні камені. Згідно з документацією Solidity по подіях (https://docs.soliditylang.org/en/v0.8.28/contracts.html#events), складності виникають з:
- Indexed vs non-indexed: indexed параметри потрапляють у topics, non-indexed — у data. Подія з 3 indexed параметрами займає 4 topics. Декодування topics для structs неможливе — дані хешуються.
- Анонімні події: без теми event — рідкісні, вимагають нестандартного підходу.
- Proxy-контракти (EIP-1967): події емітяться з адреси proxy, але ABI у реалізації. Потрібно резолвити implementation через storage slot
0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc. Детальніше в EIP-1967 (https://eips.ethereum.org/EIPS/eip-1967).
Приклад декодування на TypeScript:
import { decodeEventLog } from 'viem' function parseSwapEvent(log: Log, abi: Abi): SwapEvent { const decoded = decodeEventLog({ abi, eventName: 'Swap', data: log.data, topics: log.topics, }) return { blockNumber: log.blockNumber, txHash: log.transactionHash, sender: decoded.args.sender, recipient: decoded.args.recipient, amount0: decoded.args.amount0, amount1: decoded.args.amount1, sqrtPriceX96: decoded.args.sqrtPriceX96, liquidity: decoded.args.liquidity, tick: decoded.args.tick, } } Яку базу даних вибрати для зберігання подій?
| Критерій | PostgreSQL (партиціонування) | TimescaleDB |
|---|---|---|
| Налаштування | Ручне створення партицій | Автоматичні hypertables |
| Стиснення | Немає вбудованого | Так, для старих даних (зменшення в 10 разів) |
| Агрегації | Матеріалізовані представлення | Continuous aggregates (автооновлення) |
| Продуктивність | Відмінна при хорошому партиціонуванні | У 5 разів швидше для time-series запитів |
TimescaleDB забезпечує продуктивність у 5 разів вищу за MongoDB для time-series запитів. Для зберігання подій використовуємо PostgreSQL з партиціонуванням по block_number або TimescaleDB для high-frequency контрактів. Приклад партиціонування:
CREATE TABLE swap_events ( block_number BIGINT NOT NULL, event_timestamp TIMESTAMPTZ NOT NULL, event_data JSONB ) PARTITION BY RANGE (block_number); CREATE TABLE swap_events_0_5m PARTITION OF swap_events FOR VALUES FROM (0) TO (5000000); -- аналогічно для наступних діапазонів TimescaleDB дає continuous aggregates для метрик: обсяг торгів за годину, кількість транзакцій — без фонових завдань.
Моніторинг та алерти
Включаємо метрики: indexer_lag_blocks, events_per_second, reorg_count. Алерт при відставанні більше 50 блоків. Здоров'я перевіряється через health endpoint (503 при перевищенні порогу). Стек: Go/Rust для воркера, PostgreSQL/TimescaleDB, Redis для state, Grafana + Prometheus.
Як ми це робимо: процес роботи
- Аналітика: вивчаємо смарт-контракти, виявляємо всі події, визначаємо частоту емісії. Середній час — 1-2 дні.
- Проектування: обираємо підхід (polling для простоти, firehose для high-load), проектуємо схему БД, API. Створюємо діаграму потоку даних.
- Реалізація: пишемо воркер на Go/Rust, налаштовуємо декодування, обробку реоргів, партиціонування. Типовий термін — від 5 до 15 днів.
- Тестування: симулюємо відставання, реорги, високе навантаження. Використовуємо testnet (Sepolia, Holesky).
- Деплой: Docker-контейнери з оркестрацією (Kubernetes або Docker Compose). Первинна індексація останніх 1000 блоків.
- Моніторинг та підтримка: дашборди Grafana, алерти, щомісячні звіти. Протягом першого місяця — швидкі виправлення.
Орієнтовні терміни: базова індексація одного контракту — від 5 днів; складний pipeline з партиціонуванням та моніторингом — від 2 тижнів. Орієнтовна вартість базової індексації — від $5,000, складного pipeline — від $15,000.
Що входить в роботу
- Архітектурне проектування: вибір підходу, схема БД, специфікація API.
- Реалізація pipeline: воркер, декодування, обробка реоргів, партиціонування.
- GraphQL API на основі підписок (Hasura або власний resolver).
- Моніторинг та алерти: дашборди, метрики lag.
- Документація: діаграма даних, точки масштабування, runbook.
- Навчання команди клієнта.
- Підтримка 1 місяць після введення.
Замовте розробку системи індексації під ключ. Отримайте консультацію — ми оцінимо проект за 1 день і запропонуємо оптимальне рішення. Зв'яжіться з нами.







