git.delta.rocks / unique-network / refs/commits / 59e224731e80

difftreelog

feat benchmark property calls

Yaroslav Bolyukin2022-05-23parent: #fd1b071.patch.diff
in: master

30 files changed

modifiedCargo.lockdiffbeforeafterboth
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -5913,6 +5913,7 @@
 dependencies = [
  "evm-coder",
  "fp-evm-mapping",
+ "frame-benchmarking",
  "frame-support",
  "frame-system",
  "pallet-evm",
@@ -6649,6 +6650,7 @@
  "frame-support",
  "frame-system",
  "pallet-common",
+ "pallet-evm",
  "parity-scale-codec 3.1.2",
  "scale-info",
  "sp-std",
modifiedMakefilediffbeforeafterboth
--- a/Makefile
+++ b/Makefile
@@ -41,6 +41,10 @@
 bench-evm-migration:
 	make _bench PALLET=evm-migration
 
+.PHONY: bench-common
+bench-common:
+	make _bench PALLET=common
+
 .PHONY: bench-unique
 bench-unique:
 	make _bench PALLET=unique
modifiedpallets/common/Cargo.tomldiffbeforeafterboth
--- a/pallets/common/Cargo.toml
+++ b/pallets/common/Cargo.toml
@@ -25,6 +25,7 @@
 scale-info = { version = "2.0.1", default-features = false, features = [
     "derive",
 ] }
+frame-benchmarking = { default-features = false, optional = true, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.21" }
 
 [features]
 default = ["std"]
@@ -37,4 +38,6 @@
     "up-data-structs/std",
     "pallet-evm/std",
 ]
-runtime-benchmarks = []
+runtime-benchmarks = [
+    "frame-benchmarking"
+]
modifiedpallets/common/src/benchmarking.rsdiffbeforeafterboth
--- a/pallets/common/src/benchmarking.rs
+++ b/pallets/common/src/benchmarking.rs
@@ -15,11 +15,13 @@
 // along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
 
 use sp_std::vec::Vec;
-use crate::{Config, CollectionHandle};
+use crate::{Config, CollectionHandle, Pallet};
+use pallet_evm::account::CrossAccountId;
+use frame_benchmarking::{benchmarks, account};
 use up_data_structs::{
-	CollectionMode, CreateCollectionData, CollectionId, MAX_COLLECTION_NAME_LENGTH,
-	MAX_COLLECTION_DESCRIPTION_LENGTH, MAX_TOKEN_PREFIX_LENGTH, OFFCHAIN_SCHEMA_LIMIT,
-	CONST_ON_CHAIN_SCHEMA_LIMIT,
+	CollectionMode, CreateCollectionData, CollectionId, Property, PropertyKey, PropertyValue,
+	MAX_COLLECTION_NAME_LENGTH, MAX_COLLECTION_DESCRIPTION_LENGTH, MAX_TOKEN_PREFIX_LENGTH,
+	OFFCHAIN_SCHEMA_LIMIT, CONST_ON_CHAIN_SCHEMA_LIMIT, MAX_PROPERTIES_PER_ITEM,
 };
 use frame_support::{
 	traits::{Currency, Get},
@@ -29,6 +31,8 @@
 use core::convert::TryInto;
 use sp_runtime::DispatchError;
 
+const SEED: u32 = 1;
+
 pub fn create_data<const S: u32>() -> BoundedVec<u8, ConstU32<S>> {
 	create_var_data::<S>(S)
 }
@@ -52,6 +56,22 @@
 		.try_into()
 		.unwrap()
 }
+pub fn property_key(id: usize) -> PropertyKey {
+	#[cfg(not(feature = "std"))]
+	use alloc::string::ToString;
+	let mut data = create_data();
+	// No DerefMut available for .fill
+	for i in 0..data.len() {
+		data[i] = b'0';
+	}
+	let bytes = id.to_string();
+	let len = data.len();
+	data[len - bytes.len()..].copy_from_slice(&bytes.as_bytes());
+	data
+}
+pub fn property_value() -> PropertyValue {
+	create_data()
+}
 
 pub fn create_collection_raw<T: Config, R>(
 	owner: T::AccountId,
@@ -83,6 +103,14 @@
 	.and_then(CollectionHandle::try_get)
 	.map(cast)
 }
+fn create_collection<T: Config>(owner: T::AccountId) -> Result<CollectionHandle<T>, DispatchError> {
+	create_collection_raw(
+		owner,
+		CollectionMode::NFT,
+		|owner, data| <Pallet<T>>::init_collection(owner, data),
+		|h| h,
+	)
+}
 
 /// Helper macros, which handles all benchmarking preparation in semi-declarative way
 ///
@@ -125,3 +153,31 @@
 	};
 	() => {}
 }
+
+benchmarks! {
+	set_collection_properties {
+		let b in 0..MAX_PROPERTIES_PER_ITEM;
+		bench_init!{
+			owner: sub; collection: collection(owner);
+			owner: cross_from_sub;
+		};
+		let props = (0..b).map(|p| Property {
+			key: property_key(p as usize),
+			value: property_value(),
+		}).collect::<Vec<_>>();
+	}: {<Pallet<T>>::set_collection_properties(&collection, &owner, props)?}
+
+	delete_collection_properties {
+		let b in 0..MAX_PROPERTIES_PER_ITEM;
+		bench_init!{
+			owner: sub; collection: collection(owner);
+			owner: cross_from_sub;
+		};
+		let props = (0..b).map(|p| Property {
+			key: property_key(p as usize),
+			value: property_value(),
+		}).collect::<Vec<_>>();
+		<Pallet<T>>::set_collection_properties(&collection, &owner, props)?;
+		let to_delete = (0..b).map(|p| property_key(p as usize)).collect::<Vec<_>>();
+	}: {<Pallet<T>>::delete_collection_properties(&collection, &owner, to_delete)?}
+}
modifiedpallets/common/src/lib.rsdiffbeforeafterboth
--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -16,6 +16,8 @@
 
 #![cfg_attr(not(feature = "std"), no_std)]
 
+extern crate alloc;
+
 use core::ops::{Deref, DerefMut};
 use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};
 use sp_std::vec::Vec;
@@ -85,6 +87,9 @@
 pub mod dispatch;
 pub mod erc;
 pub mod eth;
+pub mod weights;
+
+pub type SelfWeightOf<T> = <T as Config>::WeightInfo;
 
 #[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]
 pub struct CollectionHandle<T: Config> {
@@ -186,11 +191,13 @@
 	use frame_support::traits::Currency;
 	use up_data_structs::{TokenId, mapping::TokenAddressMapping};
 	use scale_info::TypeInfo;
+	use weights::WeightInfo;
 
 	#[pallet::config]
 	pub trait Config:
 		frame_system::Config + pallet_evm_coder_substrate::Config + TypeInfo + account::Config
 	{
+		type WeightInfo: WeightInfo;
 		type Event: IsType<<Self as frame_system::Config>::Event> + From<Event<Self>>;
 
 		type Currency: Currency<Self::AccountId>;
@@ -804,7 +811,7 @@
 	pub fn set_scoped_collection_properties(
 		collection: &CollectionHandle<T>,
 		scope: PropertyScope,
-		properties: impl Iterator<Item=Property>,
+		properties: impl Iterator<Item = Property>,
 	) -> DispatchResult {
 		CollectionProperties::<T>::try_mutate(collection.id, |stored_properties| {
 			stored_properties.try_scoped_set_from_iter(scope, properties)
@@ -903,10 +910,11 @@
 		Ok(())
 	}
 
-	pub fn get_collection_property(collection_id: CollectionId, key: &PropertyKey) -> Option<PropertyValue> {
-		Self::collection_properties(collection_id)
-			.get(key)
-			.cloned()
+	pub fn get_collection_property(
+		collection_id: CollectionId,
+		key: &PropertyKey,
+	) -> Option<PropertyValue> {
+		Self::collection_properties(collection_id).get(key).cloned()
 	}
 
 	pub fn bytes_keys_to_property_keys(
@@ -1131,7 +1139,7 @@
 /// Worst cases
 pub trait CommonWeightInfo<CrossAccountId> {
 	fn create_item() -> Weight;
-	fn create_multiple_items(amount: u32) -> Weight;
+	fn create_multiple_items(amount: &[CreateItemData]) -> Weight;
 	fn create_multiple_items_ex(cost: &CreateItemExData<CrossAccountId>) -> Weight;
 	fn burn_item() -> Weight;
 	fn set_collection_properties(amount: u32) -> Weight;
addedpallets/common/src/weights.rsdiffbeforeafterboth
--- /dev/null
+++ b/pallets/common/src/weights.rs
@@ -0,0 +1,79 @@
+// Template adopted from https://github.com/paritytech/substrate/blob/master/.maintain/frame-weight-template.hbs
+
+//! Autogenerated weights for pallet_common
+//!
+//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
+//! DATE: 2022-05-23, STEPS: `50`, REPEAT: 1, LOW RANGE: `[]`, HIGH RANGE: `[]`
+//! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024
+
+// Executed Command:
+// target/release/unique-collator
+// benchmark
+// pallet
+// --pallet
+// pallet-common
+// --wasm-execution
+// compiled
+// --extrinsic
+// *
+// --template
+// .maintain/frame-weight-template.hbs
+// --steps=50
+// --repeat=1
+// --heap-pages=4096
+// --output=./pallets/common/src/weights.rs
+
+#![cfg_attr(rustfmt, rustfmt_skip)]
+#![allow(unused_parens)]
+#![allow(unused_imports)]
+#![allow(clippy::unnecessary_cast)]
+
+use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};
+use sp_std::marker::PhantomData;
+
+/// Weight functions needed for pallet_common.
+pub trait WeightInfo {
+	fn set_collection_properties(b: u32, ) -> Weight;
+	fn delete_collection_properties(b: u32, ) -> Weight;
+}
+
+/// Weights for pallet_common using the Substrate node and recommended hardware.
+pub struct SubstrateWeight<T>(PhantomData<T>);
+impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
+	// Storage: Common CollectionProperties (r:1 w:1)
+	fn set_collection_properties(b: u32, ) -> Weight {
+		(0 as Weight)
+			// Standard Error: 142_818_000
+			.saturating_add((2_786_252_000 as Weight).saturating_mul(b as Weight))
+			.saturating_add(T::DbWeight::get().reads(1 as Weight))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+	}
+	// Storage: Common CollectionProperties (r:1 w:1)
+	fn delete_collection_properties(b: u32, ) -> Weight {
+		(0 as Weight)
+			// Standard Error: 101_087_000
+			.saturating_add((2_739_521_000 as Weight).saturating_mul(b as Weight))
+			.saturating_add(T::DbWeight::get().reads(1 as Weight))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+	}
+}
+
+// For backwards compatibility and tests
+impl WeightInfo for () {
+	// Storage: Common CollectionProperties (r:1 w:1)
+	fn set_collection_properties(b: u32, ) -> Weight {
+		(0 as Weight)
+			// Standard Error: 142_818_000
+			.saturating_add((2_786_252_000 as Weight).saturating_mul(b as Weight))
+			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+	}
+	// Storage: Common CollectionProperties (r:1 w:1)
+	fn delete_collection_properties(b: u32, ) -> Weight {
+		(0 as Weight)
+			// Standard Error: 101_087_000
+			.saturating_add((2_739_521_000 as Weight).saturating_mul(b as Weight))
+			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+	}
+}
modifiedpallets/fungible/src/benchmarking.rsdiffbeforeafterboth
--- a/pallets/fungible/src/benchmarking.rs
+++ b/pallets/fungible/src/benchmarking.rs
@@ -40,7 +40,7 @@
 			owner: sub; collection: collection(owner);
 			sender: cross_from_sub(owner); to: cross_sub;
 		};
-	}: {<Pallet<T>>::create_item(&collection, &sender, (to, 200))?}
+	}: {<Pallet<T>>::create_item(&collection, &sender, (to, 200), &Unlimited)?}
 
 	create_multiple_items_ex {
 		let b in 0..MAX_ITEMS_PER_BATCH;
@@ -52,14 +52,14 @@
 			bench_init!(to: cross_sub(i););
 			(to, 200)
 		}).collect::<BTreeMap<_, _>>().try_into().unwrap();
-	}: {<Pallet<T>>::create_multiple_items(&collection, &sender, data)?}
+	}: {<Pallet<T>>::create_multiple_items(&collection, &sender, data, &Unlimited)?}
 
 	burn_item {
 		bench_init!{
 			owner: sub; collection: collection(owner);
 			owner: cross_from_sub; burner: cross_sub;
 		};
-		<Pallet<T>>::create_item(&collection, &owner, (burner.clone(), 200))?;
+		<Pallet<T>>::create_item(&collection, &owner, (burner.clone(), 200), &Unlimited)?;
 	}: {<Pallet<T>>::burn(&collection, &burner, 100)?}
 
 	transfer {
@@ -67,15 +67,15 @@
 			owner: sub; collection: collection(owner);
 			owner: cross_from_sub; sender: cross_sub; to: cross_sub;
 		};
