Diffusal

DiffusalInsuranceFund

Fee collection and liquidation shortfall coverage

The DiffusalInsuranceFund contract collects protocol trading fees and provides a safety net for liquidation and settlement shortfalls. When a liquidation's proceeds don't cover the user's debt, or when shorts can't pay during batch settlement, the insurance fund steps in to cover the gap, preventing protocol insolvency.


Overview

The insurance fund serves four purposes:

FunctionDescription
Fee CollectionReceives trading fees as feeRecipient for OrderBook and RFQ
Liquidation ShortfallCovers debt when liquidation proceeds are insufficient
Settlement ShortfallCovers batch settlement shortfalls when shorts can't pay
Settlement ReadinessCovers bounty payments for settlement readiness liquidations

Fund Flow

┌─────────────────────────────────────────────────────────────────────────────┐
│                              FEE COLLECTION                                  │
│                                                                              │
│   ┌───────────────────────┐    ┌───────────────────────┐                    │
│   │      OrderBook        │    │         RFQ           │                    │
│   │ (maker + taker fees)  │    │     (RFQ fees)        │                    │
│   └───────────┬───────────┘    └───────────┬───────────┘                    │
│               │                            │                                 │
│               └────────────┬───────────────┘                                 │
│                            ▼                                                 │
│               ┌───────────────────────┐                                      │
│               │    Insurance Fund     │                                      │
│               └───────────┬───────────┘                                      │
│                           ▼                                                  │
│               ┌───────────────────────┐                                      │
│               │   Vault (deposit)     │                                      │
│               └───────────────────────┘                                      │
└─────────────────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────────────┐
│                           SHORTFALL COVERAGE                                 │
│                                                                              │
│  ┌─────────────────┐  ┌─────────────────┐  ┌────────────────────────────┐   │
│  │   Liquidation   │  │   Settlement    │  │   Settlement Readiness     │   │
│  │     Engine      │  │     Engine      │  │        Liquidator          │   │
│  └────────┬────────┘  └────────┬────────┘  └─────────────┬──────────────┘   │
│           │                    │                         │                   │
│           ▼                    ▼                         ▼                   │
│    ┌──────────────┐     ┌──────────────┐          ┌──────────────┐          │
│    │  Shortfall?  │     │  Shortfall?  │          │   Bounty?    │          │
│    └──────┬───────┘     └──────┬───────┘          └──────┬───────┘          │
│           │                    │                         │                   │
│           └────────────────────┴─────────────────────────┘                   │
│                                │                                             │
│                                ▼                                             │
│               ┌────────────────────────────────┐                             │
│               │   InsuranceFund.cover()        │                             │
│               └───────────────┬────────────────┘                             │
│                               ▼                                              │
│               ┌────────────────────────────────┐                             │
│               │  Vault.transferCollateral()    │                             │
│               └───────────────┬────────────────┘                             │
│                               ▼                                              │
│               ┌────────────────────────────────┐                             │
│               │       Cover shortfall          │                             │
│               └────────────────────────────────┘                             │
└─────────────────────────────────────────────────────────────────────────────┘

Key Concepts

Balance Storage

The insurance fund doesn't hold USDC directly. Instead, it maintains a deposit in the CollateralVault:

function getBalance() external view returns (uint256 balance) {
    return vault.getDeposit(address(this));
}

This design:

  • Leverages existing vault infrastructure
  • Simplifies fund accounting
  • Enables seamless transfers during liquidation

Fee Recipient Role

The insurance fund should be set as feeRecipient on both trading contracts:

// On OrderBook
orderBook.setFeeRecipient(address(insuranceFund));

// On RFQ
rfq.setFeeRecipient(address(insuranceFund));

When fees are collected, they're transferred to the insurance fund's vault deposit.

Shortfall Coverage

When a liquidation, batch settlement, or settlement readiness liquidation has insufficient funds:

// In LiquidationEngine._coverShortfall()
if (shortfall <= insuranceBalance) {
    insuranceFund.cover(shortfall, CoverageSource.LIQUIDATION, user);  // Full coverage
} else if (insuranceBalance > 0) {
    insuranceFund.cover(insuranceBalance, CoverageSource.LIQUIDATION, user);  // Partial
}

