use crate::{error::Contextable, error::MangoError, error_msg}; use anchor_lang::prelude::*; use bytemuck::cast_ref; use fixed::types::I80F48; use num_enum::{IntoPrimitive, TryFromPrimitive}; use static_assertions::const_assert_eq; use std::mem::size_of; use super::Side; pub const MAX_NUM_EVENTS: u32 = 488; pub trait QueueHeader: bytemuck::Pod { type Item: bytemuck::Pod + Copy; fn head(&self) -> usize; fn set_head(&mut self, value: u32); fn count(&self) -> usize; fn set_count(&mut self, value: u32); fn incr_event_id(&mut self); fn decr_event_id(&mut self, n: u64); } #[account(zero_copy)] pub struct EventQueue { pub header: EventQueueHeader, pub buf: [AnyEvent; MAX_NUM_EVENTS as usize], pub reserved: [u8; 64], } const_assert_eq!(std::mem::size_of::(), 16 + 488 * 208 + 64); const_assert_eq!(std::mem::size_of::(), 101584); const_assert_eq!(std::mem::size_of::() % 8, 0); impl EventQueue { pub fn len(&self) -> usize { self.header.count() } pub fn is_empty(&self) -> bool { self.len() == 0 } pub fn full(&self) -> bool { self.header.count() == self.buf.len() } pub fn push_back(&mut self, value: AnyEvent) -> std::result::Result<(), AnyEvent> { 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) as u32); // guaranteed because of full() check self.header.incr_event_id(); Ok(()) } pub fn peek_front(&self) -> Option<&AnyEvent> { if self.is_empty() { return None; } Some(&self.buf[self.header.head()]) } pub fn peek_front_mut(&mut self) -> Option<&mut AnyEvent> { if self.is_empty() { return None; } Some(&mut self.buf[self.header.head()]) } pub fn pop_front(&mut self) -> Result { require!(!self.is_empty(), MangoError::SomeError); let value = self.buf[self.header.head()]; let count = self.header.count(); self.header.set_count((count - 1) as u32); let head = self.header.head(); self.header.set_head(((head + 1) % self.buf.len()) as u32); 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 as u32); self.header.decr_event_id(len_diff as u64); Ok(()) } pub fn iter(&self) -> impl Iterator { EventQueueIterator { queue: self, index: 0, } } } struct EventQueueIterator<'a> { queue: &'a EventQueue, index: usize, } impl<'a> Iterator for EventQueueIterator<'a> { type Item = &'a AnyEvent; 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) } } } #[zero_copy] #[derive(bytemuck::Pod, bytemuck::Zeroable)] pub struct EventQueueHeader { head: u32, count: u32, pub seq_num: u64, } const_assert_eq!(std::mem::size_of::(), 16); const_assert_eq!(std::mem::size_of::() % 8, 0); impl QueueHeader for EventQueueHeader { type Item = AnyEvent; fn head(&self) -> usize { self.head as usize } fn set_head(&mut self, value: u32) { self.head = value; } fn count(&self) -> usize { self.count as usize } fn set_count(&mut self, value: u32) { self.count = value; } fn incr_event_id(&mut self) { self.seq_num += 1; } fn decr_event_id(&mut self, n: u64) { self.seq_num -= n; } } const EVENT_SIZE: usize = 208; #[zero_copy] #[derive(Debug, bytemuck::Pod, bytemuck::Zeroable)] pub struct AnyEvent { pub event_type: u8, pub padding: [u8; 207], } const_assert_eq!(size_of::(), EVENT_SIZE); #[derive(Copy, Clone, IntoPrimitive, TryFromPrimitive, Eq, PartialEq)] #[repr(u8)] pub enum EventType { Fill, Out, Liquidate, } #[derive( Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable, AnchorSerialize, AnchorDeserialize, )] #[repr(C)] pub struct FillEvent { pub event_type: u8, pub taker_side: u8, // Side, from the taker's POV pub maker_out: u8, // 1 if maker order quantity == 0 pub maker_slot: u8, pub padding: [u8; 4], pub timestamp: u64, pub seq_num: u64, pub maker: Pubkey, pub padding2: [u8; 32], // Timestamp of when the maker order was placed; copied over from the LeafNode pub maker_timestamp: u64, pub taker: Pubkey, pub padding3: [u8; 16], pub taker_client_order_id: u64, pub padding4: [u8; 16], pub price: i64, pub quantity: i64, // number of quote lots pub maker_client_order_id: u64, pub maker_fee: f32, pub taker_fee: f32, pub reserved: [u8; 8], } const_assert_eq!(size_of::() % 8, 0); const_assert_eq!(size_of::(), EVENT_SIZE); impl FillEvent { #[allow(clippy::too_many_arguments)] pub fn new( taker_side: Side, maker_out: bool, maker_slot: u8, timestamp: u64, seq_num: u64, maker: Pubkey, maker_client_order_id: u64, maker_fee: I80F48, maker_timestamp: u64, taker: Pubkey, taker_client_order_id: u64, taker_fee: I80F48, price: i64, quantity: i64, ) -> FillEvent { Self { event_type: EventType::Fill as u8, taker_side: taker_side.into(), maker_out: maker_out.into(), maker_slot, timestamp, seq_num, maker, maker_client_order_id, maker_fee: maker_fee.to_num::(), maker_timestamp, taker, taker_client_order_id, taker_fee: taker_fee.to_num::(), price, quantity, padding: Default::default(), padding2: Default::default(), padding3: Default::default(), padding4: Default::default(), reserved: [0; 8], } } pub fn base_quote_change(&self, side: Side) -> (i64, i64) { match side { Side::Bid => (self.quantity, -self.price * self.quantity), Side::Ask => (-self.quantity, self.price * self.quantity), } } pub fn taker_side(&self) -> Side { self.taker_side.try_into().unwrap() } pub fn maker_out(&self) -> bool { self.maker_out == 1 } } impl TryFrom for FillEvent { type Error = error::Error; fn try_from(e: AnyEvent) -> Result { if e.event_type != EventType::Fill as u8 { Err(error_msg!( "could not convert event with type={} to FillEvent", e.event_type )) } else { Ok(*cast_ref(&e)) } } } impl<'a> TryFrom<&'a AnyEvent> for &'a FillEvent { type Error = error::Error; fn try_from(e: &'a AnyEvent) -> Result { if e.event_type != EventType::Fill as u8 { Err(error_msg!( "could not convert event with type={} to FillEvent", e.event_type )) } else { Ok(cast_ref(e)) } } } #[derive( Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable, AnchorSerialize, AnchorDeserialize, )] #[repr(C)] pub struct OutEvent { pub event_type: u8, pub side: u8, // Side pub owner_slot: u8, padding0: [u8; 5], pub timestamp: u64, pub seq_num: u64, pub owner: Pubkey, pub quantity: i64, padding1: [u8; 144], } const_assert_eq!(size_of::() % 8, 0); const_assert_eq!(size_of::(), EVENT_SIZE); impl OutEvent { pub fn new( side: Side, owner_slot: u8, timestamp: u64, seq_num: u64, owner: Pubkey, quantity: i64, ) -> Self { Self { event_type: EventType::Out.into(), side: side.into(), owner_slot, padding0: [0; 5], timestamp, seq_num, owner, quantity, padding1: [0; EVENT_SIZE - 64], } } pub fn side(&self) -> Side { self.side.try_into().unwrap() } } impl TryFrom for OutEvent { type Error = error::Error; fn try_from(e: AnyEvent) -> Result { if e.event_type != EventType::Out as u8 { Err(error_msg!( "could not convert event with type={} to OutEvent", e.event_type )) } else { Ok(*cast_ref(&e)) } } } impl<'a> TryFrom<&'a AnyEvent> for &'a OutEvent { type Error = error::Error; fn try_from(e: &'a AnyEvent) -> Result { if e.event_type != EventType::Out as u8 { Err(error_msg!( "could not convert event with type={} to OutEvent", e.event_type )) } else { Ok(cast_ref(e)) } } }