Розробка індексатора TON blockchain

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

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

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

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

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

Розробка індексатора TON blockchain

TON — одна з найскладніших блокчейн-платформ для індексації. Причина в архітектурі: TON використовує infinite sharding — кількість шардів динамічно змінюється залежно від навантаження. Транзакції всередину одного шарду финалізуються швидко, але транзакція між аккаунтами в різних шардах це ланцюг сообщень, яку потрібно відслідковувати через кілька блоків та шардів. Стандартний підхід "слухаємо блоки по одному" тут не працює.

Архітектура TON: що потрібно зрозуміти перед індексацією

TON складається з трьох рівнів:

  • Masterchain — головна цепь, финалізує стан усіх workcheins
  • Basechain (workchain 0) — основна користувальницька цепь
  • Shardchains — шарди workchain-а, їх може бути від 1 до 256

Кожний masterchain блок ссилається на останні блоки всіх шардів (ShardStateUnsplit). Для повної індексації потрібно:

  1. Отримати masterchain блок
  2. З нього витягти список актуальних шард-блоків
  3. З кожного шард-блока витягти транзакції
  4. Для кожної транзакції — відследити дочірні сообщення (out messages)
MasterBlock[N]
  └─ Shard(0:0..7fff)[blockX]
       ├─ tx1 → out_msg → [інший шард або аккаунт]
       └─ tx2 → out_msg → ...
  └─ Shard(0:8000..ffff)[blockY]
       └─ tx3 ...

TON API vs запуск своєї ноди

Hosted API (швидкий старт)

  • TonAPI (tonapi.io) — багатий REST + WebSocket, підтримує events, transactions, traces; free tier з rate limits
  • TON Center (toncenter.com/api/v2) — офіційний публічний API, платний тариф без rate limits
  • GetBlock.io, Chainbase — enterprise RPC

Hosted API — правильний вибір для MVP та середніх навантажень.

Своя нода

Повний TON архів (~2 TB, зростає ~500 GB/рік) потрібен при:

  • Необхідність трасування транзакцій (runGetMethod по історичних блокам)
  • Rate limits стають bottleneck
  • Вимоги до SLA
# TON node (C++ реалізація) — складна сборка з вихідних текстів
git clone --recurse-submodules https://github.com/ton-blockchain/ton
cd ton && mkdir build && cd build
cmake -DCMAKE_BUILD_TYPE=Release ..
make -j$(nproc) validator-engine

# Або tonutils-go для Go-інструментів

Простіший шлях — mytonctrl (скрипт Ton Foundation для встановлення ноди):

wget https://raw.githubusercontent.com/ton-blockchain/mytonctrl/master/scripts/install.sh
sudo bash install.sh -m full

Реалізація індексера: основний цикл

import { TonClient4, Cell, loadTransaction } from '@ton/ton';

class TonIndexer {
  private client: TonClient4;
  private db: Pool;
  private lastMasterBlock: number;

  async indexLoop(): Promise<void> {
    while (true) {
      try {
        // Отримуємо актуальний masterchain блок
        const masterInfo = await this.client.getLastBlock();
        const currentSeqno = masterInfo.last.seqno;

        if (currentSeqno <= this.lastMasterBlock) {
          await this.sleep(2000); // Чекаємо новий блок (~5 сек у TON)
          continue;
        }

        // Обробляємо усі пропущені мастер-блоки
        for (let seqno = this.lastMasterBlock + 1; seqno <= currentSeqno; seqno++) {
          await this.processMasterBlock(seqno);
        }

        this.lastMasterBlock = currentSeqno;
      } catch (e) {
        console.error('Indexer error:', e);
        await this.sleep(5000);
      }
    }
  }

  private async processMasterBlock(seqno: number): Promise<void> {
    const masterBlock = await this.client.getBlock(seqno);
    
    // Обробляємо мастер-блок транзакції
    await this.processShardTransactions(
      -1, // workchain -1 = masterchain
      masterBlock.shards
        .filter(s => s.workchain === -1)
        .flatMap(s => s.transactions)
    );

    // Отримуємо та обробляємо кожний шард
    for (const shard of masterBlock.shards.filter(s => s.workchain === 0)) {
      const transactions = await this.getShardTransactions(
        shard.workchain,
        shard.shard,
        shard.seqno
      );
      await this.processShardTransactions(shard.workchain, transactions);
    }
  }
}

Транзакції TON: структура та парсинг

Транзакція у TON має:

  • in_msg — вхідне сообщение (причина виконання)
  • out_msgs — вихідні сообщения (результат виконання)
  • compute_ph — фаза вичислень (gas, exit code)
  • action_ph — фаза дій (відправка вихідних сообщений)
