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
--- a/pallets/foreign-assets/src/lib.rs
+++ b/pallets/foreign-assets/src/lib.rs
@@ -22,13 +22,6 @@
 //!
 //! ## Overview
 //!
-//! The foreign assests pallet provides functions for:
-//!
-//! - Local and foreign assets management. The foreign assets can be updated without runtime upgrade.
-//! - Bounds between asset and target collection for cross chain transfer and inner transfers.
-//!
-//! ## Overview
-//!
 //! Under construction
 
 #![cfg_attr(not(feature = "std"), no_std)]
@@ -37,22 +30,21 @@
 use frame_support::{dispatch::DispatchResult, pallet_prelude::*, traits::EnsureOrigin, PalletId};
 use frame_system::pallet_prelude::*;
 use pallet_common::{
-	dispatch::CollectionDispatch, erc::CrossAccountId, NATIVE_FUNGIBLE_COLLECTION_ID,
+	dispatch::CollectionDispatch, erc::CrossAccountId, XcmExtensions, NATIVE_FUNGIBLE_COLLECTION_ID,
 };
 use sp_runtime::traits::AccountIdConversion;
 use sp_std::{vec, vec::Vec};
-// NOTE: MultiLocation is used in storages, we will need to do migration if upgrade the
-// MultiLocation to the XCM v3.
 use staging_xcm::{
 	opaque::latest::{prelude::XcmError, Weight},
 	v3::{prelude::*, MultiAsset, XcmContext},
 };
 use staging_xcm_executor::{
-	traits::{TransactAsset, WeightTrader},
+	traits::{ConvertLocation, Error as XcmExecutorError, TransactAsset, WeightTrader},
 	Assets,
 };
 use up_data_structs::{
-	CollectionId, CollectionMode, CollectionName, CreateCollectionData, PropertyKey, TokenId,
+	budget::ZeroBudget, CollectionId, CollectionMode, CollectionName, CreateCollectionData,
+	CreateFungibleData, CreateItemData, CreateNftData, Property, PropertyKey, TokenId,
 };
 
 pub mod weights;
@@ -87,6 +79,12 @@
 		/// The ID of the foreign assets pallet.
 		type PalletId: Get<PalletId>;
 
+		/// Self-location of this parachain.
+		type SelfLocation: Get<MultiLocation>;
+
+		/// The converter from a MultiLocation to a CrossAccountId.
+		type LocationToAccountId: ConvertLocation<Self::CrossAccountId>;
+
 		/// Weight information for the extrinsics in this module.
 		type WeightInfo: WeightInfo;
 	}
@@ -113,6 +111,12 @@
 	pub type ForeignReserveLocationToCollection<T: Config> =
 		StorageMap<_, Twox64Concat, staging_xcm::v3::MultiLocation, CollectionId, OptionQuery>;
 
+	/// The corresponding reserve location of collections.
+	#[pallet::storage]
+	#[pallet::getter(fn collection_to_foreign_reserve_location)]
+	pub type CollectionToForeignReserveLocation<T: Config> =
+		StorageMap<_, Twox64Concat, CollectionId, staging_xcm::v3::MultiLocation, OptionQuery>;
+
 	/// The correponding NFT token id of reserve NFTs
 	#[pallet::storage]
 	#[pallet::getter(fn foreign_reserve_asset_instance_to_token_id)]
@@ -180,6 +184,7 @@
 			)?;
 
 			<ForeignReserveLocationToCollection<T>>::insert(reserve_location, collection_id);
