use std::{
cmp::Ordering,
ops::{Deref, Div, DivAssign, Mul, MulAssign},
};
use cosmwasm_schema::cw_serde;
use cosmwasm_std::{Decimal, Fraction, StdResult, Uint128};
use crate::querier::KujiraQuerier;
pub const REFERENCE_DECIMAL_PLACES: u8 = 6;
#[cw_serde]
#[derive(Copy, Eq, PartialOrd, Ord)]
pub struct HumanPrice(Decimal);
impl HumanPrice {
pub fn normalize(&self, decimals: u8) -> NormalizedPrice {
NormalizedPrice::from_raw(self.0, decimals)
}
}
impl From<Decimal> for HumanPrice {
fn from(value: Decimal) -> Self {
HumanPrice(value)
}
}
impl From<HumanPrice> for Decimal {
fn from(value: HumanPrice) -> Self {
value.0
}
}
#[cw_serde]
#[derive(Copy, Eq, PartialOrd, Ord)]
pub struct NormalizedPrice(Decimal);
impl NormalizedPrice {
pub fn unsafe_unchecked(price: Decimal) -> Self {
Self(price)
}
pub fn from_raw(price: Decimal, decimals: u8) -> Self {
let delta: i16 = i16::from(REFERENCE_DECIMAL_PLACES) - i16::from(decimals);
Self::from_delta(price, delta)
}
pub fn from_delta(price: Decimal, delta: i16) -> Self {
match delta.cmp(&0) {
Ordering::Equal => Self(price),
Ordering::Greater => Self(Decimal::from_ratio(
price.numerator() * Uint128::from(10u128.pow(u32::from(delta.unsigned_abs()))),
price.denominator(),
)),
Ordering::Less => Self(Decimal::from_ratio(
price.numerator(),
price.denominator() * Uint128::from(10u128.pow(u32::from(delta.unsigned_abs()))),
)),
}
}
pub fn from_oracle<T: Into<String>>(
querier: &KujiraQuerier,
denom: T,
decimals: u8,
) -> StdResult<Self> {
querier
.query_exchange_rate(denom)
.map(|price| price.normalize(decimals))
}
pub fn inner(&self) -> Decimal {
self.0
}
}
impl Deref for NormalizedPrice {
type Target = Decimal;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl From<NormalizedPrice> for Decimal {
fn from(price: NormalizedPrice) -> Self {
price.0
}
}
impl Mul<NormalizedPrice> for NormalizedPrice {
type Output = NormalizedPrice;
fn mul(self, rhs: NormalizedPrice) -> Self::Output {
NormalizedPrice(self.0 * rhs.0)
}
}
impl MulAssign<NormalizedPrice> for NormalizedPrice {
fn mul_assign(&mut self, rhs: NormalizedPrice) {
self.0 *= rhs.0
}
}
impl Div<NormalizedPrice> for NormalizedPrice {
type Output = NormalizedPrice;
fn div(self, rhs: NormalizedPrice) -> Self::Output {
NormalizedPrice(self.0 / rhs.0)
}
}
impl DivAssign<NormalizedPrice> for NormalizedPrice {
fn div_assign(&mut self, rhs: NormalizedPrice) {
self.0 /= rhs.0
}
}
impl Mul<Uint128> for NormalizedPrice {
type Output = Uint128;
fn mul(self, rhs: Uint128) -> Self::Output {
self.0 * rhs
}
}
impl Mul<NormalizedPrice> for Uint128 {
type Output = Uint128;
fn mul(self, rhs: NormalizedPrice) -> Self::Output {
rhs.0 * self
}
}
impl MulAssign<NormalizedPrice> for Uint128 {
fn mul_assign(&mut self, rhs: NormalizedPrice) {
*self = *self * rhs.0
}
}
impl Div<Uint128> for NormalizedPrice {
type Output = Option<Uint128>;
fn div(self, rhs: Uint128) -> Self::Output {
self.0.inv().map(|inv| inv * rhs)
}
}
impl Div<NormalizedPrice> for Uint128 {
type Output = Option<Uint128>;
fn div(self, rhs: NormalizedPrice) -> Self::Output {
rhs.0.inv().map(|inv| inv * self)
}
}
#[cfg(test)]
mod tests {
use cosmwasm_std::Decimal;
use super::{HumanPrice, NormalizedPrice};
#[test]
fn serialize_human_price() {
let price = HumanPrice(Decimal::percent(459));
let serialized = serde_json::to_string(&price).unwrap();
assert_eq!(serialized, r#""4.59""#);
}
#[test]
fn deserialize_human_price() {
let price = HumanPrice(Decimal::percent(459));
let deserialized: HumanPrice = serde_json::from_str(r#""4.59""#).unwrap();
assert_eq!(price, deserialized);
}
#[test]
fn serialize_normalized_price() {
let price = NormalizedPrice(Decimal::percent(459));
let serialized = serde_json::to_string(&price).unwrap();
assert_eq!(serialized, r#""4.59""#);
}
#[test]
fn deserialize_normalized_price() {
let price = NormalizedPrice(Decimal::percent(459));
let deserialized: NormalizedPrice = serde_json::from_str(r#""4.59""#).unwrap();
assert_eq!(price, deserialized);
}
}