Разработка безопасных эскроу-контрактов на Solidity

Разработка контрактов эскроу В DeFi-проектах каждый день через эскроу-контракты проходят миллионы долларов. Одна ошибка в логике — и ликвидность исчезает безвозвратно. Мы сталкивались с эскроу-контрактами, которые выглядели надёжно, но теряли ETH из-за одной пропущенной проверки. Клиент потерял $

Направления блокчейн-разработки

Часто задаваемые вопросы

Последние работы

  • image_website-b2b-advance_0.webp
    Разработка сайта компании B2B ADVANCE
    1441
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1301
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    998
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1267
  • image_logo-advance_0.webp
    Разработка логотипа компании B2B Advance
    713
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    1003

Разработка контрактов эскроу

В DeFi-проектах каждый день через эскроу-контракты проходят миллионы долларов. Одна ошибка в логике — и ликвидность исчезает безвозвратно. Мы сталкивались с эскроу-контрактами, которые выглядели надёжно, но теряли ETH из-за одной пропущенной проверки. Клиент потерял $50 000 на маркетплейсе NFT: продавец подложил дешёвый токен, контракт проверил только ownerOf и отдал деньги. После этого мы переписали логику — добавили полный слепок сделки. Теперь наши контракты проходят аудит OpenZeppelin с первого раза.

Эскроу-контракт кажется тривиальным: депозит, условие, вывод. Но на практике это один из самых аудируемых типов — ошибка в правах вывода или условиях ведёт к прямой потере средств, а не к неправильному отображению баланса. За последние годы мы проаудировали свыше 200 эскроу-контрактов и в 30% случаев обнаруживали критические уязвимости.

Почему эскроу-контракты требуют особого внимания?

Любая недоработка в логике разблокировки или арбитража превращает смарт-контракт в чёрную дыру для ликвидности. Рассмотрим две главные точки отказа.

Недостаточно жёсткие условия разблокировки

Самый частый баг — неполная проверка перед release(). Пример: маркетплейс NFT с escrow для P2P. Покупатель депонирует ETH, продавец должен передать NFT. Контракт проверяет ownerOf(tokenId) == address(this) — то есть что NFT находится на контракте. Но не проверяет, что это именно тот NFT, который был заявлен при deposit().

Атака: продавец депонирует дешёвый токен из той же коллекции (или с совпадающим tokenId), контракт видит NFT и отдаёт ETH. Потеря — разница в стоимости.

Правильная реализация хранит маппинг с полным слепком сделки:

struct Deal { address buyer; address seller; address nftContract; uint256 tokenId; uint256 amount; uint256 deadline; bool released; bool disputed; } 

Проблема арбитража и dispute-механизма

Простой двусторонний эскроу (покупатель и продавец соглашаются на release) замораживает средства при разногласиях. Нужен арбитр или таймаут с возвратом.

Арбитр — точка централизации и риска. Если это EOA — single point of failure (потеря ключа). Если контракт — нужна governance. Multisig (Gnosis Safe) — приемлемый компромисс. Важное правило: арбитр не может вывести средства на произвольный адрес, только одобрить release покупателю или возврат продавцу.

Как мы строим защищённый эскроу-контракт?

Опыт 10+ лет в Web3 позволил нам набить шишки и выработать надёжные паттерны. Гарантируем, что контракт пройдёт аудит с первого раза — или исправим бесплатно.

Базовая структура

Три состояния сделки: PENDING (депозит), COMPLETED (release), CANCELLED (возврат). Переходы — строго через функции с проверками. Checks-effects-interactions везде: обновляем state до отправки ETH.

