git.delta.rocks / unique-network / refs/commits / 5bde44b9e4f5

difftreelog

feat draft xcm deposit_asset

Daniel Shiposha2023-10-17parent: #c652c1e.patch.diff
in: master

8 files changed

modifiedpallets/balances-adapter/src/common.rsdiffbeforeafterboth
--- a/pallets/balances-adapter/src/common.rs
+++ b/pallets/balances-adapter/src/common.rs
@@ -1,10 +1,16 @@
 use alloc::{vec, vec::Vec};
 use core::marker::PhantomData;
 
-use frame_support::{ensure, fail, weights::Weight};
+use frame_support::{
+	ensure, fail,
+	traits::tokens::{fungible::Mutate, Fortitude, Precision},
+	weights::Weight,
+};
 use pallet_balances::{weights::SubstrateWeight as BalancesWeight, WeightInfo};
-use pallet_common::{CommonCollectionOperations, CommonWeightInfo, Error as CommonError};
-use up_data_structs::TokenId;
+use pallet_common::{
+	erc::CrossAccountId, CommonCollectionOperations, CommonWeightInfo, Error as CommonError,
+};
+use up_data_structs::{budget::Budget, TokenId};
 
 use crate::{Config, NativeFungibleHandle, Pallet};
 
@@ -332,6 +338,10 @@
 		0
 	}
 
+	fn xcm_extensions(&self) -> Option<&dyn pallet_common::XcmExtensions<T>> {
+		Some(self)
+	}
+
 	fn set_allowance_for_all(
 		&self,
 		_owner: <T>::CrossAccountId,
@@ -356,3 +366,76 @@
 		fail!(<CommonError<T>>::UnsupportedOperation);
 	}
 }
+
+impl<T: Config> pallet_common::XcmExtensions<T> for NativeFungibleHandle<T> {
+	fn is_foreign(&self) -> bool {
+		false
+	}
+
+	fn create_item_internal(
+		&self,
+		_depositor: &<T>::CrossAccountId,
+		to: <T>::CrossAccountId,
+		data: up_data_structs::CreateItemData,
+		_nesting_budget: &dyn Budget,
+	) -> Result<TokenId, sp_runtime::DispatchError> {
+		match &data {
+			up_data_structs::CreateItemData::Fungible(fungible_data) => {
+				T::Mutate::mint_into(
+					to.as_sub(),
+					fungible_data
+						.value
+						.try_into()
+						.map_err(|_| sp_runtime::ArithmeticError::Overflow)?,
+				)?;
+
+				Ok(TokenId::default())
+			}
+			_ => {
+				fail!(<CommonError<T>>::NotFungibleDataUsedToMintFungibleCollectionToken)
+			}
+		}
+	}
+
+	fn transfer_item_internal(
+		&self,
+		_depositor: &<T>::CrossAccountId,
+		from: &<T>::CrossAccountId,
+		to: &<T>::CrossAccountId,
+		token: TokenId,
+		amount: u128,
+		_nesting_budget: &dyn Budget,
+	) -> sp_runtime::DispatchResult {
+		ensure!(
+			token == TokenId::default(),
+			<CommonError<T>>::FungibleItemsHaveNoId
+		);
+
+		<Pallet<T>>::transfer(from, to, amount)
+			.map(|_| ())
+			.map_err(|post_info| post_info.error)
+	}
+
+	fn burn_item_internal(
+		&self,
+		from: T::CrossAccountId,
+		token: TokenId,
+		amount: u128,
+	) -> sp_runtime::DispatchResult {
+		ensure!(
+			token == TokenId::default(),
+			<CommonError<T>>::FungibleItemsHaveNoId
+		);
+
+		T::Mutate::burn_from(
+			from.as_sub(),
+			amount
+				.try_into()
+				.map_err(|_| sp_runtime::ArithmeticError::Overflow)?,
+			Precision::Exact,
+			Fortitude::Polite,
+		)?;
+
+		Ok(())
+	}
+}
modifiedpallets/common/src/lib.rsdiffbeforeafterboth
--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -80,15 +80,16 @@
 use sp_std::vec::Vec;
 use sp_weights::Weight;
 use up_data_structs::{
-	budget::Budget, AccessMode, Collection, CollectionId, CollectionLimits, CollectionMode,
-	CollectionPermissions, CollectionProperties as CollectionPropertiesT, CollectionStats,
-	CreateCollectionData, CreateItemData, CreateItemExData, PhantomType, PropertiesError,
-	PropertiesPermissionMap, Property, PropertyKey, PropertyKeyPermission, PropertyPermission,
-	PropertyScope, PropertyValue, RpcCollection, RpcCollectionFlags, SponsoringRateLimit,
-	SponsorshipState, TokenChild, TokenData, TokenId, TokenOwnerError, TokenProperties,
-	TrySetProperty, COLLECTION_ADMINS_LIMIT, COLLECTION_NUMBER_LIMIT, CUSTOM_DATA_LIMIT,
-	FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, MAX_SPONSOR_TIMEOUT, MAX_TOKEN_OWNERSHIP,
-	MAX_TOKEN_PREFIX_LENGTH, NFT_SPONSOR_TRANSFER_TIMEOUT, REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
+	budget::Budget, mapping::TokenAddressMapping, AccessMode, Collection, CollectionId,
+	CollectionLimits, CollectionMode, CollectionPermissions,
+	CollectionProperties as CollectionPropertiesT, CollectionStats, CreateCollectionData,
+	CreateItemData, CreateItemExData, PhantomType, PropertiesError, PropertiesPermissionMap,
+	Property, PropertyKey, PropertyKeyPermission, PropertyPermission, PropertyScope, PropertyValue,
+	RpcCollection, RpcCollectionFlags, SponsoringRateLimit, SponsorshipState, TokenChild,
+	TokenData, TokenId, TokenOwnerError, TokenProperties, TrySetProperty, COLLECTION_ADMINS_LIMIT,
+	COLLECTION_NUMBER_LIMIT, CUSTOM_DATA_LIMIT, FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
+	MAX_SPONSOR_TIMEOUT, MAX_TOKEN_OWNERSHIP, MAX_TOKEN_PREFIX_LENGTH,
+	NFT_SPONSOR_TRANSFER_TIMEOUT, REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
 };
 use up_pov_estimate_rpc::PovInfo;
 
