Перейти к содержимому
Последнее обновление

События и мониторинг транзакций

Когда транзакция происходит в блокчейне, это редко бывает единичным действием. EVM-транзакция может переводить ETH, оплачивать комиссию за газ, инициировать одобрение токена и вызывать смарт-контракт — всё в одной атомарной операции. Если ваша система отслеживает только «деньги переведены от A к B», вы упускаете большую часть произошедшего.

Vilna разбивает каждую транзакцию на отдельные канонические события — по одному событию на каждое значимое действие. На этой странице объясняется модель событий, значение каждого типа события, как работают уведомления и как строить системы на их основе.

Что такое события

Блокчейн-транзакция — это контейнер. Внутри неё происходит множество вещей: перемещается стоимость, уплачиваются комиссии, выдаются разрешения, вызываются контракты. Vilna анализирует каждую транзакцию и извлекает каждое отдельное действие в самостоятельный объект события.

Каждое событие содержит:

  • kind — тип действия (transfer, fee, approval, call, contract_create)
  • sequence — позиция (начиная с 0) внутри транзакции, чтобы вы знали порядок операций
  • data — данные, специфичные для типа события: адреса, суммы и идентификаторы активов

Простой перевод ETH создаёт два события: transfer и fee. Обмен DeFi может создать пять и более: call к контракту-маршрутизатору, несколько событий transfer при перемещении токенов через пулы ликвидности и событие fee за газ.

Такая детализация позволяет вашей системе реагировать на то, что именно произошло, а не на приблизительную оценку.

Типы событий

Vilna нормализует всю ончейн-активность в пять канонических типов событий. Независимо от того, в каком блокчейне произошла транзакция, события всегда имеют одинаковую структуру.

Транзакция

transfer
активы перемещены

fee
газ оплачен

approval
разрешение на расход

call
контракт вызван

contract_create
новый контракт

transfer

Активы перемещены с одного адреса на другой.

ПолеОписание
asset_gidИдентификатор CAIP-19 переведённого актива
fromАдрес отправителя
toАдрес получателя
amountСумма перевода (base + formatted)
sourceИсточник перевода: native (на уровне сети), contract (контракт токена) или internal (внутренняя транзакция)
token_idИдентификатор токена для NFT (ERC-721/ERC-1155), если применимо

Пример сценария: Пользователь отправляет 1 000 USDT на кошелёк мерчанта. Vilna генерирует событие transfer с указанием отправителя, получателя, токена и точной суммы. Ваша система обнаружения депозитов сопоставляет получателя с клиентом и зачисляет средства на его счёт.

{
  "kind": "transfer",
  "asset_gid": "eip155:1/erc20:0xdAC17F958D2ee523a2206206994597C13D831ec7",
  "from": "0x742d35cc6634c0532925a3b844Bc9e7595f7B123",
  "to": "0x8ba1f109551bd432803012645ac136c0d1e5c400",
  "amount": { "base": "1000000000", "formatted": "1000.0" },
  "source": "contract"
}

fee

Уплачена сетевая комиссия за обработку транзакции.

ПолеОписание
asset_gidИдентификатор CAIP-19 актива комиссии (всегда нативная валюта сети)
payerАдрес, оплативший комиссию
amountСумма комиссии (base + formatted)

Пример сценария: Каждая транзакция Ethereum стоит газ. Вашей бухгалтерской системе необходимо отслеживать расходы на комиссии отдельно от переводов, чтобы рассчитать реальную стоимость каждой транзакции. Событие fee предоставляет точную уплаченную сумму без ручных расчётов.

{
  "kind": "fee",
  "asset_gid": "eip155:1/slip44:60",
  "payer": "0x742d35cc6634c0532925a3b844Bc9e7595f7B123",
  "amount": { "base": "2100000000000000", "formatted": "0.0021" }
}

approval

Выдано или изменено разрешение на расходование токенов.

ПолеОписание
asset_gidИдентификатор CAIP-19 одобренного токена
ownerАдрес, выдающий разрешение
spenderАдрес, получающий разрешение на расходование
amountОдобренный лимит расходования (base + formatted)
token_idИдентификатор токена для одобрений NFT (ERC-721/ERC-1155), если применимо

Пример сценария: Пользователь одобряет маршрутизатор DEX для расходования своих USDT. Для кастодиальной платформы это критическое событие безопасности — кто-то авторизовал третью сторону на перемещение токенов с отслеживаемого адреса. Ваша система комплаенса должна отмечать и проверять неограниченные одобрения.

