Diffusal

DiffusalSettlementReadinessLiquidator

Handles liquidation when users lack cash to cover expiring position obligations

The DiffusalSettlementReadinessLiquidator contract handles a specific liquidation scenario: when users have expiring positions with settlement obligations but insufficient cash (deposited collateral) to cover them. This is a high-risk contract with significant impact on user funds.


Overview

Settlement readiness liquidation is triggered when a user has positions expiring within 1 day but lacks sufficient cash to cover worst-case settlement obligations:

Settlement Readiness Liquidatable=Cash Shortfall>0Has Liquidatable Assets\text{Settlement Readiness Liquidatable} = \text{Cash Shortfall} > 0 \land \text{Has Liquidatable Assets}

Where:

  • Cash Shortfall = Worst-case obligations − Deposited collateral
  • Liquidatable Assets = Non-expiring LONG positions OR Premium receivables

Key Difference from Regular Liquidation:

AspectRegular LiquidationSettlement Readiness
TriggerEquity < Maintenance MarginCash shortfall for expiring obligations
What's liquidatedAll positions (longs AND shorts)Non-expiring LONGs + premium receivables
GoalRestore margin healthRaise cash for settlement
Can shorts be liquidated?YesNo (shorts are obligations)

See Liquidation (Protocol): Settlement Readiness for detailed mechanics and examples.


Key Concepts

The Settlement Cash Problem

A user can have high portfolio equity from profitable long positions but minimal actual cash. At settlement:

  • Shorts may owe intrinsic value
  • Premium Payers owe their fixed premium obligation
  • Without cash, settlement fails

Liquidation Waterfall

The contract liquidates assets in order of priority:

  1. Non-expiring LONG positions (sorted by expiry, longest-dated first)

    • Liquidator pays penalized mark price
    • User receives cash for positions sold
  2. Premium receivables (if shortfall remains)

    • Liquidated at a discount rate (default 5%)
    • Value = premiumReceivable × (1 - discountRate)

Worst-Case Obligation Calculation

For expiring shorts: Stress spot to maximize intrinsic

  • Calls: spot UP +30%
  • Puts: spot DOWN -30%

For expiring longs with premium payables: Stress spot to minimize intrinsic

  • Calls: spot DOWN -30%
  • Puts: spot UP +30%

Storage & State

The contract uses ERC-7201 namespaced storage for upgradeability:

/// @custom:storage-location erc7201:diffusal.storage.SettlementReadinessLiquidator
struct SettlementReadinessLiquidatorStorage {
    address owner;
    address pendingOwner;                   // Two-step ownership transfer
    mapping(address => bool) operators;
    address collateralVault;
    address positionManager;
    address seriesRegistry;
    address quoter;
    address oracle;
    address insuranceFund;
    uint256 liquidatorBountyRate;           // Default 5% (0.05e18)
    uint256 settlementReadinessBuffer;      // Default 5% (0.05e18)
    uint256 premiumReceivableDiscountRate;  // Default 5% (0.05e18)
    address marginCalculator;               // Margin calculator for health checks
    mapping(address => bool) approvedLiquidators;
    address viewContract;                   // Calculator for view functions
}

SettlementReadinessInfo Struct

struct SettlementReadinessInfo {
    bool isLiquidatable;                      // Can be liquidated for settlement readiness
    uint256 cashRequired;                     // Total cash needed for expiring obligations (USDC)
    uint256 cashAvailable;                    // Deposited collateral (USDC)
    uint256 cashShortfall;                    // Amount obligations can't cover (USDC)
    uint256 positionValueAvailable;           // Value of liquidatable longs (USDC)
    uint256 expiringShortsCount;              // Number of short positions expiring within window
    uint256 expiringLongsCount;               // Number of long positions expiring within window
    uint256 liquidatablePositionsCount;       // Number of non-expiring long positions available
    int256 totalPremiumReceivable;            // Total premium receivable available (WAD)
    uint256 premiumReceivableValueAfterDiscount; // Value after discount rate (USDC)
}

SettlementReadinessLiquidationResult Struct

