Merge branch 'main' into mc/clippy

This commit is contained in:
microwavedcola1 2022-03-23 09:53:50 +01:00
commit 5919a5a1df
19 changed files with 662 additions and 469 deletions

View File

@ -30,6 +30,8 @@ pub struct CreatePerpMarket<'info> {
#[account(zero)] #[account(zero)]
pub asks: AccountLoader<'info, BookSide>, pub asks: AccountLoader<'info, BookSide>,
pub event_queue: UncheckedAccount<'info>,
#[account(mut)] #[account(mut)]
pub payer: Signer<'info>, pub payer: Signer<'info>,
@ -50,6 +52,7 @@ pub fn create_perp_market(
oracle: ctx.accounts.oracle.key(), oracle: ctx.accounts.oracle.key(),
bids: ctx.accounts.bids.key(), bids: ctx.accounts.bids.key(),
asks: ctx.accounts.asks.key(), asks: ctx.accounts.asks.key(),
event_queue: ctx.accounts.event_queue.key(),
quote_lot_size, quote_lot_size,
base_lot_size, base_lot_size,
seq_num: 0, seq_num: 0,
@ -65,5 +68,7 @@ pub fn create_perp_market(
let mut asks = ctx.accounts.asks.load_init()?; let mut asks = ctx.accounts.asks.load_init()?;
asks.book_side_type = BookSideType::Asks; asks.book_side_type = BookSideType::Asks;
// TODO: discriminator on event queue
Ok(()) Ok(())
} }

View File

@ -70,7 +70,7 @@ pub fn deposit(ctx: Context<Deposit>, amount: u64) -> Result<()> {
// TODO: This will be used to disable is_bankrupt or being_liquidated // TODO: This will be used to disable is_bankrupt or being_liquidated
// when health recovers sufficiently // when health recovers sufficiently
// //
let health = compute_health(&account, ctx.remaining_accounts)?; let health = compute_health_from_fixed_accounts(&account, ctx.remaining_accounts)?;
msg!("health: {}", health); msg!("health: {}", health);
// //

View File

@ -0,0 +1,49 @@
use anchor_lang::prelude::*;
use crate::state::*;
#[derive(Accounts)]
pub struct LiqTokenWithToken<'info> {
pub group: AccountLoader<'info, Group>,
#[account(
mut,
has_one = group,
constraint = liqor.load()?.owner == liqor_owner.key(),
)]
pub liqor: AccountLoader<'info, MangoAccount>,
pub liqor_owner: Signer<'info>,
#[account(
mut,
has_one = group,
)]
pub liqee: AccountLoader<'info, MangoAccount>,
// TODO: these banks are also passed in remainingAccounts
#[account(
mut,
has_one = group,
constraint = asset_bank.load()?.token_index != liab_bank.load()?.token_index,
)]
pub asset_bank: AccountLoader<'info, Bank>,
#[account(
mut,
has_one = group,
)]
pub liab_bank: AccountLoader<'info, Bank>,
}
pub fn liq_token_with_token(ctx: Context<LiqTokenWithToken>) -> Result<()> {
//
// Health computation
//
let liqee = ctx.accounts.liqee.load()?;
let health = compute_health_by_scanning_accounts(&liqee, ctx.remaining_accounts)?;
msg!("health: {}", health);
// TODO: everything
Ok(())
}

View File

