git.delta.rocks / unique-network / refs/commits / 00b9fd625e6d

difftreelog

feat initial rmrk send impl

Daniel Shiposha2022-06-03parent: #70623cc.patch.diff
in: master

10 files changed

modifiedpallets/common/src/lib.rsdiffbeforeafterboth
--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -1298,6 +1298,7 @@
 		spender: T::CrossAccountId,
 		token: TokenId,
 		amount: u128,
+		nesting_budget: &dyn Budget,
 	) -> DispatchResultWithPostInfo;
 	fn transfer_from(
 		&self,
modifiedpallets/fungible/src/common.rsdiffbeforeafterboth
--- a/pallets/fungible/src/common.rs
+++ b/pallets/fungible/src/common.rs
@@ -226,6 +226,7 @@
 		spender: T::CrossAccountId,
 		token: TokenId,
 		amount: u128,
+		_nesting_budget: &dyn Budget,
 	) -> DispatchResultWithPostInfo {
 		ensure!(
 			token == TokenId::default(),
modifiedpallets/nonfungible/src/common.rsdiffbeforeafterboth
--- a/pallets/nonfungible/src/common.rs
+++ b/pallets/nonfungible/src/common.rs
@@ -308,14 +308,15 @@
 		spender: T::CrossAccountId,
 		token: TokenId,
 		amount: u128,
+		nesting_budget: &dyn Budget,
 	) -> DispatchResultWithPostInfo {
 		ensure!(amount <= 1, <Error<T>>::NonfungibleItemsHaveNoAmount);
 
 		with_weight(
 			if amount == 1 {
-				<Pallet<T>>::set_allowance(self, &sender, token, Some(&spender))
+				<Pallet<T>>::set_allowance(self, &sender, token, Some(&spender), nesting_budget)
 			} else {
-				<Pallet<T>>::set_allowance(self, &sender, token, None)
+				<Pallet<T>>::set_allowance(self, &sender, token, None, nesting_budget)
 			},
 			<CommonWeights<T>>::approve(),
 		)
modifiedpallets/nonfungible/src/erc.rsdiffbeforeafterboth
--- a/pallets/nonfungible/src/erc.rs
+++ b/pallets/nonfungible/src/erc.rs
@@ -269,8 +269,11 @@
 		let caller = T::CrossAccountId::from_eth(caller);
 		let approved = T::CrossAccountId::from_eth(approved);
 		let token = token_id.try_into()?;
+		let budget = self
+			.recorder
+			.weight_calls_budget(<StructureWeight<T>>::find_parent());
 
-		<Pallet<T>>::set_allowance(self, &caller, token, Some(&approved))
+		<Pallet<T>>::set_allowance(self, &caller, token, Some(&approved), &budget)
 			.map_err(dispatch_to_evm::<T>)?;
 		Ok(())
 	}
modifiedpallets/nonfungible/src/lib.rsdiffbeforeafterboth
--- a/pallets/nonfungible/src/lib.rs
+++ b/pallets/nonfungible/src/lib.rs
@@ -878,6 +878,7 @@
 		sender: &T::CrossAccountId,
 		token: TokenId,
 		spender: Option<&T::CrossAccountId>,
+		nesting_budget: &dyn Budget,
 	) -> DispatchResult {
 		if collection.permissions.access() == AccessMode::AllowList {
 			collection.check_allowlist(sender)?;
@@ -889,9 +890,16 @@
 		if let Some(spender) = spender {
 			<PalletCommon<T>>::ensure_correct_receiver(spender)?;
 		}
-		let token_data =
-			<TokenData<T>>::get((collection.id, token)).ok_or(<CommonError<T>>::TokenNotFound)?;
-		if &token_data.owner != sender {
+
+		let is_owned = <PalletStructure<T>>::check_indirectly_owned(
+			sender.clone(),
+			collection.id,
+			token,
+			None,
+			nesting_budget
+		)?;
+
+		if !is_owned {
 			ensure!(
 				collection.ignores_owned_amount(sender),
 				<CommonError<T>>::CantApproveMoreThanOwned
@@ -918,6 +926,9 @@
 			// `from`, `to` checked in [`transfer`]
 			collection.check_allowlist(spender)?;
 		}
+		if <Allowance<T>>::get((collection.id, token)).as_ref() == Some(spender) {
+			return Ok(());
+		}
 		if let Some(source) = T::CrossTokenAddressMapping::address_to_token(from) {
 			// TODO: should collection owner be allowed to perform this transfer?
 			ensure!(
@@ -930,9 +941,6 @@
 				)?,
 				<CommonError<T>>::ApprovedValueTooLow,
 			);
-			return Ok(());
-		}
-		if <Allowance<T>>::get((collection.id, token)).as_ref() == Some(spender) {
 			return Ok(());
 		}
 		ensure!(
modifiedpallets/proxy-rmrk-core/src/lib.rsdiffbeforeafterboth
before · pallets/proxy-rmrk-core/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#![cfg_attr(not(feature = "std"), no_std)]1819use frame_support::{pallet_prelude::*, transactional, BoundedVec, dispatch::DispatchResult};20use frame_system::{pallet_prelude::*, ensure_signed};21use sp_runtime::{DispatchError, Permill, traits::StaticLookup};22use sp_std::vec::Vec;23use up_data_structs::*;24use pallet_common::{25	Pallet as PalletCommon, Error as CommonError, CollectionHandle, CommonCollectionOperations,26};27use pallet_nonfungible::{Pallet as PalletNft, NonfungibleHandle, TokenData};28use pallet_structure::Pallet as PalletStructure;29use pallet_evm::account::CrossAccountId;30use core::convert::AsRef;3132pub use pallet::*;3334pub mod misc;35pub mod property;3637use misc::*;38pub use property::*;3940use RmrkProperty::*;4142#[frame_support::pallet]43pub mod pallet {44	use super::*;45	use pallet_evm::account;4647	#[pallet::config]48	pub trait Config:49		frame_system::Config + pallet_common::Config + pallet_nonfungible::Config + account::Config50	{51		type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;52	}5354	#[pallet::storage]55	#[pallet::getter(fn collection_index)]56	pub type CollectionIndex<T: Config> = StorageValue<_, RmrkCollectionId, ValueQuery>;5758	#[pallet::storage]59	#[pallet::getter(fn collection_index_map)]60	pub type CollectionIndexMap<T: Config> =61		StorageMap<_, Twox64Concat, RmrkCollectionId, CollectionId, ValueQuery>;6263	#[pallet::pallet]64	#[pallet::generate_store(pub(super) trait Store)]65	pub struct Pallet<T>(_);6667	#[pallet::event]68	#[pallet::generate_deposit(pub(super) fn deposit_event)]69	pub enum Event<T: Config> {70		CollectionCreated {71			issuer: T::AccountId,72			collection_id: RmrkCollectionId,73		},74		CollectionDestroyed {75			issuer: T::AccountId,76			collection_id: RmrkCollectionId,77		},78		IssuerChanged {79			old_issuer: T::AccountId,80			new_issuer: T::AccountId,81			collection_id: RmrkCollectionId,82		},83		CollectionLocked {84			issuer: T::AccountId,85			collection_id: RmrkCollectionId,86		},87		NftMinted {88			owner: T::AccountId,89			collection_id: RmrkCollectionId,90			nft_id: RmrkNftId,91		},92		NFTBurned {93			owner: T::AccountId,94			nft_id: RmrkNftId,95		},96		PropertySet {97			collection_id: RmrkCollectionId,98			maybe_nft_id: Option<RmrkNftId>,99			key: RmrkKeyString,100			value: RmrkValueString,101		},102		ResourceAdded {103			nft_id: RmrkNftId,104			resource_id: RmrkResourceId,105		},106		ResourceRemoval {107			nft_id: RmrkNftId,108			resource_id: RmrkResourceId,109		},110	}111112	#[pallet::error]113	pub enum Error<T> {114		/* Unique-specific events */115		CorruptedCollectionType,116		NftTypeEncodeError,117		RmrkPropertyKeyIsTooLong,118		RmrkPropertyValueIsTooLong,119120		/* RMRK compatible events */121		CollectionNotEmpty,122		NoAvailableCollectionId,123		NoAvailableNftId,124		CollectionUnknown,125		NoPermission,126		CollectionFullOrLocked,127		ResourceDoesntExist,128	}129130	#[pallet::call]131	impl<T: Config> Pallet<T> {132		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]133		#[transactional]134		pub fn create_collection(135			origin: OriginFor<T>,136			metadata: RmrkString,137			max: Option<u32>,138			symbol: RmrkCollectionSymbol,139		) -> DispatchResult {140			let sender = ensure_signed(origin)?;141142			let limits = CollectionLimits {143				owner_can_transfer: Some(false),144				token_limit: max,145				..Default::default()146			};147148			let data = CreateCollectionData {149				limits: Some(limits),150				token_prefix: symbol151					.into_inner()152					.try_into()153					.map_err(|_| <CommonError<T>>::CollectionTokenPrefixLimitExceeded)?,154				..Default::default()155			};156157			<CollectionIndex<T>>::mutate(|n| *n += 1);158159			let unique_collection_id = Self::init_collection(160				T::CrossAccountId::from_sub(sender.clone()),161				data,162				[163					Self::rmrk_property(Metadata, &metadata)?,164					Self::rmrk_property(CollectionType, &misc::CollectionType::Regular)?,165				]166				.into_iter(),167			)?;168			let rmrk_collection_id = <CollectionIndex<T>>::get();169170			<CollectionIndexMap<T>>::insert(rmrk_collection_id, unique_collection_id);171172			Self::deposit_event(Event::CollectionCreated {173				issuer: sender,174				collection_id: rmrk_collection_id,175			});176177			Ok(())178		}179180		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]181		#[transactional]182		pub fn destroy_collection(183			origin: OriginFor<T>,184			collection_id: RmrkCollectionId,185		) -> DispatchResult {186			let sender = ensure_signed(origin)?;187			let cross_sender = T::CrossAccountId::from_sub(sender.clone());188189			let collection = Self::get_typed_nft_collection(190				Self::unique_collection_id(collection_id)?,191				misc::CollectionType::Regular,192			)?;193194			ensure!(195				collection.total_supply() == 0,196				<Error<T>>::CollectionNotEmpty197			);198199			<PalletNft<T>>::destroy_collection(collection, &cross_sender)200				.map_err(Self::map_common_err_to_proxy)?;201202			Self::deposit_event(Event::CollectionDestroyed {203				issuer: sender,204				collection_id,205			});206207			Ok(())208		}209210		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]211		#[transactional]212		pub fn change_collection_issuer(213			origin: OriginFor<T>,214			collection_id: RmrkCollectionId,215			new_issuer: <T::Lookup as StaticLookup>::Source,216		) -> DispatchResult {217			let sender = ensure_signed(origin)?;218219			let new_issuer = T::Lookup::lookup(new_issuer)?;220221			Self::change_collection_owner(222				Self::unique_collection_id(collection_id)?,223				misc::CollectionType::Regular,224				sender.clone(),225				new_issuer.clone(),226			)?;227228			Self::deposit_event(Event::IssuerChanged {229				old_issuer: sender,230				new_issuer,231				collection_id,232			});233234			Ok(())235		}236237		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]238		#[transactional]239		pub fn lock_collection(240			origin: OriginFor<T>,241			collection_id: RmrkCollectionId,242		) -> DispatchResult {243			let sender = ensure_signed(origin)?;244			let cross_sender = T::CrossAccountId::from_sub(sender.clone());245246			let collection = Self::get_typed_nft_collection(247				Self::unique_collection_id(collection_id)?,248				misc::CollectionType::Regular,249			)?;250251			Self::check_collection_owner(&collection, &cross_sender)?;252253			let token_count = collection.total_supply();254255			let mut collection = collection.into_inner();256			collection.limits.token_limit = Some(token_count);257			collection.save()?;258259			Self::deposit_event(Event::CollectionLocked {260				issuer: sender,261				collection_id,262			});263264			Ok(())265		}266267		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]268		#[transactional]269		pub fn mint_nft(270			origin: OriginFor<T>,271			owner: T::AccountId,272			collection_id: RmrkCollectionId,273			recipient: Option<T::AccountId>,274			royalty_amount: Option<Permill>,275			metadata: RmrkString,276		) -> DispatchResult {277			let sender = ensure_signed(origin)?;278			let sender = T::CrossAccountId::from_sub(sender);279			let cross_owner = T::CrossAccountId::from_sub(owner.clone());280281			let royalty_info = royalty_amount.map(|amount| rmrk::RoyaltyInfo {282				recipient: recipient.unwrap_or_else(|| owner.clone()),283				amount,284			});285286			let collection = Self::get_typed_nft_collection(287				Self::unique_collection_id(collection_id)?,288				misc::CollectionType::Regular,289			)?;290291			let nft_id = Self::create_nft(292				&sender,293				&cross_owner,294				&collection,295				[296					Self::rmrk_property(TokenType, &NftType::Regular)?,297					Self::rmrk_property(RoyaltyInfo, &royalty_info)?,298					Self::rmrk_property(Metadata, &metadata)?,299					Self::rmrk_property(Equipped, &false)?,300					Self::rmrk_property(301						ResourceCollection,302						&Self::init_collection(303							sender.clone(),304							CreateCollectionData {305								..Default::default()306							},307							[Self::rmrk_property(308								CollectionType,309								&misc::CollectionType::Resource,310							)?]311							.into_iter(),312						)?,313					)?, // todo possibly add limits to the collection if rmrk warrants them314					Self::rmrk_property(ResourcePriorities, &<Vec<u8>>::new())?,315				]316				.into_iter(),317			)318			.map_err(|err| match err {319				DispatchError::Arithmetic(_) => <Error<T>>::NoAvailableNftId.into(),320				err => Self::map_common_err_to_proxy(err),321			})?;322323			Self::deposit_event(Event::NftMinted {324				owner,325				collection_id,326				nft_id: nft_id.0,327			});328329			Ok(())330		}331332		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]333		#[transactional]334		pub fn burn_nft(335			origin: OriginFor<T>,336			collection_id: RmrkCollectionId,337			nft_id: RmrkNftId,338		) -> DispatchResult {339			let sender = ensure_signed(origin)?;340			let cross_sender = T::CrossAccountId::from_sub(sender.clone());341342			Self::destroy_nft(343				cross_sender,344				Self::unique_collection_id(collection_id)?,345				misc::CollectionType::Regular,346				nft_id.into(),347			)?;348349			Self::deposit_event(Event::NFTBurned {350				owner: sender,351				nft_id,352			});353354			Ok(())355		}356357		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]358		#[transactional]359		pub fn set_property(360			origin: OriginFor<T>,361			#[pallet::compact] rmrk_collection_id: RmrkCollectionId,362			maybe_nft_id: Option<RmrkNftId>,363			key: RmrkKeyString,364			value: RmrkValueString,365		) -> DispatchResult {366			let sender = ensure_signed(origin)?;367			let sender = T::CrossAccountId::from_sub(sender);368369			let collection_id = Self::unique_collection_id(rmrk_collection_id)?;370371			match maybe_nft_id {372				Some(nft_id) => {373					let token_id: TokenId = nft_id.into();374375					Self::ensure_nft_owner(collection_id, token_id, &sender)?;376					Self::ensure_nft_type(collection_id, token_id, NftType::Regular)?;377378					<PalletNft<T>>::set_scoped_token_property(379						collection_id,380						token_id,381						PropertyScope::Rmrk,382						Self::rmrk_property(UserProperty(key.as_slice()), &value)?,383					)?;384				}385				None => {386					let collection = Self::get_typed_nft_collection(387						collection_id,388						misc::CollectionType::Regular,389					)?;390391					Self::check_collection_owner(&collection, &sender)?;392393					<PalletCommon<T>>::set_scoped_collection_property(394						collection_id,395						PropertyScope::Rmrk,396						Self::rmrk_property(UserProperty(key.as_slice()), &value)?,397					)?;398				}399			}400401			Self::deposit_event(Event::PropertySet {402				collection_id: rmrk_collection_id,403				maybe_nft_id,404				key,405				value,406			});407408			Ok(())409		}410411		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]412		#[transactional]413		pub fn add_basic_resource(414			origin: OriginFor<T>,415			collection_id: RmrkCollectionId,416			nft_id: RmrkNftId,417			resource: RmrkBasicResource,418		) -> DispatchResult {419			let sender = ensure_signed(origin.clone())?;420421			let resource_id = Self::resource_add(422				sender,423				Self::unique_collection_id(collection_id)?,424				nft_id.into(),425				[426					Self::rmrk_property(TokenType, &NftType::Resource)?,427					Self::rmrk_property(ResourceType, &misc::ResourceType::Basic)?,428					Self::rmrk_property(Src, &resource.src)?,429					Self::rmrk_property(Metadata, &resource.metadata)?,430					Self::rmrk_property(License, &resource.license)?,431					Self::rmrk_property(Thumb, &resource.thumb)?,432				]433				.into_iter(),434			)?;435436			Self::deposit_event(Event::ResourceAdded {437				nft_id,438				resource_id,439			});440			Ok(())441		}442443		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]444		#[transactional]445		pub fn add_composable_resource(446			origin: OriginFor<T>,447			collection_id: RmrkCollectionId,448			nft_id: RmrkNftId,449			_resource_id: RmrkBoundedResource,450			resource: RmrkComposableResource,451		) -> DispatchResult {452			let sender = ensure_signed(origin.clone())?;453454			let resource_id = Self::resource_add(455				sender,456				Self::unique_collection_id(collection_id)?,457				nft_id.into(),458				[459					Self::rmrk_property(TokenType, &NftType::Resource)?,460					Self::rmrk_property(ResourceType, &misc::ResourceType::Composable)?,461					Self::rmrk_property(Parts, &resource.parts)?,462					Self::rmrk_property(Base, &resource.base)?,463					Self::rmrk_property(Src, &resource.src)?,464					Self::rmrk_property(Metadata, &resource.metadata)?,465					Self::rmrk_property(License, &resource.license)?,466					Self::rmrk_property(Thumb, &resource.thumb)?,467				]468				.into_iter(),469			)?;470471			Self::deposit_event(Event::ResourceAdded {472				nft_id,473				resource_id,474			});475			Ok(())476		}477478		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]479		#[transactional]480		pub fn add_slot_resource(481			origin: OriginFor<T>,482			collection_id: RmrkCollectionId,483			nft_id: RmrkNftId,484			resource: RmrkSlotResource,485		) -> DispatchResult {486			let sender = ensure_signed(origin.clone())?;487488			let resource_id = Self::resource_add(489				sender,490				Self::unique_collection_id(collection_id)?,491				nft_id.into(),492				[493					Self::rmrk_property(TokenType, &NftType::Resource)?,494					Self::rmrk_property(ResourceType, &misc::ResourceType::Slot)?,495					Self::rmrk_property(Base, &resource.base)?,496					Self::rmrk_property(Src, &resource.src)?,497					Self::rmrk_property(Metadata, &resource.metadata)?,498					Self::rmrk_property(Slot, &resource.slot)?,499					Self::rmrk_property(License, &resource.license)?,500					Self::rmrk_property(Thumb, &resource.thumb)?,501				]502				.into_iter(),503			)?;504505			Self::deposit_event(Event::ResourceAdded {506				nft_id,507				resource_id,508			});509			Ok(())510		}511512		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]513		#[transactional]514		pub fn remove_resource(515			origin: OriginFor<T>,516			collection_id: RmrkCollectionId,517			nft_id: RmrkNftId,518			resource_id: RmrkResourceId,519		) -> DispatchResult {520			let sender = ensure_signed(origin.clone())?;521522			Self::resource_remove(523				sender,524				Self::unique_collection_id(collection_id)?,525				nft_id.into(),526				resource_id.into(),527			)?;528529			Self::deposit_event(Event::ResourceRemoval {530				nft_id,531				resource_id,532			});533			Ok(())534		}535	}536}537538impl<T: Config> Pallet<T> {539	pub fn rmrk_property_key(rmrk_key: RmrkProperty) -> Result<PropertyKey, DispatchError> {540		let key = rmrk_key.to_key::<T>()?;541542		let scoped_key = PropertyScope::Rmrk543			.apply(key)544			.map_err(|_| <Error<T>>::RmrkPropertyKeyIsTooLong)?;545546		Ok(scoped_key)547	}548549	// todo think about renaming these550	pub fn rmrk_property<E: Encode>(551		rmrk_key: RmrkProperty,552		value: &E,553	) -> Result<Property, DispatchError> {554		let key = rmrk_key.to_key::<T>()?;555556		let value = value557			.encode()558			.try_into()559			.map_err(|_| <Error<T>>::RmrkPropertyValueIsTooLong)?;560561		let property = Property { key, value };562563		Ok(property)564	}565566	pub fn decode_property<D: Decode>(vec: PropertyValue) -> Result<D, DispatchError> {567		vec.decode()568			.map_err(|_| <Error<T>>::RmrkPropertyValueIsTooLong.into())569	}570571	pub fn rebind<L, S>(vec: &BoundedVec<u8, L>) -> Result<BoundedVec<u8, S>, DispatchError>572	where573		BoundedVec<u8, S>: TryFrom<Vec<u8>>,574	{575		vec.rebind()576			.map_err(|_| <Error<T>>::RmrkPropertyValueIsTooLong.into())577	}578579	fn init_collection(580		sender: T::CrossAccountId,581		data: CreateCollectionData<T::AccountId>,582		properties: impl Iterator<Item = Property>,583	) -> Result<CollectionId, DispatchError> {584		let collection_id = <PalletNft<T>>::init_collection(sender, data);585586		if let Err(DispatchError::Arithmetic(_)) = &collection_id {587			return Err(<Error<T>>::NoAvailableCollectionId.into());588		}589590		<PalletCommon<T>>::set_scoped_collection_properties(591			collection_id?,592			PropertyScope::Rmrk,593			properties,594		)?;595596		collection_id597	}598599	pub fn create_nft(600		sender: &T::CrossAccountId,601		owner: &T::CrossAccountId,602		collection: &NonfungibleHandle<T>,603		properties: impl Iterator<Item = Property>,604	) -> Result<TokenId, DispatchError> {605		let data = CreateNftExData {606			properties: BoundedVec::default(),607			owner: owner.clone(),608		};609610		let budget = budget::Value::new(2);611612		<PalletNft<T>>::create_item(collection, sender, data, &budget)?;613614		let nft_id = <PalletNft<T>>::current_token_id(collection.id);615616		<PalletNft<T>>::set_scoped_token_properties(617			collection.id,618			nft_id,619			PropertyScope::Rmrk,620			properties,621		)?;622623		Ok(nft_id)624	}625626	fn destroy_nft(627		sender: T::CrossAccountId,628		collection_id: CollectionId,629		collection_type: misc::CollectionType,630		token_id: TokenId,631	) -> DispatchResult {632		let collection = Self::get_typed_nft_collection(collection_id, collection_type)?;633634		<PalletNft<T>>::burn(&collection, &sender, token_id)635			.map_err(Self::map_common_err_to_proxy)?;636637		Ok(())638	}639640	fn resource_add(641		sender: T::AccountId,642		collection_id: CollectionId,643		token_id: TokenId,644		resource_properties: impl Iterator<Item = Property>,645	) -> Result<RmrkResourceId, DispatchError> {646		let collection =647			Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;648		ensure!(collection.owner == sender, Error::<T>::NoPermission);649650		// Check NFT lock status // todo depends on market, maybe later651		//ensure!(!Pallet::<T>::is_locked(collection_id, nft_id), pallet_uniques::Error::<T>::Locked);652653		let sender = T::CrossAccountId::from_sub(sender);654		let budget = budget::Value::new(10);655		let pending = !<PalletStructure<T>>::check_indirectly_owned(656			sender.clone(),657			collection_id,658			token_id,659			None,660			&budget,661		)?;662663		let resource_collection_id: CollectionId =664			Self::get_nft_property_decoded(collection_id, token_id, ResourceCollection)?;665		let resource_collection =666			Self::get_typed_nft_collection(resource_collection_id, misc::CollectionType::Resource)?;667668		// todo probably add extra connections to bases, slots, etc., when RMRK starts to use them669670		let resource_id = Self::create_nft(671			&sender, // todo owner of the nft?672			&sender,673			&resource_collection,674			resource_properties.chain(675				[676					Self::rmrk_property(PendingResourceAccept, &pending)?,677					Self::rmrk_property(PendingResourceRemoval, &false)?,678				]679				.into_iter(),680			),681		)682		.map_err(|err| match err {683			DispatchError::Arithmetic(_) => <Error<T>>::NoAvailableNftId.into(),684			err => Self::map_common_err_to_proxy(err),685		})?;686687		Ok(resource_id.0)688	}689690	fn resource_remove(691		sender: T::AccountId,692		collection_id: CollectionId,693		nft_id: TokenId,694		resource_id: TokenId,695	) -> DispatchResult {696		let collection =697			Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;698		ensure!(collection.owner == sender, Error::<T>::NoPermission);699700		let resource_collection_id: CollectionId =701			Self::get_nft_property_decoded(collection_id, nft_id, ResourceCollection)?;702		let resource_collection =703			Self::get_typed_nft_collection(resource_collection_id, misc::CollectionType::Resource)?;704		ensure!(705			<PalletNft<T>>::token_exists(&resource_collection, resource_id),706			Error::<T>::ResourceDoesntExist707		);708709		let budget = up_data_structs::budget::Value::new(10);710		let topmost_owner =711			<PalletStructure<T>>::find_topmost_owner(collection_id, nft_id, &budget)?;712713		let sender = T::CrossAccountId::from_sub(sender);714		if topmost_owner == sender {715			<PalletNft<T>>::burn(&collection, &sender, nft_id)716				.map_err(Self::map_common_err_to_proxy)?;717		} else {718			<PalletNft<T>>::set_scoped_token_property(719				collection_id,720				nft_id,721				PropertyScope::Rmrk,722				Self::rmrk_property(PendingResourceRemoval, &true)?,723			)?;724		}725726		Ok(())727	}728729	fn change_collection_owner(730		collection_id: CollectionId,731		collection_type: misc::CollectionType,732		sender: T::AccountId,733		new_owner: T::AccountId,734	) -> DispatchResult {735		let collection = Self::get_typed_nft_collection(collection_id, collection_type)?;736		Self::check_collection_owner(&collection, &T::CrossAccountId::from_sub(sender))?;737738		let mut collection = collection.into_inner();739740		collection.owner = new_owner;741		collection.save()742	}743744	fn check_collection_owner(745		collection: &NonfungibleHandle<T>,746		account: &T::CrossAccountId,747	) -> DispatchResult {748		collection749			.check_is_owner(account)750			.map_err(Self::map_common_err_to_proxy)751	}752753	pub fn last_collection_idx() -> RmrkCollectionId {754		<CollectionIndex<T>>::get()755	}756757	pub fn unique_collection_id(758		rmrk_collection_id: RmrkCollectionId,759	) -> Result<CollectionId, DispatchError> {760		<CollectionIndexMap<T>>::try_get(rmrk_collection_id)761			.map_err(|_| <Error<T>>::CollectionUnknown.into())762	}763764	pub fn get_nft_collection(765		collection_id: CollectionId,766	) -> Result<NonfungibleHandle<T>, DispatchError> {767		let collection = <CollectionHandle<T>>::try_get(collection_id)768			.map_err(|_| <Error<T>>::CollectionUnknown)?;769770		match collection.mode {771			CollectionMode::NFT => Ok(NonfungibleHandle::cast(collection)),772			_ => Err(<Error<T>>::CollectionUnknown.into()),773		}774	}775776	pub fn collection_exists(collection_id: CollectionId) -> bool {777		<CollectionHandle<T>>::try_get(collection_id).is_ok()778	}779780	pub fn get_collection_property(781		collection_id: CollectionId,782		key: RmrkProperty,783	) -> Result<PropertyValue, DispatchError> {784		let collection_property = <PalletCommon<T>>::collection_properties(collection_id)785			.get(&Self::rmrk_property_key(key)?)786			.ok_or(<Error<T>>::CollectionUnknown)?787			.clone();788789		Ok(collection_property)790	}791792	pub fn get_collection_property_decoded<V: Decode>(793		collection_id: CollectionId,794		key: RmrkProperty,795	) -> Result<V, DispatchError> {796		Self::decode_property(Self::get_collection_property(collection_id, key)?)797	}798799	pub fn get_collection_type(800		collection_id: CollectionId,801	) -> Result<misc::CollectionType, DispatchError> {802		Self::get_collection_property_decoded(collection_id, CollectionType)803			.map_err(|_| <Error<T>>::CorruptedCollectionType.into())804	}805806	pub fn ensure_collection_type(807		collection_id: CollectionId,808		collection_type: misc::CollectionType,809	) -> DispatchResult {810		let actual_type = Self::get_collection_type(collection_id)?;811		ensure!(812			actual_type == collection_type,813			<CommonError<T>>::NoPermission814		);815816		Ok(())817	}818819	pub fn get_typed_nft_collection(820		collection_id: CollectionId,821		collection_type: misc::CollectionType,822	) -> Result<NonfungibleHandle<T>, DispatchError> {823		Self::ensure_collection_type(collection_id, collection_type)?;824825		Self::get_nft_collection(collection_id)826	}827828	pub fn get_nft_property(829		collection_id: CollectionId,830		nft_id: TokenId,831		key: RmrkProperty,832	) -> Result<PropertyValue, DispatchError> {833		let nft_property = <PalletNft<T>>::token_properties((collection_id, nft_id))834			.get(&Self::rmrk_property_key(key)?)835			.ok_or(<Error<T>>::NoAvailableNftId)? // todo replace with better error?836			.clone();837838		Ok(nft_property)839	}840841	pub fn get_nft_property_decoded<V: Decode>(842		collection_id: CollectionId,843		nft_id: TokenId,844		key: RmrkProperty,845	) -> Result<V, DispatchError> {846		Self::decode_property(Self::get_nft_property(collection_id, nft_id, key)?)847	}848849	pub fn nft_exists(collection_id: CollectionId, nft_id: TokenId) -> bool {850		<TokenData<T>>::contains_key((collection_id, nft_id))851	}852853	pub fn get_nft_type(854		collection_id: CollectionId,855		token_id: TokenId,856	) -> Result<NftType, DispatchError> {857		Self::get_nft_property_decoded(collection_id, token_id, TokenType)858			.map_err(|_| <Error<T>>::NftTypeEncodeError.into())859	}860861	pub fn ensure_nft_type(862		collection_id: CollectionId,863		token_id: TokenId,864		nft_type: NftType,865	) -> DispatchResult {866		let actual_type = Self::get_nft_type(collection_id, token_id)?;867		ensure!(actual_type == nft_type, <Error<T>>::NoPermission);868869		Ok(())870	}871872	pub fn ensure_nft_owner(873		collection_id: CollectionId,874		token_id: TokenId,875		possible_owner: &T::CrossAccountId,876	) -> DispatchResult {877		let token_data =878			<TokenData<T>>::get((collection_id, token_id)).ok_or(<Error<T>>::NoAvailableNftId)?;879880		ensure!(881			token_data.owner == *possible_owner,882			<Error<T>>::NoPermission883		);884885		Ok(())886	}887888	pub fn filter_user_properties<Key, Value, R, Mapper>(889		collection_id: CollectionId,890		token_id: Option<TokenId>,891		filter_keys: Option<Vec<RmrkPropertyKey>>,892		mapper: Mapper,893	) -> Result<Vec<R>, DispatchError>894	where895		Key: TryFrom<RmrkPropertyKey> + AsRef<[u8]>,896		Value: Decode + Default,897		Mapper: Fn(Key, Value) -> R,898	{899		filter_keys900			.map(|keys| {901				let properties = keys902					.into_iter()903					.filter_map(|key| {904						let key: Key = key.try_into().ok()?;905906						let value = match token_id {907							Some(token_id) => Self::get_nft_property_decoded(908								collection_id,909								token_id,910								UserProperty(key.as_ref()),911							),912							None => Self::get_collection_property_decoded(913								collection_id,914								UserProperty(key.as_ref()),915							),916						}917						.ok()?;918919						Some(mapper(key, value))920					})921					.collect();922923				Ok(properties)924			})925			.unwrap_or_else(|| {926				let properties =927					Self::iterate_user_properties(collection_id, token_id, mapper)?.collect();928929				Ok(properties)930			})931	}932933	pub fn iterate_user_properties<Key, Value, R, Mapper>(934		collection_id: CollectionId,935		token_id: Option<TokenId>,936		mapper: Mapper,937	) -> Result<impl Iterator<Item = R>, DispatchError>938	where939		Key: TryFrom<RmrkPropertyKey> + AsRef<[u8]>,940		Value: Decode + Default,941		Mapper: Fn(Key, Value) -> R,942	{943		let key_prefix = Self::rmrk_property_key(UserProperty(b""))?;944945		let properties = match token_id {946			Some(token_id) => <PalletNft<T>>::token_properties((collection_id, token_id)),947			None => <PalletCommon<T>>::collection_properties(collection_id),948		};949950		let properties = properties.into_iter().filter_map(move |(key, value)| {951			let key = key.as_slice().strip_prefix(key_prefix.as_slice())?;952953			let key: Key = key.to_vec().try_into().ok()?;954			let value: Value = value.decode().ok()?;955956			Some(mapper(key, value))957		});958959		Ok(properties)960	}961962	fn map_common_err_to_proxy(err: DispatchError) -> DispatchError {963		map_common_err_to_proxy! {964			match err {965				NoPermission => NoPermission,966				CollectionTokenLimitExceeded => CollectionFullOrLocked,967				PublicMintingNotAllowed => NoPermission,968				TokenNotFound => NoAvailableNftId969			}970		}971	}972}
after · pallets/proxy-rmrk-core/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#![cfg_attr(not(feature = "std"), no_std)]1819use frame_support::{pallet_prelude::*, transactional, BoundedVec, dispatch::DispatchResult};20use frame_system::{pallet_prelude::*, ensure_signed};21use sp_runtime::{DispatchError, Permill, traits::StaticLookup};22use sp_std::vec::Vec;23use up_data_structs::{*, mapping::TokenAddressMapping};24use pallet_common::{25	Pallet as PalletCommon, Error as CommonError, CollectionHandle, CommonCollectionOperations,26};27use pallet_nonfungible::{Pallet as PalletNft, NonfungibleHandle, TokenData};28use pallet_structure::Pallet as PalletStructure;29use pallet_evm::account::CrossAccountId;30use core::convert::AsRef;3132pub use pallet::*;3334pub mod misc;35pub mod property;3637use misc::*;38pub use property::*;3940use RmrkProperty::*;4142const NESTING_BUDGET: u32 = 5;4344#[frame_support::pallet]45pub mod pallet {46	use super::*;47	use pallet_evm::account;4849	#[pallet::config]50	pub trait Config:51		frame_system::Config + pallet_common::Config + pallet_nonfungible::Config + account::Config52	{53		type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;54	}5556	#[pallet::storage]57	#[pallet::getter(fn collection_index)]58	pub type CollectionIndex<T: Config> = StorageValue<_, RmrkCollectionId, ValueQuery>;5960	#[pallet::storage]61	pub type UniqueCollectionId<T: Config> =62		StorageMap<_, Twox64Concat, RmrkCollectionId, CollectionId, ValueQuery>;6364	#[pallet::storage]65	pub type RmrkInernalCollectionId<T: Config> =66		StorageMap<_, Twox64Concat, CollectionId, RmrkCollectionId, ValueQuery>;6768	#[pallet::pallet]69	#[pallet::generate_store(pub(super) trait Store)]70	pub struct Pallet<T>(_);7172	#[pallet::event]73	#[pallet::generate_deposit(pub(super) fn deposit_event)]74	pub enum Event<T: Config> {75		CollectionCreated {76			issuer: T::AccountId,77			collection_id: RmrkCollectionId,78		},79		CollectionDestroyed {80			issuer: T::AccountId,81			collection_id: RmrkCollectionId,82		},83		IssuerChanged {84			old_issuer: T::AccountId,85			new_issuer: T::AccountId,86			collection_id: RmrkCollectionId,87		},88		CollectionLocked {89			issuer: T::AccountId,90			collection_id: RmrkCollectionId,91		},92		NftMinted {93			owner: T::AccountId,94			collection_id: RmrkCollectionId,95			nft_id: RmrkNftId,96		},97		NFTBurned {98			owner: T::AccountId,99			nft_id: RmrkNftId,100		},101		PropertySet {102			collection_id: RmrkCollectionId,103			maybe_nft_id: Option<RmrkNftId>,104			key: RmrkKeyString,105			value: RmrkValueString,106		},107		ResourceAdded {108			nft_id: RmrkNftId,109			resource_id: RmrkResourceId,110		},111		ResourceRemoval {112			nft_id: RmrkNftId,113			resource_id: RmrkResourceId,114		},115	}116117	#[pallet::error]118	pub enum Error<T> {119		/* Unique-specific events */120		CorruptedCollectionType,121		NftTypeEncodeError,122		RmrkPropertyKeyIsTooLong,123		RmrkPropertyValueIsTooLong,124125		/* RMRK compatible events */126		CollectionNotEmpty,127		NoAvailableCollectionId,128		NoAvailableNftId,129		CollectionUnknown,130		NoPermission,131		CollectionFullOrLocked,132		ResourceDoesntExist,133	}134135	#[pallet::call]136	impl<T: Config> Pallet<T> {137		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]138		#[transactional]139		pub fn create_collection(140			origin: OriginFor<T>,141			metadata: RmrkString,142			max: Option<u32>,143			symbol: RmrkCollectionSymbol,144		) -> DispatchResult {145			let sender = ensure_signed(origin)?;146147			let limits = CollectionLimits {148				owner_can_transfer: Some(false),149				token_limit: max,150				..Default::default()151			};152153			let data = CreateCollectionData {154				limits: Some(limits),155				token_prefix: symbol156					.into_inner()157					.try_into()158					.map_err(|_| <CommonError<T>>::CollectionTokenPrefixLimitExceeded)?,159				permissions: Some(CollectionPermissions {160					nesting: Some(NestingRule::Owner),161					..Default::default()162				}),163				..Default::default()164			};165166			let unique_collection_id = Self::init_collection(167				T::CrossAccountId::from_sub(sender.clone()),168				data,169				[170					Self::rmrk_property(Metadata, &metadata)?,171					Self::rmrk_property(CollectionType, &misc::CollectionType::Regular)?,172				]173				.into_iter(),174			)?;175			let rmrk_collection_id = <CollectionIndex<T>>::get();176177			<UniqueCollectionId<T>>::insert(rmrk_collection_id, unique_collection_id);178			<RmrkInernalCollectionId<T>>::insert(unique_collection_id, rmrk_collection_id);179180			<CollectionIndex<T>>::mutate(|n| *n += 1);181182			Self::deposit_event(Event::CollectionCreated {183				issuer: sender,184				collection_id: rmrk_collection_id,185			});186187			Ok(())188		}189190		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]191		#[transactional]192		pub fn destroy_collection(193			origin: OriginFor<T>,194			collection_id: RmrkCollectionId,195		) -> DispatchResult {196			let sender = ensure_signed(origin)?;197			let cross_sender = T::CrossAccountId::from_sub(sender.clone());198199			let collection = Self::get_typed_nft_collection(200				Self::unique_collection_id(collection_id)?,201				misc::CollectionType::Regular,202			)?;203204			ensure!(205				collection.total_supply() == 0,206				<Error<T>>::CollectionNotEmpty207			);208209			<PalletNft<T>>::destroy_collection(collection, &cross_sender)210				.map_err(Self::map_common_err_to_proxy)?;211212			Self::deposit_event(Event::CollectionDestroyed {213				issuer: sender,214				collection_id,215			});216217			Ok(())218		}219220		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]221		#[transactional]222		pub fn change_collection_issuer(223			origin: OriginFor<T>,224			collection_id: RmrkCollectionId,225			new_issuer: <T::Lookup as StaticLookup>::Source,226		) -> DispatchResult {227			let sender = ensure_signed(origin)?;228229			let new_issuer = T::Lookup::lookup(new_issuer)?;230231			Self::change_collection_owner(232				Self::unique_collection_id(collection_id)?,233				misc::CollectionType::Regular,234				sender.clone(),235				new_issuer.clone(),236			)?;237238			Self::deposit_event(Event::IssuerChanged {239				old_issuer: sender,240				new_issuer,241				collection_id,242			});243244			Ok(())245		}246247		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]248		#[transactional]249		pub fn lock_collection(250			origin: OriginFor<T>,251			collection_id: RmrkCollectionId,252		) -> DispatchResult {253			let sender = ensure_signed(origin)?;254			let cross_sender = T::CrossAccountId::from_sub(sender.clone());255256			let collection = Self::get_typed_nft_collection(257				Self::unique_collection_id(collection_id)?,258				misc::CollectionType::Regular,259			)?;260261			Self::check_collection_owner(&collection, &cross_sender)?;262263			let token_count = collection.total_supply();264265			let mut collection = collection.into_inner();266			collection.limits.token_limit = Some(token_count);267			collection.save()?;268269			Self::deposit_event(Event::CollectionLocked {270				issuer: sender,271				collection_id,272			});273274			Ok(())275		}276277		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]278		#[transactional]279		pub fn mint_nft(280			origin: OriginFor<T>,281			owner: T::AccountId,282			collection_id: RmrkCollectionId,283			recipient: Option<T::AccountId>,284			royalty_amount: Option<Permill>,285			metadata: RmrkString,286		) -> DispatchResult {287			let sender = ensure_signed(origin)?;288			let sender = T::CrossAccountId::from_sub(sender);289			let cross_owner = T::CrossAccountId::from_sub(owner.clone());290291			let royalty_info = royalty_amount.map(|amount| rmrk::RoyaltyInfo {292				recipient: recipient.unwrap_or_else(|| owner.clone()),293				amount,294			});295296			let collection = Self::get_typed_nft_collection(297				Self::unique_collection_id(collection_id)?,298				misc::CollectionType::Regular,299			)?;300301			let nft_id = Self::create_nft(302				&sender,303				&cross_owner,304				&collection,305				[306					Self::rmrk_property(TokenType, &NftType::Regular)?,307					Self::rmrk_property(RoyaltyInfo, &royalty_info)?,308					Self::rmrk_property(Metadata, &metadata)?,309					Self::rmrk_property(Equipped, &false)?,310					Self::rmrk_property(311						ResourceCollection,312						&Self::init_collection(313							sender.clone(),314							CreateCollectionData {315								..Default::default()316							},317							[Self::rmrk_property(318								CollectionType,319								&misc::CollectionType::Resource,320							)?]321							.into_iter(),322						)?,323					)?, // todo possibly add limits to the collection if rmrk warrants them324					Self::rmrk_property(ResourcePriorities, &<Vec<u8>>::new())?,325				]326				.into_iter(),327			)328			.map_err(|err| match err {329				DispatchError::Arithmetic(_) => <Error<T>>::NoAvailableNftId.into(),330				err => Self::map_common_err_to_proxy(err),331			})?;332333			Self::deposit_event(Event::NftMinted {334				owner,335				collection_id,336				nft_id: nft_id.0,337			});338339			Ok(())340		}341342		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]343		#[transactional]344		pub fn burn_nft(345			origin: OriginFor<T>,346			collection_id: RmrkCollectionId,347			nft_id: RmrkNftId,348		) -> DispatchResult {349			let sender = ensure_signed(origin)?;350			let cross_sender = T::CrossAccountId::from_sub(sender.clone());351352			Self::destroy_nft(353				cross_sender,354				Self::unique_collection_id(collection_id)?,355				misc::CollectionType::Regular,356				nft_id.into(),357			)?;358359			Self::deposit_event(Event::NFTBurned {360				owner: sender,361				nft_id,362			});363364			Ok(())365		}366367		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]368		#[transactional]369		pub fn send(370			origin: OriginFor<T>,371			rmrk_collection_id: RmrkCollectionId,372			rmrk_nft_id: RmrkNftId,373			new_owner: RmrkAccountIdOrCollectionNftTuple<T::AccountId>,374		) -> DispatchResult {375			let sender = ensure_signed(origin.clone())?;376			let cross_sender = T::CrossAccountId::from_sub(sender.clone());377378			let collection_id = Self::unique_collection_id(rmrk_collection_id)?;379			let nft_id = rmrk_nft_id.into();380381			let token_data = <TokenData<T>>::get((collection_id, nft_id))382				.ok_or(<Error<T>>::NoAvailableNftId)?;383384			let from = token_data.owner;385386			let collection = Self::get_typed_nft_collection(387				collection_id,388				misc::CollectionType::Regular,389			)?;390391			let budget = budget::Value::new(NESTING_BUDGET);392393			let target_owner;394395			match new_owner {396				RmrkAccountIdOrCollectionNftTuple::AccountId(account_id) => {397					target_owner = T::CrossAccountId::from_sub(account_id);398				},399				RmrkAccountIdOrCollectionNftTuple::CollectionAndNftTuple(target_collection_id, target_nft_id) => {400					let target_collection_id = Self::unique_collection_id(target_collection_id)?;401402					target_owner = T::CrossTokenAddressMapping::token_to_address(403						target_collection_id,404						target_nft_id.into(),405					);406407					let spender = <PalletStructure<T>>::get_indirect_owner(408						target_collection_id,409						target_nft_id.into(),410						Some((collection_id, nft_id)),411						&budget,412					)?;413414					let is_approval_required = cross_sender != spender;415416					if is_approval_required {417						<PalletNft<T>>::set_allowance(418							&collection,419							&cross_sender,420							nft_id,421							Some(&spender),422							&budget423						).map_err(Self::map_common_err_to_proxy)?;424425						return Ok(());426					}427				}428			}429430			<PalletNft<T>>::transfer_from(431				&collection,432				&cross_sender,433				&from,434				&target_owner,435				nft_id,436				&budget437			).map_err(Self::map_common_err_to_proxy)?;438439			Ok(())440		}441442		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]443		#[transactional]444		pub fn set_property(445			origin: OriginFor<T>,446			#[pallet::compact] rmrk_collection_id: RmrkCollectionId,447			maybe_nft_id: Option<RmrkNftId>,448			key: RmrkKeyString,449			value: RmrkValueString,450		) -> DispatchResult {451			let sender = ensure_signed(origin)?;452			let sender = T::CrossAccountId::from_sub(sender);453454			let collection_id = Self::unique_collection_id(rmrk_collection_id)?;455			let budget = budget::Value::new(NESTING_BUDGET);456457			match maybe_nft_id {458				Some(nft_id) => {459					let token_id: TokenId = nft_id.into();460461					Self::ensure_nft_owner(collection_id, token_id, &sender, &budget)?;462					Self::ensure_nft_type(collection_id, token_id, NftType::Regular)?;463464					<PalletNft<T>>::set_scoped_token_property(465						collection_id,466						token_id,467						PropertyScope::Rmrk,468						Self::rmrk_property(UserProperty(key.as_slice()), &value)?,469					)?;470				}471				None => {472					let collection = Self::get_typed_nft_collection(473						collection_id,474						misc::CollectionType::Regular,475					)?;476477					Self::check_collection_owner(&collection, &sender)?;478479					<PalletCommon<T>>::set_scoped_collection_property(480						collection_id,481						PropertyScope::Rmrk,482						Self::rmrk_property(UserProperty(key.as_slice()), &value)?,483					)?;484				}485			}486487			Self::deposit_event(Event::PropertySet {488				collection_id: rmrk_collection_id,489				maybe_nft_id,490				key,491				value,492			});493494			Ok(())495		}496497		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]498		#[transactional]499		pub fn add_basic_resource(500			origin: OriginFor<T>,501			collection_id: RmrkCollectionId,502			nft_id: RmrkNftId,503			resource: RmrkBasicResource,504		) -> DispatchResult {505			let sender = ensure_signed(origin.clone())?;506507			let resource_id = Self::resource_add(508				sender,509				Self::unique_collection_id(collection_id)?,510				nft_id.into(),511				[512					Self::rmrk_property(TokenType, &NftType::Resource)?,513					Self::rmrk_property(ResourceType, &misc::ResourceType::Basic)?,514					Self::rmrk_property(Src, &resource.src)?,515					Self::rmrk_property(Metadata, &resource.metadata)?,516					Self::rmrk_property(License, &resource.license)?,517					Self::rmrk_property(Thumb, &resource.thumb)?,518				]519				.into_iter(),520			)?;521522			Self::deposit_event(Event::ResourceAdded {523				nft_id,524				resource_id,525			});526			Ok(())527		}528529		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]530		#[transactional]531		pub fn add_composable_resource(532			origin: OriginFor<T>,533			collection_id: RmrkCollectionId,534			nft_id: RmrkNftId,535			_resource_id: RmrkBoundedResource,536			resource: RmrkComposableResource,537		) -> DispatchResult {538			let sender = ensure_signed(origin.clone())?;539540			let resource_id = Self::resource_add(541				sender,542				Self::unique_collection_id(collection_id)?,543				nft_id.into(),544				[545					Self::rmrk_property(TokenType, &NftType::Resource)?,546					Self::rmrk_property(ResourceType, &misc::ResourceType::Composable)?,547					Self::rmrk_property(Parts, &resource.parts)?,548					Self::rmrk_property(Base, &resource.base)?,549					Self::rmrk_property(Src, &resource.src)?,550					Self::rmrk_property(Metadata, &resource.metadata)?,551					Self::rmrk_property(License, &resource.license)?,552					Self::rmrk_property(Thumb, &resource.thumb)?,553				]554				.into_iter(),555			)?;556557			Self::deposit_event(Event::ResourceAdded {558				nft_id,559				resource_id,560			});561			Ok(())562		}563564		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]565		#[transactional]566		pub fn add_slot_resource(567			origin: OriginFor<T>,568			collection_id: RmrkCollectionId,569			nft_id: RmrkNftId,570			resource: RmrkSlotResource,571		) -> DispatchResult {572			let sender = ensure_signed(origin.clone())?;573574			let resource_id = Self::resource_add(575				sender,576				Self::unique_collection_id(collection_id)?,577				nft_id.into(),578				[579					Self::rmrk_property(TokenType, &NftType::Resource)?,580					Self::rmrk_property(ResourceType, &misc::ResourceType::Slot)?,581					Self::rmrk_property(Base, &resource.base)?,582					Self::rmrk_property(Src, &resource.src)?,583					Self::rmrk_property(Metadata, &resource.metadata)?,584					Self::rmrk_property(Slot, &resource.slot)?,585					Self::rmrk_property(License, &resource.license)?,586					Self::rmrk_property(Thumb, &resource.thumb)?,587				]588				.into_iter(),589			)?;590591			Self::deposit_event(Event::ResourceAdded {592				nft_id,593				resource_id,594			});595			Ok(())596		}597598		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]599		#[transactional]600		pub fn remove_resource(601			origin: OriginFor<T>,602			collection_id: RmrkCollectionId,603			nft_id: RmrkNftId,604			resource_id: RmrkResourceId,605		) -> DispatchResult {606			let sender = ensure_signed(origin.clone())?;607608			Self::resource_remove(609				sender,610				Self::unique_collection_id(collection_id)?,611				nft_id.into(),612				resource_id.into(),613			)?;614615			Self::deposit_event(Event::ResourceRemoval {616				nft_id,617				resource_id,618			});619			Ok(())620		}621	}622}623624impl<T: Config> Pallet<T> {625	pub fn rmrk_property_key(rmrk_key: RmrkProperty) -> Result<PropertyKey, DispatchError> {626		let key = rmrk_key.to_key::<T>()?;627628		let scoped_key = PropertyScope::Rmrk629			.apply(key)630			.map_err(|_| <Error<T>>::RmrkPropertyKeyIsTooLong)?;631632		Ok(scoped_key)633	}634635	// todo think about renaming these636	pub fn rmrk_property<E: Encode>(637		rmrk_key: RmrkProperty,638		value: &E,639	) -> Result<Property, DispatchError> {640		let key = rmrk_key.to_key::<T>()?;641642		let value = value643			.encode()644			.try_into()645			.map_err(|_| <Error<T>>::RmrkPropertyValueIsTooLong)?;646647		let property = Property { key, value };648649		Ok(property)650	}651652	pub fn decode_property<D: Decode>(vec: PropertyValue) -> Result<D, DispatchError> {653		vec.decode()654			.map_err(|_| <Error<T>>::RmrkPropertyValueIsTooLong.into())655	}656657	pub fn rebind<L, S>(vec: &BoundedVec<u8, L>) -> Result<BoundedVec<u8, S>, DispatchError>658	where659		BoundedVec<u8, S>: TryFrom<Vec<u8>>,660	{661		vec.rebind()662			.map_err(|_| <Error<T>>::RmrkPropertyValueIsTooLong.into())663	}664665	fn init_collection(666		sender: T::CrossAccountId,667		data: CreateCollectionData<T::AccountId>,668		properties: impl Iterator<Item = Property>,669	) -> Result<CollectionId, DispatchError> {670		let collection_id = <PalletNft<T>>::init_collection(sender, data);671672		if let Err(DispatchError::Arithmetic(_)) = &collection_id {673			return Err(<Error<T>>::NoAvailableCollectionId.into());674		}675676		<PalletCommon<T>>::set_scoped_collection_properties(677			collection_id?,678			PropertyScope::Rmrk,679			properties,680		)?;681682		collection_id683	}684685	pub fn create_nft(686		sender: &T::CrossAccountId,687		owner: &T::CrossAccountId,688		collection: &NonfungibleHandle<T>,689		properties: impl Iterator<Item = Property>,690	) -> Result<TokenId, DispatchError> {691		let data = CreateNftExData {692			properties: BoundedVec::default(),693			owner: owner.clone(),694		};695696		let budget = budget::Value::new(NESTING_BUDGET);697698		<PalletNft<T>>::create_item(collection, sender, data, &budget)?;699700		let nft_id = <PalletNft<T>>::current_token_id(collection.id);701702		<PalletNft<T>>::set_scoped_token_properties(703			collection.id,704			nft_id,705			PropertyScope::Rmrk,706			properties,707		)?;708709		Ok(nft_id)710	}711712	fn destroy_nft(713		sender: T::CrossAccountId,714		collection_id: CollectionId,715		collection_type: misc::CollectionType,716		token_id: TokenId,717	) -> DispatchResult {718		let collection = Self::get_typed_nft_collection(collection_id, collection_type)?;719720		<PalletNft<T>>::burn(&collection, &sender, token_id)721			.map_err(Self::map_common_err_to_proxy)?;722723		Ok(())724	}725726	fn resource_add(727		sender: T::AccountId,728		collection_id: CollectionId,729		token_id: TokenId,730		resource_properties: impl Iterator<Item = Property>,731	) -> Result<RmrkResourceId, DispatchError> {732		let collection =733			Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;734		ensure!(collection.owner == sender, Error::<T>::NoPermission);735736		// Check NFT lock status // todo depends on market, maybe later737		//ensure!(!Pallet::<T>::is_locked(collection_id, nft_id), pallet_uniques::Error::<T>::Locked);738739		let sender = T::CrossAccountId::from_sub(sender);740		let budget = budget::Value::new(NESTING_BUDGET);741		let pending = Self::ensure_nft_owner(collection_id, token_id, &sender, &budget).is_err();742743		let resource_collection_id: CollectionId =744			Self::get_nft_property_decoded(collection_id, token_id, ResourceCollection)?;745		let resource_collection =746			Self::get_typed_nft_collection(resource_collection_id, misc::CollectionType::Resource)?;747748		// todo probably add extra connections to bases, slots, etc., when RMRK starts to use them749750		let resource_id = Self::create_nft(751			&sender, // todo owner of the nft?752			&sender,753			&resource_collection,754			resource_properties.chain(755				[756					Self::rmrk_property(PendingResourceAccept, &pending)?,757					Self::rmrk_property(PendingResourceRemoval, &false)?,758				]759				.into_iter(),760			),761		)762		.map_err(|err| match err {763			DispatchError::Arithmetic(_) => <Error<T>>::NoAvailableNftId.into(),764			err => Self::map_common_err_to_proxy(err),765		})?;766767		Ok(resource_id.0)768	}769770	fn resource_remove(771		sender: T::AccountId,772		collection_id: CollectionId,773		nft_id: TokenId,774		resource_id: TokenId,775	) -> DispatchResult {776		let collection =777			Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;778		ensure!(collection.owner == sender, Error::<T>::NoPermission);779780		let resource_collection_id: CollectionId =781			Self::get_nft_property_decoded(collection_id, nft_id, ResourceCollection)?;782		let resource_collection =783			Self::get_typed_nft_collection(resource_collection_id, misc::CollectionType::Resource)?;784		ensure!(785			<PalletNft<T>>::token_exists(&resource_collection, resource_id),786			Error::<T>::ResourceDoesntExist787		);788789		let budget = up_data_structs::budget::Value::new(10);790		let topmost_owner =791			<PalletStructure<T>>::find_topmost_owner(collection_id, nft_id, &budget)?;792793		let sender = T::CrossAccountId::from_sub(sender);794		if topmost_owner == sender {795			<PalletNft<T>>::burn(&collection, &sender, nft_id)796				.map_err(Self::map_common_err_to_proxy)?;797		} else {798			<PalletNft<T>>::set_scoped_token_property(799				collection_id,800				nft_id,801				PropertyScope::Rmrk,802				Self::rmrk_property(PendingResourceRemoval, &true)?,803			)?;804		}805806		Ok(())807	}808809	fn change_collection_owner(810		collection_id: CollectionId,811		collection_type: misc::CollectionType,812		sender: T::AccountId,813		new_owner: T::AccountId,814	) -> DispatchResult {815		let collection = Self::get_typed_nft_collection(collection_id, collection_type)?;816		Self::check_collection_owner(&collection, &T::CrossAccountId::from_sub(sender))?;817818		let mut collection = collection.into_inner();819820		collection.owner = new_owner;821		collection.save()822	}823824	fn check_collection_owner(825		collection: &NonfungibleHandle<T>,826		account: &T::CrossAccountId,827	) -> DispatchResult {828		collection829			.check_is_owner(account)830			.map_err(Self::map_common_err_to_proxy)831	}832833	pub fn last_collection_idx() -> RmrkCollectionId {834		<CollectionIndex<T>>::get()835	}836837	pub fn unique_collection_id(838		rmrk_collection_id: RmrkCollectionId,839	) -> Result<CollectionId, DispatchError> {840		<UniqueCollectionId<T>>::try_get(rmrk_collection_id)841			.map_err(|_| <Error<T>>::CollectionUnknown.into())842	}843844	pub fn rmrk_collection_id(845		unique_collection_id: CollectionId846	) -> Result<RmrkCollectionId, DispatchError> {847		<RmrkInernalCollectionId<T>>::try_get(unique_collection_id)848			.map_err(|_| <Error<T>>::CollectionUnknown.into())849	}850851	pub fn get_nft_collection(852		collection_id: CollectionId,853	) -> Result<NonfungibleHandle<T>, DispatchError> {854		let collection = <CollectionHandle<T>>::try_get(collection_id)855			.map_err(|_| <Error<T>>::CollectionUnknown)?;856857		match collection.mode {858			CollectionMode::NFT => Ok(NonfungibleHandle::cast(collection)),859			_ => Err(<Error<T>>::CollectionUnknown.into()),860		}861	}862863	pub fn collection_exists(collection_id: CollectionId) -> bool {864		<CollectionHandle<T>>::try_get(collection_id).is_ok()865	}866867	pub fn get_collection_property(868		collection_id: CollectionId,869		key: RmrkProperty,870	) -> Result<PropertyValue, DispatchError> {871		let collection_property = <PalletCommon<T>>::collection_properties(collection_id)872			.get(&Self::rmrk_property_key(key)?)873			.ok_or(<Error<T>>::CollectionUnknown)?874			.clone();875876		Ok(collection_property)877	}878879	pub fn get_collection_property_decoded<V: Decode>(880		collection_id: CollectionId,881		key: RmrkProperty,882	) -> Result<V, DispatchError> {883		Self::decode_property(Self::get_collection_property(collection_id, key)?)884	}885886	pub fn get_collection_type(887		collection_id: CollectionId,888	) -> Result<misc::CollectionType, DispatchError> {889		Self::get_collection_property_decoded(collection_id, CollectionType)890			.map_err(|_| <Error<T>>::CorruptedCollectionType.into())891	}892893	pub fn ensure_collection_type(894		collection_id: CollectionId,895		collection_type: misc::CollectionType,896	) -> DispatchResult {897		let actual_type = Self::get_collection_type(collection_id)?;898		ensure!(899			actual_type == collection_type,900			<CommonError<T>>::NoPermission901		);902903		Ok(())904	}905906	pub fn get_typed_nft_collection(907		collection_id: CollectionId,908		collection_type: misc::CollectionType,909	) -> Result<NonfungibleHandle<T>, DispatchError> {910		Self::ensure_collection_type(collection_id, collection_type)?;911912		Self::get_nft_collection(collection_id)913	}914915	pub fn get_nft_property(916		collection_id: CollectionId,917		nft_id: TokenId,918		key: RmrkProperty,919	) -> Result<PropertyValue, DispatchError> {920		let nft_property = <PalletNft<T>>::token_properties((collection_id, nft_id))921			.get(&Self::rmrk_property_key(key)?)922			.ok_or(<Error<T>>::NoAvailableNftId)? // todo replace with better error?923			.clone();924925		Ok(nft_property)926	}927928	pub fn get_nft_property_decoded<V: Decode>(929		collection_id: CollectionId,930		nft_id: TokenId,931		key: RmrkProperty,932	) -> Result<V, DispatchError> {933		Self::decode_property(Self::get_nft_property(collection_id, nft_id, key)?)934	}935936	pub fn nft_exists(collection_id: CollectionId, nft_id: TokenId) -> bool {937		<TokenData<T>>::contains_key((collection_id, nft_id))938	}939940	pub fn get_nft_type(941		collection_id: CollectionId,942		token_id: TokenId,943	) -> Result<NftType, DispatchError> {944		Self::get_nft_property_decoded(collection_id, token_id, TokenType)945			.map_err(|_| <Error<T>>::NftTypeEncodeError.into())946	}947948	pub fn ensure_nft_type(949		collection_id: CollectionId,950		token_id: TokenId,951		nft_type: NftType,952	) -> DispatchResult {953		let actual_type = Self::get_nft_type(collection_id, token_id)?;954		ensure!(actual_type == nft_type, <Error<T>>::NoPermission);955956		Ok(())957	}958959	pub fn ensure_nft_owner(960		collection_id: CollectionId,961		token_id: TokenId,962		possible_owner: &T::CrossAccountId,963		nesting_budget: &dyn budget::Budget964	) -> DispatchResult {965		let is_owned = <PalletStructure<T>>::check_indirectly_owned(966			possible_owner.clone(),967			collection_id,968			token_id,969			None,970			nesting_budget,971		)?;972973		ensure!(974			is_owned,975			<Error<T>>::NoPermission976		);977978		Ok(())979	}980981	pub fn filter_user_properties<Key, Value, R, Mapper>(982		collection_id: CollectionId,983		token_id: Option<TokenId>,984		filter_keys: Option<Vec<RmrkPropertyKey>>,985		mapper: Mapper,986	) -> Result<Vec<R>, DispatchError>987	where988		Key: TryFrom<RmrkPropertyKey> + AsRef<[u8]>,989		Value: Decode + Default,990		Mapper: Fn(Key, Value) -> R,991	{992		filter_keys993			.map(|keys| {994				let properties = keys995					.into_iter()996					.filter_map(|key| {997						let key: Key = key.try_into().ok()?;998999						let value = match token_id {1000							Some(token_id) => Self::get_nft_property_decoded(1001								collection_id,1002								token_id,1003								UserProperty(key.as_ref()),1004							),1005							None => Self::get_collection_property_decoded(1006								collection_id,1007								UserProperty(key.as_ref()),1008							),1009						}1010						.ok()?;10111012						Some(mapper(key, value))1013					})1014					.collect();10151016				Ok(properties)1017			})1018			.unwrap_or_else(|| {1019				let properties =1020					Self::iterate_user_properties(collection_id, token_id, mapper)?.collect();10211022				Ok(properties)1023			})1024	}10251026	pub fn iterate_user_properties<Key, Value, R, Mapper>(1027		collection_id: CollectionId,1028		token_id: Option<TokenId>,1029		mapper: Mapper,1030	) -> Result<impl Iterator<Item = R>, DispatchError>1031	where1032		Key: TryFrom<RmrkPropertyKey> + AsRef<[u8]>,1033		Value: Decode + Default,1034		Mapper: Fn(Key, Value) -> R,1035	{1036		let key_prefix = Self::rmrk_property_key(UserProperty(b""))?;10371038		let properties = match token_id {1039			Some(token_id) => <PalletNft<T>>::token_properties((collection_id, token_id)),1040			None => <PalletCommon<T>>::collection_properties(collection_id),1041		};10421043		let properties = properties.into_iter().filter_map(move |(key, value)| {1044			let key = key.as_slice().strip_prefix(key_prefix.as_slice())?;10451046			let key: Key = key.to_vec().try_into().ok()?;1047			let value: Value = value.decode().ok()?;10481049			Some(mapper(key, value))1050		});10511052		Ok(properties)1053	}10541055	fn map_common_err_to_proxy(err: DispatchError) -> DispatchError {1056		map_common_err_to_proxy! {1057			match err {1058				NoPermission => NoPermission,1059				CollectionTokenLimitExceeded => CollectionFullOrLocked,1060				PublicMintingNotAllowed => NoPermission,1061				TokenNotFound => NoAvailableNftId,1062				ApprovedValueTooLow => NoPermission1063			}1064		}1065	}1066}
modifiedpallets/refungible/src/common.rsdiffbeforeafterboth
--- a/pallets/refungible/src/common.rs
+++ b/pallets/refungible/src/common.rs
@@ -254,6 +254,7 @@
 		spender: T::CrossAccountId,
 		token: TokenId,
 		amount: u128,
+		_nesting_budget: &dyn Budget,
 	) -> DispatchResultWithPostInfo {
 		with_weight(
 			<Pallet<T>>::set_allowance(self, &sender, &spender, token, amount),
modifiedpallets/structure/src/lib.rsdiffbeforeafterboth
--- a/pallets/structure/src/lib.rs
+++ b/pallets/structure/src/lib.rs
@@ -149,19 +149,12 @@
 		})
 	}
 
-	/// Check if token indirectly owned by specified user
-	pub fn check_indirectly_owned(
-		user: T::CrossAccountId,
+	pub fn get_indirect_owner(
 		collection: CollectionId,
 		token: TokenId,
 		for_nest: Option<(CollectionId, TokenId)>,
 		budget: &dyn Budget,
-	) -> Result<bool, DispatchError> {
-		let target_parent = match T::CrossTokenAddressMapping::address_to_token(&user) {
-			Some((collection, token)) => Self::find_topmost_owner(collection, token, budget)?,
-			None => user,
-		};
-
+	) -> Result<T::CrossAccountId, DispatchError> {
 		// Tried to nest token in itself
 		if Some((collection, token)) == for_nest {
 			return Err(<Error<T>>::OuroborosDetected.into());
@@ -173,10 +166,8 @@
 				Parent::Token(collection, token) if Some((collection, token)) == for_nest => {
 					return Err(<Error<T>>::OuroborosDetected.into())
 				}
-				// Found needed parent, token is indirecty owned
-				Parent::User(user) if user == target_parent => return Ok(true),
 				// Token is owned by other user
-				Parent::User(_) => return Ok(false),
+				Parent::User(user) => return Ok(user),
 				Parent::TokenNotFound => return Err(<Error<T>>::TokenNotFound.into()),
 				// Continue parent chain
 				Parent::Token(_, _) => {}
@@ -199,6 +190,27 @@
 		dispatch.burn_item_recursively(from.clone(), token, self_budget, breadth_budget)
 	}
 
+	/// Check if token indirectly owned by specified user
+	pub fn check_indirectly_owned(
+		user: T::CrossAccountId,
+		collection: CollectionId,
+		token: TokenId,
+		for_nest: Option<(CollectionId, TokenId)>,
+		budget: &dyn Budget,
+	) -> Result<bool, DispatchError> {
+		let target_parent = match T::CrossTokenAddressMapping::address_to_token(&user) {
+			Some((collection, token)) => Self::find_topmost_owner(collection, token, budget)?,
+			None => user,
+		};
+
+		Self::get_indirect_owner(
+			collection,
+			token,
+			for_nest,
+			budget
+		).map(|indirect_owner| indirect_owner == target_parent)
+	}
+
 	pub fn check_nesting(
 		from: T::CrossAccountId,
 		under: &T::CrossAccountId,
modifiedpallets/unique/src/lib.rsdiffbeforeafterboth
--- a/pallets/unique/src/lib.rs
+++ b/pallets/unique/src/lib.rs
@@ -815,8 +815,9 @@
 		#[transactional]
 		pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResultWithPostInfo {
 			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
+			let budget = budget::Value::new(NESTING_BUDGET);
 
-			dispatch_call::<T, _>(collection_id, |d| d.approve(sender, spender, item_id, amount))
+			dispatch_call::<T, _>(collection_id, |d| d.approve(sender, spender, item_id, amount, &budget))
 		}
 
 		/// Change ownership of a NFT on behalf of the owner. See Approve method for additional information. After this method executes, the approval is removed so that the approved address will not be able to transfer this NFT again from this owner.
modifiedruntime/common/src/runtime_apis.rsdiffbeforeafterboth
--- a/runtime/common/src/runtime_apis.rs
+++ b/runtime/common/src/runtime_apis.rs
@@ -147,7 +147,11 @@
                     use pallet_proxy_rmrk_core::{RmrkProperty, misc::CollectionType};
                     use pallet_common::CommonCollectionOperations;
 
-                    let collection_id = RmrkCore::unique_collection_id(collection_id)?;
+                    let collection_id = match RmrkCore::unique_collection_id(collection_id) {
+                        Ok(id) => id,
+                        Err(_) => return Ok(None)
+                    };
+
                     let collection = match RmrkCore::get_typed_nft_collection(collection_id, CollectionType::Regular) {
                         Ok(c) => c,
                         Err(_) => return Ok(None),
@@ -169,7 +173,10 @@
                     use pallet_proxy_rmrk_core::{RmrkProperty, misc::CollectionType};
                     use pallet_common::CommonCollectionOperations;
 
-                    let collection_id = RmrkCore::unique_collection_id(collection_id)?;
+                    let collection_id = match RmrkCore::unique_collection_id(collection_id) {
+                        Ok(id) => id,
+                        Err(_) => return Ok(None)
+                    };
                     let collection = match RmrkCore::get_typed_nft_collection(collection_id, CollectionType::Regular) {
                         Ok(c) => c,
                         Err(_) => return Ok(None),
@@ -180,7 +187,11 @@
 
                     let owner = match collection.token_owner(nft_id) {
                         Some(owner) => match <Runtime as pallet_common::Config>::CrossTokenAddressMapping::address_to_token(&owner) {
-                            Some((col, tok)) => RmrkAccountIdOrCollectionNftTuple::CollectionAndNftTuple(col.0, tok.0),
+                            Some((col, tok)) => {
+                                let rmrk_collection = RmrkCore::rmrk_collection_id(col)?;
+
+                                RmrkAccountIdOrCollectionNftTuple::CollectionAndNftTuple(rmrk_collection, tok.0)
+                            }
                             None => RmrkAccountIdOrCollectionNftTuple::AccountId(owner.as_sub().clone())
                         },
                         None => return Ok(None)
@@ -202,11 +213,11 @@
                     use pallet_common::CommonCollectionOperations;
 
                     let cross_account_id = CrossAccountId::from_sub(account_id);
-                    let collection_id = RmrkCore::unique_collection_id(collection_id)?;
-                    let collection = match RmrkCore::get_typed_nft_collection(collection_id, CollectionType::Regular) {
-                        Ok(c) => c,
-                        Err(_) => return Ok(Vec::new()),
+                    let collection_id = match RmrkCore::unique_collection_id(collection_id) {
+                        Ok(id) => id,
+                        Err(_) => return Ok(Vec::new())
                     };
+                    if RmrkCore::ensure_collection_type(collection_id, CollectionType::Regular).is_err() { return Ok(Vec::new()); }
 
                     Ok(
                         collection.account_tokens(cross_account_id)
@@ -217,28 +228,35 @@
                 }
 
                 fn nft_children(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Result<Vec<RmrkNftChild>, DispatchError> {
-                    let collection_id = RmrkCore::unique_collection_id(collection_id)?;
+                    let collection_id = match RmrkCore::unique_collection_id(collection_id) {
+                        Ok(id) => id,
+                        Err(_) => return Ok(Vec::new())
+                    };
                     let nft_id = TokenId(nft_id);
                     if !RmrkCore::nft_exists(collection_id, nft_id) { return Ok(Vec::new()); }
 
                     Ok(
                         pallet_nonfungible::TokenChildren::<Runtime>::iter_prefix((collection_id, nft_id))
-                            .filter_map(|(child_id, is_child)|
-                                match is_child {
-                                    true => Some(RmrkNftChild {
-                                        collection_id: child_id.0.0,
-                                        nft_id: child_id.1.0,
-                                    }),
-                                    false => None,
-                                }
-                            ).collect()
+                            .filter_map(|((child_collection, child_token), _)| {
+                                let rmrk_child_collection = RmrkCore::rmrk_collection_id(
+                                    child_collection
+                                ).ok()?;
+
+                                Some(RmrkNftChild {
+                                    collection_id: rmrk_child_collection,
+                                    nft_id: child_token.0,
+                                })
+                            }).collect()
                     )
                 }
 
                 fn collection_properties(collection_id: RmrkCollectionId, filter_keys: Option<Vec<RmrkPropertyKey>>) -> Result<Vec<RmrkPropertyInfo>, DispatchError> {
                     use pallet_proxy_rmrk_core::misc::CollectionType;
 
-                    let collection_id = RmrkCore::unique_collection_id(collection_id)?;
+                    let collection_id = match RmrkCore::unique_collection_id(collection_id) {
+                        Ok(id) => id,
+                        Err(_) => return Ok(Vec::new())
+                    };
                     if RmrkCore::ensure_collection_type(collection_id, CollectionType::Regular).is_err() {
                         return Ok(Vec::new());
                     }
@@ -259,7 +277,10 @@
                 fn nft_properties(collection_id: RmrkCollectionId, nft_id: RmrkNftId, filter_keys: Option<Vec<RmrkPropertyKey>>) -> Result<Vec<RmrkPropertyInfo>, DispatchError> {
                     use pallet_proxy_rmrk_core::misc::NftType;
 
-                    let collection_id = RmrkCore::unique_collection_id(collection_id)?;
+                    let collection_id = match RmrkCore::unique_collection_id(collection_id) {
+                        Ok(id) => id,
+                        Err(_) => return Ok(Vec::new())
+                    };
                     let token_id = TokenId(nft_id);
 
                     if RmrkCore::ensure_nft_type(collection_id, token_id, NftType::Regular).is_err() {
@@ -283,7 +304,10 @@
                     use pallet_proxy_rmrk_core::{RmrkProperty, misc::{CollectionType, NftType, ResourceType}};
                     use pallet_common::CommonCollectionOperations;
 
-                    let collection_id = RmrkCore::unique_collection_id(collection_id)?;
+                    let collection_id = match RmrkCore::unique_collection_id(collection_id) {
+                        Ok(id) => id,
+                        Err(_) => return Ok(Vec::new())
+                    };
                     if RmrkCore::ensure_collection_type(collection_id, CollectionType::Regular).is_err() { return Ok(Vec::new()); }
 
                     let nft_id = TokenId(nft_id);
@@ -332,7 +356,10 @@
                 fn nft_resource_priorities(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Result<Vec<RmrkResourceId>, DispatchError> {
                     use pallet_proxy_rmrk_core::{RmrkProperty, misc::{CollectionType, NftType}};
 
-                    let collection_id = RmrkCore::unique_collection_id(collection_id)?;
+                    let collection_id = match RmrkCore::unique_collection_id(collection_id) {
+                        Ok(id) => id,
+                        Err(_) => return Ok(Vec::new())
+                    };
                     if RmrkCore::ensure_collection_type(collection_id, CollectionType::Regular).is_err() { return Ok(Vec::new()); }
 
                     let nft_id = TokenId(nft_id);
@@ -360,7 +387,10 @@
                         RmrkProperty, misc::{CollectionType},
                     };
 
-                    let collection_id = RmrkCore::unique_collection_id(base_id)?;
+                    let collection_id = match RmrkCore::unique_collection_id(base_id) {
+                        Ok(id) => id,
+                        Err(_) => return Ok(None)
+                    };
                     let collection = match RmrkCore::get_typed_nft_collection(collection_id, CollectionType::Base) {
                         Ok(c) => c,
                         Err(_) => return Ok(None),
@@ -377,12 +407,11 @@
                     use pallet_proxy_rmrk_core::{RmrkProperty, misc::{CollectionType, NftType}};
                     use pallet_common::CommonCollectionOperations;
 
-                    let collection_id = RmrkCore::unique_collection_id(base_id)?;
-                    let collection = match RmrkCore::get_typed_nft_collection(collection_id, CollectionType::Base) {
-                        Ok(c) => c,
-                        Err(_) => return Ok(Vec::new()),
+                    let collection_id = match RmrkCore::unique_collection_id(base_id) {
+                        Ok(id) => id,
+                        Err(_) => return Ok(Vec::new())
                     };
-
+                    if RmrkCore::ensure_collection_type(collection_id, CollectionType::Base).is_err() { return Ok(Vec::new()); }
 
                     let parts = collection.collection_tokens()
                         .into_iter()
@@ -413,11 +442,13 @@
                     use pallet_proxy_rmrk_core::{RmrkProperty, misc::CollectionType};
                     use pallet_common::CommonCollectionOperations;
 
-                    let collection_id = RmrkCore::unique_collection_id(base_id)?;
-                    let collection = match RmrkCore::get_typed_nft_collection(collection_id, CollectionType::Base) {
-                        Ok(c) => c,
-                        Err(_) => return Ok(Vec::new()),
+                    let collection_id = match RmrkCore::unique_collection_id(base_id) {
+                        Ok(id) => id,
+                        Err(_) => return Ok(Vec::new())
                     };
+                    if RmrkCore::ensure_collection_type(collection_id, CollectionType::Base).is_err() {
+                        return Ok(Vec::new());
+                    }
 
 
                     let theme_names = collection.collection_tokens()
@@ -444,11 +475,13 @@
                     };
                     use pallet_common::CommonCollectionOperations;
 
-                    let collection_id = RmrkCore::unique_collection_id(base_id)?;
-                    let collection = match RmrkCore::get_typed_nft_collection(collection_id, CollectionType::Base) {
-                        Ok(c) => c,
-                        Err(_) => return Ok(None),
+                    let collection_id = match RmrkCore::unique_collection_id(base_id) {
+                        Ok(id) => id,
+                        Err(_) => return Ok(None)
                     };
+                    if RmrkCore::ensure_collection_type(collection_id, CollectionType::Base).is_err() {
+                        return Ok(None);
+                    }
 
                     let theme_info = collection.collection_tokens()
                         .into_iter()