Обработка активов и off-chain индексирование
Обработка активов и off-chain индексирование — это основа для любого приложения, работающего с платежами, токенами или NFT. Блокчейн — это журнал транзакций, но для отображения балансов, истории переводов и уведомлений нужен индексатор, который преобразует raw-данные блоков в структурированные записи.
Production-приложения на TON — биржи, платёжные сервисы, маркетплейсы — не могут полагаться только на on-chain данные. Им необходимо отслеживать события в реальном времени: поступление депозитов, переводы токенов, смену владельца NFT. В этом уроке мы разберём, как устроена обработка активов в TON: от структуры транзакций и 3-message chain Jetton-переводов до стратегий индексирования и best practices.
Структура транзакций TON
Каждая транзакция в TON состоит из нескольких компонентов, важных для off-chain парсинга:
| Компонент | Описание |
|---|---|
in_msg | Входящее сообщение, которое инициировало транзакцию |
out_msgs | Список исходящих сообщений, сгенерированных смарт-контрактом |
compute_phase | Результат выполнения кода контракта (exit code, gas used) |
action_phase | Результат применения действий (отправка сообщений, изменение кода) |
total_fees | Суммарная комиссия за транзакцию |
lt | Logical time — порядковый номер транзакции аккаунта |
Для off-chain мониторинга ключевыми являются in_msg и out_msgs — именно в них закодированы opcodes операций и payload данные.
Logical time (lt) гарантирует строгий порядок транзакций одного аккаунта. Используйте пару (lt, hash) как курсор при постраничном обходе истории транзакций через getTransactions.
Jetton-переводы: 3-message chain
Перевод Jetton (токена стандарта TEP-74) — это не одна транзакция, а цепочка из трёх сообщений между четырьмя участниками. Это фундаментальное отличие от ERC-20, где перевод — одна транзакция одного контракта.
Архитектура Jetton была подробно рассмотрена в модуле 5 (Стандарты Токенов). Здесь мы сфокусируемся на off-chain обработке этой цепочки.
Поток сообщений
Opcodes
| Шаг | Операция | Opcode | Направление |
|---|---|---|---|
| 1 | transfer | 0xf8a7ea5 | Отправитель -> его Jetton Wallet |
| 2 | internal_transfer | 0x178d4519 | Jetton Wallet отправителя -> Jetton Wallet получателя |
| 3 | transfer_notification | 0x7362d09c | Jetton Wallet получателя -> контракт получателя |
Парсинг transfer_notification
Для off-chain сервиса, принимающего Jetton-депозиты, ключевое сообщение — transfer_notification (шаг 3). Именно его получает ваш контракт или адрес:
// Структура transfer_notification (op: 0x7362d09c)
// query_id:uint64
// amount:Coins -- количество переведённых Jetton
// sender:MsgAddress -- адрес отправителя (Alice)
// forward_payload:Cell -- произвольные данные (комментарий, memo)
forward_ton_amount — критический параметр. При отправке transfer отправитель указывает forward_ton_amount — количество TON, которое будет приложено к transfer_notification. Если установить 0, уведомление не будет доставлено. Минимум 1 наноTON необходим для доставки notification получателю.
Идентификация отправителя
Важный нюанс: transfer_notification приходит не от Alice напрямую, а от Jetton Wallet получателя (Bob’s Jetton Wallet). Чтобы узнать реального отправителя, нужно прочитать поле sender из payload сообщения.
Мониторинг NFT-событий
NFT в TON (стандарт TEP-62) также используют сообщения для уведомления о событиях. Основные операции для мониторинга:
ownership_assigned
При передаче NFT новому владельцу отправляется уведомление ownership_assigned:
// Структура ownership_assigned (op: 0x05138d91)
// query_id:uint64
// prev_owner:MsgAddress -- предыдущий владелец
// forward_payload:Cell -- произвольные данные
Парсинг метаданных NFT
NFT-метаданные в TON хранятся тремя способами:
| Способ | Описание | Индикатор |
|---|---|---|
| On-chain | Данные полностью в Cell смарт-контракта | Нет префикса |
| Off-chain | URL в Cell, данные по ссылке (IPFS/HTTPS) | Префикс 0x01 |
| Semi-chain | Часть данных on-chain, остальное по ссылке | Комбинация |
Для off-chain мониторинга:
// Получение метаданных NFT item
const nftData = await client.nft.getItem(nftAddress);
// Off-chain метаданные: читаем URL из Cell
// Cell начинается с 0x01, далее UTF-8 URL
// Пример: 0x01 + "https://example.com/metadata.json"
Стратегии мониторинга коллекций
| Стратегия | Когда использовать |
|---|---|
| Мониторинг Collection | Отслеживание mint новых item в коллекции |
| Мониторинг отдельных Item | Отслеживание передач конкретных NFT |
| TonAPI Events API | Агрегированные события по аккаунту (включая NFT) |
Подходы к индексированию
Для off-chain обработки событий TON существует три основных подхода, каждый со своими компромиссами.
1. Polling через TON Center
Простейший подход — периодический опрос getTransactions через TON Center API:
// Polling: получение новых транзакций
const transactions = await client.getTransactions(address, {
limit: 20,
lt: lastProcessedLt, // logical time последней обработанной транзакции
hash: lastProcessedHash, // hash последней обработанной транзакции
});
for (const tx of transactions) {
// Парсинг in_msg для определения типа операции
const opcode = tx.inMessage?.body?.beginParse().loadUint(32);
if (opcode === 0x7362d09c) {
// transfer_notification -- Jetton депозит
await processJettonDeposit(tx);
}
}
2. Webhooks/SSE через TonAPI
TonAPI предоставляет Streaming API для real-time мониторинга:
// SSE: подписка на события аккаунта через TonAPI
const eventSource = new EventSource(
`https://tonapi.io/v2/sse/accounts/transactions?accounts=${address}`,
{
headers: { Authorization: `Bearer ${apiKey}` },
}
);
eventSource.onmessage = (event) => {
const tx = JSON.parse(event.data);
// Обработка транзакции в реальном времени
processTransaction(tx);
};
TonAPI также поддерживает webhooks — push-уведомления на ваш HTTP endpoint при возникновении событий.
3. Полные индексеры
Для сложных аналитических запросов существуют специализированные индексеры:
- dton.io — GraphQL API для произвольных запросов по блокчейну
- TON Center v3 — индексер с фильтрацией и агрегацией
Сравнение подходов
| Характеристика | Polling (TON Center) | Webhooks/SSE (TonAPI) | Полный индексер (dton.io) |
|---|---|---|---|
| Задержка | 5-30 сек (зависит от интервала) | < 1 сек | < 5 сек |
| Сложность | Низкая | Средняя | Высокая |
| Стоимость | Бесплатно (Free tier) | Бесплатный tier | Зависит от провайдера |
| Пропущенные события | Возможно при сбое | Нет (persistent connection) | Нет |
| Произвольные запросы | Ограничены | Ограничены | GraphQL — любые |
| Self-hosting | Да (TON Center) | Нет | Зависит от решения |
Для большинства dApp комбинация подходит лучше всего: webhooks TonAPI для real-time уведомлений + polling TON Center как fallback для пропущенных событий.
Mintless Jettons
Mintless Jettons — расширение стандарта TEP-74, позволяющее раздать токены без создания отдельных Jetton Wallet контрактов для каждого получателя. Вместо mint-транзакции для каждого адреса используется merkle-proof для подтверждения права на claim.
Mintless Jettons — сравнительно новый паттерн в TON. Он существенно удешевляет массовые раздачи (airdrops), но требует специальной обработки на стороне off-chain сервисов.
Как это работает
- Эмитент формирует merkle-дерево со всеми адресами и суммами
- Merkle root публикуется в Jetton Master контракте
- Получатель предоставляет merkle-proof при первом claim
- Jetton Wallet создаётся только при claim, а не при распределении
Влияние на off-chain обработку
- Custom Payload API — сервисы должны предоставлять endpoint для генерации merkle-proof по адресу
- Первая транзакция отличается — claim создаёт Jetton Wallet, последующие переводы идут стандартным 3-message chain
- Balance может быть “виртуальным” — до claim баланс существует только в merkle-дереве, не в блокчейне
Лучшие практики
Идемпотентная обработка
Каждая транзакция в TON имеет уникальный хеш. Используйте его для дедупликации:
async function processTransaction(tx: Transaction) {
const txHash = tx.hash;
// Проверка: уже обработана?
if (await isProcessed(txHash)) {
return; // Пропускаем дубликат
}
// Обработка
await handleTransaction(tx);
// Отметка как обработанная
await markProcessed(txHash);
}
Глубина подтверждения
В TON транзакция считается финальной после включения в masterchain block. На практике:
- Транзакция в workchain подтверждается за 2-5 секунд
- Для критических операций (крупные депозиты) рекомендуется ждать подтверждения в masterchain — это гарантирует необратимость
Не полагайтесь на транзакцию в workchain до её подтверждения в masterchain. Хотя chain reorgs в TON крайне редки, для финансовых операций всегда ждите masterchain confirmation.
Обработка ошибок и ретраи
- Exponential backoff при ошибках API (429, 500, 503)
- Dead letter queue для транзакций, которые не удалось обработать после N попыток
- Alerting при нарушении SLA (задержка обработки > порога)
Мониторинг и алертинг
Ключевые метрики для production-мониторинга:
| Метрика | Порог для алерта |
|---|---|
| Задержка обработки (lag) | > 60 сек |
| Ошибки парсинга | > 0 за 5 мин |
| Пропущенные транзакции | > 0 |
| API rate limit hits | > 10 за минуту |
| Размер очереди необработанных | Растёт > 5 мин |
Ключевые выводы
- Jetton-перевод — это 3 сообщения, а не одна транзакция:
transfer(0xf8a7ea5) ->internal_transfer(0x178d4519) ->transfer_notification(0x7362d09c). - Для приёма Jetton-депозитов слушайте transfer_notification — именно его получает контракт получателя. Не забудьте указать
forward_ton_amount >= 1 наноTON. - Три подхода к индексированию: polling (простой), webhooks/SSE (real-time), полные индексеры (аналитика). Комбинируйте для надёжности.
- Mintless Jettons используют merkle-proof для mass distribution без предварительного создания Jetton Wallet — требуют специальной обработки.
- Всегда реализуйте идемпотентную обработку с дедупликацией по хешу транзакции.
- Ждите подтверждения в masterchain для финансово критических операций.
Частые ошибки
- Определяют зачисление по одной транзакции вместо отслеживания всей цепочки сообщений: Jetton-перевод включает несколько транзакций.
- Не обрабатывают реорганизации (forks): блок может быть заменён, и транзакция, которую вы уже обработали, окажется невалидной.
- Используют polling с фиксированным интервалом вместо long polling или webhooks, что либо создаёт задержку, либо перегружает API.
- Не сохраняют lt (logical time) и hash последнего обработанного блока: при перезапуске сервиса повторно обрабатывают все транзакции.
Проверка знанийКакой opcode получает контракт получателя при Jetton-переводе, и почему важно установить forward_ton_amount минимум в 1 наноTON?
Check Your Understanding
Finished the lesson?
Mark it as complete to track your progress
Войдите чтобы оценить урок