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

difftreelog

feat add token children

Daniel Shiposha2022-05-26parent: #459d9af.patch.diff
in: master

6 files changed

modifiedpallets/common/src/lib.rsdiffbeforeafterboth
--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -1237,6 +1237,18 @@
 		budget: &dyn Budget,
 	) -> DispatchResult;
 
+	fn nest(
+		&self,
+		_under: TokenId,
+		_to_nest: (CollectionId, TokenId)
+	) {}
+
+	fn unnest(
+		&self,
+		_under: TokenId,
+		_to_nest: (CollectionId, TokenId)
+	) {}
+
 	fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId>;
 	fn collection_tokens(&self) -> Vec<TokenId>;
 	fn token_exists(&self, token: TokenId) -> bool;
modifiedpallets/fungible/src/lib.rsdiffbeforeafterboth
--- a/pallets/fungible/src/lib.rs
+++ b/pallets/fungible/src/lib.rs
@@ -25,8 +25,8 @@
 	budget::Budget,
 };
 use pallet_common::{
-	Error as CommonError, Event as CommonEvent, Pallet as PalletCommon, CollectionHandle,
-	dispatch::CollectionDispatch, eth::collection_id_to_address,
+	Error as CommonError, Event as CommonEvent, Pallet as PalletCommon,
+	eth::collection_id_to_address,
 };
 use pallet_evm::Pallet as PalletEvm;
 use pallet_structure::Pallet as PalletStructure;
@@ -176,6 +176,11 @@
 
 		if balance == 0 {
 			<Balance<T>>::remove((collection.id, owner));
+			<PalletStructure<T>>::unnest_if_nested(
+				owner,
+				collection.id,
+				TokenId::default()
+			);
 		} else {
 			<Balance<T>>::insert((collection.id, owner), balance);
 		}
@@ -228,22 +233,17 @@
 		} else {
 			None
 		};
-
-		if let Some(target) = T::CrossTokenAddressMapping::address_to_token(to) {
-			let handle = <CollectionHandle<T>>::try_get(target.0)?;
-			let dispatch = T::CollectionDispatch::dispatch(handle);
-			let dispatch = dispatch.as_dyn();
 
-			dispatch.check_nesting(
-				from.clone(),
-				(collection.id, TokenId::default()),
-				target.1,
-				nesting_budget,
-			)?;
-		}
-
 		// =========
 
