pub mod msgs {
use cosmwasm_schema::{cw_serde, QueryResponses};
use cosmwasm_std::Decimal;
use super::definitions::price_source::AssetPriceSource;
#[cw_serde]
pub struct InstantiateMsg {
pub owner: String,
pub pyth_addr: Option<String>,
pub assets_precision_sub: u8
}
#[cw_serde]
pub enum ExecuteMsg<C> {
RegisterAsset(AssetPriceSource<C>),
}
#[cw_serde]
#[derive(QueryResponses)]
pub enum QueryMsg {
#[returns(GetPriceResponse)]
GetPrice { base: String, quote: Option<String> },
#[returns(GetPricesResponse)]
GetPrices {
start_after: Option<String>,
limit: Option<u32>,
},
}
#[cw_serde]
pub struct MigrateMsg {}
#[cw_serde]
pub struct GetPriceResponse {
pub price: Decimal,
}
#[cw_serde]
pub struct GetPricesResponse {
pub prices: Vec<(String, Decimal)>,
}
}
pub mod definitions {
use cosmwasm_schema::cw_serde;
use cosmwasm_std::Addr;
use cw_asset::AssetInfo;
use crate::traits::AssertOwner;
pub mod price_source {
use std::fmt::Debug;
use cosmwasm_schema::{
cw_serde,
schemars::JsonSchema,
serde::{de::DeserializeOwned, Serialize},
};
use cosmwasm_std::{Addr, Decimal, Deps, Empty, QueryRequest, StdResult};
use pyth_sdk_cw::PriceIdentifier;
use super::{AssetInfoPrecisioned, Config};
use rhaki_cw_plus::{
math::IntoDecimal,
serde_value::{PathKey, SerdeValue, Value},
traits::IntoStdResult,
};
pub trait PriceSource:
Serialize + DeserializeOwned + Clone + Debug + PartialEq + JsonSchema
{
fn get_price(&self, config: &Config, deps: &Deps) -> StdResult<Decimal>;
}
#[cw_serde]
pub struct AssetPriceSource<C> {
pub asset: AssetInfoPrecisioned,
pub price_source: PriceSourceType<C>,
}
impl<C: PriceSource> AssetPriceSource<C> {
pub fn get_precisioned_price(
&self,
config: &Config,
deps: &Deps,
) -> StdResult<Decimal> {
let price = self.get_precisionless_price(config, deps)?;
Ok(price
* Decimal::from_atomics(1_u128, self.asset.precision as u32)
.into_std_result()?)
}
pub fn get_precisionless_price(
&self,
config: &Config,
deps: &Deps,
) -> StdResult<Decimal> {
match &self.price_source {
PriceSourceType::Base(ps) => ps.get_price(config, deps),
PriceSourceType::Custom(ps) => ps.get_price(config, deps),
}
}
}
#[cw_serde]
pub enum PriceSourceType<C> {
Base(PriceSourceBase),
Custom(C),
}
#[cw_serde]
pub enum PriceSourceBase {
Pyth(PriceSourcePyth),
Querier(PriceSourceQuerier),
}
impl PriceSource for PriceSourceBase {
fn get_price(&self, config: &Config, deps: &Deps) -> StdResult<Decimal> {
match self {
PriceSourceBase::Pyth(ps) => {
ps.get_pyth_price(config.pyth_addr.clone().unwrap(), deps)
}
PriceSourceBase::Querier(ps) => {
let mut price: Decimal = deps
.querier
.query::<Value>(&ps.query)?
.get_value_by_path(ps.path_value.clone())?;
let base_price = ps
.base
.as_ref()
.map(|base| base.get_price(&config, &deps).unwrap())
.unwrap_or(Decimal::one());
if ps.inverted {
price = Decimal::one() / price
}
Ok(price / base_price)
}
}
}
}
#[cw_serde]
pub struct PriceSourcePyth {
pub identifier: String,
}
#[cw_serde]
pub struct PriceSourceQuerier {
pub query: QueryRequest<Empty>,
pub path_value: Vec<PathKey>,
pub base: Option<Box<PriceSourceBase>>,
pub inverted: bool,
}
impl PriceSourcePyth {
pub fn get_pyth_price(&self, pyth_addr: Addr, deps: &Deps) -> StdResult<Decimal> {
pyth_sdk_cw::query_price_feed(
&deps.querier,
pyth_addr.clone(),
PriceIdentifier::from_hex(self.identifier.clone()).into_std_result()?,
)?
.try_into_decimal()
}
}
}
#[cw_serde]
pub struct AssetInfoPrecisioned {
pub info: AssetInfo,
pub precision: u8,
}
#[cw_serde]
pub struct Config {
pub owner: Addr,
pub pyth_addr: Option<Addr>,
pub assets_precision_sub: u8
}
impl AssertOwner for Config {
fn get_admin(&self) -> Addr {
self.owner.clone()
}
}
}
#[cfg(test)]
mod test {
use cosmwasm_std::Decimal;
use pyth_sdk_cw::{Price, PriceFeed, PriceFeedResponse, PriceIdentifier};
#[test]
pub fn test() {
let pfc = PriceFeedResponse {
price_feed: PriceFeed::new(
PriceIdentifier::from_hex(
"f9c0172ba10dfa4d19088d94f5bf61d3b54d5bd7483a322a982e1373ee8ea31b",
)
.unwrap(),
Price {
price: 3721680889279,
conf: 884804937,
expo: -8,
publish_time: 1701174642,
},
Price {
price: 3721680889279,
conf: 884804937,
expo: -8,
publish_time: 1701174642,
},
),
};
let price = pfc.price_feed.get_price_unchecked();
let price = Decimal::from_atomics(price.price as u128, price.expo.abs() as u32).unwrap();
println!("{price}")
}
}