Options Settlement
How option series are settled at expiry using manipulation-resistant TWAP pricing
This document explains how option series are settled at expiry using TWAP pricing and how payouts are calculated and distributed. Options are created via lazy registration—see Options Creation for series mechanics.
Design Principles
The options settlement system follows these core principles:
| Principle | Description |
|---|---|
| TWAP Settlement | Settlement uses a 1-hour time-weighted average price |
| Shared Price History | Rolling price buffer maintained per trading pair |
| Permissionless | Anyone can trigger snapshots and settlements |
| Manipulation Resistant | 1-hour TWAP window prevents price manipulation |
Settlement Phase
At or after expiry (block.timestamp >= expiry), the settlement process begins:
- Anyone calls
settle(seriesId) - Query PriceHistory for 1-hour TWAP ending at expiry
- Calculate intrinsic value: Call = max(0, settlementPrice - strike), Put = max(0, strike - settlementPrice)
- Mark series as settled
- Trading disabled for this series
Settlement Window: Settlement must occur within 1 hour after expiry (while price history data is still available).
Settlement Execution
After the settlement price is finalized, the protocol collects from shorts and pays longs.
Critical Invariant: The protocol takes no position. Shorts pay longs. The protocol is only the intermediary.
Step 1: Calculate total obligations
- Intrinsic value = max(0, settlementPrice - strike) for calls, or max(0, strike - settlementPrice) for puts
- Total long payout = Σ(longPosition × intrinsicValue)
- Total short obligation = Σ(|shortPosition| × intrinsicValue)
- Note: totalLongPayout == totalShortObligation (by invariant)
Step 2: Collect from shorts
- For each short holder, deduct |position| × intrinsicValue from collateral
- If a short has insufficient collateral, take what's available and record shortfall
- Accumulate into settlement pool
Step 3: Cover shortfall from insurance fund (if any)
- Shortfall = totalLongPayout - collectedFromShorts
- If shortfall > 0, the insurance fund covers it
Step 4: Pay longs
- For each long holder, credit position × intrinsicValue to collateral
- Zero out all positions for this series
Settlement Flow
Funds flow from short holders to the settlement pool, then to long holders. If there's a shortfall (shorts couldn't cover full obligations), the insurance fund provides coverage.
Price History (TWAP Data)
Instead of accumulating TWAP per-series, a shared PriceHistory contract maintains rolling price snapshots per-pair. This is more efficient:
- One keeper job per pair (not per series)
- Shared storage — all ETH-USD options use the same price buffer
- Guaranteed data — as long as the pair is active, price history exists
Buffer Configuration
| Parameter | Value | Description |
|---|---|---|
BUFFER_DURATION | 2 hours | Total history retained |
TWAP_WINDOW | 1 hour | Window for TWAP calculation |
MIN_SNAPSHOT_INTERVAL | 15 seconds | Minimum time between snapshots |
BUFFER_SIZE | 240 | Number of snapshots in buffer |
Key insight: With 2 hours of data and a 1-hour TWAP window, there is a 1-hour buffer after expiry to settle. As long as settlement happens within 1 hour of expiry, the required price data is available.
TWAP Calculation
At settlement, the TWAP is calculated from price snapshots in the window [expiry - 1 hour, expiry]:
where is the number of snapshots in the 1-hour window.
If no snapshots exist in the window (fallback case), the current spot price is used.
Intrinsic Value Calculation
At Settlement
For calls:
For puts:
Payout Calculation
For each user:
- Positive position (long) → positive payout if ITM
- Negative position (short) → negative payout (obligation) if ITM
Keeper Operations
Keepers automate two simple jobs:
| Job | Action | Frequency |
|---|---|---|
| Price Snapshots | takeSnapshot(pairId) | Every ~30 seconds per pair |
| Settlement | settle(seriesId) | After expiry for each series |
All functions are permissionless and self-validating—they revert if called too early or already completed.
Batch Operations
For gas efficiency, keepers can batch multiple operations:
takeSnapshotBatch(pairIds[])— Snapshot multiple pairs in one transactionsettleBatch(seriesIds[])— Settle multiple series in one transaction
Security Considerations
TWAP Manipulation Resistance
- 1-hour window requires sustained price manipulation
- Minimum snapshot interval prevents spam/gaming
- Multiple snapshots (~120) smooth out outliers
Oracle Failures
- Fallback to spot price if TWAP data is insufficient
- Staleness checks on oracle reads
- Events emitted when fallback is used (for monitoring)
Keeper Failures
- All functions are permissionless (anyone can call)
- Multiple keepers can operate in parallel without conflict
- Protocol can run backup keepers
Front-running
- TWAP accumulation is rate-limited
- Settlement can only happen after expiry
- No advantage to front-running settlement (price is already fixed by TWAP)
Settlement Engine Contract
The DiffusalSettlementEngine contract implements the settlement execution logic. It is an operator on both PositionManager and CollateralVault.
Key Functions
settlePosition(address user, bytes32 seriesId)— Settle a single user's position (operator only)settlePositionBatch(address[] users, bytes32 seriesId)— Batch settle multiple userscalculateIntrinsicValue(bytes32 seriesId)— View function to calculate intrinsic valuecalculateSettlementAmount(bytes32 seriesId, int256 positionSize)— View function to calculate settlement amountisPositionSettled(address user, bytes32 seriesId)— View function to check if position is already settled
Settlement Result
Each settlement returns a result containing: user address, settled status, seriesId, original position size (+ = long, - = short), intrinsic value per contract (WAD), and settlement amount (USDC, 6 decimals, signed).
Integration
The Settlement Engine must be registered as an operator on both PositionManager and CollateralVault during deployment.
Summary
| Phase | Timing | Actions |
|---|---|---|
| Price Snapshots | Continuous | Keeper takes snapshots per-pair every ~30s |
| Settlement | At/after expiry (within 1hr) | Query TWAP, calculate intrinsic value, mark settled |
| Execution | After settlement | Collect from shorts, pay longs, zero positions |
The system is:
- Efficient — Shared price history per-pair (not per-series)
- Permissionless — Anyone can take snapshots or settle
- Manipulation-resistant — 1-hour TWAP prevents price manipulation
- Fallback-safe — Current spot price fallback if no snapshots available
Contract Implementation
| Contract | Role |
|---|---|
| DiffusalSettlementEngine | Core settlement logic and payout calculation |
| DiffusalPriceHistory | Rolling price snapshots and TWAP computation |
| DiffusalOptionsSeriesRegistry | Series lifecycle and settlement price storage |
| DiffusalCollateralVault | Collateral transfers during settlement |
Related
- Options Creation — How series are created via lazy registration
- Margin System — Collateral requirements before settlement
- Liquidation — What happens if users can't cover obligations