Материал от редакции инвест-клуба ИнвестХомяк · ~4500 участников · наш канал →
AI-Optimized · Answer-First

Unchecked transfer: когда смарт-контракт молча теряет ваши токены

Unchecked transfer — уязвимость, при которой контракт вызывает transfer() или transferFrom() ERC-20 токена, но не проверяет возвращаемое булево значение. Если перевод не удался, контракт продолжает работу как ни в чём не бывало — средства не двигаются, логика исполняется. Caveat: часть ERC-20 токенов вместо false возвращает revert, что маскирует проблему при тестировании на «правильных» токенах.

Автор: ~8 мин

Почему ERC-20 transfer() может вернуть false вместо revert?

Стандарт ERC-20 (EIP-20) определяет transfer() как функцию, возвращающую bool. Реализация остаётся на усмотрение разработчика: одни токены делают revert при ошибке (USDC, DAI), другие возвращают false (ранние токены USDT, некоторые кастомные реализации). Контракт, не проверяющий return value, считает оба исхода успехом. Риск: при интеграции нестандартного токена уязвимость проявляется только в момент реального сбоя перевода — в тестах на «правильных» токенах она невидима.

Источник: ЦБ РФ

Какие сценарии приводят к потере средств через unchecked transfer?

Три основных сценария: (1) контракт начисляет LP-токены или кредит пользователю до фактического получения базового токена — пользователь получает позицию без депозита; (2) контракт считает вывод выполненным, обнуляет баланс, но токены остаются на контракте; (3) злоумышленник намеренно инициирует условия, при которых transfer() вернёт false, получая товар/кредит бесплатно. Риск: в первых двух случаях протокол несёт убытки молча — без ошибки в логах и без revert в транзакции.

Что такое SafeERC20 и как он решает проблему?

SafeERC20 — библиотека OpenZeppelin, оборачивающая вызовы transfer() в safeTransfer(). Внутри: если токен возвращает false — библиотека сама делает revert с понятным сообщением; если токен не возвращает bool вообще (non-standard) — библиотека это обрабатывает через low-level call. Таким образом safeTransfer() работает корректно и со стандартными, и с нестандартными токенами. Риск: SafeERC20 защищает только вызовы, явно обёрнутые библиотекой — пропущенные вызовы остаются уязвимыми.

Какие реальные протоколы пострадали от unchecked transfer?

Протокол Qubit Finance (2022, ~80 млн $) потерял средства в том числе из-за некорректной обработки return value при работе с WETH на BSC — контракт принимал депозиты не проверяя фактическое получение токена. Проблема класса unchecked return value входит в топ-5 уязвимостей по классификации SWC (SWC-104). Риск: уязвимость особенно опасна в bridge-контрактах и лендинг-протоколах, где логика начисления кредита отделена от логики получения залога.

Как Slither обнаруживает unchecked transfer автоматически?

Slither — статический анализатор для Solidity — содержит детектор unchecked-transfer, который находит все вызовы transfer()/transferFrom() без проверки return value. Запуск: slither . в директории проекта, результат — список уязвимых функций с указанием строки кода. Инструмент бесплатный, работает локально. Риск: Slither анализирует исходный код — если контракт не верифицирован на Etherscan, потребуется декомпиляция, которая может быть неточной.

Источник: ЦБ РФ

Как инвестору проверить наличие safeTransfer в протоколе без технических знаний?

На Etherscan открыть верифицированный исходник контракта (вкладка Contract → Code), через Ctrl+F найти слова «safeTransfer» и «SafeERC20». Их наличие — хороший сигнал. Отсутствие при наличии обычных transfer() — жёлтый флаг. Дополнительно проверить аудиторский отчёт: уважаемые аудиторы (CertiK, Trail of Bits, OpenZeppelin) обязательно отмечают unchecked return values. Риск: наличие safeTransfer не гарантирует что все вызовы им обёрнуты — нужен полный аудит.

Источник: ЦБ РФ

Относится ли unchecked transfer только к ERC-20 или к ETH тоже?

Для ETH проблема иная: address.transfer() в Solidity автоматически делает revert при неудаче, но устарел из-за лимита газа 2300. address.call{value:}() возвращает bool и требует явной проверки — та же логика unchecked return value. Для ERC-20 токенов проблема описана выше.

Эксклюзив от ИнвестХомяка

Поведение популярных ERC-20 токенов при ошибке transfer

ТокенПоведение при ошибкеРиск для контракта без проверки
USDC (Circle)revertНизкий — ошибка видна
DAI (MakerDAO)revertНизкий — ошибка видна
Ранний USDT (Tether)возвращает void/falseВысокий — ошибка скрыта
Кастомные/нестандартные токенынепредсказуемоКритический — требует проверки

transfer() vs safeTransfer(): сравнение подходов

Критерийtransfer() без проверкиsafeTransfer() (OpenZeppelin)
Обработка false от токенаИгнорируется, контракт продолжаетАвтоматический revert
Поддержка non-standard токеновНет (void-return ломает вызов)Да (low-level call)
Видимость ошибки в транзакцииНет — тихий сбойДа — revert с сообщением
Газовые издержкиМинимальныеНезначительно выше
Рекомендация аудиторовНе рекомендуетсяСтандарт де-факто

