Skip to content

Commit

Permalink
don't care about staleness or variance for the extra oracle
Browse files Browse the repository at this point in the history
  • Loading branch information
0xripleys committed Dec 16, 2023
1 parent 6f3445d commit ea1089d
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 17 deletions.
19 changes: 12 additions & 7 deletions token-lending/program/src/processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ use solana_program::{
},
};
use solend_sdk::{
oracles::{get_oracle_type, validate_pyth_price_account_info, OracleType},
oracles::{
get_oracle_type, get_pyth_price_unchecked, validate_pyth_price_account_info, OracleType,
},
state::{LendingMarketMetadata, RateLimiter, RateLimiterConfig, ReserveType},
};
use solend_sdk::{switchboard_v2_devnet, switchboard_v2_mainnet};
Expand Down Expand Up @@ -578,10 +580,12 @@ fn _refresh_reserve<'a>(
}

match get_oracle_type(extra_oracle_account_info)? {
OracleType::Pyth => Some(get_pyth_price(extra_oracle_account_info, clock)?.0),
OracleType::Switchboard => {
Some(get_switchboard_price_v2(extra_oracle_account_info, clock)?)
}
OracleType::Pyth => Some(get_pyth_price_unchecked(extra_oracle_account_info)?),
OracleType::Switchboard => Some(get_switchboard_price_v2(
extra_oracle_account_info,
clock,
false,
)?),
}
}
None => {
Expand Down Expand Up @@ -3074,7 +3078,7 @@ fn get_switchboard_price(
if switchboard_feed_info.owner == &switchboard_v2_mainnet::id()
|| switchboard_feed_info.owner == &switchboard_v2_devnet::id()
{
return get_switchboard_price_v2(switchboard_feed_info, clock);
return get_switchboard_price_v2(switchboard_feed_info, clock, true);
}

let account_buf = switchboard_feed_info.try_borrow_data()?;
Expand Down Expand Up @@ -3113,6 +3117,7 @@ fn get_switchboard_price(
fn get_switchboard_price_v2(
switchboard_feed_info: &AccountInfo,
clock: &Clock,
check_staleness: bool,
) -> Result<Decimal, ProgramError> {
const STALE_AFTER_SLOTS_ELAPSED: u64 = 240;
let data = &switchboard_feed_info.try_borrow_data()?;
Expand All @@ -3122,7 +3127,7 @@ fn get_switchboard_price_v2(
.slot
.checked_sub(feed.latest_confirmed_round.round_open_slot)
.ok_or(LendingError::MathOverflow)?;
if slots_elapsed >= STALE_AFTER_SLOTS_ELAPSED {
if check_staleness && slots_elapsed >= STALE_AFTER_SLOTS_ELAPSED {
msg!("Switchboard oracle price is stale");
return Err(LendingError::InvalidOracleConfig.into());
}
Expand Down
26 changes: 16 additions & 10 deletions token-lending/program/tests/refresh_reserve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,20 @@ async fn test_success_pyth_price_stale_switchboard_valid() {
wsol_reserve_post.account.liquidity.smoothed_market_price,
Decimal::from(11u64)
);

test.advance_clock_by_slots(241).await;
let err = lending_market
.refresh_reserve(&mut test, &wsol_reserve)
.await
.unwrap_err()
.unwrap();
assert_eq!(
err,
TransactionError::InstructionError(
1,
InstructionError::Custom(LendingError::InvalidOracleConfig as u32)
)
);
}

#[tokio::test]
Expand Down Expand Up @@ -676,19 +690,11 @@ async fn test_use_extra_oracle_bad_cases() {

let mut msol_reserve = test.load_account::<Reserve>(reserves[0].pubkey).await;

// this should fail because the extra oracle is stale
let err = lending_market
// this no longer fails because the extra oracle is not checked for staleness/variance
lending_market
.refresh_reserve(&mut test, &msol_reserve)
.await
.unwrap_err()
.unwrap();
assert_eq!(
err,
TransactionError::InstructionError(
1,
InstructionError::Custom(LendingError::InvalidOracleConfig as u32)
)
);

msol_reserve.account.config.extra_oracle_pubkey =
Some(msol_reserve.account.liquidity.pyth_oracle_pubkey);
Expand Down
60 changes: 60 additions & 0 deletions token-lending/sdk/src/oracles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,23 @@ pub fn validate_pyth_price_account_info(
Ok(())
}

/// get pyth price without caring about staleness or variance. only used
pub fn get_pyth_price_unchecked(pyth_price_info: &AccountInfo) -> Result<Decimal, ProgramError> {
if *pyth_price_info.key == solend_program::NULL_PUBKEY {
return Err(LendingError::NullOracleConfig.into());
}

let data = &pyth_price_info.try_borrow_data()?;
let price_account = pyth_sdk_solana::state::load_price_account(data).map_err(|e| {
msg!("Couldn't load price feed from account info: {:?}", e);
LendingError::InvalidOracleConfig
})?;

let price_feed = price_account.to_price_feed(pyth_price_info.key);
let price = price_feed.get_price_unchecked();
pyth_price_to_decimal(&price)
}

pub fn get_pyth_price(
pyth_price_info: &AccountInfo,
clock: &Clock,
Expand Down Expand Up @@ -453,4 +470,47 @@ mod test {
);
}
}

#[test]
fn pyth_price_unchecked_test_cases() {
let mut price_account = PriceAccount {
magic: MAGIC,
ver: VERSION_2,
atype: AccountType::Price as u32,
ptype: PriceType::Price,
expo: 1,
timestamp: 1,
ema_price: Rational {
val: 11,
numer: 110,
denom: 10,
},
agg: PriceInfo {
price: 200,
conf: 40,
status: PriceStatus::Trading,
corp_act: CorpAction::NoCorpAct,
pub_slot: 0,
},
..PriceAccount::default()
};

let mut lamports = 20;
let pubkey = Pubkey::new_unique();
let account_info = AccountInfo::new(
&pubkey,
false,
false,
&mut lamports,
bytes_of_mut(&mut price_account),
&pubkey,
false,
0,
);

assert_eq!(
get_pyth_price_unchecked(&account_info),
Ok(Decimal::from(2000_u64))
);
}
}

0 comments on commit ea1089d

Please sign in to comment.