diff --git a/zrml/swaps/src/tests.rs b/zrml/swaps/src/tests.rs index 13a7b5685..7a2fe224a 100644 --- a/zrml/swaps/src/tests.rs +++ b/zrml/swaps/src/tests.rs @@ -31,6 +31,8 @@ pub const ASSET_E: Asset = Asset::CategoricalOutcome(0, 69); pub const ASSETS: [Asset; 4] = [ASSET_A, ASSET_B, ASSET_C, ASSET_D]; const _1_2: u128 = BASE / 2; +const _1_10: u128 = BASE / 10; +const _1_20: u128 = BASE / 20; const _1: u128 = BASE; const _2: u128 = 2 * BASE; const _3: u128 = 3 * BASE; @@ -54,7 +56,7 @@ const _10000: u128 = 10000 * BASE; #[test] fn destroy_pool_fails_if_pool_does_not_exist() { ExtBuilder::default().build().execute_with(|| { - create_initial_pool(ScoringRule::CPMM, true); + create_initial_pool(ScoringRule::CPMM, Some(0), true); assert_noop!(Swaps::destroy_pool(42), crate::Error::::PoolDoesNotExist); }); } @@ -62,7 +64,7 @@ fn destroy_pool_fails_if_pool_does_not_exist() { #[test] fn destroy_pool_correctly_cleans_up_pool() { ExtBuilder::default().build().execute_with(|| { - create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, true); + create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, Some(0), true); let pool_id = 0; let alice_balance_before = [ Currencies::free_balance(ASSET_A, &ALICE), @@ -81,7 +83,7 @@ fn destroy_pool_correctly_cleans_up_pool() { fn destroy_pool_emits_correct_event() { ExtBuilder::default().build().execute_with(|| { frame_system::Pallet::::set_block_number(1); - create_initial_pool(ScoringRule::CPMM, true); + create_initial_pool(ScoringRule::CPMM, Some(0), true); let pool_id = 0; assert_ok!(Swaps::destroy_pool(pool_id)); System::assert_last_event(Event::PoolDestroyed(pool_id).into()); @@ -91,7 +93,7 @@ fn destroy_pool_emits_correct_event() { #[test] fn allows_the_full_user_lifecycle() { ExtBuilder::default().build().execute_with(|| { - create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, true); + create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, Some(0), true); assert_ok!(Swaps::pool_join(alice_signed(), 0, _5, vec!(_25, _25, _25, _25),)); @@ -169,7 +171,7 @@ fn allows_the_full_user_lifecycle() { #[test] fn assets_must_be_bounded() { ExtBuilder::default().build().execute_with(|| { - create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, true); + create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, Some(0), true); assert_ok!(Swaps::mutate_pool(0, |pool| { pool.weights.as_mut().unwrap().remove(&ASSET_B); Ok(()) @@ -221,7 +223,7 @@ fn create_pool_generates_a_new_pool_with_correct_parameters_for_cpmm() { let next_pool_before = Swaps::next_pool_id(); assert_eq!(next_pool_before, 0); - create_initial_pool(ScoringRule::CPMM, true); + create_initial_pool(ScoringRule::CPMM, Some(0), true); let next_pool_after = Swaps::next_pool_id(); assert_eq!(next_pool_after, 1); @@ -258,7 +260,7 @@ fn create_pool_generates_a_new_pool_with_correct_parameters_for_rikiddo() { let next_pool_before = Swaps::next_pool_id(); assert_eq!(next_pool_before, 0); - create_initial_pool(ScoringRule::RikiddoSigmoidFeeMarketEma, false); + create_initial_pool(ScoringRule::RikiddoSigmoidFeeMarketEma, None, false); let next_pool_after = Swaps::next_pool_id(); assert_eq!(next_pool_after, 1); @@ -284,13 +286,17 @@ fn destroy_pool_in_subsidy_phase_returns_subsidy_and_closes_pool() { Swaps::destroy_pool_in_subsidy_phase(0), crate::Error::::PoolDoesNotExist ); - create_initial_pool(ScoringRule::CPMM, true); + create_initial_pool(ScoringRule::CPMM, Some(0), true); assert_noop!( Swaps::destroy_pool_in_subsidy_phase(0), crate::Error::::InvalidStateTransition ); - create_initial_pool_with_funds_for_alice(ScoringRule::RikiddoSigmoidFeeMarketEma, false); + create_initial_pool_with_funds_for_alice( + ScoringRule::RikiddoSigmoidFeeMarketEma, + None, + false, + ); let pool_id = 1; // Reserve some funds for subsidy assert_ok!(Swaps::pool_join_subsidy(alice_signed(), pool_id, _25)); @@ -315,7 +321,7 @@ fn destroy_pool_in_subsidy_phase_returns_subsidy_and_closes_pool() { fn distribute_pool_share_rewards() { ExtBuilder::default().build().execute_with(|| { // Create Rikiddo pool - create_initial_pool(ScoringRule::RikiddoSigmoidFeeMarketEma, false); + create_initial_pool(ScoringRule::RikiddoSigmoidFeeMarketEma, None, false); let pool_id = 0; let subsidy_per_acc = ::MinSubsidy::get(); let asset_per_acc = subsidy_per_acc / 10; @@ -386,10 +392,14 @@ fn distribute_pool_share_rewards() { fn end_subsidy_phase_distributes_shares_and_outcome_assets() { ExtBuilder::default().build().execute_with(|| { frame_system::Pallet::::set_block_number(1); - create_initial_pool(ScoringRule::CPMM, true); + create_initial_pool(ScoringRule::CPMM, Some(0), true); assert_noop!(Swaps::end_subsidy_phase(0), crate::Error::::InvalidStateTransition); assert_noop!(Swaps::end_subsidy_phase(1), crate::Error::::PoolDoesNotExist); - create_initial_pool_with_funds_for_alice(ScoringRule::RikiddoSigmoidFeeMarketEma, false); + create_initial_pool_with_funds_for_alice( + ScoringRule::RikiddoSigmoidFeeMarketEma, + None, + false, + ); let pool_id = 1; assert_storage_noop!(Swaps::end_subsidy_phase(pool_id).unwrap()); @@ -443,7 +453,7 @@ fn end_subsidy_phase_distributes_shares_and_outcome_assets() { fn ensure_which_operations_can_be_called_depending_on_the_pool_status() { ExtBuilder::default().build().execute_with(|| { use zeitgeist_primitives::traits::Swaps as _; - create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, true); + create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, Some(0), true); // For this test, we need to give Alice some pool shares, as well. We don't do this in // `create_initial_pool_...` so that there are exacly 100 pool shares, making computations // in other tests easier. @@ -514,11 +524,11 @@ fn ensure_which_operations_can_be_called_depending_on_the_pool_status() { fn get_spot_price_returns_correct_results() { ExtBuilder::default().build().execute_with(|| { // CPMM. - create_initial_pool(ScoringRule::CPMM, true); + create_initial_pool(ScoringRule::CPMM, Some(0), true); assert_eq!(Swaps::get_spot_price(0, ASSETS[0], ASSETS[1]), Ok(BASE)); // Rikiddo. - create_initial_pool(ScoringRule::RikiddoSigmoidFeeMarketEma, false); + create_initial_pool(ScoringRule::RikiddoSigmoidFeeMarketEma, None, false); let pool_id = 1; assert_noop!( Swaps::get_spot_price(pool_id, ASSETS[0], ASSETS[0]), @@ -546,7 +556,7 @@ fn get_spot_price_returns_correct_results() { #[test] fn in_amount_must_be_equal_or_less_than_max_in_ratio() { ExtBuilder::default().build().execute_with(|| { - create_initial_pool(ScoringRule::CPMM, true); + create_initial_pool(ScoringRule::CPMM, Some(0), true); assert_ok!(Currencies::deposit(ASSET_A, &ALICE, u64::MAX.into())); @@ -618,7 +628,7 @@ fn pool_join_amount_satisfies_max_in_ratio_constraints() { fn set_pool_to_stale_fails_if_origin_is_not_root() { ExtBuilder::default().build().execute_with(|| { let idx = if let Asset::CategoricalOutcome(_, idx) = ASSET_A { idx } else { 0 }; - create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, true); + create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, Some(0), true); assert_ok!(MarketCommons::push_market(mock_market(69))); assert_ok!(MarketCommons::insert_market_pool(0, 0)); assert_noop!( @@ -631,7 +641,7 @@ fn set_pool_to_stale_fails_if_origin_is_not_root() { #[test] fn out_amount_must_be_equal_or_less_than_max_out_ratio() { ExtBuilder::default().build().execute_with(|| { - create_initial_pool(ScoringRule::CPMM, true); + create_initial_pool(ScoringRule::CPMM, Some(0), true); assert_noop!( Swaps::swap_exact_amount_out( @@ -656,7 +666,7 @@ fn out_amount_must_be_equal_or_less_than_max_out_ratio() { #[test] fn pool_join_or_exit_raises_on_zero_value() { ExtBuilder::default().build().execute_with(|| { - create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, true); + create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, Some(0), true); assert_noop!( Swaps::pool_join(alice_signed(), 0, 0, vec!(_1, _1, _1, _1)), @@ -695,7 +705,7 @@ fn pool_exit_decreases_correct_pool_parameters() { ExtBuilder::default().build().execute_with(|| { ::ExitFee::set(&0u128); frame_system::Pallet::::set_block_number(1); - create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, true); + create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, Some(0), true); assert_ok!(Swaps::pool_join(alice_signed(), 0, _1, vec!(_1, _1, _1, _1),)); @@ -724,7 +734,7 @@ fn pool_exit_decreases_correct_pool_parameters() { fn pool_exit_emits_correct_events() { ExtBuilder::default().build().execute_with(|| { frame_system::Pallet::::set_block_number(1); - create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, true); + create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, Some(0), true); assert_ok!(Swaps::pool_exit(Origin::signed(BOB), 0, _1, vec!(1, 2, 3, 4),)); let amount = _1 - BASE / 10; // Subtract 10% fees! System::assert_last_event( @@ -744,7 +754,7 @@ fn pool_exit_emits_correct_events() { fn pool_exit_decreases_correct_pool_parameters_with_exit_fee() { ExtBuilder::default().build().execute_with(|| { frame_system::Pallet::::set_block_number(1); - create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, true); + create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, Some(0), true); assert_ok!(Swaps::pool_exit(Origin::signed(BOB), 0, _10, vec!(_1, _1, _1, _1),)); @@ -779,7 +789,7 @@ fn pool_exit_decreases_correct_pool_parameters_on_stale_pool() { // Test is the same as ExtBuilder::default().build().execute_with(|| { frame_system::Pallet::::set_block_number(1); - create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, true); + create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, Some(0), true); assert_ok!(MarketCommons::push_market(mock_market(69))); assert_ok!(MarketCommons::insert_market_pool(0, 0)); @@ -818,7 +828,11 @@ fn pool_exit_subsidy_unreserves_correct_values() { // Events cannot be emitted on block zero... frame_system::Pallet::::set_block_number(1); - create_initial_pool_with_funds_for_alice(ScoringRule::RikiddoSigmoidFeeMarketEma, false); + create_initial_pool_with_funds_for_alice( + ScoringRule::RikiddoSigmoidFeeMarketEma, + None, + false, + ); let pool_id = 0; // Add some subsidy @@ -876,7 +890,11 @@ fn pool_exit_subsidy_unreserves_correct_values() { #[test] fn pool_exit_subsidy_fails_if_no_subsidy_is_provided() { ExtBuilder::default().build().execute_with(|| { - create_initial_pool_with_funds_for_alice(ScoringRule::RikiddoSigmoidFeeMarketEma, false); + create_initial_pool_with_funds_for_alice( + ScoringRule::RikiddoSigmoidFeeMarketEma, + None, + false, + ); assert_noop!( Swaps::pool_exit_subsidy(alice_signed(), 0, _1), crate::Error::::NoSubsidyProvided @@ -887,7 +905,11 @@ fn pool_exit_subsidy_fails_if_no_subsidy_is_provided() { #[test] fn pool_exit_subsidy_fails_if_amount_is_zero() { ExtBuilder::default().build().execute_with(|| { - create_initial_pool_with_funds_for_alice(ScoringRule::RikiddoSigmoidFeeMarketEma, false); + create_initial_pool_with_funds_for_alice( + ScoringRule::RikiddoSigmoidFeeMarketEma, + None, + false, + ); assert_noop!( Swaps::pool_exit_subsidy(alice_signed(), 0, 0), crate::Error::::ZeroAmount @@ -908,7 +930,7 @@ fn pool_exit_subsidy_fails_if_pool_does_not_exist() { #[test] fn pool_exit_subsidy_fails_if_scoring_rule_is_not_rikiddo() { ExtBuilder::default().build().execute_with(|| { - create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, true); + create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, Some(0), true); assert_noop!( Swaps::pool_exit_subsidy(alice_signed(), 0, _1), crate::Error::::InvalidScoringRule @@ -916,144 +938,115 @@ fn pool_exit_subsidy_fails_if_scoring_rule_is_not_rikiddo() { }); } -#[test] -fn pool_exit_with_exact_pool_amount_exchanges_correct_values() { +#[test_case(49_999_999_665, 12_272_234_300, 0, 0; "no_fees")] +#[test_case(45_082_061_850, 12_272_234_300, _1_10, 0; "with_exit_fees")] +#[test_case(46_403_174_924, 11_820_024_200, 0, _1_20; "with_swap_fees")] +#[test_case(41_836_235_739, 11_820_024_200, _1_10, _1_20; "with_both_fees")] +fn pool_exit_with_exact_pool_amount_exchanges_correct_values( + asset_amount_expected: BalanceOf, + pool_amount_expected: BalanceOf, + exit_fee: BalanceOf, + swap_fee: BalanceOf, +) { ExtBuilder::default().build().execute_with(|| { - ::ExitFee::set(&0u128); + let bound = _4; + let asset_amount_joined = _5; + ::ExitFee::set(&exit_fee); frame_system::Pallet::::set_block_number(1); - create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, true); - assert_ok!(Swaps::pool_join_with_exact_asset_amount(alice_signed(), 0, ASSET_A, _5, 0)); - let pool_amount = Currencies::free_balance(Swaps::pool_shares_id(0), &ALICE); - assert_ok!(Swaps::pool_exit_with_exact_pool_amount( + create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, Some(swap_fee), true); + assert_ok!(Swaps::pool_join_with_exact_asset_amount( alice_signed(), 0, ASSET_A, - pool_amount, - _4 + asset_amount_joined, + 0 )); - System::assert_last_event( - Event::PoolExitWithExactPoolAmount(PoolAssetEvent { - asset: ASSET_A, - bound: _4, - cpep: CommonPoolEventParams { pool_id: 0, who: 0 }, - transferred: _5 - 335, - pool_amount, - }) - .into(), - ); - assert_all_parameters([_25 - 335, _25, _25, _25], 0, [_100 + 335, _100, _100, _100], _100) - }); -} - -#[test] -fn pool_exit_with_exact_pool_amount_exchanges_correct_values_with_fee() { - ExtBuilder::default().build().execute_with(|| { - ::ExitFee::set(&(BASE / 10)); - frame_system::Pallet::::set_block_number(1); - create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, true); - assert_ok!(Swaps::pool_join_with_exact_asset_amount(alice_signed(), 0, ASSET_A, _5, 0)); let pool_amount = Currencies::free_balance(Swaps::pool_shares_id(0), &ALICE); + assert_eq!(pool_amount, pool_amount_expected); // (This is just a sanity check) + assert_ok!(Swaps::pool_exit_with_exact_pool_amount( alice_signed(), 0, ASSET_A, pool_amount, - _4 + bound, )); - assert_all_parameters( - [245_082_061_850, _25, _25, _25], - 0, - [1_004_917_938_150, _100, _100, _100], - _100, - ); System::assert_last_event( Event::PoolExitWithExactPoolAmount(PoolAssetEvent { asset: ASSET_A, - bound: _4, + bound, cpep: CommonPoolEventParams { pool_id: 0, who: 0 }, - transferred: 45_082_061_850, + transferred: asset_amount_expected, pool_amount, }) .into(), ); + assert_all_parameters( + [_25 - asset_amount_joined + asset_amount_expected, _25, _25, _25], + 0, + [_100 + asset_amount_joined - asset_amount_expected, _100, _100, _100], + _100, + ) }); } -#[test] -fn pool_exit_with_exact_asset_amount_exchanges_correct_values() { +#[test_case(49999999297, 12272234248, 0, 0; "no_fees")] +#[test_case(45_082_061_850, 12272234293, _1_10, 0; "with_exit_fees")] +#[test_case(46403174873, 11820024153, 0, _1_20; "with_swap_fees")] +#[test_case(41_836_235_739, 11820024187, _1_10, _1_20; "with_both_fees")] +fn pool_exit_with_exact_asset_amount_exchanges_correct_values( + asset_amount: BalanceOf, + pool_amount_expected: BalanceOf, + exit_fee: BalanceOf, + swap_fee: BalanceOf, +) { + // This test is based on `pool_exit_with_exact_pool_amount_exchanges_correct_values`. Due to + // rounding errors, the numbers aren't _exactly_ the same, which results in this test ending up + // with a little bit of dust in some accounts. ExtBuilder::default().build().execute_with(|| { - ::ExitFee::set(&0u128); + let bound = _2; + let asset_amount_joined = _5; + ::ExitFee::set(&exit_fee); frame_system::Pallet::::set_block_number(1); - create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, true); - let asset_before_join = Currencies::free_balance(ASSET_A, &ALICE); - assert_ok!(Swaps::pool_join_with_exact_pool_amount(alice_signed(), 0, ASSET_A, _1, _5)); - let pool_amount_before_exit = Currencies::free_balance(Swaps::pool_shares_id(0), &ALICE); - let asset_after_join = asset_before_join - Currencies::free_balance(ASSET_A, &ALICE); - assert_ok!(Swaps::pool_exit_with_exact_asset_amount( + create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, Some(swap_fee), true); + assert_ok!(Swaps::pool_join_with_exact_asset_amount( alice_signed(), 0, ASSET_A, - asset_after_join - 1000, - _1 + asset_amount_joined, + 0 )); - let pool_amount_after_exit = Currencies::free_balance(Swaps::pool_shares_id(0), &ALICE); - let pool_amount = pool_amount_before_exit - pool_amount_after_exit; - System::assert_last_event( - Event::PoolExitWithExactAssetAmount(PoolAssetEvent { - asset: ASSET_A, - bound: _1, - cpep: CommonPoolEventParams { pool_id: 0, who: 0 }, - transferred: asset_after_join - 1000, - pool_amount, - }) - .into(), - ); - assert_eq!(asset_after_join, 40604010000); - assert_all_parameters( - [_25 - 1000, _25, _25, _25], - 100, - [_100 + 1000, _100, _100, _100], - 1000000000100, - ) - }); -} -#[test] -fn pool_exit_with_exact_asset_amount_exchanges_correct_values_with_fee() { - ExtBuilder::default().build().execute_with(|| { - ::ExitFee::set(&(BASE / 10)); - frame_system::Pallet::::set_block_number(1); - create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, true); - let asset_before_join = Currencies::free_balance(ASSET_A, &ALICE); - assert_ok!(Swaps::pool_join_with_exact_pool_amount(alice_signed(), 0, ASSET_A, _1, _5)); - let asset_after_join = asset_before_join - Currencies::free_balance(ASSET_A, &ALICE); - let exit_amount = (asset_after_join * 9) / 10; - let amount_left_behind_in_pool = asset_after_join - exit_amount; + // (Sanity check for dust size) + let pool_amount = Currencies::free_balance(Swaps::pool_shares_id(0), &ALICE); + let abs_diff = |x, y| { + if x < y { y - x } else { x - y } + }; + let dust = abs_diff(pool_amount, pool_amount_expected); + assert_le!(dust, 100); + assert_ok!(Swaps::pool_exit_with_exact_asset_amount( alice_signed(), 0, ASSET_A, - exit_amount, - _1 + asset_amount, + bound, )); - let pool_amount = 9_984_935_413; System::assert_last_event( Event::PoolExitWithExactAssetAmount(PoolAssetEvent { asset: ASSET_A, - bound: _1, + bound, cpep: CommonPoolEventParams { pool_id: 0, who: 0 }, - transferred: exit_amount, - pool_amount, + transferred: asset_amount, + pool_amount: pool_amount_expected, }) .into(), ); - assert_eq!(asset_after_join, 40604010000); - let shares_remaining = _1 - pool_amount; // shares_after_join - pool_amount_in assert_all_parameters( - [_25 - amount_left_behind_in_pool, _25, _25, _25], - shares_remaining, - [_100 + amount_left_behind_in_pool, _100, _100, _100], - _100 + shares_remaining, + [_25 - asset_amount_joined + asset_amount, _25, _25, _25], + dust, + [_100 + asset_amount_joined - asset_amount, _100, _100, _100], + _100 + dust, ) }); } @@ -1062,7 +1055,7 @@ fn pool_exit_with_exact_asset_amount_exchanges_correct_values_with_fee() { fn pool_exit_is_not_allowed_with_insufficient_funds() { ExtBuilder::default().build().execute_with(|| { frame_system::Pallet::::set_block_number(1); - create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, true); + create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, Some(0), true); // Alice has no pool shares! assert_noop!( @@ -1083,7 +1076,7 @@ fn pool_exit_is_not_allowed_with_insufficient_funds() { fn pool_join_increases_correct_pool_parameters() { ExtBuilder::default().build().execute_with(|| { frame_system::Pallet::::set_block_number(1); - create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, true); + create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, Some(0), true); assert_ok!(Swaps::pool_join(alice_signed(), 0, _5, vec!(_25, _25, _25, _25),)); System::assert_last_event( @@ -1104,7 +1097,7 @@ fn pool_join_increases_correct_pool_parameters() { fn pool_join_emits_correct_events() { ExtBuilder::default().build().execute_with(|| { frame_system::Pallet::::set_block_number(1); - create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, true); + create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, Some(0), true); assert_ok!(Swaps::pool_join(alice_signed(), 0, _1, vec!(_1, _1, _1, _1),)); System::assert_last_event( Event::PoolJoin(PoolAssetsEvent { @@ -1124,7 +1117,11 @@ fn pool_join_subsidy_reserves_correct_values() { ExtBuilder::default().build().execute_with(|| { // Events cannot be emitted on block zero... frame_system::Pallet::::set_block_number(1); - create_initial_pool_with_funds_for_alice(ScoringRule::RikiddoSigmoidFeeMarketEma, false); + create_initial_pool_with_funds_for_alice( + ScoringRule::RikiddoSigmoidFeeMarketEma, + None, + false, + ); let pool_id = 0; assert_ok!(Swaps::pool_join_subsidy(alice_signed(), pool_id, _20)); let mut reserved = Currencies::reserved_balance(ASSET_D, &ALICE); @@ -1154,7 +1151,11 @@ fn pool_join_subsidy_reserves_correct_values() { #[test] fn pool_join_subsidy_fails_if_amount_is_zero() { ExtBuilder::default().build().execute_with(|| { - create_initial_pool_with_funds_for_alice(ScoringRule::RikiddoSigmoidFeeMarketEma, false); + create_initial_pool_with_funds_for_alice( + ScoringRule::RikiddoSigmoidFeeMarketEma, + None, + false, + ); assert_noop!( Swaps::pool_join_subsidy(alice_signed(), 0, 0), crate::Error::::ZeroAmount @@ -1175,7 +1176,7 @@ fn pool_join_subsidy_fails_if_pool_does_not_exist() { #[test] fn pool_join_subsidy_fails_if_scoring_rule_is_not_rikiddo() { ExtBuilder::default().build().execute_with(|| { - create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, true); + create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, Some(0), true); assert_noop!( Swaps::pool_join_subsidy(alice_signed(), 0, _1), crate::Error::::InvalidScoringRule @@ -1186,7 +1187,11 @@ fn pool_join_subsidy_fails_if_scoring_rule_is_not_rikiddo() { #[test] fn pool_join_subsidy_fails_if_subsidy_is_below_min_per_account() { ExtBuilder::default().build().execute_with(|| { - create_initial_pool_with_funds_for_alice(ScoringRule::RikiddoSigmoidFeeMarketEma, false); + create_initial_pool_with_funds_for_alice( + ScoringRule::RikiddoSigmoidFeeMarketEma, + None, + false, + ); assert_noop!( Swaps::pool_join_subsidy( alice_signed(), @@ -1201,7 +1206,11 @@ fn pool_join_subsidy_fails_if_subsidy_is_below_min_per_account() { #[test] fn pool_join_subsidy_with_small_amount_is_ok_if_account_is_already_a_provider() { ExtBuilder::default().build().execute_with(|| { - create_initial_pool_with_funds_for_alice(ScoringRule::RikiddoSigmoidFeeMarketEma, false); + create_initial_pool_with_funds_for_alice( + ScoringRule::RikiddoSigmoidFeeMarketEma, + None, + false, + ); let pool_id = 0; let large_amount = ::MinSubsidyPerAccount::get(); let small_amount = 1; @@ -1221,7 +1230,11 @@ fn pool_join_subsidy_with_small_amount_is_ok_if_account_is_already_a_provider() fn pool_exit_subsidy_unreserves_remaining_subsidy_if_below_min_per_account() { ExtBuilder::default().build().execute_with(|| { frame_system::Pallet::::set_block_number(1); - create_initial_pool_with_funds_for_alice(ScoringRule::RikiddoSigmoidFeeMarketEma, false); + create_initial_pool_with_funds_for_alice( + ScoringRule::RikiddoSigmoidFeeMarketEma, + None, + false, + ); let pool_id = 0; let large_amount = ::MinSubsidyPerAccount::get(); let small_amount = 1; @@ -1245,71 +1258,77 @@ fn pool_exit_subsidy_unreserves_remaining_subsidy_if_below_min_per_account() { }); } -#[test] -fn pool_join_with_exact_asset_amount_exchanges_correct_values() { +#[test_case(_1, 2_490_679_300, 0; "without_swap_fee")] +#[test_case(_1, 2_304_521_500, _1_10; "with_swap_fee")] +fn pool_join_with_exact_asset_amount_exchanges_correct_values( + asset_amount: BalanceOf, + pool_amount_expected: BalanceOf, + swap_fee: BalanceOf, +) { ExtBuilder::default().build().execute_with(|| { frame_system::Pallet::::set_block_number(1); - create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, true); + create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, Some(swap_fee), true); + let bound = 0; let alice_sent = _1; assert_ok!(Swaps::pool_join_with_exact_asset_amount( alice_signed(), 0, ASSET_A, - alice_sent, - 0 + asset_amount, + bound, )); - let alice_received = Currencies::free_balance(Swaps::pool_shares_id(0), &ALICE); System::assert_last_event( Event::PoolJoinWithExactAssetAmount(PoolAssetEvent { asset: ASSET_A, - bound: 0, + bound, cpep: CommonPoolEventParams { pool_id: 0, who: 0 }, - transferred: alice_sent, - pool_amount: alice_received, + transferred: asset_amount, + pool_amount: pool_amount_expected, }) .into(), ); assert_all_parameters( - [_25 - alice_sent, _25, _25, _25], - alice_received, + [_25 - asset_amount, _25, _25, _25], + pool_amount_expected, [_100 + alice_sent, _100, _100, _100], - _100 + alice_received, + _100 + pool_amount_expected, ); }); } -#[test] -fn pool_join_with_exact_pool_amount_exchanges_correct_values() { +#[test_case(_1, 40_604_010_000, 0; "without_swap_fee")] +#[test_case(_1, 43_896_227_027, _1_10; "with_swap_fee")] +fn pool_join_with_exact_pool_amount_exchanges_correct_values( + pool_amount: BalanceOf, + asset_amount_expected: BalanceOf, + swap_fee: BalanceOf, +) { ExtBuilder::default().build().execute_with(|| { frame_system::Pallet::::set_block_number(1); - create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, true); - let alice_initial = Currencies::free_balance(ASSET_A, &ALICE); - let alice_sent = _1; + create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, Some(swap_fee), true); + let bound = _5; assert_ok!(Swaps::pool_join_with_exact_pool_amount( alice_signed(), 0, ASSET_A, - alice_sent, - _5 + pool_amount, + bound, )); - let asset_amount = alice_initial - Currencies::free_balance(ASSET_A, &ALICE); System::assert_last_event( Event::PoolJoinWithExactPoolAmount(PoolAssetEvent { asset: ASSET_A, - bound: _5, + bound, cpep: CommonPoolEventParams { pool_id: 0, who: 0 }, - transferred: asset_amount, - pool_amount: alice_sent, + transferred: asset_amount_expected, + pool_amount, }) .into(), ); - let alice_received = alice_initial - Currencies::free_balance(ASSET_A, &ALICE); - assert_eq!(alice_received, 40604010000); assert_all_parameters( - [_25 - alice_received, _25, _25, _25], - alice_sent, - [_100 + alice_received, _100, _100, _100], - _100 + alice_sent, + [_25 - asset_amount_expected, _25, _25, _25], + pool_amount, + [_100 + asset_amount_expected, _100, _100, _100], + _100 + pool_amount, ); }); } @@ -1317,7 +1336,7 @@ fn pool_join_with_exact_pool_amount_exchanges_correct_values() { #[test] fn provided_values_len_must_equal_assets_len() { ExtBuilder::default().build().execute_with(|| { - create_initial_pool(ScoringRule::CPMM, true); + create_initial_pool(ScoringRule::CPMM, Some(0), true); assert_noop!( Swaps::pool_join(alice_signed(), 0, _5, vec![]), crate::Error::::ProvidedValuesLenMustEqualAssetsLen @@ -1332,7 +1351,7 @@ fn provided_values_len_must_equal_assets_len() { #[test] fn set_pool_to_stale_leaves_only_correct_assets() { ExtBuilder::default().build().execute_with(|| { - create_initial_pool(ScoringRule::CPMM, true); + create_initial_pool(ScoringRule::CPMM, Some(0), true); let pool_id = 0; assert_noop!( @@ -1362,7 +1381,7 @@ fn set_pool_to_stale_leaves_only_correct_assets() { #[test] fn set_pool_to_stale_handles_rikiddo_pools_properly() { ExtBuilder::default().build().execute_with(|| { - create_initial_pool(ScoringRule::RikiddoSigmoidFeeMarketEma, false); + create_initial_pool(ScoringRule::RikiddoSigmoidFeeMarketEma, None, false); let pool_id = 0; let cat_idx = if let Asset::CategoricalOutcome(_, cidx) = ASSET_A { cidx } else { 0 }; @@ -1403,7 +1422,7 @@ fn swap_exact_amount_in_exchanges_correct_values_with_cpmm( ExtBuilder::default().build().execute_with(|| { // CPMM frame_system::Pallet::::set_block_number(1); - create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, true); + create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, Some(0), true); assert_ok!(Swaps::swap_exact_amount_in( alice_signed(), 0, @@ -1491,7 +1510,7 @@ fn swap_exact_amount_in_exchanges_correct_values_with_cpmm_with_fees() { #[test] fn swap_exact_amount_in_fails_if_min_asset_amount_out_is_not_satisfied_with_cpmm() { ExtBuilder::default().build().execute_with(|| { - create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, true); + create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, Some(0), true); // Expected amount to receive from trading BASE of A for B. See // swap_exact_amount_in_exchanges_correct_values_with_cpmm for details. let expected_amount = 9900990100; @@ -1513,7 +1532,7 @@ fn swap_exact_amount_in_fails_if_min_asset_amount_out_is_not_satisfied_with_cpmm #[test] fn swap_exact_amount_in_fails_if_max_price_is_not_satisfied_with_cpmm() { ExtBuilder::default().build().execute_with(|| { - create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, true); + create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, Some(0), true); // We're swapping 1:1, but due to slippage the price will exceed _1, so this should raise an // error: assert_noop!( @@ -1526,7 +1545,7 @@ fn swap_exact_amount_in_fails_if_max_price_is_not_satisfied_with_cpmm() { #[test] fn swap_exact_amount_in_exchanges_correct_values_with_rikiddo() { ExtBuilder::default().build().execute_with(|| { - create_initial_pool(ScoringRule::RikiddoSigmoidFeeMarketEma, true); + create_initial_pool(ScoringRule::RikiddoSigmoidFeeMarketEma, None, true); let pool_id = 0; // Generate funds, add subsidy and start pool. @@ -1592,7 +1611,7 @@ fn swap_exact_amount_out_exchanges_correct_values_with_cpmm( ) { ExtBuilder::default().build().execute_with(|| { frame_system::Pallet::::set_block_number(1); - create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, true); + create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, Some(0), true); assert_ok!(Swaps::swap_exact_amount_out( alice_signed(), 0, @@ -1679,7 +1698,7 @@ fn swap_exact_amount_out_exchanges_correct_values_with_cpmm_with_fees() { #[test] fn swap_exact_amount_out_fails_if_min_asset_amount_out_is_not_satisfied_with_cpmm() { ExtBuilder::default().build().execute_with(|| { - create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, true); + create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, Some(0), true); // Expected amount of A to swap in for receiving BASE of B. See // swap_exact_amount_out_exchanges_correct_values_with_cpmm for details! let expected_amount = 10101010100; @@ -1701,7 +1720,7 @@ fn swap_exact_amount_out_fails_if_min_asset_amount_out_is_not_satisfied_with_cpm #[test] fn swap_exact_amount_out_fails_if_max_price_is_not_satisfied_with_cpmm() { ExtBuilder::default().build().execute_with(|| { - create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, true); + create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, Some(0), true); // We're swapping 1:1, but due to slippage the price will exceed 1, so this should raise an // error: assert_noop!( @@ -1715,7 +1734,7 @@ fn swap_exact_amount_out_fails_if_max_price_is_not_satisfied_with_cpmm() { fn swap_exact_amount_out_exchanges_correct_values_with_rikiddo() { ExtBuilder::default().build().execute_with(|| { frame_system::Pallet::::set_block_number(1); - create_initial_pool(ScoringRule::RikiddoSigmoidFeeMarketEma, true); + create_initial_pool(ScoringRule::RikiddoSigmoidFeeMarketEma, None, true); let pool_id = 0; // Generate funds, add subsidy and start pool. @@ -1886,7 +1905,7 @@ fn create_pool_fails_if_swap_fee_is_unspecified_for_cpmm() { #[test] fn join_pool_exit_pool_does_not_create_extra_tokens() { ExtBuilder::default().build().execute_with(|| { - create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, true); + create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, Some(0), true); ASSETS.iter().cloned().for_each(|asset| { let _ = Currencies::deposit(asset, &CHARLIE, _100); @@ -2039,7 +2058,7 @@ fn create_pool_transfers_the_correct_amount_of_tokens() { #[test] fn pool_join_fails_if_max_assets_in_is_violated() { ExtBuilder::default().build().execute_with(|| { - create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, true); + create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, Some(0), true); assert_noop!( Swaps::pool_join(alice_signed(), 0, _1, vec!(_1, _1, _1 - 1, _1)), crate::Error::::LimitIn, @@ -2050,7 +2069,7 @@ fn pool_join_fails_if_max_assets_in_is_violated() { #[test] fn pool_join_with_exact_asset_amount_fails_if_min_pool_tokens_is_violated() { ExtBuilder::default().build().execute_with(|| { - create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, true); + create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, Some(0), true); // Expected pool amount when joining with exactly BASE A. let expected_pool_amount = 2490679300; assert_noop!( @@ -2069,7 +2088,7 @@ fn pool_join_with_exact_asset_amount_fails_if_min_pool_tokens_is_violated() { #[test] fn pool_join_with_exact_pool_amount_fails_if_max_asset_amount_is_violated() { ExtBuilder::default().build().execute_with(|| { - create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, true); + create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, Some(0), true); // Expected asset amount required to joining for BASE pool share. let expected_asset_amount = 40604010000; assert_noop!( @@ -2088,7 +2107,7 @@ fn pool_join_with_exact_pool_amount_fails_if_max_asset_amount_is_violated() { #[test] fn pool_exit_fails_if_min_assets_out_is_violated() { ExtBuilder::default().build().execute_with(|| { - create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, true); + create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, Some(0), true); assert_ok!(Swaps::pool_join(alice_signed(), 0, _1, vec!(_1, _1, _1, _1))); assert_noop!( Swaps::pool_exit(alice_signed(), 0, _1, vec!(_1, _1, _1 + 1, _1)), @@ -2101,7 +2120,7 @@ fn pool_exit_fails_if_min_assets_out_is_violated() { fn pool_exit_with_exact_asset_amount_fails_if_min_pool_amount_is_violated() { ExtBuilder::default().build().execute_with(|| { ::ExitFee::set(&(BASE / 10)); - create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, true); + create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, Some(0), true); assert_ok!(Swaps::pool_join_with_exact_asset_amount(alice_signed(), 0, ASSET_A, _5, 0)); let pool_amount = Currencies::free_balance(Swaps::pool_shares_id(0), &ALICE); let expected_amount = 45_082_061_850; @@ -2122,7 +2141,7 @@ fn pool_exit_with_exact_asset_amount_fails_if_min_pool_amount_is_violated() { fn pool_exit_with_exact_pool_amount_fails_if_max_asset_amount_is_violated() { ExtBuilder::default().build().execute_with(|| { ::ExitFee::set(&(BASE / 10)); - create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, true); + create_initial_pool_with_funds_for_alice(ScoringRule::CPMM, Some(0), true); let asset_before_join = Currencies::free_balance(ASSET_A, &ALICE); assert_ok!(Swaps::pool_join_with_exact_pool_amount(alice_signed(), 0, ASSET_A, _1, _5)); let asset_after_join = asset_before_join - Currencies::free_balance(ASSET_A, &ALICE); @@ -2145,7 +2164,11 @@ fn alice_signed() -> Origin { Origin::signed(ALICE) } -fn create_initial_pool(scoring_rule: ScoringRule, deposit: bool) { +fn create_initial_pool( + scoring_rule: ScoringRule, + swap_fee: Option>, + deposit: bool, +) { if deposit { ASSETS.iter().cloned().for_each(|asset| { let _ = Currencies::deposit(asset, &BOB, _100); @@ -2158,7 +2181,7 @@ fn create_initial_pool(scoring_rule: ScoringRule, deposit: bool) { ASSETS.last().unwrap().clone(), 0, scoring_rule, - if scoring_rule == ScoringRule::CPMM { Some(0) } else { None }, + swap_fee, if scoring_rule == ScoringRule::CPMM { Some(::MinLiquidity::get()) } else { @@ -2168,8 +2191,12 @@ fn create_initial_pool(scoring_rule: ScoringRule, deposit: bool) { )); } -fn create_initial_pool_with_funds_for_alice(scoring_rule: ScoringRule, deposit: bool) { - create_initial_pool(scoring_rule, deposit); +fn create_initial_pool_with_funds_for_alice( + scoring_rule: ScoringRule, + swap_fee: Option>, + deposit: bool, +) { + create_initial_pool(scoring_rule, swap_fee, deposit); let _ = Currencies::deposit(ASSET_A, &ALICE, _25); let _ = Currencies::deposit(ASSET_B, &ALICE, _25); let _ = Currencies::deposit(ASSET_C, &ALICE, _25);