{
  "kind": "approval",
  "asset_gid": "eip155:1/erc20:0xdAC17F958D2ee523a2206206994597C13D831ec7",
  "owner": "0x742d35cc6634c0532925a3b844Bc9e7595f7B123",
  "spender": "0x1f9840a85d5af5bf1d1762f925bdaddc4201f984",
  "amount": { "base": "115792089237316195423570985008687907853269984665640564039457584007913129639935", "formatted": "unlimited" }
}

call

Выполнена функция смарт-контракта.

ПолеОписание
callerАдрес, инициировавший вызов
targetАдрес контракта, который был вызван

Пример сценария: Отслеживаемый адрес взаимодействует с неизвестным смарт-контрактом. Событие call сообщает вашей системе оценки рисков, что произошло взаимодействие с контрактом, даже если токены не перемещались напрямую. Это важно для обнаружения фишинговых контрактов, отслеживания позиций DeFi и ведения полного аудиторского журнала.

{
  "kind": "call",
  "caller": "0x742d35cc6634c0532925a3b844Bc9e7595f7B123",
  "target": "0x1f9840a85d5af5bf1d1762f925bdaddc4201f984"
}

contract_create

Развёрнут новый смарт-контракт.

ПолеОписание
creatorАдрес, развернувший контракт
created_addressАдрес нового развёрнутого контракта

Пример сценария: Один из ваших отслеживаемых адресов развёртывает новый смарт-контракт. Для команд комплаенса развёртывание контракта — это значимое действие, которое должно быть зафиксировано и проверено. Событие contract_create точно фиксирует, кто, что и где развернул.

{
  "kind": "contract_create",
  "creator": "0x742d35cc6634c0532925a3b844Bc9e7595f7B123",
  "created_address": "0x1f9840a85d5af5bf1d1762f925bdaddc4201f984"
}

Зачем отслеживать больше, чем переводы

Многие решения для мониторинга блокчейна отслеживают только переводы токенов. Vilna отслеживает все пять типов событий, потому что реальные сценарии использования этого требуют.

Безопасность

Событие approval означает, что кто-то авторизовал третью сторону на расходование токенов с отслеживаемого адреса. Для криптобанка, биржи или кастодиального кошелька это критическое событие безопасности. Если злоумышленник получит доступ к одному из ваших адресов и выдаст себе неограниченное одобрение, система, отслеживающая только переводы, ничего не заметит до фактического перемещения токенов — когда будет уже поздно. Мониторинг одобрений даёт вам окно для раннего предупреждения.

Комплаенс

Регуляторы ожидают полный аудиторский журнал. Журнал, содержащий только переводы, упускает взаимодействия с контрактами, которые перемещают стоимость косвенно. Депозиты в пулы ликвидности, операции стейкинга и вызовы управления DAO влияют на вашу экспозицию, не создавая простого события «A отправил X адресату B». События call и contract_create заполняют эти пробелы.

Видимость DeFi

В DeFi стоимость перемещается через вызовы смарт-контрактов, а не только через простые переводы. Пользователь может вызвать маршрутизатор обмена, который затем инициирует три внутренних перевода через два пула ликвидности. Без события call вы видите появление и исчезновение токенов без объяснений. С ним у вас есть полный контекст: какой контракт был вызван, какой адрес его инициировал и какие переводы произошли в результате.

Мультичейн-консистентность

Разные блокчейны представляют одни и те же действия по-разному. Ethereum использует события ERC-20 Transfer; Tron использует события TRC-20; Bitcoin использует UTXO-входы и выходы. Vilna нормализует всё в одни и те же пять канонических типов событий. Ваш код обрабатывает перевод Ethereum и перевод Bitcoin одинаково — одни и те же имена полей, одна и та же структура, одна и та же логика.

Полная картина

Одна транзакция может содержать множество событий. Рассмотрим обмен токенов пользователем на DEX:

БлокчейнПул ликвидностиРоутер DEXПользовательБлокчейнПул ликвидностиРоутер DEXПользователь1 транзакция → 4 события1. вызов (свап)2. перевод (входные токены)3. перевод (выходные токены)4. комиссия (газ)
  1. call — пользователь вызывает контракт-маршрутизатор обмена
  2. transfer — входные токены пользователя перемещаются в пул ликвидности
  3. transfer — выходные токены перемещаются из пула к пользователю
  4. fee — уплачивается комиссия за газ

