remove base_token_index from perps and include oracles (#224)
Co-authored-by: Conj0iner <conj0iner@users.noreply.github.com>
This commit is contained in:
parent
f96b9ded0d
commit
1c67b8ed5f
|
@ -39,6 +39,7 @@ pub fn new_health_cache_(
|
||||||
let retriever = FixedOrderAccountRetriever {
|
let retriever = FixedOrderAccountRetriever {
|
||||||
ais: accounts,
|
ais: accounts,
|
||||||
n_banks: active_token_len,
|
n_banks: active_token_len,
|
||||||
|
n_perps: active_perp_len,
|
||||||
begin_perp: active_token_len * 2,
|
begin_perp: active_token_len * 2,
|
||||||
begin_serum3: active_token_len * 2 + active_perp_len,
|
begin_serum3: active_token_len * 2 + active_perp_len,
|
||||||
};
|
};
|
||||||
|
|
|
@ -49,8 +49,7 @@ pub fn perp_create_market(
|
||||||
perp_market_index: PerpMarketIndex,
|
perp_market_index: PerpMarketIndex,
|
||||||
name: String,
|
name: String,
|
||||||
oracle_config: OracleConfig,
|
oracle_config: OracleConfig,
|
||||||
base_token_index_opt: Option<TokenIndex>,
|
base_decimals: u8,
|
||||||
base_token_decimals: u8,
|
|
||||||
quote_lot_size: i64,
|
quote_lot_size: i64,
|
||||||
base_lot_size: i64,
|
base_lot_size: i64,
|
||||||
maint_asset_weight: f32,
|
maint_asset_weight: f32,
|
||||||
|
@ -94,10 +93,10 @@ pub fn perp_create_market(
|
||||||
fees_settled: I80F48::ZERO,
|
fees_settled: I80F48::ZERO,
|
||||||
// Why optional - Perp could be based purely on an oracle
|
// Why optional - Perp could be based purely on an oracle
|
||||||
bump: *ctx.bumps.get("perp_market").ok_or(MangoError::SomeError)?,
|
bump: *ctx.bumps.get("perp_market").ok_or(MangoError::SomeError)?,
|
||||||
base_token_decimals,
|
base_decimals,
|
||||||
perp_market_index,
|
perp_market_index,
|
||||||
base_token_index: base_token_index_opt.ok_or(TokenIndex::MAX).unwrap(),
|
|
||||||
registration_time: Clock::get()?.unix_timestamp,
|
registration_time: Clock::get()?.unix_timestamp,
|
||||||
|
padding0: Default::default(),
|
||||||
padding1: Default::default(),
|
padding1: Default::default(),
|
||||||
padding2: Default::default(),
|
padding2: Default::default(),
|
||||||
reserved: [0; 112],
|
reserved: [0; 112],
|
||||||
|
|
|
@ -22,8 +22,7 @@ pub fn perp_edit_market(
|
||||||
ctx: Context<PerpEditMarket>,
|
ctx: Context<PerpEditMarket>,
|
||||||
oracle_opt: Option<Pubkey>,
|
oracle_opt: Option<Pubkey>,
|
||||||
oracle_config_opt: Option<OracleConfig>,
|
oracle_config_opt: Option<OracleConfig>,
|
||||||
base_token_index_opt: Option<TokenIndex>,
|
base_decimals_opt: Option<u8>,
|
||||||
base_token_decimals_opt: Option<u8>,
|
|
||||||
maint_asset_weight_opt: Option<f32>,
|
maint_asset_weight_opt: Option<f32>,
|
||||||
init_asset_weight_opt: Option<f32>,
|
init_asset_weight_opt: Option<f32>,
|
||||||
maint_liab_weight_opt: Option<f32>,
|
maint_liab_weight_opt: Option<f32>,
|
||||||
|
@ -100,17 +99,13 @@ pub fn perp_edit_market(
|
||||||
// fees_accrued
|
// fees_accrued
|
||||||
// bump
|
// bump
|
||||||
|
|
||||||
if let Some(base_token_decimals) = base_token_decimals_opt {
|
if let Some(base_decimals) = base_decimals_opt {
|
||||||
perp_market.base_token_decimals = base_token_decimals;
|
perp_market.base_decimals = base_decimals;
|
||||||
}
|
}
|
||||||
|
|
||||||
// unchanged -
|
// unchanged -
|
||||||
// perp_market_index
|
// perp_market_index
|
||||||
|
|
||||||
if let Some(base_token_index) = base_token_index_opt {
|
|
||||||
perp_market.base_token_index = base_token_index;
|
|
||||||
}
|
|
||||||
|
|
||||||
// unchanged -
|
// unchanged -
|
||||||
// quote_token_index
|
// quote_token_index
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,9 @@ pub struct PerpLiqForceCancelOrders<'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>,
|
||||||
|
|
||||||
|
/// CHECK: Oracle can have different account types, constrained by address in perp_market
|
||||||
|
pub oracle: UncheckedAccount<'info>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn perp_liq_force_cancel_orders(
|
pub fn perp_liq_force_cancel_orders(
|
||||||
|
|
|
@ -382,8 +382,7 @@ pub mod mango_v4 {
|
||||||
perp_market_index: PerpMarketIndex,
|
perp_market_index: PerpMarketIndex,
|
||||||
name: String,
|
name: String,
|
||||||
oracle_config: OracleConfig,
|
oracle_config: OracleConfig,
|
||||||
base_token_index_opt: Option<TokenIndex>,
|
base_decimals: u8,
|
||||||
base_token_decimals: u8,
|
|
||||||
quote_lot_size: i64,
|
quote_lot_size: i64,
|
||||||
base_lot_size: i64,
|
base_lot_size: i64,
|
||||||
maint_asset_weight: f32,
|
maint_asset_weight: f32,
|
||||||
|
@ -402,8 +401,7 @@ pub mod mango_v4 {
|
||||||
perp_market_index,
|
perp_market_index,
|
||||||
name,
|
name,
|
||||||
oracle_config,
|
oracle_config,
|
||||||
base_token_index_opt,
|
base_decimals,
|
||||||
base_token_decimals,
|
|
||||||
quote_lot_size,
|
quote_lot_size,
|
||||||
base_lot_size,
|
base_lot_size,
|
||||||
maint_asset_weight,
|
maint_asset_weight,
|
||||||
|
@ -424,8 +422,7 @@ pub mod mango_v4 {
|
||||||
ctx: Context<PerpEditMarket>,
|
ctx: Context<PerpEditMarket>,
|
||||||
oracle_opt: Option<Pubkey>,
|
oracle_opt: Option<Pubkey>,
|
||||||
oracle_config_opt: Option<OracleConfig>,
|
oracle_config_opt: Option<OracleConfig>,
|
||||||
base_token_index_opt: Option<TokenIndex>,
|
base_decimals_opt: Option<u8>,
|
||||||
base_token_decimals_opt: Option<u8>,
|
|
||||||
maint_asset_weight_opt: Option<f32>,
|
maint_asset_weight_opt: Option<f32>,
|
||||||
init_asset_weight_opt: Option<f32>,
|
init_asset_weight_opt: Option<f32>,
|
||||||
maint_liab_weight_opt: Option<f32>,
|
maint_liab_weight_opt: Option<f32>,
|
||||||
|
@ -441,8 +438,7 @@ pub mod mango_v4 {
|
||||||
ctx,
|
ctx,
|
||||||
oracle_opt,
|
oracle_opt,
|
||||||
oracle_config_opt,
|
oracle_config_opt,
|
||||||
base_token_index_opt,
|
base_decimals_opt,
|
||||||
base_token_decimals_opt,
|
|
||||||
maint_asset_weight_opt,
|
maint_asset_weight_opt,
|
||||||
init_asset_weight_opt,
|
init_asset_weight_opt,
|
||||||
maint_liab_weight_opt,
|
maint_liab_weight_opt,
|
||||||
|
|
|
@ -41,12 +41,12 @@ pub trait AccountRetriever {
|
||||||
|
|
||||||
fn serum_oo(&self, account_index: usize, key: &Pubkey) -> Result<&OpenOrders>;
|
fn serum_oo(&self, account_index: usize, key: &Pubkey) -> Result<&OpenOrders>;
|
||||||
|
|
||||||
fn perp_market(
|
fn perp_market_and_oracle_price(
|
||||||
&self,
|
&self,
|
||||||
group: &Pubkey,
|
group: &Pubkey,
|
||||||
account_index: usize,
|
account_index: usize,
|
||||||
perp_market_index: PerpMarketIndex,
|
perp_market_index: PerpMarketIndex,
|
||||||
) -> Result<&PerpMarket>;
|
) -> Result<(&PerpMarket, I80F48)>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Assumes the account infos needed for the health computation follow a strict order.
|
/// Assumes the account infos needed for the health computation follow a strict order.
|
||||||
|
@ -54,10 +54,12 @@ pub trait AccountRetriever {
|
||||||
/// 1. n_banks Bank account, in the order of account.token_iter_active()
|
/// 1. n_banks Bank account, in the order of account.token_iter_active()
|
||||||
/// 2. n_banks oracle accounts, one for each bank in the same order
|
/// 2. n_banks oracle accounts, one for each bank in the same order
|
||||||
/// 3. PerpMarket accounts, in the order of account.perps.iter_active_accounts()
|
/// 3. PerpMarket accounts, in the order of account.perps.iter_active_accounts()
|
||||||
/// 4. serum3 OpenOrders accounts, in the order of account.serum3.iter_active()
|
/// 4. PerpMarket oracle accounts, in the order of the perp market accounts
|
||||||
|
/// 5. serum3 OpenOrders accounts, in the order of account.serum3.iter_active()
|
||||||
pub struct FixedOrderAccountRetriever<T: KeyedAccountReader> {
|
pub struct FixedOrderAccountRetriever<T: KeyedAccountReader> {
|
||||||
pub ais: Vec<T>,
|
pub ais: Vec<T>,
|
||||||
pub n_banks: usize,
|
pub n_banks: usize,
|
||||||
|
pub n_perps: usize,
|
||||||
pub begin_perp: usize,
|
pub begin_perp: usize,
|
||||||
pub begin_serum3: usize,
|
pub begin_serum3: usize,
|
||||||
}
|
}
|
||||||
|
@ -70,15 +72,16 @@ pub fn new_fixed_order_account_retriever<'a, 'info>(
|
||||||
let active_serum3_len = account.active_serum3_orders().count();
|
let active_serum3_len = account.active_serum3_orders().count();
|
||||||
let active_perp_len = account.active_perp_positions().count();
|
let active_perp_len = account.active_perp_positions().count();
|
||||||
let expected_ais = cm!(active_token_len * 2 // banks + oracles
|
let expected_ais = cm!(active_token_len * 2 // banks + oracles
|
||||||
+ active_perp_len // PerpMarkets
|
+ active_perp_len * 2 // PerpMarkets + Oracles
|
||||||
+ active_serum3_len); // open_orders
|
+ active_serum3_len); // open_orders
|
||||||
require_eq!(ais.len(), expected_ais);
|
require_eq!(ais.len(), expected_ais);
|
||||||
|
|
||||||
Ok(FixedOrderAccountRetriever {
|
Ok(FixedOrderAccountRetriever {
|
||||||
ais: AccountInfoRef::borrow_slice(ais)?,
|
ais: AccountInfoRef::borrow_slice(ais)?,
|
||||||
n_banks: active_token_len,
|
n_banks: active_token_len,
|
||||||
|
n_perps: active_perp_len,
|
||||||
begin_perp: cm!(active_token_len * 2),
|
begin_perp: cm!(active_token_len * 2),
|
||||||
begin_serum3: cm!(active_token_len * 2 + active_perp_len),
|
begin_serum3: cm!(active_token_len * 2 + active_perp_len * 2),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,10 +93,28 @@ impl<T: KeyedAccountReader> FixedOrderAccountRetriever<T> {
|
||||||
Ok(bank)
|
Ok(bank)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn perp_market(
|
||||||
|
&self,
|
||||||
|
group: &Pubkey,
|
||||||
|
account_index: usize,
|
||||||
|
perp_market_index: PerpMarketIndex,
|
||||||
|
) -> Result<&PerpMarket> {
|
||||||
|
let market_ai = &self.ais[self.begin_perp + account_index];
|
||||||
|
let market = market_ai.load::<PerpMarket>()?;
|
||||||
|
require_keys_eq!(market.group, *group);
|
||||||
|
require_eq!(market.perp_market_index, perp_market_index);
|
||||||
|
Ok(market)
|
||||||
|
}
|
||||||
|
|
||||||
fn oracle_price(&self, account_index: usize, bank: &Bank) -> Result<I80F48> {
|
fn oracle_price(&self, account_index: usize, bank: &Bank) -> Result<I80F48> {
|
||||||
let oracle = &self.ais[cm!(self.n_banks + account_index)];
|
let oracle = &self.ais[cm!(self.n_banks + account_index)];
|
||||||
bank.oracle_price(oracle)
|
bank.oracle_price(oracle)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn oracle_price_perp(&self, account_index: usize, perp_market: &PerpMarket) -> Result<I80F48> {
|
||||||
|
let oracle = &self.ais[self.begin_perp + self.n_perps + account_index];
|
||||||
|
perp_market.oracle_price(oracle)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: KeyedAccountReader> AccountRetriever for FixedOrderAccountRetriever<T> {
|
impl<T: KeyedAccountReader> AccountRetriever for FixedOrderAccountRetriever<T> {
|
||||||
|
@ -126,27 +147,32 @@ impl<T: KeyedAccountReader> AccountRetriever for FixedOrderAccountRetriever<T> {
|
||||||
Ok((bank, oracle_price))
|
Ok((bank, oracle_price))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn perp_market(
|
fn perp_market_and_oracle_price(
|
||||||
&self,
|
&self,
|
||||||
group: &Pubkey,
|
group: &Pubkey,
|
||||||
account_index: usize,
|
account_index: usize,
|
||||||
perp_market_index: PerpMarketIndex,
|
perp_market_index: PerpMarketIndex,
|
||||||
) -> Result<&PerpMarket> {
|
) -> Result<(&PerpMarket, I80F48)> {
|
||||||
let ai = &self.ais[cm!(self.begin_perp + account_index)];
|
let perp_market = self
|
||||||
(|| {
|
.perp_market(group, account_index, perp_market_index)
|
||||||
let market = ai.load::<PerpMarket>()?;
|
|
||||||
require_keys_eq!(market.group, *group);
|
|
||||||
require_eq!(market.perp_market_index, perp_market_index);
|
|
||||||
Ok(market)
|
|
||||||
})()
|
|
||||||
.with_context(|| {
|
.with_context(|| {
|
||||||
format!(
|
format!(
|
||||||
"loading perp market with health account index {} and perp market index {}, passed account {}",
|
"loading perp market with health account index {} and perp market index {}, passed account {}",
|
||||||
account_index,
|
account_index,
|
||||||
perp_market_index,
|
perp_market_index,
|
||||||
ai.key(),
|
self.ais[self.begin_perp + account_index].key(),
|
||||||
)
|
)
|
||||||
})
|
})?;
|
||||||
|
|
||||||
|
let oracle_price = self.oracle_price_perp(account_index, perp_market).with_context(|| {
|
||||||
|
format!(
|
||||||
|
"getting oracle for perp market with health account index {} and perp market index {}, passed account {}",
|
||||||
|
account_index,
|
||||||
|
perp_market_index,
|
||||||
|
self.ais[self.begin_perp + self.n_perps + account_index].key(),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
Ok((perp_market, oracle_price))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serum_oo(&self, account_index: usize, key: &Pubkey) -> Result<&OpenOrders> {
|
fn serum_oo(&self, account_index: usize, key: &Pubkey) -> Result<&OpenOrders> {
|
||||||
|
@ -169,6 +195,7 @@ impl<T: KeyedAccountReader> AccountRetriever for FixedOrderAccountRetriever<T> {
|
||||||
/// - an unknown number of Banks in any order, followed by
|
/// - an unknown number of Banks in any order, followed by
|
||||||
/// - the same number of oracles in the same order as the banks, followed by
|
/// - the same number of oracles in the same order as the banks, followed by
|
||||||
/// - an unknown number of PerpMarket accounts
|
/// - an unknown number of PerpMarket accounts
|
||||||
|
/// - the same number of oracles in the same order as the perp markets
|
||||||
/// - an unknown number of serum3 OpenOrders accounts
|
/// - an unknown number of serum3 OpenOrders accounts
|
||||||
/// and retrieves accounts needed for the health computation by doing a linear
|
/// and retrieves accounts needed for the health computation by doing a linear
|
||||||
/// scan for each request.
|
/// scan for each request.
|
||||||
|
@ -176,6 +203,7 @@ pub struct ScanningAccountRetriever<'a, 'info> {
|
||||||
banks: Vec<AccountInfoRefMut<'a, 'info>>,
|
banks: Vec<AccountInfoRefMut<'a, 'info>>,
|
||||||
oracles: Vec<AccountInfoRef<'a, 'info>>,
|
oracles: Vec<AccountInfoRef<'a, 'info>>,
|
||||||
perp_markets: Vec<AccountInfoRef<'a, 'info>>,
|
perp_markets: Vec<AccountInfoRef<'a, 'info>>,
|
||||||
|
perp_oracles: Vec<AccountInfoRef<'a, 'info>>,
|
||||||
serum3_oos: Vec<AccountInfoRef<'a, 'info>>,
|
serum3_oos: Vec<AccountInfoRef<'a, 'info>>,
|
||||||
token_index_map: HashMap<TokenIndex, usize>,
|
token_index_map: HashMap<TokenIndex, usize>,
|
||||||
perp_index_map: HashMap<PerpMarketIndex, usize>,
|
perp_index_map: HashMap<PerpMarketIndex, usize>,
|
||||||
|
@ -239,13 +267,16 @@ impl<'a, 'info> ScanningAccountRetriever<'a, 'info> {
|
||||||
})
|
})
|
||||||
})?;
|
})?;
|
||||||
let n_perps = perp_index_map.len();
|
let n_perps = perp_index_map.len();
|
||||||
|
let perp_oracles_start = perps_start + n_perps;
|
||||||
let serum3_start = perps_start + n_perps;
|
let serum3_start = perp_oracles_start + n_perps;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
banks: AccountInfoRefMut::borrow_slice(&ais[..n_banks])?,
|
banks: AccountInfoRefMut::borrow_slice(&ais[..n_banks])?,
|
||||||
oracles: AccountInfoRef::borrow_slice(&ais[n_banks..2 * n_banks])?,
|
oracles: AccountInfoRef::borrow_slice(&ais[n_banks..2 * n_banks])?,
|
||||||
perp_markets: AccountInfoRef::borrow_slice(&ais[perps_start..perps_start + n_perps])?,
|
perp_markets: AccountInfoRef::borrow_slice(&ais[perps_start..perps_start + n_perps])?,
|
||||||
|
perp_oracles: AccountInfoRef::borrow_slice(
|
||||||
|
&ais[perp_oracles_start..perp_oracles_start + n_perps],
|
||||||
|
)?,
|
||||||
serum3_oos: AccountInfoRef::borrow_slice(&ais[serum3_start..])?,
|
serum3_oos: AccountInfoRef::borrow_slice(&ais[serum3_start..])?,
|
||||||
token_index_map,
|
token_index_map,
|
||||||
perp_index_map,
|
perp_index_map,
|
||||||
|
@ -312,9 +343,15 @@ impl<'a, 'info> ScanningAccountRetriever<'a, 'info> {
|
||||||
Ok((bank, bank.oracle_price(oracle)?))
|
Ok((bank, bank.oracle_price(oracle)?))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scanned_perp_market(&self, perp_market_index: PerpMarketIndex) -> Result<&PerpMarket> {
|
pub fn scanned_perp_market_and_oracle(
|
||||||
|
&self,
|
||||||
|
perp_market_index: PerpMarketIndex,
|
||||||
|
) -> Result<(&PerpMarket, I80F48)> {
|
||||||
let index = self.perp_market_index(perp_market_index)?;
|
let index = self.perp_market_index(perp_market_index)?;
|
||||||
self.perp_markets[index].load_fully_unchecked::<PerpMarket>()
|
let perp_market = self.perp_markets[index].load_fully_unchecked::<PerpMarket>()?;
|
||||||
|
let oracle_acc = &self.perp_oracles[index];
|
||||||
|
let oracle_price = perp_market.oracle_price(oracle_acc)?;
|
||||||
|
Ok((perp_market, oracle_price))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scanned_serum_oo(&self, key: &Pubkey) -> Result<&OpenOrders> {
|
pub fn scanned_serum_oo(&self, key: &Pubkey) -> Result<&OpenOrders> {
|
||||||
|
@ -337,13 +374,13 @@ impl<'a, 'info> AccountRetriever for ScanningAccountRetriever<'a, 'info> {
|
||||||
self.scanned_bank_and_oracle(token_index)
|
self.scanned_bank_and_oracle(token_index)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn perp_market(
|
fn perp_market_and_oracle_price(
|
||||||
&self,
|
&self,
|
||||||
_group: &Pubkey,
|
_group: &Pubkey,
|
||||||
_account_index: usize,
|
_account_index: usize,
|
||||||
perp_market_index: PerpMarketIndex,
|
perp_market_index: PerpMarketIndex,
|
||||||
) -> Result<&PerpMarket> {
|
) -> Result<(&PerpMarket, I80F48)> {
|
||||||
self.scanned_perp_market(perp_market_index)
|
self.scanned_perp_market_and_oracle(perp_market_index)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serum_oo(&self, _account_index: usize, key: &Pubkey) -> Result<&OpenOrders> {
|
fn serum_oo(&self, _account_index: usize, key: &Pubkey) -> Result<&OpenOrders> {
|
||||||
|
@ -484,19 +521,15 @@ pub struct PerpInfo {
|
||||||
pub base: I80F48,
|
pub base: I80F48,
|
||||||
// in health-reference-token native units, no asset/liab factor needed
|
// in health-reference-token native units, no asset/liab factor needed
|
||||||
pub quote: I80F48,
|
pub quote: I80F48,
|
||||||
|
oracle_price: I80F48,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PerpInfo {
|
impl PerpInfo {
|
||||||
fn new(
|
fn new(
|
||||||
perp_position: &PerpPosition,
|
perp_position: &PerpPosition,
|
||||||
perp_market: &PerpMarket,
|
perp_market: &PerpMarket,
|
||||||
token_infos: &[TokenInfo],
|
oracle_price: I80F48,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
// find the TokenInfos for the market's base and quote tokens
|
|
||||||
let base_index = find_token_info_index(token_infos, perp_market.base_token_index)?;
|
|
||||||
// TODO: base_index could be unset
|
|
||||||
let base_info = &token_infos[base_index];
|
|
||||||
|
|
||||||
let base_lot_size = I80F48::from(perp_market.base_lot_size);
|
let base_lot_size = I80F48::from(perp_market.base_lot_size);
|
||||||
let base_lots = cm!(perp_position.base_position_lots() + perp_position.taker_base_lots);
|
let base_lots = cm!(perp_position.base_position_lots() + perp_position.taker_base_lots);
|
||||||
|
|
||||||
|
@ -550,7 +583,7 @@ impl PerpInfo {
|
||||||
let bids_net_lots = cm!(base_lots + perp_position.bids_base_lots);
|
let bids_net_lots = cm!(base_lots + perp_position.bids_base_lots);
|
||||||
let asks_net_lots = cm!(base_lots - perp_position.asks_base_lots);
|
let asks_net_lots = cm!(base_lots - perp_position.asks_base_lots);
|
||||||
|
|
||||||
let lots_to_quote = base_lot_size * base_info.oracle_price;
|
let lots_to_quote = base_lot_size * oracle_price;
|
||||||
let base;
|
let base;
|
||||||
let quote;
|
let quote;
|
||||||
if cm!(bids_net_lots.abs()) > cm!(asks_net_lots.abs()) {
|
if cm!(bids_net_lots.abs()) > cm!(asks_net_lots.abs()) {
|
||||||
|
@ -573,6 +606,7 @@ impl PerpInfo {
|
||||||
maint_liab_weight: perp_market.maint_liab_weight,
|
maint_liab_weight: perp_market.maint_liab_weight,
|
||||||
base,
|
base,
|
||||||
quote,
|
quote,
|
||||||
|
oracle_price,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -723,7 +757,7 @@ impl HealthCache {
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.find(|m| m.perp_market_index == perp_market.perp_market_index)
|
.find(|m| m.perp_market_index == perp_market.perp_market_index)
|
||||||
.ok_or_else(|| error_msg!("perp market {} not found", perp_market.perp_market_index))?;
|
.ok_or_else(|| error_msg!("perp market {} not found", perp_market.perp_market_index))?;
|
||||||
*perp_entry = PerpInfo::new(perp_position, perp_market, &self.token_infos)?;
|
*perp_entry = PerpInfo::new(perp_position, perp_market, perp_entry.oracle_price)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1066,9 +1100,12 @@ pub fn new_health_cache(
|
||||||
// health contribution from perp accounts
|
// health contribution from perp accounts
|
||||||
let mut perp_infos = Vec::with_capacity(account.active_perp_positions().count());
|
let mut perp_infos = Vec::with_capacity(account.active_perp_positions().count());
|
||||||
for (i, perp_position) in account.active_perp_positions().enumerate() {
|
for (i, perp_position) in account.active_perp_positions().enumerate() {
|
||||||
let perp_market =
|
let (perp_market, oracle_price) = retriever.perp_market_and_oracle_price(
|
||||||
retriever.perp_market(&account.fixed.group, i, perp_position.market_index)?;
|
&account.fixed.group,
|
||||||
perp_infos.push(PerpInfo::new(perp_position, perp_market, &token_infos)?);
|
i,
|
||||||
|
perp_position.market_index,
|
||||||
|
)?;
|
||||||
|
perp_infos.push(PerpInfo::new(perp_position, perp_market, oracle_price)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(HealthCache {
|
Ok(HealthCache {
|
||||||
|
@ -1201,15 +1238,15 @@ mod tests {
|
||||||
|
|
||||||
fn mock_perp_market(
|
fn mock_perp_market(
|
||||||
group: Pubkey,
|
group: Pubkey,
|
||||||
|
oracle: Pubkey,
|
||||||
market_index: PerpMarketIndex,
|
market_index: PerpMarketIndex,
|
||||||
base_token: TokenIndex,
|
|
||||||
init_weights: f64,
|
init_weights: f64,
|
||||||
maint_weights: f64,
|
maint_weights: f64,
|
||||||
) -> TestAccount<PerpMarket> {
|
) -> TestAccount<PerpMarket> {
|
||||||
let mut pm = TestAccount::<PerpMarket>::new_zeroed();
|
let mut pm = TestAccount::<PerpMarket>::new_zeroed();
|
||||||
pm.data().group = group;
|
pm.data().group = group;
|
||||||
|
pm.data().oracle = oracle;
|
||||||
pm.data().perp_market_index = market_index;
|
pm.data().perp_market_index = market_index;
|
||||||
pm.data().base_token_index = base_token;
|
|
||||||
pm.data().init_asset_weight = I80F48::from_num(1.0 - init_weights);
|
pm.data().init_asset_weight = I80F48::from_num(1.0 - init_weights);
|
||||||
pm.data().init_liab_weight = I80F48::from_num(1.0 + init_weights);
|
pm.data().init_liab_weight = I80F48::from_num(1.0 + init_weights);
|
||||||
pm.data().maint_asset_weight = I80F48::from_num(1.0 - maint_weights);
|
pm.data().maint_asset_weight = I80F48::from_num(1.0 - maint_weights);
|
||||||
|
@ -1264,7 +1301,7 @@ mod tests {
|
||||||
oo1.data().native_coin_free = 3;
|
oo1.data().native_coin_free = 3;
|
||||||
oo1.data().referrer_rebates_accrued = 2;
|
oo1.data().referrer_rebates_accrued = 2;
|
||||||
|
|
||||||
let mut perp1 = mock_perp_market(group, 9, 4, 0.2, 0.1);
|
let mut perp1 = mock_perp_market(group, oracle2.pubkey, 9, 0.2, 0.1);
|
||||||
let perpaccount = account.ensure_perp_position(9).unwrap().0;
|
let perpaccount = account.ensure_perp_position(9).unwrap().0;
|
||||||
perpaccount.change_base_and_quote_positions(perp1.data(), 3, -I80F48::from(310u16));
|
perpaccount.change_base_and_quote_positions(perp1.data(), 3, -I80F48::from(310u16));
|
||||||
perpaccount.bids_base_lots = 7;
|
perpaccount.bids_base_lots = 7;
|
||||||
|
@ -1272,12 +1309,15 @@ mod tests {
|
||||||
perpaccount.taker_base_lots = 1;
|
perpaccount.taker_base_lots = 1;
|
||||||
perpaccount.taker_quote_lots = 2;
|
perpaccount.taker_quote_lots = 2;
|
||||||
|
|
||||||
|
let oracle2_ai = oracle2.as_account_info();
|
||||||
|
|
||||||
let ais = vec![
|
let ais = vec![
|
||||||
bank1.as_account_info(),
|
bank1.as_account_info(),
|
||||||
bank2.as_account_info(),
|
bank2.as_account_info(),
|
||||||
oracle1.as_account_info(),
|
oracle1.as_account_info(),
|
||||||
oracle2.as_account_info(),
|
oracle2_ai.clone(),
|
||||||
perp1.as_account_info(),
|
perp1.as_account_info(),
|
||||||
|
oracle2_ai,
|
||||||
oo1.as_account_info(),
|
oo1.as_account_info(),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -1298,10 +1338,12 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_scanning_account_retriever() {
|
fn test_scanning_account_retriever() {
|
||||||
|
let oracle1_price = 1.0;
|
||||||
|
let oracle2_price = 5.0;
|
||||||
let group = Pubkey::new_unique();
|
let group = Pubkey::new_unique();
|
||||||
|
|
||||||
let (mut bank1, mut oracle1) = mock_bank_and_oracle(group, 1, 1.0, 0.2, 0.1);
|
let (mut bank1, mut oracle1) = mock_bank_and_oracle(group, 1, oracle1_price, 0.2, 0.1);
|
||||||
let (mut bank2, mut oracle2) = mock_bank_and_oracle(group, 4, 5.0, 0.5, 0.3);
|
let (mut bank2, mut oracle2) = mock_bank_and_oracle(group, 4, oracle2_price, 0.5, 0.3);
|
||||||
let (mut bank3, _) = mock_bank_and_oracle(group, 5, 1.0, 0.5, 0.3);
|
let (mut bank3, _) = mock_bank_and_oracle(group, 5, 1.0, 0.5, 0.3);
|
||||||
|
|
||||||
// bank3 reuses the bank2 oracle, to ensure the ScanningAccountRetriever doesn't choke on that
|
// bank3 reuses the bank2 oracle, to ensure the ScanningAccountRetriever doesn't choke on that
|
||||||
|
@ -1311,17 +1353,22 @@ mod tests {
|
||||||
let oo1key = oo1.pubkey;
|
let oo1key = oo1.pubkey;
|
||||||
oo1.data().native_pc_total = 20;
|
oo1.data().native_pc_total = 20;
|
||||||
|
|
||||||
let mut perp1 = mock_perp_market(group, 9, 4, 0.2, 0.1);
|
let mut perp1 = mock_perp_market(group, oracle2.pubkey, 9, 0.2, 0.1);
|
||||||
|
let mut perp2 = mock_perp_market(group, oracle1.pubkey, 8, 0.2, 0.1);
|
||||||
|
|
||||||
|
let oracle1_account_info = oracle1.as_account_info();
|
||||||
let oracle2_account_info = oracle2.as_account_info();
|
let oracle2_account_info = oracle2.as_account_info();
|
||||||
let ais = vec![
|
let ais = vec![
|
||||||
bank1.as_account_info(),
|
bank1.as_account_info(),
|
||||||
bank2.as_account_info(),
|
bank2.as_account_info(),
|
||||||
bank3.as_account_info(),
|
bank3.as_account_info(),
|
||||||
oracle1.as_account_info(),
|
oracle1_account_info.clone(),
|
||||||
|
oracle2_account_info.clone(),
|
||||||
oracle2_account_info.clone(),
|
oracle2_account_info.clone(),
|
||||||
oracle2_account_info,
|
|
||||||
perp1.as_account_info(),
|
perp1.as_account_info(),
|
||||||
|
perp2.as_account_info(),
|
||||||
|
oracle2_account_info,
|
||||||
|
oracle1_account_info,
|
||||||
oo1.as_account_info(),
|
oo1.as_account_info(),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -1330,8 +1377,9 @@ mod tests {
|
||||||
assert_eq!(retriever.banks.len(), 3);
|
assert_eq!(retriever.banks.len(), 3);
|
||||||
assert_eq!(retriever.token_index_map.len(), 3);
|
assert_eq!(retriever.token_index_map.len(), 3);
|
||||||
assert_eq!(retriever.oracles.len(), 3);
|
assert_eq!(retriever.oracles.len(), 3);
|
||||||
assert_eq!(retriever.perp_markets.len(), 1);
|
assert_eq!(retriever.perp_markets.len(), 2);
|
||||||
assert_eq!(retriever.perp_index_map.len(), 1);
|
assert_eq!(retriever.perp_oracles.len(), 2);
|
||||||
|
assert_eq!(retriever.perp_index_map.len(), 2);
|
||||||
assert_eq!(retriever.serum3_oos.len(), 1);
|
assert_eq!(retriever.serum3_oos.len(), 1);
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -1372,10 +1420,21 @@ mod tests {
|
||||||
|
|
||||||
assert!(retriever.serum_oo(1, &Pubkey::default()).is_err());
|
assert!(retriever.serum_oo(1, &Pubkey::default()).is_err());
|
||||||
|
|
||||||
let perp = retriever.perp_market(&group, 0, 9).unwrap();
|
let (perp, oracle_price) = retriever
|
||||||
|
.perp_market_and_oracle_price(&group, 0, 9)
|
||||||
|
.unwrap();
|
||||||
assert_eq!(identity(perp.perp_market_index), 9);
|
assert_eq!(identity(perp.perp_market_index), 9);
|
||||||
|
assert_eq!(oracle_price, oracle2_price);
|
||||||
|
|
||||||
assert!(retriever.perp_market(&group, 1, 5).is_err());
|
let (perp, oracle_price) = retriever
|
||||||
|
.perp_market_and_oracle_price(&group, 1, 8)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(identity(perp.perp_market_index), 8);
|
||||||
|
assert_eq!(oracle_price, oracle1_price);
|
||||||
|
|
||||||
|
assert!(retriever
|
||||||
|
.perp_market_and_oracle_price(&group, 1, 5)
|
||||||
|
.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -1435,7 +1494,7 @@ mod tests {
|
||||||
oo2.data().native_pc_total = testcase.oo_1_3.0;
|
oo2.data().native_pc_total = testcase.oo_1_3.0;
|
||||||
oo2.data().native_coin_total = testcase.oo_1_3.1;
|
oo2.data().native_coin_total = testcase.oo_1_3.1;
|
||||||
|
|
||||||
let mut perp1 = mock_perp_market(group, 9, 4, 0.2, 0.1);
|
let mut perp1 = mock_perp_market(group, oracle2.pubkey, 9, 0.2, 0.1);
|
||||||
let perpaccount = account.ensure_perp_position(9).unwrap().0;
|
let perpaccount = account.ensure_perp_position(9).unwrap().0;
|
||||||
perpaccount.change_base_and_quote_positions(
|
perpaccount.change_base_and_quote_positions(
|
||||||
perp1.data(),
|
perp1.data(),
|
||||||
|
@ -1445,14 +1504,16 @@ mod tests {
|
||||||
perpaccount.bids_base_lots = testcase.perp1.2;
|
perpaccount.bids_base_lots = testcase.perp1.2;
|
||||||
perpaccount.asks_base_lots = testcase.perp1.3;
|
perpaccount.asks_base_lots = testcase.perp1.3;
|
||||||
|
|
||||||
|
let oracle2_ai = oracle2.as_account_info();
|
||||||
let ais = vec![
|
let ais = vec![
|
||||||
bank1.as_account_info(),
|
bank1.as_account_info(),
|
||||||
bank2.as_account_info(),
|
bank2.as_account_info(),
|
||||||
bank3.as_account_info(),
|
bank3.as_account_info(),
|
||||||
oracle1.as_account_info(),
|
oracle1.as_account_info(),
|
||||||
oracle2.as_account_info(),
|
oracle2_ai.clone(),
|
||||||
oracle3.as_account_info(),
|
oracle3.as_account_info(),
|
||||||
perp1.as_account_info(),
|
perp1.as_account_info(),
|
||||||
|
oracle2_ai,
|
||||||
oo1.as_account_info(),
|
oo1.as_account_info(),
|
||||||
oo2.as_account_info(),
|
oo2.as_account_info(),
|
||||||
];
|
];
|
||||||
|
@ -1785,16 +1846,18 @@ mod tests {
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let mut perp1 = mock_perp_market(group, 9, 1, 0.2, 0.1);
|
let mut perp1 = mock_perp_market(group, oracle1.pubkey, 9, 0.2, 0.1);
|
||||||
perp1.data().long_funding = I80F48::from_num(10.1);
|
perp1.data().long_funding = I80F48::from_num(10.1);
|
||||||
let perpaccount = account.ensure_perp_position(9).unwrap().0;
|
let perpaccount = account.ensure_perp_position(9).unwrap().0;
|
||||||
perpaccount.change_base_and_quote_positions(perp1.data(), 10, I80F48::from(-110));
|
perpaccount.change_base_and_quote_positions(perp1.data(), 10, I80F48::from(-110));
|
||||||
perpaccount.long_settled_funding = I80F48::from_num(10.0);
|
perpaccount.long_settled_funding = I80F48::from_num(10.0);
|
||||||
|
|
||||||
|
let oracle1_ai = oracle1.as_account_info();
|
||||||
let ais = vec![
|
let ais = vec![
|
||||||
bank1.as_account_info(),
|
bank1.as_account_info(),
|
||||||
oracle1.as_account_info(),
|
oracle1_ai.clone(),
|
||||||
perp1.as_account_info(),
|
perp1.as_account_info(),
|
||||||
|
oracle1_ai,
|
||||||
];
|
];
|
||||||
|
|
||||||
let retriever = ScanningAccountRetriever::new(&ais, &group).unwrap();
|
let retriever = ScanningAccountRetriever::new(&ais, &group).unwrap();
|
||||||
|
@ -1811,4 +1874,35 @@ mod tests {
|
||||||
- 1.0
|
- 1.0
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_scanning_retreiver_mismatched_oracle_for_perps_throws_error() {
|
||||||
|
let group = Pubkey::new_unique();
|
||||||
|
|
||||||
|
let (mut bank1, mut oracle1) = mock_bank_and_oracle(group, 1, 1.0, 0.2, 0.1);
|
||||||
|
let (mut bank2, mut oracle2) = mock_bank_and_oracle(group, 4, 5.0, 0.5, 0.3);
|
||||||
|
|
||||||
|
let mut oo1 = TestAccount::<OpenOrders>::new_zeroed();
|
||||||
|
|
||||||
|
let mut perp1 = mock_perp_market(group, oracle1.pubkey, 9, 0.2, 0.1);
|
||||||
|
let mut perp2 = mock_perp_market(group, oracle2.pubkey, 8, 0.2, 0.1);
|
||||||
|
|
||||||
|
let oracle1_account_info = oracle1.as_account_info();
|
||||||
|
let oracle2_account_info = oracle2.as_account_info();
|
||||||
|
let ais = vec![
|
||||||
|
bank1.as_account_info(),
|
||||||
|
bank2.as_account_info(),
|
||||||
|
oracle1_account_info.clone(),
|
||||||
|
oracle2_account_info.clone(),
|
||||||
|
perp1.as_account_info(),
|
||||||
|
perp2.as_account_info(),
|
||||||
|
oracle2_account_info, // Oracles wrong way around
|
||||||
|
oracle1_account_info,
|
||||||
|
oo1.as_account_info(),
|
||||||
|
];
|
||||||
|
|
||||||
|
let retriever = ScanningAccountRetriever::new(&ais, &group).unwrap();
|
||||||
|
let result = retriever.perp_market_and_oracle_price(&group, 0, 9);
|
||||||
|
assert!(result.is_err());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -419,7 +419,6 @@ mod tests {
|
||||||
fn create_perp_market() -> PerpMarket {
|
fn create_perp_market() -> PerpMarket {
|
||||||
return PerpMarket {
|
return PerpMarket {
|
||||||
group: Pubkey::new_unique(),
|
group: Pubkey::new_unique(),
|
||||||
base_token_index: 0,
|
|
||||||
perp_market_index: 0,
|
perp_market_index: 0,
|
||||||
name: Default::default(),
|
name: Default::default(),
|
||||||
oracle: Pubkey::new_unique(),
|
oracle: Pubkey::new_unique(),
|
||||||
|
@ -449,8 +448,9 @@ mod tests {
|
||||||
fees_accrued: I80F48::ZERO,
|
fees_accrued: I80F48::ZERO,
|
||||||
fees_settled: I80F48::ZERO,
|
fees_settled: I80F48::ZERO,
|
||||||
bump: 0,
|
bump: 0,
|
||||||
base_token_decimals: 0,
|
base_decimals: 0,
|
||||||
reserved: [0; 112],
|
reserved: [0; 112],
|
||||||
|
padding0: Default::default(),
|
||||||
padding1: Default::default(),
|
padding1: Default::default(),
|
||||||
padding2: Default::default(),
|
padding2: Default::default(),
|
||||||
registration_time: 0,
|
registration_time: 0,
|
||||||
|
|
|
@ -116,7 +116,7 @@ pub fn determine_oracle_type(acc_info: &impl KeyedAccountReader) -> Result<Oracl
|
||||||
pub fn oracle_price(
|
pub fn oracle_price(
|
||||||
acc_info: &impl KeyedAccountReader,
|
acc_info: &impl KeyedAccountReader,
|
||||||
oracle_conf_filter: I80F48,
|
oracle_conf_filter: I80F48,
|
||||||
base_token_decimals: u8,
|
base_decimals: u8,
|
||||||
) -> Result<I80F48> {
|
) -> Result<I80F48> {
|
||||||
let data = &acc_info.data();
|
let data = &acc_info.data();
|
||||||
let oracle_type = determine_oracle_type(acc_info)?;
|
let oracle_type = determine_oracle_type(acc_info)?;
|
||||||
|
@ -142,8 +142,7 @@ pub fn oracle_price(
|
||||||
return Err(MangoError::SomeError.into());
|
return Err(MangoError::SomeError.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let decimals =
|
let decimals = cm!((price_account.expo as i8) + QUOTE_DECIMALS - (base_decimals as i8));
|
||||||
cm!((price_account.expo as i8) + QUOTE_DECIMALS - (base_token_decimals as i8));
|
|
||||||
let decimal_adj = power_of_ten(decimals);
|
let decimal_adj = power_of_ten(decimals);
|
||||||
cm!(price * decimal_adj)
|
cm!(price * decimal_adj)
|
||||||
}
|
}
|
||||||
|
@ -173,7 +172,7 @@ pub fn oracle_price(
|
||||||
return Err(MangoError::SomeError.into());
|
return Err(MangoError::SomeError.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let decimals = cm!(QUOTE_DECIMALS - (base_token_decimals as i8));
|
let decimals = cm!(QUOTE_DECIMALS - (base_decimals as i8));
|
||||||
let decimal_adj = power_of_ten(decimals);
|
let decimal_adj = power_of_ten(decimals);
|
||||||
cm!(price * decimal_adj)
|
cm!(price * decimal_adj)
|
||||||
}
|
}
|
||||||
|
@ -195,7 +194,7 @@ pub fn oracle_price(
|
||||||
return Err(MangoError::SomeError.into());
|
return Err(MangoError::SomeError.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let decimals = cm!(QUOTE_DECIMALS - (base_token_decimals as i8));
|
let decimals = cm!(QUOTE_DECIMALS - (base_decimals as i8));
|
||||||
let decimal_adj = power_of_ten(decimals);
|
let decimal_adj = power_of_ten(decimals);
|
||||||
cm!(price * decimal_adj)
|
cm!(price * decimal_adj)
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,8 @@ use fixed::types::I80F48;
|
||||||
use static_assertions::const_assert_eq;
|
use static_assertions::const_assert_eq;
|
||||||
|
|
||||||
use crate::accounts_zerocopy::KeyedAccountReader;
|
use crate::accounts_zerocopy::KeyedAccountReader;
|
||||||
|
use crate::state::oracle;
|
||||||
use crate::state::orderbook::order_type::Side;
|
use crate::state::orderbook::order_type::Side;
|
||||||
use crate::state::{oracle, TokenIndex};
|
|
||||||
use crate::util::checked_math as cm;
|
use crate::util::checked_math as cm;
|
||||||
|
|
||||||
use super::{Book, OracleConfig, DAY_I80F48};
|
use super::{Book, OracleConfig, DAY_I80F48};
|
||||||
|
@ -20,9 +20,8 @@ pub struct PerpMarket {
|
||||||
// ABI: Clients rely on this being at offset 8
|
// ABI: Clients rely on this being at offset 8
|
||||||
pub group: Pubkey,
|
pub group: Pubkey,
|
||||||
|
|
||||||
// TODO: Remove!
|
|
||||||
// ABI: Clients rely on this being at offset 40
|
// ABI: Clients rely on this being at offset 40
|
||||||
pub base_token_index: TokenIndex,
|
pub padding0: [u8; 2],
|
||||||
|
|
||||||
/// Lookup indices
|
/// Lookup indices
|
||||||
pub perp_market_index: PerpMarketIndex,
|
pub perp_market_index: PerpMarketIndex,
|
||||||
|
@ -85,7 +84,7 @@ pub struct PerpMarket {
|
||||||
/// PDA bump
|
/// PDA bump
|
||||||
pub bump: u8,
|
pub bump: u8,
|
||||||
|
|
||||||
pub base_token_decimals: u8,
|
pub base_decimals: u8,
|
||||||
|
|
||||||
pub padding2: [u8; 6],
|
pub padding2: [u8; 6],
|
||||||
|
|
||||||
|
@ -125,7 +124,7 @@ impl PerpMarket {
|
||||||
oracle::oracle_price(
|
oracle::oracle_price(
|
||||||
oracle_acc,
|
oracle_acc,
|
||||||
self.oracle_config.conf_filter,
|
self.oracle_config.conf_filter,
|
||||||
self.base_token_decimals,
|
self.base_decimals,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -166,6 +166,14 @@ fn get_perp_market_address_by_index(group: Pubkey, perp_market_index: PerpMarket
|
||||||
.0
|
.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn get_oracle_address_from_perp_market_address(
|
||||||
|
account_loader: &impl ClientAccountLoader,
|
||||||
|
perp_market_address: &Pubkey,
|
||||||
|
) -> Pubkey {
|
||||||
|
let perp_market: PerpMarket = account_loader.load(&perp_market_address).await.unwrap();
|
||||||
|
perp_market.oracle
|
||||||
|
}
|
||||||
|
|
||||||
// all the accounts that instructions like deposit/withdraw need to compute account health
|
// all the accounts that instructions like deposit/withdraw need to compute account health
|
||||||
async fn derive_health_check_remaining_account_metas(
|
async fn derive_health_check_remaining_account_metas(
|
||||||
account_loader: &impl ClientAccountLoader,
|
account_loader: &impl ClientAccountLoader,
|
||||||
|
@ -201,6 +209,14 @@ async fn derive_health_check_remaining_account_metas(
|
||||||
.active_perp_positions()
|
.active_perp_positions()
|
||||||
.map(|perp| get_perp_market_address_by_index(account.fixed.group, perp.market_index));
|
.map(|perp| get_perp_market_address_by_index(account.fixed.group, perp.market_index));
|
||||||
|
|
||||||
|
let mut perp_oracles = vec![];
|
||||||
|
for perp in adjusted_account
|
||||||
|
.active_perp_positions()
|
||||||
|
.map(|perp| get_perp_market_address_by_index(account.fixed.group, perp.market_index))
|
||||||
|
{
|
||||||
|
perp_oracles.push(get_oracle_address_from_perp_market_address(account_loader, &perp).await)
|
||||||
|
}
|
||||||
|
|
||||||
let serum_oos = account.active_serum3_orders().map(|&s| s.open_orders);
|
let serum_oos = account.active_serum3_orders().map(|&s| s.open_orders);
|
||||||
|
|
||||||
let to_account_meta = |pubkey| AccountMeta {
|
let to_account_meta = |pubkey| AccountMeta {
|
||||||
|
@ -218,6 +234,7 @@ async fn derive_health_check_remaining_account_metas(
|
||||||
})
|
})
|
||||||
.chain(oracles.into_iter().map(to_account_meta))
|
.chain(oracles.into_iter().map(to_account_meta))
|
||||||
.chain(perp_markets.map(to_account_meta))
|
.chain(perp_markets.map(to_account_meta))
|
||||||
|
.chain(perp_oracles.into_iter().map(to_account_meta))
|
||||||
.chain(serum_oos.map(to_account_meta))
|
.chain(serum_oos.map(to_account_meta))
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
@ -251,11 +268,17 @@ async fn derive_liquidation_remaining_account_metas(
|
||||||
oracles.push(mint_info.oracle);
|
oracles.push(mint_info.oracle);
|
||||||
}
|
}
|
||||||
|
|
||||||
let perp_markets = liqee
|
let perp_markets: Vec<Pubkey> = liqee
|
||||||
.active_perp_positions()
|
.active_perp_positions()
|
||||||
.chain(liqee.active_perp_positions())
|
.chain(liqee.active_perp_positions())
|
||||||
.map(|perp| get_perp_market_address_by_index(liqee.fixed.group, perp.market_index))
|
.map(|perp| get_perp_market_address_by_index(liqee.fixed.group, perp.market_index))
|
||||||
.unique();
|
.unique()
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut perp_oracles = vec![];
|
||||||
|
for &perp in &perp_markets {
|
||||||
|
perp_oracles.push(get_oracle_address_from_perp_market_address(account_loader, &perp).await)
|
||||||
|
}
|
||||||
|
|
||||||
let serum_oos = liqee
|
let serum_oos = liqee
|
||||||
.active_serum3_orders()
|
.active_serum3_orders()
|
||||||
|
@ -276,7 +299,8 @@ async fn derive_liquidation_remaining_account_metas(
|
||||||
is_signer: false,
|
is_signer: false,
|
||||||
})
|
})
|
||||||
.chain(oracles.into_iter().map(to_account_meta))
|
.chain(oracles.into_iter().map(to_account_meta))
|
||||||
.chain(perp_markets.map(to_account_meta))
|
.chain(perp_markets.into_iter().map(to_account_meta))
|
||||||
|
.chain(perp_oracles.into_iter().map(to_account_meta))
|
||||||
.chain(serum_oos.map(to_account_meta))
|
.chain(serum_oos.map(to_account_meta))
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
@ -2107,8 +2131,7 @@ pub struct PerpCreateMarketInstruction {
|
||||||
pub event_queue: Pubkey,
|
pub event_queue: Pubkey,
|
||||||
pub payer: TestKeypair,
|
pub payer: TestKeypair,
|
||||||
pub perp_market_index: PerpMarketIndex,
|
pub perp_market_index: PerpMarketIndex,
|
||||||
pub base_token_index: TokenIndex,
|
pub base_decimals: u8,
|
||||||
pub base_token_decimals: u8,
|
|
||||||
pub quote_lot_size: i64,
|
pub quote_lot_size: i64,
|
||||||
pub base_lot_size: i64,
|
pub base_lot_size: i64,
|
||||||
pub maint_asset_weight: f32,
|
pub maint_asset_weight: f32,
|
||||||
|
@ -2135,8 +2158,7 @@ impl PerpCreateMarketInstruction {
|
||||||
.create_account_for_type::<EventQueue>(&mango_v4::id())
|
.create_account_for_type::<EventQueue>(&mango_v4::id())
|
||||||
.await,
|
.await,
|
||||||
oracle: base.oracle,
|
oracle: base.oracle,
|
||||||
base_token_index: base.index,
|
base_decimals: base.mint.decimals,
|
||||||
base_token_decimals: base.mint.decimals,
|
|
||||||
..PerpCreateMarketInstruction::default()
|
..PerpCreateMarketInstruction::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2156,7 +2178,6 @@ impl ClientInstruction for PerpCreateMarketInstruction {
|
||||||
conf_filter: I80F48::from_num::<f32>(0.10),
|
conf_filter: I80F48::from_num::<f32>(0.10),
|
||||||
},
|
},
|
||||||
perp_market_index: self.perp_market_index,
|
perp_market_index: self.perp_market_index,
|
||||||
base_token_index_opt: Option::from(self.base_token_index),
|
|
||||||
quote_lot_size: self.quote_lot_size,
|
quote_lot_size: self.quote_lot_size,
|
||||||
base_lot_size: self.base_lot_size,
|
base_lot_size: self.base_lot_size,
|
||||||
maint_asset_weight: self.maint_asset_weight,
|
maint_asset_weight: self.maint_asset_weight,
|
||||||
|
@ -2169,7 +2190,7 @@ impl ClientInstruction for PerpCreateMarketInstruction {
|
||||||
max_funding: 0.05,
|
max_funding: 0.05,
|
||||||
min_funding: 0.05,
|
min_funding: 0.05,
|
||||||
impact_quantity: 100,
|
impact_quantity: 100,
|
||||||
base_token_decimals: self.base_token_decimals,
|
base_decimals: self.base_decimals,
|
||||||
};
|
};
|
||||||
|
|
||||||
let perp_market = Pubkey::find_program_address(
|
let perp_market = Pubkey::find_program_address(
|
||||||
|
@ -2666,6 +2687,7 @@ impl ClientInstruction for PerpLiqForceCancelOrdersInstruction {
|
||||||
account: self.account,
|
account: self.account,
|
||||||
bids: perp_market.bids,
|
bids: perp_market.bids,
|
||||||
asks: perp_market.asks,
|
asks: perp_market.asks,
|
||||||
|
oracle: perp_market.oracle,
|
||||||
};
|
};
|
||||||
let mut instruction = make_instruction(program_id, &accounts, instruction);
|
let mut instruction = make_instruction(program_id, &accounts, instruction);
|
||||||
instruction.accounts.extend(health_check_metas);
|
instruction.accounts.extend(health_check_metas);
|
||||||
|
|
|
@ -221,10 +221,6 @@ async fn test_health_compute_perp() -> Result<(), TransportError> {
|
||||||
liquidation_fee: 0.012,
|
liquidation_fee: 0.012,
|
||||||
maker_fee: 0.0002,
|
maker_fee: 0.0002,
|
||||||
taker_fee: 0.000,
|
taker_fee: 0.000,
|
||||||
// HACK: Currently the base_token_index token needs to be active on the account.
|
|
||||||
// Using token[0] for each market allows us to have multiple perp positions with
|
|
||||||
// just a single token position.
|
|
||||||
base_token_index: tokens[0].index,
|
|
||||||
..PerpCreateMarketInstruction::with_new_book_and_queue(&solana, &token).await
|
..PerpCreateMarketInstruction::with_new_book_and_queue(&solana, &token).await
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
@ -31,7 +31,6 @@ export class PerpMarket {
|
||||||
publicKey: PublicKey,
|
publicKey: PublicKey,
|
||||||
obj: {
|
obj: {
|
||||||
group: PublicKey;
|
group: PublicKey;
|
||||||
baseTokenIndex: number;
|
|
||||||
quoteTokenIndex: number;
|
quoteTokenIndex: number;
|
||||||
perpMarketIndex: number;
|
perpMarketIndex: number;
|
||||||
name: number[];
|
name: number[];
|
||||||
|
@ -59,14 +58,13 @@ export class PerpMarket {
|
||||||
seqNum: any; // TODO: ts complains that this is unknown for whatever reason
|
seqNum: any; // TODO: ts complains that this is unknown for whatever reason
|
||||||
feesAccrued: I80F48Dto;
|
feesAccrued: I80F48Dto;
|
||||||
bump: number;
|
bump: number;
|
||||||
baseTokenDecimals: number;
|
baseDecimals: number;
|
||||||
registrationTime: BN;
|
registrationTime: BN;
|
||||||
},
|
},
|
||||||
): PerpMarket {
|
): PerpMarket {
|
||||||
return new PerpMarket(
|
return new PerpMarket(
|
||||||
publicKey,
|
publicKey,
|
||||||
obj.group,
|
obj.group,
|
||||||
obj.baseTokenIndex,
|
|
||||||
obj.quoteTokenIndex,
|
obj.quoteTokenIndex,
|
||||||
obj.perpMarketIndex,
|
obj.perpMarketIndex,
|
||||||
obj.name,
|
obj.name,
|
||||||
|
@ -94,7 +92,7 @@ export class PerpMarket {
|
||||||
obj.seqNum,
|
obj.seqNum,
|
||||||
obj.feesAccrued,
|
obj.feesAccrued,
|
||||||
obj.bump,
|
obj.bump,
|
||||||
obj.baseTokenDecimals,
|
obj.baseDecimals,
|
||||||
obj.registrationTime,
|
obj.registrationTime,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -102,7 +100,6 @@ export class PerpMarket {
|
||||||
constructor(
|
constructor(
|
||||||
public publicKey: PublicKey,
|
public publicKey: PublicKey,
|
||||||
public group: PublicKey,
|
public group: PublicKey,
|
||||||
public baseTokenIndex: number,
|
|
||||||
public quoteTokenIndex: number,
|
public quoteTokenIndex: number,
|
||||||
public perpMarketIndex: number,
|
public perpMarketIndex: number,
|
||||||
name: number[],
|
name: number[],
|
||||||
|
@ -130,7 +127,7 @@ export class PerpMarket {
|
||||||
seqNum: BN,
|
seqNum: BN,
|
||||||
feesAccrued: I80F48Dto,
|
feesAccrued: I80F48Dto,
|
||||||
bump: number,
|
bump: number,
|
||||||
public baseTokenDecimals: number,
|
public baseDecimals: number,
|
||||||
public registrationTime: BN,
|
public registrationTime: BN,
|
||||||
) {
|
) {
|
||||||
this.name = utf8.decode(new Uint8Array(name)).split('\x00')[0];
|
this.name = utf8.decode(new Uint8Array(name)).split('\x00')[0];
|
||||||
|
|
|
@ -1273,8 +1273,7 @@ export class MangoClient {
|
||||||
perpMarketIndex: number,
|
perpMarketIndex: number,
|
||||||
name: string,
|
name: string,
|
||||||
oracleConfFilter: number,
|
oracleConfFilter: number,
|
||||||
baseTokenIndex: number,
|
baseDecimals: number,
|
||||||
baseTokenDecimals: number,
|
|
||||||
quoteTokenIndex: number,
|
quoteTokenIndex: number,
|
||||||
quoteLotSize: number,
|
quoteLotSize: number,
|
||||||
baseLotSize: number,
|
baseLotSize: number,
|
||||||
|
@ -1302,8 +1301,7 @@ export class MangoClient {
|
||||||
val: I80F48.fromNumber(oracleConfFilter).getData(),
|
val: I80F48.fromNumber(oracleConfFilter).getData(),
|
||||||
},
|
},
|
||||||
} as any, // future: nested custom types dont typecheck, fix if possible?
|
} as any, // future: nested custom types dont typecheck, fix if possible?
|
||||||
baseTokenIndex,
|
baseDecimals,
|
||||||
baseTokenDecimals,
|
|
||||||
new BN(quoteLotSize),
|
new BN(quoteLotSize),
|
||||||
new BN(baseLotSize),
|
new BN(baseLotSize),
|
||||||
maintAssetWeight,
|
maintAssetWeight,
|
||||||
|
@ -1374,8 +1372,7 @@ export class MangoClient {
|
||||||
perpMarketName: string,
|
perpMarketName: string,
|
||||||
oracle: PublicKey,
|
oracle: PublicKey,
|
||||||
oracleConfFilter: number,
|
oracleConfFilter: number,
|
||||||
baseTokenIndex: number,
|
baseDecimals: number,
|
||||||
baseTokenDecimals: number,
|
|
||||||
maintAssetWeight: number,
|
maintAssetWeight: number,
|
||||||
initAssetWeight: number,
|
initAssetWeight: number,
|
||||||
maintLiabWeight: number,
|
maintLiabWeight: number,
|
||||||
|
@ -1397,8 +1394,7 @@ export class MangoClient {
|
||||||
val: I80F48.fromNumber(oracleConfFilter).getData(),
|
val: I80F48.fromNumber(oracleConfFilter).getData(),
|
||||||
},
|
},
|
||||||
} as any, // future: nested custom types dont typecheck, fix if possible?
|
} as any, // future: nested custom types dont typecheck, fix if possible?
|
||||||
baseTokenIndex,
|
baseDecimals,
|
||||||
baseTokenDecimals,
|
|
||||||
maintAssetWeight,
|
maintAssetWeight,
|
||||||
initAssetWeight,
|
initAssetWeight,
|
||||||
maintLiabWeight,
|
maintLiabWeight,
|
||||||
|
@ -1439,10 +1435,7 @@ export class MangoClient {
|
||||||
.rpc();
|
.rpc();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async perpGetMarkets(
|
public async perpGetMarkets(group: Group): Promise<PerpMarket[]> {
|
||||||
group: Group,
|
|
||||||
baseTokenIndex?: number,
|
|
||||||
): Promise<PerpMarket[]> {
|
|
||||||
const bumpfbuf = Buffer.alloc(1);
|
const bumpfbuf = Buffer.alloc(1);
|
||||||
bumpfbuf.writeUInt8(255);
|
bumpfbuf.writeUInt8(255);
|
||||||
|
|
||||||
|
@ -1455,17 +1448,6 @@ export class MangoClient {
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
if (baseTokenIndex) {
|
|
||||||
const bbuf = Buffer.alloc(2);
|
|
||||||
bbuf.writeUInt16LE(baseTokenIndex);
|
|
||||||
filters.push({
|
|
||||||
memcmp: {
|
|
||||||
bytes: bs58.encode(bbuf),
|
|
||||||
offset: 40,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return (await this.program.account.perpMarket.all(filters)).map((tuple) =>
|
return (await this.program.account.perpMarket.all(filters)).map((tuple) =>
|
||||||
PerpMarket.from(tuple.publicKey, tuple.account),
|
PerpMarket.from(tuple.publicKey, tuple.account),
|
||||||
);
|
);
|
||||||
|
@ -1910,6 +1892,18 @@ export class MangoClient {
|
||||||
)[0].publicKey,
|
)[0].publicKey,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
healthRemainingAccounts.push(
|
||||||
|
...mangoAccount.perps
|
||||||
|
.filter((perp) => perp.marketIndex !== 65535)
|
||||||
|
.map(
|
||||||
|
(perp) =>
|
||||||
|
Array.from(group.perpMarketsMap.values()).filter(
|
||||||
|
(perpMarket) => perpMarket.perpMarketIndex === perp.marketIndex,
|
||||||
|
)[0].oracle,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
for (const perpMarket of perpMarkets) {
|
for (const perpMarket of perpMarkets) {
|
||||||
const alreadyAdded = mangoAccount.perps.find(
|
const alreadyAdded = mangoAccount.perps.find(
|
||||||
(p) => p.marketIndex === perpMarket.perpMarketIndex,
|
(p) => p.marketIndex === perpMarket.perpMarketIndex,
|
||||||
|
@ -1968,23 +1962,17 @@ export class MangoClient {
|
||||||
...mintInfos.map((mintInfo) => mintInfo.oracle),
|
...mintInfos.map((mintInfo) => mintInfo.oracle),
|
||||||
);
|
);
|
||||||
|
|
||||||
for (const mangoAccount of mangoAccounts) {
|
const perpsToAdd: PerpMarket[] = [];
|
||||||
healthRemainingAccounts.push(
|
|
||||||
...mangoAccount.serum3
|
|
||||||
.filter((serum3Account) => serum3Account.marketIndex !== 65535)
|
|
||||||
.map((serum3Account) => serum3Account.openOrders),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const mangoAccount of mangoAccounts) {
|
for (const mangoAccount of mangoAccounts) {
|
||||||
healthRemainingAccounts.push(
|
perpsToAdd.push(
|
||||||
...mangoAccount.perps
|
...mangoAccount.perps
|
||||||
.filter((perp) => perp.marketIndex !== 65535)
|
.filter((perp) => perp.marketIndex !== 65535)
|
||||||
.map(
|
.map(
|
||||||
(perp) =>
|
(perp) =>
|
||||||
Array.from(group.perpMarketsMap.values()).filter(
|
Array.from(group.perpMarketsMap.values()).filter(
|
||||||
(perpMarket) => perpMarket.perpMarketIndex === perp.marketIndex,
|
(perpMarket) => perpMarket.perpMarketIndex === perp.marketIndex,
|
||||||
)[0].publicKey,
|
)[0],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1994,15 +1982,28 @@ export class MangoClient {
|
||||||
(p) => p.marketIndex === perpMarket.perpMarketIndex,
|
(p) => p.marketIndex === perpMarket.perpMarketIndex,
|
||||||
);
|
);
|
||||||
if (!alreadyAdded) {
|
if (!alreadyAdded) {
|
||||||
healthRemainingAccounts.push(
|
perpsToAdd.push(
|
||||||
Array.from(group.perpMarketsMap.values()).filter(
|
Array.from(group.perpMarketsMap.values()).filter(
|
||||||
(p) => p.perpMarketIndex === perpMarket.perpMarketIndex,
|
(p) => p.perpMarketIndex === perpMarket.perpMarketIndex,
|
||||||
)[0].publicKey,
|
)[0],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add perp accounts
|
||||||
|
healthRemainingAccounts.push(...perpsToAdd.map((p) => p.publicKey));
|
||||||
|
// Add oracle for each perp
|
||||||
|
healthRemainingAccounts.push(...perpsToAdd.map((p) => p.oracle));
|
||||||
|
|
||||||
|
for (const mangoAccount of mangoAccounts) {
|
||||||
|
healthRemainingAccounts.push(
|
||||||
|
...mangoAccount.serum3
|
||||||
|
.filter((serum3Account) => serum3Account.marketIndex !== 65535)
|
||||||
|
.map((serum3Account) => serum3Account.openOrders),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return healthRemainingAccounts;
|
return healthRemainingAccounts;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2257,13 +2257,7 @@ export type MangoV4 = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "baseTokenIndexOpt",
|
"name": "baseDecimals",
|
||||||
"type": {
|
|
||||||
"option": "u16"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "baseTokenDecimals",
|
|
||||||
"type": "u8"
|
"type": "u8"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -2351,13 +2345,7 @@ export type MangoV4 = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "baseTokenIndexOpt",
|
"name": "baseDecimalsOpt",
|
||||||
"type": {
|
|
||||||
"option": "u16"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "baseTokenDecimalsOpt",
|
|
||||||
"type": {
|
"type": {
|
||||||
"option": "u8"
|
"option": "u8"
|
||||||
}
|
}
|
||||||
|
@ -2953,6 +2941,11 @@ export type MangoV4 = {
|
||||||
"name": "bids",
|
"name": "bids",
|
||||||
"isMut": true,
|
"isMut": true,
|
||||||
"isSigner": false
|
"isSigner": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "oracle",
|
||||||
|
"isMut": false,
|
||||||
|
"isSigner": false
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"args": [
|
"args": [
|
||||||
|
@ -3726,8 +3719,13 @@ export type MangoV4 = {
|
||||||
"type": "publicKey"
|
"type": "publicKey"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "baseTokenIndex",
|
"name": "padding0",
|
||||||
"type": "u16"
|
"type": {
|
||||||
|
"array": [
|
||||||
|
"u8",
|
||||||
|
2
|
||||||
|
]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "perpMarketIndex",
|
"name": "perpMarketIndex",
|
||||||
|
@ -3901,7 +3899,7 @@ export type MangoV4 = {
|
||||||
"type": "u8"
|
"type": "u8"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "baseTokenDecimals",
|
"name": "baseDecimals",
|
||||||
"type": "u8"
|
"type": "u8"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -4293,6 +4291,12 @@ export type MangoV4 = {
|
||||||
"type": {
|
"type": {
|
||||||
"defined": "I80F48"
|
"defined": "I80F48"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "oraclePrice",
|
||||||
|
"type": {
|
||||||
|
"defined": "I80F48"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -8256,13 +8260,7 @@ export const IDL: MangoV4 = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "baseTokenIndexOpt",
|
"name": "baseDecimals",
|
||||||
"type": {
|
|
||||||
"option": "u16"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "baseTokenDecimals",
|
|
||||||
"type": "u8"
|
"type": "u8"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -8350,13 +8348,7 @@ export const IDL: MangoV4 = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "baseTokenIndexOpt",
|
"name": "baseDecimalsOpt",
|
||||||
"type": {
|
|
||||||
"option": "u16"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "baseTokenDecimalsOpt",
|
|
||||||
"type": {
|
"type": {
|
||||||
"option": "u8"
|
"option": "u8"
|
||||||
}
|
}
|
||||||
|
@ -8952,6 +8944,11 @@ export const IDL: MangoV4 = {
|
||||||
"name": "bids",
|
"name": "bids",
|
||||||
"isMut": true,
|
"isMut": true,
|
||||||
"isSigner": false
|
"isSigner": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "oracle",
|
||||||
|
"isMut": false,
|
||||||
|
"isSigner": false
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"args": [
|
"args": [
|
||||||
|
@ -9725,8 +9722,13 @@ export const IDL: MangoV4 = {
|
||||||
"type": "publicKey"
|
"type": "publicKey"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "baseTokenIndex",
|
"name": "padding0",
|
||||||
"type": "u16"
|
"type": {
|
||||||
|
"array": [
|
||||||
|
"u8",
|
||||||
|
2
|
||||||
|
]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "perpMarketIndex",
|
"name": "perpMarketIndex",
|
||||||
|
@ -9900,7 +9902,7 @@ export const IDL: MangoV4 = {
|
||||||
"type": "u8"
|
"type": "u8"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "baseTokenDecimals",
|
"name": "baseDecimals",
|
||||||
"type": "u8"
|
"type": "u8"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -10292,6 +10294,12 @@ export const IDL: MangoV4 = {
|
||||||
"type": {
|
"type": {
|
||||||
"defined": "I80F48"
|
"defined": "I80F48"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "oraclePrice",
|
||||||
|
"type": {
|
||||||
|
"defined": "I80F48"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue