[DEPRECATED - ONLY FOR REFERENCE] Capturing the core logic behind order placement, matching, finalisation, and settlement in Fermi DEX v0.2
1. Order placement
Adding to Bids/Asks PDA
A new order is placed on the orderbook, by adding it to the bids or asks queue, for later matching. Note, the Order_ID contains complete information on the price-time priority of that trade.
When new orders are placed, the program checks if there is enough tokens in the user's openorders to perform the trade. If not, the delta tokens needed are "approved" from the user, for later just-in-time settlement:
```rust```rustSide::Bid=> {let lock_qty_native = max_native_pc_qty; native_pc_qty_locked =Some(lock_qty_native);let free_qty_to_lock = lock_qty_native.min(open_orders.native_pc_free);let total_deposit_amount = lock_qty_native - free_qty_to_lock;//deposit_amount = total_deposit_amount * 2/100; //marginal deposit up front deposit_amount = total_deposit_amount; //for test with matching, L1044 deposit_vault = pc_vault;```rustSide::Ask=> { native_pc_qty_locked =None;let lock_qty_native = max_coin_qty.checked_mul(market.coin_lot_size).ok_or(error!(ErrorCode::InsufficientFunds))?;let free_qty_to_lock = lock_qty_native.min(open_orders.native_coin_free);let total_deposit_amount = lock_qty_native - free_qty_to_lock;//deposit_amount = total_deposit_amount * 2/100; //marginal deposit up front deposit_amount = total_deposit_amount; //for test with matching, L1044 deposit_vault = coin_vault;```
if deposit_amount >0 {
The calculation of the required amount, and the successul call to the native approve function, is visible in the logs:
2. Matching
Any new order is tested against the counterpart of the orderbook (i.e. bids are matched against asks, and vice versa. The price is derived from the orderId using the impl .price().For example, for a bid:
Note that the bids and asks are updated in case of a fill. If no quantity remains, that order is removed from the BidsPDA or AsksPDA. If an order is partially filled, the Bid or Ask is modified in place to reflect the remaining quantity still to be traded.
3. Finalisation
In the finalisation stage (finalise_bid and finalise_ask), the program checks whether the user has sufficient tokens in their openorders account to complete the trade. If not, it attempts to transfer those tokens from the user/counterparty, to provide just in time liquidity. This is in addition to general validation of the events passed.
```rustletmut qty_pc = parsed_event.native_qty_paid;letmut qty_coin = parsed_event.native_qty_released;letmut available_funds = open_orders_auth.native_pc_free;msg!("the available funds is {}", available_funds);msg!("the required funds are {}", qty_pc);//let mut deposit_amount = qty_pc / 1000;letmut deposit_amount = qty_pc / market.pc_lot_size ;msg!("Deposit amt {}", deposit_amount);letmut cpty_deposit_amt = qty_coin;letmut deposit_vault = pc_vault;if deposit_amount >0 {// Derive the market's PDA and bump seed.let (market_pda, bump_seed) =Pubkey::find_program_address(&[b"market", coin_mint.key().as_ref(), pc_mint.key().as_ref()],&program_id );let market_seed =b"market";let coin_mint_key = coin_mint.key();let pc_mint_key = pc_mint.key();let coin_mint_seed = coin_mint_key.as_ref();let pc_mint_seed = pc_mint_key.as_ref();let bump_seed_arr:&[u8] =&[bump_seed];let seed_slices: [&[u8]; 4] = [market_seed, coin_mint_seed, pc_mint_seed, bump_seed_arr];let seeds:&[&[&[u8]]] =&[&seed_slices];let transfer_ix =Transfer { from: payerpc.to_account_info(), to: deposit_vault.to_account_info(), authority: market.to_account_info(), // Using the market PDA as the authority. };// Construct the context with the market PDA and bump seed.let cpi_ctx =CpiContext::new_with_signer( token_program.to_account_info(), transfer_ix, seeds,//&[&[b"market", coin_mint.key().as_ref(), pc_mint.key().as_ref(), &[seeds]]] ); anchor_spl::token::transfer(cpi_ctx, deposit_amount).map_err(|err|match err { _ =>error!(ErrorCode::TransferFailed), })?;```// Some code
```rust
let mut qty_coin = parsed_event.native_qty_paid;
let mut available_funds = open_orders_auth.native_coin_free;
msg!("the available funds is {}", available_funds);
msg!("the required funds are {}", qty_coin);
//let mut deposit_amount = qty_pc / 1000;
let mut deposit_amount = qty_coin; //decimals already multiplied
msg!("Deposit amt {}", deposit_amount);
let mut cpty_deposit_amt = qty_coin;
let mut deposit_vault = coin_vault;
if deposit_amount > 0 {
// Derive the market's PDA and bump seed.
let (market_pda, bump_seed) = Pubkey::find_program_address(
&[b"market", coin_mint.key().as_ref(), pc_mint.key().as_ref()],
&program_id
);
let market_seed = b"market";
let coin_mint_key = coin_mint.key();
let pc_mint_key = pc_mint.key();
let coin_mint_seed = coin_mint_key.as_ref();
let pc_mint_seed = pc_mint_key.as_ref();
let bump_seed_arr: &[u8] = &[bump_seed];
let seed_slices: [&[u8]; 4] = [market_seed, coin_mint_seed, pc_mint_seed, bump_seed_arr];
let seeds: &[&[&[u8]]] = &[&seed_slices];
// let seeds = &[&[b"market", coin_mint.key().as_ref(), pc_mint.key().as_ref(), &[bump_seed]]];
let transfer_ix = Transfer {
from: payercoin.to_account_info(),
to: deposit_vault.to_account_info(),
authority: market.to_account_info(), // Using the market PDA as the authority.
};
// Construct the context with the market PDA and bump seed.
let cpi_ctx = CpiContext::new_with_signer(
token_program.to_account_info(),
transfer_ix,
seeds,
//&[&[b"market", coin_mint.key().as_ref(), pc_mint.key().as_ref(), &[seeds]]]
);
anchor_spl::token::transfer(cpi_ctx, deposit_amount).map_err(|err| match err {
_ => error!(ErrorCode::TransferFailed),
})?;
```
Finalize Events
Once liquidity has been fetched for one side of the trade, an EventView::Finalize event is created in place of the original EventView:Fill. This marks the order as "finalized" - therby preventing any double spending, and marking the order eligible for settlement.
The transfer of tokens from both counterparties is visible in the on chain logs:
4. Settlement
Once liquidity has been fetched from both counterparties for both sides of the order, the order can be settled by the program. This is done by debiting the openOrders balances of one Counterparty, and crediting the same amount to the other counterparty. After settlement, the updated balances of the users are free to be withdrawn, or used for further trades.
if eventBidFinalised ==true&& eventAskFinalised ==true {//checked subtract pc from event1 owner open_orders_auth.native_pc_free -= event1.native_qty_paid;//subtract coin from event2 owner open_orders_cpty.native_coin_free -= event2.native_qty_paid;//add pc to event2 owner open_orders_cpty.native_pc_free += event2.native_qty_released;//add coin to event1 owner open_orders_auth.native_coin_free += event1.native_qty_released;