gmsol_store/states/oracle/
switchboard.rs1use std::ops::Deref;
2
3use crate::{states::TokenConfig, CoreError};
4use anchor_lang::prelude::*;
5use anchor_lang::Discriminator;
6use anchor_lang::ZeroCopy;
7use gmsol_utils::price::Decimal;
8use gmsol_utils::price::Price;
9use switchboard_on_demand::Discriminator as _;
10
11pub struct Switchboard;
13
14#[cfg(feature = "devnet")]
15impl Id for Switchboard {
16 fn id() -> Pubkey {
17 switchboard_on_demand::ON_DEMAND_DEVNET_PID
18 }
19}
20
21#[cfg(not(feature = "devnet"))]
22impl Id for Switchboard {
23 fn id() -> Pubkey {
24 switchboard_on_demand::ON_DEMAND_MAINNET_PID
25 }
26}
27
28#[repr(C)]
29#[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
30struct SbFeed {
31 feed: switchboard_on_demand::SbFeed,
32}
33
34impl Deref for SbFeed {
35 type Target = switchboard_on_demand::SbFeed;
36
37 fn deref(&self) -> &Self::Target {
38 &self.feed
39 }
40}
41
42impl ZeroCopy for SbFeed {}
43
44impl Owner for SbFeed {
45 fn owner() -> Pubkey {
46 Switchboard::id()
47 }
48}
49
50impl Discriminator for SbFeed {
51 const DISCRIMINATOR: [u8; 8] = switchboard_on_demand::SbFeed::DISCRIMINATOR;
52}
53
54impl Switchboard {
55 #[allow(clippy::manual_inspect)]
56 pub(super) fn check_and_get_price<'info>(
57 clock: &Clock,
58 token_config: &TokenConfig,
59 feed: &'info AccountInfo<'info>,
60 ) -> Result<(u64, i64, Price)> {
61 let feed = AccountLoader::<SbFeed>::try_from(feed)?;
62 let feed = feed.load()?;
63 let result_ts = feed.result_ts();
64 require_gte!(
65 result_ts.saturating_add(token_config.heartbeat_duration().into()),
66 clock.unix_timestamp,
67 CoreError::PriceFeedNotUpdated
68 );
69 Ok((
70 feed.result_land_slot(),
71 result_ts,
72 Self::price_from(&feed, token_config)?,
73 ))
74 }
75
76 fn price_from(feed: &SbFeed, token_config: &TokenConfig) -> Result<Price> {
77 let min_price = feed
78 .min_value()
79 .ok_or_else(|| error!(CoreError::PriceIsStale))?;
80 let min_price = from_rust_decimal_price(&min_price, token_config)?;
81 let max_price = feed
82 .max_value()
83 .ok_or_else(|| error!(CoreError::PriceIsStale))?;
84 let max_price = from_rust_decimal_price(&max_price, token_config)?;
85 Ok(Price {
86 min: min_price,
87 max: max_price,
88 })
89 }
90}
91
92fn from_rust_decimal_price(
93 price: &rust_decimal::Decimal,
94 token_config: &TokenConfig,
95) -> Result<Decimal> {
96 Decimal::try_from_price(
97 price
98 .mantissa()
99 .try_into()
100 .map_err(|_| error!(CoreError::InvalidPriceFeedPrice))?,
101 price
102 .scale()
103 .try_into()
104 .map_err(|_| error!(CoreError::InvalidPriceFeedPrice))?,
105 token_config.token_decimals(),
106 token_config.precision(),
107 )
108 .map_err(|_| error!(CoreError::InvalidPriceFeedPrice))
109}