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::{
70 session_rotation::{self, Eras, Rotator},
71 IsValidatorInactive, PagedExposureMetadata, SnapshotStatus,
72 };
73 use codec::HasCompact;
74 use frame_election_provider_support::{ElectionDataProvider, PageIndex};
75 use frame_support::{traits::ConstBool, weights::WeightMeter, DefaultNoBound, PalletError};
76
77 pub(crate) type IncentiveWeight<T> = BalanceOf<T>;
81
82 #[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, Debug, TypeInfo, MaxEncodedLen)]
84 pub enum PruningStep {
85 ErasStakersPaged,
87 ErasStakersOverview,
89 ErasValidatorPrefs,
91 ClaimedRewards,
93 ErasValidatorReward,
95 ErasRewardPoints,
97 SingleEntryCleanups,
99 ValidatorSlashInEra,
101 ErasValidatorIncentiveWeight,
103 }
104
105 const STORAGE_VERSION: StorageVersion = StorageVersion::new(18);
107
108 #[pallet::pallet]
109 #[pallet::storage_version(STORAGE_VERSION)]
110 pub struct Pallet<T>(_);
111
112 #[derive(TypeInfo, Debug, Clone, Encode, Decode, DecodeWithMemTracking, PartialEq)]
114 pub enum ConfigOp<T: Default + Codec> {
115 Noop,
117 Set(T),
119 Remove,
121 }
122
123 #[pallet::config(with_default)]
124 pub trait Config: frame_system::Config {
125 #[pallet::no_default]
127 type OldCurrency: InspectLockableCurrency<
128 Self::AccountId,
129 Moment = BlockNumberFor<Self>,
130 Balance = Self::CurrencyBalance,
131 >;
132
133 #[pallet::no_default]
135 type Currency: FunHoldMutate<
136 Self::AccountId,
137 Reason = Self::RuntimeHoldReason,
138 Balance = Self::CurrencyBalance,
139 > + FunMutate<Self::AccountId, Balance = Self::CurrencyBalance>
140 + FunHoldBalanced<Self::AccountId, Balance = Self::CurrencyBalance>;
141
142 #[pallet::no_default_bounds]
144 type RuntimeHoldReason: From<HoldReason>;
145
146 type CurrencyBalance: sp_runtime::traits::AtLeast32BitUnsigned
149 + codec::FullCodec
150 + DecodeWithMemTracking
151 + HasCompact<Type: DecodeWithMemTracking>
152 + Copy
153 + MaybeSerializeDeserialize
154 + core::fmt::Debug
155 + Default
156 + From<u64>
157 + TypeInfo
158 + Send
159 + Sync
160 + MaxEncodedLen;
161
162 #[pallet::no_default_bounds]
169 type CurrencyToVote: sp_staking::currency_to_vote::CurrencyToVote<BalanceOf<Self>>;
170
171 #[pallet::no_default]
173 type ElectionProvider: ElectionProvider<
174 AccountId = Self::AccountId,
175 BlockNumber = BlockNumberFor<Self>,
176 DataProvider = Pallet<Self>,
178 >;
179
180 #[pallet::no_default_bounds]
182 type NominationsQuota: NominationsQuota<BalanceOf<Self>>;
183
184 #[pallet::constant]
198 type HistoryDepth: Get<u32>;
199
200 #[pallet::no_default_bounds]
204 type RewardRemainder: OnUnbalanced<NegativeImbalanceOf<Self>>;
205
206 #[pallet::no_default_bounds]
208 type Slash: OnUnbalanced<NegativeImbalanceOf<Self>>;
209
210 #[pallet::no_default_bounds]
216 type Reward: OnUnbalanced<PositiveImbalanceOf<Self>>;
217
218 #[pallet::constant]
220 type SessionsPerEra: Get<SessionIndex>;
221
222 #[pallet::constant]
237 type PlanningEraOffset: Get<SessionIndex>;
238
239 #[pallet::constant]
245 type BondingDuration: Get<EraIndex>;
246
247 #[pallet::constant]
256 type NominatorFastUnbondDuration: Get<EraIndex>;
257
258 #[pallet::constant]
263 type SlashDeferDuration: Get<EraIndex>;
264
265 #[pallet::no_default]
269 type AdminOrigin: EnsureOrigin<Self::RuntimeOrigin>;
270
271 #[pallet::no_default]
277 type EraPayout: EraPayout<BalanceOf<Self>>;
278
279 #[pallet::constant]
290 type DisableMinting: Get<bool>;
291
292 #[pallet::no_default_bounds]
297 type UnclaimedRewardHandler: OnUnbalanced<NegativeImbalanceOf<Self>>;
298
299 #[pallet::no_default]
304 type RewardPots: crate::PotAccountProvider<Self::AccountId>;
305
306 #[pallet::no_default_bounds]
310 type StakerRewardCalculator: sp_staking::StakerRewardCalculator<BalanceOf<Self>>;
311
312 #[pallet::constant]
324 type MaxExposurePageSize: Get<u32>;
325
326 #[pallet::constant]
331 type MaxValidatorSet: Get<u32>;
332
333 #[pallet::no_default]
345 type VoterList: SortedListProvider<Self::AccountId, Score = VoteWeight>;
346
347 #[pallet::no_default]
368 type TargetList: SortedListProvider<Self::AccountId, Score = BalanceOf<Self>>;
369
370 #[pallet::constant]
381 type MaxUnlockingChunks: Get<u32>;
382
383 type MaxControllersInDeprecationBatch: Get<u32>;
385
386 #[pallet::no_default_bounds]
391 type EventListeners: sp_staking::OnStakingUpdate<Self::AccountId, BalanceOf<Self>>;
392
393 #[pallet::constant]
404 type MaxEraDuration: Get<u64>;
405
406 #[pallet::constant]
412 type MaxPruningItems: Get<u32>;
413
414 #[pallet::no_default]
417 type RcClientInterface: pallet_staking_async_rc_client::RcClientInterface<
418 AccountId = Self::AccountId,
419 >;
420
421 #[pallet::no_default_bounds]
422 type Filter: Contains<Self::AccountId>;
427
428 type WeightInfo: WeightInfo;
430
431 type IsValidatorInactive: IsValidatorInactive<Self::AccountId>;
436 }
437
438 #[pallet::composite_enum]
440 pub enum HoldReason {
441 #[codec(index = 0)]
443 Staking,
444 }
445
446 pub mod config_preludes {
448 use super::*;
449 use frame_support::{derive_impl, parameter_types, traits::ConstU32};
450 pub struct TestDefaultConfig;
451
452 #[derive_impl(frame_system::config_preludes::TestDefaultConfig, no_aggregated_types)]
453 impl frame_system::DefaultConfig for TestDefaultConfig {}
454
455 parameter_types! {
456 pub const SessionsPerEra: SessionIndex = 3;
457 pub const BondingDuration: EraIndex = 3;
458 pub const NominatorFastUnbondDuration: EraIndex = 2;
459 pub const MaxPruningItems: u32 = 100;
460 }
461
462 #[frame_support::register_default_impl(TestDefaultConfig)]
463 impl DefaultConfig for TestDefaultConfig {
464 #[inject_runtime_type]
465 type RuntimeHoldReason = ();
466 type CurrencyBalance = u128;
467 type CurrencyToVote = ();
468 type NominationsQuota = crate::FixedNominationsQuota<16>;
469 type HistoryDepth = ConstU32<84>;
470 type RewardRemainder = ();
471 type Slash = ();
472 type Reward = ();
473 type UnclaimedRewardHandler = ();
474 type StakerRewardCalculator = ();
475 type DisableMinting = ConstBool<false>;
476 type SessionsPerEra = SessionsPerEra;
477 type BondingDuration = BondingDuration;
478 type NominatorFastUnbondDuration = NominatorFastUnbondDuration;
479 type PlanningEraOffset = ConstU32<1>;
480 type SlashDeferDuration = ();
481 type MaxExposurePageSize = ConstU32<64>;
482 type MaxUnlockingChunks = ConstU32<32>;
483 type MaxValidatorSet = ConstU32<100>;
484 type MaxControllersInDeprecationBatch = ConstU32<100>;
485 type MaxEraDuration = ();
486 type MaxPruningItems = MaxPruningItems;
487 type EventListeners = ();
488 type Filter = Nothing;
489 type WeightInfo = ();
490 type IsValidatorInactive = ();
491 }
492 }
493
494 #[pallet::storage]
496 pub type ValidatorCount<T> = StorageValue<_, u32, ValueQuery>;
497
498 #[pallet::storage]
502 pub type Bonded<T: Config> = StorageMap<_, Twox64Concat, T::AccountId, T::AccountId>;
503
504 #[pallet::storage]
506 pub type MinNominatorBond<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;
507
508 #[pallet::storage]
510 pub type MinValidatorBond<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;
511
512 #[pallet::storage]
514 pub type MinimumActiveStake<T> = StorageValue<_, BalanceOf<T>, ValueQuery>;
515
516 #[pallet::storage]
520 pub type MinCommission<T: Config> = StorageValue<_, Perbill, ValueQuery>;
521
522 #[pallet::storage]
526 pub type MaxCommission<T: Config> = StorageValue<_, Perbill, ValueQuery, MaxCommissionDefault>;
527
528 pub struct MaxCommissionDefault;
530 impl Get<Perbill> for MaxCommissionDefault {
531 fn get() -> Perbill {
532 Perbill::one()
533 }
534 }
535
536 #[pallet::storage]
544 pub type DisableMintingGuard<T: Config> = StorageValue<_, EraIndex>;
545
546 #[pallet::storage]
551 pub type OptimumSelfStake<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;
552
553 #[pallet::storage]
557 pub type HardCapSelfStake<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;
558
559 #[pallet::storage]
563 pub type SelfStakeSlopeFactor<T: Config> = StorageValue<_, Perbill, ValueQuery>;
564
565 #[pallet::storage]
569 pub type ErasValidatorIncentiveBudget<T: Config> =
570 StorageMap<_, Twox64Concat, EraIndex, BalanceOf<T>, ValueQuery>;
571
572 #[pallet::storage]
576 pub type ErasSumValidatorIncentiveWeight<T: Config> =
577 StorageMap<_, Twox64Concat, EraIndex, IncentiveWeight<T>, ValueQuery>;
578
579 #[pallet::storage]
582 pub type ErasValidatorIncentiveWeight<T: Config> = StorageDoubleMap<
583 _,
584 Twox64Concat,
585 EraIndex,
586 Twox64Concat,
587 T::AccountId,
588 IncentiveWeight<T>,
589 OptionQuery,
590 >;
591
592 #[pallet::storage]
599 pub type ErasSumWeightedPoints<T: Config> =
600 StorageMap<_, Twox64Concat, EraIndex, IncentiveWeight<T>, ValueQuery>;
601
602 #[pallet::storage]
621 pub type WeightedPointsFormulaStartEra<T: Config> = StorageValue<_, EraIndex, OptionQuery>;
622
623 #[pallet::storage]
631 pub type AreNominatorsSlashable<T: Config> = StorageValue<_, bool, ValueQuery, ConstBool<true>>;
632
633 #[pallet::storage]
641 pub type ErasNominatorsSlashable<T: Config> =
642 StorageMap<_, Twox64Concat, EraIndex, bool, OptionQuery>;
643
644 #[pallet::storage]
649 pub type Ledger<T: Config> = StorageMap<_, Blake2_128Concat, T::AccountId, StakingLedger<T>>;
650
651 #[pallet::storage]
655 pub type Payee<T: Config> =
656 StorageMap<_, Twox64Concat, T::AccountId, RewardDestination<T::AccountId>, OptionQuery>;
657
658 #[pallet::storage]
662 pub type Validators<T: Config> =
663 CountedStorageMap<_, Twox64Concat, T::AccountId, ValidatorPrefs, ValueQuery>;
664
665 #[pallet::storage]
669 pub type MaxValidatorsCount<T> = StorageValue<_, u32, OptionQuery>;
670
671 #[pallet::storage]
684 pub type LastValidatorEra<T: Config> = StorageMap<_, Twox64Concat, T::AccountId, EraIndex>;
685
686 #[pallet::storage]
706 pub type Nominators<T: Config> =
707 CountedStorageMap<_, Twox64Concat, T::AccountId, Nominations<T>>;
708
709 #[pallet::storage]
716 pub type VirtualStakers<T: Config> = CountedStorageMap<_, Twox64Concat, T::AccountId, ()>;
717
718 #[pallet::storage]
722 pub type MaxNominatorsCount<T> = StorageValue<_, u32, OptionQuery>;
723
724 #[pallet::storage]
731 pub type CurrentEra<T> = StorageValue<_, EraIndex>;
732
733 #[pallet::storage]
738 pub type ActiveEra<T> = StorageValue<_, ActiveEraInfo>;
739
740 pub struct BondedErasBound<T>(core::marker::PhantomData<T>);
742 impl<T: Config> Get<u32> for BondedErasBound<T> {
743 fn get() -> u32 {
744 T::BondingDuration::get().saturating_add(1)
745 }
746 }
747
748 const OFFENCE_QUEUE_ERAS_BOUND: u32 = 10;
749 pub struct OffenceQueueErasBound<T>(core::marker::PhantomData<T>);
752 impl<T: Config> Get<u32> for OffenceQueueErasBound<T> {
753 fn get() -> u32 {
754 let bonding_duration = T::BondingDuration::get();
755 bonding_duration.saturating_add(OFFENCE_QUEUE_ERAS_BOUND) }
761 }
762
763 #[pallet::storage]
768 pub type BondedEras<T: Config> =
769 StorageValue<_, BoundedVec<(EraIndex, SessionIndex), BondedErasBound<T>>, ValueQuery>;
770
771 #[pallet::storage]
786 pub type ErasStakersOverview<T: Config> = StorageDoubleMap<
787 _,
788 Twox64Concat,
789 EraIndex,
790 Twox64Concat,
791 T::AccountId,
792 PagedExposureMetadata<BalanceOf<T>>,
793 OptionQuery,
794 >;
795
796 #[derive(PartialEqNoBound, Encode, Decode, DebugNoBound, TypeInfo, DefaultNoBound)]
805 #[scale_info(skip_type_params(T))]
806 pub struct BoundedExposurePage<T: Config>(pub ExposurePage<T::AccountId, BalanceOf<T>>);
807 impl<T: Config> Deref for BoundedExposurePage<T> {
808 type Target = ExposurePage<T::AccountId, BalanceOf<T>>;
809
810 fn deref(&self) -> &Self::Target {
811 &self.0
812 }
813 }
814
815 impl<T: Config> core::ops::DerefMut for BoundedExposurePage<T> {
816 fn deref_mut(&mut self) -> &mut Self::Target {
817 &mut self.0
818 }
819 }
820
821 impl<T: Config> codec::MaxEncodedLen for BoundedExposurePage<T> {
822 fn max_encoded_len() -> usize {
823 let max_exposure_page_size = T::MaxExposurePageSize::get() as usize;
824 let individual_size =
825 T::AccountId::max_encoded_len() + BalanceOf::<T>::max_encoded_len();
826
827 BalanceOf::<T>::max_encoded_len() +
829 max_exposure_page_size.saturating_mul(individual_size)
831 }
832 }
833
834 impl<T: Config> From<ExposurePage<T::AccountId, BalanceOf<T>>> for BoundedExposurePage<T> {
835 fn from(value: ExposurePage<T::AccountId, BalanceOf<T>>) -> Self {
836 Self(value)
837 }
838 }
839
840 impl<T: Config> From<BoundedExposurePage<T>> for ExposurePage<T::AccountId, BalanceOf<T>> {
841 fn from(value: BoundedExposurePage<T>) -> Self {
842 value.0
843 }
844 }
845
846 impl<T: Config> codec::EncodeLike<BoundedExposurePage<T>>
847 for ExposurePage<T::AccountId, BalanceOf<T>>
848 {
849 }
850
851 #[pallet::storage]
858 pub type ErasStakersPaged<T: Config> = StorageNMap<
859 _,
860 (
861 NMapKey<Twox64Concat, EraIndex>,
862 NMapKey<Twox64Concat, T::AccountId>,
863 NMapKey<Twox64Concat, Page>,
864 ),
865 BoundedExposurePage<T>,
866 OptionQuery,
867 >;
868
869 pub struct ClaimedRewardsBound<T>(core::marker::PhantomData<T>);
870 impl<T: Config> Get<u32> for ClaimedRewardsBound<T> {
871 fn get() -> u32 {
872 let max_total_nominators_per_validator =
873 <T::ElectionProvider as ElectionProvider>::MaxBackersPerWinnerFinal::get();
874 let exposure_page_size = T::MaxExposurePageSize::get();
875 max_total_nominators_per_validator
876 .saturating_div(exposure_page_size)
877 .saturating_add(1)
878 }
879 }
880
881 #[pallet::storage]
888 pub type ClaimedRewards<T: Config> = StorageDoubleMap<
889 _,
890 Twox64Concat,
891 EraIndex,
892 Twox64Concat,
893 T::AccountId,
894 WeakBoundedVec<Page, ClaimedRewardsBound<T>>,
895 ValueQuery,
896 >;
897
898 #[pallet::storage]
905 pub type ErasValidatorPrefs<T: Config> = StorageDoubleMap<
906 _,
907 Twox64Concat,
908 EraIndex,
909 Twox64Concat,
910 T::AccountId,
911 ValidatorPrefs,
912 ValueQuery,
913 >;
914
915 #[pallet::storage]
921 pub type ErasValidatorReward<T: Config> = StorageMap<_, Twox64Concat, EraIndex, BalanceOf<T>>;
922
923 #[pallet::storage]
926 pub type ErasRewardPoints<T: Config> =
927 StorageMap<_, Twox64Concat, EraIndex, EraRewardPoints<T>, ValueQuery>;
928
929 #[pallet::storage]
932 pub type ErasTotalStake<T: Config> =
933 StorageMap<_, Twox64Concat, EraIndex, BalanceOf<T>, ValueQuery>;
934
935 #[pallet::storage]
937 pub type ForceEra<T> = StorageValue<_, Forcing, ValueQuery>;
938
939 #[pallet::storage]
944 pub type MaxStakedRewards<T> = StorageValue<_, Percent, OptionQuery>;
945
946 #[pallet::storage]
950 pub type SlashRewardFraction<T> = StorageValue<_, Perbill, ValueQuery>;
951
952 #[pallet::storage]
955 pub type CanceledSlashPayout<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;
956
957 #[pallet::storage]
968 pub type OffenceQueue<T: Config> = StorageDoubleMap<
969 _,
970 Twox64Concat,
971 EraIndex,
972 Twox64Concat,
973 T::AccountId,
974 slashing::OffenceRecord<T::AccountId>,
975 >;
976
977 #[pallet::storage]
990 pub type OffenceQueueEras<T: Config> =
991 StorageValue<_, WeakBoundedVec<u32, OffenceQueueErasBound<T>>>;
992
993 #[pallet::storage]
1006 pub type ProcessingOffence<T: Config> =
1007 StorageValue<_, (EraIndex, T::AccountId, slashing::OffenceRecord<T::AccountId>)>;
1008
1009 #[pallet::storage]
1011 pub type UnappliedSlashes<T: Config> = StorageDoubleMap<
1012 _,
1013 Twox64Concat,
1014 EraIndex,
1015 Twox64Concat,
1016 (T::AccountId, Perbill, u32),
1018 UnappliedSlash<T>,
1019 OptionQuery,
1020 >;
1021
1022 #[pallet::storage]
1028 pub type CancelledSlashes<T: Config> = StorageMap<
1029 _,
1030 Twox64Concat,
1031 EraIndex,
1032 BoundedVec<(T::AccountId, Perbill), T::MaxValidatorSet>,
1033 ValueQuery,
1034 >;
1035
1036 #[pallet::storage]
1039 pub type ValidatorSlashInEra<T: Config> = StorageDoubleMap<
1040 _,
1041 Twox64Concat,
1042 EraIndex,
1043 Twox64Concat,
1044 T::AccountId,
1045 (Perbill, BalanceOf<T>),
1046 >;
1047
1048 #[pallet::storage]
1052 pub type ChillThreshold<T: Config> = StorageValue<_, Percent, OptionQuery>;
1053
1054 #[pallet::storage]
1059 pub type VoterSnapshotStatus<T: Config> =
1060 StorageValue<_, SnapshotStatus<T::AccountId>, ValueQuery>;
1061
1062 #[pallet::storage]
1069 pub type NextElectionPage<T: Config> = StorageValue<_, PageIndex, OptionQuery>;
1070
1071 #[pallet::storage]
1073 pub type ElectableStashes<T: Config> =
1074 StorageValue<_, BoundedBTreeSet<T::AccountId, T::MaxValidatorSet>, ValueQuery>;
1075
1076 #[pallet::storage]
1078 pub type EraPruningState<T: Config> = StorageMap<_, Twox64Concat, EraIndex, PruningStep>;
1079
1080 #[pallet::storage]
1085 pub type ChillInactiveThreshold<T: Config> = StorageValue<_, u32, ValueQuery, T::HistoryDepth>;
1086
1087 #[pallet::genesis_config]
1088 #[derive(frame_support::DefaultNoBound, frame_support::DebugNoBound)]
1089 pub struct GenesisConfig<T: Config> {
1090 pub validator_count: u32,
1091 pub force_era: Forcing,
1092 pub slash_reward_fraction: Perbill,
1093 pub canceled_payout: BalanceOf<T>,
1094 pub stakers: Vec<(T::AccountId, BalanceOf<T>, crate::StakerStatus<T::AccountId>)>,
1095 pub min_nominator_bond: BalanceOf<T>,
1096 pub min_validator_bond: BalanceOf<T>,
1097 pub max_validator_count: Option<u32>,
1098 pub max_nominator_count: Option<u32>,
1099 pub dev_stakers: Option<(u32, u32)>,
1106 pub active_era: (u32, u32, u64),
1108 }
1109
1110 impl<T: Config> GenesisConfig<T> {
1111 fn generate_endowed_bonded_account(derivation: &str, rng: &mut ChaChaRng) -> T::AccountId {
1112 let pair: SrPair = Pair::from_string(&derivation, None)
1113 .expect(&format!("Failed to parse derivation string: {derivation}"));
1114 let who = T::AccountId::decode(&mut &pair.public().encode()[..])
1115 .expect(&format!("Failed to decode public key from pair: {:?}", pair.public()));
1116
1117 let (min, max) = T::VoterList::range();
1118 let stake = BalanceOf::<T>::from(rng.next_u64().min(max).max(min));
1119 let two: BalanceOf<T> = 2u32.into();
1120
1121 assert_ok!(T::Currency::mint_into(&who, stake * two));
1122 assert_ok!(<Pallet<T>>::bond(
1123 T::RuntimeOrigin::from(Some(who.clone()).into()),
1124 stake,
1125 RewardDestination::Staked,
1126 ));
1127 who
1128 }
1129 }
1130
1131 #[pallet::genesis_build]
1132 impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
1133 fn build(&self) {
1134 crate::log!(trace, "initializing with {:?}", self);
1135 assert!(
1136 self.validator_count <=
1137 <T::ElectionProvider as ElectionProvider>::MaxWinnersPerPage::get() *
1138 <T::ElectionProvider as ElectionProvider>::Pages::get(),
1139 "validator count is too high, `ElectionProvider` can never fulfill this"
1140 );
1141 ValidatorCount::<T>::put(self.validator_count);
1142
1143 ForceEra::<T>::put(self.force_era);
1144 CanceledSlashPayout::<T>::put(self.canceled_payout);
1145 SlashRewardFraction::<T>::put(self.slash_reward_fraction);
1146 MinNominatorBond::<T>::put(self.min_nominator_bond);
1147 MinValidatorBond::<T>::put(self.min_validator_bond);
1148 if let Some(x) = self.max_validator_count {
1149 MaxValidatorsCount::<T>::put(x);
1150 }
1151 if let Some(x) = self.max_nominator_count {
1152 MaxNominatorsCount::<T>::put(x);
1153 }
1154
1155 for &(ref stash, balance, ref status) in &self.stakers {
1157 match status {
1158 crate::StakerStatus::Validator => {
1159 crate::log!(
1160 trace,
1161 "inserting genesis validator: {:?} => {:?} => {:?}",
1162 stash,
1163 balance,
1164 status
1165 );
1166 assert!(
1167 asset::free_to_stake::<T>(stash) >= balance,
1168 "Stash does not have enough balance to bond."
1169 );
1170 assert_ok!(<Pallet<T>>::bond(
1171 T::RuntimeOrigin::from(Some(stash.clone()).into()),
1172 balance,
1173 RewardDestination::Staked,
1174 ));
1175 assert_ok!(<Pallet<T>>::validate(
1176 T::RuntimeOrigin::from(Some(stash.clone()).into()),
1177 Default::default(),
1178 ));
1179 },
1180 crate::StakerStatus::Idle => {
1181 crate::log!(
1182 trace,
1183 "inserting genesis idle staker: {:?} => {:?} => {:?}",
1184 stash,
1185 balance,
1186 status
1187 );
1188 assert!(
1189 asset::free_to_stake::<T>(stash) >= balance,
1190 "Stash does not have enough balance to bond."
1191 );
1192 assert_ok!(<Pallet<T>>::bond(
1193 T::RuntimeOrigin::from(Some(stash.clone()).into()),
1194 balance,
1195 RewardDestination::Staked,
1196 ));
1197 },
1198 _ => {},
1199 }
1200 }
1201
1202 for &(ref stash, balance, ref status) in &self.stakers {
1204 match status {
1205 crate::StakerStatus::Nominator(votes) => {
1206 crate::log!(
1207 trace,
1208 "inserting genesis nominator: {:?} => {:?} => {:?}",
1209 stash,
1210 balance,
1211 status
1212 );
1213 assert!(
1214 asset::free_to_stake::<T>(stash) >= balance,
1215 "Stash does not have enough balance to bond."
1216 );
1217 assert_ok!(<Pallet<T>>::bond(
1218 T::RuntimeOrigin::from(Some(stash.clone()).into()),
1219 balance,
1220 RewardDestination::Staked,
1221 ));
1222 assert_ok!(<Pallet<T>>::nominate(
1223 T::RuntimeOrigin::from(Some(stash.clone()).into()),
1224 votes.iter().map(|l| T::Lookup::unlookup(l.clone())).collect(),
1225 ));
1226 },
1227 _ => {},
1228 }
1229 }
1230
1231 assert_eq!(
1233 T::VoterList::count(),
1234 Nominators::<T>::count() + Validators::<T>::count(),
1235 "not all genesis stakers were inserted into sorted list provider, something is wrong."
1236 );
1237
1238 if let Some((validators, nominators)) = self.dev_stakers {
1240 crate::log!(
1241 debug,
1242 "generating dev stakers: validators: {}, nominators: {}",
1243 validators,
1244 nominators
1245 );
1246 let base_derivation = "//staker//{}";
1247
1248 let mut rng = ChaChaRng::from_seed(
1251 base_derivation.using_encoded(sp_crypto_hashing::blake2_256),
1252 );
1253
1254 (0..validators).for_each(|index| {
1255 let derivation = base_derivation.replace("{}", &format!("validator{}", index));
1256 let who = Self::generate_endowed_bonded_account(&derivation, &mut rng);
1257 assert_ok!(<Pallet<T>>::validate(
1258 T::RuntimeOrigin::from(Some(who.clone()).into()),
1259 Default::default(),
1260 ));
1261 });
1262
1263 let all_validators = Validators::<T>::iter_keys().collect::<Vec<_>>();
1266
1267 (0..nominators).for_each(|index| {
1268 let derivation = base_derivation.replace("{}", &format!("nominator{}", index));
1269 let who = Self::generate_endowed_bonded_account(&derivation, &mut rng);
1270
1271 let random_nominations = all_validators
1272 .choose_multiple(&mut rng, MaxNominationsOf::<T>::get() as usize)
1273 .map(|v| v.clone())
1274 .collect::<Vec<_>>();
1275
1276 assert_ok!(<Pallet<T>>::nominate(
1277 T::RuntimeOrigin::from(Some(who.clone()).into()),
1278 random_nominations.iter().map(|l| T::Lookup::unlookup(l.clone())).collect(),
1279 ));
1280 })
1281 }
1282
1283 WeightedPointsFormulaStartEra::<T>::put(0);
1287
1288 let (active_era, session_index, timestamp) = self.active_era;
1289 ActiveEra::<T>::put(ActiveEraInfo { index: active_era, start: Some(timestamp) });
1290 CurrentEra::<T>::put(active_era);
1292 BondedEras::<T>::put(
1294 BoundedVec::<_, BondedErasBound<T>>::try_from(
1295 alloc::vec![(active_era, session_index)]
1296 )
1297 .expect("bound for BondedEras is BondingDuration + 1; can contain at least one element; qed")
1298 );
1299 }
1300 }
1301
1302 #[pallet::event]
1303 #[pallet::generate_deposit(pub fn deposit_event)]
1304 pub enum Event<T: Config> {
1305 EraPaid {
1311 era_index: EraIndex,
1312 validator_payout: BalanceOf<T>,
1313 remainder: BalanceOf<T>,
1314 },
1315 Rewarded {
1317 stash: T::AccountId,
1318 dest: RewardDestination<T::AccountId>,
1319 amount: BalanceOf<T>,
1320 },
1321 Slashed {
1323 staker: T::AccountId,
1324 amount: BalanceOf<T>,
1325 },
1326 OldSlashingReportDiscarded {
1329 session_index: SessionIndex,
1330 },
1331 Bonded {
1336 stash: T::AccountId,
1337 amount: BalanceOf<T>,
1338 },
1339 Unbonded {
1341 stash: T::AccountId,
1342 amount: BalanceOf<T>,
1343 },
1344 Withdrawn {
1347 stash: T::AccountId,
1348 amount: BalanceOf<T>,
1349 },
1350 StakerRemoved {
1353 stash: T::AccountId,
1354 },
1355 Kicked {
1357 nominator: T::AccountId,
1358 stash: T::AccountId,
1359 },
1360 Chilled {
1362 stash: T::AccountId,
1363 },
1364 PayoutStarted {
1366 era_index: EraIndex,
1367 validator_stash: T::AccountId,
1368 page: Page,
1369 next: Option<Page>,
1370 },
1371 ValidatorPrefsSet {
1373 stash: T::AccountId,
1374 prefs: ValidatorPrefs,
1375 },
1376 SnapshotVotersSizeExceeded {
1378 size: u32,
1379 },
1380 SnapshotTargetsSizeExceeded {
1382 size: u32,
1383 },
1384 ForceEra {
1385 mode: Forcing,
1386 },
1387 ControllerBatchDeprecated {
1389 failures: u32,
1390 },
1391 CurrencyMigrated {
1394 stash: T::AccountId,
1395 force_withdraw: BalanceOf<T>,
1396 },
1397 PagedElectionProceeded {
1407 page: PageIndex,
1408 result: Result<u32, u32>,
1409 },
1410 OffenceReported {
1413 offence_era: EraIndex,
1414 validator: T::AccountId,
1415 fraction: Perbill,
1416 },
1417 SlashComputed {
1419 offence_era: EraIndex,
1420 slash_era: EraIndex,
1421 offender: T::AccountId,
1422 page: u32,
1423 },
1424 SlashCancelled {
1426 slash_era: EraIndex,
1427 validator: T::AccountId,
1428 },
1429 SessionRotated {
1434 starting_session: SessionIndex,
1435 active_era: EraIndex,
1436 planned_era: EraIndex,
1437 },
1438 Unexpected(UnexpectedKind<T>),
1441 OffenceTooOld {
1443 offence_era: EraIndex,
1444 validator: T::AccountId,
1445 fraction: Perbill,
1446 },
1447 EraPruned {
1449 index: EraIndex,
1450 },
1451 ValidatorIncentivePaid {
1453 era: EraIndex,
1454 validator_stash: T::AccountId,
1455 dest: RewardDestination<T::AccountId>,
1456 amount: BalanceOf<T>,
1457 },
1458 ValidatorIncentiveConfigSet {
1460 optimum_self_stake: BalanceOf<T>,
1461 hard_cap_self_stake: BalanceOf<T>,
1462 slope_factor: Perbill,
1463 },
1464 }
1465
1466 #[derive(Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, TypeInfo, DebugNoBound)]
1472 #[codec(mel_bound())]
1473 #[scale_info(skip_type_params(T))]
1474 pub enum UnexpectedKind<T: Config> {
1475 EraDurationBoundExceeded,
1477 UnknownValidatorActivation,
1479 PagedElectionOutOfWeight { page: PageIndex, required: Weight, had: Weight },
1481 MissingPayee { era: EraIndex, stash: T::AccountId },
1483 ValidatorIncentiveWeightMismatch { era: EraIndex },
1485 ValidatorIncentiveTransferFailed { era: EraIndex },
1487 }
1488
1489 #[pallet::error]
1490 #[derive(PartialEq)]
1491 pub enum Error<T> {
1492 NotController,
1494 NotStash,
1496 AlreadyBonded,
1498 AlreadyPaired,
1500 EmptyTargets,
1502 DuplicateIndex,
1504 InvalidSlashRecord,
1506 InsufficientBond,
1510 NoMoreChunks,
1512 NoUnlockChunk,
1514 FundedTarget,
1516 InvalidEraToReward,
1518 InvalidNumberOfNominations,
1520 AlreadyClaimed,
1522 InvalidPage,
1524 IncorrectHistoryDepth,
1526 BadState,
1528 TooManyTargets,
1530 BadTarget,
1532 CannotChillOther,
1534 TooManyNominators,
1537 TooManyValidators,
1540 CommissionTooLow,
1542 BoundNotMet,
1544 ControllerDeprecated,
1546 CannotRestoreLedger,
1548 RewardDestinationRestricted,
1550 NotEnoughFunds,
1552 VirtualStakerNotAllowed,
1554 CannotReapStash,
1556 AlreadyMigrated,
1558 EraNotStarted,
1560 Restricted,
1563 UnappliedSlashesInPreviousEra,
1566 EraNotPrunable,
1568 CancelledSlash,
1570 CommissionTooHigh,
1572 OptimumGreaterThanCap,
1574 InvalidInactivityProof(InvalidInactivityProofError),
1576 InvalidChillInactiveThreshold,
1578 }
1579
1580 #[derive(Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, TypeInfo, PalletError)]
1581 pub enum InvalidInactivityProofError {
1582 InvalidLen,
1584 NotSorted,
1586 ValidatorNotExposed,
1588 ValidatorActive,
1590 InvalidEra,
1592 }
1593
1594 impl<T: Config> Pallet<T> {
1595 pub fn apply_unapplied_slashes(active_era: EraIndex) -> Weight {
1597 let mut slashes = UnappliedSlashes::<T>::iter_prefix(&active_era).take(1);
1598 if let Some((key, slash)) = slashes.next() {
1599 crate::log!(
1600 debug,
1601 "🦹 found slash {:?} scheduled to be executed in era {:?}",
1602 slash,
1603 active_era,
1604 );
1605
1606 let nominators_slashed = slash.others.len() as u32;
1607
1608 if Self::check_slash_cancelled(active_era, &key.0, key.1) {
1610 crate::log!(
1611 debug,
1612 "🦹 slash for {:?} in era {:?} was cancelled, skipping",
1613 key.0,
1614 active_era,
1615 );
1616 } else {
1617 slashing::apply_slash::<T>(slash, Self::offence_era_of(active_era));
1618 }
1619
1620 UnappliedSlashes::<T>::remove(&active_era, &key);
1622
1623 if UnappliedSlashes::<T>::iter_prefix(&active_era).next().is_none() {
1625 CancelledSlashes::<T>::remove(&active_era);
1627 }
1628
1629 T::WeightInfo::apply_slash(nominators_slashed)
1630 } else {
1631 T::DbWeight::get().reads(1)
1633 }
1634 }
1635
1636 fn do_prune_era_step(era: EraIndex) -> Result<Weight, DispatchError> {
1638 let current_step = EraPruningState::<T>::get(era).ok_or(Error::<T>::EraNotPrunable)?;
1643
1644 let items_limit = T::MaxPruningItems::get().min(T::MaxValidatorSet::get());
1647
1648 let actual_weight = match current_step {
1649 PruningStep::ErasStakersPaged => {
1650 let result = ErasStakersPaged::<T>::clear_prefix((era,), items_limit, None);
1651 let items_deleted = result.backend as u32;
1652 result.maybe_cursor.is_none().then(|| {
1653 EraPruningState::<T>::insert(era, PruningStep::ErasStakersOverview)
1654 });
1655 T::WeightInfo::prune_era_stakers_paged(items_deleted)
1656 },
1657 PruningStep::ErasStakersOverview => {
1658 let result = ErasStakersOverview::<T>::clear_prefix(era, items_limit, None);
1659 let items_deleted = result.backend as u32;
1660 result.maybe_cursor.is_none().then(|| {
1661 EraPruningState::<T>::insert(era, PruningStep::ErasValidatorPrefs)
1662 });
1663 T::WeightInfo::prune_era_stakers_overview(items_deleted)
1664 },
1665 PruningStep::ErasValidatorPrefs => {
1666 let result = ErasValidatorPrefs::<T>::clear_prefix(era, items_limit, None);
1667 let items_deleted = result.backend as u32;
1668 result
1669 .maybe_cursor
1670 .is_none()
1671 .then(|| EraPruningState::<T>::insert(era, PruningStep::ClaimedRewards));
1672 T::WeightInfo::prune_era_validator_prefs(items_deleted)
1673 },
1674 PruningStep::ClaimedRewards => {
1675 let result = ClaimedRewards::<T>::clear_prefix(era, items_limit, None);
1676 let items_deleted = result.backend as u32;
1677 result.maybe_cursor.is_none().then(|| {
1678 EraPruningState::<T>::insert(era, PruningStep::ErasValidatorReward)
1679 });
1680 T::WeightInfo::prune_era_claimed_rewards(items_deleted)
1681 },
1682 PruningStep::ErasValidatorReward => {
1683 ErasValidatorReward::<T>::remove(era);
1684 EraPruningState::<T>::insert(era, PruningStep::ErasRewardPoints);
1685 T::WeightInfo::prune_era_validator_reward()
1686 },
1687 PruningStep::ErasRewardPoints => {
1688 ErasRewardPoints::<T>::remove(era);
1689 EraPruningState::<T>::insert(era, PruningStep::SingleEntryCleanups);
1690 T::WeightInfo::prune_era_reward_points()
1691 },
1692 PruningStep::SingleEntryCleanups => {
1693 ErasTotalStake::<T>::remove(era);
1694 ErasNominatorsSlashable::<T>::remove(era);
1695 ErasValidatorIncentiveBudget::<T>::remove(era);
1696 ErasSumValidatorIncentiveWeight::<T>::remove(era);
1697 ErasSumWeightedPoints::<T>::remove(era);
1698 EraPruningState::<T>::insert(era, PruningStep::ValidatorSlashInEra);
1699 T::WeightInfo::prune_era_single_entry_cleanups()
1700 },
1701 PruningStep::ValidatorSlashInEra => {
1702 let result = ValidatorSlashInEra::<T>::clear_prefix(era, items_limit, None);
1703 let items_deleted = result.backend as u32;
1704
1705 if result.maybe_cursor.is_none() {
1706 EraPruningState::<T>::insert(
1707 era,
1708 PruningStep::ErasValidatorIncentiveWeight,
1709 );
1710 }
1711
1712 T::WeightInfo::prune_era_validator_slash_in_era(items_deleted)
1713 },
1714 PruningStep::ErasValidatorIncentiveWeight => {
1715 let result =
1716 ErasValidatorIncentiveWeight::<T>::clear_prefix(era, items_limit, None);
1717 if result.maybe_cursor.is_none() {
1718 EraPruningState::<T>::remove(era);
1720 }
1721 T::WeightInfo::prune_era_validator_incentive_weight(result.backend as u32)
1722 },
1723 };
1724
1725 if EraPruningState::<T>::get(era).is_none() {
1727 Self::deposit_event(Event::<T>::EraPruned { index: era });
1728 }
1729
1730 Ok(actual_weight)
1731 }
1732 }
1733
1734 #[pallet::hooks]
1735 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
1736 fn on_poll(_now: BlockNumberFor<T>, weight_meter: &mut WeightMeter) {
1737 let (weight, exec) = EraElectionPlanner::<T>::maybe_fetch_election_results();
1738 crate::log!(
1739 trace,
1740 "weight of fetching next election page is {:?}, have {:?}",
1741 weight,
1742 weight_meter.remaining()
1743 );
1744
1745 if weight_meter.can_consume(weight) {
1746 exec(weight_meter);
1747 } else {
1748 Self::deposit_event(Event::<T>::Unexpected(
1749 UnexpectedKind::PagedElectionOutOfWeight {
1750 page: NextElectionPage::<T>::get().unwrap_or(
1751 EraElectionPlanner::<T>::election_pages().defensive_saturating_sub(1),
1752 ),
1753 required: weight,
1754 had: weight_meter.remaining(),
1755 },
1756 ));
1757 }
1758 }
1759
1760 fn on_initialize(_now: BlockNumberFor<T>) -> Weight {
1761 let mut consumed_weight = slashing::process_offence_for_era::<T>();
1763
1764 consumed_weight.saturating_accrue(T::DbWeight::get().reads(1));
1766 if let Some(active_era) = ActiveEra::<T>::get() {
1767 let slash_weight = Self::apply_unapplied_slashes(active_era.index);
1768 consumed_weight.saturating_accrue(slash_weight);
1769 }
1770
1771 consumed_weight
1772 }
1773
1774 fn integrity_test() {
1775 assert_eq!(
1777 MaxNominationsOf::<T>::get(),
1778 <Self as ElectionDataProvider>::MaxVotesPerVoter::get()
1779 );
1780
1781 assert!(!MaxNominationsOf::<T>::get().is_zero());
1783
1784 assert!(
1785 T::SlashDeferDuration::get() < T::BondingDuration::get() || T::BondingDuration::get() == 0,
1786 "As per documentation, slash defer duration ({}) should be less than bonding duration ({}).",
1787 T::SlashDeferDuration::get(),
1788 T::BondingDuration::get(),
1789 );
1790
1791 assert!(
1793 T::NominatorFastUnbondDuration::get() <= T::BondingDuration::get(),
1794 "NominatorFastUnbondDuration ({}) must not exceed BondingDuration ({}).",
1795 T::NominatorFastUnbondDuration::get(),
1796 T::BondingDuration::get(),
1797 );
1798 assert!(
1800 T::MaxPruningItems::get() >= 100,
1801 "MaxPruningItems must be at least 100 for efficient pruning, got: {}",
1802 T::MaxPruningItems::get()
1803 );
1804
1805 assert!(
1806 crate::POT_POOL_SIZE > T::HistoryDepth::get(),
1807 "POT_POOL_SIZE ({}) must be strictly greater than HistoryDepth ({}) \
1808 to avoid reusing a pot slot whose era is still in the active history.",
1809 crate::POT_POOL_SIZE,
1810 T::HistoryDepth::get(),
1811 );
1812
1813 if T::DisableMinting::get() {
1815 let (v, r) = T::EraPayout::era_payout(
1816 BalanceOf::<T>::from(1u64),
1817 BalanceOf::<T>::from(1u64),
1818 1000u64,
1819 );
1820 assert!(
1821 v.is_zero() && r.is_zero(),
1822 "DisableMinting is true but EraPayout returns non-zero. \
1823 Set EraPayout = () when DisableMinting = true."
1824 );
1825 }
1826 }
1827
1828 #[cfg(feature = "try-runtime")]
1829 fn try_state(n: BlockNumberFor<T>) -> Result<(), sp_runtime::TryRuntimeError> {
1830 Self::do_try_state(n)
1831 }
1832 }
1833
1834 #[pallet::call]
1835 impl<T: Config> Pallet<T> {
1836 #[pallet::call_index(0)]
1849 #[pallet::weight(T::WeightInfo::bond())]
1850 pub fn bond(
1851 origin: OriginFor<T>,
1852 #[pallet::compact] value: BalanceOf<T>,
1853 payee: RewardDestination<T::AccountId>,
1854 ) -> DispatchResult {
1855 let stash = ensure_signed(origin)?;
1856
1857 ensure!(!T::Filter::contains(&stash), Error::<T>::Restricted);
1858
1859 if StakingLedger::<T>::is_bonded(StakingAccount::Stash(stash.clone())) {
1860 return Err(Error::<T>::AlreadyBonded.into());
1861 }
1862
1863 if StakingLedger::<T>::is_bonded(StakingAccount::Controller(stash.clone())) {
1865 return Err(Error::<T>::AlreadyPaired.into());
1866 }
1867
1868 if value < Self::min_chilled_bond() {
1870 return Err(Error::<T>::InsufficientBond.into());
1871 }
1872
1873 let stash_balance = asset::free_to_stake::<T>(&stash);
1874 let value = value.min(stash_balance);
1875 Self::deposit_event(Event::<T>::Bonded { stash: stash.clone(), amount: value });
1876 let ledger = StakingLedger::<T>::new(stash.clone(), value);
1877
1878 ledger.bond(payee)?;
1881
1882 Ok(())
1883 }
1884
1885 #[pallet::call_index(1)]
1896 #[pallet::weight(T::WeightInfo::bond_extra())]
1897 pub fn bond_extra(
1898 origin: OriginFor<T>,
1899 #[pallet::compact] max_additional: BalanceOf<T>,
1900 ) -> DispatchResult {
1901 let stash = ensure_signed(origin)?;
1902 ensure!(!T::Filter::contains(&stash), Error::<T>::Restricted);
1903 Self::do_bond_extra(&stash, max_additional)
1904 }
1905
1906 #[pallet::call_index(2)]
1926 #[pallet::weight(
1927 T::WeightInfo::withdraw_unbonded_kill().saturating_add(T::WeightInfo::unbond()))
1928 ]
1929 pub fn unbond(
1930 origin: OriginFor<T>,
1931 #[pallet::compact] value: BalanceOf<T>,
1932 ) -> DispatchResultWithPostInfo {
1933 let controller = ensure_signed(origin)?;
1934 let unlocking =
1935 Self::ledger(Controller(controller.clone())).map(|l| l.unlocking.len())?;
1936
1937 let maybe_withdraw_weight = {
1940 if unlocking == T::MaxUnlockingChunks::get() as usize {
1941 Some(Self::do_withdraw_unbonded(&controller)?)
1942 } else {
1943 None
1944 }
1945 };
1946
1947 let mut ledger = Self::ledger(Controller(controller))?;
1950 let mut value = value.min(ledger.active);
1951 let stash = ledger.stash.clone();
1952
1953 let chill_weight = if value >= ledger.active {
1956 Self::chill_stash(&stash);
1957 T::WeightInfo::chill()
1958 } else {
1959 Weight::zero()
1960 };
1961
1962 ensure!(
1963 ledger.unlocking.len() < T::MaxUnlockingChunks::get() as usize,
1964 Error::<T>::NoMoreChunks,
1965 );
1966
1967 if !value.is_zero() {
1968 ledger.active -= value;
1969
1970 if ledger.active < asset::existential_deposit::<T>() {
1972 value += ledger.active;
1973 ledger.active = Zero::zero();
1974 }
1975
1976 let is_nominator = Nominators::<T>::contains_key(&stash);
1977
1978 let min_active_bond = if is_nominator {
1979 Self::min_nominator_bond()
1980 } else if Validators::<T>::contains_key(&stash) {
1981 Self::min_validator_bond()
1982 } else {
1983 Zero::zero()
1985 };
1986
1987 ensure!(ledger.active >= min_active_bond, Error::<T>::InsufficientBond);
1990
1991 let active_era = session_rotation::Rotator::<T>::active_era();
1997 let was_recent_validator = LastValidatorEra::<T>::get(&stash)
1998 .map(|last_era| active_era.saturating_sub(last_era) < T::BondingDuration::get())
1999 .unwrap_or(false);
2000
2001 let unbond_duration = if was_recent_validator {
2002 T::BondingDuration::get()
2004 } else {
2005 <Self as sp_staking::StakingInterface>::nominator_bonding_duration()
2007 };
2008
2009 let era =
2010 session_rotation::Rotator::<T>::active_era().saturating_add(unbond_duration);
2011 if let Some(chunk) = ledger.unlocking.last_mut().filter(|chunk| chunk.era == era) {
2012 chunk.value = chunk.value.defensive_saturating_add(value)
2016 } else {
2017 ledger
2018 .unlocking
2019 .try_push(UnlockChunk { value, era })
2020 .map_err(|_| Error::<T>::NoMoreChunks)?;
2021 };
2022 ledger.update()?;
2024
2025 if T::VoterList::contains(&stash) {
2027 let _ = T::VoterList::on_update(&stash, Self::weight_of(&stash));
2028 }
2029
2030 Self::deposit_event(Event::<T>::Unbonded { stash, amount: value });
2031 }
2032
2033 let actual_weight = if let Some(withdraw_weight) = maybe_withdraw_weight {
2034 Some(
2035 T::WeightInfo::unbond()
2036 .saturating_add(withdraw_weight)
2037 .saturating_add(chill_weight),
2038 )
2039 } else {
2040 Some(T::WeightInfo::unbond().saturating_add(chill_weight))
2041 };
2042
2043 Ok(actual_weight.into())
2044 }
2045
2046 #[pallet::call_index(3)]
2066 #[pallet::weight(T::WeightInfo::withdraw_unbonded_kill())]
2067 pub fn withdraw_unbonded(
2068 origin: OriginFor<T>,
2069 _num_slashing_spans: u32,
2070 ) -> DispatchResultWithPostInfo {
2071 let controller = ensure_signed(origin)?;
2072
2073 let actual_weight = Self::do_withdraw_unbonded(&controller)?;
2074 Ok(Some(actual_weight).into())
2075 }
2076
2077 #[pallet::call_index(4)]
2083 #[pallet::weight(T::WeightInfo::validate())]
2084 pub fn validate(origin: OriginFor<T>, prefs: ValidatorPrefs) -> DispatchResult {
2085 let controller = ensure_signed(origin)?;
2086
2087 let ledger = Self::ledger(Controller(controller))?;
2088
2089 ensure!(ledger.active >= Self::min_validator_bond(), Error::<T>::InsufficientBond);
2090 let stash = &ledger.stash;
2091
2092 ensure!(prefs.commission >= MinCommission::<T>::get(), Error::<T>::CommissionTooLow);
2094 ensure!(prefs.commission <= MaxCommission::<T>::get(), Error::<T>::CommissionTooHigh);
2095
2096 if !Validators::<T>::contains_key(stash) {
2098 if let Some(max_validators) = MaxValidatorsCount::<T>::get() {
2102 ensure!(
2103 Validators::<T>::count() < max_validators,
2104 Error::<T>::TooManyValidators
2105 );
2106 }
2107 }
2108
2109 Self::do_remove_nominator(stash);
2110 Self::do_add_validator(stash, prefs.clone());
2111 Self::deposit_event(Event::<T>::ValidatorPrefsSet { stash: ledger.stash, prefs });
2112
2113 Ok(())
2114 }
2115
2116 #[pallet::call_index(5)]
2122 #[pallet::weight(T::WeightInfo::nominate(targets.len() as u32))]
2123 pub fn nominate(
2124 origin: OriginFor<T>,
2125 targets: Vec<AccountIdLookupOf<T>>,
2126 ) -> DispatchResult {
2127 let controller = ensure_signed(origin)?;
2128
2129 let ledger = Self::ledger(StakingAccount::Controller(controller.clone()))?;
2130
2131 ensure!(ledger.active >= Self::min_nominator_bond(), Error::<T>::InsufficientBond);
2132 let stash = &ledger.stash;
2133
2134 if !Nominators::<T>::contains_key(stash) {
2136 if let Some(max_nominators) = MaxNominatorsCount::<T>::get() {
2140 ensure!(
2141 Nominators::<T>::count() < max_nominators,
2142 Error::<T>::TooManyNominators
2143 );
2144 }
2145 }
2146
2147 let mut targets = targets
2149 .into_iter()
2150 .map(|t| T::Lookup::lookup(t).map_err(DispatchError::from))
2151 .collect::<Result<Vec<_>, _>>()?;
2152 targets.sort();
2153 targets.dedup();
2154
2155 ensure!(!targets.is_empty(), Error::<T>::EmptyTargets);
2156 ensure!(
2157 targets.len() <= T::NominationsQuota::get_quota(ledger.active) as usize,
2158 Error::<T>::TooManyTargets
2159 );
2160
2161 let old = Nominators::<T>::get(stash).map_or_else(Vec::new, |x| x.targets.into_inner());
2162
2163 let targets: BoundedVec<_, _> = targets
2164 .into_iter()
2165 .map(|n| {
2166 if old.contains(&n) ||
2167 (Validators::<T>::contains_key(&n) && !Validators::<T>::get(&n).blocked)
2168 {
2169 Ok(n)
2170 } else {
2171 Err(Error::<T>::BadTarget.into())
2172 }
2173 })
2174 .collect::<Result<Vec<_>, DispatchError>>()?
2175 .try_into()
2176 .map_err(|_| Error::<T>::TooManyNominators)?;
2177
2178 let nominations = Nominations {
2179 targets,
2180 submitted_in: CurrentEra::<T>::get().unwrap_or(0),
2182 suppressed: false,
2183 };
2184
2185 Self::do_remove_validator(stash);
2186 Self::do_add_nominator(stash, nominations);
2187 Ok(())
2188 }
2189
2190 #[pallet::call_index(6)]
2201 #[pallet::weight(T::WeightInfo::chill())]
2202 pub fn chill(origin: OriginFor<T>) -> DispatchResult {
2203 let controller = ensure_signed(origin)?;
2204
2205 let ledger = Self::ledger(StakingAccount::Controller(controller))?;
2206
2207 Self::chill_stash(&ledger.stash);
2208 Ok(())
2209 }
2210
2211 #[pallet::call_index(7)]
2217 #[pallet::weight(T::WeightInfo::set_payee())]
2218 pub fn set_payee(
2219 origin: OriginFor<T>,
2220 payee: RewardDestination<T::AccountId>,
2221 ) -> DispatchResult {
2222 let controller = ensure_signed(origin)?;
2223 let ledger = Self::ledger(Controller(controller.clone()))?;
2224
2225 ensure!(
2226 (payee != {
2227 #[allow(deprecated)]
2228 RewardDestination::Controller
2229 }),
2230 Error::<T>::ControllerDeprecated
2231 );
2232
2233 let _ = ledger
2234 .set_payee(payee)
2235 .defensive_proof("ledger was retrieved from storage, thus it's bonded; qed.")?;
2236
2237 Ok(())
2238 }
2239
2240 #[pallet::call_index(8)]
2249 #[pallet::weight(T::WeightInfo::set_controller())]
2250 pub fn set_controller(origin: OriginFor<T>) -> DispatchResult {
2251 let stash = ensure_signed(origin)?;
2252
2253 Self::ledger(StakingAccount::Stash(stash.clone())).map(|ledger| {
2254 let controller = ledger.controller()
2255 .defensive_proof("Ledger's controller field didn't exist. The controller should have been fetched using StakingLedger.")
2256 .ok_or(Error::<T>::NotController)?;
2257
2258 if controller == stash {
2259 return Err(Error::<T>::AlreadyPaired.into())
2261 }
2262
2263 let _ = ledger.set_controller_to_stash()?;
2264 Ok(())
2265 })?
2266 }
2267
2268 #[pallet::call_index(9)]
2272 #[pallet::weight(T::WeightInfo::set_validator_count())]
2273 pub fn set_validator_count(
2274 origin: OriginFor<T>,
2275 #[pallet::compact] new: u32,
2276 ) -> DispatchResult {
2277 ensure_root(origin)?;
2278
2279 ensure!(new <= T::MaxValidatorSet::get(), Error::<T>::TooManyValidators);
2280
2281 ValidatorCount::<T>::put(new);
2282 Ok(())
2283 }
2284
2285 #[pallet::call_index(10)]
2290 #[pallet::weight(T::WeightInfo::set_validator_count())]
2291 pub fn increase_validator_count(
2292 origin: OriginFor<T>,
2293 #[pallet::compact] additional: u32,
2294 ) -> DispatchResult {
2295 ensure_root(origin)?;
2296 let old = ValidatorCount::<T>::get();
2297 let new = old.checked_add(additional).ok_or(ArithmeticError::Overflow)?;
2298
2299 ensure!(new <= T::MaxValidatorSet::get(), Error::<T>::TooManyValidators);
2300
2301 ValidatorCount::<T>::put(new);
2302 Ok(())
2303 }
2304
2305 #[pallet::call_index(11)]
2310 #[pallet::weight(T::WeightInfo::set_validator_count())]
2311 pub fn scale_validator_count(origin: OriginFor<T>, factor: Percent) -> DispatchResult {
2312 ensure_root(origin)?;
2313 let old = ValidatorCount::<T>::get();
2314 let new = old.checked_add(factor.mul_floor(old)).ok_or(ArithmeticError::Overflow)?;
2315
2316 ensure!(new <= T::MaxValidatorSet::get(), Error::<T>::TooManyValidators);
2317
2318 ValidatorCount::<T>::put(new);
2319 Ok(())
2320 }
2321
2322 #[pallet::call_index(12)]
2332 #[pallet::weight(T::WeightInfo::force_no_eras())]
2333 pub fn force_no_eras(origin: OriginFor<T>) -> DispatchResult {
2334 ensure_root(origin)?;
2335 Self::set_force_era(Forcing::ForceNone);
2336 Ok(())
2337 }
2338
2339 #[pallet::call_index(13)]
2350 #[pallet::weight(T::WeightInfo::force_new_era())]
2351 pub fn force_new_era(origin: OriginFor<T>) -> DispatchResult {
2352 ensure_root(origin)?;
2353 Self::set_force_era(Forcing::ForceNew);
2354 Ok(())
2355 }
2356
2357 #[pallet::call_index(15)]
2366 #[pallet::weight(T::WeightInfo::force_unstake())]
2367 pub fn force_unstake(
2368 origin: OriginFor<T>,
2369 stash: T::AccountId,
2370 _num_slashing_spans: u32,
2371 ) -> DispatchResult {
2372 ensure_root(origin)?;
2373
2374 Self::kill_stash(&stash)?;
2376
2377 Ok(())
2378 }
2379
2380 #[pallet::call_index(16)]
2390 #[pallet::weight(T::WeightInfo::force_new_era_always())]
2391 pub fn force_new_era_always(origin: OriginFor<T>) -> DispatchResult {
2392 ensure_root(origin)?;
2393 Self::set_force_era(Forcing::ForceAlways);
2394 Ok(())
2395 }
2396
2397 #[pallet::call_index(17)]
2409 #[pallet::weight(T::WeightInfo::cancel_deferred_slash(validator_slashes.len() as u32))]
2410 pub fn cancel_deferred_slash(
2411 origin: OriginFor<T>,
2412 era: EraIndex,
2413 validator_slashes: Vec<(T::AccountId, Perbill)>,
2414 ) -> DispatchResult {
2415 T::AdminOrigin::ensure_origin(origin)?;
2416 ensure!(!validator_slashes.is_empty(), Error::<T>::EmptyTargets);
2417
2418 let mut cancelled_slashes = CancelledSlashes::<T>::get(&era);
2420
2421 for (validator, slash_fraction) in validator_slashes {
2423 cancelled_slashes.retain(|(v, _)| v != &validator);
2428
2429 cancelled_slashes
2431 .try_push((validator.clone(), slash_fraction))
2432 .map_err(|_| Error::<T>::BoundNotMet)
2433 .defensive_proof("cancelled_slashes should have capacity for all validators")?;
2434
2435 Self::deposit_event(Event::<T>::SlashCancelled { slash_era: era, validator });
2436 }
2437
2438 CancelledSlashes::<T>::insert(&era, cancelled_slashes);
2440
2441 Ok(())
2442 }
2443
2444 #[pallet::call_index(18)]
2458 #[pallet::weight(T::WeightInfo::payout_stakers_alive_staked(T::MaxExposurePageSize::get()))]
2459 pub fn payout_stakers(
2460 origin: OriginFor<T>,
2461 validator_stash: T::AccountId,
2462 era: EraIndex,
2463 ) -> DispatchResultWithPostInfo {
2464 ensure_signed(origin)?;
2465
2466 Self::do_payout_stakers(validator_stash, era)
2467 }
2468
2469 #[pallet::call_index(19)]
2473 #[pallet::weight(T::WeightInfo::rebond(T::MaxUnlockingChunks::get() as u32))]
2474 pub fn rebond(
2475 origin: OriginFor<T>,
2476 #[pallet::compact] value: BalanceOf<T>,
2477 ) -> DispatchResultWithPostInfo {
2478 let controller = ensure_signed(origin)?;
2479 let ledger = Self::ledger(Controller(controller))?;
2480
2481 ensure!(!T::Filter::contains(&ledger.stash), Error::<T>::Restricted);
2482 ensure!(!ledger.unlocking.is_empty(), Error::<T>::NoUnlockChunk);
2483
2484 let initial_unlocking = ledger.unlocking.len() as u32;
2485 let (ledger, rebonded_value) = ledger.rebond(value);
2486 ensure!(ledger.active >= Self::min_chilled_bond(), Error::<T>::InsufficientBond);
2488
2489 Self::deposit_event(Event::<T>::Bonded {
2490 stash: ledger.stash.clone(),
2491 amount: rebonded_value,
2492 });
2493
2494 let stash = ledger.stash.clone();
2495 let final_unlocking = ledger.unlocking.len();
2496
2497 ledger.update()?;
2499 if T::VoterList::contains(&stash) {
2500 let _ = T::VoterList::on_update(&stash, Self::weight_of(&stash));
2501 }
2502
2503 let removed_chunks = 1u32 .saturating_add(initial_unlocking)
2505 .saturating_sub(final_unlocking as u32);
2506 Ok(Some(T::WeightInfo::rebond(removed_chunks)).into())
2507 }
2508
2509 #[pallet::call_index(20)]
2534 #[pallet::weight(T::WeightInfo::reap_stash())]
2535 pub fn reap_stash(
2536 origin: OriginFor<T>,
2537 stash: T::AccountId,
2538 _num_slashing_spans: u32,
2539 ) -> DispatchResultWithPostInfo {
2540 let _ = ensure_signed(origin)?;
2541
2542 ensure!(!Self::is_virtual_staker(&stash), Error::<T>::VirtualStakerNotAllowed);
2544
2545 let ed = asset::existential_deposit::<T>();
2546 let origin_balance = asset::total_balance::<T>(&stash);
2547 let ledger_total =
2548 Self::ledger(Stash(stash.clone())).map(|l| l.total).unwrap_or_default();
2549 let reapable = origin_balance < ed ||
2550 origin_balance.is_zero() ||
2551 ledger_total < ed ||
2552 ledger_total.is_zero();
2553 ensure!(reapable, Error::<T>::FundedTarget);
2554
2555 Self::kill_stash(&stash)?;
2557
2558 Ok(Pays::No.into())
2559 }
2560
2561 #[pallet::call_index(21)]
2573 #[pallet::weight(T::WeightInfo::kick(who.len() as u32))]
2574 pub fn kick(origin: OriginFor<T>, who: Vec<AccountIdLookupOf<T>>) -> DispatchResult {
2575 let controller = ensure_signed(origin)?;
2576 let ledger = Self::ledger(Controller(controller))?;
2577 let stash = &ledger.stash;
2578
2579 for nom_stash in who
2580 .into_iter()
2581 .map(T::Lookup::lookup)
2582 .collect::<Result<Vec<T::AccountId>, _>>()?
2583 .into_iter()
2584 {
2585 Nominators::<T>::mutate(&nom_stash, |maybe_nom| {
2586 if let Some(ref mut nom) = maybe_nom {
2587 if let Some(pos) = nom.targets.iter().position(|v| v == stash) {
2588 nom.targets.swap_remove(pos);
2589 Self::deposit_event(Event::<T>::Kicked {
2590 nominator: nom_stash.clone(),
2591 stash: stash.clone(),
2592 });
2593 }
2594 }
2595 });
2596 }
2597
2598 Ok(())
2599 }
2600
2601 #[pallet::call_index(22)]
2624 #[pallet::weight(
2625 T::WeightInfo::set_staking_configs_all_set()
2626 .max(T::WeightInfo::set_staking_configs_all_remove())
2627 )]
2628 pub fn set_staking_configs(
2629 origin: OriginFor<T>,
2630 min_nominator_bond: ConfigOp<BalanceOf<T>>,
2631 min_validator_bond: ConfigOp<BalanceOf<T>>,
2632 max_nominator_count: ConfigOp<u32>,
2633 max_validator_count: ConfigOp<u32>,
2634 chill_threshold: ConfigOp<Percent>,
2635 min_commission: ConfigOp<Perbill>,
2636 max_staked_rewards: ConfigOp<Percent>,
2637 are_nominators_slashable: ConfigOp<bool>,
2638 chill_inactive_threshold: ConfigOp<u32>,
2639 ) -> DispatchResult {
2640 ensure_root(origin)?;
2641
2642 if let ConfigOp::Set(threshold) = chill_inactive_threshold {
2643 ensure!(
2644 threshold > 1 && threshold <= T::HistoryDepth::get(),
2645 Error::<T>::InvalidChillInactiveThreshold
2646 );
2647 }
2648
2649 macro_rules! config_op_exp {
2650 ($storage:ty, $op:ident) => {
2651 match $op {
2652 ConfigOp::Noop => (),
2653 ConfigOp::Set(v) => <$storage>::put(v),
2654 ConfigOp::Remove => <$storage>::kill(),
2655 }
2656 };
2657 }
2658
2659 config_op_exp!(MinNominatorBond<T>, min_nominator_bond);
2660 config_op_exp!(MinValidatorBond<T>, min_validator_bond);
2661 config_op_exp!(MaxNominatorsCount<T>, max_nominator_count);
2662 config_op_exp!(MaxValidatorsCount<T>, max_validator_count);
2663 config_op_exp!(ChillThreshold<T>, chill_threshold);
2664 config_op_exp!(MinCommission<T>, min_commission);
2665 config_op_exp!(MaxStakedRewards<T>, max_staked_rewards);
2666 config_op_exp!(AreNominatorsSlashable<T>, are_nominators_slashable);
2667 config_op_exp!(ChillInactiveThreshold<T>, chill_inactive_threshold);
2668
2669 Ok(())
2670 }
2671 #[pallet::call_index(23)]
2698 #[pallet::weight(T::WeightInfo::chill_other())]
2699 pub fn chill_other(origin: OriginFor<T>, stash: T::AccountId) -> DispatchResult {
2700 let caller = ensure_signed(origin)?;
2702 let ledger = Self::ledger(Stash(stash.clone()))?;
2703 let controller = ledger
2704 .controller()
2705 .defensive_proof(
2706 "Ledger's controller field didn't exist. The controller should have been fetched using StakingLedger.",
2707 )
2708 .ok_or(Error::<T>::NotController)?;
2709
2710 if Nominators::<T>::contains_key(&stash) && Nominators::<T>::get(&stash).is_none() {
2727 Self::chill_stash(&stash);
2728 return Ok(());
2729 }
2730
2731 if caller != controller {
2732 let threshold = ChillThreshold::<T>::get().ok_or(Error::<T>::CannotChillOther)?;
2733 let min_active_bond = if Nominators::<T>::contains_key(&stash) {
2734 let max_nominator_count =
2735 MaxNominatorsCount::<T>::get().ok_or(Error::<T>::CannotChillOther)?;
2736 let current_nominator_count = Nominators::<T>::count();
2737 ensure!(
2738 threshold * max_nominator_count < current_nominator_count,
2739 Error::<T>::CannotChillOther
2740 );
2741 Self::min_nominator_bond()
2742 } else if Validators::<T>::contains_key(&stash) {
2743 let max_validator_count =
2744 MaxValidatorsCount::<T>::get().ok_or(Error::<T>::CannotChillOther)?;
2745 let current_validator_count = Validators::<T>::count();
2746 ensure!(
2747 threshold * max_validator_count < current_validator_count,
2748 Error::<T>::CannotChillOther
2749 );
2750 Self::min_validator_bond()
2751 } else {
2752 Zero::zero()
2753 };
2754
2755 ensure!(ledger.active < min_active_bond, Error::<T>::CannotChillOther);
2756 }
2757
2758 Self::chill_stash(&stash);
2759 Ok(())
2760 }
2761
2762 #[pallet::call_index(24)]
2767 #[pallet::weight(T::WeightInfo::force_apply_min_commission())]
2768 pub fn force_apply_min_commission(
2769 origin: OriginFor<T>,
2770 validator_stash: T::AccountId,
2771 ) -> DispatchResult {
2772 ensure_signed(origin)?;
2773 let min_commission = MinCommission::<T>::get();
2774 let max_commission = MaxCommission::<T>::get();
2775 Validators::<T>::try_mutate_exists(validator_stash, |maybe_prefs| {
2776 maybe_prefs
2777 .as_mut()
2778 .map(|prefs| {
2779 if prefs.commission < min_commission {
2780 prefs.commission = min_commission;
2781 }
2782 if prefs.commission > max_commission {
2783 prefs.commission = max_commission;
2784 }
2785 })
2786 .ok_or(Error::<T>::NotStash)
2787 })?;
2788 Ok(())
2789 }
2790
2791 #[pallet::call_index(25)]
2796 #[pallet::weight(T::WeightInfo::set_min_commission())]
2797 pub fn set_min_commission(origin: OriginFor<T>, new: Perbill) -> DispatchResult {
2798 T::AdminOrigin::ensure_origin(origin)?;
2799 ensure!(new <= MaxCommission::<T>::get(), Error::<T>::CommissionTooHigh);
2800 MinCommission::<T>::put(new);
2801 Ok(())
2802 }
2803
2804 #[pallet::call_index(26)]
2826 #[pallet::weight(T::WeightInfo::payout_stakers_alive_staked(T::MaxExposurePageSize::get()))]
2827 pub fn payout_stakers_by_page(
2828 origin: OriginFor<T>,
2829 validator_stash: T::AccountId,
2830 era: EraIndex,
2831 page: Page,
2832 ) -> DispatchResultWithPostInfo {
2833 ensure_signed(origin)?;
2834 Self::do_payout_stakers_by_page(validator_stash, era, page)
2835 }
2836
2837 #[pallet::call_index(27)]
2844 #[pallet::weight(T::WeightInfo::update_payee())]
2845 pub fn update_payee(
2846 origin: OriginFor<T>,
2847 controller: T::AccountId,
2848 ) -> DispatchResultWithPostInfo {
2849 let _ = ensure_signed(origin)?;
2850 let ledger = Self::ledger(StakingAccount::Controller(controller.clone()))?;
2851
2852 ensure!(
2853 (Payee::<T>::get(&ledger.stash) == {
2854 #[allow(deprecated)]
2855 Some(RewardDestination::Controller)
2856 }),
2857 Error::<T>::NotController
2858 );
2859
2860 let _ = ledger
2861 .set_payee(RewardDestination::Account(controller))
2862 .defensive_proof("ledger should have been previously retrieved from storage.")?;
2863
2864 Ok(Pays::No.into())
2865 }
2866
2867 #[pallet::call_index(28)]
2875 #[pallet::weight(T::WeightInfo::deprecate_controller_batch(controllers.len() as u32))]
2876 pub fn deprecate_controller_batch(
2877 origin: OriginFor<T>,
2878 controllers: BoundedVec<T::AccountId, T::MaxControllersInDeprecationBatch>,
2879 ) -> DispatchResultWithPostInfo {
2880 T::AdminOrigin::ensure_origin(origin)?;
2881
2882 let filtered_batch_with_ledger: Vec<_> = controllers
2884 .iter()
2885 .filter_map(|controller| {
2886 let ledger = Self::ledger(StakingAccount::Controller(controller.clone()));
2887 ledger.ok().map_or(None, |ledger| {
2888 let payee_deprecated = Payee::<T>::get(&ledger.stash) == {
2891 #[allow(deprecated)]
2892 Some(RewardDestination::Controller)
2893 };
2894
2895 if ledger.stash != *controller && !payee_deprecated {
2896 Some(ledger)
2897 } else {
2898 None
2899 }
2900 })
2901 })
2902 .collect();
2903
2904 let mut failures = 0;
2906 for ledger in filtered_batch_with_ledger {
2907 let _ = ledger.clone().set_controller_to_stash().map_err(|_| failures += 1);
2908 }
2909 Self::deposit_event(Event::<T>::ControllerBatchDeprecated { failures });
2910
2911 Ok(Some(T::WeightInfo::deprecate_controller_batch(controllers.len() as u32)).into())
2912 }
2913
2914 #[pallet::call_index(29)]
2926 #[pallet::weight(T::WeightInfo::restore_ledger())]
2927 pub fn restore_ledger(
2928 origin: OriginFor<T>,
2929 stash: T::AccountId,
2930 maybe_controller: Option<T::AccountId>,
2931 maybe_total: Option<BalanceOf<T>>,
2932 maybe_unlocking: Option<BoundedVec<UnlockChunk<BalanceOf<T>>, T::MaxUnlockingChunks>>,
2933 ) -> DispatchResult {
2934 T::AdminOrigin::ensure_origin(origin)?;
2935
2936 ensure!(!Self::is_virtual_staker(&stash), Error::<T>::VirtualStakerNotAllowed);
2938
2939 let current_lock = asset::staked::<T>(&stash);
2940 let stash_balance = asset::stakeable_balance::<T>(&stash);
2941
2942 let (new_controller, new_total) = match Self::inspect_bond_state(&stash) {
2943 Ok(LedgerIntegrityState::Corrupted) => {
2944 let new_controller = maybe_controller.unwrap_or(stash.clone());
2945
2946 let new_total = if let Some(total) = maybe_total {
2947 let new_total = total.min(stash_balance);
2948 asset::update_stake::<T>(&stash, new_total)?;
2950 new_total
2951 } else {
2952 current_lock
2953 };
2954
2955 Ok((new_controller, new_total))
2956 },
2957 Ok(LedgerIntegrityState::CorruptedKilled) => {
2958 if current_lock == Zero::zero() {
2959 ensure!(maybe_total.is_some(), Error::<T>::CannotRestoreLedger);
2963 Ok((
2964 stash.clone(),
2965 maybe_total.expect("total exists as per the check above; qed."),
2966 ))
2967 } else {
2968 Ok((stash.clone(), current_lock))
2969 }
2970 },
2971 Ok(LedgerIntegrityState::LockCorrupted) => {
2972 let new_total =
2975 maybe_total.ok_or(Error::<T>::CannotRestoreLedger)?.min(stash_balance);
2976 asset::update_stake::<T>(&stash, new_total)?;
2977
2978 Ok((stash.clone(), new_total))
2979 },
2980 Err(Error::<T>::BadState) => {
2981 asset::kill_stake::<T>(&stash)?;
2983 ensure!(
2984 Self::inspect_bond_state(&stash) == Err(Error::<T>::NotStash),
2985 Error::<T>::BadState
2986 );
2987
2988 return Ok(());
2989 },
2990 Ok(LedgerIntegrityState::Ok) | Err(_) => Err(Error::<T>::CannotRestoreLedger),
2991 }?;
2992
2993 Bonded::<T>::insert(&stash, &new_controller);
2995
2996 let mut ledger = StakingLedger::<T>::new(stash.clone(), new_total);
2998 ledger.controller = Some(new_controller);
2999 ledger.unlocking = maybe_unlocking.unwrap_or_default();
3000 ledger.update()?;
3001
3002 ensure!(
3003 Self::inspect_bond_state(&stash) == Ok(LedgerIntegrityState::Ok),
3004 Error::<T>::BadState
3005 );
3006 Ok(())
3007 }
3008
3009 #[pallet::call_index(30)]
3017 #[pallet::weight(T::WeightInfo::migrate_currency())]
3018 pub fn migrate_currency(
3019 origin: OriginFor<T>,
3020 stash: T::AccountId,
3021 ) -> DispatchResultWithPostInfo {
3022 let _ = ensure_signed(origin)?;
3023 Self::do_migrate_currency(&stash)?;
3024
3025 Ok(Pays::No.into())
3027 }
3028
3029 #[pallet::call_index(31)]
3064 #[pallet::weight(T::WeightInfo::apply_slash(T::MaxExposurePageSize::get()))]
3065 pub fn apply_slash(
3066 origin: OriginFor<T>,
3067 slash_era: EraIndex,
3068 slash_key: (T::AccountId, Perbill, u32),
3069 ) -> DispatchResultWithPostInfo {
3070 let _ = ensure_signed(origin)?;
3071 let active_era = ActiveEra::<T>::get().map(|a| a.index).unwrap_or_default();
3072 ensure!(slash_era <= active_era, Error::<T>::EraNotStarted);
3073
3074 ensure!(
3076 !Self::check_slash_cancelled(slash_era, &slash_key.0, slash_key.1),
3077 Error::<T>::CancelledSlash
3078 );
3079
3080 let unapplied_slash = UnappliedSlashes::<T>::take(&slash_era, &slash_key)
3081 .ok_or(Error::<T>::InvalidSlashRecord)?;
3082 slashing::apply_slash::<T>(unapplied_slash, Self::offence_era_of(slash_era));
3083
3084 Ok(Pays::No.into())
3085 }
3086
3087 #[pallet::call_index(32)]
3099 #[pallet::weight({
3101 let v = T::MaxValidatorSet::get();
3102 T::WeightInfo::prune_era_stakers_paged(v)
3103 .max(T::WeightInfo::prune_era_stakers_overview(v))
3104 .max(T::WeightInfo::prune_era_validator_prefs(v))
3105 .max(T::WeightInfo::prune_era_claimed_rewards(v))
3106 .max(T::WeightInfo::prune_era_validator_reward())
3107 .max(T::WeightInfo::prune_era_reward_points())
3108 .max(T::WeightInfo::prune_era_single_entry_cleanups())
3109 .max(T::WeightInfo::prune_era_validator_slash_in_era(v))
3110 .max(T::WeightInfo::prune_era_validator_incentive_weight(v))
3111 })]
3112 pub fn prune_era_step(origin: OriginFor<T>, era: EraIndex) -> DispatchResultWithPostInfo {
3113 let _ = ensure_signed(origin)?;
3114
3115 let active_era = crate::session_rotation::Rotator::<T>::active_era();
3117 let history_depth = T::HistoryDepth::get();
3118 let earliest_prunable_era = active_era.saturating_sub(history_depth).saturating_sub(1);
3119 ensure!(era <= earliest_prunable_era, Error::<T>::EraNotPrunable);
3120
3121 let actual_weight = Self::do_prune_era_step(era)?;
3122
3123 Ok(frame_support::dispatch::PostDispatchInfo {
3124 actual_weight: Some(actual_weight),
3125 pays_fee: frame_support::dispatch::Pays::No,
3126 })
3127 }
3128
3129 #[pallet::call_index(33)]
3133 #[pallet::weight(T::WeightInfo::set_max_commission())]
3134 pub fn set_max_commission(origin: OriginFor<T>, new: Perbill) -> DispatchResult {
3135 T::AdminOrigin::ensure_origin(origin)?;
3136 ensure!(new >= MinCommission::<T>::get(), Error::<T>::CommissionTooLow);
3137 MaxCommission::<T>::put(new);
3138 Ok(())
3139 }
3140
3141 #[pallet::call_index(34)]
3147 #[pallet::weight(T::WeightInfo::set_validator_self_stake_incentive_config())]
3148 pub fn set_validator_self_stake_incentive_config(
3149 origin: OriginFor<T>,
3150 optimum_self_stake: ConfigOp<BalanceOf<T>>,
3151 hard_cap_self_stake: ConfigOp<BalanceOf<T>>,
3152 self_stake_slope_factor: ConfigOp<Perbill>,
3153 ) -> DispatchResult {
3154 T::AdminOrigin::ensure_origin(origin)?;
3155
3156 let new_optimum = match optimum_self_stake {
3157 ConfigOp::Noop => OptimumSelfStake::<T>::get(),
3158 ConfigOp::Set(v) => v,
3159 ConfigOp::Remove => BalanceOf::<T>::zero(),
3160 };
3161
3162 let new_cap = match hard_cap_self_stake {
3163 ConfigOp::Noop => HardCapSelfStake::<T>::get(),
3164 ConfigOp::Set(v) => v,
3165 ConfigOp::Remove => BalanceOf::<T>::zero(),
3166 };
3167
3168 ensure!(new_optimum <= new_cap, Error::<T>::OptimumGreaterThanCap);
3169
3170 let has_changes = !matches!(
3171 (&optimum_self_stake, &hard_cap_self_stake, &self_stake_slope_factor),
3172 (ConfigOp::Noop, ConfigOp::Noop, ConfigOp::Noop)
3173 );
3174
3175 macro_rules! config_op_exp {
3176 ($storage:ty, $op:ident) => {
3177 match $op {
3178 ConfigOp::Noop => (),
3179 ConfigOp::Set(v) => <$storage>::put(v),
3180 ConfigOp::Remove => <$storage>::kill(),
3181 }
3182 };
3183 }
3184
3185 config_op_exp!(OptimumSelfStake<T>, optimum_self_stake);
3186 config_op_exp!(HardCapSelfStake<T>, hard_cap_self_stake);
3187 config_op_exp!(SelfStakeSlopeFactor<T>, self_stake_slope_factor);
3188
3189 if has_changes {
3190 Self::deposit_event(Event::<T>::ValidatorIncentiveConfigSet {
3191 optimum_self_stake: OptimumSelfStake::<T>::get(),
3192 hard_cap_self_stake: HardCapSelfStake::<T>::get(),
3193 slope_factor: SelfStakeSlopeFactor::<T>::get(),
3194 });
3195 }
3196
3197 Ok(())
3198 }
3199
3200 #[pallet::call_index(35)]
3215 #[pallet::weight(T::WeightInfo::chill_inactive(proof.len() as _))]
3216 pub fn chill_inactive(
3217 origin: OriginFor<T>,
3218 stash: T::AccountId,
3219 proof: BoundedVec<EraIndex, T::HistoryDepth>,
3220 ) -> DispatchResultWithPostInfo {
3221 ensure_signed(origin)?;
3222
3223 let threshold = ChillInactiveThreshold::<T>::get();
3224 ensure!(
3225 proof.len() as EraIndex == threshold,
3226 Error::<T>::InvalidInactivityProof(InvalidInactivityProofError::InvalidLen)
3227 );
3228 ensure!(
3229 proof.is_sorted_by(|a, b| a < b),
3230 Error::<T>::InvalidInactivityProof(InvalidInactivityProofError::NotSorted)
3231 );
3232
3233 let active_era = Rotator::<T>::active_era();
3236 let oldest_allowed_era = active_era.saturating_sub(T::HistoryDepth::get());
3237 let oldest_proof_era = proof.first().copied().unwrap_or(EraIndex::MAX);
3238 let most_recent_proof_era = proof.last().copied().unwrap_or(EraIndex::MAX);
3239 ensure!(
3240 oldest_proof_era >= oldest_allowed_era && most_recent_proof_era < active_era,
3241 Error::<T>::InvalidInactivityProof(InvalidInactivityProofError::InvalidEra)
3242 );
3243
3244 for era in proof {
3245 ensure!(
3246 Eras::<T>::was_validator_exposed(era, &stash),
3247 Error::<T>::InvalidInactivityProof(
3248 InvalidInactivityProofError::ValidatorNotExposed
3249 )
3250 );
3251
3252 let points = Eras::<T>::get_reward_points_for_validator(era, &stash);
3253
3254 ensure!(
3255 T::IsValidatorInactive::is_inactive(era, &stash, points),
3256 Error::<T>::InvalidInactivityProof(
3257 InvalidInactivityProofError::ValidatorActive
3258 )
3259 );
3260 }
3261
3262 if Self::do_remove_validator(&stash) {
3263 Self::deposit_event(Event::<T>::Chilled { stash });
3264
3265 Ok(Pays::No.into())
3266 } else {
3267 Err(Error::<T>::BadTarget.into())
3268 }
3269 }
3270 }
3271
3272 #[pallet::view_functions]
3273 impl<T: Config> Pallet<T> {
3274 pub fn pot_account(pot: crate::RewardPot) -> T::AccountId {
3276 <T::RewardPots as crate::PotAccountProvider<T::AccountId>>::pot_account(pot)
3277 }
3278
3279 pub fn pot_balance(pot: crate::RewardPot) -> BalanceOf<T> {
3281 let account =
3282 <T::RewardPots as crate::PotAccountProvider<T::AccountId>>::pot_account(pot);
3283 <T::Currency as frame_support::traits::fungible::Inspect<T::AccountId>>::balance(
3284 &account,
3285 )
3286 }
3287
3288 pub fn era_reward_allocation(
3292 era: EraIndex,
3293 ) -> crate::reward::EraRewardAllocation<BalanceOf<T>> {
3294 crate::reward::EraRewardAllocation {
3295 staker_rewards: ErasValidatorReward::<T>::get(era).unwrap_or_else(Zero::zero),
3296 validator_incentive: ErasValidatorIncentiveBudget::<T>::get(era),
3297 }
3298 }
3299 }
3300}