git.delta.rocks / unique-network / refs/commits / f9edb50d1095

difftreelog

added bench for sponsoring, logic broken , commit for rebase

PraetorP2022-08-30parent: #8ee0040.patch.diff
in: master

14 files changed

modifiedCargo.lockdiffbeforeafterboth
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -5307,6 +5307,7 @@
  "pallet-common",
  "pallet-evm",
  "pallet-evm-contract-helpers",
+ "pallet-evm-migration",
  "pallet-randomness-collective-flip",
  "pallet-timestamp",
  "pallet-unique",
modifiedpallets/app-promotion/Cargo.tomldiffbeforeafterboth
--- a/pallets/app-promotion/Cargo.toml
+++ b/pallets/app-promotion/Cargo.toml
@@ -15,7 +15,7 @@
 targets = ['x86_64-unknown-linux-gnu']
 
 [features]
-default = ['std']
+default = ['std',]
 runtime-benchmarks = [
     'frame-benchmarking',
     'frame-support/runtime-benchmarks',
@@ -121,6 +121,13 @@
 [dependencies.pallet-evm-contract-helpers]
 default-features = false
 path =  "../evm-contract-helpers"
+
+[dev-dependencies]
+[dependencies.pallet-evm-migration]
+default-features = false
+path =  "../evm-migration"
+
+
 ################################################################################
 
 [dependencies]
modifiedpallets/app-promotion/src/benchmarking.rsdiffbeforeafterboth
--- a/pallets/app-promotion/src/benchmarking.rs
+++ b/pallets/app-promotion/src/benchmarking.rs
@@ -20,53 +20,109 @@
 use crate::Pallet as PromototionPallet;
 
 use sp_runtime::traits::Bounded;
+use sp_std::vec;
 
 use frame_benchmarking::{benchmarks, account};
-use frame_support::traits::OnInitialize;
+
 use frame_system::{Origin, RawOrigin};
+use pallet_unique::benchmarking::create_nft_collection;
+use pallet_evm_migration::Pallet as EvmMigrationPallet;
+
+// trait BenchmarkingConfig: Config + pallet_unique::Config { }
+
+// impl<T: Config + pallet_unique::Config> BenchmarkingConfig for T { }
 
 const SEED: u32 = 0;
 benchmarks! {
 	where_clause{
-		where T: Config
-
+		where T:  Config + pallet_unique::Config + pallet_evm_migration::Config ,
+		T::BlockNumber: From<u32>
 	}
-	on_initialize {
-		let block1: T::BlockNumber = T::BlockNumber::from(1u32);
-		let block2: T::BlockNumber = T::BlockNumber::from(2u32);
-		PromototionPallet::<T>::on_initialize(block1); // Create Treasury account
-	}: { PromototionPallet::<T>::on_initialize(block2); } // Benchmark deposit_into_existing path
-
 	start_app_promotion {
-		let caller = account::<T::AccountId>("caller", 0, SEED);
 
-	} : {PromototionPallet::<T>::start_app_promotion(RawOrigin::Root.into(), T::BlockNumber::from(2u32))?}
+	} : {PromototionPallet::<T>::start_app_promotion(RawOrigin::Root.into(), None)?}
+
+	stop_app_promotion{
+		PromototionPallet::<T>::start_app_promotion(RawOrigin::Root.into(), Some(25.into()))?;
+	} : {PromototionPallet::<T>::stop_app_promotion(RawOrigin::Root.into())?}
 
 	set_admin_address {
-		let caller = account::<T::AccountId>("caller", 0, SEED);
-		let _ = T::Currency::make_free_balance_be(&caller,  Perbill::from_rational(1u32, 2) * BalanceOf::<T>::max_value());
-	} : {PromototionPallet::<T>::set_admin_address(RawOrigin::Root.into(), caller)?}
+		let pallet_admin = account::<T::AccountId>("admin", 0, SEED);
+		let _ = <T as Config>::Currency::make_free_balance_be(&pallet_admin,  Perbill::from_rational(1u32, 2) * BalanceOf::<T>::max_value());
+	} : {PromototionPallet::<T>::set_admin_address(RawOrigin::Root.into(), T::CrossAccountId::from_sub(pallet_admin))?}
 
+	payout_stakers{
+		let pallet_admin = account::<T::AccountId>("admin", 0, SEED);
+		let share = Perbill::from_rational(1u32, 10);
+		PromototionPallet::<T>::set_admin_address(RawOrigin::Root.into(), T::CrossAccountId::from_sub(pallet_admin.clone()))?;
+		let _ = <T as Config>::Currency::make_free_balance_be(&pallet_admin,  Perbill::from_rational(1u32, 2) * BalanceOf::<T>::max_value());
+		let staker: T::AccountId = account("caller", 0, SEED);
+		let _ = <T as Config>::Currency::make_free_balance_be(&staker,  Perbill::from_rational(1u32, 2) * BalanceOf::<T>::max_value());
+		let _ = PromototionPallet::<T>::stake(RawOrigin::Signed(staker.clone()).into(), share * <T as Config>::Currency::total_balance(&staker))?;
+	} : {PromototionPallet::<T>::payout_stakers(RawOrigin::Signed(pallet_admin.clone()).into(), Some(1))?}
+
 	stake {
 		let caller = account::<T::AccountId>("caller", 0, SEED);
 		let share = Perbill::from_rational(1u32, 10);
-		let _ = T::Currency::make_free_balance_be(&caller,  Perbill::from_rational(1u32, 2) * BalanceOf::<T>::max_value());
-	} : {PromototionPallet::<T>::stake(RawOrigin::Signed(caller.clone()).into(), share * T::Currency::total_balance(&caller))?}
+		let _ = <T as Config>::Currency::make_free_balance_be(&caller,  Perbill::from_rational(1u32, 2) * BalanceOf::<T>::max_value());
+	} : {PromototionPallet::<T>::stake(RawOrigin::Signed(caller.clone()).into(), share * <T as Config>::Currency::total_balance(&caller))?}
 
 	unstake {
 		let caller = account::<T::AccountId>("caller", 0, SEED);
 		let share = Perbill::from_rational(1u32, 10);
-		let _ = T::Currency::make_free_balance_be(&caller,  Perbill::from_rational(1u32, 2) * BalanceOf::<T>::max_value());
-		let _ = PromototionPallet::<T>::stake(RawOrigin::Signed(caller.clone()).into(), share * T::Currency::total_balance(&caller))?;
+		let _ = <T as Config>::Currency::make_free_balance_be(&caller,  Perbill::from_rational(1u32, 2) * BalanceOf::<T>::max_value());
+		let _ = PromototionPallet::<T>::stake(RawOrigin::Signed(caller.clone()).into(), share * <T as Config>::Currency::total_balance(&caller))?;
 
-	} : {PromototionPallet::<T>::unstake(RawOrigin::Signed(caller.clone()).into(), share * T::Currency::total_balance(&caller))?}
+	} : {PromototionPallet::<T>::unstake(RawOrigin::Signed(caller.clone()).into(), share * <T as Config>::Currency::total_balance(&caller))?}
 
 	recalculate_stake {
 		let caller = account::<T::AccountId>("caller", 0, SEED);
 		let share = Perbill::from_rational(1u32, 10);
-		let _ = T::Currency::make_free_balance_be(&caller,  Perbill::from_rational(1u32, 2) * BalanceOf::<T>::max_value());
-		let _ = PromototionPallet::<T>::stake(RawOrigin::Signed(caller.clone()).into(), share * T::Currency::total_balance(&caller))?;
-		let block = <T::BlockNumberProvider as BlockNumberProvider>::current_block_number();
+		let _ = <T as Config>::Currency::make_free_balance_be(&caller,  Perbill::from_rational(1u32, 2) * BalanceOf::<T>::max_value());
+		let _ = PromototionPallet::<T>::stake(RawOrigin::Signed(caller.clone()).into(), share * <T as Config>::Currency::total_balance(&caller))?;
+		let block = <T::RelayBlockNumberProvider as BlockNumberProvider>::current_block_number();
 		let mut acc = <BalanceOf<T>>::default();
-	} : {PromototionPallet::<T>::recalculate_stake(&caller, block, share * T::Currency::total_balance(&caller), &mut acc)}
+	} : {PromototionPallet::<T>::recalculate_stake(&caller, block, share * <T as Config>::Currency::total_balance(&caller), &mut acc)}
+
+	sponsor_collection {
+		let pallet_admin = account::<T::AccountId>("admin", 0, SEED);
+		PromototionPallet::<T>::set_admin_address(RawOrigin::Root.into(), T::CrossAccountId::from_sub(pallet_admin.clone()))?;
+		let _ = <T as Config>::Currency::make_free_balance_be(&pallet_admin,  Perbill::from_rational(1u32, 2) * BalanceOf::<T>::max_value());
+		let caller: T::AccountId = account("caller", 0, SEED);
+		let _ = <T as Config>::Currency::make_free_balance_be(&caller,  Perbill::from_rational(1u32, 2) * BalanceOf::<T>::max_value());
+		let collection = create_nft_collection::<T>(caller.clone())?;
+	} : {PromototionPallet::<T>::sponsor_collection(RawOrigin::Signed(pallet_admin.clone()).into(), collection)?}
+
+	stop_sponsoring_collection {
+		let pallet_admin = account::<T::AccountId>("admin", 0, SEED);
+		PromototionPallet::<T>::set_admin_address(RawOrigin::Root.into(), T::CrossAccountId::from_sub(pallet_admin.clone()))?;
+		let _ = <T as Config>::Currency::make_free_balance_be(&pallet_admin,  Perbill::from_rational(1u32, 2) * BalanceOf::<T>::max_value());
+		let caller: T::AccountId = account("caller", 0, SEED);
+		let _ = <T as Config>::Currency::make_free_balance_be(&caller,  Perbill::from_rational(1u32, 2) * BalanceOf::<T>::max_value());
+		let collection = create_nft_collection::<T>(caller.clone())?;
+		PromototionPallet::<T>::sponsor_collection(RawOrigin::Signed(pallet_admin.clone()).into(), collection)?;
+	} : {PromototionPallet::<T>::stop_sponsoring_collection(RawOrigin::Signed(pallet_admin.clone()).into(), collection)?}
+
+	sponsor_contract {
+		let pallet_admin = account::<T::AccountId>("admin", 0, SEED);
+		PromototionPallet::<T>::set_admin_address(RawOrigin::Root.into(), T::CrossAccountId::from_sub(pallet_admin.clone()))?;
+
+		let _ = <T as Config>::Currency::make_free_balance_be(&pallet_admin,  Perbill::from_rational(1u32, 2) * BalanceOf::<T>::max_value());
+		let address = H160::from_low_u64_be(SEED as u64);
+		let data: Vec<u8> = (0..20 as u8).collect();
+		<EvmMigrationPallet<T>>::begin(RawOrigin::Root.into(), address)?;
+		<EvmMigrationPallet<T>>::finish(RawOrigin::Root.into(), address, data)?;
+	} : {PromototionPallet::<T>::sponsor_conract(RawOrigin::Signed(pallet_admin.clone()).into(), address)?}
+
+	stop_sponsoring_contract {
+		let pallet_admin = account::<T::AccountId>("admin", 0, SEED);
+		PromototionPallet::<T>::set_admin_address(RawOrigin::Root.into(), T::CrossAccountId::from_sub(pallet_admin.clone()))?;
+
+		let _ = <T as Config>::Currency::make_free_balance_be(&pallet_admin,  Perbill::from_rational(1u32, 2) * BalanceOf::<T>::max_value());
+		let address = H160::from_low_u64_be(SEED as u64);
+		let data: Vec<u8> = (0..20 as u8).collect();
+		<EvmMigrationPallet<T>>::begin(RawOrigin::Root.into(), address)?;
+		<EvmMigrationPallet<T>>::finish(RawOrigin::Root.into(), address, data)?;
+		PromototionPallet::<T>::sponsor_conract(RawOrigin::Signed(pallet_admin.clone()).into(), address)?;
+	} : {PromototionPallet::<T>::stop_sponsoring_contract(RawOrigin::Signed(pallet_admin.clone()).into(), address)?}
 }
modifiedpallets/app-promotion/src/lib.rsdiffbeforeafterboth
--- a/pallets/app-promotion/src/lib.rs
+++ b/pallets/app-promotion/src/lib.rs
@@ -132,6 +132,8 @@
 	#[pallet::generate_deposit(fn deposit_event)]
 	pub enum Event<T: Config> {
 		StakingRecalculation(
+			/// An recalculated staker
+			T::AccountId,
 			/// Base on which interest is calculated
 			BalanceOf<T>,
 			/// Amount of accrued interest
@@ -164,7 +166,7 @@
 			Key<Blake2_128Concat, T::AccountId>,
 			Key<Twox64Concat, T::BlockNumber>,
 		),
-		Value = BalanceOf<T>,
+		Value = (BalanceOf<T>, T::BlockNumber),
 		QueryKind = ValueQuery,
 	>;
 
@@ -189,6 +191,13 @@
 	pub type NextInterestBlock<T: Config> =
 		StorageValue<Value = T::BlockNumber, QueryKind = ValueQuery>;
 
+	/// Stores the address of the staker for which the last revenue recalculation was performed.
+	/// If `None`, then recalculation has not yet been performed or calculations have been completed for all stakers.
+	#[pallet::storage]
+	#[pallet::getter(fn get_last_calculated_staker)]
+	pub type LastCalcucaltedStaker<T: Config> =
+		StorageValue<Value = T::AccountId, QueryKind = OptionQuery>;
+
 	#[pallet::hooks]
 	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
 		fn on_initialize(current_block: T::BlockNumber) -> Weight
@@ -196,10 +205,10 @@
 			<T as frame_system::Config>::BlockNumber: From<u32>,
 		{
 			let mut consumed_weight = 0;
-			let mut add_weight = |reads, writes, weight| {
-				consumed_weight += T::DbWeight::get().reads_writes(reads, writes);
-				consumed_weight += weight;
-			};
+			// let mut add_weight = |reads, writes, weight| {
+			// 	consumed_weight += T::DbWeight::get().reads_writes(reads, writes);
+			// 	consumed_weight += weight;
+			// };
 
 			PendingUnstake::<T>::iter()
 				.filter_map(|((staker, block), amount)| {
@@ -214,41 +223,44 @@
 					<PendingUnstake<T>>::remove((staker, block));
 				});
 
-			let next_interest_block = Self::get_interest_block();
-			let current_relay_block = T::RelayBlockNumberProvider::current_block_number();
-			if next_interest_block != 0.into() && current_relay_block >= next_interest_block {
-				let mut acc = <BalanceOf<T>>::default();
-				let mut base_acc = <BalanceOf<T>>::default();
+			// let next_interest_block = Self::get_interest_block();
+			// let current_relay_block = T::RelayBlockNumberProvider::current_block_number();
+			// if next_interest_block != 0.into() && current_relay_block >= next_interest_block {
+			// 	let mut acc = <BalanceOf<T>>::default();
+			// 	let mut base_acc = <BalanceOf<T>>::default();
 
-				NextInterestBlock::<T>::set(
-					NextInterestBlock::<T>::get() + T::RecalculationInterval::get(),
-				);
-				add_weight(0, 1, 0);
+			// 	NextInterestBlock::<T>::set(
+			// 		NextInterestBlock::<T>::get() + T::RecalculationInterval::get(),
+			// 	);
+			// 	add_weight(0, 1, 0);
 
-				Staked::<T>::iter()
-					.filter(|((_, block), _)| {
-						*block + T::RecalculationInterval::get() <= current_relay_block
-					})
-					.for_each(|((staker, block), amount)| {
-						Self::recalculate_stake(&staker, block, amount, &mut acc);
-						add_weight(0, 0, T::WeightInfo::recalculate_stake());
-						base_acc += amount;
-					});
-				<TotalStaked<T>>::get()
-					.checked_add(&acc)
-					.map(|res| <TotalStaked<T>>::set(res));
+			// 	Staked::<T>::iter()
+			// 		.filter(|((_, block), _)| {
+			// 			*block + T::RecalculationInterval::get() <= current_relay_block
+			// 		})
+			// 		.for_each(|((staker, block), amount)| {
+			// 			Self::recalculate_stake(&staker, block, amount, &mut acc);
+			// 			add_weight(0, 0, T::WeightInfo::recalculate_stake());
+			// 			base_acc += amount;
+			// 		});
+			// 	<TotalStaked<T>>::get()
+			// 		.checked_add(&acc)
+			// 		.map(|res| <TotalStaked<T>>::set(res));
 
-				Self::deposit_event(Event::StakingRecalculation(base_acc, acc));
-				add_weight(0, 1, 0);
-			} else {
-				add_weight(1, 0, 0)
-			};
+			// 	Self::deposit_event(Event::StakingRecalculation(base_acc, acc));
+			// 	add_weight(0, 1, 0);
+			// } else {
+			// 	add_weight(1, 0, 0)
+			// };
 			consumed_weight
 		}
 	}
 
 	#[pallet::call]
-	impl<T: Config> Pallet<T> {
+	impl<T: Config> Pallet<T>
+	where
+		T::BlockNumber: From<u32>,
+	{
 		#[pallet::weight(T::WeightInfo::set_admin_address())]
 		pub fn set_admin_address(origin: OriginFor<T>, admin: T::CrossAccountId) -> DispatchResult {
 			ensure_root(origin)?;
@@ -281,7 +293,7 @@
 			Ok(())
 		}
 
-		#[pallet::weight(0)]
+		#[pallet::weight(T::WeightInfo::stop_app_promotion())]
 		pub fn stop_app_promotion(origin: OriginFor<T>) -> DispatchResult
 		where
 			<T as frame_system::Config>::BlockNumber: From<u32>,
@@ -317,19 +329,24 @@
 			Self::add_lock_balance(&staker_id, amount)?;
 
 			let block_number = T::RelayBlockNumberProvider::current_block_number();
+			let recalc_block = (block_number / T::RecalculationInterval::get() + 2u32.into())
+				* T::RecalculationInterval::get();
 
-			<Staked<T>>::insert(
-				(&staker_id, block_number),
-				<Staked<T>>::get((&staker_id, block_number))
+			<Staked<T>>::insert((&staker_id, block_number), {
+				let mut balance_and_recalc_block = <Staked<T>>::get((&staker_id, block_number));
+				balance_and_recalc_block.0 = balance_and_recalc_block
+					.0
 					.checked_add(&amount)
-					.ok_or(ArithmeticError::Overflow)?,
-			);
+					.ok_or(ArithmeticError::Overflow)?;
+				balance_and_recalc_block.1 = recalc_block;
+				balance_and_recalc_block
+			});
 
-			<TotalStaked<T>>::set(
-				<TotalStaked<T>>::get()
-					.checked_add(&amount)
-					.ok_or(ArithmeticError::Overflow)?,
-			);
+			// <TotalStaked<T>>::set(
+			// 	<TotalStaked<T>>::get()
+			// 		.checked_add(&amount)
+			// 		.ok_or(ArithmeticError::Overflow)?,
+			// );
 
 			Ok(())
 		}
@@ -338,63 +355,120 @@
 		pub fn unstake(staker: OriginFor<T>, amount: BalanceOf<T>) -> DispatchResult {
 			let staker_id = ensure_signed(staker)?;
 
-			let mut stakes = Staked::<T>::iter_prefix((&staker_id,)).collect::<Vec<_>>();
+			let mut stakes = Staked::<T>::drain_prefix((&staker_id,));
 
-			let total_staked = stakes
-				.iter()
-				.fold(<BalanceOf<T>>::default(), |acc, (_, amount)| acc + *amount);
+			// let total_staked = stakes
+			// 	.iter()
+			// 	.fold(<BalanceOf<T>>::default(), |acc, (_, amount)| acc + *amount);
 
-			ensure!(total_staked >= amount, ArithmeticError::Underflow);
+			// ensure!(total_staked >= amount, ArithmeticError::Underflow);
 
-			<TotalStaked<T>>::set(
-				<TotalStaked<T>>::get()
-					.checked_sub(&amount)
-					.ok_or(ArithmeticError::Underflow)?,
-			);
+			// <TotalStaked<T>>::set(
+			// 	<TotalStaked<T>>::get()
+			// 		.checked_sub(&amount)
+			// 		.ok_or(ArithmeticError::Underflow)?,
+			// );
 
-			let block =
-				T::RelayBlockNumberProvider::current_block_number() + T::PendingInterval::get();
-			<PendingUnstake<T>>::insert(
-				(&staker_id, block),
-				<PendingUnstake<T>>::get((&staker_id, block))
-					.checked_add(&amount)
-					.ok_or(ArithmeticError::Overflow)?,
-			);
+			// let block =
+			// 	T::RelayBlockNumberProvider::current_block_number() + T::PendingInterval::get();
+			// <PendingUnstake<T>>::insert(
+			// 	(&staker_id, block),
+			// 	<PendingUnstake<T>>::get((&staker_id, block))
+			// 		.checked_add(&amount)
+			// 		.ok_or(ArithmeticError::Overflow)?,
+			// );
 
-			stakes.sort_by_key(|(block, _)| *block);
+			// stakes.sort_by_key(|(block, _)| *block);
 
-			let mut acc_amount = amount;
-			let new_state = stakes
-				.into_iter()
-				.map_while(|(block, balance_per_block)| {
-					if acc_amount == <BalanceOf<T>>::default() {
-						return None;
-					}
-					if acc_amount <= balance_per_block {
-						let res = (block, balance_per_block - acc_amount, acc_amount);
-						acc_amount = <BalanceOf<T>>::default();
-						return Some(res);
-					} else {
-						acc_amount -= balance_per_block;
-						return Some((block, <BalanceOf<T>>::default(), acc_amount));
-					}
-				})
-				.collect::<Vec<_>>();
+			// let mut acc_amount = amount;
+			// let new_state = stakes
+			// 	.into_iter()
+			// 	.map_while(|(block, balance_per_block)| {
+			// 		if acc_amount == <BalanceOf<T>>::default() {
+			// 			return None;
+			// 		}
+			// 		if acc_amount <= balance_per_block {
+			// 			let res = (block, balance_per_block - acc_amount, acc_amount);
+			// 			acc_amount = <BalanceOf<T>>::default();
+			// 			return Some(res);
+			// 		} else {
+			// 			acc_amount -= balance_per_block;
+			// 			return Some((block, <BalanceOf<T>>::default(), acc_amount));
+			// 		}
+			// 	})
+			// 	.collect::<Vec<_>>();
 
-			new_state
-				.into_iter()
-				.for_each(|(block, to_staked, _to_pending)| {
-					if to_staked == <BalanceOf<T>>::default() {
-						<Staked<T>>::remove((&staker_id, block));
-					} else {
-						<Staked<T>>::insert((&staker_id, block), to_staked);
-					}
-				});
+			// new_state
+			// 	.into_iter()
+			// 	.for_each(|(block, to_staked, _to_pending)| {
+			// 		if to_staked == <BalanceOf<T>>::default() {
+			// 			<Staked<T>>::remove((&staker_id, block));
+			// 		} else {
+			// 			<Staked<T>>::insert((&staker_id, block), to_staked);
+			// 		}
+			// 	});
 
 			Ok(())
+
+			// let staker_id = ensure_signed(staker)?;
+
+			// let mut stakes = Staked::<T>::iter_prefix((&staker_id,)).collect::<Vec<_>>();
+
+			// let total_staked = stakes
+			// 	.iter()
+			// 	.fold(<BalanceOf<T>>::default(), |acc, (_, amount)| acc + *amount);
+
+			// ensure!(total_staked >= amount, ArithmeticError::Underflow);
+
+			// <TotalStaked<T>>::set(
+			// 	<TotalStaked<T>>::get()
+			// 		.checked_sub(&amount)
+			// 		.ok_or(ArithmeticError::Underflow)?,
+			// );
+
+			// let block =
+			// 	T::RelayBlockNumberProvider::current_block_number() + T::PendingInterval::get();
+			// <PendingUnstake<T>>::insert(
+			// 	(&staker_id, block),
+			// 	<PendingUnstake<T>>::get((&staker_id, block))
+			// 		.checked_add(&amount)
+			// 		.ok_or(ArithmeticError::Overflow)?,
+			// );
+
+			// stakes.sort_by_key(|(block, _)| *block);
+
+			// let mut acc_amount = amount;
+			// let new_state = stakes
+			// 	.into_iter()
+			// 	.map_while(|(block, balance_per_block)| {
+			// 		if acc_amount == <BalanceOf<T>>::default() {
+			// 			return None;
+			// 		}
+			// 		if acc_amount <= balance_per_block {
+			// 			let res = (block, balance_per_block - acc_amount, acc_amount);
+			// 			acc_amount = <BalanceOf<T>>::default();
+			// 			return Some(res);
+			// 		} else {
+			// 			acc_amount -= balance_per_block;
+			// 			return Some((block, <BalanceOf<T>>::default(), acc_amount));
+			// 		}
+			// 	})
+			// 	.collect::<Vec<_>>();
+
+			// new_state
+			// 	.into_iter()
+			// 	.for_each(|(block, to_staked, _to_pending)| {
+			// 		if to_staked == <BalanceOf<T>>::default() {
+			// 			<Staked<T>>::remove((&staker_id, block));
+			// 		} else {
+			// 			<Staked<T>>::insert((&staker_id, block), to_staked);
+			// 		}
+			// 	});
+
+			// Ok(())
 		}
 
-		#[pallet::weight(0)]
+		#[pallet::weight(T::WeightInfo::sponsor_collection())]
 		pub fn sponsor_collection(
 			admin: OriginFor<T>,
 			collection_id: CollectionId,
@@ -407,8 +481,8 @@
 
 			T::CollectionHandler::set_sponsor(Self::account_id(), collection_id)
 		}
-		#[pallet::weight(0)]
-		pub fn stop_sponsorign_collection(
+		#[pallet::weight(T::WeightInfo::stop_sponsoring_collection())]
+		pub fn stop_sponsoring_collection(
 			admin: OriginFor<T>,
 			collection_id: CollectionId,
 		) -> DispatchResult {
@@ -428,7 +502,7 @@
 			T::CollectionHandler::remove_collection_sponsor(collection_id)
 		}
 
-		#[pallet::weight(0)]
+		#[pallet::weight(T::WeightInfo::sponsor_contract())]
 		pub fn sponsor_conract(admin: OriginFor<T>, contract_id: H160) -> DispatchResult {
 			let admin_id = ensure_signed(admin)?;
 
@@ -443,8 +517,8 @@
 			)
 		}
 
-		#[pallet::weight(0)]
-		pub fn stop_sponsorign_contract(admin: OriginFor<T>, contract_id: H160) -> DispatchResult {
+		#[pallet::weight(T::WeightInfo::stop_sponsoring_contract())]
+		pub fn stop_sponsoring_contract(admin: OriginFor<T>, contract_id: H160) -> DispatchResult {
 			let admin_id = ensure_signed(admin)?;
 
 			ensure!(
@@ -459,6 +533,18 @@
 			);
 			T::ContractHandler::remove_contract_sponsor(contract_id)
 		}
+
+		#[pallet::weight(0)]
+		pub fn payout_stakers(admin: OriginFor<T>, stakers_number: Option<u8>) -> DispatchResult {
+			let admin_id = ensure_signed(admin)?;
+
+			ensure!(
+				admin_id == Admin::<T>::get().ok_or(Error::<T>::AdminNotSet)?,
+				Error::<T>::NoPermission
+			);
+
+			Ok(())
+		}
 	}
 }
 
