Розробка системи ліквідацій для perpetual DEX

Проєктуємо та розробляємо блокчейн-рішення повного циклу: від архітектури смарт-контрактів до запуску DeFi-протоколів, NFT-маркетплейсів та криптобірж. Аудит безпеки, токеноміка, інтеграція з наявною інфраструктурою.
Показано 1 з 1Усі 1306 послуг
Розробка системи ліквідацій для perpetual DEX
Складний
~1-2 тижні
Часті запитання

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

Етапи блокчейн-розробки

Останні роботи

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1285
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1198
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    902
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1121
  • image_logo-advance_0.webp
    Розробка логотипу компанії B2B Advance
    589
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    858

Розробка системи ліквідацій для Perpetual DEX

У листопаді 2022 року, після краху FTX, GMX v1 пройшов стрес-тест: ціни різко рухалися, об'єми торгів злетіли в 10x, і система ліквідацій витримала навантаження. Це тому що GMX використовує keeper-based ліквідації з правильно вибудованими incentive-ами: ліквідатор отримує частину ліквідаційного fee, але тільки якщо виконує швидко. Погано спроектована система ліквідацій в такі моменти або не встигає закривати позиції, або створює bad debt, який ляжає на LP провайдерів.

Механіка ліквідацій у perpetual DEX

Що таке ліквідована позиція

На perpetual DEX трейдер відкриває позицію з левередж: 10x long ETH за 1000 USDC колатералью означає позицію на 10,000 USDC notional. Якщо ETH впадає на 9%, unrealized loss = 900 USDC (9% × 10,000), колатераль зменшується до 100 USDC. Margin ratio = 100/10,000 = 1%. Якщо це нижче maintenance margin (зазвичай 0.5-1%), позиція ліквідується.

Формула margin ratio: marginRatio = (collateral + unrealized_pnl) / notional_value

Протокол повинен ліквідувати позицію до того як collateral + unrealized_pnl < 0 — інакше bad debt.

Gap risk: головна проблема при високій волатильності

При gap (різкий стрибок ціни, наприклад від новини) mark price стрибає через кілька рівнів ліквідації одночасно. Позиція може піти прямо в negative equity без можливості ліквідації по дорозі.

Як GMX v2 і dYdX v4 вирішують gap risk:

  • Insurance fund — резерв, сформований з частини торгових комісій
  • ADL (Auto-Deleveraging) — якщо insurance fund не покриває, прибуткові позиції протилежної сторони примусово закриваються
  • Max open interest limits — обмеження сукупного OI по активу знижує потенціальний bad debt

Архітектура системи ліквідацій

On-chain компонент

Контракт зберігає позиції та постійно оновлює mark price через oracle. Ліквідація відбувається в два кроки:

1. Перевірка ліквідовності (view функція):

function isLiquidatable(uint256 positionId) public view returns (bool) {
    Position memory pos = positions[positionId];
    uint256 markPrice = oracle.getMarkPrice(pos.indexToken);
    
    int256 unrealizedPnl = calculatePnl(pos, markPrice);
    int256 equity = int256(pos.collateral) + unrealizedPnl;
    
    // Вираховуємо накопичений funding fee
    int256 pendingFunding = calculateFundingFee(pos);
    equity -= pendingFunding;
    
    uint256 notional = pos.size; // size = notional value
    
    // Нижче maintenance margin порогу
    return equity < int256(notional * MAINTENANCE_MARGIN_BPS / 10000);
}

2. Виконання ліквідації:

function liquidate(uint256 positionId, address recipient) external nonReentrant {
    require(isLiquidatable(positionId), "Not liquidatable");
    
    Position memory pos = positions[positionId];
    uint256 markPrice = oracle.getMarkPrice(pos.indexToken);
    
    // Розраховуємо залишок колатералью після втрат
    int256 remainingCollateral = calculateRemainingCollateral(pos, markPrice);
    
    uint256 liquidationFee = pos.collateral * LIQUIDATION_FEE_BPS / 10000;
    
    // Виплата keeper
    uint256 keeperFee = liquidationFee * KEEPER_SHARE / 100;
    token.transfer(recipient, keeperFee);
    
    // Залишок в insurance fund або протокол
    if (remainingCollateral > 0) {
        uint256 toInsurance = uint256(remainingCollateral) - keeperFee;
        insuranceFund.deposit(toInsurance);
    } else {
        // Bad debt — покриваємо з insurance fund
        insuranceFund.cover(uint256(-remainingCollateral));
    }
    
    _closePosition(positionId);
    
    emit PositionLiquidated(positionId, msg.sender, keeperFee, block.timestamp);
}

