git.delta.rocks / unique-network / refs/commits / ceb84596992e

difftreelog

Merge pull request #801 from UniqueNetwork/fix/xcm-qtz-unq-tests

Yaroslav Bolyukin2022-12-22parents: #b9448b5 #3e66406.patch.diff
in: master
Fix xcm

10 files changed

modifiedpallets/foreign-assets/src/lib.rsdiffbeforeafterboth
before · pallets/foreign-assets/src/lib.rs
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617//! # Foreign assets18//!19//! - [`Config`]20//! - [`Call`]21//! - [`Pallet`]22//!23//! ## Overview24//!25//! The foreign assests pallet provides functions for:26//!27//! - Local and foreign assets management. The foreign assets can be updated without runtime upgrade.28//! - Bounds between asset and target collection for cross chain transfer and inner transfers.29//!30//! ## Overview31//!32//! Under construction3334#![cfg_attr(not(feature = "std"), no_std)]35#![allow(clippy::unused_unit)]3637use frame_support::{38	dispatch::DispatchResult,39	ensure,40	pallet_prelude::*,41	traits::{fungible, fungibles, Currency, EnsureOrigin},42	RuntimeDebug,43};44use frame_system::pallet_prelude::*;45use up_data_structs::{CollectionMode};46use pallet_fungible::{Pallet as PalletFungible};47use scale_info::{TypeInfo};48use sp_runtime::{49	traits::{One, Zero},50	ArithmeticError,51};52use sp_std::{boxed::Box, vec::Vec};53use up_data_structs::{CollectionId, TokenId, CreateCollectionData};5455// NOTE:v1::MultiLocation is used in storages, we would need to do migration if upgrade the56// MultiLocation in the future.57use xcm::opaque::latest::{prelude::XcmError, Weight};58use xcm::{v1::MultiLocation, VersionedMultiLocation};59use xcm_executor::{traits::WeightTrader, Assets};6061use pallet_common::erc::CrossAccountId;6263#[cfg(feature = "std")]64use serde::{Deserialize, Serialize};6566// TODO: Move to primitives67// Id of native currency.68// 0 - QTZ\UNQ69// 1 - KSM\DOT70#[derive(71	Clone,72	Copy,73	Eq,74	PartialEq,75	PartialOrd,76	Ord,77	MaxEncodedLen,78	RuntimeDebug,79	Encode,80	Decode,81	TypeInfo,82)]83#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]84pub enum NativeCurrency {85	Here = 0,86	Parent = 1,87}8889#[derive(90	Clone,91	Copy,92	Eq,93	PartialEq,94	PartialOrd,95	Ord,96	MaxEncodedLen,97	RuntimeDebug,98	Encode,99	Decode,100	TypeInfo,101)]102#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]103pub enum AssetIds {104	ForeignAssetId(ForeignAssetId),105	NativeAssetId(NativeCurrency),106}107108pub trait TryAsForeign<T, F> {109	fn try_as_foreign(asset: T) -> Option<F>;110}111112impl TryAsForeign<AssetIds, ForeignAssetId> for AssetIds {113	fn try_as_foreign(asset: AssetIds) -> Option<ForeignAssetId> {114		match asset {115			AssetIds::ForeignAssetId(id) => Some(id),116			_ => None,117		}118	}119}120121pub type ForeignAssetId = u32;122pub type CurrencyId = AssetIds;123124mod impl_fungibles;125pub mod weights;126127#[cfg(feature = "runtime-benchmarks")]128mod benchmarking;129130pub use module::*;131pub use weights::WeightInfo;132133/// Type alias for currency balance.134pub type BalanceOf<T> =135	<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;136137/// A mapping between ForeignAssetId and AssetMetadata.138pub trait AssetIdMapping<ForeignAssetId, MultiLocation, AssetMetadata> {139	/// Returns the AssetMetadata associated with a given ForeignAssetId.140	fn get_asset_metadata(foreign_asset_id: ForeignAssetId) -> Option<AssetMetadata>;141	/// Returns the MultiLocation associated with a given ForeignAssetId.142	fn get_multi_location(foreign_asset_id: ForeignAssetId) -> Option<MultiLocation>;143	/// Returns the CurrencyId associated with a given MultiLocation.144	fn get_currency_id(multi_location: MultiLocation) -> Option<CurrencyId>;145}146147pub struct XcmForeignAssetIdMapping<T>(sp_std::marker::PhantomData<T>);148149impl<T: Config> AssetIdMapping<ForeignAssetId, MultiLocation, AssetMetadata<BalanceOf<T>>>150	for XcmForeignAssetIdMapping<T>151{152	fn get_asset_metadata(foreign_asset_id: ForeignAssetId) -> Option<AssetMetadata<BalanceOf<T>>> {153		log::trace!(target: "fassets::asset_metadatas", "call");154		Pallet::<T>::asset_metadatas(AssetIds::ForeignAssetId(foreign_asset_id))155	}156157	fn get_multi_location(foreign_asset_id: ForeignAssetId) -> Option<MultiLocation> {158		log::trace!(target: "fassets::get_multi_location", "call");159		Pallet::<T>::foreign_asset_locations(foreign_asset_id)160	}161162	fn get_currency_id(multi_location: MultiLocation) -> Option<CurrencyId> {163		log::trace!(target: "fassets::get_currency_id", "call");164		Some(AssetIds::ForeignAssetId(165			Pallet::<T>::location_to_currency_ids(multi_location).unwrap_or(0),166		))167	}168}169170#[frame_support::pallet]171pub mod module {172	use super::*;173174	#[pallet::config]175	pub trait Config:176		frame_system::Config177		+ pallet_common::Config178		+ pallet_fungible::Config179		+ orml_tokens::Config180		+ pallet_balances::Config181	{182		/// The overarching event type.183		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;184185		/// Currency type for withdraw and balance storage.186		type Currency: Currency<Self::AccountId>;187188		/// Required origin for registering asset.189		type RegisterOrigin: EnsureOrigin<Self::RuntimeOrigin>;190191		/// Weight information for the extrinsics in this module.192		type WeightInfo: WeightInfo;193	}194195	#[derive(Clone, Eq, PartialEq, RuntimeDebug, Encode, Decode, TypeInfo)]196	pub struct AssetMetadata<Balance> {197		pub name: Vec<u8>,198		pub symbol: Vec<u8>,199		pub decimals: u8,200		pub minimal_balance: Balance,201	}202203	#[pallet::error]204	pub enum Error<T> {205		/// The given location could not be used (e.g. because it cannot be expressed in the206		/// desired version of XCM).207		BadLocation,208		/// MultiLocation existed209		MultiLocationExisted,210		/// AssetId not exists211		AssetIdNotExists,212		/// AssetId exists213		AssetIdExisted,214	}215216	#[pallet::event]217	#[pallet::generate_deposit(fn deposit_event)]218	pub enum Event<T: Config> {219		/// The foreign asset registered.220		ForeignAssetRegistered {221			asset_id: ForeignAssetId,222			asset_address: MultiLocation,223			metadata: AssetMetadata<BalanceOf<T>>,224		},225		/// The foreign asset updated.226		ForeignAssetUpdated {227			asset_id: ForeignAssetId,228			asset_address: MultiLocation,229			metadata: AssetMetadata<BalanceOf<T>>,230		},231		/// The asset registered.232		AssetRegistered {233			asset_id: AssetIds,234			metadata: AssetMetadata<BalanceOf<T>>,235		},236		/// The asset updated.237		AssetUpdated {238			asset_id: AssetIds,239			metadata: AssetMetadata<BalanceOf<T>>,240		},241	}242243	/// Next available Foreign AssetId ID.244	///245	/// NextForeignAssetId: ForeignAssetId246	#[pallet::storage]247	#[pallet::getter(fn next_foreign_asset_id)]248	pub type NextForeignAssetId<T: Config> = StorageValue<_, ForeignAssetId, ValueQuery>;249	/// The storages for MultiLocations.250	///251	/// ForeignAssetLocations: map ForeignAssetId => Option<MultiLocation>252	#[pallet::storage]253	#[pallet::getter(fn foreign_asset_locations)]254	pub type ForeignAssetLocations<T: Config> =255		StorageMap<_, Twox64Concat, ForeignAssetId, MultiLocation, OptionQuery>;256257	/// The storages for CurrencyIds.258	///259	/// LocationToCurrencyIds: map MultiLocation => Option<ForeignAssetId>260	#[pallet::storage]261	#[pallet::getter(fn location_to_currency_ids)]262	pub type LocationToCurrencyIds<T: Config> =263		StorageMap<_, Twox64Concat, MultiLocation, ForeignAssetId, OptionQuery>;264265	/// The storages for AssetMetadatas.266	///267	/// AssetMetadatas: map AssetIds => Option<AssetMetadata>268	#[pallet::storage]269	#[pallet::getter(fn asset_metadatas)]270	pub type AssetMetadatas<T: Config> =271		StorageMap<_, Twox64Concat, AssetIds, AssetMetadata<BalanceOf<T>>, OptionQuery>;272273	/// The storages for assets to fungible collection binding274	///275	#[pallet::storage]276	#[pallet::getter(fn asset_binding)]277	pub type AssetBinding<T: Config> =278		StorageMap<_, Twox64Concat, ForeignAssetId, CollectionId, OptionQuery>;279280	#[pallet::pallet]281	#[pallet::without_storage_info]282	pub struct Pallet<T>(_);283284	#[pallet::call]285	impl<T: Config> Pallet<T> {286		#[pallet::call_index(0)]287		#[pallet::weight(<T as Config>::WeightInfo::register_foreign_asset())]288		pub fn register_foreign_asset(289			origin: OriginFor<T>,290			owner: T::AccountId,291			location: Box<VersionedMultiLocation>,292			metadata: Box<AssetMetadata<BalanceOf<T>>>,293		) -> DispatchResult {294			T::RegisterOrigin::ensure_origin(origin.clone())?;295296			let location: MultiLocation = (*location)297				.try_into()298				.map_err(|()| Error::<T>::BadLocation)?;299300			let md = metadata.clone();301			let name: Vec<u16> = md.name.into_iter().map(|x| x as u16).collect::<Vec<u16>>();302			let mut description: Vec<u16> = "Foreign assets collection for "303				.encode_utf16()304				.collect::<Vec<u16>>();305			description.append(&mut name.clone());306307			let data: CreateCollectionData<T::AccountId> = CreateCollectionData {308				name: name.try_into().unwrap(),309				description: description.try_into().unwrap(),310				mode: CollectionMode::Fungible(md.decimals),311				..Default::default()312			};313			let owner = T::CrossAccountId::from_sub(owner);314			let bounded_collection_id =315				<PalletFungible<T>>::init_foreign_collection(owner.clone(), owner, data)?;316			let foreign_asset_id =317				Self::do_register_foreign_asset(&location, &metadata, bounded_collection_id)?;318319			Self::deposit_event(Event::<T>::ForeignAssetRegistered {320				asset_id: foreign_asset_id,321				asset_address: location,322				metadata: *metadata,323			});324			Ok(())325		}326327		#[pallet::call_index(1)]328		#[pallet::weight(<T as Config>::WeightInfo::update_foreign_asset())]329		pub fn update_foreign_asset(330			origin: OriginFor<T>,331			foreign_asset_id: ForeignAssetId,332			location: Box<VersionedMultiLocation>,333			metadata: Box<AssetMetadata<BalanceOf<T>>>,334		) -> DispatchResult {335			T::RegisterOrigin::ensure_origin(origin)?;336337			let location: MultiLocation = (*location)338				.try_into()339				.map_err(|()| Error::<T>::BadLocation)?;340			Self::do_update_foreign_asset(foreign_asset_id, &location, &metadata)?;341342			Self::deposit_event(Event::<T>::ForeignAssetUpdated {343				asset_id: foreign_asset_id,344				asset_address: location,345				metadata: *metadata,346			});347			Ok(())348		}349	}350}351352impl<T: Config> Pallet<T> {353	fn get_next_foreign_asset_id() -> Result<ForeignAssetId, DispatchError> {354		NextForeignAssetId::<T>::try_mutate(|current| -> Result<ForeignAssetId, DispatchError> {355			let id = *current;356			*current = current357				.checked_add(One::one())358				.ok_or(ArithmeticError::Overflow)?;359			Ok(id)360		})361	}362363	fn do_register_foreign_asset(364		location: &MultiLocation,365		metadata: &AssetMetadata<BalanceOf<T>>,366		bounded_collection_id: CollectionId,367	) -> Result<ForeignAssetId, DispatchError> {368		let foreign_asset_id = Self::get_next_foreign_asset_id()?;369		LocationToCurrencyIds::<T>::try_mutate(location, |maybe_currency_ids| -> DispatchResult {370			ensure!(371				maybe_currency_ids.is_none(),372				Error::<T>::MultiLocationExisted373			);374			*maybe_currency_ids = Some(foreign_asset_id);375			// *maybe_currency_ids = Some(CurrencyId::ForeignAsset(foreign_asset_id));376377			ForeignAssetLocations::<T>::try_mutate(378				foreign_asset_id,379				|maybe_location| -> DispatchResult {380					ensure!(maybe_location.is_none(), Error::<T>::MultiLocationExisted);381					*maybe_location = Some(location.clone());382383					AssetMetadatas::<T>::try_mutate(384						AssetIds::ForeignAssetId(foreign_asset_id),385						|maybe_asset_metadatas| -> DispatchResult {386							ensure!(maybe_asset_metadatas.is_none(), Error::<T>::AssetIdExisted);387							*maybe_asset_metadatas = Some(metadata.clone());388							Ok(())389						},390					)391				},392			)?;393394			AssetBinding::<T>::try_mutate(foreign_asset_id, |collection_id| -> DispatchResult {395				*collection_id = Some(bounded_collection_id);396				Ok(())397			})398		})?;399400		Ok(foreign_asset_id)401	}402403	fn do_update_foreign_asset(404		foreign_asset_id: ForeignAssetId,405		location: &MultiLocation,406		metadata: &AssetMetadata<BalanceOf<T>>,407	) -> DispatchResult {408		ForeignAssetLocations::<T>::try_mutate(409			foreign_asset_id,410			|maybe_multi_locations| -> DispatchResult {411				let old_multi_locations = maybe_multi_locations412					.as_mut()413					.ok_or(Error::<T>::AssetIdNotExists)?;414415				AssetMetadatas::<T>::try_mutate(416					AssetIds::ForeignAssetId(foreign_asset_id),417					|maybe_asset_metadatas| -> DispatchResult {418						ensure!(419							maybe_asset_metadatas.is_some(),420							Error::<T>::AssetIdNotExists421						);422423						// modify location424						if location != old_multi_locations {425							LocationToCurrencyIds::<T>::remove(old_multi_locations.clone());426							LocationToCurrencyIds::<T>::try_mutate(427								location,428								|maybe_currency_ids| -> DispatchResult {429									ensure!(430										maybe_currency_ids.is_none(),431										Error::<T>::MultiLocationExisted432									);433									// *maybe_currency_ids = Some(CurrencyId::ForeignAsset(foreign_asset_id));434									*maybe_currency_ids = Some(foreign_asset_id);435									Ok(())436								},437							)?;438						}439						*maybe_asset_metadatas = Some(metadata.clone());440						*old_multi_locations = location.clone();441						Ok(())442					},443				)444			},445		)446	}447}448449pub use frame_support::{450	traits::{451		fungibles::{Balanced, CreditOf},452		tokens::currency::Currency as CurrencyT,453		OnUnbalanced as OnUnbalancedT,454	},455	weights::{WeightToFeePolynomial, WeightToFee},456};457458pub struct FreeForAll<459	WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,460	AssetId: Get<MultiLocation>,461	AccountId,462	Currency: CurrencyT<AccountId>,463	OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,464>(465	Weight,466	Currency::Balance,467	PhantomData<(WeightToFee, AssetId, AccountId, Currency, OnUnbalanced)>,468);469470impl<471		WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,472		AssetId: Get<MultiLocation>,473		AccountId,474		Currency: CurrencyT<AccountId>,475		OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,476	> WeightTrader for FreeForAll<WeightToFee, AssetId, AccountId, Currency, OnUnbalanced>477{478	fn new() -> Self {479		Self(0, Zero::zero(), PhantomData)480	}481482	fn buy_weight(&mut self, weight: Weight, payment: Assets) -> Result<Assets, XcmError> {483		log::trace!(target: "fassets::weight", "buy_weight weight: {:?}, payment: {:?}", weight, payment);484		Ok(payment)485	}486}487impl<WeightToFee, AssetId, AccountId, Currency, OnUnbalanced> Drop488	for FreeForAll<WeightToFee, AssetId, AccountId, Currency, OnUnbalanced>489where490	WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,491	AssetId: Get<MultiLocation>,492	Currency: CurrencyT<AccountId>,493	OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,494{495	fn drop(&mut self) {496		OnUnbalanced::on_unbalanced(Currency::issue(self.1));497	}498}
after · pallets/foreign-assets/src/lib.rs
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617//! # Foreign assets18//!19//! - [`Config`]20//! - [`Call`]21//! - [`Pallet`]22//!23//! ## Overview24//!25//! The foreign assests pallet provides functions for:26//!27//! - Local and foreign assets management. The foreign assets can be updated without runtime upgrade.28//! - Bounds between asset and target collection for cross chain transfer and inner transfers.29//!30//! ## Overview31//!32//! Under construction3334#![cfg_attr(not(feature = "std"), no_std)]35#![allow(clippy::unused_unit)]3637use frame_support::{38	dispatch::DispatchResult,39	ensure,40	pallet_prelude::*,41	traits::{fungible, fungibles, Currency, EnsureOrigin},42	RuntimeDebug,43};44use frame_system::pallet_prelude::*;45use up_data_structs::{CollectionMode};46use pallet_fungible::{Pallet as PalletFungible};47use scale_info::{TypeInfo};48use sp_runtime::{49	traits::{One, Zero},50	ArithmeticError,51};52use sp_std::{boxed::Box, vec::Vec};53use up_data_structs::{CollectionId, TokenId, CreateCollectionData};5455// NOTE:v1::MultiLocation is used in storages, we would need to do migration if upgrade the56// MultiLocation in the future.57use xcm::opaque::latest::{prelude::XcmError, Weight};58use xcm::{v1::MultiLocation, VersionedMultiLocation};59use xcm_executor::{traits::WeightTrader, Assets};6061use pallet_common::erc::CrossAccountId;6263#[cfg(feature = "std")]64use serde::{Deserialize, Serialize};6566// TODO: Move to primitives67// Id of native currency.68// 0 - QTZ\UNQ69// 1 - KSM\DOT70#[derive(71	Clone,72	Copy,73	Eq,74	PartialEq,75	PartialOrd,76	Ord,77	MaxEncodedLen,78	RuntimeDebug,79	Encode,80	Decode,81	TypeInfo,82)]83#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]84pub enum NativeCurrency {85	Here = 0,86	Parent = 1,87}8889#[derive(90	Clone,91	Copy,92	Eq,93	PartialEq,94	PartialOrd,95	Ord,96	MaxEncodedLen,97	RuntimeDebug,98	Encode,99	Decode,100	TypeInfo,101)]102#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]103pub enum AssetIds {104	ForeignAssetId(ForeignAssetId),105	NativeAssetId(NativeCurrency),106}107108pub trait TryAsForeign<T, F> {109	fn try_as_foreign(asset: T) -> Option<F>;110}111112impl TryAsForeign<AssetIds, ForeignAssetId> for AssetIds {113	fn try_as_foreign(asset: AssetIds) -> Option<ForeignAssetId> {114		match asset {115			AssetIds::ForeignAssetId(id) => Some(id),116			_ => None,117		}118	}119}120121pub type ForeignAssetId = u32;122pub type CurrencyId = AssetIds;123124mod impl_fungibles;125pub mod weights;126127#[cfg(feature = "runtime-benchmarks")]128mod benchmarking;129130pub use module::*;131pub use weights::WeightInfo;132133/// Type alias for currency balance.134pub type BalanceOf<T> =135	<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;136137/// A mapping between ForeignAssetId and AssetMetadata.138pub trait AssetIdMapping<ForeignAssetId, MultiLocation, AssetMetadata> {139	/// Returns the AssetMetadata associated with a given ForeignAssetId.140	fn get_asset_metadata(foreign_asset_id: ForeignAssetId) -> Option<AssetMetadata>;141	/// Returns the MultiLocation associated with a given ForeignAssetId.142	fn get_multi_location(foreign_asset_id: ForeignAssetId) -> Option<MultiLocation>;143	/// Returns the CurrencyId associated with a given MultiLocation.144	fn get_currency_id(multi_location: MultiLocation) -> Option<CurrencyId>;145}146147pub struct XcmForeignAssetIdMapping<T>(sp_std::marker::PhantomData<T>);148149impl<T: Config> AssetIdMapping<ForeignAssetId, MultiLocation, AssetMetadata<BalanceOf<T>>>150	for XcmForeignAssetIdMapping<T>151{152	fn get_asset_metadata(foreign_asset_id: ForeignAssetId) -> Option<AssetMetadata<BalanceOf<T>>> {153		log::trace!(target: "fassets::asset_metadatas", "call");154		Pallet::<T>::asset_metadatas(AssetIds::ForeignAssetId(foreign_asset_id))155	}156157	fn get_multi_location(foreign_asset_id: ForeignAssetId) -> Option<MultiLocation> {158		log::trace!(target: "fassets::get_multi_location", "call");159		Pallet::<T>::foreign_asset_locations(foreign_asset_id)160	}161162	fn get_currency_id(multi_location: MultiLocation) -> Option<CurrencyId> {163		log::trace!(target: "fassets::get_currency_id", "call");164		Pallet::<T>::location_to_currency_ids(multi_location).map(|id| AssetIds::ForeignAssetId(id))165	}166}167168#[frame_support::pallet]169pub mod module {170	use super::*;171172	#[pallet::config]173	pub trait Config:174		frame_system::Config175		+ pallet_common::Config176		+ pallet_fungible::Config177		+ orml_tokens::Config178		+ pallet_balances::Config179	{180		/// The overarching event type.181		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;182183		/// Currency type for withdraw and balance storage.184		type Currency: Currency<Self::AccountId>;185186		/// Required origin for registering asset.187		type RegisterOrigin: EnsureOrigin<Self::RuntimeOrigin>;188189		/// Weight information for the extrinsics in this module.190		type WeightInfo: WeightInfo;191	}192193	#[derive(Clone, Eq, PartialEq, RuntimeDebug, Encode, Decode, TypeInfo)]194	pub struct AssetMetadata<Balance> {195		pub name: Vec<u8>,196		pub symbol: Vec<u8>,197		pub decimals: u8,198		pub minimal_balance: Balance,199	}200201	#[pallet::error]202	pub enum Error<T> {203		/// The given location could not be used (e.g. because it cannot be expressed in the204		/// desired version of XCM).205		BadLocation,206		/// MultiLocation existed207		MultiLocationExisted,208		/// AssetId not exists209		AssetIdNotExists,210		/// AssetId exists211		AssetIdExisted,212	}213214	#[pallet::event]215	#[pallet::generate_deposit(fn deposit_event)]216	pub enum Event<T: Config> {217		/// The foreign asset registered.218		ForeignAssetRegistered {219			asset_id: ForeignAssetId,220			asset_address: MultiLocation,221			metadata: AssetMetadata<BalanceOf<T>>,222		},223		/// The foreign asset updated.224		ForeignAssetUpdated {225			asset_id: ForeignAssetId,226			asset_address: MultiLocation,227			metadata: AssetMetadata<BalanceOf<T>>,228		},229		/// The asset registered.230		AssetRegistered {231			asset_id: AssetIds,232			metadata: AssetMetadata<BalanceOf<T>>,233		},234		/// The asset updated.235		AssetUpdated {236			asset_id: AssetIds,237			metadata: AssetMetadata<BalanceOf<T>>,238		},239	}240241	/// Next available Foreign AssetId ID.242	///243	/// NextForeignAssetId: ForeignAssetId244	#[pallet::storage]245	#[pallet::getter(fn next_foreign_asset_id)]246	pub type NextForeignAssetId<T: Config> = StorageValue<_, ForeignAssetId, ValueQuery>;247	/// The storages for MultiLocations.248	///249	/// ForeignAssetLocations: map ForeignAssetId => Option<MultiLocation>250	#[pallet::storage]251	#[pallet::getter(fn foreign_asset_locations)]252	pub type ForeignAssetLocations<T: Config> =253		StorageMap<_, Twox64Concat, ForeignAssetId, MultiLocation, OptionQuery>;254255	/// The storages for CurrencyIds.256	///257	/// LocationToCurrencyIds: map MultiLocation => Option<ForeignAssetId>258	#[pallet::storage]259	#[pallet::getter(fn location_to_currency_ids)]260	pub type LocationToCurrencyIds<T: Config> =261		StorageMap<_, Twox64Concat, MultiLocation, ForeignAssetId, OptionQuery>;262263	/// The storages for AssetMetadatas.264	///265	/// AssetMetadatas: map AssetIds => Option<AssetMetadata>266	#[pallet::storage]267	#[pallet::getter(fn asset_metadatas)]268	pub type AssetMetadatas<T: Config> =269		StorageMap<_, Twox64Concat, AssetIds, AssetMetadata<BalanceOf<T>>, OptionQuery>;270271	/// The storages for assets to fungible collection binding272	///273	#[pallet::storage]274	#[pallet::getter(fn asset_binding)]275	pub type AssetBinding<T: Config> =276		StorageMap<_, Twox64Concat, ForeignAssetId, CollectionId, OptionQuery>;277278	#[pallet::pallet]279	#[pallet::without_storage_info]280	pub struct Pallet<T>(_);281282	#[pallet::call]283	impl<T: Config> Pallet<T> {284		#[pallet::call_index(0)]285		#[pallet::weight(<T as Config>::WeightInfo::register_foreign_asset())]286		pub fn register_foreign_asset(287			origin: OriginFor<T>,288			owner: T::AccountId,289			location: Box<VersionedMultiLocation>,290			metadata: Box<AssetMetadata<BalanceOf<T>>>,291		) -> DispatchResult {292			T::RegisterOrigin::ensure_origin(origin.clone())?;293294			let location: MultiLocation = (*location)295				.try_into()296				.map_err(|()| Error::<T>::BadLocation)?;297298			let md = metadata.clone();299			let name: Vec<u16> = md.name.into_iter().map(|x| x as u16).collect::<Vec<u16>>();300			let mut description: Vec<u16> = "Foreign assets collection for "301				.encode_utf16()302				.collect::<Vec<u16>>();303			description.append(&mut name.clone());304305			let data: CreateCollectionData<T::AccountId> = CreateCollectionData {306				name: name.try_into().unwrap(),307				description: description.try_into().unwrap(),308				mode: CollectionMode::Fungible(md.decimals),309				..Default::default()310			};311			let owner = T::CrossAccountId::from_sub(owner);312			let bounded_collection_id =313				<PalletFungible<T>>::init_foreign_collection(owner.clone(), owner, data)?;314			let foreign_asset_id =315				Self::do_register_foreign_asset(&location, &metadata, bounded_collection_id)?;316317			Self::deposit_event(Event::<T>::ForeignAssetRegistered {318				asset_id: foreign_asset_id,319				asset_address: location,320				metadata: *metadata,321			});322			Ok(())323		}324325		#[pallet::call_index(1)]326		#[pallet::weight(<T as Config>::WeightInfo::update_foreign_asset())]327		pub fn update_foreign_asset(328			origin: OriginFor<T>,329			foreign_asset_id: ForeignAssetId,330			location: Box<VersionedMultiLocation>,331			metadata: Box<AssetMetadata<BalanceOf<T>>>,332		) -> DispatchResult {333			T::RegisterOrigin::ensure_origin(origin)?;334335			let location: MultiLocation = (*location)336				.try_into()337				.map_err(|()| Error::<T>::BadLocation)?;338			Self::do_update_foreign_asset(foreign_asset_id, &location, &metadata)?;339340			Self::deposit_event(Event::<T>::ForeignAssetUpdated {341				asset_id: foreign_asset_id,342				asset_address: location,343				metadata: *metadata,344			});345			Ok(())346		}347	}348}349350impl<T: Config> Pallet<T> {351	fn get_next_foreign_asset_id() -> Result<ForeignAssetId, DispatchError> {352		NextForeignAssetId::<T>::try_mutate(|current| -> Result<ForeignAssetId, DispatchError> {353			let id = *current;354			*current = current355				.checked_add(One::one())356				.ok_or(ArithmeticError::Overflow)?;357			Ok(id)358		})359	}360361	fn do_register_foreign_asset(362		location: &MultiLocation,363		metadata: &AssetMetadata<BalanceOf<T>>,364		bounded_collection_id: CollectionId,365	) -> Result<ForeignAssetId, DispatchError> {366		let foreign_asset_id = Self::get_next_foreign_asset_id()?;367		LocationToCurrencyIds::<T>::try_mutate(location, |maybe_currency_ids| -> DispatchResult {368			ensure!(369				maybe_currency_ids.is_none(),370				Error::<T>::MultiLocationExisted371			);372			*maybe_currency_ids = Some(foreign_asset_id);373			// *maybe_currency_ids = Some(CurrencyId::ForeignAsset(foreign_asset_id));374375			ForeignAssetLocations::<T>::try_mutate(376				foreign_asset_id,377				|maybe_location| -> DispatchResult {378					ensure!(maybe_location.is_none(), Error::<T>::MultiLocationExisted);379					*maybe_location = Some(location.clone());380381					AssetMetadatas::<T>::try_mutate(382						AssetIds::ForeignAssetId(foreign_asset_id),383						|maybe_asset_metadatas| -> DispatchResult {384							ensure!(maybe_asset_metadatas.is_none(), Error::<T>::AssetIdExisted);385							*maybe_asset_metadatas = Some(metadata.clone());386							Ok(())387						},388					)389				},390			)?;391392			AssetBinding::<T>::try_mutate(foreign_asset_id, |collection_id| -> DispatchResult {393				*collection_id = Some(bounded_collection_id);394				Ok(())395			})396		})?;397398		Ok(foreign_asset_id)399	}400401	fn do_update_foreign_asset(402		foreign_asset_id: ForeignAssetId,403		location: &MultiLocation,404		metadata: &AssetMetadata<BalanceOf<T>>,405	) -> DispatchResult {406		ForeignAssetLocations::<T>::try_mutate(407			foreign_asset_id,408			|maybe_multi_locations| -> DispatchResult {409				let old_multi_locations = maybe_multi_locations410					.as_mut()411					.ok_or(Error::<T>::AssetIdNotExists)?;412413				AssetMetadatas::<T>::try_mutate(414					AssetIds::ForeignAssetId(foreign_asset_id),415					|maybe_asset_metadatas| -> DispatchResult {416						ensure!(417							maybe_asset_metadatas.is_some(),418							Error::<T>::AssetIdNotExists419						);420421						// modify location422						if location != old_multi_locations {423							LocationToCurrencyIds::<T>::remove(old_multi_locations.clone());424							LocationToCurrencyIds::<T>::try_mutate(425								location,426								|maybe_currency_ids| -> DispatchResult {427									ensure!(428										maybe_currency_ids.is_none(),429										Error::<T>::MultiLocationExisted430									);431									// *maybe_currency_ids = Some(CurrencyId::ForeignAsset(foreign_asset_id));432									*maybe_currency_ids = Some(foreign_asset_id);433									Ok(())434								},435							)?;436						}437						*maybe_asset_metadatas = Some(metadata.clone());438						*old_multi_locations = location.clone();439						Ok(())440					},441				)442			},443		)444	}445}446447pub use frame_support::{448	traits::{449		fungibles::{Balanced, CreditOf},450		tokens::currency::Currency as CurrencyT,451		OnUnbalanced as OnUnbalancedT,452	},453	weights::{WeightToFeePolynomial, WeightToFee},454};455456pub struct FreeForAll<457	WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,458	AssetId: Get<MultiLocation>,459	AccountId,460	Currency: CurrencyT<AccountId>,461	OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,462>(463	Weight,464	Currency::Balance,465	PhantomData<(WeightToFee, AssetId, AccountId, Currency, OnUnbalanced)>,466);467468impl<469		WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,470		AssetId: Get<MultiLocation>,471		AccountId,472		Currency: CurrencyT<AccountId>,473		OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,474	> WeightTrader for FreeForAll<WeightToFee, AssetId, AccountId, Currency, OnUnbalanced>475{476	fn new() -> Self {477		Self(0, Zero::zero(), PhantomData)478	}479480	fn buy_weight(&mut self, weight: Weight, payment: Assets) -> Result<Assets, XcmError> {481		log::trace!(target: "fassets::weight", "buy_weight weight: {:?}, payment: {:?}", weight, payment);482		Ok(payment)483	}484}485impl<WeightToFee, AssetId, AccountId, Currency, OnUnbalanced> Drop486	for FreeForAll<WeightToFee, AssetId, AccountId, Currency, OnUnbalanced>487where488	WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,489	AssetId: Get<MultiLocation>,490	Currency: CurrencyT<AccountId>,491	OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,492{493	fn drop(&mut self) {494		OnUnbalanced::on_unbalanced(Currency::issue(self.1));495	}496}
modifiedruntime/common/config/xcm/foreignassets.rsdiffbeforeafterboth
--- a/runtime/common/config/xcm/foreignassets.rs
+++ b/runtime/common/config/xcm/foreignassets.rs
@@ -18,7 +18,7 @@
 	traits::{Contains, Get, fungibles},
 	parameter_types,
 };
