mango-v4/programs/mango-v4/src/state/oracle.rs

75 lines
2.1 KiB
Rust

use std::mem::size_of;
use anchor_lang::prelude::*;
use anchor_lang::Discriminator;
use fixed::types::I80F48;
use static_assertions::const_assert_eq;
use crate::error::MangoError;
use crate::util::LoadZeroCopy;
#[derive(PartialEq)]
pub enum OracleType {
Stub,
Pyth,
}
#[account(zero_copy)]
pub struct StubOracle {
pub group: Pubkey,
pub mint: Pubkey,
pub price: I80F48,
pub last_updated: i64,
pub reserved: [u8; 8],
}
const_assert_eq!(size_of::<StubOracle>(), 32 + 32 + 16 + 8 + 8);
const_assert_eq!(size_of::<StubOracle>() % 8, 0);
pub fn determine_oracle_type(data: &[u8]) -> Result<OracleType> {
if u32::from_le_bytes(data[0..4].try_into().unwrap()) == pyth_sdk_solana::state::MAGIC {
return Ok(OracleType::Pyth);
} else if data[0..8] == StubOracle::discriminator() {
return Ok(OracleType::Stub);
}
Err(MangoError::UnknownOracleType.into())
}
pub fn oracle_price(acc_info: &AccountInfo) -> Result<I80F48> {
let data = &acc_info.try_borrow_data()?;
let oracle_type = determine_oracle_type(data)?;
Ok(match oracle_type {
OracleType::Stub => acc_info.load::<StubOracle>()?.price,
OracleType::Pyth => {
let price_struct = pyth_sdk_solana::load_price(data).unwrap();
I80F48::from_num(price_struct.price)
}
})
}
#[cfg(test)]
mod tests {
use super::*;
use solana_program_test::{find_file, read_file};
use std::path::PathBuf;
#[test]
pub fn test_determine_oracle_type_from_pyth_price_ai() -> Result<()> {
// add ability to find fixtures
let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
d.push("resources/test");
// load fixture
// J83w4HKfqxwcq3BEMMkPFSppX3gqekLyLJBexebFVkix.bin is SOL_PYTH_PRICE
let filename = "resources/test/J83w4HKfqxwcq3BEMMkPFSppX3gqekLyLJBexebFVkix.bin";
let pyth_price_data = read_file(find_file(filename).unwrap());
assert!(determine_oracle_type(&pyth_price_data).unwrap() == OracleType::Pyth);
let price = pyth_sdk_solana::load_price(pyth_price_data.as_slice()).unwrap();
assert_eq!(price.price, 32112500000);
Ok(())
}
}