-		<Pallet<T>>::create_item(&collection, &owner, (sender.clone(), 200))?;
-	}: {<Pallet<T>>::transfer(&collection, &sender, &to, 200)?}
+		<Pallet<T>>::create_item(&collection, &owner, (sender.clone(), 200), &Unlimited)?;
+	}: {<Pallet<T>>::transfer(&collection, &sender, &to, 200, &Unlimited)?}
 
 	approve {
 		bench_init!{
 			owner: sub; collection: collection(owner);
 			owner: cross_from_sub; sender: cross_sub; spender: cross_sub;
 		};
-		<Pallet<T>>::create_item(&collection, &owner, (sender.clone(), 200))?;
+		<Pallet<T>>::create_item(&collection, &owner, (sender.clone(), 200), &Unlimited)?;
 	}: {<Pallet<T>>::set_allowance(&collection, &sender, &spender, 100)?}
 
 	transfer_from {
@@ -83,7 +83,7 @@
 			owner: sub; collection: collection(owner);
 			owner: cross_from_sub; sender: cross_sub; spender: cross_sub; receiver: cross_sub;
 		};
-		<Pallet<T>>::create_item(&collection, &owner, (sender.clone(), 200))?;
+		<Pallet<T>>::create_item(&collection, &owner, (sender.clone(), 200), &Unlimited)?;
 		<Pallet<T>>::set_allowance(&collection, &sender, &spender, 200)?;
 	}: {<Pallet<T>>::transfer_from(&collection, &spender, &sender, &receiver, 100, &Unlimited)?}
 
@@ -92,7 +92,7 @@
 			owner: sub; collection: collection(owner);
 			owner: cross_from_sub; sender: cross_sub; burner: cross_sub;
 		};
-		<Pallet<T>>::create_item(&collection, &owner, (sender.clone(), 200))?;
+		<Pallet<T>>::create_item(&collection, &owner, (sender.clone(), 200), &Unlimited)?;
 		<Pallet<T>>::set_allowance(&collection, &sender, &burner, 200)?;
 	}: {<Pallet<T>>::burn_from(&collection, &burner, &sender, 100, &Unlimited)?}
 }
modifiedpallets/fungible/src/common.rsdiffbeforeafterboth
--- a/pallets/fungible/src/common.rs
+++ b/pallets/fungible/src/common.rs
@@ -17,7 +17,7 @@
 use core::marker::PhantomData;
 
 use frame_support::{dispatch::DispatchResultWithPostInfo, ensure, fail, weights::Weight};
-use up_data_structs::{TokenId, CollectionId, CreateItemExData, budget::Budget};
+use up_data_structs::{TokenId, CollectionId, CreateItemExData, budget::Budget, CreateItemData};
 use pallet_common::{CommonCollectionOperations, CommonWeightInfo, with_weight};
 use sp_runtime::ArithmeticError;
 use sp_std::{vec::Vec, vec};
@@ -33,7 +33,8 @@
 		<SelfWeightOf<T>>::create_item()
 	}
 
-	fn create_multiple_items(_amount: u32) -> Weight {
+	fn create_multiple_items(_data: &[CreateItemData]) -> Weight {
+		// All items minted for the same user, so it works same as create_item
 		Self::create_item()
 	}
 
@@ -51,23 +52,28 @@
 	}
 
 	fn set_collection_properties(amount: u32) -> Weight {
-		<SelfWeightOf<T>>::set_collection_properties(amount)
+		// Error
+		0
 	}
 
 	fn delete_collection_properties(amount: u32) -> Weight {
-		<SelfWeightOf<T>>::delete_collection_properties(amount)
+		// Error
+		0
 	}
 
 	fn set_token_properties(amount: u32) -> Weight {
-		<SelfWeightOf<T>>::set_token_properties(amount)
+		// Error
+		0
 	}
 
 	fn delete_token_properties(amount: u32) -> Weight {
-		<SelfWeightOf<T>>::delete_token_properties(amount)
+		// Error
+		0
 	}
 
 	fn set_property_permissions(amount: u32) -> Weight {
-		<SelfWeightOf<T>>::set_property_permissions(amount)
+		// Error
+		0
 	}
 
 	fn transfer() -> Weight {
modifiedpallets/fungible/src/weights.rsdiffbeforeafterboth
--- a/pallets/fungible/src/weights.rs
+++ b/pallets/fungible/src/weights.rs
@@ -35,11 +35,6 @@
 	fn create_item() -> Weight;
 	fn create_multiple_items_ex(b: u32, ) -> Weight;
 	fn burn_item() -> Weight;
-	fn set_collection_properties(amount: u32) -> Weight;
-	fn delete_collection_properties(amount: u32) -> Weight;
-	fn set_token_properties(amount: u32) -> Weight;
-	fn delete_token_properties(amount: u32) -> Weight;
-	fn set_property_permissions(amount: u32) -> Weight;
 	fn transfer() -> Weight;
 	fn approve() -> Weight;
 	fn transfer_from() -> Weight;
@@ -73,33 +68,8 @@
 		(15_565_000 as Weight)
 			.saturating_add(T::DbWeight::get().reads(2 as Weight))
 			.saturating_add(T::DbWeight::get().writes(2 as Weight))
-	}
-
-	fn set_collection_properties(_amount: u32) -> Weight {
-		// Error
-		0
-	}
-
-	fn delete_collection_properties(_amount: u32) -> Weight {
-		// Error
-		0
-	}
-
-	fn set_token_properties(_amount: u32) -> Weight {
-		// Error
-		0
-	}
-
-	fn delete_token_properties(_amount: u32) -> Weight {
-		// Error
-		0
 	}
 
-	fn set_property_permissions(_amount: u32) -> Weight {
-		// Error
-		0
-	}
-
 	// Storage: Fungible Balance (r:2 w:2)
 	fn transfer() -> Weight {
 		(17_713_000 as Weight)
@@ -156,31 +126,6 @@
 		(15_565_000 as Weight)
 			.saturating_add(RocksDbWeight::get().reads(2 as Weight))
 			.saturating_add(RocksDbWeight::get().writes(2 as Weight))
-	}
-
-	fn set_collection_properties(_amount: u32) -> Weight {
-		// Error
-		0
-	}
-
-	fn delete_collection_properties(_amount: u32) -> Weight {
-		// Error
-		0
-	}
-
-	fn set_token_properties(_amount: u32) -> Weight {
-		// Error
-		0
-	}
-
-	fn delete_token_properties(_amount: u32) -> Weight {
-		// Error
-		0
-	}
-
-	fn set_property_permissions(_amount: u32) -> Weight {
-		// Error
-		0
 	}
 
 	// Storage: Fungible Balance (r:2 w:2)