+		<PalletStructure<T>>::try_nest_if_sent_to_token(
+			from.clone(),
+			to,
+			collection.id,
+			TokenId::default(),
+			nesting_budget
+		)?;
+
 		if let Some(balance_to) = balance_to {
 			// from != to
 			if balance_from == 0 {
@@ -306,18 +306,13 @@
 		}
 
 		for (to, _) in balances.iter() {
-			if let Some(target) = T::CrossTokenAddressMapping::address_to_token(to) {
-				let handle = <CollectionHandle<T>>::try_get(target.0)?;
-				let dispatch = T::CollectionDispatch::dispatch(handle);
-				let dispatch = dispatch.as_dyn();
-
-				dispatch.check_nesting(
-					sender.clone(),
-					(collection.id, TokenId::default()),
-					target.1,
-					nesting_budget,
-				)?;
-			}
+			<PalletStructure<T>>::check_nesting(
+				sender.clone(),
+				to,
+				collection.id,
+				TokenId::default(),
+				nesting_budget,
+			)?;
 		}
 
 		// =========
@@ -325,7 +320,7 @@
 		<TotalSupply<T>>::insert(collection.id, total_supply);
 		for (user, amount) in balances {
 			<Balance<T>>::insert((collection.id, &user), amount);
-
+			<PalletStructure<T>>::nest_if_sent_to_token(&user, collection.id, TokenId::default());
 			<PalletEvm<T>>::deposit_log(
 				ERC20Events::Transfer {
 					from: H160::default(),
modifiedpallets/nonfungible/src/common.rsdiffbeforeafterboth
--- a/pallets/nonfungible/src/common.rs
+++ b/pallets/nonfungible/src/common.rs
@@ -353,6 +353,22 @@
 		<Pallet<T>>::check_nesting(self, sender, from, under, budget)
 	}
 
+	fn nest(
+		&self,
+		under: TokenId,
+		to_nest: (CollectionId, TokenId)
+	) {
+		<Pallet<T>>::nest((self.id, under), to_nest);
+	}
+
+	fn unnest(
+		&self,
+		under: TokenId,
+		to_unnest: (CollectionId, TokenId)
+	) {
+		<Pallet<T>>::unnest((self.id, under), to_unnest);
+	}
+
 	fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId> {
 		<Owned<T>>::iter_prefix((self.id, account))
 			.map(|(id, _)| id)
modifiedpallets/nonfungible/src/lib.rsdiffbeforeafterboth
27use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm};27use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm};
28use pallet_common::{28use pallet_common::{
29 Error as CommonError, Pallet as PalletCommon, Event as CommonEvent, CollectionHandle,29 Error as CommonError, Pallet as PalletCommon, Event as CommonEvent, CollectionHandle,
30 dispatch::CollectionDispatch, eth::collection_id_to_address,30 eth::collection_id_to_address,
31};31};
32use pallet_structure::Pallet as PalletStructure;32use pallet_structure::Pallet as PalletStructure;
33use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};33use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};
77 NotNonfungibleDataUsedToMintFungibleCollectionToken,77 NotNonfungibleDataUsedToMintFungibleCollectionToken,
78 /// Used amount > 1 with NFT78 /// Used amount > 1 with NFT
79 NonfungibleItemsHaveNoAmount,79 NonfungibleItemsHaveNoAmount,
80 /// Unable to burn NFT with children
81 CantBurnNftWithChildren,
80 }82 }
8183
82 #[pallet::config]84 #[pallet::config]
128 QueryKind = ValueQuery,130 QueryKind = ValueQuery,
129 >;131 >;
132
133 /// Used to enumerate token's children
134 #[pallet::storage]
135 #[pallet::getter(fn token_children)]
136 pub type TokenChildren<T: Config> = StorageNMap<
137 Key = (
138 Key<Twox64Concat, CollectionId>,
139 Key<Twox64Concat, TokenId>,
140 Key<Twox64Concat, (CollectionId, TokenId)>,
141 ),
142 Value = bool,
143 QueryKind = ValueQuery,
144 >;
130145
131 #[pallet::storage]146 #[pallet::storage]
132 pub type AccountBalance<T: Config> = StorageNMap<147 pub type AccountBalance<T: Config> = StorageNMap<
283 PalletCommon::destroy_collection(collection.0, sender)?;298 PalletCommon::destroy_collection(collection.0, sender)?;
284299
285 <TokenData<T>>::remove_prefix((id,), None);300 <TokenData<T>>::remove_prefix((id,), None);
301 <TokenChildren<T>>::remove_prefix((id,), None);
286 <Owned<T>>::remove_prefix((id,), None);302 <Owned<T>>::remove_prefix((id,), None);
287 <TokensMinted<T>>::remove(id);303 <TokensMinted<T>>::remove(id);
288 <TokensBurnt<T>>::remove(id);304 <TokensBurnt<T>>::remove(id);
308 collection.check_allowlist(sender)?;324 collection.check_allowlist(sender)?;
309 }325 }
326
327 if Self::token_has_children(collection.id, token) {
328 return Err(<Error<T>>::CantBurnNftWithChildren.into());
329 }
310330
311 let burnt = <TokensBurnt<T>>::get(collection.id)331 let burnt = <TokensBurnt<T>>::get(collection.id)
312 .checked_add(1)332 .checked_add(1)
322 <AccountBalance<T>>::insert((collection.id, token_data.owner.clone()), balance);342 <AccountBalance<T>>::insert((collection.id, token_data.owner.clone()), balance);
323 }343 }
344
345 if let Some(owner) = T::CrossTokenAddressMapping::address_to_token(&token_data.owner) {
346 Self::unnest(owner, (collection.id, token));
347 }
348
324 // =========349 // =========
325350
554 None579 None
555 };580 };
556
557 if let Some(target) = T::CrossTokenAddressMapping::address_to_token(to) {
558 let handle = <CollectionHandle<T>>::try_get(target.0)?;
559 let dispatch = T::CollectionDispatch::dispatch(handle);
560 let dispatch = dispatch.as_dyn();
561581
562 dispatch.check_nesting(582 <PalletStructure<T>>::try_nest_if_sent_to_token(
563 from.clone(),583 from.clone(),
584 to,
564 (collection.id, token),585 collection.id,
565 target.1,586 token,
566 nesting_budget,587 nesting_budget
567 )?;588 )?;
568 }
569589
570 // =========590 // =========
571591
654674
655 for (i, data) in data.iter().enumerate() {675 for (i, data) in data.iter().enumerate() {
656 let token = TokenId(first_token + i as u32 + 1);676 let token = TokenId(first_token + i as u32 + 1);
657 if let Some(target) = T::CrossTokenAddressMapping::address_to_token(&data.owner) {677
658 let handle = <CollectionHandle<T>>::try_get(target.0)?;
659 let dispatch = T::CollectionDispatch::dispatch(handle);
660 let dispatch = dispatch.as_dyn();
661 dispatch.check_nesting(678 <PalletStructure<T>>::check_nesting(
662 sender.clone(),679 sender.clone(),
680 &data.owner,
663 (collection.id, token),681 collection.id,
664 target.1,682 token,
665 nesting_budget,683 nesting_budget,
666 )?;684 )?;
667 }
668 }685 }
669686
670 // =========687 // =========
681 },698 },
682 );699 );
700
701 <PalletStructure<T>>::nest_if_sent_to_token(&data.owner, collection.id, TokenId(token));
683702
684 if let Err(e) = Self::set_token_properties(703 if let Err(e) = Self::set_token_properties(
685 collection,704 collection,
928 Ok(())947 Ok(())
929 }948 }
949
950 fn nest(
951 under: (CollectionId, TokenId),
952 to_nest: (CollectionId, TokenId),
953 ) {
954 <TokenChildren<T>>::insert(
955 (under.0, under.1, (to_nest.0, to_nest.1)),
956 true
957 );
958 }
959
960 fn unnest(
961 under: (CollectionId, TokenId),
962 to_unnest: (CollectionId, TokenId),
963 ) {
964 <TokenChildren<T>>::remove(
965 (under.0, under.1, to_unnest)
966 );
967 }
968
969 fn token_has_children(collection_id: CollectionId, token_id: TokenId) -> bool {
970 <TokenChildren<T>>::iter_prefix((collection_id, token_id)).next().is_some()
971 }
930972
931 /// Delegated to `create_multiple_items`973 /// Delegated to `create_multiple_items`
932 pub fn create_item(974 pub fn create_item(
modifiedpallets/refungible/src/lib.rsdiffbeforeafterboth
--- a/pallets/refungible/src/lib.rs
+++ b/pallets/refungible/src/lib.rs
@@ -23,8 +23,7 @@
 };
 use pallet_evm::account::CrossAccountId;
 use pallet_common::{
-	Error as CommonError, Event as CommonEvent, Pallet as PalletCommon, CollectionHandle,
-	dispatch::CollectionDispatch,
+	Error as CommonError, Event as CommonEvent, Pallet as PalletCommon,
 };
 use pallet_structure::Pallet as PalletStructure;
 use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};
