diff --git a/node/src/chain_spec.rs b/node/src/chain_spec.rs index 7abbdef..23bc901 100644 --- a/node/src/chain_spec.rs +++ b/node/src/chain_spec.rs @@ -110,7 +110,7 @@ pub fn staging_test_net(id: ParaId) -> ChainSpec { fn testnet_genesis( root_key: AccountId, endowed_accounts: Vec, - id: ParaId, + _id: ParaId, ) -> GenesisConfig { GenesisConfig { frame_system: Some(SystemConfig { diff --git a/pallets/pallet-subdex-xcmp/src/mock.rs b/pallets/pallet-subdex-xcmp/src/mock.rs index 38622d6..e030352 100644 --- a/pallets/pallet-subdex-xcmp/src/mock.rs +++ b/pallets/pallet-subdex-xcmp/src/mock.rs @@ -78,7 +78,7 @@ impl Get for FirstParaId { } // Used to get min parachain asset amount, based on its type size, set on node runtime level -const fn get_min_parachain_asset_amount() -> Balance { +pub const fn get_min_parachain_asset_amount() -> Balance { match core::mem::size_of::() { size if size <= 64 => 1000, // cosider 112 instead @@ -88,7 +88,7 @@ const fn get_min_parachain_asset_amount() -> Balance { } // Used to get min main network asset amount, based on its type size, set on node runtime level -const fn get_min_main_network_asset_amount() -> Balance { +pub const fn get_min_main_network_asset_amount() -> Balance { match core::mem::size_of::() { size if size <= 64 => 10_000, // cosider 112 instead @@ -97,7 +97,7 @@ const fn get_min_main_network_asset_amount() -> Balance { } } -#[derive(Clone, Eq, PartialEq)] +#[derive(Clone, Eq, Debug, PartialEq)] pub struct Test; parameter_types! { pub const ExistentialDeposit: Balance = 100; @@ -280,12 +280,19 @@ pub fn with_test_externalities R>(f: F) -> R { ExtBuilder::build().execute_with(func) } -type RawTestEvent = RawEvent, AssetId>; +type SubDexXcmpRawTestEvent = RawEvent, AssetId>; -pub fn get_test_event(raw_event: RawTestEvent) -> TestEvent { +type SubdexRawTestEvent = + pallet_subdex::RawEvent, Balance, Balance, Option>; + +pub fn get_subdex_xcmp_test_event(raw_event: SubDexXcmpRawTestEvent) -> TestEvent { TestEvent::subdex_xcmp(raw_event) } +pub fn get_subdex_test_event(raw_event: SubdexRawTestEvent) -> TestEvent { + TestEvent::pallet_subdex(raw_event) +} + pub fn assert_event_success(tested_event: TestEvent, number_of_events_after_call: usize) { // Ensure runtime events length is equal to expected number of events after call assert_eq!(System::events().len(), number_of_events_after_call); diff --git a/pallets/pallet-subdex-xcmp/src/tests.rs b/pallets/pallet-subdex-xcmp/src/tests.rs index 189e844..a742abc 100644 --- a/pallets/pallet-subdex-xcmp/src/tests.rs +++ b/pallets/pallet-subdex-xcmp/src/tests.rs @@ -1,10 +1,46 @@ mod handle_downward_message; mod handle_xcmp_message; +mod initialize_exchange; +mod invest_liquidity; mod transfer_balance_to_parachain_chain; mod transfer_balance_to_relay_chain; pub use super::*; pub use crate::mock::*; +use pallet_subdex::Exchange; + +// Receive provided amounts for both main network curency and parachain assets through xcmp and use them to initialize exchange +pub fn initialize_simple_exchange( + account_id: AccountId, + main_network_currency_transfer_amount: Balance, + para_asset_id: Option, + para_asset_transfer_amount: Balance, +) { + // Transfer both main network currency and custom parachain assets to dex parachain. + + let asset_id = get_next_asset_id(); + + // Emulate xcmp message + emulate_xcmp_message( + FirstParaId::get(), + account_id.clone(), + para_asset_transfer_amount, + para_asset_id, + ); + + // Emulate downward message + emulate_downward_message(account_id.clone(), main_network_currency_transfer_amount); + + // Initialize new exchange + assert_ok!(initialize_new_exchange( + account_id.clone(), + // previosuly mapped parachain asset representation + Asset::ParachainAsset(asset_id), + para_asset_transfer_amount, + Asset::MainNetworkCurrency, + main_network_currency_transfer_amount + )); +} // Subdex @@ -12,6 +48,35 @@ pub fn asset_balances(account_id: AccountId, asset_id: AssetId) -> Balance { SubDex::asset_balances(account_id, asset_id) } +pub fn dex_exchanges(first_asset: Asset, second_asset: Asset) -> Exchange { + SubDex::exchanges(first_asset, second_asset) +} + +pub fn initialize_new_exchange( + origin: AccountId, + first_asset: Asset, + first_asset_amount: Balance, + second_asset: Asset, + second_asset_amount: Balance, +) -> DispatchResult { + SubDex::initialize_exchange( + Origin::signed(origin), + first_asset, + first_asset_amount, + second_asset, + second_asset_amount, + ) +} + +pub fn emulate_invest_liquidity( + origin: AccountId, + first_asset: Asset, + second_asset: Asset, + shares: Balance, +) -> DispatchResult { + SubDex::invest_liquidity(Origin::signed(origin), first_asset, second_asset, shares) +} + // Subdex Xcmp pub fn asset_id_exists(para_id: ParaId, asset_id: Option) -> bool { diff --git a/pallets/pallet-subdex-xcmp/src/tests/handle_downward_message.rs b/pallets/pallet-subdex-xcmp/src/tests/handle_downward_message.rs index 9885d6d..060ed13 100644 --- a/pallets/pallet-subdex-xcmp/src/tests/handle_downward_message.rs +++ b/pallets/pallet-subdex-xcmp/src/tests/handle_downward_message.rs @@ -23,7 +23,7 @@ fn handle_downward_message() { transfer_amount ); - let transferred_tokens_from_relay_chain_event = get_test_event( + let transferred_tokens_from_relay_chain_event = get_subdex_xcmp_test_event( RawEvent::TransferredTokensFromRelayChain(FirstAccountId::get(), transfer_amount), ); diff --git a/pallets/pallet-subdex-xcmp/src/tests/handle_xcmp_message.rs b/pallets/pallet-subdex-xcmp/src/tests/handle_xcmp_message.rs index 11cc567..103777e 100644 --- a/pallets/pallet-subdex-xcmp/src/tests/handle_xcmp_message.rs +++ b/pallets/pallet-subdex-xcmp/src/tests/handle_xcmp_message.rs @@ -53,7 +53,7 @@ fn handle_xcmp_message() { assert_eq!(next_asset_id + 1, get_next_asset_id()); let transferred_tokens_from_relay_chain_event = - get_test_event(RawEvent::DepositAssetViaXCMP( + get_subdex_xcmp_test_event(RawEvent::DepositAssetViaXCMP( FirstParaId::get(), para_asset_id, FirstAccountId::get(), diff --git a/pallets/pallet-subdex-xcmp/src/tests/initialize_exchange.rs b/pallets/pallet-subdex-xcmp/src/tests/initialize_exchange.rs new file mode 100644 index 0000000..071932f --- /dev/null +++ b/pallets/pallet-subdex-xcmp/src/tests/initialize_exchange.rs @@ -0,0 +1,360 @@ +use super::*; + +#[test] +fn initialize_exchange() { + with_test_externalities(|| { + // Transfer both main network currency and custom parachain assets to dex parachain. + + let main_network_currency_transfer_amount = 10_000; + + let para_asset_transfer_amount = 6_000; + + let para_asset_id = Some(5); + + let asset_id = get_next_asset_id(); + + // Emulate xcmp message + emulate_xcmp_message( + FirstParaId::get(), + FirstAccountId::get(), + para_asset_transfer_amount, + para_asset_id, + ); + + // Emulate downward message + emulate_downward_message(FirstAccountId::get(), main_network_currency_transfer_amount); + + // Runtime tested state before call + + // Events number before tested calls + let number_of_events_before_call = System::events().len(); + + // Initialize new exchange + assert_ok!(initialize_new_exchange( + FirstAccountId::get(), + // previosuly mapped parachain asset representation + Asset::ParachainAsset(asset_id), + para_asset_transfer_amount, + Asset::MainNetworkCurrency, + main_network_currency_transfer_amount + )); + + // Runtime tested state after call + + // Ensure both main network and parachain asset balances were successfully invested + assert_eq!(asset_balances(FirstAccountId::get(), asset_id), 0); + + assert_eq!(Balances::free_balance(FirstAccountId::get()), 0); + + // Ensure exchanges storage updated successfully + let (newly_created_exchange, initial_shares) = Exchange::::initialize_new( + main_network_currency_transfer_amount, + para_asset_transfer_amount, + FirstAccountId::get(), + ) + .unwrap(); + + assert_eq!( + newly_created_exchange, + dex_exchanges(Asset::MainNetworkCurrency, Asset::ParachainAsset(asset_id)) + ); + + let exchange_initialized_event = + get_subdex_test_event(pallet_subdex::RawEvent::Initialized( + FirstAccountId::get(), + Asset::MainNetworkCurrency, + Asset::ParachainAsset(asset_id), + initial_shares, + )); + + // Last event checked + assert_event_success( + exchange_initialized_event, + // additional event emitted when Currency slash() performed + number_of_events_before_call + 2, + ); + }) +} + +#[test] +fn initialize_invalid_exchange() { + with_test_externalities(|| { + // Transfer both main network currency and custom parachain assets to dex parachain. + + let transfer_amount = 10_000; + + let para_asset_id = Some(5); + + // Emulate xcmp message + emulate_xcmp_message( + FirstParaId::get(), + FirstAccountId::get(), + transfer_amount, + para_asset_id, + ); + + // Emulate downward message + emulate_downward_message(FirstAccountId::get(), transfer_amount); + + // Runtime tested state before call + + // Events number before tested calls + let number_of_events_before_call = System::events().len(); + + // Make an attempt to initialize exchange, providing the same first and second assets + let initialize_new_exchange_result = initialize_new_exchange( + FirstAccountId::get(), + Asset::MainNetworkCurrency, + transfer_amount / 2, + Asset::MainNetworkCurrency, + transfer_amount / 2, + ); + + // Failure checked + assert_subdex_failure( + initialize_new_exchange_result, + pallet_subdex::Error::::InvalidExchange, + number_of_events_before_call, + ) + }) +} + +#[test] +fn initialize_exchange_main_network_asset_amount_below_min() { + with_test_externalities(|| { + let main_network_currency_transfer_amount = get_min_main_network_asset_amount() - 1; + + let para_asset_transfer_amount = get_min_parachain_asset_amount(); + + let para_asset_id = Some(5); + + let asset_id = get_next_asset_id(); + + // Emulate xcmp message + emulate_xcmp_message( + FirstParaId::get(), + FirstAccountId::get(), + para_asset_transfer_amount, + para_asset_id, + ); + + // Emulate downward message + emulate_downward_message(FirstAccountId::get(), main_network_currency_transfer_amount); + + // Runtime tested state before call + + // Events number before tested calls + let number_of_events_before_call = System::events().len(); + + // Make an attempt to initialize exchange, providing main network asset amount, which is below minimual for this operation + let initialize_new_exchange_result = initialize_new_exchange( + FirstAccountId::get(), + // previosuly mapped parachain asset representation + Asset::ParachainAsset(asset_id), + para_asset_transfer_amount, + Asset::MainNetworkCurrency, + main_network_currency_transfer_amount, + ); + + // Failure checked + assert_subdex_failure( + initialize_new_exchange_result, + pallet_subdex::Error::::MainNetworkAssetAmountBelowMin, + number_of_events_before_call, + ) + }) +} + +#[test] +fn initialize_exchange_parachain_asset_amount_below_min() { + with_test_externalities(|| { + let main_network_currency_transfer_amount = get_min_main_network_asset_amount(); + + let para_asset_transfer_amount = get_min_parachain_asset_amount() - 1; + + let para_asset_id = Some(5); + + let asset_id = get_next_asset_id(); + + // Emulate xcmp message + emulate_xcmp_message( + FirstParaId::get(), + FirstAccountId::get(), + para_asset_transfer_amount, + para_asset_id, + ); + + // Emulate downward message + emulate_downward_message(FirstAccountId::get(), main_network_currency_transfer_amount); + + // Runtime tested state before call + + // Events number before tested calls + let number_of_events_before_call = System::events().len(); + + // Make an attempt to initialize exchange, providing parachain asset amount, which is below minimual for this operation + let initialize_new_exchange_result = initialize_new_exchange( + FirstAccountId::get(), + // previosuly mapped parachain asset representation + Asset::ParachainAsset(asset_id), + para_asset_transfer_amount, + Asset::MainNetworkCurrency, + main_network_currency_transfer_amount, + ); + + // Failure checked + assert_subdex_failure( + initialize_new_exchange_result, + pallet_subdex::Error::::ParachainAssetAmountBelowMin, + number_of_events_before_call, + ) + }) +} + +#[test] +fn initialize_exchange_already_exists() { + with_test_externalities(|| { + let main_network_currency_transfer_amount = get_min_main_network_asset_amount(); + + let para_asset_transfer_amount = get_min_parachain_asset_amount(); + + let para_asset_id = Some(5); + + let asset_id = get_next_asset_id(); + + // Emulate xcmp message + emulate_xcmp_message( + FirstParaId::get(), + FirstAccountId::get(), + para_asset_transfer_amount, + para_asset_id, + ); + + // Emulate downward message + emulate_downward_message(FirstAccountId::get(), main_network_currency_transfer_amount); + + // Initialize new exchange + assert_ok!(initialize_new_exchange( + FirstAccountId::get(), + // previosuly mapped parachain asset representation + Asset::ParachainAsset(asset_id), + para_asset_transfer_amount, + Asset::MainNetworkCurrency, + main_network_currency_transfer_amount + )); + + // Runtime tested state before call + + // Events number before tested calls + let number_of_events_before_call = System::events().len(); + + // Make an attempt to reinitialize already existing exchange + let initialize_new_exchange_result = initialize_new_exchange( + FirstAccountId::get(), + // previosuly mapped parachain asset representation + Asset::ParachainAsset(asset_id), + para_asset_transfer_amount, + Asset::MainNetworkCurrency, + main_network_currency_transfer_amount, + ); + + // Failure checked + assert_subdex_failure( + initialize_new_exchange_result, + pallet_subdex::Error::::ExchangeAlreadyExists, + number_of_events_before_call, + ) + }) +} + +#[test] +fn initialize_exchange_insufficient_main_network_asset_amount() { + with_test_externalities(|| { + let main_network_currency_transfer_amount = get_min_main_network_asset_amount(); + + let para_asset_transfer_amount = get_min_parachain_asset_amount(); + + let para_asset_id = Some(5); + + let asset_id = get_next_asset_id(); + + // Emulate xcmp message + emulate_xcmp_message( + FirstParaId::get(), + FirstAccountId::get(), + para_asset_transfer_amount, + para_asset_id, + ); + + // Emulate downward message + emulate_downward_message(FirstAccountId::get(), main_network_currency_transfer_amount); + + // Runtime tested state before call + + // Events number before tested calls + let number_of_events_before_call = System::events().len(); + + // Make an attempt to initialize exchange, when origin does not have a sufficient main network asset amount + let initialize_new_exchange_result = initialize_new_exchange( + FirstAccountId::get(), + // previosuly mapped parachain asset representation + Asset::ParachainAsset(asset_id), + para_asset_transfer_amount, + Asset::MainNetworkCurrency, + 2 * main_network_currency_transfer_amount, + ); + + // Failure checked + assert_subdex_failure( + initialize_new_exchange_result, + pallet_subdex::Error::::InsufficientMainNetworkAssetAmount, + number_of_events_before_call, + ) + }) +} + +#[test] +fn initialize_exchange_insufficient_parachain_asset_amount() { + with_test_externalities(|| { + let main_network_currency_transfer_amount = get_min_main_network_asset_amount(); + + let para_asset_transfer_amount = get_min_parachain_asset_amount(); + + let para_asset_id = Some(5); + + let asset_id = get_next_asset_id(); + + // Emulate xcmp message + emulate_xcmp_message( + FirstParaId::get(), + FirstAccountId::get(), + para_asset_transfer_amount, + para_asset_id, + ); + + // Emulate downward message + emulate_downward_message(FirstAccountId::get(), main_network_currency_transfer_amount); + + // Runtime tested state before call + + // Events number before tested calls + let number_of_events_before_call = System::events().len(); + + // Make an attempt to initialize exchange, when origin does not have a sufficient parachain asset amount + let initialize_new_exchange_result = initialize_new_exchange( + FirstAccountId::get(), + // previosuly mapped parachain asset representation + Asset::ParachainAsset(asset_id), + 2 * para_asset_transfer_amount, + Asset::MainNetworkCurrency, + main_network_currency_transfer_amount, + ); + + // Failure checked + assert_subdex_failure( + initialize_new_exchange_result, + pallet_subdex::Error::::InsufficientParachainAssetAmount, + number_of_events_before_call, + ) + }) +} diff --git a/pallets/pallet-subdex-xcmp/src/tests/invest_liquidity.rs b/pallets/pallet-subdex-xcmp/src/tests/invest_liquidity.rs new file mode 100644 index 0000000..aaf00b7 --- /dev/null +++ b/pallets/pallet-subdex-xcmp/src/tests/invest_liquidity.rs @@ -0,0 +1,346 @@ +use super::*; + +#[test] +fn invest_liquidity() { + with_test_externalities(|| { + // Transfer both main network currency and custom parachain assets to dex parachain. + + let main_network_currency_transfer_amount = 10_0000; + + let para_asset_transfer_amount = 6_0000; + + let para_asset_id = Some(5); + + // Receive provided amounts for both main network curency and parachain assets through xcmp and use them to initialize exchange + initialize_simple_exchange( + FirstAccountId::get(), + main_network_currency_transfer_amount, + para_asset_id, + para_asset_transfer_amount, + ); + + // previosuly mapped parachain asset representation + let dex_para_asset_id = get_next_asset_id() - 1; + + // An amount of shares to be own by specific actor + let shares_to_be_own = 100000; + + let exchange = dex_exchanges( + Asset::MainNetworkCurrency, + // previosuly mapped parachain asset representation + Asset::ParachainAsset(get_next_asset_id() - 1), + ); + + // Calculate an amount of both assets, needed to be invested, to own an exact amount of shares. + let (first_asset_cost, second_asset_cost) = + exchange.calculate_costs(shares_to_be_own).unwrap(); + + // Emulate downward message + emulate_downward_message(FirstAccountId::get(), first_asset_cost); + + // Emulate xcmp message + emulate_xcmp_message( + FirstParaId::get(), + FirstAccountId::get(), + second_asset_cost, + para_asset_id, + ); + + // Runtime tested state before call + + // Events number before tested call + let number_of_events_before_call = System::events().len(); + + // Invest liquidity + assert_ok!(emulate_invest_liquidity( + FirstAccountId::get(), + Asset::MainNetworkCurrency, + // previosuly mapped parachain asset representation + Asset::ParachainAsset(dex_para_asset_id), + shares_to_be_own + )); + + // Runtime tested state after call + + // Ensure both main network and parachain asset balances were successfully invested + assert_eq!(asset_balances(FirstAccountId::get(), dex_para_asset_id), 0); + + assert_eq!(Balances::free_balance(FirstAccountId::get()), 0); + + // Ensure exchanges storage updated successfully + let (mut newly_created_exchange, _) = Exchange::::initialize_new( + main_network_currency_transfer_amount, + para_asset_transfer_amount, + FirstAccountId::get(), + ) + .unwrap(); + + let _ = newly_created_exchange.invest( + first_asset_cost, + second_asset_cost, + shares_to_be_own, + &FirstAccountId::get(), + ); + + assert_eq!( + newly_created_exchange, + dex_exchanges( + Asset::MainNetworkCurrency, + Asset::ParachainAsset(get_next_asset_id() - 1) + ) + ); + + let exchange_invested_event = get_subdex_test_event(pallet_subdex::RawEvent::Invested( + FirstAccountId::get(), + Asset::MainNetworkCurrency, + Asset::ParachainAsset(dex_para_asset_id), + shares_to_be_own, + )); + + // Last event checked + assert_event_success( + exchange_invested_event, + // additional event emitted when Currency slash() performed + number_of_events_before_call + 2, + ); + }) +} + +#[test] +fn invest_liquidity_exchange_does_not_exist() { + with_test_externalities(|| { + // Transfer both main network currency and custom parachain assets to dex parachain. + + let main_network_currency_transfer_amount = 10_0000; + + let para_asset_transfer_amount = 6_0000; + + let para_asset_id = Some(5); + + // previosuly mapped parachain asset representation + let dex_para_asset_id = get_next_asset_id() - 1; + + // An amount of shares to be own by specific actor + let shares_to_be_own = 1000; + + // Emulate downward message + emulate_downward_message(FirstAccountId::get(), main_network_currency_transfer_amount); + + // Emulate xcmp message + emulate_xcmp_message( + FirstParaId::get(), + FirstAccountId::get(), + para_asset_transfer_amount, + para_asset_id, + ); + + // Runtime tested state before call + + // Events number before tested call + let number_of_events_before_call = System::events().len(); + + // Making attempt o invest liquidity to non existent exchange + let invest_liquidity_result = emulate_invest_liquidity( + FirstAccountId::get(), + Asset::MainNetworkCurrency, + // previosuly mapped parachain asset representation + Asset::ParachainAsset(dex_para_asset_id), + shares_to_be_own, + ); + + // Failure checked + assert_subdex_failure( + invest_liquidity_result, + pallet_subdex::Error::::ExchangeNotExists, + number_of_events_before_call, + ) + }) +} + +#[test] +fn invest_liquidity_invalid_exchange() { + with_test_externalities(|| { + // Transfer both main network currency and custom parachain assets to dex parachain. + + let main_network_currency_transfer_amount = 10_0000; + + let para_asset_transfer_amount = 6_0000; + + let para_asset_id = Some(5); + + // Receive provided amounts for both main network curency and parachain assets through xcmp and use them to initialize exchange + initialize_simple_exchange( + FirstAccountId::get(), + main_network_currency_transfer_amount, + para_asset_id, + para_asset_transfer_amount, + ); + + // An amount of shares to be own by specific actor + let shares_to_be_own = 100000; + + let exchange = dex_exchanges( + Asset::MainNetworkCurrency, + // previosuly mapped parachain asset representation + Asset::ParachainAsset(get_next_asset_id() - 1), + ); + + // Calculate an amount of both assets, needed to be invested, to own an exact amount of shares. + let (first_asset_cost, second_asset_cost) = + exchange.calculate_costs(shares_to_be_own).unwrap(); + + // Emulate downward message + emulate_downward_message(FirstAccountId::get(), first_asset_cost); + + // Emulate xcmp message + emulate_xcmp_message( + FirstParaId::get(), + FirstAccountId::get(), + second_asset_cost, + para_asset_id, + ); + + // Runtime tested state before call + + // Events number before tested call + let number_of_events_before_call = System::events().len(); + + // Make an attempt to invest liqudity, providing the same first and second assets. + let invest_liquidity_result = emulate_invest_liquidity( + FirstAccountId::get(), + Asset::MainNetworkCurrency, + Asset::MainNetworkCurrency, + shares_to_be_own, + ); + + // Failure checked + assert_subdex_failure( + invest_liquidity_result, + pallet_subdex::Error::::InvalidExchange, + number_of_events_before_call, + ) + }) +} + +#[test] +fn invest_liquidity_insufficient_parachain_currency_amount() { + with_test_externalities(|| { + // Transfer both main network currency and custom parachain assets to dex parachain. + + let main_network_currency_transfer_amount = 10_0000; + + let para_asset_transfer_amount = 6_0000; + + let para_asset_id = Some(5); + + // Receive provided amounts for both main network curency and parachain assets through xcmp and use them to initialize exchange + initialize_simple_exchange( + FirstAccountId::get(), + main_network_currency_transfer_amount, + para_asset_id, + para_asset_transfer_amount, + ); + + // previosuly mapped parachain asset representation + let dex_para_asset_id = get_next_asset_id() - 1; + + // An amount of shares to be own by specific actor + let shares_to_be_own = 100000; + + let exchange = dex_exchanges( + Asset::MainNetworkCurrency, + // previosuly mapped parachain asset representation + Asset::ParachainAsset(dex_para_asset_id), + ); + + // Calculate an amount of both assets, needed to be invested, to own an exact amount of shares. + let (first_asset_cost, _) = exchange.calculate_costs(shares_to_be_own).unwrap(); + + // Emulate downward message + emulate_downward_message(FirstAccountId::get(), first_asset_cost); + + // Runtime tested state before call + + // Events number before tested call + let number_of_events_before_call = System::events().len(); + + // Make an attempt to invest liqudity, when account does not have sufficient parachain asset amount + let invest_liquidity_result = emulate_invest_liquidity( + FirstAccountId::get(), + Asset::MainNetworkCurrency, + Asset::ParachainAsset(dex_para_asset_id), + shares_to_be_own, + ); + + // Failure checked + assert_subdex_failure( + invest_liquidity_result, + pallet_subdex::Error::::InsufficientParachainAssetAmount, + number_of_events_before_call, + ) + }) +} + +#[test] +fn invest_liquidity_insufficient_main_network_currency_amount() { + with_test_externalities(|| { + // Transfer both main network currency and custom parachain assets to dex parachain. + + let main_network_currency_transfer_amount = 10_0000; + + let para_asset_transfer_amount = 6_0000; + + let para_asset_id = Some(5); + + // Receive provided amounts for both main network curency and parachain assets through xcmp and use them to initialize exchange + initialize_simple_exchange( + FirstAccountId::get(), + main_network_currency_transfer_amount, + para_asset_id, + para_asset_transfer_amount, + ); + + // previosuly mapped parachain asset representation + let dex_para_asset_id = get_next_asset_id() - 1; + + // An amount of shares to be own by specific actor + let shares_to_be_own = 100000; + + let exchange = dex_exchanges( + Asset::MainNetworkCurrency, + // previosuly mapped parachain asset representation + Asset::ParachainAsset(dex_para_asset_id), + ); + + // Calculate an amount of both assets, needed to be invested, to own an exact amount of shares. + let (_, second_asset_cost) = exchange.calculate_costs(shares_to_be_own).unwrap(); + + // Emulate xcmp message + emulate_xcmp_message( + FirstParaId::get(), + FirstAccountId::get(), + second_asset_cost, + para_asset_id, + ); + + // Runtime tested state before call + + // Events number before tested call + let number_of_events_before_call = System::events().len(); + + // Make an attempt to invest liqudity, when account does not have sufficient main network currency amount + let invest_liquidity_result = emulate_invest_liquidity( + FirstAccountId::get(), + Asset::MainNetworkCurrency, + Asset::ParachainAsset(dex_para_asset_id), + shares_to_be_own, + ); + + // Failure checked + assert_subdex_failure( + invest_liquidity_result, + pallet_subdex::Error::::InsufficientMainNetworkAssetAmount, + number_of_events_before_call, + ) + }) +} diff --git a/pallets/pallet-subdex-xcmp/src/tests/transfer_balance_to_parachain_chain.rs b/pallets/pallet-subdex-xcmp/src/tests/transfer_balance_to_parachain_chain.rs index abf6850..0aa5e1b 100644 --- a/pallets/pallet-subdex-xcmp/src/tests/transfer_balance_to_parachain_chain.rs +++ b/pallets/pallet-subdex-xcmp/src/tests/transfer_balance_to_parachain_chain.rs @@ -37,7 +37,7 @@ fn transfer_balance_to_parachain_chain() { assert_eq!(asset_balances(FirstAccountId::get(), next_asset_id), 0); let transferred_balance_to_parachain_chain_event = - get_test_event(RawEvent::WithdrawAssetViaXCMP( + get_subdex_xcmp_test_event(RawEvent::WithdrawAssetViaXCMP( FirstParaId::get(), para_asset_id, FirstAccountId::get(), diff --git a/pallets/pallet-subdex-xcmp/src/tests/transfer_balance_to_relay_chain.rs b/pallets/pallet-subdex-xcmp/src/tests/transfer_balance_to_relay_chain.rs index 52fc959..a5c7523 100644 --- a/pallets/pallet-subdex-xcmp/src/tests/transfer_balance_to_relay_chain.rs +++ b/pallets/pallet-subdex-xcmp/src/tests/transfer_balance_to_relay_chain.rs @@ -24,7 +24,7 @@ fn transfer_balance_to_relay_chain() { // Runtime tested state after call assert_eq!(Balances::free_balance(FirstAccountId::get()), 0); - let transferred_balance_to_relay_chain_event = get_test_event( + let transferred_balance_to_relay_chain_event = get_subdex_xcmp_test_event( RawEvent::TransferredTokensToRelayChain(FirstAccountId::get(), transfer_amount), ); diff --git a/pallets/pallet-subdex/src/exchange.rs b/pallets/pallet-subdex/src/exchange.rs index 6669121..eb4f943 100644 --- a/pallets/pallet-subdex/src/exchange.rs +++ b/pallets/pallet-subdex/src/exchange.rs @@ -65,7 +65,6 @@ impl SwapDelta { } impl Exchange { - // Calculate min fee, used to substract from initial shares amount, based on balances type size set fn get_min_fee() -> BalanceOf { match core::mem::size_of::>() { diff --git a/pallets/pallet-subdex/src/lib.rs b/pallets/pallet-subdex/src/lib.rs index 6489764..0b6d9b4 100644 --- a/pallets/pallet-subdex/src/lib.rs +++ b/pallets/pallet-subdex/src/lib.rs @@ -16,7 +16,7 @@ use sp_runtime::traits::{ use sp_std::{collections::btree_map::BTreeMap, fmt::Debug, prelude::*}; mod exchange; -use exchange::Exchange; +pub use exchange::Exchange; #[cfg(feature = "std")] pub use serde::{Deserialize, Serialize}; @@ -131,6 +131,7 @@ decl_event!( // account id, asset in, asset in amount, asset out, asset out amount, treasury fee Exchanged(AccountId, Asset, Balance, Asset, Balance, TreasuryFee), Invested(AccountId, Asset, Asset, Shares), + Initialized(AccountId, Asset, Asset, Shares), Divested(AccountId, Asset, Asset, Shares), } ); @@ -182,6 +183,12 @@ decl_error! { /// Insufficient amount of parachain asset provided InsufficientParachainAssetAmount, + /// Amount of main network currency provided is below minimum + MainNetworkAssetAmountBelowMin, + + /// Amount of parachain asset provided is below minimum + ParachainAssetAmountBelowMin, + // Safe math OverflowOccured, @@ -200,7 +207,8 @@ decl_module! { /// Initialize new exchange pool #[weight = 10_000] pub fn initialize_exchange( - origin, first_asset: Asset, + origin, + first_asset: Asset, first_asset_amount: BalanceOf, second_asset: Asset, second_asset_amount: BalanceOf @@ -238,7 +246,7 @@ decl_module! { Exchanges::::insert(first_asset, second_asset, exchange); - Self::deposit_event(RawEvent::Invested(sender, first_asset, second_asset, initial_shares)); + Self::deposit_event(RawEvent::Initialized(sender, first_asset, second_asset, initial_shares)); Ok(()) } @@ -348,9 +356,6 @@ decl_module! { // Calculate costs for both first and second currencies, needed to get a given amount of shares let (first_asset_cost, second_asset_cost) = exchange.calculate_costs(shares)?; - // Ensure min asset amounts constraint satisfied - Self::ensure_min_asset_amounts(first_asset, first_asset_cost, second_asset, second_asset_cost)?; - // Ensure account has sufficient balances to perform invest operation Self::ensure_sufficient_balances(&sender, first_asset, first_asset_cost, second_asset, second_asset_cost)?; @@ -386,9 +391,6 @@ decl_module! { // Ensure assets are different Self::ensure_valid_exchange(first_asset, second_asset)?; - // Ensure min asset amounts constraint satisfied - Self::ensure_min_asset_amounts(first_asset, min_first_asset_received, second_asset, min_second_asset_received)?; - let (first_asset, second_asset, _) = Self::adjust_assets_order(first_asset, second_asset); // Ensure given exchange already exists @@ -703,12 +705,12 @@ impl Module { ) -> dispatch::DispatchResult { match asset { Asset::MainNetworkCurrency if asset_amount < T::MinMainNetworkAssetAmount::get() => { - Err(Error::::InsufficientMainNetworkAssetAmount.into()) + Err(Error::::MainNetworkAssetAmountBelowMin.into()) } // (room for upgrade - indroduce different parachain asset restrictions, based on decimals/other data) Asset::ParachainAsset(_) if asset_amount < T::MinParachainAssetAmount::get() => { - Err(Error::::InsufficientParachainAssetAmount.into()) + Err(Error::::ParachainAssetAmountBelowMin.into()) } _ => Ok(()), }