modifiedpallets/nonfungible/Cargo.tomldiffbeforeafterboth
--- a/pallets/nonfungible/Cargo.toml
+++ b/pallets/nonfungible/Cargo.toml
@@ -49,4 +49,5 @@
     'frame-benchmarking',
     'frame-support/runtime-benchmarks',
     'frame-system/runtime-benchmarks',
+    'up-data-structs/runtime-benchmarks',
 ]
modifiedpallets/nonfungible/src/benchmarking.rsdiffbeforeafterboth
--- a/pallets/nonfungible/src/benchmarking.rs
+++ b/pallets/nonfungible/src/benchmarking.rs
@@ -18,24 +18,35 @@
 use crate::{Pallet, Config, NonfungibleHandle};
 
 use sp_std::prelude::*;
-use pallet_common::benchmarking::{create_collection_raw, create_data, create_var_data};
+use pallet_common::benchmarking::{create_collection_raw, create_data, property_key, property_value};
 use frame_benchmarking::{benchmarks, account};
-use up_data_structs::{CollectionMode, MAX_ITEMS_PER_BATCH, CUSTOM_DATA_LIMIT, budget::Unlimited};
+use up_data_structs::{
+	CollectionMode, MAX_ITEMS_PER_BATCH, MAX_PROPERTIES_PER_ITEM, CUSTOM_DATA_LIMIT,
+	budget::Unlimited,
+};
 use pallet_common::bench_init;
-use core::convert::TryInto;
 
 const SEED: u32 = 1;
 
 fn create_max_item_data<T: Config>(owner: T::CrossAccountId) -> CreateItemData<T> {
 	let const_data = create_data::<CUSTOM_DATA_LIMIT>();
-	CreateItemData::<T> { const_data, owner }
+	CreateItemData::<T> {
+		const_data,
+		owner,
+		properties: Default::default(),
+	}
 }
 fn create_max_item<T: Config>(
 	collection: &NonfungibleHandle<T>,
 	sender: &T::CrossAccountId,
 	owner: T::CrossAccountId,
 ) -> Result<TokenId, DispatchError> {
-	<Pallet<T>>::create_item(&collection, sender, create_max_item_data::<T>(owner))?;
+	<Pallet<T>>::create_item(
+		&collection,
+		sender,
+		create_max_item_data::<T>(owner),
+		&Unlimited,
+	)?;
 	Ok(TokenId(<TokensMinted<T>>::get(&collection.id)))
 }
 
@@ -65,7 +76,7 @@
 			sender: cross_from_sub(owner); to: cross_sub;
 		};
 		let data = (0..b).map(|_| create_max_item_data::<T>(to.clone())).collect();
-	}: {<Pallet<T>>::create_multiple_items(&collection, &sender, data)?}
+	}: {<Pallet<T>>::create_multiple_items(&collection, &sender, data, &Unlimited)?}
 
 	create_multiple_items_ex {
 		let b in 0..MAX_ITEMS_PER_BATCH;
@@ -77,7 +88,7 @@
 			bench_init!(to: cross_sub(i););
 			create_max_item_data::<T>(to)
 		}).collect();
-	}: {<Pallet<T>>::create_multiple_items(&collection, &sender, data)?}
+	}: {<Pallet<T>>::create_multiple_items(&collection, &sender, data, &Unlimited)?}
 
 	burn_item {
 		bench_init!{
@@ -93,7 +104,7 @@
 			owner: cross_from_sub; sender: cross_sub; receiver: cross_sub;
 		};
 		let item = create_max_item(&collection, &owner, sender.clone())?;
-	}: {<Pallet<T>>::transfer(&collection, &sender, &receiver, item)?}
+	}: {<Pallet<T>>::transfer(&collection, &sender, &receiver, item, &Unlimited)?}
 
 	approve {
 		bench_init!{
@@ -120,4 +131,66 @@
 		let item = create_max_item(&collection, &owner, sender.clone())?;
 		<Pallet<T>>::set_allowance(&collection, &sender, item, Some(&burner))?;
 	}: {<Pallet<T>>::burn_from(&collection, &burner, &sender, item, &Unlimited)?}
+
+	set_property_permissions {
+		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 {
+				mutable: false,
+				collection_admin: false,
+				token_owner: false,
+			},
+		}).collect::<Vec<_>>();
+	}: {<Pallet<T>>::set_property_permissions(&collection, &owner, perms)?}
+
+	set_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 {
+				mutable: false,
+				collection_admin: true,
+				token_owner: true,
+			},
+		}).collect::<Vec<_>>();
+		<Pallet<T>>::set_property_permissions(&collection, &owner, perms)?;
+		let props = (0..b).map(|k| Property {
+			key: property_key(k as usize),
+			value: property_value(),
+		}).collect::<Vec<_>>();
+		let item = create_max_item(&collection, &owner, owner.clone())?;
+	}: {<Pallet<T>>::set_token_properties(&collection, &owner, item, props)?}
+
+	delete_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 {
+				mutable: true,
+				collection_admin: true,
+				token_owner: true,
+			},
+		}).collect::<Vec<_>>();
+		<Pallet<T>>::set_property_permissions(&collection, &owner, perms)?;
+		let props = (0..b).map(|k| Property {
+			key: property_key(k as usize),
+			value: property_value(),
+		}).collect::<Vec<_>>();
+		let item = create_max_item(&collection, &owner, owner.clone())?;
+		<Pallet<T>>::set_token_properties(&collection, &owner, item, props)?;
+		let to_delete = (0..b).map(|k| property_key(k as usize)).collect::<Vec<_>>();
+	}: {<Pallet<T>>::delete_token_properties(&collection, &owner, item, to_delete)?}
 }
modifiedpallets/nonfungible/src/common.rsdiffbeforeafterboth
--- a/pallets/nonfungible/src/common.rs
+++ b/pallets/nonfungible/src/common.rs
@@ -21,7 +21,9 @@
 	TokenId, CreateItemExData, CollectionId, budget::Budget, Property, PropertyKey,
 	PropertyKeyPermission, PropertyValue,
 };
-use pallet_common::{CommonCollectionOperations, CommonWeightInfo, with_weight};
+use pallet_common::{
+	CommonCollectionOperations, CommonWeightInfo, with_weight, weights::WeightInfo as _,
+};
 use sp_runtime::DispatchError;
 use sp_std::vec::Vec;
 
@@ -38,13 +40,33 @@
 
 	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),
+			CreateItemExData::NFT(t) => {
+				<SelfWeightOf<T>>::create_multiple_items_ex(t.len() as u32)
+					+ t.iter()
+						.map(|t| {
+							if t.properties.len() > 0 {
+								Self::set_token_properties(t.properties.len() as u32)
+							} else {
+								0
+							}
+						})
+						.sum::<u64>()
+			}
 			_ => 0,
 		}
 	}
 
-	fn create_multiple_items(amount: u32) -> Weight {
-		<SelfWeightOf<T>>::create_multiple_items(amount)
+	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(Self::set_token_properties(n.properties.len() as u32))
+					}
+					_ => None,
+				})
+				.sum::<u64>()
 	}
 
 	fn burn_item() -> Weight {
@@ -52,11 +74,11 @@
 	}
 
 	fn set_collection_properties(amount: u32) -> Weight {
-		<SelfWeightOf<T>>::set_collection_properties(amount)
+		<pallet_common::SelfWeightOf<T>>::set_collection_properties(amount)
 	}
 
 	fn delete_collection_properties(amount: u32) -> Weight {
-		<SelfWeightOf<T>>::delete_collection_properties(amount)
+		<pallet_common::SelfWeightOf<T>>::delete_collection_properties(amount)
 	}
 
 	fn set_token_properties(amount: u32) -> Weight {
@@ -128,15 +150,15 @@
 		data: Vec<up_data_structs::CreateItemData>,
 		nesting_budget: &dyn Budget,
 	) -> DispatchResultWithPostInfo {
+		let weight = <CommonWeights<T>>::create_multiple_items(&data);
 		let data = data
 			.into_iter()
 			.map(|d| map_create_data::<T>(d, &to))
 			.collect::<Result<Vec<_>, DispatchError>>()?;
 
-		let amount = data.len();
 		with_weight(
 			<Pallet<T>>::create_multiple_items(self, &sender, data, nesting_budget),
-			<CommonWeights<T>>::create_multiple_items(amount as u32),
+			weight,
 		)
 	}
 