@ -1,5 +1,5 @@
use crate::error::MangoError; use crate::error::MangoError;
use crate::state::{compute_health, Bank, Group, MangoAccount}; use crate::state::{compute_health_from_fixed_accounts, Bank, Group, MangoAccount};
use crate::{group_seeds, Mango}; use crate::{group_seeds, Mango};
use anchor_lang::prelude::*; use anchor_lang::prelude::*;
use anchor_spl::token::TokenAccount; use anchor_spl::token::TokenAccount;
@ -94,7 +94,7 @@ pub fn margin_trade<'key, 'accounts, 'remaining, 'info>(
} }
// compute pre cpi health // compute pre cpi health
let pre_cpi_health = compute_health(&account, health_ais)?; let pre_cpi_health = compute_health_from_fixed_accounts(&account, health_ais)?;
require!(pre_cpi_health > 0, MangoError::HealthMustBePositive); require!(pre_cpi_health > 0, MangoError::HealthMustBePositive);
msg!("pre_cpi_health {:?}", pre_cpi_health); msg!("pre_cpi_health {:?}", pre_cpi_health);
@ -118,7 +118,7 @@ pub fn margin_trade<'key, 'accounts, 'remaining, 'info>(
// compute post cpi health // compute post cpi health
// todo: this is not working, the health is computed on old bank state and not taking into account // todo: this is not working, the health is computed on old bank state and not taking into account
// withdraws done in adjust_for_post_cpi_token_amounts // withdraws done in adjust_for_post_cpi_token_amounts
let post_cpi_health = compute_health(&account, health_ais)?; let post_cpi_health = compute_health_from_fixed_accounts(&account, health_ais)?;
require!(post_cpi_health > 0, MangoError::HealthMustBePositive); require!(post_cpi_health > 0, MangoError::HealthMustBePositive);
msg!("post_cpi_health {:?}", post_cpi_health); msg!("post_cpi_health {:?}", post_cpi_health);

View File

@ -4,6 +4,7 @@ pub use create_group::*;
pub use create_perp_market::*; pub use create_perp_market::*;
pub use create_stub_oracle::*; pub use create_stub_oracle::*;
pub use deposit::*; pub use deposit::*;
pub use liq_token_with_token::*;
pub use place_perp_order::*; pub use place_perp_order::*;
pub use register_token::*; pub use register_token::*;
pub use serum3_cancel_order::*; pub use serum3_cancel_order::*;
@ -20,6 +21,7 @@ mod create_group;
mod create_perp_market; mod create_perp_market;
mod create_stub_oracle; mod create_stub_oracle;
mod deposit; mod deposit;
mod liq_token_with_token;
mod margin_trade; mod margin_trade;
mod place_perp_order; mod place_perp_order;
mod register_token; mod register_token;

View File

@ -1,6 +1,6 @@
use anchor_lang::prelude::*; 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)] #[derive(Accounts)]
pub struct PlacePerpOrder<'info> { pub struct PlacePerpOrder<'info> {
@ -25,6 +25,7 @@ pub struct PlacePerpOrder<'info> {
pub asks: AccountLoader<'info, BookSide>, pub asks: AccountLoader<'info, BookSide>,
#[account(mut)] #[account(mut)]
pub bids: AccountLoader<'info, BookSide>, pub bids: AccountLoader<'info, BookSide>,
pub event_queue: UncheckedAccount<'info>,
pub oracle: UncheckedAccount<'info>, pub oracle: UncheckedAccount<'info>,
pub owner: Signer<'info>, pub owner: Signer<'info>,
@ -50,6 +51,10 @@ pub fn place_perp_order(
let asks = &ctx.accounts.asks.to_account_info(); let asks = &ctx.accounts.asks.to_account_info();
let mut book = Book::load_checked(bids, asks, &perp_market)?; 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 oracle_price = oracle_price(&ctx.accounts.oracle.to_account_info())?;
let now_ts = Clock::get()?.unix_timestamp as u64; 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 // TODO reduce_only based on event queue
book.new_bid( book.new_bid(
&mut event_queue,
&mut perp_market, &mut perp_market,
// oracle_price, // oracle_price,
// &mut account, // &mut account,

View File

@ -101,7 +101,7 @@ pub fn serum3_liq_force_cancel_orders(
// TODO: do the correct health / being_liquidated check // TODO: do the correct health / being_liquidated check
{ {
let account = ctx.accounts.account.load()?; let account = ctx.accounts.account.load()?;
let health = compute_health(&account, ctx.remaining_accounts)?; let health = compute_health_from_fixed_accounts(&account, ctx.remaining_accounts)?;
msg!("health: {}", health); msg!("health: {}", health);
require!(health < 0, MangoError::SomeError); require!(health < 0, MangoError::SomeError);
} }

View File

@ -234,7 +234,7 @@ pub fn serum3_place_order(
// Health check // Health check
// //
let account = ctx.accounts.account.load()?; let account = ctx.accounts.account.load()?;
let health = compute_health(&account, ctx.remaining_accounts)?; let health = compute_health_from_fixed_accounts(&account, &ctx.remaining_accounts)?;
msg!("health: {}", health); msg!("health: {}", health);
require!(health >= 0, MangoError::SomeError); require!(health >= 0, MangoError::SomeError);

View File

@ -99,7 +99,7 @@ pub fn withdraw(ctx: Context<Withdraw>, amount: u64, allow_borrow: bool) -> Resu
// //
// Health check // Health check
// //
let health = compute_health(&account, ctx.remaining_accounts)?; let health = compute_health_from_fixed_accounts(&account, &ctx.remaining_accounts)?;
msg!("health: {}", health); msg!("health: {}", health);
require!(health >= 0, MangoError::SomeError); require!(health >= 0, MangoError::SomeError);

View File

@ -1,25 +1,152 @@
use anchor_lang::prelude::*; use anchor_lang::prelude::*;
use fixed::types::I80F48; use fixed::types::I80F48;
use serum_dex::state::OpenOrders;
use std::cell::Ref; use std::cell::Ref;
use crate::error::MangoError; use crate::error::MangoError;
use crate::serum3_cpi; use crate::serum3_cpi;
use crate::state::{oracle_price, Bank, MangoAccount}; use crate::state::{oracle_price, Bank, MangoAccount, TokenIndex};
use crate::util;
use crate::util::checked_math as cm; use crate::util::checked_math as cm;
use crate::util::LoadZeroCopy; use crate::util::LoadZeroCopy;
pub fn compute_health(account: &MangoAccount, ais: &[AccountInfo]) -> Result<I80F48> { /// This trait abstracts how to find accounts needed for the health computation.
///
/// There are different ways they are retrieved from remainingAccounts, based
/// on the instruction:
/// - FixedOrderAccountRetriever requires the remainingAccounts to be in a well
/// defined order and is the fastest. It's used where possible.
/// - ScanningAccountRetriever does a linear scan for each account it needs.
/// It needs more compute, but works when a union of bank/oracle/market accounts
/// are passed because health needs to be computed for different baskets in
/// one instruction (such as for liquidation instructions).
trait AccountRetriever<'a, 'b> {
fn bank_and_oracle(
&self,
group: &Pubkey,
account_index: usize,
token_index: TokenIndex,
) -> Result<(Ref<'a, Bank>, &'a AccountInfo<'b>)>;
fn serum_oo(&self, account_index: usize, key: &Pubkey) -> Result<Ref<'a, OpenOrders>>;
}
/// Assumes the account infos needed for the health computation follow a strict order.
///
/// 1. n_banks Bank account, in the order of account.token_account_map.iter_active()
/// 2. n_banks oracle accounts, one for each bank in the same order
/// 3. serum3 OpenOrders accounts, in the order of account.serum3_account_map.iter_active()
struct FixedOrderAccountRetriever<'a, 'b> {
ais: &'a [AccountInfo<'b>],
n_banks: usize,
}
impl<'a, 'b> AccountRetriever<'a, 'b> for FixedOrderAccountRetriever<'a, 'b> {
fn bank_and_oracle(
&self,
group: &Pubkey,
account_index: usize,
token_index: TokenIndex,
) -> Result<(Ref<'a, Bank>, &'a AccountInfo<'b>)> {
let bank = self.ais[account_index].load::<Bank>()?;
require!(&bank.group == group, MangoError::SomeError);
require!(bank.token_index == token_index, MangoError::SomeError);
let oracle = &self.ais[self.n_banks + account_index];
require!(&bank.oracle == oracle.key, MangoError::SomeError);
Ok((bank, oracle))
}
fn serum_oo(&self, account_index: usize, key: &Pubkey) -> Result<Ref<'a, OpenOrders>> {
let ai = &self.ais[2 * self.n_banks + account_index];
require!(key == ai.key, MangoError::SomeError);
serum3_cpi::load_open_orders(ai)
}
}
/// Takes a list of account infos containing
/// - an unknown number of Banks in any order, followed by
/// - the same number of oracles in the same order as the banks, followed by
/// - an unknown number of serum3 OpenOrders accounts
/// and retrieves accounts needed for the health computation by doing a linear
/// scan for each request.
struct ScanningAccountRetriever<'a, 'b> {
ais: &'a [AccountInfo<'b>],
banks: Vec<Ref<'a, Bank>>,
}
impl<'a, 'b> ScanningAccountRetriever<'a, 'b> {
fn new(ais: &'a [AccountInfo<'b>]) -> Result<Self> {
let mut banks = vec![];
for ai in ais.iter() {
match ai.load::<Bank>() {
Ok(bank) => banks.push(bank),
Err(Error::AnchorError(error))
if error.error_code_number
== ErrorCode::AccountDiscriminatorMismatch as u32 =>
{
break;
}
Err(error) => return Err(error),
};
}
Ok(Self { ais, banks })
}
fn n_banks(&self) -> usize {
self.banks.len()
}
}
impl<'a, 'b> AccountRetriever<'a, 'b> for ScanningAccountRetriever<'a, 'b> {
fn bank_and_oracle(
&self,
group: &Pubkey,
_account_index: usize,
token_index: TokenIndex,
) -> Result<(Ref<'a, Bank>, &'a AccountInfo<'b>)> {
let (i, bank) = self
.banks
.iter()
.enumerate()
.find(|(_, b)| b.token_index == token_index)
.unwrap();
require!(&bank.group == group, MangoError::SomeError);
let oracle = &self.ais[self.n_banks() + i];
require!(&bank.oracle == oracle.key, MangoError::SomeError);
Ok((Ref::clone(bank), oracle))
}
fn serum_oo(&self, _account_index: usize, key: &Pubkey) -> Result<Ref<'a, OpenOrders>> {
let oo = self.ais[2 * self.n_banks()..]
.iter()
.find(|ai| ai.key == key)
.unwrap();
serum3_cpi::load_open_orders(oo)
}
}
pub fn compute_health_from_fixed_accounts<'a, 'b>(
account: &MangoAccount,
ais: &'a [AccountInfo<'b>],
) -> Result<I80F48> {
let active_token_len = account.token_account_map.iter_active().count(); let active_token_len = account.token_account_map.iter_active().count();
let active_serum_len = account.serum3_account_map.iter_active().count(); let active_serum_len = account.serum3_account_map.iter_active().count();
let expected_ais = active_token_len * 2 // banks + oracles let expected_ais = active_token_len * 2 // banks + oracles
+ active_serum_len; // open_orders + active_serum_len; // open_orders
require!(ais.len() == expected_ais, MangoError::SomeError); require!(ais.len() == expected_ais, MangoError::SomeError);
let banks = &ais[0..active_token_len];
let oracles = &ais[active_token_len..active_token_len * 2];
let serum_oos = &ais[active_token_len * 2..];
compute_health_detail(account, banks, oracles, serum_oos) let retriever = FixedOrderAccountRetriever {
ais,
n_banks: active_token_len,
};
compute_health_detail(account, retriever)
}
pub fn compute_health_by_scanning_accounts<'a, 'b>(
account: &MangoAccount,
ais: &'a [AccountInfo<'b>],
) -> Result<I80F48> {
let retriever = ScanningAccountRetriever::new(ais)?;
compute_health_detail(account, retriever)
} }
struct TokenInfo<'a> { struct TokenInfo<'a> {
@ -84,57 +211,34 @@ fn pair_health(
Ok(cm!(health1 + health2)) Ok(cm!(health1 + health2))
} }
fn compute_health_detail( fn compute_health_detail<'a, 'b: 'a>(
account: &MangoAccount, account: &MangoAccount,
banks: &[AccountInfo], retriever: impl AccountRetriever<'a, 'b>,
oracles: &[AccountInfo],
serum_oos: &[AccountInfo],
) -> Result<I80F48> { ) -> Result<I80F48> {
// collect the bank and oracle data once
let mut token_infos = util::zip!(banks.iter(), oracles.iter())
.map(|(bank_ai, oracle_ai)| {
let bank = bank_ai.load::<Bank>()?;
require!(bank.group == account.group, MangoError::SomeError);
require!(bank.oracle == oracle_ai.key(), MangoError::UnexpectedOracle);
let oracle_price = oracle_price(oracle_ai)?;
Ok(TokenInfo {
bank,
oracle_price,
balance: I80F48::ZERO,
price_asset_cache: I80F48::ZERO,
price_liab_cache: I80F48::ZERO,
price_inv_cache: I80F48::ZERO,
})
})
.collect::<Result<Vec<_>>>()?;
// token contribution from token accounts // token contribution from token accounts
for (position, token_info) in util::zip!( let mut token_infos = vec![];
account.token_account_map.iter_active(), for (i, position) in account.token_account_map.iter_active().enumerate() {
token_infos.iter_mut() let (bank, oracle_ai) =
) { retriever.bank_and_oracle(&account.group, i, position.token_index)?;
let bank = &token_info.bank; let oracle_price = oracle_price(oracle_ai)?;
// This assumes banks are passed in order
require!(
bank.token_index == position.token_index,
MangoError::SomeError
);
// converts the token value to the basis token value for health computations // converts the token value to the basis token value for health computations
// TODO: health basis token == USDC? // TODO: health basis token == USDC?
let native = position.native(bank); let native = position.native(&bank);
token_info.balance = cm!(token_info.balance + native);
token_infos.push(TokenInfo {
bank,
oracle_price,
balance: native,
price_asset_cache: I80F48::ZERO,
price_liab_cache: I80F48::ZERO,
price_inv_cache: I80F48::ZERO,
});
} }
// token contribution from serum accounts // token contribution from serum accounts
for (serum_account, oo_ai) in for (i, serum_account) in account.serum3_account_map.iter_active().enumerate() {
util::zip!(account.serum3_account_map.iter_active(), serum_oos.iter()) let oo = retriever.serum_oo(i, &serum_account.open_orders)?;
{
// This assumes serum open orders are passed in order
require!(
&serum_account.open_orders == oo_ai.key,
MangoError::SomeError
);
// find the TokenInfos for the market's base and quote tokens // find the TokenInfos for the market's base and quote tokens
let base_index = token_infos let base_index = token_infos
@ -153,8 +257,6 @@ fn compute_health_detail(
(&mut r[0], &mut l[quote_index]) (&mut r[0], &mut l[quote_index])
}; };
let oo = serum3_cpi::load_open_orders(oo_ai)?;
// add the amounts that are freely settleable // add the amounts that are freely settleable
let base_free = I80F48::from_num(oo.native_coin_free); let base_free = I80F48::from_num(oo.native_coin_free);
let quote_free = I80F48::from_num(cm!(oo.native_pc_free + oo.referrer_rebates_accrued)); let quote_free = I80F48::from_num(cm!(oo.native_pc_free + oo.referrer_rebates_accrued));

View File

@ -14,6 +14,6 @@ mod health;
mod mango_account; mod mango_account;
mod mint_info; mod mint_info;
mod oracle; mod oracle;
pub mod orderbook; mod orderbook;
mod perp_market; mod perp_market;
mod serum3_market; mod serum3_market;

View File

@ -4,10 +4,11 @@ use crate::{
error::MangoError, error::MangoError,
state::{ state::{
orderbook::{bookside::BookSide, nodes::LeafNode}, orderbook::{bookside::BookSide, nodes::LeafNode},
PerpMarket, EventQueue, OutEvent, PerpMarket,
}, },
}; };
use anchor_lang::prelude::*; use anchor_lang::prelude::*;
use bytemuck::cast;
use fixed::types::I80F48; use fixed::types::I80F48;
use fixed_macro::types::I80F48; use fixed_macro::types::I80F48;
@ -340,7 +341,7 @@ impl<'a> Book<'a> {
// mango_group: &MangoGroup, // mango_group: &MangoGroup,
// mango_group_pk: &Pubkey, // mango_group_pk: &Pubkey,
// mango_cache: &MangoCache, // mango_cache: &MangoCache,
// event_queue: &mut EventQueue, event_queue: &mut EventQueue,
market: &mut PerpMarket, market: &mut PerpMarket,
// oracle_price: I80F48, // oracle_price: I80F48,
// mango_account: &mut MangoAccount, // mango_account: &mut MangoAccount,
@ -404,16 +405,16 @@ impl<'a> Book<'a> {
// Remove the order from the book unless we've done that enough // Remove the order from the book unless we've done that enough
if number_of_dropped_expired_orders < DROP_EXPIRED_ORDER_LIMIT { if number_of_dropped_expired_orders < DROP_EXPIRED_ORDER_LIMIT {
number_of_dropped_expired_orders += 1; number_of_dropped_expired_orders += 1;
// let event = OutEvent::new( let event = OutEvent::new(
// Side::Ask, Side::Ask,
// best_ask.owner_slot, best_ask.owner_slot,
// now_ts, now_ts,
// event_queue.header.seq_num, event_queue.header.seq_num,
// best_ask.owner, best_ask.owner,
// best_ask.quantity, best_ask.quantity,
// ); );
// event_queue.push_back(cast(event)).unwrap(); event_queue.push_back(cast(event)).unwrap();
// ask_deletes.push(best_ask.key); ask_deletes.push(best_ask.key);
} }
continue; continue;
} }

View File

@ -6,7 +6,6 @@ pub use metadata::*;
pub use nodes::*; pub use nodes::*;
pub use ob_utils::*; pub use ob_utils::*;
pub use order_type::*; pub use order_type::*;
pub use order_type::*;
pub use queue::*; pub use queue::*;
pub mod book; pub mod book;

View File

@ -1,437 +1,439 @@
// use std::cell::RefMut; use std::cell::RefMut;
// use std::mem::size_of; use std::mem::size_of;
// use crate::error::MangoError; use crate::error::MangoError;
// use crate::state::PerpMarket; use crate::state::PerpMarket;
// use anchor_lang::prelude::*; use anchor_lang::prelude::*;
// use fixed::types::I80F48; use fixed::types::I80F48;
// use num_enum::{IntoPrimitive, TryFromPrimitive}; use num_enum::{IntoPrimitive, TryFromPrimitive};
// use solana_program::account_info::AccountInfo; use solana_program::account_info::AccountInfo;
// use solana_program::pubkey::Pubkey; use solana_program::pubkey::Pubkey;
// use solana_program::sysvar::rent::Rent; use solana_program::sysvar::rent::Rent;
// use static_assertions::const_assert_eq; use static_assertions::const_assert_eq;
// // use mango_logs::FillLog; // use mango_logs::FillLog;
// use mango_macro::Pod; use mango_macro::Pod;
// use super::metadata::MetaData; use super::metadata::MetaData;
// use super::ob_utils::strip_header_mut; use super::ob_utils::strip_header_mut;
// use super::order_type::Side; use super::order_type::Side;
// // use safe_transmute::{self, trivial::TriviallyTransmutable}; // use safe_transmute::{self, trivial::TriviallyTransmutable};
// // use crate::error::{check_assert, MangoErrorCode, MangoResult, SourceFileId}; // use crate::error::{check_assert, MangoErrorCode, MangoResult, SourceFileId};
// // use crate::matching::Side; // use crate::matching::Side;
// // use crate::state::{DataType, MetaData, PerpMarket}; // use crate::state::{DataType, MetaData, PerpMarket};
// // use crate::utils::strip_header_mut; // use crate::utils::strip_header_mut;
// // Don't want event queue to become single threaded if it's logging liquidations // 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 // Most common scenario will be liqors depositing USDC and withdrawing some other token
// // So tying it to token deposited is not wise // 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 // 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 { pub const MAX_NUM_EVENTS: usize = 512;
// type Item: bytemuck::Pod + Copy;
// fn head(&self) -> usize; pub trait QueueHeader: bytemuck::Pod {
// fn set_head(&mut self, value: usize); type Item: bytemuck::Pod + Copy;
// fn count(&self) -> usize;
// fn set_count(&mut self, value: usize);
// fn incr_event_id(&mut self); fn head(&self) -> usize;
// fn decr_event_id(&mut self, n: 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> { fn incr_event_id(&mut self);
// pub header: RefMut<'a, H>, fn decr_event_id(&mut self, n: usize);
// pub buf: RefMut<'a, [H::Item]>, }
// }
// impl<'a, H: QueueHeader> Queue<'a, H> { pub struct Queue<'a, H: QueueHeader> {
// pub fn new(header: RefMut<'a, H>, buf: RefMut<'a, [H::Item]>) -> Self { pub header: RefMut<'a, H>,
// Self { header, buf } pub buf: RefMut<'a, [H::Item]>,
// } }
// pub fn load_mut(account: &'a AccountInfo) -> Result<Self> { impl<'a, H: QueueHeader> Queue<'a, H> {
// let (header, buf) = strip_header_mut::<H, H::Item>(account)?; pub fn new(header: RefMut<'a, H>, buf: RefMut<'a, [H::Item]>) -> Self {
// Ok(Self { header, buf }) Self { header, buf }
// } }
// pub fn len(&self) -> usize { pub fn load_mut(account: &'a AccountInfo) -> Result<Self> {
// self.header.count() let (header, buf) = strip_header_mut::<H, H::Item>(account)?;
// } Ok(Self { header, buf })
}
// pub fn full(&self) -> bool { pub fn len(&self) -> usize {
// self.header.count() == self.buf.len() self.header.count()
// } }
// pub fn empty(&self) -> bool { pub fn full(&self) -> bool {
// self.header.count() == 0 self.header.count() == self.buf.len()
// } }
// pub fn push_back(&mut self, value: H::Item) -> std::result::Result<(), H::Item> { pub fn empty(&self) -> bool {
// if self.full() { self.header.count() == 0
// return Err(value); }
// }
// let slot = (self.header.head() + self.header.count()) % self.buf.len();
// self.buf[slot] = value;
// let count = self.header.count(); pub fn push_back(&mut self, value: H::Item) -> std::result::Result<(), H::Item> {
// self.header.set_count(count + 1); 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(); let count = self.header.count();
// Ok(()) self.header.set_count(count + 1);
// }
// pub fn peek_front(&self) -> Option<&H::Item> { self.header.incr_event_id();
// if self.empty() { Ok(())
// return None; }
// }
// Some(&self.buf[self.header.head()])
// }
// pub fn peek_front_mut(&mut self) -> Option<&mut H::Item> { pub fn peek_front(&self) -> Option<&H::Item> {
// if self.empty() { if self.empty() {
// return None; return None;
// } }
// Some(&mut self.buf[self.header.head()]) Some(&self.buf[self.header.head()])
// } }
// pub fn pop_front(&mut self) -> std::result::Result<H::Item, ()> { pub fn peek_front_mut(&mut self) -> Option<&mut H::Item> {
// if self.empty() { if self.empty() {
// return Err(()); return None;
// } }
// let value = self.buf[self.header.head()]; Some(&mut self.buf[self.header.head()])
}
// let count = self.header.count(); pub fn pop_front(&mut self) -> std::result::Result<H::Item, ()> {
// self.header.set_count(count - 1); if self.empty() {
return Err(());
}
let value = self.buf[self.header.head()];
// let head = self.header.head(); let count = self.header.count();
// self.header.set_head((head + 1) % self.buf.len()); 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<()> { Ok(value)
// 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<Item = &H::Item> { pub fn revert_pushes(&mut self, desired_len: usize) -> Result<()> {
// QueueIterator { require!(desired_len <= self.header.count(), MangoError::SomeError);
// queue: self, let len_diff = self.header.count() - desired_len;
// index: 0, self.header.set_count(desired_len);
// } self.header.decr_event_id(len_diff);
// } Ok(())
// } }
// struct QueueIterator<'a, 'b, H: QueueHeader> { pub fn iter(&self) -> impl Iterator<Item = &H::Item> {
// queue: &'b Queue<'a, H>, QueueIterator {
// index: usize, queue: self,
// } index: 0,
}
}
}
// impl<'a, 'b, H: QueueHeader> Iterator for QueueIterator<'a, 'b, H> { struct QueueIterator<'a, 'b, H: QueueHeader> {
// type Item = &'b H::Item; queue: &'b Queue<'a, H>,
// fn next(&mut self) -> Option<Self::Item> { index: usize,
// 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)] impl<'a, 'b, H: QueueHeader> Iterator for QueueIterator<'a, 'b, H> {
// pub struct EventQueueHeader { type Item = &'b H::Item;
// pub meta_data: MetaData, fn next(&mut self) -> Option<Self::Item> {
// head: usize, if self.index == self.queue.len() {
// count: usize, None
// pub seq_num: usize, } else {
// } let item =
// // unsafe impl TriviallyTransmutable for EventQueueHeader {} &self.queue.buf[(self.queue.header.head() + self.index) % self.queue.buf.len()];
self.index += 1;
Some(item)
}
}
}
// impl QueueHeader for EventQueueHeader { #[account(zero_copy)]
// type Item = AnyEvent; pub struct EventQueueHeader {
pub meta_data: MetaData,
head: usize,
count: usize,
pub seq_num: usize,
}
// unsafe impl TriviallyTransmutable for EventQueueHeader {}
// fn head(&self) -> usize { impl QueueHeader for EventQueueHeader {
// self.head type Item = AnyEvent;
// }
// 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>; 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 type EventQueue<'a> = Queue<'a, EventQueueHeader>;
// 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 fn load_and_init( impl<'a> EventQueue<'a> {
// account: &'a AccountInfo, pub fn load_mut_checked(
// program_id: &Pubkey, account: &'a AccountInfo,
// rent: &Rent, program_id: &Pubkey,
// ) -> Result<Self> { perp_market: &PerpMarket,
// // NOTE: check this first so we can borrow account later ) -> Result<Self> {
// require!( require!(account.owner == program_id, MangoError::SomeError); // InvalidOwner
// rent.is_exempt(account.lamports(), account.data_len()), require!(
// MangoError::SomeError &perp_market.event_queue == account.key,
// ); //MangoErrorCode::AccountNotRentExempt MangoError::SomeError
); //InvalidAccount
Self::load_mut(account)
}
// let mut state = Self::load_mut(account)?; pub fn load_and_init(
// require!(account.owner == program_id, MangoError::SomeError); // MangoErrorCode::InvalidOwner 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!( let state = Self::load_mut(account)?;
// // !state.header.meta_data.is_initialized, require!(account.owner == program_id, MangoError::SomeError); // MangoErrorCode::InvalidOwner
// // MangoError::SomeError
// // );
// // state.header.meta_data = MetaData::new(DataType::EventQueue, 0, true);
// 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)] Ok(state)
// #[repr(u8)] }
// pub enum EventType { }
// Fill,
// Out,
// Liquidate,
// }
// const EVENT_SIZE: usize = 200; #[derive(Copy, Clone, IntoPrimitive, TryFromPrimitive, Eq, PartialEq)]
// #[derive(Copy, Clone, Debug, Pod)] #[repr(u8)]
// #[repr(C)] pub enum EventType {
// pub struct AnyEvent { Fill,
// pub event_type: u8, Out,
// pub padding: [u8; EVENT_SIZE - 1], Liquidate,
// } }
// // unsafe impl TriviallyTransmutable for AnyEvent {}
// #[derive(Copy, Clone, Debug, Pod)] const EVENT_SIZE: usize = 200;
// #[repr(C)] #[derive(Copy, Clone, Debug, Pod)]
// pub struct FillEvent { #[repr(C)]
// pub event_type: u8, pub struct AnyEvent {
// pub taker_side: Side, // side from the taker's POV pub event_type: u8,
// pub maker_slot: u8, pub padding: [u8; EVENT_SIZE - 1],
// pub maker_out: bool, // true if maker order quantity == 0 }
// pub version: u8, // unsafe impl TriviallyTransmutable for AnyEvent {}
// pub market_fees_applied: bool,
// pub padding: [u8; 2],
// pub timestamp: u64,
// pub seq_num: usize, // note: usize same as u64
// pub maker: Pubkey, #[derive(Copy, Clone, Debug, Pod)]
// pub maker_order_id: i128, #[repr(C)]
// pub maker_client_order_id: u64, pub struct FillEvent {
// pub maker_fee: I80F48, 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 maker: Pubkey,
// pub best_initial: i64, 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 // The best bid/ask at the time the maker order was placed. Used for liquidity incentives
// pub maker_timestamp: u64, pub best_initial: i64,
// pub taker: Pubkey, // Timestamp of when the maker order was placed; copied over from the LeafNode
// pub taker_order_id: i128, pub maker_timestamp: u64,
// pub taker_client_order_id: u64,
// pub taker_fee: I80F48,
// pub price: i64, pub taker: Pubkey,
// pub quantity: i64, // number of quote lots pub taker_order_id: i128,
// } pub taker_client_order_id: u64,
// // unsafe impl TriviallyTransmutable for FillEvent {} pub taker_fee: I80F48,
// impl FillEvent { pub price: i64,
// pub fn new( pub quantity: i64, // number of quote lots
// taker_side: Side, }
// maker_slot: u8, // unsafe impl TriviallyTransmutable for FillEvent {}
// 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, impl FillEvent {
// taker_order_id: i128, pub fn new(
// taker_client_order_id: u64, taker_side: Side,
// taker_fee: I80F48, maker_slot: u8,
// price: i64, maker_out: bool,
// quantity: i64, timestamp: u64,
// version: u8, seq_num: usize,
// ) -> FillEvent { maker: Pubkey,
// Self { maker_order_id: i128,
// event_type: EventType::Fill as u8, maker_client_order_id: u64,
// taker_side, maker_fee: I80F48,
// maker_slot, best_initial: i64,
// maker_out, maker_timestamp: u64,
// 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) { taker: Pubkey,
// match side { taker_order_id: i128,
// Side::Bid => ( taker_client_order_id: u64,
// self.quantity, taker_fee: I80F48,
// -self.price.checked_mul(self.quantity).unwrap(), price: i64,
// ), quantity: i64,
// Side::Ask => ( version: u8,
// -self.quantity, ) -> FillEvent {
// self.price.checked_mul(self.quantity).unwrap(), 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 { pub fn base_quote_change(&self, side: Side) -> (i64, i64) {
// // FillLog { match side {
// // mango_group, Side::Bid => (
// // market_index: market_index as u64, self.quantity,
// // taker_side: self.taker_side as u8, -self.price.checked_mul(self.quantity).unwrap(),
// // maker_slot: self.maker_slot, ),
// // maker_out: self.maker_out, Side::Ask => (
// // timestamp: self.timestamp, -self.quantity,
// // seq_num: self.seq_num as u64, self.price.checked_mul(self.quantity).unwrap(),
// // 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)] // pub fn to_fill_log(&self, mango_group: Pubkey, market_index: usize) -> FillLog {
// #[repr(C)] // FillLog {
// pub struct OutEvent { // mango_group,
// pub event_type: u8, // market_index: market_index as u64,
// pub side: Side, // taker_side: self.taker_side as u8,
// pub slot: u8, // maker_slot: self.maker_slot,
// padding0: [u8; 5], // maker_out: self.maker_out,
// pub timestamp: u64, // timestamp: self.timestamp,
// pub seq_num: usize, // seq_num: self.seq_num as u64,
// pub owner: Pubkey, // maker: self.maker,
// pub quantity: i64, // maker_order_id: self.maker_order_id,
// padding1: [u8; EVENT_SIZE - 64], // maker_client_order_id: self.maker_client_order_id,
// } // maker_fee: self.maker_fee.to_bits(),
// // unsafe impl TriviallyTransmutable for OutEvent {} // best_initial: self.best_initial,
// impl OutEvent { // maker_timestamp: self.maker_timestamp,
// pub fn new( // taker: self.taker,
// side: Side, // taker_order_id: self.taker_order_id,
// slot: u8, // taker_client_order_id: self.taker_client_order_id,
// timestamp: u64, // taker_fee: self.taker_fee.to_bits(),
// seq_num: usize, // price: self.price,
// owner: Pubkey, // quantity: self.quantity,
// 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)] #[derive(Copy, Clone, Debug, Pod)]
// #[repr(C)] #[repr(C)]
// /// Liquidation for the PerpMarket this EventQueue is for pub struct OutEvent {
// pub struct LiquidateEvent { pub event_type: u8,
// pub event_type: u8, pub side: Side,
// padding0: [u8; 7], pub slot: u8,
// pub timestamp: u64, padding0: [u8; 5],
// pub seq_num: usize, pub timestamp: u64,
// pub liqee: Pubkey, pub seq_num: usize,
// pub liqor: Pubkey, pub owner: Pubkey,
// pub price: I80F48, // oracle price at the time of liquidation pub quantity: i64,
// pub quantity: i64, // number of contracts that were moved from liqee to liqor padding1: [u8; EVENT_SIZE - 64],
// pub liquidation_fee: I80F48, // liq fee for this earned for this market }
// padding1: [u8; EVENT_SIZE - 128], // unsafe impl TriviallyTransmutable for OutEvent {}
// } impl OutEvent {
// // unsafe impl TriviallyTransmutable for LiquidateEvent {} pub fn new(
// impl LiquidateEvent { side: Side,
// pub fn new( slot: u8,
// timestamp: u64, timestamp: u64,
// seq_num: usize, seq_num: usize,
// liqee: Pubkey, owner: Pubkey,
// liqor: Pubkey, quantity: i64,
// price: I80F48, ) -> Self {
// quantity: i64, Self {
// liquidation_fee: I80F48, event_type: EventType::Out.into(),
// ) -> Self { side,
// Self { slot,
// event_type: EventType::Liquidate.into(), padding0: [0; 5],
// padding0: [0u8; 7], timestamp,
// timestamp, seq_num,
// seq_num, owner,
// liqee, quantity,
// liqor, padding1: [0; EVENT_SIZE - 64],
// price, }
// quantity, }
// liquidation_fee, }
// padding1: [0u8; EVENT_SIZE - 128],
// } #[derive(Copy, Clone, Debug, Pod)]
// } #[repr(C)]
// } /// Liquidation for the PerpMarket this EventQueue is for
// const_assert_eq!(size_of::<AnyEvent>(), size_of::<FillEvent>()); pub struct LiquidateEvent {
// const_assert_eq!(size_of::<AnyEvent>(), size_of::<OutEvent>()); pub event_type: u8,
// const_assert_eq!(size_of::<AnyEvent>(), size_of::<LiquidateEvent>()); 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>());

