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 {
|
||||
ais: accounts,
|
||||
n_banks: active_token_len,
|
||||
n_perps: active_perp_len,
|
||||
begin_perp: active_token_len * 2,
|
||||
begin_serum3: active_token_len * 2 + active_perp_len,
|
||||
};
|
||||
|
|
|
@ -49,8 +49,7 @@ pub fn perp_create_market(
|
|||
perp_market_index: PerpMarketIndex,
|
||||
name: String,
|
||||
oracle_config: OracleConfig,
|
||||
base_token_index_opt: Option<TokenIndex>,
|
||||
base_token_decimals: u8,
|
||||
base_decimals: u8,
|
||||
quote_lot_size: i64,
|
||||
base_lot_size: i64,
|
||||
maint_asset_weight: f32,
|
||||
|
@ -94,10 +93,10 @@ pub fn perp_create_market(
|
|||
fees_settled: I80F48::ZERO,
|
||||
// Why optional - Perp could be based purely on an oracle
|
||||
bump: *ctx.bumps.get("perp_market").ok_or(MangoError::SomeError)?,
|
||||
base_token_decimals,
|
||||
base_decimals,
|
||||
perp_market_index,
|
||||
base_token_index: base_token_index_opt.ok_or(TokenIndex::MAX).unwrap(),
|
||||
registration_time: Clock::get()?.unix_timestamp,
|
||||
padding0: Default::default(),
|
||||
padding1: Default::default(),
|
||||
padding2: Default::default(),
|
||||
reserved: [0; 112],
|
||||
|
|
|
@ -22,8 +22,7 @@ pub fn perp_edit_market(
|
|||
ctx: Context<PerpEditMarket>,
|
||||
oracle_opt: Option<Pubkey>,
|
||||
oracle_config_opt: Option<OracleConfig>,
|
||||
base_token_index_opt: Option<TokenIndex>,
|
||||
base_token_decimals_opt: Option<u8>,
|
||||
base_decimals_opt: Option<u8>,
|
||||
maint_asset_weight_opt: Option<f32>,
|
||||
init_asset_weight_opt: Option<f32>,
|
||||
maint_liab_weight_opt: Option<f32>,
|
||||
|
@ -100,17 +99,13 @@ pub fn perp_edit_market(
|
|||
// fees_accrued
|
||||
// bump
|
||||
|
||||
if let Some(base_token_decimals) = base_token_decimals_opt {
|
||||
perp_market.base_token_decimals = base_token_decimals;
|
||||
if let Some(base_decimals) = base_decimals_opt {
|
||||
perp_market.base_decimals = base_decimals;
|
||||
}
|
||||
|
||||
// unchanged -
|
||||
// perp_market_index
|
||||
|
||||
if let Some(base_token_index) = base_token_index_opt {
|
||||
perp_market.base_token_index = base_token_index;
|
||||
}
|
||||
|
||||
// unchanged -
|
||||
// quote_token_index
|
||||
|
||||
|
|
|
@ -22,6 +22,9 @@ pub struct PerpLiqForceCancelOrders<'info> {
|
|||
pub asks: AccountLoader<'info, BookSide>,
|
||||
#[account(mut)]
|
||||
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(
|
||||
|
|
|
@ -382,8 +382,7 @@ pub mod mango_v4 {
|
|||
perp_market_index: PerpMarketIndex,
|
||||
name: String,
|
||||
oracle_config: OracleConfig,
|
||||
base_token_index_opt: Option<TokenIndex>,
|
||||
base_token_decimals: u8,
|
||||
base_decimals: u8,
|
||||
quote_lot_size: i64,
|
||||
base_lot_size: i64,
|
||||
maint_asset_weight: f32,
|
||||
|
@ -402,8 +401,7 @@ pub mod mango_v4 {
|
|||
perp_market_index,
|
||||
name,
|
||||
oracle_config,
|
||||
base_token_index_opt,
|
||||
base_token_decimals,
|
||||
base_decimals,
|
||||
quote_lot_size,
|
||||
base_lot_size,
|
||||
maint_asset_weight,
|
||||
|
@ -424,8 +422,7 @@ pub mod mango_v4 {
|
|||
ctx: Context<PerpEditMarket>,
|
||||
oracle_opt: Option<Pubkey>,
|
||||
oracle_config_opt: Option<OracleConfig>,
|
||||
base_token_index_opt: Option<TokenIndex>,
|
||||
base_token_decimals_opt: Option<u8>,
|
||||
base_decimals_opt: Option<u8>,
|
||||
maint_asset_weight_opt: Option<f32>,
|
||||
init_asset_weight_opt: Option<f32>,
|
||||
maint_liab_weight_opt: Option<f32>,
|
||||
|
@ -441,8 +438,7 @@ pub mod mango_v4 {
|
|||
ctx,
|
||||
oracle_opt,
|
||||
oracle_config_opt,
|
||||
base_token_index_opt,
|
||||
base_token_decimals_opt,
|
||||
base_decimals_opt,
|
||||
maint_asset_weight_opt,
|
||||
init_asset_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 perp_market(
|
||||
fn perp_market_and_oracle_price(
|
||||
&self,
|
||||
group: &Pubkey,
|
||||
account_index: usize,
|
||||
perp_market_index: PerpMarketIndex,
|
||||
) -> Result<&PerpMarket>;
|
||||
) -> Result<(&PerpMarket, I80F48)>;
|
||||
}
|
||||
|
||||
/// 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()
|
||||
/// 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()
|
||||
/// 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 ais: Vec<T>,
|
||||
pub n_banks: usize,
|
||||
pub n_perps: usize,
|
||||
pub begin_perp: 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_perp_len = account.active_perp_positions().count();
|
||||
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
|
||||
require_eq!(ais.len(), expected_ais);
|
||||
|
||||
Ok(FixedOrderAccountRetriever {
|
||||
ais: AccountInfoRef::borrow_slice(ais)?,
|
||||
n_banks: active_token_len,
|
||||
n_perps: active_perp_len,
|
||||
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)
|
||||
}
|
||||
|
||||
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> {
|
||||
let oracle = &self.ais[cm!(self.n_banks + account_index)];
|
||||
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> {
|
||||
|
@ -126,27 +147,32 @@ impl<T: KeyedAccountReader> AccountRetriever for FixedOrderAccountRetriever<T> {
|
|||
Ok((bank, oracle_price))
|
||||
}
|
||||
|
||||
fn perp_market(
|
||||
fn perp_market_and_oracle_price(
|
||||
&self,
|
||||
group: &Pubkey,
|
||||
account_index: usize,
|
||||
perp_market_index: PerpMarketIndex,
|
||||
) -> Result<&PerpMarket> {
|
||||
let ai = &self.ais[cm!(self.begin_perp + account_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(|| {
|
||||
) -> Result<(&PerpMarket, I80F48)> {
|
||||
let perp_market = self
|
||||
.perp_market(group, account_index, perp_market_index)
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"loading perp market with health account index {} and perp market index {}, passed account {}",
|
||||
account_index,
|
||||
perp_market_index,
|
||||
self.ais[self.begin_perp + account_index].key(),
|
||||
)
|
||||
})?;
|
||||
|
||||
let oracle_price = self.oracle_price_perp(account_index, perp_market).with_context(|| {
|
||||
format!(
|
||||
"loading perp market with health account index {} and perp market index {}, passed account {}",
|
||||
"getting oracle for perp market with health account index {} and perp market index {}, passed account {}",
|
||||
account_index,
|
||||
perp_market_index,
|
||||
ai.key(),
|
||||
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> {
|
||||
|
@ -169,6 +195,7 @@ impl<T: KeyedAccountReader> AccountRetriever for FixedOrderAccountRetriever<T> {
|
|||
/// - 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 PerpMarket accounts
|
||||
/// - the same number of oracles in the same order as the perp markets
|
||||
/// - an unknown number of serum3 OpenOrders accounts
|
||||
/// and retrieves accounts needed for the health computation by doing a linear
|
||||
/// scan for each request.
|
||||
|
@ -176,6 +203,7 @@ pub struct ScanningAccountRetriever<'a, 'info> {
|
|||
banks: Vec<AccountInfoRefMut<'a, 'info>>,
|
||||
oracles: Vec<AccountInfoRef<'a, 'info>>,
|
||||
perp_markets: Vec<AccountInfoRef<'a, 'info>>,
|
||||
perp_oracles: Vec<AccountInfoRef<'a, 'info>>,
|
||||
serum3_oos: Vec<AccountInfoRef<'a, 'info>>,
|
||||
token_index_map: HashMap<TokenIndex, 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 serum3_start = perps_start + n_perps;
|
||||
let perp_oracles_start = perps_start + n_perps;
|
||||
let serum3_start = perp_oracles_start + n_perps;
|
||||
|
||||
Ok(Self {
|
||||
banks: AccountInfoRefMut::borrow_slice(&ais[..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_oracles: AccountInfoRef::borrow_slice(
|
||||
&ais[perp_oracles_start..perp_oracles_start + n_perps],
|
||||
)?,
|
||||
serum3_oos: AccountInfoRef::borrow_slice(&ais[serum3_start..])?,
|
||||
token_index_map,
|
||||
perp_index_map,
|
||||
|
@ -312,9 +343,15 @@ impl<'a, 'info> ScanningAccountRetriever<'a, 'info> {
|
|||
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)?;
|
||||
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> {
|
||||
|
@ -337,13 +374,13 @@ impl<'a, 'info> AccountRetriever for ScanningAccountRetriever<'a, 'info> {
|
|||
self.scanned_bank_and_oracle(token_index)
|
||||
}
|
||||
|
||||
fn perp_market(
|
||||
fn perp_market_and_oracle_price(
|
||||
&self,
|
||||
_group: &Pubkey,
|
||||
_account_index: usize,
|
||||
perp_market_index: PerpMarketIndex,
|
||||
) -> Result<&PerpMarket> {
|
||||
self.scanned_perp_market(perp_market_index)
|
||||
) -> Result<(&PerpMarket, I80F48)> {
|
||||
self.scanned_perp_market_and_oracle(perp_market_index)
|
||||
}
|
||||
|
||||
fn serum_oo(&self, _account_index: usize, key: &Pubkey) -> Result<&OpenOrders> {
|
||||
|
@ -484,19 +521,15 @@ pub struct PerpInfo {
|
|||
pub base: I80F48,
|
||||
// in health-reference-token native units, no asset/liab factor needed
|
||||
pub quote: I80F48,
|
||||
oracle_price: I80F48,
|
||||
}
|
||||
|
||||
impl PerpInfo {
|
||||
fn new(
|
||||
perp_position: &PerpPosition,
|
||||
perp_market: &PerpMarket,
|
||||
token_infos: &[TokenInfo],
|
||||
oracle_price: I80F48,
|
||||
) -> 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_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 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 quote;
|
||||
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,
|
||||
base,
|
||||
quote,
|
||||
oracle_price,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -723,7 +757,7 @@ impl HealthCache {
|
|||
.iter_mut()
|
||||
.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))?;
|
||||
*perp_entry = PerpInfo::new(perp_position, perp_market, &self.token_infos)?;
|
||||
*perp_entry = PerpInfo::new(perp_position, perp_market, perp_entry.oracle_price)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -1066,9 +1100,12 @@ pub fn new_health_cache(
|
|||
// health contribution from perp accounts
|
||||
let mut perp_infos = Vec::with_capacity(account.active_perp_positions().count());
|
||||
for (i, perp_position) in account.active_perp_positions().enumerate() {
|
||||
let perp_market =
|
||||
retriever.perp_market(&account.fixed.group, i, perp_position.market_index)?;
|
||||
perp_infos.push(PerpInfo::new(perp_position, perp_market, &token_infos)?);
|
||||
let (perp_market, oracle_price) = retriever.perp_market_and_oracle_price(
|
||||
&account.fixed.group,
|
||||
i,
|
||||
perp_position.market_index,
|
||||
)?;
|
||||
perp_infos.push(PerpInfo::new(perp_position, perp_market, oracle_price)?);
|
||||
}
|
||||
|
||||
Ok(HealthCache {
|
||||
|
@ -1201,15 +1238,15 @@ mod tests {
|
|||
|
||||
fn mock_perp_market(
|
||||
group: Pubkey,
|
||||
oracle: Pubkey,
|
||||
market_index: PerpMarketIndex,
|
||||
base_token: TokenIndex,
|
||||
init_weights: f64,
|
||||
maint_weights: f64,
|
||||
) -> TestAccount<PerpMarket> {
|
||||
let mut pm = TestAccount::<PerpMarket>::new_zeroed();
|
||||
pm.data().group = group;
|
||||
pm.data().oracle = oracle;
|
||||
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_liab_weight = I80F48::from_num(1.0 + init_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().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;
|
||||
perpaccount.change_base_and_quote_positions(perp1.data(), 3, -I80F48::from(310u16));
|
||||
perpaccount.bids_base_lots = 7;
|
||||
|
@ -1272,12 +1309,15 @@ mod tests {
|
|||
perpaccount.taker_base_lots = 1;
|
||||
perpaccount.taker_quote_lots = 2;
|
||||
|
||||
let oracle2_ai = oracle2.as_account_info();
|
||||
|
||||
let ais = vec![
|
||||
bank1.as_account_info(),
|
||||
bank2.as_account_info(),
|
||||
oracle1.as_account_info(),
|
||||
oracle2.as_account_info(),
|
||||
oracle2_ai.clone(),
|
||||
perp1.as_account_info(),
|
||||
oracle2_ai,
|
||||
oo1.as_account_info(),
|
||||
];
|
||||
|
||||
|
@ -1298,10 +1338,12 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_scanning_account_retriever() {
|
||||
let oracle1_price = 1.0;
|
||||
let oracle2_price = 5.0;
|
||||
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 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, oracle2_price, 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
|
||||
|
@ -1311,17 +1353,22 @@ mod tests {
|
|||
let oo1key = oo1.pubkey;
|
||||
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 ais = vec![
|
||||
bank1.as_account_info(),
|
||||
bank2.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,
|
||||
perp1.as_account_info(),
|
||||
perp2.as_account_info(),
|
||||
oracle2_account_info,
|
||||
oracle1_account_info,
|
||||
oo1.as_account_info(),
|
||||
];
|
||||
|
||||
|
@ -1330,8 +1377,9 @@ mod tests {
|
|||
assert_eq!(retriever.banks.len(), 3);
|
||||
assert_eq!(retriever.token_index_map.len(), 3);
|
||||
assert_eq!(retriever.oracles.len(), 3);
|
||||
assert_eq!(retriever.perp_markets.len(), 1);
|
||||
assert_eq!(retriever.perp_index_map.len(), 1);
|
||||
assert_eq!(retriever.perp_markets.len(), 2);
|
||||
assert_eq!(retriever.perp_oracles.len(), 2);
|
||||
assert_eq!(retriever.perp_index_map.len(), 2);
|
||||
assert_eq!(retriever.serum3_oos.len(), 1);
|
||||
|
||||
{
|
||||
|
@ -1372,10 +1420,21 @@ mod tests {
|
|||
|
||||
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!(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)]
|
||||
|
@ -1435,7 +1494,7 @@ mod tests {
|
|||
oo2.data().native_pc_total = testcase.oo_1_3.0;
|
||||
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;
|
||||
perpaccount.change_base_and_quote_positions(
|
||||
perp1.data(),
|
||||
|
@ -1445,14 +1504,16 @@ mod tests {
|
|||
perpaccount.bids_base_lots = testcase.perp1.2;
|
||||
perpaccount.asks_base_lots = testcase.perp1.3;
|
||||
|
||||
let oracle2_ai = oracle2.as_account_info();
|
||||
let ais = vec![
|
||||
bank1.as_account_info(),
|
||||
bank2.as_account_info(),
|
||||
bank3.as_account_info(),
|
||||
oracle1.as_account_info(),
|
||||
oracle2.as_account_info(),
|
||||
oracle2_ai.clone(),
|
||||
oracle3.as_account_info(),
|
||||
perp1.as_account_info(),
|
||||
oracle2_ai,
|
||||
oo1.as_account_info(),
|
||||
oo2.as_account_info(),
|
||||
];
|
||||
|
@ -1785,16 +1846,18 @@ mod tests {
|
|||
)
|
||||
.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);
|
||||
let perpaccount = account.ensure_perp_position(9).unwrap().0;
|
||||
perpaccount.change_base_and_quote_positions(perp1.data(), 10, I80F48::from(-110));
|
||||
perpaccount.long_settled_funding = I80F48::from_num(10.0);
|
||||
|
||||
let oracle1_ai = oracle1.as_account_info();
|
||||
let ais = vec![
|
||||
bank1.as_account_info(),
|
||||
oracle1.as_account_info(),
|
||||
oracle1_ai.clone(),
|
||||
perp1.as_account_info(),
|
||||
oracle1_ai,
|
||||
];
|
||||
|
||||
let retriever = ScanningAccountRetriever::new(&ais, &group).unwrap();
|
||||
|
@ -1811,4 +1874,35 @@ mod tests {
|
|||
- 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 {
|
||||
return PerpMarket {
|
||||
group: Pubkey::new_unique(),
|
||||
base_token_index: 0,
|
||||
perp_market_index: 0,
|
||||
name: Default::default(),
|
||||
oracle: Pubkey::new_unique(),
|
||||
|
@ -449,8 +448,9 @@ mod tests {
|
|||
fees_accrued: I80F48::ZERO,
|
||||
fees_settled: I80F48::ZERO,
|
||||
bump: 0,
|
||||
base_token_decimals: 0,
|
||||
base_decimals: 0,
|
||||
reserved: [0; 112],
|
||||
padding0: Default::default(),
|
||||
padding1: Default::default(),
|
||||
padding2: Default::default(),
|
||||
registration_time: 0,
|
||||
|
|
|
@ -116,7 +116,7 @@ pub fn determine_oracle_type(acc_info: &impl KeyedAccountReader) -> Result<Oracl
|
|||
pub fn oracle_price(
|
||||
acc_info: &impl KeyedAccountReader,
|
||||
oracle_conf_filter: I80F48,
|
||||
base_token_decimals: u8,
|
||||
base_decimals: u8,
|
||||
) -> Result<I80F48> {
|
||||
let data = &acc_info.data();
|
||||
let oracle_type = determine_oracle_type(acc_info)?;
|
||||
|
@ -142,8 +142,7 @@ pub fn oracle_price(
|
|||
return Err(MangoError::SomeError.into());
|
||||
}
|
||||
|
||||
let decimals =
|
||||
cm!((price_account.expo as i8) + QUOTE_DECIMALS - (base_token_decimals as i8));
|
||||
let decimals = cm!((price_account.expo as i8) + QUOTE_DECIMALS - (base_decimals as i8));
|
||||
let decimal_adj = power_of_ten(decimals);
|
||||
cm!(price * decimal_adj)
|
||||
}
|
||||
|
@ -173,7 +172,7 @@ pub fn oracle_price(
|
|||
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);
|
||||
cm!(price * decimal_adj)
|
||||
}
|
||||
|
@ -195,7 +194,7 @@ pub fn oracle_price(
|
|||
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);
|
||||
cm!(price * decimal_adj)
|
||||
}
|
||||
|
|
|
@ -6,8 +6,8 @@ use fixed::types::I80F48;
|
|||
use static_assertions::const_assert_eq;
|
||||
|
||||
use crate::accounts_zerocopy::KeyedAccountReader;
|
||||
use crate::state::oracle;
|
||||
use crate::state::orderbook::order_type::Side;
|
||||
use crate::state::{oracle, TokenIndex};
|
||||
use crate::util::checked_math as cm;
|
||||
|
||||
use super::{Book, OracleConfig, DAY_I80F48};
|
||||
|
@ -20,9 +20,8 @@ pub struct PerpMarket {
|
|||
// ABI: Clients rely on this being at offset 8
|
||||
pub group: Pubkey,
|
||||
|
||||
// TODO: Remove!
|
||||
// ABI: Clients rely on this being at offset 40
|
||||
pub base_token_index: TokenIndex,
|
||||
pub padding0: [u8; 2],
|
||||
|
||||
/// Lookup indices
|
||||
pub perp_market_index: PerpMarketIndex,
|
||||
|
@ -85,7 +84,7 @@ pub struct PerpMarket {
|
|||
/// PDA bump
|
||||
pub bump: u8,
|
||||
|
||||
pub base_token_decimals: u8,
|
||||
pub base_decimals: u8,
|
||||
|
||||
pub padding2: [u8; 6],
|
||||
|
||||
|
@ -125,7 +124,7 @@ impl PerpMarket {
|
|||
oracle::oracle_price(
|
||||
oracle_acc,
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
async fn derive_health_check_remaining_account_metas(
|
||||
account_loader: &impl ClientAccountLoader,
|
||||
|
@ -201,6 +209,14 @@ async fn derive_health_check_remaining_account_metas(
|
|||
.active_perp_positions()
|
||||
.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 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(perp_markets.map(to_account_meta))
|
||||
.chain(perp_oracles.into_iter().map(to_account_meta))
|
||||
.chain(serum_oos.map(to_account_meta))
|
||||
.collect()
|
||||
}
|
||||
|
@ -251,11 +268,17 @@ async fn derive_liquidation_remaining_account_metas(
|
|||
oracles.push(mint_info.oracle);
|
||||
}
|
||||
|
||||
let perp_markets = liqee
|
||||
let perp_markets: Vec<Pubkey> = liqee
|
||||
.active_perp_positions()
|
||||
.chain(liqee.active_perp_positions())
|
||||
.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
|
||||
.active_serum3_orders()
|
||||
|
@ -276,7 +299,8 @@ async fn derive_liquidation_remaining_account_metas(
|
|||
is_signer: false,
|
||||
})
|
||||
.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))
|
||||
.collect()
|
||||
}
|
||||
|
@ -2107,8 +2131,7 @@ pub struct PerpCreateMarketInstruction {
|
|||
pub event_queue: Pubkey,
|
||||
pub payer: TestKeypair,
|
||||
pub perp_market_index: PerpMarketIndex,
|
||||
pub base_token_index: TokenIndex,
|
||||
pub base_token_decimals: u8,
|
||||
pub base_decimals: u8,
|
||||
pub quote_lot_size: i64,
|
||||
pub base_lot_size: i64,
|
||||
pub maint_asset_weight: f32,
|
||||
|
@ -2135,8 +2158,7 @@ impl PerpCreateMarketInstruction {
|
|||
.create_account_for_type::<EventQueue>(&mango_v4::id())
|
||||
.await,
|
||||
oracle: base.oracle,
|
||||
base_token_index: base.index,
|
||||
base_token_decimals: base.mint.decimals,
|
||||
base_decimals: base.mint.decimals,
|
||||
..PerpCreateMarketInstruction::default()
|
||||
}
|
||||
}
|
||||
|
@ -2156,7 +2178,6 @@ impl ClientInstruction for PerpCreateMarketInstruction {
|
|||
conf_filter: I80F48::from_num::<f32>(0.10),
|
||||
},
|
||||
perp_market_index: self.perp_market_index,
|
||||
base_token_index_opt: Option::from(self.base_token_index),
|
||||
quote_lot_size: self.quote_lot_size,
|
||||
base_lot_size: self.base_lot_size,
|
||||
maint_asset_weight: self.maint_asset_weight,
|
||||
|
@ -2169,7 +2190,7 @@ impl ClientInstruction for PerpCreateMarketInstruction {
|
|||
max_funding: 0.05,
|
||||
min_funding: 0.05,
|
||||
impact_quantity: 100,
|
||||
base_token_decimals: self.base_token_decimals,
|
||||
base_decimals: self.base_decimals,
|
||||
};
|
||||
|
||||
let perp_market = Pubkey::find_program_address(
|
||||
|
@ -2666,6 +2687,7 @@ impl ClientInstruction for PerpLiqForceCancelOrdersInstruction {
|
|||
account: self.account,
|
||||
bids: perp_market.bids,
|
||||
asks: perp_market.asks,
|
||||
oracle: perp_market.oracle,
|
||||
};
|
||||
let mut instruction = make_instruction(program_id, &accounts, instruction);
|
||||
instruction.accounts.extend(health_check_metas);
|
||||
|
|
|
@ -221,10 +221,6 @@ async fn test_health_compute_perp() -> Result<(), TransportError> {
|
|||
liquidation_fee: 0.012,
|
||||
maker_fee: 0.0002,
|
||||
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
|
||||
},
|
||||
)
|
||||
|
|
|
@ -31,7 +31,6 @@ export class PerpMarket {
|
|||
publicKey: PublicKey,
|
||||
obj: {
|
||||
group: PublicKey;
|
||||
baseTokenIndex: number;
|
||||
quoteTokenIndex: number;
|
||||
perpMarketIndex: number;
|
||||
name: number[];
|
||||
|
@ -59,14 +58,13 @@ export class PerpMarket {
|
|||
seqNum: any; // TODO: ts complains that this is unknown for whatever reason
|
||||
feesAccrued: I80F48Dto;
|
||||
bump: number;
|
||||
baseTokenDecimals: number;
|
||||
baseDecimals: number;
|
||||
registrationTime: BN;
|
||||
},
|
||||
): PerpMarket {
|
||||
return new PerpMarket(
|
||||
publicKey,
|
||||
obj.group,
|
||||
obj.baseTokenIndex,
|
||||
obj.quoteTokenIndex,
|
||||
obj.perpMarketIndex,
|
||||
obj.name,
|
||||
|
@ -94,7 +92,7 @@ export class PerpMarket {
|
|||
obj.seqNum,
|
||||
obj.feesAccrued,
|
||||
obj.bump,
|
||||
obj.baseTokenDecimals,
|
||||
obj.baseDecimals,
|
||||
obj.registrationTime,
|
||||
);
|
||||
}
|
||||
|
@ -102,7 +100,6 @@ export class PerpMarket {
|
|||
constructor(
|
||||
public publicKey: PublicKey,
|
||||
public group: PublicKey,
|
||||
public baseTokenIndex: number,
|
||||
public quoteTokenIndex: number,
|
||||
public perpMarketIndex: number,
|
||||
name: number[],
|
||||
|
@ -130,7 +127,7 @@ export class PerpMarket {
|
|||
seqNum: BN,
|
||||
feesAccrued: I80F48Dto,
|
||||
bump: number,
|
||||
public baseTokenDecimals: number,
|
||||
public baseDecimals: number,
|
||||
public registrationTime: BN,
|
||||
) {
|
||||
this.name = utf8.decode(new Uint8Array(name)).split('\x00')[0];
|
||||
|
|
|
@ -1273,8 +1273,7 @@ export class MangoClient {
|
|||
perpMarketIndex: number,
|
||||
name: string,
|
||||
oracleConfFilter: number,
|
||||
baseTokenIndex: number,
|
||||
baseTokenDecimals: number,
|
||||
baseDecimals: number,
|
||||
quoteTokenIndex: number,
|
||||
quoteLotSize: number,
|
||||
baseLotSize: number,
|
||||
|
@ -1302,8 +1301,7 @@ export class MangoClient {
|
|||
val: I80F48.fromNumber(oracleConfFilter).getData(),
|
||||
},
|
||||
} as any, // future: nested custom types dont typecheck, fix if possible?
|
||||
baseTokenIndex,
|
||||
baseTokenDecimals,
|
||||
baseDecimals,
|
||||
new BN(quoteLotSize),
|
||||
new BN(baseLotSize),
|
||||
maintAssetWeight,
|
||||
|
@ -1374,8 +1372,7 @@ export class MangoClient {
|
|||
perpMarketName: string,
|
||||
oracle: PublicKey,
|
||||
oracleConfFilter: number,
|
||||
baseTokenIndex: number,
|
||||
baseTokenDecimals: number,
|
||||
baseDecimals: number,
|
||||
maintAssetWeight: number,
|
||||
initAssetWeight: number,
|
||||
maintLiabWeight: number,
|
||||
|
@ -1397,8 +1394,7 @@ export class MangoClient {
|
|||
val: I80F48.fromNumber(oracleConfFilter).getData(),
|
||||
},
|
||||
} as any, // future: nested custom types dont typecheck, fix if possible?
|
||||
baseTokenIndex,
|
||||
baseTokenDecimals,
|
||||
baseDecimals,
|
||||
maintAssetWeight,
|
||||
initAssetWeight,
|
||||
maintLiabWeight,
|
||||
|
@ -1439,10 +1435,7 @@ export class MangoClient {
|
|||
.rpc();
|
||||
}
|
||||
|
||||
public async perpGetMarkets(
|
||||
group: Group,
|
||||
baseTokenIndex?: number,
|
||||
): Promise<PerpMarket[]> {
|
||||
public async perpGetMarkets(group: Group): Promise<PerpMarket[]> {
|
||||
const bumpfbuf = Buffer.alloc(1);
|
||||
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) =>
|
||||
PerpMarket.from(tuple.publicKey, tuple.account),
|
||||
);
|
||||
|
@ -1910,6 +1892,18 @@ export class MangoClient {
|
|||
)[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) {
|
||||
const alreadyAdded = mangoAccount.perps.find(
|
||||
(p) => p.marketIndex === perpMarket.perpMarketIndex,
|
||||
|
@ -1968,23 +1962,17 @@ export class MangoClient {
|
|||
...mintInfos.map((mintInfo) => mintInfo.oracle),
|
||||
);
|
||||
|
||||
for (const mangoAccount of mangoAccounts) {
|
||||
healthRemainingAccounts.push(
|
||||
...mangoAccount.serum3
|
||||
.filter((serum3Account) => serum3Account.marketIndex !== 65535)
|
||||
.map((serum3Account) => serum3Account.openOrders),
|
||||
);
|
||||
}
|
||||
const perpsToAdd: PerpMarket[] = [];
|
||||
|
||||
for (const mangoAccount of mangoAccounts) {
|
||||
healthRemainingAccounts.push(
|
||||
perpsToAdd.push(
|
||||
...mangoAccount.perps
|
||||
.filter((perp) => perp.marketIndex !== 65535)
|
||||
.map(
|
||||
(perp) =>
|
||||
Array.from(group.perpMarketsMap.values()).filter(
|
||||
(perpMarket) => perpMarket.perpMarketIndex === perp.marketIndex,
|
||||
)[0].publicKey,
|
||||
)[0],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -1994,15 +1982,28 @@ export class MangoClient {
|
|||
(p) => p.marketIndex === perpMarket.perpMarketIndex,
|
||||
);
|
||||
if (!alreadyAdded) {
|
||||
healthRemainingAccounts.push(
|
||||
perpsToAdd.push(
|
||||
Array.from(group.perpMarketsMap.values()).filter(
|
||||
(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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2257,13 +2257,7 @@ export type MangoV4 = {
|
|||
}
|
||||
},
|
||||
{
|
||||
"name": "baseTokenIndexOpt",
|
||||
"type": {
|
||||
"option": "u16"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "baseTokenDecimals",
|
||||
"name": "baseDecimals",
|
||||
"type": "u8"
|
||||
},
|
||||
{
|
||||
|
@ -2351,13 +2345,7 @@ export type MangoV4 = {
|
|||
}
|
||||
},
|
||||
{
|
||||
"name": "baseTokenIndexOpt",
|
||||
"type": {
|
||||
"option": "u16"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "baseTokenDecimalsOpt",
|
||||
"name": "baseDecimalsOpt",
|
||||
"type": {
|
||||
"option": "u8"
|
||||
}
|
||||
|
@ -2953,6 +2941,11 @@ export type MangoV4 = {
|
|||
"name": "bids",
|
||||
"isMut": true,
|
||||
"isSigner": false
|
||||
},
|
||||
{
|
||||
"name": "oracle",
|
||||
"isMut": false,
|
||||
"isSigner": false
|
||||
}
|
||||
],
|
||||
"args": [
|
||||
|
@ -3726,8 +3719,13 @@ export type MangoV4 = {
|
|||
"type": "publicKey"
|
||||
},
|
||||
{
|
||||
"name": "baseTokenIndex",
|
||||
"type": "u16"
|
||||
"name": "padding0",
|
||||
"type": {
|
||||
"array": [
|
||||
"u8",
|
||||
2
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "perpMarketIndex",
|
||||
|
@ -3901,7 +3899,7 @@ export type MangoV4 = {
|
|||
"type": "u8"
|
||||
},
|
||||
{
|
||||
"name": "baseTokenDecimals",
|
||||
"name": "baseDecimals",
|
||||
"type": "u8"
|
||||
},
|
||||
{
|
||||
|
@ -4293,6 +4291,12 @@ export type MangoV4 = {
|
|||
"type": {
|
||||
"defined": "I80F48"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "oraclePrice",
|
||||
"type": {
|
||||
"defined": "I80F48"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -8256,13 +8260,7 @@ export const IDL: MangoV4 = {
|
|||
}
|
||||
},
|
||||
{
|
||||
"name": "baseTokenIndexOpt",
|
||||
"type": {
|
||||
"option": "u16"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "baseTokenDecimals",
|
||||
"name": "baseDecimals",
|
||||
"type": "u8"
|
||||
},
|
||||
{
|
||||
|
@ -8350,13 +8348,7 @@ export const IDL: MangoV4 = {
|
|||
}
|
||||
},
|
||||
{
|
||||
"name": "baseTokenIndexOpt",
|
||||
"type": {
|
||||
"option": "u16"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "baseTokenDecimalsOpt",
|
||||
"name": "baseDecimalsOpt",
|
||||
"type": {
|
||||
"option": "u8"
|
||||
}
|
||||
|
@ -8952,6 +8944,11 @@ export const IDL: MangoV4 = {
|
|||
"name": "bids",
|
||||
"isMut": true,
|
||||
"isSigner": false
|
||||
},
|
||||
{
|
||||
"name": "oracle",
|
||||
"isMut": false,
|
||||
"isSigner": false
|
||||
}
|
||||
],
|
||||
"args": [
|
||||
|
@ -9725,8 +9722,13 @@ export const IDL: MangoV4 = {
|
|||
"type": "publicKey"
|
||||
},
|
||||
{
|
||||
"name": "baseTokenIndex",
|
||||
"type": "u16"
|
||||
"name": "padding0",
|
||||
"type": {
|
||||
"array": [
|
||||
"u8",
|
||||
2
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "perpMarketIndex",
|
||||
|
@ -9900,7 +9902,7 @@ export const IDL: MangoV4 = {
|
|||
"type": "u8"
|
||||
},
|
||||
{
|
||||
"name": "baseTokenDecimals",
|
||||
"name": "baseDecimals",
|
||||
"type": "u8"
|
||||
},
|
||||
{
|
||||
|
@ -10292,6 +10294,12 @@ export const IDL: MangoV4 = {
|
|||
"type": {
|
||||
"defined": "I80F48"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "oraclePrice",
|
||||
"type": {
|
||||
"defined": "I80F48"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue