git.delta.rocks / unique-network / refs/commits / 21b23e7b1db8

difftreelog

feat benchmark rmrk proxy equip

Daniel Shiposha2022-06-16parent: #c214759.patch.diff
in: master

9 files changed

modifiedMakefilediffbeforeafterboth
--- a/Makefile
+++ b/Makefile
@@ -101,5 +101,9 @@
 bench-rmrk-core:
 	make _bench PALLET=proxy-rmrk-core
 
+.PHONY: bench-rmrk-equip
+bench-rmrk-equip:
+	make _bench PALLET=proxy-rmrk-equip
+
 .PHONY: bench
-bench: bench-evm-migration bench-unique bench-structure bench-fungible bench-refungible bench-nonfungible bench-scheduler bench-rmrk-core
+bench: bench-evm-migration bench-unique bench-structure bench-fungible bench-refungible bench-nonfungible bench-scheduler bench-rmrk-core bench-rmrk-equip
addedpallets/proxy-rmrk-equip/src/benchmarking.rsdiffbeforeafterboth
--- /dev/null
+++ b/pallets/proxy-rmrk-equip/src/benchmarking.rs
@@ -0,0 +1,91 @@
+use sp_std::vec;
+
+use frame_benchmarking::{benchmarks, account};
+use frame_system::RawOrigin;
+use frame_support::{
+	traits::{Currency, Get},
+	BoundedVec,
+};
+
+use up_data_structs::*;
+
+use super::*;
+
+const SEED: u32 = 1;
+
+fn create_data<S: Get<u32>>() -> BoundedVec<u8, S> {
+	vec![b'A'; S::get() as usize].try_into().expect("size == S")
+}
+
+fn create_u32_array<S: Get<u32>>() -> BoundedVec<u32, S> {
+	vec![0; S::get() as usize].try_into().expect("size == S")
+}
+
+fn create_max_part() -> RmrkPartType {
+    RmrkPartType::SlotPart(
+        RmrkSlotPart {
+            id: 42,
+            equippable: RmrkEquippableList::Custom(create_u32_array()),
+            src: create_data(),
+            z: 1,
+        }
+    )
+}
+
+fn create_parts_array<S: Get<u32>>(num: u32) -> BoundedVec<RmrkPartType, S> {
+	vec![create_max_part(); num as usize].try_into().expect("num <= S")
+}
+
+fn create_max_theme_property() -> RmrkThemeProperty {
+    RmrkThemeProperty {
+        key: create_data(),
+        value: create_data(),
+    }   
+}
+
+fn create_theme_properties_array<S: Get<u32>>(num: u32) -> BoundedVec<RmrkThemeProperty, S> {
+	vec![create_max_theme_property(); num as usize].try_into().expect("num <= S")
+}
+
+fn create_max_theme(name: RmrkString, props_num: u32) -> RmrkBoundedTheme {
+    RmrkBoundedTheme {
+        name,
+        properties: create_theme_properties_array(props_num),
+        inherit: false,
+    }
+}
+
+benchmarks! {
+    create_base {
+        let b in 0..RmrkPartsLimit::get();
+
+        let caller = account("caller", 0, SEED);
+		<T as pallet_common::Config>::Currency::deposit_creating(&caller, T::CollectionCreationPrice::get());
+
+        let base_type = create_data();
+        let symbol = create_data();
+        let parts = create_parts_array(b);
+    }: _(RawOrigin::Signed(caller), base_type, symbol, parts)
+
+    theme_add {
+        let b in 0..MaxPropertiesPerTheme::get();
+
+        let caller = account("caller", 0, SEED);
+		<T as pallet_common::Config>::Currency::deposit_creating(&caller, T::CollectionCreationPrice::get());
+
+        let base_type = create_data();
+        let symbol = create_data();
+        let parts = create_parts_array(0);
+
+        <Pallet<T>>::create_base(RawOrigin::Signed(caller.clone()).into(), base_type, symbol, parts)?;
+        let base_id = 1;
+
+        
+        let default_theme_name = b"default".to_vec().try_into().expect("default is a valid name; qed");
+        let default_theme = create_max_theme(default_theme_name, 0);
+
+        <Pallet<T>>::theme_add(RawOrigin::Signed(caller.clone()).into(), base_id, default_theme)?;
+
+        let theme = create_max_theme(create_data(), b);
+    }: _(RawOrigin::Signed(caller), base_id, theme)
+}
modifiedpallets/proxy-rmrk-equip/src/lib.rsdiffbeforeafterboth
before · pallets/proxy-rmrk-equip/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 frame_support::{pallet_prelude::*, transactional, BoundedVec, dispatch::DispatchResult};20use frame_system::{pallet_prelude::*, ensure_signed};21use sp_runtime::DispatchError;22use up_data_structs::*;23use pallet_common::{Pallet as PalletCommon, Error as CommonError};24use pallet_rmrk_core::{25	Pallet as PalletCore,26	misc::{self, *},27	property::RmrkProperty::*,28};29use pallet_nonfungible::{Pallet as PalletNft, NonfungibleHandle};30use pallet_evm::account::CrossAccountId;3132pub use pallet::*;3334#[frame_support::pallet]35pub mod pallet {36	use super::*;3738	#[pallet::config]39	pub trait Config: frame_system::Config + pallet_rmrk_core::Config {40		type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;41	}4243	#[pallet::storage]44	#[pallet::getter(fn internal_part_id)]45	pub type InernalPartId<T: Config> =46		StorageDoubleMap<_, Twox64Concat, CollectionId, Twox64Concat, RmrkPartId, TokenId>;4748	#[pallet::storage]49	#[pallet::getter(fn base_has_default_theme)]50	pub type BaseHasDefaultTheme<T: Config> =51		StorageMap<_, Twox64Concat, CollectionId, bool, ValueQuery>;5253	#[pallet::pallet]54	#[pallet::generate_store(pub(super) trait Store)]55	pub struct Pallet<T>(_);5657	#[pallet::event]58	#[pallet::generate_deposit(pub(super) fn deposit_event)]59	pub enum Event<T: Config> {60		BaseCreated {61			issuer: T::AccountId,62			base_id: RmrkBaseId,63		},64	}6566	#[pallet::error]67	pub enum Error<T> {68		PermissionError,69		NoAvailableBaseId,70		NoAvailablePartId,71		BaseDoesntExist,72		NeedsDefaultThemeFirst,73	}7475	#[pallet::call]76	impl<T: Config> Pallet<T> {77		/// Creates a new Base.78		/// Modeled after [base interaction](https://github.com/rmrk-team/rmrk-spec/blob/master/standards/rmrk2.0.0/interactions/base.md)79		///80		/// Parameters:81		/// - origin: Caller, will be assigned as the issuer of the Base82		/// - base_type: media type, e.g. "svg"83		/// - symbol: arbitrary client-chosen symbol84		/// - parts: array of Fixed and Slot parts composing the base, confined in length by85		///   RmrkPartsLimit86		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]87		#[transactional]88		pub fn create_base(89			origin: OriginFor<T>,90			base_type: RmrkString,91			symbol: RmrkString,92			parts: BoundedVec<RmrkPartType, RmrkPartsLimit>,93		) -> DispatchResult {94			let sender = ensure_signed(origin)?;95			let cross_sender = T::CrossAccountId::from_sub(sender.clone());9697			let data = CreateCollectionData {98				limits: None,99				token_prefix: symbol100					.into_inner()101					.try_into()102					.map_err(|_| <CommonError<T>>::CollectionTokenPrefixLimitExceeded)?,103				..Default::default()104			};105106			let collection_id_res =107				<PalletNft<T>>::init_collection(cross_sender.clone(), data, true);108109			if let Err(DispatchError::Arithmetic(_)) = &collection_id_res {110				return Err(<Error<T>>::NoAvailableBaseId.into());111			}112113			let collection_id = collection_id_res?;114115			<PalletCommon<T>>::set_scoped_collection_properties(116				collection_id,117				PropertyScope::Rmrk,118				[119					<PalletCore<T>>::rmrk_property(CollectionType, &misc::CollectionType::Base)?,120					<PalletCore<T>>::rmrk_property(BaseType, &base_type)?,121				]122				.into_iter(),123			)?;124125			let collection = <PalletCore<T>>::get_nft_collection(collection_id)?;126127			for part in parts {128				let part_id = part.id();129				let part_token_id = Self::create_part(&cross_sender, &collection, part)?;130131				<InernalPartId<T>>::insert(collection_id, part_id, part_token_id);132133				<PalletNft<T>>::set_scoped_token_property(134					collection_id,135					part_token_id,136					PropertyScope::Rmrk,137					<PalletCore<T>>::rmrk_property(ExternalPartId, &part_id)?,138				)?;139			}140141			Self::deposit_event(Event::BaseCreated {142				issuer: sender,143				base_id: collection_id.0,144			});145146			Ok(())147		}148149		/// Adds a Theme to a Base.150		/// Modeled after [themeadd interaction](https://github.com/rmrk-team/rmrk-spec/blob/master/standards/rmrk2.0.0/interactions/themeadd.md)151		/// Themes are stored in the Themes storage152		/// A Theme named "default" is required prior to adding other Themes.153		///154		/// Parameters:155		/// - origin: The caller of the function, must be issuer of the base156		/// - base_id: The Base containing the Theme to be updated157		/// - theme: The Theme to add to the Base.  A Theme has a name and properties, which are an158		///   array of [key, value, inherit].159		///   - key: arbitrary BoundedString, defined by client160		///   - value: arbitrary BoundedString, defined by client161		///   - inherit: optional bool162		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]163		#[transactional]164		pub fn theme_add(165			origin: OriginFor<T>,166			base_id: RmrkBaseId,167			theme: RmrkTheme,168		) -> DispatchResult {169			let sender = ensure_signed(origin)?;170171			let sender = T::CrossAccountId::from_sub(sender);172			let owner = &sender;173174			let collection_id: CollectionId = base_id.into();175176			let collection = <PalletCore<T>>::get_typed_nft_collection(177				collection_id,178				misc::CollectionType::Base,179			)180			.map_err(|_| <Error<T>>::BaseDoesntExist)?;181			collection.check_is_external()?;182183			if theme.name.as_slice() == b"default" {184				<BaseHasDefaultTheme<T>>::insert(collection_id, true);185			} else if !Self::base_has_default_theme(collection_id) {186				return Err(<Error<T>>::NeedsDefaultThemeFirst.into());187			}188189			let token_id = <PalletCore<T>>::create_nft(190				&sender,191				owner,192				&collection,193				[194					<PalletCore<T>>::rmrk_property(TokenType, &NftType::Theme)?,195					<PalletCore<T>>::rmrk_property(ThemeName, &theme.name)?,196					<PalletCore<T>>::rmrk_property(ThemeInherit, &theme.inherit)?,197				]198				.into_iter(),199			)200			.map_err(|_| <Error<T>>::PermissionError)?;201202			for property in theme.properties {203				<PalletNft<T>>::set_scoped_token_property(204					collection_id,205					token_id,206					PropertyScope::Rmrk,207					<PalletCore<T>>::rmrk_property(208						UserProperty(property.key.as_slice()),209						&property.value,210					)?,211				)?;212			}213214			Ok(())215		}216	}217}218219impl<T: Config> Pallet<T> {220	fn create_part(221		sender: &T::CrossAccountId,222		collection: &NonfungibleHandle<T>,223		part: RmrkPartType,224	) -> Result<TokenId, DispatchError> {225		let owner = sender;226227		let src = part.src();228		let z_index = part.z_index();229230		let nft_type = match part {231			RmrkPartType::FixedPart(_) => NftType::FixedPart,232			RmrkPartType::SlotPart(_) => NftType::SlotPart,233		};234235		let token_id = <PalletCore<T>>::create_nft(236			sender,237			owner,238			collection,239			[240				<PalletCore<T>>::rmrk_property(TokenType, &nft_type)?,241				<PalletCore<T>>::rmrk_property(Src, &src)?,242				<PalletCore<T>>::rmrk_property(ZIndex, &z_index)?,243			]244			.into_iter(),245		)246		.map_err(|err| match err {247			DispatchError::Arithmetic(_) => <Error<T>>::NoAvailablePartId.into(),248			err => err,249		})?;250251		if let RmrkPartType::SlotPart(part) = part {252			<PalletNft<T>>::set_scoped_token_property(253				collection.id,254				token_id,255				PropertyScope::Rmrk,256				<PalletCore<T>>::rmrk_property(EquippableList, &part.equippable)?,257			)?;258		}259260		Ok(token_id)261	}262}
after · pallets/proxy-rmrk-equip/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 frame_support::{pallet_prelude::*, transactional, BoundedVec, dispatch::DispatchResult};20use frame_system::{pallet_prelude::*, ensure_signed};21use sp_runtime::DispatchError;22use up_data_structs::*;23use pallet_common::{Pallet as PalletCommon, Error as CommonError};24use pallet_rmrk_core::{25	Pallet as PalletCore,26	misc::{self, *},27	property::RmrkProperty::*,28};29use pallet_nonfungible::{Pallet as PalletNft, NonfungibleHandle};30use pallet_evm::account::CrossAccountId;31use weights::WeightInfo;3233pub use pallet::*;3435#[cfg(feature = "runtime-benchmarks")]36pub mod benchmarking;37pub mod weights;3839pub type SelfWeightOf<T> = <T as Config>::WeightInfo;4041#[frame_support::pallet]42pub mod pallet {43	use super::*;4445	#[pallet::config]46	pub trait Config: frame_system::Config + pallet_rmrk_core::Config {47		type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;48		type WeightInfo: WeightInfo;49	}5051	#[pallet::storage]52	#[pallet::getter(fn internal_part_id)]53	pub type InernalPartId<T: Config> =54		StorageDoubleMap<_, Twox64Concat, CollectionId, Twox64Concat, RmrkPartId, TokenId>;5556	#[pallet::storage]57	#[pallet::getter(fn base_has_default_theme)]58	pub type BaseHasDefaultTheme<T: Config> =59		StorageMap<_, Twox64Concat, CollectionId, bool, ValueQuery>;6061	#[pallet::pallet]62	#[pallet::generate_store(pub(super) trait Store)]63	pub struct Pallet<T>(_);6465	#[pallet::event]66	#[pallet::generate_deposit(pub(super) fn deposit_event)]67	pub enum Event<T: Config> {68		BaseCreated {69			issuer: T::AccountId,70			base_id: RmrkBaseId,71		},72	}7374	#[pallet::error]75	pub enum Error<T> {76		PermissionError,77		NoAvailableBaseId,78		NoAvailablePartId,79		BaseDoesntExist,80		NeedsDefaultThemeFirst,81	}8283	#[pallet::call]84	impl<T: Config> Pallet<T> {85		/// Creates a new Base.86		/// Modeled after [base interaction](https://github.com/rmrk-team/rmrk-spec/blob/master/standards/rmrk2.0.0/interactions/base.md)87		///88		/// Parameters:89		/// - origin: Caller, will be assigned as the issuer of the Base90		/// - base_type: media type, e.g. "svg"91		/// - symbol: arbitrary client-chosen symbol92		/// - parts: array of Fixed and Slot parts composing the base, confined in length by93		///   RmrkPartsLimit94		#[transactional]95		#[pallet::weight(<SelfWeightOf<T>>::create_base(parts.len() as u32))]96		pub fn create_base(97			origin: OriginFor<T>,98			base_type: RmrkString,99			symbol: RmrkBaseSymbol,100			parts: BoundedVec<RmrkPartType, RmrkPartsLimit>,101		) -> DispatchResult {102			let sender = ensure_signed(origin)?;103			let cross_sender = T::CrossAccountId::from_sub(sender.clone());104105			let data = CreateCollectionData {106				limits: None,107				token_prefix: symbol108					.into_inner()109					.try_into()110					.map_err(|_| <CommonError<T>>::CollectionTokenPrefixLimitExceeded)?,111				..Default::default()112			};113114			let collection_id_res =115				<PalletNft<T>>::init_collection(cross_sender.clone(), data, true);116117			if let Err(DispatchError::Arithmetic(_)) = &collection_id_res {118				return Err(<Error<T>>::NoAvailableBaseId.into());119			}120121			let collection_id = collection_id_res?;122123			<PalletCommon<T>>::set_scoped_collection_properties(124				collection_id,125				PropertyScope::Rmrk,126				[127					<PalletCore<T>>::rmrk_property(CollectionType, &misc::CollectionType::Base)?,128					<PalletCore<T>>::rmrk_property(BaseType, &base_type)?,129				]130				.into_iter(),131			)?;132133			let collection = <PalletCore<T>>::get_nft_collection(collection_id)?;134135			for part in parts {136				let part_id = part.id();137				let part_token_id = Self::create_part(&cross_sender, &collection, part)?;138139				<InernalPartId<T>>::insert(collection_id, part_id, part_token_id);140141				<PalletNft<T>>::set_scoped_token_property(142					collection_id,143					part_token_id,144					PropertyScope::Rmrk,145					<PalletCore<T>>::rmrk_property(ExternalPartId, &part_id)?,146				)?;147			}148149			Self::deposit_event(Event::BaseCreated {150				issuer: sender,151				base_id: collection_id.0,152			});153154			Ok(())155		}156157		/// Adds a Theme to a Base.158		/// Modeled after [themeadd interaction](https://github.com/rmrk-team/rmrk-spec/blob/master/standards/rmrk2.0.0/interactions/themeadd.md)159		/// Themes are stored in the Themes storage160		/// A Theme named "default" is required prior to adding other Themes.161		///162		/// Parameters:163		/// - origin: The caller of the function, must be issuer of the base164		/// - base_id: The Base containing the Theme to be updated165		/// - theme: The Theme to add to the Base.  A Theme has a name and properties, which are an166		///   array of [key, value, inherit].167		///   - key: arbitrary BoundedString, defined by client168		///   - value: arbitrary BoundedString, defined by client169		///   - inherit: optional bool170		#[transactional]171		#[pallet::weight(<SelfWeightOf<T>>::theme_add(theme.properties.len() as u32))]172		pub fn theme_add(173			origin: OriginFor<T>,174			base_id: RmrkBaseId,175			theme: RmrkBoundedTheme,176		) -> DispatchResult {177			let sender = ensure_signed(origin)?;178179			let sender = T::CrossAccountId::from_sub(sender);180			let owner = &sender;181182			let collection_id: CollectionId = base_id.into();183184			let collection = <PalletCore<T>>::get_typed_nft_collection(185				collection_id,186				misc::CollectionType::Base,187			)188			.map_err(|_| <Error<T>>::BaseDoesntExist)?;189			collection.check_is_external()?;190191			if theme.name.as_slice() == b"default" {192				<BaseHasDefaultTheme<T>>::insert(collection_id, true);193			} else if !Self::base_has_default_theme(collection_id) {194				return Err(<Error<T>>::NeedsDefaultThemeFirst.into());195			}196197			let token_id = <PalletCore<T>>::create_nft(198				&sender,199				owner,200				&collection,201				[202					<PalletCore<T>>::rmrk_property(TokenType, &NftType::Theme)?,203					<PalletCore<T>>::rmrk_property(ThemeName, &theme.name)?,204					<PalletCore<T>>::rmrk_property(ThemeInherit, &theme.inherit)?,205				]206				.into_iter(),207			)208			.map_err(|_| <Error<T>>::PermissionError)?;209210			for property in theme.properties {211				<PalletNft<T>>::set_scoped_token_property(212					collection_id,213					token_id,214					PropertyScope::Rmrk,215					<PalletCore<T>>::rmrk_property(216						UserProperty(property.key.as_slice()),217						&property.value,218					)?,219				)?;220			}221222			Ok(())223		}224	}225}226227impl<T: Config> Pallet<T> {228	fn create_part(229		sender: &T::CrossAccountId,230		collection: &NonfungibleHandle<T>,231		part: RmrkPartType,232	) -> Result<TokenId, DispatchError> {233		let owner = sender;234235		let src = part.src();236		let z_index = part.z_index();237238		let nft_type = match part {239			RmrkPartType::FixedPart(_) => NftType::FixedPart,240			RmrkPartType::SlotPart(_) => NftType::SlotPart,241		};242243		let token_id = <PalletCore<T>>::create_nft(244			sender,245			owner,246			collection,247			[248				<PalletCore<T>>::rmrk_property(TokenType, &nft_type)?,249				<PalletCore<T>>::rmrk_property(Src, &src)?,250				<PalletCore<T>>::rmrk_property(ZIndex, &z_index)?,251			]252			.into_iter(),253		)254		.map_err(|err| match err {255			DispatchError::Arithmetic(_) => <Error<T>>::NoAvailablePartId.into(),256			err => err,257		})?;258259		if let RmrkPartType::SlotPart(part) = part {260			<PalletNft<T>>::set_scoped_token_property(261				collection.id,262				token_id,263				PropertyScope::Rmrk,264				<PalletCore<T>>::rmrk_property(EquippableList, &part.equippable)?,265			)?;266		}267268		Ok(token_id)269	}270}
addedpallets/proxy-rmrk-equip/src/weights.rsdiffbeforeafterboth
--- /dev/null
+++ b/pallets/proxy-rmrk-equip/src/weights.rs
@@ -0,0 +1,119 @@
+// Template adopted from https://github.com/paritytech/substrate/blob/master/.maintain/frame-weight-template.hbs
+
+//! Autogenerated weights for pallet_proxy_rmrk_equip
+//!
+//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
+//! DATE: 2022-06-16, STEPS: `50`, REPEAT: 80, LOW RANGE: `[]`, HIGH RANGE: `[]`
+//! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024
+
+// Executed Command:
+// target/release/unique-collator
+// benchmark
+// pallet
+// --pallet
+// pallet-proxy-rmrk-equip
+// --wasm-execution
+// compiled
+// --extrinsic
+// *
+// --template
+// .maintain/frame-weight-template.hbs
+// --steps=50
+// --repeat=80
+// --heap-pages=4096
+// --output=./pallets/proxy-rmrk-equip/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_proxy_rmrk_equip.
+pub trait WeightInfo {
+	fn create_base(b: u32, ) -> Weight;
+	fn theme_add(b: u32, ) -> Weight;
+}
+
+/// Weights for pallet_proxy_rmrk_equip using the Substrate node and recommended hardware.
+pub struct SubstrateWeight<T>(PhantomData<T>);
+impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
+	// Storage: Common CreatedCollectionCount (r:1 w:1)
+	// Storage: Common DestroyedCollectionCount (r:1 w:0)
+	// Storage: System Account (r:2 w:2)
+	// Storage: Common CollectionPropertyPermissions (r:0 w:1)
+	// Storage: Common CollectionProperties (r:0 w:1)
+	// Storage: Common CollectionById (r:0 w:1)
+	// Storage: Nonfungible TokensMinted (r:1 w:1)
+	// Storage: Nonfungible AccountBalance (r:1 w:1)
+	// Storage: Nonfungible TokenProperties (r:1 w:1)
+	// Storage: Nonfungible TokenData (r:0 w:1)
+	// Storage: Nonfungible Owned (r:0 w:1)
+	// Storage: RmrkEquip InernalPartId (r:0 w:1)
+	fn create_base(b: u32, ) -> Weight {
+		(43_216_000 as Weight)
+			// Standard Error: 10_000
+			.saturating_add((16_253_000 as Weight).saturating_mul(b as Weight))
+			.saturating_add(T::DbWeight::get().reads(6 as Weight))
+			.saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(b as Weight)))
+			.saturating_add(T::DbWeight::get().writes(9 as Weight))
+			.saturating_add(T::DbWeight::get().writes((3 as Weight).saturating_mul(b as Weight)))
+	}
+	// Storage: Common CollectionProperties (r:1 w:0)
+	// Storage: Common CollectionById (r:1 w:0)
+	// Storage: RmrkEquip BaseHasDefaultTheme (r:1 w:0)
+	// Storage: Nonfungible TokensMinted (r:1 w:1)
+	// Storage: Nonfungible AccountBalance (r:1 w:1)
+	// Storage: Nonfungible TokenProperties (r:1 w:1)
+	// Storage: Nonfungible TokenData (r:0 w:1)
+	// Storage: Nonfungible Owned (r:0 w:1)
+	fn theme_add(b: u32, ) -> Weight {
+		(39_467_000 as Weight)
+			// Standard Error: 15_000
+			.saturating_add((2_332_000 as Weight).saturating_mul(b as Weight))
+			.saturating_add(T::DbWeight::get().reads(6 as Weight))
+			.saturating_add(T::DbWeight::get().writes(5 as Weight))
+	}
+}
+
+// For backwards compatibility and tests
+impl WeightInfo for () {
+	// Storage: Common CreatedCollectionCount (r:1 w:1)
+	// Storage: Common DestroyedCollectionCount (r:1 w:0)
+	// Storage: System Account (r:2 w:2)
+	// Storage: Common CollectionPropertyPermissions (r:0 w:1)
+	// Storage: Common CollectionProperties (r:0 w:1)
+	// Storage: Common CollectionById (r:0 w:1)
+	// Storage: Nonfungible TokensMinted (r:1 w:1)
+	// Storage: Nonfungible AccountBalance (r:1 w:1)
+	// Storage: Nonfungible TokenProperties (r:1 w:1)
+	// Storage: Nonfungible TokenData (r:0 w:1)
+	// Storage: Nonfungible Owned (r:0 w:1)
+	// Storage: RmrkEquip InernalPartId (r:0 w:1)
+	fn create_base(b: u32, ) -> Weight {
+		(43_216_000 as Weight)
+			// Standard Error: 10_000
+			.saturating_add((16_253_000 as Weight).saturating_mul(b as Weight))
+			.saturating_add(RocksDbWeight::get().reads(6 as Weight))
+			.saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(b as Weight)))
+			.saturating_add(RocksDbWeight::get().writes(9 as Weight))
+			.saturating_add(RocksDbWeight::get().writes((3 as Weight).saturating_mul(b as Weight)))
+	}
+	// Storage: Common CollectionProperties (r:1 w:0)
+	// Storage: Common CollectionById (r:1 w:0)
+	// Storage: RmrkEquip BaseHasDefaultTheme (r:1 w:0)
+	// Storage: Nonfungible TokensMinted (r:1 w:1)
+	// Storage: Nonfungible AccountBalance (r:1 w:1)
+	// Storage: Nonfungible TokenProperties (r:1 w:1)
+	// Storage: Nonfungible TokenData (r:0 w:1)
+	// Storage: Nonfungible Owned (r:0 w:1)
+	fn theme_add(b: u32, ) -> Weight {
+		(39_467_000 as Weight)
+			// Standard Error: 15_000
+			.saturating_add((2_332_000 as Weight).saturating_mul(b as Weight))
+			.saturating_add(RocksDbWeight::get().reads(6 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(5 as Weight))
+	}
+}
modifiedprimitives/data-structs/src/lib.rsdiffbeforeafterboth
--- a/primitives/data-structs/src/lib.rs
+++ b/primitives/data-structs/src/lib.rs
@@ -950,7 +950,9 @@
 	#[derive(PartialEq)]
 	pub const RmrkCollectionSymbolLimit: u32 = MAX_TOKEN_PREFIX_LENGTH;
 	#[derive(PartialEq)]
-	pub const RmrkResourceSymbolLimit: u32 = MAX_TOKEN_PREFIX_LENGTH;
+	pub const RmrkResourceSymbolLimit: u32 = 10;
+	#[derive(PartialEq)]
+	pub const RmrkBaseSymbolLimit: u32 = MAX_TOKEN_PREFIX_LENGTH;
 	#[derive(PartialEq)]
 	pub const RmrkKeyLimit: u32 = 32;
 	#[derive(PartialEq)]
@@ -958,6 +960,8 @@
 	#[derive(PartialEq)]
 	pub const RmrkMaxCollectionsEquippablePerPart: u32 = 100;
 	#[derive(PartialEq)]
+	pub const MaxPropertiesPerTheme: u32 = 5;
+	#[derive(PartialEq)]
 	pub const RmrkPartsLimit: u32 = 25;
 	#[derive(PartialEq)]
 	pub const RmrkMaxPriorities: u32 = 25;
@@ -987,6 +991,7 @@
 	PartType<RmrkString, BoundedVec<RmrkCollectionId, RmrkMaxCollectionsEquippablePerPart>>;
 pub type RmrkThemeProperty = ThemeProperty<RmrkString>;
 pub type RmrkTheme = Theme<RmrkString, Vec<RmrkThemeProperty>>;
