Розробка контрактів ескроу
У 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%. Зв'яжіться — обговоримо ваше завдання.