modifiedpallets/nonfungible/src/lib.rsdiffbeforeafterboth
before · pallets/nonfungible/src/lib.rs
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/>.1617#![cfg_attr(not(feature = "std"), no_std)]1819use erc::ERC721Events;20use evm_coder::ToLog;21use frame_support::{BoundedVec, ensure, fail, transactional};22use up_data_structs::{23	AccessMode, CollectionId, CustomDataLimit, TokenId, CreateCollectionData, CreateNftExData,24	mapping::TokenAddressMapping, NestingRule, budget::Budget, Property, PropertyPermission,25	PropertyKey, PropertyKeyPermission, Properties, TrySetProperty,26};27use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm};28use pallet_common::{29	Error as CommonError, Pallet as PalletCommon, Event as CommonEvent, CollectionHandle,30	dispatch::CollectionDispatch, eth::collection_id_to_address,31};32use pallet_structure::Pallet as PalletStructure;33use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};34use sp_core::H160;35use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};36use sp_std::{vec::Vec, vec};37use core::ops::Deref;38use sp_std::collections::btree_map::BTreeMap;39use codec::{Encode, Decode, MaxEncodedLen};40use scale_info::TypeInfo;4142pub use pallet::*;43#[cfg(feature = "runtime-benchmarks")]44pub mod benchmarking;45pub mod common;46pub mod erc;47pub mod weights;4849pub type CreateItemData<T> = CreateNftExData<<T as pallet_evm::account::Config>::CrossAccountId>;50pub(crate) type SelfWeightOf<T> = <T as Config>::WeightInfo;5152#[struct_versioning::versioned(version = 2, upper)]53#[derive(Encode, Decode, TypeInfo, MaxEncodedLen)]54pub struct ItemData<CrossAccountId> {55	pub const_data: BoundedVec<u8, CustomDataLimit>,5657	#[version(..2)]58	pub variable_data: BoundedVec<u8, CustomDataLimit>,5960	pub owner: CrossAccountId,61}6263#[frame_support::pallet]64pub mod pallet {65	use super::*;66	use frame_support::{67		Blake2_128Concat, Twox64Concat, pallet_prelude::*, storage::Key, traits::StorageVersion,68	};69	use frame_system::pallet_prelude::*;70	use up_data_structs::{CollectionId, TokenId};71	use super::weights::WeightInfo;7273	#[pallet::error]74	pub enum Error<T> {75		/// Not Nonfungible item data used to mint in Nonfungible collection.76		NotNonfungibleDataUsedToMintFungibleCollectionToken,77		/// Used amount > 1 with NFT78		NonfungibleItemsHaveNoAmount,79	}8081	#[pallet::config]82	pub trait Config:83		frame_system::Config + pallet_common::Config + pallet_structure::Config + pallet_evm::Config84	{85		type WeightInfo: WeightInfo;86	}8788	const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);8990	#[pallet::pallet]91	#[pallet::storage_version(STORAGE_VERSION)]92	#[pallet::generate_store(pub(super) trait Store)]93	pub struct Pallet<T>(_);9495	#[pallet::storage]96	pub type TokensMinted<T: Config> =97		StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;98	#[pallet::storage]99	pub type TokensBurnt<T: Config> =100		StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;101102	#[pallet::storage]103	pub type TokenData<T: Config> = StorageNMap<104		Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),105		Value = ItemData<T::CrossAccountId>,106		QueryKind = OptionQuery,107	>;108109	#[pallet::storage]110	#[pallet::getter(fn token_properties)]111	pub type TokenProperties<T: Config> = StorageNMap<112		Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),113		Value = Properties,114		QueryKind = ValueQuery,115		OnEmpty = up_data_structs::TokenProperties,116	>;117118	/// Used to enumerate tokens owned by account119	#[pallet::storage]120	pub type Owned<T: Config> = StorageNMap<121		Key = (122			Key<Twox64Concat, CollectionId>,123			Key<Blake2_128Concat, T::CrossAccountId>,124			Key<Twox64Concat, TokenId>,125		),126		Value = bool,127		QueryKind = ValueQuery,128	>;129130	#[pallet::storage]131	pub type AccountBalance<T: Config> = StorageNMap<132		Key = (133			Key<Twox64Concat, CollectionId>,134			Key<Blake2_128Concat, T::CrossAccountId>,135		),136		Value = u32,137		QueryKind = ValueQuery,138	>;139140	#[pallet::storage]141	pub type Allowance<T: Config> = StorageNMap<142		Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),143		Value = T::CrossAccountId,144		QueryKind = OptionQuery,145	>;146147	#[pallet::hooks]148	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {149		fn on_runtime_upgrade() -> Weight {150			if StorageVersion::get::<Pallet<T>>() < StorageVersion::new(1) {151				<TokenData<T>>::translate_values::<ItemDataVersion1<T::CrossAccountId>, _>(|v| {152					Some(<ItemDataVersion2<T::CrossAccountId>>::from(v))153				})154			}155156			0157		}158	}159}160161pub struct NonfungibleHandle<T: Config>(pallet_common::CollectionHandle<T>);162impl<T: Config> NonfungibleHandle<T> {163	pub fn cast(inner: pallet_common::CollectionHandle<T>) -> Self {164		Self(inner)165	}166	pub fn into_inner(self) -> pallet_common::CollectionHandle<T> {167		self.0168	}169	pub fn common_mut(&mut self) -> &mut pallet_common::CollectionHandle<T> {170		&mut self.0171	}172}173impl<T: Config> WithRecorder<T> for NonfungibleHandle<T> {174	fn recorder(&self) -> &SubstrateRecorder<T> {175		self.0.recorder()176	}177	fn into_recorder(self) -> SubstrateRecorder<T> {178		self.0.into_recorder()179	}180}181impl<T: Config> Deref for NonfungibleHandle<T> {182	type Target = pallet_common::CollectionHandle<T>;183184	fn deref(&self) -> &Self::Target {185		&self.0186	}187}188189impl<T: Config> Pallet<T> {190	pub fn total_supply(collection: &NonfungibleHandle<T>) -> u32 {191		<TokensMinted<T>>::get(collection.id) - <TokensBurnt<T>>::get(collection.id)192	}193	pub fn token_exists(collection: &NonfungibleHandle<T>, token: TokenId) -> bool {194		<TokenData<T>>::contains_key((collection.id, token))195	}196}197198// unchecked calls skips any permission checks199impl<T: Config> Pallet<T> {200	pub fn init_collection(201		owner: T::AccountId,202		data: CreateCollectionData<T::AccountId>,203	) -> Result<CollectionId, DispatchError> {204		<PalletCommon<T>>::init_collection(owner, data)205	}206	pub fn destroy_collection(207		collection: NonfungibleHandle<T>,208		sender: &T::CrossAccountId,209	) -> DispatchResult {210		let id = collection.id;211212		// =========213214		PalletCommon::destroy_collection(collection.0, sender)?;215216		<TokenData<T>>::remove_prefix((id,), None);217		<Owned<T>>::remove_prefix((id,), None);218		<TokensMinted<T>>::remove(id);219		<TokensBurnt<T>>::remove(id);220		<Allowance<T>>::remove_prefix((id,), None);221		<AccountBalance<T>>::remove_prefix((id,), None);222		Ok(())223	}224225	pub fn burn(226		collection: &NonfungibleHandle<T>,227		sender: &T::CrossAccountId,228		token: TokenId,229	) -> DispatchResult {230		let token_data =231			<TokenData<T>>::get((collection.id, token)).ok_or(<CommonError<T>>::TokenNotFound)?;232		ensure!(233			&token_data.owner == sender234				|| (collection.limits.owner_can_transfer() && collection.is_owner_or_admin(sender)),235			<CommonError<T>>::NoPermission236		);237238		if collection.access == AccessMode::AllowList {239			collection.check_allowlist(sender)?;240		}241242		let burnt = <TokensBurnt<T>>::get(collection.id)243			.checked_add(1)244			.ok_or(ArithmeticError::Overflow)?;245246		let balance = <AccountBalance<T>>::get((collection.id, token_data.owner.clone()))247			.checked_sub(1)248			.ok_or(ArithmeticError::Overflow)?;249250		if balance == 0 {251			<AccountBalance<T>>::remove((collection.id, token_data.owner.clone()));252		} else {253			<AccountBalance<T>>::insert((collection.id, token_data.owner.clone()), balance);254		}255		// =========256257		<Owned<T>>::remove((collection.id, &token_data.owner, token));258		<TokensBurnt<T>>::insert(collection.id, burnt);259		<TokenData<T>>::remove((collection.id, token));260		<TokenProperties<T>>::remove((collection.id, token));261		let old_spender = <Allowance<T>>::take((collection.id, token));262263		if let Some(old_spender) = old_spender {264			<PalletCommon<T>>::deposit_event(CommonEvent::Approved(265				collection.id,266				token,267				sender.clone(),268				old_spender,269				0,270			));271		}272273		<PalletEvm<T>>::deposit_log(274			ERC721Events::Transfer {275				from: *token_data.owner.as_eth(),276				to: H160::default(),277				token_id: token.into(),278			}279			.to_log(collection_id_to_address(collection.id)),280		);281		<PalletCommon<T>>::deposit_event(CommonEvent::ItemDestroyed(282			collection.id,283			token,284			token_data.owner,285			1,286		));287		Ok(())288	}289290	pub fn set_token_property(291		collection: &NonfungibleHandle<T>,292		sender: &T::CrossAccountId,293		token_id: TokenId,294		property: Property,295	) -> DispatchResult {296		Self::check_token_change_permission(collection, sender, token_id, &property.key)?;297298		<TokenProperties<T>>::try_mutate((collection.id, token_id), |properties| {299			let property = property.clone();300			properties.try_set(property.key, property.value)301		})302		.map_err(<CommonError<T>>::from)?;303304		<PalletCommon<T>>::deposit_event(CommonEvent::TokenPropertySet(305			collection.id,306			token_id,307			property.key,308		));309310		Ok(())311	}312313	#[transactional]314	pub fn set_token_properties(315		collection: &NonfungibleHandle<T>,316		sender: &T::CrossAccountId,317		token_id: TokenId,318		properties: Vec<Property>,319	) -> DispatchResult {320		for property in properties {321			Self::set_token_property(collection, sender, token_id, property)?;322		}323324		Ok(())325	}326327	pub fn delete_token_property(328		collection: &NonfungibleHandle<T>,329		sender: &T::CrossAccountId,330		token_id: TokenId,331		property_key: PropertyKey,332	) -> DispatchResult {333		Self::check_token_change_permission(collection, sender, token_id, &property_key)?;334335		<TokenProperties<T>>::try_mutate((collection.id, token_id), |properties| {336			properties.remove(&property_key)337		})338		.map_err(<CommonError<T>>::from)?;339340		<PalletCommon<T>>::deposit_event(CommonEvent::TokenPropertyDeleted(341			collection.id,342			token_id,343			property_key,344		));345346		Ok(())347	}348349	fn check_token_change_permission(350		collection: &NonfungibleHandle<T>,351		sender: &T::CrossAccountId,352		token_id: TokenId,353		property_key: &PropertyKey,354	) -> DispatchResult {355		let permission = <PalletCommon<T>>::property_permissions(collection.id)356			.get(property_key)357			.cloned()358			.unwrap_or_else(PropertyPermission::none);359360		let token_data = <TokenData<T>>::get((collection.id, token_id))361			.ok_or(<CommonError<T>>::TokenNotFound)?;362363		let check_token_owner = || -> DispatchResult {364			ensure!(&token_data.owner == sender, <CommonError<T>>::NoPermission);365			Ok(())366		};367368		let is_property_exists = TokenProperties::<T>::get((collection.id, token_id))369			.get(property_key)370			.is_some();371372		match permission {373			PropertyPermission { mutable: false, .. } if is_property_exists => {374				Err(<CommonError<T>>::NoPermission.into())375			}376377			PropertyPermission {378				collection_admin,379				token_owner,380				..381			} => {382				let mut check_result = Err(<CommonError<T>>::NoPermission.into());383384				if collection_admin {385					check_result = collection.check_is_owner_or_admin(sender);386				}387388				if token_owner {389					check_result.or_else(|_| check_token_owner())390				} else {391					check_result392				}393			}394		}395	}396397	#[transactional]398	pub fn delete_token_properties(399		collection: &NonfungibleHandle<T>,400		sender: &T::CrossAccountId,401		token_id: TokenId,402		property_keys: Vec<PropertyKey>,403	) -> DispatchResult {404		for key in property_keys {405			Self::delete_token_property(collection, sender, token_id, key)?;406		}407408		Ok(())409	}410411	pub fn set_collection_properties(412		collection: &NonfungibleHandle<T>,413		sender: &T::CrossAccountId,414		properties: Vec<Property>,415	) -> DispatchResult {416		<PalletCommon<T>>::set_collection_properties(collection, sender, properties)417	}418419	pub fn delete_collection_properties(420		collection: &CollectionHandle<T>,421		sender: &T::CrossAccountId,422		property_keys: Vec<PropertyKey>,423	) -> DispatchResult {424		<PalletCommon<T>>::delete_collection_properties(collection, sender, property_keys)425	}426427	pub fn set_property_permissions(428		collection: &CollectionHandle<T>,429		sender: &T::CrossAccountId,430		property_permissions: Vec<PropertyKeyPermission>,431	) -> DispatchResult {432		<PalletCommon<T>>::set_property_permissions(collection, sender, property_permissions)433	}434435	pub fn set_property_permission(436		collection: &CollectionHandle<T>,437		sender: &T::CrossAccountId,438		permission: PropertyKeyPermission,439	) -> DispatchResult {440		<PalletCommon<T>>::set_property_permission(collection, sender, permission)441	}442443	pub fn transfer(444		collection: &NonfungibleHandle<T>,445		from: &T::CrossAccountId,446		to: &T::CrossAccountId,447		token: TokenId,448		nesting_budget: &dyn Budget,449	) -> DispatchResult {450		ensure!(451			collection.limits.transfers_enabled(),452			<CommonError<T>>::TransferNotAllowed453		);454455		let token_data =456			<TokenData<T>>::get((collection.id, token)).ok_or(<CommonError<T>>::TokenNotFound)?;457		// TODO: require sender to be token, owner, require admins to go through transfer_from458		ensure!(459			&token_data.owner == from460				|| (collection.limits.owner_can_transfer() && collection.is_owner_or_admin(from)),461			<CommonError<T>>::NoPermission462		);463464		if collection.access == AccessMode::AllowList {465			collection.check_allowlist(from)?;466			collection.check_allowlist(to)?;467		}468		<PalletCommon<T>>::ensure_correct_receiver(to)?;469470		let balance_from = <AccountBalance<T>>::get((collection.id, from))471			.checked_sub(1)472			.ok_or(<CommonError<T>>::TokenValueTooLow)?;473		let balance_to = if from != to {474			let balance_to = <AccountBalance<T>>::get((collection.id, to))475				.checked_add(1)476				.ok_or(ArithmeticError::Overflow)?;477478			ensure!(479				balance_to < collection.limits.account_token_ownership_limit(),480				<CommonError<T>>::AccountTokenLimitExceeded,481			);482483			Some(balance_to)484		} else {485			None486		};487488		if let Some(target) = T::CrossTokenAddressMapping::address_to_token(to) {489			let handle = <CollectionHandle<T>>::try_get(target.0)?;490			let dispatch = T::CollectionDispatch::dispatch(handle);491			let dispatch = dispatch.as_dyn();492493			dispatch.check_nesting(494				from.clone(),495				(collection.id, token),496				target.1,497				nesting_budget,498			)?;499		}500501		// =========502503		<TokenData<T>>::insert(504			(collection.id, token),505			ItemData {506				owner: to.clone(),507				..token_data508			},509		);510511		if let Some(balance_to) = balance_to {512			// from != to513			if balance_from == 0 {514				<AccountBalance<T>>::remove((collection.id, from));515			} else {516				<AccountBalance<T>>::insert((collection.id, from), balance_from);517			}518			<AccountBalance<T>>::insert((collection.id, to), balance_to);519			<Owned<T>>::remove((collection.id, from, token));520			<Owned<T>>::insert((collection.id, to, token), true);521		}522		Self::set_allowance_unchecked(collection, from, token, None, true);523524		<PalletEvm<T>>::deposit_log(525			ERC721Events::Transfer {526				from: *from.as_eth(),527				to: *to.as_eth(),528				token_id: token.into(),529			}530			.to_log(collection_id_to_address(collection.id)),531		);532		<PalletCommon<T>>::deposit_event(CommonEvent::Transfer(533			collection.id,534			token,535			from.clone(),536			to.clone(),537			1,538		));539		Ok(())540	}541542	pub fn create_multiple_items(543		collection: &NonfungibleHandle<T>,544		sender: &T::CrossAccountId,545		data: Vec<CreateItemData<T>>,546		nesting_budget: &dyn Budget,547	) -> DispatchResult {548		if !collection.is_owner_or_admin(sender) {549			ensure!(550				collection.mint_mode,551				<CommonError<T>>::PublicMintingNotAllowed552			);553			collection.check_allowlist(sender)?;554555			for item in data.iter() {556				collection.check_allowlist(&item.owner)?;557			}558		}559560		for data in data.iter() {561			<PalletCommon<T>>::ensure_correct_receiver(&data.owner)?;562		}563564		let first_token = <TokensMinted<T>>::get(collection.id);565		let tokens_minted = first_token566			.checked_add(data.len() as u32)567			.ok_or(ArithmeticError::Overflow)?;568		ensure!(569			tokens_minted <= collection.limits.token_limit(),570			<CommonError<T>>::CollectionTokenLimitExceeded571		);572573		let mut balances = BTreeMap::new();574		for data in &data {575			let balance = balances576				.entry(&data.owner)577				.or_insert_with(|| <AccountBalance<T>>::get((collection.id, &data.owner)));578			*balance = balance.checked_add(1).ok_or(ArithmeticError::Overflow)?;579580			ensure!(581				*balance <= collection.limits.account_token_ownership_limit(),582				<CommonError<T>>::AccountTokenLimitExceeded,583			);584		}585586		for (i, data) in data.iter().enumerate() {587			let token = TokenId(first_token + i as u32 + 1);588			if let Some(target) = T::CrossTokenAddressMapping::address_to_token(&data.owner) {589				let handle = <CollectionHandle<T>>::try_get(target.0)?;590				let dispatch = T::CollectionDispatch::dispatch(handle);591				let dispatch = dispatch.as_dyn();592				dispatch.check_nesting(593					sender.clone(),594					(collection.id, token),595					target.1,596					nesting_budget,597				)?;598			}599		}600601		// =========602603		<TokensMinted<T>>::insert(collection.id, tokens_minted);604		for (account, balance) in balances {605			<AccountBalance<T>>::insert((collection.id, account), balance);606		}607		for (i, data) in data.into_iter().enumerate() {608			let token = first_token + i as u32 + 1;609610			<TokenData<T>>::insert(611				(collection.id, token),612				ItemData {613					const_data: data.const_data,614					owner: data.owner.clone(),615				},616			);617			<Owned<T>>::insert((collection.id, &data.owner, token), true);618619			Self::set_token_properties(620				collection,621				sender,622				TokenId(token),623				data.properties.into_inner(),624			)?;625626			<PalletEvm<T>>::deposit_log(627				ERC721Events::Transfer {628					from: H160::default(),629					to: *data.owner.as_eth(),630					token_id: token.into(),631				}632				.to_log(collection_id_to_address(collection.id)),633			);634			<PalletCommon<T>>::deposit_event(CommonEvent::ItemCreated(635				collection.id,636				TokenId(token),637				data.owner.clone(),638				1,639			));640		}641		Ok(())642	}643644	pub fn set_allowance_unchecked(645		collection: &NonfungibleHandle<T>,646		sender: &T::CrossAccountId,647		token: TokenId,648		spender: Option<&T::CrossAccountId>,649		assume_implicit_eth: bool,650	) {651		if let Some(spender) = spender {652			let old_spender = <Allowance<T>>::get((collection.id, token));653			<Allowance<T>>::insert((collection.id, token), spender);654			// In ERC721 there is only one possible approved user of token, so we set655			// approved user to spender656			<PalletEvm<T>>::deposit_log(657				ERC721Events::Approval {658					owner: *sender.as_eth(),659					approved: *spender.as_eth(),660					token_id: token.into(),661				}662				.to_log(collection_id_to_address(collection.id)),663			);664			// In Unique chain, any token can have any amount of approved users, so we need to665			// set allowance of old owner to 0, and allowance of new owner to 1666			if old_spender.as_ref() != Some(spender) {667				if let Some(old_owner) = old_spender {668					<PalletCommon<T>>::deposit_event(CommonEvent::Approved(669						collection.id,670						token,671						sender.clone(),672						old_owner,673						0,674					));675				}676				<PalletCommon<T>>::deposit_event(CommonEvent::Approved(677					collection.id,678					token,679					sender.clone(),680					spender.clone(),681					1,682				));683			}684		} else {685			let old_spender = <Allowance<T>>::take((collection.id, token));686			if !assume_implicit_eth {687				// In ERC721 there is only one possible approved user of token, so we set688				// approved user to zero address689				<PalletEvm<T>>::deposit_log(690					ERC721Events::Approval {691						owner: *sender.as_eth(),692						approved: H160::default(),693						token_id: token.into(),694					}695					.to_log(collection_id_to_address(collection.id)),696				);697			}698			// In Unique chain, any token can have any amount of approved users, so we need to699			// set allowance of old owner to 0700			if let Some(old_spender) = old_spender {701				<PalletCommon<T>>::deposit_event(CommonEvent::Approved(702					collection.id,703					token,704					sender.clone(),705					old_spender,706					0,707				));708			}709		}710	}711712	pub fn set_allowance(713		collection: &NonfungibleHandle<T>,714		sender: &T::CrossAccountId,715		token: TokenId,716		spender: Option<&T::CrossAccountId>,717	) -> DispatchResult {718		if collection.access == AccessMode::AllowList {719			collection.check_allowlist(sender)?;720			if let Some(spender) = spender {721				collection.check_allowlist(spender)?;722			}723		}724725		if let Some(spender) = spender {726			<PalletCommon<T>>::ensure_correct_receiver(spender)?;727		}728		let token_data =729			<TokenData<T>>::get((collection.id, token)).ok_or(<CommonError<T>>::TokenNotFound)?;730		if &token_data.owner != sender {731			ensure!(732				collection.ignores_owned_amount(sender),733				<CommonError<T>>::CantApproveMoreThanOwned734			);735		}736737		// =========738739		Self::set_allowance_unchecked(collection, sender, token, spender, false);740		Ok(())741	}742743	fn check_allowed(744		collection: &NonfungibleHandle<T>,745		spender: &T::CrossAccountId,746		from: &T::CrossAccountId,747		token: TokenId,748		nesting_budget: &dyn Budget,749	) -> DispatchResult {750		if spender.conv_eq(from) {751			return Ok(());752		}753		if collection.access == AccessMode::AllowList {754			// `from`, `to` checked in [`transfer`]755			collection.check_allowlist(spender)?;756		}757		if let Some(source) = T::CrossTokenAddressMapping::address_to_token(from) {758			// TODO: should collection owner be allowed to perform this transfer?759			ensure!(760				<PalletStructure<T>>::check_indirectly_owned(761					spender.clone(),762					source.0,763					source.1,764					None,765					nesting_budget766				)?,767				<CommonError<T>>::ApprovedValueTooLow,768			);769			return Ok(());770		}771		if <Allowance<T>>::get((collection.id, token)).as_ref() == Some(spender) {772			return Ok(());773		}774		ensure!(775			collection.ignores_allowance(spender),776			<CommonError<T>>::ApprovedValueTooLow777		);778		Ok(())779	}780781	pub fn transfer_from(782		collection: &NonfungibleHandle<T>,783		spender: &T::CrossAccountId,784		from: &T::CrossAccountId,785		to: &T::CrossAccountId,786		token: TokenId,787		nesting_budget: &dyn Budget,788	) -> DispatchResult {789		Self::check_allowed(collection, spender, from, token, nesting_budget)?;790791		// =========792793		// Allowance is reset in [`transfer`]794		Self::transfer(collection, from, to, token, nesting_budget)795	}796797	pub fn burn_from(798		collection: &NonfungibleHandle<T>,799		spender: &T::CrossAccountId,800		from: &T::CrossAccountId,801		token: TokenId,802		nesting_budget: &dyn Budget,803	) -> DispatchResult {804		Self::check_allowed(collection, spender, from, token, nesting_budget)?;805806		// =========807808		Self::burn(collection, from, token)809	}810811	pub fn check_nesting(812		handle: &NonfungibleHandle<T>,813		sender: T::CrossAccountId,814		from: (CollectionId, TokenId),815		under: TokenId,816		nesting_budget: &dyn Budget,817	) -> DispatchResult {818		fn ensure_sender_allowed<T: Config>(819			collection: CollectionId,820			token: TokenId,821			for_nest: (CollectionId, TokenId),822			sender: T::CrossAccountId,823			budget: &dyn Budget,824		) -> DispatchResult {825			ensure!(826				<PalletStructure<T>>::check_indirectly_owned(827					sender,828					collection,829					token,830					Some(for_nest),831					budget832				)?,833				<CommonError<T>>::OnlyOwnerAllowedToNest,834			);835			Ok(())836		}837		match handle.limits.nesting_rule() {838			NestingRule::Disabled => fail!(<CommonError<T>>::NestingIsDisabled),839			NestingRule::Owner => {840				ensure_sender_allowed::<T>(handle.id, under, from, sender, nesting_budget)?841			}842			NestingRule::OwnerRestricted(whitelist) => {843				ensure!(844					whitelist.contains(&from.0),845					<CommonError<T>>::SourceCollectionIsNotAllowedToNest846				);847				ensure_sender_allowed::<T>(handle.id, under, from, sender, nesting_budget)?848			}849		}850		Ok(())851	}852853	/// Delegated to `create_multiple_items`854	pub fn create_item(855		collection: &NonfungibleHandle<T>,856		sender: &T::CrossAccountId,857		data: CreateItemData<T>,858		nesting_budget: &dyn Budget,859	) -> DispatchResult {860		Self::create_multiple_items(collection, sender, vec![data], nesting_budget)861	}862}
modifiedpallets/nonfungible/src/weights.rsdiffbeforeafterboth
--- a/pallets/nonfungible/src/weights.rs
+++ b/pallets/nonfungible/src/weights.rs
@@ -151,10 +151,35 @@
 	// Storage: Nonfungible AccountBalance (r:1 w:1)
 	// Storage: Nonfungible Owned (r:0 w:1)
 	fn burn_from() -> Weight {
-		(27_580_000 as Weight)
-			.saturating_add(T::DbWeight::get().reads(4 as Weight))
-			.saturating_add(T::DbWeight::get().writes(5 as Weight))
 	}