-use sp_runtime::traits::{Zero, Convert};
+use sp_runtime::traits::Convert;
 use xcm::v1::{Junction::*, MultiLocation, Junctions::*};
 use xcm::latest::MultiAsset;
 use xcm_builder::{FungiblesAdapter, ConvertedConcreteAssetId};
@@ -38,16 +38,16 @@
 	pub CheckingAccount: AccountId = PolkadotXcm::check_account();
 }
 
-/// Allow checking in assets that have issuance > 0.
-pub struct NonZeroIssuance<AccountId, ForeignAssets>(PhantomData<(AccountId, ForeignAssets)>);
+/// No teleports are allowed
+pub struct NoTeleports<AccountId, ForeignAssets>(PhantomData<(AccountId, ForeignAssets)>);
 
 impl<AccountId, ForeignAssets> Contains<<ForeignAssets as fungibles::Inspect<AccountId>>::AssetId>
-	for NonZeroIssuance<AccountId, ForeignAssets>
+	for NoTeleports<AccountId, ForeignAssets>
 where
 	ForeignAssets: fungibles::Inspect<AccountId>,
 {
-	fn contains(id: &<ForeignAssets as fungibles::Inspect<AccountId>>::AssetId) -> bool {
-		!ForeignAssets::total_issuance(*id).is_zero()
+	fn contains(_id: &<ForeignAssets as fungibles::Inspect<AccountId>>::AssetId) -> bool {
+		false
 	}
 }
 
@@ -84,7 +84,7 @@
 			Some(AssetIds::ForeignAssetId(foreign_asset_id)) => {
 				ConvertAssetId::convert_ref(AssetIds::ForeignAssetId(foreign_asset_id))
 			}
-			_ => ConvertAssetId::convert_ref(AssetIds::ForeignAssetId(0)),
+			_ => Err(()),
 		}
 	}
 
@@ -132,9 +132,8 @@
 	LocationToAccountId,
 	// Our chain's account ID type (we can't get away without mentioning it explicitly):
 	AccountId,
-	// We only want to allow teleports of known assets. We use non-zero issuance as an indication
-	// that this asset is known.
-	NonZeroIssuance<AccountId, ForeignAssets>,
+	// No teleports are allowed
+	NoTeleports<AccountId, ForeignAssets>,
 	// The account to use for tracking teleports.
 	CheckingAccount,
 >;
modifiedruntime/common/config/xcm/mod.rsdiffbeforeafterboth
--- a/runtime/common/config/xcm/mod.rs
+++ b/runtime/common/config/xcm/mod.rs
@@ -186,6 +186,9 @@
 			TransferReserveAsset { dest: dst, .. } => {
 				allowed |= allowed_locations.contains(dst);
 			}
+			InitiateReserveWithdraw { reserve: dst, .. } => {
+				allowed |= allowed_locations.contains(dst);
+			}
 			_ => {}
 		});
 
modifiedtests/src/config.tsdiffbeforeafterboth
--- a/tests/src/config.ts
+++ b/tests/src/config.ts
@@ -25,6 +25,8 @@
   moonbeamUrl: process.env.moonbeamUrl || 'ws://127.0.0.1:9947',
   moonriverUrl: process.env.moonbeamUrl || 'ws://127.0.0.1:9947',
   westmintUrl: process.env.westmintUrl || 'ws://127.0.0.1:9948',