+pub type RmrkBoundedTheme = Theme<RmrkString, BoundedVec<RmrkThemeProperty, MaxPropertiesPerTheme>>;
 pub type RmrkResourceTypes = ResourceTypes<RmrkString, RmrkBoundedParts>;
 
 pub type RmrkBasicResource = BasicResource<RmrkString>;
@@ -995,6 +1000,7 @@
 
 pub type RmrkString = BoundedVec<u8, RmrkStringLimit>;
 pub type RmrkCollectionSymbol = BoundedVec<u8, RmrkCollectionSymbolLimit>;
+pub type RmrkBaseSymbol = BoundedVec<u8, RmrkBaseSymbolLimit>;
 pub type RmrkKeyString = BoundedVec<u8, RmrkKeyLimit>;
 pub type RmrkValueString = BoundedVec<u8, RmrkValueLimit>;
 pub type RmrkBoundedResource = BoundedVec<u8, RmrkResourceSymbolLimit>;
modifiedruntime/common/src/runtime_apis.rsdiffbeforeafterboth
--- a/runtime/common/src/runtime_apis.rs
+++ b/runtime/common/src/runtime_apis.rs
@@ -458,6 +458,9 @@
                     #[cfg(not(feature = "unique-runtime"))]
                     list_benchmark!(list, extra, pallet_proxy_rmrk_core, RmrkCore);
 