View File

@ -5,9 +5,6 @@ use crate::state::TokenIndex;
pub type PerpMarketIndex = u16; pub type PerpMarketIndex = u16;
#[account(zero_copy)]
pub struct EventQueue {}
#[account(zero_copy)] #[account(zero_copy)]
pub struct PerpMarket { pub struct PerpMarket {
pub group: Pubkey, pub group: Pubkey,
@ -17,8 +14,7 @@ pub struct PerpMarket {
pub bids: Pubkey, pub bids: Pubkey,
pub asks: Pubkey, pub asks: Pubkey,
/// Event queue of TODO pub event_queue: Pubkey,
/// pub event_queue: Pubkey,
/// Number of quote native that reresents min tick /// 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 /// 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

View File

@ -12,6 +12,7 @@ macro_rules! zip {
zip!($($y), +)) zip!($($y), +))
) )
} }
#[allow(unused_imports)]
pub(crate) use zip; pub(crate) use zip;
#[macro_export] #[macro_export]

View File

@ -1086,6 +1086,7 @@ pub struct CreatePerpMarketInstruction<'keypair> {
pub oracle: Pubkey, pub oracle: Pubkey,
pub asks: Pubkey, pub asks: Pubkey,
pub bids: Pubkey, pub bids: Pubkey,
pub event_queue: Pubkey,
pub payer: &'keypair Keypair, pub payer: &'keypair Keypair,
pub perp_market_index: PerpMarketIndex, pub perp_market_index: PerpMarketIndex,
pub base_token_index: TokenIndex, pub base_token_index: TokenIndex,
@ -1127,6 +1128,7 @@ impl<'keypair> ClientInstruction for CreatePerpMarketInstruction<'keypair> {
perp_market, perp_market,
asks: self.asks, asks: self.asks,
bids: self.bids, bids: self.bids,
event_queue: self.event_queue,
payer: self.payer.pubkey(), payer: self.payer.pubkey(),
system_program: System::id(), system_program: System::id(),
}; };
@ -1146,6 +1148,7 @@ pub struct PlacePerpOrderInstruction<'keypair> {
pub perp_market: Pubkey, pub perp_market: Pubkey,
pub asks: Pubkey, pub asks: Pubkey,
pub bids: Pubkey, pub bids: Pubkey,
pub event_queue: Pubkey,
pub oracle: Pubkey, pub oracle: Pubkey,
pub owner: &'keypair Keypair, pub owner: &'keypair Keypair,
} }
@ -1173,6 +1176,7 @@ impl<'keypair> ClientInstruction for PlacePerpOrderInstruction<'keypair> {
perp_market: self.perp_market, perp_market: self.perp_market,
asks: self.asks, asks: self.asks,
bids: self.bids, bids: self.bids,
event_queue: self.event_queue,
oracle: self.oracle, oracle: self.oracle,
owner: self.owner.pubkey(), owner: self.owner.pubkey(),
}; };

