Diffusal
Protocol

Order Book

How the limit order book works in the Diffusal protocol

The order book enables peer-to-peer limit order trading for options. Unlike the RFQ system where users trade against the Main Market Maker, the order book allows any user to place and fill orders against each other.


Overview

The order book uses a hybrid architecture combining off-chain order management with on-chain settlement:

PhaseLocationDescription
Order CreationOff-chainMaker signs an EIP-712 order (zero gas cost)
Order StorageOff-chainOrders indexed by relayer for discovery
Order MatchingOff-chainTaker finds orders in the order book
ExecutionOn-chainTaker submits fill transaction
SettlementOn-chainPositions minted, premium transferred

Key Benefits:

  • Gas-free placement — Orders are signed messages, no transaction needed
  • Transparent depth — Full order book visible to all participants
  • Partial fills — Orders can be filled incrementally
  • Maker incentives — Negative fees (rebates) possible for liquidity providers

Order Structure

Each order contains everything needed for on-chain execution:

Order Fields

FieldDescription
makerAddress that created and signed the order
seriesIdOption series identifier
isBuyDirection: true = bid (maker buys), false = ask (maker sells)
pricePremium per contract in WAD (1e18 = $1.00)
sizeMaximum number of contracts
nonceReplay protection counter
expiryOrder validity deadline (unix timestamp)

Series Identification

The seriesId uniquely identifies an option series:

seriesId=keccak256(pairId,strike,optionExpiry,isCall)\text{seriesId} = \text{keccak256}(\text{pairId}, \text{strike}, \text{optionExpiry}, \text{isCall})

When filling an order, the taker provides the full series parameters for verification and lazy registration.


Order Types

Bid Order (isBuy = true)

The maker wants to buy options. They are placing a bid. For example, if Alice places a bid to buy 10 contracts at $150 each and Bob fills it:

  • Alice (maker) receives +10 position (long)
  • Bob (taker) receives -10 position (short)
  • Alice pays Bob $1,500 premium

Ask Order (isBuy = false)

The maker wants to sell options. They are placing an ask. For example, if Carol places an ask to sell 10 contracts at $160 each and Dave fills it:

  • Carol (maker) receives -10 position (short)
  • Dave (taker) receives +10 position (long)
  • Dave pays Carol $1,600 premium

Position Flow Summary

Order TypeMaker PositionTaker PositionPremium Flow
Bid (isBuy=true)+size (long)-size (short)Maker → Taker
Ask (isBuy=false)-size (short)+size (long)Taker → Maker

Key insight: The buyer always gets long (+size), the seller always gets short (-size). The isBuy flag determines which party is the buyer.


Execution Flow

High-Level Flow

Off-Chain Phase:

  1. Maker creates and signs an EIP-712 order containing maker, seriesId, isBuy, price, size, nonce, and expiry
  2. Maker broadcasts the order and signature to a relayer/indexer
  3. Taker discovers the order by querying the relayer for bids/asks on a seriesId

On-Chain Phase: 4. Taker submits a fill transaction with the order, signature, fill amount, and series parameters 5. Contract validates: signature matches maker, order not expired, series not expired, nonce matches, fill amount ≤ remaining size, no self-trade, and series ID matches parameters 6. Contract executes: registers series if new (lazy creation), updates positions for maker and taker, transfers premium from buyer to seller, and collects fees 7. Emits OrderFilled event

Validation Checks

The contract performs these checks before executing any trade:

CheckRequirement
Order expiryblock.timestamp < order.expiry
Option expiryblock.timestamp < optionExpiry
Signature validityEIP-712 signature matches maker
Nonce validityOrder nonce matches maker's current nonce
Fill capacityfillAmount ≤ order.size - filled
Self-trademsg.sender ≠ order.maker
Series ID matchseriesId == hash(pairId, strike, optionExpiry, isCall)

Trade Examples

Example 1: Filling a Bid

