123456789101112131415161718192021222324252627282930313233343536373839404142434445464748#![cfg_attr(not(feature = "std"), no_std)]4950#[cfg(feature = "runtime-benchmarks")]51mod benchmarking;5253pub mod types;54pub mod weights;5556use frame_support::{57 dispatch::DispatchResult,58 ensure,59 pallet_prelude::*,60 storage::Key,61 traits::{62 fungible::{Inspect, InspectFreeze, Mutate, MutateFreeze},63 tokens::Balance,64 Get,65 },66 weights::Weight,67 Blake2_128Concat, BoundedVec, PalletId, Twox64Concat,68};69use frame_system::pallet_prelude::*;70pub use pallet::*;71use pallet_evm::account::CrossAccountId;72use parity_scale_codec::EncodeLike;73use sp_core::H160;74use sp_runtime::{75 traits::{AccountIdConversion, BlockNumberProvider, CheckedAdd, CheckedSub, Zero},76 ArithmeticError, DispatchError, Perbill,77};78use sp_std::{borrow::ToOwned, cell::RefCell, iter::Sum, vec, vec::Vec};79pub use types::*;80use up_data_structs::CollectionId;81use weights::WeightInfo;8283const PENDING_LIMIT_PER_BLOCK: u32 = 3;8485type BalanceOf<T> =86 <<T as Config>::Currency as Inspect<<T as frame_system::Config>::AccountId>>::Balance;8788#[frame_support::pallet]89pub mod pallet {9091 use super::*;9293 #[pallet::config]94 pub trait Config:95 frame_system::Config + pallet_evm::Config + pallet_configuration::Config96 {97 98 type Currency: MutateFreeze<Self::AccountId> + Mutate<Self::AccountId>;99100 101 type CollectionHandler: CollectionHandler<102 AccountId = Self::AccountId,103 CollectionId = CollectionId,104 >;105106 107 type ContractHandler: ContractHandler<AccountId = Self::CrossAccountId, ContractId = H160>;108109 110 type TreasuryAccountId: Get<Self::AccountId>;111112 113 #[pallet::constant]114 type PalletId: Get<PalletId>;115116 117 #[pallet::constant]118 type FreezeIdentifier: Get<119 <<Self as Config>::Currency as InspectFreeze<Self::AccountId>>::Id,120 >;121122 123 #[pallet::constant]124 type RecalculationInterval: Get<BlockNumberFor<Self>>;125126 127 #[pallet::constant]128 type PendingInterval: Get<BlockNumberFor<Self>>;129130 131 #[pallet::constant]132 type IntervalIncome: Get<Perbill>;133134 135 #[pallet::constant]136 type Nominal: Get<BalanceOf<Self>>;137138 139 type IsMaintenanceModeEnabled: Get<bool>;140141 142 type WeightInfo: WeightInfo;143144 145 type RelayBlockNumberProvider: BlockNumberProvider<BlockNumber = BlockNumberFor<Self>>;146147 148 type RuntimeEvent: IsType<<Self as frame_system::Config>::RuntimeEvent> + From<Event<Self>>;149 }150151 #[pallet::pallet]152 pub struct Pallet<T>(_);153154 #[pallet::event]155 #[pallet::generate_deposit(pub(super) fn deposit_event)]156 pub enum Event<T: Config> {157 158 159 160 161 162 163 StakingRecalculation(164 165 T::AccountId,166 167 BalanceOf<T>,168 169 BalanceOf<T>,170 ),171172 173 174 175 176 177 Stake(T::AccountId, BalanceOf<T>),178179 180 181 182 183 184 Unstake(T::AccountId, BalanceOf<T>),185186 187 188 189 190 SetAdmin(T::AccountId),191 }192193 #[pallet::error]194 pub enum Error<T> {195 196 AdminNotSet,197 198 NoPermission,199 200 NotSufficientFunds,201 202 PendingForBlockOverflow,203 204 SponsorNotSet,205 206 InsufficientStakedBalance,207 208 InconsistencyState,209 }210211 212 #[pallet::storage]213 pub type TotalStaked<T: Config> = StorageValue<Value = BalanceOf<T>, QueryKind = ValueQuery>;214215 216 #[pallet::storage]217 pub type Admin<T: Config> = StorageValue<Value = T::AccountId, QueryKind = OptionQuery>;218219 220 221 222 223 224 225 #[pallet::storage]226 pub type Staked<T: Config> = StorageNMap<227 Key = (228 Key<Blake2_128Concat, T::AccountId>,229 Key<Twox64Concat, BlockNumberFor<T>>,230 ),231 Value = (BalanceOf<T>, BlockNumberFor<T>),232 QueryKind = ValueQuery,233 >;234235 236 237 238 239 #[pallet::storage]240 pub type StakesPerAccount<T: Config> =241 StorageMap<_, Blake2_128Concat, T::AccountId, u8, ValueQuery>;242243 244 245 246 247 #[pallet::storage]248 pub type PendingUnstake<T: Config> = StorageMap<249 _,250 Twox64Concat,251 BlockNumberFor<T>,252 BoundedVec<(T::AccountId, BalanceOf<T>), ConstU32<PENDING_LIMIT_PER_BLOCK>>,253 ValueQuery,254 >;255256 257 258 #[pallet::storage]259 #[pallet::getter(fn get_next_calculated_record)]260 pub type PreviousCalculatedRecord<T: Config> =261 StorageValue<Value = (T::AccountId, BlockNumberFor<T>), QueryKind = OptionQuery>;262263 #[pallet::hooks]264 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {265 266 267 268 fn on_initialize(current_block_number: BlockNumberFor<T>) -> Weight269 where270 BlockNumberFor<T>: From<u32>,271 {272 if T::IsMaintenanceModeEnabled::get() {273 return T::DbWeight::get().reads_writes(1, 0);274 }275276 let block_pending = PendingUnstake::<T>::take(current_block_number);277 let counter = block_pending.len() as u32;278279 if !block_pending.is_empty() {280 block_pending.into_iter().for_each(|(staker, amount)| {281 if let Some(b) = Self::get_frozen_balance(&staker) {282 let new_state = b.checked_sub(&amount).unwrap_or_default();283284 285 286 287 288 289 Self::set_freeze_unchecked(&staker, new_state);290 };291 });292 }293294 <T as Config>::WeightInfo::on_initialize(counter)295 }296 }297298 #[pallet::call]299 impl<T: Config> Pallet<T>300 where301 BlockNumberFor<T>: From<u32> + Into<u32>,302 <<T as Config>::Currency as Inspect<T::AccountId>>::Balance: Sum + From<u128>,303 {304 305 306 307 308 309 310 311 312 313 #[pallet::call_index(0)]314 #[pallet::weight(<T as Config>::WeightInfo::set_admin_address())]315 pub fn set_admin_address(origin: OriginFor<T>, admin: T::CrossAccountId) -> DispatchResult {316 ensure_root(origin)?;317318 <Admin<T>>::set(Some(admin.as_sub().to_owned()));319320 Self::deposit_event(Event::SetAdmin(admin.as_sub().to_owned()));321322 Ok(())323 }324325 326 327 328 329 330 331 332 #[pallet::call_index(1)]333 #[pallet::weight(<T as Config>::WeightInfo::stake())]334 pub fn stake(staker: OriginFor<T>, amount: BalanceOf<T>) -> DispatchResult {335 let staker_id = ensure_signed(staker)?;336337 ensure!(338 StakesPerAccount::<T>::get(&staker_id) < 10,339 Error::<T>::NoPermission340 );341342 ensure!(343 amount >= <BalanceOf<T>>::from(100u128) * T::Nominal::get(),344 ArithmeticError::Underflow345 );346 let config = <PalletConfiguration<T>>::get();347348 let balance = <<T as Config>::Currency as Inspect<T::AccountId>>::balance(&staker_id);349350 351 ensure!(352 amount353 <= match Self::get_frozen_balance(&staker_id) {354 Some(frozen_by_pallet) => balance355 .checked_sub(&frozen_by_pallet)356 .ok_or(ArithmeticError::Underflow)?,357 None => balance,358 },359 ArithmeticError::Underflow360 );361362 Self::add_freeze_balance(&staker_id, amount)?;363364 let block_number = T::RelayBlockNumberProvider::current_block_number();365366 367 368 let recalculate_after_interval: BlockNumberFor<T> =369 if block_number % config.recalculation_interval == 0u32.into() {370 1u32.into()371 } else {372 2u32.into()373 };374375 376 377 let recalc_block = (block_number / config.recalculation_interval378 + recalculate_after_interval)379 * config.recalculation_interval;380381 <Staked<T>>::insert((&staker_id, block_number), {382 let mut balance_and_recalc_block = <Staked<T>>::get((&staker_id, block_number));383 balance_and_recalc_block.0 = balance_and_recalc_block384 .0385 .checked_add(&amount)386 .ok_or(ArithmeticError::Overflow)?;387 balance_and_recalc_block.1 = recalc_block;388 balance_and_recalc_block389 });390391 <TotalStaked<T>>::set(392 <TotalStaked<T>>::get()393 .checked_add(&amount)394 .ok_or(ArithmeticError::Overflow)?,395 );396397 StakesPerAccount::<T>::mutate(&staker_id, |stakes| *stakes += 1);398399 Self::deposit_event(Event::Stake(staker_id, amount));400401 Ok(())402 }403404 405 406 407 #[pallet::call_index(2)]408 #[pallet::weight(<T as Config>::WeightInfo::unstake_all())]409 pub fn unstake_all(staker: OriginFor<T>) -> DispatchResult {410 let staker_id = ensure_signed(staker)?;411412 Self::unstake_all_internal(staker_id)413 }414415 416 417 418 419 420 421 422 423 #[pallet::call_index(8)]424 #[pallet::weight(<T as Config>::WeightInfo::unstake_partial())]425 pub fn unstake_partial(staker: OriginFor<T>, amount: BalanceOf<T>) -> DispatchResult {426 let staker_id = ensure_signed(staker)?;427428 Self::unstake_partial_internal(staker_id, amount)429 }430431 432 433 434 435 436 437 438 439 440 #[pallet::call_index(3)]441 #[pallet::weight(<T as Config>::WeightInfo::sponsor_collection())]442 pub fn sponsor_collection(443 admin: OriginFor<T>,444 collection_id: CollectionId,445 ) -> DispatchResult {446 let admin_id = ensure_signed(admin)?;447 ensure!(448 admin_id == Admin::<T>::get().ok_or(Error::<T>::AdminNotSet)?,449 Error::<T>::NoPermission450 );451452 T::CollectionHandler::set_sponsor(Self::account_id(), collection_id)453 }454455 456 457 458 459 460 461 462 463 464 465 466 #[pallet::call_index(4)]467 #[pallet::weight(<T as Config>::WeightInfo::stop_sponsoring_collection())]468 pub fn stop_sponsoring_collection(469 admin: OriginFor<T>,470 collection_id: CollectionId,471 ) -> DispatchResult {472 let admin_id = ensure_signed(admin)?;473474 ensure!(475 admin_id == Admin::<T>::get().ok_or(Error::<T>::AdminNotSet)?,476 Error::<T>::NoPermission477 );478479 ensure!(480 T::CollectionHandler::sponsor(collection_id)?.ok_or(<Error<T>>::SponsorNotSet)?481 == Self::account_id(),482 <Error<T>>::NoPermission483 );484 T::CollectionHandler::remove_collection_sponsor(collection_id)485 }486487 488 489 490 491 492 493 494 495 496 #[pallet::call_index(5)]497 #[pallet::weight(<T as Config>::WeightInfo::sponsor_contract())]498 pub fn sponsor_contract(admin: OriginFor<T>, contract_id: H160) -> DispatchResult {499 let admin_id = ensure_signed(admin)?;500501 ensure!(502 admin_id == Admin::<T>::get().ok_or(Error::<T>::AdminNotSet)?,503 Error::<T>::NoPermission504 );505506 T::ContractHandler::set_sponsor(507 T::CrossAccountId::from_sub(Self::account_id()),508 contract_id,509 )510 }511512 513 514 515 516 517 518 519 520 521 522 523 #[pallet::call_index(6)]524 #[pallet::weight(<T as Config>::WeightInfo::stop_sponsoring_contract())]525 pub fn stop_sponsoring_contract(admin: OriginFor<T>, contract_id: H160) -> DispatchResult {526 let admin_id = ensure_signed(admin)?;527528 ensure!(529 admin_id == Admin::<T>::get().ok_or(Error::<T>::AdminNotSet)?,530 Error::<T>::NoPermission531 );532533 ensure!(534 T::ContractHandler::sponsor(contract_id)?535 .ok_or(<Error<T>>::SponsorNotSet)?536 .as_sub() == &Self::account_id(),537 <Error<T>>::NoPermission538 );539 T::ContractHandler::remove_contract_sponsor(contract_id)540 }541542 543 544 545 546 547 548 549 550 551 552 553 554 #[pallet::call_index(7)]555 #[pallet::weight(<T as Config>::WeightInfo::payout_stakers(stakers_number.unwrap_or(DEFAULT_NUMBER_PAYOUTS) as u32))]556 pub fn payout_stakers(admin: OriginFor<T>, stakers_number: Option<u8>) -> DispatchResult {557 let admin_id = ensure_signed(admin)?;558559 ensure!(560 admin_id == Admin::<T>::get().ok_or(Error::<T>::AdminNotSet)?,561 Error::<T>::NoPermission562 );563 let config = <PalletConfiguration<T>>::get();564565 let mut stakers_number = stakers_number.unwrap_or(DEFAULT_NUMBER_PAYOUTS);566567 ensure!(568 stakers_number <= config.max_stakers_per_calculation && stakers_number != 0,569 Error::<T>::NoPermission570 );571572 573 574 let current_recalc_block = Self::get_current_recalc_block(575 T::RelayBlockNumberProvider::current_block_number(),576 &config,577 );578579 580 581 let next_recalc_block = current_recalc_block + config.recalculation_interval;582583 let storage_iterator =584 Self::get_next_calculated_key().map_or(Staked::<T>::iter(), Staked::<T>::iter_from);585586 PreviousCalculatedRecord::<T>::set(None);587588 {589 590 let last_id = RefCell::new(None);591 592 let mut last_staked_calculated_block = Default::default();593 594 let income_acc = RefCell::new(BalanceOf::<T>::default());595 596 let amount_acc = RefCell::new(BalanceOf::<T>::default());597598 599 600 601 602 603 604 605 let flush_stake = || -> DispatchResult {606 if let Some(last_id) = &*last_id.borrow() {607 if !income_acc.borrow().is_zero() {608 609 <<T as Config>::Currency as Mutate<T::AccountId>>::transfer(610 &T::TreasuryAccountId::get(),611 last_id,612 *income_acc.borrow(),613 frame_support::traits::tokens::Preservation::Protect,614 )?;615616 Self::add_freeze_balance(last_id, *income_acc.borrow())?;617 <TotalStaked<T>>::try_mutate(|staked| -> DispatchResult {618 *staked = staked619 .checked_add(&*income_acc.borrow())620 .ok_or(ArithmeticError::Overflow)?;621 Ok(())622 })?;623624 Self::deposit_event(Event::StakingRecalculation(625 last_id.clone(),626 *amount_acc.borrow(),627 *income_acc.borrow(),628 ));629 }630631 *income_acc.borrow_mut() = BalanceOf::<T>::default();632 *amount_acc.borrow_mut() = BalanceOf::<T>::default();633 }634 Ok(())635 };636637 638 639 640 641 642 643 for ((current_id, staked_block), (amount, next_recalc_block_for_stake)) in644 storage_iterator645 {646 647 648 649 if last_id.borrow().as_ref() != Some(¤t_id) {650 if stakers_number > 0 {651 flush_stake()?;652 *last_id.borrow_mut() = Some(current_id.clone());653 stakers_number -= 1;654 }655 656 else {657 if let Some(staker) = &*last_id.borrow() {658 659 PreviousCalculatedRecord::<T>::set(Some((660 staker.clone(),661 last_staked_calculated_block,662 )));663 }664 break;665 };666 };667668 669 if current_recalc_block >= next_recalc_block_for_stake {670 *amount_acc.borrow_mut() += amount;671 Self::recalculate_and_insert_stake(672 ¤t_id,673 staked_block,674 next_recalc_block,675 amount,676 ((current_recalc_block - next_recalc_block_for_stake)677 / config.recalculation_interval)678 .into() + 1,679 &mut *income_acc.borrow_mut(),680 );681 }682 last_staked_calculated_block = staked_block;683 }684 flush_stake()?;685 }686687 Ok(())688 }689690 691 692 693 694 695 696 697 #[pallet::call_index(9)]698 #[pallet::weight(<T as Config>::WeightInfo::on_initialize(PENDING_LIMIT_PER_BLOCK*pending_blocks.len() as u32))]699 pub fn resolve_skipped_blocks(700 origin: OriginFor<T>,701 pending_blocks: Vec<BlockNumberFor<T>>,702 ) -> DispatchResult {703 ensure_signed(origin)?;704705 ensure!(706 pending_blocks707 .iter()708 .all(|b| *b < <frame_system::Pallet<T>>::block_number()),709 <Error<T>>::NoPermission710 );711712 let mut pendings =713 Vec::with_capacity(PENDING_LIMIT_PER_BLOCK as usize * pending_blocks.len());714 pending_blocks715 .into_iter()716 .for_each(|b| pendings.append(&mut PendingUnstake::<T>::take(b).into_inner()));717718 pendings719 .into_iter()720 .try_for_each(|(staker, amount)| -> Result<(), DispatchError> {721 if let Some(b) = Self::get_frozen_balance(&staker) {722 let new_state = b.checked_sub(&amount).unwrap_or_default();723 Self::set_freeze_with_result(&staker, new_state)?;724 }725726 Ok(())727 })?;728729 Ok(())730 }731 }732}733734impl<T: Config> Pallet<T> {735 736 737 738 739 pub fn account_id() -> T::AccountId {740 T::PalletId::get().into_account_truncating()741 }742743 744 745 746 747 fn unstake_partial_internal(748 staker_id: T::AccountId,749 unstaked_balance: BalanceOf<T>,750 ) -> DispatchResult {751 if unstaked_balance == Default::default() {752 return Ok(());753 }754755 let config = <PalletConfiguration<T>>::get();756757 758 let unpending_block = <frame_system::Pallet<T>>::block_number() + config.pending_interval;759760 let mut pendings = <PendingUnstake<T>>::get(unpending_block);761762 763 ensure!(!pendings.is_full(), Error::<T>::PendingForBlockOverflow);764765 let mut stakes = Staked::<T>::iter_prefix((&staker_id,)).collect::<Vec<_>>();766767 let total_staked = stakes768 .iter()769 .fold(<BalanceOf<T>>::default(), |acc, (_, (balance, _))| {770 acc + *balance771 });772773 ensure!(774 unstaked_balance <= total_staked,775 <Error<T>>::InsufficientStakedBalance776 );777778 <TotalStaked<T>>::set(779 <TotalStaked<T>>::get()780 .checked_sub(&unstaked_balance)781 .ok_or(ArithmeticError::Underflow)?,782 );783784 stakes.sort_by_key(|(block, _)| *block);785786 let mut acc_amount = unstaked_balance;787 let mut will_deleted_stakes_count = 0u8;788789 let changed_stakes = stakes790 .into_iter()791 .map_while(|(block, (balance_per_block, _))| {792 if acc_amount == <BalanceOf<T>>::default() {793 return None;794 }795 if acc_amount < balance_per_block {796 let res = (block, balance_per_block - acc_amount);797 acc_amount = <BalanceOf<T>>::default();798 Some(res)799 } else {800 acc_amount -= balance_per_block;801 will_deleted_stakes_count += 1;802 Some((block, <BalanceOf<T>>::default()))803 }804 })805 .collect::<Vec<_>>();806807 pendings808 .try_push((staker_id.clone(), unstaked_balance))809 .map_err(|_| Error::<T>::PendingForBlockOverflow)?;810811 StakesPerAccount::<T>::try_mutate(&staker_id, |stakes| -> DispatchResult {812 *stakes = stakes813 .checked_sub(will_deleted_stakes_count)814 .ok_or(ArithmeticError::Underflow)?;815 Ok(())816 })?;817818 changed_stakes819 .into_iter()820 .for_each(|(staked_block, current_stake_state)| {821 if current_stake_state == Default::default() {822 <Staked<T>>::remove((&staker_id, staked_block));823 } else {824 <Staked<T>>::mutate((&staker_id, staked_block), |(old_stake_state, _)| {825 *old_stake_state = current_stake_state826 });827 }828 });829830 <PendingUnstake<T>>::insert(unpending_block, pendings);831832 Self::deposit_event(Event::Unstake(staker_id, unstaked_balance));833834 Ok(())835 }836837 838 839 840 841 fn add_freeze_balance(staker: &T::AccountId, amount: BalanceOf<T>) -> DispatchResult {842 Self::get_frozen_balance(staker)843 .unwrap_or_default()844 .checked_add(&amount)845 .map(|freeze| Self::set_freeze_with_result(staker, freeze))846 .ok_or::<DispatchError>(ArithmeticError::Overflow.into())?847 }848849 850 851 852 853 fn set_freeze_unchecked(staker: &T::AccountId, amount: BalanceOf<T>) {854 let _ = Self::set_freeze_with_result(staker, amount);855 }856857 858 859 860 861 fn set_freeze_with_result(staker: &T::AccountId, amount: BalanceOf<T>) -> DispatchResult {862 if amount.is_zero() {863 <<T as Config>::Currency as MutateFreeze<T::AccountId>>::thaw(864 &T::FreezeIdentifier::get(),865 staker,866 )867 } else {868 <<T as Config>::Currency as MutateFreeze<T::AccountId>>::set_freeze(869 &T::FreezeIdentifier::get(),870 staker,871 amount,872 )873 }874 }875876 877 878 879 pub fn get_frozen_balance(staker: &T::AccountId) -> Option<BalanceOf<T>> {880 let res = <<T as Config>::Currency as InspectFreeze<T::AccountId>>::balance_frozen(881 &T::FreezeIdentifier::get(),882 staker,883 );884885 if res == Zero::zero() {886 None887 } else {888 Some(res)889 }890 }891892 893 894 895 pub fn total_staked_by_id(staker: impl EncodeLike<T::AccountId>) -> Option<BalanceOf<T>> {896 let staked = Staked::<T>::iter_prefix((staker,))897 .fold(<BalanceOf<T>>::default(), |acc, (_, (amount, _))| {898 acc + amount899 });900 if staked != <BalanceOf<T>>::default() {901 Some(staked)902 } else {903 None904 }905 }906907 908 909 910 911 pub fn total_staked_by_id_per_block(912 staker: impl EncodeLike<T::AccountId>,913 ) -> Option<Vec<(BlockNumberFor<T>, BalanceOf<T>)>> {914 let mut staked = Staked::<T>::iter_prefix((staker,))915 .map(|(block, (amount, _))| (block, amount))916 .collect::<Vec<_>>();917 staked.sort_by_key(|(block, _)| *block);918 if !staked.is_empty() {919 Some(staked)920 } else {921 None922 }923 }924925 926 927 928 pub fn cross_id_total_staked(staker: Option<T::CrossAccountId>) -> Option<BalanceOf<T>> {929 staker.map_or(Some(<TotalStaked<T>>::get()), |s| {930 Self::total_staked_by_id(s.as_sub())931 })932 }933934 935 936 937 938 pub fn cross_id_total_staked_per_block(939 staker: T::CrossAccountId,940 ) -> Vec<(BlockNumberFor<T>, BalanceOf<T>)> {941 Self::total_staked_by_id_per_block(staker.as_sub()).unwrap_or_default()942 }943944 fn recalculate_and_insert_stake(945 staker: &T::AccountId,946 staked_block: BlockNumberFor<T>,947 next_recalc_block: BlockNumberFor<T>,948 base: BalanceOf<T>,949 iters: u32,950 income_acc: &mut BalanceOf<T>,951 ) {952 let income = Self::calculate_income(base, iters);953954 if let Some(res) = base.checked_add(&income) {955 <Staked<T>>::insert((staker, staked_block), (res, next_recalc_block));956 *income_acc += income;957 };958 }959960 fn calculate_income<I>(base: I, iters: u32) -> I961 where962 I: EncodeLike<BalanceOf<T>> + Balance,963 {964 let config = <PalletConfiguration<T>>::get();965 let mut income = base;966967 (0..iters).for_each(|_| income += config.interval_income * income);968969 income - base970 }971972 973 974 fn get_current_recalc_block(975 current_relay_block: BlockNumberFor<T>,976 config: &PalletConfiguration<T>,977 ) -> BlockNumberFor<T> {978 (current_relay_block / config.recalculation_interval) * config.recalculation_interval979 }980981 fn get_next_calculated_key() -> Option<Vec<u8>> {982 Self::get_next_calculated_record().map(Staked::<T>::hashed_key_for)983 }984}985986impl<T: Config> Pallet<T>987where988 <<T as Config>::Currency as Inspect<T::AccountId>>::Balance: Sum,989{990 991 992 993 994 995 996 997 998 pub fn cross_id_pending_unstake(staker: Option<T::CrossAccountId>) -> BalanceOf<T> {999 staker.map_or(1000 PendingUnstake::<T>::iter_values()1001 .flat_map(|pendings| pendings.into_iter().map(|(_, amount)| amount))1002 .sum(),1003 |s| {1004 PendingUnstake::<T>::iter_values()1005 .flatten()1006 .filter_map(|(id, amount)| {1007 if id == *s.as_sub() {1008 Some(amount)1009 } else {1010 None1011 }1012 })1013 .sum()1014 },1015 )1016 }10171018 1019 1020 1021 1022 pub fn cross_id_pending_unstake_per_block(1023 staker: T::CrossAccountId,1024 ) -> Vec<(BlockNumberFor<T>, BalanceOf<T>)> {1025 let mut unsorted_res = vec![];1026 PendingUnstake::<T>::iter().for_each(|(block, pendings)| {1027 pendings.into_iter().for_each(|(id, amount)| {1028 if id == *staker.as_sub() {1029 unsorted_res.push((block, amount));1030 };1031 })1032 });10331034 unsorted_res.sort_by_key(|(block, _)| *block);1035 unsorted_res1036 }10371038 fn unstake_all_internal(staker_id: T::AccountId) -> DispatchResult {1039 let config = <PalletConfiguration<T>>::get();10401041 1042 let block = <frame_system::Pallet<T>>::block_number() + config.pending_interval;10431044 let mut pendings = <PendingUnstake<T>>::get(block);10451046 1047 ensure!(!pendings.is_full(), Error::<T>::PendingForBlockOverflow);10481049 let total_staked: BalanceOf<T> = Staked::<T>::drain_prefix((&staker_id,))1050 .map(|(_, (amount, _))| amount)1051 .sum();10521053 if total_staked.is_zero() {1054 return Ok(());1055 }10561057 pendings1058 .try_push((staker_id.clone(), total_staked))1059 .map_err(|_| Error::<T>::PendingForBlockOverflow)?;10601061 <PendingUnstake<T>>::insert(block, pendings);10621063 TotalStaked::<T>::set(1064 TotalStaked::<T>::get()1065 .checked_sub(&total_staked)1066 .ok_or(ArithmeticError::Underflow)?,1067 );10681069 StakesPerAccount::<T>::remove(&staker_id);10701071 Self::deposit_event(Event::Unstake(staker_id, total_staked));10721073 Ok(())1074 }1075}