From 8ba58b475f54908f0074d78d560cc476111686a8 Mon Sep 17 00:00:00 2001
From: muharem <ismailov.m.h@gmail.com>
Date: Thu, 18 Jan 2024 15:24:48 +0800
Subject: [PATCH 1/5] setup take-first-asset trader for rococo and westend

---
 .../assets/asset-hub-rococo/src/tests/send.rs |  75 +++
 .../asset-hub-westend/src/tests/send.rs       |  75 +++
 .../assets/asset-hub-rococo/src/xcm_config.rs |  26 +
 .../assets/asset-hub-rococo/tests/tests.rs    | 447 +++++++++++++++++-
 .../asset-hub-westend/src/xcm_config.rs       |  26 +
 .../assets/asset-hub-westend/tests/tests.rs   | 446 ++++++++++++++++-
 6 files changed, 1067 insertions(+), 28 deletions(-)

diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/send.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/send.rs
index fcf4513859ebb..3c9e76a34e36a 100644
--- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/send.rs
+++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/send.rs
@@ -27,3 +27,78 @@ fn send_transact_as_superuser_from_relay_to_system_para_works() {
 		Some(Weight::from_parts(1_019_445_000, 200_000)),
 	)
 }
+
+/// Parachain should be able to send XCM paying its fee with sufficient asset
+/// in the System Parachain
+#[test]
+fn send_xcm_from_para_to_system_para_paying_fee_with_assets_works() {
+	let para_sovereign_account = AssetHubRococo::sovereign_account_id_of(
+		AssetHubRococo::sibling_location_of(PenpalA::para_id()),
+	);
+
+	// Force create and mint assets for Parachain's sovereign account
+	AssetHubRococo::force_create_and_mint_asset(
+		ASSET_ID,
+		ASSET_MIN_BALANCE,
+		true,
+		para_sovereign_account.clone(),
+		Some(Weight::from_parts(1_019_445_000, 200_000)),
+		ASSET_MIN_BALANCE * 1000000000,
+	);
+
+	// We just need a call that can pass the `SafeCallFilter`
+	// Call values are not relevant
+	let call = AssetHubRococo::force_create_asset_call(
+		ASSET_ID,
+		para_sovereign_account.clone(),
+		true,
+		ASSET_MIN_BALANCE,
+	);
+
+	let origin_kind = OriginKind::SovereignAccount;
+	let fee_amount = ASSET_MIN_BALANCE * 1000000;
+	let native_asset =
+		([PalletInstance(ASSETS_PALLET_ID), GeneralIndex(ASSET_ID.into())], fee_amount).into();
+
+	let root_origin = <PenpalA as Chain>::RuntimeOrigin::root();
+	let system_para_destination = PenpalA::sibling_location_of(AssetHubRococo::para_id()).into();
+	let xcm = xcm_transact_paid_execution(
+		call,
+		origin_kind,
+		native_asset,
+		para_sovereign_account.clone(),
+	);
+
+	PenpalA::execute_with(|| {
+		assert_ok!(<PenpalA as PenpalAPallet>::PolkadotXcm::send(
+			root_origin,
+			bx!(system_para_destination),
+			bx!(xcm),
+		));
+
+		PenpalA::assert_xcm_pallet_sent();
+	});
+
+	AssetHubRococo::execute_with(|| {
+		type RuntimeEvent = <AssetHubRococo as Chain>::RuntimeEvent;
+
+		AssetHubRococo::assert_xcmp_queue_success(Some(Weight::from_parts(
+			15_594_564_000,
+			562_893,
+		)));
+
+		assert_expected_events!(
+			AssetHubRococo,
+			vec![
+				RuntimeEvent::Assets(pallet_assets::Event::Burned { asset_id, owner, balance }) => {
+					asset_id: *asset_id == ASSET_ID,
+					owner: *owner == para_sovereign_account,
+					balance: *balance == fee_amount,
+				},
+				RuntimeEvent::Assets(pallet_assets::Event::Issued { asset_id, .. }) => {
+					asset_id: *asset_id == ASSET_ID,
+				},
+			]
+		);
+	});
+}
diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/send.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/send.rs
index d943bafc379b5..a3cd5c5803eef 100644
--- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/send.rs
+++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/send.rs
@@ -27,3 +27,78 @@ fn send_transact_as_superuser_from_relay_to_system_para_works() {
 		Some(Weight::from_parts(1_019_445_000, 200_000)),
 	)
 }
+
+/// Parachain should be able to send XCM paying its fee with sufficient asset
+/// in the System Parachain
+#[test]
+fn send_xcm_from_para_to_system_para_paying_fee_with_assets_works() {
+	let para_sovereign_account = AssetHubWestend::sovereign_account_id_of(
+		AssetHubWestend::sibling_location_of(PenpalB::para_id()),
+	);
+
+	// Force create and mint assets for Parachain's sovereign account
+	AssetHubWestend::force_create_and_mint_asset(
+		ASSET_ID,
+		ASSET_MIN_BALANCE,
+		true,
+		para_sovereign_account.clone(),
+		Some(Weight::from_parts(1_019_445_000, 200_000)),
+		ASSET_MIN_BALANCE * 1000000000,
+	);
+
+	// We just need a call that can pass the `SafeCallFilter`
+	// Call values are not relevant
+	let call = AssetHubWestend::force_create_asset_call(
+		ASSET_ID,
+		para_sovereign_account.clone(),
+		true,
+		ASSET_MIN_BALANCE,
+	);
+
+	let origin_kind = OriginKind::SovereignAccount;
+	let fee_amount = ASSET_MIN_BALANCE * 1000000;
+	let native_asset =
+		([PalletInstance(ASSETS_PALLET_ID), GeneralIndex(ASSET_ID.into())], fee_amount).into();
+
+	let root_origin = <PenpalB as Chain>::RuntimeOrigin::root();
+	let system_para_destination = PenpalB::sibling_location_of(AssetHubWestend::para_id()).into();
+	let xcm = xcm_transact_paid_execution(
+		call,
+		origin_kind,
+		native_asset,
+		para_sovereign_account.clone(),
+	);
+
+	PenpalB::execute_with(|| {
+		assert_ok!(<PenpalB as PenpalBPallet>::PolkadotXcm::send(
+			root_origin,
+			bx!(system_para_destination),
+			bx!(xcm),
+		));
+
+		PenpalB::assert_xcm_pallet_sent();
+	});
+
+	AssetHubWestend::execute_with(|| {
+		type RuntimeEvent = <AssetHubWestend as Chain>::RuntimeEvent;
+
+		AssetHubWestend::assert_xcmp_queue_success(Some(Weight::from_parts(
+			16_290_336_000,
+			562_893,
+		)));
+
+		assert_expected_events!(
+			AssetHubWestend,
+			vec![
+				RuntimeEvent::Assets(pallet_assets::Event::Burned { asset_id, owner, balance }) => {
+					asset_id: *asset_id == ASSET_ID,
+					owner: *owner == para_sovereign_account,
+					balance: *balance == fee_amount,
+				},
+				RuntimeEvent::Assets(pallet_assets::Event::Issued { asset_id, .. }) => {
+					asset_id: *asset_id == ASSET_ID,
+				},
+			]
+		);
+	});
+}
diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs
index 98b23efc06ec3..b9b9025b69795 100644
--- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs
+++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs
@@ -585,6 +585,32 @@ impl xcm_executor::Config for XcmConfig {
 			ResolveAssetTo<StakingPot, crate::NativeAndAssets>,
 			AccountId,
 		>,
+		// This trader allows to pay with `is_sufficient=true` "Trust Backed" assets from dedicated
+		// `pallet_assets` instance - `Assets`.
+		cumulus_primitives_utility::TakeFirstAssetTrader<
+			AccountId,
+			AssetFeeAsExistentialDepositMultiplierFeeCharger,
+			TrustBackedAssetsConvertedConcreteId,
+			Assets,
+			cumulus_primitives_utility::XcmFeesTo32ByteAccount<
+				FungiblesTransactor,
+				AccountId,
+				XcmAssetFeesReceiver,
+			>,
+		>,
+		// This trader allows to pay with `is_sufficient=true` "Foreign" assets from dedicated
+		// `pallet_assets` instance - `ForeignAssets`.
+		cumulus_primitives_utility::TakeFirstAssetTrader<
+			AccountId,
+			ForeignAssetFeeAsExistentialDepositMultiplierFeeCharger,
+			ForeignAssetsConvertedConcreteId,
+			ForeignAssets,
+			cumulus_primitives_utility::XcmFeesTo32ByteAccount<
+				ForeignFungiblesTransactor,
+				AccountId,
+				XcmAssetFeesReceiver,
+			>,
+		>,
 	);
 	type ResponseHandler = PolkadotXcm;
 	type AssetTrap = PolkadotXcm;
diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs
index 3da1a1e66bd82..d0cab4a34a66d 100644
--- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs
+++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs
@@ -20,27 +20,23 @@
 use asset_hub_rococo_runtime::{
 	xcm_config,
 	xcm_config::{
-		bridging, ForeignCreatorsSovereignAccountOf, LocationToAccountId, TokenLocation,
-		TokenLocationV3,
+		bridging, AssetFeeAsExistentialDepositMultiplierFeeCharger, CheckingAccount,
+		ForeignAssetFeeAsExistentialDepositMultiplierFeeCharger, ForeignCreatorsSovereignAccountOf,
+		LocationToAccountId, TokenLocation, TokenLocationV3, TrustBackedAssetsPalletLocation,
+		TrustBackedAssetsPalletLocationV3, XcmConfig,
 	},
-	AllPalletsWithoutSystem, MetadataDepositBase, MetadataDepositPerByte, RuntimeCall,
-	RuntimeEvent, ToWestendXcmRouterInstance, XcmpQueue,
-};
-pub use asset_hub_rococo_runtime::{
-	xcm_config::{
-		CheckingAccount, TrustBackedAssetsPalletLocation, TrustBackedAssetsPalletLocationV3,
-		XcmConfig,
-	},
-	AssetConversion, AssetDeposit, Assets, Balances, CollatorSelection, ExistentialDeposit,
-	ForeignAssets, ForeignAssetsInstance, ParachainSystem, Runtime, SessionKeys, System,
-	TrustBackedAssetsInstance,
+	AllPalletsWithoutSystem, AssetConversion, AssetDeposit, Assets, Balances, CollatorSelection,
+	ExistentialDeposit, ForeignAssets, ForeignAssetsInstance, MetadataDepositBase,
+	MetadataDepositPerByte, ParachainSystem, Runtime, RuntimeCall, RuntimeEvent, SessionKeys,
+	System, ToWestendXcmRouterInstance, TrustBackedAssetsInstance, XcmpQueue,
 };
 use asset_test_utils::{
 	test_cases_over_bridge::TestBridgingConfig, CollatorSessionKey, CollatorSessionKeys, ExtBuilder,
 };
 use codec::{Decode, Encode};
