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