Overview
The DLOB provides sophisticated order matching capabilities that handle:
- Auction orders crossing resting limit orders
- Limit order crosses at the top of the book
- vAMM crosses with the protocol’s virtual AMM
- Trigger order matching with post-trigger price calculations
- Partial fills with remaining order tracking
Core Matching Types
TakerOrder
Minimal taker order information for matching:
pub struct TakerOrder {
pub price: u64,
pub size: u64,
pub market_index: u16,
pub direction: Direction,
pub market_type: MarketType,
}
Construction from OrderParams:
let taker = TakerOrder::from_order_params(order_params, price);
Fields:
price - Limit price of the taker order (in market quote precision)
size - Base asset amount to fill (in market base precision)
market_index - Index of the market (e.g., 0 for SOL-PERP)
direction - Long (buy) or Short (sell)
market_type - Perp or Spot
MakerCrosses
Result of matching a taker order against resting maker orders:
pub struct MakerCrosses {
pub orders: ArrayVec<(L3Order, u64), 16>, // (maker order, fill_size)
pub slot: u64, // Slot when crosses were found
pub has_vamm_cross: bool, // True if order crosses vAMM
pub is_partial: bool, // True if taker not fully filled
pub taker_direction: Direction, // Direction of taker order
}
Methods:
Returns true if no crosses were found (neither maker orders nor vAMM)if maker_crosses.is_empty() {
println!("No fills available");
}
Fields:
orders - Up to 16 matched maker orders with their fill sizes
has_vamm_cross - Indicates if the order can fill against vAMM
is_partial - True if taker order cannot be fully filled
taker_direction - Direction to determine bid/ask side
CrossesAndTopMakers
Comprehensive result of finding all auction crosses at current slot:
pub struct CrossesAndTopMakers {
pub top_maker_asks: ArrayVec<Pubkey, 3>, // Best 3 ask makers
pub top_maker_bids: ArrayVec<Pubkey, 3>, // Best 3 bid makers
pub limit_crosses: Option<(L3Order, L3Order)>, // Limit cross if any
pub vamm_taker_ask: Option<L3Order>, // Maker ask crossing vAMM
pub vamm_taker_bid: Option<L3Order>, // Maker bid crossing vAMM
pub crosses: Vec<(L3Order, MakerCrosses)>, // Taker auctions and makers
}
Use Cases:
- Finding all fillable auction orders at once
- Identifying top maker accounts for rewards
- Detecting limit crosses for atomic matching
- Finding vAMM arbitrage opportunities
CrossingRegion
Region where the orderbook is crossed (best bid >= best ask):
pub struct CrossingRegion {
pub slot: u64,
pub crossing_bids: Vec<L3Order>,
pub crossing_asks: Vec<L3Order>,
}
A crossing region indicates market inefficiency or pending atomic matches.
Matching Methods
find_crosses_for_taker_order
Find all maker orders that would match against a given taker order:
pub fn find_crosses_for_taker_order(
&self,
current_slot: u64,
oracle_price: u64,
taker_order: TakerOrder,
perp_market: Option<&PerpMarket>,
depth: Option<usize>,
) -> MakerCrosses
Current blockchain slot for time-sensitive order logic (auctions, expiry)
Current oracle price for:
- Floating order price calculations
- Trigger condition evaluation
- vAMM fallback price
The taker order to match against the book
PerpMarket struct for:
- vAMM price calculations
- Fallback price for auctions
- Trigger price determination
Use None for spot markets or when only interested in maker orders
Maximum order depth to consider (default: 32)Higher values find more matches but increase computation
Returns: MakerCrosses containing matched orders
Example:
let taker = TakerOrder {
price: 50_000_000_000, // $50k limit
size: 1_000_000_000, // 1.0 SOL
market_index: 0,
direction: Direction::Long,
market_type: MarketType::Perp,
};
let crosses = dlob.find_crosses_for_taker_order(
current_slot,
oracle_price,
taker,
Some(&perp_market),
Some(50), // Check top 50 orders
);
if crosses.is_empty() {
println!("No fills available");
} else if crosses.is_partial {
println!("Partial fill: {} makers, vAMM: {}",
crosses.orders.len(),
crosses.has_vamm_cross
);
} else {
println!("Full fill possible with {} makers", crosses.orders.len());
}
for (maker, fill_size) in crosses.orders {
println!("Fill {} @ {} from user {}",
fill_size, maker.price, maker.user
);
}
find_crosses_for_auctions
Find all auction orders crossing resting limit orders at current slot:
pub fn find_crosses_for_auctions(
&self,
market_index: u16,
market_type: MarketType,
slot: u64,
oracle_price: u64,
perp_market: Option<&PerpMarket>,
trigger_price: u64,
depth: Option<usize>,
) -> CrossesAndTopMakers
Market index to check for crosses
Current slot for auction price calculations
Oracle price for floating order calculations
Market data for vAMM calculations (required for perp markets)
Price for evaluating trigger conditions and post-trigger prices
Max order depth (default: 64)
Returns: CrossesAndTopMakers with all crosses and top maker info
Example:
let result = dlob.find_crosses_for_auctions(
0, // SOL-PERP
MarketType::Perp,
current_slot,
oracle_price,
Some(&perp_market),
oracle_price, // Use oracle as trigger price
Some(100),
);
// Check for limit crosses
if let Some((taker, maker)) = result.limit_crosses {
println!("Limit cross: {} crossing {}", taker.user, maker.user);
}
// Check for vAMM crosses
if let Some(ask) = result.vamm_taker_ask {
println!("Bid {} can fill against vAMM", ask.user);
}
// Process all auction crosses
for (taker, makers) in result.crosses {
println!("Taker auction {} @ {} crossing {} makers",
taker.order_id,
taker.price,
makers.orders.len()
);
}
// Top makers for rewards
println!("Top bid makers: {:?}", result.top_maker_bids);
println!("Top ask makers: {:?}", result.top_maker_asks);
find_crossing_region
Find the region where orderbook is crossed:
pub fn find_crossing_region(
&self,
oracle_price: u64,
market_index: u16,
market_type: MarketType,
perp_market: Option<&PerpMarket>,
) -> Option<CrossingRegion>
Returns: Some(CrossingRegion) if best bid >= best ask, None otherwise
Example:
if let Some(region) = dlob.find_crossing_region(
oracle_price,
0,
MarketType::Perp,
Some(&perp_market)
) {
println!("Book is crossed!");
println!("Crossing bids: {}", region.crossing_bids.len());
println!("Crossing asks: {}", region.crossing_asks.len());
// Atomic match opportunity
for bid in region.crossing_bids {
println!("Bid {} @ {}", bid.size, bid.price);
}
}
Matching Algorithm
Order Priority
Orders are matched in price-time priority:
- Best price first - Highest bids, lowest asks
- Time priority - Earlier orders (lower
max_ts) at same price
- Order ID - Lower order IDs for deterministic ordering
Price Crossing Logic
For long takers (buyers):
taker_price > maker_ask_price // Can cross
For short takers (sellers):
taker_price < maker_bid_price // Can cross
Partial Fill Handling
The algorithm tracks remaining size:
let mut remaining_size = taker_size;
while let Some(maker) = makers.next() {
let fill_size = remaining_size.min(maker.size);
matches.push((maker, fill_size));
remaining_size -= fill_size;
if remaining_size == 0 {
break; // Fully filled
}
}
let is_partial = remaining_size > 0;
Post-Only Order Matching
Post-only orders follow special crossing rules:
match (bid.is_post_only(), ask.is_post_only()) {
(true, false) => Some((ask, bid)), // Ask is taker
(false, true) => Some((bid, ask)), // Bid is taker
(false, false) => { // Both can be taker
if bid.max_ts < ask.max_ts { // Older order is maker
Some((bid, ask))
} else {
Some((ask, bid))
}
}
(true, true) => None, // Both post-only, no cross
}
vAMM Cross Detection
For long takers:
has_vamm_cross = taker_size > vamm_min_order_size
&& taker_price > vamm_ask_price
For short takers:
has_vamm_cross = taker_size > vamm_min_order_size
&& taker_price < vamm_bid_price
Advanced Matching Scenarios
Auction Order Matching
Auction orders have dynamic prices that change with each slot:
// Market auction at slot N
let auction_price = order.get_price(current_slot, oracle_price, tick_size);
// Auction completes after duration
let is_complete = Order::is_auction_complete(
current_slot,
order.slot,
order.auction_duration
);
if is_complete {
// Convert to resting limit order
let limit_order = auction.to_limit_order();
}
Trigger Order Matching
Trigger orders require evaluation at trigger price:
// Check if order would trigger
let would_trigger = match order.trigger_condition {
OrderTriggerCondition::Above => trigger_price > order.trigger_price,
OrderTriggerCondition::Below => trigger_price < order.trigger_price,
_ => false,
};
if would_trigger {
// Calculate post-trigger price
let post_price = order.post_trigger_price(
current_slot,
oracle_price,
&perp_market
);
// Match at post-trigger price
find_crosses_for_price(post_price, ...);
}
Floating Order Matching
Floating orders have prices relative to oracle:
let floating_price = (oracle_price as i64 + order.offset_price as i64) as u64;
let standardized = standardize_price(floating_price, tick_size, direction);
Match Limits
- Maximum 16 maker orders per taker (ArrayVec capacity)
- Configurable depth parameter (default 32-64)
- Early termination when taker fully filled
Time Complexity
- Best case: O(1) - No crosses found immediately
- Average case: O(k) - Where k is number of crossing orders
- Worst case: O(depth) - Full depth search
Memory Usage
- Stack-allocated for up to 16 matches (no heap allocation)
- Efficient iterator-based approach
- Peekable iterators avoid unnecessary advances
Common Patterns
Check Fillability
let crosses = dlob.find_crosses_for_taker_order(...);
if crosses.is_empty() {
return Err("No liquidity available");
}
if crosses.is_partial {
println!("Warning: Only partial fill available");
}
Calculate Expected Fill Price
let mut total_quote = 0u64;
let mut total_base = 0u64;
for (maker, fill_size) in crosses.orders {
total_quote += maker.price * fill_size;
total_base += fill_size;
}
let avg_price = total_quote / total_base;
println!("Average fill price: {}", avg_price);
Find Best Maker
let result = dlob.find_crosses_for_auctions(...);
if let Some(best_maker) = result.top_maker_bids.first() {
println!("Best bid maker: {}", best_maker);
}
Multi-Market Matching
for market_id in markets {
let crosses = dlob.find_crosses_for_auctions(
market_id.index(),
market_id.kind(),
slot,
oracle_prices[&market_id],
perp_markets.get(&market_id),
trigger_price,
None,
);
process_crosses(market_id, crosses);
}
Testing Example
#[test]
fn test_taker_maker_matching() {
let dlob = create_test_dlob();
// Add maker orders
dlob.insert_order(&maker1, slot, limit_ask_49_500);
dlob.insert_order(&maker2, slot, limit_ask_50_000);
// Create taker
let taker = TakerOrder {
price: 50_000,
size: 2_000,
direction: Direction::Long,
market_index: 0,
market_type: MarketType::Perp,
};
// Find crosses
let crosses = dlob.find_crosses_for_taker_order(
slot,
oracle_price,
taker,
None,
None,
);
assert_eq!(crosses.orders.len(), 2);
assert!(!crosses.is_partial);
assert_eq!(crosses.orders[0].0.price, 49_500); // Best price first
}
See Also
- DLOB Overview - Core DLOB concepts
- DLOB Builder - Builder pattern for DLOB
L3Order - Order-level detail (in types.rs:672-760)
OrderKind - Order type classification (in types.rs:26-52)