+  statemineUrl: process.env.statemineUrl || 'ws://127.0.0.1:9948',
+  statemintUrl: process.env.statemintUrl || 'ws://127.0.0.1:9948',
 };
 
 export default config;
modifiedtests/src/util/index.tsdiffbeforeafterboth
--- a/tests/src/util/index.ts
+++ b/tests/src/util/index.ts
@@ -11,7 +11,7 @@
 import config from '../config';
 import {ChainHelperBase} from './playgrounds/unique';
 import {ILogger} from './playgrounds/types';
-import {DevUniqueHelper, SilentLogger, SilentConsole, DevMoonbeamHelper, DevMoonriverHelper, DevAcalaHelper, DevKaruraHelper, DevRelayHelper, DevWestmintHelper} from './playgrounds/unique.dev';
+import {DevUniqueHelper, SilentLogger, SilentConsole, DevMoonbeamHelper, DevMoonriverHelper, DevAcalaHelper, DevKaruraHelper, DevRelayHelper, DevWestmintHelper, DevStatemineHelper, DevStatemintHelper} from './playgrounds/unique.dev';
 
 chai.use(chaiAsPromised);
 chai.use(chaiSubset);
@@ -65,6 +65,14 @@
   return usingPlaygroundsGeneral<DevWestmintHelper>(DevWestmintHelper, url, code);
 };
 
+export const usingStateminePlaygrounds = (url: string, code: (helper: DevWestmintHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => {
+  return usingPlaygroundsGeneral<DevStatemineHelper>(DevWestmintHelper, url, code);
+};
+
+export const usingStatemintPlaygrounds = (url: string, code: (helper: DevWestmintHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => {
+  return usingPlaygroundsGeneral<DevStatemintHelper>(DevWestmintHelper, url, code);
+};
+
 export const usingRelayPlaygrounds = (url: string, code: (helper: DevRelayHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => {
   return usingPlaygroundsGeneral<DevRelayHelper>(DevRelayHelper, url, code);
 };
modifiedtests/src/util/playgrounds/unique.dev.tsdiffbeforeafterboth
--- a/tests/src/util/playgrounds/unique.dev.ts
+++ b/tests/src/util/playgrounds/unique.dev.ts
@@ -118,7 +118,16 @@
   }
 }
 
-export class DevRelayHelper extends RelayHelper {}
+export class DevRelayHelper extends RelayHelper {
+  wait: WaitGroup;
+
+  constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {
+    options.helperBase = options.helperBase ?? DevRelayHelper;
+
+    super(logger, options);
+    this.wait = new WaitGroup(this);
+  }
+}
 
 export class DevWestmintHelper extends WestmintHelper {
   wait: WaitGroup;
@@ -131,12 +140,17 @@
   }
 }
 
+export class DevStatemineHelper extends DevWestmintHelper {}
+
+export class DevStatemintHelper extends DevWestmintHelper {}
+
 export class DevMoonbeamHelper extends MoonbeamHelper {
   account: MoonbeamAccountGroup;
   wait: WaitGroup;
 
   constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {
     options.helperBase = options.helperBase ?? DevMoonbeamHelper;
+    options.notePreimagePallet = options.notePreimagePallet ?? 'democracy';
 
     super(logger, options);
     this.account = new MoonbeamAccountGroup(this);
@@ -144,7 +158,12 @@
   }
 }
 
-export class DevMoonriverHelper extends DevMoonbeamHelper {}
+export class DevMoonriverHelper extends DevMoonbeamHelper {
+  constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {
+    options.notePreimagePallet = options.notePreimagePallet ?? 'preimage';
+    super(logger, options);
+  }
+}
 
 export class DevAcalaHelper extends AcalaHelper {
   wait: WaitGroup;
modifiedtests/src/util/playgrounds/unique.tsdiffbeforeafterboth
--- a/tests/src/util/playgrounds/unique.ts
+++ b/tests/src/util/playgrounds/unique.ts
@@ -2740,21 +2740,72 @@
     this.palletName = palletName;
   }
 
-  async limitedReserveTransferAssets(signer: TSigner, destination: any, beneficiary: any, assets: any, feeAssetItem: number, weightLimit: number) {
-    await this.helper.executeExtrinsic(signer, `api.tx.${this.palletName}.limitedReserveTransferAssets`, [destination, beneficiary, assets, feeAssetItem, {Limited: weightLimit}], true);
+  async limitedReserveTransferAssets(signer: TSigner, destination: any, beneficiary: any, assets: any, feeAssetItem: number, weightLimit: any) {
+    await this.helper.executeExtrinsic(signer, `api.tx.${this.palletName}.limitedReserveTransferAssets`, [destination, beneficiary, assets, feeAssetItem, weightLimit], true);
+  }
+
+  async teleportAssets(signer: TSigner, destination: any, beneficiary: any, assets: any, feeAssetItem: number) {
+    await this.helper.executeExtrinsic(signer, `api.tx.${this.palletName}.teleportAssets`, [destination, beneficiary, assets, feeAssetItem], true);
+  }
+
+  async teleportNativeAsset(signer: TSigner, destinationParaId: number, targetAccount: Uint8Array, amount: bigint) {
+    const destination = {
+      V1: {
+        parents: 0,
+        interior: {
+          X1: {
+            Parachain: destinationParaId,
+          },
+        },
+      },
+    };
+
+    const beneficiary = {
+      V1: {
+        parents: 0,
+        interior: {
+          X1: {
+            AccountId32: {
+              network: 'Any',
+              id: targetAccount,
+            },
+          },
+        },
+      },
+    };
+
+    const assets = {
+      V1: [
+        {
+          id: {
+            Concrete: {
+              parents: 0,
+              interior: 'Here',
+            },
+          },
+          fun: {
+            Fungible: amount,
+          },
+        },
+      ],
+    };
+
+    const feeAssetItem = 0;
+
+    await this.teleportAssets(signer, destination, beneficiary, assets, feeAssetItem);
   }
 }
 
 class XTokensGroup<T extends ChainHelperBase> extends HelperGroup<T> {
-  async transfer(signer: TSigner, currencyId: any, amount: bigint, destination: any, destWeight: number) {
+  async transfer(signer: TSigner, currencyId: any, amount: bigint, destination: any, destWeight: any) {
     await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transfer', [currencyId, amount, destination, destWeight], true);
   }
 
-  async transferMultiasset(signer: TSigner, asset: any, destination: any, destWeight: number) {
+  async transferMultiasset(signer: TSigner, asset: any, destination: any, destWeight: any) {
     await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transferMultiasset', [asset, destination, destWeight], true);
   }
 
-  async transferMulticurrencies(signer: TSigner, currencies: any[], feeItem: number, destLocation: any, destWeight: number) {
+  async transferMulticurrencies(signer: TSigner, currencies: any[], feeItem: number, destLocation: any, destWeight: any) {
     await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transferMulticurrencies', [currencies, feeItem, destLocation, destWeight], true);
   }
 }
@@ -2823,12 +2874,19 @@
 }
 
 class MoonbeamDemocracyGroup extends HelperGroup<MoonbeamHelper> {
+  notePreimagePallet: string;
+
+  constructor(helper: MoonbeamHelper, options: {[key: string]: any} = {}) {
+    super(helper);
+    this.notePreimagePallet = options.notePreimagePallet;
+  }
+
   async notePreimage(signer: TSigner, encodedProposal: string) {
-    await this.helper.executeExtrinsic(signer, 'api.tx.democracy.notePreimage', [encodedProposal], true);
+    await this.helper.executeExtrinsic(signer, `api.tx.${this.notePreimagePallet}.notePreimage`, [encodedProposal], true);
   }
 
-  externalProposeMajority(proposalHash: string) {
-    return this.helper.constructApiCall('api.tx.democracy.externalProposeMajority', [proposalHash]);
+  externalProposeMajority(proposal: any) {
+    return this.helper.constructApiCall('api.tx.democracy.externalProposeMajority', [proposal]);
   }
 
   fastTrack(proposalHash: string, votingPeriod: number, delayPeriod: number) {
@@ -2857,7 +2915,7 @@
     await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.vote`, [proposalHash, proposalIndex, approve], true);
   }
 
-  async close(signer: TSigner, proposalHash: string, proposalIndex: number, weightBound: number, lengthBound: number) {
+  async close(signer: TSigner, proposalHash: string, proposalIndex: number, weightBound: any, lengthBound: number) {
     await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.close`, [proposalHash, proposalIndex, weightBound, lengthBound], true);
   }
 
@@ -2917,11 +2975,13 @@
 }
 
 export class RelayHelper extends XcmChainHelper {
+  balance: SubstrateBalanceGroup<RelayHelper>;
   xcm: XcmGroup<RelayHelper>;
 
   constructor(logger?: ILogger, options: {[key: string]: any} = {}) {
     super(logger, options.helperBase ?? RelayHelper);
 
+    this.balance = new SubstrateBalanceGroup(this);
     this.xcm = new XcmGroup(this, 'xcmPallet');
   }
 }
@@ -2960,7 +3020,7 @@
     this.assetManager = new MoonbeamAssetManagerGroup(this);
     this.assets = new AssetsGroup(this);
     this.xTokens = new XTokensGroup(this);
-    this.democracy = new MoonbeamDemocracyGroup(this);
+    this.democracy = new MoonbeamDemocracyGroup(this, options);
     this.collective = {
       council: new MoonbeamCollectiveGroup(this, 'councilCollective'),
       techCommittee: new MoonbeamCollectiveGroup(this, 'techCommitteeCollective'),
modifiedtests/src/xcm/xcmOpal.test.tsdiffbeforeafterboth
--- a/tests/src/xcm/xcmOpal.test.ts
+++ b/tests/src/xcm/xcmOpal.test.ts
@@ -31,6 +31,7 @@
 const ASSET_METADATA_DESCRIPTION = 'USDT';
 const ASSET_METADATA_MINIMAL_BALANCE = 1n;
 
+const RELAY_DECIMALS = 12;
 const WESTMINT_DECIMALS = 12;
 
 const TRANSFER_AMOUNT = 1_000_000_000_000_000_000n;
@@ -147,9 +148,8 @@
       };
 
       const feeAssetItem = 0;
-      const weightLimit = 5_000_000_000;
 
-      await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, weightLimit);
+      await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, 'Unlimited');
     });
   
   });
@@ -202,16 +202,15 @@
       };
 
       const feeAssetItem = 0;
-      const weightLimit = 5000000000;
 
       balanceStmnBefore = await helper.balance.getSubstrate(alice.address);
-      await helper.xcm.limitedReserveTransferAssets(alice, dest, beneficiary, assets, feeAssetItem, weightLimit);
+      await helper.xcm.limitedReserveTransferAssets(alice, dest, beneficiary, assets, feeAssetItem, 'Unlimited');
 
       balanceStmnAfter = await helper.balance.getSubstrate(alice.address);
 
       // common good parachain take commission in it native token
       console.log(
-        'Opal to Westmint transaction fees on Westmint: %s WND',
+        '[Westmint -> Opal] transaction fees on Westmint: %s WND',
         helper.util.bigIntToDecimals(balanceStmnBefore - balanceStmnAfter, WESTMINT_DECIMALS),
       );
       expect(balanceStmnBefore > balanceStmnAfter).to.be.true;
