difftreelog
refactor use type-safe propertywriter to set/delete properties
in: master
12 files changed
pallets/balances-adapter/src/common.rsdiffbeforeafterboth172 fail!(<pallet_common::Error<T>>::UnsupportedOperation);172 fail!(<pallet_common::Error<T>>::UnsupportedOperation);173 }173 }174175 fn get_token_properties_map(&self, _token_id: TokenId) -> up_data_structs::TokenProperties {176 // No token properties are defined on fungibles177 up_data_structs::TokenProperties::new()178 }179180 fn set_token_properties_map(&self, _token_id: TokenId, _map: up_data_structs::TokenProperties) {181 // No token properties are defined on fungibles182 }183184 fn properties_exist(&self, _token: TokenId) -> bool {185 // No token properties are defined on fungibles186 false187 }174188175 fn set_token_property_permissions(189 fn set_token_property_permissions(176 &self,190 &self,277 Err(up_data_structs::TokenOwnerError::MultipleOwners)291 Err(up_data_structs::TokenOwnerError::MultipleOwners)278 }292 }293294 fn check_token_indirect_owner(295 &self,296 _token: TokenId,297 _maybe_owner: &<T>::CrossAccountId,298 _nesting_budget: &dyn up_data_structs::budget::Budget,299 ) -> Result<bool, frame_support::sp_runtime::DispatchError> {300 Ok(false)301 }279302280 fn token_owners(&self, _token: TokenId) -> Vec<<T>::CrossAccountId> {303 fn token_owners(&self, _token: TokenId) -> Vec<<T>::CrossAccountId> {281 vec![]304 vec![]pallets/common/src/benchmarking.rsdiffbeforeafterboth22use frame_benchmarking::{benchmarks, account};22use frame_benchmarking::{benchmarks, account};23use up_data_structs::{23use up_data_structs::{24 CollectionMode, CreateCollectionData, CollectionId, Property, PropertyKey, PropertyValue,24 CollectionMode, CreateCollectionData, CollectionId, Property, PropertyKey, PropertyValue,25 CollectionPermissions, NestingPermissions, AccessMode, MAX_COLLECTION_NAME_LENGTH,25 CollectionPermissions, NestingPermissions, AccessMode, PropertiesPermissionMap,26 MAX_COLLECTION_DESCRIPTION_LENGTH, MAX_TOKEN_PREFIX_LENGTH, MAX_PROPERTIES_PER_ITEM,26 MAX_COLLECTION_NAME_LENGTH, MAX_COLLECTION_DESCRIPTION_LENGTH, MAX_TOKEN_PREFIX_LENGTH,27 MAX_PROPERTIES_PER_ITEM,27};28};123 )124 )124}125}126127pub fn load_is_admin_and_property_permissions<T: Config>(128 collection: &CollectionHandle<T>,129 sender: &T::CrossAccountId,130) -> (bool, PropertiesPermissionMap) {131 (132 collection.is_owner_or_admin(sender),133 <Pallet<T>>::property_permissions(collection.id),134 )135}125136126/// Helper macros, which handles all benchmarking preparation in semi-declarative way137/// Helper macros, which handles all benchmarking preparation in semi-declarative way127///138///216227217 }: {collection_handle.check_allowlist(&sender)?;}228 }: {collection_handle.check_allowlist(&sender)?;}229230 init_token_properties_common {231 bench_init!{232 owner: sub; collection: collection(owner);233 sender: sub;234 sender: cross_from_sub(sender);235 };236 }: {load_is_admin_and_property_permissions(&collection, &sender);}218}237}219238pallets/common/src/lib.rsdiffbeforeafterboth56use core::{56use core::{57 ops::{Deref, DerefMut},57 ops::{Deref, DerefMut},58 slice::from_ref,58 slice::from_ref,59 marker::PhantomData,59};60};60use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};61use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};61use sp_std::vec::Vec;62use sp_std::vec::Vec;98#[allow(missing_docs)]99#[allow(missing_docs)]99pub mod weights;100pub mod weights;101102use weights::WeightInfo;103100/// Weight info.104/// Weight info.101pub type SelfWeightOf<T> = <T as Config>::WeightInfo;105pub type SelfWeightOf<T> = <T as Config>::WeightInfo;865 >;869 >;866}870}867868/// Represents the change mode for the token property.869pub enum SetPropertyMode {870 /// The token already exists.871 ExistingToken,872873 /// New token.874 NewToken {875 /// The creator of the token is the recipient.876 mint_target_is_sender: bool,877 },878}879871880/// Value representation with delayed initialization time.872/// Value representation with delayed initialization time.881pub struct LazyValue<T, F: FnOnce() -> T> {873pub struct LazyValue<T, F: FnOnce() -> T> {892 }884 }893 }885 }894886895 /// Get the value. If it call furst time the value will be initialized.887 /// Get the value. If it is called the first time, the value will be initialized.896 pub fn value(&mut self) -> &T {888 pub fn value(&mut self) -> &T {897 if self.value.is_none() {889 self.compute_value_if_not_already();898 self.value = Some(self.f.take().unwrap()())899 }900901 self.value.as_ref().unwrap()890 self.value.as_ref().unwrap()902 }891 }892893 /// Get the value. If it is called the first time, the value will be initialized.894 pub fn value_mut(&mut self) -> &mut T {895 self.compute_value_if_not_already();896 self.value.as_mut().unwrap()897 }898899 fn into_inner(mut self) -> T {900 self.compute_value_if_not_already();901 self.value.unwrap()902 }903903904 /// Is value initialized.904 /// Is value initialized?905 pub fn has_value(&self) -> bool {905 pub fn has_value(&self) -> bool {906 self.value.is_some()906 self.value.is_some()907 }907 }908909 fn compute_value_if_not_already(&mut self) {910 if self.value.is_none() {911 self.value = Some(self.f.take().unwrap()())912 }913 }908}914}909915910fn check_token_permissions<T, FCA, FTO, FTE>(916fn check_token_permissions<T, FCA, FTO, FTE>(926 fail!(<Error<T>>::NoPermission);932 fail!(<Error<T>>::NoPermission);927 }933 }928934929 let token_certainly_exist = is_token_owner.has_value() && (*is_token_owner.value())?;935 let token_exist_due_to_owner_check_success =936 is_token_owner.has_value() && (*is_token_owner.value())?;937938 // If the token owner check has occurred and succeeded,939 // we know the token exists (otherwise, the owner check must fail).930 if !token_certainly_exist && !is_token_exist.value() {940 if !token_exist_due_to_owner_check_success {941 // If the token owner check didn't occur,942 // we must check the token's existence ourselves.943 if !is_token_exist.value() {931 fail!(<Error<T>>::TokenNotFound);944 fail!(<Error<T>>::TokenNotFound);932 }945 }946 }947933 Ok(())948 Ok(())934}949}1312 Ok(())1327 Ok(())1313 }1328 }13141315 /// A batch operation to add, edit or remove properties for a token.1316 /// It sets or removes a token's properties according to1317 /// `properties_updates` contents:1318 /// * sets a property under the <key> with the value provided `(<key>, Some(<value>))`1319 /// * removes a property under the <key> if the value is `None` `(<key>, None)`.1320 ///1321 /// All affected properties should have `mutable` permission1322 /// to be **deleted** or to be **set more than once**,1323 /// and the sender should have permission to edit those properties.1324 ///1325 /// This function fires an event for each property change.1326 /// In case of an error, all the changes (including the events) will be reverted1327 /// since the function is transactional.1328 #[allow(clippy::too_many_arguments)]1329 pub fn modify_token_properties<FTO, FTE>(1330 collection: &CollectionHandle<T>,1331 sender: &T::CrossAccountId,1332 token_id: TokenId,1333 is_token_exist: &mut LazyValue<bool, FTE>,1334 properties_updates: impl Iterator<Item = (PropertyKey, Option<PropertyValue>)>,1335 mut stored_properties: TokenProperties,1336 is_token_owner: &mut LazyValue<Result<bool, DispatchError>, FTO>,1337 set_token_properties: impl FnOnce(TokenProperties),1338 log: evm_coder::ethereum::Log,1339 ) -> DispatchResult1340 where1341 FTO: FnOnce() -> Result<bool, DispatchError>,1342 FTE: FnOnce() -> bool,1343 {1344 let mut is_collection_admin = LazyValue::new(|| collection.is_owner_or_admin(sender));1345 let mut permissions = LazyValue::new(|| Self::property_permissions(collection.id));13461347 let mut changed = false;1348 for (key, value) in properties_updates {1349 let permission = permissions1350 .value()1351 .get(&key)1352 .cloned()1353 .unwrap_or_else(PropertyPermission::none);13541355 let property_exists = stored_properties.get(&key).is_some();13561357 match permission {1358 PropertyPermission { mutable: false, .. } if property_exists => {1359 return Err(<Error<T>>::NoPermission.into());1360 }13611362 PropertyPermission {1363 collection_admin,1364 token_owner,1365 ..1366 } => check_token_permissions::<T, _, FTO, FTE>(1367 collection_admin,1368 token_owner,1369 &mut is_collection_admin,1370 is_token_owner,1371 is_token_exist,1372 )?,1373 }13741375 match value {1376 Some(value) => {1377 stored_properties1378 .try_set(key.clone(), value)1379 .map_err(<Error<T>>::from)?;13801381 Self::deposit_event(Event::TokenPropertySet(collection.id, token_id, key));1382 }1383 None => {1384 stored_properties.remove(&key).map_err(<Error<T>>::from)?;13851386 Self::deposit_event(Event::TokenPropertyDeleted(collection.id, token_id, key));1387 }1388 }13891390 changed = true;1391 }13921393 if changed {1394 <PalletEvm<T>>::deposit_log(log);1395 set_token_properties(stored_properties);1396 }13971398 Ok(())1399 }140013291401 /// Sets or unsets the approval of a given operator.1330 /// Sets or unsets the approval of a given operator.1402 ///1331 ///2166 budget: &dyn Budget,2095 budget: &dyn Budget,2167 ) -> DispatchResultWithPostInfo;2096 ) -> DispatchResultWithPostInfo;20972098 /// Get token properties raw map.2099 ///2100 /// * `token_id` - The token which properties are needed.2101 fn get_token_properties_map(&self, token_id: TokenId) -> TokenProperties;21022103 /// Set token properties raw map.2104 ///2105 /// * `token_id` - The token for which the properties are being set.2106 /// * `map` - The raw map containing the token's properties.2107 fn set_token_properties_map(&self, token_id: TokenId, map: TokenProperties);21082109 /// Whether the given token has properties.2110 ///2111 /// * `token_id` - The token in question.2112 fn properties_exist(&self, token: TokenId) -> bool;216821132169 /// Set token property permissions.2114 /// Set token property permissions.2170 ///2115 ///2309 /// * `token` - The token for which you need to find out the owner.2254 /// * `token` - The token for which you need to find out the owner.2310 fn token_owner(&self, token: TokenId) -> Result<T::CrossAccountId, TokenOwnerError>;2255 fn token_owner(&self, token: TokenId) -> Result<T::CrossAccountId, TokenOwnerError>;22562257 /// Checks if the `maybe_owner` is the indirect owner of the `token`.2258 ///2259 /// * `token` - Id token to check.2260 /// * `maybe_owner` - The account to check.2261 /// * `nesting_budget` - A budget that can be spent on nesting tokens.2262 fn check_token_indirect_owner(2263 &self,2264 token: TokenId,2265 maybe_owner: &T::CrossAccountId,2266 nesting_budget: &dyn Budget,2267 ) -> Result<bool, DispatchError>;231122682312 /// Returns 10 tokens owners in no particular order.2269 /// Returns 10 tokens owners in no particular order.2313 ///2270 ///2420 }2377 }2421}2378}23792380/// A marker structure that enables the writer implementation2381/// to provide the interface to write properties to **newly created** tokens.2382pub struct NewTokenPropertyWriter;23832384/// A marker structure that enables the writer implementation2385/// to provide the interface to write properties to **already existing** tokens.2386pub struct ExistingTokenPropertyWriter;23872388/// The type-safe interface for writing properties (setting or deleting) to tokens.2389/// It has two distinct implementations for newly created tokens and existing ones.2390///2391/// This type utilizes the lazy evaluation to avoid repeating the computation2392/// of several performance-heavy or PoV-heavy tasks,2393/// such as checking the indirect ownership or reading the token property permissions.2394pub struct PropertyWriter<2395 'a,2396 T,2397 Handle,2398 WriterVariant,2399 FIsAdmin,2400 FPropertyPermissions,2401 FCheckTokenExist,2402 FGetProperties,2403> where2404 T: Config,2405 FIsAdmin: FnOnce() -> bool,2406 FPropertyPermissions: FnOnce() -> PropertiesPermissionMap,2407{2408 collection: &'a Handle,2409 is_collection_admin: LazyValue<bool, FIsAdmin>,2410 property_permissions: LazyValue<PropertiesPermissionMap, FPropertyPermissions>,2411 check_token_exist: FCheckTokenExist,2412 get_properties: FGetProperties,2413 _phantom: PhantomData<(T, WriterVariant)>,2414}24152416impl<'a, T, Handle, FIsAdmin, FPropertyPermissions, FCheckTokenExist, FGetProperties>2417 PropertyWriter<2418 'a,2419 T,2420 Handle,2421 NewTokenPropertyWriter,2422 FIsAdmin,2423 FPropertyPermissions,2424 FCheckTokenExist,2425 FGetProperties,2426 > where2427 T: Config,2428 Handle: CommonCollectionOperations<T> + Deref<Target = CollectionHandle<T>>,2429 FIsAdmin: FnOnce() -> bool,2430 FPropertyPermissions: FnOnce() -> PropertiesPermissionMap,2431 FCheckTokenExist: Copy + FnOnce(TokenId) -> bool,2432 FGetProperties: Copy + FnOnce(TokenId) -> TokenProperties,2433{2434 /// A function to write properties to a **newly created** token.2435 pub fn write_token_properties(2436 &mut self,2437 mint_target_is_sender: bool,2438 token_id: TokenId,2439 properties_updates: impl Iterator<Item = Property>,2440 log: evm_coder::ethereum::Log,2441 ) -> DispatchResult {2442 self.internal_write_token_properties(2443 token_id,2444 properties_updates.map(|p| (p.key, Some(p.value))),2445 |_| Ok(mint_target_is_sender),2446 log,2447 )2448 }2449}24502451impl<'a, T, Handle, FIsAdmin, FPropertyPermissions, FCheckTokenExist, FGetProperties>2452 PropertyWriter<2453 'a,2454 T,2455 Handle,2456 ExistingTokenPropertyWriter,2457 FIsAdmin,2458 FPropertyPermissions,2459 FCheckTokenExist,2460 FGetProperties,2461 > where2462 T: Config,2463 Handle: CommonCollectionOperations<T> + Deref<Target = CollectionHandle<T>>,2464 FIsAdmin: FnOnce() -> bool,2465 FPropertyPermissions: FnOnce() -> PropertiesPermissionMap,2466 FCheckTokenExist: Copy + FnOnce(TokenId) -> bool,2467 FGetProperties: Copy + FnOnce(TokenId) -> TokenProperties,2468{2469 /// A function to write properties to an **already existing** token.2470 pub fn write_token_properties(2471 &mut self,2472 sender: &T::CrossAccountId,2473 token_id: TokenId,2474 properties_updates: impl Iterator<Item = (PropertyKey, Option<PropertyValue>)>,2475 nesting_budget: &dyn Budget,2476 log: evm_coder::ethereum::Log,2477 ) -> DispatchResult {2478 self.internal_write_token_properties(2479 token_id,2480 properties_updates,2481 |collection| collection.check_token_indirect_owner(token_id, sender, nesting_budget),2482 log,2483 )2484 }2485}24862487impl<2488 'a,2489 T,2490 Handle,2491 WriterVariant,2492 FIsAdmin,2493 FPropertyPermissions,2494 FCheckTokenExist,2495 FGetProperties,2496 >2497 PropertyWriter<2498 'a,2499 T,2500 Handle,2501 WriterVariant,2502 FIsAdmin,2503 FPropertyPermissions,2504 FCheckTokenExist,2505 FGetProperties,2506 > where2507 T: Config,2508 Handle: CommonCollectionOperations<T> + Deref<Target = CollectionHandle<T>>,2509 FIsAdmin: FnOnce() -> bool,2510 FPropertyPermissions: FnOnce() -> PropertiesPermissionMap,2511 FCheckTokenExist: Copy + FnOnce(TokenId) -> bool,2512 FGetProperties: Copy + FnOnce(TokenId) -> TokenProperties,2513{2514 fn internal_write_token_properties<FCheckTokenOwner>(2515 &mut self,2516 token_id: TokenId,2517 properties_updates: impl Iterator<Item = (PropertyKey, Option<PropertyValue>)>,2518 check_token_owner: FCheckTokenOwner,2519 log: evm_coder::ethereum::Log,2520 ) -> DispatchResult2521 where2522 FCheckTokenOwner: FnOnce(&Handle) -> Result<bool, DispatchError>,2523 {2524 let get_properties = self.get_properties;2525 let mut stored_properties = LazyValue::new(move || get_properties(token_id));25262527 let mut is_token_owner = LazyValue::new(|| check_token_owner(self.collection));25282529 let check_token_exist = self.check_token_exist;2530 let mut is_token_exist = LazyValue::new(move || check_token_exist(token_id));25312532 for (key, value) in properties_updates {2533 let permission = self2534 .property_permissions2535 .value()2536 .get(&key)2537 .cloned()2538 .unwrap_or_else(PropertyPermission::none);25392540 match permission {2541 PropertyPermission { mutable: false, .. }2542 if stored_properties.value().get(&key).is_some() =>2543 {2544 return Err(<Error<T>>::NoPermission.into());2545 }25462547 PropertyPermission {2548 collection_admin,2549 token_owner,2550 ..2551 } => check_token_permissions::<T, _, _, _>(2552 collection_admin,2553 token_owner,2554 &mut self.is_collection_admin,2555 &mut is_token_owner,2556 &mut is_token_exist,2557 )?,2558 }25592560 match value {2561 Some(value) => {2562 stored_properties2563 .value_mut()2564 .try_set(key.clone(), value)2565 .map_err(<Error<T>>::from)?;25662567 <Pallet<T>>::deposit_event(Event::TokenPropertySet(2568 self.collection.id,2569 token_id,2570 key,2571 ));2572 }2573 None => {2574 stored_properties2575 .value_mut()2576 .remove(&key)2577 .map_err(<Error<T>>::from)?;25782579 <Pallet<T>>::deposit_event(Event::TokenPropertyDeleted(2580 self.collection.id,2581 token_id,2582 key,2583 ));2584 }2585 }2586 }25872588 let properties_changed = stored_properties.has_value();2589 if properties_changed {2590 <PalletEvm<T>>::deposit_log(log);25912592 self.collection2593 .set_token_properties_map(token_id, stored_properties.into_inner());2594 }25952596 Ok(())2597 }2598}25992600/// Create a [`PropertyWriter`] for newly created tokens.2601pub fn property_writer_for_new_token<'a, T, Handle>(2602 collection: &'a Handle,2603 sender: &'a T::CrossAccountId,2604) -> PropertyWriter<2605 'a,2606 T,2607 Handle,2608 NewTokenPropertyWriter,2609 impl FnOnce() -> bool + 'a,2610 impl FnOnce() -> PropertiesPermissionMap + 'a,2611 impl Copy + FnOnce(TokenId) -> bool + 'a,2612 impl Copy + FnOnce(TokenId) -> TokenProperties + 'a,2613>2614where2615 T: Config,2616 Handle: CommonCollectionOperations<T> + Deref<Target = CollectionHandle<T>>,2617{2618 PropertyWriter {2619 collection,2620 is_collection_admin: LazyValue::new(|| collection.is_owner_or_admin(sender)),2621 property_permissions: LazyValue::new(|| <Pallet<T>>::property_permissions(collection.id)),2622 check_token_exist: |token_id| {2623 debug_assert!(collection.token_exists(token_id));2624 true2625 },2626 get_properties: |token_id| {2627 debug_assert!(!collection.properties_exist(token_id));2628 TokenProperties::new()2629 },2630 _phantom: PhantomData,2631 }2632}26332634#[cfg(feature = "runtime-benchmarks")]2635/// Create a `PropertyWriter` with preloaded `is_collection_admin` and `property_permissions.2636/// Also:2637/// * it will return `true` for the token ownership check.2638/// * it will return empty stored properties without reading them from the storage.2639pub fn collection_info_loaded_property_writer<T, Handle>(2640 collection: &Handle,2641 is_collection_admin: bool,2642 property_permissions: PropertiesPermissionMap,2643) -> PropertyWriter<2644 T,2645 Handle,2646 NewTokenPropertyWriter,2647 impl FnOnce() -> bool,2648 impl FnOnce() -> PropertiesPermissionMap,2649 impl Copy + FnOnce(TokenId) -> bool,2650 impl Copy + FnOnce(TokenId) -> TokenProperties,2651>2652where2653 T: Config,2654 Handle: CommonCollectionOperations<T> + Deref<Target = CollectionHandle<T>>,2655{2656 PropertyWriter {2657 collection,2658 is_collection_admin: LazyValue::new(move || is_collection_admin),2659 property_permissions: LazyValue::new(move || property_permissions),2660 check_token_exist: |_token_id| true,2661 get_properties: |_token_id| TokenProperties::new(),2662 _phantom: PhantomData,2663 }2664}26652666/// Create a [`PropertyWriter`] for already existing tokens.2667pub fn property_writer_for_existing_token<'a, T, Handle>(2668 collection: &'a Handle,2669 sender: &'a T::CrossAccountId,2670) -> PropertyWriter<2671 'a,2672 T,2673 Handle,2674 ExistingTokenPropertyWriter,2675 impl FnOnce() -> bool + 'a,2676 impl FnOnce() -> PropertiesPermissionMap + 'a,2677 impl Copy + FnOnce(TokenId) -> bool + 'a,2678 impl Copy + FnOnce(TokenId) -> TokenProperties + 'a,2679>2680where2681 T: Config,2682 Handle: CommonCollectionOperations<T> + Deref<Target = CollectionHandle<T>>,2683{2684 PropertyWriter {2685 collection,2686 is_collection_admin: LazyValue::new(|| collection.is_owner_or_admin(sender)),2687 property_permissions: LazyValue::new(|| <Pallet<T>>::property_permissions(collection.id)),2688 check_token_exist: |token_id| collection.token_exists(token_id),2689 get_properties: |token_id| collection.get_token_properties_map(token_id),2690 _phantom: PhantomData,2691 }2692}26932694/// Computes the weight delta for newly created tokens with properties.2695/// * `properties_nums` - The properties num of each created token.2696/// * `init_token_properties` - The function to obtain the weight from a token's properties num.2697pub fn init_token_properties_delta<T: Config, I: Fn(u32) -> Weight>(2698 properties_nums: impl Iterator<Item = u32>,2699 init_token_properties: I,2700) -> Weight {2701 let mut delta = properties_nums2702 .filter_map(|properties_num| {2703 if properties_num > 0 {2704 Some(init_token_properties(properties_num))2705 } else {2706 None2707 }2708 })2709 .fold(Weight::zero(), |a, b| a.saturating_add(b));27102711 // If at least once the `init_token_properties` was called,2712 // it means at least one newly created token has properties.2713 // Becuase of that, some common collection data also was loaded and we need to add this weight.2714 // However, these common data was loaded only once which is guaranteed by the `PropertyWriter`.2715 if !delta.is_zero() {2716 delta = delta.saturating_add(<SelfWeightOf<T>>::init_token_properties_common())2717 }27182719 delta2720}242227212423#[cfg(any(feature = "tests", test))]2722#[cfg(any(feature = "tests", test))]2424#[allow(missing_docs)]2723#[allow(missing_docs)]pallets/fungible/src/common.rsdiffbeforeafterboth25 weights::WeightInfo as _, SelfWeightOf as PalletCommonWeightOf,25 weights::WeightInfo as _, SelfWeightOf as PalletCommonWeightOf,26};26};27use pallet_structure::Error as StructureError;27use pallet_structure::Error as StructureError;28use sp_runtime::ArithmeticError;28use sp_runtime::{ArithmeticError, DispatchError};29use sp_std::{vec::Vec, vec};29use sp_std::{vec::Vec, vec};30use up_data_structs::{Property, PropertyKey, PropertyValue, PropertyKeyPermission};30use up_data_structs::{Property, PropertyKey, PropertyValue, PropertyKeyPermission};3131364 fail!(<Error<T>>::SettingPropertiesNotAllowed)364 fail!(<Error<T>>::SettingPropertiesNotAllowed)365 }365 }366367 fn get_token_properties_map(&self, _token_id: TokenId) -> up_data_structs::TokenProperties {368 // No token properties are defined on fungibles369 up_data_structs::TokenProperties::new()370 }371372 fn set_token_properties_map(&self, _token_id: TokenId, _map: up_data_structs::TokenProperties) {373 // No token properties are defined on fungibles374 }375376 fn properties_exist(&self, _token: TokenId) -> bool {377 // No token properties are defined on fungibles378 false379 }366380367 fn check_nesting(381 fn check_nesting(368 &self,382 &self,402 Err(TokenOwnerError::MultipleOwners)416 Err(TokenOwnerError::MultipleOwners)403 }417 }418419 fn check_token_indirect_owner(420 &self,421 _token: TokenId,422 _maybe_owner: &T::CrossAccountId,423 _nesting_budget: &dyn Budget,424 ) -> Result<bool, DispatchError> {425 Ok(false)426 }404427405 /// Returns 10 tokens owners in no particular order.428 /// Returns 10 tokens owners in no particular order.406 fn token_owners(&self, token: TokenId) -> Vec<T::CrossAccountId> {429 fn token_owners(&self, token: TokenId) -> Vec<T::CrossAccountId> {pallets/nonfungible/src/benchmarking.rsdiffbeforeafterboth21use pallet_common::{21use pallet_common::{22 bench_init,22 bench_init,23 benchmarking::{create_collection_raw, property_key, property_value},23 benchmarking::{24 create_collection_raw, property_key, property_value, load_is_admin_and_property_permissions,25 },24 CommonCollectionOperations,26 CommonCollectionOperations,25};27};198 value: property_value(),200 value: property_value(),199 }).collect::<Vec<_>>();201 }).collect::<Vec<_>>();200 let item = create_max_item(&collection, &owner, owner.clone())?;202 let item = create_max_item(&collection, &owner, owner.clone())?;201 }: {<Pallet<T>>::set_token_properties(&collection, &owner, item, props.into_iter(), SetPropertyMode::ExistingToken, &Unlimited)?}203 }: {<Pallet<T>>::set_token_properties(&collection, &owner, item, props.into_iter(), &Unlimited)?}202204203 reset_token_properties {205 init_token_properties {204 let b in 0..MAX_PROPERTIES_PER_ITEM;206 let b in 0..MAX_PROPERTIES_PER_ITEM;205 bench_init!{207 bench_init!{206 owner: sub; collection: collection(owner);208 owner: sub; collection: collection(owner);221 }).collect::<Vec<_>>();224 }).collect::<Vec<_>>();222 let item = create_max_item(&collection, &owner, owner.clone())?;225 let item = create_max_item(&collection, &owner, owner.clone())?;226227 let (is_collection_admin, property_permissions) = load_is_admin_and_property_permissions(&collection, &owner);223 }: {<Pallet<T>>::set_token_properties(&collection, &owner, item, props.into_iter(), SetPropertyMode::NewToken { mint_target_is_sender: true }, &Unlimited)?}228 }: {229 let mut property_writer = pallet_common::collection_info_loaded_property_writer(230 &collection,231 is_collection_admin,232 property_permissions,233 );234235 property_writer.write_token_properties(236 true,237 item,238 props.into_iter(),239 crate::erc::ERC721TokenEvent::TokenChanged {240 token_id: item.into(),241 }242 .to_log(T::ContractAddress::get()),243 )?244 }224245225 delete_token_properties {246 delete_token_properties {242 value: property_value(),263 value: property_value(),243 }).collect::<Vec<_>>();264 }).collect::<Vec<_>>();244 let item = create_max_item(&collection, &owner, owner.clone())?;265 let item = create_max_item(&collection, &owner, owner.clone())?;245 <Pallet<T>>::set_token_properties(&collection, &owner, item, props.into_iter(), SetPropertyMode::ExistingToken, &Unlimited)?;266 <Pallet<T>>::set_token_properties(&collection, &owner, item, props.into_iter(), &Unlimited)?;246 let to_delete = (0..b).map(|k| property_key(k as usize)).collect::<Vec<_>>();267 let to_delete = (0..b).map(|k| property_key(k as usize)).collect::<Vec<_>>();247 }: {<Pallet<T>>::delete_token_properties(&collection, &owner, item, to_delete.into_iter(), &Unlimited)?}268 }: {<Pallet<T>>::delete_token_properties(&collection, &owner, item, to_delete.into_iter(), &Unlimited)?}248269pallets/nonfungible/src/common.rsdiffbeforeafterboth23};23};24use pallet_common::{24use pallet_common::{25 CommonCollectionOperations, CommonWeightInfo, RefungibleExtensions, with_weight,25 CommonCollectionOperations, CommonWeightInfo, RefungibleExtensions, with_weight,26 weights::WeightInfo as _, SelfWeightOf as PalletCommonWeightOf,26 weights::WeightInfo as _, SelfWeightOf as PalletCommonWeightOf, init_token_properties_delta,27};27};28use pallet_structure::Pallet as PalletStructure;28use sp_runtime::DispatchError;29use sp_runtime::DispatchError;29use sp_std::{vec::Vec, vec};30use sp_std::{vec::Vec, vec};303131use crate::{32use crate::{32 AccountBalance, Allowance, Config, CreateItemData, Error, NonfungibleHandle, Owned, Pallet,33 AccountBalance, Allowance, Config, CreateItemData, Error, NonfungibleHandle, Owned, Pallet,33 SelfWeightOf, TokenData, weights::WeightInfo, TokensMinted,34 SelfWeightOf, TokenData, weights::WeightInfo, TokensMinted, TokenProperties,34};35};353636pub struct CommonWeights<T: Config>(PhantomData<T>);37pub struct CommonWeights<T: Config>(PhantomData<T>);37impl<T: Config> CommonWeightInfo<T::CrossAccountId> for CommonWeights<T> {38impl<T: Config> CommonWeightInfo<T::CrossAccountId> for CommonWeights<T> {38 fn create_multiple_items_ex(data: &CreateItemExData<T::CrossAccountId>) -> Weight {39 fn create_multiple_items_ex(data: &CreateItemExData<T::CrossAccountId>) -> Weight {39 match data {40 match data {40 CreateItemExData::NFT(t) => {41 CreateItemExData::NFT(t) => <SelfWeightOf<T>>::create_multiple_items_ex(t.len() as u32)41 <SelfWeightOf<T>>::create_multiple_items_ex(t.len() as u32)42 .saturating_add(init_token_properties_delta::<T, _>(42 + t.iter()43 t.iter().map(|t| t.properties.len() as u32),43 .filter_map(|t| {44 if t.properties.len() > 0 {45 Some(<SelfWeightOf<T>>::reset_token_properties(46 t.properties.len() as u32,47 ))48 } else {49 None50 }51 })52 .fold(Weight::zero(), |a, b| a.saturating_add(b))44 <SelfWeightOf<T>>::init_token_properties,53 }45 )),54 _ => Weight::zero(),46 _ => Weight::zero(),55 }47 }56 }48 }574958 fn create_multiple_items(data: &[up_data_structs::CreateItemData]) -> Weight {50 fn create_multiple_items(data: &[up_data_structs::CreateItemData]) -> Weight {59 <SelfWeightOf<T>>::create_multiple_items(data.len() as u32)51 <SelfWeightOf<T>>::create_multiple_items(data.len() as u32).saturating_add(52 init_token_properties_delta::<T, _>(60 + data53 data.iter().map(|t| match t {61 .iter()62 .filter_map(|t| match t {63 up_data_structs::CreateItemData::NFT(n) if n.properties.len() > 0 => Some(54 up_data_structs::CreateItemData::NFT(n) => n.properties.len() as u32,64 <SelfWeightOf<T>>::reset_token_properties(n.properties.len() as u32),65 ),66 _ => None,55 _ => 0,67 })56 }),68 .fold(Weight::zero(), |a, b| a.saturating_add(b))57 <SelfWeightOf<T>>::init_token_properties,58 ),59 )69 }60 }706171 fn burn_item() -> Weight {62 fn burn_item() -> Weight {247 &sender,238 &sender,248 token_id,239 token_id,249 properties.into_iter(),240 properties.into_iter(),250 pallet_common::SetPropertyMode::ExistingToken,251 nesting_budget,241 nesting_budget,252 ),242 ),253 weight,243 weight,275 )265 )276 }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 }277275278 fn set_token_property_permissions(276 fn set_token_property_permissions(279 &self,277 &self,289 )287 )290 }288 }289290 fn properties_exist(&self, token: TokenId) -> bool {291 <TokenProperties<T>>::contains_key((self.id, token))292 }291293292 fn burn_item(294 fn burn_item(293 &self,295 &self,459 .ok_or(TokenOwnerError::NotFound)461 .ok_or(TokenOwnerError::NotFound)460 }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 }461478462 /// Returns token owners.479 /// Returns token owners.463 fn token_owners(&self, token: TokenId) -> Vec<T::CrossAccountId> {480 fn token_owners(&self, token: TokenId) -> Vec<T::CrossAccountId> {pallets/nonfungible/src/erc.rsdiffbeforeafterboth203 &caller,203 &caller,204 TokenId(token_id),204 TokenId(token_id),205 properties.into_iter(),205 properties.into_iter(),206 pallet_common::SetPropertyMode::ExistingToken,207 &nesting_budget,206 &nesting_budget,208 )207 )209 .map_err(dispatch_to_evm::<T>)208 .map_err(dispatch_to_evm::<T>)pallets/nonfungible/src/lib.rsdiffbeforeafterboth109use pallet_common::{109use pallet_common::{110 Error as CommonError, Pallet as PalletCommon, Event as CommonEvent, CollectionHandle,110 Error as CommonError, Pallet as PalletCommon, Event as CommonEvent, CollectionHandle,111 eth::collection_id_to_address, SelfWeightOf as PalletCommonWeightOf,111 eth::collection_id_to_address, SelfWeightOf as PalletCommonWeightOf,112 weights::WeightInfo as CommonWeightInfo, helpers::add_weight_to_post_info, SetPropertyMode,112 weights::WeightInfo as CommonWeightInfo, helpers::add_weight_to_post_info,113};113};114use pallet_structure::{Pallet as PalletStructure, Error as StructureError};114use pallet_structure::{Pallet as PalletStructure, Error as StructureError};115use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};115use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};598 sender: &T::CrossAccountId,598 sender: &T::CrossAccountId,599 token_id: TokenId,599 token_id: TokenId,600 properties_updates: impl Iterator<Item = (PropertyKey, Option<PropertyValue>)>,600 properties_updates: impl Iterator<Item = (PropertyKey, Option<PropertyValue>)>,601 mode: SetPropertyMode,602 nesting_budget: &dyn Budget,601 nesting_budget: &dyn Budget,603 ) -> DispatchResult {602 ) -> DispatchResult {604 let mut is_token_owner = pallet_common::LazyValue::new(|| {603 let mut property_writer =605 if let SetPropertyMode::NewToken {606 mint_target_is_sender,607 } = mode608 {609 return Ok(mint_target_is_sender);610 }611612 let is_owned = <PalletStructure<T>>::check_indirectly_owned(613 sender.clone(),614 collection.id,604 pallet_common::property_writer_for_existing_token(collection, sender);615 token_id,616 None,617 nesting_budget,618 )?;619620 Ok(is_owned)621 });622623 let is_new_token = matches!(mode, SetPropertyMode::NewToken { .. });624625 let mut is_token_exist = pallet_common::LazyValue::new(|| {626 if is_new_token {627 debug_assert!(Self::token_exists(collection, token_id));628 true629 } else {630 Self::token_exists(collection, token_id)631 }632 });633634 let stored_properties = if is_new_token {635 debug_assert!(!<TokenProperties<T>>::contains_key((636 collection.id,637 token_id638 )));639 TokenPropertiesT::new()640 } else {641 <TokenProperties<T>>::get((collection.id, token_id))642 };643605644 <PalletCommon<T>>::modify_token_properties(606 property_writer.write_token_properties(645 collection,646 sender,607 sender,647 token_id,608 token_id,648 &mut is_token_exist,649 properties_updates,609 properties_updates,650 stored_properties,610 nesting_budget,651 &mut is_token_owner,652 |properties| <TokenProperties<T>>::set((collection.id, token_id), properties),653 erc::ERC721TokenEvent::TokenChanged {611 erc::ERC721TokenEvent::TokenChanged {654 token_id: token_id.into(),612 token_id: token_id.into(),655 }613 }680 sender: &T::CrossAccountId,638 sender: &T::CrossAccountId,681 token_id: TokenId,639 token_id: TokenId,682 properties: impl Iterator<Item = Property>,640 properties: impl Iterator<Item = Property>,683 mode: SetPropertyMode,684 nesting_budget: &dyn Budget,641 nesting_budget: &dyn Budget,685 ) -> DispatchResult {642 ) -> DispatchResult {686 Self::modify_token_properties(643 Self::modify_token_properties(687 collection,644 collection,688 sender,645 sender,689 token_id,646 token_id,690 properties.map(|p| (p.key, Some(p.value))),647 properties.map(|p| (p.key, Some(p.value))),691 mode,692 nesting_budget,648 nesting_budget,693 )649 )694 }650 }710 sender,666 sender,711 token_id,667 token_id,712 [property].into_iter(),668 [property].into_iter(),713 SetPropertyMode::ExistingToken,714 nesting_budget,669 nesting_budget,715 )670 )716 }671 }732 sender,687 sender,733 token_id,688 token_id,734 property_keys.into_iter().map(|key| (key, None)),689 property_keys.into_iter().map(|key| (key, None)),735 SetPropertyMode::ExistingToken,736 nesting_budget,690 nesting_budget,737 )691 )738 }692 }994948995 // =========949 // =========950951 let mut property_writer = pallet_common::property_writer_for_new_token(collection, sender);996952997 with_transaction(|| {953 with_transaction(|| {998 for (i, data) in data.iter().enumerate() {954 for (i, data) in data.iter().enumerate() {1006 },962 },1007 );963 );964965 let token = TokenId(token);10089661009 <PalletStructure<T>>::nest_if_sent_to_token_unchecked(967 <PalletStructure<T>>::nest_if_sent_to_token_unchecked(1010 &data.owner,968 &data.owner,1011 collection.id,969 collection.id,1012 TokenId(token),970 token,1013 );971 );10149721015 if let Err(e) = Self::set_token_properties(973 if let Err(e) = property_writer.write_token_properties(1016 collection,1017 sender,974 sender.conv_eq(&data.owner),1018 TokenId(token),975 token,1019 data.properties.clone().into_iter(),976 data.properties.clone().into_iter(),1020 SetPropertyMode::NewToken {977 erc::ERC721TokenEvent::TokenChanged {1021 mint_target_is_sender: sender.conv_eq(&data.owner),978 token_id: token.into(),1022 },979 }1023 nesting_budget,980 .to_log(T::ContractAddress::get()),1024 ) {981 ) {1025 return TransactionOutcome::Rollback(Err(e));982 return TransactionOutcome::Rollback(Err(e));1026 }983 }pallets/refungible/src/benchmarking.rsdiffbeforeafterboth23use pallet_common::{23use pallet_common::{24 bench_init,24 bench_init,25 benchmarking::{create_collection_raw, property_key, property_value},25 benchmarking::{26 create_collection_raw, property_key, property_value, load_is_admin_and_property_permissions,27 },26};28};27use sp_std::prelude::*;29use sp_std::prelude::*;255 value: property_value(),257 value: property_value(),256 }).collect::<Vec<_>>();258 }).collect::<Vec<_>>();257 let item = create_max_item(&collection, &owner, [(owner.clone(), 200)])?;259 let item = create_max_item(&collection, &owner, [(owner.clone(), 200)])?;258 }: {<Pallet<T>>::set_token_properties(&collection, &owner, item, props.into_iter(), SetPropertyMode::ExistingToken, &Unlimited)?}260 }: {<Pallet<T>>::set_token_properties(&collection, &owner, item, props.into_iter(), &Unlimited)?}259261260 reset_token_properties {262 init_token_properties {261 let b in 0..MAX_PROPERTIES_PER_ITEM;263 let b in 0..MAX_PROPERTIES_PER_ITEM;262 bench_init!{264 bench_init!{263 owner: sub; collection: collection(owner);265 owner: sub; collection: collection(owner);278 }).collect::<Vec<_>>();281 }).collect::<Vec<_>>();279 let item = create_max_item(&collection, &owner, [(owner.clone(), 200)])?;282 let item = create_max_item(&collection, &owner, [(owner.clone(), 200)])?;283284 let (is_collection_admin, property_permissions) = load_is_admin_and_property_permissions(&collection, &owner);280 }: {<Pallet<T>>::set_token_properties(&collection, &owner, item, props.into_iter(), SetPropertyMode::NewToken { mint_target_is_sender: true }, &Unlimited)?}285 }: {286 let mut property_writer = pallet_common::collection_info_loaded_property_writer(287 &collection,288 is_collection_admin,289 property_permissions,290 );291292 property_writer.write_token_properties(293 true,294 item,295 props.into_iter(),296 crate::erc::ERC721TokenEvent::TokenChanged {297 token_id: item.into(),298 }299 .to_log(T::ContractAddress::get()),300 )?301 }281302282 delete_token_properties {303 delete_token_properties {299 value: property_value(),320 value: property_value(),300 }).collect::<Vec<_>>();321 }).collect::<Vec<_>>();301 let item = create_max_item(&collection, &owner, [(owner.clone(), 200)])?;322 let item = create_max_item(&collection, &owner, [(owner.clone(), 200)])?;302 <Pallet<T>>::set_token_properties(&collection, &owner, item, props.into_iter(), SetPropertyMode::ExistingToken, &Unlimited)?;323 <Pallet<T>>::set_token_properties(&collection, &owner, item, props.into_iter(), &Unlimited)?;303 let to_delete = (0..b).map(|k| property_key(k as usize)).collect::<Vec<_>>();324 let to_delete = (0..b).map(|k| property_key(k as usize)).collect::<Vec<_>>();304 }: {<Pallet<T>>::delete_token_properties(&collection, &owner, item, to_delete.into_iter(), &Unlimited)?}325 }: {<Pallet<T>>::delete_token_properties(&collection, &owner, item, to_delete.into_iter(), &Unlimited)?}305326pallets/refungible/src/common.rsdiffbeforeafterboth20use frame_support::{dispatch::DispatchResultWithPostInfo, ensure, fail, weights::Weight, traits::Get};20use frame_support::{dispatch::DispatchResultWithPostInfo, ensure, fail, weights::Weight, traits::Get};21use up_data_structs::{21use up_data_structs::{22 CollectionId, TokenId, CreateItemExData, budget::Budget, Property, PropertyKey, PropertyValue,22 CollectionId, TokenId, CreateItemExData, budget::Budget, Property, PropertyKey, PropertyValue,23 PropertyKeyPermission, CollectionPropertiesVec, CreateRefungibleExMultipleOwners,23 PropertyKeyPermission, CreateRefungibleExMultipleOwners, CreateRefungibleExSingleOwner,24 CreateRefungibleExSingleOwner, TokenOwnerError,24 TokenOwnerError,25};25};26use pallet_common::{26use pallet_common::{27 CommonCollectionOperations, CommonWeightInfo, RefungibleExtensions, with_weight,27 CommonCollectionOperations, CommonWeightInfo, RefungibleExtensions, with_weight,28 weights::WeightInfo as _,28 weights::WeightInfo as _, init_token_properties_delta,29};29};30use pallet_structure::Error as StructureError;30use pallet_structure::{Pallet as PalletStructure, Error as StructureError};31use sp_runtime::{DispatchError};31use sp_runtime::{DispatchError};32use sp_std::{vec::Vec, vec};32use sp_std::{vec::Vec, vec};333334use crate::{34use crate::{35 AccountBalance, Allowance, Balance, Config, Error, Owned, Pallet, RefungibleHandle,35 AccountBalance, Allowance, Balance, Config, Error, Owned, Pallet, RefungibleHandle,36 SelfWeightOf, weights::WeightInfo, TokensMinted, TotalSupply, CreateItemData,36 SelfWeightOf, weights::WeightInfo, TokensMinted, TotalSupply, CreateItemData, TokenProperties,37};37};383839macro_rules! max_weight_of {39macro_rules! max_weight_of {45 };45 };46}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}554756pub struct CommonWeights<T: Config>(PhantomData<T>);48pub struct CommonWeights<T: Config>(PhantomData<T>);57impl<T: Config> CommonWeightInfo<T::CrossAccountId> for CommonWeights<T> {49impl<T: Config> CommonWeightInfo<T::CrossAccountId> for CommonWeights<T> {58 fn create_multiple_items(data: &[up_data_structs::CreateItemData]) -> Weight {50 fn create_multiple_items(data: &[up_data_structs::CreateItemData]) -> Weight {59 <SelfWeightOf<T>>::create_multiple_items(data.len() as u32).saturating_add(51 <SelfWeightOf<T>>::create_multiple_items(data.len() as u32).saturating_add(52 init_token_properties_delta::<T, _>(60 data.iter()53 data.iter().map(|data| match data {61 .map(|data| match data {62 up_data_structs::CreateItemData::ReFungible(rft_data) => {54 up_data_structs::CreateItemData::ReFungible(rft_data) => {63 properties_weight::<T>(&rft_data.properties)55 rft_data.properties.len() as u3264 }56 }65 _ => Weight::zero(),57 _ => 0,66 })58 }),67 .fold(Weight::zero(), |a, b| a.saturating_add(b)),59 <SelfWeightOf<T>>::init_token_properties,60 ),68 )61 )69 }62 }706371 fn create_multiple_items_ex(call: &CreateItemExData<T::CrossAccountId>) -> Weight {64 fn create_multiple_items_ex(call: &CreateItemExData<T::CrossAccountId>) -> Weight {72 match call {65 match call {73 CreateItemExData::RefungibleMultipleOwners(i) => {66 CreateItemExData::RefungibleMultipleOwners(i) => {74 <SelfWeightOf<T>>::create_multiple_items_ex_multiple_owners(i.users.len() as u32)67 <SelfWeightOf<T>>::create_multiple_items_ex_multiple_owners(i.users.len() as u32)75 .saturating_add(properties_weight::<T>(&i.properties))68 .saturating_add(init_token_properties_delta::<T, _>(69 [i.properties.len() as u32].into_iter(),70 <SelfWeightOf<T>>::init_token_properties,71 ))76 }72 }77 CreateItemExData::RefungibleMultipleItems(i) => {73 CreateItemExData::RefungibleMultipleItems(i) => {78 <SelfWeightOf<T>>::create_multiple_items_ex_multiple_items(i.len() as u32)74 <SelfWeightOf<T>>::create_multiple_items_ex_multiple_items(i.len() as u32)79 .saturating_add(75 .saturating_add(init_token_properties_delta::<T, _>(80 i.iter()76 i.iter().map(|d| d.properties.len() as u32),81 .map(|d| properties_weight::<T>(&d.properties))82 .fold(Weight::zero(), |a, b| a.saturating_add(b)),77 <SelfWeightOf<T>>::init_token_properties,83 )78 ))84 }79 }85 _ => Weight::zero(),80 _ => Weight::zero(),86 }81 }399 &sender,394 &sender,400 token_id,395 token_id,401 properties.into_iter(),396 properties.into_iter(),402 pallet_common::SetPropertyMode::ExistingToken,403 nesting_budget,397 nesting_budget,404 ),398 ),405 weight,399 weight,441 )435 )442 }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 }443449444 fn check_nesting(450 fn check_nesting(445 &self,451 &self,479 <Pallet<T>>::token_owner(self.id, token)485 <Pallet<T>>::token_owner(self.id, token)480 }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 }481510482 /// Returns 10 token in no particular order.511 /// Returns 10 token in no particular order.483 fn token_owners(&self, token: TokenId) -> Vec<T::CrossAccountId> {512 fn token_owners(&self, token: TokenId) -> Vec<T::CrossAccountId> {pallets/refungible/src/erc.rsdiffbeforeafterboth214 &caller,214 &caller,215 TokenId(token_id),215 TokenId(token_id),216 properties.into_iter(),216 properties.into_iter(),217 pallet_common::SetPropertyMode::ExistingToken,218 &nesting_budget,217 &nesting_budget,219 )218 )220 .map_err(dispatch_to_evm::<T>)219 .map_err(dispatch_to_evm::<T>)pallets/refungible/src/lib.rsdiffbeforeafterboth96use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm};96use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm};97use pallet_evm_coder_substrate::WithRecorder;97use pallet_evm_coder_substrate::WithRecorder;98use pallet_common::{98use pallet_common::{99 CommonCollectionOperations, Error as CommonError, eth::collection_id_to_address,99 Error as CommonError, eth::collection_id_to_address, Event as CommonEvent,100 Event as CommonEvent, Pallet as PalletCommon, SetPropertyMode,100 Pallet as PalletCommon,101};101};102use pallet_structure::Pallet as PalletStructure;102use pallet_structure::Pallet as PalletStructure;103use sp_core::{Get, H160};103use sp_core::{Get, H160};533 sender: &T::CrossAccountId,533 sender: &T::CrossAccountId,534 token_id: TokenId,534 token_id: TokenId,535 properties_updates: impl Iterator<Item = (PropertyKey, Option<PropertyValue>)>,535 properties_updates: impl Iterator<Item = (PropertyKey, Option<PropertyValue>)>,536 mode: SetPropertyMode,537 nesting_budget: &dyn Budget,536 nesting_budget: &dyn Budget,538 ) -> DispatchResult {537 ) -> DispatchResult {539 let mut is_token_owner =538 let mut property_writer =540 pallet_common::LazyValue::new(|| -> Result<bool, DispatchError> {539 pallet_common::property_writer_for_existing_token(collection, sender);541 if let SetPropertyMode::NewToken {542 mint_target_is_sender,543 } = mode544 {545 return Ok(mint_target_is_sender);546 }547548 let balance = collection.balance(sender.clone(), token_id);549 let total_pieces: u128 =550 Self::total_pieces(collection.id, token_id).unwrap_or(u128::MAX);551 if balance != total_pieces {552 return Ok(false);553 }554555 let is_bundle_owner = <PalletStructure<T>>::check_indirectly_owned(556 sender.clone(),557 collection.id,558 token_id,559 None,560 nesting_budget,561 )?;562563 Ok(is_bundle_owner)564 });565566 let is_new_token = matches!(mode, SetPropertyMode::NewToken { .. });567568 let mut is_token_exist = pallet_common::LazyValue::new(|| {569 if is_new_token {570 debug_assert!(Self::token_exists(collection, token_id));571 true572 } else {573 Self::token_exists(collection, token_id)574 }575 });576577 let stored_properties = if is_new_token {578 debug_assert!(!<TokenProperties<T>>::contains_key((579 collection.id,580 token_id581 )));582 TokenPropertiesT::new()583 } else {584 <TokenProperties<T>>::get((collection.id, token_id))585 };586540587 <PalletCommon<T>>::modify_token_properties(541 property_writer.write_token_properties(588 collection,589 sender,542 sender,590 token_id,543 token_id,591 &mut is_token_exist,592 properties_updates,544 properties_updates,593 stored_properties,545 nesting_budget,594 &mut is_token_owner,595 |properties| <TokenProperties<T>>::set((collection.id, token_id), properties),596 erc::ERC721TokenEvent::TokenChanged {546 erc::ERC721TokenEvent::TokenChanged {597 token_id: token_id.into(),547 token_id: token_id.into(),598 }548 }618 sender: &T::CrossAccountId,568 sender: &T::CrossAccountId,619 token_id: TokenId,569 token_id: TokenId,620 properties: impl Iterator<Item = Property>,570 properties: impl Iterator<Item = Property>,621 mode: SetPropertyMode,622 nesting_budget: &dyn Budget,571 nesting_budget: &dyn Budget,623 ) -> DispatchResult {572 ) -> DispatchResult {624 Self::modify_token_properties(573 Self::modify_token_properties(625 collection,574 collection,626 sender,575 sender,627 token_id,576 token_id,628 properties.map(|p| (p.key, Some(p.value))),577 properties.map(|p| (p.key, Some(p.value))),629 mode,630 nesting_budget,578 nesting_budget,631 )579 )632 }580 }643 sender,591 sender,644 token_id,592 token_id,645 [property].into_iter(),593 [property].into_iter(),646 SetPropertyMode::ExistingToken,647 nesting_budget,594 nesting_budget,648 )595 )649 }596 }660 sender,607 sender,661 token_id,608 token_id,662 property_keys.into_iter().map(|key| (key, None)),609 property_keys.into_iter().map(|key| (key, None)),663 SetPropertyMode::ExistingToken,664 nesting_budget,610 nesting_budget,665 )611 )666 }612 }941887942 // =========888 // =========889890 let mut property_writer = pallet_common::property_writer_for_new_token(collection, sender);943891944 with_transaction(|| {892 with_transaction(|| {945 for (i, data) in data.iter().enumerate() {893 for (i, data) in data.iter().enumerate() {946 let token_id = first_token_id + i as u32 + 1;894 let token_id = first_token_id + i as u32 + 1;947 <TotalSupply<T>>::insert((collection.id, token_id), totals[i]);895 <TotalSupply<T>>::insert((collection.id, token_id), totals[i]);896897 let token = TokenId(token_id);948898949 let mut mint_target_is_sender = true;899 let mut mint_target_is_sender = true;950 for (user, amount) in data.users.iter() {900 for (user, amount) in data.users.iter() {955 mint_target_is_sender = mint_target_is_sender && sender.conv_eq(user);905 mint_target_is_sender = mint_target_is_sender && sender.conv_eq(user);956906957 <Balance<T>>::insert((collection.id, token_id, &user), amount);907 <Balance<T>>::insert((collection.id, token_id, &user), amount);958 <Owned<T>>::insert((collection.id, &user, TokenId(token_id)), true);908 <Owned<T>>::insert((collection.id, &user, token), true);959 <PalletStructure<T>>::nest_if_sent_to_token_unchecked(909 <PalletStructure<T>>::nest_if_sent_to_token_unchecked(960 user,910 user,961 collection.id,911 collection.id,962 TokenId(token_id),912 token,963 );913 );964 }914 }965915966 if let Err(e) = Self::set_token_properties(916 if let Err(e) = property_writer.write_token_properties(967 collection,917 mint_target_is_sender,968 sender,918 token,969 TokenId(token_id),970 data.properties.clone().into_iter(),919 data.properties.clone().into_iter(),971 SetPropertyMode::NewToken {920 erc::ERC721TokenEvent::TokenChanged {972 mint_target_is_sender,921 token_id: token.into(),973 },922 }974 nesting_budget,923 .to_log(T::ContractAddress::get()),975 ) {924 ) {976 return TransactionOutcome::Rollback(Err(e));925 return TransactionOutcome::Rollback(Err(e));977 }926 }