API событий смарт-контрактов: чтение, фильтрация и подписка
Как работает API событий смарт-контрактов: логи, топики и декодирование ABI, прошлые события против живых, и когда использовать вебхуки, WebSockets или поллинг.

API событий смарт-контрактов позволяет вашему приложению узнать, что сделал контракт, не сканируя каждую транзакцию самостоятельно. Вы запрашиваете прошлые логи по адресу контракта и сигнатуре события, а затем подписываетесь на новые по мере прибытия блоков. И то, и другое можно сделать через сырой RPC узла, через библиотеку вроде ethers.js или через хостинговый API, который декодирует логи и отправляет их вам в виде вебхуков. Это руководство описывает, как работает каждый путь, где каждый из них ломается и как выбрать.
TL;DR
- Событие контракта — это запись лога, создаваемая при майнинге транзакции. Ваше приложение читает её после. Сам контракт не может её прочитать.
- Прошлые события приходят из запроса логов (
eth_getLogsилиqueryFilter). Живые события — из подписки или вебхука. Подписки узла не воспроизводят историю. - В качестве фильтров можно использовать только индексированные параметры. Декодирование требует ABI контракта.
- WebSocket-подписки умирают вместе с соединением и могут быть отброшены, если вы обрабатываете слишком медленно. Вебхуки с очередью и идемпотентными обработчиками лучше выживают при перезапусках.
- Хостинговый API устраняет работу с узлом, ABI и переподключениями. Именно это делает Chaingateway для платежей и токенных событий.
Оглавление
- Что такое событие смарт-контракта
- Что внутри лога события
- Чтение прошлых событий
- Подписка на новые события
- Реорги и подтверждения
- Вебхуки, WebSockets или поллинг
- Настройка мониторинга событий пошагово
- События в разных сетях
- Сделать самому или использовать хостинговый API
- Наш взгляд
- Получение событий без запуска узлов
- FAQ
- Источники
Что такое событие смарт-контракта
События — это способ контракта общаться с внешним миром. Согласно ethereum.org, события позволяют контракту взаимодействовать с фронтендом или другими подписывающимися приложениями, которые могут обрабатывать записанную информацию после того, как транзакция проверена и добавлена в блок.
Отсюда следуют три последствия, которые объясняют большую часть путаницы в результатах поиска:
- События — это вывод, а не хранилище, которое можно опросить из Solidity. Код контракта пишет логи, но не может их прочитать обратно. Именно поэтому темы с вопросом, как контракту получить доступ к прошлым событиям, всегда заканчиваются фразой «делайте это оффчейн».
- События появляются только после включения. Для транзакции, всё ещё находящейся в мемпуле, лога не существует.
- События дешевы в эмиссии и дешевы в поиске, поэтому ERC-20
TransferиApproval, минты NFT и свопы DEX отслеживаются через логи, а не через чтение состояния.
API событий смарт-контрактов — это всё, что даёт вашему бэкенду структурированный доступ к этим логам: JSON-RPC узла, библиотека-обёртка, индексатор или хостинговый сервис, присылающий вам вебхуки.
Что внутри лога события
Чтобы правильно фильтровать или декодировать логи, нужно знать их структуру. Согласно спецификации Solidity ABI, лог содержит адрес контракта, до четырёх топиков и сегмент данных. Анонимные события опускают хэш сигнатуры события.
Для обычного события:
- Топик 0 — это хэш Keccak-256 от сигнатуры события, например
Transfer(address,address,uint256). - Топики 1–3 хранят индексированные параметры.
- Data хранит неиндексированные параметры, закодированные по ABI.
ABI разделяет параметры на индексированные, которые хранятся как хэши топиков для эффективной фильтрации, и неиндексированные, которые хранятся как бинарные данные в логе.
Есть одна ловушка, о которой стоит знать. Та же спецификация (в выпуске v0.8.3) отмечает, что динамические индексированные параметры, такие как строки и байтовые массивы, хранятся как хэш Keccak от специального кодирования, а не как значение. Вы не можете восстановить исходную строку из лога. Вы можете только проверить, соответствует ли кандидатское значение.
Конкретный пример: ERC-20 Transfer
Лог перевода токена имеет топик 0 для сигнатуры Transfer, адрес отправителя в топике 1, адрес получателя в топике 2 и сумму в поле data. Если вы хотите отслеживать все депозиты на один адрес клиента, фильтруйте по топику 0 и топику 2. Мы подробно разбираем это в нашем руководстве по событию ERC-20 Transfer.