@@ -227,18 +226,19 @@
 
     balanceOpalAfter = await helper.balance.getSubstrate(alice.address);
 
-    // commission has not paid in USDT token
-    expect(free == TRANSFER_AMOUNT).to.be.true;
     console.log(
-      'Opal to Westmint transaction fees on Opal: %s USDT',
-      helper.util.bigIntToDecimals(TRANSFER_AMOUNT - free),
+      '[Westmint -> Opal] transaction fees on Opal: %s USDT',
+      helper.util.bigIntToDecimals(TRANSFER_AMOUNT - free, ASSET_METADATA_DECIMALS),
     );
+    console.log(
+      '[Westmint -> Opal] transaction fees on Opal: %s OPL',
+      helper.util.bigIntToDecimals(balanceOpalAfter - balanceOpalBefore),
+    );
+
+    // commission has not paid in USDT token
+    expect(free == TRANSFER_AMOUNT).to.be.true;
     // ... and parachain native token
     expect(balanceOpalAfter == balanceOpalBefore).to.be.true;
-    console.log(
-      'Opal to Westmint transaction fees on Opal: %s WND',
-      helper.util.bigIntToDecimals(balanceOpalAfter - balanceOpalBefore, WESTMINT_DECIMALS),
-    );    
   });
 
   itSub('Should connect and send USDT from Unique to Statemine back', async ({helper}) => {
@@ -276,9 +276,8 @@
     ];
 
     const feeItem = 1;
-    const destWeight = 500000000000;
 
-    await helper.xTokens.transferMulticurrencies(alice, currencies, feeItem, destination, destWeight);
+    await helper.xTokens.transferMulticurrencies(alice, currencies, feeItem, destination, 'Unlimited');
     
     // the commission has been paid in parachain native token
     balanceOpalFinal = await helper.balance.getSubstrate(alice.address);
@@ -339,9 +338,8 @@
       };
 
       const feeAssetItem = 0;
-      const weightLimit = 5_000_000_000;
 
-      await helper.xcm.limitedReserveTransferAssets(bob, destination, beneficiary, assets, feeAssetItem, weightLimit);
+      await helper.xcm.limitedReserveTransferAssets(bob, destination, beneficiary, assets, feeAssetItem, 'Unlimited');
     });
   
     await helper.wait.newBlocks(3);
@@ -363,20 +361,23 @@
   });
 
   itSub('Should connect and send Relay token back', async ({helper}) => {
+    let relayTokenBalanceBefore: bigint;
+    let relayTokenBalanceAfter: bigint;
+    await usingRelayPlaygrounds(relayUrl, async (helper) => {
+      relayTokenBalanceBefore = await helper.balance.getSubstrate(bob.address);
+    });
+
     const destination = {
       V1: {
         parents: 1,
-        interior: {X2: [
-          {
-            Parachain: STATEMINE_CHAIN,
-          },
-          {
+        interior: {
+          X1:{
             AccountId32: {
               network: 'Any',
               id: bob.addressRaw,
             },
           },
-        ]},
+        },
       },
     };
 
@@ -390,11 +391,19 @@
     ];
 
     const feeItem = 0;
-    const destWeight = 500000000000;
 
-    await helper.xTokens.transferMulticurrencies(bob, currencies, feeItem, destination, destWeight);
+    await helper.xTokens.transferMulticurrencies(bob, currencies, feeItem, destination, 'Unlimited');
 
     balanceBobFinal = await helper.balance.getSubstrate(bob.address);
-    console.log('Relay (Westend) to Opal transaction fees: %s OPL', balanceBobAfter - balanceBobFinal);
+    console.log('[Opal -> Relay (Westend)] transaction fees: %s OPL',  helper.util.bigIntToDecimals(balanceBobAfter - balanceBobFinal));
+
+    await usingRelayPlaygrounds(relayUrl, async (helper) => {
+      await helper.wait.newBlocks(10);
+      relayTokenBalanceAfter = await helper.balance.getSubstrate(bob.address);
+
+      const diff = relayTokenBalanceAfter - relayTokenBalanceBefore;
+      console.log('[Opal -> Relay (Westend)] actually delivered: %s WND', helper.util.bigIntToDecimals(diff, RELAY_DECIMALS));
+      expect(diff > 0, 'Relay tokens was not delivered back').to.be.true;
+    });
   });
 });
modifiedtests/src/xcm/xcmQuartz.test.tsdiffbeforeafterboth
--- a/tests/src/xcm/xcmQuartz.test.ts
+++ b/tests/src/xcm/xcmQuartz.test.ts
@@ -17,21 +17,430 @@
 import {IKeyringPair} from '@polkadot/types/types';
 import {blake2AsHex} from '@polkadot/util-crypto';
 import config from '../config';
-import {XcmV2TraitsOutcome, XcmV2TraitsError} from '../interfaces';
-import {itSub, expect, describeXCM, usingPlaygrounds, usingKaruraPlaygrounds, usingRelayPlaygrounds, usingMoonriverPlaygrounds} from '../util';
+import {XcmV2TraitsError} from '../interfaces';
+import {itSub, expect, describeXCM, usingPlaygrounds, usingKaruraPlaygrounds, usingRelayPlaygrounds, usingMoonriverPlaygrounds, usingStateminePlaygrounds} from '../util';
 
 const QUARTZ_CHAIN = 2095;
+const STATEMINE_CHAIN = 1000;
 const KARURA_CHAIN = 2000;
 const MOONRIVER_CHAIN = 2023;
 
+const STATEMINE_PALLET_INSTANCE = 50;
+
 const relayUrl = config.relayUrl;
+const statemineUrl = config.statemineUrl;
 const karuraUrl = config.karuraUrl;
 const moonriverUrl = config.moonriverUrl;
 
+const RELAY_DECIMALS = 12;
+const STATEMINE_DECIMALS = 12;
 const KARURA_DECIMALS = 12;
 
 const TRANSFER_AMOUNT = 2000000000000000000000000n;
 