Keeper система

Keeper — зовнішній учасник, який моніторить позиції і викликає liquidate(). Incentive: keeper fee. Це створює конкурентний ринок ліквідаторів.

Для побудови keeper-мережі потрібна off-chain інфраструктура:

class LiquidationKeeper {
    private positionCache: Map<bigint, Position> = new Map();
    
    async monitorPositions(): Promise<void> {
        // Підписка на eventi оновлення позицій
        contract.on('PositionUpdated', (positionId, position) => {
            this.positionCache.set(positionId, position);
        });
        
        // Періодична перевірка при кожному новому блоці
        provider.on('block', async (blockNumber) => {
            const markPrice = await oracle.getMarkPrice(INDEX_TOKEN);
            
            const liquidatable = [...this.positionCache.entries()]
                .filter(([_, pos]) => this.isLiquidatable(pos, markPrice))
                .sort((a, b) => this.prioritize(a, b, markPrice)); // Найвигіднішім першим
            
            for (const [positionId] of liquidatable) {
                await this.attemptLiquidation(positionId);
            }
        });
    }
    
    private prioritize(a: [bigint, Position], b: [bigint, Position], price: bigint): number {
        // Пріоритет: чим більше колатералью — тим вищий keeper fee
        return Number(b[1].collateral - a[1].collateral);
    }
}

Mark price oracle

Ключовий компонент: mark price не повинен маніпулюватися flash loan-ами. dYdX v4 використовує Pyth oracle з агрегованою медіаною з кількох джерел. GMX v2 використовує Chainlink + custom keeper oracle з верифікацією підпису.

Вимоги до oracle:

  • Freshness check: ціна не старша ніж N секунд (зазвичай 30-60)
  • Deviation check: нова ціна не відрізняється від попередньої більш ніж на X% (circuit breaker)
  • Multi-source aggregation: медіана з 3+ джерел
function getMarkPrice(address token) external view returns (uint256) {
    PriceData memory data = priceData[token];
    
    require(block.timestamp - data.timestamp <= STALENESS_THRESHOLD, "Stale price");
    require(data.numSources >= MIN_SOURCES, "Insufficient sources");
    
    return data.medianPrice;
}

ADL механізм

Auto-Deleveraging — остання лінія захисту. Якщо insurance fund вичерпаний, протокол примусово закриває прибуткові позиції по mark price (без slippage). Порядок закриття: позиції з найбільшим profit AND найбільшим leverge першими (найбільш ризиковані для системи).

ADL — болісний механізм для трейдерів. Важливо:

  • Чітко розкрити ризик ADL в документації
  • Показувати ADL indicator на UI (як на Binance futures)
  • Обмежити OI щоб мінімізувати потребу в ADL

Стек

Solidity + Foundry — ліквідаційні контракти, oracle, insurance fund. TypeScript + viem — keeper бот, моніторинг позицій. Chainlink + Pyth — price feeds. Gelato Network — автоматизований виклик keeper функцій (як fallback). Foundry fork tests — симуляція стрес-сценаріїв на mainnet fork.

Процес роботи

Аналітика (3-5 днів). Параметри ризиків: maintenance margin, ліквідаційний fee, страховий фонд. Моделювання стрес-сценаріїв: що при -50% основного активу за один блок.

Розробка (2-4 тижні). Ліквідаційний контракт + keeper бот + oracle інтеграція + insurance fund.

Тестування (1 тиждень). Fork-тести з історичними price шоками (March 2020, LUNA краш, FTX). Інваріант: після кожної ліквідації margin ratio позиції не менше 0.

Аудит. Для perpetual DEX з реальним TVL — обов'язково.

Орієнтири по термінам

Ліквідаційна система для одного активу без ADL — 1-2 тижні. Повна система з ADL, insurance fund, мульти-oracle агрегатором і keeper інфраструктурою — 4-6 тижнів. Вартість рахується індивідуально.