Если вы отслеживаете только переводы, вы увидите появление и исчезновение токенов, но упустите взаимодействие с контрактом, которое их вызвало. Вы также упустите комиссию, что сделает невозможным расчёт реальной стоимости операции.

Что отслеживается для каждого семейства сетей

Разные блокчейны имеют разные возможности. Вот что Vilna отслеживает для каждого семейства сетей:

ВозможностьEVMBitcoinTRONSolana
transferНативные + токены ERC-20BTC (UTXO-входы/выходы)Нативные + токены TRC-20Нативные + токены SPL
feeКомиссии за газКомиссии майнеровКомиссии за bandwidth/energyКомиссии за транзакции
approvalОдобрения ERC-20Н/ДОдобрения TRC-20Н/Д
callВызовы смарт-контрактовН/ДВызовы смарт-контрактовВзаимодействия с программами
contract_createРазвёртывание контрактовН/ДРазвёртывание контрактовРазвёртывание программ

EVM-сети (Ethereum, Polygon, Arbitrum, BSC и др.) обладают наиболее полным мониторингом. Все пять типов событий полностью поддерживаются, включая внутренние транзакции и события NFT ERC-721/ERC-1155.

Bitcoin поддерживает события transfer и fee. Bitcoin не имеет смарт-контрактов в понимании EVM, поэтому approval, call и contract_create неприменимы. Переводы Bitcoin моделируются из UTXO-входов и выходов.

TRON похож на EVM — поддерживает все пять типов событий для токенов TRC-20 и взаимодействий со смарт-контрактами.

Solana поддерживает события transfer и fee, а также отслеживание взаимодействий на уровне программ для событий call.

Полный и актуальный список поддерживаемых сетей и их возможностей доступен через GET /blockchains в Platform API.

Жизненный цикл уведомлений

Когда транзакция затрагивает один из ваших отслеживаемых адресов, Vilna отправляет уведомления на определённых этапах жизненного цикла. Понимание этого жизненного цикла необходимо для построения надёжных систем.

Процесс

Ваша системаVilnaБлокчейнВаша системаVilnaБлокчейнloop[Подтверждения]Новый блокПарсинг транзакцийИзвлечение событийСопоставление адресовtransaction_alert (обнаружено — быстро, не финально)Блок +1, +2, ... +NПодсчёт подтвержденийtransaction_alert (подтверждено — надёжно)
  1. Транзакция включается в блок в блокчейне.
  2. Vilna обнаруживает блок, анализирует каждую транзакцию и извлекает канонические события.
  3. Vilna проверяет, участвуют ли отслеживаемые адреса в событиях или активности транзакции.
  4. При обнаружении совпадения уведомление transaction_alert отправляется через все настроенные каналы (вебхук и Telegram). Статус блока транзакции — processed (обнаружено, но ещё не подтверждено).
  5. По мере добавления новых блоков транзакция накапливает подтверждения.
  6. Когда достигнуто необходимое количество подтверждений, отправляется ещё одно уведомление transaction_alert. Статус блока теперь confirmed.
  7. Если происходит реорганизация блокчейна (редко, но возможно), статус блока меняется на reorged. Блок обрабатывается заново из нового состояния цепочки.

Обнаружение vs. подтверждение

Уведомления отправляются в два ключевых момента, и ваша система должна обрабатывать их по-разному:

УведомлениеСтатус блокаСкоростьФинальностьПрименение
transaction_alertprocessed (обнаружено)Быстро (секунды после блока)Не финальное — может быть отменено реорганизациейОбновления UI, предварительное отображение, оповещение людей
transaction_alertconfirmedМедленнее (минуты, зависит от сети)Финальное — безопасно для действийЗачисление на счета, запуск бизнес-логики, обновление балансов

Проектируйте систему с учётом этого различия. Показывайте обнаруженные транзакции в вашем UI немедленно, чтобы пользователи видели отзывчивую обратную связь. Но не зачисляйте средства на счета, не инициируйте выплаты и не обновляйте бухгалтерские книги до получения уведомления о подтверждении. Такой двухфазный подход обеспечивает и скорость, и безопасность.

Не действуйте по обнаруженным событиям

Показывайте обнаруженные уведомления (статус блока processed) в интерфейсе для отзывчивости, но никогда не зачисляйте средства, не запускайте выплаты и не обновляйте реестры до получения подтверждённого уведомления (статус блока confirmed).

Уведомления через вебхуки на практике

Когда срабатывает уведомление, ваш вебхук получает TransactionAlertPayload — самодостаточный JSON-объект со всем необходимым для обработки события без дополнительных API-запросов.