+const FUNDING_AMOUNT = 3_500_000_0000_000_000n; 
+
+const TRANSFER_AMOUNT_RELAY = 50_000_000_000_000_000n;
+
+const USDT_ASSET_ID = 100;
+const USDT_ASSET_METADATA_DECIMALS = 18;
+const USDT_ASSET_METADATA_NAME = 'USDT';
+const USDT_ASSET_METADATA_DESCRIPTION = 'USDT';
+const USDT_ASSET_METADATA_MINIMAL_BALANCE = 1n;
+const USDT_ASSET_AMOUNT = 10_000_000_000_000_000_000_000_000n;
+
+describeXCM('[XCM] Integration test: Exchanging USDT with Statemine', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+  
+  let balanceStmnBefore: bigint;
+  let balanceStmnAfter: bigint;
+
+  let balanceQuartzBefore: bigint;
+  let balanceQuartzAfter: bigint;
+  let balanceQuartzFinal: bigint;
+
+  let balanceBobBefore: bigint;
+  let balanceBobAfter: bigint;
+  let balanceBobFinal: bigint;
+
+  let balanceBobRelayTokenBefore: bigint;
+  let balanceBobRelayTokenAfter: bigint;
+
+
+  before(async () => {
+    await usingPlaygrounds(async (_helper, privateKey) => {
+      alice = await privateKey('//Alice');
+      bob = await privateKey('//Bob'); // sovereign account on Statemine(t) funds donor
+    });
+
+    await usingRelayPlaygrounds(relayUrl, async (helper) => {
+      // Fund accounts on Statemine(t)
+      await helper.xcm.teleportNativeAsset(alice, STATEMINE_CHAIN, alice.addressRaw, FUNDING_AMOUNT);
+      await helper.xcm.teleportNativeAsset(alice, STATEMINE_CHAIN, bob.addressRaw, FUNDING_AMOUNT);
+    });
+
+    await usingStateminePlaygrounds(statemineUrl, async (helper) => {
+      const sovereignFundingAmount = 3_500_000_000n; 
+
+      await helper.assets.create(
+        alice,
+        USDT_ASSET_ID,
+        alice.address,
+        USDT_ASSET_METADATA_MINIMAL_BALANCE,
+      );
+      await helper.assets.setMetadata(
+        alice,
+        USDT_ASSET_ID,
+        USDT_ASSET_METADATA_NAME,
+        USDT_ASSET_METADATA_DESCRIPTION,
+        USDT_ASSET_METADATA_DECIMALS,
+      );
+      await helper.assets.mint(
+        alice,
+        USDT_ASSET_ID,
+        alice.address,
+        USDT_ASSET_AMOUNT,
+      );
+
+      // funding parachain sovereing account on Statemine(t).
+      // The sovereign account should be created before any action
+      // (the assets pallet on Statemine(t) check if the sovereign account exists)
+      const parachainSovereingAccount = helper.address.paraSiblingSovereignAccount(QUARTZ_CHAIN);
+      await helper.balance.transferToSubstrate(bob, parachainSovereingAccount, sovereignFundingAmount);
+    });
+
+
+    await usingPlaygrounds(async (helper) => {
+      const location = {
+        V1: {
+          parents: 1,
+          interior: {X3: [
+            {
+              Parachain: STATEMINE_CHAIN,
+            },
+            {
+              PalletInstance: STATEMINE_PALLET_INSTANCE,
+            },
+            {
+              GeneralIndex: USDT_ASSET_ID,
+            },
+          ]},
+        },
+      };
+
+      const metadata =
+      {
+        name: USDT_ASSET_ID,
+        symbol: USDT_ASSET_METADATA_NAME,
+        decimals: USDT_ASSET_METADATA_DECIMALS,
+        minimalBalance: USDT_ASSET_METADATA_MINIMAL_BALANCE,
+      };
+      await helper.getSudo().foreignAssets.register(alice, alice.address, location, metadata);
+      balanceQuartzBefore = await helper.balance.getSubstrate(alice.address);
+    });
+
+
+    // Providing the relay currency to the quartz sender account
+    // (fee for USDT XCM are paid in relay tokens)
+    await usingRelayPlaygrounds(relayUrl, async (helper) => {
+      const destination = {
+        V1: {
+          parents: 0,
+          interior: {X1: {
+            Parachain: QUARTZ_CHAIN,
+          },
+          },
+        }};
+
+      const beneficiary = {
+        V1: {
+          parents: 0,
+          interior: {X1: {
+            AccountId32: {
+              network: 'Any',
+              id: alice.addressRaw,
+            },
+          }},
+        },
+      };
+
+      const assets = {
+        V1: [
+          {
+            id: {
+              Concrete: {
+                parents: 0,
+                interior: 'Here',
+              },
+            },
+            fun: {
+              Fungible: TRANSFER_AMOUNT_RELAY,
+            },
+          },
+        ],
+      };
+
+      const feeAssetItem = 0;
+
+      await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, 'Unlimited');
+    });
+  
+  });
+
+  itSub('Should connect and send USDT from Statemine to Quartz', async ({helper}) => {
+    await usingStateminePlaygrounds(statemineUrl, async (helper) => {
+      const dest = {
+        V1: {
+          parents: 1,
+          interior: {X1: {
+            Parachain: QUARTZ_CHAIN,
+          },
+          },
+        }};
+
+      const beneficiary = {
+        V1: {
+          parents: 0,
+          interior: {X1: {
+            AccountId32: {
+              network: 'Any',
+              id: alice.addressRaw,
+            },
+          }},
+        },
+      };
+
+      const assets = {
+        V1: [
+          {
+            id: {
+              Concrete: {
+                parents: 0,
+                interior: {
+                  X2: [
+                    {
+                      PalletInstance: STATEMINE_PALLET_INSTANCE,
+                    },
+                    {
+                      GeneralIndex: USDT_ASSET_ID,
+                    }, 
+                  ]},
+              },
+            },
+            fun: {
+              Fungible: TRANSFER_AMOUNT,
+            },
+          },
+        ],
+      };
+
+      const feeAssetItem = 0;
+
+      balanceStmnBefore = await helper.balance.getSubstrate(alice.address);
+      await helper.xcm.limitedReserveTransferAssets(alice, dest, beneficiary, assets, feeAssetItem, 'Unlimited');
+
+      balanceStmnAfter = await helper.balance.getSubstrate(alice.address);
+
+      // common good parachain take commission in it native token
+      console.log(
+        '[Statemine -> Quartz] transaction fees on Statemine: %s WND',
+        helper.util.bigIntToDecimals(balanceStmnBefore - balanceStmnAfter, STATEMINE_DECIMALS),
+      );
+      expect(balanceStmnBefore > balanceStmnAfter).to.be.true;
+
+    });
+
+
+    // ensure that asset has been delivered
+    await helper.wait.newBlocks(3);
+
+    // expext collection id will be with id 1
+    const free = await helper.ft.getBalance(1, {Substrate: alice.address});
+
+    balanceQuartzAfter = await helper.balance.getSubstrate(alice.address);
+
+    console.log(
+      '[Statemine -> Quartz] transaction fees on Quartz: %s USDT',
+      helper.util.bigIntToDecimals(TRANSFER_AMOUNT - free, USDT_ASSET_METADATA_DECIMALS),
+    );
+    console.log(
+      '[Statemine -> Quartz] transaction fees on Quartz: %s QTZ',
+      helper.util.bigIntToDecimals(balanceQuartzAfter - balanceQuartzBefore),
+    );    
+    // commission has not paid in USDT token
+    expect(free).to.be.equal(TRANSFER_AMOUNT);
+    // ... and parachain native token
+    expect(balanceQuartzAfter == balanceQuartzBefore).to.be.true;
+  });
+
+  itSub('Should connect and send USDT from Quartz to Statemine back', async ({helper}) => {
+    const destination = {
+      V1: {
+        parents: 1,
+        interior: {X2: [
+          {
+            Parachain: STATEMINE_CHAIN,
+          },
+          {
+            AccountId32: {
+              network: 'Any',
+              id: alice.addressRaw,
+            },
+          },
+        ]},
+      },
+    };
+
+    const relayFee = 400_000_000_000_000n;
+    const currencies: [any, bigint][] = [
+      [
+        {
+          ForeignAssetId: 0,
+        },
+        TRANSFER_AMOUNT,
+      ], 
+      [
+        {
+          NativeAssetId: 'Parent',
+        },
+        relayFee,
+      ],
+    ];
+
+    const feeItem = 1;
+
+    await helper.xTokens.transferMulticurrencies(alice, currencies, feeItem, destination, 'Unlimited');
+    
+    // the commission has been paid in parachain native token
+    balanceQuartzFinal = await helper.balance.getSubstrate(alice.address);
+    console.log('[Quartz -> Statemine] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(balanceQuartzFinal - balanceQuartzAfter));
+    expect(balanceQuartzAfter > balanceQuartzFinal).to.be.true;
+
+    await usingStateminePlaygrounds(statemineUrl, async (helper) => {
+      await helper.wait.newBlocks(3);
+      
+      // The USDT token never paid fees. Its amount not changed from begin value.
+      // Also check that xcm transfer has been succeeded 
+      expect((await helper.assets.account(USDT_ASSET_ID, alice.address))! == USDT_ASSET_AMOUNT).to.be.true;
+    });
+  });
+
+  itSub('Should connect and send Relay token to Quartz', async ({helper}) => {
+    balanceBobBefore = await helper.balance.getSubstrate(bob.address);
+    balanceBobRelayTokenBefore = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});
+
+    await usingRelayPlaygrounds(relayUrl, async (helper) => {
+      const destination = {
+        V1: {
+          parents: 0,
+          interior: {X1: {
+            Parachain: QUARTZ_CHAIN,
+          },
+          },
+        }};
+
+      const beneficiary = {
+        V1: {
+          parents: 0,
+          interior: {X1: {
+            AccountId32: {
+              network: 'Any',
+              id: bob.addressRaw,
+            },
+          }},
+        },
+      };
+
+      const assets = {
+        V1: [
+          {
+            id: {
+              Concrete: {
+                parents: 0,
+                interior: 'Here',
+              },
+            },
+            fun: {
+              Fungible: TRANSFER_AMOUNT_RELAY,
+            },
+          },
+        ],
+      };
+
+      const feeAssetItem = 0;
+
+      await helper.xcm.limitedReserveTransferAssets(bob, destination, beneficiary, assets, feeAssetItem, 'Unlimited');
+    });
+  
+    await helper.wait.newBlocks(3);
+
+    balanceBobAfter = await helper.balance.getSubstrate(bob.address);  
+    balanceBobRelayTokenAfter = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});
+
+    const wndFeeOnQuartz = balanceBobRelayTokenAfter - TRANSFER_AMOUNT_RELAY - balanceBobRelayTokenBefore;
+    const wndDiffOnQuartz = balanceBobRelayTokenAfter - balanceBobRelayTokenBefore;
+    console.log(
+      '[Relay (Westend) -> Quartz] transaction fees: %s QTZ',
+      helper.util.bigIntToDecimals(balanceBobAfter - balanceBobBefore),
+    );
+    console.log(
+      '[Relay (Westend) -> Quartz] transaction fees: %s WND',
+      helper.util.bigIntToDecimals(wndFeeOnQuartz, STATEMINE_DECIMALS),
+    );
+    console.log('[Relay (Westend) -> Quartz] actually delivered: %s WND', wndDiffOnQuartz);
+    expect(wndFeeOnQuartz == 0n, 'No incoming WND fees should be taken').to.be.true;
+    expect(balanceBobBefore == balanceBobAfter, 'No incoming QTZ fees should be taken').to.be.true;
+  });
+
+  itSub('Should connect and send Relay token back', async ({helper}) => {
+    let relayTokenBalanceBefore: bigint;
+    let relayTokenBalanceAfter: bigint;
+    await usingRelayPlaygrounds(relayUrl, async (helper) => {
+      relayTokenBalanceBefore = await helper.balance.getSubstrate(bob.address);
+    });
+
+    const destination = {
+      V1: {
+        parents: 1,
+        interior: {
+          X1:{
+            AccountId32: {
+              network: 'Any',
+              id: bob.addressRaw,
+            },
+          },
+        },
+      },
+    };
+
+    const currencies: any = [
+      [
+        {
+          NativeAssetId: 'Parent',
+        },
+        TRANSFER_AMOUNT_RELAY,
+      ],
+    ];
+
+    const feeItem = 0;
+
+    await helper.xTokens.transferMulticurrencies(bob, currencies, feeItem, destination, 'Unlimited');
+
+    balanceBobFinal = await helper.balance.getSubstrate(bob.address);
+    console.log('[Quartz -> Relay (Westend)] transaction fees: %s QTZ',  helper.util.bigIntToDecimals(balanceBobAfter - balanceBobFinal));
+
+    await usingRelayPlaygrounds(relayUrl, async (helper) => {
+      await helper.wait.newBlocks(10);
+      relayTokenBalanceAfter = await helper.balance.getSubstrate(bob.address);
+
+      const diff = relayTokenBalanceAfter - relayTokenBalanceBefore;
+      console.log('[Quartz -> Relay (Westend)] actually delivered: %s WND', helper.util.bigIntToDecimals(diff, RELAY_DECIMALS));
+      expect(diff > 0, 'Relay tokens was not delivered back').to.be.true;
+    });
+  });
+});
+
 describeXCM('[XCM] Integration test: Exchanging tokens with Karura', () => {
   let alice: IKeyringPair;
   let randomAccount: IKeyringPair;
@@ -123,14 +532,13 @@
     };
 
     const feeAssetItem = 0;
-    const weightLimit = 5000000000;
 
-    await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, weightLimit);
+    await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');
     balanceQuartzTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);
 
     const qtzFees = balanceQuartzTokenInit - balanceQuartzTokenMiddle - TRANSFER_AMOUNT;
+    expect(qtzFees > 0n, 'Negative fees QTZ, looks like nothing was transferred').to.be.true;
     console.log('[Quartz -> Karura] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(qtzFees));
-    expect(qtzFees > 0n).to.be.true;
 
     await usingKaruraPlaygrounds(karuraUrl, async (helper) => {
       await helper.wait.newBlocks(3);
@@ -173,9 +581,7 @@
         ForeignAsset: 0,
       };
 
-      const destWeight = 50000000;
-
-      await helper.xTokens.transfer(randomAccount, id, TRANSFER_AMOUNT, destination, destWeight);
+      await helper.xTokens.transfer(randomAccount, id, TRANSFER_AMOUNT, destination, 'Unlimited');
       balanceKaruraTokenFinal = await helper.balance.getSubstrate(randomAccount.address);
       balanceQuartzForeignTokenFinal = await helper.tokens.accounts(randomAccount.address, id);
 
@@ -188,7 +594,7 @@
       );
       console.log('[Karura -> Quartz] outcome %s QTZ', helper.util.bigIntToDecimals(qtzOutcomeTransfer));
 
-      expect(karFees > 0).to.be.true;
+      expect(karFees > 0, 'Negative fees KAR, looks like nothing was transferred').to.be.true;
       expect(qtzOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;
     });
 
@@ -215,76 +621,7 @@
       alice = await privateKey('//Alice');
     });
   });
-
-  itSub('Quartz rejects tokens from the Relay', async ({helper}) => {
-    await usingRelayPlaygrounds(relayUrl, async (helper) => {
-      const destination = {
-        V1: {
-          parents: 0,
-          interior: {X1: {
-            Parachain: QUARTZ_CHAIN,
-          },
-          },
-        }};
-
-      const beneficiary = {
-        V1: {
-          parents: 0,
-          interior: {X1: {
-            AccountId32: {
-              network: 'Any',
-              id: alice.addressRaw,
-            },
-          }},
-        },
-      };
-
-      const assets = {
-        V1: [
-          {
-            id: {
-              Concrete: {
-                parents: 0,
-                interior: 'Here',
-              },
-            },
-            fun: {
-              Fungible: 50_000_000_000_000_000n,
-            },
-          },
-        ],
-      };
-
-      const feeAssetItem = 0;
-      const weightLimit = 5_000_000_000;
-
-      await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, weightLimit);
-    });
 
-    const maxWaitBlocks = 3;
-
-    const dmpQueueExecutedDownward = await helper.wait.event(maxWaitBlocks, 'dmpQueue', 'ExecutedDownward');
-
-    expect(
-      dmpQueueExecutedDownward != null,
-      '[Relay] dmpQueue.ExecutedDownward event is expected',
-    ).to.be.true;
-
-    const event = dmpQueueExecutedDownward!.event;
-    const outcome = event.data[1] as XcmV2TraitsOutcome;
-
-    expect(
-      outcome.isIncomplete,
-      '[Relay] The outcome of the XCM should be `Incomplete`',
-    ).to.be.true;
-
-    const incomplete = outcome.asIncomplete;
-    expect(
-      incomplete[1].toString() == 'AssetNotFound',
-      '[Relay] The XCM error should be `AssetNotFound`',
-    ).to.be.true;
-  });
-
   itSub('Quartz rejects KAR tokens from Karura', async ({helper}) => {
     await usingKaruraPlaygrounds(karuraUrl, async (helper) => {
       const destination = {
@@ -308,9 +645,7 @@
         Token: 'KAR',
       };
 
-      const destWeight = 50000000;
-
-      await helper.xTokens.transfer(alice, id, 100_000_000_000n, destination, destWeight);
+      await helper.xTokens.transfer(alice, id, 100_000_000_000n, destination, 'Unlimited');
     });
 
     const maxWaitBlocks = 3;
@@ -326,8 +661,8 @@
     const outcome = event.data[1] as XcmV2TraitsError;
 
     expect(
-      outcome.isUntrustedReserveLocation,
-      '[Karura] The XCM error should be `UntrustedReserveLocation`',
+      outcome.isFailedToTransactAsset,
+      '[Karura] The XCM error should be `FailedToTransactAsset`',
     ).to.be.true;
   });
 });
@@ -420,7 +755,7 @@
 
       // >>> Propose external motion through council >>>
       console.log('Propose external motion through council.......');
