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 era: EraIndex,
1345 },
1346 Withdrawn {
1349 stash: T::AccountId,
1350 amount: BalanceOf<T>,
1351 },
1352 StakerRemoved {
1355 stash: T::AccountId,
1356 },
1357 Kicked {
1359 nominator: T::AccountId,
1360 stash: T::AccountId,
1361 },
1362 Chilled {
1364 stash: T::AccountId,
1365 },
1366 PayoutStarted {
1368 era_index: EraIndex,
1369 validator_stash: T::AccountId,
1370 page: Page,
1371 next: Option<Page>,
1372 },
1373 ValidatorPrefsSet {
1375 stash: T::AccountId,
1376 prefs: ValidatorPrefs,
1377 },
1378 SnapshotVotersSizeExceeded {
1380 size: u32,
1381 },
1382 SnapshotTargetsSizeExceeded {
1384 size: u32,
1385 },
1386 ForceEra {
1387 mode: Forcing,
1388 },
1389 ControllerBatchDeprecated {
1391 failures: u32,
1392 },
1393 CurrencyMigrated {
1396 stash: T::AccountId,
1397 force_withdraw: BalanceOf<T>,
1398 },
1399 PagedElectionProceeded {
1409 page: PageIndex,
1410 result: Result<u32, u32>,
1411 },
1412 OffenceReported {
1415 offence_era: EraIndex,
1416 validator: T::AccountId,
1417 fraction: Perbill,
1418 },
1419 SlashComputed {
1421 offence_era: EraIndex,
1422 slash_era: EraIndex,
1423 offender: T::AccountId,
1424 page: u32,
1425 },
1426 SlashCancelled {
1428 slash_era: EraIndex,
1429 validator: T::AccountId,
1430 },
1431 SessionRotated {
1436 starting_session: SessionIndex,
1437 active_era: EraIndex,
1438 planned_era: EraIndex,
1439 },
1440 Unexpected(UnexpectedKind<T>),
1443 OffenceTooOld {
1445 offence_era: EraIndex,
1446 validator: T::AccountId,
1447 fraction: Perbill,
1448 },
1449 EraPruned {
1451 index: EraIndex,
1452 },
1453 ValidatorIncentivePaid {
1455 era: EraIndex,
1456 validator_stash: T::AccountId,
1457 dest: RewardDestination<T::AccountId>,
1458 amount: BalanceOf<T>,
1459 },
1460 ValidatorIncentiveConfigSet {
1462 optimum_self_stake: BalanceOf<T>,
1463 hard_cap_self_stake: BalanceOf<T>,
1464 slope_factor: Perbill,
1465 },
1466 }
1467
1468 #[derive(Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, TypeInfo, DebugNoBound)]
1474 #[codec(mel_bound())]
1475 #[scale_info(skip_type_params(T))]
1476 pub enum UnexpectedKind<T: Config> {
1477 EraDurationBoundExceeded,
1479 UnknownValidatorActivation,
1481 PagedElectionOutOfWeight { page: PageIndex, required: Weight, had: Weight },
1483 MissingPayee { era: EraIndex, stash: T::AccountId },
1485 ValidatorIncentiveWeightMismatch { era: EraIndex },
1487 ValidatorIncentiveTransferFailed { era: EraIndex },
1489 }
1490
1491 #[pallet::error]
1492 #[derive(PartialEq)]
1493 pub enum Error<T> {
1494 NotController,
1496 NotStash,
1498 AlreadyBonded,
1500 AlreadyPaired,
1502 EmptyTargets,
1504 DuplicateIndex,
1506 InvalidSlashRecord,
1508 InsufficientBond,
1512 NoMoreChunks,
1514 NoUnlockChunk,
1516 FundedTarget,
1518 InvalidEraToReward,
1520 InvalidNumberOfNominations,
1522 AlreadyClaimed,
1524 InvalidPage,
1526 IncorrectHistoryDepth,
1528 BadState,
1530 TooManyTargets,
1532 BadTarget,
1534 CannotChillOther,
1536 TooManyNominators,
1539 TooManyValidators,
1542 CommissionTooLow,
1544 BoundNotMet,
1546 ControllerDeprecated,
1548 CannotRestoreLedger,
1550 RewardDestinationRestricted,
1552 NotEnoughFunds,
1554 VirtualStakerNotAllowed,
1556 CannotReapStash,
1558 AlreadyMigrated,
1560 EraNotStarted,
1562 Restricted,
1565 UnappliedSlashesInPreviousEra,
1568 EraNotPrunable,
1570 CancelledSlash,
1572 CommissionTooHigh,
1574 OptimumGreaterThanCap,
1576 InvalidInactivityProof(InvalidInactivityProofError),
1578 InvalidChillInactiveThreshold,
1580 }
1581
1582 #[derive(Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, TypeInfo, PalletError)]
1583 pub enum InvalidInactivityProofError {
1584 InvalidLen,
1586 NotSorted,
1588 ValidatorNotExposed,
1590 ValidatorActive,
1592 InvalidEra,
1594 }
1595
1596 impl<T: Config> Pallet<T> {
1597 pub fn apply_unapplied_slashes(active_era: EraIndex) -> Weight {
1599 let mut slashes = UnappliedSlashes::<T>::iter_prefix(&active_era).take(1);
1600 if let Some((key, slash)) = slashes.next() {
1601 crate::log!(
1602 debug,
1603 "🦹 found slash {:?} scheduled to be executed in era {:?}",
1604 slash,
1605 active_era,
1606 );
1607
1608 let nominators_slashed = slash.others.len() as u32;
1609
1610 if Self::check_slash_cancelled(active_era, &key.0, key.1) {
1612 crate::log!(
1613 debug,
1614 "🦹 slash for {:?} in era {:?} was cancelled, skipping",
1615 key.0,
1616 active_era,
1617 );
1618 } else {
1619 slashing::apply_slash::<T>(slash, Self::offence_era_of(active_era));
1620 }
1621
1622 UnappliedSlashes::<T>::remove(&active_era, &key);
1624
1625 if UnappliedSlashes::<T>::iter_prefix(&active_era).next().is_none() {
1627 CancelledSlashes::<T>::remove(&active_era);
1629 }
1630
1631 T::WeightInfo::apply_slash(nominators_slashed)
1632 } else {
1633 T::DbWeight::get().reads(1)
1635 }
1636 }
1637
1638 fn do_prune_era_step(era: EraIndex) -> Result<Weight, DispatchError> {
1640 let current_step = EraPruningState::<T>::get(era).ok_or(Error::<T>::EraNotPrunable)?;
1645
1646 let items_limit = T::MaxPruningItems::get().min(T::MaxValidatorSet::get());
1649
1650 let actual_weight = match current_step {
1651 PruningStep::ErasStakersPaged => {
1652 let result = ErasStakersPaged::<T>::clear_prefix((era,), items_limit, None);
1653 let items_deleted = result.backend as u32;
1654 result.maybe_cursor.is_none().then(|| {
1655 EraPruningState::<T>::insert(era, PruningStep::ErasStakersOverview)
1656 });
1657 T::WeightInfo::prune_era_stakers_paged(items_deleted)
1658 },
1659 PruningStep::ErasStakersOverview => {
1660 let result = ErasStakersOverview::<T>::clear_prefix(era, items_limit, None);
1661 let items_deleted = result.backend as u32;
1662 result.maybe_cursor.is_none().then(|| {
1663 EraPruningState::<T>::insert(era, PruningStep::ErasValidatorPrefs)
1664 });
1665 T::WeightInfo::prune_era_stakers_overview(items_deleted)
1666 },
1667 PruningStep::ErasValidatorPrefs => {
1668 let result = ErasValidatorPrefs::<T>::clear_prefix(era, items_limit, None);
1669 let items_deleted = result.backend as u32;
1670 result
1671 .maybe_cursor
1672 .is_none()
1673 .then(|| EraPruningState::<T>::insert(era, PruningStep::ClaimedRewards));
1674 T::WeightInfo::prune_era_validator_prefs(items_deleted)
1675 },
1676 PruningStep::ClaimedRewards => {
1677 let result = ClaimedRewards::<T>::clear_prefix(era, items_limit, None);
1678 let items_deleted = result.backend as u32;
1679 result.maybe_cursor.is_none().then(|| {
1680 EraPruningState::<T>::insert(era, PruningStep::ErasValidatorReward)
1681 });
1682 T::WeightInfo::prune_era_claimed_rewards(items_deleted)
1683 },
1684 PruningStep::ErasValidatorReward => {
1685 ErasValidatorReward::<T>::remove(era);
1686 EraPruningState::<T>::insert(era, PruningStep::ErasRewardPoints);
1687 T::WeightInfo::prune_era_validator_reward()
1688 },
1689 PruningStep::ErasRewardPoints => {
1690 ErasRewardPoints::<T>::remove(era);
1691 EraPruningState::<T>::insert(era, PruningStep::SingleEntryCleanups);
1692 T::WeightInfo::prune_era_reward_points()
1693 },
1694 PruningStep::SingleEntryCleanups => {
1695 ErasTotalStake::<T>::remove(era);
1696 ErasNominatorsSlashable::<T>::remove(era);
1697 ErasValidatorIncentiveBudget::<T>::remove(era);
1698 ErasSumValidatorIncentiveWeight::<T>::remove(era);
1699 ErasSumWeightedPoints::<T>::remove(era);
1700 EraPruningState::<T>::insert(era, PruningStep::ValidatorSlashInEra);
1701 T::WeightInfo::prune_era_single_entry_cleanups()
1702 },
1703 PruningStep::ValidatorSlashInEra => {
1704 let result = ValidatorSlashInEra::<T>::clear_prefix(era, items_limit, None);
1705 let items_deleted = result.backend as u32;
1706
1707 if result.maybe_cursor.is_none() {
1708 EraPruningState::<T>::insert(
1709 era,
1710 PruningStep::ErasValidatorIncentiveWeight,
1711 );
1712 }
1713
1714 T::WeightInfo::prune_era_validator_slash_in_era(items_deleted)
1715 },
1716 PruningStep::ErasValidatorIncentiveWeight => {
1717 let result =
1718 ErasValidatorIncentiveWeight::<T>::clear_prefix(era, items_limit, None);
1719 if result.maybe_cursor.is_none() {
1720 EraPruningState::<T>::remove(era);
1722 }
1723 T::WeightInfo::prune_era_validator_incentive_weight(result.backend as u32)
1724 },
1725 };
1726
1727 if EraPruningState::<T>::get(era).is_none() {
1729 Self::deposit_event(Event::<T>::EraPruned { index: era });
1730 }
1731
1732 Ok(actual_weight)
1733 }
1734 }
1735
1736 #[pallet::hooks]
1737 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
1738 fn on_poll(_now: BlockNumberFor<T>, weight_meter: &mut WeightMeter) {
1739 let (weight, exec) = EraElectionPlanner::<T>::maybe_fetch_election_results();
1740 crate::log!(
1741 trace,
1742 "weight of fetching next election page is {:?}, have {:?}",
1743 weight,
1744 weight_meter.remaining()
1745 );
1746
1747 if weight_meter.can_consume(weight) {
1748 exec(weight_meter);
1749 } else {
1750 Self::deposit_event(Event::<T>::Unexpected(
1751 UnexpectedKind::PagedElectionOutOfWeight {
1752 page: NextElectionPage::<T>::get().unwrap_or(
1753 EraElectionPlanner::<T>::election_pages().defensive_saturating_sub(1),
1754 ),
1755 required: weight,
1756 had: weight_meter.remaining(),
1757 },
1758 ));
1759 }
1760 }
1761
1762 fn on_initialize(_now: BlockNumberFor<T>) -> Weight {
1763 let mut consumed_weight = slashing::process_offence_for_era::<T>();
1765
1766 consumed_weight.saturating_accrue(T::DbWeight::get().reads(1));
1768 if let Some(active_era) = ActiveEra::<T>::get() {
1769 let slash_weight = Self::apply_unapplied_slashes(active_era.index);
1770 consumed_weight.saturating_accrue(slash_weight);
1771 }
1772
1773 consumed_weight
1774 }
1775
1776 fn integrity_test() {
1777 assert_eq!(
1779 MaxNominationsOf::<T>::get(),
1780 <Self as ElectionDataProvider>::MaxVotesPerVoter::get()
1781 );
1782
1783 assert!(!MaxNominationsOf::<T>::get().is_zero());
1785
1786 assert!(
1787 T::SlashDeferDuration::get() < T::BondingDuration::get() || T::BondingDuration::get() == 0,
1788 "As per documentation, slash defer duration ({}) should be less than bonding duration ({}).",
1789 T::SlashDeferDuration::get(),
1790 T::BondingDuration::get(),
1791 );
1792
1793 assert!(
1795 T::NominatorFastUnbondDuration::get() <= T::BondingDuration::get(),
1796 "NominatorFastUnbondDuration ({}) must not exceed BondingDuration ({}).",
1797 T::NominatorFastUnbondDuration::get(),
1798 T::BondingDuration::get(),
1799 );
1800 assert!(
1802 T::MaxPruningItems::get() >= 100,
1803 "MaxPruningItems must be at least 100 for efficient pruning, got: {}",
1804 T::MaxPruningItems::get()
1805 );
1806
1807 assert!(
1808 crate::POT_POOL_SIZE > T::HistoryDepth::get(),
1809 "POT_POOL_SIZE ({}) must be strictly greater than HistoryDepth ({}) \
1810 to avoid reusing a pot slot whose era is still in the active history.",
1811 crate::POT_POOL_SIZE,
1812 T::HistoryDepth::get(),
1813 );
1814
1815 if T::DisableMinting::get() {
1817 let (v, r) = T::EraPayout::era_payout(
1818 BalanceOf::<T>::from(1u64),
1819 BalanceOf::<T>::from(1u64),
1820 1000u64,
1821 );
1822 assert!(
1823 v.is_zero() && r.is_zero(),
1824 "DisableMinting is true but EraPayout returns non-zero. \
1825 Set EraPayout = () when DisableMinting = true."
1826 );
1827 }
1828 }
1829
1830 #[cfg(feature = "try-runtime")]
1831 fn try_state(n: BlockNumberFor<T>) -> Result<(), sp_runtime::TryRuntimeError> {
1832 Self::do_try_state(n)
1833 }
1834 }
1835
1836 #[pallet::call]
1837 impl<T: Config> Pallet<T> {
1838 #[pallet::call_index(0)]
1851 #[pallet::weight(T::WeightInfo::bond())]
1852 pub fn bond(
1853 origin: OriginFor<T>,
1854 #[pallet::compact] value: BalanceOf<T>,
1855 payee: RewardDestination<T::AccountId>,
1856 ) -> DispatchResult {
1857 let stash = ensure_signed(origin)?;
1858
1859 ensure!(!T::Filter::contains(&stash), Error::<T>::Restricted);
1860
1861 if StakingLedger::<T>::is_bonded(StakingAccount::Stash(stash.clone())) {
1862 return Err(Error::<T>::AlreadyBonded.into());
1863 }
1864
1865 if StakingLedger::<T>::is_bonded(StakingAccount::Controller(stash.clone())) {
1867 return Err(Error::<T>::AlreadyPaired.into());
1868 }
1869
1870 if value < Self::min_chilled_bond() {
1872 return Err(Error::<T>::InsufficientBond.into());
1873 }
1874
1875 let stash_balance = asset::free_to_stake::<T>(&stash);
1876 let value = value.min(stash_balance);
1877 Self::deposit_event(Event::<T>::Bonded { stash: stash.clone(), amount: value });
1878 let ledger = StakingLedger::<T>::new(stash.clone(), value);
1879
1880 ledger.bond(payee)?;
1883
1884 Ok(())
1885 }
1886
1887 #[pallet::call_index(1)]
1898 #[pallet::weight(T::WeightInfo::bond_extra())]
1899 pub fn bond_extra(
1900 origin: OriginFor<T>,
1901 #[pallet::compact] max_additional: BalanceOf<T>,
1902 ) -> DispatchResult {
1903 let stash = ensure_signed(origin)?;
1904 ensure!(!T::Filter::contains(&stash), Error::<T>::Restricted);
1905 Self::do_bond_extra(&stash, max_additional)
1906 }
1907
1908 #[pallet::call_index(2)]
1928 #[pallet::weight(
1929 T::WeightInfo::withdraw_unbonded_kill().saturating_add(T::WeightInfo::unbond()))
1930 ]
1931 pub fn unbond(
1932 origin: OriginFor<T>,
1933 #[pallet::compact] value: BalanceOf<T>,
1934 ) -> DispatchResultWithPostInfo {
1935 let controller = ensure_signed(origin)?;
1936 let unlocking =
1937 Self::ledger(Controller(controller.clone())).map(|l| l.unlocking.len())?;
1938
1939 let maybe_withdraw_weight = {
1942 if unlocking == T::MaxUnlockingChunks::get() as usize {
1943 Some(Self::do_withdraw_unbonded(&controller)?)
1944 } else {
1945 None
1946 }
1947 };
1948
1949 let mut ledger = Self::ledger(Controller(controller))?;
1952 let mut value = value.min(ledger.active);
1953 let stash = ledger.stash.clone();
1954
1955 let chill_weight = if value >= ledger.active {
1958 Self::chill_stash(&stash);
1959 T::WeightInfo::chill()
1960 } else {
1961 Weight::zero()
1962 };
1963
1964 ensure!(
1965 ledger.unlocking.len() < T::MaxUnlockingChunks::get() as usize,
1966 Error::<T>::NoMoreChunks,
1967 );
1968
1969 if !value.is_zero() {
1970 ledger.active -= value;
1971
1972 if ledger.active < asset::existential_deposit::<T>() {
1974 value += ledger.active;
1975 ledger.active = Zero::zero();
1976 }
1977
1978 let is_nominator = Nominators::<T>::contains_key(&stash);
1979
1980 let min_active_bond = if is_nominator {
1981 Self::min_nominator_bond()
1982 } else if Validators::<T>::contains_key(&stash) {
1983 Self::min_validator_bond()
1984 } else {
1985 Zero::zero()
1987 };
1988
1989 ensure!(ledger.active >= min_active_bond, Error::<T>::InsufficientBond);
1992
1993 let active_era = session_rotation::Rotator::<T>::active_era();
1999 let was_recent_validator = LastValidatorEra::<T>::get(&stash)
2000 .map(|last_era| active_era.saturating_sub(last_era) < T::BondingDuration::get())
2001 .unwrap_or(false);
2002
2003 let unbond_duration = if was_recent_validator {
2004 T::BondingDuration::get()
2006 } else {
2007 <Self as sp_staking::StakingInterface>::nominator_bonding_duration()
2009 };
2010
2011 let era =
2012 session_rotation::Rotator::<T>::active_era().saturating_add(unbond_duration);
2013 if let Some(chunk) = ledger.unlocking.last_mut().filter(|chunk| chunk.era == era) {
2014 chunk.value = chunk.value.defensive_saturating_add(value)
2018 } else {
2019 ledger
2020 .unlocking
2021 .try_push(UnlockChunk { value, era })
2022 .map_err(|_| Error::<T>::NoMoreChunks)?;
2023 };
2024 ledger.update()?;
2026
2027 if T::VoterList::contains(&stash) {
2029 let _ = T::VoterList::on_update(&stash, Self::weight_of(&stash));
2030 }
2031
2032 Self::deposit_event(Event::<T>::Unbonded { stash, amount: value, era });
2033 }
2034
2035 let actual_weight = if let Some(withdraw_weight) = maybe_withdraw_weight {
2036 Some(
2037 T::WeightInfo::unbond()
2038 .saturating_add(withdraw_weight)
2039 .saturating_add(chill_weight),
2040 )
2041 } else {
2042 Some(T::WeightInfo::unbond().saturating_add(chill_weight))
2043 };
2044
2045 Ok(actual_weight.into())
2046 }
2047
2048 #[pallet::call_index(3)]
2068 #[pallet::weight(T::WeightInfo::withdraw_unbonded_kill())]
2069 pub fn withdraw_unbonded(
2070 origin: OriginFor<T>,
2071 _num_slashing_spans: u32,
2072 ) -> DispatchResultWithPostInfo {
2073 let controller = ensure_signed(origin)?;
2074
2075 let actual_weight = Self::do_withdraw_unbonded(&controller)?;
2076 Ok(Some(actual_weight).into())
2077 }
2078
2079 #[pallet::call_index(4)]
2085 #[pallet::weight(T::WeightInfo::validate())]
2086 pub fn validate(origin: OriginFor<T>, prefs: ValidatorPrefs) -> DispatchResult {
2087 let controller = ensure_signed(origin)?;
2088
2089 let ledger = Self::ledger(Controller(controller))?;
2090
2091 ensure!(ledger.active >= Self::min_validator_bond(), Error::<T>::InsufficientBond);
2092 let stash = &ledger.stash;
2093
2094 ensure!(prefs.commission >= MinCommission::<T>::get(), Error::<T>::CommissionTooLow);
2096 ensure!(prefs.commission <= MaxCommission::<T>::get(), Error::<T>::CommissionTooHigh);
2097
2098 if !Validators::<T>::contains_key(stash) {
2100 if let Some(max_validators) = MaxValidatorsCount::<T>::get() {
2104 ensure!(
2105 Validators::<T>::count() < max_validators,
2106 Error::<T>::TooManyValidators
2107 );
2108 }
2109 }
2110
2111 Self::do_remove_nominator(stash);
2112 Self::do_add_validator(stash, prefs.clone());
2113 Self::deposit_event(Event::<T>::ValidatorPrefsSet { stash: ledger.stash, prefs });
2114
2115 Ok(())
2116 }
2117
2118 #[pallet::call_index(5)]
2124 #[pallet::weight(T::WeightInfo::nominate(targets.len() as u32))]
2125 pub fn nominate(
2126 origin: OriginFor<T>,
2127 targets: Vec<AccountIdLookupOf<T>>,
2128 ) -> DispatchResult {
2129 let controller = ensure_signed(origin)?;
2130
2131 let ledger = Self::ledger(StakingAccount::Controller(controller.clone()))?;
2132
2133 ensure!(ledger.active >= Self::min_nominator_bond(), Error::<T>::InsufficientBond);
2134 let stash = &ledger.stash;
2135
2136 if !Nominators::<T>::contains_key(stash) {
2138 if let Some(max_nominators) = MaxNominatorsCount::<T>::get() {
2142 ensure!(
2143 Nominators::<T>::count() < max_nominators,
2144 Error::<T>::TooManyNominators
2145 );
2146 }
2147 }
2148
2149 let mut targets = targets
2151 .into_iter()
2152 .map(|t| T::Lookup::lookup(t).map_err(DispatchError::from))
2153 .collect::<Result<Vec<_>, _>>()?;
2154 targets.sort();
2155 targets.dedup();
2156
2157 ensure!(!targets.is_empty(), Error::<T>::EmptyTargets);
2158 ensure!(
2159 targets.len() <= T::NominationsQuota::get_quota(ledger.active) as usize,
2160 Error::<T>::TooManyTargets
2161 );
2162
2163 let old = Nominators::<T>::get(stash).map_or_else(Vec::new, |x| x.targets.into_inner());
2164
2165 let targets: BoundedVec<_, _> = targets
2166 .into_iter()
2167 .map(|n| {
2168 if old.contains(&n) ||
2169 (Validators::<T>::contains_key(&n) && !Validators::<T>::get(&n).blocked)
2170 {
2171 Ok(n)
2172 } else {
2173 Err(Error::<T>::BadTarget.into())
2174 }
2175 })
2176 .collect::<Result<Vec<_>, DispatchError>>()?
2177 .try_into()
2178 .map_err(|_| Error::<T>::TooManyNominators)?;
2179
2180 let nominations = Nominations {
2181 targets,
2182 submitted_in: CurrentEra::<T>::get().unwrap_or(0),
2184 suppressed: false,
2185 };
2186
2187 Self::do_remove_validator(stash);
2188 Self::do_add_nominator(stash, nominations);
2189 Ok(())
2190 }
2191
2192 #[pallet::call_index(6)]
2203 #[pallet::weight(T::WeightInfo::chill())]
2204 pub fn chill(origin: OriginFor<T>) -> DispatchResult {
2205 let controller = ensure_signed(origin)?;
2206
2207 let ledger = Self::ledger(StakingAccount::Controller(controller))?;
2208
2209 Self::chill_stash(&ledger.stash);
2210 Ok(())
2211 }
2212
2213 #[pallet::call_index(7)]
2219 #[pallet::weight(T::WeightInfo::set_payee())]
2220 pub fn set_payee(
2221 origin: OriginFor<T>,
2222 payee: RewardDestination<T::AccountId>,
2223 ) -> DispatchResult {
2224 let controller = ensure_signed(origin)?;
2225 let ledger = Self::ledger(Controller(controller.clone()))?;
2226
2227 ensure!(
2228 (payee != {
2229 #[allow(deprecated)]
2230 RewardDestination::Controller
2231 }),
2232 Error::<T>::ControllerDeprecated
2233 );
2234
2235 let _ = ledger
2236 .set_payee(payee)
2237 .defensive_proof("ledger was retrieved from storage, thus it's bonded; qed.")?;
2238
2239 Ok(())
2240 }
2241
2242 #[pallet::call_index(8)]
2251 #[pallet::weight(T::WeightInfo::set_controller())]
2252 pub fn set_controller(origin: OriginFor<T>) -> DispatchResult {
2253 let stash = ensure_signed(origin)?;
2254
2255 Self::ledger(StakingAccount::Stash(stash.clone())).map(|ledger| {
2256 let controller = ledger.controller()
2257 .defensive_proof("Ledger's controller field didn't exist. The controller should have been fetched using StakingLedger.")
2258 .ok_or(Error::<T>::NotController)?;
2259
2260 if controller == stash {
2261 return Err(Error::<T>::AlreadyPaired.into())
2263 }
2264
2265 let _ = ledger.set_controller_to_stash()?;
2266 Ok(())
2267 })?
2268 }
2269
2270 #[pallet::call_index(9)]
2274 #[pallet::weight(T::WeightInfo::set_validator_count())]
2275 pub fn set_validator_count(
2276 origin: OriginFor<T>,
2277 #[pallet::compact] new: u32,
2278 ) -> DispatchResult {
2279 ensure_root(origin)?;
2280
2281 ensure!(new <= T::MaxValidatorSet::get(), Error::<T>::TooManyValidators);
2282
2283 ValidatorCount::<T>::put(new);
2284 Ok(())
2285 }
2286
2287 #[pallet::call_index(10)]
2292 #[pallet::weight(T::WeightInfo::set_validator_count())]
2293 pub fn increase_validator_count(
2294 origin: OriginFor<T>,
2295 #[pallet::compact] additional: u32,
2296 ) -> DispatchResult {
2297 ensure_root(origin)?;
2298 let old = ValidatorCount::<T>::get();
2299 let new = old.checked_add(additional).ok_or(ArithmeticError::Overflow)?;
2300
2301 ensure!(new <= T::MaxValidatorSet::get(), Error::<T>::TooManyValidators);
2302
2303 ValidatorCount::<T>::put(new);
2304 Ok(())
2305 }
2306
2307 #[pallet::call_index(11)]
2312 #[pallet::weight(T::WeightInfo::set_validator_count())]
2313 pub fn scale_validator_count(origin: OriginFor<T>, factor: Percent) -> DispatchResult {
2314 ensure_root(origin)?;
2315 let old = ValidatorCount::<T>::get();
2316 let new = old.checked_add(factor.mul_floor(old)).ok_or(ArithmeticError::Overflow)?;
2317
2318 ensure!(new <= T::MaxValidatorSet::get(), Error::<T>::TooManyValidators);
2319
2320 ValidatorCount::<T>::put(new);
2321 Ok(())
2322 }
2323
2324 #[pallet::call_index(12)]
2334 #[pallet::weight(T::WeightInfo::force_no_eras())]
2335 pub fn force_no_eras(origin: OriginFor<T>) -> DispatchResult {
2336 ensure_root(origin)?;
2337 Self::set_force_era(Forcing::ForceNone);
2338 Ok(())
2339 }
2340
2341 #[pallet::call_index(13)]
2352 #[pallet::weight(T::WeightInfo::force_new_era())]
2353 pub fn force_new_era(origin: OriginFor<T>) -> DispatchResult {
2354 ensure_root(origin)?;
2355 Self::set_force_era(Forcing::ForceNew);
2356 Ok(())
2357 }
2358
2359 #[pallet::call_index(15)]
2368 #[pallet::weight(T::WeightInfo::force_unstake())]
2369 pub fn force_unstake(
2370 origin: OriginFor<T>,
2371 stash: T::AccountId,
2372 _num_slashing_spans: u32,
2373 ) -> DispatchResult {
2374 ensure_root(origin)?;
2375
2376 Self::kill_stash(&stash)?;
2378
2379 Ok(())
2380 }
2381
2382 #[pallet::call_index(16)]
2392 #[pallet::weight(T::WeightInfo::force_new_era_always())]
2393 pub fn force_new_era_always(origin: OriginFor<T>) -> DispatchResult {
2394 ensure_root(origin)?;
2395 Self::set_force_era(Forcing::ForceAlways);
2396 Ok(())
2397 }
2398
2399 #[pallet::call_index(17)]
2411 #[pallet::weight(T::WeightInfo::cancel_deferred_slash(validator_slashes.len() as u32))]
2412 pub fn cancel_deferred_slash(
2413 origin: OriginFor<T>,
2414 era: EraIndex,
2415 validator_slashes: Vec<(T::AccountId, Perbill)>,
2416 ) -> DispatchResult {
2417 T::AdminOrigin::ensure_origin(origin)?;
2418 ensure!(!validator_slashes.is_empty(), Error::<T>::EmptyTargets);
2419
2420 let mut cancelled_slashes = CancelledSlashes::<T>::get(&era);
2422
2423 for (validator, slash_fraction) in validator_slashes {
2425 cancelled_slashes.retain(|(v, _)| v != &validator);
2430
2431 cancelled_slashes
2433 .try_push((validator.clone(), slash_fraction))
2434 .map_err(|_| Error::<T>::BoundNotMet)
2435 .defensive_proof("cancelled_slashes should have capacity for all validators")?;
2436
2437 Self::deposit_event(Event::<T>::SlashCancelled { slash_era: era, validator });
2438 }
2439
2440 CancelledSlashes::<T>::insert(&era, cancelled_slashes);
2442
2443 Ok(())
2444 }
2445
2446 #[pallet::call_index(18)]
2460 #[pallet::weight(T::WeightInfo::payout_stakers_alive_staked(T::MaxExposurePageSize::get()))]
2461 pub fn payout_stakers(
2462 origin: OriginFor<T>,
2463 validator_stash: T::AccountId,
2464 era: EraIndex,
2465 ) -> DispatchResultWithPostInfo {
2466 ensure_signed(origin)?;
2467
2468 Self::do_payout_stakers(validator_stash, era)
2469 }
2470
2471 #[pallet::call_index(19)]
2475 #[pallet::weight(T::WeightInfo::rebond(T::MaxUnlockingChunks::get() as u32))]
2476 pub fn rebond(
2477 origin: OriginFor<T>,
2478 #[pallet::compact] value: BalanceOf<T>,
2479 ) -> DispatchResultWithPostInfo {
2480 let controller = ensure_signed(origin)?;
2481 let ledger = Self::ledger(Controller(controller))?;
2482
2483 ensure!(!T::Filter::contains(&ledger.stash), Error::<T>::Restricted);
2484 ensure!(!ledger.unlocking.is_empty(), Error::<T>::NoUnlockChunk);
2485
2486 let initial_unlocking = ledger.unlocking.len() as u32;
2487 let (ledger, rebonded_value) = ledger.rebond(value);
2488 ensure!(ledger.active >= Self::min_chilled_bond(), Error::<T>::InsufficientBond);
2490
2491 Self::deposit_event(Event::<T>::Bonded {
2492 stash: ledger.stash.clone(),
2493 amount: rebonded_value,
2494 });
2495
2496 let stash = ledger.stash.clone();
2497 let final_unlocking = ledger.unlocking.len();
2498
2499 ledger.update()?;
2501 if T::VoterList::contains(&stash) {
2502 let _ = T::VoterList::on_update(&stash, Self::weight_of(&stash));
2503 }
2504
2505 let removed_chunks = 1u32 .saturating_add(initial_unlocking)
2507 .saturating_sub(final_unlocking as u32);
2508 Ok(Some(T::WeightInfo::rebond(removed_chunks)).into())
2509 }
2510
2511 #[pallet::call_index(20)]
2536 #[pallet::weight(T::WeightInfo::reap_stash())]
2537 pub fn reap_stash(
2538 origin: OriginFor<T>,
2539 stash: T::AccountId,
2540 _num_slashing_spans: u32,
2541 ) -> DispatchResultWithPostInfo {
2542 let _ = ensure_signed(origin)?;
2543
2544 ensure!(!Self::is_virtual_staker(&stash), Error::<T>::VirtualStakerNotAllowed);
2546
2547 let ed = asset::existential_deposit::<T>();
2548 let origin_balance = asset::total_balance::<T>(&stash);
2549 let ledger_total =
2550 Self::ledger(Stash(stash.clone())).map(|l| l.total).unwrap_or_default();
2551 let reapable = origin_balance < ed ||
2552 origin_balance.is_zero() ||
2553 ledger_total < ed ||
2554 ledger_total.is_zero();
2555 ensure!(reapable, Error::<T>::FundedTarget);
2556
2557 Self::kill_stash(&stash)?;
2559
2560 Ok(Pays::No.into())
2561 }
2562
2563 #[pallet::call_index(21)]
2575 #[pallet::weight(T::WeightInfo::kick(who.len() as u32))]
2576 pub fn kick(origin: OriginFor<T>, who: Vec<AccountIdLookupOf<T>>) -> DispatchResult {
2577 let controller = ensure_signed(origin)?;
2578 let ledger = Self::ledger(Controller(controller))?;
2579 let stash = &ledger.stash;
2580
2581 for nom_stash in who
2582 .into_iter()
2583 .map(T::Lookup::lookup)
2584 .collect::<Result<Vec<T::AccountId>, _>>()?
2585 .into_iter()
2586 {
2587 Nominators::<T>::mutate(&nom_stash, |maybe_nom| {
2588 if let Some(ref mut nom) = maybe_nom {
2589 if let Some(pos) = nom.targets.iter().position(|v| v == stash) {
2590 nom.targets.swap_remove(pos);
2591 Self::deposit_event(Event::<T>::Kicked {
2592 nominator: nom_stash.clone(),
2593 stash: stash.clone(),
2594 });
2595 }
2596 }
2597 });
2598 }
2599
2600 Ok(())
2601 }
2602
2603 #[pallet::call_index(22)]
2626 #[pallet::weight(
2627 T::WeightInfo::set_staking_configs_all_set()
2628 .max(T::WeightInfo::set_staking_configs_all_remove())
2629 )]
2630 pub fn set_staking_configs(
2631 origin: OriginFor<T>,
2632 min_nominator_bond: ConfigOp<BalanceOf<T>>,
2633 min_validator_bond: ConfigOp<BalanceOf<T>>,
2634 max_nominator_count: ConfigOp<u32>,
2635 max_validator_count: ConfigOp<u32>,
2636 chill_threshold: ConfigOp<Percent>,
2637 min_commission: ConfigOp<Perbill>,
2638 max_staked_rewards: ConfigOp<Percent>,
2639 are_nominators_slashable: ConfigOp<bool>,
2640 chill_inactive_threshold: ConfigOp<u32>,
2641 ) -> DispatchResult {
2642 ensure_root(origin)?;
2643
2644 if let ConfigOp::Set(threshold) = chill_inactive_threshold {
2645 ensure!(
2646 threshold > 1 && threshold <= T::HistoryDepth::get(),
2647 Error::<T>::InvalidChillInactiveThreshold
2648 );
2649 }
2650
2651 macro_rules! config_op_exp {
2652 ($storage:ty, $op:ident) => {
2653 match $op {
2654 ConfigOp::Noop => (),
2655 ConfigOp::Set(v) => <$storage>::put(v),
2656 ConfigOp::Remove => <$storage>::kill(),
2657 }
2658 };
2659 }
2660
2661 config_op_exp!(MinNominatorBond<T>, min_nominator_bond);
2662 config_op_exp!(MinValidatorBond<T>, min_validator_bond);
2663 config_op_exp!(MaxNominatorsCount<T>, max_nominator_count);
2664 config_op_exp!(MaxValidatorsCount<T>, max_validator_count);
2665 config_op_exp!(ChillThreshold<T>, chill_threshold);
2666 config_op_exp!(MinCommission<T>, min_commission);
2667 config_op_exp!(MaxStakedRewards<T>, max_staked_rewards);
2668 config_op_exp!(AreNominatorsSlashable<T>, are_nominators_slashable);
2669 config_op_exp!(ChillInactiveThreshold<T>, chill_inactive_threshold);
2670
2671 Ok(())
2672 }
2673 #[pallet::call_index(23)]
2700 #[pallet::weight(T::WeightInfo::chill_other())]
2701 pub fn chill_other(origin: OriginFor<T>, stash: T::AccountId) -> DispatchResult {
2702 let caller = ensure_signed(origin)?;
2704 let ledger = Self::ledger(Stash(stash.clone()))?;
2705 let controller = ledger
2706 .controller()
2707 .defensive_proof(
2708 "Ledger's controller field didn't exist. The controller should have been fetched using StakingLedger.",
2709 )
2710 .ok_or(Error::<T>::NotController)?;
2711
2712 if Nominators::<T>::contains_key(&stash) && Nominators::<T>::get(&stash).is_none() {
2729 Self::chill_stash(&stash);
2730 return Ok(());
2731 }
2732
2733 if caller != controller {
2734 let threshold = ChillThreshold::<T>::get().ok_or(Error::<T>::CannotChillOther)?;
2735 let min_active_bond = if Nominators::<T>::contains_key(&stash) {
2736 let max_nominator_count =
2737 MaxNominatorsCount::<T>::get().ok_or(Error::<T>::CannotChillOther)?;
2738 let current_nominator_count = Nominators::<T>::count();
2739 ensure!(
2740 threshold * max_nominator_count < current_nominator_count,
2741 Error::<T>::CannotChillOther
2742 );
2743 Self::min_nominator_bond()
2744 } else if Validators::<T>::contains_key(&stash) {
2745 let max_validator_count =
2746 MaxValidatorsCount::<T>::get().ok_or(Error::<T>::CannotChillOther)?;
2747 let current_validator_count = Validators::<T>::count();
2748 ensure!(
2749 threshold * max_validator_count < current_validator_count,
2750 Error::<T>::CannotChillOther
2751 );
2752 Self::min_validator_bond()
2753 } else {
2754 Zero::zero()
2755 };
2756
2757 ensure!(ledger.active < min_active_bond, Error::<T>::CannotChillOther);
2758 }
2759
2760 Self::chill_stash(&stash);
2761 Ok(())
2762 }
2763
2764 #[pallet::call_index(24)]
2769 #[pallet::weight(T::WeightInfo::force_apply_min_commission())]
2770 pub fn force_apply_min_commission(
2771 origin: OriginFor<T>,
2772 validator_stash: T::AccountId,
2773 ) -> DispatchResult {
2774 ensure_signed(origin)?;
2775 let min_commission = MinCommission::<T>::get();
2776 let max_commission = MaxCommission::<T>::get();
2777 Validators::<T>::try_mutate_exists(validator_stash, |maybe_prefs| {
2778 maybe_prefs
2779 .as_mut()
2780 .map(|prefs| {
2781 if prefs.commission < min_commission {
2782 prefs.commission = min_commission;
2783 }
2784 if prefs.commission > max_commission {
2785 prefs.commission = max_commission;
2786 }
2787 })
2788 .ok_or(Error::<T>::NotStash)
2789 })?;
2790 Ok(())
2791 }
2792
2793 #[pallet::call_index(25)]
2798 #[pallet::weight(T::WeightInfo::set_min_commission())]
2799 pub fn set_min_commission(origin: OriginFor<T>, new: Perbill) -> DispatchResult {
2800 T::AdminOrigin::ensure_origin(origin)?;
2801 ensure!(new <= MaxCommission::<T>::get(), Error::<T>::CommissionTooHigh);
2802 MinCommission::<T>::put(new);
2803 Ok(())
2804 }
2805
2806 #[pallet::call_index(26)]
2828 #[pallet::weight(T::WeightInfo::payout_stakers_alive_staked(T::MaxExposurePageSize::get()))]
2829 pub fn payout_stakers_by_page(
2830 origin: OriginFor<T>,
2831 validator_stash: T::AccountId,
2832 era: EraIndex,
2833 page: Page,
2834 ) -> DispatchResultWithPostInfo {
2835 ensure_signed(origin)?;
2836 Self::do_payout_stakers_by_page(validator_stash, era, page)
2837 }
2838
2839 #[pallet::call_index(27)]
2846 #[pallet::weight(T::WeightInfo::update_payee())]
2847 pub fn update_payee(
2848 origin: OriginFor<T>,
2849 controller: T::AccountId,
2850 ) -> DispatchResultWithPostInfo {
2851 let _ = ensure_signed(origin)?;
2852 let ledger = Self::ledger(StakingAccount::Controller(controller.clone()))?;
2853
2854 ensure!(
2855 (Payee::<T>::get(&ledger.stash) == {
2856 #[allow(deprecated)]
2857 Some(RewardDestination::Controller)
2858 }),
2859 Error::<T>::NotController
2860 );
2861
2862 let _ = ledger
2863 .set_payee(RewardDestination::Account(controller))
2864 .defensive_proof("ledger should have been previously retrieved from storage.")?;
2865
2866 Ok(Pays::No.into())
2867 }
2868
2869 #[pallet::call_index(28)]
2877 #[pallet::weight(T::WeightInfo::deprecate_controller_batch(controllers.len() as u32))]
2878 pub fn deprecate_controller_batch(
2879 origin: OriginFor<T>,
2880 controllers: BoundedVec<T::AccountId, T::MaxControllersInDeprecationBatch>,
2881 ) -> DispatchResultWithPostInfo {
2882 T::AdminOrigin::ensure_origin(origin)?;
2883
2884 let filtered_batch_with_ledger: Vec<_> = controllers
2886 .iter()
2887 .filter_map(|controller| {
2888 let ledger = Self::ledger(StakingAccount::Controller(controller.clone()));
2889 ledger.ok().map_or(None, |ledger| {
2890 let payee_deprecated = Payee::<T>::get(&ledger.stash) == {
2893 #[allow(deprecated)]
2894 Some(RewardDestination::Controller)
2895 };
2896
2897 if ledger.stash != *controller && !payee_deprecated {
2898 Some(ledger)
2899 } else {
2900 None
2901 }
2902 })
2903 })
2904 .collect();
2905
2906 let mut failures = 0;
2908 for ledger in filtered_batch_with_ledger {
2909 let _ = ledger.clone().set_controller_to_stash().map_err(|_| failures += 1);
2910 }
2911 Self::deposit_event(Event::<T>::ControllerBatchDeprecated { failures });
2912
2913 Ok(Some(T::WeightInfo::deprecate_controller_batch(controllers.len() as u32)).into())
2914 }
2915
2916 #[pallet::call_index(29)]
2928 #[pallet::weight(T::WeightInfo::restore_ledger())]
2929 pub fn restore_ledger(
2930 origin: OriginFor<T>,
2931 stash: T::AccountId,
2932 maybe_controller: Option<T::AccountId>,
2933 maybe_total: Option<BalanceOf<T>>,
2934 maybe_unlocking: Option<BoundedVec<UnlockChunk<BalanceOf<T>>, T::MaxUnlockingChunks>>,
2935 ) -> DispatchResult {
2936 T::AdminOrigin::ensure_origin(origin)?;
2937
2938 ensure!(!Self::is_virtual_staker(&stash), Error::<T>::VirtualStakerNotAllowed);
2940
2941 let current_lock = asset::staked::<T>(&stash);
2942 let stash_balance = asset::stakeable_balance::<T>(&stash);
2943
2944 let (new_controller, new_total) = match Self::inspect_bond_state(&stash) {
2945 Ok(LedgerIntegrityState::Corrupted) => {
2946 let new_controller = maybe_controller.unwrap_or(stash.clone());
2947
2948 let new_total = if let Some(total) = maybe_total {
2949 let new_total = total.min(stash_balance);
2950 asset::update_stake::<T>(&stash, new_total)?;
2952 new_total
2953 } else {
2954 current_lock
2955 };
2956
2957 Ok((new_controller, new_total))
2958 },
2959 Ok(LedgerIntegrityState::CorruptedKilled) => {
2960 if current_lock == Zero::zero() {
2961 ensure!(maybe_total.is_some(), Error::<T>::CannotRestoreLedger);
2965 Ok((
2966 stash.clone(),
2967 maybe_total.expect("total exists as per the check above; qed."),
2968 ))
2969 } else {
2970 Ok((stash.clone(), current_lock))
2971 }
2972 },
2973 Ok(LedgerIntegrityState::LockCorrupted) => {
2974 let new_total =
2977 maybe_total.ok_or(Error::<T>::CannotRestoreLedger)?.min(stash_balance);
2978 asset::update_stake::<T>(&stash, new_total)?;
2979
2980 Ok((stash.clone(), new_total))
2981 },
2982 Err(Error::<T>::BadState) => {
2983 asset::kill_stake::<T>(&stash)?;
2985 ensure!(
2986 Self::inspect_bond_state(&stash) == Err(Error::<T>::NotStash),
2987 Error::<T>::BadState
2988 );
2989
2990 return Ok(());
2991 },
2992 Ok(LedgerIntegrityState::Ok) | Err(_) => Err(Error::<T>::CannotRestoreLedger),
2993 }?;
2994
2995 Bonded::<T>::insert(&stash, &new_controller);
2997
2998 let mut ledger = StakingLedger::<T>::new(stash.clone(), new_total);
3000 ledger.controller = Some(new_controller);
3001 ledger.unlocking = maybe_unlocking.unwrap_or_default();
3002 ledger.update()?;
3003
3004 ensure!(
3005 Self::inspect_bond_state(&stash) == Ok(LedgerIntegrityState::Ok),
3006 Error::<T>::BadState
3007 );
3008 Ok(())
3009 }
3010
3011 #[pallet::call_index(30)]
3019 #[pallet::weight(T::WeightInfo::migrate_currency())]
3020 pub fn migrate_currency(
3021 origin: OriginFor<T>,
3022 stash: T::AccountId,
3023 ) -> DispatchResultWithPostInfo {
3024 let _ = ensure_signed(origin)?;
3025 Self::do_migrate_currency(&stash)?;
3026
3027 Ok(Pays::No.into())
3029 }
3030
3031 #[pallet::call_index(31)]
3066 #[pallet::weight(T::WeightInfo::apply_slash(T::MaxExposurePageSize::get()))]
3067 pub fn apply_slash(
3068 origin: OriginFor<T>,
3069 slash_era: EraIndex,
3070 slash_key: (T::AccountId, Perbill, u32),
3071 ) -> DispatchResultWithPostInfo {
3072 let _ = ensure_signed(origin)?;
3073 let active_era = ActiveEra::<T>::get().map(|a| a.index).unwrap_or_default();
3074 ensure!(slash_era <= active_era, Error::<T>::EraNotStarted);
3075
3076 ensure!(
3078 !Self::check_slash_cancelled(slash_era, &slash_key.0, slash_key.1),
3079 Error::<T>::CancelledSlash
3080 );
3081
3082 let unapplied_slash = UnappliedSlashes::<T>::take(&slash_era, &slash_key)
3083 .ok_or(Error::<T>::InvalidSlashRecord)?;
3084 slashing::apply_slash::<T>(unapplied_slash, Self::offence_era_of(slash_era));
3085
3086 Ok(Pays::No.into())
3087 }
3088
3089 #[pallet::call_index(32)]
3101 #[pallet::weight({
3103 let v = T::MaxValidatorSet::get();
3104 T::WeightInfo::prune_era_stakers_paged(v)
3105 .max(T::WeightInfo::prune_era_stakers_overview(v))
3106 .max(T::WeightInfo::prune_era_validator_prefs(v))
3107 .max(T::WeightInfo::prune_era_claimed_rewards(v))
3108 .max(T::WeightInfo::prune_era_validator_reward())
3109 .max(T::WeightInfo::prune_era_reward_points())
3110 .max(T::WeightInfo::prune_era_single_entry_cleanups())
3111 .max(T::WeightInfo::prune_era_validator_slash_in_era(v))
3112 .max(T::WeightInfo::prune_era_validator_incentive_weight(v))
3113 })]
3114 pub fn prune_era_step(origin: OriginFor<T>, era: EraIndex) -> DispatchResultWithPostInfo {
3115 let _ = ensure_signed(origin)?;
3116
3117 let active_era = crate::session_rotation::Rotator::<T>::active_era();
3119 let history_depth = T::HistoryDepth::get();
3120 let earliest_prunable_era = active_era.saturating_sub(history_depth).saturating_sub(1);
3121 ensure!(era <= earliest_prunable_era, Error::<T>::EraNotPrunable);
3122
3123 let actual_weight = Self::do_prune_era_step(era)?;
3124
3125 Ok(frame_support::dispatch::PostDispatchInfo {
3126 actual_weight: Some(actual_weight),
3127 pays_fee: frame_support::dispatch::Pays::No,
3128 })
3129 }
3130
3131 #[pallet::call_index(33)]
3135 #[pallet::weight(T::WeightInfo::set_max_commission())]
3136 pub fn set_max_commission(origin: OriginFor<T>, new: Perbill) -> DispatchResult {
3137 T::AdminOrigin::ensure_origin(origin)?;
3138 ensure!(new >= MinCommission::<T>::get(), Error::<T>::CommissionTooLow);
3139 MaxCommission::<T>::put(new);
3140 Ok(())
3141 }
3142
3143 #[pallet::call_index(34)]
3149 #[pallet::weight(T::WeightInfo::set_validator_self_stake_incentive_config())]
3150 pub fn set_validator_self_stake_incentive_config(
3151 origin: OriginFor<T>,
3152 optimum_self_stake: ConfigOp<BalanceOf<T>>,
3153 hard_cap_self_stake: ConfigOp<BalanceOf<T>>,
3154 self_stake_slope_factor: ConfigOp<Perbill>,
3155 ) -> DispatchResult {
3156 T::AdminOrigin::ensure_origin(origin)?;
3157
3158 let new_optimum = match optimum_self_stake {
3159 ConfigOp::Noop => OptimumSelfStake::<T>::get(),
3160 ConfigOp::Set(v) => v,
3161 ConfigOp::Remove => BalanceOf::<T>::zero(),
3162 };
3163
3164 let new_cap = match hard_cap_self_stake {
3165 ConfigOp::Noop => HardCapSelfStake::<T>::get(),
3166 ConfigOp::Set(v) => v,
3167 ConfigOp::Remove => BalanceOf::<T>::zero(),
3168 };
3169
3170 ensure!(new_optimum <= new_cap, Error::<T>::OptimumGreaterThanCap);
3171
3172 let has_changes = !matches!(
3173 (&optimum_self_stake, &hard_cap_self_stake, &self_stake_slope_factor),
3174 (ConfigOp::Noop, ConfigOp::Noop, ConfigOp::Noop)
3175 );
3176
3177 macro_rules! config_op_exp {
3178 ($storage:ty, $op:ident) => {
3179 match $op {
3180 ConfigOp::Noop => (),
3181 ConfigOp::Set(v) => <$storage>::put(v),
3182 ConfigOp::Remove => <$storage>::kill(),
3183 }
3184 };
3185 }
3186
3187 config_op_exp!(OptimumSelfStake<T>, optimum_self_stake);
3188 config_op_exp!(HardCapSelfStake<T>, hard_cap_self_stake);
3189 config_op_exp!(SelfStakeSlopeFactor<T>, self_stake_slope_factor);
3190
3191 if has_changes {
3192 Self::deposit_event(Event::<T>::ValidatorIncentiveConfigSet {
3193 optimum_self_stake: OptimumSelfStake::<T>::get(),
3194 hard_cap_self_stake: HardCapSelfStake::<T>::get(),
3195 slope_factor: SelfStakeSlopeFactor::<T>::get(),
3196 });
3197 }
3198
3199 Ok(())
3200 }
3201
3202 #[pallet::call_index(35)]
3217 #[pallet::weight(T::WeightInfo::chill_inactive(proof.len() as _))]
3218 pub fn chill_inactive(
3219 origin: OriginFor<T>,
3220 stash: T::AccountId,
3221 proof: BoundedVec<EraIndex, T::HistoryDepth>,
3222 ) -> DispatchResultWithPostInfo {
3223 ensure_signed(origin)?;
3224
3225 let threshold = ChillInactiveThreshold::<T>::get();
3226 ensure!(
3227 proof.len() as EraIndex == threshold,
3228 Error::<T>::InvalidInactivityProof(InvalidInactivityProofError::InvalidLen)
3229 );
3230 ensure!(
3231 proof.is_sorted_by(|a, b| a < b),
3232 Error::<T>::InvalidInactivityProof(InvalidInactivityProofError::NotSorted)
3233 );
3234
3235 let active_era = Rotator::<T>::active_era();
3238 let oldest_allowed_era = active_era.saturating_sub(T::HistoryDepth::get());
3239 let oldest_proof_era = proof.first().copied().unwrap_or(EraIndex::MAX);
3240 let most_recent_proof_era = proof.last().copied().unwrap_or(EraIndex::MAX);
3241 ensure!(
3242 oldest_proof_era >= oldest_allowed_era && most_recent_proof_era < active_era,
3243 Error::<T>::InvalidInactivityProof(InvalidInactivityProofError::InvalidEra)
3244 );
3245
3246 for era in proof {
3247 ensure!(
3248 Eras::<T>::was_validator_exposed(era, &stash),
3249 Error::<T>::InvalidInactivityProof(
3250 InvalidInactivityProofError::ValidatorNotExposed
3251 )
3252 );
3253
3254 let points = Eras::<T>::get_reward_points_for_validator(era, &stash);
3255
3256 ensure!(
3257 T::IsValidatorInactive::is_inactive(era, &stash, points),
3258 Error::<T>::InvalidInactivityProof(
3259 InvalidInactivityProofError::ValidatorActive
3260 )
3261 );
3262 }
3263
3264 if Self::do_remove_validator(&stash) {
3265 Self::deposit_event(Event::<T>::Chilled { stash });
3266
3267 Ok(Pays::No.into())
3268 } else {
3269 Err(Error::<T>::BadTarget.into())
3270 }
3271 }
3272 }
3273
3274 #[pallet::view_functions]
3275 impl<T: Config> Pallet<T> {
3276 pub fn pot_account(pot: crate::RewardPot) -> T::AccountId {
3278 <T::RewardPots as crate::PotAccountProvider<T::AccountId>>::pot_account(pot)
3279 }
3280
3281 pub fn pot_balance(pot: crate::RewardPot) -> BalanceOf<T> {
3283 let account =
3284 <T::RewardPots as crate::PotAccountProvider<T::AccountId>>::pot_account(pot);
3285 <T::Currency as frame_support::traits::fungible::Inspect<T::AccountId>>::balance(
3286 &account,
3287 )
3288 }
3289
3290 pub fn era_reward_allocation(
3294 era: EraIndex,
3295 ) -> crate::reward::EraRewardAllocation<BalanceOf<T>> {
3296 crate::reward::EraRewardAllocation {
3297 staker_rewards: ErasValidatorReward::<T>::get(era).unwrap_or_else(Zero::zero),
3298 validator_incentive: ErasValidatorIncentiveBudget::<T>::get(era),
3299 }
3300 }
3301 }
3302}