@@ -786,6 +787,9 @@
 
 		/// Fungible tokens hold no ID, and the default value of TokenId for a fungible collection is 0.
 		FungibleItemsHaveNoId,
+
+		/// Not Fungible item data used to mint in Fungible collection.
+		NotFungibleDataUsedToMintFungibleCollectionToken,
 	}
 
 	/// Storage of the count of created collections. Essentially contains the last collection ID.
@@ -2347,24 +2351,77 @@
 	/// Is the collection a foreign one?
 	fn is_foreign(&self) -> bool;
 
-	/// Create a collection's item.
+	/// Create a collection's item using a transaction.
+	///
+	/// This function performs additional XCM-related checks before the actual creation.
+	#[transactional]
 	fn create_item(
 		&self,
+		depositor: &T::CrossAccountId,
+		to: T::CrossAccountId,
+		data: CreateItemData,
+		nesting_budget: &dyn Budget,
+	) -> Result<TokenId, DispatchError> {
+		if T::CrossTokenAddressMapping::is_token_address(&to) {
+			return unsupported!(T);
+		}
+
+		self.create_item_internal(depositor, to, data, nesting_budget)
+	}
+
+	/// Create a collection's item.
+	fn create_item_internal(
+		&self,
+		depositor: &T::CrossAccountId,
 		to: T::CrossAccountId,
 		data: CreateItemData,
+		nesting_budget: &dyn Budget,
 	) -> Result<TokenId, DispatchError>;
 
+	/// Transfer an item from the `from` account to the `to` account using a transaction.
+	///
+	/// This function performs additional XCM-related checks before the actual transfer.
+	#[transactional]
+	fn transfer_item(
+		&self,
+		depositor: &T::CrossAccountId,
+		from: &T::CrossAccountId,
+		to: &T::CrossAccountId,
+		token: TokenId,
+		amount: u128,
+		nesting_budget: &dyn Budget,
+	) -> DispatchResult {
+		if T::CrossTokenAddressMapping::is_token_address(&to) {
+			return unsupported!(T);
+		}
+
+		self.transfer_item_internal(depositor, from, to, token, amount, nesting_budget)
+	}
+
 	/// Transfer an item from the `from` account to the `to` account.
-	fn transfer(
+	fn transfer_item_internal(
 		&self,
-		from: T::CrossAccountId,
-		to: T::CrossAccountId,
+		depositor: &T::CrossAccountId,
+		from: &T::CrossAccountId,
+		to: &T::CrossAccountId,
 		token: TokenId,
 		amount: u128,
+		nesting_budget: &dyn Budget,
 	) -> DispatchResult;
 
+	/// Burn a collection's item using a transaction.
+	#[transactional]
+	fn burn_item(&self, from: T::CrossAccountId, token: TokenId, amount: u128) -> DispatchResult {
+		self.burn_item_internal(from, token, amount)
+	}
+
 	/// Burn a collection's item.
-	fn burn(&self, from: T::CrossAccountId, token: TokenId, amount: u128) -> DispatchResult;
+	fn burn_item_internal(
+		&self,
+		from: T::CrossAccountId,
+		token: TokenId,
+		amount: u128,
+	) -> DispatchResult;
 }
 
 /// Merge [`DispatchResult`] with [`Weight`] into [`DispatchResultWithPostInfo`].
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::{dispatch::DispatchResult, pallet_prelude::*, traits::EnsureOrigin, PalletId};38use frame_system::pallet_prelude::*;39use pallet_common::{40	dispatch::CollectionDispatch, erc::CrossAccountId, NATIVE_FUNGIBLE_COLLECTION_ID,41};42use sp_runtime::traits::AccountIdConversion;43use sp_std::{vec, vec::Vec};44// NOTE: MultiLocation is used in storages, we will need to do migration if upgrade the45// MultiLocation to the XCM v3.46use staging_xcm::{47	opaque::latest::{prelude::XcmError, Weight},48	v3::{prelude::*, MultiAsset, XcmContext},49};50use staging_xcm_executor::{51	traits::{TransactAsset, WeightTrader},52	Assets,53};54use up_data_structs::{55	CollectionId, CollectionMode, CollectionName, CreateCollectionData, PropertyKey, TokenId,56};5758pub mod weights;5960#[cfg(feature = "runtime-benchmarks")]61mod benchmarking;6263pub use module::*;64pub use weights::WeightInfo;6566#[frame_support::pallet]67pub mod module {68	use up_data_structs::{69		CollectionDescription, Property, PropertyKeyPermission, PropertyPermission,70	};7172	use super::*;7374	#[pallet::config]75	pub trait Config:76		frame_system::Config77		+ pallet_common::Config78		+ pallet_fungible::Config79		+ pallet_balances::Config80	{81		/// The overarching event type.82		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;8384		/// Origin for force registering of a foreign asset.85		type ForceRegisterOrigin: EnsureOrigin<Self::RuntimeOrigin>;8687		/// The ID of the foreign assets pallet.88		type PalletId: Get<PalletId>;8990		/// Weight information for the extrinsics in this module.91		type WeightInfo: WeightInfo;92	}9394	#[pallet::error]95	pub enum Error<T> {96		/// The foreign asset is already registered97		ForeignAssetAlreadyRegistered,98	}99100	#[pallet::event]101	#[pallet::generate_deposit(fn deposit_event)]102	pub enum Event<T: Config> {103		/// The foreign asset registered.104		ForeignAssetRegistered {105			asset_id: CollectionId,106			reserve_location: MultiLocation,107		},108	}109110	/// The corresponding collections of reserve locations.111	#[pallet::storage]112	#[pallet::getter(fn foreign_reserve_location_to_collection)]113	pub type ForeignReserveLocationToCollection<T: Config> =114		StorageMap<_, Twox64Concat, staging_xcm::v3::MultiLocation, CollectionId, OptionQuery>;115116	/// The correponding NFT token id of reserve NFTs117	#[pallet::storage]118	#[pallet::getter(fn foreign_reserve_asset_instance_to_token_id)]119	pub type ForeignReserveAssetInstanceToTokenId<T: Config> = StorageDoubleMap<120		Hasher1 = Twox64Concat,121		Key1 = CollectionId,122		Hasher2 = Twox64Concat,123		Key2 = staging_xcm::v3::AssetInstance,124		Value = TokenId,125		QueryKind = OptionQuery,126	>;127128	#[pallet::pallet]129	pub struct Pallet<T>(_);130131	#[pallet::call]132	impl<T: Config> Pallet<T> {133		#[pallet::call_index(0)]134		#[pallet::weight(<T as Config>::WeightInfo::register_foreign_asset())]135		pub fn force_register_foreign_asset(136			origin: OriginFor<T>,137			reserve_location: MultiLocation,138			name: CollectionName,139			mode: CollectionMode,140		) -> DispatchResult {141			T::ForceRegisterOrigin::ensure_origin(origin.clone())?;142143			let foreign_collection_owner = Self::pallet_account();144145			let description: CollectionDescription = "Foreign Assets Collection"146				.encode_utf16()147				.collect::<Vec<_>>()148				.try_into()149				.expect("description length < max description length; qed");150151			let collection_id = T::CollectionDispatch::create_foreign(152				foreign_collection_owner,153				CreateCollectionData {154					name,155					description,156					mode,157158					properties: vec![Property {159						key: Self::reserve_location_property_key(),160						value: reserve_location161							.encode()162							.try_into()163							.expect("multilocation is less than 32k; qed"),164					}]165					.try_into()166					.expect("just one property can always be stored; qed"),167168					token_property_permissions: vec![PropertyKeyPermission {169						key: Self::reserve_asset_instance_property_key(),170						permission: PropertyPermission {171							mutable: false,172							collection_admin: true,173							token_owner: false,174						},175					}]176					.try_into()177					.expect("just one property permission can always be stored; qed"),178					..Default::default()179				},180			)?;181182			<ForeignReserveLocationToCollection<T>>::insert(reserve_location, collection_id);183184			Self::deposit_event(Event::<T>::ForeignAssetRegistered {185				asset_id: collection_id,186				reserve_location,187			});188189			Ok(())190		}191	}192}193194impl<T: Config> Pallet<T> {195	fn pallet_account() -> T::CrossAccountId {196		let owner: T::AccountId = T::PalletId::get().into_account_truncating();197		T::CrossAccountId::from_sub(owner)198	}199200	fn reserve_location_property_key() -> PropertyKey {201		b"reserve-location"202			.to_vec()203			.try_into()204			.expect("key length < max property key length; qed")205	}206207	fn reserve_asset_instance_property_key() -> PropertyKey {208		b"reserve-asset-instance"209			.to_vec()210			.try_into()211			.expect("key length < max property key length; qed")212	}213}214215impl<T: Config> TransactAsset for Pallet<T> {216	fn can_check_in(217		_origin: &MultiLocation,218		_what: &MultiAsset,219		_context: &XcmContext,220	) -> XcmResult {221		Err(XcmError::Unimplemented)222	}223224	fn check_in(_origin: &MultiLocation, _what: &MultiAsset, _context: &XcmContext) {}225226	fn can_check_out(227		_dest: &MultiLocation,228		_what: &MultiAsset,229		_context: &XcmContext,230	) -> XcmResult {231		Err(XcmError::Unimplemented)232	}233234	fn check_out(_dest: &MultiLocation, _what: &MultiAsset, _context: &XcmContext) {}235236	fn deposit_asset(what: &MultiAsset, to: &MultiLocation, context: &XcmContext) -> XcmResult {237		Err(XcmError::Unimplemented)238	}239240	fn withdraw_asset(241		what: &MultiAsset,242		from: &MultiLocation,243		_maybe_context: Option<&XcmContext>,244	) -> Result<staging_xcm_executor::Assets, XcmError> {245		Err(XcmError::Unimplemented)246	}247248	fn internal_transfer_asset(249		what: &MultiAsset,250		from: &MultiLocation,251		to: &MultiLocation,252		_context: &XcmContext,253	) -> Result<staging_xcm_executor::Assets, XcmError> {254		Err(XcmError::Unimplemented)255	}256}257258pub struct CurrencyIdConvert<T: Config>(PhantomData<T>);259impl<T: Config> sp_runtime::traits::Convert<CollectionId, Option<MultiLocation>>260	for CurrencyIdConvert<T>261{262	fn convert(collection_id: CollectionId) -> Option<MultiLocation> {263		if collection_id == NATIVE_FUNGIBLE_COLLECTION_ID {264			Some(Here.into())265		} else {266			// let dispatch = T::CollectionDispatch::dispatch(collection_id).ok()?;267			// let collection = dispatch.as_dyn();268			// let xcm_ext = collection.xcm_extensions()?;269270			// if xcm_ext.is_foreign() {271			// 	let encoded_location =272			// 		collection.property(&<Pallet<T>>::reserve_location_property_key())?;273			// 	MultiLocation::decode(&mut &encoded_location[..]).ok()274			// } else {275			// 	T::SelfLocation::get()276			// 		.pushed_with_interior(GeneralIndex(collection_id.0.into()))277			// 		.ok()278			// }279			todo!()280		}281	}282}283284pub use frame_support::{285	traits::{286		fungibles::Balanced, tokens::currency::Currency as CurrencyT, OnUnbalanced as OnUnbalancedT,287	},288	weights::{WeightToFee, WeightToFeePolynomial},289};290291pub struct FreeForAll;292293impl WeightTrader for FreeForAll {294	fn new() -> Self {295		Self296	}297298	fn buy_weight(299		&mut self,300		weight: Weight,301		payment: Assets,302		_xcm: &XcmContext,303	) -> Result<Assets, XcmError> {304		log::trace!(target: "fassets::weight", "buy_weight weight: {:?}, payment: {:?}", weight, payment);305		Ok(payment)306	}307}
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//! Under construction2627#![cfg_attr(not(feature = "std"), no_std)]28#![allow(clippy::unused_unit)]2930use frame_support::{dispatch::DispatchResult, pallet_prelude::*, traits::EnsureOrigin, PalletId};31use frame_system::pallet_prelude::*;32use pallet_common::{33	dispatch::CollectionDispatch, erc::CrossAccountId, XcmExtensions, NATIVE_FUNGIBLE_COLLECTION_ID,34};35use sp_runtime::traits::AccountIdConversion;36use sp_std::{vec, vec::Vec};37use staging_xcm::{38	opaque::latest::{prelude::XcmError, Weight},39	v3::{prelude::*, MultiAsset, XcmContext},40};41use staging_xcm_executor::{42	traits::{ConvertLocation, Error as XcmExecutorError, TransactAsset, WeightTrader},43	Assets,44};45use up_data_structs::{46	budget::ZeroBudget, CollectionId, CollectionMode, CollectionName, CreateCollectionData,47	CreateFungibleData, CreateItemData, CreateNftData, Property, PropertyKey, TokenId,48};4950pub mod weights;5152#[cfg(feature = "runtime-benchmarks")]53mod benchmarking;5455pub use module::*;56pub use weights::WeightInfo;5758#[frame_support::pallet]59pub mod module {60	use up_data_structs::{61		CollectionDescription, Property, PropertyKeyPermission, PropertyPermission,62	};6364	use super::*;6566	#[pallet::config]67	pub trait Config:68		frame_system::Config69		+ pallet_common::Config70		+ pallet_fungible::Config71		+ pallet_balances::Config72	{73		/// The overarching event type.74		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;7576		/// Origin for force registering of a foreign asset.77		type ForceRegisterOrigin: EnsureOrigin<Self::RuntimeOrigin>;7879		/// The ID of the foreign assets pallet.80		type PalletId: Get<PalletId>;8182		/// Self-location of this parachain.83		type SelfLocation: Get<MultiLocation>;8485		/// The converter from a MultiLocation to a CrossAccountId.86		type LocationToAccountId: ConvertLocation<Self::CrossAccountId>;8788		/// Weight information for the extrinsics in this module.89		type WeightInfo: WeightInfo;90	}9192	#[pallet::error]93	pub enum Error<T> {94		/// The foreign asset is already registered95		ForeignAssetAlreadyRegistered,96	}9798	#[pallet::event]99	#[pallet::generate_deposit(fn deposit_event)]100	pub enum Event<T: Config> {101		/// The foreign asset registered.102		ForeignAssetRegistered {103			asset_id: CollectionId,104			reserve_location: MultiLocation,105		},106	}107108	/// The corresponding collections of reserve locations.109	#[pallet::storage]110	#[pallet::getter(fn foreign_reserve_location_to_collection)]111	pub type ForeignReserveLocationToCollection<T: Config> =112		StorageMap<_, Twox64Concat, staging_xcm::v3::MultiLocation, CollectionId, OptionQuery>;113114	/// The corresponding reserve location of collections.115	#[pallet::storage]116	#[pallet::getter(fn collection_to_foreign_reserve_location)]117	pub type CollectionToForeignReserveLocation<T: Config> =118		StorageMap<_, Twox64Concat, CollectionId, staging_xcm::v3::MultiLocation, OptionQuery>;119120	/// The correponding NFT token id of reserve NFTs121	#[pallet::storage]122	#[pallet::getter(fn foreign_reserve_asset_instance_to_token_id)]123	pub type ForeignReserveAssetInstanceToTokenId<T: Config> = StorageDoubleMap<124		Hasher1 = Twox64Concat,125		Key1 = CollectionId,126		Hasher2 = Twox64Concat,127		Key2 = staging_xcm::v3::AssetInstance,128		Value = TokenId,129		QueryKind = OptionQuery,130	>;131132	#[pallet::pallet]133	pub struct Pallet<T>(_);134135	#[pallet::call]136	impl<T: Config> Pallet<T> {137		#[pallet::call_index(0)]138		#[pallet::weight(<T as Config>::WeightInfo::register_foreign_asset())]139		pub fn force_register_foreign_asset(140			origin: OriginFor<T>,141			reserve_location: MultiLocation,142			name: CollectionName,143			mode: CollectionMode,144		) -> DispatchResult {145			T::ForceRegisterOrigin::ensure_origin(origin.clone())?;146147			let foreign_collection_owner = Self::pallet_account();148149			let description: CollectionDescription = "Foreign Assets Collection"150				.encode_utf16()151				.collect::<Vec<_>>()152				.try_into()153				.expect("description length < max description length; qed");154155			let collection_id = T::CollectionDispatch::create_foreign(156				foreign_collection_owner,157				CreateCollectionData {158					name,159					description,160					mode,161162					properties: vec![Property {163						key: Self::reserve_location_property_key(),164						value: reserve_location165							.encode()166							.try_into()167							.expect("multilocation is less than 32k; qed"),168					}]169					.try_into()170					.expect("just one property can always be stored; qed"),171172					token_property_permissions: vec![PropertyKeyPermission {173						key: Self::reserve_asset_instance_property_key(),174						permission: PropertyPermission {175							mutable: false,176							collection_admin: true,177							token_owner: false,178						},179					}]180					.try_into()181					.expect("just one property permission can always be stored; qed"),182					..Default::default()183				},184			)?;185186			<ForeignReserveLocationToCollection<T>>::insert(reserve_location, collection_id);187			<CollectionToForeignReserveLocation<T>>::insert(collection_id, reserve_location);188189			Self::deposit_event(Event::<T>::ForeignAssetRegistered {190				asset_id: collection_id,191				reserve_location,192			});193194			Ok(())195		}196	}197}198199impl<T: Config> Pallet<T> {200	fn pallet_account() -> T::CrossAccountId {201		let owner: T::AccountId = T::PalletId::get().into_account_truncating();202		T::CrossAccountId::from_sub(owner)203	}204205	fn reserve_location_property_key() -> PropertyKey {206		b"reserve-location"207			.to_vec()208			.try_into()209			.expect("key length < max property key length; qed")210	}211212	fn reserve_asset_instance_property_key() -> PropertyKey {213		b"reserve-asset-instance"214			.to_vec()215			.try_into()216			.expect("key length < max property key length; qed")217	}218219	fn native_asset_location_to_collection(220		asset_location: &MultiLocation,221	) -> Result<Option<CollectionId>, XcmError> {222		let self_location = T::SelfLocation::get();223224		if *asset_location == Here.into() {225			Ok(Some(NATIVE_FUNGIBLE_COLLECTION_ID))226		} else if *asset_location == self_location {227			Ok(Some(NATIVE_FUNGIBLE_COLLECTION_ID))228		} else if asset_location.parents == self_location.parents {229			match asset_location230				.interior231				.match_and_split(&self_location.interior)232			{233				Some(GeneralIndex(collection_id)) => Ok(Some(CollectionId(234					(*collection_id)235						.try_into()236						.map_err(|_| XcmExecutorError::AssetIdConversionFailed)?,237				))),238				_ => Ok(None),239			}240		} else {241			Ok(None)242		}243	}244245	fn multiasset_to_collection(asset: &MultiAsset) -> Result<CollectionId, XcmError> {246		let AssetId::Concrete(asset_reserve_location) = asset.id else {247			return Err(XcmExecutorError::AssetNotHandled.into());248		};249250		Self::native_asset_location_to_collection(&asset_reserve_location)?251			.or_else(|| Self::foreign_reserve_location_to_collection(asset_reserve_location))252			.ok_or_else(|| XcmExecutorError::AssetIdConversionFailed.into())253	}254255	fn native_asset_instance_to_token_id(256		asset_instance: &AssetInstance,257	) -> Result<TokenId, XcmError> {258		match asset_instance {259			AssetInstance::Index(token_id) => Ok(TokenId(260				(*token_id)261					.try_into()262					.map_err(|_| XcmError::AssetNotFound)?,263			)),264			_ => Err(XcmError::AssetNotFound),265		}266	}267268	/// Obtains the token id of the `asset_instance` in the collection.269	///270	/// Returns `Ok(None)` only if the `asset_instance` points to a foreign item271	/// and it haven't been created on this blockchain yet.272	///273	/// If the `asset_instance` points to a native item, it cannot return `Ok(None)`.274	fn asset_instance_to_token_id(275		xcm_ext: &dyn XcmExtensions<T>,276		collection_id: CollectionId,277		asset_instance: &AssetInstance,278	) -> Result<Option<TokenId>, XcmError> {279		if xcm_ext.is_foreign() {280			Ok(Self::foreign_reserve_asset_instance_to_token_id(281				collection_id,282				asset_instance,283			))284		} else {285			Self::native_asset_instance_to_token_id(asset_instance).map(Some)286		}287	}288289	fn create_foreign_asset_instance(290		xcm_ext: &dyn XcmExtensions<T>,291		collection_id: CollectionId,292		asset_instance: &AssetInstance,293		to: T::CrossAccountId,294	) -> XcmResult {295		let asset_instance_encoded = asset_instance.encode();296297		let derivative_token_id = xcm_ext298			.create_item(299				&Self::pallet_account(),300				to,301				CreateItemData::NFT(CreateNftData {302					properties: vec![Property {303					key: Self::reserve_asset_instance_property_key(),304					value: asset_instance_encoded305						.try_into()306						.expect("asset instance length <= 32 bytes which is less than value length limit; qed"),307				}]308					.try_into()309					.expect("just one property can always be stored; qed"),310				}),311				&ZeroBudget,312			)313			.map_err(|_| XcmError::FailedToTransactAsset("non-fungible item deposit failed"))?;314315		<ForeignReserveAssetInstanceToTokenId<T>>::insert(316			collection_id,317			asset_instance,318			derivative_token_id,319		);320321		Ok(())322	}323324	fn deposit_asset_instance(325		xcm_ext: &dyn XcmExtensions<T>,326		collection_id: CollectionId,327		to: T::CrossAccountId,328		asset_instance: &AssetInstance,329	) -> XcmResult {330		if let Some(token_id) =331			Self::asset_instance_to_token_id(xcm_ext, collection_id, asset_instance)?332		{333			let depositor = &Self::pallet_account();334			let from = depositor;335			let amount = 1;336337			xcm_ext338				.transfer_item(depositor, from, &to, token_id, amount, &ZeroBudget)339				.map_err(|_| XcmError::FailedToTransactAsset("non-fungible item deposit failed"))340		} else {341			Self::create_foreign_asset_instance(xcm_ext, collection_id, asset_instance, to)342		}343	}344}345346impl<T: Config> TransactAsset for Pallet<T> {347	fn can_check_in(348		_origin: &MultiLocation,349		_what: &MultiAsset,350		_context: &XcmContext,351	) -> XcmResult {352		Err(XcmError::Unimplemented)353	}354355	fn check_in(_origin: &MultiLocation, _what: &MultiAsset, _context: &XcmContext) {}356357	fn can_check_out(358		_dest: &MultiLocation,359		_what: &MultiAsset,360		_context: &XcmContext,361	) -> XcmResult {362		Err(XcmError::Unimplemented)363	}364365	fn check_out(_dest: &MultiLocation, _what: &MultiAsset, _context: &XcmContext) {}366367	fn deposit_asset(what: &MultiAsset, to: &MultiLocation, context: &XcmContext) -> XcmResult {368		let collection_id = Self::multiasset_to_collection(what)?;369		let dispatch =370			T::CollectionDispatch::dispatch(collection_id).map_err(|_| XcmError::AssetNotFound)?;371372		let collection = dispatch.as_dyn();373		let xcm_ext = collection.xcm_extensions().ok_or(XcmError::Unimplemented)?;374375		let to = T::LocationToAccountId::convert_location(to)376			.ok_or(XcmExecutorError::AccountIdConversionFailed)?;377378		match what.fun {379			Fungibility::Fungible(amount) => xcm_ext380				.create_item(381					&Self::pallet_account(),382					to,383					CreateItemData::Fungible(CreateFungibleData { value: amount }),384					&ZeroBudget,385				)386				.map(|_| ())387				.map_err(|_| XcmError::FailedToTransactAsset("fungible item deposit failed")),388389			Fungibility::NonFungible(asset_instance) => {390				Self::deposit_asset_instance(xcm_ext, collection_id, to, &asset_instance)391			}392		}393	}394395	fn withdraw_asset(396		what: &MultiAsset,397		from: &MultiLocation,398		_maybe_context: Option<&XcmContext>,399	) -> Result<staging_xcm_executor::Assets, XcmError> {400		Err(XcmError::Unimplemented)401	}402403	fn internal_transfer_asset(404		what: &MultiAsset,405		from: &MultiLocation,406		to: &MultiLocation,407		_context: &XcmContext,408	) -> Result<staging_xcm_executor::Assets, XcmError> {409		Err(XcmError::Unimplemented)410	}411}412413pub struct CurrencyIdConvert<T: Config>(PhantomData<T>);414impl<T: Config> sp_runtime::traits::Convert<CollectionId, Option<MultiLocation>>415	for CurrencyIdConvert<T>416{417	fn convert(collection_id: CollectionId) -> Option<MultiLocation> {418		if collection_id == NATIVE_FUNGIBLE_COLLECTION_ID {419			Some(Here.into())420		} else {421			let dispatch = T::CollectionDispatch::dispatch(collection_id).ok()?;422			let collection = dispatch.as_dyn();423			let xcm_ext = collection.xcm_extensions()?;424425			if xcm_ext.is_foreign() {426				<Pallet<T>>::collection_to_foreign_reserve_location(collection_id)427			} else {428				T::SelfLocation::get()429					.pushed_with_interior(GeneralIndex(collection_id.0.into()))430					.ok()431			}432		}433	}434}435436pub use frame_support::{437	traits::{438		fungibles::Balanced, tokens::currency::Currency as CurrencyT, OnUnbalanced as OnUnbalancedT,439	},440	weights::{WeightToFee, WeightToFeePolynomial},441};442443pub struct FreeForAll;444445impl WeightTrader for FreeForAll {446	fn new() -> Self {447		Self448	}449450	fn buy_weight(451		&mut self,452		weight: Weight,453		payment: Assets,454		_xcm: &XcmContext,455	) -> Result<Assets, XcmError> {456		log::trace!(target: "fassets::weight", "buy_weight weight: {:?}, payment: {:?}", weight, payment);457		Ok(payment)458	}459}
modifiedpallets/fungible/src/common.rsdiffbeforeafterboth
--- a/pallets/fungible/src/common.rs
+++ b/pallets/fungible/src/common.rs
@@ -19,7 +19,7 @@
 use frame_support::{dispatch::DispatchResultWithPostInfo, ensure, fail, weights::Weight};
 use pallet_common::{
 	weights::WeightInfo as _, with_weight, CommonCollectionOperations, CommonWeightInfo,
-	Error as CommonError, RefungibleExtensions, SelfWeightOf as PalletCommonWeightOf,
+	Error as CommonError, SelfWeightOf as PalletCommonWeightOf, XcmExtensions,
 };
 use sp_runtime::{ArithmeticError, DispatchError};
 use sp_std::{vec, vec::Vec};
