1use alloc::vec::Vec;
21use codec::Codec;
22use frame_election_provider_support::{
23 ElectionProvider, ElectionProviderBase, SortedListProvider, VoteWeight,
24};
25use frame_support::{
26 pallet_prelude::*,
27 traits::{
28 fungible::{
29 hold::{Balanced as FunHoldBalanced, Mutate as FunHoldMutate},
30 Mutate as FunMutate,
31 },
32 Contains, Defensive, DefensiveSaturating, EnsureOrigin, EstimateNextNewSession, Get,
33 InspectLockableCurrency, Nothing, OnUnbalanced, UnixTime,
34 },
35 weights::Weight,
36 BoundedVec,
37};
38use frame_system::{ensure_root, ensure_signed, pallet_prelude::*};
39use sp_runtime::{
40 traits::{SaturatedConversion, StaticLookup, Zero},
41 ArithmeticError, Perbill, Percent,
42};
43
44use sp_staking::{
45 EraIndex, Page, SessionIndex,
46 StakingAccount::{self, Controller, Stash},
47 StakingInterface,
48};
49
50mod impls;
51
52pub use impls::*;
53
54use crate::{
55 asset, slashing, weights::WeightInfo, AccountIdLookupOf, ActiveEraInfo, BalanceOf, EraPayout,
56 EraRewardPoints, Exposure, ExposurePage, Forcing, LedgerIntegrityState, MaxNominationsOf,
57 NegativeImbalanceOf, Nominations, NominationsQuota, PositiveImbalanceOf, RewardDestination,
58 SessionInterface, StakingLedger, UnappliedSlash, UnlockChunk, ValidatorPrefs,
59};
60
61pub(crate) const SPECULATIVE_NUM_SPANS: u32 = 32;
65
66#[frame_support::pallet]
67pub mod pallet {
68 use super::*;
69 use codec::HasCompact;
70 use frame_election_provider_support::ElectionDataProvider;
71
72 use crate::{BenchmarkingConfig, PagedExposureMetadata};
73
74 const STORAGE_VERSION: StorageVersion = StorageVersion::new(16);
76
77 #[pallet::pallet]
78 #[pallet::storage_version(STORAGE_VERSION)]
79 pub struct Pallet<T>(_);
80
81 #[derive(TypeInfo, Debug, Clone, Encode, Decode, DecodeWithMemTracking, PartialEq)]
83 pub enum ConfigOp<T: Default + Codec> {
84 Noop,
86 Set(T),
88 Remove,
90 }
91
92 #[pallet::config(with_default)]
93 pub trait Config: frame_system::Config {
94 #[pallet::no_default]
96 type OldCurrency: InspectLockableCurrency<
97 Self::AccountId,
98 Moment = BlockNumberFor<Self>,
99 Balance = Self::CurrencyBalance,
100 >;
101
102 #[pallet::no_default]
104 type Currency: FunHoldMutate<
105 Self::AccountId,
106 Reason = Self::RuntimeHoldReason,
107 Balance = Self::CurrencyBalance,
108 > + FunMutate<Self::AccountId, Balance = Self::CurrencyBalance>
109 + FunHoldBalanced<Self::AccountId, Balance = Self::CurrencyBalance>;
110
111 #[pallet::no_default_bounds]
113 type RuntimeHoldReason: From<HoldReason>;
114
115 type CurrencyBalance: sp_runtime::traits::AtLeast32BitUnsigned
118 + codec::FullCodec
119 + DecodeWithMemTracking
120 + HasCompact<Type: DecodeWithMemTracking>
121 + Copy
122 + MaybeSerializeDeserialize
123 + core::fmt::Debug
124 + Default
125 + From<u64>
126 + TypeInfo
127 + Send
128 + Sync
129 + MaxEncodedLen;
130 #[pallet::no_default]
135 type UnixTime: UnixTime;
136
137 #[pallet::no_default_bounds]
144 type CurrencyToVote: sp_staking::currency_to_vote::CurrencyToVote<BalanceOf<Self>>;
145
146 #[pallet::no_default]
148 type ElectionProvider: ElectionProvider<
149 AccountId = Self::AccountId,
150 BlockNumber = BlockNumberFor<Self>,
151 DataProvider = Pallet<Self>,
153 >;
154 #[pallet::no_default]
156 type GenesisElectionProvider: ElectionProvider<
157 AccountId = Self::AccountId,
158 BlockNumber = BlockNumberFor<Self>,
159 DataProvider = Pallet<Self>,
160 >;
161
162 #[pallet::no_default_bounds]
164 type NominationsQuota: NominationsQuota<BalanceOf<Self>>;
165
166 #[pallet::constant]
187 type HistoryDepth: Get<u32>;
188
189 #[pallet::no_default_bounds]
192 type RewardRemainder: OnUnbalanced<NegativeImbalanceOf<Self>>;
193
194 #[pallet::no_default_bounds]
196 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
197
198 #[pallet::no_default_bounds]
200 type Slash: OnUnbalanced<NegativeImbalanceOf<Self>>;
201
202 #[pallet::no_default_bounds]
206 type Reward: OnUnbalanced<PositiveImbalanceOf<Self>>;
207
208 #[pallet::constant]
210 type SessionsPerEra: Get<SessionIndex>;
211
212 #[pallet::constant]
214 type BondingDuration: Get<EraIndex>;
215
216 #[pallet::constant]
221 type SlashDeferDuration: Get<EraIndex>;
222
223 #[pallet::no_default]
227 type AdminOrigin: EnsureOrigin<Self::RuntimeOrigin>;
228
229 type SessionInterface: SessionInterface<Self::AccountId>;
231
232 #[pallet::no_default]
235 type EraPayout: EraPayout<BalanceOf<Self>>;
236
237 #[pallet::no_default_bounds]
240 type NextNewSession: EstimateNextNewSession<BlockNumberFor<Self>>;
241
242 #[pallet::constant]
254 type MaxExposurePageSize: Get<u32>;
255
256 #[pallet::no_default]
268 type VoterList: SortedListProvider<Self::AccountId, Score = VoteWeight>;
269
270 #[pallet::no_default]
291 type TargetList: SortedListProvider<Self::AccountId, Score = BalanceOf<Self>>;
292
293 #[pallet::constant]
304 type MaxUnlockingChunks: Get<u32>;
305
306 type MaxControllersInDeprecationBatch: Get<u32>;
308
309 #[pallet::no_default_bounds]
314 type EventListeners: sp_staking::OnStakingUpdate<Self::AccountId, BalanceOf<Self>>;
315
316 #[pallet::no_default_bounds]
317 type Filter: Contains<Self::AccountId>;
322
323 #[cfg(feature = "std")]
325 type BenchmarkingConfig: BenchmarkingConfig;
326
327 #[cfg(not(feature = "std"))]
328 #[pallet::no_default]
329 type BenchmarkingConfig: BenchmarkingConfig;
330
331 type WeightInfo: WeightInfo;
333 }
334
335 #[pallet::composite_enum]
337 pub enum HoldReason {
338 #[codec(index = 0)]
340 Staking,
341 }
342
343 pub mod config_preludes {
345 use super::*;
346 use frame_support::{derive_impl, parameter_types, traits::ConstU32};
347 pub struct TestDefaultConfig;
348
349 #[derive_impl(frame_system::config_preludes::TestDefaultConfig, no_aggregated_types)]
350 impl frame_system::DefaultConfig for TestDefaultConfig {}
351
352 parameter_types! {
353 pub const SessionsPerEra: SessionIndex = 3;
354 pub const BondingDuration: EraIndex = 3;
355 }
356
357 #[frame_support::register_default_impl(TestDefaultConfig)]
358 impl DefaultConfig for TestDefaultConfig {
359 #[inject_runtime_type]
360 type RuntimeEvent = ();
361 #[inject_runtime_type]
362 type RuntimeHoldReason = ();
363 type CurrencyBalance = u128;
364 type CurrencyToVote = ();
365 type NominationsQuota = crate::FixedNominationsQuota<16>;
366 type HistoryDepth = ConstU32<84>;
367 type RewardRemainder = ();
368 type Slash = ();
369 type Reward = ();
370 type SessionsPerEra = SessionsPerEra;
371 type BondingDuration = BondingDuration;
372 type SlashDeferDuration = ();
373 type SessionInterface = ();
374 type NextNewSession = ();
375 type MaxExposurePageSize = ConstU32<64>;
376 type MaxUnlockingChunks = ConstU32<32>;
377 type MaxControllersInDeprecationBatch = ConstU32<100>;
378 type EventListeners = ();
379 type Filter = Nothing;
380 #[cfg(feature = "std")]
381 type BenchmarkingConfig = crate::TestBenchmarkingConfig;
382 type WeightInfo = ();
383 }
384 }
385
386 #[pallet::storage]
388 pub type ValidatorCount<T> = StorageValue<_, u32, ValueQuery>;
389
390 #[pallet::storage]
392 pub type MinimumValidatorCount<T> = StorageValue<_, u32, ValueQuery>;
393
394 #[pallet::storage]
398 #[pallet::unbounded]
399 pub type Invulnerables<T: Config> = StorageValue<_, Vec<T::AccountId>, ValueQuery>;
400
401 #[pallet::storage]
405 pub type Bonded<T: Config> = StorageMap<_, Twox64Concat, T::AccountId, T::AccountId>;
406
407 #[pallet::storage]
409 pub type MinNominatorBond<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;
410
411 #[pallet::storage]
413 pub type MinValidatorBond<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;
414
415 #[pallet::storage]
417 pub type MinimumActiveStake<T> = StorageValue<_, BalanceOf<T>, ValueQuery>;
418
419 #[pallet::storage]
423 pub type MinCommission<T: Config> = StorageValue<_, Perbill, ValueQuery>;
424
425 #[pallet::storage]
430 pub type Ledger<T: Config> = StorageMap<_, Blake2_128Concat, T::AccountId, StakingLedger<T>>;
431
432 #[pallet::storage]
436 pub type Payee<T: Config> =
437 StorageMap<_, Twox64Concat, T::AccountId, RewardDestination<T::AccountId>, OptionQuery>;
438
439 #[pallet::storage]
443 pub type Validators<T: Config> =
444 CountedStorageMap<_, Twox64Concat, T::AccountId, ValidatorPrefs, ValueQuery>;
445
446 #[pallet::storage]
450 pub type MaxValidatorsCount<T> = StorageValue<_, u32, OptionQuery>;
451
452 #[pallet::storage]
472 pub type Nominators<T: Config> =
473 CountedStorageMap<_, Twox64Concat, T::AccountId, Nominations<T>>;
474
475 #[pallet::storage]
482 pub type VirtualStakers<T: Config> = CountedStorageMap<_, Twox64Concat, T::AccountId, ()>;
483
484 #[pallet::storage]
488 pub type MaxNominatorsCount<T> = StorageValue<_, u32, OptionQuery>;
489
490 #[pallet::storage]
495 pub type CurrentEra<T> = StorageValue<_, EraIndex>;
496
497 #[pallet::storage]
502 pub type ActiveEra<T> = StorageValue<_, ActiveEraInfo>;
503
504 #[pallet::storage]
509 pub type ErasStartSessionIndex<T> = StorageMap<_, Twox64Concat, EraIndex, SessionIndex>;
510
511 #[pallet::storage]
520 #[pallet::unbounded]
521 pub type ErasStakers<T: Config> = StorageDoubleMap<
522 _,
523 Twox64Concat,
524 EraIndex,
525 Twox64Concat,
526 T::AccountId,
527 Exposure<T::AccountId, BalanceOf<T>>,
528 ValueQuery,
529 >;
530
531 #[pallet::storage]
544 pub type ErasStakersOverview<T: Config> = StorageDoubleMap<
545 _,
546 Twox64Concat,
547 EraIndex,
548 Twox64Concat,
549 T::AccountId,
550 PagedExposureMetadata<BalanceOf<T>>,
551 OptionQuery,
552 >;
553
554 #[pallet::storage]
571 #[pallet::unbounded]
572 pub type ErasStakersClipped<T: Config> = StorageDoubleMap<
573 _,
574 Twox64Concat,
575 EraIndex,
576 Twox64Concat,
577 T::AccountId,
578 Exposure<T::AccountId, BalanceOf<T>>,
579 ValueQuery,
580 >;
581
582 #[pallet::storage]
589 #[pallet::unbounded]
590 pub type ErasStakersPaged<T: Config> = StorageNMap<
591 _,
592 (
593 NMapKey<Twox64Concat, EraIndex>,
594 NMapKey<Twox64Concat, T::AccountId>,
595 NMapKey<Twox64Concat, Page>,
596 ),
597 ExposurePage<T::AccountId, BalanceOf<T>>,
598 OptionQuery,
599 >;
600
601 #[pallet::storage]
608 #[pallet::unbounded]
609 pub type ClaimedRewards<T: Config> = StorageDoubleMap<
610 _,
611 Twox64Concat,
612 EraIndex,
613 Twox64Concat,
614 T::AccountId,
615 Vec<Page>,
616 ValueQuery,
617 >;
618
619 #[pallet::storage]
626 pub type ErasValidatorPrefs<T: Config> = StorageDoubleMap<
627 _,
628 Twox64Concat,
629 EraIndex,
630 Twox64Concat,
631 T::AccountId,
632 ValidatorPrefs,
633 ValueQuery,
634 >;
635
636 #[pallet::storage]
640 pub type ErasValidatorReward<T: Config> = StorageMap<_, Twox64Concat, EraIndex, BalanceOf<T>>;
641
642 #[pallet::storage]
645 #[pallet::unbounded]
646 pub type ErasRewardPoints<T: Config> =
647 StorageMap<_, Twox64Concat, EraIndex, EraRewardPoints<T::AccountId>, ValueQuery>;
648
649 #[pallet::storage]
652 pub type ErasTotalStake<T: Config> =
653 StorageMap<_, Twox64Concat, EraIndex, BalanceOf<T>, ValueQuery>;
654
655 #[pallet::storage]
657 pub type ForceEra<T> = StorageValue<_, Forcing, ValueQuery>;
658
659 #[pallet::storage]
663 pub type MaxStakedRewards<T> = StorageValue<_, Percent, OptionQuery>;
664
665 #[pallet::storage]
669 pub type SlashRewardFraction<T> = StorageValue<_, Perbill, ValueQuery>;
670
671 #[pallet::storage]
674 pub type CanceledSlashPayout<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;
675
676 #[pallet::storage]
678 #[pallet::unbounded]
679 pub type UnappliedSlashes<T: Config> = StorageMap<
680 _,
681 Twox64Concat,
682 EraIndex,
683 Vec<UnappliedSlash<T::AccountId, BalanceOf<T>>>,
684 ValueQuery,
685 >;
686
687 #[pallet::storage]
692 #[pallet::unbounded]
693 pub(crate) type BondedEras<T: Config> =
694 StorageValue<_, Vec<(EraIndex, SessionIndex)>, ValueQuery>;
695
696 #[pallet::storage]
699 pub(crate) type ValidatorSlashInEra<T: Config> = StorageDoubleMap<
700 _,
701 Twox64Concat,
702 EraIndex,
703 Twox64Concat,
704 T::AccountId,
705 (Perbill, BalanceOf<T>),
706 >;
707
708 #[pallet::storage]
710 pub(crate) type NominatorSlashInEra<T: Config> =
711 StorageDoubleMap<_, Twox64Concat, EraIndex, Twox64Concat, T::AccountId, BalanceOf<T>>;
712
713 #[pallet::storage]
715 #[pallet::unbounded]
716 pub type SlashingSpans<T: Config> =
717 StorageMap<_, Twox64Concat, T::AccountId, slashing::SlashingSpans>;
718
719 #[pallet::storage]
722 pub(crate) type SpanSlash<T: Config> = StorageMap<
723 _,
724 Twox64Concat,
725 (T::AccountId, slashing::SpanIndex),
726 slashing::SpanRecord<BalanceOf<T>>,
727 ValueQuery,
728 >;
729
730 #[pallet::storage]
734 pub type CurrentPlannedSession<T> = StorageValue<_, SessionIndex, ValueQuery>;
735
736 #[pallet::storage]
740 pub(crate) type ChillThreshold<T: Config> = StorageValue<_, Percent, OptionQuery>;
741
742 #[pallet::genesis_config]
743 #[derive(frame_support::DefaultNoBound)]
744 pub struct GenesisConfig<T: Config> {
745 pub validator_count: u32,
746 pub minimum_validator_count: u32,
747 pub invulnerables: Vec<T::AccountId>,
748 pub force_era: Forcing,
749 pub slash_reward_fraction: Perbill,
750 pub canceled_payout: BalanceOf<T>,
751 pub stakers:
752 Vec<(T::AccountId, T::AccountId, BalanceOf<T>, crate::StakerStatus<T::AccountId>)>,
753 pub min_nominator_bond: BalanceOf<T>,
754 pub min_validator_bond: BalanceOf<T>,
755 pub max_validator_count: Option<u32>,
756 pub max_nominator_count: Option<u32>,
757 }
758
759 #[pallet::genesis_build]
760 impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
761 fn build(&self) {
762 ValidatorCount::<T>::put(self.validator_count);
763 MinimumValidatorCount::<T>::put(self.minimum_validator_count);
764 Invulnerables::<T>::put(&self.invulnerables);
765 ForceEra::<T>::put(self.force_era);
766 CanceledSlashPayout::<T>::put(self.canceled_payout);
767 SlashRewardFraction::<T>::put(self.slash_reward_fraction);
768 MinNominatorBond::<T>::put(self.min_nominator_bond);
769 MinValidatorBond::<T>::put(self.min_validator_bond);
770 if let Some(x) = self.max_validator_count {
771 MaxValidatorsCount::<T>::put(x);
772 }
773 if let Some(x) = self.max_nominator_count {
774 MaxNominatorsCount::<T>::put(x);
775 }
776
777 for &(ref stash, _, balance, ref status) in &self.stakers {
778 crate::log!(
779 trace,
780 "inserting genesis staker: {:?} => {:?} => {:?}",
781 stash,
782 balance,
783 status
784 );
785 assert!(
786 asset::free_to_stake::<T>(stash) >= balance,
787 "Stash does not have enough balance to bond."
788 );
789 frame_support::assert_ok!(<Pallet<T>>::bond(
790 T::RuntimeOrigin::from(Some(stash.clone()).into()),
791 balance,
792 RewardDestination::Staked,
793 ));
794 frame_support::assert_ok!(match status {
795 crate::StakerStatus::Validator => <Pallet<T>>::validate(
796 T::RuntimeOrigin::from(Some(stash.clone()).into()),
797 Default::default(),
798 ),
799 crate::StakerStatus::Nominator(votes) => <Pallet<T>>::nominate(
800 T::RuntimeOrigin::from(Some(stash.clone()).into()),
801 votes.iter().map(|l| T::Lookup::unlookup(l.clone())).collect(),
802 ),
803 _ => Ok(()),
804 });
805 assert!(
806 ValidatorCount::<T>::get() <=
807 <T::ElectionProvider as ElectionProviderBase>::MaxWinners::get()
808 );
809 }
810
811 assert_eq!(
813 T::VoterList::count(),
814 Nominators::<T>::count() + Validators::<T>::count(),
815 "not all genesis stakers were inserted into sorted list provider, something is wrong."
816 );
817 }
818 }
819
820 #[pallet::event]
821 #[pallet::generate_deposit(pub(crate) fn deposit_event)]
822 pub enum Event<T: Config> {
823 EraPaid { era_index: EraIndex, validator_payout: BalanceOf<T>, remainder: BalanceOf<T> },
826 Rewarded {
828 stash: T::AccountId,
829 dest: RewardDestination<T::AccountId>,
830 amount: BalanceOf<T>,
831 },
832 Slashed { staker: T::AccountId, amount: BalanceOf<T> },
834 SlashReported { validator: T::AccountId, fraction: Perbill, slash_era: EraIndex },
837 OldSlashingReportDiscarded { session_index: SessionIndex },
840 StakersElected,
842 Bonded { stash: T::AccountId, amount: BalanceOf<T> },
847 Unbonded { stash: T::AccountId, amount: BalanceOf<T> },
849 Withdrawn { stash: T::AccountId, amount: BalanceOf<T> },
852 Kicked { nominator: T::AccountId, stash: T::AccountId },
854 StakingElectionFailed,
856 Chilled { stash: T::AccountId },
858 PayoutStarted {
860 era_index: EraIndex,
861 validator_stash: T::AccountId,
862 page: Page,
863 next: Option<Page>,
864 },
865 ValidatorPrefsSet { stash: T::AccountId, prefs: ValidatorPrefs },
867 SnapshotVotersSizeExceeded { size: u32 },
869 SnapshotTargetsSizeExceeded { size: u32 },
871 ForceEra { mode: Forcing },
873 ControllerBatchDeprecated { failures: u32 },
875 CurrencyMigrated { stash: T::AccountId, force_withdraw: BalanceOf<T> },
878 }
879
880 #[pallet::error]
881 #[derive(PartialEq)]
882 pub enum Error<T> {
883 NotController,
885 NotStash,
887 AlreadyBonded,
889 AlreadyPaired,
891 EmptyTargets,
893 DuplicateIndex,
895 InvalidSlashIndex,
897 InsufficientBond,
901 NoMoreChunks,
903 NoUnlockChunk,
905 FundedTarget,
907 InvalidEraToReward,
909 InvalidNumberOfNominations,
911 NotSortedAndUnique,
913 AlreadyClaimed,
915 InvalidPage,
917 IncorrectHistoryDepth,
919 IncorrectSlashingSpans,
921 BadState,
923 TooManyTargets,
925 BadTarget,
927 CannotChillOther,
929 TooManyNominators,
932 TooManyValidators,
935 CommissionTooLow,
937 BoundNotMet,
939 ControllerDeprecated,
941 CannotRestoreLedger,
943 RewardDestinationRestricted,
945 NotEnoughFunds,
947 VirtualStakerNotAllowed,
949 CannotReapStash,
951 AlreadyMigrated,
953 Restricted,
956 }
957
958 #[pallet::hooks]
959 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
960 fn on_initialize(_now: BlockNumberFor<T>) -> Weight {
961 T::DbWeight::get().reads(1)
963 }
964
965 fn on_finalize(_n: BlockNumberFor<T>) {
966 if let Some(mut active_era) = ActiveEra::<T>::get() {
968 if active_era.start.is_none() {
969 let now_as_millis_u64 = T::UnixTime::now().as_millis().saturated_into::<u64>();
970 active_era.start = Some(now_as_millis_u64);
971 ActiveEra::<T>::put(active_era);
974 }
975 }
976 }
978
979 fn integrity_test() {
980 assert_eq!(
982 MaxNominationsOf::<T>::get(),
983 <Self as ElectionDataProvider>::MaxVotesPerVoter::get()
984 );
985 assert!(!MaxNominationsOf::<T>::get().is_zero());
987
988 assert!(
990 <T::ElectionProvider as ElectionProviderBase>::MaxWinners::get() ==
991 <T::GenesisElectionProvider as ElectionProviderBase>::MaxWinners::get()
992 );
993
994 assert!(
995 T::SlashDeferDuration::get() < T::BondingDuration::get() || T::BondingDuration::get() == 0,
996 "As per documentation, slash defer duration ({}) should be less than bonding duration ({}).",
997 T::SlashDeferDuration::get(),
998 T::BondingDuration::get(),
999 )
1000 }
1001
1002 #[cfg(feature = "try-runtime")]
1003 fn try_state(n: BlockNumberFor<T>) -> Result<(), sp_runtime::TryRuntimeError> {
1004 Self::do_try_state(n)
1005 }
1006 }
1007
1008 impl<T: Config> Pallet<T> {
1009 pub fn validator_count() -> u32 {
1011 ValidatorCount::<T>::get()
1012 }
1013
1014 pub fn minimum_validator_count() -> u32 {
1016 MinimumValidatorCount::<T>::get()
1017 }
1018
1019 pub fn invulnerables() -> Vec<T::AccountId> {
1021 Invulnerables::<T>::get()
1022 }
1023
1024 pub fn validators<EncodeLikeAccountId>(account_id: EncodeLikeAccountId) -> ValidatorPrefs
1026 where
1027 EncodeLikeAccountId: codec::EncodeLike<T::AccountId>,
1028 {
1029 Validators::<T>::get(account_id)
1030 }
1031
1032 pub fn nominators<EncodeLikeAccountId>(
1034 account_id: EncodeLikeAccountId,
1035 ) -> Option<Nominations<T>>
1036 where
1037 EncodeLikeAccountId: codec::EncodeLike<T::AccountId>,
1038 {
1039 Nominators::<T>::get(account_id)
1040 }
1041
1042 pub fn current_era() -> Option<EraIndex> {
1044 CurrentEra::<T>::get()
1045 }
1046
1047 pub fn active_era() -> Option<ActiveEraInfo> {
1049 ActiveEra::<T>::get()
1050 }
1051
1052 pub fn eras_start_session_index<EncodeLikeEraIndex>(
1055 era_index: EncodeLikeEraIndex,
1056 ) -> Option<SessionIndex>
1057 where
1058 EncodeLikeEraIndex: codec::EncodeLike<EraIndex>,
1059 {
1060 ErasStartSessionIndex::<T>::get(era_index)
1061 }
1062
1063 pub fn eras_stakers_clipped<EncodeLikeEraIndex, EncodeLikeAccountId>(
1065 era_index: EncodeLikeEraIndex,
1066 account_id: EncodeLikeAccountId,
1067 ) -> Exposure<T::AccountId, BalanceOf<T>>
1068 where
1069 EncodeLikeEraIndex: codec::EncodeLike<EraIndex>,
1070 EncodeLikeAccountId: codec::EncodeLike<T::AccountId>,
1071 {
1072 ErasStakersClipped::<T>::get(era_index, account_id)
1073 }
1074
1075 pub fn claimed_rewards<EncodeLikeEraIndex, EncodeLikeAccountId>(
1077 era_index: EncodeLikeEraIndex,
1078 account_id: EncodeLikeAccountId,
1079 ) -> Vec<Page>
1080 where
1081 EncodeLikeEraIndex: codec::EncodeLike<EraIndex>,
1082 EncodeLikeAccountId: codec::EncodeLike<T::AccountId>,
1083 {
1084 ClaimedRewards::<T>::get(era_index, account_id)
1085 }
1086
1087 pub fn eras_validator_prefs<EncodeLikeEraIndex, EncodeLikeAccountId>(
1089 era_index: EncodeLikeEraIndex,
1090 account_id: EncodeLikeAccountId,
1091 ) -> ValidatorPrefs
1092 where
1093 EncodeLikeEraIndex: codec::EncodeLike<EraIndex>,
1094 EncodeLikeAccountId: codec::EncodeLike<T::AccountId>,
1095 {
1096 ErasValidatorPrefs::<T>::get(era_index, account_id)
1097 }
1098
1099 pub fn eras_validator_reward<EncodeLikeEraIndex>(
1101 era_index: EncodeLikeEraIndex,
1102 ) -> Option<BalanceOf<T>>
1103 where
1104 EncodeLikeEraIndex: codec::EncodeLike<EraIndex>,
1105 {
1106 ErasValidatorReward::<T>::get(era_index)
1107 }
1108
1109 pub fn eras_reward_points<EncodeLikeEraIndex>(
1111 era_index: EncodeLikeEraIndex,
1112 ) -> EraRewardPoints<T::AccountId>
1113 where
1114 EncodeLikeEraIndex: codec::EncodeLike<EraIndex>,
1115 {
1116 ErasRewardPoints::<T>::get(era_index)
1117 }
1118
1119 pub fn eras_total_stake<EncodeLikeEraIndex>(era_index: EncodeLikeEraIndex) -> BalanceOf<T>
1121 where
1122 EncodeLikeEraIndex: codec::EncodeLike<EraIndex>,
1123 {
1124 ErasTotalStake::<T>::get(era_index)
1125 }
1126
1127 pub fn force_era() -> Forcing {
1129 ForceEra::<T>::get()
1130 }
1131
1132 pub fn slash_reward_fraction() -> Perbill {
1134 SlashRewardFraction::<T>::get()
1135 }
1136
1137 pub fn canceled_payout() -> BalanceOf<T> {
1139 CanceledSlashPayout::<T>::get()
1140 }
1141
1142 pub fn slashing_spans<EncodeLikeAccountId>(
1144 account_id: EncodeLikeAccountId,
1145 ) -> Option<slashing::SlashingSpans>
1146 where
1147 EncodeLikeAccountId: codec::EncodeLike<T::AccountId>,
1148 {
1149 SlashingSpans::<T>::get(account_id)
1150 }
1151
1152 pub fn current_planned_session() -> SessionIndex {
1154 CurrentPlannedSession::<T>::get()
1155 }
1156 }
1157
1158 #[pallet::call]
1159 impl<T: Config> Pallet<T> {
1160 #[pallet::call_index(0)]
1177 #[pallet::weight(T::WeightInfo::bond())]
1178 pub fn bond(
1179 origin: OriginFor<T>,
1180 #[pallet::compact] value: BalanceOf<T>,
1181 payee: RewardDestination<T::AccountId>,
1182 ) -> DispatchResult {
1183 let stash = ensure_signed(origin)?;
1184
1185 ensure!(!T::Filter::contains(&stash), Error::<T>::Restricted);
1186
1187 if StakingLedger::<T>::is_bonded(StakingAccount::Stash(stash.clone())) {
1188 return Err(Error::<T>::AlreadyBonded.into())
1189 }
1190
1191 if StakingLedger::<T>::is_bonded(StakingAccount::Controller(stash.clone())) {
1193 return Err(Error::<T>::AlreadyPaired.into())
1194 }
1195
1196 if value < asset::existential_deposit::<T>() {
1198 return Err(Error::<T>::InsufficientBond.into())
1199 }
1200
1201 let stash_balance = asset::free_to_stake::<T>(&stash);
1202 let value = value.min(stash_balance);
1203 Self::deposit_event(Event::<T>::Bonded { stash: stash.clone(), amount: value });
1204 let ledger = StakingLedger::<T>::new(stash.clone(), value);
1205
1206 ledger.bond(payee)?;
1209
1210 Ok(())
1211 }
1212
1213 #[pallet::call_index(1)]
1228 #[pallet::weight(T::WeightInfo::bond_extra())]
1229 pub fn bond_extra(
1230 origin: OriginFor<T>,
1231 #[pallet::compact] max_additional: BalanceOf<T>,
1232 ) -> DispatchResult {
1233 let stash = ensure_signed(origin)?;
1234 ensure!(!T::Filter::contains(&stash), Error::<T>::Restricted);
1235 Self::do_bond_extra(&stash, max_additional)
1236 }
1237
1238 #[pallet::call_index(2)]
1258 #[pallet::weight(
1259 T::WeightInfo::withdraw_unbonded_kill(SPECULATIVE_NUM_SPANS).saturating_add(T::WeightInfo::unbond()))
1260 ]
1261 pub fn unbond(
1262 origin: OriginFor<T>,
1263 #[pallet::compact] value: BalanceOf<T>,
1264 ) -> DispatchResultWithPostInfo {
1265 let controller = ensure_signed(origin)?;
1266 let unlocking =
1267 Self::ledger(Controller(controller.clone())).map(|l| l.unlocking.len())?;
1268
1269 let maybe_withdraw_weight = {
1272 if unlocking == T::MaxUnlockingChunks::get() as usize {
1273 let real_num_slashing_spans =
1274 SlashingSpans::<T>::get(&controller).map_or(0, |s| s.iter().count());
1275 Some(Self::do_withdraw_unbonded(&controller, real_num_slashing_spans as u32)?)
1276 } else {
1277 None
1278 }
1279 };
1280
1281 let mut ledger = Self::ledger(Controller(controller))?;
1284 let mut value = value.min(ledger.active);
1285 let stash = ledger.stash.clone();
1286
1287 ensure!(
1288 ledger.unlocking.len() < T::MaxUnlockingChunks::get() as usize,
1289 Error::<T>::NoMoreChunks,
1290 );
1291
1292 if !value.is_zero() {
1293 ledger.active -= value;
1294
1295 if ledger.active < asset::existential_deposit::<T>() {
1297 value += ledger.active;
1298 ledger.active = Zero::zero();
1299 }
1300
1301 let min_active_bond = if Nominators::<T>::contains_key(&stash) {
1302 MinNominatorBond::<T>::get()
1303 } else if Validators::<T>::contains_key(&stash) {
1304 MinValidatorBond::<T>::get()
1305 } else {
1306 Zero::zero()
1307 };
1308
1309 ensure!(ledger.active >= min_active_bond, Error::<T>::InsufficientBond);
1312
1313 let era = CurrentEra::<T>::get()
1315 .unwrap_or(0)
1316 .defensive_saturating_add(T::BondingDuration::get());
1317 if let Some(chunk) = ledger.unlocking.last_mut().filter(|chunk| chunk.era == era) {
1318 chunk.value = chunk.value.defensive_saturating_add(value)
1322 } else {
1323 ledger
1324 .unlocking
1325 .try_push(UnlockChunk { value, era })
1326 .map_err(|_| Error::<T>::NoMoreChunks)?;
1327 };
1328 ledger.update()?;
1330
1331 if T::VoterList::contains(&stash) {
1333 let _ = T::VoterList::on_update(&stash, Self::weight_of(&stash)).defensive();
1334 }
1335
1336 Self::deposit_event(Event::<T>::Unbonded { stash, amount: value });
1337 }
1338
1339 let actual_weight = if let Some(withdraw_weight) = maybe_withdraw_weight {
1340 Some(T::WeightInfo::unbond().saturating_add(withdraw_weight))
1341 } else {
1342 Some(T::WeightInfo::unbond())
1343 };
1344
1345 Ok(actual_weight.into())
1346 }
1347
1348 #[pallet::call_index(3)]
1372 #[pallet::weight(T::WeightInfo::withdraw_unbonded_kill(*num_slashing_spans))]
1373 pub fn withdraw_unbonded(
1374 origin: OriginFor<T>,
1375 num_slashing_spans: u32,
1376 ) -> DispatchResultWithPostInfo {
1377 let controller = ensure_signed(origin)?;
1378
1379 let actual_weight = Self::do_withdraw_unbonded(&controller, num_slashing_spans)?;
1380 Ok(Some(actual_weight).into())
1381 }
1382
1383 #[pallet::call_index(4)]
1389 #[pallet::weight(T::WeightInfo::validate())]
1390 pub fn validate(origin: OriginFor<T>, prefs: ValidatorPrefs) -> DispatchResult {
1391 let controller = ensure_signed(origin)?;
1392
1393 let ledger = Self::ledger(Controller(controller))?;
1394
1395 ensure!(ledger.active >= MinValidatorBond::<T>::get(), Error::<T>::InsufficientBond);
1396 let stash = &ledger.stash;
1397
1398 ensure!(prefs.commission >= MinCommission::<T>::get(), Error::<T>::CommissionTooLow);
1400
1401 if !Validators::<T>::contains_key(stash) {
1403 if let Some(max_validators) = MaxValidatorsCount::<T>::get() {
1407 ensure!(
1408 Validators::<T>::count() < max_validators,
1409 Error::<T>::TooManyValidators
1410 );
1411 }
1412 }
1413
1414 Self::do_remove_nominator(stash);
1415 Self::do_add_validator(stash, prefs.clone());
1416 Self::deposit_event(Event::<T>::ValidatorPrefsSet { stash: ledger.stash, prefs });
1417
1418 Ok(())
1419 }
1420
1421 #[pallet::call_index(5)]
1432 #[pallet::weight(T::WeightInfo::nominate(targets.len() as u32))]
1433 pub fn nominate(
1434 origin: OriginFor<T>,
1435 targets: Vec<AccountIdLookupOf<T>>,
1436 ) -> DispatchResult {
1437 let controller = ensure_signed(origin)?;
1438
1439 let ledger = Self::ledger(StakingAccount::Controller(controller.clone()))?;
1440
1441 ensure!(ledger.active >= MinNominatorBond::<T>::get(), Error::<T>::InsufficientBond);
1442 let stash = &ledger.stash;
1443
1444 if !Nominators::<T>::contains_key(stash) {
1446 if let Some(max_nominators) = MaxNominatorsCount::<T>::get() {
1450 ensure!(
1451 Nominators::<T>::count() < max_nominators,
1452 Error::<T>::TooManyNominators
1453 );
1454 }
1455 }
1456
1457 ensure!(!targets.is_empty(), Error::<T>::EmptyTargets);
1458 ensure!(
1459 targets.len() <= T::NominationsQuota::get_quota(ledger.active) as usize,
1460 Error::<T>::TooManyTargets
1461 );
1462
1463 let old = Nominators::<T>::get(stash).map_or_else(Vec::new, |x| x.targets.into_inner());
1464
1465 let targets: BoundedVec<_, _> = targets
1466 .into_iter()
1467 .map(|t| T::Lookup::lookup(t).map_err(DispatchError::from))
1468 .map(|n| {
1469 n.and_then(|n| {
1470 if old.contains(&n) || !Validators::<T>::get(&n).blocked {
1471 Ok(n)
1472 } else {
1473 Err(Error::<T>::BadTarget.into())
1474 }
1475 })
1476 })
1477 .collect::<Result<Vec<_>, _>>()?
1478 .try_into()
1479 .map_err(|_| Error::<T>::TooManyNominators)?;
1480
1481 let nominations = Nominations {
1482 targets,
1483 submitted_in: CurrentEra::<T>::get().unwrap_or(0),
1485 suppressed: false,
1486 };
1487
1488 Self::do_remove_validator(stash);
1489 Self::do_add_nominator(stash, nominations);
1490 Ok(())
1491 }
1492
1493 #[pallet::call_index(6)]
1504 #[pallet::weight(T::WeightInfo::chill())]
1505 pub fn chill(origin: OriginFor<T>) -> DispatchResult {
1506 let controller = ensure_signed(origin)?;
1507
1508 let ledger = Self::ledger(StakingAccount::Controller(controller))?;
1509
1510 Self::chill_stash(&ledger.stash);
1511 Ok(())
1512 }
1513
1514 #[pallet::call_index(7)]
1527 #[pallet::weight(T::WeightInfo::set_payee())]
1528 pub fn set_payee(
1529 origin: OriginFor<T>,
1530 payee: RewardDestination<T::AccountId>,
1531 ) -> DispatchResult {
1532 let controller = ensure_signed(origin)?;
1533 let ledger = Self::ledger(Controller(controller.clone()))?;
1534
1535 ensure!(
1536 (payee != {
1537 #[allow(deprecated)]
1538 RewardDestination::Controller
1539 }),
1540 Error::<T>::ControllerDeprecated
1541 );
1542
1543 let _ = ledger
1544 .set_payee(payee)
1545 .defensive_proof("ledger was retrieved from storage, thus it's bonded; qed.")?;
1546
1547 Ok(())
1548 }
1549
1550 #[pallet::call_index(8)]
1565 #[pallet::weight(T::WeightInfo::set_controller())]
1566 pub fn set_controller(origin: OriginFor<T>) -> DispatchResult {
1567 let stash = ensure_signed(origin)?;
1568
1569 Self::ledger(StakingAccount::Stash(stash.clone())).map(|ledger| {
1570 let controller = ledger.controller()
1571 .defensive_proof("Ledger's controller field didn't exist. The controller should have been fetched using StakingLedger.")
1572 .ok_or(Error::<T>::NotController)?;
1573
1574 if controller == stash {
1575 return Err(Error::<T>::AlreadyPaired.into())
1577 }
1578
1579 let _ = ledger.set_controller_to_stash()?;
1580 Ok(())
1581 })?
1582 }
1583
1584 #[pallet::call_index(9)]
1591 #[pallet::weight(T::WeightInfo::set_validator_count())]
1592 pub fn set_validator_count(
1593 origin: OriginFor<T>,
1594 #[pallet::compact] new: u32,
1595 ) -> DispatchResult {
1596 ensure_root(origin)?;
1597 ensure!(
1600 new <= <T::ElectionProvider as ElectionProviderBase>::MaxWinners::get(),
1601 Error::<T>::TooManyValidators
1602 );
1603 ValidatorCount::<T>::put(new);
1604 Ok(())
1605 }
1606
1607 #[pallet::call_index(10)]
1615 #[pallet::weight(T::WeightInfo::set_validator_count())]
1616 pub fn increase_validator_count(
1617 origin: OriginFor<T>,
1618 #[pallet::compact] additional: u32,
1619 ) -> DispatchResult {
1620 ensure_root(origin)?;
1621 let old = ValidatorCount::<T>::get();
1622 let new = old.checked_add(additional).ok_or(ArithmeticError::Overflow)?;
1623 ensure!(
1624 new <= <T::ElectionProvider as ElectionProviderBase>::MaxWinners::get(),
1625 Error::<T>::TooManyValidators
1626 );
1627
1628 ValidatorCount::<T>::put(new);
1629 Ok(())
1630 }
1631
1632 #[pallet::call_index(11)]
1640 #[pallet::weight(T::WeightInfo::set_validator_count())]
1641 pub fn scale_validator_count(origin: OriginFor<T>, factor: Percent) -> DispatchResult {
1642 ensure_root(origin)?;
1643 let old = ValidatorCount::<T>::get();
1644 let new = old.checked_add(factor.mul_floor(old)).ok_or(ArithmeticError::Overflow)?;
1645
1646 ensure!(
1647 new <= <T::ElectionProvider as ElectionProviderBase>::MaxWinners::get(),
1648 Error::<T>::TooManyValidators
1649 );
1650
1651 ValidatorCount::<T>::put(new);
1652 Ok(())
1653 }
1654
1655 #[pallet::call_index(12)]
1669 #[pallet::weight(T::WeightInfo::force_no_eras())]
1670 pub fn force_no_eras(origin: OriginFor<T>) -> DispatchResult {
1671 ensure_root(origin)?;
1672 Self::set_force_era(Forcing::ForceNone);
1673 Ok(())
1674 }
1675
1676 #[pallet::call_index(13)]
1691 #[pallet::weight(T::WeightInfo::force_new_era())]
1692 pub fn force_new_era(origin: OriginFor<T>) -> DispatchResult {
1693 ensure_root(origin)?;
1694 Self::set_force_era(Forcing::ForceNew);
1695 Ok(())
1696 }
1697
1698 #[pallet::call_index(14)]
1702 #[pallet::weight(T::WeightInfo::set_invulnerables(invulnerables.len() as u32))]
1703 pub fn set_invulnerables(
1704 origin: OriginFor<T>,
1705 invulnerables: Vec<T::AccountId>,
1706 ) -> DispatchResult {
1707 ensure_root(origin)?;
1708 <Invulnerables<T>>::put(invulnerables);
1709 Ok(())
1710 }
1711
1712 #[pallet::call_index(15)]
1721 #[pallet::weight(T::WeightInfo::force_unstake(*num_slashing_spans))]
1722 pub fn force_unstake(
1723 origin: OriginFor<T>,
1724 stash: T::AccountId,
1725 num_slashing_spans: u32,
1726 ) -> DispatchResult {
1727 ensure_root(origin)?;
1728
1729 Self::kill_stash(&stash, num_slashing_spans)?;
1731
1732 Ok(())
1733 }
1734
1735 #[pallet::call_index(16)]
1745 #[pallet::weight(T::WeightInfo::force_new_era_always())]
1746 pub fn force_new_era_always(origin: OriginFor<T>) -> DispatchResult {
1747 ensure_root(origin)?;
1748 Self::set_force_era(Forcing::ForceAlways);
1749 Ok(())
1750 }
1751
1752 #[pallet::call_index(17)]
1758 #[pallet::weight(T::WeightInfo::cancel_deferred_slash(slash_indices.len() as u32))]
1759 pub fn cancel_deferred_slash(
1760 origin: OriginFor<T>,
1761 era: EraIndex,
1762 slash_indices: Vec<u32>,
1763 ) -> DispatchResult {
1764 T::AdminOrigin::ensure_origin(origin)?;
1765
1766 ensure!(!slash_indices.is_empty(), Error::<T>::EmptyTargets);
1767 ensure!(is_sorted_and_unique(&slash_indices), Error::<T>::NotSortedAndUnique);
1768
1769 let mut unapplied = UnappliedSlashes::<T>::get(&era);
1770 let last_item = slash_indices[slash_indices.len() - 1];
1771 ensure!((last_item as usize) < unapplied.len(), Error::<T>::InvalidSlashIndex);
1772
1773 for (removed, index) in slash_indices.into_iter().enumerate() {
1774 let index = (index as usize) - removed;
1775 unapplied.remove(index);
1776 }
1777
1778 UnappliedSlashes::<T>::insert(&era, &unapplied);
1779 Ok(())
1780 }
1781
1782 #[pallet::call_index(18)]
1796 #[pallet::weight(T::WeightInfo::payout_stakers_alive_staked(T::MaxExposurePageSize::get()))]
1797 pub fn payout_stakers(
1798 origin: OriginFor<T>,
1799 validator_stash: T::AccountId,
1800 era: EraIndex,
1801 ) -> DispatchResultWithPostInfo {
1802 ensure_signed(origin)?;
1803 Self::do_payout_stakers(validator_stash, era)
1804 }
1805
1806 #[pallet::call_index(19)]
1814 #[pallet::weight(T::WeightInfo::rebond(T::MaxUnlockingChunks::get() as u32))]
1815 pub fn rebond(
1816 origin: OriginFor<T>,
1817 #[pallet::compact] value: BalanceOf<T>,
1818 ) -> DispatchResultWithPostInfo {
1819 let controller = ensure_signed(origin)?;
1820 let ledger = Self::ledger(Controller(controller))?;
1821
1822 ensure!(!T::Filter::contains(&ledger.stash), Error::<T>::Restricted);
1823 ensure!(!ledger.unlocking.is_empty(), Error::<T>::NoUnlockChunk);
1824
1825 let initial_unlocking = ledger.unlocking.len() as u32;
1826 let (ledger, rebonded_value) = ledger.rebond(value);
1827 ensure!(
1829 ledger.active >= asset::existential_deposit::<T>(),
1830 Error::<T>::InsufficientBond
1831 );
1832
1833 Self::deposit_event(Event::<T>::Bonded {
1834 stash: ledger.stash.clone(),
1835 amount: rebonded_value,
1836 });
1837
1838 let stash = ledger.stash.clone();
1839 let final_unlocking = ledger.unlocking.len();
1840
1841 ledger.update()?;
1843 if T::VoterList::contains(&stash) {
1844 let _ = T::VoterList::on_update(&stash, Self::weight_of(&stash)).defensive();
1845 }
1846
1847 let removed_chunks = 1u32 .saturating_add(initial_unlocking)
1849 .saturating_sub(final_unlocking as u32);
1850 Ok(Some(T::WeightInfo::rebond(removed_chunks)).into())
1851 }
1852
1853 #[pallet::call_index(20)]
1872 #[pallet::weight(T::WeightInfo::reap_stash(*num_slashing_spans))]
1873 pub fn reap_stash(
1874 origin: OriginFor<T>,
1875 stash: T::AccountId,
1876 num_slashing_spans: u32,
1877 ) -> DispatchResultWithPostInfo {
1878 let _ = ensure_signed(origin)?;
1879
1880 ensure!(!Self::is_virtual_staker(&stash), Error::<T>::VirtualStakerNotAllowed);
1882
1883 let ed = asset::existential_deposit::<T>();
1884 let origin_balance = asset::total_balance::<T>(&stash);
1885 let ledger_total =
1886 Self::ledger(Stash(stash.clone())).map(|l| l.total).unwrap_or_default();
1887 let reapable = origin_balance < ed ||
1888 origin_balance.is_zero() ||
1889 ledger_total < ed ||
1890 ledger_total.is_zero();
1891 ensure!(reapable, Error::<T>::FundedTarget);
1892
1893 Self::kill_stash(&stash, num_slashing_spans)?;
1895
1896 Ok(Pays::No.into())
1897 }
1898
1899 #[pallet::call_index(21)]
1911 #[pallet::weight(T::WeightInfo::kick(who.len() as u32))]
1912 pub fn kick(origin: OriginFor<T>, who: Vec<AccountIdLookupOf<T>>) -> DispatchResult {
1913 let controller = ensure_signed(origin)?;
1914 let ledger = Self::ledger(Controller(controller))?;
1915 let stash = &ledger.stash;
1916
1917 for nom_stash in who
1918 .into_iter()
1919 .map(T::Lookup::lookup)
1920 .collect::<Result<Vec<T::AccountId>, _>>()?
1921 .into_iter()
1922 {
1923 Nominators::<T>::mutate(&nom_stash, |maybe_nom| {
1924 if let Some(ref mut nom) = maybe_nom {
1925 if let Some(pos) = nom.targets.iter().position(|v| v == stash) {
1926 nom.targets.swap_remove(pos);
1927 Self::deposit_event(Event::<T>::Kicked {
1928 nominator: nom_stash.clone(),
1929 stash: stash.clone(),
1930 });
1931 }
1932 }
1933 });
1934 }
1935
1936 Ok(())
1937 }
1938
1939 #[pallet::call_index(22)]
1959 #[pallet::weight(
1960 T::WeightInfo::set_staking_configs_all_set()
1961 .max(T::WeightInfo::set_staking_configs_all_remove())
1962 )]
1963 pub fn set_staking_configs(
1964 origin: OriginFor<T>,
1965 min_nominator_bond: ConfigOp<BalanceOf<T>>,
1966 min_validator_bond: ConfigOp<BalanceOf<T>>,
1967 max_nominator_count: ConfigOp<u32>,
1968 max_validator_count: ConfigOp<u32>,
1969 chill_threshold: ConfigOp<Percent>,
1970 min_commission: ConfigOp<Perbill>,
1971 max_staked_rewards: ConfigOp<Percent>,
1972 ) -> DispatchResult {
1973 ensure_root(origin)?;
1974
1975 macro_rules! config_op_exp {
1976 ($storage:ty, $op:ident) => {
1977 match $op {
1978 ConfigOp::Noop => (),
1979 ConfigOp::Set(v) => <$storage>::put(v),
1980 ConfigOp::Remove => <$storage>::kill(),
1981 }
1982 };
1983 }
1984
1985 config_op_exp!(MinNominatorBond<T>, min_nominator_bond);
1986 config_op_exp!(MinValidatorBond<T>, min_validator_bond);
1987 config_op_exp!(MaxNominatorsCount<T>, max_nominator_count);
1988 config_op_exp!(MaxValidatorsCount<T>, max_validator_count);
1989 config_op_exp!(ChillThreshold<T>, chill_threshold);
1990 config_op_exp!(MinCommission<T>, min_commission);
1991 config_op_exp!(MaxStakedRewards<T>, max_staked_rewards);
1992 Ok(())
1993 }
1994 #[pallet::call_index(23)]
2021 #[pallet::weight(T::WeightInfo::chill_other())]
2022 pub fn chill_other(origin: OriginFor<T>, stash: T::AccountId) -> DispatchResult {
2023 let caller = ensure_signed(origin)?;
2025 let ledger = Self::ledger(Stash(stash.clone()))?;
2026 let controller = ledger
2027 .controller()
2028 .defensive_proof(
2029 "Ledger's controller field didn't exist. The controller should have been fetched using StakingLedger.",
2030 )
2031 .ok_or(Error::<T>::NotController)?;
2032
2033 if Nominators::<T>::contains_key(&stash) && Nominators::<T>::get(&stash).is_none() {
2050 Self::chill_stash(&stash);
2051 return Ok(())
2052 }
2053
2054 if caller != controller {
2055 let threshold = ChillThreshold::<T>::get().ok_or(Error::<T>::CannotChillOther)?;
2056 let min_active_bond = if Nominators::<T>::contains_key(&stash) {
2057 let max_nominator_count =
2058 MaxNominatorsCount::<T>::get().ok_or(Error::<T>::CannotChillOther)?;
2059 let current_nominator_count = Nominators::<T>::count();
2060 ensure!(
2061 threshold * max_nominator_count < current_nominator_count,
2062 Error::<T>::CannotChillOther
2063 );
2064 MinNominatorBond::<T>::get()
2065 } else if Validators::<T>::contains_key(&stash) {
2066 let max_validator_count =
2067 MaxValidatorsCount::<T>::get().ok_or(Error::<T>::CannotChillOther)?;
2068 let current_validator_count = Validators::<T>::count();
2069 ensure!(
2070 threshold * max_validator_count < current_validator_count,
2071 Error::<T>::CannotChillOther
2072 );
2073 MinValidatorBond::<T>::get()
2074 } else {
2075 Zero::zero()
2076 };
2077
2078 ensure!(ledger.active < min_active_bond, Error::<T>::CannotChillOther);
2079 }
2080
2081 Self::chill_stash(&stash);
2082 Ok(())
2083 }
2084
2085 #[pallet::call_index(24)]
2089 #[pallet::weight(T::WeightInfo::force_apply_min_commission())]
2090 pub fn force_apply_min_commission(
2091 origin: OriginFor<T>,
2092 validator_stash: T::AccountId,
2093 ) -> DispatchResult {
2094 ensure_signed(origin)?;
2095 let min_commission = MinCommission::<T>::get();
2096 Validators::<T>::try_mutate_exists(validator_stash, |maybe_prefs| {
2097 maybe_prefs
2098 .as_mut()
2099 .map(|prefs| {
2100 (prefs.commission < min_commission)
2101 .then(|| prefs.commission = min_commission)
2102 })
2103 .ok_or(Error::<T>::NotStash)
2104 })?;
2105 Ok(())
2106 }
2107
2108 #[pallet::call_index(25)]
2113 #[pallet::weight(T::WeightInfo::set_min_commission())]
2114 pub fn set_min_commission(origin: OriginFor<T>, new: Perbill) -> DispatchResult {
2115 T::AdminOrigin::ensure_origin(origin)?;
2116 MinCommission::<T>::put(new);
2117 Ok(())
2118 }
2119
2120 #[pallet::call_index(26)]
2138 #[pallet::weight(T::WeightInfo::payout_stakers_alive_staked(T::MaxExposurePageSize::get()))]
2139 pub fn payout_stakers_by_page(
2140 origin: OriginFor<T>,
2141 validator_stash: T::AccountId,
2142 era: EraIndex,
2143 page: Page,
2144 ) -> DispatchResultWithPostInfo {
2145 ensure_signed(origin)?;
2146 Self::do_payout_stakers_by_page(validator_stash, era, page)
2147 }
2148
2149 #[pallet::call_index(27)]
2156 #[pallet::weight(T::WeightInfo::update_payee())]
2157 pub fn update_payee(
2158 origin: OriginFor<T>,
2159 controller: T::AccountId,
2160 ) -> DispatchResultWithPostInfo {
2161 let _ = ensure_signed(origin)?;
2162 let ledger = Self::ledger(StakingAccount::Controller(controller.clone()))?;
2163
2164 ensure!(
2165 (Payee::<T>::get(&ledger.stash) == {
2166 #[allow(deprecated)]
2167 Some(RewardDestination::Controller)
2168 }),
2169 Error::<T>::NotController
2170 );
2171
2172 let _ = ledger
2173 .set_payee(RewardDestination::Account(controller))
2174 .defensive_proof("ledger should have been previously retrieved from storage.")?;
2175
2176 Ok(Pays::No.into())
2177 }
2178
2179 #[pallet::call_index(28)]
2187 #[pallet::weight(T::WeightInfo::deprecate_controller_batch(controllers.len() as u32))]
2188 pub fn deprecate_controller_batch(
2189 origin: OriginFor<T>,
2190 controllers: BoundedVec<T::AccountId, T::MaxControllersInDeprecationBatch>,
2191 ) -> DispatchResultWithPostInfo {
2192 T::AdminOrigin::ensure_origin(origin)?;
2193
2194 let filtered_batch_with_ledger: Vec<_> = controllers
2196 .iter()
2197 .filter_map(|controller| {
2198 let ledger = Self::ledger(StakingAccount::Controller(controller.clone()));
2199 ledger.ok().map_or(None, |ledger| {
2200 let payee_deprecated = Payee::<T>::get(&ledger.stash) == {
2203 #[allow(deprecated)]
2204 Some(RewardDestination::Controller)
2205 };
2206
2207 if ledger.stash != *controller && !payee_deprecated {
2208 Some(ledger)
2209 } else {
2210 None
2211 }
2212 })
2213 })
2214 .collect();
2215
2216 let mut failures = 0;
2218 for ledger in filtered_batch_with_ledger {
2219 let _ = ledger.clone().set_controller_to_stash().map_err(|_| failures += 1);
2220 }
2221 Self::deposit_event(Event::<T>::ControllerBatchDeprecated { failures });
2222
2223 Ok(Some(T::WeightInfo::deprecate_controller_batch(controllers.len() as u32)).into())
2224 }
2225
2226 #[pallet::call_index(29)]
2238 #[pallet::weight(T::WeightInfo::restore_ledger())]
2239 pub fn restore_ledger(
2240 origin: OriginFor<T>,
2241 stash: T::AccountId,
2242 maybe_controller: Option<T::AccountId>,
2243 maybe_total: Option<BalanceOf<T>>,
2244 maybe_unlocking: Option<BoundedVec<UnlockChunk<BalanceOf<T>>, T::MaxUnlockingChunks>>,
2245 ) -> DispatchResult {
2246 T::AdminOrigin::ensure_origin(origin)?;
2247
2248 ensure!(!Self::is_virtual_staker(&stash), Error::<T>::VirtualStakerNotAllowed);
2250
2251 let current_lock = asset::staked::<T>(&stash);
2252 let stash_balance = asset::stakeable_balance::<T>(&stash);
2253
2254 let (new_controller, new_total) = match Self::inspect_bond_state(&stash) {
2255 Ok(LedgerIntegrityState::Corrupted) => {
2256 let new_controller = maybe_controller.unwrap_or(stash.clone());
2257
2258 let new_total = if let Some(total) = maybe_total {
2259 let new_total = total.min(stash_balance);
2260 asset::update_stake::<T>(&stash, new_total)?;
2262 new_total
2263 } else {
2264 current_lock
2265 };
2266
2267 Ok((new_controller, new_total))
2268 },
2269 Ok(LedgerIntegrityState::CorruptedKilled) => {
2270 if current_lock == Zero::zero() {
2271 ensure!(maybe_total.is_some(), Error::<T>::CannotRestoreLedger);
2275 Ok((
2276 stash.clone(),
2277 maybe_total.expect("total exists as per the check above; qed."),
2278 ))
2279 } else {
2280 Ok((stash.clone(), current_lock))
2281 }
2282 },
2283 Ok(LedgerIntegrityState::LockCorrupted) => {
2284 let new_total =
2287 maybe_total.ok_or(Error::<T>::CannotRestoreLedger)?.min(stash_balance);
2288 asset::update_stake::<T>(&stash, new_total)?;
2289
2290 Ok((stash.clone(), new_total))
2291 },
2292 Err(Error::<T>::BadState) => {
2293 asset::kill_stake::<T>(&stash)?;
2295 ensure!(
2296 Self::inspect_bond_state(&stash) == Err(Error::<T>::NotStash),
2297 Error::<T>::BadState
2298 );
2299
2300 return Ok(());
2301 },
2302 Ok(LedgerIntegrityState::Ok) | Err(_) => Err(Error::<T>::CannotRestoreLedger),
2303 }?;
2304
2305 Bonded::<T>::insert(&stash, &new_controller);
2307
2308 let mut ledger = StakingLedger::<T>::new(stash.clone(), new_total);
2310 ledger.controller = Some(new_controller);
2311 ledger.unlocking = maybe_unlocking.unwrap_or_default();
2312 ledger.update()?;
2313
2314 ensure!(
2315 Self::inspect_bond_state(&stash) == Ok(LedgerIntegrityState::Ok),
2316 Error::<T>::BadState
2317 );
2318 Ok(())
2319 }
2320
2321 #[pallet::call_index(30)]
2329 #[pallet::weight(T::WeightInfo::migrate_currency())]
2330 pub fn migrate_currency(
2331 origin: OriginFor<T>,
2332 stash: T::AccountId,
2333 ) -> DispatchResultWithPostInfo {
2334 let _ = ensure_signed(origin)?;
2335 Self::do_migrate_currency(&stash)?;
2336
2337 Ok(Pays::No.into())
2339 }
2340
2341 #[pallet::call_index(33)]
2363 #[pallet::weight(T::WeightInfo::manual_slash())]
2364 pub fn manual_slash(
2365 origin: OriginFor<T>,
2366 validator_stash: T::AccountId,
2367 era: EraIndex,
2368 slash_fraction: Perbill,
2369 ) -> DispatchResult {
2370 T::AdminOrigin::ensure_origin(origin)?;
2371
2372 let current_era = CurrentEra::<T>::get().ok_or(Error::<T>::InvalidEraToReward)?;
2374 let history_depth = T::HistoryDepth::get();
2375 ensure!(
2376 era <= current_era && era >= current_era.saturating_sub(history_depth),
2377 Error::<T>::InvalidEraToReward
2378 );
2379
2380 let offence_details = sp_staking::offence::OffenceDetails {
2381 offender: validator_stash.clone(),
2382 reporters: Vec::new(),
2383 };
2384
2385 let session_index =
2387 ErasStartSessionIndex::<T>::get(era).ok_or(Error::<T>::InvalidEraToReward)?;
2388
2389 let _ = Self::on_offence(
2391 core::iter::once(offence_details),
2392 &[slash_fraction],
2393 session_index,
2394 );
2395
2396 Ok(())
2397 }
2398 }
2399}
2400
2401fn is_sorted_and_unique(list: &[u32]) -> bool {
2403 list.windows(2).all(|w| w[0] < w[1])
2404}