Alice places a bid to buy 10 ETH-USD 3000callsat3000 calls at 150 premium each. Before the trade, Alice has no position and 5,000USDC;Bobhasnopositionand5,000 USDC; Bob has no position and 3,000 USDC.

When Bob fills Alice's bid (fillAmount = 10):

  • Alice receives +10 position (long), Bob receives -10 position (short)
  • Premium: 10 × 150=150 = 1,500 flows from Alice to Bob
  • After: Alice has 3,500USDC,Bobhas3,500 USDC, Bob has 4,500 USDC

Example 2: Filling an Ask

Carol places an ask to sell 5 ETH-USD 3000putsat3000 puts at 200 premium each. Before the trade, Carol has no position and 4,000USDC;Davehasnopositionand4,000 USDC; Dave has no position and 6,000 USDC.

When Dave fills Carol's ask (fillAmount = 5):

  • Carol receives -5 position (short), Dave receives +5 position (long)
  • Premium: 5 × 200=200 = 1,000 flows from Dave to Carol
  • After: Carol has 5,000USDC,Davehas5,000 USDC, Dave has 5,000 USDC

Premium Calculation

The premium transferred is calculated from the order price and fill amount:

premium=price×fillAmount1018\text{premium} = \frac{\text{price} \times \text{fillAmount}}{10^{18}}

Buyer always pays seller:

DirectionBuyerSellerPremium Flow
Bid (isBuy = true)MakerTakerMaker → Taker
Ask (isBuy = false)TakerMakerTaker → Maker

Fee Structure

The order book uses a maker/taker fee model with possible maker rebates.

Fee Parameters

ParameterDescription
makerFeeBpsMaker fee in basis points (can be negative for rebates)
takerFeeBpsTaker fee in basis points (always positive)

Where 1 BPS = 0.01%.

Fee Calculation

makerFee=premium×makerFeeBps10000\text{makerFee} = \frac{\text{premium} \times \text{makerFeeBps}}{10000} takerFee=premium×takerFeeBps10000\text{takerFee} = \frac{\text{premium} \times \text{takerFeeBps}}{10000}

Fee Examples

Standard fees (makerFeeBps = 5, takerFeeBps = 10): On a 1,000premiumtrade,themakerpays1,000 premium trade, the maker pays 0.50 to the protocol and the taker pays 1.00,fortotalprotocolrevenueof1.00, for total protocol revenue of 1.50.

Maker rebate (makerFeeBps = -3, takerFeeBps = 10): On a 1,000premiumtrade,themakerreceives1,000 premium trade, the maker receives 0.30 from the protocol (rebate) and the taker pays 1.00,fornetprotocolrevenueof1.00, for net protocol revenue of 0.70.

Fee Invariant

The protocol enforces:

takerFeeBps+makerFeeBps>0\text{takerFeeBps} + \text{makerFeeBps} > 0

This ensures net positive protocol revenue even with maker rebates.


Order Cancellation

Cancellation Methods

MethodEffectGas Cost
Wait for expiryOrder naturally expiresFree
Increment nonceInvalidates ALL pending ordersOne transaction
Individual cancelCancel specific orderOne transaction per order

Nonce Management

Each user has an independent nonce counter:

PropertyDescription
Per-user counterEach user has their own nonce
Order validationOrder nonce must match current user nonce
Bulk cancellationIncrementing nonce invalidates all pending orders

Bulk cancellation example: If Alice has 50 pending orders all with nonce = 7, calling incrementNonce() changes her nonce to 8, instantly invalidating all 50 orders due to nonce mismatch.

Individual Cancellation

To cancel a specific order without affecting others:

  1. Submit the order and signature to cancelOrder()
  2. Contract verifies caller is the maker
  3. Order hash marked as cancelled
  4. Future fill attempts will revert

Partial Fills

Orders support partial fills—the taker can fill less than the full order size.

Fill Tracking

StateDescription
orderFilled[orderHash]Amount already filled
Remainingorder.size - orderFilled[orderHash]

An order can be filled multiple times until fully consumed.

Partial Fill Example

