git.delta.rocks / unique-network / refs/commits / 6ad82a288c79

difftreelog

fix disallow nesting under non-existing tokens

Yaroslav Bolyukin2023-01-18parent: #d3f529b.patch.diff
in: master

3 files changed

modifiedpallets/nonfungible/src/lib.rsdiffbeforeafterboth
1332 let permissive = nesting.permissive;1332 let permissive = nesting.permissive;
13331333
1334 if permissive {1334 if permissive {
1335 // Pass1335 ensure!(
1336 <TokenData<T>>::contains_key((handle.id, under)),
1337 <CommonError<T>>::TokenNotFound
1338 );
1336 } else if nesting.token_owner1339 } else if nesting.token_owner
1337 && <PalletStructure<T>>::check_indirectly_owned(1340 && <PalletStructure<T>>::check_indirectly_owned(
1338 sender.clone(),1341 sender.clone(),
1341 Some(from),1344 Some(from),
1342 nesting_budget,1345 nesting_budget,
1343 )? {1346 )? {
1344 // Pass1347 // Pass, token existence is checked in `check_indirectly_owned`
1345 } else if nesting.collection_admin && handle.is_owner_or_admin(&sender) {1348 } else if nesting.collection_admin && handle.is_owner_or_admin(&sender) {
1346 // Pass1349 ensure!(
1350 <TokenData<T>>::contains_key((handle.id, under)),
1351 <CommonError<T>>::TokenNotFound
1352 );
1347 } else {1353 } else {
1348 fail!(<CommonError<T>>::UserIsNotAllowedToNest);1354 fail!(<CommonError<T>>::UserIsNotAllowedToNest);
1349 }1355 }
modifiedpallets/structure/Cargo.tomldiffbeforeafterboth
--- a/pallets/structure/Cargo.toml
+++ b/pallets/structure/Cargo.toml
@@ -17,19 +17,10 @@
 ] }
 up-data-structs = { path = "../../primitives/data-structs", default-features = false }
 pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.36" }
+log = { version = "0.4.17", default-features = false }
 
 [features]
 default = ["std"]
-std = [
-	"frame-support/std",
-	"frame-system/std",
-	"frame-benchmarking/std",
-	"sp-std/std",
-	"pallet-common/std",
-	"scale-info/std",
-	"parity-scale-codec/std",
-	"up-data-structs/std",
-	"pallet-evm/std",
-]
+std = ["frame-support/std", "frame-system/std", "frame-benchmarking/std", "sp-std/std", "pallet-common/std", "scale-info/std", "parity-scale-codec/std", "up-data-structs/std", "pallet-evm/std"]
 runtime-benchmarks = ['frame-benchmarking', 'pallet-common/runtime-benchmarks']
 try-runtime = ["frame-support/try-runtime"]
modifiedpallets/structure/src/lib.rsdiffbeforeafterboth
--- a/pallets/structure/src/lib.rs
+++ b/pallets/structure/src/lib.rs
@@ -54,6 +54,7 @@
 #![cfg_attr(not(feature = "std"), no_std)]
 
 use pallet_common::CommonCollectionOperations;
+use pallet_common::{erc::CrossAccountId, eth::is_collection};
 use sp_std::collections::btree_set::BTreeSet;
 
 use frame_support::dispatch::{DispatchError, DispatchResult, DispatchResultWithPostInfo};
@@ -86,6 +87,8 @@
 		BreadthLimit,
 		/// Couldn't find the token owner that is itself a token.
 		TokenNotFound,
+		/// Tried to nest token under collection contract address, instead of token address
+		CantNestTokenUnderCollection,
 	}
 
 	#[pallet::event]
@@ -302,7 +305,7 @@
 		token_id: TokenId,
 		nesting_budget: &dyn Budget,
 	) -> DispatchResult {
-		Self::try_exec_if_owner_is_valid_nft(under, |collection, parent_id| {
+		Self::try_exec_if_token(under, |collection, parent_id| {
 			collection.check_nesting(from, (collection_id, token_id), parent_id, nesting_budget)
 		})
 	}
@@ -319,7 +322,7 @@
 		token_id: TokenId,
 		nesting_budget: &dyn Budget,
 	) -> DispatchResult {
-		Self::try_exec_if_owner_is_valid_nft(under, |collection, parent_id| {
+		Self::try_exec_if_token(under, |collection, parent_id| {
 			collection.check_nesting(from, (collection_id, token_id), parent_id, nesting_budget)?;
 
 			collection.nest(parent_id, (collection_id, token_id));
@@ -336,7 +339,7 @@
 		collection_id: CollectionId,
 		token_id: TokenId,
 	) {
-		Self::exec_if_owner_is_valid_nft(owner, |collection, parent_id| {
+		Self::exec_if_token(owner, |collection, parent_id| {
 			collection.nest(parent_id, (collection_id, token_id))
 		});
 	}
@@ -347,45 +350,45 @@
 		collection_id: CollectionId,
 		token_id: TokenId,
 	) {
-		Self::exec_if_owner_is_valid_nft(owner, |collection, parent_id| {
-			collection.unnest(parent_id, (collection_id, token_id))
-		});
+		if let Err(e) = Self::try_exec_if_token(owner, |collection, parent_id| {
+			collection.unnest(parent_id, (collection_id, token_id));
+			Ok(())
+		}) {
+			log::warn!("unnest precondition failed: {e:?}")
+		}
 	}
 
-	fn exec_if_owner_is_valid_nft(
+	/// # Panics
+	/// If [`Self::try_exec_if_token`] fails
+	fn exec_if_token(
 		account: &T::CrossAccountId,
 		action: impl FnOnce(&dyn CommonCollectionOperations<T>, TokenId),
 	) {
-		Self::try_exec_if_owner_is_valid_nft(account, |collection, id| {
+		Self::try_exec_if_token(account, |collection, id| {
 			action(collection, id);
 			Ok(())
 		})
 		.unwrap();
 	}
 
-	fn try_exec_if_owner_is_valid_nft(
+	/// If `account` is a token address, execute `action` providing found collection as an argument
+	/// Token may not exist, it is expected it will be checked in the callback.
+	fn try_exec_if_token(
 		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(());
+		if is_collection(&account.as_eth()) {
+			fail!(<Error<T>>::CantNestTokenUnderCollection);
 		}
+		let Some((collection, token)) = T::CrossTokenAddressMapping::address_to_token(account) else {
+			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 handle = <CollectionHandle<T>>::try_get(collection)?;
 
 		let dispatch = T::CollectionDispatch::dispatch(handle);
 		let dispatch = dispatch.as_dyn();
 
-		action(dispatch, account.1)
+		action(dispatch, token)
 	}
 }