diff --git a/Cargo.lock b/Cargo.lock index 46f9f7d94..a85b0f38f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3685,7 +3685,7 @@ dependencies = [ [[package]] name = "hydra-dx-math" -version = "7.4.0" +version = "7.4.1" dependencies = [ "approx", "criterion", diff --git a/math/Cargo.toml b/math/Cargo.toml index 19c1b8ee2..5f0bfb8c0 100644 --- a/math/Cargo.toml +++ b/math/Cargo.toml @@ -6,7 +6,7 @@ license = 'Apache-2.0' name = "hydra-dx-math" description = "A collection of utilities to make performing liquidity pool calculations more convenient." repository = 'https://github.com/galacticcouncil/hydradx-math' -version = "7.4.0" +version = "7.4.1" [dependencies] primitive-types = {default-features = false, version = '0.12.0'} diff --git a/math/src/stableswap/math.rs b/math/src/stableswap/math.rs index 2d66d58ae..bc3076bcc 100644 --- a/math/src/stableswap/math.rs +++ b/math/src/stableswap/math.rs @@ -168,7 +168,7 @@ pub fn calculate_withdraw_one_asset( reserve.checked_mul(d1)?.checked_div(d_hp)?.checked_sub(y_hp)? } else { // dx_expected = xp[j] - xp[j] * d1 / d0 - reserve.checked_sub(xp_hp[idx].checked_mul(d1)?.checked_div(d_hp)?)? + reserve.checked_sub(reserve.checked_mul(d1)?.checked_div(d_hp)?)? }; let expected = Balance::try_from(dx_expected).ok()?; @@ -251,8 +251,14 @@ pub(crate) fn calculate_y_given_out( pub fn calculate_d(xp: &[Balance], amplification: Balance) -> Option { let two_u256 = to_u256!(2_u128); - //let mut xp_hp: [U256; 2] = [to_u256!(xp[0]), to_u256!(xp[1])]; + // Filter out zero balance assets, and return error if there is one. + // Either all assets are zero balance, or none are zero balance. + // Otherwise, it breaks the math. let mut xp_hp: Vec = xp.iter().filter(|v| !(*v).is_zero()).map(|v| to_u256!(*v)).collect(); + if xp_hp.len() != xp.len() && !xp_hp.is_empty() { + return None; + } + xp_hp.sort(); let ann = calculate_ann(xp_hp.len(), amplification)?; @@ -307,7 +313,13 @@ pub fn calculate_d(xp: &[Balance], amplification: Balance) -> Optio } pub(crate) fn calculate_y(xp: &[Balance], d: Balance, amplification: Balance) -> Option { + // Filter out zero balance assets, and return error if there is one. + // Either all assets are zero balance, or none are zero balance. + // Otherwise, it breaks the math. let mut xp_hp: Vec = xp.iter().filter(|v| !(*v).is_zero()).map(|v| to_u256!(*v)).collect(); + if xp_hp.len() != xp.len() && !xp_hp.is_empty() { + return None; + } xp_hp.sort(); let ann = calculate_ann(xp_hp.len().checked_add(1)?, amplification)?; diff --git a/math/src/stableswap/tests/invariants.rs b/math/src/stableswap/tests/invariants.rs index 1468a7887..a730aeda7 100644 --- a/math/src/stableswap/tests/invariants.rs +++ b/math/src/stableswap/tests/invariants.rs @@ -145,24 +145,3 @@ proptest! { assert!(y >= reserve_a); } } - -proptest! { - #![proptest_config(ProptestConfig::with_cases(1000))] - #[test] - fn round_trip_d_y_4(reserve_a in asset_reserve(), - reserve_b in asset_reserve(), - reserve_c in asset_reserve(), - reserve_e in asset_reserve(), - amp in amplification(), - ) { - let ann = amp * 256u128; // 4^4 - - let reserve_d = 0u128; - - let d = calculate_d::(&[reserve_a, reserve_b, reserve_c, reserve_d, reserve_e], ann).unwrap(); - let y = calculate_y::(&[reserve_b, reserve_c, reserve_d, reserve_e], d, ann).unwrap(); - - assert!(y - 4 <= reserve_a); - assert!(y >= reserve_a); - } -} diff --git a/math/src/stableswap/tests/two_assets.rs b/math/src/stableswap/tests/two_assets.rs index 4a95327f0..13dd98910 100644 --- a/math/src/stableswap/tests/two_assets.rs +++ b/math/src/stableswap/tests/two_assets.rs @@ -24,6 +24,12 @@ fn test_d_with_zero_reserves() { assert_eq!(calculate_d::(&reserves, 1), Some(0u128)); } +#[test] +fn test_d_with_one_zero_reserves() { + let reserves = [1000u128, 0u128]; + assert_eq!(calculate_d::(&reserves, 1), None); +} + #[test] fn test_y_given_in() { let reserves = [1000u128, 2000u128]; @@ -94,12 +100,12 @@ fn test_shares() { let amp = 100u128; let initial_reserves = &[0u128, 0u128]; - let updated_reserves = &[1000 * ONE, 0u128]; + let updated_reserves = &[1000 * ONE, 500u128]; let result = calculate_shares::(initial_reserves, updated_reserves, amp, 0u128); assert!(result.is_some()); - assert_eq!(result.unwrap(), 1_000_000_000_000_000u128); + assert_eq!(result.unwrap(), 928031226918u128); } #[test] fn remove_one_asset_should_work() {