1use crate::{
21 asset, slashing, weights::WeightInfo, AccountIdLookupOf, ActiveEraInfo, BalanceOf, EraPayout,
22 EraRewardPoints, ExposurePage, Forcing, LedgerIntegrityState, MaxNominationsOf,
23 NegativeImbalanceOf, Nominations, NominationsQuota, PositiveImbalanceOf, RewardDestination,
24 StakingLedger, UnappliedSlash, UnlockChunk, ValidatorPrefs,
25};
26use alloc::{format, vec::Vec};
27use codec::Codec;
28use frame_election_provider_support::{ElectionProvider, SortedListProvider, VoteWeight};
29use frame_support::{
30 assert_ok,
31 pallet_prelude::*,
32 traits::{
33 fungible::{
34 hold::{Balanced as FunHoldBalanced, Mutate as FunHoldMutate},
35 Mutate, Mutate as FunMutate,
36 },
37 Contains, Defensive, DefensiveSaturating, EnsureOrigin, Get, InspectLockableCurrency,
38 Nothing, OnUnbalanced,
39 },
40 weights::Weight,
41 BoundedBTreeSet, BoundedVec,
42};
43use frame_system::{ensure_root, ensure_signed, pallet_prelude::*};
44pub use impls::*;
45use rand::seq::SliceRandom;
46use rand_chacha::{
47 rand_core::{RngCore, SeedableRng},
48 ChaChaRng,
49};
50use sp_core::{sr25519::Pair as SrPair, Pair};
51use sp_runtime::{
52 traits::{StaticLookup, Zero},
53 ArithmeticError, Perbill, Percent,
54};
55use sp_staking::{
56 EraIndex, Page, SessionIndex,
57 StakingAccount::{self, Controller, Stash},
58 StakingInterface,
59};
60
61mod impls;
62
63#[frame_support::pallet]
64pub mod pallet {
65 use core::ops::Deref;
66
67 use super::*;
68 use crate::{session_rotation, PagedExposureMetadata, SnapshotStatus};
69 use codec::HasCompact;
70 use frame_election_provider_support::{ElectionDataProvider, PageIndex};
71 use frame_support::DefaultNoBound;
72
73 const STORAGE_VERSION: StorageVersion = StorageVersion::new(17);
75
76 #[pallet::pallet]
77 #[pallet::storage_version(STORAGE_VERSION)]
78 pub struct Pallet<T>(_);
79
80 #[derive(TypeInfo, Debug, Clone, Encode, Decode, DecodeWithMemTracking, PartialEq)]
82 pub enum ConfigOp<T: Default + Codec> {
83 Noop,
85 Set(T),
87 Remove,
89 }
90
91 #[pallet::config(with_default)]
92 pub trait Config: frame_system::Config {
93 #[pallet::no_default]
95 type OldCurrency: InspectLockableCurrency<
96 Self::AccountId,
97 Moment = BlockNumberFor<Self>,
98 Balance = Self::CurrencyBalance,
99 >;
100
101 #[pallet::no_default]
103 type Currency: FunHoldMutate<
104 Self::AccountId,
105 Reason = Self::RuntimeHoldReason,
106 Balance = Self::CurrencyBalance,
107 > + FunMutate<Self::AccountId, Balance = Self::CurrencyBalance>
108 + FunHoldBalanced<Self::AccountId, Balance = Self::CurrencyBalance>;
109
110 #[pallet::no_default_bounds]
112 type RuntimeHoldReason: From<HoldReason>;
113
114 type CurrencyBalance: sp_runtime::traits::AtLeast32BitUnsigned
117 + codec::FullCodec
118 + DecodeWithMemTracking
119 + HasCompact<Type: DecodeWithMemTracking>
120 + Copy
121 + MaybeSerializeDeserialize
122 + core::fmt::Debug
123 + Default
124 + From<u64>
125 + TypeInfo
126 + Send
127 + Sync
128 + MaxEncodedLen;
129
130 #[pallet::no_default_bounds]
137 type CurrencyToVote: sp_staking::currency_to_vote::CurrencyToVote<BalanceOf<Self>>;
138
139 #[pallet::no_default]
141 type ElectionProvider: ElectionProvider<
142 AccountId = Self::AccountId,
143 BlockNumber = BlockNumberFor<Self>,
144 DataProvider = Pallet<Self>,
146 >;
147
148 #[pallet::no_default_bounds]
150 type NominationsQuota: NominationsQuota<BalanceOf<Self>>;
151
152 #[pallet::constant]
166 type HistoryDepth: Get<u32>;
167
168 #[pallet::no_default_bounds]
171 type RewardRemainder: OnUnbalanced<NegativeImbalanceOf<Self>>;
172
173 #[pallet::no_default_bounds]
175 type Slash: OnUnbalanced<NegativeImbalanceOf<Self>>;
176
177 #[pallet::no_default_bounds]
181 type Reward: OnUnbalanced<PositiveImbalanceOf<Self>>;
182
183 #[pallet::constant]
185 type SessionsPerEra: Get<SessionIndex>;
186
187 #[pallet::constant]
202 type PlanningEraOffset: Get<SessionIndex>;
203
204 #[pallet::constant]
206 type BondingDuration: Get<EraIndex>;
207
208 #[pallet::constant]
213 type SlashDeferDuration: Get<EraIndex>;
214
215 #[pallet::no_default]
219 type AdminOrigin: EnsureOrigin<Self::RuntimeOrigin>;
220
221 #[pallet::no_default]
224 type EraPayout: EraPayout<BalanceOf<Self>>;
225
226 #[pallet::constant]
238 type MaxExposurePageSize: Get<u32>;
239
240 #[pallet::constant]
245 type MaxValidatorSet: Get<u32>;
246
247 #[pallet::no_default]
259 type VoterList: SortedListProvider<Self::AccountId, Score = VoteWeight>;
260
261 #[pallet::no_default]
282 type TargetList: SortedListProvider<Self::AccountId, Score = BalanceOf<Self>>;
283
284 #[pallet::constant]
295 type MaxUnlockingChunks: Get<u32>;
296
297 type MaxControllersInDeprecationBatch: Get<u32>;
299
300 #[pallet::no_default_bounds]
305 type EventListeners: sp_staking::OnStakingUpdate<Self::AccountId, BalanceOf<Self>>;
306
307 #[pallet::constant]
309 type MaxInvulnerables: Get<u32>;
310
311 #[pallet::constant]
313 type MaxDisabledValidators: Get<u32>;
314
315 #[pallet::constant]
324 type MaxEraDuration: Get<u64>;
325
326 #[pallet::no_default]
329 type RcClientInterface: pallet_staking_async_rc_client::RcClientInterface<
330 AccountId = Self::AccountId,
331 >;
332
333 #[pallet::no_default_bounds]
334 type Filter: Contains<Self::AccountId>;
339
340 type WeightInfo: WeightInfo;
342 }
343
344 #[pallet::composite_enum]
346 pub enum HoldReason {
347 #[codec(index = 0)]
349 Staking,
350 }
351
352 pub mod config_preludes {
354 use super::*;
355 use frame_support::{derive_impl, parameter_types, traits::ConstU32};
356 pub struct TestDefaultConfig;
357
358 #[derive_impl(frame_system::config_preludes::TestDefaultConfig, no_aggregated_types)]
359 impl frame_system::DefaultConfig for TestDefaultConfig {}
360
361 parameter_types! {
362 pub const SessionsPerEra: SessionIndex = 3;
363 pub const BondingDuration: EraIndex = 3;
364 }
365
366 #[frame_support::register_default_impl(TestDefaultConfig)]
367 impl DefaultConfig for TestDefaultConfig {
368 #[inject_runtime_type]
369 type RuntimeHoldReason = ();
370 type CurrencyBalance = u128;
371 type CurrencyToVote = ();
372 type NominationsQuota = crate::FixedNominationsQuota<16>;
373 type HistoryDepth = ConstU32<84>;
374 type RewardRemainder = ();
375 type Slash = ();
376 type Reward = ();
377 type SessionsPerEra = SessionsPerEra;
378 type BondingDuration = BondingDuration;
379 type PlanningEraOffset = ConstU32<1>;
380 type SlashDeferDuration = ();
381 type MaxExposurePageSize = ConstU32<64>;
382 type MaxUnlockingChunks = ConstU32<32>;
383 type MaxValidatorSet = ConstU32<100>;
384 type MaxControllersInDeprecationBatch = ConstU32<100>;
385 type MaxInvulnerables = ConstU32<20>;
386 type MaxDisabledValidators = ConstU32<100>;
387 type MaxEraDuration = ();
388 type EventListeners = ();
389 type Filter = Nothing;
390 type WeightInfo = ();
391 }
392 }
393
394 #[pallet::storage]
396 pub type ValidatorCount<T> = StorageValue<_, u32, ValueQuery>;
397
398 #[pallet::storage]
402 pub type Invulnerables<T: Config> =
403 StorageValue<_, BoundedVec<T::AccountId, T::MaxInvulnerables>, ValueQuery>;
404
405 #[pallet::storage]
409 pub type Bonded<T: Config> = StorageMap<_, Twox64Concat, T::AccountId, T::AccountId>;
410
411 #[pallet::storage]
413 pub type MinNominatorBond<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;
414
415 #[pallet::storage]
417 pub type MinValidatorBond<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;
418
419 #[pallet::storage]
421 pub type MinimumActiveStake<T> = StorageValue<_, BalanceOf<T>, ValueQuery>;
422
423 #[pallet::storage]
427 pub type MinCommission<T: Config> = StorageValue<_, Perbill, ValueQuery>;
428
429 #[pallet::storage]
434 pub type Ledger<T: Config> = StorageMap<_, Blake2_128Concat, T::AccountId, StakingLedger<T>>;
435
436 #[pallet::storage]
440 pub type Payee<T: Config> =
441 StorageMap<_, Twox64Concat, T::AccountId, RewardDestination<T::AccountId>, OptionQuery>;
442
443 #[pallet::storage]
447 pub type Validators<T: Config> =
448 CountedStorageMap<_, Twox64Concat, T::AccountId, ValidatorPrefs, ValueQuery>;
449
450 #[pallet::storage]
454 pub type MaxValidatorsCount<T> = StorageValue<_, u32, OptionQuery>;
455
456 #[pallet::storage]
476 pub type Nominators<T: Config> =
477 CountedStorageMap<_, Twox64Concat, T::AccountId, Nominations<T>>;
478
479 #[pallet::storage]
486 pub type VirtualStakers<T: Config> = CountedStorageMap<_, Twox64Concat, T::AccountId, ()>;
487
488 #[pallet::storage]
492 pub type MaxNominatorsCount<T> = StorageValue<_, u32, OptionQuery>;
493
494 #[pallet::storage]
501 pub type CurrentEra<T> = StorageValue<_, EraIndex>;
502
503 #[pallet::storage]
508 pub type ActiveEra<T> = StorageValue<_, ActiveEraInfo>;
509
510 pub struct BondedErasBound<T>(core::marker::PhantomData<T>);
512 impl<T: Config> Get<u32> for BondedErasBound<T> {
513 fn get() -> u32 {
514 T::BondingDuration::get().saturating_add(1)
515 }
516 }
517
518 #[pallet::storage]
523 pub type BondedEras<T: Config> =
524 StorageValue<_, BoundedVec<(EraIndex, SessionIndex), BondedErasBound<T>>, ValueQuery>;
525
526 #[pallet::storage]
541 pub type ErasStakersOverview<T: Config> = StorageDoubleMap<
542 _,
543 Twox64Concat,
544 EraIndex,
545 Twox64Concat,
546 T::AccountId,
547 PagedExposureMetadata<BalanceOf<T>>,
548 OptionQuery,
549 >;
550
551 #[derive(PartialEqNoBound, Encode, Decode, DebugNoBound, TypeInfo, DefaultNoBound)]
560 #[scale_info(skip_type_params(T))]
561 pub struct BoundedExposurePage<T: Config>(pub ExposurePage<T::AccountId, BalanceOf<T>>);
562 impl<T: Config> Deref for BoundedExposurePage<T> {
563 type Target = ExposurePage<T::AccountId, BalanceOf<T>>;
564
565 fn deref(&self) -> &Self::Target {
566 &self.0
567 }
568 }
569
570 impl<T: Config> core::ops::DerefMut for BoundedExposurePage<T> {
571 fn deref_mut(&mut self) -> &mut Self::Target {
572 &mut self.0
573 }
574 }
575
576 impl<T: Config> codec::MaxEncodedLen for BoundedExposurePage<T> {
577 fn max_encoded_len() -> usize {
578 let max_exposure_page_size = T::MaxExposurePageSize::get() as usize;
579 let individual_size =
580 T::AccountId::max_encoded_len() + BalanceOf::<T>::max_encoded_len();
581
582 BalanceOf::<T>::max_encoded_len() +
584 max_exposure_page_size.saturating_mul(individual_size)
586 }
587 }
588
589 impl<T: Config> From<ExposurePage<T::AccountId, BalanceOf<T>>> for BoundedExposurePage<T> {
590 fn from(value: ExposurePage<T::AccountId, BalanceOf<T>>) -> Self {
591 Self(value)
592 }
593 }
594
595 impl<T: Config> From<BoundedExposurePage<T>> for ExposurePage<T::AccountId, BalanceOf<T>> {
596 fn from(value: BoundedExposurePage<T>) -> Self {
597 value.0
598 }
599 }
600
601 impl<T: Config> codec::EncodeLike<BoundedExposurePage<T>>
602 for ExposurePage<T::AccountId, BalanceOf<T>>
603 {
604 }
605
606 #[pallet::storage]
613 pub type ErasStakersPaged<T: Config> = StorageNMap<
614 _,
615 (
616 NMapKey<Twox64Concat, EraIndex>,
617 NMapKey<Twox64Concat, T::AccountId>,
618 NMapKey<Twox64Concat, Page>,
619 ),
620 BoundedExposurePage<T>,
621 OptionQuery,
622 >;
623
624 pub struct ClaimedRewardsBound<T>(core::marker::PhantomData<T>);
625 impl<T: Config> Get<u32> for ClaimedRewardsBound<T> {
626 fn get() -> u32 {
627 let max_total_nominators_per_validator =
628 <T::ElectionProvider as ElectionProvider>::MaxBackersPerWinner::get();
629 let exposure_page_size = T::MaxExposurePageSize::get();
630 max_total_nominators_per_validator
631 .saturating_div(exposure_page_size)
632 .saturating_add(1)
633 }
634 }
635
636 #[pallet::storage]
643 pub type ClaimedRewards<T: Config> = StorageDoubleMap<
644 _,
645 Twox64Concat,
646 EraIndex,
647 Twox64Concat,
648 T::AccountId,
649 WeakBoundedVec<Page, ClaimedRewardsBound<T>>,
650 ValueQuery,
651 >;
652
653 #[pallet::storage]
660 pub type ErasValidatorPrefs<T: Config> = StorageDoubleMap<
661 _,
662 Twox64Concat,
663 EraIndex,
664 Twox64Concat,
665 T::AccountId,
666 ValidatorPrefs,
667 ValueQuery,
668 >;
669
670 #[pallet::storage]
674 pub type ErasValidatorReward<T: Config> = StorageMap<_, Twox64Concat, EraIndex, BalanceOf<T>>;
675
676 #[pallet::storage]
679 pub type ErasRewardPoints<T: Config> =
680 StorageMap<_, Twox64Concat, EraIndex, EraRewardPoints<T>, ValueQuery>;
681
682 #[pallet::storage]
685 pub type ErasTotalStake<T: Config> =
686 StorageMap<_, Twox64Concat, EraIndex, BalanceOf<T>, ValueQuery>;
687
688 #[pallet::storage]
690 pub type ForceEra<T> = StorageValue<_, Forcing, ValueQuery>;
691
692 #[pallet::storage]
696 pub type MaxStakedRewards<T> = StorageValue<_, Percent, OptionQuery>;
697
698 #[pallet::storage]
702 pub type SlashRewardFraction<T> = StorageValue<_, Perbill, ValueQuery>;
703
704 #[pallet::storage]
707 pub type CanceledSlashPayout<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;
708
709 #[pallet::storage]
720 pub type OffenceQueue<T: Config> = StorageDoubleMap<
721 _,
722 Twox64Concat,
723 EraIndex,
724 Twox64Concat,
725 T::AccountId,
726 slashing::OffenceRecord<T::AccountId>,
727 >;
728
729 #[pallet::storage]
741 pub type OffenceQueueEras<T: Config> = StorageValue<_, BoundedVec<u32, T::BondingDuration>>;
742
743 #[pallet::storage]
756 pub type ProcessingOffence<T: Config> =
757 StorageValue<_, (EraIndex, T::AccountId, slashing::OffenceRecord<T::AccountId>)>;
758
759 #[pallet::storage]
761 pub type UnappliedSlashes<T: Config> = StorageDoubleMap<
762 _,
763 Twox64Concat,
764 EraIndex,
765 Twox64Concat,
766 (T::AccountId, Perbill, u32),
768 UnappliedSlash<T>,
769 OptionQuery,
770 >;
771
772 #[pallet::storage]
775 pub type ValidatorSlashInEra<T: Config> = StorageDoubleMap<
776 _,
777 Twox64Concat,
778 EraIndex,
779 Twox64Concat,
780 T::AccountId,
781 (Perbill, BalanceOf<T>),
782 >;
783
784 #[pallet::storage]
786 pub type NominatorSlashInEra<T: Config> =
787 StorageDoubleMap<_, Twox64Concat, EraIndex, Twox64Concat, T::AccountId, BalanceOf<T>>;
788
789 #[pallet::storage]
793 pub type ChillThreshold<T: Config> = StorageValue<_, Percent, OptionQuery>;
794
795 #[pallet::storage]
800 pub type VoterSnapshotStatus<T: Config> =
801 StorageValue<_, SnapshotStatus<T::AccountId>, ValueQuery>;
802
803 #[pallet::storage]
810 pub type NextElectionPage<T: Config> = StorageValue<_, PageIndex, OptionQuery>;
811
812 #[pallet::storage]
814 pub type ElectableStashes<T: Config> =
815 StorageValue<_, BoundedBTreeSet<T::AccountId, T::MaxValidatorSet>, ValueQuery>;
816
817 #[pallet::genesis_config]
818 #[derive(frame_support::DefaultNoBound, frame_support::DebugNoBound)]
819 pub struct GenesisConfig<T: Config> {
820 pub validator_count: u32,
821 pub invulnerables: BoundedVec<T::AccountId, T::MaxInvulnerables>,
822 pub force_era: Forcing,
823 pub slash_reward_fraction: Perbill,
824 pub canceled_payout: BalanceOf<T>,
825 pub stakers: Vec<(T::AccountId, BalanceOf<T>, crate::StakerStatus<T::AccountId>)>,
826 pub min_nominator_bond: BalanceOf<T>,
827 pub min_validator_bond: BalanceOf<T>,
828 pub max_validator_count: Option<u32>,
829 pub max_nominator_count: Option<u32>,
830 pub dev_stakers: Option<(u32, u32)>,
837 pub active_era: (u32, u32, u64),
839 }
840
841 impl<T: Config> GenesisConfig<T> {
842 fn generate_endowed_bonded_account(derivation: &str, rng: &mut ChaChaRng) -> T::AccountId {
843 let pair: SrPair = Pair::from_string(&derivation, None)
844 .expect(&format!("Failed to parse derivation string: {derivation}"));
845 let who = T::AccountId::decode(&mut &pair.public().encode()[..])
846 .expect(&format!("Failed to decode public key from pair: {:?}", pair.public()));
847
848 let (min, max) = T::VoterList::range();
849 let stake = BalanceOf::<T>::from(rng.next_u64().min(max).max(min));
850 let two: BalanceOf<T> = 2u32.into();
851
852 assert_ok!(T::Currency::mint_into(&who, stake * two));
853 assert_ok!(<Pallet<T>>::bond(
854 T::RuntimeOrigin::from(Some(who.clone()).into()),
855 stake,
856 RewardDestination::Staked,
857 ));
858 who
859 }
860 }
861
862 #[pallet::genesis_build]
863 impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
864 fn build(&self) {
865 crate::log!(trace, "initializing with {:?}", self);
866 ValidatorCount::<T>::put(self.validator_count);
867 assert!(
868 self.invulnerables.len() as u32 <= T::MaxInvulnerables::get(),
869 "Too many invulnerable validators at genesis."
870 );
871 <Invulnerables<T>>::put(&self.invulnerables);
872 ForceEra::<T>::put(self.force_era);
873 CanceledSlashPayout::<T>::put(self.canceled_payout);
874 SlashRewardFraction::<T>::put(self.slash_reward_fraction);
875 MinNominatorBond::<T>::put(self.min_nominator_bond);
876 MinValidatorBond::<T>::put(self.min_validator_bond);
877 if let Some(x) = self.max_validator_count {
878 MaxValidatorsCount::<T>::put(x);
879 }
880 if let Some(x) = self.max_nominator_count {
881 MaxNominatorsCount::<T>::put(x);
882 }
883
884 for &(ref stash, balance, ref status) in &self.stakers {
885 crate::log!(
886 trace,
887 "inserting genesis staker: {:?} => {:?} => {:?}",
888 stash,
889 balance,
890 status
891 );
892 assert!(
893 asset::free_to_stake::<T>(stash) >= balance,
894 "Stash does not have enough balance to bond."
895 );
896 assert_ok!(<Pallet<T>>::bond(
897 T::RuntimeOrigin::from(Some(stash.clone()).into()),
898 balance,
899 RewardDestination::Staked,
900 ));
901 assert_ok!(match status {
902 crate::StakerStatus::Validator => <Pallet<T>>::validate(
903 T::RuntimeOrigin::from(Some(stash.clone()).into()),
904 Default::default(),
905 ),
906 crate::StakerStatus::Nominator(votes) => <Pallet<T>>::nominate(
907 T::RuntimeOrigin::from(Some(stash.clone()).into()),
908 votes.iter().map(|l| T::Lookup::unlookup(l.clone())).collect(),
909 ),
910 _ => Ok(()),
911 });
912 assert!(
913 ValidatorCount::<T>::get() <=
914 <T::ElectionProvider as ElectionProvider>::MaxWinnersPerPage::get() *
915 <T::ElectionProvider as ElectionProvider>::Pages::get()
916 );
917 }
918
919 assert_eq!(
921 T::VoterList::count(),
922 Nominators::<T>::count() + Validators::<T>::count(),
923 "not all genesis stakers were inserted into sorted list provider, something is wrong."
924 );
925
926 if let Some((validators, nominators)) = self.dev_stakers {
928 crate::log!(
929 debug,
930 "generating dev stakers: validators: {}, nominators: {}",
931 validators,
932 nominators
933 );
934 let base_derivation = "//staker//{}";
935
936 let mut rng =
939 ChaChaRng::from_seed(base_derivation.using_encoded(sp_core::blake2_256));
940
941 let validators = (0..validators)
942 .map(|index| {
943 let derivation =
944 base_derivation.replace("{}", &format!("validator{}", index));
945 let who = Self::generate_endowed_bonded_account(&derivation, &mut rng);
946 assert_ok!(<Pallet<T>>::validate(
947 T::RuntimeOrigin::from(Some(who.clone()).into()),
948 Default::default(),
949 ));
950 who
951 })
952 .collect::<Vec<_>>();
953
954 (0..nominators).for_each(|index| {
955 let derivation = base_derivation.replace("{}", &format!("nominator{}", index));
956 let who = Self::generate_endowed_bonded_account(&derivation, &mut rng);
957
958 let random_nominations = validators
959 .choose_multiple(&mut rng, MaxNominationsOf::<T>::get() as usize)
960 .map(|v| v.clone())
961 .collect::<Vec<_>>();
962
963 assert_ok!(<Pallet<T>>::nominate(
964 T::RuntimeOrigin::from(Some(who.clone()).into()),
965 random_nominations.iter().map(|l| T::Lookup::unlookup(l.clone())).collect(),
966 ));
967 })
968 }
969
970 let (active_era, session_index, timestamp) = self.active_era;
971 ActiveEra::<T>::put(ActiveEraInfo { index: active_era, start: Some(timestamp) });
972 CurrentEra::<T>::put(active_era);
974 BondedEras::<T>::put(
976 BoundedVec::<_, BondedErasBound<T>>::try_from(
977 alloc::vec![(active_era, session_index)]
978 )
979 .expect("bound for BondedEras is BondingDuration + 1; can contain at least one element; qed")
980 );
981 }
982 }
983
984 #[pallet::event]
985 #[pallet::generate_deposit(pub fn deposit_event)]
986 pub enum Event<T: Config> {
987 EraPaid {
990 era_index: EraIndex,
991 validator_payout: BalanceOf<T>,
992 remainder: BalanceOf<T>,
993 },
994 Rewarded {
996 stash: T::AccountId,
997 dest: RewardDestination<T::AccountId>,
998 amount: BalanceOf<T>,
999 },
1000 Slashed {
1002 staker: T::AccountId,
1003 amount: BalanceOf<T>,
1004 },
1005 OldSlashingReportDiscarded {
1008 session_index: SessionIndex,
1009 },
1010 Bonded {
1015 stash: T::AccountId,
1016 amount: BalanceOf<T>,
1017 },
1018 Unbonded {
1020 stash: T::AccountId,
1021 amount: BalanceOf<T>,
1022 },
1023 Withdrawn {
1026 stash: T::AccountId,
1027 amount: BalanceOf<T>,
1028 },
1029 StakerRemoved {
1032 stash: T::AccountId,
1033 },
1034 Kicked {
1036 nominator: T::AccountId,
1037 stash: T::AccountId,
1038 },
1039 Chilled {
1041 stash: T::AccountId,
1042 },
1043 PayoutStarted {
1045 era_index: EraIndex,
1046 validator_stash: T::AccountId,
1047 page: Page,
1048 next: Option<Page>,
1049 },
1050 ValidatorPrefsSet {
1052 stash: T::AccountId,
1053 prefs: ValidatorPrefs,
1054 },
1055 SnapshotVotersSizeExceeded {
1057 size: u32,
1058 },
1059 SnapshotTargetsSizeExceeded {
1061 size: u32,
1062 },
1063 ForceEra {
1064 mode: Forcing,
1065 },
1066 ControllerBatchDeprecated {
1068 failures: u32,
1069 },
1070 CurrencyMigrated {
1073 stash: T::AccountId,
1074 force_withdraw: BalanceOf<T>,
1075 },
1076 PagedElectionProceeded {
1086 page: PageIndex,
1087 result: Result<u32, u32>,
1088 },
1089 OffenceReported {
1092 offence_era: EraIndex,
1093 validator: T::AccountId,
1094 fraction: Perbill,
1095 },
1096 SlashComputed {
1098 offence_era: EraIndex,
1099 slash_era: EraIndex,
1100 offender: T::AccountId,
1101 page: u32,
1102 },
1103 SlashCancelled {
1105 slash_era: EraIndex,
1106 slash_key: (T::AccountId, Perbill, u32),
1107 payout: BalanceOf<T>,
1108 },
1109 SessionRotated {
1114 starting_session: SessionIndex,
1115 active_era: EraIndex,
1116 planned_era: EraIndex,
1117 },
1118 Unexpected(UnexpectedKind),
1121 }
1122
1123 #[derive(Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, TypeInfo, RuntimeDebug)]
1129 pub enum UnexpectedKind {
1130 EraDurationBoundExceeded,
1132 UnknownValidatorActivation,
1134 }
1135
1136 #[pallet::error]
1137 #[derive(PartialEq)]
1138 pub enum Error<T> {
1139 NotController,
1141 NotStash,
1143 AlreadyBonded,
1145 AlreadyPaired,
1147 EmptyTargets,
1149 DuplicateIndex,
1151 InvalidSlashRecord,
1153 InsufficientBond,
1157 NoMoreChunks,
1159 NoUnlockChunk,
1161 FundedTarget,
1163 InvalidEraToReward,
1165 InvalidNumberOfNominations,
1167 AlreadyClaimed,
1169 InvalidPage,
1171 IncorrectHistoryDepth,
1173 BadState,
1175 TooManyTargets,
1177 BadTarget,
1179 CannotChillOther,
1181 TooManyNominators,
1184 TooManyValidators,
1187 CommissionTooLow,
1189 BoundNotMet,
1191 ControllerDeprecated,
1193 CannotRestoreLedger,
1195 RewardDestinationRestricted,
1197 NotEnoughFunds,
1199 VirtualStakerNotAllowed,
1201 CannotReapStash,
1203 AlreadyMigrated,
1205 EraNotStarted,
1207 Restricted,
1210 }
1211
1212 impl<T: Config> Pallet<T> {
1213 pub fn apply_unapplied_slashes(active_era: EraIndex) -> Weight {
1215 let mut slashes = UnappliedSlashes::<T>::iter_prefix(&active_era).take(1);
1216 if let Some((key, slash)) = slashes.next() {
1217 crate::log!(
1218 debug,
1219 "🦹 found slash {:?} scheduled to be executed in era {:?}",
1220 slash,
1221 active_era,
1222 );
1223 let offence_era = active_era.saturating_sub(T::SlashDeferDuration::get());
1224 slashing::apply_slash::<T>(slash, offence_era);
1225 UnappliedSlashes::<T>::remove(&active_era, &key);
1227 T::WeightInfo::apply_slash()
1228 } else {
1229 T::DbWeight::get().reads(1)
1230 }
1231 }
1232 }
1233
1234 #[pallet::hooks]
1235 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
1236 fn on_initialize(_now: BlockNumberFor<T>) -> Weight {
1237 let mut consumed_weight = slashing::process_offence::<T>();
1239
1240 consumed_weight.saturating_accrue(T::DbWeight::get().reads(1));
1242 if let Some(active_era) = ActiveEra::<T>::get() {
1243 let slash_weight = Self::apply_unapplied_slashes(active_era.index);
1244 consumed_weight.saturating_accrue(slash_weight);
1245 }
1246
1247 session_rotation::EraElectionPlanner::<T>::maybe_fetch_election_results();
1250 consumed_weight
1251 }
1252
1253 fn integrity_test() {
1254 assert_eq!(
1256 MaxNominationsOf::<T>::get(),
1257 <Self as ElectionDataProvider>::MaxVotesPerVoter::get()
1258 );
1259 assert!(!MaxNominationsOf::<T>::get().is_zero());
1261
1262 assert!(
1263 T::SlashDeferDuration::get() < T::BondingDuration::get() || T::BondingDuration::get() == 0,
1264 "As per documentation, slash defer duration ({}) should be less than bonding duration ({}).",
1265 T::SlashDeferDuration::get(),
1266 T::BondingDuration::get(),
1267 );
1268 }
1269
1270 #[cfg(feature = "try-runtime")]
1271 fn try_state(n: BlockNumberFor<T>) -> Result<(), sp_runtime::TryRuntimeError> {
1272 Self::do_try_state(n)
1273 }
1274 }
1275
1276 #[pallet::call]
1277 impl<T: Config> Pallet<T> {
1278 #[pallet::call_index(0)]
1291 #[pallet::weight(T::WeightInfo::bond())]
1292 pub fn bond(
1293 origin: OriginFor<T>,
1294 #[pallet::compact] value: BalanceOf<T>,
1295 payee: RewardDestination<T::AccountId>,
1296 ) -> DispatchResult {
1297 let stash = ensure_signed(origin)?;
1298
1299 ensure!(!T::Filter::contains(&stash), Error::<T>::Restricted);
1300
1301 if StakingLedger::<T>::is_bonded(StakingAccount::Stash(stash.clone())) {
1302 return Err(Error::<T>::AlreadyBonded.into());
1303 }
1304
1305 if StakingLedger::<T>::is_bonded(StakingAccount::Controller(stash.clone())) {
1307 return Err(Error::<T>::AlreadyPaired.into());
1308 }
1309
1310 if value < Self::min_chilled_bond() {
1312 return Err(Error::<T>::InsufficientBond.into());
1313 }
1314
1315 let stash_balance = asset::free_to_stake::<T>(&stash);
1316 let value = value.min(stash_balance);
1317 Self::deposit_event(Event::<T>::Bonded { stash: stash.clone(), amount: value });
1318 let ledger = StakingLedger::<T>::new(stash.clone(), value);
1319
1320 ledger.bond(payee)?;
1323
1324 Ok(())
1325 }
1326
1327 #[pallet::call_index(1)]
1338 #[pallet::weight(T::WeightInfo::bond_extra())]
1339 pub fn bond_extra(
1340 origin: OriginFor<T>,
1341 #[pallet::compact] max_additional: BalanceOf<T>,
1342 ) -> DispatchResult {
1343 let stash = ensure_signed(origin)?;
1344 ensure!(!T::Filter::contains(&stash), Error::<T>::Restricted);
1345 Self::do_bond_extra(&stash, max_additional)
1346 }
1347
1348 #[pallet::call_index(2)]
1368 #[pallet::weight(
1369 T::WeightInfo::withdraw_unbonded_kill().saturating_add(T::WeightInfo::unbond()))
1370 ]
1371 pub fn unbond(
1372 origin: OriginFor<T>,
1373 #[pallet::compact] value: BalanceOf<T>,
1374 ) -> DispatchResultWithPostInfo {
1375 let controller = ensure_signed(origin)?;
1376 let unlocking =
1377 Self::ledger(Controller(controller.clone())).map(|l| l.unlocking.len())?;
1378
1379 let maybe_withdraw_weight = {
1382 if unlocking == T::MaxUnlockingChunks::get() as usize {
1383 Some(Self::do_withdraw_unbonded(&controller)?)
1384 } else {
1385 None
1386 }
1387 };
1388
1389 let mut ledger = Self::ledger(Controller(controller))?;
1392 let mut value = value.min(ledger.active);
1393 let stash = ledger.stash.clone();
1394
1395 ensure!(
1396 ledger.unlocking.len() < T::MaxUnlockingChunks::get() as usize,
1397 Error::<T>::NoMoreChunks,
1398 );
1399
1400 if !value.is_zero() {
1401 ledger.active -= value;
1402
1403 if ledger.active < asset::existential_deposit::<T>() {
1405 value += ledger.active;
1406 ledger.active = Zero::zero();
1407 }
1408
1409 let min_active_bond = if Nominators::<T>::contains_key(&stash) {
1410 Self::min_nominator_bond()
1411 } else if Validators::<T>::contains_key(&stash) {
1412 Self::min_validator_bond()
1413 } else {
1414 Zero::zero()
1416 };
1417
1418 ensure!(ledger.active >= min_active_bond, Error::<T>::InsufficientBond);
1421
1422 let era = CurrentEra::<T>::get()
1424 .unwrap_or(0)
1425 .defensive_saturating_add(T::BondingDuration::get());
1426 if let Some(chunk) = ledger.unlocking.last_mut().filter(|chunk| chunk.era == era) {
1427 chunk.value = chunk.value.defensive_saturating_add(value)
1431 } else {
1432 ledger
1433 .unlocking
1434 .try_push(UnlockChunk { value, era })
1435 .map_err(|_| Error::<T>::NoMoreChunks)?;
1436 };
1437 ledger.update()?;
1439
1440 if T::VoterList::contains(&stash) {
1442 let _ = T::VoterList::on_update(&stash, Self::weight_of(&stash));
1443 }
1444
1445 Self::deposit_event(Event::<T>::Unbonded { stash, amount: value });
1446 }
1447
1448 let actual_weight = if let Some(withdraw_weight) = maybe_withdraw_weight {
1449 Some(T::WeightInfo::unbond().saturating_add(withdraw_weight))
1450 } else {
1451 Some(T::WeightInfo::unbond())
1452 };
1453
1454 Ok(actual_weight.into())
1455 }
1456
1457 #[pallet::call_index(3)]
1473 #[pallet::weight(T::WeightInfo::withdraw_unbonded_kill())]
1474 pub fn withdraw_unbonded(
1475 origin: OriginFor<T>,
1476 _num_slashing_spans: u32,
1477 ) -> DispatchResultWithPostInfo {
1478 let controller = ensure_signed(origin)?;
1479
1480 let actual_weight = Self::do_withdraw_unbonded(&controller)?;
1481 Ok(Some(actual_weight).into())
1482 }
1483
1484 #[pallet::call_index(4)]
1490 #[pallet::weight(T::WeightInfo::validate())]
1491 pub fn validate(origin: OriginFor<T>, prefs: ValidatorPrefs) -> DispatchResult {
1492 let controller = ensure_signed(origin)?;
1493
1494 let ledger = Self::ledger(Controller(controller))?;
1495
1496 ensure!(ledger.active >= Self::min_validator_bond(), Error::<T>::InsufficientBond);
1497 let stash = &ledger.stash;
1498
1499 ensure!(prefs.commission >= MinCommission::<T>::get(), Error::<T>::CommissionTooLow);
1501
1502 if !Validators::<T>::contains_key(stash) {
1504 if let Some(max_validators) = MaxValidatorsCount::<T>::get() {
1508 ensure!(
1509 Validators::<T>::count() < max_validators,
1510 Error::<T>::TooManyValidators
1511 );
1512 }
1513 }
1514
1515 Self::do_remove_nominator(stash);
1516 Self::do_add_validator(stash, prefs.clone());
1517 Self::deposit_event(Event::<T>::ValidatorPrefsSet { stash: ledger.stash, prefs });
1518
1519 Ok(())
1520 }
1521
1522 #[pallet::call_index(5)]
1528 #[pallet::weight(T::WeightInfo::nominate(targets.len() as u32))]
1529 pub fn nominate(
1530 origin: OriginFor<T>,
1531 targets: Vec<AccountIdLookupOf<T>>,
1532 ) -> DispatchResult {
1533 let controller = ensure_signed(origin)?;
1534
1535 let ledger = Self::ledger(StakingAccount::Controller(controller.clone()))?;
1536
1537 ensure!(ledger.active >= Self::min_nominator_bond(), Error::<T>::InsufficientBond);
1538 let stash = &ledger.stash;
1539
1540 if !Nominators::<T>::contains_key(stash) {
1542 if let Some(max_nominators) = MaxNominatorsCount::<T>::get() {
1546 ensure!(
1547 Nominators::<T>::count() < max_nominators,
1548 Error::<T>::TooManyNominators
1549 );
1550 }
1551 }
1552
1553 let mut targets = targets
1555 .into_iter()
1556 .map(|t| T::Lookup::lookup(t).map_err(DispatchError::from))
1557 .collect::<Result<Vec<_>, _>>()?;
1558 targets.sort();
1559 targets.dedup();
1560
1561 ensure!(!targets.is_empty(), Error::<T>::EmptyTargets);
1562 ensure!(
1563 targets.len() <= T::NominationsQuota::get_quota(ledger.active) as usize,
1564 Error::<T>::TooManyTargets
1565 );
1566
1567 let old = Nominators::<T>::get(stash).map_or_else(Vec::new, |x| x.targets.into_inner());
1568
1569 let targets: BoundedVec<_, _> = targets
1570 .into_iter()
1571 .map(|n| {
1572 if old.contains(&n) || !Validators::<T>::get(&n).blocked {
1573 Ok(n)
1574 } else {
1575 Err(Error::<T>::BadTarget.into())
1576 }
1577 })
1578 .collect::<Result<Vec<_>, DispatchError>>()?
1579 .try_into()
1580 .map_err(|_| Error::<T>::TooManyNominators)?;
1581
1582 let nominations = Nominations {
1583 targets,
1584 submitted_in: CurrentEra::<T>::get().unwrap_or(0),
1586 suppressed: false,
1587 };
1588
1589 Self::do_remove_validator(stash);
1590 Self::do_add_nominator(stash, nominations);
1591 Ok(())
1592 }
1593
1594 #[pallet::call_index(6)]
1605 #[pallet::weight(T::WeightInfo::chill())]
1606 pub fn chill(origin: OriginFor<T>) -> DispatchResult {
1607 let controller = ensure_signed(origin)?;
1608
1609 let ledger = Self::ledger(StakingAccount::Controller(controller))?;
1610
1611 Self::chill_stash(&ledger.stash);
1612 Ok(())
1613 }
1614
1615 #[pallet::call_index(7)]
1621 #[pallet::weight(T::WeightInfo::set_payee())]
1622 pub fn set_payee(
1623 origin: OriginFor<T>,
1624 payee: RewardDestination<T::AccountId>,
1625 ) -> DispatchResult {
1626 let controller = ensure_signed(origin)?;
1627 let ledger = Self::ledger(Controller(controller.clone()))?;
1628
1629 ensure!(
1630 (payee != {
1631 #[allow(deprecated)]
1632 RewardDestination::Controller
1633 }),
1634 Error::<T>::ControllerDeprecated
1635 );
1636
1637 let _ = ledger
1638 .set_payee(payee)
1639 .defensive_proof("ledger was retrieved from storage, thus it's bonded; qed.")?;
1640
1641 Ok(())
1642 }
1643
1644 #[pallet::call_index(8)]
1653 #[pallet::weight(T::WeightInfo::set_controller())]
1654 pub fn set_controller(origin: OriginFor<T>) -> DispatchResult {
1655 let stash = ensure_signed(origin)?;
1656
1657 Self::ledger(StakingAccount::Stash(stash.clone())).map(|ledger| {
1658 let controller = ledger.controller()
1659 .defensive_proof("Ledger's controller field didn't exist. The controller should have been fetched using StakingLedger.")
1660 .ok_or(Error::<T>::NotController)?;
1661
1662 if controller == stash {
1663 return Err(Error::<T>::AlreadyPaired.into())
1665 }
1666
1667 let _ = ledger.set_controller_to_stash()?;
1668 Ok(())
1669 })?
1670 }
1671
1672 #[pallet::call_index(9)]
1676 #[pallet::weight(T::WeightInfo::set_validator_count())]
1677 pub fn set_validator_count(
1678 origin: OriginFor<T>,
1679 #[pallet::compact] new: u32,
1680 ) -> DispatchResult {
1681 ensure_root(origin)?;
1682
1683 ensure!(new <= T::MaxValidatorSet::get(), Error::<T>::TooManyValidators);
1684
1685 ValidatorCount::<T>::put(new);
1686 Ok(())
1687 }
1688
1689 #[pallet::call_index(10)]
1694 #[pallet::weight(T::WeightInfo::set_validator_count())]
1695 pub fn increase_validator_count(
1696 origin: OriginFor<T>,
1697 #[pallet::compact] additional: u32,
1698 ) -> DispatchResult {
1699 ensure_root(origin)?;
1700 let old = ValidatorCount::<T>::get();
1701 let new = old.checked_add(additional).ok_or(ArithmeticError::Overflow)?;
1702
1703 ensure!(new <= T::MaxValidatorSet::get(), Error::<T>::TooManyValidators);
1704
1705 ValidatorCount::<T>::put(new);
1706 Ok(())
1707 }
1708
1709 #[pallet::call_index(11)]
1714 #[pallet::weight(T::WeightInfo::set_validator_count())]
1715 pub fn scale_validator_count(origin: OriginFor<T>, factor: Percent) -> DispatchResult {
1716 ensure_root(origin)?;
1717 let old = ValidatorCount::<T>::get();
1718 let new = old.checked_add(factor.mul_floor(old)).ok_or(ArithmeticError::Overflow)?;
1719
1720 ensure!(new <= T::MaxValidatorSet::get(), Error::<T>::TooManyValidators);
1721
1722 ValidatorCount::<T>::put(new);
1723 Ok(())
1724 }
1725
1726 #[pallet::call_index(12)]
1736 #[pallet::weight(T::WeightInfo::force_no_eras())]
1737 pub fn force_no_eras(origin: OriginFor<T>) -> DispatchResult {
1738 ensure_root(origin)?;
1739 Self::set_force_era(Forcing::ForceNone);
1740 Ok(())
1741 }
1742
1743 #[pallet::call_index(13)]
1754 #[pallet::weight(T::WeightInfo::force_new_era())]
1755 pub fn force_new_era(origin: OriginFor<T>) -> DispatchResult {
1756 ensure_root(origin)?;
1757 Self::set_force_era(Forcing::ForceNew);
1758 Ok(())
1759 }
1760
1761 #[pallet::call_index(14)]
1765 #[pallet::weight(T::WeightInfo::set_invulnerables(invulnerables.len() as u32))]
1766 pub fn set_invulnerables(
1767 origin: OriginFor<T>,
1768 invulnerables: Vec<T::AccountId>,
1769 ) -> DispatchResult {
1770 ensure_root(origin)?;
1771 let invulnerables =
1772 BoundedVec::try_from(invulnerables).map_err(|_| Error::<T>::BoundNotMet)?;
1773 <Invulnerables<T>>::put(invulnerables);
1774 Ok(())
1775 }
1776
1777 #[pallet::call_index(15)]
1786 #[pallet::weight(T::WeightInfo::force_unstake())]
1787 pub fn force_unstake(
1788 origin: OriginFor<T>,
1789 stash: T::AccountId,
1790 _num_slashing_spans: u32,
1791 ) -> DispatchResult {
1792 ensure_root(origin)?;
1793
1794 Self::kill_stash(&stash)?;
1796
1797 Ok(())
1798 }
1799
1800 #[pallet::call_index(16)]
1810 #[pallet::weight(T::WeightInfo::force_new_era_always())]
1811 pub fn force_new_era_always(origin: OriginFor<T>) -> DispatchResult {
1812 ensure_root(origin)?;
1813 Self::set_force_era(Forcing::ForceAlways);
1814 Ok(())
1815 }
1816
1817 #[pallet::call_index(17)]
1827 #[pallet::weight(T::WeightInfo::cancel_deferred_slash(slash_keys.len() as u32))]
1828 pub fn cancel_deferred_slash(
1829 origin: OriginFor<T>,
1830 era: EraIndex,
1831 slash_keys: Vec<(T::AccountId, Perbill, u32)>,
1832 ) -> DispatchResult {
1833 T::AdminOrigin::ensure_origin(origin)?;
1834 ensure!(!slash_keys.is_empty(), Error::<T>::EmptyTargets);
1835
1836 slash_keys.into_iter().for_each(|i| {
1838 UnappliedSlashes::<T>::take(&era, &i).map(|unapplied_slash| {
1839 Self::deposit_event(Event::<T>::SlashCancelled {
1840 slash_era: era,
1841 slash_key: i,
1842 payout: unapplied_slash.payout,
1843 });
1844 });
1845 });
1846 Ok(())
1847 }
1848
1849 #[pallet::call_index(18)]
1863 #[pallet::weight(T::WeightInfo::payout_stakers_alive_staked(T::MaxExposurePageSize::get()))]
1864 pub fn payout_stakers(
1865 origin: OriginFor<T>,
1866 validator_stash: T::AccountId,
1867 era: EraIndex,
1868 ) -> DispatchResultWithPostInfo {
1869 ensure_signed(origin)?;
1870
1871 Self::do_payout_stakers(validator_stash, era)
1872 }
1873
1874 #[pallet::call_index(19)]
1878 #[pallet::weight(T::WeightInfo::rebond(T::MaxUnlockingChunks::get() as u32))]
1879 pub fn rebond(
1880 origin: OriginFor<T>,
1881 #[pallet::compact] value: BalanceOf<T>,
1882 ) -> DispatchResultWithPostInfo {
1883 let controller = ensure_signed(origin)?;
1884 let ledger = Self::ledger(Controller(controller))?;
1885
1886 ensure!(!T::Filter::contains(&ledger.stash), Error::<T>::Restricted);
1887 ensure!(!ledger.unlocking.is_empty(), Error::<T>::NoUnlockChunk);
1888
1889 let initial_unlocking = ledger.unlocking.len() as u32;
1890 let (ledger, rebonded_value) = ledger.rebond(value);
1891 ensure!(ledger.active >= Self::min_chilled_bond(), Error::<T>::InsufficientBond);
1893
1894 Self::deposit_event(Event::<T>::Bonded {
1895 stash: ledger.stash.clone(),
1896 amount: rebonded_value,
1897 });
1898
1899 let stash = ledger.stash.clone();
1900 let final_unlocking = ledger.unlocking.len();
1901
1902 ledger.update()?;
1904 if T::VoterList::contains(&stash) {
1905 let _ = T::VoterList::on_update(&stash, Self::weight_of(&stash));
1906 }
1907
1908 let removed_chunks = 1u32 .saturating_add(initial_unlocking)
1910 .saturating_sub(final_unlocking as u32);
1911 Ok(Some(T::WeightInfo::rebond(removed_chunks)).into())
1912 }
1913
1914 #[pallet::call_index(20)]
1934 #[pallet::weight(T::WeightInfo::reap_stash())]
1935 pub fn reap_stash(
1936 origin: OriginFor<T>,
1937 stash: T::AccountId,
1938 _num_slashing_spans: u32,
1939 ) -> DispatchResultWithPostInfo {
1940 let _ = ensure_signed(origin)?;
1941
1942 ensure!(!Self::is_virtual_staker(&stash), Error::<T>::VirtualStakerNotAllowed);
1944
1945 let min_chilled_bond = Self::min_chilled_bond();
1946 let origin_balance = asset::total_balance::<T>(&stash);
1947 let ledger_total =
1948 Self::ledger(Stash(stash.clone())).map(|l| l.total).unwrap_or_default();
1949 let reapable = origin_balance < min_chilled_bond ||
1950 origin_balance.is_zero() ||
1951 ledger_total < min_chilled_bond ||
1952 ledger_total.is_zero();
1953 ensure!(reapable, Error::<T>::FundedTarget);
1954
1955 Self::kill_stash(&stash)?;
1957
1958 Ok(Pays::No.into())
1959 }
1960
1961 #[pallet::call_index(21)]
1973 #[pallet::weight(T::WeightInfo::kick(who.len() as u32))]
1974 pub fn kick(origin: OriginFor<T>, who: Vec<AccountIdLookupOf<T>>) -> DispatchResult {
1975 let controller = ensure_signed(origin)?;
1976 let ledger = Self::ledger(Controller(controller))?;
1977 let stash = &ledger.stash;
1978
1979 for nom_stash in who
1980 .into_iter()
1981 .map(T::Lookup::lookup)
1982 .collect::<Result<Vec<T::AccountId>, _>>()?
1983 .into_iter()
1984 {
1985 Nominators::<T>::mutate(&nom_stash, |maybe_nom| {
1986 if let Some(ref mut nom) = maybe_nom {
1987 if let Some(pos) = nom.targets.iter().position(|v| v == stash) {
1988 nom.targets.swap_remove(pos);
1989 Self::deposit_event(Event::<T>::Kicked {
1990 nominator: nom_stash.clone(),
1991 stash: stash.clone(),
1992 });
1993 }
1994 }
1995 });
1996 }
1997
1998 Ok(())
1999 }
2000
2001 #[pallet::call_index(22)]
2021 #[pallet::weight(
2022 T::WeightInfo::set_staking_configs_all_set()
2023 .max(T::WeightInfo::set_staking_configs_all_remove())
2024 )]
2025 pub fn set_staking_configs(
2026 origin: OriginFor<T>,
2027 min_nominator_bond: ConfigOp<BalanceOf<T>>,
2028 min_validator_bond: ConfigOp<BalanceOf<T>>,
2029 max_nominator_count: ConfigOp<u32>,
2030 max_validator_count: ConfigOp<u32>,
2031 chill_threshold: ConfigOp<Percent>,
2032 min_commission: ConfigOp<Perbill>,
2033 max_staked_rewards: ConfigOp<Percent>,
2034 ) -> DispatchResult {
2035 ensure_root(origin)?;
2036
2037 macro_rules! config_op_exp {
2038 ($storage:ty, $op:ident) => {
2039 match $op {
2040 ConfigOp::Noop => (),
2041 ConfigOp::Set(v) => <$storage>::put(v),
2042 ConfigOp::Remove => <$storage>::kill(),
2043 }
2044 };
2045 }
2046
2047 config_op_exp!(MinNominatorBond<T>, min_nominator_bond);
2048 config_op_exp!(MinValidatorBond<T>, min_validator_bond);
2049 config_op_exp!(MaxNominatorsCount<T>, max_nominator_count);
2050 config_op_exp!(MaxValidatorsCount<T>, max_validator_count);
2051 config_op_exp!(ChillThreshold<T>, chill_threshold);
2052 config_op_exp!(MinCommission<T>, min_commission);
2053 config_op_exp!(MaxStakedRewards<T>, max_staked_rewards);
2054 Ok(())
2055 }
2056 #[pallet::call_index(23)]
2083 #[pallet::weight(T::WeightInfo::chill_other())]
2084 pub fn chill_other(origin: OriginFor<T>, stash: T::AccountId) -> DispatchResult {
2085 let caller = ensure_signed(origin)?;
2087 let ledger = Self::ledger(Stash(stash.clone()))?;
2088 let controller = ledger
2089 .controller()
2090 .defensive_proof(
2091 "Ledger's controller field didn't exist. The controller should have been fetched using StakingLedger.",
2092 )
2093 .ok_or(Error::<T>::NotController)?;
2094
2095 if Nominators::<T>::contains_key(&stash) && Nominators::<T>::get(&stash).is_none() {
2112 Self::chill_stash(&stash);
2113 return Ok(());
2114 }
2115
2116 if caller != controller {
2117 let threshold = ChillThreshold::<T>::get().ok_or(Error::<T>::CannotChillOther)?;
2118 let min_active_bond = if Nominators::<T>::contains_key(&stash) {
2119 let max_nominator_count =
2120 MaxNominatorsCount::<T>::get().ok_or(Error::<T>::CannotChillOther)?;
2121 let current_nominator_count = Nominators::<T>::count();
2122 ensure!(
2123 threshold * max_nominator_count < current_nominator_count,
2124 Error::<T>::CannotChillOther
2125 );
2126 Self::min_nominator_bond()
2127 } else if Validators::<T>::contains_key(&stash) {
2128 let max_validator_count =
2129 MaxValidatorsCount::<T>::get().ok_or(Error::<T>::CannotChillOther)?;
2130 let current_validator_count = Validators::<T>::count();
2131 ensure!(
2132 threshold * max_validator_count < current_validator_count,
2133 Error::<T>::CannotChillOther
2134 );
2135 Self::min_validator_bond()
2136 } else {
2137 Zero::zero()
2138 };
2139
2140 ensure!(ledger.active < min_active_bond, Error::<T>::CannotChillOther);
2141 }
2142
2143 Self::chill_stash(&stash);
2144 Ok(())
2145 }
2146
2147 #[pallet::call_index(24)]
2151 #[pallet::weight(T::WeightInfo::force_apply_min_commission())]
2152 pub fn force_apply_min_commission(
2153 origin: OriginFor<T>,
2154 validator_stash: T::AccountId,
2155 ) -> DispatchResult {
2156 ensure_signed(origin)?;
2157 let min_commission = MinCommission::<T>::get();
2158 Validators::<T>::try_mutate_exists(validator_stash, |maybe_prefs| {
2159 maybe_prefs
2160 .as_mut()
2161 .map(|prefs| {
2162 (prefs.commission < min_commission)
2163 .then(|| prefs.commission = min_commission)
2164 })
2165 .ok_or(Error::<T>::NotStash)
2166 })?;
2167 Ok(())
2168 }
2169
2170 #[pallet::call_index(25)]
2175 #[pallet::weight(T::WeightInfo::set_min_commission())]
2176 pub fn set_min_commission(origin: OriginFor<T>, new: Perbill) -> DispatchResult {
2177 T::AdminOrigin::ensure_origin(origin)?;
2178 MinCommission::<T>::put(new);
2179 Ok(())
2180 }
2181
2182 #[pallet::call_index(26)]
2200 #[pallet::weight(T::WeightInfo::payout_stakers_alive_staked(T::MaxExposurePageSize::get()))]
2201 pub fn payout_stakers_by_page(
2202 origin: OriginFor<T>,
2203 validator_stash: T::AccountId,
2204 era: EraIndex,
2205 page: Page,
2206 ) -> DispatchResultWithPostInfo {
2207 ensure_signed(origin)?;
2208 Self::do_payout_stakers_by_page(validator_stash, era, page)
2209 }
2210
2211 #[pallet::call_index(27)]
2218 #[pallet::weight(T::WeightInfo::update_payee())]
2219 pub fn update_payee(
2220 origin: OriginFor<T>,
2221 controller: T::AccountId,
2222 ) -> DispatchResultWithPostInfo {
2223 let _ = ensure_signed(origin)?;
2224 let ledger = Self::ledger(StakingAccount::Controller(controller.clone()))?;
2225
2226 ensure!(
2227 (Payee::<T>::get(&ledger.stash) == {
2228 #[allow(deprecated)]
2229 Some(RewardDestination::Controller)
2230 }),
2231 Error::<T>::NotController
2232 );
2233
2234 let _ = ledger
2235 .set_payee(RewardDestination::Account(controller))
2236 .defensive_proof("ledger should have been previously retrieved from storage.")?;
2237
2238 Ok(Pays::No.into())
2239 }
2240
2241 #[pallet::call_index(28)]
2249 #[pallet::weight(T::WeightInfo::deprecate_controller_batch(controllers.len() as u32))]
2250 pub fn deprecate_controller_batch(
2251 origin: OriginFor<T>,
2252 controllers: BoundedVec<T::AccountId, T::MaxControllersInDeprecationBatch>,
2253 ) -> DispatchResultWithPostInfo {
2254 T::AdminOrigin::ensure_origin(origin)?;
2255
2256 let filtered_batch_with_ledger: Vec<_> = controllers
2258 .iter()
2259 .filter_map(|controller| {
2260 let ledger = Self::ledger(StakingAccount::Controller(controller.clone()));
2261 ledger.ok().map_or(None, |ledger| {
2262 let payee_deprecated = Payee::<T>::get(&ledger.stash) == {
2265 #[allow(deprecated)]
2266 Some(RewardDestination::Controller)
2267 };
2268
2269 if ledger.stash != *controller && !payee_deprecated {
2270 Some(ledger)
2271 } else {
2272 None
2273 }
2274 })
2275 })
2276 .collect();
2277
2278 let mut failures = 0;
2280 for ledger in filtered_batch_with_ledger {
2281 let _ = ledger.clone().set_controller_to_stash().map_err(|_| failures += 1);
2282 }
2283 Self::deposit_event(Event::<T>::ControllerBatchDeprecated { failures });
2284
2285 Ok(Some(T::WeightInfo::deprecate_controller_batch(controllers.len() as u32)).into())
2286 }
2287
2288 #[pallet::call_index(29)]
2300 #[pallet::weight(T::WeightInfo::restore_ledger())]
2301 pub fn restore_ledger(
2302 origin: OriginFor<T>,
2303 stash: T::AccountId,
2304 maybe_controller: Option<T::AccountId>,
2305 maybe_total: Option<BalanceOf<T>>,
2306 maybe_unlocking: Option<BoundedVec<UnlockChunk<BalanceOf<T>>, T::MaxUnlockingChunks>>,
2307 ) -> DispatchResult {
2308 T::AdminOrigin::ensure_origin(origin)?;
2309
2310 ensure!(!Self::is_virtual_staker(&stash), Error::<T>::VirtualStakerNotAllowed);
2312
2313 let current_lock = asset::staked::<T>(&stash);
2314 let stash_balance = asset::stakeable_balance::<T>(&stash);
2315
2316 let (new_controller, new_total) = match Self::inspect_bond_state(&stash) {
2317 Ok(LedgerIntegrityState::Corrupted) => {
2318 let new_controller = maybe_controller.unwrap_or(stash.clone());
2319
2320 let new_total = if let Some(total) = maybe_total {
2321 let new_total = total.min(stash_balance);
2322 asset::update_stake::<T>(&stash, new_total)?;
2324 new_total
2325 } else {
2326 current_lock
2327 };
2328
2329 Ok((new_controller, new_total))
2330 },
2331 Ok(LedgerIntegrityState::CorruptedKilled) => {
2332 if current_lock == Zero::zero() {
2333 ensure!(maybe_total.is_some(), Error::<T>::CannotRestoreLedger);
2337 Ok((
2338 stash.clone(),
2339 maybe_total.expect("total exists as per the check above; qed."),
2340 ))
2341 } else {
2342 Ok((stash.clone(), current_lock))
2343 }
2344 },
2345 Ok(LedgerIntegrityState::LockCorrupted) => {
2346 let new_total =
2349 maybe_total.ok_or(Error::<T>::CannotRestoreLedger)?.min(stash_balance);
2350 asset::update_stake::<T>(&stash, new_total)?;
2351
2352 Ok((stash.clone(), new_total))
2353 },
2354 Err(Error::<T>::BadState) => {
2355 asset::kill_stake::<T>(&stash)?;
2357 ensure!(
2358 Self::inspect_bond_state(&stash) == Err(Error::<T>::NotStash),
2359 Error::<T>::BadState
2360 );
2361
2362 return Ok(());
2363 },
2364 Ok(LedgerIntegrityState::Ok) | Err(_) => Err(Error::<T>::CannotRestoreLedger),
2365 }?;
2366
2367 Bonded::<T>::insert(&stash, &new_controller);
2369
2370 let mut ledger = StakingLedger::<T>::new(stash.clone(), new_total);
2372 ledger.controller = Some(new_controller);
2373 ledger.unlocking = maybe_unlocking.unwrap_or_default();
2374 ledger.update()?;
2375
2376 ensure!(
2377 Self::inspect_bond_state(&stash) == Ok(LedgerIntegrityState::Ok),
2378 Error::<T>::BadState
2379 );
2380 Ok(())
2381 }
2382
2383 #[pallet::call_index(30)]
2391 #[pallet::weight(T::WeightInfo::migrate_currency())]
2392 pub fn migrate_currency(
2393 origin: OriginFor<T>,
2394 stash: T::AccountId,
2395 ) -> DispatchResultWithPostInfo {
2396 let _ = ensure_signed(origin)?;
2397 Self::do_migrate_currency(&stash)?;
2398
2399 Ok(Pays::No.into())
2401 }
2402
2403 #[pallet::call_index(31)]
2426 #[pallet::weight(T::WeightInfo::apply_slash())]
2427 pub fn apply_slash(
2428 origin: OriginFor<T>,
2429 slash_era: EraIndex,
2430 slash_key: (T::AccountId, Perbill, u32),
2431 ) -> DispatchResultWithPostInfo {
2432 let _ = ensure_signed(origin)?;
2433 let active_era = ActiveEra::<T>::get().map(|a| a.index).unwrap_or_default();
2434 ensure!(slash_era <= active_era, Error::<T>::EraNotStarted);
2435 let unapplied_slash = UnappliedSlashes::<T>::take(&slash_era, &slash_key)
2436 .ok_or(Error::<T>::InvalidSlashRecord)?;
2437 slashing::apply_slash::<T>(unapplied_slash, slash_era);
2438
2439 Ok(Pays::No.into())
2440 }
2441
2442 #[pallet::call_index(32)]
2449 #[pallet::weight(T::DbWeight::get().reads_writes(2, 1))]
2450 pub fn withdraw_overstake(origin: OriginFor<T>, stash: T::AccountId) -> DispatchResult {
2451 use sp_runtime::Saturating;
2452 let _ = ensure_signed(origin)?;
2453
2454 let ledger = Self::ledger(Stash(stash.clone()))?;
2455 let actual_stake = asset::staked::<T>(&stash);
2456 let force_withdraw_amount = ledger.total.defensive_saturating_sub(actual_stake);
2457
2458 ensure!(!force_withdraw_amount.is_zero(), Error::<T>::BoundNotMet);
2460
2461 StakingLedger {
2464 total: actual_stake,
2465 active: ledger.active.saturating_sub(force_withdraw_amount),
2466 ..ledger
2467 }
2468 .update()?;
2469
2470 Self::deposit_event(Event::<T>::Withdrawn { stash, amount: force_withdraw_amount });
2471
2472 Ok(())
2473 }
2474 }
2475}