@@ -114,7 +114,7 @@
 				<Pallet<T>>::create_item(self, &sender, (to, fungible_data.value), nesting_budget),
 				<CommonWeights<T>>::create_item(&data),
 			),
-			_ => fail!(<Error<T>>::NotFungibleDataUsedToMintFungibleCollectionToken),
+			_ => fail!(<CommonError<T>>::NotFungibleDataUsedToMintFungibleCollectionToken),
 		}
 	}
 
@@ -133,7 +133,7 @@
 						.checked_add(data.value)
 						.ok_or(ArithmeticError::Overflow)?;
 				}
-				_ => fail!(<Error<T>>::NotFungibleDataUsedToMintFungibleCollectionToken),
+				_ => fail!(<CommonError<T>>::NotFungibleDataUsedToMintFungibleCollectionToken),
 			}
 		}
 
@@ -152,7 +152,7 @@
 		let weight = <CommonWeights<T>>::create_multiple_items_ex(&data);
 		let data = match data {
 			up_data_structs::CreateItemExData::Fungible(f) => f,
-			_ => fail!(<Error<T>>::NotFungibleDataUsedToMintFungibleCollectionToken),
+			_ => fail!(<CommonError<T>>::NotFungibleDataUsedToMintFungibleCollectionToken),
 		};
 
 		with_weight(
@@ -435,6 +435,10 @@
 		<TotalSupply<T>>::try_get(self.id).ok()
 	}
 
