Разработка протокола кредитования
Типичная ситуация: команда хочет запустить lending-протокол, форкает Compound v2, меняет параметры collateral factor, деплоит — и через три недели обнаруживает, что оракул работает через TWAP с 30-минутным окном, а ликвидации не успевают при резких движениях цены. Позиции уходят в минус, протокол несёт убытки. Это не баг форка — это архитектурное решение, которое в оригинале компенсировалось другими параметрами риска.
Разработка lending-протокола с нуля или адаптация существующего — это в первую очередь работа с математикой рисков и механикой ликвидаций, а не просто написание Solidity.
Где ломаются lending-протоколы
Oracle manipulation через flash loan
Самый разрушительный вектор атаки в DeFi-лендинге — манипуляция ценовым оракулом. Если протокол читает цену напрямую из spot-цены пула Uniswap v2, атакующий берёт flash loan, двигает цену в пуле, получает недообеспеченный кредит, возвращает flash loan. Протокол теряет коллатераль.
Атака на Mango Markets в 2022 году — 117 миллионов долларов — работала именно так. Атакующий использовал собственный токен как залог, искусственно поднял его цену через спот-покупки, взял кредиты против раздутого коллатераля.
Защита строится на нескольких уровнях:
-
Chainlink price feeds с проверкой
updatedAt— если данные старше N секунд, транзакция реверсируется - TWAP от Uniswap v3 как вторичный источник с окном не менее 30 минут для неликвидных активов
- Deviation check — если Chainlink и TWAP расходятся более чем на X%, принимаем меньшее значение
- Circuit breaker — временная пауза новых заимствований при аномальном движении цены
function getPrice(address asset) internal view returns (uint256) {
(, int256 answer, , uint256 updatedAt, ) = chainlinkFeed.latestRoundData();
require(block.timestamp - updatedAt <= STALENESS_THRESHOLD, "Stale price");
require(answer > 0, "Invalid price");
uint256 twapPrice = getTWAP(asset, TWAP_PERIOD);
uint256 chainlinkPrice = uint256(answer);
// Принимаем минимальное из двух — консервативная позиция
return twapPrice < chainlinkPrice ? twapPrice : chainlinkPrice;
}
Механика ликвидаций и bad debt
Второй критичный момент — порог ликвидации и health factor. Aave использует healthFactor = (collateralETH * liquidationThreshold) / totalDebtETH. Как только health factor опускается ниже 1.0, позиция открыта для ликвидаторов.
Проблема возникает при gap risk: актив падает на 30% за одну свечу (ликвидный кризис, крах биржи), ликвидаторы не успевают закрыть позиции, протокол накапливает bad debt. Compound столкнулся с этим при обвале LUNA — часть позиций ушла в минус.
Архитектурные решения:
| Механизм | Суть | Применение |
|---|---|---|
| Liquidation bonus | Ликвидатор получает коллатераль со скидкой 5-10% | Incentive для быстрой ликвидации |
| Partial liquidation | Закрывается только часть позиции | Снижение gas cost для ликвидаторов |
| Dutch auction liquidation | Цена бонуса растёт со временем | Автоматическая привлекательность при волатильности |
| Insurance fund | Резерв из части процентных доходов | Покрытие bad debt при gap risk |
Мы имплементируем dutch auction по образцу MakerDAO: если позиция не ликвидирована в течение N блоков, liquidation bonus начинает расти. Это гарантирует, что даже при низком интересе ликвидаторов позиция в итоге закроется.
Interest rate model: kink и utilization
Процентная ставка в Compound v2 и Aave v3 считается через utilization rate: U = totalBorrow / totalSupply. При низкой утилизации ставка низкая, при высокой — резко растёт (kink model).
Параметр kink критичен. Если utilization достигает 100%, вкладчики не могут вывести средства — ликвидности нет. Классическая kink-модель ставит inflection point на 80-90% и резко поднимает ставку выше, чтобы стимулировать новые депозиты или погашение долгов.
function getBorrowRate(uint256 cash, uint256 borrows, uint256 reserves)
external view returns (uint256)
{
uint256 util = utilizationRate(cash, borrows, reserves);
if (util <= kink) {
return util * multiplierPerBlock / BASE + baseRatePerBlock;
} else {
uint256 normalRate = kink * multiplierPerBlock / BASE + baseRatePerBlock;
uint256 excessUtil = util - kink;
return excessUtil * jumpMultiplierPerBlock / BASE + normalRate;
}
}
Как мы строим lending-протокол
Архитектура и стек
Базовая архитектура разделяет логику на несколько контрактов:
- LendingPool — точка входа, маршрутизирует вызовы
- ReserveLogic — расчёт индексов, начисление процентов
- ValidationLogic — проверки перед операциями
- aToken / debtToken — ERC-20 токены позиций (по образцу Aave v3)
- PriceOracle — агрегатор цен с fallback-логикой
- InterestRateStrategy — сменная стратегия ставок
Используем Foundry для разработки и тестирования. Fork-тесты против Ethereum mainnet обязательны: нужно проверять интеграцию с реальными Chainlink feeds, реальными ценами активов.
Для апгрейдаемости — UUPS proxy (EIP-1822). Transparent proxy на таких объёмах логики создаёт storage коллизии риск при апгрейдах. ERC-7201 namespaced storage изолирует переменные каждого логического модуля.
Особое внимание к reentrancy в cross-contract вызовах: aToken.mint() при депозите, transfer коллатераля при ликвидации — все внешние вызовы после обновления state, nonReentrant на всех точках входа.
Тестирование
Property-based тесты через Echidna с инвариантами:
- Сумма всех долгов никогда не превышает сумму всех депозитов
- Health factor позиции после ликвидации всегда выше 1.0
- Interest index монотонно возрастает
Fuzz-тесты в Foundry на функции депозита, заимствования, погашения — параметры: случайные суммы, случайные последовательности операций, случайные значения оракула.
Процесс работы
Аналитика (3-5 дней). Определяем список активов, collateral factor для каждого, параметры ликвидаций, источники оракулов. Моделируем stress scenarios: что происходит при -50% основного коллатерального актива за 1 блок.
Проектирование (5-7 дней). Storage layout, интерфейсы контрактов, математические модели ставок. Formal verification для core invariants через Certora Prover или Halmos — для протоколов с TVL-целью >10M USD это обязательный шаг.
Разработка (4-8 недель). Core контракты + тесты. Fork-тесты на mainnet. Покрытие >95%.
Внутренний security review (1 неделя). Slither, Mythril, ручной review по SWC checklist + DeFi-специфичные векторы: flash loan attacks, oracle manipulation, liquidation griefing.
Внешний аудит. Для lending-протокола — обязательно. Рекомендуем Trail of Bits, Spearbit, или Code4rena contest в зависимости от бюджета.
Деплой. Сначала testnet (Sepolia) с имитацией рыночных условий, затем mainnet через Gnosis Safe multisig. Параметры рисков выставляются консервативно с возможностью корректировки через governance.
Ориентиры по срокам
Минимальная версия протокола (один актив, базовые операции) — 4-6 недель. Полноценный multi-asset lending с governance и страховым фондом — 3-4 месяца. Сроки аудита не включены и зависят от выбранной компании (обычно 2-6 недель в очереди).
Стоимость рассчитывается после детального обсуждения архитектуры и требований к безопасности.







