Когда транзакция происходит в блокчейне, это редко бывает единичным действием. EVM-транзакция может переводить ETH, оплачивать комиссию за газ, инициировать одобрение токена и вызывать смарт-контракт — всё в одной атомарной операции. Если ваша система отслеживает только «деньги переведены от A к B», вы упускаете большую часть произошедшего.
Vilna разбивает каждую транзакцию на отдельные канонические события — по одному событию на каждое значимое действие. На этой странице объясняется модель событий, значение каждого типа события, как работают уведомления и как строить системы на их основе.
Блокчейн-транзакция — это контейнер. Внутри неё происходит множество вещей: перемещается стоимость, уплачиваются комиссии, выдаются разрешения, вызываются контракты. Vilna анализирует каждую транзакцию и извлекает каждое отдельное действие в самостоятельный объект события.
Каждое событие содержит:
kind— тип действия (transfer,fee,approval,call,contract_create)sequence— позиция (начиная с 0) внутри транзакции, чтобы вы знали порядок операцийdata— данные, специфичные для типа события: адреса, суммы и идентификаторы активов
Простой перевод ETH создаёт два события: transfer и fee. Обмен DeFi может создать пять и более: call к контракту-маршрутизатору, несколько событий transfer при перемещении токенов через пулы ликвидности и событие fee за газ.
Такая детализация позволяет вашей системе реагировать на то, что именно произошло, а не на приблизительную оценку.
Vilna нормализует всю ончейн-активность в пять канонических типов событий. Независимо от того, в каком блокчейне произошла транзакция, события всегда имеют одинаковую структуру.
Активы перемещены с одного адреса на другой.
| Поле | Описание |
|---|---|
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"
}Уплачена сетевая комиссия за обработку транзакции.
| Поле | Описание |
|---|---|
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" }
}Выдано или изменено разрешение на расходование токенов.
| Поле | Описание |
|---|---|
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" }
}Выполнена функция смарт-контракта.
| Поле | Описание |
|---|---|
caller | Адрес, инициировавший вызов |
target | Адрес контракта, который был вызван |
Пример сценария: Отслеживаемый адрес взаимодействует с неизвестным смарт-контрактом. Событие call сообщает вашей системе оценки рисков, что произошло взаимодействие с контрактом, даже если токены не перемещались напрямую. Это важно для обнаружения фишинговых контрактов, отслеживания позиций DeFi и ведения полного аудиторского журнала.
{
"kind": "call",
"caller": "0x742d35cc6634c0532925a3b844Bc9e7595f7B123",
"target": "0x1f9840a85d5af5bf1d1762f925bdaddc4201f984"
}Развёрнут новый смарт-контракт.
| Поле | Описание |
|---|---|
creator | Адрес, развернувший контракт |
created_address | Адрес нового развёрнутого контракта |
Пример сценария: Один из ваших отслеживаемых адресов развёртывает новый смарт-контракт. Для команд комплаенса развёртывание контракта — это значимое действие, которое должно быть зафиксировано и проверено. Событие contract_create точно фиксирует, кто, что и где развернул.
{
"kind": "contract_create",
"creator": "0x742d35cc6634c0532925a3b844Bc9e7595f7B123",
"created_address": "0x1f9840a85d5af5bf1d1762f925bdaddc4201f984"
}Многие решения для мониторинга блокчейна отслеживают только переводы токенов. Vilna отслеживает все пять типов событий, потому что реальные сценарии использования этого требуют.
Событие approval означает, что кто-то авторизовал третью сторону на расходование токенов с отслеживаемого адреса. Для криптобанка, биржи или кастодиального кошелька это критическое событие безопасности. Если злоумышленник получит доступ к одному из ваших адресов и выдаст себе неограниченное одобрение, система, отслеживающая только переводы, ничего не заметит до фактического перемещения токенов — когда будет уже поздно. Мониторинг одобрений даёт вам окно для раннего предупреждения.
Регуляторы ожидают полный аудиторский журнал. Журнал, содержащий только переводы, упускает взаимодействия с контрактами, которые перемещают стоимость косвенно. Депозиты в пулы ликвидности, операции стейкинга и вызовы управления DAO влияют на вашу экспозицию, не создавая простого события «A отправил X адресату B». События call и contract_create заполняют эти пробелы.
В DeFi стоимость перемещается через вызовы смарт-контрактов, а не только через простые переводы. Пользователь может вызвать маршрутизатор обмена, который затем инициирует три внутренних перевода через два пула ликвидности. Без события call вы видите появление и исчезновение токенов без объяснений. С ним у вас есть полный контекст: какой контракт был вызван, какой адрес его инициировал и какие переводы произошли в результате.
Разные блокчейны представляют одни и те же действия по-разному. Ethereum использует события ERC-20 Transfer; Tron использует события TRC-20; Bitcoin использует UTXO-входы и выходы. Vilna нормализует всё в одни и те же пять канонических типов событий. Ваш код обрабатывает перевод Ethereum и перевод Bitcoin одинаково — одни и те же имена полей, одна и та же структура, одна и та же логика.
Одна транзакция может содержать множество событий. Рассмотрим обмен токенов пользователем на DEX:
call— пользователь вызывает контракт-маршрутизатор обменаtransfer— входные токены пользователя перемещаются в пул ликвидностиtransfer— выходные токены перемещаются из пула к пользователюfee— уплачивается комиссия за газ
Если вы отслеживаете только переводы, вы увидите появление и исчезновение токенов, но упустите взаимодействие с контрактом, которое их вызвало. Вы также упустите комиссию, что сделает невозможным расчёт реальной стоимости операции.
Разные блокчейны имеют разные возможности. Вот что Vilna отслеживает для каждого семейства сетей:
| Возможность | EVM | Bitcoin | TRON | Solana |
|---|---|---|---|---|
transfer | Нативные + токены ERC-20 | BTC (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 проверяет, участвуют ли отслеживаемые адреса в событиях или активности транзакции.
- При обнаружении совпадения уведомление
transaction_alertотправляется через все настроенные каналы (вебхук и Telegram). Статус блока транзакции —processed(обнаружено, но ещё не подтверждено). - По мере добавления новых блоков транзакция накапливает подтверждения.
- Когда достигнуто необходимое количество подтверждений, отправляется ещё одно уведомление
transaction_alert. Статус блока теперьconfirmed. - Если происходит реорганизация блокчейна (редко, но возможно), статус блока меняется на
reorged. Блок обрабатывается заново из нового состояния цепочки.
Уведомления отправляются в два ключевых момента, и ваша система должна обрабатывать их по-разному:
| Уведомление | Статус блока | Скорость | Финальность | Применение |
|---|---|---|---|---|
transaction_alert | processed (обнаружено) | Быстро (секунды после блока) | Не финальное — может быть отменено реорганизацией | Обновления UI, предварительное отображение, оповещение людей |
transaction_alert | confirmed | Медленнее (минуты, зависит от сети) | Финальное — безопасно для действий | Зачисление на счета, запуск бизнес-логики, обновление балансов |
Проектируйте систему с учётом этого различия. Показывайте обнаруженные транзакции в вашем UI немедленно, чтобы пользователи видели отзывчивую обратную связь. Но не зачисляйте средства на счета, не инициируйте выплаты и не обновляйте бухгалтерские книги до получения уведомления о подтверждении. Такой двухфазный подход обеспечивает и скорость, и безопасность.
Показывайте обнаруженные уведомления (статус блока processed) в интерфейсе для отзывчивости, но никогда не зачисляйте средства, не запускайте выплаты и не обновляйте реестры до получения подтверждённого уведомления (статус блока confirmed).
Когда срабатывает уведомление, ваш вебхук получает TransactionAlertPayload — самодостаточный JSON-объект со всем необходимым для обработки события без дополнительных API-запросов.
Payload содержит три поля верхнего уровня:
| Поле | Описание |
|---|---|
item | Полный объект транзакции, включая все события и записи активности |
references | Справочные данные по токенам, блокчейнам и адресам, упоминаемым в транзакции |
is_test_message | true, если отправлено тестовым действием, false для реальных событий блокчейна |
Вот реалистичный 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 | Немедленно |
| 2 | 30 секунд |
| 3 | 2 минуты |
| 4 | 10 минут |
| 5 | 30 минут |
Если все пять попыток завершаются неудачей, событие отмечается как недоставленное. Одно недоставленное событие не отключает канал — временные сбои ожидаемы. Однако если эндпоинт систематически не отвечает, Vilna отключает канал, чтобы не расходовать ресурсы. Ответ 410 Gone немедленно отключает канал при первой же попытке.
Порядок доставки: Уведомления отправляются в порядке возникновения событий, но доставка не строго упорядочена. Если эндпоинт отвечает медленно, более позднее событие может прийти раньше завершения повторной попытки предыдущего. Используйте block_number и sequence из payload для восстановления правильного порядка на вашей стороне.
Хорошо спроектированный обработчик вебхуков надёжен, идемпотентен и быстр. Следуйте этим практикам, чтобы избежать распространённых ошибок.
Ваш эндпоинт вебхука должен возвращать HTTP 2xx как можно быстрее. Ставьте payload в очередь для асинхронной обработки вместо выполнения тяжёлых операций в обработчике запроса.
Верните HTTP 200 немедленно и поставьте данные в очередь для асинхронной обработки. Тяжёлые операции в обработчике запроса рискуют вызвать таймауты и пропуск доставок.
Когда проверка подписи вебхуков станет доступна (запланировано -- см. Аутентификация), каждая доставка будет включать заголовок 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 содержит метаданные токенов (название, символ, десятичные знаки) и информацию о блокчейне для каждого актива и сети, упомянутых в транзакции. Используйте эти данные напрямую вместо дополнительных 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-Timestamp | Unix-метка времени для предотвращения атак повторного воспроизведения (запланировано) |
X-Webhook-Idempotency-Key | Уникальный идентификатор доставки для дедупликации |
X-Webhook-Event | Тип события (transaction_alert или test) |