referrerpolicy=no-referrer-when-downgrade

pallet_staking_async/pallet/
mod.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! `pallet-staking-async`'s main `pallet` module.
19
20use crate::{
21	asset, session_rotation::EraElectionPlanner, slashing, weights::WeightInfo, AccountIdLookupOf,
22	ActiveEraInfo, BalanceOf, EraPayout, EraRewardPoints, ExposurePage, Forcing,
23	LedgerIntegrityState, MaxNominationsOf, NegativeImbalanceOf, Nominations, NominationsQuota,
24	PositiveImbalanceOf, RewardDestination, StakingLedger, UnappliedSlash, UnlockChunk,
25	ValidatorPrefs,
26};
27use alloc::{format, vec::Vec};
28use codec::Codec;
29use frame_election_provider_support::{ElectionProvider, SortedListProvider, VoteWeight};
30use frame_support::{
31	assert_ok,
32	pallet_prelude::*,
33	traits::{
34		fungible::{
35			hold::{Balanced as FunHoldBalanced, Mutate as FunHoldMutate},
36			Mutate, Mutate as FunMutate,
37		},
38		Contains, Defensive, DefensiveSaturating, EnsureOrigin, Get, InspectLockableCurrency,
39		Nothing, OnUnbalanced,
40	},
41	weights::Weight,
42	BoundedBTreeSet, BoundedVec,
43};
44use frame_system::{ensure_root, ensure_signed, pallet_prelude::*};
45pub use impls::*;
46use rand::seq::SliceRandom;
47use rand_chacha::{
48	rand_core::{RngCore, SeedableRng},
49	ChaChaRng,
50};
51use sp_core::{sr25519::Pair as SrPair, Pair};
52use sp_runtime::{
53	traits::{StaticLookup, Zero},
54	ArithmeticError, Perbill, Percent,
55};
56use sp_staking::{
57	EraIndex, Page, SessionIndex,
58	StakingAccount::{self, Controller, Stash},
59	StakingInterface,
60};
61
62mod impls;
63
64#[frame_support::pallet]
65pub mod pallet {
66	use core::ops::Deref;
67
68	use super::*;
69	use crate::{
70		session_rotation::{self, Eras, Rotator},
71		IsValidatorInactive, OnEraStart, PagedExposureMetadata, SnapshotStatus,
72	};
73	use codec::HasCompact;
74	use frame_election_provider_support::{ElectionDataProvider, PageIndex};
75	use frame_support::{traits::ConstBool, weights::WeightMeter, DefaultNoBound, PalletError};
76
77	/// Dimensionless weight from the validator self-stake incentive curve. Same underlying type as
78	/// `BalanceOf<T>` for arithmetic compatibility, but represents the output of the sqrt weight
79	/// function.
80	pub(crate) type IncentiveWeight<T> = BalanceOf<T>;
81
82	/// Represents the current step in the era pruning process
83	#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, Debug, TypeInfo, MaxEncodedLen)]
84	pub enum PruningStep {
85		/// Pruning ErasStakersPaged storage
86		ErasStakersPaged,
87		/// Pruning ErasStakersOverview storage
88		ErasStakersOverview,
89		/// Pruning ErasValidatorPrefs storage
90		ErasValidatorPrefs,
91		/// Pruning ClaimedRewards storage
92		ClaimedRewards,
93		/// Pruning ErasValidatorReward storage
94		ErasValidatorReward,
95		/// Pruning ErasRewardPoints storage
96		ErasRewardPoints,
97		/// Pruning single-entry storages
98		SingleEntryCleanups,
99		/// Pruning ValidatorSlashInEra storage
100		ValidatorSlashInEra,
101		/// Pruning ErasValidatorIncentiveWeight storage
102		ErasValidatorIncentiveWeight,
103	}
104
105	/// The in-code storage version.
106	const STORAGE_VERSION: StorageVersion = StorageVersion::new(18);
107
108	#[pallet::pallet]
109	#[pallet::storage_version(STORAGE_VERSION)]
110	pub struct Pallet<T>(_);
111
112	/// Possible operations on the configuration values of this pallet.
113	#[derive(TypeInfo, Debug, Clone, Encode, Decode, DecodeWithMemTracking, PartialEq)]
114	pub enum ConfigOp<T: Default + Codec> {
115		/// Don't change.
116		Noop,
117		/// Set the given value.
118		Set(T),
119		/// Remove from storage.
120		Remove,
121	}
122
123	#[pallet::config(with_default)]
124	pub trait Config: frame_system::Config {
125		/// The old trait for staking balance. Deprecated and only used for migrating old ledgers.
126		#[pallet::no_default]
127		type OldCurrency: InspectLockableCurrency<
128			Self::AccountId,
129			Moment = BlockNumberFor<Self>,
130			Balance = Self::CurrencyBalance,
131		>;
132
133		/// The staking balance.
134		#[pallet::no_default]
135		type Currency: FunHoldMutate<
136				Self::AccountId,
137				Reason = Self::RuntimeHoldReason,
138				Balance = Self::CurrencyBalance,
139			> + FunMutate<Self::AccountId, Balance = Self::CurrencyBalance>
140			+ FunHoldBalanced<Self::AccountId, Balance = Self::CurrencyBalance>;
141
142		/// Overarching hold reason.
143		#[pallet::no_default_bounds]
144		type RuntimeHoldReason: From<HoldReason>;
145
146		/// Just the `Currency::Balance` type; we have this item to allow us to constrain it to
147		/// `From<u64>`.
148		type CurrencyBalance: sp_runtime::traits::AtLeast32BitUnsigned
149			+ codec::FullCodec
150			+ DecodeWithMemTracking
151			+ HasCompact<Type: DecodeWithMemTracking>
152			+ Copy
153			+ MaybeSerializeDeserialize
154			+ core::fmt::Debug
155			+ Default
156			+ From<u64>
157			+ TypeInfo
158			+ Send
159			+ Sync
160			+ MaxEncodedLen;
161
162		/// Convert a balance into a number used for election calculation. This must fit into a
163		/// `u64` but is allowed to be sensibly lossy. The `u64` is used to communicate with the
164		/// [`frame_election_provider_support`] crate which accepts u64 numbers and does operations
165		/// in 128.
166		/// Consequently, the backward convert is used convert the u128s from sp-elections back to a
167		/// [`BalanceOf`].
168		#[pallet::no_default_bounds]
169		type CurrencyToVote: sp_staking::currency_to_vote::CurrencyToVote<BalanceOf<Self>>;
170
171		/// Something that provides the election functionality.
172		#[pallet::no_default]
173		type ElectionProvider: ElectionProvider<
174			AccountId = Self::AccountId,
175			BlockNumber = BlockNumberFor<Self>,
176			// we only accept an election provider that has staking as data provider.
177			DataProvider = Pallet<Self>,
178		>;
179
180		/// Something that defines the maximum number of nominations per nominator.
181		#[pallet::no_default_bounds]
182		type NominationsQuota: NominationsQuota<BalanceOf<Self>>;
183
184		/// Number of eras to keep in history.
185		///
186		/// Following information is kept for eras in `[current_era -
187		/// HistoryDepth, current_era]`: `ErasValidatorPrefs`, `ErasValidatorReward`,
188		/// `ErasRewardPoints`, `ErasTotalStake`, `ClaimedRewards`,
189		/// `ErasStakersPaged`, `ErasStakersOverview`.
190		///
191		/// Must be more than the number of eras delayed by session.
192		/// I.e. active era must always be in history. I.e. `active_era >
193		/// current_era - history_depth` must be guaranteed.
194		///
195		/// If migrating an existing pallet from storage value to config value,
196		/// this should be set to same value or greater as in storage.
197		#[pallet::constant]
198		type HistoryDepth: Get<u32>;
199
200		/// Tokens have been minted and are unused for validator-reward.
201		///
202		/// Only used in legacy minting mode (`DisableMinting = false`).
203		#[pallet::no_default_bounds]
204		type RewardRemainder: OnUnbalanced<NegativeImbalanceOf<Self>>;
205
206		/// Handler for the unbalanced reduction when slashing a staker.
207		#[pallet::no_default_bounds]
208		type Slash: OnUnbalanced<NegativeImbalanceOf<Self>>;
209
210		/// Handler for the unbalanced increment when rewarding a staker.
211		/// NOTE: in most cases, the implementation of `OnUnbalanced` should modify the total
212		/// issuance.
213		///
214		/// Only used in legacy minting mode (`DisableMinting = false`).
215		#[pallet::no_default_bounds]
216		type Reward: OnUnbalanced<PositiveImbalanceOf<Self>>;
217
218		/// Number of sessions per era, as per the preferences of the **relay chain**.
219		#[pallet::constant]
220		type SessionsPerEra: Get<SessionIndex>;
221
222		/// Number of sessions before the end of an era when the election for the next era will
223		/// start.
224		///
225		/// - This determines how many sessions **before** the last session of the era the staking
226		///   election process should begin.
227		/// - The value is bounded between **1** (election starts at the beginning of the last
228		///   session) and `SessionsPerEra` (election starts at the beginning of the first session
229		///   of the era).
230		///
231		/// ### Example:
232		/// - If `SessionsPerEra = 6` and `PlanningEraOffset = 1`, the election starts at the
233		///   beginning of session `6 - 1 = 5`.
234		/// - If `PlanningEraOffset = 6`, the election starts at the beginning of session `6 - 6 =
235		///   0`, meaning it starts at the very beginning of the era.
236		#[pallet::constant]
237		type PlanningEraOffset: Get<SessionIndex>;
238
239		/// Number of eras that staked funds must remain bonded for.
240		///
241		/// This is the bonding duration for validators. Nominators may have a shorter bonding
242		/// duration when [`AreNominatorsSlashable`] is set to `false` (see
243		/// [`StakingInterface::nominator_bonding_duration`]).
244		#[pallet::constant]
245		type BondingDuration: Get<EraIndex>;
246
247		/// Number of eras nominators must wait to unbond when they are not slashable.
248		///
249		/// This duration is used for nominators when [`AreNominatorsSlashable`] is `false`.
250		/// When nominators are slashable, they use the full [`Config::BondingDuration`] to ensure
251		/// slashes can be applied during the unbonding period.
252		///
253		/// Setting this to a lower value (e.g., 1 era) allows for faster withdrawals when
254		/// nominators are not subject to slashing risk.
255		#[pallet::constant]
256		type NominatorFastUnbondDuration: Get<EraIndex>;
257
258		/// Number of eras that slashes are deferred by, after computation.
259		///
260		/// This should be less than the bonding duration. Set to 0 if slashes
261		/// should be applied immediately, without opportunity for intervention.
262		#[pallet::constant]
263		type SlashDeferDuration: Get<EraIndex>;
264
265		/// The origin which can manage less critical staking parameters that does not require root.
266		///
267		/// Supported actions: (1) cancel deferred slash, (2) set minimum commission.
268		#[pallet::no_default]
269		type AdminOrigin: EnsureOrigin<Self::RuntimeOrigin>;
270
271		/// The payout for validators and the system for the current era.
272		/// See [Era payout](./index.html#era-payout).
273		///
274		/// Only used in legacy minting mode (`DisableMinting = false`).
275		/// Should be set to () in non-minting mode.
276		#[pallet::no_default]
277		type EraPayout: EraPayout<BalanceOf<Self>>;
278
279		/// When `true`, staking does not mint. It expects an external source to fund
280		/// the general reward pot. At era boundary, rewards are snapshotted from
281		/// the pot. `EraPayout` is not called.
282		///
283		/// When `false`, staking uses the legacy path: `EraPayout` computes inflation,
284		/// tokens are minted on-the-fly during payout.
285		///
286		/// **Irreversible**: once set to `true`, must never be switched back. Eras
287		/// created in non-minting mode have funded reward pots — switching to legacy
288		/// would orphan those pots and cause double-minting.
289		#[pallet::constant]
290		type DisableMinting: Get<bool>;
291
292		/// Handler for unclaimed era rewards (non-minting mode only).
293		///
294		/// When era pots are cleaned up past `HistoryDepth`, remaining funds are
295		/// withdrawn and passed to this handler.
296		#[pallet::no_default_bounds]
297		type UnclaimedRewardHandler: OnUnbalanced<NegativeImbalanceOf<Self>>;
298
299		/// Provider for generating reward pot account IDs (non-minting mode only).
300		///
301		/// Provides both general pots (funded by an external source like pallet-dap)
302		/// and era-specific pots (snapshotted at era boundaries).
303		#[pallet::no_default]
304		type RewardPots: crate::PotAccountProvider<Self::AccountId>;
305
306		/// Calculator for staker rewards.
307		///
308		/// Determines how staking rewards are distributed between validators and nominators.
309		#[pallet::no_default_bounds]
310		type StakerRewardCalculator: sp_staking::StakerRewardCalculator<BalanceOf<Self>>;
311
312		/// The maximum size of each `T::ExposurePage`.
313		///
314		/// An `ExposurePage` is weakly bounded to a maximum of `MaxExposurePageSize`
315		/// nominators.
316		///
317		/// For older non-paged exposure, a reward payout was restricted to the top
318		/// `MaxExposurePageSize` nominators. This is to limit the i/o cost for the
319		/// nominator payout.
320		///
321		/// Note: `MaxExposurePageSize` is used to bound `ClaimedRewards` and is unsafe to
322		/// reduce without handling it in a migration.
323		#[pallet::constant]
324		type MaxExposurePageSize: Get<u32>;
325
326		/// The absolute maximum of winner validators this pallet should return.
327		///
328		/// As this pallet supports multi-block election, the set of winner validators *per
329		/// election* is bounded by this type.
330		#[pallet::constant]
331		type MaxValidatorSet: Get<u32>;
332
333		/// Something that provides a best-effort sorted list of voters aka electing nominators,
334		/// used for NPoS election.
335		///
336		/// The changes to nominators are reported to this. Moreover, each validator's self-vote is
337		/// also reported as one independent vote.
338		///
339		/// To keep the load off the chain as much as possible, changes made to the staked amount
340		/// via rewards and slashes are not reported and thus need to be manually fixed by the
341		/// staker. In case of `bags-list`, this always means using `rebag` and `putInFrontOf`.
342		///
343		/// Invariant: what comes out of this list will always be a nominator.
344		#[pallet::no_default]
345		type VoterList: SortedListProvider<Self::AccountId, Score = VoteWeight>;
346
347		/// WIP: This is a noop as of now, the actual business logic that's described below is going
348		/// to be introduced in a follow-up PR.
349		///
350		/// Something that provides a best-effort sorted list of targets aka electable validators,
351		/// used for NPoS election.
352		///
353		/// The changes to the approval stake of each validator are reported to this. This means any
354		/// change to:
355		/// 1. The stake of any validator or nominator.
356		/// 2. The targets of any nominator
357		/// 3. The role of any staker (e.g. validator -> chilled, nominator -> validator, etc)
358		///
359		/// Unlike `VoterList`, the values in this list are always kept up to date with reward and
360		/// slash as well, and thus represent the accurate approval stake of all account being
361		/// nominated by nominators.
362		///
363		/// Note that while at the time of nomination, all targets are checked to be real
364		/// validators, they can chill at any point, and their approval stakes will still be
365		/// recorded. This implies that what comes out of iterating this list MIGHT NOT BE AN ACTIVE
366		/// VALIDATOR.
367		#[pallet::no_default]
368		type TargetList: SortedListProvider<Self::AccountId, Score = BalanceOf<Self>>;
369
370		/// The maximum number of `unlocking` chunks a [`StakingLedger`] can
371		/// have. Effectively determines how many unique eras a staker may be
372		/// unbonding in.
373		///
374		/// Note: `MaxUnlockingChunks` is used as the upper bound for the
375		/// `BoundedVec` item `StakingLedger.unlocking`. Setting this value
376		/// lower than the existing value can lead to inconsistencies in the
377		/// `StakingLedger` and will need to be handled properly in a runtime
378		/// migration. The test `reducing_max_unlocking_chunks_abrupt` shows
379		/// this effect.
380		#[pallet::constant]
381		type MaxUnlockingChunks: Get<u32>;
382
383		/// The maximum amount of controller accounts that can be deprecated in one call.
384		type MaxControllersInDeprecationBatch: Get<u32>;
385
386		/// Something that listens to staking updates and performs actions based on the data it
387		/// receives.
388		///
389		/// WARNING: this only reports slashing and withdraw events for the time being.
390		#[pallet::no_default_bounds]
391		type EventListeners: sp_staking::OnStakingUpdate<Self::AccountId, BalanceOf<Self>>;
392
393		/// Maximum allowed era duration in milliseconds.
394		///
395		/// This provides a defensive upper bound to cap the effective era duration, preventing
396		/// excessively long eras from causing runaway inflation (e.g., due to bugs). If the actual
397		/// era duration exceeds this value, it will be clamped to this maximum.
398		///
399		/// Example: For an ideal era duration of 24 hours (86,400,000 ms),
400		/// this can be set to 604,800,000 ms (7 days).
401		///
402		/// Only used in legacy minting mode (`DisableMinting = false`).
403		#[pallet::constant]
404		type MaxEraDuration: Get<u64>;
405
406		/// Maximum number of storage items that can be pruned in a single call.
407		///
408		/// This controls how many storage items can be deleted in each call to `prune_era_step`.
409		/// This should be set to a conservative value (e.g., 100-500 items) to ensure pruning
410		/// doesn't consume too much block space. The actual weight is determined by benchmarks.
411		#[pallet::constant]
412		type MaxPruningItems: Get<u32>;
413
414		/// Interface to talk to the RC-Client pallet, possibly sending election results to the
415		/// relay chain.
416		#[pallet::no_default]
417		type RcClientInterface: pallet_staking_async_rc_client::RcClientInterface<
418			AccountId = Self::AccountId,
419		>;
420
421		#[pallet::no_default_bounds]
422		/// Filter some accounts from participating in staking.
423		///
424		/// This is useful for example to blacklist an account that is participating in staking in
425		/// another way (such as pools).
426		type Filter: Contains<Self::AccountId>;
427
428		/// Weight information for extrinsics in this pallet.
429		type WeightInfo: WeightInfo;
430
431		/// Check if validator was inactive at some era.
432		///
433		/// This check is used by `chill_inactive` extrinsic to check which
434		/// validators can be chilled.
435		type IsValidatorInactive: IsValidatorInactive<Self::AccountId>;
436
437		/// Notified with the validators of each era when it becomes active.
438		#[pallet::no_default_bounds]
439		type OnEraStart: OnEraStart<Self::AccountId>;
440	}
441
442	/// A reason for placing a hold on funds.
443	#[pallet::composite_enum]
444	pub enum HoldReason {
445		/// Funds on stake by a nominator or a validator.
446		#[codec(index = 0)]
447		Staking,
448	}
449
450	/// Default implementations of [`DefaultConfig`], which can be used to implement [`Config`].
451	pub mod config_preludes {
452		use super::*;
453		use frame_support::{derive_impl, parameter_types, traits::ConstU32};
454		pub struct TestDefaultConfig;
455
456		#[derive_impl(frame_system::config_preludes::TestDefaultConfig, no_aggregated_types)]
457		impl frame_system::DefaultConfig for TestDefaultConfig {}
458
459		parameter_types! {
460			pub const SessionsPerEra: SessionIndex = 3;
461			pub const BondingDuration: EraIndex = 3;
462			pub const NominatorFastUnbondDuration: EraIndex = 2;
463			pub const MaxPruningItems: u32 = 100;
464		}
465
466		#[frame_support::register_default_impl(TestDefaultConfig)]
467		impl DefaultConfig for TestDefaultConfig {
468			#[inject_runtime_type]
469			type RuntimeHoldReason = ();
470			type CurrencyBalance = u128;
471			type CurrencyToVote = ();
472			type NominationsQuota = crate::FixedNominationsQuota<16>;
473			type HistoryDepth = ConstU32<84>;
474			type RewardRemainder = ();
475			type Slash = ();
476			type Reward = ();
477			type UnclaimedRewardHandler = ();
478			type StakerRewardCalculator = ();
479			type DisableMinting = ConstBool<false>;
480			type SessionsPerEra = SessionsPerEra;
481			type BondingDuration = BondingDuration;
482			type NominatorFastUnbondDuration = NominatorFastUnbondDuration;
483			type PlanningEraOffset = ConstU32<1>;
484			type SlashDeferDuration = ();
485			type MaxExposurePageSize = ConstU32<64>;
486			type MaxUnlockingChunks = ConstU32<32>;
487			type MaxValidatorSet = ConstU32<100>;
488			type MaxControllersInDeprecationBatch = ConstU32<100>;
489			type MaxEraDuration = ();
490			type MaxPruningItems = MaxPruningItems;
491			type EventListeners = ();
492			type Filter = Nothing;
493			type WeightInfo = ();
494			type IsValidatorInactive = ();
495			type OnEraStart = ();
496		}
497	}
498
499	/// The ideal number of active validators.
500	#[pallet::storage]
501	pub type ValidatorCount<T> = StorageValue<_, u32, ValueQuery>;
502
503	/// Map from all locked "stash" accounts to the controller account.
504	///
505	/// TWOX-NOTE: SAFE since `AccountId` is a secure hash.
506	#[pallet::storage]
507	pub type Bonded<T: Config> = StorageMap<_, Twox64Concat, T::AccountId, T::AccountId>;
508
509	/// The minimum active bond to become and maintain the role of a nominator.
510	#[pallet::storage]
511	pub type MinNominatorBond<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;
512
513	/// The minimum active bond to become and maintain the role of a validator.
514	#[pallet::storage]
515	pub type MinValidatorBond<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;
516
517	/// The minimum active nominator stake of the last successful election.
518	#[pallet::storage]
519	pub type MinimumActiveStake<T> = StorageValue<_, BalanceOf<T>, ValueQuery>;
520
521	/// The minimum amount of commission that validators can set.
522	///
523	/// If set to `0`, no limit exists.
524	#[pallet::storage]
525	pub type MinCommission<T: Config> = StorageValue<_, Perbill, ValueQuery>;
526
527	/// The maximum commission that validators can set.
528	///
529	/// If not set, defaults to `Perbill::one()` (100%), i.e. no upper limit.
530	#[pallet::storage]
531	pub type MaxCommission<T: Config> = StorageValue<_, Perbill, ValueQuery, MaxCommissionDefault>;
532
533	/// Default for MaxCommission: 100% (no restriction).
534	pub struct MaxCommissionDefault;
535	impl Get<Perbill> for MaxCommissionDefault {
536		fn get() -> Perbill {
537			Perbill::one()
538		}
539	}
540
541	/// Safety guard: the era from which legacy minting is permanently disabled on the
542	/// payout side. **Irreversible** — once set, should never be cleared.
543	///
544	/// Separate from [`Config::DisableMinting`] which controls the `end_era` path.
545	/// This storage guards against minting during payout for eras that were created
546	/// in DAP mode. Set automatically by `end_era_dap` on first successful pot snapshot.
547	/// In legacy mode (Kusama), this is never set and the guard is inactive.
548	#[pallet::storage]
549	pub type DisableMintingGuard<T: Config> = StorageValue<_, EraIndex>;
550
551	/// Optimum self-stake threshold for validators.
552	///
553	/// Below this threshold, the incentive weight grows as `sqrt(self_stake)`.
554	/// Above it, growth is dampened by [`SelfStakeSlopeFactor`].
555	#[pallet::storage]
556	pub type OptimumSelfStake<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;
557
558	/// Hard cap on effective validator self-stake.
559	///
560	/// Self-stake above this value receives no additional reward benefit (plateau).
561	#[pallet::storage]
562	pub type HardCapSelfStake<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;
563
564	/// Slope factor controlling the discouragement rate for self-stake between optimum and cap.
565	///
566	/// Value between 0 and 1: k=1 means no discouragement, k=0 means immediate plateau.
567	#[pallet::storage]
568	pub type SelfStakeSlopeFactor<T: Config> = StorageValue<_, Perbill, ValueQuery>;
569
570	/// The total validator incentive budget for the given era, snapshotted at era end.
571	///
572	/// This is the similar to [`ErasValidatorReward`] but for the self-stake incentive pot.
573	#[pallet::storage]
574	pub type ErasValidatorIncentiveBudget<T: Config> =
575		StorageMap<_, Twox64Concat, EraIndex, BalanceOf<T>, ValueQuery>;
576
577	/// Sum of all validators' incentive weights for the era.
578	///
579	/// Directly linked to [`ErasValidatorIncentiveWeight`].
580	#[pallet::storage]
581	pub type ErasSumValidatorIncentiveWeight<T: Config> =
582		StorageMap<_, Twox64Concat, EraIndex, IncentiveWeight<T>, ValueQuery>;
583
584	/// Individual validator incentive weight per era.
585	/// Each validator's share of the incentive pot = `their_weight / sum_weight`.
586	#[pallet::storage]
587	pub type ErasValidatorIncentiveWeight<T: Config> = StorageDoubleMap<
588		_,
589		Twox64Concat,
590		EraIndex,
591		Twox64Concat,
592		T::AccountId,
593		IncentiveWeight<T>,
594		OptionQuery,
595	>;
596
597	/// Running sum of `validator_incentive_weight × era_points` across all validators
598	/// with non-zero era points for the era.
599	///
600	/// Maintained incrementally inside [`session_rotation::Eras::reward_active_era`] every
601	/// time validator points are credited. Used as the denominator of the weighted-points
602	/// share that determines each validator's slice of [`ErasValidatorIncentiveBudget`].
603	#[pallet::storage]
604	pub type ErasSumWeightedPoints<T: Config> =
605		StorageMap<_, Twox64Concat, EraIndex, IncentiveWeight<T>, ValueQuery>;
606
607	/// Cutoff era from which the validator self-stake incentive switches to the
608	/// weighted-points formula.
609	///
610	/// `None` is the pre-migration state for chains whose storage predates this item. Until the
611	/// migration records a cutoff, [`session_rotation::Eras::uses_weighted_points`] treats all
612	/// eras as weighted-points eras. Chains initialized with this storage item set the cutoff to
613	/// `0` in `genesis_build`, and the upgrade migration leaves any existing value untouched.
614	///
615	/// See [`session_rotation::Eras::uses_weighted_points`] for the exact semantics and
616	/// the rationale for the cutoff.
617	///
618	/// TODO(staking-async): remove this storage item, the legacy stake-only branch in
619	/// [`crate::Pallet::calculate_validator_incentive_for_page`], the
620	/// [`session_rotation::Eras::uses_weighted_points`] cutoff helper, and the
621	/// [`crate::migrations::SetWeightedPointsFormulaStartEra`] migration once
622	/// [`Config::HistoryDepth`] eras have elapsed since the upgrade — i.e. once the cutoff
623	/// satisfies `cutoff <= active_era - HistoryDepth`, at which point no pre-cutoff era
624	/// remains claimable and every live era uses the weighted-points formula.
625	#[pallet::storage]
626	pub type WeightedPointsFormulaStartEra<T: Config> = StorageValue<_, EraIndex, OptionQuery>;
627
628	/// Whether nominators are slashable or not.
629	///
630	/// - When set to `true` (default), nominators are slashed along with validators and must wait
631	///   the full [`Config::BondingDuration`] before withdrawing unbonded funds.
632	/// - When set to `false`, nominators are not slashed, and can unbond in
633	///   [`Config::NominatorFastUnbondDuration`] eras instead of the full
634	///   [`Config::BondingDuration`] (see [`StakingInterface::nominator_bonding_duration`]).
635	#[pallet::storage]
636	pub type AreNominatorsSlashable<T: Config> = StorageValue<_, bool, ValueQuery, ConstBool<true>>;
637
638	/// Per-era snapshot of whether nominators are slashable.
639	///
640	/// This is copied from [`AreNominatorsSlashable`] at the start of each era. When processing
641	/// offences, we use the value from this storage for the offence era to ensure that the
642	/// slashing rules at the time of the offence are applied, not the current rules.
643	///
644	/// If an entry does not exist for an era, nominators are assumed to be slashable (default).
645	#[pallet::storage]
646	pub type ErasNominatorsSlashable<T: Config> =
647		StorageMap<_, Twox64Concat, EraIndex, bool, OptionQuery>;
648
649	/// Map from all (unlocked) "controller" accounts to the info regarding the staking.
650	///
651	/// Note: All the reads and mutations to this storage *MUST* be done through the methods exposed
652	/// by [`StakingLedger`] to ensure data and lock consistency.
653	#[pallet::storage]
654	pub type Ledger<T: Config> = StorageMap<_, Blake2_128Concat, T::AccountId, StakingLedger<T>>;
655
656	/// Where the reward payment should be made. Keyed by stash.
657	///
658	/// TWOX-NOTE: SAFE since `AccountId` is a secure hash.
659	#[pallet::storage]
660	pub type Payee<T: Config> =
661		StorageMap<_, Twox64Concat, T::AccountId, RewardDestination<T::AccountId>, OptionQuery>;
662
663	/// The map from (wannabe) validator stash key to the preferences of that validator.
664	///
665	/// TWOX-NOTE: SAFE since `AccountId` is a secure hash.
666	#[pallet::storage]
667	pub type Validators<T: Config> =
668		CountedStorageMap<_, Twox64Concat, T::AccountId, ValidatorPrefs, ValueQuery>;
669
670	/// The maximum validator count before we stop allowing new validators to join.
671	///
672	/// When this value is not set, no limits are enforced.
673	#[pallet::storage]
674	pub type MaxValidatorsCount<T> = StorageValue<_, u32, OptionQuery>;
675
676	/// Tracks the last era in which an account was active as a validator (included in the era's
677	/// exposure/snapshot).
678	///
679	/// This is used to enforce that accounts who were recently validators must wait the full
680	/// [`Config::BondingDuration`] before their funds can be withdrawn, even if they switch to
681	/// nominator role. This prevents validators from:
682	/// 1. Committing a slashable offence in era N
683	/// 2. Switching to nominator role
684	/// 3. Using the shorter nominator unbonding duration to withdraw funds before being slashed
685	///
686	/// Updated when era snapshots are created (in `ErasStakersPaged`/`ErasStakersOverview`).
687	/// Cleaned up when the stash is killed (fully withdrawn/reaped).
688	#[pallet::storage]
689	pub type LastValidatorEra<T: Config> = StorageMap<_, Twox64Concat, T::AccountId, EraIndex>;
690
691	/// The map from nominator stash key to their nomination preferences, namely the validators that
692	/// they wish to support.
693	///
694	/// Note that the keys of this storage map might become non-decodable in case the
695	/// account's [`NominationsQuota::MaxNominations`] configuration is decreased.
696	/// In this rare case, these nominators
697	/// are still existent in storage, their key is correct and retrievable (i.e. `contains_key`
698	/// indicates that they exist), but their value cannot be decoded. Therefore, the non-decodable
699	/// nominators will effectively not-exist, until they re-submit their preferences such that it
700	/// is within the bounds of the newly set `Config::MaxNominations`.
701	///
702	/// This implies that `::iter_keys().count()` and `::iter().count()` might return different
703	/// values for this map. Moreover, the main `::count()` is aligned with the former, namely the
704	/// number of keys that exist.
705	///
706	/// Lastly, if any of the nominators become non-decodable, they can be chilled immediately via
707	/// [`Call::chill_other`] dispatchable by anyone.
708	///
709	/// TWOX-NOTE: SAFE since `AccountId` is a secure hash.
710	#[pallet::storage]
711	pub type Nominators<T: Config> =
712		CountedStorageMap<_, Twox64Concat, T::AccountId, Nominations<T>>;
713
714	/// Stakers whose funds are managed by other pallets.
715	///
716	/// This pallet does not apply any locks on them, therefore they are only virtually bonded. They
717	/// are expected to be keyless accounts and hence should not be allowed to mutate their ledger
718	/// directly via this pallet. Instead, these accounts are managed by other pallets and accessed
719	/// via low level apis. We keep track of them to do minimal integrity checks.
720	#[pallet::storage]
721	pub type VirtualStakers<T: Config> = CountedStorageMap<_, Twox64Concat, T::AccountId, ()>;
722
723	/// The maximum nominator count before we stop allowing new validators to join.
724	///
725	/// When this value is not set, no limits are enforced.
726	#[pallet::storage]
727	pub type MaxNominatorsCount<T> = StorageValue<_, u32, OptionQuery>;
728
729	// --- AUDIT NOTE: the following storage items should only be controlled by `Rotator`
730
731	/// The current planned era index.
732	///
733	/// This is the latest planned era, depending on how the Session pallet queues the validator
734	/// set, it might be active or not.
735	#[pallet::storage]
736	pub type CurrentEra<T> = StorageValue<_, EraIndex>;
737
738	/// The active era information, it holds index and start.
739	///
740	/// The active era is the era being currently rewarded. Validator set of this era must be
741	/// equal to what is RC's session pallet.
742	#[pallet::storage]
743	pub type ActiveEra<T> = StorageValue<_, ActiveEraInfo>;
744
745	/// Custom bound for [`BondedEras`] which is equal to [`Config::BondingDuration`] + 1.
746	pub struct BondedErasBound<T>(core::marker::PhantomData<T>);
747	impl<T: Config> Get<u32> for BondedErasBound<T> {
748		fn get() -> u32 {
749			T::BondingDuration::get().saturating_add(1)
750		}
751	}
752
753	const OFFENCE_QUEUE_ERAS_BOUND: u32 = 10;
754	/// Custom bound for [`OffenceQueueEras`] which is equal to `Config::BondingDuration +
755	/// OFFENCE_QUEUE_ERAS_BOUND`.
756	pub struct OffenceQueueErasBound<T>(core::marker::PhantomData<T>);
757	impl<T: Config> Get<u32> for OffenceQueueErasBound<T> {
758		fn get() -> u32 {
759			let bonding_duration = T::BondingDuration::get();
760			bonding_duration.saturating_add(OFFENCE_QUEUE_ERAS_BOUND) // adding OFFENCE_QUEUE_ERAS_BOUND eras
761			                                                 // to add headroom to
762			                                                 // the bound for runtime upgrades that
763			                                                 // lower BondingDuration so we avoid
764			                                                 // the try_into trap.
765		}
766	}
767
768	/// A mapping from still-bonded eras to the first session index of that era.
769	///
770	/// Must contains information for eras for the range:
771	/// `[active_era - bounding_duration; active_era]`
772	#[pallet::storage]
773	pub type BondedEras<T: Config> =
774		StorageValue<_, BoundedVec<(EraIndex, SessionIndex), BondedErasBound<T>>, ValueQuery>;
775
776	// --- AUDIT Note: end of storage items controlled by `Rotator`.
777
778	/// Summary of validator exposure at a given era.
779	///
780	/// This contains the total stake in support of the validator and their own stake. In addition,
781	/// it can also be used to get the number of nominators backing this validator and the number of
782	/// exposure pages they are divided into. The page count is useful to determine the number of
783	/// pages of rewards that needs to be claimed.
784	///
785	/// This is keyed first by the era index to allow bulk deletion and then the stash account.
786	/// Should only be accessed through `Eras`.
787	///
788	/// Is it removed after [`Config::HistoryDepth`] eras.
789	/// If stakers hasn't been set or has been removed then empty overview is returned.
790	#[pallet::storage]
791	pub type ErasStakersOverview<T: Config> = StorageDoubleMap<
792		_,
793		Twox64Concat,
794		EraIndex,
795		Twox64Concat,
796		T::AccountId,
797		PagedExposureMetadata<BalanceOf<T>>,
798		OptionQuery,
799	>;
800
801	/// A bounded wrapper for [`sp_staking::ExposurePage`].
802	///
803	/// It has `Deref` and `DerefMut` impls that map it back [`sp_staking::ExposurePage`] for all
804	/// purposes. This is done in such a way because we prefer to keep the types in [`sp_staking`]
805	/// pure, and not polluted by pallet-specific bounding logic.
806	///
807	/// It encoded and decodes exactly the same as [`sp_staking::ExposurePage`], and provides a
808	/// manual `MaxEncodedLen` implementation, to be used in benchmarking
809	#[derive(PartialEqNoBound, Encode, Decode, DebugNoBound, TypeInfo, DefaultNoBound)]
810	#[scale_info(skip_type_params(T))]
811	pub struct BoundedExposurePage<T: Config>(pub ExposurePage<T::AccountId, BalanceOf<T>>);
812	impl<T: Config> Deref for BoundedExposurePage<T> {
813		type Target = ExposurePage<T::AccountId, BalanceOf<T>>;
814
815		fn deref(&self) -> &Self::Target {
816			&self.0
817		}
818	}
819
820	impl<T: Config> core::ops::DerefMut for BoundedExposurePage<T> {
821		fn deref_mut(&mut self) -> &mut Self::Target {
822			&mut self.0
823		}
824	}
825
826	impl<T: Config> codec::MaxEncodedLen for BoundedExposurePage<T> {
827		fn max_encoded_len() -> usize {
828			let max_exposure_page_size = T::MaxExposurePageSize::get() as usize;
829			let individual_size =
830				T::AccountId::max_encoded_len() + BalanceOf::<T>::max_encoded_len();
831
832			// 1 balance for `total`
833			BalanceOf::<T>::max_encoded_len() +
834			// individual_size multiplied by page size
835				max_exposure_page_size.saturating_mul(individual_size)
836		}
837	}
838
839	impl<T: Config> From<ExposurePage<T::AccountId, BalanceOf<T>>> for BoundedExposurePage<T> {
840		fn from(value: ExposurePage<T::AccountId, BalanceOf<T>>) -> Self {
841			Self(value)
842		}
843	}
844
845	impl<T: Config> From<BoundedExposurePage<T>> for ExposurePage<T::AccountId, BalanceOf<T>> {
846		fn from(value: BoundedExposurePage<T>) -> Self {
847			value.0
848		}
849	}
850
851	impl<T: Config> codec::EncodeLike<BoundedExposurePage<T>>
852		for ExposurePage<T::AccountId, BalanceOf<T>>
853	{
854	}
855
856	/// Paginated exposure of a validator at given era.
857	///
858	/// This is keyed first by the era index to allow bulk deletion, then stash account and finally
859	/// the page. Should only be accessed through `Eras`.
860	///
861	/// This is cleared after [`Config::HistoryDepth`] eras.
862	#[pallet::storage]
863	pub type ErasStakersPaged<T: Config> = StorageNMap<
864		_,
865		(
866			NMapKey<Twox64Concat, EraIndex>,
867			NMapKey<Twox64Concat, T::AccountId>,
868			NMapKey<Twox64Concat, Page>,
869		),
870		BoundedExposurePage<T>,
871		OptionQuery,
872	>;
873
874	pub struct ClaimedRewardsBound<T>(core::marker::PhantomData<T>);
875	impl<T: Config> Get<u32> for ClaimedRewardsBound<T> {
876		fn get() -> u32 {
877			let max_total_nominators_per_validator =
878				<T::ElectionProvider as ElectionProvider>::MaxBackersPerWinnerFinal::get();
879			let exposure_page_size = T::MaxExposurePageSize::get();
880			max_total_nominators_per_validator
881				.saturating_div(exposure_page_size)
882				.saturating_add(1)
883		}
884	}
885
886	/// History of claimed paged rewards by era and validator.
887	///
888	/// This is keyed by era and validator stash which maps to the set of page indexes which have
889	/// been claimed.
890	///
891	/// It is removed after [`Config::HistoryDepth`] eras.
892	#[pallet::storage]
893	pub type ClaimedRewards<T: Config> = StorageDoubleMap<
894		_,
895		Twox64Concat,
896		EraIndex,
897		Twox64Concat,
898		T::AccountId,
899		WeakBoundedVec<Page, ClaimedRewardsBound<T>>,
900		ValueQuery,
901	>;
902
903	/// Exposure of validator at era with the preferences of validators.
904	///
905	/// This is keyed first by the era index to allow bulk deletion and then the stash account.
906	///
907	/// Is it removed after [`Config::HistoryDepth`] eras.
908	// If prefs hasn't been set or has been removed then 0 commission is returned.
909	#[pallet::storage]
910	pub type ErasValidatorPrefs<T: Config> = StorageDoubleMap<
911		_,
912		Twox64Concat,
913		EraIndex,
914		Twox64Concat,
915		T::AccountId,
916		ValidatorPrefs,
917		ValueQuery,
918	>;
919
920	/// The total staker reward budget for each era within [`Config::HistoryDepth`].
921	///
922	/// Set at era finalization:
923	/// - in non-minting mode this is the snapshot of the era pot balance before any payouts.
924	/// - in legacy mode it comes from `EraPayout`, with rewards minted on the fly.
925	#[pallet::storage]
926	pub type ErasValidatorReward<T: Config> = StorageMap<_, Twox64Concat, EraIndex, BalanceOf<T>>;
927
928	/// Rewards for the last [`Config::HistoryDepth`] eras.
929	/// If reward hasn't been set or has been removed then 0 reward is returned.
930	#[pallet::storage]
931	pub type ErasRewardPoints<T: Config> =
932		StorageMap<_, Twox64Concat, EraIndex, EraRewardPoints<T>, ValueQuery>;
933
934	/// The total amount staked for the last [`Config::HistoryDepth`] eras.
935	/// If total hasn't been set or has been removed then 0 stake is returned.
936	#[pallet::storage]
937	pub type ErasTotalStake<T: Config> =
938		StorageMap<_, Twox64Concat, EraIndex, BalanceOf<T>, ValueQuery>;
939
940	/// Mode of era forcing.
941	#[pallet::storage]
942	pub type ForceEra<T> = StorageValue<_, Forcing, ValueQuery>;
943
944	/// Maximum staked rewards, i.e. the percentage of the era inflation that
945	/// is used for stake rewards.
946	///
947	/// Only used in legacy minting mode (`DisableMinting = false`).
948	#[pallet::storage]
949	pub type MaxStakedRewards<T> = StorageValue<_, Percent, OptionQuery>;
950
951	/// The percentage of the slash that is distributed to reporters.
952	///
953	/// The rest of the slashed value is handled by the `Slash`.
954	#[pallet::storage]
955	pub type SlashRewardFraction<T> = StorageValue<_, Perbill, ValueQuery>;
956
957	/// The amount of currency given to reporters of a slash event which was
958	/// canceled by extraordinary circumstances (e.g. governance).
959	#[pallet::storage]
960	pub type CanceledSlashPayout<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;
961
962	/// Stores reported offences in a queue until they are processed in subsequent blocks.
963	///
964	/// Each offence is recorded under the corresponding era index and the offending validator's
965	/// account. If an offence spans multiple pages, only one page is processed at a time. Offences
966	/// are handled sequentially, with their associated slashes computed and stored in
967	/// `UnappliedSlashes`. These slashes are then applied in a future era as determined by
968	/// `SlashDeferDuration`.
969	///
970	/// Any offences tied to an era older than `BondingDuration` are automatically dropped.
971	/// Processing always prioritizes the oldest era first.
972	#[pallet::storage]
973	pub type OffenceQueue<T: Config> = StorageDoubleMap<
974		_,
975		Twox64Concat,
976		EraIndex,
977		Twox64Concat,
978		T::AccountId,
979		slashing::OffenceRecord<T::AccountId>,
980	>;
981
982	/// Tracks the eras that contain offences in `OffenceQueue`, sorted from **earliest to latest**.
983	///
984	/// - This ensures efficient retrieval of the oldest offence without iterating through
985	/// `OffenceQueue`.
986	/// - When a new offence is added to `OffenceQueue`, its era is **inserted in sorted order**
987	/// if not already present.
988	/// - When all offences for an era are processed, it is **removed** from this list.
989	/// - The maximum length of this vector is bounded by `BondingDuration +
990	///   OFFENCE_QUEUE_ERAS_BOUND`.
991	///
992	/// This eliminates the need for expensive iteration and sorting when fetching the next offence
993	/// to process.
994	#[pallet::storage]
995	pub type OffenceQueueEras<T: Config> =
996		StorageValue<_, WeakBoundedVec<u32, OffenceQueueErasBound<T>>>;
997
998	/// Tracks the currently processed offence record from the `OffenceQueue`.
999	///
1000	/// - When processing offences, an offence record is **popped** from the oldest era in
1001	///   `OffenceQueue` and stored here.
1002	/// - The function `process_offence` reads from this storage, processing one page of exposure at
1003	///   a time.
1004	/// - After processing a page, the `exposure_page` count is **decremented** until it reaches
1005	///   zero.
1006	/// - Once fully processed, the offence record is removed from this storage.
1007	///
1008	/// This ensures that offences are processed incrementally, preventing excessive computation
1009	/// in a single block while maintaining correct slashing behavior.
1010	#[pallet::storage]
1011	pub type ProcessingOffence<T: Config> =
1012		StorageValue<_, (EraIndex, T::AccountId, slashing::OffenceRecord<T::AccountId>)>;
1013
1014	/// All unapplied slashes that are queued for later.
1015	#[pallet::storage]
1016	pub type UnappliedSlashes<T: Config> = StorageDoubleMap<
1017		_,
1018		Twox64Concat,
1019		EraIndex,
1020		Twox64Concat,
1021		// Unique key for unapplied slashes: (validator, slash fraction, page index).
1022		(T::AccountId, Perbill, u32),
1023		UnappliedSlash<T>,
1024		OptionQuery,
1025	>;
1026
1027	/// Cancelled slashes by era and validator with maximum slash fraction to be cancelled.
1028	///
1029	/// When slashes are cancelled by governance, this stores the era and the validators
1030	/// whose slashes should be cancelled, along with the maximum slash fraction that should
1031	/// be cancelled for each validator.
1032	#[pallet::storage]
1033	pub type CancelledSlashes<T: Config> = StorageMap<
1034		_,
1035		Twox64Concat,
1036		EraIndex,
1037		BoundedVec<(T::AccountId, Perbill), T::MaxValidatorSet>,
1038		ValueQuery,
1039	>;
1040
1041	/// All slashing events on validators, mapped by era to the highest slash proportion
1042	/// and slash value of the era.
1043	#[pallet::storage]
1044	pub type ValidatorSlashInEra<T: Config> = StorageDoubleMap<
1045		_,
1046		Twox64Concat,
1047		EraIndex,
1048		Twox64Concat,
1049		T::AccountId,
1050		(Perbill, BalanceOf<T>),
1051	>;
1052
1053	/// The threshold for when users can start calling `chill_other` for other validators /
1054	/// nominators. The threshold is compared to the actual number of validators / nominators
1055	/// (`CountFor*`) in the system compared to the configured max (`Max*Count`).
1056	#[pallet::storage]
1057	pub type ChillThreshold<T: Config> = StorageValue<_, Percent, OptionQuery>;
1058
1059	/// Voter snapshot progress status.
1060	///
1061	/// If the status is `Ongoing`, it keeps a cursor of the last voter retrieved to proceed when
1062	/// creating the next snapshot page.
1063	#[pallet::storage]
1064	pub type VoterSnapshotStatus<T: Config> =
1065		StorageValue<_, SnapshotStatus<T::AccountId>, ValueQuery>;
1066
1067	/// Keeps track of an ongoing multi-page election solution request.
1068	///
1069	/// If `Some(_)``, it is the next page that we intend to elect. If `None`, we are not in the
1070	/// election process.
1071	///
1072	/// This is only set in multi-block elections. Should always be `None` otherwise.
1073	#[pallet::storage]
1074	pub type NextElectionPage<T: Config> = StorageValue<_, PageIndex, OptionQuery>;
1075
1076	/// A bounded list of the "electable" stashes that resulted from a successful election.
1077	#[pallet::storage]
1078	pub type ElectableStashes<T: Config> =
1079		StorageValue<_, BoundedBTreeSet<T::AccountId, T::MaxValidatorSet>, ValueQuery>;
1080
1081	/// The validators elected for the planned era, kept from the moment its election completes
1082	/// until the era starts, for [`Config::OnEraStart`].
1083	///
1084	/// Only written when [`OnEraStart::enabled`] is true, and removed at the start of its era
1085	/// whether or not the hook is still enabled. An era whose election completed before the hook
1086	/// was enabled has no copy and is not notified.
1087	#[pallet::storage]
1088	pub type NextEraValidators<T: Config> =
1089		StorageValue<_, (EraIndex, BoundedVec<T::AccountId, T::MaxValidatorSet>), OptionQuery>;
1090
1091	/// Tracks the current step of era pruning process for each era being lazily pruned.
1092	#[pallet::storage]
1093	pub type EraPruningState<T: Config> = StorageMap<_, Twox64Concat, EraIndex, PruningStep>;
1094
1095	/// The number of eras a validator can remain inactive during the last
1096	/// [`Config::HistoryDepth`] before being subject to chilling because of inactivity.
1097	///
1098	/// This must be less than or equal [`Config::HistoryDepth`] and bigger than 1.
1099	#[pallet::storage]
1100	pub type ChillInactiveThreshold<T: Config> = StorageValue<_, u32, ValueQuery, T::HistoryDepth>;
1101
1102	#[pallet::genesis_config]
1103	#[derive(frame_support::DefaultNoBound, frame_support::DebugNoBound)]
1104	pub struct GenesisConfig<T: Config> {
1105		pub validator_count: u32,
1106		pub force_era: Forcing,
1107		pub slash_reward_fraction: Perbill,
1108		pub canceled_payout: BalanceOf<T>,
1109		pub stakers: Vec<(T::AccountId, BalanceOf<T>, crate::StakerStatus<T::AccountId>)>,
1110		pub min_nominator_bond: BalanceOf<T>,
1111		pub min_validator_bond: BalanceOf<T>,
1112		pub max_validator_count: Option<u32>,
1113		pub max_nominator_count: Option<u32>,
1114		/// Create the given number of validators and nominators.
1115		///
1116		/// These account need not be in the endowment list of balances, and are auto-topped up
1117		/// here.
1118		///
1119		/// Useful for testing genesis config.
1120		pub dev_stakers: Option<(u32, u32)>,
1121		/// initial active era, corresponding session index and start timestamp.
1122		pub active_era: (u32, u32, u64),
1123	}
1124
1125	impl<T: Config> GenesisConfig<T> {
1126		fn generate_endowed_bonded_account(derivation: &str, rng: &mut ChaChaRng) -> T::AccountId {
1127			let pair: SrPair = Pair::from_string(&derivation, None)
1128				.expect(&format!("Failed to parse derivation string: {derivation}"));
1129			let who = T::AccountId::decode(&mut &pair.public().encode()[..])
1130				.expect(&format!("Failed to decode public key from pair: {:?}", pair.public()));
1131
1132			let (min, max) = T::VoterList::range();
1133			let stake = BalanceOf::<T>::from(rng.next_u64().min(max).max(min));
1134			let two: BalanceOf<T> = 2u32.into();
1135
1136			assert_ok!(T::Currency::mint_into(&who, stake * two));
1137			assert_ok!(<Pallet<T>>::bond(
1138				T::RuntimeOrigin::from(Some(who.clone()).into()),
1139				stake,
1140				RewardDestination::Staked,
1141			));
1142			who
1143		}
1144	}
1145
1146	#[pallet::genesis_build]
1147	impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
1148		fn build(&self) {
1149			crate::log!(trace, "initializing with {:?}", self);
1150			assert!(
1151				self.validator_count <=
1152					<T::ElectionProvider as ElectionProvider>::MaxWinnersPerPage::get() *
1153						<T::ElectionProvider as ElectionProvider>::Pages::get(),
1154				"validator count is too high, `ElectionProvider` can never fulfill this"
1155			);
1156			ValidatorCount::<T>::put(self.validator_count);
1157
1158			ForceEra::<T>::put(self.force_era);
1159			CanceledSlashPayout::<T>::put(self.canceled_payout);
1160			SlashRewardFraction::<T>::put(self.slash_reward_fraction);
1161			MinNominatorBond::<T>::put(self.min_nominator_bond);
1162			MinValidatorBond::<T>::put(self.min_validator_bond);
1163			if let Some(x) = self.max_validator_count {
1164				MaxValidatorsCount::<T>::put(x);
1165			}
1166			if let Some(x) = self.max_nominator_count {
1167				MaxNominatorsCount::<T>::put(x);
1168			}
1169
1170			// First pass: set up all validators and idle stakers
1171			for &(ref stash, balance, ref status) in &self.stakers {
1172				match status {
1173					crate::StakerStatus::Validator => {
1174						crate::log!(
1175							trace,
1176							"inserting genesis validator: {:?} => {:?} => {:?}",
1177							stash,
1178							balance,
1179							status
1180						);
1181						assert!(
1182							asset::free_to_stake::<T>(stash) >= balance,
1183							"Stash does not have enough balance to bond."
1184						);
1185						assert_ok!(<Pallet<T>>::bond(
1186							T::RuntimeOrigin::from(Some(stash.clone()).into()),
1187							balance,
1188							RewardDestination::Staked,
1189						));
1190						assert_ok!(<Pallet<T>>::validate(
1191							T::RuntimeOrigin::from(Some(stash.clone()).into()),
1192							Default::default(),
1193						));
1194					},
1195					crate::StakerStatus::Idle => {
1196						crate::log!(
1197							trace,
1198							"inserting genesis idle staker: {:?} => {:?} => {:?}",
1199							stash,
1200							balance,
1201							status
1202						);
1203						assert!(
1204							asset::free_to_stake::<T>(stash) >= balance,
1205							"Stash does not have enough balance to bond."
1206						);
1207						assert_ok!(<Pallet<T>>::bond(
1208							T::RuntimeOrigin::from(Some(stash.clone()).into()),
1209							balance,
1210							RewardDestination::Staked,
1211						));
1212					},
1213					_ => {},
1214				}
1215			}
1216
1217			// Second pass: set up all nominators (now that validators exist)
1218			for &(ref stash, balance, ref status) in &self.stakers {
1219				match status {
1220					crate::StakerStatus::Nominator(votes) => {
1221						crate::log!(
1222							trace,
1223							"inserting genesis nominator: {:?} => {:?} => {:?}",
1224							stash,
1225							balance,
1226							status
1227						);
1228						assert!(
1229							asset::free_to_stake::<T>(stash) >= balance,
1230							"Stash does not have enough balance to bond."
1231						);
1232						assert_ok!(<Pallet<T>>::bond(
1233							T::RuntimeOrigin::from(Some(stash.clone()).into()),
1234							balance,
1235							RewardDestination::Staked,
1236						));
1237						assert_ok!(<Pallet<T>>::nominate(
1238							T::RuntimeOrigin::from(Some(stash.clone()).into()),
1239							votes.iter().map(|l| T::Lookup::unlookup(l.clone())).collect(),
1240						));
1241					},
1242					_ => {},
1243				}
1244			}
1245
1246			// all voters are reported to the `VoterList`.
1247			assert_eq!(
1248				T::VoterList::count(),
1249				Nominators::<T>::count() + Validators::<T>::count(),
1250				"not all genesis stakers were inserted into sorted list provider, something is wrong."
1251			);
1252
1253			// now generate the dev stakers, after all else is setup
1254			if let Some((validators, nominators)) = self.dev_stakers {
1255				crate::log!(
1256					debug,
1257					"generating dev stakers: validators: {}, nominators: {}",
1258					validators,
1259					nominators
1260				);
1261				let base_derivation = "//staker//{}";
1262
1263				// it is okay for the randomness to be the same on every call. If we want different,
1264				// we can make `base_derivation` configurable.
1265				let mut rng = ChaChaRng::from_seed(
1266					base_derivation.using_encoded(sp_crypto_hashing::blake2_256),
1267				);
1268
1269				(0..validators).for_each(|index| {
1270					let derivation = base_derivation.replace("{}", &format!("validator{}", index));
1271					let who = Self::generate_endowed_bonded_account(&derivation, &mut rng);
1272					assert_ok!(<Pallet<T>>::validate(
1273						T::RuntimeOrigin::from(Some(who.clone()).into()),
1274						Default::default(),
1275					));
1276				});
1277
1278				// This allows us to work with configs like `dev_stakers: (0, 10)`. Don't create new
1279				// validators, just add a bunch of nominators. Useful for slashing tests.
1280				let all_validators = Validators::<T>::iter_keys().collect::<Vec<_>>();
1281
1282				(0..nominators).for_each(|index| {
1283					let derivation = base_derivation.replace("{}", &format!("nominator{}", index));
1284					let who = Self::generate_endowed_bonded_account(&derivation, &mut rng);
1285
1286					let random_nominations = all_validators
1287						.choose_multiple(&mut rng, MaxNominationsOf::<T>::get() as usize)
1288						.map(|v| v.clone())
1289						.collect::<Vec<_>>();
1290
1291					assert_ok!(<Pallet<T>>::nominate(
1292						T::RuntimeOrigin::from(Some(who.clone()).into()),
1293						random_nominations.iter().map(|l| T::Lookup::unlookup(l.clone())).collect(),
1294					));
1295				})
1296			}
1297
1298			// Chains initialized with this storage item maintain `ErasSumWeightedPoints` from
1299			// era 0. Pin the cutoff to the first era so every era uses the weighted-points formula
1300			// and the idempotent migration only acts on chains whose storage predates this item.
1301			WeightedPointsFormulaStartEra::<T>::put(0);
1302
1303			let (active_era, session_index, timestamp) = self.active_era;
1304			ActiveEra::<T>::put(ActiveEraInfo { index: active_era, start: Some(timestamp) });
1305			// at genesis, we do not have any new planned era.
1306			CurrentEra::<T>::put(active_era);
1307			// set the bonded genesis era
1308			BondedEras::<T>::put(
1309				BoundedVec::<_, BondedErasBound<T>>::try_from(
1310					alloc::vec![(active_era, session_index)]
1311				)
1312				.expect("bound for BondedEras is BondingDuration + 1; can contain at least one element; qed")
1313			);
1314		}
1315	}
1316
1317	#[pallet::event]
1318	#[pallet::generate_deposit(pub fn deposit_event)]
1319	pub enum Event<T: Config> {
1320		/// The era payout has been set.
1321		///
1322		/// In non-minting mode, `validator_payout` is the staker reward budget
1323		/// snapshotted from the general pot, and `remainder` is always zero.
1324		/// In legacy minting mode, both fields reflect the `EraPayout` computation.
1325		EraPaid {
1326			era_index: EraIndex,
1327			validator_payout: BalanceOf<T>,
1328			remainder: BalanceOf<T>,
1329		},
1330		/// The nominator has been rewarded by this amount to this destination.
1331		Rewarded {
1332			stash: T::AccountId,
1333			dest: RewardDestination<T::AccountId>,
1334			amount: BalanceOf<T>,
1335		},
1336		/// A staker (validator or nominator) has been slashed by the given amount.
1337		Slashed {
1338			staker: T::AccountId,
1339			amount: BalanceOf<T>,
1340		},
1341		/// An old slashing report from a prior era was discarded because it could
1342		/// not be processed.
1343		OldSlashingReportDiscarded {
1344			session_index: SessionIndex,
1345		},
1346		/// An account has bonded this amount. \[stash, amount\]
1347		///
1348		/// NOTE: This event is only emitted when funds are bonded via a dispatchable. Notably,
1349		/// it will not be emitted for staking rewards when they are added to stake.
1350		Bonded {
1351			stash: T::AccountId,
1352			amount: BalanceOf<T>,
1353		},
1354		/// An account has unbonded this amount.
1355		Unbonded {
1356			stash: T::AccountId,
1357			amount: BalanceOf<T>,
1358			/// The era at which `amount` becomes withdrawable.
1359			era: EraIndex,
1360		},
1361		/// An account has called `withdraw_unbonded` and removed unbonding chunks worth `Balance`
1362		/// from the unlocking queue.
1363		Withdrawn {
1364			stash: T::AccountId,
1365			amount: BalanceOf<T>,
1366		},
1367		/// A subsequent event of `Withdrawn`, indicating that `stash` was fully removed from the
1368		/// system.
1369		StakerRemoved {
1370			stash: T::AccountId,
1371		},
1372		/// A nominator has been kicked from a validator.
1373		Kicked {
1374			nominator: T::AccountId,
1375			stash: T::AccountId,
1376		},
1377		/// An account has stopped participating as either a validator or nominator.
1378		Chilled {
1379			stash: T::AccountId,
1380		},
1381		/// A Page of stakers rewards are getting paid. `next` is `None` if all pages are claimed.
1382		PayoutStarted {
1383			era_index: EraIndex,
1384			validator_stash: T::AccountId,
1385			page: Page,
1386			next: Option<Page>,
1387		},
1388		/// A validator has set their preferences.
1389		ValidatorPrefsSet {
1390			stash: T::AccountId,
1391			prefs: ValidatorPrefs,
1392		},
1393		/// Voters size limit reached.
1394		SnapshotVotersSizeExceeded {
1395			size: u32,
1396		},
1397		/// Targets size limit reached.
1398		SnapshotTargetsSizeExceeded {
1399			size: u32,
1400		},
1401		ForceEra {
1402			mode: Forcing,
1403		},
1404		/// Report of a controller batch deprecation.
1405		ControllerBatchDeprecated {
1406			failures: u32,
1407		},
1408		/// Staking balance migrated from locks to holds, with any balance that could not be held
1409		/// is force withdrawn.
1410		CurrencyMigrated {
1411			stash: T::AccountId,
1412			force_withdraw: BalanceOf<T>,
1413		},
1414		/// A page from a multi-page election was fetched. A number of these are followed by
1415		/// `StakersElected`.
1416		///
1417		/// `Ok(count)` indicates the give number of stashes were added.
1418		/// `Err(index)` indicates that the stashes after index were dropped.
1419		/// `Err(0)` indicates that an error happened but no stashes were dropped nor added.
1420		///
1421		/// The error indicates that a number of validators were dropped due to excess size, but
1422		/// the overall election will continue.
1423		PagedElectionProceeded {
1424			page: PageIndex,
1425			result: Result<u32, u32>,
1426		},
1427		/// An offence for the given validator, for the given percentage of their stake, at the
1428		/// given era as been reported.
1429		OffenceReported {
1430			offence_era: EraIndex,
1431			validator: T::AccountId,
1432			fraction: Perbill,
1433		},
1434		/// An offence has been processed and the corresponding slash has been computed.
1435		SlashComputed {
1436			offence_era: EraIndex,
1437			slash_era: EraIndex,
1438			offender: T::AccountId,
1439			page: u32,
1440		},
1441		/// An unapplied slash has been cancelled.
1442		SlashCancelled {
1443			slash_era: EraIndex,
1444			validator: T::AccountId,
1445		},
1446		/// Session change has been triggered.
1447		///
1448		/// If planned_era is one era ahead of active_era, it implies new era is being planned and
1449		/// election is ongoing.
1450		SessionRotated {
1451			starting_session: SessionIndex,
1452			active_era: EraIndex,
1453			planned_era: EraIndex,
1454		},
1455		/// Something occurred that should never happen under normal operation.
1456		/// Logged as an event for fail-safe observability.
1457		Unexpected(UnexpectedKind<T>),
1458		/// An offence was reported that was too old to be processed, and thus was dropped.
1459		OffenceTooOld {
1460			offence_era: EraIndex,
1461			validator: T::AccountId,
1462			fraction: Perbill,
1463		},
1464		/// An old era with the given index was pruned.
1465		EraPruned {
1466			index: EraIndex,
1467		},
1468		/// The validator has been paid their self-stake incentive bonus.
1469		ValidatorIncentivePaid {
1470			era: EraIndex,
1471			validator_stash: T::AccountId,
1472			dest: RewardDestination<T::AccountId>,
1473			amount: BalanceOf<T>,
1474		},
1475		/// Validator self-stake incentive configuration has been updated.
1476		ValidatorIncentiveConfigSet {
1477			optimum_self_stake: BalanceOf<T>,
1478			hard_cap_self_stake: BalanceOf<T>,
1479			slope_factor: Perbill,
1480		},
1481	}
1482
1483	/// Represents unexpected or invariant-breaking conditions encountered during execution.
1484	///
1485	/// These variants are emitted as [`Event::Unexpected`] and indicate a defensive check has
1486	/// failed. While these should never occur under normal operation, they are useful for
1487	/// diagnosing issues in production or test environments.
1488	#[derive(Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, TypeInfo, DebugNoBound)]
1489	#[codec(mel_bound())]
1490	#[scale_info(skip_type_params(T))]
1491	pub enum UnexpectedKind<T: Config> {
1492		/// Emitted when calculated era duration exceeds the configured maximum.
1493		EraDurationBoundExceeded,
1494		/// Received a validator activation event that is not recognized.
1495		UnknownValidatorActivation,
1496		/// Failed to proceed paged election due to weight limits
1497		PagedElectionOutOfWeight { page: PageIndex, required: Weight, had: Weight },
1498		/// Payee not set for a staker when paying rewards.
1499		MissingPayee { era: EraIndex, stash: T::AccountId },
1500		/// Total validator weight is zero but incentive allocation exists.
1501		ValidatorIncentiveWeightMismatch { era: EraIndex },
1502		/// Validator incentive transfer from era pot failed.
1503		ValidatorIncentiveTransferFailed { era: EraIndex },
1504	}
1505
1506	#[pallet::error]
1507	#[derive(PartialEq)]
1508	pub enum Error<T> {
1509		/// Not a controller account.
1510		NotController,
1511		/// Not a stash account.
1512		NotStash,
1513		/// Stash is already bonded.
1514		AlreadyBonded,
1515		/// Controller is already paired.
1516		AlreadyPaired,
1517		/// Targets cannot be empty.
1518		EmptyTargets,
1519		/// Duplicate index.
1520		DuplicateIndex,
1521		/// Slash record not found.
1522		InvalidSlashRecord,
1523		/// Cannot bond, nominate or validate with value less than the minimum defined by
1524		/// governance (see `MinValidatorBond` and `MinNominatorBond`). If unbonding is the
1525		/// intention, `chill` first to remove one's role as validator/nominator.
1526		InsufficientBond,
1527		/// Can not schedule more unlock chunks.
1528		NoMoreChunks,
1529		/// Can not rebond without unlocking chunks.
1530		NoUnlockChunk,
1531		/// Attempting to target a stash that still has funds.
1532		FundedTarget,
1533		/// Invalid era to reward.
1534		InvalidEraToReward,
1535		/// Invalid number of nominations.
1536		InvalidNumberOfNominations,
1537		/// Rewards for this era have already been claimed for this validator.
1538		AlreadyClaimed,
1539		/// No nominators exist on this page.
1540		InvalidPage,
1541		/// Incorrect previous history depth input provided.
1542		IncorrectHistoryDepth,
1543		/// Internal state has become somehow corrupted and the operation cannot continue.
1544		BadState,
1545		/// Too many nomination targets supplied.
1546		TooManyTargets,
1547		/// A nomination target was supplied that was blocked or otherwise not a validator.
1548		BadTarget,
1549		/// The user has enough bond and thus cannot be chilled forcefully by an external person.
1550		CannotChillOther,
1551		/// There are too many nominators in the system. Governance needs to adjust the staking
1552		/// settings to keep things safe for the runtime.
1553		TooManyNominators,
1554		/// There are too many validator candidates in the system. Governance needs to adjust the
1555		/// staking settings to keep things safe for the runtime.
1556		TooManyValidators,
1557		/// Commission is too low. Must be at least `MinCommission`.
1558		CommissionTooLow,
1559		/// Some bound is not met.
1560		BoundNotMet,
1561		/// Used when attempting to use deprecated controller account logic.
1562		ControllerDeprecated,
1563		/// Cannot reset a ledger.
1564		CannotRestoreLedger,
1565		/// Provided reward destination is not allowed.
1566		RewardDestinationRestricted,
1567		/// Not enough funds available to withdraw.
1568		NotEnoughFunds,
1569		/// Operation not allowed for virtual stakers.
1570		VirtualStakerNotAllowed,
1571		/// Stash could not be reaped as other pallet might depend on it.
1572		CannotReapStash,
1573		/// The stake of this account is already migrated to `Fungible` holds.
1574		AlreadyMigrated,
1575		/// Era not yet started.
1576		EraNotStarted,
1577		/// Account is restricted from participation in staking. This may happen if the account is
1578		/// staking in another way already, such as via pool.
1579		Restricted,
1580		/// Unapplied slashes in the recently concluded era is blocking this operation.
1581		/// See `Call::apply_slash` to apply them.
1582		UnappliedSlashesInPreviousEra,
1583		/// The era is not eligible for pruning.
1584		EraNotPrunable,
1585		/// The slash has been cancelled and cannot be applied.
1586		CancelledSlash,
1587		/// Commission is higher than the allowed maximum `MaxCommission`.
1588		CommissionTooHigh,
1589		/// Optimum self-stake cannot be greater than hard cap.
1590		OptimumGreaterThanCap,
1591		/// Validator inactivity proof is invalid.
1592		InvalidInactivityProof(InvalidInactivityProofError),
1593		/// Cannot set [`ChillInactiveThreshold`] to the provided value.
1594		InvalidChillInactiveThreshold,
1595	}
1596
1597	#[derive(Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, TypeInfo, PalletError)]
1598	pub enum InvalidInactivityProofError {
1599		/// Invalid proof length.
1600		InvalidLen,
1601		/// Eras in proof are not sorted.
1602		NotSorted,
1603		/// Validator wasn't expected to validate at some era.
1604		ValidatorNotExposed,
1605		/// Validator wasn't inactive at some era.
1606		ValidatorActive,
1607		/// An invalid era was provided in the proof.
1608		InvalidEra,
1609	}
1610
1611	impl<T: Config> Pallet<T> {
1612		/// Apply previously-unapplied slashes on the beginning of a new era, after a delay.
1613		pub fn apply_unapplied_slashes(active_era: EraIndex) -> Weight {
1614			let mut slashes = UnappliedSlashes::<T>::iter_prefix(&active_era).take(1);
1615			if let Some((key, slash)) = slashes.next() {
1616				crate::log!(
1617					debug,
1618					"🦹 found slash {:?} scheduled to be executed in era {:?}",
1619					slash,
1620					active_era,
1621				);
1622
1623				let nominators_slashed = slash.others.len() as u32;
1624
1625				// Check if this slash has been cancelled
1626				if Self::check_slash_cancelled(active_era, &key.0, key.1) {
1627					crate::log!(
1628						debug,
1629						"🦹 slash for {:?} in era {:?} was cancelled, skipping",
1630						key.0,
1631						active_era,
1632					);
1633				} else {
1634					slashing::apply_slash::<T>(slash, Self::offence_era_of(active_era));
1635				}
1636
1637				// Always remove the slash from UnappliedSlashes
1638				UnappliedSlashes::<T>::remove(&active_era, &key);
1639
1640				// Check if there are more slashes for this era
1641				if UnappliedSlashes::<T>::iter_prefix(&active_era).next().is_none() {
1642					// No more slashes for this era, clear CancelledSlashes
1643					CancelledSlashes::<T>::remove(&active_era);
1644				}
1645
1646				T::WeightInfo::apply_slash(nominators_slashed)
1647			} else {
1648				// No slashes found for this era
1649				T::DbWeight::get().reads(1)
1650			}
1651		}
1652
1653		/// Execute one step of era pruning and get actual weight used
1654		fn do_prune_era_step(era: EraIndex) -> Result<Weight, DispatchError> {
1655			// Get current pruning state. If EraPruningState doesn't exist, it means:
1656			// - Era was never marked for pruning, OR
1657			// - Era was already fully pruned (pruning state was removed on final step)
1658			// In either case, this is an error - user should not call prune on non-prunable eras
1659			let current_step = EraPruningState::<T>::get(era).ok_or(Error::<T>::EraNotPrunable)?;
1660
1661			// Limit items to prevent deleting more than we can safely account for in weight
1662			// calculations
1663			let items_limit = T::MaxPruningItems::get().min(T::MaxValidatorSet::get());
1664
1665			let actual_weight = match current_step {
1666				PruningStep::ErasStakersPaged => {
1667					let result = ErasStakersPaged::<T>::clear_prefix((era,), items_limit, None);
1668					let items_deleted = result.backend as u32;
1669					result.maybe_cursor.is_none().then(|| {
1670						EraPruningState::<T>::insert(era, PruningStep::ErasStakersOverview)
1671					});
1672					T::WeightInfo::prune_era_stakers_paged(items_deleted)
1673				},
1674				PruningStep::ErasStakersOverview => {
1675					let result = ErasStakersOverview::<T>::clear_prefix(era, items_limit, None);
1676					let items_deleted = result.backend as u32;
1677					result.maybe_cursor.is_none().then(|| {
1678						EraPruningState::<T>::insert(era, PruningStep::ErasValidatorPrefs)
1679					});
1680					T::WeightInfo::prune_era_stakers_overview(items_deleted)
1681				},
1682				PruningStep::ErasValidatorPrefs => {
1683					let result = ErasValidatorPrefs::<T>::clear_prefix(era, items_limit, None);
1684					let items_deleted = result.backend as u32;
1685					result
1686						.maybe_cursor
1687						.is_none()
1688						.then(|| EraPruningState::<T>::insert(era, PruningStep::ClaimedRewards));
1689					T::WeightInfo::prune_era_validator_prefs(items_deleted)
1690				},
1691				PruningStep::ClaimedRewards => {
1692					let result = ClaimedRewards::<T>::clear_prefix(era, items_limit, None);
1693					let items_deleted = result.backend as u32;
1694					result.maybe_cursor.is_none().then(|| {
1695						EraPruningState::<T>::insert(era, PruningStep::ErasValidatorReward)
1696					});
1697					T::WeightInfo::prune_era_claimed_rewards(items_deleted)
1698				},
1699				PruningStep::ErasValidatorReward => {
1700					ErasValidatorReward::<T>::remove(era);
1701					EraPruningState::<T>::insert(era, PruningStep::ErasRewardPoints);
1702					T::WeightInfo::prune_era_validator_reward()
1703				},
1704				PruningStep::ErasRewardPoints => {
1705					ErasRewardPoints::<T>::remove(era);
1706					EraPruningState::<T>::insert(era, PruningStep::SingleEntryCleanups);
1707					T::WeightInfo::prune_era_reward_points()
1708				},
1709				PruningStep::SingleEntryCleanups => {
1710					ErasTotalStake::<T>::remove(era);
1711					ErasNominatorsSlashable::<T>::remove(era);
1712					ErasValidatorIncentiveBudget::<T>::remove(era);
1713					ErasSumValidatorIncentiveWeight::<T>::remove(era);
1714					ErasSumWeightedPoints::<T>::remove(era);
1715					EraPruningState::<T>::insert(era, PruningStep::ValidatorSlashInEra);
1716					T::WeightInfo::prune_era_single_entry_cleanups()
1717				},
1718				PruningStep::ValidatorSlashInEra => {
1719					let result = ValidatorSlashInEra::<T>::clear_prefix(era, items_limit, None);
1720					let items_deleted = result.backend as u32;
1721
1722					if result.maybe_cursor.is_none() {
1723						EraPruningState::<T>::insert(
1724							era,
1725							PruningStep::ErasValidatorIncentiveWeight,
1726						);
1727					}
1728
1729					T::WeightInfo::prune_era_validator_slash_in_era(items_deleted)
1730				},
1731				PruningStep::ErasValidatorIncentiveWeight => {
1732					let result =
1733						ErasValidatorIncentiveWeight::<T>::clear_prefix(era, items_limit, None);
1734					if result.maybe_cursor.is_none() {
1735						// Final step — remove pruning state.
1736						EraPruningState::<T>::remove(era);
1737					}
1738					T::WeightInfo::prune_era_validator_incentive_weight(result.backend as u32)
1739				},
1740			};
1741
1742			// Check if era is fully pruned (pruning state removed) and emit event
1743			if EraPruningState::<T>::get(era).is_none() {
1744				Self::deposit_event(Event::<T>::EraPruned { index: era });
1745			}
1746
1747			Ok(actual_weight)
1748		}
1749	}
1750
1751	#[pallet::hooks]
1752	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
1753		fn on_poll(_now: BlockNumberFor<T>, weight_meter: &mut WeightMeter) {
1754			let (weight, exec) = EraElectionPlanner::<T>::maybe_fetch_election_results();
1755			crate::log!(
1756				trace,
1757				"weight of fetching next election page is {:?}, have {:?}",
1758				weight,
1759				weight_meter.remaining()
1760			);
1761
1762			if weight_meter.can_consume(weight) {
1763				exec(weight_meter);
1764			} else {
1765				Self::deposit_event(Event::<T>::Unexpected(
1766					UnexpectedKind::PagedElectionOutOfWeight {
1767						page: NextElectionPage::<T>::get().unwrap_or(
1768							EraElectionPlanner::<T>::election_pages().defensive_saturating_sub(1),
1769						),
1770						required: weight,
1771						had: weight_meter.remaining(),
1772					},
1773				));
1774			}
1775		}
1776
1777		fn on_initialize(_now: BlockNumberFor<T>) -> Weight {
1778			// Process our queue, using the era-specific nominators slashable setting.
1779			let mut consumed_weight = slashing::process_offence_for_era::<T>();
1780
1781			// apply any pending slashes after `SlashDeferDuration`.
1782			consumed_weight.saturating_accrue(T::DbWeight::get().reads(1));
1783			if let Some(active_era) = ActiveEra::<T>::get() {
1784				let slash_weight = Self::apply_unapplied_slashes(active_era.index);
1785				consumed_weight.saturating_accrue(slash_weight);
1786			}
1787
1788			consumed_weight
1789		}
1790
1791		fn integrity_test() {
1792			// ensure that we funnel the correct value to the `DataProvider::MaxVotesPerVoter`;
1793			assert_eq!(
1794				MaxNominationsOf::<T>::get(),
1795				<Self as ElectionDataProvider>::MaxVotesPerVoter::get()
1796			);
1797
1798			// and that MaxNominations is always greater than 1, since we count on this.
1799			assert!(!MaxNominationsOf::<T>::get().is_zero());
1800
1801			assert!(
1802				T::SlashDeferDuration::get() < T::BondingDuration::get() || T::BondingDuration::get() == 0,
1803				"As per documentation, slash defer duration ({}) should be less than bonding duration ({}).",
1804				T::SlashDeferDuration::get(),
1805				T::BondingDuration::get(),
1806			);
1807
1808			// Ensure NominatorFastUnbondDuration is not greater than BondingDuration
1809			assert!(
1810				T::NominatorFastUnbondDuration::get() <= T::BondingDuration::get(),
1811				"NominatorFastUnbondDuration ({}) must not exceed BondingDuration ({}).",
1812				T::NominatorFastUnbondDuration::get(),
1813				T::BondingDuration::get(),
1814			);
1815			// Ensure MaxPruningItems is reasonable (minimum 100 for efficiency)
1816			assert!(
1817				T::MaxPruningItems::get() >= 100,
1818				"MaxPruningItems must be at least 100 for efficient pruning, got: {}",
1819				T::MaxPruningItems::get()
1820			);
1821
1822			assert!(
1823				crate::POT_POOL_SIZE > T::HistoryDepth::get(),
1824				"POT_POOL_SIZE ({}) must be strictly greater than HistoryDepth ({}) \
1825				 to avoid reusing a pot slot whose era is still in the active history.",
1826				crate::POT_POOL_SIZE,
1827				T::HistoryDepth::get(),
1828			);
1829
1830			// If minting is disabled, EraPayout must be a noop to prevent double-minting.
1831			if T::DisableMinting::get() {
1832				let (v, r) = T::EraPayout::era_payout(
1833					BalanceOf::<T>::from(1u64),
1834					BalanceOf::<T>::from(1u64),
1835					1000u64,
1836				);
1837				assert!(
1838					v.is_zero() && r.is_zero(),
1839					"DisableMinting is true but EraPayout returns non-zero. \
1840					 Set EraPayout = () when DisableMinting = true."
1841				);
1842			}
1843		}
1844
1845		#[cfg(feature = "try-runtime")]
1846		fn try_state(n: BlockNumberFor<T>) -> Result<(), sp_runtime::TryRuntimeError> {
1847			Self::do_try_state(n)
1848		}
1849	}
1850
1851	#[pallet::call]
1852	impl<T: Config> Pallet<T> {
1853		/// Take the origin account as a stash and lock up `value` of its balance. `controller` will
1854		/// be the account that controls it.
1855		///
1856		/// `value` must be more than the `minimum_balance` specified by `T::Currency`.
1857		///
1858		/// The dispatch origin for this call must be _Signed_ by the stash account.
1859		///
1860		/// Emits `Bonded`.
1861		///
1862		/// NOTE: Two of the storage writes (`Self::bonded`, `Self::payee`) are _never_ cleaned
1863		/// unless the `origin` falls below _existential deposit_ (or equal to 0) and gets removed
1864		/// as dust.
1865		#[pallet::call_index(0)]
1866		#[pallet::weight(T::WeightInfo::bond())]
1867		pub fn bond(
1868			origin: OriginFor<T>,
1869			#[pallet::compact] value: BalanceOf<T>,
1870			payee: RewardDestination<T::AccountId>,
1871		) -> DispatchResult {
1872			let stash = ensure_signed(origin)?;
1873
1874			ensure!(!T::Filter::contains(&stash), Error::<T>::Restricted);
1875
1876			if StakingLedger::<T>::is_bonded(StakingAccount::Stash(stash.clone())) {
1877				return Err(Error::<T>::AlreadyBonded.into());
1878			}
1879
1880			// An existing controller cannot become a stash.
1881			if StakingLedger::<T>::is_bonded(StakingAccount::Controller(stash.clone())) {
1882				return Err(Error::<T>::AlreadyPaired.into());
1883			}
1884
1885			// Reject a bond which is lower than the minimum bond.
1886			if value < Self::min_chilled_bond() {
1887				return Err(Error::<T>::InsufficientBond.into());
1888			}
1889
1890			let stash_balance = asset::free_to_stake::<T>(&stash);
1891			let value = value.min(stash_balance);
1892			Self::deposit_event(Event::<T>::Bonded { stash: stash.clone(), amount: value });
1893			let ledger = StakingLedger::<T>::new(stash.clone(), value);
1894
1895			// You're auto-bonded forever, here. We might improve this by only bonding when
1896			// you actually validate/nominate and remove once you unbond __everything__.
1897			ledger.bond(payee)?;
1898
1899			Ok(())
1900		}
1901
1902		/// Add some extra amount that have appeared in the stash `free_balance` into the balance up
1903		/// for staking.
1904		///
1905		/// The dispatch origin for this call must be _Signed_ by the stash, not the controller.
1906		///
1907		/// Use this if there are additional funds in your stash account that you wish to bond.
1908		/// Unlike [`bond`](Self::bond) or [`unbond`](Self::unbond) this function does not impose
1909		/// any limitation on the amount that can be added.
1910		///
1911		/// Emits `Bonded`.
1912		#[pallet::call_index(1)]
1913		#[pallet::weight(T::WeightInfo::bond_extra())]
1914		pub fn bond_extra(
1915			origin: OriginFor<T>,
1916			#[pallet::compact] max_additional: BalanceOf<T>,
1917		) -> DispatchResult {
1918			let stash = ensure_signed(origin)?;
1919			ensure!(!T::Filter::contains(&stash), Error::<T>::Restricted);
1920			Self::do_bond_extra(&stash, max_additional)
1921		}
1922
1923		/// Schedule a portion of the stash to be unlocked ready for transfer out after the bond
1924		/// period ends. If this leaves an amount actively bonded less than
1925		/// [`asset::existential_deposit`], then it is increased to the full amount.
1926		///
1927		/// The dispatch origin for this call must be _Signed_ by the controller, not the stash.
1928		///
1929		/// Once the unlock period is done, you can call `withdraw_unbonded` to actually move
1930		/// the funds out of management ready for transfer.
1931		///
1932		/// No more than a limited number of unlocking chunks (see `MaxUnlockingChunks`)
1933		/// can co-exists at the same time. If there are no unlocking chunks slots available
1934		/// [`Call::withdraw_unbonded`] is called to remove some of the chunks (if possible).
1935		///
1936		/// If a user encounters the `InsufficientBond` error when calling this extrinsic,
1937		/// they should call `chill` first in order to free up their bonded funds.
1938		///
1939		/// Emits `Unbonded`.
1940		///
1941		/// See also [`Call::withdraw_unbonded`].
1942		#[pallet::call_index(2)]
1943		#[pallet::weight(
1944            T::WeightInfo::withdraw_unbonded_kill().saturating_add(T::WeightInfo::unbond()))
1945        ]
1946		pub fn unbond(
1947			origin: OriginFor<T>,
1948			#[pallet::compact] value: BalanceOf<T>,
1949		) -> DispatchResultWithPostInfo {
1950			let controller = ensure_signed(origin)?;
1951			let unlocking =
1952				Self::ledger(Controller(controller.clone())).map(|l| l.unlocking.len())?;
1953
1954			// if there are no unlocking chunks available, try to remove any chunks by withdrawing
1955			// funds that have fully unbonded.
1956			let maybe_withdraw_weight = {
1957				if unlocking == T::MaxUnlockingChunks::get() as usize {
1958					Some(Self::do_withdraw_unbonded(&controller)?)
1959				} else {
1960					None
1961				}
1962			};
1963
1964			// we need to fetch the ledger again because it may have been mutated in the call
1965			// to `Self::do_withdraw_unbonded` above.
1966			let mut ledger = Self::ledger(Controller(controller))?;
1967			let mut value = value.min(ledger.active);
1968			let stash = ledger.stash.clone();
1969
1970			// If unbonding all active stake, chill the stash first to avoid `InsufficientBond`
1971			// errors. This matches the behavior of pallet-staking.
1972			let chill_weight = if value >= ledger.active {
1973				Self::chill_stash(&stash);
1974				T::WeightInfo::chill()
1975			} else {
1976				Weight::zero()
1977			};
1978
1979			ensure!(
1980				ledger.unlocking.len() < T::MaxUnlockingChunks::get() as usize,
1981				Error::<T>::NoMoreChunks,
1982			);
1983
1984			if !value.is_zero() {
1985				ledger.active -= value;
1986
1987				// Avoid there being a dust balance left in the staking system.
1988				if ledger.active < asset::existential_deposit::<T>() {
1989					value += ledger.active;
1990					ledger.active = Zero::zero();
1991				}
1992
1993				let is_nominator = Nominators::<T>::contains_key(&stash);
1994
1995				let min_active_bond = if is_nominator {
1996					Self::min_nominator_bond()
1997				} else if Validators::<T>::contains_key(&stash) {
1998					Self::min_validator_bond()
1999				} else {
2000					// staker is chilled, no min bond.
2001					Zero::zero()
2002				};
2003
2004				// Make sure that the user maintains enough active bond for their role.
2005				// If a user runs into this error, they should chill first.
2006				ensure!(ledger.active >= min_active_bond, Error::<T>::InsufficientBond);
2007
2008				// Determine unbonding duration based on validator history.
2009				// If the account was a validator in recent eras (within BondingDuration), they must
2010				// wait the full BondingDuration even if they've switched to nominator role.
2011				// This prevents validators from avoiding slashing by switching roles and using the
2012				// shorter nominator unbonding period.
2013				let active_era = session_rotation::Rotator::<T>::active_era();
2014				let was_recent_validator = LastValidatorEra::<T>::get(&stash)
2015					.map(|last_era| active_era.saturating_sub(last_era) < T::BondingDuration::get())
2016					.unwrap_or(false);
2017
2018				let unbond_duration = if was_recent_validator {
2019					// Use full bonding duration for recent validators
2020					T::BondingDuration::get()
2021				} else {
2022					// Use nominator bonding duration for pure nominators
2023					<Self as sp_staking::StakingInterface>::nominator_bonding_duration()
2024				};
2025
2026				let era =
2027					session_rotation::Rotator::<T>::active_era().saturating_add(unbond_duration);
2028				if let Some(chunk) = ledger.unlocking.last_mut().filter(|chunk| chunk.era == era) {
2029					// To keep the chunk count down, we only keep one chunk per era. Since
2030					// `unlocking` is a FiFo queue, if a chunk exists for `era` we know that it will
2031					// be the last one.
2032					chunk.value = chunk.value.defensive_saturating_add(value)
2033				} else {
2034					ledger
2035						.unlocking
2036						.try_push(UnlockChunk { value, era })
2037						.map_err(|_| Error::<T>::NoMoreChunks)?;
2038				};
2039				// NOTE: ledger must be updated prior to calling `Self::weight_of`.
2040				ledger.update()?;
2041
2042				// update this staker in the sorted list, if they exist in it.
2043				if T::VoterList::contains(&stash) {
2044					let _ = T::VoterList::on_update(&stash, Self::weight_of(&stash));
2045				}
2046
2047				Self::deposit_event(Event::<T>::Unbonded { stash, amount: value, era });
2048			}
2049
2050			let actual_weight = if let Some(withdraw_weight) = maybe_withdraw_weight {
2051				Some(
2052					T::WeightInfo::unbond()
2053						.saturating_add(withdraw_weight)
2054						.saturating_add(chill_weight),
2055				)
2056			} else {
2057				Some(T::WeightInfo::unbond().saturating_add(chill_weight))
2058			};
2059
2060			Ok(actual_weight.into())
2061		}
2062
2063		/// Remove any stake that has been fully unbonded and is ready for withdrawal.
2064		///
2065		/// Stake is considered fully unbonded once [`Config::BondingDuration`] has elapsed since
2066		/// the unbonding was initiated. In rare cases—such as when offences for the unbonded era
2067		/// have been reported but not yet processed—withdrawal is restricted to eras for which
2068		/// all offences have been processed.
2069		///
2070		/// The unlocked stake will be returned as free balance in the stash account.
2071		///
2072		/// The dispatch origin for this call must be _Signed_ by the controller.
2073		///
2074		/// Emits `Withdrawn`.
2075		///
2076		/// See also [`Call::unbond`].
2077		///
2078		/// ## Parameters
2079		///
2080		/// - `num_slashing_spans`: **Deprecated**. Retained only for backward compatibility; this
2081		///   parameter has no effect.
2082		#[pallet::call_index(3)]
2083		#[pallet::weight(T::WeightInfo::withdraw_unbonded_kill())]
2084		pub fn withdraw_unbonded(
2085			origin: OriginFor<T>,
2086			_num_slashing_spans: u32,
2087		) -> DispatchResultWithPostInfo {
2088			let controller = ensure_signed(origin)?;
2089
2090			let actual_weight = Self::do_withdraw_unbonded(&controller)?;
2091			Ok(Some(actual_weight).into())
2092		}
2093
2094		/// Declare the desire to validate for the origin controller.
2095		///
2096		/// Effects will be felt at the beginning of the next era.
2097		///
2098		/// The dispatch origin for this call must be _Signed_ by the controller, not the stash.
2099		#[pallet::call_index(4)]
2100		#[pallet::weight(T::WeightInfo::validate())]
2101		pub fn validate(origin: OriginFor<T>, prefs: ValidatorPrefs) -> DispatchResult {
2102			let controller = ensure_signed(origin)?;
2103
2104			let ledger = Self::ledger(Controller(controller))?;
2105
2106			ensure!(ledger.active >= Self::min_validator_bond(), Error::<T>::InsufficientBond);
2107			let stash = &ledger.stash;
2108
2109			// ensure their commission is correct.
2110			ensure!(prefs.commission >= MinCommission::<T>::get(), Error::<T>::CommissionTooLow);
2111			ensure!(prefs.commission <= MaxCommission::<T>::get(), Error::<T>::CommissionTooHigh);
2112
2113			// Only check limits if they are not already a validator.
2114			if !Validators::<T>::contains_key(stash) {
2115				// If this error is reached, we need to adjust the `MinValidatorBond` and start
2116				// calling `chill_other`. Until then, we explicitly block new validators to protect
2117				// the runtime.
2118				if let Some(max_validators) = MaxValidatorsCount::<T>::get() {
2119					ensure!(
2120						Validators::<T>::count() < max_validators,
2121						Error::<T>::TooManyValidators
2122					);
2123				}
2124			}
2125
2126			Self::do_remove_nominator(stash);
2127			Self::do_add_validator(stash, prefs.clone());
2128			Self::deposit_event(Event::<T>::ValidatorPrefsSet { stash: ledger.stash, prefs });
2129
2130			Ok(())
2131		}
2132
2133		/// Declare the desire to nominate `targets` for the origin controller.
2134		///
2135		/// Effects will be felt at the beginning of the next era.
2136		///
2137		/// The dispatch origin for this call must be _Signed_ by the controller, not the stash.
2138		#[pallet::call_index(5)]
2139		#[pallet::weight(T::WeightInfo::nominate(targets.len() as u32))]
2140		pub fn nominate(
2141			origin: OriginFor<T>,
2142			targets: Vec<AccountIdLookupOf<T>>,
2143		) -> DispatchResult {
2144			let controller = ensure_signed(origin)?;
2145
2146			let ledger = Self::ledger(StakingAccount::Controller(controller.clone()))?;
2147
2148			ensure!(ledger.active >= Self::min_nominator_bond(), Error::<T>::InsufficientBond);
2149			let stash = &ledger.stash;
2150
2151			// Only check limits if they are not already a nominator.
2152			if !Nominators::<T>::contains_key(stash) {
2153				// If this error is reached, we need to adjust the `MinNominatorBond` and start
2154				// calling `chill_other`. Until then, we explicitly block new nominators to protect
2155				// the runtime.
2156				if let Some(max_nominators) = MaxNominatorsCount::<T>::get() {
2157					ensure!(
2158						Nominators::<T>::count() < max_nominators,
2159						Error::<T>::TooManyNominators
2160					);
2161				}
2162			}
2163
2164			// dedup targets
2165			let mut targets = targets
2166				.into_iter()
2167				.map(|t| T::Lookup::lookup(t).map_err(DispatchError::from))
2168				.collect::<Result<Vec<_>, _>>()?;
2169			targets.sort();
2170			targets.dedup();
2171
2172			ensure!(!targets.is_empty(), Error::<T>::EmptyTargets);
2173			ensure!(
2174				targets.len() <= T::NominationsQuota::get_quota(ledger.active) as usize,
2175				Error::<T>::TooManyTargets
2176			);
2177
2178			let old = Nominators::<T>::get(stash).map_or_else(Vec::new, |x| x.targets.into_inner());
2179
2180			let targets: BoundedVec<_, _> = targets
2181				.into_iter()
2182				.map(|n| {
2183					if old.contains(&n) ||
2184						(Validators::<T>::contains_key(&n) && !Validators::<T>::get(&n).blocked)
2185					{
2186						Ok(n)
2187					} else {
2188						Err(Error::<T>::BadTarget.into())
2189					}
2190				})
2191				.collect::<Result<Vec<_>, DispatchError>>()?
2192				.try_into()
2193				.map_err(|_| Error::<T>::TooManyNominators)?;
2194
2195			let nominations = Nominations {
2196				targets,
2197				// Initial nominations are considered submitted at era 0. See `Nominations` doc.
2198				submitted_in: CurrentEra::<T>::get().unwrap_or(0),
2199				suppressed: false,
2200			};
2201
2202			Self::do_remove_validator(stash);
2203			Self::do_add_nominator(stash, nominations);
2204			Ok(())
2205		}
2206
2207		/// Declare no desire to either validate or nominate.
2208		///
2209		/// Effects will be felt at the beginning of the next era.
2210		///
2211		/// The dispatch origin for this call must be _Signed_ by the controller, not the stash.
2212		///
2213		/// ## Complexity
2214		/// - Independent of the arguments. Insignificant complexity.
2215		/// - Contains one read.
2216		/// - Writes are limited to the `origin` account key.
2217		#[pallet::call_index(6)]
2218		#[pallet::weight(T::WeightInfo::chill())]
2219		pub fn chill(origin: OriginFor<T>) -> DispatchResult {
2220			let controller = ensure_signed(origin)?;
2221
2222			let ledger = Self::ledger(StakingAccount::Controller(controller))?;
2223
2224			Self::chill_stash(&ledger.stash);
2225			Ok(())
2226		}
2227
2228		/// (Re-)set the payment target for a controller.
2229		///
2230		/// Effects will be felt instantly (as soon as this function is completed successfully).
2231		///
2232		/// The dispatch origin for this call must be _Signed_ by the controller, not the stash.
2233		#[pallet::call_index(7)]
2234		#[pallet::weight(T::WeightInfo::set_payee())]
2235		pub fn set_payee(
2236			origin: OriginFor<T>,
2237			payee: RewardDestination<T::AccountId>,
2238		) -> DispatchResult {
2239			let controller = ensure_signed(origin)?;
2240			let ledger = Self::ledger(Controller(controller.clone()))?;
2241
2242			ensure!(
2243				(payee != {
2244					#[allow(deprecated)]
2245					RewardDestination::Controller
2246				}),
2247				Error::<T>::ControllerDeprecated
2248			);
2249
2250			let _ = ledger
2251				.set_payee(payee)
2252				.defensive_proof("ledger was retrieved from storage, thus it's bonded; qed.")?;
2253
2254			Ok(())
2255		}
2256
2257		/// (Re-)sets the controller of a stash to the stash itself. This function previously
2258		/// accepted a `controller` argument to set the controller to an account other than the
2259		/// stash itself. This functionality has now been removed, now only setting the controller
2260		/// to the stash, if it is not already.
2261		///
2262		/// Effects will be felt instantly (as soon as this function is completed successfully).
2263		///
2264		/// The dispatch origin for this call must be _Signed_ by the stash, not the controller.
2265		#[pallet::call_index(8)]
2266		#[pallet::weight(T::WeightInfo::set_controller())]
2267		pub fn set_controller(origin: OriginFor<T>) -> DispatchResult {
2268			let stash = ensure_signed(origin)?;
2269
2270			Self::ledger(StakingAccount::Stash(stash.clone())).map(|ledger| {
2271				let controller = ledger.controller()
2272                    .defensive_proof("Ledger's controller field didn't exist. The controller should have been fetched using StakingLedger.")
2273                    .ok_or(Error::<T>::NotController)?;
2274
2275				if controller == stash {
2276					// Stash is already its own controller.
2277					return Err(Error::<T>::AlreadyPaired.into())
2278				}
2279
2280				let _ = ledger.set_controller_to_stash()?;
2281				Ok(())
2282			})?
2283		}
2284
2285		/// Sets the ideal number of validators.
2286		///
2287		/// The dispatch origin must be Root.
2288		#[pallet::call_index(9)]
2289		#[pallet::weight(T::WeightInfo::set_validator_count())]
2290		pub fn set_validator_count(
2291			origin: OriginFor<T>,
2292			#[pallet::compact] new: u32,
2293		) -> DispatchResult {
2294			ensure_root(origin)?;
2295
2296			ensure!(new <= T::MaxValidatorSet::get(), Error::<T>::TooManyValidators);
2297
2298			ValidatorCount::<T>::put(new);
2299			Ok(())
2300		}
2301
2302		/// Increments the ideal number of validators up to maximum of
2303		/// `T::MaxValidatorSet`.
2304		///
2305		/// The dispatch origin must be Root.
2306		#[pallet::call_index(10)]
2307		#[pallet::weight(T::WeightInfo::set_validator_count())]
2308		pub fn increase_validator_count(
2309			origin: OriginFor<T>,
2310			#[pallet::compact] additional: u32,
2311		) -> DispatchResult {
2312			ensure_root(origin)?;
2313			let old = ValidatorCount::<T>::get();
2314			let new = old.checked_add(additional).ok_or(ArithmeticError::Overflow)?;
2315
2316			ensure!(new <= T::MaxValidatorSet::get(), Error::<T>::TooManyValidators);
2317
2318			ValidatorCount::<T>::put(new);
2319			Ok(())
2320		}
2321
2322		/// Scale up the ideal number of validators by a factor up to maximum of
2323		/// `T::MaxValidatorSet`.
2324		///
2325		/// The dispatch origin must be Root.
2326		#[pallet::call_index(11)]
2327		#[pallet::weight(T::WeightInfo::set_validator_count())]
2328		pub fn scale_validator_count(origin: OriginFor<T>, factor: Percent) -> DispatchResult {
2329			ensure_root(origin)?;
2330			let old = ValidatorCount::<T>::get();
2331			let new = old.checked_add(factor.mul_floor(old)).ok_or(ArithmeticError::Overflow)?;
2332
2333			ensure!(new <= T::MaxValidatorSet::get(), Error::<T>::TooManyValidators);
2334
2335			ValidatorCount::<T>::put(new);
2336			Ok(())
2337		}
2338
2339		/// Force there to be no new eras indefinitely.
2340		///
2341		/// The dispatch origin must be Root.
2342		///
2343		/// # Warning
2344		///
2345		/// The election process starts multiple blocks before the end of the era.
2346		/// Thus the election process may be ongoing when this is called. In this case the
2347		/// election will continue until the next era is triggered.
2348		#[pallet::call_index(12)]
2349		#[pallet::weight(T::WeightInfo::force_no_eras())]
2350		pub fn force_no_eras(origin: OriginFor<T>) -> DispatchResult {
2351			ensure_root(origin)?;
2352			Self::set_force_era(Forcing::ForceNone);
2353			Ok(())
2354		}
2355
2356		/// Force there to be a new era at the end of the next session. After this, it will be
2357		/// reset to normal (non-forced) behaviour.
2358		///
2359		/// The dispatch origin must be Root.
2360		///
2361		/// # Warning
2362		///
2363		/// The election process starts multiple blocks before the end of the era.
2364		/// If this is called just before a new era is triggered, the election process may not
2365		/// have enough blocks to get a result.
2366		#[pallet::call_index(13)]
2367		#[pallet::weight(T::WeightInfo::force_new_era())]
2368		pub fn force_new_era(origin: OriginFor<T>) -> DispatchResult {
2369			ensure_root(origin)?;
2370			Self::set_force_era(Forcing::ForceNew);
2371			Ok(())
2372		}
2373
2374		/// Force a current staker to become completely unstaked, immediately.
2375		///
2376		/// The dispatch origin must be Root.
2377		/// ## Parameters
2378		///
2379		/// - `stash`: The stash account to be unstaked.
2380		/// - `num_slashing_spans`: **Deprecated**. This parameter is retained for backward
2381		/// compatibility. It no longer has any effect.
2382		#[pallet::call_index(15)]
2383		#[pallet::weight(T::WeightInfo::force_unstake())]
2384		pub fn force_unstake(
2385			origin: OriginFor<T>,
2386			stash: T::AccountId,
2387			_num_slashing_spans: u32,
2388		) -> DispatchResult {
2389			ensure_root(origin)?;
2390
2391			// Remove all staking-related information and lock.
2392			Self::kill_stash(&stash)?;
2393
2394			Ok(())
2395		}
2396
2397		/// Force there to be a new era at the end of sessions indefinitely.
2398		///
2399		/// The dispatch origin must be Root.
2400		///
2401		/// # Warning
2402		///
2403		/// The election process starts multiple blocks before the end of the era.
2404		/// If this is called just before a new era is triggered, the election process may not
2405		/// have enough blocks to get a result.
2406		#[pallet::call_index(16)]
2407		#[pallet::weight(T::WeightInfo::force_new_era_always())]
2408		pub fn force_new_era_always(origin: OriginFor<T>) -> DispatchResult {
2409			ensure_root(origin)?;
2410			Self::set_force_era(Forcing::ForceAlways);
2411			Ok(())
2412		}
2413
2414		/// Cancels scheduled slashes for a given era before they are applied.
2415		///
2416		/// This function allows `T::AdminOrigin` to cancel pending slashes for specified validators
2417		/// in a given era. The cancelled slashes are stored and will be checked when applying
2418		/// slashes.
2419		///
2420		/// ## Parameters
2421		/// - `era`: The staking era for which slashes should be cancelled. This is the era where
2422		///   the slash would be applied, not the era in which the offence was committed.
2423		/// - `validator_slashes`: A list of validator stash accounts and their slash fractions to
2424		///   be cancelled.
2425		#[pallet::call_index(17)]
2426		#[pallet::weight(T::WeightInfo::cancel_deferred_slash(validator_slashes.len() as u32))]
2427		pub fn cancel_deferred_slash(
2428			origin: OriginFor<T>,
2429			era: EraIndex,
2430			validator_slashes: Vec<(T::AccountId, Perbill)>,
2431		) -> DispatchResult {
2432			T::AdminOrigin::ensure_origin(origin)?;
2433			ensure!(!validator_slashes.is_empty(), Error::<T>::EmptyTargets);
2434
2435			// Get current cancelled slashes for this era
2436			let mut cancelled_slashes = CancelledSlashes::<T>::get(&era);
2437
2438			// Process each validator slash
2439			for (validator, slash_fraction) in validator_slashes {
2440				// Since this is gated by admin origin, we don't need to check if they are really
2441				// validators and trust governance to correctly set the parameters.
2442
2443				// Remove any existing entry for this validator
2444				cancelled_slashes.retain(|(v, _)| v != &validator);
2445
2446				// Add the validator with the specified slash fraction
2447				cancelled_slashes
2448					.try_push((validator.clone(), slash_fraction))
2449					.map_err(|_| Error::<T>::BoundNotMet)
2450					.defensive_proof("cancelled_slashes should have capacity for all validators")?;
2451
2452				Self::deposit_event(Event::<T>::SlashCancelled { slash_era: era, validator });
2453			}
2454
2455			// Update storage
2456			CancelledSlashes::<T>::insert(&era, cancelled_slashes);
2457
2458			Ok(())
2459		}
2460
2461		/// Pay out next page of the stakers behind a validator for the given era.
2462		///
2463		/// - `validator_stash` is the stash account of the validator.
2464		/// - `era` may be any era between `[current_era - history_depth; current_era]`.
2465		///
2466		/// The origin of this call must be _Signed_. Any account can call this function, even if
2467		/// it is not one of the stakers.
2468		///
2469		/// The reward payout could be paged in case there are too many nominators backing the
2470		/// `validator_stash`. This call will payout unpaid pages in an ascending order. To claim a
2471		/// specific page, use `payout_stakers_by_page`.`
2472		///
2473		/// If all pages are claimed, it returns an error `InvalidPage`.
2474		#[pallet::call_index(18)]
2475		#[pallet::weight(T::WeightInfo::payout_stakers_alive_staked(T::MaxExposurePageSize::get()))]
2476		pub fn payout_stakers(
2477			origin: OriginFor<T>,
2478			validator_stash: T::AccountId,
2479			era: EraIndex,
2480		) -> DispatchResultWithPostInfo {
2481			ensure_signed(origin)?;
2482
2483			Self::do_payout_stakers(validator_stash, era)
2484		}
2485
2486		/// Rebond a portion of the stash scheduled to be unlocked.
2487		///
2488		/// The dispatch origin must be signed by the controller.
2489		#[pallet::call_index(19)]
2490		#[pallet::weight(T::WeightInfo::rebond(T::MaxUnlockingChunks::get() as u32))]
2491		pub fn rebond(
2492			origin: OriginFor<T>,
2493			#[pallet::compact] value: BalanceOf<T>,
2494		) -> DispatchResultWithPostInfo {
2495			let controller = ensure_signed(origin)?;
2496			let ledger = Self::ledger(Controller(controller))?;
2497
2498			ensure!(!T::Filter::contains(&ledger.stash), Error::<T>::Restricted);
2499			ensure!(!ledger.unlocking.is_empty(), Error::<T>::NoUnlockChunk);
2500
2501			let initial_unlocking = ledger.unlocking.len() as u32;
2502			let (ledger, rebonded_value) = ledger.rebond(value);
2503			// Last check: the new active amount of ledger must be more than min bond.
2504			ensure!(ledger.active >= Self::min_chilled_bond(), Error::<T>::InsufficientBond);
2505
2506			Self::deposit_event(Event::<T>::Bonded {
2507				stash: ledger.stash.clone(),
2508				amount: rebonded_value,
2509			});
2510
2511			let stash = ledger.stash.clone();
2512			let final_unlocking = ledger.unlocking.len();
2513
2514			// NOTE: ledger must be updated prior to calling `Self::weight_of`.
2515			ledger.update()?;
2516			if T::VoterList::contains(&stash) {
2517				let _ = T::VoterList::on_update(&stash, Self::weight_of(&stash));
2518			}
2519
2520			let removed_chunks = 1u32 // for the case where the last iterated chunk is not removed
2521				.saturating_add(initial_unlocking)
2522				.saturating_sub(final_unlocking as u32);
2523			Ok(Some(T::WeightInfo::rebond(removed_chunks)).into())
2524		}
2525
2526		/// Remove all data structures concerning a staker/stash once it is at a state where it can
2527		/// be considered `dust` in the staking system. The requirements are:
2528		///
2529		/// 1. the `total_balance` of the stash is below the existential deposit.
2530		/// 2. or, the `ledger.total` of the stash is below the existential deposit.
2531		///
2532		/// The former can happen in cases like a slash; the latter when a fully unbonded account
2533		/// is still receiving staking rewards in `RewardDestination::Staked`.
2534		///
2535		/// The gate is intentionally the existential deposit and *not* `min_chilled_bond`: a
2536		/// governance change to `MinValidatorBond` / `MinNominatorBond` must not turn previously
2537		/// safe stashes into permissionlessly reapable ones. Accounts that fall below the new
2538		/// minimums after such a change should be `chill_other`-ed (which has a density gate and
2539		/// does not destroy the ledger), not reaped.
2540		///
2541		/// It can be called by anyone, as long as `stash` meets the above requirements.
2542		///
2543		/// Refunds the transaction fees upon successful execution.
2544		///
2545		/// ## Parameters
2546		///
2547		/// - `stash`: The stash account to be reaped.
2548		/// - `num_slashing_spans`: **Deprecated**. This parameter is retained for backward
2549		/// compatibility. It no longer has any effect.
2550		#[pallet::call_index(20)]
2551		#[pallet::weight(T::WeightInfo::reap_stash())]
2552		pub fn reap_stash(
2553			origin: OriginFor<T>,
2554			stash: T::AccountId,
2555			_num_slashing_spans: u32,
2556		) -> DispatchResultWithPostInfo {
2557			let _ = ensure_signed(origin)?;
2558
2559			// virtual stakers should not be allowed to be reaped.
2560			ensure!(!Self::is_virtual_staker(&stash), Error::<T>::VirtualStakerNotAllowed);
2561
2562			let ed = asset::existential_deposit::<T>();
2563			let origin_balance = asset::total_balance::<T>(&stash);
2564			let ledger_total =
2565				Self::ledger(Stash(stash.clone())).map(|l| l.total).unwrap_or_default();
2566			let reapable = origin_balance < ed ||
2567				origin_balance.is_zero() ||
2568				ledger_total < ed ||
2569				ledger_total.is_zero();
2570			ensure!(reapable, Error::<T>::FundedTarget);
2571
2572			// Remove all staking-related information and lock.
2573			Self::kill_stash(&stash)?;
2574
2575			Ok(Pays::No.into())
2576		}
2577
2578		/// Remove the given nominations from the calling validator.
2579		///
2580		/// Effects will be felt at the beginning of the next era.
2581		///
2582		/// The dispatch origin for this call must be _Signed_ by the controller, not the stash.
2583		///
2584		/// - `who`: A list of nominator stash accounts who are nominating this validator which
2585		///   should no longer be nominating this validator.
2586		///
2587		/// Note: Making this call only makes sense if you first set the validator preferences to
2588		/// block any further nominations.
2589		#[pallet::call_index(21)]
2590		#[pallet::weight(T::WeightInfo::kick(who.len() as u32))]
2591		pub fn kick(origin: OriginFor<T>, who: Vec<AccountIdLookupOf<T>>) -> DispatchResult {
2592			let controller = ensure_signed(origin)?;
2593			let ledger = Self::ledger(Controller(controller))?;
2594			let stash = &ledger.stash;
2595
2596			for nom_stash in who
2597				.into_iter()
2598				.map(T::Lookup::lookup)
2599				.collect::<Result<Vec<T::AccountId>, _>>()?
2600				.into_iter()
2601			{
2602				Nominators::<T>::mutate(&nom_stash, |maybe_nom| {
2603					if let Some(ref mut nom) = maybe_nom {
2604						if let Some(pos) = nom.targets.iter().position(|v| v == stash) {
2605							nom.targets.swap_remove(pos);
2606							Self::deposit_event(Event::<T>::Kicked {
2607								nominator: nom_stash.clone(),
2608								stash: stash.clone(),
2609							});
2610						}
2611					}
2612				});
2613			}
2614
2615			Ok(())
2616		}
2617
2618		/// Update the various staking configurations .
2619		///
2620		/// * `min_nominator_bond`: The minimum active bond needed to be a nominator.
2621		/// * `min_validator_bond`: The minimum active bond needed to be a validator.
2622		/// * `max_nominator_count`: The max number of users who can be a nominator at once. When
2623		///   set to `None`, no limit is enforced.
2624		/// * `max_validator_count`: The max number of users who can be a validator at once. When
2625		///   set to `None`, no limit is enforced.
2626		/// * `chill_threshold`: The ratio of `max_nominator_count` or `max_validator_count` which
2627		///   should be filled in order for the `chill_other` transaction to work.
2628		/// * `min_commission`: The minimum amount of commission that each validators must maintain.
2629		///   This is checked only upon calling `validate`. Existing validators are not affected.
2630		/// * `chill_inactive_threshold`: The number of eras a validator can remain inactive during
2631		///   the last [`Config::HistoryDepth`] eras before being subject to chilling becuase of
2632		///   inactivity.
2633		///
2634		/// RuntimeOrigin must be Root to call this function.
2635		///
2636		/// NOTE: Existing nominators and validators will not be affected by this update.
2637		/// to kick people under the new limits, `chill_other` should be called.
2638		// We assume the worst case for this call is either: all items are set or all items are
2639		// removed.
2640		#[pallet::call_index(22)]
2641		#[pallet::weight(
2642			T::WeightInfo::set_staking_configs_all_set()
2643				.max(T::WeightInfo::set_staking_configs_all_remove())
2644		)]
2645		pub fn set_staking_configs(
2646			origin: OriginFor<T>,
2647			min_nominator_bond: ConfigOp<BalanceOf<T>>,
2648			min_validator_bond: ConfigOp<BalanceOf<T>>,
2649			max_nominator_count: ConfigOp<u32>,
2650			max_validator_count: ConfigOp<u32>,
2651			chill_threshold: ConfigOp<Percent>,
2652			min_commission: ConfigOp<Perbill>,
2653			max_staked_rewards: ConfigOp<Percent>,
2654			are_nominators_slashable: ConfigOp<bool>,
2655			chill_inactive_threshold: ConfigOp<u32>,
2656		) -> DispatchResult {
2657			ensure_root(origin)?;
2658
2659			if let ConfigOp::Set(threshold) = chill_inactive_threshold {
2660				ensure!(
2661					threshold > 1 && threshold <= T::HistoryDepth::get(),
2662					Error::<T>::InvalidChillInactiveThreshold
2663				);
2664			}
2665
2666			macro_rules! config_op_exp {
2667				($storage:ty, $op:ident) => {
2668					match $op {
2669						ConfigOp::Noop => (),
2670						ConfigOp::Set(v) => <$storage>::put(v),
2671						ConfigOp::Remove => <$storage>::kill(),
2672					}
2673				};
2674			}
2675
2676			config_op_exp!(MinNominatorBond<T>, min_nominator_bond);
2677			config_op_exp!(MinValidatorBond<T>, min_validator_bond);
2678			config_op_exp!(MaxNominatorsCount<T>, max_nominator_count);
2679			config_op_exp!(MaxValidatorsCount<T>, max_validator_count);
2680			config_op_exp!(ChillThreshold<T>, chill_threshold);
2681			config_op_exp!(MinCommission<T>, min_commission);
2682			config_op_exp!(MaxStakedRewards<T>, max_staked_rewards);
2683			config_op_exp!(AreNominatorsSlashable<T>, are_nominators_slashable);
2684			config_op_exp!(ChillInactiveThreshold<T>, chill_inactive_threshold);
2685
2686			Ok(())
2687		}
2688		/// Declare a `controller` to stop participating as either a validator or nominator.
2689		///
2690		/// Effects will be felt at the beginning of the next era.
2691		///
2692		/// The dispatch origin for this call must be _Signed_, but can be called by anyone.
2693		///
2694		/// If the caller is the same as the controller being targeted, then no further checks are
2695		/// enforced, and this function behaves just like `chill`.
2696		///
2697		/// If the caller is different than the controller being targeted, the following conditions
2698		/// must be met:
2699		///
2700		/// * `controller` must belong to a nominator who has become non-decodable,
2701		///
2702		/// Or:
2703		///
2704		/// * A `ChillThreshold` must be set and checked which defines how close to the max
2705		///   nominators or validators we must reach before users can start chilling one-another.
2706		/// * A `MaxNominatorCount` and `MaxValidatorCount` must be set which is used to determine
2707		///   how close we are to the threshold.
2708		/// * A `MinNominatorBond` and `MinValidatorBond` must be set and checked, which determines
2709		///   if this is a person that should be chilled because they have not met the threshold
2710		///   bond required.
2711		///
2712		/// This can be helpful if bond requirements are updated, and we need to remove old users
2713		/// who do not satisfy these requirements.
2714		#[pallet::call_index(23)]
2715		#[pallet::weight(T::WeightInfo::chill_other())]
2716		pub fn chill_other(origin: OriginFor<T>, stash: T::AccountId) -> DispatchResult {
2717			// Anyone can call this function.
2718			let caller = ensure_signed(origin)?;
2719			let ledger = Self::ledger(Stash(stash.clone()))?;
2720			let controller = ledger
2721				.controller()
2722				.defensive_proof(
2723					"Ledger's controller field didn't exist. The controller should have been fetched using StakingLedger.",
2724				)
2725				.ok_or(Error::<T>::NotController)?;
2726
2727			// In order for one user to chill another user, the following conditions must be met:
2728			//
2729			// * `controller` belongs to a nominator who has become non-decodable,
2730			//
2731			// Or
2732			//
2733			// * A `ChillThreshold` is set which defines how close to the max nominators or
2734			//   validators we must reach before users can start chilling one-another.
2735			// * A `MaxNominatorCount` and `MaxValidatorCount` which is used to determine how close
2736			//   we are to the threshold.
2737			// * A `MinNominatorBond` and `MinValidatorBond` which is the final condition checked to
2738			//   determine this is a person that should be chilled because they have not met the
2739			//   threshold bond required.
2740			//
2741			// Otherwise, if caller is the same as the controller, this is just like `chill`.
2742
2743			if Nominators::<T>::contains_key(&stash) && Nominators::<T>::get(&stash).is_none() {
2744				Self::chill_stash(&stash);
2745				return Ok(());
2746			}
2747
2748			if caller != controller {
2749				let threshold = ChillThreshold::<T>::get().ok_or(Error::<T>::CannotChillOther)?;
2750				let min_active_bond = if Nominators::<T>::contains_key(&stash) {
2751					let max_nominator_count =
2752						MaxNominatorsCount::<T>::get().ok_or(Error::<T>::CannotChillOther)?;
2753					let current_nominator_count = Nominators::<T>::count();
2754					ensure!(
2755						threshold * max_nominator_count < current_nominator_count,
2756						Error::<T>::CannotChillOther
2757					);
2758					Self::min_nominator_bond()
2759				} else if Validators::<T>::contains_key(&stash) {
2760					let max_validator_count =
2761						MaxValidatorsCount::<T>::get().ok_or(Error::<T>::CannotChillOther)?;
2762					let current_validator_count = Validators::<T>::count();
2763					ensure!(
2764						threshold * max_validator_count < current_validator_count,
2765						Error::<T>::CannotChillOther
2766					);
2767					Self::min_validator_bond()
2768				} else {
2769					Zero::zero()
2770				};
2771
2772				ensure!(ledger.active < min_active_bond, Error::<T>::CannotChillOther);
2773			}
2774
2775			Self::chill_stash(&stash);
2776			Ok(())
2777		}
2778
2779		/// Clamps a validator's commission to the `[MinCommission, MaxCommission]` range.
2780		///
2781		/// Named `force_apply_min_commission` for legacy reasons — it also enforces the
2782		/// maximum. Any account can call this.
2783		#[pallet::call_index(24)]
2784		#[pallet::weight(T::WeightInfo::force_apply_min_commission())]
2785		pub fn force_apply_min_commission(
2786			origin: OriginFor<T>,
2787			validator_stash: T::AccountId,
2788		) -> DispatchResult {
2789			ensure_signed(origin)?;
2790			let min_commission = MinCommission::<T>::get();
2791			let max_commission = MaxCommission::<T>::get();
2792			Validators::<T>::try_mutate_exists(validator_stash, |maybe_prefs| {
2793				maybe_prefs
2794					.as_mut()
2795					.map(|prefs| {
2796						if prefs.commission < min_commission {
2797							prefs.commission = min_commission;
2798						}
2799						if prefs.commission > max_commission {
2800							prefs.commission = max_commission;
2801						}
2802					})
2803					.ok_or(Error::<T>::NotStash)
2804			})?;
2805			Ok(())
2806		}
2807
2808		/// Sets the minimum amount of commission that each validators must maintain.
2809		///
2810		/// This call has lower privilege requirements than `set_staking_config` and can be called
2811		/// by the `T::AdminOrigin`. Root can always call this.
2812		#[pallet::call_index(25)]
2813		#[pallet::weight(T::WeightInfo::set_min_commission())]
2814		pub fn set_min_commission(origin: OriginFor<T>, new: Perbill) -> DispatchResult {
2815			T::AdminOrigin::ensure_origin(origin)?;
2816			ensure!(new <= MaxCommission::<T>::get(), Error::<T>::CommissionTooHigh);
2817			MinCommission::<T>::put(new);
2818			Ok(())
2819		}
2820
2821		/// Pay out a page of the stakers behind a validator for the given era and page.
2822		///
2823		/// - `validator_stash` is the stash account of the validator.
2824		/// - `era` may be any era between `[current_era - history_depth; current_era]`.
2825		/// - `page` is the page index of nominators to pay out with value between 0 and
2826		///   `num_nominators / T::MaxExposurePageSize`.
2827		///
2828		/// The origin of this call must be _Signed_. Any account can call this function, even if
2829		/// it is not one of the stakers.
2830		///
2831		/// If a validator has more than [`Config::MaxExposurePageSize`] nominators backing
2832		/// them, then the list of nominators is paged, with each page being capped at
2833		/// [`Config::MaxExposurePageSize`]. If a validator has more than one page of nominators,
2834		/// the call needs to be made for each page separately in order for all the nominators
2835		/// backing a validator to receive the reward. The nominators are not sorted across pages
2836		/// and so it should not be assumed the highest staker would be on the topmost page and vice
2837		/// versa. If rewards are not claimed in [`Config::HistoryDepth`] eras, they are lost.
2838		///
2839		/// The validator's own reward (commission + own-stake share) is prorated across pages
2840		/// proportional to each page's stake. The full validator reward is the sum across all
2841		/// pages.
2842		#[pallet::call_index(26)]
2843		#[pallet::weight(T::WeightInfo::payout_stakers_alive_staked(T::MaxExposurePageSize::get()))]
2844		pub fn payout_stakers_by_page(
2845			origin: OriginFor<T>,
2846			validator_stash: T::AccountId,
2847			era: EraIndex,
2848			page: Page,
2849		) -> DispatchResultWithPostInfo {
2850			ensure_signed(origin)?;
2851			Self::do_payout_stakers_by_page(validator_stash, era, page)
2852		}
2853
2854		/// Migrates an account's `RewardDestination::Controller` to
2855		/// `RewardDestination::Account(controller)`.
2856		///
2857		/// Effects will be felt instantly (as soon as this function is completed successfully).
2858		///
2859		/// This will waive the transaction fee if the `payee` is successfully migrated.
2860		#[pallet::call_index(27)]
2861		#[pallet::weight(T::WeightInfo::update_payee())]
2862		pub fn update_payee(
2863			origin: OriginFor<T>,
2864			controller: T::AccountId,
2865		) -> DispatchResultWithPostInfo {
2866			let _ = ensure_signed(origin)?;
2867			let ledger = Self::ledger(StakingAccount::Controller(controller.clone()))?;
2868
2869			ensure!(
2870				(Payee::<T>::get(&ledger.stash) == {
2871					#[allow(deprecated)]
2872					Some(RewardDestination::Controller)
2873				}),
2874				Error::<T>::NotController
2875			);
2876
2877			let _ = ledger
2878				.set_payee(RewardDestination::Account(controller))
2879				.defensive_proof("ledger should have been previously retrieved from storage.")?;
2880
2881			Ok(Pays::No.into())
2882		}
2883
2884		/// Updates a batch of controller accounts to their corresponding stash account if they are
2885		/// not the same. Ignores any controller accounts that do not exist, and does not operate if
2886		/// the stash and controller are already the same.
2887		///
2888		/// Effects will be felt instantly (as soon as this function is completed successfully).
2889		///
2890		/// The dispatch origin must be `T::AdminOrigin`.
2891		#[pallet::call_index(28)]
2892		#[pallet::weight(T::WeightInfo::deprecate_controller_batch(controllers.len() as u32))]
2893		pub fn deprecate_controller_batch(
2894			origin: OriginFor<T>,
2895			controllers: BoundedVec<T::AccountId, T::MaxControllersInDeprecationBatch>,
2896		) -> DispatchResultWithPostInfo {
2897			T::AdminOrigin::ensure_origin(origin)?;
2898
2899			// Ignore controllers that do not exist or are already the same as stash.
2900			let filtered_batch_with_ledger: Vec<_> = controllers
2901				.iter()
2902				.filter_map(|controller| {
2903					let ledger = Self::ledger(StakingAccount::Controller(controller.clone()));
2904					ledger.ok().map_or(None, |ledger| {
2905						// If the controller `RewardDestination` is still the deprecated
2906						// `Controller` variant, skip deprecating this account.
2907						let payee_deprecated = Payee::<T>::get(&ledger.stash) == {
2908							#[allow(deprecated)]
2909							Some(RewardDestination::Controller)
2910						};
2911
2912						if ledger.stash != *controller && !payee_deprecated {
2913							Some(ledger)
2914						} else {
2915							None
2916						}
2917					})
2918				})
2919				.collect();
2920
2921			// Update unique pairs.
2922			let mut failures = 0;
2923			for ledger in filtered_batch_with_ledger {
2924				let _ = ledger.clone().set_controller_to_stash().map_err(|_| failures += 1);
2925			}
2926			Self::deposit_event(Event::<T>::ControllerBatchDeprecated { failures });
2927
2928			Ok(Some(T::WeightInfo::deprecate_controller_batch(controllers.len() as u32)).into())
2929		}
2930
2931		/// Restores the state of a ledger which is in an inconsistent state.
2932		///
2933		/// The requirements to restore a ledger are the following:
2934		/// * The stash is bonded; or
2935		/// * The stash is not bonded but it has a staking lock left behind; or
2936		/// * If the stash has an associated ledger and its state is inconsistent; or
2937		/// * If the ledger is not corrupted *but* its staking lock is out of sync.
2938		///
2939		/// The `maybe_*` input parameters will overwrite the corresponding data and metadata of the
2940		/// ledger associated with the stash. If the input parameters are not set, the ledger will
2941		/// be reset values from on-chain state.
2942		#[pallet::call_index(29)]
2943		#[pallet::weight(T::WeightInfo::restore_ledger())]
2944		pub fn restore_ledger(
2945			origin: OriginFor<T>,
2946			stash: T::AccountId,
2947			maybe_controller: Option<T::AccountId>,
2948			maybe_total: Option<BalanceOf<T>>,
2949			maybe_unlocking: Option<BoundedVec<UnlockChunk<BalanceOf<T>>, T::MaxUnlockingChunks>>,
2950		) -> DispatchResult {
2951			T::AdminOrigin::ensure_origin(origin)?;
2952
2953			// cannot restore ledger for virtual stakers.
2954			ensure!(!Self::is_virtual_staker(&stash), Error::<T>::VirtualStakerNotAllowed);
2955
2956			let current_lock = asset::staked::<T>(&stash);
2957			let stash_balance = asset::stakeable_balance::<T>(&stash);
2958
2959			let (new_controller, new_total) = match Self::inspect_bond_state(&stash) {
2960				Ok(LedgerIntegrityState::Corrupted) => {
2961					let new_controller = maybe_controller.unwrap_or(stash.clone());
2962
2963					let new_total = if let Some(total) = maybe_total {
2964						let new_total = total.min(stash_balance);
2965						// enforce hold == ledger.amount.
2966						asset::update_stake::<T>(&stash, new_total)?;
2967						new_total
2968					} else {
2969						current_lock
2970					};
2971
2972					Ok((new_controller, new_total))
2973				},
2974				Ok(LedgerIntegrityState::CorruptedKilled) => {
2975					if current_lock == Zero::zero() {
2976						// this case needs to restore both lock and ledger, so the new total needs
2977						// to be given by the called since there's no way to restore the total
2978						// on-chain.
2979						ensure!(maybe_total.is_some(), Error::<T>::CannotRestoreLedger);
2980						Ok((
2981							stash.clone(),
2982							maybe_total.expect("total exists as per the check above; qed."),
2983						))
2984					} else {
2985						Ok((stash.clone(), current_lock))
2986					}
2987				},
2988				Ok(LedgerIntegrityState::LockCorrupted) => {
2989					// ledger is not corrupted but its locks are out of sync. In this case, we need
2990					// to enforce a new ledger.total and staking lock for this stash.
2991					let new_total =
2992						maybe_total.ok_or(Error::<T>::CannotRestoreLedger)?.min(stash_balance);
2993					asset::update_stake::<T>(&stash, new_total)?;
2994
2995					Ok((stash.clone(), new_total))
2996				},
2997				Err(Error::<T>::BadState) => {
2998					// the stash and ledger do not exist but lock is lingering.
2999					asset::kill_stake::<T>(&stash)?;
3000					ensure!(
3001						Self::inspect_bond_state(&stash) == Err(Error::<T>::NotStash),
3002						Error::<T>::BadState
3003					);
3004
3005					return Ok(());
3006				},
3007				Ok(LedgerIntegrityState::Ok) | Err(_) => Err(Error::<T>::CannotRestoreLedger),
3008			}?;
3009
3010			// re-bond stash and controller tuple.
3011			Bonded::<T>::insert(&stash, &new_controller);
3012
3013			// resoter ledger state.
3014			let mut ledger = StakingLedger::<T>::new(stash.clone(), new_total);
3015			ledger.controller = Some(new_controller);
3016			ledger.unlocking = maybe_unlocking.unwrap_or_default();
3017			ledger.update()?;
3018
3019			ensure!(
3020				Self::inspect_bond_state(&stash) == Ok(LedgerIntegrityState::Ok),
3021				Error::<T>::BadState
3022			);
3023			Ok(())
3024		}
3025
3026		/// Migrates permissionlessly a stash from locks to holds.
3027		///
3028		/// This removes the old lock on the stake and creates a hold on it atomically. If all
3029		/// stake cannot be held, the best effort is made to hold as much as possible. The remaining
3030		/// stake is removed from the ledger.
3031		///
3032		/// The fee is waived if the migration is successful.
3033		#[pallet::call_index(30)]
3034		#[pallet::weight(T::WeightInfo::migrate_currency())]
3035		pub fn migrate_currency(
3036			origin: OriginFor<T>,
3037			stash: T::AccountId,
3038		) -> DispatchResultWithPostInfo {
3039			let _ = ensure_signed(origin)?;
3040			Self::do_migrate_currency(&stash)?;
3041
3042			// Refund the transaction fee if successful.
3043			Ok(Pays::No.into())
3044		}
3045
3046		/// Manually and permissionlessly applies a deferred slash for a given era.
3047		///
3048		/// Normally, slashes are automatically applied shortly after the start of the `slash_era`.
3049		/// The automatic application of slashes is handled by the pallet's internal logic, and it
3050		/// tries to apply one slash page per block of the era.
3051		/// If for some reason, one era is not enough for applying all slash pages, the remaining
3052		/// slashes need to be manually (permissionlessly) applied.
3053		///
3054		/// For a given era x, if at era x+1, slashes are still unapplied, all withdrawals get
3055		/// blocked, and these need to be manually applied by calling this function.
3056		/// This function exists as a **fallback mechanism** for this extreme situation, but we
3057		/// never expect to encounter this in normal scenarios.
3058		///
3059		/// The parameters for this call can be queried by looking at the `UnappliedSlashes` storage
3060		/// for eras older than the active era.
3061		///
3062		/// ## Parameters
3063		/// - `slash_era`: The application era (`offence_era + SlashDeferDuration`), i.e. the key
3064		///   into [`UnappliedSlashes`].
3065		/// - `slash_key`: A unique identifier for the slash, represented as a tuple:
3066		///   - `stash`: The stash account of the validator being slashed.
3067		///   - `slash_fraction`: The fraction of the stake that was slashed.
3068		///   - `page_index`: The index of the exposure page being processed.
3069		///
3070		/// ## Behavior
3071		/// - The function is **permissionless**—anyone can call it.
3072		/// - The `slash_era` **must be the current era or a past era**.
3073		/// If it is in the future, the
3074		///   call fails with `EraNotStarted`.
3075		/// - The fee is waived if the slash is successfully applied.
3076		///
3077		/// ## Future Improvement
3078		/// - Implement an **off-chain worker (OCW) task** to automatically apply slashes when there
3079		///   is unused block space, improving efficiency.
3080		#[pallet::call_index(31)]
3081		#[pallet::weight(T::WeightInfo::apply_slash(T::MaxExposurePageSize::get()))]
3082		pub fn apply_slash(
3083			origin: OriginFor<T>,
3084			slash_era: EraIndex,
3085			slash_key: (T::AccountId, Perbill, u32),
3086		) -> DispatchResultWithPostInfo {
3087			let _ = ensure_signed(origin)?;
3088			let active_era = ActiveEra::<T>::get().map(|a| a.index).unwrap_or_default();
3089			ensure!(slash_era <= active_era, Error::<T>::EraNotStarted);
3090
3091			// Check if this slash has been cancelled
3092			ensure!(
3093				!Self::check_slash_cancelled(slash_era, &slash_key.0, slash_key.1),
3094				Error::<T>::CancelledSlash
3095			);
3096
3097			let unapplied_slash = UnappliedSlashes::<T>::take(&slash_era, &slash_key)
3098				.ok_or(Error::<T>::InvalidSlashRecord)?;
3099			slashing::apply_slash::<T>(unapplied_slash, Self::offence_era_of(slash_era));
3100
3101			Ok(Pays::No.into())
3102		}
3103
3104		/// Perform one step of era pruning to prevent PoV size exhaustion from unbounded deletions.
3105		///
3106		/// This extrinsic enables permissionless lazy pruning of era data by performing
3107		/// incremental deletion of storage items. Each call processes a limited number
3108		/// of items based on available block weight to avoid exceeding block limits.
3109		///
3110		/// Returns `Pays::No` when work is performed to incentivize regular maintenance.
3111		/// Anyone can call this to help maintain the chain's storage health.
3112		///
3113		/// The era must be eligible for pruning (older than HistoryDepth + 1).
3114		/// Check `EraPruningState` storage to see if an era needs pruning before calling.
3115		#[pallet::call_index(32)]
3116		// NOTE: as pre-dispatch weight, use the maximum of all possible pruning step weights
3117		#[pallet::weight({
3118			let v = T::MaxValidatorSet::get();
3119			T::WeightInfo::prune_era_stakers_paged(v)
3120				.max(T::WeightInfo::prune_era_stakers_overview(v))
3121				.max(T::WeightInfo::prune_era_validator_prefs(v))
3122				.max(T::WeightInfo::prune_era_claimed_rewards(v))
3123				.max(T::WeightInfo::prune_era_validator_reward())
3124				.max(T::WeightInfo::prune_era_reward_points())
3125				.max(T::WeightInfo::prune_era_single_entry_cleanups())
3126				.max(T::WeightInfo::prune_era_validator_slash_in_era(v))
3127				.max(T::WeightInfo::prune_era_validator_incentive_weight(v))
3128		})]
3129		pub fn prune_era_step(origin: OriginFor<T>, era: EraIndex) -> DispatchResultWithPostInfo {
3130			let _ = ensure_signed(origin)?;
3131
3132			// Verify era is eligible for pruning: era <= active_era - history_depth - 1
3133			let active_era = crate::session_rotation::Rotator::<T>::active_era();
3134			let history_depth = T::HistoryDepth::get();
3135			let earliest_prunable_era = active_era.saturating_sub(history_depth).saturating_sub(1);
3136			ensure!(era <= earliest_prunable_era, Error::<T>::EraNotPrunable);
3137
3138			let actual_weight = Self::do_prune_era_step(era)?;
3139
3140			Ok(frame_support::dispatch::PostDispatchInfo {
3141				actual_weight: Some(actual_weight),
3142				pays_fee: frame_support::dispatch::Pays::No,
3143			})
3144		}
3145
3146		/// Sets the maximum commission that validators can set.
3147		///
3148		/// The dispatch origin must be `T::AdminOrigin`.
3149		#[pallet::call_index(33)]
3150		#[pallet::weight(T::WeightInfo::set_max_commission())]
3151		pub fn set_max_commission(origin: OriginFor<T>, new: Perbill) -> DispatchResult {
3152			T::AdminOrigin::ensure_origin(origin)?;
3153			ensure!(new >= MinCommission::<T>::get(), Error::<T>::CommissionTooLow);
3154			MaxCommission::<T>::put(new);
3155			Ok(())
3156		}
3157
3158		/// Configure the validator self-stake incentive parameters.
3159		///
3160		/// The dispatch origin must be `T::AdminOrigin`.
3161		///
3162		/// Changes take effect in the next era when rewards are calculated.
3163		#[pallet::call_index(34)]
3164		#[pallet::weight(T::WeightInfo::set_validator_self_stake_incentive_config())]
3165		pub fn set_validator_self_stake_incentive_config(
3166			origin: OriginFor<T>,
3167			optimum_self_stake: ConfigOp<BalanceOf<T>>,
3168			hard_cap_self_stake: ConfigOp<BalanceOf<T>>,
3169			self_stake_slope_factor: ConfigOp<Perbill>,
3170		) -> DispatchResult {
3171			T::AdminOrigin::ensure_origin(origin)?;
3172
3173			let new_optimum = match optimum_self_stake {
3174				ConfigOp::Noop => OptimumSelfStake::<T>::get(),
3175				ConfigOp::Set(v) => v,
3176				ConfigOp::Remove => BalanceOf::<T>::zero(),
3177			};
3178
3179			let new_cap = match hard_cap_self_stake {
3180				ConfigOp::Noop => HardCapSelfStake::<T>::get(),
3181				ConfigOp::Set(v) => v,
3182				ConfigOp::Remove => BalanceOf::<T>::zero(),
3183			};
3184
3185			ensure!(new_optimum <= new_cap, Error::<T>::OptimumGreaterThanCap);
3186
3187			let has_changes = !matches!(
3188				(&optimum_self_stake, &hard_cap_self_stake, &self_stake_slope_factor),
3189				(ConfigOp::Noop, ConfigOp::Noop, ConfigOp::Noop)
3190			);
3191
3192			macro_rules! config_op_exp {
3193				($storage:ty, $op:ident) => {
3194					match $op {
3195						ConfigOp::Noop => (),
3196						ConfigOp::Set(v) => <$storage>::put(v),
3197						ConfigOp::Remove => <$storage>::kill(),
3198					}
3199				};
3200			}
3201
3202			config_op_exp!(OptimumSelfStake<T>, optimum_self_stake);
3203			config_op_exp!(HardCapSelfStake<T>, hard_cap_self_stake);
3204			config_op_exp!(SelfStakeSlopeFactor<T>, self_stake_slope_factor);
3205
3206			if has_changes {
3207				Self::deposit_event(Event::<T>::ValidatorIncentiveConfigSet {
3208					optimum_self_stake: OptimumSelfStake::<T>::get(),
3209					hard_cap_self_stake: HardCapSelfStake::<T>::get(),
3210					slope_factor: SelfStakeSlopeFactor::<T>::get(),
3211				});
3212			}
3213
3214			Ok(())
3215		}
3216
3217		/// Chill an inactive validator.
3218		///
3219		/// This extrinsic can be called by anyone, given that the valid inactivity proof is
3220		/// provided. Inactivity proof is a vector of eras where the given validator was inactive.
3221		///
3222		/// Requirements for the inactivity proof:
3223		/// - The length must be equal to [`ChillInactiveThreshold`].
3224		/// - It must be sorted in ascending order.
3225		/// - It must not contain duplicate entries.
3226		/// - For every era the `stash` account must be exposed.
3227		/// - Every item must pass the check provided by [`Config::IsValidatorInactive`].
3228		/// - Every era must be less than the active era.
3229		///
3230		/// On a successfull execution, caller doesn't pay fees.
3231		#[pallet::call_index(35)]
3232		#[pallet::weight(T::WeightInfo::chill_inactive(proof.len() as _))]
3233		pub fn chill_inactive(
3234			origin: OriginFor<T>,
3235			stash: T::AccountId,
3236			proof: BoundedVec<EraIndex, T::HistoryDepth>,
3237		) -> DispatchResultWithPostInfo {
3238			ensure_signed(origin)?;
3239
3240			let threshold = ChillInactiveThreshold::<T>::get();
3241			ensure!(
3242				proof.len() as EraIndex == threshold,
3243				Error::<T>::InvalidInactivityProof(InvalidInactivityProofError::InvalidLen)
3244			);
3245			ensure!(
3246				proof.is_sorted_by(|a, b| a < b),
3247				Error::<T>::InvalidInactivityProof(InvalidInactivityProofError::NotSorted)
3248			);
3249
3250			// All proof eras must fall in the retained window `[active_era - HistoryDepth,
3251			// active_era)`.
3252			let active_era = Rotator::<T>::active_era();
3253			let oldest_allowed_era = active_era.saturating_sub(T::HistoryDepth::get());
3254			let oldest_proof_era = proof.first().copied().unwrap_or(EraIndex::MAX);
3255			let most_recent_proof_era = proof.last().copied().unwrap_or(EraIndex::MAX);
3256			ensure!(
3257				oldest_proof_era >= oldest_allowed_era && most_recent_proof_era < active_era,
3258				Error::<T>::InvalidInactivityProof(InvalidInactivityProofError::InvalidEra)
3259			);
3260
3261			for era in proof {
3262				ensure!(
3263					Eras::<T>::was_validator_exposed(era, &stash),
3264					Error::<T>::InvalidInactivityProof(
3265						InvalidInactivityProofError::ValidatorNotExposed
3266					)
3267				);
3268
3269				let points = Eras::<T>::get_reward_points_for_validator(era, &stash);
3270
3271				ensure!(
3272					T::IsValidatorInactive::is_inactive(era, &stash, points),
3273					Error::<T>::InvalidInactivityProof(
3274						InvalidInactivityProofError::ValidatorActive
3275					)
3276				);
3277			}
3278
3279			if Self::do_remove_validator(&stash) {
3280				Self::deposit_event(Event::<T>::Chilled { stash });
3281
3282				Ok(Pays::No.into())
3283			} else {
3284				Err(Error::<T>::BadTarget.into())
3285			}
3286		}
3287	}
3288
3289	#[pallet::view_functions]
3290	impl<T: Config> Pallet<T> {
3291		/// Resolve the account ID for a given reward pot.
3292		pub fn pot_account(pot: crate::RewardPot) -> T::AccountId {
3293			<T::RewardPots as crate::PotAccountProvider<T::AccountId>>::pot_account(pot)
3294		}
3295
3296		/// Current balance held in a given reward pot.
3297		pub fn pot_balance(pot: crate::RewardPot) -> BalanceOf<T> {
3298			let account =
3299				<T::RewardPots as crate::PotAccountProvider<T::AccountId>>::pot_account(pot);
3300			<T::Currency as frame_support::traits::fungible::Inspect<T::AccountId>>::balance(
3301				&account,
3302			)
3303		}
3304
3305		/// Per-era reward allocation (staker rewards + validator incentive budget).
3306		///
3307		/// Both fields are zero for eras created in legacy minting mode.
3308		pub fn era_reward_allocation(
3309			era: EraIndex,
3310		) -> crate::reward::EraRewardAllocation<BalanceOf<T>> {
3311			crate::reward::EraRewardAllocation {
3312				staker_rewards: ErasValidatorReward::<T>::get(era).unwrap_or_else(Zero::zero),
3313				validator_incentive: ErasValidatorIncentiveBudget::<T>::get(era),
3314			}
3315		}
3316	}
3317}