git.delta.rocks / unique-network / refs/commits / 0ba1adb79fd8

difftreelog

misk: generalize 2 methods

Trubnikov Sergey2023-02-01parent: #e31d9d0.patch.diff
in: master

3 files changed

modifiedpallets/common/src/lib.rsdiffbeforeafterboth
--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -66,10 +66,11 @@
 	ensure,
 	traits::{Imbalance, Get, Currency, WithdrawReasons, ExistenceRequirement},
 	dispatch::Pays,
-	transactional,
+	transactional, fail,
 };
 use pallet_evm::GasWeightMapping;
 use up_data_structs::{
+	AccessMode,
 	COLLECTION_NUMBER_LIMIT,
 	Collection,
 	RpcCollection,
@@ -124,6 +125,8 @@
 pub use pallet::*;
 use sp_core::H160;
 use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};
+
+use crate::erc::CollectionHelpersEvents;
 #[cfg(feature = "runtime-benchmarks")]
 pub mod benchmarking;
 pub mod dispatch;
@@ -1264,6 +1267,128 @@
 		Ok(())
 	}
 
+	/// A batch operation to add, edit or remove properties for a token.
+	/// It sets or removes a token's properties according to
+	/// `properties_updates` contents:
+	/// * sets a property under the <key> with the value provided `(<key>, Some(<value>))`
+	/// * removes a property under the <key> if the value is `None` `(<key>, None)`.
+	///
+	/// - `nesting_budget`: Limit for searching parents in-depth to check ownership.
+	/// - `is_token_create`: Indicates that method is called during token initialization.
+	///   Allows to bypass ownership check.
+	///
+	/// All affected properties should have `mutable` permission
+	/// to be **deleted** or to be **set more than once**,
+	/// and the sender should have permission to edit those properties.
+	///
+	/// This function fires an event for each property change.
+	/// In case of an error, all the changes (including the events) will be reverted
+	/// since the function is transactional.
+	pub fn modify_token_properties(
+		collection: &CollectionHandle<T>,
+		sender: &T::CrossAccountId,
+		token_id: TokenId,
+		properties_updates: impl Iterator<Item = (PropertyKey, Option<PropertyValue>)>,
+		is_token_create: bool,
+		mut stored_properties: Properties,
+		is_token_owner: impl Fn() -> Result<bool, DispatchError>,
+		set_token_properties: impl FnOnce(Properties),
+	) -> DispatchResult {
+		let is_collection_admin = collection.is_owner_or_admin(sender);
+		let permissions = Self::property_permissions(collection.id);
+
+		for (key, value) in properties_updates {
+			let permission = permissions
+				.get(&key)
+				.cloned()
+				.unwrap_or_else(PropertyPermission::none);
+
+			let is_property_exists = stored_properties.get(&key).is_some();
+
+			match permission {
+				PropertyPermission { mutable: false, .. } if is_property_exists => {
+					return Err(<Error<T>>::NoPermission.into());
+				}
+
+				PropertyPermission {
+					collection_admin,
+					token_owner,
+					..
+				} => {
+					//TODO: investigate threats during public minting.
+					let is_token_create =
+						is_token_create && (collection_admin || token_owner) && value.is_some();
+					if !(is_token_create
+						|| (collection_admin && is_collection_admin)
+						|| (token_owner && is_token_owner()?))
+					{
+						fail!(<Error<T>>::NoPermission);
+					}
+				}
+			}
+
+			match value {
+				Some(value) => {
+					stored_properties
+						.try_set(key.clone(), value)
+						.map_err(<Error<T>>::from)?;
+
+					Self::deposit_event(Event::TokenPropertySet(collection.id, token_id, key));
+				}
+				None => {
+					stored_properties.remove(&key).map_err(<Error<T>>::from)?;
+
+					Self::deposit_event(Event::TokenPropertyDeleted(collection.id, token_id, key));
+				}
+			}
+
+			<PalletEvm<T>>::deposit_log(
+				CollectionHelpersEvents::TokenChanged {
+					collection_id: eth::collection_id_to_address(collection.id),
+					token_id: token_id.into(),
+				}
+				.to_log(T::ContractAddress::get()),
+			);
+		}
+
+		set_token_properties(stored_properties);
+
+		Ok(())
+	}
+
+	/// Sets or unsets the approval of a given operator.
+	///
+	/// The `operator` is allowed to transfer all token pieces of the `owner` on their behalf.
+	/// - `owner`: Token owner
+	/// - `operator`: Operator
+	/// - `approve`: Should operator status be granted or revoked?
+	pub fn set_allowance_for_all(
+		collection: &CollectionHandle<T>,
+		owner: &T::CrossAccountId,
+		operator: &T::CrossAccountId,
+		approve: bool,
+		set_allowance: impl FnOnce(),
+		log: evm_coder::ethereum::Log,
+	) -> DispatchResult {
+		if collection.permissions.access() == AccessMode::AllowList {
+			collection.check_allowlist(owner)?;
+			collection.check_allowlist(operator)?;
+		}
+
+		Self::ensure_correct_receiver(operator)?;
+
+		set_allowance();
+
+		<PalletEvm<T>>::deposit_log(log);
+		Self::deposit_event(Event::ApprovedForAll(
+			collection.id,
+			owner.clone(),
+			operator.clone(),
+			approve,
+		));
+		Ok(())
+	}
+
 	/// Set collection property.
 	///
 	/// * `collection` - Collection handler.
