git.delta.rocks / unique-network / refs/commits / 11758e7eb7b8

difftreelog

doc: foreign assets

Daniel Shiposha2023-10-18parent: #e2da42b.patch.diff
in: master

1 file 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//! 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		let from = T::LocationToAccountId::convert_location(from)401			.ok_or(XcmExecutorError::AccountIdConversionFailed)?;402403		let collection_id = Self::multiasset_to_collection(what)?;404		let dispatch =405			T::CollectionDispatch::dispatch(collection_id).map_err(|_| XcmError::AssetNotFound)?;406407		let collection = dispatch.as_dyn();408		let xcm_ext = collection.xcm_extensions().ok_or(XcmError::NoPermission)?;409410		match what.fun {411			Fungibility::Fungible(amount) => xcm_ext412				.burn_item(from, TokenId::default(), amount)413				.map_err(|_| XcmError::FailedToTransactAsset("fungible item withdraw failed"))?,414415			Fungibility::NonFungible(asset_instance) => {416				let token_id =417					Self::asset_instance_to_token_id(xcm_ext, collection_id, &asset_instance)?418						.ok_or(XcmError::AssetNotFound)?;419420				if xcm_ext.token_has_children(token_id) {421					return Err(XcmError::Unimplemented);422				}423424				let depositor = &from;425				let to = Self::pallet_account();426				let amount = 1;427				xcm_ext428					.transfer_item(depositor, &from, &to, token_id, amount, &ZeroBudget)429					.map_err(|_| {430						XcmError::FailedToTransactAsset("nonfungible item withdraw failed")431					})?;432			}433		}434435		Ok(what.clone().into())436	}437438	fn internal_transfer_asset(439		what: &MultiAsset,440		from: &MultiLocation,441		to: &MultiLocation,442		_context: &XcmContext,443	) -> Result<staging_xcm_executor::Assets, XcmError> {444		let collection_id = Self::multiasset_to_collection(what)?;445446		let dispatch =447			T::CollectionDispatch::dispatch(collection_id).map_err(|_| XcmError::AssetNotFound)?;448		let collection = dispatch.as_dyn();449		let xcm_ext = collection.xcm_extensions().ok_or(XcmError::NoPermission)?;450451		let from = T::LocationToAccountId::convert_location(from)452			.ok_or(XcmExecutorError::AccountIdConversionFailed)?;453454		let to = T::LocationToAccountId::convert_location(to)455			.ok_or(XcmExecutorError::AccountIdConversionFailed)?;456457		let depositor = &from;458459		match what.fun {460			Fungibility::Fungible(amount) => xcm_ext461				.transfer_item(462					depositor,463					&from,464					&to,465					TokenId::default(),466					amount,467					&ZeroBudget,468				)469				.map_err(|_| XcmError::FailedToTransactAsset("fungible item transfer failed"))?,470471			Fungibility::NonFungible(asset_instance) => {472				let token_id =473					Self::asset_instance_to_token_id(xcm_ext, collection_id, &asset_instance)?474						.ok_or(XcmError::AssetNotFound)?;475476				let amount = 1;477478				xcm_ext479					.transfer_item(depositor, &from, &to, token_id, amount, &ZeroBudget)480					.map_err(|_| {481						XcmError::FailedToTransactAsset("nonfungible item transfer failed")482					})?;483			}484		}485486		Ok(what.clone().into())487	}488}489490pub struct CurrencyIdConvert<T: Config>(PhantomData<T>);491impl<T: Config> sp_runtime::traits::Convert<CollectionId, Option<MultiLocation>>492	for CurrencyIdConvert<T>493{494	fn convert(collection_id: CollectionId) -> Option<MultiLocation> {495		if collection_id == NATIVE_FUNGIBLE_COLLECTION_ID {496			Some(Here.into())497		} else {498			let dispatch = T::CollectionDispatch::dispatch(collection_id).ok()?;499			let collection = dispatch.as_dyn();500			let xcm_ext = collection.xcm_extensions()?;501502			if xcm_ext.is_foreign() {503				<Pallet<T>>::collection_to_foreign_reserve_location(collection_id)504			} else {505				T::SelfLocation::get()506					.pushed_with_interior(GeneralIndex(collection_id.0.into()))507					.ok()508			}509		}510	}511}512513pub use frame_support::{514	traits::{515		fungibles::Balanced, tokens::currency::Currency as CurrencyT, OnUnbalanced as OnUnbalancedT,516	},517	weights::{WeightToFee, WeightToFeePolynomial},518};519520pub struct FreeForAll;521522impl WeightTrader for FreeForAll {523	fn new() -> Self {524		Self525	}526527	fn buy_weight(528		&mut self,529		weight: Weight,530		payment: Assets,531		_xcm: &XcmContext,532	) -> Result<Assets, XcmError> {533		log::trace!(target: "fassets::weight", "buy_weight weight: {:?}, payment: {:?}", weight, payment);534		Ok(payment)535	}536}
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//! ## Overview20//!21//! The Foreign Assets is a proxy that maps XCM operations to the Unique Network's pallets logic.2223#![cfg_attr(not(feature = "std"), no_std)]24#![allow(clippy::unused_unit)]2526use frame_support::{dispatch::DispatchResult, pallet_prelude::*, traits::EnsureOrigin, PalletId};27use frame_system::pallet_prelude::*;28use pallet_common::{29	dispatch::CollectionDispatch, erc::CrossAccountId, XcmExtensions, NATIVE_FUNGIBLE_COLLECTION_ID,30};31use sp_runtime::traits::AccountIdConversion;32use sp_std::{vec, vec::Vec};33use staging_xcm::{34	opaque::latest::{prelude::XcmError, Weight},35	v3::{prelude::*, MultiAsset, XcmContext},36};37use staging_xcm_executor::{38	traits::{ConvertLocation, Error as XcmExecutorError, TransactAsset, WeightTrader},39	Assets,40};41use up_data_structs::{42	budget::ZeroBudget, CollectionId, CollectionMode, CollectionName, CreateCollectionData,43	CreateFungibleData, CreateItemData, CreateNftData, Property, PropertyKey, TokenId,44};4546pub mod weights;4748#[cfg(feature = "runtime-benchmarks")]49mod benchmarking;5051pub use module::*;52pub use weights::WeightInfo;5354#[frame_support::pallet]55pub mod module {56	use up_data_structs::{57		CollectionDescription, Property, PropertyKeyPermission, PropertyPermission,58	};5960	use super::*;6162	#[pallet::config]63	pub trait Config:64		frame_system::Config65		+ pallet_common::Config66		+ pallet_fungible::Config67		+ pallet_balances::Config68	{69		/// The overarching event type.70		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;7172		/// Origin for force registering of a foreign asset.73		type ForceRegisterOrigin: EnsureOrigin<Self::RuntimeOrigin>;7475		/// The ID of the foreign assets pallet.76		type PalletId: Get<PalletId>;7778		/// Self-location of this parachain.79		type SelfLocation: Get<MultiLocation>;8081		/// The converter from a MultiLocation to a CrossAccountId.82		type LocationToAccountId: ConvertLocation<Self::CrossAccountId>;8384		/// Weight information for the extrinsics in this module.85		type WeightInfo: WeightInfo;86	}8788	#[pallet::error]89	pub enum Error<T> {90		/// The foreign asset is already registered91		ForeignAssetAlreadyRegistered,92	}9394	#[pallet::event]95	#[pallet::generate_deposit(fn deposit_event)]96	pub enum Event<T: Config> {97		/// The foreign asset registered.98		ForeignAssetRegistered {99			asset_id: CollectionId,100			reserve_location: MultiLocation,101		},102	}103104	/// The corresponding collections of reserve locations.105	#[pallet::storage]106	#[pallet::getter(fn foreign_reserve_location_to_collection)]107	pub type ForeignReserveLocationToCollection<T: Config> =108		StorageMap<_, Twox64Concat, staging_xcm::v3::MultiLocation, CollectionId, OptionQuery>;109110	/// The corresponding reserve location of collections.111	#[pallet::storage]112	#[pallet::getter(fn collection_to_foreign_reserve_location)]113	pub type CollectionToForeignReserveLocation<T: Config> =114		StorageMap<_, Twox64Concat, CollectionId, staging_xcm::v3::MultiLocation, 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);183			<CollectionToForeignReserveLocation<T>>::insert(collection_id, reserve_location);184185			Self::deposit_event(Event::<T>::ForeignAssetRegistered {186				asset_id: collection_id,187				reserve_location,188			});189190			Ok(())191		}192	}193}194195impl<T: Config> Pallet<T> {196	fn pallet_account() -> T::CrossAccountId {197		let owner: T::AccountId = T::PalletId::get().into_account_truncating();198		T::CrossAccountId::from_sub(owner)199	}200201	fn reserve_location_property_key() -> PropertyKey {202		b"reserve-location"203			.to_vec()204			.try_into()205			.expect("key length < max property key length; qed")206	}207208	fn reserve_asset_instance_property_key() -> PropertyKey {209		b"reserve-asset-instance"210			.to_vec()211			.try_into()212			.expect("key length < max property key length; qed")213	}214215	fn native_asset_location_to_collection(216		asset_location: &MultiLocation,217	) -> Result<Option<CollectionId>, XcmError> {218		let self_location = T::SelfLocation::get();219220		if *asset_location == Here.into() {221			Ok(Some(NATIVE_FUNGIBLE_COLLECTION_ID))222		} else if *asset_location == self_location {223			Ok(Some(NATIVE_FUNGIBLE_COLLECTION_ID))224		} else if asset_location.parents == self_location.parents {225			match asset_location226				.interior227				.match_and_split(&self_location.interior)228			{229				Some(GeneralIndex(collection_id)) => Ok(Some(CollectionId(230					(*collection_id)231						.try_into()232						.map_err(|_| XcmExecutorError::AssetIdConversionFailed)?,233				))),234				_ => Ok(None),235			}236		} else {237			Ok(None)238		}239	}240241	fn multiasset_to_collection(asset: &MultiAsset) -> Result<CollectionId, XcmError> {242		let AssetId::Concrete(asset_reserve_location) = asset.id else {243			return Err(XcmExecutorError::AssetNotHandled.into());244		};245246		Self::native_asset_location_to_collection(&asset_reserve_location)?247			.or_else(|| Self::foreign_reserve_location_to_collection(asset_reserve_location))248			.ok_or_else(|| XcmExecutorError::AssetIdConversionFailed.into())249	}250251	fn native_asset_instance_to_token_id(252		asset_instance: &AssetInstance,253	) -> Result<TokenId, XcmError> {254		match asset_instance {255			AssetInstance::Index(token_id) => Ok(TokenId(256				(*token_id)257					.try_into()258					.map_err(|_| XcmError::AssetNotFound)?,259			)),260			_ => Err(XcmError::AssetNotFound),261		}262	}263264	/// Obtains the token id of the `asset_instance` in the collection.265	///266	/// Returns `Ok(None)` only if the `asset_instance` points to a foreign item267	/// and it haven't been created on this blockchain yet.268	///269	/// If the `asset_instance` points to a native item, it cannot return `Ok(None)`.270	fn asset_instance_to_token_id(271		xcm_ext: &dyn XcmExtensions<T>,272		collection_id: CollectionId,273		asset_instance: &AssetInstance,274	) -> Result<Option<TokenId>, XcmError> {275		if xcm_ext.is_foreign() {276			Ok(Self::foreign_reserve_asset_instance_to_token_id(277				collection_id,278				asset_instance,279			))280		} else {281			Self::native_asset_instance_to_token_id(asset_instance).map(Some)282		}283	}284285	fn create_foreign_asset_instance(286		xcm_ext: &dyn XcmExtensions<T>,287		collection_id: CollectionId,288		asset_instance: &AssetInstance,289		to: T::CrossAccountId,290	) -> XcmResult {291		let asset_instance_encoded = asset_instance.encode();292293		let derivative_token_id = xcm_ext294			.create_item(295				&Self::pallet_account(),296				to,297				CreateItemData::NFT(CreateNftData {298					properties: vec![Property {299					key: Self::reserve_asset_instance_property_key(),300					value: asset_instance_encoded301						.try_into()302						.expect("asset instance length <= 32 bytes which is less than value length limit; qed"),303				}]304					.try_into()305					.expect("just one property can always be stored; qed"),306				}),307				&ZeroBudget,308			)309			.map_err(|_| XcmError::FailedToTransactAsset("non-fungible item deposit failed"))?;310311		<ForeignReserveAssetInstanceToTokenId<T>>::insert(312			collection_id,313			asset_instance,314			derivative_token_id,315		);316317		Ok(())318	}319320	fn deposit_asset_instance(321		xcm_ext: &dyn XcmExtensions<T>,322		collection_id: CollectionId,323		to: T::CrossAccountId,324		asset_instance: &AssetInstance,325	) -> XcmResult {326		if let Some(token_id) =327			Self::asset_instance_to_token_id(xcm_ext, collection_id, asset_instance)?328		{329			let depositor = &Self::pallet_account();330			let from = depositor;331			let amount = 1;332333			xcm_ext334				.transfer_item(depositor, from, &to, token_id, amount, &ZeroBudget)335				.map_err(|_| XcmError::FailedToTransactAsset("non-fungible item deposit failed"))336		} else {337			Self::create_foreign_asset_instance(xcm_ext, collection_id, asset_instance, to)338		}339	}340}341342impl<T: Config> TransactAsset for Pallet<T> {343	fn can_check_in(344		_origin: &MultiLocation,345		_what: &MultiAsset,346		_context: &XcmContext,347	) -> XcmResult {348		Err(XcmError::Unimplemented)349	}350351	fn check_in(_origin: &MultiLocation, _what: &MultiAsset, _context: &XcmContext) {}352353	fn can_check_out(354		_dest: &MultiLocation,355		_what: &MultiAsset,356		_context: &XcmContext,357	) -> XcmResult {358		Err(XcmError::Unimplemented)359	}360361	fn check_out(_dest: &MultiLocation, _what: &MultiAsset, _context: &XcmContext) {}362363	fn deposit_asset(what: &MultiAsset, to: &MultiLocation, _context: &XcmContext) -> XcmResult {364		let collection_id = Self::multiasset_to_collection(what)?;365		let dispatch =366			T::CollectionDispatch::dispatch(collection_id).map_err(|_| XcmError::AssetNotFound)?;367368		let collection = dispatch.as_dyn();369		let xcm_ext = collection.xcm_extensions().ok_or(XcmError::Unimplemented)?;370371		let to = T::LocationToAccountId::convert_location(to)372			.ok_or(XcmExecutorError::AccountIdConversionFailed)?;373374		match what.fun {375			Fungibility::Fungible(amount) => xcm_ext376				.create_item(377					&Self::pallet_account(),378					to,379					CreateItemData::Fungible(CreateFungibleData { value: amount }),380					&ZeroBudget,381				)382				.map(|_| ())383				.map_err(|_| XcmError::FailedToTransactAsset("fungible item deposit failed")),384385			Fungibility::NonFungible(asset_instance) => {386				Self::deposit_asset_instance(xcm_ext, collection_id, to, &asset_instance)387			}388		}389	}390391	fn withdraw_asset(392		what: &MultiAsset,393		from: &MultiLocation,394		_maybe_context: Option<&XcmContext>,395	) -> Result<staging_xcm_executor::Assets, XcmError> {396		let from = T::LocationToAccountId::convert_location(from)397			.ok_or(XcmExecutorError::AccountIdConversionFailed)?;398399		let collection_id = Self::multiasset_to_collection(what)?;400		let dispatch =401			T::CollectionDispatch::dispatch(collection_id).map_err(|_| XcmError::AssetNotFound)?;402403		let collection = dispatch.as_dyn();404		let xcm_ext = collection.xcm_extensions().ok_or(XcmError::NoPermission)?;405406		match what.fun {407			Fungibility::Fungible(amount) => xcm_ext408				.burn_item(from, TokenId::default(), amount)409				.map_err(|_| XcmError::FailedToTransactAsset("fungible item withdraw failed"))?,410411			Fungibility::NonFungible(asset_instance) => {412				let token_id =413					Self::asset_instance_to_token_id(xcm_ext, collection_id, &asset_instance)?414						.ok_or(XcmError::AssetNotFound)?;415416				if xcm_ext.token_has_children(token_id) {417					return Err(XcmError::Unimplemented);418				}419420				let depositor = &from;421				let to = Self::pallet_account();422				let amount = 1;423				xcm_ext424					.transfer_item(depositor, &from, &to, token_id, amount, &ZeroBudget)425					.map_err(|_| {426						XcmError::FailedToTransactAsset("nonfungible item withdraw failed")427					})?;428			}429		}430431		Ok(what.clone().into())432	}433434	fn internal_transfer_asset(435		what: &MultiAsset,436		from: &MultiLocation,437		to: &MultiLocation,438		_context: &XcmContext,439	) -> Result<staging_xcm_executor::Assets, XcmError> {440		let collection_id = Self::multiasset_to_collection(what)?;441442		let dispatch =443			T::CollectionDispatch::dispatch(collection_id).map_err(|_| XcmError::AssetNotFound)?;444		let collection = dispatch.as_dyn();445		let xcm_ext = collection.xcm_extensions().ok_or(XcmError::NoPermission)?;446447		let from = T::LocationToAccountId::convert_location(from)448			.ok_or(XcmExecutorError::AccountIdConversionFailed)?;449450		let to = T::LocationToAccountId::convert_location(to)451			.ok_or(XcmExecutorError::AccountIdConversionFailed)?;452453		let depositor = &from;454455		match what.fun {456			Fungibility::Fungible(amount) => xcm_ext457				.transfer_item(458					depositor,459					&from,460					&to,461					TokenId::default(),462					amount,463					&ZeroBudget,464				)465				.map_err(|_| XcmError::FailedToTransactAsset("fungible item transfer failed"))?,466467			Fungibility::NonFungible(asset_instance) => {468				let token_id =469					Self::asset_instance_to_token_id(xcm_ext, collection_id, &asset_instance)?470						.ok_or(XcmError::AssetNotFound)?;471472				let amount = 1;473474				xcm_ext475					.transfer_item(depositor, &from, &to, token_id, amount, &ZeroBudget)476					.map_err(|_| {477						XcmError::FailedToTransactAsset("nonfungible item transfer failed")478					})?;479			}480		}481482		Ok(what.clone().into())483	}484}485486pub struct CurrencyIdConvert<T: Config>(PhantomData<T>);487impl<T: Config> sp_runtime::traits::Convert<CollectionId, Option<MultiLocation>>488	for CurrencyIdConvert<T>489{490	fn convert(collection_id: CollectionId) -> Option<MultiLocation> {491		if collection_id == NATIVE_FUNGIBLE_COLLECTION_ID {492			Some(Here.into())493		} else {494			let dispatch = T::CollectionDispatch::dispatch(collection_id).ok()?;495			let collection = dispatch.as_dyn();496			let xcm_ext = collection.xcm_extensions()?;497498			if xcm_ext.is_foreign() {499				<Pallet<T>>::collection_to_foreign_reserve_location(collection_id)500			} else {501				T::SelfLocation::get()502					.pushed_with_interior(GeneralIndex(collection_id.0.into()))503					.ok()504			}505		}506	}507}508509pub use frame_support::{510	traits::{511		fungibles::Balanced, tokens::currency::Currency as CurrencyT, OnUnbalanced as OnUnbalancedT,512	},513	weights::{WeightToFee, WeightToFeePolynomial},514};515516pub struct FreeForAll;517518impl WeightTrader for FreeForAll {519	fn new() -> Self {520		Self521	}522523	fn buy_weight(524		&mut self,525		weight: Weight,526		payment: Assets,527		_xcm: &XcmContext,528	) -> Result<Assets, XcmError> {529		log::trace!(target: "fassets::weight", "buy_weight weight: {:?}, payment: {:?}", weight, payment);530		Ok(payment)531	}532}