1use hopper_runtime::error::ProgramError;
8
9#[inline(always)]
13pub fn checked_add(a: u64, b: u64) -> Result<u64, ProgramError> {
14 a.checked_add(b).ok_or(ProgramError::ArithmeticOverflow)
15}
16
17#[inline(always)]
19pub fn checked_sub(a: u64, b: u64) -> Result<u64, ProgramError> {
20 a.checked_sub(b).ok_or(ProgramError::ArithmeticOverflow)
21}
22
23#[inline(always)]
25pub fn checked_mul(a: u64, b: u64) -> Result<u64, ProgramError> {
26 a.checked_mul(b).ok_or(ProgramError::ArithmeticOverflow)
27}
28
29#[inline(always)]
31pub fn checked_div(a: u64, b: u64) -> Result<u64, ProgramError> {
32 a.checked_div(b).ok_or(ProgramError::ArithmeticOverflow)
33}
34
35#[inline(always)]
37pub fn checked_add_i64(a: i64, b: i64) -> Result<i64, ProgramError> {
38 a.checked_add(b).ok_or(ProgramError::ArithmeticOverflow)
39}
40
41#[inline(always)]
43pub fn checked_sub_i64(a: i64, b: i64) -> Result<i64, ProgramError> {
44 a.checked_sub(b).ok_or(ProgramError::ArithmeticOverflow)
45}
46
47#[inline(always)]
51pub fn div_ceil(a: u64, b: u64) -> Result<u64, ProgramError> {
52 if b == 0 {
53 return Err(ProgramError::ArithmeticOverflow);
54 }
55 Ok(a.div_ceil(b))
56}
57
58#[inline(always)]
63pub fn checked_div_ceil(a: u64, b: u64) -> Result<u64, ProgramError> {
64 if b == 0 {
65 return Err(ProgramError::ArithmeticOverflow);
66 }
67 Ok(a.checked_add(b - 1)
68 .ok_or(ProgramError::ArithmeticOverflow)?
69 / b)
70}
71
72#[inline(always)]
85pub fn checked_mul_div(a: u64, b: u64, c: u64) -> Result<u64, ProgramError> {
86 if c == 0 {
87 return Err(ProgramError::ArithmeticOverflow);
88 }
89 let result = (a as u128)
90 .checked_mul(b as u128)
91 .ok_or(ProgramError::ArithmeticOverflow)?
92 / (c as u128);
93 to_u64(result)
94}
95
96#[inline(always)]
101pub fn checked_mul_div_ceil(a: u64, b: u64, c: u64) -> Result<u64, ProgramError> {
102 if c == 0 {
103 return Err(ProgramError::ArithmeticOverflow);
104 }
105 let numerator = (a as u128)
106 .checked_mul(b as u128)
107 .ok_or(ProgramError::ArithmeticOverflow)?;
108 let c128 = c as u128;
109 let result = numerator
110 .checked_add(c128 - 1)
111 .ok_or(ProgramError::ArithmeticOverflow)?
112 / c128;
113 to_u64(result)
114}
115
116#[inline(always)]
118pub fn to_u64(val: u128) -> Result<u64, ProgramError> {
119 if val > u64::MAX as u128 {
120 return Err(ProgramError::ArithmeticOverflow);
121 }
122 Ok(val as u64)
123}
124
125#[inline(always)]
130pub fn scale_bps(value: u64, bps: u64) -> Result<u64, ProgramError> {
131 checked_mul_div(value, bps, 10_000)
132}
133
134#[inline(always)]
138pub fn bps_of(amount: u64, basis_points: u16) -> Result<u64, ProgramError> {
139 checked_mul_div(amount, basis_points as u64, 10_000)
140}
141
142#[inline(always)]
147pub fn bps_of_ceil(amount: u64, basis_points: u16) -> Result<u64, ProgramError> {
148 checked_mul_div_ceil(amount, basis_points as u64, 10_000)
149}
150
151#[inline(always)]
153pub fn scale_fraction(value: u64, numerator: u64, denominator: u64) -> Result<u64, ProgramError> {
154 checked_mul_div(value, numerator, denominator)
155}
156
157#[inline(always)]
169pub fn scale_amount(amount: u64, from_decimals: u8, to_decimals: u8) -> Result<u64, ProgramError> {
170 if from_decimals == to_decimals {
171 return Ok(amount);
172 }
173 if to_decimals > from_decimals {
174 let factor = 10u128
175 .checked_pow((to_decimals - from_decimals) as u32)
176 .ok_or(ProgramError::ArithmeticOverflow)?;
177 let result = (amount as u128)
178 .checked_mul(factor)
179 .ok_or(ProgramError::ArithmeticOverflow)?;
180 to_u64(result)
181 } else {
182 let factor = 10u64
183 .checked_pow((from_decimals - to_decimals) as u32)
184 .ok_or(ProgramError::ArithmeticOverflow)?;
185 checked_div(amount, factor)
186 }
187}
188
189#[inline(always)]
195pub fn scale_amount_ceil(
196 amount: u64,
197 from_decimals: u8,
198 to_decimals: u8,
199) -> Result<u64, ProgramError> {
200 if from_decimals == to_decimals {
201 return Ok(amount);
202 }
203 if to_decimals > from_decimals {
204 let factor = 10u128
205 .checked_pow((to_decimals - from_decimals) as u32)
206 .ok_or(ProgramError::ArithmeticOverflow)?;
207 let result = (amount as u128)
208 .checked_mul(factor)
209 .ok_or(ProgramError::ArithmeticOverflow)?;
210 to_u64(result)
211 } else {
212 let factor = 10u64
213 .checked_pow((from_decimals - to_decimals) as u32)
214 .ok_or(ProgramError::ArithmeticOverflow)?;
215 checked_div_ceil(amount, factor)
216 }
217}
218
219#[inline(always)]
226pub fn checked_pow(base: u64, exp: u32) -> Result<u64, ProgramError> {
227 if exp == 0 {
228 return Ok(1);
229 }
230 let mut result: u64 = 1;
231 let mut b = base;
232 let mut e = exp;
233 while e > 0 {
234 if e & 1 == 1 {
235 result = result
236 .checked_mul(b)
237 .ok_or(ProgramError::ArithmeticOverflow)?;
238 }
239 e >>= 1;
240 if e > 0 {
241 b = b.checked_mul(b).ok_or(ProgramError::ArithmeticOverflow)?;
242 }
243 }
244 Ok(result)
245}