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, DispatchError,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::*;104 use 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<114 Self::AccountId,115 Balance = <<Self as Config>::Currency as Inspect<Self::AccountId>>::Balance,116 >;117118 119 type CollectionHandler: CollectionHandler<120 AccountId = Self::AccountId,121 CollectionId = CollectionId,122 >;123124 125 type ContractHandler: ContractHandler<AccountId = Self::CrossAccountId, ContractId = H160>;126127 128 type TreasuryAccountId: Get<Self::AccountId>;129130 131 #[pallet::constant]132 type PalletId: Get<PalletId>;133134 135 #[pallet::constant]136 type FreezeIdentifier: Get<137 <<Self as Config>::Currency as InspectFreeze<Self::AccountId>>::Id,138 >;139140 141 #[pallet::constant]142 type RecalculationInterval: Get<Self::BlockNumber>;143144 145 #[pallet::constant]146 type PendingInterval: Get<Self::BlockNumber>;147148 149 #[pallet::constant]150 type IntervalIncome: Get<Perbill>;151152 153 #[pallet::constant]154 type Nominal: Get<BalanceOf<Self>>;155156 157 type IsMaintenanceModeEnabled: Get<bool>;158159 160 type WeightInfo: WeightInfo;161162 163 type RelayBlockNumberProvider: BlockNumberProvider<BlockNumber = Self::BlockNumber>;164165 166 type RuntimeEvent: IsType<<Self as frame_system::Config>::RuntimeEvent> + From<Event<Self>>;167 }168169 #[pallet::pallet]170 pub struct Pallet<T>(_);171172 #[pallet::event]173 #[pallet::generate_deposit(pub(super) fn deposit_event)]174 pub enum Event<T: Config> {175 176 177 178 179 180 181 StakingRecalculation(182 183 T::AccountId,184 185 BalanceOf<T>,186 187 BalanceOf<T>,188 ),189190 191 192 193 194 195 Stake(T::AccountId, BalanceOf<T>),196197 198 199 200 201 202 Unstake(T::AccountId, BalanceOf<T>),203204 205 206 207 208 SetAdmin(T::AccountId),209 }210211 #[pallet::error]212 pub enum Error<T> {213 214 AdminNotSet,215 216 NoPermission,217 218 NotSufficientFunds,219 220 PendingForBlockOverflow,221 222 SponsorNotSet,223 224 InsufficientStakedBalance,225 226 InconsistencyState,227 }228229 230 #[pallet::storage]231 pub type TotalStaked<T: Config> = StorageValue<Value = BalanceOf<T>, QueryKind = ValueQuery>;232233 234 #[pallet::storage]235 pub type Admin<T: Config> = StorageValue<Value = T::AccountId, QueryKind = OptionQuery>;236237 238 239 240 241 242 243 #[pallet::storage]244 pub type Staked<T: Config> = StorageNMap<245 Key = (246 Key<Blake2_128Concat, T::AccountId>,247 Key<Twox64Concat, T::BlockNumber>,248 ),249 Value = (BalanceOf<T>, T::BlockNumber),250 QueryKind = ValueQuery,251 >;252253 254 255 256 257 #[pallet::storage]258 pub type StakesPerAccount<T: Config> =259 StorageMap<_, Blake2_128Concat, T::AccountId, u8, ValueQuery>;260261 262 263 264 265 #[pallet::storage]266 pub type PendingUnstake<T: Config> = StorageMap<267 _,268 Twox64Concat,269 T::BlockNumber,270 BoundedVec<(T::AccountId, BalanceOf<T>), ConstU32<PENDING_LIMIT_PER_BLOCK>>,271 ValueQuery,272 >;273274 275 276 #[pallet::storage]277 #[pallet::getter(fn get_next_calculated_record)]278 pub type PreviousCalculatedRecord<T: Config> =279 StorageValue<Value = (T::AccountId, T::BlockNumber), QueryKind = OptionQuery>;280281 282 283 284285 #[pallet::hooks]286 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {287 288 289 290 fn on_initialize(current_block_number: T::BlockNumber) -> Weight291 where292 <T as frame_system::Config>::BlockNumber: From<u32>,293 {294 if T::IsMaintenanceModeEnabled::get() {295 return T::DbWeight::get().reads_writes(1, 0);296 }297298 let block_pending = PendingUnstake::<T>::take(current_block_number);299 let counter = block_pending.len() as u32;300301 if !block_pending.is_empty() {302 block_pending.into_iter().for_each(|(staker, amount)| {303 Self::get_frozen_balance(&staker).map(|b| {304 let new_state = b.checked_sub(&amount).unwrap_or_default();305 Self::set_freeze_unchecked(&staker, new_state);306 });307 });308 }309310 <T as Config>::WeightInfo::on_initialize(counter)311 }312313 314 315 316 317 318 319 320321 322323 324 325 326 327 328 329 330 331 332 333 334335 336 337 338 339 340 341342 343 344 345 346 347 348 349350 351 352 353 354 355 356 357 358 359 360 361 362 363 364365 366 367368 369 370 371 372 373 374 375 376377 378 379 380 381 382 383 384385 386 387 388 389 390 391 392 393 394395 396 397398 399 400 401402 403 404 405406 407408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423424 425 426 427 428 429 430 431 432433 434 435 436 437 438 439 }440441 #[pallet::call]442 impl<T: Config> Pallet<T>443 where444 T::BlockNumber: From<u32> + Into<u32>,445 <<T as Config>::Currency as Inspect<T::AccountId>>::Balance: Sum + From<u128>,446 {447 448 449 450 451 452 453 454 455 456 #[pallet::call_index(0)]457 #[pallet::weight(<T as Config>::WeightInfo::set_admin_address())]458 pub fn set_admin_address(origin: OriginFor<T>, admin: T::CrossAccountId) -> DispatchResult {459 ensure_root(origin)?;460461 <Admin<T>>::set(Some(admin.as_sub().to_owned()));462463 Self::deposit_event(Event::SetAdmin(admin.as_sub().to_owned()));464465 Ok(())466 }467468 469 470 471 472 473 474 475 #[pallet::call_index(1)]476 #[pallet::weight(<T as Config>::WeightInfo::stake())]477 pub fn stake(staker: OriginFor<T>, amount: BalanceOf<T>) -> DispatchResult {478 let staker_id = ensure_signed(staker)?;479480 ensure!(481 StakesPerAccount::<T>::get(&staker_id) < 10,482 Error::<T>::NoPermission483 );484485 ensure!(486 amount >= <BalanceOf<T>>::from(100u128) * T::Nominal::get(),487 ArithmeticError::Underflow488 );489 let config = <PalletConfiguration<T>>::get();490491 let balance = <<T as Config>::Currency as Inspect<T::AccountId>>::balance(&staker_id);492493 494 ensure!(495 amount496 <= match Self::get_frozen_balance(&staker_id) {497 Some(frozen_by_pallet) => balance498 .checked_sub(&frozen_by_pallet)499 .ok_or(ArithmeticError::Underflow)?,500 None => balance,501 },502 ArithmeticError::Underflow503 );504505 Self::add_freeze_balance(&staker_id, amount)?;506507 let block_number = T::RelayBlockNumberProvider::current_block_number();508509 510 511 let recalculate_after_interval: T::BlockNumber =512 if block_number % config.recalculation_interval == 0u32.into() {513 1u32.into()514 } else {515 2u32.into()516 };517518 519 520 let recalc_block = (block_number / config.recalculation_interval521 + recalculate_after_interval)522 * config.recalculation_interval;523524 <Staked<T>>::insert((&staker_id, block_number), {525 let mut balance_and_recalc_block = <Staked<T>>::get((&staker_id, block_number));526 balance_and_recalc_block.0 = balance_and_recalc_block527 .0528 .checked_add(&amount)529 .ok_or(ArithmeticError::Overflow)?;530 balance_and_recalc_block.1 = recalc_block;531 balance_and_recalc_block532 });533534 <TotalStaked<T>>::set(535 <TotalStaked<T>>::get()536 .checked_add(&amount)537 .ok_or(ArithmeticError::Overflow)?,538 );539540 StakesPerAccount::<T>::mutate(&staker_id, |stakes| *stakes += 1);541542 Self::deposit_event(Event::Stake(staker_id, amount));543544 Ok(())545 }546547 548 549 550 #[pallet::call_index(2)]551 #[pallet::weight(<T as Config>::WeightInfo::unstake_all())]552 pub fn unstake_all(staker: OriginFor<T>) -> DispatchResult {553 let staker_id = ensure_signed(staker)?;554555 Self::unstake_all_internal(staker_id)556 }557558 559 560 561 562 563 564 565 566 #[pallet::call_index(8)]567 #[pallet::weight(<T as Config>::WeightInfo::unstake_partial())]568 pub fn unstake_partial(staker: OriginFor<T>, amount: BalanceOf<T>) -> DispatchResult {569 let staker_id = ensure_signed(staker)?;570571 Self::unstake_partial_internal(staker_id, amount)572 }573574 575 576 577 578 579 580 581 582 583 #[pallet::call_index(3)]584 #[pallet::weight(<T as Config>::WeightInfo::sponsor_collection())]585 pub fn sponsor_collection(586 admin: OriginFor<T>,587 collection_id: CollectionId,588 ) -> DispatchResult {589 let admin_id = ensure_signed(admin)?;590 ensure!(591 admin_id == Admin::<T>::get().ok_or(Error::<T>::AdminNotSet)?,592 Error::<T>::NoPermission593 );594595 T::CollectionHandler::set_sponsor(Self::account_id(), collection_id)596 }597598 599 600 601 602 603 604 605 606 607 608 609 #[pallet::call_index(4)]610 #[pallet::weight(<T as Config>::WeightInfo::stop_sponsoring_collection())]611 pub fn stop_sponsoring_collection(612 admin: OriginFor<T>,613 collection_id: CollectionId,614 ) -> DispatchResult {615 let admin_id = ensure_signed(admin)?;616617 ensure!(618 admin_id == Admin::<T>::get().ok_or(Error::<T>::AdminNotSet)?,619 Error::<T>::NoPermission620 );621622 ensure!(623 T::CollectionHandler::sponsor(collection_id)?.ok_or(<Error<T>>::SponsorNotSet)?624 == Self::account_id(),625 <Error<T>>::NoPermission626 );627 T::CollectionHandler::remove_collection_sponsor(collection_id)628 }629630 631 632 633 634 635 636 637 638 639 #[pallet::call_index(5)]640 #[pallet::weight(<T as Config>::WeightInfo::sponsor_contract())]641 pub fn sponsor_contract(admin: OriginFor<T>, contract_id: H160) -> DispatchResult {642 let admin_id = ensure_signed(admin)?;643644 ensure!(645 admin_id == Admin::<T>::get().ok_or(Error::<T>::AdminNotSet)?,646 Error::<T>::NoPermission647 );648649 T::ContractHandler::set_sponsor(650 T::CrossAccountId::from_sub(Self::account_id()),651 contract_id,652 )653 }654655 656 657 658 659 660 661 662 663 664 665 666 #[pallet::call_index(6)]667 #[pallet::weight(<T as Config>::WeightInfo::stop_sponsoring_contract())]668 pub fn stop_sponsoring_contract(admin: OriginFor<T>, contract_id: H160) -> DispatchResult {669 let admin_id = ensure_signed(admin)?;670671 ensure!(672 admin_id == Admin::<T>::get().ok_or(Error::<T>::AdminNotSet)?,673 Error::<T>::NoPermission674 );675676 ensure!(677 T::ContractHandler::sponsor(contract_id)?678 .ok_or(<Error<T>>::SponsorNotSet)?679 .as_sub() == &Self::account_id(),680 <Error<T>>::NoPermission681 );682 T::ContractHandler::remove_contract_sponsor(contract_id)683 }684685 686 687 688 689 690 691 692 693 694 695 696 697 #[pallet::call_index(7)]698 #[pallet::weight(<T as Config>::WeightInfo::payout_stakers(stakers_number.unwrap_or(DEFAULT_NUMBER_PAYOUTS) as u32))]699 pub fn payout_stakers(admin: OriginFor<T>, stakers_number: Option<u8>) -> DispatchResult {700 let admin_id = ensure_signed(admin)?;701702 ensure!(703 admin_id == Admin::<T>::get().ok_or(Error::<T>::AdminNotSet)?,704 Error::<T>::NoPermission705 );706 let config = <PalletConfiguration<T>>::get();707708 let mut stakers_number = stakers_number.unwrap_or(DEFAULT_NUMBER_PAYOUTS);709710 ensure!(711 stakers_number <= config.max_stakers_per_calculation && stakers_number != 0,712 Error::<T>::NoPermission713 );714715 716 717 let current_recalc_block = Self::get_current_recalc_block(718 T::RelayBlockNumberProvider::current_block_number(),719 &config,720 );721722 723 724 let next_recalc_block = current_recalc_block + config.recalculation_interval;725726 let mut storage_iterator = Self::get_next_calculated_key()727 .map_or(Staked::<T>::iter(), |key| Staked::<T>::iter_from(key));728729 PreviousCalculatedRecord::<T>::set(None);730731 {732 733 let last_id = RefCell::new(None);734 735 let mut last_staked_calculated_block = Default::default();736 737 let income_acc = RefCell::new(BalanceOf::<T>::default());738 739 let amount_acc = RefCell::new(BalanceOf::<T>::default());740741 742 743 744 745 746 747 748 let flush_stake = || -> DispatchResult {749 if let Some(last_id) = &*last_id.borrow() {750 if !income_acc.borrow().is_zero() {751 <<T as Config>::Currency as Mutate<T::AccountId>>::transfer(752 &T::TreasuryAccountId::get(),753 last_id,754 *income_acc.borrow(),755 frame_support::traits::tokens::Preservation::Protect,756 )?;757758 Self::add_freeze_balance(last_id, *income_acc.borrow())?;759 <TotalStaked<T>>::try_mutate(|staked| -> DispatchResult {760 *staked = staked761 .checked_add(&*income_acc.borrow())762 .ok_or(ArithmeticError::Overflow)?;763 Ok(())764 })?;765766 Self::deposit_event(Event::StakingRecalculation(767 last_id.clone(),768 *amount_acc.borrow(),769 *income_acc.borrow(),770 ));771 }772773 *income_acc.borrow_mut() = BalanceOf::<T>::default();774 *amount_acc.borrow_mut() = BalanceOf::<T>::default();775 }776 Ok(())777 };778779 780 781 782 783 784 785 while let Some((786 (current_id, staked_block),787 (amount, next_recalc_block_for_stake),788 )) = storage_iterator.next()789 {790 791 792 793 if last_id.borrow().as_ref() != Some(¤t_id) {794 if stakers_number > 0 {795 flush_stake()?;796 *last_id.borrow_mut() = Some(current_id.clone());797 stakers_number -= 1;798 }799 800 else {801 if let Some(staker) = &*last_id.borrow() {802 803 PreviousCalculatedRecord::<T>::set(Some((804 staker.clone(),805 last_staked_calculated_block,806 )));807 }808 break;809 };810 };811812 813 if current_recalc_block >= next_recalc_block_for_stake {814 *amount_acc.borrow_mut() += amount;815 Self::recalculate_and_insert_stake(816 ¤t_id,817 staked_block,818 next_recalc_block,819 amount,820 ((current_recalc_block - next_recalc_block_for_stake)821 / config.recalculation_interval)822 .into() + 1,823 &mut *income_acc.borrow_mut(),824 );825 }826 last_staked_calculated_block = staked_block;827 }828 flush_stake()?;829 }830831 Ok(())832 }833834 835 836 837 838 839 840 841 842 843 844 #[pallet::call_index(9)]845 #[pallet::weight(T::DbWeight::get().reads_writes(2, 2) * stakers.len() as u64)]846 pub fn upgrade_accounts(847 origin: OriginFor<T>,848 stakers: Vec<T::AccountId>,849 ) -> DispatchResult {850 ensure_root(origin)?;851852 stakers853 .into_iter()854 .try_for_each(|s| -> Result<_, DispatchError> {855 if let Some(BalanceLock { amount, .. }) = Self::get_locked_balance(&s) {856 if Self::get_frozen_balance(&s).is_some() {857 return Err(Error::<T>::InconsistencyState.into());858 }859860 <<T as Config>::Currency as LockableCurrency<T::AccountId>>::remove_lock(861 LOCK_IDENTIFIER,862 &s,863 );864865 Self::set_freeze_with_result(&s, amount)?;866 Ok(())867 } else {868 Ok(())869 }870 })?;871872 Ok(())873 }874875 876 877 878 879 880 881 882 883 884 885 #[pallet::call_index(10)]886 #[pallet::weight(<T as Config>::WeightInfo::on_initialize(PENDING_LIMIT_PER_BLOCK*pending_blocks.len() as u32))]887 pub fn force_unstake(888 origin: OriginFor<T>,889 pending_blocks: Vec<T::BlockNumber>,890 ) -> DispatchResult {891 ensure_root(origin)?;892893 ensure!(894 pending_blocks895 .iter()896 .all(|b| *b < <frame_system::Pallet<T>>::block_number()),897 <Error<T>>::NoPermission898 );899900 let mut pendings =901 Vec::with_capacity(PENDING_LIMIT_PER_BLOCK as usize * pending_blocks.len());902 pending_blocks903 .into_iter()904 .for_each(|b| pendings.append(&mut PendingUnstake::<T>::take(b).into_inner()));905906 pendings.into_iter().for_each(|(staker, amount)| {907 Self::get_frozen_balance(&staker).map(|b| {908 let new_state = b.checked_sub(&amount).unwrap_or_default();909 Self::set_freeze_unchecked(&staker, new_state);910 });911 });912913 Ok(())914 }915 }916}917918impl<T: Config> Pallet<T> {919 920 921 922 923 pub fn account_id() -> T::AccountId {924 T::PalletId::get().into_account_truncating()925 }926927 928 929 930 931 fn unstake_partial_internal(932 staker_id: T::AccountId,933 unstaked_balance: BalanceOf<T>,934 ) -> DispatchResult {935 if unstaked_balance == Default::default() {936 return Ok(());937 }938939 let config = <PalletConfiguration<T>>::get();940941 942 let unpending_block = <frame_system::Pallet<T>>::block_number() + config.pending_interval;943944 let mut pendings = <PendingUnstake<T>>::get(unpending_block);945946 947 ensure!(!pendings.is_full(), Error::<T>::PendingForBlockOverflow);948949 let mut stakes = Staked::<T>::iter_prefix((&staker_id,)).collect::<Vec<_>>();950951 let total_staked = stakes952 .iter()953 .fold(<BalanceOf<T>>::default(), |acc, (_, (balance, _))| {954 acc + *balance955 });956957 ensure!(958 unstaked_balance <= total_staked,959 <Error<T>>::InsufficientStakedBalance960 );961962 <TotalStaked<T>>::set(963 <TotalStaked<T>>::get()964 .checked_sub(&unstaked_balance)965 .ok_or(ArithmeticError::Underflow)?,966 );967968 stakes.sort_by_key(|(block, _)| *block);969970 let mut acc_amount = unstaked_balance;971 let mut will_deleted_stakes_count = 0u8;972973 let changed_stakes = stakes974 .into_iter()975 .map_while(|(block, (balance_per_block, _))| {976 if acc_amount == <BalanceOf<T>>::default() {977 return None;978 }979 if acc_amount < balance_per_block {980 let res = (block, balance_per_block - acc_amount);981 acc_amount = <BalanceOf<T>>::default();982 return Some(res);983 } else {984 acc_amount -= balance_per_block;985 will_deleted_stakes_count += 1;986 return Some((block, <BalanceOf<T>>::default()));987 }988 })989 .collect::<Vec<_>>();990991 pendings992 .try_push((staker_id.clone(), unstaked_balance))993 .map_err(|_| Error::<T>::PendingForBlockOverflow)?;994995 StakesPerAccount::<T>::try_mutate(&staker_id, |stakes| -> DispatchResult {996 *stakes = stakes997 .checked_sub(will_deleted_stakes_count)998 .ok_or(ArithmeticError::Underflow)?;999 Ok(())1000 })?;10011002 changed_stakes1003 .into_iter()1004 .for_each(|(staked_block, current_stake_state)| {1005 if current_stake_state == Default::default() {1006 <Staked<T>>::remove((&staker_id, staked_block));1007 } else {1008 <Staked<T>>::mutate((&staker_id, staked_block), |(old_stake_state, _)| {1009 *old_stake_state = current_stake_state1010 });1011 }1012 });10131014 <PendingUnstake<T>>::insert(unpending_block, pendings);10151016 Self::deposit_event(Event::Unstake(staker_id, unstaked_balance));10171018 Ok(())1019 }10201021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 10321033 1034 1035 1036 1037 fn add_freeze_balance(staker: &T::AccountId, amount: BalanceOf<T>) -> DispatchResult {1038 Self::get_frozen_balance(staker)1039 .unwrap_or_default()1040 .checked_add(&amount)1041 .map(|freeze| Self::set_freeze_with_result(staker, freeze))1042 .ok_or::<DispatchError>(ArithmeticError::Overflow.into())?1043 }10441045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 10641065 1066 1067 1068 1069 fn set_freeze_unchecked(staker: &T::AccountId, amount: BalanceOf<T>) {1070 Self::set_freeze_with_result(staker, amount);1071 }10721073 1074 1075 1076 1077 fn set_freeze_with_result(staker: &T::AccountId, amount: BalanceOf<T>) -> DispatchResult {1078 if amount.is_zero() {1079 <<T as Config>::Currency as MutateFreeze<T::AccountId>>::thaw(1080 &T::FreezeIdentifier::get(),1081 &staker,1082 )1083 } else {1084 <<T as Config>::Currency as MutateFreeze<T::AccountId>>::set_freeze(1085 &T::FreezeIdentifier::get(),1086 staker,1087 amount,1088 )1089 }1090 }10911092 1093 1094 1095 pub fn get_locked_balance(1096 staker: impl EncodeLike<T::AccountId>,1097 ) -> Option<BalanceLock<BalanceOf<T>>> {1098 <<T as Config>::Currency as ExtendedLockableCurrency<T::AccountId>>::locks(staker)1099 .into_iter()1100 .find(|l| l.id == LOCK_IDENTIFIER)1101 }11021103 1104 1105 1106 pub fn get_frozen_balance(staker: &T::AccountId) -> Option<BalanceOf<T>> {1107 let res = <<T as Config>::Currency as InspectFreeze<T::AccountId>>::balance_frozen(1108 &T::FreezeIdentifier::get(),1109 staker,1110 );11111112 if res == Zero::zero() {1113 None1114 } else {1115 Some(res)1116 }1117 }11181119 1120 1121 1122 pub fn total_staked_by_id(staker: impl EncodeLike<T::AccountId>) -> Option<BalanceOf<T>> {1123 let staked = Staked::<T>::iter_prefix((staker,))1124 .fold(<BalanceOf<T>>::default(), |acc, (_, (amount, _))| {1125 acc + amount1126 });1127 if staked != <BalanceOf<T>>::default() {1128 Some(staked)1129 } else {1130 None1131 }1132 }11331134 1135 1136 1137 1138 pub fn total_staked_by_id_per_block(1139 staker: impl EncodeLike<T::AccountId>,1140 ) -> Option<Vec<(T::BlockNumber, BalanceOf<T>)>> {1141 let mut staked = Staked::<T>::iter_prefix((staker,))1142 .map(|(block, (amount, _))| (block, amount))1143 .collect::<Vec<_>>();1144 staked.sort_by_key(|(block, _)| *block);1145 if !staked.is_empty() {1146 Some(staked)1147 } else {1148 None1149 }1150 }11511152 1153 1154 1155 pub fn cross_id_total_staked(staker: Option<T::CrossAccountId>) -> Option<BalanceOf<T>> {1156 staker.map_or(Some(<TotalStaked<T>>::get()), |s| {1157 Self::total_staked_by_id(s.as_sub())1158 })1159 }11601161 1162 1163 1164 1165 pub fn cross_id_total_staked_per_block(1166 staker: T::CrossAccountId,1167 ) -> Vec<(T::BlockNumber, BalanceOf<T>)> {1168 Self::total_staked_by_id_per_block(staker.as_sub()).unwrap_or_default()1169 }11701171 fn recalculate_and_insert_stake(1172 staker: &T::AccountId,1173 staked_block: T::BlockNumber,1174 next_recalc_block: T::BlockNumber,1175 base: BalanceOf<T>,1176 iters: u32,1177 income_acc: &mut BalanceOf<T>,1178 ) {1179 let income = Self::calculate_income(base, iters);11801181 base.checked_add(&income).map(|res| {1182 <Staked<T>>::insert((staker, staked_block), (res, next_recalc_block));1183 *income_acc += income;1184 });1185 }11861187 fn calculate_income<I>(base: I, iters: u32) -> I1188 where1189 I: EncodeLike<BalanceOf<T>> + Balance,1190 {1191 let config = <PalletConfiguration<T>>::get();1192 let mut income = base;11931194 (0..iters).for_each(|_| income += config.interval_income * income);11951196 income - base1197 }11981199 1200 1201 fn get_current_recalc_block(1202 current_relay_block: T::BlockNumber,1203 config: &PalletConfiguration<T>,1204 ) -> T::BlockNumber {1205 (current_relay_block / config.recalculation_interval) * config.recalculation_interval1206 }12071208 fn get_next_calculated_key() -> Option<Vec<u8>> {1209 Self::get_next_calculated_record().map(Staked::<T>::hashed_key_for)1210 }1211}12121213impl<T: Config> Pallet<T>1214where1215 <<T as Config>::Currency as Inspect<T::AccountId>>::Balance: Sum,1216{1217 1218 1219 1220 1221 1222 1223 1224 1225 pub fn cross_id_pending_unstake(staker: Option<T::CrossAccountId>) -> BalanceOf<T> {1226 staker.map_or(1227 PendingUnstake::<T>::iter_values()1228 .flat_map(|pendings| pendings.into_iter().map(|(_, amount)| amount))1229 .sum(),1230 |s| {1231 PendingUnstake::<T>::iter_values()1232 .flatten()1233 .filter_map(|(id, amount)| {1234 if id == *s.as_sub() {1235 Some(amount)1236 } else {1237 None1238 }1239 })1240 .sum()1241 },1242 )1243 }12441245 1246 1247 1248 1249 pub fn cross_id_pending_unstake_per_block(1250 staker: T::CrossAccountId,1251 ) -> Vec<(T::BlockNumber, BalanceOf<T>)> {1252 let mut unsorted_res = vec![];1253 PendingUnstake::<T>::iter().for_each(|(block, pendings)| {1254 pendings.into_iter().for_each(|(id, amount)| {1255 if id == *staker.as_sub() {1256 unsorted_res.push((block, amount));1257 };1258 })1259 });12601261 unsorted_res.sort_by_key(|(block, _)| *block);1262 unsorted_res1263 }12641265 fn unstake_all_internal(staker_id: T::AccountId) -> DispatchResult {1266 let config = <PalletConfiguration<T>>::get();12671268 1269 let block = <frame_system::Pallet<T>>::block_number() + config.pending_interval;12701271 let mut pendings = <PendingUnstake<T>>::get(block);12721273 1274 ensure!(!pendings.is_full(), Error::<T>::PendingForBlockOverflow);12751276 let total_staked: BalanceOf<T> = Staked::<T>::drain_prefix((&staker_id,))1277 .map(|(_, (amount, _))| amount)1278 .sum();12791280 if total_staked.is_zero() {1281 return Ok(());1282 }12831284 pendings1285 .try_push((staker_id.clone(), total_staked))1286 .map_err(|_| Error::<T>::PendingForBlockOverflow)?;12871288 <PendingUnstake<T>>::insert(block, pendings);12891290 TotalStaked::<T>::set(1291 TotalStaked::<T>::get()1292 .checked_sub(&total_staked)1293 .ok_or(ArithmeticError::Underflow)?,1294 );12951296 StakesPerAccount::<T>::remove(&staker_id);12971298 Self::deposit_event(Event::Unstake(staker_id, total_staked));12991300 Ok(())1301 }1302}