diff --git a/programs/mango-v4/src/instructions/create_perp_market.rs b/programs/mango-v4/src/instructions/create_perp_market.rs index 973029933..958021e0a 100644 --- a/programs/mango-v4/src/instructions/create_perp_market.rs +++ b/programs/mango-v4/src/instructions/create_perp_market.rs @@ -16,7 +16,7 @@ pub struct CreatePerpMarket<'info> { #[account( init, - seeds = [group.key().as_ref(), b"PerpMarket".as_ref(), &perp_market_index.to_le_bytes().as_ref()], + seeds = [group.key().as_ref(), b"PerpMarket".as_ref(), perp_market_index.to_le_bytes().as_ref()], bump, payer = payer, space = 8 + std::mem::size_of::(), @@ -50,8 +50,8 @@ pub fn create_perp_market( oracle: ctx.accounts.oracle.key(), bids: ctx.accounts.bids.key(), asks: ctx.accounts.asks.key(), - quote_lot_size: quote_lot_size, - base_lot_size: base_lot_size, + quote_lot_size, + base_lot_size, seq_num: 0, perp_market_index, base_token_index: base_token_index_opt.ok_or(TokenIndex::MAX).unwrap(), diff --git a/programs/mango-v4/src/instructions/margin_trade.rs b/programs/mango-v4/src/instructions/margin_trade.rs index 03ac2e5b9..e4fb603cd 100644 --- a/programs/mango-v4/src/instructions/margin_trade.rs +++ b/programs/mango-v4/src/instructions/margin_trade.rs @@ -141,9 +141,9 @@ fn get_pre_cpi_amounts(ctx: &Context, cpi_ais: &Vec) - fn adjust_for_post_cpi_amounts( ctx: &Context, - cpi_ais: &Vec, + cpi_ais: &[AccountInfo], pre_cpi_amounts: Vec, - banks: &mut Vec, + banks: &mut [AccountInfo], account: &mut MangoAccount, ) -> Result<()> { let token_accounts_iter = cpi_ais diff --git a/programs/mango-v4/src/instructions/place_perp_order.rs b/programs/mango-v4/src/instructions/place_perp_order.rs index 33d8c5b23..38f644815 100644 --- a/programs/mango-v4/src/instructions/place_perp_order.rs +++ b/programs/mango-v4/src/instructions/place_perp_order.rs @@ -1,8 +1,6 @@ use anchor_lang::prelude::*; -use crate::state::{ - oracle_price, Book, BookSide, Group, MangoAccount, OrderType, PerpMarket, Side, -}; +use crate::state::{Book, BookSide, Group, MangoAccount, OrderType, PerpMarket}; #[derive(Accounts)] pub struct PlacePerpOrder<'info> { @@ -32,22 +30,19 @@ pub struct PlacePerpOrder<'info> { pub owner: Signer<'info>, } +// TODO +#[allow(clippy::too_many_arguments)] pub fn place_perp_order( ctx: Context, - // TODO side is harcoded for now - // maybe new_bid and new_ask can be folded into one function - // side: Side, price: i64, max_base_quantity: i64, max_quote_quantity: i64, client_order_id: u64, order_type: OrderType, - // TODO reduce_only relies on event queue - // reduce_only: bool, expiry_timestamp: u64, limit: u8, ) -> Result<()> { - let mut account = ctx.accounts.account.load_mut()?; + // let mut account = ctx.accounts.account.load_mut()?; let mango_account_pk = ctx.accounts.account.key(); let mut perp_market = ctx.accounts.perp_market.load_mut()?; @@ -55,7 +50,7 @@ pub fn place_perp_order( let asks = &ctx.accounts.asks.to_account_info(); let mut book = Book::load_checked(&bids, &asks, &perp_market)?; - let oracle_price = oracle_price(&ctx.accounts.oracle.to_account_info())?; + // let oracle_price = oracle_price(&ctx.accounts.oracle.to_account_info())?; let now_ts = Clock::get()?.unix_timestamp as u64; let time_in_force = if expiry_timestamp != 0 { @@ -75,14 +70,9 @@ pub fn place_perp_order( // TODO reduce_only based on event queue book.new_bid( - // program_id: &Pubkey, - // mango_group: &MangoGroup, - // mango_group_pk: &Pubkey, - // mango_cache: &MangoCache, - // event_queue: &mut EventQueue, &mut perp_market, - oracle_price, - &mut account, + // oracle_price, + // &mut account, &mango_account_pk, // market_index: usize, price, @@ -92,7 +82,6 @@ pub fn place_perp_order( time_in_force, client_order_id, now_ts, - // referrer_mango_account_ai: Option<&AccountInfo>, limit, )?; diff --git a/programs/mango-v4/src/lib.rs b/programs/mango-v4/src/lib.rs index db8558fbd..3836efc69 100644 --- a/programs/mango-v4/src/lib.rs +++ b/programs/mango-v4/src/lib.rs @@ -15,7 +15,7 @@ pub mod instructions; mod serum3_cpi; pub mod state; -use state::{OrderType, PerpMarketIndex, Serum3MarketIndex, Side, TokenIndex}; +use state::{OrderType, PerpMarketIndex, Serum3MarketIndex, TokenIndex}; declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"); @@ -145,25 +145,21 @@ pub mod mango_v4 { pub fn place_perp_order( ctx: Context, - side: Side, price: i64, max_base_quantity: i64, max_quote_quantity: i64, client_order_id: u64, order_type: OrderType, - reduce_only: bool, expiry_timestamp: u64, limit: u8, ) -> Result<()> { instructions::place_perp_order( ctx, - // side, price, max_base_quantity, max_quote_quantity, client_order_id, order_type, - // reduce_only, expiry_timestamp, limit, ) diff --git a/programs/mango-v4/src/state/mango_account.rs b/programs/mango-v4/src/state/mango_account.rs index 03d160ebb..120d900c7 100644 --- a/programs/mango-v4/src/state/mango_account.rs +++ b/programs/mango-v4/src/state/mango_account.rs @@ -14,7 +14,7 @@ use crate::state::*; // MangoAccount size and health compute needs. const MAX_INDEXED_POSITIONS: usize = 16; const MAX_SERUM_OPEN_ORDERS: usize = 8; -const MAX_PERP_OPEN_ORDERS: usize = 8; +// const MAX_PERP_OPEN_ORDERS: usize = 8; #[zero_copy] pub struct TokenAccount { @@ -95,7 +95,7 @@ impl TokenAccountMap { if let Some(i) = pos { self.values[i] = TokenAccount { indexed_value: I80F48::ZERO, - token_index: token_index, + token_index, in_use_count: 0, }; } diff --git a/programs/mango-v4/src/state/orderbook/book.rs b/programs/mango-v4/src/state/orderbook/book.rs index ec622493e..3de1c3a0b 100644 --- a/programs/mango-v4/src/state/orderbook/book.rs +++ b/programs/mango-v4/src/state/orderbook/book.rs @@ -4,19 +4,18 @@ use crate::{ error::MangoError, state::{ orderbook::{bookside::BookSide, nodes::LeafNode}, - MangoAccount, PerpMarket, + PerpMarket, }, }; use anchor_lang::prelude::*; -use bytemuck::cast; use fixed::types::I80F48; use fixed_macro::types::I80F48; use super::{ nodes::NodeHandle, order_type::{OrderType, Side}, - queue::{EventQueue, FillEvent, OutEvent}, }; +use crate::util::checked_math as cm; pub const CENTIBPS_PER_UNIT: I80F48 = I80F48!(1_000_000); // todo move to a constants module or something @@ -191,146 +190,146 @@ impl<'a> Book<'a> { /// Iterate over the book and return /// return changes to (taker_base, taker_quote, bids_quantity, asks_quantity) - pub fn sim_new_bid( - &self, - market: &PerpMarket, - // info: &PerpMarketInfo, - oracle_price: I80F48, - price: i64, - max_base_quantity: i64, // guaranteed to be greater than zero due to initial check - max_quote_quantity: i64, // guaranteed to be greater than zero due to initial check - order_type: OrderType, - now_ts: u64, - ) -> std::result::Result<(i64, i64, i64, i64), Error> { - let (mut taker_base, mut taker_quote, mut bids_quantity, asks_quantity) = (0, 0, 0i64, 0); + // pub fn sim_new_bid( + // &self, + // market: &PerpMarket, + // // info: &PerpMarketInfo, + // oracle_price: I80F48, + // price: i64, + // max_base_quantity: i64, // guaranteed to be greater than zero due to initial check + // max_quote_quantity: i64, // guaranteed to be greater than zero due to initial check + // order_type: OrderType, + // now_ts: u64, + // ) -> std::result::Result<(i64, i64, i64, i64), Error> { + // let (mut taker_base, mut taker_quote, mut bids_quantity, asks_quantity) = (0, 0, 0i64, 0); - let (post_only, mut post_allowed, price) = match order_type { - OrderType::Limit => (false, true, price), - OrderType::ImmediateOrCancel => (false, false, price), - OrderType::PostOnly => (true, true, price), - OrderType::Market => (false, false, i64::MAX), - OrderType::PostOnlySlide => { - let price = if let Some(best_ask_price) = self.get_best_ask_price(now_ts) { - price.min(best_ask_price.checked_sub(1).ok_or(MangoError::SomeError)?) - // math_err - } else { - price - }; - (true, true, price) - } - }; - // if post_allowed { - // // price limit check computed lazily to save CU on average - // let native_price = market.lot_to_native_price(price); - // if native_price.checked_div(oracle_price).unwrap() > info.maint_liab_weight { - // msg!("Posting on book disallowed due to price limits"); - // post_allowed = false; - // } - // } + // let (post_only, mut post_allowed, price) = match order_type { + // OrderType::Limit => (false, true, price), + // OrderType::ImmediateOrCancel => (false, false, price), + // OrderType::PostOnly => (true, true, price), + // OrderType::Market => (false, false, i64::MAX), + // OrderType::PostOnlySlide => { + // let price = if let Some(best_ask_price) = self.get_best_ask_price(now_ts) { + // price.min(best_ask_price.checked_sub(1).ok_or(MangoError::SomeError)?) + // // math_err + // } else { + // price + // }; + // (true, true, price) + // } + // }; + // // if post_allowed { + // // // price limit check computed lazily to save CU on average + // // let native_price = market.lot_to_native_price(price); + // // if native_price.checked_div(oracle_price).unwrap() > info.maint_liab_weight { + // // msg!("Posting on book disallowed due to price limits"); + // // post_allowed = false; + // // } + // // } - let mut rem_base_quantity = max_base_quantity; // base lots (aka contracts) - let mut rem_quote_quantity = max_quote_quantity; + // let mut rem_base_quantity = max_base_quantity; // base lots (aka contracts) + // let mut rem_quote_quantity = max_quote_quantity; - for (_, best_ask) in self.asks.iter_valid(now_ts) { - let best_ask_price = best_ask.price(); - if price < best_ask_price { - break; - } else if post_only { - return Ok((taker_base, taker_quote, bids_quantity, asks_quantity)); - } + // for (_, best_ask) in self.asks.iter_valid(now_ts) { + // let best_ask_price = best_ask.price(); + // if price < best_ask_price { + // break; + // } else if post_only { + // return Ok((taker_base, taker_quote, bids_quantity, asks_quantity)); + // } - let max_match_by_quote = rem_quote_quantity / best_ask_price; - let match_quantity = rem_base_quantity - .min(best_ask.quantity) - .min(max_match_by_quote); + // let max_match_by_quote = rem_quote_quantity / best_ask_price; + // let match_quantity = rem_base_quantity + // .min(best_ask.quantity) + // .min(max_match_by_quote); - let match_quote = match_quantity * best_ask_price; - rem_base_quantity -= match_quantity; - rem_quote_quantity -= match_quote; + // let match_quote = match_quantity * best_ask_price; + // rem_base_quantity -= match_quantity; + // rem_quote_quantity -= match_quote; - taker_base += match_quantity; - taker_quote -= match_quote; - if match_quantity == max_match_by_quote || rem_base_quantity == 0 { - break; - } - } - let book_base_quantity = rem_base_quantity.min(rem_quote_quantity / price); - if post_allowed && book_base_quantity > 0 { - bids_quantity = bids_quantity.checked_add(book_base_quantity).unwrap(); - } - Ok((taker_base, taker_quote, bids_quantity, asks_quantity)) - } + // taker_base += match_quantity; + // taker_quote -= match_quote; + // if match_quantity == max_match_by_quote || rem_base_quantity == 0 { + // break; + // } + // } + // let book_base_quantity = rem_base_quantity.min(rem_quote_quantity / price); + // if post_allowed && book_base_quantity > 0 { + // bids_quantity = bids_quantity.checked_add(book_base_quantity).unwrap(); + // } + // Ok((taker_base, taker_quote, bids_quantity, asks_quantity)) + // } - pub fn sim_new_ask( - &self, - market: &PerpMarket, - // info: &PerpMarketInfo, - oracle_price: I80F48, - price: i64, - max_base_quantity: i64, // guaranteed to be greater than zero due to initial check - max_quote_quantity: i64, // guaranteed to be greater than zero due to initial check - order_type: OrderType, - now_ts: u64, - ) -> std::result::Result<(i64, i64, i64, i64), Error> { - let (mut taker_base, mut taker_quote, bids_quantity, mut asks_quantity) = (0, 0, 0, 0i64); + // pub fn sim_new_ask( + // &self, + // market: &PerpMarket, + // // info: &PerpMarketInfo, + // oracle_price: I80F48, + // price: i64, + // max_base_quantity: i64, // guaranteed to be greater than zero due to initial check + // max_quote_quantity: i64, // guaranteed to be greater than zero due to initial check + // order_type: OrderType, + // now_ts: u64, + // ) -> std::result::Result<(i64, i64, i64, i64), Error> { + // let (mut taker_base, mut taker_quote, bids_quantity, mut asks_quantity) = (0, 0, 0, 0i64); - let (post_only, mut post_allowed, price) = match order_type { - OrderType::Limit => (false, true, price), - OrderType::ImmediateOrCancel => (false, false, price), - OrderType::PostOnly => (true, true, price), - OrderType::Market => (false, false, 1), - OrderType::PostOnlySlide => { - let price = if let Some(best_bid_price) = self.get_best_bid_price(now_ts) { - price.max(best_bid_price.checked_add(1).ok_or(MangoError::SomeError)?) - // todo math_err - } else { - price - }; - (true, true, price) - } - }; - // if post_allowed { - // // price limit check computed lazily to save CU on average - // let native_price = market.lot_to_native_price(price); - // if native_price.checked_div(oracle_price).unwrap() < info.maint_asset_weight { - // msg!("Posting on book disallowed due to price limits"); - // post_allowed = false; - // } - // } + // let (post_only, mut post_allowed, price) = match order_type { + // OrderType::Limit => (false, true, price), + // OrderType::ImmediateOrCancel => (false, false, price), + // OrderType::PostOnly => (true, true, price), + // OrderType::Market => (false, false, 1), + // OrderType::PostOnlySlide => { + // let price = if let Some(best_bid_price) = self.get_best_bid_price(now_ts) { + // price.max(best_bid_price.checked_add(1).ok_or(MangoError::SomeError)?) + // // todo math_err + // } else { + // price + // }; + // (true, true, price) + // } + // }; + // // if post_allowed { + // // // price limit check computed lazily to save CU on average + // // let native_price = market.lot_to_native_price(price); + // // if native_price.checked_div(oracle_price).unwrap() < info.maint_asset_weight { + // // msg!("Posting on book disallowed due to price limits"); + // // post_allowed = false; + // // } + // // } - let mut rem_base_quantity = max_base_quantity; // base lots (aka contracts) - let mut rem_quote_quantity = max_quote_quantity; + // let mut rem_base_quantity = max_base_quantity; // base lots (aka contracts) + // let mut rem_quote_quantity = max_quote_quantity; - for (_, best_bid) in self.bids.iter_valid(now_ts) { - let best_bid_price = best_bid.price(); - if price > best_bid_price { - break; - } else if post_only { - return Ok((taker_base, taker_quote, bids_quantity, asks_quantity)); - } + // for (_, best_bid) in self.bids.iter_valid(now_ts) { + // let best_bid_price = best_bid.price(); + // if price > best_bid_price { + // break; + // } else if post_only { + // return Ok((taker_base, taker_quote, bids_quantity, asks_quantity)); + // } - let max_match_by_quote = rem_quote_quantity / best_bid_price; - let match_quantity = rem_base_quantity - .min(best_bid.quantity) - .min(max_match_by_quote); + // let max_match_by_quote = rem_quote_quantity / best_bid_price; + // let match_quantity = rem_base_quantity + // .min(best_bid.quantity) + // .min(max_match_by_quote); - let match_quote = match_quantity * best_bid_price; - rem_base_quantity -= match_quantity; - rem_quote_quantity -= match_quote; + // let match_quote = match_quantity * best_bid_price; + // rem_base_quantity -= match_quantity; + // rem_quote_quantity -= match_quote; - taker_base -= match_quantity; - taker_quote += match_quote; - if match_quantity == max_match_by_quote || rem_base_quantity == 0 { - break; - } - } + // taker_base -= match_quantity; + // taker_quote += match_quote; + // if match_quantity == max_match_by_quote || rem_base_quantity == 0 { + // break; + // } + // } - let book_base_quantity = rem_base_quantity.min(rem_quote_quantity / price); - if post_allowed && book_base_quantity > 0 { - asks_quantity = asks_quantity.checked_add(book_base_quantity).unwrap(); - } - Ok((taker_base, taker_quote, bids_quantity, asks_quantity)) - } + // let book_base_quantity = rem_base_quantity.min(rem_quote_quantity / price); + // if post_allowed && book_base_quantity > 0 { + // asks_quantity = asks_quantity.checked_add(book_base_quantity).unwrap(); + // } + // Ok((taker_base, taker_quote, bids_quantity, asks_quantity)) + // } // todo: can new_bid and new_ask be elegantly folded into one method? #[inline(never)] @@ -342,8 +341,8 @@ impl<'a> Book<'a> { // mango_cache: &MangoCache, // event_queue: &mut EventQueue, market: &mut PerpMarket, - oracle_price: I80F48, - mango_account: &mut MangoAccount, + // oracle_price: I80F48, + // mango_account: &mut MangoAccount, mango_account_pk: &Pubkey, // market_index: usize, price: i64, @@ -438,12 +437,12 @@ impl<'a> Book<'a> { .min(max_match_by_quote); let done = match_quantity == max_match_by_quote || match_quantity == rem_base_quantity; - let match_quote = match_quantity * best_ask_price; - rem_base_quantity -= match_quantity; - rem_quote_quantity -= match_quote; + let match_quote = cm!(match_quantity * best_ask_price); + rem_base_quantity = cm!(rem_base_quantity - match_quantity); + rem_quote_quantity = cm!(rem_quote_quantity - match_quote); // mango_account.perp_accounts[market_index].add_taker_trade(match_quantity, -match_quote); - let new_best_ask_quantity = best_ask.quantity - match_quantity; + let new_best_ask_quantity = cm!(best_ask.quantity - match_quantity); let maker_out = new_best_ask_quantity == 0; if maker_out { ask_deletes.push(best_ask.key); @@ -495,7 +494,7 @@ impl<'a> Book<'a> { break; } } - let total_quote_taken = max_quote_quantity - rem_quote_quantity; + // let total_quote_taken = cm!(max_quote_quantity - rem_quote_quantity); // Apply changes to matched asks (handles invalidate on delete!) for (handle, new_quantity) in ask_changes { @@ -514,17 +513,17 @@ impl<'a> Book<'a> { let book_base_quantity = rem_base_quantity.min(rem_quote_quantity / price); if post_allowed && book_base_quantity > 0 { // Drop an expired order if possible - if let Some(expired_bid) = self.bids.remove_one_expired(now_ts) { - // let event = OutEvent::new( - // Side::Bid, - // expired_bid.owner_slot, - // now_ts, - // event_queue.header.seq_num, - // expired_bid.owner, - // expired_bid.quantity, - // ); - // event_queue.push_back(cast(event)).unwrap(); - } + // if let Some(expired_bid) = self.bids.remove_one_expired(now_ts) { + // let event = OutEvent::new( + // Side::Bid, + // expired_bid.owner_slot, + // now_ts, + // event_queue.header.seq_num, + // expired_bid.owner, + // expired_bid.quantity, + // ); + // event_queue.push_back(cast(event)).unwrap(); + // } if self.bids.is_full() { // If this bid is higher than lowest bid, boot that bid and insert this one @@ -599,271 +598,271 @@ impl<'a> Book<'a> { Ok(()) } - #[inline(never)] - pub fn new_ask( - &mut self, - program_id: &Pubkey, - // mango_group: &MangoGroup, - mango_group_pk: &Pubkey, - // mango_cache: &MangoCache, - event_queue: &mut EventQueue, - market: &mut PerpMarket, - oracle_price: I80F48, - mango_account: &mut MangoAccount, - mango_account_pk: &Pubkey, - market_index: usize, - price: i64, - max_base_quantity: i64, // guaranteed to be greater than zero due to initial check - max_quote_quantity: i64, // guaranteed to be greater than zero due to initial check - order_type: OrderType, - time_in_force: u8, - client_order_id: u64, - now_ts: u64, - referrer_mango_account_ai: Option<&AccountInfo>, - mut limit: u8, // max number of FillEvents allowed; guaranteed to be greater than 0 - ) -> Result<()> { - let (post_only, mut post_allowed, price) = match order_type { - OrderType::Limit => (false, true, price), - OrderType::ImmediateOrCancel => (false, false, price), - OrderType::PostOnly => (true, true, price), - OrderType::Market => (false, false, 1), - OrderType::PostOnlySlide => { - let price = if let Some(best_bid_price) = self.get_best_bid_price(now_ts) { - price.max(best_bid_price.checked_add(1).ok_or(MangoError::SomeError)?) - // math_err - } else { - price - }; - (true, true, price) - } - }; + // #[inline(never)] + // pub fn new_ask( + // &mut self, + // program_id: &Pubkey, + // // mango_group: &MangoGroup, + // mango_group_pk: &Pubkey, + // // mango_cache: &MangoCache, + // event_queue: &mut EventQueue, + // market: &mut PerpMarket, + // oracle_price: I80F48, + // mango_account: &mut MangoAccount, + // mango_account_pk: &Pubkey, + // market_index: usize, + // price: i64, + // max_base_quantity: i64, // guaranteed to be greater than zero due to initial check + // max_quote_quantity: i64, // guaranteed to be greater than zero due to initial check + // order_type: OrderType, + // time_in_force: u8, + // client_order_id: u64, + // now_ts: u64, + // referrer_mango_account_ai: Option<&AccountInfo>, + // mut limit: u8, // max number of FillEvents allowed; guaranteed to be greater than 0 + // ) -> Result<()> { + // let (post_only, mut post_allowed, price) = match order_type { + // OrderType::Limit => (false, true, price), + // OrderType::ImmediateOrCancel => (false, false, price), + // OrderType::PostOnly => (true, true, price), + // OrderType::Market => (false, false, 1), + // OrderType::PostOnlySlide => { + // let price = if let Some(best_bid_price) = self.get_best_bid_price(now_ts) { + // price.max(best_bid_price.checked_add(1).ok_or(MangoError::SomeError)?) + // // math_err + // } else { + // price + // }; + // (true, true, price) + // } + // }; - // let info = &mango_group.perp_markets[market_index]; - // if post_allowed { - // // price limit check computed lazily to save CU on average - // let native_price = market.lot_to_native_price(price); - // if native_price.checked_div(oracle_price).unwrap() < info.maint_asset_weight { - // msg!("Posting on book disallowed due to price limits"); - // post_allowed = false; - // } - // } + // // let info = &mango_group.perp_markets[market_index]; + // // if post_allowed { + // // // price limit check computed lazily to save CU on average + // // let native_price = market.lot_to_native_price(price); + // // if native_price.checked_div(oracle_price).unwrap() < info.maint_asset_weight { + // // msg!("Posting on book disallowed due to price limits"); + // // post_allowed = false; + // // } + // // } - // referral fee related variables - // let mut ref_fee_rate = None; - // let mut referrer_mango_account_opt = None; + // // referral fee related variables + // // let mut ref_fee_rate = None; + // // let mut referrer_mango_account_opt = None; - // generate new order id - let order_id = market.gen_order_id(Side::Ask, price); + // // generate new order id + // let order_id = market.gen_order_id(Side::Ask, price); - // Iterate through book and match against this new ask - // - // Any changes to matching bids are collected in bid_changes - // and then applied after this loop. - let mut rem_base_quantity = max_base_quantity; // base lots (aka contracts) - let mut rem_quote_quantity = max_quote_quantity; - let mut bid_changes: Vec<(NodeHandle, i64)> = vec![]; - let mut bid_deletes: Vec = vec![]; - let mut number_of_dropped_expired_orders = 0; - for (best_bid_h, best_bid) in self.bids.iter_all_including_invalid() { - if !best_bid.is_valid(now_ts) { - // Remove the order from the book unless we've done that enough - if number_of_dropped_expired_orders < DROP_EXPIRED_ORDER_LIMIT { - number_of_dropped_expired_orders += 1; - let event = OutEvent::new( - Side::Bid, - best_bid.owner_slot, - now_ts, - event_queue.header.seq_num, - best_bid.owner, - best_bid.quantity, - ); - event_queue.push_back(cast(event)).unwrap(); - bid_deletes.push(best_bid.key); - } - continue; - } + // // Iterate through book and match against this new ask + // // + // // Any changes to matching bids are collected in bid_changes + // // and then applied after this loop. + // let mut rem_base_quantity = max_base_quantity; // base lots (aka contracts) + // let mut rem_quote_quantity = max_quote_quantity; + // let mut bid_changes: Vec<(NodeHandle, i64)> = vec![]; + // let mut bid_deletes: Vec = vec![]; + // let mut number_of_dropped_expired_orders = 0; + // for (best_bid_h, best_bid) in self.bids.iter_all_including_invalid() { + // if !best_bid.is_valid(now_ts) { + // // Remove the order from the book unless we've done that enough + // if number_of_dropped_expired_orders < DROP_EXPIRED_ORDER_LIMIT { + // number_of_dropped_expired_orders += 1; + // let event = OutEvent::new( + // Side::Bid, + // best_bid.owner_slot, + // now_ts, + // event_queue.header.seq_num, + // best_bid.owner, + // best_bid.quantity, + // ); + // event_queue.push_back(cast(event)).unwrap(); + // bid_deletes.push(best_bid.key); + // } + // continue; + // } - let best_bid_price = best_bid.price(); + // let best_bid_price = best_bid.price(); - if price > best_bid_price { - break; - } else if post_only { - msg!("Order could not be placed due to PostOnly"); - post_allowed = false; - break; // return silently to not fail other instructions in tx - } else if limit == 0 { - msg!("Order matching limit reached"); - post_allowed = false; - break; - } + // if price > best_bid_price { + // break; + // } else if post_only { + // msg!("Order could not be placed due to PostOnly"); + // post_allowed = false; + // break; // return silently to not fail other instructions in tx + // } else if limit == 0 { + // msg!("Order matching limit reached"); + // post_allowed = false; + // break; + // } - let max_match_by_quote = rem_quote_quantity / best_bid_price; - let match_quantity = rem_base_quantity - .min(best_bid.quantity) - .min(max_match_by_quote); - let done = match_quantity == max_match_by_quote || match_quantity == rem_base_quantity; + // let max_match_by_quote = rem_quote_quantity / best_bid_price; + // let match_quantity = rem_base_quantity + // .min(best_bid.quantity) + // .min(max_match_by_quote); + // let done = match_quantity == max_match_by_quote || match_quantity == rem_base_quantity; - let match_quote = match_quantity * best_bid_price; - rem_base_quantity -= match_quantity; - rem_quote_quantity -= match_quote; - // mango_account.perp_accounts[market_index].add_taker_trade(-match_quantity, match_quote); + // let match_quote = match_quantity * best_bid_price; + // rem_base_quantity -= match_quantity; + // rem_quote_quantity -= match_quote; + // // mango_account.perp_accounts[market_index].add_taker_trade(-match_quantity, match_quote); - let new_best_bid_quantity = best_bid.quantity - match_quantity; - let maker_out = new_best_bid_quantity == 0; - if maker_out { - bid_deletes.push(best_bid.key); - } else { - bid_changes.push((best_bid_h, new_best_bid_quantity)); - } + // let new_best_bid_quantity = best_bid.quantity - match_quantity; + // let maker_out = new_best_bid_quantity == 0; + // if maker_out { + // bid_deletes.push(best_bid.key); + // } else { + // bid_changes.push((best_bid_h, new_best_bid_quantity)); + // } - // todo - // if ref_fee_rate is none, determine it - // if ref_valid, then pay into referrer, else pay to perp market - // if ref_fee_rate.is_none() { - // let (a, b) = determine_ref_vars( - // program_id, - // mango_group, - // mango_group_pk, - // mango_cache, - // mango_account, - // referrer_mango_account_ai, - // now_ts, - // )?; - // ref_fee_rate = Some(a); - // referrer_mango_account_opt = b; - // } + // // todo + // // if ref_fee_rate is none, determine it + // // if ref_valid, then pay into referrer, else pay to perp market + // // if ref_fee_rate.is_none() { + // // let (a, b) = determine_ref_vars( + // // program_id, + // // mango_group, + // // mango_group_pk, + // // mango_cache, + // // mango_account, + // // referrer_mango_account_ai, + // // now_ts, + // // )?; + // // ref_fee_rate = Some(a); + // // referrer_mango_account_opt = b; + // // } - // let fill = FillEvent::new( - // Side::Ask, - // best_bid.owner_slot, - // maker_out, - // now_ts, - // event_queue.header.seq_num, - // best_bid.owner, - // best_bid.key, - // best_bid.client_order_id, - // info.maker_fee, - // best_bid.best_initial, - // best_bid.timestamp, - // *mango_account_pk, - // order_id, - // client_order_id, - // info.taker_fee + ref_fee_rate.unwrap(), - // best_bid_price, - // match_quantity, - // best_bid.version, - // ); + // // let fill = FillEvent::new( + // // Side::Ask, + // // best_bid.owner_slot, + // // maker_out, + // // now_ts, + // // event_queue.header.seq_num, + // // best_bid.owner, + // // best_bid.key, + // // best_bid.client_order_id, + // // info.maker_fee, + // // best_bid.best_initial, + // // best_bid.timestamp, + // // *mango_account_pk, + // // order_id, + // // client_order_id, + // // info.taker_fee + ref_fee_rate.unwrap(), + // // best_bid_price, + // // match_quantity, + // // best_bid.version, + // // ); - // event_queue.push_back(cast(fill)).unwrap(); - limit -= 1; + // // event_queue.push_back(cast(fill)).unwrap(); + // limit -= 1; - if done { - break; - } - } - let total_quote_taken = max_quote_quantity - rem_quote_quantity; + // if done { + // break; + // } + // } + // let total_quote_taken = max_quote_quantity - rem_quote_quantity; - // Apply changes to matched bids (handles invalidate on delete!) - for (handle, new_quantity) in bid_changes { - self.bids - .get_mut(handle) - .unwrap() - .as_leaf_mut() - .unwrap() - .quantity = new_quantity; - } - for key in bid_deletes { - let _removed_leaf = self.bids.remove_by_key(key).unwrap(); - } + // // Apply changes to matched bids (handles invalidate on delete!) + // for (handle, new_quantity) in bid_changes { + // self.bids + // .get_mut(handle) + // .unwrap() + // .as_leaf_mut() + // .unwrap() + // .quantity = new_quantity; + // } + // for key in bid_deletes { + // let _removed_leaf = self.bids.remove_by_key(key).unwrap(); + // } - // If there are still quantity unmatched, place on the book - let book_base_quantity = rem_base_quantity.min(rem_quote_quantity / price); - if book_base_quantity > 0 && post_allowed { - // Drop an expired order if possible - if let Some(expired_ask) = self.asks.remove_one_expired(now_ts) { - let event = OutEvent::new( - Side::Ask, - expired_ask.owner_slot, - now_ts, - event_queue.header.seq_num, - expired_ask.owner, - expired_ask.quantity, - ); - event_queue.push_back(cast(event)).unwrap(); - } + // // If there are still quantity unmatched, place on the book + // let book_base_quantity = rem_base_quantity.min(rem_quote_quantity / price); + // if book_base_quantity > 0 && post_allowed { + // // Drop an expired order if possible + // if let Some(expired_ask) = self.asks.remove_one_expired(now_ts) { + // let event = OutEvent::new( + // Side::Ask, + // expired_ask.owner_slot, + // now_ts, + // event_queue.header.seq_num, + // expired_ask.owner, + // expired_ask.quantity, + // ); + // event_queue.push_back(cast(event)).unwrap(); + // } - if self.asks.is_full() { - // If this asks is lower than highest ask, boot that ask and insert this one - let max_ask = self.asks.remove_max().unwrap(); - require!(price < max_ask.price(), MangoError::SomeError); // OutOfSpace - let event = OutEvent::new( - Side::Ask, - max_ask.owner_slot, - now_ts, - event_queue.header.seq_num, - max_ask.owner, - max_ask.quantity, - ); - event_queue.push_back(cast(event)).unwrap(); - } + // if self.asks.is_full() { + // // If this asks is lower than highest ask, boot that ask and insert this one + // let max_ask = self.asks.remove_max().unwrap(); + // require!(price < max_ask.price(), MangoError::SomeError); // OutOfSpace + // let event = OutEvent::new( + // Side::Ask, + // max_ask.owner_slot, + // now_ts, + // event_queue.header.seq_num, + // max_ask.owner, + // max_ask.quantity, + // ); + // event_queue.push_back(cast(event)).unwrap(); + // } - // let best_initial = if market.meta_data.version == 0 { - // match self.get_best_ask_price(now_ts) { - // None => price, - // Some(p) => p, - // } - // } else { - // let max_depth: i64 = market.liquidity_mining_info.max_depth_bps.to_num(); - // self.get_asks_size_below(price, max_depth, now_ts) - // }; + // // let best_initial = if market.meta_data.version == 0 { + // // match self.get_best_ask_price(now_ts) { + // // None => price, + // // Some(p) => p, + // // } + // // } else { + // // let max_depth: i64 = market.liquidity_mining_info.max_depth_bps.to_num(); + // // self.get_asks_size_below(price, max_depth, now_ts) + // // }; - // let owner_slot = mango_account - // .next_order_slot() - // .ok_or(MangoError::SomeError)?; // TooManyOpenOrders - let new_ask = LeafNode::new( - 1, // todo market.meta_data.version, - 0, // todo owner_slot as u8, - order_id, - *mango_account_pk, - book_base_quantity, - client_order_id, - now_ts, - 0, // todo best_initial, - order_type, - time_in_force, - ); - let _result = self.asks.insert_leaf(&new_ask)?; + // // let owner_slot = mango_account + // // .next_order_slot() + // // .ok_or(MangoError::SomeError)?; // TooManyOpenOrders + // let new_ask = LeafNode::new( + // 1, // todo market.meta_data.version, + // 0, // todo owner_slot as u8, + // order_id, + // *mango_account_pk, + // book_base_quantity, + // client_order_id, + // now_ts, + // 0, // todo best_initial, + // order_type, + // time_in_force, + // ); + // let _result = self.asks.insert_leaf(&new_ask)?; - // TODO OPT remove if PlacePerpOrder needs more compute - msg!( - "ask on book order_id={} quantity={} price={}", - order_id, - book_base_quantity, - price - ); + // // TODO OPT remove if PlacePerpOrder needs more compute + // msg!( + // "ask on book order_id={} quantity={} price={}", + // order_id, + // book_base_quantity, + // price + // ); - // mango_account.add_order(market_index, Side::Ask, &new_ask)?; - } + // // mango_account.add_order(market_index, Side::Ask, &new_ask)?; + // } - // if there were matched taker quote apply ref fees - // we know ref_fee_rate is not None if total_quote_taken > 0 - // if total_quote_taken > 0 { - // apply_fees( - // market, - // info, - // mango_account, - // mango_account_pk, - // market_index, - // referrer_mango_account_opt, - // referrer_mango_account_ai, - // total_quote_taken, - // ref_fee_rate.unwrap(), - // // &mango_cache.perp_market_cache[market_index], - // ); - // } + // // if there were matched taker quote apply ref fees + // // we know ref_fee_rate is not None if total_quote_taken > 0 + // // if total_quote_taken > 0 { + // // apply_fees( + // // market, + // // info, + // // mango_account, + // // mango_account_pk, + // // market_index, + // // referrer_mango_account_opt, + // // referrer_mango_account_ai, + // // total_quote_taken, + // // ref_fee_rate.unwrap(), + // // // &mango_cache.perp_market_cache[market_index], + // // ); + // // } - Ok(()) - } + // Ok(()) + // } // pub fn cancel_order(&mut self, order_id: i128, side: Side) -> Result<()> { // match side { diff --git a/programs/mango-v4/src/state/orderbook/bookside.rs b/programs/mango-v4/src/state/orderbook/bookside.rs index ab24dbe0b..0c08474b5 100644 --- a/programs/mango-v4/src/state/orderbook/bookside.rs +++ b/programs/mango-v4/src/state/orderbook/bookside.rs @@ -1,4 +1,3 @@ -use bytemuck::Zeroable; use std::cell::RefMut; use anchor_lang::prelude::*; @@ -15,8 +14,6 @@ use crate::state::orderbook::nodes::{ }; use crate::util::LoadZeroCopy; -use super::Book; - pub const MAX_BOOK_NODES: usize = 1024; #[derive( diff --git a/programs/mango-v4/src/state/orderbook/metadata.rs b/programs/mango-v4/src/state/orderbook/metadata.rs index 95bf0dbeb..3a7e8fb21 100644 --- a/programs/mango-v4/src/state/orderbook/metadata.rs +++ b/programs/mango-v4/src/state/orderbook/metadata.rs @@ -1,7 +1,5 @@ use mango_macro::Pod; -use super::datatype::DataType; - #[derive(Copy, Clone, Pod, Default)] #[repr(C)] /// Stores meta information about the `Account` on chain diff --git a/programs/mango-v4/src/state/orderbook/ob_utils.rs b/programs/mango-v4/src/state/orderbook/ob_utils.rs index a01b99016..e0a0dcfc4 100644 --- a/programs/mango-v4/src/state/orderbook/ob_utils.rs +++ b/programs/mango-v4/src/state/orderbook/ob_utils.rs @@ -1,9 +1,7 @@ use anchor_lang::prelude::Error; -use bytemuck::{bytes_of, cast_slice_mut, from_bytes_mut, Contiguous, Pod}; +use bytemuck::{cast_slice_mut, from_bytes_mut, Pod}; use solana_program::account_info::AccountInfo; -use solana_program::program_error::ProgramError; -use solana_program::pubkey::Pubkey; use std::cell::RefMut; use std::mem::size_of; diff --git a/programs/mango-v4/src/state/orderbook/order_type.rs b/programs/mango-v4/src/state/orderbook/order_type.rs index ed5ce3c83..3379bdd87 100644 --- a/programs/mango-v4/src/state/orderbook/order_type.rs +++ b/programs/mango-v4/src/state/orderbook/order_type.rs @@ -1,5 +1,4 @@ use anchor_lang::prelude::*; -use bytemuck::{Pod, Zeroable}; use num_enum::{IntoPrimitive, TryFromPrimitive}; #[derive( diff --git a/programs/mango-v4/src/state/orderbook/queue.rs b/programs/mango-v4/src/state/orderbook/queue.rs index 0c8362af8..39d90d897 100644 --- a/programs/mango-v4/src/state/orderbook/queue.rs +++ b/programs/mango-v4/src/state/orderbook/queue.rs @@ -1,438 +1,437 @@ -use std::cell::RefMut; -use std::mem::size_of; +// use std::cell::RefMut; +// use std::mem::size_of; -use crate::error::MangoError; -use crate::state::orderbook::datatype::DataType; -use crate::state::PerpMarket; -use anchor_lang::prelude::*; -use fixed::types::I80F48; -use num_enum::{IntoPrimitive, TryFromPrimitive}; -use solana_program::account_info::AccountInfo; -use solana_program::pubkey::Pubkey; -use solana_program::sysvar::rent::Rent; -use static_assertions::const_assert_eq; +// use crate::error::MangoError; +// use crate::state::PerpMarket; +// use anchor_lang::prelude::*; +// use fixed::types::I80F48; +// use num_enum::{IntoPrimitive, TryFromPrimitive}; +// use solana_program::account_info::AccountInfo; +// use solana_program::pubkey::Pubkey; +// use solana_program::sysvar::rent::Rent; +// use static_assertions::const_assert_eq; -// use mango_logs::FillLog; -use mango_macro::Pod; +// // use mango_logs::FillLog; +// use mango_macro::Pod; -use super::metadata::MetaData; -use super::ob_utils::strip_header_mut; -use super::order_type::Side; -// use safe_transmute::{self, trivial::TriviallyTransmutable}; +// use super::metadata::MetaData; +// use super::ob_utils::strip_header_mut; +// use super::order_type::Side; +// // use safe_transmute::{self, trivial::TriviallyTransmutable}; -// use crate::error::{check_assert, MangoErrorCode, MangoResult, SourceFileId}; -// use crate::matching::Side; -// use crate::state::{DataType, MetaData, PerpMarket}; -// use crate::utils::strip_header_mut; +// // use crate::error::{check_assert, MangoErrorCode, MangoResult, SourceFileId}; +// // use crate::matching::Side; +// // use crate::state::{DataType, MetaData, PerpMarket}; +// // use crate::utils::strip_header_mut; -// Don't want event queue to become single threaded if it's logging liquidations -// Most common scenario will be liqors depositing USDC and withdrawing some other token -// So tying it to token deposited is not wise -// also can't tie it to token withdrawn because during bull market, liqs will be depositing all base tokens and withdrawing quote -// +// // Don't want event queue to become single threaded if it's logging liquidations +// // Most common scenario will be liqors depositing USDC and withdrawing some other token +// // So tying it to token deposited is not wise +// // also can't tie it to token withdrawn because during bull market, liqs will be depositing all base tokens and withdrawing quote +// // -pub trait QueueHeader: bytemuck::Pod { - type Item: bytemuck::Pod + Copy; +// pub trait QueueHeader: bytemuck::Pod { +// type Item: bytemuck::Pod + Copy; - fn head(&self) -> usize; - fn set_head(&mut self, value: usize); - fn count(&self) -> usize; - fn set_count(&mut self, value: usize); +// fn head(&self) -> usize; +// fn set_head(&mut self, value: usize); +// fn count(&self) -> usize; +// fn set_count(&mut self, value: usize); - fn incr_event_id(&mut self); - fn decr_event_id(&mut self, n: usize); -} +// fn incr_event_id(&mut self); +// fn decr_event_id(&mut self, n: usize); +// } -pub struct Queue<'a, H: QueueHeader> { - pub header: RefMut<'a, H>, - pub buf: RefMut<'a, [H::Item]>, -} +// pub struct Queue<'a, H: QueueHeader> { +// pub header: RefMut<'a, H>, +// pub buf: RefMut<'a, [H::Item]>, +// } -impl<'a, H: QueueHeader> Queue<'a, H> { - pub fn new(header: RefMut<'a, H>, buf: RefMut<'a, [H::Item]>) -> Self { - Self { header, buf } - } +// impl<'a, H: QueueHeader> Queue<'a, H> { +// pub fn new(header: RefMut<'a, H>, buf: RefMut<'a, [H::Item]>) -> Self { +// Self { header, buf } +// } - pub fn load_mut(account: &'a AccountInfo) -> Result { - let (header, buf) = strip_header_mut::(account)?; - Ok(Self { header, buf }) - } +// pub fn load_mut(account: &'a AccountInfo) -> Result { +// let (header, buf) = strip_header_mut::(account)?; +// Ok(Self { header, buf }) +// } - pub fn len(&self) -> usize { - self.header.count() - } +// pub fn len(&self) -> usize { +// self.header.count() +// } - pub fn full(&self) -> bool { - self.header.count() == self.buf.len() - } +// pub fn full(&self) -> bool { +// self.header.count() == self.buf.len() +// } - pub fn empty(&self) -> bool { - self.header.count() == 0 - } +// pub fn empty(&self) -> bool { +// self.header.count() == 0 +// } - pub fn push_back(&mut self, value: H::Item) -> std::result::Result<(), H::Item> { - if self.full() { - return Err(value); - } - let slot = (self.header.head() + self.header.count()) % self.buf.len(); - self.buf[slot] = value; +// pub fn push_back(&mut self, value: H::Item) -> std::result::Result<(), H::Item> { +// if self.full() { +// return Err(value); +// } +// let slot = (self.header.head() + self.header.count()) % self.buf.len(); +// self.buf[slot] = value; - let count = self.header.count(); - self.header.set_count(count + 1); +// let count = self.header.count(); +// self.header.set_count(count + 1); - self.header.incr_event_id(); - Ok(()) - } +// self.header.incr_event_id(); +// Ok(()) +// } - pub fn peek_front(&self) -> Option<&H::Item> { - if self.empty() { - return None; - } - Some(&self.buf[self.header.head()]) - } +// pub fn peek_front(&self) -> Option<&H::Item> { +// if self.empty() { +// return None; +// } +// Some(&self.buf[self.header.head()]) +// } - pub fn peek_front_mut(&mut self) -> Option<&mut H::Item> { - if self.empty() { - return None; - } - Some(&mut self.buf[self.header.head()]) - } +// pub fn peek_front_mut(&mut self) -> Option<&mut H::Item> { +// if self.empty() { +// return None; +// } +// Some(&mut self.buf[self.header.head()]) +// } - pub fn pop_front(&mut self) -> std::result::Result { - if self.empty() { - return Err(()); - } - let value = self.buf[self.header.head()]; +// pub fn pop_front(&mut self) -> std::result::Result { +// if self.empty() { +// return Err(()); +// } +// let value = self.buf[self.header.head()]; - let count = self.header.count(); - self.header.set_count(count - 1); +// let count = self.header.count(); +// self.header.set_count(count - 1); - let head = self.header.head(); - self.header.set_head((head + 1) % self.buf.len()); +// let head = self.header.head(); +// self.header.set_head((head + 1) % self.buf.len()); - Ok(value) - } +// Ok(value) +// } - pub fn revert_pushes(&mut self, desired_len: usize) -> Result<()> { - require!(desired_len <= self.header.count(), MangoError::SomeError); - let len_diff = self.header.count() - desired_len; - self.header.set_count(desired_len); - self.header.decr_event_id(len_diff); - Ok(()) - } +// pub fn revert_pushes(&mut self, desired_len: usize) -> Result<()> { +// require!(desired_len <= self.header.count(), MangoError::SomeError); +// let len_diff = self.header.count() - desired_len; +// self.header.set_count(desired_len); +// self.header.decr_event_id(len_diff); +// Ok(()) +// } - pub fn iter(&self) -> impl Iterator { - QueueIterator { - queue: self, - index: 0, - } - } -} +// pub fn iter(&self) -> impl Iterator { +// QueueIterator { +// queue: self, +// index: 0, +// } +// } +// } -struct QueueIterator<'a, 'b, H: QueueHeader> { - queue: &'b Queue<'a, H>, - index: usize, -} +// struct QueueIterator<'a, 'b, H: QueueHeader> { +// queue: &'b Queue<'a, H>, +// index: usize, +// } -impl<'a, 'b, H: QueueHeader> Iterator for QueueIterator<'a, 'b, H> { - type Item = &'b H::Item; - fn next(&mut self) -> Option { - if self.index == self.queue.len() { - None - } else { - let item = - &self.queue.buf[(self.queue.header.head() + self.index) % self.queue.buf.len()]; - self.index += 1; - Some(item) - } - } -} +// impl<'a, 'b, H: QueueHeader> Iterator for QueueIterator<'a, 'b, H> { +// type Item = &'b H::Item; +// fn next(&mut self) -> Option { +// if self.index == self.queue.len() { +// None +// } else { +// let item = +// &self.queue.buf[(self.queue.header.head() + self.index) % self.queue.buf.len()]; +// self.index += 1; +// Some(item) +// } +// } +// } -#[account(zero_copy)] -pub struct EventQueueHeader { - pub meta_data: MetaData, - head: usize, - count: usize, - pub seq_num: usize, -} -// unsafe impl TriviallyTransmutable for EventQueueHeader {} +// #[account(zero_copy)] +// pub struct EventQueueHeader { +// pub meta_data: MetaData, +// head: usize, +// count: usize, +// pub seq_num: usize, +// } +// // unsafe impl TriviallyTransmutable for EventQueueHeader {} -impl QueueHeader for EventQueueHeader { - type Item = AnyEvent; +// impl QueueHeader for EventQueueHeader { +// type Item = AnyEvent; - fn head(&self) -> usize { - self.head - } - fn set_head(&mut self, value: usize) { - self.head = value; - } - fn count(&self) -> usize { - self.count - } - fn set_count(&mut self, value: usize) { - self.count = value; - } - fn incr_event_id(&mut self) { - self.seq_num += 1; - } - fn decr_event_id(&mut self, n: usize) { - self.seq_num -= n; - } -} +// fn head(&self) -> usize { +// self.head +// } +// fn set_head(&mut self, value: usize) { +// self.head = value; +// } +// fn count(&self) -> usize { +// self.count +// } +// fn set_count(&mut self, value: usize) { +// self.count = value; +// } +// fn incr_event_id(&mut self) { +// self.seq_num += 1; +// } +// fn decr_event_id(&mut self, n: usize) { +// self.seq_num -= n; +// } +// } -pub type EventQueue<'a> = Queue<'a, EventQueueHeader>; +// pub type EventQueue<'a> = Queue<'a, EventQueueHeader>; -impl<'a> EventQueue<'a> { - pub fn load_mut_checked( - account: &'a AccountInfo, - program_id: &Pubkey, - perp_market: &PerpMarket, - ) -> Result { - require!(account.owner == program_id, MangoError::SomeError); // MangoErrorCode::InvalidOwner - // require!( - // &perp_market.event_queue == account.key, - // MangoError::SomeError - // ); // MangoErrorCode::InvalidAccount - Self::load_mut(account) - } +// impl<'a> EventQueue<'a> { +// pub fn load_mut_checked( +// account: &'a AccountInfo, +// program_id: &Pubkey, +// perp_market: &PerpMarket, +// ) -> Result { +// require!(account.owner == program_id, MangoError::SomeError); // MangoErrorCode::InvalidOwner +// // require!( +// // &perp_market.event_queue == account.key, +// // MangoError::SomeError +// // ); // MangoErrorCode::InvalidAccount +// Self::load_mut(account) +// } - pub fn load_and_init( - account: &'a AccountInfo, - program_id: &Pubkey, - rent: &Rent, - ) -> Result { - // NOTE: check this first so we can borrow account later - require!( - rent.is_exempt(account.lamports(), account.data_len()), - MangoError::SomeError - ); //MangoErrorCode::AccountNotRentExempt +// pub fn load_and_init( +// account: &'a AccountInfo, +// program_id: &Pubkey, +// rent: &Rent, +// ) -> Result { +// // NOTE: check this first so we can borrow account later +// require!( +// rent.is_exempt(account.lamports(), account.data_len()), +// MangoError::SomeError +// ); //MangoErrorCode::AccountNotRentExempt - let mut state = Self::load_mut(account)?; - require!(account.owner == program_id, MangoError::SomeError); // MangoErrorCode::InvalidOwner +// let mut state = Self::load_mut(account)?; +// require!(account.owner == program_id, MangoError::SomeError); // MangoErrorCode::InvalidOwner - // require!( - // !state.header.meta_data.is_initialized, - // MangoError::SomeError - // ); - // state.header.meta_data = MetaData::new(DataType::EventQueue, 0, true); +// // require!( +// // !state.header.meta_data.is_initialized, +// // MangoError::SomeError +// // ); +// // state.header.meta_data = MetaData::new(DataType::EventQueue, 0, true); - Ok(state) - } -} +// Ok(state) +// } +// } -#[derive(Copy, Clone, IntoPrimitive, TryFromPrimitive, Eq, PartialEq)] -#[repr(u8)] -pub enum EventType { - Fill, - Out, - Liquidate, -} +// #[derive(Copy, Clone, IntoPrimitive, TryFromPrimitive, Eq, PartialEq)] +// #[repr(u8)] +// pub enum EventType { +// Fill, +// Out, +// Liquidate, +// } -const EVENT_SIZE: usize = 200; -#[derive(Copy, Clone, Debug, Pod)] -#[repr(C)] -pub struct AnyEvent { - pub event_type: u8, - pub padding: [u8; EVENT_SIZE - 1], -} -// unsafe impl TriviallyTransmutable for AnyEvent {} +// const EVENT_SIZE: usize = 200; +// #[derive(Copy, Clone, Debug, Pod)] +// #[repr(C)] +// pub struct AnyEvent { +// pub event_type: u8, +// pub padding: [u8; EVENT_SIZE - 1], +// } +// // unsafe impl TriviallyTransmutable for AnyEvent {} -#[derive(Copy, Clone, Debug, Pod)] -#[repr(C)] -pub struct FillEvent { - pub event_type: u8, - pub taker_side: Side, // side from the taker's POV - pub maker_slot: u8, - pub maker_out: bool, // true if maker order quantity == 0 - pub version: u8, - pub market_fees_applied: bool, - pub padding: [u8; 2], - pub timestamp: u64, - pub seq_num: usize, // note: usize same as u64 +// #[derive(Copy, Clone, Debug, Pod)] +// #[repr(C)] +// pub struct FillEvent { +// pub event_type: u8, +// pub taker_side: Side, // side from the taker's POV +// pub maker_slot: u8, +// pub maker_out: bool, // true if maker order quantity == 0 +// pub version: u8, +// pub market_fees_applied: bool, +// pub padding: [u8; 2], +// pub timestamp: u64, +// pub seq_num: usize, // note: usize same as u64 - pub maker: Pubkey, - pub maker_order_id: i128, - pub maker_client_order_id: u64, - pub maker_fee: I80F48, +// pub maker: Pubkey, +// pub maker_order_id: i128, +// pub maker_client_order_id: u64, +// pub maker_fee: I80F48, - // The best bid/ask at the time the maker order was placed. Used for liquidity incentives - pub best_initial: i64, +// // The best bid/ask at the time the maker order was placed. Used for liquidity incentives +// pub best_initial: i64, - // Timestamp of when the maker order was placed; copied over from the LeafNode - pub maker_timestamp: u64, +// // Timestamp of when the maker order was placed; copied over from the LeafNode +// pub maker_timestamp: u64, - pub taker: Pubkey, - pub taker_order_id: i128, - pub taker_client_order_id: u64, - pub taker_fee: I80F48, +// pub taker: Pubkey, +// pub taker_order_id: i128, +// pub taker_client_order_id: u64, +// pub taker_fee: I80F48, - pub price: i64, - pub quantity: i64, // number of quote lots -} -// unsafe impl TriviallyTransmutable for FillEvent {} +// pub price: i64, +// pub quantity: i64, // number of quote lots +// } +// // unsafe impl TriviallyTransmutable for FillEvent {} -impl FillEvent { - pub fn new( - taker_side: Side, - maker_slot: u8, - maker_out: bool, - timestamp: u64, - seq_num: usize, - maker: Pubkey, - maker_order_id: i128, - maker_client_order_id: u64, - maker_fee: I80F48, - best_initial: i64, - maker_timestamp: u64, +// impl FillEvent { +// pub fn new( +// taker_side: Side, +// maker_slot: u8, +// maker_out: bool, +// timestamp: u64, +// seq_num: usize, +// maker: Pubkey, +// maker_order_id: i128, +// maker_client_order_id: u64, +// maker_fee: I80F48, +// best_initial: i64, +// maker_timestamp: u64, - taker: Pubkey, - taker_order_id: i128, - taker_client_order_id: u64, - taker_fee: I80F48, - price: i64, - quantity: i64, - version: u8, - ) -> FillEvent { - Self { - event_type: EventType::Fill as u8, - taker_side, - maker_slot, - maker_out, - version, - market_fees_applied: true, // Since mango v3.3.5, market fees are adjusted at matching time - padding: [0u8; 2], - timestamp, - seq_num, - maker, - maker_order_id, - maker_client_order_id, - maker_fee, - best_initial, - maker_timestamp, - taker, - taker_order_id, - taker_client_order_id, - taker_fee, - price, - quantity, - } - } +// taker: Pubkey, +// taker_order_id: i128, +// taker_client_order_id: u64, +// taker_fee: I80F48, +// price: i64, +// quantity: i64, +// version: u8, +// ) -> FillEvent { +// Self { +// event_type: EventType::Fill as u8, +// taker_side, +// maker_slot, +// maker_out, +// version, +// market_fees_applied: true, // Since mango v3.3.5, market fees are adjusted at matching time +// padding: [0u8; 2], +// timestamp, +// seq_num, +// maker, +// maker_order_id, +// maker_client_order_id, +// maker_fee, +// best_initial, +// maker_timestamp, +// taker, +// taker_order_id, +// taker_client_order_id, +// taker_fee, +// price, +// quantity, +// } +// } - pub fn base_quote_change(&self, side: Side) -> (i64, i64) { - match side { - Side::Bid => ( - self.quantity, - -self.price.checked_mul(self.quantity).unwrap(), - ), - Side::Ask => ( - -self.quantity, - self.price.checked_mul(self.quantity).unwrap(), - ), - } - } +// pub fn base_quote_change(&self, side: Side) -> (i64, i64) { +// match side { +// Side::Bid => ( +// self.quantity, +// -self.price.checked_mul(self.quantity).unwrap(), +// ), +// Side::Ask => ( +// -self.quantity, +// self.price.checked_mul(self.quantity).unwrap(), +// ), +// } +// } - // pub fn to_fill_log(&self, mango_group: Pubkey, market_index: usize) -> FillLog { - // FillLog { - // mango_group, - // market_index: market_index as u64, - // taker_side: self.taker_side as u8, - // maker_slot: self.maker_slot, - // maker_out: self.maker_out, - // timestamp: self.timestamp, - // seq_num: self.seq_num as u64, - // maker: self.maker, - // maker_order_id: self.maker_order_id, - // maker_client_order_id: self.maker_client_order_id, - // maker_fee: self.maker_fee.to_bits(), - // best_initial: self.best_initial, - // maker_timestamp: self.maker_timestamp, - // taker: self.taker, - // taker_order_id: self.taker_order_id, - // taker_client_order_id: self.taker_client_order_id, - // taker_fee: self.taker_fee.to_bits(), - // price: self.price, - // quantity: self.quantity, - // } - // } -} +// // pub fn to_fill_log(&self, mango_group: Pubkey, market_index: usize) -> FillLog { +// // FillLog { +// // mango_group, +// // market_index: market_index as u64, +// // taker_side: self.taker_side as u8, +// // maker_slot: self.maker_slot, +// // maker_out: self.maker_out, +// // timestamp: self.timestamp, +// // seq_num: self.seq_num as u64, +// // maker: self.maker, +// // maker_order_id: self.maker_order_id, +// // maker_client_order_id: self.maker_client_order_id, +// // maker_fee: self.maker_fee.to_bits(), +// // best_initial: self.best_initial, +// // maker_timestamp: self.maker_timestamp, +// // taker: self.taker, +// // taker_order_id: self.taker_order_id, +// // taker_client_order_id: self.taker_client_order_id, +// // taker_fee: self.taker_fee.to_bits(), +// // price: self.price, +// // quantity: self.quantity, +// // } +// // } +// } -#[derive(Copy, Clone, Debug, Pod)] -#[repr(C)] -pub struct OutEvent { - pub event_type: u8, - pub side: Side, - pub slot: u8, - padding0: [u8; 5], - pub timestamp: u64, - pub seq_num: usize, - pub owner: Pubkey, - pub quantity: i64, - padding1: [u8; EVENT_SIZE - 64], -} -// unsafe impl TriviallyTransmutable for OutEvent {} -impl OutEvent { - pub fn new( - side: Side, - slot: u8, - timestamp: u64, - seq_num: usize, - owner: Pubkey, - quantity: i64, - ) -> Self { - Self { - event_type: EventType::Out.into(), - side, - slot, - padding0: [0; 5], - timestamp, - seq_num, - owner, - quantity, - padding1: [0; EVENT_SIZE - 64], - } - } -} +// #[derive(Copy, Clone, Debug, Pod)] +// #[repr(C)] +// pub struct OutEvent { +// pub event_type: u8, +// pub side: Side, +// pub slot: u8, +// padding0: [u8; 5], +// pub timestamp: u64, +// pub seq_num: usize, +// pub owner: Pubkey, +// pub quantity: i64, +// padding1: [u8; EVENT_SIZE - 64], +// } +// // unsafe impl TriviallyTransmutable for OutEvent {} +// impl OutEvent { +// pub fn new( +// side: Side, +// slot: u8, +// timestamp: u64, +// seq_num: usize, +// owner: Pubkey, +// quantity: i64, +// ) -> Self { +// Self { +// event_type: EventType::Out.into(), +// side, +// slot, +// padding0: [0; 5], +// timestamp, +// seq_num, +// owner, +// quantity, +// padding1: [0; EVENT_SIZE - 64], +// } +// } +// } -#[derive(Copy, Clone, Debug, Pod)] -#[repr(C)] -/// Liquidation for the PerpMarket this EventQueue is for -pub struct LiquidateEvent { - pub event_type: u8, - padding0: [u8; 7], - pub timestamp: u64, - pub seq_num: usize, - pub liqee: Pubkey, - pub liqor: Pubkey, - pub price: I80F48, // oracle price at the time of liquidation - pub quantity: i64, // number of contracts that were moved from liqee to liqor - pub liquidation_fee: I80F48, // liq fee for this earned for this market - padding1: [u8; EVENT_SIZE - 128], -} -// unsafe impl TriviallyTransmutable for LiquidateEvent {} -impl LiquidateEvent { - pub fn new( - timestamp: u64, - seq_num: usize, - liqee: Pubkey, - liqor: Pubkey, - price: I80F48, - quantity: i64, - liquidation_fee: I80F48, - ) -> Self { - Self { - event_type: EventType::Liquidate.into(), - padding0: [0u8; 7], - timestamp, - seq_num, - liqee, - liqor, - price, - quantity, - liquidation_fee, - padding1: [0u8; EVENT_SIZE - 128], - } - } -} -const_assert_eq!(size_of::(), size_of::()); -const_assert_eq!(size_of::(), size_of::()); -const_assert_eq!(size_of::(), size_of::()); +// #[derive(Copy, Clone, Debug, Pod)] +// #[repr(C)] +// /// Liquidation for the PerpMarket this EventQueue is for +// pub struct LiquidateEvent { +// pub event_type: u8, +// padding0: [u8; 7], +// pub timestamp: u64, +// pub seq_num: usize, +// pub liqee: Pubkey, +// pub liqor: Pubkey, +// pub price: I80F48, // oracle price at the time of liquidation +// pub quantity: i64, // number of contracts that were moved from liqee to liqor +// pub liquidation_fee: I80F48, // liq fee for this earned for this market +// padding1: [u8; EVENT_SIZE - 128], +// } +// // unsafe impl TriviallyTransmutable for LiquidateEvent {} +// impl LiquidateEvent { +// pub fn new( +// timestamp: u64, +// seq_num: usize, +// liqee: Pubkey, +// liqor: Pubkey, +// price: I80F48, +// quantity: i64, +// liquidation_fee: I80F48, +// ) -> Self { +// Self { +// event_type: EventType::Liquidate.into(), +// padding0: [0u8; 7], +// timestamp, +// seq_num, +// liqee, +// liqor, +// price, +// quantity, +// liquidation_fee, +// padding1: [0u8; EVENT_SIZE - 128], +// } +// } +// } +// const_assert_eq!(size_of::(), size_of::()); +// const_assert_eq!(size_of::(), size_of::()); +// const_assert_eq!(size_of::(), size_of::()); diff --git a/programs/mango-v4/tests/program_test/mango_client.rs b/programs/mango-v4/tests/program_test/mango_client.rs index 14a623fd3..633ff66e2 100644 --- a/programs/mango-v4/tests/program_test/mango_client.rs +++ b/programs/mango-v4/tests/program_test/mango_client.rs @@ -1159,13 +1159,11 @@ impl<'keypair> ClientInstruction for PlacePerpOrderInstruction<'keypair> { ) -> (Self::Accounts, instruction::Instruction) { let program_id = mango_v4::id(); let instruction = Self::Instruction { - side: Side::Bid, price: 1, max_base_quantity: 1, max_quote_quantity: 1, client_order_id: 0, order_type: OrderType::Limit, - reduce_only: false, expiry_timestamp: 0, limit: 1, }; diff --git a/programs/mango-v4/tests/program_test/mod.rs b/programs/mango-v4/tests/program_test/mod.rs index 97ef97e06..8664e6867 100644 --- a/programs/mango-v4/tests/program_test/mod.rs +++ b/programs/mango-v4/tests/program_test/mod.rs @@ -109,7 +109,9 @@ impl TestContextBuilder { })); // intentionally set to half the limit, to catch potential problems early - test.set_compute_max_units(100000); + // TODO make configurable + // margin trade test just goes above 100000 atm + test.set_compute_max_units(101000); Self { test,