-      const externalMotion = helper.democracy.externalProposeMajority(proposalHash);
+      const externalMotion = helper.democracy.externalProposeMajority({Legacy: proposalHash});
       const encodedMotion = externalMotion?.method.toHex() || '';
       const motionHash = blake2AsHex(encodedMotion);
       console.log('Motion hash is %s', motionHash);
@@ -431,7 +766,16 @@
       await helper.collective.council.vote(dorothyAccount, motionHash, councilProposalIdx, true);
       await helper.collective.council.vote(baltatharAccount, motionHash, councilProposalIdx, true);
 
-      await helper.collective.council.close(dorothyAccount, motionHash, councilProposalIdx, 1_000_000_000, externalMotion.encodedLength);
+      await helper.collective.council.close(
+        dorothyAccount,
+        motionHash,
+        councilProposalIdx,
+        {
+          refTime: 1_000_000_000,
+          proofSize: 1_000_000,
+        },
+        externalMotion.encodedLength,
+      );
       console.log('Propose external motion through council.......DONE');
       // <<< Propose external motion through council <<<
 
@@ -448,7 +792,16 @@
       await helper.collective.techCommittee.vote(baltatharAccount, fastTrackHash, techProposalIdx, true);
       await helper.collective.techCommittee.vote(alithAccount, fastTrackHash, techProposalIdx, true);
 
-      await helper.collective.techCommittee.close(baltatharAccount, fastTrackHash, techProposalIdx, 1_000_000_000, fastTrack.encodedLength);
+      await helper.collective.techCommittee.close(
+        baltatharAccount,
+        fastTrackHash,
+        techProposalIdx,
+        {
+          refTime: 1_000_000_000,
+          proofSize: 1_000_000,
+        },
+        fastTrack.encodedLength,
+      );
       console.log('Fast track proposal through technical committee.......DONE');
       // <<< Fast track proposal through technical committee <<<
 
@@ -504,16 +857,15 @@
       },
     };
     const amount = TRANSFER_AMOUNT;
-    const destWeight = 850000000;
 
-    await helper.xTokens.transfer(randomAccountQuartz, currencyId, amount, dest, destWeight);
+    await helper.xTokens.transfer(randomAccountQuartz, currencyId, amount, dest, 'Unlimited');
 
     balanceQuartzTokenMiddle = await helper.balance.getSubstrate(randomAccountQuartz.address);
     expect(balanceQuartzTokenMiddle < balanceQuartzTokenInit).to.be.true;
 
     const transactionFees = balanceQuartzTokenInit - balanceQuartzTokenMiddle - TRANSFER_AMOUNT;
     console.log('[Quartz -> Moonriver] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(transactionFees));
-    expect(transactionFees > 0).to.be.true;
+    expect(transactionFees > 0, 'Negative fees QTZ, looks like nothing was transferred').to.be.true;
 
     await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {
       await helper.wait.newBlocks(3);
@@ -559,15 +911,14 @@
           },
         },
       };
-      const destWeight = 50000000;
 
-      await helper.xTokens.transferMultiasset(randomAccountMoonriver, asset, destination, destWeight);
+      await helper.xTokens.transferMultiasset(randomAccountMoonriver, asset, destination, 'Unlimited');
 
       balanceMovrTokenFinal = await helper.balance.getEthereum(randomAccountMoonriver.address);
 
       const movrFees = balanceMovrTokenMiddle - balanceMovrTokenFinal;
       console.log('[Moonriver -> Quartz] transaction fees on Moonriver: %s MOVR', helper.util.bigIntToDecimals(movrFees));
-      expect(movrFees > 0).to.be.true;
+      expect(movrFees > 0, 'Negative fees MOVR, looks like nothing was transferred').to.be.true;
 
       const qtzRandomAccountAsset = await helper.assets.account(assetId, randomAccountMoonriver.address);
 
modifiedtests/src/xcm/xcmUnique.test.tsdiffbeforeafterboth
--- a/tests/src/xcm/xcmUnique.test.ts
+++ b/tests/src/xcm/xcmUnique.test.ts
@@ -17,21 +17,430 @@
 import {IKeyringPair} from '@polkadot/types/types';
 import {blake2AsHex} from '@polkadot/util-crypto';
 import config from '../config';
-import {XcmV2TraitsError, XcmV2TraitsOutcome} from '../interfaces';
-import {itSub, expect, describeXCM, usingPlaygrounds, usingAcalaPlaygrounds, usingRelayPlaygrounds, usingMoonbeamPlaygrounds} from '../util';
+import {XcmV2TraitsError} from '../interfaces';
+import {itSub, expect, describeXCM, usingPlaygrounds, usingAcalaPlaygrounds, usingRelayPlaygrounds, usingMoonbeamPlaygrounds, usingStatemintPlaygrounds} from '../util';
 
 const UNIQUE_CHAIN = 2037;
+const STATEMINT_CHAIN = 1000;
 const ACALA_CHAIN = 2000;
 const MOONBEAM_CHAIN = 2004;
 
+const STATEMINT_PALLET_INSTANCE = 50;
+
 const relayUrl = config.relayUrl;
+const statemintUrl = config.statemintUrl;
 const acalaUrl = config.acalaUrl;
 const moonbeamUrl = config.moonbeamUrl;
 
+const RELAY_DECIMALS = 12;
+const STATEMINT_DECIMALS = 12;
 const ACALA_DECIMALS = 12;
 
 const TRANSFER_AMOUNT = 2000000000000000000000000n;
 