+	// Storage: Common CollectionPropertyPermissions (r:1 w:1)
+	fn set_property_permissions(b: u32, ) -> Weight {
+		(0 as Weight)
+			// Standard Error: 3_432_000
+			.saturating_add((126_888_000 as Weight).saturating_mul(b as Weight))
+			.saturating_add(T::DbWeight::get().reads(1 as Weight))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+	}
+	// Storage: Common CollectionPropertyPermissions (r:1 w:0)
+	// Storage: Nonfungible TokenData (r:1 w:0)
+	// Storage: Nonfungible TokenProperties (r:1 w:1)
+	fn set_token_properties(b: u32, ) -> Weight {
+		(0 as Weight)
+			// Standard Error: 158_583_000
+			.saturating_add((4_707_700_000 as Weight).saturating_mul(b as Weight))
+			.saturating_add(T::DbWeight::get().reads(3 as Weight))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+	}
+	// Storage: Common CollectionPropertyPermissions (r:1 w:0)
+	// Storage: Nonfungible TokenData (r:1 w:0)
+	// Storage: Nonfungible TokenProperties (r:1 w:1)
+	fn delete_token_properties(b: u32, ) -> Weight {
+		(0 as Weight)
+			// Standard Error: 169_018_000
+			.saturating_add((4_783_967_000 as Weight).saturating_mul(b as Weight))
+			.saturating_add(T::DbWeight::get().reads(3 as Weight))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+	}
 }
 
 // For backwards compatibility and tests
