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 698 699 700 #[pallet::call_index(9)]701 #[pallet::weight(<T as Config>::WeightInfo::on_initialize(PENDING_LIMIT_PER_BLOCK*pending_blocks.len() as u32))]702 pub fn force_unstake(703 origin: OriginFor<T>,704 pending_blocks: Vec<BlockNumberFor<T>>,705 ) -> DispatchResult {706 ensure_root(origin)?;707708 ensure!(709 pending_blocks710 .iter()711 .all(|b| *b < <frame_system::Pallet<T>>::block_number()),712 <Error<T>>::NoPermission713 );714715 let mut pendings =716 Vec::with_capacity(PENDING_LIMIT_PER_BLOCK as usize * pending_blocks.len());717 pending_blocks718 .into_iter()719 .for_each(|b| pendings.append(&mut PendingUnstake::<T>::take(b).into_inner()));720721 pendings722 .into_iter()723 .try_for_each(|(staker, amount)| -> Result<(), DispatchError> {724 if let Some(b) = Self::get_frozen_balance(&staker) {725 let new_state = b.checked_sub(&amount).unwrap_or_default();726 Self::set_freeze_with_result(&staker, new_state)?;727 }728729 Ok(())730 })?;731732 Ok(())733 }734 }735}736737impl<T: Config> Pallet<T> {738 739 740 741 742 pub fn account_id() -> T::AccountId {743 T::PalletId::get().into_account_truncating()744 }745746 747 748 749 750 fn unstake_partial_internal(751 staker_id: T::AccountId,752 unstaked_balance: BalanceOf<T>,753 ) -> DispatchResult {754 if unstaked_balance == Default::default() {755 return Ok(());756 }757758 let config = <PalletConfiguration<T>>::get();759760 761 let unpending_block = <frame_system::Pallet<T>>::block_number() + config.pending_interval;762763 let mut pendings = <PendingUnstake<T>>::get(unpending_block);764765 766 ensure!(!pendings.is_full(), Error::<T>::PendingForBlockOverflow);767768 let mut stakes = Staked::<T>::iter_prefix((&staker_id,)).collect::<Vec<_>>();769770 let total_staked = stakes771 .iter()772 .fold(<BalanceOf<T>>::default(), |acc, (_, (balance, _))| {773 acc + *balance774 });775776 ensure!(777 unstaked_balance <= total_staked,778 <Error<T>>::InsufficientStakedBalance779 );780781 <TotalStaked<T>>::set(782 <TotalStaked<T>>::get()783 .checked_sub(&unstaked_balance)784 .ok_or(ArithmeticError::Underflow)?,785 );786787 stakes.sort_by_key(|(block, _)| *block);788789 let mut acc_amount = unstaked_balance;790 let mut will_deleted_stakes_count = 0u8;791792 let changed_stakes = stakes793 .into_iter()794 .map_while(|(block, (balance_per_block, _))| {795 if acc_amount == <BalanceOf<T>>::default() {796 return None;797 }798 if acc_amount < balance_per_block {799 let res = (block, balance_per_block - acc_amount);800 acc_amount = <BalanceOf<T>>::default();801 Some(res)802 } else {803 acc_amount -= balance_per_block;804 will_deleted_stakes_count += 1;805 Some((block, <BalanceOf<T>>::default()))806 }807 })808 .collect::<Vec<_>>();809810 pendings811 .try_push((staker_id.clone(), unstaked_balance))812 .map_err(|_| Error::<T>::PendingForBlockOverflow)?;813814 StakesPerAccount::<T>::try_mutate(&staker_id, |stakes| -> DispatchResult {815 *stakes = stakes816 .checked_sub(will_deleted_stakes_count)817 .ok_or(ArithmeticError::Underflow)?;818 Ok(())819 })?;820821 changed_stakes822 .into_iter()823 .for_each(|(staked_block, current_stake_state)| {824 if current_stake_state == Default::default() {825 <Staked<T>>::remove((&staker_id, staked_block));826 } else {827 <Staked<T>>::mutate((&staker_id, staked_block), |(old_stake_state, _)| {828 *old_stake_state = current_stake_state829 });830 }831 });832833 <PendingUnstake<T>>::insert(unpending_block, pendings);834835 Self::deposit_event(Event::Unstake(staker_id, unstaked_balance));836837 Ok(())838 }839840 841 842 843 844 fn add_freeze_balance(staker: &T::AccountId, amount: BalanceOf<T>) -> DispatchResult {845 Self::get_frozen_balance(staker)846 .unwrap_or_default()847 .checked_add(&amount)848 .map(|freeze| Self::set_freeze_with_result(staker, freeze))849 .ok_or::<DispatchError>(ArithmeticError::Overflow.into())?850 }851852 853 854 855 856 fn set_freeze_unchecked(staker: &T::AccountId, amount: BalanceOf<T>) {857 let _ = Self::set_freeze_with_result(staker, amount);858 }859860 861 862 863 864 fn set_freeze_with_result(staker: &T::AccountId, amount: BalanceOf<T>) -> DispatchResult {865 if amount.is_zero() {866 <<T as Config>::Currency as MutateFreeze<T::AccountId>>::thaw(867 &T::FreezeIdentifier::get(),868 staker,869 )870 } else {871 <<T as Config>::Currency as MutateFreeze<T::AccountId>>::set_freeze(872 &T::FreezeIdentifier::get(),873 staker,874 amount,875 )876 }877 }878879 880 881 882 pub fn get_frozen_balance(staker: &T::AccountId) -> Option<BalanceOf<T>> {883 let res = <<T as Config>::Currency as InspectFreeze<T::AccountId>>::balance_frozen(884 &T::FreezeIdentifier::get(),885 staker,886 );887888 if res == Zero::zero() {889 None890 } else {891 Some(res)892 }893 }894895 896 897 898 pub fn total_staked_by_id(staker: impl EncodeLike<T::AccountId>) -> Option<BalanceOf<T>> {899 let staked = Staked::<T>::iter_prefix((staker,))900 .fold(<BalanceOf<T>>::default(), |acc, (_, (amount, _))| {901 acc + amount902 });903 if staked != <BalanceOf<T>>::default() {904 Some(staked)905 } else {906 None907 }908 }909910 911 912 913 914 pub fn total_staked_by_id_per_block(915 staker: impl EncodeLike<T::AccountId>,916 ) -> Option<Vec<(BlockNumberFor<T>, BalanceOf<T>)>> {917 let mut staked = Staked::<T>::iter_prefix((staker,))918 .map(|(block, (amount, _))| (block, amount))919 .collect::<Vec<_>>();920 staked.sort_by_key(|(block, _)| *block);921 if !staked.is_empty() {922 Some(staked)923 } else {924 None925 }926 }927928 929 930 931 pub fn cross_id_total_staked(staker: Option<T::CrossAccountId>) -> Option<BalanceOf<T>> {932 staker.map_or(Some(<TotalStaked<T>>::get()), |s| {933 Self::total_staked_by_id(s.as_sub())934 })935 }936937 938 939 940 941 pub fn cross_id_total_staked_per_block(942 staker: T::CrossAccountId,943 ) -> Vec<(BlockNumberFor<T>, BalanceOf<T>)> {944 Self::total_staked_by_id_per_block(staker.as_sub()).unwrap_or_default()945 }946947 fn recalculate_and_insert_stake(948 staker: &T::AccountId,949 staked_block: BlockNumberFor<T>,950 next_recalc_block: BlockNumberFor<T>,951 base: BalanceOf<T>,952 iters: u32,953 income_acc: &mut BalanceOf<T>,954 ) {955 let income = Self::calculate_income(base, iters);956957 if let Some(res) = base.checked_add(&income) {958 <Staked<T>>::insert((staker, staked_block), (res, next_recalc_block));959 *income_acc += income;960 };961 }962963 fn calculate_income<I>(base: I, iters: u32) -> I964 where965 I: EncodeLike<BalanceOf<T>> + Balance,966 {967 let config = <PalletConfiguration<T>>::get();968 let mut income = base;969970 (0..iters).for_each(|_| income += config.interval_income * income);971972 income - base973 }974975 976 977 fn get_current_recalc_block(978 current_relay_block: BlockNumberFor<T>,979 config: &PalletConfiguration<T>,980 ) -> BlockNumberFor<T> {981 (current_relay_block / config.recalculation_interval) * config.recalculation_interval982 }983984 fn get_next_calculated_key() -> Option<Vec<u8>> {985 Self::get_next_calculated_record().map(Staked::<T>::hashed_key_for)986 }987}988989impl<T: Config> Pallet<T>990where991 <<T as Config>::Currency as Inspect<T::AccountId>>::Balance: Sum,992{993 994 995 996 997 998 999 1000 1001 pub fn cross_id_pending_unstake(staker: Option<T::CrossAccountId>) -> BalanceOf<T> {1002 staker.map_or(1003 PendingUnstake::<T>::iter_values()1004 .flat_map(|pendings| pendings.into_iter().map(|(_, amount)| amount))1005 .sum(),1006 |s| {1007 PendingUnstake::<T>::iter_values()1008 .flatten()1009 .filter_map(|(id, amount)| {1010 if id == *s.as_sub() {1011 Some(amount)1012 } else {1013 None1014 }1015 })1016 .sum()1017 },1018 )1019 }10201021 1022 1023 1024 1025 pub fn cross_id_pending_unstake_per_block(1026 staker: T::CrossAccountId,1027 ) -> Vec<(BlockNumberFor<T>, BalanceOf<T>)> {1028 let mut unsorted_res = vec![];1029 PendingUnstake::<T>::iter().for_each(|(block, pendings)| {1030 pendings.into_iter().for_each(|(id, amount)| {1031 if id == *staker.as_sub() {1032 unsorted_res.push((block, amount));1033 };1034 })1035 });10361037 unsorted_res.sort_by_key(|(block, _)| *block);1038 unsorted_res1039 }10401041 fn unstake_all_internal(staker_id: T::AccountId) -> DispatchResult {1042 let config = <PalletConfiguration<T>>::get();10431044 1045 let block = <frame_system::Pallet<T>>::block_number() + config.pending_interval;10461047 let mut pendings = <PendingUnstake<T>>::get(block);10481049 1050 ensure!(!pendings.is_full(), Error::<T>::PendingForBlockOverflow);10511052 let total_staked: BalanceOf<T> = Staked::<T>::drain_prefix((&staker_id,))1053 .map(|(_, (amount, _))| amount)1054 .sum();10551056 if total_staked.is_zero() {1057 return Ok(());1058 }10591060 pendings1061 .try_push((staker_id.clone(), total_staked))1062 .map_err(|_| Error::<T>::PendingForBlockOverflow)?;10631064 <PendingUnstake<T>>::insert(block, pendings);10651066 TotalStaked::<T>::set(1067 TotalStaked::<T>::get()1068 .checked_sub(&total_staked)1069 .ok_or(ArithmeticError::Underflow)?,1070 );10711072 StakesPerAccount::<T>::remove(&staker_id);10731074 Self::deposit_event(Event::Unstake(staker_id, total_staked));10751076 Ok(())1077 }1078}