Структура payload

Payload содержит три поля верхнего уровня:

ПолеОписание
itemПолный объект транзакции, включая все события и записи активности
referencesСправочные данные по токенам, блокчейнам и адресам, упоминаемым в транзакции
is_test_messagetrue, если отправлено тестовым действием, false для реальных событий блокчейна

Пример payload

Вот реалистичный payload вебхука для перевода 1 000 USDT в сети Ethereum. Транзакция содержит два события (перевод токена и комиссия за газ) и одну запись активности, показывающую чистое влияние на отслеживаемый адрес:

{
  "item": {
    "chain_gid": "eip155:1",
    "txid": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
    "block_number": 19500000,
    "is_success": true,
    "confirmed_at": "2025-03-15T10:30:00Z",
    "events": [
      {
        "txid": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
        "sequence": 0,
        "kind": "transfer",
        "data": {
          "kind": "transfer",
          "asset_gid": "eip155:1/erc20:0xdAC17F958D2ee523a2206206994597C13D831ec7",
          "from": "0x742d35cc6634c0532925a3b844Bc9e7595f7B123",
          "to": "0x8ba1f109551bd432803012645ac136c0d1e5c400",
          "amount": { "base": "1000000000", "formatted": "1000.0" },
          "source": "contract"
        }
      },
      {
        "txid": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
        "sequence": 1,
        "kind": "fee",
        "data": {
          "kind": "fee",
          "asset_gid": "eip155:1/slip44:60",
          "payer": "0x742d35cc6634c0532925a3b844Bc9e7595f7B123",
          "amount": { "base": "2100000000000000", "formatted": "0.0021" }
        }
      }
    ],
    "activity": [
      {
        "chain_gid": "eip155:1",
        "txid": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
        "address": "0x8ba1f109551bd432803012645ac136c0d1e5c400",
        "asset_gid": "eip155:1/erc20:0xdAC17F958D2ee523a2206206994597C13D831ec7",
        "direction": "in",
        "delta": { "base": "1000000000", "formatted": "1000.0" },
        "created_at": "2025-03-15T10:30:00Z"
      }
    ]
  },
  "references": {
    "tokens": {
      "eip155:1/erc20:0xdAC17F958D2ee523a2206206994597C13D831ec7": {
        "gid": "eip155:1/erc20:0xdAC17F958D2ee523a2206206994597C13D831ec7",
        "name": "Tether USD",
        "symbol": "USDT",
        "decimals": 6
      },
      "eip155:1/slip44:60": {
        "gid": "eip155:1/slip44:60",
        "name": "Ethereum",
        "symbol": "ETH",
        "decimals": 18
      }
    },
    "blockchains": {
      "eip155:1": {
        "gid": "eip155:1",
        "name": "ethereum",
        "short_name": "eth"
      }
    },
    "addresses": {
      "0x8ba1f109551bd432803012645ac136c0d1e5c400": "merchant_wallet"
    }
  },
  "is_test_message": false
}

Ключевые моменты:

  • Массив events содержит все канонические события транзакции. Вы можете фильтровать по kind, чтобы обрабатывать только нужные типы событий.
  • Массив activity показывает чистое влияние на баланс ваших отслеживаемых адресов. Каждая запись указывает, какой адрес был затронут, какой актив изменился, направление (in или out) и точную дельту.
  • Объект references позволяет получить символы токенов, названия, количество десятичных знаков и метаданные блокчейна без обращения к API. Всё, что нужно для отображения и обработки, содержится в payload.

Маршрутизация уведомлений за пределы вебхуков

Vilna нативно доставляет уведомления через вебхуки и Telegram. Вебхук — это просто HTTP POST, и вы можете маршрутизировать эти данные куда угодно.

Очереди сообщений — Направьте URL вебхука на лёгкий прокси, который ставит сообщения в очередь RabbitMQ, AWS SQS или Google Pub/Sub. Это разделяет приём событий и их обработку.

Slack / Discord — Обработчик вебхуков может переформатировать payload и перенаправить его на URL входящих вебхуков Slack или Discord. Полезно для команд, которым нужны человекочитаемые оповещения в чат-каналах.

PagerDuty / OpsGenie — Перенаправляйте события высокой значимости (крупные переводы, неожиданные одобрения, развёртывания контрактов) в системы оповещения для эскалации дежурных.

Пользовательские бэкенды — Любая система, принимающая HTTP POST, может получать события Vilna. Направьте вебхук на ваш внутренний API, серверless-функцию или эндпоинт приёма данных.

