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, OnEraStart, 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 #[pallet::no_default_bounds]
439 type OnEraStart: OnEraStart<Self::AccountId>;
440 }
441
442 #[pallet::composite_enum]
444 pub enum HoldReason {
445 #[codec(index = 0)]
447 Staking,
448 }
449
450 pub mod config_preludes {
452 use super::*;
453 use frame_support::{derive_impl, parameter_types, traits::ConstU32};
454 pub struct TestDefaultConfig;
455
456 #[derive_impl(frame_system::config_preludes::TestDefaultConfig, no_aggregated_types)]
457 impl frame_system::DefaultConfig for TestDefaultConfig {}
458
459 parameter_types! {
460 pub const SessionsPerEra: SessionIndex = 3;
461 pub const BondingDuration: EraIndex = 3;
462 pub const NominatorFastUnbondDuration: EraIndex = 2;
463 pub const MaxPruningItems: u32 = 100;
464 }
465
466 #[frame_support::register_default_impl(TestDefaultConfig)]
467 impl DefaultConfig for TestDefaultConfig {
468 #[inject_runtime_type]
469 type RuntimeHoldReason = ();
470 type CurrencyBalance = u128;
471 type CurrencyToVote = ();
472 type NominationsQuota = crate::FixedNominationsQuota<16>;
473 type HistoryDepth = ConstU32<84>;
474 type RewardRemainder = ();
475 type Slash = ();
476 type Reward = ();
477 type UnclaimedRewardHandler = ();
478 type StakerRewardCalculator = ();
479 type DisableMinting = ConstBool<false>;
480 type SessionsPerEra = SessionsPerEra;
481 type BondingDuration = BondingDuration;
482 type NominatorFastUnbondDuration = NominatorFastUnbondDuration;
483 type PlanningEraOffset = ConstU32<1>;
484 type SlashDeferDuration = ();
485 type MaxExposurePageSize = ConstU32<64>;
486 type MaxUnlockingChunks = ConstU32<32>;
487 type MaxValidatorSet = ConstU32<100>;
488 type MaxControllersInDeprecationBatch = ConstU32<100>;
489 type MaxEraDuration = ();
490 type MaxPruningItems = MaxPruningItems;
491 type EventListeners = ();
492 type Filter = Nothing;
493 type WeightInfo = ();
494 type IsValidatorInactive = ();
495 type OnEraStart = ();
496 }
497 }
498
499 #[pallet::storage]
501 pub type ValidatorCount<T> = StorageValue<_, u32, ValueQuery>;
502
503 #[pallet::storage]
507 pub type Bonded<T: Config> = StorageMap<_, Twox64Concat, T::AccountId, T::AccountId>;
508
509 #[pallet::storage]
511 pub type MinNominatorBond<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;
512
513 #[pallet::storage]
515 pub type MinValidatorBond<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;
516
517 #[pallet::storage]
519 pub type MinimumActiveStake<T> = StorageValue<_, BalanceOf<T>, ValueQuery>;
520
521 #[pallet::storage]
525 pub type MinCommission<T: Config> = StorageValue<_, Perbill, ValueQuery>;
526
527 #[pallet::storage]
531 pub type MaxCommission<T: Config> = StorageValue<_, Perbill, ValueQuery, MaxCommissionDefault>;
532
533 pub struct MaxCommissionDefault;
535 impl Get<Perbill> for MaxCommissionDefault {
536 fn get() -> Perbill {
537 Perbill::one()
538 }
539 }
540
541 #[pallet::storage]
549 pub type DisableMintingGuard<T: Config> = StorageValue<_, EraIndex>;
550
551 #[pallet::storage]
556 pub type OptimumSelfStake<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;
557
558 #[pallet::storage]
562 pub type HardCapSelfStake<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;
563
564 #[pallet::storage]
568 pub type SelfStakeSlopeFactor<T: Config> = StorageValue<_, Perbill, ValueQuery>;
569
570 #[pallet::storage]
574 pub type ErasValidatorIncentiveBudget<T: Config> =
575 StorageMap<_, Twox64Concat, EraIndex, BalanceOf<T>, ValueQuery>;
576
577 #[pallet::storage]
581 pub type ErasSumValidatorIncentiveWeight<T: Config> =
582 StorageMap<_, Twox64Concat, EraIndex, IncentiveWeight<T>, ValueQuery>;
583
584 #[pallet::storage]
587 pub type ErasValidatorIncentiveWeight<T: Config> = StorageDoubleMap<
588 _,
589 Twox64Concat,
590 EraIndex,
591 Twox64Concat,
592 T::AccountId,
593 IncentiveWeight<T>,
594 OptionQuery,
595 >;
596
597 #[pallet::storage]
604 pub type ErasSumWeightedPoints<T: Config> =
605 StorageMap<_, Twox64Concat, EraIndex, IncentiveWeight<T>, ValueQuery>;
606
607 #[pallet::storage]
626 pub type WeightedPointsFormulaStartEra<T: Config> = StorageValue<_, EraIndex, OptionQuery>;
627
628 #[pallet::storage]
636 pub type AreNominatorsSlashable<T: Config> = StorageValue<_, bool, ValueQuery, ConstBool<true>>;
637
638 #[pallet::storage]
646 pub type ErasNominatorsSlashable<T: Config> =
647 StorageMap<_, Twox64Concat, EraIndex, bool, OptionQuery>;
648
649 #[pallet::storage]
654 pub type Ledger<T: Config> = StorageMap<_, Blake2_128Concat, T::AccountId, StakingLedger<T>>;
655
656 #[pallet::storage]
660 pub type Payee<T: Config> =
661 StorageMap<_, Twox64Concat, T::AccountId, RewardDestination<T::AccountId>, OptionQuery>;
662
663 #[pallet::storage]
667 pub type Validators<T: Config> =
668 CountedStorageMap<_, Twox64Concat, T::AccountId, ValidatorPrefs, ValueQuery>;
669
670 #[pallet::storage]
674 pub type MaxValidatorsCount<T> = StorageValue<_, u32, OptionQuery>;
675
676 #[pallet::storage]
689 pub type LastValidatorEra<T: Config> = StorageMap<_, Twox64Concat, T::AccountId, EraIndex>;
690
691 #[pallet::storage]
711 pub type Nominators<T: Config> =
712 CountedStorageMap<_, Twox64Concat, T::AccountId, Nominations<T>>;
713
714 #[pallet::storage]
721 pub type VirtualStakers<T: Config> = CountedStorageMap<_, Twox64Concat, T::AccountId, ()>;
722
723 #[pallet::storage]
727 pub type MaxNominatorsCount<T> = StorageValue<_, u32, OptionQuery>;
728
729 #[pallet::storage]
736 pub type CurrentEra<T> = StorageValue<_, EraIndex>;
737
738 #[pallet::storage]
743 pub type ActiveEra<T> = StorageValue<_, ActiveEraInfo>;
744
745 pub struct BondedErasBound<T>(core::marker::PhantomData<T>);
747 impl<T: Config> Get<u32> for BondedErasBound<T> {
748 fn get() -> u32 {
749 T::BondingDuration::get().saturating_add(1)
750 }
751 }
752
753 const OFFENCE_QUEUE_ERAS_BOUND: u32 = 10;
754 pub struct OffenceQueueErasBound<T>(core::marker::PhantomData<T>);
757 impl<T: Config> Get<u32> for OffenceQueueErasBound<T> {
758 fn get() -> u32 {
759 let bonding_duration = T::BondingDuration::get();
760 bonding_duration.saturating_add(OFFENCE_QUEUE_ERAS_BOUND) }
766 }
767
768 #[pallet::storage]
773 pub type BondedEras<T: Config> =
774 StorageValue<_, BoundedVec<(EraIndex, SessionIndex), BondedErasBound<T>>, ValueQuery>;
775
776 #[pallet::storage]
791 pub type ErasStakersOverview<T: Config> = StorageDoubleMap<
792 _,
793 Twox64Concat,
794 EraIndex,
795 Twox64Concat,
796 T::AccountId,
797 PagedExposureMetadata<BalanceOf<T>>,
798 OptionQuery,
799 >;
800
801 #[derive(PartialEqNoBound, Encode, Decode, DebugNoBound, TypeInfo, DefaultNoBound)]
810 #[scale_info(skip_type_params(T))]
811 pub struct BoundedExposurePage<T: Config>(pub ExposurePage<T::AccountId, BalanceOf<T>>);
812 impl<T: Config> Deref for BoundedExposurePage<T> {
813 type Target = ExposurePage<T::AccountId, BalanceOf<T>>;
814
815 fn deref(&self) -> &Self::Target {
816 &self.0
817 }
818 }
819
820 impl<T: Config> core::ops::DerefMut for BoundedExposurePage<T> {
821 fn deref_mut(&mut self) -> &mut Self::Target {
822 &mut self.0
823 }
824 }
825
826 impl<T: Config> codec::MaxEncodedLen for BoundedExposurePage<T> {
827 fn max_encoded_len() -> usize {
828 let max_exposure_page_size = T::MaxExposurePageSize::get() as usize;
829 let individual_size =
830 T::AccountId::max_encoded_len() + BalanceOf::<T>::max_encoded_len();
831
832 BalanceOf::<T>::max_encoded_len() +
834 max_exposure_page_size.saturating_mul(individual_size)
836 }
837 }
838
839 impl<T: Config> From<ExposurePage<T::AccountId, BalanceOf<T>>> for BoundedExposurePage<T> {
840 fn from(value: ExposurePage<T::AccountId, BalanceOf<T>>) -> Self {
841 Self(value)
842 }
843 }
844
845 impl<T: Config> From<BoundedExposurePage<T>> for ExposurePage<T::AccountId, BalanceOf<T>> {
846 fn from(value: BoundedExposurePage<T>) -> Self {
847 value.0
848 }
849 }
850
851 impl<T: Config> codec::EncodeLike<BoundedExposurePage<T>>
852 for ExposurePage<T::AccountId, BalanceOf<T>>
853 {
854 }
855
856 #[pallet::storage]
863 pub type ErasStakersPaged<T: Config> = StorageNMap<
864 _,
865 (
866 NMapKey<Twox64Concat, EraIndex>,
867 NMapKey<Twox64Concat, T::AccountId>,
868 NMapKey<Twox64Concat, Page>,
869 ),
870 BoundedExposurePage<T>,
871 OptionQuery,
872 >;
873
874 pub struct ClaimedRewardsBound<T>(core::marker::PhantomData<T>);
875 impl<T: Config> Get<u32> for ClaimedRewardsBound<T> {
876 fn get() -> u32 {
877 let max_total_nominators_per_validator =
878 <T::ElectionProvider as ElectionProvider>::MaxBackersPerWinnerFinal::get();
879 let exposure_page_size = T::MaxExposurePageSize::get();
880 max_total_nominators_per_validator
881 .saturating_div(exposure_page_size)
882 .saturating_add(1)
883 }
884 }
885
886 #[pallet::storage]
893 pub type ClaimedRewards<T: Config> = StorageDoubleMap<
894 _,
895 Twox64Concat,
896 EraIndex,
897 Twox64Concat,
898 T::AccountId,
899 WeakBoundedVec<Page, ClaimedRewardsBound<T>>,
900 ValueQuery,
901 >;
902
903 #[pallet::storage]
910 pub type ErasValidatorPrefs<T: Config> = StorageDoubleMap<
911 _,
912 Twox64Concat,
913 EraIndex,
914 Twox64Concat,
915 T::AccountId,
916 ValidatorPrefs,
917 ValueQuery,
918 >;
919
920 #[pallet::storage]
926 pub type ErasValidatorReward<T: Config> = StorageMap<_, Twox64Concat, EraIndex, BalanceOf<T>>;
927
928 #[pallet::storage]
931 pub type ErasRewardPoints<T: Config> =
932 StorageMap<_, Twox64Concat, EraIndex, EraRewardPoints<T>, ValueQuery>;
933
934 #[pallet::storage]
937 pub type ErasTotalStake<T: Config> =
938 StorageMap<_, Twox64Concat, EraIndex, BalanceOf<T>, ValueQuery>;
939
940 #[pallet::storage]
942 pub type ForceEra<T> = StorageValue<_, Forcing, ValueQuery>;
943
944 #[pallet::storage]
949 pub type MaxStakedRewards<T> = StorageValue<_, Percent, OptionQuery>;
950
951 #[pallet::storage]
955 pub type SlashRewardFraction<T> = StorageValue<_, Perbill, ValueQuery>;
956
957 #[pallet::storage]
960 pub type CanceledSlashPayout<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;
961
962 #[pallet::storage]
973 pub type OffenceQueue<T: Config> = StorageDoubleMap<
974 _,
975 Twox64Concat,
976 EraIndex,
977 Twox64Concat,
978 T::AccountId,
979 slashing::OffenceRecord<T::AccountId>,
980 >;
981
982 #[pallet::storage]
995 pub type OffenceQueueEras<T: Config> =
996 StorageValue<_, WeakBoundedVec<u32, OffenceQueueErasBound<T>>>;
997
998 #[pallet::storage]
1011 pub type ProcessingOffence<T: Config> =
1012 StorageValue<_, (EraIndex, T::AccountId, slashing::OffenceRecord<T::AccountId>)>;
1013
1014 #[pallet::storage]
1016 pub type UnappliedSlashes<T: Config> = StorageDoubleMap<
1017 _,
1018 Twox64Concat,
1019 EraIndex,
1020 Twox64Concat,
1021 (T::AccountId, Perbill, u32),
1023 UnappliedSlash<T>,
1024 OptionQuery,
1025 >;
1026
1027 #[pallet::storage]
1033 pub type CancelledSlashes<T: Config> = StorageMap<
1034 _,
1035 Twox64Concat,
1036 EraIndex,
1037 BoundedVec<(T::AccountId, Perbill), T::MaxValidatorSet>,
1038 ValueQuery,
1039 >;
1040
1041 #[pallet::storage]
1044 pub type ValidatorSlashInEra<T: Config> = StorageDoubleMap<
1045 _,
1046 Twox64Concat,
1047 EraIndex,
1048 Twox64Concat,
1049 T::AccountId,
1050 (Perbill, BalanceOf<T>),
1051 >;
1052
1053 #[pallet::storage]
1057 pub type ChillThreshold<T: Config> = StorageValue<_, Percent, OptionQuery>;
1058
1059 #[pallet::storage]
1064 pub type VoterSnapshotStatus<T: Config> =
1065 StorageValue<_, SnapshotStatus<T::AccountId>, ValueQuery>;
1066
1067 #[pallet::storage]
1074 pub type NextElectionPage<T: Config> = StorageValue<_, PageIndex, OptionQuery>;
1075
1076 #[pallet::storage]
1078 pub type ElectableStashes<T: Config> =
1079 StorageValue<_, BoundedBTreeSet<T::AccountId, T::MaxValidatorSet>, ValueQuery>;
1080
1081 #[pallet::storage]
1088 pub type NextEraValidators<T: Config> =
1089 StorageValue<_, (EraIndex, BoundedVec<T::AccountId, T::MaxValidatorSet>), OptionQuery>;
1090
1091 #[pallet::storage]
1093 pub type EraPruningState<T: Config> = StorageMap<_, Twox64Concat, EraIndex, PruningStep>;
1094
1095 #[pallet::storage]
1100 pub type ChillInactiveThreshold<T: Config> = StorageValue<_, u32, ValueQuery, T::HistoryDepth>;
1101
1102 #[pallet::genesis_config]
1103 #[derive(frame_support::DefaultNoBound, frame_support::DebugNoBound)]
1104 pub struct GenesisConfig<T: Config> {
1105 pub validator_count: u32,
1106 pub force_era: Forcing,
1107 pub slash_reward_fraction: Perbill,
1108 pub canceled_payout: BalanceOf<T>,
1109 pub stakers: Vec<(T::AccountId, BalanceOf<T>, crate::StakerStatus<T::AccountId>)>,
1110 pub min_nominator_bond: BalanceOf<T>,
1111 pub min_validator_bond: BalanceOf<T>,
1112 pub max_validator_count: Option<u32>,
1113 pub max_nominator_count: Option<u32>,
1114 pub dev_stakers: Option<(u32, u32)>,
1121 pub active_era: (u32, u32, u64),
1123 }
1124
1125 impl<T: Config> GenesisConfig<T> {
1126 fn generate_endowed_bonded_account(derivation: &str, rng: &mut ChaChaRng) -> T::AccountId {
1127 let pair: SrPair = Pair::from_string(&derivation, None)
1128 .expect(&format!("Failed to parse derivation string: {derivation}"));
1129 let who = T::AccountId::decode(&mut &pair.public().encode()[..])
1130 .expect(&format!("Failed to decode public key from pair: {:?}", pair.public()));
1131
1132 let (min, max) = T::VoterList::range();
1133 let stake = BalanceOf::<T>::from(rng.next_u64().min(max).max(min));
1134 let two: BalanceOf<T> = 2u32.into();
1135
1136 assert_ok!(T::Currency::mint_into(&who, stake * two));
1137 assert_ok!(<Pallet<T>>::bond(
1138 T::RuntimeOrigin::from(Some(who.clone()).into()),
1139 stake,
1140 RewardDestination::Staked,
1141 ));
1142 who
1143 }
1144 }
1145
1146 #[pallet::genesis_build]
1147 impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
1148 fn build(&self) {
1149 crate::log!(trace, "initializing with {:?}", self);
1150 assert!(
1151 self.validator_count <=
1152 <T::ElectionProvider as ElectionProvider>::MaxWinnersPerPage::get() *
1153 <T::ElectionProvider as ElectionProvider>::Pages::get(),
1154 "validator count is too high, `ElectionProvider` can never fulfill this"
1155 );
1156 ValidatorCount::<T>::put(self.validator_count);
1157
1158 ForceEra::<T>::put(self.force_era);
1159 CanceledSlashPayout::<T>::put(self.canceled_payout);
1160 SlashRewardFraction::<T>::put(self.slash_reward_fraction);
1161 MinNominatorBond::<T>::put(self.min_nominator_bond);
1162 MinValidatorBond::<T>::put(self.min_validator_bond);
1163 if let Some(x) = self.max_validator_count {
1164 MaxValidatorsCount::<T>::put(x);
1165 }
1166 if let Some(x) = self.max_nominator_count {
1167 MaxNominatorsCount::<T>::put(x);
1168 }
1169
1170 for &(ref stash, balance, ref status) in &self.stakers {
1172 match status {
1173 crate::StakerStatus::Validator => {
1174 crate::log!(
1175 trace,
1176 "inserting genesis validator: {:?} => {:?} => {:?}",
1177 stash,
1178 balance,
1179 status
1180 );
1181 assert!(
1182 asset::free_to_stake::<T>(stash) >= balance,
1183 "Stash does not have enough balance to bond."
1184 );
1185 assert_ok!(<Pallet<T>>::bond(
1186 T::RuntimeOrigin::from(Some(stash.clone()).into()),
1187 balance,
1188 RewardDestination::Staked,
1189 ));
1190 assert_ok!(<Pallet<T>>::validate(
1191 T::RuntimeOrigin::from(Some(stash.clone()).into()),
1192 Default::default(),
1193 ));
1194 },
1195 crate::StakerStatus::Idle => {
1196 crate::log!(
1197 trace,
1198 "inserting genesis idle staker: {:?} => {:?} => {:?}",
1199 stash,
1200 balance,
1201 status
1202 );
1203 assert!(
1204 asset::free_to_stake::<T>(stash) >= balance,
1205 "Stash does not have enough balance to bond."
1206 );
1207 assert_ok!(<Pallet<T>>::bond(
1208 T::RuntimeOrigin::from(Some(stash.clone()).into()),
1209 balance,
1210 RewardDestination::Staked,
1211 ));
1212 },
1213 _ => {},
1214 }
1215 }
1216
1217 for &(ref stash, balance, ref status) in &self.stakers {
1219 match status {
1220 crate::StakerStatus::Nominator(votes) => {
1221 crate::log!(
1222 trace,
1223 "inserting genesis nominator: {:?} => {:?} => {:?}",
1224 stash,
1225 balance,
1226 status
1227 );
1228 assert!(
1229 asset::free_to_stake::<T>(stash) >= balance,
1230 "Stash does not have enough balance to bond."
1231 );
1232 assert_ok!(<Pallet<T>>::bond(
1233 T::RuntimeOrigin::from(Some(stash.clone()).into()),
1234 balance,
1235 RewardDestination::Staked,
1236 ));
1237 assert_ok!(<Pallet<T>>::nominate(
1238 T::RuntimeOrigin::from(Some(stash.clone()).into()),
1239 votes.iter().map(|l| T::Lookup::unlookup(l.clone())).collect(),
1240 ));
1241 },
1242 _ => {},
1243 }
1244 }
1245
1246 assert_eq!(
1248 T::VoterList::count(),
1249 Nominators::<T>::count() + Validators::<T>::count(),
1250 "not all genesis stakers were inserted into sorted list provider, something is wrong."
1251 );
1252
1253 if let Some((validators, nominators)) = self.dev_stakers {
1255 crate::log!(
1256 debug,
1257 "generating dev stakers: validators: {}, nominators: {}",
1258 validators,
1259 nominators
1260 );
1261 let base_derivation = "//staker//{}";
1262
1263 let mut rng = ChaChaRng::from_seed(
1266 base_derivation.using_encoded(sp_crypto_hashing::blake2_256),
1267 );
1268
1269 (0..validators).for_each(|index| {
1270 let derivation = base_derivation.replace("{}", &format!("validator{}", index));
1271 let who = Self::generate_endowed_bonded_account(&derivation, &mut rng);
1272 assert_ok!(<Pallet<T>>::validate(
1273 T::RuntimeOrigin::from(Some(who.clone()).into()),
1274 Default::default(),
1275 ));
1276 });
1277
1278 let all_validators = Validators::<T>::iter_keys().collect::<Vec<_>>();
1281
1282 (0..nominators).for_each(|index| {
1283 let derivation = base_derivation.replace("{}", &format!("nominator{}", index));
1284 let who = Self::generate_endowed_bonded_account(&derivation, &mut rng);
1285
1286 let random_nominations = all_validators
1287 .choose_multiple(&mut rng, MaxNominationsOf::<T>::get() as usize)
1288 .map(|v| v.clone())
1289 .collect::<Vec<_>>();
1290
1291 assert_ok!(<Pallet<T>>::nominate(
1292 T::RuntimeOrigin::from(Some(who.clone()).into()),
1293 random_nominations.iter().map(|l| T::Lookup::unlookup(l.clone())).collect(),
1294 ));
1295 })
1296 }
1297
1298 WeightedPointsFormulaStartEra::<T>::put(0);
1302
1303 let (active_era, session_index, timestamp) = self.active_era;
1304 ActiveEra::<T>::put(ActiveEraInfo { index: active_era, start: Some(timestamp) });
1305 CurrentEra::<T>::put(active_era);
1307 BondedEras::<T>::put(
1309 BoundedVec::<_, BondedErasBound<T>>::try_from(
1310 alloc::vec![(active_era, session_index)]
1311 )
1312 .expect("bound for BondedEras is BondingDuration + 1; can contain at least one element; qed")
1313 );
1314 }
1315 }
1316
1317 #[pallet::event]
1318 #[pallet::generate_deposit(pub fn deposit_event)]
1319 pub enum Event<T: Config> {
1320 EraPaid {
1326 era_index: EraIndex,
1327 validator_payout: BalanceOf<T>,
1328 remainder: BalanceOf<T>,
1329 },
1330 Rewarded {
1332 stash: T::AccountId,
1333 dest: RewardDestination<T::AccountId>,
1334 amount: BalanceOf<T>,
1335 },
1336 Slashed {
1338 staker: T::AccountId,
1339 amount: BalanceOf<T>,
1340 },
1341 OldSlashingReportDiscarded {
1344 session_index: SessionIndex,
1345 },
1346 Bonded {
1351 stash: T::AccountId,
1352 amount: BalanceOf<T>,
1353 },
1354 Unbonded {
1356 stash: T::AccountId,
1357 amount: BalanceOf<T>,
1358 era: EraIndex,
1360 },
1361 Withdrawn {
1364 stash: T::AccountId,
1365 amount: BalanceOf<T>,
1366 },
1367 StakerRemoved {
1370 stash: T::AccountId,
1371 },
1372 Kicked {
1374 nominator: T::AccountId,
1375 stash: T::AccountId,
1376 },
1377 Chilled {
1379 stash: T::AccountId,
1380 },
1381 PayoutStarted {
1383 era_index: EraIndex,
1384 validator_stash: T::AccountId,
1385 page: Page,
1386 next: Option<Page>,
1387 },
1388 ValidatorPrefsSet {
1390 stash: T::AccountId,
1391 prefs: ValidatorPrefs,
1392 },
1393 SnapshotVotersSizeExceeded {
1395 size: u32,
1396 },
1397 SnapshotTargetsSizeExceeded {
1399 size: u32,
1400 },
1401 ForceEra {
1402 mode: Forcing,
1403 },
1404 ControllerBatchDeprecated {
1406 failures: u32,
1407 },
1408 CurrencyMigrated {
1411 stash: T::AccountId,
1412 force_withdraw: BalanceOf<T>,
1413 },
1414 PagedElectionProceeded {
1424 page: PageIndex,
1425 result: Result<u32, u32>,
1426 },
1427 OffenceReported {
1430 offence_era: EraIndex,
1431 validator: T::AccountId,
1432 fraction: Perbill,
1433 },
1434 SlashComputed {
1436 offence_era: EraIndex,
1437 slash_era: EraIndex,
1438 offender: T::AccountId,
1439 page: u32,
1440 },
1441 SlashCancelled {
1443 slash_era: EraIndex,
1444 validator: T::AccountId,
1445 },
1446 SessionRotated {
1451 starting_session: SessionIndex,
1452 active_era: EraIndex,
1453 planned_era: EraIndex,
1454 },
1455 Unexpected(UnexpectedKind<T>),
1458 OffenceTooOld {
1460 offence_era: EraIndex,
1461 validator: T::AccountId,
1462 fraction: Perbill,
1463 },
1464 EraPruned {
1466 index: EraIndex,
1467 },
1468 ValidatorIncentivePaid {
1470 era: EraIndex,
1471 validator_stash: T::AccountId,
1472 dest: RewardDestination<T::AccountId>,
1473 amount: BalanceOf<T>,
1474 },
1475 ValidatorIncentiveConfigSet {
1477 optimum_self_stake: BalanceOf<T>,
1478 hard_cap_self_stake: BalanceOf<T>,
1479 slope_factor: Perbill,
1480 },
1481 }
1482
1483 #[derive(Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, TypeInfo, DebugNoBound)]
1489 #[codec(mel_bound())]
1490 #[scale_info(skip_type_params(T))]
1491 pub enum UnexpectedKind<T: Config> {
1492 EraDurationBoundExceeded,
1494 UnknownValidatorActivation,
1496 PagedElectionOutOfWeight { page: PageIndex, required: Weight, had: Weight },
1498 MissingPayee { era: EraIndex, stash: T::AccountId },
1500 ValidatorIncentiveWeightMismatch { era: EraIndex },
1502 ValidatorIncentiveTransferFailed { era: EraIndex },
1504 }
1505
1506 #[pallet::error]
1507 #[derive(PartialEq)]
1508 pub enum Error<T> {
1509 NotController,
1511 NotStash,
1513 AlreadyBonded,
1515 AlreadyPaired,
1517 EmptyTargets,
1519 DuplicateIndex,
1521 InvalidSlashRecord,
1523 InsufficientBond,
1527 NoMoreChunks,
1529 NoUnlockChunk,
1531 FundedTarget,
1533 InvalidEraToReward,
1535 InvalidNumberOfNominations,
1537 AlreadyClaimed,
1539 InvalidPage,
1541 IncorrectHistoryDepth,
1543 BadState,
1545 TooManyTargets,
1547 BadTarget,
1549 CannotChillOther,
1551 TooManyNominators,
1554 TooManyValidators,
1557 CommissionTooLow,
1559 BoundNotMet,
1561 ControllerDeprecated,
1563 CannotRestoreLedger,
1565 RewardDestinationRestricted,
1567 NotEnoughFunds,
1569 VirtualStakerNotAllowed,
1571 CannotReapStash,
1573 AlreadyMigrated,
1575 EraNotStarted,
1577 Restricted,
1580 UnappliedSlashesInPreviousEra,
1583 EraNotPrunable,
1585 CancelledSlash,
1587 CommissionTooHigh,
1589 OptimumGreaterThanCap,
1591 InvalidInactivityProof(InvalidInactivityProofError),
1593 InvalidChillInactiveThreshold,
1595 }
1596
1597 #[derive(Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, TypeInfo, PalletError)]
1598 pub enum InvalidInactivityProofError {
1599 InvalidLen,
1601 NotSorted,
1603 ValidatorNotExposed,
1605 ValidatorActive,
1607 InvalidEra,
1609 }
1610
1611 impl<T: Config> Pallet<T> {
1612 pub fn apply_unapplied_slashes(active_era: EraIndex) -> Weight {
1614 let mut slashes = UnappliedSlashes::<T>::iter_prefix(&active_era).take(1);
1615 if let Some((key, slash)) = slashes.next() {
1616 crate::log!(
1617 debug,
1618 "🦹 found slash {:?} scheduled to be executed in era {:?}",
1619 slash,
1620 active_era,
1621 );
1622
1623 let nominators_slashed = slash.others.len() as u32;
1624
1625 if Self::check_slash_cancelled(active_era, &key.0, key.1) {
1627 crate::log!(
1628 debug,
1629 "🦹 slash for {:?} in era {:?} was cancelled, skipping",
1630 key.0,
1631 active_era,
1632 );
1633 } else {
1634 slashing::apply_slash::<T>(slash, Self::offence_era_of(active_era));
1635 }
1636
1637 UnappliedSlashes::<T>::remove(&active_era, &key);
1639
1640 if UnappliedSlashes::<T>::iter_prefix(&active_era).next().is_none() {
1642 CancelledSlashes::<T>::remove(&active_era);
1644 }
1645
1646 T::WeightInfo::apply_slash(nominators_slashed)
1647 } else {
1648 T::DbWeight::get().reads(1)
1650 }
1651 }
1652
1653 fn do_prune_era_step(era: EraIndex) -> Result<Weight, DispatchError> {
1655 let current_step = EraPruningState::<T>::get(era).ok_or(Error::<T>::EraNotPrunable)?;
1660
1661 let items_limit = T::MaxPruningItems::get().min(T::MaxValidatorSet::get());
1664
1665 let actual_weight = match current_step {
1666 PruningStep::ErasStakersPaged => {
1667 let result = ErasStakersPaged::<T>::clear_prefix((era,), items_limit, None);
1668 let items_deleted = result.backend as u32;
1669 result.maybe_cursor.is_none().then(|| {
1670 EraPruningState::<T>::insert(era, PruningStep::ErasStakersOverview)
1671 });
1672 T::WeightInfo::prune_era_stakers_paged(items_deleted)
1673 },
1674 PruningStep::ErasStakersOverview => {
1675 let result = ErasStakersOverview::<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::ErasValidatorPrefs)
1679 });
1680 T::WeightInfo::prune_era_stakers_overview(items_deleted)
1681 },
1682 PruningStep::ErasValidatorPrefs => {
1683 let result = ErasValidatorPrefs::<T>::clear_prefix(era, items_limit, None);
1684 let items_deleted = result.backend as u32;
1685 result
1686 .maybe_cursor
1687 .is_none()
1688 .then(|| EraPruningState::<T>::insert(era, PruningStep::ClaimedRewards));
1689 T::WeightInfo::prune_era_validator_prefs(items_deleted)
1690 },
1691 PruningStep::ClaimedRewards => {
1692 let result = ClaimedRewards::<T>::clear_prefix(era, items_limit, None);
1693 let items_deleted = result.backend as u32;
1694 result.maybe_cursor.is_none().then(|| {
1695 EraPruningState::<T>::insert(era, PruningStep::ErasValidatorReward)
1696 });
1697 T::WeightInfo::prune_era_claimed_rewards(items_deleted)
1698 },
1699 PruningStep::ErasValidatorReward => {
1700 ErasValidatorReward::<T>::remove(era);
1701 EraPruningState::<T>::insert(era, PruningStep::ErasRewardPoints);
1702 T::WeightInfo::prune_era_validator_reward()
1703 },
1704 PruningStep::ErasRewardPoints => {
1705 ErasRewardPoints::<T>::remove(era);
1706 EraPruningState::<T>::insert(era, PruningStep::SingleEntryCleanups);
1707 T::WeightInfo::prune_era_reward_points()
1708 },
1709 PruningStep::SingleEntryCleanups => {
1710 ErasTotalStake::<T>::remove(era);
1711 ErasNominatorsSlashable::<T>::remove(era);
1712 ErasValidatorIncentiveBudget::<T>::remove(era);
1713 ErasSumValidatorIncentiveWeight::<T>::remove(era);
1714 ErasSumWeightedPoints::<T>::remove(era);
1715 EraPruningState::<T>::insert(era, PruningStep::ValidatorSlashInEra);
1716 T::WeightInfo::prune_era_single_entry_cleanups()
1717 },
1718 PruningStep::ValidatorSlashInEra => {
1719 let result = ValidatorSlashInEra::<T>::clear_prefix(era, items_limit, None);
1720 let items_deleted = result.backend as u32;
1721
1722 if result.maybe_cursor.is_none() {
1723 EraPruningState::<T>::insert(
1724 era,
1725 PruningStep::ErasValidatorIncentiveWeight,
1726 );
1727 }
1728
1729 T::WeightInfo::prune_era_validator_slash_in_era(items_deleted)
1730 },
1731 PruningStep::ErasValidatorIncentiveWeight => {
1732 let result =
1733 ErasValidatorIncentiveWeight::<T>::clear_prefix(era, items_limit, None);
1734 if result.maybe_cursor.is_none() {
1735 EraPruningState::<T>::remove(era);
1737 }
1738 T::WeightInfo::prune_era_validator_incentive_weight(result.backend as u32)
1739 },
1740 };
1741
1742 if EraPruningState::<T>::get(era).is_none() {
1744 Self::deposit_event(Event::<T>::EraPruned { index: era });
1745 }
1746
1747 Ok(actual_weight)
1748 }
1749 }
1750
1751 #[pallet::hooks]
1752 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
1753 fn on_poll(_now: BlockNumberFor<T>, weight_meter: &mut WeightMeter) {
1754 let (weight, exec) = EraElectionPlanner::<T>::maybe_fetch_election_results();
1755 crate::log!(
1756 trace,
1757 "weight of fetching next election page is {:?}, have {:?}",
1758 weight,
1759 weight_meter.remaining()
1760 );
1761
1762 if weight_meter.can_consume(weight) {
1763 exec(weight_meter);
1764 } else {
1765 Self::deposit_event(Event::<T>::Unexpected(
1766 UnexpectedKind::PagedElectionOutOfWeight {
1767 page: NextElectionPage::<T>::get().unwrap_or(
1768 EraElectionPlanner::<T>::election_pages().defensive_saturating_sub(1),
1769 ),
1770 required: weight,
1771 had: weight_meter.remaining(),
1772 },
1773 ));
1774 }
1775 }
1776
1777 fn on_initialize(_now: BlockNumberFor<T>) -> Weight {
1778 let mut consumed_weight = slashing::process_offence_for_era::<T>();
1780
1781 consumed_weight.saturating_accrue(T::DbWeight::get().reads(1));
1783 if let Some(active_era) = ActiveEra::<T>::get() {
1784 let slash_weight = Self::apply_unapplied_slashes(active_era.index);
1785 consumed_weight.saturating_accrue(slash_weight);
1786 }
1787
1788 consumed_weight
1789 }
1790
1791 fn integrity_test() {
1792 assert_eq!(
1794 MaxNominationsOf::<T>::get(),
1795 <Self as ElectionDataProvider>::MaxVotesPerVoter::get()
1796 );
1797
1798 assert!(!MaxNominationsOf::<T>::get().is_zero());
1800
1801 assert!(
1802 T::SlashDeferDuration::get() < T::BondingDuration::get() || T::BondingDuration::get() == 0,
1803 "As per documentation, slash defer duration ({}) should be less than bonding duration ({}).",
1804 T::SlashDeferDuration::get(),
1805 T::BondingDuration::get(),
1806 );
1807
1808 assert!(
1810 T::NominatorFastUnbondDuration::get() <= T::BondingDuration::get(),
1811 "NominatorFastUnbondDuration ({}) must not exceed BondingDuration ({}).",
1812 T::NominatorFastUnbondDuration::get(),
1813 T::BondingDuration::get(),
1814 );
1815 assert!(
1817 T::MaxPruningItems::get() >= 100,
1818 "MaxPruningItems must be at least 100 for efficient pruning, got: {}",
1819 T::MaxPruningItems::get()
1820 );
1821
1822 assert!(
1823 crate::POT_POOL_SIZE > T::HistoryDepth::get(),
1824 "POT_POOL_SIZE ({}) must be strictly greater than HistoryDepth ({}) \
1825 to avoid reusing a pot slot whose era is still in the active history.",
1826 crate::POT_POOL_SIZE,
1827 T::HistoryDepth::get(),
1828 );
1829
1830 if T::DisableMinting::get() {
1832 let (v, r) = T::EraPayout::era_payout(
1833 BalanceOf::<T>::from(1u64),
1834 BalanceOf::<T>::from(1u64),
1835 1000u64,
1836 );
1837 assert!(
1838 v.is_zero() && r.is_zero(),
1839 "DisableMinting is true but EraPayout returns non-zero. \
1840 Set EraPayout = () when DisableMinting = true."
1841 );
1842 }
1843 }
1844
1845 #[cfg(feature = "try-runtime")]
1846 fn try_state(n: BlockNumberFor<T>) -> Result<(), sp_runtime::TryRuntimeError> {
1847 Self::do_try_state(n)
1848 }
1849 }
1850
1851 #[pallet::call]
1852 impl<T: Config> Pallet<T> {
1853 #[pallet::call_index(0)]
1866 #[pallet::weight(T::WeightInfo::bond())]
1867 pub fn bond(
1868 origin: OriginFor<T>,
1869 #[pallet::compact] value: BalanceOf<T>,
1870 payee: RewardDestination<T::AccountId>,
1871 ) -> DispatchResult {
1872 let stash = ensure_signed(origin)?;
1873
1874 ensure!(!T::Filter::contains(&stash), Error::<T>::Restricted);
1875
1876 if StakingLedger::<T>::is_bonded(StakingAccount::Stash(stash.clone())) {
1877 return Err(Error::<T>::AlreadyBonded.into());
1878 }
1879
1880 if StakingLedger::<T>::is_bonded(StakingAccount::Controller(stash.clone())) {
1882 return Err(Error::<T>::AlreadyPaired.into());
1883 }
1884
1885 if value < Self::min_chilled_bond() {
1887 return Err(Error::<T>::InsufficientBond.into());
1888 }
1889
1890 let stash_balance = asset::free_to_stake::<T>(&stash);
1891 let value = value.min(stash_balance);
1892 Self::deposit_event(Event::<T>::Bonded { stash: stash.clone(), amount: value });
1893 let ledger = StakingLedger::<T>::new(stash.clone(), value);
1894
1895 ledger.bond(payee)?;
1898
1899 Ok(())
1900 }
1901
1902 #[pallet::call_index(1)]
1913 #[pallet::weight(T::WeightInfo::bond_extra())]
1914 pub fn bond_extra(
1915 origin: OriginFor<T>,
1916 #[pallet::compact] max_additional: BalanceOf<T>,
1917 ) -> DispatchResult {
1918 let stash = ensure_signed(origin)?;
1919 ensure!(!T::Filter::contains(&stash), Error::<T>::Restricted);
1920 Self::do_bond_extra(&stash, max_additional)
1921 }
1922
1923 #[pallet::call_index(2)]
1943 #[pallet::weight(
1944 T::WeightInfo::withdraw_unbonded_kill().saturating_add(T::WeightInfo::unbond()))
1945 ]
1946 pub fn unbond(
1947 origin: OriginFor<T>,
1948 #[pallet::compact] value: BalanceOf<T>,
1949 ) -> DispatchResultWithPostInfo {
1950 let controller = ensure_signed(origin)?;
1951 let unlocking =
1952 Self::ledger(Controller(controller.clone())).map(|l| l.unlocking.len())?;
1953
1954 let maybe_withdraw_weight = {
1957 if unlocking == T::MaxUnlockingChunks::get() as usize {
1958 Some(Self::do_withdraw_unbonded(&controller)?)
1959 } else {
1960 None
1961 }
1962 };
1963
1964 let mut ledger = Self::ledger(Controller(controller))?;
1967 let mut value = value.min(ledger.active);
1968 let stash = ledger.stash.clone();
1969
1970 let chill_weight = if value >= ledger.active {
1973 Self::chill_stash(&stash);
1974 T::WeightInfo::chill()
1975 } else {
1976 Weight::zero()
1977 };
1978
1979 ensure!(
1980 ledger.unlocking.len() < T::MaxUnlockingChunks::get() as usize,
1981 Error::<T>::NoMoreChunks,
1982 );
1983
1984 if !value.is_zero() {
1985 ledger.active -= value;
1986
1987 if ledger.active < asset::existential_deposit::<T>() {
1989 value += ledger.active;
1990 ledger.active = Zero::zero();
1991 }
1992
1993 let is_nominator = Nominators::<T>::contains_key(&stash);
1994
1995 let min_active_bond = if is_nominator {
1996 Self::min_nominator_bond()
1997 } else if Validators::<T>::contains_key(&stash) {
1998 Self::min_validator_bond()
1999 } else {
2000 Zero::zero()
2002 };
2003
2004 ensure!(ledger.active >= min_active_bond, Error::<T>::InsufficientBond);
2007
2008 let active_era = session_rotation::Rotator::<T>::active_era();
2014 let was_recent_validator = LastValidatorEra::<T>::get(&stash)
2015 .map(|last_era| active_era.saturating_sub(last_era) < T::BondingDuration::get())
2016 .unwrap_or(false);
2017
2018 let unbond_duration = if was_recent_validator {
2019 T::BondingDuration::get()
2021 } else {
2022 <Self as sp_staking::StakingInterface>::nominator_bonding_duration()
2024 };
2025
2026 let era =
2027 session_rotation::Rotator::<T>::active_era().saturating_add(unbond_duration);
2028 if let Some(chunk) = ledger.unlocking.last_mut().filter(|chunk| chunk.era == era) {
2029 chunk.value = chunk.value.defensive_saturating_add(value)
2033 } else {
2034 ledger
2035 .unlocking
2036 .try_push(UnlockChunk { value, era })
2037 .map_err(|_| Error::<T>::NoMoreChunks)?;
2038 };
2039 ledger.update()?;
2041
2042 if T::VoterList::contains(&stash) {
2044 let _ = T::VoterList::on_update(&stash, Self::weight_of(&stash));
2045 }
2046
2047 Self::deposit_event(Event::<T>::Unbonded { stash, amount: value, era });
2048 }
2049
2050 let actual_weight = if let Some(withdraw_weight) = maybe_withdraw_weight {
2051 Some(
2052 T::WeightInfo::unbond()
2053 .saturating_add(withdraw_weight)
2054 .saturating_add(chill_weight),
2055 )
2056 } else {
2057 Some(T::WeightInfo::unbond().saturating_add(chill_weight))
2058 };
2059
2060 Ok(actual_weight.into())
2061 }
2062
2063 #[pallet::call_index(3)]
2083 #[pallet::weight(T::WeightInfo::withdraw_unbonded_kill())]
2084 pub fn withdraw_unbonded(
2085 origin: OriginFor<T>,
2086 _num_slashing_spans: u32,
2087 ) -> DispatchResultWithPostInfo {
2088 let controller = ensure_signed(origin)?;
2089
2090 let actual_weight = Self::do_withdraw_unbonded(&controller)?;
2091 Ok(Some(actual_weight).into())
2092 }
2093
2094 #[pallet::call_index(4)]
2100 #[pallet::weight(T::WeightInfo::validate())]
2101 pub fn validate(origin: OriginFor<T>, prefs: ValidatorPrefs) -> DispatchResult {
2102 let controller = ensure_signed(origin)?;
2103
2104 let ledger = Self::ledger(Controller(controller))?;
2105
2106 ensure!(ledger.active >= Self::min_validator_bond(), Error::<T>::InsufficientBond);
2107 let stash = &ledger.stash;
2108
2109 ensure!(prefs.commission >= MinCommission::<T>::get(), Error::<T>::CommissionTooLow);
2111 ensure!(prefs.commission <= MaxCommission::<T>::get(), Error::<T>::CommissionTooHigh);
2112
2113 if !Validators::<T>::contains_key(stash) {
2115 if let Some(max_validators) = MaxValidatorsCount::<T>::get() {
2119 ensure!(
2120 Validators::<T>::count() < max_validators,
2121 Error::<T>::TooManyValidators
2122 );
2123 }
2124 }
2125
2126 Self::do_remove_nominator(stash);
2127 Self::do_add_validator(stash, prefs.clone());
2128 Self::deposit_event(Event::<T>::ValidatorPrefsSet { stash: ledger.stash, prefs });
2129
2130 Ok(())
2131 }
2132
2133 #[pallet::call_index(5)]
2139 #[pallet::weight(T::WeightInfo::nominate(targets.len() as u32))]
2140 pub fn nominate(
2141 origin: OriginFor<T>,
2142 targets: Vec<AccountIdLookupOf<T>>,
2143 ) -> DispatchResult {
2144 let controller = ensure_signed(origin)?;
2145
2146 let ledger = Self::ledger(StakingAccount::Controller(controller.clone()))?;
2147
2148 ensure!(ledger.active >= Self::min_nominator_bond(), Error::<T>::InsufficientBond);
2149 let stash = &ledger.stash;
2150
2151 if !Nominators::<T>::contains_key(stash) {
2153 if let Some(max_nominators) = MaxNominatorsCount::<T>::get() {
2157 ensure!(
2158 Nominators::<T>::count() < max_nominators,
2159 Error::<T>::TooManyNominators
2160 );
2161 }
2162 }
2163
2164 let mut targets = targets
2166 .into_iter()
2167 .map(|t| T::Lookup::lookup(t).map_err(DispatchError::from))
2168 .collect::<Result<Vec<_>, _>>()?;
2169 targets.sort();
2170 targets.dedup();
2171
2172 ensure!(!targets.is_empty(), Error::<T>::EmptyTargets);
2173 ensure!(
2174 targets.len() <= T::NominationsQuota::get_quota(ledger.active) as usize,
2175 Error::<T>::TooManyTargets
2176 );
2177
2178 let old = Nominators::<T>::get(stash).map_or_else(Vec::new, |x| x.targets.into_inner());
2179
2180 let targets: BoundedVec<_, _> = targets
2181 .into_iter()
2182 .map(|n| {
2183 if old.contains(&n) ||
2184 (Validators::<T>::contains_key(&n) && !Validators::<T>::get(&n).blocked)
2185 {
2186 Ok(n)
2187 } else {
2188 Err(Error::<T>::BadTarget.into())
2189 }
2190 })
2191 .collect::<Result<Vec<_>, DispatchError>>()?
2192 .try_into()
2193 .map_err(|_| Error::<T>::TooManyNominators)?;
2194
2195 let nominations = Nominations {
2196 targets,
2197 submitted_in: CurrentEra::<T>::get().unwrap_or(0),
2199 suppressed: false,
2200 };
2201
2202 Self::do_remove_validator(stash);
2203 Self::do_add_nominator(stash, nominations);
2204 Ok(())
2205 }
2206
2207 #[pallet::call_index(6)]
2218 #[pallet::weight(T::WeightInfo::chill())]
2219 pub fn chill(origin: OriginFor<T>) -> DispatchResult {
2220 let controller = ensure_signed(origin)?;
2221
2222 let ledger = Self::ledger(StakingAccount::Controller(controller))?;
2223
2224 Self::chill_stash(&ledger.stash);
2225 Ok(())
2226 }
2227
2228 #[pallet::call_index(7)]
2234 #[pallet::weight(T::WeightInfo::set_payee())]
2235 pub fn set_payee(
2236 origin: OriginFor<T>,
2237 payee: RewardDestination<T::AccountId>,
2238 ) -> DispatchResult {
2239 let controller = ensure_signed(origin)?;
2240 let ledger = Self::ledger(Controller(controller.clone()))?;
2241
2242 ensure!(
2243 (payee != {
2244 #[allow(deprecated)]
2245 RewardDestination::Controller
2246 }),
2247 Error::<T>::ControllerDeprecated
2248 );
2249
2250 let _ = ledger
2251 .set_payee(payee)
2252 .defensive_proof("ledger was retrieved from storage, thus it's bonded; qed.")?;
2253
2254 Ok(())
2255 }
2256
2257 #[pallet::call_index(8)]
2266 #[pallet::weight(T::WeightInfo::set_controller())]
2267 pub fn set_controller(origin: OriginFor<T>) -> DispatchResult {
2268 let stash = ensure_signed(origin)?;
2269
2270 Self::ledger(StakingAccount::Stash(stash.clone())).map(|ledger| {
2271 let controller = ledger.controller()
2272 .defensive_proof("Ledger's controller field didn't exist. The controller should have been fetched using StakingLedger.")
2273 .ok_or(Error::<T>::NotController)?;
2274
2275 if controller == stash {
2276 return Err(Error::<T>::AlreadyPaired.into())
2278 }
2279
2280 let _ = ledger.set_controller_to_stash()?;
2281 Ok(())
2282 })?
2283 }
2284
2285 #[pallet::call_index(9)]
2289 #[pallet::weight(T::WeightInfo::set_validator_count())]
2290 pub fn set_validator_count(
2291 origin: OriginFor<T>,
2292 #[pallet::compact] new: u32,
2293 ) -> DispatchResult {
2294 ensure_root(origin)?;
2295
2296 ensure!(new <= T::MaxValidatorSet::get(), Error::<T>::TooManyValidators);
2297
2298 ValidatorCount::<T>::put(new);
2299 Ok(())
2300 }
2301
2302 #[pallet::call_index(10)]
2307 #[pallet::weight(T::WeightInfo::set_validator_count())]
2308 pub fn increase_validator_count(
2309 origin: OriginFor<T>,
2310 #[pallet::compact] additional: u32,
2311 ) -> DispatchResult {
2312 ensure_root(origin)?;
2313 let old = ValidatorCount::<T>::get();
2314 let new = old.checked_add(additional).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(11)]
2327 #[pallet::weight(T::WeightInfo::set_validator_count())]
2328 pub fn scale_validator_count(origin: OriginFor<T>, factor: Percent) -> DispatchResult {
2329 ensure_root(origin)?;
2330 let old = ValidatorCount::<T>::get();
2331 let new = old.checked_add(factor.mul_floor(old)).ok_or(ArithmeticError::Overflow)?;
2332
2333 ensure!(new <= T::MaxValidatorSet::get(), Error::<T>::TooManyValidators);
2334
2335 ValidatorCount::<T>::put(new);
2336 Ok(())
2337 }
2338
2339 #[pallet::call_index(12)]
2349 #[pallet::weight(T::WeightInfo::force_no_eras())]
2350 pub fn force_no_eras(origin: OriginFor<T>) -> DispatchResult {
2351 ensure_root(origin)?;
2352 Self::set_force_era(Forcing::ForceNone);
2353 Ok(())
2354 }
2355
2356 #[pallet::call_index(13)]
2367 #[pallet::weight(T::WeightInfo::force_new_era())]
2368 pub fn force_new_era(origin: OriginFor<T>) -> DispatchResult {
2369 ensure_root(origin)?;
2370 Self::set_force_era(Forcing::ForceNew);
2371 Ok(())
2372 }
2373
2374 #[pallet::call_index(15)]
2383 #[pallet::weight(T::WeightInfo::force_unstake())]
2384 pub fn force_unstake(
2385 origin: OriginFor<T>,
2386 stash: T::AccountId,
2387 _num_slashing_spans: u32,
2388 ) -> DispatchResult {
2389 ensure_root(origin)?;
2390
2391 Self::kill_stash(&stash)?;
2393
2394 Ok(())
2395 }
2396
2397 #[pallet::call_index(16)]
2407 #[pallet::weight(T::WeightInfo::force_new_era_always())]
2408 pub fn force_new_era_always(origin: OriginFor<T>) -> DispatchResult {
2409 ensure_root(origin)?;
2410 Self::set_force_era(Forcing::ForceAlways);
2411 Ok(())
2412 }
2413
2414 #[pallet::call_index(17)]
2426 #[pallet::weight(T::WeightInfo::cancel_deferred_slash(validator_slashes.len() as u32))]
2427 pub fn cancel_deferred_slash(
2428 origin: OriginFor<T>,
2429 era: EraIndex,
2430 validator_slashes: Vec<(T::AccountId, Perbill)>,
2431 ) -> DispatchResult {
2432 T::AdminOrigin::ensure_origin(origin)?;
2433 ensure!(!validator_slashes.is_empty(), Error::<T>::EmptyTargets);
2434
2435 let mut cancelled_slashes = CancelledSlashes::<T>::get(&era);
2437
2438 for (validator, slash_fraction) in validator_slashes {
2440 cancelled_slashes.retain(|(v, _)| v != &validator);
2445
2446 cancelled_slashes
2448 .try_push((validator.clone(), slash_fraction))
2449 .map_err(|_| Error::<T>::BoundNotMet)
2450 .defensive_proof("cancelled_slashes should have capacity for all validators")?;
2451
2452 Self::deposit_event(Event::<T>::SlashCancelled { slash_era: era, validator });
2453 }
2454
2455 CancelledSlashes::<T>::insert(&era, cancelled_slashes);
2457
2458 Ok(())
2459 }
2460
2461 #[pallet::call_index(18)]
2475 #[pallet::weight(T::WeightInfo::payout_stakers_alive_staked(T::MaxExposurePageSize::get()))]
2476 pub fn payout_stakers(
2477 origin: OriginFor<T>,
2478 validator_stash: T::AccountId,
2479 era: EraIndex,
2480 ) -> DispatchResultWithPostInfo {
2481 ensure_signed(origin)?;
2482
2483 Self::do_payout_stakers(validator_stash, era)
2484 }
2485
2486 #[pallet::call_index(19)]
2490 #[pallet::weight(T::WeightInfo::rebond(T::MaxUnlockingChunks::get() as u32))]
2491 pub fn rebond(
2492 origin: OriginFor<T>,
2493 #[pallet::compact] value: BalanceOf<T>,
2494 ) -> DispatchResultWithPostInfo {
2495 let controller = ensure_signed(origin)?;
2496 let ledger = Self::ledger(Controller(controller))?;
2497
2498 ensure!(!T::Filter::contains(&ledger.stash), Error::<T>::Restricted);
2499 ensure!(!ledger.unlocking.is_empty(), Error::<T>::NoUnlockChunk);
2500
2501 let initial_unlocking = ledger.unlocking.len() as u32;
2502 let (ledger, rebonded_value) = ledger.rebond(value);
2503 ensure!(ledger.active >= Self::min_chilled_bond(), Error::<T>::InsufficientBond);
2505
2506 Self::deposit_event(Event::<T>::Bonded {
2507 stash: ledger.stash.clone(),
2508 amount: rebonded_value,
2509 });
2510
2511 let stash = ledger.stash.clone();
2512 let final_unlocking = ledger.unlocking.len();
2513
2514 ledger.update()?;
2516 if T::VoterList::contains(&stash) {
2517 let _ = T::VoterList::on_update(&stash, Self::weight_of(&stash));
2518 }
2519
2520 let removed_chunks = 1u32 .saturating_add(initial_unlocking)
2522 .saturating_sub(final_unlocking as u32);
2523 Ok(Some(T::WeightInfo::rebond(removed_chunks)).into())
2524 }
2525
2526 #[pallet::call_index(20)]
2551 #[pallet::weight(T::WeightInfo::reap_stash())]
2552 pub fn reap_stash(
2553 origin: OriginFor<T>,
2554 stash: T::AccountId,
2555 _num_slashing_spans: u32,
2556 ) -> DispatchResultWithPostInfo {
2557 let _ = ensure_signed(origin)?;
2558
2559 ensure!(!Self::is_virtual_staker(&stash), Error::<T>::VirtualStakerNotAllowed);
2561
2562 let ed = asset::existential_deposit::<T>();
2563 let origin_balance = asset::total_balance::<T>(&stash);
2564 let ledger_total =
2565 Self::ledger(Stash(stash.clone())).map(|l| l.total).unwrap_or_default();
2566 let reapable = origin_balance < ed ||
2567 origin_balance.is_zero() ||
2568 ledger_total < ed ||
2569 ledger_total.is_zero();
2570 ensure!(reapable, Error::<T>::FundedTarget);
2571
2572 Self::kill_stash(&stash)?;
2574
2575 Ok(Pays::No.into())
2576 }
2577
2578 #[pallet::call_index(21)]
2590 #[pallet::weight(T::WeightInfo::kick(who.len() as u32))]
2591 pub fn kick(origin: OriginFor<T>, who: Vec<AccountIdLookupOf<T>>) -> DispatchResult {
2592 let controller = ensure_signed(origin)?;
2593 let ledger = Self::ledger(Controller(controller))?;
2594 let stash = &ledger.stash;
2595
2596 for nom_stash in who
2597 .into_iter()
2598 .map(T::Lookup::lookup)
2599 .collect::<Result<Vec<T::AccountId>, _>>()?
2600 .into_iter()
2601 {
2602 Nominators::<T>::mutate(&nom_stash, |maybe_nom| {
2603 if let Some(ref mut nom) = maybe_nom {
2604 if let Some(pos) = nom.targets.iter().position(|v| v == stash) {
2605 nom.targets.swap_remove(pos);
2606 Self::deposit_event(Event::<T>::Kicked {
2607 nominator: nom_stash.clone(),
2608 stash: stash.clone(),
2609 });
2610 }
2611 }
2612 });
2613 }
2614
2615 Ok(())
2616 }
2617
2618 #[pallet::call_index(22)]
2641 #[pallet::weight(
2642 T::WeightInfo::set_staking_configs_all_set()
2643 .max(T::WeightInfo::set_staking_configs_all_remove())
2644 )]
2645 pub fn set_staking_configs(
2646 origin: OriginFor<T>,
2647 min_nominator_bond: ConfigOp<BalanceOf<T>>,
2648 min_validator_bond: ConfigOp<BalanceOf<T>>,
2649 max_nominator_count: ConfigOp<u32>,
2650 max_validator_count: ConfigOp<u32>,
2651 chill_threshold: ConfigOp<Percent>,
2652 min_commission: ConfigOp<Perbill>,
2653 max_staked_rewards: ConfigOp<Percent>,
2654 are_nominators_slashable: ConfigOp<bool>,
2655 chill_inactive_threshold: ConfigOp<u32>,
2656 ) -> DispatchResult {
2657 ensure_root(origin)?;
2658
2659 if let ConfigOp::Set(threshold) = chill_inactive_threshold {
2660 ensure!(
2661 threshold > 1 && threshold <= T::HistoryDepth::get(),
2662 Error::<T>::InvalidChillInactiveThreshold
2663 );
2664 }
2665
2666 macro_rules! config_op_exp {
2667 ($storage:ty, $op:ident) => {
2668 match $op {
2669 ConfigOp::Noop => (),
2670 ConfigOp::Set(v) => <$storage>::put(v),
2671 ConfigOp::Remove => <$storage>::kill(),
2672 }
2673 };
2674 }
2675
2676 config_op_exp!(MinNominatorBond<T>, min_nominator_bond);
2677 config_op_exp!(MinValidatorBond<T>, min_validator_bond);
2678 config_op_exp!(MaxNominatorsCount<T>, max_nominator_count);
2679 config_op_exp!(MaxValidatorsCount<T>, max_validator_count);
2680 config_op_exp!(ChillThreshold<T>, chill_threshold);
2681 config_op_exp!(MinCommission<T>, min_commission);
2682 config_op_exp!(MaxStakedRewards<T>, max_staked_rewards);
2683 config_op_exp!(AreNominatorsSlashable<T>, are_nominators_slashable);
2684 config_op_exp!(ChillInactiveThreshold<T>, chill_inactive_threshold);
2685
2686 Ok(())
2687 }
2688 #[pallet::call_index(23)]
2715 #[pallet::weight(T::WeightInfo::chill_other())]
2716 pub fn chill_other(origin: OriginFor<T>, stash: T::AccountId) -> DispatchResult {
2717 let caller = ensure_signed(origin)?;
2719 let ledger = Self::ledger(Stash(stash.clone()))?;
2720 let controller = ledger
2721 .controller()
2722 .defensive_proof(
2723 "Ledger's controller field didn't exist. The controller should have been fetched using StakingLedger.",
2724 )
2725 .ok_or(Error::<T>::NotController)?;
2726
2727 if Nominators::<T>::contains_key(&stash) && Nominators::<T>::get(&stash).is_none() {
2744 Self::chill_stash(&stash);
2745 return Ok(());
2746 }
2747
2748 if caller != controller {
2749 let threshold = ChillThreshold::<T>::get().ok_or(Error::<T>::CannotChillOther)?;
2750 let min_active_bond = if Nominators::<T>::contains_key(&stash) {
2751 let max_nominator_count =
2752 MaxNominatorsCount::<T>::get().ok_or(Error::<T>::CannotChillOther)?;
2753 let current_nominator_count = Nominators::<T>::count();
2754 ensure!(
2755 threshold * max_nominator_count < current_nominator_count,
2756 Error::<T>::CannotChillOther
2757 );
2758 Self::min_nominator_bond()
2759 } else if Validators::<T>::contains_key(&stash) {
2760 let max_validator_count =
2761 MaxValidatorsCount::<T>::get().ok_or(Error::<T>::CannotChillOther)?;
2762 let current_validator_count = Validators::<T>::count();
2763 ensure!(
2764 threshold * max_validator_count < current_validator_count,
2765 Error::<T>::CannotChillOther
2766 );
2767 Self::min_validator_bond()
2768 } else {
2769 Zero::zero()
2770 };
2771
2772 ensure!(ledger.active < min_active_bond, Error::<T>::CannotChillOther);
2773 }
2774
2775 Self::chill_stash(&stash);
2776 Ok(())
2777 }
2778
2779 #[pallet::call_index(24)]
2784 #[pallet::weight(T::WeightInfo::force_apply_min_commission())]
2785 pub fn force_apply_min_commission(
2786 origin: OriginFor<T>,
2787 validator_stash: T::AccountId,
2788 ) -> DispatchResult {
2789 ensure_signed(origin)?;
2790 let min_commission = MinCommission::<T>::get();
2791 let max_commission = MaxCommission::<T>::get();
2792 Validators::<T>::try_mutate_exists(validator_stash, |maybe_prefs| {
2793 maybe_prefs
2794 .as_mut()
2795 .map(|prefs| {
2796 if prefs.commission < min_commission {
2797 prefs.commission = min_commission;
2798 }
2799 if prefs.commission > max_commission {
2800 prefs.commission = max_commission;
2801 }
2802 })
2803 .ok_or(Error::<T>::NotStash)
2804 })?;
2805 Ok(())
2806 }
2807
2808 #[pallet::call_index(25)]
2813 #[pallet::weight(T::WeightInfo::set_min_commission())]
2814 pub fn set_min_commission(origin: OriginFor<T>, new: Perbill) -> DispatchResult {
2815 T::AdminOrigin::ensure_origin(origin)?;
2816 ensure!(new <= MaxCommission::<T>::get(), Error::<T>::CommissionTooHigh);
2817 MinCommission::<T>::put(new);
2818 Ok(())
2819 }
2820
2821 #[pallet::call_index(26)]
2843 #[pallet::weight(T::WeightInfo::payout_stakers_alive_staked(T::MaxExposurePageSize::get()))]
2844 pub fn payout_stakers_by_page(
2845 origin: OriginFor<T>,
2846 validator_stash: T::AccountId,
2847 era: EraIndex,
2848 page: Page,
2849 ) -> DispatchResultWithPostInfo {
2850 ensure_signed(origin)?;
2851 Self::do_payout_stakers_by_page(validator_stash, era, page)
2852 }
2853
2854 #[pallet::call_index(27)]
2861 #[pallet::weight(T::WeightInfo::update_payee())]
2862 pub fn update_payee(
2863 origin: OriginFor<T>,
2864 controller: T::AccountId,
2865 ) -> DispatchResultWithPostInfo {
2866 let _ = ensure_signed(origin)?;
2867 let ledger = Self::ledger(StakingAccount::Controller(controller.clone()))?;
2868
2869 ensure!(
2870 (Payee::<T>::get(&ledger.stash) == {
2871 #[allow(deprecated)]
2872 Some(RewardDestination::Controller)
2873 }),
2874 Error::<T>::NotController
2875 );
2876
2877 let _ = ledger
2878 .set_payee(RewardDestination::Account(controller))
2879 .defensive_proof("ledger should have been previously retrieved from storage.")?;
2880
2881 Ok(Pays::No.into())
2882 }
2883
2884 #[pallet::call_index(28)]
2892 #[pallet::weight(T::WeightInfo::deprecate_controller_batch(controllers.len() as u32))]
2893 pub fn deprecate_controller_batch(
2894 origin: OriginFor<T>,
2895 controllers: BoundedVec<T::AccountId, T::MaxControllersInDeprecationBatch>,
2896 ) -> DispatchResultWithPostInfo {
2897 T::AdminOrigin::ensure_origin(origin)?;
2898
2899 let filtered_batch_with_ledger: Vec<_> = controllers
2901 .iter()
2902 .filter_map(|controller| {
2903 let ledger = Self::ledger(StakingAccount::Controller(controller.clone()));
2904 ledger.ok().map_or(None, |ledger| {
2905 let payee_deprecated = Payee::<T>::get(&ledger.stash) == {
2908 #[allow(deprecated)]
2909 Some(RewardDestination::Controller)
2910 };
2911
2912 if ledger.stash != *controller && !payee_deprecated {
2913 Some(ledger)
2914 } else {
2915 None
2916 }
2917 })
2918 })
2919 .collect();
2920
2921 let mut failures = 0;
2923 for ledger in filtered_batch_with_ledger {
2924 let _ = ledger.clone().set_controller_to_stash().map_err(|_| failures += 1);
2925 }
2926 Self::deposit_event(Event::<T>::ControllerBatchDeprecated { failures });
2927
2928 Ok(Some(T::WeightInfo::deprecate_controller_batch(controllers.len() as u32)).into())
2929 }
2930
2931 #[pallet::call_index(29)]
2943 #[pallet::weight(T::WeightInfo::restore_ledger())]
2944 pub fn restore_ledger(
2945 origin: OriginFor<T>,
2946 stash: T::AccountId,
2947 maybe_controller: Option<T::AccountId>,
2948 maybe_total: Option<BalanceOf<T>>,
2949 maybe_unlocking: Option<BoundedVec<UnlockChunk<BalanceOf<T>>, T::MaxUnlockingChunks>>,
2950 ) -> DispatchResult {
2951 T::AdminOrigin::ensure_origin(origin)?;
2952
2953 ensure!(!Self::is_virtual_staker(&stash), Error::<T>::VirtualStakerNotAllowed);
2955
2956 let current_lock = asset::staked::<T>(&stash);
2957 let stash_balance = asset::stakeable_balance::<T>(&stash);
2958
2959 let (new_controller, new_total) = match Self::inspect_bond_state(&stash) {
2960 Ok(LedgerIntegrityState::Corrupted) => {
2961 let new_controller = maybe_controller.unwrap_or(stash.clone());
2962
2963 let new_total = if let Some(total) = maybe_total {
2964 let new_total = total.min(stash_balance);
2965 asset::update_stake::<T>(&stash, new_total)?;
2967 new_total
2968 } else {
2969 current_lock
2970 };
2971
2972 Ok((new_controller, new_total))
2973 },
2974 Ok(LedgerIntegrityState::CorruptedKilled) => {
2975 if current_lock == Zero::zero() {
2976 ensure!(maybe_total.is_some(), Error::<T>::CannotRestoreLedger);
2980 Ok((
2981 stash.clone(),
2982 maybe_total.expect("total exists as per the check above; qed."),
2983 ))
2984 } else {
2985 Ok((stash.clone(), current_lock))
2986 }
2987 },
2988 Ok(LedgerIntegrityState::LockCorrupted) => {
2989 let new_total =
2992 maybe_total.ok_or(Error::<T>::CannotRestoreLedger)?.min(stash_balance);
2993 asset::update_stake::<T>(&stash, new_total)?;
2994
2995 Ok((stash.clone(), new_total))
2996 },
2997 Err(Error::<T>::BadState) => {
2998 asset::kill_stake::<T>(&stash)?;
3000 ensure!(
3001 Self::inspect_bond_state(&stash) == Err(Error::<T>::NotStash),
3002 Error::<T>::BadState
3003 );
3004
3005 return Ok(());
3006 },
3007 Ok(LedgerIntegrityState::Ok) | Err(_) => Err(Error::<T>::CannotRestoreLedger),
3008 }?;
3009
3010 Bonded::<T>::insert(&stash, &new_controller);
3012
3013 let mut ledger = StakingLedger::<T>::new(stash.clone(), new_total);
3015 ledger.controller = Some(new_controller);
3016 ledger.unlocking = maybe_unlocking.unwrap_or_default();
3017 ledger.update()?;
3018
3019 ensure!(
3020 Self::inspect_bond_state(&stash) == Ok(LedgerIntegrityState::Ok),
3021 Error::<T>::BadState
3022 );
3023 Ok(())
3024 }
3025
3026 #[pallet::call_index(30)]
3034 #[pallet::weight(T::WeightInfo::migrate_currency())]
3035 pub fn migrate_currency(
3036 origin: OriginFor<T>,
3037 stash: T::AccountId,
3038 ) -> DispatchResultWithPostInfo {
3039 let _ = ensure_signed(origin)?;
3040 Self::do_migrate_currency(&stash)?;
3041
3042 Ok(Pays::No.into())
3044 }
3045
3046 #[pallet::call_index(31)]
3081 #[pallet::weight(T::WeightInfo::apply_slash(T::MaxExposurePageSize::get()))]
3082 pub fn apply_slash(
3083 origin: OriginFor<T>,
3084 slash_era: EraIndex,
3085 slash_key: (T::AccountId, Perbill, u32),
3086 ) -> DispatchResultWithPostInfo {
3087 let _ = ensure_signed(origin)?;
3088 let active_era = ActiveEra::<T>::get().map(|a| a.index).unwrap_or_default();
3089 ensure!(slash_era <= active_era, Error::<T>::EraNotStarted);
3090
3091 ensure!(
3093 !Self::check_slash_cancelled(slash_era, &slash_key.0, slash_key.1),
3094 Error::<T>::CancelledSlash
3095 );
3096
3097 let unapplied_slash = UnappliedSlashes::<T>::take(&slash_era, &slash_key)
3098 .ok_or(Error::<T>::InvalidSlashRecord)?;
3099 slashing::apply_slash::<T>(unapplied_slash, Self::offence_era_of(slash_era));
3100
3101 Ok(Pays::No.into())
3102 }
3103
3104 #[pallet::call_index(32)]
3116 #[pallet::weight({
3118 let v = T::MaxValidatorSet::get();
3119 T::WeightInfo::prune_era_stakers_paged(v)
3120 .max(T::WeightInfo::prune_era_stakers_overview(v))
3121 .max(T::WeightInfo::prune_era_validator_prefs(v))
3122 .max(T::WeightInfo::prune_era_claimed_rewards(v))
3123 .max(T::WeightInfo::prune_era_validator_reward())
3124 .max(T::WeightInfo::prune_era_reward_points())
3125 .max(T::WeightInfo::prune_era_single_entry_cleanups())
3126 .max(T::WeightInfo::prune_era_validator_slash_in_era(v))
3127 .max(T::WeightInfo::prune_era_validator_incentive_weight(v))
3128 })]
3129 pub fn prune_era_step(origin: OriginFor<T>, era: EraIndex) -> DispatchResultWithPostInfo {
3130 let _ = ensure_signed(origin)?;
3131
3132 let active_era = crate::session_rotation::Rotator::<T>::active_era();
3134 let history_depth = T::HistoryDepth::get();
3135 let earliest_prunable_era = active_era.saturating_sub(history_depth).saturating_sub(1);
3136 ensure!(era <= earliest_prunable_era, Error::<T>::EraNotPrunable);
3137
3138 let actual_weight = Self::do_prune_era_step(era)?;
3139
3140 Ok(frame_support::dispatch::PostDispatchInfo {
3141 actual_weight: Some(actual_weight),
3142 pays_fee: frame_support::dispatch::Pays::No,
3143 })
3144 }
3145
3146 #[pallet::call_index(33)]
3150 #[pallet::weight(T::WeightInfo::set_max_commission())]
3151 pub fn set_max_commission(origin: OriginFor<T>, new: Perbill) -> DispatchResult {
3152 T::AdminOrigin::ensure_origin(origin)?;
3153 ensure!(new >= MinCommission::<T>::get(), Error::<T>::CommissionTooLow);
3154 MaxCommission::<T>::put(new);
3155 Ok(())
3156 }
3157
3158 #[pallet::call_index(34)]
3164 #[pallet::weight(T::WeightInfo::set_validator_self_stake_incentive_config())]
3165 pub fn set_validator_self_stake_incentive_config(
3166 origin: OriginFor<T>,
3167 optimum_self_stake: ConfigOp<BalanceOf<T>>,
3168 hard_cap_self_stake: ConfigOp<BalanceOf<T>>,
3169 self_stake_slope_factor: ConfigOp<Perbill>,
3170 ) -> DispatchResult {
3171 T::AdminOrigin::ensure_origin(origin)?;
3172
3173 let new_optimum = match optimum_self_stake {
3174 ConfigOp::Noop => OptimumSelfStake::<T>::get(),
3175 ConfigOp::Set(v) => v,
3176 ConfigOp::Remove => BalanceOf::<T>::zero(),
3177 };
3178
3179 let new_cap = match hard_cap_self_stake {
3180 ConfigOp::Noop => HardCapSelfStake::<T>::get(),
3181 ConfigOp::Set(v) => v,
3182 ConfigOp::Remove => BalanceOf::<T>::zero(),
3183 };
3184
3185 ensure!(new_optimum <= new_cap, Error::<T>::OptimumGreaterThanCap);
3186
3187 let has_changes = !matches!(
3188 (&optimum_self_stake, &hard_cap_self_stake, &self_stake_slope_factor),
3189 (ConfigOp::Noop, ConfigOp::Noop, ConfigOp::Noop)
3190 );
3191
3192 macro_rules! config_op_exp {
3193 ($storage:ty, $op:ident) => {
3194 match $op {
3195 ConfigOp::Noop => (),
3196 ConfigOp::Set(v) => <$storage>::put(v),
3197 ConfigOp::Remove => <$storage>::kill(),
3198 }
3199 };
3200 }
3201
3202 config_op_exp!(OptimumSelfStake<T>, optimum_self_stake);
3203 config_op_exp!(HardCapSelfStake<T>, hard_cap_self_stake);
3204 config_op_exp!(SelfStakeSlopeFactor<T>, self_stake_slope_factor);
3205
3206 if has_changes {
3207 Self::deposit_event(Event::<T>::ValidatorIncentiveConfigSet {
3208 optimum_self_stake: OptimumSelfStake::<T>::get(),
3209 hard_cap_self_stake: HardCapSelfStake::<T>::get(),
3210 slope_factor: SelfStakeSlopeFactor::<T>::get(),
3211 });
3212 }
3213
3214 Ok(())
3215 }
3216
3217 #[pallet::call_index(35)]
3232 #[pallet::weight(T::WeightInfo::chill_inactive(proof.len() as _))]
3233 pub fn chill_inactive(
3234 origin: OriginFor<T>,
3235 stash: T::AccountId,
3236 proof: BoundedVec<EraIndex, T::HistoryDepth>,
3237 ) -> DispatchResultWithPostInfo {
3238 ensure_signed(origin)?;
3239
3240 let threshold = ChillInactiveThreshold::<T>::get();
3241 ensure!(
3242 proof.len() as EraIndex == threshold,
3243 Error::<T>::InvalidInactivityProof(InvalidInactivityProofError::InvalidLen)
3244 );
3245 ensure!(
3246 proof.is_sorted_by(|a, b| a < b),
3247 Error::<T>::InvalidInactivityProof(InvalidInactivityProofError::NotSorted)
3248 );
3249
3250 let active_era = Rotator::<T>::active_era();
3253 let oldest_allowed_era = active_era.saturating_sub(T::HistoryDepth::get());
3254 let oldest_proof_era = proof.first().copied().unwrap_or(EraIndex::MAX);
3255 let most_recent_proof_era = proof.last().copied().unwrap_or(EraIndex::MAX);
3256 ensure!(
3257 oldest_proof_era >= oldest_allowed_era && most_recent_proof_era < active_era,
3258 Error::<T>::InvalidInactivityProof(InvalidInactivityProofError::InvalidEra)
3259 );
3260
3261 for era in proof {
3262 ensure!(
3263 Eras::<T>::was_validator_exposed(era, &stash),
3264 Error::<T>::InvalidInactivityProof(
3265 InvalidInactivityProofError::ValidatorNotExposed
3266 )
3267 );
3268
3269 let points = Eras::<T>::get_reward_points_for_validator(era, &stash);
3270
3271 ensure!(
3272 T::IsValidatorInactive::is_inactive(era, &stash, points),
3273 Error::<T>::InvalidInactivityProof(
3274 InvalidInactivityProofError::ValidatorActive
3275 )
3276 );
3277 }
3278
3279 if Self::do_remove_validator(&stash) {
3280 Self::deposit_event(Event::<T>::Chilled { stash });
3281
3282 Ok(Pays::No.into())
3283 } else {
3284 Err(Error::<T>::BadTarget.into())
3285 }
3286 }
3287 }
3288
3289 #[pallet::view_functions]
3290 impl<T: Config> Pallet<T> {
3291 pub fn pot_account(pot: crate::RewardPot) -> T::AccountId {
3293 <T::RewardPots as crate::PotAccountProvider<T::AccountId>>::pot_account(pot)
3294 }
3295
3296 pub fn pot_balance(pot: crate::RewardPot) -> BalanceOf<T> {
3298 let account =
3299 <T::RewardPots as crate::PotAccountProvider<T::AccountId>>::pot_account(pot);
3300 <T::Currency as frame_support::traits::fungible::Inspect<T::AccountId>>::balance(
3301 &account,
3302 )
3303 }
3304
3305 pub fn era_reward_allocation(
3309 era: EraIndex,
3310 ) -> crate::reward::EraRewardAllocation<BalanceOf<T>> {
3311 crate::reward::EraRewardAllocation {
3312 staker_rewards: ErasValidatorReward::<T>::get(era).unwrap_or_else(Zero::zero),
3313 validator_incentive: ErasValidatorIncentiveBudget::<T>::get(era),
3314 }
3315 }
3316 }
3317}