struct SettlementReadinessLiquidationResult {
    address user;                // User being liquidated
    uint256 portfolioId;         // Portfolio ID being liquidated (0 for default)
    address liquidator;          // Who executed liquidation
    uint256 cashShortfall;       // Original shortfall (USDC)
    uint256 cashRaised;          // Total cash raised (USDC)
    uint256 liquidatorCost;      // Total USDC paid by liquidator
    uint256 bounty;              // Bounty paid to liquidator
    uint256 positionsLiquidated; // Number of long positions liquidated
    uint256 newCashAvailable;    // User's deposit after liquidation (USDC)
    int256 premiumLiquidated;    // Premium receivable value liquidated (WAD)
    uint256 premiumProceeds;     // Cash from premium liquidation (USDC)
}

View Functions

Portfolio-Aware Only

All settlement readiness functions are portfolio-aware. You must always specify a portfolioId. View functions are delegated to DiffusalSettlementReadinessCalculator for contract size reduction.

getPortfolioSettlementReadinessInfo

Returns settlement readiness status for a specific portfolio.

function getPortfolioSettlementReadinessInfo(address user, uint256 portfolioId) external view returns (SettlementReadinessInfo memory info)
ParameterTypeDescription
useraddressUser address
portfolioIduint256Portfolio ID

Returns: SettlementReadinessInfo struct with detailed status.

Checks:

  • Identifies positions expiring within 1 day (SETTLEMENT_READINESS_WINDOW)
  • Calculates worst-case obligations using SPAN stress (spot ±30%)
  • Compares to deposited collateral (actual cash)
  • Identifies liquidatable assets (non-expiring longs + premium receivables)

isPortfolioSettlementReadinessLiquidatable

Checks if a specific portfolio can be liquidated for settlement readiness.

function isPortfolioSettlementReadinessLiquidatable(address user, uint256 portfolioId) external view returns (bool)
ParameterTypeDescription
useraddressUser address
portfolioIduint256Portfolio ID

Returns true if:

  • User is not an MMM
  • Portfolio has positions expiring within 1 day with net obligations
  • Portfolio's cash shortfall > 0
  • Portfolio has liquidatable assets (premium receivables OR non-expiring long positions)

calculatePenaltyRate

Returns the penalty rate for a trading pair.

function calculatePenaltyRate(bytes32 pairId) external view returns (uint256 penaltyRate)
Penalty Rate=Base (1%)+Current IV50%100\text{Penalty Rate} = \text{Base (1\%)} + \frac{\text{Current IV} - 50\%}{100}

Returns: Penalty rate in WAD (e.g., 0.01e18 = 1%).


getSettlementReadinessBuffer

Returns the settlement readiness buffer.

function getSettlementReadinessBuffer() external view returns (uint256 buffer)

Returns: Buffer in WAD (e.g., 0.05e18 = 5%). Adds margin to prevent spam re-liquidation.


getPremiumReceivableDiscountRate

Returns the discount rate for premium receivables.

function getPremiumReceivableDiscountRate() external view returns (uint256 rate)

Returns: Rate in WAD (e.g., 0.05e18 = 5%). Premium receivables are sold at this discount.


getLiquidatorBountyRate

Returns the liquidator bounty rate.

function getLiquidatorBountyRate() external view returns (uint256 rate)

Returns: Rate in WAD (e.g., 0.05e18 = 5%). Bounty = shortfall × rate.


isApprovedLiquidator

Checks if an address is an approved liquidator.

function isApprovedLiquidator(address liquidator) external view returns (bool)

Returns: true if the address is approved to execute settlement readiness liquidations.


Liquidation Functions

These functions require approved liquidators—only addresses approved by the owner can execute settlement readiness liquidations.

liquidatePortfolioForSettlementReadiness

Liquidates non-expiring long positions and premium receivables in a specific portfolio to raise cash for expiring obligations.

function liquidatePortfolioForSettlementReadiness(
    address user,
    uint256 portfolioId,
    uint256 liquidatorPortfolioId
) external returns (SettlementReadinessLiquidationResult memory result)
ParameterTypeDescription
useraddressUser to liquidate
portfolioIduint256Portfolio ID to liquidate
liquidatorPortfolioIduint256Liquidator's portfolio to receive positions

