Перейти к основному контенту
Tech Path Finder
КурсыИнтервьюКод-ревьюБлог
Tech Path Finder

Персонализированный путеводитель в IT. Квизы, мок-интервью, код ревью и аналитика прогресса.

@potapov_me

Платформа

  • Курсы
  • Прогресс
  • Мок-интервью
  • Код ревью
  • Живое ревью с ИИ
  • Тренажёр переговоров
  • Закладки

Контент

  • Блог
  • Главная
  • Обратная связь

Компания

  • О проекте
  • Тарифы
  • Условия использования
  • Конфиденциальность
  • Согласие на обработку данных
  • Cookie
  • Реквизиты

Аккаунт

  • Войти
  • Зарегистрироваться
  • Профиль

© 2026 Tech Path Finder. Все права защищены.

·ИП Потапов К.С.·Политика конфиденциальности·
Сделано с ❤️ в России
  1. События и обработка ошибок
events_errors

События и обработка ошибок

Events, indexed-параметры, require/assert/revert, custom errors Solidity 0.8+

События и обработка ошибок

События — это единственный дешёвый способ записи данных из контракта; правильная обработка ошибок определяет, насколько понятен контракт при сбоях

#События (Events)

События позволяют контракту записывать информацию в лог транзакции. Лог не хранится в storage EVM (поэтому дешевле), но доступен через фильтрацию блокчейна — ноды, The Graph, ethers.js.

Типичные применения: уведомление фронтенда об изменениях, аудит-трейл для off-chain систем, реакция на действия (мониторинг), замена хранения данных в storage когда нужна только история.

Объявление и вызов события:

// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; contract Token { mapping(address => uint256) public balances; // Объявление события event Transfer(address indexed from, address indexed to, uint256 amount); event Minted(address indexed to, uint256 amount, string note); function mint(address to, uint256 amount) external { balances[to] += amount; emit Minted(to, amount, "Initial mint"); emit Transfer(address(0), to, amount); // Mint = transfer from zero address } }

Каждый emit записывает строку в лог транзакции. Это стоит газ (LOG0-LOG4 опкоды), но значительно дешевле SSTORE.

#Indexed параметры

Параметр с модификатором indexed индексируется и может использоваться для фильтрации событий по значению. Без indexed параметр записывается в data-часть лога и не фильтруется.

Ограничение: максимум 3 indexed параметра на событие. Типы: любые значимые типы, bytes32. Динамические типы (string, bytes) при индексировании хэшируются — хранится keccak256, а не сам текст.

Практический пример фильтрации в ethers.js (как это используется на фронтенде):

// Все переводы на конкретный адрес const filter = token.filters.Transfer(null, userAddress); const events = await token.queryFilter(filter, fromBlock, toBlock);

Именно поэтому from и to в ERC-20 Transfer событии помечаются indexed — это позволяет эффективно получать историю транзакций адреса.

#Обработка ошибок: require, assert, revert

Solidity предоставляет три механизма прерывания выполнения.

require(condition, message) — проверяет входные данные и предусловия. При ложном условии выбрасывает revert с сообщением. Нужен для: валидации аргументов, проверки прав доступа, проверки достаточности баланса. Возвращает неизрасходованный газ.

assert(condition) — проверяет инварианты, которые никогда не должны нарушаться. При ложном условии выбрасывает Panic(0x01). Используется для проверки логических ошибок в самом коде (не пользовательского ввода). Исторически assert потреблял весь газ — сейчас в 0.8.x тоже возвращает остаток газа.

revert(message) — безусловный откат с сообщением. Полезен для сложных условий, которые неудобно записать в require.

function withdraw(uint256 amount) external { // require: проверяем пользовательское условие require(amount > 0, "Amount must be positive"); require(balances[msg.sender] >= amount, "Insufficient balance"); balances[msg.sender] -= amount; // assert: проверяем инвариант — после списания баланс не должен быть больше начального assert(balances[msg.sender] <= type(uint256).max); (bool success, ) = payable(msg.sender).call{value: amount}(""); if (!success) revert("Transfer failed"); }

#Custom Errors (Solidity 0.8.4+)

Кастомные ошибки — эффективная альтернатива строковым сообщениям. Определяются как именованные типы, могут содержать параметры, кодируются компактно (4 байта selector + данные vs длинная строка).

Использование кастомных ошибок экономит газ при деплое и при revert (меньше данных в returndata) и улучшает читаемость кода.

// Объявление кастомных ошибок error Unauthorized(address caller, address owner); error InsufficientBalance(uint256 requested, uint256 available); error DeadlineExpired(uint256 deadline, uint256 current); contract Vault { address public owner; mapping(address => uint256) public balances; modifier onlyOwner() { if (msg.sender != owner) revert Unauthorized(msg.sender, owner); _; } function withdraw(uint256 amount) external { uint256 available = balances[msg.sender]; if (amount > available) revert InsufficientBalance(amount, available); balances[msg.sender] -= amount; payable(msg.sender).transfer(amount); } function timedAction(uint256 deadline) external { if (block.timestamp > deadline) revert DeadlineExpired(deadline, block.timestamp); // выполняем действие } }

Для декодирования кастомных ошибок в ethers.js или Tenderly нужен ABI контракта — ошибки попадают в него как обычные события. При работе с кастомными ошибками в тестах можно использовать vm.expectRevert(abi.encodeWithSelector(MyError.selector, param1, param2)) в Foundry.

#Паттерны обработки ошибок

Несколько практических правил, которые улучшают контракт:

Проверяйте условия в начале функции — до изменений state (Checks-Effects-Interactions). Если что-то пойдёт не так, газ на выполненные изменения не будет потрачен.

Используйте кастомные ошибки с параметрами вместо строковых сообщений в новом коде — это дешевле и информативнее для отладки.

При внешних вызовах всегда проверяйте success: (bool success, bytes memory data) = addr.call{value: amount}(""); и обрабатывайте !success через revert.

Никогда не глушите ошибки внешних вызовов без явной причины — если call вернул false, это означает, что что-то пошло не так на стороне получателя.

Далее: ООП: наследование и интерфейсы