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