@@ -265,6 +264,7 @@
 			// =========
 
 			<Owned<T>>::remove((collection.id, owner, token));
+			<PalletStructure<T>>::unnest_if_nested(owner, collection.id, token);
 			<AccountBalance<T>>::insert((collection.id, owner), account_balance);
 			Self::burn_token(collection, token)?;
 			<PalletCommon<T>>::deposit_event(CommonEvent::ItemDestroyed(
@@ -292,6 +292,7 @@
 
 		if balance == 0 {
 			<Owned<T>>::remove((collection.id, owner, token));
+			<PalletStructure<T>>::unnest_if_nested(owner, collection.id, token);
 			<Balance<T>>::remove((collection.id, token, owner));
 			<AccountBalance<T>>::insert((collection.id, owner), account_balance);
 		} else {
@@ -371,22 +372,17 @@
 		} else {
 			None
 		};
-
-		if let Some(target) = T::CrossTokenAddressMapping::address_to_token(to) {
-			let handle = <CollectionHandle<T>>::try_get(target.0)?;
-			let dispatch = T::CollectionDispatch::dispatch(handle);
-			let dispatch = dispatch.as_dyn();
-
-			dispatch.check_nesting(
-				from.clone(),
-				(collection.id, token),
-				target.1,
-				nesting_budget,
-			)?;
-		}
 
 		// =========
 
+		<PalletStructure<T>>::try_nest_if_sent_to_token(
+			from.clone(),
+			to,
+			collection.id,
+			token,
+			nesting_budget
+		)?;
+
 		if let Some(balance_to) = balance_to {
 			// from != to
 			if balance_from == 0 {
@@ -488,18 +484,14 @@
 		for (i, token) in data.iter().enumerate() {
 			let token_id = TokenId(first_token_id + i as u32 + 1);
 			for (to, _) in token.users.iter() {
-				if let Some(target) = T::CrossTokenAddressMapping::address_to_token(to) {
-					let handle = <CollectionHandle<T>>::try_get(target.0)?;
-					let dispatch = T::CollectionDispatch::dispatch(handle);
-					let dispatch = dispatch.as_dyn();
 
-					dispatch.check_nesting(
-						sender.clone(),
-						(collection.id, token_id),
-						target.1,
-						nesting_budget,
-					)?;
-				}
+				<PalletStructure<T>>::check_nesting(
+					sender.clone(),
+					to,
+					collection.id,
+					token_id,
+					nesting_budget,
+				)?;
 			}
 		}
 
@@ -519,12 +511,15 @@
 					const_data: token.const_data,
 				},
 			);
+
 			for (user, amount) in token.users.into_iter() {
 				if amount == 0 {
 					continue;
 				}
 				<Balance<T>>::insert((collection.id, token_id, &user), amount);
 				<Owned<T>>::insert((collection.id, &user, TokenId(token_id)), true);
+				<PalletStructure<T>>::nest_if_sent_to_token(&user, collection.id, TokenId(token_id));
+
 				// TODO: ERC20 transfer event
 				<PalletCommon<T>>::deposit_event(CommonEvent::ItemCreated(
 					collection.id,
modifiedpallets/structure/src/lib.rsdiffbeforeafterboth
--- a/pallets/structure/src/lib.rs
+++ b/pallets/structure/src/lib.rs
@@ -1,8 +1,9 @@
 #![cfg_attr(not(feature = "std"), no_std)]
 
+use pallet_common::CommonCollectionOperations;
 use sp_std::collections::btree_set::BTreeSet;
 
-use frame_support::dispatch::DispatchError;
+use frame_support::dispatch::{DispatchError, DispatchResult};
 use frame_support::fail;
 pub use pallet::*;
 use pallet_common::{dispatch::CollectionDispatch, CollectionHandle};
@@ -174,7 +175,7 @@
 				v if v == target_parent => return Ok(true),
 				// Token is owned by other user
 				Parent::User(_) => return Ok(false),
-				Parent::TokenNotFound => return Ok(false),
+				Parent::TokenNotFound => return Err(<Error<T>>::TokenNotFound.into()),
 				// Continue parent chain
 				Parent::Token(_, _) => {}
 			}
@@ -182,4 +183,113 @@
 
 		Err(<Error<T>>::DepthLimit.into())
 	}
+
+	pub fn check_nesting(
+		from: T::CrossAccountId,
+		under: &T::CrossAccountId,
+		collection_id: CollectionId,
+		token_id: TokenId,
+		nesting_budget: &dyn Budget
+	) -> DispatchResult {
+		Self::try_dispatched(
+			under,
+			|d, parent_id| d.check_nesting(
+				from,
+				(collection_id, token_id),
+				parent_id,
+				nesting_budget
+			)
+		)
+	}
+
+	pub fn try_nest_if_sent_to_token(
+		from: T::CrossAccountId,
+		under: &T::CrossAccountId,
+		collection_id: CollectionId,
+		token_id: TokenId,
+		nesting_budget: &dyn Budget
+	) -> DispatchResult {
+		Self::try_dispatched(
+			under,
+			|d, parent_id| {
+				d.check_nesting(
+					from,
+					(collection_id, token_id),
+					parent_id,
+					nesting_budget
+				)?;
+
+				d.nest(parent_id, (collection_id, token_id));
+
+				Ok(())
+			}
+		)
+	}
+
+	pub fn nest_if_sent_to_token(
+		owner: &T::CrossAccountId,
+		collection_id: CollectionId,
+		token_id: TokenId
+	) {
+		Self::dispatched(
+			owner,
+			|d, parent_id| d.nest(
+				parent_id,
+				(collection_id, token_id)
+			)
+		);
+	}
+
+	pub fn unnest_if_nested(
+		owner: &T::CrossAccountId,
+		collection_id: CollectionId,
+		token_id: TokenId
+	) {
+		Self::dispatched(
+			owner,
+			|d, parent_id| d.unnest(
+			parent_id,
+			(collection_id, token_id)
+			)
+		);
+	}
+
+	fn dispatched(
+		account: &T::CrossAccountId,
+		action: impl FnOnce(&dyn CommonCollectionOperations<T>, TokenId)
+	) {
+		Self::try_dispatched(
+			account,
+			|d, id| {
+				action(d, id);
+				Ok(())
+			}
+		).unwrap();
+	}
+
+	fn try_dispatched(
+		account: &T::CrossAccountId,
+		action: impl FnOnce(&dyn CommonCollectionOperations<T>, TokenId) -> DispatchResult
+	) -> DispatchResult {
+		let account = T::CrossTokenAddressMapping::address_to_token(account);
+
+		if account.is_none() {
+			return Ok(());
+		}
+
+		let account = account.unwrap();
+
+		let handle = <CollectionHandle<T>>::try_get(account.0);
+
+		if handle.is_err() {
+			return Ok(());
+		}
+
+		let handle = handle.unwrap();
+
+		let dispatch = T::CollectionDispatch::dispatch(handle);
+		let dispatch = dispatch.as_dyn();
+
+		action(dispatch, account.1)
+	}
 }