События смарт-контракта – это записи в transaction receipt, которые приложение оставляет во время успешного выполнения. По ним обозреватели строят историю переводов, обменов, голосований, смены владельца и обновлений proxy. Но event не является самостоятельной транзакцией и не доказывает правильность бизнес-логики.
Чтобы прочитать событие, нужны сеть, transaction hash, адрес контракта и ABI. Название вроде Transfer удобно для человека, а блокчейн хранит адрес emitter, массив topics и байты data. Обозреватель декодирует их только тогда, когда знает точное описание event.
Где найти logs транзакции
Откройте transaction hash в обозревателе правильной сети. Сначала проверьте status, номер блока, адрес отправителя и поле to. У pending-транзакции receipt ещё нет. У неуспешной транзакции состояние и созданные во время выполнения logs откатываются.
Затем найдите раздел transaction receipt, event logs или logs. Названия вкладок различаются у обозревателей, но исходные поля одинаковы: address, topics, data, logIndex, transaction hash и block number. Один вызов router может породить десятки событий от разных контрактов.
Не путайте to транзакции с emitter. Пользователь вызывает router, тот обращается к pool и token contracts, а каждый из них пишет собственные logs. Для каждого события главным ориентиром служит его поле address.
Из чего состоит event
В Solidity событие объявляется именем и набором типизированных параметров. Например, стандартная запись перевода имеет форму Transfer(address indexed from, address indexed to, uint256 value). Слово indexed определяет, какие значения попадают в topics и могут эффективно фильтроваться.
У обычного, не anonymous event элемент topics[0] содержит Keccak-256 от канонической сигнатуры. Имена переменных не входят в сигнатуру: используется строка вида Transfer(address,address,uint256). Следующие topics содержат indexed-параметры, а неиндексированные значения ABI-кодируются в data.
В одном log может быть не более четырёх topics. Для не anonymous event один занят сигнатурой, поэтому остаётся до трёх indexed-параметров. Anonymous event может использовать четыре indexed-поля, но у него нет сигнатуры в topic0, и определить тип только по имени невозможно.
Как декодировать topic0
Если обозреватель показывает название event, всё равно откройте raw view и сравните ABI. Один и тот же topic0 соответствует одной сигнатуре, но не конкретному проекту. Контракты ERC-20 и ERC-721 используют одинаковую сигнатуру Transfer(address,address,uint256). В токене ERC-20 последнее поле обычно amount, а в NFT – token ID.
Контекст задают emitting contract и его ABI. Сначала убедитесь, что адрес является ожидаемым токеном, pool или governance contract. Затем найдите event в verified source либо в ABI опубликованной реализации. Проверка исходного кода означает совпадение компиляции с bytecode, но не гарантирует отсутствие уязвимостей.
Если ABI не верифицирован, сигнатуру иногда удаётся сопоставить с публичными базами селекторов. Это гипотеза, а не доказательство. Разные объявления с одинаковыми типами декодируются одинаково, а смысл параметров определяется кодом.
Как читать indexed-поля
Статические значения размером до 32 байт помещаются в topic с дополнением слева. В адресе полезны последние 20 байт. uint256 читается как большое целое hex-число, а bytes32 может быть идентификатором, хешем или коротким закодированным значением.
Динамические типы – string, bytes, массивы и structs – при indexed-декларации хешируются. Исходную строку нельзя восстановить из topic. Можно лишь проверить известный кандидат, повторив нужное кодирование и хеширование. Если интерфейс уверенно показывает текст из одного такого topic, выясните, откуда он взял исходное значение.
Порядок важен. topics[1] соответствует первому indexed-параметру, а не первому параметру вообще. Неиндексированное поле между ними находится в data и не сдвигает порядок topics.
Как читать data
Поле data содержит ABI-кодирование всех non-indexed параметров в объявленном порядке. Простые типы занимают 32-байтовые слова. Динамические значения используют offsets, указывающие на область с длиной и содержимым. Делить длинную строку hex на куски недостаточно – нужно учитывать ABI types.
Для суммы токена сначала получите целое значение, затем примените decimals именно этого контракта. Запись 1000000 может означать один токен с шестью decimals или миллион единиц токена без decimals. Цена и денежная стоимость вообще не входят в стандартный Transfer.
Автоматический decoder экономит время, но результат нужно сверить с ABI. Особенно осторожно относитесь к tuples, массивам и событиям после upgrade. Старый ABI может красиво, но неверно интерпретировать новый формат.
Proxy и delegatecall
В upgradeable-системе пользователь вызывает proxy, а код implementation исполняется через delegatecall в контексте proxy. Поэтому event, вызванный логикой implementation, обычно имеет адрес proxy. Поиск logs только по адресу implementation пропустит пользовательскую историю.
Для декодирования нужны event definitions действовавшей implementation. Сначала проверьте proxy slots, событие Upgraded и блок смены версии по методике из статьи о proxy admin и implementation. Затем применяйте ABI той версии, которая работала в исследуемом блоке.
Diamond и модульные proxy ещё сложнее: один адрес вызывает код нескольких facets. Emitter остаётся общим адресом, а сигнатуры берутся из разных модулей. Название контракта, показанное обозревателем, может быть лишь ярлыком.
Что означают частые события
- Transfer. Передача ERC-20 или ERC-721; значение трактуется по стандарту и контракту.
- Approval. Изменение allowance или разрешения operator, а не фактическое движение актива.
- OwnershipTransferred. Смена владельца в конкретной реализации, но не обязательно всех административных ролей.
- RoleGranted и RoleRevoked. Изменение AccessControl для указанной роли и аккаунта.
- Upgraded. Новая implementation в распространённой proxy-модели.
- Swap, Mint, Burn. Действия пула, смысл полей зависит от версии DEX.
- ProposalCreated и VoteCast. Создание предложения и голосование, не его исполнение.
Zero address в from часто обозначает mint, а в to – burn, но полагаться только на обычай нельзя. Прочитайте код и изменение total supply. Некоторые токены отправляют активы на dead address без уменьшения supply.
Событие не равно состоянию
Event – сообщение, которое контракт сам решил записать. Код способен эмитировать событие с вводящими в заблуждение полями, не менять ожидаемую переменную или не эмитировать запись при важном действии. Смарт-контракты не могут читать прошлые logs как обычное storage, поэтому consensus опирается на состояние, а не на человеческое название события.
После декодирования вызовите read methods на нужном block tag: balanceOf, owner, getRoleMember, implementation или параметры позиции. Для анализа держателей методика приведена в статье о концентрации токенов.
Один event может быть промежуточным. Router получает токены, передаёт их pool, получает другой актив и отправляет пользователю. Отдельный Transfer на адрес router не доказывает конечную потерю – прочитайте все logs по logIndex и итоговые балансы.
Как искать события за период
JSON-RPC метод eth_getLogs фильтрует по диапазону блоков, address и позициям topics. Topics зависят от порядка: конкретное значение в первой позиции отличается от того же значения во второй. Внутри позиции можно задать несколько вариантов, а null оставить как wildcard.
Большой диапазон может превысить лимит RPC. Разбивайте запрос по блокам, сохраняйте block hash и обрабатывайте повторы. При реорганизации подписка способна прислать ранее увиденный log с признаком removed, а затем новое событие из канонической цепочки. Для важных действий ждите достаточную финальность.
Практический порядок проверки
- Сверьте сеть, transaction hash и status.
- Откройте raw receipt и выпишите каждый log по logIndex.
- Для события проверьте emitting address, а не только поле to транзакции.
- Найдите ABI текущего или исторического implementation.
- Сопоставьте topic0 с канонической сигнатурой.
- Разберите indexed topics и non-indexed data по типам.
- Примените decimals и различайте amount, token ID и role hash.
- Прочитайте итоговое storage на нужном блоке.
- Проверьте связанные события, внутренние вызовы и возможный upgrade.
Итог
Event log состоит из адреса emitter, topic0 с сигнатурой, indexed topics и ABI-кодированного data. Правильное чтение начинается с verified ABI и версии контракта, а заканчивается проверкой storage. Автоматическая подпись обозревателя полезна, но не является независимым доказательством.
Особое внимание требуется router-транзакциям, proxy, dynamic indexed-полям и одинаковым сигнатурам разных стандартов. События отлично показывают последовательность действий, но оценивать результат и риск нужно по коду, текущим ролям и фактическому состоянию сети.



