use crate::constants::SCALE;
use crate::error::SolMathError;
use crate::arithmetic::{fp_mul, fp_div};
use crate::mul_div::mul_div_ceil_u128;
use crate::hp::pow_fixed_hp;
pub fn token_to_fp(raw_amount: u64, token_decimals: u8) -> Result<u128, SolMathError> {
let amount = raw_amount as u128;
let decimals = token_decimals as u32;
if decimals > 38 {
return Err(SolMathError::DomainError);
}
if decimals <= 12 {
amount.checked_mul(10u128.pow(12 - decimals)).ok_or(SolMathError::Overflow)
} else {
Ok(amount / 10u128.pow(decimals - 12))
}
}
pub fn fp_to_token_floor(fp_amount: u128, token_decimals: u8) -> Result<u64, SolMathError> {
let decimals = token_decimals as u32;
if decimals > 38 {
return Err(SolMathError::DomainError);
}
let raw = if decimals <= 12 {
fp_amount / 10u128.pow(12 - decimals)
} else {
fp_amount.checked_mul(10u128.pow(decimals - 12)).ok_or(SolMathError::Overflow)?
};
if raw > u64::MAX as u128 {
return Err(SolMathError::Overflow);
}
Ok(raw as u64)
}
pub fn fp_to_token_ceil(fp_amount: u128, token_decimals: u8) -> Result<u64, SolMathError> {
let decimals = token_decimals as u32;
if decimals > 38 {
return Err(SolMathError::DomainError);
}
let divisor = if decimals <= 12 {
10u128.pow(12 - decimals)
} else {
let raw = fp_amount.checked_mul(10u128.pow(decimals - 12)).ok_or(SolMathError::Overflow)?;
if raw > u64::MAX as u128 {
return Err(SolMathError::Overflow);
}
return Ok(raw as u64);
};
let raw = fp_amount.checked_add(divisor - 1).ok_or(SolMathError::Overflow)? / divisor;
if raw > u64::MAX as u128 {
return Err(SolMathError::Overflow);
}
Ok(raw as u64)
}
pub fn weighted_pool_swap(
balance_in: u128,
balance_out: u128,
weight_in: u128,
weight_out: u128,
amount_in: u128,
fee_rate: u128,
) -> Result<(u128, u128), SolMathError> {
if weight_out == 0 {
return Err(SolMathError::DivisionByZero);
}
if weight_in == 0 {
return Err(SolMathError::DomainError);
}
if balance_in == 0 || balance_out == 0 {
return Err(SolMathError::DomainError);
}
if fee_rate > SCALE {
return Err(SolMathError::DomainError);
}
if amount_in == 0 {
return Ok((0, 0));
}
let denominator = balance_in.checked_add(amount_in).ok_or(SolMathError::Overflow)?;
let ratio = fp_div(balance_in, denominator)?;
let weight_ratio = fp_div(weight_in, weight_out)?;
let power = pow_fixed_hp(ratio, weight_ratio)?;
if power > SCALE {
return Err(SolMathError::Overflow);
}
let one_minus_power = SCALE - power;
let gross_out = fp_mul(balance_out, one_minus_power)?;
let fee = mul_div_ceil_u128(gross_out, fee_rate, SCALE)?;
if fee > gross_out {
return Err(SolMathError::Overflow);
}
let net_out = gross_out - fee;
Ok((net_out, fee))
}