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.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/common.rs
+++ b/pallets/nonfungible/src/common.rs
@@ -23,49 +23,40 @@
};
use pallet_common::{
CommonCollectionOperations, CommonWeightInfo, RefungibleExtensions, with_weight,
- weights::WeightInfo as _, SelfWeightOf as PalletCommonWeightOf,
+ weights::WeightInfo as _, SelfWeightOf as PalletCommonWeightOf, init_token_properties_delta,
};
+use pallet_structure::Pallet as PalletStructure;
use sp_runtime::DispatchError;
use sp_std::{vec::Vec, vec};
use crate::{
AccountBalance, Allowance, Config, CreateItemData, Error, NonfungibleHandle, Owned, Pallet,
- SelfWeightOf, TokenData, weights::WeightInfo, TokensMinted,
+ SelfWeightOf, TokenData, weights::WeightInfo, TokensMinted, TokenProperties,
};
pub struct CommonWeights<T: Config>(PhantomData<T>);
impl<T: Config> CommonWeightInfo<T::CrossAccountId> for CommonWeights<T> {
fn create_multiple_items_ex(data: &CreateItemExData<T::CrossAccountId>) -> Weight {
match data {
- CreateItemExData::NFT(t) => {
- <SelfWeightOf<T>>::create_multiple_items_ex(t.len() as u32)
- + t.iter()
- .filter_map(|t| {
- if t.properties.len() > 0 {
- Some(<SelfWeightOf<T>>::reset_token_properties(
- t.properties.len() as u32,
- ))
- } else {
- None
- }
- })
- .fold(Weight::zero(), |a, b| a.saturating_add(b))
- }
+ CreateItemExData::NFT(t) => <SelfWeightOf<T>>::create_multiple_items_ex(t.len() as u32)
+ .saturating_add(init_token_properties_delta::<T, _>(
+ t.iter().map(|t| t.properties.len() as u32),
+ <SelfWeightOf<T>>::init_token_properties,
+ )),
_ => Weight::zero(),
}
}
fn create_multiple_items(data: &[up_data_structs::CreateItemData]) -> Weight {
- <SelfWeightOf<T>>::create_multiple_items(data.len() as u32)
- + data
- .iter()
- .filter_map(|t| match t {
- up_data_structs::CreateItemData::NFT(n) if n.properties.len() > 0 => Some(
- <SelfWeightOf<T>>::reset_token_properties(n.properties.len() as u32),
- ),
- _ => None,
- })
- .fold(Weight::zero(), |a, b| a.saturating_add(b))
+ <SelfWeightOf<T>>::create_multiple_items(data.len() as u32).saturating_add(
+ init_token_properties_delta::<T, _>(
+ data.iter().map(|t| match t {
+ up_data_structs::CreateItemData::NFT(n) => n.properties.len() as u32,
+ _ => 0,
+ }),
+ <SelfWeightOf<T>>::init_token_properties,
+ ),
+ )
}
fn burn_item() -> Weight {
@@ -247,7 +238,6 @@
&sender,
token_id,
properties.into_iter(),
- pallet_common::SetPropertyMode::ExistingToken,
nesting_budget,
),
weight,
@@ -275,6 +265,14 @@
)
}
+ 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 set_token_property_permissions(
&self,
sender: &T::CrossAccountId,
@@ -289,6 +287,10 @@
)
}
+ fn properties_exist(&self, token: TokenId) -> bool {
+ <TokenProperties<T>>::contains_key((self.id, token))
+ }
+
fn burn_item(
&self,
sender: T::CrossAccountId,
@@ -459,6 +461,21 @@
.ok_or(TokenOwnerError::NotFound)
}
+ fn check_token_indirect_owner(
+ &self,
+ token: TokenId,
+ maybe_owner: &T::CrossAccountId,
+ nesting_budget: &dyn Budget,
+ ) -> Result<bool, DispatchError> {
+ <PalletStructure<T>>::check_indirectly_owned(
+ maybe_owner.clone(),
+ self.id,
+ token,
+ None,
+ nesting_budget,
+ )
+ }
+
/// Returns token owners.
fn token_owners(&self, token: TokenId) -> Vec<T::CrossAccountId> {
self.token_owner(token).map_or_else(|_| vec![], |t| vec![t])
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.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 sp_std::collections::btree_map::BTreeMap;20use frame_support::{dispatch::DispatchResultWithPostInfo, ensure, fail, weights::Weight, traits::Get};21use up_data_structs::{22 CollectionId, TokenId, CreateItemExData, budget::Budget, Property, PropertyKey, PropertyValue,23 PropertyKeyPermission, CollectionPropertiesVec, CreateRefungibleExMultipleOwners,24 CreateRefungibleExSingleOwner, TokenOwnerError,25};26use pallet_common::{27 CommonCollectionOperations, CommonWeightInfo, RefungibleExtensions, with_weight,28 weights::WeightInfo as _,29};30use pallet_structure::Error as StructureError;31use sp_runtime::{DispatchError};32use sp_std::{vec::Vec, vec};3334use crate::{35 AccountBalance, Allowance, Balance, Config, Error, Owned, Pallet, RefungibleHandle,36 SelfWeightOf, weights::WeightInfo, TokensMinted, TotalSupply, CreateItemData,37};3839macro_rules! max_weight_of {40 ($($method:ident ($($args:tt)*)),*) => {41 Weight::zero()42 $(43 .max(<SelfWeightOf<T>>::$method($($args)*))44 )*45 };46}4748fn properties_weight<T: Config>(properties: &CollectionPropertiesVec) -> Weight {49 if properties.len() > 0 {50 <SelfWeightOf<T>>::reset_token_properties(properties.len() as u32)51 } else {52 Weight::zero()53 }54}5556pub struct CommonWeights<T: Config>(PhantomData<T>);57impl<T: Config> CommonWeightInfo<T::CrossAccountId> for CommonWeights<T> {58 fn create_multiple_items(data: &[up_data_structs::CreateItemData]) -> Weight {59 <SelfWeightOf<T>>::create_multiple_items(data.len() as u32).saturating_add(60 data.iter()61 .map(|data| match data {62 up_data_structs::CreateItemData::ReFungible(rft_data) => {63 properties_weight::<T>(&rft_data.properties)64 }65 _ => Weight::zero(),66 })67 .fold(Weight::zero(), |a, b| a.saturating_add(b)),68 )69 }7071 fn create_multiple_items_ex(call: &CreateItemExData<T::CrossAccountId>) -> Weight {72 match call {73 CreateItemExData::RefungibleMultipleOwners(i) => {74 <SelfWeightOf<T>>::create_multiple_items_ex_multiple_owners(i.users.len() as u32)75 .saturating_add(properties_weight::<T>(&i.properties))76 }77 CreateItemExData::RefungibleMultipleItems(i) => {78 <SelfWeightOf<T>>::create_multiple_items_ex_multiple_items(i.len() as u32)79 .saturating_add(80 i.iter()81 .map(|d| properties_weight::<T>(&d.properties))82 .fold(Weight::zero(), |a, b| a.saturating_add(b)),83 )84 }85 _ => Weight::zero(),86 }87 }8889 fn burn_item() -> Weight {90 max_weight_of!(burn_item_partial(), burn_item_fully())91 }9293 fn set_collection_properties(amount: u32) -> Weight {94 <pallet_common::SelfWeightOf<T>>::set_collection_properties(amount)95 }9697 fn delete_collection_properties(amount: u32) -> Weight {98 <pallet_common::SelfWeightOf<T>>::delete_collection_properties(amount)99 }100101 fn set_token_properties(amount: u32) -> Weight {102 <SelfWeightOf<T>>::set_token_properties(amount)103 }104105 fn delete_token_properties(amount: u32) -> Weight {106 <SelfWeightOf<T>>::delete_token_properties(amount)107 }108109 fn set_token_property_permissions(amount: u32) -> Weight {110 <SelfWeightOf<T>>::set_token_property_permissions(amount)111 }112113 fn transfer() -> Weight {114 max_weight_of!(115 transfer_normal(),116 transfer_creating(),117 transfer_removing(),118 transfer_creating_removing()119 )120 }121122 fn approve() -> Weight {123 <SelfWeightOf<T>>::approve()124 }125126 fn approve_from() -> Weight {127 <SelfWeightOf<T>>::approve_from()128 }129130 fn transfer_from() -> Weight {131 max_weight_of!(132 transfer_from_normal(),133 transfer_from_creating(),134 transfer_from_removing(),135 transfer_from_creating_removing()136 )137 }138139 fn burn_from() -> Weight {140 <SelfWeightOf<T>>::burn_from()141 }142143 fn burn_recursively_self_raw() -> Weight {144 // Read to get total balance145 Self::burn_item() + T::DbWeight::get().reads(1)146 }147 fn burn_recursively_breadth_raw(_amount: u32) -> Weight {148 // Refungible token can't have children149 Weight::zero()150 }151152 fn token_owner() -> Weight {153 <SelfWeightOf<T>>::token_owner()154 }155156 fn set_allowance_for_all() -> Weight {157 <SelfWeightOf<T>>::set_allowance_for_all()158 }159160 fn force_repair_item() -> Weight {161 <SelfWeightOf<T>>::repair_item()162 }163}164165fn map_create_data<T: Config>(166 data: up_data_structs::CreateItemData,167 to: &T::CrossAccountId,168) -> Result<CreateItemData<T>, DispatchError> {169 match data {170 up_data_structs::CreateItemData::ReFungible(data) => Ok(CreateItemData::<T> {171 users: {172 let mut out = BTreeMap::new();173 out.insert(to.clone(), data.pieces);174 out.try_into().expect("limit > 0")175 },176 properties: data.properties,177 }),178 _ => fail!(<Error<T>>::NotRefungibleDataUsedToMintFungibleCollectionToken),179 }180}181182/// Implementation of `CommonCollectionOperations` for `RefungibleHandle`. It wraps Refungible Pallete183/// methods and adds weight info.184impl<T: Config> CommonCollectionOperations<T> for RefungibleHandle<T> {185 fn create_item(186 &self,187 sender: T::CrossAccountId,188 to: T::CrossAccountId,189 data: up_data_structs::CreateItemData,190 nesting_budget: &dyn Budget,191 ) -> DispatchResultWithPostInfo {192 let weight = <CommonWeights<T>>::create_item(&data);193 with_weight(194 <Pallet<T>>::create_item(195 self,196 &sender,197 map_create_data::<T>(data, &to)?,198 nesting_budget,199 ),200 weight,201 )202 }203204 fn create_multiple_items(205 &self,206 sender: T::CrossAccountId,207 to: T::CrossAccountId,208 data: Vec<up_data_structs::CreateItemData>,209 nesting_budget: &dyn Budget,210 ) -> DispatchResultWithPostInfo {211 let weight = <CommonWeights<T>>::create_multiple_items(&data);212 let data = data213 .into_iter()214 .map(|d| map_create_data::<T>(d, &to))215 .collect::<Result<Vec<_>, DispatchError>>()?;216217 with_weight(218 <Pallet<T>>::create_multiple_items(self, &sender, data, nesting_budget),219 weight,220 )221 }222223 fn create_multiple_items_ex(224 &self,225 sender: <T>::CrossAccountId,226 data: CreateItemExData<T::CrossAccountId>,227 nesting_budget: &dyn Budget,228 ) -> DispatchResultWithPostInfo {229 let weight = <CommonWeights<T>>::create_multiple_items_ex(&data);230 let data = match data {231 CreateItemExData::RefungibleMultipleOwners(CreateRefungibleExMultipleOwners {232 users,233 properties,234 }) => vec![CreateItemData::<T> { users, properties }],235 CreateItemExData::RefungibleMultipleItems(r) => r236 .into_inner()237 .into_iter()238 .map(239 |CreateRefungibleExSingleOwner {240 user,241 pieces,242 properties,243 }| CreateItemData::<T> {244 users: BTreeMap::from([(user, pieces)])245 .try_into()246 .expect("limit >= 1"),247 properties,248 },249 )250 .collect(),251 _ => fail!(<Error<T>>::NotRefungibleDataUsedToMintFungibleCollectionToken),252 };253254 with_weight(255 <Pallet<T>>::create_multiple_items(self, &sender, data, nesting_budget),256 weight,257 )258 }259260 fn burn_item(261 &self,262 sender: T::CrossAccountId,263 token: TokenId,264 amount: u128,265 ) -> DispatchResultWithPostInfo {266 with_weight(267 <Pallet<T>>::burn(self, &sender, token, amount),268 <CommonWeights<T>>::burn_item(),269 )270 }271272 fn burn_item_recursively(273 &self,274 sender: T::CrossAccountId,275 token: TokenId,276 self_budget: &dyn Budget,277 _breadth_budget: &dyn Budget,278 ) -> DispatchResultWithPostInfo {279 ensure!(self_budget.consume(), <StructureError<T>>::DepthLimit,);280 with_weight(281 <Pallet<T>>::burn(282 self,283 &sender,284 token,285 <Balance<T>>::get((self.id, token, &sender)),286 ),287 <CommonWeights<T>>::burn_recursively_self_raw(),288 )289 }290291 fn transfer(292 &self,293 from: T::CrossAccountId,294 to: T::CrossAccountId,295 token: TokenId,296 amount: u128,297 nesting_budget: &dyn Budget,298 ) -> DispatchResultWithPostInfo {299 with_weight(300 <Pallet<T>>::transfer(self, &from, &to, token, amount, nesting_budget),301 <CommonWeights<T>>::transfer(),302 )303 }304305 fn approve(306 &self,307 sender: T::CrossAccountId,308 spender: T::CrossAccountId,309 token: TokenId,310 amount: u128,311 ) -> DispatchResultWithPostInfo {312 with_weight(313 <Pallet<T>>::set_allowance(self, &sender, &spender, token, amount),314 <CommonWeights<T>>::approve(),315 )316 }317318 fn approve_from(319 &self,320 sender: T::CrossAccountId,321 from: T::CrossAccountId,322 to: T::CrossAccountId,323 token_id: TokenId,324 amount: u128,325 ) -> DispatchResultWithPostInfo {326 with_weight(327 <Pallet<T>>::set_allowance_from(self, &sender, &from, &to, token_id, amount),328 <CommonWeights<T>>::approve_from(),329 )330 }331332 fn transfer_from(333 &self,334 sender: T::CrossAccountId,335 from: T::CrossAccountId,336 to: T::CrossAccountId,337 token: TokenId,338 amount: u128,339 nesting_budget: &dyn Budget,340 ) -> DispatchResultWithPostInfo {341 with_weight(342 <Pallet<T>>::transfer_from(self, &sender, &from, &to, token, amount, nesting_budget),343 <CommonWeights<T>>::transfer_from(),344 )345 }346347 fn burn_from(348 &self,349 sender: T::CrossAccountId,350 from: T::CrossAccountId,351 token: TokenId,352 amount: u128,353 nesting_budget: &dyn Budget,354 ) -> DispatchResultWithPostInfo {355 with_weight(356 <Pallet<T>>::burn_from(self, &sender, &from, token, amount, nesting_budget),357 <CommonWeights<T>>::burn_from(),358 )359 }360361 fn set_collection_properties(362 &self,363 sender: T::CrossAccountId,364 properties: Vec<Property>,365 ) -> DispatchResultWithPostInfo {366 let weight = <CommonWeights<T>>::set_collection_properties(properties.len() as u32);367368 with_weight(369 <Pallet<T>>::set_collection_properties(self, &sender, properties),370 weight,371 )372 }373374 fn delete_collection_properties(375 &self,376 sender: &T::CrossAccountId,377 property_keys: Vec<PropertyKey>,378 ) -> DispatchResultWithPostInfo {379 let weight = <CommonWeights<T>>::delete_collection_properties(property_keys.len() as u32);380381 with_weight(382 <Pallet<T>>::delete_collection_properties(self, sender, property_keys),383 weight,384 )385 }386387 fn set_token_properties(388 &self,389 sender: T::CrossAccountId,390 token_id: TokenId,391 properties: Vec<Property>,392 nesting_budget: &dyn Budget,393 ) -> DispatchResultWithPostInfo {394 let weight = <CommonWeights<T>>::set_token_properties(properties.len() as u32);395396 with_weight(397 <Pallet<T>>::set_token_properties(398 self,399 &sender,400 token_id,401 properties.into_iter(),402 pallet_common::SetPropertyMode::ExistingToken,403 nesting_budget,404 ),405 weight,406 )407 }408409 fn set_token_property_permissions(410 &self,411 sender: &T::CrossAccountId,412 property_permissions: Vec<PropertyKeyPermission>,413 ) -> DispatchResultWithPostInfo {414 let weight =415 <CommonWeights<T>>::set_token_property_permissions(property_permissions.len() as u32);416417 with_weight(418 <Pallet<T>>::set_token_property_permissions(self, sender, property_permissions),419 weight,420 )421 }422423 fn delete_token_properties(424 &self,425 sender: T::CrossAccountId,426 token_id: TokenId,427 property_keys: Vec<PropertyKey>,428 nesting_budget: &dyn Budget,429 ) -> DispatchResultWithPostInfo {430 let weight = <CommonWeights<T>>::delete_token_properties(property_keys.len() as u32);431432 with_weight(433 <Pallet<T>>::delete_token_properties(434 self,435 &sender,436 token_id,437 property_keys.into_iter(),438 nesting_budget,439 ),440 weight,441 )442 }443444 fn check_nesting(445 &self,446 _sender: <T>::CrossAccountId,447 _from: (CollectionId, TokenId),448 _under: TokenId,449 _nesting_budget: &dyn Budget,450 ) -> sp_runtime::DispatchResult {451 fail!(<Error<T>>::RefungibleDisallowsNesting)452 }453454 fn nest(&self, _under: TokenId, _to_nest: (CollectionId, TokenId)) {}455456 fn unnest(&self, _under: TokenId, _to_nest: (CollectionId, TokenId)) {}457458 fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId> {459 <Owned<T>>::iter_prefix((self.id, account))460 .map(|(id, _)| id)461 .collect()462 }463464 fn collection_tokens(&self) -> Vec<TokenId> {465 <TotalSupply<T>>::iter_prefix((self.id,))466 .map(|(id, _)| id)467 .collect()468 }469470 fn token_exists(&self, token: TokenId) -> bool {471 <Pallet<T>>::token_exists(self, token)472 }473474 fn last_token_id(&self) -> TokenId {475 TokenId(<TokensMinted<T>>::get(self.id))476 }477478 fn token_owner(&self, token: TokenId) -> Result<T::CrossAccountId, TokenOwnerError> {479 <Pallet<T>>::token_owner(self.id, token)480 }481482 /// Returns 10 token in no particular order.483 fn token_owners(&self, token: TokenId) -> Vec<T::CrossAccountId> {484 <Pallet<T>>::token_owners(self.id, token).unwrap_or_default()485 }486487 fn token_property(&self, token_id: TokenId, key: &PropertyKey) -> Option<PropertyValue> {488 <Pallet<T>>::token_properties((self.id, token_id))489 .get(key)490 .cloned()491 }492493 fn token_properties(&self, token_id: TokenId, keys: Option<Vec<PropertyKey>>) -> Vec<Property> {494 let properties = <Pallet<T>>::token_properties((self.id, token_id));495496 keys.map(|keys| {497 keys.into_iter()498 .filter_map(|key| {499 properties.get(&key).map(|value| Property {500 key,501 value: value.clone(),502 })503 })504 .collect()505 })506 .unwrap_or_else(|| {507 properties508 .into_iter()509 .map(|(key, value)| Property { key, value })510 .collect()511 })512 }513514 fn total_supply(&self) -> u32 {515 <Pallet<T>>::total_supply(self)516 }517518 fn account_balance(&self, account: T::CrossAccountId) -> u32 {519 <AccountBalance<T>>::get((self.id, account))520 }521522 fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128 {523 <Balance<T>>::get((self.id, token, account))524 }525526 fn allowance(527 &self,528 sender: T::CrossAccountId,529 spender: T::CrossAccountId,530 token: TokenId,531 ) -> u128 {532 <Allowance<T>>::get((self.id, token, sender, spender))533 }534535 fn refungible_extensions(&self) -> Option<&dyn RefungibleExtensions<T>> {536 Some(self)537 }538539 fn total_pieces(&self, token: TokenId) -> Option<u128> {540 <Pallet<T>>::total_pieces(self.id, token)541 }542543 fn set_allowance_for_all(544 &self,545 owner: T::CrossAccountId,546 operator: T::CrossAccountId,547 approve: bool,548 ) -> DispatchResultWithPostInfo {549 with_weight(550 <Pallet<T>>::set_allowance_for_all(self, &owner, &operator, approve),551 <CommonWeights<T>>::set_allowance_for_all(),552 )553 }554555 fn allowance_for_all(&self, owner: T::CrossAccountId, operator: T::CrossAccountId) -> bool {556 <Pallet<T>>::allowance_for_all(self, &owner, &operator)557 }558559 fn repair_item(&self, token: TokenId) -> DispatchResultWithPostInfo {560 with_weight(561 <Pallet<T>>::repair_item(self, token),562 <CommonWeights<T>>::force_repair_item(),563 )564 }565}566567impl<T: Config> RefungibleExtensions<T> for RefungibleHandle<T> {568 fn repartition(569 &self,570 owner: &T::CrossAccountId,571 token: TokenId,572 amount: u128,573 ) -> DispatchResultWithPostInfo {574 with_weight(575 <Pallet<T>>::repartition(self, owner, token, amount),576 <SelfWeightOf<T>>::repartition_item(),577 )578 }579}1// 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 sp_std::collections::btree_map::BTreeMap;20use frame_support::{dispatch::DispatchResultWithPostInfo, ensure, fail, weights::Weight, traits::Get};21use up_data_structs::{22 CollectionId, TokenId, CreateItemExData, budget::Budget, Property, PropertyKey, PropertyValue,23 PropertyKeyPermission, CreateRefungibleExMultipleOwners, CreateRefungibleExSingleOwner,24 TokenOwnerError,25};26use pallet_common::{27 CommonCollectionOperations, CommonWeightInfo, RefungibleExtensions, with_weight,28 weights::WeightInfo as _, init_token_properties_delta,29};30use pallet_structure::{Pallet as PalletStructure, Error as StructureError};31use sp_runtime::{DispatchError};32use sp_std::{vec::Vec, vec};3334use crate::{35 AccountBalance, Allowance, Balance, Config, Error, Owned, Pallet, RefungibleHandle,36 SelfWeightOf, weights::WeightInfo, TokensMinted, TotalSupply, CreateItemData, TokenProperties,37};3839macro_rules! max_weight_of {40 ($($method:ident ($($args:tt)*)),*) => {41 Weight::zero()42 $(43 .max(<SelfWeightOf<T>>::$method($($args)*))44 )*45 };46}4748pub struct CommonWeights<T: Config>(PhantomData<T>);49impl<T: Config> CommonWeightInfo<T::CrossAccountId> for CommonWeights<T> {50 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(|data| match data {54 up_data_structs::CreateItemData::ReFungible(rft_data) => {55 rft_data.properties.len() as u3256 }57 _ => 0,58 }),59 <SelfWeightOf<T>>::init_token_properties,60 ),61 )62 }6364 fn create_multiple_items_ex(call: &CreateItemExData<T::CrossAccountId>) -> Weight {65 match call {66 CreateItemExData::RefungibleMultipleOwners(i) => {67 <SelfWeightOf<T>>::create_multiple_items_ex_multiple_owners(i.users.len() as u32)68 .saturating_add(init_token_properties_delta::<T, _>(69 [i.properties.len() as u32].into_iter(),70 <SelfWeightOf<T>>::init_token_properties,71 ))72 }73 CreateItemExData::RefungibleMultipleItems(i) => {74 <SelfWeightOf<T>>::create_multiple_items_ex_multiple_items(i.len() as u32)75 .saturating_add(init_token_properties_delta::<T, _>(76 i.iter().map(|d| d.properties.len() as u32),77 <SelfWeightOf<T>>::init_token_properties,78 ))79 }80 _ => Weight::zero(),81 }82 }8384 fn burn_item() -> Weight {85 max_weight_of!(burn_item_partial(), burn_item_fully())86 }8788 fn set_collection_properties(amount: u32) -> Weight {89 <pallet_common::SelfWeightOf<T>>::set_collection_properties(amount)90 }9192 fn delete_collection_properties(amount: u32) -> Weight {93 <pallet_common::SelfWeightOf<T>>::delete_collection_properties(amount)94 }9596 fn set_token_properties(amount: u32) -> Weight {97 <SelfWeightOf<T>>::set_token_properties(amount)98 }99100 fn delete_token_properties(amount: u32) -> Weight {101 <SelfWeightOf<T>>::delete_token_properties(amount)102 }103104 fn set_token_property_permissions(amount: u32) -> Weight {105 <SelfWeightOf<T>>::set_token_property_permissions(amount)106 }107108 fn transfer() -> Weight {109 max_weight_of!(110 transfer_normal(),111 transfer_creating(),112 transfer_removing(),113 transfer_creating_removing()114 )115 }116117 fn approve() -> Weight {118 <SelfWeightOf<T>>::approve()119 }120121 fn approve_from() -> Weight {122 <SelfWeightOf<T>>::approve_from()123 }124125 fn transfer_from() -> Weight {126 max_weight_of!(127 transfer_from_normal(),128 transfer_from_creating(),129 transfer_from_removing(),130 transfer_from_creating_removing()131 )132 }133134 fn burn_from() -> Weight {135 <SelfWeightOf<T>>::burn_from()136 }137138 fn burn_recursively_self_raw() -> Weight {139 // Read to get total balance140 Self::burn_item() + T::DbWeight::get().reads(1)141 }142 fn burn_recursively_breadth_raw(_amount: u32) -> Weight {143 // Refungible token can't have children144 Weight::zero()145 }146147 fn token_owner() -> Weight {148 <SelfWeightOf<T>>::token_owner()149 }150151 fn set_allowance_for_all() -> Weight {152 <SelfWeightOf<T>>::set_allowance_for_all()153 }154155 fn force_repair_item() -> Weight {156 <SelfWeightOf<T>>::repair_item()157 }158}159160fn map_create_data<T: Config>(161 data: up_data_structs::CreateItemData,162 to: &T::CrossAccountId,163) -> Result<CreateItemData<T>, DispatchError> {164 match data {165 up_data_structs::CreateItemData::ReFungible(data) => Ok(CreateItemData::<T> {166 users: {167 let mut out = BTreeMap::new();168 out.insert(to.clone(), data.pieces);169 out.try_into().expect("limit > 0")170 },171 properties: data.properties,172 }),173 _ => fail!(<Error<T>>::NotRefungibleDataUsedToMintFungibleCollectionToken),174 }175}176177/// Implementation of `CommonCollectionOperations` for `RefungibleHandle`. It wraps Refungible Pallete178/// methods and adds weight info.179impl<T: Config> CommonCollectionOperations<T> for RefungibleHandle<T> {180 fn create_item(181 &self,182 sender: T::CrossAccountId,183 to: T::CrossAccountId,184 data: up_data_structs::CreateItemData,185 nesting_budget: &dyn Budget,186 ) -> DispatchResultWithPostInfo {187 let weight = <CommonWeights<T>>::create_item(&data);188 with_weight(189 <Pallet<T>>::create_item(190 self,191 &sender,192 map_create_data::<T>(data, &to)?,193 nesting_budget,194 ),195 weight,196 )197 }198199 fn create_multiple_items(200 &self,201 sender: T::CrossAccountId,202 to: T::CrossAccountId,203 data: Vec<up_data_structs::CreateItemData>,204 nesting_budget: &dyn Budget,205 ) -> DispatchResultWithPostInfo {206 let weight = <CommonWeights<T>>::create_multiple_items(&data);207 let data = data208 .into_iter()209 .map(|d| map_create_data::<T>(d, &to))210 .collect::<Result<Vec<_>, DispatchError>>()?;211212 with_weight(213 <Pallet<T>>::create_multiple_items(self, &sender, data, nesting_budget),214 weight,215 )216 }217218 fn create_multiple_items_ex(219 &self,220 sender: <T>::CrossAccountId,221 data: CreateItemExData<T::CrossAccountId>,222 nesting_budget: &dyn Budget,223 ) -> DispatchResultWithPostInfo {224 let weight = <CommonWeights<T>>::create_multiple_items_ex(&data);225 let data = match data {226 CreateItemExData::RefungibleMultipleOwners(CreateRefungibleExMultipleOwners {227 users,228 properties,229 }) => vec![CreateItemData::<T> { users, properties }],230 CreateItemExData::RefungibleMultipleItems(r) => r231 .into_inner()232 .into_iter()233 .map(234 |CreateRefungibleExSingleOwner {235 user,236 pieces,237 properties,238 }| CreateItemData::<T> {239 users: BTreeMap::from([(user, pieces)])240 .try_into()241 .expect("limit >= 1"),242 properties,243 },244 )245 .collect(),246 _ => fail!(<Error<T>>::NotRefungibleDataUsedToMintFungibleCollectionToken),247 };248249 with_weight(250 <Pallet<T>>::create_multiple_items(self, &sender, data, nesting_budget),251 weight,252 )253 }254255 fn burn_item(256 &self,257 sender: T::CrossAccountId,258 token: TokenId,259 amount: u128,260 ) -> DispatchResultWithPostInfo {261 with_weight(262 <Pallet<T>>::burn(self, &sender, token, amount),263 <CommonWeights<T>>::burn_item(),264 )265 }266267 fn burn_item_recursively(268 &self,269 sender: T::CrossAccountId,270 token: TokenId,271 self_budget: &dyn Budget,272 _breadth_budget: &dyn Budget,273 ) -> DispatchResultWithPostInfo {274 ensure!(self_budget.consume(), <StructureError<T>>::DepthLimit,);275 with_weight(276 <Pallet<T>>::burn(277 self,278 &sender,279 token,280 <Balance<T>>::get((self.id, token, &sender)),281 ),282 <CommonWeights<T>>::burn_recursively_self_raw(),283 )284 }285286 fn transfer(287 &self,288 from: T::CrossAccountId,289 to: T::CrossAccountId,290 token: TokenId,291 amount: u128,292 nesting_budget: &dyn Budget,293 ) -> DispatchResultWithPostInfo {294 with_weight(295 <Pallet<T>>::transfer(self, &from, &to, token, amount, nesting_budget),296 <CommonWeights<T>>::transfer(),297 )298 }299300 fn approve(301 &self,302 sender: T::CrossAccountId,303 spender: T::CrossAccountId,304 token: TokenId,305 amount: u128,306 ) -> DispatchResultWithPostInfo {307 with_weight(308 <Pallet<T>>::set_allowance(self, &sender, &spender, token, amount),309 <CommonWeights<T>>::approve(),310 )311 }312313 fn approve_from(314 &self,315 sender: T::CrossAccountId,316 from: T::CrossAccountId,317 to: T::CrossAccountId,318 token_id: TokenId,319 amount: u128,320 ) -> DispatchResultWithPostInfo {321 with_weight(322 <Pallet<T>>::set_allowance_from(self, &sender, &from, &to, token_id, amount),323 <CommonWeights<T>>::approve_from(),324 )325 }326327 fn transfer_from(328 &self,329 sender: T::CrossAccountId,330 from: T::CrossAccountId,331 to: T::CrossAccountId,332 token: TokenId,333 amount: u128,334 nesting_budget: &dyn Budget,335 ) -> DispatchResultWithPostInfo {336 with_weight(337 <Pallet<T>>::transfer_from(self, &sender, &from, &to, token, amount, nesting_budget),338 <CommonWeights<T>>::transfer_from(),339 )340 }341342 fn burn_from(343 &self,344 sender: T::CrossAccountId,345 from: T::CrossAccountId,346 token: TokenId,347 amount: u128,348 nesting_budget: &dyn Budget,349 ) -> DispatchResultWithPostInfo {350 with_weight(351 <Pallet<T>>::burn_from(self, &sender, &from, token, amount, nesting_budget),352 <CommonWeights<T>>::burn_from(),353 )354 }355356 fn set_collection_properties(357 &self,358 sender: T::CrossAccountId,359 properties: Vec<Property>,360 ) -> DispatchResultWithPostInfo {361 let weight = <CommonWeights<T>>::set_collection_properties(properties.len() as u32);362363 with_weight(364 <Pallet<T>>::set_collection_properties(self, &sender, properties),365 weight,366 )367 }368369 fn delete_collection_properties(370 &self,371 sender: &T::CrossAccountId,372 property_keys: Vec<PropertyKey>,373 ) -> DispatchResultWithPostInfo {374 let weight = <CommonWeights<T>>::delete_collection_properties(property_keys.len() as u32);375376 with_weight(377 <Pallet<T>>::delete_collection_properties(self, sender, property_keys),378 weight,379 )380 }381382 fn set_token_properties(383 &self,384 sender: T::CrossAccountId,385 token_id: TokenId,386 properties: Vec<Property>,387 nesting_budget: &dyn Budget,388 ) -> DispatchResultWithPostInfo {389 let weight = <CommonWeights<T>>::set_token_properties(properties.len() as u32);390391 with_weight(392 <Pallet<T>>::set_token_properties(393 self,394 &sender,395 token_id,396 properties.into_iter(),397 nesting_budget,398 ),399 weight,400 )401 }402403 fn set_token_property_permissions(404 &self,405 sender: &T::CrossAccountId,406 property_permissions: Vec<PropertyKeyPermission>,407 ) -> DispatchResultWithPostInfo {408 let weight =409 <CommonWeights<T>>::set_token_property_permissions(property_permissions.len() as u32);410411 with_weight(412 <Pallet<T>>::set_token_property_permissions(self, sender, property_permissions),413 weight,414 )415 }416417 fn delete_token_properties(418 &self,419 sender: T::CrossAccountId,420 token_id: TokenId,421 property_keys: Vec<PropertyKey>,422 nesting_budget: &dyn Budget,423 ) -> DispatchResultWithPostInfo {424 let weight = <CommonWeights<T>>::delete_token_properties(property_keys.len() as u32);425426 with_weight(427 <Pallet<T>>::delete_token_properties(428 self,429 &sender,430 token_id,431 property_keys.into_iter(),432 nesting_budget,433 ),434 weight,435 )436 }437438 fn get_token_properties_map(&self, token_id: TokenId) -> up_data_structs::TokenProperties {439 <TokenProperties<T>>::get((self.id, token_id))440 }441442 fn set_token_properties_map(&self, token_id: TokenId, map: up_data_structs::TokenProperties) {443 <TokenProperties<T>>::set((self.id, token_id), map)444 }445446 fn properties_exist(&self, token: TokenId) -> bool {447 <TokenProperties<T>>::contains_key((self.id, token))448 }449450 fn check_nesting(451 &self,452 _sender: <T>::CrossAccountId,453 _from: (CollectionId, TokenId),454 _under: TokenId,455 _nesting_budget: &dyn Budget,456 ) -> sp_runtime::DispatchResult {457 fail!(<Error<T>>::RefungibleDisallowsNesting)458 }459460 fn nest(&self, _under: TokenId, _to_nest: (CollectionId, TokenId)) {}461462 fn unnest(&self, _under: TokenId, _to_nest: (CollectionId, TokenId)) {}463464 fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId> {465 <Owned<T>>::iter_prefix((self.id, account))466 .map(|(id, _)| id)467 .collect()468 }469470 fn collection_tokens(&self) -> Vec<TokenId> {471 <TotalSupply<T>>::iter_prefix((self.id,))472 .map(|(id, _)| id)473 .collect()474 }475476 fn token_exists(&self, token: TokenId) -> bool {477 <Pallet<T>>::token_exists(self, token)478 }479480 fn last_token_id(&self) -> TokenId {481 TokenId(<TokensMinted<T>>::get(self.id))482 }483484 fn token_owner(&self, token: TokenId) -> Result<T::CrossAccountId, TokenOwnerError> {485 <Pallet<T>>::token_owner(self.id, token)486 }487488 fn check_token_indirect_owner(489 &self,490 token: TokenId,491 maybe_owner: &T::CrossAccountId,492 nesting_budget: &dyn Budget,493 ) -> Result<bool, DispatchError> {494 let balance = self.balance(maybe_owner.clone(), token);495 let total_pieces: u128 = <Pallet<T>>::total_pieces(self.id, token).unwrap_or(u128::MAX);496 if balance != total_pieces {497 return Ok(false);498 }499500 let is_bundle_owner = <PalletStructure<T>>::check_indirectly_owned(501 maybe_owner.clone(),502 self.id,503 token,504 None,505 nesting_budget,506 )?;507508 Ok(is_bundle_owner)509 }510511 /// Returns 10 token in no particular order.512 fn token_owners(&self, token: TokenId) -> Vec<T::CrossAccountId> {513 <Pallet<T>>::token_owners(self.id, token).unwrap_or_default()514 }515516 fn token_property(&self, token_id: TokenId, key: &PropertyKey) -> Option<PropertyValue> {517 <Pallet<T>>::token_properties((self.id, token_id))518 .get(key)519 .cloned()520 }521522 fn token_properties(&self, token_id: TokenId, keys: Option<Vec<PropertyKey>>) -> Vec<Property> {523 let properties = <Pallet<T>>::token_properties((self.id, token_id));524525 keys.map(|keys| {526 keys.into_iter()527 .filter_map(|key| {528 properties.get(&key).map(|value| Property {529 key,530 value: value.clone(),531 })532 })533 .collect()534 })535 .unwrap_or_else(|| {536 properties537 .into_iter()538 .map(|(key, value)| Property { key, value })539 .collect()540 })541 }542543 fn total_supply(&self) -> u32 {544 <Pallet<T>>::total_supply(self)545 }546547 fn account_balance(&self, account: T::CrossAccountId) -> u32 {548 <AccountBalance<T>>::get((self.id, account))549 }550551 fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128 {552 <Balance<T>>::get((self.id, token, account))553 }554555 fn allowance(556 &self,557 sender: T::CrossAccountId,558 spender: T::CrossAccountId,559 token: TokenId,560 ) -> u128 {561 <Allowance<T>>::get((self.id, token, sender, spender))562 }563564 fn refungible_extensions(&self) -> Option<&dyn RefungibleExtensions<T>> {565 Some(self)566 }567568 fn total_pieces(&self, token: TokenId) -> Option<u128> {569 <Pallet<T>>::total_pieces(self.id, token)570 }571572 fn set_allowance_for_all(573 &self,574 owner: T::CrossAccountId,575 operator: T::CrossAccountId,576 approve: bool,577 ) -> DispatchResultWithPostInfo {578 with_weight(579 <Pallet<T>>::set_allowance_for_all(self, &owner, &operator, approve),580 <CommonWeights<T>>::set_allowance_for_all(),581 )582 }583584 fn allowance_for_all(&self, owner: T::CrossAccountId, operator: T::CrossAccountId) -> bool {585 <Pallet<T>>::allowance_for_all(self, &owner, &operator)586 }587588 fn repair_item(&self, token: TokenId) -> DispatchResultWithPostInfo {589 with_weight(590 <Pallet<T>>::repair_item(self, token),591 <CommonWeights<T>>::force_repair_item(),592 )593 }594}595596impl<T: Config> RefungibleExtensions<T> for RefungibleHandle<T> {597 fn repartition(598 &self,599 owner: &T::CrossAccountId,600 token: TokenId,601 amount: u128,602 ) -> DispatchResultWithPostInfo {603 with_weight(604 <Pallet<T>>::repartition(self, owner, token, amount),605 <SelfWeightOf<T>>::repartition_item(),606 )607 }608}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));
}