Diffusal
Contracts

DiffusalOptionsSeriesRegistry

Registry for option series lifecycle management

The DiffusalOptionsSeriesRegistry contract manages the lifecycle of option series from creation through settlement. It handles lazy series creation on first trade, validates series parameters, and coordinates TWAP-based settlement when options expire.


Overview

An option series is defined by four parameters:

ParameterDescription
pairIdTrading pair (e.g., keccak256("ETH-USD"))
strikeStrike price (WAD)
expiryOption expiry timestamp
isCallCall (true) or put (false)

Series Lifecycle

1. CREATION (lazy)
   └─ Created on first trade via getOrCreateSeries()

2. ACTIVE
   └─ Trading allowed until expiry

3. EXPIRED
   └─ Trading stops, waiting for settlement

4. SETTLED
   └─ Settlement price set, positions can settle

Key Concepts

Lazy Series Creation

Series are not created in advance. Instead, they're created on-demand when the first trade occurs:

// Called by OrderBook or RFQ on every trade
SERIES_REGISTRY.getOrCreateSeries(seriesId, seriesParams);

This approach:

  • Eliminates need for admin intervention to list new options
  • Reduces gas costs (only create series that are actually traded)
  • Allows infinite series combinations

Series ID Generation

The series ID is a deterministic hash of the series parameters:

seriesId = keccak256(abi.encodePacked(pairId, strike, expiry, isCall));

TWAP Settlement

When a series expires, it's settled using a 1-hour Time-Weighted Average Price (TWAP) from the PriceHistory contract:

(settlementPrice, snapshotCount) = priceHistory.getTwap(pairId, expiry);

This prevents settlement price manipulation via single-block price attacks.


Storage & State

/// @custom:storage-location erc7201:diffusal.storage.SeriesRegistry
struct SeriesRegistryStorage {
    address owner;
    mapping(address => bool) operators;        // Authorized operators
    mapping(bytes32 => SeriesInfo) series;     // Series ID → info
    uint256 minTimeToExpiry;                   // Min time for new series
    address priceHistory;                      // TWAP provider
}

SeriesInfo Struct

struct SeriesInfo {
    bytes32 pairId;        // Trading pair identifier
    uint256 strike;        // Strike price (WAD)
    uint256 expiry;        // Option expiry timestamp
    bool isCall;           // true = call, false = put
    bool isSettled;        // Whether settled
    uint256 settlementPrice; // Settlement price (WAD, 0 until settled)
}

SeriesParams Struct

struct SeriesParams {
    bytes32 pairId;
    uint256 strike;
    uint256 optionExpiry;
    bool isCall;
}

External Functions

Series Management

getOrCreateSeries

Gets existing series or creates a new one.

function getOrCreateSeries(bytes32 seriesId, SeriesParams calldata params)
    external returns (SeriesInfo memory info)
ParameterTypeDescription
seriesIdbytes32Expected series identifier
paramsSeriesParamsSeries parameters for validation/creation

If series exists:

  • Validates series is still tradeable (not expired, not settled)
  • Returns existing SeriesInfo

If series doesn't exist:

  • Validates seriesId matches computed hash of params
  • Validates strike > 0
  • Validates expiry > now + minTimeToExpiry
  • Creates and returns new series

Emits: SeriesCreated (on new series only)


settle

Settles an expired series using TWAP.

function settle(bytes32 seriesId) external

Requirements:

  • Series must exist
  • Series must be expired (block.timestamp >= expiry)
  • Series must not already be settled

Process:

  1. Query TWAP from PriceHistory contract
  2. Set settlement price
  3. Mark series as settled

Emits: SeriesSettled, optionally SettlementFallbackUsed

Note: This function is permissionless—anyone can trigger settlement.


View Functions

seriesExists

Checks if a series has been created.

function seriesExists(bytes32 seriesId) external view returns (bool)

getSeriesInfo

Returns full series information.

function getSeriesInfo(bytes32 seriesId) external view returns (SeriesInfo memory info)

Reverts: SeriesNotFound if series doesn't exist.


isExpired

Checks if a series has expired.

function isExpired(bytes32 seriesId) external view returns (bool)

isSettled

Checks if a series has been settled.

function isSettled(bytes32 seriesId) external view returns (bool)

canTrade

Checks if a series can be traded.

function canTrade(bytes32 seriesId) external view returns (bool)

Returns true if:

  • Series doesn't exist yet (will be created on first trade), OR
  • Series exists AND not expired AND not settled

computeSeriesId

Computes the series ID from parameters.

function computeSeriesId(bytes32 pairId, uint256 strike, uint256 expiry, bool isCall)
    external pure returns (bytes32)

validateSeriesId

Validates that a series ID matches its parameters.

function validateSeriesId(bytes32 seriesId, SeriesParams calldata params)
    external pure returns (bool)

Owner Functions

setOperator

Authorizes or deauthorizes an operator.

function setOperator(address operator, bool authorized) external onlyOwner

Emits: OperatorUpdated


setMinTimeToExpiry

Sets the minimum time to expiry for new series.

function setMinTimeToExpiry(uint256 _minTimeToExpiry) external onlyOwner

Default: MIN_TIME_TO_EXPIRY from Constants (1 hour)


setPriceHistory

Sets the price history contract for TWAP settlement.

function setPriceHistory(address priceHistory_) external onlyOwner

Emits: PriceHistoryUpdated


transferOwnership

Transfers contract ownership.

function transferOwnership(address newOwner) external onlyOwner

Emits: OwnershipTransferred


Events

EventParametersDescription
SeriesCreatedseriesId, pairId, strike, expiry, isCallNew series created
SeriesSettledseriesId, settlementPrice, snapshotCountSeries settlement completed
SettlementFallbackUsedseriesIdNo TWAP snapshots available
OperatorUpdatedoperator, authorizedOperator status changed
PriceHistoryUpdatedoldPriceHistory, newPriceHistoryPrice history contract changed
MinTimeToExpiryUpdatedoldMinTime, newMinTimeMinimum time to expiry changed
OwnershipTransferredpreviousOwner, newOwnerOwnership changed

Integration Points

Depends On

ContractPurpose
DiffusalPriceHistoryTWAP for settlement

Used By

ContractPurpose
DiffusalOptionsOrderBookSeries validation on order fill
DiffusalOptionsRFQSeries validation on RFQ fill
DiffusalSettlementEngineSettlement price lookup
DiffusalCollateralVaultSeries info for margin calculations

Security Considerations

Series ID Validation

Every trade validates that the provided seriesId matches the computed hash:

if (_computeSeriesIdFromParams(p) != seriesId) revert Errors.InvalidSeriesId();

This prevents:

  • Trading non-existent series with arbitrary IDs
  • Parameter mismatches between order and series

Minimum Time to Expiry

New series must have sufficient time until expiry:

if (p.optionExpiry < block.timestamp + $.minTimeToExpiry) {
    revert Errors.OptionExpiryTooSoon();
}

This ensures:

  • Adequate time for position management
  • Prevention of near-expiry manipulation

Settlement Security

TWAP-based settlement provides:

  • Resistance to single-block price manipulation
  • Fair settlement price based on historical data
  • Permissionless triggering (no admin bottleneck)

Code Reference

Source: packages/contracts/src/DiffusalOptionsSeriesRegistry.sol

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

Testnet: View on MonadVision

Key Constants

// From Constants.sol
uint256 constant MIN_TIME_TO_EXPIRY = 1 hours;

On this page