git.delta.rocks / unique-network / refs/commits / 1eed41e9dfea

difftreelog

fix restore foreign flag, minor fixes

Daniel Shiposha2023-10-19parent: #56fde60.patch.diff
in: master

11 files changed

modifiedpallets/common/src/benchmarking.rsdiffbeforeafterboth
--- a/pallets/common/src/benchmarking.rs
+++ b/pallets/common/src/benchmarking.rs
@@ -121,7 +121,7 @@
 		owner,
 		CollectionMode::NFT,
 		|owner: T::CrossAccountId, data| {
-			<Pallet<T>>::init_collection(owner.clone(), Some(owner), data)
+			<Pallet<T>>::init_collection(owner.clone(), Some(owner), false, data)
 		},
 		|h| h,
 	)
modifiedpallets/common/src/dispatch.rsdiffbeforeafterboth
--- a/pallets/common/src/dispatch.rs
+++ b/pallets/common/src/dispatch.rs
@@ -78,6 +78,21 @@
 		sender: T::CrossAccountId,
 		payer: Option<T::CrossAccountId>,
 		data: CreateCollectionData<T::CrossAccountId>,
+	) -> Result<CollectionId, DispatchError> {
+		Self::create_internal(sender, payer, false, data)
+	}
+
+	/// Create a collection. The collection will be created according to the value of [`data.mode`](CreateCollectionData::mode).
+	///
+	/// * `sender` - The user who will become the owner of the collection.
+	/// * `payer` - If set, the user who pays the collection creation deposit.
+	/// * `data` - Description of the created collection.
+	/// * `is_special_collection` -- Whether this collection is a system one, i.e. can have special flags set.
+	fn create_internal(
+		sender: T::CrossAccountId,
+		payer: Option<T::CrossAccountId>,
+		is_special_collection: bool,
+		data: CreateCollectionData<T::CrossAccountId>,
 	) -> Result<CollectionId, DispatchError>;
 
 	/// Delete the collection.
modifiedpallets/common/src/lib.rsdiffbeforeafterboth
--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -1088,6 +1088,7 @@
 			read_only: flags.external,
 
 			flags: RpcCollectionFlags {
+				foreign: flags.foreign,
 				erc721metadata: flags.erc721metadata,
 			},
 		})
@@ -1129,12 +1130,16 @@
 	/// * `owner` - The owner of the collection.
 	/// * `payer` - If set, the user that will pay a deposit for the collection creation.
 	/// * `data` - Description of the created collection.