@@ -260,8 +285,33 @@
 	// Storage: Nonfungible AccountBalance (r:1 w:1)
 	// Storage: Nonfungible Owned (r:0 w:1)
 	fn burn_from() -> Weight {
-		(27_580_000 as Weight)
-			.saturating_add(RocksDbWeight::get().reads(4 as Weight))
-			.saturating_add(RocksDbWeight::get().writes(5 as Weight))
 	}
+	// Storage: Common CollectionPropertyPermissions (r:1 w:1)
+	fn set_property_permissions(b: u32, ) -> Weight {
+		(0 as Weight)
+			// Standard Error: 3_432_000
+			.saturating_add((126_888_000 as Weight).saturating_mul(b as Weight))
+			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+	}
+	// Storage: Common CollectionPropertyPermissions (r:1 w:0)
+	// Storage: Nonfungible TokenData (r:1 w:0)
+	// Storage: Nonfungible TokenProperties (r:1 w:1)
+	fn set_token_properties(b: u32, ) -> Weight {
+		(0 as Weight)
+			// Standard Error: 158_583_000
+			.saturating_add((4_707_700_000 as Weight).saturating_mul(b as Weight))
+			.saturating_add(RocksDbWeight::get().reads(3 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+	}
+	// Storage: Common CollectionPropertyPermissions (r:1 w:0)
+	// Storage: Nonfungible TokenData (r:1 w:0)
+	// Storage: Nonfungible TokenProperties (r:1 w:1)
+	fn delete_token_properties(b: u32, ) -> Weight {
+		(0 as Weight)
+			// Standard Error: 169_018_000
+			.saturating_add((4_783_967_000 as Weight).saturating_mul(b as Weight))
+			.saturating_add(RocksDbWeight::get().reads(3 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+	}
 }
modifiedpallets/refungible/src/benchmarking.rsdiffbeforeafterboth
--- a/pallets/refungible/src/benchmarking.rs
+++ b/pallets/refungible/src/benchmarking.rs
@@ -18,7 +18,7 @@
 use crate::{Pallet, Config, RefungibleHandle};
 
 use sp_std::prelude::*;
-use pallet_common::benchmarking::{create_collection_raw, create_data, create_var_data};
+use pallet_common::benchmarking::{create_collection_raw, create_data};
 use frame_benchmarking::{benchmarks, account};
 use up_data_structs::{CollectionMode, MAX_ITEMS_PER_BATCH, CUSTOM_DATA_LIMIT, budget::Unlimited};
 use pallet_common::bench_init;
@@ -46,7 +46,7 @@
 	users: impl IntoIterator<Item = (T::CrossAccountId, u128)>,
 ) -> Result<TokenId, DispatchError> {
 	let data: CreateRefungibleExData<T::CrossAccountId> = create_max_item_data(users);
-	<Pallet<T>>::create_item(&collection, sender, data)?;
+	<Pallet<T>>::create_item(&collection, sender, data, &Unlimited)?;
 	Ok(TokenId(<TokensMinted<T>>::get(&collection.id)))
 }
 
@@ -73,7 +73,7 @@
 			sender: cross_from_sub(owner); to: cross_sub;
 		};
 		let data = (0..b).map(|_| create_max_item_data([(to.clone(), 200)])).collect();
-	}: {<Pallet<T>>::create_multiple_items(&collection, &sender, data)?}
+	}: {<Pallet<T>>::create_multiple_items(&collection, &sender, data, &Unlimited)?}
 
 	create_multiple_items_ex_multiple_items {
 		let b in 0..MAX_ITEMS_PER_BATCH;
@@ -85,7 +85,7 @@
 			bench_init!(to: cross_sub(t););
 			create_max_item_data([(to, 200)])
 		}).collect();
