Разработка протокола ликвидаций
Aave V3 теряет ликвидатора быстрее, чем вы думаете. При высокой нагрузке на сеть газ на liquidationCall вырастает до 400-600k gas units — при цене 80 gwei это 0.03-0.05 ETH только на газ. Если спред между долгом и залогом меньше этой суммы, ликвидация нерентабельна, и позиция висит в состоянии bad debt. Именно поэтому дизайн инцентивов ликвидаторов — не детали, а основа платёжеспособности всего lending-протокола.
Механика ликвидаций: почему стандартный подход ломается
Health factor и liquidation threshold
Каждая позиция имеет health factor:
HF = Σ(collateral_i * LT_i) / total_debt
Когда HF < 1 — позиция ликвидируема. Liquidation threshold (LT) задаётся отдельно для каждого актива: ETH — 82.5%, USDC — 85%, более волатильные активы — 65-75%. Это значит, что кастомный lending с экзотическими активами требует тщательной калибровки LT на основе исторической волатильности.
Проблема «пылевых позиций»: если позиция маленькая (долг $50), газ на ликвидацию ($30-80) съедает всю прибыль. Ликвидаторы игнорируют такие позиции — протокол накапливает bad debt. Решение — minimum debt threshold (минимальный размер позиции) или flat fee компонент в инцентиве.
Liquidation bonus и проблема больших позиций
Стандартный подход: ликвидатор получает liquidation bonus (5-10%) от ликвидируемого залога. Это работает для средних позиций. Для позиций с залогом >$10M возникает другая проблема: ликвидатор не может ликвидировать сразу из-за slippage при продаже залога — цена актива падает в процессе, bonus нивелируется.
Aave V3 решает это через partial liquidation (до 50% за раз) и close factor. Для кастомных протоколов мы реализуем dutch auction: bonus стартует с 5% и растёт со временем, пока позиция не ликвидирована. Это минимизирует переплату при нормальных условиях и гарантирует ликвидацию при стрессе.
Flash loan ликвидации и MEV
Профессиональные ликвидаторы не держат капитал — они используют flash loans. Схема: взять flash loan актива, погасить долг позиции, получить залог с бонусом, продать залог, вернуть flash loan. Всё в одной транзакции, нулевой капитал.
Это означает, что ваш протокол должен быть совместим с flash loan паттерном. Конкретно: liquidationCall должен принимать collateralAsset и debtAsset отдельно, возвращать aToken или базовый актив по выбору ликвидатора, не блокировать вызовы из смарт-контрактов.
MEV-аспект: большинство ликвидаций перехватываются MEV-ботами через frontrunning. Это не обязательно плохо для протокола — ликвидация всё равно происходит. Но если хотите защитить «честных» ликвидаторов, можно интегрировать Flashbots MEV Blocker или реализовать приватный мемпул для ликвидаций.
Архитектура liquidation engine
Двухуровневая система ликвидаций
Мы строим два режима ликвидации в одном контракте:
Режим 1 — standard liquidation. Ликвидатор предоставляет актив для погашения долга, получает залог с бонусом. Простой, газэффективный, работает для большинства позиций.
Режим 2 — auction liquidation. Активируется при позициях с залогом выше threshold (например, $500K). Голландский аукцион: начальная цена залога = рыночная цена * (1 - максимальный discount), цена растёт каждые N блоков. Первый ликвидатор, который готов принять текущую цену, выигрывает аукцион.
function getAuctionPrice(
uint256 startPrice,
uint256 startBlock,
uint256 priceIncreasePerBlock
) public view returns (uint256) {
uint256 elapsed = block.number - startBlock;
return startPrice + (elapsed * priceIncreasePerBlock);
}
Оракульная интеграция и защита от манипуляции
Liquidation logic критически зависит от цен оракулов. Chainlink — основной источник. Но для защиты от stale data:
function _getPrice(address asset) internal view returns (uint256) {
(, int256 price, , uint256 updatedAt, ) = AggregatorV3Interface(feed).latestRoundData();
require(block.timestamp - updatedAt <= MAX_STALENESS, "Stale oracle");
require(price > 0, "Invalid price");
return uint256(price);
}
MAX_STALENESS — параметр для каждого актива отдельно: ETH/USD обновляется каждые 27 секунд или при отклонении >0.5%, более экзотические активы — реже. Для активов без Chainlink — Uniswap V3 TWAP с минимальным window 30 минут.
Bad debt socialisation
Если протокол всё-таки накопил bad debt (позиция стала неликвидируемой при падении залога ниже стоимости долга), нужен механизм его покрытия. Варианты:
- Insurance module: отдельный пул токенов протокола, стейкеры которого несут первый убыток
- Reserve factor: часть процентных доходов идёт в резерв
- Bad debt socialisation: убыток размазывается по всем LP пропорционально
Aave использует Insurance Module (Safety Module) со staked AAVE. Compound V3 использует reserve factor. Мы реализуем тот вариант, который соответствует tokenomics протокола.
Стек и инструменты
| Слой | Технология |
|---|---|
| Контракты | Solidity 0.8.24, OpenZeppelin 5.x |
| Оракулы | Chainlink AggregatorV3, Uniswap V3 TWAP |
| Flash loans | Aave V3 FlashLoanSimpleReceiver |
| Тестирование | Foundry fork mainnet, Echidna |
| Мониторинг | The Graph subgraph, Tenderly Alerts |
| Деплой | Foundry forge script, Gnosis Safe |
Процесс работы
Аналитика. Моделируем stress scenarios: падение ETH на 40% за 1 час (как в мае 2021). Считаем, при каких параметрах LT/bonus протокол остаётся платёжеспособным.
Разработка. Ликвидационный engine + интеграция с Chainlink + аукционный модуль. Параллельно — off-chain liquidation bot для первых недель работы протокола (пока внешние ликвидаторы не пришли).
Тестирование. Fork-тест на Ethereum mainnet: симулируем массовые ликвидации марта 2020 (Black Thursday) на нашем контракте. Проверяем, что bad debt не накапливается при реалистичных сценариях.
Ориентиры по срокам
Базовый liquidation module для встраивания в lending-протокол — 1-2 недели. Автономный протокол ликвидаций с dutch auction и bad debt socialisation — 3-4 недели. Включая написание off-chain liquidation bot — плюс 1 неделя.







