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}