Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,149 +13,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::{
assets_balance_on, create_pool_with_wnd_on, foreign_balance_on,
imports::{
asset_hub_westend_runtime::{ExistentialDeposit, Runtime},
*,
},
};
use asset_hub_westend_runtime::{
xcm_config::WestendLocation, Balances, ForeignAssets, PolkadotXcm, RuntimeOrigin,
};
use emulated_integration_tests_common::{accounts::ALICE, xcm_emulator::TestExt};
use frame_support::{
assert_err_ignore_postinfo, assert_ok,
traits::fungible::{Inspect, Mutate},
};
use parachains_common::{AccountId, Balance};
use sp_tracing::capture_test_logs;
use crate::{assets_balance_on, create_pool_with_wnd_on, foreign_balance_on, imports::*};
use emulated_integration_tests_common::xcm_emulator::TestExt;
use frame_support::assert_ok;
use std::convert::Into;
use xcm::latest::{Assets, Error as XcmError, Location, Xcm};

const UNITS: Balance = 1_000_000_000;

#[test]
fn exchange_asset_success() {
test_exchange_asset(true, 500 * UNITS, 665 * UNITS, None);
}

#[test]
fn exchange_asset_insufficient_liquidity() {
let log_capture = capture_test_logs!({
test_exchange_asset(
true,
1_000 * UNITS,
2_000 * UNITS,
Some(InstructionError { index: 1, error: XcmError::NoDeal }),
);
});
assert!(log_capture.contains("NoDeal"));
}

#[test]
fn exchange_asset_insufficient_balance() {
let log_capture = capture_test_logs!({
test_exchange_asset(
true,
5_000 * UNITS,
1_665 * UNITS,
Some(InstructionError { index: 0, error: XcmError::FailedToTransactAsset("") }),
);
});
assert!(log_capture.contains("Funds are unavailable"));
}

#[test]
fn exchange_asset_pool_not_created() {
test_exchange_asset(
false,
500 * UNITS,
665 * UNITS,
Some(InstructionError { index: 1, error: XcmError::NoDeal }),
);
}

fn test_exchange_asset(
create_pool: bool,
give_amount: Balance,
want_amount: Balance,
expected_error: Option<InstructionError>,
) {
let alice: AccountId = Westend::account_id_of(ALICE);
let native_asset_location = WestendLocation::get();
let native_asset_id = AssetId(native_asset_location.clone());
let origin = RuntimeOrigin::signed(alice.clone());
let asset_location = Location::new(1, [Parachain(2001)]);
let asset_id = AssetId(asset_location.clone());

// Setup initial state
AssetHubWestend::execute_with(|| {
assert_ok!(<Balances as Mutate<_>>::mint_into(
&alice,
ExistentialDeposit::get() + (1_000 * UNITS)
));

assert_ok!(ForeignAssets::force_create(
RuntimeOrigin::root(),
asset_location.clone().into(),
alice.clone().into(),
true,
1
));
});

if create_pool {
create_pool_with_wnd_on!(AssetHubWestend, asset_location.clone(), true, alice.clone());
}

// Execute and verify swap
AssetHubWestend::execute_with(|| {
let foreign_balance_before = ForeignAssets::balance(asset_location.clone(), &alice);
let wnd_balance_before = Balances::total_balance(&alice);

let give: Assets = (native_asset_id, give_amount).into();
let want: Assets = (asset_id, want_amount).into();
let xcm = Xcm(vec![
WithdrawAsset(give.clone().into()),
ExchangeAsset { give: give.into(), want: want.into(), maximal: true },
DepositAsset { assets: Wild(All), beneficiary: alice.clone().into() },
]);

let result = PolkadotXcm::execute(origin, bx!(xcm::VersionedXcm::from(xcm)), Weight::MAX);

let foreign_balance_after = ForeignAssets::balance(asset_location, &alice);
let wnd_balance_after = Balances::total_balance(&alice);

if let Some(InstructionError { index, error }) = expected_error {
assert_err_ignore_postinfo!(
result,
pallet_xcm::Error::<Runtime>::LocalExecutionIncompleteWithError {
index,
error: error.into()
}
);
assert_eq!(
foreign_balance_after, foreign_balance_before,
"Foreign balance changed unexpectedly: got {foreign_balance_after}, expected {foreign_balance_before}"
);
assert_eq!(
wnd_balance_after, wnd_balance_before,
"WND balance changed unexpectedly: got {wnd_balance_after}, expected {wnd_balance_before}"
);
} else {
assert_ok!(result);
assert!(
foreign_balance_after >= foreign_balance_before + want_amount,
"Expected foreign balance to increase by at least {want_amount} units, got {foreign_balance_after} from {foreign_balance_before}"
);
assert_eq!(
wnd_balance_after, wnd_balance_before - give_amount,
"Expected WND balance to decrease by {give_amount} units, got {wnd_balance_after} from {wnd_balance_before}"
);
}
});
}
use xcm::latest::{Location, Xcm};

