Liquidation
Proactive liquidation mechanics to ensure protocol solvency
Liquidation ensures protocol solvency by allowing third-party keepers (or anyone) to intervene when a user's equity falls below the maintenance margin. This prevents bad debt from cascading during market volatility.
Liquidation Trigger
A user becomes liquidatable when:
This can happen to any user regardless of position type:
| Position Type | How Liquidation Can Trigger |
|---|---|
| Long holders | Option values drop significantly |
| Short holders | Option values rise significantly (obligation increases) |
| Mixed portfolios | Net losses exceed collateral buffer |
The Liquidation Process
- Trigger: Keeper detects Equity < MM
- Calculate: Determine debt and positions to close
- Close Positions at penalized mark price: Long (size > 0) liquidator buys at mark × (1 - penalty%); Short (size < 0) liquidator buys back at mark × (1 + penalty%)
- Settle: Proceeds applied to user's collateral, liquidator receives bounty, and if shortfall, insurance fund covers
Debt Calculation
If Debt > 0 and Equity < MM, the user is underwater and liquidation is triggered.
Mark Price Liquidation
Liquidations use the mark price (Black-Scholes theoretical) directly. They do not go through the orderbook or RFQ system.
The liquidator buys positions directly from the liquidated user at a penalized mark price:
| Position Type | Liquidation Price | Effect |
|---|---|---|
| Long (size > 0) | mark × (1 - penalty%) | Liquidator buys at discount |
| Short (size < 0) | mark × (1 + penalty%) | Liquidator buys back at premium |
The liquidator immediately earns the penalty as profit. They can then sell the acquired positions via orderbook or RFQ.
Volatility-Adjusted Penalty
The liquidation penalty scales with implied volatility to properly incentivize liquidators during market stress:
Examples
| IV | Penalty Calculation | Rate |
|---|---|---|
| 50% | 1% + (50-50)/100 | 1.0% |
| 75% | 1% + (75-50)/100 | 1.25% |
| 100% | 1% + (100-50)/100 | 1.5% |
| 150% | 1% + (150-50)/100 | 2.0% |
Why Volatility Adjustment Matters
In high volatility environments:
- Positions move faster - A barely underwater position can become deeply underwater within blocks
- Liquidators take more risk - Prices may move against them before they can hedge
- Higher penalties compensate - Ensures liquidators remain incentivized during stress
Without vol-adjustment, liquidators might delay during market stress (when liquidations are most critical), leading to bad debt accumulation.
Partial vs Full Liquidation
The protocol supports partial liquidation to give users a chance to recover.
Key Insight
Margin requirements use stressed scenarios (±30% spot, +50% IV), but liquidation executes at current mark prices. This means:
- Selling positions at current mark barely reduces equity (just the penalty)
- But it dramatically reduces the margin requirement (which was stress-based)
- So partial liquidation often restores health without destroying the user
Proportional Liquidation
Partial liquidation uses a proportional approach based on the user's debt-to-margin ratio:
This ensures just enough positions are closed to cover the debt proportionally.
Liquidation Process
- Trigger: Equity < MM
- First Attempt: Close
debt/IMfraction of positions — Liquidator acquires proportional amount at penalized mark price - Re-check: Is user now healthy? (Equity ≥ MM)?
- If yes, stop and user retains remaining positions
- If no, automatically escalate to full liquidation (close remaining positions)
Why Proportional?
- Closing a fraction proportional to debt targets the exact amount needed
- Equity only decreases by the penalty (~1-2% of closed position value)
- Automatic escalation ensures health is always restored
- More efficient than fixed 50% - avoids over-liquidating healthy-ish users
After liquidation, the user retains any remaining equity and positions (if partial was sufficient).
Liquidator Incentives
Liquidators are compensated through two mechanisms:
1. Penalty Profit (1-2%+)
Liquidators acquire positions at a discount/premium from mark price. For example, when liquidating a long position with mark price 100 × 0.985 = 98.50 for a position worth 1.50 per contract.
2. Bounty (5% of debt)
The bounty is always paid first from liquidation proceeds to incentivize liquidators, even in shortfall cases. The bounty is capped at total proceeds to ensure we don't pay more than available.
Total Liquidator Profit
This incentivizes keepers to monitor positions and liquidate promptly, especially during high volatility when the penalty (and thus profit) is higher.
Post-Liquidation
After acquiring positions, the liquidator can:
| Action | Description |
|---|---|
| Sell via limit orders | Place orders on the orderbook |
| Sell via RFQ | Request quote from Main Market Maker |
| Hold positions | Keep exposure if desired |
| Hedge externally | Offset risk on other venues |
Insurance Fund
When liquidation proceeds are insufficient to cover debt (bad debt scenario):
The Insurance Fund covers the shortfall.
DiffusalInsuranceFund Contract
The insurance fund is a separate contract (DiffusalInsuranceFund) that:
- Receives all protocol trading fees (set as
feeRecipienton OrderBook and RFQ) - Holds funds in a deposit on the
CollateralVault - Provides
cover()function callable byLiquidationEngineorSettlementEngine
Insurance Fund Sources
| Source | Description |
|---|---|
| Protocol fees | All trading fees routed via feeRecipient |
| Initial seed capital | Bootstrap funding via deposits |
Flow Diagram
At settlement, funds flow from short holders (obligations) to the settlement pool, then to long holders (payouts). If there's a shortfall, the insurance fund provides coverage.
Worked Example
Initial State
Portfolio (ETH spot = $3,000, IV = 50%):
| Position | Strike | Mark | Entry | Size |
|---|---|---|---|---|
| Long ETH Call | $3,200 | $98.76 | $150 | +10 |
| Short ETH Put | $2,800 | $80.63 | $120 | -5 |
- Deposited: $2,000
- Unrealized PnL: -$315.58
- Equity: $1,684.42
- IM: $4,416.53
- MM: $3,533.22
Trigger: Equity (3,533.22) - LIQUIDATE
Calculation
Step 1: Debt
Step 2: Penalized Prices (1% penalty)
| Position | Mark | Penalty Direction | Liq Price |
|---|---|---|---|
| Long Call | $98.76 | -1% | $97.77 |
| Short Put | $80.63 | +1% | $81.44 |
Step 3: Proceeds
| Position | Size | Liq Price | Proceeds |
|---|---|---|---|
| Long | 10 | $97.77 | $977.70 |
| Short | 5 | $81.44 | $407.20 |
| Total | $1,384.90 |
Step 4: Bounty
Step 5: Settlement
1. Pay bounty first: $136.61 to liquidator
2. Remaining proceeds: $1,384.90 - $136.61 = $1,248.29
3. Apply to debt: $1,248.29 < $2,732.11 (shortfall)
4. Shortfall: $2,732.11 - $1,248.29 = $1,483.82
5. Insurance covers: $1,483.82User retains $0 after liquidation (insufficient proceeds to cover debt after bounty). Positions are closed.
Extreme Case
If spot crashed to $2,000:
- Proceeds drop to ~$800
- Bounty: min(800) = $136.61
- Remaining: 136.61 = $663.39
- Shortfall: 663.39 = $2,068.72
- Insurance fund covers $2,068.72
- Liquidator still receives bounty (paid first to incentivize action even in stress)
Risk Parameters
| Parameter | Default | Description |
|---|---|---|
LIQUIDATION_PENALTY_BASE | 1% | Base price penalty |
LIQUIDATION_PENALTY_IV_BASELINE | 50% | IV threshold for penalty scaling |
LIQUIDATOR_BOUNTY | 5% | % of debt paid to liquidator |
Note: Partial liquidation uses proportional position closure (debt/IM fraction) rather than a fixed percentage.
Main Market Maker Exception
The Main Market Maker (MMM) is a privileged, non-liquidatable entity. The liquidation function rejects any attempt to liquidate the MMM address by first checking if the user is an MMM (returning false if so) before checking equity vs maintenance margin.
Why MMM Is Exempt
The MMM's solvency is managed operationally, not by protocol rules:
| Concern | Mitigation |
|---|---|
| MMM goes underwater | MMM operator monitors and rebalances externally |
| MMM accumulates too much risk | MMM offloads shorts via limit orders or OTC |
| MMM runs out of capital | MMM operator injects capital before insolvency |
| MMM becomes malicious | Governance can replace MMM address |
This is a deliberate trust tradeoff: by trusting one well-managed entity, the protocol guarantees that RFQ always functions and liquidations can always execute against MMM inventory.
Security Considerations
Keeper Monitoring
- Permissionless: Anyone can call
liquidate()- no special access required - Multiple keepers: Protocol can run backup keepers alongside third-party services (Gelato, Chainlink)
- Incentive alignment: Penalty + bounty ensures keepers are motivated
Front-Running Protection
- Liquidation price is determined by mark price at execution time
- No advantage to front-running the liquidation call
- MEV protection via fast block finality on supported chains
Manipulation Resistance
- Mark price uses oracle (Pyth/Chainlink), not order book
- Cannot avoid liquidation by placing fake orders
- TWAP smoothing prevents flash loan attacks on oracle
Smart Contract API
The DiffusalLiquidationEngine contract implements the liquidation mechanics. It is permissionless - anyone can call liquidation functions.
Liquidation Functions
liquidate(user)— Full liquidation that closes all positions. Returns a LiquidationResult containing: user address, liquidator, debt (USDC 6 decimals), totalProceeds, bounty, insuranceUsed, newEquity, positionsLiquidated, and isPartial (false for full liquidation).liquidatePartial(user)— Partial liquidation that closes a proportional fraction (debt/IM) of positions. If partial liquidation doesn't restore health, automatically escalates to full liquidation. Returns same result struct with isPartial = true.
View Functions
getLiquidationInfo(user)— Returns comprehensive info: isLiquidatable, debt (USDC 6 decimals), estimatedProceeds, estimatedBounty, positionCountcalculateDebt(user)— Returns debt = max(0, Initial Margin - Equity)calculatePenaltyRate(pairId)— Returns penalty rate in WAD (e.g., 0.015e18 = 1.5%). Penalty = Base (1%) + (IV - 50%) / 100getInsuranceFundBalance()— Returns current insurance fund balance
External Insurance Fund
The insurance fund is a separate contract (DiffusalInsuranceFund) with these functions:
getBalance()— Current fund balance (USDC, 6 decimals)collateralVault()— CollateralVault referenceliquidationEngine()— LiquidationEngine referencecover(shortfallAmount)— Draw funds for shortfalls (callable by LiquidationEngine or SettlementEngine)withdraw(amount)— Withdraw excess funds (owner only)
The insurance fund receives all protocol trading fees by being set as feeRecipient on OrderBook and RFQ contracts.
Deployment Integration
The liquidation engine must be registered as an operator on DiffusalOptionsPositionManager (for updatePosition()) and DiffusalCollateralVault (for creditCollateral() and debitCollateral()). Additionally, the insurance fund must be connected to the liquidation engine, and fees should be routed to the insurance fund.
Contract Implementation
| Contract | Role |
|---|---|
| DiffusalLiquidationEngine | Core liquidation logic |
| DiffusalInsuranceFund | Shortfall coverage |
| DiffusalCollateralVault | Collateral settlement |
Related
- Margin System — How IM and MM are calculated
- Margin Calculations — Stress testing formulas
- DiffusalOptionsPositionManager — Position updates during liquidation