+	fn xcm_extensions(&self) -> Option<&dyn XcmExtensions<T>> {
+		Some(self)
+	}
+
 	fn set_allowance_for_all(
 		&self,
 		_owner: T::CrossAccountId,
@@ -453,3 +457,61 @@
 		fail!(<Error<T>>::FungibleTokensAreAlwaysValid)
 	}
 }
+
+impl<T: Config> XcmExtensions<T> for FungibleHandle<T> {
+	fn is_foreign(&self) -> bool {
+		self.flags.foreign
+	}
+
+	fn create_item_internal(
+		&self,
+		depositor: &<T>::CrossAccountId,
+		to: <T>::CrossAccountId,
+		data: CreateItemData,
+		nesting_budget: &dyn Budget,
+	) -> Result<TokenId, sp_runtime::DispatchError> {
+		match &data {
+			up_data_structs::CreateItemData::Fungible(fungible_data) => {
+				<Pallet<T>>::create_multiple_items(
+					self,
+					&depositor,
+					[(to, fungible_data.value)].into_iter().collect(),
+					nesting_budget,
+				)?
+			}
+			_ => fail!(<CommonError<T>>::NotFungibleDataUsedToMintFungibleCollectionToken),
+		}
+
+		Ok(TokenId::default())
+	}
+
+	fn transfer_item_internal(
+		&self,
+		depositor: &<T>::CrossAccountId,
+		from: &<T>::CrossAccountId,
+		to: &<T>::CrossAccountId,
+		token: TokenId,
+		amount: u128,
+		nesting_budget: &dyn Budget,
+	) -> sp_runtime::DispatchResult {
+		ensure!(
+			token == TokenId::default(),
+			<CommonError<T>>::FungibleItemsHaveNoId
+		);
+
+		<Pallet<T>>::transfer_internal(self, &depositor, &from, &to, amount, nesting_budget)
+			.map(|_| ())
+			.map_err(|post_info| post_info.error)
+	}
+
+	fn burn_item_internal(
+		&self,
+		from: <T>::CrossAccountId,
+		token: TokenId,
+		amount: u128,
+	) -> sp_runtime::DispatchResult {
+		<Self as CommonCollectionOperations<T>>::burn_item(&self, from, token, amount)
+			.map(|_| ())
+			.map_err(|post_info| post_info.error)
+	}
+}
modifiedpallets/fungible/src/lib.rsdiffbeforeafterboth
--- a/pallets/fungible/src/lib.rs
+++ b/pallets/fungible/src/lib.rs
@@ -95,8 +95,9 @@
 use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};
 use sp_std::{collections::btree_map::BTreeMap, vec::Vec};
 use up_data_structs::{
-	budget::Budget, mapping::TokenAddressMapping, AccessMode, CollectionId, CreateCollectionData,
-	Property, PropertyKey, TokenId,
+	budget::{Budget, ZeroBudget},
+	mapping::TokenAddressMapping,
+	AccessMode, CollectionId, CreateCollectionData, Property, PropertyKey, TokenId,
 };
 use weights::WeightInfo;
 