+			<CollectionToForeignReserveLocation<T>>::insert(collection_id, reserve_location);
 
 			Self::deposit_event(Event::<T>::ForeignAssetRegistered {
 				asset_id: collection_id,
@@ -210,6 +215,132 @@
 			.try_into()
 			.expect("key length < max property key length; qed")
 	}
+
+	fn native_asset_location_to_collection(
+		asset_location: &MultiLocation,
+	) -> Result<Option<CollectionId>, XcmError> {
+		let self_location = T::SelfLocation::get();
+
+		if *asset_location == Here.into() {
+			Ok(Some(NATIVE_FUNGIBLE_COLLECTION_ID))
+		} else if *asset_location == self_location {
+			Ok(Some(NATIVE_FUNGIBLE_COLLECTION_ID))
+		} else if asset_location.parents == self_location.parents {
+			match asset_location
+				.interior
+				.match_and_split(&self_location.interior)
+			{
+				Some(GeneralIndex(collection_id)) => Ok(Some(CollectionId(
+					(*collection_id)
+						.try_into()
+						.map_err(|_| XcmExecutorError::AssetIdConversionFailed)?,
+				))),
+				_ => Ok(None),
+			}
+		} else {
+			Ok(None)
+		}
+	}
+
+	fn multiasset_to_collection(asset: &MultiAsset) -> Result<CollectionId, XcmError> {
+		let AssetId::Concrete(asset_reserve_location) = asset.id else {
+			return Err(XcmExecutorError::AssetNotHandled.into());
+		};
+
+		Self::native_asset_location_to_collection(&asset_reserve_location)?
+			.or_else(|| Self::foreign_reserve_location_to_collection(asset_reserve_location))
+			.ok_or_else(|| XcmExecutorError::AssetIdConversionFailed.into())
+	}
+
+	fn native_asset_instance_to_token_id(
+		asset_instance: &AssetInstance,
+	) -> Result<TokenId, XcmError> {
+		match asset_instance {
+			AssetInstance::Index(token_id) => Ok(TokenId(
+				(*token_id)
+					.try_into()
+					.map_err(|_| XcmError::AssetNotFound)?,
+			)),
+			_ => Err(XcmError::AssetNotFound),
+		}
+	}
+
+	/// Obtains the token id of the `asset_instance` in the collection.
+	///
+	/// Returns `Ok(None)` only if the `asset_instance` points to a foreign item
+	/// and it haven't been created on this blockchain yet.
+	///
+	/// If the `asset_instance` points to a native item, it cannot return `Ok(None)`.
+	fn asset_instance_to_token_id(
+		xcm_ext: &dyn XcmExtensions<T>,
+		collection_id: CollectionId,
+		asset_instance: &AssetInstance,
+	) -> Result<Option<TokenId>, XcmError> {
+		if xcm_ext.is_foreign() {
+			Ok(Self::foreign_reserve_asset_instance_to_token_id(
+				collection_id,
+				asset_instance,
+			))
+		} else {
+			Self::native_asset_instance_to_token_id(asset_instance).map(Some)
+		}
+	}
+
+	fn create_foreign_asset_instance(
+		xcm_ext: &dyn XcmExtensions<T>,
+		collection_id: CollectionId,
+		asset_instance: &AssetInstance,
+		to: T::CrossAccountId,
+	) -> XcmResult {
+		let asset_instance_encoded = asset_instance.encode();
+
+		let derivative_token_id = xcm_ext
+			.create_item(
+				&Self::pallet_account(),
+				to,
+				CreateItemData::NFT(CreateNftData {
+					properties: vec![Property {
+					key: Self::reserve_asset_instance_property_key(),
+					value: asset_instance_encoded
+						.try_into()
+						.expect("asset instance length <= 32 bytes which is less than value length limit; qed"),
+				}]
+					.try_into()
+					.expect("just one property can always be stored; qed"),
+				}),
+				&ZeroBudget,
+			)
+			.map_err(|_| XcmError::FailedToTransactAsset("non-fungible item deposit failed"))?;
+
+		<ForeignReserveAssetInstanceToTokenId<T>>::insert(
+			collection_id,
+			asset_instance,
+			derivative_token_id,
+		);
+
+		Ok(())
+	}
+
+	fn deposit_asset_instance(
+		xcm_ext: &dyn XcmExtensions<T>,
+		collection_id: CollectionId,
+		to: T::CrossAccountId,
+		asset_instance: &AssetInstance,
+	) -> XcmResult {
+		if let Some(token_id) =
+			Self::asset_instance_to_token_id(xcm_ext, collection_id, asset_instance)?
+		{
+			let depositor = &Self::pallet_account();
+			let from = depositor;
+			let amount = 1;
+
+			xcm_ext
+				.transfer_item(depositor, from, &to, token_id, amount, &ZeroBudget)
+				.map_err(|_| XcmError::FailedToTransactAsset("non-fungible item deposit failed"))
+		} else {
+			Self::create_foreign_asset_instance(xcm_ext, collection_id, asset_instance, to)
+		}
+	}
 }
 
 impl<T: Config> TransactAsset for Pallet<T> {
@@ -234,7 +365,31 @@
 	fn check_out(_dest: &MultiLocation, _what: &MultiAsset, _context: &XcmContext) {}
 
 	fn deposit_asset(what: &MultiAsset, to: &MultiLocation, context: &XcmContext) -> XcmResult {
-		Err(XcmError::Unimplemented)
+		let collection_id = Self::multiasset_to_collection(what)?;
+		let dispatch =
+			T::CollectionDispatch::dispatch(collection_id).map_err(|_| XcmError::AssetNotFound)?;
+
+		let collection = dispatch.as_dyn();
+		let xcm_ext = collection.xcm_extensions().ok_or(XcmError::Unimplemented)?;
+
+		let to = T::LocationToAccountId::convert_location(to)
+			.ok_or(XcmExecutorError::AccountIdConversionFailed)?;
+
+		match what.fun {
+			Fungibility::Fungible(amount) => xcm_ext
+				.create_item(
+					&Self::pallet_account(),
+					to,
+					CreateItemData::Fungible(CreateFungibleData { value: amount }),
+					&ZeroBudget,
+				)
+				.map(|_| ())
+				.map_err(|_| XcmError::FailedToTransactAsset("fungible item deposit failed")),
+
+			Fungibility::NonFungible(asset_instance) => {
+				Self::deposit_asset_instance(xcm_ext, collection_id, to, &asset_instance)
+			}
+		}
 	}
 
 	fn withdraw_asset(
@@ -263,20 +418,17 @@
 		if collection_id == NATIVE_FUNGIBLE_COLLECTION_ID {
 			Some(Here.into())
 		} else {
-			// let dispatch = T::CollectionDispatch::dispatch(collection_id).ok()?;
-			// let collection = dispatch.as_dyn();
-			// let xcm_ext = collection.xcm_extensions()?;
+			let dispatch = T::CollectionDispatch::dispatch(collection_id).ok()?;
+			let collection = dispatch.as_dyn();
+			let xcm_ext = collection.xcm_extensions()?;
 
-			// if xcm_ext.is_foreign() {
-			// 	let encoded_location =
-			// 		collection.property(&<Pallet<T>>::reserve_location_property_key())?;
-			// 	MultiLocation::decode(&mut &encoded_location[..]).ok()
-			// } else {
-			// 	T::SelfLocation::get()
-			// 		.pushed_with_interior(GeneralIndex(collection_id.0.into()))
-			// 		.ok()
-			// }
-			todo!()
+			if xcm_ext.is_foreign() {
+				<Pallet<T>>::collection_to_foreign_reserve_location(collection_id)
+			} else {
+				T::SelfLocation::get()
+					.pushed_with_interior(GeneralIndex(collection_id.0.into()))
+					.ok()
+			}
 		}
 	}
 }
