123456789101112131415161718192021222324252627282930313233343536373839404142434445464748#![cfg_attr(not(feature = "std"), no_std)]4950#[cfg(feature = "runtime-benchmarks")]51mod benchmarking;5253pub mod types;54pub mod weights;5556use sp_std::{57 vec::{Vec},58 vec,59 iter::Sum,60 borrow::ToOwned,61 cell::RefCell,62};63use sp_core::H160;64use codec::EncodeLike;65use pallet_balances::BalanceLock;66pub use types::*;6768use up_data_structs::CollectionId;6970use frame_support::{71 dispatch::{DispatchResult},72 traits::{73 Get, LockableCurrency,74 tokens::Balance,75 fungible::{Inspect, InspectFreeze, Mutate, MutateFreeze},76 },77 ensure, BoundedVec,78};7980use weights::WeightInfo;8182pub use pallet::*;83use pallet_evm::account::CrossAccountId;84use sp_runtime::{85 Perbill,86 traits::{BlockNumberProvider, CheckedAdd, CheckedSub, AccountIdConversion, Zero},87 ArithmeticError,88};8990pub const LOCK_IDENTIFIER: [u8; 8] = *b"appstake";9192const PENDING_LIMIT_PER_BLOCK: u32 = 3;9394type BalanceOf<T> =95 <<T as Config>::Currency as Inspect<<T as frame_system::Config>::AccountId>>::Balance;9697#[frame_support::pallet]98pub mod pallet {99 use super::*;100 use frame_support::{101 Blake2_128Concat, Twox64Concat, pallet_prelude::*, storage::Key, PalletId, weights::Weight,102 };103 use frame_system::pallet_prelude::*;104use sp_runtime::DispatchError;105106 #[pallet::config]107 pub trait Config:108 frame_system::Config + pallet_evm::Config + pallet_configuration::Config109 {110 111 type Currency: MutateFreeze<Self::AccountId>112 + Mutate<Self::AccountId> 113 + ExtendedLockableCurrency<Self::AccountId, Balance = <<Self as Config>::Currency as Inspect<Self::AccountId>>::Balance>;114115 116 type CollectionHandler: CollectionHandler<117 AccountId = Self::AccountId,118 CollectionId = CollectionId,119 >;120121 122 type ContractHandler: ContractHandler<AccountId = Self::CrossAccountId, ContractId = H160>;123124 125 type TreasuryAccountId: Get<Self::AccountId>;126127 128 #[pallet::constant]129 type PalletId: Get<PalletId>;130131 132 #[pallet::constant]133 type FreezeIdentifier: Get<<<Self as Config>::Currency as InspectFreeze<Self::AccountId>>::Id>;134135 136 #[pallet::constant]137 type RecalculationInterval: Get<Self::BlockNumber>;138139 140 #[pallet::constant]141 type PendingInterval: Get<Self::BlockNumber>;142143 144 #[pallet::constant]145 type IntervalIncome: Get<Perbill>;146147 148 #[pallet::constant]149 type Nominal: Get<BalanceOf<Self>>;150151 152 type WeightInfo: WeightInfo;153154 155 type RelayBlockNumberProvider: BlockNumberProvider<BlockNumber = Self::BlockNumber>;156157 158 type RuntimeEvent: IsType<<Self as frame_system::Config>::RuntimeEvent> + From<Event<Self>>;159 }160161 #[pallet::pallet]162 pub struct Pallet<T>(_);163164 #[pallet::event]165 #[pallet::generate_deposit(pub(super) fn deposit_event)]166 pub enum Event<T: Config> {167 168 169 170 171 172 173 StakingRecalculation(174 175 T::AccountId,176 177 BalanceOf<T>,178 179 BalanceOf<T>,180 ),181182 183 184 185 186 187 Stake(T::AccountId, BalanceOf<T>),188189 190 191 192 193 194 Unstake(T::AccountId, BalanceOf<T>),195196 197 198 199 200 SetAdmin(T::AccountId),201 }202203 #[pallet::error]204 pub enum Error<T> {205 206 AdminNotSet,207 208 NoPermission,209 210 NotSufficientFunds,211 212 PendingForBlockOverflow,213 214 SponsorNotSet,215 216 IncorrectLockedBalanceOperation,217 218 InsufficientStakedBalance,219 220 InconsistencyState221 }222223 224 #[pallet::storage]225 pub type TotalStaked<T: Config> = StorageValue<Value = BalanceOf<T>, QueryKind = ValueQuery>;226227 228 #[pallet::storage]229 pub type Admin<T: Config> = StorageValue<Value = T::AccountId, QueryKind = OptionQuery>;230231 232 233 234 235 236 237 #[pallet::storage]238 pub type Staked<T: Config> = StorageNMap<239 Key = (240 Key<Blake2_128Concat, T::AccountId>,241 Key<Twox64Concat, T::BlockNumber>,242 ),243 Value = (BalanceOf<T>, T::BlockNumber),244 QueryKind = ValueQuery,245 >;246247 248 249 250 251 #[pallet::storage]252 pub type StakesPerAccount<T: Config> =253 StorageMap<_, Blake2_128Concat, T::AccountId, u8, ValueQuery>;254255 256 257 258 259 #[pallet::storage]260 pub type PendingUnstake<T: Config> = StorageMap<261 _,262 Twox64Concat,263 T::BlockNumber,264 BoundedVec<(T::AccountId, BalanceOf<T>), ConstU32<PENDING_LIMIT_PER_BLOCK>>,265 ValueQuery,266 >;267268 269 270 #[pallet::storage]271 #[pallet::getter(fn get_next_calculated_record)]272 pub type PreviousCalculatedRecord<T: Config> =273 StorageValue<Value = (T::AccountId, T::BlockNumber), QueryKind = OptionQuery>;274275 276 277 278279 #[pallet::hooks]280 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {281 282 283 284 fn on_initialize(current_block_number: T::BlockNumber) -> Weight285 where286 <T as frame_system::Config>::BlockNumber: From<u32>,287 {288 let block_pending = PendingUnstake::<T>::take(current_block_number);289 let counter = block_pending.len() as u32;290291 if !block_pending.is_empty() {292 block_pending.into_iter().for_each(|(staker, amount)| {293 Self::get_freezed_balance(&staker).map(|b| {294 let new_state = b.checked_sub(&amount).unwrap_or_default();295 Self::set_freeze_unchecked(&staker, new_state);296 });297 });298 }299300 <T as Config>::WeightInfo::on_initialize(counter)301 }302303 304 305 306 307 308 309 310311 312313 314 315 316 317 318 319 320 321 322 323 324325 326 327 328 329 330 331332 333 334 335 336 337 338 339340 341 342 343 344 345 346 347 348 349 350 351 352 353 354355 356 357358 359 360 361 362 363 364 365 366367 368 369 370 371 372 373 374375 376 377 378 379 380 381 382 383 384385 386 387388 389 390 391392 393 394 395396 397398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413414 415 416 417 418 419 420 421 422423 424 425 426 427 428 429 }430431 #[pallet::call]432 impl<T: Config> Pallet<T>433 where434 T::BlockNumber: From<u32> + Into<u32>,435 <<T as Config>::Currency as Inspect<T::AccountId>>::Balance: Sum + From<u128>,436 {437 438 439 440 441 442 443 444 445 446 #[pallet::call_index(0)]447 #[pallet::weight(<T as Config>::WeightInfo::set_admin_address())]448 pub fn set_admin_address(origin: OriginFor<T>, admin: T::CrossAccountId) -> DispatchResult {449 ensure_root(origin)?;450451 <Admin<T>>::set(Some(admin.as_sub().to_owned()));452453 Self::deposit_event(Event::SetAdmin(admin.as_sub().to_owned()));454455 Ok(())456 }457458 459 460 461 462 463 464 465 #[pallet::call_index(1)]466 #[pallet::weight(<T as Config>::WeightInfo::stake())]467 pub fn stake(staker: OriginFor<T>, amount: BalanceOf<T>) -> DispatchResult {468 let staker_id = ensure_signed(staker)?;469470 ensure!(471 StakesPerAccount::<T>::get(&staker_id) < 10,472 Error::<T>::NoPermission473 );474475 ensure!(476 amount >= <BalanceOf<T>>::from(100u128) * T::Nominal::get(),477 ArithmeticError::Underflow478 );479 let config = <PalletConfiguration<T>>::get();480481 let balance = <<T as Config>::Currency as Inspect<T::AccountId>>::balance(&staker_id);482483 484 ensure!(485 amount486 <= match Self::get_freezed_balance(&staker_id) {487 Some(freezed_by_pallet) => balance488 .checked_sub(&freezed_by_pallet)489 .ok_or(ArithmeticError::Underflow)?,490 None => balance,491 },492 ArithmeticError::Underflow493 );494495 Self::add_freeze_balance(&staker_id, amount)?;496497 let block_number = T::RelayBlockNumberProvider::current_block_number();498499 500 501 let recalculate_after_interval: T::BlockNumber =502 if block_number % config.recalculation_interval == 0u32.into() {503 1u32.into()504 } else {505 2u32.into()506 };507508 509 510 let recalc_block = (block_number / config.recalculation_interval511 + recalculate_after_interval)512 * config.recalculation_interval;513514 <Staked<T>>::insert((&staker_id, block_number), {515 let mut balance_and_recalc_block = <Staked<T>>::get((&staker_id, block_number));516 balance_and_recalc_block.0 = balance_and_recalc_block517 .0518 .checked_add(&amount)519 .ok_or(ArithmeticError::Overflow)?;520 balance_and_recalc_block.1 = recalc_block;521 balance_and_recalc_block522 });523524 <TotalStaked<T>>::set(525 <TotalStaked<T>>::get()526 .checked_add(&amount)527 .ok_or(ArithmeticError::Overflow)?,528 );529530 StakesPerAccount::<T>::mutate(&staker_id, |stakes| *stakes += 1);531532 Self::deposit_event(Event::Stake(staker_id, amount));533534 Ok(())535 }536537 538 539 540 #[pallet::call_index(2)]541 #[pallet::weight(<T as Config>::WeightInfo::unstake_all())]542 pub fn unstake_all(staker: OriginFor<T>) -> DispatchResult {543 let staker_id = ensure_signed(staker)?;544545 Self::unstake_all_internal(staker_id)546 }547548 549 550 551 552 553 554 555 556 #[pallet::call_index(8)]557 #[pallet::weight(<T as Config>::WeightInfo::unstake_partial())]558 pub fn unstake_partial(staker: OriginFor<T>, amount: BalanceOf<T>) -> DispatchResult {559 let staker_id = ensure_signed(staker)?;560561 Self::unstake_partial_internal(staker_id, amount)562 }563564 565 566 567 568 569 570 571 572 573 #[pallet::call_index(3)]574 #[pallet::weight(<T as Config>::WeightInfo::sponsor_collection())]575 pub fn sponsor_collection(576 admin: OriginFor<T>,577 collection_id: CollectionId,578 ) -> DispatchResult {579 let admin_id = ensure_signed(admin)?;580 ensure!(581 admin_id == Admin::<T>::get().ok_or(Error::<T>::AdminNotSet)?,582 Error::<T>::NoPermission583 );584585 T::CollectionHandler::set_sponsor(Self::account_id(), collection_id)586 }587588 589 590 591 592 593 594 595 596 597 598 599 #[pallet::call_index(4)]600 #[pallet::weight(<T as Config>::WeightInfo::stop_sponsoring_collection())]601 pub fn stop_sponsoring_collection(602 admin: OriginFor<T>,603 collection_id: CollectionId,604 ) -> DispatchResult {605 let admin_id = ensure_signed(admin)?;606607 ensure!(608 admin_id == Admin::<T>::get().ok_or(Error::<T>::AdminNotSet)?,609 Error::<T>::NoPermission610 );611612 ensure!(613 T::CollectionHandler::sponsor(collection_id)?.ok_or(<Error<T>>::SponsorNotSet)?614 == Self::account_id(),615 <Error<T>>::NoPermission616 );617 T::CollectionHandler::remove_collection_sponsor(collection_id)618 }619620 621 622 623 624 625 626 627 628 629 #[pallet::call_index(5)]630 #[pallet::weight(<T as Config>::WeightInfo::sponsor_contract())]631 pub fn sponsor_contract(admin: OriginFor<T>, contract_id: H160) -> DispatchResult {632 let admin_id = ensure_signed(admin)?;633634 ensure!(635 admin_id == Admin::<T>::get().ok_or(Error::<T>::AdminNotSet)?,636 Error::<T>::NoPermission637 );638639 T::ContractHandler::set_sponsor(640 T::CrossAccountId::from_sub(Self::account_id()),641 contract_id,642 )643 }644645 646 647 648 649 650 651 652 653 654 655 656 #[pallet::call_index(6)]657 #[pallet::weight(<T as Config>::WeightInfo::stop_sponsoring_contract())]658 pub fn stop_sponsoring_contract(admin: OriginFor<T>, contract_id: H160) -> DispatchResult {659 let admin_id = ensure_signed(admin)?;660661 ensure!(662 admin_id == Admin::<T>::get().ok_or(Error::<T>::AdminNotSet)?,663 Error::<T>::NoPermission664 );665666 ensure!(667 T::ContractHandler::sponsor(contract_id)?668 .ok_or(<Error<T>>::SponsorNotSet)?669 .as_sub() == &Self::account_id(),670 <Error<T>>::NoPermission671 );672 T::ContractHandler::remove_contract_sponsor(contract_id)673 }674675 676 677 678 679 680 681 682 683 684 685 686 687 #[pallet::call_index(7)]688 #[pallet::weight(<T as Config>::WeightInfo::payout_stakers(stakers_number.unwrap_or(DEFAULT_NUMBER_PAYOUTS) as u32))]689 pub fn payout_stakers(admin: OriginFor<T>, stakers_number: Option<u8>) -> DispatchResult {690 let admin_id = ensure_signed(admin)?;691692 ensure!(693 admin_id == Admin::<T>::get().ok_or(Error::<T>::AdminNotSet)?,694 Error::<T>::NoPermission695 );696 let config = <PalletConfiguration<T>>::get();697698 let mut stakers_number = stakers_number.unwrap_or(DEFAULT_NUMBER_PAYOUTS);699700 ensure!(701 stakers_number <= config.max_stakers_per_calculation && stakers_number != 0,702 Error::<T>::NoPermission703 );704705 706 707 let current_recalc_block = Self::get_current_recalc_block(708 T::RelayBlockNumberProvider::current_block_number(),709 &config,710 );711712 713 714 let next_recalc_block = current_recalc_block + config.recalculation_interval;715716 let mut storage_iterator = Self::get_next_calculated_key()717 .map_or(Staked::<T>::iter(), |key| Staked::<T>::iter_from(key));718719 PreviousCalculatedRecord::<T>::set(None);720721 {722 723 let last_id = RefCell::new(None);724 725 let mut last_staked_calculated_block = Default::default();726 727 let income_acc = RefCell::new(BalanceOf::<T>::default());728 729 let amount_acc = RefCell::new(BalanceOf::<T>::default());730731 732 733 734 735 736 737 738 let flush_stake = || -> DispatchResult {739 if let Some(last_id) = &*last_id.borrow() {740 if !income_acc.borrow().is_zero() {741 <<T as Config>::Currency as Mutate<T::AccountId>>::transfer(742 &T::TreasuryAccountId::get(),743 last_id,744 *income_acc.borrow(),745 frame_support::traits::tokens::Preservation::Protect,746 )?;747748 Self::add_freeze_balance(last_id, *income_acc.borrow())?;749 <TotalStaked<T>>::try_mutate(|staked| -> DispatchResult {750 *staked = staked751 .checked_add(&*income_acc.borrow())752 .ok_or(ArithmeticError::Overflow)?;753 Ok(())754 })?;755756 Self::deposit_event(Event::StakingRecalculation(757 last_id.clone(),758 *amount_acc.borrow(),759 *income_acc.borrow(),760 ));761 }762763 *income_acc.borrow_mut() = BalanceOf::<T>::default();764 *amount_acc.borrow_mut() = BalanceOf::<T>::default();765 }766 Ok(())767 };768769 770 771 772 773 774 775 while let Some((776 (current_id, staked_block),777 (amount, next_recalc_block_for_stake),778 )) = storage_iterator.next()779 {780 781 782 783 if last_id.borrow().as_ref() != Some(¤t_id) {784 if stakers_number > 0 {785 flush_stake()?;786 *last_id.borrow_mut() = Some(current_id.clone());787 stakers_number -= 1;788 }789 790 else {791 if let Some(staker) = &*last_id.borrow() {792 793 PreviousCalculatedRecord::<T>::set(Some((794 staker.clone(),795 last_staked_calculated_block,796 )));797 }798 break;799 };800 };801802 803 if current_recalc_block >= next_recalc_block_for_stake {804 *amount_acc.borrow_mut() += amount;805 Self::recalculate_and_insert_stake(806 ¤t_id,807 staked_block,808 next_recalc_block,809 amount,810 ((current_recalc_block - next_recalc_block_for_stake)811 / config.recalculation_interval)812 .into() + 1,813 &mut *income_acc.borrow_mut(),814 );815 }816 last_staked_calculated_block = staked_block;817 }818 flush_stake()?;819 }820821 Ok(())822 }823824 825 826 827 828 829 830 #[pallet::call_index(9)]831 #[pallet::weight(T::DbWeight::get().reads_writes(2, 2) * stakers.len() as u64)]832 pub fn upgrade_accounts(833 origin: OriginFor<T>,834 stakers: Vec<T::AccountId>,835 ) -> DispatchResult {836 ensure_signed(origin)?;837838 stakers.into_iter().try_for_each(|s| -> Result<_, DispatchError> {839 if let Some(lock) = Self::get_locked_balance(&s) {840 841 if let Some(_) = Self::get_freezed_balance(&s) {842 return Err(Error::<T>::InconsistencyState.into())843 }844 845 <<T as Config>::Currency as LockableCurrency<T::AccountId>>::remove_lock(846 LOCK_IDENTIFIER,847 &s,848 );849850 Self::set_freeze_unchecked(&s, lock.amount);851 Ok(())852 } else {853 Ok(())854 }855 })?;856 857 Ok(())858 }859 }860}861862impl<T: Config> Pallet<T> {863 864 865 866 867 pub fn account_id() -> T::AccountId {868 T::PalletId::get().into_account_truncating()869 }870871 872 873 874 875 fn unstake_partial_internal(876 staker_id: T::AccountId,877 unstaked_balance: BalanceOf<T>,878 ) -> DispatchResult {879 if unstaked_balance == Default::default() {880 return Ok(());881 }882883 let config = <PalletConfiguration<T>>::get();884885 886 let unpending_block = <frame_system::Pallet<T>>::block_number() + config.pending_interval;887888 let mut pendings = <PendingUnstake<T>>::get(unpending_block);889890 891 ensure!(!pendings.is_full(), Error::<T>::PendingForBlockOverflow);892893 let mut stakes = Staked::<T>::iter_prefix((&staker_id,)).collect::<Vec<_>>();894895 let total_staked = stakes896 .iter()897 .fold(<BalanceOf<T>>::default(), |acc, (_, (balance, _))| {898 acc + *balance899 });900901 ensure!(902 unstaked_balance <= total_staked,903 <Error<T>>::InsufficientStakedBalance904 );905906 <TotalStaked<T>>::set(907 <TotalStaked<T>>::get()908 .checked_sub(&unstaked_balance)909 .ok_or(ArithmeticError::Underflow)?,910 );911912 stakes.sort_by_key(|(block, _)| *block);913914 let mut acc_amount = unstaked_balance;915 let mut will_deleted_stakes_count = 0u8;916917 let changed_stakes = stakes918 .into_iter()919 .map_while(|(block, (balance_per_block, _))| {920 if acc_amount == <BalanceOf<T>>::default() {921 return None;922 }923 if acc_amount < balance_per_block {924 let res = (block, balance_per_block - acc_amount);925 acc_amount = <BalanceOf<T>>::default();926 return Some(res);927 } else {928 acc_amount -= balance_per_block;929 will_deleted_stakes_count += 1;930 return Some((block, <BalanceOf<T>>::default()));931 }932 })933 .collect::<Vec<_>>();934935 pendings936 .try_push((staker_id.clone(), unstaked_balance))937 .map_err(|_| Error::<T>::PendingForBlockOverflow)?;938939 StakesPerAccount::<T>::try_mutate(&staker_id, |stakes| -> DispatchResult {940 *stakes = stakes941 .checked_sub(will_deleted_stakes_count)942 .ok_or(ArithmeticError::Underflow)?;943 Ok(())944 })?;945946 changed_stakes947 .into_iter()948 .for_each(|(staked_block, current_stake_state)| {949 if current_stake_state == Default::default() {950 <Staked<T>>::remove((&staker_id, staked_block));951 } else {952 <Staked<T>>::mutate((&staker_id, staked_block), |(old_stake_state, _)| {953 *old_stake_state = current_stake_state954 });955 }956 });957958 <PendingUnstake<T>>::insert(unpending_block, pendings);959960 Self::deposit_event(Event::Unstake(staker_id, unstaked_balance));961962 Ok(())963 }964965 966 967 968 969 970 971 972 973 974 975 976977 978 979 980 981 fn add_freeze_balance(staker: &T::AccountId, amount: BalanceOf<T>) -> DispatchResult {982 Self::get_freezed_balance(staker)983 .unwrap_or_default()984 .checked_add(&amount)985 .map(|freeze| Self::set_freeze_unchecked(staker, freeze))986 .ok_or(ArithmeticError::Overflow.into())987 }988989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 10081009 1010 1011 1012 1013 fn set_freeze_unchecked(staker: &T::AccountId, amount: BalanceOf<T>) {1014 if amount.is_zero() {1015 <<T as Config>::Currency as MutateFreeze<T::AccountId>>::thaw(1016 &T::FreezeIdentifier::get(),1017 &staker,1018 );1019 } else {1020 <<T as Config>::Currency as MutateFreeze<T::AccountId>>::set_freeze(1021 &T::FreezeIdentifier::get(),1022 staker,1023 amount,1024 );1025 }1026 }10271028 1029 1030 1031 pub fn get_locked_balance(1032 staker: impl EncodeLike<T::AccountId>,1033 ) -> Option<BalanceLock<BalanceOf<T>>> {1034 <<T as Config>::Currency as ExtendedLockableCurrency<T::AccountId>>::locks(staker)1035 .into_iter()1036 .find(|l| l.id == LOCK_IDENTIFIER)1037 }10381039 1040 1041 1042 pub fn get_freezed_balance(staker: &T::AccountId) -> Option<BalanceOf<T>> {1043 let res = <<T as Config>::Currency as InspectFreeze<T::AccountId>>::balance_frozen(1044 &T::FreezeIdentifier::get(),1045 staker,1046 );10471048 if res == Zero::zero() {1049 None1050 } else {1051 Some(res)1052 }1053 }10541055 1056 1057 1058 pub fn total_staked_by_id(staker: impl EncodeLike<T::AccountId>) -> Option<BalanceOf<T>> {1059 let staked = Staked::<T>::iter_prefix((staker,))1060 .fold(<BalanceOf<T>>::default(), |acc, (_, (amount, _))| {1061 acc + amount1062 });1063 if staked != <BalanceOf<T>>::default() {1064 Some(staked)1065 } else {1066 None1067 }1068 }10691070 1071 1072 1073 1074 pub fn total_staked_by_id_per_block(1075 staker: impl EncodeLike<T::AccountId>,1076 ) -> Option<Vec<(T::BlockNumber, BalanceOf<T>)>> {1077 let mut staked = Staked::<T>::iter_prefix((staker,))1078 .map(|(block, (amount, _))| (block, amount))1079 .collect::<Vec<_>>();1080 staked.sort_by_key(|(block, _)| *block);1081 if !staked.is_empty() {1082 Some(staked)1083 } else {1084 None1085 }1086 }10871088 1089 1090 1091 pub fn cross_id_total_staked(staker: Option<T::CrossAccountId>) -> Option<BalanceOf<T>> {1092 staker.map_or(Some(<TotalStaked<T>>::get()), |s| {1093 Self::total_staked_by_id(s.as_sub())1094 })1095 }10961097 1098 1099 1100 1101 pub fn cross_id_total_staked_per_block(1102 staker: T::CrossAccountId,1103 ) -> Vec<(T::BlockNumber, BalanceOf<T>)> {1104 Self::total_staked_by_id_per_block(staker.as_sub()).unwrap_or_default()1105 }11061107 fn recalculate_and_insert_stake(1108 staker: &T::AccountId,1109 staked_block: T::BlockNumber,1110 next_recalc_block: T::BlockNumber,1111 base: BalanceOf<T>,1112 iters: u32,1113 income_acc: &mut BalanceOf<T>,1114 ) {1115 let income = Self::calculate_income(base, iters);11161117 base.checked_add(&income).map(|res| {1118 <Staked<T>>::insert((staker, staked_block), (res, next_recalc_block));1119 *income_acc += income;1120 });1121 }11221123 fn calculate_income<I>(base: I, iters: u32) -> I1124 where1125 I: EncodeLike<BalanceOf<T>> + Balance,1126 {1127 let config = <PalletConfiguration<T>>::get();1128 let mut income = base;11291130 (0..iters).for_each(|_| income += config.interval_income * income);11311132 income - base1133 }11341135 1136 1137 fn get_current_recalc_block(1138 current_relay_block: T::BlockNumber,1139 config: &PalletConfiguration<T>,1140 ) -> T::BlockNumber {1141 (current_relay_block / config.recalculation_interval) * config.recalculation_interval1142 }11431144 fn get_next_calculated_key() -> Option<Vec<u8>> {1145 Self::get_next_calculated_record().map(|key| Staked::<T>::hashed_key_for(key))1146 }1147}11481149impl<T: Config> Pallet<T>1150where1151 <<T as Config>::Currency as Inspect<T::AccountId>>::Balance: Sum,1152{1153 1154 1155 1156 1157 1158 1159 1160 1161 pub fn cross_id_pending_unstake(staker: Option<T::CrossAccountId>) -> BalanceOf<T> {1162 staker.map_or(1163 PendingUnstake::<T>::iter_values()1164 .flat_map(|pendings| pendings.into_iter().map(|(_, amount)| amount))1165 .sum(),1166 |s| {1167 PendingUnstake::<T>::iter_values()1168 .flatten()1169 .filter_map(|(id, amount)| {1170 if id == *s.as_sub() {1171 Some(amount)1172 } else {1173 None1174 }1175 })1176 .sum()1177 },1178 )1179 }11801181 1182 1183 1184 1185 pub fn cross_id_pending_unstake_per_block(1186 staker: T::CrossAccountId,1187 ) -> Vec<(T::BlockNumber, BalanceOf<T>)> {1188 let mut unsorted_res = vec![];1189 PendingUnstake::<T>::iter().for_each(|(block, pendings)| {1190 pendings.into_iter().for_each(|(id, amount)| {1191 if id == *staker.as_sub() {1192 unsorted_res.push((block, amount));1193 };1194 })1195 });11961197 unsorted_res.sort_by_key(|(block, _)| *block);1198 unsorted_res1199 }12001201 fn unstake_all_internal(staker_id: T::AccountId) -> DispatchResult {1202 let config = <PalletConfiguration<T>>::get();12031204 1205 let block = <frame_system::Pallet<T>>::block_number() + config.pending_interval;12061207 let mut pendings = <PendingUnstake<T>>::get(block);12081209 1210 ensure!(!pendings.is_full(), Error::<T>::PendingForBlockOverflow);12111212 let total_staked: BalanceOf<T> = Staked::<T>::drain_prefix((&staker_id,))1213 .map(|(_, (amount, _))| amount)1214 .sum();12151216 if total_staked.is_zero() {1217 return Ok(());1218 }12191220 pendings1221 .try_push((staker_id.clone(), total_staked))1222 .map_err(|_| Error::<T>::PendingForBlockOverflow)?;12231224 <PendingUnstake<T>>::insert(block, pendings);12251226 TotalStaked::<T>::set(1227 TotalStaked::<T>::get()1228 .checked_sub(&total_staked)1229 .ok_or(ArithmeticError::Underflow)?,1230 );12311232 StakesPerAccount::<T>::remove(&staker_id);12331234 Self::deposit_event(Event::Unstake(staker_id, total_staked));12351236 Ok(())1237 }1238}