Requirements:

  • Caller must be an approved liquidator
  • Portfolio must be settlement-readiness-liquidatable
  • User must not be an MMM
  • Liquidator must have sufficient USDC deposited
  • Liquidator must remain healthy after acquiring positions

Process (Waterfall):

  1. Verify portfolio is settlement-readiness-liquidatable
  2. Step 1: Liquidate non-expiring long positions first
    • Sort by expiry (longest-dated first for liquidity)
    • Liquidator pays penalized mark price, receives the option position
    • Stop when shortfall is covered
  3. Step 2: Liquidate premium receivables (if shortfall remains)
    • Premium receivables are liquidated at discount rate (default 5%, max 20%)
    • Liquidator acquires premium receivable position
  4. Pay bounty (5% of shortfall, guaranteed by insurance)
  5. Verify liquidator remains healthy after acquiring positions

Emits: PortfolioSettlementReadinessLiquidation, LongPositionSold (per position), PremiumReceivableLiquidated


Owner Functions

setApprovedLiquidator

Approves or revokes an address to execute settlement readiness liquidations.

function setApprovedLiquidator(address liquidator, bool approved) external onlyOwner

Emits: ApprovedLiquidatorUpdated


setSettlementReadinessBuffer

Sets the buffer for settlement readiness calculations.

function setSettlementReadinessBuffer(uint256 buffer) external onlyOwner

Constraints: 0 <= buffer <= 0.2e18 (0-20%)

Emits: SettlementReadinessBufferUpdated


setPremiumReceivableDiscountRate

Sets the discount rate applied to premium receivables during liquidation.

function setPremiumReceivableDiscountRate(uint256 rate) external onlyOwner

Constraints: 0 <= rate <= 0.2e18 (0-20%)

Emits: PremiumReceivableDiscountRateUpdated


setLiquidatorBountyRate

Sets the bounty rate for liquidators.

function setLiquidatorBountyRate(uint256 rate) external onlyOwner

Constraints: 0 <= rate <= 0.1e18 (0-10%)

Emits: LiquidatorBountyRateUpdated


setOperator

Authorizes or deauthorizes an operator.

function setOperator(address operator, bool authorized) external onlyOwner

Contract Reference Setters

function setCollateralVault(address vault_) external onlyOwner
function setPositionManager(address manager_) external onlyOwner
function setSeriesRegistry(address registry_) external onlyOwner
function setQuoter(address quoter_) external onlyOwner
function setOracle(address oracle_) external onlyOwner
function setInsuranceFund(address insuranceFund_) external onlyOwner
function setMarginCalculator(address marginCalculator_) external onlyOwner
function setViewContract(address viewContract_) external onlyOwner

transferOwnership

Transfers contract ownership.

function transferOwnership(address newOwner) external onlyOwner

Events

EventParametersDescription
PortfolioSettlementReadinessLiquidationuser, portfolioId, liquidator, cashShortfall, cashRaised, bounty, positionsLiquidatedSettlement readiness liquidation completed for a portfolio
LongPositionSolduser, liquidator, seriesId, size, markPrice, salePrice, proceedsLong position sold for settlement readiness
PremiumReceivableLiquidateduser, liquidator, seriesId, premiumAmount, discountedValuePremium receivable liquidated at discount
ApprovedLiquidatorUpdatedliquidator, approvedApproved liquidator status changed
SettlementReadinessBufferUpdatedoldBuffer, newBufferBuffer parameter changed
PremiumReceivableDiscountRateUpdatedoldRate, newRateDiscount rate changed
LiquidatorBountyRateUpdatedoldRate, newRateBounty rate changed
OperatorUpdatedoperator, authorizedOperator status changed
OwnershipTransferredpreviousOwner, newOwnerOwnership changed
OwnershipTransferStartedpreviousOwner, newOwnerOwnership transfer initiated (two-step)
CollateralVaultUpdatedoldVault, newVaultCollateral vault address changed
PositionManagerUpdatedoldManager, newManagerPosition manager address changed
SeriesRegistryUpdatedoldRegistry, newRegistrySeries registry address changed
QuoterUpdatedoldQuoter, newQuoterQuoter address changed
OracleUpdatedoldOracle, newOracleOracle address changed
InsuranceFundAddressUpdatedoldFund, newFundInsurance fund address changed
MarginCalculatorUpdatedoldCalculator, newCalculatorMargin calculator address changed
ViewContractUpdatedoldContract, newContractView contract address changed