modifiedpallets/fungible/src/common.rsdiffbeforeafterboth
before · pallets/fungible/src/common.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/>.1617use core::marker::PhantomData;1819use frame_support::{dispatch::DispatchResultWithPostInfo, ensure, fail, weights::Weight};20use pallet_common::{21	weights::WeightInfo as _, with_weight, CommonCollectionOperations, CommonWeightInfo,22	Error as CommonError, RefungibleExtensions, SelfWeightOf as PalletCommonWeightOf,23};24use sp_runtime::{ArithmeticError, DispatchError};25use sp_std::{vec, vec::Vec};26use up_data_structs::{27	budget::Budget, CollectionId, CreateItemData, CreateItemExData, Property, PropertyKey,28	PropertyKeyPermission, PropertyValue, TokenId, TokenOwnerError,29};3031use crate::{32	weights::WeightInfo, Allowance, Balance, Config, Error, FungibleHandle, Pallet, SelfWeightOf,33	TotalSupply,34};3536pub struct CommonWeights<T: Config>(PhantomData<T>);37impl<T: Config> CommonWeightInfo<T::CrossAccountId> for CommonWeights<T> {38	fn create_multiple_items(_data: &[CreateItemData]) -> Weight {39		// All items minted for the same user, so it works same as create_item40		<SelfWeightOf<T>>::create_item()41	}4243	fn create_multiple_items_ex(data: &CreateItemExData<T::CrossAccountId>) -> Weight {44		match data {45			CreateItemExData::Fungible(f) => {46				<SelfWeightOf<T>>::create_multiple_items_ex(f.len() as u32)47			}48			_ => Weight::zero(),49		}50	}5152	fn burn_item() -> Weight {53		<SelfWeightOf<T>>::burn_item()54	}5556	fn set_collection_properties(amount: u32) -> Weight {57		<pallet_common::SelfWeightOf<T>>::set_collection_properties(amount)58	}5960	fn set_token_properties(_amount: u32) -> Weight {61		// Error62		Weight::zero()63	}6465	fn set_token_property_permissions(_amount: u32) -> Weight {66		// Error67		Weight::zero()68	}6970	fn transfer() -> Weight {71		<SelfWeightOf<T>>::transfer_raw()72			.saturating_add(<PalletCommonWeightOf<T>>::check_accesslist().saturating_mul(2))73	}7475	fn approve() -> Weight {76		<SelfWeightOf<T>>::approve()77	}7879	fn approve_from() -> Weight {80		<SelfWeightOf<T>>::approve_from()81	}8283	fn transfer_from() -> Weight {84		Self::transfer()85			.saturating_add(<SelfWeightOf<T>>::check_allowed_raw())86			.saturating_add(<SelfWeightOf<T>>::set_allowance_unchecked_raw())87	}8889	fn burn_from() -> Weight {90		<SelfWeightOf<T>>::burn_from()91	}9293	fn set_allowance_for_all() -> Weight {94		Weight::zero()95	}9697	fn force_repair_item() -> Weight {98		Weight::zero()99	}100}101102/// Implementation of `CommonCollectionOperations` for `FungibleHandle`. It wraps FungibleHandle Pallete103/// methods and adds weight info.104impl<T: Config> CommonCollectionOperations<T> for FungibleHandle<T> {105	fn create_item(106		&self,107		sender: T::CrossAccountId,108		to: T::CrossAccountId,109		data: up_data_structs::CreateItemData,110		nesting_budget: &dyn Budget,111	) -> DispatchResultWithPostInfo {112		match &data {113			up_data_structs::CreateItemData::Fungible(fungible_data) => with_weight(114				<Pallet<T>>::create_item(self, &sender, (to, fungible_data.value), nesting_budget),115				<CommonWeights<T>>::create_item(&data),116			),117			_ => fail!(<Error<T>>::NotFungibleDataUsedToMintFungibleCollectionToken),118		}119	}120121	fn create_multiple_items(122		&self,123		sender: T::CrossAccountId,124		to: T::CrossAccountId,125		data: Vec<up_data_structs::CreateItemData>,126		nesting_budget: &dyn Budget,127	) -> DispatchResultWithPostInfo {128		let mut sum: u128 = 0;129		for data in &data {130			match &data {131				up_data_structs::CreateItemData::Fungible(data) => {132					sum = sum133						.checked_add(data.value)134						.ok_or(ArithmeticError::Overflow)?;135				}136				_ => fail!(<Error<T>>::NotFungibleDataUsedToMintFungibleCollectionToken),137			}138		}139140		with_weight(141			<Pallet<T>>::create_item(self, &sender, (to, sum), nesting_budget),142			<CommonWeights<T>>::create_multiple_items(&data),143		)144	}145146	fn create_multiple_items_ex(147		&self,148		sender: <T>::CrossAccountId,149		data: up_data_structs::CreateItemExData<<T>::CrossAccountId>,150		nesting_budget: &dyn Budget,151	) -> DispatchResultWithPostInfo {152		let weight = <CommonWeights<T>>::create_multiple_items_ex(&data);153		let data = match data {154			up_data_structs::CreateItemExData::Fungible(f) => f,155			_ => fail!(<Error<T>>::NotFungibleDataUsedToMintFungibleCollectionToken),156		};157158		with_weight(159			<Pallet<T>>::create_multiple_items(self, &sender, data.into_inner(), nesting_budget),160			weight,161		)162	}163164	fn burn_item(165		&self,166		sender: T::CrossAccountId,167		token: TokenId,168		amount: u128,169	) -> DispatchResultWithPostInfo {170		ensure!(171			token == TokenId::default(),172			<CommonError<T>>::FungibleItemsHaveNoId173		);174175		with_weight(176			<Pallet<T>>::burn(self, &sender, amount),177			<CommonWeights<T>>::burn_item(),178		)179	}180181	fn transfer(182		&self,183		from: T::CrossAccountId,184		to: T::CrossAccountId,185		token: TokenId,186		amount: u128,187		nesting_budget: &dyn Budget,188	) -> DispatchResultWithPostInfo {189		ensure!(190			token == TokenId::default(),191			<CommonError<T>>::FungibleItemsHaveNoId192		);193194		<Pallet<T>>::transfer(self, &from, &to, amount, nesting_budget)195	}196197	fn approve(198		&self,199		sender: T::CrossAccountId,200		spender: T::CrossAccountId,201		token: TokenId,202		amount: u128,203	) -> DispatchResultWithPostInfo {204		ensure!(205			token == TokenId::default(),206			<CommonError<T>>::FungibleItemsHaveNoId207		);208209		with_weight(210			<Pallet<T>>::set_allowance(self, &sender, &spender, amount),211			<CommonWeights<T>>::approve(),212		)213	}214215	fn approve_from(216		&self,217		sender: T::CrossAccountId,218		from: T::CrossAccountId,219		to: T::CrossAccountId,220		token: TokenId,221		amount: u128,222	) -> DispatchResultWithPostInfo {223		ensure!(224			token == TokenId::default(),225			<CommonError<T>>::FungibleItemsHaveNoId226		);227228		with_weight(229			<Pallet<T>>::set_allowance_from(self, &sender, &from, &to, amount),230			<CommonWeights<T>>::approve_from(),231		)232	}233234	fn transfer_from(235		&self,236		sender: T::CrossAccountId,237		from: T::CrossAccountId,238		to: T::CrossAccountId,239		token: TokenId,240		amount: u128,241		nesting_budget: &dyn Budget,242	) -> DispatchResultWithPostInfo {243		ensure!(244			token == TokenId::default(),245			<CommonError<T>>::FungibleItemsHaveNoId246		);247248		<Pallet<T>>::transfer_from(self, &sender, &from, &to, amount, nesting_budget)249	}250251	fn burn_from(252		&self,253		sender: T::CrossAccountId,254		from: T::CrossAccountId,255		token: TokenId,256		amount: u128,257		nesting_budget: &dyn Budget,258	) -> DispatchResultWithPostInfo {259		ensure!(260			token == TokenId::default(),261			<CommonError<T>>::FungibleItemsHaveNoId262		);263264		with_weight(265			<Pallet<T>>::burn_from(self, &sender, &from, amount, nesting_budget),266			<CommonWeights<T>>::burn_from(),267		)268	}269270	fn set_collection_properties(271		&self,272		sender: T::CrossAccountId,273		properties: Vec<Property>,274	) -> DispatchResultWithPostInfo {275		let weight = <CommonWeights<T>>::set_collection_properties(properties.len() as u32);276277		with_weight(278			<Pallet<T>>::set_collection_properties(self, &sender, properties),279			weight,280		)281	}282283	fn delete_collection_properties(284		&self,285		sender: &T::CrossAccountId,286		property_keys: Vec<PropertyKey>,287	) -> DispatchResultWithPostInfo {288		let weight = <CommonWeights<T>>::delete_collection_properties(property_keys.len() as u32);289290		with_weight(291			<Pallet<T>>::delete_collection_properties(self, sender, property_keys),292			weight,293		)294	}295296	fn set_token_properties(297		&self,298		_sender: T::CrossAccountId,299		_token_id: TokenId,300		_property: Vec<Property>,301		_nesting_budget: &dyn Budget,302	) -> DispatchResultWithPostInfo {303		fail!(<Error<T>>::SettingPropertiesNotAllowed)304	}305306	fn set_token_property_permissions(307		&self,308		_sender: &T::CrossAccountId,309		_property_permissions: Vec<PropertyKeyPermission>,310	) -> DispatchResultWithPostInfo {311		fail!(<Error<T>>::SettingPropertiesNotAllowed)312	}313314	fn delete_token_properties(315		&self,316		_sender: T::CrossAccountId,317		_token_id: TokenId,318		_property_keys: Vec<PropertyKey>,319		_nesting_budget: &dyn Budget,320	) -> DispatchResultWithPostInfo {321		fail!(<Error<T>>::SettingPropertiesNotAllowed)322	}323324	fn get_token_properties_raw(325		&self,326		_token_id: TokenId,327	) -> Option<up_data_structs::TokenProperties> {328		// No token properties are defined on fungibles329		None330	}331332	fn set_token_properties_raw(&self, _token_id: TokenId, _map: up_data_structs::TokenProperties) {333		// No token properties are defined on fungibles334	}335336	fn check_nesting(337		&self,338		_sender: &<T>::CrossAccountId,339		_from: (CollectionId, TokenId),340		_under: TokenId,341		_nesting_budget: &dyn Budget,342	) -> sp_runtime::DispatchResult {343		fail!(<Error<T>>::FungibleDisallowsNesting)344	}345346	fn nest(&self, _under: TokenId, _to_nest: (CollectionId, TokenId)) {}347348	fn unnest(&self, _under: TokenId, _to_nest: (CollectionId, TokenId)) {}349350	fn collection_tokens(&self) -> Vec<TokenId> {351		vec![TokenId::default()]352	}353354	fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId> {355		if <Balance<T>>::get((self.id, account)) != 0 {356			vec![TokenId::default()]357		} else {358			vec![]359		}360	}361362	fn token_exists(&self, token: TokenId) -> bool {363		token == TokenId::default()364	}365366	fn last_token_id(&self) -> TokenId {367		TokenId::default()368	}369370	fn token_owner(&self, _token: TokenId) -> Result<T::CrossAccountId, TokenOwnerError> {371		Err(TokenOwnerError::MultipleOwners)372	}373374	fn check_token_indirect_owner(375		&self,376		_token: TokenId,377		_maybe_owner: &T::CrossAccountId,378		_nesting_budget: &dyn Budget,379	) -> Result<bool, DispatchError> {380		Ok(false)381	}382383	/// Returns 10 tokens owners in no particular order.384	fn token_owners(&self, token: TokenId) -> Vec<T::CrossAccountId> {385		<Pallet<T>>::token_owners(self.id, token).unwrap_or_default()386	}387388	fn token_property(&self, _token_id: TokenId, _key: &PropertyKey) -> Option<PropertyValue> {389		None390	}391392	fn token_properties(393		&self,394		_token_id: TokenId,395		_keys: Option<Vec<PropertyKey>>,396	) -> Vec<Property> {397		Vec::new()398	}399400	fn total_supply(&self) -> u32 {401		1402	}403404	fn account_balance(&self, account: T::CrossAccountId) -> u32 {405		if <Balance<T>>::get((self.id, account)) != 0 {406			1407		} else {408			0409		}410	}411412	fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128 {413		if token != TokenId::default() {414			return 0;415		}416		<Balance<T>>::get((self.id, account))417	}418419	fn allowance(420		&self,421		sender: T::CrossAccountId,422		spender: T::CrossAccountId,423		token: TokenId,424	) -> u128 {425		if token != TokenId::default() {426			return 0;427		}428		<Allowance<T>>::get((self.id, sender, spender))429	}430431	fn total_pieces(&self, token: TokenId) -> Option<u128> {432		if token != TokenId::default() {433			return None;434		}435		<TotalSupply<T>>::try_get(self.id).ok()436	}437438	fn set_allowance_for_all(439		&self,440		_owner: T::CrossAccountId,441		_operator: T::CrossAccountId,442		_approve: bool,443	) -> DispatchResultWithPostInfo {444		fail!(<Error<T>>::SettingAllowanceForAllNotAllowed)445	}446447	fn allowance_for_all(&self, _owner: T::CrossAccountId, _operator: T::CrossAccountId) -> bool {448		false449	}450451	/// Repairs a possibly broken item.452	fn repair_item(&self, _token: TokenId) -> DispatchResultWithPostInfo {453		fail!(<Error<T>>::FungibleTokensAreAlwaysValid)454	}455}
after · pallets/fungible/src/common.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/>.1617use core::marker::PhantomData;1819use frame_support::{dispatch::DispatchResultWithPostInfo, ensure, fail, weights::Weight};20use pallet_common::{21	weights::WeightInfo as _, with_weight, CommonCollectionOperations, CommonWeightInfo,22	Error as CommonError, SelfWeightOf as PalletCommonWeightOf, XcmExtensions,23};24use sp_runtime::{ArithmeticError, DispatchError};25use sp_std::{vec, vec::Vec};26use up_data_structs::{27	budget::Budget, CollectionId, CreateItemData, CreateItemExData, Property, PropertyKey,28	PropertyKeyPermission, PropertyValue, TokenId, TokenOwnerError,29};3031use crate::{32	weights::WeightInfo, Allowance, Balance, Config, Error, FungibleHandle, Pallet, SelfWeightOf,33	TotalSupply,34};3536pub struct CommonWeights<T: Config>(PhantomData<T>);37impl<T: Config> CommonWeightInfo<T::CrossAccountId> for CommonWeights<T> {38	fn create_multiple_items(_data: &[CreateItemData]) -> Weight {39		// All items minted for the same user, so it works same as create_item40		<SelfWeightOf<T>>::create_item()41	}4243	fn create_multiple_items_ex(data: &CreateItemExData<T::CrossAccountId>) -> Weight {44		match data {45			CreateItemExData::Fungible(f) => {46				<SelfWeightOf<T>>::create_multiple_items_ex(f.len() as u32)47			}48			_ => Weight::zero(),49		}50	}5152	fn burn_item() -> Weight {53		<SelfWeightOf<T>>::burn_item()54	}5556	fn set_collection_properties(amount: u32) -> Weight {57		<pallet_common::SelfWeightOf<T>>::set_collection_properties(amount)58	}5960	fn set_token_properties(_amount: u32) -> Weight {61		// Error62		Weight::zero()63	}6465	fn set_token_property_permissions(_amount: u32) -> Weight {66		// Error67		Weight::zero()68	}6970	fn transfer() -> Weight {71		<SelfWeightOf<T>>::transfer_raw()72			.saturating_add(<PalletCommonWeightOf<T>>::check_accesslist().saturating_mul(2))73	}7475	fn approve() -> Weight {76		<SelfWeightOf<T>>::approve()77	}7879	fn approve_from() -> Weight {80		<SelfWeightOf<T>>::approve_from()81	}8283	fn transfer_from() -> Weight {84		Self::transfer()85			.saturating_add(<SelfWeightOf<T>>::check_allowed_raw())86			.saturating_add(<SelfWeightOf<T>>::set_allowance_unchecked_raw())87	}8889	fn burn_from() -> Weight {90		<SelfWeightOf<T>>::burn_from()91	}9293	fn set_allowance_for_all() -> Weight {94		Weight::zero()95	}9697	fn force_repair_item() -> Weight {98		Weight::zero()99	}100}101102/// Implementation of `CommonCollectionOperations` for `FungibleHandle`. It wraps FungibleHandle Pallete103/// methods and adds weight info.104impl<T: Config> CommonCollectionOperations<T> for FungibleHandle<T> {105	fn create_item(106		&self,107		sender: T::CrossAccountId,108		to: T::CrossAccountId,109		data: up_data_structs::CreateItemData,110		nesting_budget: &dyn Budget,111	) -> DispatchResultWithPostInfo {112		match &data {113			up_data_structs::CreateItemData::Fungible(fungible_data) => with_weight(114				<Pallet<T>>::create_item(self, &sender, (to, fungible_data.value), nesting_budget),115				<CommonWeights<T>>::create_item(&data),116			),117			_ => fail!(<CommonError<T>>::NotFungibleDataUsedToMintFungibleCollectionToken),118		}119	}120121	fn create_multiple_items(122		&self,123		sender: T::CrossAccountId,124		to: T::CrossAccountId,125		data: Vec<up_data_structs::CreateItemData>,126		nesting_budget: &dyn Budget,127	) -> DispatchResultWithPostInfo {128		let mut sum: u128 = 0;129		for data in &data {130			match &data {131				up_data_structs::CreateItemData::Fungible(data) => {132					sum = sum133						.checked_add(data.value)134						.ok_or(ArithmeticError::Overflow)?;135				}136				_ => fail!(<CommonError<T>>::NotFungibleDataUsedToMintFungibleCollectionToken),137			}138		}139140		with_weight(141			<Pallet<T>>::create_item(self, &sender, (to, sum), nesting_budget),142			<CommonWeights<T>>::create_multiple_items(&data),143		)144	}145146	fn create_multiple_items_ex(147		&self,148		sender: <T>::CrossAccountId,149		data: up_data_structs::CreateItemExData<<T>::CrossAccountId>,150		nesting_budget: &dyn Budget,151	) -> DispatchResultWithPostInfo {152		let weight = <CommonWeights<T>>::create_multiple_items_ex(&data);153		let data = match data {154			up_data_structs::CreateItemExData::Fungible(f) => f,155			_ => fail!(<CommonError<T>>::NotFungibleDataUsedToMintFungibleCollectionToken),156		};157158		with_weight(159			<Pallet<T>>::create_multiple_items(self, &sender, data.into_inner(), nesting_budget),160			weight,161		)162	}163164	fn burn_item(165		&self,166		sender: T::CrossAccountId,167		token: TokenId,168		amount: u128,169	) -> DispatchResultWithPostInfo {170		ensure!(171			token == TokenId::default(),172			<CommonError<T>>::FungibleItemsHaveNoId173		);174175		with_weight(176			<Pallet<T>>::burn(self, &sender, amount),177			<CommonWeights<T>>::burn_item(),178		)179	}180181	fn transfer(182		&self,183		from: T::CrossAccountId,184		to: T::CrossAccountId,185		token: TokenId,186		amount: u128,187		nesting_budget: &dyn Budget,188	) -> DispatchResultWithPostInfo {189		ensure!(190			token == TokenId::default(),191			<CommonError<T>>::FungibleItemsHaveNoId192		);193194		<Pallet<T>>::transfer(self, &from, &to, amount, nesting_budget)195	}196197	fn approve(198		&self,199		sender: T::CrossAccountId,200		spender: T::CrossAccountId,201		token: TokenId,202		amount: u128,203	) -> DispatchResultWithPostInfo {204		ensure!(205			token == TokenId::default(),206			<CommonError<T>>::FungibleItemsHaveNoId207		);208209		with_weight(210			<Pallet<T>>::set_allowance(self, &sender, &spender, amount),211			<CommonWeights<T>>::approve(),212		)213	}214215	fn approve_from(216		&self,217		sender: T::CrossAccountId,218		from: T::CrossAccountId,219		to: T::CrossAccountId,220		token: TokenId,221		amount: u128,222	) -> DispatchResultWithPostInfo {223		ensure!(224			token == TokenId::default(),225			<CommonError<T>>::FungibleItemsHaveNoId226		);227228		with_weight(229			<Pallet<T>>::set_allowance_from(self, &sender, &from, &to, amount),230			<CommonWeights<T>>::approve_from(),231		)232	}233234	fn transfer_from(235		&self,236		sender: T::CrossAccountId,237		from: T::CrossAccountId,238		to: T::CrossAccountId,239		token: TokenId,240		amount: u128,241		nesting_budget: &dyn Budget,242	) -> DispatchResultWithPostInfo {243		ensure!(244			token == TokenId::default(),245			<CommonError<T>>::FungibleItemsHaveNoId246		);247248		<Pallet<T>>::transfer_from(self, &sender, &from, &to, amount, nesting_budget)249	}250251	fn burn_from(252		&self,253		sender: T::CrossAccountId,254		from: T::CrossAccountId,255		token: TokenId,256		amount: u128,257		nesting_budget: &dyn Budget,258	) -> DispatchResultWithPostInfo {259		ensure!(260			token == TokenId::default(),261			<CommonError<T>>::FungibleItemsHaveNoId262		);263264		with_weight(265			<Pallet<T>>::burn_from(self, &sender, &from, amount, nesting_budget),266			<CommonWeights<T>>::burn_from(),267		)268	}269270	fn set_collection_properties(271		&self,272		sender: T::CrossAccountId,273		properties: Vec<Property>,274	) -> DispatchResultWithPostInfo {275		let weight = <CommonWeights<T>>::set_collection_properties(properties.len() as u32);276277		with_weight(278			<Pallet<T>>::set_collection_properties(self, &sender, properties),279			weight,280		)281	}282283	fn delete_collection_properties(284		&self,285		sender: &T::CrossAccountId,286		property_keys: Vec<PropertyKey>,287	) -> DispatchResultWithPostInfo {288		let weight = <CommonWeights<T>>::delete_collection_properties(property_keys.len() as u32);289290		with_weight(291			<Pallet<T>>::delete_collection_properties(self, sender, property_keys),292			weight,293		)294	}295296	fn set_token_properties(297		&self,298		_sender: T::CrossAccountId,299		_token_id: TokenId,300		_property: Vec<Property>,301		_nesting_budget: &dyn Budget,302	) -> DispatchResultWithPostInfo {303		fail!(<Error<T>>::SettingPropertiesNotAllowed)304	}305306	fn set_token_property_permissions(307		&self,308		_sender: &T::CrossAccountId,309		_property_permissions: Vec<PropertyKeyPermission>,310	) -> DispatchResultWithPostInfo {311		fail!(<Error<T>>::SettingPropertiesNotAllowed)312	}313314	fn delete_token_properties(315		&self,316		_sender: T::CrossAccountId,317		_token_id: TokenId,318		_property_keys: Vec<PropertyKey>,319		_nesting_budget: &dyn Budget,320	) -> DispatchResultWithPostInfo {321		fail!(<Error<T>>::SettingPropertiesNotAllowed)322	}323324	fn get_token_properties_raw(325		&self,326		_token_id: TokenId,327	) -> Option<up_data_structs::TokenProperties> {328		// No token properties are defined on fungibles329		None330	}331332	fn set_token_properties_raw(&self, _token_id: TokenId, _map: up_data_structs::TokenProperties) {333		// No token properties are defined on fungibles334	}335336	fn check_nesting(337		&self,338		_sender: &<T>::CrossAccountId,339		_from: (CollectionId, TokenId),340		_under: TokenId,341		_nesting_budget: &dyn Budget,342	) -> sp_runtime::DispatchResult {343		fail!(<Error<T>>::FungibleDisallowsNesting)344	}345346	fn nest(&self, _under: TokenId, _to_nest: (CollectionId, TokenId)) {}347348	fn unnest(&self, _under: TokenId, _to_nest: (CollectionId, TokenId)) {}349350	fn collection_tokens(&self) -> Vec<TokenId> {351		vec![TokenId::default()]352	}353354	fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId> {355		if <Balance<T>>::get((self.id, account)) != 0 {356			vec![TokenId::default()]357		} else {358			vec![]359		}360	}361362	fn token_exists(&self, token: TokenId) -> bool {363		token == TokenId::default()364	}365366	fn last_token_id(&self) -> TokenId {367		TokenId::default()368	}369370	fn token_owner(&self, _token: TokenId) -> Result<T::CrossAccountId, TokenOwnerError> {371		Err(TokenOwnerError::MultipleOwners)372	}373374	fn check_token_indirect_owner(375		&self,376		_token: TokenId,377		_maybe_owner: &T::CrossAccountId,378		_nesting_budget: &dyn Budget,379	) -> Result<bool, DispatchError> {380		Ok(false)381	}382383	/// Returns 10 tokens owners in no particular order.384	fn token_owners(&self, token: TokenId) -> Vec<T::CrossAccountId> {385		<Pallet<T>>::token_owners(self.id, token).unwrap_or_default()386	}387388	fn token_property(&self, _token_id: TokenId, _key: &PropertyKey) -> Option<PropertyValue> {389		None390	}391392	fn token_properties(393		&self,394		_token_id: TokenId,395		_keys: Option<Vec<PropertyKey>>,396	) -> Vec<Property> {397		Vec::new()398	}399400	fn total_supply(&self) -> u32 {401		1402	}403404	fn account_balance(&self, account: T::CrossAccountId) -> u32 {405		if <Balance<T>>::get((self.id, account)) != 0 {406			1407		} else {408			0409		}410	}411412	fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128 {413		if token != TokenId::default() {414			return 0;415		}416		<Balance<T>>::get((self.id, account))417	}418419	fn allowance(420		&self,421		sender: T::CrossAccountId,422		spender: T::CrossAccountId,423		token: TokenId,424	) -> u128 {425		if token != TokenId::default() {426			return 0;427		}428		<Allowance<T>>::get((self.id, sender, spender))429	}430431	fn total_pieces(&self, token: TokenId) -> Option<u128> {432		if token != TokenId::default() {433			return None;434		}435		<TotalSupply<T>>::try_get(self.id).ok()436	}437438	fn xcm_extensions(&self) -> Option<&dyn XcmExtensions<T>> {439		Some(self)440	}441442	fn set_allowance_for_all(443		&self,444		_owner: T::CrossAccountId,445		_operator: T::CrossAccountId,446		_approve: bool,447	) -> DispatchResultWithPostInfo {448		fail!(<Error<T>>::SettingAllowanceForAllNotAllowed)449	}450451	fn allowance_for_all(&self, _owner: T::CrossAccountId, _operator: T::CrossAccountId) -> bool {452		false453	}454455	/// Repairs a possibly broken item.456	fn repair_item(&self, _token: TokenId) -> DispatchResultWithPostInfo {457		fail!(<Error<T>>::FungibleTokensAreAlwaysValid)458	}459}460461impl<T: Config> XcmExtensions<T> for FungibleHandle<T> {462	fn is_foreign(&self) -> bool {463		self.flags.foreign464	}465466	fn create_item_internal(467		&self,468		depositor: &<T>::CrossAccountId,469		to: <T>::CrossAccountId,470		data: CreateItemData,471		nesting_budget: &dyn Budget,472	) -> Result<TokenId, sp_runtime::DispatchError> {473		match &data {474			up_data_structs::CreateItemData::Fungible(fungible_data) => {475				<Pallet<T>>::create_multiple_items(476					self,477					&depositor,478					[(to, fungible_data.value)].into_iter().collect(),479					nesting_budget,480				)?481			}482			_ => fail!(<CommonError<T>>::NotFungibleDataUsedToMintFungibleCollectionToken),483		}484485		Ok(TokenId::default())486	}487488	fn transfer_item_internal(489		&self,490		depositor: &<T>::CrossAccountId,491		from: &<T>::CrossAccountId,492		to: &<T>::CrossAccountId,493		token: TokenId,494		amount: u128,495		nesting_budget: &dyn Budget,496	) -> sp_runtime::DispatchResult {497		ensure!(498			token == TokenId::default(),499			<CommonError<T>>::FungibleItemsHaveNoId500		);501502		<Pallet<T>>::transfer_internal(self, &depositor, &from, &to, amount, nesting_budget)503			.map(|_| ())504			.map_err(|post_info| post_info.error)505	}506507	fn burn_item_internal(508		&self,509		from: <T>::CrossAccountId,510		token: TokenId,511		amount: u128,512	) -> sp_runtime::DispatchResult {513		<Self as CommonCollectionOperations<T>>::burn_item(&self, from, token, amount)514			.map(|_| ())515			.map_err(|post_info| post_info.error)516	}517}
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>;
 }