interface IndexedTransaction {
  hash: string;
  lt: bigint;            // logical time
  account: string;
  inMsgHash?: string;
  value?: bigint;        // nanoTON
  opcode?: number;       // перші 4 байти in_msg body
  exitCode: number;
  computeFee: bigint;
  timestamp: number;
  blockSeqno: number;
}

function parseTransaction(rawTx: RawTransaction): IndexedTransaction {
  const inMsg = rawTx.inMessage;
  let opcode: number | undefined;
  
  if (inMsg?.body) {
    // Перші 4 байти body — op code для Jetton/NFT/DEX протоколів
    const slice = inMsg.body.beginParse();
    if (slice.remainingBits >= 32) {
      opcode = slice.loadUint(32);
    }
  }
  
  return {
    hash: rawTx.hash().toString('hex'),
    lt: rawTx.lt,
    account: rawTx.address.toString(),
    value: inMsg?.info.type === 'internal' ? inMsg.info.value.coins : undefined,
    opcode,
    exitCode: rawTx.description.computePhase?.exitCode ?? 0,
    computeFee: rawTx.totalFees.coins,
    timestamp: rawTx.now,
    blockSeqno: rawTx.blockSeqno,
  };
}

Op-коди Jetton: індексація переводів токенів

Jetton Transfer має стандартний op-код 0x0f8a7ea5. Для індексації всіх Jetton переводів моніторимо транзакції на JettonWallet контрактах з цим op-кодом:

const JETTON_TRANSFER_OP = 0x0f8a7ea5;
const JETTON_TRANSFER_NOTIFICATION_OP = 0x7362d09c;

function isJettonTransfer(tx: IndexedTransaction): boolean {
  return tx.opcode === JETTON_TRANSFER_OP;
}

function parseJettonTransferNotification(body: Cell): {
  amount: bigint;
  sender: Address;
  forwardPayload: Cell | null;
} {
  const slice = body.beginParse();
  slice.loadUint(32); // op
  slice.loadUint(64); // query_id
  const amount = slice.loadCoins();
  const sender = slice.loadAddress();
  const hasPayload = slice.loadBit();
  const forwardPayload = hasPayload ? slice.loadRef() : null;
  return { amount, sender, forwardPayload };
}

Трасування транзакцій (Trace)

Для DEX аналітики, бот-детекції та аудиту потрібна повна трасування: одна вхідна транзакція породжує дерево викликів. TonAPI надає /v2/traces/{hash} — граф всіх пов'язаних транзакцій.

async function getTransactionTrace(txHash: string) {
  const response = await fetch(
    `https://tonapi.io/v2/traces/${txHash}`,
    { headers: { 'Authorization': `Bearer ${TONAPI_KEY}` } }
  );
  const trace = await response.json();
  // trace.transaction + trace.children[] — рекурсивне дерево
  return trace;
}

База даних: схема для TON індексера

CREATE TABLE ton_transactions (
  hash          CHAR(64) PRIMARY KEY,
  lt            BIGINT NOT NULL,
  account       VARCHAR(66) NOT NULL,
  block_seqno   INTEGER NOT NULL,
  timestamp     TIMESTAMPTZ NOT NULL,
  in_msg_hash   CHAR(64),
  value         NUMERIC(38,0),  -- nanoTON
  opcode        INTEGER,
  exit_code     SMALLINT NOT NULL,
  compute_fee   NUMERIC(38,0),
  raw_data      BYTESEA           -- оригінальний BOC для повторного парсинга
);

CREATE INDEX idx_ton_tx_account ON ton_transactions(account);
CREATE INDEX idx_ton_tx_timestamp ON ton_transactions(timestamp DESC);
CREATE INDEX idx_ton_tx_opcode ON ton_transactions(opcode) WHERE opcode IS NOT NULL;

-- Для Jetton індексації
CREATE TABLE jetton_transfers (
  id            BIGSERIAL PRIMARY KEY,
  tx_hash       CHAR(64) REFERENCES ton_transactions(hash),
  jetton_master VARCHAR(66) NOT NULL,
  from_address  VARCHAR(66) NOT NULL,
  to_address    VARCHAR(66) NOT NULL,
  amount        NUMERIC(38,0) NOT NULL,
  timestamp     TIMESTAMPTZ NOT NULL
);

Моніторинг індексера

Ключові метрики:

  • Отставання від masterchaincurrent_seqno - indexed_seqno має бути < 5
  • Кількість транзакцій в очереді — якщо растає, обработчик не встигає
  • Помилки парсинга — некоректний BOC у raw_data
  • RPC latency — час ответа TonAPI/TonCenter

Реорганізації у TON рідкісні, але можливі для свіжих блоків. Зберігаємо raw_data (BOC) кожної транзакції — при обнаруженні розбіжності перепарсимо з сирих даних без повторного звернення до мережи.