Liquidation System for Perpetual DEX Development

We design and develop full-cycle blockchain solutions: from smart contract architecture to launching DeFi protocols, NFT marketplaces and crypto exchanges. Security audits, tokenomics, integration with existing infrastructure.
Showing 1 of 1 servicesAll 1306 services
Liquidation System for Perpetual DEX Development
Complex
~1-2 weeks
FAQ
Blockchain Development Services
Blockchain Development Stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1214
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    852
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1041
  • image_logo-advance_0.png
    B2B Advance company logo design
    561
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    823

Perpetual DEX Liquidation System Development

In November 2022, after the FTX collapse, GMX v1 underwent a stress test: prices moved sharply, trading volumes jumped 10x, and the liquidation system held the load. This is because GMX uses keeper-based liquidations with properly aligned incentives: the liquidator gets part of the liquidation fee, but only if executed quickly. A poorly designed liquidation system in such moments either fails to close positions or creates bad debt that falls on LP providers.

Liquidation mechanics in perpetual DEX

What is a liquidatable position

On a perpetual DEX, a trader opens a leveraged position: 10x long ETH for 1000 USDC collateral means a 10,000 USDC notional position. If ETH falls 9%, unrealized loss = 900 USDC (9% × 10,000), collateral shrinks to 100 USDC. Margin ratio = 100/10,000 = 1%. If this is below maintenance margin (typically 0.5-1%), the position is liquidated.

Margin ratio formula: marginRatio = (collateral + unrealized_pnl) / notional_value

The protocol must liquidate the position before collateral + unrealized_pnl < 0 — otherwise bad debt.

Gap risk: the main problem during high volatility

During a gap (sharp price jump, e.g., from news), mark price jumps through several liquidation levels simultaneously. A position can go straight into negative equity without liquidation opportunity along the way.

How GMX v2 and dYdX v4 solve gap risk:

  • Insurance fund — reserve formed from part of trading fees
  • ADL (Auto-Deleveraging) — if insurance fund doesn't cover, profitable positions on the opposite side are forcibly closed
  • Max open interest limits — limiting aggregate OI per asset reduces potential bad debt

Liquidation system architecture

On-chain component

Contract stores positions and continuously updates mark price via oracle. Liquidation happens in two steps:

1. Liquidatability check (view function):

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;
    
    // Subtract accumulated funding fee
    int256 pendingFunding = calculateFundingFee(pos);
    equity -= pendingFunding;
    
    uint256 notional = pos.size; // size = notional value
    
    // Below maintenance margin threshold
    return equity < int256(notional * MAINTENANCE_MARGIN_BPS / 10000);
}

2. Liquidation execution:

function liquidate(uint256 positionId, address recipient) external nonReentrant {
    require(isLiquidatable(positionId), "Not liquidatable");
    
    Position memory pos = positions[positionId];
    uint256 markPrice = oracle.getMarkPrice(pos.indexToken);
    
    // Calculate remaining collateral after losses
    int256 remainingCollateral = calculateRemainingCollateral(pos, markPrice);
    
    uint256 liquidationFee = pos.collateral * LIQUIDATION_FEE_BPS / 10000;
    
    // Keeper payment
    uint256 keeperFee = liquidationFee * KEEPER_SHARE / 100;
    token.transfer(recipient, keeperFee);
    
    // Remainder to insurance fund or protocol
    if (remainingCollateral > 0) {
        uint256 toInsurance = uint256(remainingCollateral) - keeperFee;
        insuranceFund.deposit(toInsurance);
    } else {
        // Bad debt — cover from insurance fund
        insuranceFund.cover(uint256(-remainingCollateral));
    }
    
    _closePosition(positionId);
    
    emit PositionLiquidated(positionId, msg.sender, keeperFee, block.timestamp);
}

Keeper system

A keeper is an external participant who monitors positions and calls liquidate(). Incentive: keeper fee. This creates a competitive market of liquidators.

To build a keeper network, off-chain infrastructure is needed:

class LiquidationKeeper {
    private positionCache: Map<bigint, Position> = new Map();
    
    async monitorPositions(): Promise<void> {
        // Subscribe to position update events
        contract.on('PositionUpdated', (positionId, position) => {
            this.positionCache.set(positionId, position);
        });
        
        // Periodic check on each new block
        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)); // Most profitable first
            
            for (const [positionId] of liquidatable) {
                await this.attemptLiquidation(positionId);
            }
        });
    }
    
    private prioritize(a: [bigint, Position], b: [bigint, Position], price: bigint): number {
        // Priority: larger collateral = higher keeper fee
        return Number(b[1].collateral - a[1].collateral);
    }
}

Mark price oracle

Key component: mark price must not be manipulated by flash loans. dYdX v4 uses Pyth oracle with aggregated median from multiple sources. GMX v2 uses Chainlink + custom keeper oracle with signature verification.

Oracle requirements:

  • Freshness check: price not older than N seconds (typically 30-60)
  • Deviation check: new price doesn't differ from previous by more than X% (circuit breaker)
  • Multi-source aggregation: median from 3+ sources
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 mechanism

Auto-Deleveraging — the last line of defense. If the insurance fund is exhausted, the protocol forcibly closes profitable positions at mark price (no slippage). Closing order: positions with highest profit AND highest leverage first (most risky for system).

ADL is painful for traders. Important:

  • Clearly disclose ADL risk in documentation
  • Show ADL indicator on UI (like on Binance futures)
  • Limit OI to minimize need for ADL

Stack

Solidity + Foundry — liquidation contracts, oracle, insurance fund. TypeScript + viem — keeper bot, position monitoring. Chainlink + Pyth — price feeds. Gelato Network — automated keeper function calls (as fallback). Foundry fork tests — stress scenario simulation on mainnet fork.

Working process

Analytics (3-5 days). Risk parameters: maintenance margin, liquidation fee, insurance fund. Stress scenario modeling: what if -50% of main asset in one block.

Development (2-4 weeks). Liquidation contract + keeper bot + oracle integration + insurance fund.

Testing (1 week). Fork tests with historical price shocks (March 2020, LUNA crash, FTX). Invariant: after each liquidation, position margin ratio >= 0.

Audit. For perpetual DEX with real TVL — mandatory.

Timeline estimates

Liquidation system for one asset without ADL — 1-2 weeks. Full system with ADL, insurance fund, multi-oracle aggregation and keeper infrastructure — 4-6 weeks. Cost is calculated individually.