use hopper_runtime::ProgramError;
#[inline(always)]
pub fn checked_add(a: u64, b: u64) -> Result<u64, ProgramError> {
a.checked_add(b).ok_or(ProgramError::ArithmeticOverflow)
}
#[inline(always)]
pub fn checked_sub(a: u64, b: u64) -> Result<u64, ProgramError> {
a.checked_sub(b).ok_or(ProgramError::ArithmeticOverflow)
}
#[inline(always)]
pub fn checked_mul(a: u64, b: u64) -> Result<u64, ProgramError> {
a.checked_mul(b).ok_or(ProgramError::ArithmeticOverflow)
}
#[inline(always)]
pub fn checked_div(a: u64, b: u64) -> Result<u64, ProgramError> {
a.checked_div(b).ok_or(ProgramError::ArithmeticOverflow)
}
#[inline(always)]
pub fn checked_div_ceil(a: u64, b: u64) -> Result<u64, ProgramError> {
if b == 0 {
return Err(ProgramError::ArithmeticOverflow);
}
Ok(a.checked_add(b - 1)
.ok_or(ProgramError::ArithmeticOverflow)?
/ b)
}
#[inline(always)]
pub fn checked_mul_div(a: u64, b: u64, c: u64) -> Result<u64, ProgramError> {
if c == 0 {
return Err(ProgramError::ArithmeticOverflow);
}
let result = (a as u128)
.checked_mul(b as u128)
.ok_or(ProgramError::ArithmeticOverflow)?
/ (c as u128);
to_u64(result)
}
#[inline(always)]
pub fn checked_mul_div_ceil(a: u64, b: u64, c: u64) -> Result<u64, ProgramError> {
if c == 0 {
return Err(ProgramError::ArithmeticOverflow);
}
let numerator = (a as u128)
.checked_mul(b as u128)
.ok_or(ProgramError::ArithmeticOverflow)?;
let c128 = c as u128;
let result = numerator
.checked_add(c128 - 1)
.ok_or(ProgramError::ArithmeticOverflow)?
/ c128;
to_u64(result)
}
#[inline(always)]
pub fn bps_of(amount: u64, basis_points: u16) -> Result<u64, ProgramError> {
checked_mul_div(amount, basis_points as u64, 10_000)
}
#[inline(always)]
pub fn bps_of_ceil(amount: u64, basis_points: u16) -> Result<u64, ProgramError> {
checked_mul_div_ceil(amount, basis_points as u64, 10_000)
}
#[inline(always)]
pub fn checked_pow(base: u64, exp: u32) -> Result<u64, ProgramError> {
if exp == 0 {
return Ok(1);
}
let mut result: u64 = 1;
let mut b = base;
let mut e = exp;
while e > 0 {
if e & 1 == 1 {
result = result.checked_mul(b).ok_or(ProgramError::ArithmeticOverflow)?;
}
e >>= 1;
if e > 0 {
b = b.checked_mul(b).ok_or(ProgramError::ArithmeticOverflow)?;
}
}
Ok(result)
}
#[inline(always)]
pub fn to_u64(val: u128) -> Result<u64, ProgramError> {
if val > u64::MAX as u128 {
return Err(ProgramError::ArithmeticOverflow);
}
Ok(val as u64)
}
#[inline(always)]
pub fn scale_amount(amount: u64, from_decimals: u8, to_decimals: u8) -> Result<u64, ProgramError> {
if from_decimals == to_decimals {
return Ok(amount);
}
if to_decimals > from_decimals {
let factor = 10u128.checked_pow((to_decimals - from_decimals) as u32)
.ok_or(ProgramError::ArithmeticOverflow)?;
let result = (amount as u128)
.checked_mul(factor)
.ok_or(ProgramError::ArithmeticOverflow)?;
to_u64(result)
} else {
let factor = 10u64.checked_pow((from_decimals - to_decimals) as u32)
.ok_or(ProgramError::ArithmeticOverflow)?;
checked_div(amount, factor)
}
}
#[inline(always)]
pub fn scale_amount_ceil(amount: u64, from_decimals: u8, to_decimals: u8) -> Result<u64, ProgramError> {
if from_decimals == to_decimals {
return Ok(amount);
}
if to_decimals > from_decimals {
let factor = 10u128.checked_pow((to_decimals - from_decimals) as u32)
.ok_or(ProgramError::ArithmeticOverflow)?;
let result = (amount as u128)
.checked_mul(factor)
.ok_or(ProgramError::ArithmeticOverflow)?;
to_u64(result)
} else {
let factor = 10u64.checked_pow((from_decimals - to_decimals) as u32)
.ok_or(ProgramError::ArithmeticOverflow)?;
checked_div_ceil(amount, factor)
}
}