Разработка крипто-лендинговой платформы
Протокол Euler Finance потерял $197M в марте 2023 года. Уязвимость — в функции donateToReserves, которая позволяла накапливать дефицит без соответствующего collateral check. Атакующий использовал flash loan для создания позиции с нарушенным health factor, а затем ликвидировал самого себя через donate-механику. Это не была очевидная ошибка — код прошёл несколько аудитов.
Лендинговые протоколы — одна из самых сложных категорий DeFi по количеству векторов атак. Каждая функция (deposit, borrow, repay, liquidate) взаимодействует с другими, и инварианты системы нетривиальны.
Критические уязвимости лендинговых протоколов
Oracle manipulation и cascading liquidations
Лендинговый протокол зависит от price oracle для расчёта collateral ratio. Компрометация оракула = возможность взять кредит под переоценённое обеспечение или избежать ликвидации.
Cream Finance потеряла $130M в 2021 году именно через oracle manipulation: токен с тонкой ликвидностью использовался как collateral, его цена была манипулирована через flash loan в AMM-пуле, и атакующий получил кредит на многократно превышающую реальную сумму.
Защита — многоуровневый оракул:
- Chainlink как primary feed с проверкой
latestRoundDataна staleness (>1 час — pause) - TWAP из Uniswap v3 как secondary с окном 30 минут
- Circuit breaker: если два оракула расходятся больше чем на 5% — new borrows suspended
Cascading liquidations — отдельная проблема. При резком падении рынка одновременная ликвидация тысяч позиций давит цену актива вниз, что триггерит новые ликвидации. Compound v3 решает это через liquidation caps на уровне протокола.
Health factor расчёт и целочисленная точность
Здесь совершается больше всего ошибок. Health factor = (collateralValue * collateralFactor) / borrowedValue. Когда это считается с uint256 без правильного масштабирования — округление режет точность.
Конкретный пример: collateral 1.001 ETH при collateralFactor 0.8 даёт 0.8008 ETH покрытия. Borrow 0.8 ETH. Health factor должен быть 1.001. Если расчёт делается через (collateral * factor / 1e18) / borrow, а не через mulDiv — возможна потеря точности, при которой здоровая позиция помечается как ликвидируемая.
Используем FullMath.mulDiv из Uniswap v3 core для всех расчётов, где важна точность. Ни одного деления до всех умножений.
Reentrancy в liquidate + transfer цепочках
Функция liquidate обычно делает несколько действий: забирает collateral у заёмщика, погашает долг, выплачивает bonus ликвидатору. Если токен collateral реализует ERC-777 или имеет кастомный transfer hook — есть окно для reentrancy между этапами.
AAVE v3 использует ReentrancyGuard на уровне Pool контракта и дополнительно — проверку флага _status в critical paths. Для любого лендингового протокола — nonReentrant на deposit, borrow, repay, liquidate обязателен.
Архитектура протокола
Модульная структура по образцу Compound v3
Comptroller / RiskManager
├── CToken / CometMarket (per asset)
│ ├── InterestRateModel
│ └── PriceOracle
├── LiquidationEngine
└── GovernanceModule
Отдельный контракт на каждый поддерживаемый актив (CToken-паттерн) vs единый Comet-контракт с маппингами — принципиальное архитектурное решение. CToken-паттерн проще в аудите и изоляции риска: проблема в одном рынке не затрагивает другие. Единый контракт дешевле в деплое и проще для пользователей.
Interest rate model
Kinked interest rate — стандарт: низкая ставка при utilization < 80%, резкий рост выше. Формула:
- Если
U < kink:borrowRate = baseRate + slope1 * U - Если
U >= kink:borrowRate = baseRate + slope1 * kink + slope2 * (U - kink)
При utilization 95% ставки могут достигать 100%+ APR — это механизм давления на заёмщиков для возврата ликвидности в пул.
Параметры (baseRate, slope1, slope2, kink) должны быть изменяемы через governance с timelock. Рынок меняется, оптимальные параметры меняются вместе с ним.
Liquidation mechanics
Liquidation bonus (5-10% от collateral) — вознаграждение ликвидатору. Слишком маленький бонус — ликвидаторы не заинтересованы, протокол накапливает bad debt. Слишком большой — заёмщики теряют больше при ликвидации, чем разумно.
Partial liquidation (погашение только части долга) — обязательна. Полная ликвидация большой позиции за один раз требует огромного капитала у ликвидатора. Compound v3 позволяет ликвидировать до health factor 1.05.
Flash loan ликвидации — стандартный паттерн. Ликвидатор берёт flash loan, погашает долг, получает collateral (с бонусом), продаёт на DEX, возвращает flash loan + fee. Протокол должен поддерживать это — то есть не блокировать вывод collateral в той же транзакции.
Поддерживаемые активы и collateral factors
| Актив | Collateral Factor | Liquidation Threshold | Пример (Aave v3) |
|---|---|---|---|
| ETH/WETH | 80% | 82.5% | 80% / 82.5% |
| WBTC | 70% | 75% | 70% / 75% |
| USDC | 85% | 88% | 86% / 88% |
| LINK | 65% | 70% | 65% / 70% |
| Volatile ERC-20 | 40-60% | 50-65% | varies |
Isolation mode (Aave v3) — активы с ограниченным долговым потолком, используемые только как collateral для стейблкоинов. Для новых или менее ликвидных активов это правильная стратегия: снижает системный риск.
Процесс разработки
Спецификация (3-5 дней). Какие активы, какие interest rate модели, есть ли governance, нужна ли апгрейдаемость.
Математические спецификации (2-3 дня). Все формулы верифицируем на Python-референсе до написания Solidity. Это экономит неделю отладки в контрактах.
Разработка (4-6 недель). Foundry + fuzz-тесты на все инварианты: health factor не может стать отрицательным, общий долг не превышает резервы, liquidation bonus считается корректно.
Fork-тестирование. Симуляция flash loan атак на форке mainnet. Тест cascading liquidations через price manipulation.
Внешний аудит. Обязателен для любого протокола с TVL. Желательно — два независимых аудитора.
Ориентиры по срокам
Минимальный лендинговый протокол (один актив, базовая ликвидация) — 4-6 недель. Полноценная платформа с несколькими рынками, governance, апгрейдами — 2-3 месяца. Аудит — дополнительно 4-8 недель в зависимости от объёма кода.