Чтение прошлых событий
Историческое чтение — это запрос, а не подписка. Стандартный метод JSON-RPC — eth_getLogs. Он принимает объект фильтра с диапазоном блоков, необязательным адресом контракта и необязательным массивом топиков. Справочник Coinbase Developer Documentation для eth_getLogs показывает структуру: fromBlock, toBlock и topics, а также отмечает, что хэш блока может заменить диапазон.
Библиотеки оборачивают это. В ethers.js contract.queryFilter(event, fromBlock, toBlock) возвращает соответствующие события, и документация ethers v5 по контрактам указывает, что фильтровать можно только индексированные параметры. Параметр, оставленный null, соответствует любому значению.
Практические правила для исторических запросов:
- Всегда ограничивайте диапазон. Многие провайдеры ограничивают охват блоков или количество результатов за вызов. Перебирайте диапазоны порциями (например, по несколько тысяч блоков) и уменьшайте порцию вдвое при ошибке.
- Фильтруйте как минимум по адресу и топику 0. Нефильтрованные запросы логов — самый быстрый способ упреться в лимит частоты.
- Сохраняйте последний обработанный блок. Эта контрольная точка позволяет возобновить работу после сбоя без пробелов и дублей.
- Декодируйте с правильным ABI. Если у вас есть только топик 0, вы можете найти сигнатуру события, но прокси-контракт может эмитить события, определённые в его реализации, поэтому используйте ABI реализации.
Если хотите освежить знания об общении с нодами напрямую, наше руководство по JSON-RPC с примерами на PHP, Python и JavaScript охватывает формат запроса.
Подписка на новые события
Работа в реальном времени устроена иначе, и именно здесь терпят неудачу многие первые реализации.
В документации go-ethereum по событиям в реальном времени объясняется, что подписки используют постоянные WebSocket- или IPC-соединения, обычно через eth_subscribe, и передают события по мере их обработки узлом. Важны два ограничения:
- Нет воспроизведения. Уведомления подписок охватывают только текущие события, а не прошлые. Если ваш процесс был остановлен на десять минут, подписка не выдаст то, что вы пропустили. Пропуски заполняются запросом логов.
- Привязка к соединению. Подписки привязаны к одному соединению. Когда оно закрывается, закрываются и все его подписки.
На той же странице указано, что узлы буферизируют уведомления, и если ваш клиент не успевает их обрабатывать, соединение может быть закрыто при достижении лимита буфера (в примере Geth — 10 000). Медленный обработчик, выполняющий запись в базу данных встроенно, — как раз путь к этому.
Надежный паттерн
Сочетайте оба механизма:
- При запуске прочитайте ваш контрольный блок (checkpoint).
- Дозаполните данные от контрольного блока до текущей головы цепочки с помощью
eth_getLogs, порциями. - Откройте подписку и дедуплицируйте события по хешу транзакции плюс индексу лога.
- Помещайте каждое событие в очередь и сразу подтверждайте получение. Обрабатывайте его в воркере.
- При разрыве соединения возвращайтесь к шагу 1.
Это остается корректным даже при обрыве соединения, который неизбежно произойдет.
Реорганизации и подтверждения
Лог, увиденный в последнем блоке, может исчезнуть при реорганизации цепочки. Для любых операций, связанных с деньгами, не зачисляйте средства пользователю при первом обнаружении. Ждите глубину подтверждений, подходящую для данной цепочки и суммы, и обрабатывайте флаг removed, который Ethereum-совместимые узлы добавляют к логам из осиротевших блоков. Сохраняйте идентификатор обработанного события и делайте свой обработчик идемпотентным, чтобы повторное или исправленное событие не могло зачислить средства дважды.