-	}: {<Pallet<T>>::create_multiple_items(&collection, &sender, data)?}
+	}: {<Pallet<T>>::create_multiple_items(&collection, &sender, data, &Unlimited)?}
 
 	create_multiple_items_ex_multiple_owners {
 		let b in 0..MAX_ITEMS_PER_BATCH;
@@ -97,7 +97,7 @@
 			bench_init!(to: cross_sub(u););
 			(to, 200)
 		}))].try_into().unwrap();
-	}: {<Pallet<T>>::create_multiple_items(&collection, &sender, data)?}
+	}: {<Pallet<T>>::create_multiple_items(&collection, &sender, data, &Unlimited)?}
 
 	// Other user left, token data is kept
 	burn_item_partial {
@@ -122,7 +122,7 @@
 			sender: cross_from_sub(owner); receiver: cross_sub;
 		};
 		let item = create_max_item(&collection, &sender, [(sender.clone(), 200), (receiver.clone(), 200)])?;
-	}: {<Pallet<T>>::transfer(&collection, &sender, &receiver, item, 100)?}
+	}: {<Pallet<T>>::transfer(&collection, &sender, &receiver, item, 100, &Unlimited)?}
 	// Target account is created
 	transfer_creating {
 		bench_init!{
@@ -130,7 +130,7 @@
 			sender: cross_from_sub(owner); receiver: cross_sub;
 		};
 		let item = create_max_item(&collection, &sender, [(sender.clone(), 200)])?;
-	}: {<Pallet<T>>::transfer(&collection, &sender, &receiver, item, 100)?}
+	}: {<Pallet<T>>::transfer(&collection, &sender, &receiver, item, 100, &Unlimited)?}
 	// Source account is destroyed
 	transfer_removing {
 		bench_init!{
@@ -138,7 +138,7 @@
 			sender: cross_from_sub(owner); receiver: cross_sub;
 		};
 		let item = create_max_item(&collection, &sender, [(sender.clone(), 200), (receiver.clone(), 200)])?;
-	}: {<Pallet<T>>::transfer(&collection, &sender, &receiver, item, 200)?}
+	}: {<Pallet<T>>::transfer(&collection, &sender, &receiver, item, 200, &Unlimited)?}
 	// Source account destroyed, target created
 	transfer_creating_removing {
 		bench_init!{
@@ -146,7 +146,7 @@
 			sender: cross_from_sub(owner); receiver: cross_sub;
 		};
 		let item = create_max_item(&collection, &sender, [(sender.clone(), 200)])?;
-	}: {<Pallet<T>>::transfer(&collection, &sender, &receiver, item, 200)?}
+	}: {<Pallet<T>>::transfer(&collection, &sender, &receiver, item, 200, &Unlimited)?}
 
 	approve {
 		bench_init!{
modifiedpallets/refungible/src/common.rsdiffbeforeafterboth
--- a/pallets/refungible/src/common.rs
+++ b/pallets/refungible/src/common.rs
@@ -20,7 +20,7 @@
 use frame_support::{dispatch::DispatchResultWithPostInfo, fail, weights::Weight};
 use up_data_structs::{
 	CollectionId, TokenId, CreateItemExData, CreateRefungibleExData, budget::Budget, Property,
-	PropertyKey, PropertyValue, PropertyKeyPermission,
+	PropertyKey, PropertyValue, PropertyKeyPermission, CreateItemData,
 };
 use pallet_common::{CommonCollectionOperations, CommonWeightInfo, with_weight};
 use sp_runtime::DispatchError;
@@ -46,8 +46,8 @@
 		<SelfWeightOf<T>>::create_item()
 	}
 
-	fn create_multiple_items(amount: u32) -> Weight {
-		<SelfWeightOf<T>>::create_multiple_items(amount)
+	fn create_multiple_items(data: &[CreateItemData]) -> Weight {
+		<SelfWeightOf<T>>::create_multiple_items(data.len() as u32)
 	}
 
 	fn create_multiple_items_ex(call: &CreateItemExData<T::CrossAccountId>) -> Weight {
@@ -66,12 +66,14 @@
 		max_weight_of!(burn_item_partial(), burn_item_fully())
 	}
 
-	fn set_collection_properties(amount: u32) -> Weight {
-		<SelfWeightOf<T>>::set_collection_properties(amount)
+	fn set_collection_properties(_amount: u32) -> Weight {
+		// Error
+		0
 	}
 
-	fn delete_collection_properties(amount: u32) -> Weight {
-		<SelfWeightOf<T>>::delete_collection_properties(amount)
+	fn delete_collection_properties(_amount: u32) -> Weight {
+		// Error
+		0
 	}
 
 	fn set_token_properties(amount: u32) -> Weight {
@@ -156,15 +158,15 @@
 		data: Vec<up_data_structs::CreateItemData>,
 		nesting_budget: &dyn Budget,
 	) -> DispatchResultWithPostInfo {
+		let weight = <CommonWeights<T>>::create_multiple_items(&data);
 		let data = data
 			.into_iter()
 			.map(|d| map_create_data::<T>(d, &to))
 			.collect::<Result<Vec<_>, DispatchError>>()?;
 
-		let amount = data.len();
 		with_weight(
 			<Pallet<T>>::create_multiple_items(self, &sender, data, nesting_budget),
-			<CommonWeights<T>>::create_multiple_items(amount as u32),
+			weight,
 		)
 	}
 
modifiedpallets/refungible/src/weights.rsdiffbeforeafterboth
--- a/pallets/refungible/src/weights.rs
+++ b/pallets/refungible/src/weights.rs
@@ -38,8 +38,6 @@
 	fn create_multiple_items_ex_multiple_owners(b: u32, ) -> Weight;
 	fn burn_item_partial() -> Weight;
 	fn burn_item_fully() -> Weight;
-	fn set_collection_properties(amount: u32) -> Weight;
-	fn delete_collection_properties(amount: u32) -> Weight;
 	fn set_token_properties(amount: u32) -> Weight;
 	fn delete_token_properties(amount: u32) -> Weight;
 	fn set_property_permissions(amount: u32) -> Weight;
@@ -132,16 +130,6 @@
 		(32_489_000 as Weight)
 			.saturating_add(T::DbWeight::get().reads(4 as Weight))
 			.saturating_add(T::DbWeight::get().writes(6 as Weight))
-	}
-
-	fn set_collection_properties(_amount: u32) -> Weight {
-		// Error
-		0
-	}
-
-	fn delete_collection_properties(_amount: u32) -> Weight {
-		// Error
-		0
 	}
 
 	fn set_token_properties(_amount: u32) -> Weight {
@@ -320,16 +308,6 @@
 		(32_489_000 as Weight)
 			.saturating_add(RocksDbWeight::get().reads(4 as Weight))
 			.saturating_add(RocksDbWeight::get().writes(6 as Weight))
-	}
-
-	fn set_collection_properties(_amount: u32) -> Weight {
-		// Error
-		0
-	}
-
-	fn delete_collection_properties(_amount: u32) -> Weight {
-		// Error
-		0
 	}
 
 	fn set_token_properties(_amount: u32) -> Weight {
modifiedpallets/structure/Cargo.tomldiffbeforeafterboth
--- a/pallets/structure/Cargo.toml
+++ b/pallets/structure/Cargo.toml
@@ -16,6 +16,7 @@
 	"derive",
 ] }
 up-data-structs = { path = "../../primitives/data-structs", default-features = false }
+pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.21-logs" }
 
 [features]
 default = ["std"]
@@ -28,5 +29,6 @@
 	"scale-info/std",
 	"parity-scale-codec/std",
 	"up-data-structs/std",
+	"pallet-evm/std",
 ]
 runtime-benchmarks = ['frame-benchmarking', 'pallet-common/runtime-benchmarks']
modifiedpallets/structure/src/benchmarking.rsdiffbeforeafterboth
--- a/pallets/structure/src/benchmarking.rs
+++ b/pallets/structure/src/benchmarking.rs
@@ -2,8 +2,10 @@
 
 use frame_benchmarking::{benchmarks, account};
 use frame_support::traits::{Currency, Get};
-use up_data_structs::{CreateCollectionData, CollectionMode, CreateItemData, CreateNftData};
-use pallet_common::CrossAccountId;
+use up_data_structs::{
+	CreateCollectionData, CollectionMode, CreateItemData, CreateNftData, budget::Unlimited,
+};
+use pallet_evm::account::CrossAccountId;
 
 const SEED: u32 = 1;
 
@@ -20,9 +22,9 @@
 		let dispatch = T::CollectionDispatch::dispatch(CollectionHandle::try_get(CollectionId(1))?);
 		let dispatch = dispatch.as_dyn();
 
-		dispatch.create_item(caller_cross.clone(), caller_cross.clone(), CreateItemData::NFT(CreateNftData::default()))?;
+		dispatch.create_item(caller_cross.clone(), caller_cross.clone(), CreateItemData::NFT(CreateNftData::default()), &Unlimited)?;
 	}: {
 		let parent = <Pallet<T>>::find_parent(CollectionId(1), TokenId(1))?;
-		assert!(matches!(parent, Parent::Normal(_)))
+		assert!(matches!(parent, Parent::User(_)))
 	}
 }