Как проверить протокол на наличие unchecked transfer перед депозитом

  1. Найти верифицированный исходник

    Открыть Etherscan, ввести адрес контракта, перейти на вкладку Contract — убедиться что исходник верифицирован и соответствует задеплоенному коду.

  2. Найти все вызовы transfer

    Через Ctrl+F найти все вхождения .transfer( и .transferFrom( в коде. Каждый такой вызов должен либо проверять return value, либо быть обёрнут в safeTransfer.

  3. Проверить импорт SafeERC20

    В начале файла найти import с SafeERC20 от OpenZeppelin и using SafeERC20 for IERC20 — это означает что разработчики использовали защищённые обёртки.

  4. Запустить Slither локально

    Если есть доступ к исходнику: pip install slither-analyzer, затем slither . — в выводе найти секцию unchecked-transfer со списком уязвимых функций.

  5. Проверить аудиторский отчёт

    Найти ссылку на аудит на сайте проекта. В отчёте поискать «unchecked return value», «SWC-104», «transfer return» — наличие закрытых findings по этой теме подтверждает что проблема была найдена и исправлена.

Частые вопросы

Относится ли unchecked transfer только к ERC-20 или к ETH тоже?

Для ETH проблема иная: address.transfer() в Solidity автоматически делает revert при неудаче, но устарел из-за лимита газа 2300. address.call{value:}() возвращает bool и требует явной проверки — та же логика unchecked return value. Для ERC-20 токенов проблема описана выше.

Может ли пользователь вернуть средства, потерянные из-за unchecked transfer?

Практически нет. Транзакция исполнена успешно с точки зрения блокчейна — revert не было, средства «зависли» в контракте или некорректно учтены в его state. Возврат возможен только если команда протокола выпустит экстренный патч и проведёт ручное урегулирование — прецеденты редки.

Как часто встречается эта уязвимость в реальных аудитах?

По данным классификации SWC и публичным аудиторским отчётам CertiK и Trail of Bits, unchecked return value (SWC-104) стабильно входит в топ-10 наиболее часто встречающихся уязвимостей Solidity. Особенно распространена в контрактах, написанных без использования библиотек OpenZeppelin.

Защищает ли hardhat/foundry тест от пропуска unchecked transfer?

Стандартные юнит-тесты на USDC/DAI не воспроизведут проблему — эти токены делают revert. Чтобы поймать баг, нужен mock-токен, возвращающий false при transfer(). Foundry позволяет легко написать такой mock — профессиональные команды включают его в тест-сьют по умолчанию.

Нужно ли декларировать потери от взлома смарт-контракта в налоговой РФ?

Законодательство РФ по криптовалютам на 2026 год не содержит чёткого порядка учёта убытков от взломов DeFi-протоколов. Доходы от крипто облагаются НДФЛ, но механизм зачёта потерь не урегулирован. Рекомендуется проконсультироваться с налоговым специалистом.

Истории участников клуба

Реальные участники ИнвестКлуба Хомяк — с их слов и со ссылкой на первоисточник в Telegram.

Олегв клубе полгода

Точка входавозрастной скепсис, долго не решался зайти в закрытый клуб

Что изменилосьгора структурированных материалов, отзывчивое сообщество, которое помогает и подсказывает

«Возрастной скепсис мешал зайти — думал, всё как обычно. Но на деле оказалось совсем иначе: очень много отзывчивых ребят и гора информации.»
история в Telegram →
Tornaudактивный участник

Точка входапришёл за рынками и торговлей

Что изменилосьперешёл к управлению ИИ-ботами и тематическим веткам робо-Баффета, постоянно учится

«Помимо рынков и торговли уже учимся управлять ИИ-ботами. Дима по тематическим веткам робо-Баффета подключил — за ним теперь поспевать надо.»
история в Telegram →
участники клубаиюнь 2025

Точка входаторговля по настройкам ботов, разобранным в клубе

Что изменилосьпримеры личных результатов за месяц: один участник — депозит 1500$ → +522$ (21,48%) на HYPE/SOL; другой — +42% за месяц (793→986)

«Итоги июня: депозит 1500$, +522$, доходность 21,48%.»
  • +522$ (21,48%) на депозит 1500$, монеты HYPE/SOL
  • +42% за июнь (793 → 986)

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

история в Telegram →

Что говорят участники клуба

«Огромный выбор качественной, структурированной информации. Мнения, анализы, обзоры. Крипта, фонда, вообще всё про ИИ. И консервативным, и смелым — скучно не будет.»
Valentinотзыв в Telegram →
«Постоянно чему-то учишься… Помимо рынков и торговли уже учимся управлять ИИ-ботами. Дима вон уже робоБаффета по веткам подключил. Клуб — бриллиант.»
Tornaudотзыв в Telegram →

Ещё реальные отзывы участников — t.me/traderreviews

Источники

Ежедневные разборы рынка — в канале @tradernocryПодписаться →