Почему вызов контракта срывается после успешной симуляции

2026-09-03

Почему вызов контракта срывается после успешной симуляции

Кошелёк показывает предпросмотр обмена, называет сумму, которую вы получите, и не сообщает ни о каких проблемах. Вы подписываете, а транзакция попадает в блок как неудачная и всё равно списывает газ. Предпросмотр вас не обманул. Он ответил на вопрос об одном моменте, а ваша транзакция была исполнена в другом.

Почему вызов контракта срывается после успешной симуляции: ключевые моменты

Что на самом деле выполняет симуляция

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

Из этого описания следуют два свойства, и оба пригодятся дальше. Холостой прогон исполняется относительно выбранного блока, поэтому его ответ приколот к состоянию на этом блоке. И он выполняется в одиночку: между вызовом и результатом ничего другого не исполняется.

Состояние, на котором вы симулировали, — не то, в которое вы попадаете

От предпросмотра до исполнения транзакции нужно проделать путь. Её подписывают, транслируют в сеть, держат в мемпуле, затем производитель блока выбирает её, и только тогда она исполняется по правилам того блока, который её содержит. Каждый шаг этого пути занимает время, а сеть на это время не останавливается.

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

Минимальный выход и дедлайн: проверки, которые срабатывают намеренно

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

Допустим, предпросмотр называет 10 000 USDC за продаваемые вами токены, а ваш допуск выставлен на 0,5 %. Тогда вызов несёт в себе границу 9 950 USDC, и контракт получает указание отказаться от всего взаимодействия, вместо того чтобы выдать меньше. Когда цена уходит дальше вашего допуска, пока транзакция в пути, защита делает ровно то, о чём вы её просили. Дедлайн работает так же: вызов, который висит неподтверждённым дольше собственной отметки времени, отклоняется по прибытии, хотя тот же самый вызов прошёл бы несколькими минутами раньше.

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

Порядок: та же транзакция на другом месте

Блок — это последовательность, и порядок транзакций определяет, какое взаимодействие видит какое состояние. Ваш предпросмотр поставил вызов в начало пустой очереди. Блок ставит его после всего, что туда поместил производитель, и эти соседи могут израсходовать ту самую ликвидность, тот самый лимит расходования или тот самый остаток предложения, на которые рассчитывал ваш вызов.

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

Газ: оценка — это не бронь

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

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

Когда симуляция симулировала не ту транзакцию, которую вы отправили

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

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

Что показал предпросмотр Что изменилось к моменту исполнения Куда смотреть
Названную сумму на выходе Сдвинулись резервы или ответ оракула Аргумент минимального выхода в подписанном вызове
Действительный вызов Подписанная отметка времени истекла Аргумент дедлайна и время ожидания подтверждения
Доступное предложение или ликвидность Другая транзакция в блоке забрала это раньше Позиция вашей транзакции внутри её блока
Оценку газа Исполнение пошло по более длинной ветке Израсходованный газ против лимита газа в квитанции
Чистый холостой прогон Кошелёк был в другой сети или на другом узле Идентификатор сети и точка доступа при подписании

Как это выглядит после неудачи

Взаимодействие, остановившееся таким образом, записывается. Оно занимает позицию в блоке, расходует газ, а его квитанция несёт поле статуса: EIP-658 заменил промежуточный корень состояния в квитанции кодом статуса, в котором ноль означает неудачу, а единица — успех. Обозреватели превращают это поле в метку откат.

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

Что действительно снижает долю неудач

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

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

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

Итог

Симуляция отвечает на вопрос, что произошло бы, если бы этот вызов выполнился сейчас, в одиночку, на этом блоке. Исполнение в сети отвечает на вопрос, что произошло, когда вызов выполнился позже, среди других транзакций, на другом блоке. Неудача после чистого предпросмотра — это расстояние между двумя вопросами, а подписанные вами защиты превращают это расстояние в остановку, а не в плохую цену исполнения. Проверьте аргументы в подписанном вызове, позицию транзакции внутри её блока и израсходованный газ против лимита газа: причина будет в одном из трёх. Чтобы продолжать изучать основы, читайте другие материалы Bitbase Academy.

Похожие материалы

Другие материалы Bitbase по этой теме:

- Как безопасно сменить RPC-эндпоинт

- Максимальная комиссия за транзакцию превышена: что означает это предупреждение кошелька

- Превышен лимит запросов RPC и что с этим делать

- FIFO и LIFO в расчёте себестоимости крипто

- Меметическая премия: почему мемы влияют на цены

Дисклеймер: эта статья — образовательный материал Bitbase Academy, только для информационных целей. Она не является инвестиционным, торговым, налоговым или финансовым советом. Криптоактивы волатильны — оценивайте риски самостоятельно. Написано в сентябре 2026 года; сверяйтесь с актуальной официальной информацией.

Источники

[1] Документация для разработчиков Ethereum.org, JSON-RPC API (eth_call, eth_estimateGas) ethereum.org

[2] Ethereum Improvement Proposals, EIP-658: Embedding transaction status code in receipts eips.ethereum.org

[3] Документация для разработчиков Ethereum.org, «Транзакции» (Transactions) ethereum.org