Розробка парсера on-chain даних: транзакції, баланси, контракти

Парсинг on-chain даних (транзакції, баланси, контракти) Ви намагаєтеся отримати історичні баланси гаманців Ethereum, але `eth_getBalance` повертає лише поточний стан? Або потрібно відстежити внутрішні транзакції контракту, які не видно у звичайних транзакціях? Для ефективного парсингу блокчейну п

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

Часті запитання

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

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

Парсинг on-chain даних (транзакції, баланси, контракти)

Ви намагаєтеся отримати історичні баланси гаманців Ethereum, але eth_getBalance повертає лише поточний стан? Або потрібно відстежити внутрішні транзакції контракту, які не видно у звичайних транзакціях? Для ефективного парсингу блокчейну потрібно отримувати on-chain дані: транзакції, баланси гаманців, логи контрактів. Ми стикалися з цими завданнями сотні разів і знаємо, як їх вирішити ефективно. Замість того щоб використовувати Dune або Etherscan з їхніми обмеженнями, ви можете розгорнути власний парсер, який дає повний контроль над даними, швидкістю та вартістю. Наша компанія має понад 10 років досвіду в блокчейн-розробці та реалізувала десятки успішних проєктів з парсингу. У цій статті ми розповімо про ключові аспекти парсингу on-chain даних: від вибору джерела до оптимізації зберігання. Парсинг блокчейну дозволяє отримувати дані для аналітики, моніторингу транзакцій в реальному часі, індексування блокчейну для швидкого доступу до історичних даних. Економія на інфраструктурі при такому підході може сягати 60% (наприклад, заміна Dune на власний парсер економить $2000–5000 на місяць при високих навантаженнях). Вартість базового рішення — від $5000, а типова економія на інфраструктурі складає до 60% (наприклад, $2000–5000 на місяць при високих навантаженнях порівняно з Dune).

Ethereum JSON-RPC API — офіційна документація, на якій ґрунтуються приклади нижче.

Які дані можна отримати з блокчейну?

Типи даних та джерела

Транзакції рівня блоку (eth_getBlockByNumber з fullTx: true): from/to/value/gas/gasPrice/nonce, input data (calldata в hex), receipt (status, gasUsed, logs).

Internal transactions — виклики між контрактами, не видно у звичайних транзакціях. Потрібен debug_traceTransaction або trace_block (Erigon/OpenEthereum trace namespace). Парсинг трасувань (traces) дає доступ до внутрішніх транзакцій.

Події (logs) — емітуються через emit Event(...) у Solidity, доступні через eth_getLogs. Найпродуктивніший спосіб фільтрації за address + topic на рівні ноди. Отримання логів дозволяє відстежувати Transfer, Swap та інші події контрактів.

Storage state — значення storage variables контракту через eth_getStorageAt(address, slot, blockNumber). Для archive node — на будь-якому історичному блоці.

ERC-20 баланси — через balanceOf(address) view call або через Transfer event history. Щоб отримати historical balance, використовуйте archive node.

Джерело Що дає Обмеження
Публічний Ethereum RPC (Infura/Alchemy) Стандартний JSON-RPC Rate limits, немає traces
Self-hosted Geth Повний JSON-RPC Немає traces без --gcmode=archive
Self-hosted Erigon JSON-RPC + trace namespace ~2.5 TB, 3-5 днів синхронізації
Alchemy/QuickNode (платні плани) Розширений API + traces Вартість при високому RPS
Firehose (StreamingFast) Бінарний стрімінг, всі дані Складне налаштування
Dune Analytics / Flipside SQL інтерфейс до indexed даних Затримка, обмеження схеми

Self-hosted Erigon в 10 разів швидше Geth для парсингу traces і краще підходить для високих навантажень. Це стандарт де-факто для завдань парсингу. Порівняно з Dune, наш парсер знижує витрати в 2-3 рази при великих обсягах. Важливо розуміти, що дані зберігаються в Merkle Patricia Trie, що впливає на методи доступу до історичних даних.

Як отримати історичні баланси та оптимізувати запити?

Для отримання балансу на будь-якому блоці використовуйте archive node:

const historicalBalance = await client.readContract({ address: TOKEN_ADDRESS, abi: erc20Abi, functionName: 'balanceOf', args: [walletAddress], blockNumber: 18_500_000n, }); 

Мультикол прискорює масові запити в 5–10 разів, об'єднуючи кілька view-викликів в один запит:

import { multicall } from 'viem'; const balances = await client.multicall({ contracts: addresses.map(addr => ({ address: TOKEN_ADDRESS, abi: erc20Abi, functionName: 'balanceOf', args: [addr], })), allowFailure: true, }); 

Багатомережевий парсинг та продуктивність

Конфігурація під різні мережі:

const CHAIN_CONFIGS = { ethereum: { rpc: INFURA_ETH, chunkSize: 1000, blockTime: 12 }, bsc: { rpc: BSC_RPC, chunkSize: 2000, blockTime: 3 }, polygon: { rpc: POLYGON_RPC, chunkSize: 1500, blockTime: 2 }, arbitrum: { rpc: ARB_RPC, chunkSize: 5000, blockTime: 0.25 }, }; 

Для Ethereum: ~6500 блоків/день × ~6000 TX/блок = ~40M транзакцій/день. Кожна з receipts: ~1-5 KB. Разом: ~40-200 GB/день для повного парсингу. Для більшості завдань достатньо парсингу лише цільових контрактів. Дані зберігаються у вигляді state trie, який оновлюється з кожним блоком.

Тип даних Типовий об'єм Рекомендоване сховище
Транзакції + receipts 40-200 GB/день PostgreSQL + TimescaleDB
Логи подій 5-20 GB/день Columnar (ClickHouse)
Historical balances Залежить від кількості адрес S3 + кеш Redis

Зберігання: PostgreSQL + TimescaleDB для time-series + S3 для raw архіву. Критичні індекси: CREATE INDEX ON transactions (from_address, block_number DESC), CREATE INDEX ON transfers (token_address, block_number DESC).

Як ми працюємо: етапи, терміни та вартість

Пропонуємо розробку парсера під ключ: від аналізу вимог до деплою та моніторингу. Оцінимо ваш проект безкоштовно.

Що входить у роботу

  • Архітектурна документація
  • Доступ до розгорнутої інфраструктури
  • Вихідний код парсера з коментарями
  • Навчання вашої команди
  • Підтримка протягом 3 місяців після здачі

Як розгорнути парсер за 5 кроків

  1. Аналіз вимог та визначення джерел даних.
  2. Архітектурне проєктування та вибір технологій.
  3. Розробка парсера, тестування на тестовому блокчейні.
  4. Деплой у хмарне середовище (AWS/GCP) з моніторингом.
  5. Моніторинг, оптимізація та підтримка.

Терміни та вартість: базова версія для 2-3 мереж з PostgreSQL та REST API — 2-4 тижні, вартість від $5000. Складні проєкти до 8 тижнів. Типова економія на інфраструктурі — до 60% ($2000–5000 на місяць). Вартість розробки окупається за 2-3 місяці.

Наша компанія має 10+ років досвіду в блокчейн-розробці та реалізувала десятки проєктів з парсингу та індексації. Ми гарантуємо прозорість та ефективність.

Код прикладу: парсинг транзакцій блоку
import { createPublicClient, http } from 'viem';

const client = createPublicClient({
  transport: http(RPC_URL),
});

async function processBlock(blockNumber: bigint) {
  const block = await client.getBlock({
    blockNumber,
    includeTransactions: true,
  });

  for (const tx of block.transactions) {
    if (typeof tx === 'string') continue;
    const receipt = await client.getTransactionReceipt({ hash: tx.hash });
    await db.insertTransaction({
      hash: tx.hash,
      blockNumber: Number(tx.blockNumber),
      blockTimestamp: Number(block.timestamp),
      from: tx.from,
      to: tx.to,
      value: tx.value.toString(),
      gasPrice: tx.gasPrice?.toString(),
      gasLimit: tx.gas.toString(),
      input: tx.input,
      nonce: tx.nonce,
      status: receipt.status === 'success',
      gasUsed: receipt.gasUsed.toString(),
    });
  }
}
Код прикладу: отримання подій Transfer
const logs = await client.getLogs({
  address: TOKEN_ADDRESS,
  event: parseAbiItem('event Transfer(address indexed from, address indexed to, uint256 value)'),
  fromBlock: 19_000_000n,
  toBlock: 19_100_000n,
});

for (const log of logs) {
  await db.insertTransfer({
    txHash: log.transactionHash,
    blockNumber: Number(log.blockNumber),
    from: log.args.from,
    to: log.args.to,
    value: log.args.value.toString(),
  });
}