@@ -121,8 +122,6 @@
 
 	#[pallet::error]
 	pub enum Error<T> {
-		/// Not Fungible item data used to mint in Fungible collection.
-		NotFungibleDataUsedToMintFungibleCollectionToken,
 		/// Tried to set data for fungible item.
 		FungibleItemsDontHaveData,
 		/// Fungible token does not support nesting.
@@ -275,9 +274,6 @@
 		let balance = <Balance<T>>::get((collection.id, owner))
 			.checked_sub(amount)
 			.ok_or(<CommonError<T>>::TokenValueTooLow)?;
-
-		// Foreign collection check
-		ensure!(!collection.flags.foreign, <CommonError<T>>::NoPermission);
 
 		if collection.permissions.access() == AccessMode::AllowList {
 			collection.check_allowlist(owner)?;
@@ -309,47 +305,7 @@
 		));
 		Ok(())
 	}
-
-	/// Burns the specified amount of the token.
-	pub fn burn_foreign(
-		collection: &FungibleHandle<T>,
-		owner: &T::CrossAccountId,
-		amount: u128,
-	) -> DispatchResult {
-		let total_supply = <TotalSupply<T>>::get(collection.id)
-			.checked_sub(amount)
-			.ok_or(<CommonError<T>>::TokenValueTooLow)?;
-
-		let balance = <Balance<T>>::get((collection.id, owner))
-			.checked_sub(amount)
-			.ok_or(<CommonError<T>>::TokenValueTooLow)?;
-		// =========
 
-		if balance == 0 {
-			<Balance<T>>::remove((collection.id, owner));
-			<PalletStructure<T>>::unnest_if_nested(owner, collection.id, TokenId::default());
-		} else {
-			<Balance<T>>::insert((collection.id, owner), balance);
-		}
-		<TotalSupply<T>>::insert(collection.id, total_supply);
-
-		<PalletEvm<T>>::deposit_log(
-			ERC20Events::Transfer {
-				from: *owner.as_eth(),
-				to: H160::default(),
-				value: amount.into(),
-			}
-			.to_log(collection_id_to_address(collection.id)),
-		);
-		<PalletCommon<T>>::deposit_event(CommonEvent::ItemDestroyed(
-			collection.id,
-			TokenId::default(),
-			owner.clone(),
-			amount,
-		));
-		Ok(())
-	}
-
 	/// Transfers the specified amount of tokens. Will check that
 	/// the transfer is allowed for the token.
 	///
