FixedOrderAccountRetriever uses fallback oracles

This commit is contained in:
Lou-Kamades 2023-11-20 15:00:36 -06:00
parent c22db3be0c
commit d4017e6038
3 changed files with 41 additions and 12 deletions

View File

@ -47,6 +47,7 @@ pub trait AccountRetriever {
/// 3. PerpMarket accounts, in the order of account.perps.iter_active_accounts()
/// 4. PerpMarket oracle accounts, in the order of the perp market accounts
/// 5. serum3 OpenOrders accounts, in the order of account.serum3.iter_active()
/// 6. fallback oracle accounts, order and existence of accounts is not guaranteed
pub struct FixedOrderAccountRetriever<T: KeyedAccountReader> {
pub ais: Vec<T>,
pub n_banks: usize,
@ -54,6 +55,7 @@ pub struct FixedOrderAccountRetriever<T: KeyedAccountReader> {
pub begin_perp: usize,
pub begin_serum3: usize,
pub staleness_slot: Option<u64>,
pub fallback_oracle_ais: Vec<T>,
}
pub fn new_fixed_order_account_retriever<'a, 'info>(
@ -66,19 +68,22 @@ pub fn new_fixed_order_account_retriever<'a, 'info>(
let expected_ais = active_token_len * 2 // banks + oracles
+ active_perp_len * 2 // PerpMarkets + Oracles
+ active_serum3_len; // open_orders
require_msg_typed!(ais.len() == expected_ais, MangoError::InvalidHealthAccountCount,
require_msg_typed!(ais.len() >= expected_ais, MangoError::InvalidHealthAccountCount,
"received {} accounts but expected {} ({} banks, {} bank oracles, {} perp markets, {} perp oracles, {} serum3 oos)",
ais.len(), expected_ais,
active_token_len, active_token_len, active_perp_len, active_perp_len, active_serum3_len
);
let fixed_ais = AccountInfoRef::borrow_slice(&ais[..expected_ais])?;
let fallback_oracle_ais = AccountInfoRef::borrow_slice(&ais[expected_ais..])?;
Ok(FixedOrderAccountRetriever {
ais: AccountInfoRef::borrow_slice(ais)?,
ais: fixed_ais,
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 * 2,
staleness_slot: Some(Clock::get()?.slot),
fallback_oracle_ais,
})
}
@ -103,11 +108,6 @@ impl<T: KeyedAccountReader> FixedOrderAccountRetriever<T> {
Ok(market)
}
fn oracle_price_bank(&self, account_index: usize, bank: &Bank) -> Result<I80F48> {
let oracle = &self.ais[account_index];
bank.oracle_price(oracle, self.staleness_slot)
}
fn oracle_price_perp(&self, account_index: usize, perp_market: &PerpMarket) -> Result<I80F48> {
let oracle = &self.ais[account_index];
perp_market.oracle_price(oracle, self.staleness_slot)
@ -134,7 +134,14 @@ impl<T: KeyedAccountReader> AccountRetriever for FixedOrderAccountRetriever<T> {
})?;
let oracle_index = self.n_banks + active_token_position_index;
let oracle_price = self.oracle_price_bank(oracle_index, bank).with_context(|| {
let oracle = &self.ais[oracle_index];
let fallback_opt = self
.fallback_oracle_ais
.iter()
.find(|ai| ai.key() == &bank.fallback_oracle);
let oracle_price_result =
bank.oracle_price_with_fallback(oracle, fallback_opt, self.staleness_slot);
let oracle_price = oracle_price_result.with_context(|| {
format!(
"getting oracle for bank with health account index {} and token index {}, passed account {}",
bank_account_index,

View File

@ -3,10 +3,7 @@ use fixed::types::I80F48;
use crate::accounts_ix::*;
pub fn stub_oracle_create(
ctx: Context<StubOracleCreate>,
price: I80F48
) -> Result<()> {
pub fn stub_oracle_create(ctx: Context<StubOracleCreate>, price: I80F48) -> Result<()> {
let mut oracle = ctx.accounts.oracle.load_init()?;
oracle.group = ctx.accounts.group.key();
oracle.mint = ctx.accounts.mint.key();

View File

@ -949,6 +949,14 @@ impl Bank {
staleness_slot: Option<u64>,
) -> Result<I80F48> {
require_keys_eq!(self.oracle, *oracle_acc.key());
self.oracle_price_inner(oracle_acc, staleness_slot)
}
fn oracle_price_inner(
&self,
oracle_acc: &impl KeyedAccountReader,
staleness_slot: Option<u64>,
) -> Result<I80F48> {
let state = oracle::oracle_state_unchecked(oracle_acc, self.mint_decimals)?;
state.check_confidence_and_maybe_staleness(
&self.oracle,
@ -958,6 +966,23 @@ impl Bank {
Ok(state.price)
}
/// Tries to return the primary oracle price, and if there is a confidence or staleness issue returns the fallback oracle price.
pub fn oracle_price_with_fallback(
&self,
oracle_acc: &impl KeyedAccountReader,
fallback_oracle_acc_opt: Option<&impl KeyedAccountReader>,
staleness_slot: Option<u64>,
) -> Result<I80F48> {
let primary_price = self.oracle_price(oracle_acc, staleness_slot);
if primary_price.is_ok() || fallback_oracle_acc_opt.is_none() {
primary_price
} else {
let fallback_oracle_acc = fallback_oracle_acc_opt.unwrap();
require_keys_eq!(self.fallback_oracle, *fallback_oracle_acc.key());
self.oracle_price_inner(fallback_oracle_acc, staleness_slot)
}
}
pub fn stable_price(&self) -> I80F48 {
I80F48::from_num(self.stable_price_model.stable_price)
}