1pub mod msgs {
2
3    use cosmwasm_schema::{cw_serde, QueryResponses};
4    use cosmwasm_std::Decimal;
5
6    use super::definitions::price_source::AssetPriceSource;
7
8    #[cw_serde]
9    pub struct InstantiateMsg {
10        pub owner: String,
11        pub pyth_addr: Option<String>,
12        pub assets_precision_sub: u8
14    }
15
16    #[cw_serde]
17    pub enum ExecuteMsg<C> {
18        RegisterAsset(AssetPriceSource<C>),
19    }
20
21    #[cw_serde]
22    #[derive(QueryResponses)]
23    pub enum QueryMsg {
24        #[returns(GetPriceResponse)]
25        GetPrice { base: String, quote: Option<String> },
26        #[returns(GetPricesResponse)]
27        GetPrices {
28            start_after: Option<String>,
29            limit: Option<u32>,
30        },
31    }
32
33    #[cw_serde]
34    pub struct MigrateMsg {}
35
36    #[cw_serde]
37    pub struct GetPriceResponse {
38        pub price: Decimal,
39    }
40
41    #[cw_serde]
42    pub struct GetPricesResponse {
43        pub prices: Vec<(String, Decimal)>,
44    }
45}
46
47pub mod definitions {
48    use cosmwasm_schema::cw_serde;
49    use cosmwasm_std::Addr;
50    use cw_asset::AssetInfo;
51
52    use crate::traits::AssertOwner;
53
54    pub mod price_source {
55        use std::fmt::Debug;
56
57        use cosmwasm_schema::{
58            cw_serde,
59            schemars::JsonSchema,
60            serde::{de::DeserializeOwned, Serialize},
61        };
62        use cosmwasm_std::{Addr, Decimal, Deps, Empty, QueryRequest, StdResult};
63        use pyth_sdk_cw::PriceIdentifier;
64
65        use super::{AssetInfoPrecisioned, Config};
66
67        use rhaki_cw_plus::{
68            math::IntoDecimal,
69            serde_value::{PathKey, SerdeValue, Value},
70            traits::IntoStdResult,
71        };
72
73        pub trait PriceSource:
74            Serialize + DeserializeOwned + Clone + Debug + PartialEq + JsonSchema
75        {
76            fn get_price(&self, config: &Config, deps: &Deps) -> StdResult<Decimal>;
77        }
78
79        #[cw_serde]
80        pub struct AssetPriceSource<C> {
81            pub asset: AssetInfoPrecisioned,
82            pub price_source: PriceSourceType<C>,
83        }
84
85        impl<C: PriceSource> AssetPriceSource<C> {
86            pub fn get_precisioned_price(
87                &self,
88                config: &Config,
89                deps: &Deps,
90            ) -> StdResult<Decimal> {
91                let price = self.get_precisionless_price(config, deps)?;
92
93                Ok(price
94                    * Decimal::from_atomics(1_u128, self.asset.precision as u32)
95                        .into_std_result()?)
96            }
97
98            pub fn get_precisionless_price(
99                &self,
100                config: &Config,
101                deps: &Deps,
102            ) -> StdResult<Decimal> {
103                match &self.price_source {
104                    PriceSourceType::Base(ps) => ps.get_price(config, deps),
105                    PriceSourceType::Custom(ps) => ps.get_price(config, deps),
106                }
107            }
108        }
109
110        #[cw_serde]
111        pub enum PriceSourceType<C> {
112            Base(PriceSourceBase),
113            Custom(C),
114        }
115
116        #[cw_serde]
117        pub enum PriceSourceBase {
118            Pyth(PriceSourcePyth),
119            Querier(PriceSourceQuerier),
120        }
121
122        impl PriceSource for PriceSourceBase {
123            fn get_price(&self, config: &Config, deps: &Deps) -> StdResult<Decimal> {
124                match self {
125                    PriceSourceBase::Pyth(ps) => {
126                        ps.get_pyth_price(config.pyth_addr.clone().unwrap(), deps)
127                    }
128                    PriceSourceBase::Querier(ps) => {
129                        let mut price: Decimal = deps
130                            .querier
131                            .query::<Value>(&ps.query)?
132                            .get_value_by_path(ps.path_value.clone())?;
133                        let base_price = ps
134                            .base
135                            .as_ref()
136                            .map(|base| base.get_price(&config, &deps).unwrap())
137                            .unwrap_or(Decimal::one());
138
139                        if ps.inverted {
140                            price = Decimal::one() / price
141                        }
142
143                        Ok(price / base_price)
144                    }
145                }
146            }
147        }
148
149        #[cw_serde]
150        pub struct PriceSourcePyth {
151            pub identifier: String,
152        }
153
154        #[cw_serde]
155        pub struct PriceSourceQuerier {
156            pub query: QueryRequest<Empty>,
157            pub path_value: Vec<PathKey>,
158            pub base: Option<Box<PriceSourceBase>>,
159            pub inverted: bool,
160        }
161
162        impl PriceSourcePyth {
163            pub fn get_pyth_price(&self, pyth_addr: Addr, deps: &Deps) -> StdResult<Decimal> {
164                pyth_sdk_cw::query_price_feed(
165                    &deps.querier,
166                    pyth_addr.clone(),
167                    PriceIdentifier::from_hex(self.identifier.clone()).into_std_result()?,
168                )?
169                .try_into_decimal()
170            }
171        }
172    }
173
174    #[cw_serde]
175    pub struct AssetInfoPrecisioned {
176        pub info: AssetInfo,
177        pub precision: u8,
178    }
179
180    #[cw_serde]
181    pub struct Config {
182        pub owner: Addr,
183        pub pyth_addr: Option<Addr>,
184        pub assets_precision_sub: u8
186    }
187
188    impl AssertOwner for Config {
189        fn get_admin(&self) -> Addr {
190            self.owner.clone()
191        }
192    }
193}
194
195#[cfg(test)]
196mod test {
197    use cosmwasm_std::Decimal;
198    use pyth_sdk_cw::{Price, PriceFeed, PriceFeedResponse, PriceIdentifier};
199    #[test]
200    pub fn test() {
201        let pfc = PriceFeedResponse {
202            price_feed: PriceFeed::new(
203                PriceIdentifier::from_hex(
204                    "f9c0172ba10dfa4d19088d94f5bf61d3b54d5bd7483a322a982e1373ee8ea31b",
205                )
206                .unwrap(),
207                Price {
208                    price: 3721680889279,
209                    conf: 884804937,
210                    expo: -8,
211                    publish_time: 1701174642,
212                },
213                Price {
214                    price: 3721680889279,
215                    conf: 884804937,
216                    expo: -8,
217                    publish_time: 1701174642,
218                },
219            ),
220        };
221
222        let price = pfc.price_feed.get_price_unchecked();
223
224        let price = Decimal::from_atomics(price.price as u128, price.expo.abs() as u32).unwrap();
225
226        println!("{price}")
227    }
228}