modifiedpallets/nonfungible/src/lib.rsdiffbeforeafterboth
--- a/pallets/nonfungible/src/lib.rs
+++ b/pallets/nonfungible/src/lib.rs
@@ -101,18 +101,18 @@
 };
 use up_data_structs::{
 	AccessMode, CollectionId, CollectionFlags, CustomDataLimit, TokenId, CreateCollectionData,
-	CreateNftExData, mapping::TokenAddressMapping, budget::Budget, Property, PropertyPermission,
-	PropertyKey, PropertyValue, PropertyKeyPermission, Properties, PropertyScope, TrySetProperty,
-	TokenChild, AuxPropertyValue, PropertiesPermissionMap,
+	CreateNftExData, mapping::TokenAddressMapping, budget::Budget, Property, PropertyKey,
+	PropertyValue, PropertyKeyPermission, Properties, PropertyScope, TrySetProperty, TokenChild,
+	AuxPropertyValue, PropertiesPermissionMap,
 };
 use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm};
 use pallet_common::{
 	Error as CommonError, Pallet as PalletCommon, Event as CommonEvent, CollectionHandle,
-	eth::collection_id_to_address, erc::CollectionHelpersEvents,
+	eth::collection_id_to_address,
 };
 use pallet_structure::{Pallet as PalletStructure, Error as StructureError};
 use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};
-use sp_core::{H160, Get};
+use sp_core::H160;
 use sp_runtime::{ArithmeticError, DispatchError, DispatchResult, TransactionOutcome};
 use sp_std::{vec::Vec, vec, collections::btree_map::BTreeMap};
 use core::ops::Deref;
@@ -578,10 +578,6 @@
 	}
 
 	/// A batch operation to add, edit or remove properties for a token.
-	/// It sets or removes a token's properties according to
-	/// `properties_updates` contents:
-	/// * sets a property under the <key> with the value provided `(<key>, Some(<value>))`
-	/// * removes a property under the <key> if the value is `None` `(<key>, None)`.
 	///
 	/// - `nesting_budget`: Limit for searching parents in-depth to check ownership.
 	/// - `is_token_create`: Indicates that method is called during token initialization.
