1use crate::{
21 asset, session_rotation::EraElectionPlanner, slashing, weights::WeightInfo, AccountIdLookupOf,
22 ActiveEraInfo, BalanceOf, EraPayout, EraRewardPoints, ExposurePage, Forcing,
23 LedgerIntegrityState, MaxNominationsOf, NegativeImbalanceOf, Nominations, NominationsQuota,
24 PositiveImbalanceOf, RewardDestination, StakingLedger, UnappliedSlash, UnlockChunk,
25 ValidatorPrefs,
26};
27use alloc::{format, vec::Vec};
28use codec::Codec;
29use frame_election_provider_support::{ElectionProvider, SortedListProvider, VoteWeight};
30use frame_support::{
31 assert_ok,
32 pallet_prelude::*,
33 traits::{
34 fungible::{
35 hold::{Balanced as FunHoldBalanced, Mutate as FunHoldMutate},
36 Mutate, Mutate as FunMutate,
37 },
38 Contains, Defensive, DefensiveSaturating, EnsureOrigin, Get, InspectLockableCurrency,
39 Nothing, OnUnbalanced,
40 },
41 weights::Weight,
42 BoundedBTreeSet, BoundedVec,
43};
44use frame_system::{ensure_root, ensure_signed, pallet_prelude::*};
45pub use impls::*;
46use rand::seq::SliceRandom;
47use rand_chacha::{
48 rand_core::{RngCore, SeedableRng},
49 ChaChaRng,
50};
51use sp_core::{sr25519::Pair as SrPair, Pair};
52use sp_runtime::{
53 traits::{StaticLookup, Zero},
54 ArithmeticError, Perbill, Percent,
55};
56use sp_staking::{
57 EraIndex, Page, SessionIndex,
58 StakingAccount::{self, Controller, Stash},
59 StakingInterface,
60};
61
62mod impls;
63
64#[frame_support::pallet]
65pub mod pallet {
66 use core::ops::Deref;
67
68 use super::*;
69 use crate::{session_rotation, PagedExposureMetadata, SnapshotStatus};
70 use codec::HasCompact;
71 use frame_election_provider_support::{ElectionDataProvider, PageIndex};
72 use frame_support::{traits::ConstBool, weights::WeightMeter, DefaultNoBound};
73
74 type IncentiveWeight<T> = BalanceOf<T>;
78
79 #[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, Debug, TypeInfo, MaxEncodedLen)]
81 pub enum PruningStep {
82 ErasStakersPaged,
84 ErasStakersOverview,
86 ErasValidatorPrefs,
88 ClaimedRewards,
90 ErasValidatorReward,
92 ErasRewardPoints,
94 SingleEntryCleanups,
96 ValidatorSlashInEra,
98 ErasValidatorIncentiveWeight,
100 }
101
102 const STORAGE_VERSION: StorageVersion = StorageVersion::new(17);
104
105 #[pallet::pallet]
106 #[pallet::storage_version(STORAGE_VERSION)]
107 pub struct Pallet<T>(_);
108
109 #[derive(TypeInfo, Debug, Clone, Encode, Decode, DecodeWithMemTracking, PartialEq)]
111 pub enum ConfigOp<T: Default + Codec> {
112 Noop,
114 Set(T),
116 Remove,
118 }
119
120 #[pallet::config(with_default)]
121 pub trait Config: frame_system::Config {
122 #[pallet::no_default]
124 type OldCurrency: InspectLockableCurrency<
125 Self::AccountId,
126 Moment = BlockNumberFor<Self>,
127 Balance = Self::CurrencyBalance,
128 >;
129
130 #[pallet::no_default]
132 type Currency: FunHoldMutate<
133 Self::AccountId,
134 Reason = Self::RuntimeHoldReason,
135 Balance = Self::CurrencyBalance,
136 > + FunMutate<Self::AccountId, Balance = Self::CurrencyBalance>
137 + FunHoldBalanced<Self::AccountId, Balance = Self::CurrencyBalance>;
138
139 #[pallet::no_default_bounds]
141 type RuntimeHoldReason: From<HoldReason>;
142
143 type CurrencyBalance: sp_runtime::traits::AtLeast32BitUnsigned
146 + codec::FullCodec
147 + DecodeWithMemTracking
148 + HasCompact<Type: DecodeWithMemTracking>
149 + Copy
150 + MaybeSerializeDeserialize
151 + core::fmt::Debug
152 + Default
153 + From<u64>
154 + TypeInfo
155 + Send
156 + Sync
157 + MaxEncodedLen;
158
159 #[pallet::no_default_bounds]
166 type CurrencyToVote: sp_staking::currency_to_vote::CurrencyToVote<BalanceOf<Self>>;
167
168 #[pallet::no_default]
170 type ElectionProvider: ElectionProvider<
171 AccountId = Self::AccountId,
172 BlockNumber = BlockNumberFor<Self>,
173 DataProvider = Pallet<Self>,
175 >;
176
177 #[pallet::no_default_bounds]
179 type NominationsQuota: NominationsQuota<BalanceOf<Self>>;
180
181 #[pallet::constant]
195 type HistoryDepth: Get<u32>;
196
197 #[pallet::no_default_bounds]
201 type RewardRemainder: OnUnbalanced<NegativeImbalanceOf<Self>>;
202
203 #[pallet::no_default_bounds]
205 type Slash: OnUnbalanced<NegativeImbalanceOf<Self>>;
206
207 #[pallet::no_default_bounds]
213 type Reward: OnUnbalanced<PositiveImbalanceOf<Self>>;
214
215 #[pallet::constant]
217 type SessionsPerEra: Get<SessionIndex>;
218
219 #[pallet::constant]
234 type PlanningEraOffset: Get<SessionIndex>;
235
236 #[pallet::constant]
242 type BondingDuration: Get<EraIndex>;
243
244 #[pallet::constant]
253 type NominatorFastUnbondDuration: Get<EraIndex>;
254
255 #[pallet::constant]
260 type SlashDeferDuration: Get<EraIndex>;
261
262 #[pallet::no_default]
266 type AdminOrigin: EnsureOrigin<Self::RuntimeOrigin>;
267
268 #[pallet::no_default]
274 type EraPayout: EraPayout<BalanceOf<Self>>;
275
276 #[pallet::constant]
287 type DisableMinting: Get<bool>;
288
289 #[pallet::no_default_bounds]
294 type UnclaimedRewardHandler: OnUnbalanced<NegativeImbalanceOf<Self>>;
295
296 #[pallet::no_default]
301 type RewardPots: crate::PotAccountProvider<Self::AccountId>;
302
303 #[pallet::no_default_bounds]
307 type StakerRewardCalculator: sp_staking::StakerRewardCalculator<BalanceOf<Self>>;
308
309 #[pallet::constant]
321 type MaxExposurePageSize: Get<u32>;
322
323 #[pallet::constant]
328 type MaxValidatorSet: Get<u32>;
329
330 #[pallet::no_default]
342 type VoterList: SortedListProvider<Self::AccountId, Score = VoteWeight>;
343
344 #[pallet::no_default]
365 type TargetList: SortedListProvider<Self::AccountId, Score = BalanceOf<Self>>;
366
367 #[pallet::constant]
378 type MaxUnlockingChunks: Get<u32>;
379
380 type MaxControllersInDeprecationBatch: Get<u32>;
382
383 #[pallet::no_default_bounds]
388 type EventListeners: sp_staking::OnStakingUpdate<Self::AccountId, BalanceOf<Self>>;
389
390 #[pallet::constant]
401 type MaxEraDuration: Get<u64>;
402
403 #[pallet::constant]
409 type MaxPruningItems: Get<u32>;
410
411 #[pallet::no_default]
414 type RcClientInterface: pallet_staking_async_rc_client::RcClientInterface<
415 AccountId = Self::AccountId,
416 >;
417
418 #[pallet::no_default_bounds]
419 type Filter: Contains<Self::AccountId>;
424
425 type WeightInfo: WeightInfo;
427 }
428
429 #[pallet::composite_enum]
431 pub enum HoldReason {
432 #[codec(index = 0)]
434 Staking,
435 }
436
437 pub mod config_preludes {
439 use super::*;
440 use frame_support::{derive_impl, parameter_types, traits::ConstU32};
441 pub struct TestDefaultConfig;
442
443 #[derive_impl(frame_system::config_preludes::TestDefaultConfig, no_aggregated_types)]
444 impl frame_system::DefaultConfig for TestDefaultConfig {}
445
446 parameter_types! {
447 pub const SessionsPerEra: SessionIndex = 3;
448 pub const BondingDuration: EraIndex = 3;
449 pub const NominatorFastUnbondDuration: EraIndex = 2;
450 pub const MaxPruningItems: u32 = 100;
451 }
452
453 #[frame_support::register_default_impl(TestDefaultConfig)]
454 impl DefaultConfig for TestDefaultConfig {
455 #[inject_runtime_type]
456 type RuntimeHoldReason = ();
457 type CurrencyBalance = u128;
458 type CurrencyToVote = ();
459 type NominationsQuota = crate::FixedNominationsQuota<16>;
460 type HistoryDepth = ConstU32<84>;
461 type RewardRemainder = ();
462 type Slash = ();
463 type Reward = ();
464 type UnclaimedRewardHandler = ();
465 type StakerRewardCalculator = ();
466 type DisableMinting = ConstBool<false>;
467 type SessionsPerEra = SessionsPerEra;
468 type BondingDuration = BondingDuration;
469 type NominatorFastUnbondDuration = NominatorFastUnbondDuration;
470 type PlanningEraOffset = ConstU32<1>;
471 type SlashDeferDuration = ();
472 type MaxExposurePageSize = ConstU32<64>;
473 type MaxUnlockingChunks = ConstU32<32>;
474 type MaxValidatorSet = ConstU32<100>;
475 type MaxControllersInDeprecationBatch = ConstU32<100>;
476 type MaxEraDuration = ();
477 type MaxPruningItems = MaxPruningItems;
478 type EventListeners = ();
479 type Filter = Nothing;
480 type WeightInfo = ();
481 }
482 }
483
484 #[pallet::storage]
486 pub type ValidatorCount<T> = StorageValue<_, u32, ValueQuery>;
487
488 #[pallet::storage]
492 pub type Bonded<T: Config> = StorageMap<_, Twox64Concat, T::AccountId, T::AccountId>;
493
494 #[pallet::storage]
496 pub type MinNominatorBond<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;
497
498 #[pallet::storage]
500 pub type MinValidatorBond<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;
501
502 #[pallet::storage]
504 pub type MinimumActiveStake<T> = StorageValue<_, BalanceOf<T>, ValueQuery>;
505
506 #[pallet::storage]
510 pub type MinCommission<T: Config> = StorageValue<_, Perbill, ValueQuery>;
511
512 #[pallet::storage]
516 pub type MaxCommission<T: Config> = StorageValue<_, Perbill, ValueQuery, MaxCommissionDefault>;
517
518 pub struct MaxCommissionDefault;
520 impl Get<Perbill> for MaxCommissionDefault {
521 fn get() -> Perbill {
522 Perbill::one()
523 }
524 }
525
526 #[pallet::storage]
534 pub type DisableMintingGuard<T: Config> = StorageValue<_, EraIndex>;
535
536 #[pallet::storage]
541 pub type OptimumSelfStake<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;
542
543 #[pallet::storage]
547 pub type HardCapSelfStake<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;
548
549 #[pallet::storage]
553 pub type SelfStakeSlopeFactor<T: Config> = StorageValue<_, Perbill, ValueQuery>;
554
555 #[pallet::storage]
559 pub type ErasValidatorIncentiveBudget<T: Config> =
560 StorageMap<_, Twox64Concat, EraIndex, BalanceOf<T>, ValueQuery>;
561
562 #[pallet::storage]
566 pub type ErasSumValidatorIncentiveWeight<T: Config> =
567 StorageMap<_, Twox64Concat, EraIndex, IncentiveWeight<T>, ValueQuery>;
568
569 #[pallet::storage]
572 pub type ErasValidatorIncentiveWeight<T: Config> = StorageDoubleMap<
573 _,
574 Twox64Concat,
575 EraIndex,
576 Twox64Concat,
577 T::AccountId,
578 IncentiveWeight<T>,
579 OptionQuery,
580 >;
581
582 #[pallet::storage]
590 pub type AreNominatorsSlashable<T: Config> = StorageValue<_, bool, ValueQuery, ConstBool<true>>;
591
592 #[pallet::storage]
600 pub type ErasNominatorsSlashable<T: Config> =
601 StorageMap<_, Twox64Concat, EraIndex, bool, OptionQuery>;
602
603 #[pallet::storage]
608 pub type Ledger<T: Config> = StorageMap<_, Blake2_128Concat, T::AccountId, StakingLedger<T>>;
609
610 #[pallet::storage]
614 pub type Payee<T: Config> =
615 StorageMap<_, Twox64Concat, T::AccountId, RewardDestination<T::AccountId>, OptionQuery>;
616
617 #[pallet::storage]
621 pub type Validators<T: Config> =
622 CountedStorageMap<_, Twox64Concat, T::AccountId, ValidatorPrefs, ValueQuery>;
623
624 #[pallet::storage]
628 pub type MaxValidatorsCount<T> = StorageValue<_, u32, OptionQuery>;
629
630 #[pallet::storage]
643 pub type LastValidatorEra<T: Config> = StorageMap<_, Twox64Concat, T::AccountId, EraIndex>;
644
645 #[pallet::storage]
665 pub type Nominators<T: Config> =
666 CountedStorageMap<_, Twox64Concat, T::AccountId, Nominations<T>>;
667
668 #[pallet::storage]
675 pub type VirtualStakers<T: Config> = CountedStorageMap<_, Twox64Concat, T::AccountId, ()>;
676
677 #[pallet::storage]
681 pub type MaxNominatorsCount<T> = StorageValue<_, u32, OptionQuery>;
682
683 #[pallet::storage]
690 pub type CurrentEra<T> = StorageValue<_, EraIndex>;
691
692 #[pallet::storage]
697 pub type ActiveEra<T> = StorageValue<_, ActiveEraInfo>;
698
699 pub struct BondedErasBound<T>(core::marker::PhantomData<T>);
701 impl<T: Config> Get<u32> for BondedErasBound<T> {
702 fn get() -> u32 {
703 T::BondingDuration::get().saturating_add(1)
704 }
705 }
706
707 const OFFENCE_QUEUE_ERAS_BOUND: u32 = 10;
708 pub struct OffenceQueueErasBound<T>(core::marker::PhantomData<T>);
711 impl<T: Config> Get<u32> for OffenceQueueErasBound<T> {
712 fn get() -> u32 {
713 let bonding_duration = T::BondingDuration::get();
714 bonding_duration.saturating_add(OFFENCE_QUEUE_ERAS_BOUND) }
720 }
721
722 #[pallet::storage]
727 pub type BondedEras<T: Config> =
728 StorageValue<_, BoundedVec<(EraIndex, SessionIndex), BondedErasBound<T>>, ValueQuery>;
729
730 #[pallet::storage]
745 pub type ErasStakersOverview<T: Config> = StorageDoubleMap<
746 _,
747 Twox64Concat,
748 EraIndex,
749 Twox64Concat,
750 T::AccountId,
751 PagedExposureMetadata<BalanceOf<T>>,
752 OptionQuery,
753 >;
754
755 #[derive(PartialEqNoBound, Encode, Decode, DebugNoBound, TypeInfo, DefaultNoBound)]
764 #[scale_info(skip_type_params(T))]
765 pub struct BoundedExposurePage<T: Config>(pub ExposurePage<T::AccountId, BalanceOf<T>>);
766 impl<T: Config> Deref for BoundedExposurePage<T> {
767 type Target = ExposurePage<T::AccountId, BalanceOf<T>>;
768
769 fn deref(&self) -> &Self::Target {
770 &self.0
771 }
772 }
773
774 impl<T: Config> core::ops::DerefMut for BoundedExposurePage<T> {
775 fn deref_mut(&mut self) -> &mut Self::Target {
776 &mut self.0
777 }
778 }
779
780 impl<T: Config> codec::MaxEncodedLen for BoundedExposurePage<T> {
781 fn max_encoded_len() -> usize {
782 let max_exposure_page_size = T::MaxExposurePageSize::get() as usize;
783 let individual_size =
784 T::AccountId::max_encoded_len() + BalanceOf::<T>::max_encoded_len();
785
786 BalanceOf::<T>::max_encoded_len() +
788 max_exposure_page_size.saturating_mul(individual_size)
790 }
791 }
792
793 impl<T: Config> From<ExposurePage<T::AccountId, BalanceOf<T>>> for BoundedExposurePage<T> {
794 fn from(value: ExposurePage<T::AccountId, BalanceOf<T>>) -> Self {
795 Self(value)
796 }
797 }
798
799 impl<T: Config> From<BoundedExposurePage<T>> for ExposurePage<T::AccountId, BalanceOf<T>> {
800 fn from(value: BoundedExposurePage<T>) -> Self {
801 value.0
802 }
803 }
804
805 impl<T: Config> codec::EncodeLike<BoundedExposurePage<T>>
806 for ExposurePage<T::AccountId, BalanceOf<T>>
807 {
808 }
809
810 #[pallet::storage]
817 pub type ErasStakersPaged<T: Config> = StorageNMap<
818 _,
819 (
820 NMapKey<Twox64Concat, EraIndex>,
821 NMapKey<Twox64Concat, T::AccountId>,
822 NMapKey<Twox64Concat, Page>,
823 ),
824 BoundedExposurePage<T>,
825 OptionQuery,
826 >;
827
828 pub struct ClaimedRewardsBound<T>(core::marker::PhantomData<T>);
829 impl<T: Config> Get<u32> for ClaimedRewardsBound<T> {
830 fn get() -> u32 {
831 let max_total_nominators_per_validator =
832 <T::ElectionProvider as ElectionProvider>::MaxBackersPerWinnerFinal::get();
833 let exposure_page_size = T::MaxExposurePageSize::get();
834 max_total_nominators_per_validator
835 .saturating_div(exposure_page_size)
836 .saturating_add(1)
837 }
838 }
839
840 #[pallet::storage]
847 pub type ClaimedRewards<T: Config> = StorageDoubleMap<
848 _,
849 Twox64Concat,
850 EraIndex,
851 Twox64Concat,
852 T::AccountId,
853 WeakBoundedVec<Page, ClaimedRewardsBound<T>>,
854 ValueQuery,
855 >;
856
857 #[pallet::storage]
864 pub type ErasValidatorPrefs<T: Config> = StorageDoubleMap<
865 _,
866 Twox64Concat,
867 EraIndex,
868 Twox64Concat,
869 T::AccountId,
870 ValidatorPrefs,
871 ValueQuery,
872 >;
873
874 #[pallet::storage]
880 pub type ErasValidatorReward<T: Config> = StorageMap<_, Twox64Concat, EraIndex, BalanceOf<T>>;
881
882 #[pallet::storage]
885 pub type ErasRewardPoints<T: Config> =
886 StorageMap<_, Twox64Concat, EraIndex, EraRewardPoints<T>, ValueQuery>;
887
888 #[pallet::storage]
891 pub type ErasTotalStake<T: Config> =
892 StorageMap<_, Twox64Concat, EraIndex, BalanceOf<T>, ValueQuery>;
893
894 #[pallet::storage]
896 pub type ForceEra<T> = StorageValue<_, Forcing, ValueQuery>;
897
898 #[pallet::storage]
903 pub type MaxStakedRewards<T> = StorageValue<_, Percent, OptionQuery>;
904
905 #[pallet::storage]
909 pub type SlashRewardFraction<T> = StorageValue<_, Perbill, ValueQuery>;
910
911 #[pallet::storage]
914 pub type CanceledSlashPayout<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;
915
916 #[pallet::storage]
927 pub type OffenceQueue<T: Config> = StorageDoubleMap<
928 _,
929 Twox64Concat,
930 EraIndex,
931 Twox64Concat,
932 T::AccountId,
933 slashing::OffenceRecord<T::AccountId>,
934 >;
935
936 #[pallet::storage]
949 pub type OffenceQueueEras<T: Config> =
950 StorageValue<_, WeakBoundedVec<u32, OffenceQueueErasBound<T>>>;
951
952 #[pallet::storage]
965 pub type ProcessingOffence<T: Config> =
966 StorageValue<_, (EraIndex, T::AccountId, slashing::OffenceRecord<T::AccountId>)>;
967
968 #[pallet::storage]
970 pub type UnappliedSlashes<T: Config> = StorageDoubleMap<
971 _,
972 Twox64Concat,
973 EraIndex,
974 Twox64Concat,
975 (T::AccountId, Perbill, u32),
977 UnappliedSlash<T>,
978 OptionQuery,
979 >;
980
981 #[pallet::storage]
987 pub type CancelledSlashes<T: Config> = StorageMap<
988 _,
989 Twox64Concat,
990 EraIndex,
991 BoundedVec<(T::AccountId, Perbill), T::MaxValidatorSet>,
992 ValueQuery,
993 >;
994
995 #[pallet::storage]
998 pub type ValidatorSlashInEra<T: Config> = StorageDoubleMap<
999 _,
1000 Twox64Concat,
1001 EraIndex,
1002 Twox64Concat,
1003 T::AccountId,
1004 (Perbill, BalanceOf<T>),
1005 >;
1006
1007 #[pallet::storage]
1011 pub type ChillThreshold<T: Config> = StorageValue<_, Percent, OptionQuery>;
1012
1013 #[pallet::storage]
1018 pub type VoterSnapshotStatus<T: Config> =
1019 StorageValue<_, SnapshotStatus<T::AccountId>, ValueQuery>;
1020
1021 #[pallet::storage]
1028 pub type NextElectionPage<T: Config> = StorageValue<_, PageIndex, OptionQuery>;
1029
1030 #[pallet::storage]
1032 pub type ElectableStashes<T: Config> =
1033 StorageValue<_, BoundedBTreeSet<T::AccountId, T::MaxValidatorSet>, ValueQuery>;
1034
1035 #[pallet::storage]
1037 pub type EraPruningState<T: Config> = StorageMap<_, Twox64Concat, EraIndex, PruningStep>;
1038
1039 #[pallet::genesis_config]
1040 #[derive(frame_support::DefaultNoBound, frame_support::DebugNoBound)]
1041 pub struct GenesisConfig<T: Config> {
1042 pub validator_count: u32,
1043 pub force_era: Forcing,
1044 pub slash_reward_fraction: Perbill,
1045 pub canceled_payout: BalanceOf<T>,
1046 pub stakers: Vec<(T::AccountId, BalanceOf<T>, crate::StakerStatus<T::AccountId>)>,
1047 pub min_nominator_bond: BalanceOf<T>,
1048 pub min_validator_bond: BalanceOf<T>,
1049 pub max_validator_count: Option<u32>,
1050 pub max_nominator_count: Option<u32>,
1051 pub dev_stakers: Option<(u32, u32)>,
1058 pub active_era: (u32, u32, u64),
1060 }
1061
1062 impl<T: Config> GenesisConfig<T> {
1063 fn generate_endowed_bonded_account(derivation: &str, rng: &mut ChaChaRng) -> T::AccountId {
1064 let pair: SrPair = Pair::from_string(&derivation, None)
1065 .expect(&format!("Failed to parse derivation string: {derivation}"));
1066 let who = T::AccountId::decode(&mut &pair.public().encode()[..])
1067 .expect(&format!("Failed to decode public key from pair: {:?}", pair.public()));
1068
1069 let (min, max) = T::VoterList::range();
1070 let stake = BalanceOf::<T>::from(rng.next_u64().min(max).max(min));
1071 let two: BalanceOf<T> = 2u32.into();
1072
1073 assert_ok!(T::Currency::mint_into(&who, stake * two));
1074 assert_ok!(<Pallet<T>>::bond(
1075 T::RuntimeOrigin::from(Some(who.clone()).into()),
1076 stake,
1077 RewardDestination::Staked,
1078 ));
1079 who
1080 }
1081 }
1082
1083 #[pallet::genesis_build]
1084 impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
1085 fn build(&self) {
1086 crate::log!(trace, "initializing with {:?}", self);
1087 assert!(
1088 self.validator_count <=
1089 <T::ElectionProvider as ElectionProvider>::MaxWinnersPerPage::get() *
1090 <T::ElectionProvider as ElectionProvider>::Pages::get(),
1091 "validator count is too high, `ElectionProvider` can never fulfill this"
1092 );
1093 ValidatorCount::<T>::put(self.validator_count);
1094
1095 ForceEra::<T>::put(self.force_era);
1096 CanceledSlashPayout::<T>::put(self.canceled_payout);
1097 SlashRewardFraction::<T>::put(self.slash_reward_fraction);
1098 MinNominatorBond::<T>::put(self.min_nominator_bond);
1099 MinValidatorBond::<T>::put(self.min_validator_bond);
1100 if let Some(x) = self.max_validator_count {
1101 MaxValidatorsCount::<T>::put(x);
1102 }
1103 if let Some(x) = self.max_nominator_count {
1104 MaxNominatorsCount::<T>::put(x);
1105 }
1106
1107 for &(ref stash, balance, ref status) in &self.stakers {
1109 match status {
1110 crate::StakerStatus::Validator => {
1111 crate::log!(
1112 trace,
1113 "inserting genesis validator: {:?} => {:?} => {:?}",
1114 stash,
1115 balance,
1116 status
1117 );
1118 assert!(
1119 asset::free_to_stake::<T>(stash) >= balance,
1120 "Stash does not have enough balance to bond."
1121 );
1122 assert_ok!(<Pallet<T>>::bond(
1123 T::RuntimeOrigin::from(Some(stash.clone()).into()),
1124 balance,
1125 RewardDestination::Staked,
1126 ));
1127 assert_ok!(<Pallet<T>>::validate(
1128 T::RuntimeOrigin::from(Some(stash.clone()).into()),
1129 Default::default(),
1130 ));
1131 },
1132 crate::StakerStatus::Idle => {
1133 crate::log!(
1134 trace,
1135 "inserting genesis idle staker: {:?} => {:?} => {:?}",
1136 stash,
1137 balance,
1138 status
1139 );
1140 assert!(
1141 asset::free_to_stake::<T>(stash) >= balance,
1142 "Stash does not have enough balance to bond."
1143 );
1144 assert_ok!(<Pallet<T>>::bond(
1145 T::RuntimeOrigin::from(Some(stash.clone()).into()),
1146 balance,
1147 RewardDestination::Staked,
1148 ));
1149 },
1150 _ => {},
1151 }
1152 }
1153
1154 for &(ref stash, balance, ref status) in &self.stakers {
1156 match status {
1157 crate::StakerStatus::Nominator(votes) => {
1158 crate::log!(
1159 trace,
1160 "inserting genesis nominator: {:?} => {:?} => {:?}",
1161 stash,
1162 balance,
1163 status
1164 );
1165 assert!(
1166 asset::free_to_stake::<T>(stash) >= balance,
1167 "Stash does not have enough balance to bond."
1168 );
1169 assert_ok!(<Pallet<T>>::bond(
1170 T::RuntimeOrigin::from(Some(stash.clone()).into()),
1171 balance,
1172 RewardDestination::Staked,
1173 ));
1174 assert_ok!(<Pallet<T>>::nominate(
1175 T::RuntimeOrigin::from(Some(stash.clone()).into()),
1176 votes.iter().map(|l| T::Lookup::unlookup(l.clone())).collect(),
1177 ));
1178 },
1179 _ => {},
1180 }
1181 }
1182
1183 assert_eq!(
1185 T::VoterList::count(),
1186 Nominators::<T>::count() + Validators::<T>::count(),
1187 "not all genesis stakers were inserted into sorted list provider, something is wrong."
1188 );
1189
1190 if let Some((validators, nominators)) = self.dev_stakers {
1192 crate::log!(
1193 debug,
1194 "generating dev stakers: validators: {}, nominators: {}",
1195 validators,
1196 nominators
1197 );
1198 let base_derivation = "//staker//{}";
1199
1200 let mut rng =
1203 ChaChaRng::from_seed(base_derivation.using_encoded(sp_core::blake2_256));
1204
1205 (0..validators).for_each(|index| {
1206 let derivation = base_derivation.replace("{}", &format!("validator{}", index));
1207 let who = Self::generate_endowed_bonded_account(&derivation, &mut rng);
1208 assert_ok!(<Pallet<T>>::validate(
1209 T::RuntimeOrigin::from(Some(who.clone()).into()),
1210 Default::default(),
1211 ));
1212 });
1213
1214 let all_validators = Validators::<T>::iter_keys().collect::<Vec<_>>();
1217
1218 (0..nominators).for_each(|index| {
1219 let derivation = base_derivation.replace("{}", &format!("nominator{}", index));
1220 let who = Self::generate_endowed_bonded_account(&derivation, &mut rng);
1221
1222 let random_nominations = all_validators
1223 .choose_multiple(&mut rng, MaxNominationsOf::<T>::get() as usize)
1224 .map(|v| v.clone())
1225 .collect::<Vec<_>>();
1226
1227 assert_ok!(<Pallet<T>>::nominate(
1228 T::RuntimeOrigin::from(Some(who.clone()).into()),
1229 random_nominations.iter().map(|l| T::Lookup::unlookup(l.clone())).collect(),
1230 ));
1231 })
1232 }
1233
1234 let (active_era, session_index, timestamp) = self.active_era;
1235 ActiveEra::<T>::put(ActiveEraInfo { index: active_era, start: Some(timestamp) });
1236 CurrentEra::<T>::put(active_era);
1238 BondedEras::<T>::put(
1240 BoundedVec::<_, BondedErasBound<T>>::try_from(
1241 alloc::vec![(active_era, session_index)]
1242 )
1243 .expect("bound for BondedEras is BondingDuration + 1; can contain at least one element; qed")
1244 );
1245 }
1246 }
1247
1248 #[pallet::event]
1249 #[pallet::generate_deposit(pub fn deposit_event)]
1250 pub enum Event<T: Config> {
1251 EraPaid {
1257 era_index: EraIndex,
1258 validator_payout: BalanceOf<T>,
1259 remainder: BalanceOf<T>,
1260 },
1261 Rewarded {
1263 stash: T::AccountId,
1264 dest: RewardDestination<T::AccountId>,
1265 amount: BalanceOf<T>,
1266 },
1267 Slashed {
1269 staker: T::AccountId,
1270 amount: BalanceOf<T>,
1271 },
1272 OldSlashingReportDiscarded {
1275 session_index: SessionIndex,
1276 },
1277 Bonded {
1282 stash: T::AccountId,
1283 amount: BalanceOf<T>,
1284 },
1285 Unbonded {
1287 stash: T::AccountId,
1288 amount: BalanceOf<T>,
1289 },
1290 Withdrawn {
1293 stash: T::AccountId,
1294 amount: BalanceOf<T>,
1295 },
1296 StakerRemoved {
1299 stash: T::AccountId,
1300 },
1301 Kicked {
1303 nominator: T::AccountId,
1304 stash: T::AccountId,
1305 },
1306 Chilled {
1308 stash: T::AccountId,
1309 },
1310 PayoutStarted {
1312 era_index: EraIndex,
1313 validator_stash: T::AccountId,
1314 page: Page,
1315 next: Option<Page>,
1316 },
1317 ValidatorPrefsSet {
1319 stash: T::AccountId,
1320 prefs: ValidatorPrefs,
1321 },
1322 SnapshotVotersSizeExceeded {
1324 size: u32,
1325 },
1326 SnapshotTargetsSizeExceeded {
1328 size: u32,
1329 },
1330 ForceEra {
1331 mode: Forcing,
1332 },
1333 ControllerBatchDeprecated {
1335 failures: u32,
1336 },
1337 CurrencyMigrated {
1340 stash: T::AccountId,
1341 force_withdraw: BalanceOf<T>,
1342 },
1343 PagedElectionProceeded {
1353 page: PageIndex,
1354 result: Result<u32, u32>,
1355 },
1356 OffenceReported {
1359 offence_era: EraIndex,
1360 validator: T::AccountId,
1361 fraction: Perbill,
1362 },
1363 SlashComputed {
1365 offence_era: EraIndex,
1366 slash_era: EraIndex,
1367 offender: T::AccountId,
1368 page: u32,
1369 },
1370 SlashCancelled {
1372 slash_era: EraIndex,
1373 validator: T::AccountId,
1374 },
1375 SessionRotated {
1380 starting_session: SessionIndex,
1381 active_era: EraIndex,
1382 planned_era: EraIndex,
1383 },
1384 Unexpected(UnexpectedKind<T>),
1387 OffenceTooOld {
1389 offence_era: EraIndex,
1390 validator: T::AccountId,
1391 fraction: Perbill,
1392 },
1393 EraPruned {
1395 index: EraIndex,
1396 },
1397 ValidatorIncentivePaid {
1399 era: EraIndex,
1400 validator_stash: T::AccountId,
1401 dest: RewardDestination<T::AccountId>,
1402 amount: BalanceOf<T>,
1403 },
1404 ValidatorIncentiveConfigSet {
1406 optimum_self_stake: BalanceOf<T>,
1407 hard_cap_self_stake: BalanceOf<T>,
1408 slope_factor: Perbill,
1409 },
1410 }
1411
1412 #[derive(Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, TypeInfo, DebugNoBound)]
1418 #[codec(mel_bound())]
1419 #[scale_info(skip_type_params(T))]
1420 pub enum UnexpectedKind<T: Config> {
1421 EraDurationBoundExceeded,
1423 UnknownValidatorActivation,
1425 PagedElectionOutOfWeight { page: PageIndex, required: Weight, had: Weight },
1427 MissingPayee { era: EraIndex, stash: T::AccountId },
1429 ValidatorIncentiveWeightMismatch { era: EraIndex },
1431 ValidatorIncentiveTransferFailed { era: EraIndex },
1433 }
1434
1435 #[pallet::error]
1436 #[derive(PartialEq)]
1437 pub enum Error<T> {
1438 NotController,
1440 NotStash,
1442 AlreadyBonded,
1444 AlreadyPaired,
1446 EmptyTargets,
1448 DuplicateIndex,
1450 InvalidSlashRecord,
1452 InsufficientBond,
1456 NoMoreChunks,
1458 NoUnlockChunk,
1460 FundedTarget,
1462 InvalidEraToReward,
1464 InvalidNumberOfNominations,
1466 AlreadyClaimed,
1468 InvalidPage,
1470 IncorrectHistoryDepth,
1472 BadState,
1474 TooManyTargets,
1476 BadTarget,
1478 CannotChillOther,
1480 TooManyNominators,
1483 TooManyValidators,
1486 CommissionTooLow,
1488 BoundNotMet,
1490 ControllerDeprecated,
1492 CannotRestoreLedger,
1494 RewardDestinationRestricted,
1496 NotEnoughFunds,
1498 VirtualStakerNotAllowed,
1500 CannotReapStash,
1502 AlreadyMigrated,
1504 EraNotStarted,
1506 Restricted,
1509 UnappliedSlashesInPreviousEra,
1512 EraNotPrunable,
1514 CancelledSlash,
1516 CommissionTooHigh,
1518 OptimumGreaterThanCap,
1520 }
1521
1522 impl<T: Config> Pallet<T> {
1523 pub fn apply_unapplied_slashes(active_era: EraIndex) -> Weight {
1525 let mut slashes = UnappliedSlashes::<T>::iter_prefix(&active_era).take(1);
1526 if let Some((key, slash)) = slashes.next() {
1527 crate::log!(
1528 debug,
1529 "🦹 found slash {:?} scheduled to be executed in era {:?}",
1530 slash,
1531 active_era,
1532 );
1533
1534 let nominators_slashed = slash.others.len() as u32;
1535
1536 if Self::check_slash_cancelled(active_era, &key.0, key.1) {
1538 crate::log!(
1539 debug,
1540 "🦹 slash for {:?} in era {:?} was cancelled, skipping",
1541 key.0,
1542 active_era,
1543 );
1544 } else {
1545 slashing::apply_slash::<T>(slash, Self::offence_era_of(active_era));
1546 }
1547
1548 UnappliedSlashes::<T>::remove(&active_era, &key);
1550
1551 if UnappliedSlashes::<T>::iter_prefix(&active_era).next().is_none() {
1553 CancelledSlashes::<T>::remove(&active_era);
1555 }
1556
1557 T::WeightInfo::apply_slash(nominators_slashed)
1558 } else {
1559 T::DbWeight::get().reads(1)
1561 }
1562 }
1563
1564 fn do_prune_era_step(era: EraIndex) -> Result<Weight, DispatchError> {
1566 let current_step = EraPruningState::<T>::get(era).ok_or(Error::<T>::EraNotPrunable)?;
1571
1572 let items_limit = T::MaxPruningItems::get().min(T::MaxValidatorSet::get());
1575
1576 let actual_weight = match current_step {
1577 PruningStep::ErasStakersPaged => {
1578 let result = ErasStakersPaged::<T>::clear_prefix((era,), items_limit, None);
1579 let items_deleted = result.backend as u32;
1580 result.maybe_cursor.is_none().then(|| {
1581 EraPruningState::<T>::insert(era, PruningStep::ErasStakersOverview)
1582 });
1583 T::WeightInfo::prune_era_stakers_paged(items_deleted)
1584 },
1585 PruningStep::ErasStakersOverview => {
1586 let result = ErasStakersOverview::<T>::clear_prefix(era, items_limit, None);
1587 let items_deleted = result.backend as u32;
1588 result.maybe_cursor.is_none().then(|| {
1589 EraPruningState::<T>::insert(era, PruningStep::ErasValidatorPrefs)
1590 });
1591 T::WeightInfo::prune_era_stakers_overview(items_deleted)
1592 },
1593 PruningStep::ErasValidatorPrefs => {
1594 let result = ErasValidatorPrefs::<T>::clear_prefix(era, items_limit, None);
1595 let items_deleted = result.backend as u32;
1596 result
1597 .maybe_cursor
1598 .is_none()
1599 .then(|| EraPruningState::<T>::insert(era, PruningStep::ClaimedRewards));
1600 T::WeightInfo::prune_era_validator_prefs(items_deleted)
1601 },
1602 PruningStep::ClaimedRewards => {
1603 let result = ClaimedRewards::<T>::clear_prefix(era, items_limit, None);
1604 let items_deleted = result.backend as u32;
1605 result.maybe_cursor.is_none().then(|| {
1606 EraPruningState::<T>::insert(era, PruningStep::ErasValidatorReward)
1607 });
1608 T::WeightInfo::prune_era_claimed_rewards(items_deleted)
1609 },
1610 PruningStep::ErasValidatorReward => {
1611 ErasValidatorReward::<T>::remove(era);
1612 EraPruningState::<T>::insert(era, PruningStep::ErasRewardPoints);
1613 T::WeightInfo::prune_era_validator_reward()
1614 },
1615 PruningStep::ErasRewardPoints => {
1616 ErasRewardPoints::<T>::remove(era);
1617 EraPruningState::<T>::insert(era, PruningStep::SingleEntryCleanups);
1618 T::WeightInfo::prune_era_reward_points()
1619 },
1620 PruningStep::SingleEntryCleanups => {
1621 ErasTotalStake::<T>::remove(era);
1622 ErasNominatorsSlashable::<T>::remove(era);
1623 ErasValidatorIncentiveBudget::<T>::remove(era);
1624 ErasSumValidatorIncentiveWeight::<T>::remove(era);
1625 EraPruningState::<T>::insert(era, PruningStep::ValidatorSlashInEra);
1626 T::WeightInfo::prune_era_single_entry_cleanups()
1627 },
1628 PruningStep::ValidatorSlashInEra => {
1629 let result = ValidatorSlashInEra::<T>::clear_prefix(era, items_limit, None);
1630 let items_deleted = result.backend as u32;
1631
1632 if result.maybe_cursor.is_none() {
1633 EraPruningState::<T>::insert(
1634 era,
1635 PruningStep::ErasValidatorIncentiveWeight,
1636 );
1637 }
1638
1639 T::WeightInfo::prune_era_validator_slash_in_era(items_deleted)
1640 },
1641 PruningStep::ErasValidatorIncentiveWeight => {
1642 let result =
1643 ErasValidatorIncentiveWeight::<T>::clear_prefix(era, items_limit, None);
1644 if result.maybe_cursor.is_none() {
1645 EraPruningState::<T>::remove(era);
1647 }
1648 T::WeightInfo::prune_era_validator_incentive_weight(result.backend as u32)
1649 },
1650 };
1651
1652 if EraPruningState::<T>::get(era).is_none() {
1654 Self::deposit_event(Event::<T>::EraPruned { index: era });
1655 }
1656
1657 Ok(actual_weight)
1658 }
1659 }
1660
1661 #[pallet::hooks]
1662 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
1663 fn on_poll(_now: BlockNumberFor<T>, weight_meter: &mut WeightMeter) {
1664 let (weight, exec) = EraElectionPlanner::<T>::maybe_fetch_election_results();
1665 crate::log!(
1666 trace,
1667 "weight of fetching next election page is {:?}, have {:?}",
1668 weight,
1669 weight_meter.remaining()
1670 );
1671
1672 if weight_meter.can_consume(weight) {
1673 exec(weight_meter);
1674 } else {
1675 Self::deposit_event(Event::<T>::Unexpected(
1676 UnexpectedKind::PagedElectionOutOfWeight {
1677 page: NextElectionPage::<T>::get().unwrap_or(
1678 EraElectionPlanner::<T>::election_pages().defensive_saturating_sub(1),
1679 ),
1680 required: weight,
1681 had: weight_meter.remaining(),
1682 },
1683 ));
1684 }
1685 }
1686
1687 fn on_initialize(_now: BlockNumberFor<T>) -> Weight {
1688 let mut consumed_weight = slashing::process_offence_for_era::<T>();
1690
1691 consumed_weight.saturating_accrue(T::DbWeight::get().reads(1));
1693 if let Some(active_era) = ActiveEra::<T>::get() {
1694 let slash_weight = Self::apply_unapplied_slashes(active_era.index);
1695 consumed_weight.saturating_accrue(slash_weight);
1696 }
1697
1698 consumed_weight
1699 }
1700
1701 fn integrity_test() {
1702 assert_eq!(
1704 MaxNominationsOf::<T>::get(),
1705 <Self as ElectionDataProvider>::MaxVotesPerVoter::get()
1706 );
1707
1708 assert!(!MaxNominationsOf::<T>::get().is_zero());
1710
1711 assert!(
1712 T::SlashDeferDuration::get() < T::BondingDuration::get() || T::BondingDuration::get() == 0,
1713 "As per documentation, slash defer duration ({}) should be less than bonding duration ({}).",
1714 T::SlashDeferDuration::get(),
1715 T::BondingDuration::get(),
1716 );
1717
1718 assert!(
1720 T::NominatorFastUnbondDuration::get() <= T::BondingDuration::get(),
1721 "NominatorFastUnbondDuration ({}) must not exceed BondingDuration ({}).",
1722 T::NominatorFastUnbondDuration::get(),
1723 T::BondingDuration::get(),
1724 );
1725 assert!(
1727 T::MaxPruningItems::get() >= 100,
1728 "MaxPruningItems must be at least 100 for efficient pruning, got: {}",
1729 T::MaxPruningItems::get()
1730 );
1731
1732 assert!(
1733 crate::POT_POOL_SIZE > T::HistoryDepth::get(),
1734 "POT_POOL_SIZE ({}) must be strictly greater than HistoryDepth ({}) \
1735 to avoid reusing a pot slot whose era is still in the active history.",
1736 crate::POT_POOL_SIZE,
1737 T::HistoryDepth::get(),
1738 );
1739
1740 if T::DisableMinting::get() {
1742 let (v, r) = T::EraPayout::era_payout(
1743 BalanceOf::<T>::from(1u64),
1744 BalanceOf::<T>::from(1u64),
1745 1000u64,
1746 );
1747 assert!(
1748 v.is_zero() && r.is_zero(),
1749 "DisableMinting is true but EraPayout returns non-zero. \
1750 Set EraPayout = () when DisableMinting = true."
1751 );
1752 }
1753 }
1754
1755 #[cfg(feature = "try-runtime")]
1756 fn try_state(n: BlockNumberFor<T>) -> Result<(), sp_runtime::TryRuntimeError> {
1757 Self::do_try_state(n)
1758 }
1759 }
1760
1761 #[pallet::call]
1762 impl<T: Config> Pallet<T> {
1763 #[pallet::call_index(0)]
1776 #[pallet::weight(T::WeightInfo::bond())]
1777 pub fn bond(
1778 origin: OriginFor<T>,
1779 #[pallet::compact] value: BalanceOf<T>,
1780 payee: RewardDestination<T::AccountId>,
1781 ) -> DispatchResult {
1782 let stash = ensure_signed(origin)?;
1783
1784 ensure!(!T::Filter::contains(&stash), Error::<T>::Restricted);
1785
1786 if StakingLedger::<T>::is_bonded(StakingAccount::Stash(stash.clone())) {
1787 return Err(Error::<T>::AlreadyBonded.into());
1788 }
1789
1790 if StakingLedger::<T>::is_bonded(StakingAccount::Controller(stash.clone())) {
1792 return Err(Error::<T>::AlreadyPaired.into());
1793 }
1794
1795 if value < Self::min_chilled_bond() {
1797 return Err(Error::<T>::InsufficientBond.into());
1798 }
1799
1800 let stash_balance = asset::free_to_stake::<T>(&stash);
1801 let value = value.min(stash_balance);
1802 Self::deposit_event(Event::<T>::Bonded { stash: stash.clone(), amount: value });
1803 let ledger = StakingLedger::<T>::new(stash.clone(), value);
1804
1805 ledger.bond(payee)?;
1808
1809 Ok(())
1810 }
1811
1812 #[pallet::call_index(1)]
1823 #[pallet::weight(T::WeightInfo::bond_extra())]
1824 pub fn bond_extra(
1825 origin: OriginFor<T>,
1826 #[pallet::compact] max_additional: BalanceOf<T>,
1827 ) -> DispatchResult {
1828 let stash = ensure_signed(origin)?;
1829 ensure!(!T::Filter::contains(&stash), Error::<T>::Restricted);
1830 Self::do_bond_extra(&stash, max_additional)
1831 }
1832
1833 #[pallet::call_index(2)]
1853 #[pallet::weight(
1854 T::WeightInfo::withdraw_unbonded_kill().saturating_add(T::WeightInfo::unbond()))
1855 ]
1856 pub fn unbond(
1857 origin: OriginFor<T>,
1858 #[pallet::compact] value: BalanceOf<T>,
1859 ) -> DispatchResultWithPostInfo {
1860 let controller = ensure_signed(origin)?;
1861 let unlocking =
1862 Self::ledger(Controller(controller.clone())).map(|l| l.unlocking.len())?;
1863
1864 let maybe_withdraw_weight = {
1867 if unlocking == T::MaxUnlockingChunks::get() as usize {
1868 Some(Self::do_withdraw_unbonded(&controller)?)
1869 } else {
1870 None
1871 }
1872 };
1873
1874 let mut ledger = Self::ledger(Controller(controller))?;
1877 let mut value = value.min(ledger.active);
1878 let stash = ledger.stash.clone();
1879
1880 let chill_weight = if value >= ledger.active {
1883 Self::chill_stash(&stash);
1884 T::WeightInfo::chill()
1885 } else {
1886 Weight::zero()
1887 };
1888
1889 ensure!(
1890 ledger.unlocking.len() < T::MaxUnlockingChunks::get() as usize,
1891 Error::<T>::NoMoreChunks,
1892 );
1893
1894 if !value.is_zero() {
1895 ledger.active -= value;
1896
1897 if ledger.active < asset::existential_deposit::<T>() {
1899 value += ledger.active;
1900 ledger.active = Zero::zero();
1901 }
1902
1903 let is_nominator = Nominators::<T>::contains_key(&stash);
1904
1905 let min_active_bond = if is_nominator {
1906 Self::min_nominator_bond()
1907 } else if Validators::<T>::contains_key(&stash) {
1908 Self::min_validator_bond()
1909 } else {
1910 Zero::zero()
1912 };
1913
1914 ensure!(ledger.active >= min_active_bond, Error::<T>::InsufficientBond);
1917
1918 let active_era = session_rotation::Rotator::<T>::active_era();
1924 let was_recent_validator = LastValidatorEra::<T>::get(&stash)
1925 .map(|last_era| active_era.saturating_sub(last_era) < T::BondingDuration::get())
1926 .unwrap_or(false);
1927
1928 let unbond_duration = if was_recent_validator {
1929 T::BondingDuration::get()
1931 } else {
1932 <Self as sp_staking::StakingInterface>::nominator_bonding_duration()
1934 };
1935
1936 let era =
1937 session_rotation::Rotator::<T>::active_era().saturating_add(unbond_duration);
1938 if let Some(chunk) = ledger.unlocking.last_mut().filter(|chunk| chunk.era == era) {
1939 chunk.value = chunk.value.defensive_saturating_add(value)
1943 } else {
1944 ledger
1945 .unlocking
1946 .try_push(UnlockChunk { value, era })
1947 .map_err(|_| Error::<T>::NoMoreChunks)?;
1948 };
1949 ledger.update()?;
1951
1952 if T::VoterList::contains(&stash) {
1954 let _ = T::VoterList::on_update(&stash, Self::weight_of(&stash));
1955 }
1956
1957 Self::deposit_event(Event::<T>::Unbonded { stash, amount: value });
1958 }
1959
1960 let actual_weight = if let Some(withdraw_weight) = maybe_withdraw_weight {
1961 Some(
1962 T::WeightInfo::unbond()
1963 .saturating_add(withdraw_weight)
1964 .saturating_add(chill_weight),
1965 )
1966 } else {
1967 Some(T::WeightInfo::unbond().saturating_add(chill_weight))
1968 };
1969
1970 Ok(actual_weight.into())
1971 }
1972
1973 #[pallet::call_index(3)]
1993 #[pallet::weight(T::WeightInfo::withdraw_unbonded_kill())]
1994 pub fn withdraw_unbonded(
1995 origin: OriginFor<T>,
1996 _num_slashing_spans: u32,
1997 ) -> DispatchResultWithPostInfo {
1998 let controller = ensure_signed(origin)?;
1999
2000 let actual_weight = Self::do_withdraw_unbonded(&controller)?;
2001 Ok(Some(actual_weight).into())
2002 }
2003
2004 #[pallet::call_index(4)]
2010 #[pallet::weight(T::WeightInfo::validate())]
2011 pub fn validate(origin: OriginFor<T>, prefs: ValidatorPrefs) -> DispatchResult {
2012 let controller = ensure_signed(origin)?;
2013
2014 let ledger = Self::ledger(Controller(controller))?;
2015
2016 ensure!(ledger.active >= Self::min_validator_bond(), Error::<T>::InsufficientBond);
2017 let stash = &ledger.stash;
2018
2019 ensure!(prefs.commission >= MinCommission::<T>::get(), Error::<T>::CommissionTooLow);
2021 ensure!(prefs.commission <= MaxCommission::<T>::get(), Error::<T>::CommissionTooHigh);
2022
2023 if !Validators::<T>::contains_key(stash) {
2025 if let Some(max_validators) = MaxValidatorsCount::<T>::get() {
2029 ensure!(
2030 Validators::<T>::count() < max_validators,
2031 Error::<T>::TooManyValidators
2032 );
2033 }
2034 }
2035
2036 Self::do_remove_nominator(stash);
2037 Self::do_add_validator(stash, prefs.clone());
2038 Self::deposit_event(Event::<T>::ValidatorPrefsSet { stash: ledger.stash, prefs });
2039
2040 Ok(())
2041 }
2042
2043 #[pallet::call_index(5)]
2049 #[pallet::weight(T::WeightInfo::nominate(targets.len() as u32))]
2050 pub fn nominate(
2051 origin: OriginFor<T>,
2052 targets: Vec<AccountIdLookupOf<T>>,
2053 ) -> DispatchResult {
2054 let controller = ensure_signed(origin)?;
2055
2056 let ledger = Self::ledger(StakingAccount::Controller(controller.clone()))?;
2057
2058 ensure!(ledger.active >= Self::min_nominator_bond(), Error::<T>::InsufficientBond);
2059 let stash = &ledger.stash;
2060
2061 if !Nominators::<T>::contains_key(stash) {
2063 if let Some(max_nominators) = MaxNominatorsCount::<T>::get() {
2067 ensure!(
2068 Nominators::<T>::count() < max_nominators,
2069 Error::<T>::TooManyNominators
2070 );
2071 }
2072 }
2073
2074 let mut targets = targets
2076 .into_iter()
2077 .map(|t| T::Lookup::lookup(t).map_err(DispatchError::from))
2078 .collect::<Result<Vec<_>, _>>()?;
2079 targets.sort();
2080 targets.dedup();
2081
2082 ensure!(!targets.is_empty(), Error::<T>::EmptyTargets);
2083 ensure!(
2084 targets.len() <= T::NominationsQuota::get_quota(ledger.active) as usize,
2085 Error::<T>::TooManyTargets
2086 );
2087
2088 let old = Nominators::<T>::get(stash).map_or_else(Vec::new, |x| x.targets.into_inner());
2089
2090 let targets: BoundedVec<_, _> = targets
2091 .into_iter()
2092 .map(|n| {
2093 if old.contains(&n) ||
2094 (Validators::<T>::contains_key(&n) && !Validators::<T>::get(&n).blocked)
2095 {
2096 Ok(n)
2097 } else {
2098 Err(Error::<T>::BadTarget.into())
2099 }
2100 })
2101 .collect::<Result<Vec<_>, DispatchError>>()?
2102 .try_into()
2103 .map_err(|_| Error::<T>::TooManyNominators)?;
2104
2105 let nominations = Nominations {
2106 targets,
2107 submitted_in: CurrentEra::<T>::get().unwrap_or(0),
2109 suppressed: false,
2110 };
2111
2112 Self::do_remove_validator(stash);
2113 Self::do_add_nominator(stash, nominations);
2114 Ok(())
2115 }
2116
2117 #[pallet::call_index(6)]
2128 #[pallet::weight(T::WeightInfo::chill())]
2129 pub fn chill(origin: OriginFor<T>) -> DispatchResult {
2130 let controller = ensure_signed(origin)?;
2131
2132 let ledger = Self::ledger(StakingAccount::Controller(controller))?;
2133
2134 Self::chill_stash(&ledger.stash);
2135 Ok(())
2136 }
2137
2138 #[pallet::call_index(7)]
2144 #[pallet::weight(T::WeightInfo::set_payee())]
2145 pub fn set_payee(
2146 origin: OriginFor<T>,
2147 payee: RewardDestination<T::AccountId>,
2148 ) -> DispatchResult {
2149 let controller = ensure_signed(origin)?;
2150 let ledger = Self::ledger(Controller(controller.clone()))?;
2151
2152 ensure!(
2153 (payee != {
2154 #[allow(deprecated)]
2155 RewardDestination::Controller
2156 }),
2157 Error::<T>::ControllerDeprecated
2158 );
2159
2160 let _ = ledger
2161 .set_payee(payee)
2162 .defensive_proof("ledger was retrieved from storage, thus it's bonded; qed.")?;
2163
2164 Ok(())
2165 }
2166
2167 #[pallet::call_index(8)]
2176 #[pallet::weight(T::WeightInfo::set_controller())]
2177 pub fn set_controller(origin: OriginFor<T>) -> DispatchResult {
2178 let stash = ensure_signed(origin)?;
2179
2180 Self::ledger(StakingAccount::Stash(stash.clone())).map(|ledger| {
2181 let controller = ledger.controller()
2182 .defensive_proof("Ledger's controller field didn't exist. The controller should have been fetched using StakingLedger.")
2183 .ok_or(Error::<T>::NotController)?;
2184
2185 if controller == stash {
2186 return Err(Error::<T>::AlreadyPaired.into())
2188 }
2189
2190 let _ = ledger.set_controller_to_stash()?;
2191 Ok(())
2192 })?
2193 }
2194
2195 #[pallet::call_index(9)]
2199 #[pallet::weight(T::WeightInfo::set_validator_count())]
2200 pub fn set_validator_count(
2201 origin: OriginFor<T>,
2202 #[pallet::compact] new: u32,
2203 ) -> DispatchResult {
2204 ensure_root(origin)?;
2205
2206 ensure!(new <= T::MaxValidatorSet::get(), Error::<T>::TooManyValidators);
2207
2208 ValidatorCount::<T>::put(new);
2209 Ok(())
2210 }
2211
2212 #[pallet::call_index(10)]
2217 #[pallet::weight(T::WeightInfo::set_validator_count())]
2218 pub fn increase_validator_count(
2219 origin: OriginFor<T>,
2220 #[pallet::compact] additional: u32,
2221 ) -> DispatchResult {
2222 ensure_root(origin)?;
2223 let old = ValidatorCount::<T>::get();
2224 let new = old.checked_add(additional).ok_or(ArithmeticError::Overflow)?;
2225
2226 ensure!(new <= T::MaxValidatorSet::get(), Error::<T>::TooManyValidators);
2227
2228 ValidatorCount::<T>::put(new);
2229 Ok(())
2230 }
2231
2232 #[pallet::call_index(11)]
2237 #[pallet::weight(T::WeightInfo::set_validator_count())]
2238 pub fn scale_validator_count(origin: OriginFor<T>, factor: Percent) -> DispatchResult {
2239 ensure_root(origin)?;
2240 let old = ValidatorCount::<T>::get();
2241 let new = old.checked_add(factor.mul_floor(old)).ok_or(ArithmeticError::Overflow)?;
2242
2243 ensure!(new <= T::MaxValidatorSet::get(), Error::<T>::TooManyValidators);
2244
2245 ValidatorCount::<T>::put(new);
2246 Ok(())
2247 }
2248
2249 #[pallet::call_index(12)]
2259 #[pallet::weight(T::WeightInfo::force_no_eras())]
2260 pub fn force_no_eras(origin: OriginFor<T>) -> DispatchResult {
2261 ensure_root(origin)?;
2262 Self::set_force_era(Forcing::ForceNone);
2263 Ok(())
2264 }
2265
2266 #[pallet::call_index(13)]
2277 #[pallet::weight(T::WeightInfo::force_new_era())]
2278 pub fn force_new_era(origin: OriginFor<T>) -> DispatchResult {
2279 ensure_root(origin)?;
2280 Self::set_force_era(Forcing::ForceNew);
2281 Ok(())
2282 }
2283
2284 #[pallet::call_index(15)]
2293 #[pallet::weight(T::WeightInfo::force_unstake())]
2294 pub fn force_unstake(
2295 origin: OriginFor<T>,
2296 stash: T::AccountId,
2297 _num_slashing_spans: u32,
2298 ) -> DispatchResult {
2299 ensure_root(origin)?;
2300
2301 Self::kill_stash(&stash)?;
2303
2304 Ok(())
2305 }
2306
2307 #[pallet::call_index(16)]
2317 #[pallet::weight(T::WeightInfo::force_new_era_always())]
2318 pub fn force_new_era_always(origin: OriginFor<T>) -> DispatchResult {
2319 ensure_root(origin)?;
2320 Self::set_force_era(Forcing::ForceAlways);
2321 Ok(())
2322 }
2323
2324 #[pallet::call_index(17)]
2336 #[pallet::weight(T::WeightInfo::cancel_deferred_slash(validator_slashes.len() as u32))]
2337 pub fn cancel_deferred_slash(
2338 origin: OriginFor<T>,
2339 era: EraIndex,
2340 validator_slashes: Vec<(T::AccountId, Perbill)>,
2341 ) -> DispatchResult {
2342 T::AdminOrigin::ensure_origin(origin)?;
2343 ensure!(!validator_slashes.is_empty(), Error::<T>::EmptyTargets);
2344
2345 let mut cancelled_slashes = CancelledSlashes::<T>::get(&era);
2347
2348 for (validator, slash_fraction) in validator_slashes {
2350 cancelled_slashes.retain(|(v, _)| v != &validator);
2355
2356 cancelled_slashes
2358 .try_push((validator.clone(), slash_fraction))
2359 .map_err(|_| Error::<T>::BoundNotMet)
2360 .defensive_proof("cancelled_slashes should have capacity for all validators")?;
2361
2362 Self::deposit_event(Event::<T>::SlashCancelled { slash_era: era, validator });
2363 }
2364
2365 CancelledSlashes::<T>::insert(&era, cancelled_slashes);
2367
2368 Ok(())
2369 }
2370
2371 #[pallet::call_index(18)]
2385 #[pallet::weight(T::WeightInfo::payout_stakers_alive_staked(T::MaxExposurePageSize::get()))]
2386 pub fn payout_stakers(
2387 origin: OriginFor<T>,
2388 validator_stash: T::AccountId,
2389 era: EraIndex,
2390 ) -> DispatchResultWithPostInfo {
2391 ensure_signed(origin)?;
2392
2393 Self::do_payout_stakers(validator_stash, era)
2394 }
2395
2396 #[pallet::call_index(19)]
2400 #[pallet::weight(T::WeightInfo::rebond(T::MaxUnlockingChunks::get() as u32))]
2401 pub fn rebond(
2402 origin: OriginFor<T>,
2403 #[pallet::compact] value: BalanceOf<T>,
2404 ) -> DispatchResultWithPostInfo {
2405 let controller = ensure_signed(origin)?;
2406 let ledger = Self::ledger(Controller(controller))?;
2407
2408 ensure!(!T::Filter::contains(&ledger.stash), Error::<T>::Restricted);
2409 ensure!(!ledger.unlocking.is_empty(), Error::<T>::NoUnlockChunk);
2410
2411 let initial_unlocking = ledger.unlocking.len() as u32;
2412 let (ledger, rebonded_value) = ledger.rebond(value);
2413 ensure!(ledger.active >= Self::min_chilled_bond(), Error::<T>::InsufficientBond);
2415
2416 Self::deposit_event(Event::<T>::Bonded {
2417 stash: ledger.stash.clone(),
2418 amount: rebonded_value,
2419 });
2420
2421 let stash = ledger.stash.clone();
2422 let final_unlocking = ledger.unlocking.len();
2423
2424 ledger.update()?;
2426 if T::VoterList::contains(&stash) {
2427 let _ = T::VoterList::on_update(&stash, Self::weight_of(&stash));
2428 }
2429
2430 let removed_chunks = 1u32 .saturating_add(initial_unlocking)
2432 .saturating_sub(final_unlocking as u32);
2433 Ok(Some(T::WeightInfo::rebond(removed_chunks)).into())
2434 }
2435
2436 #[pallet::call_index(20)]
2455 #[pallet::weight(T::WeightInfo::reap_stash())]
2456 pub fn reap_stash(
2457 origin: OriginFor<T>,
2458 stash: T::AccountId,
2459 _num_slashing_spans: u32,
2460 ) -> DispatchResultWithPostInfo {
2461 let _ = ensure_signed(origin)?;
2462
2463 ensure!(!Self::is_virtual_staker(&stash), Error::<T>::VirtualStakerNotAllowed);
2465
2466 let min_chilled_bond = Self::min_chilled_bond();
2467 let origin_balance = asset::total_balance::<T>(&stash);
2468 let ledger_total =
2469 Self::ledger(Stash(stash.clone())).map(|l| l.total).unwrap_or_default();
2470 let reapable = origin_balance < min_chilled_bond ||
2471 origin_balance.is_zero() ||
2472 ledger_total < min_chilled_bond ||
2473 ledger_total.is_zero();
2474 ensure!(reapable, Error::<T>::FundedTarget);
2475
2476 Self::kill_stash(&stash)?;
2478
2479 Ok(Pays::No.into())
2480 }
2481
2482 #[pallet::call_index(21)]
2494 #[pallet::weight(T::WeightInfo::kick(who.len() as u32))]
2495 pub fn kick(origin: OriginFor<T>, who: Vec<AccountIdLookupOf<T>>) -> DispatchResult {
2496 let controller = ensure_signed(origin)?;
2497 let ledger = Self::ledger(Controller(controller))?;
2498 let stash = &ledger.stash;
2499
2500 for nom_stash in who
2501 .into_iter()
2502 .map(T::Lookup::lookup)
2503 .collect::<Result<Vec<T::AccountId>, _>>()?
2504 .into_iter()
2505 {
2506 Nominators::<T>::mutate(&nom_stash, |maybe_nom| {
2507 if let Some(ref mut nom) = maybe_nom {
2508 if let Some(pos) = nom.targets.iter().position(|v| v == stash) {
2509 nom.targets.swap_remove(pos);
2510 Self::deposit_event(Event::<T>::Kicked {
2511 nominator: nom_stash.clone(),
2512 stash: stash.clone(),
2513 });
2514 }
2515 }
2516 });
2517 }
2518
2519 Ok(())
2520 }
2521
2522 #[pallet::call_index(22)]
2542 #[pallet::weight(
2543 T::WeightInfo::set_staking_configs_all_set()
2544 .max(T::WeightInfo::set_staking_configs_all_remove())
2545 )]
2546 pub fn set_staking_configs(
2547 origin: OriginFor<T>,
2548 min_nominator_bond: ConfigOp<BalanceOf<T>>,
2549 min_validator_bond: ConfigOp<BalanceOf<T>>,
2550 max_nominator_count: ConfigOp<u32>,
2551 max_validator_count: ConfigOp<u32>,
2552 chill_threshold: ConfigOp<Percent>,
2553 min_commission: ConfigOp<Perbill>,
2554 max_staked_rewards: ConfigOp<Percent>,
2555 are_nominators_slashable: ConfigOp<bool>,
2556 ) -> DispatchResult {
2557 ensure_root(origin)?;
2558
2559 macro_rules! config_op_exp {
2560 ($storage:ty, $op:ident) => {
2561 match $op {
2562 ConfigOp::Noop => (),
2563 ConfigOp::Set(v) => <$storage>::put(v),
2564 ConfigOp::Remove => <$storage>::kill(),
2565 }
2566 };
2567 }
2568
2569 config_op_exp!(MinNominatorBond<T>, min_nominator_bond);
2570 config_op_exp!(MinValidatorBond<T>, min_validator_bond);
2571 config_op_exp!(MaxNominatorsCount<T>, max_nominator_count);
2572 config_op_exp!(MaxValidatorsCount<T>, max_validator_count);
2573 config_op_exp!(ChillThreshold<T>, chill_threshold);
2574 config_op_exp!(MinCommission<T>, min_commission);
2575 config_op_exp!(MaxStakedRewards<T>, max_staked_rewards);
2576 config_op_exp!(AreNominatorsSlashable<T>, are_nominators_slashable);
2577 Ok(())
2578 }
2579 #[pallet::call_index(23)]
2606 #[pallet::weight(T::WeightInfo::chill_other())]
2607 pub fn chill_other(origin: OriginFor<T>, stash: T::AccountId) -> DispatchResult {
2608 let caller = ensure_signed(origin)?;
2610 let ledger = Self::ledger(Stash(stash.clone()))?;
2611 let controller = ledger
2612 .controller()
2613 .defensive_proof(
2614 "Ledger's controller field didn't exist. The controller should have been fetched using StakingLedger.",
2615 )
2616 .ok_or(Error::<T>::NotController)?;
2617
2618 if Nominators::<T>::contains_key(&stash) && Nominators::<T>::get(&stash).is_none() {
2635 Self::chill_stash(&stash);
2636 return Ok(());
2637 }
2638
2639 if caller != controller {
2640 let threshold = ChillThreshold::<T>::get().ok_or(Error::<T>::CannotChillOther)?;
2641 let min_active_bond = if Nominators::<T>::contains_key(&stash) {
2642 let max_nominator_count =
2643 MaxNominatorsCount::<T>::get().ok_or(Error::<T>::CannotChillOther)?;
2644 let current_nominator_count = Nominators::<T>::count();
2645 ensure!(
2646 threshold * max_nominator_count < current_nominator_count,
2647 Error::<T>::CannotChillOther
2648 );
2649 Self::min_nominator_bond()
2650 } else if Validators::<T>::contains_key(&stash) {
2651 let max_validator_count =
2652 MaxValidatorsCount::<T>::get().ok_or(Error::<T>::CannotChillOther)?;
2653 let current_validator_count = Validators::<T>::count();
2654 ensure!(
2655 threshold * max_validator_count < current_validator_count,
2656 Error::<T>::CannotChillOther
2657 );
2658 Self::min_validator_bond()
2659 } else {
2660 Zero::zero()
2661 };
2662
2663 ensure!(ledger.active < min_active_bond, Error::<T>::CannotChillOther);
2664 }
2665
2666 Self::chill_stash(&stash);
2667 Ok(())
2668 }
2669
2670 #[pallet::call_index(24)]
2675 #[pallet::weight(T::WeightInfo::force_apply_min_commission())]
2676 pub fn force_apply_min_commission(
2677 origin: OriginFor<T>,
2678 validator_stash: T::AccountId,
2679 ) -> DispatchResult {
2680 ensure_signed(origin)?;
2681 let min_commission = MinCommission::<T>::get();
2682 let max_commission = MaxCommission::<T>::get();
2683 Validators::<T>::try_mutate_exists(validator_stash, |maybe_prefs| {
2684 maybe_prefs
2685 .as_mut()
2686 .map(|prefs| {
2687 if prefs.commission < min_commission {
2688 prefs.commission = min_commission;
2689 }
2690 if prefs.commission > max_commission {
2691 prefs.commission = max_commission;
2692 }
2693 })
2694 .ok_or(Error::<T>::NotStash)
2695 })?;
2696 Ok(())
2697 }
2698
2699 #[pallet::call_index(25)]
2704 #[pallet::weight(T::WeightInfo::set_min_commission())]
2705 pub fn set_min_commission(origin: OriginFor<T>, new: Perbill) -> DispatchResult {
2706 T::AdminOrigin::ensure_origin(origin)?;
2707 ensure!(new <= MaxCommission::<T>::get(), Error::<T>::CommissionTooHigh);
2708 MinCommission::<T>::put(new);
2709 Ok(())
2710 }
2711
2712 #[pallet::call_index(26)]
2734 #[pallet::weight(T::WeightInfo::payout_stakers_alive_staked(T::MaxExposurePageSize::get()))]
2735 pub fn payout_stakers_by_page(
2736 origin: OriginFor<T>,
2737 validator_stash: T::AccountId,
2738 era: EraIndex,
2739 page: Page,
2740 ) -> DispatchResultWithPostInfo {
2741 ensure_signed(origin)?;
2742 Self::do_payout_stakers_by_page(validator_stash, era, page)
2743 }
2744
2745 #[pallet::call_index(27)]
2752 #[pallet::weight(T::WeightInfo::update_payee())]
2753 pub fn update_payee(
2754 origin: OriginFor<T>,
2755 controller: T::AccountId,
2756 ) -> DispatchResultWithPostInfo {
2757 let _ = ensure_signed(origin)?;
2758 let ledger = Self::ledger(StakingAccount::Controller(controller.clone()))?;
2759
2760 ensure!(
2761 (Payee::<T>::get(&ledger.stash) == {
2762 #[allow(deprecated)]
2763 Some(RewardDestination::Controller)
2764 }),
2765 Error::<T>::NotController
2766 );
2767
2768 let _ = ledger
2769 .set_payee(RewardDestination::Account(controller))
2770 .defensive_proof("ledger should have been previously retrieved from storage.")?;
2771
2772 Ok(Pays::No.into())
2773 }
2774
2775 #[pallet::call_index(28)]
2783 #[pallet::weight(T::WeightInfo::deprecate_controller_batch(controllers.len() as u32))]
2784 pub fn deprecate_controller_batch(
2785 origin: OriginFor<T>,
2786 controllers: BoundedVec<T::AccountId, T::MaxControllersInDeprecationBatch>,
2787 ) -> DispatchResultWithPostInfo {
2788 T::AdminOrigin::ensure_origin(origin)?;
2789
2790 let filtered_batch_with_ledger: Vec<_> = controllers
2792 .iter()
2793 .filter_map(|controller| {
2794 let ledger = Self::ledger(StakingAccount::Controller(controller.clone()));
2795 ledger.ok().map_or(None, |ledger| {
2796 let payee_deprecated = Payee::<T>::get(&ledger.stash) == {
2799 #[allow(deprecated)]
2800 Some(RewardDestination::Controller)
2801 };
2802
2803 if ledger.stash != *controller && !payee_deprecated {
2804 Some(ledger)
2805 } else {
2806 None
2807 }
2808 })
2809 })
2810 .collect();
2811
2812 let mut failures = 0;
2814 for ledger in filtered_batch_with_ledger {
2815 let _ = ledger.clone().set_controller_to_stash().map_err(|_| failures += 1);
2816 }
2817 Self::deposit_event(Event::<T>::ControllerBatchDeprecated { failures });
2818
2819 Ok(Some(T::WeightInfo::deprecate_controller_batch(controllers.len() as u32)).into())
2820 }
2821
2822 #[pallet::call_index(29)]
2834 #[pallet::weight(T::WeightInfo::restore_ledger())]
2835 pub fn restore_ledger(
2836 origin: OriginFor<T>,
2837 stash: T::AccountId,
2838 maybe_controller: Option<T::AccountId>,
2839 maybe_total: Option<BalanceOf<T>>,
2840 maybe_unlocking: Option<BoundedVec<UnlockChunk<BalanceOf<T>>, T::MaxUnlockingChunks>>,
2841 ) -> DispatchResult {
2842 T::AdminOrigin::ensure_origin(origin)?;
2843
2844 ensure!(!Self::is_virtual_staker(&stash), Error::<T>::VirtualStakerNotAllowed);
2846
2847 let current_lock = asset::staked::<T>(&stash);
2848 let stash_balance = asset::stakeable_balance::<T>(&stash);
2849
2850 let (new_controller, new_total) = match Self::inspect_bond_state(&stash) {
2851 Ok(LedgerIntegrityState::Corrupted) => {
2852 let new_controller = maybe_controller.unwrap_or(stash.clone());
2853
2854 let new_total = if let Some(total) = maybe_total {
2855 let new_total = total.min(stash_balance);
2856 asset::update_stake::<T>(&stash, new_total)?;
2858 new_total
2859 } else {
2860 current_lock
2861 };
2862
2863 Ok((new_controller, new_total))
2864 },
2865 Ok(LedgerIntegrityState::CorruptedKilled) => {
2866 if current_lock == Zero::zero() {
2867 ensure!(maybe_total.is_some(), Error::<T>::CannotRestoreLedger);
2871 Ok((
2872 stash.clone(),
2873 maybe_total.expect("total exists as per the check above; qed."),
2874 ))
2875 } else {
2876 Ok((stash.clone(), current_lock))
2877 }
2878 },
2879 Ok(LedgerIntegrityState::LockCorrupted) => {
2880 let new_total =
2883 maybe_total.ok_or(Error::<T>::CannotRestoreLedger)?.min(stash_balance);
2884 asset::update_stake::<T>(&stash, new_total)?;
2885
2886 Ok((stash.clone(), new_total))
2887 },
2888 Err(Error::<T>::BadState) => {
2889 asset::kill_stake::<T>(&stash)?;
2891 ensure!(
2892 Self::inspect_bond_state(&stash) == Err(Error::<T>::NotStash),
2893 Error::<T>::BadState
2894 );
2895
2896 return Ok(());
2897 },
2898 Ok(LedgerIntegrityState::Ok) | Err(_) => Err(Error::<T>::CannotRestoreLedger),
2899 }?;
2900
2901 Bonded::<T>::insert(&stash, &new_controller);
2903
2904 let mut ledger = StakingLedger::<T>::new(stash.clone(), new_total);
2906 ledger.controller = Some(new_controller);
2907 ledger.unlocking = maybe_unlocking.unwrap_or_default();
2908 ledger.update()?;
2909
2910 ensure!(
2911 Self::inspect_bond_state(&stash) == Ok(LedgerIntegrityState::Ok),
2912 Error::<T>::BadState
2913 );
2914 Ok(())
2915 }
2916
2917 #[pallet::call_index(30)]
2925 #[pallet::weight(T::WeightInfo::migrate_currency())]
2926 pub fn migrate_currency(
2927 origin: OriginFor<T>,
2928 stash: T::AccountId,
2929 ) -> DispatchResultWithPostInfo {
2930 let _ = ensure_signed(origin)?;
2931 Self::do_migrate_currency(&stash)?;
2932
2933 Ok(Pays::No.into())
2935 }
2936
2937 #[pallet::call_index(31)]
2972 #[pallet::weight(T::WeightInfo::apply_slash(T::MaxExposurePageSize::get()))]
2973 pub fn apply_slash(
2974 origin: OriginFor<T>,
2975 slash_era: EraIndex,
2976 slash_key: (T::AccountId, Perbill, u32),
2977 ) -> DispatchResultWithPostInfo {
2978 let _ = ensure_signed(origin)?;
2979 let active_era = ActiveEra::<T>::get().map(|a| a.index).unwrap_or_default();
2980 ensure!(slash_era <= active_era, Error::<T>::EraNotStarted);
2981
2982 ensure!(
2984 !Self::check_slash_cancelled(slash_era, &slash_key.0, slash_key.1),
2985 Error::<T>::CancelledSlash
2986 );
2987
2988 let unapplied_slash = UnappliedSlashes::<T>::take(&slash_era, &slash_key)
2989 .ok_or(Error::<T>::InvalidSlashRecord)?;
2990 slashing::apply_slash::<T>(unapplied_slash, Self::offence_era_of(slash_era));
2991
2992 Ok(Pays::No.into())
2993 }
2994
2995 #[pallet::call_index(32)]
3007 #[pallet::weight({
3009 let v = T::MaxValidatorSet::get();
3010 T::WeightInfo::prune_era_stakers_paged(v)
3011 .max(T::WeightInfo::prune_era_stakers_overview(v))
3012 .max(T::WeightInfo::prune_era_validator_prefs(v))
3013 .max(T::WeightInfo::prune_era_claimed_rewards(v))
3014 .max(T::WeightInfo::prune_era_validator_reward())
3015 .max(T::WeightInfo::prune_era_reward_points())
3016 .max(T::WeightInfo::prune_era_single_entry_cleanups())
3017 .max(T::WeightInfo::prune_era_validator_slash_in_era(v))
3018 .max(T::WeightInfo::prune_era_validator_incentive_weight(v))
3019 })]
3020 pub fn prune_era_step(origin: OriginFor<T>, era: EraIndex) -> DispatchResultWithPostInfo {
3021 let _ = ensure_signed(origin)?;
3022
3023 let active_era = crate::session_rotation::Rotator::<T>::active_era();
3025 let history_depth = T::HistoryDepth::get();
3026 let earliest_prunable_era = active_era.saturating_sub(history_depth).saturating_sub(1);
3027 ensure!(era <= earliest_prunable_era, Error::<T>::EraNotPrunable);
3028
3029 let actual_weight = Self::do_prune_era_step(era)?;
3030
3031 Ok(frame_support::dispatch::PostDispatchInfo {
3032 actual_weight: Some(actual_weight),
3033 pays_fee: frame_support::dispatch::Pays::No,
3034 })
3035 }
3036
3037 #[pallet::call_index(33)]
3041 #[pallet::weight(T::WeightInfo::set_max_commission())]
3042 pub fn set_max_commission(origin: OriginFor<T>, new: Perbill) -> DispatchResult {
3043 T::AdminOrigin::ensure_origin(origin)?;
3044 ensure!(new >= MinCommission::<T>::get(), Error::<T>::CommissionTooLow);
3045 MaxCommission::<T>::put(new);
3046 Ok(())
3047 }
3048
3049 #[pallet::call_index(34)]
3055 #[pallet::weight(T::WeightInfo::set_validator_self_stake_incentive_config())]
3056 pub fn set_validator_self_stake_incentive_config(
3057 origin: OriginFor<T>,
3058 optimum_self_stake: ConfigOp<BalanceOf<T>>,
3059 hard_cap_self_stake: ConfigOp<BalanceOf<T>>,
3060 self_stake_slope_factor: ConfigOp<Perbill>,
3061 ) -> DispatchResult {
3062 T::AdminOrigin::ensure_origin(origin)?;
3063
3064 let new_optimum = match optimum_self_stake {
3065 ConfigOp::Noop => OptimumSelfStake::<T>::get(),
3066 ConfigOp::Set(v) => v,
3067 ConfigOp::Remove => BalanceOf::<T>::zero(),
3068 };
3069
3070 let new_cap = match hard_cap_self_stake {
3071 ConfigOp::Noop => HardCapSelfStake::<T>::get(),
3072 ConfigOp::Set(v) => v,
3073 ConfigOp::Remove => BalanceOf::<T>::zero(),
3074 };
3075
3076 ensure!(new_optimum <= new_cap, Error::<T>::OptimumGreaterThanCap);
3077
3078 let has_changes = !matches!(
3079 (&optimum_self_stake, &hard_cap_self_stake, &self_stake_slope_factor),
3080 (ConfigOp::Noop, ConfigOp::Noop, ConfigOp::Noop)
3081 );
3082
3083 macro_rules! config_op_exp {
3084 ($storage:ty, $op:ident) => {
3085 match $op {
3086 ConfigOp::Noop => (),
3087 ConfigOp::Set(v) => <$storage>::put(v),
3088 ConfigOp::Remove => <$storage>::kill(),
3089 }
3090 };
3091 }
3092
3093 config_op_exp!(OptimumSelfStake<T>, optimum_self_stake);
3094 config_op_exp!(HardCapSelfStake<T>, hard_cap_self_stake);
3095 config_op_exp!(SelfStakeSlopeFactor<T>, self_stake_slope_factor);
3096
3097 if has_changes {
3098 Self::deposit_event(Event::<T>::ValidatorIncentiveConfigSet {
3099 optimum_self_stake: OptimumSelfStake::<T>::get(),
3100 hard_cap_self_stake: HardCapSelfStake::<T>::get(),
3101 slope_factor: SelfStakeSlopeFactor::<T>::get(),
3102 });
3103 }
3104
3105 Ok(())
3106 }
3107 }
3108
3109 #[pallet::view_functions]
3110 impl<T: Config> Pallet<T> {
3111 pub fn pot_account(pot: crate::RewardPot) -> T::AccountId {
3113 <T::RewardPots as crate::PotAccountProvider<T::AccountId>>::pot_account(pot)
3114 }
3115
3116 pub fn pot_balance(pot: crate::RewardPot) -> BalanceOf<T> {
3118 let account =
3119 <T::RewardPots as crate::PotAccountProvider<T::AccountId>>::pot_account(pot);
3120 <T::Currency as frame_support::traits::fungible::Inspect<T::AccountId>>::balance(
3121 &account,
3122 )
3123 }
3124
3125 pub fn era_reward_allocation(
3129 era: EraIndex,
3130 ) -> crate::reward::EraRewardAllocation<BalanceOf<T>> {
3131 crate::reward::EraRewardAllocation {
3132 staker_rewards: ErasValidatorReward::<T>::get(era).unwrap_or_else(Zero::zero),
3133 validator_incentive: ErasValidatorIncentiveBudget::<T>::get(era),
3134 }
3135 }
3136 }
3137}