Skip to main content

ve3_shared/helpers/
bps.rs

1use cosmwasm_schema::cw_serde;
2use cosmwasm_std::{Decimal, Fraction, StdError, Uint128};
3use std::convert::{TryFrom, TryInto};
4use std::ops::Mul;
5
6/// BasicPoints struct implementation. BasicPoints value is within [0, 10000] interval.
7/// Technically BasicPoints is wrapper over [`u16`] with additional limit checks and
8/// several implementations of math functions so BasicPoints object
9/// can be used in formulas along with [`Uint128`] and [`Decimal`].
10#[cw_serde]
11#[derive(Default, Copy)]
12pub struct BasicPoints(u16);
13
14impl BasicPoints {
15  pub const MAX: u16 = 10000;
16
17  pub fn checked_add(self, rhs: Self) -> Result<Self, StdError> {
18    let next_value = self.0 + rhs.0;
19    if next_value > Self::MAX {
20      Err(StdError::generic_err("Basic points sum exceeds limit"))
21    } else {
22      Ok(Self(next_value))
23    }
24  }
25
26  pub fn from_ratio(numerator: Uint128, denominator: Uint128) -> Result<Self, StdError> {
27    numerator
28      .checked_multiply_ratio(Self::MAX, denominator)
29      .map_err(|_| StdError::generic_err("Checked multiply ratio error!"))?
30      .u128()
31      .try_into()
32  }
33
34  pub fn percent(percent: u16) -> BasicPoints {
35    BasicPoints(percent * 100)
36  }
37
38  pub fn reverse(self) -> BasicPoints {
39    BasicPoints(Self::MAX - self.0)
40  }
41
42  pub fn decimal(self) -> Decimal {
43    Decimal::from_ratio(self.0, Self::MAX)
44  }
45
46  pub fn div_decimal(self, rhs: Self) -> Decimal {
47    if self.is_zero() {
48      return Decimal::zero();
49    }
50    Decimal::from_ratio(self.0, rhs.0)
51  }
52
53  #[inline]
54  pub const fn max() -> Self {
55    BasicPoints(BasicPoints::MAX)
56  }
57
58  #[inline]
59  pub const fn one() -> Self {
60    BasicPoints(BasicPoints::MAX)
61  }
62
63  #[inline]
64  pub const fn zero() -> Self {
65    BasicPoints(0)
66  }
67
68  pub const fn u16(self) -> u16 {
69    self.0
70  }
71
72  pub const fn is_max(self) -> bool {
73    self.0 == BasicPoints::MAX
74  }
75  pub const fn is_zero(self) -> bool {
76    self.0 == 0
77  }
78}
79
80impl TryFrom<u16> for BasicPoints {
81  type Error = StdError;
82
83  fn try_from(value: u16) -> Result<Self, Self::Error> {
84    if value <= Self::MAX {
85      Ok(Self(value))
86    } else {
87      Err(StdError::generic_err(format!("Basic points conversion error. {0} > 10000", value)))
88    }
89  }
90}
91
92impl TryFrom<u128> for BasicPoints {
93  type Error = StdError;
94
95  fn try_from(value: u128) -> Result<Self, Self::Error> {
96    if value <= Self::MAX as u128 {
97      Ok(Self(value as u16))
98    } else {
99      Err(StdError::generic_err(format!("Basic points conversion error. {0} > 10000", value)))
100    }
101  }
102}
103
104impl TryFrom<Decimal> for BasicPoints {
105  type Error = StdError;
106
107  fn try_from(value: Decimal) -> Result<Self, Self::Error> {
108    if value > Decimal::one() {
109      Err(StdError::generic_err(format!("Basic points conversion error. {0} > 10000", value)))
110    } else {
111      BasicPoints::from_ratio(value.numerator(), value.denominator())
112    }
113  }
114}
115
116impl From<BasicPoints> for u16 {
117  fn from(value: BasicPoints) -> Self {
118    value.0
119  }
120}
121
122impl From<BasicPoints> for Uint128 {
123  fn from(value: BasicPoints) -> Self {
124    Uint128::from(u16::from(value))
125  }
126}
127
128impl Mul<Uint128> for BasicPoints {
129  type Output = Uint128;
130
131  fn mul(self, rhs: Uint128) -> Self::Output {
132    if self.is_max() {
133      rhs
134    } else {
135      rhs.multiply_ratio(self.0, Self::MAX)
136    }
137  }
138}
139
140impl Mul<Decimal> for BasicPoints {
141  type Output = Decimal;
142
143  fn mul(self, rhs: Decimal) -> Self::Output {
144    Decimal::from_ratio(
145      rhs.numerator() * Uint128::from(self.0),
146      rhs.denominator() * Uint128::from(Self::MAX),
147    )
148  }
149}