@@ -603,97 +599,30 @@
 		is_token_create: bool,
 		nesting_budget: &dyn Budget,
 	) -> DispatchResult {
-		let mut collection_admin_status = None;
-		let mut token_owner_result = None;
-
-		let mut is_collection_admin =
-			|| *collection_admin_status.get_or_insert_with(|| collection.is_owner_or_admin(sender));
+		let is_token_owner = || {
+			let is_owned = <PalletStructure<T>>::check_indirectly_owned(
+				sender.clone(),
+				collection.id,
+				token_id,
+				None,
+				nesting_budget,
+			)?;
 
-		let mut is_token_owner = || {
-			*token_owner_result.get_or_insert_with(|| -> Result<bool, DispatchError> {
-				let is_owned = <PalletStructure<T>>::check_indirectly_owned(
-					sender.clone(),
-					collection.id,
-					token_id,
-					None,
-					nesting_budget,
-				)?;
-
-				Ok(is_owned)
-			})
+			Ok(is_owned)
 		};
-
-		let mut stored_properties = <TokenProperties<T>>::get((collection.id, token_id));
-		let permissions = <PalletCommon<T>>::property_permissions(collection.id);
-
-		for (key, value) in properties_updates {
-			let permission = permissions
-				.get(&key)
-				.cloned()
-				.unwrap_or_else(PropertyPermission::none);
-
-			let is_property_exists = stored_properties.get(&key).is_some();
-
-			match permission {
-				PropertyPermission { mutable: false, .. } if is_property_exists => {
-					return Err(<CommonError<T>>::NoPermission.into());
-				}
-
-				PropertyPermission {
-					collection_admin,
-					token_owner,
-					..
-				} => {
-					//TODO: investigate threats during public minting.
-					if is_token_create && (collection_admin || token_owner) && value.is_some() {
-						// Pass
-					} else if collection_admin && is_collection_admin() {
-						// Pass
-					} else if token_owner && is_token_owner()? {
-						// Pass
-					} else {
-						fail!(<CommonError<T>>::NoPermission);
-					}
-				}
-			}
 
-			match value {
-				Some(value) => {
-					stored_properties
-						.try_set(key.clone(), value)
-						.map_err(<CommonError<T>>::from)?;
-
-					<PalletCommon<T>>::deposit_event(CommonEvent::TokenPropertySet(
-						collection.id,
-						token_id,
-						key,
-					));
-				}
-				None => {
-					stored_properties
-						.remove(&key)
-						.map_err(<CommonError<T>>::from)?;
-
-					<PalletCommon<T>>::deposit_event(CommonEvent::TokenPropertyDeleted(
-						collection.id,
-						token_id,
-						key,
-					));
-				}
-			}
-
-			<PalletEvm<T>>::deposit_log(
-				CollectionHelpersEvents::TokenChanged {
-					collection_id: collection_id_to_address(collection.id),
-					token_id: token_id.into(),
-				}
-				.to_log(T::ContractAddress::get()),
-			);
-		}
-
-		<TokenProperties<T>>::set((collection.id, token_id), stored_properties);
+		let stored_properties = <TokenProperties<T>>::get((collection.id, token_id));
 
-		Ok(())
+		<PalletCommon<T>>::modify_token_properties(
+			collection,
+			sender,
+			token_id,
+			properties_updates,
+			is_token_create,
+			stored_properties,
+			is_token_owner,
+			|properties| <TokenProperties<T>>::set((collection.id, token_id), properties),
+		)
 	}
 
 	/// Batch operation to add or edit properties for the token
@@ -1418,31 +1347,19 @@
 		operator: &T::CrossAccountId,
 		approve: bool,
 	) -> DispatchResult {
-		if collection.permissions.access() == AccessMode::AllowList {
-			collection.check_allowlist(owner)?;
-			collection.check_allowlist(operator)?;
-		}
-
-		<PalletCommon<T>>::ensure_correct_receiver(operator)?;
-
-		// =========
-
-		<CollectionAllowance<T>>::insert((collection.id, owner, operator), approve);
-		<PalletEvm<T>>::deposit_log(
+		<PalletCommon<T>>::set_allowance_for_all(
+			collection,
+			owner,
+			operator,
+			approve,
+			|| <CollectionAllowance<T>>::insert((collection.id, owner, operator), approve),
 			ERC721Events::ApprovalForAll {
 				owner: *owner.as_eth(),
 				operator: *operator.as_eth(),
 				approved: approve,
 			}
 			.to_log(collection_id_to_address(collection.id)),
-		);
-		<PalletCommon<T>>::deposit_event(CommonEvent::ApprovedForAll(
-			collection.id,
-			owner.clone(),
-			operator.clone(),
-			approve,
-		));
-		Ok(())
+		)
 	}
 
 	/// Tells whether the given `owner` approves the `operator`.
