Skip to main content

gmsol_utils/price/
mod.rs

1/// Decimal type for price.
2pub mod decimal;
3
4/// Price Feed Price.
5pub mod feed_price;
6
7pub use self::{
8    decimal::{Decimal, DecimalError},
9    feed_price::PriceFeedPrice,
10};
11use anchor_lang::prelude::*;
12
13pub use ruint::aliases::U192;
14
15/// [`U192`] number 10.
16pub const TEN: U192 = U192::from_limbs([10, 0, 0]);
17
18/// Max number of price feed flags.
19pub const MAX_PRICE_FLAG: usize = 8;
20
21/// Max number of oracle price flags.
22pub const MAX_ORACLE_PRICE_FLAG: usize = 8;
23
24/// Price type.
25#[derive(Debug, AnchorSerialize, AnchorDeserialize, Clone, Copy, InitSpace)]
26pub struct Price {
27    /// Min Price.
28    pub min: Decimal,
29    /// Max Price.
30    pub max: Decimal,
31}
32
33fn get_power_bounds() -> &'static [U192; 20] {
34    const BOUNDS: [U192; 20] = [
35        U192::from_limbs([18446744073709551615, 18446744073709551615, 0]),
36        U192::from_limbs([18446744073709551606, 18446744073709551615, 9]),
37        U192::from_limbs([18446744073709551516, 18446744073709551615, 99]),
38        U192::from_limbs([18446744073709550616, 18446744073709551615, 999]),
39        U192::from_limbs([18446744073709541616, 18446744073709551615, 9999]),
40        U192::from_limbs([18446744073709451616, 18446744073709551615, 99999]),
41        U192::from_limbs([18446744073708551616, 18446744073709551615, 999999]),
42        U192::from_limbs([18446744073699551616, 18446744073709551615, 9999999]),
43        U192::from_limbs([18446744073609551616, 18446744073709551615, 99999999]),
44        U192::from_limbs([18446744072709551616, 18446744073709551615, 999999999]),
45        U192::from_limbs([18446744063709551616, 18446744073709551615, 9999999999]),
46        U192::from_limbs([18446743973709551616, 18446744073709551615, 99999999999]),
47        U192::from_limbs([18446743073709551616, 18446744073709551615, 999999999999]),
48        U192::from_limbs([18446734073709551616, 18446744073709551615, 9999999999999]),
49        U192::from_limbs([18446644073709551616, 18446744073709551615, 99999999999999]),
50        U192::from_limbs([18445744073709551616, 18446744073709551615, 999999999999999]),
51        U192::from_limbs([18436744073709551616, 18446744073709551615, 9999999999999999]),
52        U192::from_limbs([
53            18346744073709551616,
54            18446744073709551615,
55            99999999999999999,
56        ]),
57        U192::from_limbs([
58            17446744073709551616,
59            18446744073709551615,
60            999999999999999999,
61        ]),
62        U192::from_limbs([
63            8446744073709551616,
64            18446744073709551615,
65            9999999999999999999,
66        ]),
67    ];
68
69    &BOUNDS
70}
71
72/// Finds the minimum divisor decimals needed to convert a fixed-point number
73/// from [`U192`] storage to [`u128`] storage.
74pub fn find_divisor_decimals(num: &U192) -> u8 {
75    let bounds = get_power_bounds();
76
77    match bounds.binary_search(num) {
78        Ok(idx) | Err(idx) => idx as u8,
79    }
80}
81
82/// Convert to [`u128`] storage.
83pub fn convert_to_u128_storage(mut num: U192, decimals: u8) -> Option<(u128, u8)> {
84    let divisor_decimals = find_divisor_decimals(&num);
85
86    if divisor_decimals > decimals {
87        return None;
88    }
89
90    num /= TEN.pow(U192::from(divisor_decimals));
91
92    Some((num.try_into().unwrap(), decimals - divisor_decimals))
93}
94
95/// Price Feed Flags.
96#[repr(u8)]
97#[non_exhaustive]
98#[derive(num_enum::IntoPrimitive, num_enum::TryFromPrimitive)]
99pub enum PriceFlag {
100    /// Is Market Opened.
101    Open,
102    /// Indicates whether the `last_update_diff` field is enabled.
103    LastUpdateDiffEnabled,
104    /// Indicates whether the `last_update_diff` field is in seconds.
105    /// Otherwise, it is in nanoseconds.
106    LastUpdateDiffSecs,
107    // CHECK: should have no more than `MAX_PRICE_FLAG` of flags.
108}
109
110/// Flags for oracle price.
111#[repr(u8)]
112#[non_exhaustive]
113#[derive(num_enum::IntoPrimitive, num_enum::TryFromPrimitive)]
114pub enum OraclePriceFlag {
115    /// Is synthetic.
116    Synthetic,
117    /// Is Market Opened.
118    Open,
119    // CHECK: should have no more than `MAX_ORACLE_PRICE_FLAG` of flags.
120}
121
122crate::flags!(OraclePriceFlag, MAX_ORACLE_PRICE_FLAG, u8);
123
124#[cfg(test)]
125mod tests {
126    use super::*;
127
128    #[test]
129    fn test_bounds() {
130        let bounds = get_power_bounds();
131
132        assert_eq!(bounds.len(), 20);
133        assert_eq!(u128::try_from(bounds[0]).unwrap(), u128::MAX);
134        assert!(bounds[19].checked_mul(U192::from(10)).is_none());
135
136        assert_eq!(find_divisor_decimals(&U192::from(u128::MAX)), 0);
137        assert_eq!(find_divisor_decimals(&U192::MAX), 20);
138    }
139
140    #[test]
141    fn test_convert_to_u128_storage() {
142        assert_eq!(
143            convert_to_u128_storage(U192::from(u128::MAX), 18),
144            Some((u128::MAX, 18))
145        );
146
147        assert_eq!(
148            convert_to_u128_storage(U192::from(u128::MAX) + U192::from(1), 18),
149            Some((34028236692093846346337460743176821145, 17))
150        );
151
152        assert_eq!(
153            convert_to_u128_storage(U192::MAX, 20),
154            Some((62771017353866807638357894232076664161, 0))
155        );
156
157        assert_eq!(
158            convert_to_u128_storage(
159                U192::from(u128::MAX)
160                    .checked_mul(U192::from(10).pow(U192::from(19)))
161                    .unwrap()
162                    - U192::from(11),
163                20
164            ),
165            Some((340282366920938463463374607431768211454, 1))
166        );
167        assert_eq!(
168            convert_to_u128_storage(
169                U192::from(u128::MAX)
170                    .checked_mul(U192::from(10).pow(U192::from(19)))
171                    .unwrap()
172                    + U192::from(1),
173                20
174            ),
175            Some((34028236692093846346337460743176821145, 0))
176        );
177
178        assert_eq!(
179            convert_to_u128_storage(
180                U192::from(u128::MAX)
181                    .checked_mul(U192::from(10).pow(U192::from(18)))
182                    .unwrap(),
183                18
184            ),
185            Some((340282366920938463463374607431768211455, 0))
186        );
187    }
188}