Чому індексація TON потребує іншого підходу?
TON Blockchain — одна з найскладніших блокчейн-платформ для індексації. Причина в архітектурі: infinite sharding — кількість шардів динамічно змінюється залежно від навантаження, від 1 до 256. Транзакції всередині одного шарда фіналізуються швидко, але транзакція між акаунтами в різних шардах — це ланцюжок повідомлень, який потрібно відстежувати через кілька блоків і шардів. Кожне повідомлення серіалізоване в Cell і потребує рекурсивного парсингу. Стандартний підхід «слухаємо блоки по одному» тут не працює. Багато років ми працюємо з блокчейнами, брали участь в аудитах смарт-контрактів TON, індексували дані для великого DeFi-проєкту: понад 2 млн транзакцій на місяць. Індексація TON у 3–5 разів складніша, ніж аналогічне завдання для EVM-ланцюгів, і потребує спеціалізованого пайплайну.
Розробка індексатора для TON вимагає глибокого розуміння TVM (TON Virtual Machine) та BOC-формату. Ми використовуємо бібліотеку @ton/ton версій 0.57+ для роботи з блоками та транзакціями. На відміну від EVM, де кожна транзакція має один in-message, у TON одна транзакція може містити до 255 out-messages, що робить традиційні SQL-моделі неефективними.
Як архітектура TON впливає на індексацію?
TON складається з трьох рівнів:
- Masterchain — головний ланцюг, фіналізує стан всіх воркчейнів
- Basechain (workchain 0) — основний користувацький ланцюг
- Shardchains — шарди workchain-а, їх може бути від 1 до 256
Кожен masterchain блок посилається на останні блоки всіх шардів (ShardStateUnsplit). Для повного індексування потрібно:
- Отримати masterchain блок
- З нього витягти список актуальних шард-блоків
- З кожного шард-блока витягти транзакції
- Для кожної транзакції — відстежити дочірні повідомлення (out messages)
MasterBlock[N] └─ Shard(0:0..7fff)[blockX] ├─ tx1 → out_msg → [інший шард або акаунт] └─ tx2 → out_msg → ... └─ Shard(0:8000..ffff)[blockY] └─ tx3 ... Hosted API vs власний вузол
| Критерій | Hosted API | Власний вузол |
|---|---|---|
| Час до старту | Хвилини | Дні (збірка, синхронізація) |
| Навантаження | Обмежено тарифом | Повний контроль |
| Історичні дані | Обмежений буфер | Повний архів |
| Вартість | Від безкоштовного рівня до корпоративних тарифів | Витрати на сервер та дискове сховище: дешевше в 3-5 разів при великих обсягах |
| Складність експлуатації | Мінімальна | DevOps-інженер у штаті |
Hosted API (TonAPI, TON Center, GetBlock) — правильний вибір для MVP. Для high-load продакшену та історичних запитів потрібен свій вузол. Повний архівний вузол TON займає ~2 TB і зростає на ~500 GB на рік. Ми допомагаємо вибрати архітектуру та розгорнути ноду.
Порівняння СУБД для зберігання TON-даних
| СУБД | Підходить для | Особливості |
|---|---|---|
| PostgreSQL | MVP, до 1 млн tx/день | ACID, легке налаштування, обмежена швидкість вставки |
| TimescaleDB | Висока частота запису, часові ряди | Автоматичне партиціювання, неперервні агрегати |
| ClickHouse | Аналітика, мільярди рядків | Колоночне зберігання, в 5-10 разів швидше за агрегаціями |
Що входить у роботу?
- Проєктування схеми даних: під вашу бізнес-логіку (транзакції, Jetton, NFT, DeFi)
- Реалізація індексатора на TypeScript/Node.js з використанням
@ton/ton - Інтеграція з TonAPI або власним вузлом
- Парсинг довільних повідомлень (op-коди, аргументи контрактів)
- Трасування транзакцій (trace chain) для аналітики
- База даних (PostgreSQL, TimescaleDB або ClickHouse для high-load)
- REST API та WebSocket для клієнтів
- Моніторинг (відставання, помилки, RPC latency)
- Документація та навчання команди
- Обробка форків: зберігання сирих BOC для перепарсингу без втрати даних
Як реалізувати основний цикл індексатора?
class TonIndexer { private client: TonClient4; private db: Pool; private lastMasterBlock: number; async indexLoop(): Promise<void> { while (true) { try { const masterInfo = await this.client.getLastBlock(); const currentSeqno = masterInfo.last.seqno; if (currentSeqno <= this.lastMasterBlock) { await this.sleep(2000); // ~5 сек на блок 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, 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); } } } Парсинг транзакцій та Jetton-переказів
Транзакція TON містить in_msg (причина) та out_msgs (результат). Для Jetton Transfer використовується op-код 0x0f8a7ea5. Приклад вилучення даних:
function parseTransaction(rawTx: RawTransaction): IndexedTransaction { const inMsg = rawTx.inMessage; let opcode: number | undefined; if (inMsg?.body) { 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, }; } Якщо потрібне повне трасування транзакцій (наприклад, для DEX-аналітики), використовуємо TonAPI: виклик /v2/traces/{hash} повертає дерево пов'язаних транзакцій. Це дозволяє відстежити весь шлях від виклику користувача до фінальної події.
Схема бази даних для 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), opcode INTEGER, exit_code SMALLINT NOT NULL, compute_fee NUMERIC(38,0), raw_data BYTEA ); 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; 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 ); Моніторинг та надійність
Ключові метрики: відставання від masterchain (мета — менше 5 блоків), кількість помилок парсингу, RPC latency. Реорганізації в TON рідкісні, але можливі; ми зберігаємо сирі BOC для перепарсингу без повторних запитів.
Отримайте робочий індексатор TON у стислі терміни. Оцінимо навантаження, запропонуємо архітектуру та терміни при зверненні. Гарантуємо документацію, код та підтримку після запуску. Досвід: 5+ років у DeFi, понад 10 успішних індексаторів для різних мереж. Замовте розробку — отримайте готове рішення від 2 тижнів.







