Обучающие материалы

Логи событий и состояние смарт-контракта: в чём разница

Разбираем, что события Solidity записывают в журнал транзакций, где хранится текущее состояние контракта и как читать receipt, логи и запросы обозревателя без путаницы.

Логи событий и состояние смарт-контракта: в чём разница

Коротко: событие Solidity создаёт запись в логе транзакции, которую приложения и обозреватели могут индексировать и показывать как историю действий. Состояние контракта – это текущие значения, с которыми код работает при следующих вызовах. Событие помогает наблюдать, что контракт сообщил о выполнении, но само по себе не является переменной хранилища и не заменяет чтение состояния.

Что именно записывает событие

В Solidity разработчик объявляет событие и вызывает его из функции через emit. Например, токен ERC-20 обычно испускает Transfer при переводе: в событии указываются отправитель, получатель и количество. После включения транзакции узел помещает сведения о событии в журнал receipt. Клиенты могут фильтровать записи по адресу контракта и сигнатуре события, а затем собирать удобную ленту операций.

У записи есть адрес-источник, массив topics и поле data. Первый topic обычно содержит хеш сигнатуры события, а аргументы с пометкой indexed могут попасть в следующие topics. Неиндексированные значения кодируются в data. Это технический формат, который удобен для поиска и индексаторов, но не всегда читается человеком без ABI контракта. Одинаковые поля данных разных событий могут иметь разный смысл, поэтому проверяйте правильный адрес и версию ABI.

Логи историчны и привязаны к блоку, транзакции и позиции в блоке. Они подходят для уведомлений, графиков и приложений, которым нужно быстро находить действия по адресу. Документация Ethereum описывает события как способ выдавать сигналы внешним dApp через JSON-RPC. При этом события не являются обычной переменной, доступной контракту для последующего чтения во время исполнения.

Что называют состоянием контракта

Состояние – это значения в storage, которые контракт сохраняет между транзакциями: баланс адреса, владелец, параметры пула, разрешения или счётчик. Когда функция выполняется, код читает эти значения и может изменить их. Узел сохраняет итоговое состояние после выполнения транзакции в блоке. Запрос к storage отвечает на вопрос, какое значение действует на выбранной высоте, а лог события – какое сообщение контракт создал в конкретной операции.

Эти источники могут дополнять друг друга. Событие Transfer сообщает об операции и позволяет индексатору обновить историю, а storage отражает текущий баланс, который изменился после успешного перевода. Но не всякое обновление состояния обязано сопровождаться нужным событием, если контракт написан некорректно или использует нестандартную логику. И наоборот, запись события не доказывает автоматически, что читатель правильно интерпретировал адрес и сумму.

Внешний обозреватель часто отображает переводы токенов, NFT и вызовы контрактов на основании логов и собственных индексаторов. Сводка на странице удобна, но это представление сервиса. Для проверки можно перейти к receipt транзакции, изучить адрес контракта и соответствующие логи, затем сопоставить их с текущим состоянием через надёжный RPC или документацию проекта.

Как читать логи в receipt

Receipt появляется после включения транзакции в блок. В нём обычно есть статус выполнения, использованный gas и массив logs. Каждый лог связывается с контрактом, который его испустил, и включает topics и data. Сначала сравните адрес контракта с официальным адресом токена или приложения. Затем проверьте имя события, значения indexed-параметров и декодированные поля. Для токена одинаковый символ не подтверждает совпадение контракта.

Успешное выполнение транзакции может создать несколько событий от разных контрактов. Например, обмен через роутер может вызвать пул, токен и дополнительные контракты. Если интерфейс агрегирует всё в одну строку «обмен», receipt содержит более подробную последовательность. Следите за тем, где именно возник лог и какое действие его породило. Не делайте вывод о переводе только по текстовой подписи, нарисованной обозревателем.

Во время реорганизации блока лог может быть удалён из текущей канонической цепи. Некоторые RPC-подписки помечают такой лог как removed, после чего приложение должно скорректировать собственную историю. Поэтому до достаточного подтверждения событие может отображаться как предварительное. Техническая глубина подтверждений зависит от сети и сервиса, а финальность в rollup может иметь дополнительные этапы; их различие описано в статье о подтверждении L2 и финальности Ethereum.

Типичные ошибки при проверке

Первая ошибка – принимать название события за юридически или технически независимое доказательство. Смарт-контракт сам создаёт лог, а обозреватель интерпретирует его на основе ABI. Если код испускает событие до фактического изменения storage или записывает неполные данные, внешнее приложение может показать misleading картину. В обычном корректном токене стандартные события полезны, но для нестандартных контрактов нужно читать исходный код и проверенные описания.

Вторая ошибка – путать данные calldata с результатом операции. Calldata показывает аргументы, переданные в вызов, а logs – сообщения после исполнения. Состояние – результат изменений на выбранном блоке. Эти три слоя отвечают на разные вопросы: что пользователь запросил, какие события контракт испустил и какие значения в итоге сохранены. При неудачной транзакции входные данные могут присутствовать, но изменения состояния откатываются, а ожидаемых логов может не быть.

Третья ошибка – читать текущее состояние как доказательство всей истории. Баланс сегодня не говорит, сколько переводов его сформировало, а исторический лог не сообщает автоматически текущее разрешение spender. Если нужно проверить подписанное разрешение, сверяйте его отдельными методами контракта и полями подписи. Руководство по проверке EIP-712 помогает понять домен и параметры сообщения, а не подменять ими чтение логов.

Практический порядок анализа

  1. Найдите TxID и откройте его в обозревателе нужной сети.
  2. Проверьте receipt, статус, номер и hash блока.
  3. Сверьте адрес контракта каждого важного лога с официальными данными проекта.
  4. Раскройте topics и data, сопоставьте их с ABI и декодированными полями.
  5. При необходимости прочитайте состояние на том же или более позднем блоке.
  6. Если транзакция ещё свежая, дождитесь порога подтверждений, важного для вашего сценария.

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