git.delta.rocks / unique-network / refs/commits / a74fad685f6e

difftreelog

feat(rmrk) add associated bases property

Daniel Shiposha2022-06-30parent: #51e15a0.patch.diff
in: master

2 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
@@ -24,7 +24,7 @@
 };
 use frame_system::{pallet_prelude::*, ensure_signed};
 use sp_runtime::{DispatchError, Permill, traits::StaticLookup};
-use sp_std::{vec::Vec, collections::btree_set::BTreeSet};
+use sp_std::{vec::Vec, collections::{btree_set::BTreeSet, btree_map::BTreeMap}};
 use up_data_structs::{*, mapping::TokenAddressMapping};
 use pallet_common::{
 	Pallet as PalletCommon, Error as CommonError, CollectionHandle, CommonCollectionOperations,
@@ -55,7 +55,9 @@
 
 type PendingTarget = (CollectionId, TokenId);
 type PendingChild = (RmrkCollectionId, RmrkNftId);
-type PendingChildrenMap = BTreeSet<PendingChild>;
+type PendingChildrenSet = BTreeSet<PendingChild>;
+
+type BasesMap = BTreeMap<RmrkBaseId, u32>;
 
 #[frame_support::pallet]
 pub mod pallet {
@@ -398,7 +400,8 @@
 					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())?,
+					Self::rmrk_property(PendingChildren, &PendingChildrenSet::new())?,
+					Self::rmrk_property(AssociatedBases, &BasesMap::new())?,
 				]
 				.into_iter(),
 			)
@@ -816,6 +819,12 @@
 				resource_id_key,
 			);
 
