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
before · pallets/app-promotion/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//! # App promotion18//!19//! The app promotion pallet is designed to ... .20//!21//! ## Interface22//!23//! ### Dispatchable Functions24//!25//! * `start_inflation` - This method sets the inflation start date. Can be only called once.26//! Inflation start block can be backdated and will catch up. The method will create Treasury27//!	account if it does not exist and perform the first inflation deposit.2829// #![recursion_limit = "1024"]30#![cfg_attr(not(feature = "std"), no_std)]3132#[cfg(feature = "runtime-benchmarks")]33mod benchmarking;34#[cfg(test)]35mod tests;36pub mod types;37pub mod weights;3839use sp_std::{vec::Vec, iter::Sum, borrow::ToOwned};40use sp_core::H160;41use codec::EncodeLike;42use pallet_balances::BalanceLock;43pub use types::*;4445// use up_common::constants::{DAYS, UNIQUE};46use up_data_structs::CollectionId;4748use frame_support::{49	dispatch::{DispatchResult},50	traits::{51		Currency, Get, LockableCurrency, WithdrawReasons, tokens::Balance, ExistenceRequirement,52	},53	ensure,54};5556use weights::WeightInfo;5758pub use pallet::*;59use pallet_evm::account::CrossAccountId;60use sp_runtime::{61	Perbill,62	traits::{BlockNumberProvider, CheckedAdd, CheckedSub, AccountIdConversion},63	ArithmeticError,64};6566type BalanceOf<T> =67	<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;6869// const SECONDS_TO_BLOCK: u32 = 6;70// const DAY: u32 = 60 * 60 * 24 / SECONDS_TO_BLOCK;71// const WEEK: u32 = 7 * DAY;72// const TWO_WEEK: u32 = 2 * WEEK;73// const YEAR: u32 = DAY * 365;7475pub const LOCK_IDENTIFIER: [u8; 8] = *b"appstake";7677#[frame_support::pallet]78pub mod pallet {79	use super::*;80	use frame_support::{Blake2_128Concat, Twox64Concat, pallet_prelude::*, storage::Key, PalletId};81	use frame_system::pallet_prelude::*;8283	#[pallet::config]84	pub trait Config: frame_system::Config + pallet_evm::account::Config {85		type Currency: ExtendedLockableCurrency<Self::AccountId>;8687		type CollectionHandler: CollectionHandler<88			AccountId = Self::AccountId,89			CollectionId = CollectionId,90		>;9192		type ContractHandler: ContractHandler<AccountId = Self::CrossAccountId, ContractId = H160>;9394		type TreasuryAccountId: Get<Self::AccountId>;9596		/// The app's pallet id, used for deriving its sovereign account ID.97		#[pallet::constant]98		type PalletId: Get<PalletId>;99100		/// In relay blocks.101		#[pallet::constant]102		type RecalculationInterval: Get<Self::BlockNumber>;103		/// In relay blocks.104		#[pallet::constant]105		type PendingInterval: Get<Self::BlockNumber>;106107		/// In chain blocks.108		#[pallet::constant]109		type Day: Get<Self::BlockNumber>; // useless110111		#[pallet::constant]112		type Nominal: Get<BalanceOf<Self>>;113114		#[pallet::constant]115		type IntervalIncome: Get<Perbill>;116117		/// Weight information for extrinsics in this pallet.118		type WeightInfo: WeightInfo;119120		// The relay block number provider121		type RelayBlockNumberProvider: BlockNumberProvider<BlockNumber = Self::BlockNumber>;122123		/// Events compatible with [`frame_system::Config::Event`].124		type Event: IsType<<Self as frame_system::Config>::Event> + From<Event<Self>>;125	}126127	#[pallet::pallet]128	#[pallet::generate_store(pub(super) trait Store)]129	pub struct Pallet<T>(_);130131	#[pallet::event]132	#[pallet::generate_deposit(fn deposit_event)]133	pub enum Event<T: Config> {134		StakingRecalculation(135			/// Base on which interest is calculated136			BalanceOf<T>,137			/// Amount of accrued interest138			BalanceOf<T>,139		),140	}141142	#[pallet::error]143	pub enum Error<T> {144		/// Error due to action requiring admin to be set145		AdminNotSet,146		/// No permission to perform an action147		NoPermission,148		/// Insufficient funds to perform an action149		NotSufficientFounds,150		/// An error related to the fact that an invalid argument was passed to perform an action151		InvalidArgument,152	}153154	#[pallet::storage]155	pub type TotalStaked<T: Config> = StorageValue<Value = BalanceOf<T>, QueryKind = ValueQuery>;156157	#[pallet::storage]158	pub type Admin<T: Config> = StorageValue<Value = T::AccountId, QueryKind = OptionQuery>;159160	/// Amount of tokens staked by account in the blocknumber.161	#[pallet::storage]162	pub type Staked<T: Config> = StorageNMap<163		Key = (164			Key<Blake2_128Concat, T::AccountId>,165			Key<Twox64Concat, T::BlockNumber>,166		),167		Value = BalanceOf<T>,168		QueryKind = ValueQuery,169	>;170171	/// Amount of tokens pending unstake per user per block.172	#[pallet::storage]173	pub type PendingUnstake<T: Config> = StorageNMap<174		Key = (175			Key<Blake2_128Concat, T::AccountId>,176			Key<Twox64Concat, T::BlockNumber>,177		),178		Value = BalanceOf<T>,179		QueryKind = ValueQuery,180	>;181182	/// A block when app-promotion has started .I think this is redundant, because we only need `NextInterestBlock`.183	#[pallet::storage]184	pub type StartBlock<T: Config> = StorageValue<Value = T::BlockNumber, QueryKind = ValueQuery>;185186	/// Next target block when interest is recalculated187	#[pallet::storage]188	#[pallet::getter(fn get_interest_block)]189	pub type NextInterestBlock<T: Config> =190		StorageValue<Value = T::BlockNumber, QueryKind = ValueQuery>;191192	#[pallet::hooks]193	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {194		fn on_initialize(current_block: T::BlockNumber) -> Weight195		where196			<T as frame_system::Config>::BlockNumber: From<u32>,197		{198			let mut consumed_weight = 0;199			let mut add_weight = |reads, writes, weight| {200				consumed_weight += T::DbWeight::get().reads_writes(reads, writes);201				consumed_weight += weight;202			};203204			PendingUnstake::<T>::iter()205				.filter_map(|((staker, block), amount)| {206					if block <= current_block {207						Some((staker, block, amount))208					} else {209						None210					}211				})212				.for_each(|(staker, block, amount)| {213					Self::unlock_balance_unchecked(&staker, amount); // TO-DO : Replace with a method that will check that the unstack is less than it was blocked, otherwise take the delta from the treasuries214					<PendingUnstake<T>>::remove((staker, block));215				});216217			let next_interest_block = Self::get_interest_block();218			let current_relay_block = T::RelayBlockNumberProvider::current_block_number();219			if next_interest_block != 0.into() && current_relay_block >= next_interest_block {220				let mut acc = <BalanceOf<T>>::default();221				let mut base_acc = <BalanceOf<T>>::default();222223				NextInterestBlock::<T>::set(224					NextInterestBlock::<T>::get() + T::RecalculationInterval::get(),225				);226				add_weight(0, 1, 0);227228				Staked::<T>::iter()229					.filter(|((_, block), _)| {230						*block + T::RecalculationInterval::get() <= current_relay_block231					})232					.for_each(|((staker, block), amount)| {233						Self::recalculate_stake(&staker, block, amount, &mut acc);234						add_weight(0, 0, T::WeightInfo::recalculate_stake());235						base_acc += amount;236					});237				<TotalStaked<T>>::get()238					.checked_add(&acc)239					.map(|res| <TotalStaked<T>>::set(res));240241				Self::deposit_event(Event::StakingRecalculation(base_acc, acc));242				add_weight(0, 1, 0);243			} else {244				add_weight(1, 0, 0)245			};246			consumed_weight247		}248	}249250	#[pallet::call]251	impl<T: Config> Pallet<T> {252		#[pallet::weight(T::WeightInfo::set_admin_address())]253		pub fn set_admin_address(origin: OriginFor<T>, admin: T::CrossAccountId) -> DispatchResult {254			ensure_root(origin)?;255			<Admin<T>>::set(Some(admin.as_sub().to_owned()));256257			Ok(())258		}259260		#[pallet::weight(T::WeightInfo::start_app_promotion())]261		pub fn start_app_promotion(262			origin: OriginFor<T>,263			promotion_start_relay_block: Option<T::BlockNumber>,264		) -> DispatchResult265		where266			<T as frame_system::Config>::BlockNumber: From<u32>,267		{268			ensure_root(origin)?;269270			// Start app-promotion mechanics if it has not been yet initialized271			if <StartBlock<T>>::get() == 0u32.into() {272				let start_block = promotion_start_relay_block273					.unwrap_or(T::RelayBlockNumberProvider::current_block_number());274275				// Set promotion global start block276				<StartBlock<T>>::set(start_block);277278				<NextInterestBlock<T>>::set(start_block + T::RecalculationInterval::get());279			}280281			Ok(())282		}283284		#[pallet::weight(0)]285		pub fn stop_app_promotion(origin: OriginFor<T>) -> DispatchResult286		where287			<T as frame_system::Config>::BlockNumber: From<u32>,288		{289			ensure_root(origin)?;290291			if <StartBlock<T>>::get() != 0u32.into() {292				<StartBlock<T>>::set(T::BlockNumber::default());293				<NextInterestBlock<T>>::set(T::BlockNumber::default());294			}295296			Ok(())297		}298299		#[pallet::weight(T::WeightInfo::stake())]300		pub fn stake(staker: OriginFor<T>, amount: BalanceOf<T>) -> DispatchResult {301			let staker_id = ensure_signed(staker)?;302303			ensure!(amount >= T::Nominal::get(), ArithmeticError::Underflow);304305			let balance =306				<<T as Config>::Currency as Currency<T::AccountId>>::free_balance(&staker_id);307308			ensure!(balance >= amount, ArithmeticError::Underflow);309310			<<T as Config>::Currency as Currency<T::AccountId>>::ensure_can_withdraw(311				&staker_id,312				amount,313				WithdrawReasons::all(),314				balance - amount,315			)?;316317			Self::add_lock_balance(&staker_id, amount)?;318319			let block_number = T::RelayBlockNumberProvider::current_block_number();320321			<Staked<T>>::insert(322				(&staker_id, block_number),323				<Staked<T>>::get((&staker_id, block_number))324					.checked_add(&amount)325					.ok_or(ArithmeticError::Overflow)?,326			);327328			<TotalStaked<T>>::set(329				<TotalStaked<T>>::get()330					.checked_add(&amount)331					.ok_or(ArithmeticError::Overflow)?,332			);333334			Ok(())335		}336337		#[pallet::weight(T::WeightInfo::unstake())]338		pub fn unstake(staker: OriginFor<T>, amount: BalanceOf<T>) -> DispatchResult {339			let staker_id = ensure_signed(staker)?;340341			let mut stakes = Staked::<T>::iter_prefix((&staker_id,)).collect::<Vec<_>>();342343			let total_staked = stakes344				.iter()345				.fold(<BalanceOf<T>>::default(), |acc, (_, amount)| acc + *amount);346347			ensure!(total_staked >= amount, ArithmeticError::Underflow);348349			<TotalStaked<T>>::set(350				<TotalStaked<T>>::get()351					.checked_sub(&amount)352					.ok_or(ArithmeticError::Underflow)?,353			);354355			let block =356				T::RelayBlockNumberProvider::current_block_number() + T::PendingInterval::get();357			<PendingUnstake<T>>::insert(358				(&staker_id, block),359				<PendingUnstake<T>>::get((&staker_id, block))360					.checked_add(&amount)361					.ok_or(ArithmeticError::Overflow)?,362			);363364			stakes.sort_by_key(|(block, _)| *block);365366			let mut acc_amount = amount;367			let new_state = stakes368				.into_iter()369				.map_while(|(block, balance_per_block)| {370					if acc_amount == <BalanceOf<T>>::default() {371						return None;372					}373					if acc_amount <= balance_per_block {374						let res = (block, balance_per_block - acc_amount, acc_amount);375						acc_amount = <BalanceOf<T>>::default();376						return Some(res);377					} else {378						acc_amount -= balance_per_block;379						return Some((block, <BalanceOf<T>>::default(), acc_amount));380					}381				})382				.collect::<Vec<_>>();383384			new_state385				.into_iter()386				.for_each(|(block, to_staked, _to_pending)| {387					if to_staked == <BalanceOf<T>>::default() {388						<Staked<T>>::remove((&staker_id, block));389					} else {390						<Staked<T>>::insert((&staker_id, block), to_staked);391					}392				});393394			Ok(())395		}396397		#[pallet::weight(0)]398		pub fn sponsor_collection(399			admin: OriginFor<T>,400			collection_id: CollectionId,401		) -> DispatchResult {402			let admin_id = ensure_signed(admin)?;403			ensure!(404				admin_id == Admin::<T>::get().ok_or(Error::<T>::AdminNotSet)?,405				Error::<T>::NoPermission406			);407408			T::CollectionHandler::set_sponsor(Self::account_id(), collection_id)409		}410		#[pallet::weight(0)]411		pub fn stop_sponsorign_collection(412			admin: OriginFor<T>,413			collection_id: CollectionId,414		) -> DispatchResult {415			let admin_id = ensure_signed(admin)?;416417			ensure!(418				admin_id == Admin::<T>::get().ok_or(Error::<T>::AdminNotSet)?,419				Error::<T>::NoPermission420			);421422			ensure!(423				T::CollectionHandler::get_sponsor(collection_id)?424					.ok_or(<Error<T>>::InvalidArgument)?425					== Self::account_id(),426				<Error<T>>::NoPermission427			);428			T::CollectionHandler::remove_collection_sponsor(collection_id)429		}430431		#[pallet::weight(0)]432		pub fn sponsor_conract(admin: OriginFor<T>, contract_id: H160) -> DispatchResult {433			let admin_id = ensure_signed(admin)?;434435			ensure!(436				admin_id == Admin::<T>::get().ok_or(Error::<T>::AdminNotSet)?,437				Error::<T>::NoPermission438			);439440			T::ContractHandler::set_sponsor(441				T::CrossAccountId::from_sub(Self::account_id()),442				contract_id,443			)444		}445446		#[pallet::weight(0)]447		pub fn stop_sponsorign_contract(admin: OriginFor<T>, contract_id: H160) -> DispatchResult {448			let admin_id = ensure_signed(admin)?;449450			ensure!(451				admin_id == Admin::<T>::get().ok_or(Error::<T>::AdminNotSet)?,452				Error::<T>::NoPermission453			);454455			ensure!(456				T::ContractHandler::get_sponsor(contract_id)?.ok_or(<Error<T>>::InvalidArgument)?457					== T::CrossAccountId::from_sub(Self::account_id()),458				<Error<T>>::NoPermission459			);460			T::ContractHandler::remove_contract_sponsor(contract_id)461		}462	}463}464465impl<T: Config> Pallet<T> {466	pub fn account_id() -> T::AccountId {467		T::PalletId::get().into_account_truncating()468	}469470	fn unlock_balance_unchecked(staker: &T::AccountId, amount: BalanceOf<T>) {471		let mut locked_balance = Self::get_locked_balance(staker).map(|l| l.amount).unwrap();472		locked_balance -= amount;473		Self::set_lock_unchecked(staker, locked_balance);474	}475476	fn add_lock_balance(staker: &T::AccountId, amount: BalanceOf<T>) -> DispatchResult {477		Self::get_locked_balance(staker)478			.map_or(<BalanceOf<T>>::default(), |l| l.amount)479			.checked_add(&amount)480			.map(|new_lock| Self::set_lock_unchecked(staker, new_lock))481			.ok_or(ArithmeticError::Overflow.into())482	}483484	fn set_lock_unchecked(staker: &T::AccountId, amount: BalanceOf<T>) {485		<T::Currency as LockableCurrency<T::AccountId>>::set_lock(486			LOCK_IDENTIFIER,487			staker,488			amount,489			WithdrawReasons::all(),490		)491	}492493	pub fn get_locked_balance(494		staker: impl EncodeLike<T::AccountId>,495	) -> Option<BalanceLock<BalanceOf<T>>> {496		<T::Currency as ExtendedLockableCurrency<T::AccountId>>::locks(staker)497			.into_iter()498			.find(|l| l.id == LOCK_IDENTIFIER)499	}500501	pub fn total_staked_by_id(staker: impl EncodeLike<T::AccountId>) -> Option<BalanceOf<T>> {502		let staked = Staked::<T>::iter_prefix((staker,))503			.into_iter()504			.fold(<BalanceOf<T>>::default(), |acc, (_, amount)| acc + amount);505		if staked != <BalanceOf<T>>::default() {506			Some(staked)507		} else {508			None509		}510	}511512	pub fn total_staked_by_id_per_block(513		staker: impl EncodeLike<T::AccountId>,514	) -> Option<Vec<(T::BlockNumber, BalanceOf<T>)>> {515		let mut staked = Staked::<T>::iter_prefix((staker,))516			.into_iter()517			.map(|(block, amount)| (block, amount))518			.collect::<Vec<_>>();519		staked.sort_by_key(|(block, _)| *block);520		if !staked.is_empty() {521			Some(staked)522		} else {523			None524		}525	}526527	pub fn cross_id_total_staked(staker: Option<T::CrossAccountId>) -> Option<BalanceOf<T>> {528		staker.map_or(Some(<TotalStaked<T>>::get()), |s| {529			Self::total_staked_by_id(s.as_sub())530		})531		// Self::total_staked_by_id(staker.as_sub())532	}533534	pub fn cross_id_locked_balance(staker: T::CrossAccountId) -> BalanceOf<T> {535		Self::get_locked_balance(staker.as_sub())536			.map(|l| l.amount)537			.unwrap_or_default()538	}539540	pub fn cross_id_total_staked_per_block(541		staker: T::CrossAccountId,542	) -> Vec<(T::BlockNumber, BalanceOf<T>)> {543		Self::total_staked_by_id_per_block(staker.as_sub()).unwrap_or_default()544	}545546	fn recalculate_stake(547		staker: &T::AccountId,548		block: T::BlockNumber,549		base: BalanceOf<T>,550		income_acc: &mut BalanceOf<T>,551	) {552		let income = Self::calculate_income(base);553		base.checked_add(&income).map(|res| {554			<Staked<T>>::insert((staker, block), res);555			*income_acc += income;556			<T::Currency as Currency<T::AccountId>>::transfer(557				&T::TreasuryAccountId::get(),558				staker,559				income,560				ExistenceRequirement::KeepAlive,561			)562			.and_then(|_| Self::add_lock_balance(staker, income));563		});564	}565566	fn calculate_income<I>(base: I) -> I567	where568		I: EncodeLike<BalanceOf<T>> + Balance,569	{570		T::IntervalIncome::get() * base571	}572}573574impl<T: Config> Pallet<T>575where576	<<T as pallet::Config>::Currency as Currency<T::AccountId>>::Balance: Sum,577{578	pub fn cross_id_pending_unstake(staker: Option<T::CrossAccountId>) -> BalanceOf<T> {579		staker.map_or(PendingUnstake::<T>::iter_values().sum(), |s| {580			PendingUnstake::<T>::iter_prefix_values((s.as_sub(),)).sum()581		})582	}583584	pub fn cross_id_pending_unstake_per_block(585		staker: T::CrossAccountId,586	) -> Vec<(T::BlockNumber, BalanceOf<T>)> {587		let mut unsorted_res = PendingUnstake::<T>::iter_prefix((staker.as_sub(),))588			.into_iter()589			.collect::<Vec<_>>();590		unsorted_res.sort_by_key(|(block, _)| *block);591		unsorted_res592	}593}
after · pallets/app-promotion/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//! # App promotion18//!19//! The app promotion pallet is designed to ... .20//!21//! ## Interface22//!23//! ### Dispatchable Functions24//!25//! * `start_inflation` - This method sets the inflation start date. Can be only called once.26//! Inflation start block can be backdated and will catch up. The method will create Treasury27//!	account if it does not exist and perform the first inflation deposit.2829// #![recursion_limit = "1024"]30#![cfg_attr(not(feature = "std"), no_std)]3132#[cfg(feature = "runtime-benchmarks")]33mod benchmarking;34#[cfg(test)]35mod tests;36pub mod types;37pub mod weights;3839use sp_std::{vec::Vec, iter::Sum, borrow::ToOwned};40use sp_core::H160;41use codec::EncodeLike;42use pallet_balances::BalanceLock;43pub use types::*;4445// use up_common::constants::{DAYS, UNIQUE};46use up_data_structs::CollectionId;4748use frame_support::{49	dispatch::{DispatchResult},50	traits::{51		Currency, Get, LockableCurrency, WithdrawReasons, tokens::Balance, ExistenceRequirement,52	},53	ensure,54};5556use weights::WeightInfo;5758pub use pallet::*;59use pallet_evm::account::CrossAccountId;60use sp_runtime::{61	Perbill,62	traits::{BlockNumberProvider, CheckedAdd, CheckedSub, AccountIdConversion},63	ArithmeticError,64};6566type BalanceOf<T> =67	<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;6869// const SECONDS_TO_BLOCK: u32 = 6;70// const DAY: u32 = 60 * 60 * 24 / SECONDS_TO_BLOCK;71// const WEEK: u32 = 7 * DAY;72// const TWO_WEEK: u32 = 2 * WEEK;73// const YEAR: u32 = DAY * 365;7475pub const LOCK_IDENTIFIER: [u8; 8] = *b"appstake";7677#[frame_support::pallet]78pub mod pallet {79	use super::*;80	use frame_support::{Blake2_128Concat, Twox64Concat, pallet_prelude::*, storage::Key, PalletId};81	use frame_system::pallet_prelude::*;8283	#[pallet::config]84	pub trait Config: frame_system::Config + pallet_evm::account::Config {85		type Currency: ExtendedLockableCurrency<Self::AccountId>;8687		type CollectionHandler: CollectionHandler<88			AccountId = Self::AccountId,89			CollectionId = CollectionId,90		>;9192		type ContractHandler: ContractHandler<AccountId = Self::CrossAccountId, ContractId = H160>;9394		type TreasuryAccountId: Get<Self::AccountId>;9596		/// The app's pallet id, used for deriving its sovereign account ID.97		#[pallet::constant]98		type PalletId: Get<PalletId>;99100		/// In relay blocks.101		#[pallet::constant]102		type RecalculationInterval: Get<Self::BlockNumber>;103		/// In relay blocks.104		#[pallet::constant]105		type PendingInterval: Get<Self::BlockNumber>;106107		/// In chain blocks.108		#[pallet::constant]109		type Day: Get<Self::BlockNumber>; // useless110111		#[pallet::constant]112		type Nominal: Get<BalanceOf<Self>>;113114		#[pallet::constant]115		type IntervalIncome: Get<Perbill>;116117		/// Weight information for extrinsics in this pallet.118		type WeightInfo: WeightInfo;119120		// The relay block number provider121		type RelayBlockNumberProvider: BlockNumberProvider<BlockNumber = Self::BlockNumber>;122123		/// Events compatible with [`frame_system::Config::Event`].124		type Event: IsType<<Self as frame_system::Config>::Event> + From<Event<Self>>;125	}126127	#[pallet::pallet]128	#[pallet::generate_store(pub(super) trait Store)]129	pub struct Pallet<T>(_);130131	#[pallet::event]132	#[pallet::generate_deposit(fn deposit_event)]133	pub enum Event<T: Config> {134		StakingRecalculation(135			/// An recalculated staker136			T::AccountId,137			/// Base on which interest is calculated138			BalanceOf<T>,139			/// Amount of accrued interest140			BalanceOf<T>,141		),142	}143144	#[pallet::error]145	pub enum Error<T> {146		/// Error due to action requiring admin to be set147		AdminNotSet,148		/// No permission to perform an action149		NoPermission,150		/// Insufficient funds to perform an action151		NotSufficientFounds,152		/// An error related to the fact that an invalid argument was passed to perform an action153		InvalidArgument,154	}155156	#[pallet::storage]157	pub type TotalStaked<T: Config> = StorageValue<Value = BalanceOf<T>, QueryKind = ValueQuery>;158159	#[pallet::storage]160	pub type Admin<T: Config> = StorageValue<Value = T::AccountId, QueryKind = OptionQuery>;161162	/// Amount of tokens staked by account in the blocknumber.163	#[pallet::storage]164	pub type Staked<T: Config> = StorageNMap<165		Key = (166			Key<Blake2_128Concat, T::AccountId>,167			Key<Twox64Concat, T::BlockNumber>,168		),169		Value = (BalanceOf<T>, T::BlockNumber),170		QueryKind = ValueQuery,171	>;172173	/// Amount of tokens pending unstake per user per block.174	#[pallet::storage]175	pub type PendingUnstake<T: Config> = StorageNMap<176		Key = (177			Key<Blake2_128Concat, T::AccountId>,178			Key<Twox64Concat, T::BlockNumber>,179		),180		Value = BalanceOf<T>,181		QueryKind = ValueQuery,182	>;183184	/// A block when app-promotion has started .I think this is redundant, because we only need `NextInterestBlock`.185	#[pallet::storage]186	pub type StartBlock<T: Config> = StorageValue<Value = T::BlockNumber, QueryKind = ValueQuery>;187188	/// Next target block when interest is recalculated189	#[pallet::storage]190	#[pallet::getter(fn get_interest_block)]191	pub type NextInterestBlock<T: Config> =192		StorageValue<Value = T::BlockNumber, QueryKind = ValueQuery>;193194	/// Stores the address of the staker for which the last revenue recalculation was performed.195	/// If `None`, then recalculation has not yet been performed or calculations have been completed for all stakers.196	#[pallet::storage]197	#[pallet::getter(fn get_last_calculated_staker)]198	pub type LastCalcucaltedStaker<T: Config> =199		StorageValue<Value = T::AccountId, QueryKind = OptionQuery>;200201	#[pallet::hooks]202	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {203		fn on_initialize(current_block: T::BlockNumber) -> Weight204		where205			<T as frame_system::Config>::BlockNumber: From<u32>,206		{207			let mut consumed_weight = 0;208			// let mut add_weight = |reads, writes, weight| {209			// 	consumed_weight += T::DbWeight::get().reads_writes(reads, writes);210			// 	consumed_weight += weight;211			// };212213			PendingUnstake::<T>::iter()214				.filter_map(|((staker, block), amount)| {215					if block <= current_block {216						Some((staker, block, amount))217					} else {218						None219					}220				})221				.for_each(|(staker, block, amount)| {222					Self::unlock_balance_unchecked(&staker, amount); // TO-DO : Replace with a method that will check that the unstack is less than it was blocked, otherwise take the delta from the treasuries223					<PendingUnstake<T>>::remove((staker, block));224				});225226			// let next_interest_block = Self::get_interest_block();227			// let current_relay_block = T::RelayBlockNumberProvider::current_block_number();228			// if next_interest_block != 0.into() && current_relay_block >= next_interest_block {229			// 	let mut acc = <BalanceOf<T>>::default();230			// 	let mut base_acc = <BalanceOf<T>>::default();231232			// 	NextInterestBlock::<T>::set(233			// 		NextInterestBlock::<T>::get() + T::RecalculationInterval::get(),234			// 	);235			// 	add_weight(0, 1, 0);236237			// 	Staked::<T>::iter()238			// 		.filter(|((_, block), _)| {239			// 			*block + T::RecalculationInterval::get() <= current_relay_block240			// 		})241			// 		.for_each(|((staker, block), amount)| {242			// 			Self::recalculate_stake(&staker, block, amount, &mut acc);243			// 			add_weight(0, 0, T::WeightInfo::recalculate_stake());244			// 			base_acc += amount;245			// 		});246			// 	<TotalStaked<T>>::get()247			// 		.checked_add(&acc)248			// 		.map(|res| <TotalStaked<T>>::set(res));249250			// 	Self::deposit_event(Event::StakingRecalculation(base_acc, acc));251			// 	add_weight(0, 1, 0);252			// } else {253			// 	add_weight(1, 0, 0)254			// };255			consumed_weight256		}257	}258259	#[pallet::call]260	impl<T: Config> Pallet<T>261	where262		T::BlockNumber: From<u32>,263	{264		#[pallet::weight(T::WeightInfo::set_admin_address())]265		pub fn set_admin_address(origin: OriginFor<T>, admin: T::CrossAccountId) -> DispatchResult {266			ensure_root(origin)?;267			<Admin<T>>::set(Some(admin.as_sub().to_owned()));268269			Ok(())270		}271272		#[pallet::weight(T::WeightInfo::start_app_promotion())]273		pub fn start_app_promotion(274			origin: OriginFor<T>,275			promotion_start_relay_block: Option<T::BlockNumber>,276		) -> DispatchResult277		where278			<T as frame_system::Config>::BlockNumber: From<u32>,279		{280			ensure_root(origin)?;281282			// Start app-promotion mechanics if it has not been yet initialized283			if <StartBlock<T>>::get() == 0u32.into() {284				let start_block = promotion_start_relay_block285					.unwrap_or(T::RelayBlockNumberProvider::current_block_number());286287				// Set promotion global start block288				<StartBlock<T>>::set(start_block);289290				<NextInterestBlock<T>>::set(start_block + T::RecalculationInterval::get());291			}292293			Ok(())294		}295296		#[pallet::weight(T::WeightInfo::stop_app_promotion())]297		pub fn stop_app_promotion(origin: OriginFor<T>) -> DispatchResult298		where299			<T as frame_system::Config>::BlockNumber: From<u32>,300		{301			ensure_root(origin)?;302303			if <StartBlock<T>>::get() != 0u32.into() {304				<StartBlock<T>>::set(T::BlockNumber::default());305				<NextInterestBlock<T>>::set(T::BlockNumber::default());306			}307308			Ok(())309		}310311		#[pallet::weight(T::WeightInfo::stake())]312		pub fn stake(staker: OriginFor<T>, amount: BalanceOf<T>) -> DispatchResult {313			let staker_id = ensure_signed(staker)?;314315			ensure!(amount >= T::Nominal::get(), ArithmeticError::Underflow);316317			let balance =318				<<T as Config>::Currency as Currency<T::AccountId>>::free_balance(&staker_id);319320			ensure!(balance >= amount, ArithmeticError::Underflow);321322			<<T as Config>::Currency as Currency<T::AccountId>>::ensure_can_withdraw(323				&staker_id,324				amount,325				WithdrawReasons::all(),326				balance - amount,327			)?;328329			Self::add_lock_balance(&staker_id, amount)?;330331			let block_number = T::RelayBlockNumberProvider::current_block_number();332			let recalc_block = (block_number / T::RecalculationInterval::get() + 2u32.into())333				* T::RecalculationInterval::get();334335			<Staked<T>>::insert((&staker_id, block_number), {336				let mut balance_and_recalc_block = <Staked<T>>::get((&staker_id, block_number));337				balance_and_recalc_block.0 = balance_and_recalc_block338					.0339					.checked_add(&amount)340					.ok_or(ArithmeticError::Overflow)?;341				balance_and_recalc_block.1 = recalc_block;342				balance_and_recalc_block343			});344345			// <TotalStaked<T>>::set(346			// 	<TotalStaked<T>>::get()347			// 		.checked_add(&amount)348			// 		.ok_or(ArithmeticError::Overflow)?,349			// );350351			Ok(())352		}353354		#[pallet::weight(T::WeightInfo::unstake())]355		pub fn unstake(staker: OriginFor<T>, amount: BalanceOf<T>) -> DispatchResult {356			let staker_id = ensure_signed(staker)?;357358			let mut stakes = Staked::<T>::drain_prefix((&staker_id,));359360			// let total_staked = stakes361			// 	.iter()362			// 	.fold(<BalanceOf<T>>::default(), |acc, (_, amount)| acc + *amount);363364			// ensure!(total_staked >= amount, ArithmeticError::Underflow);365366			// <TotalStaked<T>>::set(367			// 	<TotalStaked<T>>::get()368			// 		.checked_sub(&amount)369			// 		.ok_or(ArithmeticError::Underflow)?,370			// );371372			// let block =373			// 	T::RelayBlockNumberProvider::current_block_number() + T::PendingInterval::get();374			// <PendingUnstake<T>>::insert(375			// 	(&staker_id, block),376			// 	<PendingUnstake<T>>::get((&staker_id, block))377			// 		.checked_add(&amount)378			// 		.ok_or(ArithmeticError::Overflow)?,379			// );380381			// stakes.sort_by_key(|(block, _)| *block);382383			// let mut acc_amount = amount;384			// let new_state = stakes385			// 	.into_iter()386			// 	.map_while(|(block, balance_per_block)| {387			// 		if acc_amount == <BalanceOf<T>>::default() {388			// 			return None;389			// 		}390			// 		if acc_amount <= balance_per_block {391			// 			let res = (block, balance_per_block - acc_amount, acc_amount);392			// 			acc_amount = <BalanceOf<T>>::default();393			// 			return Some(res);394			// 		} else {395			// 			acc_amount -= balance_per_block;396			// 			return Some((block, <BalanceOf<T>>::default(), acc_amount));397			// 		}398			// 	})399			// 	.collect::<Vec<_>>();400401			// new_state402			// 	.into_iter()403			// 	.for_each(|(block, to_staked, _to_pending)| {404			// 		if to_staked == <BalanceOf<T>>::default() {405			// 			<Staked<T>>::remove((&staker_id, block));406			// 		} else {407			// 			<Staked<T>>::insert((&staker_id, block), to_staked);408			// 		}409			// 	});410411			Ok(())412413			// let staker_id = ensure_signed(staker)?;414415			// let mut stakes = Staked::<T>::iter_prefix((&staker_id,)).collect::<Vec<_>>();416417			// let total_staked = stakes418			// 	.iter()419			// 	.fold(<BalanceOf<T>>::default(), |acc, (_, amount)| acc + *amount);420421			// ensure!(total_staked >= amount, ArithmeticError::Underflow);422423			// <TotalStaked<T>>::set(424			// 	<TotalStaked<T>>::get()425			// 		.checked_sub(&amount)426			// 		.ok_or(ArithmeticError::Underflow)?,427			// );428429			// let block =430			// 	T::RelayBlockNumberProvider::current_block_number() + T::PendingInterval::get();431			// <PendingUnstake<T>>::insert(432			// 	(&staker_id, block),433			// 	<PendingUnstake<T>>::get((&staker_id, block))434			// 		.checked_add(&amount)435			// 		.ok_or(ArithmeticError::Overflow)?,436			// );437438			// stakes.sort_by_key(|(block, _)| *block);439440			// let mut acc_amount = amount;441			// let new_state = stakes442			// 	.into_iter()443			// 	.map_while(|(block, balance_per_block)| {444			// 		if acc_amount == <BalanceOf<T>>::default() {445			// 			return None;446			// 		}447			// 		if acc_amount <= balance_per_block {448			// 			let res = (block, balance_per_block - acc_amount, acc_amount);449			// 			acc_amount = <BalanceOf<T>>::default();450			// 			return Some(res);451			// 		} else {452			// 			acc_amount -= balance_per_block;453			// 			return Some((block, <BalanceOf<T>>::default(), acc_amount));454			// 		}455			// 	})456			// 	.collect::<Vec<_>>();457458			// new_state459			// 	.into_iter()460			// 	.for_each(|(block, to_staked, _to_pending)| {461			// 		if to_staked == <BalanceOf<T>>::default() {462			// 			<Staked<T>>::remove((&staker_id, block));463			// 		} else {464			// 			<Staked<T>>::insert((&staker_id, block), to_staked);465			// 		}466			// 	});467468			// Ok(())469		}470471		#[pallet::weight(T::WeightInfo::sponsor_collection())]472		pub fn sponsor_collection(473			admin: OriginFor<T>,474			collection_id: CollectionId,475		) -> DispatchResult {476			let admin_id = ensure_signed(admin)?;477			ensure!(478				admin_id == Admin::<T>::get().ok_or(Error::<T>::AdminNotSet)?,479				Error::<T>::NoPermission480			);481482			T::CollectionHandler::set_sponsor(Self::account_id(), collection_id)483		}484		#[pallet::weight(T::WeightInfo::stop_sponsoring_collection())]485		pub fn stop_sponsoring_collection(486			admin: OriginFor<T>,487			collection_id: CollectionId,488		) -> DispatchResult {489			let admin_id = ensure_signed(admin)?;490491			ensure!(492				admin_id == Admin::<T>::get().ok_or(Error::<T>::AdminNotSet)?,493				Error::<T>::NoPermission494			);495496			ensure!(497				T::CollectionHandler::get_sponsor(collection_id)?498					.ok_or(<Error<T>>::InvalidArgument)?499					== Self::account_id(),500				<Error<T>>::NoPermission501			);502			T::CollectionHandler::remove_collection_sponsor(collection_id)503		}504505		#[pallet::weight(T::WeightInfo::sponsor_contract())]506		pub fn sponsor_conract(admin: OriginFor<T>, contract_id: H160) -> DispatchResult {507			let admin_id = ensure_signed(admin)?;508509			ensure!(510				admin_id == Admin::<T>::get().ok_or(Error::<T>::AdminNotSet)?,511				Error::<T>::NoPermission512			);513514			T::ContractHandler::set_sponsor(515				T::CrossAccountId::from_sub(Self::account_id()),516				contract_id,517			)518		}519520		#[pallet::weight(T::WeightInfo::stop_sponsoring_contract())]521		pub fn stop_sponsoring_contract(admin: OriginFor<T>, contract_id: H160) -> DispatchResult {522			let admin_id = ensure_signed(admin)?;523524			ensure!(525				admin_id == Admin::<T>::get().ok_or(Error::<T>::AdminNotSet)?,526				Error::<T>::NoPermission527			);528529			ensure!(530				T::ContractHandler::get_sponsor(contract_id)?.ok_or(<Error<T>>::InvalidArgument)?531					== T::CrossAccountId::from_sub(Self::account_id()),532				<Error<T>>::NoPermission533			);534			T::ContractHandler::remove_contract_sponsor(contract_id)535		}536537		#[pallet::weight(0)]538		pub fn payout_stakers(admin: OriginFor<T>, stakers_number: Option<u8>) -> DispatchResult {539			let admin_id = ensure_signed(admin)?;540541			ensure!(542				admin_id == Admin::<T>::get().ok_or(Error::<T>::AdminNotSet)?,543				Error::<T>::NoPermission544			);545546			Ok(())547		}548	}549}550551impl<T: Config> Pallet<T> {552	pub fn account_id() -> T::AccountId {553		T::PalletId::get().into_account_truncating()554	}555556	fn unlock_balance_unchecked(staker: &T::AccountId, amount: BalanceOf<T>) {557		let mut locked_balance = Self::get_locked_balance(staker).map(|l| l.amount).unwrap();558		locked_balance -= amount;559		Self::set_lock_unchecked(staker, locked_balance);560	}561562	fn add_lock_balance(staker: &T::AccountId, amount: BalanceOf<T>) -> DispatchResult {563		Self::get_locked_balance(staker)564			.map_or(<BalanceOf<T>>::default(), |l| l.amount)565			.checked_add(&amount)566			.map(|new_lock| Self::set_lock_unchecked(staker, new_lock))567			.ok_or(ArithmeticError::Overflow.into())568	}569570	fn set_lock_unchecked(staker: &T::AccountId, amount: BalanceOf<T>) {571		<T::Currency as LockableCurrency<T::AccountId>>::set_lock(572			LOCK_IDENTIFIER,573			staker,574			amount,575			WithdrawReasons::all(),576		)577	}578579	pub fn get_locked_balance(580		staker: impl EncodeLike<T::AccountId>,581	) -> Option<BalanceLock<BalanceOf<T>>> {582		<T::Currency as ExtendedLockableCurrency<T::AccountId>>::locks(staker)583			.into_iter()584			.find(|l| l.id == LOCK_IDENTIFIER)585	}586587	pub fn total_staked_by_id(staker: impl EncodeLike<T::AccountId>) -> Option<BalanceOf<T>> {588		let staked = Staked::<T>::iter_prefix((staker,))589			.into_iter()590			.fold(<BalanceOf<T>>::default(), |acc, (_, (amount, _))| {591				acc + amount592			});593		if staked != <BalanceOf<T>>::default() {594			Some(staked)595		} else {596			None597		}598	}599600	pub fn total_staked_by_id_per_block(601		staker: impl EncodeLike<T::AccountId>,602	) -> Option<Vec<(T::BlockNumber, BalanceOf<T>)>> {603		let mut staked = Staked::<T>::iter_prefix((staker,))604			.into_iter()605			.map(|(block, (amount, _))| (block, amount))606			.collect::<Vec<_>>();607		staked.sort_by_key(|(block, _)| *block);608		if !staked.is_empty() {609			Some(staked)610		} else {611			None612		}613	}614615	pub fn cross_id_total_staked(staker: Option<T::CrossAccountId>) -> Option<BalanceOf<T>> {616		staker.map_or(Some(<TotalStaked<T>>::get()), |s| {617			Self::total_staked_by_id(s.as_sub())618		})619		// Self::total_staked_by_id(staker.as_sub())620	}621622	pub fn cross_id_locked_balance(staker: T::CrossAccountId) -> BalanceOf<T> {623		Self::get_locked_balance(staker.as_sub())624			.map(|l| l.amount)625			.unwrap_or_default()626	}627628	pub fn cross_id_total_staked_per_block(629		staker: T::CrossAccountId,630	) -> Vec<(T::BlockNumber, BalanceOf<T>)> {631		Self::total_staked_by_id_per_block(staker.as_sub()).unwrap_or_default()632	}633634	fn recalculate_stake(635		staker: &T::AccountId,636		block: T::BlockNumber,637		base: BalanceOf<T>,638		income_acc: &mut BalanceOf<T>,639	) {640		let income = Self::calculate_income(base);641		// base.checked_add(&income).map(|res| {642		// 	<Staked<T>>::insert((staker, block), res);643		// 	*income_acc += income;644		// 	<T::Currency as Currency<T::AccountId>>::transfer(645		// 		&T::TreasuryAccountId::get(),646		// 		staker,647		// 		income,648		// 		ExistenceRequirement::KeepAlive,649		// 	)650		// 	.and_then(|_| Self::add_lock_balance(staker, income));651		// });652	}653654	fn calculate_income<I>(base: I) -> I655	where656		I: EncodeLike<BalanceOf<T>> + Balance,657	{658		T::IntervalIncome::get() * base659	}660}661662impl<T: Config> Pallet<T>663where664	<<T as pallet::Config>::Currency as Currency<T::AccountId>>::Balance: Sum,665{666	pub fn cross_id_pending_unstake(staker: Option<T::CrossAccountId>) -> BalanceOf<T> {667		staker.map_or(PendingUnstake::<T>::iter_values().sum(), |s| {668			PendingUnstake::<T>::iter_prefix_values((s.as_sub(),)).sum()669		})670	}671672	pub fn cross_id_pending_unstake_per_block(673		staker: T::CrossAccountId,674	) -> Vec<(T::BlockNumber, BalanceOf<T>)> {675		let mut unsorted_res = PendingUnstake::<T>::iter_prefix((staker.as_sub(),))676			.into_iter()677			.collect::<Vec<_>>();678		unsorted_res.sort_by_key(|(block, _)| *block);679		unsorted_res680	}681}
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
--- a/pallets/unique/src/lib.rs
+++ b/pallets/unique/src/lib.rs
@@ -98,7 +98,7 @@
 pub mod eth;
 
 #[cfg(feature = "runtime-benchmarks")]
-mod benchmarking;
+pub mod benchmarking;
 pub mod weights;
 use weights::WeightInfo;
 
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