astroport_pair_stable/
math.rs1use cosmwasm_std::{Decimal256, StdError, StdResult, Uint128, Uint64};
2
3use astroport::asset::Decimal256Ext;
4
5const ITERATIONS: u8 = 64;
7
8pub const MAX_AMP: u64 = 1_000_000;
9pub const MAX_AMP_CHANGE: u64 = 10;
10pub const MIN_AMP_CHANGING_TIME: u64 = 86400;
11pub const AMP_PRECISION: u64 = 100;
12pub const N_COINS: Decimal256 = Decimal256::raw(2000000000000000000);
14pub const TOL: Decimal256 = Decimal256::raw(1000000000000);
16
17pub fn compute_d(amp: Uint64, pools: &[Decimal256]) -> StdResult<Decimal256> {
24 let leverage = Decimal256::from_ratio(amp, AMP_PRECISION) * N_COINS;
25 let amount_a_times_coins = pools[0] * N_COINS;
26 let amount_b_times_coins = pools[1] * N_COINS;
27
28 let sum_x = pools[0].checked_add(pools[1])?; if sum_x.is_zero() {
30 Ok(Decimal256::zero())
31 } else {
32 let mut d_previous: Decimal256;
33 let mut d: Decimal256 = sum_x;
34
35 for _ in 0..ITERATIONS {
37 let d_product = d.pow(3) / (amount_a_times_coins * amount_b_times_coins);
38 d_previous = d;
39 d = calculate_step(d, leverage, sum_x, d_product)?;
40 if d.abs_diff(d_previous) <= TOL {
42 return Ok(d);
43 }
44 }
45
46 Err(StdError::generic_err(
47 "Newton method for D failed to converge",
48 ))
49 }
50}
51
52fn calculate_step(
58 initial_d: Decimal256,
59 leverage: Decimal256,
60 sum_x: Decimal256,
61 d_product: Decimal256,
62) -> StdResult<Decimal256> {
63 let leverage_mul = leverage.checked_mul(sum_x)?;
64 let d_p_mul = d_product.checked_mul(N_COINS)?;
65
66 let l_val = leverage_mul.checked_add(d_p_mul)?.checked_mul(initial_d)?;
67
68 let leverage_sub = initial_d.checked_mul(leverage - Decimal256::one())?;
69 let n_coins_sum = d_product.checked_mul(N_COINS.checked_add(Decimal256::one())?)?;
70
71 let r_val = leverage_sub.checked_add(n_coins_sum)?;
72
73 l_val
74 .checked_div(r_val)
75 .map_err(|e| StdError::generic_err(e.to_string()))
76}
77
78pub(crate) fn calc_y(
86 amp: Uint64,
87 new_amount: Decimal256,
88 xp: &[Decimal256],
89 target_precision: u8,
90) -> StdResult<Uint128> {
91 let d = compute_d(amp, xp)?;
92 let leverage = Decimal256::from_ratio(amp, 1u8) * N_COINS;
93 let amp_prec = Decimal256::from_ratio(AMP_PRECISION, 1u8);
94
95 let c = d.checked_pow(3)?.checked_mul(amp_prec)?
96 / new_amount
97 .checked_mul(N_COINS * N_COINS)?
98 .checked_mul(leverage)?;
99
100 let b = new_amount.checked_add(d.checked_mul(amp_prec)? / leverage)?;
101
102 let mut y_prev;
104 let mut y = d;
105 for _ in 0..ITERATIONS {
106 y_prev = y;
107 y = y
108 .checked_pow(2)?
109 .checked_add(c)?
110 .checked_div(y.checked_mul(N_COINS)?.checked_add(b)?.checked_sub(d)?)
111 .map_err(|e| StdError::generic_err(e.to_string()))?;
112 if y.abs_diff(y_prev) <= TOL {
113 return y.to_uint128_with_precision(target_precision);
114 }
115 }
116
117 Err(StdError::generic_err("y is not converging"))
119}