+use cumulus_primitives_utility::ChargeWeightInFungibles;
 use frame_support::{
-	assert_ok,
+	assert_noop, assert_ok,
 	traits::{
 		fungible::{Inspect, Mutate},
 		fungibles::{
@@ -353,6 +349,429 @@ fn test_buy_and_refund_weight_with_swap_foreign_asset_xcm_trader() {
 		})
 }
 
+#[test]
+fn test_asset_xcm_take_first_trader() {
+	ExtBuilder::<Runtime>::default()
+		.with_collators(vec![AccountId::from(ALICE)])
+		.with_session_keys(vec![(
+			AccountId::from(ALICE),
+			AccountId::from(ALICE),
+			SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) },
+		)])
+		.build()
+		.execute_with(|| {
+			// We need root origin to create a sufficient asset
+			let minimum_asset_balance = 3333333_u128;
+			let local_asset_id = 1;
+			assert_ok!(Assets::force_create(
+				RuntimeHelper::root_origin(),
+				local_asset_id.into(),
+				AccountId::from(ALICE).into(),
+				true,
+				minimum_asset_balance
+			));
+
+			// We first mint enough asset for the account to exist for assets
+			assert_ok!(Assets::mint(
+				RuntimeHelper::origin_of(AccountId::from(ALICE)),
+				local_asset_id.into(),
+				AccountId::from(ALICE).into(),
+				minimum_asset_balance
+			));
+
+			// get asset id as location
+			let asset_location =
+				AssetIdForTrustBackedAssetsConvertLatest::convert_back(&local_asset_id).unwrap();
+
+			// Set Alice as block author, who will receive fees
+			RuntimeHelper::run_to_block(2, AccountId::from(ALICE));
+
+			// We are going to buy 4e9 weight
+			let bought = Weight::from_parts(4_000_000_000u64, 0);
+
+			// Lets calculate amount needed
+			let asset_amount_needed =
+				AssetFeeAsExistentialDepositMultiplierFeeCharger::charge_weight_in_fungibles(
+					local_asset_id,
+					bought,
+				)
+				.expect("failed to compute");
+
+			// Lets pay with: asset_amount_needed + asset_amount_extra
+			let asset_amount_extra = 100_u128;
+			let asset: Asset =
+				(asset_location.clone(), asset_amount_needed + asset_amount_extra).into();
+
+			let mut trader = <XcmConfig as xcm_executor::Config>::Trader::new();
+			let ctx = XcmContext { origin: None, message_id: XcmHash::default(), topic: None };
+
+			// Lets buy_weight and make sure buy_weight does not return an error
+			let unused_assets = trader.buy_weight(bought, asset.into(), &ctx).expect("Expected Ok");
+			// Check whether a correct amount of unused assets is returned
+			assert_ok!(unused_assets.ensure_contains(&(asset_location, asset_amount_extra).into()));
+
+			// Drop trader
+			drop(trader);
+
+			// Make sure author(Alice) has received the amount
+			assert_eq!(
+				Assets::balance(local_asset_id, AccountId::from(ALICE)),
+				minimum_asset_balance + asset_amount_needed
+			);
+
+			// We also need to ensure the total supply increased
+			assert_eq!(
+				Assets::total_supply(local_asset_id),
+				minimum_asset_balance + asset_amount_needed
+			);
+		});
+}
+
+#[test]
+fn test_foreign_asset_xcm_take_first_trader() {
+	ExtBuilder::<Runtime>::default()
+		.with_collators(vec![AccountId::from(ALICE)])
+		.with_session_keys(vec![(
+			AccountId::from(ALICE),
+			AccountId::from(ALICE),
+			SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) },
+		)])
+		.build()
+		.execute_with(|| {
+			// We need root origin to create a sufficient asset
+			let minimum_asset_balance = 3333333_u128;
+			let foreign_location = xcm::v3::Location {
+				parents: 1,
+				interior: (
+					xcm::v3::Junction::Parachain(1234),
+					xcm::v3::Junction::GeneralIndex(12345),
+				)
+					.into(),
+			};
+			assert_ok!(ForeignAssets::force_create(
+				RuntimeHelper::root_origin(),
+				foreign_location.into(),
+				AccountId::from(ALICE).into(),
+				true,
+				minimum_asset_balance
+			));
+
+			// We first mint enough asset for the account to exist for assets
+			assert_ok!(ForeignAssets::mint(
+				RuntimeHelper::origin_of(AccountId::from(ALICE)),
+				foreign_location.into(),
+				AccountId::from(ALICE).into(),
+				minimum_asset_balance
+			));
+
+			let asset_location_v4: Location = foreign_location.try_into().unwrap();
+
+			// Set Alice as block author, who will receive fees
+			RuntimeHelper::run_to_block(2, AccountId::from(ALICE));
+
+			// We are going to buy 4e9 weight
+			let bought = Weight::from_parts(4_000_000_000u64, 0);
+
+			// Lets calculate amount needed
+			let asset_amount_needed =
+			ForeignAssetFeeAsExistentialDepositMultiplierFeeCharger::charge_weight_in_fungibles(
+					foreign_location,
+					bought,
+				)
+				.expect("failed to compute");
+
+			// Lets pay with: asset_amount_needed + asset_amount_extra
+			let asset_amount_extra = 100_u128;
+			let asset: Asset =
+				(asset_location_v4.clone(), asset_amount_needed + asset_amount_extra).into();
+
+			let mut trader = <XcmConfig as xcm_executor::Config>::Trader::new();
+			let ctx = XcmContext { origin: None, message_id: XcmHash::default(), topic: None };
+
+			// Lets buy_weight and make sure buy_weight does not return an error
+			let unused_assets = trader.buy_weight(bought, asset.into(), &ctx).expect("Expected Ok");
+			// Check whether a correct amount of unused assets is returned
+			assert_ok!(
+				unused_assets.ensure_contains(&(asset_location_v4, asset_amount_extra).into())
+			);
+
+			// Drop trader
+			drop(trader);
+
+			// Make sure author(Alice) has received the amount
+			assert_eq!(
+				ForeignAssets::balance(foreign_location, AccountId::from(ALICE)),
+				minimum_asset_balance + asset_amount_needed
+			);
+
+			// We also need to ensure the total supply increased
+			assert_eq!(
+				ForeignAssets::total_supply(foreign_location),
+				minimum_asset_balance + asset_amount_needed
+			);
+		});
+}
+
+#[test]
+fn test_asset_xcm_take_first_trader_with_refund() {
+	ExtBuilder::<Runtime>::default()
+		.with_collators(vec![AccountId::from(ALICE)])
+		.with_session_keys(vec![(
+			AccountId::from(ALICE),
+			AccountId::from(ALICE),
+			SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) },
+		)])
+		.build()
+		.execute_with(|| {
+			// We need root origin to create a sufficient asset
+			// We set existential deposit to be identical to the one for Balances first
+			assert_ok!(Assets::force_create(
+				RuntimeHelper::root_origin(),
+				1.into(),
+				AccountId::from(ALICE).into(),
+				true,
+				ExistentialDeposit::get()
+			));
+
+			// We first mint enough asset for the account to exist for assets
+			assert_ok!(Assets::mint(
+				RuntimeHelper::origin_of(AccountId::from(ALICE)),
+				1.into(),
+				AccountId::from(ALICE).into(),
+				ExistentialDeposit::get()
+			));
+
+			let mut trader = <XcmConfig as xcm_executor::Config>::Trader::new();
+			let ctx = XcmContext { origin: None, message_id: XcmHash::default(), topic: None };
+
+			// Set Alice as block author, who will receive fees
+			RuntimeHelper::run_to_block(2, AccountId::from(ALICE));
+
+			// We are going to buy 4e9 weight
+			let bought = Weight::from_parts(4_000_000_000u64, 0);
+
+			let asset_location =
+				AssetIdForTrustBackedAssetsConvertLatest::convert_back(&1).unwrap();
+
+			// lets calculate amount needed
+			let amount_bought = WeightToFee::weight_to_fee(&bought);
+
+			let asset: Asset = (asset_location.clone(), amount_bought).into();
+
+			// Make sure buy_weight does not return an error
+			assert_ok!(trader.buy_weight(bought, asset.clone().into(), &ctx));
+
+			// Make sure again buy_weight does return an error
+			// This assert relies on the fact, that we use `TakeFirstAssetTrader` in `WeightTrader`
+			// tuple chain, which cannot be called twice
+			assert_noop!(trader.buy_weight(bought, asset.into(), &ctx), XcmError::TooExpensive);
+
+			// We actually use half of the weight
+			let weight_used = bought / 2;
+
+			// Make sure refurnd works.
+			let amount_refunded = WeightToFee::weight_to_fee(&(bought - weight_used));
+
+			assert_eq!(
+				trader.refund_weight(bought - weight_used, &ctx),
+				Some((asset_location, amount_refunded).into())
+			);
+
+			// Drop trader
+			drop(trader);
+
+			// We only should have paid for half of the bought weight
+			let fees_paid = WeightToFee::weight_to_fee(&weight_used);
+
+			assert_eq!(
+				Assets::balance(1, AccountId::from(ALICE)),
+				ExistentialDeposit::get() + fees_paid
+			);
+
+			// We also need to ensure the total supply increased
+			assert_eq!(Assets::total_supply(1), ExistentialDeposit::get() + fees_paid);
+		});
+}
+
+#[test]
+fn test_asset_xcm_take_first_trader_refund_not_possible_since_amount_less_than_ed() {
+	ExtBuilder::<Runtime>::default()
+		.with_collators(vec![AccountId::from(ALICE)])
+		.with_session_keys(vec![(
+			AccountId::from(ALICE),
+			AccountId::from(ALICE),
+			SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) },
+		)])
+		.build()
+		.execute_with(|| {
+			// We need root origin to create a sufficient asset
+			// We set existential deposit to be identical to the one for Balances first
+			assert_ok!(Assets::force_create(
+				RuntimeHelper::root_origin(),
+				1.into(),
+				AccountId::from(ALICE).into(),
+				true,
+				ExistentialDeposit::get()
+			));
+
+			let mut trader = <XcmConfig as xcm_executor::Config>::Trader::new();
+			let ctx = XcmContext { origin: None, message_id: XcmHash::default(), topic: None };
+
+			// Set Alice as block author, who will receive fees
+			RuntimeHelper::run_to_block(2, AccountId::from(ALICE));
+
+			// We are going to buy small amount
+			let bought = Weight::from_parts(500_000_000u64, 0);
+
+			let asset_location =
+				AssetIdForTrustBackedAssetsConvertLatest::convert_back(&1).unwrap();
+
+			let amount_bought = WeightToFee::weight_to_fee(&bought);
+
+			assert!(
+				amount_bought < ExistentialDeposit::get(),
+				"we are testing what happens when the amount does not exceed ED"
+			);
+
+			let asset: Asset = (asset_location, amount_bought).into();
+
+			// Buy weight should return an error
+			assert_noop!(trader.buy_weight(bought, asset.into(), &ctx), XcmError::TooExpensive);
+
+			// not credited since the ED is higher than this value
+			assert_eq!(Assets::balance(1, AccountId::from(ALICE)), 0);
+
+			// We also need to ensure the total supply did not increase
+			assert_eq!(Assets::total_supply(1), 0);
+		});
+}
+
+#[test]
+fn test_that_buying_ed_refund_does_not_refund_for_take_first_trader() {
+	ExtBuilder::<Runtime>::default()
+		.with_collators(vec![AccountId::from(ALICE)])
+		.with_session_keys(vec![(
+			AccountId::from(ALICE),
+			AccountId::from(ALICE),
+			SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) },
+		)])
+		.build()
+		.execute_with(|| {
+			// We need root origin to create a sufficient asset
+			// We set existential deposit to be identical to the one for Balances first
+			assert_ok!(Assets::force_create(
+				RuntimeHelper::root_origin(),
+				1.into(),
+				AccountId::from(ALICE).into(),
+				true,
+				ExistentialDeposit::get()
+			));
+
+			let mut trader = <XcmConfig as xcm_executor::Config>::Trader::new();
+			let ctx = XcmContext { origin: None, message_id: XcmHash::default(), topic: None };
+
+			// Set Alice as block author, who will receive fees
+			RuntimeHelper::run_to_block(2, AccountId::from(ALICE));
+
+			// We are gonna buy ED
+			let bought = Weight::from_parts(ExistentialDeposit::get().try_into().unwrap(), 0);
+
+			let asset_location =
+				AssetIdForTrustBackedAssetsConvertLatest::convert_back(&1).unwrap();
+
+			let amount_bought = WeightToFee::weight_to_fee(&bought);
+
+			assert!(
+				amount_bought < ExistentialDeposit::get(),
+				"we are testing what happens when the amount does not exceed ED"
+			);
+
+			// We know we will have to buy at least ED, so lets make sure first it will
+			// fail with a payment of less than ED
+			let asset: Asset = (asset_location.clone(), amount_bought).into();
+			assert_noop!(trader.buy_weight(bought, asset.into(), &ctx), XcmError::TooExpensive);
+
+			// Now lets buy ED at least
+			let asset: Asset = (asset_location, ExistentialDeposit::get()).into();
+
+			// Buy weight should work
+			assert_ok!(trader.buy_weight(bought, asset.into(), &ctx));
+
+			// Should return None. We have a specific check making sure we dont go below ED for
+			// drop payment
+			assert_eq!(trader.refund_weight(bought, &ctx), None);
+
+			// Drop trader
+			drop(trader);
+
+			// Make sure author(Alice) has received the amount
+			assert_eq!(Assets::balance(1, AccountId::from(ALICE)), ExistentialDeposit::get());
+
+			// We also need to ensure the total supply increased
+			assert_eq!(Assets::total_supply(1), ExistentialDeposit::get());
+		});
+}
+
+#[test]
+fn test_asset_xcm_trader_not_possible_for_non_sufficient_assets() {
+	ExtBuilder::<Runtime>::default()
+		.with_collators(vec![AccountId::from(ALICE)])
+		.with_session_keys(vec![(
+			AccountId::from(ALICE),
+			AccountId::from(ALICE),
+			SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) },
+		)])
+		.build()
+		.execute_with(|| {
+			// Create a non-sufficient asset with specific existential deposit
+			let minimum_asset_balance = 1_000_000_u128;
+			assert_ok!(Assets::force_create(
+				RuntimeHelper::root_origin(),
+				1.into(),
+				AccountId::from(ALICE).into(),
+				false,
+				minimum_asset_balance
+			));
+
+			// We first mint enough asset for the account to exist for assets
+			assert_ok!(Assets::mint(
+				RuntimeHelper::origin_of(AccountId::from(ALICE)),
+				1.into(),
+				AccountId::from(ALICE).into(),
+				minimum_asset_balance
+			));
+
+			let mut trader = <XcmConfig as xcm_executor::Config>::Trader::new();
+			let ctx = XcmContext { origin: None, message_id: XcmHash::default(), topic: None };
+
+			// Set Alice as block author, who will receive fees
+			RuntimeHelper::run_to_block(2, AccountId::from(ALICE));
+
+			// We are going to buy 4e9 weight
+			let bought = Weight::from_parts(4_000_000_000u64, 0);
+
+			// lets calculate amount needed
+			let asset_amount_needed = WeightToFee::weight_to_fee(&bought);
+
+			let asset_location =
+				AssetIdForTrustBackedAssetsConvertLatest::convert_back(&1).unwrap();
+
+			let asset: Asset = (asset_location, asset_amount_needed).into();
+
+			// Make sure again buy_weight does return an error
+			assert_noop!(trader.buy_weight(bought, asset.into(), &ctx), XcmError::TooExpensive);
+
+			// Drop trader
+			drop(trader);
+
+			// Make sure author(Alice) has NOT received the amount
+			assert_eq!(Assets::balance(1, AccountId::from(ALICE)), minimum_asset_balance);
+
+			// We also need to ensure the total supply NOT increased
+			assert_eq!(Assets::total_supply(1), minimum_asset_balance);
+		});
+}
+
 #[test]
 fn test_assets_balances_api_works() {
 	use assets_common::runtime_api::runtime_decl_for_fungibles_api::FungiblesApi;
diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs
index b2498418cc73d..70522eda4b703 100644
--- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs
+++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs
@@ -609,6 +609,32 @@ impl xcm_executor::Config for XcmConfig {
 			ResolveAssetTo<StakingPot, crate::NativeAndAssets>,
 			AccountId,
 		>,
+		// This trader allows to pay with `is_sufficient=true` "Trust Backed" assets from dedicated
+		// `pallet_assets` instance - `Assets`.
+		cumulus_primitives_utility::TakeFirstAssetTrader<
+			AccountId,
+			AssetFeeAsExistentialDepositMultiplierFeeCharger,
+			TrustBackedAssetsConvertedConcreteId,
+			Assets,
+			cumulus_primitives_utility::XcmFeesTo32ByteAccount<
+				FungiblesTransactor,
+				AccountId,
+				XcmAssetFeesReceiver,
+			>,
+		>,
+		// This trader allows to pay with `is_sufficient=true` "Foreign" assets from dedicated
+		// `pallet_assets` instance - `ForeignAssets`.
+		cumulus_primitives_utility::TakeFirstAssetTrader<
+			AccountId,
+			ForeignAssetFeeAsExistentialDepositMultiplierFeeCharger,
+			ForeignAssetsConvertedConcreteId,
+			ForeignAssets,
+			cumulus_primitives_utility::XcmFeesTo32ByteAccount<
+				ForeignFungiblesTransactor,
+				AccountId,
+				XcmAssetFeesReceiver,
+			>,
+		>,
 	);
 	type ResponseHandler = PolkadotXcm;
 	type AssetTrap = PolkadotXcm;
diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs
index 28fc7ba705a4d..3fc9f4a96585b 100644
--- a/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs
+++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs
@@ -20,27 +20,24 @@
 use asset_hub_westend_runtime::{
 	xcm_config,
 	xcm_config::{
-		bridging, ForeignCreatorsSovereignAccountOf, LocationToAccountId, WestendLocation,
-		WestendLocationV3,
+		bridging, AssetFeeAsExistentialDepositMultiplierFeeCharger, CheckingAccount,
+		ForeignAssetFeeAsExistentialDepositMultiplierFeeCharger, ForeignCreatorsSovereignAccountOf,
+		LocationToAccountId, TrustBackedAssetsPalletLocation, TrustBackedAssetsPalletLocationV3,
+		WestendLocation, WestendLocationV3, XcmConfig,
 	},
-	AllPalletsWithoutSystem, MetadataDepositBase, MetadataDepositPerByte, PolkadotXcm, RuntimeCall,
-	RuntimeEvent, RuntimeOrigin, ToRococoXcmRouterInstance, XcmpQueue,
-};
-pub use asset_hub_westend_runtime::{
-	xcm_config::{
-		CheckingAccount, TrustBackedAssetsPalletLocation, TrustBackedAssetsPalletLocationV3,
-		XcmConfig,
-	},
-	AssetConversion, AssetDeposit, Assets, Balances, CollatorSelection, ExistentialDeposit,
-	ForeignAssets, ForeignAssetsInstance, ParachainSystem, Runtime, SessionKeys, System,
-	TrustBackedAssetsInstance,
+	AllPalletsWithoutSystem, Assets, Balances, ExistentialDeposit, ForeignAssets,
+	ForeignAssetsInstance, MetadataDepositBase, MetadataDepositPerByte, ParachainSystem,
+	PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, SessionKeys,
+	ToRococoXcmRouterInstance, TrustBackedAssetsInstance, XcmpQueue,
 };
+pub use asset_hub_westend_runtime::{AssetConversion, AssetDeposit, CollatorSelection, System};
 use asset_test_utils::{
 	test_cases_over_bridge::TestBridgingConfig, CollatorSessionKey, CollatorSessionKeys, ExtBuilder,
 };
 use codec::{Decode, Encode};
+use cumulus_primitives_utility::ChargeWeightInFungibles;
 use frame_support::{
-	assert_ok,
+	assert_noop, assert_ok,
 	traits::{
 		fungible::{Inspect, Mutate},
 		fungibles::{
@@ -353,6 +350,427 @@ fn test_buy_and_refund_weight_with_swap_foreign_asset_xcm_trader() {
 		})
 }
 
+#[test]
+fn test_asset_xcm_take_first_trader() {
+	ExtBuilder::<Runtime>::default()
+		.with_collators(vec![AccountId::from(ALICE)])
+		.with_session_keys(vec![(
+			AccountId::from(ALICE),
+			AccountId::from(ALICE),
+			SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) },
+		)])
+		.build()
+		.execute_with(|| {
+			// We need root origin to create a sufficient asset
+			let minimum_asset_balance = 3333333_u128;
+			let local_asset_id = 1;
+			assert_ok!(Assets::force_create(
+				RuntimeHelper::root_origin(),
+				local_asset_id.into(),
+				AccountId::from(ALICE).into(),
+				true,
+				minimum_asset_balance
+			));
+
+			// We first mint enough asset for the account to exist for assets
+			assert_ok!(Assets::mint(
+				RuntimeHelper::origin_of(AccountId::from(ALICE)),
+				local_asset_id.into(),
+				AccountId::from(ALICE).into(),
+				minimum_asset_balance
+			));
+
+			// get asset id as location
+			let asset_location =
+				AssetIdForTrustBackedAssetsConvertLatest::convert_back(&local_asset_id).unwrap();
+
+			// Set Alice as block author, who will receive fees
+			RuntimeHelper::run_to_block(2, AccountId::from(ALICE));
+
+			// We are going to buy 4e9 weight
+			let bought = Weight::from_parts(4_000_000_000u64, 0);
+
+			// Lets calculate amount needed
+			let asset_amount_needed =
+				AssetFeeAsExistentialDepositMultiplierFeeCharger::charge_weight_in_fungibles(
+					local_asset_id,
+					bought,
+				)
+				.expect("failed to compute");
+
+			// Lets pay with: asset_amount_needed + asset_amount_extra
+			let asset_amount_extra = 100_u128;
+			let asset: Asset =
+				(asset_location.clone(), asset_amount_needed + asset_amount_extra).into();
+
+			let mut trader = <XcmConfig as xcm_executor::Config>::Trader::new();
+			let ctx = XcmContext { origin: None, message_id: XcmHash::default(), topic: None };
+
+			// Lets buy_weight and make sure buy_weight does not return an error
+			let unused_assets = trader.buy_weight(bought, asset.into(), &ctx).expect("Expected Ok");
+			// Check whether a correct amount of unused assets is returned
+			assert_ok!(unused_assets.ensure_contains(&(asset_location, asset_amount_extra).into()));
+
+			// Drop trader
+			drop(trader);
+
+			// Make sure author(Alice) has received the amount
+			assert_eq!(
+				Assets::balance(local_asset_id, AccountId::from(ALICE)),
+				minimum_asset_balance + asset_amount_needed
+			);
+
+			// We also need to ensure the total supply increased
+			assert_eq!(
+				Assets::total_supply(local_asset_id),
+				minimum_asset_balance + asset_amount_needed
+			);
+		});
+}
+
+#[test]
+fn test_foreign_asset_xcm_take_first_trader() {
+	ExtBuilder::<Runtime>::default()
+		.with_collators(vec![AccountId::from(ALICE)])
+		.with_session_keys(vec![(
+			AccountId::from(ALICE),
+			AccountId::from(ALICE),
+			SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) },
+		)])
+		.build()
+		.execute_with(|| {
+			// We need root origin to create a sufficient asset
+			let minimum_asset_balance = 3333333_u128;
+			let foreign_location = xcm::v3::Location {
+				parents: 1,
+				interior: (
+					xcm::v3::Junction::Parachain(1234),
+					xcm::v3::Junction::GeneralIndex(12345),
+				)
+					.into(),
+			};
+			assert_ok!(ForeignAssets::force_create(
+				RuntimeHelper::root_origin(),
+				foreign_location.into(),
+				AccountId::from(ALICE).into(),
+				true,
+				minimum_asset_balance
+			));
+
+			// We first mint enough asset for the account to exist for assets
+			assert_ok!(ForeignAssets::mint(
+				RuntimeHelper::origin_of(AccountId::from(ALICE)),
+				foreign_location.into(),
+				AccountId::from(ALICE).into(),
+				minimum_asset_balance
+			));
+
+			let asset_location_v4: Location = foreign_location.try_into().unwrap();
+
+			// Set Alice as block author, who will receive fees
+			RuntimeHelper::run_to_block(2, AccountId::from(ALICE));
+
+			// We are going to buy 4e9 weight
+			let bought = Weight::from_parts(4_000_000_000u64, 0);
+
+			// Lets calculate amount needed
+			let asset_amount_needed =
+			ForeignAssetFeeAsExistentialDepositMultiplierFeeCharger::charge_weight_in_fungibles(
+					foreign_location,
+					bought,
+				)
+				.expect("failed to compute");
+
+			// Lets pay with: asset_amount_needed + asset_amount_extra
+			let asset_amount_extra = 100_u128;
+			let asset: Asset =
+				(asset_location_v4.clone(), asset_amount_needed + asset_amount_extra).into();
+
+			let mut trader = <XcmConfig as xcm_executor::Config>::Trader::new();
+			let ctx = XcmContext { origin: None, message_id: XcmHash::default(), topic: None };
+
+			// Lets buy_weight and make sure buy_weight does not return an error
+			let unused_assets = trader.buy_weight(bought, asset.into(), &ctx).expect("Expected Ok");
+			// Check whether a correct amount of unused assets is returned
+			assert_ok!(
+				unused_assets.ensure_contains(&(asset_location_v4, asset_amount_extra).into())
+			);
+
+			// Drop trader
+			drop(trader);
+
+			// Make sure author(Alice) has received the amount
+			assert_eq!(
+				ForeignAssets::balance(foreign_location, AccountId::from(ALICE)),
+				minimum_asset_balance + asset_amount_needed
+			);
+
+			// We also need to ensure the total supply increased
+			assert_eq!(
+				ForeignAssets::total_supply(foreign_location),
+				minimum_asset_balance + asset_amount_needed
+			);
+		});
+}
+
+#[test]
+fn test_asset_xcm_take_first_trader_with_refund() {
+	ExtBuilder::<Runtime>::default()
+		.with_collators(vec![AccountId::from(ALICE)])
+		.with_session_keys(vec![(
+			AccountId::from(ALICE),
+			AccountId::from(ALICE),
+			SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) },
+		)])
+		.build()
+		.execute_with(|| {
+			// We need root origin to create a sufficient asset
+			// We set existential deposit to be identical to the one for Balances first
+			assert_ok!(Assets::force_create(
+				RuntimeHelper::root_origin(),
+				1.into(),
+				AccountId::from(ALICE).into(),
+				true,
+				ExistentialDeposit::get()
+			));
+
+			// We first mint enough asset for the account to exist for assets
+			assert_ok!(Assets::mint(
+				RuntimeHelper::origin_of(AccountId::from(ALICE)),
+				1.into(),
+				AccountId::from(ALICE).into(),
+				ExistentialDeposit::get()
+			));
+
+			let mut trader = <XcmConfig as xcm_executor::Config>::Trader::new();
+			let ctx = XcmContext { origin: None, message_id: XcmHash::default(), topic: None };
+
+			// Set Alice as block author, who will receive fees
+			RuntimeHelper::run_to_block(2, AccountId::from(ALICE));
+
+			// We are going to buy 4e9 weight
+			let bought = Weight::from_parts(4_000_000_000u64, 0);
+			let asset_location =
+				AssetIdForTrustBackedAssetsConvertLatest::convert_back(&1).unwrap();
+
+			// lets calculate amount needed
+			let amount_bought = WeightToFee::weight_to_fee(&bought);
+
+			let asset: Asset = (asset_location.clone(), amount_bought).into();
+
+			// Make sure buy_weight does not return an error
+			assert_ok!(trader.buy_weight(bought, asset.clone().into(), &ctx));
+
+			// Make sure again buy_weight does return an error
+			// This assert relies on the fact, that we use `TakeFirstAssetTrader` in `WeightTrader`
+			// tuple chain, which cannot be called twice
+			assert_noop!(trader.buy_weight(bought, asset.into(), &ctx), XcmError::TooExpensive);
+
+			// We actually use half of the weight
+			let weight_used = bought / 2;
+
+			// Make sure refurnd works.
+			let amount_refunded = WeightToFee::weight_to_fee(&(bought - weight_used));
+
+			assert_eq!(
+				trader.refund_weight(bought - weight_used, &ctx),
+				Some((asset_location, amount_refunded).into())
+			);
+
+			// Drop trader
+			drop(trader);
+
+			// We only should have paid for half of the bought weight
+			let fees_paid = WeightToFee::weight_to_fee(&weight_used);
+
+			assert_eq!(
+				Assets::balance(1, AccountId::from(ALICE)),
+				ExistentialDeposit::get() + fees_paid
+			);
+
+			// We also need to ensure the total supply increased
+			assert_eq!(Assets::total_supply(1), ExistentialDeposit::get() + fees_paid);
+		});
+}
+
+#[test]
+fn test_asset_xcm_take_first_trader_refund_not_possible_since_amount_less_than_ed() {
+	ExtBuilder::<Runtime>::default()
+		.with_collators(vec![AccountId::from(ALICE)])
+		.with_session_keys(vec![(
+			AccountId::from(ALICE),
+			AccountId::from(ALICE),
+			SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) },
+		)])
+		.build()
+		.execute_with(|| {
+			// We need root origin to create a sufficient asset
+			// We set existential deposit to be identical to the one for Balances first
+			assert_ok!(Assets::force_create(
+				RuntimeHelper::root_origin(),
+				1.into(),
+				AccountId::from(ALICE).into(),
+				true,
+				ExistentialDeposit::get()
+			));
+
+			let mut trader = <XcmConfig as xcm_executor::Config>::Trader::new();
+			let ctx = XcmContext { origin: None, message_id: XcmHash::default(), topic: None };
+
+			// Set Alice as block author, who will receive fees
+			RuntimeHelper::run_to_block(2, AccountId::from(ALICE));
+
+			// We are going to buy small amount
+			let bought = Weight::from_parts(500_000_000u64, 0);
+
+			let asset_location =
+				AssetIdForTrustBackedAssetsConvertLatest::convert_back(&1).unwrap();
+
+			let amount_bought = WeightToFee::weight_to_fee(&bought);
+
+			assert!(
+				amount_bought < ExistentialDeposit::get(),
+				"we are testing what happens when the amount does not exceed ED"
+			);
+
+			let asset: Asset = (asset_location, amount_bought).into();
+
+			// Buy weight should return an error
+			assert_noop!(trader.buy_weight(bought, asset.into(), &ctx), XcmError::TooExpensive);
+
+			// not credited since the ED is higher than this value
+			assert_eq!(Assets::balance(1, AccountId::from(ALICE)), 0);
+
+			// We also need to ensure the total supply did not increase
+			assert_eq!(Assets::total_supply(1), 0);
+		});
+}
+
+#[test]
+fn test_that_buying_ed_refund_does_not_refund_for_take_first_trader() {
+	ExtBuilder::<Runtime>::default()
+		.with_collators(vec![AccountId::from(ALICE)])
+		.with_session_keys(vec![(
+			AccountId::from(ALICE),
+			AccountId::from(ALICE),
+			SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) },
+		)])
+		.build()
+		.execute_with(|| {
+			// We need root origin to create a sufficient asset
+			// We set existential deposit to be identical to the one for Balances first
+			assert_ok!(Assets::force_create(
+				RuntimeHelper::root_origin(),
+				1.into(),
+				AccountId::from(ALICE).into(),
+				true,
+				ExistentialDeposit::get()
+			));
+
+			let mut trader = <XcmConfig as xcm_executor::Config>::Trader::new();
+			let ctx = XcmContext { origin: None, message_id: XcmHash::default(), topic: None };
+
+			// Set Alice as block author, who will receive fees
+			RuntimeHelper::run_to_block(2, AccountId::from(ALICE));
+
+			let bought = Weight::from_parts(500_000_000u64, 0);
+
+			let asset_location =
+				AssetIdForTrustBackedAssetsConvertLatest::convert_back(&1).unwrap();
+
+			let amount_bought = WeightToFee::weight_to_fee(&bought);
+
+			assert!(
+				amount_bought < ExistentialDeposit::get(),
+				"we are testing what happens when the amount does not exceed ED"
+			);
+
+			// We know we will have to buy at least ED, so lets make sure first it will
+			// fail with a payment of less than ED
+			let asset: Asset = (asset_location.clone(), amount_bought).into();
+			assert_noop!(trader.buy_weight(bought, asset.into(), &ctx), XcmError::TooExpensive);
+
+			// Now lets buy ED at least
+			let asset: Asset = (asset_location.clone(), ExistentialDeposit::get()).into();
+
+			// Buy weight should work
+			assert_ok!(trader.buy_weight(bought, asset.into(), &ctx));
+
+			// Should return None. We have a specific check making sure we dont go below ED for
+			// drop payment
+			assert_eq!(trader.refund_weight(bought, &ctx), None);
+
+			// Drop trader
+			drop(trader);
+
+			// Make sure author(Alice) has received the amount
+			assert_eq!(Assets::balance(1, AccountId::from(ALICE)), ExistentialDeposit::get());
+
+			// We also need to ensure the total supply increased
+			assert_eq!(Assets::total_supply(1), ExistentialDeposit::get());
+		});
+}
+
+#[test]
+fn test_asset_xcm_take_first_trader_not_possible_for_non_sufficient_assets() {
+	ExtBuilder::<Runtime>::default()
+		.with_collators(vec![AccountId::from(ALICE)])
+		.with_session_keys(vec![(
+			AccountId::from(ALICE),
+			AccountId::from(ALICE),
+			SessionKeys { aura: AuraId::from(sp_core::sr25519::Public::from_raw(ALICE)) },
+		)])
+		.build()
+		.execute_with(|| {
+			// Create a non-sufficient asset with specific existential deposit
+			let minimum_asset_balance = 1_000_000_u128;
+			assert_ok!(Assets::force_create(
+				RuntimeHelper::root_origin(),
+				1.into(),
+				AccountId::from(ALICE).into(),
+				false,
+				minimum_asset_balance
+			));
+
+			// We first mint enough asset for the account to exist for assets
+			assert_ok!(Assets::mint(
+				RuntimeHelper::origin_of(AccountId::from(ALICE)),
+				1.into(),
+				AccountId::from(ALICE).into(),
+				minimum_asset_balance
+			));
+
+			let mut trader = <XcmConfig as xcm_executor::Config>::Trader::new();
+			let ctx = XcmContext { origin: None, message_id: XcmHash::default(), topic: None };
+
+			// Set Alice as block author, who will receive fees
+			RuntimeHelper::run_to_block(2, AccountId::from(ALICE));
+
+			// We are going to buy 4e9 weight
+			let bought = Weight::from_parts(4_000_000_000u64, 0);
+
+			// lets calculate amount needed
+			let asset_amount_needed = WeightToFee::weight_to_fee(&bought);
+
+			let asset_location =
+				AssetIdForTrustBackedAssetsConvertLatest::convert_back(&1).unwrap();
+
+			let asset: Asset = (asset_location, asset_amount_needed).into();
+
+			// Make sure again buy_weight does return an error
+			assert_noop!(trader.buy_weight(bought, asset.into(), &ctx), XcmError::TooExpensive);
+
+			// Drop trader
+			drop(trader);
+
+			// Make sure author(Alice) has NOT received the amount
+			assert_eq!(Assets::balance(1, AccountId::from(ALICE)), minimum_asset_balance);
+
+			// We also need to ensure the total supply NOT increased
+			assert_eq!(Assets::total_supply(1), minimum_asset_balance);
+		});
+}
+
 #[test]
 fn test_assets_balances_api_works() {
 	use assets_common::runtime_api::runtime_decl_for_fungibles_api::FungiblesApi;

From d833e19eb9c8f0de0d2b8f71e8b587b18c93290e Mon Sep 17 00:00:00 2001
From: muharem <ismailov.m.h@gmail.com>
Date: Thu, 18 Jan 2024 16:08:55 +0800
Subject: [PATCH 2/5] clippy

---
 .../parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs  | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs
index d0cab4a34a66d..cb7ea34a05727 100644
--- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs
+++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs
@@ -28,7 +28,7 @@ use asset_hub_rococo_runtime::{
 	AllPalletsWithoutSystem, AssetConversion, AssetDeposit, Assets, Balances, CollatorSelection,
 	ExistentialDeposit, ForeignAssets, ForeignAssetsInstance, MetadataDepositBase,
 	MetadataDepositPerByte, ParachainSystem, Runtime, RuntimeCall, RuntimeEvent, SessionKeys,
-	System, ToWestendXcmRouterInstance, TrustBackedAssetsInstance, XcmpQueue,
+	ToWestendXcmRouterInstance, TrustBackedAssetsInstance, XcmpQueue,
 };
 use asset_test_utils::{
 	test_cases_over_bridge::TestBridgingConfig, CollatorSessionKey, CollatorSessionKeys, ExtBuilder,

From 3764bd50824e47176751f518903439b26b306fdb Mon Sep 17 00:00:00 2001
From: muharem <ismailov.m.h@gmail.com>
Date: Thu, 18 Jan 2024 17:00:26 +0800
Subject: [PATCH 3/5] fix test names

---
 .../bridges/bridge-hub-rococo/src/tests/asset_transfers.rs      | 2 +-
 .../bridges/bridge-hub-westend/src/tests/asset_transfers.rs     | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/asset_transfers.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/asset_transfers.rs
index a203de0f8c930..469ced4125614 100644
--- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/asset_transfers.rs
+++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/asset_transfers.rs
@@ -140,7 +140,7 @@ fn send_rocs_from_asset_hub_rococo_to_asset_hub_westend() {
 }
 
 #[test]
-fn send_wnds_from_asset_hub_rococo_to_asset_hub_westend() {
+fn send_rocs_from_asset_hub_rococo_to_asset_hub_westend() {
 	let prefund_amount = 10_000_000_000_000u128;
 	let wnd_at_asset_hub_rococo =
 		v3::Location::new(2, [v3::Junction::GlobalConsensus(v3::NetworkId::Westend)]);
diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/asset_transfers.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/asset_transfers.rs
index c2a9c00890222..433f2ff62d795 100644
--- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/asset_transfers.rs
+++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/asset_transfers.rs
@@ -138,7 +138,7 @@ fn send_wnds_from_asset_hub_westend_to_asset_hub_rococo() {
 }
 
 #[test]
-fn send_rocs_from_asset_hub_westend_to_asset_hub_rococo() {
+fn send_wnds_from_asset_hub_westend_to_asset_hub_rococo() {
 	let prefund_amount = 10_000_000_000_000u128;
 	let roc_at_asset_hub_westend =
 		v3::Location::new(2, [v3::Junction::GlobalConsensus(v3::NetworkId::Rococo)]);

From 0faf35e4c601654530df09d6a8f0c5a9fab2c8fe Mon Sep 17 00:00:00 2001
From: muharem <ismailov.m.h@gmail.com>
Date: Thu, 18 Jan 2024 17:14:20 +0800
Subject: [PATCH 4/5] revert tests rename

---
 .../bridges/bridge-hub-rococo/src/tests/asset_transfers.rs      | 2 +-
 .../bridges/bridge-hub-westend/src/tests/asset_transfers.rs     | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/asset_transfers.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/asset_transfers.rs
index 469ced4125614..a203de0f8c930 100644
--- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/asset_transfers.rs
+++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/asset_transfers.rs
@@ -140,7 +140,7 @@ fn send_rocs_from_asset_hub_rococo_to_asset_hub_westend() {
 }
 
 #[test]
-fn send_rocs_from_asset_hub_rococo_to_asset_hub_westend() {
+fn send_wnds_from_asset_hub_rococo_to_asset_hub_westend() {
 	let prefund_amount = 10_000_000_000_000u128;
 	let wnd_at_asset_hub_rococo =
 		v3::Location::new(2, [v3::Junction::GlobalConsensus(v3::NetworkId::Westend)]);
diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/asset_transfers.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/asset_transfers.rs
index 433f2ff62d795..c2a9c00890222 100644
--- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/asset_transfers.rs
+++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/asset_transfers.rs
@@ -138,7 +138,7 @@ fn send_wnds_from_asset_hub_westend_to_asset_hub_rococo() {
 }
 
 #[test]
-fn send_wnds_from_asset_hub_westend_to_asset_hub_rococo() {
+fn send_rocs_from_asset_hub_westend_to_asset_hub_rococo() {
 	let prefund_amount = 10_000_000_000_000u128;
 	let roc_at_asset_hub_westend =
 		v3::Location::new(2, [v3::Junction::GlobalConsensus(v3::NetworkId::Rococo)]);

From 2026c5f2ccfd8d3a3df3f4866e88a9da6b3bbbfd Mon Sep 17 00:00:00 2001
From: Svyatoslav Nikolsky <svyatonik@gmail.com>
Date: Thu, 18 Jan 2024 15:45:14 +0300
Subject: [PATCH 5/5] increase timeout for transfers

---
 .../tests/0001-asset-transfer-works-rococo-to-westend.zndsl   | 4 ++--
 .../tests/0001-asset-transfer-works-westend-to-rococo.zndsl   | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/bridges/zombienet/tests/0001-asset-transfer-works-rococo-to-westend.zndsl b/bridges/zombienet/tests/0001-asset-transfer-works-rococo-to-westend.zndsl
index a61f1e039f451..fe7dc26b00112 100644
--- a/bridges/zombienet/tests/0001-asset-transfer-works-rococo-to-westend.zndsl
+++ b/bridges/zombienet/tests/0001-asset-transfer-works-rococo-to-westend.zndsl
@@ -14,7 +14,7 @@ bridge-hub-westend-collator1: js-script ../helpers/best-finalized-header-at-brid
 
 # step 4: send WND to //Alice on Rococo AH
 # (that's a required part of a sibling 0001-asset-transfer-works-westend-to-rococo.zndsl test)
-asset-hub-westend-collator1: run ../scripts/invoke-script.sh with "reserve-transfer-assets-from-asset-hub-westend-local" within 60 seconds
+asset-hub-westend-collator1: run ../scripts/invoke-script.sh with "reserve-transfer-assets-from-asset-hub-westend-local" within 120 seconds
 
 # step 5: elsewhere Rococo has sent ROC to //Alice - let's wait for it
 asset-hub-westend-collator1: js-script ../helpers/wrapped-assets-balance.js with "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY,0,Rococo" within 600 seconds
@@ -24,7 +24,7 @@ bridge-hub-westend-collator1: js-script ../helpers/relayer-rewards.js with "5FLS
 bridge-hub-westend-collator1: js-script ../helpers/relayer-rewards.js with "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y,0x00000002,0x6268726F,ThisChain,0" within 300 seconds
 
 # step 7: send wROC back to Alice at Rococo AH
-asset-hub-westend-collator1: run ../scripts/invoke-script.sh with "withdraw-reserve-assets-from-asset-hub-westend-local" within 60 seconds
+asset-hub-westend-collator1: run ../scripts/invoke-script.sh with "withdraw-reserve-assets-from-asset-hub-westend-local" within 120 seconds
 
 # step 8: elsewhere Rococo has sent wWND to //Alice - let's wait for it
 # (we wait until //Alice account increases here - there are no other transactionc that may increase it)
diff --git a/bridges/zombienet/tests/0001-asset-transfer-works-westend-to-rococo.zndsl b/bridges/zombienet/tests/0001-asset-transfer-works-westend-to-rococo.zndsl
index 2da5b7a772a7e..610b4ca7acdc3 100644
--- a/bridges/zombienet/tests/0001-asset-transfer-works-westend-to-rococo.zndsl
+++ b/bridges/zombienet/tests/0001-asset-transfer-works-westend-to-rococo.zndsl
@@ -14,7 +14,7 @@ bridge-hub-rococo-collator1: js-script ../helpers/best-finalized-header-at-bridg
 
 # step 4: send ROC to //Alice on Westend AH
 # (that's a required part of a sibling 0001-asset-transfer-works-rococo-to-westend.zndsl test)
-asset-hub-rococo-collator1: run ../scripts/invoke-script.sh with "reserve-transfer-assets-from-asset-hub-rococo-local" within 60 seconds
+asset-hub-rococo-collator1: run ../scripts/invoke-script.sh with "reserve-transfer-assets-from-asset-hub-rococo-local" within 120 seconds
 
 # step 5: elsewhere Westend has sent WND to //Alice - let's wait for it
 asset-hub-rococo-collator1: js-script ../helpers/wrapped-assets-balance.js with "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY,0,Westend" within 600 seconds
@@ -24,7 +24,7 @@ bridge-hub-rococo-collator1: js-script ../helpers/relayer-rewards.js with "5FLSi
 bridge-hub-rococo-collator1: js-script ../helpers/relayer-rewards.js with "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y,0x00000002,0x62687764,ThisChain,0" within 300 seconds
 
 # step 7: send wWND back to Alice at Westend AH
-asset-hub-rococo-collator1: run ../scripts/invoke-script.sh with "withdraw-reserve-assets-from-asset-hub-rococo-local" within 60 seconds
+asset-hub-rococo-collator1: run ../scripts/invoke-script.sh with "withdraw-reserve-assets-from-asset-hub-rococo-local" within 120 seconds
 
 # step 8: elsewhere Westend has sent wROC to //Alice - let's wait for it
 # (we wait until //Alice account increases here - there are no other transactionc that may increase it)