1#![allow(clippy::assign_op_pattern)]
12#![allow(clippy::ptr_offset_with_cast)]
13#![allow(clippy::manual_range_contains)]
14use crate::{
15 MathError, Rate, TryAdd, TryDiv, TryMul, TrySub, BIPS_SCALER, HALF_WAD, PERCENT_SCALER, SCALE,
16 WAD,
17};
18use arrayref::{array_mut_ref, array_ref};
19use solana_program::program_error::ProgramError;
20use solana_program::program_pack::{Pack, Sealed};
21use std::{convert::TryFrom, fmt};
22use uint::construct_uint;
23
24construct_uint! {
26 pub struct U192(3);
27}
28
29#[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd, Eq, Ord)]
31pub struct Decimal(pub U192);
32
33impl Decimal {
34 pub fn one() -> Self {
36 Self(Self::wad())
37 }
38
39 pub fn zero() -> Self {
41 Self(U192::zero())
42 }
43
44 fn wad() -> U192 {
46 U192::from(WAD)
47 }
48
49 fn half_wad() -> U192 {
51 U192::from(HALF_WAD)
52 }
53
54 pub fn from_percent(percent: u8) -> Self {
56 Self(U192::from(percent as u64 * PERCENT_SCALER))
57 }
58
59 pub fn from_bips(percent: u64) -> Self {
60 Self(U192::from(percent * BIPS_SCALER))
61 }
62 #[allow(clippy::wrong_self_convention)]
64 pub fn to_scaled_val(&self) -> Result<u128, ProgramError> {
65 Ok(u128::try_from(self.0).map_err(|_| MathError::UnableToRoundU128)?)
66 }
67
68 pub fn from_scaled_val(scaled_val: u128) -> Self {
70 Self(U192::from(scaled_val))
71 }
72
73 pub fn try_round_u64(&self) -> Result<u64, ProgramError> {
75 let rounded_val = Self::half_wad()
76 .checked_add(self.0)
77 .ok_or(MathError::AddOverflow)?
78 .checked_div(Self::wad())
79 .ok_or(MathError::DividedByZero)?;
80 Ok(u64::try_from(rounded_val).map_err(|_| MathError::UnableToRoundU64)?)
81 }
82
83 pub fn try_ceil_u64(&self) -> Result<u64, ProgramError> {
85 let ceil_val = Self::wad()
86 .checked_sub(U192::from(1u64))
87 .ok_or(MathError::SubUnderflow)?
88 .checked_add(self.0)
89 .ok_or(MathError::AddOverflow)?
90 .checked_div(Self::wad())
91 .ok_or(MathError::DividedByZero)?;
92 Ok(u64::try_from(ceil_val).map_err(|_| MathError::UnableToRoundU64)?)
93 }
94
95 pub fn try_floor_u64(&self) -> Result<u64, ProgramError> {
97 let ceil_val = self
98 .0
99 .checked_div(Self::wad())
100 .ok_or(MathError::DividedByZero)?;
101 Ok(u64::try_from(ceil_val).map_err(|_| MathError::UnableToRoundU64)?)
102 }
103}
104
105impl fmt::Display for Decimal {
106 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
107 let mut scaled_val = self.0.to_string();
108 if scaled_val.len() <= SCALE {
109 scaled_val.insert_str(0, &vec!["0"; SCALE - scaled_val.len()].join(""));
110 scaled_val.insert_str(0, "0.");
111 } else {
112 scaled_val.insert(scaled_val.len() - SCALE, '.');
113 }
114 f.write_str(&scaled_val)
115 }
116}
117
118impl From<u64> for Decimal {
119 fn from(val: u64) -> Self {
120 Self(Self::wad() * U192::from(val))
121 }
122}
123
124impl From<u128> for Decimal {
125 fn from(val: u128) -> Self {
126 Self(Self::wad() * U192::from(val))
127 }
128}
129
130impl From<Rate> for Decimal {
131 fn from(val: Rate) -> Self {
132 Self(U192::from(val.to_scaled_val()))
133 }
134}
135
136impl TryAdd for Decimal {
137 fn try_add(self, rhs: Self) -> Result<Self, ProgramError> {
138 Ok(Self(
139 self.0.checked_add(rhs.0).ok_or(MathError::AddOverflow)?,
140 ))
141 }
142}
143
144impl TrySub for Decimal {
145 fn try_sub(self, rhs: Self) -> Result<Self, ProgramError> {
146 Ok(Self(
147 self.0.checked_sub(rhs.0).ok_or(MathError::MulOverflow)?,
148 ))
149 }
150}
151
152impl TryDiv<u64> for Decimal {
153 fn try_div(self, rhs: u64) -> Result<Self, ProgramError> {
154 Ok(Self(
155 self.0
156 .checked_div(U192::from(rhs))
157 .ok_or(MathError::DividedByZero)?,
158 ))
159 }
160}
161
162impl TryDiv<Rate> for Decimal {
163 fn try_div(self, rhs: Rate) -> Result<Self, ProgramError> {
164 self.try_div(Self::from(rhs))
165 }
166}
167
168impl TryDiv<Decimal> for Decimal {
169 fn try_div(self, rhs: Self) -> Result<Self, ProgramError> {
170 Ok(Self(
171 self.0
172 .checked_mul(Self::wad())
173 .ok_or(MathError::MulOverflow)?
174 .checked_div(rhs.0)
175 .ok_or(MathError::DividedByZero)?,
176 ))
177 }
178}
179
180impl TryMul<u64> for Decimal {
181 fn try_mul(self, rhs: u64) -> Result<Self, ProgramError> {
182 Ok(Self(
183 self.0
184 .checked_mul(U192::from(rhs))
185 .ok_or(MathError::MulOverflow)?,
186 ))
187 }
188}
189
190impl TryMul<Rate> for Decimal {
191 fn try_mul(self, rhs: Rate) -> Result<Self, ProgramError> {
192 self.try_mul(Self::from(rhs))
193 }
194}
195
196impl TryMul<Decimal> for Decimal {
197 fn try_mul(self, rhs: Self) -> Result<Self, ProgramError> {
198 Ok(Self(
199 self.0
200 .checked_mul(rhs.0)
201 .ok_or(MathError::MulOverflow)?
202 .checked_div(Self::wad())
203 .ok_or(MathError::DividedByZero)?,
204 ))
205 }
206}
207impl Sealed for Decimal {}
208impl Pack for Decimal {
209 const LEN: usize = 16;
210 fn pack_into_slice(&self, dst: &mut [u8]) {
211 let output = array_mut_ref![dst, 0, Decimal::LEN];
212 *output = self
213 .to_scaled_val()
214 .expect("Decimal cannot be packed")
215 .to_le_bytes();
216 }
217 fn unpack_from_slice(src: &[u8]) -> Result<Self, ProgramError> {
218 let input = array_ref![src, 0, Decimal::LEN];
219 Ok(Decimal::from_scaled_val(u128::from_le_bytes(*input)))
220 }
221}
222
223#[cfg(test)]
224mod test {
225 use super::*;
226 use crate::SCALE;
227
228 #[test]
229 fn test_scaler() {
230 assert_eq!(U192::exp10(SCALE), Decimal::wad());
231 }
232}