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:
Where:
- Cash Shortfall = Worst-case obligations − Deposited collateral
- Liquidatable Assets = Non-expiring LONG positions OR Premium receivables
Key Difference from Regular Liquidation:
| Aspect | Regular Liquidation | Settlement Readiness |
|---|---|---|
| Trigger | Equity < Maintenance Margin | Cash shortfall for expiring obligations |
| What's liquidated | All positions (longs AND shorts) | Non-expiring LONGs + premium receivables |
| Goal | Restore margin health | Raise cash for settlement |
| Can shorts be liquidated? | Yes | No (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:
-
Non-expiring LONG positions (sorted by expiry, longest-dated first)
- Liquidator pays penalized mark price
- User receives cash for positions sold
-
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)| Parameter | Type | Description |
|---|---|---|
user | address | User address |
portfolioId | uint256 | Portfolio 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)| Parameter | Type | Description |
|---|---|---|
user | address | User address |
portfolioId | uint256 | Portfolio 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)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)| Parameter | Type | Description |
|---|---|---|
user | address | User to liquidate |
portfolioId | uint256 | Portfolio ID to liquidate |
liquidatorPortfolioId | uint256 | Liquidator'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):
- Verify portfolio is settlement-readiness-liquidatable
- 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
- Step 2: Liquidate premium receivables (if shortfall remains)
- Premium receivables are liquidated at discount rate (default 5%, max 20%)
- Liquidator acquires premium receivable position
- Pay bounty (5% of shortfall, guaranteed by insurance)
- 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 onlyOwnerEmits: ApprovedLiquidatorUpdated
setSettlementReadinessBuffer
Sets the buffer for settlement readiness calculations.
function setSettlementReadinessBuffer(uint256 buffer) external onlyOwnerConstraints: 0 <= buffer <= 0.2e18 (0-20%)
Emits: SettlementReadinessBufferUpdated
setPremiumReceivableDiscountRate
Sets the discount rate applied to premium receivables during liquidation.
function setPremiumReceivableDiscountRate(uint256 rate) external onlyOwnerConstraints: 0 <= rate <= 0.2e18 (0-20%)
Emits: PremiumReceivableDiscountRateUpdated
setLiquidatorBountyRate
Sets the bounty rate for liquidators.
function setLiquidatorBountyRate(uint256 rate) external onlyOwnerConstraints: 0 <= rate <= 0.1e18 (0-10%)
Emits: LiquidatorBountyRateUpdated
setOperator
Authorizes or deauthorizes an operator.
function setOperator(address operator, bool authorized) external onlyOwnerContract 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 onlyOwnertransferOwnership
Transfers contract ownership.
function transferOwnership(address newOwner) external onlyOwnerEvents
| Event | Parameters | Description |
|---|---|---|
PortfolioSettlementReadinessLiquidation | user, portfolioId, liquidator, cashShortfall, cashRaised, bounty, positionsLiquidated | Settlement readiness liquidation completed for a portfolio |
LongPositionSold | user, liquidator, seriesId, size, markPrice, salePrice, proceeds | Long position sold for settlement readiness |
PremiumReceivableLiquidated | user, liquidator, seriesId, premiumAmount, discountedValue | Premium receivable liquidated at discount |
ApprovedLiquidatorUpdated | liquidator, approved | Approved liquidator status changed |
SettlementReadinessBufferUpdated | oldBuffer, newBuffer | Buffer parameter changed |
PremiumReceivableDiscountRateUpdated | oldRate, newRate | Discount rate changed |
LiquidatorBountyRateUpdated | oldRate, newRate | Bounty rate changed |
OperatorUpdated | operator, authorized | Operator status changed |
OwnershipTransferred | previousOwner, newOwner | Ownership changed |
OwnershipTransferStarted | previousOwner, newOwner | Ownership transfer initiated (two-step) |
CollateralVaultUpdated | oldVault, newVault | Collateral vault address changed |
PositionManagerUpdated | oldManager, newManager | Position manager address changed |
SeriesRegistryUpdated | oldRegistry, newRegistry | Series registry address changed |
QuoterUpdated | oldQuoter, newQuoter | Quoter address changed |
OracleUpdated | oldOracle, newOracle | Oracle address changed |
InsuranceFundAddressUpdated | oldFund, newFund | Insurance fund address changed |
MarginCalculatorUpdated | oldCalculator, newCalculator | Margin calculator address changed |
ViewContractUpdated | oldContract, newContract | View 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
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.44Step 2: Bounty payment
Bounty = min($900 × 5%, $897.44) = $45Result:
- 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
| Contract | Purpose |
|---|---|
| DiffusalSettlementReadinessCalculator | View function delegation (contract size) |
| DiffusalCollateralVault | Deposit queries, collateral transfers |
| DiffusalOptionsPositionManager | Position queries, updates |
| DiffusalOptionsSeriesRegistry | Series info for pricing |
| DiffusalOptionsQuoter | Mark prices |
| DiffusalOracle | Volatility for penalty calculation |
| DiffusalMarginCalculator | Liquidator health verification |
| DiffusalInsuranceFund | Bounty guarantee |
Used By
| Contract | Purpose |
|---|---|
| Approved liquidation bots | Liquidation triggering (requires approval) |
| Keepers | Automated 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 liquidatorLiquidator 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;Related
Protocol Documentation
- Liquidation (Protocol) — High-level liquidation mechanics, settlement readiness details
- Options Settlement (Protocol) — Why cash is needed at expiry
- Margin System (Protocol) — IM/MM calculation, stress scenarios
Contract Documentation
- DiffusalSettlementReadinessCalculator — View functions (extracted for contract size)
- DiffusalLiquidationEngine — Regular margin-based liquidation
- DiffusalInsuranceFund — Bounty guarantee mechanism
- DiffusalCollateralVault — Collateral management
- DiffusalOptionsPositionManager — Position tracking
- DiffusalSettlementEngine — Settlement of expired options