git.delta.rocks / unique-network / refs/commits / 51e15a07c4a1

difftreelog

fix(rmrk) include pending children into nft_children RPC

Daniel Shiposha2022-06-30parent: #66c5541.patch.diff
in: master

3 files changed

modifiedpallets/proxy-rmrk-core/src/lib.rsdiffbeforeafterboth
--- a/pallets/proxy-rmrk-core/src/lib.rs
+++ b/pallets/proxy-rmrk-core/src/lib.rs
@@ -16,10 +16,15 @@
 
 #![cfg_attr(not(feature = "std"), no_std)]
 
-use frame_support::{pallet_prelude::*, transactional, BoundedVec, dispatch::DispatchResult};
+use frame_support::{
+	pallet_prelude::*,
+	transactional,
+	BoundedVec,
+	dispatch::DispatchResult,
+};
 use frame_system::{pallet_prelude::*, ensure_signed};
 use sp_runtime::{DispatchError, Permill, traits::StaticLookup};
-use sp_std::vec::Vec;
+use sp_std::{vec::Vec, collections::btree_set::BTreeSet};
 use up_data_structs::{*, mapping::TokenAddressMapping};
 use pallet_common::{
 	Pallet as PalletCommon, Error as CommonError, CollectionHandle, CommonCollectionOperations,
@@ -48,6 +53,10 @@
 
 pub const NESTING_BUDGET: u32 = 5;
 
+type PendingTarget = (CollectionId, TokenId);
+type PendingChild = (RmrkCollectionId, RmrkNftId);
+type PendingChildrenMap = BTreeSet<PendingChild>;
+
 #[frame_support::pallet]
 pub mod pallet {
 	use super::*;
@@ -383,12 +392,13 @@
 				[
 					Self::rmrk_property(TokenType, &NftType::Regular)?,
 					Self::rmrk_property(Transferable, &transferable)?,
-					Self::rmrk_property(PendingNftAccept, &false)?,
+					Self::rmrk_property(PendingNftAccept, &None::<PendingTarget>)?,
 					Self::rmrk_property(RoyaltyInfo, &royalty_info)?,
 					Self::rmrk_property(Metadata, &metadata)?,
 					Self::rmrk_property(Equipped, &false)?,
 					Self::rmrk_property(ResourcePriorities, &<Vec<u8>>::new())?,
 					Self::rmrk_property(NextResourceId, &(0 as RmrkResourceId))?,
+					Self::rmrk_property(PendingChildren, &PendingChildrenMap::new())?,
 				]
 				.into_iter(),
 			)
@@ -483,11 +493,11 @@
 			);
 
 			ensure!(
-				!Self::get_nft_property_decoded(
+				Self::get_nft_property_decoded::<Option<PendingTarget>>(
 					collection_id,
 					nft_id,
 					RmrkProperty::PendingNftAccept
-				)?,
+				)?.is_none(),
 				<Error<T>>::NoPermission
 			);
 
@@ -524,7 +534,15 @@
 							collection.id,
 							nft_id,
 							PropertyScope::Rmrk,
-							Self::rmrk_property(PendingNftAccept, &approval_required)?,
+							Self::rmrk_property::<Option<PendingTarget>>(
+								PendingNftAccept,
+								&Some((target_collection_id, target_nft_id.into()))
+							)?,
+						)?;
+
+						Self::insert_pending_child(
+							(target_collection_id, target_nft_id.into()),
+							(rmrk_collection_id, rmrk_nft_id),
 						)?;
 					} else {
 						target_owner = T::CrossTokenAddressMapping::token_to_address(
@@ -618,13 +636,23 @@
 				}
 			})?;
 