@@ -450,14 +406,25 @@
 	}
 
 	/// Minting tokens for multiple IDs.
-	/// It is a utility function used in [`create_multiple_items`][`Pallet::create_multiple_items`]
-	/// and [`create_multiple_items_foreign`][`Pallet::create_multiple_items_foreign`]
-	pub fn create_multiple_items_common(
+	/// See [`create_item`][`Pallet::create_item`] for more details.
+	pub fn create_multiple_items(
 		collection: &FungibleHandle<T>,
-		sender: &T::CrossAccountId,
+		depositor: &T::CrossAccountId,
 		data: BTreeMap<T::CrossAccountId, u128>,
 		nesting_budget: &dyn Budget,
 	) -> DispatchResult {
+		if !collection.is_owner_or_admin(depositor) {
+			ensure!(
+				collection.permissions.mint_mode(),
+				<CommonError<T>>::PublicMintingNotAllowed
+			);
+			collection.check_allowlist(depositor)?;
+
+			for (owner, _) in data.iter() {
+				collection.check_allowlist(owner)?;
+			}
+		}
+
 		let total_supply = data
 			.values()
 			.copied()
@@ -468,7 +435,7 @@
 
 		for (to, _) in data.iter() {
 			<PalletStructure<T>>::check_nesting(
-				sender,
+				depositor,
 				to,
 				collection.id,
 				TokenId::default(),
@@ -514,44 +481,7 @@
 
 		Ok(())
 	}
-
-	/// Minting tokens for multiple IDs.
-	/// See [`create_item`][`Pallet::create_item`] for more details.
-	pub fn create_multiple_items(
-		collection: &FungibleHandle<T>,
-		sender: &T::CrossAccountId,
-		data: BTreeMap<T::CrossAccountId, u128>,
-		nesting_budget: &dyn Budget,
-	) -> DispatchResult {
-		// Foreign collection check
-		ensure!(!collection.flags.foreign, <CommonError<T>>::NoPermission);
-
-		if !collection.is_owner_or_admin(sender) {
-			ensure!(
-				collection.permissions.mint_mode(),
-				<CommonError<T>>::PublicMintingNotAllowed
-			);
-			collection.check_allowlist(sender)?;
-
-			for (owner, _) in data.iter() {
-				collection.check_allowlist(owner)?;
-			}
-		}
 
-		Self::create_multiple_items_common(collection, sender, data, nesting_budget)
-	}
-
-	/// Minting tokens for multiple IDs.
-	/// See [`create_item_foreign`][`Pallet::create_item_foreign`] for more details.
-	pub fn create_multiple_items_foreign(
-		collection: &FungibleHandle<T>,
-		sender: &T::CrossAccountId,
-		data: BTreeMap<T::CrossAccountId, u128>,
-		nesting_budget: &dyn Budget,
-	) -> DispatchResult {
-		Self::create_multiple_items_common(collection, sender, data, nesting_budget)
-	}
-
 	fn set_allowance_unchecked(
 		collection: &FungibleHandle<T>,
 		owner: &T::CrossAccountId,
@@ -777,24 +707,6 @@
 		nesting_budget: &dyn Budget,
 	) -> DispatchResult {
 		Self::create_multiple_items(
-			collection,
-			sender,
-			[(data.0, data.1)].into_iter().collect(),
-			nesting_budget,
-		)
-	}
-
-	/// Creates fungible token.
-	///
-	/// - `data`: Contains user who will become the owners of the tokens and amount
-	///   of tokens he will receive.
-	pub fn create_item_foreign(
-		collection: &FungibleHandle<T>,
-		sender: &T::CrossAccountId,
-		data: CreateItemData<T>,
-		nesting_budget: &dyn Budget,
-	) -> DispatchResult {
-		Self::create_multiple_items_foreign(
 			collection,
 			sender,
 			[(data.0, data.1)].into_iter().collect(),
modifiedpallets/nonfungible/src/common.rsdiffbeforeafterboth
--- a/pallets/nonfungible/src/common.rs
+++ b/pallets/nonfungible/src/common.rs
@@ -19,8 +19,8 @@
 use frame_support::{dispatch::DispatchResultWithPostInfo, ensure, fail, weights::Weight};
 use pallet_common::{
 	weights::WeightInfo as _, with_weight, write_token_properties_total_weight,
-	CommonCollectionOperations, CommonWeightInfo, RefungibleExtensions,
-	SelfWeightOf as PalletCommonWeightOf,
+	CommonCollectionOperations, CommonWeightInfo, SelfWeightOf as PalletCommonWeightOf,
+	XcmExtensions,
 };
 use pallet_structure::Pallet as PalletStructure;
 use sp_runtime::DispatchError;
@@ -543,6 +543,10 @@
 		}
 	}
 
+	fn xcm_extensions(&self) -> Option<&dyn XcmExtensions<T>> {
+		Some(self)
+	}
+
 	fn set_allowance_for_all(
 		&self,
 		owner: T::CrossAccountId,
@@ -566,3 +570,53 @@
 		)
 	}
 }
+
+impl<T: Config> XcmExtensions<T> for NonfungibleHandle<T> {
+	fn is_foreign(&self) -> bool {
+		self.flags.foreign
+	}
+
+	fn create_item_internal(
+		&self,
+		depositor: &<T>::CrossAccountId,
+		to: <T>::CrossAccountId,
+		data: up_data_structs::CreateItemData,
+		nesting_budget: &dyn Budget,
+	) -> Result<TokenId, sp_runtime::DispatchError> {
+		<Pallet<T>>::create_multiple_items(
+			self,
+			&depositor,
+			vec![map_create_data::<T>(data, &to)?],
+			nesting_budget,
+		)?;
+
+		Ok(self.last_token_id())
+	}
+
+	fn transfer_item_internal(
+		&self,
+		depositor: &<T>::CrossAccountId,
+		from: &<T>::CrossAccountId,
+		to: &<T>::CrossAccountId,
+		token: TokenId,
+		amount: u128,
+		nesting_budget: &dyn Budget,
+	) -> sp_runtime::DispatchResult {
+		ensure!(amount == 1, <Error<T>>::NonfungibleItemsHaveNoAmount);
+
+		<Pallet<T>>::transfer_internal(self, &depositor, &from, &to, token, nesting_budget)
+			.map(|_| ())
+			.map_err(|post_info| post_info.error)
+	}
+
+	fn burn_item_internal(
+		&self,
+		from: T::CrossAccountId,
+		token: TokenId,
+		amount: u128,
+	) -> sp_runtime::DispatchResult {
+		ensure!(amount == 1, <Error<T>>::NonfungibleItemsHaveNoAmount);
+
+		<Pallet<T>>::burn(self, &from, token)
+	}
+}
modifiedprimitives/data-structs/src/budget.rsdiffbeforeafterboth
--- a/primitives/data-structs/src/budget.rs
+++ b/primitives/data-structs/src/budget.rs
@@ -36,3 +36,10 @@
 		true
 	}
 }
