Перейти к содержанию

Custom errors в Solidity: собственные ошибки контракта

Редактор получает отказ публикации. Без ясного интерфейса он не знает, ошибся ли адресом, передал нулевой отпечаток или пытается повторить отключение. Собственные ошибки Solidity позволяют отделить эти причины и предоставить клиенту структурированный результат отката.

В реестре уже используются Unauthorized, InvalidCourse и NotActive. Разберём, почему их три, как они связаны с порядком проверок и какие сведения нельзя обещать интерфейсу при неизвестном ответе RPC.

Ошибка является частью интерфейса

Объявления в CourseCatalog:

error Unauthorized();
error InvalidCourse();
error NotActive();

Это не события и не обычные поля состояния. При revert Unauthorized() выполнение останавливается с закодированными данными ошибки. Клиент, у которого есть подходящий ABI, может сопоставить эти данные с объявлением и показать понятное сообщение.

Для нашей модели Unauthorized означает неверного отправителя либо недопустимый первоначальный адрес в конструкторе. InvalidCourse означает нулевой номер или отпечаток публикации. NotActive означает, что деактивировать нечего по текущему флагу. Такие значения следует описать рядом с интерфейсом, а не менять их смысл между страницами курса.

Можно добавить параметры ошибки, например номер курса. Это полезно для диагностики пакетных действий. Однако параметры также становятся частью сигнатуры и ABI. Если клиент использует старое описание, он может перестать распознавать новый вариант. Поэтому изменение ошибки относится к версии интерфейса.

Порядок определяет первое наблюдение

Сначала onlyPublisher проверяет отправителя, затем тело publish проверяет значения. Если читатель передаёт нулевой номер, ожидается Unauthorized, поскольку выполнение не дошло до проверки номера. Если издатель передаёт такой же номер, ожидается InvalidCourse.

R + publish(0,H1) → Unauthorized
P + publish(0,H1) → InvalidCourse
P + deactivate(17), если inactive → NotActive

Эти наблюдения помогают изолировать условия в будущем наборе проверок. Сценарий «проверить запрет нулевого номера» должен использовать допустимого издателя. Иначе он подтверждает только запрет чужого адреса и оставляет исследуемую строку необоснованной.

Для интерфейса порядок тоже важен. После изменения модификаторов первая ошибка может поменяться при нескольких одновременно неверных условиях. Поэтому сообщения должны отражать реально декодированные данные, а не угадывать причину по заполненности формы.

Строковое сообщение и структурированная причина

Традиционный require со строкой даёт читаемое описание, но большие строки могут влиять на артефакт и не создают удобного перечисления специальных случаев. Собственные ошибки помогают назвать причины компактно и связываются с ABI. Конкретную экономию в нашей лаборатории мы не измеряли, поэтому не будем обещать число единиц газа.

Выбор структурированной ошибки полезен ещё и потому, что пользовательская формулировка может меняться независимо от кода. Unauthorized в русской версии сайта показывается как «Этот адрес не является издателем». В другой локализации сообщение иное, но договор контракта остаётся прежним.

При этом название ошибки не является доказательством происхождения. В сложной композиции внешний контракт может вернуть байты, похожие на вашу ошибку. Нужно учитывать место отказа и доверие к вызываемому коду. У первого реестра нет чужих внешних вызовов, но клиент всё равно должен обращаться к известному экземпляру.

Неполный ответ не превращается в известную ошибку

RPC может вернуть транспортную ошибку, узел может не поддержать запрос, а приложение может использовать неверный ABI. Это не обязательно InvalidCourse. Если данных отката нет или они не распознаются, показывайте общий результат «вызов не удалось разобрать» с техническими сведениями для редактора.

Особенно нельзя считать любой отказ пользователя в кошельке ошибкой контракта. До подписи EVM ещё ничего не исполняла. Если сайт пишет Unauthorized после нажатия «Отмена», он объясняет несуществующее исполнение и затрудняет дальнейшую работу.

Источник отказа Что известно Корректная категория
Кошелёк до отправки Пользователь отказался Отмена запроса
RPC без данных исполнения Доставка или обслуживание не состоялись Ошибка соединения
Декодирован Unauthorized Условие адреса не выполнено Нет полномочий
Неизвестные bytes отката Есть отказ, причина не распознана Неизвестная ошибка исполнения

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

Как уточнить ошибку параметрами

Для отдельной версии инспектора можно объявить:

error InvalidCourseId(uint256 supplied);

function requireCourseId(uint256 courseId) external pure {
    if (courseId == 0) revert InvalidCourseId(courseId);
}

Это автономный учебный фрагмент и новый интерфейс, а не скрытая замена InvalidCourse базового реестра. Он показывает, как значение входит в данные ошибки. На входе 0 ожидается ошибка с параметром 0; ненулевой номер не вызывает этот отказ. Никакая запись курса здесь не создаётся.

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

Теперь ошибки представляют три осмысленных предусловия реестра. Их можно сопоставить с ожидаемыми сценариями и сообщениями сайта, не выдавая сетевой сбой за свойство курса. На следующей странице рассмотрим события, которые, в отличие от ошибок, сообщают об успешных изменениях истории. Ошибки Solidity описывают кодирование и доступные формы.

Что хранить в редакционном отчёте

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

Неизвестный результат отличается от известного отказа. Например, таймаут ожидания квитанции не содержит Unauthorized, даже если выбранный аккаунт кажется неподходящим. Для окончательного вывода потребуется фактическое исполнение либо другой подтверждённый результат. Предварительная проверка адреса остаётся полезной подсказкой, а не заменой данных.

При повторении действия сначала проверяют, не произошло ли оно уже. Особенно это важно для publish: прежняя попытка могла пройти, а повтор создаст новое событие. Ошибка интерфейса после отправки не должна автоматически приводить к повторной публикации без объяснения.

Название и параметры ошибки можно локализовать в сообщении сайта, сохранив исходную сигнатуру в подробностях. Так редактор видит понятную причину, а технический разбор не теряет точный идентификатор. Эти две формы полезны вместе, но ни одна не должна включать секретные ключи или произвольное содержимое кошелька.

Оглавление курса. Листинги, расчёты и наблюдения здесь разобраны как ожидаемое поведение; компиляция, учебные программы и проверки при написании не запускались.

Оглавление серии