Вебхуки, WebSockets или поллинг
Все три способа могут доставлять события. Они отличаются тем, кто удерживает соединение и кто несет ответственность за режимы сбоев.
| Подход | Как это работает | Сильная сторона | Слабая сторона |
|---|---|---|---|
| Поллинг | Ваш код вызывает eth_getLogs по таймеру от последней контрольной точки | Просто, без сохранения состояния, легко возобновить | Задержка равна вашему интервалу, и это стоит запросов |
| WebSocket-подписка | Постоянное соединение передает логи от узла | Наименьшая задержка | Умирает вместе с соединением, нет воспроизведения, лимиты буфера |
| Вебхук | Сервис следит за цепочкой и отправляет HTTP POST на вашу точку входа | Не нужно поддерживать долгое соединение, работает с бессерверными функциями | Нужен публичный эндпоинт, проверка подписей и обработка дубликатов |
Поллинг подходит для низконагруженных бэк-офисных задач. WebSockets удобны для дашбордов или ботов, которым нужно реагировать в течение блока и которые могут терпеть пробелы. Вебхуки подходят для платежных потоков, где нужен один надежный пуш на событие и очередь воркеров за ним. В нашей статье о паттерне прием, очередь, разветвление для вебхуков против WebSockets разбор компромиссов дан подробнее.
Правильная обработка доставки вебхуков
Какой бы сервис ни отправлял вебхук, принимающий конец строите одинаково:
- Проверяйте подпись перед разбором тела запроса.
- Быстро возвращайте 2xx и обрабатывайте асинхронно.
- Делайте обработчики идемпотентными, используя хеш транзакции и индекс лога в качестве ключа.
- Ожидайте повторные попытки и доставку не по порядку.
- Логируйте сбои, чтобы позже можно было сверяться с запросом логов.
Поведение при повторных попытках — отдельная тема для проектирования. Для более глубокого погружения в проектирование с учетом повторов и пропущенных уведомлений см. статью об обработке повторов платежных вебхуков.
Пошаговая настройка мониторинга событий
Вот минимальный путь для одного EVM-контракта, независимо от используемого инструмента.
- Получите ABI. Из верифицированного исходного кода контракта или из собственного артефакта сборки. Без него вы увидите только сырые топики и hex-данные.
- Выберите события. Составьте список сигнатур, которые вас интересуют, например
Transfer,Approvalили ваше собственноеOrderPaid. - Определите фильтры. Адрес контракта, топик 0 и любые индексированные параметры, такие как получатель. Помните, что фильтровать можно только индексированные значения.
- Выполните бэкфилл. Запросите данные от блока развертывания или от даты начала бизнеса, порциями.
- Перейдите в продакшн. Подпишитесь через WebSocket или зарегистрируйте вебхук.
- Декодируйте и нормализуйте. Преобразуйте hex-суммы с учетом decimals токена, и сохраняйте номер блока, хеш транзакции, индекс лога и адрес контракта.
- Ждите финальности. Помечайте события как ожидающие (pending), затем как подтвержденные.
- Мониторьте монитор. Настройте алерты, если событие не поступало дольше нормального интервала для данной цепи, или если ваша контрольная точка отстает от головы цепи.
События в разных сетях
Модель EVM с топиками и данными, декодируемыми через ABI, применима к Ethereum, Polygon, BNB Chain и Arbitrum, поэтому один декодер работает во всех них с разными RPC-эндпоинтами и правилами подтверждений. Другие сети не используют эту модель. В Bitcoin нет контрактных логов вовсе, поэтому вы отслеживаете адреса и транзакции. У Tron и Solana свои форматы и API. Некоторые стеки в стиле Cosmos снова отличаются: Tendermint v0.36 устарело его легаси-стриминговый API в пользу API логов событий, который клиенты опрашивают длинным поллингом с курсорами.
Практический итог: мультичейн-продукт в итоге поддерживает по одному адаптеру на семейство сетей, каждый со своей логикой переподключения, декодирования и финальности. Именно эту часть большинство команд недооценивает. Для обзора вариантов см. наше сравнение шести типов блокчейн-API и объяснение блокчейн-API против запуска собственной ноды.
Собирать стек самостоятельно или использовать хостинговый API
Запуск собственного стека означает ноду (или платный RPC-эндпоинт), сканер логов, реестр ABI, обработчик реоргов, очередь и настройку алертинга. Это оправдано, если события — ваш основной продукт или вам нужен кастомный индексинг по тысячам контрактов.
Это менее оправдано, если события — лишь средство для цели, например зачисление депозита или подтверждение заказа. В таком случае вам нужны декодированные события, доставляемые на ваш эндпоинт, и больше ничего эксплуатировать не требуется.
Наш взгляд
Большинство багов в обработке событий, которые мы видим, не связаны с чтением логов. Они касаются того, что происходит вокруг: пропущенное уведомление во время деплоя, двойное зачисление после повтора, неверное преобразование decimals для одного токена, соединение, которое тихо умирает ночью.
Мы строим продукт вокруг трёх убеждений. Во-первых, приложение должно получать декодированные, структурированные данные, а не необработанные топики, которые приходится интерпретировать самостоятельно. Во-вторых, пуш лучше поллинга для платежей, пока каждое сообщение подписано и может быть верифицировано. В-третьих, разработчику не должно приходиться запускать ноды или поддерживать в актуальном состоянии эндпоинты для конкретных цепочек, лишь чтобы узнать, что платёж зачислен.
Именно поэтому Chaingateway предоставляет один REST API для Ethereum, Bitcoin, Tron, Solana, Polygon, BNB Chain и Arbitrum с предварительно декодированными данными транзакций, автоматической оценкой газа и HMAC-подписанными запросами. Вебхуки работают поверх этого, поэтому ваш обработчик получает событие, а ваша команда может сосредоточиться на продукте. Что бы вы ни выбрали, сохраняйте чекпоинт, ключ идемпотентности и правило подтверждений. Именно эти три фактора определяют, верны ли ваши цифры.
— Bitblade
Получение событий без запуска нод
Если ваша цель — события платежей и токенов в приложении, наши Вебхуки Chaingateway доставляют уведомления блокчейна в реальном времени на ваш эндпоинт, а Blockchain API предоставляет декодированные данные транзакций, кошельки, переводы и оценки комиссий через единый интерфейс. Тарифы начинаются от €49 в месяц за Plus, Pro — €99, Premium — €249 и Enterprise — €499 в месяц. При годовой оплате — €490, €990, €2 490 и €4 990 соответственно. Подробности о том, что включает каждый уровень, см. на странице цен. Для примера, специфичного для цепочки, прочитайте о вебхуках Polygon без поллинга.
Источники
- Anatomy of smart contracts, ethereum.org
- Contract ABI Specification, Solidity v0.8.22
- Contract ABI Specification, Solidity v0.8.3
- Real-time Events, go-ethereum
- eth_getLogs, Coinbase Developer Documentation
- Contract, ethers v5 documentation
- Subscribing to Events, Tendermint Core
Рекомендуем
Часто задаваемые вопросы
Нет. Контракты могут эмитировать логи, но не имеют доступа к ним после этого, поэтому данные событий предназначены для оффчейн-потребителей. Если вашему контракту нужны исторические данные ончейн, сохраняйте значение в состоянии контракта, хотя это и обойдётся в более высокий газ.
Выполните запрос логов от последнего обработанного блока до текущей головы цепочки. Подписки на ноды отправляют только текущие события, поэтому переподключение не воспроизводит упущенное. Сохраняйте чекпоинт после каждого обработанного блока и дедуплицируйте по хешу транзакции и индексу лога.
Только индексированные параметры хранятся как топики, и только по топикам можно фильтровать. Неиндексированные значения находятся в поле data, поэтому вам нужно получить логи и отфильтровать их в собственном коде. Если вы контролируете контракт, пометьте поля, по которым будете искать, как indexed — до трёх на событие.
Для платежей вебхуки с очередью обычно проще в эксплуатации, так как вам не нужно удерживать соединение, которое может разорваться. Используйте WebSockets, когда важна реакция за доли секунды и вы можете дозаполнить пробелы. В обоих случаях обрабатывайте события идемпотентно и ждите достаточное количество подтверждений перед зачислением средств.
Готовы собрать это сами? Получите свой API-ключ — 7-дневный пробный период, без карты — или посмотрите API блокчейна для полного справочника по эндпоинтам.