gmsol_store/states/oracle/
pyth.rs

1use anchor_lang::prelude::*;
2use gmsol_utils::oracle::OracleError;
3use pyth_solana_receiver_sdk::price_update::PriceUpdateV2;
4
5use crate::{states::TokenConfig, CoreError};
6
7pub use gmsol_utils::oracle::pyth_price_with_confidence_to_price;
8
9use super::OraclePriceParts;
10
11/// The Pyth receiver program.
12pub struct Pyth;
13
14impl Id for Pyth {
15    fn id() -> Pubkey {
16        pyth_solana_receiver_sdk::ID
17    }
18}
19
20impl Pyth {
21    /// Push Oracle ID.
22    pub const PUSH_ORACLE_ID: Pubkey = pyth_solana_receiver_sdk::PYTH_PUSH_ORACLE_ID;
23
24    #[allow(clippy::manual_inspect)]
25    pub(super) fn check_and_get_price<'info>(
26        clock: &Clock,
27        token_config: &TokenConfig,
28        feed: &'info AccountInfo<'info>,
29        feed_id: &Pubkey,
30    ) -> Result<OraclePriceParts> {
31        let feed = Account::<PriceUpdateV2>::try_from(feed)?;
32        let feed_id = feed_id.to_bytes();
33        let max_age = token_config.heartbeat_duration().into();
34        let price = feed
35            .get_price_no_older_than(clock, max_age, &feed_id)
36            .map_err(|err| {
37                let price_ts = feed.price_message.publish_time;
38                msg!(
39                    "[Pyth] get price error, clock={} price_ts={} max_age={}",
40                    clock.unix_timestamp,
41                    price_ts,
42                    max_age,
43                );
44                err
45            })?;
46        let parsed_price = pyth_price_with_confidence_to_price(
47            price.price,
48            price.conf,
49            price.exponent,
50            token_config,
51        )
52        .map_err(CoreError::from)
53        .map_err(|err| error!(err))?;
54        Ok(OraclePriceParts {
55            oracle_slot: feed.posted_slot,
56            oracle_ts: price.publish_time,
57            price: parsed_price,
58            ref_price: None,
59            is_open: true,
60        })
61    }
62}
63
64impl From<OracleError> for CoreError {
65    fn from(err: OracleError) -> Self {
66        msg!("Oracle error: {}", err);
67        match err {
68            OracleError::InvalidPriceFeedPrice(_) => CoreError::InvalidPriceFeedPrice,
69        }
70    }
71}