SDK
Here we outline the order flow process with simplified code snippets, illustrating the core logic behind the implementation of Fermi's intent-based DLOB.
Order flow Logic
1. Order Placement
When placing an order, 1% of the order value is deposited as security to ensure credibility. The remaining amount of tokens needed for the trade are delegated from the user to the Market Authority, to be conditionally transferred later if the order is filled. A counter is maintained to account for cumulative approvals of a token for a user across all of their open orders on a market.
In case of a limit order, the user's position (tokens deposited & tokens approved) is recorded in their user-specific openOrders struct. In case of a taker order the order is immediately filled and added to eventQ, or cancelled.
2. Order Matching Engine
Any limit orders placed are matched against the opposing bookSide to see if they can be partially or fully filled:
The following depicts key parts of the matching code (book.new_order):
If an order is partially or fully filled, a fillEvent is generated and added to the EventQueue. This fill event contains all the information needed to "Finalize" the order, and execute just-in-time transfers from both counterparties.
3. Order Finalization
Can be called by either counterparty/ a third party "cranker". Unlike Openbooks' consume_events function, this is an atomic function, so a trade can be finalised sequence independently, even if there are other matched orders before/after this order on the eventQueue that have not yet been finalized.
A. atomic_finalize_events
Accounting
After executing just-in-time transfers from both parties, their openorders balances are updated to reflect the completed trade:
B. atomicFinalizeEventsDirect
This function is used to conduct JIT transfers directly into each counterparties' wallet, without using the intermediate openorders for accounting. The purpose of this is convinience for taker orders, and easier integration with aggregators.
4. Handling Settlement Failure: Cancel With Penalty
let oo_account = &mut open_orders_account;
let position = oo_account.position;
let deposit_amount = match order.side {
Side::Bid => {
let free_quote = position.quote_free_native;
let max_quote_including_fees =
total_quote_taken_native + posted_quote_native + taker_fees + maker_fees;
let free_qty_to_lock = cmp::min(max_quote_including_fees, free_quote);
// new total approved
oo_account.total_approved_quote += max_quote_including_fees;
let total_quote_approved = &oo_account.total_approved_quote;
let deposit_amount = *total_quote_approved;
market.quote_deposit_total += max_quote_including_fees;
deposit_amount
}
let oo_account = &mut open_orders_account;
let position = oo_account.position;
let deposit_amount = match order.side {
...
Side::Ask => {
let free_base = position.base_free_native;
let max_base_native = total_base_taken_native + posted_base_native;
// add additional amt to oo.total approved base
oo_account.total_approved_base += max_base_native;
let total_base_approved = &oo_account.total_approved_base;
//let deposit_amount = max_base_native - free_qty_to_lock;
let deposit_amount = *total_base_approved;
market.base_deposit_total += max_base_native;
deposit_amount
}
};
...
// Calculations ommitted for brevity
let from_account_base = match side {
Side::Ask => maker_ata,
Side::Bid => taker_ata,
};
let to_account_base = market_base_vault;
let from_account_quote = match side {
Side::Ask => taker_ata,
Side::Bid => maker_ata,
};
let to_account_quote = market_quote_vault;
if base_amount_transfer > 0 {
msg!("{} tokens of base mint {} transferring from user's account {} to market's vault {}", base_amount_transfer, from_account_base.mint, from_account_base.key(), market_base_vault.key());
// transfer base token
token_transfer_signed(
base_amount_transfer,
&ctx.accounts.token_program,
from_account_base,
to_account_base,
&ctx.accounts.market_authority,
seeds,
)?;
// Bid recieves base, ASKER recieves quote
// credit base to counterparty
} else {
msg!("base transfer amount is 0");
}
//transfer quote token
if quote_amount_transfer > 0 {
token_transfer_signed(
quote_amount_transfer,
&ctx.accounts.token_program,
from_account_quote,
to_account_quote,
&ctx.accounts.market_authority,
seeds,
)?;
// Bid recieves base, ASKER recieves quote
// credit quote to counterparty
} else {
msg!("quote transfer amount is 0");
}
... // accounting
// CREDIT the maker and taker with the filled amount
if side == Side::Bid {
taker.position.quote_free_native += quote_amount;
maker.position.base_free_native += base_amount;
} else {
maker.position.quote_free_native += quote_amount;
taker.position.base_free_native += base_amount;
}
let (from_account_base, to_account_base) = match side {
Side::Ask => (maker_base_account, taker_base_account),
Side::Bid => (taker_base_account, maker_base_account),
};
//let to_account_base = market_base_vault;
// if maker is ASK, maker sends base, gets quote. If maker is BID, maker sends quote, gets base
let (from_account_quote, to_account_quote) = match side {
Side::Ask => (taker_quote_account, maker_quote_account),
Side::Bid => (maker_quote_account, taker_quote_account),
};
let seeds = market_seeds!(market, ctx.accounts.market.key());
msg!(
"transferrring {} tokens from user's ata {} to market's vault {}",
base_amount_transfer,
from_account_base.to_account_info().key(),
market_base_vault.to_account_info().key()
);
// Perform the transfer if the amount is greater than zero
if base_amount_transfer > 0 {
// transfer base token
token_transfer_signed(
base_amount_transfer,
&ctx.accounts.token_program,
from_account_base.as_ref(),
to_account_base.as_ref(),
&ctx.accounts.market_authority,
seeds,
)?;
// Bid recieves base, ASKER recieves quote
// credit base to counterparty
} else {
msg!("base transfer amount is 0");
}
//transfer quote token
if quote_amount_transfer > 0 {
token_transfer_signed(
quote_amount_transfer,
&ctx.accounts.token_program,
from_account_quote.as_ref(),
to_account_quote.as_ref(),
&ctx.accounts.market_authority,
seeds,
)?;
// Bid recieves base, ASKER recieves quote
// credit quote to counterparty
} else {
msg!("quote transfer amount is 0");
}