use cosmwasm_std::{Decimal256, StdError, StdResult, Uint128, Uint64};
use astroport::asset::Decimal256Ext;
const ITERATIONS: u8 = 64;
pub const MAX_AMP: u64 = 1_000_000;
pub const MAX_AMP_CHANGE: u64 = 10;
pub const MIN_AMP_CHANGING_TIME: u64 = 86400;
pub const AMP_PRECISION: u64 = 100;
pub const N_COINS: Decimal256 = Decimal256::raw(2000000000000000000);
pub const TOL: Decimal256 = Decimal256::raw(1000000000000);
pub(crate) fn compute_d(amp: Uint64, pools: &[Decimal256]) -> StdResult<Decimal256> {
let leverage = Decimal256::from_ratio(amp, AMP_PRECISION) * N_COINS;
let amount_a_times_coins = pools[0] * N_COINS;
let amount_b_times_coins = pools[1] * N_COINS;
let sum_x = pools[0].checked_add(pools[1])?; if sum_x.is_zero() {
Ok(Decimal256::zero())
} else {
let mut d_previous: Decimal256;
let mut d: Decimal256 = sum_x;
for _ in 0..ITERATIONS {
let d_product = d.pow(3) / (amount_a_times_coins * amount_b_times_coins);
d_previous = d;
d = calculate_step(d, leverage, sum_x, d_product)?;
if d.abs_diff(d_previous) <= TOL {
return Ok(d);
}
}
Err(StdError::generic_err(
"Newton method for D failed to converge",
))
}
}
fn calculate_step(
initial_d: Decimal256,
leverage: Decimal256,
sum_x: Decimal256,
d_product: Decimal256,
) -> StdResult<Decimal256> {
let leverage_mul = leverage.checked_mul(sum_x)?;
let d_p_mul = d_product.checked_mul(N_COINS)?;
let l_val = leverage_mul.checked_add(d_p_mul)?.checked_mul(initial_d)?;
let leverage_sub = initial_d.checked_mul(leverage - Decimal256::one())?;
let n_coins_sum = d_product.checked_mul(N_COINS.checked_add(Decimal256::one())?)?;
let r_val = leverage_sub.checked_add(n_coins_sum)?;
l_val
.checked_div(r_val)
.map_err(|e| StdError::generic_err(e.to_string()))
}
pub(crate) fn calc_y(
amp: Uint64,
new_amount: Decimal256,
xp: &[Decimal256],
target_precision: u8,
) -> StdResult<Uint128> {
let d = compute_d(amp, xp)?;
let leverage = Decimal256::from_ratio(amp, 1u8) * N_COINS;
let amp_prec = Decimal256::from_ratio(AMP_PRECISION, 1u8);
let c = d.checked_pow(3)?.checked_mul(amp_prec)?
/ new_amount
.checked_mul(N_COINS * N_COINS)?
.checked_mul(leverage)?;
let b = new_amount.checked_add(d.checked_mul(amp_prec)? / leverage)?;
let mut y_prev;
let mut y = d;
for _ in 0..ITERATIONS {
y_prev = y;
y = y
.checked_pow(2)?
.checked_add(c)?
.checked_div(y.checked_mul(N_COINS)?.checked_add(b)?.checked_sub(d)?)
.map_err(|e| StdError::generic_err(e.to_string()))?;
if y.abs_diff(y_prev) <= TOL {
return y.to_uint128_with_precision(target_precision);
}
}
Err(StdError::generic_err("y is not converging"))
}