Розробка системи індексації блокчейн-подій
При типових запитах — «покажи всі транзакції користувача за 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 день і запропонуємо оптимальне рішення. Зв'яжіться з нами.







