difftreelog
misk: generalize 2 methods
in: master
3 files changed
pallets/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.
pallets/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`.
pallets/refungible/src/lib.rsdiffbeforeafterboth929293use 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};112112245 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>, // Owner249 Key<Blake2_128Concat, T::CrossAccountId>,249 Key<Blake2_128Concat, T::CrossAccountId>, // Operator250 ),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 };563562564 let mut stored_properties = <TokenProperties<T>>::get((collection.id, token_id));563 let stored_properties = <TokenProperties<T>>::get((collection.id, token_id));564565 let permissions = <PalletCommon<T>>::property_permissions(collection.id);565 <PalletCommon<T>>::modify_token_properties(566567 for (key, value) in properties_updates {566 collection,568 let permission = permissions569 .get(&key)570 .cloned()571 .unwrap_or_else(PropertyPermission::none);572573 let is_property_exists = stored_properties.get(&key).is_some();574575 match permission {576 PropertyPermission { mutable: false, .. } if is_property_exists => {567 sender,577 return Err(<CommonError<T>>::NoPermission.into());578 }579580 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_create589 || (collection_admin && is_collection_admin())590 || (token_owner && is_token_owner()?))591 {592 fail!(<CommonError<T>>::NoPermission);593 }594 }595 }596597 match value {598 Some(value) => {599 stored_properties600 .try_set(key.clone(), value)601 .map_err(<CommonError<T>>::from)?;602603 <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)?;613614 <PalletCommon<T>>::deposit_event(CommonEvent::TokenPropertyDeleted(615 collection.id,616 token_id,617 key,618 ));619 }620 }621622 <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 }630631 <TokenProperties<T>>::set((collection.id, token_id), stored_properties);573 |properties| <TokenProperties<T>>::set((collection.id, token_id), properties),632574 )633 Ok(())634 }575 }635576636 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)?;14741475 // =========14761477 <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 }149414231495 /// Tells whether the given `owner` approves the `operator`.1424 /// Tells whether the given `owner` approves the `operator`.