1use num_enum::{IntoPrimitive, TryFromPrimitive};
2
3use crate::{price::Decimal, token_config::TokenConfig, Price};
4
5#[derive(Debug, thiserror::Error)]
7pub enum OracleError {
8 #[error("invalid price feed price: {0}")]
10 InvalidPriceFeedPrice(&'static str),
11}
12
13type OracleResult<T> = std::result::Result<T, OracleError>;
14
15#[repr(u8)]
17#[derive(
18 Clone,
19 Copy,
20 Default,
21 TryFromPrimitive,
22 IntoPrimitive,
23 PartialEq,
24 Eq,
25 Hash,
26 strum::EnumString,
27 strum::Display,
28)]
29#[strum(serialize_all = "snake_case")]
30#[cfg_attr(feature = "enum-iter", derive(strum::EnumIter))]
31#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
32#[cfg_attr(feature = "clap", clap(rename_all = "snake_case"))]
33#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
34#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
35#[cfg_attr(feature = "debug", derive(Debug))]
36#[non_exhaustive]
37pub enum PriceProviderKind {
38 #[default]
40 ChainlinkDataStreams = 0,
41 Pyth = 1,
43 Chainlink = 2,
45 Switchboard = 3,
47}
48
49pub fn pyth_price_with_confidence_to_price(
51 price: i64,
52 confidence: u64,
53 exponent: i32,
54 token_config: &TokenConfig,
55) -> OracleResult<Price> {
56 let mid_price: u64 = price
57 .try_into()
58 .map_err(|_| OracleError::InvalidPriceFeedPrice("mid_price"))?;
59 let min_price = mid_price
62 .checked_sub(confidence)
63 .ok_or(OracleError::InvalidPriceFeedPrice("min_price"))?;
64 let max_price = mid_price
65 .checked_add(confidence)
66 .ok_or(OracleError::InvalidPriceFeedPrice("max_price"))?;
67 Ok(Price {
68 min: pyth_price_value_to_decimal(min_price, exponent, token_config)?,
69 max: pyth_price_value_to_decimal(max_price, exponent, token_config)?,
70 })
71}
72
73pub fn pyth_price_value_to_decimal(
75 mut value: u64,
76 exponent: i32,
77 token_config: &TokenConfig,
78) -> OracleResult<Decimal> {
79 let decimals: u8 = if exponent <= 0 {
83 (-exponent)
84 .try_into()
85 .map_err(|_| OracleError::InvalidPriceFeedPrice("exponent too small"))?
86 } else {
87 let factor = 10u64
88 .checked_pow(exponent as u32)
89 .ok_or(OracleError::InvalidPriceFeedPrice("exponent too big"))?;
90 value = value
91 .checked_mul(factor)
92 .ok_or(OracleError::InvalidPriceFeedPrice("price overflow"))?;
93 0
94 };
95 let price = Decimal::try_from_price(
96 value as u128,
97 decimals,
98 token_config.token_decimals(),
99 token_config.precision(),
100 )
101 .map_err(|_| OracleError::InvalidPriceFeedPrice("converting to Decimal"))?;
102 Ok(price)
103}