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:
| Phase | Location | Description |
|---|---|---|
| Order Creation | Off-chain | Maker signs an EIP-712 order (zero gas cost) |
| Order Storage | Off-chain | Orders indexed by relayer for discovery |
| Order Matching | Off-chain | Taker finds orders in the order book |
| Execution | On-chain | Taker submits fill transaction |
| Settlement | On-chain | Positions 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
| Field | Description |
|---|---|
maker | Address that created and signed the order |
seriesId | Option series identifier |
isBuy | Direction: true = bid (maker buys), false = ask (maker sells) |
price | Premium per contract in WAD (1e18 = $1.00) |
size | Maximum number of contracts |
nonce | Replay protection counter |
expiry | Order validity deadline (unix timestamp) |
Series Identification
The seriesId uniquely identifies an option series:
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 Type | Maker Position | Taker Position | Premium 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:
- Maker creates and signs an EIP-712 order containing maker, seriesId, isBuy, price, size, nonce, and expiry
- Maker broadcasts the order and signature to a relayer/indexer
- 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:
| Check | Requirement |
|---|---|
| Order expiry | block.timestamp < order.expiry |
| Option expiry | block.timestamp < optionExpiry |
| Signature validity | EIP-712 signature matches maker |
| Nonce validity | Order nonce matches maker's current nonce |
| Fill capacity | fillAmount ≤ order.size - filled |
| Self-trade | msg.sender ≠ order.maker |
| Series ID match | seriesId == hash(pairId, strike, optionExpiry, isCall) |
Trade Examples
Example 1: Filling a Bid
Alice places a bid to buy 10 ETH-USD 150 premium each. Before the trade, Alice 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 × 1,500 flows from Alice to Bob
- After: Alice has 4,500 USDC
Example 2: Filling an Ask
Carol places an ask to sell 5 ETH-USD 200 premium each. Before the trade, Carol 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 × 1,000 flows from Dave to Carol
- After: Carol has 5,000 USDC
Premium Calculation
The premium transferred is calculated from the order price and fill amount:
Buyer always pays seller:
| Direction | Buyer | Seller | Premium Flow |
|---|---|---|---|
Bid (isBuy = true) | Maker | Taker | Maker → Taker |
Ask (isBuy = false) | Taker | Maker | Taker → Maker |
Fee Structure
The order book uses a maker/taker fee model with possible maker rebates.
Fee Parameters
| Parameter | Description |
|---|---|
makerFeeBps | Maker fee in basis points (can be negative for rebates) |
takerFeeBps | Taker fee in basis points (always positive) |
Where 1 BPS = 0.01%.
Fee Calculation
Fee Examples
Standard fees (makerFeeBps = 5, takerFeeBps = 10): On a 0.50 to the protocol and the taker pays 1.50.
Maker rebate (makerFeeBps = -3, takerFeeBps = 10): On a 0.30 from the protocol (rebate) and the taker pays 0.70.
Fee Invariant
The protocol enforces:
This ensures net positive protocol revenue even with maker rebates.
Order Cancellation
Cancellation Methods
| Method | Effect | Gas Cost |
|---|---|---|
| Wait for expiry | Order naturally expires | Free |
| Increment nonce | Invalidates ALL pending orders | One transaction |
| Individual cancel | Cancel specific order | One transaction per order |
Nonce Management
Each user has an independent nonce counter:
| Property | Description |
|---|---|
| Per-user counter | Each user has their own nonce |
| Order validation | Order nonce must match current user nonce |
| Bulk cancellation | Incrementing 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:
- Submit the order and signature to
cancelOrder() - Contract verifies caller is the maker
- Order hash marked as cancelled
- Future fill attempts will revert
Partial Fills
Orders support partial fills—the taker can fill less than the full order size.
Fill Tracking
| State | Description |
|---|---|
orderFilled[orderHash] | Amount already filled |
| Remaining | order.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:
- Fill 1: Bob fills 30 → orderFilled = 30, remaining = 70
- Fill 2: Carol fills 50 → orderFilled = 80, remaining = 20
- Fill 3: Dave tries to fill 30, but only 20 available → reverts with FillAmountExceedsRemaining
- 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
| Protection | Mechanism |
|---|---|
| EIP-712 domain | Includes chainId and contract address |
| Nonce | Order nonce must match user's current nonce |
| Fill tracking | orderFilled[orderHash] prevents double-filling |
| Expiry | Orders 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
| Aspect | Order Book | RFQ |
|---|---|---|
| Counterparty | Any user | Main Market Maker only |
| Price Discovery | Market-driven via bids/asks | MMM quotes |
| Order Creation | Free (signed message) | N/A (MMM creates quotes) |
| Execution | Fill any visible order | Fill MMM's signed quote |
| Partial Fills | Supported | Supported |
| Maker Fees | Can be negative (rebates) | N/A |
| Best For | Price discovery, liquidity provision | Large orders, instant execution |
Summary
| Component | Description |
|---|---|
| Order | EIP-712 signed message with maker, seriesId, isBuy, price, size, nonce, expiry |
| Execution | On-chain validation + position minting + premium transfer |
| Direction | isBuy=true: maker gets long, taker gets short; false: opposite |
| Premium | Buyer pays seller; calculated as price × fillAmount |
| Fees | Maker fee (can be negative rebate) + taker fee; collected per fill |
| Nonce | Per-user counter; increment to invalidate all pending orders |
| Cancellation | Via nonce increment (all orders) or individual cancel |
| Partial fills | Supported 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
| Contract | Role |
|---|---|
| DiffusalOptionsOrderBook | Core order book logic, EIP-712 validation, and fee collection |
| DiffusalOptionsPositionManager | Position minting via updatePosition() |
| DiffusalOptionsSeriesRegistry | Lazy series registration and validation |
| DiffusalCollateralVault | Collateral for margin requirements |
Related
- 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