git.delta.rocks / unique-network / refs/commits / 25c961748e58

difftreelog

source

pallets/app-promotion/src/lib.rs23.4 KiBsourcehistory
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::{40	vec::{Vec},41	vec,42	iter::Sum,43	borrow::ToOwned,44	cell::RefCell,45};46use sp_core::H160;47use codec::EncodeLike;48use pallet_balances::BalanceLock;49pub use types::*;5051// use up_common::constants::{DAYS, UNIQUE};52use up_data_structs::CollectionId;5354use frame_support::{55	dispatch::{DispatchResult},56	traits::{57		Currency, Get, LockableCurrency, WithdrawReasons, tokens::Balance, ExistenceRequirement,58	},59	ensure,60};6162use weights::WeightInfo;6364pub use pallet::*;65use pallet_evm::account::CrossAccountId;66use sp_runtime::{67	Perbill,68	traits::{BlockNumberProvider, CheckedAdd, CheckedSub, AccountIdConversion, Zero},69	ArithmeticError,70};7172pub const LOCK_IDENTIFIER: [u8; 8] = *b"appstake";7374const PENDING_LIMIT_PER_BLOCK: u32 = 3;7576type BalanceOf<T> =77	<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;7879#[frame_support::pallet]80pub mod pallet {81	use super::*;82	use frame_support::{83		Blake2_128Concat, Twox64Concat, pallet_prelude::*, storage::Key, PalletId,84		traits::ReservableCurrency,85	};86	use frame_system::pallet_prelude::*;8788	#[pallet::config]89	pub trait Config: frame_system::Config + pallet_evm::account::Config {90		type Currency: ExtendedLockableCurrency<Self::AccountId>91			+ ReservableCurrency<Self::AccountId>;9293		type CollectionHandler: CollectionHandler<94			AccountId = Self::AccountId,95			CollectionId = CollectionId,96		>;9798		type ContractHandler: ContractHandler<AccountId = Self::CrossAccountId, ContractId = H160>;99100		type TreasuryAccountId: Get<Self::AccountId>;101102		/// The app's pallet id, used for deriving its sovereign account ID.103		#[pallet::constant]104		type PalletId: Get<PalletId>;105106		/// In relay blocks.107		#[pallet::constant]108		type RecalculationInterval: Get<Self::BlockNumber>;109		/// In relay blocks.110		#[pallet::constant]111		type PendingInterval: Get<Self::BlockNumber>;112113		// /// In chain blocks.114		// #[pallet::constant]115		// type Day: Get<Self::BlockNumber>; // useless116117		#[pallet::constant]118		type Nominal: Get<BalanceOf<Self>>;119120		#[pallet::constant]121		type IntervalIncome: Get<Perbill>;122123		/// Weight information for extrinsics in this pallet.124		type WeightInfo: WeightInfo;125126		// The relay block number provider127		type RelayBlockNumberProvider: BlockNumberProvider<BlockNumber = Self::BlockNumber>;128129		/// Events compatible with [`frame_system::Config::Event`].130		type Event: IsType<<Self as frame_system::Config>::Event> + From<Event<Self>>;131	}132133	#[pallet::pallet]134	#[pallet::generate_store(pub(super) trait Store)]135	pub struct Pallet<T>(_);136137	#[pallet::event]138	#[pallet::generate_deposit(fn deposit_event)]139	pub enum Event<T: Config> {140		StakingRecalculation(141			/// An recalculated staker142			T::AccountId,143			/// Base on which interest is calculated144			BalanceOf<T>,145			/// Amount of accrued interest146			BalanceOf<T>,147		),148		Stake(T::AccountId, BalanceOf<T>),149		Unstake(T::AccountId, BalanceOf<T>),150		SetAdmin(T::AccountId),151	}152153	#[pallet::error]154	pub enum Error<T> {155		/// Error due to action requiring admin to be set156		AdminNotSet,157		/// No permission to perform an action158		NoPermission,159		/// Insufficient funds to perform an action160		NotSufficientFunds,161		PendingForBlockOverflow,162		/// An error related to the fact that an invalid argument was passed to perform an action163		InvalidArgument,164	}165166	#[pallet::storage]167	pub type TotalStaked<T: Config> = StorageValue<Value = BalanceOf<T>, QueryKind = ValueQuery>;168169	#[pallet::storage]170	pub type Admin<T: Config> = StorageValue<Value = T::AccountId, QueryKind = OptionQuery>;171172	/// Amount of tokens staked by account in the blocknumber.173	#[pallet::storage]174	pub type Staked<T: Config> = StorageNMap<175		Key = (176			Key<Blake2_128Concat, T::AccountId>,177			Key<Twox64Concat, T::BlockNumber>,178		),179		Value = (BalanceOf<T>, T::BlockNumber),180		QueryKind = ValueQuery,181	>;182	/// Amount of stakes for an Account183	#[pallet::storage]184	pub type StakesPerAccount<T: Config> =185		StorageMap<_, Blake2_128Concat, T::AccountId, u8, ValueQuery>;186187	/// Amount of tokens pending unstake per user per block.188	// #[pallet::storage]189	// pub type PendingUnstake<T: Config> = StorageNMap<190	// 	Key = (191	// 		Key<Blake2_128Concat, T::AccountId>,192	// 		Key<Twox64Concat, T::BlockNumber>,193	// 	),194	// 	Value = BalanceOf<T>,195	// 	QueryKind = ValueQuery,196	// >;197	#[pallet::storage]198	pub type PendingUnstake<T: Config> = StorageMap<199		_,200		Twox64Concat,201		T::BlockNumber,202		BoundedVec<(T::AccountId, BalanceOf<T>), ConstU32<PENDING_LIMIT_PER_BLOCK>>,203		ValueQuery,204	>;205206	// /// A block when app-promotion has started .I think this is redundant, because we only need `NextInterestBlock`.207	// #[pallet::storage]208	// pub type StartBlock<T: Config> = StorageValue<Value = T::BlockNumber, QueryKind = ValueQuery>;209210	// /// Next target block when interest is recalculated211	// #[pallet::storage]212	// #[pallet::getter(fn get_interest_block)]213	// pub type NextInterestBlock<T: Config> =214	// 	StorageValue<Value = T::BlockNumber, QueryKind = ValueQuery>;215216	/// Stores hash a record for which the last revenue recalculation was performed.217	/// If `None`, then recalculation has not yet been performed or calculations have been completed for all stakers.218	#[pallet::storage]219	#[pallet::getter(fn get_next_calculated_record)]220	pub type NextCalculatedRecord<T: Config> =221		StorageValue<Value = (T::AccountId, T::BlockNumber), QueryKind = OptionQuery>;222223	#[pallet::hooks]224	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {225		fn on_initialize(current_block_number: T::BlockNumber) -> Weight226		where227			<T as frame_system::Config>::BlockNumber: From<u32>,228		{229			let block_pending = PendingUnstake::<T>::take(current_block_number);230			let counter = block_pending.len() as u32;231			// add_weight(0, 1, 0);232233			if !block_pending.is_empty() {234				block_pending.into_iter().for_each(|(staker, amount)| {235					<T::Currency as ReservableCurrency<T::AccountId>>::unreserve(&staker, amount);236				});237			}238239			T::WeightInfo::on_initialize(counter)240		}241	}242243	#[pallet::call]244	impl<T: Config> Pallet<T>245	where246		T::BlockNumber: From<u32> + Into<u32>,247		<<T as Config>::Currency as Currency<T::AccountId>>::Balance: Sum + From<u128>,248	{249		#[pallet::weight(T::WeightInfo::set_admin_address())]250		pub fn set_admin_address(origin: OriginFor<T>, admin: T::CrossAccountId) -> DispatchResult {251			ensure_root(origin)?;252253			<Admin<T>>::set(Some(admin.as_sub().to_owned()));254255			Self::deposit_event(Event::SetAdmin(admin.as_sub().to_owned()));256257			Ok(())258		}259260		#[pallet::weight(T::WeightInfo::stake())]261		pub fn stake(staker: OriginFor<T>, amount: BalanceOf<T>) -> DispatchResult {262			let staker_id = ensure_signed(staker)?;263264			ensure!(265				StakesPerAccount::<T>::get(&staker_id) < 10,266				Error::<T>::NoPermission267			);268269			ensure!(270				amount >= Into::<BalanceOf<T>>::into(100u128) * T::Nominal::get(),271				ArithmeticError::Underflow272			);273274			let balance =275				<<T as Config>::Currency as Currency<T::AccountId>>::free_balance(&staker_id);276277			// ensure!(balance >= amount, ArithmeticError::Underflow);278279			<<T as Config>::Currency as Currency<T::AccountId>>::ensure_can_withdraw(280				&staker_id,281				amount,282				WithdrawReasons::all(),283				balance284					.checked_sub(&amount)285					.ok_or(ArithmeticError::Underflow)?,286			)?;287288			Self::add_lock_balance(&staker_id, amount)?;289290			let block_number = T::RelayBlockNumberProvider::current_block_number();291292			let recalculate_after_interval: T::BlockNumber =293				if block_number % T::RecalculationInterval::get() == 0u32.into() {294					1u32.into()295				} else {296					2u32.into()297				};298299			let recalc_block = (block_number / T::RecalculationInterval::get()300				+ recalculate_after_interval)301				* T::RecalculationInterval::get();302303			<Staked<T>>::insert((&staker_id, block_number), {304				let mut balance_and_recalc_block = <Staked<T>>::get((&staker_id, block_number));305				balance_and_recalc_block.0 = balance_and_recalc_block306					.0307					.checked_add(&amount)308					.ok_or(ArithmeticError::Overflow)?;309				balance_and_recalc_block.1 = recalc_block;310				balance_and_recalc_block311			});312313			<TotalStaked<T>>::set(314				<TotalStaked<T>>::get()315					.checked_add(&amount)316					.ok_or(ArithmeticError::Overflow)?,317			);318319			StakesPerAccount::<T>::mutate(&staker_id, |stakes| *stakes += 1);320321			Self::deposit_event(Event::Stake(staker_id, amount));322323			Ok(())324		}325326		#[pallet::weight(T::WeightInfo::unstake())]327		pub fn unstake(staker: OriginFor<T>) -> DispatchResultWithPostInfo {328			let staker_id = ensure_signed(staker)?;329			let block = <frame_system::Pallet<T>>::block_number() + T::PendingInterval::get();330			let mut pendings = <PendingUnstake<T>>::get(block);331332			ensure!(!pendings.is_full(), Error::<T>::PendingForBlockOverflow);333334			let mut total_stakes = 0u64;335336			let total_staked: BalanceOf<T> = Staked::<T>::drain_prefix((&staker_id,))337				.map(|(_, (amount, _))| {338					total_stakes += 1;339					amount340				})341				.sum();342343			if total_staked.is_zero() {344				return Ok(None.into()); // TO-DO345			}346347			pendings348				.try_push((staker_id.clone(), total_staked))349				.map_err(|_| Error::<T>::PendingForBlockOverflow)?;350351			<PendingUnstake<T>>::insert(block, pendings);352353			Self::unlock_balance_unchecked(&staker_id, total_staked);354355			<T::Currency as ReservableCurrency<T::AccountId>>::reserve(&staker_id, total_staked)?;356357			TotalStaked::<T>::set(358				TotalStaked::<T>::get()359					.checked_sub(&total_staked)360					.ok_or(ArithmeticError::Underflow)?,361			);362363			StakesPerAccount::<T>::remove(&staker_id);364365			Self::deposit_event(Event::Unstake(staker_id, total_staked));366367			Ok(None.into())368		}369370		#[pallet::weight(T::WeightInfo::sponsor_collection())]371		pub fn sponsor_collection(372			admin: OriginFor<T>,373			collection_id: CollectionId,374		) -> DispatchResult {375			let admin_id = ensure_signed(admin)?;376			ensure!(377				admin_id == Admin::<T>::get().ok_or(Error::<T>::AdminNotSet)?,378				Error::<T>::NoPermission379			);380381			T::CollectionHandler::set_sponsor(Self::account_id(), collection_id)382		}383		#[pallet::weight(T::WeightInfo::stop_sponsoring_collection())]384		pub fn stop_sponsoring_collection(385			admin: OriginFor<T>,386			collection_id: CollectionId,387		) -> DispatchResult {388			let admin_id = ensure_signed(admin)?;389390			ensure!(391				admin_id == Admin::<T>::get().ok_or(Error::<T>::AdminNotSet)?,392				Error::<T>::NoPermission393			);394395			ensure!(396				T::CollectionHandler::sponsor(collection_id)?.ok_or(<Error<T>>::InvalidArgument)?397					== Self::account_id(),398				<Error<T>>::NoPermission399			);400			T::CollectionHandler::remove_collection_sponsor(collection_id)401		}402403		#[pallet::weight(T::WeightInfo::sponsor_contract())]404		pub fn sponsor_contract(admin: OriginFor<T>, contract_id: H160) -> DispatchResult {405			let admin_id = ensure_signed(admin)?;406407			ensure!(408				admin_id == Admin::<T>::get().ok_or(Error::<T>::AdminNotSet)?,409				Error::<T>::NoPermission410			);411412			T::ContractHandler::set_sponsor(413				T::CrossAccountId::from_sub(Self::account_id()),414				contract_id,415			)416		}417418		#[pallet::weight(T::WeightInfo::stop_sponsoring_contract())]419		pub fn stop_sponsoring_contract(admin: OriginFor<T>, contract_id: H160) -> DispatchResult {420			let admin_id = ensure_signed(admin)?;421422			ensure!(423				admin_id == Admin::<T>::get().ok_or(Error::<T>::AdminNotSet)?,424				Error::<T>::NoPermission425			);426427			ensure!(428				T::ContractHandler::sponsor(contract_id)?.ok_or(<Error<T>>::InvalidArgument)?429					== T::CrossAccountId::from_sub(Self::account_id()),430				<Error<T>>::NoPermission431			);432			T::ContractHandler::remove_contract_sponsor(contract_id)433		}434435		#[pallet::weight(T::WeightInfo::payout_stakers(stakers_number.unwrap_or(20) as u32))]436		pub fn payout_stakers(admin: OriginFor<T>, stakers_number: Option<u8>) -> DispatchResult {437			let admin_id = ensure_signed(admin)?;438439			ensure!(440				admin_id == Admin::<T>::get().ok_or(Error::<T>::AdminNotSet)?,441				Error::<T>::NoPermission442			);443444			let current_recalc_block =445				Self::get_current_recalc_block(T::RelayBlockNumberProvider::current_block_number());446			let next_recalc_block = current_recalc_block + T::RecalculationInterval::get();447448			let mut storage_iterator = Self::get_next_calculated_key()449				.map_or(Staked::<T>::iter(), |key| Staked::<T>::iter_from(key));450451			NextCalculatedRecord::<T>::set(None);452453			// {454			// 	let mut stakers_number = stakers_number.unwrap_or(20);455			// 	let mut last_id = admin_id;456			// 	let mut income_acc = BalanceOf::<T>::default();457			// 	let mut amount_acc = BalanceOf::<T>::default();458459			// 	while let Some((460			// 		(current_id, staked_block),461			// 		(amount, next_recalc_block_for_stake),462			// 	)) = storage_iterator.next()463			// 	{464			// 		if last_id != current_id {465			// 			if income_acc != BalanceOf::<T>::default() {466			// 				<T::Currency as Currency<T::AccountId>>::transfer(467			// 					&T::TreasuryAccountId::get(),468			// 					&last_id,469			// 					income_acc,470			// 					ExistenceRequirement::KeepAlive,471			// 				)472			// 				.and_then(|_| Self::add_lock_balance(&last_id, income_acc))?;473474			// 				Self::deposit_event(Event::StakingRecalculation(475			// 					last_id, amount, income_acc,476			// 				));477			// 			}478479			// 			if stakers_number == 0 {480			// 				NextCalculatedRecord::<T>::set(Some((current_id, staked_block)));481			// 				break;482			// 			}483			// 			stakers_number -= 1;484			// 			income_acc = BalanceOf::<T>::default();485			// 			last_id = current_id;486			// 		};487			// 		if current_recalc_block >= next_recalc_block_for_stake {488			// 			Self::recalculate_and_insert_stake(489			// 				&last_id,490			// 				staked_block,491			// 				next_recalc_block,492			// 				amount,493			// 				((current_recalc_block - next_recalc_block_for_stake)494			// 					/ T::RecalculationInterval::get())495			// 				.into() + 1,496			// 				&mut income_acc,497			// 			);498			// 		}499			// 	}500			// }501502			// {503			// 	let mut stakers_number = stakers_number.unwrap_or(20);504			// 	let last_id = RefCell::new(None);505			// 	let income_acc = RefCell::new(BalanceOf::<T>::default());506			// 	let amount_acc = RefCell::new(BalanceOf::<T>::default());507508			// 	let flush_stake = || -> DispatchResult {509			// 		if let Some(last_id) = &*last_id.borrow() {510			// 			if !income_acc.borrow().is_zero() {511			// 				<T::Currency as Currency<T::AccountId>>::transfer(512			// 					&T::TreasuryAccountId::get(),513			// 					last_id,514			// 					*income_acc.borrow(),515			// 					ExistenceRequirement::KeepAlive,516			// 				)517			// 				.and_then(|_| {518			// 					Self::add_lock_balance(last_id, *income_acc.borrow());519			// 					<TotalStaked<T>>::try_mutate(|staked| {520			// 						staked521			// 							.checked_add(&*income_acc.borrow())522			// 							.ok_or(ArithmeticError::Overflow.into())523			// 					})524			// 				})?;525526			// 				Self::deposit_event(Event::StakingRecalculation(527			// 					last_id.clone(),528			// 					*amount_acc.borrow(),529			// 					*income_acc.borrow(),530			// 				));531			// 			}532533			// 			*income_acc.borrow_mut() = BalanceOf::<T>::default();534			// 			*amount_acc.borrow_mut() = BalanceOf::<T>::default();535			// 		}536			// 		Ok(())537			// 	};538539			// 	while let Some((540			// 		(current_id, staked_block),541			// 		(amount, next_recalc_block_for_stake),542			// 	)) = storage_iterator.next()543			// 	{544			// 		if stakers_number == 0 {545			// 			NextCalculatedRecord::<T>::set(Some((current_id, staked_block)));546			// 			break;547			// 		}548			// 		stakers_number -= 1;549			// 		if last_id.borrow().as_ref() != Some(&current_id) {550			// 			flush_stake()?;551			// 		};552			// 		*last_id.borrow_mut() = Some(current_id.clone());553			// 		if current_recalc_block >= next_recalc_block_for_stake {554			// 			*amount_acc.borrow_mut() += amount;555			// 			Self::recalculate_and_insert_stake(556			// 				&current_id,557			// 				staked_block,558			// 				next_recalc_block,559			// 				amount,560			// 				((current_recalc_block - next_recalc_block_for_stake)561			// 					/ T::RecalculationInterval::get())562			// 				.into() + 1,563			// 				&mut *income_acc.borrow_mut(),564			// 			);565			// 		}566			// 	}567			// 	flush_stake()?;568			// }569570			{571				let mut stakers_number = stakers_number.unwrap_or(20);572				let last_id = RefCell::new(None);573				let income_acc = RefCell::new(BalanceOf::<T>::default());574				let amount_acc = RefCell::new(BalanceOf::<T>::default());575576				let flush_stake = || -> DispatchResult {577					if let Some(last_id) = &*last_id.borrow() {578						if !income_acc.borrow().is_zero() {579							<T::Currency as Currency<T::AccountId>>::transfer(580								&T::TreasuryAccountId::get(),581								last_id,582								*income_acc.borrow(),583								ExistenceRequirement::KeepAlive,584							)585							.and_then(|_| {586								Self::add_lock_balance(last_id, *income_acc.borrow())?;587								<TotalStaked<T>>::try_mutate(|staked| {588									staked589										.checked_add(&*income_acc.borrow())590										.ok_or(ArithmeticError::Overflow.into())591								})592							})?;593594							Self::deposit_event(Event::StakingRecalculation(595								last_id.clone(),596								*amount_acc.borrow(),597								*income_acc.borrow(),598							));599						}600601						*income_acc.borrow_mut() = BalanceOf::<T>::default();602						*amount_acc.borrow_mut() = BalanceOf::<T>::default();603					}604					Ok(())605				};606607				while let Some((608					(current_id, staked_block),609					(amount, next_recalc_block_for_stake),610				)) = storage_iterator.next()611				{612					if stakers_number == 0 {613						NextCalculatedRecord::<T>::set(Some((current_id, staked_block)));614						break;615					}616					if last_id.borrow().as_ref() != Some(&current_id) {617						flush_stake()?;618						*last_id.borrow_mut() = Some(current_id.clone());619						stakers_number -= 1;620					};621					if current_recalc_block >= next_recalc_block_for_stake {622						*amount_acc.borrow_mut() += amount;623						Self::recalculate_and_insert_stake(624							&current_id,625							staked_block,626							next_recalc_block,627							amount,628							((current_recalc_block - next_recalc_block_for_stake)629								/ T::RecalculationInterval::get())630							.into() + 1,631							&mut *income_acc.borrow_mut(),632						);633					}634				}635				flush_stake()?;636			}637638			Ok(())639		}640	}641}642643impl<T: Config> Pallet<T> {644	pub fn account_id() -> T::AccountId {645		T::PalletId::get().into_account_truncating()646	}647648	fn unlock_balance_unchecked(staker: &T::AccountId, amount: BalanceOf<T>) {649		let mut locked_balance = Self::get_locked_balance(staker).map(|l| l.amount).unwrap();650		locked_balance -= amount;651		Self::set_lock_unchecked(staker, locked_balance);652	}653654	fn add_lock_balance(staker: &T::AccountId, amount: BalanceOf<T>) -> DispatchResult {655		Self::get_locked_balance(staker)656			.map_or(<BalanceOf<T>>::default(), |l| l.amount)657			.checked_add(&amount)658			.map(|new_lock| Self::set_lock_unchecked(staker, new_lock))659			.ok_or(ArithmeticError::Overflow.into())660	}661662	fn set_lock_unchecked(staker: &T::AccountId, amount: BalanceOf<T>) {663		if amount.is_zero() {664			<T::Currency as LockableCurrency<T::AccountId>>::remove_lock(LOCK_IDENTIFIER, &staker);665		} else {666			<T::Currency as LockableCurrency<T::AccountId>>::set_lock(667				LOCK_IDENTIFIER,668				staker,669				amount,670				WithdrawReasons::all(),671			)672		}673	}674675	pub fn get_locked_balance(676		staker: impl EncodeLike<T::AccountId>,677	) -> Option<BalanceLock<BalanceOf<T>>> {678		<T::Currency as ExtendedLockableCurrency<T::AccountId>>::locks(staker)679			.into_iter()680			.find(|l| l.id == LOCK_IDENTIFIER)681	}682683	pub fn total_staked_by_id(staker: impl EncodeLike<T::AccountId>) -> Option<BalanceOf<T>> {684		let staked = Staked::<T>::iter_prefix((staker,))685			.into_iter()686			.fold(<BalanceOf<T>>::default(), |acc, (_, (amount, _))| {687				acc + amount688			});689		if staked != <BalanceOf<T>>::default() {690			Some(staked)691		} else {692			None693		}694	}695696	pub fn total_staked_by_id_per_block(697		staker: impl EncodeLike<T::AccountId>,698	) -> Option<Vec<(T::BlockNumber, BalanceOf<T>)>> {699		let mut staked = Staked::<T>::iter_prefix((staker,))700			.into_iter()701			.map(|(block, (amount, _))| (block, amount))702			.collect::<Vec<_>>();703		staked.sort_by_key(|(block, _)| *block);704		if !staked.is_empty() {705			Some(staked)706		} else {707			None708		}709	}710711	pub fn cross_id_total_staked(staker: Option<T::CrossAccountId>) -> Option<BalanceOf<T>> {712		staker.map_or(Some(<TotalStaked<T>>::get()), |s| {713			Self::total_staked_by_id(s.as_sub())714		})715		// Self::total_staked_by_id(staker.as_sub())716	}717718	pub fn cross_id_locked_balance(staker: T::CrossAccountId) -> BalanceOf<T> {719		Self::get_locked_balance(staker.as_sub())720			.map(|l| l.amount)721			.unwrap_or_default()722	}723724	pub fn cross_id_total_staked_per_block(725		staker: T::CrossAccountId,726	) -> Vec<(T::BlockNumber, BalanceOf<T>)> {727		Self::total_staked_by_id_per_block(staker.as_sub()).unwrap_or_default()728	}729730	fn recalculate_and_insert_stake(731		staker: &T::AccountId,732		staked_block: T::BlockNumber,733		next_recalc_block: T::BlockNumber,734		base: BalanceOf<T>,735		iters: u32,736		income_acc: &mut BalanceOf<T>,737	) {738		let income = Self::calculate_income(base, iters);739740		base.checked_add(&income).map(|res| {741			<Staked<T>>::insert((staker, staked_block), (res, next_recalc_block));742			*income_acc += income;743		});744	}745746	fn calculate_income<I>(base: I, iters: u32) -> I747	where748		I: EncodeLike<BalanceOf<T>> + Balance,749	{750		let mut income = base;751752		(0..iters).for_each(|_| income += T::IntervalIncome::get() * income);753754		income - base755	}756757	fn get_current_recalc_block(current_relay_block: T::BlockNumber) -> T::BlockNumber {758		(current_relay_block / T::RecalculationInterval::get()) * T::RecalculationInterval::get()759	}760761	// fn get_next_recalc_block(current_relay_block: T::BlockNumber) -> T::BlockNumber {762	// 	Self::get_current_recalc_block(current_relay_block) + T::RecalculationInterval::get()763	// }764765	fn get_next_calculated_key() -> Option<Vec<u8>> {766		Self::get_next_calculated_record().map(|key| Staked::<T>::hashed_key_for(key))767	}768}769770impl<T: Config> Pallet<T>771where772	<<T as Config>::Currency as Currency<T::AccountId>>::Balance: Sum,773{774	pub fn cross_id_pending_unstake(staker: Option<T::CrossAccountId>) -> BalanceOf<T> {775		staker.map_or(776			PendingUnstake::<T>::iter_values()777				.flat_map(|pendings| pendings.into_iter().map(|(_, amount)| amount))778				.sum(),779			|s| {780				PendingUnstake::<T>::iter_values()781					.flatten()782					.filter_map(|(id, amount)| {783						if id == *s.as_sub() {784							Some(amount)785						} else {786							None787						}788					})789					.sum()790			},791		)792	}793794	pub fn cross_id_pending_unstake_per_block(795		staker: T::CrossAccountId,796	) -> Vec<(T::BlockNumber, BalanceOf<T>)> {797		let mut unsorted_res = vec![];798		PendingUnstake::<T>::iter().for_each(|(block, pendings)| {799			pendings.into_iter().for_each(|(id, amount)| {800				if id == *staker.as_sub() {801					unsorted_res.push((block, amount));802				};803			})804		});805806		unsorted_res.sort_by_key(|(block, _)| *block);807		unsorted_res808	}809}