Compute health in perp_place_order
This commit is contained in:
parent
65a5139db7
commit
48cb1f0b88
|
@ -2,7 +2,8 @@ use anchor_lang::prelude::*;
|
||||||
|
|
||||||
use crate::error::*;
|
use crate::error::*;
|
||||||
use crate::state::{
|
use crate::state::{
|
||||||
oracle_price, Book, EventQueue, Group, MangoAccount, OrderType, PerpMarket, Side,
|
compute_health_from_fixed_accounts, oracle_price, Book, EventQueue, Group, HealthType,
|
||||||
|
MangoAccount, OrderType, PerpMarket, Side,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Accounts)]
|
#[derive(Accounts)]
|
||||||
|
@ -76,12 +77,11 @@ pub fn perp_place_order(
|
||||||
// When the limit is reached, processing stops and the instruction succeeds.
|
// When the limit is reached, processing stops and the instruction succeeds.
|
||||||
limit: u8,
|
limit: u8,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
// TODO: check pre and post health
|
|
||||||
|
|
||||||
let mut mango_account = ctx.accounts.account.load_mut()?;
|
let mut mango_account = ctx.accounts.account.load_mut()?;
|
||||||
require!(mango_account.is_bankrupt == 0, MangoError::IsBankrupt);
|
require!(mango_account.is_bankrupt == 0, MangoError::IsBankrupt);
|
||||||
let mango_account_pk = ctx.accounts.account.key();
|
let mango_account_pk = ctx.accounts.account.key();
|
||||||
|
|
||||||
|
{
|
||||||
let mut perp_market = ctx.accounts.perp_market.load_mut()?;
|
let mut perp_market = ctx.accounts.perp_market.load_mut()?;
|
||||||
let bids = &ctx.accounts.bids.to_account_info();
|
let bids = &ctx.accounts.bids.to_account_info();
|
||||||
let asks = &ctx.accounts.asks.to_account_info();
|
let asks = &ctx.accounts.asks.to_account_info();
|
||||||
|
@ -124,6 +124,15 @@ pub fn perp_place_order(
|
||||||
now_ts,
|
now_ts,
|
||||||
limit,
|
limit,
|
||||||
)?;
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let health = compute_health_from_fixed_accounts(
|
||||||
|
&mango_account,
|
||||||
|
HealthType::Init,
|
||||||
|
ctx.remaining_accounts,
|
||||||
|
)?;
|
||||||
|
msg!("health: {}", health);
|
||||||
|
require!(health >= 0, MangoError::HealthMustBePositive);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -244,15 +244,16 @@ pub fn compute_health_from_fixed_accounts(
|
||||||
let active_serum3_len = account.serum3.iter_active().count();
|
let active_serum3_len = account.serum3.iter_active().count();
|
||||||
let active_perp_len = account.perps.iter_active_accounts().count();
|
let active_perp_len = account.perps.iter_active_accounts().count();
|
||||||
let expected_ais = cm!(active_token_len * 2 // banks + oracles
|
let expected_ais = cm!(active_token_len * 2 // banks + oracles
|
||||||
+ active_serum3_len // open_orders
|
+ active_perp_len // PerpMarkets
|
||||||
+ active_perp_len); // PerpMarkets
|
+ active_serum3_len); // open_orders
|
||||||
|
msg!("{} {}", ais.len(), expected_ais);
|
||||||
require!(ais.len() == expected_ais, MangoError::SomeError);
|
require!(ais.len() == expected_ais, MangoError::SomeError);
|
||||||
|
|
||||||
let retriever = FixedOrderAccountRetriever {
|
let retriever = FixedOrderAccountRetriever {
|
||||||
ais,
|
ais,
|
||||||
n_banks: active_token_len,
|
n_banks: active_token_len,
|
||||||
begin_serum3: cm!(active_token_len * 2),
|
begin_perp: cm!(active_token_len * 2),
|
||||||
begin_perp: cm!(active_token_len * 2 + active_serum3_len),
|
begin_serum3: cm!(active_token_len * 2 + active_perp_len),
|
||||||
};
|
};
|
||||||
compute_health_detail(account, &retriever, health_type, true)?.health(health_type)
|
compute_health_detail(account, &retriever, health_type, true)?.health(health_type)
|
||||||
}
|
}
|
||||||
|
|
|
@ -121,11 +121,27 @@ async fn derive_health_check_remaining_account_metas(
|
||||||
account: &MangoAccount,
|
account: &MangoAccount,
|
||||||
affected_bank: Option<Pubkey>,
|
affected_bank: Option<Pubkey>,
|
||||||
writable_banks: bool,
|
writable_banks: bool,
|
||||||
|
affected_perp_market_index: Option<PerpMarketIndex>,
|
||||||
) -> Vec<AccountMeta> {
|
) -> Vec<AccountMeta> {
|
||||||
|
let mut adjusted_account = account.clone();
|
||||||
|
if let Some(affected_bank) = affected_bank {
|
||||||
|
let bank: Bank = account_loader.load(&affected_bank).await.unwrap();
|
||||||
|
adjusted_account
|
||||||
|
.tokens
|
||||||
|
.get_mut_or_create(bank.token_index)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
if let Some(affected_perp_market_index) = affected_perp_market_index {
|
||||||
|
adjusted_account
|
||||||
|
.perps
|
||||||
|
.get_account_mut_or_create(affected_perp_market_index)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
// figure out all the banks/oracles that need to be passed for the health check
|
// figure out all the banks/oracles that need to be passed for the health check
|
||||||
let mut banks = vec![];
|
let mut banks = vec![];
|
||||||
let mut oracles = vec![];
|
let mut oracles = vec![];
|
||||||
for position in account.tokens.iter_active() {
|
for position in adjusted_account.tokens.iter_active() {
|
||||||
let mint_info =
|
let mint_info =
|
||||||
get_mint_info_by_token_index(account_loader, account, position.token_index).await;
|
get_mint_info_by_token_index(account_loader, account, position.token_index).await;
|
||||||
// TODO: ALTs are unavailable
|
// TODO: ALTs are unavailable
|
||||||
|
@ -139,26 +155,12 @@ async fn derive_health_check_remaining_account_metas(
|
||||||
banks.push(mint_info.bank);
|
banks.push(mint_info.bank);
|
||||||
oracles.push(mint_info.oracle);
|
oracles.push(mint_info.oracle);
|
||||||
}
|
}
|
||||||
if let Some(affected_bank) = affected_bank {
|
|
||||||
if banks.iter().find(|&&v| v == affected_bank).is_none() {
|
|
||||||
// If there is not yet an active position for the token, we need to pass
|
|
||||||
// the bank/oracle for health check anyway.
|
|
||||||
let new_position = account
|
|
||||||
.tokens
|
|
||||||
.values
|
|
||||||
.iter()
|
|
||||||
.position(|p| !p.is_active())
|
|
||||||
.unwrap();
|
|
||||||
banks.insert(new_position, affected_bank);
|
|
||||||
let affected_bank: Bank = account_loader.load(&affected_bank).await.unwrap();
|
|
||||||
oracles.insert(new_position, affected_bank.oracle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let perp_markets = account
|
let perp_markets = adjusted_account
|
||||||
.perps
|
.perps
|
||||||
.iter_active_accounts()
|
.iter_active_accounts()
|
||||||
.map(|perp| get_perp_market_address_by_index(account.group, perp.market_index));
|
.map(|perp| get_perp_market_address_by_index(account.group, perp.market_index));
|
||||||
|
|
||||||
let serum_oos = account.serum3.iter_active().map(|&s| s.open_orders);
|
let serum_oos = account.serum3.iter_active().map(|&s| s.open_orders);
|
||||||
|
|
||||||
let to_account_meta = |pubkey| AccountMeta {
|
let to_account_meta = |pubkey| AccountMeta {
|
||||||
|
@ -311,6 +313,7 @@ impl<'keypair> ClientInstruction for MarginTradeInstruction<'keypair> {
|
||||||
&account,
|
&account,
|
||||||
Some(self.mango_token_bank),
|
Some(self.mango_token_bank),
|
||||||
true,
|
true,
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
@ -405,6 +408,7 @@ impl<'keypair> ClientInstruction for WithdrawInstruction<'keypair> {
|
||||||
&account,
|
&account,
|
||||||
Some(mint_info.bank),
|
Some(mint_info.bank),
|
||||||
false,
|
false,
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
@ -468,6 +472,7 @@ impl<'keypair> ClientInstruction for DepositInstruction<'keypair> {
|
||||||
&account,
|
&account,
|
||||||
Some(mint_info.bank),
|
Some(mint_info.bank),
|
||||||
false,
|
false,
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
@ -1003,8 +1008,13 @@ impl<'keypair> ClientInstruction for Serum3PlaceOrderInstruction<'keypair> {
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let health_check_metas =
|
let health_check_metas = derive_health_check_remaining_account_metas(
|
||||||
derive_health_check_remaining_account_metas(&account_loader, &account, None, false)
|
&account_loader,
|
||||||
|
&account,
|
||||||
|
None,
|
||||||
|
false,
|
||||||
|
None,
|
||||||
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let accounts = Self::Accounts {
|
let accounts = Self::Accounts {
|
||||||
|
@ -1230,8 +1240,13 @@ impl ClientInstruction for Serum3LiqForceCancelOrdersInstruction {
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let health_check_metas =
|
let health_check_metas = derive_health_check_remaining_account_metas(
|
||||||
derive_health_check_remaining_account_metas(&account_loader, &account, None, false)
|
&account_loader,
|
||||||
|
&account,
|
||||||
|
None,
|
||||||
|
false,
|
||||||
|
None,
|
||||||
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let accounts = Self::Accounts {
|
let accounts = Self::Accounts {
|
||||||
|
@ -1419,7 +1434,7 @@ impl<'keypair> ClientInstruction for PerpPlaceOrderInstruction<'keypair> {
|
||||||
type Instruction = mango_v4::instruction::PerpPlaceOrder;
|
type Instruction = mango_v4::instruction::PerpPlaceOrder;
|
||||||
async fn to_instruction(
|
async fn to_instruction(
|
||||||
&self,
|
&self,
|
||||||
_loader: impl ClientAccountLoader + 'async_trait,
|
account_loader: impl ClientAccountLoader + 'async_trait,
|
||||||
) -> (Self::Accounts, instruction::Instruction) {
|
) -> (Self::Accounts, instruction::Instruction) {
|
||||||
let program_id = mango_v4::id();
|
let program_id = mango_v4::id();
|
||||||
let instruction = Self::Instruction {
|
let instruction = Self::Instruction {
|
||||||
|
@ -1443,7 +1458,20 @@ impl<'keypair> ClientInstruction for PerpPlaceOrderInstruction<'keypair> {
|
||||||
owner: self.owner.pubkey(),
|
owner: self.owner.pubkey(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let instruction = make_instruction(program_id, &accounts, instruction);
|
let perp_market: PerpMarket = account_loader.load(&self.perp_market).await.unwrap();
|
||||||
|
let account: MangoAccount = account_loader.load(&self.account).await.unwrap();
|
||||||
|
let health_check_metas = derive_health_check_remaining_account_metas(
|
||||||
|
&account_loader,
|
||||||
|
&account,
|
||||||
|
None,
|
||||||
|
false,
|
||||||
|
Some(perp_market.perp_market_index),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let mut instruction = make_instruction(program_id, &accounts, instruction);
|
||||||
|
instruction.accounts.extend(health_check_metas);
|
||||||
|
|
||||||
(accounts, instruction)
|
(accounts, instruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#![cfg(feature = "test-bpf")]
|
#![cfg(feature = "test-bpf")]
|
||||||
|
|
||||||
|
use fixed::types::I80F48;
|
||||||
use mango_v4::state::*;
|
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};
|
||||||
|
@ -284,6 +285,11 @@ async fn test_health_compute_perp() -> Result<(), TransportError> {
|
||||||
perp_markets.push((perp_market, asks, bids, event_queue));
|
perp_markets.push((perp_market, asks, bids, event_queue));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let price_lots = {
|
||||||
|
let perp_market = solana.get_account::<PerpMarket>(perp_markets[0].0).await;
|
||||||
|
perp_market.native_price_to_lot(I80F48::from(1))
|
||||||
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
// TEST: Create a perp order for each market
|
// TEST: Create a perp order for each market
|
||||||
//
|
//
|
||||||
|
@ -301,7 +307,7 @@ async fn test_health_compute_perp() -> Result<(), TransportError> {
|
||||||
oracle: tokens[i + 1].oracle,
|
oracle: tokens[i + 1].oracle,
|
||||||
owner,
|
owner,
|
||||||
side: Side::Bid,
|
side: Side::Bid,
|
||||||
price_lots: 1,
|
price_lots,
|
||||||
max_base_lots: 1,
|
max_base_lots: 1,
|
||||||
max_quote_lots: i64::MAX,
|
max_quote_lots: i64::MAX,
|
||||||
client_order_id: 0,
|
client_order_id: 0,
|
||||||
|
|
Loading…
Reference in New Issue