modifiedpallets/unique/src/lib.rsdiffbeforeafterboth
--- a/pallets/unique/src/lib.rs
+++ b/pallets/unique/src/lib.rs
@@ -687,7 +687,7 @@
 		/// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].
 		///
 		/// * owner: Address, initial owner of the NFT.
-		#[weight = T::CommonWeightInfo::create_multiple_items(items_data.len() as u32)]
+		#[weight = T::CommonWeightInfo::create_multiple_items(&items_data)]
 		#[transactional]
 		pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResultWithPostInfo {
 			ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);
modifiedprimitives/data-structs/Cargo.tomldiffbeforeafterboth
--- a/primitives/data-structs/Cargo.toml
+++ b/primitives/data-structs/Cargo.toml
@@ -42,3 +42,4 @@
 ]
 serde1 = ["serde"]
 limit-testing = []
+runtime-benchmarks = []
\ No newline at end of file
modifiedprimitives/data-structs/src/lib.rsdiffbeforeafterboth
--- a/primitives/data-structs/src/lib.rs
+++ b/primitives/data-structs/src/lib.rs
@@ -861,7 +861,9 @@
 	) -> Result<(), PropertiesError> {
 		let value_len = value.len();
 
-		if self.consumed_space as usize + value_len > self.space_limit as usize {
+		if self.consumed_space as usize + value_len > self.space_limit as usize
+			&& !cfg!(feature = "runtime-benchmarks")
+		{
 			return Err(PropertiesError::NoSpaceForProperty);
 		}
 
modifiedruntime/common/src/eth_sponsoring.rsdiffbeforeafterboth
--- a/runtime/common/src/eth_sponsoring.rs
+++ b/runtime/common/src/eth_sponsoring.rs
@@ -50,16 +50,20 @@
 			CollectionMode::NFT => {
 				let call = <UniqueNFTCall<T>>::parse(method_id, &mut reader).ok()??;
 				match call {
-					UniqueNFTCall::TokenProperties(
-						TokenPropertiesCall::SetProperty { token_id, key, value, .. },
-					) => {
+					UniqueNFTCall::TokenProperties(TokenPropertiesCall::SetProperty {
+						token_id,
+						key,
+						value,
+						..
+					}) => {
 						let token_id: TokenId = token_id.try_into().ok()?;
 						withdraw_set_token_property::<T>(
 							&collection,
 							&who,
 							&token_id,
 							key.len() + value.len(),
-						).map(|()| sponsor)
+						)
+						.map(|()| sponsor)
 					}
 					UniqueNFTCall::ERC721UniqueExtensions(
 						ERC721UniqueExtensionsCall::Transfer { token_id, .. },
modifiedruntime/common/src/runtime_apis.rsdiffbeforeafterboth
--- a/runtime/common/src/runtime_apis.rs
+++ b/runtime/common/src/runtime_apis.rs
@@ -776,6 +776,7 @@
                     let mut list = Vec::<BenchmarkList>::new();
 
                     list_benchmark!(list, extra, pallet_evm_migration, EvmMigration);
+                    list_benchmark!(list, extra, pallet_common, Common);
                     list_benchmark!(list, extra, pallet_unique, Unique);
                     list_benchmark!(list, extra, pallet_structure, Structure);
                     list_benchmark!(list, extra, pallet_inflation, Inflation);
@@ -814,6 +815,7 @@
                     let params = (&config, &allowlist);
 
                     add_benchmark!(params, batches, pallet_evm_migration, EvmMigration);
+                    add_benchmark!(params, batches, pallet_common, Common);
                     add_benchmark!(params, batches, pallet_unique, Unique);
                     add_benchmark!(params, batches, pallet_structure, Structure);
                     add_benchmark!(params, batches, pallet_inflation, Inflation);
modifiedruntime/common/src/sponsoring.rsdiffbeforeafterboth
--- a/runtime/common/src/sponsoring.rs
+++ b/runtime/common/src/sponsoring.rs
@@ -29,8 +29,8 @@
 use pallet_evm::account::CrossAccountId;
 use pallet_unique::{
 	Call as UniqueCall, Config as UniqueConfig, FungibleApproveBasket, RefungibleApproveBasket,
-	NftApproveBasket, CreateItemBasket, ReFungibleTransferBasket,
-	FungibleTransferBasket, NftTransferBasket, TokenPropertyBasket,
+	NftApproveBasket, CreateItemBasket, ReFungibleTransferBasket, FungibleTransferBasket,
+	NftTransferBasket, TokenPropertyBasket,
 };
 use pallet_fungible::Config as FungibleConfig;
 use pallet_nonfungible::Config as NonfungibleConfig;
@@ -247,7 +247,7 @@
 					&T::CrossAccountId::from_sub(who.clone()),
 					&token_id,
 					// No overflow may happen, as data larger than usize can't reach here
-					properties.iter().map(|p| p.key.len() + p.value.len()).sum()
+					properties.iter().map(|p| p.key.len() + p.value.len()).sum(),
 				)
 				.map(|()| sponsor)
 			}
modifiedruntime/common/src/weights.rsdiffbeforeafterboth
--- a/runtime/common/src/weights.rs
+++ b/runtime/common/src/weights.rs
@@ -21,7 +21,7 @@
 use pallet_fungible::{Config as FungibleConfig, common::CommonWeights as FungibleWeights};
 use pallet_nonfungible::{Config as NonfungibleConfig, common::CommonWeights as NonfungibleWeights};
 use pallet_refungible::{Config as RefungibleConfig, common::CommonWeights as RefungibleWeights};
-use up_data_structs::CreateItemExData;
+use up_data_structs::{CreateItemExData, CreateItemData};
 
 macro_rules! max_weight_of {
 	($method:ident ( $($args:tt)* )) => {
@@ -42,8 +42,8 @@
 		dispatch_weight::<T>() + max_weight_of!(create_item())
 	}
 
-	fn create_multiple_items(amount: u32) -> Weight {
-		dispatch_weight::<T>() + max_weight_of!(create_multiple_items(amount))
+	fn create_multiple_items(data: &[CreateItemData]) -> Weight {
+		dispatch_weight::<T>() + max_weight_of!(create_multiple_items(data))
 	}
 
 	fn create_multiple_items_ex(data: &CreateItemExData<T::CrossAccountId>) -> Weight {
modifiedruntime/opal/src/lib.rsdiffbeforeafterboth
--- a/runtime/opal/src/lib.rs
+++ b/runtime/opal/src/lib.rs
@@ -875,6 +875,7 @@
 }
 
 impl pallet_common::Config for Runtime {
+	type WeightInfo = pallet_common::weights::SubstrateWeight<Self>;
 	type Event = Event;
 	type Currency = Balances;
 	type CollectionCreationPrice = CollectionCreationPrice;
modifiedruntime/tests/src/lib.rsdiffbeforeafterboth
--- a/runtime/tests/src/lib.rs
+++ b/runtime/tests/src/lib.rs
@@ -212,6 +212,7 @@
 }
 
 impl pallet_common::Config for Test {
+	type WeightInfo = ();
 	type Event = ();
 	type Currency = Balances;
 	type CollectionCreationPrice = CollectionCreationPrice;
modifiedruntime/unique/Cargo.tomldiffbeforeafterboth
--- a/runtime/unique/Cargo.toml
+++ b/runtime/unique/Cargo.toml
@@ -39,6 +39,7 @@
     'pallet-xcm/runtime-benchmarks',
     'sp-runtime/runtime-benchmarks',
     'xcm-builder/runtime-benchmarks',
+    'up-data-structs/runtime-benchmarks',
 ]
 try-runtime = [
     'frame-try-runtime',
modifiedruntime/unique/src/lib.rsdiffbeforeafterboth
--- a/runtime/unique/src/lib.rs
+++ b/runtime/unique/src/lib.rs
@@ -66,7 +66,12 @@
 		WeightToFeePolynomial, WeightToFeeCoefficient, WeightToFeeCoefficients, ConstantMultiplier,
 	},
 };
-use unique_runtime_common::dispatch::{CollectionDispatchT, CollectionDispatch};
+use unique_runtime_common::{
+	dispatch::{CollectionDispatchT, CollectionDispatch},
+	weights::CommonWeights,
+	sponsoring::UniqueSponsorshipHandler,
+	eth_sponsoring::UniqueEthSponsorshipHandler,
+};
 use up_data_structs::*;
 // use pallet_contracts::weights::WeightInfo;
 // #[cfg(any(feature = "std", test))]
@@ -846,6 +851,7 @@
 }
 
 impl pallet_common::Config for Runtime {
+	type WeightInfo = pallet_common::weights::SubstrateWeight<Self>;
 	type Event = Event;
 	type Currency = Balances;
 	type CollectionCreationPrice = CollectionCreationPrice;
@@ -881,6 +887,7 @@
 impl pallet_unique::Config for Runtime {
 	type Event = Event;
 	type WeightInfo = pallet_unique::weights::SubstrateWeight<Self>;
+	type CommonWeightInfo = CommonWeights<Self>;
 }
 
 parameter_types! {
@@ -902,11 +909,11 @@
 // }
 
 type EvmSponsorshipHandler = (
-	pallet_unique::UniqueEthSponsorshipHandler<Runtime>,
+	UniqueEthSponsorshipHandler<Runtime>,
 	pallet_evm_contract_helpers::HelpersContractSponsoring<Runtime>,
 );
 type SponsorshipHandler = (
-	pallet_unique::UniqueSponsorshipHandler<Runtime>,
+	UniqueSponsorshipHandler<Runtime>,
 	//pallet_contract_helpers::ContractSponsorshipHandler<Runtime>,
 	pallet_evm_transaction_payment::BridgeSponsorshipHandler<Runtime>,
 );