#[test]
fn exchange_asset_from_penpal_via_asset_hub_back_to_penpal() {
Expand Down
115 changes: 113 additions & 2 deletions cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ use asset_hub_westend_runtime::{
};
pub use asset_hub_westend_runtime::{AssetConversion, AssetDeposit, CollatorSelection, System};
use asset_test_utils::{
test_cases_over_bridge::TestBridgingConfig, CollatorSessionKey, CollatorSessionKeys,
ExtBuilder, GovernanceOrigin, SlotDurations,
test_cases::exchange_asset_on_asset_hub_works, test_cases_over_bridge::TestBridgingConfig,
CollatorSessionKey, CollatorSessionKeys, ExtBuilder, GovernanceOrigin, SlotDurations,
};
use assets_common::local_and_foreign_assets::ForeignAssetReserveData;
use codec::{Decode, Encode};
Expand Down Expand Up @@ -66,8 +66,10 @@ use parachains_common::{AccountId, AssetIdForTrustBackedAssets, AuraId, Balance}
use sp_consensus_aura::SlotDuration;
use sp_core::crypto::Ss58Codec;
use sp_runtime::{traits::MaybeEquivalence, Either, MultiAddress};
use sp_tracing::capture_test_logs;
use std::convert::Into;
use testnet_parachains_constants::westend::{consensus::*, currency::UNITS};
use westend_runtime_constants::system_parachain::ASSET_HUB_ID;
use xcm::{
latest::{
prelude::{Assets as XcmAssets, *},
Expand Down Expand Up @@ -1962,3 +1964,112 @@ fn expensive_erc20_runs_out_of_gas() {
.is_err());
});
}

#[test]
fn exchange_asset_success() {
exchange_asset_on_asset_hub_works::<
Runtime,
RuntimeCall,
RuntimeOrigin,
Block,
ForeignAssetsInstance,
>(
collator_session_keys(),
ASSET_HUB_ID,
AccountId::from(ALICE),
WestendLocation::get(),
true,
500 * UNITS,
665 * UNITS,
None,
);
}
Comment on lines +1969 to +1986
Copy link
Contributor

@x3c41a x3c41a Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OOC: I was looking for a pattern name and it turned out that
::<...> syntax in Rust community is called turbofish 😅


#[test]
fn exchange_asset_insufficient_liquidity() {
let log_capture = capture_test_logs!({
exchange_asset_on_asset_hub_works::<
Runtime,
RuntimeCall,
RuntimeOrigin,
Block,
ForeignAssetsInstance,
>(
collator_session_keys(),
ASSET_HUB_ID,
AccountId::from(ALICE),
WestendLocation::get(),
true,
1_000 * UNITS,
2_000 * UNITS,
Some(xcm::v5::InstructionError { index: 1, error: xcm::v5::Error::NoDeal }),
);
});
assert!(log_capture.contains("NoDeal"));
}

#[test]
fn exchange_asset_insufficient_balance() {
let log_capture = capture_test_logs!({
exchange_asset_on_asset_hub_works::<
Runtime,
RuntimeCall,
RuntimeOrigin,
Block,
ForeignAssetsInstance,
>(
collator_session_keys(),
ASSET_HUB_ID,
AccountId::from(ALICE),
WestendLocation::get(),
true,
5_000 * UNITS, // This amount will be greater than initial balance
1_665 * UNITS,
Some(xcm::v5::InstructionError {
index: 0,
error: xcm::v5::Error::FailedToTransactAsset(""),
}),
);
});
assert!(log_capture.contains("Funds are unavailable"));
}

#[test]
fn exchange_asset_pool_not_created() {
exchange_asset_on_asset_hub_works::<
Runtime,
RuntimeCall,
RuntimeOrigin,
Block,
ForeignAssetsInstance,
>(
collator_session_keys(),
ASSET_HUB_ID,
AccountId::from(ALICE),
WestendLocation::get(),
false, // Pool not created
500 * UNITS,
665 * UNITS,
Some(xcm::v5::InstructionError { index: 1, error: xcm::v5::Error::NoDeal }),
);
}

#[test]
fn exchange_asset_from_penpal_via_asset_hub_back_to_penpal() {
exchange_asset_on_asset_hub_works::<
Runtime,
RuntimeCall,
RuntimeOrigin,
Block,
ForeignAssetsInstance,
>(
collator_session_keys(),
ASSET_HUB_ID,
AccountId::from(ALICE),
WestendLocation::get(),
true,
100_000_000_000u128,
1_000_000_000u128,
None,
);
}
Loading
Loading