port_variable_rate_lending/math/
rate.rs1#![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 fn half_wad() -> U128 {
56 U128::from(HALF_WAD)
57 }
58
59 pub fn from_percent(percent: u8) -> Self {
61 Self(U128::from(percent as u64 * PERCENT_SCALER))
62 }
63
64 #[allow(clippy::wrong_self_convention)]
66 pub fn to_scaled_val(&self) -> u128 {
67 self.0.as_u128()
68 }
69
70 pub fn from_scaled_val(scaled_val: u64) -> Self {
72 Self(U128::from(scaled_val))
73 }
74
75 pub fn try_round_u64(&self) -> Result<u64, ProgramError> {
77 let rounded_val = Self::half_wad()
78 .checked_add(self.0)
79 .ok_or(LendingError::MathOverflow)?
80 .checked_div(Self::wad())
81 .ok_or(LendingError::MathOverflow)?;
82 Ok(u64::try_from(rounded_val).map_err(|_| LendingError::MathOverflow)?)
83 }
84
85 pub fn try_pow(&self, mut exp: u64) -> Result<Rate, ProgramError> {
87 let mut base = *self;
88 let mut ret = if exp % 2 != 0 {
89 base
90 } else {
91 Rate(Self::wad())
92 };
93
94 while exp > 0 {
95 exp /= 2;
96 base = base.try_mul(base)?;
97
98 if exp % 2 != 0 {
99 ret = ret.try_mul(base)?;
100 }
101 }
102
103 Ok(ret)
104 }
105}
106
107impl fmt::Display for Rate {
108 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
109 let mut scaled_val = self.0.to_string();
110 if scaled_val.len() <= SCALE {
111 scaled_val.insert_str(0, &vec!["0"; SCALE - scaled_val.len()].join(""));
112 scaled_val.insert_str(0, "0.");
113 } else {
114 scaled_val.insert(scaled_val.len() - SCALE, '.');
115 }
116 f.write_str(&scaled_val)
117 }
118}
119
120impl TryFrom<Decimal> for Rate {
121 type Error = ProgramError;
122 fn try_from(decimal: Decimal) -> Result<Self, Self::Error> {
123 Ok(Self(U128::from(decimal.to_scaled_val()?)))
124 }
125}
126
127impl TryAdd for Rate {
128 fn try_add(self, rhs: Self) -> Result<Self, ProgramError> {
129 Ok(Self(
130 self.0
131 .checked_add(rhs.0)
132 .ok_or(LendingError::MathOverflow)?,
133 ))
134 }
135}
136
137impl TrySub for Rate {
138 fn try_sub(self, rhs: Self) -> Result<Self, ProgramError> {
139 Ok(Self(
140 self.0
141 .checked_sub(rhs.0)
142 .ok_or(LendingError::MathOverflow)?,
143 ))
144 }
145}
146
147impl TryDiv<u64> for Rate {
148 fn try_div(self, rhs: u64) -> Result<Self, ProgramError> {
149 Ok(Self(
150 self.0
151 .checked_div(U128::from(rhs))
152 .ok_or(LendingError::MathOverflow)?,
153 ))
154 }
155}
156
157impl TryDiv<Rate> for Rate {
158 fn try_div(self, rhs: Self) -> Result<Self, ProgramError> {
159 Ok(Self(
160 self.0
161 .checked_mul(Self::wad())
162 .ok_or(LendingError::MathOverflow)?
163 .checked_div(rhs.0)
164 .ok_or(LendingError::MathOverflow)?,
165 ))
166 }
167}
168
169impl TryMul<u64> for Rate {
170 fn try_mul(self, rhs: u64) -> Result<Self, ProgramError> {
171 Ok(Self(
172 self.0
173 .checked_mul(U128::from(rhs))
174 .ok_or(LendingError::MathOverflow)?,
175 ))
176 }
177}
178
179impl TryMul<Rate> for Rate {
180 fn try_mul(self, rhs: Self) -> Result<Self, ProgramError> {
181 Ok(Self(
182 self.0
183 .checked_mul(rhs.0)
184 .ok_or(LendingError::MathOverflow)?
185 .checked_div(Self::wad())
186 .ok_or(LendingError::MathOverflow)?,
187 ))
188 }
189}
190
191#[cfg(test)]
192mod test {
193 use super::*;
194
195 #[test]
196 fn checked_pow() {
197 assert_eq!(Rate::one(), Rate::one().try_pow(u64::MAX).unwrap());
198 }
199}