+                    #[cfg(not(feature = "unique-runtime"))]
+                    list_benchmark!(list, extra, pallet_proxy_rmrk_equip, RmrkEquip);
+
                     // list_benchmark!(list, extra, pallet_evm_coder_substrate, EvmCoderSubstrate);
 
                     let storage_info = AllPalletsReversedWithSystemFirst::storage_info();
@@ -506,6 +509,9 @@
                     #[cfg(not(feature = "unique-runtime"))]
                     add_benchmark!(params, batches, pallet_proxy_rmrk_core, RmrkCore);
 
+                    #[cfg(not(feature = "unique-runtime"))]
+                    add_benchmark!(params, batches, pallet_proxy_rmrk_equip, RmrkEquip);
+
                     // add_benchmark!(params, batches, pallet_evm_coder_substrate, EvmCoderSubstrate);
 
                     if batches.is_empty() { return Err("Benchmark not found for this pallet.".into()) }
modifiedruntime/opal/Cargo.tomldiffbeforeafterboth
--- a/runtime/opal/Cargo.toml
+++ b/runtime/opal/Cargo.toml
@@ -34,6 +34,7 @@
     'pallet-refungible/runtime-benchmarks',
     'pallet-nonfungible/runtime-benchmarks',
     'pallet-proxy-rmrk-core/runtime-benchmarks',
+    'pallet-proxy-rmrk-equip/runtime-benchmarks',
     'pallet-unique/runtime-benchmarks',
     'pallet-inflation/runtime-benchmarks',
     'pallet-unique-scheduler/runtime-benchmarks',
modifiedruntime/opal/src/lib.rsdiffbeforeafterboth
--- a/runtime/opal/src/lib.rs
+++ b/runtime/opal/src/lib.rs
@@ -923,6 +923,7 @@
 }
 
 impl pallet_proxy_rmrk_equip::Config for Runtime {
+	type WeightInfo = pallet_proxy_rmrk_equip::weights::SubstrateWeight<Self>;
 	type Event = Event;
 }
 
modifiedruntime/quartz/src/lib.rsdiffbeforeafterboth
--- a/runtime/quartz/src/lib.rs
+++ b/runtime/quartz/src/lib.rs
@@ -922,6 +922,7 @@
 }
 
 impl pallet_proxy_rmrk_equip::Config for Runtime {
+	type WeightInfo = pallet_proxy_rmrk_equip::weights::SubstrateWeight<Self>;
 	type Event = Event;
 }