1#![allow(deprecated)]
2
3use num_enum::{IntoPrimitive, TryFromPrimitive};
4
5use crate::{price::Decimal, token_config::TokenConfig, Price};
6
7pub const MAX_ORACLE_FLAGS: usize = 8;
9
10#[derive(Debug, thiserror::Error)]
12pub enum OracleError {
13 #[error("invalid price feed price: {0}")]
15 InvalidPriceFeedPrice(&'static str),
16}
17
18type OracleResult<T> = std::result::Result<T, OracleError>;
19
20#[repr(u8)]
22#[derive(
23 Clone,
24 Copy,
25 Default,
26 TryFromPrimitive,
27 IntoPrimitive,
28 PartialEq,
29 Eq,
30 Hash,
31 strum::EnumString,
32 strum::Display,
33)]
34#[strum(serialize_all = "snake_case")]
35#[cfg_attr(feature = "enum-iter", derive(strum::EnumIter))]
36#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
37#[cfg_attr(feature = "clap", clap(rename_all = "snake_case"))]
38#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
39#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
40#[cfg_attr(feature = "debug", derive(Debug))]
41#[non_exhaustive]
42pub enum PriceProviderKind {
43 #[default]
45 ChainlinkDataStreams = 0,
46 Pyth = 1,
48 #[deprecated(since = "0.6.0", note = "use `ChainlinkDataStreams` instead")]
50 Chainlink = 2,
51 Switchboard = 3,
53}
54
55pub fn pyth_price_with_confidence_to_price(
57 price: i64,
58 confidence: u64,
59 exponent: i32,
60 token_config: &TokenConfig,
61) -> OracleResult<Price> {
62 let mid_price: u64 = price
63 .try_into()
64 .map_err(|_| OracleError::InvalidPriceFeedPrice("mid_price"))?;
65 let min_price = mid_price
68 .checked_sub(confidence)
69 .ok_or(OracleError::InvalidPriceFeedPrice("min_price"))?;
70 let max_price = mid_price
71 .checked_add(confidence)
72 .ok_or(OracleError::InvalidPriceFeedPrice("max_price"))?;
73 Ok(Price {
74 min: pyth_price_value_to_decimal(min_price, exponent, token_config)?,
75 max: pyth_price_value_to_decimal(max_price, exponent, token_config)?,
76 })
77}
78
79pub fn pyth_price_value_to_decimal(
81 mut value: u64,
82 exponent: i32,
83 token_config: &TokenConfig,
84) -> OracleResult<Decimal> {
85 let decimals: u8 = if exponent <= 0 {
89 (-exponent)
90 .try_into()
91 .map_err(|_| OracleError::InvalidPriceFeedPrice("exponent too small"))?
92 } else {
93 let factor = 10u64
94 .checked_pow(exponent as u32)
95 .ok_or(OracleError::InvalidPriceFeedPrice("exponent too big"))?;
96 value = value
97 .checked_mul(factor)
98 .ok_or(OracleError::InvalidPriceFeedPrice("price overflow"))?;
99 0
100 };
101 let price = Decimal::try_from_price(
102 value as u128,
103 decimals,
104 token_config.token_decimals(),
105 token_config.precision(),
106 )
107 .map_err(|_| OracleError::InvalidPriceFeedPrice("converting to Decimal"))?;
108 Ok(price)
109}
110
111#[repr(u8)]
113#[non_exhaustive]
114#[derive(num_enum::IntoPrimitive, num_enum::TryFromPrimitive)]
115pub enum OracleFlag {
116 Cleared,
118 }