@@ -501,7 +587,9 @@
 	pub fn total_staked_by_id(staker: impl EncodeLike<T::AccountId>) -> Option<BalanceOf<T>> {
 		let staked = Staked::<T>::iter_prefix((staker,))
 			.into_iter()
-			.fold(<BalanceOf<T>>::default(), |acc, (_, amount)| acc + amount);
+			.fold(<BalanceOf<T>>::default(), |acc, (_, (amount, _))| {
+				acc + amount
+			});
 		if staked != <BalanceOf<T>>::default() {
 			Some(staked)
 		} else {
@@ -514,7 +602,7 @@
 	) -> Option<Vec<(T::BlockNumber, BalanceOf<T>)>> {
 		let mut staked = Staked::<T>::iter_prefix((staker,))
 			.into_iter()
-			.map(|(block, amount)| (block, amount))
+			.map(|(block, (amount, _))| (block, amount))
 			.collect::<Vec<_>>();
 		staked.sort_by_key(|(block, _)| *block);
 		if !staked.is_empty() {
@@ -550,17 +638,17 @@
 		income_acc: &mut BalanceOf<T>,
 	) {
 		let income = Self::calculate_income(base);
-		base.checked_add(&income).map(|res| {
-			<Staked<T>>::insert((staker, block), res);
-			*income_acc += income;
-			<T::Currency as Currency<T::AccountId>>::transfer(
-				&T::TreasuryAccountId::get(),
-				staker,
-				income,
-				ExistenceRequirement::KeepAlive,
-			)
-			.and_then(|_| Self::add_lock_balance(staker, income));
-		});
+		// base.checked_add(&income).map(|res| {
+		// 	<Staked<T>>::insert((staker, block), res);
+		// 	*income_acc += income;
+		// 	<T::Currency as Currency<T::AccountId>>::transfer(
+		// 		&T::TreasuryAccountId::get(),
+		// 		staker,
+		// 		income,
+		// 		ExistenceRequirement::KeepAlive,
+		// 	)
+		// 	.and_then(|_| Self::add_lock_balance(staker, income));
+		// });
 	}
 
 	fn calculate_income<I>(base: I) -> I
modifiedpallets/app-promotion/src/types.rsdiffbeforeafterboth
--- a/pallets/app-promotion/src/types.rs
+++ b/pallets/app-promotion/src/types.rs
@@ -101,7 +101,7 @@
 
 	fn set_sponsor(sponsor_id: Self::AccountId, contract_id: Self::ContractId) -> DispatchResult;
 
-	fn remove_contract_sponsor(collection_id: Self::ContractId) -> DispatchResult;
+	fn remove_contract_sponsor(contract_id: Self::ContractId) -> DispatchResult;
 
 	fn get_sponsor(contract_id: Self::ContractId)
 		-> Result<Option<Self::AccountId>, DispatchError>;
modifiedpallets/app-promotion/src/weights.rsdiffbeforeafterboth
--- a/pallets/app-promotion/src/weights.rs
+++ b/pallets/app-promotion/src/weights.rs
@@ -3,7 +3,7 @@
 //! Autogenerated weights for pallet_app_promotion
 //!
 //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
-//! DATE: 2022-08-09, STEPS: `50`, REPEAT: 80, LOW RANGE: `[]`, HIGH RANGE: `[]`
+//! DATE: 2022-08-30, STEPS: `50`, REPEAT: 80, LOW RANGE: `[]`, HIGH RANGE: `[]`
 //! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024
 
 // Executed Command:
@@ -33,108 +33,167 @@
 
 /// Weight functions needed for pallet_app_promotion.
 pub trait WeightInfo {
-	fn on_initialize() -> Weight;
 	fn start_app_promotion() -> Weight;
+	fn stop_app_promotion() -> Weight;
 	fn set_admin_address() -> Weight;
+	fn payout_stakers() -> Weight;
 	fn stake() -> Weight;
 	fn unstake() -> Weight;
 	fn recalculate_stake() -> Weight;
+	fn sponsor_collection() -> Weight;
+	fn stop_sponsoring_collection() -> Weight;
+	fn sponsor_contract() -> Weight;
+	fn stop_sponsoring_contract() -> Weight;
 }
 
 /// Weights for pallet_app_promotion using the Substrate node and recommended hardware.
 pub struct SubstrateWeight<T>(PhantomData<T>);
 impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
-	// Storage: Promotion PendingUnstake (r:1 w:0)
-	// Storage: Promotion NextInterestBlock (r:1 w:0)
-	fn on_initialize() -> Weight {
-		(2_705_000 as Weight)
+	// Storage: Promotion StartBlock (r:1 w:1)
+	// Storage: ParachainSystem ValidationData (r:1 w:0)
+	// Storage: Promotion NextInterestBlock (r:0 w:1)
+	fn start_app_promotion() -> Weight {
+		(2_299_000 as Weight)
 			.saturating_add(T::DbWeight::get().reads(2 as Weight))
+			.saturating_add(T::DbWeight::get().writes(2 as Weight))
 	}
 	// Storage: Promotion StartBlock (r:1 w:1)
 	// Storage: Promotion NextInterestBlock (r:0 w:1)
-	fn start_app_promotion() -> Weight {
-		(1_436_000 as Weight)
+	fn stop_app_promotion() -> Weight {
+		(1_733_000 as Weight)
 			.saturating_add(T::DbWeight::get().reads(1 as Weight))
 			.saturating_add(T::DbWeight::get().writes(2 as Weight))
 	}
 	// Storage: Promotion Admin (r:0 w:1)
 	fn set_admin_address() -> Weight {
-		(516_000 as Weight)
+		(553_000 as Weight)
 			.saturating_add(T::DbWeight::get().writes(1 as Weight))
 	}
+	// Storage: Promotion Admin (r:1 w:0)
+	fn payout_stakers() -> Weight {
+		(1_398_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(1 as Weight))
+	}
 	// Storage: System Account (r:1 w:1)
 	// Storage: Balances Locks (r:1 w:1)
 	// Storage: ParachainSystem ValidationData (r:1 w:0)
 	// Storage: Promotion Staked (r:1 w:1)
-	// Storage: Promotion TotalStaked (r:1 w:1)
 	fn stake() -> Weight {
-		(10_019_000 as Weight)
-			.saturating_add(T::DbWeight::get().reads(5 as Weight))
-			.saturating_add(T::DbWeight::get().writes(4 as Weight))
+		(9_506_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(4 as Weight))
+			.saturating_add(T::DbWeight::get().writes(3 as Weight))
 	}
-	// Storage: System Account (r:1 w:1)
-	// Storage: Balances Locks (r:1 w:1)
-	// Storage: ParachainSystem ValidationData (r:1 w:0)
-	// Storage: Promotion Staked (r:1 w:1)
-	// Storage: Promotion TotalStaked (r:1 w:1)
+	// Storage: System Account (r:1 w:0)
 	fn unstake() -> Weight {
-		(10_619_000 as Weight)
-			.saturating_add(T::DbWeight::get().reads(5 as Weight))
-			.saturating_add(T::DbWeight::get().writes(4 as Weight))
+		(2_529_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(1 as Weight))
 	}
-	// Storage: System Account (r:2 w:0)
-	// Storage: Promotion Staked (r:0 w:1)
+	// Storage: System Account (r:1 w:0)
 	fn recalculate_stake() -> Weight {
-		(4_932_000 as Weight)
+		(2_203_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(1 as Weight))
+	}
+	// Storage: Promotion Admin (r:1 w:0)
+	// Storage: Common CollectionById (r:1 w:1)
+	fn sponsor_collection() -> Weight {
+		(10_882_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(2 as Weight))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+	}
+	// Storage: Promotion Admin (r:1 w:0)
+	// Storage: Common CollectionById (r:1 w:1)
+	fn stop_sponsoring_collection() -> Weight {
+		(10_544_000 as Weight)
 			.saturating_add(T::DbWeight::get().reads(2 as Weight))
 			.saturating_add(T::DbWeight::get().writes(1 as Weight))
 	}
+	// Storage: Promotion Admin (r:1 w:0)
+	// Storage: EvmContractHelpers Sponsoring (r:0 w:1)
+	fn sponsor_contract() -> Weight {
+		(2_163_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(1 as Weight))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+	}
+	// Storage: Promotion Admin (r:1 w:0)
+	// Storage: EvmContractHelpers Sponsoring (r:1 w:1)
+	fn stop_sponsoring_contract() -> Weight {
+		(3_511_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(2 as Weight))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
+	}
 }
 
 // For backwards compatibility and tests
 impl WeightInfo for () {
-	// Storage: Promotion PendingUnstake (r:1 w:0)
-	// Storage: Promotion NextInterestBlock (r:1 w:0)
-	fn on_initialize() -> Weight {
-		(2_705_000 as Weight)
+	// Storage: Promotion StartBlock (r:1 w:1)
+	// Storage: ParachainSystem ValidationData (r:1 w:0)
+	// Storage: Promotion NextInterestBlock (r:0 w:1)
+	fn start_app_promotion() -> Weight {
+		(2_299_000 as Weight)
 			.saturating_add(RocksDbWeight::get().reads(2 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(2 as Weight))
 	}
 	// Storage: Promotion StartBlock (r:1 w:1)
 	// Storage: Promotion NextInterestBlock (r:0 w:1)
-	fn start_app_promotion() -> Weight {
-		(1_436_000 as Weight)
+	fn stop_app_promotion() -> Weight {
+		(1_733_000 as Weight)
 			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
 			.saturating_add(RocksDbWeight::get().writes(2 as Weight))
 	}
 	// Storage: Promotion Admin (r:0 w:1)
 	fn set_admin_address() -> Weight {
-		(516_000 as Weight)
+		(553_000 as Weight)
 			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
 	}
+	// Storage: Promotion Admin (r:1 w:0)
+	fn payout_stakers() -> Weight {
+		(1_398_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
+	}
 	// Storage: System Account (r:1 w:1)
 	// Storage: Balances Locks (r:1 w:1)
 	// Storage: ParachainSystem ValidationData (r:1 w:0)
 	// Storage: Promotion Staked (r:1 w:1)
-	// Storage: Promotion TotalStaked (r:1 w:1)
 	fn stake() -> Weight {
-		(10_019_000 as Weight)
-			.saturating_add(RocksDbWeight::get().reads(5 as Weight))
-			.saturating_add(RocksDbWeight::get().writes(4 as Weight))
+		(9_506_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(4 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(3 as Weight))
 	}
-	// Storage: System Account (r:1 w:1)
-	// Storage: Balances Locks (r:1 w:1)
-	// Storage: ParachainSystem ValidationData (r:1 w:0)
-	// Storage: Promotion Staked (r:1 w:1)
-	// Storage: Promotion TotalStaked (r:1 w:1)
+	// Storage: System Account (r:1 w:0)
 	fn unstake() -> Weight {
-		(10_619_000 as Weight)
-			.saturating_add(RocksDbWeight::get().reads(5 as Weight))
-			.saturating_add(RocksDbWeight::get().writes(4 as Weight))
+		(2_529_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
 	}
-	// Storage: System Account (r:2 w:0)
-	// Storage: Promotion Staked (r:0 w:1)
+	// Storage: System Account (r:1 w:0)
 	fn recalculate_stake() -> Weight {
-		(4_932_000 as Weight)
+		(2_203_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
+	}
+	// Storage: Promotion Admin (r:1 w:0)
+	// Storage: Common CollectionById (r:1 w:1)
+	fn sponsor_collection() -> Weight {
+		(10_882_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(2 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+	}
+	// Storage: Promotion Admin (r:1 w:0)
+	// Storage: Common CollectionById (r:1 w:1)
+	fn stop_sponsoring_collection() -> Weight {
+		(10_544_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(2 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+	}
+	// Storage: Promotion Admin (r:1 w:0)
+	// Storage: EvmContractHelpers Sponsoring (r:0 w:1)
+	fn sponsor_contract() -> Weight {
+		(2_163_000 as Weight)
+			.saturating_add(RocksDbWeight::get().reads(1 as Weight))
+			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
+	}
+	// Storage: Promotion Admin (r:1 w:0)
+	// Storage: EvmContractHelpers Sponsoring (r:1 w:1)
+	fn stop_sponsoring_contract() -> Weight {
+		(3_511_000 as Weight)
 			.saturating_add(RocksDbWeight::get().reads(2 as Weight))
 			.saturating_add(RocksDbWeight::get().writes(1 as Weight))
 	}
modifiedpallets/unique/src/benchmarking.rsdiffbeforeafterboth
--- a/pallets/unique/src/benchmarking.rs
+++ b/pallets/unique/src/benchmarking.rs
@@ -46,7 +46,9 @@
 	)?;
 	Ok(<pallet_common::CreatedCollectionCount<T>>::get())
 }
-fn create_nft_collection<T: Config>(owner: T::AccountId) -> Result<CollectionId, DispatchError> {
+pub fn create_nft_collection<T: Config>(
+	owner: T::AccountId,
+) -> Result<CollectionId, DispatchError> {
 	create_collection_helper::<T>(owner, CollectionMode::NFT)
 }
 
modifiedpallets/unique/src/lib.rsdiffbeforeafterboth
before · pallets/unique/src/lib.rs
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617//! # Unique Pallet18//!19//! A pallet governing Unique transactions.20//!21//! - [`Config`]22//! - [`Call`]23//! - [`Pallet`]24//!25//! ## Overview26//!27//! The Unique pallet's purpose is to be the primary interface between28//! external users and the inner structure of the Unique chains.29//!30//! It also contains an implementation of [`CollectionHelpers`][`eth`],31//! an Ethereum contract dealing with collection operations.32//!33//! ## Interface34//!35//! ### Dispatchables36//!37//! - `create_collection` - Create a collection of tokens. **Deprecated**, use `create_collection_ex`.38//! - `create_collection_ex` - Create a collection of tokens with explicit parameters.39//! - `destroy_collection` - Destroy a collection if no tokens exist within.40//! - `add_to_allow_list` - Add an address to allow list.41//! - `remove_from_allow_list` - Remove an address from allow list.42//! - `change_collection_owner` - Change the owner of the collection.43//! - `add_collection_admin` - Add an admin to a collection.44//! - `remove_collection_admin` - Remove admin of a collection.45//! - `set_collection_sponsor` - Invite a new collection sponsor.46//! - `confirm_sponsorship` - Confirm own sponsorship of a collection, becoming the sponsor.47//! - `remove_collection_sponsor` - Remove a sponsor from a collection.48//! - `create_item` - Create an item within a collection.49//! - `create_multiple_items` - Create multiple items within a collection.50//! - `set_collection_properties` - Add or change collection properties.51//! - `delete_collection_properties` - Delete specified collection properties.52//! - `set_token_properties` - Add or change token properties.53//! - `delete_token_properties` - Delete token properties.54//! - `set_token_property_permissions` - Add or change token property permissions of a collection.55//! - `create_multiple_items_ex` - Create multiple items within a collection with explicitly specified initial parameters.56//! - `set_transfers_enabled_flag` - Completely allow or disallow transfers for a particular collection.57//! - `burn_item` - Destroy an item.58//! - `burn_from` - Destroy an item on behalf of the owner as a non-owner account.59//! - `transfer` - Change ownership of the token.60//! - `transfer_from` - Change ownership of the token on behalf of the owner as a non-owner account.61//! - `approve` - Allow a non-permissioned address to transfer or burn an item.62//! - `set_collection_limits` - Set specific limits of a collection.63//! - `set_collection_permissions` - Set specific permissions of a collection.64//! - `repartition` - Re-partition a refungible token, while owning all of its parts.6566#![recursion_limit = "1024"]67#![cfg_attr(not(feature = "std"), no_std)]68#![allow(69	clippy::too_many_arguments,70	clippy::unnecessary_mut_passed,71	clippy::unused_unit72)]7374extern crate alloc;7576use frame_support::{77	decl_module, decl_storage, decl_error, decl_event,78	dispatch::DispatchResult,79	ensure, fail,80	weights::{Weight},81	pallet_prelude::{DispatchResultWithPostInfo, ConstU32},82	BoundedVec,83};84use scale_info::TypeInfo;85use frame_system::{self as system, ensure_signed};86use sp_runtime::{sp_std::prelude::Vec};87use up_data_structs::{88	MAX_COLLECTION_NAME_LENGTH, MAX_COLLECTION_DESCRIPTION_LENGTH, MAX_TOKEN_PREFIX_LENGTH,89	CreateItemData, CollectionLimits, CollectionPermissions, CollectionId, CollectionMode, TokenId,90	SponsorshipState, CreateCollectionData, CreateItemExData, budget, Property, PropertyKey,91	PropertyKeyPermission,92};93use pallet_evm::account::CrossAccountId;94use pallet_common::{95	CollectionHandle, Pallet as PalletCommon, CommonWeightInfo, dispatch::dispatch_tx,96	dispatch::CollectionDispatch, RefungibleExtensionsWeightInfo,97};98pub mod eth;99100#[cfg(feature = "runtime-benchmarks")]101mod benchmarking;102pub mod weights;103use weights::WeightInfo;104105/// Maximum number of levels of depth in the token nesting tree.106pub const NESTING_BUDGET: u32 = 5;107108decl_error! {109	/// Errors for the common Unique transactions.110	pub enum Error for Module<T: Config> {111		/// Decimal_points parameter must be lower than [`up_data_structs::MAX_DECIMAL_POINTS`].112		CollectionDecimalPointLimitExceeded,113		/// This address is not set as sponsor, use setCollectionSponsor first.114		ConfirmUnsetSponsorFail,115		/// Length of items properties must be greater than 0.116		EmptyArgument,117		/// Repertition is only supported by refungible collection.118		RepartitionCalledOnNonRefungibleCollection,119	}120}121122/// Configuration trait of this pallet.123pub trait Config: system::Config + pallet_common::Config + Sized + TypeInfo {124	/// Overarching event type.125	type Event: From<Event<Self>> + Into<<Self as frame_system::Config>::Event>;126127	/// Weight information for extrinsics in this pallet.128	type WeightInfo: WeightInfo;129130	/// Weight information for common pallet operations.131	type CommonWeightInfo: CommonWeightInfo<Self::CrossAccountId>;132133	/// Weight info information for extra refungible pallet operations.134	type RefungibleExtensionsWeightInfo: RefungibleExtensionsWeightInfo;135}136137decl_event! {138	pub enum Event<T>139	where140		<T as frame_system::Config>::AccountId,141		<T as pallet_evm::account::Config>::CrossAccountId,142	{143		/// Collection sponsor was removed144		///145		/// # Arguments146		/// * collection_id: ID of the affected collection.147		CollectionSponsorRemoved(CollectionId),148149		/// Collection admin was added150		///151		/// # Arguments152		/// * collection_id: ID of the affected collection.153		/// * admin: Admin address.154		CollectionAdminAdded(CollectionId, CrossAccountId),155156		/// Collection owned was changed157		///158		/// # Arguments159		/// * collection_id: ID of the affected collection.160		/// * owner: New owner address.161		CollectionOwnedChanged(CollectionId, AccountId),162163		/// Collection sponsor was set164		///165		/// # Arguments166		/// * collection_id: ID of the affected collection.167		/// * owner: New sponsor address.168		CollectionSponsorSet(CollectionId, AccountId),169170		/// New sponsor was confirm171		///172		/// # Arguments173		/// * collection_id: ID of the affected collection.174		/// * sponsor: New sponsor address.175		SponsorshipConfirmed(CollectionId, AccountId),176177		/// Collection admin was removed178		///179		/// # Arguments180		/// * collection_id: ID of the affected collection.181		/// * admin: Removed admin address.182		CollectionAdminRemoved(CollectionId, CrossAccountId),183184		/// Address was removed from the allow list185		///186		/// # Arguments187		/// * collection_id: ID of the affected collection.188		/// * user: Address of the removed account.189		AllowListAddressRemoved(CollectionId, CrossAccountId),190191		/// Address was added to the allow list192		///193		/// # Arguments194		/// * collection_id: ID of the affected collection.195		/// * user: Address of the added account.196		AllowListAddressAdded(CollectionId, CrossAccountId),197198		/// Collection limits were set199		///200		/// # Arguments201		/// * collection_id: ID of the affected collection.202		CollectionLimitSet(CollectionId),203204		/// Collection permissions were set205		///206		/// # Arguments207		/// * collection_id: ID of the affected collection.208		CollectionPermissionSet(CollectionId),209	}210}211212type SelfWeightOf<T> = <T as Config>::WeightInfo;213214// # Used definitions215//216// ## User control levels217//218// chain-controlled - key is uncontrolled by user219//                    i.e autoincrementing index220//                    can use non-cryptographic hash221// real - key is controlled by user222//        but it is hard to generate enough colliding values, i.e owner of signed txs223//        can use non-cryptographic hash224// controlled - key is completly controlled by users225//              i.e maps with mutable keys226//              should use cryptographic hash227//228// ## User control level downgrade reasons229//230// ?1 - chain-controlled -> controlled231//      collections/tokens can be destroyed, resulting in massive holes232// ?2 - chain-controlled -> controlled233//      same as ?1, but can be only added, resulting in easier exploitation234// ?3 - real -> controlled235//      no confirmation required, so addresses can be easily generated236decl_storage! {237	trait Store for Module<T: Config> as Unique {238239		//#region Private members240		/// Used for migrations241		ChainVersion: u64;242		//#endregion243244		//#region Tokens transfer sponosoring rate limit baskets245		/// (Collection id (controlled?2), who created (real))246		/// TODO: Off chain worker should remove from this map when collection gets removed247		pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => Option<T::BlockNumber>;248		/// Collection id (controlled?2), token id (controlled?2)249		pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;250		/// Collection id (controlled?2), owning user (real)251		pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;252		/// Collection id (controlled?2), token id (controlled?2)253		pub ReFungibleTransferBasket get(fn refungible_transfer_basket): nmap hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;254		//#endregion255256		/// Variable metadata sponsoring257		/// Collection id (controlled?2), token id (controlled?2)258		#[deprecated]259		pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;260		/// Last sponsoring of token property setting // todo:doc rephrase this and the following261		pub TokenPropertyBasket get(fn token_property_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;262263		/// Last sponsoring of NFT approval in a collection264		pub NftApproveBasket get(fn nft_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;265		/// Last sponsoring of fungible tokens approval in a collection266		pub FungibleApproveBasket get(fn fungible_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;267		/// Last sponsoring of RFT approval in a collection268		pub RefungibleApproveBasket get(fn refungible_approve_basket): nmap hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;269	}270}271272decl_module! {273	/// Type alias to Pallet, to be used by construct_runtime.274	pub struct Module<T: Config> for enum Call275	where276		origin: T::Origin277	{278		type Error = Error<T>;279280		pub fn deposit_event() = default;281282		fn on_initialize(_now: T::BlockNumber) -> Weight {283			0284		}285286		fn on_runtime_upgrade() -> Weight {287			0288		}289290		/// Create a collection of tokens.291		///292		/// Each Token may have multiple properties encoded as an array of bytes293		/// of certain length. The initial owner of the collection is set294		/// to the address that signed the transaction and can be changed later.295		///296		/// Prefer the more advanced [`create_collection_ex`][`Pallet::create_collection_ex`] instead.297		///298		/// # Permissions299		///300		/// * Anyone - becomes the owner of the new collection.301		///302		/// # Arguments303		///304		/// * `collection_name`: Wide-character string with collection name305		/// (limit [`MAX_COLLECTION_NAME_LENGTH`]).306		/// * `collection_description`: Wide-character string with collection description307		/// (limit [`MAX_COLLECTION_DESCRIPTION_LENGTH`]).308		/// * `token_prefix`: Byte string containing the token prefix to mark a collection309		/// to which a token belongs (limit [`MAX_TOKEN_PREFIX_LENGTH`]).310		/// * `mode`: Type of items stored in the collection and type dependent data.311		// returns collection ID312		#[weight = <SelfWeightOf<T>>::create_collection()]313		#[deprecated(note = "`create_collection_ex` is more up-to-date and advanced, prefer it instead")]314		pub fn create_collection(315			origin,316			collection_name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,317			collection_description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,318			token_prefix: BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>,319			mode: CollectionMode320		) -> DispatchResult {321			let data: CreateCollectionData<T::AccountId> = CreateCollectionData {322				name: collection_name,323				description: collection_description,324				token_prefix,325				mode,326				..Default::default()327			};328			Self::create_collection_ex(origin, data)329		}330331		/// Create a collection with explicit parameters.332		///333		/// Prefer it to the deprecated [`create_collection`][`Pallet::create_collection`] method.334		///335		/// # Permissions336		///337		/// * Anyone - becomes the owner of the new collection.338		///339		/// # Arguments340		///341		/// * `data`: Explicit data of a collection used for its creation.342		#[weight = <SelfWeightOf<T>>::create_collection()]343		pub fn create_collection_ex(origin, data: CreateCollectionData<T::AccountId>) -> DispatchResult {344			let sender = ensure_signed(origin)?;345346			// =========347348			let _id = T::CollectionDispatch::create(T::CrossAccountId::from_sub(sender), data)?;349350			Ok(())351		}352353		/// Destroy a collection if no tokens exist within.354		///355		/// # Permissions356		///357		/// * Collection owner358		///359		/// # Arguments360		///361		/// * `collection_id`: Collection to destroy.362		#[weight = <SelfWeightOf<T>>::destroy_collection()]363		pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {364			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);365			let collection = <CollectionHandle<T>>::try_get(collection_id)?;366			collection.check_is_internal()?;367368			// =========369370			T::CollectionDispatch::destroy(sender, collection)?;371372			// TODO: basket cleanup should be moved elsewhere373			// Maybe runtime dispatch.rs should perform it?374375			let _ = <NftTransferBasket<T>>::clear_prefix(collection_id, u32::MAX, None);376			let _ = <FungibleTransferBasket<T>>::clear_prefix(collection_id, u32::MAX, None);377			let _ = <ReFungibleTransferBasket<T>>::clear_prefix((collection_id,), u32::MAX, None);378379			let _ = <NftApproveBasket<T>>::clear_prefix(collection_id, u32::MAX, None);380			let _ = <FungibleApproveBasket<T>>::clear_prefix(collection_id, u32::MAX, None);381			let _ = <RefungibleApproveBasket<T>>::clear_prefix((collection_id,), u32::MAX, None);382383			Ok(())384		}385386		/// Add an address to allow list.387		///388		/// # Permissions389		///390		/// * Collection owner391		/// * Collection admin392		///393		/// # Arguments394		///395		/// * `collection_id`: ID of the modified collection.396		/// * `address`: ID of the address to be added to the allowlist.397		#[weight = <SelfWeightOf<T>>::add_to_allow_list()]398		pub fn add_to_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{399400			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);401			let collection = <CollectionHandle<T>>::try_get(collection_id)?;402			collection.check_is_internal()?;403404			<PalletCommon<T>>::toggle_allowlist(405				&collection,406				&sender,407				&address,408				true,409			)?;410411			Self::deposit_event(Event::<T>::AllowListAddressAdded(412				collection_id,413				address414			));415416			Ok(())417		}418419		/// Remove an address from allow list.420		///421		/// # Permissions422		///423		/// * Collection owner424		/// * Collection admin425		///426		/// # Arguments427		///428		/// * `collection_id`: ID of the modified collection.429		/// * `address`: ID of the address to be removed from the allowlist.430		#[weight = <SelfWeightOf<T>>::remove_from_allow_list()]431		pub fn remove_from_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{432433			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);434			let collection = <CollectionHandle<T>>::try_get(collection_id)?;435			collection.check_is_internal()?;436437			<PalletCommon<T>>::toggle_allowlist(438				&collection,439				&sender,440				&address,441				false,442			)?;443444			<Pallet<T>>::deposit_event(Event::<T>::AllowListAddressRemoved(445				collection_id,446				address447			));448449			Ok(())450		}451452		/// Change the owner of the collection.453		///454		/// # Permissions455		///456		/// * Collection owner457		///458		/// # Arguments459		///460		/// * `collection_id`: ID of the modified collection.461		/// * `new_owner`: ID of the account that will become the owner.462		#[weight = <SelfWeightOf<T>>::change_collection_owner()]463		pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {464465			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);466467			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;468			target_collection.check_is_internal()?;469			target_collection.check_is_owner(&sender)?;470471			target_collection.owner = new_owner.clone();472			<Pallet<T>>::deposit_event(Event::<T>::CollectionOwnedChanged(473				collection_id,474				new_owner475			));476477			target_collection.save()478		}479480		/// Add an admin to a collection.481		///482		/// NFT Collection can be controlled by multiple admin addresses483		/// (some which can also be servers, for example). Admins can issue484		/// and burn NFTs, as well as add and remove other admins,485		/// but cannot change NFT or Collection ownership.486		///487		/// # Permissions488		///489		/// * Collection owner490		/// * Collection admin491		///492		/// # Arguments493		///494		/// * `collection_id`: ID of the Collection to add an admin for.495		/// * `new_admin`: Address of new admin to add.496		#[weight = <SelfWeightOf<T>>::add_collection_admin()]497		pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {498			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);499			let collection = <CollectionHandle<T>>::try_get(collection_id)?;500			collection.check_is_internal()?;501502			<Pallet<T>>::deposit_event(Event::<T>::CollectionAdminAdded(503				collection_id,504				new_admin_id.clone()505			));506507			<PalletCommon<T>>::toggle_admin(&collection, &sender, &new_admin_id, true)508		}509510		/// Remove admin of a collection.511		///512		/// An admin address can remove itself. List of admins may become empty,513		/// in which case only Collection Owner will be able to add an Admin.514		///515		/// # Permissions516		///517		/// * Collection owner518		/// * Collection admin519		///520		/// # Arguments521		///522		/// * `collection_id`: ID of the collection to remove the admin for.523		/// * `account_id`: Address of the admin to remove.524		#[weight = <SelfWeightOf<T>>::remove_collection_admin()]525		pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {526			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);527			let collection = <CollectionHandle<T>>::try_get(collection_id)?;528			collection.check_is_internal()?;529530			<Pallet<T>>::deposit_event(Event::<T>::CollectionAdminRemoved(531				collection_id,532				account_id.clone()533			));534535			<PalletCommon<T>>::toggle_admin(&collection, &sender, &account_id, false)536		}537538		/// Set (invite) a new collection sponsor.539		///540		/// If successful, confirmation from the sponsor-to-be will be pending.541		///542		/// # Permissions543		///544		/// * Collection owner545		/// * Collection admin546		///547		/// # Arguments548		///549		/// * `collection_id`: ID of the modified collection.550		/// * `new_sponsor`: ID of the account of the sponsor-to-be.551		#[weight = <SelfWeightOf<T>>::set_collection_sponsor()]552		pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {553			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);554555			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;556			target_collection.check_is_owner_or_admin(&sender)?;557			target_collection.check_is_internal()?;558559			target_collection.set_sponsor(new_sponsor.clone())?;560561			<Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(562				collection_id,563				new_sponsor564			));565566			target_collection.save()567		}568569		/// Confirm own sponsorship of a collection, becoming the sponsor.570		///571		/// An invitation must be pending, see [`set_collection_sponsor`][`Pallet::set_collection_sponsor`].572		/// Sponsor can pay the fees of a transaction instead of the sender,573		/// but only within specified limits.574		///575		/// # Permissions576		///577		/// * Sponsor-to-be578		///579		/// # Arguments580		///581		/// * `collection_id`: ID of the collection with the pending sponsor.582		#[weight = <SelfWeightOf<T>>::confirm_sponsorship()]583		pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {584			let sender = ensure_signed(origin)?;585586			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;587			target_collection.check_is_internal()?;588			ensure!(589				target_collection.confirm_sponsorship(&sender)?,590				Error::<T>::ConfirmUnsetSponsorFail591			);592593			<Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(594				collection_id,595				sender596			));597598			target_collection.save()599		}600601		/// Remove a collection's a sponsor, making everyone pay for their own transactions.602		///603		/// # Permissions604		///605		/// * Collection owner606		///607		/// # Arguments608		///609		/// * `collection_id`: ID of the collection with the sponsor to remove.610		#[weight = <SelfWeightOf<T>>::remove_collection_sponsor()]611		pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {612			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);613614			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;615			target_collection.check_is_internal()?;616			target_collection.check_is_owner(&sender)?;617618			target_collection.sponsorship = SponsorshipState::Disabled;619620			<Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(621				collection_id622			));623			target_collection.save()624		}625626		/// Mint an item within a collection.627		///628		/// A collection must exist first, see [`create_collection_ex`][`Pallet::create_collection_ex`].629		///630		/// # Permissions631		///632		/// * Collection owner633		/// * Collection admin634		/// * Anyone if635		///     * Allow List is enabled, and636		///     * Address is added to allow list, and637		///     * MintPermission is enabled (see [`set_collection_permissions`][`Pallet::set_collection_permissions`])638		///639		/// # Arguments640		///641		/// * `collection_id`: ID of the collection to which an item would belong.642		/// * `owner`: Address of the initial owner of the item.643		/// * `data`: Token data describing the item to store on chain.644		#[weight = T::CommonWeightInfo::create_item()]645		pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResultWithPostInfo {646			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);647			let budget = budget::Value::new(NESTING_BUDGET);648649			dispatch_tx::<T, _>(collection_id, |d| d.create_item(sender, owner, data, &budget))650		}651652		/// Create multiple items within a collection.653		///654		/// A collection must exist first, see [`create_collection_ex`][`Pallet::create_collection_ex`].655		///656		/// # Permissions657		///658		/// * Collection owner659		/// * Collection admin660		/// * Anyone if661		///     * Allow List is enabled, and662		///     * Address is added to the allow list, and663		///     * MintPermission is enabled (see [`set_collection_permissions`][`Pallet::set_collection_permissions`])664		///665		/// # Arguments666		///667		/// * `collection_id`: ID of the collection to which the tokens would belong.668		/// * `owner`: Address of the initial owner of the tokens.669		/// * `items_data`: Vector of data describing each item to be created.670		#[weight = T::CommonWeightInfo::create_multiple_items(&items_data)]671		pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResultWithPostInfo {672			ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);673			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);674			let budget = budget::Value::new(NESTING_BUDGET);675676			dispatch_tx::<T, _>(collection_id, |d| d.create_multiple_items(sender, owner, items_data, &budget))677		}678679		/// Add or change collection properties.680		///681		/// # Permissions682		///683		/// * Collection owner684		/// * Collection admin685		///686		/// # Arguments687		///688		/// * `collection_id`: ID of the modified collection.689		/// * `properties`: Vector of key-value pairs stored as the collection's metadata.690		/// Keys support Latin letters, `-`, `_`, and `.` as symbols.691		#[weight = T::CommonWeightInfo::set_collection_properties(properties.len() as u32)]692		pub fn set_collection_properties(693			origin,694			collection_id: CollectionId,695			properties: Vec<Property>696		) -> DispatchResultWithPostInfo {697			ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);698699			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);700701			dispatch_tx::<T, _>(collection_id, |d| d.set_collection_properties(sender, properties))702		}703704		/// Delete specified collection properties.705		///706		/// # Permissions707		///708		/// * Collection Owner709		/// * Collection Admin710		///711		/// # Arguments712		///713		/// * `collection_id`: ID of the modified collection.714		/// * `property_keys`: Vector of keys of the properties to be deleted.715		/// Keys support Latin letters, `-`, `_`, and `.` as symbols.716		#[weight = T::CommonWeightInfo::delete_collection_properties(property_keys.len() as u32)]717		pub fn delete_collection_properties(718			origin,719			collection_id: CollectionId,720			property_keys: Vec<PropertyKey>,721		) -> DispatchResultWithPostInfo {722			ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);723724			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);725726			dispatch_tx::<T, _>(collection_id, |d| d.delete_collection_properties(&sender, property_keys))727		}728729		/// Add or change token properties according to collection's permissions.730		/// Currently properties only work with NFTs.731		///732		/// # Permissions733		///734		/// * Depends on collection's token property permissions and specified property mutability:735		/// 	* Collection owner736		/// 	* Collection admin737		/// 	* Token owner738		///739		/// See [`set_token_property_permissions`][`Pallet::set_token_property_permissions`].740		///741		/// # Arguments742		///743		/// * `collection_id: ID of the collection to which the token belongs.744		/// * `token_id`: ID of the modified token.745		/// * `properties`: Vector of key-value pairs stored as the token's metadata.746		/// Keys support Latin letters, `-`, `_`, and `.` as symbols.747		#[weight = T::CommonWeightInfo::set_token_properties(properties.len() as u32)]748		pub fn set_token_properties(749			origin,750			collection_id: CollectionId,751			token_id: TokenId,752			properties: Vec<Property>753		) -> DispatchResultWithPostInfo {754			ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);755756			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);757			let budget = budget::Value::new(NESTING_BUDGET);758759			dispatch_tx::<T, _>(collection_id, |d| d.set_token_properties(sender, token_id, properties, &budget))760		}761762		/// Delete specified token properties. Currently properties only work with NFTs.763		///764		/// # Permissions765		///766		/// * Depends on collection's token property permissions and specified property mutability:767		/// 	* Collection owner768		/// 	* Collection admin769		/// 	* Token owner770		///771		/// # Arguments772		///773		/// * `collection_id`: ID of the collection to which the token belongs.774		/// * `token_id`: ID of the modified token.775		/// * `property_keys`: Vector of keys of the properties to be deleted.776		/// Keys support Latin letters, `-`, `_`, and `.` as symbols.777		#[weight = T::CommonWeightInfo::delete_token_properties(property_keys.len() as u32)]778		pub fn delete_token_properties(779			origin,780			collection_id: CollectionId,781			token_id: TokenId,782			property_keys: Vec<PropertyKey>783		) -> DispatchResultWithPostInfo {784			ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);785786			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);787			let budget = budget::Value::new(NESTING_BUDGET);788789			dispatch_tx::<T, _>(collection_id, |d| d.delete_token_properties(sender, token_id, property_keys, &budget))790		}791792		/// Add or change token property permissions of a collection.793		///794		/// Without a permission for a particular key, a property with that key795		/// cannot be created in a token.796		///797		/// # Permissions798		///799		/// * Collection owner800		/// * Collection admin801		///802		/// # Arguments803		///804		/// * `collection_id`: ID of the modified collection.805		/// * `property_permissions`: Vector of permissions for property keys.806		/// Keys support Latin letters, `-`, `_`, and `.` as symbols.807		#[weight = T::CommonWeightInfo::set_token_property_permissions(property_permissions.len() as u32)]808		pub fn set_token_property_permissions(809			origin,810			collection_id: CollectionId,811			property_permissions: Vec<PropertyKeyPermission>,812		) -> DispatchResultWithPostInfo {813			ensure!(!property_permissions.is_empty(), Error::<T>::EmptyArgument);814815			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);816817			dispatch_tx::<T, _>(collection_id, |d| d.set_token_property_permissions(&sender, property_permissions))818		}819820		/// Create multiple items within a collection with explicitly specified initial parameters.821		///822		/// # Permissions823		///824		/// * Collection owner825		/// * Collection admin826		/// * Anyone if827		///     * Allow List is enabled, and828		///     * Address is added to allow list, and829		///     * MintPermission is enabled (see [`set_collection_permissions`][`Pallet::set_collection_permissions`])830		///831		/// # Arguments832		///833		/// * `collection_id`: ID of the collection to which the tokens would belong.834		/// * `data`: Explicit item creation data.835		#[weight = T::CommonWeightInfo::create_multiple_items_ex(&data)]836		pub fn create_multiple_items_ex(origin, collection_id: CollectionId, data: CreateItemExData<T::CrossAccountId>) -> DispatchResultWithPostInfo {837			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);838			let budget = budget::Value::new(NESTING_BUDGET);839840			dispatch_tx::<T, _>(collection_id, |d| d.create_multiple_items_ex(sender, data, &budget))841		}842843		/// Completely allow or disallow transfers for a particular collection.844		///845		/// # Permissions846		///847		/// * Collection owner848		///849		/// # Arguments850		///851		/// * `collection_id`: ID of the collection.852		/// * `value`: New value of the flag, are transfers allowed?853		#[weight = <SelfWeightOf<T>>::set_transfers_enabled_flag()]854		pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {855			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);856			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;857			target_collection.check_is_internal()?;858			target_collection.check_is_owner(&sender)?;859860			// =========861862			target_collection.limits.transfers_enabled = Some(value);863			target_collection.save()864		}865866		/// Destroy an item.867		///868		/// # Permissions869		///870		/// * Collection owner871		/// * Collection admin872		/// * Current item owner873		///874		/// # Arguments875		///876		/// * `collection_id`: ID of the collection to which the item belongs.877		/// * `item_id`: ID of item to burn.878		/// * `value`: Number of pieces of the item to destroy.879		/// 	* Non-Fungible Mode: An NFT is indivisible, there is always 1 corresponding to an ID.880		///     * Fungible Mode: The desired number of pieces to burn.881		///     * Re-Fungible Mode: The desired number of pieces to burn.882		#[weight = T::CommonWeightInfo::burn_item()]883		pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {884			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);885886			let post_info = dispatch_tx::<T, _>(collection_id, |d| d.burn_item(sender, item_id, value))?;887			if value == 1 {888				<NftTransferBasket<T>>::remove(collection_id, item_id);889				<NftApproveBasket<T>>::remove(collection_id, item_id);890			}891			// Those maps should be cleared only if token disappears completly, need to move this part of logic to pallets?892			// <FungibleApproveBasket<T>>::remove(collection_id, sender.as_sub());893			// <RefungibleApproveBasket<T>>::remove((collection_id, item_id, sender.as_sub()));894			Ok(post_info)895		}896897		/// Destroy a token on behalf of the owner as a non-owner account.898		///899		/// See also: [`approve`][`Pallet::approve`].900		///901		/// After this method executes, one approval is removed from the total so that902		/// the approved address will not be able to transfer this item again from this owner.903		///904		/// # Permissions905		///906		/// * Collection owner907		/// * Collection admin908		/// * Current token owner909		/// * Address approved by current item owner910		///911		/// # Arguments912		///913		/// * `from`: The owner of the burning item.914		/// * `collection_id`: ID of the collection to which the item belongs.915		/// * `item_id`: ID of item to burn.916		/// * `value`: Number of pieces to burn.917		/// 	* Non-Fungible Mode: An NFT is indivisible, there is always 1 corresponding to an ID.918		///     * Fungible Mode: The desired number of pieces to burn.919		///     * Re-Fungible Mode: The desired number of pieces to burn.920		#[weight = T::CommonWeightInfo::burn_from()]921		pub fn burn_from(origin, collection_id: CollectionId, from: T::CrossAccountId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {922			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);923			let budget = budget::Value::new(NESTING_BUDGET);924925			dispatch_tx::<T, _>(collection_id, |d| d.burn_from(sender, from, item_id, value, &budget))926		}927928		/// Change ownership of the token.929		///930		/// # Permissions931		///932		/// * Collection owner933		/// * Collection admin934		/// * Current token owner935		///936		/// # Arguments937		///938		/// * `recipient`: Address of token recipient.939		/// * `collection_id`: ID of the collection the item belongs to.940		/// * `item_id`: ID of the item.941		///     * Non-Fungible Mode: Required.942		///     * Fungible Mode: Ignored.943		///     * Re-Fungible Mode: Required.944		///945		/// * `value`: Amount to transfer.946		/// 	* Non-Fungible Mode: An NFT is indivisible, there is always 1 corresponding to an ID.947		///     * Fungible Mode: The desired number of pieces to transfer.948		///     * Re-Fungible Mode: The desired number of pieces to transfer.949		#[weight = T::CommonWeightInfo::transfer()]950		pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {951			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);952			let budget = budget::Value::new(NESTING_BUDGET);953954			dispatch_tx::<T, _>(collection_id, |d| d.transfer(sender, recipient, item_id, value, &budget))955		}956957		/// Allow a non-permissioned address to transfer or burn an item.958		///959		/// # Permissions960		///961		/// * Collection owner962		/// * Collection admin963		/// * Current item owner964		///965		/// # Arguments966		///967		/// * `spender`: Account to be approved to make specific transactions on non-owned tokens.968		/// * `collection_id`: ID of the collection the item belongs to.969		/// * `item_id`: ID of the item transactions on which are now approved.970		/// * `amount`: Number of pieces of the item approved for a transaction (maximum of 1 for NFTs).971		/// Set to 0 to revoke the approval.972		#[weight = T::CommonWeightInfo::approve()]973		pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResultWithPostInfo {974			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);975976			dispatch_tx::<T, _>(collection_id, |d| d.approve(sender, spender, item_id, amount))977		}978979		/// Change ownership of an item on behalf of the owner as a non-owner account.980		///981		/// See the [`approve`][`Pallet::approve`] method for additional information.982		///983		/// After this method executes, one approval is removed from the total so that984		/// the approved address will not be able to transfer this item again from this owner.985		///986		/// # Permissions987		///988		/// * Collection owner989		/// * Collection admin990		/// * Current item owner991		/// * Address approved by current item owner992		///993		/// # Arguments994		///995		/// * `from`: Address that currently owns the token.996		/// * `recipient`: Address of the new token-owner-to-be.997		/// * `collection_id`: ID of the collection the item.998		/// * `item_id`: ID of the item to be transferred.999		/// * `value`: Amount to transfer.1000		/// 	* Non-Fungible Mode: An NFT is indivisible, there is always 1 corresponding to an ID.1001		///     * Fungible Mode: The desired number of pieces to transfer.1002		///     * Re-Fungible Mode: The desired number of pieces to transfer.1003		#[weight = T::CommonWeightInfo::transfer_from()]1004		pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResultWithPostInfo {1005			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1006			let budget = budget::Value::new(NESTING_BUDGET);10071008			dispatch_tx::<T, _>(collection_id, |d| d.transfer_from(sender, from, recipient, item_id, value, &budget))1009		}10101011		/// Set specific limits of a collection. Empty, or None fields mean chain default.1012		///1013		/// # Permissions1014		///1015		/// * Collection owner1016		/// * Collection admin1017		///1018		/// # Arguments1019		///1020		/// * `collection_id`: ID of the modified collection.1021		/// * `new_limit`: New limits of the collection. Fields that are not set (None)1022		/// will not overwrite the old ones.1023		#[weight = <SelfWeightOf<T>>::set_collection_limits()]1024		pub fn set_collection_limits(1025			origin,1026			collection_id: CollectionId,1027			new_limit: CollectionLimits,1028		) -> DispatchResult {1029			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1030			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1031			target_collection.check_is_internal()?;1032			target_collection.check_is_owner_or_admin(&sender)?;1033			let old_limit = &target_collection.limits;10341035			target_collection.limits = <PalletCommon<T>>::clamp_limits(target_collection.mode.clone(), &old_limit, new_limit)?;10361037			<Pallet<T>>::deposit_event(Event::<T>::CollectionLimitSet(1038				collection_id1039			));10401041			target_collection.save()1042		}10431044		/// Set specific permissions of a collection. Empty, or None fields mean chain default.1045		///1046		/// # Permissions1047		///1048		/// * Collection owner1049		/// * Collection admin1050		///1051		/// # Arguments1052		///1053		/// * `collection_id`: ID of the modified collection.1054		/// * `new_permission`: New permissions of the collection. Fields that are not set (None)1055		/// will not overwrite the old ones.1056		#[weight = <SelfWeightOf<T>>::set_collection_limits()]1057		pub fn set_collection_permissions(1058			origin,1059			collection_id: CollectionId,1060			new_permission: CollectionPermissions,1061		) -> DispatchResult {1062			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1063			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1064			target_collection.check_is_internal()?;1065			target_collection.check_is_owner_or_admin(&sender)?;1066			let old_limit = &target_collection.permissions;10671068			target_collection.permissions = <PalletCommon<T>>::clamp_permissions(target_collection.mode.clone(), &old_limit, new_permission)?;10691070			<Pallet<T>>::deposit_event(Event::<T>::CollectionPermissionSet(1071				collection_id1072			));10731074			target_collection.save()1075		}10761077		/// Re-partition a refungible token, while owning all of its parts/pieces.1078		///1079		/// # Permissions1080		///1081		/// * Token owner (must own every part)1082		///1083		/// # Arguments1084		///1085		/// * `collection_id`: ID of the collection the RFT belongs to.1086		/// * `token_id`: ID of the RFT.1087		/// * `amount`: New number of parts/pieces into which the token shall be partitioned.1088		#[weight = T::RefungibleExtensionsWeightInfo::repartition()]1089		pub fn repartition(1090			origin,1091			collection_id: CollectionId,1092			token_id: TokenId,1093			amount: u128,1094		) -> DispatchResultWithPostInfo {1095			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1096			dispatch_tx::<T, _>(collection_id, |d| {1097				if let Some(refungible_extensions) = d.refungible_extensions() {1098					refungible_extensions.repartition(&sender, token_id, amount)1099				} else {1100					fail!(<Error<T>>::RepartitionCalledOnNonRefungibleCollection)1101				}1102			})1103		}1104	}1105}
after · pallets/unique/src/lib.rs
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617//! # Unique Pallet18//!19//! A pallet governing Unique transactions.20//!21//! - [`Config`]22//! - [`Call`]23//! - [`Pallet`]24//!25//! ## Overview26//!27//! The Unique pallet's purpose is to be the primary interface between28//! external users and the inner structure of the Unique chains.29//!30//! It also contains an implementation of [`CollectionHelpers`][`eth`],31//! an Ethereum contract dealing with collection operations.32//!33//! ## Interface34//!35//! ### Dispatchables36//!37//! - `create_collection` - Create a collection of tokens. **Deprecated**, use `create_collection_ex`.38//! - `create_collection_ex` - Create a collection of tokens with explicit parameters.39//! - `destroy_collection` - Destroy a collection if no tokens exist within.40//! - `add_to_allow_list` - Add an address to allow list.41//! - `remove_from_allow_list` - Remove an address from allow list.42//! - `change_collection_owner` - Change the owner of the collection.43//! - `add_collection_admin` - Add an admin to a collection.44//! - `remove_collection_admin` - Remove admin of a collection.45//! - `set_collection_sponsor` - Invite a new collection sponsor.46//! - `confirm_sponsorship` - Confirm own sponsorship of a collection, becoming the sponsor.47//! - `remove_collection_sponsor` - Remove a sponsor from a collection.48//! - `create_item` - Create an item within a collection.49//! - `create_multiple_items` - Create multiple items within a collection.50//! - `set_collection_properties` - Add or change collection properties.51//! - `delete_collection_properties` - Delete specified collection properties.52//! - `set_token_properties` - Add or change token properties.53//! - `delete_token_properties` - Delete token properties.54//! - `set_token_property_permissions` - Add or change token property permissions of a collection.55//! - `create_multiple_items_ex` - Create multiple items within a collection with explicitly specified initial parameters.56//! - `set_transfers_enabled_flag` - Completely allow or disallow transfers for a particular collection.57//! - `burn_item` - Destroy an item.58//! - `burn_from` - Destroy an item on behalf of the owner as a non-owner account.59//! - `transfer` - Change ownership of the token.60//! - `transfer_from` - Change ownership of the token on behalf of the owner as a non-owner account.61//! - `approve` - Allow a non-permissioned address to transfer or burn an item.62//! - `set_collection_limits` - Set specific limits of a collection.63//! - `set_collection_permissions` - Set specific permissions of a collection.64//! - `repartition` - Re-partition a refungible token, while owning all of its parts.6566#![recursion_limit = "1024"]67#![cfg_attr(not(feature = "std"), no_std)]68#![allow(69	clippy::too_many_arguments,70	clippy::unnecessary_mut_passed,71	clippy::unused_unit72)]7374extern crate alloc;7576use frame_support::{77	decl_module, decl_storage, decl_error, decl_event,78	dispatch::DispatchResult,79	ensure, fail,80	weights::{Weight},81	pallet_prelude::{DispatchResultWithPostInfo, ConstU32},82	BoundedVec,83};84use scale_info::TypeInfo;85use frame_system::{self as system, ensure_signed};86use sp_runtime::{sp_std::prelude::Vec};87use up_data_structs::{88	MAX_COLLECTION_NAME_LENGTH, MAX_COLLECTION_DESCRIPTION_LENGTH, MAX_TOKEN_PREFIX_LENGTH,89	CreateItemData, CollectionLimits, CollectionPermissions, CollectionId, CollectionMode, TokenId,90	SponsorshipState, CreateCollectionData, CreateItemExData, budget, Property, PropertyKey,91	PropertyKeyPermission,92};93use pallet_evm::account::CrossAccountId;94use pallet_common::{95	CollectionHandle, Pallet as PalletCommon, CommonWeightInfo, dispatch::dispatch_tx,96	dispatch::CollectionDispatch, RefungibleExtensionsWeightInfo,97};98pub mod eth;99100#[cfg(feature = "runtime-benchmarks")]101pub mod benchmarking;102pub mod weights;103use weights::WeightInfo;104105/// Maximum number of levels of depth in the token nesting tree.106pub const NESTING_BUDGET: u32 = 5;107108decl_error! {109	/// Errors for the common Unique transactions.110	pub enum Error for Module<T: Config> {111		/// Decimal_points parameter must be lower than [`up_data_structs::MAX_DECIMAL_POINTS`].112		CollectionDecimalPointLimitExceeded,113		/// This address is not set as sponsor, use setCollectionSponsor first.114		ConfirmUnsetSponsorFail,115		/// Length of items properties must be greater than 0.116		EmptyArgument,117		/// Repertition is only supported by refungible collection.118		RepartitionCalledOnNonRefungibleCollection,119	}120}121122/// Configuration trait of this pallet.123pub trait Config: system::Config + pallet_common::Config + Sized + TypeInfo {124	/// Overarching event type.125	type Event: From<Event<Self>> + Into<<Self as frame_system::Config>::Event>;126127	/// Weight information for extrinsics in this pallet.128	type WeightInfo: WeightInfo;129130	/// Weight information for common pallet operations.131	type CommonWeightInfo: CommonWeightInfo<Self::CrossAccountId>;132133	/// Weight info information for extra refungible pallet operations.134	type RefungibleExtensionsWeightInfo: RefungibleExtensionsWeightInfo;135}136137decl_event! {138	pub enum Event<T>139	where140		<T as frame_system::Config>::AccountId,141		<T as pallet_evm::account::Config>::CrossAccountId,142	{143		/// Collection sponsor was removed144		///145		/// # Arguments146		/// * collection_id: ID of the affected collection.147		CollectionSponsorRemoved(CollectionId),148149		/// Collection admin was added150		///151		/// # Arguments152		/// * collection_id: ID of the affected collection.153		/// * admin: Admin address.154		CollectionAdminAdded(CollectionId, CrossAccountId),155156		/// Collection owned was changed157		///158		/// # Arguments159		/// * collection_id: ID of the affected collection.160		/// * owner: New owner address.161		CollectionOwnedChanged(CollectionId, AccountId),162163		/// Collection sponsor was set164		///165		/// # Arguments166		/// * collection_id: ID of the affected collection.167		/// * owner: New sponsor address.168		CollectionSponsorSet(CollectionId, AccountId),169170		/// New sponsor was confirm171		///172		/// # Arguments173		/// * collection_id: ID of the affected collection.174		/// * sponsor: New sponsor address.175		SponsorshipConfirmed(CollectionId, AccountId),176177		/// Collection admin was removed178		///179		/// # Arguments180		/// * collection_id: ID of the affected collection.181		/// * admin: Removed admin address.182		CollectionAdminRemoved(CollectionId, CrossAccountId),183184		/// Address was removed from the allow list185		///186		/// # Arguments187		/// * collection_id: ID of the affected collection.188		/// * user: Address of the removed account.189		AllowListAddressRemoved(CollectionId, CrossAccountId),190191		/// Address was added to the allow list192		///193		/// # Arguments194		/// * collection_id: ID of the affected collection.195		/// * user: Address of the added account.196		AllowListAddressAdded(CollectionId, CrossAccountId),197198		/// Collection limits were set199		///200		/// # Arguments201		/// * collection_id: ID of the affected collection.202		CollectionLimitSet(CollectionId),203204		/// Collection permissions were set205		///206		/// # Arguments207		/// * collection_id: ID of the affected collection.208		CollectionPermissionSet(CollectionId),209	}210}211212type SelfWeightOf<T> = <T as Config>::WeightInfo;213214// # Used definitions215//216// ## User control levels217//218// chain-controlled - key is uncontrolled by user219//                    i.e autoincrementing index220//                    can use non-cryptographic hash221// real - key is controlled by user222//        but it is hard to generate enough colliding values, i.e owner of signed txs223//        can use non-cryptographic hash224// controlled - key is completly controlled by users225//              i.e maps with mutable keys226//              should use cryptographic hash227//228// ## User control level downgrade reasons229//230// ?1 - chain-controlled -> controlled231//      collections/tokens can be destroyed, resulting in massive holes232// ?2 - chain-controlled -> controlled233//      same as ?1, but can be only added, resulting in easier exploitation234// ?3 - real -> controlled235//      no confirmation required, so addresses can be easily generated236decl_storage! {237	trait Store for Module<T: Config> as Unique {238239		//#region Private members240		/// Used for migrations241		ChainVersion: u64;242		//#endregion243244		//#region Tokens transfer sponosoring rate limit baskets245		/// (Collection id (controlled?2), who created (real))246		/// TODO: Off chain worker should remove from this map when collection gets removed247		pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => Option<T::BlockNumber>;248		/// Collection id (controlled?2), token id (controlled?2)249		pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;250		/// Collection id (controlled?2), owning user (real)251		pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;252		/// Collection id (controlled?2), token id (controlled?2)253		pub ReFungibleTransferBasket get(fn refungible_transfer_basket): nmap hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;254		//#endregion255256		/// Variable metadata sponsoring257		/// Collection id (controlled?2), token id (controlled?2)258		#[deprecated]259		pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;260		/// Last sponsoring of token property setting // todo:doc rephrase this and the following261		pub TokenPropertyBasket get(fn token_property_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;262263		/// Last sponsoring of NFT approval in a collection264		pub NftApproveBasket get(fn nft_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;265		/// Last sponsoring of fungible tokens approval in a collection266		pub FungibleApproveBasket get(fn fungible_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;267		/// Last sponsoring of RFT approval in a collection268		pub RefungibleApproveBasket get(fn refungible_approve_basket): nmap hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;269	}270}271272decl_module! {273	/// Type alias to Pallet, to be used by construct_runtime.274	pub struct Module<T: Config> for enum Call275	where276		origin: T::Origin277	{278		type Error = Error<T>;279280		pub fn deposit_event() = default;281282		fn on_initialize(_now: T::BlockNumber) -> Weight {283			0284		}285286		fn on_runtime_upgrade() -> Weight {287			0288		}289290		/// Create a collection of tokens.291		///292		/// Each Token may have multiple properties encoded as an array of bytes293		/// of certain length. The initial owner of the collection is set294		/// to the address that signed the transaction and can be changed later.295		///296		/// Prefer the more advanced [`create_collection_ex`][`Pallet::create_collection_ex`] instead.297		///298		/// # Permissions299		///300		/// * Anyone - becomes the owner of the new collection.301		///302		/// # Arguments303		///304		/// * `collection_name`: Wide-character string with collection name305		/// (limit [`MAX_COLLECTION_NAME_LENGTH`]).306		/// * `collection_description`: Wide-character string with collection description307		/// (limit [`MAX_COLLECTION_DESCRIPTION_LENGTH`]).308		/// * `token_prefix`: Byte string containing the token prefix to mark a collection309		/// to which a token belongs (limit [`MAX_TOKEN_PREFIX_LENGTH`]).310		/// * `mode`: Type of items stored in the collection and type dependent data.311		// returns collection ID312		#[weight = <SelfWeightOf<T>>::create_collection()]313		#[deprecated(note = "`create_collection_ex` is more up-to-date and advanced, prefer it instead")]314		pub fn create_collection(315			origin,316			collection_name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,317			collection_description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,318			token_prefix: BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>,319			mode: CollectionMode320		) -> DispatchResult {321			let data: CreateCollectionData<T::AccountId> = CreateCollectionData {322				name: collection_name,323				description: collection_description,324				token_prefix,325				mode,326				..Default::default()327			};328			Self::create_collection_ex(origin, data)329		}330331		/// Create a collection with explicit parameters.332		///333		/// Prefer it to the deprecated [`create_collection`][`Pallet::create_collection`] method.334		///335		/// # Permissions336		///337		/// * Anyone - becomes the owner of the new collection.338		///339		/// # Arguments340		///341		/// * `data`: Explicit data of a collection used for its creation.342		#[weight = <SelfWeightOf<T>>::create_collection()]343		pub fn create_collection_ex(origin, data: CreateCollectionData<T::AccountId>) -> DispatchResult {344			let sender = ensure_signed(origin)?;345346			// =========347348			let _id = T::CollectionDispatch::create(T::CrossAccountId::from_sub(sender), data)?;349350			Ok(())351		}352353		/// Destroy a collection if no tokens exist within.354		///355		/// # Permissions356		///357		/// * Collection owner358		///359		/// # Arguments360		///361		/// * `collection_id`: Collection to destroy.362		#[weight = <SelfWeightOf<T>>::destroy_collection()]363		pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {364			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);365			let collection = <CollectionHandle<T>>::try_get(collection_id)?;366			collection.check_is_internal()?;367368			// =========369370			T::CollectionDispatch::destroy(sender, collection)?;371372			// TODO: basket cleanup should be moved elsewhere373			// Maybe runtime dispatch.rs should perform it?374375			let _ = <NftTransferBasket<T>>::clear_prefix(collection_id, u32::MAX, None);376			let _ = <FungibleTransferBasket<T>>::clear_prefix(collection_id, u32::MAX, None);377			let _ = <ReFungibleTransferBasket<T>>::clear_prefix((collection_id,), u32::MAX, None);378379			let _ = <NftApproveBasket<T>>::clear_prefix(collection_id, u32::MAX, None);380			let _ = <FungibleApproveBasket<T>>::clear_prefix(collection_id, u32::MAX, None);381			let _ = <RefungibleApproveBasket<T>>::clear_prefix((collection_id,), u32::MAX, None);382383			Ok(())384		}385386		/// Add an address to allow list.387		///388		/// # Permissions389		///390		/// * Collection owner391		/// * Collection admin392		///393		/// # Arguments394		///395		/// * `collection_id`: ID of the modified collection.396		/// * `address`: ID of the address to be added to the allowlist.397		#[weight = <SelfWeightOf<T>>::add_to_allow_list()]398		pub fn add_to_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{399400			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);401			let collection = <CollectionHandle<T>>::try_get(collection_id)?;402			collection.check_is_internal()?;403404			<PalletCommon<T>>::toggle_allowlist(405				&collection,406				&sender,407				&address,408				true,409			)?;410411			Self::deposit_event(Event::<T>::AllowListAddressAdded(412				collection_id,413				address414			));415416			Ok(())417		}418419		/// Remove an address from allow list.420		///421		/// # Permissions422		///423		/// * Collection owner424		/// * Collection admin425		///426		/// # Arguments427		///428		/// * `collection_id`: ID of the modified collection.429		/// * `address`: ID of the address to be removed from the allowlist.430		#[weight = <SelfWeightOf<T>>::remove_from_allow_list()]431		pub fn remove_from_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{432433			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);434			let collection = <CollectionHandle<T>>::try_get(collection_id)?;435			collection.check_is_internal()?;436437			<PalletCommon<T>>::toggle_allowlist(438				&collection,439				&sender,440				&address,441				false,442			)?;443444			<Pallet<T>>::deposit_event(Event::<T>::AllowListAddressRemoved(445				collection_id,446				address447			));448449			Ok(())450		}451452		/// Change the owner of the collection.453		///454		/// # Permissions455		///456		/// * Collection owner457		///458		/// # Arguments459		///460		/// * `collection_id`: ID of the modified collection.461		/// * `new_owner`: ID of the account that will become the owner.462		#[weight = <SelfWeightOf<T>>::change_collection_owner()]463		pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {464465			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);466467			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;468			target_collection.check_is_internal()?;469			target_collection.check_is_owner(&sender)?;470471			target_collection.owner = new_owner.clone();472			<Pallet<T>>::deposit_event(Event::<T>::CollectionOwnedChanged(473				collection_id,474				new_owner475			));476477			target_collection.save()478		}479480		/// Add an admin to a collection.481		///482		/// NFT Collection can be controlled by multiple admin addresses483		/// (some which can also be servers, for example). Admins can issue484		/// and burn NFTs, as well as add and remove other admins,485		/// but cannot change NFT or Collection ownership.486		///487		/// # Permissions488		///489		/// * Collection owner490		/// * Collection admin491		///492		/// # Arguments493		///494		/// * `collection_id`: ID of the Collection to add an admin for.495		/// * `new_admin`: Address of new admin to add.496		#[weight = <SelfWeightOf<T>>::add_collection_admin()]497		pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {498			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);499			let collection = <CollectionHandle<T>>::try_get(collection_id)?;500			collection.check_is_internal()?;501502			<Pallet<T>>::deposit_event(Event::<T>::CollectionAdminAdded(503				collection_id,504				new_admin_id.clone()505			));506507			<PalletCommon<T>>::toggle_admin(&collection, &sender, &new_admin_id, true)508		}509510		/// Remove admin of a collection.511		///512		/// An admin address can remove itself. List of admins may become empty,513		/// in which case only Collection Owner will be able to add an Admin.514		///515		/// # Permissions516		///517		/// * Collection owner518		/// * Collection admin519		///520		/// # Arguments521		///522		/// * `collection_id`: ID of the collection to remove the admin for.523		/// * `account_id`: Address of the admin to remove.524		#[weight = <SelfWeightOf<T>>::remove_collection_admin()]525		pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {526			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);527			let collection = <CollectionHandle<T>>::try_get(collection_id)?;528			collection.check_is_internal()?;529530			<Pallet<T>>::deposit_event(Event::<T>::CollectionAdminRemoved(531				collection_id,532				account_id.clone()533			));534535			<PalletCommon<T>>::toggle_admin(&collection, &sender, &account_id, false)536		}537538		/// Set (invite) a new collection sponsor.539		///540		/// If successful, confirmation from the sponsor-to-be will be pending.541		///542		/// # Permissions543		///544		/// * Collection owner545		/// * Collection admin546		///547		/// # Arguments548		///549		/// * `collection_id`: ID of the modified collection.550		/// * `new_sponsor`: ID of the account of the sponsor-to-be.551		#[weight = <SelfWeightOf<T>>::set_collection_sponsor()]552		pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {553			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);554555			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;556			target_collection.check_is_owner_or_admin(&sender)?;557			target_collection.check_is_internal()?;558559			target_collection.set_sponsor(new_sponsor.clone())?;560561			<Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(562				collection_id,563				new_sponsor564			));565566			target_collection.save()567		}568569		/// Confirm own sponsorship of a collection, becoming the sponsor.570		///571		/// An invitation must be pending, see [`set_collection_sponsor`][`Pallet::set_collection_sponsor`].572		/// Sponsor can pay the fees of a transaction instead of the sender,573		/// but only within specified limits.574		///575		/// # Permissions576		///577		/// * Sponsor-to-be578		///579		/// # Arguments580		///581		/// * `collection_id`: ID of the collection with the pending sponsor.582		#[weight = <SelfWeightOf<T>>::confirm_sponsorship()]583		pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {584			let sender = ensure_signed(origin)?;585586			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;587			target_collection.check_is_internal()?;588			ensure!(589				target_collection.confirm_sponsorship(&sender)?,590				Error::<T>::ConfirmUnsetSponsorFail591			);592593			<Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(594				collection_id,595				sender596			));597598			target_collection.save()599		}600601		/// Remove a collection's a sponsor, making everyone pay for their own transactions.602		///603		/// # Permissions604		///605		/// * Collection owner606		///607		/// # Arguments608		///609		/// * `collection_id`: ID of the collection with the sponsor to remove.610		#[weight = <SelfWeightOf<T>>::remove_collection_sponsor()]611		pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {612			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);613614			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;615			target_collection.check_is_internal()?;616			target_collection.check_is_owner(&sender)?;617618			target_collection.sponsorship = SponsorshipState::Disabled;619620			<Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(621				collection_id622			));623			target_collection.save()624		}625626		/// Mint an item within a collection.627		///628		/// A collection must exist first, see [`create_collection_ex`][`Pallet::create_collection_ex`].629		///630		/// # Permissions631		///632		/// * Collection owner633		/// * Collection admin634		/// * Anyone if635		///     * Allow List is enabled, and636		///     * Address is added to allow list, and637		///     * MintPermission is enabled (see [`set_collection_permissions`][`Pallet::set_collection_permissions`])638		///639		/// # Arguments640		///641		/// * `collection_id`: ID of the collection to which an item would belong.642		/// * `owner`: Address of the initial owner of the item.643		/// * `data`: Token data describing the item to store on chain.644		#[weight = T::CommonWeightInfo::create_item()]645		pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResultWithPostInfo {646			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);647			let budget = budget::Value::new(NESTING_BUDGET);648649			dispatch_tx::<T, _>(collection_id, |d| d.create_item(sender, owner, data, &budget))650		}651652		/// Create multiple items within a collection.653		///654		/// A collection must exist first, see [`create_collection_ex`][`Pallet::create_collection_ex`].655		///656		/// # Permissions657		///658		/// * Collection owner659		/// * Collection admin660		/// * Anyone if661		///     * Allow List is enabled, and662		///     * Address is added to the allow list, and663		///     * MintPermission is enabled (see [`set_collection_permissions`][`Pallet::set_collection_permissions`])664		///665		/// # Arguments666		///667		/// * `collection_id`: ID of the collection to which the tokens would belong.668		/// * `owner`: Address of the initial owner of the tokens.669		/// * `items_data`: Vector of data describing each item to be created.670		#[weight = T::CommonWeightInfo::create_multiple_items(&items_data)]671		pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResultWithPostInfo {672			ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);673			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);674			let budget = budget::Value::new(NESTING_BUDGET);675676			dispatch_tx::<T, _>(collection_id, |d| d.create_multiple_items(sender, owner, items_data, &budget))677		}678679		/// Add or change collection properties.680		///681		/// # Permissions682		///683		/// * Collection owner684		/// * Collection admin685		///686		/// # Arguments687		///688		/// * `collection_id`: ID of the modified collection.689		/// * `properties`: Vector of key-value pairs stored as the collection's metadata.690		/// Keys support Latin letters, `-`, `_`, and `.` as symbols.691		#[weight = T::CommonWeightInfo::set_collection_properties(properties.len() as u32)]692		pub fn set_collection_properties(693			origin,694			collection_id: CollectionId,695			properties: Vec<Property>696		) -> DispatchResultWithPostInfo {697			ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);698699			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);700701			dispatch_tx::<T, _>(collection_id, |d| d.set_collection_properties(sender, properties))702		}703704		/// Delete specified collection properties.705		///706		/// # Permissions707		///708		/// * Collection Owner709		/// * Collection Admin710		///711		/// # Arguments712		///713		/// * `collection_id`: ID of the modified collection.714		/// * `property_keys`: Vector of keys of the properties to be deleted.715		/// Keys support Latin letters, `-`, `_`, and `.` as symbols.716		#[weight = T::CommonWeightInfo::delete_collection_properties(property_keys.len() as u32)]717		pub fn delete_collection_properties(718			origin,719			collection_id: CollectionId,720			property_keys: Vec<PropertyKey>,721		) -> DispatchResultWithPostInfo {722			ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);723724			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);725726			dispatch_tx::<T, _>(collection_id, |d| d.delete_collection_properties(&sender, property_keys))727		}728729		/// Add or change token properties according to collection's permissions.730		/// Currently properties only work with NFTs.731		///732		/// # Permissions733		///734		/// * Depends on collection's token property permissions and specified property mutability:735		/// 	* Collection owner736		/// 	* Collection admin737		/// 	* Token owner738		///739		/// See [`set_token_property_permissions`][`Pallet::set_token_property_permissions`].740		///741		/// # Arguments742		///743		/// * `collection_id: ID of the collection to which the token belongs.744		/// * `token_id`: ID of the modified token.745		/// * `properties`: Vector of key-value pairs stored as the token's metadata.746		/// Keys support Latin letters, `-`, `_`, and `.` as symbols.747		#[weight = T::CommonWeightInfo::set_token_properties(properties.len() as u32)]748		pub fn set_token_properties(749			origin,750			collection_id: CollectionId,751			token_id: TokenId,752			properties: Vec<Property>753		) -> DispatchResultWithPostInfo {754			ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);755756			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);757			let budget = budget::Value::new(NESTING_BUDGET);758759			dispatch_tx::<T, _>(collection_id, |d| d.set_token_properties(sender, token_id, properties, &budget))760		}761762		/// Delete specified token properties. Currently properties only work with NFTs.763		///764		/// # Permissions765		///766		/// * Depends on collection's token property permissions and specified property mutability:767		/// 	* Collection owner768		/// 	* Collection admin769		/// 	* Token owner770		///771		/// # Arguments772		///773		/// * `collection_id`: ID of the collection to which the token belongs.774		/// * `token_id`: ID of the modified token.775		/// * `property_keys`: Vector of keys of the properties to be deleted.776		/// Keys support Latin letters, `-`, `_`, and `.` as symbols.777		#[weight = T::CommonWeightInfo::delete_token_properties(property_keys.len() as u32)]778		pub fn delete_token_properties(779			origin,780			collection_id: CollectionId,781			token_id: TokenId,782			property_keys: Vec<PropertyKey>783		) -> DispatchResultWithPostInfo {784			ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);785786			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);787			let budget = budget::Value::new(NESTING_BUDGET);788789			dispatch_tx::<T, _>(collection_id, |d| d.delete_token_properties(sender, token_id, property_keys, &budget))790		}791792		/// Add or change token property permissions of a collection.793		///794		/// Without a permission for a particular key, a property with that key795		/// cannot be created in a token.796		///797		/// # Permissions798		///799		/// * Collection owner800		/// * Collection admin801		///802		/// # Arguments803		///804		/// * `collection_id`: ID of the modified collection.805		/// * `property_permissions`: Vector of permissions for property keys.806		/// Keys support Latin letters, `-`, `_`, and `.` as symbols.807		#[weight = T::CommonWeightInfo::set_token_property_permissions(property_permissions.len() as u32)]808		pub fn set_token_property_permissions(809			origin,810			collection_id: CollectionId,811			property_permissions: Vec<PropertyKeyPermission>,812		) -> DispatchResultWithPostInfo {813			ensure!(!property_permissions.is_empty(), Error::<T>::EmptyArgument);814815			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);816817			dispatch_tx::<T, _>(collection_id, |d| d.set_token_property_permissions(&sender, property_permissions))818		}819820		/// Create multiple items within a collection with explicitly specified initial parameters.821		///822		/// # Permissions823		///824		/// * Collection owner825		/// * Collection admin826		/// * Anyone if827		///     * Allow List is enabled, and828		///     * Address is added to allow list, and829		///     * MintPermission is enabled (see [`set_collection_permissions`][`Pallet::set_collection_permissions`])830		///831		/// # Arguments832		///833		/// * `collection_id`: ID of the collection to which the tokens would belong.834		/// * `data`: Explicit item creation data.835		#[weight = T::CommonWeightInfo::create_multiple_items_ex(&data)]836		pub fn create_multiple_items_ex(origin, collection_id: CollectionId, data: CreateItemExData<T::CrossAccountId>) -> DispatchResultWithPostInfo {837			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);838			let budget = budget::Value::new(NESTING_BUDGET);839840			dispatch_tx::<T, _>(collection_id, |d| d.create_multiple_items_ex(sender, data, &budget))841		}842843		/// Completely allow or disallow transfers for a particular collection.844		///845		/// # Permissions846		///847		/// * Collection owner848		///849		/// # Arguments850		///851		/// * `collection_id`: ID of the collection.852		/// * `value`: New value of the flag, are transfers allowed?853		#[weight = <SelfWeightOf<T>>::set_transfers_enabled_flag()]854		pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {855			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);856			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;857			target_collection.check_is_internal()?;858			target_collection.check_is_owner(&sender)?;859860			// =========861862			target_collection.limits.transfers_enabled = Some(value);863			target_collection.save()864		}865866		/// Destroy an item.867		///868		/// # Permissions869		///870		/// * Collection owner871		/// * Collection admin872		/// * Current item owner873		///874		/// # Arguments875		///876		/// * `collection_id`: ID of the collection to which the item belongs.877		/// * `item_id`: ID of item to burn.878		/// * `value`: Number of pieces of the item to destroy.879		/// 	* Non-Fungible Mode: An NFT is indivisible, there is always 1 corresponding to an ID.880		///     * Fungible Mode: The desired number of pieces to burn.881		///     * Re-Fungible Mode: The desired number of pieces to burn.882		#[weight = T::CommonWeightInfo::burn_item()]883		pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {884			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);885886			let post_info = dispatch_tx::<T, _>(collection_id, |d| d.burn_item(sender, item_id, value))?;887			if value == 1 {888				<NftTransferBasket<T>>::remove(collection_id, item_id);889				<NftApproveBasket<T>>::remove(collection_id, item_id);890			}891			// Those maps should be cleared only if token disappears completly, need to move this part of logic to pallets?892			// <FungibleApproveBasket<T>>::remove(collection_id, sender.as_sub());893			// <RefungibleApproveBasket<T>>::remove((collection_id, item_id, sender.as_sub()));894			Ok(post_info)895		}896897		/// Destroy a token on behalf of the owner as a non-owner account.898		///899		/// See also: [`approve`][`Pallet::approve`].900		///901		/// After this method executes, one approval is removed from the total so that902		/// the approved address will not be able to transfer this item again from this owner.903		///904		/// # Permissions905		///906		/// * Collection owner907		/// * Collection admin908		/// * Current token owner909		/// * Address approved by current item owner910		///911		/// # Arguments912		///913		/// * `from`: The owner of the burning item.914		/// * `collection_id`: ID of the collection to which the item belongs.915		/// * `item_id`: ID of item to burn.916		/// * `value`: Number of pieces to burn.917		/// 	* Non-Fungible Mode: An NFT is indivisible, there is always 1 corresponding to an ID.918		///     * Fungible Mode: The desired number of pieces to burn.919		///     * Re-Fungible Mode: The desired number of pieces to burn.920		#[weight = T::CommonWeightInfo::burn_from()]921		pub fn burn_from(origin, collection_id: CollectionId, from: T::CrossAccountId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {922			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);923			let budget = budget::Value::new(NESTING_BUDGET);924925			dispatch_tx::<T, _>(collection_id, |d| d.burn_from(sender, from, item_id, value, &budget))926		}927928		/// Change ownership of the token.929		///930		/// # Permissions931		///932		/// * Collection owner933		/// * Collection admin934		/// * Current token owner935		///936		/// # Arguments937		///938		/// * `recipient`: Address of token recipient.939		/// * `collection_id`: ID of the collection the item belongs to.940		/// * `item_id`: ID of the item.941		///     * Non-Fungible Mode: Required.942		///     * Fungible Mode: Ignored.943		///     * Re-Fungible Mode: Required.944		///945		/// * `value`: Amount to transfer.946		/// 	* Non-Fungible Mode: An NFT is indivisible, there is always 1 corresponding to an ID.947		///     * Fungible Mode: The desired number of pieces to transfer.948		///     * Re-Fungible Mode: The desired number of pieces to transfer.949		#[weight = T::CommonWeightInfo::transfer()]950		pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {951			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);952			let budget = budget::Value::new(NESTING_BUDGET);953954			dispatch_tx::<T, _>(collection_id, |d| d.transfer(sender, recipient, item_id, value, &budget))955		}956957		/// Allow a non-permissioned address to transfer or burn an item.958		///959		/// # Permissions960		///961		/// * Collection owner962		/// * Collection admin963		/// * Current item owner964		///965		/// # Arguments966		///967		/// * `spender`: Account to be approved to make specific transactions on non-owned tokens.968		/// * `collection_id`: ID of the collection the item belongs to.969		/// * `item_id`: ID of the item transactions on which are now approved.970		/// * `amount`: Number of pieces of the item approved for a transaction (maximum of 1 for NFTs).971		/// Set to 0 to revoke the approval.972		#[weight = T::CommonWeightInfo::approve()]973		pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResultWithPostInfo {974			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);975976			dispatch_tx::<T, _>(collection_id, |d| d.approve(sender, spender, item_id, amount))977		}978979		/// Change ownership of an item on behalf of the owner as a non-owner account.980		///981		/// See the [`approve`][`Pallet::approve`] method for additional information.982		///983		/// After this method executes, one approval is removed from the total so that984		/// the approved address will not be able to transfer this item again from this owner.985		///986		/// # Permissions987		///988		/// * Collection owner989		/// * Collection admin990		/// * Current item owner991		/// * Address approved by current item owner992		///993		/// # Arguments994		///995		/// * `from`: Address that currently owns the token.996		/// * `recipient`: Address of the new token-owner-to-be.997		/// * `collection_id`: ID of the collection the item.998		/// * `item_id`: ID of the item to be transferred.999		/// * `value`: Amount to transfer.1000		/// 	* Non-Fungible Mode: An NFT is indivisible, there is always 1 corresponding to an ID.1001		///     * Fungible Mode: The desired number of pieces to transfer.1002		///     * Re-Fungible Mode: The desired number of pieces to transfer.1003		#[weight = T::CommonWeightInfo::transfer_from()]1004		pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResultWithPostInfo {1005			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1006			let budget = budget::Value::new(NESTING_BUDGET);10071008			dispatch_tx::<T, _>(collection_id, |d| d.transfer_from(sender, from, recipient, item_id, value, &budget))1009		}10101011		/// Set specific limits of a collection. Empty, or None fields mean chain default.1012		///1013		/// # Permissions1014		///1015		/// * Collection owner1016		/// * Collection admin1017		///1018		/// # Arguments1019		///1020		/// * `collection_id`: ID of the modified collection.1021		/// * `new_limit`: New limits of the collection. Fields that are not set (None)1022		/// will not overwrite the old ones.1023		#[weight = <SelfWeightOf<T>>::set_collection_limits()]1024		pub fn set_collection_limits(1025			origin,1026			collection_id: CollectionId,1027			new_limit: CollectionLimits,1028		) -> DispatchResult {1029			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1030			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1031			target_collection.check_is_internal()?;1032			target_collection.check_is_owner_or_admin(&sender)?;1033			let old_limit = &target_collection.limits;10341035			target_collection.limits = <PalletCommon<T>>::clamp_limits(target_collection.mode.clone(), &old_limit, new_limit)?;10361037			<Pallet<T>>::deposit_event(Event::<T>::CollectionLimitSet(1038				collection_id1039			));10401041			target_collection.save()1042		}10431044		/// Set specific permissions of a collection. Empty, or None fields mean chain default.1045		///1046		/// # Permissions1047		///1048		/// * Collection owner1049		/// * Collection admin1050		///1051		/// # Arguments1052		///1053		/// * `collection_id`: ID of the modified collection.1054		/// * `new_permission`: New permissions of the collection. Fields that are not set (None)1055		/// will not overwrite the old ones.1056		#[weight = <SelfWeightOf<T>>::set_collection_limits()]1057		pub fn set_collection_permissions(1058			origin,1059			collection_id: CollectionId,1060			new_permission: CollectionPermissions,1061		) -> DispatchResult {1062			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1063			let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1064			target_collection.check_is_internal()?;1065			target_collection.check_is_owner_or_admin(&sender)?;1066			let old_limit = &target_collection.permissions;10671068			target_collection.permissions = <PalletCommon<T>>::clamp_permissions(target_collection.mode.clone(), &old_limit, new_permission)?;10691070			<Pallet<T>>::deposit_event(Event::<T>::CollectionPermissionSet(1071				collection_id1072			));10731074			target_collection.save()1075		}10761077		/// Re-partition a refungible token, while owning all of its parts/pieces.1078		///1079		/// # Permissions1080		///1081		/// * Token owner (must own every part)1082		///1083		/// # Arguments1084		///1085		/// * `collection_id`: ID of the collection the RFT belongs to.1086		/// * `token_id`: ID of the RFT.1087		/// * `amount`: New number of parts/pieces into which the token shall be partitioned.1088		#[weight = T::RefungibleExtensionsWeightInfo::repartition()]1089		pub fn repartition(1090			origin,1091			collection_id: CollectionId,1092			token_id: TokenId,1093			amount: u128,1094		) -> DispatchResultWithPostInfo {1095			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1096			dispatch_tx::<T, _>(collection_id, |d| {1097				if let Some(refungible_extensions) = d.refungible_extensions() {1098					refungible_extensions.repartition(&sender, token_id, amount)1099				} else {1100					fail!(<Error<T>>::RepartitionCalledOnNonRefungibleCollection)1101				}1102			})1103		}1104	}1105}
modifiedtests/src/interfaces/augment-api-events.tsdiffbeforeafterboth
--- a/tests/src/interfaces/augment-api-events.ts
+++ b/tests/src/interfaces/augment-api-events.ts
@@ -361,7 +361,7 @@
       [key: string]: AugmentedEvent<ApiType>;
     };
     promotion: {
-      StakingRecalculation: AugmentedEvent<ApiType, [u128, u128]>;
+      StakingRecalculation: AugmentedEvent<ApiType, [AccountId32, u128, u128]>;
       /**
        * Generic event
        **/
modifiedtests/src/interfaces/augment-api-query.tsdiffbeforeafterboth
--- a/tests/src/interfaces/augment-api-query.ts
+++ b/tests/src/interfaces/augment-api-query.ts
@@ -513,6 +513,11 @@
     promotion: {
       admin: AugmentedQuery<ApiType, () => Observable<Option<AccountId32>>, []> & QueryableStorageEntry<ApiType, []>;
       /**
+       * Stores the address of the staker for which the last revenue recalculation was performed.
+       * If `None`, then recalculation has not yet been performed or calculations have been completed for all stakers.
+       **/
+      lastCalcucaltedStaker: AugmentedQuery<ApiType, () => Observable<Option<AccountId32>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
        * Next target block when interest is recalculated
        **/
       nextInterestBlock: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
@@ -523,7 +528,7 @@
       /**
        * Amount of tokens staked by account in the blocknumber.
        **/
-      staked: AugmentedQuery<ApiType, (arg1: AccountId32 | string | Uint8Array, arg2: u32 | AnyNumber | Uint8Array) => Observable<u128>, [AccountId32, u32]> & QueryableStorageEntry<ApiType, [AccountId32, u32]>;
+      staked: AugmentedQuery<ApiType, (arg1: AccountId32 | string | Uint8Array, arg2: u32 | AnyNumber | Uint8Array) => Observable<ITuple<[u128, u32]>>, [AccountId32, u32]> & QueryableStorageEntry<ApiType, [AccountId32, u32]>;
       /**
        * A block when app-promotion has started .I think this is redundant, because we only need `NextInterestBlock`.
        **/
modifiedtests/src/interfaces/augment-api-tx.tsdiffbeforeafterboth
--- a/tests/src/interfaces/augment-api-tx.ts
+++ b/tests/src/interfaces/augment-api-tx.ts
@@ -363,14 +363,15 @@
       [key: string]: SubmittableExtrinsicFunction<ApiType>;
     };
     promotion: {
+      payoutStakers: AugmentedSubmittable<(stakersNumber: Option<u8> | null | Uint8Array | u8 | AnyNumber) => SubmittableExtrinsic<ApiType>, [Option<u8>]>;
       setAdminAddress: AugmentedSubmittable<(admin: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [PalletEvmAccountBasicCrossAccountIdRepr]>;
       sponsorCollection: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32]>;
       sponsorConract: AugmentedSubmittable<(contractId: H160 | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [H160]>;
       stake: AugmentedSubmittable<(amount: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u128]>;
       startAppPromotion: AugmentedSubmittable<(promotionStartRelayBlock: Option<u32> | null | Uint8Array | u32 | AnyNumber) => SubmittableExtrinsic<ApiType>, [Option<u32>]>;
       stopAppPromotion: AugmentedSubmittable<() => SubmittableExtrinsic<ApiType>, []>;
-      stopSponsorignCollection: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32]>;
-      stopSponsorignContract: AugmentedSubmittable<(contractId: H160 | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [H160]>;
+      stopSponsoringCollection: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32]>;
+      stopSponsoringContract: AugmentedSubmittable<(contractId: H160 | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [H160]>;
       unstake: AugmentedSubmittable<(amount: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u128]>;
       /**
        * Generic tx
modifiedtests/src/interfaces/default/types.tsdiffbeforeafterboth
--- a/tests/src/interfaces/default/types.ts
+++ b/tests/src/interfaces/default/types.ts
@@ -829,19 +829,23 @@
   readonly asSponsorCollection: {
     readonly collectionId: u32;
   } & Struct;
-  readonly isStopSponsorignCollection: boolean;
-  readonly asStopSponsorignCollection: {
+  readonly isStopSponsoringCollection: boolean;
+  readonly asStopSponsoringCollection: {
     readonly collectionId: u32;
   } & Struct;
   readonly isSponsorConract: boolean;
   readonly asSponsorConract: {
     readonly contractId: H160;
   } & Struct;
-  readonly isStopSponsorignContract: boolean;
-  readonly asStopSponsorignContract: {
+  readonly isStopSponsoringContract: boolean;
+  readonly asStopSponsoringContract: {
     readonly contractId: H160;
   } & Struct;
-  readonly type: 'SetAdminAddress' | 'StartAppPromotion' | 'StopAppPromotion' | 'Stake' | 'Unstake' | 'SponsorCollection' | 'StopSponsorignCollection' | 'SponsorConract' | 'StopSponsorignContract';
+  readonly isPayoutStakers: boolean;
+  readonly asPayoutStakers: {
+    readonly stakersNumber: Option<u8>;
+  } & Struct;
+  readonly type: 'SetAdminAddress' | 'StartAppPromotion' | 'StopAppPromotion' | 'Stake' | 'Unstake' | 'SponsorCollection' | 'StopSponsoringCollection' | 'SponsorConract' | 'StopSponsoringContract' | 'PayoutStakers';
 }
 
 /** @name PalletAppPromotionError */
@@ -856,7 +860,7 @@
 /** @name PalletAppPromotionEvent */
 export interface PalletAppPromotionEvent extends Enum {
   readonly isStakingRecalculation: boolean;
-  readonly asStakingRecalculation: ITuple<[u128, u128]>;
+  readonly asStakingRecalculation: ITuple<[AccountId32, u128, u128]>;
   readonly type: 'StakingRecalculation';
 }
 
modifiedtests/src/interfaces/lookup.tsdiffbeforeafterboth
--- a/tests/src/interfaces/lookup.ts
+++ b/tests/src/interfaces/lookup.ts
@@ -1061,7 +1061,7 @@
    **/
   PalletAppPromotionEvent: {
     _enum: {
-      StakingRecalculation: '(u128,u128)'
+      StakingRecalculation: '(AccountId32,u128,u128)'
     }
   },
   /**
@@ -2473,19 +2473,22 @@
       sponsor_collection: {
         collectionId: 'u32',
       },
-      stop_sponsorign_collection: {
+      stop_sponsoring_collection: {
         collectionId: 'u32',
       },
       sponsor_conract: {
         contractId: 'H160',
       },
-      stop_sponsorign_contract: {
-        contractId: 'H160'
+      stop_sponsoring_contract: {
+        contractId: 'H160',
+      },
+      payout_stakers: {
+        stakersNumber: 'Option<u8>'
       }
     }
   },
   /**
-   * Lookup305: pallet_evm::pallet::Call<T>
+   * Lookup306: pallet_evm::pallet::Call<T>
    **/
   PalletEvmCall: {
     _enum: {
@@ -2528,7 +2531,7 @@
     }
   },
   /**
-   * Lookup309: pallet_ethereum::pallet::Call<T>
+   * Lookup310: pallet_ethereum::pallet::Call<T>
    **/
   PalletEthereumCall: {
     _enum: {
@@ -2538,7 +2541,7 @@
     }
   },
   /**
-   * Lookup310: ethereum::transaction::TransactionV2
+   * Lookup311: ethereum::transaction::TransactionV2
    **/
   EthereumTransactionTransactionV2: {
     _enum: {
@@ -2548,7 +2551,7 @@
     }
   },
   /**
-   * Lookup311: ethereum::transaction::LegacyTransaction
+   * Lookup312: ethereum::transaction::LegacyTransaction
    **/
   EthereumTransactionLegacyTransaction: {
     nonce: 'U256',
@@ -2560,7 +2563,7 @@
     signature: 'EthereumTransactionTransactionSignature'
   },
   /**
-   * Lookup312: ethereum::transaction::TransactionAction
+   * Lookup313: ethereum::transaction::TransactionAction
    **/
   EthereumTransactionTransactionAction: {
     _enum: {
@@ -2569,7 +2572,7 @@
     }
   },
   /**
-   * Lookup313: ethereum::transaction::TransactionSignature
+   * Lookup314: ethereum::transaction::TransactionSignature
    **/
   EthereumTransactionTransactionSignature: {
     v: 'u64',
@@ -2577,7 +2580,7 @@
     s: 'H256'
   },
   /**
-   * Lookup315: ethereum::transaction::EIP2930Transaction
+   * Lookup316: ethereum::transaction::EIP2930Transaction
    **/
   EthereumTransactionEip2930Transaction: {
     chainId: 'u64',
@@ -2593,14 +2596,14 @@
     s: 'H256'
   },
   /**
-   * Lookup317: ethereum::transaction::AccessListItem
+   * Lookup318: ethereum::transaction::AccessListItem
    **/
   EthereumTransactionAccessListItem: {
     address: 'H160',
     storageKeys: 'Vec<H256>'
   },
   /**
-   * Lookup318: ethereum::transaction::EIP1559Transaction
+   * Lookup319: ethereum::transaction::EIP1559Transaction
    **/
   EthereumTransactionEip1559Transaction: {
     chainId: 'u64',
@@ -2617,7 +2620,7 @@
     s: 'H256'
   },
   /**
-   * Lookup319: pallet_evm_migration::pallet::Call<T>
+   * Lookup320: pallet_evm_migration::pallet::Call<T>
    **/
   PalletEvmMigrationCall: {
     _enum: {
@@ -2635,19 +2638,19 @@
     }
   },
   /**
-   * Lookup322: pallet_sudo::pallet::Error<T>
+   * Lookup323: pallet_sudo::pallet::Error<T>
    **/
   PalletSudoError: {
     _enum: ['RequireSudo']
   },
   /**
-   * Lookup324: orml_vesting::module::Error<T>
+   * Lookup325: orml_vesting::module::Error<T>
    **/
   OrmlVestingModuleError: {
     _enum: ['ZeroVestingPeriod', 'ZeroVestingPeriodCount', 'InsufficientBalanceToLock', 'TooManyVestingSchedules', 'AmountLow', 'MaxVestingSchedulesExceeded']
   },
   /**
-   * Lookup326: cumulus_pallet_xcmp_queue::InboundChannelDetails
+   * Lookup327: cumulus_pallet_xcmp_queue::InboundChannelDetails
    **/
   CumulusPalletXcmpQueueInboundChannelDetails: {
     sender: 'u32',
@@ -2655,19 +2658,19 @@
     messageMetadata: 'Vec<(u32,PolkadotParachainPrimitivesXcmpMessageFormat)>'
   },
   /**
-   * Lookup327: cumulus_pallet_xcmp_queue::InboundState
+   * Lookup328: cumulus_pallet_xcmp_queue::InboundState
    **/
   CumulusPalletXcmpQueueInboundState: {
     _enum: ['Ok', 'Suspended']
   },
   /**
-   * Lookup330: polkadot_parachain::primitives::XcmpMessageFormat
+   * Lookup331: polkadot_parachain::primitives::XcmpMessageFormat
    **/
   PolkadotParachainPrimitivesXcmpMessageFormat: {
     _enum: ['ConcatenatedVersionedXcm', 'ConcatenatedEncodedBlob', 'Signals']
   },
   /**
-   * Lookup333: cumulus_pallet_xcmp_queue::OutboundChannelDetails
+   * Lookup334: cumulus_pallet_xcmp_queue::OutboundChannelDetails
    **/
   CumulusPalletXcmpQueueOutboundChannelDetails: {
     recipient: 'u32',
@@ -2677,13 +2680,13 @@
     lastIndex: 'u16'
   },
   /**
-   * Lookup334: cumulus_pallet_xcmp_queue::OutboundState
+   * Lookup335: cumulus_pallet_xcmp_queue::OutboundState
    **/
   CumulusPalletXcmpQueueOutboundState: {
     _enum: ['Ok', 'Suspended']
   },
   /**
-   * Lookup336: cumulus_pallet_xcmp_queue::QueueConfigData
+   * Lookup337: cumulus_pallet_xcmp_queue::QueueConfigData
    **/
   CumulusPalletXcmpQueueQueueConfigData: {
     suspendThreshold: 'u32',
@@ -2694,29 +2697,29 @@
     xcmpMaxIndividualWeight: 'u64'
   },
   /**
-   * Lookup338: cumulus_pallet_xcmp_queue::pallet::Error<T>
+   * Lookup339: cumulus_pallet_xcmp_queue::pallet::Error<T>
    **/
   CumulusPalletXcmpQueueError: {
     _enum: ['FailedToSend', 'BadXcmOrigin', 'BadXcm', 'BadOverweightIndex', 'WeightOverLimit']
   },
   /**
-   * Lookup339: pallet_xcm::pallet::Error<T>
+   * Lookup340: pallet_xcm::pallet::Error<T>
    **/
   PalletXcmError: {
     _enum: ['Unreachable', 'SendFailure', 'Filtered', 'UnweighableMessage', 'DestinationNotInvertible', 'Empty', 'CannotReanchor', 'TooManyAssets', 'InvalidOrigin', 'BadVersion', 'BadLocation', 'NoSubscription', 'AlreadySubscribed']
   },
   /**
-   * Lookup340: cumulus_pallet_xcm::pallet::Error<T>
+   * Lookup341: cumulus_pallet_xcm::pallet::Error<T>
    **/
   CumulusPalletXcmError: 'Null',
   /**
-   * Lookup341: cumulus_pallet_dmp_queue::ConfigData
+   * Lookup342: cumulus_pallet_dmp_queue::ConfigData
    **/
   CumulusPalletDmpQueueConfigData: {
     maxIndividual: 'u64'
   },
   /**
-   * Lookup342: cumulus_pallet_dmp_queue::PageIndexData
+   * Lookup343: cumulus_pallet_dmp_queue::PageIndexData
    **/
   CumulusPalletDmpQueuePageIndexData: {
     beginUsed: 'u32',
@@ -2724,19 +2727,19 @@
     overweightCount: 'u64'
   },
   /**
-   * Lookup345: cumulus_pallet_dmp_queue::pallet::Error<T>
+   * Lookup346: cumulus_pallet_dmp_queue::pallet::Error<T>
    **/
   CumulusPalletDmpQueueError: {
     _enum: ['Unknown', 'OverLimit']
   },
   /**
-   * Lookup349: pallet_unique::Error<T>
+   * Lookup350: pallet_unique::Error<T>
    **/
   PalletUniqueError: {
     _enum: ['CollectionDecimalPointLimitExceeded', 'ConfirmUnsetSponsorFail', 'EmptyArgument', 'RepartitionCalledOnNonRefungibleCollection']
   },
   /**
-   * Lookup352: pallet_unique_scheduler::ScheduledV3<frame_support::traits::schedule::MaybeHashed<opal_runtime::Call, primitive_types::H256>, BlockNumber, opal_runtime::OriginCaller, sp_core::crypto::AccountId32>
+   * Lookup353: pallet_unique_scheduler::ScheduledV3<frame_support::traits::schedule::MaybeHashed<opal_runtime::Call, primitive_types::H256>, BlockNumber, opal_runtime::OriginCaller, sp_core::crypto::AccountId32>
    **/
   PalletUniqueSchedulerScheduledV3: {
     maybeId: 'Option<[u8;16]>',
@@ -2746,7 +2749,7 @@
     origin: 'OpalRuntimeOriginCaller'
   },
   /**
-   * Lookup353: opal_runtime::OriginCaller
+   * Lookup354: opal_runtime::OriginCaller
    **/
   OpalRuntimeOriginCaller: {
     _enum: {
@@ -2855,7 +2858,7 @@
     }
   },
   /**
-   * Lookup354: frame_support::dispatch::RawOrigin<sp_core::crypto::AccountId32>
+   * Lookup355: frame_support::dispatch::RawOrigin<sp_core::crypto::AccountId32>
    **/
   FrameSupportDispatchRawOrigin: {
     _enum: {
@@ -2865,7 +2868,7 @@
     }
   },
   /**
-   * Lookup355: pallet_xcm::pallet::Origin
+   * Lookup356: pallet_xcm::pallet::Origin
    **/
   PalletXcmOrigin: {
     _enum: {
@@ -2874,7 +2877,7 @@
     }
   },
   /**
-   * Lookup356: cumulus_pallet_xcm::pallet::Origin
+   * Lookup357: cumulus_pallet_xcm::pallet::Origin
    **/
   CumulusPalletXcmOrigin: {
     _enum: {
@@ -2883,7 +2886,7 @@
     }
   },
   /**
-   * Lookup357: pallet_ethereum::RawOrigin
+   * Lookup358: pallet_ethereum::RawOrigin
    **/
   PalletEthereumRawOrigin: {
     _enum: {
@@ -2891,17 +2894,17 @@
     }
   },
   /**
-   * Lookup358: sp_core::Void
+   * Lookup359: sp_core::Void
    **/
   SpCoreVoid: 'Null',
   /**
-   * Lookup359: pallet_unique_scheduler::pallet::Error<T>
+   * Lookup360: pallet_unique_scheduler::pallet::Error<T>
    **/
   PalletUniqueSchedulerError: {
     _enum: ['FailedToSchedule', 'NotFound', 'TargetBlockNumberInPast', 'RescheduleNoChange']
   },
   /**
-   * Lookup360: up_data_structs::Collection<sp_core::crypto::AccountId32>
+   * Lookup361: up_data_structs::Collection<sp_core::crypto::AccountId32>
    **/
   UpDataStructsCollection: {
     owner: 'AccountId32',
@@ -2915,7 +2918,7 @@
     externalCollection: 'bool'
   },
   /**
-   * Lookup361: up_data_structs::SponsorshipState<sp_core::crypto::AccountId32>
+   * Lookup362: up_data_structs::SponsorshipState<sp_core::crypto::AccountId32>
    **/
   UpDataStructsSponsorshipStateAccountId32: {
     _enum: {
@@ -2925,7 +2928,7 @@
     }
   },
   /**
-   * Lookup362: up_data_structs::Properties
+   * Lookup363: up_data_structs::Properties
    **/
   UpDataStructsProperties: {
     map: 'UpDataStructsPropertiesMapBoundedVec',
@@ -2933,15 +2936,15 @@
     spaceLimit: 'u32'
   },
   /**
-   * Lookup363: up_data_structs::PropertiesMap<sp_runtime::bounded::bounded_vec::BoundedVec<T, S>>
+   * Lookup364: up_data_structs::PropertiesMap<sp_runtime::bounded::bounded_vec::BoundedVec<T, S>>
    **/
   UpDataStructsPropertiesMapBoundedVec: 'BTreeMap<Bytes, Bytes>',
   /**
-   * Lookup368: up_data_structs::PropertiesMap<up_data_structs::PropertyPermission>
+   * Lookup369: up_data_structs::PropertiesMap<up_data_structs::PropertyPermission>
    **/
   UpDataStructsPropertiesMapPropertyPermission: 'BTreeMap<Bytes, UpDataStructsPropertyPermission>',
   /**
-   * Lookup375: up_data_structs::CollectionStats
+   * Lookup376: up_data_structs::CollectionStats
    **/
   UpDataStructsCollectionStats: {
     created: 'u32',
@@ -2949,18 +2952,18 @@
     alive: 'u32'
   },
   /**
-   * Lookup376: up_data_structs::TokenChild
+   * Lookup377: up_data_structs::TokenChild
    **/
   UpDataStructsTokenChild: {
     token: 'u32',
     collection: 'u32'
   },
   /**
-   * Lookup377: PhantomType::up_data_structs<T>
+   * Lookup378: PhantomType::up_data_structs<T>
    **/
   PhantomTypeUpDataStructs: '[(UpDataStructsTokenData,UpDataStructsRpcCollection,RmrkTraitsCollectionCollectionInfo,RmrkTraitsNftNftInfo,RmrkTraitsResourceResourceInfo,RmrkTraitsPropertyPropertyInfo,RmrkTraitsBaseBaseInfo,RmrkTraitsPartPartType,RmrkTraitsTheme,RmrkTraitsNftNftChild);0]',
   /**
-   * Lookup379: up_data_structs::TokenData<pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
+   * Lookup380: up_data_structs::TokenData<pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
    **/
   UpDataStructsTokenData: {
     properties: 'Vec<UpDataStructsProperty>',
@@ -2968,7 +2971,7 @@
     pieces: 'u128'
   },
   /**
-   * Lookup381: up_data_structs::RpcCollection<sp_core::crypto::AccountId32>
+   * Lookup382: up_data_structs::RpcCollection<sp_core::crypto::AccountId32>
    **/
   UpDataStructsRpcCollection: {
     owner: 'AccountId32',
@@ -2984,7 +2987,7 @@
     readOnly: 'bool'
   },
   /**
-   * Lookup382: rmrk_traits::collection::CollectionInfo<sp_runtime::bounded::bounded_vec::BoundedVec<T, S>, sp_runtime::bounded::bounded_vec::BoundedVec<T, S>, sp_core::crypto::AccountId32>
+   * Lookup383: rmrk_traits::collection::CollectionInfo<sp_runtime::bounded::bounded_vec::BoundedVec<T, S>, sp_runtime::bounded::bounded_vec::BoundedVec<T, S>, sp_core::crypto::AccountId32>
    **/
   RmrkTraitsCollectionCollectionInfo: {
     issuer: 'AccountId32',
@@ -2994,7 +2997,7 @@
     nftsCount: 'u32'
   },
   /**
-   * Lookup383: rmrk_traits::nft::NftInfo<sp_core::crypto::AccountId32, sp_arithmetic::per_things::Permill, sp_runtime::bounded::bounded_vec::BoundedVec<T, S>>
+   * Lookup384: rmrk_traits::nft::NftInfo<sp_core::crypto::AccountId32, sp_arithmetic::per_things::Permill, sp_runtime::bounded::bounded_vec::BoundedVec<T, S>>
    **/
   RmrkTraitsNftNftInfo: {
     owner: 'RmrkTraitsNftAccountIdOrCollectionNftTuple',
@@ -3004,14 +3007,14 @@
     pending: 'bool'
   },
   /**
-   * Lookup385: rmrk_traits::nft::RoyaltyInfo<sp_core::crypto::AccountId32, sp_arithmetic::per_things::Permill>
+   * Lookup386: rmrk_traits::nft::RoyaltyInfo<sp_core::crypto::AccountId32, sp_arithmetic::per_things::Permill>
    **/
   RmrkTraitsNftRoyaltyInfo: {
     recipient: 'AccountId32',
     amount: 'Permill'
   },
   /**
-   * Lookup386: rmrk_traits::resource::ResourceInfo<sp_runtime::bounded::bounded_vec::BoundedVec<T, S>, sp_runtime::bounded::bounded_vec::BoundedVec<T, S>>
+   * Lookup387: rmrk_traits::resource::ResourceInfo<sp_runtime::bounded::bounded_vec::BoundedVec<T, S>, sp_runtime::bounded::bounded_vec::BoundedVec<T, S>>
    **/
   RmrkTraitsResourceResourceInfo: {
     id: 'u32',
@@ -3020,14 +3023,14 @@
     pendingRemoval: 'bool'
   },
   /**
-   * Lookup387: rmrk_traits::property::PropertyInfo<sp_runtime::bounded::bounded_vec::BoundedVec<T, S>, sp_runtime::bounded::bounded_vec::BoundedVec<T, S>>
+   * Lookup388: rmrk_traits::property::PropertyInfo<sp_runtime::bounded::bounded_vec::BoundedVec<T, S>, sp_runtime::bounded::bounded_vec::BoundedVec<T, S>>
    **/
   RmrkTraitsPropertyPropertyInfo: {
     key: 'Bytes',
     value: 'Bytes'
   },
   /**
-   * Lookup388: rmrk_traits::base::BaseInfo<sp_core::crypto::AccountId32, sp_runtime::bounded::bounded_vec::BoundedVec<T, S>>
+   * Lookup389: rmrk_traits::base::BaseInfo<sp_core::crypto::AccountId32, sp_runtime::bounded::bounded_vec::BoundedVec<T, S>>
    **/
   RmrkTraitsBaseBaseInfo: {
     issuer: 'AccountId32',
@@ -3035,86 +3038,86 @@
     symbol: 'Bytes'
   },
   /**
-   * Lookup389: rmrk_traits::nft::NftChild
+   * Lookup390: rmrk_traits::nft::NftChild
    **/
   RmrkTraitsNftNftChild: {
     collectionId: 'u32',
     nftId: 'u32'
   },
   /**
-   * Lookup391: pallet_common::pallet::Error<T>
+   * Lookup392: pallet_common::pallet::Error<T>
    **/
   PalletCommonError: {
     _enum: ['CollectionNotFound', 'MustBeTokenOwner', 'NoPermission', 'CantDestroyNotEmptyCollection', 'PublicMintingNotAllowed', 'AddressNotInAllowlist', 'CollectionNameLimitExceeded', 'CollectionDescriptionLimitExceeded', 'CollectionTokenPrefixLimitExceeded', 'TotalCollectionsLimitExceeded', 'CollectionAdminCountExceeded', 'CollectionLimitBoundsExceeded', 'OwnerPermissionsCantBeReverted', 'TransferNotAllowed', 'AccountTokenLimitExceeded', 'CollectionTokenLimitExceeded', 'MetadataFlagFrozen', 'TokenNotFound', 'TokenValueTooLow', 'ApprovedValueTooLow', 'CantApproveMoreThanOwned', 'AddressIsZero', 'UnsupportedOperation', 'NotSufficientFounds', 'UserIsNotAllowedToNest', 'SourceCollectionIsNotAllowedToNest', 'CollectionFieldSizeExceeded', 'NoSpaceForProperty', 'PropertyLimitReached', 'PropertyKeyIsTooLong', 'InvalidCharacterInPropertyKey', 'EmptyPropertyKey', 'CollectionIsExternal', 'CollectionIsInternal']
   },
   /**
-   * Lookup393: pallet_fungible::pallet::Error<T>
+   * Lookup394: pallet_fungible::pallet::Error<T>
    **/
   PalletFungibleError: {
     _enum: ['NotFungibleDataUsedToMintFungibleCollectionToken', 'FungibleItemsHaveNoId', 'FungibleItemsDontHaveData', 'FungibleDisallowsNesting', 'SettingPropertiesNotAllowed']
   },
   /**
-   * Lookup394: pallet_refungible::ItemData
+   * Lookup395: pallet_refungible::ItemData
    **/
   PalletRefungibleItemData: {
     constData: 'Bytes'
   },
   /**
-   * Lookup399: pallet_refungible::pallet::Error<T>
+   * Lookup400: pallet_refungible::pallet::Error<T>
    **/
   PalletRefungibleError: {
     _enum: ['NotRefungibleDataUsedToMintFungibleCollectionToken', 'WrongRefungiblePieces', 'RepartitionWhileNotOwningAllPieces', 'RefungibleDisallowsNesting', 'SettingPropertiesNotAllowed']
   },
   /**
-   * Lookup400: pallet_nonfungible::ItemData<pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
+   * Lookup401: pallet_nonfungible::ItemData<pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
    **/
   PalletNonfungibleItemData: {
     owner: 'PalletEvmAccountBasicCrossAccountIdRepr'
   },
   /**
-   * Lookup402: up_data_structs::PropertyScope
+   * Lookup403: up_data_structs::PropertyScope
    **/
   UpDataStructsPropertyScope: {
     _enum: ['None', 'Rmrk', 'Eth']
   },
   /**
-   * Lookup404: pallet_nonfungible::pallet::Error<T>
+   * Lookup405: pallet_nonfungible::pallet::Error<T>
    **/
   PalletNonfungibleError: {
     _enum: ['NotNonfungibleDataUsedToMintFungibleCollectionToken', 'NonfungibleItemsHaveNoAmount', 'CantBurnNftWithChildren']
   },
   /**
-   * Lookup405: pallet_structure::pallet::Error<T>
+   * Lookup406: pallet_structure::pallet::Error<T>
    **/
   PalletStructureError: {
     _enum: ['OuroborosDetected', 'DepthLimit', 'BreadthLimit', 'TokenNotFound']
   },
   /**
-   * Lookup406: pallet_rmrk_core::pallet::Error<T>
+   * Lookup407: pallet_rmrk_core::pallet::Error<T>
    **/
   PalletRmrkCoreError: {
     _enum: ['CorruptedCollectionType', 'RmrkPropertyKeyIsTooLong', 'RmrkPropertyValueIsTooLong', 'RmrkPropertyIsNotFound', 'UnableToDecodeRmrkData', 'CollectionNotEmpty', 'NoAvailableCollectionId', 'NoAvailableNftId', 'CollectionUnknown', 'NoPermission', 'NonTransferable', 'CollectionFullOrLocked', 'ResourceDoesntExist', 'CannotSendToDescendentOrSelf', 'CannotAcceptNonOwnedNft', 'CannotRejectNonOwnedNft', 'CannotRejectNonPendingNft', 'ResourceNotPending', 'NoAvailableResourceId']
   },
   /**
-   * Lookup408: pallet_rmrk_equip::pallet::Error<T>
+   * Lookup409: pallet_rmrk_equip::pallet::Error<T>
    **/
   PalletRmrkEquipError: {
     _enum: ['PermissionError', 'NoAvailableBaseId', 'NoAvailablePartId', 'BaseDoesntExist', 'NeedsDefaultThemeFirst', 'PartDoesntExist', 'NoEquippableOnFixedPart']
   },
   /**
-   * Lookup410: pallet_app_promotion::pallet::Error<T>
+   * Lookup412: pallet_app_promotion::pallet::Error<T>
    **/
   PalletAppPromotionError: {
     _enum: ['AdminNotSet', 'NoPermission', 'NotSufficientFounds', 'InvalidArgument']
   },
   /**
-   * Lookup413: pallet_evm::pallet::Error<T>
+   * Lookup415: pallet_evm::pallet::Error<T>
    **/
   PalletEvmError: {
     _enum: ['BalanceLow', 'FeeOverflow', 'PaymentOverflow', 'WithdrawFailed', 'GasPriceTooLow', 'InvalidNonce']
   },
   /**
-   * Lookup416: fp_rpc::TransactionStatus
+   * Lookup418: fp_rpc::TransactionStatus
    **/
   FpRpcTransactionStatus: {
     transactionHash: 'H256',
@@ -3126,11 +3129,11 @@
     logsBloom: 'EthbloomBloom'
   },
   /**
-   * Lookup418: ethbloom::Bloom
+   * Lookup420: ethbloom::Bloom
    **/
   EthbloomBloom: '[u8;256]',
   /**
-   * Lookup420: ethereum::receipt::ReceiptV3
+   * Lookup422: ethereum::receipt::ReceiptV3
    **/
   EthereumReceiptReceiptV3: {
     _enum: {
@@ -3140,7 +3143,7 @@
     }
   },
   /**
-   * Lookup421: ethereum::receipt::EIP658ReceiptData
+   * Lookup423: ethereum::receipt::EIP658ReceiptData
    **/
   EthereumReceiptEip658ReceiptData: {
     statusCode: 'u8',
@@ -3149,7 +3152,7 @@
     logs: 'Vec<EthereumLog>'
   },
   /**
-   * Lookup422: ethereum::block::Block<ethereum::transaction::TransactionV2>
+   * Lookup424: ethereum::block::Block<ethereum::transaction::TransactionV2>
    **/
   EthereumBlock: {
     header: 'EthereumHeader',
@@ -3157,7 +3160,7 @@
     ommers: 'Vec<EthereumHeader>'
   },
   /**
-   * Lookup423: ethereum::header::Header
+   * Lookup425: ethereum::header::Header
    **/
   EthereumHeader: {
     parentHash: 'H256',
@@ -3177,23 +3180,23 @@
     nonce: 'EthereumTypesHashH64'
   },
   /**
-   * Lookup424: ethereum_types::hash::H64
+   * Lookup426: ethereum_types::hash::H64
    **/
   EthereumTypesHashH64: '[u8;8]',
   /**
-   * Lookup429: pallet_ethereum::pallet::Error<T>
+   * Lookup431: pallet_ethereum::pallet::Error<T>
    **/
   PalletEthereumError: {
     _enum: ['InvalidSignature', 'PreLogExists']
   },
   /**
-   * Lookup430: pallet_evm_coder_substrate::pallet::Error<T>
+   * Lookup432: pallet_evm_coder_substrate::pallet::Error<T>
    **/
   PalletEvmCoderSubstrateError: {
     _enum: ['OutOfGas', 'OutOfFund']
   },
   /**
-   * Lookup431: up_data_structs::SponsorshipState<pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
+   * Lookup433: up_data_structs::SponsorshipState<pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
    **/
   UpDataStructsSponsorshipStateBasicCrossAccountIdRepr: {
     _enum: {
@@ -3203,25 +3206,25 @@
     }
   },
   /**
-   * Lookup432: pallet_evm_contract_helpers::SponsoringModeT
+   * Lookup434: pallet_evm_contract_helpers::SponsoringModeT
    **/
   PalletEvmContractHelpersSponsoringModeT: {
     _enum: ['Disabled', 'Allowlisted', 'Generous']
   },
   /**
-   * Lookup434: pallet_evm_contract_helpers::pallet::Error<T>
+   * Lookup436: pallet_evm_contract_helpers::pallet::Error<T>
    **/
   PalletEvmContractHelpersError: {
     _enum: ['NoPermission', 'NoPendingSponsor']
   },
   /**
-   * Lookup435: pallet_evm_migration::pallet::Error<T>
+   * Lookup437: pallet_evm_migration::pallet::Error<T>
    **/
   PalletEvmMigrationError: {
     _enum: ['AccountNotEmpty', 'AccountIsNotMigrating']
   },
   /**
-   * Lookup437: sp_runtime::MultiSignature
+   * Lookup439: sp_runtime::MultiSignature
    **/
   SpRuntimeMultiSignature: {
     _enum: {
@@ -3231,43 +3234,43 @@
     }
   },
   /**
-   * Lookup438: sp_core::ed25519::Signature
+   * Lookup440: sp_core::ed25519::Signature
    **/
   SpCoreEd25519Signature: '[u8;64]',
   /**
-   * Lookup440: sp_core::sr25519::Signature
+   * Lookup442: sp_core::sr25519::Signature
    **/
   SpCoreSr25519Signature: '[u8;64]',
   /**
-   * Lookup441: sp_core::ecdsa::Signature
+   * Lookup443: sp_core::ecdsa::Signature
    **/
   SpCoreEcdsaSignature: '[u8;65]',
   /**
-   * Lookup444: frame_system::extensions::check_spec_version::CheckSpecVersion<T>
+   * Lookup446: frame_system::extensions::check_spec_version::CheckSpecVersion<T>
    **/
   FrameSystemExtensionsCheckSpecVersion: 'Null',
   /**
-   * Lookup445: frame_system::extensions::check_genesis::CheckGenesis<T>
+   * Lookup447: frame_system::extensions::check_genesis::CheckGenesis<T>
    **/
   FrameSystemExtensionsCheckGenesis: 'Null',
   /**
-   * Lookup448: frame_system::extensions::check_nonce::CheckNonce<T>
+   * Lookup450: frame_system::extensions::check_nonce::CheckNonce<T>
    **/
   FrameSystemExtensionsCheckNonce: 'Compact<u32>',
   /**
-   * Lookup449: frame_system::extensions::check_weight::CheckWeight<T>
+   * Lookup451: frame_system::extensions::check_weight::CheckWeight<T>
    **/
   FrameSystemExtensionsCheckWeight: 'Null',
   /**
-   * Lookup450: pallet_template_transaction_payment::ChargeTransactionPayment<opal_runtime::Runtime>
+   * Lookup452: pallet_template_transaction_payment::ChargeTransactionPayment<opal_runtime::Runtime>
    **/
   PalletTemplateTransactionPaymentChargeTransactionPayment: 'Compact<u128>',
   /**
-   * Lookup451: opal_runtime::Runtime
+   * Lookup453: opal_runtime::Runtime
    **/
   OpalRuntimeRuntime: 'Null',
   /**
-   * Lookup452: pallet_ethereum::FakeTransactionFinalizer<opal_runtime::Runtime>
+   * Lookup454: pallet_ethereum::FakeTransactionFinalizer<opal_runtime::Runtime>
    **/
   PalletEthereumFakeTransactionFinalizer: 'Null'
 };
modifiedtests/src/interfaces/types-lookup.tsdiffbeforeafterboth
--- a/tests/src/interfaces/types-lookup.ts
+++ b/tests/src/interfaces/types-lookup.ts
@@ -1201,7 +1201,7 @@
   /** @name PalletAppPromotionEvent (103) */
   interface PalletAppPromotionEvent extends Enum {
     readonly isStakingRecalculation: boolean;
-    readonly asStakingRecalculation: ITuple<[u128, u128]>;
+    readonly asStakingRecalculation: ITuple<[AccountId32, u128, u128]>;
     readonly type: 'StakingRecalculation';
   }
 
@@ -2682,22 +2682,26 @@
     readonly asSponsorCollection: {
       readonly collectionId: u32;
     } & Struct;
-    readonly isStopSponsorignCollection: boolean;
-    readonly asStopSponsorignCollection: {
+    readonly isStopSponsoringCollection: boolean;
+    readonly asStopSponsoringCollection: {
       readonly collectionId: u32;
     } & Struct;
     readonly isSponsorConract: boolean;
     readonly asSponsorConract: {
       readonly contractId: H160;
     } & Struct;
-    readonly isStopSponsorignContract: boolean;
-    readonly asStopSponsorignContract: {
+    readonly isStopSponsoringContract: boolean;
+    readonly asStopSponsoringContract: {
       readonly contractId: H160;
     } & Struct;
-    readonly type: 'SetAdminAddress' | 'StartAppPromotion' | 'StopAppPromotion' | 'Stake' | 'Unstake' | 'SponsorCollection' | 'StopSponsorignCollection' | 'SponsorConract' | 'StopSponsorignContract';
+    readonly isPayoutStakers: boolean;
+    readonly asPayoutStakers: {
+      readonly stakersNumber: Option<u8>;
+    } & Struct;
+    readonly type: 'SetAdminAddress' | 'StartAppPromotion' | 'StopAppPromotion' | 'Stake' | 'Unstake' | 'SponsorCollection' | 'StopSponsoringCollection' | 'SponsorConract' | 'StopSponsoringContract' | 'PayoutStakers';
   }
 
-  /** @name PalletEvmCall (305) */
+  /** @name PalletEvmCall (306) */
   interface PalletEvmCall extends Enum {
     readonly isWithdraw: boolean;
     readonly asWithdraw: {
@@ -2742,7 +2746,7 @@
     readonly type: 'Withdraw' | 'Call' | 'Create' | 'Create2';
   }
 
-  /** @name PalletEthereumCall (309) */
+  /** @name PalletEthereumCall (310) */
   interface PalletEthereumCall extends Enum {
     readonly isTransact: boolean;
     readonly asTransact: {
@@ -2751,7 +2755,7 @@
     readonly type: 'Transact';
   }
 
-  /** @name EthereumTransactionTransactionV2 (310) */
+  /** @name EthereumTransactionTransactionV2 (311) */
   interface EthereumTransactionTransactionV2 extends Enum {
     readonly isLegacy: boolean;
     readonly asLegacy: EthereumTransactionLegacyTransaction;
@@ -2762,7 +2766,7 @@
     readonly type: 'Legacy' | 'Eip2930' | 'Eip1559';
   }
 
-  /** @name EthereumTransactionLegacyTransaction (311) */
+  /** @name EthereumTransactionLegacyTransaction (312) */
   interface EthereumTransactionLegacyTransaction extends Struct {
     readonly nonce: U256;
     readonly gasPrice: U256;
@@ -2773,7 +2777,7 @@
     readonly signature: EthereumTransactionTransactionSignature;
   }
 
-  /** @name EthereumTransactionTransactionAction (312) */
+  /** @name EthereumTransactionTransactionAction (313) */
   interface EthereumTransactionTransactionAction extends Enum {
     readonly isCall: boolean;
     readonly asCall: H160;
@@ -2781,14 +2785,14 @@
     readonly type: 'Call' | 'Create';
   }
 
-  /** @name EthereumTransactionTransactionSignature (313) */
+  /** @name EthereumTransactionTransactionSignature (314) */
   interface EthereumTransactionTransactionSignature extends Struct {
     readonly v: u64;
     readonly r: H256;
     readonly s: H256;
   }
 
-  /** @name EthereumTransactionEip2930Transaction (315) */
+  /** @name EthereumTransactionEip2930Transaction (316) */
   interface EthereumTransactionEip2930Transaction extends Struct {
     readonly chainId: u64;
     readonly nonce: U256;
@@ -2803,13 +2807,13 @@
     readonly s: H256;
   }
 
-  /** @name EthereumTransactionAccessListItem (317) */
+  /** @name EthereumTransactionAccessListItem (318) */
   interface EthereumTransactionAccessListItem extends Struct {
     readonly address: H160;
     readonly storageKeys: Vec<H256>;
   }
 
-  /** @name EthereumTransactionEip1559Transaction (318) */
+  /** @name EthereumTransactionEip1559Transaction (319) */
   interface EthereumTransactionEip1559Transaction extends Struct {
     readonly chainId: u64;
     readonly nonce: U256;
@@ -2825,7 +2829,7 @@
     readonly s: H256;
   }
 
-  /** @name PalletEvmMigrationCall (319) */
+  /** @name PalletEvmMigrationCall (320) */
   interface PalletEvmMigrationCall extends Enum {
     readonly isBegin: boolean;
     readonly asBegin: {
@@ -2844,13 +2848,13 @@
     readonly type: 'Begin' | 'SetData' | 'Finish';
   }
 
-  /** @name PalletSudoError (322) */
+  /** @name PalletSudoError (323) */
   interface PalletSudoError extends Enum {
     readonly isRequireSudo: boolean;
     readonly type: 'RequireSudo';
   }
 
-  /** @name OrmlVestingModuleError (324) */
+  /** @name OrmlVestingModuleError (325) */
   interface OrmlVestingModuleError extends Enum {
     readonly isZeroVestingPeriod: boolean;
     readonly isZeroVestingPeriodCount: boolean;
@@ -2861,21 +2865,21 @@
     readonly type: 'ZeroVestingPeriod' | 'ZeroVestingPeriodCount' | 'InsufficientBalanceToLock' | 'TooManyVestingSchedules' | 'AmountLow' | 'MaxVestingSchedulesExceeded';
   }
 
-  /** @name CumulusPalletXcmpQueueInboundChannelDetails (326) */
+  /** @name CumulusPalletXcmpQueueInboundChannelDetails (327) */
   interface CumulusPalletXcmpQueueInboundChannelDetails extends Struct {
     readonly sender: u32;
     readonly state: CumulusPalletXcmpQueueInboundState;
     readonly messageMetadata: Vec<ITuple<[u32, PolkadotParachainPrimitivesXcmpMessageFormat]>>;
   }
 
-  /** @name CumulusPalletXcmpQueueInboundState (327) */
+  /** @name CumulusPalletXcmpQueueInboundState (328) */
   interface CumulusPalletXcmpQueueInboundState extends Enum {
     readonly isOk: boolean;
     readonly isSuspended: boolean;
     readonly type: 'Ok' | 'Suspended';
   }
 
-  /** @name PolkadotParachainPrimitivesXcmpMessageFormat (330) */
+  /** @name PolkadotParachainPrimitivesXcmpMessageFormat (331) */
   interface PolkadotParachainPrimitivesXcmpMessageFormat extends Enum {
     readonly isConcatenatedVersionedXcm: boolean;
     readonly isConcatenatedEncodedBlob: boolean;
@@ -2883,7 +2887,7 @@
     readonly type: 'ConcatenatedVersionedXcm' | 'ConcatenatedEncodedBlob' | 'Signals';
   }
 
-  /** @name CumulusPalletXcmpQueueOutboundChannelDetails (333) */
+  /** @name CumulusPalletXcmpQueueOutboundChannelDetails (334) */
   interface CumulusPalletXcmpQueueOutboundChannelDetails extends Struct {
     readonly recipient: u32;
     readonly state: CumulusPalletXcmpQueueOutboundState;
@@ -2892,14 +2896,14 @@
     readonly lastIndex: u16;
   }
 
-  /** @name CumulusPalletXcmpQueueOutboundState (334) */
+  /** @name CumulusPalletXcmpQueueOutboundState (335) */
   interface CumulusPalletXcmpQueueOutboundState extends Enum {
     readonly isOk: boolean;
     readonly isSuspended: boolean;
     readonly type: 'Ok' | 'Suspended';
   }
 
-  /** @name CumulusPalletXcmpQueueQueueConfigData (336) */
+  /** @name CumulusPalletXcmpQueueQueueConfigData (337) */
   interface CumulusPalletXcmpQueueQueueConfigData extends Struct {
     readonly suspendThreshold: u32;
     readonly dropThreshold: u32;
@@ -2909,7 +2913,7 @@
     readonly xcmpMaxIndividualWeight: u64;
   }
 
-  /** @name CumulusPalletXcmpQueueError (338) */
+  /** @name CumulusPalletXcmpQueueError (339) */
   interface CumulusPalletXcmpQueueError extends Enum {
     readonly isFailedToSend: boolean;
     readonly isBadXcmOrigin: boolean;
@@ -2919,7 +2923,7 @@
     readonly type: 'FailedToSend' | 'BadXcmOrigin' | 'BadXcm' | 'BadOverweightIndex' | 'WeightOverLimit';
   }
 
-  /** @name PalletXcmError (339) */
+  /** @name PalletXcmError (340) */
   interface PalletXcmError extends Enum {
     readonly isUnreachable: boolean;
     readonly isSendFailure: boolean;
@@ -2937,29 +2941,29 @@
     readonly type: 'Unreachable' | 'SendFailure' | 'Filtered' | 'UnweighableMessage' | 'DestinationNotInvertible' | 'Empty' | 'CannotReanchor' | 'TooManyAssets' | 'InvalidOrigin' | 'BadVersion' | 'BadLocation' | 'NoSubscription' | 'AlreadySubscribed';
   }
 
-  /** @name CumulusPalletXcmError (340) */
+  /** @name CumulusPalletXcmError (341) */
   type CumulusPalletXcmError = Null;
 
-  /** @name CumulusPalletDmpQueueConfigData (341) */
+  /** @name CumulusPalletDmpQueueConfigData (342) */
   interface CumulusPalletDmpQueueConfigData extends Struct {
     readonly maxIndividual: u64;
   }
 
-  /** @name CumulusPalletDmpQueuePageIndexData (342) */
+  /** @name CumulusPalletDmpQueuePageIndexData (343) */
   interface CumulusPalletDmpQueuePageIndexData extends Struct {
     readonly beginUsed: u32;
     readonly endUsed: u32;
     readonly overweightCount: u64;
   }
 
-  /** @name CumulusPalletDmpQueueError (345) */
+  /** @name CumulusPalletDmpQueueError (346) */
   interface CumulusPalletDmpQueueError extends Enum {
     readonly isUnknown: boolean;
     readonly isOverLimit: boolean;
     readonly type: 'Unknown' | 'OverLimit';
   }
 
-  /** @name PalletUniqueError (349) */
+  /** @name PalletUniqueError (350) */
   interface PalletUniqueError extends Enum {
     readonly isCollectionDecimalPointLimitExceeded: boolean;
     readonly isConfirmUnsetSponsorFail: boolean;
@@ -2968,7 +2972,7 @@
     readonly type: 'CollectionDecimalPointLimitExceeded' | 'ConfirmUnsetSponsorFail' | 'EmptyArgument' | 'RepartitionCalledOnNonRefungibleCollection';
   }
 
-  /** @name PalletUniqueSchedulerScheduledV3 (352) */
+  /** @name PalletUniqueSchedulerScheduledV3 (353) */
   interface PalletUniqueSchedulerScheduledV3 extends Struct {
     readonly maybeId: Option<U8aFixed>;
     readonly priority: u8;
@@ -2977,7 +2981,7 @@
     readonly origin: OpalRuntimeOriginCaller;
   }
 
-  /** @name OpalRuntimeOriginCaller (353) */
+  /** @name OpalRuntimeOriginCaller (354) */
   interface OpalRuntimeOriginCaller extends Enum {
     readonly isSystem: boolean;
     readonly asSystem: FrameSupportDispatchRawOrigin;
@@ -2991,7 +2995,7 @@
     readonly type: 'System' | 'Void' | 'PolkadotXcm' | 'CumulusXcm' | 'Ethereum';
   }
 
-  /** @name FrameSupportDispatchRawOrigin (354) */
+  /** @name FrameSupportDispatchRawOrigin (355) */
   interface FrameSupportDispatchRawOrigin extends Enum {
     readonly isRoot: boolean;
     readonly isSigned: boolean;
@@ -3000,7 +3004,7 @@
     readonly type: 'Root' | 'Signed' | 'None';
   }
 
-  /** @name PalletXcmOrigin (355) */
+  /** @name PalletXcmOrigin (356) */
   interface PalletXcmOrigin extends Enum {
     readonly isXcm: boolean;
     readonly asXcm: XcmV1MultiLocation;
@@ -3009,7 +3013,7 @@
     readonly type: 'Xcm' | 'Response';
   }
 
-  /** @name CumulusPalletXcmOrigin (356) */
+  /** @name CumulusPalletXcmOrigin (357) */
   interface CumulusPalletXcmOrigin extends Enum {
     readonly isRelay: boolean;
     readonly isSiblingParachain: boolean;
@@ -3017,17 +3021,17 @@
     readonly type: 'Relay' | 'SiblingParachain';
   }
 
-  /** @name PalletEthereumRawOrigin (357) */
+  /** @name PalletEthereumRawOrigin (358) */
   interface PalletEthereumRawOrigin extends Enum {
     readonly isEthereumTransaction: boolean;
     readonly asEthereumTransaction: H160;
     readonly type: 'EthereumTransaction';
   }
 
-  /** @name SpCoreVoid (358) */
+  /** @name SpCoreVoid (359) */
   type SpCoreVoid = Null;
 
-  /** @name PalletUniqueSchedulerError (359) */
+  /** @name PalletUniqueSchedulerError (360) */
   interface PalletUniqueSchedulerError extends Enum {
     readonly isFailedToSchedule: boolean;
     readonly isNotFound: boolean;
@@ -3036,7 +3040,7 @@
     readonly type: 'FailedToSchedule' | 'NotFound' | 'TargetBlockNumberInPast' | 'RescheduleNoChange';
   }
 
-  /** @name UpDataStructsCollection (360) */
+  /** @name UpDataStructsCollection (361) */
   interface UpDataStructsCollection extends Struct {
     readonly owner: AccountId32;
     readonly mode: UpDataStructsCollectionMode;
@@ -3049,7 +3053,7 @@
     readonly externalCollection: bool;
   }
 
-  /** @name UpDataStructsSponsorshipStateAccountId32 (361) */
+  /** @name UpDataStructsSponsorshipStateAccountId32 (362) */
   interface UpDataStructsSponsorshipStateAccountId32 extends Enum {
     readonly isDisabled: boolean;
     readonly isUnconfirmed: boolean;
@@ -3059,43 +3063,43 @@
     readonly type: 'Disabled' | 'Unconfirmed' | 'Confirmed';
   }
 
-  /** @name UpDataStructsProperties (362) */
+  /** @name UpDataStructsProperties (363) */
   interface UpDataStructsProperties extends Struct {
     readonly map: UpDataStructsPropertiesMapBoundedVec;
     readonly consumedSpace: u32;
     readonly spaceLimit: u32;
   }
 
-  /** @name UpDataStructsPropertiesMapBoundedVec (363) */
+  /** @name UpDataStructsPropertiesMapBoundedVec (364) */
   interface UpDataStructsPropertiesMapBoundedVec extends BTreeMap<Bytes, Bytes> {}
 
-  /** @name UpDataStructsPropertiesMapPropertyPermission (368) */
+  /** @name UpDataStructsPropertiesMapPropertyPermission (369) */
   interface UpDataStructsPropertiesMapPropertyPermission extends BTreeMap<Bytes, UpDataStructsPropertyPermission> {}
 
-  /** @name UpDataStructsCollectionStats (375) */
+  /** @name UpDataStructsCollectionStats (376) */
   interface UpDataStructsCollectionStats extends Struct {
     readonly created: u32;
     readonly destroyed: u32;
     readonly alive: u32;
   }
 
-  /** @name UpDataStructsTokenChild (376) */
+  /** @name UpDataStructsTokenChild (377) */
   interface UpDataStructsTokenChild extends Struct {
     readonly token: u32;
     readonly collection: u32;
   }
 
-  /** @name PhantomTypeUpDataStructs (377) */
+  /** @name PhantomTypeUpDataStructs (378) */
   interface PhantomTypeUpDataStructs extends Vec<ITuple<[UpDataStructsTokenData, UpDataStructsRpcCollection, RmrkTraitsCollectionCollectionInfo, RmrkTraitsNftNftInfo, RmrkTraitsResourceResourceInfo, RmrkTraitsPropertyPropertyInfo, RmrkTraitsBaseBaseInfo, RmrkTraitsPartPartType, RmrkTraitsTheme, RmrkTraitsNftNftChild]>> {}
 
-  /** @name UpDataStructsTokenData (379) */
+  /** @name UpDataStructsTokenData (380) */
   interface UpDataStructsTokenData extends Struct {
     readonly properties: Vec<UpDataStructsProperty>;
     readonly owner: Option<PalletEvmAccountBasicCrossAccountIdRepr>;
     readonly pieces: u128;
   }
 
-  /** @name UpDataStructsRpcCollection (381) */
+  /** @name UpDataStructsRpcCollection (382) */
   interface UpDataStructsRpcCollection extends Struct {
     readonly owner: AccountId32;
     readonly mode: UpDataStructsCollectionMode;
@@ -3110,7 +3114,7 @@
     readonly readOnly: bool;
   }
 
-  /** @name RmrkTraitsCollectionCollectionInfo (382) */
+  /** @name RmrkTraitsCollectionCollectionInfo (383) */
   interface RmrkTraitsCollectionCollectionInfo extends Struct {
     readonly issuer: AccountId32;
     readonly metadata: Bytes;
@@ -3119,7 +3123,7 @@
     readonly nftsCount: u32;
   }
 
-  /** @name RmrkTraitsNftNftInfo (383) */
+  /** @name RmrkTraitsNftNftInfo (384) */
   interface RmrkTraitsNftNftInfo extends Struct {
     readonly owner: RmrkTraitsNftAccountIdOrCollectionNftTuple;
     readonly royalty: Option<RmrkTraitsNftRoyaltyInfo>;
@@ -3128,13 +3132,13 @@
     readonly pending: bool;
   }
 
-  /** @name RmrkTraitsNftRoyaltyInfo (385) */
+  /** @name RmrkTraitsNftRoyaltyInfo (386) */
   interface RmrkTraitsNftRoyaltyInfo extends Struct {
     readonly recipient: AccountId32;
     readonly amount: Permill;
   }
 
-  /** @name RmrkTraitsResourceResourceInfo (386) */
+  /** @name RmrkTraitsResourceResourceInfo (387) */
   interface RmrkTraitsResourceResourceInfo extends Struct {
     readonly id: u32;
     readonly resource: RmrkTraitsResourceResourceTypes;
@@ -3142,26 +3146,26 @@
     readonly pendingRemoval: bool;
   }
 
-  /** @name RmrkTraitsPropertyPropertyInfo (387) */
+  /** @name RmrkTraitsPropertyPropertyInfo (388) */
   interface RmrkTraitsPropertyPropertyInfo extends Struct {
     readonly key: Bytes;
     readonly value: Bytes;
   }
 
-  /** @name RmrkTraitsBaseBaseInfo (388) */
+  /** @name RmrkTraitsBaseBaseInfo (389) */
   interface RmrkTraitsBaseBaseInfo extends Struct {
     readonly issuer: AccountId32;
     readonly baseType: Bytes;
     readonly symbol: Bytes;
   }
 
-  /** @name RmrkTraitsNftNftChild (389) */
+  /** @name RmrkTraitsNftNftChild (390) */
   interface RmrkTraitsNftNftChild extends Struct {
     readonly collectionId: u32;
     readonly nftId: u32;
   }
 
-  /** @name PalletCommonError (391) */
+  /** @name PalletCommonError (392) */
   interface PalletCommonError extends Enum {
     readonly isCollectionNotFound: boolean;
     readonly isMustBeTokenOwner: boolean;
@@ -3200,7 +3204,7 @@
     readonly type: 'CollectionNotFound' | 'MustBeTokenOwner' | 'NoPermission' | 'CantDestroyNotEmptyCollection' | 'PublicMintingNotAllowed' | 'AddressNotInAllowlist' | 'CollectionNameLimitExceeded' | 'CollectionDescriptionLimitExceeded' | 'CollectionTokenPrefixLimitExceeded' | 'TotalCollectionsLimitExceeded' | 'CollectionAdminCountExceeded' | 'CollectionLimitBoundsExceeded' | 'OwnerPermissionsCantBeReverted' | 'TransferNotAllowed' | 'AccountTokenLimitExceeded' | 'CollectionTokenLimitExceeded' | 'MetadataFlagFrozen' | 'TokenNotFound' | 'TokenValueTooLow' | 'ApprovedValueTooLow' | 'CantApproveMoreThanOwned' | 'AddressIsZero' | 'UnsupportedOperation' | 'NotSufficientFounds' | 'UserIsNotAllowedToNest' | 'SourceCollectionIsNotAllowedToNest' | 'CollectionFieldSizeExceeded' | 'NoSpaceForProperty' | 'PropertyLimitReached' | 'PropertyKeyIsTooLong' | 'InvalidCharacterInPropertyKey' | 'EmptyPropertyKey' | 'CollectionIsExternal' | 'CollectionIsInternal';
   }
 
-  /** @name PalletFungibleError (393) */
+  /** @name PalletFungibleError (394) */
   interface PalletFungibleError extends Enum {
     readonly isNotFungibleDataUsedToMintFungibleCollectionToken: boolean;
     readonly isFungibleItemsHaveNoId: boolean;
@@ -3210,12 +3214,12 @@
     readonly type: 'NotFungibleDataUsedToMintFungibleCollectionToken' | 'FungibleItemsHaveNoId' | 'FungibleItemsDontHaveData' | 'FungibleDisallowsNesting' | 'SettingPropertiesNotAllowed';
   }
 
-  /** @name PalletRefungibleItemData (394) */
+  /** @name PalletRefungibleItemData (395) */
   interface PalletRefungibleItemData extends Struct {
     readonly constData: Bytes;
   }
 
-  /** @name PalletRefungibleError (399) */
+  /** @name PalletRefungibleError (400) */
   interface PalletRefungibleError extends Enum {
     readonly isNotRefungibleDataUsedToMintFungibleCollectionToken: boolean;
     readonly isWrongRefungiblePieces: boolean;
@@ -3225,12 +3229,12 @@
     readonly type: 'NotRefungibleDataUsedToMintFungibleCollectionToken' | 'WrongRefungiblePieces' | 'RepartitionWhileNotOwningAllPieces' | 'RefungibleDisallowsNesting' | 'SettingPropertiesNotAllowed';
   }
 
-  /** @name PalletNonfungibleItemData (400) */
+  /** @name PalletNonfungibleItemData (401) */
   interface PalletNonfungibleItemData extends Struct {
     readonly owner: PalletEvmAccountBasicCrossAccountIdRepr;
   }
 
-  /** @name UpDataStructsPropertyScope (402) */
+  /** @name UpDataStructsPropertyScope (403) */
   interface UpDataStructsPropertyScope extends Enum {
     readonly isNone: boolean;
     readonly isRmrk: boolean;
@@ -3238,7 +3242,7 @@
     readonly type: 'None' | 'Rmrk' | 'Eth';
   }
 
-  /** @name PalletNonfungibleError (404) */
+  /** @name PalletNonfungibleError (405) */
   interface PalletNonfungibleError extends Enum {
     readonly isNotNonfungibleDataUsedToMintFungibleCollectionToken: boolean;
     readonly isNonfungibleItemsHaveNoAmount: boolean;
@@ -3246,7 +3250,7 @@
     readonly type: 'NotNonfungibleDataUsedToMintFungibleCollectionToken' | 'NonfungibleItemsHaveNoAmount' | 'CantBurnNftWithChildren';
   }
 
-  /** @name PalletStructureError (405) */
+  /** @name PalletStructureError (406) */
   interface PalletStructureError extends Enum {
     readonly isOuroborosDetected: boolean;
     readonly isDepthLimit: boolean;
@@ -3255,7 +3259,7 @@
     readonly type: 'OuroborosDetected' | 'DepthLimit' | 'BreadthLimit' | 'TokenNotFound';
   }
 
-  /** @name PalletRmrkCoreError (406) */
+  /** @name PalletRmrkCoreError (407) */
   interface PalletRmrkCoreError extends Enum {
     readonly isCorruptedCollectionType: boolean;
     readonly isRmrkPropertyKeyIsTooLong: boolean;
@@ -3279,7 +3283,7 @@
     readonly type: 'CorruptedCollectionType' | 'RmrkPropertyKeyIsTooLong' | 'RmrkPropertyValueIsTooLong' | 'RmrkPropertyIsNotFound' | 'UnableToDecodeRmrkData' | 'CollectionNotEmpty' | 'NoAvailableCollectionId' | 'NoAvailableNftId' | 'CollectionUnknown' | 'NoPermission' | 'NonTransferable' | 'CollectionFullOrLocked' | 'ResourceDoesntExist' | 'CannotSendToDescendentOrSelf' | 'CannotAcceptNonOwnedNft' | 'CannotRejectNonOwnedNft' | 'CannotRejectNonPendingNft' | 'ResourceNotPending' | 'NoAvailableResourceId';
   }
 
-  /** @name PalletRmrkEquipError (408) */
+  /** @name PalletRmrkEquipError (409) */
   interface PalletRmrkEquipError extends Enum {
     readonly isPermissionError: boolean;
     readonly isNoAvailableBaseId: boolean;
@@ -3291,7 +3295,7 @@
     readonly type: 'PermissionError' | 'NoAvailableBaseId' | 'NoAvailablePartId' | 'BaseDoesntExist' | 'NeedsDefaultThemeFirst' | 'PartDoesntExist' | 'NoEquippableOnFixedPart';
   }
 
-  /** @name PalletAppPromotionError (410) */
+  /** @name PalletAppPromotionError (412) */
   interface PalletAppPromotionError extends Enum {
     readonly isAdminNotSet: boolean;
     readonly isNoPermission: boolean;
@@ -3300,7 +3304,7 @@
     readonly type: 'AdminNotSet' | 'NoPermission' | 'NotSufficientFounds' | 'InvalidArgument';
   }
 
-  /** @name PalletEvmError (413) */
+  /** @name PalletEvmError (415) */
   interface PalletEvmError extends Enum {
     readonly isBalanceLow: boolean;
     readonly isFeeOverflow: boolean;
@@ -3311,7 +3315,7 @@
     readonly type: 'BalanceLow' | 'FeeOverflow' | 'PaymentOverflow' | 'WithdrawFailed' | 'GasPriceTooLow' | 'InvalidNonce';
   }
 
-  /** @name FpRpcTransactionStatus (416) */
+  /** @name FpRpcTransactionStatus (418) */
   interface FpRpcTransactionStatus extends Struct {
     readonly transactionHash: H256;
     readonly transactionIndex: u32;
@@ -3322,10 +3326,10 @@
     readonly logsBloom: EthbloomBloom;
   }
 
-  /** @name EthbloomBloom (418) */
+  /** @name EthbloomBloom (420) */
   interface EthbloomBloom extends U8aFixed {}
 
-  /** @name EthereumReceiptReceiptV3 (420) */
+  /** @name EthereumReceiptReceiptV3 (422) */
   interface EthereumReceiptReceiptV3 extends Enum {
     readonly isLegacy: boolean;
     readonly asLegacy: EthereumReceiptEip658ReceiptData;
@@ -3336,7 +3340,7 @@
     readonly type: 'Legacy' | 'Eip2930' | 'Eip1559';
   }
 
-  /** @name EthereumReceiptEip658ReceiptData (421) */
+  /** @name EthereumReceiptEip658ReceiptData (423) */
   interface EthereumReceiptEip658ReceiptData extends Struct {
     readonly statusCode: u8;
     readonly usedGas: U256;
@@ -3344,14 +3348,14 @@
     readonly logs: Vec<EthereumLog>;
   }
 
-  /** @name EthereumBlock (422) */
+  /** @name EthereumBlock (424) */
   interface EthereumBlock extends Struct {
     readonly header: EthereumHeader;
     readonly transactions: Vec<EthereumTransactionTransactionV2>;
     readonly ommers: Vec<EthereumHeader>;
   }
 
-  /** @name EthereumHeader (423) */
+  /** @name EthereumHeader (425) */
   interface EthereumHeader extends Struct {
     readonly parentHash: H256;
     readonly ommersHash: H256;
@@ -3370,24 +3374,24 @@
     readonly nonce: EthereumTypesHashH64;
   }
 
-  /** @name EthereumTypesHashH64 (424) */
+  /** @name EthereumTypesHashH64 (426) */
   interface EthereumTypesHashH64 extends U8aFixed {}
 
-  /** @name PalletEthereumError (429) */
+  /** @name PalletEthereumError (431) */
   interface PalletEthereumError extends Enum {
     readonly isInvalidSignature: boolean;
     readonly isPreLogExists: boolean;
     readonly type: 'InvalidSignature' | 'PreLogExists';
   }
 
-  /** @name PalletEvmCoderSubstrateError (430) */
+  /** @name PalletEvmCoderSubstrateError (432) */
   interface PalletEvmCoderSubstrateError extends Enum {
     readonly isOutOfGas: boolean;
     readonly isOutOfFund: boolean;
     readonly type: 'OutOfGas' | 'OutOfFund';
   }
 
-  /** @name UpDataStructsSponsorshipStateBasicCrossAccountIdRepr (431) */
+  /** @name UpDataStructsSponsorshipStateBasicCrossAccountIdRepr (433) */
   interface UpDataStructsSponsorshipStateBasicCrossAccountIdRepr extends Enum {
     readonly isDisabled: boolean;
     readonly isUnconfirmed: boolean;
@@ -3397,7 +3401,7 @@
     readonly type: 'Disabled' | 'Unconfirmed' | 'Confirmed';
   }
 
-  /** @name PalletEvmContractHelpersSponsoringModeT (432) */
+  /** @name PalletEvmContractHelpersSponsoringModeT (434) */
   interface PalletEvmContractHelpersSponsoringModeT extends Enum {
     readonly isDisabled: boolean;
     readonly isAllowlisted: boolean;
@@ -3405,21 +3409,21 @@
     readonly type: 'Disabled' | 'Allowlisted' | 'Generous';
   }
 
-  /** @name PalletEvmContractHelpersError (434) */
+  /** @name PalletEvmContractHelpersError (436) */
   interface PalletEvmContractHelpersError extends Enum {
     readonly isNoPermission: boolean;
     readonly isNoPendingSponsor: boolean;
     readonly type: 'NoPermission' | 'NoPendingSponsor';
   }
 
-  /** @name PalletEvmMigrationError (435) */
+  /** @name PalletEvmMigrationError (437) */
   interface PalletEvmMigrationError extends Enum {
     readonly isAccountNotEmpty: boolean;
     readonly isAccountIsNotMigrating: boolean;
     readonly type: 'AccountNotEmpty' | 'AccountIsNotMigrating';
   }
 
-  /** @name SpRuntimeMultiSignature (437) */
+  /** @name SpRuntimeMultiSignature (439) */
   interface SpRuntimeMultiSignature extends Enum {
     readonly isEd25519: boolean;
     readonly asEd25519: SpCoreEd25519Signature;
@@ -3430,34 +3434,34 @@
     readonly type: 'Ed25519' | 'Sr25519' | 'Ecdsa';
   }
 
-  /** @name SpCoreEd25519Signature (438) */
+  /** @name SpCoreEd25519Signature (440) */
   interface SpCoreEd25519Signature extends U8aFixed {}
 
-  /** @name SpCoreSr25519Signature (440) */
+  /** @name SpCoreSr25519Signature (442) */
   interface SpCoreSr25519Signature extends U8aFixed {}
 
-  /** @name SpCoreEcdsaSignature (441) */
+  /** @name SpCoreEcdsaSignature (443) */
   interface SpCoreEcdsaSignature extends U8aFixed {}
 
-  /** @name FrameSystemExtensionsCheckSpecVersion (444) */
+  /** @name FrameSystemExtensionsCheckSpecVersion (446) */
   type FrameSystemExtensionsCheckSpecVersion = Null;
 
-  /** @name FrameSystemExtensionsCheckGenesis (445) */
+  /** @name FrameSystemExtensionsCheckGenesis (447) */
   type FrameSystemExtensionsCheckGenesis = Null;
 
-  /** @name FrameSystemExtensionsCheckNonce (448) */
+  /** @name FrameSystemExtensionsCheckNonce (450) */
   interface FrameSystemExtensionsCheckNonce extends Compact<u32> {}
 
-  /** @name FrameSystemExtensionsCheckWeight (449) */
+  /** @name FrameSystemExtensionsCheckWeight (451) */
   type FrameSystemExtensionsCheckWeight = Null;
 
-  /** @name PalletTemplateTransactionPaymentChargeTransactionPayment (450) */
+  /** @name PalletTemplateTransactionPaymentChargeTransactionPayment (452) */
   interface PalletTemplateTransactionPaymentChargeTransactionPayment extends Compact<u128> {}
 
-  /** @name OpalRuntimeRuntime (451) */
+  /** @name OpalRuntimeRuntime (453) */
   type OpalRuntimeRuntime = Null;
 
-  /** @name PalletEthereumFakeTransactionFinalizer (452) */
+  /** @name PalletEthereumFakeTransactionFinalizer (454) */
   type PalletEthereumFakeTransactionFinalizer = Null;
 
 } // declare module