+const FUNDING_AMOUNT = 3_500_000_0000_000_000n; 
+
+const TRANSFER_AMOUNT_RELAY = 50_000_000_000_000_000n;
+
+const USDT_ASSET_ID = 100;
+const USDT_ASSET_METADATA_DECIMALS = 18;
+const USDT_ASSET_METADATA_NAME = 'USDT';
+const USDT_ASSET_METADATA_DESCRIPTION = 'USDT';
+const USDT_ASSET_METADATA_MINIMAL_BALANCE = 1n;
+const USDT_ASSET_AMOUNT = 10_000_000_000_000_000_000_000_000n;
+
+describeXCM('[XCM] Integration test: Exchanging USDT with Statemint', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+  
+  let balanceStmnBefore: bigint;
+  let balanceStmnAfter: bigint;
+
+  let balanceUniqueBefore: bigint;
+  let balanceUniqueAfter: bigint;
+  let balanceUniqueFinal: bigint;
+
+  let balanceBobBefore: bigint;
+  let balanceBobAfter: bigint;
+  let balanceBobFinal: bigint;
+
+  let balanceBobRelayTokenBefore: bigint;
+  let balanceBobRelayTokenAfter: bigint;
+
+
+  before(async () => {
+    await usingPlaygrounds(async (_helper, privateKey) => {
+      alice = await privateKey('//Alice');
+      bob = await privateKey('//Bob'); // sovereign account on Statemint funds donor
+    });
+
+    await usingRelayPlaygrounds(relayUrl, async (helper) => {
+      // Fund accounts on Statemint
+      await helper.xcm.teleportNativeAsset(alice, STATEMINT_CHAIN, alice.addressRaw, FUNDING_AMOUNT);
+      await helper.xcm.teleportNativeAsset(alice, STATEMINT_CHAIN, bob.addressRaw, FUNDING_AMOUNT);
+    });
+
+    await usingStatemintPlaygrounds(statemintUrl, async (helper) => {
+      const sovereignFundingAmount = 3_500_000_000n; 
+
+      await helper.assets.create(
+        alice,
+        USDT_ASSET_ID,
+        alice.address,
+        USDT_ASSET_METADATA_MINIMAL_BALANCE,
+      );
+      await helper.assets.setMetadata(
+        alice,
+        USDT_ASSET_ID,
+        USDT_ASSET_METADATA_NAME,
+        USDT_ASSET_METADATA_DESCRIPTION,
+        USDT_ASSET_METADATA_DECIMALS,
+      );
+      await helper.assets.mint(
+        alice,
+        USDT_ASSET_ID,
+        alice.address,
+        USDT_ASSET_AMOUNT,
+      );
+
+      // funding parachain sovereing account on Statemint.
+      // The sovereign account should be created before any action
+      // (the assets pallet on Statemint check if the sovereign account exists)
+      const parachainSovereingAccount = helper.address.paraSiblingSovereignAccount(UNIQUE_CHAIN);
+      await helper.balance.transferToSubstrate(bob, parachainSovereingAccount, sovereignFundingAmount);
+    });
+
+
+    await usingPlaygrounds(async (helper) => {
+      const location = {
+        V1: {
+          parents: 1,
+          interior: {X3: [
+            {
+              Parachain: STATEMINT_CHAIN,
+            },
+            {
+              PalletInstance: STATEMINT_PALLET_INSTANCE,
+            },
+            {
+              GeneralIndex: USDT_ASSET_ID,
+            },
+          ]},
+        },
+      };
+
+      const metadata =
+      {
+        name: USDT_ASSET_ID,
+        symbol: USDT_ASSET_METADATA_NAME,
+        decimals: USDT_ASSET_METADATA_DECIMALS,
+        minimalBalance: USDT_ASSET_METADATA_MINIMAL_BALANCE,
+      };
+      await helper.getSudo().foreignAssets.register(alice, alice.address, location, metadata);
+      balanceUniqueBefore = await helper.balance.getSubstrate(alice.address);
+    });
+
+
+    // Providing the relay currency to the unique sender account
+    // (fee for USDT XCM are paid in relay tokens)
+    await usingRelayPlaygrounds(relayUrl, async (helper) => {
+      const destination = {
+        V1: {
+          parents: 0,
+          interior: {X1: {
+            Parachain: UNIQUE_CHAIN,
+          },
+          },
+        }};
+
+      const beneficiary = {
+        V1: {
+          parents: 0,
+          interior: {X1: {
+            AccountId32: {
+              network: 'Any',
+              id: alice.addressRaw,
+            },
+          }},
+        },
+      };
+
+      const assets = {
+        V1: [
+          {
+            id: {
+              Concrete: {
+                parents: 0,
+                interior: 'Here',
+              },
+            },
+            fun: {
+              Fungible: TRANSFER_AMOUNT_RELAY,
+            },
+          },
+        ],
+      };
+
+      const feeAssetItem = 0;
+
+      await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, 'Unlimited');
+    });
+  
+  });
+
+  itSub('Should connect and send USDT from Statemint to Unique', async ({helper}) => {
+    await usingStatemintPlaygrounds(statemintUrl, async (helper) => {
+      const dest = {
+        V1: {
+          parents: 1,
+          interior: {X1: {
+            Parachain: UNIQUE_CHAIN,
+          },
+          },
+        }};
+
+      const beneficiary = {
+        V1: {
+          parents: 0,
+          interior: {X1: {
+            AccountId32: {
+              network: 'Any',
+              id: alice.addressRaw,
+            },
+          }},
+        },
+      };
+
+      const assets = {
+        V1: [
+          {
+            id: {
+              Concrete: {
+                parents: 0,
+                interior: {
+                  X2: [
+                    {
+                      PalletInstance: STATEMINT_PALLET_INSTANCE,
+                    },
+                    {
+                      GeneralIndex: USDT_ASSET_ID,
+                    }, 
+                  ]},
+              },
+            },
+            fun: {
+              Fungible: TRANSFER_AMOUNT,
+            },
+          },
+        ],
+      };
+
+      const feeAssetItem = 0;
+
+      balanceStmnBefore = await helper.balance.getSubstrate(alice.address);
+      await helper.xcm.limitedReserveTransferAssets(alice, dest, beneficiary, assets, feeAssetItem, 'Unlimited');
+
+      balanceStmnAfter = await helper.balance.getSubstrate(alice.address);
+
+      // common good parachain take commission in it native token
+      console.log(
+        '[Statemint -> Unique] transaction fees on Statemint: %s WND',
+        helper.util.bigIntToDecimals(balanceStmnBefore - balanceStmnAfter, STATEMINT_DECIMALS),
+      );
+      expect(balanceStmnBefore > balanceStmnAfter).to.be.true;
+
+    });
+
+
+    // ensure that asset has been delivered
+    await helper.wait.newBlocks(3);
+
+    // expext collection id will be with id 1
+    const free = await helper.ft.getBalance(1, {Substrate: alice.address});
+
+    balanceUniqueAfter = await helper.balance.getSubstrate(alice.address);
+
+    console.log(
+      '[Statemint -> Unique] transaction fees on Unique: %s USDT',
+      helper.util.bigIntToDecimals(TRANSFER_AMOUNT - free, USDT_ASSET_METADATA_DECIMALS),
+    );
+    console.log(
+      '[Statemint -> Unique] transaction fees on Unique: %s UNQ',
+      helper.util.bigIntToDecimals(balanceUniqueAfter - balanceUniqueBefore),
+    );    
+    // commission has not paid in USDT token
+    expect(free).to.be.equal(TRANSFER_AMOUNT);
+    // ... and parachain native token
+    expect(balanceUniqueAfter == balanceUniqueBefore).to.be.true;
+  });
+
+  itSub('Should connect and send USDT from Unique to Statemint back', async ({helper}) => {
+    const destination = {
+      V1: {
+        parents: 1,
+        interior: {X2: [
+          {
+            Parachain: STATEMINT_CHAIN,
+          },
+          {
+            AccountId32: {
+              network: 'Any',
+              id: alice.addressRaw,
+            },
+          },
+        ]},
+      },
+    };
+
+    const relayFee = 400_000_000_000_000n;
+    const currencies: [any, bigint][] = [
+      [
+        {
+          ForeignAssetId: 0,
+        },
+        TRANSFER_AMOUNT,
+      ], 
+      [
+        {
+          NativeAssetId: 'Parent',
+        },
+        relayFee,
+      ],
+    ];
+
+    const feeItem = 1;
+
+    await helper.xTokens.transferMulticurrencies(alice, currencies, feeItem, destination, 'Unlimited');
+    
+    // the commission has been paid in parachain native token
+    balanceUniqueFinal = await helper.balance.getSubstrate(alice.address);
+    console.log('[Unique -> Statemint] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(balanceUniqueFinal - balanceUniqueAfter));
+    expect(balanceUniqueAfter > balanceUniqueFinal).to.be.true;
+
+    await usingStatemintPlaygrounds(statemintUrl, async (helper) => {
+      await helper.wait.newBlocks(3);
+      
+      // The USDT token never paid fees. Its amount not changed from begin value.
+      // Also check that xcm transfer has been succeeded 
+      expect((await helper.assets.account(USDT_ASSET_ID, alice.address))! == USDT_ASSET_AMOUNT).to.be.true;
+    });
+  });
+
+  itSub('Should connect and send Relay token to Unique', async ({helper}) => {
+    balanceBobBefore = await helper.balance.getSubstrate(bob.address);
+    balanceBobRelayTokenBefore = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});
+
+    await usingRelayPlaygrounds(relayUrl, async (helper) => {
+      const destination = {
+        V1: {
+          parents: 0,
+          interior: {X1: {
+            Parachain: UNIQUE_CHAIN,
+          },
+          },
+        }};
+
+      const beneficiary = {
+        V1: {
+          parents: 0,
+          interior: {X1: {
+            AccountId32: {
+              network: 'Any',
+              id: bob.addressRaw,
+            },
+          }},
+        },
+      };
+
+      const assets = {
+        V1: [
+          {
+            id: {
+              Concrete: {
+                parents: 0,
+                interior: 'Here',
+              },
+            },
+            fun: {
+              Fungible: TRANSFER_AMOUNT_RELAY,
+            },
+          },
+        ],
+      };
+
+      const feeAssetItem = 0;
+
+      await helper.xcm.limitedReserveTransferAssets(bob, destination, beneficiary, assets, feeAssetItem, 'Unlimited');
+    });
+  
+    await helper.wait.newBlocks(3);
+
+    balanceBobAfter = await helper.balance.getSubstrate(bob.address);  
+    balanceBobRelayTokenAfter = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});
+
+    const wndFeeOnUnique = balanceBobRelayTokenAfter - TRANSFER_AMOUNT_RELAY - balanceBobRelayTokenBefore;
+    const wndDiffOnUnique = balanceBobRelayTokenAfter - balanceBobRelayTokenBefore;
+    console.log(
+      '[Relay (Westend) -> Unique] transaction fees: %s UNQ',
+      helper.util.bigIntToDecimals(balanceBobAfter - balanceBobBefore),
+    );
+    console.log(
+      '[Relay (Westend) -> Unique] transaction fees: %s WND',
+      helper.util.bigIntToDecimals(wndFeeOnUnique, STATEMINT_DECIMALS),
+    );
+    console.log('[Relay (Westend) -> Unique] actually delivered: %s WND', wndDiffOnUnique);
+    expect(wndFeeOnUnique == 0n, 'No incoming WND fees should be taken').to.be.true;
+    expect(balanceBobBefore == balanceBobAfter, 'No incoming UNQ fees should be taken').to.be.true;
+  });
+
+  itSub('Should connect and send Relay token back', async ({helper}) => {
+    let relayTokenBalanceBefore: bigint;
+    let relayTokenBalanceAfter: bigint;
+    await usingRelayPlaygrounds(relayUrl, async (helper) => {
+      relayTokenBalanceBefore = await helper.balance.getSubstrate(bob.address);
+    });
+
+    const destination = {
+      V1: {
+        parents: 1,
+        interior: {
+          X1:{
+            AccountId32: {
+              network: 'Any',
+              id: bob.addressRaw,
+            },
+          },
+        },
+      },
+    };
+
+    const currencies: any = [
+      [
+        {
+          NativeAssetId: 'Parent',
+        },
+        TRANSFER_AMOUNT_RELAY,
+      ],
+    ];
+
+    const feeItem = 0;
+
+    await helper.xTokens.transferMulticurrencies(bob, currencies, feeItem, destination, 'Unlimited');
+
+    balanceBobFinal = await helper.balance.getSubstrate(bob.address);
+    console.log('[Unique -> Relay (Westend)] transaction fees: %s UNQ',  helper.util.bigIntToDecimals(balanceBobAfter - balanceBobFinal));
+
+    await usingRelayPlaygrounds(relayUrl, async (helper) => {
+      await helper.wait.newBlocks(10);
+      relayTokenBalanceAfter = await helper.balance.getSubstrate(bob.address);
+
+      const diff = relayTokenBalanceAfter - relayTokenBalanceBefore;
+      console.log('[Unique -> Relay (Westend)] actually delivered: %s WND', helper.util.bigIntToDecimals(diff, RELAY_DECIMALS));
+      expect(diff > 0, 'Relay tokens was not delivered back').to.be.true;
+    });
+  });
+});
+
 describeXCM('[XCM] Integration test: Exchanging tokens with Acala', () => {
   let alice: IKeyringPair;
   let randomAccount: IKeyringPair;
@@ -124,15 +533,13 @@
     };
 
     const feeAssetItem = 0;
-    const weightLimit = 5000000000;
 
-    await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, weightLimit);
-
+    await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');
     balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);
 
     const unqFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;
     console.log('[Unique -> Acala] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));
-    expect(unqFees > 0n).to.be.true;
+    expect(unqFees > 0n, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;
 
     await usingAcalaPlaygrounds(acalaUrl, async (helper) => {
       await helper.wait.newBlocks(3);
@@ -176,10 +583,7 @@
         ForeignAsset: 0,
       };
 
-      const destWeight = 50000000;
-
-      await helper.xTokens.transfer(randomAccount, id, TRANSFER_AMOUNT, destination, destWeight);
-
+      await helper.xTokens.transfer(randomAccount, id, TRANSFER_AMOUNT, destination, 'Unlimited');
       balanceAcalaTokenFinal = await helper.balance.getSubstrate(randomAccount.address);
       balanceUniqueForeignTokenFinal = await helper.tokens.accounts(randomAccount.address, id);
 
@@ -192,7 +596,7 @@
       );
       console.log('[Acala -> Unique] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer));
 
-      expect(acaFees > 0).to.be.true;
+      expect(acaFees > 0, 'Negative fees ACA, looks like nothing was transferred').to.be.true;
       expect(unqOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;
     });
 
@@ -219,76 +623,7 @@
       alice = await privateKey('//Alice');
     });
   });
-
-  itSub('Unique rejects tokens from the Relay', async ({helper}) => {
-    await usingRelayPlaygrounds(relayUrl, async (helper) => {
-      const destination = {
-        V1: {
-          parents: 0,
-          interior: {X1: {
-            Parachain: UNIQUE_CHAIN,
-          },
-          },
-        }};
 
-      const beneficiary = {
-        V1: {
-          parents: 0,
-          interior: {X1: {
-            AccountId32: {
-              network: 'Any',
-              id: alice.addressRaw,
-            },
-          }},
-        },
-      };
-
-      const assets = {
-        V1: [
-          {
-            id: {
-              Concrete: {
-                parents: 0,
-                interior: 'Here',
-              },
-            },
-            fun: {
-              Fungible: 50_000_000_000_000_000n,
-            },
-          },
-        ],
-      };
-
-      const feeAssetItem = 0;
-      const weightLimit = 5_000_000_000;
-
-      await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, weightLimit);
-    });
-
-    const maxWaitBlocks = 3;
-
-    const dmpQueueExecutedDownward = await helper.wait.event(maxWaitBlocks, 'dmpQueue', 'ExecutedDownward');
-
-    expect(
-      dmpQueueExecutedDownward != null,
-      '[Relay] dmpQueue.ExecutedDownward event is expected',
-    ).to.be.true;
-
-    const event = dmpQueueExecutedDownward!.event;
-    const outcome = event.data[1] as XcmV2TraitsOutcome;
-
-    expect(
-      outcome.isIncomplete,
-      '[Relay] The outcome of the XCM should be `Incomplete`',
-    ).to.be.true;
-
-    const incomplete = outcome.asIncomplete;
-    expect(
-      incomplete[1].toString() == 'AssetNotFound',
-      '[Relay] The XCM error should be `AssetNotFound`',
-    ).to.be.true;
-  });
-
   itSub('Unique rejects ACA tokens from Acala', async ({helper}) => {
     await usingAcalaPlaygrounds(acalaUrl, async (helper) => {
       const destination = {
@@ -312,9 +647,7 @@
         Token: 'ACA',
       };
 
-      const destWeight = 50000000;
-
-      await helper.xTokens.transfer(alice, id, 100_000_000_000n, destination, destWeight);
+      await helper.xTokens.transfer(alice, id, 100_000_000_000n, destination, 'Unlimited');
     });
 
     const maxWaitBlocks = 3;
@@ -330,8 +663,8 @@
     const outcome = event.data[1] as XcmV2TraitsError;
 
     expect(
-      outcome.isUntrustedReserveLocation,
-      '[Acala] The XCM error should be `UntrustedReserveLocation`',
+      outcome.isFailedToTransactAsset,
+      '[Acala] The XCM error should be `FailedToTransactAsset`',
     ).to.be.true;
   });
 });
@@ -508,16 +841,15 @@
       },
     };
     const amount = TRANSFER_AMOUNT;
-    const destWeight = 850000000;
 
-    await helper.xTokens.transfer(randomAccountUnique, currencyId, amount, dest, destWeight);
+    await helper.xTokens.transfer(randomAccountUnique, currencyId, amount, dest, 'Unlimited');
 
     balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccountUnique.address);
     expect(balanceUniqueTokenMiddle < balanceUniqueTokenInit).to.be.true;
 
     const transactionFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;
     console.log('[Unique -> Moonbeam] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(transactionFees));
-    expect(transactionFees > 0).to.be.true;
+    expect(transactionFees > 0, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;
 
     await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {
       await helper.wait.newBlocks(3);
@@ -572,7 +904,7 @@
 
       const glmrFees = balanceGlmrTokenMiddle - balanceGlmrTokenFinal;
       console.log('[Moonbeam -> Unique] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));
-      expect(glmrFees > 0).to.be.true;
+      expect(glmrFees > 0, 'Negative fees GLMR, looks like nothing was transferred').to.be.true;
 
       const unqRandomAccountAsset = await helper.assets.account(assetId, randomAccountMoonbeam.address);