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 InsufficientStakedBalance,217 218 InconsistencyState219 }220221 222 #[pallet::storage]223 pub type TotalStaked<T: Config> = StorageValue<Value = BalanceOf<T>, QueryKind = ValueQuery>;224225 226 #[pallet::storage]227 pub type Admin<T: Config> = StorageValue<Value = T::AccountId, QueryKind = OptionQuery>;228229 230 231 232 233 234 235 #[pallet::storage]236 pub type Staked<T: Config> = StorageNMap<237 Key = (238 Key<Blake2_128Concat, T::AccountId>,239 Key<Twox64Concat, T::BlockNumber>,240 ),241 Value = (BalanceOf<T>, T::BlockNumber),242 QueryKind = ValueQuery,243 >;244245 246 247 248 249 #[pallet::storage]250 pub type StakesPerAccount<T: Config> =251 StorageMap<_, Blake2_128Concat, T::AccountId, u8, ValueQuery>;252253 254 255 256 257 #[pallet::storage]258 pub type PendingUnstake<T: Config> = StorageMap<259 _,260 Twox64Concat,261 T::BlockNumber,262 BoundedVec<(T::AccountId, BalanceOf<T>), ConstU32<PENDING_LIMIT_PER_BLOCK>>,263 ValueQuery,264 >;265266 267 268 #[pallet::storage]269 #[pallet::getter(fn get_next_calculated_record)]270 pub type PreviousCalculatedRecord<T: Config> =271 StorageValue<Value = (T::AccountId, T::BlockNumber), QueryKind = OptionQuery>;272273 274 275 276277 #[pallet::hooks]278 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {279 280 281 282 fn on_initialize(current_block_number: T::BlockNumber) -> Weight283 where284 <T as frame_system::Config>::BlockNumber: From<u32>,285 {286 let block_pending = PendingUnstake::<T>::take(current_block_number);287 let counter = block_pending.len() as u32;288289 if !block_pending.is_empty() {290 block_pending.into_iter().for_each(|(staker, amount)| {291 Self::get_frozen_balance(&staker).map(|b| {292 let new_state = b.checked_sub(&amount).unwrap_or_default();293 Self::set_freeze_unchecked(&staker, new_state);294 });295 });296 }297298 <T as Config>::WeightInfo::on_initialize(counter)299 }300301 302 303 304 305 306 307 308309 310311 312 313 314 315 316 317 318 319 320 321 322323 324 325 326 327 328 329330 331 332 333 334 335 336 337338 339 340 341 342 343 344 345 346 347 348 349 350 351 352353 354 355356 357 358 359 360 361 362 363 364365 366 367 368 369 370 371 372373 374 375 376 377 378 379 380 381 382383 384 385386 387 388 389390 391 392 393394 395396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411412 413 414 415 416 417 418 419 420421 422 423 424 425 426 427 }428429 #[pallet::call]430 impl<T: Config> Pallet<T>431 where432 T::BlockNumber: From<u32> + Into<u32>,433 <<T as Config>::Currency as Inspect<T::AccountId>>::Balance: Sum + From<u128>,434 {435 436 437 438 439 440 441 442 443 444 #[pallet::call_index(0)]445 #[pallet::weight(<T as Config>::WeightInfo::set_admin_address())]446 pub fn set_admin_address(origin: OriginFor<T>, admin: T::CrossAccountId) -> DispatchResult {447 ensure_root(origin)?;448449 <Admin<T>>::set(Some(admin.as_sub().to_owned()));450451 Self::deposit_event(Event::SetAdmin(admin.as_sub().to_owned()));452453 Ok(())454 }455456 457 458 459 460 461 462 463 #[pallet::call_index(1)]464 #[pallet::weight(<T as Config>::WeightInfo::stake())]465 pub fn stake(staker: OriginFor<T>, amount: BalanceOf<T>) -> DispatchResult {466 let staker_id = ensure_signed(staker)?;467468 ensure!(469 StakesPerAccount::<T>::get(&staker_id) < 10,470 Error::<T>::NoPermission471 );472473 ensure!(474 amount >= <BalanceOf<T>>::from(100u128) * T::Nominal::get(),475 ArithmeticError::Underflow476 );477 let config = <PalletConfiguration<T>>::get();478479 let balance = <<T as Config>::Currency as Inspect<T::AccountId>>::balance(&staker_id);480481 482 ensure!(483 amount484 <= match Self::get_frozen_balance(&staker_id) {485 Some(frozen_by_pallet) => balance486 .checked_sub(&frozen_by_pallet)487 .ok_or(ArithmeticError::Underflow)?,488 None => balance,489 },490 ArithmeticError::Underflow491 );492493 Self::add_freeze_balance(&staker_id, amount)?;494495 let block_number = T::RelayBlockNumberProvider::current_block_number();496497 498 499 let recalculate_after_interval: T::BlockNumber =500 if block_number % config.recalculation_interval == 0u32.into() {501 1u32.into()502 } else {503 2u32.into()504 };505506 507 508 let recalc_block = (block_number / config.recalculation_interval509 + recalculate_after_interval)510 * config.recalculation_interval;511512 <Staked<T>>::insert((&staker_id, block_number), {513 let mut balance_and_recalc_block = <Staked<T>>::get((&staker_id, block_number));514 balance_and_recalc_block.0 = balance_and_recalc_block515 .0516 .checked_add(&amount)517 .ok_or(ArithmeticError::Overflow)?;518 balance_and_recalc_block.1 = recalc_block;519 balance_and_recalc_block520 });521522 <TotalStaked<T>>::set(523 <TotalStaked<T>>::get()524 .checked_add(&amount)525 .ok_or(ArithmeticError::Overflow)?,526 );527528 StakesPerAccount::<T>::mutate(&staker_id, |stakes| *stakes += 1);529530 Self::deposit_event(Event::Stake(staker_id, amount));531532 Ok(())533 }534535 536 537 538 #[pallet::call_index(2)]539 #[pallet::weight(<T as Config>::WeightInfo::unstake_all())]540 pub fn unstake_all(staker: OriginFor<T>) -> DispatchResult {541 let staker_id = ensure_signed(staker)?;542543 Self::unstake_all_internal(staker_id)544 }545546 547 548 549 550 551 552 553 554 #[pallet::call_index(8)]555 #[pallet::weight(<T as Config>::WeightInfo::unstake_partial())]556 pub fn unstake_partial(staker: OriginFor<T>, amount: BalanceOf<T>) -> DispatchResult {557 let staker_id = ensure_signed(staker)?;558559 Self::unstake_partial_internal(staker_id, amount)560 }561562 563 564 565 566 567 568 569 570 571 #[pallet::call_index(3)]572 #[pallet::weight(<T as Config>::WeightInfo::sponsor_collection())]573 pub fn sponsor_collection(574 admin: OriginFor<T>,575 collection_id: CollectionId,576 ) -> DispatchResult {577 let admin_id = ensure_signed(admin)?;578 ensure!(579 admin_id == Admin::<T>::get().ok_or(Error::<T>::AdminNotSet)?,580 Error::<T>::NoPermission581 );582583 T::CollectionHandler::set_sponsor(Self::account_id(), collection_id)584 }585586 587 588 589 590 591 592 593 594 595 596 597 #[pallet::call_index(4)]598 #[pallet::weight(<T as Config>::WeightInfo::stop_sponsoring_collection())]599 pub fn stop_sponsoring_collection(600 admin: OriginFor<T>,601 collection_id: CollectionId,602 ) -> DispatchResult {603 let admin_id = ensure_signed(admin)?;604605 ensure!(606 admin_id == Admin::<T>::get().ok_or(Error::<T>::AdminNotSet)?,607 Error::<T>::NoPermission608 );609610 ensure!(611 T::CollectionHandler::sponsor(collection_id)?.ok_or(<Error<T>>::SponsorNotSet)?612 == Self::account_id(),613 <Error<T>>::NoPermission614 );615 T::CollectionHandler::remove_collection_sponsor(collection_id)616 }617618 619 620 621 622 623 624 625 626 627 #[pallet::call_index(5)]628 #[pallet::weight(<T as Config>::WeightInfo::sponsor_contract())]629 pub fn sponsor_contract(admin: OriginFor<T>, contract_id: H160) -> DispatchResult {630 let admin_id = ensure_signed(admin)?;631632 ensure!(633 admin_id == Admin::<T>::get().ok_or(Error::<T>::AdminNotSet)?,634 Error::<T>::NoPermission635 );636637 T::ContractHandler::set_sponsor(638 T::CrossAccountId::from_sub(Self::account_id()),639 contract_id,640 )641 }642643 644 645 646 647 648 649 650 651 652 653 654 #[pallet::call_index(6)]655 #[pallet::weight(<T as Config>::WeightInfo::stop_sponsoring_contract())]656 pub fn stop_sponsoring_contract(admin: OriginFor<T>, contract_id: H160) -> DispatchResult {657 let admin_id = ensure_signed(admin)?;658659 ensure!(660 admin_id == Admin::<T>::get().ok_or(Error::<T>::AdminNotSet)?,661 Error::<T>::NoPermission662 );663664 ensure!(665 T::ContractHandler::sponsor(contract_id)?666 .ok_or(<Error<T>>::SponsorNotSet)?667 .as_sub() == &Self::account_id(),668 <Error<T>>::NoPermission669 );670 T::ContractHandler::remove_contract_sponsor(contract_id)671 }672673 674 675 676 677 678 679 680 681 682 683 684 685 #[pallet::call_index(7)]686 #[pallet::weight(<T as Config>::WeightInfo::payout_stakers(stakers_number.unwrap_or(DEFAULT_NUMBER_PAYOUTS) as u32))]687 pub fn payout_stakers(admin: OriginFor<T>, stakers_number: Option<u8>) -> DispatchResult {688 let admin_id = ensure_signed(admin)?;689690 ensure!(691 admin_id == Admin::<T>::get().ok_or(Error::<T>::AdminNotSet)?,692 Error::<T>::NoPermission693 );694 let config = <PalletConfiguration<T>>::get();695696 let mut stakers_number = stakers_number.unwrap_or(DEFAULT_NUMBER_PAYOUTS);697698 ensure!(699 stakers_number <= config.max_stakers_per_calculation && stakers_number != 0,700 Error::<T>::NoPermission701 );702703 704 705 let current_recalc_block = Self::get_current_recalc_block(706 T::RelayBlockNumberProvider::current_block_number(),707 &config,708 );709710 711 712 let next_recalc_block = current_recalc_block + config.recalculation_interval;713714 let mut storage_iterator = Self::get_next_calculated_key()715 .map_or(Staked::<T>::iter(), |key| Staked::<T>::iter_from(key));716717 PreviousCalculatedRecord::<T>::set(None);718719 {720 721 let last_id = RefCell::new(None);722 723 let mut last_staked_calculated_block = Default::default();724 725 let income_acc = RefCell::new(BalanceOf::<T>::default());726 727 let amount_acc = RefCell::new(BalanceOf::<T>::default());728729 730 731 732 733 734 735 736 let flush_stake = || -> DispatchResult {737 if let Some(last_id) = &*last_id.borrow() {738 if !income_acc.borrow().is_zero() {739 <<T as Config>::Currency as Mutate<T::AccountId>>::transfer(740 &T::TreasuryAccountId::get(),741 last_id,742 *income_acc.borrow(),743 frame_support::traits::tokens::Preservation::Protect,744 )?;745746 Self::add_freeze_balance(last_id, *income_acc.borrow())?;747 <TotalStaked<T>>::try_mutate(|staked| -> DispatchResult {748 *staked = staked749 .checked_add(&*income_acc.borrow())750 .ok_or(ArithmeticError::Overflow)?;751 Ok(())752 })?;753754 Self::deposit_event(Event::StakingRecalculation(755 last_id.clone(),756 *amount_acc.borrow(),757 *income_acc.borrow(),758 ));759 }760761 *income_acc.borrow_mut() = BalanceOf::<T>::default();762 *amount_acc.borrow_mut() = BalanceOf::<T>::default();763 }764 Ok(())765 };766767 768 769 770 771 772 773 while let Some((774 (current_id, staked_block),775 (amount, next_recalc_block_for_stake),776 )) = storage_iterator.next()777 {778 779 780 781 if last_id.borrow().as_ref() != Some(¤t_id) {782 if stakers_number > 0 {783 flush_stake()?;784 *last_id.borrow_mut() = Some(current_id.clone());785 stakers_number -= 1;786 }787 788 else {789 if let Some(staker) = &*last_id.borrow() {790 791 PreviousCalculatedRecord::<T>::set(Some((792 staker.clone(),793 last_staked_calculated_block,794 )));795 }796 break;797 };798 };799800 801 if current_recalc_block >= next_recalc_block_for_stake {802 *amount_acc.borrow_mut() += amount;803 Self::recalculate_and_insert_stake(804 ¤t_id,805 staked_block,806 next_recalc_block,807 amount,808 ((current_recalc_block - next_recalc_block_for_stake)809 / config.recalculation_interval)810 .into() + 1,811 &mut *income_acc.borrow_mut(),812 );813 }814 last_staked_calculated_block = staked_block;815 }816 flush_stake()?;817 }818819 Ok(())820 }821822 823 824 825 826 827 828 #[pallet::call_index(9)]829 #[pallet::weight(T::DbWeight::get().reads_writes(2, 2) * stakers.len() as u64)]830 pub fn upgrade_accounts(831 origin: OriginFor<T>,832 stakers: Vec<T::AccountId>,833 ) -> DispatchResult {834 ensure_signed(origin)?;835836 stakers.into_iter().try_for_each(|s| -> Result<_, DispatchError> {837 if let Some(lock) = Self::get_locked_balance(&s) {838 839 if let Some(_) = Self::get_frozen_balance(&s) {840 return Err(Error::<T>::InconsistencyState.into())841 }842 843 <<T as Config>::Currency as LockableCurrency<T::AccountId>>::remove_lock(844 LOCK_IDENTIFIER,845 &s,846 );847848 Self::set_freeze_unchecked(&s, lock.amount);849 Ok(())850 } else {851 Ok(())852 }853 })?;854 855 Ok(())856 }857 }858}859860impl<T: Config> Pallet<T> {861 862 863 864 865 pub fn account_id() -> T::AccountId {866 T::PalletId::get().into_account_truncating()867 }868869 870 871 872 873 fn unstake_partial_internal(874 staker_id: T::AccountId,875 unstaked_balance: BalanceOf<T>,876 ) -> DispatchResult {877 if unstaked_balance == Default::default() {878 return Ok(());879 }880881 let config = <PalletConfiguration<T>>::get();882883 884 let unpending_block = <frame_system::Pallet<T>>::block_number() + config.pending_interval;885886 let mut pendings = <PendingUnstake<T>>::get(unpending_block);887888 889 ensure!(!pendings.is_full(), Error::<T>::PendingForBlockOverflow);890891 let mut stakes = Staked::<T>::iter_prefix((&staker_id,)).collect::<Vec<_>>();892893 let total_staked = stakes894 .iter()895 .fold(<BalanceOf<T>>::default(), |acc, (_, (balance, _))| {896 acc + *balance897 });898899 ensure!(900 unstaked_balance <= total_staked,901 <Error<T>>::InsufficientStakedBalance902 );903904 <TotalStaked<T>>::set(905 <TotalStaked<T>>::get()906 .checked_sub(&unstaked_balance)907 .ok_or(ArithmeticError::Underflow)?,908 );909910 stakes.sort_by_key(|(block, _)| *block);911912 let mut acc_amount = unstaked_balance;913 let mut will_deleted_stakes_count = 0u8;914915 let changed_stakes = stakes916 .into_iter()917 .map_while(|(block, (balance_per_block, _))| {918 if acc_amount == <BalanceOf<T>>::default() {919 return None;920 }921 if acc_amount < balance_per_block {922 let res = (block, balance_per_block - acc_amount);923 acc_amount = <BalanceOf<T>>::default();924 return Some(res);925 } else {926 acc_amount -= balance_per_block;927 will_deleted_stakes_count += 1;928 return Some((block, <BalanceOf<T>>::default()));929 }930 })931 .collect::<Vec<_>>();932933 pendings934 .try_push((staker_id.clone(), unstaked_balance))935 .map_err(|_| Error::<T>::PendingForBlockOverflow)?;936937 StakesPerAccount::<T>::try_mutate(&staker_id, |stakes| -> DispatchResult {938 *stakes = stakes939 .checked_sub(will_deleted_stakes_count)940 .ok_or(ArithmeticError::Underflow)?;941 Ok(())942 })?;943944 changed_stakes945 .into_iter()946 .for_each(|(staked_block, current_stake_state)| {947 if current_stake_state == Default::default() {948 <Staked<T>>::remove((&staker_id, staked_block));949 } else {950 <Staked<T>>::mutate((&staker_id, staked_block), |(old_stake_state, _)| {951 *old_stake_state = current_stake_state952 });953 }954 });955956 <PendingUnstake<T>>::insert(unpending_block, pendings);957958 Self::deposit_event(Event::Unstake(staker_id, unstaked_balance));959960 Ok(())961 }962963 964 965 966 967 968 969 970 971 972 973 974975 976 977 978 979 fn add_freeze_balance(staker: &T::AccountId, amount: BalanceOf<T>) -> DispatchResult {980 Self::get_frozen_balance(staker)981 .unwrap_or_default()982 .checked_add(&amount)983 .map(|freeze| Self::set_freeze_unchecked(staker, freeze))984 .ok_or(ArithmeticError::Overflow.into())985 }986987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 10061007 1008 1009 1010 1011 fn set_freeze_unchecked(staker: &T::AccountId, amount: BalanceOf<T>) {1012 if amount.is_zero() {1013 <<T as Config>::Currency as MutateFreeze<T::AccountId>>::thaw(1014 &T::FreezeIdentifier::get(),1015 &staker,1016 );1017 } else {1018 <<T as Config>::Currency as MutateFreeze<T::AccountId>>::set_freeze(1019 &T::FreezeIdentifier::get(),1020 staker,1021 amount,1022 );1023 }1024 }10251026 1027 1028 1029 pub fn get_locked_balance(1030 staker: impl EncodeLike<T::AccountId>,1031 ) -> Option<BalanceLock<BalanceOf<T>>> {1032 <<T as Config>::Currency as ExtendedLockableCurrency<T::AccountId>>::locks(staker)1033 .into_iter()1034 .find(|l| l.id == LOCK_IDENTIFIER)1035 }10361037 1038 1039 1040 pub fn get_frozen_balance(staker: &T::AccountId) -> Option<BalanceOf<T>> {1041 let res = <<T as Config>::Currency as InspectFreeze<T::AccountId>>::balance_frozen(1042 &T::FreezeIdentifier::get(),1043 staker,1044 );10451046 if res == Zero::zero() {1047 None1048 } else {1049 Some(res)1050 }1051 }10521053 1054 1055 1056 pub fn total_staked_by_id(staker: impl EncodeLike<T::AccountId>) -> Option<BalanceOf<T>> {1057 let staked = Staked::<T>::iter_prefix((staker,))1058 .fold(<BalanceOf<T>>::default(), |acc, (_, (amount, _))| {1059 acc + amount1060 });1061 if staked != <BalanceOf<T>>::default() {1062 Some(staked)1063 } else {1064 None1065 }1066 }10671068 1069 1070 1071 1072 pub fn total_staked_by_id_per_block(1073 staker: impl EncodeLike<T::AccountId>,1074 ) -> Option<Vec<(T::BlockNumber, BalanceOf<T>)>> {1075 let mut staked = Staked::<T>::iter_prefix((staker,))1076 .map(|(block, (amount, _))| (block, amount))1077 .collect::<Vec<_>>();1078 staked.sort_by_key(|(block, _)| *block);1079 if !staked.is_empty() {1080 Some(staked)1081 } else {1082 None1083 }1084 }10851086 1087 1088 1089 pub fn cross_id_total_staked(staker: Option<T::CrossAccountId>) -> Option<BalanceOf<T>> {1090 staker.map_or(Some(<TotalStaked<T>>::get()), |s| {1091 Self::total_staked_by_id(s.as_sub())1092 })1093 }10941095 1096 1097 1098 1099 pub fn cross_id_total_staked_per_block(1100 staker: T::CrossAccountId,1101 ) -> Vec<(T::BlockNumber, BalanceOf<T>)> {1102 Self::total_staked_by_id_per_block(staker.as_sub()).unwrap_or_default()1103 }11041105 fn recalculate_and_insert_stake(1106 staker: &T::AccountId,1107 staked_block: T::BlockNumber,1108 next_recalc_block: T::BlockNumber,1109 base: BalanceOf<T>,1110 iters: u32,1111 income_acc: &mut BalanceOf<T>,1112 ) {1113 let income = Self::calculate_income(base, iters);11141115 base.checked_add(&income).map(|res| {1116 <Staked<T>>::insert((staker, staked_block), (res, next_recalc_block));1117 *income_acc += income;1118 });1119 }11201121 fn calculate_income<I>(base: I, iters: u32) -> I1122 where1123 I: EncodeLike<BalanceOf<T>> + Balance,1124 {1125 let config = <PalletConfiguration<T>>::get();1126 let mut income = base;11271128 (0..iters).for_each(|_| income += config.interval_income * income);11291130 income - base1131 }11321133 1134 1135 fn get_current_recalc_block(1136 current_relay_block: T::BlockNumber,1137 config: &PalletConfiguration<T>,1138 ) -> T::BlockNumber {1139 (current_relay_block / config.recalculation_interval) * config.recalculation_interval1140 }11411142 fn get_next_calculated_key() -> Option<Vec<u8>> {1143 Self::get_next_calculated_record().map(|key| Staked::<T>::hashed_key_for(key))1144 }1145}11461147impl<T: Config> Pallet<T>1148where1149 <<T as Config>::Currency as Inspect<T::AccountId>>::Balance: Sum,1150{1151 1152 1153 1154 1155 1156 1157 1158 1159 pub fn cross_id_pending_unstake(staker: Option<T::CrossAccountId>) -> BalanceOf<T> {1160 staker.map_or(1161 PendingUnstake::<T>::iter_values()1162 .flat_map(|pendings| pendings.into_iter().map(|(_, amount)| amount))1163 .sum(),1164 |s| {1165 PendingUnstake::<T>::iter_values()1166 .flatten()1167 .filter_map(|(id, amount)| {1168 if id == *s.as_sub() {1169 Some(amount)1170 } else {1171 None1172 }1173 })1174 .sum()1175 },1176 )1177 }11781179 1180 1181 1182 1183 pub fn cross_id_pending_unstake_per_block(1184 staker: T::CrossAccountId,1185 ) -> Vec<(T::BlockNumber, BalanceOf<T>)> {1186 let mut unsorted_res = vec![];1187 PendingUnstake::<T>::iter().for_each(|(block, pendings)| {1188 pendings.into_iter().for_each(|(id, amount)| {1189 if id == *staker.as_sub() {1190 unsorted_res.push((block, amount));1191 };1192 })1193 });11941195 unsorted_res.sort_by_key(|(block, _)| *block);1196 unsorted_res1197 }11981199 fn unstake_all_internal(staker_id: T::AccountId) -> DispatchResult {1200 let config = <PalletConfiguration<T>>::get();12011202 1203 let block = <frame_system::Pallet<T>>::block_number() + config.pending_interval;12041205 let mut pendings = <PendingUnstake<T>>::get(block);12061207 1208 ensure!(!pendings.is_full(), Error::<T>::PendingForBlockOverflow);12091210 let total_staked: BalanceOf<T> = Staked::<T>::drain_prefix((&staker_id,))1211 .map(|(_, (amount, _))| amount)1212 .sum();12131214 if total_staked.is_zero() {1215 return Ok(());1216 }12171218 pendings1219 .try_push((staker_id.clone(), total_staked))1220 .map_err(|_| Error::<T>::PendingForBlockOverflow)?;12211222 <PendingUnstake<T>>::insert(block, pendings);12231224 TotalStaked::<T>::set(1225 TotalStaked::<T>::get()1226 .checked_sub(&total_staked)1227 .ok_or(ArithmeticError::Underflow)?,1228 );12291230 StakesPerAccount::<T>::remove(&staker_id);12311232 Self::deposit_event(Event::Unstake(staker_id, total_staked));12331234 Ok(())1235 }1236}