git.delta.rocks / unique-network / refs/commits / 4469adb5861d

difftreelog

feat rmrk-core benchmarking

Yaroslav Bolyukin2022-06-09parent: #d171b11.patch.diff
in: master

11 files changed

modifiedMakefilediffbeforeafterboth
--- a/Makefile
+++ b/Makefile
@@ -93,5 +93,9 @@
 bench-structure:
 	make _bench PALLET=structure
 
+.PHONY: bench-rmrk-core
+bench-rmrk-core:
+	make _bench PALLET=proxy-rmrk-core
+
 .PHONY: bench
 bench: bench-evm-migration bench-unique bench-structure bench-fungible bench-refungible bench-nonfungible
modifiedpallets/common/src/benchmarking.rsdiffbeforeafterboth
--- a/pallets/common/src/benchmarking.rs
+++ b/pallets/common/src/benchmarking.rs
@@ -109,7 +109,7 @@
 	create_collection_raw(
 		owner,
 		CollectionMode::NFT,
-		|owner, data| <Pallet<T>>::init_collection(owner, data),
+		|owner, data| <Pallet<T>>::init_collection(owner, data, true),
 		|h| h,
 	)
 }
modifiedpallets/common/src/lib.rsdiffbeforeafterboth
--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -427,7 +427,7 @@
 		/// Target collection doesn't supports this operation
 		UnsupportedOperation,
 
-		/// Not sufficient founds to perform action
+		/// Not sufficient funds to perform action
 		NotSufficientFounds,
 
 		/// Collection has nesting disabled
modifiedpallets/nonfungible/src/benchmarking.rsdiffbeforeafterboth
--- a/pallets/nonfungible/src/benchmarking.rs
+++ b/pallets/nonfungible/src/benchmarking.rs
@@ -51,7 +51,7 @@
 	create_collection_raw(
 		owner,
 		CollectionMode::NFT,
-		<Pallet<T>>::init_collection,
+		|owner, data| <Pallet<T>>::init_collection(owner, data, true),
 		NonfungibleHandle::cast,
 	)
 }
@@ -99,7 +99,7 @@
 			sender: cross_from_sub(owner); burner: cross_sub;
 		};
 		let item = create_max_item(&collection, &sender, burner.clone())?;
-	}: {<Pallet<T>>::burn_recursively(&collection, &burner, item, &Unlimited, &Unlimited)}
+	}: {<Pallet<T>>::burn_recursively(&collection, &burner, item, &Unlimited, &Unlimited)?}
 
 	burn_recursively_breadth_plus_self_plus_self_per_each_raw {
 		let b in 0..200;
@@ -111,7 +111,7 @@
 		for i in 0..b {
 			create_max_item(&collection, &sender, T::CrossTokenAddressMapping::token_to_address(collection.id, item))?;
 		}
-	}: {<Pallet<T>>::burn_recursively(&collection, &burner, item, &Unlimited, &Unlimited)}
+	}: {<Pallet<T>>::burn_recursively(&collection, &burner, item, &Unlimited, &Unlimited)?}
 
 	transfer {
 		bench_init!{
@@ -183,7 +183,7 @@
 			value: property_value(),
 		}).collect::<Vec<_>>();
 		let item = create_max_item(&collection, &owner, owner.clone())?;
-	}: {<Pallet<T>>::set_token_properties(&collection, &owner, item, props)?}
+	}: {<Pallet<T>>::set_token_properties(&collection, &owner, item, props, false)?}
 
 	delete_token_properties {
 		let b in 0..MAX_PROPERTIES_PER_ITEM;
@@ -205,7 +205,7 @@
 			value: property_value(),
 		}).collect::<Vec<_>>();
 		let item = create_max_item(&collection, &owner, owner.clone())?;
-		<Pallet<T>>::set_token_properties(&collection, &owner, item, props)?;
+		<Pallet<T>>::set_token_properties(&collection, &owner, item, props, false)?;
 		let to_delete = (0..b).map(|k| property_key(k as usize)).collect::<Vec<_>>();
 	}: {<Pallet<T>>::delete_token_properties(&collection, &owner, item, to_delete)?}
 }
