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
--- a/pallets/nonfungible/src/lib.rs
+++ b/pallets/nonfungible/src/lib.rs
@@ -27,7 +27,7 @@
 use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm};
 use pallet_common::{
 	Error as CommonError, Pallet as PalletCommon, Event as CommonEvent, CollectionHandle,
-	dispatch::CollectionDispatch, eth::collection_id_to_address,
+	eth::collection_id_to_address,
 };
 use pallet_structure::Pallet as PalletStructure;
 use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};
@@ -77,6 +77,8 @@
 		NotNonfungibleDataUsedToMintFungibleCollectionToken,
 		/// Used amount > 1 with NFT
 		NonfungibleItemsHaveNoAmount,
+		/// Unable to burn NFT with children
+		CantBurnNftWithChildren,
 	}
 
 	#[pallet::config]
@@ -128,7 +130,20 @@
 		QueryKind = ValueQuery,
 	>;
 
+	/// Used to enumerate token's children
 	#[pallet::storage]
+	#[pallet::getter(fn token_children)]
+	pub type TokenChildren<T: Config> = StorageNMap<
+		Key = (
+			Key<Twox64Concat, CollectionId>,
+			Key<Twox64Concat, TokenId>,
+			Key<Twox64Concat, (CollectionId, TokenId)>,
+		),
+		Value = bool,
+		QueryKind = ValueQuery,
+	>;
+
+	#[pallet::storage]
 	pub type AccountBalance<T: Config> = StorageNMap<
 		Key = (
 			Key<Twox64Concat, CollectionId>,
@@ -283,6 +298,7 @@
 		PalletCommon::destroy_collection(collection.0, sender)?;
 
 		<TokenData<T>>::remove_prefix((id,), None);
+		<TokenChildren<T>>::remove_prefix((id,), None);
 		<Owned<T>>::remove_prefix((id,), None);
 		<TokensMinted<T>>::remove(id);
 		<TokensBurnt<T>>::remove(id);
@@ -308,6 +324,10 @@
 			collection.check_allowlist(sender)?;
 		}
 
+		if Self::token_has_children(collection.id, token) {
+			return Err(<Error<T>>::CantBurnNftWithChildren.into());
+		}
+
 		let burnt = <TokensBurnt<T>>::get(collection.id)
 			.checked_add(1)
 			.ok_or(ArithmeticError::Overflow)?;
@@ -321,6 +341,11 @@
 		} else {
 			<AccountBalance<T>>::insert((collection.id, token_data.owner.clone()), balance);
 		}
+
+		if let Some(owner) = T::CrossTokenAddressMapping::address_to_token(&token_data.owner) {
+			Self::unnest(owner, (collection.id, token));
+		}
+
 		// =========
 
 		<Owned<T>>::remove((collection.id, &token_data.owner, token));
@@ -554,18 +579,13 @@
 			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
+		)?;
 
 		// =========
 
@@ -654,17 +674,14 @@
 
 		for (i, data) in data.iter().enumerate() {
 			let token = TokenId(first_token + i as u32 + 1);
-			if let Some(target) = T::CrossTokenAddressMapping::address_to_token(&data.owner) {
-				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),
-					target.1,
-					nesting_budget,
-				)?;
-			}
+
+			<PalletStructure<T>>::check_nesting(
+				sender.clone(),
+				&data.owner,
+				collection.id,
+				token,
+				nesting_budget,
+			)?;
 		}
 
 		// =========
@@ -681,6 +698,8 @@
 					},
 				);
 
+				<PalletStructure<T>>::nest_if_sent_to_token(&data.owner, collection.id, TokenId(token));
+
 				if let Err(e) = Self::set_token_properties(
 					collection,
 					sender,
@@ -928,6 +947,29 @@
 		Ok(())
 	}
 
