Парсинг даних NFT-колекцій (floor price, volume, holders)
OpenSea API повертає floor price з затримкою 5-15 хвилин та агрегує дані по своїй методології. Для торгових ботів, аналітичних платформ та minting dApps, яким потрібен реальний floor — це неприйнятно. Єдиний шлях до точних даних: читати подіїї прямо з блокчейну.
Джерела даних: звідки брати що
On-chain подіїї
Для ERC-721/ERC-1155 колекцій усі продажи видимі через подіїї маркетплейсів. Кожен маркетплейс еміцирує свою подію:
-
OpenSea Seaport:
OrderFulfilled(bytes32 orderHash, address offerer, address zone, address recipient, SpentItem[] offer, ReceivedItem[] consideration)— контракт0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC -
Blur:
TakerAsk/TakerBidна0x000000000000Ad05Ccc4F10045630fb830B95127 -
LooksRare v2:
TakerAsk/TakerBid -
X2Y2:
EvInventory
Floor price не можна отримати з подій напрямки — подіїї показують виконані ордери, а не активні листинги. Для актуального floor потрібно або індексувати активні листинги через marketplace API, або використовувати агрегатори.
Holders та Transfers
Transfer(address indexed from, address indexed to, uint256 indexed tokenId) — стандарт ERC-721. Повний граф володення будується через replay всіх Transfer подій від блоку деплою. Unique holders = унікальні адреси to мінус адреси, які потім перевели токени іншій адресі.
Для ERC-1155: TransferSingle та TransferBatch. Тут володіння це баланс, не бінарне становище: balanceOf(address, tokenId).
Архітектура парсера
Стек
ethereum-node (Alchemy/Infura/Quicknode)
→ ethers.js / viem (event filtering)
→ message queue (Redis Streams / BullMQ)
→ PostgreSQL / ClickHouse (storage)
→ REST/WebSocket API (видача даних)
Для історичних даних — getLogs з фільтром по address та topics[0]. Батчимо блоки по 2000 (обмеження більшості RPC провайдерів на eth_getLogs):
async function fetchTransferEvents(
contract: string,
fromBlock: number,
toBlock: number,
provider: JsonRpcProvider
) {
const iface = new Interface(['event Transfer(address indexed from, address indexed to, uint256 indexed tokenId)']);
const filter = {
address: contract,
topics: [iface.getEventTopic('Transfer')],
fromBlock,
toBlock,
};
const logs = await provider.getLogs(filter);
return logs.map(log => iface.parseLog(log));
}
Для real-time: WebSocket підписка через provider.on(filter, callback) або Alchemy eth_subscribe newLogs.
Обчислення floor price
Два підходи:
1. Marketplace API агрегація — запитуємо floor у OpenSea, Blur, LooksRare, беремо мінімум. Проблема: rate limits та кешування на стороні API.
2. Orderbook індексування — підписуємось на подіїї створення/скасування ордерів. Seaport: OrderValidated (створення), OrderCancelled, OrderFulfilled (виконання). Будуємо локальний orderbook, обчислюємо floor самостійно. Точніше, але складніше в підтримці при оновленнях контрактів маркетплейсу.
Для більшості задач достатньо першого підходу з кешем на 60 секунд.
Зберігання та запити
ClickHouse ефективніше за PostgreSQL для time-series NFT даних — аналітичні запити на мільйонах рядків у 10–50 разів швидше. Схема:
| Колонка | Тип | Описання |
|---|---|---|
block_number |
UInt64 | Блок подіїї |
tx_hash |
FixedString(66) | Хеш транзакції |
contract |
FixedString(42) | Адреса колекції |
token_id |
UInt256 | ID токена |
from |
FixedString(42) | Продавець/відправник |
to |
FixedString(42) | Покупець/одержувач |
price_wei |
UInt256 | Ціна в wei |
marketplace |
LowCardinality(String) | Маркетплейс |
timestamp |
DateTime | Час блоку |
Партиціювання по місяцях (toYYYYMM(timestamp)), сортувальний ключ (contract, timestamp).
Розв'язання типових проблем
Rate limits: Alchemy Free — 330 CUPS, Growth — 660 CUPS. При історичному парсингу крупної колекції (BAYC: 500k+ Transfer подій) без throttling отримаємо 429. Реалізуємо exponential backoff + queue з concurrency control.
Реорганізації блокчейну: подіїї з останніх 12 блоків потрібно позначати як «pending» та підтверджувати тільки після finality. Для Ethereum PoS — 2 епохи (64 блоки) для економічної finality.
Wash trading: обсяг по адресам з циклічними переводами спотворює статистику. Базова евристика: угоди де from та to це пов'язані адреси (отримали ETH з одного джерела) позначаються флагом.
Орієнтири по строкам
Парсер Transfer подій + holders tracker — 1 день. Додавання floor price через marketplace API + кеш — ще пів дня. Історичний backfill для крупної колекції + дашборд — 2-3 дні усього.