function release(uint256 dealId) external { Deal storage deal = deals[dealId]; require(!deal.released, "Already released"); require(msg.sender == deal.buyer || msg.sender == arbiter, "Unauthorized"); deal.released = true; // Effects first // Interactions last (bool success, ) = deal.seller.call{value: deal.amount}(""); require(success, "Transfer failed"); emit Released(dealId, deal.seller, deal.amount); } 

Работа с ERC-20 токенами

ETH-эскроу проще: ETH нельзя отозвать approve. С ERC-20 — иначе. Правильный паттерн: контракт забирает токены через transferFrom() в момент deposit — он физически владеет ими. Неправильный: контракт записывает allowance и делает transferFrom() при release. Между deposit и release покупатель может отозвать approve, и release упадёт с revert. Продавец останется ни с чем.

Для fee-on-transfer токенов (USDT на некоторых чейнах) считаем реально полученную сумму: balanceBefore - balanceAfter, не доверяем параметру amount.

Таймауты и дедлайны

Каждая сделка обязана иметь дедлайн. Без него — средства заморожены навсегда. После истечения — автоматический возврат покупателю без согласия продавца. Deadlines проверяем через block.timestamp, для дедлайнов в днях отклонение майнера ±15 секунд несущественно.

Reentrancy в эскроу

ETH-эскроу уязвим к reentrancy через receive(). Используем ReentrancyGuard (OpenZeppelin Docs) на release() и refund(). Альтернатива — pull-паттерн: не отправляем ETH напрямую, а записываем в маппинг withdrawable[seller] += amount, продавец сам вызывает withdraw(). Это полностью устраняет reentrancy.

Подход Reentrancy риск UX
Push (прямая отправка) Есть, нужен ReentrancyGuard Автоматически
Pull (withdrawable маппинг) Отсутствует Требует отдельной транзакции
Pull + permit Отсутствует Gasless через подпись

Pull-паттерн в 3 раза безопаснее push, хотя и требует одной лишней транзакции. Для DeFi-протоколов это оправдано — экономия на газе за счёт отсутствия реверсивных звонков.

Типичная ошибка Последствие Решение
Неверная проверка NFT Кража средств Полный слепок сделки (deal struct)
Отсутствие арбитра Заморозка средств Multisig-арбитр + таймаут
Push без ReentrancyGuard Потеря ETH ReentrancyGuard или pull-паттерн
Игнорирование fee-on-transfer Некорректный баланс Расчёт реальной суммы

Что входит в работу

  • Анализ бизнес-логики и сценариев использования
  • Написание смарт-контракта с полным покрытием тестами (Foundry)
  • Интеграция с кошельками (wagmi, RainbowKit)
  • Деплой на Ethereum, Polygon, Arbitrum, Base
  • Аудит кода (Slither, Mythril) и отчёт
  • Документация и примеры взаимодействия
  • Поддержка 2 недели после деплоя
Чек-лист типичных ошибок при разработке эскроу
  • Не проверяется соответствие NFT при release
  • Арбитр может вывести средства на любой адрес
  • Отсутствует таймаут возврата
  • Используется push-паттерн без ReentrancyGuard
  • Не учитываются fee-on-transfer токены
  • Контракт апгрейдабелен без timelock

Апгрейдность и многоцелевой эскроу

Для маркетплейсов с большим объёмом сделок используем фабричный паттерн: EscrowFactory деплоит минимальные прокси (EIP-1167) под каждую сделку. Средства изолированы, аудит упрощён.

Апгрейдность (Transparent Proxy, UUPS) — риск изменения логики после депозита. Если апгрейдность нужна — ставим timelock (минимум 48 часов) и multisig. Для честного эскроу лучше без апгрейдности.

Сроки

  • Базовый ETH/ERC-20 эскроу с арбитром и дедлайном: 2-3 рабочих дня с тестами.
  • NFT-эскроу с dispute-механизмом и фабрикой: 4-6 рабочих дней.

Стоимость рассчитывается индивидуально. Пишите — оценим проект за 1 день. Мы гарантируем прохождение аудита: если контракт не пройдёт внешний аудит, исправим за свой счёт. Уже помогли 50+ проектам сэкономить на gas optimization до 40%. Свяжитесь — обсудим вашу задачу.