// In SettlementEngine.settlePortfolioPositionBatch()
// If short collection < long payout, cover each user's shortfall individually:
for (uint256 i = 0; i < results.length; i++) {
    if (results[i].settled && shortfallAmounts[i] > 0) {
        insuranceFund.cover(shortfallAmounts[i], CoverageSource.SETTLEMENT, users[i]);
    }
}

// In SettlementEngine._settlePortfolioPosition() (single position)
// If user can't pay full obligation:
if (actualDebit < obligation) {
    insuranceFund.cover(obligation - actualDebit, CoverageSource.SETTLEMENT, user);
}

// In SettlementReadinessLiquidator
// If user lacks collateral for bounty:
if (bountyShortfall > 0) {
    insuranceFund.cover(bountyShortfall, CoverageSource.SETTLEMENT_READINESS, user);
}

Storage & State

The contract uses ERC-7201 namespaced storage for upgradeability:

/// @custom:storage-location erc7201:diffusal.storage.InsuranceFund
struct InsuranceFundStorage {
    address owner;
    address pendingOwner;
    address collateralVault;
    address liquidationEngine;
    address settlementEngine;
    address settlementReadinessLiquidator;
    address liquidationSettlement;  // NEW: Extracted from LiquidationEngine
}

The contract stores funds in the vault via its deposit.

Note: The liquidationSettlement field was added when settlement logic was extracted from DiffusalLiquidationEngine to DiffusalLiquidationSettlement.


View Functions

getBalance

Returns the current insurance fund balance.

function getBalance() external view returns (uint256 balance)

Returns: USDC balance (6 decimals) from vault deposit.


owner

Returns the contract owner.

function owner() external view returns (address)

pendingOwner

Returns the pending owner address (for two-step ownership transfer).

function pendingOwner() external view returns (address)

collateralVault

Returns the collateral vault address.

function collateralVault() external view returns (address)

liquidationEngine

Returns the authorized liquidation engine address.

function liquidationEngine() external view returns (address)

settlementEngine

Returns the authorized settlement engine address.

function settlementEngine() external view returns (address)

settlementReadinessLiquidator

Returns the authorized settlement readiness liquidator address.

function settlementReadinessLiquidator() external view returns (address)

liquidationSettlement

Returns the authorized liquidation settlement contract address.

function liquidationSettlement() external view returns (address)

Authorized Engine Functions

cover

Covers a liquidation or settlement shortfall by transferring funds.

function cover(uint256 amount, CoverageSource source, address user) external nonReentrant onlyAuthorizedEngine
ParameterTypeDescription
amountuint256Shortfall amount to cover (USDC)
sourceCoverageSourceSource of coverage request (for indexing)
useraddressUser whose shortfall is being covered (must be non-zero, reverts on address(0))

CoverageSource Values:

ValueDescription
LIQUIDATIONTraditional liquidation shortfall
SETTLEMENTSettlement shortfall (single or batch)
SETTLEMENT_READINESSSettlement readiness liquidation bounty

Requirements:

  • Caller must be an authorized engine (liquidation, settlement, settlement readiness, or liquidation settlement)
  • user != address(0) (reverts with ZeroAddress)
  • amount > 0
  • amount <= getBalance()

Effect: Transfers amount from insurance fund's vault deposit to the calling engine.

Emits: ShortfallCovered(engine, user, source, amount)


Owner Functions

withdraw

Withdraws excess funds from the insurance fund.

function withdraw(uint256 amount) external nonReentrant onlyOwner
ParameterTypeDescription
amountuint256Amount to withdraw (USDC)

Requirements:

  • amount > 0
  • amount <= getBalance()

Effect: Transfers amount to owner's vault deposit.

Use case: Withdraw excess funds when balance grows beyond needed safety cushion.

Emits: Withdrawal


setLiquidationEngine

Sets the authorized liquidation engine.

function setLiquidationEngine(address engine_) external onlyOwner

Emits: LiquidationEngineUpdated


setSettlementEngine

Sets the authorized settlement engine.

function setSettlementEngine(address engine_) external onlyOwner

Emits: SettlementEngineUpdated


setSettlementReadinessLiquidator

Sets the authorized settlement readiness liquidator.