View File

@ -83,7 +83,23 @@ impl SolanaCookie {
.newest() .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 key = Keypair::new();
let len = 8 + std::mem::size_of::<T>(); let len = 8 + std::mem::size_of::<T>();
let rent = self.rent.minimum_balance(len); let rent = self.rent.minimum_balance(len);

View File

@ -1,6 +1,6 @@
#![cfg(feature = "test-bpf")] #![cfg(feature = "test-bpf")]
use mango_v4::state::BookSide; use mango_v4::state::*;
use solana_program_test::*; use solana_program_test::*;
use solana_sdk::{signature::Keypair, transport::TransportError}; use solana_sdk::{signature::Keypair, transport::TransportError};
@ -82,6 +82,7 @@ async fn test_perp() -> Result<(), TransportError> {
perp_market, perp_market,
asks, asks,
bids, bids,
event_queue,
.. ..
} = send_tx( } = send_tx(
solana, solana,
@ -90,12 +91,20 @@ async fn test_perp() -> Result<(), TransportError> {
oracle: tokens[0].oracle, oracle: tokens[0].oracle,
asks: context asks: context
.solana .solana
.create_account::<BookSide>(&mango_v4::id()) .create_account_for_type::<BookSide>(&mango_v4::id())
.await, .await,
bids: context bids: context
.solana .solana
.create_account::<BookSide>(&mango_v4::id()) .create_account_for_type::<BookSide>(&mango_v4::id())
.await, .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, admin,
payer, payer,
perp_market_index: 0, perp_market_index: 0,
@ -117,6 +126,7 @@ async fn test_perp() -> Result<(), TransportError> {
perp_market, perp_market,
asks, asks,
bids, bids,
event_queue,
oracle: tokens[0].oracle, oracle: tokens[0].oracle,
owner, owner,
}, },