CLOBMatchingFacet
🏠 Home > Smart Contracts/Overview > Facets > CLOBMatchingFacet
CLOBMatchingFacet.sol is the matching engine of Aurellion's Central Limit Order Book. It implements price-time priority matching, trade settlement, and order book depth management using Red-Black Trees for O(log n) performance.
Overview
| Property | Value |
|---|---|
| File | contracts/diamond/facets/CLOBMatchingFacet.sol |
| Inherits | ReentrancyGuard |
| Algorithm | Price-Time Priority (best price first, then FIFO within price level) |
| Data structure | Red-Black Tree for price levels + linked list FIFO queues |
Matching Algorithm
Incoming BUY order at price P
↓
Find lowest ASK price in Red-Black Tree
↓
If lowestAsk <= P (price overlap):
Take first order in FIFO queue at that price level
Calculate fill: min(buyAmount, sellAmount)
Execute trade
Remove from queue if fully filled
Repeat until order filled or no more matching asks
↓
If partially filled AND TIF == GTC:
Insert remainder into bid tree at price P
Else if IOC: cancel remainder
Else if FOK: revert entire tx (FOKNotFilled)Core Functions
`matchOrder(bytes32 orderId)`
Attempts to match a newly placed order against the existing order book.
Called by: OrderRouterFacet immediately after order creation.
Modifiers: whenNotPaused
Process:
- Loads
PackedOrderfrom storage - Determines direction (bid/ask)
- Traverses Red-Black Tree to find matching price levels
- Executes fills via
_executeFill - Updates order book depth
- Emits
MarketDepthChanged
`executeMatch(bytes32 takerOrderId, bytes32 makerOrderId)`
Admin/settlement function to force-match two specific orders.
Use case: Manual matching for MEV-protected commit-reveal orders after reveal.
Trade Execution
For each fill:
Calculate amounts:
fillAmount = min(takerRemaining, makerRemaining) quoteAmount = fillAmount × fillPrice takerFee = quoteAmount × takerFeeBps / 10000 makerFee = quoteAmount × makerFeeBps / 10000Transfer tokens:
- Buy taker: receives
fillAmountERC-1155 tokens from Diamond - Sell taker: receives
quoteAmount - takerFeeERC-20 from Diamond - Maker (sell): receives
quoteAmount - makerFeeERC-20 - Maker (buy): receives
fillAmountERC-1155
- Buy taker: receives
Fee distribution:
- Fees accumulated in
collectedFees[quoteToken] - Claimable by owner via
CLOBAdminFacet.withdrawFees
- Fees accumulated in
Update order records:
packedOrders[orderId].filledAmount += fillAmount- Mark FILLED if
filledAmount == amount
Events
| Event | Parameters |
|---|---|
MatchingOrderFilled |
orderId, tradeId, fillAmount, fillPrice, remainingAmount, cumulativeFilled |
TradeExecuted |
tradeId, takerOrderId, makerOrderId, taker, maker, marketId, price, amount, quoteAmount, takerFee, makerFee, timestamp, takerIsBuy |
MarketDepthChanged |
marketId, bestBid, bestBidSize, bestAsk, bestAskSize, spread |
CLOBOrderCancelled |
orderId, maker, remainingAmount, reason |
OrderExpired |
orderId, expiredAt |
Errors
| Error | Condition |
|---|---|
OrderNotFound() |
Unknown orderId |
MarketPaused() |
Circuit breaker active |
CircuitBreakerTrippedError() |
Price moved beyond threshold |
FOKNotFilled() |
Fill-or-Kill order couldn't be fully filled |
Red-Black Tree Price Levels
Price levels use a Red-Black Tree (self-balancing BST) for O(log n) best-price lookup:
Bid Tree (max-heap behaviour — highest bid at root):
100
/ \
95 98
/ \
90 93
Ask Tree (min-heap behaviour — lowest ask at root):
105
/ \
110 108
/ \
115 112Each price level contains a FIFO queue of orders at that price. Time priority is maintained by the queue — earlier orders fill first.
RBTreeMeta fields: root, count, totalVolume
RBNode fields: key (price), parent, left, right, isRed, orderId (head of queue)
PriceLevel fields: head, tail, totalVolume, orderCount
Circuit Breaker Integration
Before executing any fill, the matching facet checks the circuit breaker:
newPrice = fillPrice
threshold = market.defaultPriceChangeThreshold
if |newPrice - lastPrice| / lastPrice > threshold:
tripCircuitBreaker(marketId)
revert CircuitBreakerTrippedError()Tripped markets are paused for defaultCooldownPeriod blocks.