Только в образовательных целях; не является инвестиционным советом или инвестиционной рекомендацией. Инвестиции могут привести к убыткам.
Краткий ответ
Calldata — неизменяемая строка байтов, передаваемая на вход транзакции Ethereum верхнего уровня или внутреннего вызова сообщения. Обычный вызов функции Solidity начинается с селектора 4-byte, за которым следуют аргументы в кодировке ABI. Однако calldata не является самоописываемой: одни и те же байты могут означать разное для различного исполняемого кода, реализаций прокси или схем. Функции fallback, низкоуровневый ассемблер и протоколы не на Solidity вообще не обязаны следовать стандартному ABI функций.
Поэтому кошелёк должен показывать больше, чем предполагаемое имя функции. Безопасная проверка связывает байты с chainId, блоком, from, to, нативным value, codeHash исполняемого кода, активной реализацией и доверенным ABI; строго декодирует каждый вложенный вызов; отличает calldata в сети от подписей EIP-712; симулирует в явно заданном состоянии; а после включения сверяет фактическую квитанцию и изменения состояния.
Завершение этой проверки не доказывает безопасность актива, транзакции или системы.
Как это работает
- Зафиксируйте конверт подписи и точку наблюдения:
chainId, номер и хеш блока,from,to, нативноеvalue, входные байты, nonce и поля комиссии. Сохраните источник данных кошелька или RPC: декодированная нагрузка из другой сети или другого блока подтверждает уже не то же самое. - Перед декодированием определите тип объекта. Транзакция, запрос типизированных данных EIP-712, permit ERC-2612, UserOperation ERC-4337 и обычное personal-sign сообщение используют разные домены и схемы; нельзя принудительно пропускать их все через ABI транзакций.
- Определите цель на зафиксированном блоке. Прочитайте исполняемый байткод и
codeHash; при необходимости выявите прокси, beacon или реализацию; запишите слоты реализации и администратора; получите ABI именно для этой версии кода. Реестр селекторов предлагает варианты, но не является доказательством. - Декодируйте строго. Селектор — первые
4 bytesKeccak-256 от канонической сигнатуры функции без типов возвращаемых значений. Статические значения занимают слова по32-byte, а заголовки динамических значений содержат смещения от блока аргументов после селектора. Отклоняйте усечённые данные, смещения за границами, невозможные длины, некорректное дополнение и необъяснимые конечные байты. - Рекурсивно раскройте multicall, вложенную calldata и делегированное выполнение. Для каждого дочернего вызова укажите цель, нативную стоимость, селектор, аргументы, тип вызова и флаг
allowFailure, если он есть. Приdelegatecallкод реализации выполняется в контексте адреса, баланса и хранилища вызывающего контракта, аmsg.senderиmsg.valueсохраняются. - Создайте отдельные реестры полномочий и стоимости, затем выполните симуляцию. Запишите получателей, операторов расходов, операторов NFT, сырые единицы токенов, десятичность, сроки, пределы проскальзывания и нативную стоимость. Симулируйте с точным блоком, отправителем и стоимостью, но считайте результат условным снимком: состояние, цены, время, код и порядок транзакций могут измениться.
- До подписи подтвердите каждое существенное поле. После включения проверьте статус квитанции, события, трассы при наличии, а также изменения балансов, разрешений и статуса операторов; отличайте перехваченные ошибки дочерних вызовов от успеха верхнего уровня; учитывайте gas даже при откате; дождитесь требуемой финальности; при необъяснимой ошибке остановитесь, а не подписывайте вслепую повторно.
Разобранные примеры
- Статический перевод ERC-20.
transfer(address,uint256)обычно использует селектор0xa9059cbb. Один селектор и два слова ABI занимают4 + 2 * 32 = 68 bytes. Сырое значение1,500,000для токена, у которого независимо подтверждено6 decimals, отображается как1.5 tokens. Число десятичных знаков — внешние метаданные контракта, в аргументах оно не закодировано; один селектор не определяет контракт или функцию однозначно. - Смещение динамических байтов. Для
f(address,bytes)с нагрузкой3-byteзаголовок из двух слов занимает64 bytes. Динамическое смещение равно0x40и отсчитывается от начала блока аргументов без селектора. Хвост содержит слово длины32-byteи дополненное слово данных32-byte, поэтому общий размер calldata равен4 + 64 + 32 + 32 = 132 bytes. Если ошибочно считать смещение абсолютным от нулевого байта, адрес окажется на четыре байта дальше. - Стоимость пакета зависит от реализации. Внешний вызов передаёт
1.00 ETH; три декодированных дочерних вызова явно требуют0.20 ETH,0.30 ETHи0.10 ETH, всего0.60 ETH. Оставшиеся0.40 ETHкод пакета может вернуть, удержать, переслать или из-за них откатиться. Если третий вызов завершается ошибкой приallowFailure=true, предыдущие могут остаться исполненными; атомарная реализация может откатить всё. - Permit во время подписи — не calldata ретранслятора. Владелец с
1,000 USDCподписывает permit ERC-2612 на300 USDCпри nonce41. Одна подпись не меняет ни баланс, ни разрешение. После успешной отправки ретранслятором nonce становится42, а разрешение —300; после расходования180баланс равен820, остаток разрешения —120. Отключение сайта не отзывает полномочие.
Риски
- Декодирование для неверной сети, развилки, метки блока или конверта транзакции.
- Подпись для поддельного домена, целевого адреса или получателя.
- Ошибочное предположение об уникальности селектора
4-byteпри возможных коллизиях. - Использование предполагаемого, устаревшего или неверно проверенного ABI.
- Доверие метке проверенного исходного кода без сверки с текущим
codeHash. - Пропуск смены реализации, beacon или администратора между проверкой и выполнением.
- Игнорирование функции прокси, чей селектор конфликтует с реализацией.
- Непонимание того, что
delegatecallзаписывает в хранилище вызывающего контракта. - Принятие некорректных динамических смещений, длин, дополнения или конечных байтов.
- Нераскрытый вложенный пакет, скрывающий цели, стоимость или полномочия.
- Предположение об атомарности, когда реализация перехватывает или допускает ошибки дочерних вызовов.
- Игнорирование нативного
valueверхнего уровня из-за безобидного вида аргументов токена. - Неверная десятичность или предположение, что токены с комиссией за перевод и ребейзингом стандартны.
- Неограниченное разрешение ERC-20 или неверная обработка гонки при его изменении.
- Игнорирование того, что NFT
setApprovalForAllраспространяется на всю коллекцию. - Принятие типизированных данных EIP-712 или permit ERC-2612 за calldata транзакции.
- Пропуск nonce, срока, проверяющего контракта, домена сети или границ защиты от повтора.
- Доверие симуляции вопреки изменениям oracle, времени, ожидающего состояния, MEV или кода.
- Принятие статуса квитанции, событий или трасс провайдера за полное доказательство экономического состояния.
- Слепая повторная подпись через скомпрометированный интерфейс или игнорирование включения, реорганизации и финальности.
Распространённые заблуждения
- Селектор функции однозначно определяет, что исполнит контракт.
- Сводка проверенного интерфейса совпадает с подписываемыми байтами и текущей реализацией.
- Транзакция с
value=0не может перемещать токены, NFT или делегированные активы. - Успех симуляции или квитанции доказывает безопасность и ожидаемый экономический результат.
- Отключение dapp отзывает разрешения, permit и полномочия операторов NFT.
Связанные темы
Источники
- Contract ABI Specification - Solidity Documentation (дата обращения: 2026-08-12)
- Introduction to Smart Contracts - Solidity Documentation (дата обращения: 2026-08-12)
- Transactions - Ethereum.org (дата обращения: 2026-08-12)
- ERC-20: Token Standard - Ethereum Improvement Proposals (дата обращения: 2026-08-12)
- EIP-712: Typed structured data hashing and signing - Ethereum Improvement Proposals (дата обращения: 2026-08-12)
- ERC-2612: Permit Extension for EIP-20 Signed Approvals - Ethereum Improvement Proposals (дата обращения: 2026-08-12)
- ERC-721: Non-Fungible Token Standard - Ethereum Improvement Proposals (дата обращения: 2026-08-12)
- ERC-1967: Proxy Storage Slots - Ethereum Improvement Proposals (дата обращения: 2026-08-12)