+			if let RmrkResourceTypes::Composable(resource) = resource_info.resource {
+				let base_id = resource.base;
+
+				Self::remove_associated_base_id(collection_id, nft_id, base_id)?;
+			}
+
 			Self::deposit_event(Event::<T>::ResourceRemovalAccepted {
 				nft_id: rmrk_nft_id,
 				resource_id,
@@ -969,6 +978,8 @@
 				Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;
 			collection.check_is_external()?;
 
+			let base_id = resource.base;
+
 			let resource_id = Self::resource_add(
 				sender,
 				collection_id,
@@ -976,6 +987,24 @@
 				RmrkResourceTypes::Composable(resource),
 			)?;
 
+			<PalletNft<T>>::try_mutate_token_aux_property(
+				collection_id,
+				nft_id.into(),
+				PropertyScope::Rmrk,
+				Self::rmrk_property_key(AssociatedBases)?,
+				|value| -> DispatchResult {
+					let mut bases: BasesMap = match value {
+						Some(value) => Self::decode_property(value)?,
+						None => BasesMap::new()
+					};
+	
+					*bases.entry(base_id).or_insert(0) += 1;
+	
+					*value = Some(Self::encode_property(&bases)?);
+					Ok(())
+				}
+			)?;
+
 			Self::deposit_event(Event::ResourceAdded {
 				nft_id,
 				resource_id,
@@ -1198,7 +1227,7 @@
 
 	fn mutate_pending_child(
 		(target_collection_id, target_nft_id): (CollectionId, TokenId),
-		f: impl FnOnce(&mut PendingChildrenMap),
+		f: impl FnOnce(&mut PendingChildrenSet),
 	) -> DispatchResult {
 		<PalletNft<T>>::try_mutate_token_aux_property(
 			target_collection_id,
@@ -1208,7 +1237,7 @@
 			|pending_children| -> DispatchResult {
 				let mut map = match pending_children {
 					Some(map) => Self::decode_property(map)?,
-					None => PendingChildrenMap::new(),
+					None => PendingChildrenSet::new(),
 				};
 
 				f(&mut map);
@@ -1230,7 +1259,7 @@
 
 		let pending_children = match property {
 			Some(map) => Self::decode_property(&map)?,
-			None => PendingChildrenMap::new(),
+			None => PendingChildrenSet::new(),
 		};
 
 		Ok(pending_children.into_iter())
@@ -1312,16 +1341,14 @@
 		let resource_id_key = Self::rmrk_property_key(ResourceId(resource_id))?;
 		let scope = PropertyScope::Rmrk;
 
-		ensure!(
-			<PalletNft<T>>::token_aux_property((
-				collection_id,
-				nft_id,
-				scope,
-				resource_id_key.clone()
-			))
-			.is_some(),
-			<Error<T>>::ResourceDoesntExist
-		);
+		let resource = <PalletNft<T>>::token_aux_property((
+			collection_id,
+			nft_id,
+			scope,
+			resource_id_key.clone()
+		)).ok_or(<Error<T>>::ResourceDoesntExist)?;
+
+		let resource_info: RmrkResourceInfo = Self::decode_property(&resource)?;
 
 		let budget = up_data_structs::budget::Value::new(NESTING_BUDGET);
 		let topmost_owner =
@@ -1335,6 +1362,12 @@
 				PropertyScope::Rmrk,
 				Self::rmrk_property_key(ResourceId(resource_id))?,
 			);
+
+			if let RmrkResourceTypes::Composable(resource) = resource_info.resource {
+				let base_id = resource.base;
+
+				Self::remove_associated_base_id(collection_id, nft_id, base_id)?;
+			}
 		} else {
 			Self::try_mutate_resource_info(collection_id, nft_id, resource_id, |res| {
 				res.pending_removal = true;
@@ -1346,6 +1379,36 @@
 		Ok(())
 	}
 
+	fn remove_associated_base_id(
+		collection_id: CollectionId,
+		nft_id: TokenId,
+		base_id: RmrkBaseId,
+	) -> DispatchResult {
+		<PalletNft<T>>::try_mutate_token_aux_property(
+			collection_id,
+			nft_id,
+			PropertyScope::Rmrk,
+			Self::rmrk_property_key(AssociatedBases)?,
+			|value| -> DispatchResult {
+				let mut bases: BasesMap = match value {
+					Some(value) => Self::decode_property(value)?,
+					None => BasesMap::new()
+				};
+
+				let remaining = bases.get(&base_id);
+
+				if let Some(remaining) = remaining {
+					if let Some(0) | None = remaining.checked_sub(1) {
+						bases.remove(&base_id);
+					}
+				}
+
+				*value = Some(Self::encode_property(&bases)?);
+				Ok(())
+			}
+		)
+	}
+
 	fn try_mutate_resource_info(
 		collection_id: CollectionId,
 		nft_id: TokenId,
modifiedpallets/proxy-rmrk-core/src/property.rsdiffbeforeafterboth
before · pallets/proxy-rmrk-core/src/property.rs
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617use super::*;18use up_data_structs::PropertyScope;19use core::convert::AsRef;2021pub const RESOURCE_ID_PREFIX: &str = "rsid-";22pub const USER_PROPERTY_PREFIX: &str = "userprop-";2324pub enum RmrkProperty<'r> {25	Metadata,26	CollectionType,27	RmrkInternalCollectionId,28	TokenType,29	Transferable,30	RoyaltyInfo,31	Equipped,32	ResourcePriorities,33	NextResourceId,34	ResourceId(RmrkResourceId),35	PendingNftAccept,36	PendingChildren,37	Parts,38	Base,39	Src,40	EquippedNft,41	BaseType,42	ExternalPartId,43	EquippableList,44	ZIndex,45	ThemeName,46	ThemeInherit,47	UserProperty(&'r [u8]),48}4950impl<'r> RmrkProperty<'r> {51	pub fn to_key<T: Config>(self) -> Result<PropertyKey, Error<T>> {52		fn get_bytes<T: AsRef<[u8]>>(container: &T) -> &[u8] {53			container.as_ref()54		}5556		macro_rules! key {57            ($($component:expr),+) => {58                PropertyKey::try_from([$(key!(@ &$component)),+].concat())59                    .map_err(|_| <Error<T>>::RmrkPropertyKeyIsTooLong)60            };6162            (@ $key:expr) => {63                get_bytes($key)64            };65        }6667		match self {68			Self::Metadata => key!("metadata"),69			Self::CollectionType => key!("collection-type"),70			Self::RmrkInternalCollectionId => key!("internal-id"),71			Self::TokenType => key!("token-type"),72			Self::Transferable => key!("transferable"),73			Self::RoyaltyInfo => key!("royalty-info"),74			Self::Equipped => key!("equipped"),75			Self::ResourcePriorities => key!("resource-priorities"),76			Self::NextResourceId => key!("next-resource-id"),77			Self::ResourceId(id) => key!(RESOURCE_ID_PREFIX, id.to_le_bytes()),78			Self::PendingNftAccept => key!("pending-nft-accept"),79			Self::PendingChildren => key!("pending-children"),80			Self::Parts => key!("parts"),81			Self::Base => key!("base"),82			Self::Src => key!("src"),83			Self::EquippedNft => key!("equipped-nft"),84			Self::BaseType => key!("base-type"),85			Self::ExternalPartId => key!("ext-part-id"),86			Self::EquippableList => key!("equippable-list"),87			Self::ZIndex => key!("z-index"),88			Self::ThemeName => key!("theme-name"),89			Self::ThemeInherit => key!("theme-inherit"),90			Self::UserProperty(name) => key!(USER_PROPERTY_PREFIX, name),91		}92	}93}9495pub fn strip_key_prefix(key: &PropertyKey, prefix: &str) -> Option<PropertyKey> {96	let key_prefix = PropertyKey::try_from(prefix.as_bytes().to_vec()).ok()?;97	let key_prefix = PropertyScope::Rmrk.apply(key_prefix).ok()?;9899	key.as_slice().strip_prefix(key_prefix.as_slice())?100		.to_vec().try_into().ok()101}102103pub fn is_valid_key_prefix(key: &PropertyKey, prefix: &str) -> bool {104	strip_key_prefix(key, prefix).is_some()105}
after · pallets/proxy-rmrk-core/src/property.rs
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617use super::*;18use up_data_structs::PropertyScope;19use core::convert::AsRef;2021pub const RESOURCE_ID_PREFIX: &str = "rsid-";22pub const USER_PROPERTY_PREFIX: &str = "userprop-";2324pub enum RmrkProperty<'r> {25	Metadata,26	CollectionType,27	RmrkInternalCollectionId,28	TokenType,29	Transferable,30	RoyaltyInfo,31	Equipped,32	ResourcePriorities,33	NextResourceId,34	ResourceId(RmrkResourceId),35	PendingNftAccept,36	PendingChildren,37	AssociatedBases,38	Parts,39	Base,40	Src,41	EquippedNft,42	BaseType,43	ExternalPartId,44	EquippableList,45	ZIndex,46	ThemeName,47	ThemeInherit,48	UserProperty(&'r [u8]),49}5051impl<'r> RmrkProperty<'r> {52	pub fn to_key<T: Config>(self) -> Result<PropertyKey, Error<T>> {53		fn get_bytes<T: AsRef<[u8]>>(container: &T) -> &[u8] {54			container.as_ref()55		}5657		macro_rules! key {58            ($($component:expr),+) => {59                PropertyKey::try_from([$(key!(@ &$component)),+].concat())60                    .map_err(|_| <Error<T>>::RmrkPropertyKeyIsTooLong)61            };6263            (@ $key:expr) => {64                get_bytes($key)65            };66        }6768		match self {69			Self::Metadata => key!("metadata"),70			Self::CollectionType => key!("collection-type"),71			Self::RmrkInternalCollectionId => key!("internal-id"),72			Self::TokenType => key!("token-type"),73			Self::Transferable => key!("transferable"),74			Self::RoyaltyInfo => key!("royalty-info"),75			Self::Equipped => key!("equipped"),76			Self::ResourcePriorities => key!("resource-priorities"),77			Self::NextResourceId => key!("next-resource-id"),78			Self::ResourceId(id) => key!(RESOURCE_ID_PREFIX, id.to_le_bytes()),79			Self::PendingNftAccept => key!("pending-nft-accept"),80			Self::PendingChildren => key!("pending-children"),81			Self::AssociatedBases => key!("assoc-bases"),82			Self::Parts => key!("parts"),83			Self::Base => key!("base"),84			Self::Src => key!("src"),85			Self::EquippedNft => key!("equipped-nft"),86			Self::BaseType => key!("base-type"),87			Self::ExternalPartId => key!("ext-part-id"),88			Self::EquippableList => key!("equippable-list"),89			Self::ZIndex => key!("z-index"),90			Self::ThemeName => key!("theme-name"),91			Self::ThemeInherit => key!("theme-inherit"),92			Self::UserProperty(name) => key!(USER_PROPERTY_PREFIX, name),93		}94	}95}9697pub fn strip_key_prefix(key: &PropertyKey, prefix: &str) -> Option<PropertyKey> {98	let key_prefix = PropertyKey::try_from(prefix.as_bytes().to_vec()).ok()?;99	let key_prefix = PropertyScope::Rmrk.apply(key_prefix).ok()?;100101	key.as_slice().strip_prefix(key_prefix.as_slice())?102		.to_vec().try_into().ok()103}104105pub fn is_valid_key_prefix(key: &PropertyKey, prefix: &str) -> bool {106	strip_key_prefix(key, prefix).is_some()107}