function setSettlementReadinessLiquidator(address liquidator_) external onlyOwner

Emits: SettlementReadinessLiquidatorUpdated


setLiquidationSettlement

Sets the authorized liquidation settlement contract.

function setLiquidationSettlement(address settlement_) external onlyOwner

Emits: LiquidationSettlementUpdated


setCollateralVault

Updates the collateral vault reference.

function setCollateralVault(address vault_) external onlyOwner

Emits: CollateralVaultUpdated


transferOwnership

Initiates a two-step ownership transfer.

function transferOwnership(address newOwner) external onlyOwner
ParameterTypeDescription
newOwneraddressThe address to transfer ownership to. Set to address(0) to cancel a pending transfer.

Emits: OwnershipTransferStarted

Note: The new owner must call acceptOwnership() to complete the transfer.


acceptOwnership

Completes the two-step ownership transfer (called by the pending owner).

function acceptOwnership() external

Requirements: Caller must be the pending owner set by transferOwnership().

Emits: OwnershipTransferred


Events

EventParametersDescription
ShortfallCoveredengine, user, source, amountShortfall covered with source and user for indexing
Withdrawalto, amountOwner withdrew funds
LiquidationEngineUpdatedoldEngine, newEngineLiquidation engine changed
SettlementEngineUpdatedoldEngine, newEngineSettlement engine changed
SettlementReadinessLiquidatorUpdatedoldLiquidator, newLiquidatorSettlement readiness liquidator changed
LiquidationSettlementUpdatedoldSettlement, newSettlementLiquidation settlement contract changed
CollateralVaultUpdatedoldVault, newVaultVault reference changed
OwnershipTransferStartedpreviousOwner, newOwnerTwo-step ownership transfer initiated
OwnershipTransferredpreviousOwner, newOwnerOwnership changed

Integration Points

Depends On

ContractPurpose
DiffusalCollateralVaultStores insurance fund balance

Used By

ContractPurpose
DiffusalLiquidationEngineLiquidation shortfall coverage
DiffusalLiquidationSettlementBounty and shortfall coverage
DiffusalSettlementEngineBatch settlement shortfall coverage
DiffusalSettlementReadinessLiquidatorBounty shortfall coverage
DiffusalOptionsOrderBookFee recipient
DiffusalOptionsRFQFee recipient

Security Considerations

Access Control

FunctionAccess
cover()Liquidation engine, liquidation settlement, settlement engine, OR settlement readiness liquidator
withdraw()Owner only
set*()Owner only
getBalance()Public

Reentrancy Protection

Both cover() and withdraw() use nonReentrant modifier.

Fund Adequacy

The insurance fund should maintain sufficient balance to cover potential shortfalls. Monitoring should track:

  • Fund balance vs total protocol exposure
  • Historical shortfall frequency
  • Average shortfall size

No Margin Requirements

The insurance fund has no positions and thus no margin requirements. Its "deposit" in the vault is a pure balance.


Example: Shortfall Coverage

Scenario

A user with 3 positions gets liquidated:

  • Debt: $10,000 (IM - equity)
  • Liquidation proceeds: $8,000
  • Shortfall: $2,000

Process

// In LiquidationEngine._settleCollateral()
if (totalProceeds < debt) {
    uint256 shortfall = debt - totalProceeds;  // $2,000
    insuranceUsed = _coverShortfall(shortfall);
}

// In _coverShortfall()
function _coverShortfall(uint256 shortfall, address user) internal returns (uint256 insuranceUsed) {
    uint256 balance = insuranceFund.getBalance();  // e.g., $50,000

    if (shortfall <= balance) {
        // Cover full $2,000 with LIQUIDATION source
        insuranceFund.cover(shortfall, CoverageSource.LIQUIDATION, user);
        insuranceUsed = shortfall;
    }
}

Result

  • User's positions closed
  • $8,000 collected from positions
  • $2,000 drawn from insurance fund
  • Total coverage: $10,000 (matches debt)
  • Insurance fund balance: $48,000

Code Reference

Source: packages/contracts/src/DiffusalInsuranceFund.sol

Interface: packages/contracts/src/interfaces/IDiffusalInsuranceFund.sol


On this page