+	/// * `is_special_collection` -- Whether this collection is a special one, i.e. can have special flags set.
 	pub fn init_collection(
 		owner: T::CrossAccountId,
 		payer: Option<T::CrossAccountId>,
+		is_special_collection: bool,
 		data: CreateCollectionData<T::CrossAccountId>,
 	) -> Result<CollectionId, DispatchError> {
-		ensure!(data.flags.is_allowed_for_user(), <Error<T>>::NoPermission);
+		if !is_special_collection {
+			ensure!(data.flags.is_allowed_for_user(), <Error<T>>::NoPermission);
+		}
 
 		// Take a (non-refundable) deposit of collection creation
 		if let Some(payer) = payer {
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//! ## 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			if <ForeignReserveLocationToCollection<T>>::contains_key(reserve_location) {144				return Err(<Error<T>>::ForeignAssetAlreadyRegistered.into());145			}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 payer = None;156			let collection_id = T::CollectionDispatch::create(157				foreign_collection_owner,158				payer,159				CreateCollectionData {160					name,161					description,162					mode,163164					properties: vec![Property {165						key: Self::reserve_location_property_key(),166						value: reserve_location167							.encode()168							.try_into()169							.expect("multilocation is less than 32k; qed"),170					}]171					.try_into()172					.expect("just one property can always be stored; qed"),173174					token_property_permissions: vec![PropertyKeyPermission {175						key: Self::reserve_asset_instance_property_key(),176						permission: PropertyPermission {177							mutable: false,178							collection_admin: true,179							token_owner: false,180						},181					}]182					.try_into()183					.expect("just one property permission can always be stored; qed"),184					..Default::default()185				},186			)?;187188			<ForeignReserveLocationToCollection<T>>::insert(reserve_location, collection_id);189			<CollectionToForeignReserveLocation<T>>::insert(collection_id, reserve_location);190191			Self::deposit_event(Event::<T>::ForeignAssetRegistered {192				asset_id: collection_id,193				reserve_location,194			});195196			Ok(())197		}198	}199}200201impl<T: Config> Pallet<T> {202	fn pallet_account() -> T::CrossAccountId {203		let owner: T::AccountId = T::PalletId::get().into_account_truncating();204		T::CrossAccountId::from_sub(owner)205	}206207	fn reserve_location_property_key() -> PropertyKey {208		b"reserve-location"209			.to_vec()210			.try_into()211			.expect("key length < max property key length; qed")212	}213214	fn reserve_asset_instance_property_key() -> PropertyKey {215		b"reserve-asset-instance"216			.to_vec()217			.try_into()218			.expect("key length < max property key length; qed")219	}220221	/// Converts a multilocation to the Unique Network's local collection222	/// (i.e. the collection originally created on the Unique Network's parachain).223	///224	/// The multilocation corresponds to a Unique Network's collection if:225	/// * It is `Here` location that corresponds to the native token of this parachain.226	/// * It is `../Parachain(<Unique Network Para ID>)` that also corresponds to the native token of this parachain.227	/// * It is `../Parachain(<Unique Network Para ID>)/GeneralIndex(<Collection ID>)` that corresponds228	/// to the collection with the ID equal to `<Collection ID>`. The `<Collection ID>` must be in the valid range,229	/// otherwise the `AssetIdConversionFailed` error will be returned.230	///231	/// If the multilocation doesn't match the patterns listed above, the function returns `Ok(None)`,232	/// identifying that the given multilocation doesn't correspond to a local collection.233	fn native_asset_location_to_collection(234		asset_location: &MultiLocation,235	) -> Result<Option<CollectionId>, XcmError> {236		let self_location = T::SelfLocation::get();237238		if *asset_location == Here.into() {239			Ok(Some(NATIVE_FUNGIBLE_COLLECTION_ID))240		} else if *asset_location == self_location {241			Ok(Some(NATIVE_FUNGIBLE_COLLECTION_ID))242		} else if asset_location.parents == self_location.parents {243			match asset_location244				.interior245				.match_and_split(&self_location.interior)246			{247				Some(GeneralIndex(collection_id)) => Ok(Some(CollectionId(248					(*collection_id)249						.try_into()250						.map_err(|_| XcmExecutorError::AssetIdConversionFailed)?,251				))),252				_ => Ok(None),253			}254		} else {255			Ok(None)256		}257	}258259	/// Converts a multiasset to a Unique Network's collection (either local or the foreign one).260	///261	/// The function will try to convert the multiasset's reserve location262	/// to the Unique Network's local collection.263	///264	/// If the multilocation doesn't correspond to a local collection,265	/// the function will check if the reserve location has the corresponding266	/// derivative Unique Network's collection, and will return the said collection ID if found.267	///268	/// If all of the above have failed, the `AssetIdConversionFailed` error will be returned.269	fn multiasset_to_collection(asset: &MultiAsset) -> Result<CollectionId, XcmError> {270		let AssetId::Concrete(asset_reserve_location) = asset.id else {271			return Err(XcmExecutorError::AssetNotHandled.into());272		};273274		Self::native_asset_location_to_collection(&asset_reserve_location)?275			.or_else(|| Self::foreign_reserve_location_to_collection(asset_reserve_location))276			.ok_or_else(|| XcmExecutorError::AssetIdConversionFailed.into())277	}278279	/// Converts an XCM asset instance to the Unique Network's token ID.280	///281	/// The asset instance corresponds to the Unique Network's token ID if it is in the following format:282	/// `AssetInstance::Index(<token ID>)`.283	///284	/// If the asset instance is not in the valid format or the `<token ID>` points to a non-existent token,285	/// the `AssetNotFound` error will be returned.286	fn native_asset_instance_to_token_id(287		asset_instance: &AssetInstance,288	) -> Result<TokenId, XcmError> {289		match asset_instance {290			AssetInstance::Index(token_id) => Ok(TokenId(291				(*token_id)292					.try_into()293					.map_err(|_| XcmError::AssetNotFound)?,294			)),295			_ => Err(XcmError::AssetNotFound),296		}297	}298299	/// Obtains the token ID of the `asset_instance` in the collection.300	///301	/// Returns `Ok(None)` only if the `asset_instance` is a part of a foreign collection302	/// and the item hasn't yet been created on this blockchain.303	///304	/// If the `asset_instance` exists and it is a part of a foreign collection, the function will return `Ok(Some(<token ID>))`.305	///306	/// If the `asset_instance` is a part of a local collection,307	/// the function will return either `Ok(Some(<token ID>))` or an error if the token is not found.308	fn asset_instance_to_token_id(309		collection_id: CollectionId,310		asset_instance: &AssetInstance,311	) -> Result<Option<TokenId>, XcmError> {312		if <CollectionToForeignReserveLocation<T>>::contains_key(collection_id) {313			Ok(Self::foreign_reserve_asset_instance_to_token_id(314				collection_id,315				asset_instance,316			))317		} else {318			Self::native_asset_instance_to_token_id(asset_instance).map(Some)319		}320	}321322	/// Creates a foreign item in the the collection.323	fn create_foreign_asset_instance(324		xcm_ext: &dyn XcmExtensions<T>,325		collection_id: CollectionId,326		asset_instance: &AssetInstance,327		to: T::CrossAccountId,328	) -> DispatchResult {329		let asset_instance_encoded = asset_instance.encode();330331		let derivative_token_id = xcm_ext.create_item(332			&Self::pallet_account(),333			to,334			CreateItemData::NFT(CreateNftData {335				properties: vec![Property {336					key: Self::reserve_asset_instance_property_key(),337					value: asset_instance_encoded338						.try_into()339						.expect("asset instance length <= 32 bytes which is less than value length limit; qed"),340				}]341				.try_into()342				.expect("just one property can always be stored; qed"),343			}),344			&ZeroBudget,345		)?;346347		<ForeignReserveAssetInstanceToTokenId<T>>::insert(348			collection_id,349			asset_instance,350			derivative_token_id,351		);352353		Ok(())354	}355356	/// Deposits an asset instance to the `to` account.357	///358	/// Either transfers an existing item from the pallet's account359	/// or creates a foreign item.360	fn deposit_asset_instance(361		xcm_ext: &dyn XcmExtensions<T>,362		collection_id: CollectionId,363		asset_instance: &AssetInstance,364		to: T::CrossAccountId,365	) -> XcmResult {366		let deposit_result = if let Some(token_id) =367			Self::asset_instance_to_token_id(collection_id, asset_instance)?368		{369			let depositor = &Self::pallet_account();370			let from = depositor;371			let amount = 1;372373			xcm_ext.transfer_item(depositor, from, &to, token_id, amount, &ZeroBudget)374		} else {375			Self::create_foreign_asset_instance(xcm_ext, collection_id, asset_instance, to)376		};377378		deposit_result379			.map_err(|_| XcmError::FailedToTransactAsset("non-fungible item deposit failed"))380	}381382	/// Withdraws an asset instance from the `from` account.383	///384	/// Transfers the asset instance to the pallet's account.385	///386	/// Won't withdraw the instance if it has children.387	fn withdraw_asset_instance(388		xcm_ext: &dyn XcmExtensions<T>,389		collection_id: CollectionId,390		asset_instance: &AssetInstance,391		from: T::CrossAccountId,392	) -> XcmResult {393		let token_id = Self::asset_instance_to_token_id(collection_id, &asset_instance)?394			.ok_or(XcmError::AssetNotFound)?;395396		if xcm_ext.token_has_children(token_id) {397			return Err(XcmError::Unimplemented);398		}399400		let depositor = &from;401		let to = Self::pallet_account();402		let amount = 1;403		xcm_ext404			.transfer_item(depositor, &from, &to, token_id, amount, &ZeroBudget)405			.map_err(|_| XcmError::FailedToTransactAsset("nonfungible item withdraw failed"))?;406407		Ok(())408	}409}410411impl<T: Config> TransactAsset for Pallet<T> {412	fn can_check_in(413		_origin: &MultiLocation,414		_what: &MultiAsset,415		_context: &XcmContext,416	) -> XcmResult {417		Err(XcmError::Unimplemented)418	}419420	fn check_in(_origin: &MultiLocation, _what: &MultiAsset, _context: &XcmContext) {}421422	fn can_check_out(423		_dest: &MultiLocation,424		_what: &MultiAsset,425		_context: &XcmContext,426	) -> XcmResult {427		Err(XcmError::Unimplemented)428	}429430	fn check_out(_dest: &MultiLocation, _what: &MultiAsset, _context: &XcmContext) {}431432	fn deposit_asset(what: &MultiAsset, to: &MultiLocation, _context: &XcmContext) -> XcmResult {433		let to = T::LocationToAccountId::convert_location(to)434			.ok_or(XcmExecutorError::AccountIdConversionFailed)?;435436		let collection_id = Self::multiasset_to_collection(what)?;437		let dispatch =438			T::CollectionDispatch::dispatch(collection_id).map_err(|_| XcmError::AssetNotFound)?;439440		let collection = dispatch.as_dyn();441		let xcm_ext = collection.xcm_extensions().ok_or(XcmError::Unimplemented)?;442443		match what.fun {444			Fungibility::Fungible(amount) => xcm_ext445				.create_item(446					&Self::pallet_account(),447					to,448					CreateItemData::Fungible(CreateFungibleData { value: amount }),449					&ZeroBudget,450				)451				.map(|_| ())452				.map_err(|_| XcmError::FailedToTransactAsset("fungible item deposit failed")),453454			Fungibility::NonFungible(asset_instance) => {455				Self::deposit_asset_instance(xcm_ext, collection_id, &asset_instance, to)456			}457		}458	}459460	fn withdraw_asset(461		what: &MultiAsset,462		from: &MultiLocation,463		_maybe_context: Option<&XcmContext>,464	) -> Result<staging_xcm_executor::Assets, XcmError> {465		let from = T::LocationToAccountId::convert_location(from)466			.ok_or(XcmExecutorError::AccountIdConversionFailed)?;467468		let collection_id = Self::multiasset_to_collection(what)?;469		let dispatch =470			T::CollectionDispatch::dispatch(collection_id).map_err(|_| XcmError::AssetNotFound)?;471472		let collection = dispatch.as_dyn();473		let xcm_ext = collection.xcm_extensions().ok_or(XcmError::NoPermission)?;474475		match what.fun {476			Fungibility::Fungible(amount) => xcm_ext477				.burn_item(from, TokenId::default(), amount)478				.map_err(|_| XcmError::FailedToTransactAsset("fungible item withdraw failed"))?,479480			Fungibility::NonFungible(asset_instance) => {481				Self::withdraw_asset_instance(xcm_ext, collection_id, &asset_instance, from)?;482			}483		}484485		Ok(what.clone().into())486	}487488	fn internal_transfer_asset(489		what: &MultiAsset,490		from: &MultiLocation,491		to: &MultiLocation,492		_context: &XcmContext,493	) -> Result<staging_xcm_executor::Assets, XcmError> {494		let from = T::LocationToAccountId::convert_location(from)495			.ok_or(XcmExecutorError::AccountIdConversionFailed)?;496497		let to = T::LocationToAccountId::convert_location(to)498			.ok_or(XcmExecutorError::AccountIdConversionFailed)?;499500		let collection_id = Self::multiasset_to_collection(what)?;501502		let dispatch =503			T::CollectionDispatch::dispatch(collection_id).map_err(|_| XcmError::AssetNotFound)?;504		let collection = dispatch.as_dyn();505		let xcm_ext = collection.xcm_extensions().ok_or(XcmError::NoPermission)?;506507		let depositor = &from;508509		let token_id;510		let amount;511		let map_error: fn(DispatchError) -> XcmError;512513		match what.fun {514			Fungibility::Fungible(fungible_amount) => {515				token_id = TokenId::default();516				amount = fungible_amount;517				map_error = |_| XcmError::FailedToTransactAsset("fungible item transfer failed");518			}519520			Fungibility::NonFungible(asset_instance) => {521				token_id = Self::asset_instance_to_token_id(collection_id, &asset_instance)?522					.ok_or(XcmError::AssetNotFound)?;523524				amount = 1;525				map_error = |_| XcmError::FailedToTransactAsset("nonfungible item transfer failed")526			}527		}528529		xcm_ext530			.transfer_item(depositor, &from, &to, token_id, amount, &ZeroBudget)531			.map_err(map_error)?;532533		Ok(what.clone().into())534	}535}536537pub struct CurrencyIdConvert<T: Config>(PhantomData<T>);538impl<T: Config> sp_runtime::traits::Convert<CollectionId, Option<MultiLocation>>539	for CurrencyIdConvert<T>540{541	fn convert(collection_id: CollectionId) -> Option<MultiLocation> {542		if collection_id == NATIVE_FUNGIBLE_COLLECTION_ID {543			Some(Here.into())544		} else {545			<Pallet<T>>::collection_to_foreign_reserve_location(collection_id).or_else(|| {546				T::SelfLocation::get()547					.pushed_with_interior(GeneralIndex(collection_id.0.into()))548					.ok()549			})550		}551	}552}553554pub use frame_support::{555	traits::{556		fungibles::Balanced, tokens::currency::Currency as CurrencyT, OnUnbalanced as OnUnbalancedT,557	},558	weights::{WeightToFee, WeightToFeePolynomial},559};560561pub struct FreeForAll;562563impl WeightTrader for FreeForAll {564	fn new() -> Self {565		Self566	}567568	fn buy_weight(569		&mut self,570		weight: Weight,571		payment: Assets,572		_xcm: &XcmContext,573	) -> Result<Assets, XcmError> {574		log::trace!(target: "fassets::weight", "buy_weight weight: {:?}, payment: {:?}", weight, payment);575		Ok(payment)576	}577}
modifiedpallets/fungible/src/benchmarking.rsdiffbeforeafterboth
--- a/pallets/fungible/src/benchmarking.rs
+++ b/pallets/fungible/src/benchmarking.rs
@@ -31,7 +31,7 @@
 		owner,
 		CollectionMode::Fungible(0),
 		|owner: T::CrossAccountId, data| {
-			<PalletCommon<T>>::init_collection(owner.clone(), Some(owner), data)
+			<PalletCommon<T>>::init_collection(owner.clone(), Some(owner), false, data)
 		},
 		FungibleHandle::cast,
 	)
modifiedpallets/nonfungible/src/benchmarking.rsdiffbeforeafterboth
--- a/pallets/nonfungible/src/benchmarking.rs
+++ b/pallets/nonfungible/src/benchmarking.rs
@@ -58,7 +58,7 @@
 		owner,
 		CollectionMode::NFT,
 		|owner: T::CrossAccountId, data| {
-			<PalletCommon<T>>::init_collection(owner.clone(), Some(owner), data)
+			<PalletCommon<T>>::init_collection(owner.clone(), Some(owner), false, data)
 		},
 		NonfungibleHandle::cast,
 	)