-			<PalletNft<T>>::set_scoped_token_property(
-				collection.id,
+			let pending_target = Self::get_nft_property_decoded::<Option<PendingTarget>>(
+				collection_id,
 				nft_id,
-				PropertyScope::Rmrk,
-				Self::rmrk_property(PendingNftAccept, &false)?,
+				RmrkProperty::PendingNftAccept
 			)?;
 
+			if let Some(pending_target) = pending_target {
+				Self::remove_pending_child(pending_target, (rmrk_collection_id, rmrk_nft_id))?;
+
+				<PalletNft<T>>::set_scoped_token_property(
+					collection.id,
+					nft_id,
+					PropertyScope::Rmrk,
+					Self::rmrk_property(PendingNftAccept, &None::<PendingTarget>)?,
+				)?;
+			}
+
 			Self::deposit_event(Event::NFTAccepted {
 				sender,
 				recipient: new_owner,
@@ -663,15 +691,18 @@
 				<Error<T>>::NoAvailableNftId
 			);
 
-			ensure!(
-				Self::get_nft_property_decoded(
-					collection_id,
-					nft_id,
-					RmrkProperty::PendingNftAccept
-				)?,
-				<Error<T>>::CannotRejectNonPendingNft
-			);
 
+			let pending_target = Self::get_nft_property_decoded::<Option<PendingTarget>>(
+				collection_id,
+				nft_id,
+				RmrkProperty::PendingNftAccept
+			)?;
+
+			match pending_target {
+				Some(pending_target) => Self::remove_pending_child(pending_target, (rmrk_collection_id, rmrk_nft_id))?,
+				None => return Err(<Error<T>>::CannotRejectNonPendingNft.into()),
+			}
+
 			Self::destroy_nft(
 				cross_sender,
 				collection_id,
@@ -1147,6 +1178,64 @@
 		)
 	}
 
+	fn insert_pending_child(
+		target: (CollectionId, TokenId),
+		child: (RmrkCollectionId, RmrkNftId),
+	) -> DispatchResult {
+		Self::mutate_pending_child(target, |pending_children| {
+			pending_children.insert(child);
+		})
+	}
+
+	fn remove_pending_child(
+		target: (CollectionId, TokenId),
+		child: (RmrkCollectionId, RmrkNftId),
+	) -> DispatchResult {
+		Self::mutate_pending_child(target, |pending_children| {
+			pending_children.remove(&child);
+		})
+	}
+
+	fn mutate_pending_child(
+		(target_collection_id, target_nft_id): (CollectionId, TokenId),
+		f: impl FnOnce(&mut PendingChildrenMap),
+	) -> DispatchResult {
+		<PalletNft<T>>::try_mutate_token_aux_property(
+			target_collection_id,
+			target_nft_id,
+			PropertyScope::Rmrk,
+			Self::rmrk_property_key(PendingChildren)?,
+			|pending_children| -> DispatchResult {
+				let mut map = match pending_children {
+					Some(map) => Self::decode_property(map)?,
+					None => PendingChildrenMap::new(),
+				};
+
+				f(&mut map);
+
+				*pending_children = Some(Self::encode_property(&map)?);
+
+				Ok(())
+			},
+		)
+	}
+
+	fn iterate_pending_children(collection_id: CollectionId, nft_id: TokenId) -> Result<impl Iterator<Item=PendingChild>, DispatchError> {
+		let property = <PalletNft<T>>::token_aux_property((
+			collection_id,
+			nft_id,
+			PropertyScope::Rmrk,
+			Self::rmrk_property_key(PendingChildren)?
+		));
+
+		let pending_children = match property {
+			Some(map) => Self::decode_property(&map)?,
+			None => PendingChildrenMap::new(),
+		};
+
+		Ok(pending_children.into_iter())
+	}
+
 	fn acquire_next_resource_id(
 		collection_id: CollectionId,
 		nft_id: TokenId,
@@ -1526,15 +1615,13 @@
 		Value: Decode + Default,
 		Mapper: Fn(Key, Value) -> R,
 	{
-		let key_prefix = Self::rmrk_property_key(UserProperty(b""))?;
-
 		let properties = match token_id {
 			Some(token_id) => <PalletNft<T>>::token_properties((collection_id, token_id)),
 			None => <PalletCommon<T>>::collection_properties(collection_id),
 		};
 
 		let properties = properties.into_iter().filter_map(move |(key, value)| {
-			let key = key.as_slice().strip_prefix(key_prefix.as_slice())?;
+			let key = strip_key_prefix(&key, USER_PROPERTY_PREFIX)?;
 
 			let key: Key = key.to_vec().try_into().ok()?;
 			let value: Value = value.decode().ok()?;
modifiedpallets/proxy-rmrk-core/src/property.rsdiffbeforeafterboth
--- a/pallets/proxy-rmrk-core/src/property.rs
+++ b/pallets/proxy-rmrk-core/src/property.rs
@@ -15,9 +15,11 @@
 // along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
 
 use super::*;
+use up_data_structs::PropertyScope;
 use core::convert::AsRef;
 
-const RESOURCE_ID_PREFIX: &str = "rsid-";
+pub const RESOURCE_ID_PREFIX: &str = "rsid-";
+pub const USER_PROPERTY_PREFIX: &str = "userprop-";
 
 pub enum RmrkProperty<'r> {
 	Metadata,
@@ -31,6 +33,7 @@
 	NextResourceId,
 	ResourceId(RmrkResourceId),
 	PendingNftAccept,
+	PendingChildren,
 	Parts,
 	Base,
 	Src,
@@ -73,6 +76,7 @@
 			Self::NextResourceId => key!("next-resource-id"),
 			Self::ResourceId(id) => key!(RESOURCE_ID_PREFIX, id.to_le_bytes()),
 			Self::PendingNftAccept => key!("pending-nft-accept"),
+			Self::PendingChildren => key!("pending-children"),
 			Self::Parts => key!("parts"),
 			Self::Base => key!("base"),
 			Self::Src => key!("src"),
@@ -83,7 +87,19 @@
 			Self::ZIndex => key!("z-index"),
 			Self::ThemeName => key!("theme-name"),
 			Self::ThemeInherit => key!("theme-inherit"),
-			Self::UserProperty(name) => key!("userprop-", name),
+			Self::UserProperty(name) => key!(USER_PROPERTY_PREFIX, name),
 		}
 	}
 }
+
+pub fn strip_key_prefix(key: &PropertyKey, prefix: &str) -> Option<PropertyKey> {
+	let key_prefix = PropertyKey::try_from(prefix.as_bytes().to_vec()).ok()?;
+	let key_prefix = PropertyScope::Rmrk.apply(key_prefix).ok()?;
+
+	key.as_slice().strip_prefix(key_prefix.as_slice())?
+		.to_vec().try_into().ok()
+}
+
+pub fn is_valid_key_prefix(key: &PropertyKey, prefix: &str) -> bool {
+	strip_key_prefix(key, prefix).is_some()
+}
modifiedpallets/proxy-rmrk-core/src/rpc.rsdiffbeforeafterboth
before · pallets/proxy-rmrk-core/src/rpc.rs
1use super::*;23pub fn last_collection_idx<T: Config>() -> Result<RmrkCollectionId, DispatchError> {4	Ok(<Pallet<T>>::last_collection_idx())5}67pub fn collection_by_id<T: Config>(8	collection_id: RmrkCollectionId,9) -> Result<Option<RmrkCollectionInfo<T::AccountId>>, DispatchError> {10	let (collection, collection_id) = match <Pallet<T>>::get_typed_nft_collection_mapped(11		collection_id,12		misc::CollectionType::Regular,13	) {14		Ok(c) => c,15		Err(_) => return Ok(None),16	};1718	let nfts_count = collection.total_supply();1920	Ok(Some(RmrkCollectionInfo {21		issuer: collection.owner.clone(),22		metadata: <Pallet<T>>::get_collection_property_decoded(23			collection_id,24			RmrkProperty::Metadata,25		)?,26		max: collection.limits.token_limit,27		symbol: <Pallet<T>>::rebind(&collection.token_prefix)?,28		nfts_count,29	}))30}3132pub fn nft_by_id<T: Config>(33	collection_id: RmrkCollectionId,34	nft_by_id: RmrkNftId,35) -> Result<Option<RmrkInstanceInfo<T::AccountId>>, DispatchError> {36	let (collection, collection_id) = match <Pallet<T>>::get_typed_nft_collection_mapped(37		collection_id,38		misc::CollectionType::Regular,39	) {40		Ok(c) => c,41		Err(_) => return Ok(None),42	};4344	let nft_id = TokenId(nft_by_id);45	if !<Pallet<T>>::nft_exists(collection_id, nft_id) {46		return Ok(None);47	}4849	let owner = match collection.token_owner(nft_id) {50		Some(owner) => match T::CrossTokenAddressMapping::address_to_token(&owner) {51			Some((col, tok)) => {52				let rmrk_collection = <Pallet<T>>::rmrk_collection_id(col)?;5354				RmrkAccountIdOrCollectionNftTuple::CollectionAndNftTuple(rmrk_collection, tok.0)55			}56			None => RmrkAccountIdOrCollectionNftTuple::AccountId(owner.as_sub().clone()),57		},58		None => return Ok(None),59	};6061	Ok(Some(RmrkInstanceInfo {62		owner: owner,63		royalty: <Pallet<T>>::get_nft_property_decoded(64			collection_id,65			nft_id,66			RmrkProperty::RoyaltyInfo,67		)?,68		metadata: <Pallet<T>>::get_nft_property_decoded(69			collection_id,70			nft_id,71			RmrkProperty::Metadata,72		)?,73		equipped: <Pallet<T>>::get_nft_property_decoded(74			collection_id,75			nft_id,76			RmrkProperty::Equipped,77		)?,78		pending: <Pallet<T>>::get_nft_property_decoded(79			collection_id,80			nft_id,81			RmrkProperty::PendingNftAccept,82		)?,83	}))84}8586pub fn account_tokens<T: Config>(87	account_id: T::AccountId,88	collection_id: RmrkCollectionId,89) -> Result<Vec<RmrkNftId>, DispatchError> {90	let cross_account_id = CrossAccountId::from_sub(account_id);9192	let (collection, collection_id) = match <Pallet<T>>::get_typed_nft_collection_mapped(93		collection_id,94		misc::CollectionType::Regular,95	) {96		Ok(c) => c,97		Err(_) => return Ok(Vec::new()),98	};99100	let tokens = collection101		.account_tokens(cross_account_id)102		.into_iter()103		.filter(|token| {104			let is_pending = <Pallet<T>>::get_nft_property_decoded(105				collection_id,106				*token,107				RmrkProperty::PendingNftAccept,108			)109			.unwrap_or(true);110111			!is_pending112		})113		.map(|token| token.0)114		.collect();115116	Ok(tokens)117}118119pub fn nft_children<T: Config>(120	collection_id: RmrkCollectionId,121	nft_id: RmrkNftId,122) -> Result<Vec<RmrkNftChild>, DispatchError> {123	let collection_id = match <Pallet<T>>::unique_collection_id(collection_id) {124		Ok(id) => id,125		Err(_) => return Ok(Vec::new()),126	};127	let nft_id = TokenId(nft_id);128	if !<Pallet<T>>::nft_exists(collection_id, nft_id) {129		return Ok(Vec::new());130	}131132	Ok(133		pallet_nonfungible::TokenChildren::<T>::iter_prefix((collection_id, nft_id))134			.filter_map(|((child_collection, child_token), _)| {135				let is_pending = <Pallet<T>>::get_nft_property_decoded(136					child_collection,137					child_token,138					RmrkProperty::PendingNftAccept,139				)140				.ok()?;141142				if is_pending {143					return None;144				}145146				let rmrk_child_collection =147					<Pallet<T>>::rmrk_collection_id(child_collection).ok()?;148149				Some(RmrkNftChild {150					collection_id: rmrk_child_collection,151					nft_id: child_token.0,152				})153			})154			.collect(),155	)156}157158pub fn collection_properties<T: Config>(159	collection_id: RmrkCollectionId,160	filter_keys: Option<Vec<RmrkPropertyKey>>,161) -> Result<Vec<RmrkPropertyInfo>, DispatchError> {162	let collection_id = match <Pallet<T>>::unique_collection_id(collection_id) {163		Ok(id) => id,164		Err(_) => return Ok(Vec::new()),165	};166	if <Pallet<T>>::ensure_collection_type(collection_id, misc::CollectionType::Regular).is_err() {167		return Ok(Vec::new());168	}169170	let properties = <Pallet<T>>::filter_user_properties(171		collection_id,172		/* token_id = */ None,173		filter_keys,174		|key, value| RmrkPropertyInfo { key, value },175	)?;176177	Ok(properties)178}179180pub fn nft_properties<T: Config>(181	collection_id: RmrkCollectionId,182	nft_id: RmrkNftId,183	filter_keys: Option<Vec<RmrkPropertyKey>>,184) -> Result<Vec<RmrkPropertyInfo>, DispatchError> {185	let collection_id = match <Pallet<T>>::unique_collection_id(collection_id) {186		Ok(id) => id,187		Err(_) => return Ok(Vec::new()),188	};189	let token_id = TokenId(nft_id);190191	if <Pallet<T>>::ensure_nft_type(collection_id, token_id, NftType::Regular).is_err() {192		return Ok(Vec::new());193	}194195	let properties = <Pallet<T>>::filter_user_properties(196		collection_id,197		Some(token_id),198		filter_keys,199		|key, value| RmrkPropertyInfo { key, value },200	)?;201202	Ok(properties)203}204205pub fn nft_resources<T: Config>(206	collection_id: RmrkCollectionId,207	nft_id: RmrkNftId,208) -> Result<Vec<RmrkResourceInfo>, DispatchError> {209	let collection_id = match <Pallet<T>>::unique_collection_id(collection_id) {210		Ok(id) => id,211		Err(_) => return Ok(Vec::new()),212	};213	if <Pallet<T>>::ensure_collection_type(collection_id, misc::CollectionType::Regular).is_err() {214		return Ok(Vec::new());215	}216217	let nft_id = TokenId(nft_id);218	if <Pallet<T>>::ensure_nft_type(collection_id, nft_id, NftType::Regular).is_err() {219		return Ok(Vec::new());220	}221222	let resources = <pallet_nonfungible::Pallet<T>>::iterate_token_aux_properties(223		collection_id,224		nft_id,225		PropertyScope::Rmrk,226	)227	.filter_map(|(_, value)| {228		let resource_info: RmrkResourceInfo = <Pallet<T>>::decode_property(&value).ok()?;229230		Some(resource_info)231	})232	.collect();233234	Ok(resources)235}236237pub fn nft_resource_priority<T: Config>(238	collection_id: RmrkCollectionId,239	nft_id: RmrkNftId,240	resource_id: RmrkResourceId,241) -> Result<Option<u32>, DispatchError> {242	let collection_id = match <Pallet<T>>::unique_collection_id(collection_id) {243		Ok(id) => id,244		Err(_) => return Ok(None),245	};246	if <Pallet<T>>::ensure_collection_type(collection_id, misc::CollectionType::Regular).is_err() {247		return Ok(None);248	}249250	let nft_id = TokenId(nft_id);251	if <Pallet<T>>::ensure_nft_type(collection_id, nft_id, NftType::Regular).is_err() {252		return Ok(None);253	}254255	let priorities: Vec<_> = <Pallet<T>>::get_nft_property_decoded(256		collection_id,257		nft_id,258		RmrkProperty::ResourcePriorities,259	)?;260	Ok(priorities261		.into_iter()262		.enumerate()263		.find(|(_, id)| *id == resource_id)264		.map(|(priority, _): (usize, RmrkResourceId)| priority as u32))265}
after · pallets/proxy-rmrk-core/src/rpc.rs
1use super::*;23pub fn last_collection_idx<T: Config>() -> Result<RmrkCollectionId, DispatchError> {4	Ok(<Pallet<T>>::last_collection_idx())5}67pub fn collection_by_id<T: Config>(8	collection_id: RmrkCollectionId,9) -> Result<Option<RmrkCollectionInfo<T::AccountId>>, DispatchError> {10	let (collection, collection_id) = match <Pallet<T>>::get_typed_nft_collection_mapped(11		collection_id,12		misc::CollectionType::Regular,13	) {14		Ok(c) => c,15		Err(_) => return Ok(None),16	};1718	let nfts_count = collection.total_supply();1920	Ok(Some(RmrkCollectionInfo {21		issuer: collection.owner.clone(),22		metadata: <Pallet<T>>::get_collection_property_decoded(23			collection_id,24			RmrkProperty::Metadata,25		)?,26		max: collection.limits.token_limit,27		symbol: <Pallet<T>>::rebind(&collection.token_prefix)?,28		nfts_count,29	}))30}3132pub fn nft_by_id<T: Config>(33	collection_id: RmrkCollectionId,34	nft_by_id: RmrkNftId,35) -> Result<Option<RmrkInstanceInfo<T::AccountId>>, DispatchError> {36	let (collection, collection_id) = match <Pallet<T>>::get_typed_nft_collection_mapped(37		collection_id,38		misc::CollectionType::Regular,39	) {40		Ok(c) => c,41		Err(_) => return Ok(None),42	};4344	let nft_id = TokenId(nft_by_id);45	if !<Pallet<T>>::nft_exists(collection_id, nft_id) {46		return Ok(None);47	}4849	let owner = match collection.token_owner(nft_id) {50		Some(owner) => match T::CrossTokenAddressMapping::address_to_token(&owner) {51			Some((col, tok)) => {52				let rmrk_collection = <Pallet<T>>::rmrk_collection_id(col)?;5354				RmrkAccountIdOrCollectionNftTuple::CollectionAndNftTuple(rmrk_collection, tok.0)55			}56			None => RmrkAccountIdOrCollectionNftTuple::AccountId(owner.as_sub().clone()),57		},58		None => return Ok(None),59	};6061	Ok(Some(RmrkInstanceInfo {62		owner: owner,63		royalty: <Pallet<T>>::get_nft_property_decoded(64			collection_id,65			nft_id,66			RmrkProperty::RoyaltyInfo,67		)?,68		metadata: <Pallet<T>>::get_nft_property_decoded(69			collection_id,70			nft_id,71			RmrkProperty::Metadata,72		)?,73		equipped: <Pallet<T>>::get_nft_property_decoded(74			collection_id,75			nft_id,76			RmrkProperty::Equipped,77		)?,78		pending: <Pallet<T>>::get_nft_property_decoded(79			collection_id,80			nft_id,81			RmrkProperty::PendingNftAccept,82		)?,83	}))84}8586pub fn account_tokens<T: Config>(87	account_id: T::AccountId,88	collection_id: RmrkCollectionId,89) -> Result<Vec<RmrkNftId>, DispatchError> {90	let cross_account_id = CrossAccountId::from_sub(account_id);9192	let (collection, collection_id) = match <Pallet<T>>::get_typed_nft_collection_mapped(93		collection_id,94		misc::CollectionType::Regular,95	) {96		Ok(c) => c,97		Err(_) => return Ok(Vec::new()),98	};99100	let tokens = collection101		.account_tokens(cross_account_id)102		.into_iter()103		.filter(|token| {104			let is_pending = <Pallet<T>>::get_nft_property_decoded(105				collection_id,106				*token,107				RmrkProperty::PendingNftAccept,108			)109			.unwrap_or(true);110111			!is_pending112		})113		.map(|token| token.0)114		.collect();115116	Ok(tokens)117}118119pub fn nft_children<T: Config>(120	collection_id: RmrkCollectionId,121	nft_id: RmrkNftId,122) -> Result<Vec<RmrkNftChild>, DispatchError> {123	let collection_id = match <Pallet<T>>::unique_collection_id(collection_id) {124		Ok(id) => id,125		Err(_) => return Ok(Vec::new()),126	};127	let nft_id = TokenId(nft_id);128	if !<Pallet<T>>::nft_exists(collection_id, nft_id) {129		return Ok(Vec::new());130	}131132	Ok(133		pallet_nonfungible::TokenChildren::<T>::iter_prefix((collection_id, nft_id))134			.filter_map(|((child_collection, child_token), _)| {135				let rmrk_child_collection =136					<Pallet<T>>::rmrk_collection_id(child_collection).ok()?;137138				Some(RmrkNftChild {139					collection_id: rmrk_child_collection,140					nft_id: child_token.0,141				})142			})143			.chain(144				<Pallet<T>>::iterate_pending_children(collection_id, nft_id)?145					.map(|(child_collection, child_nft_id)| RmrkNftChild {146						collection_id: child_collection,147						nft_id: child_nft_id,148					})149			)150			.collect(),151	)152}153154pub fn collection_properties<T: Config>(155	collection_id: RmrkCollectionId,156	filter_keys: Option<Vec<RmrkPropertyKey>>,157) -> Result<Vec<RmrkPropertyInfo>, DispatchError> {158	let collection_id = match <Pallet<T>>::unique_collection_id(collection_id) {159		Ok(id) => id,160		Err(_) => return Ok(Vec::new()),161	};162	if <Pallet<T>>::ensure_collection_type(collection_id, misc::CollectionType::Regular).is_err() {163		return Ok(Vec::new());164	}165166	let properties = <Pallet<T>>::filter_user_properties(167		collection_id,168		/* token_id = */ None,169		filter_keys,170		|key, value| RmrkPropertyInfo { key, value },171	)?;172173	Ok(properties)174}175176pub fn nft_properties<T: Config>(177	collection_id: RmrkCollectionId,178	nft_id: RmrkNftId,179	filter_keys: Option<Vec<RmrkPropertyKey>>,180) -> Result<Vec<RmrkPropertyInfo>, DispatchError> {181	let collection_id = match <Pallet<T>>::unique_collection_id(collection_id) {182		Ok(id) => id,183		Err(_) => return Ok(Vec::new()),184	};185	let token_id = TokenId(nft_id);186187	if <Pallet<T>>::ensure_nft_type(collection_id, token_id, NftType::Regular).is_err() {188		return Ok(Vec::new());189	}190191	let properties = <Pallet<T>>::filter_user_properties(192		collection_id,193		Some(token_id),194		filter_keys,195		|key, value| RmrkPropertyInfo { key, value },196	)?;197198	Ok(properties)199}200201pub fn nft_resources<T: Config>(202	collection_id: RmrkCollectionId,203	nft_id: RmrkNftId,204) -> Result<Vec<RmrkResourceInfo>, DispatchError> {205	let collection_id = match <Pallet<T>>::unique_collection_id(collection_id) {206		Ok(id) => id,207		Err(_) => return Ok(Vec::new()),208	};209	if <Pallet<T>>::ensure_collection_type(collection_id, misc::CollectionType::Regular).is_err() {210		return Ok(Vec::new());211	}212213	let nft_id = TokenId(nft_id);214	if <Pallet<T>>::ensure_nft_type(collection_id, nft_id, NftType::Regular).is_err() {215		return Ok(Vec::new());216	}217218	let resources = <pallet_nonfungible::Pallet<T>>::iterate_token_aux_properties(219		collection_id,220		nft_id,221		PropertyScope::Rmrk,222	)223	.filter_map(|(key, value)| {224		if !is_valid_key_prefix(&key, RESOURCE_ID_PREFIX) {225			return None;226		}227228		let resource_info: RmrkResourceInfo = <Pallet<T>>::decode_property(&value).ok()?;229230		Some(resource_info)231	})232	.collect();233234	Ok(resources)235}236237pub fn nft_resource_priority<T: Config>(238	collection_id: RmrkCollectionId,239	nft_id: RmrkNftId,240	resource_id: RmrkResourceId,241) -> Result<Option<u32>, DispatchError> {242	let collection_id = match <Pallet<T>>::unique_collection_id(collection_id) {243		Ok(id) => id,244		Err(_) => return Ok(None),245	};246	if <Pallet<T>>::ensure_collection_type(collection_id, misc::CollectionType::Regular).is_err() {247		return Ok(None);248	}249250	let nft_id = TokenId(nft_id);251	if <Pallet<T>>::ensure_nft_type(collection_id, nft_id, NftType::Regular).is_err() {252		return Ok(None);253	}254255	let priorities: Vec<_> = <Pallet<T>>::get_nft_property_decoded(256		collection_id,257		nft_id,258		RmrkProperty::ResourcePriorities,259	)?;260	Ok(priorities261		.into_iter()262		.enumerate()263		.find(|(_, id)| *id == resource_id)264		.map(|(priority, _): (usize, RmrkResourceId)| priority as u32))265}