+	fn nest(
+		under: (CollectionId, TokenId),
+		to_nest: (CollectionId, TokenId),
+	) {
+		<TokenChildren<T>>::insert(
+			(under.0, under.1, (to_nest.0, to_nest.1)),
+			true
+		);
+	}
+
+	fn unnest(
+		under: (CollectionId, TokenId),
+		to_unnest: (CollectionId, TokenId),
+	) {
+		<TokenChildren<T>>::remove(
+			(under.0, under.1, to_unnest)
+		);
+	}
+
+	fn token_has_children(collection_id: CollectionId, token_id: TokenId) -> bool {
+		<TokenChildren<T>>::iter_prefix((collection_id, token_id)).next().is_some()
+	}
+
 	/// Delegated to `create_multiple_items`
 	pub fn create_item(
 		collection: &NonfungibleHandle<T>,
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
1#![cfg_attr(not(feature = "std"), no_std)]1#![cfg_attr(not(feature = "std"), no_std)]
22
3use pallet_common::CommonCollectionOperations;
3use sp_std::collections::btree_set::BTreeSet;4use sp_std::collections::btree_set::BTreeSet;
45
5use frame_support::dispatch::DispatchError;6use frame_support::dispatch::{DispatchError, DispatchResult};
6use frame_support::fail;7use frame_support::fail;
7pub use pallet::*;8pub use pallet::*;
8use pallet_common::{dispatch::CollectionDispatch, CollectionHandle};9use pallet_common::{dispatch::CollectionDispatch, CollectionHandle};
174 v if v == target_parent => return Ok(true),175 v if v == target_parent => return Ok(true),
175 // Token is owned by other user176 // Token is owned by other user
176 Parent::User(_) => return Ok(false),177 Parent::User(_) => return Ok(false),
177 Parent::TokenNotFound => return Ok(false),178 Parent::TokenNotFound => return Err(<Error<T>>::TokenNotFound.into()),
178 // Continue parent chain179 // Continue parent chain
179 Parent::Token(_, _) => {}180 Parent::Token(_, _) => {}
180 }181 }
183 Err(<Error<T>>::DepthLimit.into())184 Err(<Error<T>>::DepthLimit.into())
184 }185 }
186
187 pub fn check_nesting(
188 from: T::CrossAccountId,
189 under: &T::CrossAccountId,
190 collection_id: CollectionId,
191 token_id: TokenId,
192 nesting_budget: &dyn Budget
193 ) -> DispatchResult {
194 Self::try_dispatched(
195 under,
196 |d, parent_id| d.check_nesting(
197 from,
198 (collection_id, token_id),
199 parent_id,
200 nesting_budget
201 )
202 )
203 }
204
205 pub fn try_nest_if_sent_to_token(
206 from: T::CrossAccountId,
207 under: &T::CrossAccountId,
208 collection_id: CollectionId,
209 token_id: TokenId,
210 nesting_budget: &dyn Budget
211 ) -> DispatchResult {
212 Self::try_dispatched(
213 under,
214 |d, parent_id| {
215 d.check_nesting(
216 from,
217 (collection_id, token_id),
218 parent_id,
219 nesting_budget
220 )?;
221
222 d.nest(parent_id, (collection_id, token_id));
223
224 Ok(())
225 }
226 )
227 }
228
229 pub fn nest_if_sent_to_token(
230 owner: &T::CrossAccountId,
231 collection_id: CollectionId,
232 token_id: TokenId
233 ) {
234 Self::dispatched(
235 owner,
236 |d, parent_id| d.nest(
237 parent_id,
238 (collection_id, token_id)
239 )
240 );
241 }
242
243 pub fn unnest_if_nested(
244 owner: &T::CrossAccountId,
245 collection_id: CollectionId,
246 token_id: TokenId
247 ) {
248 Self::dispatched(
249 owner,
250 |d, parent_id| d.unnest(
251 parent_id,
252 (collection_id, token_id)
253 )
254 );
255 }
256
257 fn dispatched(
258 account: &T::CrossAccountId,
259 action: impl FnOnce(&dyn CommonCollectionOperations<T>, TokenId)
260 ) {
261 Self::try_dispatched(
262 account,
263 |d, id| {
264 action(d, id);
265 Ok(())
266 }
267 ).unwrap();
268 }
269
270 fn try_dispatched(
271 account: &T::CrossAccountId,
272 action: impl FnOnce(&dyn CommonCollectionOperations<T>, TokenId) -> DispatchResult
273 ) -> DispatchResult {
274 let account = T::CrossTokenAddressMapping::address_to_token(account);
275
276 if account.is_none() {
277 return Ok(());
278 }
279
280 let account = account.unwrap();
281
282 let handle = <CollectionHandle<T>>::try_get(account.0);
283
284 if handle.is_err() {
285 return Ok(());
286 }
287
288 let handle = handle.unwrap();
289
290 let dispatch = T::CollectionDispatch::dispatch(handle);
291 let dispatch = dispatch.as_dyn();
292
293 action(dispatch, account.1)
294 }
185}295}
186296