addedpallets/proxy-rmrk-core/src/benchmarking.rsdiffbeforeafterboth
--- /dev/null
+++ b/pallets/proxy-rmrk-core/src/benchmarking.rs
@@ -0,0 +1,26 @@
+use sp_std::vec;
+
+use frame_benchmarking::{benchmarks, account};
+use frame_system::RawOrigin;
+use frame_support::{
+	traits::{Currency, Get},
+	BoundedVec,
+};
+
+use crate::{Config, Pallet, Call};
+
+const SEED: u32 = 1;
+
+fn create_data<S: Get<u32>>() -> BoundedVec<u8, S> {
+	vec![0; S::get() as usize].try_into().expect("size == S")
+}
+
+benchmarks! {
+	create_collection {
+		let caller = account("caller", 0, SEED);
+		<T as pallet_common::Config>::Currency::deposit_creating(&caller, T::CollectionCreationPrice::get());
+		let metadata = create_data();
+		// TODO: Fix CollectionTokenPrefixLimitExceeded with create_data
+		let symbol = vec![].try_into().expect("0 <= x");
+	}: _(RawOrigin::Signed(caller), metadata, None, symbol)
+}
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::{*, 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, Error as StructureError};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		NFTSent {102			sender: T::AccountId,103			recipient: RmrkAccountIdOrCollectionNftTuple<T::AccountId>,104			collection_id: RmrkCollectionId,105			nft_id: RmrkNftId,106			approval_required: bool,107		},108		NFTAccepted {109			sender: T::AccountId,110			recipient: RmrkAccountIdOrCollectionNftTuple<T::AccountId>,111			collection_id: RmrkCollectionId,112			nft_id: RmrkNftId,113		},114		NFTRejected {115			sender: T::AccountId,116			collection_id: RmrkCollectionId,117			nft_id: RmrkNftId,118		},119		PropertySet {120			collection_id: RmrkCollectionId,121			maybe_nft_id: Option<RmrkNftId>,122			key: RmrkKeyString,123			value: RmrkValueString,124		},125		ResourceAdded {126			nft_id: RmrkNftId,127			resource_id: RmrkResourceId,128		},129		ResourceRemoval {130			nft_id: RmrkNftId,131			resource_id: RmrkResourceId,132		},133		ResourceAccepted {134			nft_id: RmrkNftId,135			resource_id: RmrkResourceId,136		},137		ResourceRemovalAccepted {138			nft_id: RmrkNftId,139			resource_id: RmrkResourceId,140		},141		PrioritySet {142			collection_id: RmrkCollectionId,143			nft_id: RmrkNftId,144		},145	}146147	#[pallet::error]148	pub enum Error<T> {149		/* Unique-specific events */150		CorruptedCollectionType,151		NftTypeEncodeError,152		RmrkPropertyKeyIsTooLong,153		RmrkPropertyValueIsTooLong,154155		/* RMRK compatible events */156		CollectionNotEmpty,157		NoAvailableCollectionId,158		NoAvailableNftId,159		CollectionUnknown,160		NoPermission,161		NonTransferable,162		CollectionFullOrLocked,163		ResourceDoesntExist,164		CannotSendToDescendentOrSelf,165		CannotAcceptNonOwnedNft,166		CannotRejectNonOwnedNft,167		ResourceNotPending,168	}169170	#[pallet::call]171	impl<T: Config> Pallet<T> {172		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]173		#[transactional]174		pub fn create_collection(175			origin: OriginFor<T>,176			metadata: RmrkString,177			max: Option<u32>,178			symbol: RmrkCollectionSymbol,179		) -> DispatchResult {180			let sender = ensure_signed(origin)?;181182			let limits = CollectionLimits {183				owner_can_transfer: Some(false),184				token_limit: max,185				..Default::default()186			};187188			let data = CreateCollectionData {189				limits: Some(limits),190				token_prefix: symbol191					.into_inner()192					.try_into()193					.map_err(|_| <CommonError<T>>::CollectionTokenPrefixLimitExceeded)?,194				permissions: Some(CollectionPermissions {195					nesting: Some(NestingRule::Owner),196					..Default::default()197				}),198				..Default::default()199			};200201			let unique_collection_id = Self::init_collection(202				T::CrossAccountId::from_sub(sender.clone()),203				data,204				[205					Self::rmrk_property(Metadata, &metadata)?,206					Self::rmrk_property(CollectionType, &misc::CollectionType::Regular)?,207				]208				.into_iter(),209			)?;210			let rmrk_collection_id = <CollectionIndex<T>>::get();211212			<UniqueCollectionId<T>>::insert(rmrk_collection_id, unique_collection_id);213			<RmrkInernalCollectionId<T>>::insert(unique_collection_id, rmrk_collection_id);214215			<CollectionIndex<T>>::mutate(|n| *n += 1);216217			Self::deposit_event(Event::CollectionCreated {218				issuer: sender,219				collection_id: rmrk_collection_id,220			});221222			Ok(())223		}224225		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]226		#[transactional]227		pub fn destroy_collection(228			origin: OriginFor<T>,229			collection_id: RmrkCollectionId,230		) -> DispatchResult {231			let sender = ensure_signed(origin)?;232			let cross_sender = T::CrossAccountId::from_sub(sender.clone());233234			let collection = Self::get_typed_nft_collection(235				Self::unique_collection_id(collection_id)?,236				misc::CollectionType::Regular,237			)?;238			collection.check_is_external()?;239240			<PalletNft<T>>::destroy_collection(collection, &cross_sender)241				.map_err(Self::map_unique_err_to_proxy)?;242243			Self::deposit_event(Event::CollectionDestroyed {244				issuer: sender,245				collection_id,246			});247248			Ok(())249		}250251		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]252		#[transactional]253		pub fn change_collection_issuer(254			origin: OriginFor<T>,255			collection_id: RmrkCollectionId,256			new_issuer: <T::Lookup as StaticLookup>::Source,257		) -> DispatchResult {258			let sender = ensure_signed(origin)?;259260			let collection = Self::get_nft_collection(Self::unique_collection_id(collection_id)?)?;261			collection.check_is_external()?;262263			let new_issuer = T::Lookup::lookup(new_issuer)?;264265			Self::change_collection_owner(266				Self::unique_collection_id(collection_id)?,267				misc::CollectionType::Regular,268				sender.clone(),269				new_issuer.clone(),270			)?;271272			Self::deposit_event(Event::IssuerChanged {273				old_issuer: sender,274				new_issuer,275				collection_id,276			});277278			Ok(())279		}280281		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]282		#[transactional]283		pub fn lock_collection(284			origin: OriginFor<T>,285			collection_id: RmrkCollectionId,286		) -> DispatchResult {287			let sender = ensure_signed(origin)?;288			let cross_sender = T::CrossAccountId::from_sub(sender.clone());289290			let collection = Self::get_typed_nft_collection(291				Self::unique_collection_id(collection_id)?,292				misc::CollectionType::Regular,293			)?;294			collection.check_is_external()?;295296			Self::check_collection_owner(&collection, &cross_sender)?;297298			let token_count = collection.total_supply();299300			let mut collection = collection.into_inner();301			collection.limits.token_limit = Some(token_count);302			collection.save()?;303304			Self::deposit_event(Event::CollectionLocked {305				issuer: sender,306				collection_id,307			});308309			Ok(())310		}311312		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]313		#[transactional]314		pub fn mint_nft(315			origin: OriginFor<T>,316			owner: T::AccountId,317			collection_id: RmrkCollectionId,318			recipient: Option<T::AccountId>,319			royalty_amount: Option<Permill>,320			metadata: RmrkString,321			transferable: bool,322		) -> DispatchResult {323			let sender = ensure_signed(origin)?;324			let sender = T::CrossAccountId::from_sub(sender);325			let cross_owner = T::CrossAccountId::from_sub(owner.clone());326327			let collection = Self::get_typed_nft_collection(328				Self::unique_collection_id(collection_id)?,329				misc::CollectionType::Regular,330			)?;331			collection.check_is_external()?;332333			let royalty_info = royalty_amount.map(|amount| rmrk_traits::RoyaltyInfo {334				recipient: recipient.unwrap_or_else(|| owner.clone()),335				amount,336			});337338			let nft_id = Self::create_nft(339				&sender,340				&cross_owner,341				&collection,342				[343					Self::rmrk_property(TokenType, &NftType::Regular)?,344					Self::rmrk_property(Transferable, &transferable)?,345					Self::rmrk_property(PendingNftAccept, &false)?,346					Self::rmrk_property(RoyaltyInfo, &royalty_info)?,347					Self::rmrk_property(Metadata, &metadata)?,348					Self::rmrk_property(Equipped, &false)?,349					Self::rmrk_property(350						ResourceCollection,351						&Self::init_collection(352							sender.clone(),353							CreateCollectionData {354								..Default::default()355							},356							[Self::rmrk_property(357								CollectionType,358								&misc::CollectionType::Resource,359							)?]360							.into_iter(),361						)?,362					)?, // todo possibly add limits to the collection if rmrk warrants them363					Self::rmrk_property(ResourcePriorities, &<Vec<u8>>::new())?,364				]365				.into_iter(),366			)367			.map_err(|err| match err {368				DispatchError::Arithmetic(_) => <Error<T>>::NoAvailableNftId.into(),369				err => Self::map_unique_err_to_proxy(err),370			})?;371372			Self::deposit_event(Event::NftMinted {373				owner,374				collection_id,375				nft_id: nft_id.0,376			});377378			Ok(())379		}380381		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]382		#[transactional]383		pub fn burn_nft(384			origin: OriginFor<T>,385			collection_id: RmrkCollectionId,386			nft_id: RmrkNftId,387		) -> DispatchResult {388			let sender = ensure_signed(origin)?;389			let cross_sender = T::CrossAccountId::from_sub(sender.clone());390391			let collection = Self::get_typed_nft_collection(392				Self::unique_collection_id(collection_id)?,393				misc::CollectionType::Regular,394			)?;395			collection.check_is_external()?;396397			Self::destroy_nft(398				cross_sender,399				Self::unique_collection_id(collection_id)?,400				nft_id.into(),401			)402			.map_err(Self::map_unique_err_to_proxy)?;403404			Self::deposit_event(Event::NFTBurned {405				owner: sender,406				nft_id,407			});408409			Ok(())410		}411412		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]413		#[transactional]414		pub fn send(415			origin: OriginFor<T>,416			rmrk_collection_id: RmrkCollectionId,417			rmrk_nft_id: RmrkNftId,418			new_owner: RmrkAccountIdOrCollectionNftTuple<T::AccountId>,419		) -> DispatchResult {420			let sender = ensure_signed(origin.clone())?;421			let cross_sender = T::CrossAccountId::from_sub(sender.clone());422423			let collection_id = Self::unique_collection_id(rmrk_collection_id)?;424			let nft_id = rmrk_nft_id.into();425426			let collection =427				Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;428			collection.check_is_external()?;429430			let token_data =431				<TokenData<T>>::get((collection_id, nft_id)).ok_or(<Error<T>>::NoAvailableNftId)?;432433			let from = token_data.owner;434435			ensure!(436				Self::get_nft_property_decoded(collection_id, nft_id, RmrkProperty::Transferable)?,437				<Error<T>>::NonTransferable438			);439440			ensure!(441				!Self::get_nft_property_decoded(442					collection_id,443					nft_id,444					RmrkProperty::PendingNftAccept445				)?,446				<Error<T>>::NoPermission447			);448449			let target_owner;450			let approval_required;451452			match new_owner {453				RmrkAccountIdOrCollectionNftTuple::AccountId(ref account_id) => {454					target_owner = T::CrossAccountId::from_sub(account_id.clone());455					approval_required = false;456				}457				RmrkAccountIdOrCollectionNftTuple::CollectionAndNftTuple(458					target_collection_id,459					target_nft_id,460				) => {461					let target_collection_id = Self::unique_collection_id(target_collection_id)?;462463					let target_nft_budget = budget::Value::new(NESTING_BUDGET);464465					let target_nft_owner = <PalletStructure<T>>::get_checked_topmost_owner(466						target_collection_id,467						target_nft_id.into(),468						Some((collection_id, nft_id)),469						&target_nft_budget,470					)471					.map_err(Self::map_unique_err_to_proxy)?;472473					approval_required = cross_sender != target_nft_owner;474475					if approval_required {476						target_owner = target_nft_owner;477478						<PalletNft<T>>::set_scoped_token_property(479							collection.id,480							nft_id,481							PropertyScope::Rmrk,482							Self::rmrk_property(PendingNftAccept, &approval_required)?,483						)?;484					} else {485						target_owner = T::CrossTokenAddressMapping::token_to_address(486							target_collection_id,487							target_nft_id.into(),488						);489					}490				}491			}492493			let src_nft_budget = budget::Value::new(NESTING_BUDGET);494495			<PalletNft<T>>::transfer_from(496				&collection,497				&cross_sender,498				&from,499				&target_owner,500				nft_id,501				&src_nft_budget,502			)503			.map_err(Self::map_unique_err_to_proxy)?;504505			Self::deposit_event(Event::NFTSent {506				sender,507				recipient: new_owner,508				collection_id: rmrk_collection_id,509				nft_id: rmrk_nft_id,510				approval_required,511			});512513			Ok(())514		}515516		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]517		#[transactional]518		pub fn accept_nft(519			origin: OriginFor<T>,520			rmrk_collection_id: RmrkCollectionId,521			rmrk_nft_id: RmrkNftId,522			new_owner: RmrkAccountIdOrCollectionNftTuple<T::AccountId>,523		) -> DispatchResult {524			let sender = ensure_signed(origin.clone())?;525			let cross_sender = T::CrossAccountId::from_sub(sender.clone());526527			let collection_id = Self::unique_collection_id(rmrk_collection_id)?;528			let nft_id = rmrk_nft_id.into();529530			let collection =531				Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;532			collection.check_is_external()?;533534			let new_cross_owner = match new_owner {535				RmrkAccountIdOrCollectionNftTuple::AccountId(ref account_id) => {536					T::CrossAccountId::from_sub(account_id.clone())537				}538				RmrkAccountIdOrCollectionNftTuple::CollectionAndNftTuple(539					target_collection_id,540					target_nft_id,541				) => {542					let target_collection_id = Self::unique_collection_id(target_collection_id)?;543544					T::CrossTokenAddressMapping::token_to_address(545						target_collection_id,546						TokenId(target_nft_id),547					)548				}549			};550551			let budget = budget::Value::new(NESTING_BUDGET);552553			<PalletNft<T>>::transfer(554				&collection,555				&cross_sender,556				&new_cross_owner,557				nft_id,558				&budget,559			)560			.map_err(|err| {561				if err == <CommonError<T>>::OnlyOwnerAllowedToNest.into() {562					<Error<T>>::CannotAcceptNonOwnedNft.into()563				} else {564					Self::map_unique_err_to_proxy(err)565				}566			})?;567568			<PalletNft<T>>::set_scoped_token_property(569				collection.id,570				nft_id,571				PropertyScope::Rmrk,572				Self::rmrk_property(PendingNftAccept, &false)?,573			)?;574575			Self::deposit_event(Event::NFTAccepted {576				sender,577				recipient: new_owner,578				collection_id: rmrk_collection_id,579				nft_id: rmrk_nft_id,580			});581582			Ok(())583		}584585		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]586		#[transactional]587		pub fn reject_nft(588			origin: OriginFor<T>,589			rmrk_collection_id: RmrkCollectionId,590			rmrk_nft_id: RmrkNftId,591		) -> DispatchResult {592			let sender = ensure_signed(origin)?;593			let cross_sender = T::CrossAccountId::from_sub(sender.clone());594595			let collection_id = Self::unique_collection_id(rmrk_collection_id)?;596			let nft_id = rmrk_nft_id.into();597598			let collection =599				Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;600			collection.check_is_external()?;601602			Self::destroy_nft(cross_sender, collection_id, nft_id).map_err(|err| {603				if err == <CommonError<T>>::NoPermission.into()604					|| err == <CommonError<T>>::ApprovedValueTooLow.into()605				{606					<Error<T>>::CannotRejectNonOwnedNft.into()607				} else {608					Self::map_unique_err_to_proxy(err)609				}610			})?;611612			Self::deposit_event(Event::NFTRejected {613				sender,614				collection_id: rmrk_collection_id,615				nft_id: rmrk_nft_id,616			});617618			Ok(())619		}620621		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]622		#[transactional]623		pub fn accept_resource(624			origin: OriginFor<T>,625			rmrk_collection_id: RmrkCollectionId,626			rmrk_nft_id: RmrkNftId,627			rmrk_resource_id: RmrkResourceId,628		) -> DispatchResult {629			let sender = ensure_signed(origin)?;630			let cross_sender = T::CrossAccountId::from_sub(sender);631632			let collection_id = Self::unique_collection_id(rmrk_collection_id)633				.map_err(|_| <Error<T>>::ResourceDoesntExist)?;634			let collection =635				Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;636			collection.check_is_external()?;637638			let nft_id = rmrk_nft_id.into();639			let resource_id = rmrk_resource_id.into();640641			let budget = budget::Value::new(NESTING_BUDGET);642643			let nft_owner =644				<PalletStructure<T>>::find_topmost_owner(collection_id, nft_id, &budget)645					.map_err(|_| <Error<T>>::ResourceDoesntExist)?;646647			let resource_collection_id: CollectionId =648				Self::get_nft_property_decoded(collection_id, nft_id, ResourceCollection)649					.map_err(|_| <Error<T>>::ResourceDoesntExist)?;650651			let is_pending: bool = Self::get_nft_property_decoded(652				resource_collection_id,653				resource_id,654				PendingResourceAccept,655			)656			.map_err(|_| <Error<T>>::ResourceDoesntExist)?;657658			ensure!(is_pending, <Error<T>>::ResourceNotPending);659660			ensure!(cross_sender == nft_owner, <Error<T>>::NoPermission);661662			<PalletNft<T>>::set_scoped_token_property(663				resource_collection_id,664				rmrk_resource_id.into(),665				PropertyScope::Rmrk,666				Self::rmrk_property(PendingResourceAccept, &false)?,667			)?;668669			Self::deposit_event(Event::<T>::ResourceAccepted {670				nft_id: rmrk_nft_id,671				resource_id: rmrk_resource_id,672			});673674			Ok(())675		}676677		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]678		#[transactional]679		pub fn accept_resource_removal(680			origin: OriginFor<T>,681			rmrk_collection_id: RmrkCollectionId,682			rmrk_nft_id: RmrkNftId,683			rmrk_resource_id: RmrkResourceId,684		) -> DispatchResult {685			let sender = ensure_signed(origin)?;686			let cross_sender = T::CrossAccountId::from_sub(sender);687688			let collection_id = Self::unique_collection_id(rmrk_collection_id)689				.map_err(|_| <Error<T>>::ResourceDoesntExist)?;690			let collection =691				Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;692			collection.check_is_external()?;693694			let nft_id = rmrk_nft_id.into();695			let resource_id = rmrk_resource_id.into();696697			let budget = budget::Value::new(NESTING_BUDGET);698699			let nft_owner =700				<PalletStructure<T>>::find_topmost_owner(collection_id, nft_id, &budget)701					.map_err(|_| <Error<T>>::ResourceDoesntExist)?;702703			ensure!(cross_sender == nft_owner, <Error<T>>::NoPermission);704705			let resource_collection_id: CollectionId =706				Self::get_nft_property_decoded(collection_id, nft_id, ResourceCollection)707					.map_err(|_| <Error<T>>::ResourceDoesntExist)?;708709			let is_pending: bool = Self::get_nft_property_decoded(710				resource_collection_id,711				resource_id,712				PendingResourceRemoval,713			)714			.map_err(|_| <Error<T>>::ResourceDoesntExist)?;715716			ensure!(is_pending, <Error<T>>::ResourceNotPending);717718			let resource_collection = Self::get_typed_nft_collection(719				resource_collection_id,720				misc::CollectionType::Resource,721			)?;722723			<PalletNft<T>>::burn(&resource_collection, &cross_sender, rmrk_resource_id.into())724				.map_err(Self::map_unique_err_to_proxy)?;725726			Self::deposit_event(Event::<T>::ResourceRemovalAccepted {727				nft_id: rmrk_nft_id,728				resource_id: rmrk_resource_id,729			});730731			Ok(())732		}733734		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]735		#[transactional]736		pub fn set_property(737			origin: OriginFor<T>,738			#[pallet::compact] rmrk_collection_id: RmrkCollectionId,739			maybe_nft_id: Option<RmrkNftId>,740			key: RmrkKeyString,741			value: RmrkValueString,742		) -> DispatchResult {743			let sender = ensure_signed(origin)?;744			let sender = T::CrossAccountId::from_sub(sender);745746			let collection_id = Self::unique_collection_id(rmrk_collection_id)?;747			let collection =748				Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;749			collection.check_is_external()?;750751			let budget = budget::Value::new(NESTING_BUDGET);752753			match maybe_nft_id {754				Some(nft_id) => {755					let token_id: TokenId = nft_id.into();756757					Self::ensure_nft_type(collection_id, token_id, NftType::Regular)?;758					Self::ensure_nft_owner(collection_id, token_id, &sender, &budget)?;759760					<PalletNft<T>>::set_scoped_token_property(761						collection_id,762						token_id,763						PropertyScope::Rmrk,764						Self::rmrk_property(UserProperty(key.as_slice()), &value)?,765					)?;766				}767				None => {768					let collection = Self::get_typed_nft_collection(769						collection_id,770						misc::CollectionType::Regular,771					)?;772773					Self::check_collection_owner(&collection, &sender)?;774775					<PalletCommon<T>>::set_scoped_collection_property(776						collection_id,777						PropertyScope::Rmrk,778						Self::rmrk_property(UserProperty(key.as_slice()), &value)?,779					)?;780				}781			}782783			Self::deposit_event(Event::PropertySet {784				collection_id: rmrk_collection_id,785				maybe_nft_id,786				key,787				value,788			});789790			Ok(())791		}792793		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]794		#[transactional]795		pub fn set_priority(796			origin: OriginFor<T>,797			rmrk_collection_id: RmrkCollectionId,798			rmrk_nft_id: RmrkNftId,799			priorities: BoundedVec<RmrkResourceId, RmrkMaxPriorities>,800		) -> DispatchResult {801			let sender = ensure_signed(origin)?;802			let sender = T::CrossAccountId::from_sub(sender);803804			let collection_id = Self::unique_collection_id(rmrk_collection_id)?;805			let nft_id = rmrk_nft_id.into();806807			let collection =808				Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;809			collection.check_is_external()?;810811			let budget = budget::Value::new(NESTING_BUDGET);812813			Self::ensure_nft_type(collection_id, nft_id, NftType::Regular)?;814			Self::ensure_nft_owner(collection_id, nft_id, &sender, &budget)?;815816			<PalletNft<T>>::set_scoped_token_property(817				collection_id,818				nft_id,819				PropertyScope::Rmrk,820				Self::rmrk_property(ResourcePriorities, &priorities.into_inner())?,821			)?;822823			Self::deposit_event(Event::<T>::PrioritySet {824				collection_id: rmrk_collection_id,825				nft_id: rmrk_nft_id,826			});827828			Ok(())829		}830831		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]832		#[transactional]833		pub fn add_basic_resource(834			origin: OriginFor<T>,835			rmrk_collection_id: RmrkCollectionId,836			nft_id: RmrkNftId,837			resource: RmrkBasicResource,838		) -> DispatchResult {839			let sender = ensure_signed(origin.clone())?;840841			let collection_id = Self::unique_collection_id(rmrk_collection_id)?;842			let collection =843				Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;844			collection.check_is_external()?;845846			let resource_id = Self::resource_add(847				sender,848				collection_id,849				nft_id.into(),850				[851					Self::rmrk_property(TokenType, &NftType::Resource)?,852					Self::rmrk_property(ResourceType, &misc::ResourceType::Basic)?,853					Self::rmrk_property(Src, &resource.src)?,854					Self::rmrk_property(Metadata, &resource.metadata)?,855					Self::rmrk_property(License, &resource.license)?,856					Self::rmrk_property(Thumb, &resource.thumb)?,857				]858				.into_iter(),859			)?;860861			Self::deposit_event(Event::ResourceAdded {862				nft_id,863				resource_id,864			});865			Ok(())866		}867868		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]869		#[transactional]870		pub fn add_composable_resource(871			origin: OriginFor<T>,872			rmrk_collection_id: RmrkCollectionId,873			nft_id: RmrkNftId,874			_resource_id: RmrkBoundedResource,875			resource: RmrkComposableResource,876		) -> DispatchResult {877			let sender = ensure_signed(origin.clone())?;878879			let collection_id = Self::unique_collection_id(rmrk_collection_id)?;880			let collection =881				Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;882			collection.check_is_external()?;883884			let resource_id = Self::resource_add(885				sender,886				collection_id,887				nft_id.into(),888				[889					Self::rmrk_property(TokenType, &NftType::Resource)?,890					Self::rmrk_property(ResourceType, &misc::ResourceType::Composable)?,891					Self::rmrk_property(Parts, &resource.parts)?,892					Self::rmrk_property(Base, &resource.base)?,893					Self::rmrk_property(Src, &resource.src)?,894					Self::rmrk_property(Metadata, &resource.metadata)?,895					Self::rmrk_property(License, &resource.license)?,896					Self::rmrk_property(Thumb, &resource.thumb)?,897				]898				.into_iter(),899			)?;900901			Self::deposit_event(Event::ResourceAdded {902				nft_id,903				resource_id,904			});905			Ok(())906		}907908		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]909		#[transactional]910		pub fn add_slot_resource(911			origin: OriginFor<T>,912			rmrk_collection_id: RmrkCollectionId,913			nft_id: RmrkNftId,914			resource: RmrkSlotResource,915		) -> DispatchResult {916			let sender = ensure_signed(origin.clone())?;917918			let collection_id = Self::unique_collection_id(rmrk_collection_id)?;919			let collection =920				Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;921			collection.check_is_external()?;922923			let resource_id = Self::resource_add(924				sender,925				collection_id,926				nft_id.into(),927				[928					Self::rmrk_property(TokenType, &NftType::Resource)?,929					Self::rmrk_property(ResourceType, &misc::ResourceType::Slot)?,930					Self::rmrk_property(Base, &resource.base)?,931					Self::rmrk_property(Src, &resource.src)?,932					Self::rmrk_property(Metadata, &resource.metadata)?,933					Self::rmrk_property(Slot, &resource.slot)?,934					Self::rmrk_property(License, &resource.license)?,935					Self::rmrk_property(Thumb, &resource.thumb)?,936				]937				.into_iter(),938			)?;939940			Self::deposit_event(Event::ResourceAdded {941				nft_id,942				resource_id,943			});944			Ok(())945		}946947		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]948		#[transactional]949		pub fn remove_resource(950			origin: OriginFor<T>,951			rmrk_collection_id: RmrkCollectionId,952			nft_id: RmrkNftId,953			resource_id: RmrkResourceId,954		) -> DispatchResult {955			let sender = ensure_signed(origin.clone())?;956957			let collection_id = Self::unique_collection_id(rmrk_collection_id)?;958			let collection =959				Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;960			collection.check_is_external()?;961962			Self::resource_remove(sender, collection_id, nft_id.into(), resource_id.into())?;963964			Self::deposit_event(Event::ResourceRemoval {965				nft_id,966				resource_id,967			});968			Ok(())969		}970	}971}972973impl<T: Config> Pallet<T> {974	pub fn rmrk_property_key(rmrk_key: RmrkProperty) -> Result<PropertyKey, DispatchError> {975		let key = rmrk_key.to_key::<T>()?;976977		let scoped_key = PropertyScope::Rmrk978			.apply(key)979			.map_err(|_| <Error<T>>::RmrkPropertyKeyIsTooLong)?;980981		Ok(scoped_key)982	}983984	// todo think about renaming these985	pub fn rmrk_property<E: Encode>(986		rmrk_key: RmrkProperty,987		value: &E,988	) -> Result<Property, DispatchError> {989		let key = rmrk_key.to_key::<T>()?;990991		let value = value992			.encode()993			.try_into()994			.map_err(|_| <Error<T>>::RmrkPropertyValueIsTooLong)?;995996		let property = Property { key, value };997998		Ok(property)999	}10001001	pub fn decode_property<D: Decode>(vec: PropertyValue) -> Result<D, DispatchError> {1002		vec.decode()1003			.map_err(|_| <Error<T>>::RmrkPropertyValueIsTooLong.into())1004	}10051006	pub fn rebind<L, S>(vec: &BoundedVec<u8, L>) -> Result<BoundedVec<u8, S>, DispatchError>1007	where1008		BoundedVec<u8, S>: TryFrom<Vec<u8>>,1009	{1010		vec.rebind()1011			.map_err(|_| <Error<T>>::RmrkPropertyValueIsTooLong.into())1012	}10131014	fn init_collection(1015		sender: T::CrossAccountId,1016		data: CreateCollectionData<T::AccountId>,1017		properties: impl Iterator<Item = Property>,1018	) -> Result<CollectionId, DispatchError> {1019		let collection_id = <PalletNft<T>>::init_collection(sender, data, true);10201021		if let Err(DispatchError::Arithmetic(_)) = &collection_id {1022			return Err(<Error<T>>::NoAvailableCollectionId.into());1023		}10241025		<PalletCommon<T>>::set_scoped_collection_properties(1026			collection_id?,1027			PropertyScope::Rmrk,1028			properties,1029		)?;10301031		collection_id1032	}10331034	pub fn create_nft(1035		sender: &T::CrossAccountId,1036		owner: &T::CrossAccountId,1037		collection: &NonfungibleHandle<T>,1038		properties: impl Iterator<Item = Property>,1039	) -> Result<TokenId, DispatchError> {1040		let data = CreateNftExData {1041			properties: BoundedVec::default(),1042			owner: owner.clone(),1043		};10441045		let budget = budget::Value::new(NESTING_BUDGET);10461047		<PalletNft<T>>::create_item(collection, sender, data, &budget)?;10481049		let nft_id = <PalletNft<T>>::current_token_id(collection.id);10501051		<PalletNft<T>>::set_scoped_token_properties(1052			collection.id,1053			nft_id,1054			PropertyScope::Rmrk,1055			properties,1056		)?;10571058		Ok(nft_id)1059	}10601061	fn destroy_nft(1062		sender: T::CrossAccountId,1063		collection_id: CollectionId,1064		token_id: TokenId,1065	) -> DispatchResult {1066		let collection =1067			Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;10681069		let token_data =1070			<TokenData<T>>::get((collection_id, token_id)).ok_or(<Error<T>>::NoAvailableNftId)?;10711072		let from = token_data.owner;10731074		let budget = budget::Value::new(NESTING_BUDGET);10751076		<PalletNft<T>>::burn_from(&collection, &sender, &from, token_id, &budget)1077	}10781079	fn resource_add(1080		sender: T::AccountId,1081		collection_id: CollectionId,1082		token_id: TokenId,1083		resource_properties: impl Iterator<Item = Property>,1084	) -> Result<RmrkResourceId, DispatchError> {1085		let collection =1086			Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;1087		ensure!(collection.owner == sender, Error::<T>::NoPermission);10881089		let sender = T::CrossAccountId::from_sub(sender);1090		let budget = budget::Value::new(NESTING_BUDGET);10911092		let nft_owner = <PalletStructure<T>>::find_topmost_owner(collection_id, token_id, &budget)1093			.map_err(Self::map_unique_err_to_proxy)?;10941095		let pending = sender != nft_owner;10961097		let resource_collection_id: CollectionId =1098			Self::get_nft_property_decoded(collection_id, token_id, ResourceCollection)?;1099		let resource_collection =1100			Self::get_typed_nft_collection(resource_collection_id, misc::CollectionType::Resource)?;11011102		// todo probably add extra connections to bases, slots, etc., when RMRK starts to use them11031104		let resource_id = Self::create_nft(1105			&sender,1106			&nft_owner,1107			&resource_collection,1108			resource_properties.chain(1109				[1110					Self::rmrk_property(PendingResourceAccept, &pending)?,1111					Self::rmrk_property(PendingResourceRemoval, &false)?,1112				]1113				.into_iter(),1114			),1115		)1116		.map_err(|err| match err {1117			DispatchError::Arithmetic(_) => <Error<T>>::NoAvailableNftId.into(),1118			err => Self::map_unique_err_to_proxy(err),1119		})?;11201121		Ok(resource_id.0)1122	}11231124	fn resource_remove(1125		sender: T::AccountId,1126		collection_id: CollectionId,1127		nft_id: TokenId,1128		resource_id: TokenId,1129	) -> DispatchResult {1130		let collection =1131			Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;1132		ensure!(collection.owner == sender, Error::<T>::NoPermission);11331134		let resource_collection_id: CollectionId =1135			Self::get_nft_property_decoded(collection_id, nft_id, ResourceCollection)?;1136		let resource_collection =1137			Self::get_typed_nft_collection(resource_collection_id, misc::CollectionType::Resource)?;1138		ensure!(1139			<PalletNft<T>>::token_exists(&resource_collection, resource_id),1140			Error::<T>::ResourceDoesntExist1141		);11421143		let budget = up_data_structs::budget::Value::new(10);1144		let topmost_owner =1145			<PalletStructure<T>>::find_topmost_owner(collection_id, nft_id, &budget)?;11461147		let sender = T::CrossAccountId::from_sub(sender);1148		if topmost_owner == sender {1149			<PalletNft<T>>::burn(&resource_collection, &sender, resource_id)1150				.map_err(Self::map_unique_err_to_proxy)?;1151		} else {1152			<PalletNft<T>>::set_scoped_token_property(1153				resource_collection_id,1154				resource_id,1155				PropertyScope::Rmrk,1156				Self::rmrk_property(PendingResourceRemoval, &true)?,1157			)?;1158		}11591160		Ok(())1161	}11621163	fn change_collection_owner(1164		collection_id: CollectionId,1165		collection_type: misc::CollectionType,1166		sender: T::AccountId,1167		new_owner: T::AccountId,1168	) -> DispatchResult {1169		let collection = Self::get_typed_nft_collection(collection_id, collection_type)?;1170		Self::check_collection_owner(&collection, &T::CrossAccountId::from_sub(sender))?;11711172		let mut collection = collection.into_inner();11731174		collection.owner = new_owner;1175		collection.save()1176	}11771178	fn check_collection_owner(1179		collection: &NonfungibleHandle<T>,1180		account: &T::CrossAccountId,1181	) -> DispatchResult {1182		collection1183			.check_is_owner(account)1184			.map_err(Self::map_unique_err_to_proxy)1185	}11861187	pub fn last_collection_idx() -> RmrkCollectionId {1188		<CollectionIndex<T>>::get()1189	}11901191	pub fn unique_collection_id(1192		rmrk_collection_id: RmrkCollectionId,1193	) -> Result<CollectionId, DispatchError> {1194		<UniqueCollectionId<T>>::try_get(rmrk_collection_id)1195			.map_err(|_| <Error<T>>::CollectionUnknown.into())1196	}11971198	pub fn rmrk_collection_id(1199		unique_collection_id: CollectionId,1200	) -> Result<RmrkCollectionId, DispatchError> {1201		<RmrkInernalCollectionId<T>>::try_get(unique_collection_id)1202			.map_err(|_| <Error<T>>::CollectionUnknown.into())1203	}12041205	pub fn get_nft_collection(1206		collection_id: CollectionId,1207	) -> Result<NonfungibleHandle<T>, DispatchError> {1208		let collection = <CollectionHandle<T>>::try_get(collection_id)1209			.map_err(|_| <Error<T>>::CollectionUnknown)?;12101211		match collection.mode {1212			CollectionMode::NFT => Ok(NonfungibleHandle::cast(collection)),1213			_ => Err(<Error<T>>::CollectionUnknown.into()),1214		}1215	}12161217	pub fn collection_exists(collection_id: CollectionId) -> bool {1218		<CollectionHandle<T>>::try_get(collection_id).is_ok()1219	}12201221	pub fn get_collection_property(1222		collection_id: CollectionId,1223		key: RmrkProperty,1224	) -> Result<PropertyValue, DispatchError> {1225		let collection_property = <PalletCommon<T>>::collection_properties(collection_id)1226			.get(&Self::rmrk_property_key(key)?)1227			.ok_or(<Error<T>>::CollectionUnknown)?1228			.clone();12291230		Ok(collection_property)1231	}12321233	pub fn get_collection_property_decoded<V: Decode>(1234		collection_id: CollectionId,1235		key: RmrkProperty,1236	) -> Result<V, DispatchError> {1237		Self::decode_property(Self::get_collection_property(collection_id, key)?)1238	}12391240	pub fn get_collection_type(1241		collection_id: CollectionId,1242	) -> Result<misc::CollectionType, DispatchError> {1243		Self::get_collection_property_decoded(collection_id, CollectionType)1244			.map_err(|_| <Error<T>>::CorruptedCollectionType.into())1245	}12461247	pub fn ensure_collection_type(1248		collection_id: CollectionId,1249		collection_type: misc::CollectionType,1250	) -> DispatchResult {1251		let actual_type = Self::get_collection_type(collection_id)?;1252		ensure!(1253			actual_type == collection_type,1254			<CommonError<T>>::NoPermission1255		);12561257		Ok(())1258	}12591260	pub fn get_typed_nft_collection(1261		collection_id: CollectionId,1262		collection_type: misc::CollectionType,1263	) -> Result<NonfungibleHandle<T>, DispatchError> {1264		Self::ensure_collection_type(collection_id, collection_type)?;12651266		Self::get_nft_collection(collection_id)1267	}12681269	pub fn get_typed_nft_collection_mapped(1270		rmrk_collection_id: RmrkCollectionId,1271		collection_type: misc::CollectionType,1272	) -> Result<(NonfungibleHandle<T>, CollectionId), DispatchError> {1273		let unique_collection_id = match collection_type {1274			misc::CollectionType::Regular => Self::unique_collection_id(rmrk_collection_id)?,1275			_ => rmrk_collection_id.into(),1276		};12771278		let collection = Self::get_typed_nft_collection(unique_collection_id, collection_type)?;12791280		Ok((collection, unique_collection_id))1281	}12821283	pub fn get_nft_property(1284		collection_id: CollectionId,1285		nft_id: TokenId,1286		key: RmrkProperty,1287	) -> Result<PropertyValue, DispatchError> {1288		let nft_property = <PalletNft<T>>::token_properties((collection_id, nft_id))1289			.get(&Self::rmrk_property_key(key)?)1290			.ok_or(<Error<T>>::NoAvailableNftId)? // todo replace with better error?1291			.clone();12921293		Ok(nft_property)1294	}12951296	pub fn get_nft_property_decoded<V: Decode>(1297		collection_id: CollectionId,1298		nft_id: TokenId,1299		key: RmrkProperty,1300	) -> Result<V, DispatchError> {1301		Self::decode_property(Self::get_nft_property(collection_id, nft_id, key)?)1302	}13031304	pub fn nft_exists(collection_id: CollectionId, nft_id: TokenId) -> bool {1305		<TokenData<T>>::contains_key((collection_id, nft_id))1306	}13071308	pub fn get_nft_type(1309		collection_id: CollectionId,1310		token_id: TokenId,1311	) -> Result<NftType, DispatchError> {1312		Self::get_nft_property_decoded(collection_id, token_id, TokenType)1313			.map_err(|_| <Error<T>>::NoAvailableNftId.into())1314	}13151316	pub fn ensure_nft_type(1317		collection_id: CollectionId,1318		token_id: TokenId,1319		nft_type: NftType,1320	) -> DispatchResult {1321		let actual_type = Self::get_nft_type(collection_id, token_id)?;1322		ensure!(actual_type == nft_type, <Error<T>>::NoPermission);13231324		Ok(())1325	}13261327	pub fn ensure_nft_owner(1328		collection_id: CollectionId,1329		token_id: TokenId,1330		possible_owner: &T::CrossAccountId,1331		nesting_budget: &dyn budget::Budget,1332	) -> DispatchResult {1333		let is_owned = <PalletStructure<T>>::check_indirectly_owned(1334			possible_owner.clone(),1335			collection_id,1336			token_id,1337			None,1338			nesting_budget,1339		)1340		.map_err(Self::map_unique_err_to_proxy)?;13411342		ensure!(is_owned, <Error<T>>::NoPermission);13431344		Ok(())1345	}13461347	pub fn filter_user_properties<Key, Value, R, Mapper>(1348		collection_id: CollectionId,1349		token_id: Option<TokenId>,1350		filter_keys: Option<Vec<RmrkPropertyKey>>,1351		mapper: Mapper,1352	) -> Result<Vec<R>, DispatchError>1353	where1354		Key: TryFrom<RmrkPropertyKey> + AsRef<[u8]>,1355		Value: Decode + Default,1356		Mapper: Fn(Key, Value) -> R,1357	{1358		filter_keys1359			.map(|keys| {1360				let properties = keys1361					.into_iter()1362					.filter_map(|key| {1363						let key: Key = key.try_into().ok()?;13641365						let value = match token_id {1366							Some(token_id) => Self::get_nft_property_decoded(1367								collection_id,1368								token_id,1369								UserProperty(key.as_ref()),1370							),1371							None => Self::get_collection_property_decoded(1372								collection_id,1373								UserProperty(key.as_ref()),1374							),1375						}1376						.ok()?;13771378						Some(mapper(key, value))1379					})1380					.collect();13811382				Ok(properties)1383			})1384			.unwrap_or_else(|| {1385				let properties =1386					Self::iterate_user_properties(collection_id, token_id, mapper)?.collect();13871388				Ok(properties)1389			})1390	}13911392	pub fn iterate_user_properties<Key, Value, R, Mapper>(1393		collection_id: CollectionId,1394		token_id: Option<TokenId>,1395		mapper: Mapper,1396	) -> Result<impl Iterator<Item = R>, DispatchError>1397	where1398		Key: TryFrom<RmrkPropertyKey> + AsRef<[u8]>,1399		Value: Decode + Default,1400		Mapper: Fn(Key, Value) -> R,1401	{1402		let key_prefix = Self::rmrk_property_key(UserProperty(b""))?;14031404		let properties = match token_id {1405			Some(token_id) => <PalletNft<T>>::token_properties((collection_id, token_id)),1406			None => <PalletCommon<T>>::collection_properties(collection_id),1407		};14081409		let properties = properties.into_iter().filter_map(move |(key, value)| {1410			let key = key.as_slice().strip_prefix(key_prefix.as_slice())?;14111412			let key: Key = key.to_vec().try_into().ok()?;1413			let value: Value = value.decode().ok()?;14141415			Some(mapper(key, value))1416		});14171418		Ok(properties)1419	}14201421	fn map_unique_err_to_proxy(err: DispatchError) -> DispatchError {1422		map_unique_err_to_proxy! {1423			match err {1424				CommonError::NoPermission => NoPermission,1425				CommonError::CollectionTokenLimitExceeded => CollectionFullOrLocked,1426				CommonError::PublicMintingNotAllowed => NoPermission,1427				CommonError::TokenNotFound => NoAvailableNftId,1428				CommonError::ApprovedValueTooLow => NoPermission,1429				CommonError::CantDestroyNotEmptyCollection => CollectionNotEmpty,1430				StructureError::TokenNotFound => NoAvailableNftId,1431				StructureError::OuroborosDetected => CannotSendToDescendentOrSelf,1432			}1433		}1434	}1435}
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, Error as StructureError};29use pallet_evm::account::CrossAccountId;30use core::convert::AsRef;3132pub use pallet::*;3334#[cfg(feature = "runtime-benchmarks")]35pub mod benchmarking;36pub mod misc;37pub mod property;38pub mod weights;3940pub type SelfWeightOf<T> = <T as Config>::WeightInfo;4142use weights::WeightInfo;43use misc::*;44pub use property::*;4546use RmrkProperty::*;4748const NESTING_BUDGET: u32 = 5;4950#[frame_support::pallet]51pub mod pallet {52	use super::*;53	use pallet_evm::account;5455	#[pallet::config]56	pub trait Config:57		frame_system::Config + pallet_common::Config + pallet_nonfungible::Config + account::Config58	{59		type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;60		type WeightInfo: WeightInfo;61	}6263	#[pallet::storage]64	#[pallet::getter(fn collection_index)]65	pub type CollectionIndex<T: Config> = StorageValue<_, RmrkCollectionId, ValueQuery>;6667	#[pallet::storage]68	pub type UniqueCollectionId<T: Config> =69		StorageMap<_, Twox64Concat, RmrkCollectionId, CollectionId, ValueQuery>;7071	#[pallet::storage]72	pub type RmrkInernalCollectionId<T: Config> =73		StorageMap<_, Twox64Concat, CollectionId, RmrkCollectionId, ValueQuery>;7475	#[pallet::pallet]76	#[pallet::generate_store(pub(super) trait Store)]77	pub struct Pallet<T>(_);7879	#[pallet::event]80	#[pallet::generate_deposit(pub(super) fn deposit_event)]81	pub enum Event<T: Config> {82		CollectionCreated {83			issuer: T::AccountId,84			collection_id: RmrkCollectionId,85		},86		CollectionDestroyed {87			issuer: T::AccountId,88			collection_id: RmrkCollectionId,89		},90		IssuerChanged {91			old_issuer: T::AccountId,92			new_issuer: T::AccountId,93			collection_id: RmrkCollectionId,94		},95		CollectionLocked {96			issuer: T::AccountId,97			collection_id: RmrkCollectionId,98		},99		NftMinted {100			owner: T::AccountId,101			collection_id: RmrkCollectionId,102			nft_id: RmrkNftId,103		},104		NFTBurned {105			owner: T::AccountId,106			nft_id: RmrkNftId,107		},108		NFTSent {109			sender: T::AccountId,110			recipient: RmrkAccountIdOrCollectionNftTuple<T::AccountId>,111			collection_id: RmrkCollectionId,112			nft_id: RmrkNftId,113			approval_required: bool,114		},115		NFTAccepted {116			sender: T::AccountId,117			recipient: RmrkAccountIdOrCollectionNftTuple<T::AccountId>,118			collection_id: RmrkCollectionId,119			nft_id: RmrkNftId,120		},121		NFTRejected {122			sender: T::AccountId,123			collection_id: RmrkCollectionId,124			nft_id: RmrkNftId,125		},126		PropertySet {127			collection_id: RmrkCollectionId,128			maybe_nft_id: Option<RmrkNftId>,129			key: RmrkKeyString,130			value: RmrkValueString,131		},132		ResourceAdded {133			nft_id: RmrkNftId,134			resource_id: RmrkResourceId,135		},136		ResourceRemoval {137			nft_id: RmrkNftId,138			resource_id: RmrkResourceId,139		},140		ResourceAccepted {141			nft_id: RmrkNftId,142			resource_id: RmrkResourceId,143		},144		ResourceRemovalAccepted {145			nft_id: RmrkNftId,146			resource_id: RmrkResourceId,147		},148		PrioritySet {149			collection_id: RmrkCollectionId,150			nft_id: RmrkNftId,151		},152	}153154	#[pallet::error]155	pub enum Error<T> {156		/* Unique-specific events */157		CorruptedCollectionType,158		NftTypeEncodeError,159		RmrkPropertyKeyIsTooLong,160		RmrkPropertyValueIsTooLong,161162		/* RMRK compatible events */163		CollectionNotEmpty,164		NoAvailableCollectionId,165		NoAvailableNftId,166		CollectionUnknown,167		NoPermission,168		NonTransferable,169		CollectionFullOrLocked,170		ResourceDoesntExist,171		CannotSendToDescendentOrSelf,172		CannotAcceptNonOwnedNft,173		CannotRejectNonOwnedNft,174		ResourceNotPending,175	}176177	#[pallet::call]178	impl<T: Config> Pallet<T> {179		#[pallet::weight(<SelfWeightOf<T>>::create_collection())]180		#[transactional]181		pub fn create_collection(182			origin: OriginFor<T>,183			metadata: RmrkString,184			max: Option<u32>,185			symbol: RmrkCollectionSymbol,186		) -> DispatchResult {187			let sender = ensure_signed(origin)?;188189			let limits = CollectionLimits {190				owner_can_transfer: Some(false),191				token_limit: max,192				..Default::default()193			};194195			let data = CreateCollectionData {196				limits: Some(limits),197				token_prefix: symbol198					.into_inner()199					.try_into()200					.map_err(|_| <CommonError<T>>::CollectionTokenPrefixLimitExceeded)?,201				permissions: Some(CollectionPermissions {202					nesting: Some(NestingRule::Owner),203					..Default::default()204				}),205				..Default::default()206			};207208			let unique_collection_id = Self::init_collection(209				T::CrossAccountId::from_sub(sender.clone()),210				data,211				[212					Self::rmrk_property(Metadata, &metadata)?,213					Self::rmrk_property(CollectionType, &misc::CollectionType::Regular)?,214				]215				.into_iter(),216			)?;217			let rmrk_collection_id = <CollectionIndex<T>>::get();218219			<UniqueCollectionId<T>>::insert(rmrk_collection_id, unique_collection_id);220			<RmrkInernalCollectionId<T>>::insert(unique_collection_id, rmrk_collection_id);221222			<CollectionIndex<T>>::mutate(|n| *n += 1);223224			Self::deposit_event(Event::CollectionCreated {225				issuer: sender,226				collection_id: rmrk_collection_id,227			});228229			Ok(())230		}231232		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]233		#[transactional]234		pub fn destroy_collection(235			origin: OriginFor<T>,236			collection_id: RmrkCollectionId,237		) -> DispatchResult {238			let sender = ensure_signed(origin)?;239			let cross_sender = T::CrossAccountId::from_sub(sender.clone());240241			let collection = Self::get_typed_nft_collection(242				Self::unique_collection_id(collection_id)?,243				misc::CollectionType::Regular,244			)?;245			collection.check_is_external()?;246247			<PalletNft<T>>::destroy_collection(collection, &cross_sender)248				.map_err(Self::map_unique_err_to_proxy)?;249250			Self::deposit_event(Event::CollectionDestroyed {251				issuer: sender,252				collection_id,253			});254255			Ok(())256		}257258		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]259		#[transactional]260		pub fn change_collection_issuer(261			origin: OriginFor<T>,262			collection_id: RmrkCollectionId,263			new_issuer: <T::Lookup as StaticLookup>::Source,264		) -> DispatchResult {265			let sender = ensure_signed(origin)?;266267			let collection = Self::get_nft_collection(Self::unique_collection_id(collection_id)?)?;268			collection.check_is_external()?;269270			let new_issuer = T::Lookup::lookup(new_issuer)?;271272			Self::change_collection_owner(273				Self::unique_collection_id(collection_id)?,274				misc::CollectionType::Regular,275				sender.clone(),276				new_issuer.clone(),277			)?;278279			Self::deposit_event(Event::IssuerChanged {280				old_issuer: sender,281				new_issuer,282				collection_id,283			});284285			Ok(())286		}287288		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]289		#[transactional]290		pub fn lock_collection(291			origin: OriginFor<T>,292			collection_id: RmrkCollectionId,293		) -> DispatchResult {294			let sender = ensure_signed(origin)?;295			let cross_sender = T::CrossAccountId::from_sub(sender.clone());296297			let collection = Self::get_typed_nft_collection(298				Self::unique_collection_id(collection_id)?,299				misc::CollectionType::Regular,300			)?;301			collection.check_is_external()?;302303			Self::check_collection_owner(&collection, &cross_sender)?;304305			let token_count = collection.total_supply();306307			let mut collection = collection.into_inner();308			collection.limits.token_limit = Some(token_count);309			collection.save()?;310311			Self::deposit_event(Event::CollectionLocked {312				issuer: sender,313				collection_id,314			});315316			Ok(())317		}318319		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]320		#[transactional]321		pub fn mint_nft(322			origin: OriginFor<T>,323			owner: T::AccountId,324			collection_id: RmrkCollectionId,325			recipient: Option<T::AccountId>,326			royalty_amount: Option<Permill>,327			metadata: RmrkString,328			transferable: bool,329		) -> DispatchResult {330			let sender = ensure_signed(origin)?;331			let sender = T::CrossAccountId::from_sub(sender);332			let cross_owner = T::CrossAccountId::from_sub(owner.clone());333334			let collection = Self::get_typed_nft_collection(335				Self::unique_collection_id(collection_id)?,336				misc::CollectionType::Regular,337			)?;338			collection.check_is_external()?;339340			let royalty_info = royalty_amount.map(|amount| rmrk_traits::RoyaltyInfo {341				recipient: recipient.unwrap_or_else(|| owner.clone()),342				amount,343			});344345			let nft_id = Self::create_nft(346				&sender,347				&cross_owner,348				&collection,349				[350					Self::rmrk_property(TokenType, &NftType::Regular)?,351					Self::rmrk_property(Transferable, &transferable)?,352					Self::rmrk_property(PendingNftAccept, &false)?,353					Self::rmrk_property(RoyaltyInfo, &royalty_info)?,354					Self::rmrk_property(Metadata, &metadata)?,355					Self::rmrk_property(Equipped, &false)?,356					Self::rmrk_property(357						ResourceCollection,358						&Self::init_collection(359							sender.clone(),360							CreateCollectionData {361								..Default::default()362							},363							[Self::rmrk_property(364								CollectionType,365								&misc::CollectionType::Resource,366							)?]367							.into_iter(),368						)?,369					)?, // todo possibly add limits to the collection if rmrk warrants them370					Self::rmrk_property(ResourcePriorities, &<Vec<u8>>::new())?,371				]372				.into_iter(),373			)374			.map_err(|err| match err {375				DispatchError::Arithmetic(_) => <Error<T>>::NoAvailableNftId.into(),376				err => Self::map_unique_err_to_proxy(err),377			})?;378379			Self::deposit_event(Event::NftMinted {380				owner,381				collection_id,382				nft_id: nft_id.0,383			});384385			Ok(())386		}387388		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]389		#[transactional]390		pub fn burn_nft(391			origin: OriginFor<T>,392			collection_id: RmrkCollectionId,393			nft_id: RmrkNftId,394		) -> DispatchResult {395			let sender = ensure_signed(origin)?;396			let cross_sender = T::CrossAccountId::from_sub(sender.clone());397398			let collection = Self::get_typed_nft_collection(399				Self::unique_collection_id(collection_id)?,400				misc::CollectionType::Regular,401			)?;402			collection.check_is_external()?;403404			Self::destroy_nft(405				cross_sender,406				Self::unique_collection_id(collection_id)?,407				nft_id.into(),408			)409			.map_err(Self::map_unique_err_to_proxy)?;410411			Self::deposit_event(Event::NFTBurned {412				owner: sender,413				nft_id,414			});415416			Ok(())417		}418419		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]420		#[transactional]421		pub fn send(422			origin: OriginFor<T>,423			rmrk_collection_id: RmrkCollectionId,424			rmrk_nft_id: RmrkNftId,425			new_owner: RmrkAccountIdOrCollectionNftTuple<T::AccountId>,426		) -> DispatchResult {427			let sender = ensure_signed(origin.clone())?;428			let cross_sender = T::CrossAccountId::from_sub(sender.clone());429430			let collection_id = Self::unique_collection_id(rmrk_collection_id)?;431			let nft_id = rmrk_nft_id.into();432433			let collection =434				Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;435			collection.check_is_external()?;436437			let token_data =438				<TokenData<T>>::get((collection_id, nft_id)).ok_or(<Error<T>>::NoAvailableNftId)?;439440			let from = token_data.owner;441442			ensure!(443				Self::get_nft_property_decoded(collection_id, nft_id, RmrkProperty::Transferable)?,444				<Error<T>>::NonTransferable445			);446447			ensure!(448				!Self::get_nft_property_decoded(449					collection_id,450					nft_id,451					RmrkProperty::PendingNftAccept452				)?,453				<Error<T>>::NoPermission454			);455456			let target_owner;457			let approval_required;458459			match new_owner {460				RmrkAccountIdOrCollectionNftTuple::AccountId(ref account_id) => {461					target_owner = T::CrossAccountId::from_sub(account_id.clone());462					approval_required = false;463				}464				RmrkAccountIdOrCollectionNftTuple::CollectionAndNftTuple(465					target_collection_id,466					target_nft_id,467				) => {468					let target_collection_id = Self::unique_collection_id(target_collection_id)?;469470					let target_nft_budget = budget::Value::new(NESTING_BUDGET);471472					let target_nft_owner = <PalletStructure<T>>::get_checked_topmost_owner(473						target_collection_id,474						target_nft_id.into(),475						Some((collection_id, nft_id)),476						&target_nft_budget,477					)478					.map_err(Self::map_unique_err_to_proxy)?;479480					approval_required = cross_sender != target_nft_owner;481482					if approval_required {483						target_owner = target_nft_owner;484485						<PalletNft<T>>::set_scoped_token_property(486							collection.id,487							nft_id,488							PropertyScope::Rmrk,489							Self::rmrk_property(PendingNftAccept, &approval_required)?,490						)?;491					} else {492						target_owner = T::CrossTokenAddressMapping::token_to_address(493							target_collection_id,494							target_nft_id.into(),495						);496					}497				}498			}499500			let src_nft_budget = budget::Value::new(NESTING_BUDGET);501502			<PalletNft<T>>::transfer_from(503				&collection,504				&cross_sender,505				&from,506				&target_owner,507				nft_id,508				&src_nft_budget,509			)510			.map_err(Self::map_unique_err_to_proxy)?;511512			Self::deposit_event(Event::NFTSent {513				sender,514				recipient: new_owner,515				collection_id: rmrk_collection_id,516				nft_id: rmrk_nft_id,517				approval_required,518			});519520			Ok(())521		}522523		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]524		#[transactional]525		pub fn accept_nft(526			origin: OriginFor<T>,527			rmrk_collection_id: RmrkCollectionId,528			rmrk_nft_id: RmrkNftId,529			new_owner: RmrkAccountIdOrCollectionNftTuple<T::AccountId>,530		) -> DispatchResult {531			let sender = ensure_signed(origin.clone())?;532			let cross_sender = T::CrossAccountId::from_sub(sender.clone());533534			let collection_id = Self::unique_collection_id(rmrk_collection_id)?;535			let nft_id = rmrk_nft_id.into();536537			let collection =538				Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;539			collection.check_is_external()?;540541			let new_cross_owner = match new_owner {542				RmrkAccountIdOrCollectionNftTuple::AccountId(ref account_id) => {543					T::CrossAccountId::from_sub(account_id.clone())544				}545				RmrkAccountIdOrCollectionNftTuple::CollectionAndNftTuple(546					target_collection_id,547					target_nft_id,548				) => {549					let target_collection_id = Self::unique_collection_id(target_collection_id)?;550551					T::CrossTokenAddressMapping::token_to_address(552						target_collection_id,553						TokenId(target_nft_id),554					)555				}556			};557558			let budget = budget::Value::new(NESTING_BUDGET);559560			<PalletNft<T>>::transfer(561				&collection,562				&cross_sender,563				&new_cross_owner,564				nft_id,565				&budget,566			)567			.map_err(|err| {568				if err == <CommonError<T>>::OnlyOwnerAllowedToNest.into() {569					<Error<T>>::CannotAcceptNonOwnedNft.into()570				} else {571					Self::map_unique_err_to_proxy(err)572				}573			})?;574575			<PalletNft<T>>::set_scoped_token_property(576				collection.id,577				nft_id,578				PropertyScope::Rmrk,579				Self::rmrk_property(PendingNftAccept, &false)?,580			)?;581582			Self::deposit_event(Event::NFTAccepted {583				sender,584				recipient: new_owner,585				collection_id: rmrk_collection_id,586				nft_id: rmrk_nft_id,587			});588589			Ok(())590		}591592		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]593		#[transactional]594		pub fn reject_nft(595			origin: OriginFor<T>,596			rmrk_collection_id: RmrkCollectionId,597			rmrk_nft_id: RmrkNftId,598		) -> DispatchResult {599			let sender = ensure_signed(origin)?;600			let cross_sender = T::CrossAccountId::from_sub(sender.clone());601602			let collection_id = Self::unique_collection_id(rmrk_collection_id)?;603			let nft_id = rmrk_nft_id.into();604605			let collection =606				Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;607			collection.check_is_external()?;608609			Self::destroy_nft(cross_sender, collection_id, nft_id).map_err(|err| {610				if err == <CommonError<T>>::NoPermission.into()611					|| err == <CommonError<T>>::ApprovedValueTooLow.into()612				{613					<Error<T>>::CannotRejectNonOwnedNft.into()614				} else {615					Self::map_unique_err_to_proxy(err)616				}617			})?;618619			Self::deposit_event(Event::NFTRejected {620				sender,621				collection_id: rmrk_collection_id,622				nft_id: rmrk_nft_id,623			});624625			Ok(())626		}627628		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]629		#[transactional]630		pub fn accept_resource(631			origin: OriginFor<T>,632			rmrk_collection_id: RmrkCollectionId,633			rmrk_nft_id: RmrkNftId,634			rmrk_resource_id: RmrkResourceId,635		) -> DispatchResult {636			let sender = ensure_signed(origin)?;637			let cross_sender = T::CrossAccountId::from_sub(sender);638639			let collection_id = Self::unique_collection_id(rmrk_collection_id)640				.map_err(|_| <Error<T>>::ResourceDoesntExist)?;641			let collection =642				Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;643			collection.check_is_external()?;644645			let nft_id = rmrk_nft_id.into();646			let resource_id = rmrk_resource_id.into();647648			let budget = budget::Value::new(NESTING_BUDGET);649650			let nft_owner =651				<PalletStructure<T>>::find_topmost_owner(collection_id, nft_id, &budget)652					.map_err(|_| <Error<T>>::ResourceDoesntExist)?;653654			let resource_collection_id: CollectionId =655				Self::get_nft_property_decoded(collection_id, nft_id, ResourceCollection)656					.map_err(|_| <Error<T>>::ResourceDoesntExist)?;657658			let is_pending: bool = Self::get_nft_property_decoded(659				resource_collection_id,660				resource_id,661				PendingResourceAccept,662			)663			.map_err(|_| <Error<T>>::ResourceDoesntExist)?;664665			ensure!(is_pending, <Error<T>>::ResourceNotPending);666667			ensure!(cross_sender == nft_owner, <Error<T>>::NoPermission);668669			<PalletNft<T>>::set_scoped_token_property(670				resource_collection_id,671				rmrk_resource_id.into(),672				PropertyScope::Rmrk,673				Self::rmrk_property(PendingResourceAccept, &false)?,674			)?;675676			Self::deposit_event(Event::<T>::ResourceAccepted {677				nft_id: rmrk_nft_id,678				resource_id: rmrk_resource_id,679			});680681			Ok(())682		}683684		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]685		#[transactional]686		pub fn accept_resource_removal(687			origin: OriginFor<T>,688			rmrk_collection_id: RmrkCollectionId,689			rmrk_nft_id: RmrkNftId,690			rmrk_resource_id: RmrkResourceId,691		) -> DispatchResult {692			let sender = ensure_signed(origin)?;693			let cross_sender = T::CrossAccountId::from_sub(sender);694695			let collection_id = Self::unique_collection_id(rmrk_collection_id)696				.map_err(|_| <Error<T>>::ResourceDoesntExist)?;697			let collection =698				Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;699			collection.check_is_external()?;700701			let nft_id = rmrk_nft_id.into();702			let resource_id = rmrk_resource_id.into();703704			let budget = budget::Value::new(NESTING_BUDGET);705706			let nft_owner =707				<PalletStructure<T>>::find_topmost_owner(collection_id, nft_id, &budget)708					.map_err(|_| <Error<T>>::ResourceDoesntExist)?;709710			ensure!(cross_sender == nft_owner, <Error<T>>::NoPermission);711712			let resource_collection_id: CollectionId =713				Self::get_nft_property_decoded(collection_id, nft_id, ResourceCollection)714					.map_err(|_| <Error<T>>::ResourceDoesntExist)?;715716			let is_pending: bool = Self::get_nft_property_decoded(717				resource_collection_id,718				resource_id,719				PendingResourceRemoval,720			)721			.map_err(|_| <Error<T>>::ResourceDoesntExist)?;722723			ensure!(is_pending, <Error<T>>::ResourceNotPending);724725			let resource_collection = Self::get_typed_nft_collection(726				resource_collection_id,727				misc::CollectionType::Resource,728			)?;729730			<PalletNft<T>>::burn(&resource_collection, &cross_sender, rmrk_resource_id.into())731				.map_err(Self::map_unique_err_to_proxy)?;732733			Self::deposit_event(Event::<T>::ResourceRemovalAccepted {734				nft_id: rmrk_nft_id,735				resource_id: rmrk_resource_id,736			});737738			Ok(())739		}740741		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]742		#[transactional]743		pub fn set_property(744			origin: OriginFor<T>,745			#[pallet::compact] rmrk_collection_id: RmrkCollectionId,746			maybe_nft_id: Option<RmrkNftId>,747			key: RmrkKeyString,748			value: RmrkValueString,749		) -> DispatchResult {750			let sender = ensure_signed(origin)?;751			let sender = T::CrossAccountId::from_sub(sender);752753			let collection_id = Self::unique_collection_id(rmrk_collection_id)?;754			let collection =755				Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;756			collection.check_is_external()?;757758			let budget = budget::Value::new(NESTING_BUDGET);759760			match maybe_nft_id {761				Some(nft_id) => {762					let token_id: TokenId = nft_id.into();763764					Self::ensure_nft_type(collection_id, token_id, NftType::Regular)?;765					Self::ensure_nft_owner(collection_id, token_id, &sender, &budget)?;766767					<PalletNft<T>>::set_scoped_token_property(768						collection_id,769						token_id,770						PropertyScope::Rmrk,771						Self::rmrk_property(UserProperty(key.as_slice()), &value)?,772					)?;773				}774				None => {775					let collection = Self::get_typed_nft_collection(776						collection_id,777						misc::CollectionType::Regular,778					)?;779780					Self::check_collection_owner(&collection, &sender)?;781782					<PalletCommon<T>>::set_scoped_collection_property(783						collection_id,784						PropertyScope::Rmrk,785						Self::rmrk_property(UserProperty(key.as_slice()), &value)?,786					)?;787				}788			}789790			Self::deposit_event(Event::PropertySet {791				collection_id: rmrk_collection_id,792				maybe_nft_id,793				key,794				value,795			});796797			Ok(())798		}799800		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]801		#[transactional]802		pub fn set_priority(803			origin: OriginFor<T>,804			rmrk_collection_id: RmrkCollectionId,805			rmrk_nft_id: RmrkNftId,806			priorities: BoundedVec<RmrkResourceId, RmrkMaxPriorities>,807		) -> DispatchResult {808			let sender = ensure_signed(origin)?;809			let sender = T::CrossAccountId::from_sub(sender);810811			let collection_id = Self::unique_collection_id(rmrk_collection_id)?;812			let nft_id = rmrk_nft_id.into();813814			let collection =815				Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;816			collection.check_is_external()?;817818			let budget = budget::Value::new(NESTING_BUDGET);819820			Self::ensure_nft_type(collection_id, nft_id, NftType::Regular)?;821			Self::ensure_nft_owner(collection_id, nft_id, &sender, &budget)?;822823			<PalletNft<T>>::set_scoped_token_property(824				collection_id,825				nft_id,826				PropertyScope::Rmrk,827				Self::rmrk_property(ResourcePriorities, &priorities.into_inner())?,828			)?;829830			Self::deposit_event(Event::<T>::PrioritySet {831				collection_id: rmrk_collection_id,832				nft_id: rmrk_nft_id,833			});834835			Ok(())836		}837838		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]839		#[transactional]840		pub fn add_basic_resource(841			origin: OriginFor<T>,842			rmrk_collection_id: RmrkCollectionId,843			nft_id: RmrkNftId,844			resource: RmrkBasicResource,845		) -> DispatchResult {846			let sender = ensure_signed(origin.clone())?;847848			let collection_id = Self::unique_collection_id(rmrk_collection_id)?;849			let collection =850				Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;851			collection.check_is_external()?;852853			let resource_id = Self::resource_add(854				sender,855				collection_id,856				nft_id.into(),857				[858					Self::rmrk_property(TokenType, &NftType::Resource)?,859					Self::rmrk_property(ResourceType, &misc::ResourceType::Basic)?,860					Self::rmrk_property(Src, &resource.src)?,861					Self::rmrk_property(Metadata, &resource.metadata)?,862					Self::rmrk_property(License, &resource.license)?,863					Self::rmrk_property(Thumb, &resource.thumb)?,864				]865				.into_iter(),866			)?;867868			Self::deposit_event(Event::ResourceAdded {869				nft_id,870				resource_id,871			});872			Ok(())873		}874875		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]876		#[transactional]877		pub fn add_composable_resource(878			origin: OriginFor<T>,879			rmrk_collection_id: RmrkCollectionId,880			nft_id: RmrkNftId,881			_resource_id: RmrkBoundedResource,882			resource: RmrkComposableResource,883		) -> DispatchResult {884			let sender = ensure_signed(origin.clone())?;885886			let collection_id = Self::unique_collection_id(rmrk_collection_id)?;887			let collection =888				Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;889			collection.check_is_external()?;890891			let resource_id = Self::resource_add(892				sender,893				collection_id,894				nft_id.into(),895				[896					Self::rmrk_property(TokenType, &NftType::Resource)?,897					Self::rmrk_property(ResourceType, &misc::ResourceType::Composable)?,898					Self::rmrk_property(Parts, &resource.parts)?,899					Self::rmrk_property(Base, &resource.base)?,900					Self::rmrk_property(Src, &resource.src)?,901					Self::rmrk_property(Metadata, &resource.metadata)?,902					Self::rmrk_property(License, &resource.license)?,903					Self::rmrk_property(Thumb, &resource.thumb)?,904				]905				.into_iter(),906			)?;907908			Self::deposit_event(Event::ResourceAdded {909				nft_id,910				resource_id,911			});912			Ok(())913		}914915		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]916		#[transactional]917		pub fn add_slot_resource(918			origin: OriginFor<T>,919			rmrk_collection_id: RmrkCollectionId,920			nft_id: RmrkNftId,921			resource: RmrkSlotResource,922		) -> DispatchResult {923			let sender = ensure_signed(origin.clone())?;924925			let collection_id = Self::unique_collection_id(rmrk_collection_id)?;926			let collection =927				Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;928			collection.check_is_external()?;929930			let resource_id = Self::resource_add(931				sender,932				collection_id,933				nft_id.into(),934				[935					Self::rmrk_property(TokenType, &NftType::Resource)?,936					Self::rmrk_property(ResourceType, &misc::ResourceType::Slot)?,937					Self::rmrk_property(Base, &resource.base)?,938					Self::rmrk_property(Src, &resource.src)?,939					Self::rmrk_property(Metadata, &resource.metadata)?,940					Self::rmrk_property(Slot, &resource.slot)?,941					Self::rmrk_property(License, &resource.license)?,942					Self::rmrk_property(Thumb, &resource.thumb)?,943				]944				.into_iter(),945			)?;946947			Self::deposit_event(Event::ResourceAdded {948				nft_id,949				resource_id,950			});951			Ok(())952		}953954		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]955		#[transactional]956		pub fn remove_resource(957			origin: OriginFor<T>,958			rmrk_collection_id: RmrkCollectionId,959			nft_id: RmrkNftId,960			resource_id: RmrkResourceId,961		) -> DispatchResult {962			let sender = ensure_signed(origin.clone())?;963964			let collection_id = Self::unique_collection_id(rmrk_collection_id)?;965			let collection =966				Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;967			collection.check_is_external()?;968969			Self::resource_remove(sender, collection_id, nft_id.into(), resource_id.into())?;970971			Self::deposit_event(Event::ResourceRemoval {972				nft_id,973				resource_id,974			});975			Ok(())976		}977	}978}979980impl<T: Config> Pallet<T> {981	pub fn rmrk_property_key(rmrk_key: RmrkProperty) -> Result<PropertyKey, DispatchError> {982		let key = rmrk_key.to_key::<T>()?;983984		let scoped_key = PropertyScope::Rmrk985			.apply(key)986			.map_err(|_| <Error<T>>::RmrkPropertyKeyIsTooLong)?;987988		Ok(scoped_key)989	}990991	// todo think about renaming these992	pub fn rmrk_property<E: Encode>(993		rmrk_key: RmrkProperty,994		value: &E,995	) -> Result<Property, DispatchError> {996		let key = rmrk_key.to_key::<T>()?;997998		let value = value999			.encode()1000			.try_into()1001			.map_err(|_| <Error<T>>::RmrkPropertyValueIsTooLong)?;10021003		let property = Property { key, value };10041005		Ok(property)1006	}10071008	pub fn decode_property<D: Decode>(vec: PropertyValue) -> Result<D, DispatchError> {1009		vec.decode()1010			.map_err(|_| <Error<T>>::RmrkPropertyValueIsTooLong.into())1011	}10121013	pub fn rebind<L, S>(vec: &BoundedVec<u8, L>) -> Result<BoundedVec<u8, S>, DispatchError>1014	where1015		BoundedVec<u8, S>: TryFrom<Vec<u8>>,1016	{1017		vec.rebind()1018			.map_err(|_| <Error<T>>::RmrkPropertyValueIsTooLong.into())1019	}10201021	fn init_collection(1022		sender: T::CrossAccountId,1023		data: CreateCollectionData<T::AccountId>,1024		properties: impl Iterator<Item = Property>,1025	) -> Result<CollectionId, DispatchError> {1026		let collection_id = <PalletNft<T>>::init_collection(sender, data, true);10271028		if let Err(DispatchError::Arithmetic(_)) = &collection_id {1029			return Err(<Error<T>>::NoAvailableCollectionId.into());1030		}10311032		<PalletCommon<T>>::set_scoped_collection_properties(1033			collection_id?,1034			PropertyScope::Rmrk,1035			properties,1036		)?;10371038		collection_id1039	}10401041	pub fn create_nft(1042		sender: &T::CrossAccountId,1043		owner: &T::CrossAccountId,1044		collection: &NonfungibleHandle<T>,1045		properties: impl Iterator<Item = Property>,1046	) -> Result<TokenId, DispatchError> {1047		let data = CreateNftExData {1048			properties: BoundedVec::default(),1049			owner: owner.clone(),1050		};10511052		let budget = budget::Value::new(NESTING_BUDGET);10531054		<PalletNft<T>>::create_item(collection, sender, data, &budget)?;10551056		let nft_id = <PalletNft<T>>::current_token_id(collection.id);10571058		<PalletNft<T>>::set_scoped_token_properties(1059			collection.id,1060			nft_id,1061			PropertyScope::Rmrk,1062			properties,1063		)?;10641065		Ok(nft_id)1066	}10671068	fn destroy_nft(1069		sender: T::CrossAccountId,1070		collection_id: CollectionId,1071		token_id: TokenId,1072	) -> DispatchResult {1073		let collection =1074			Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;10751076		let token_data =1077			<TokenData<T>>::get((collection_id, token_id)).ok_or(<Error<T>>::NoAvailableNftId)?;10781079		let from = token_data.owner;10801081		let budget = budget::Value::new(NESTING_BUDGET);10821083		<PalletNft<T>>::burn_from(&collection, &sender, &from, token_id, &budget)1084	}10851086	fn resource_add(1087		sender: T::AccountId,1088		collection_id: CollectionId,1089		token_id: TokenId,1090		resource_properties: impl Iterator<Item = Property>,1091	) -> Result<RmrkResourceId, DispatchError> {1092		let collection =1093			Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;1094		ensure!(collection.owner == sender, Error::<T>::NoPermission);10951096		let sender = T::CrossAccountId::from_sub(sender);1097		let budget = budget::Value::new(NESTING_BUDGET);10981099		let nft_owner = <PalletStructure<T>>::find_topmost_owner(collection_id, token_id, &budget)1100			.map_err(Self::map_unique_err_to_proxy)?;11011102		let pending = sender != nft_owner;11031104		let resource_collection_id: CollectionId =1105			Self::get_nft_property_decoded(collection_id, token_id, ResourceCollection)?;1106		let resource_collection =1107			Self::get_typed_nft_collection(resource_collection_id, misc::CollectionType::Resource)?;11081109		// todo probably add extra connections to bases, slots, etc., when RMRK starts to use them11101111		let resource_id = Self::create_nft(1112			&sender,1113			&nft_owner,1114			&resource_collection,1115			resource_properties.chain(1116				[1117					Self::rmrk_property(PendingResourceAccept, &pending)?,1118					Self::rmrk_property(PendingResourceRemoval, &false)?,1119				]1120				.into_iter(),1121			),1122		)1123		.map_err(|err| match err {1124			DispatchError::Arithmetic(_) => <Error<T>>::NoAvailableNftId.into(),1125			err => Self::map_unique_err_to_proxy(err),1126		})?;11271128		Ok(resource_id.0)1129	}11301131	fn resource_remove(1132		sender: T::AccountId,1133		collection_id: CollectionId,1134		nft_id: TokenId,1135		resource_id: TokenId,1136	) -> DispatchResult {1137		let collection =1138			Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;1139		ensure!(collection.owner == sender, Error::<T>::NoPermission);11401141		let resource_collection_id: CollectionId =1142			Self::get_nft_property_decoded(collection_id, nft_id, ResourceCollection)?;1143		let resource_collection =1144			Self::get_typed_nft_collection(resource_collection_id, misc::CollectionType::Resource)?;1145		ensure!(1146			<PalletNft<T>>::token_exists(&resource_collection, resource_id),1147			Error::<T>::ResourceDoesntExist1148		);11491150		let budget = up_data_structs::budget::Value::new(10);1151		let topmost_owner =1152			<PalletStructure<T>>::find_topmost_owner(collection_id, nft_id, &budget)?;11531154		let sender = T::CrossAccountId::from_sub(sender);1155		if topmost_owner == sender {1156			<PalletNft<T>>::burn(&resource_collection, &sender, resource_id)1157				.map_err(Self::map_unique_err_to_proxy)?;1158		} else {1159			<PalletNft<T>>::set_scoped_token_property(1160				resource_collection_id,1161				resource_id,1162				PropertyScope::Rmrk,1163				Self::rmrk_property(PendingResourceRemoval, &true)?,1164			)?;1165		}11661167		Ok(())1168	}11691170	fn change_collection_owner(1171		collection_id: CollectionId,1172		collection_type: misc::CollectionType,1173		sender: T::AccountId,1174		new_owner: T::AccountId,1175	) -> DispatchResult {1176		let collection = Self::get_typed_nft_collection(collection_id, collection_type)?;1177		Self::check_collection_owner(&collection, &T::CrossAccountId::from_sub(sender))?;11781179		let mut collection = collection.into_inner();11801181		collection.owner = new_owner;1182		collection.save()1183	}11841185	fn check_collection_owner(1186		collection: &NonfungibleHandle<T>,1187		account: &T::CrossAccountId,1188	) -> DispatchResult {1189		collection1190			.check_is_owner(account)1191			.map_err(Self::map_unique_err_to_proxy)1192	}11931194	pub fn last_collection_idx() -> RmrkCollectionId {1195		<CollectionIndex<T>>::get()1196	}11971198	pub fn unique_collection_id(1199		rmrk_collection_id: RmrkCollectionId,1200	) -> Result<CollectionId, DispatchError> {1201		<UniqueCollectionId<T>>::try_get(rmrk_collection_id)1202			.map_err(|_| <Error<T>>::CollectionUnknown.into())1203	}12041205	pub fn rmrk_collection_id(1206		unique_collection_id: CollectionId,1207	) -> Result<RmrkCollectionId, DispatchError> {1208		<RmrkInernalCollectionId<T>>::try_get(unique_collection_id)1209			.map_err(|_| <Error<T>>::CollectionUnknown.into())1210	}12111212	pub fn get_nft_collection(1213		collection_id: CollectionId,1214	) -> Result<NonfungibleHandle<T>, DispatchError> {1215		let collection = <CollectionHandle<T>>::try_get(collection_id)1216			.map_err(|_| <Error<T>>::CollectionUnknown)?;12171218		match collection.mode {1219			CollectionMode::NFT => Ok(NonfungibleHandle::cast(collection)),1220			_ => Err(<Error<T>>::CollectionUnknown.into()),1221		}1222	}12231224	pub fn collection_exists(collection_id: CollectionId) -> bool {1225		<CollectionHandle<T>>::try_get(collection_id).is_ok()1226	}12271228	pub fn get_collection_property(1229		collection_id: CollectionId,1230		key: RmrkProperty,1231	) -> Result<PropertyValue, DispatchError> {1232		let collection_property = <PalletCommon<T>>::collection_properties(collection_id)1233			.get(&Self::rmrk_property_key(key)?)1234			.ok_or(<Error<T>>::CollectionUnknown)?1235			.clone();12361237		Ok(collection_property)1238	}12391240	pub fn get_collection_property_decoded<V: Decode>(1241		collection_id: CollectionId,1242		key: RmrkProperty,1243	) -> Result<V, DispatchError> {1244		Self::decode_property(Self::get_collection_property(collection_id, key)?)1245	}12461247	pub fn get_collection_type(1248		collection_id: CollectionId,1249	) -> Result<misc::CollectionType, DispatchError> {1250		Self::get_collection_property_decoded(collection_id, CollectionType)1251			.map_err(|_| <Error<T>>::CorruptedCollectionType.into())1252	}12531254	pub fn ensure_collection_type(1255		collection_id: CollectionId,1256		collection_type: misc::CollectionType,1257	) -> DispatchResult {1258		let actual_type = Self::get_collection_type(collection_id)?;1259		ensure!(1260			actual_type == collection_type,1261			<CommonError<T>>::NoPermission1262		);12631264		Ok(())1265	}12661267	pub fn get_typed_nft_collection(1268		collection_id: CollectionId,1269		collection_type: misc::CollectionType,1270	) -> Result<NonfungibleHandle<T>, DispatchError> {1271		Self::ensure_collection_type(collection_id, collection_type)?;12721273		Self::get_nft_collection(collection_id)1274	}12751276	pub fn get_typed_nft_collection_mapped(1277		rmrk_collection_id: RmrkCollectionId,1278		collection_type: misc::CollectionType,1279	) -> Result<(NonfungibleHandle<T>, CollectionId), DispatchError> {1280		let unique_collection_id = match collection_type {1281			misc::CollectionType::Regular => Self::unique_collection_id(rmrk_collection_id)?,1282			_ => rmrk_collection_id.into(),1283		};12841285		let collection = Self::get_typed_nft_collection(unique_collection_id, collection_type)?;12861287		Ok((collection, unique_collection_id))1288	}12891290	pub fn get_nft_property(1291		collection_id: CollectionId,1292		nft_id: TokenId,1293		key: RmrkProperty,1294	) -> Result<PropertyValue, DispatchError> {1295		let nft_property = <PalletNft<T>>::token_properties((collection_id, nft_id))1296			.get(&Self::rmrk_property_key(key)?)1297			.ok_or(<Error<T>>::NoAvailableNftId)? // todo replace with better error?1298			.clone();12991300		Ok(nft_property)1301	}13021303	pub fn get_nft_property_decoded<V: Decode>(1304		collection_id: CollectionId,1305		nft_id: TokenId,1306		key: RmrkProperty,1307	) -> Result<V, DispatchError> {1308		Self::decode_property(Self::get_nft_property(collection_id, nft_id, key)?)1309	}13101311	pub fn nft_exists(collection_id: CollectionId, nft_id: TokenId) -> bool {1312		<TokenData<T>>::contains_key((collection_id, nft_id))1313	}13141315	pub fn get_nft_type(1316		collection_id: CollectionId,1317		token_id: TokenId,1318	) -> Result<NftType, DispatchError> {1319		Self::get_nft_property_decoded(collection_id, token_id, TokenType)1320			.map_err(|_| <Error<T>>::NoAvailableNftId.into())1321	}13221323	pub fn ensure_nft_type(1324		collection_id: CollectionId,1325		token_id: TokenId,1326		nft_type: NftType,1327	) -> DispatchResult {1328		let actual_type = Self::get_nft_type(collection_id, token_id)?;1329		ensure!(actual_type == nft_type, <Error<T>>::NoPermission);13301331		Ok(())1332	}13331334	pub fn ensure_nft_owner(1335		collection_id: CollectionId,1336		token_id: TokenId,1337		possible_owner: &T::CrossAccountId,1338		nesting_budget: &dyn budget::Budget,1339	) -> DispatchResult {1340		let is_owned = <PalletStructure<T>>::check_indirectly_owned(1341			possible_owner.clone(),1342			collection_id,1343			token_id,1344			None,1345			nesting_budget,1346		)1347		.map_err(Self::map_unique_err_to_proxy)?;13481349		ensure!(is_owned, <Error<T>>::NoPermission);13501351		Ok(())1352	}13531354	pub fn filter_user_properties<Key, Value, R, Mapper>(1355		collection_id: CollectionId,1356		token_id: Option<TokenId>,1357		filter_keys: Option<Vec<RmrkPropertyKey>>,1358		mapper: Mapper,1359	) -> Result<Vec<R>, DispatchError>1360	where1361		Key: TryFrom<RmrkPropertyKey> + AsRef<[u8]>,1362		Value: Decode + Default,1363		Mapper: Fn(Key, Value) -> R,1364	{1365		filter_keys1366			.map(|keys| {1367				let properties = keys1368					.into_iter()1369					.filter_map(|key| {1370						let key: Key = key.try_into().ok()?;13711372						let value = match token_id {1373							Some(token_id) => Self::get_nft_property_decoded(1374								collection_id,1375								token_id,1376								UserProperty(key.as_ref()),1377							),1378							None => Self::get_collection_property_decoded(1379								collection_id,1380								UserProperty(key.as_ref()),1381							),1382						}1383						.ok()?;13841385						Some(mapper(key, value))1386					})1387					.collect();13881389				Ok(properties)1390			})1391			.unwrap_or_else(|| {1392				let properties =1393					Self::iterate_user_properties(collection_id, token_id, mapper)?.collect();13941395				Ok(properties)1396			})1397	}13981399	pub fn iterate_user_properties<Key, Value, R, Mapper>(1400		collection_id: CollectionId,1401		token_id: Option<TokenId>,1402		mapper: Mapper,1403	) -> Result<impl Iterator<Item = R>, DispatchError>1404	where1405		Key: TryFrom<RmrkPropertyKey> + AsRef<[u8]>,1406		Value: Decode + Default,1407		Mapper: Fn(Key, Value) -> R,1408	{1409		let key_prefix = Self::rmrk_property_key(UserProperty(b""))?;14101411		let properties = match token_id {1412			Some(token_id) => <PalletNft<T>>::token_properties((collection_id, token_id)),1413			None => <PalletCommon<T>>::collection_properties(collection_id),1414		};14151416		let properties = properties.into_iter().filter_map(move |(key, value)| {1417			let key = key.as_slice().strip_prefix(key_prefix.as_slice())?;14181419			let key: Key = key.to_vec().try_into().ok()?;1420			let value: Value = value.decode().ok()?;14211422			Some(mapper(key, value))1423		});14241425		Ok(properties)1426	}14271428	fn map_unique_err_to_proxy(err: DispatchError) -> DispatchError {1429		map_unique_err_to_proxy! {1430			match err {1431				CommonError::NoPermission => NoPermission,1432				CommonError::CollectionTokenLimitExceeded => CollectionFullOrLocked,1433				CommonError::PublicMintingNotAllowed => NoPermission,1434				CommonError::TokenNotFound => NoAvailableNftId,1435				CommonError::ApprovedValueTooLow => NoPermission,1436				CommonError::CantDestroyNotEmptyCollection => CollectionNotEmpty,1437				StructureError::TokenNotFound => NoAvailableNftId,1438				StructureError::OuroborosDetected => CannotSendToDescendentOrSelf,1439			}1440		}1441	}1442}
addedpallets/proxy-rmrk-core/src/weights.rsdiffbeforeafterboth
--- /dev/null
+++ b/pallets/proxy-rmrk-core/src/weights.rs
@@ -0,0 +1,74 @@
+// Template adopted from https://github.com/paritytech/substrate/blob/master/.maintain/frame-weight-template.hbs
+
+//! Autogenerated weights for pallet_proxy_rmrk_core
+//!
+//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
+//! DATE: 2022-06-09, STEPS: `50`, REPEAT: 200, LOW RANGE: `[]`, HIGH RANGE: `[]`
+//! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024
+
+// Executed Command:
+// target/release/unique-collator
+// benchmark
+// pallet
+// --pallet
+// pallet-proxy-rmrk-core
+// --wasm-execution
+// compiled
+// --extrinsic
+// *
+// --template
+// .maintain/frame-weight-template.hbs
+// --steps=50
+// --repeat=200
+// --heap-pages=4096
+// --output=./pallets/proxy-rmrk-core/src/weights.rs
+
+#![cfg_attr(rustfmt, rustfmt_skip)]
+#![allow(unused_parens)]
+#![allow(unused_imports)]
+#![allow(clippy::unnecessary_cast)]
+
+use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};
+use sp_std::marker::PhantomData;
+
+/// Weight functions needed for pallet_proxy_rmrk_core.
+pub trait WeightInfo {
+	fn create_collection() -> Weight;
+}
+
+/// Weights for pallet_proxy_rmrk_core using the Substrate node and recommended hardware.
+pub struct SubstrateWeight<T>(PhantomData<T>);
+impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
+	// Storage: Common CreatedCollectionCount (r:1 w:1)
+	// Storage: Common DestroyedCollectionCount (r:1 w:0)
+	// Storage: System Account (r:2 w:2)
+	// Storage: RmrkCore CollectionIndex (r:1 w:1)
+	// Storage: Common CollectionPropertyPermissions (r:0 w:1)
+	// Storage: Common CollectionProperties (r:0 w:1)
+	// Storage: Common CollectionById (r:0 w:1)
+	// Storage: RmrkCore UniqueCollectionId (r:0 w:1)
+	// Storage: RmrkCore RmrkInernalCollectionId (r:0 w:1)
+	fn create_collection() -> Weight {
+		(76_647_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(5 as Weight))
+			.saturating_add(T::DbWeight::get().writes(9 as Weight))
+	}
+}
+
+// For backwards compatibility and tests
+impl WeightInfo for () {
+	// Storage: Common CreatedCollectionCount (r:1 w:1)
+	// Storage: Common DestroyedCollectionCount (r:1 w:0)
+	// Storage: System Account (r:2 w:2)
+	// Storage: RmrkCore CollectionIndex (r:1 w:1)
+	// Storage: Common CollectionPropertyPermissions (r:0 w:1)
+	// Storage: Common CollectionProperties (r:0 w:1)
+	// Storage: Common CollectionById (r:0 w:1)
+	// Storage: RmrkCore UniqueCollectionId (r:0 w:1)
+	// Storage: RmrkCore RmrkInernalCollectionId (r:0 w:1)
+	fn create_collection() -> Weight {
+		(76_647_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(5 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(9 as Weight))
+	}
+}
modifiedruntime/common/src/runtime_apis.rsdiffbeforeafterboth
--- a/runtime/common/src/runtime_apis.rs
+++ b/runtime/common/src/runtime_apis.rs
@@ -827,6 +827,7 @@
                     list_benchmark!(list, extra, pallet_fungible, Fungible);
                     list_benchmark!(list, extra, pallet_refungible, Refungible);
                     list_benchmark!(list, extra, pallet_nonfungible, Nonfungible);
+                    list_benchmark!(list, extra, pallet_proxy_rmrk_core, RmrkCore);
                     // list_benchmark!(list, extra, pallet_evm_coder_substrate, EvmCoderSubstrate);
 
                     let storage_info = AllPalletsReversedWithSystemFirst::storage_info();
@@ -870,6 +871,7 @@
                     add_benchmark!(params, batches, pallet_fungible, Fungible);
                     add_benchmark!(params, batches, pallet_refungible, Refungible);
                     add_benchmark!(params, batches, pallet_nonfungible, Nonfungible);
+                    add_benchmark!(params, batches, pallet_proxy_rmrk_core, RmrkCore);
                     // add_benchmark!(params, batches, pallet_evm_coder_substrate, EvmCoderSubstrate);
 
                     if batches.is_empty() { return Err("Benchmark not found for this pallet.".into()) }
modifiedruntime/opal/src/lib.rsdiffbeforeafterboth
--- a/runtime/opal/src/lib.rs
+++ b/runtime/opal/src/lib.rs
@@ -919,6 +919,7 @@
 }
 
 impl pallet_proxy_rmrk_core::Config for Runtime {
+	type WeightInfo = pallet_proxy_rmrk_core::weights::SubstrateWeight<Self>;
 	type Event = Event;
 }
 
modifiedruntime/quartz/src/lib.rsdiffbeforeafterboth
--- a/runtime/quartz/src/lib.rs
+++ b/runtime/quartz/src/lib.rs
@@ -918,6 +918,7 @@
 }
 
 impl pallet_proxy_rmrk_core::Config for Runtime {
+	type WeightInfo = pallet_proxy_rmrk_core::weights::SubstrateWeight<Self>;
 	type Event = Event;
 }
 
modifiedruntime/unique/src/lib.rsdiffbeforeafterboth
--- a/runtime/unique/src/lib.rs
+++ b/runtime/unique/src/lib.rs
@@ -917,6 +917,7 @@
 }
 
 impl pallet_proxy_rmrk_core::Config for Runtime {
+	type WeightInfo = pallet_proxy_rmrk_core::weights::SubstrateWeight<Self>;
 	type Event = Event;
 }