Merge pull request #29 from blockworks-foundation/mc/event_queue
add event queue
This commit is contained in:
commit
fde4bce86f
|
@ -5,7 +5,7 @@ use solana_program::pubkey::{Pubkey, PUBKEY_BYTES};
|
|||
use std::str::FromStr;
|
||||
|
||||
pub fn id() -> Pubkey {
|
||||
Pubkey::from_str(&"AddressLookupTab1e1111111111111111111111111").unwrap()
|
||||
Pubkey::from_str("AddressLookupTab1e1111111111111111111111111").unwrap()
|
||||
}
|
||||
|
||||
/// The maximum number of addresses that a lookup table can hold
|
||||
|
@ -17,7 +17,7 @@ pub const LOOKUP_TABLE_META_SIZE: usize = 56;
|
|||
pub const LOOKUP_TABLE_MAX_ACCOUNT_SIZE: usize =
|
||||
LOOKUP_TABLE_META_SIZE + LOOKUP_TABLE_MAX_ADDRESSES * PUBKEY_BYTES;
|
||||
|
||||
pub fn addresses<'a>(table: &[u8]) -> &[Pubkey] {
|
||||
pub fn addresses(table: &[u8]) -> &[Pubkey] {
|
||||
bytemuck::try_cast_slice(&table[LOOKUP_TABLE_META_SIZE..]).unwrap()
|
||||
}
|
||||
|
||||
|
|
|
@ -30,6 +30,8 @@ pub struct CreatePerpMarket<'info> {
|
|||
#[account(zero)]
|
||||
pub asks: AccountLoader<'info, BookSide>,
|
||||
|
||||
pub event_queue: UncheckedAccount<'info>,
|
||||
|
||||
#[account(mut)]
|
||||
pub payer: Signer<'info>,
|
||||
|
||||
|
@ -50,6 +52,7 @@ pub fn create_perp_market(
|
|||
oracle: ctx.accounts.oracle.key(),
|
||||
bids: ctx.accounts.bids.key(),
|
||||
asks: ctx.accounts.asks.key(),
|
||||
event_queue: ctx.accounts.event_queue.key(),
|
||||
quote_lot_size,
|
||||
base_lot_size,
|
||||
seq_num: 0,
|
||||
|
@ -65,5 +68,7 @@ pub fn create_perp_market(
|
|||
let mut asks = ctx.accounts.asks.load_init()?;
|
||||
asks.book_side_type = BookSideType::Asks;
|
||||
|
||||
// TODO: discriminator on event queue
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use anchor_lang::prelude::*;
|
||||
|
||||
use crate::state::{Book, BookSide, Group, MangoAccount, OrderType, PerpMarket};
|
||||
use crate::state::{Book, BookSide, EventQueue, Group, MangoAccount, OrderType, PerpMarket};
|
||||
|
||||
#[derive(Accounts)]
|
||||
pub struct PlacePerpOrder<'info> {
|
||||
|
@ -25,6 +25,7 @@ pub struct PlacePerpOrder<'info> {
|
|||
pub asks: AccountLoader<'info, BookSide>,
|
||||
#[account(mut)]
|
||||
pub bids: AccountLoader<'info, BookSide>,
|
||||
pub event_queue: UncheckedAccount<'info>,
|
||||
pub oracle: UncheckedAccount<'info>,
|
||||
|
||||
pub owner: Signer<'info>,
|
||||
|
@ -50,6 +51,10 @@ pub fn place_perp_order(
|
|||
let asks = &ctx.accounts.asks.to_account_info();
|
||||
let mut book = Book::load_checked(&bids, &asks, &perp_market)?;
|
||||
|
||||
let event_queue_ai = &ctx.accounts.event_queue.to_account_info();
|
||||
let mut event_queue =
|
||||
EventQueue::load_mut_checked(event_queue_ai, ctx.program_id, &perp_market)?;
|
||||
|
||||
// let oracle_price = oracle_price(&ctx.accounts.oracle.to_account_info())?;
|
||||
|
||||
let now_ts = Clock::get()?.unix_timestamp as u64;
|
||||
|
@ -70,6 +75,7 @@ pub fn place_perp_order(
|
|||
// TODO reduce_only based on event queue
|
||||
|
||||
book.new_bid(
|
||||
&mut event_queue,
|
||||
&mut perp_market,
|
||||
// oracle_price,
|
||||
// &mut account,
|
||||
|
|
|
@ -14,6 +14,6 @@ mod health;
|
|||
mod mango_account;
|
||||
mod mint_info;
|
||||
mod oracle;
|
||||
pub mod orderbook;
|
||||
mod orderbook;
|
||||
mod perp_market;
|
||||
mod serum3_market;
|
||||
|
|
|
@ -4,10 +4,11 @@ use crate::{
|
|||
error::MangoError,
|
||||
state::{
|
||||
orderbook::{bookside::BookSide, nodes::LeafNode},
|
||||
PerpMarket,
|
||||
EventQueue, OutEvent, PerpMarket,
|
||||
},
|
||||
};
|
||||
use anchor_lang::prelude::*;
|
||||
use bytemuck::cast;
|
||||
use fixed::types::I80F48;
|
||||
use fixed_macro::types::I80F48;
|
||||
|
||||
|
@ -339,7 +340,7 @@ impl<'a> Book<'a> {
|
|||
// mango_group: &MangoGroup,
|
||||
// mango_group_pk: &Pubkey,
|
||||
// mango_cache: &MangoCache,
|
||||
// event_queue: &mut EventQueue,
|
||||
event_queue: &mut EventQueue,
|
||||
market: &mut PerpMarket,
|
||||
// oracle_price: I80F48,
|
||||
// mango_account: &mut MangoAccount,
|
||||
|
@ -403,16 +404,16 @@ impl<'a> Book<'a> {
|
|||
// 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::Ask,
|
||||
// best_ask.owner_slot,
|
||||
// now_ts,
|
||||
// event_queue.header.seq_num,
|
||||
// best_ask.owner,
|
||||
// best_ask.quantity,
|
||||
// );
|
||||
// event_queue.push_back(cast(event)).unwrap();
|
||||
// ask_deletes.push(best_ask.key);
|
||||
let event = OutEvent::new(
|
||||
Side::Ask,
|
||||
best_ask.owner_slot,
|
||||
now_ts,
|
||||
event_queue.header.seq_num,
|
||||
best_ask.owner,
|
||||
best_ask.quantity,
|
||||
);
|
||||
event_queue.push_back(cast(event)).unwrap();
|
||||
ask_deletes.push(best_ask.key);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ pub use metadata::*;
|
|||
pub use nodes::*;
|
||||
pub use ob_utils::*;
|
||||
pub use order_type::*;
|
||||
pub use order_type::*;
|
||||
pub use queue::*;
|
||||
|
||||
pub mod book;
|
||||
|
|
|
@ -1,437 +1,439 @@
|
|||
// 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::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 const MAX_NUM_EVENTS: usize = 512;
|
||||
|
||||
// fn head(&self) -> usize;
|
||||
// fn set_head(&mut self, value: usize);
|
||||
// fn count(&self) -> usize;
|
||||
// fn set_count(&mut self, value: usize);
|
||||
pub trait QueueHeader: bytemuck::Pod {
|
||||
type Item: bytemuck::Pod + Copy;
|
||||
|
||||
// fn incr_event_id(&mut self);
|
||||
// fn decr_event_id(&mut self, n: usize);
|
||||
// }
|
||||
fn head(&self) -> usize;
|
||||
fn set_head(&mut self, value: usize);
|
||||
fn count(&self) -> usize;
|
||||
fn set_count(&mut self, value: usize);
|
||||
|
||||
// pub struct Queue<'a, H: QueueHeader> {
|
||||
// pub header: RefMut<'a, H>,
|
||||
// pub buf: RefMut<'a, [H::Item]>,
|
||||
// }
|
||||
fn incr_event_id(&mut self);
|
||||
fn decr_event_id(&mut self, n: usize);
|
||||
}
|
||||
|
||||
// impl<'a, H: QueueHeader> Queue<'a, H> {
|
||||
// pub fn new(header: RefMut<'a, H>, buf: RefMut<'a, [H::Item]>) -> Self {
|
||||
// Self { header, buf }
|
||||
// }
|
||||
pub struct Queue<'a, H: QueueHeader> {
|
||||
pub header: RefMut<'a, H>,
|
||||
pub buf: RefMut<'a, [H::Item]>,
|
||||
}
|
||||
|
||||
// pub fn load_mut(account: &'a AccountInfo) -> Result<Self> {
|
||||
// let (header, buf) = strip_header_mut::<H, H::Item>(account)?;
|
||||
// Ok(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 len(&self) -> usize {
|
||||
// self.header.count()
|
||||
// }
|
||||
pub fn load_mut(account: &'a AccountInfo) -> Result<Self> {
|
||||
let (header, buf) = strip_header_mut::<H, H::Item>(account)?;
|
||||
Ok(Self { header, buf })
|
||||
}
|
||||
|
||||
// pub fn full(&self) -> bool {
|
||||
// self.header.count() == self.buf.len()
|
||||
// }
|
||||
pub fn len(&self) -> usize {
|
||||
self.header.count()
|
||||
}
|
||||
|
||||
// pub fn empty(&self) -> bool {
|
||||
// self.header.count() == 0
|
||||
// }
|
||||
pub fn full(&self) -> bool {
|
||||
self.header.count() == self.buf.len()
|
||||
}
|
||||
|
||||
// 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 empty(&self) -> bool {
|
||||
self.header.count() == 0
|
||||
}
|
||||
|
||||
// let count = self.header.count();
|
||||
// self.header.set_count(count + 1);
|
||||
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;
|
||||
|
||||
// self.header.incr_event_id();
|
||||
// Ok(())
|
||||
// }
|
||||
let count = self.header.count();
|
||||
self.header.set_count(count + 1);
|
||||
|
||||
// pub fn peek_front(&self) -> Option<&H::Item> {
|
||||
// if self.empty() {
|
||||
// return None;
|
||||
// }
|
||||
// Some(&self.buf[self.header.head()])
|
||||
// }
|
||||
self.header.incr_event_id();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// 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(&self) -> Option<&H::Item> {
|
||||
if self.empty() {
|
||||
return None;
|
||||
}
|
||||
Some(&self.buf[self.header.head()])
|
||||
}
|
||||
|
||||
// pub fn pop_front(&mut self) -> std::result::Result<H::Item, ()> {
|
||||
// if self.empty() {
|
||||
// return Err(());
|
||||
// }
|
||||
// let value = 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()])
|
||||
}
|
||||
|
||||
// let count = self.header.count();
|
||||
// self.header.set_count(count - 1);
|
||||
pub fn pop_front(&mut self) -> std::result::Result<H::Item, ()> {
|
||||
if self.empty() {
|
||||
return Err(());
|
||||
}
|
||||
let value = self.buf[self.header.head()];
|
||||
|
||||
// let head = self.header.head();
|
||||
// self.header.set_head((head + 1) % self.buf.len());
|
||||
let count = self.header.count();
|
||||
self.header.set_count(count - 1);
|
||||
|
||||
// Ok(value)
|
||||
// }
|
||||
let head = self.header.head();
|
||||
self.header.set_head((head + 1) % self.buf.len());
|
||||
|
||||
// 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(())
|
||||
// }
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
// pub fn iter(&self) -> impl Iterator<Item = &H::Item> {
|
||||
// QueueIterator {
|
||||
// queue: self,
|
||||
// index: 0,
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
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(())
|
||||
}
|
||||
|
||||
// struct QueueIterator<'a, 'b, H: QueueHeader> {
|
||||
// queue: &'b Queue<'a, H>,
|
||||
// index: usize,
|
||||
// }
|
||||
pub fn iter(&self) -> impl Iterator<Item = &H::Item> {
|
||||
QueueIterator {
|
||||
queue: self,
|
||||
index: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// impl<'a, 'b, H: QueueHeader> Iterator for QueueIterator<'a, 'b, H> {
|
||||
// type Item = &'b H::Item;
|
||||
// fn next(&mut self) -> Option<Self::Item> {
|
||||
// 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)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
struct QueueIterator<'a, 'b, H: QueueHeader> {
|
||||
queue: &'b Queue<'a, H>,
|
||||
index: usize,
|
||||
}
|
||||
|
||||
// #[account(zero_copy)]
|
||||
// pub struct EventQueueHeader {
|
||||
// pub meta_data: MetaData,
|
||||
// head: usize,
|
||||
// count: usize,
|
||||
// pub seq_num: usize,
|
||||
// }
|
||||
// // unsafe impl TriviallyTransmutable for EventQueueHeader {}
|
||||
impl<'a, 'b, H: QueueHeader> Iterator for QueueIterator<'a, 'b, H> {
|
||||
type Item = &'b H::Item;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
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 QueueHeader for EventQueueHeader {
|
||||
// type Item = AnyEvent;
|
||||
#[account(zero_copy)]
|
||||
pub struct EventQueueHeader {
|
||||
pub meta_data: MetaData,
|
||||
head: usize,
|
||||
count: usize,
|
||||
pub seq_num: usize,
|
||||
}
|
||||
// unsafe impl TriviallyTransmutable for EventQueueHeader {}
|
||||
|
||||
// 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;
|
||||
// }
|
||||
// }
|
||||
impl QueueHeader for EventQueueHeader {
|
||||
type Item = AnyEvent;
|
||||
|
||||
// pub type EventQueue<'a> = Queue<'a, EventQueueHeader>;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// impl<'a> EventQueue<'a> {
|
||||
// pub fn load_mut_checked(
|
||||
// account: &'a AccountInfo,
|
||||
// program_id: &Pubkey,
|
||||
// perp_market: &PerpMarket,
|
||||
// ) -> Result<Self> {
|
||||
// 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 type EventQueue<'a> = Queue<'a, EventQueueHeader>;
|
||||
|
||||
// pub fn load_and_init(
|
||||
// account: &'a AccountInfo,
|
||||
// program_id: &Pubkey,
|
||||
// rent: &Rent,
|
||||
// ) -> Result<Self> {
|
||||
// // NOTE: check this first so we can borrow account later
|
||||
// require!(
|
||||
// rent.is_exempt(account.lamports(), account.data_len()),
|
||||
// MangoError::SomeError
|
||||
// ); //MangoErrorCode::AccountNotRentExempt
|
||||
impl<'a> EventQueue<'a> {
|
||||
pub fn load_mut_checked(
|
||||
account: &'a AccountInfo,
|
||||
program_id: &Pubkey,
|
||||
perp_market: &PerpMarket,
|
||||
) -> Result<Self> {
|
||||
require!(account.owner == program_id, MangoError::SomeError); // InvalidOwner
|
||||
require!(
|
||||
&perp_market.event_queue == account.key,
|
||||
MangoError::SomeError
|
||||
); //InvalidAccount
|
||||
Self::load_mut(account)
|
||||
}
|
||||
|
||||
// let mut state = Self::load_mut(account)?;
|
||||
// require!(account.owner == program_id, MangoError::SomeError); // MangoErrorCode::InvalidOwner
|
||||
pub fn load_and_init(
|
||||
account: &'a AccountInfo,
|
||||
program_id: &Pubkey,
|
||||
rent: &Rent,
|
||||
) -> Result<Self> {
|
||||
// NOTE: check this first so we can borrow account later
|
||||
require!(
|
||||
rent.is_exempt(account.lamports(), account.data_len()),
|
||||
MangoError::SomeError
|
||||
); //MangoErrorCode::AccountNotRentExempt
|
||||
|
||||
// // require!(
|
||||
// // !state.header.meta_data.is_initialized,
|
||||
// // MangoError::SomeError
|
||||
// // );
|
||||
// // state.header.meta_data = MetaData::new(DataType::EventQueue, 0, true);
|
||||
let state = Self::load_mut(account)?;
|
||||
require!(account.owner == program_id, MangoError::SomeError); // MangoErrorCode::InvalidOwner
|
||||
|
||||
// Ok(state)
|
||||
// }
|
||||
// }
|
||||
// require!(
|
||||
// !state.header.meta_data.is_initialized,
|
||||
// MangoError::SomeError
|
||||
// );
|
||||
// state.header.meta_data = MetaData::new(DataType::EventQueue, 0, true);
|
||||
|
||||
// #[derive(Copy, Clone, IntoPrimitive, TryFromPrimitive, Eq, PartialEq)]
|
||||
// #[repr(u8)]
|
||||
// pub enum EventType {
|
||||
// Fill,
|
||||
// Out,
|
||||
// Liquidate,
|
||||
// }
|
||||
Ok(state)
|
||||
}
|
||||
}
|
||||
|
||||
// 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, IntoPrimitive, TryFromPrimitive, Eq, PartialEq)]
|
||||
#[repr(u8)]
|
||||
pub enum EventType {
|
||||
Fill,
|
||||
Out,
|
||||
Liquidate,
|
||||
}
|
||||
|
||||
// #[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
|
||||
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 {}
|
||||
|
||||
// pub maker: Pubkey,
|
||||
// pub maker_order_id: i128,
|
||||
// pub maker_client_order_id: u64,
|
||||
// pub maker_fee: I80F48,
|
||||
#[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
|
||||
|
||||
// // The best bid/ask at the time the maker order was placed. Used for liquidity incentives
|
||||
// pub best_initial: i64,
|
||||
pub maker: Pubkey,
|
||||
pub maker_order_id: i128,
|
||||
pub maker_client_order_id: u64,
|
||||
pub maker_fee: I80F48,
|
||||
|
||||
// // Timestamp of when the maker order was placed; copied over from the LeafNode
|
||||
// pub maker_timestamp: u64,
|
||||
// The best bid/ask at the time the maker order was placed. Used for liquidity incentives
|
||||
pub best_initial: i64,
|
||||
|
||||
// pub taker: Pubkey,
|
||||
// pub taker_order_id: i128,
|
||||
// pub taker_client_order_id: u64,
|
||||
// pub taker_fee: I80F48,
|
||||
// Timestamp of when the maker order was placed; copied over from the LeafNode
|
||||
pub maker_timestamp: u64,
|
||||
|
||||
// pub price: i64,
|
||||
// pub quantity: i64, // number of quote lots
|
||||
// }
|
||||
// // unsafe impl TriviallyTransmutable for FillEvent {}
|
||||
pub taker: Pubkey,
|
||||
pub taker_order_id: i128,
|
||||
pub taker_client_order_id: u64,
|
||||
pub taker_fee: I80F48,
|
||||
|
||||
// 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,
|
||||
pub price: i64,
|
||||
pub quantity: i64, // number of quote lots
|
||||
}
|
||||
// unsafe impl TriviallyTransmutable for FillEvent {}
|
||||
|
||||
// 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,
|
||||
// }
|
||||
// }
|
||||
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,
|
||||
|
||||
// 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(),
|
||||
// ),
|
||||
// }
|
||||
// }
|
||||
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 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 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(),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
// #[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],
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// 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)]
|
||||
// /// 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::<AnyEvent>(), size_of::<FillEvent>());
|
||||
// const_assert_eq!(size_of::<AnyEvent>(), size_of::<OutEvent>());
|
||||
// const_assert_eq!(size_of::<AnyEvent>(), size_of::<LiquidateEvent>());
|
||||
#[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],
|
||||
}
|
||||
|
||||
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::<AnyEvent>(), size_of::<FillEvent>());
|
||||
const_assert_eq!(size_of::<AnyEvent>(), size_of::<OutEvent>());
|
||||
const_assert_eq!(size_of::<AnyEvent>(), size_of::<LiquidateEvent>());
|
||||
|
|
|
@ -5,9 +5,6 @@ use crate::state::TokenIndex;
|
|||
|
||||
pub type PerpMarketIndex = u16;
|
||||
|
||||
#[account(zero_copy)]
|
||||
pub struct EventQueue {}
|
||||
|
||||
#[account(zero_copy)]
|
||||
pub struct PerpMarket {
|
||||
pub group: Pubkey,
|
||||
|
@ -17,8 +14,7 @@ pub struct PerpMarket {
|
|||
pub bids: Pubkey,
|
||||
pub asks: Pubkey,
|
||||
|
||||
/// Event queue of TODO
|
||||
/// pub event_queue: Pubkey,
|
||||
pub event_queue: Pubkey,
|
||||
|
||||
/// Number of quote native that reresents min tick
|
||||
/// e.g. when base lot size is 100, and quote lot size is 10, then tick i.e. price increment is 10/100 i.e. 0.1
|
||||
|
|
|
@ -1086,6 +1086,7 @@ pub struct CreatePerpMarketInstruction<'keypair> {
|
|||
pub oracle: Pubkey,
|
||||
pub asks: Pubkey,
|
||||
pub bids: Pubkey,
|
||||
pub event_queue: Pubkey,
|
||||
pub payer: &'keypair Keypair,
|
||||
pub perp_market_index: PerpMarketIndex,
|
||||
pub base_token_index: TokenIndex,
|
||||
|
@ -1127,6 +1128,7 @@ impl<'keypair> ClientInstruction for CreatePerpMarketInstruction<'keypair> {
|
|||
perp_market,
|
||||
asks: self.asks,
|
||||
bids: self.bids,
|
||||
event_queue: self.event_queue,
|
||||
payer: self.payer.pubkey(),
|
||||
system_program: System::id(),
|
||||
};
|
||||
|
@ -1146,6 +1148,7 @@ pub struct PlacePerpOrderInstruction<'keypair> {
|
|||
pub perp_market: Pubkey,
|
||||
pub asks: Pubkey,
|
||||
pub bids: Pubkey,
|
||||
pub event_queue: Pubkey,
|
||||
pub oracle: Pubkey,
|
||||
pub owner: &'keypair Keypair,
|
||||
}
|
||||
|
@ -1173,6 +1176,7 @@ impl<'keypair> ClientInstruction for PlacePerpOrderInstruction<'keypair> {
|
|||
perp_market: self.perp_market,
|
||||
asks: self.asks,
|
||||
bids: self.bids,
|
||||
event_queue: self.event_queue,
|
||||
oracle: self.oracle,
|
||||
owner: self.owner.pubkey(),
|
||||
};
|
||||
|
|
|
@ -83,7 +83,23 @@ impl SolanaCookie {
|
|||
.newest()
|
||||
}
|
||||
|
||||
pub async fn create_account<T>(&self, owner: &Pubkey) -> Pubkey {
|
||||
pub async fn create_account_from_len(&self, owner: &Pubkey, len: usize) -> Pubkey {
|
||||
let key = Keypair::new();
|
||||
let rent = self.rent.minimum_balance(len);
|
||||
let create_account_instr = solana_sdk::system_instruction::create_account(
|
||||
&self.context.borrow().payer.pubkey(),
|
||||
&key.pubkey(),
|
||||
rent,
|
||||
len as u64,
|
||||
&owner,
|
||||
);
|
||||
self.process_transaction(&[create_account_instr], Some(&[&key]))
|
||||
.await
|
||||
.unwrap();
|
||||
key.pubkey()
|
||||
}
|
||||
|
||||
pub async fn create_account_for_type<T>(&self, owner: &Pubkey) -> Pubkey {
|
||||
let key = Keypair::new();
|
||||
let len = 8 + std::mem::size_of::<T>();
|
||||
let rent = self.rent.minimum_balance(len);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#![cfg(feature = "test-bpf")]
|
||||
|
||||
use mango_v4::state::BookSide;
|
||||
use mango_v4::state::*;
|
||||
use solana_program_test::*;
|
||||
use solana_sdk::{signature::Keypair, transport::TransportError};
|
||||
|
||||
|
@ -82,6 +82,7 @@ async fn test_perp() -> Result<(), TransportError> {
|
|||
perp_market,
|
||||
asks,
|
||||
bids,
|
||||
event_queue,
|
||||
..
|
||||
} = send_tx(
|
||||
solana,
|
||||
|
@ -90,12 +91,20 @@ async fn test_perp() -> Result<(), TransportError> {
|
|||
oracle: tokens[0].oracle,
|
||||
asks: context
|
||||
.solana
|
||||
.create_account::<BookSide>(&mango_v4::id())
|
||||
.create_account_for_type::<BookSide>(&mango_v4::id())
|
||||
.await,
|
||||
bids: context
|
||||
.solana
|
||||
.create_account::<BookSide>(&mango_v4::id())
|
||||
.create_account_for_type::<BookSide>(&mango_v4::id())
|
||||
.await,
|
||||
event_queue: {
|
||||
let len = std::mem::size_of::<queue::EventQueue>()
|
||||
+ std::mem::size_of::<AnyEvent>() * MAX_NUM_EVENTS;
|
||||
context
|
||||
.solana
|
||||
.create_account_from_len(&mango_v4::id(), len)
|
||||
.await
|
||||
},
|
||||
admin,
|
||||
payer,
|
||||
perp_market_index: 0,
|
||||
|
@ -117,6 +126,7 @@ async fn test_perp() -> Result<(), TransportError> {
|
|||
perp_market,
|
||||
asks,
|
||||
bids,
|
||||
event_queue,
|
||||
oracle: tokens[0].oracle,
|
||||
owner,
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue