difftreelog
refactor use type-safe propertywriter to set/delete properties
in: master
12 files changed
pallets/balances-adapter/src/common.rsdiffbeforeafterboth--- a/pallets/balances-adapter/src/common.rs
+++ b/pallets/balances-adapter/src/common.rs
@@ -172,6 +172,20 @@
fail!(<pallet_common::Error<T>>::UnsupportedOperation);
}
+ fn get_token_properties_map(&self, _token_id: TokenId) -> up_data_structs::TokenProperties {
+ // No token properties are defined on fungibles
+ up_data_structs::TokenProperties::new()
+ }
+
+ fn set_token_properties_map(&self, _token_id: TokenId, _map: up_data_structs::TokenProperties) {
+ // No token properties are defined on fungibles
+ }
+
+ fn properties_exist(&self, _token: TokenId) -> bool {
+ // No token properties are defined on fungibles
+ false
+ }
+
fn set_token_property_permissions(
&self,
_sender: &<T>::CrossAccountId,
@@ -277,6 +291,15 @@
Err(up_data_structs::TokenOwnerError::MultipleOwners)
}
+ fn check_token_indirect_owner(
+ &self,
+ _token: TokenId,
+ _maybe_owner: &<T>::CrossAccountId,
+ _nesting_budget: &dyn up_data_structs::budget::Budget,
+ ) -> Result<bool, frame_support::sp_runtime::DispatchError> {
+ Ok(false)
+ }
+
fn token_owners(&self, _token: TokenId) -> Vec<<T>::CrossAccountId> {
vec![]
}
pallets/common/src/benchmarking.rsdiffbeforeafterboth--- a/pallets/common/src/benchmarking.rs
+++ b/pallets/common/src/benchmarking.rs
@@ -22,8 +22,9 @@
use frame_benchmarking::{benchmarks, account};
use up_data_structs::{
CollectionMode, CreateCollectionData, CollectionId, Property, PropertyKey, PropertyValue,
- CollectionPermissions, NestingPermissions, AccessMode, MAX_COLLECTION_NAME_LENGTH,
- MAX_COLLECTION_DESCRIPTION_LENGTH, MAX_TOKEN_PREFIX_LENGTH, MAX_PROPERTIES_PER_ITEM,
+ CollectionPermissions, NestingPermissions, AccessMode, PropertiesPermissionMap,
+ MAX_COLLECTION_NAME_LENGTH, MAX_COLLECTION_DESCRIPTION_LENGTH, MAX_TOKEN_PREFIX_LENGTH,
+ MAX_PROPERTIES_PER_ITEM,
};
use frame_support::{
traits::{Get, fungible::Balanced, Imbalance, tokens::Precision},
@@ -123,6 +124,16 @@
)
}
+pub fn load_is_admin_and_property_permissions<T: Config>(
+ collection: &CollectionHandle<T>,
+ sender: &T::CrossAccountId,
+) -> (bool, PropertiesPermissionMap) {
+ (
+ collection.is_owner_or_admin(sender),
+ <Pallet<T>>::property_permissions(collection.id),
+ )
+}
+
/// Helper macros, which handles all benchmarking preparation in semi-declarative way
///
/// `name` is a substrate account
@@ -215,4 +226,12 @@
assert_eq!(collection_handle.permissions.access(), AccessMode::AllowList);
}: {collection_handle.check_allowlist(&sender)?;}
+
+ init_token_properties_common {
+ bench_init!{
+ owner: sub; collection: collection(owner);
+ sender: sub;
+ sender: cross_from_sub(sender);
+ };
+ }: {load_is_admin_and_property_permissions(&collection, &sender);}
}
pallets/common/src/lib.rsdiffbeforeafterboth--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -56,6 +56,7 @@
use core::{
ops::{Deref, DerefMut},
slice::from_ref,
+ marker::PhantomData,
};
use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};
use sp_std::vec::Vec;
@@ -97,6 +98,9 @@
pub mod helpers;
#[allow(missing_docs)]
pub mod weights;
+
+use weights::WeightInfo;
+
/// Weight info.
pub type SelfWeightOf<T> = <T as Config>::WeightInfo;
@@ -864,19 +868,7 @@
QueryKind = OptionQuery,
>;
}
-
-/// Represents the change mode for the token property.
-pub enum SetPropertyMode {
- /// The token already exists.
- ExistingToken,
- /// New token.
- NewToken {
- /// The creator of the token is the recipient.
- mint_target_is_sender: bool,
- },
-}
-
/// Value representation with delayed initialization time.
pub struct LazyValue<T, F: FnOnce() -> T> {
value: Option<T>,
@@ -892,19 +884,33 @@
}
}
- /// Get the value. If it call furst time the value will be initialized.
+ /// Get the value. If it is called the first time, the value will be initialized.
pub fn value(&mut self) -> &T {
- if self.value.is_none() {
- self.value = Some(self.f.take().unwrap()())
- }
+ self.compute_value_if_not_already();
+ self.value.as_ref().unwrap()
+ }
- self.value.as_ref().unwrap()
+ /// Get the value. If it is called the first time, the value will be initialized.
+ pub fn value_mut(&mut self) -> &mut T {
+ self.compute_value_if_not_already();
+ self.value.as_mut().unwrap()
}
- /// Is value initialized.
+ fn into_inner(mut self) -> T {
+ self.compute_value_if_not_already();
+ self.value.unwrap()
+ }
+
+ /// Is value initialized?
pub fn has_value(&self) -> bool {
self.value.is_some()
}
+
+ fn compute_value_if_not_already(&mut self) {
+ if self.value.is_none() {
+ self.value = Some(self.f.take().unwrap()())
+ }
+ }
}
fn check_token_permissions<T, FCA, FTO, FTE>(
@@ -926,10 +932,19 @@
fail!(<Error<T>>::NoPermission);
}
- let token_certainly_exist = is_token_owner.has_value() && (*is_token_owner.value())?;
- if !token_certainly_exist && !is_token_exist.value() {
- fail!(<Error<T>>::TokenNotFound);
+ let token_exist_due_to_owner_check_success =
+ is_token_owner.has_value() && (*is_token_owner.value())?;
+
+ // If the token owner check has occurred and succeeded,
+ // we know the token exists (otherwise, the owner check must fail).
+ if !token_exist_due_to_owner_check_success {
+ // If the token owner check didn't occur,
+ // we must check the token's existence ourselves.
+ if !is_token_exist.value() {
+ fail!(<Error<T>>::TokenNotFound);
+ }
}
+
Ok(())
}
@@ -1312,92 +1327,6 @@
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)`.
- ///
- /// 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.
- #[allow(clippy::too_many_arguments)]
- pub fn modify_token_properties<FTO, FTE>(
- collection: &CollectionHandle<T>,
- sender: &T::CrossAccountId,
- token_id: TokenId,
- is_token_exist: &mut LazyValue<bool, FTE>,
- properties_updates: impl Iterator<Item = (PropertyKey, Option<PropertyValue>)>,
- mut stored_properties: TokenProperties,
- is_token_owner: &mut LazyValue<Result<bool, DispatchError>, FTO>,
- set_token_properties: impl FnOnce(TokenProperties),
- log: evm_coder::ethereum::Log,
- ) -> DispatchResult
- where
- FTO: FnOnce() -> Result<bool, DispatchError>,
- FTE: FnOnce() -> bool,
- {
- let mut is_collection_admin = LazyValue::new(|| collection.is_owner_or_admin(sender));
- let mut permissions = LazyValue::new(|| Self::property_permissions(collection.id));
-
- let mut changed = false;
- for (key, value) in properties_updates {
- let permission = permissions
- .value()
- .get(&key)
- .cloned()
- .unwrap_or_else(PropertyPermission::none);
-
- let property_exists = stored_properties.get(&key).is_some();
-
- match permission {
- PropertyPermission { mutable: false, .. } if property_exists => {
- return Err(<Error<T>>::NoPermission.into());
- }
-
- PropertyPermission {
- collection_admin,
- token_owner,
- ..
- } => check_token_permissions::<T, _, FTO, FTE>(
- collection_admin,
- token_owner,
- &mut is_collection_admin,
- is_token_owner,
- is_token_exist,
- )?,
- }
-
- 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));
- }
- }
-
- changed = true;
- }
-
- if changed {
- <PalletEvm<T>>::deposit_log(log);
- 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.
@@ -2166,6 +2095,22 @@
budget: &dyn Budget,
) -> DispatchResultWithPostInfo;
+ /// Get token properties raw map.
+ ///
+ /// * `token_id` - The token which properties are needed.
+ fn get_token_properties_map(&self, token_id: TokenId) -> TokenProperties;
+
+ /// Set token properties raw map.
+ ///
+ /// * `token_id` - The token for which the properties are being set.
+ /// * `map` - The raw map containing the token's properties.
+ fn set_token_properties_map(&self, token_id: TokenId, map: TokenProperties);
+
+ /// Whether the given token has properties.
+ ///
+ /// * `token_id` - The token in question.
+ fn properties_exist(&self, token: TokenId) -> bool;
+
/// Set token property permissions.
///
/// * `sender` - Must be either the owner of the token or its admin.
@@ -2309,6 +2254,18 @@
/// * `token` - The token for which you need to find out the owner.
fn token_owner(&self, token: TokenId) -> Result<T::CrossAccountId, TokenOwnerError>;
+ /// Checks if the `maybe_owner` is the indirect owner of the `token`.
+ ///
+ /// * `token` - Id token to check.
+ /// * `maybe_owner` - The account to check.
+ /// * `nesting_budget` - A budget that can be spent on nesting tokens.
+ fn check_token_indirect_owner(
+ &self,
+ token: TokenId,
+ maybe_owner: &T::CrossAccountId,
+ nesting_budget: &dyn Budget,
+ ) -> Result<bool, DispatchError>;
+
/// Returns 10 tokens owners in no particular order.
///
/// * `token` - The token for which you need to find out the owners.
@@ -2420,6 +2377,348 @@
}
}
+/// A marker structure that enables the writer implementation
+/// to provide the interface to write properties to **newly created** tokens.
+pub struct NewTokenPropertyWriter;
+
+/// A marker structure that enables the writer implementation
+/// to provide the interface to write properties to **already existing** tokens.
+pub struct ExistingTokenPropertyWriter;
+
+/// The type-safe interface for writing properties (setting or deleting) to tokens.
+/// It has two distinct implementations for newly created tokens and existing ones.
+///
+/// This type utilizes the lazy evaluation to avoid repeating the computation
+/// of several performance-heavy or PoV-heavy tasks,
+/// such as checking the indirect ownership or reading the token property permissions.
+pub struct PropertyWriter<
+ 'a,
+ T,
+ Handle,
+ WriterVariant,
+ FIsAdmin,
+ FPropertyPermissions,
+ FCheckTokenExist,
+ FGetProperties,
+> where
+ T: Config,
+ FIsAdmin: FnOnce() -> bool,
+ FPropertyPermissions: FnOnce() -> PropertiesPermissionMap,
+{
+ collection: &'a Handle,
+ is_collection_admin: LazyValue<bool, FIsAdmin>,
+ property_permissions: LazyValue<PropertiesPermissionMap, FPropertyPermissions>,
+ check_token_exist: FCheckTokenExist,
+ get_properties: FGetProperties,
+ _phantom: PhantomData<(T, WriterVariant)>,
+}
+
+impl<'a, T, Handle, FIsAdmin, FPropertyPermissions, FCheckTokenExist, FGetProperties>
+ PropertyWriter<
+ 'a,
+ T,
+ Handle,
+ NewTokenPropertyWriter,
+ FIsAdmin,
+ FPropertyPermissions,
+ FCheckTokenExist,
+ FGetProperties,
+ > where
+ T: Config,
+ Handle: CommonCollectionOperations<T> + Deref<Target = CollectionHandle<T>>,
+ FIsAdmin: FnOnce() -> bool,
+ FPropertyPermissions: FnOnce() -> PropertiesPermissionMap,
+ FCheckTokenExist: Copy + FnOnce(TokenId) -> bool,
+ FGetProperties: Copy + FnOnce(TokenId) -> TokenProperties,
+{
+ /// A function to write properties to a **newly created** token.
+ pub fn write_token_properties(
+ &mut self,
+ mint_target_is_sender: bool,
+ token_id: TokenId,
+ properties_updates: impl Iterator<Item = Property>,
+ log: evm_coder::ethereum::Log,
+ ) -> DispatchResult {
+ self.internal_write_token_properties(
+ token_id,
+ properties_updates.map(|p| (p.key, Some(p.value))),
+ |_| Ok(mint_target_is_sender),
+ log,
+ )
+ }
+}
+
+impl<'a, T, Handle, FIsAdmin, FPropertyPermissions, FCheckTokenExist, FGetProperties>
+ PropertyWriter<
+ 'a,
+ T,
+ Handle,
+ ExistingTokenPropertyWriter,
+ FIsAdmin,
+ FPropertyPermissions,
+ FCheckTokenExist,
+ FGetProperties,
+ > where
+ T: Config,
+ Handle: CommonCollectionOperations<T> + Deref<Target = CollectionHandle<T>>,
+ FIsAdmin: FnOnce() -> bool,
+ FPropertyPermissions: FnOnce() -> PropertiesPermissionMap,
+ FCheckTokenExist: Copy + FnOnce(TokenId) -> bool,
+ FGetProperties: Copy + FnOnce(TokenId) -> TokenProperties,
+{
+ /// A function to write properties to an **already existing** token.
+ pub fn write_token_properties(
+ &mut self,
+ sender: &T::CrossAccountId,
+ token_id: TokenId,
+ properties_updates: impl Iterator<Item = (PropertyKey, Option<PropertyValue>)>,
+ nesting_budget: &dyn Budget,
+ log: evm_coder::ethereum::Log,
+ ) -> DispatchResult {
+ self.internal_write_token_properties(
+ token_id,
+ properties_updates,
+ |collection| collection.check_token_indirect_owner(token_id, sender, nesting_budget),
+ log,
+ )
+ }
+}
+
+impl<
+ 'a,
+ T,
+ Handle,
+ WriterVariant,
+ FIsAdmin,
+ FPropertyPermissions,
+ FCheckTokenExist,
+ FGetProperties,
+ >
+ PropertyWriter<
+ 'a,
+ T,
+ Handle,
+ WriterVariant,
+ FIsAdmin,
+ FPropertyPermissions,
+ FCheckTokenExist,
+ FGetProperties,
+ > where
+ T: Config,
+ Handle: CommonCollectionOperations<T> + Deref<Target = CollectionHandle<T>>,
+ FIsAdmin: FnOnce() -> bool,
+ FPropertyPermissions: FnOnce() -> PropertiesPermissionMap,
+ FCheckTokenExist: Copy + FnOnce(TokenId) -> bool,
+ FGetProperties: Copy + FnOnce(TokenId) -> TokenProperties,
+{
+ fn internal_write_token_properties<FCheckTokenOwner>(
+ &mut self,
+ token_id: TokenId,
+ properties_updates: impl Iterator<Item = (PropertyKey, Option<PropertyValue>)>,
+ check_token_owner: FCheckTokenOwner,
+ log: evm_coder::ethereum::Log,
+ ) -> DispatchResult
+ where
+ FCheckTokenOwner: FnOnce(&Handle) -> Result<bool, DispatchError>,
+ {
+ let get_properties = self.get_properties;
+ let mut stored_properties = LazyValue::new(move || get_properties(token_id));
+
+ let mut is_token_owner = LazyValue::new(|| check_token_owner(self.collection));
+
+ let check_token_exist = self.check_token_exist;
+ let mut is_token_exist = LazyValue::new(move || check_token_exist(token_id));
+
+ for (key, value) in properties_updates {
+ let permission = self
+ .property_permissions
+ .value()
+ .get(&key)
+ .cloned()
+ .unwrap_or_else(PropertyPermission::none);
+
+ match permission {
+ PropertyPermission { mutable: false, .. }
+ if stored_properties.value().get(&key).is_some() =>
+ {
+ return Err(<Error<T>>::NoPermission.into());
+ }
+
+ PropertyPermission {
+ collection_admin,
+ token_owner,
+ ..
+ } => check_token_permissions::<T, _, _, _>(
+ collection_admin,
+ token_owner,
+ &mut self.is_collection_admin,
+ &mut is_token_owner,
+ &mut is_token_exist,
+ )?,
+ }
+
+ match value {
+ Some(value) => {
+ stored_properties
+ .value_mut()
+ .try_set(key.clone(), value)
+ .map_err(<Error<T>>::from)?;
+
+ <Pallet<T>>::deposit_event(Event::TokenPropertySet(
+ self.collection.id,
+ token_id,
+ key,
+ ));
+ }
+ None => {
+ stored_properties
+ .value_mut()
+ .remove(&key)
+ .map_err(<Error<T>>::from)?;
+
+ <Pallet<T>>::deposit_event(Event::TokenPropertyDeleted(
+ self.collection.id,
+ token_id,
+ key,
+ ));
+ }
+ }
+ }
+
+ let properties_changed = stored_properties.has_value();
+ if properties_changed {
+ <PalletEvm<T>>::deposit_log(log);
+
+ self.collection
+ .set_token_properties_map(token_id, stored_properties.into_inner());
+ }
+
+ Ok(())
+ }
+}
+
+/// Create a [`PropertyWriter`] for newly created tokens.
+pub fn property_writer_for_new_token<'a, T, Handle>(
+ collection: &'a Handle,
+ sender: &'a T::CrossAccountId,
+) -> PropertyWriter<
+ 'a,
+ T,
+ Handle,
+ NewTokenPropertyWriter,
+ impl FnOnce() -> bool + 'a,
+ impl FnOnce() -> PropertiesPermissionMap + 'a,
+ impl Copy + FnOnce(TokenId) -> bool + 'a,
+ impl Copy + FnOnce(TokenId) -> TokenProperties + 'a,
+>
+where
+ T: Config,
+ Handle: CommonCollectionOperations<T> + Deref<Target = CollectionHandle<T>>,
+{
+ PropertyWriter {
+ collection,
+ is_collection_admin: LazyValue::new(|| collection.is_owner_or_admin(sender)),
+ property_permissions: LazyValue::new(|| <Pallet<T>>::property_permissions(collection.id)),
+ check_token_exist: |token_id| {
+ debug_assert!(collection.token_exists(token_id));
+ true
+ },
+ get_properties: |token_id| {
+ debug_assert!(!collection.properties_exist(token_id));
+ TokenProperties::new()
+ },
+ _phantom: PhantomData,
+ }
+}
+
+#[cfg(feature = "runtime-benchmarks")]
+/// Create a `PropertyWriter` with preloaded `is_collection_admin` and `property_permissions.
+/// Also:
+/// * it will return `true` for the token ownership check.
+/// * it will return empty stored properties without reading them from the storage.
+pub fn collection_info_loaded_property_writer<T, Handle>(
+ collection: &Handle,
+ is_collection_admin: bool,
+ property_permissions: PropertiesPermissionMap,
+) -> PropertyWriter<
+ T,
+ Handle,
+ NewTokenPropertyWriter,
+ impl FnOnce() -> bool,
+ impl FnOnce() -> PropertiesPermissionMap,
+ impl Copy + FnOnce(TokenId) -> bool,
+ impl Copy + FnOnce(TokenId) -> TokenProperties,
+>
+where
+ T: Config,
+ Handle: CommonCollectionOperations<T> + Deref<Target = CollectionHandle<T>>,
+{
+ PropertyWriter {
+ collection,
+ is_collection_admin: LazyValue::new(move || is_collection_admin),
+ property_permissions: LazyValue::new(move || property_permissions),
+ check_token_exist: |_token_id| true,
+ get_properties: |_token_id| TokenProperties::new(),
+ _phantom: PhantomData,
+ }
+}
+
+/// Create a [`PropertyWriter`] for already existing tokens.
+pub fn property_writer_for_existing_token<'a, T, Handle>(
+ collection: &'a Handle,
+ sender: &'a T::CrossAccountId,
+) -> PropertyWriter<
+ 'a,
+ T,
+ Handle,
+ ExistingTokenPropertyWriter,
+ impl FnOnce() -> bool + 'a,
+ impl FnOnce() -> PropertiesPermissionMap + 'a,
+ impl Copy + FnOnce(TokenId) -> bool + 'a,
+ impl Copy + FnOnce(TokenId) -> TokenProperties + 'a,
+>
+where
+ T: Config,
+ Handle: CommonCollectionOperations<T> + Deref<Target = CollectionHandle<T>>,
+{
+ PropertyWriter {
+ collection,
+ is_collection_admin: LazyValue::new(|| collection.is_owner_or_admin(sender)),
+ property_permissions: LazyValue::new(|| <Pallet<T>>::property_permissions(collection.id)),
+ check_token_exist: |token_id| collection.token_exists(token_id),
+ get_properties: |token_id| collection.get_token_properties_map(token_id),
+ _phantom: PhantomData,
+ }
+}
+
+/// Computes the weight delta for newly created tokens with properties.
+/// * `properties_nums` - The properties num of each created token.
+/// * `init_token_properties` - The function to obtain the weight from a token's properties num.
+pub fn init_token_properties_delta<T: Config, I: Fn(u32) -> Weight>(
+ properties_nums: impl Iterator<Item = u32>,
+ init_token_properties: I,
+) -> Weight {
+ let mut delta = properties_nums
+ .filter_map(|properties_num| {
+ if properties_num > 0 {
+ Some(init_token_properties(properties_num))
+ } else {
+ None
+ }
+ })
+ .fold(Weight::zero(), |a, b| a.saturating_add(b));
+
+ // If at least once the `init_token_properties` was called,
+ // it means at least one newly created token has properties.
+ // Becuase of that, some common collection data also was loaded and we need to add this weight.
+ // However, these common data was loaded only once which is guaranteed by the `PropertyWriter`.
+ if !delta.is_zero() {
+ delta = delta.saturating_add(<SelfWeightOf<T>>::init_token_properties_common())
+ }
+
+ delta
+}
+
#[cfg(any(feature = "tests", test))]
#[allow(missing_docs)]
pub mod tests {
pallets/fungible/src/common.rsdiffbeforeafterboth--- a/pallets/fungible/src/common.rs
+++ b/pallets/fungible/src/common.rs
@@ -25,7 +25,7 @@
weights::WeightInfo as _, SelfWeightOf as PalletCommonWeightOf,
};
use pallet_structure::Error as StructureError;
-use sp_runtime::ArithmeticError;
+use sp_runtime::{ArithmeticError, DispatchError};
use sp_std::{vec::Vec, vec};
use up_data_structs::{Property, PropertyKey, PropertyValue, PropertyKeyPermission};
@@ -364,6 +364,20 @@
fail!(<Error<T>>::SettingPropertiesNotAllowed)
}
+ fn get_token_properties_map(&self, _token_id: TokenId) -> up_data_structs::TokenProperties {
+ // No token properties are defined on fungibles
+ up_data_structs::TokenProperties::new()
+ }
+
+ fn set_token_properties_map(&self, _token_id: TokenId, _map: up_data_structs::TokenProperties) {
+ // No token properties are defined on fungibles
+ }
+
+ fn properties_exist(&self, _token: TokenId) -> bool {
+ // No token properties are defined on fungibles
+ false
+ }
+
fn check_nesting(
&self,
_sender: <T>::CrossAccountId,
@@ -402,6 +416,15 @@
Err(TokenOwnerError::MultipleOwners)
}
+ fn check_token_indirect_owner(
+ &self,
+ _token: TokenId,
+ _maybe_owner: &T::CrossAccountId,
+ _nesting_budget: &dyn Budget,
+ ) -> Result<bool, DispatchError> {
+ Ok(false)
+ }
+
/// Returns 10 tokens owners in no particular order.
fn token_owners(&self, token: TokenId) -> Vec<T::CrossAccountId> {
<Pallet<T>>::token_owners(self.id, token).unwrap_or_default()
pallets/nonfungible/src/benchmarking.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/benchmarking.rs
+++ b/pallets/nonfungible/src/benchmarking.rs
@@ -20,7 +20,9 @@
use frame_benchmarking::{benchmarks, account};
use pallet_common::{
bench_init,
- benchmarking::{create_collection_raw, property_key, property_value},
+ benchmarking::{
+ create_collection_raw, property_key, property_value, load_is_admin_and_property_permissions,
+ },
CommonCollectionOperations,
};
use sp_std::prelude::*;
@@ -198,14 +200,15 @@
value: property_value(),
}).collect::<Vec<_>>();
let item = create_max_item(&collection, &owner, owner.clone())?;
- }: {<Pallet<T>>::set_token_properties(&collection, &owner, item, props.into_iter(), SetPropertyMode::ExistingToken, &Unlimited)?}
+ }: {<Pallet<T>>::set_token_properties(&collection, &owner, item, props.into_iter(), &Unlimited)?}
- reset_token_properties {
+ init_token_properties {
let b in 0..MAX_PROPERTIES_PER_ITEM;
bench_init!{
owner: sub; collection: collection(owner);
owner: cross_from_sub;
};
+
let perms = (0..b).map(|k| PropertyKeyPermission {
key: property_key(k as usize),
permission: PropertyPermission {
@@ -220,8 +223,26 @@
value: property_value(),
}).collect::<Vec<_>>();
let item = create_max_item(&collection, &owner, owner.clone())?;
- }: {<Pallet<T>>::set_token_properties(&collection, &owner, item, props.into_iter(), SetPropertyMode::NewToken { mint_target_is_sender: true }, &Unlimited)?}
+ let (is_collection_admin, property_permissions) = load_is_admin_and_property_permissions(&collection, &owner);
+ }: {
+ let mut property_writer = pallet_common::collection_info_loaded_property_writer(
+ &collection,
+ is_collection_admin,
+ property_permissions,
+ );
+
+ property_writer.write_token_properties(
+ true,
+ item,
+ props.into_iter(),
+ crate::erc::ERC721TokenEvent::TokenChanged {
+ token_id: item.into(),
+ }
+ .to_log(T::ContractAddress::get()),
+ )?
+ }
+
delete_token_properties {
let b in 0..MAX_PROPERTIES_PER_ITEM;
bench_init!{
@@ -242,7 +263,7 @@
value: property_value(),
}).collect::<Vec<_>>();
let item = create_max_item(&collection, &owner, owner.clone())?;
- <Pallet<T>>::set_token_properties(&collection, &owner, item, props.into_iter(), SetPropertyMode::ExistingToken, &Unlimited)?;
+ <Pallet<T>>::set_token_properties(&collection, &owner, item, props.into_iter(), &Unlimited)?;
let to_delete = (0..b).map(|k| property_key(k as usize)).collect::<Vec<_>>();
}: {<Pallet<T>>::delete_token_properties(&collection, &owner, item, to_delete.into_iter(), &Unlimited)?}
pallets/nonfungible/src/common.rsdiffbeforeafterboth1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617use core::marker::PhantomData;1819use frame_support::{dispatch::DispatchResultWithPostInfo, ensure, fail, weights::Weight};20use up_data_structs::{21 TokenId, CreateItemExData, CollectionId, budget::Budget, Property, PropertyKey,22 PropertyKeyPermission, PropertyValue, TokenOwnerError,23};24use pallet_common::{25 CommonCollectionOperations, CommonWeightInfo, RefungibleExtensions, with_weight,26 weights::WeightInfo as _, SelfWeightOf as PalletCommonWeightOf, init_token_properties_delta,27};28use pallet_structure::Pallet as PalletStructure;29use sp_runtime::DispatchError;30use sp_std::{vec::Vec, vec};3132use crate::{33 AccountBalance, Allowance, Config, CreateItemData, Error, NonfungibleHandle, Owned, Pallet,34 SelfWeightOf, TokenData, weights::WeightInfo, TokensMinted, TokenProperties,35};3637pub struct CommonWeights<T: Config>(PhantomData<T>);38impl<T: Config> CommonWeightInfo<T::CrossAccountId> for CommonWeights<T> {39 fn create_multiple_items_ex(data: &CreateItemExData<T::CrossAccountId>) -> Weight {40 match data {41 CreateItemExData::NFT(t) => <SelfWeightOf<T>>::create_multiple_items_ex(t.len() as u32)42 .saturating_add(init_token_properties_delta::<T, _>(43 t.iter().map(|t| t.properties.len() as u32),44 <SelfWeightOf<T>>::init_token_properties,45 )),46 _ => Weight::zero(),47 }48 }4950 fn create_multiple_items(data: &[up_data_structs::CreateItemData]) -> Weight {51 <SelfWeightOf<T>>::create_multiple_items(data.len() as u32).saturating_add(52 init_token_properties_delta::<T, _>(53 data.iter().map(|t| match t {54 up_data_structs::CreateItemData::NFT(n) => n.properties.len() as u32,55 _ => 0,56 }),57 <SelfWeightOf<T>>::init_token_properties,58 ),59 )60 }6162 fn burn_item() -> Weight {63 <SelfWeightOf<T>>::burn_item()64 }6566 fn set_collection_properties(amount: u32) -> Weight {67 <pallet_common::SelfWeightOf<T>>::set_collection_properties(amount)68 }6970 fn delete_collection_properties(amount: u32) -> Weight {71 <pallet_common::SelfWeightOf<T>>::delete_collection_properties(amount)72 }7374 fn set_token_properties(amount: u32) -> Weight {75 <SelfWeightOf<T>>::set_token_properties(amount)76 }7778 fn delete_token_properties(amount: u32) -> Weight {79 <SelfWeightOf<T>>::delete_token_properties(amount)80 }8182 fn set_token_property_permissions(amount: u32) -> Weight {83 <SelfWeightOf<T>>::set_token_property_permissions(amount)84 }8586 fn transfer() -> Weight {87 <SelfWeightOf<T>>::transfer_raw() + <PalletCommonWeightOf<T>>::check_accesslist() * 288 }8990 fn approve() -> Weight {91 <SelfWeightOf<T>>::approve()92 }9394 fn approve_from() -> Weight {95 <SelfWeightOf<T>>::approve_from()96 }9798 fn transfer_from() -> Weight {99 Self::transfer() + <SelfWeightOf<T>>::check_allowed_raw()100 }101102 fn burn_from() -> Weight {103 <SelfWeightOf<T>>::burn_from()104 }105106 fn burn_recursively_self_raw() -> Weight {107 <SelfWeightOf<T>>::burn_recursively_self_raw()108 }109110 fn burn_recursively_breadth_raw(amount: u32) -> Weight {111 <SelfWeightOf<T>>::burn_recursively_breadth_plus_self_plus_self_per_each_raw(amount)112 .saturating_sub(Self::burn_recursively_self_raw().saturating_mul(amount as u64 + 1))113 }114115 fn token_owner() -> Weight {116 <SelfWeightOf<T>>::token_owner()117 }118119 fn set_allowance_for_all() -> Weight {120 <SelfWeightOf<T>>::set_allowance_for_all()121 }122123 fn force_repair_item() -> Weight {124 <SelfWeightOf<T>>::repair_item()125 }126}127128fn map_create_data<T: Config>(129 data: up_data_structs::CreateItemData,130 to: &T::CrossAccountId,131) -> Result<CreateItemData<T>, DispatchError> {132 match data {133 up_data_structs::CreateItemData::NFT(data) => Ok(CreateItemData::<T> {134 properties: data.properties,135 owner: to.clone(),136 }),137 _ => fail!(<Error<T>>::NotNonfungibleDataUsedToMintFungibleCollectionToken),138 }139}140141/// Implementation of `CommonCollectionOperations` for `NonfungibleHandle`. It wraps Nonfungible Pallete142/// methods and adds weight info.143impl<T: Config> CommonCollectionOperations<T> for NonfungibleHandle<T> {144 fn create_item(145 &self,146 sender: T::CrossAccountId,147 to: T::CrossAccountId,148 data: up_data_structs::CreateItemData,149 nesting_budget: &dyn Budget,150 ) -> DispatchResultWithPostInfo {151 let weight = <CommonWeights<T>>::create_item(&data);152 with_weight(153 <Pallet<T>>::create_item(154 self,155 &sender,156 map_create_data::<T>(data, &to)?,157 nesting_budget,158 ),159 weight,160 )161 }162163 fn create_multiple_items(164 &self,165 sender: T::CrossAccountId,166 to: T::CrossAccountId,167 data: Vec<up_data_structs::CreateItemData>,168 nesting_budget: &dyn Budget,169 ) -> DispatchResultWithPostInfo {170 let weight = <CommonWeights<T>>::create_multiple_items(&data);171 let data = data172 .into_iter()173 .map(|d| map_create_data::<T>(d, &to))174 .collect::<Result<Vec<_>, DispatchError>>()?;175176 with_weight(177 <Pallet<T>>::create_multiple_items(self, &sender, data, nesting_budget),178 weight,179 )180 }181182 fn create_multiple_items_ex(183 &self,184 sender: <T>::CrossAccountId,185 data: up_data_structs::CreateItemExData<<T>::CrossAccountId>,186 nesting_budget: &dyn Budget,187 ) -> DispatchResultWithPostInfo {188 let weight = <CommonWeights<T>>::create_multiple_items_ex(&data);189 let data = match data {190 up_data_structs::CreateItemExData::NFT(nft) => nft,191 _ => fail!(Error::<T>::NotNonfungibleDataUsedToMintFungibleCollectionToken),192 };193194 with_weight(195 <Pallet<T>>::create_multiple_items(self, &sender, data.into_inner(), nesting_budget),196 weight,197 )198 }199200 fn set_collection_properties(201 &self,202 sender: T::CrossAccountId,203 properties: Vec<Property>,204 ) -> DispatchResultWithPostInfo {205 let weight = <CommonWeights<T>>::set_collection_properties(properties.len() as u32);206207 with_weight(208 <Pallet<T>>::set_collection_properties(self, &sender, properties),209 weight,210 )211 }212213 fn delete_collection_properties(214 &self,215 sender: &T::CrossAccountId,216 property_keys: Vec<PropertyKey>,217 ) -> DispatchResultWithPostInfo {218 let weight = <CommonWeights<T>>::delete_collection_properties(property_keys.len() as u32);219220 with_weight(221 <Pallet<T>>::delete_collection_properties(self, sender, property_keys),222 weight,223 )224 }225226 fn set_token_properties(227 &self,228 sender: T::CrossAccountId,229 token_id: TokenId,230 properties: Vec<Property>,231 nesting_budget: &dyn Budget,232 ) -> DispatchResultWithPostInfo {233 let weight = <CommonWeights<T>>::set_token_properties(properties.len() as u32);234235 with_weight(236 <Pallet<T>>::set_token_properties(237 self,238 &sender,239 token_id,240 properties.into_iter(),241 nesting_budget,242 ),243 weight,244 )245 }246247 fn delete_token_properties(248 &self,249 sender: T::CrossAccountId,250 token_id: TokenId,251 property_keys: Vec<PropertyKey>,252 nesting_budget: &dyn Budget,253 ) -> DispatchResultWithPostInfo {254 let weight = <CommonWeights<T>>::delete_token_properties(property_keys.len() as u32);255256 with_weight(257 <Pallet<T>>::delete_token_properties(258 self,259 &sender,260 token_id,261 property_keys.into_iter(),262 nesting_budget,263 ),264 weight,265 )266 }267268 fn get_token_properties_map(&self, token_id: TokenId) -> up_data_structs::TokenProperties {269 <TokenProperties<T>>::get((self.id, token_id))270 }271272 fn set_token_properties_map(&self, token_id: TokenId, map: up_data_structs::TokenProperties) {273 <TokenProperties<T>>::set((self.id, token_id), map)274 }275276 fn set_token_property_permissions(277 &self,278 sender: &T::CrossAccountId,279 property_permissions: Vec<PropertyKeyPermission>,280 ) -> DispatchResultWithPostInfo {281 let weight =282 <CommonWeights<T>>::set_token_property_permissions(property_permissions.len() as u32);283284 with_weight(285 <Pallet<T>>::set_token_property_permissions(self, sender, property_permissions),286 weight,287 )288 }289290 fn properties_exist(&self, token: TokenId) -> bool {291 <TokenProperties<T>>::contains_key((self.id, token))292 }293294 fn burn_item(295 &self,296 sender: T::CrossAccountId,297 token: TokenId,298 amount: u128,299 ) -> DispatchResultWithPostInfo {300 ensure!(amount <= 1, <Error<T>>::NonfungibleItemsHaveNoAmount);301 if amount == 1 {302 with_weight(303 <Pallet<T>>::burn(self, &sender, token),304 <CommonWeights<T>>::burn_item(),305 )306 } else {307 <Pallet<T>>::check_token_immediate_ownership(self, token, &sender)?;308 Ok(().into())309 }310 }311312 fn burn_item_recursively(313 &self,314 sender: T::CrossAccountId,315 token: TokenId,316 self_budget: &dyn Budget,317 breadth_budget: &dyn Budget,318 ) -> DispatchResultWithPostInfo {319 <Pallet<T>>::burn_recursively(self, &sender, token, self_budget, breadth_budget)320 }321322 fn transfer(323 &self,324 from: T::CrossAccountId,325 to: T::CrossAccountId,326 token: TokenId,327 amount: u128,328 nesting_budget: &dyn Budget,329 ) -> DispatchResultWithPostInfo {330 ensure!(amount <= 1, <Error<T>>::NonfungibleItemsHaveNoAmount);331 if amount == 1 {332 <Pallet<T>>::transfer(self, &from, &to, token, nesting_budget)333 } else {334 <Pallet<T>>::check_token_immediate_ownership(self, token, &from)?;335 Ok(().into())336 }337 }338339 fn approve(340 &self,341 sender: T::CrossAccountId,342 spender: T::CrossAccountId,343 token: TokenId,344 amount: u128,345 ) -> DispatchResultWithPostInfo {346 ensure!(amount <= 1, <Error<T>>::NonfungibleItemsHaveNoAmount);347348 with_weight(349 if amount == 1 {350 <Pallet<T>>::set_allowance(self, &sender, token, Some(&spender))351 } else {352 <Pallet<T>>::set_allowance(self, &sender, token, None)353 },354 <CommonWeights<T>>::approve(),355 )356 }357358 fn approve_from(359 &self,360 sender: T::CrossAccountId,361 from: T::CrossAccountId,362 to: T::CrossAccountId,363 token: TokenId,364 amount: u128,365 ) -> DispatchResultWithPostInfo {366 ensure!(amount <= 1, <Error<T>>::NonfungibleItemsHaveNoAmount);367368 with_weight(369 if amount == 1 {370 <Pallet<T>>::set_allowance_from(self, &sender, &from, token, Some(&to))371 } else {372 <Pallet<T>>::set_allowance_from(self, &sender, &from, token, None)373 },374 <CommonWeights<T>>::approve_from(),375 )376 }377378 fn transfer_from(379 &self,380 sender: T::CrossAccountId,381 from: T::CrossAccountId,382 to: T::CrossAccountId,383 token: TokenId,384 amount: u128,385 nesting_budget: &dyn Budget,386 ) -> DispatchResultWithPostInfo {387 ensure!(amount <= 1, <Error<T>>::NonfungibleItemsHaveNoAmount);388389 if amount == 1 {390 <Pallet<T>>::transfer_from(self, &sender, &from, &to, token, nesting_budget)391 } else {392 <Pallet<T>>::check_allowed(self, &sender, &from, token, nesting_budget)?;393394 Ok(().into())395 }396 }397398 fn burn_from(399 &self,400 sender: T::CrossAccountId,401 from: T::CrossAccountId,402 token: TokenId,403 amount: u128,404 nesting_budget: &dyn Budget,405 ) -> DispatchResultWithPostInfo {406 ensure!(amount <= 1, <Error<T>>::NonfungibleItemsHaveNoAmount);407408 if amount == 1 {409 with_weight(410 <Pallet<T>>::burn_from(self, &sender, &from, token, nesting_budget),411 <CommonWeights<T>>::burn_from(),412 )413 } else {414 <Pallet<T>>::check_allowed(self, &sender, &from, token, nesting_budget)?;415416 Ok(().into())417 }418 }419420 fn check_nesting(421 &self,422 sender: T::CrossAccountId,423 from: (CollectionId, TokenId),424 under: TokenId,425 nesting_budget: &dyn Budget,426 ) -> sp_runtime::DispatchResult {427 <Pallet<T>>::check_nesting(self, sender, from, under, nesting_budget)428 }429430 fn nest(&self, under: TokenId, to_nest: (CollectionId, TokenId)) {431 <Pallet<T>>::nest((self.id, under), to_nest);432 }433434 fn unnest(&self, under: TokenId, to_unnest: (CollectionId, TokenId)) {435 <Pallet<T>>::unnest((self.id, under), to_unnest);436 }437438 fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId> {439 <Owned<T>>::iter_prefix((self.id, account))440 .map(|(id, _)| id)441 .collect()442 }443444 fn collection_tokens(&self) -> Vec<TokenId> {445 <TokenData<T>>::iter_prefix((self.id,))446 .map(|(id, _)| id)447 .collect()448 }449450 fn token_exists(&self, token: TokenId) -> bool {451 <Pallet<T>>::token_exists(self, token)452 }453454 fn last_token_id(&self) -> TokenId {455 TokenId(<TokensMinted<T>>::get(self.id))456 }457458 fn token_owner(&self, token: TokenId) -> Result<T::CrossAccountId, TokenOwnerError> {459 <TokenData<T>>::get((self.id, token))460 .map(|t| t.owner)461 .ok_or(TokenOwnerError::NotFound)462 }463464 fn check_token_indirect_owner(465 &self,466 token: TokenId,467 maybe_owner: &T::CrossAccountId,468 nesting_budget: &dyn Budget,469 ) -> Result<bool, DispatchError> {470 <PalletStructure<T>>::check_indirectly_owned(471 maybe_owner.clone(),472 self.id,473 token,474 None,475 nesting_budget,476 )477 }478479 /// Returns token owners.480 fn token_owners(&self, token: TokenId) -> Vec<T::CrossAccountId> {481 self.token_owner(token).map_or_else(|_| vec![], |t| vec![t])482 }483484 fn token_property(&self, token_id: TokenId, key: &PropertyKey) -> Option<PropertyValue> {485 <Pallet<T>>::token_properties((self.id, token_id))486 .get(key)487 .cloned()488 }489490 fn token_properties(&self, token_id: TokenId, keys: Option<Vec<PropertyKey>>) -> Vec<Property> {491 let properties = <Pallet<T>>::token_properties((self.id, token_id));492493 keys.map(|keys| {494 keys.into_iter()495 .filter_map(|key| {496 properties.get(&key).map(|value| Property {497 key,498 value: value.clone(),499 })500 })501 .collect()502 })503 .unwrap_or_else(|| {504 properties505 .into_iter()506 .map(|(key, value)| Property { key, value })507 .collect()508 })509 }510511 fn total_supply(&self) -> u32 {512 <Pallet<T>>::total_supply(self)513 }514515 fn account_balance(&self, account: T::CrossAccountId) -> u32 {516 <AccountBalance<T>>::get((self.id, account))517 }518519 fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128 {520 if <TokenData<T>>::get((self.id, token))521 .map(|a| a.owner == account)522 .unwrap_or(false)523 {524 1525 } else {526 0527 }528 }529530 fn allowance(531 &self,532 sender: T::CrossAccountId,533 spender: T::CrossAccountId,534 token: TokenId,535 ) -> u128 {536 if <TokenData<T>>::get((self.id, token))537 .map(|a| a.owner != sender)538 .unwrap_or(true)539 {540 0541 } else if <Allowance<T>>::get((self.id, token)) == Some(spender) {542 1543 } else {544 0545 }546 }547548 fn refungible_extensions(&self) -> Option<&dyn RefungibleExtensions<T>> {549 None550 }551552 fn total_pieces(&self, token: TokenId) -> Option<u128> {553 if <TokenData<T>>::contains_key((self.id, token)) {554 Some(1)555 } else {556 None557 }558 }559560 fn set_allowance_for_all(561 &self,562 owner: T::CrossAccountId,563 operator: T::CrossAccountId,564 approve: bool,565 ) -> DispatchResultWithPostInfo {566 with_weight(567 <Pallet<T>>::set_allowance_for_all(self, &owner, &operator, approve),568 <CommonWeights<T>>::set_allowance_for_all(),569 )570 }571572 fn allowance_for_all(&self, owner: T::CrossAccountId, operator: T::CrossAccountId) -> bool {573 <Pallet<T>>::allowance_for_all(self, &owner, &operator)574 }575576 fn repair_item(&self, token: TokenId) -> DispatchResultWithPostInfo {577 with_weight(578 <Pallet<T>>::repair_item(self, token),579 <CommonWeights<T>>::force_repair_item(),580 )581 }582}pallets/nonfungible/src/erc.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/erc.rs
+++ b/pallets/nonfungible/src/erc.rs
@@ -203,7 +203,6 @@
&caller,
TokenId(token_id),
properties.into_iter(),
- pallet_common::SetPropertyMode::ExistingToken,
&nesting_budget,
)
.map_err(dispatch_to_evm::<T>)
pallets/nonfungible/src/lib.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/lib.rs
+++ b/pallets/nonfungible/src/lib.rs
@@ -109,7 +109,7 @@
use pallet_common::{
Error as CommonError, Pallet as PalletCommon, Event as CommonEvent, CollectionHandle,
eth::collection_id_to_address, SelfWeightOf as PalletCommonWeightOf,
- weights::WeightInfo as CommonWeightInfo, helpers::add_weight_to_post_info, SetPropertyMode,
+ weights::WeightInfo as CommonWeightInfo, helpers::add_weight_to_post_info,
};
use pallet_structure::{Pallet as PalletStructure, Error as StructureError};
use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};
@@ -598,58 +598,16 @@
sender: &T::CrossAccountId,
token_id: TokenId,
properties_updates: impl Iterator<Item = (PropertyKey, Option<PropertyValue>)>,
- mode: SetPropertyMode,
nesting_budget: &dyn Budget,
) -> DispatchResult {
- let mut is_token_owner = pallet_common::LazyValue::new(|| {
- if let SetPropertyMode::NewToken {
- mint_target_is_sender,
- } = mode
- {
- return Ok(mint_target_is_sender);
- }
-
- let is_owned = <PalletStructure<T>>::check_indirectly_owned(
- sender.clone(),
- collection.id,
- token_id,
- None,
- nesting_budget,
- )?;
-
- Ok(is_owned)
- });
-
- let is_new_token = matches!(mode, SetPropertyMode::NewToken { .. });
-
- let mut is_token_exist = pallet_common::LazyValue::new(|| {
- if is_new_token {
- debug_assert!(Self::token_exists(collection, token_id));
- true
- } else {
- Self::token_exists(collection, token_id)
- }
- });
-
- let stored_properties = if is_new_token {
- debug_assert!(!<TokenProperties<T>>::contains_key((
- collection.id,
- token_id
- )));
- TokenPropertiesT::new()
- } else {
- <TokenProperties<T>>::get((collection.id, token_id))
- };
+ let mut property_writer =
+ pallet_common::property_writer_for_existing_token(collection, sender);
- <PalletCommon<T>>::modify_token_properties(
- collection,
+ property_writer.write_token_properties(
sender,
token_id,
- &mut is_token_exist,
properties_updates,
- stored_properties,
- &mut is_token_owner,
- |properties| <TokenProperties<T>>::set((collection.id, token_id), properties),
+ nesting_budget,
erc::ERC721TokenEvent::TokenChanged {
token_id: token_id.into(),
}
@@ -680,7 +638,6 @@
sender: &T::CrossAccountId,
token_id: TokenId,
properties: impl Iterator<Item = Property>,
- mode: SetPropertyMode,
nesting_budget: &dyn Budget,
) -> DispatchResult {
Self::modify_token_properties(
@@ -688,7 +645,6 @@
sender,
token_id,
properties.map(|p| (p.key, Some(p.value))),
- mode,
nesting_budget,
)
}
@@ -710,7 +666,6 @@
sender,
token_id,
[property].into_iter(),
- SetPropertyMode::ExistingToken,
nesting_budget,
)
}
@@ -732,7 +687,6 @@
sender,
token_id,
property_keys.into_iter().map(|key| (key, None)),
- SetPropertyMode::ExistingToken,
nesting_budget,
)
}
@@ -994,6 +948,8 @@
// =========
+ let mut property_writer = pallet_common::property_writer_for_new_token(collection, sender);
+
with_transaction(|| {
for (i, data) in data.iter().enumerate() {
let token = first_token + i as u32 + 1;
@@ -1006,21 +962,22 @@
},
);
+ let token = TokenId(token);
+
<PalletStructure<T>>::nest_if_sent_to_token_unchecked(
&data.owner,
collection.id,
- TokenId(token),
+ token,
);
- if let Err(e) = Self::set_token_properties(
- collection,
- sender,
- TokenId(token),
+ if let Err(e) = property_writer.write_token_properties(
+ sender.conv_eq(&data.owner),
+ token,
data.properties.clone().into_iter(),
- SetPropertyMode::NewToken {
- mint_target_is_sender: sender.conv_eq(&data.owner),
- },
- nesting_budget,
+ erc::ERC721TokenEvent::TokenChanged {
+ token_id: token.into(),
+ }
+ .to_log(T::ContractAddress::get()),
) {
return TransactionOutcome::Rollback(Err(e));
}
pallets/refungible/src/benchmarking.rsdiffbeforeafterboth--- a/pallets/refungible/src/benchmarking.rs
+++ b/pallets/refungible/src/benchmarking.rs
@@ -22,7 +22,9 @@
use frame_benchmarking::{benchmarks, account};
use pallet_common::{
bench_init,
- benchmarking::{create_collection_raw, property_key, property_value},
+ benchmarking::{
+ create_collection_raw, property_key, property_value, load_is_admin_and_property_permissions,
+ },
};
use sp_std::prelude::*;
use up_data_structs::{
@@ -255,14 +257,15 @@
value: property_value(),
}).collect::<Vec<_>>();
let item = create_max_item(&collection, &owner, [(owner.clone(), 200)])?;
- }: {<Pallet<T>>::set_token_properties(&collection, &owner, item, props.into_iter(), SetPropertyMode::ExistingToken, &Unlimited)?}
+ }: {<Pallet<T>>::set_token_properties(&collection, &owner, item, props.into_iter(), &Unlimited)?}
- reset_token_properties {
+ init_token_properties {
let b in 0..MAX_PROPERTIES_PER_ITEM;
bench_init!{
owner: sub; collection: collection(owner);
owner: cross_from_sub;
};
+
let perms = (0..b).map(|k| PropertyKeyPermission {
key: property_key(k as usize),
permission: PropertyPermission {
@@ -277,8 +280,26 @@
value: property_value(),
}).collect::<Vec<_>>();
let item = create_max_item(&collection, &owner, [(owner.clone(), 200)])?;
- }: {<Pallet<T>>::set_token_properties(&collection, &owner, item, props.into_iter(), SetPropertyMode::NewToken { mint_target_is_sender: true }, &Unlimited)?}
+ let (is_collection_admin, property_permissions) = load_is_admin_and_property_permissions(&collection, &owner);
+ }: {
+ let mut property_writer = pallet_common::collection_info_loaded_property_writer(
+ &collection,
+ is_collection_admin,
+ property_permissions,
+ );
+
+ property_writer.write_token_properties(
+ true,
+ item,
+ props.into_iter(),
+ crate::erc::ERC721TokenEvent::TokenChanged {
+ token_id: item.into(),
+ }
+ .to_log(T::ContractAddress::get()),
+ )?
+ }
+
delete_token_properties {
let b in 0..MAX_PROPERTIES_PER_ITEM;
bench_init!{
@@ -299,7 +320,7 @@
value: property_value(),
}).collect::<Vec<_>>();
let item = create_max_item(&collection, &owner, [(owner.clone(), 200)])?;
- <Pallet<T>>::set_token_properties(&collection, &owner, item, props.into_iter(), SetPropertyMode::ExistingToken, &Unlimited)?;
+ <Pallet<T>>::set_token_properties(&collection, &owner, item, props.into_iter(), &Unlimited)?;
let to_delete = (0..b).map(|k| property_key(k as usize)).collect::<Vec<_>>();
}: {<Pallet<T>>::delete_token_properties(&collection, &owner, item, to_delete.into_iter(), &Unlimited)?}
pallets/refungible/src/common.rsdiffbeforeafterboth--- a/pallets/refungible/src/common.rs
+++ b/pallets/refungible/src/common.rs
@@ -20,20 +20,20 @@
use frame_support::{dispatch::DispatchResultWithPostInfo, ensure, fail, weights::Weight, traits::Get};
use up_data_structs::{
CollectionId, TokenId, CreateItemExData, budget::Budget, Property, PropertyKey, PropertyValue,
- PropertyKeyPermission, CollectionPropertiesVec, CreateRefungibleExMultipleOwners,
- CreateRefungibleExSingleOwner, TokenOwnerError,
+ PropertyKeyPermission, CreateRefungibleExMultipleOwners, CreateRefungibleExSingleOwner,
+ TokenOwnerError,
};
use pallet_common::{
CommonCollectionOperations, CommonWeightInfo, RefungibleExtensions, with_weight,
- weights::WeightInfo as _,
+ weights::WeightInfo as _, init_token_properties_delta,
};
-use pallet_structure::Error as StructureError;
+use pallet_structure::{Pallet as PalletStructure, Error as StructureError};
use sp_runtime::{DispatchError};
use sp_std::{vec::Vec, vec};
use crate::{
AccountBalance, Allowance, Balance, Config, Error, Owned, Pallet, RefungibleHandle,
- SelfWeightOf, weights::WeightInfo, TokensMinted, TotalSupply, CreateItemData,
+ SelfWeightOf, weights::WeightInfo, TokensMinted, TotalSupply, CreateItemData, TokenProperties,
};
macro_rules! max_weight_of {
@@ -45,26 +45,19 @@
};
}
-fn properties_weight<T: Config>(properties: &CollectionPropertiesVec) -> Weight {
- if properties.len() > 0 {
- <SelfWeightOf<T>>::reset_token_properties(properties.len() as u32)
- } else {
- Weight::zero()
- }
-}
-
pub struct CommonWeights<T: Config>(PhantomData<T>);
impl<T: Config> CommonWeightInfo<T::CrossAccountId> for CommonWeights<T> {
fn create_multiple_items(data: &[up_data_structs::CreateItemData]) -> Weight {
<SelfWeightOf<T>>::create_multiple_items(data.len() as u32).saturating_add(
- data.iter()
- .map(|data| match data {
+ init_token_properties_delta::<T, _>(
+ data.iter().map(|data| match data {
up_data_structs::CreateItemData::ReFungible(rft_data) => {
- properties_weight::<T>(&rft_data.properties)
+ rft_data.properties.len() as u32
}
- _ => Weight::zero(),
- })
- .fold(Weight::zero(), |a, b| a.saturating_add(b)),
+ _ => 0,
+ }),
+ <SelfWeightOf<T>>::init_token_properties,
+ ),
)
}
@@ -72,15 +65,17 @@
match call {
CreateItemExData::RefungibleMultipleOwners(i) => {
<SelfWeightOf<T>>::create_multiple_items_ex_multiple_owners(i.users.len() as u32)
- .saturating_add(properties_weight::<T>(&i.properties))
+ .saturating_add(init_token_properties_delta::<T, _>(
+ [i.properties.len() as u32].into_iter(),
+ <SelfWeightOf<T>>::init_token_properties,
+ ))
}
CreateItemExData::RefungibleMultipleItems(i) => {
<SelfWeightOf<T>>::create_multiple_items_ex_multiple_items(i.len() as u32)
- .saturating_add(
- i.iter()
- .map(|d| properties_weight::<T>(&d.properties))
- .fold(Weight::zero(), |a, b| a.saturating_add(b)),
- )
+ .saturating_add(init_token_properties_delta::<T, _>(
+ i.iter().map(|d| d.properties.len() as u32),
+ <SelfWeightOf<T>>::init_token_properties,
+ ))
}
_ => Weight::zero(),
}
@@ -399,7 +394,6 @@
&sender,
token_id,
properties.into_iter(),
- pallet_common::SetPropertyMode::ExistingToken,
nesting_budget,
),
weight,
@@ -441,6 +435,18 @@
)
}
+ fn get_token_properties_map(&self, token_id: TokenId) -> up_data_structs::TokenProperties {
+ <TokenProperties<T>>::get((self.id, token_id))
+ }
+
+ fn set_token_properties_map(&self, token_id: TokenId, map: up_data_structs::TokenProperties) {
+ <TokenProperties<T>>::set((self.id, token_id), map)
+ }
+
+ fn properties_exist(&self, token: TokenId) -> bool {
+ <TokenProperties<T>>::contains_key((self.id, token))
+ }
+
fn check_nesting(
&self,
_sender: <T>::CrossAccountId,
@@ -479,6 +485,29 @@
<Pallet<T>>::token_owner(self.id, token)
}
+ fn check_token_indirect_owner(
+ &self,
+ token: TokenId,
+ maybe_owner: &T::CrossAccountId,
+ nesting_budget: &dyn Budget,
+ ) -> Result<bool, DispatchError> {
+ let balance = self.balance(maybe_owner.clone(), token);
+ let total_pieces: u128 = <Pallet<T>>::total_pieces(self.id, token).unwrap_or(u128::MAX);
+ if balance != total_pieces {
+ return Ok(false);
+ }
+
+ let is_bundle_owner = <PalletStructure<T>>::check_indirectly_owned(
+ maybe_owner.clone(),
+ self.id,
+ token,
+ None,
+ nesting_budget,
+ )?;
+
+ Ok(is_bundle_owner)
+ }
+
/// Returns 10 token in no particular order.
fn token_owners(&self, token: TokenId) -> Vec<T::CrossAccountId> {
<Pallet<T>>::token_owners(self.id, token).unwrap_or_default()
pallets/refungible/src/erc.rsdiffbeforeafterboth--- a/pallets/refungible/src/erc.rs
+++ b/pallets/refungible/src/erc.rs
@@ -214,7 +214,6 @@
&caller,
TokenId(token_id),
properties.into_iter(),
- pallet_common::SetPropertyMode::ExistingToken,
&nesting_budget,
)
.map_err(dispatch_to_evm::<T>)
pallets/refungible/src/lib.rsdiffbeforeafterboth--- a/pallets/refungible/src/lib.rs
+++ b/pallets/refungible/src/lib.rs
@@ -96,8 +96,8 @@
use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm};
use pallet_evm_coder_substrate::WithRecorder;
use pallet_common::{
- CommonCollectionOperations, Error as CommonError, eth::collection_id_to_address,
- Event as CommonEvent, Pallet as PalletCommon, SetPropertyMode,
+ Error as CommonError, eth::collection_id_to_address, Event as CommonEvent,
+ Pallet as PalletCommon,
};
use pallet_structure::Pallet as PalletStructure;
use sp_core::{Get, H160};
@@ -533,66 +533,16 @@
sender: &T::CrossAccountId,
token_id: TokenId,
properties_updates: impl Iterator<Item = (PropertyKey, Option<PropertyValue>)>,
- mode: SetPropertyMode,
nesting_budget: &dyn Budget,
) -> DispatchResult {
- let mut is_token_owner =
- pallet_common::LazyValue::new(|| -> Result<bool, DispatchError> {
- if let SetPropertyMode::NewToken {
- mint_target_is_sender,
- } = mode
- {
- return Ok(mint_target_is_sender);
- }
-
- let balance = collection.balance(sender.clone(), token_id);
- let total_pieces: u128 =
- Self::total_pieces(collection.id, token_id).unwrap_or(u128::MAX);
- if balance != total_pieces {
- return Ok(false);
- }
-
- let is_bundle_owner = <PalletStructure<T>>::check_indirectly_owned(
- sender.clone(),
- collection.id,
- token_id,
- None,
- nesting_budget,
- )?;
-
- Ok(is_bundle_owner)
- });
-
- let is_new_token = matches!(mode, SetPropertyMode::NewToken { .. });
-
- let mut is_token_exist = pallet_common::LazyValue::new(|| {
- if is_new_token {
- debug_assert!(Self::token_exists(collection, token_id));
- true
- } else {
- Self::token_exists(collection, token_id)
- }
- });
-
- let stored_properties = if is_new_token {
- debug_assert!(!<TokenProperties<T>>::contains_key((
- collection.id,
- token_id
- )));
- TokenPropertiesT::new()
- } else {
- <TokenProperties<T>>::get((collection.id, token_id))
- };
+ let mut property_writer =
+ pallet_common::property_writer_for_existing_token(collection, sender);
- <PalletCommon<T>>::modify_token_properties(
- collection,
+ property_writer.write_token_properties(
sender,
token_id,
- &mut is_token_exist,
properties_updates,
- stored_properties,
- &mut is_token_owner,
- |properties| <TokenProperties<T>>::set((collection.id, token_id), properties),
+ nesting_budget,
erc::ERC721TokenEvent::TokenChanged {
token_id: token_id.into(),
}
@@ -618,7 +568,6 @@
sender: &T::CrossAccountId,
token_id: TokenId,
properties: impl Iterator<Item = Property>,
- mode: SetPropertyMode,
nesting_budget: &dyn Budget,
) -> DispatchResult {
Self::modify_token_properties(
@@ -626,7 +575,6 @@
sender,
token_id,
properties.map(|p| (p.key, Some(p.value))),
- mode,
nesting_budget,
)
}
@@ -643,7 +591,6 @@
sender,
token_id,
[property].into_iter(),
- SetPropertyMode::ExistingToken,
nesting_budget,
)
}
@@ -660,7 +607,6 @@
sender,
token_id,
property_keys.into_iter().map(|key| (key, None)),
- SetPropertyMode::ExistingToken,
nesting_budget,
)
}
@@ -941,11 +887,15 @@
// =========
+ let mut property_writer = pallet_common::property_writer_for_new_token(collection, sender);
+
with_transaction(|| {
for (i, data) in data.iter().enumerate() {
let token_id = first_token_id + i as u32 + 1;
<TotalSupply<T>>::insert((collection.id, token_id), totals[i]);
+ let token = TokenId(token_id);
+
let mut mint_target_is_sender = true;
for (user, amount) in data.users.iter() {
if *amount == 0 {
@@ -955,23 +905,22 @@
mint_target_is_sender = mint_target_is_sender && sender.conv_eq(user);
<Balance<T>>::insert((collection.id, token_id, &user), amount);
- <Owned<T>>::insert((collection.id, &user, TokenId(token_id)), true);
+ <Owned<T>>::insert((collection.id, &user, token), true);
<PalletStructure<T>>::nest_if_sent_to_token_unchecked(
user,
collection.id,
- TokenId(token_id),
+ token,
);
}
- if let Err(e) = Self::set_token_properties(
- collection,
- sender,
- TokenId(token_id),
+ if let Err(e) = property_writer.write_token_properties(
+ mint_target_is_sender,
+ token,
data.properties.clone().into_iter(),
- SetPropertyMode::NewToken {
- mint_target_is_sender,
- },
- nesting_budget,
+ erc::ERC721TokenEvent::TokenChanged {
+ token_id: token.into(),
+ }
+ .to_log(T::ContractAddress::get()),
) {
return TransactionOutcome::Rollback(Err(e));
}