git.delta.rocks / unique-network / refs/commits / 64ceec5bba92

difftreelog

Merge pull request #298 from UniqueNetwork/feature/create-multiple-items-ex

kozyrevdev2022-03-01parents: #c9e84df #9d95058.patch.diff
in: master
Add createMultipleItemsEx call

27 files changed

modified.maintain/frame-weight-template.hbsdiffbeforeafterboth
--- a/.maintain/frame-weight-template.hbs
+++ b/.maintain/frame-weight-template.hbs
@@ -7,7 +7,7 @@
 //! EXECUTION: {{cmd.execution}}, WASM-EXECUTION: {{cmd.wasm_execution}}, CHAIN: {{cmd.chain}}, DB CACHE: {{cmd.db_cache}}
 
 // Executed Command:
-{{#each args as |arg|~}}
+{{#each args as |arg|}}
 // {{arg}}
 {{/each}}
 
@@ -21,76 +21,80 @@
 
 /// Weight functions needed for {{pallet}}.
 pub trait WeightInfo {
-	{{~#each benchmarks as |benchmark|}}
+	{{#each benchmarks as |benchmark|}}
 	fn {{benchmark.name~}}
 	(
 		{{~#each benchmark.components as |c| ~}}
 		{{c.name}}: u32, {{/each~}}
 	) -> Weight;
-	{{~/each}}
+	{{/each}}
 }
 
 /// Weights for {{pallet}} using the Substrate node and recommended hardware.
 pub struct SubstrateWeight<T>(PhantomData<T>);
+{{#if (eq pallet "frame_system")}}
+impl<T: crate::Config> WeightInfo for SubstrateWeight<T> {
+{{else}}
 impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
-	{{~#each benchmarks as |benchmark|}}
-	{{~#each benchmark.comments as |comment|}}
+{{/if}}
+	{{#each benchmarks as |benchmark|}}
+	{{#each benchmark.comments as |comment|}}
 	// {{comment}}
-	{{~/each}}
+	{{/each}}
 	fn {{benchmark.name~}}
 	(
 		{{~#each benchmark.components as |c| ~}}
 		{{~#if (not c.is_used)}}_{{/if}}{{c.name}}: u32, {{/each~}}
 	) -> Weight {
 		({{underscore benchmark.base_weight}} as Weight)
-			{{~#each benchmark.component_weight as |cw|}}
+			{{#each benchmark.component_weight as |cw|}}
 			// Standard Error: {{underscore cw.error}}
 			.saturating_add(({{underscore cw.slope}} as Weight).saturating_mul({{cw.name}} as Weight))
-			{{~/each}}
-			{{~#if (ne benchmark.base_reads "0")}}
+			{{/each}}
+			{{#if (ne benchmark.base_reads "0")}}
 			.saturating_add(T::DbWeight::get().reads({{benchmark.base_reads}} as Weight))
-			{{~/if}}
-			{{~#each benchmark.component_reads as |cr|}}
+			{{/if}}
+			{{#each benchmark.component_reads as |cr|}}
 			.saturating_add(T::DbWeight::get().reads(({{cr.slope}} as Weight).saturating_mul({{cr.name}} as Weight)))
-			{{~/each}}
-			{{~#if (ne benchmark.base_writes "0")}}
+			{{/each}}
+			{{#if (ne benchmark.base_writes "0")}}
 			.saturating_add(T::DbWeight::get().writes({{benchmark.base_writes}} as Weight))
-			{{~/if}}
-			{{~#each benchmark.component_writes as |cw|}}
+			{{/if}}
+			{{#each benchmark.component_writes as |cw|}}
 			.saturating_add(T::DbWeight::get().writes(({{cw.slope}} as Weight).saturating_mul({{cw.name}} as Weight)))
-			{{~/each}}
+			{{/each}}
 	}
-	{{~/each}}
+	{{/each}}
 }
 
 // For backwards compatibility and tests
 impl WeightInfo for () {
-	{{~#each benchmarks as |benchmark|}}
-	{{~#each benchmark.comments as |comment|}}
+	{{#each benchmarks as |benchmark|}}
+	{{#each benchmark.comments as |comment|}}
 	// {{comment}}
-	{{~/each}}
+	{{/each}}
 	fn {{benchmark.name~}}
 	(
 		{{~#each benchmark.components as |c| ~}}
 		{{~#if (not c.is_used)}}_{{/if}}{{c.name}}: u32, {{/each~}}
 	) -> Weight {
 		({{underscore benchmark.base_weight}} as Weight)
-			{{~#each benchmark.component_weight as |cw|}}
+			{{#each benchmark.component_weight as |cw|}}
 			// Standard Error: {{underscore cw.error}}
 			.saturating_add(({{underscore cw.slope}} as Weight).saturating_mul({{cw.name}} as Weight))
-			{{~/each}}
-			{{~#if (ne benchmark.base_reads "0")}}
+			{{/each}}
+			{{#if (ne benchmark.base_reads "0")}}
 			.saturating_add(RocksDbWeight::get().reads({{benchmark.base_reads}} as Weight))
-			{{~/if}}
-			{{~#each benchmark.component_reads as |cr|}}
+			{{/if}}
+			{{#each benchmark.component_reads as |cr|}}
 			.saturating_add(RocksDbWeight::get().reads(({{cr.slope}} as Weight).saturating_mul({{cr.name}} as Weight)))
-			{{~/each}}
-			{{~#if (ne benchmark.base_writes "0")}}
+			{{/each}}
+			{{#if (ne benchmark.base_writes "0")}}
 			.saturating_add(RocksDbWeight::get().writes({{benchmark.base_writes}} as Weight))
-			{{~/if}}
-			{{~#each benchmark.component_writes as |cw|}}
+			{{/if}}
+			{{#each benchmark.component_writes as |cw|}}
 			.saturating_add(RocksDbWeight::get().writes(({{cw.slope}} as Weight).saturating_mul({{cw.name}} as Weight)))
-			{{~/each}}
+			{{/each}}
 	}
-	{{~/each}}
+	{{/each}}
 }
modifiedpallets/common/src/benchmarking.rsdiffbeforeafterboth
--- a/pallets/common/src/benchmarking.rs
+++ b/pallets/common/src/benchmarking.rs
@@ -1,64 +1,87 @@
 use sp_std::vec::Vec;
 use crate::{Config, CollectionHandle};
 use up_data_structs::{
-	CollectionMode, Collection, CollectionId, MAX_COLLECTION_NAME_LENGTH,
+	CollectionMode, CreateCollectionData, CollectionId, MAX_COLLECTION_NAME_LENGTH,
 	MAX_COLLECTION_DESCRIPTION_LENGTH, MAX_TOKEN_PREFIX_LENGTH, OFFCHAIN_SCHEMA_LIMIT,
 	VARIABLE_ON_CHAIN_SCHEMA_LIMIT, CONST_ON_CHAIN_SCHEMA_LIMIT,
 };
-use frame_support::traits::{Currency, Get};
+use frame_support::{
+	traits::{Currency, Get},
+	pallet_prelude::ConstU32,
+	BoundedVec,
+};
 use core::convert::TryInto;
 use sp_runtime::DispatchError;
 
-pub fn create_data(size: usize) -> Vec<u8> {
-	(0..size).map(|v| (v & 0xff) as u8).collect()
+pub fn create_data<const S: u32>() -> BoundedVec<u8, ConstU32<S>> {
+	create_var_data::<S>(S)
 }
-pub fn create_u16_data(size: usize) -> Vec<u16> {
-	(0..size).map(|v| (v & 0xffff) as u16).collect()
+pub fn create_u16_data<const S: u32>() -> BoundedVec<u16, ConstU32<S>> {
+	(0..S)
+		.map(|v| (v & 0xffff) as u16)
+		.collect::<Vec<_>>()
+		.try_into()
+		.unwrap()
+}
+pub fn create_var_data<const S: u32>(size: u32) -> BoundedVec<u8, ConstU32<S>> {
+	assert!(
+		size <= S,
+		"size ({}) should be less within bound ({})",
+		size,
+		S
+	);
+	(0..size)
+		.map(|v| (v & 0xff) as u8)
+		.collect::<Vec<_>>()
+		.try_into()
+		.unwrap()
 }
 
 pub fn create_collection_raw<T: Config, R>(
 	owner: T::AccountId,
 	mode: CollectionMode,
-	handler: impl FnOnce(Collection<T::AccountId>) -> Result<CollectionId, DispatchError>,
+	handler: impl FnOnce(
+		T::AccountId,
+		CreateCollectionData<T::AccountId>,
+	) -> Result<CollectionId, DispatchError>,
 	cast: impl FnOnce(CollectionHandle<T>) -> R,
 ) -> Result<R, DispatchError> {
 	T::Currency::deposit_creating(&owner, T::CollectionCreationPrice::get());
-	let name = create_u16_data(MAX_COLLECTION_NAME_LENGTH)
-		.try_into()
-		.unwrap();
-	let description = create_u16_data(MAX_COLLECTION_DESCRIPTION_LENGTH)
-		.try_into()
-		.unwrap();
-	let token_prefix = create_data(MAX_TOKEN_PREFIX_LENGTH).try_into().unwrap();
-	let offchain_schema = create_data(OFFCHAIN_SCHEMA_LIMIT as usize)
-		.try_into()
-		.unwrap();
-	let variable_on_chain_schema = create_data(VARIABLE_ON_CHAIN_SCHEMA_LIMIT as usize)
-		.try_into()
-		.unwrap();
-	let const_on_chain_schema = create_data(CONST_ON_CHAIN_SCHEMA_LIMIT as usize)
-		.try_into()
-		.unwrap();
-	handler(Collection {
+	let name = create_u16_data::<MAX_COLLECTION_NAME_LENGTH>();
+	let description = create_u16_data::<MAX_COLLECTION_DESCRIPTION_LENGTH>();
+	let token_prefix = create_data::<MAX_TOKEN_PREFIX_LENGTH>();
+	let offchain_schema = create_data::<OFFCHAIN_SCHEMA_LIMIT>();
+	let variable_on_chain_schema = create_data::<VARIABLE_ON_CHAIN_SCHEMA_LIMIT>();
+	let const_on_chain_schema = create_data::<CONST_ON_CHAIN_SCHEMA_LIMIT>();
+	handler(
 		owner,
-		mode,
-		access: Default::default(),
-		name,
-		description,
-		token_prefix,
-		mint_mode: true,
-		offchain_schema,
-		schema_version: Default::default(),
-		sponsorship: Default::default(),
-		limits: Default::default(),
-		variable_on_chain_schema,
-		const_on_chain_schema,
-		meta_update_permission: Default::default(),
-	})
+		CreateCollectionData {
+			mode,
+			name,
+			description,
+			token_prefix,
+			offchain_schema,
+			variable_on_chain_schema,
+			const_on_chain_schema,
+			..Default::default()
+		},
+	)
 	.and_then(CollectionHandle::try_get)
 	.map(cast)
 }
 
+/// Helper macros, which handles all benchmarking preparation in semi-declarative way
+///
+/// `name` is a substrate account
+/// - name: sub[(id)]
+/// `name` is a collection with owner `owner`
+/// - name: collection(owner)
+/// `name` is a cross account based on substrate
+/// - name: cross_sub[(id)]
+/// `name` is a cross account, which maps to substrate account `name`
+/// - name: cross_from_sub
+/// `name` is a cross account, which maps to substrate account `other_name`
+/// - name: cross_from_sub(other_name)
 #[macro_export]
 macro_rules! bench_init {
 	($name:ident: sub $(($id:expr))?; $($rest:tt)*) => {
modifiedpallets/common/src/lib.rsdiffbeforeafterboth
before · pallets/common/src/lib.rs
1#![cfg_attr(not(feature = "std"), no_std)]23use core::ops::{Deref, DerefMut};4use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};5use sp_std::vec::Vec;6use account::CrossAccountId;7use frame_support::{8	dispatch::{DispatchErrorWithPostInfo, DispatchResultWithPostInfo},9	ensure, fail,10	traits::{Imbalance, Get, Currency},11	BoundedVec,12};13use pallet_evm::GasWeightMapping;14use up_data_structs::{15	COLLECTION_NUMBER_LIMIT, Collection, CollectionId, CreateItemData, ExistenceRequirement,16	MAX_TOKEN_PREFIX_LENGTH, COLLECTION_ADMINS_LIMIT, MetaUpdatePermission, Pays, PostDispatchInfo,17	TokenId, Weight, WithdrawReasons, CollectionStats, MAX_TOKEN_OWNERSHIP, CollectionMode,18	NFT_SPONSOR_TRANSFER_TIMEOUT, FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,19	REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, MAX_SPONSOR_TIMEOUT, CUSTOM_DATA_LIMIT, CollectionLimits,20	CustomDataLimit, CreateCollectionData, SponsorshipState,21};22pub use pallet::*;23use sp_core::H160;24use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};25pub mod account;26#[cfg(feature = "runtime-benchmarks")]27pub mod benchmarking;28pub mod erc;29pub mod eth;3031#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]32pub struct CollectionHandle<T: Config> {33	pub id: CollectionId,34	collection: Collection<T::AccountId>,35	pub recorder: SubstrateRecorder<T>,36}37impl<T: Config> WithRecorder<T> for CollectionHandle<T> {38	fn recorder(&self) -> &SubstrateRecorder<T> {39		&self.recorder40	}41	fn into_recorder(self) -> SubstrateRecorder<T> {42		self.recorder43	}44}45impl<T: Config> CollectionHandle<T> {46	pub fn new_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {47		<CollectionById<T>>::get(id).map(|collection| Self {48			id,49			collection,50			recorder: SubstrateRecorder::new(eth::collection_id_to_address(id), gas_limit),51		})52	}53	pub fn new(id: CollectionId) -> Option<Self> {54		Self::new_with_gas_limit(id, u64::MAX)55	}56	pub fn try_get(id: CollectionId) -> Result<Self, DispatchError> {57		Ok(Self::new(id).ok_or(<Error<T>>::CollectionNotFound)?)58	}59	pub fn log_mirrored(&self, log: impl evm_coder::ToLog) {60		self.recorder.log_mirrored(log)61	}62	pub fn log_direct(&self, log: impl evm_coder::ToLog) {63		self.recorder.log_direct(log)64	}65	pub fn consume_store_reads(&self, reads: u64) -> evm_coder::execution::Result<()> {66		self.recorder67			.consume_gas(T::GasWeightMapping::weight_to_gas(68				<T as frame_system::Config>::DbWeight::get()69					.read70					.saturating_mul(reads),71			))72	}73	pub fn consume_store_writes(&self, writes: u64) -> evm_coder::execution::Result<()> {74		self.recorder75			.consume_gas(T::GasWeightMapping::weight_to_gas(76				<T as frame_system::Config>::DbWeight::get()77					.write78					.saturating_mul(writes),79			))80	}81	pub fn submit_logs(self) {82		self.recorder.submit_logs()83	}84	pub fn save(self) -> DispatchResult {85		self.recorder.submit_logs();86		<CollectionById<T>>::insert(self.id, self.collection);87		Ok(())88	}89}90impl<T: Config> Deref for CollectionHandle<T> {91	type Target = Collection<T::AccountId>;9293	fn deref(&self) -> &Self::Target {94		&self.collection95	}96}9798impl<T: Config> DerefMut for CollectionHandle<T> {99	fn deref_mut(&mut self) -> &mut Self::Target {100		&mut self.collection101	}102}103104impl<T: Config> CollectionHandle<T> {105	pub fn check_is_owner(&self, subject: &T::CrossAccountId) -> DispatchResult {106		ensure!(*subject.as_sub() == self.owner, <Error<T>>::NoPermission);107		Ok(())108	}109	pub fn is_owner_or_admin(&self, subject: &T::CrossAccountId) -> bool {110		*subject.as_sub() == self.owner || <IsAdmin<T>>::get((self.id, subject))111	}112	pub fn check_is_owner_or_admin(&self, subject: &T::CrossAccountId) -> DispatchResult {113		ensure!(self.is_owner_or_admin(subject), <Error<T>>::NoPermission);114		Ok(())115	}116	pub fn ignores_allowance(&self, user: &T::CrossAccountId) -> bool {117		self.limits.owner_can_transfer() && self.is_owner_or_admin(user)118	}119	pub fn ignores_owned_amount(&self, user: &T::CrossAccountId) -> bool {120		self.limits.owner_can_transfer() && self.is_owner_or_admin(user)121	}122	pub fn check_allowlist(&self, user: &T::CrossAccountId) -> DispatchResult {123		ensure!(124			<Allowlist<T>>::get((self.id, user)),125			<Error<T>>::AddressNotInAllowlist126		);127		Ok(())128	}129130	pub fn check_can_update_meta(131		&self,132		subject: &T::CrossAccountId,133		item_owner: &T::CrossAccountId,134	) -> DispatchResult {135		match self.meta_update_permission {136			MetaUpdatePermission::ItemOwner => {137				ensure!(subject == item_owner, <Error<T>>::NoPermission);138				Ok(())139			}140			MetaUpdatePermission::Admin => self.check_is_owner_or_admin(subject),141			MetaUpdatePermission::None => fail!(<Error<T>>::NoPermission),142		}143	}144}145146#[frame_support::pallet]147pub mod pallet {148	use super::*;149	use frame_support::{Blake2_128Concat, pallet_prelude::*, storage::Key};150	use account::CrossAccountId;151	use frame_support::traits::Currency;152	use up_data_structs::TokenId;153	use scale_info::TypeInfo;154155	#[pallet::config]156	pub trait Config: frame_system::Config + pallet_evm_coder_substrate::Config + TypeInfo {157		type Event: IsType<<Self as frame_system::Config>::Event> + From<Event<Self>>;158159		type CrossAccountId: CrossAccountId<Self::AccountId>;160161		type EvmAddressMapping: pallet_evm::AddressMapping<Self::AccountId>;162		type EvmBackwardsAddressMapping: up_evm_mapping::EvmBackwardsAddressMapping<Self::AccountId>;163164		type Currency: Currency<Self::AccountId>;165166		#[pallet::constant]167		type CollectionCreationPrice: Get<168			<<Self as Config>::Currency as Currency<Self::AccountId>>::Balance,169		>;170171		type TreasuryAccountId: Get<Self::AccountId>;172	}173174	#[pallet::pallet]175	#[pallet::generate_store(pub(super) trait Store)]176	pub struct Pallet<T>(_);177178	#[pallet::extra_constants]179	impl<T: Config> Pallet<T> {180		pub fn collection_admins_limit() -> u32 {181			COLLECTION_ADMINS_LIMIT182		}183	}184185	#[pallet::event]186	#[pallet::generate_deposit(pub fn deposit_event)]187	pub enum Event<T: Config> {188		/// New collection was created189		///190		/// # Arguments191		///192		/// * collection_id: Globally unique identifier of newly created collection.193		///194		/// * mode: [CollectionMode] converted into u8.195		///196		/// * account_id: Collection owner.197		CollectionCreated(CollectionId, u8, T::AccountId),198199		/// New collection was destroyed200		///201		/// # Arguments202		///203		/// * collection_id: Globally unique identifier of collection.204		CollectionDestroyed(CollectionId),205206		/// New item was created.207		///208		/// # Arguments209		///210		/// * collection_id: Id of the collection where item was created.211		///212		/// * item_id: Id of an item. Unique within the collection.213		///214		/// * recipient: Owner of newly created item215		///216		/// * amount: Always 1 for NFT217		ItemCreated(CollectionId, TokenId, T::CrossAccountId, u128),218219		/// Collection item was burned.220		///221		/// # Arguments222		///223		/// * collection_id.224		///225		/// * item_id: Identifier of burned NFT.226		///227		/// * owner: which user has destroyed its tokens228		///229		/// * amount: Always 1 for NFT230		ItemDestroyed(CollectionId, TokenId, T::CrossAccountId, u128),231232		/// Item was transferred233		///234		/// * collection_id: Id of collection to which item is belong235		///236		/// * item_id: Id of an item237		///238		/// * sender: Original owner of item239		///240		/// * recipient: New owner of item241		///242		/// * amount: Always 1 for NFT243		Transfer(244			CollectionId,245			TokenId,246			T::CrossAccountId,247			T::CrossAccountId,248			u128,249		),250251		/// * collection_id252		///253		/// * item_id254		///255		/// * sender256		///257		/// * spender258		///259		/// * amount260		Approved(261			CollectionId,262			TokenId,263			T::CrossAccountId,264			T::CrossAccountId,265			u128,266		),267	}268269	#[pallet::error]270	pub enum Error<T> {271		/// This collection does not exist.272		CollectionNotFound,273		/// Sender parameter and item owner must be equal.274		MustBeTokenOwner,275		/// No permission to perform action276		NoPermission,277		/// Collection is not in mint mode.278		PublicMintingNotAllowed,279		/// Address is not in allow list.280		AddressNotInAllowlist,281282		/// Collection name can not be longer than 63 char.283		CollectionNameLimitExceeded,284		/// Collection description can not be longer than 255 char.285		CollectionDescriptionLimitExceeded,286		/// Token prefix can not be longer than 15 char.287		CollectionTokenPrefixLimitExceeded,288		/// Total collections bound exceeded.289		TotalCollectionsLimitExceeded,290		/// variable_data exceeded data limit.291		TokenVariableDataLimitExceeded,292		/// Exceeded max admin count293		CollectionAdminCountExceeded,294		/// Collection limit bounds per collection exceeded295		CollectionLimitBoundsExceeded,296		/// Tried to enable permissions which are only permitted to be disabled297		OwnerPermissionsCantBeReverted,298299		/// Collection settings not allowing items transferring300		TransferNotAllowed,301		/// Account token limit exceeded per collection302		AccountTokenLimitExceeded,303		/// Collection token limit exceeded304		CollectionTokenLimitExceeded,305		/// Metadata flag frozen306		MetadataFlagFrozen,307308		/// Item not exists.309		TokenNotFound,310		/// Item balance not enough.311		TokenValueTooLow,312		/// Requested value more than approved.313		ApprovedValueTooLow,314		/// Tried to approve more than owned315		CantApproveMoreThanOwned,316317		/// Can't transfer tokens to ethereum zero address318		AddressIsZero,319		/// Target collection doesn't supports this operation320		UnsupportedOperation,321	}322323	#[pallet::storage]324	pub type CreatedCollectionCount<T> = StorageValue<Value = CollectionId, QueryKind = ValueQuery>;325	#[pallet::storage]326	pub type DestroyedCollectionCount<T> =327		StorageValue<Value = CollectionId, QueryKind = ValueQuery>;328329	/// Collection info330	#[pallet::storage]331	pub type CollectionById<T> = StorageMap<332		Hasher = Blake2_128Concat,333		Key = CollectionId,334		Value = Collection<<T as frame_system::Config>::AccountId>,335		QueryKind = OptionQuery,336	>;337338	#[pallet::storage]339	pub type AdminAmount<T> = StorageMap<340		Hasher = Blake2_128Concat,341		Key = CollectionId,342		Value = u32,343		QueryKind = ValueQuery,344	>;345346	/// List of collection admins347	#[pallet::storage]348	pub type IsAdmin<T: Config> = StorageNMap<349		Key = (350			Key<Blake2_128Concat, CollectionId>,351			Key<Blake2_128Concat, T::CrossAccountId>,352		),353		Value = bool,354		QueryKind = ValueQuery,355	>;356357	/// Allowlisted collection users358	#[pallet::storage]359	pub type Allowlist<T: Config> = StorageNMap<360		Key = (361			Key<Blake2_128Concat, CollectionId>,362			Key<Blake2_128Concat, T::CrossAccountId>,363		),364		Value = bool,365		QueryKind = ValueQuery,366	>;367368	/// Not used by code, exists only to provide some types to metadata369	#[pallet::storage]370	pub type DummyStorageValue<T> =371		StorageValue<Value = (CollectionStats, CollectionId, TokenId), QueryKind = OptionQuery>;372}373374impl<T: Config> Pallet<T> {375	/// Ethereum receiver 0x0000000000000000000000000000000000000000 is reserved, and shouldn't own tokens376	pub fn ensure_correct_receiver(receiver: &T::CrossAccountId) -> DispatchResult {377		ensure!(378			&T::CrossAccountId::from_eth(H160([0; 20])) != receiver,379			<Error<T>>::AddressIsZero380		);381		Ok(())382	}383	pub fn adminlist(collection: CollectionId) -> Vec<T::CrossAccountId> {384		<IsAdmin<T>>::iter_prefix((collection,))385			.map(|(a, _)| a)386			.collect()387	}388	pub fn allowlist(collection: CollectionId) -> Vec<T::CrossAccountId> {389		<Allowlist<T>>::iter_prefix((collection,))390			.map(|(a, _)| a)391			.collect()392	}393	pub fn allowed(collection: CollectionId, user: T::CrossAccountId) -> bool {394		<Allowlist<T>>::get((collection, user))395	}396	pub fn collection_stats() -> CollectionStats {397		let created = <CreatedCollectionCount<T>>::get();398		let destroyed = <DestroyedCollectionCount<T>>::get();399		CollectionStats {400			created: created.0,401			destroyed: destroyed.0,402			alive: created.0 - destroyed.0,403		}404	}405}406407impl<T: Config> Pallet<T> {408	pub fn init_collection(409		owner: T::AccountId,410		data: CreateCollectionData<T::AccountId>,411	) -> Result<CollectionId, DispatchError> {412		{413			ensure!(414				data.token_prefix.len() <= MAX_TOKEN_PREFIX_LENGTH as usize,415				Error::<T>::CollectionTokenPrefixLimitExceeded416			);417		}418419		let created_count = <CreatedCollectionCount<T>>::get()420			.0421			.checked_add(1)422			.ok_or(ArithmeticError::Overflow)?;423		let destroyed_count = <DestroyedCollectionCount<T>>::get().0;424		let id = CollectionId(created_count);425426		// bound Total number of collections427		ensure!(428			created_count - destroyed_count <= COLLECTION_NUMBER_LIMIT,429			<Error<T>>::TotalCollectionsLimitExceeded430		);431432		// =========433434		let collection = Collection {435			owner: owner.clone(),436			name: data.name,437			mode: data.mode.clone(),438			mint_mode: false,439			access: data.access.unwrap_or_default(),440			description: data.description,441			token_prefix: data.token_prefix,442			offchain_schema: data.offchain_schema,443			schema_version: data.schema_version.unwrap_or_default(),444			sponsorship: data445				.pending_sponsor446				.map(SponsorshipState::Unconfirmed)447				.unwrap_or_default(),448			variable_on_chain_schema: data.variable_on_chain_schema,449			const_on_chain_schema: data.const_on_chain_schema,450			limits: data451				.limits452				.map(|limits| Self::clamp_limits(data.mode.clone(), &Default::default(), limits))453				.unwrap_or_else(|| Ok(CollectionLimits::default()))?,454			meta_update_permission: data.meta_update_permission.unwrap_or_default(),455		};456457		// Take a (non-refundable) deposit of collection creation458		{459			let mut imbalance =460				<<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();461			imbalance.subsume(462				<<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(463					&T::TreasuryAccountId::get(),464					T::CollectionCreationPrice::get(),465				),466			);467			<T as Config>::Currency::settle(468				&owner,469				imbalance,470				WithdrawReasons::TRANSFER,471				ExistenceRequirement::KeepAlive,472			)473			.map_err(|_| Error::<T>::NoPermission)?;474		}475476		<CreatedCollectionCount<T>>::put(created_count);477		<Pallet<T>>::deposit_event(Event::CollectionCreated(id, data.mode.id(), owner.clone()));478		<CollectionById<T>>::insert(id, collection);479		Ok(id)480	}481482	pub fn destroy_collection(483		collection: CollectionHandle<T>,484		sender: &T::CrossAccountId,485	) -> DispatchResult {486		ensure!(487			collection.limits.owner_can_destroy(),488			<Error<T>>::NoPermission,489		);490		collection.check_is_owner(sender)?;491492		let destroyed_collections = <DestroyedCollectionCount<T>>::get()493			.0494			.checked_add(1)495			.ok_or(ArithmeticError::Overflow)?;496497		// =========498499		<DestroyedCollectionCount<T>>::put(destroyed_collections);500		<CollectionById<T>>::remove(collection.id);501		<AdminAmount<T>>::remove(collection.id);502		<IsAdmin<T>>::remove_prefix((collection.id,), None);503		<Allowlist<T>>::remove_prefix((collection.id,), None);504505		<Pallet<T>>::deposit_event(Event::CollectionDestroyed(collection.id));506		Ok(())507	}508509	pub fn toggle_allowlist(510		collection: &CollectionHandle<T>,511		sender: &T::CrossAccountId,512		user: &T::CrossAccountId,513		allowed: bool,514	) -> DispatchResult {515		collection.check_is_owner_or_admin(sender)?;516517		// =========518519		if allowed {520			<Allowlist<T>>::insert((collection.id, user), true);521		} else {522			<Allowlist<T>>::remove((collection.id, user));523		}524525		Ok(())526	}527528	pub fn toggle_admin(529		collection: &CollectionHandle<T>,530		sender: &T::CrossAccountId,531		user: &T::CrossAccountId,532		admin: bool,533	) -> DispatchResult {534		collection.check_is_owner_or_admin(sender)?;535536		let was_admin = <IsAdmin<T>>::get((collection.id, user));537		if was_admin == admin {538			return Ok(());539		}540		let amount = <AdminAmount<T>>::get(collection.id);541542		if admin {543			let amount = amount544				.checked_add(1)545				.ok_or(<Error<T>>::CollectionAdminCountExceeded)?;546			ensure!(547				amount <= Self::collection_admins_limit(),548				<Error<T>>::CollectionAdminCountExceeded,549			);550551			// =========552553			<AdminAmount<T>>::insert(collection.id, amount);554			<IsAdmin<T>>::insert((collection.id, user), true);555		} else {556			<AdminAmount<T>>::insert(collection.id, amount.saturating_sub(1));557			<IsAdmin<T>>::remove((collection.id, user));558		}559560		Ok(())561	}562563	pub fn clamp_limits(564		mode: CollectionMode,565		old_limit: &CollectionLimits,566		mut new_limit: CollectionLimits,567	) -> Result<CollectionLimits, DispatchError> {568		macro_rules! limit_default {569				($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{570					$(571						if let Some($new) = $new.$field {572							let $old = $old.$field($($arg)?);573							let _ = $new;574							let _ = $old;575							$check576						} else {577							$new.$field = $old.$field578						}579					)*580				}};581			}582583		limit_default!(old_limit, new_limit,584			account_token_ownership_limit => ensure!(585				new_limit <= MAX_TOKEN_OWNERSHIP,586				<Error<T>>::CollectionLimitBoundsExceeded,587			),588			sponsor_transfer_timeout(match mode {589				CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,590				CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,591				CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,592			}) => ensure!(593				new_limit <= MAX_SPONSOR_TIMEOUT,594				<Error<T>>::CollectionLimitBoundsExceeded,595			),596			sponsored_data_size => ensure!(597				new_limit <= CUSTOM_DATA_LIMIT,598				<Error<T>>::CollectionLimitBoundsExceeded,599			),600			token_limit => ensure!(601				old_limit >= new_limit && new_limit > 0,602				<Error<T>>::CollectionTokenLimitExceeded603			),604			owner_can_transfer => ensure!(605				old_limit || !new_limit,606				<Error<T>>::OwnerPermissionsCantBeReverted,607			),608			owner_can_destroy => ensure!(609				old_limit || !new_limit,610				<Error<T>>::OwnerPermissionsCantBeReverted,611			),612			sponsored_data_rate_limit => {},613			transfers_enabled => {},614		);615		Ok(new_limit)616	}617}618619#[macro_export]620macro_rules! unsupported {621	() => {622		Err(<Error<T>>::UnsupportedOperation.into())623	};624}625626/// Worst cases627pub trait CommonWeightInfo {628	fn create_item() -> Weight;629	fn create_multiple_items(amount: u32) -> Weight;630	fn burn_item() -> Weight;631	fn transfer() -> Weight;632	fn approve() -> Weight;633	fn transfer_from() -> Weight;634	fn burn_from() -> Weight;635	fn set_variable_metadata(bytes: u32) -> Weight;636}637638pub trait CommonCollectionOperations<T: Config> {639	fn create_item(640		&self,641		sender: T::CrossAccountId,642		to: T::CrossAccountId,643		data: CreateItemData,644	) -> DispatchResultWithPostInfo;645	fn create_multiple_items(646		&self,647		sender: T::CrossAccountId,648		to: T::CrossAccountId,649		data: Vec<CreateItemData>,650	) -> DispatchResultWithPostInfo;651	fn burn_item(652		&self,653		sender: T::CrossAccountId,654		token: TokenId,655		amount: u128,656	) -> DispatchResultWithPostInfo;657658	fn transfer(659		&self,660		sender: T::CrossAccountId,661		to: T::CrossAccountId,662		token: TokenId,663		amount: u128,664	) -> DispatchResultWithPostInfo;665	fn approve(666		&self,667		sender: T::CrossAccountId,668		spender: T::CrossAccountId,669		token: TokenId,670		amount: u128,671	) -> DispatchResultWithPostInfo;672	fn transfer_from(673		&self,674		sender: T::CrossAccountId,675		from: T::CrossAccountId,676		to: T::CrossAccountId,677		token: TokenId,678		amount: u128,679	) -> DispatchResultWithPostInfo;680	fn burn_from(681		&self,682		sender: T::CrossAccountId,683		from: T::CrossAccountId,684		token: TokenId,685		amount: u128,686	) -> DispatchResultWithPostInfo;687688	fn set_variable_metadata(689		&self,690		sender: T::CrossAccountId,691		token: TokenId,692		data: BoundedVec<u8, CustomDataLimit>,693	) -> DispatchResultWithPostInfo;694695	fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId>;696	fn token_exists(&self, token: TokenId) -> bool;697	fn last_token_id(&self) -> TokenId;698699	fn token_owner(&self, token: TokenId) -> Option<T::CrossAccountId>;700	fn const_metadata(&self, token: TokenId) -> Vec<u8>;701	fn variable_metadata(&self, token: TokenId) -> Vec<u8>;702703	/// How many tokens collection contains (Applicable to nonfungible/refungible)704	fn collection_tokens(&self) -> u32;705	/// Amount of different tokens account has (Applicable to nonfungible/refungible)706	fn account_balance(&self, account: T::CrossAccountId) -> u32;707	/// Amount of specific token account have (Applicable to fungible/refungible)708	fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128;709	fn allowance(710		&self,711		sender: T::CrossAccountId,712		spender: T::CrossAccountId,713		token: TokenId,714	) -> u128;715}716717// Flexible enough for implementing CommonCollectionOperations718pub fn with_weight(res: DispatchResult, weight: Weight) -> DispatchResultWithPostInfo {719	let post_info = PostDispatchInfo {720		actual_weight: Some(weight),721		pays_fee: Pays::Yes,722	};723	match res {724		Ok(()) => Ok(post_info),725		Err(error) => Err(DispatchErrorWithPostInfo { post_info, error }),726	}727}
after · pallets/common/src/lib.rs
1#![cfg_attr(not(feature = "std"), no_std)]23use core::ops::{Deref, DerefMut};4use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};5use sp_std::vec::Vec;6use account::CrossAccountId;7use frame_support::{8	dispatch::{DispatchErrorWithPostInfo, DispatchResultWithPostInfo},9	ensure, fail,10	traits::{Imbalance, Get, Currency},11	BoundedVec,12};13use pallet_evm::GasWeightMapping;14use up_data_structs::{15	COLLECTION_NUMBER_LIMIT, Collection, CollectionId, CreateItemData, ExistenceRequirement,16	MAX_TOKEN_PREFIX_LENGTH, COLLECTION_ADMINS_LIMIT, MetaUpdatePermission, Pays, PostDispatchInfo,17	TokenId, Weight, WithdrawReasons, CollectionStats, MAX_TOKEN_OWNERSHIP, CollectionMode,18	NFT_SPONSOR_TRANSFER_TIMEOUT, FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,19	REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, MAX_SPONSOR_TIMEOUT, CUSTOM_DATA_LIMIT, CollectionLimits,20	CustomDataLimit, CreateCollectionData, SponsorshipState, CreateItemExData,21};22pub use pallet::*;23use sp_core::H160;24use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};25pub mod account;26#[cfg(feature = "runtime-benchmarks")]27pub mod benchmarking;28pub mod erc;29pub mod eth;3031#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]32pub struct CollectionHandle<T: Config> {33	pub id: CollectionId,34	collection: Collection<T::AccountId>,35	pub recorder: SubstrateRecorder<T>,36}37impl<T: Config> WithRecorder<T> for CollectionHandle<T> {38	fn recorder(&self) -> &SubstrateRecorder<T> {39		&self.recorder40	}41	fn into_recorder(self) -> SubstrateRecorder<T> {42		self.recorder43	}44}45impl<T: Config> CollectionHandle<T> {46	pub fn new_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {47		<CollectionById<T>>::get(id).map(|collection| Self {48			id,49			collection,50			recorder: SubstrateRecorder::new(eth::collection_id_to_address(id), gas_limit),51		})52	}53	pub fn new(id: CollectionId) -> Option<Self> {54		Self::new_with_gas_limit(id, u64::MAX)55	}56	pub fn try_get(id: CollectionId) -> Result<Self, DispatchError> {57		Ok(Self::new(id).ok_or(<Error<T>>::CollectionNotFound)?)58	}59	pub fn log_mirrored(&self, log: impl evm_coder::ToLog) {60		self.recorder.log_mirrored(log)61	}62	pub fn log_direct(&self, log: impl evm_coder::ToLog) {63		self.recorder.log_direct(log)64	}65	pub fn consume_store_reads(&self, reads: u64) -> evm_coder::execution::Result<()> {66		self.recorder67			.consume_gas(T::GasWeightMapping::weight_to_gas(68				<T as frame_system::Config>::DbWeight::get()69					.read70					.saturating_mul(reads),71			))72	}73	pub fn consume_store_writes(&self, writes: u64) -> evm_coder::execution::Result<()> {74		self.recorder75			.consume_gas(T::GasWeightMapping::weight_to_gas(76				<T as frame_system::Config>::DbWeight::get()77					.write78					.saturating_mul(writes),79			))80	}81	pub fn submit_logs(self) {82		self.recorder.submit_logs()83	}84	pub fn save(self) -> DispatchResult {85		self.recorder.submit_logs();86		<CollectionById<T>>::insert(self.id, self.collection);87		Ok(())88	}89}90impl<T: Config> Deref for CollectionHandle<T> {91	type Target = Collection<T::AccountId>;9293	fn deref(&self) -> &Self::Target {94		&self.collection95	}96}9798impl<T: Config> DerefMut for CollectionHandle<T> {99	fn deref_mut(&mut self) -> &mut Self::Target {100		&mut self.collection101	}102}103104impl<T: Config> CollectionHandle<T> {105	pub fn check_is_owner(&self, subject: &T::CrossAccountId) -> DispatchResult {106		ensure!(*subject.as_sub() == self.owner, <Error<T>>::NoPermission);107		Ok(())108	}109	pub fn is_owner_or_admin(&self, subject: &T::CrossAccountId) -> bool {110		*subject.as_sub() == self.owner || <IsAdmin<T>>::get((self.id, subject))111	}112	pub fn check_is_owner_or_admin(&self, subject: &T::CrossAccountId) -> DispatchResult {113		ensure!(self.is_owner_or_admin(subject), <Error<T>>::NoPermission);114		Ok(())115	}116	pub fn ignores_allowance(&self, user: &T::CrossAccountId) -> bool {117		self.limits.owner_can_transfer() && self.is_owner_or_admin(user)118	}119	pub fn ignores_owned_amount(&self, user: &T::CrossAccountId) -> bool {120		self.limits.owner_can_transfer() && self.is_owner_or_admin(user)121	}122	pub fn check_allowlist(&self, user: &T::CrossAccountId) -> DispatchResult {123		ensure!(124			<Allowlist<T>>::get((self.id, user)),125			<Error<T>>::AddressNotInAllowlist126		);127		Ok(())128	}129130	pub fn check_can_update_meta(131		&self,132		subject: &T::CrossAccountId,133		item_owner: &T::CrossAccountId,134	) -> DispatchResult {135		match self.meta_update_permission {136			MetaUpdatePermission::ItemOwner => {137				ensure!(subject == item_owner, <Error<T>>::NoPermission);138				Ok(())139			}140			MetaUpdatePermission::Admin => self.check_is_owner_or_admin(subject),141			MetaUpdatePermission::None => fail!(<Error<T>>::NoPermission),142		}143	}144}145146#[frame_support::pallet]147pub mod pallet {148	use super::*;149	use frame_support::{Blake2_128Concat, pallet_prelude::*, storage::Key};150	use account::CrossAccountId;151	use frame_support::traits::Currency;152	use up_data_structs::TokenId;153	use scale_info::TypeInfo;154155	#[pallet::config]156	pub trait Config: frame_system::Config + pallet_evm_coder_substrate::Config + TypeInfo {157		type Event: IsType<<Self as frame_system::Config>::Event> + From<Event<Self>>;158159		type CrossAccountId: CrossAccountId<Self::AccountId>;160161		type EvmAddressMapping: pallet_evm::AddressMapping<Self::AccountId>;162		type EvmBackwardsAddressMapping: up_evm_mapping::EvmBackwardsAddressMapping<Self::AccountId>;163164		type Currency: Currency<Self::AccountId>;165166		#[pallet::constant]167		type CollectionCreationPrice: Get<168			<<Self as Config>::Currency as Currency<Self::AccountId>>::Balance,169		>;170171		type TreasuryAccountId: Get<Self::AccountId>;172	}173174	#[pallet::pallet]175	#[pallet::generate_store(pub(super) trait Store)]176	pub struct Pallet<T>(_);177178	#[pallet::extra_constants]179	impl<T: Config> Pallet<T> {180		pub fn collection_admins_limit() -> u32 {181			COLLECTION_ADMINS_LIMIT182		}183	}184185	#[pallet::event]186	#[pallet::generate_deposit(pub fn deposit_event)]187	pub enum Event<T: Config> {188		/// New collection was created189		///190		/// # Arguments191		///192		/// * collection_id: Globally unique identifier of newly created collection.193		///194		/// * mode: [CollectionMode] converted into u8.195		///196		/// * account_id: Collection owner.197		CollectionCreated(CollectionId, u8, T::AccountId),198199		/// New collection was destroyed200		///201		/// # Arguments202		///203		/// * collection_id: Globally unique identifier of collection.204		CollectionDestroyed(CollectionId),205206		/// New item was created.207		///208		/// # Arguments209		///210		/// * collection_id: Id of the collection where item was created.211		///212		/// * item_id: Id of an item. Unique within the collection.213		///214		/// * recipient: Owner of newly created item215		///216		/// * amount: Always 1 for NFT217		ItemCreated(CollectionId, TokenId, T::CrossAccountId, u128),218219		/// Collection item was burned.220		///221		/// # Arguments222		///223		/// * collection_id.224		///225		/// * item_id: Identifier of burned NFT.226		///227		/// * owner: which user has destroyed its tokens228		///229		/// * amount: Always 1 for NFT230		ItemDestroyed(CollectionId, TokenId, T::CrossAccountId, u128),231232		/// Item was transferred233		///234		/// * collection_id: Id of collection to which item is belong235		///236		/// * item_id: Id of an item237		///238		/// * sender: Original owner of item239		///240		/// * recipient: New owner of item241		///242		/// * amount: Always 1 for NFT243		Transfer(244			CollectionId,245			TokenId,246			T::CrossAccountId,247			T::CrossAccountId,248			u128,249		),250251		/// * collection_id252		///253		/// * item_id254		///255		/// * sender256		///257		/// * spender258		///259		/// * amount260		Approved(261			CollectionId,262			TokenId,263			T::CrossAccountId,264			T::CrossAccountId,265			u128,266		),267	}268269	#[pallet::error]270	pub enum Error<T> {271		/// This collection does not exist.272		CollectionNotFound,273		/// Sender parameter and item owner must be equal.274		MustBeTokenOwner,275		/// No permission to perform action276		NoPermission,277		/// Collection is not in mint mode.278		PublicMintingNotAllowed,279		/// Address is not in allow list.280		AddressNotInAllowlist,281282		/// Collection name can not be longer than 63 char.283		CollectionNameLimitExceeded,284		/// Collection description can not be longer than 255 char.285		CollectionDescriptionLimitExceeded,286		/// Token prefix can not be longer than 15 char.287		CollectionTokenPrefixLimitExceeded,288		/// Total collections bound exceeded.289		TotalCollectionsLimitExceeded,290		/// variable_data exceeded data limit.291		TokenVariableDataLimitExceeded,292		/// Exceeded max admin count293		CollectionAdminCountExceeded,294		/// Collection limit bounds per collection exceeded295		CollectionLimitBoundsExceeded,296		/// Tried to enable permissions which are only permitted to be disabled297		OwnerPermissionsCantBeReverted,298299		/// Collection settings not allowing items transferring300		TransferNotAllowed,301		/// Account token limit exceeded per collection302		AccountTokenLimitExceeded,303		/// Collection token limit exceeded304		CollectionTokenLimitExceeded,305		/// Metadata flag frozen306		MetadataFlagFrozen,307308		/// Item not exists.309		TokenNotFound,310		/// Item balance not enough.311		TokenValueTooLow,312		/// Requested value more than approved.313		ApprovedValueTooLow,314		/// Tried to approve more than owned315		CantApproveMoreThanOwned,316317		/// Can't transfer tokens to ethereum zero address318		AddressIsZero,319		/// Target collection doesn't supports this operation320		UnsupportedOperation,321	}322323	#[pallet::storage]324	pub type CreatedCollectionCount<T> = StorageValue<Value = CollectionId, QueryKind = ValueQuery>;325	#[pallet::storage]326	pub type DestroyedCollectionCount<T> =327		StorageValue<Value = CollectionId, QueryKind = ValueQuery>;328329	/// Collection info330	#[pallet::storage]331	pub type CollectionById<T> = StorageMap<332		Hasher = Blake2_128Concat,333		Key = CollectionId,334		Value = Collection<<T as frame_system::Config>::AccountId>,335		QueryKind = OptionQuery,336	>;337338	#[pallet::storage]339	pub type AdminAmount<T> = StorageMap<340		Hasher = Blake2_128Concat,341		Key = CollectionId,342		Value = u32,343		QueryKind = ValueQuery,344	>;345346	/// List of collection admins347	#[pallet::storage]348	pub type IsAdmin<T: Config> = StorageNMap<349		Key = (350			Key<Blake2_128Concat, CollectionId>,351			Key<Blake2_128Concat, T::CrossAccountId>,352		),353		Value = bool,354		QueryKind = ValueQuery,355	>;356357	/// Allowlisted collection users358	#[pallet::storage]359	pub type Allowlist<T: Config> = StorageNMap<360		Key = (361			Key<Blake2_128Concat, CollectionId>,362			Key<Blake2_128Concat, T::CrossAccountId>,363		),364		Value = bool,365		QueryKind = ValueQuery,366	>;367368	/// Not used by code, exists only to provide some types to metadata369	#[pallet::storage]370	pub type DummyStorageValue<T> =371		StorageValue<Value = (CollectionStats, CollectionId, TokenId), QueryKind = OptionQuery>;372}373374impl<T: Config> Pallet<T> {375	/// Ethereum receiver 0x0000000000000000000000000000000000000000 is reserved, and shouldn't own tokens376	pub fn ensure_correct_receiver(receiver: &T::CrossAccountId) -> DispatchResult {377		ensure!(378			&T::CrossAccountId::from_eth(H160([0; 20])) != receiver,379			<Error<T>>::AddressIsZero380		);381		Ok(())382	}383	pub fn adminlist(collection: CollectionId) -> Vec<T::CrossAccountId> {384		<IsAdmin<T>>::iter_prefix((collection,))385			.map(|(a, _)| a)386			.collect()387	}388	pub fn allowlist(collection: CollectionId) -> Vec<T::CrossAccountId> {389		<Allowlist<T>>::iter_prefix((collection,))390			.map(|(a, _)| a)391			.collect()392	}393	pub fn allowed(collection: CollectionId, user: T::CrossAccountId) -> bool {394		<Allowlist<T>>::get((collection, user))395	}396	pub fn collection_stats() -> CollectionStats {397		let created = <CreatedCollectionCount<T>>::get();398		let destroyed = <DestroyedCollectionCount<T>>::get();399		CollectionStats {400			created: created.0,401			destroyed: destroyed.0,402			alive: created.0 - destroyed.0,403		}404	}405}406407impl<T: Config> Pallet<T> {408	pub fn init_collection(409		owner: T::AccountId,410		data: CreateCollectionData<T::AccountId>,411	) -> Result<CollectionId, DispatchError> {412		{413			ensure!(414				data.token_prefix.len() <= MAX_TOKEN_PREFIX_LENGTH as usize,415				Error::<T>::CollectionTokenPrefixLimitExceeded416			);417		}418419		let created_count = <CreatedCollectionCount<T>>::get()420			.0421			.checked_add(1)422			.ok_or(ArithmeticError::Overflow)?;423		let destroyed_count = <DestroyedCollectionCount<T>>::get().0;424		let id = CollectionId(created_count);425426		// bound Total number of collections427		ensure!(428			created_count - destroyed_count <= COLLECTION_NUMBER_LIMIT,429			<Error<T>>::TotalCollectionsLimitExceeded430		);431432		// =========433434		let collection = Collection {435			owner: owner.clone(),436			name: data.name,437			mode: data.mode.clone(),438			mint_mode: false,439			access: data.access.unwrap_or_default(),440			description: data.description,441			token_prefix: data.token_prefix,442			offchain_schema: data.offchain_schema,443			schema_version: data.schema_version.unwrap_or_default(),444			sponsorship: data445				.pending_sponsor446				.map(SponsorshipState::Unconfirmed)447				.unwrap_or_default(),448			variable_on_chain_schema: data.variable_on_chain_schema,449			const_on_chain_schema: data.const_on_chain_schema,450			limits: data451				.limits452				.map(|limits| Self::clamp_limits(data.mode.clone(), &Default::default(), limits))453				.unwrap_or_else(|| Ok(CollectionLimits::default()))?,454			meta_update_permission: data.meta_update_permission.unwrap_or_default(),455		};456457		// Take a (non-refundable) deposit of collection creation458		{459			let mut imbalance =460				<<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();461			imbalance.subsume(462				<<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(463					&T::TreasuryAccountId::get(),464					T::CollectionCreationPrice::get(),465				),466			);467			<T as Config>::Currency::settle(468				&owner,469				imbalance,470				WithdrawReasons::TRANSFER,471				ExistenceRequirement::KeepAlive,472			)473			.map_err(|_| Error::<T>::NoPermission)?;474		}475476		<CreatedCollectionCount<T>>::put(created_count);477		<Pallet<T>>::deposit_event(Event::CollectionCreated(id, data.mode.id(), owner.clone()));478		<CollectionById<T>>::insert(id, collection);479		Ok(id)480	}481482	pub fn destroy_collection(483		collection: CollectionHandle<T>,484		sender: &T::CrossAccountId,485	) -> DispatchResult {486		ensure!(487			collection.limits.owner_can_destroy(),488			<Error<T>>::NoPermission,489		);490		collection.check_is_owner(sender)?;491492		let destroyed_collections = <DestroyedCollectionCount<T>>::get()493			.0494			.checked_add(1)495			.ok_or(ArithmeticError::Overflow)?;496497		// =========498499		<DestroyedCollectionCount<T>>::put(destroyed_collections);500		<CollectionById<T>>::remove(collection.id);501		<AdminAmount<T>>::remove(collection.id);502		<IsAdmin<T>>::remove_prefix((collection.id,), None);503		<Allowlist<T>>::remove_prefix((collection.id,), None);504505		<Pallet<T>>::deposit_event(Event::CollectionDestroyed(collection.id));506		Ok(())507	}508509	pub fn toggle_allowlist(510		collection: &CollectionHandle<T>,511		sender: &T::CrossAccountId,512		user: &T::CrossAccountId,513		allowed: bool,514	) -> DispatchResult {515		collection.check_is_owner_or_admin(sender)?;516517		// =========518519		if allowed {520			<Allowlist<T>>::insert((collection.id, user), true);521		} else {522			<Allowlist<T>>::remove((collection.id, user));523		}524525		Ok(())526	}527528	pub fn toggle_admin(529		collection: &CollectionHandle<T>,530		sender: &T::CrossAccountId,531		user: &T::CrossAccountId,532		admin: bool,533	) -> DispatchResult {534		collection.check_is_owner_or_admin(sender)?;535536		let was_admin = <IsAdmin<T>>::get((collection.id, user));537		if was_admin == admin {538			return Ok(());539		}540		let amount = <AdminAmount<T>>::get(collection.id);541542		if admin {543			let amount = amount544				.checked_add(1)545				.ok_or(<Error<T>>::CollectionAdminCountExceeded)?;546			ensure!(547				amount <= Self::collection_admins_limit(),548				<Error<T>>::CollectionAdminCountExceeded,549			);550551			// =========552553			<AdminAmount<T>>::insert(collection.id, amount);554			<IsAdmin<T>>::insert((collection.id, user), true);555		} else {556			<AdminAmount<T>>::insert(collection.id, amount.saturating_sub(1));557			<IsAdmin<T>>::remove((collection.id, user));558		}559560		Ok(())561	}562563	pub fn clamp_limits(564		mode: CollectionMode,565		old_limit: &CollectionLimits,566		mut new_limit: CollectionLimits,567	) -> Result<CollectionLimits, DispatchError> {568		macro_rules! limit_default {569				($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{570					$(571						if let Some($new) = $new.$field {572							let $old = $old.$field($($arg)?);573							let _ = $new;574							let _ = $old;575							$check576						} else {577							$new.$field = $old.$field578						}579					)*580				}};581			}582583		limit_default!(old_limit, new_limit,584			account_token_ownership_limit => ensure!(585				new_limit <= MAX_TOKEN_OWNERSHIP,586				<Error<T>>::CollectionLimitBoundsExceeded,587			),588			sponsor_transfer_timeout(match mode {589				CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,590				CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,591				CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,592			}) => ensure!(593				new_limit <= MAX_SPONSOR_TIMEOUT,594				<Error<T>>::CollectionLimitBoundsExceeded,595			),596			sponsored_data_size => ensure!(597				new_limit <= CUSTOM_DATA_LIMIT,598				<Error<T>>::CollectionLimitBoundsExceeded,599			),600			token_limit => ensure!(601				old_limit >= new_limit && new_limit > 0,602				<Error<T>>::CollectionTokenLimitExceeded603			),604			owner_can_transfer => ensure!(605				old_limit || !new_limit,606				<Error<T>>::OwnerPermissionsCantBeReverted,607			),608			owner_can_destroy => ensure!(609				old_limit || !new_limit,610				<Error<T>>::OwnerPermissionsCantBeReverted,611			),612			sponsored_data_rate_limit => {},613			transfers_enabled => {},614		);615		Ok(new_limit)616	}617}618619#[macro_export]620macro_rules! unsupported {621	() => {622		Err(<Error<T>>::UnsupportedOperation.into())623	};624}625626/// Worst cases627pub trait CommonWeightInfo<CrossAccountId> {628	fn create_item() -> Weight;629	fn create_multiple_items(amount: u32) -> Weight;630	fn create_multiple_items_ex(cost: &CreateItemExData<CrossAccountId>) -> Weight;631	fn burn_item() -> Weight;632	fn transfer() -> Weight;633	fn approve() -> Weight;634	fn transfer_from() -> Weight;635	fn burn_from() -> Weight;636	fn set_variable_metadata(bytes: u32) -> Weight;637}638639pub trait CommonCollectionOperations<T: Config> {640	fn create_item(641		&self,642		sender: T::CrossAccountId,643		to: T::CrossAccountId,644		data: CreateItemData,645	) -> DispatchResultWithPostInfo;646	fn create_multiple_items(647		&self,648		sender: T::CrossAccountId,649		to: T::CrossAccountId,650		data: Vec<CreateItemData>,651	) -> DispatchResultWithPostInfo;652	fn create_multiple_items_ex(653		&self,654		sender: T::CrossAccountId,655		data: CreateItemExData<T::CrossAccountId>,656	) -> DispatchResultWithPostInfo;657	fn burn_item(658		&self,659		sender: T::CrossAccountId,660		token: TokenId,661		amount: u128,662	) -> DispatchResultWithPostInfo;663664	fn transfer(665		&self,666		sender: T::CrossAccountId,667		to: T::CrossAccountId,668		token: TokenId,669		amount: u128,670	) -> DispatchResultWithPostInfo;671	fn approve(672		&self,673		sender: T::CrossAccountId,674		spender: T::CrossAccountId,675		token: TokenId,676		amount: u128,677	) -> DispatchResultWithPostInfo;678	fn transfer_from(679		&self,680		sender: T::CrossAccountId,681		from: T::CrossAccountId,682		to: T::CrossAccountId,683		token: TokenId,684		amount: u128,685	) -> DispatchResultWithPostInfo;686	fn burn_from(687		&self,688		sender: T::CrossAccountId,689		from: T::CrossAccountId,690		token: TokenId,691		amount: u128,692	) -> DispatchResultWithPostInfo;693694	fn set_variable_metadata(695		&self,696		sender: T::CrossAccountId,697		token: TokenId,698		data: BoundedVec<u8, CustomDataLimit>,699	) -> DispatchResultWithPostInfo;700701	fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId>;702	fn token_exists(&self, token: TokenId) -> bool;703	fn last_token_id(&self) -> TokenId;704705	fn token_owner(&self, token: TokenId) -> Option<T::CrossAccountId>;706	fn const_metadata(&self, token: TokenId) -> Vec<u8>;707	fn variable_metadata(&self, token: TokenId) -> Vec<u8>;708709	/// How many tokens collection contains (Applicable to nonfungible/refungible)710	fn collection_tokens(&self) -> u32;711	/// Amount of different tokens account has (Applicable to nonfungible/refungible)712	fn account_balance(&self, account: T::CrossAccountId) -> u32;713	/// Amount of specific token account have (Applicable to fungible/refungible)714	fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128;715	fn allowance(716		&self,717		sender: T::CrossAccountId,718		spender: T::CrossAccountId,719		token: TokenId,720	) -> u128;721}722723// Flexible enough for implementing CommonCollectionOperations724pub fn with_weight(res: DispatchResult, weight: Weight) -> DispatchResultWithPostInfo {725	let post_info = PostDispatchInfo {726		actual_weight: Some(weight),727		pays_fee: Pays::Yes,728	};729	match res {730		Ok(()) => Ok(post_info),731		Err(error) => Err(DispatchErrorWithPostInfo { post_info, error }),732	}733}
modifiedpallets/fungible/src/benchmarking.rsdiffbeforeafterboth
--- a/pallets/fungible/src/benchmarking.rs
+++ b/pallets/fungible/src/benchmarking.rs
@@ -4,7 +4,7 @@
 use sp_std::prelude::*;
 use pallet_common::benchmarking::create_collection_raw;
 use frame_benchmarking::{benchmarks, account};
-use up_data_structs::{CollectionMode};
+use up_data_structs::{CollectionMode, MAX_ITEMS_PER_BATCH};
 use pallet_common::bench_init;
 
 const SEED: u32 = 1;
@@ -26,6 +26,18 @@
 		};
 	}: {<Pallet<T>>::create_item(&collection, &sender, (to, 200))?}
 
+	create_multiple_items_ex {
+		let b in 0..MAX_ITEMS_PER_BATCH;
+		bench_init!{
+			owner: sub; collection: collection(owner);
+			sender: cross_from_sub(owner);
+		};
+		let data = (0..b).map(|i| {
+			bench_init!(to: cross_sub(i););
+			(to, 200)
+		}).collect::<BTreeMap<_, _>>().try_into().unwrap();
+	}: {<Pallet<T>>::create_multiple_items(&collection, &sender, data)}
+
 	burn_item {
 		bench_init!{
 			owner: sub; collection: collection(owner);
modifiedpallets/fungible/src/common.rsdiffbeforeafterboth
--- a/pallets/fungible/src/common.rs
+++ b/pallets/fungible/src/common.rs
@@ -1,7 +1,7 @@
 use core::marker::PhantomData;
 
 use frame_support::{dispatch::DispatchResultWithPostInfo, ensure, fail, weights::Weight, BoundedVec};
-use up_data_structs::TokenId;
+use up_data_structs::{TokenId, CreateItemExData};
 use pallet_common::{CommonCollectionOperations, CommonWeightInfo, with_weight};
 use sp_runtime::ArithmeticError;
 use sp_std::{vec::Vec, vec};
@@ -12,7 +12,7 @@
 };
 
 pub struct CommonWeights<T: Config>(PhantomData<T>);
-impl<T: Config> CommonWeightInfo for CommonWeights<T> {
+impl<T: Config> CommonWeightInfo<T::CrossAccountId> for CommonWeights<T> {
 	fn create_item() -> Weight {
 		<SelfWeightOf<T>>::create_item()
 	}
@@ -21,6 +21,15 @@
 		Self::create_item()
 	}
 
+	fn create_multiple_items_ex(data: &CreateItemExData<T::CrossAccountId>) -> Weight {
+		match data {
+			CreateItemExData::Fungible(f) => {
+				<SelfWeightOf<T>>::create_multiple_items_ex(f.len() as u32)
+			}
+			_ => 0,
+		}
+	}
+
 	fn burn_item() -> Weight {
 		<SelfWeightOf<T>>::burn_item()
 	}
@@ -87,6 +96,23 @@
 		)
 	}
 
+	fn create_multiple_items_ex(
+		&self,
+		sender: <T>::CrossAccountId,
+		data: up_data_structs::CreateItemExData<<T>::CrossAccountId>,
+	) -> DispatchResultWithPostInfo {
+		let weight = <CommonWeights<T>>::create_multiple_items_ex(&data);
+		let data = match data {
+			up_data_structs::CreateItemExData::Fungible(f) => f,
+			_ => fail!(<Error<T>>::NotFungibleDataUsedToMintFungibleCollectionToken),
+		};
+
+		with_weight(
+			<Pallet<T>>::create_multiple_items(self, &sender, data.into_inner()),
+			weight,
+		)
+	}
+
 	fn burn_item(
 		&self,
 		sender: T::CrossAccountId,
modifiedpallets/fungible/src/lib.rsdiffbeforeafterboth
--- a/pallets/fungible/src/lib.rs
+++ b/pallets/fungible/src/lib.rs
@@ -9,7 +9,7 @@
 use pallet_evm_coder_substrate::WithRecorder;
 use sp_core::H160;
 use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};
-use sp_std::{vec::Vec, vec, collections::btree_map::BTreeMap};
+use sp_std::collections::btree_map::BTreeMap;
 
 pub use pallet::*;
 
@@ -222,7 +222,7 @@
 	pub fn create_multiple_items(
 		collection: &FungibleHandle<T>,
 		sender: &T::CrossAccountId,
-		data: Vec<CreateItemData<T>>,
+		data: BTreeMap<T::CrossAccountId, u128>,
 	) -> DispatchResult {
 		if !collection.is_owner_or_admin(sender) {
 			ensure!(
@@ -235,23 +235,19 @@
 				collection.check_allowlist(owner)?;
 			}
 		}
-
-		let mut balances = BTreeMap::new();
 
 		let total_supply = data
 			.iter()
-			.map(|u| u.1)
+			.map(|(_, v)| *v)
 			.try_fold(<TotalSupply<T>>::get(collection.id), |acc, v| {
 				acc.checked_add(v)
 			})
 			.ok_or(ArithmeticError::Overflow)?;
 
-		for (user, amount) in data.into_iter() {
-			let balance = balances
-				.entry(user.clone())
-				.or_insert_with(|| <Balance<T>>::get((collection.id, user)));
-			*balance = (*balance)
-				.checked_add(amount)
+		let mut balances = data;
+		for (k, v) in balances.iter_mut() {
+			*v = <Balance<T>>::get((collection.id, &k))
+				.checked_add(*v)
 				.ok_or(ArithmeticError::Overflow)?;
 		}
 
@@ -396,6 +392,6 @@
 		sender: &T::CrossAccountId,
 		data: CreateItemData<T>,
 	) -> DispatchResult {
-		Self::create_multiple_items(collection, sender, vec![data])
+		Self::create_multiple_items(collection, sender, [(data.0, data.1)].into_iter().collect())
 	}
 }
modifiedpallets/fungible/src/weights.rsdiffbeforeafterboth
--- a/pallets/fungible/src/weights.rs
+++ b/pallets/fungible/src/weights.rs
@@ -33,6 +33,7 @@
 /// Weight functions needed for pallet_fungible.
 pub trait WeightInfo {
 	fn create_item() -> Weight;
+	fn create_multiple_items_ex(b: u32, ) -> Weight;
 	fn burn_item() -> Weight;
 	fn transfer() -> Weight;
 	fn approve() -> Weight;
@@ -51,6 +52,17 @@
 			.saturating_add(T::DbWeight::get().writes(2 as Weight))
 	}
 	// Storage: Fungible TotalSupply (r:1 w:1)
+	// Storage: Fungible Balance (r:4 w:4)
+	fn create_multiple_items_ex(b: u32, ) -> Weight {
+		(1_055_000 as Weight)
+			// Standard Error: 22_000
+			.saturating_add((5_273_000 as Weight).saturating_mul(b as Weight))
+			.saturating_add(T::DbWeight::get().reads(1 as Weight))
+			.saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(b as Weight)))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+			.saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(b as Weight)))
+	}
+	// Storage: Fungible TotalSupply (r:1 w:1)
 	// Storage: Fungible Balance (r:1 w:1)
 	fn burn_item() -> Weight {
 		(14_096_000 as Weight)
@@ -97,6 +109,17 @@
 			.saturating_add(RocksDbWeight::get().writes(2 as Weight))
 	}
 	// Storage: Fungible TotalSupply (r:1 w:1)
+	// Storage: Fungible Balance (r:4 w:4)
+	fn create_multiple_items_ex(b: u32, ) -> Weight {
+		(1_055_000 as Weight)
+			// Standard Error: 22_000
+			.saturating_add((5_273_000 as Weight).saturating_mul(b as Weight))
+			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
+			.saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(b as Weight)))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+			.saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(b as Weight)))
+	}
+	// Storage: Fungible TotalSupply (r:1 w:1)
 	// Storage: Fungible Balance (r:1 w:1)
 	fn burn_item() -> Weight {
 		(14_096_000 as Weight)
modifiedpallets/inflation/src/benchmarking.rsdiffbeforeafterboth
--- a/pallets/inflation/src/benchmarking.rs
+++ b/pallets/inflation/src/benchmarking.rs
@@ -1,7 +1,7 @@
 #![cfg(feature = "runtime-benchmarks")]
 
 use super::*;
-use crate::Module as Inflation;
+use crate::Pallet as Inflation;
 
 use frame_benchmarking::{benchmarks};
 use frame_support::traits::OnInitialize;
modifiedpallets/nonfungible/src/benchmarking.rsdiffbeforeafterboth
--- a/pallets/nonfungible/src/benchmarking.rs
+++ b/pallets/nonfungible/src/benchmarking.rs
@@ -2,18 +2,18 @@
 use crate::{Pallet, Config, NonfungibleHandle};
 
 use sp_std::prelude::*;
-use pallet_common::benchmarking::{create_collection_raw, create_data};
+use pallet_common::benchmarking::{create_collection_raw, create_data, create_var_data};
 use frame_benchmarking::{benchmarks, account};
-use up_data_structs::{CollectionMode, MAX_ITEMS_PER_BATCH};
+use up_data_structs::{CollectionMode, MAX_ITEMS_PER_BATCH, CUSTOM_DATA_LIMIT};
 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 as usize).try_into().unwrap();
-	let variable_data = create_data(CUSTOM_DATA_LIMIT as usize).try_into().unwrap();
-	CreateItemData {
+	let const_data = create_data::<CUSTOM_DATA_LIMIT>();
+	let variable_data = create_data::<CUSTOM_DATA_LIMIT>();
+	CreateItemData::<T> {
 		const_data,
 		variable_data,
 		owner,
@@ -24,7 +24,7 @@
 	sender: &T::CrossAccountId,
 	owner: T::CrossAccountId,
 ) -> Result<TokenId, DispatchError> {
-	<Pallet<T>>::create_item(&collection, sender, create_max_item_data(owner))?;
+	<Pallet<T>>::create_item(&collection, sender, create_max_item_data::<T>(owner))?;
 	Ok(TokenId(<TokensMinted<T>>::get(&collection.id)))
 }
 
@@ -53,7 +53,19 @@
 			owner: sub; collection: collection(owner);
 			sender: cross_from_sub(owner); to: cross_sub;
 		};
-		let data = (0..b).map(|_| create_max_item_data(to.clone())).collect();
+		let data = (0..b).map(|_| create_max_item_data::<T>(to.clone())).collect();
+	}: {<Pallet<T>>::create_multiple_items(&collection, &sender, data)?}
+
+	create_multiple_items_ex {
+		let b in 0..MAX_ITEMS_PER_BATCH;
+		bench_init!{
+			owner: sub; collection: collection(owner);
+			sender: cross_from_sub(owner);
+		};
+		let data = (0..b).map(|i| {
+			bench_init!(to: cross_sub(i););
+			create_max_item_data::<T>(to)
+		}).collect();
 	}: {<Pallet<T>>::create_multiple_items(&collection, &sender, data)?}
 
 	burn_item {
@@ -105,6 +117,6 @@
 			owner: cross_from_sub; sender: cross_sub;
 		};
 		let item = create_max_item(&collection, &owner, sender.clone())?;
-		let data = create_data(b as usize);
+		let data = create_var_data(b).try_into().unwrap();
 	}: {<Pallet<T>>::set_variable_metadata(&collection, &sender, item, data)?}
 }
modifiedpallets/nonfungible/src/common.rsdiffbeforeafterboth
--- a/pallets/nonfungible/src/common.rs
+++ b/pallets/nonfungible/src/common.rs
@@ -1,7 +1,7 @@
 use core::marker::PhantomData;
 
 use frame_support::{dispatch::DispatchResultWithPostInfo, ensure, fail, weights::Weight, BoundedVec};
-use up_data_structs::{TokenId, CustomDataLimit};
+use up_data_structs::{TokenId, CustomDataLimit, CreateItemExData};
 use pallet_common::{CommonCollectionOperations, CommonWeightInfo, with_weight};
 use sp_runtime::DispatchError;
 use sp_std::vec::Vec;
@@ -12,11 +12,18 @@
 };
 
 pub struct CommonWeights<T: Config>(PhantomData<T>);
-impl<T: Config> CommonWeightInfo for CommonWeights<T> {
+impl<T: Config> CommonWeightInfo<T::CrossAccountId> for CommonWeights<T> {
 	fn create_item() -> Weight {
 		<SelfWeightOf<T>>::create_item()
 	}
 
+	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),
+			_ => 0,
+		}
+	}
+
 	fn create_multiple_items(amount: u32) -> Weight {
 		<SelfWeightOf<T>>::create_multiple_items(amount)
 	}
@@ -51,7 +58,7 @@
 	to: &T::CrossAccountId,
 ) -> Result<CreateItemData<T>, DispatchError> {
 	match data {
-		up_data_structs::CreateItemData::NFT(data) => Ok(CreateItemData {
+		up_data_structs::CreateItemData::NFT(data) => Ok(CreateItemData::<T> {
 			const_data: data.const_data,
 			variable_data: data.variable_data,
 			owner: to.clone(),
@@ -68,7 +75,7 @@
 		data: up_data_structs::CreateItemData,
 	) -> DispatchResultWithPostInfo {
 		with_weight(
-			<Pallet<T>>::create_item(self, &sender, map_create_data(data, &to)?),
+			<Pallet<T>>::create_item(self, &sender, map_create_data::<T>(data, &to)?),
 			<CommonWeights<T>>::create_item(),
 		)
 	}
@@ -91,6 +98,23 @@
 		)
 	}
 
+	fn create_multiple_items_ex(
+		&self,
+		sender: <T>::CrossAccountId,
+		data: up_data_structs::CreateItemExData<<T>::CrossAccountId>,
+	) -> DispatchResultWithPostInfo {
+		let weight = <CommonWeights<T>>::create_multiple_items_ex(&data);
+		let data = match data {
+			up_data_structs::CreateItemExData::NFT(nft) => nft,
+			_ => fail!(Error::<T>::NotNonfungibleDataUsedToMintFungibleCollectionToken),
+		};
+
+		with_weight(
+			<Pallet<T>>::create_multiple_items(self, &sender, data.into_inner()),
+			weight,
+		)
+	}
+
 	fn burn_item(
 		&self,
 		sender: T::CrossAccountId,
modifiedpallets/nonfungible/src/erc.rsdiffbeforeafterboth
--- a/pallets/nonfungible/src/erc.rs
+++ b/pallets/nonfungible/src/erc.rs
@@ -232,7 +232,7 @@
 		<Pallet<T>>::create_item(
 			self,
 			&caller,
-			CreateItemData {
+			CreateItemData::<T> {
 				const_data: BoundedVec::default(),
 				variable_data: BoundedVec::default(),
 				owner: to,
@@ -268,7 +268,7 @@
 		<Pallet<T>>::create_item(
 			self,
 			&caller,
-			CreateItemData {
+			CreateItemData::<T> {
 				const_data: Vec::<u8>::from(token_uri)
 					.try_into()
 					.map_err(|_| "token uri is too long")?,
@@ -376,7 +376,7 @@
 			expected_index = expected_index.checked_add(1).ok_or("item id overflow")?;
 		}
 		let data = (0..total_tokens)
-			.map(|_| CreateItemData {
+			.map(|_| CreateItemData::<T> {
 				const_data: BoundedVec::default(),
 				variable_data: BoundedVec::default(),
 				owner: to.clone(),
@@ -409,7 +409,7 @@
 			}
 			expected_index = expected_index.checked_add(1).ok_or("item id overflow")?;
 
-			data.push(CreateItemData {
+			data.push(CreateItemData::<T> {
 				const_data: Vec::<u8>::from(token_uri)
 					.try_into()
 					.map_err(|_| "token uri is too long")?,
modifiedpallets/nonfungible/src/lib.rsdiffbeforeafterboth
--- a/pallets/nonfungible/src/lib.rs
+++ b/pallets/nonfungible/src/lib.rs
@@ -2,7 +2,9 @@
 
 use erc::ERC721Events;
 use frame_support::{BoundedVec, ensure};
-use up_data_structs::{AccessMode, CollectionId, CustomDataLimit, TokenId, CreateCollectionData};
+use up_data_structs::{
+	AccessMode, CollectionId, CustomDataLimit, TokenId, CreateCollectionData, CreateNftExData,
+};
 use pallet_common::{
 	Error as CommonError, Pallet as PalletCommon, Event as CommonEvent, account::CrossAccountId,
 };
@@ -22,11 +24,7 @@
 pub mod erc;
 pub mod weights;
 
-pub struct CreateItemData<T: Config> {
-	pub const_data: BoundedVec<u8, CustomDataLimit>,
-	pub variable_data: BoundedVec<u8, CustomDataLimit>,
-	pub owner: T::CrossAccountId,
-}
+pub type CreateItemData<T> = CreateNftExData<<T as pallet_common::Config>::CrossAccountId>;
 pub(crate) type SelfWeightOf<T> = <T as Config>::WeightInfo;
 
 #[derive(Encode, Decode, TypeInfo, MaxEncodedLen)]
modifiedpallets/nonfungible/src/weights.rsdiffbeforeafterboth
--- a/pallets/nonfungible/src/weights.rs
+++ b/pallets/nonfungible/src/weights.rs
@@ -34,6 +34,7 @@
 pub trait WeightInfo {
 	fn create_item() -> Weight;
 	fn create_multiple_items(b: u32, ) -> Weight;
+	fn create_multiple_items_ex(b: u32, ) -> Weight;
 	fn burn_item() -> Weight;
 	fn transfer() -> Weight;
 	fn approve() -> Weight;
@@ -66,6 +67,19 @@
 			.saturating_add(T::DbWeight::get().writes(2 as Weight))
 			.saturating_add(T::DbWeight::get().writes((2 as Weight).saturating_mul(b as Weight)))
 	}
+	// Storage: Nonfungible TokensMinted (r:1 w:1)
+	// Storage: Nonfungible AccountBalance (r:4 w:4)
+	// Storage: Nonfungible TokenData (r:0 w:4)
+	// Storage: Nonfungible Owned (r:0 w:4)
+	fn create_multiple_items_ex(b: u32, ) -> Weight {
+		(2_090_000 as Weight)
+			// Standard Error: 10_000
+			.saturating_add((9_230_000 as Weight).saturating_mul(b as Weight))
+			.saturating_add(T::DbWeight::get().reads(1 as Weight))
+			.saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(b as Weight)))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+			.saturating_add(T::DbWeight::get().writes((3 as Weight).saturating_mul(b as Weight)))
+	}
 	// Storage: Nonfungible TokenData (r:1 w:1)
 	// Storage: Nonfungible TokensBurnt (r:1 w:1)
 	// Storage: Nonfungible Allowance (r:1 w:0)
@@ -140,6 +154,19 @@
 			.saturating_add(RocksDbWeight::get().writes(2 as Weight))
 			.saturating_add(RocksDbWeight::get().writes((2 as Weight).saturating_mul(b as Weight)))
 	}
+	// Storage: Nonfungible TokensMinted (r:1 w:1)
+	// Storage: Nonfungible AccountBalance (r:4 w:4)
+	// Storage: Nonfungible TokenData (r:0 w:4)
+	// Storage: Nonfungible Owned (r:0 w:4)
+	fn create_multiple_items_ex(b: u32, ) -> Weight {
+		(2_090_000 as Weight)
+			// Standard Error: 10_000
+			.saturating_add((9_230_000 as Weight).saturating_mul(b as Weight))
+			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
+			.saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(b as Weight)))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+			.saturating_add(RocksDbWeight::get().writes((3 as Weight).saturating_mul(b as Weight)))
+	}
 	// Storage: Nonfungible TokenData (r:1 w:1)
 	// Storage: Nonfungible TokensBurnt (r:1 w:1)
 	// Storage: Nonfungible Allowance (r:1 w:0)
modifiedpallets/refungible/src/benchmarking.rsdiffbeforeafterboth
--- a/pallets/refungible/src/benchmarking.rs
+++ b/pallets/refungible/src/benchmarking.rs
@@ -2,24 +2,28 @@
 use crate::{Pallet, Config, RefungibleHandle};
 
 use sp_std::prelude::*;
-use pallet_common::benchmarking::{create_collection_raw, create_data};
+use pallet_common::benchmarking::{create_collection_raw, create_data, create_var_data};
 use frame_benchmarking::{benchmarks, account};
-use up_data_structs::{CollectionMode, MAX_ITEMS_PER_BATCH};
+use up_data_structs::{CollectionMode, MAX_ITEMS_PER_BATCH, CUSTOM_DATA_LIMIT};
 use pallet_common::bench_init;
 use core::convert::TryInto;
 use core::iter::IntoIterator;
 
 const SEED: u32 = 1;
 
-fn create_max_item_data<T: Config>(
-	users: impl IntoIterator<Item = (T::CrossAccountId, u128)>,
-) -> CreateItemData<T> {
-	let const_data = create_data(CUSTOM_DATA_LIMIT as usize).try_into().unwrap();
-	let variable_data = create_data(CUSTOM_DATA_LIMIT as usize).try_into().unwrap();
-	CreateItemData {
+fn create_max_item_data<CrossAccountId: Ord>(
+	users: impl IntoIterator<Item = (CrossAccountId, u128)>,
+) -> CreateRefungibleExData<CrossAccountId> {
+	let const_data = create_data::<CUSTOM_DATA_LIMIT>();
+	let variable_data = create_data::<CUSTOM_DATA_LIMIT>();
+	CreateRefungibleExData {
 		const_data,
 		variable_data,
-		users: users.into_iter().collect(),
+		users: users
+			.into_iter()
+			.collect::<BTreeMap<_, _>>()
+			.try_into()
+			.unwrap(),
 	}
 }
 fn create_max_item<T: Config>(
@@ -27,7 +31,8 @@
 	sender: &T::CrossAccountId,
 	users: impl IntoIterator<Item = (T::CrossAccountId, u128)>,
 ) -> Result<TokenId, DispatchError> {
-	<Pallet<T>>::create_item(&collection, sender, create_max_item_data(users))?;
+	let data: CreateRefungibleExData<T::CrossAccountId> = create_max_item_data(users);
+	<Pallet<T>>::create_item(&collection, sender, data)?;
 	Ok(TokenId(<TokensMinted<T>>::get(&collection.id)))
 }
 
@@ -56,6 +61,30 @@
 		let data = (0..b).map(|_| create_max_item_data([(to.clone(), 200)])).collect();
 	}: {<Pallet<T>>::create_multiple_items(&collection, &sender, data)?}
 
+	create_multiple_items_ex_multiple_items {
+		let b in 0..MAX_ITEMS_PER_BATCH;
+		bench_init!{
+			owner: sub; collection: collection(owner);
+			sender: cross_from_sub(owner);
+		};
+		let data = (0..b).map(|t| {
+			bench_init!(to: cross_sub(t););
+			create_max_item_data([(to, 200)])
+		}).collect();
+	}: {<Pallet<T>>::create_multiple_items(&collection, &sender, data)?}
+
+	create_multiple_items_ex_multiple_owners {
+		let b in 0..MAX_ITEMS_PER_BATCH;
+		bench_init!{
+			owner: sub; collection: collection(owner);
+			sender: cross_from_sub(owner);
+		};
+		let data = vec![create_max_item_data((0..b).map(|u| {
+			bench_init!(to: cross_sub(u););
+			(to, 200)
+		}))].try_into().unwrap();
+	}: {<Pallet<T>>::create_multiple_items(&collection, &sender, data)?}
+
 	// Other user left, token data is kept
 	burn_item_partial {
 		bench_init!{
@@ -166,6 +195,6 @@
 			sender: cross_from_sub(owner);
 		};
 		let item = create_max_item(&collection, &sender, [(sender.clone(), 200)])?;
-		let data = create_data(b as usize);
+		let data = create_var_data(b).try_into().unwrap();
 	}: {<Pallet<T>>::set_variable_metadata(&collection, &sender, item, data)?}
 }
modifiedpallets/refungible/src/common.rsdiffbeforeafterboth
--- a/pallets/refungible/src/common.rs
+++ b/pallets/refungible/src/common.rs
@@ -2,14 +2,14 @@
 
 use sp_std::collections::btree_map::BTreeMap;
 use frame_support::{dispatch::DispatchResultWithPostInfo, fail, weights::Weight, BoundedVec};
-use up_data_structs::{TokenId, CustomDataLimit};
+use up_data_structs::{TokenId, CustomDataLimit, CreateItemExData, CreateRefungibleExData};
 use pallet_common::{CommonCollectionOperations, CommonWeightInfo, with_weight};
 use sp_runtime::DispatchError;
-use sp_std::vec::Vec;
+use sp_std::{vec::Vec, vec};
 
 use crate::{
-	AccountBalance, Allowance, Balance, Config, CreateItemData, Error, Owned, Pallet,
-	RefungibleHandle, SelfWeightOf, TokenData, weights::WeightInfo, TokensMinted,
+	AccountBalance, Allowance, Balance, Config, Error, Owned, Pallet, RefungibleHandle,
+	SelfWeightOf, TokenData, weights::WeightInfo, TokensMinted,
 };
 
 macro_rules! max_weight_of {
@@ -22,7 +22,7 @@
 }
 
 pub struct CommonWeights<T: Config>(PhantomData<T>);
-impl<T: Config> CommonWeightInfo for CommonWeights<T> {
+impl<T: Config> CommonWeightInfo<T::CrossAccountId> for CommonWeights<T> {
 	fn create_item() -> Weight {
 		<SelfWeightOf<T>>::create_item()
 	}
@@ -31,6 +31,18 @@
 		<SelfWeightOf<T>>::create_multiple_items(amount)
 	}
 
+	fn create_multiple_items_ex(call: &CreateItemExData<T::CrossAccountId>) -> Weight {
+		match call {
+			CreateItemExData::RefungibleMultipleOwners(i) => {
+				<SelfWeightOf<T>>::create_multiple_items_ex_multiple_owners(i.users.len() as u32)
+			}
+			CreateItemExData::RefungibleMultipleItems(i) => {
+				<SelfWeightOf<T>>::create_multiple_items_ex_multiple_items(i.len() as u32)
+			}
+			_ => 0,
+		}
+	}
+
 	fn burn_item() -> Weight {
 		max_weight_of!(burn_item_partial(), burn_item_fully())
 	}
@@ -69,15 +81,15 @@
 fn map_create_data<T: Config>(
 	data: up_data_structs::CreateItemData,
 	to: &T::CrossAccountId,
-) -> Result<CreateItemData<T>, DispatchError> {
+) -> Result<CreateRefungibleExData<T::CrossAccountId>, DispatchError> {
 	match data {
-		up_data_structs::CreateItemData::ReFungible(data) => Ok(CreateItemData {
+		up_data_structs::CreateItemData::ReFungible(data) => Ok(CreateRefungibleExData {
 			const_data: data.const_data,
 			variable_data: data.variable_data,
 			users: {
 				let mut out = BTreeMap::new();
 				out.insert(to.clone(), data.pieces);
-				out
+				out.try_into().expect("limit > 0")
 			},
 		}),
 		_ => fail!(<Error<T>>::NotRefungibleDataUsedToMintFungibleCollectionToken),
@@ -92,7 +104,7 @@
 		data: up_data_structs::CreateItemData,
 	) -> DispatchResultWithPostInfo {
 		with_weight(
-			<Pallet<T>>::create_item(self, &sender, map_create_data(data, &to)?),
+			<Pallet<T>>::create_item(self, &sender, map_create_data::<T>(data, &to)?),
 			<CommonWeights<T>>::create_item(),
 		)
 	}
@@ -115,6 +127,28 @@
 		)
 	}
 
+	fn create_multiple_items_ex(
+		&self,
+		sender: <T>::CrossAccountId,
+		data: CreateItemExData<T::CrossAccountId>,
+	) -> DispatchResultWithPostInfo {
+		let weight = <CommonWeights<T>>::create_multiple_items_ex(&data);
+		let data = match data {
+			CreateItemExData::RefungibleMultipleOwners(r) => vec![r],
+			CreateItemExData::RefungibleMultipleItems(r)
+				if r.iter().all(|i| i.users.len() == 1) =>
+			{
+				r.into_inner()
+			}
+			_ => fail!(<Error<T>>::NotRefungibleDataUsedToMintFungibleCollectionToken),
+		};
+
+		with_weight(
+			<Pallet<T>>::create_multiple_items(self, &sender, data),
+			weight,
+		)
+	}
+
 	fn burn_item(
 		&self,
 		sender: T::CrossAccountId,
modifiedpallets/refungible/src/lib.rsdiffbeforeafterboth
--- a/pallets/refungible/src/lib.rs
+++ b/pallets/refungible/src/lib.rs
@@ -2,7 +2,8 @@
 
 use frame_support::{ensure, BoundedVec};
 use up_data_structs::{
-	AccessMode, CollectionId, CustomDataLimit, MAX_REFUNGIBLE_PIECES, TokenId, CreateCollectionData,
+	AccessMode, CollectionId, CustomDataLimit, MAX_REFUNGIBLE_PIECES, TokenId,
+	CreateCollectionData, CreateRefungibleExData,
 };
 use pallet_common::{
 	Error as CommonError, Event as CommonEvent, Pallet as PalletCommon, account::CrossAccountId,
@@ -19,11 +20,6 @@
 pub mod common;
 pub mod erc;
 pub mod weights;
-pub struct CreateItemData<T: Config> {
-	pub const_data: BoundedVec<u8, CustomDataLimit>,
-	pub variable_data: BoundedVec<u8, CustomDataLimit>,
-	pub users: BTreeMap<T::CrossAccountId, u128>,
-}
 pub(crate) type SelfWeightOf<T> = <T as Config>::WeightInfo;
 
 #[derive(Encode, Decode, Default, TypeInfo, MaxEncodedLen)]
@@ -361,7 +357,7 @@
 	pub fn create_multiple_items(
 		collection: &RefungibleHandle<T>,
 		sender: &T::CrossAccountId,
-		data: Vec<CreateItemData<T>>,
+		data: Vec<CreateRefungibleExData<T::CrossAccountId>>,
 	) -> DispatchResult {
 		if !collection.is_owner_or_admin(sender) {
 			ensure!(
@@ -606,7 +602,7 @@
 	pub fn create_item(
 		collection: &RefungibleHandle<T>,
 		sender: &T::CrossAccountId,
-		data: CreateItemData<T>,
+		data: CreateRefungibleExData<T::CrossAccountId>,
 	) -> DispatchResult {
 		Self::create_multiple_items(collection, sender, vec![data])
 	}
modifiedpallets/refungible/src/weights.rsdiffbeforeafterboth
--- a/pallets/refungible/src/weights.rs
+++ b/pallets/refungible/src/weights.rs
@@ -34,6 +34,8 @@
 pub trait WeightInfo {
 	fn create_item() -> Weight;
 	fn create_multiple_items(b: u32, ) -> Weight;
+	fn create_multiple_items_ex_multiple_items(b: u32, ) -> Weight;
+	fn create_multiple_items_ex_multiple_owners(b: u32, ) -> Weight;
 	fn burn_item_partial() -> Weight;
 	fn burn_item_fully() -> Weight;
 	fn transfer_normal() -> Weight;
@@ -77,6 +79,36 @@
 			.saturating_add(T::DbWeight::get().writes(2 as Weight))
 			.saturating_add(T::DbWeight::get().writes((4 as Weight).saturating_mul(b as Weight)))
 	}
+	// Storage: Refungible TokensMinted (r:1 w:1)
+	// Storage: Refungible AccountBalance (r:4 w:4)
+	// Storage: Refungible Balance (r:0 w:4)
+	// Storage: Refungible TotalSupply (r:0 w:4)
+	// Storage: Refungible TokenData (r:0 w:4)
+	// Storage: Refungible Owned (r:0 w:4)
+	fn create_multiple_items_ex_multiple_items(b: u32, ) -> Weight {
+		(11_953_000 as Weight)
+			// Standard Error: 27_000
+			.saturating_add((10_775_000 as Weight).saturating_mul(b as Weight))
+			.saturating_add(T::DbWeight::get().reads(1 as Weight))
+			.saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(b as Weight)))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+			.saturating_add(T::DbWeight::get().writes((5 as Weight).saturating_mul(b as Weight)))
+	}
+	// Storage: Refungible TokensMinted (r:1 w:1)
+	// Storage: Refungible TotalSupply (r:0 w:1)
+	// Storage: Refungible TokenData (r:0 w:1)
+	// Storage: Refungible AccountBalance (r:4 w:4)
+	// Storage: Refungible Balance (r:0 w:4)
+	// Storage: Refungible Owned (r:0 w:4)
+	fn create_multiple_items_ex_multiple_owners(b: u32, ) -> Weight {
+		(0 as Weight)
+			// Standard Error: 13_000
+			.saturating_add((8_528_000 as Weight).saturating_mul(b as Weight))
+			.saturating_add(T::DbWeight::get().reads(1 as Weight))
+			.saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(b as Weight)))
+			.saturating_add(T::DbWeight::get().writes(3 as Weight))
+			.saturating_add(T::DbWeight::get().writes((3 as Weight).saturating_mul(b as Weight)))
+	}
 	// Storage: Refungible TotalSupply (r:1 w:1)
 	// Storage: Refungible Balance (r:1 w:1)
 	// Storage: Refungible AccountBalance (r:1 w:1)
@@ -215,6 +247,36 @@
 			.saturating_add(RocksDbWeight::get().writes(2 as Weight))
 			.saturating_add(RocksDbWeight::get().writes((4 as Weight).saturating_mul(b as Weight)))
 	}
+	// Storage: Refungible TokensMinted (r:1 w:1)
+	// Storage: Refungible AccountBalance (r:4 w:4)
+	// Storage: Refungible Balance (r:0 w:4)
+	// Storage: Refungible TotalSupply (r:0 w:4)
+	// Storage: Refungible TokenData (r:0 w:4)
+	// Storage: Refungible Owned (r:0 w:4)
+	fn create_multiple_items_ex_multiple_items(b: u32, ) -> Weight {
+		(11_953_000 as Weight)
+			// Standard Error: 27_000
+			.saturating_add((10_775_000 as Weight).saturating_mul(b as Weight))
+			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
+			.saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(b as Weight)))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+			.saturating_add(RocksDbWeight::get().writes((5 as Weight).saturating_mul(b as Weight)))
+	}
+	// Storage: Refungible TokensMinted (r:1 w:1)
+	// Storage: Refungible TotalSupply (r:0 w:1)
+	// Storage: Refungible TokenData (r:0 w:1)
+	// Storage: Refungible AccountBalance (r:4 w:4)
+	// Storage: Refungible Balance (r:0 w:4)
+	// Storage: Refungible Owned (r:0 w:4)
+	fn create_multiple_items_ex_multiple_owners(b: u32, ) -> Weight {
+		(0 as Weight)
+			// Standard Error: 13_000
+			.saturating_add((8_528_000 as Weight).saturating_mul(b as Weight))
+			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
+			.saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(b as Weight)))
+			.saturating_add(RocksDbWeight::get().writes(3 as Weight))
+			.saturating_add(RocksDbWeight::get().writes((3 as Weight).saturating_mul(b as Weight)))
+	}
 	// Storage: Refungible TotalSupply (r:1 w:1)
 	// Storage: Refungible Balance (r:1 w:1)
 	// Storage: Refungible AccountBalance (r:1 w:1)
modifiedpallets/unique/src/benchmarking.rsdiffbeforeafterboth
--- a/pallets/unique/src/benchmarking.rs
+++ b/pallets/unique/src/benchmarking.rs
@@ -5,9 +5,8 @@
 use frame_system::RawOrigin;
 use frame_benchmarking::{benchmarks, account};
 use up_data_structs::*;
-use core::convert::TryInto;
 use sp_runtime::DispatchError;
-use pallet_common::benchmarking::{create_data, create_u16_data};
+use pallet_common::benchmarking::{create_data, create_var_data, create_u16_data};
 
 const SEED: u32 = 1;
 
@@ -16,13 +15,9 @@
 	mode: CollectionMode,
 ) -> Result<CollectionId, DispatchError> {
 	T::Currency::deposit_creating(&owner, T::CollectionCreationPrice::get());
-	let col_name = create_u16_data(MAX_COLLECTION_NAME_LENGTH)
-		.try_into()
-		.unwrap();
-	let col_desc = create_u16_data(MAX_COLLECTION_DESCRIPTION_LENGTH)
-		.try_into()
-		.unwrap();
-	let token_prefix = create_data(MAX_TOKEN_PREFIX_LENGTH).try_into().unwrap();
+	let col_name = create_u16_data::<MAX_COLLECTION_NAME_LENGTH>();
+	let col_desc = create_u16_data::<MAX_COLLECTION_DESCRIPTION_LENGTH>();
+	let token_prefix = create_data::<MAX_TOKEN_PREFIX_LENGTH>();
 	<Pallet<T>>::create_collection(
 		RawOrigin::Signed(owner).into(),
 		col_name,
@@ -37,11 +32,10 @@
 }
 
 benchmarks! {
-
 	create_collection {
-		let col_name: Vec<u16> = create_u16_data(MAX_COLLECTION_NAME_LENGTH);
-		let col_desc: Vec<u16> = create_u16_data(MAX_COLLECTION_DESCRIPTION_LENGTH);
-		let token_prefix: Vec<u8> = create_data(MAX_TOKEN_PREFIX_LENGTH);
+		let col_name = create_u16_data::<MAX_COLLECTION_NAME_LENGTH>();
+		let col_desc = create_u16_data::<MAX_COLLECTION_DESCRIPTION_LENGTH>();
+		let token_prefix = create_data::<MAX_TOKEN_PREFIX_LENGTH>();
 		let mode: CollectionMode = CollectionMode::NFT;
 		let caller: T::AccountId = account("caller", 0, SEED);
 		T::Currency::deposit_creating(&caller, T::CollectionCreationPrice::get());
@@ -125,7 +119,7 @@
 
 		let caller: T::AccountId = account("caller", 0, SEED);
 		let collection = create_nft_collection::<T>(caller.clone())?;
-		let data = create_data(b as usize);
+		let data = create_var_data(b);
 	}: set_offchain_schema(RawOrigin::Signed(caller.clone()), collection, data)
 
 	set_const_on_chain_schema {
@@ -133,7 +127,7 @@
 
 		let caller: T::AccountId = account("caller", 0, SEED);
 		let collection = create_nft_collection::<T>(caller.clone())?;
-		let data = create_data(b as usize);
+		let data = create_var_data(b);
 	}: set_const_on_chain_schema(RawOrigin::Signed(caller.clone()), collection, data)
 
 	set_variable_on_chain_schema {
@@ -141,7 +135,7 @@
 
 		let caller: T::AccountId = account("caller", 0, SEED);
 		let collection = create_nft_collection::<T>(caller.clone())?;
-		let data = create_data(b as usize);
+		let data = create_var_data(b);
 	}: set_variable_on_chain_schema(RawOrigin::Signed(caller.clone()), collection, data)
 
 	set_schema_version {
modifiedpallets/unique/src/common.rsdiffbeforeafterboth
--- a/pallets/unique/src/common.rs
+++ b/pallets/unique/src/common.rs
@@ -5,6 +5,7 @@
 use pallet_fungible::{common::CommonWeights as FungibleWeights};
 use pallet_nonfungible::{common::CommonWeights as NonfungibleWeights};
 use pallet_refungible::{common::CommonWeights as RefungibleWeights};
+use up_data_structs::CreateItemExData;
 
 use crate::{Config, dispatch::dispatch_weight};
 
@@ -17,7 +18,7 @@
 }
 
 pub struct CommonWeights<T: Config>(PhantomData<T>);
-impl<T: Config> CommonWeightInfo for CommonWeights<T> {
+impl<T: Config> CommonWeightInfo<T::CrossAccountId> for CommonWeights<T> {
 	fn create_item() -> up_data_structs::Weight {
 		dispatch_weight::<T>() + max_weight_of!(create_item())
 	}
@@ -26,6 +27,10 @@
 		dispatch_weight::<T>() + max_weight_of!(create_multiple_items(amount))
 	}
 
+	fn create_multiple_items_ex(data: &CreateItemExData<T::CrossAccountId>) -> Weight {
+		dispatch_weight::<T>() + max_weight_of!(create_multiple_items_ex(data))
+	}
+
 	fn burn_item() -> Weight {
 		dispatch_weight::<T>() + max_weight_of!(burn_item())
 	}
modifiedpallets/unique/src/lib.rsdiffbeforeafterboth
--- a/pallets/unique/src/lib.rs
+++ b/pallets/unique/src/lib.rs
@@ -40,7 +40,7 @@
 	OFFCHAIN_SCHEMA_LIMIT, MAX_COLLECTION_NAME_LENGTH, MAX_COLLECTION_DESCRIPTION_LENGTH,
 	MAX_TOKEN_PREFIX_LENGTH, AccessMode, CreateItemData, CollectionLimits, CollectionId,
 	CollectionMode, TokenId, SchemaVersion, SponsorshipState, MetaUpdatePermission,
-	CreateCollectionData, CustomDataLimit,
+	CreateCollectionData, CustomDataLimit, CreateItemExData,
 };
 use pallet_common::{
 	account::CrossAccountId, CollectionHandle, Pallet as PalletCommon, Error as CommonError,
@@ -735,6 +735,14 @@
 			dispatch_call::<T, _>(collection_id, |d| d.create_multiple_items(sender, owner, items_data))
 		}
 
+		#[weight = <CommonWeights<T>>::create_multiple_items_ex(&data)]
+		#[transactional]
+		pub fn create_multiple_items_ex(origin, collection_id: CollectionId, data: CreateItemExData<T::CrossAccountId>) -> DispatchResultWithPostInfo {
+			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
+
+			dispatch_call::<T, _>(collection_id, |d| d.create_multiple_items_ex(sender, data))
+		}
+
 		// TODO! transaction weight
 
 		/// Set transfers_enabled value for particular collection
modifiedprimitives/data-structs/src/lib.rsdiffbeforeafterboth
--- a/primitives/data-structs/src/lib.rs
+++ b/primitives/data-structs/src/lib.rs
@@ -1,6 +1,11 @@
 #![cfg_attr(not(feature = "std"), no_std)]
 
-use core::convert::{TryFrom, TryInto};
+use core::{
+	convert::{TryFrom, TryInto},
+	fmt,
+};
+use frame_support::storage::bounded_btree_map::BoundedBTreeMap;
+use sp_std::collections::btree_map::BTreeMap;
 
 #[cfg(feature = "serde")]
 pub use serde::{Serialize, Deserialize};
@@ -76,9 +81,7 @@
 /// create_many call
 pub const MAX_ITEMS_PER_BATCH: u32 = 200;
 
-parameter_types! {
-	pub const CustomDataLimit: u32 = CUSTOM_DATA_LIMIT;
-}
+pub type CustomDataLimit = ConstU32<CUSTOM_DATA_LIMIT>;
 
 #[derive(
 	Encode,
@@ -417,15 +420,72 @@
 	}
 }
 
+fn bounded_debug<V, S>(v: &BoundedVec<V, S>, f: &mut fmt::Formatter) -> Result<(), fmt::Error>
+where
+	V: fmt::Debug,
+{
+	use core::fmt::Debug;
+	(&v as &Vec<V>).fmt(f)
+}
+
+#[cfg(feature = "serde1")]
+#[allow(dead_code)]
+mod bounded_map_serde {
+	use core::convert::TryFrom;
+	use sp_std::collections::btree_map::BTreeMap;
+	use frame_support::{traits::Get, storage::bounded_btree_map::BoundedBTreeMap};
+	use serde::{
+		ser::{self, Serialize},
+		de::{self, Deserialize, Error},
+	};
+	pub fn serialize<D, K, V, S>(
+		value: &BoundedBTreeMap<K, V, S>,
+		serializer: D,
+	) -> Result<D::Ok, D::Error>
+	where
+		D: ser::Serializer,
+		K: Serialize + Ord,
+		V: Serialize,
+	{
+		(value as &BTreeMap<_, _>).serialize(serializer)
+	}
+
+	pub fn deserialize<'de, D, K, V, S>(
+		deserializer: D,
+	) -> Result<BoundedBTreeMap<K, V, S>, D::Error>
+	where
+		D: de::Deserializer<'de>,
+		K: de::Deserialize<'de> + Ord,
+		V: de::Deserialize<'de>,
+		S: Get<u32>,
+	{
+		let map = <BTreeMap<K, V>>::deserialize(deserializer)?;
+		let len = map.len();
+		TryFrom::try_from(map).map_err(|_| D::Error::invalid_length(len, &"lesser size"))
+	}
+}
+
+fn bounded_map_debug<K, V, S>(
+	v: &BoundedBTreeMap<K, V, S>,
+	f: &mut fmt::Formatter,
+) -> Result<(), fmt::Error>
+where
+	K: fmt::Debug + Ord,
+	V: fmt::Debug,
+{
+	use core::fmt::Debug;
+	(&v as &BTreeMap<K, V>).fmt(f)
+}
+
 #[derive(Encode, Decode, MaxEncodedLen, Default, PartialEq, Clone, Derivative, TypeInfo)]
 #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
 #[derivative(Debug)]
 pub struct CreateNftData {
 	#[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]
-	#[derivative(Debug = "ignore")]
+	#[derivative(Debug(format_with = "bounded_debug"))]
 	pub const_data: BoundedVec<u8, CustomDataLimit>,
 	#[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]
-	#[derivative(Debug = "ignore")]
+	#[derivative(Debug(format_with = "bounded_debug"))]
 	pub variable_data: BoundedVec<u8, CustomDataLimit>,
 }
 
@@ -440,10 +500,10 @@
 #[derivative(Debug)]
 pub struct CreateReFungibleData {
 	#[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]
-	#[derivative(Debug = "ignore")]
+	#[derivative(Debug(format_with = "bounded_debug"))]
 	pub const_data: BoundedVec<u8, CustomDataLimit>,
 	#[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]
-	#[derivative(Debug = "ignore")]
+	#[derivative(Debug(format_with = "bounded_debug"))]
 	pub variable_data: BoundedVec<u8, CustomDataLimit>,
 	pub pieces: u128,
 }
@@ -470,6 +530,47 @@
 	ReFungible(CreateReFungibleData),
 }
 
+#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]
+#[derivative(Debug)]
+pub struct CreateNftExData<CrossAccountId> {
+	#[derivative(Debug(format_with = "bounded_debug"))]
+	pub const_data: BoundedVec<u8, CustomDataLimit>,
+	#[derivative(Debug(format_with = "bounded_debug"))]
+	pub variable_data: BoundedVec<u8, CustomDataLimit>,
+	pub owner: CrossAccountId,
+}
+
+#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]
+#[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))]
+pub struct CreateRefungibleExData<CrossAccountId> {
+	#[derivative(Debug(format_with = "bounded_debug"))]
+	pub const_data: BoundedVec<u8, CustomDataLimit>,
+	#[derivative(Debug(format_with = "bounded_debug"))]
+	pub variable_data: BoundedVec<u8, CustomDataLimit>,
+	#[derivative(Debug(format_with = "bounded_map_debug"))]
+	pub users: BoundedBTreeMap<CrossAccountId, u128, ConstU32<MAX_ITEMS_PER_BATCH>>,
+}
+
+#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]
+#[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))]
+pub enum CreateItemExData<CrossAccountId> {
+	NFT(
+		#[derivative(Debug(format_with = "bounded_debug"))]
+		BoundedVec<CreateNftExData<CrossAccountId>, ConstU32<MAX_ITEMS_PER_BATCH>>,
+	),
+	Fungible(
+		#[derivative(Debug(format_with = "bounded_map_debug"))]
+		BoundedBTreeMap<CrossAccountId, u128, ConstU32<MAX_ITEMS_PER_BATCH>>,
+	),
+	/// Many tokens, each may have only one owner
+	RefungibleMultipleItems(
+		#[derivative(Debug(format_with = "bounded_debug"))]
+		BoundedVec<CreateRefungibleExData<CrossAccountId>, ConstU32<MAX_ITEMS_PER_BATCH>>,
+	),
+	/// Single token, which may have many owners
+	RefungibleMultipleOwners(CreateRefungibleExData<CrossAccountId>),
+}
+
 impl CreateItemData {
 	pub fn data_size(&self) -> usize {
 		match self {
addedtests/src/createMultipleItemsEx.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/tests/src/createMultipleItemsEx.test.ts
@@ -0,0 +1,58 @@
+import {expect} from 'chai';
+import privateKey from './substrate/privateKey';
+import usingApi, {executeTransaction} from './substrate/substrate-api';
+import {createCollectionExpectSuccess} from './util/helpers';
+
+describe('createMultipleItemsEx', () => {
+  it('can initialize multiple NFT with different owners', async () => {
+    const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
+    const alice = privateKey('//Alice');
+    const bob = privateKey('//Bob');
+    const charlie = privateKey('//Charlie');
+    await usingApi(async (api) => {
+      const data = [
+        {
+          owner: {substrate: alice.address},
+          constData: '0x0000',
+          variableData: '0x1111',
+        }, {
+          owner: {substrate: bob.address},
+          constData: '0x2222',
+          variableData: '0x3333',
+        }, {
+          owner: {substrate: charlie.address},
+          constData: '0x4444',
+          variableData: '0x5555',
+        },
+      ];
+
+      await executeTransaction(api, alice, api.tx.unique.createMultipleItemsEx(collection, {
+        NFT: data,
+      }));
+      const tokens = await api.query.nonfungible.tokenData.entries(collection);
+      const json = tokens.map(([, token]) => token.toJSON());
+      expect(json).to.be.deep.equal(data);
+    });
+  });
+
+  it('fails when trying to set multiple owners when creating multiple refungibles', async () => {
+    const collection = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
+    const alice = privateKey('//Alice');
+    const bob = privateKey('//Bob');
+
+    await usingApi(async (api) => {
+      // Polkadot requires map, and yet requires keys to be JSON encoded
+      const users = new Map();
+      users.set(JSON.stringify({substrate: alice.address}), 1);
+      users.set(JSON.stringify({substrate: bob.address}), 1);
+
+      // TODO: better error message?
+      await expect(executeTransaction(api, alice, api.tx.unique.createMultipleItemsEx(collection, {
+        RefungibleMultipleItems: [
+          {users},
+          {users},
+        ],
+      }))).to.be.rejectedWith(/^refungible\.NotRefungibleDataUsedToMintFungibleCollectionToken$/);
+    });
+  });
+});
modifiedtests/src/interfaces/augment-api-tx.tsdiffbeforeafterboth
--- a/tests/src/interfaces/augment-api-tx.ts
+++ b/tests/src/interfaces/augment-api-tx.ts
@@ -5,7 +5,7 @@
 import type { Bytes, Compact, Option, U256, Vec, bool, u128, u16, u32, u64 } from '@polkadot/types-codec';
 import type { AnyNumber, ITuple } from '@polkadot/types-codec/types';
 import type { AccountId32, Call, H160, H256, MultiAddress, Perbill } from '@polkadot/types/interfaces/runtime';
-import type { CumulusPrimitivesParachainInherentParachainInherentData, EthereumTransactionTransactionV2, OrmlVestingVestingSchedule, PalletCommonAccountBasicCrossAccountIdRepr, UpDataStructsAccessMode, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCreateCollectionData, UpDataStructsCreateItemData, UpDataStructsMetaUpdatePermission, UpDataStructsSchemaVersion, XcmV1MultiLocation, XcmV2WeightLimit, XcmVersionedMultiAssets, XcmVersionedMultiLocation, XcmVersionedXcm } from '@polkadot/types/lookup';
+import type { CumulusPrimitivesParachainInherentParachainInherentData, EthereumTransactionTransactionV2, OrmlVestingVestingSchedule, PalletCommonAccountBasicCrossAccountIdRepr, UpDataStructsAccessMode, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCreateCollectionData, UpDataStructsCreateItemData, UpDataStructsCreateItemExData, UpDataStructsMetaUpdatePermission, UpDataStructsSchemaVersion, XcmV1MultiLocation, XcmV2WeightLimit, XcmVersionedMultiAssets, XcmVersionedMultiLocation, XcmVersionedXcm } from '@polkadot/types/lookup';
 
 declare module '@polkadot/api-base/types/submittable' {
   export interface AugmentedSubmittables<ApiType extends ApiTypes> {
@@ -710,6 +710,7 @@
        * * owner: Address, initial owner of the NFT.
        **/
       createMultipleItems: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, owner: PalletCommonAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, itemsData: Vec<UpDataStructsCreateItemData> | (UpDataStructsCreateItemData | { NFT: any } | { Fungible: any } | { ReFungible: any } | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>, [u32, PalletCommonAccountBasicCrossAccountIdRepr, Vec<UpDataStructsCreateItemData>]>;
+      createMultipleItemsEx: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, data: UpDataStructsCreateItemExData | { NFT: any } | { Fungible: any } | { RefungibleMultipleItems: any } | { RefungibleMultipleOwners: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, UpDataStructsCreateItemExData]>;
       /**
        * **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.
        * 
modifiedtests/src/interfaces/augment-types.tsdiffbeforeafterboth
--- a/tests/src/interfaces/augment-types.ts
+++ b/tests/src/interfaces/augment-types.ts
@@ -1,7 +1,7 @@
 // Auto-generated via `yarn polkadot-types-from-defs`, do not edit
 /* eslint-disable */
 
-import type { CumulusPalletDmpQueueCall, CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueueError, CumulusPalletDmpQueueEvent, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemCall, CumulusPalletParachainSystemError, CumulusPalletParachainSystemEvent, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletXcmCall, CumulusPalletXcmError, CumulusPalletXcmEvent, CumulusPalletXcmpQueueCall, CumulusPalletXcmpQueueError, CumulusPalletXcmpQueueEvent, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueInboundState, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueOutboundState, CumulusPalletXcmpQueueQueueConfigData, CumulusPrimitivesParachainInherentParachainInherentData, EthbloomBloom, EthereumBlock, EthereumHeader, EthereumLog, EthereumReceiptEip658ReceiptData, EthereumReceiptReceiptV3, EthereumTransactionAccessListItem, EthereumTransactionEip1559Transaction, EthereumTransactionEip2930Transaction, EthereumTransactionLegacyTransaction, EthereumTransactionTransactionAction, EthereumTransactionTransactionSignature, EthereumTransactionTransactionV2, EthereumTypesHashH64, EvmCoreErrorExitError, EvmCoreErrorExitFatal, EvmCoreErrorExitReason, EvmCoreErrorExitRevert, EvmCoreErrorExitSucceed, FpRpcTransactionStatus, FrameSupportPalletId, FrameSupportTokensMiscBalanceStatus, FrameSupportWeightsDispatchClass, FrameSupportWeightsDispatchInfo, FrameSupportWeightsPays, FrameSupportWeightsPerDispatchClassU32, FrameSupportWeightsPerDispatchClassU64, FrameSupportWeightsPerDispatchClassWeightsPerClass, FrameSupportWeightsRuntimeDbWeight, FrameSupportWeightsWeightToFeeCoefficient, FrameSystemAccountInfo, FrameSystemCall, FrameSystemError, FrameSystemEvent, FrameSystemEventRecord, FrameSystemExtensionsCheckGenesis, FrameSystemExtensionsCheckNonce, FrameSystemExtensionsCheckSpecVersion, FrameSystemExtensionsCheckWeight, FrameSystemLastRuntimeUpgradeInfo, FrameSystemLimitsBlockLength, FrameSystemLimitsBlockWeights, FrameSystemLimitsWeightsPerClass, FrameSystemPhase, OrmlVestingModuleCall, OrmlVestingModuleError, OrmlVestingModuleEvent, OrmlVestingVestingSchedule, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesCall, PalletBalancesError, PalletBalancesEvent, PalletBalancesReasons, PalletBalancesReleases, PalletBalancesReserveData, PalletCommonAccountBasicCrossAccountIdRepr, PalletCommonError, PalletCommonEvent, PalletEthereumCall, PalletEthereumError, PalletEthereumEvent, PalletEvmCall, PalletEvmCoderSubstrateError, PalletEvmContractHelpersError, PalletEvmContractHelpersSponsoringModeT, PalletEvmError, PalletEvmEvent, PalletEvmMigrationCall, PalletEvmMigrationError, PalletFungibleError, PalletInflationCall, PalletNonfungibleError, PalletNonfungibleItemData, PalletRefungibleError, PalletRefungibleItemData, PalletSudoCall, PalletSudoError, PalletSudoEvent, PalletTemplateTransactionPaymentCall, PalletTemplateTransactionPaymentChargeTransactionPayment, PalletTimestampCall, PalletTransactionPaymentReleases, PalletTreasuryCall, PalletTreasuryError, PalletTreasuryEvent, PalletTreasuryProposal, PalletUniqueCall, PalletUniqueError, PalletUniqueRawEvent, PalletXcmCall, PalletXcmError, PalletXcmEvent, PolkadotCorePrimitivesInboundDownwardMessage, PolkadotCorePrimitivesInboundHrmpMessage, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotParachainPrimitivesXcmpMessageFormat, PolkadotPrimitivesV1AbridgedHostConfiguration, PolkadotPrimitivesV1AbridgedHrmpChannel, PolkadotPrimitivesV1PersistedValidationData, PolkadotPrimitivesV1UpgradeRestriction, SpCoreEcdsaSignature, SpCoreEd25519Signature, SpCoreSr25519Signature, SpRuntimeArithmeticError, SpRuntimeDigest, SpRuntimeDigestDigestItem, SpRuntimeDispatchError, SpRuntimeModuleError, SpRuntimeMultiSignature, SpRuntimeTokenError, SpTrieStorageProof, SpVersionRuntimeVersion, UniqueRuntimeRuntime, UpDataStructsAccessMode, UpDataStructsCollection, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCollectionStats, UpDataStructsCreateCollectionData, UpDataStructsCreateFungibleData, UpDataStructsCreateItemData, UpDataStructsCreateNftData, UpDataStructsCreateReFungibleData, UpDataStructsMetaUpdatePermission, UpDataStructsSchemaVersion, UpDataStructsSponsoringRateLimit, UpDataStructsSponsorshipState, XcmDoubleEncoded, XcmV0Junction, XcmV0JunctionBodyId, XcmV0JunctionBodyPart, XcmV0JunctionNetworkId, XcmV0MultiAsset, XcmV0MultiLocation, XcmV0Order, XcmV0OriginKind, XcmV0Response, XcmV0Xcm, XcmV1Junction, XcmV1MultiAsset, XcmV1MultiLocation, XcmV1MultiassetAssetId, XcmV1MultiassetAssetInstance, XcmV1MultiassetFungibility, XcmV1MultiassetMultiAssetFilter, XcmV1MultiassetMultiAssets, XcmV1MultiassetWildFungibility, XcmV1MultiassetWildMultiAsset, XcmV1MultilocationJunctions, XcmV1Order, XcmV1Response, XcmV1Xcm, XcmV2Instruction, XcmV2Response, XcmV2TraitsError, XcmV2TraitsOutcome, XcmV2WeightLimit, XcmV2Xcm, XcmVersionedMultiAssets, XcmVersionedMultiLocation, XcmVersionedXcm } from './unique';
+import type { CumulusPalletDmpQueueCall, CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueueError, CumulusPalletDmpQueueEvent, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemCall, CumulusPalletParachainSystemError, CumulusPalletParachainSystemEvent, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletXcmCall, CumulusPalletXcmError, CumulusPalletXcmEvent, CumulusPalletXcmpQueueCall, CumulusPalletXcmpQueueError, CumulusPalletXcmpQueueEvent, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueInboundState, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueOutboundState, CumulusPalletXcmpQueueQueueConfigData, CumulusPrimitivesParachainInherentParachainInherentData, EthbloomBloom, EthereumBlock, EthereumHeader, EthereumLog, EthereumReceiptEip658ReceiptData, EthereumReceiptReceiptV3, EthereumTransactionAccessListItem, EthereumTransactionEip1559Transaction, EthereumTransactionEip2930Transaction, EthereumTransactionLegacyTransaction, EthereumTransactionTransactionAction, EthereumTransactionTransactionSignature, EthereumTransactionTransactionV2, EthereumTypesHashH64, EvmCoreErrorExitError, EvmCoreErrorExitFatal, EvmCoreErrorExitReason, EvmCoreErrorExitRevert, EvmCoreErrorExitSucceed, FpRpcTransactionStatus, FrameSupportPalletId, FrameSupportTokensMiscBalanceStatus, FrameSupportWeightsDispatchClass, FrameSupportWeightsDispatchInfo, FrameSupportWeightsPays, FrameSupportWeightsPerDispatchClassU32, FrameSupportWeightsPerDispatchClassU64, FrameSupportWeightsPerDispatchClassWeightsPerClass, FrameSupportWeightsRuntimeDbWeight, FrameSupportWeightsWeightToFeeCoefficient, FrameSystemAccountInfo, FrameSystemCall, FrameSystemError, FrameSystemEvent, FrameSystemEventRecord, FrameSystemExtensionsCheckGenesis, FrameSystemExtensionsCheckNonce, FrameSystemExtensionsCheckSpecVersion, FrameSystemExtensionsCheckWeight, FrameSystemLastRuntimeUpgradeInfo, FrameSystemLimitsBlockLength, FrameSystemLimitsBlockWeights, FrameSystemLimitsWeightsPerClass, FrameSystemPhase, OrmlVestingModuleCall, OrmlVestingModuleError, OrmlVestingModuleEvent, OrmlVestingVestingSchedule, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesCall, PalletBalancesError, PalletBalancesEvent, PalletBalancesReasons, PalletBalancesReleases, PalletBalancesReserveData, PalletCommonAccountBasicCrossAccountIdRepr, PalletCommonError, PalletCommonEvent, PalletEthereumCall, PalletEthereumError, PalletEthereumEvent, PalletEvmCall, PalletEvmCoderSubstrateError, PalletEvmContractHelpersError, PalletEvmContractHelpersSponsoringModeT, PalletEvmError, PalletEvmEvent, PalletEvmMigrationCall, PalletEvmMigrationError, PalletFungibleError, PalletInflationCall, PalletNonfungibleError, PalletNonfungibleItemData, PalletRefungibleError, PalletRefungibleItemData, PalletSudoCall, PalletSudoError, PalletSudoEvent, PalletTemplateTransactionPaymentCall, PalletTemplateTransactionPaymentChargeTransactionPayment, PalletTimestampCall, PalletTransactionPaymentReleases, PalletTreasuryCall, PalletTreasuryError, PalletTreasuryEvent, PalletTreasuryProposal, PalletUniqueCall, PalletUniqueError, PalletUniqueRawEvent, PalletXcmCall, PalletXcmError, PalletXcmEvent, PolkadotCorePrimitivesInboundDownwardMessage, PolkadotCorePrimitivesInboundHrmpMessage, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotParachainPrimitivesXcmpMessageFormat, PolkadotPrimitivesV1AbridgedHostConfiguration, PolkadotPrimitivesV1AbridgedHrmpChannel, PolkadotPrimitivesV1PersistedValidationData, PolkadotPrimitivesV1UpgradeRestriction, SpCoreEcdsaSignature, SpCoreEd25519Signature, SpCoreSr25519Signature, SpRuntimeArithmeticError, SpRuntimeDigest, SpRuntimeDigestDigestItem, SpRuntimeDispatchError, SpRuntimeModuleError, SpRuntimeMultiSignature, SpRuntimeTokenError, SpTrieStorageProof, SpVersionRuntimeVersion, UniqueRuntimeRuntime, UpDataStructsAccessMode, UpDataStructsCollection, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCollectionStats, UpDataStructsCreateCollectionData, UpDataStructsCreateFungibleData, UpDataStructsCreateItemData, UpDataStructsCreateItemExData, UpDataStructsCreateNftData, UpDataStructsCreateNftExData, UpDataStructsCreateReFungibleData, UpDataStructsCreateRefungibleExData, UpDataStructsMetaUpdatePermission, UpDataStructsSchemaVersion, UpDataStructsSponsoringRateLimit, UpDataStructsSponsorshipState, XcmDoubleEncoded, XcmV0Junction, XcmV0JunctionBodyId, XcmV0JunctionBodyPart, XcmV0JunctionNetworkId, XcmV0MultiAsset, XcmV0MultiLocation, XcmV0Order, XcmV0OriginKind, XcmV0Response, XcmV0Xcm, XcmV1Junction, XcmV1MultiAsset, XcmV1MultiLocation, XcmV1MultiassetAssetId, XcmV1MultiassetAssetInstance, XcmV1MultiassetFungibility, XcmV1MultiassetMultiAssetFilter, XcmV1MultiassetMultiAssets, XcmV1MultiassetWildFungibility, XcmV1MultiassetWildMultiAsset, XcmV1MultilocationJunctions, XcmV1Order, XcmV1Response, XcmV1Xcm, XcmV2Instruction, XcmV2Response, XcmV2TraitsError, XcmV2TraitsOutcome, XcmV2WeightLimit, XcmV2Xcm, XcmVersionedMultiAssets, XcmVersionedMultiLocation, XcmVersionedXcm } from './unique';
 import type { Data, StorageKey } from '@polkadot/types';
 import type { BitVec, Bool, Bytes, I128, I16, I256, I32, I64, I8, Json, Null, Raw, Text, Type, U128, U16, U256, U32, U64, U8, USize, bool, i128, i16, i256, i32, i64, i8, u128, u16, u256, u32, u64, u8, usize } from '@polkadot/types-codec';
 import type { AssetApproval, AssetApprovalKey, AssetBalance, AssetDestroyWitness, AssetDetails, AssetMetadata, TAssetBalance, TAssetDepositBalance } from '@polkadot/types/interfaces/assets';
@@ -1159,8 +1159,11 @@
     UpDataStructsCreateCollectionData: UpDataStructsCreateCollectionData;
     UpDataStructsCreateFungibleData: UpDataStructsCreateFungibleData;
     UpDataStructsCreateItemData: UpDataStructsCreateItemData;
+    UpDataStructsCreateItemExData: UpDataStructsCreateItemExData;
     UpDataStructsCreateNftData: UpDataStructsCreateNftData;
+    UpDataStructsCreateNftExData: UpDataStructsCreateNftExData;
     UpDataStructsCreateReFungibleData: UpDataStructsCreateReFungibleData;
+    UpDataStructsCreateRefungibleExData: UpDataStructsCreateRefungibleExData;
     UpDataStructsMetaUpdatePermission: UpDataStructsMetaUpdatePermission;
     UpDataStructsSchemaVersion: UpDataStructsSchemaVersion;
     UpDataStructsSponsoringRateLimit: UpDataStructsSponsoringRateLimit;
modifiedtests/src/interfaces/lookup.tsdiffbeforeafterboth
--- a/tests/src/interfaces/lookup.ts
+++ b/tests/src/interfaces/lookup.ts
@@ -1300,6 +1300,10 @@
         owner: 'PalletCommonAccountBasicCrossAccountIdRepr',
         itemsData: 'Vec<UpDataStructsCreateItemData>',
       },
+      create_multiple_items_ex: {
+        collectionId: 'u32',
+        data: 'UpDataStructsCreateItemExData',
+      },
       set_transfers_enabled_flag: {
         collectionId: 'u32',
         value: 'bool',
@@ -1474,11 +1478,38 @@
     pieces: 'u128'
   },
   /**
-   * Lookup181: pallet_template_transaction_payment::Call<T>
+   * Lookup180: up_data_structs::CreateItemExData<pallet_common::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
    **/
+  UpDataStructsCreateItemExData: {
+    _enum: {
+      NFT: 'Vec<UpDataStructsCreateNftExData>',
+      Fungible: 'BTreeMap<PalletCommonAccountBasicCrossAccountIdRepr, u128>',
+      RefungibleMultipleItems: 'Vec<UpDataStructsCreateRefungibleExData>',
+      RefungibleMultipleOwners: 'UpDataStructsCreateRefungibleExData'
+    }
+  },
+  /**
+   * Lookup182: up_data_structs::CreateNftExData<pallet_common::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
+   **/
+  UpDataStructsCreateNftExData: {
+    constData: 'Bytes',
+    variableData: 'Bytes',
+    owner: 'PalletCommonAccountBasicCrossAccountIdRepr'
+  },
+  /**
+   * Lookup189: up_data_structs::CreateRefungibleExData<pallet_common::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
+   **/
+  UpDataStructsCreateRefungibleExData: {
+    constData: 'Bytes',
+    variableData: 'Bytes',
+    users: 'BTreeMap<PalletCommonAccountBasicCrossAccountIdRepr, u128>'
+  },
+  /**
+   * Lookup192: pallet_template_transaction_payment::Call<T>
+   **/
   PalletTemplateTransactionPaymentCall: 'Null',
   /**
-   * Lookup182: pallet_evm::pallet::Call<T>
+   * Lookup193: pallet_evm::pallet::Call<T>
    **/
   PalletEvmCall: {
     _enum: {
@@ -1521,7 +1552,7 @@
     }
   },
   /**
-   * Lookup188: pallet_ethereum::pallet::Call<T>
+   * Lookup199: pallet_ethereum::pallet::Call<T>
    **/
   PalletEthereumCall: {
     _enum: {
@@ -1531,7 +1562,7 @@
     }
   },
   /**
-   * Lookup189: ethereum::transaction::TransactionV2
+   * Lookup200: ethereum::transaction::TransactionV2
    **/
   EthereumTransactionTransactionV2: {
     _enum: {
@@ -1541,7 +1572,7 @@
     }
   },
   /**
-   * Lookup190: ethereum::transaction::LegacyTransaction
+   * Lookup201: ethereum::transaction::LegacyTransaction
    **/
   EthereumTransactionLegacyTransaction: {
     nonce: 'U256',
@@ -1553,7 +1584,7 @@
     signature: 'EthereumTransactionTransactionSignature'
   },
   /**
-   * Lookup191: ethereum::transaction::TransactionAction
+   * Lookup202: ethereum::transaction::TransactionAction
    **/
   EthereumTransactionTransactionAction: {
     _enum: {
@@ -1562,7 +1593,7 @@
     }
   },
   /**
-   * Lookup192: ethereum::transaction::TransactionSignature
+   * Lookup203: ethereum::transaction::TransactionSignature
    **/
   EthereumTransactionTransactionSignature: {
     v: 'u64',
@@ -1570,7 +1601,7 @@
     s: 'H256'
   },
   /**
-   * Lookup194: ethereum::transaction::EIP2930Transaction
+   * Lookup205: ethereum::transaction::EIP2930Transaction
    **/
   EthereumTransactionEip2930Transaction: {
     chainId: 'u64',
@@ -1586,14 +1617,14 @@
     s: 'H256'
   },
   /**
-   * Lookup196: ethereum::transaction::AccessListItem
+   * Lookup207: ethereum::transaction::AccessListItem
    **/
   EthereumTransactionAccessListItem: {
     address: 'H160',
     slots: 'Vec<H256>'
   },
   /**
-   * Lookup197: ethereum::transaction::EIP1559Transaction
+   * Lookup208: ethereum::transaction::EIP1559Transaction
    **/
   EthereumTransactionEip1559Transaction: {
     chainId: 'u64',
@@ -1610,7 +1641,7 @@
     s: 'H256'
   },
   /**
-   * Lookup198: pallet_evm_migration::pallet::Call<T>
+   * Lookup209: pallet_evm_migration::pallet::Call<T>
    **/
   PalletEvmMigrationCall: {
     _enum: {
@@ -1628,7 +1659,7 @@
     }
   },
   /**
-   * Lookup201: pallet_sudo::pallet::Event<T>
+   * Lookup212: pallet_sudo::pallet::Event<T>
    **/
   PalletSudoEvent: {
     _enum: {
@@ -1644,7 +1675,7 @@
     }
   },
   /**
-   * Lookup203: sp_runtime::DispatchError
+   * Lookup214: sp_runtime::DispatchError
    **/
   SpRuntimeDispatchError: {
     _enum: {
@@ -1660,32 +1691,32 @@
     }
   },
   /**
-   * Lookup204: sp_runtime::ModuleError
+   * Lookup215: sp_runtime::ModuleError
    **/
   SpRuntimeModuleError: {
     index: 'u8',
     error: 'u8'
   },
   /**
-   * Lookup205: sp_runtime::TokenError
+   * Lookup216: sp_runtime::TokenError
    **/
   SpRuntimeTokenError: {
     _enum: ['NoFunds', 'WouldDie', 'BelowMinimum', 'CannotCreate', 'UnknownAsset', 'Frozen', 'Unsupported']
   },
   /**
-   * Lookup206: sp_runtime::ArithmeticError
+   * Lookup217: sp_runtime::ArithmeticError
    **/
   SpRuntimeArithmeticError: {
     _enum: ['Underflow', 'Overflow', 'DivisionByZero']
   },
   /**
-   * Lookup207: pallet_sudo::pallet::Error<T>
+   * Lookup218: pallet_sudo::pallet::Error<T>
    **/
   PalletSudoError: {
     _enum: ['RequireSudo']
   },
   /**
-   * Lookup208: frame_system::AccountInfo<Index, pallet_balances::AccountData<Balance>>
+   * Lookup219: frame_system::AccountInfo<Index, pallet_balances::AccountData<Balance>>
    **/
   FrameSystemAccountInfo: {
     nonce: 'u32',
@@ -1695,7 +1726,7 @@
     data: 'PalletBalancesAccountData'
   },
   /**
-   * Lookup209: frame_support::weights::PerDispatchClass<T>
+   * Lookup220: frame_support::weights::PerDispatchClass<T>
    **/
   FrameSupportWeightsPerDispatchClassU64: {
     normal: 'u64',
@@ -1703,13 +1734,13 @@
     mandatory: 'u64'
   },
   /**
-   * Lookup210: sp_runtime::generic::digest::Digest
+   * Lookup221: sp_runtime::generic::digest::Digest
    **/
   SpRuntimeDigest: {
     logs: 'Vec<SpRuntimeDigestDigestItem>'
   },
   /**
-   * Lookup212: sp_runtime::generic::digest::DigestItem
+   * Lookup223: sp_runtime::generic::digest::DigestItem
    **/
   SpRuntimeDigestDigestItem: {
     _enum: {
@@ -1725,7 +1756,7 @@
     }
   },
   /**
-   * Lookup214: frame_system::EventRecord<unique_runtime::Event, primitive_types::H256>
+   * Lookup225: frame_system::EventRecord<unique_runtime::Event, primitive_types::H256>
    **/
   FrameSystemEventRecord: {
     phase: 'FrameSystemPhase',
@@ -1733,7 +1764,7 @@
     topics: 'Vec<H256>'
   },
   /**
-   * Lookup216: frame_system::pallet::Event<T>
+   * Lookup227: frame_system::pallet::Event<T>
    **/
   FrameSystemEvent: {
     _enum: {
@@ -1761,7 +1792,7 @@
     }
   },
   /**
-   * Lookup217: frame_support::weights::DispatchInfo
+   * Lookup228: frame_support::weights::DispatchInfo
    **/
   FrameSupportWeightsDispatchInfo: {
     weight: 'u64',
@@ -1769,19 +1800,19 @@
     paysFee: 'FrameSupportWeightsPays'
   },
   /**
-   * Lookup218: frame_support::weights::DispatchClass
+   * Lookup229: frame_support::weights::DispatchClass
    **/
   FrameSupportWeightsDispatchClass: {
     _enum: ['Normal', 'Operational', 'Mandatory']
   },
   /**
-   * Lookup219: frame_support::weights::Pays
+   * Lookup230: frame_support::weights::Pays
    **/
   FrameSupportWeightsPays: {
     _enum: ['Yes', 'No']
   },
   /**
-   * Lookup220: orml_vesting::module::Event<T>
+   * Lookup231: orml_vesting::module::Event<T>
    **/
   OrmlVestingModuleEvent: {
     _enum: {
@@ -1800,7 +1831,7 @@
     }
   },
   /**
-   * Lookup221: cumulus_pallet_xcmp_queue::pallet::Event<T>
+   * Lookup232: cumulus_pallet_xcmp_queue::pallet::Event<T>
    **/
   CumulusPalletXcmpQueueEvent: {
     _enum: {
@@ -1815,7 +1846,7 @@
     }
   },
   /**
-   * Lookup222: pallet_xcm::pallet::Event<T>
+   * Lookup233: pallet_xcm::pallet::Event<T>
    **/
   PalletXcmEvent: {
     _enum: {
@@ -1838,7 +1869,7 @@
     }
   },
   /**
-   * Lookup223: xcm::v2::traits::Outcome
+   * Lookup234: xcm::v2::traits::Outcome
    **/
   XcmV2TraitsOutcome: {
     _enum: {
@@ -1848,7 +1879,7 @@
     }
   },
   /**
-   * Lookup225: cumulus_pallet_xcm::pallet::Event<T>
+   * Lookup236: cumulus_pallet_xcm::pallet::Event<T>
    **/
   CumulusPalletXcmEvent: {
     _enum: {
@@ -1858,7 +1889,7 @@
     }
   },
   /**
-   * Lookup226: cumulus_pallet_dmp_queue::pallet::Event<T>
+   * Lookup237: cumulus_pallet_dmp_queue::pallet::Event<T>
    **/
   CumulusPalletDmpQueueEvent: {
     _enum: {
@@ -1871,7 +1902,7 @@
     }
   },
   /**
-   * Lookup227: pallet_unique::RawEvent<sp_core::crypto::AccountId32, pallet_common::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
+   * Lookup238: pallet_unique::RawEvent<sp_core::crypto::AccountId32, pallet_common::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
    **/
   PalletUniqueRawEvent: {
     _enum: {
@@ -1893,7 +1924,7 @@
     }
   },
   /**
-   * Lookup228: pallet_common::pallet::Event<T>
+   * Lookup239: pallet_common::pallet::Event<T>
    **/
   PalletCommonEvent: {
     _enum: {
@@ -1906,7 +1937,7 @@
     }
   },
   /**
-   * Lookup229: pallet_evm::pallet::Event<T>
+   * Lookup240: pallet_evm::pallet::Event<T>
    **/
   PalletEvmEvent: {
     _enum: {
@@ -1920,7 +1951,7 @@
     }
   },
   /**
-   * Lookup230: ethereum::log::Log
+   * Lookup241: ethereum::log::Log
    **/
   EthereumLog: {
     address: 'H160',
@@ -1928,7 +1959,7 @@
     data: 'Bytes'
   },
   /**
-   * Lookup231: pallet_ethereum::pallet::Event
+   * Lookup242: pallet_ethereum::pallet::Event
    **/
   PalletEthereumEvent: {
     _enum: {
@@ -1936,7 +1967,7 @@
     }
   },
   /**
-   * Lookup232: evm_core::error::ExitReason
+   * Lookup243: evm_core::error::ExitReason
    **/
   EvmCoreErrorExitReason: {
     _enum: {
@@ -1947,13 +1978,13 @@
     }
   },
   /**
-   * Lookup233: evm_core::error::ExitSucceed
+   * Lookup244: evm_core::error::ExitSucceed
    **/
   EvmCoreErrorExitSucceed: {
     _enum: ['Stopped', 'Returned', 'Suicided']
   },
   /**
-   * Lookup234: evm_core::error::ExitError
+   * Lookup245: evm_core::error::ExitError
    **/
   EvmCoreErrorExitError: {
     _enum: {
@@ -1975,13 +2006,13 @@
     }
   },
   /**
-   * Lookup237: evm_core::error::ExitRevert
+   * Lookup248: evm_core::error::ExitRevert
    **/
   EvmCoreErrorExitRevert: {
     _enum: ['Reverted']
   },
   /**
-   * Lookup238: evm_core::error::ExitFatal
+   * Lookup249: evm_core::error::ExitFatal
    **/
   EvmCoreErrorExitFatal: {
     _enum: {
@@ -1992,7 +2023,7 @@
     }
   },
   /**
-   * Lookup239: frame_system::Phase
+   * Lookup250: frame_system::Phase
    **/
   FrameSystemPhase: {
     _enum: {
@@ -2002,14 +2033,14 @@
     }
   },
   /**
-   * Lookup241: frame_system::LastRuntimeUpgradeInfo
+   * Lookup252: frame_system::LastRuntimeUpgradeInfo
    **/
   FrameSystemLastRuntimeUpgradeInfo: {
     specVersion: 'Compact<u32>',
     specName: 'Text'
   },
   /**
-   * Lookup242: frame_system::limits::BlockWeights
+   * Lookup253: frame_system::limits::BlockWeights
    **/
   FrameSystemLimitsBlockWeights: {
     baseBlock: 'u64',
@@ -2017,7 +2048,7 @@
     perClass: 'FrameSupportWeightsPerDispatchClassWeightsPerClass'
   },
   /**
-   * Lookup243: frame_support::weights::PerDispatchClass<frame_system::limits::WeightsPerClass>
+   * Lookup254: frame_support::weights::PerDispatchClass<frame_system::limits::WeightsPerClass>
    **/
   FrameSupportWeightsPerDispatchClassWeightsPerClass: {
     normal: 'FrameSystemLimitsWeightsPerClass',
@@ -2025,7 +2056,7 @@
     mandatory: 'FrameSystemLimitsWeightsPerClass'
   },
   /**
-   * Lookup244: frame_system::limits::WeightsPerClass
+   * Lookup255: frame_system::limits::WeightsPerClass
    **/
   FrameSystemLimitsWeightsPerClass: {
     baseExtrinsic: 'u64',
@@ -2034,13 +2065,13 @@
     reserved: 'Option<u64>'
   },
   /**
-   * Lookup246: frame_system::limits::BlockLength
+   * Lookup257: frame_system::limits::BlockLength
    **/
   FrameSystemLimitsBlockLength: {
     max: 'FrameSupportWeightsPerDispatchClassU32'
   },
   /**
-   * Lookup247: frame_support::weights::PerDispatchClass<T>
+   * Lookup258: frame_support::weights::PerDispatchClass<T>
    **/
   FrameSupportWeightsPerDispatchClassU32: {
     normal: 'u32',
@@ -2048,14 +2079,14 @@
     mandatory: 'u32'
   },
   /**
-   * Lookup248: frame_support::weights::RuntimeDbWeight
+   * Lookup259: frame_support::weights::RuntimeDbWeight
    **/
   FrameSupportWeightsRuntimeDbWeight: {
     read: 'u64',
     write: 'u64'
   },
   /**
-   * Lookup249: sp_version::RuntimeVersion
+   * Lookup260: sp_version::RuntimeVersion
    **/
   SpVersionRuntimeVersion: {
     specName: 'Text',
@@ -2068,19 +2099,19 @@
     stateVersion: 'u8'
   },
   /**
-   * Lookup253: frame_system::pallet::Error<T>
+   * Lookup264: frame_system::pallet::Error<T>
    **/
   FrameSystemError: {
     _enum: ['InvalidSpecName', 'SpecVersionNeedsToIncrease', 'FailedToExtractRuntimeVersion', 'NonDefaultComposite', 'NonZeroRefCount', 'CallFiltered']
   },
   /**
-   * Lookup255: orml_vesting::module::Error<T>
+   * Lookup266: orml_vesting::module::Error<T>
    **/
   OrmlVestingModuleError: {
     _enum: ['ZeroVestingPeriod', 'ZeroVestingPeriodCount', 'InsufficientBalanceToLock', 'TooManyVestingSchedules', 'AmountLow', 'MaxVestingSchedulesExceeded']
   },
   /**
-   * Lookup257: cumulus_pallet_xcmp_queue::InboundChannelDetails
+   * Lookup268: cumulus_pallet_xcmp_queue::InboundChannelDetails
    **/
   CumulusPalletXcmpQueueInboundChannelDetails: {
     sender: 'u32',
@@ -2088,19 +2119,19 @@
     messageMetadata: 'Vec<(u32,PolkadotParachainPrimitivesXcmpMessageFormat)>'
   },
   /**
-   * Lookup258: cumulus_pallet_xcmp_queue::InboundState
+   * Lookup269: cumulus_pallet_xcmp_queue::InboundState
    **/
   CumulusPalletXcmpQueueInboundState: {
     _enum: ['Ok', 'Suspended']
   },
   /**
-   * Lookup261: polkadot_parachain::primitives::XcmpMessageFormat
+   * Lookup272: polkadot_parachain::primitives::XcmpMessageFormat
    **/
   PolkadotParachainPrimitivesXcmpMessageFormat: {
     _enum: ['ConcatenatedVersionedXcm', 'ConcatenatedEncodedBlob', 'Signals']
   },
   /**
-   * Lookup264: cumulus_pallet_xcmp_queue::OutboundChannelDetails
+   * Lookup275: cumulus_pallet_xcmp_queue::OutboundChannelDetails
    **/
   CumulusPalletXcmpQueueOutboundChannelDetails: {
     recipient: 'u32',
@@ -2110,13 +2141,13 @@
     lastIndex: 'u16'
   },
   /**
-   * Lookup265: cumulus_pallet_xcmp_queue::OutboundState
+   * Lookup276: cumulus_pallet_xcmp_queue::OutboundState
    **/
   CumulusPalletXcmpQueueOutboundState: {
     _enum: ['Ok', 'Suspended']
   },
   /**
-   * Lookup267: cumulus_pallet_xcmp_queue::QueueConfigData
+   * Lookup278: cumulus_pallet_xcmp_queue::QueueConfigData
    **/
   CumulusPalletXcmpQueueQueueConfigData: {
     suspendThreshold: 'u32',
@@ -2127,29 +2158,29 @@
     xcmpMaxIndividualWeight: 'u64'
   },
   /**
-   * Lookup269: cumulus_pallet_xcmp_queue::pallet::Error<T>
+   * Lookup280: cumulus_pallet_xcmp_queue::pallet::Error<T>
    **/
   CumulusPalletXcmpQueueError: {
     _enum: ['FailedToSend', 'BadXcmOrigin', 'BadXcm', 'BadOverweightIndex', 'WeightOverLimit']
   },
   /**
-   * Lookup270: pallet_xcm::pallet::Error<T>
+   * Lookup281: pallet_xcm::pallet::Error<T>
    **/
   PalletXcmError: {
     _enum: ['Unreachable', 'SendFailure', 'Filtered', 'UnweighableMessage', 'DestinationNotInvertible', 'Empty', 'CannotReanchor', 'TooManyAssets', 'InvalidOrigin', 'BadVersion', 'BadLocation', 'NoSubscription', 'AlreadySubscribed']
   },
   /**
-   * Lookup271: cumulus_pallet_xcm::pallet::Error<T>
+   * Lookup282: cumulus_pallet_xcm::pallet::Error<T>
    **/
   CumulusPalletXcmError: 'Null',
   /**
-   * Lookup272: cumulus_pallet_dmp_queue::ConfigData
+   * Lookup283: cumulus_pallet_dmp_queue::ConfigData
    **/
   CumulusPalletDmpQueueConfigData: {
     maxIndividual: 'u64'
   },
   /**
-   * Lookup273: cumulus_pallet_dmp_queue::PageIndexData
+   * Lookup284: cumulus_pallet_dmp_queue::PageIndexData
    **/
   CumulusPalletDmpQueuePageIndexData: {
     beginUsed: 'u32',
@@ -2157,19 +2188,19 @@
     overweightCount: 'u64'
   },
   /**
-   * Lookup276: cumulus_pallet_dmp_queue::pallet::Error<T>
+   * Lookup287: cumulus_pallet_dmp_queue::pallet::Error<T>
    **/
   CumulusPalletDmpQueueError: {
     _enum: ['Unknown', 'OverLimit']
   },
   /**
-   * Lookup280: pallet_unique::Error<T>
+   * Lookup291: pallet_unique::Error<T>
    **/
   PalletUniqueError: {
     _enum: ['CollectionDecimalPointLimitExceeded', 'ConfirmUnsetSponsorFail', 'EmptyArgument']
   },
   /**
-   * Lookup281: up_data_structs::Collection<sp_core::crypto::AccountId32>
+   * Lookup292: up_data_structs::Collection<sp_core::crypto::AccountId32>
    **/
   UpDataStructsCollection: {
     owner: 'AccountId32',
@@ -2188,7 +2219,7 @@
     metaUpdatePermission: 'UpDataStructsMetaUpdatePermission'
   },
   /**
-   * Lookup282: up_data_structs::SponsorshipState<sp_core::crypto::AccountId32>
+   * Lookup293: up_data_structs::SponsorshipState<sp_core::crypto::AccountId32>
    **/
   UpDataStructsSponsorshipState: {
     _enum: {
@@ -2198,7 +2229,7 @@
     }
   },
   /**
-   * Lookup285: up_data_structs::CollectionStats
+   * Lookup296: up_data_structs::CollectionStats
    **/
   UpDataStructsCollectionStats: {
     created: 'u32',
@@ -2206,32 +2237,32 @@
     alive: 'u32'
   },
   /**
-   * Lookup286: pallet_common::pallet::Error<T>
+   * Lookup297: pallet_common::pallet::Error<T>
    **/
   PalletCommonError: {
     _enum: ['CollectionNotFound', 'MustBeTokenOwner', 'NoPermission', 'PublicMintingNotAllowed', 'AddressNotInAllowlist', 'CollectionNameLimitExceeded', 'CollectionDescriptionLimitExceeded', 'CollectionTokenPrefixLimitExceeded', 'TotalCollectionsLimitExceeded', 'TokenVariableDataLimitExceeded', 'CollectionAdminCountExceeded', 'CollectionLimitBoundsExceeded', 'OwnerPermissionsCantBeReverted', 'TransferNotAllowed', 'AccountTokenLimitExceeded', 'CollectionTokenLimitExceeded', 'MetadataFlagFrozen', 'TokenNotFound', 'TokenValueTooLow', 'ApprovedValueTooLow', 'CantApproveMoreThanOwned', 'AddressIsZero', 'UnsupportedOperation']
   },
   /**
-   * Lookup288: pallet_fungible::pallet::Error<T>
+   * Lookup299: pallet_fungible::pallet::Error<T>
    **/
   PalletFungibleError: {
     _enum: ['NotFungibleDataUsedToMintFungibleCollectionToken', 'FungibleItemsHaveNoId', 'FungibleItemsDontHaveData']
   },
   /**
-   * Lookup289: pallet_refungible::ItemData
+   * Lookup300: pallet_refungible::ItemData
    **/
   PalletRefungibleItemData: {
     constData: 'Bytes',
     variableData: 'Bytes'
   },
   /**
-   * Lookup293: pallet_refungible::pallet::Error<T>
+   * Lookup304: pallet_refungible::pallet::Error<T>
    **/
   PalletRefungibleError: {
     _enum: ['NotRefungibleDataUsedToMintFungibleCollectionToken', 'WrongRefungiblePieces']
   },
   /**
-   * Lookup294: pallet_nonfungible::ItemData<pallet_common::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
+   * Lookup305: pallet_nonfungible::ItemData<pallet_common::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
    **/
   PalletNonfungibleItemData: {
     constData: 'Bytes',
@@ -2239,19 +2270,19 @@
     owner: 'PalletCommonAccountBasicCrossAccountIdRepr'
   },
   /**
-   * Lookup295: pallet_nonfungible::pallet::Error<T>
+   * Lookup306: pallet_nonfungible::pallet::Error<T>
    **/
   PalletNonfungibleError: {
     _enum: ['NotNonfungibleDataUsedToMintFungibleCollectionToken', 'NonfungibleItemsHaveNoAmount']
   },
   /**
-   * Lookup297: pallet_evm::pallet::Error<T>
+   * Lookup308: pallet_evm::pallet::Error<T>
    **/
   PalletEvmError: {
     _enum: ['BalanceLow', 'FeeOverflow', 'PaymentOverflow', 'WithdrawFailed', 'GasPriceTooLow', 'InvalidNonce']
   },
   /**
-   * Lookup300: fp_rpc::TransactionStatus
+   * Lookup311: fp_rpc::TransactionStatus
    **/
   FpRpcTransactionStatus: {
     transactionHash: 'H256',
@@ -2263,11 +2294,11 @@
     logsBloom: 'EthbloomBloom'
   },
   /**
-   * Lookup303: ethbloom::Bloom
+   * Lookup314: ethbloom::Bloom
    **/
   EthbloomBloom: '[u8;256]',
   /**
-   * Lookup305: ethereum::receipt::ReceiptV3
+   * Lookup316: ethereum::receipt::ReceiptV3
    **/
   EthereumReceiptReceiptV3: {
     _enum: {
@@ -2277,7 +2308,7 @@
     }
   },
   /**
-   * Lookup306: ethereum::receipt::EIP658ReceiptData
+   * Lookup317: ethereum::receipt::EIP658ReceiptData
    **/
   EthereumReceiptEip658ReceiptData: {
     statusCode: 'u8',
@@ -2286,7 +2317,7 @@
     logs: 'Vec<EthereumLog>'
   },
   /**
-   * Lookup307: ethereum::block::Block<ethereum::transaction::TransactionV2>
+   * Lookup318: ethereum::block::Block<ethereum::transaction::TransactionV2>
    **/
   EthereumBlock: {
     header: 'EthereumHeader',
@@ -2294,7 +2325,7 @@
     ommers: 'Vec<EthereumHeader>'
   },
   /**
-   * Lookup308: ethereum::header::Header
+   * Lookup319: ethereum::header::Header
    **/
   EthereumHeader: {
     parentHash: 'H256',
@@ -2314,41 +2345,41 @@
     nonce: 'EthereumTypesHashH64'
   },
   /**
-   * Lookup309: ethereum_types::hash::H64
+   * Lookup320: ethereum_types::hash::H64
    **/
   EthereumTypesHashH64: '[u8;8]',
   /**
-   * Lookup314: pallet_ethereum::pallet::Error<T>
+   * Lookup325: pallet_ethereum::pallet::Error<T>
    **/
   PalletEthereumError: {
     _enum: ['InvalidSignature', 'PreLogExists']
   },
   /**
-   * Lookup315: pallet_evm_coder_substrate::pallet::Error<T>
+   * Lookup326: pallet_evm_coder_substrate::pallet::Error<T>
    **/
   PalletEvmCoderSubstrateError: {
     _enum: ['OutOfGas', 'OutOfFund']
   },
   /**
-   * Lookup316: pallet_evm_contract_helpers::SponsoringModeT
+   * Lookup327: pallet_evm_contract_helpers::SponsoringModeT
    **/
   PalletEvmContractHelpersSponsoringModeT: {
     _enum: ['Disabled', 'Allowlisted', 'Generous']
   },
   /**
-   * Lookup318: pallet_evm_contract_helpers::pallet::Error<T>
+   * Lookup329: pallet_evm_contract_helpers::pallet::Error<T>
    **/
   PalletEvmContractHelpersError: {
     _enum: ['NoPermission']
   },
   /**
-   * Lookup319: pallet_evm_migration::pallet::Error<T>
+   * Lookup330: pallet_evm_migration::pallet::Error<T>
    **/
   PalletEvmMigrationError: {
     _enum: ['AccountNotEmpty', 'AccountIsNotMigrating']
   },
   /**
-   * Lookup321: sp_runtime::MultiSignature
+   * Lookup332: sp_runtime::MultiSignature
    **/
   SpRuntimeMultiSignature: {
     _enum: {
@@ -2358,39 +2389,39 @@
     }
   },
   /**
-   * Lookup322: sp_core::ed25519::Signature
+   * Lookup333: sp_core::ed25519::Signature
    **/
   SpCoreEd25519Signature: '[u8;64]',
   /**
-   * Lookup324: sp_core::sr25519::Signature
+   * Lookup335: sp_core::sr25519::Signature
    **/
   SpCoreSr25519Signature: '[u8;64]',
   /**
-   * Lookup325: sp_core::ecdsa::Signature
+   * Lookup336: sp_core::ecdsa::Signature
    **/
   SpCoreEcdsaSignature: '[u8;65]',
   /**
-   * Lookup328: frame_system::extensions::check_spec_version::CheckSpecVersion<T>
+   * Lookup339: frame_system::extensions::check_spec_version::CheckSpecVersion<T>
    **/
   FrameSystemExtensionsCheckSpecVersion: 'Null',
   /**
-   * Lookup329: frame_system::extensions::check_genesis::CheckGenesis<T>
+   * Lookup340: frame_system::extensions::check_genesis::CheckGenesis<T>
    **/
   FrameSystemExtensionsCheckGenesis: 'Null',
   /**
-   * Lookup332: frame_system::extensions::check_nonce::CheckNonce<T>
+   * Lookup343: frame_system::extensions::check_nonce::CheckNonce<T>
    **/
   FrameSystemExtensionsCheckNonce: 'Compact<u32>',
   /**
-   * Lookup333: frame_system::extensions::check_weight::CheckWeight<T>
+   * Lookup344: frame_system::extensions::check_weight::CheckWeight<T>
    **/
   FrameSystemExtensionsCheckWeight: 'Null',
   /**
-   * Lookup334: pallet_template_transaction_payment::ChargeTransactionPayment<unique_runtime::Runtime>
+   * Lookup345: pallet_template_transaction_payment::ChargeTransactionPayment<unique_runtime::Runtime>
    **/
   PalletTemplateTransactionPaymentChargeTransactionPayment: 'Compact<u128>',
   /**
-   * Lookup335: unique_runtime::Runtime
+   * Lookup346: unique_runtime::Runtime
    **/
   UniqueRuntimeRuntime: 'Null'
 };
modifiedtests/src/interfaces/types-lookup.tsdiffbeforeafterboth
--- a/tests/src/interfaces/types-lookup.ts
+++ b/tests/src/interfaces/types-lookup.ts
@@ -1421,6 +1421,11 @@
       readonly owner: PalletCommonAccountBasicCrossAccountIdRepr;
       readonly itemsData: Vec<UpDataStructsCreateItemData>;
     } & Struct;
+    readonly isCreateMultipleItemsEx: boolean;
+    readonly asCreateMultipleItemsEx: {
+      readonly collectionId: u32;
+      readonly data: UpDataStructsCreateItemExData;
+    } & Struct;
     readonly isSetTransfersEnabledFlag: boolean;
     readonly asSetTransfersEnabledFlag: {
       readonly collectionId: u32;
@@ -1497,7 +1502,7 @@
       readonly collectionId: u32;
       readonly newLimit: UpDataStructsCollectionLimits;
     } & Struct;
-    readonly type: 'CreateCollection' | 'CreateCollectionEx' | 'DestroyCollection' | 'AddToAllowList' | 'RemoveFromAllowList' | 'SetPublicAccessMode' | 'SetMintPermission' | 'ChangeCollectionOwner' | 'AddCollectionAdmin' | 'RemoveCollectionAdmin' | 'SetCollectionSponsor' | 'ConfirmSponsorship' | 'RemoveCollectionSponsor' | 'CreateItem' | 'CreateMultipleItems' | 'SetTransfersEnabledFlag' | 'BurnItem' | 'BurnFrom' | 'Transfer' | 'Approve' | 'TransferFrom' | 'SetVariableMetaData' | 'SetMetaUpdatePermissionFlag' | 'SetSchemaVersion' | 'SetOffchainSchema' | 'SetConstOnChainSchema' | 'SetVariableOnChainSchema' | 'SetCollectionLimits';
+    readonly type: 'CreateCollection' | 'CreateCollectionEx' | 'DestroyCollection' | 'AddToAllowList' | 'RemoveFromAllowList' | 'SetPublicAccessMode' | 'SetMintPermission' | 'ChangeCollectionOwner' | 'AddCollectionAdmin' | 'RemoveCollectionAdmin' | 'SetCollectionSponsor' | 'ConfirmSponsorship' | 'RemoveCollectionSponsor' | 'CreateItem' | 'CreateMultipleItems' | 'CreateMultipleItemsEx' | 'SetTransfersEnabledFlag' | 'BurnItem' | 'BurnFrom' | 'Transfer' | 'Approve' | 'TransferFrom' | 'SetVariableMetaData' | 'SetMetaUpdatePermissionFlag' | 'SetSchemaVersion' | 'SetOffchainSchema' | 'SetConstOnChainSchema' | 'SetVariableOnChainSchema' | 'SetCollectionLimits';
   }
 
   /** @name UpDataStructsCollectionMode (155) */
@@ -1606,10 +1611,37 @@
     readonly pieces: u128;
   }
 
-  /** @name PalletTemplateTransactionPaymentCall (181) */
+  /** @name UpDataStructsCreateItemExData (180) */
+  export interface UpDataStructsCreateItemExData extends Enum {
+    readonly isNft: boolean;
+    readonly asNft: Vec<UpDataStructsCreateNftExData>;
+    readonly isFungible: boolean;
+    readonly asFungible: BTreeMap<PalletCommonAccountBasicCrossAccountIdRepr, u128>;
+    readonly isRefungibleMultipleItems: boolean;
+    readonly asRefungibleMultipleItems: Vec<UpDataStructsCreateRefungibleExData>;
+    readonly isRefungibleMultipleOwners: boolean;
+    readonly asRefungibleMultipleOwners: UpDataStructsCreateRefungibleExData;
+    readonly type: 'Nft' | 'Fungible' | 'RefungibleMultipleItems' | 'RefungibleMultipleOwners';
+  }
+
+  /** @name UpDataStructsCreateNftExData (182) */
+  export interface UpDataStructsCreateNftExData extends Struct {
+    readonly constData: Bytes;
+    readonly variableData: Bytes;
+    readonly owner: PalletCommonAccountBasicCrossAccountIdRepr;
+  }
+
+  /** @name UpDataStructsCreateRefungibleExData (189) */
+  export interface UpDataStructsCreateRefungibleExData extends Struct {
+    readonly constData: Bytes;
+    readonly variableData: Bytes;
+    readonly users: BTreeMap<PalletCommonAccountBasicCrossAccountIdRepr, u128>;
+  }
+
+  /** @name PalletTemplateTransactionPaymentCall (192) */
   export type PalletTemplateTransactionPaymentCall = Null;
 
-  /** @name PalletEvmCall (182) */
+  /** @name PalletEvmCall (193) */
   export interface PalletEvmCall extends Enum {
     readonly isWithdraw: boolean;
     readonly asWithdraw: {
@@ -1654,7 +1686,7 @@
     readonly type: 'Withdraw' | 'Call' | 'Create' | 'Create2';
   }
 
-  /** @name PalletEthereumCall (188) */
+  /** @name PalletEthereumCall (199) */
   export interface PalletEthereumCall extends Enum {
     readonly isTransact: boolean;
     readonly asTransact: {
@@ -1663,7 +1695,7 @@
     readonly type: 'Transact';
   }
 
-  /** @name EthereumTransactionTransactionV2 (189) */
+  /** @name EthereumTransactionTransactionV2 (200) */
   export interface EthereumTransactionTransactionV2 extends Enum {
     readonly isLegacy: boolean;
     readonly asLegacy: EthereumTransactionLegacyTransaction;
@@ -1674,7 +1706,7 @@
     readonly type: 'Legacy' | 'Eip2930' | 'Eip1559';
   }
 
-  /** @name EthereumTransactionLegacyTransaction (190) */
+  /** @name EthereumTransactionLegacyTransaction (201) */
   export interface EthereumTransactionLegacyTransaction extends Struct {
     readonly nonce: U256;
     readonly gasPrice: U256;
@@ -1685,7 +1717,7 @@
     readonly signature: EthereumTransactionTransactionSignature;
   }
 
-  /** @name EthereumTransactionTransactionAction (191) */
+  /** @name EthereumTransactionTransactionAction (202) */
   export interface EthereumTransactionTransactionAction extends Enum {
     readonly isCall: boolean;
     readonly asCall: H160;
@@ -1693,14 +1725,14 @@
     readonly type: 'Call' | 'Create';
   }
 
-  /** @name EthereumTransactionTransactionSignature (192) */
+  /** @name EthereumTransactionTransactionSignature (203) */
   export interface EthereumTransactionTransactionSignature extends Struct {
     readonly v: u64;
     readonly r: H256;
     readonly s: H256;
   }
 
-  /** @name EthereumTransactionEip2930Transaction (194) */
+  /** @name EthereumTransactionEip2930Transaction (205) */
   export interface EthereumTransactionEip2930Transaction extends Struct {
     readonly chainId: u64;
     readonly nonce: U256;
@@ -1715,13 +1747,13 @@
     readonly s: H256;
   }
 
-  /** @name EthereumTransactionAccessListItem (196) */
+  /** @name EthereumTransactionAccessListItem (207) */
   export interface EthereumTransactionAccessListItem extends Struct {
     readonly address: H160;
     readonly slots: Vec<H256>;
   }
 
-  /** @name EthereumTransactionEip1559Transaction (197) */
+  /** @name EthereumTransactionEip1559Transaction (208) */
   export interface EthereumTransactionEip1559Transaction extends Struct {
     readonly chainId: u64;
     readonly nonce: U256;
@@ -1737,7 +1769,7 @@
     readonly s: H256;
   }
 
-  /** @name PalletEvmMigrationCall (198) */
+  /** @name PalletEvmMigrationCall (209) */
   export interface PalletEvmMigrationCall extends Enum {
     readonly isBegin: boolean;
     readonly asBegin: {
@@ -1756,7 +1788,7 @@
     readonly type: 'Begin' | 'SetData' | 'Finish';
   }
 
-  /** @name PalletSudoEvent (201) */
+  /** @name PalletSudoEvent (212) */
   export interface PalletSudoEvent extends Enum {
     readonly isSudid: boolean;
     readonly asSudid: {
@@ -1773,7 +1805,7 @@
     readonly type: 'Sudid' | 'KeyChanged' | 'SudoAsDone';
   }
 
-  /** @name SpRuntimeDispatchError (203) */
+  /** @name SpRuntimeDispatchError (214) */
   export interface SpRuntimeDispatchError extends Enum {
     readonly isOther: boolean;
     readonly isCannotLookup: boolean;
@@ -1790,13 +1822,13 @@
     readonly type: 'Other' | 'CannotLookup' | 'BadOrigin' | 'Module' | 'ConsumerRemaining' | 'NoProviders' | 'TooManyConsumers' | 'Token' | 'Arithmetic';
   }
 
-  /** @name SpRuntimeModuleError (204) */
+  /** @name SpRuntimeModuleError (215) */
   export interface SpRuntimeModuleError extends Struct {
     readonly index: u8;
     readonly error: u8;
   }
 
-  /** @name SpRuntimeTokenError (205) */
+  /** @name SpRuntimeTokenError (216) */
   export interface SpRuntimeTokenError extends Enum {
     readonly isNoFunds: boolean;
     readonly isWouldDie: boolean;
@@ -1808,7 +1840,7 @@
     readonly type: 'NoFunds' | 'WouldDie' | 'BelowMinimum' | 'CannotCreate' | 'UnknownAsset' | 'Frozen' | 'Unsupported';
   }
 
-  /** @name SpRuntimeArithmeticError (206) */
+  /** @name SpRuntimeArithmeticError (217) */
   export interface SpRuntimeArithmeticError extends Enum {
     readonly isUnderflow: boolean;
     readonly isOverflow: boolean;
@@ -1816,13 +1848,13 @@
     readonly type: 'Underflow' | 'Overflow' | 'DivisionByZero';
   }
 
-  /** @name PalletSudoError (207) */
+  /** @name PalletSudoError (218) */
   export interface PalletSudoError extends Enum {
     readonly isRequireSudo: boolean;
     readonly type: 'RequireSudo';
   }
 
-  /** @name FrameSystemAccountInfo (208) */
+  /** @name FrameSystemAccountInfo (219) */
   export interface FrameSystemAccountInfo extends Struct {
     readonly nonce: u32;
     readonly consumers: u32;
@@ -1831,19 +1863,19 @@
     readonly data: PalletBalancesAccountData;
   }
 
-  /** @name FrameSupportWeightsPerDispatchClassU64 (209) */
+  /** @name FrameSupportWeightsPerDispatchClassU64 (220) */
   export interface FrameSupportWeightsPerDispatchClassU64 extends Struct {
     readonly normal: u64;
     readonly operational: u64;
     readonly mandatory: u64;
   }
 
-  /** @name SpRuntimeDigest (210) */
+  /** @name SpRuntimeDigest (221) */
   export interface SpRuntimeDigest extends Struct {
     readonly logs: Vec<SpRuntimeDigestDigestItem>;
   }
 
-  /** @name SpRuntimeDigestDigestItem (212) */
+  /** @name SpRuntimeDigestDigestItem (223) */
   export interface SpRuntimeDigestDigestItem extends Enum {
     readonly isOther: boolean;
     readonly asOther: Bytes;
@@ -1857,14 +1889,14 @@
     readonly type: 'Other' | 'Consensus' | 'Seal' | 'PreRuntime' | 'RuntimeEnvironmentUpdated';
   }
 
-  /** @name FrameSystemEventRecord (214) */
+  /** @name FrameSystemEventRecord (225) */
   export interface FrameSystemEventRecord extends Struct {
     readonly phase: FrameSystemPhase;
     readonly event: Event;
     readonly topics: Vec<H256>;
   }
 
-  /** @name FrameSystemEvent (216) */
+  /** @name FrameSystemEvent (227) */
   export interface FrameSystemEvent extends Enum {
     readonly isExtrinsicSuccess: boolean;
     readonly asExtrinsicSuccess: {
@@ -1892,14 +1924,14 @@
     readonly type: 'ExtrinsicSuccess' | 'ExtrinsicFailed' | 'CodeUpdated' | 'NewAccount' | 'KilledAccount' | 'Remarked';
   }
 
-  /** @name FrameSupportWeightsDispatchInfo (217) */
+  /** @name FrameSupportWeightsDispatchInfo (228) */
   export interface FrameSupportWeightsDispatchInfo extends Struct {
     readonly weight: u64;
     readonly class: FrameSupportWeightsDispatchClass;
     readonly paysFee: FrameSupportWeightsPays;
   }
 
-  /** @name FrameSupportWeightsDispatchClass (218) */
+  /** @name FrameSupportWeightsDispatchClass (229) */
   export interface FrameSupportWeightsDispatchClass extends Enum {
     readonly isNormal: boolean;
     readonly isOperational: boolean;
@@ -1907,14 +1939,14 @@
     readonly type: 'Normal' | 'Operational' | 'Mandatory';
   }
 
-  /** @name FrameSupportWeightsPays (219) */
+  /** @name FrameSupportWeightsPays (230) */
   export interface FrameSupportWeightsPays extends Enum {
     readonly isYes: boolean;
     readonly isNo: boolean;
     readonly type: 'Yes' | 'No';
   }
 
-  /** @name OrmlVestingModuleEvent (220) */
+  /** @name OrmlVestingModuleEvent (231) */
   export interface OrmlVestingModuleEvent extends Enum {
     readonly isVestingScheduleAdded: boolean;
     readonly asVestingScheduleAdded: {
@@ -1934,7 +1966,7 @@
     readonly type: 'VestingScheduleAdded' | 'Claimed' | 'VestingSchedulesUpdated';
   }
 
-  /** @name CumulusPalletXcmpQueueEvent (221) */
+  /** @name CumulusPalletXcmpQueueEvent (232) */
   export interface CumulusPalletXcmpQueueEvent extends Enum {
     readonly isSuccess: boolean;
     readonly asSuccess: Option<H256>;
@@ -1955,7 +1987,7 @@
     readonly type: 'Success' | 'Fail' | 'BadVersion' | 'BadFormat' | 'UpwardMessageSent' | 'XcmpMessageSent' | 'OverweightEnqueued' | 'OverweightServiced';
   }
 
-  /** @name PalletXcmEvent (222) */
+  /** @name PalletXcmEvent (233) */
   export interface PalletXcmEvent extends Enum {
     readonly isAttempted: boolean;
     readonly asAttempted: XcmV2TraitsOutcome;
@@ -1992,7 +2024,7 @@
     readonly type: 'Attempted' | 'Sent' | 'UnexpectedResponse' | 'ResponseReady' | 'Notified' | 'NotifyOverweight' | 'NotifyDispatchError' | 'NotifyDecodeFailed' | 'InvalidResponder' | 'InvalidResponderVersion' | 'ResponseTaken' | 'AssetsTrapped' | 'VersionChangeNotified' | 'SupportedVersionChanged' | 'NotifyTargetSendFail' | 'NotifyTargetMigrationFail';
   }
 
-  /** @name XcmV2TraitsOutcome (223) */
+  /** @name XcmV2TraitsOutcome (234) */
   export interface XcmV2TraitsOutcome extends Enum {
     readonly isComplete: boolean;
     readonly asComplete: u64;
@@ -2003,7 +2035,7 @@
     readonly type: 'Complete' | 'Incomplete' | 'Error';
   }
 
-  /** @name CumulusPalletXcmEvent (225) */
+  /** @name CumulusPalletXcmEvent (236) */
   export interface CumulusPalletXcmEvent extends Enum {
     readonly isInvalidFormat: boolean;
     readonly asInvalidFormat: U8aFixed;
@@ -2014,7 +2046,7 @@
     readonly type: 'InvalidFormat' | 'UnsupportedVersion' | 'ExecutedDownward';
   }
 
-  /** @name CumulusPalletDmpQueueEvent (226) */
+  /** @name CumulusPalletDmpQueueEvent (237) */
   export interface CumulusPalletDmpQueueEvent extends Enum {
     readonly isInvalidFormat: boolean;
     readonly asInvalidFormat: U8aFixed;
@@ -2031,7 +2063,7 @@
     readonly type: 'InvalidFormat' | 'UnsupportedVersion' | 'ExecutedDownward' | 'WeightExhausted' | 'OverweightEnqueued' | 'OverweightServiced';
   }
 
-  /** @name PalletUniqueRawEvent (227) */
+  /** @name PalletUniqueRawEvent (238) */
   export interface PalletUniqueRawEvent extends Enum {
     readonly isCollectionSponsorRemoved: boolean;
     readonly asCollectionSponsorRemoved: u32;
@@ -2066,7 +2098,7 @@
     readonly type: 'CollectionSponsorRemoved' | 'CollectionAdminAdded' | 'CollectionOwnedChanged' | 'CollectionSponsorSet' | 'ConstOnChainSchemaSet' | 'SponsorshipConfirmed' | 'CollectionAdminRemoved' | 'AllowListAddressRemoved' | 'AllowListAddressAdded' | 'CollectionLimitSet' | 'MintPermissionSet' | 'OffchainSchemaSet' | 'PublicAccessModeSet' | 'SchemaVersionSet' | 'VariableOnChainSchemaSet';
   }
 
-  /** @name PalletCommonEvent (228) */
+  /** @name PalletCommonEvent (239) */
   export interface PalletCommonEvent extends Enum {
     readonly isCollectionCreated: boolean;
     readonly asCollectionCreated: ITuple<[u32, u8, AccountId32]>;
@@ -2083,7 +2115,7 @@
     readonly type: 'CollectionCreated' | 'CollectionDestroyed' | 'ItemCreated' | 'ItemDestroyed' | 'Transfer' | 'Approved';
   }
 
-  /** @name PalletEvmEvent (229) */
+  /** @name PalletEvmEvent (240) */
   export interface PalletEvmEvent extends Enum {
     readonly isLog: boolean;
     readonly asLog: EthereumLog;
@@ -2102,21 +2134,21 @@
     readonly type: 'Log' | 'Created' | 'CreatedFailed' | 'Executed' | 'ExecutedFailed' | 'BalanceDeposit' | 'BalanceWithdraw';
   }
 
-  /** @name EthereumLog (230) */
+  /** @name EthereumLog (241) */
   export interface EthereumLog extends Struct {
     readonly address: H160;
     readonly topics: Vec<H256>;
     readonly data: Bytes;
   }
 
-  /** @name PalletEthereumEvent (231) */
+  /** @name PalletEthereumEvent (242) */
   export interface PalletEthereumEvent extends Enum {
     readonly isExecuted: boolean;
     readonly asExecuted: ITuple<[H160, H160, H256, EvmCoreErrorExitReason]>;
     readonly type: 'Executed';
   }
 
-  /** @name EvmCoreErrorExitReason (232) */
+  /** @name EvmCoreErrorExitReason (243) */
   export interface EvmCoreErrorExitReason extends Enum {
     readonly isSucceed: boolean;
     readonly asSucceed: EvmCoreErrorExitSucceed;
@@ -2129,7 +2161,7 @@
     readonly type: 'Succeed' | 'Error' | 'Revert' | 'Fatal';
   }
 
-  /** @name EvmCoreErrorExitSucceed (233) */
+  /** @name EvmCoreErrorExitSucceed (244) */
   export interface EvmCoreErrorExitSucceed extends Enum {
     readonly isStopped: boolean;
     readonly isReturned: boolean;
@@ -2137,7 +2169,7 @@
     readonly type: 'Stopped' | 'Returned' | 'Suicided';
   }
 
-  /** @name EvmCoreErrorExitError (234) */
+  /** @name EvmCoreErrorExitError (245) */
   export interface EvmCoreErrorExitError extends Enum {
     readonly isStackUnderflow: boolean;
     readonly isStackOverflow: boolean;
@@ -2158,13 +2190,13 @@
     readonly type: 'StackUnderflow' | 'StackOverflow' | 'InvalidJump' | 'InvalidRange' | 'DesignatedInvalid' | 'CallTooDeep' | 'CreateCollision' | 'CreateContractLimit' | 'InvalidCode' | 'OutOfOffset' | 'OutOfGas' | 'OutOfFund' | 'PcUnderflow' | 'CreateEmpty' | 'Other';
   }
 
-  /** @name EvmCoreErrorExitRevert (237) */
+  /** @name EvmCoreErrorExitRevert (248) */
   export interface EvmCoreErrorExitRevert extends Enum {
     readonly isReverted: boolean;
     readonly type: 'Reverted';
   }
 
-  /** @name EvmCoreErrorExitFatal (238) */
+  /** @name EvmCoreErrorExitFatal (249) */
   export interface EvmCoreErrorExitFatal extends Enum {
     readonly isNotSupported: boolean;
     readonly isUnhandledInterrupt: boolean;
@@ -2175,7 +2207,7 @@
     readonly type: 'NotSupported' | 'UnhandledInterrupt' | 'CallErrorAsFatal' | 'Other';
   }
 
-  /** @name FrameSystemPhase (239) */
+  /** @name FrameSystemPhase (250) */
   export interface FrameSystemPhase extends Enum {
     readonly isApplyExtrinsic: boolean;
     readonly asApplyExtrinsic: u32;
@@ -2184,27 +2216,27 @@
     readonly type: 'ApplyExtrinsic' | 'Finalization' | 'Initialization';
   }
 
-  /** @name FrameSystemLastRuntimeUpgradeInfo (241) */
+  /** @name FrameSystemLastRuntimeUpgradeInfo (252) */
   export interface FrameSystemLastRuntimeUpgradeInfo extends Struct {
     readonly specVersion: Compact<u32>;
     readonly specName: Text;
   }
 
-  /** @name FrameSystemLimitsBlockWeights (242) */
+  /** @name FrameSystemLimitsBlockWeights (253) */
   export interface FrameSystemLimitsBlockWeights extends Struct {
     readonly baseBlock: u64;
     readonly maxBlock: u64;
     readonly perClass: FrameSupportWeightsPerDispatchClassWeightsPerClass;
   }
 
-  /** @name FrameSupportWeightsPerDispatchClassWeightsPerClass (243) */
+  /** @name FrameSupportWeightsPerDispatchClassWeightsPerClass (254) */
   export interface FrameSupportWeightsPerDispatchClassWeightsPerClass extends Struct {
     readonly normal: FrameSystemLimitsWeightsPerClass;
     readonly operational: FrameSystemLimitsWeightsPerClass;
     readonly mandatory: FrameSystemLimitsWeightsPerClass;
   }
 
-  /** @name FrameSystemLimitsWeightsPerClass (244) */
+  /** @name FrameSystemLimitsWeightsPerClass (255) */
   export interface FrameSystemLimitsWeightsPerClass extends Struct {
     readonly baseExtrinsic: u64;
     readonly maxExtrinsic: Option<u64>;
@@ -2212,25 +2244,25 @@
     readonly reserved: Option<u64>;
   }
 
-  /** @name FrameSystemLimitsBlockLength (246) */
+  /** @name FrameSystemLimitsBlockLength (257) */
   export interface FrameSystemLimitsBlockLength extends Struct {
     readonly max: FrameSupportWeightsPerDispatchClassU32;
   }
 
-  /** @name FrameSupportWeightsPerDispatchClassU32 (247) */
+  /** @name FrameSupportWeightsPerDispatchClassU32 (258) */
   export interface FrameSupportWeightsPerDispatchClassU32 extends Struct {
     readonly normal: u32;
     readonly operational: u32;
     readonly mandatory: u32;
   }
 
-  /** @name FrameSupportWeightsRuntimeDbWeight (248) */
+  /** @name FrameSupportWeightsRuntimeDbWeight (259) */
   export interface FrameSupportWeightsRuntimeDbWeight extends Struct {
     readonly read: u64;
     readonly write: u64;
   }
 
-  /** @name SpVersionRuntimeVersion (249) */
+  /** @name SpVersionRuntimeVersion (260) */
   export interface SpVersionRuntimeVersion extends Struct {
     readonly specName: Text;
     readonly implName: Text;
@@ -2242,7 +2274,7 @@
     readonly stateVersion: u8;
   }
 
-  /** @name FrameSystemError (253) */
+  /** @name FrameSystemError (264) */
   export interface FrameSystemError extends Enum {
     readonly isInvalidSpecName: boolean;
     readonly isSpecVersionNeedsToIncrease: boolean;
@@ -2253,7 +2285,7 @@
     readonly type: 'InvalidSpecName' | 'SpecVersionNeedsToIncrease' | 'FailedToExtractRuntimeVersion' | 'NonDefaultComposite' | 'NonZeroRefCount' | 'CallFiltered';
   }
 
-  /** @name OrmlVestingModuleError (255) */
+  /** @name OrmlVestingModuleError (266) */
   export interface OrmlVestingModuleError extends Enum {
     readonly isZeroVestingPeriod: boolean;
     readonly isZeroVestingPeriodCount: boolean;
@@ -2264,21 +2296,21 @@
     readonly type: 'ZeroVestingPeriod' | 'ZeroVestingPeriodCount' | 'InsufficientBalanceToLock' | 'TooManyVestingSchedules' | 'AmountLow' | 'MaxVestingSchedulesExceeded';
   }
 
-  /** @name CumulusPalletXcmpQueueInboundChannelDetails (257) */
+  /** @name CumulusPalletXcmpQueueInboundChannelDetails (268) */
   export interface CumulusPalletXcmpQueueInboundChannelDetails extends Struct {
     readonly sender: u32;
     readonly state: CumulusPalletXcmpQueueInboundState;
     readonly messageMetadata: Vec<ITuple<[u32, PolkadotParachainPrimitivesXcmpMessageFormat]>>;
   }
 
-  /** @name CumulusPalletXcmpQueueInboundState (258) */
+  /** @name CumulusPalletXcmpQueueInboundState (269) */
   export interface CumulusPalletXcmpQueueInboundState extends Enum {
     readonly isOk: boolean;
     readonly isSuspended: boolean;
     readonly type: 'Ok' | 'Suspended';
   }
 
-  /** @name PolkadotParachainPrimitivesXcmpMessageFormat (261) */
+  /** @name PolkadotParachainPrimitivesXcmpMessageFormat (272) */
   export interface PolkadotParachainPrimitivesXcmpMessageFormat extends Enum {
     readonly isConcatenatedVersionedXcm: boolean;
     readonly isConcatenatedEncodedBlob: boolean;
@@ -2286,7 +2318,7 @@
     readonly type: 'ConcatenatedVersionedXcm' | 'ConcatenatedEncodedBlob' | 'Signals';
   }
 
-  /** @name CumulusPalletXcmpQueueOutboundChannelDetails (264) */
+  /** @name CumulusPalletXcmpQueueOutboundChannelDetails (275) */
   export interface CumulusPalletXcmpQueueOutboundChannelDetails extends Struct {
     readonly recipient: u32;
     readonly state: CumulusPalletXcmpQueueOutboundState;
@@ -2295,14 +2327,14 @@
     readonly lastIndex: u16;
   }
 
-  /** @name CumulusPalletXcmpQueueOutboundState (265) */
+  /** @name CumulusPalletXcmpQueueOutboundState (276) */
   export interface CumulusPalletXcmpQueueOutboundState extends Enum {
     readonly isOk: boolean;
     readonly isSuspended: boolean;
     readonly type: 'Ok' | 'Suspended';
   }
 
-  /** @name CumulusPalletXcmpQueueQueueConfigData (267) */
+  /** @name CumulusPalletXcmpQueueQueueConfigData (278) */
   export interface CumulusPalletXcmpQueueQueueConfigData extends Struct {
     readonly suspendThreshold: u32;
     readonly dropThreshold: u32;
@@ -2312,7 +2344,7 @@
     readonly xcmpMaxIndividualWeight: u64;
   }
 
-  /** @name CumulusPalletXcmpQueueError (269) */
+  /** @name CumulusPalletXcmpQueueError (280) */
   export interface CumulusPalletXcmpQueueError extends Enum {
     readonly isFailedToSend: boolean;
     readonly isBadXcmOrigin: boolean;
@@ -2322,7 +2354,7 @@
     readonly type: 'FailedToSend' | 'BadXcmOrigin' | 'BadXcm' | 'BadOverweightIndex' | 'WeightOverLimit';
   }
 
-  /** @name PalletXcmError (270) */
+  /** @name PalletXcmError (281) */
   export interface PalletXcmError extends Enum {
     readonly isUnreachable: boolean;
     readonly isSendFailure: boolean;
@@ -2340,29 +2372,29 @@
     readonly type: 'Unreachable' | 'SendFailure' | 'Filtered' | 'UnweighableMessage' | 'DestinationNotInvertible' | 'Empty' | 'CannotReanchor' | 'TooManyAssets' | 'InvalidOrigin' | 'BadVersion' | 'BadLocation' | 'NoSubscription' | 'AlreadySubscribed';
   }
 
-  /** @name CumulusPalletXcmError (271) */
+  /** @name CumulusPalletXcmError (282) */
   export type CumulusPalletXcmError = Null;
 
-  /** @name CumulusPalletDmpQueueConfigData (272) */
+  /** @name CumulusPalletDmpQueueConfigData (283) */
   export interface CumulusPalletDmpQueueConfigData extends Struct {
     readonly maxIndividual: u64;
   }
 
-  /** @name CumulusPalletDmpQueuePageIndexData (273) */
+  /** @name CumulusPalletDmpQueuePageIndexData (284) */
   export interface CumulusPalletDmpQueuePageIndexData extends Struct {
     readonly beginUsed: u32;
     readonly endUsed: u32;
     readonly overweightCount: u64;
   }
 
-  /** @name CumulusPalletDmpQueueError (276) */
+  /** @name CumulusPalletDmpQueueError (287) */
   export interface CumulusPalletDmpQueueError extends Enum {
     readonly isUnknown: boolean;
     readonly isOverLimit: boolean;
     readonly type: 'Unknown' | 'OverLimit';
   }
 
-  /** @name PalletUniqueError (280) */
+  /** @name PalletUniqueError (291) */
   export interface PalletUniqueError extends Enum {
     readonly isCollectionDecimalPointLimitExceeded: boolean;
     readonly isConfirmUnsetSponsorFail: boolean;
@@ -2370,7 +2402,7 @@
     readonly type: 'CollectionDecimalPointLimitExceeded' | 'ConfirmUnsetSponsorFail' | 'EmptyArgument';
   }
 
-  /** @name UpDataStructsCollection (281) */
+  /** @name UpDataStructsCollection (292) */
   export interface UpDataStructsCollection extends Struct {
     readonly owner: AccountId32;
     readonly mode: UpDataStructsCollectionMode;
@@ -2388,7 +2420,7 @@
     readonly metaUpdatePermission: UpDataStructsMetaUpdatePermission;
   }
 
-  /** @name UpDataStructsSponsorshipState (282) */
+  /** @name UpDataStructsSponsorshipState (293) */
   export interface UpDataStructsSponsorshipState extends Enum {
     readonly isDisabled: boolean;
     readonly isUnconfirmed: boolean;
@@ -2398,14 +2430,14 @@
     readonly type: 'Disabled' | 'Unconfirmed' | 'Confirmed';
   }
 
-  /** @name UpDataStructsCollectionStats (285) */
+  /** @name UpDataStructsCollectionStats (296) */
   export interface UpDataStructsCollectionStats extends Struct {
     readonly created: u32;
     readonly destroyed: u32;
     readonly alive: u32;
   }
 
-  /** @name PalletCommonError (286) */
+  /** @name PalletCommonError (297) */
   export interface PalletCommonError extends Enum {
     readonly isCollectionNotFound: boolean;
     readonly isMustBeTokenOwner: boolean;
@@ -2433,7 +2465,7 @@
     readonly type: 'CollectionNotFound' | 'MustBeTokenOwner' | 'NoPermission' | 'PublicMintingNotAllowed' | 'AddressNotInAllowlist' | 'CollectionNameLimitExceeded' | 'CollectionDescriptionLimitExceeded' | 'CollectionTokenPrefixLimitExceeded' | 'TotalCollectionsLimitExceeded' | 'TokenVariableDataLimitExceeded' | 'CollectionAdminCountExceeded' | 'CollectionLimitBoundsExceeded' | 'OwnerPermissionsCantBeReverted' | 'TransferNotAllowed' | 'AccountTokenLimitExceeded' | 'CollectionTokenLimitExceeded' | 'MetadataFlagFrozen' | 'TokenNotFound' | 'TokenValueTooLow' | 'ApprovedValueTooLow' | 'CantApproveMoreThanOwned' | 'AddressIsZero' | 'UnsupportedOperation';
   }
 
-  /** @name PalletFungibleError (288) */
+  /** @name PalletFungibleError (299) */
   export interface PalletFungibleError extends Enum {
     readonly isNotFungibleDataUsedToMintFungibleCollectionToken: boolean;
     readonly isFungibleItemsHaveNoId: boolean;
@@ -2441,34 +2473,34 @@
     readonly type: 'NotFungibleDataUsedToMintFungibleCollectionToken' | 'FungibleItemsHaveNoId' | 'FungibleItemsDontHaveData';
   }
 
-  /** @name PalletRefungibleItemData (289) */
+  /** @name PalletRefungibleItemData (300) */
   export interface PalletRefungibleItemData extends Struct {
     readonly constData: Bytes;
     readonly variableData: Bytes;
   }
 
-  /** @name PalletRefungibleError (293) */
+  /** @name PalletRefungibleError (304) */
   export interface PalletRefungibleError extends Enum {
     readonly isNotRefungibleDataUsedToMintFungibleCollectionToken: boolean;
     readonly isWrongRefungiblePieces: boolean;
     readonly type: 'NotRefungibleDataUsedToMintFungibleCollectionToken' | 'WrongRefungiblePieces';
   }
 
-  /** @name PalletNonfungibleItemData (294) */
+  /** @name PalletNonfungibleItemData (305) */
   export interface PalletNonfungibleItemData extends Struct {
     readonly constData: Bytes;
     readonly variableData: Bytes;
     readonly owner: PalletCommonAccountBasicCrossAccountIdRepr;
   }
 
-  /** @name PalletNonfungibleError (295) */
+  /** @name PalletNonfungibleError (306) */
   export interface PalletNonfungibleError extends Enum {
     readonly isNotNonfungibleDataUsedToMintFungibleCollectionToken: boolean;
     readonly isNonfungibleItemsHaveNoAmount: boolean;
     readonly type: 'NotNonfungibleDataUsedToMintFungibleCollectionToken' | 'NonfungibleItemsHaveNoAmount';
   }
 
-  /** @name PalletEvmError (297) */
+  /** @name PalletEvmError (308) */
   export interface PalletEvmError extends Enum {
     readonly isBalanceLow: boolean;
     readonly isFeeOverflow: boolean;
@@ -2479,7 +2511,7 @@
     readonly type: 'BalanceLow' | 'FeeOverflow' | 'PaymentOverflow' | 'WithdrawFailed' | 'GasPriceTooLow' | 'InvalidNonce';
   }
 
-  /** @name FpRpcTransactionStatus (300) */
+  /** @name FpRpcTransactionStatus (311) */
   export interface FpRpcTransactionStatus extends Struct {
     readonly transactionHash: H256;
     readonly transactionIndex: u32;
@@ -2490,10 +2522,10 @@
     readonly logsBloom: EthbloomBloom;
   }
 
-  /** @name EthbloomBloom (303) */
+  /** @name EthbloomBloom (314) */
   export interface EthbloomBloom extends U8aFixed {}
 
-  /** @name EthereumReceiptReceiptV3 (305) */
+  /** @name EthereumReceiptReceiptV3 (316) */
   export interface EthereumReceiptReceiptV3 extends Enum {
     readonly isLegacy: boolean;
     readonly asLegacy: EthereumReceiptEip658ReceiptData;
@@ -2504,7 +2536,7 @@
     readonly type: 'Legacy' | 'Eip2930' | 'Eip1559';
   }
 
-  /** @name EthereumReceiptEip658ReceiptData (306) */
+  /** @name EthereumReceiptEip658ReceiptData (317) */
   export interface EthereumReceiptEip658ReceiptData extends Struct {
     readonly statusCode: u8;
     readonly usedGas: U256;
@@ -2512,14 +2544,14 @@
     readonly logs: Vec<EthereumLog>;
   }
 
-  /** @name EthereumBlock (307) */
+  /** @name EthereumBlock (318) */
   export interface EthereumBlock extends Struct {
     readonly header: EthereumHeader;
     readonly transactions: Vec<EthereumTransactionTransactionV2>;
     readonly ommers: Vec<EthereumHeader>;
   }
 
-  /** @name EthereumHeader (308) */
+  /** @name EthereumHeader (319) */
   export interface EthereumHeader extends Struct {
     readonly parentHash: H256;
     readonly ommersHash: H256;
@@ -2538,24 +2570,24 @@
     readonly nonce: EthereumTypesHashH64;
   }
 
-  /** @name EthereumTypesHashH64 (309) */
+  /** @name EthereumTypesHashH64 (320) */
   export interface EthereumTypesHashH64 extends U8aFixed {}
 
-  /** @name PalletEthereumError (314) */
+  /** @name PalletEthereumError (325) */
   export interface PalletEthereumError extends Enum {
     readonly isInvalidSignature: boolean;
     readonly isPreLogExists: boolean;
     readonly type: 'InvalidSignature' | 'PreLogExists';
   }
 
-  /** @name PalletEvmCoderSubstrateError (315) */
+  /** @name PalletEvmCoderSubstrateError (326) */
   export interface PalletEvmCoderSubstrateError extends Enum {
     readonly isOutOfGas: boolean;
     readonly isOutOfFund: boolean;
     readonly type: 'OutOfGas' | 'OutOfFund';
   }
 
-  /** @name PalletEvmContractHelpersSponsoringModeT (316) */
+  /** @name PalletEvmContractHelpersSponsoringModeT (327) */
   export interface PalletEvmContractHelpersSponsoringModeT extends Enum {
     readonly isDisabled: boolean;
     readonly isAllowlisted: boolean;
@@ -2563,20 +2595,20 @@
     readonly type: 'Disabled' | 'Allowlisted' | 'Generous';
   }
 
-  /** @name PalletEvmContractHelpersError (318) */
+  /** @name PalletEvmContractHelpersError (329) */
   export interface PalletEvmContractHelpersError extends Enum {
     readonly isNoPermission: boolean;
     readonly type: 'NoPermission';
   }
 
-  /** @name PalletEvmMigrationError (319) */
+  /** @name PalletEvmMigrationError (330) */
   export interface PalletEvmMigrationError extends Enum {
     readonly isAccountNotEmpty: boolean;
     readonly isAccountIsNotMigrating: boolean;
     readonly type: 'AccountNotEmpty' | 'AccountIsNotMigrating';
   }
 
-  /** @name SpRuntimeMultiSignature (321) */
+  /** @name SpRuntimeMultiSignature (332) */
   export interface SpRuntimeMultiSignature extends Enum {
     readonly isEd25519: boolean;
     readonly asEd25519: SpCoreEd25519Signature;
@@ -2587,31 +2619,31 @@
     readonly type: 'Ed25519' | 'Sr25519' | 'Ecdsa';
   }
 
-  /** @name SpCoreEd25519Signature (322) */
+  /** @name SpCoreEd25519Signature (333) */
   export interface SpCoreEd25519Signature extends U8aFixed {}
 
-  /** @name SpCoreSr25519Signature (324) */
+  /** @name SpCoreSr25519Signature (335) */
   export interface SpCoreSr25519Signature extends U8aFixed {}
 
-  /** @name SpCoreEcdsaSignature (325) */
+  /** @name SpCoreEcdsaSignature (336) */
   export interface SpCoreEcdsaSignature extends U8aFixed {}
 
-  /** @name FrameSystemExtensionsCheckSpecVersion (328) */
+  /** @name FrameSystemExtensionsCheckSpecVersion (339) */
   export type FrameSystemExtensionsCheckSpecVersion = Null;
 
-  /** @name FrameSystemExtensionsCheckGenesis (329) */
+  /** @name FrameSystemExtensionsCheckGenesis (340) */
   export type FrameSystemExtensionsCheckGenesis = Null;
 
-  /** @name FrameSystemExtensionsCheckNonce (332) */
+  /** @name FrameSystemExtensionsCheckNonce (343) */
   export interface FrameSystemExtensionsCheckNonce extends Compact<u32> {}
 
-  /** @name FrameSystemExtensionsCheckWeight (333) */
+  /** @name FrameSystemExtensionsCheckWeight (344) */
   export type FrameSystemExtensionsCheckWeight = Null;
 
-  /** @name PalletTemplateTransactionPaymentChargeTransactionPayment (334) */
+  /** @name PalletTemplateTransactionPaymentChargeTransactionPayment (345) */
   export interface PalletTemplateTransactionPaymentChargeTransactionPayment extends Compact<u128> {}
 
-  /** @name UniqueRuntimeRuntime (335) */
+  /** @name UniqueRuntimeRuntime (346) */
   export type UniqueRuntimeRuntime = Null;
 
 } // declare module
modifiedtests/src/interfaces/unique/types.tsdiffbeforeafterboth
--- a/tests/src/interfaces/unique/types.ts
+++ b/tests/src/interfaces/unique/types.ts
@@ -1316,6 +1316,11 @@
     readonly owner: PalletCommonAccountBasicCrossAccountIdRepr;
     readonly itemsData: Vec<UpDataStructsCreateItemData>;
   } & Struct;
+  readonly isCreateMultipleItemsEx: boolean;
+  readonly asCreateMultipleItemsEx: {
+    readonly collectionId: u32;
+    readonly data: UpDataStructsCreateItemExData;
+  } & Struct;
   readonly isSetTransfersEnabledFlag: boolean;
   readonly asSetTransfersEnabledFlag: {
     readonly collectionId: u32;
@@ -1392,7 +1397,7 @@
     readonly collectionId: u32;
     readonly newLimit: UpDataStructsCollectionLimits;
   } & Struct;
-  readonly type: 'CreateCollection' | 'CreateCollectionEx' | 'DestroyCollection' | 'AddToAllowList' | 'RemoveFromAllowList' | 'SetPublicAccessMode' | 'SetMintPermission' | 'ChangeCollectionOwner' | 'AddCollectionAdmin' | 'RemoveCollectionAdmin' | 'SetCollectionSponsor' | 'ConfirmSponsorship' | 'RemoveCollectionSponsor' | 'CreateItem' | 'CreateMultipleItems' | 'SetTransfersEnabledFlag' | 'BurnItem' | 'BurnFrom' | 'Transfer' | 'Approve' | 'TransferFrom' | 'SetVariableMetaData' | 'SetMetaUpdatePermissionFlag' | 'SetSchemaVersion' | 'SetOffchainSchema' | 'SetConstOnChainSchema' | 'SetVariableOnChainSchema' | 'SetCollectionLimits';
+  readonly type: 'CreateCollection' | 'CreateCollectionEx' | 'DestroyCollection' | 'AddToAllowList' | 'RemoveFromAllowList' | 'SetPublicAccessMode' | 'SetMintPermission' | 'ChangeCollectionOwner' | 'AddCollectionAdmin' | 'RemoveCollectionAdmin' | 'SetCollectionSponsor' | 'ConfirmSponsorship' | 'RemoveCollectionSponsor' | 'CreateItem' | 'CreateMultipleItems' | 'CreateMultipleItemsEx' | 'SetTransfersEnabledFlag' | 'BurnItem' | 'BurnFrom' | 'Transfer' | 'Approve' | 'TransferFrom' | 'SetVariableMetaData' | 'SetMetaUpdatePermissionFlag' | 'SetSchemaVersion' | 'SetOffchainSchema' | 'SetConstOnChainSchema' | 'SetVariableOnChainSchema' | 'SetCollectionLimits';
 }
 
 /** @name PalletUniqueError */
@@ -1806,12 +1811,32 @@
   readonly type: 'Nft' | 'Fungible' | 'ReFungible';
 }
 
+/** @name UpDataStructsCreateItemExData */
+export interface UpDataStructsCreateItemExData extends Enum {
+  readonly isNft: boolean;
+  readonly asNft: Vec<UpDataStructsCreateNftExData>;
+  readonly isFungible: boolean;
+  readonly asFungible: BTreeMap<PalletCommonAccountBasicCrossAccountIdRepr,u128>;
+  readonly isRefungibleMultipleItems: boolean;
+  readonly asRefungibleMultipleItems: Vec<UpDataStructsCreateRefungibleExData>;
+  readonly isRefungibleMultipleOwners: boolean;
+  readonly asRefungibleMultipleOwners: UpDataStructsCreateRefungibleExData;
+  readonly type: 'Nft' | 'Fungible' | 'RefungibleMultipleItems' | 'RefungibleMultipleOwners';
+}
+
 /** @name UpDataStructsCreateNftData */
 export interface UpDataStructsCreateNftData extends Struct {
   readonly constData: Bytes;
   readonly variableData: Bytes;
 }
 
+/** @name UpDataStructsCreateNftExData */
+export interface UpDataStructsCreateNftExData extends Struct {
+  readonly constData: Bytes;
+  readonly variableData: Bytes;
+  readonly owner: PalletCommonAccountBasicCrossAccountIdRepr;
+}
+
 /** @name UpDataStructsCreateReFungibleData */
 export interface UpDataStructsCreateReFungibleData extends Struct {
   readonly constData: Bytes;
@@ -1819,6 +1844,13 @@
   readonly pieces: u128;
 }
 
+/** @name UpDataStructsCreateRefungibleExData */
+export interface UpDataStructsCreateRefungibleExData extends Struct {
+  readonly constData: Bytes;
+  readonly variableData: Bytes;
+  readonly users: BTreeMap<PalletCommonAccountBasicCrossAccountIdRepr, u128>;
+}
+
 /** @name UpDataStructsMetaUpdatePermission */
 export interface UpDataStructsMetaUpdatePermission extends Enum {
   readonly isItemOwner: boolean;