Consider Alice's order with size = 100 at $150:

  1. Fill 1: Bob fills 30 → orderFilled = 30, remaining = 70
  2. Fill 2: Carol fills 50 → orderFilled = 80, remaining = 20
  3. Fill 3: Dave tries to fill 30, but only 20 available → reverts with FillAmountExceedsRemaining
  4. Fill 3 (corrected): Dave fills 20 → orderFilled = 100, remaining = 0 (order fully filled)

Batch Operations

Batch Fill

Fill multiple orders in a single transaction using fillOrderBatch() with arrays of orders, signatures, fill amounts, and series parameters.

Use cases:

  • Market orders that consume multiple price levels
  • Arbitrage across multiple orders
  • Reduced gas cost per fill

Batch Cancel

Cancel multiple orders in one transaction using cancelOrderBatch() with arrays of orders and signatures.


Lazy Series Registration

The order book supports lazy series registration—if the series doesn't exist when an order is filled, it's created automatically.

When filling an order, the contract first checks if the series exists. If yes, it proceeds with the fill. If no, it validates the series parameters (verifying the hash matches seriesId, pairId is registered, optionExpiry is in the future, and strike > 0), then registers the series and proceeds with the fill.

This enables trading of new strikes/expiries without upfront admin setup.


Security Considerations

Signature Protection

ProtectionMechanism
EIP-712 domainIncludes chainId and contract address
NonceOrder nonce must match user's current nonce
Fill trackingorderFilled[orderHash] prevents double-filling
ExpiryOrders have limited validity

Self-Trade Prevention

The contract prevents a user from filling their own order by reverting with SelfTrade if the caller is the order maker.

Reentrancy Protection

All fill operations use reentrancy guards to prevent callback attacks during token transfers.


Contract Integration

Dependencies

The DiffusalOptionsOrderBook contract integrates with:

  • DiffusalOptionsPositionManager: For updatePosition() to mint long/short positions
  • SeriesRegistry: For getOrCreateSeries() (lazy series registration) and series validation/lifecycle
  • IERC20 (Collateral Token): For premium transfers between buyer and seller, and fee collection to feeRecipient

Order Book vs RFQ

AspectOrder BookRFQ
CounterpartyAny userMain Market Maker only
Price DiscoveryMarket-driven via bids/asksMMM quotes
Order CreationFree (signed message)N/A (MMM creates quotes)
ExecutionFill any visible orderFill MMM's signed quote
Partial FillsSupportedSupported
Maker FeesCan be negative (rebates)N/A
Best ForPrice discovery, liquidity provisionLarge orders, instant execution

Summary

ComponentDescription
OrderEIP-712 signed message with maker, seriesId, isBuy, price, size, nonce, expiry
ExecutionOn-chain validation + position minting + premium transfer
DirectionisBuy=true: maker gets long, taker gets short; false: opposite
PremiumBuyer pays seller; calculated as price × fillAmount
FeesMaker fee (can be negative rebate) + taker fee; collected per fill
NoncePer-user counter; increment to invalidate all pending orders
CancellationVia nonce increment (all orders) or individual cancel
Partial fillsSupported via fill amount tracking per order hash

The order book provides:

  • Gas-free order placement — EIP-712 signatures (no on-chain tx needed)
  • Transparent depth — Off-chain indexer provides real-time order book
  • Atomic execution — Single transaction for fill, settlement, fees
  • Flexible fills — Partial fills, batch fills supported
  • Maker incentives — Rebates possible via negative maker fees
  • Security — Signature verification, nonce protection, self-trade prevention

Contract Implementation

ContractRole
DiffusalOptionsOrderBookCore order book logic, EIP-712 validation, and fee collection
DiffusalOptionsPositionManagerPosition minting via updatePosition()
DiffusalOptionsSeriesRegistryLazy series registration and validation
DiffusalCollateralVaultCollateral for margin requirements

  • RFQ Flow — Professional quotes from market makers with instant execution
  • Options Creation — How series are lazily registered on first trade
  • Margin System — Collateral requirements for positions

On this page