modifiedpallets/refungible/src/lib.rsdiffbeforeafterboth
9292
93use core::ops::Deref;93use core::ops::Deref;
94use evm_coder::ToLog;94use evm_coder::ToLog;
95use frame_support::{ensure, fail, storage::with_transaction, transactional};95use frame_support::{ensure, storage::with_transaction, transactional};
96use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm};96use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm};
97use pallet_evm_coder_substrate::WithRecorder;97use pallet_evm_coder_substrate::WithRecorder;
98use pallet_common::{98use pallet_common::{
99 CommonCollectionOperations, Error as CommonError, eth::collection_id_to_address,99 CommonCollectionOperations, Error as CommonError, eth::collection_id_to_address,
100 Event as CommonEvent, Pallet as PalletCommon, erc::CollectionHelpersEvents,100 Event as CommonEvent, Pallet as PalletCommon,
101};101};
102use pallet_structure::Pallet as PalletStructure;102use pallet_structure::Pallet as PalletStructure;
103use sp_core::{Get, H160};103use sp_core::H160;
104use sp_runtime::{ArithmeticError, DispatchError, DispatchResult, TransactionOutcome};104use sp_runtime::{ArithmeticError, DispatchError, DispatchResult, TransactionOutcome};
105use sp_std::{vec::Vec, vec, collections::btree_map::BTreeMap};105use sp_std::{vec::Vec, vec, collections::btree_map::BTreeMap};
106use up_data_structs::{106use up_data_structs::{
107 AccessMode, budget::Budget, CollectionId, CollectionFlags, CreateCollectionData,107 AccessMode, budget::Budget, CollectionId, CollectionFlags, CreateCollectionData,
108 mapping::TokenAddressMapping, MAX_REFUNGIBLE_PIECES, Property, PropertyKey,108 mapping::TokenAddressMapping, MAX_REFUNGIBLE_PIECES, Property, PropertyKey,
109 PropertyKeyPermission, PropertyPermission, PropertyScope, PropertyValue, TokenId,109 PropertyKeyPermission, PropertyScope, PropertyValue, TokenId, TrySetProperty,
110 TrySetProperty, PropertiesPermissionMap, CreateRefungibleExMultipleOwners, TokenOwnerError,110 PropertiesPermissionMap, CreateRefungibleExMultipleOwners, TokenOwnerError,
111};111};
112112
245 pub type CollectionAllowance<T: Config> = StorageNMap<245 pub type CollectionAllowance<T: Config> = StorageNMap<
246 Key = (246 Key = (
247 Key<Twox64Concat, CollectionId>,247 Key<Twox64Concat, CollectionId>,
248 Key<Blake2_128Concat, T::CrossAccountId>,248 Key<Blake2_128Concat, T::CrossAccountId>, // Owner
249 Key<Blake2_128Concat, T::CrossAccountId>,249 Key<Blake2_128Concat, T::CrossAccountId>, // Operator
250 ),250 ),
251 Value = bool,251 Value = bool,
252 QueryKind = ValueQuery,252 QueryKind = ValueQuery,
541 is_token_create: bool,541 is_token_create: bool,
542 nesting_budget: &dyn Budget,542 nesting_budget: &dyn Budget,
543 ) -> DispatchResult {543 ) -> DispatchResult {
544 let is_collection_admin = || collection.is_owner_or_admin(sender);
545 let is_token_owner = || -> Result<bool, DispatchError> {544 let is_token_owner = || -> Result<bool, DispatchError> {
546 let balance = collection.balance(sender.clone(), token_id);545 let balance = collection.balance(sender.clone(), token_id);
547 let total_pieces: u128 =546 let total_pieces: u128 =
561 Ok(is_bundle_owner)560 Ok(is_bundle_owner)
562 };561 };
563562
564 let mut stored_properties = <TokenProperties<T>>::get((collection.id, token_id));563 let stored_properties = <TokenProperties<T>>::get((collection.id, token_id));
564
565 let permissions = <PalletCommon<T>>::property_permissions(collection.id);565 <PalletCommon<T>>::modify_token_properties(
566
567 for (key, value) in properties_updates {566 collection,
568 let permission = permissions
569 .get(&key)
570 .cloned()
571 .unwrap_or_else(PropertyPermission::none);
572
573 let is_property_exists = stored_properties.get(&key).is_some();
574
575 match permission {
576 PropertyPermission { mutable: false, .. } if is_property_exists => {567 sender,
577 return Err(<CommonError<T>>::NoPermission.into());
578 }
579
580 PropertyPermission {
581 collection_admin,568 token_id,
582 token_owner,569 properties_updates,
583 ..
584 } => {
585 //TODO: investigate threats during public minting.
586 let is_token_create =570 is_token_create,
587 is_token_create && (collection_admin || token_owner) && value.is_some();
588 if !(is_token_create
589 || (collection_admin && is_collection_admin())
590 || (token_owner && is_token_owner()?))
591 {
592 fail!(<CommonError<T>>::NoPermission);
593 }
594 }
595 }
596
597 match value {
598 Some(value) => {
599 stored_properties
600 .try_set(key.clone(), value)
601 .map_err(<CommonError<T>>::from)?;
602
603 <PalletCommon<T>>::deposit_event(CommonEvent::TokenPropertySet(
604 collection.id,
605 token_id,
606 key,
607 ));
608 }
609 None => {
610 stored_properties571 stored_properties,
611 .remove(&key)
612 .map_err(<CommonError<T>>::from)?;
613
614 <PalletCommon<T>>::deposit_event(CommonEvent::TokenPropertyDeleted(
615 collection.id,
616 token_id,
617 key,
618 ));
619 }
620 }
621
622 <PalletEvm<T>>::deposit_log(
623 CollectionHelpersEvents::TokenChanged {
624 collection_id: collection_id_to_address(collection.id),
625 token_id: token_id.into(),
626 }
627 .to_log(T::ContractAddress::get()),572 is_token_owner,
628 );
629 }
630
631 <TokenProperties<T>>::set((collection.id, token_id), stored_properties);573 |properties| <TokenProperties<T>>::set((collection.id, token_id), properties),
632574 )
633 Ok(())
634 }575 }
635576
636 pub fn set_token_properties(577 pub fn set_token_properties(
1465 operator: &T::CrossAccountId,1406 operator: &T::CrossAccountId,
1466 approve: bool,1407 approve: bool,
1467 ) -> DispatchResult {1408 ) -> DispatchResult {
1468 if collection.permissions.access() == AccessMode::AllowList {1409 <PalletCommon<T>>::set_allowance_for_all(
1469 collection.check_allowlist(owner)?;1410 collection,
1470 collection.check_allowlist(operator)?;1411 owner,
1471 }1412 operator,
14721413 approve,
1473 <PalletCommon<T>>::ensure_correct_receiver(operator)?;
1474
1475 // =========
1476
1477 <CollectionAllowance<T>>::insert((collection.id, owner, operator), approve);1414 || <CollectionAllowance<T>>::insert((collection.id, owner, operator), approve),
1478 <PalletEvm<T>>::deposit_log(
1479 ERC721Events::ApprovalForAll {1415 ERC721Events::ApprovalForAll {
1480 owner: *owner.as_eth(),1416 owner: *owner.as_eth(),
1481 operator: *operator.as_eth(),1417 operator: *operator.as_eth(),
1482 approved: approve,1418 approved: approve,
1483 }1419 }
1484 .to_log(collection_id_to_address(collection.id)),1420 .to_log(collection_id_to_address(collection.id)),
1485 );1421 )
1486 <PalletCommon<T>>::deposit_event(CommonEvent::ApprovedForAll(
1487 collection.id,
1488 owner.clone(),
1489 operator.clone(),
1490 approve,
1491 ));
1492 Ok(())
1493 }1422 }
14941423
1495 /// Tells whether the given `owner` approves the `operator`.1424 /// Tells whether the given `owner` approves the `operator`.