Liquidation Example

Setup

  • User has 10 long ETH calls (60 days to expiry, mark $116.25)
  • User has 5 short ETH puts (expires tomorrow, worst-case obligation $3,500)
  • Premium receivable: +$600 (received for puts)
  • Deposited collateral: $2,000

Cash Shortfall Calculation

Net Obligation=$3,500$600=$2,900\text{Net Obligation} = \$3,500 - \$600 = \$2,900 Cash Shortfall=$2,900$2,000=$900\text{Cash Shortfall} = \$2,900 - \$2,000 = \$900

Liquidation Process

Step 1: Liquidate non-expiring longs

Contracts to sell ≈ $900 / ($116.25 × 0.99) = ~7.8 calls
Sale proceeds = 7.8 × $116.25 × 0.99 = $897.44

Step 2: Bounty payment

Bounty = min($900 × 5%, $897.44) = $45

Result:

  • User retains ~2.2 long calls
  • User's cash: ~$2,897 (sufficient for settlement)
  • Short puts: unchanged (will settle normally)
  • Liquidator earns $45 bounty + penalty profit

Integration Points

Depends On

ContractPurpose
DiffusalSettlementReadinessCalculatorView function delegation (contract size)
DiffusalCollateralVaultDeposit queries, collateral transfers
DiffusalOptionsPositionManagerPosition queries, updates
DiffusalOptionsSeriesRegistrySeries info for pricing
DiffusalOptionsQuoterMark prices
DiffusalOracleVolatility for penalty calculation
DiffusalMarginCalculatorLiquidator health verification
DiffusalInsuranceFundBounty guarantee

Used By

ContractPurpose
Approved liquidation botsLiquidation triggering (requires approval)
KeepersAutomated pre-settlement monitoring

Security Considerations

Approved Liquidators

Only approved liquidators can execute settlement readiness liquidations:

function liquidatePortfolioForSettlementReadiness(
    address user,
    uint256 portfolioId,
    uint256 liquidatorPortfolioId
) external nonReentrant returns (...)
// Requires: msg.sender is an approved liquidator

Liquidator approval is controlled by the owner via setApprovedLiquidator(). This ensures only trusted entities (e.g., keeper bots, protocol operators) can execute settlement readiness liquidations.

MMM Protection

Main Market Makers cannot be liquidated:

if (pm.isMmm(user)) revert Errors.CannotLiquidateMmm();

Reentrancy Protection

All liquidation functions use nonReentrant modifier.

Bounty Guarantee

The bounty is always paid to incentivize liquidators:

  • First from cash raised
  • Insurance fund covers any shortfall

Liquidator Health Check

Liquidator must remain healthy after acquiring positions. If they would become unhealthy, the transaction reverts.

Oracle Dependency

Liquidations rely on accurate oracle prices. If oracle is stale or manipulated:

  • Penalty calculation may be incorrect
  • Mark prices may be wrong
  • Worst-case obligations may be miscalculated

Code Reference

Source: packages/contracts/src/DiffusalSettlementReadinessLiquidator.sol

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

Key Constants

// From Constants.sol
uint256 constant LIQUIDATION_PENALTY_BASE = 0.01e18;           // 1%
uint256 constant LIQUIDATION_PENALTY_IV_BASELINE = 0.5e18;     // 50%
uint256 constant SETTLEMENT_READINESS_WINDOW = 1 days;         // 1 day before expiry
uint256 constant STRESS_SPOT_UP = 1.3e18;                      // +30%
uint256 constant STRESS_SPOT_DOWN = 0.7e18;                    // -30%
uint256 constant WAD = 1e18;

Protocol Documentation

Contract Documentation

On this page