Skip to content

Commit

Permalink
integer based invariant calculation (#92)
Browse files Browse the repository at this point in the history
  • Loading branch information
betterclever authored Apr 29, 2024
2 parents 05a52c5 + 61a703c commit d475eb3
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 44 deletions.
67 changes: 35 additions & 32 deletions contracts/pools/stable_pool/src/math.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::ops::Div;

use cosmwasm_std::{Decimal256, StdError, StdResult, Uint128, Uint256, Uint64};
use dexter::asset::{AssetInfo, Decimal256Ext, DecimalAsset};
use dexter::error::ContractError;
Expand Down Expand Up @@ -39,50 +41,44 @@ pub(crate) fn compute_d(
return Ok(Decimal256::zero());
}

// Sum of all the pools liquidity, Eq - xp: [1242000000, 1542000000, 1456000000] = 4240000000
let sum_x = pools.iter().fold(Decimal256::zero(), |acc, x| acc + (*x));
let sum_x = pools.iter().fold(Uint256::zero(), |acc, x| acc + x.atomics());

let n_coins = pools.len() as u8;

// ann = amp * n Eq - 100 * 3 = 300
let ann = Decimal256::from_integer(amp.checked_mul(n_coins.into())?.u64() / AMP_PRECISION);
let n_coins = Uint64::from(n_coins);
let ann = Uint256::from(amp.u64().div(AMP_PRECISION) * n_coins as u64);
let n_coins = Uint256::from(n_coins as u64);

// Initial D = sum_x, which is the sum of all the pools liquidity
let mut d = sum_x;

// ann_sum_x = ann * sum_x
let ann_sum_x = ann * sum_x;
let ann_sum_x = ann.checked_mul(sum_x)?;

// while abs(D - Dprev) > 1:
for _ in 0..ITERATIONS {
// Start loop: D_P = D_P * D / (_x * N_COINS)
let d_p = pools
.iter()
.try_fold::<_, _, StdResult<_>>(d, |acc, pool| {
let denominator = pool.atomics().checked_mul(n_coins.into())?;
let print_calc_ = acc.checked_multiply_ratio(d, Decimal256::new(denominator));
print_calc_
})?;

let mut dp = d;
for (_, pool) in pools.iter().enumerate() {
let denominator = pool.atomics().checked_mul(n_coins.into())?;
let fraction = (d, denominator);
dp = dp.checked_mul_floor(fraction).unwrap();
}

let d_prev = d;

let numerator = (ann_sum_x + dp.checked_mul(n_coins).unwrap()).checked_mul(d)?;
let denominator = (ann - Uint256::one()).checked_mul(d)?
.checked_add(n_coins.checked_add(Uint256::one())?.checked_mul(dp)?)?;

d = (ann_sum_x + d_p * Decimal256::from_integer(n_coins.u64())) * d
/ ((ann - Decimal256::one()) * d
+ (Decimal256::from_integer(n_coins.u64()) + Decimal256::one()) * d_p);
d = numerator.checked_div(denominator)?;

if d >= d_prev {
if d - d_prev <= Decimal256::with_precision(Uint64::from(1u8), Decimal256::DECIMAL_PLACES)?
{
return Ok(d);
if d - d_prev <= Uint256::from(1u8) {
return Ok(Decimal256::from_atomics(d, Decimal256::DECIMAL_PLACES).unwrap());
}
} else if d_prev - d <= Decimal256::with_precision(Uint64::from(1u8), Decimal256::DECIMAL_PLACES)?
{
return Ok(d);
} else if d_prev - d <= Uint256::from(1u8) {
return Ok(Decimal256::from_atomics(d, Decimal256::DECIMAL_PLACES).unwrap());
}
}

Ok(d)
Ok(Decimal256::from_atomics(d, Decimal256::DECIMAL_PLACES).unwrap())
}

/// ## Description
Expand All @@ -109,12 +105,12 @@ pub(crate) fn calc_y(

let n_coins = Uint64::from(pools.len() as u8);
let ann = Uint256::from(amp.checked_mul(n_coins)?.u64() / AMP_PRECISION);
let mut sum = Decimal256::zero();
let mut sum = Uint256::zero();
let pool_values = pools.iter().map(|asset| asset.amount).collect_vec();

// d is computed with the largest precision possible i.e Decimal256::DECIMAL_PLACES i.e 18
let d = compute_d(amp, &pool_values)?
.to_uint256_with_precision(Decimal256::DECIMAL_PLACES)?;
let d = compute_d(amp, &pool_values)?.to_uint256_with_precision(Decimal256::DECIMAL_PLACES)?;
// println!("d: {:?}", d);

let mut c = d;

Expand All @@ -132,27 +128,31 @@ pub(crate) fn calc_y(
pool_amount.to_uint256_with_precision(Decimal256::DECIMAL_PLACES)? * Uint256::from(n_coins),
)
.map_err(|_| StdError::generic_err("CheckedMultiplyRatioError"))?;
sum += pool_amount;
sum += pool_amount.to_uint256_with_precision(Decimal256::DECIMAL_PLACES)?;
}

let c = c * d / (ann * Uint256::from(n_coins));
let sum = sum.to_uint256_with_precision(Decimal256::DECIMAL_PLACES)?;
// let sum = sum.to_uint256_with_precision(Decimal256::DECIMAL_PLACES)?;

let b = sum + d / ann;

let mut y = d;
// println!("y: {}", y);

let d = y;

for _ in 0..ITERATIONS {
let y_prev = y;
y = (y * y + c) / (y + y + b - d);
// println!("iter: {}, y_new: {}", iter, y);

if y >= y_prev {
if y - y_prev <= Uint256::from(1u8) {
// We need to scale the value from the MAX_PRECISION to the precision of the asset
// We do this by dividing the value by the ratio of the two precisions
let decimal_difference = Decimal256::DECIMAL_PLACES - output_precision as u32; // this is safe because ask_asset_precision is always <= 18
// println!("decimal_difference: {}", decimal_difference);

let precision_ratio = Uint256::from(10u8).pow(decimal_difference as u32);
let y = y.checked_div(precision_ratio)?;

Expand All @@ -162,6 +162,8 @@ pub(crate) fn calc_y(
// We need to scale the value from the MAX_PRECISION to the precision of the asset
// We do this by dividing the value by the ratio of the two precisions
let decimal_difference = Decimal256::DECIMAL_PLACES - output_precision as u32; // this is safe because ask_asset_precision is always <= 18
// println!("decimal_difference: {}", decimal_difference);

let precision_ratio = Uint256::from(10u8).pow(decimal_difference as u32);
let y = y.checked_div(precision_ratio)?;
return Ok(y.try_into()?);
Expand All @@ -174,6 +176,7 @@ pub(crate) fn calc_y(

#[cfg(test)]
mod tests {

use super::*;
use dexter::asset::native_asset;
use sim::StableSwapModel;
Expand Down
14 changes: 7 additions & 7 deletions contracts/pools/stable_pool/tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -764,7 +764,7 @@ fn test_query_on_join_pool() {
)
.unwrap();
assert_eq!(ResponseType::Success {}, join_pool_query_res.response);
assert_eq!(Uint128::from(217_995_261_723_832u128), join_pool_query_res.new_shares);
assert_eq!(Uint128::from(217_995_261_723_840u128), join_pool_query_res.new_shares);

// Execute AddLiquidity via the Vault contract
let msg = VaultExecuteMsg::JoinPool {
Expand Down Expand Up @@ -806,7 +806,7 @@ fn test_query_on_join_pool() {
},
)
.unwrap();
assert_eq!(Uint128::from(217_995_261_723_832u128), recepient_bal_res.balance);
assert_eq!(Uint128::from(217_995_261_723_840u128), recepient_bal_res.balance);

let vault_bal_res: BalanceResponse = app
.wrap()
Expand Down Expand Up @@ -950,7 +950,7 @@ fn test_query_on_join_pool() {
.unwrap();
assert_eq!(ResponseType::Success {}, join_pool_query_res.response);
assert_eq!(
Uint128::from(3686487023559690549804u128),
Uint128::from(3686487023559696294465u128),
join_pool_query_res.new_shares
);

Expand Down Expand Up @@ -1015,7 +1015,7 @@ fn test_query_on_join_pool() {
.unwrap();
assert_eq!(ResponseType::Success {}, join_pool_query_res.response);
assert_eq!(
Uint128::from(1416837573911030650529u128),
Uint128::from(1416837573911032858392u128),
join_pool_query_res.new_shares
);
}
Expand Down Expand Up @@ -1433,7 +1433,7 @@ fn test_on_exit_pool() {
);
assert_eq!(ResponseType::Success {}, exit_pool_query_res.response);
assert_eq!(
Uint128::from(479_662_097_799_569_595_469u128),
Uint128::from(479_662_097_799_569_595_515u128),
exit_pool_query_res.burn_shares
);
assert_eq!(
Expand Down Expand Up @@ -1538,7 +1538,7 @@ fn test_on_exit_pool() {
);
assert_eq!(ResponseType::Success {}, exit_pool_query_res.response);
assert_eq!(
Uint128::from(1734283090619359785410u128),
Uint128::from(1734283090619359785679u128),
exit_pool_query_res.burn_shares
);
assert_eq!(
Expand Down Expand Up @@ -1645,7 +1645,7 @@ fn test_on_exit_pool() {
);
assert_eq!(ResponseType::Success {}, exit_pool_query_res.response);
assert_eq!(
Uint128::from(1_976_713_420_765_243_272_086u128),
Uint128::from(1_976_713_420_765_243_272_248u128),
exit_pool_query_res.burn_shares
);
assert_eq!(
Expand Down
4 changes: 2 additions & 2 deletions contracts/pools/stable_pool/tests/test_scaling_factor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -963,7 +963,7 @@ fn test_5_asset_lsd_pool_with_different_precisions() {
pool_addr.clone(),
pool_id,
assets_msg.clone(),
Uint128::from(5_072_169_964_407_537_367_290_383u128),
Uint128::from(5_072_169_964_407_537_367_258_707u128),
);

// Swap 1 ustatom for uatom
Expand Down Expand Up @@ -1071,7 +1071,7 @@ fn test_5_asset_lsd_pool_with_different_precisions() {
pool_id,
lp_token_addr.clone(),
vec![Asset::new(atom_asset.clone(), Uint128::from(100_000_000_000u128))],
Uint128::from(100_206_787_474_015_742_826_455u128),
Uint128::from(100_206_787_474_015_742_838_214u128),
Some(vec![
// Asset::new(wrapped_atom_asset.clone(), Uint128::from(18_684_894u128)),
Asset::new(wrapped_atom_asset.clone(), Uint128::from(18_682_739u128)),
Expand Down
6 changes: 3 additions & 3 deletions contracts/vault/tests/pool_exit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,7 @@ fn test_exit_pool() {
)
.unwrap();
assert_eq!(
Uint128::from(394669089467585656459u128),
Uint128::from(394669089467585656489u128),
cur_user_lp_balance.balance - new_user_lp_balance.balance
);

Expand Down Expand Up @@ -669,7 +669,7 @@ fn test_exit_pool() {
amount: Uint128::from(257_000000u128),
},
],
max_lp_to_burn: Some(Uint128::from(404_374980_780408_081409u128)),
max_lp_to_burn: Some(Uint128::from(404_374_980_780_408_081_342u128)),
},
})
.unwrap(),
Expand All @@ -679,5 +679,5 @@ fn test_exit_pool() {
stable5_lp_token_addr.clone(),
&exit_msg,
&[]
).unwrap_err().root_cause().to_string(), "MaxLpToBurnError - burn amount 404374980780408081410 is more than maximum LP to burn 404374980780408081409 allowed by the user");
).unwrap_err().root_cause().to_string(), "MaxLpToBurnError - burn amount 404374980780408081343 is more than maximum LP to burn 404374980780408081342 allowed by the user");
}

0 comments on commit d475eb3

Please sign in to comment.