Нативные адаптеры для Slack, Discord и популярных очередей сообщений находятся в разработке. Если вам нужна конкретная интеграция, обращайтесь на support@vilna.io.

Доставка вебхуков и повторные попытки

Когда Vilna отправляет уведомление через вебхук, ожидается, что ваш эндпоинт вернёт статус-код 2xx в течение 10 секунд. Доставка считается неуспешной, если соединение не может быть установлено, превышен тайм-аут ответа или эндпоинт возвращает статус-код 4xx/5xx.

Неуспешные доставки повторяются по расписанию:

ПопыткаЗадержка
1Немедленно
230 секунд
32 минуты
410 минут
530 минут

Если все пять попыток завершаются неудачей, событие отмечается как недоставленное. Одно недоставленное событие не отключает канал — временные сбои ожидаемы. Однако если эндпоинт систематически не отвечает, Vilna отключает канал, чтобы не расходовать ресурсы. Ответ 410 Gone немедленно отключает канал при первой же попытке.

Порядок доставки: Уведомления отправляются в порядке возникновения событий, но доставка не строго упорядочена. Если эндпоинт отвечает медленно, более позднее событие может прийти раньше завершения повторной попытки предыдущего. Используйте block_number и sequence из payload для восстановления правильного порядка на вашей стороне.

Проектирование обработчика вебхуков

Хорошо спроектированный обработчик вебхуков надёжен, идемпотентен и быстр. Следуйте этим практикам, чтобы избежать распространённых ошибок.

Отвечайте немедленно, обрабатывайте позже

Ваш эндпоинт вебхука должен возвращать HTTP 2xx как можно быстрее. Ставьте payload в очередь для асинхронной обработки вместо выполнения тяжёлых операций в обработчике запроса.

Лучшая практика

Верните HTTP 200 немедленно и поставьте данные в очередь для асинхронной обработки. Тяжёлые операции в обработчике запроса рискуют вызвать таймауты и пропуск доставок.

POST

200 OK

Vilna

Ваш эндпоинт

Очередь

Воркер

Бизнес-логика

Проверяйте подпись

Когда проверка подписи вебхуков станет доступна (запланировано -- см. Аутентификация), каждая доставка будет включать заголовок X-Webhook-Signature. Проверяйте его перед обработкой, чтобы убедиться, что payload отправлен Vilna и не был подделан.

Сформируйте подписываемые данные, объединив X-Webhook-Timestamp, точку (.) и необработанное тело запроса. Вычислите HMAC-SHA256, используя ваш секрет вебхука, и сравните результат с заголовком подписи. См. Аутентификация — Проверка подписи вебхука для примеров реализации.

Дедупликация с помощью ключа идемпотентности

Используйте заголовок X-Webhook-Idempotency-Key для обнаружения повторных доставок. Сохраняйте обработанные ключи в базе данных или кеше и пропускайте любой ключ, который вы уже обрабатывали. Это критически важно для финансовых операций — вы никогда не захотите зачислить средства на счёт дважды за одну и ту же транзакцию.

Отклоняйте устаревшие сообщения

Проверяйте X-Webhook-Timestamp относительно текущего времени. Отклоняйте события старше 5 минут для предотвращения атак повторного воспроизведения.

Фильтрация по типу события

Если вашу систему интересуют только переводы, фильтруйте массив events по kind === "transfer" и игнорируйте остальное. Это сохраняет логику обработки сфокусированной:

const transfers = payload.item.events.filter(
  (event) => event.kind === "transfer"
);

for (const event of transfers) {
  // Process only transfer events
  await processTransfer(event.data);
}

Используйте объект references

Поле references содержит метаданные токенов (название, символ, десятичные знаки) и информацию о блокчейне для каждого актива и сети, упомянутых в транзакции. Используйте эти данные напрямую вместо дополнительных API-запросов:

const tokenGid = event.data.asset_gid;
const token = payload.references.tokens[tokenGid];
console.log(`Received ${event.data.amount.formatted} ${token.symbol}`);

Сводка заголовков вебхуков

ЗаголовокНазначение
X-Webhook-SignatureДайджест HMAC-SHA256 для проверки payload (запланировано)
X-Webhook-TimestampUnix-метка времени для предотвращения атак повторного воспроизведения (запланировано)
X-Webhook-Idempotency-KeyУникальный идентификатор доставки для дедупликации
X-Webhook-EventТип события (transaction_alert или test)

Дополнительные материалы