1#![allow(clippy::assign_op_pattern)]
17#![allow(clippy::ptr_offset_with_cast)]
18#![allow(clippy::reversed_empty_ranges)]
19#![allow(clippy::manual_range_contains)]
20
21use crate::{
22 error::LendingError,
23 math::{common::*, decimal::Decimal},
24};
25use solana_program::program_error::ProgramError;
26use std::{convert::TryFrom, fmt};
27use uint::construct_uint;
28
29construct_uint! {
31 pub struct U128(2);
32}
33
34#[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd, Eq, Ord)]
36pub struct Rate(pub U128);
37
38impl Rate {
39 pub fn one() -> Self {
41 Self(Self::wad())
42 }
43
44 pub fn zero() -> Self {
46 Self(U128::from(0))
47 }
48
49 fn wad() -> U128 {
51 U128::from(WAD)
52 }
53
54 pub fn from_percent(percent: u8) -> Self {
56 Self(U128::from(percent as u64 * PERCENT_SCALER))
57 }
58
59 #[allow(clippy::wrong_self_convention)]
61 pub fn to_scaled_val(&self) -> u128 {
62 self.0.as_u128()
63 }
64
65 pub fn from_scaled_val(scaled_val: u64) -> Self {
67 Self(U128::from(scaled_val))
68 }
69
70 pub fn try_pow(&self, mut exp: u64) -> Result<Rate, ProgramError> {
72 let mut base = *self;
73 let mut ret = if exp % 2 != 0 {
74 base
75 } else {
76 Rate(Self::wad())
77 };
78
79 while exp > 0 {
80 exp /= 2;
81 base = base.try_mul(base)?;
82
83 if exp % 2 != 0 {
84 ret = ret.try_mul(base)?;
85 }
86 }
87
88 Ok(ret)
89 }
90}
91
92impl fmt::Display for Rate {
93 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94 let mut scaled_val = self.0.to_string();
95 if scaled_val.len() <= SCALE {
96 scaled_val.insert_str(0, &vec!["0"; SCALE - scaled_val.len()].join(""));
97 scaled_val.insert_str(0, "0.");
98 } else {
99 scaled_val.insert(scaled_val.len() - SCALE, '.');
100 }
101 f.write_str(&scaled_val)
102 }
103}
104
105impl TryFrom<Decimal> for Rate {
106 type Error = ProgramError;
107 fn try_from(decimal: Decimal) -> Result<Self, Self::Error> {
108 Ok(Self(U128::from(decimal.to_scaled_val()?)))
109 }
110}
111
112impl TryAdd for Rate {
113 fn try_add(self, rhs: Self) -> Result<Self, ProgramError> {
114 Ok(Self(
115 self.0
116 .checked_add(rhs.0)
117 .ok_or(LendingError::MathOverflow)?,
118 ))
119 }
120}
121
122impl TrySub for Rate {
123 fn try_sub(self, rhs: Self) -> Result<Self, ProgramError> {
124 Ok(Self(
125 self.0
126 .checked_sub(rhs.0)
127 .ok_or(LendingError::MathOverflow)?,
128 ))
129 }
130}
131
132impl TryDiv<u64> for Rate {
133 fn try_div(self, rhs: u64) -> Result<Self, ProgramError> {
134 Ok(Self(
135 self.0
136 .checked_div(U128::from(rhs))
137 .ok_or(LendingError::MathOverflow)?,
138 ))
139 }
140}
141
142impl TryDiv<Rate> for Rate {
143 fn try_div(self, rhs: Self) -> Result<Self, ProgramError> {
144 Ok(Self(
145 self.0
146 .checked_mul(Self::wad())
147 .ok_or(LendingError::MathOverflow)?
148 .checked_div(rhs.0)
149 .ok_or(LendingError::MathOverflow)?,
150 ))
151 }
152}
153
154impl TryMul<u64> for Rate {
155 fn try_mul(self, rhs: u64) -> Result<Self, ProgramError> {
156 Ok(Self(
157 self.0
158 .checked_mul(U128::from(rhs))
159 .ok_or(LendingError::MathOverflow)?,
160 ))
161 }
162}
163
164impl TryMul<Rate> for Rate {
165 fn try_mul(self, rhs: Self) -> Result<Self, ProgramError> {
166 Ok(Self(
167 self.0
168 .checked_mul(rhs.0)
169 .ok_or(LendingError::MathOverflow)?
170 .checked_div(Self::wad())
171 .ok_or(LendingError::MathOverflow)?,
172 ))
173 }
174}
175
176#[cfg(test)]
177mod test {
178 use super::*;
179 use std::convert::TryInto;
180
181 #[test]
182 fn test_scaled_val() {
183 assert_eq!(Rate::from_percent(50).to_scaled_val(), HALF_WAD as u128);
184 }
185
186 #[test]
187 fn checked_pow() {
188 assert_eq!(Rate::one(), Rate::one().try_pow(u64::MAX).unwrap());
189 assert_eq!(
190 Rate::from_percent(200).try_pow(7).unwrap(),
191 Decimal::from(128u64).try_into().unwrap()
192 );
193 }
194
195 #[test]
196 fn test_display() {
197 assert_eq!(
198 Rate::one().try_div(3u64).unwrap().to_string(),
199 "0.333333333333333333"
200 );
201 }
202
203 #[test]
204 fn test_basic_arithmetic() {
205 assert_eq!(
206 Rate::one().try_add(Rate::one()).unwrap(),
207 Rate::from_scaled_val(2 * WAD)
208 );
209
210 assert_eq!(Rate::one().try_sub(Rate::one()).unwrap(), Rate::zero());
211
212 assert_eq!(
213 Rate::from_percent(240)
214 .try_mul(Rate::from_percent(50))
215 .unwrap(),
216 Rate::from_percent(120)
217 );
218 assert_eq!(
219 Rate::from_percent(240).try_mul(10).unwrap(),
220 Decimal::from(24u64).try_into().unwrap()
221 );
222
223 assert_eq!(
224 Rate::from_percent(240)
225 .try_div(Rate::from_percent(60))
226 .unwrap(),
227 Rate::from_scaled_val(4 * WAD)
228 );
229 }
230}