+
+pub struct ZeroBudget;
+impl Budget for ZeroBudget {
+	fn consume_custom(&self, _calls: u32) -> bool {
+		false
+	}
+}
modifiedruntime/common/config/pallets/foreign_asset.rsdiffbeforeafterboth
--- a/runtime/common/config/pallets/foreign_asset.rs
+++ b/runtime/common/config/pallets/foreign_asset.rs
@@ -1,14 +1,43 @@
 use frame_support::{parameter_types, PalletId};
+use pallet_evm::account::CrossAccountId;
+use sp_core::H160;
+use staging_xcm::prelude::*;
+use staging_xcm_builder::AccountKey20Aliases;
 
-use crate::{runtime_common::config::governance, Runtime, RuntimeEvent};
+use crate::{
+	runtime_common::config::{
+		ethereum::CrossAccountId as ConfigCrossAccountId,
+		governance,
+		xcm::{LocationToAccountId, SelfLocation},
+	},
+	RelayNetwork, Runtime, RuntimeEvent,
+};
 
 parameter_types! {
 	pub ForeignAssetPalletId: PalletId = PalletId(*b"frgnasts");
 }
 
+pub struct LocationToCrossAccountId;
+impl staging_xcm_executor::traits::ConvertLocation<ConfigCrossAccountId>
+	for LocationToCrossAccountId
+{
+	fn convert_location(location: &MultiLocation) -> Option<ConfigCrossAccountId> {
+		LocationToAccountId::convert_location(location)
+			.map(|sub| ConfigCrossAccountId::from_sub(sub))
+			.or_else(|| {
+				let eth_address =
+					AccountKey20Aliases::<RelayNetwork, H160>::convert_location(location)?;
+
+				Some(ConfigCrossAccountId::from_eth(eth_address))
+			})
+	}
+}
+
 impl pallet_foreign_assets::Config for Runtime {
 	type RuntimeEvent = RuntimeEvent;
 	type ForceRegisterOrigin = governance::RootOrTechnicalCommitteeMember;
 	type PalletId = ForeignAssetPalletId;
+	type SelfLocation = SelfLocation;
+	type LocationToAccountId = LocationToCrossAccountId;
 	type WeightInfo = pallet_foreign_assets::weights::SubstrateWeight<Self>;
 }