diff --git a/Cargo.lock b/Cargo.lock index aa4d74c6d..3320fdc64 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3676,7 +3676,7 @@ dependencies = [ [[package]] name = "hydra-dx-math" -version = "7.4.3" +version = "7.4.4" dependencies = [ "approx", "criterion", diff --git a/math/Cargo.toml b/math/Cargo.toml index c6005ee41..bb0d25c17 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.3" +version = "7.4.4" [dependencies] primitive-types = {default-features = false, version = '0.12.0'} diff --git a/math/src/stableswap/tests/invariants.rs b/math/src/stableswap/tests/invariants.rs index a730aeda7..919b3af02 100644 --- a/math/src/stableswap/tests/invariants.rs +++ b/math/src/stableswap/tests/invariants.rs @@ -145,3 +145,72 @@ proptest! { assert!(y >= reserve_a); } } + +fn decimals() -> impl Strategy { + prop_oneof![Just(6), Just(8), Just(10), Just(12), Just(18)] +} + +fn reserve(max: Balance, precision: u32) -> impl Strategy { + let min_reserve = 5 * 10u128.pow(precision) + 10u128.pow(precision); + let max_reserve = max * 10u128.pow(precision); + min_reserve..max_reserve +} + +prop_compose! { + fn generate_reserves(dec_1: u32, dec_2: u32, dec_3: u32) + ( + reserve_1 in reserve(1_000, dec_1), + reserve_2 in reserve(1_000, dec_2), + reserve_3 in reserve(1_000, dec_3), + ) + -> Vec { + vec![reserve_1, reserve_2, reserve_3] + } +} + +prop_compose! { + fn some_pool_reserves() + ( + dec_1 in decimals(), + dec_2 in decimals(), + dec_3 in decimals(), + ) + ( + dec_1 in Just(dec_1), + r in generate_reserves(dec_1, dec_2, dec_3), + ) + -> (Vec, u32) { + (r, dec_1) + } +} +proptest! { + #![proptest_config(ProptestConfig::with_cases(1000))] + #[test] + fn in_given_out_should_work_when_reserves_have_various_decimals( + (reserves, dec_1) in some_pool_reserves(), + amp in amplification(), + ) { + let d0 = calculate_d::(&reserves, amp).unwrap(); + let result = calculate_in_given_out::(&reserves, 0, 1, 10u128.pow(dec_1), amp); + if let Some(amount_in) = result { + let d1 = calculate_d::(&[reserves[0] + amount_in, reserves[1] - 10u128.pow(dec_1), reserves[2]], amp).unwrap(); + assert!(d1 >= d0); + } + } +} + +proptest! { + #![proptest_config(ProptestConfig::with_cases(1000))] + #[test] + fn out_given_in_should_work_when_reserves_have_various_decimals( + (reserves, dec_1) in some_pool_reserves(), + amp in amplification(), + ) { + let d0 = calculate_d::(&reserves, amp).unwrap(); + let result = calculate_out_given_in::(&reserves, 0, 1, 10u128.pow(dec_1), amp); + if let Some(amount_out) = result { + let d1 = calculate_d::(&[reserves[0] + 10u128.pow(dec_1), reserves[1] - amount_out, reserves[2]], amp).unwrap(); + assert!(d1 >= d0); + } + } +} diff --git a/math/src/stableswap/tests/multi_assets.rs b/math/src/stableswap/tests/multi_assets.rs index 7d1fb0fde..a7a40aabc 100644 --- a/math/src/stableswap/tests/multi_assets.rs +++ b/math/src/stableswap/tests/multi_assets.rs @@ -455,3 +455,175 @@ fn calculate_in_given_out_with_zero_fee_should_work_when_reserves_have_different ); assert_eq!(result.unwrap(), (841869902748480839 - 8335345571767138, 0)); } + +#[test] +fn test_compare_precision_results_01() { + let amp = 1000_u128; + + let balances: [Balance; 3] = [ + 1_000_000_000_000_000_000_000, + 3_000_000_000_000_000_000_000, + 5_000_000_000_000_000_000_000, + ]; + + let d_before = calculate_d::(&balances, amp).unwrap(); + let result = calculate_out_given_in_with_fee::( + &balances, + 1, + 2, + 1_000_000_000_000_000_000, + amp, + Permill::from_percent(0), + ); + let updated_reserves = [ + balances[0], + balances[1] + 1_000_000_000_000_000_000, + balances[2] - result.unwrap().0, + ]; + let d_after = calculate_d::(&updated_reserves, amp).unwrap(); + assert!(d_after >= d_before); + assert_eq!(result.unwrap(), (1_000_079_930_281_397_674, 0)); + + let (amount_out, fee) = calculate_out_given_in_with_fee::( + &balances, + 2, + 1, + 1_000_000_000_000_000_000, + amp, + Permill::from_percent(0), + ) + .unwrap(); + assert_eq!((amount_out, fee), (999_919_974_816_739_669, 0)); + let updated_reserves = [ + balances[0], + balances[1] - amount_out, + balances[2] + 1_000_000_000_000_000_000, + ]; + let d_after = calculate_d::(&updated_reserves, amp).unwrap(); + assert!(d_after >= d_before); +} + +#[test] +fn test_compare_precision_results_02() { + let amp = 1000_u128; + + let balances: [Balance; 3] = [1_000_000_000, 3_000_000_000, 5_000_000_000_000_000_000_000]; + + let d_before = calculate_d::(&balances, amp).unwrap(); + let result = calculate_out_given_in_with_fee::( + &balances, + 1, + 2, + 1_000_000, + amp, + Permill::from_percent(0), + ); + let updated_reserves = [balances[0], balances[1] + 1_000_000, balances[2] - result.unwrap().0]; + let d_after = calculate_d::(&updated_reserves, amp).unwrap(); + assert!(d_after >= d_before); + assert_eq!(result.unwrap(), (833_117_894_058_679_760, 0)); + + let (amount_out, fee) = calculate_out_given_in_with_fee::( + &balances, + 2, + 1, + 1_000_000_000_000_000_000, + amp, + Permill::from_percent(0), + ) + .unwrap(); + assert_eq!((amount_out, fee), (1_187_653 + 11996, 0)); + let updated_reserves = [ + balances[0], + balances[1] - amount_out, + balances[2] + 1_000_000_000_000_000_000, + ]; + let d_after = calculate_d::(&updated_reserves, amp).unwrap(); + assert!(d_after >= d_before); +} + +#[test] +fn test_compare_precision_results_03() { + let amp = 1000_u128; + + let balances: [Balance; 3] = [ + 1_000_000_000_000_000_000_000, + 3_000_000_000_000_000_000_000, + 5_000_000_000_000_000_000_000, + ]; + + let d_before = calculate_d::(&balances, amp).unwrap(); + let result = calculate_out_given_in_with_fee::( + &balances, + 1, + 2, + 1_000_000_000_000_000_000, + amp, + Permill::from_percent(0), + ); + let updated_reserves = [ + balances[0], + balances[1] + 1_000_000_000_000_000_000, + balances[2] - result.unwrap().0, + ]; + let d_after = calculate_d::(&updated_reserves, amp).unwrap(); + assert!(d_after >= d_before); + assert_eq!(result.unwrap(), (1_000_079_930_281_397_674, 0)); + + let (amount_in, fee) = calculate_in_given_out_with_fee::( + &balances, + 1, + 2, + 1_000_079_930_281_397_674, + amp, + Permill::from_percent(0), + ) + .unwrap(); + assert_eq!((amount_in, fee), (1000000000000000000, 0)); + let updated_reserves = [ + balances[0], + balances[1] + amount_in, + balances[2] - 1_000_079_930_281_397_674, + ]; + let d_after = calculate_d::(&updated_reserves, amp).unwrap(); + assert!(d_after >= d_before); +} + +#[test] +fn test_compare_precision_results_04() { + let amp = 1000_u128; + + let balances: [Balance; 3] = [1_000_000_000, 3_000_000_000, 5_000_000_000_000_000_000_000]; + + let d_before = calculate_d::(&balances, amp).unwrap(); + let result = calculate_out_given_in_with_fee::( + &balances, + 1, + 2, + 1_000_000, + amp, + Permill::from_percent(0), + ); + let updated_reserves = [balances[0], balances[1] + 1_000_000, balances[2] - result.unwrap().0]; + let d_after = calculate_d::(&updated_reserves, amp).unwrap(); + assert!(d_after >= d_before); + assert_eq!(result.unwrap(), (833_117_894_058_679_760, 0)); + + let (amount_in, fee) = calculate_in_given_out_with_fee::( + &balances, + 1, + 2, + 833_117_894_058_679_760, + amp, + Permill::from_percent(0), + ) + .unwrap(); + assert_eq!((amount_in, fee), (1000001, 0)); + let updated_reserves = [ + balances[0], + balances[1] + amount_in, + balances[2] - 833_117_894_058_679_760, + ]; + let d_after = calculate_d::(&updated_reserves, amp).unwrap(); + assert!(d_after >= d_before); +}