modifiedpallets/refungible/src/benchmarking.rsdiffbeforeafterboth
--- a/pallets/refungible/src/benchmarking.rs
+++ b/pallets/refungible/src/benchmarking.rs
@@ -20,6 +20,7 @@
 use pallet_common::{
 	bench_init,
 	benchmarking::{create_collection_raw, property_key, property_value},
+	Pallet as PalletCommon,
 };
 use sp_std::prelude::*;
 use up_data_structs::{
@@ -61,7 +62,9 @@
 	create_collection_raw(
 		owner,
 		CollectionMode::ReFungible,
-		|owner: T::CrossAccountId, data| <Pallet<T>>::init_collection(owner.clone(), owner, data),
+		|owner: T::CrossAccountId, data| {
+			<PalletCommon<T>>::init_collection(owner.clone(), Some(owner), false, data)
+		},
 		RefungibleHandle::cast,
 	)
 }
modifiedpallets/refungible/src/lib.rsdiffbeforeafterboth
--- a/pallets/refungible/src/lib.rs
+++ b/pallets/refungible/src/lib.rs
@@ -103,7 +103,7 @@
 use sp_runtime::{ArithmeticError, DispatchError, DispatchResult, TransactionOutcome};
 use sp_std::{collections::btree_map::BTreeMap, vec, vec::Vec};
 use up_data_structs::{
-	budget::Budget, mapping::TokenAddressMapping, AccessMode, CollectionId, CreateCollectionData,
+	budget::Budget, mapping::TokenAddressMapping, AccessMode, CollectionId,
 	CreateRefungibleExMultipleOwners, PropertiesPermissionMap, Property, PropertyKey,
 	PropertyKeyPermission, PropertyScope, PropertyValue, TokenId, TokenOwnerError,
 	TokenProperties as TokenPropertiesT, MAX_REFUNGIBLE_PIECES,
@@ -296,19 +296,6 @@
 
 // unchecked calls skips any permission checks
 impl<T: Config> Pallet<T> {
-	/// Create RFT collection
-	///
-	/// `init_collection` will take non-refundable deposit for collection creation.
-	///
-	/// - `data`: Contains settings for collection limits and permissions.
-	pub fn init_collection(
-		owner: T::CrossAccountId,
-		payer: T::CrossAccountId,
-		data: CreateCollectionData<T::CrossAccountId>,
-	) -> Result<CollectionId, DispatchError> {
-		<PalletCommon<T>>::init_collection(owner, Some(payer), data)
-	}
-
 	/// Destroy RFT collection
 	///
 	/// `destroy_collection` will throw error if collection contains any tokens.
modifiedprimitives/data-structs/src/lib.rsdiffbeforeafterboth
--- a/primitives/data-structs/src/lib.rs
+++ b/primitives/data-structs/src/lib.rs
@@ -380,7 +380,7 @@
 pub struct CollectionFlags {
 	/// Reserved flag
 	#[bondrewd(bits = "0..1")]
-	pub reserved_0: bool,
+	pub foreign: bool,
 	/// Supports ERC721Metadata
 	#[bondrewd(bits = "1..2")]
 	pub erc721metadata: bool,
@@ -395,7 +395,7 @@
 
 impl CollectionFlags {
 	pub fn is_allowed_for_user(self) -> bool {
-		!self.reserved_0 && !self.external && self.reserved == 0
+		!self.foreign && !self.external && self.reserved == 0
 	}
 }
 
@@ -461,6 +461,8 @@
 
 #[derive(Debug, Encode, Decode, Clone, PartialEq, TypeInfo, Serialize, Deserialize)]
 pub struct RpcCollectionFlags {
+	/// Is collection is foreign.
+	pub foreign: bool,
 	/// Collection supports ERC721Metadata.
 	pub erc721metadata: bool,
 }
@@ -503,7 +505,7 @@
 	pub read_only: bool,
 
 	/// Extra collection flags
-	#[version(2.., upper(RpcCollectionFlags {erc721metadata: false}))]
+	#[version(2.., upper(RpcCollectionFlags {foreign: false, erc721metadata: false}))]
 	pub flags: RpcCollectionFlags,
 }
 
@@ -540,6 +542,7 @@
 			read_only: true,
 
 			flags: RpcCollectionFlags {
+				foreign: false,
 				erc721metadata: 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,4 +1,6 @@
 use frame_support::{parameter_types, PalletId};
+#[cfg(not(feature = "governance"))]
+use frame_system::EnsureRoot;
 use pallet_evm::account::CrossAccountId;
 use sp_core::H160;
 use staging_xcm::prelude::*;
@@ -6,10 +8,6 @@
 
 #[cfg(feature = "governance")]
 use crate::runtime_common::config::governance;
-
-#[cfg(not(feature = "governance"))]
-use frame_system::EnsureRoot;
-
 use crate::{
 	runtime_common::config::{
 		ethereum::CrossAccountId as ConfigCrossAccountId,
modifiedruntime/common/dispatch.rsdiffbeforeafterboth
--- a/runtime/common/dispatch.rs
+++ b/runtime/common/dispatch.rs
@@ -66,9 +66,10 @@
 		}
 	}
 
-	fn create(
+	fn create_internal(
 		sender: T::CrossAccountId,
 		payer: Option<T::CrossAccountId>,
+		is_special_collection: bool,
 		data: CreateCollectionData<T::CrossAccountId>,
 	) -> Result<CollectionId, DispatchError> {
 		match data.mode {
@@ -86,7 +87,7 @@
 			_ => {}
 		};
 
-		<PalletCommon<T>>::init_collection(sender, payer, data)
+		<PalletCommon<T>>::init_collection(sender, payer, is_special_collection, data)
 	}
 
 	fn destroy(sender: T::CrossAccountId, collection_id: CollectionId) -> DispatchResult {