pezpallet_staking/pezpallet/mod.rs
1// This file is part of Bizinikiwi.
2
3// Copyright (C) Parity Technologies (UK) Ltd. and Dijital Kurdistan Tech Institute
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//! Staking FRAME Pezpallet.
19
20use alloc::vec::Vec;
21use codec::Codec;
22use pezframe_election_provider_support::{ElectionProvider, SortedListProvider, VoteWeight};
23use pezframe_support::{
24 pezpallet_prelude::*,
25 traits::{
26 fungible::{
27 hold::{Balanced as FunHoldBalanced, Mutate as FunHoldMutate},
28 Mutate as FunMutate,
29 },
30 Contains, Defensive, EnsureOrigin, EstimateNextNewSession, Get, InspectLockableCurrency,
31 Nothing, OnUnbalanced, UnixTime,
32 },
33 weights::Weight,
34 BoundedVec,
35};
36use pezframe_system::{ensure_root, ensure_signed, pezpallet_prelude::*};
37use pezsp_runtime::{
38 traits::{SaturatedConversion, StaticLookup, Zero},
39 ArithmeticError, Perbill, Percent,
40};
41
42use pezsp_staking::{
43 EraIndex, Page, SessionIndex,
44 StakingAccount::{self, Controller, Stash},
45 StakingInterface,
46};
47
48mod impls;
49
50pub use impls::*;
51
52use crate::{
53 asset, slashing, weights::WeightInfo, AccountIdLookupOf, ActiveEraInfo, BalanceOf, EraPayout,
54 EraRewardPoints, Exposure, ExposurePage, Forcing, LedgerIntegrityState, MaxNominationsOf,
55 NegativeImbalanceOf, Nominations, NominationsQuota, PositiveImbalanceOf, RewardDestination,
56 SessionInterface, StakingLedger, UnappliedSlash, UnlockChunk, ValidatorPrefs,
57};
58
59// The speculative number of spans are used as an input of the weight annotation of
60// [`Call::unbond`], as the post dispatch weight may depend on the number of slashing span on the
61// account which is not provided as an input. The value set should be conservative but sensible.
62pub(crate) const SPECULATIVE_NUM_SPANS: u32 = 32;
63
64#[pezframe_support::pezpallet]
65pub mod pezpallet {
66 use super::*;
67 use codec::HasCompact;
68 use pezframe_election_provider_support::ElectionDataProvider;
69
70 use crate::{BenchmarkingConfig, PagedExposureMetadata};
71
72 /// The in-code storage version.
73 const STORAGE_VERSION: StorageVersion = StorageVersion::new(16);
74
75 #[pezpallet::pezpallet]
76 #[pezpallet::storage_version(STORAGE_VERSION)]
77 pub struct Pezpallet<T>(_);
78
79 /// Possible operations on the configuration values of this pezpallet.
80 #[derive(TypeInfo, Debug, Clone, Encode, Decode, DecodeWithMemTracking, PartialEq)]
81 pub enum ConfigOp<T: Default + Codec> {
82 /// Don't change.
83 Noop,
84 /// Set the given value.
85 Set(T),
86 /// Remove from storage.
87 Remove,
88 }
89
90 #[pezpallet::config(with_default)]
91 pub trait Config: pezframe_system::Config {
92 /// The old trait for staking balance. Deprecated and only used for migrating old ledgers.
93 #[pezpallet::no_default]
94 type OldCurrency: InspectLockableCurrency<
95 Self::AccountId,
96 Moment = BlockNumberFor<Self>,
97 Balance = Self::CurrencyBalance,
98 >;
99
100 /// The staking balance.
101 #[pezpallet::no_default]
102 type Currency: FunHoldMutate<
103 Self::AccountId,
104 Reason = Self::RuntimeHoldReason,
105 Balance = Self::CurrencyBalance,
106 > + FunMutate<Self::AccountId, Balance = Self::CurrencyBalance>
107 + FunHoldBalanced<Self::AccountId, Balance = Self::CurrencyBalance>;
108
109 /// Overarching hold reason.
110 #[pezpallet::no_default_bounds]
111 type RuntimeHoldReason: From<HoldReason>;
112
113 /// Just the `Currency::Balance` type; we have this item to allow us to constrain it to
114 /// `From<u64>`.
115 type CurrencyBalance: pezsp_runtime::traits::AtLeast32BitUnsigned
116 + codec::FullCodec
117 + DecodeWithMemTracking
118 + HasCompact<Type: DecodeWithMemTracking>
119 + Copy
120 + MaybeSerializeDeserialize
121 + core::fmt::Debug
122 + Default
123 + From<u64>
124 + TypeInfo
125 + Send
126 + Sync
127 + MaxEncodedLen;
128 /// Time used for computing era duration.
129 ///
130 /// It is guaranteed to start being called from the first `on_finalize`. Thus value at
131 /// genesis is not used.
132 #[pezpallet::no_default]
133 type UnixTime: UnixTime;
134
135 /// Convert a balance into a number used for election calculation. This must fit into a
136 /// `u64` but is allowed to be sensibly lossy. The `u64` is used to communicate with the
137 /// [`pezframe_election_provider_support`] crate which accepts u64 numbers and does
138 /// operations in 128.
139 /// Consequently, the backward convert is used convert the u128s from sp-elections back to a
140 /// [`BalanceOf`].
141 #[pezpallet::no_default_bounds]
142 type CurrencyToVote: pezsp_staking::currency_to_vote::CurrencyToVote<BalanceOf<Self>>;
143
144 /// Something that provides the election functionality.
145 #[pezpallet::no_default]
146 type ElectionProvider: ElectionProvider<
147 AccountId = Self::AccountId,
148 BlockNumber = BlockNumberFor<Self>,
149 // we only accept an election provider that has staking as data provider.
150 DataProvider = Pezpallet<Self>,
151 >;
152 /// Something that provides the election functionality at genesis.
153 #[pezpallet::no_default]
154 type GenesisElectionProvider: ElectionProvider<
155 AccountId = Self::AccountId,
156 BlockNumber = BlockNumberFor<Self>,
157 DataProvider = Pezpallet<Self>,
158 >;
159
160 /// Something that defines the maximum number of nominations per nominator.
161 #[pezpallet::no_default_bounds]
162 type NominationsQuota: NominationsQuota<BalanceOf<Self>>;
163
164 /// Number of eras to keep in history.
165 ///
166 /// Following information is kept for eras in `[current_era -
167 /// HistoryDepth, current_era]`: `ErasStakers`, `ErasStakersClipped`,
168 /// `ErasValidatorPrefs`, `ErasValidatorReward`, `ErasRewardPoints`,
169 /// `ErasTotalStake`, `ErasStartSessionIndex`, `ClaimedRewards`, `ErasStakersPaged`,
170 /// `ErasStakersOverview`.
171 ///
172 /// Must be more than the number of eras delayed by session.
173 /// I.e. active era must always be in history. I.e. `active_era >
174 /// current_era - history_depth` must be guaranteed.
175 ///
176 /// If migrating an existing pezpallet from storage value to config value,
177 /// this should be set to same value or greater as in storage.
178 ///
179 /// Note: `HistoryDepth` is used as the upper bound for the `BoundedVec`
180 /// item `StakingLedger.legacy_claimed_rewards`. Setting this value lower than
181 /// the existing value can lead to inconsistencies in the
182 /// `StakingLedger` and will need to be handled properly in a migration.
183 /// The test `reducing_history_depth_abrupt` shows this effect.
184 #[pezpallet::constant]
185 type HistoryDepth: Get<u32>;
186
187 /// Tokens have been minted and are unused for validator-reward.
188 /// See [Era payout](./index.html#era-payout).
189 #[pezpallet::no_default_bounds]
190 type RewardRemainder: OnUnbalanced<NegativeImbalanceOf<Self>>;
191
192 /// The overarching event type.
193 #[pezpallet::no_default_bounds]
194 #[allow(deprecated)]
195 type RuntimeEvent: From<Event<Self>>
196 + IsType<<Self as pezframe_system::Config>::RuntimeEvent>;
197
198 /// Handler for the unbalanced reduction when slashing a staker.
199 #[pezpallet::no_default_bounds]
200 type Slash: OnUnbalanced<NegativeImbalanceOf<Self>>;
201
202 /// Handler for the unbalanced increment when rewarding a staker.
203 /// NOTE: in most cases, the implementation of `OnUnbalanced` should modify the total
204 /// issuance.
205 #[pezpallet::no_default_bounds]
206 type Reward: OnUnbalanced<PositiveImbalanceOf<Self>>;
207
208 /// Number of sessions per era.
209 #[pezpallet::constant]
210 type SessionsPerEra: Get<SessionIndex>;
211
212 /// Number of eras that staked funds must remain bonded for.
213 #[pezpallet::constant]
214 type BondingDuration: Get<EraIndex>;
215
216 /// Number of eras that slashes are deferred by, after computation.
217 ///
218 /// This should be less than the bonding duration. Set to 0 if slashes
219 /// should be applied immediately, without opportunity for intervention.
220 #[pezpallet::constant]
221 type SlashDeferDuration: Get<EraIndex>;
222
223 /// The origin which can manage less critical staking parameters that does not require root.
224 ///
225 /// Supported actions: (1) cancel deferred slash, (2) set minimum commission.
226 #[pezpallet::no_default]
227 type AdminOrigin: EnsureOrigin<Self::RuntimeOrigin>;
228
229 /// Interface for interacting with a session pezpallet.
230 type SessionInterface: SessionInterface<Self::AccountId>;
231
232 /// The payout for validators and the system for the current era.
233 /// See [Era payout](./index.html#era-payout).
234 #[pezpallet::no_default]
235 type EraPayout: EraPayout<BalanceOf<Self>>;
236
237 /// Something that can estimate the next session change, accurately or as a best effort
238 /// guess.
239 #[pezpallet::no_default_bounds]
240 type NextNewSession: EstimateNextNewSession<BlockNumberFor<Self>>;
241
242 /// The maximum size of each `T::ExposurePage`.
243 ///
244 /// An `ExposurePage` is weakly bounded to a maximum of `MaxExposurePageSize`
245 /// nominators.
246 ///
247 /// For older non-paged exposure, a reward payout was restricted to the top
248 /// `MaxExposurePageSize` nominators. This is to limit the i/o cost for the
249 /// nominator payout.
250 ///
251 /// Note: `MaxExposurePageSize` is used to bound `ClaimedRewards` and is unsafe to reduce
252 /// without handling it in a migration.
253 #[pezpallet::constant]
254 type MaxExposurePageSize: Get<u32>;
255
256 /// The absolute maximum of winner validators this pezpallet should return.
257 #[pezpallet::constant]
258 type MaxValidatorSet: Get<u32>;
259
260 /// Something that provides a best-effort sorted list of voters aka electing nominators,
261 /// used for NPoS election.
262 ///
263 /// The changes to nominators are reported to this. Moreover, each validator's self-vote is
264 /// also reported as one independent vote.
265 ///
266 /// To keep the load off the chain as much as possible, changes made to the staked amount
267 /// via rewards and slashes are not reported and thus need to be manually fixed by the
268 /// staker. In case of `bags-list`, this always means using `rebag` and `putInFrontOf`.
269 ///
270 /// Invariant: what comes out of this list will always be a nominator.
271 #[pezpallet::no_default]
272 type VoterList: SortedListProvider<Self::AccountId, Score = VoteWeight>;
273
274 /// WIP: This is a noop as of now, the actual business logic that's described below is going
275 /// to be introduced in a follow-up PR.
276 ///
277 /// Something that provides a best-effort sorted list of targets aka electable validators,
278 /// used for NPoS election.
279 ///
280 /// The changes to the approval stake of each validator are reported to this. This means any
281 /// change to:
282 /// 1. The stake of any validator or nominator.
283 /// 2. The targets of any nominator
284 /// 3. The role of any staker (e.g. validator -> chilled, nominator -> validator, etc)
285 ///
286 /// Unlike `VoterList`, the values in this list are always kept up to date with reward and
287 /// slash as well, and thus represent the accurate approval stake of all account being
288 /// nominated by nominators.
289 ///
290 /// Note that while at the time of nomination, all targets are checked to be real
291 /// validators, they can chill at any point, and their approval stakes will still be
292 /// recorded. This implies that what comes out of iterating this list MIGHT NOT BE AN ACTIVE
293 /// VALIDATOR.
294 #[pezpallet::no_default]
295 type TargetList: SortedListProvider<Self::AccountId, Score = BalanceOf<Self>>;
296
297 /// The maximum number of `unlocking` chunks a [`StakingLedger`] can
298 /// have. Effectively determines how many unique eras a staker may be
299 /// unbonding in.
300 ///
301 /// Note: `MaxUnlockingChunks` is used as the upper bound for the
302 /// `BoundedVec` item `StakingLedger.unlocking`. Setting this value
303 /// lower than the existing value can lead to inconsistencies in the
304 /// `StakingLedger` and will need to be handled properly in a runtime
305 /// migration. The test `reducing_max_unlocking_chunks_abrupt` shows
306 /// this effect.
307 #[pezpallet::constant]
308 type MaxUnlockingChunks: Get<u32>;
309
310 /// The maximum amount of controller accounts that can be deprecated in one call.
311 type MaxControllersInDeprecationBatch: Get<u32>;
312
313 /// Something that listens to staking updates and performs actions based on the data it
314 /// receives.
315 ///
316 /// WARNING: this only reports slashing and withdraw events for the time being.
317 #[pezpallet::no_default_bounds]
318 type EventListeners: pezsp_staking::OnStakingUpdate<Self::AccountId, BalanceOf<Self>>;
319
320 #[pezpallet::no_default_bounds]
321 /// Filter some accounts from participating in staking.
322 ///
323 /// This is useful for example to blacklist an account that is participating in staking in
324 /// another way (such as pools).
325 type Filter: Contains<Self::AccountId>;
326
327 /// Some parameters of the benchmarking.
328 #[cfg(feature = "std")]
329 type BenchmarkingConfig: BenchmarkingConfig;
330
331 #[cfg(not(feature = "std"))]
332 #[pezpallet::no_default]
333 type BenchmarkingConfig: BenchmarkingConfig;
334
335 /// Weight information for extrinsics in this pezpallet.
336 type WeightInfo: WeightInfo;
337 }
338
339 /// A reason for placing a hold on funds.
340 #[pezpallet::composite_enum]
341 pub enum HoldReason {
342 /// Funds on stake by a nominator or a validator.
343 #[codec(index = 0)]
344 Staking,
345 }
346
347 /// Default implementations of [`DefaultConfig`], which can be used to implement [`Config`].
348 pub mod config_preludes {
349 use super::*;
350 use pezframe_support::{derive_impl, parameter_types, traits::ConstU32};
351 pub struct TestDefaultConfig;
352
353 #[derive_impl(pezframe_system::config_preludes::TestDefaultConfig, no_aggregated_types)]
354 impl pezframe_system::DefaultConfig for TestDefaultConfig {}
355
356 parameter_types! {
357 pub const SessionsPerEra: SessionIndex = 3;
358 pub const BondingDuration: EraIndex = 3;
359 }
360
361 #[pezframe_support::register_default_impl(TestDefaultConfig)]
362 impl DefaultConfig for TestDefaultConfig {
363 #[inject_runtime_type]
364 type RuntimeEvent = ();
365 #[inject_runtime_type]
366 type RuntimeHoldReason = ();
367 type CurrencyBalance = u128;
368 type CurrencyToVote = ();
369 type NominationsQuota = crate::FixedNominationsQuota<16>;
370 type HistoryDepth = ConstU32<84>;
371 type RewardRemainder = ();
372 type Slash = ();
373 type Reward = ();
374 type SessionsPerEra = SessionsPerEra;
375 type BondingDuration = BondingDuration;
376 type SlashDeferDuration = ();
377 type SessionInterface = ();
378 type NextNewSession = ();
379 type MaxExposurePageSize = ConstU32<64>;
380 type MaxUnlockingChunks = ConstU32<32>;
381 type MaxValidatorSet = ConstU32<100>;
382 type MaxControllersInDeprecationBatch = ConstU32<100>;
383 type EventListeners = ();
384 type Filter = Nothing;
385 #[cfg(feature = "std")]
386 type BenchmarkingConfig = crate::TestBenchmarkingConfig;
387 type WeightInfo = ();
388 }
389 }
390
391 /// The ideal number of active validators.
392 #[pezpallet::storage]
393 pub type ValidatorCount<T> = StorageValue<_, u32, ValueQuery>;
394
395 /// Minimum number of staking participants before emergency conditions are imposed.
396 #[pezpallet::storage]
397 pub type MinimumValidatorCount<T> = StorageValue<_, u32, ValueQuery>;
398
399 /// Any validators that may never be slashed or forcibly kicked. It's a Vec since they're
400 /// easy to initialize and the performance hit is minimal (we expect no more than four
401 /// invulnerables) and restricted to testnets.
402 #[pezpallet::storage]
403 #[pezpallet::unbounded]
404 pub type Invulnerables<T: Config> = StorageValue<_, Vec<T::AccountId>, ValueQuery>;
405
406 /// Map from all locked "stash" accounts to the controller account.
407 ///
408 /// TWOX-NOTE: SAFE since `AccountId` is a secure hash.
409 #[pezpallet::storage]
410 pub type Bonded<T: Config> = StorageMap<_, Twox64Concat, T::AccountId, T::AccountId>;
411
412 /// The minimum active bond to become and maintain the role of a nominator.
413 #[pezpallet::storage]
414 pub type MinNominatorBond<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;
415
416 /// The minimum active bond to become and maintain the role of a validator.
417 #[pezpallet::storage]
418 pub type MinValidatorBond<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;
419
420 /// The minimum active nominator stake of the last successful election.
421 #[pezpallet::storage]
422 pub type MinimumActiveStake<T> = StorageValue<_, BalanceOf<T>, ValueQuery>;
423
424 /// The minimum amount of commission that validators can set.
425 ///
426 /// If set to `0`, no limit exists.
427 #[pezpallet::storage]
428 pub type MinCommission<T: Config> = StorageValue<_, Perbill, ValueQuery>;
429
430 /// Map from all (unlocked) "controller" accounts to the info regarding the staking.
431 ///
432 /// Note: All the reads and mutations to this storage *MUST* be done through the methods exposed
433 /// by [`StakingLedger`] to ensure data and lock consistency.
434 #[pezpallet::storage]
435 pub type Ledger<T: Config> = StorageMap<_, Blake2_128Concat, T::AccountId, StakingLedger<T>>;
436
437 /// Where the reward payment should be made. Keyed by stash.
438 ///
439 /// TWOX-NOTE: SAFE since `AccountId` is a secure hash.
440 #[pezpallet::storage]
441 pub type Payee<T: Config> =
442 StorageMap<_, Twox64Concat, T::AccountId, RewardDestination<T::AccountId>, OptionQuery>;
443
444 /// The map from (wannabe) validator stash key to the preferences of that validator.
445 ///
446 /// TWOX-NOTE: SAFE since `AccountId` is a secure hash.
447 #[pezpallet::storage]
448 pub type Validators<T: Config> =
449 CountedStorageMap<_, Twox64Concat, T::AccountId, ValidatorPrefs, ValueQuery>;
450
451 /// The maximum validator count before we stop allowing new validators to join.
452 ///
453 /// When this value is not set, no limits are enforced.
454 #[pezpallet::storage]
455 pub type MaxValidatorsCount<T> = StorageValue<_, u32, OptionQuery>;
456
457 /// The map from nominator stash key to their nomination preferences, namely the validators that
458 /// they wish to support.
459 ///
460 /// Note that the keys of this storage map might become non-decodable in case the
461 /// account's [`NominationsQuota::MaxNominations`] configuration is decreased.
462 /// In this rare case, these nominators
463 /// are still existent in storage, their key is correct and retrievable (i.e. `contains_key`
464 /// indicates that they exist), but their value cannot be decoded. Therefore, the non-decodable
465 /// nominators will effectively not-exist, until they re-submit their preferences such that it
466 /// is within the bounds of the newly set `Config::MaxNominations`.
467 ///
468 /// This implies that `::iter_keys().count()` and `::iter().count()` might return different
469 /// values for this map. Moreover, the main `::count()` is aligned with the former, namely the
470 /// number of keys that exist.
471 ///
472 /// Lastly, if any of the nominators become non-decodable, they can be chilled immediately via
473 /// [`Call::chill_other`] dispatchable by anyone.
474 ///
475 /// TWOX-NOTE: SAFE since `AccountId` is a secure hash.
476 #[pezpallet::storage]
477 pub type Nominators<T: Config> =
478 CountedStorageMap<_, Twox64Concat, T::AccountId, Nominations<T>>;
479
480 /// Stakers whose funds are managed by other pallets.
481 ///
482 /// This pezpallet does not apply any locks on them, therefore they are only virtually bonded.
483 /// They are expected to be keyless accounts and hence should not be allowed to mutate their
484 /// ledger directly via this pezpallet. Instead, these accounts are managed by other pallets
485 /// and accessed via low level apis. We keep track of them to do minimal integrity checks.
486 #[pezpallet::storage]
487 pub type VirtualStakers<T: Config> = CountedStorageMap<_, Twox64Concat, T::AccountId, ()>;
488
489 /// The maximum nominator count before we stop allowing new validators to join.
490 ///
491 /// When this value is not set, no limits are enforced.
492 #[pezpallet::storage]
493 pub type MaxNominatorsCount<T> = StorageValue<_, u32, OptionQuery>;
494
495 /// The current era index.
496 ///
497 /// This is the latest planned era, depending on how the Session pezpallet queues the validator
498 /// set, it might be active or not.
499 #[pezpallet::storage]
500 pub type CurrentEra<T> = StorageValue<_, EraIndex>;
501
502 /// The active era information, it holds index and start.
503 ///
504 /// The active era is the era being currently rewarded. Validator set of this era must be
505 /// equal to [`SessionInterface::validators`].
506 #[pezpallet::storage]
507 pub type ActiveEra<T> = StorageValue<_, ActiveEraInfo>;
508
509 /// The session index at which the era start for the last [`Config::HistoryDepth`] eras.
510 ///
511 /// Note: This tracks the starting session (i.e. session index when era start being active)
512 /// for the eras in `[CurrentEra - HISTORY_DEPTH, CurrentEra]`.
513 #[pezpallet::storage]
514 pub type ErasStartSessionIndex<T> = StorageMap<_, Twox64Concat, EraIndex, SessionIndex>;
515
516 /// Exposure of validator at era.
517 ///
518 /// This is keyed first by the era index to allow bulk deletion and then the stash account.
519 ///
520 /// Is it removed after [`Config::HistoryDepth`] eras.
521 /// If stakers hasn't been set or has been removed then empty exposure is returned.
522 ///
523 /// Note: Deprecated since v14. Use `EraInfo` instead to work with exposures.
524 #[pezpallet::storage]
525 #[pezpallet::unbounded]
526 pub type ErasStakers<T: Config> = StorageDoubleMap<
527 _,
528 Twox64Concat,
529 EraIndex,
530 Twox64Concat,
531 T::AccountId,
532 Exposure<T::AccountId, BalanceOf<T>>,
533 ValueQuery,
534 >;
535
536 /// Summary of validator exposure at a given era.
537 ///
538 /// This contains the total stake in support of the validator and their own stake. In addition,
539 /// it can also be used to get the number of nominators backing this validator and the number of
540 /// exposure pages they are divided into. The page count is useful to determine the number of
541 /// pages of rewards that needs to be claimed.
542 ///
543 /// This is keyed first by the era index to allow bulk deletion and then the stash account.
544 /// Should only be accessed through `EraInfo`.
545 ///
546 /// Is it removed after [`Config::HistoryDepth`] eras.
547 /// If stakers hasn't been set or has been removed then empty overview is returned.
548 #[pezpallet::storage]
549 pub type ErasStakersOverview<T: Config> = StorageDoubleMap<
550 _,
551 Twox64Concat,
552 EraIndex,
553 Twox64Concat,
554 T::AccountId,
555 PagedExposureMetadata<BalanceOf<T>>,
556 OptionQuery,
557 >;
558
559 /// Clipped Exposure of validator at era.
560 ///
561 /// Note: This is deprecated, should be used as read-only and will be removed in the future.
562 /// New `Exposure`s are stored in a paged manner in `ErasStakersPaged` instead.
563 ///
564 /// This is similar to [`ErasStakers`] but number of nominators exposed is reduced to the
565 /// `T::MaxExposurePageSize` biggest stakers.
566 /// (Note: the field `total` and `own` of the exposure remains unchanged).
567 /// This is used to limit the i/o cost for the nominator payout.
568 ///
569 /// This is keyed fist by the era index to allow bulk deletion and then the stash account.
570 ///
571 /// It is removed after [`Config::HistoryDepth`] eras.
572 /// If stakers hasn't been set or has been removed then empty exposure is returned.
573 ///
574 /// Note: Deprecated since v14. Use `EraInfo` instead to work with exposures.
575 #[pezpallet::storage]
576 #[pezpallet::unbounded]
577 pub type ErasStakersClipped<T: Config> = StorageDoubleMap<
578 _,
579 Twox64Concat,
580 EraIndex,
581 Twox64Concat,
582 T::AccountId,
583 Exposure<T::AccountId, BalanceOf<T>>,
584 ValueQuery,
585 >;
586
587 /// Paginated exposure of a validator at given era.
588 ///
589 /// This is keyed first by the era index to allow bulk deletion, then stash account and finally
590 /// the page. Should only be accessed through `EraInfo`.
591 ///
592 /// This is cleared after [`Config::HistoryDepth`] eras.
593 #[pezpallet::storage]
594 #[pezpallet::unbounded]
595 pub type ErasStakersPaged<T: Config> = StorageNMap<
596 _,
597 (
598 NMapKey<Twox64Concat, EraIndex>,
599 NMapKey<Twox64Concat, T::AccountId>,
600 NMapKey<Twox64Concat, Page>,
601 ),
602 ExposurePage<T::AccountId, BalanceOf<T>>,
603 OptionQuery,
604 >;
605
606 /// History of claimed paged rewards by era and validator.
607 ///
608 /// This is keyed by era and validator stash which maps to the set of page indexes which have
609 /// been claimed.
610 ///
611 /// It is removed after [`Config::HistoryDepth`] eras.
612 #[pezpallet::storage]
613 #[pezpallet::unbounded]
614 pub type ClaimedRewards<T: Config> = StorageDoubleMap<
615 _,
616 Twox64Concat,
617 EraIndex,
618 Twox64Concat,
619 T::AccountId,
620 Vec<Page>,
621 ValueQuery,
622 >;
623
624 /// Similar to `ErasStakers`, this holds the preferences of validators.
625 ///
626 /// This is keyed first by the era index to allow bulk deletion and then the stash account.
627 ///
628 /// Is it removed after [`Config::HistoryDepth`] eras.
629 // If prefs hasn't been set or has been removed then 0 commission is returned.
630 #[pezpallet::storage]
631 pub type ErasValidatorPrefs<T: Config> = StorageDoubleMap<
632 _,
633 Twox64Concat,
634 EraIndex,
635 Twox64Concat,
636 T::AccountId,
637 ValidatorPrefs,
638 ValueQuery,
639 >;
640
641 /// The total validator era payout for the last [`Config::HistoryDepth`] eras.
642 ///
643 /// Eras that haven't finished yet or has been removed doesn't have reward.
644 #[pezpallet::storage]
645 pub type ErasValidatorReward<T: Config> = StorageMap<_, Twox64Concat, EraIndex, BalanceOf<T>>;
646
647 /// Rewards for the last [`Config::HistoryDepth`] eras.
648 /// If reward hasn't been set or has been removed then 0 reward is returned.
649 #[pezpallet::storage]
650 #[pezpallet::unbounded]
651 pub type ErasRewardPoints<T: Config> =
652 StorageMap<_, Twox64Concat, EraIndex, EraRewardPoints<T::AccountId>, ValueQuery>;
653
654 /// The total amount staked for the last [`Config::HistoryDepth`] eras.
655 /// If total hasn't been set or has been removed then 0 stake is returned.
656 #[pezpallet::storage]
657 pub type ErasTotalStake<T: Config> =
658 StorageMap<_, Twox64Concat, EraIndex, BalanceOf<T>, ValueQuery>;
659
660 /// Mode of era forcing.
661 #[pezpallet::storage]
662 pub type ForceEra<T> = StorageValue<_, Forcing, ValueQuery>;
663
664 /// Maximum staked rewards, i.e. the percentage of the era inflation that
665 /// is used for stake rewards.
666 /// See [Era payout](./index.html#era-payout).
667 #[pezpallet::storage]
668 pub type MaxStakedRewards<T> = StorageValue<_, Percent, OptionQuery>;
669
670 /// The percentage of the slash that is distributed to reporters.
671 ///
672 /// The rest of the slashed value is handled by the `Slash`.
673 #[pezpallet::storage]
674 pub type SlashRewardFraction<T> = StorageValue<_, Perbill, ValueQuery>;
675
676 /// The amount of currency given to reporters of a slash event which was
677 /// canceled by extraordinary circumstances (e.g. governance).
678 #[pezpallet::storage]
679 pub type CanceledSlashPayout<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;
680
681 /// All unapplied slashes that are queued for later.
682 #[pezpallet::storage]
683 #[pezpallet::unbounded]
684 pub type UnappliedSlashes<T: Config> = StorageMap<
685 _,
686 Twox64Concat,
687 EraIndex,
688 Vec<UnappliedSlash<T::AccountId, BalanceOf<T>>>,
689 ValueQuery,
690 >;
691
692 /// A mapping from still-bonded eras to the first session index of that era.
693 ///
694 /// Must contains information for eras for the range:
695 /// `[active_era - bounding_duration; active_era]`
696 #[pezpallet::storage]
697 #[pezpallet::unbounded]
698 pub type BondedEras<T: Config> = StorageValue<_, Vec<(EraIndex, SessionIndex)>, ValueQuery>;
699
700 /// All slashing events on validators, mapped by era to the highest slash proportion
701 /// and slash value of the era.
702 #[pezpallet::storage]
703 pub type ValidatorSlashInEra<T: Config> = StorageDoubleMap<
704 _,
705 Twox64Concat,
706 EraIndex,
707 Twox64Concat,
708 T::AccountId,
709 (Perbill, BalanceOf<T>),
710 >;
711
712 /// All slashing events on nominators, mapped by era to the highest slash value of the era.
713 #[pezpallet::storage]
714 pub type NominatorSlashInEra<T: Config> =
715 StorageDoubleMap<_, Twox64Concat, EraIndex, Twox64Concat, T::AccountId, BalanceOf<T>>;
716
717 /// Slashing spans for stash accounts.
718 #[pezpallet::storage]
719 #[pezpallet::unbounded]
720 pub type SlashingSpans<T: Config> =
721 StorageMap<_, Twox64Concat, T::AccountId, slashing::SlashingSpans>;
722
723 /// Records information about the maximum slash of a stash within a slashing span,
724 /// as well as how much reward has been paid out.
725 #[pezpallet::storage]
726 pub type SpanSlash<T: Config> = StorageMap<
727 _,
728 Twox64Concat,
729 (T::AccountId, slashing::SpanIndex),
730 slashing::SpanRecord<BalanceOf<T>>,
731 ValueQuery,
732 >;
733
734 /// The last planned session scheduled by the session pezpallet.
735 ///
736 /// This is basically in sync with the call to
737 /// [`pezpallet_session::SessionManager::new_session`].
738 #[pezpallet::storage]
739 pub type CurrentPlannedSession<T> = StorageValue<_, SessionIndex, ValueQuery>;
740
741 /// The threshold for when users can start calling `chill_other` for other validators /
742 /// nominators. The threshold is compared to the actual number of validators / nominators
743 /// (`CountFor*`) in the system compared to the configured max (`Max*Count`).
744 #[pezpallet::storage]
745 pub type ChillThreshold<T: Config> = StorageValue<_, Percent, OptionQuery>;
746
747 #[pezpallet::genesis_config]
748 #[derive(pezframe_support::DefaultNoBound)]
749 pub struct GenesisConfig<T: Config> {
750 pub validator_count: u32,
751 pub minimum_validator_count: u32,
752 pub invulnerables: Vec<T::AccountId>,
753 pub force_era: Forcing,
754 pub slash_reward_fraction: Perbill,
755 pub canceled_payout: BalanceOf<T>,
756 pub stakers:
757 Vec<(T::AccountId, T::AccountId, BalanceOf<T>, crate::StakerStatus<T::AccountId>)>,
758 pub min_nominator_bond: BalanceOf<T>,
759 pub min_validator_bond: BalanceOf<T>,
760 pub max_validator_count: Option<u32>,
761 pub max_nominator_count: Option<u32>,
762 }
763
764 #[pezpallet::genesis_build]
765 impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
766 fn build(&self) {
767 ValidatorCount::<T>::put(self.validator_count);
768 MinimumValidatorCount::<T>::put(self.minimum_validator_count);
769 Invulnerables::<T>::put(&self.invulnerables);
770 ForceEra::<T>::put(self.force_era);
771 CanceledSlashPayout::<T>::put(self.canceled_payout);
772 SlashRewardFraction::<T>::put(self.slash_reward_fraction);
773 MinNominatorBond::<T>::put(self.min_nominator_bond);
774 MinValidatorBond::<T>::put(self.min_validator_bond);
775 if let Some(x) = self.max_validator_count {
776 MaxValidatorsCount::<T>::put(x);
777 }
778 if let Some(x) = self.max_nominator_count {
779 MaxNominatorsCount::<T>::put(x);
780 }
781
782 for &(ref stash, _, balance, ref status) in &self.stakers {
783 crate::log!(
784 trace,
785 "inserting genesis staker: {:?} => {:?} => {:?}",
786 stash,
787 balance,
788 status
789 );
790 assert!(
791 asset::free_to_stake::<T>(stash) >= balance,
792 "Stash does not have enough balance to bond."
793 );
794 pezframe_support::assert_ok!(<Pezpallet<T>>::bond(
795 T::RuntimeOrigin::from(Some(stash.clone()).into()),
796 balance,
797 RewardDestination::Staked,
798 ));
799 pezframe_support::assert_ok!(match status {
800 crate::StakerStatus::Validator => <Pezpallet<T>>::validate(
801 T::RuntimeOrigin::from(Some(stash.clone()).into()),
802 Default::default(),
803 ),
804 crate::StakerStatus::Nominator(votes) => <Pezpallet<T>>::nominate(
805 T::RuntimeOrigin::from(Some(stash.clone()).into()),
806 votes.iter().map(|l| T::Lookup::unlookup(l.clone())).collect(),
807 ),
808 _ => Ok(()),
809 });
810 assert!(
811 ValidatorCount::<T>::get()
812 <= <T::ElectionProvider as ElectionProvider>::MaxWinnersPerPage::get()
813 * <T::ElectionProvider as ElectionProvider>::Pages::get()
814 );
815 }
816
817 // all voters are reported to the `VoterList`.
818 assert_eq!(
819 T::VoterList::count(),
820 Nominators::<T>::count() + Validators::<T>::count(),
821 "not all genesis stakers were inserted into sorted list provider, something is wrong."
822 );
823 }
824 }
825
826 #[pezpallet::event]
827 #[pezpallet::generate_deposit(pub(crate) fn deposit_event)]
828 pub enum Event<T: Config> {
829 /// The era payout has been set; the first balance is the validator-payout; the second is
830 /// the remainder from the maximum amount of reward.
831 EraPaid { era_index: EraIndex, validator_payout: BalanceOf<T>, remainder: BalanceOf<T> },
832 /// The nominator has been rewarded by this amount to this destination.
833 Rewarded {
834 stash: T::AccountId,
835 dest: RewardDestination<T::AccountId>,
836 amount: BalanceOf<T>,
837 },
838 /// A staker (validator or nominator) has been slashed by the given amount.
839 Slashed { staker: T::AccountId, amount: BalanceOf<T> },
840 /// A slash for the given validator, for the given percentage of their stake, at the given
841 /// era as been reported.
842 SlashReported { validator: T::AccountId, fraction: Perbill, slash_era: EraIndex },
843 /// An old slashing report from a prior era was discarded because it could
844 /// not be processed.
845 OldSlashingReportDiscarded { session_index: SessionIndex },
846 /// A new set of stakers was elected.
847 StakersElected,
848 /// An account has bonded this amount. \[stash, amount\]
849 ///
850 /// NOTE: This event is only emitted when funds are bonded via a dispatchable. Notably,
851 /// it will not be emitted for staking rewards when they are added to stake.
852 Bonded { stash: T::AccountId, amount: BalanceOf<T> },
853 /// An account has unbonded this amount.
854 Unbonded { stash: T::AccountId, amount: BalanceOf<T> },
855 /// An account has called `withdraw_unbonded` and removed unbonding chunks worth `Balance`
856 /// from the unlocking queue.
857 Withdrawn { stash: T::AccountId, amount: BalanceOf<T> },
858 /// A nominator has been kicked from a validator.
859 Kicked { nominator: T::AccountId, stash: T::AccountId },
860 /// The election failed. No new era is planned.
861 StakingElectionFailed,
862 /// An account has stopped participating as either a validator or nominator.
863 Chilled { stash: T::AccountId },
864 /// A Page of stakers rewards are getting paid. `next` is `None` if all pages are claimed.
865 PayoutStarted {
866 era_index: EraIndex,
867 validator_stash: T::AccountId,
868 page: Page,
869 next: Option<Page>,
870 },
871 /// A validator has set their preferences.
872 ValidatorPrefsSet { stash: T::AccountId, prefs: ValidatorPrefs },
873 /// Voters size limit reached.
874 SnapshotVotersSizeExceeded { size: u32 },
875 /// Targets size limit reached.
876 SnapshotTargetsSizeExceeded { size: u32 },
877 /// A new force era mode was set.
878 ForceEra { mode: Forcing },
879 /// Report of a controller batch deprecation.
880 ControllerBatchDeprecated { failures: u32 },
881 /// Staking balance migrated from locks to holds, with any balance that could not be held
882 /// is force withdrawn.
883 CurrencyMigrated { stash: T::AccountId, force_withdraw: BalanceOf<T> },
884 }
885
886 #[pezpallet::error]
887 #[derive(PartialEq)]
888 pub enum Error<T> {
889 /// Not a controller account.
890 NotController,
891 /// Not a stash account.
892 NotStash,
893 /// Stash is already bonded.
894 AlreadyBonded,
895 /// Controller is already paired.
896 AlreadyPaired,
897 /// Targets cannot be empty.
898 EmptyTargets,
899 /// Duplicate index.
900 DuplicateIndex,
901 /// Slash record index out of bounds.
902 InvalidSlashIndex,
903 /// Cannot have a validator or nominator role, with value less than the minimum defined by
904 /// governance (see `MinValidatorBond` and `MinNominatorBond`). If unbonding is the
905 /// intention, `chill` first to remove one's role as validator/nominator.
906 InsufficientBond,
907 /// Can not schedule more unlock chunks.
908 NoMoreChunks,
909 /// Can not rebond without unlocking chunks.
910 NoUnlockChunk,
911 /// Attempting to target a stash that still has funds.
912 FundedTarget,
913 /// Invalid era to reward.
914 InvalidEraToReward,
915 /// Invalid number of nominations.
916 InvalidNumberOfNominations,
917 /// Items are not sorted and unique.
918 NotSortedAndUnique,
919 /// Rewards for this era have already been claimed for this validator.
920 AlreadyClaimed,
921 /// No nominators exist on this page.
922 InvalidPage,
923 /// Incorrect previous history depth input provided.
924 IncorrectHistoryDepth,
925 /// Incorrect number of slashing spans provided.
926 IncorrectSlashingSpans,
927 /// Internal state has become somehow corrupted and the operation cannot continue.
928 BadState,
929 /// Too many nomination targets supplied.
930 TooManyTargets,
931 /// A nomination target was supplied that was blocked or otherwise not a validator.
932 BadTarget,
933 /// The user has enough bond and thus cannot be chilled forcefully by an external person.
934 CannotChillOther,
935 /// There are too many nominators in the system. Governance needs to adjust the staking
936 /// settings to keep things safe for the runtime.
937 TooManyNominators,
938 /// There are too many validator candidates in the system. Governance needs to adjust the
939 /// staking settings to keep things safe for the runtime.
940 TooManyValidators,
941 /// Commission is too low. Must be at least `MinCommission`.
942 CommissionTooLow,
943 /// Some bound is not met.
944 BoundNotMet,
945 /// Used when attempting to use deprecated controller account logic.
946 ControllerDeprecated,
947 /// Cannot reset a ledger.
948 CannotRestoreLedger,
949 /// Provided reward destination is not allowed.
950 RewardDestinationRestricted,
951 /// Not enough funds available to withdraw.
952 NotEnoughFunds,
953 /// Operation not allowed for virtual stakers.
954 VirtualStakerNotAllowed,
955 /// Stash could not be reaped as other pezpallet might depend on it.
956 CannotReapStash,
957 /// The stake of this account is already migrated to `Fungible` holds.
958 AlreadyMigrated,
959 /// Account is restricted from participation in staking. This may happen if the account is
960 /// staking in another way already, such as via pool.
961 Restricted,
962 }
963
964 #[pezpallet::hooks]
965 impl<T: Config> Hooks<BlockNumberFor<T>> for Pezpallet<T> {
966 fn on_initialize(_now: BlockNumberFor<T>) -> Weight {
967 // just return the weight of the on_finalize.
968 T::DbWeight::get().reads(1)
969 }
970
971 fn on_finalize(_n: BlockNumberFor<T>) {
972 // Set the start of the first era.
973 if let Some(mut active_era) = ActiveEra::<T>::get() {
974 if active_era.start.is_none() {
975 let now_as_millis_u64 = T::UnixTime::now().as_millis().saturated_into::<u64>();
976 active_era.start = Some(now_as_millis_u64);
977 // This write only ever happens once, we don't include it in the weight in
978 // general
979 ActiveEra::<T>::put(active_era);
980 }
981 }
982 // `on_finalize` weight is tracked in `on_initialize`
983 }
984
985 fn integrity_test() {
986 // ensure that we funnel the correct value to the `DataProvider::MaxVotesPerVoter`;
987 assert_eq!(
988 MaxNominationsOf::<T>::get(),
989 <Self as ElectionDataProvider>::MaxVotesPerVoter::get()
990 );
991 // and that MaxNominations is always greater than 1, since we count on this.
992 assert!(!MaxNominationsOf::<T>::get().is_zero());
993
994 // ensure election results are always bounded with the same value
995 assert!(
996 <T::ElectionProvider as ElectionProvider>::MaxWinnersPerPage::get()
997 == <T::GenesisElectionProvider as ElectionProvider>::MaxWinnersPerPage::get()
998 );
999
1000 assert!(
1001 T::SlashDeferDuration::get() < T::BondingDuration::get() || T::BondingDuration::get() == 0,
1002 "As per documentation, slash defer duration ({}) should be less than bonding duration ({}).",
1003 T::SlashDeferDuration::get(),
1004 T::BondingDuration::get(),
1005 )
1006 }
1007
1008 #[cfg(feature = "try-runtime")]
1009 fn try_state(n: BlockNumberFor<T>) -> Result<(), pezsp_runtime::TryRuntimeError> {
1010 Self::do_try_state(n)
1011 }
1012 }
1013
1014 impl<T: Config> Pezpallet<T> {
1015 /// Get the ideal number of active validators.
1016 pub fn validator_count() -> u32 {
1017 ValidatorCount::<T>::get()
1018 }
1019
1020 /// Get the minimum number of staking participants before emergency conditions are imposed.
1021 pub fn minimum_validator_count() -> u32 {
1022 MinimumValidatorCount::<T>::get()
1023 }
1024
1025 /// Get the validators that may never be slashed or forcibly kicked out.
1026 pub fn invulnerables() -> Vec<T::AccountId> {
1027 Invulnerables::<T>::get()
1028 }
1029
1030 /// Get the preferences of a given validator.
1031 pub fn validators<EncodeLikeAccountId>(account_id: EncodeLikeAccountId) -> ValidatorPrefs
1032 where
1033 EncodeLikeAccountId: codec::EncodeLike<T::AccountId>,
1034 {
1035 Validators::<T>::get(account_id)
1036 }
1037
1038 /// Get the nomination preferences of a given nominator.
1039 pub fn nominators<EncodeLikeAccountId>(
1040 account_id: EncodeLikeAccountId,
1041 ) -> Option<Nominations<T>>
1042 where
1043 EncodeLikeAccountId: codec::EncodeLike<T::AccountId>,
1044 {
1045 Nominators::<T>::get(account_id)
1046 }
1047
1048 /// Get the current era index.
1049 pub fn current_era() -> Option<EraIndex> {
1050 CurrentEra::<T>::get()
1051 }
1052
1053 /// Get the active era information.
1054 pub fn active_era() -> Option<ActiveEraInfo> {
1055 ActiveEra::<T>::get()
1056 }
1057
1058 /// Get the session index at which the era starts for the last [`Config::HistoryDepth`]
1059 /// eras.
1060 pub fn eras_start_session_index<EncodeLikeEraIndex>(
1061 era_index: EncodeLikeEraIndex,
1062 ) -> Option<SessionIndex>
1063 where
1064 EncodeLikeEraIndex: codec::EncodeLike<EraIndex>,
1065 {
1066 ErasStartSessionIndex::<T>::get(era_index)
1067 }
1068
1069 /// Get the clipped exposure of a given validator at an era.
1070 pub fn eras_stakers_clipped<EncodeLikeEraIndex, EncodeLikeAccountId>(
1071 era_index: EncodeLikeEraIndex,
1072 account_id: EncodeLikeAccountId,
1073 ) -> Exposure<T::AccountId, BalanceOf<T>>
1074 where
1075 EncodeLikeEraIndex: codec::EncodeLike<EraIndex>,
1076 EncodeLikeAccountId: codec::EncodeLike<T::AccountId>,
1077 {
1078 ErasStakersClipped::<T>::get(era_index, account_id)
1079 }
1080
1081 /// Get the paged history of claimed rewards by era for given validator.
1082 pub fn claimed_rewards<EncodeLikeEraIndex, EncodeLikeAccountId>(
1083 era_index: EncodeLikeEraIndex,
1084 account_id: EncodeLikeAccountId,
1085 ) -> Vec<Page>
1086 where
1087 EncodeLikeEraIndex: codec::EncodeLike<EraIndex>,
1088 EncodeLikeAccountId: codec::EncodeLike<T::AccountId>,
1089 {
1090 ClaimedRewards::<T>::get(era_index, account_id)
1091 }
1092
1093 /// Get the preferences of given validator at given era.
1094 pub fn eras_validator_prefs<EncodeLikeEraIndex, EncodeLikeAccountId>(
1095 era_index: EncodeLikeEraIndex,
1096 account_id: EncodeLikeAccountId,
1097 ) -> ValidatorPrefs
1098 where
1099 EncodeLikeEraIndex: codec::EncodeLike<EraIndex>,
1100 EncodeLikeAccountId: codec::EncodeLike<T::AccountId>,
1101 {
1102 ErasValidatorPrefs::<T>::get(era_index, account_id)
1103 }
1104
1105 /// Get the total validator era payout for the last [`Config::HistoryDepth`] eras.
1106 pub fn eras_validator_reward<EncodeLikeEraIndex>(
1107 era_index: EncodeLikeEraIndex,
1108 ) -> Option<BalanceOf<T>>
1109 where
1110 EncodeLikeEraIndex: codec::EncodeLike<EraIndex>,
1111 {
1112 ErasValidatorReward::<T>::get(era_index)
1113 }
1114
1115 /// Get the rewards for the last [`Config::HistoryDepth`] eras.
1116 pub fn eras_reward_points<EncodeLikeEraIndex>(
1117 era_index: EncodeLikeEraIndex,
1118 ) -> EraRewardPoints<T::AccountId>
1119 where
1120 EncodeLikeEraIndex: codec::EncodeLike<EraIndex>,
1121 {
1122 ErasRewardPoints::<T>::get(era_index)
1123 }
1124
1125 /// Get the total amount staked for the last [`Config::HistoryDepth`] eras.
1126 pub fn eras_total_stake<EncodeLikeEraIndex>(era_index: EncodeLikeEraIndex) -> BalanceOf<T>
1127 where
1128 EncodeLikeEraIndex: codec::EncodeLike<EraIndex>,
1129 {
1130 ErasTotalStake::<T>::get(era_index)
1131 }
1132
1133 /// Get the mode of era forcing.
1134 pub fn force_era() -> Forcing {
1135 ForceEra::<T>::get()
1136 }
1137
1138 /// Get the percentage of the slash that is distributed to reporters.
1139 pub fn slash_reward_fraction() -> Perbill {
1140 SlashRewardFraction::<T>::get()
1141 }
1142
1143 /// Get the amount of canceled slash payout.
1144 pub fn canceled_payout() -> BalanceOf<T> {
1145 CanceledSlashPayout::<T>::get()
1146 }
1147
1148 /// Get the slashing spans for given account.
1149 pub fn slashing_spans<EncodeLikeAccountId>(
1150 account_id: EncodeLikeAccountId,
1151 ) -> Option<slashing::SlashingSpans>
1152 where
1153 EncodeLikeAccountId: codec::EncodeLike<T::AccountId>,
1154 {
1155 SlashingSpans::<T>::get(account_id)
1156 }
1157
1158 /// Get the last planned session scheduled by the session pezpallet.
1159 pub fn current_planned_session() -> SessionIndex {
1160 CurrentPlannedSession::<T>::get()
1161 }
1162 }
1163
1164 #[pezpallet::call]
1165 impl<T: Config> Pezpallet<T> {
1166 /// Take the origin account as a stash and lock up `value` of its balance. `controller` will
1167 /// be the account that controls it.
1168 ///
1169 /// `value` must be more than the `minimum_balance` specified by `T::Currency`.
1170 ///
1171 /// The dispatch origin for this call must be _Signed_ by the stash account.
1172 ///
1173 /// Emits `Bonded`.
1174 /// ## Complexity
1175 /// - Independent of the arguments. Moderate complexity.
1176 /// - O(1).
1177 /// - Three extra DB entries.
1178 ///
1179 /// NOTE: Two of the storage writes (`Self::bonded`, `Self::payee`) are _never_ cleaned
1180 /// unless the `origin` falls below _existential deposit_ (or equal to 0) and gets removed
1181 /// as dust.
1182 #[pezpallet::call_index(0)]
1183 #[pezpallet::weight(T::WeightInfo::bond())]
1184 pub fn bond(
1185 origin: OriginFor<T>,
1186 #[pezpallet::compact] value: BalanceOf<T>,
1187 payee: RewardDestination<T::AccountId>,
1188 ) -> DispatchResult {
1189 let stash = ensure_signed(origin)?;
1190
1191 ensure!(!T::Filter::contains(&stash), Error::<T>::Restricted);
1192
1193 if StakingLedger::<T>::is_bonded(StakingAccount::Stash(stash.clone())) {
1194 return Err(Error::<T>::AlreadyBonded.into());
1195 }
1196
1197 // An existing controller cannot become a stash.
1198 if StakingLedger::<T>::is_bonded(StakingAccount::Controller(stash.clone())) {
1199 return Err(Error::<T>::AlreadyPaired.into());
1200 }
1201
1202 // Reject a bond which is considered to be _dust_.
1203 if value < asset::existential_deposit::<T>() {
1204 return Err(Error::<T>::InsufficientBond.into());
1205 }
1206
1207 let stash_balance = asset::free_to_stake::<T>(&stash);
1208 let value = value.min(stash_balance);
1209 Self::deposit_event(Event::<T>::Bonded { stash: stash.clone(), amount: value });
1210 let ledger = StakingLedger::<T>::new(stash.clone(), value);
1211
1212 // You're auto-bonded forever, here. We might improve this by only bonding when
1213 // you actually validate/nominate and remove once you unbond __everything__.
1214 ledger.bond(payee)?;
1215
1216 Ok(())
1217 }
1218
1219 /// Add some extra amount that have appeared in the stash `free_balance` into the balance up
1220 /// for staking.
1221 ///
1222 /// The dispatch origin for this call must be _Signed_ by the stash, not the controller.
1223 ///
1224 /// Use this if there are additional funds in your stash account that you wish to bond.
1225 /// Unlike [`bond`](Self::bond) or [`unbond`](Self::unbond) this function does not impose
1226 /// any limitation on the amount that can be added.
1227 ///
1228 /// Emits `Bonded`.
1229 ///
1230 /// ## Complexity
1231 /// - Independent of the arguments. Insignificant complexity.
1232 /// - O(1).
1233 #[pezpallet::call_index(1)]
1234 #[pezpallet::weight(T::WeightInfo::bond_extra())]
1235 pub fn bond_extra(
1236 origin: OriginFor<T>,
1237 #[pezpallet::compact] max_additional: BalanceOf<T>,
1238 ) -> DispatchResult {
1239 let stash = ensure_signed(origin)?;
1240 ensure!(!T::Filter::contains(&stash), Error::<T>::Restricted);
1241 Self::do_bond_extra(&stash, max_additional)
1242 }
1243
1244 /// Schedule a portion of the stash to be unlocked ready for transfer out after the bond
1245 /// period ends. If this leaves an amount actively bonded less than
1246 /// [`asset::existential_deposit`], then it is increased to the full amount.
1247 ///
1248 /// The stash may be chilled if the ledger total amount falls to 0 after unbonding.
1249 ///
1250 /// The dispatch origin for this call must be _Signed_ by the controller, not the stash.
1251 ///
1252 /// Once the unlock period is done, you can call `withdraw_unbonded` to actually move
1253 /// the funds out of management ready for transfer.
1254 ///
1255 /// No more than a limited number of unlocking chunks (see `MaxUnlockingChunks`)
1256 /// can co-exists at the same time. If there are no unlocking chunks slots available
1257 /// [`Call::withdraw_unbonded`] is called to remove some of the chunks (if possible).
1258 ///
1259 /// If a user encounters the `InsufficientBond` error when calling this extrinsic,
1260 /// they should call `chill` first in order to free up their bonded funds.
1261 ///
1262 /// Emits `Unbonded`.
1263 ///
1264 /// See also [`Call::withdraw_unbonded`].
1265 #[pezpallet::call_index(2)]
1266 #[pezpallet::weight(
1267 T::WeightInfo::withdraw_unbonded_kill(SPECULATIVE_NUM_SPANS).saturating_add(T::WeightInfo::unbond()).saturating_add(T::WeightInfo::chill()))
1268 ]
1269 pub fn unbond(
1270 origin: OriginFor<T>,
1271 #[pezpallet::compact] value: BalanceOf<T>,
1272 ) -> DispatchResultWithPostInfo {
1273 let controller = ensure_signed(origin)?;
1274
1275 let ledger = Self::ledger(StakingAccount::Controller(controller.clone()))?;
1276
1277 let mut total_weight = if value >= ledger.total {
1278 Self::chill_stash(&ledger.stash);
1279 T::WeightInfo::chill()
1280 } else {
1281 Zero::zero()
1282 };
1283
1284 if let Some(withdraw_weight) = Self::do_unbond(controller, value)? {
1285 total_weight.saturating_accrue(withdraw_weight);
1286 }
1287
1288 Ok(Some(total_weight).into())
1289 }
1290
1291 /// Remove any unlocked chunks from the `unlocking` queue from our management.
1292 ///
1293 /// This essentially frees up that balance to be used by the stash account to do whatever
1294 /// it wants.
1295 ///
1296 /// The dispatch origin for this call must be _Signed_ by the controller.
1297 ///
1298 /// Emits `Withdrawn`.
1299 ///
1300 /// See also [`Call::unbond`].
1301 ///
1302 /// ## Parameters
1303 ///
1304 /// - `num_slashing_spans` indicates the number of metadata slashing spans to clear when
1305 /// this call results in a complete removal of all the data related to the stash account.
1306 /// In this case, the `num_slashing_spans` must be larger or equal to the number of
1307 /// slashing spans associated with the stash account in the [`SlashingSpans`] storage type,
1308 /// otherwise the call will fail. The call weight is directly proportional to
1309 /// `num_slashing_spans`.
1310 ///
1311 /// ## Complexity
1312 /// O(S) where S is the number of slashing spans to remove
1313 /// NOTE: Weight annotation is the kill scenario, we refund otherwise.
1314 #[pezpallet::call_index(3)]
1315 #[pezpallet::weight(T::WeightInfo::withdraw_unbonded_kill(*num_slashing_spans))]
1316 pub fn withdraw_unbonded(
1317 origin: OriginFor<T>,
1318 num_slashing_spans: u32,
1319 ) -> DispatchResultWithPostInfo {
1320 let controller = ensure_signed(origin)?;
1321
1322 let actual_weight = Self::do_withdraw_unbonded(&controller, num_slashing_spans)?;
1323 Ok(Some(actual_weight).into())
1324 }
1325
1326 /// Declare the desire to validate for the origin controller.
1327 ///
1328 /// Effects will be felt at the beginning of the next era.
1329 ///
1330 /// The dispatch origin for this call must be _Signed_ by the controller, not the stash.
1331 #[pezpallet::call_index(4)]
1332 #[pezpallet::weight(T::WeightInfo::validate())]
1333 pub fn validate(origin: OriginFor<T>, prefs: ValidatorPrefs) -> DispatchResult {
1334 let controller = ensure_signed(origin)?;
1335
1336 let ledger = Self::ledger(Controller(controller))?;
1337
1338 ensure!(ledger.active >= MinValidatorBond::<T>::get(), Error::<T>::InsufficientBond);
1339 let stash = &ledger.stash;
1340
1341 // ensure their commission is correct.
1342 ensure!(prefs.commission >= MinCommission::<T>::get(), Error::<T>::CommissionTooLow);
1343
1344 // Only check limits if they are not already a validator.
1345 if !Validators::<T>::contains_key(stash) {
1346 // If this error is reached, we need to adjust the `MinValidatorBond` and start
1347 // calling `chill_other`. Until then, we explicitly block new validators to protect
1348 // the runtime.
1349 if let Some(max_validators) = MaxValidatorsCount::<T>::get() {
1350 ensure!(
1351 Validators::<T>::count() < max_validators,
1352 Error::<T>::TooManyValidators
1353 );
1354 }
1355 }
1356
1357 Self::do_remove_nominator(stash);
1358 Self::do_add_validator(stash, prefs.clone());
1359 Self::deposit_event(Event::<T>::ValidatorPrefsSet { stash: ledger.stash, prefs });
1360
1361 Ok(())
1362 }
1363
1364 /// Declare the desire to nominate `targets` for the origin controller.
1365 ///
1366 /// Effects will be felt at the beginning of the next era.
1367 ///
1368 /// The dispatch origin for this call must be _Signed_ by the controller, not the stash.
1369 ///
1370 /// ## Complexity
1371 /// - The transaction's complexity is proportional to the size of `targets` (N)
1372 /// which is capped at CompactAssignments::LIMIT (T::MaxNominations).
1373 /// - Both the reads and writes follow a similar pattern.
1374 #[pezpallet::call_index(5)]
1375 #[pezpallet::weight(T::WeightInfo::nominate(targets.len() as u32))]
1376 pub fn nominate(
1377 origin: OriginFor<T>,
1378 targets: Vec<AccountIdLookupOf<T>>,
1379 ) -> DispatchResult {
1380 let controller = ensure_signed(origin)?;
1381
1382 let ledger = Self::ledger(StakingAccount::Controller(controller.clone()))?;
1383
1384 ensure!(ledger.active >= MinNominatorBond::<T>::get(), Error::<T>::InsufficientBond);
1385 let stash = &ledger.stash;
1386
1387 // Only check limits if they are not already a nominator.
1388 if !Nominators::<T>::contains_key(stash) {
1389 // If this error is reached, we need to adjust the `MinNominatorBond` and start
1390 // calling `chill_other`. Until then, we explicitly block new nominators to protect
1391 // the runtime.
1392 if let Some(max_nominators) = MaxNominatorsCount::<T>::get() {
1393 ensure!(
1394 Nominators::<T>::count() < max_nominators,
1395 Error::<T>::TooManyNominators
1396 );
1397 }
1398 }
1399
1400 ensure!(!targets.is_empty(), Error::<T>::EmptyTargets);
1401 ensure!(
1402 targets.len() <= T::NominationsQuota::get_quota(ledger.active) as usize,
1403 Error::<T>::TooManyTargets
1404 );
1405
1406 let old = Nominators::<T>::get(stash).map_or_else(Vec::new, |x| x.targets.into_inner());
1407
1408 let targets: BoundedVec<_, _> = targets
1409 .into_iter()
1410 .map(|t| T::Lookup::lookup(t).map_err(DispatchError::from))
1411 .map(|n| {
1412 n.and_then(|n| {
1413 if old.contains(&n) || !Validators::<T>::get(&n).blocked {
1414 Ok(n)
1415 } else {
1416 Err(Error::<T>::BadTarget.into())
1417 }
1418 })
1419 })
1420 .collect::<Result<Vec<_>, _>>()?
1421 .try_into()
1422 .map_err(|_| Error::<T>::TooManyNominators)?;
1423
1424 let nominations = Nominations {
1425 targets,
1426 // Initial nominations are considered submitted at era 0. See `Nominations` doc.
1427 submitted_in: CurrentEra::<T>::get().unwrap_or(0),
1428 suppressed: false,
1429 };
1430
1431 Self::do_remove_validator(stash);
1432 Self::do_add_nominator(stash, nominations);
1433 Ok(())
1434 }
1435
1436 /// Declare no desire to either validate or nominate.
1437 ///
1438 /// Effects will be felt at the beginning of the next era.
1439 ///
1440 /// The dispatch origin for this call must be _Signed_ by the controller, not the stash.
1441 ///
1442 /// ## Complexity
1443 /// - Independent of the arguments. Insignificant complexity.
1444 /// - Contains one read.
1445 /// - Writes are limited to the `origin` account key.
1446 #[pezpallet::call_index(6)]
1447 #[pezpallet::weight(T::WeightInfo::chill())]
1448 pub fn chill(origin: OriginFor<T>) -> DispatchResult {
1449 let controller = ensure_signed(origin)?;
1450
1451 let ledger = Self::ledger(StakingAccount::Controller(controller))?;
1452
1453 Self::chill_stash(&ledger.stash);
1454 Ok(())
1455 }
1456
1457 /// (Re-)set the payment target for a controller.
1458 ///
1459 /// Effects will be felt instantly (as soon as this function is completed successfully).
1460 ///
1461 /// The dispatch origin for this call must be _Signed_ by the controller, not the stash.
1462 ///
1463 /// ## Complexity
1464 /// - O(1)
1465 /// - Independent of the arguments. Insignificant complexity.
1466 /// - Contains a limited number of reads.
1467 /// - Writes are limited to the `origin` account key.
1468 /// ---------
1469 #[pezpallet::call_index(7)]
1470 #[pezpallet::weight(T::WeightInfo::set_payee())]
1471 pub fn set_payee(
1472 origin: OriginFor<T>,
1473 payee: RewardDestination<T::AccountId>,
1474 ) -> DispatchResult {
1475 let controller = ensure_signed(origin)?;
1476 let ledger = Self::ledger(Controller(controller.clone()))?;
1477
1478 ensure!(
1479 (payee != {
1480 #[allow(deprecated)]
1481 RewardDestination::Controller
1482 }),
1483 Error::<T>::ControllerDeprecated
1484 );
1485
1486 ledger
1487 .set_payee(payee)
1488 .defensive_proof("ledger was retrieved from storage, thus it's bonded; qed.")?;
1489
1490 Ok(())
1491 }
1492
1493 /// (Re-)sets the controller of a stash to the stash itself. This function previously
1494 /// accepted a `controller` argument to set the controller to an account other than the
1495 /// stash itself. This functionality has now been removed, now only setting the controller
1496 /// to the stash, if it is not already.
1497 ///
1498 /// Effects will be felt instantly (as soon as this function is completed successfully).
1499 ///
1500 /// The dispatch origin for this call must be _Signed_ by the stash, not the controller.
1501 ///
1502 /// ## Complexity
1503 /// O(1)
1504 /// - Independent of the arguments. Insignificant complexity.
1505 /// - Contains a limited number of reads.
1506 /// - Writes are limited to the `origin` account key.
1507 #[pezpallet::call_index(8)]
1508 #[pezpallet::weight(T::WeightInfo::set_controller())]
1509 pub fn set_controller(origin: OriginFor<T>) -> DispatchResult {
1510 let stash = ensure_signed(origin)?;
1511
1512 Self::ledger(StakingAccount::Stash(stash.clone())).map(|ledger| {
1513 let controller = ledger.controller()
1514 .defensive_proof("Ledger's controller field didn't exist. The controller should have been fetched using StakingLedger.")
1515 .ok_or(Error::<T>::NotController)?;
1516
1517 if controller == stash {
1518 // Stash is already its own controller.
1519 return Err(Error::<T>::AlreadyPaired.into())
1520 }
1521
1522 ledger.set_controller_to_stash()?;
1523 Ok(())
1524 })?
1525 }
1526
1527 /// Sets the ideal number of validators.
1528 ///
1529 /// The dispatch origin must be Root.
1530 ///
1531 /// ## Complexity
1532 /// O(1)
1533 #[pezpallet::call_index(9)]
1534 #[pezpallet::weight(T::WeightInfo::set_validator_count())]
1535 pub fn set_validator_count(
1536 origin: OriginFor<T>,
1537 #[pezpallet::compact] new: u32,
1538 ) -> DispatchResult {
1539 ensure_root(origin)?;
1540 // ensure new validator count does not exceed maximum winners
1541 // support by election provider.
1542 ensure!(new <= T::MaxValidatorSet::get(), Error::<T>::TooManyValidators);
1543
1544 ValidatorCount::<T>::put(new);
1545 Ok(())
1546 }
1547
1548 /// Increments the ideal number of validators up to maximum of
1549 /// `ElectionProviderBase::MaxWinners`.
1550 ///
1551 /// The dispatch origin must be Root.
1552 ///
1553 /// ## Complexity
1554 /// Same as [`Self::set_validator_count`].
1555 #[pezpallet::call_index(10)]
1556 #[pezpallet::weight(T::WeightInfo::set_validator_count())]
1557 pub fn increase_validator_count(
1558 origin: OriginFor<T>,
1559 #[pezpallet::compact] additional: u32,
1560 ) -> DispatchResult {
1561 ensure_root(origin)?;
1562 let old = ValidatorCount::<T>::get();
1563 let new = old.checked_add(additional).ok_or(ArithmeticError::Overflow)?;
1564 ensure!(new <= T::MaxValidatorSet::get(), Error::<T>::TooManyValidators);
1565
1566 ValidatorCount::<T>::put(new);
1567 Ok(())
1568 }
1569
1570 /// Scale up the ideal number of validators by a factor up to maximum of
1571 /// `ElectionProviderBase::MaxWinners`.
1572 ///
1573 /// The dispatch origin must be Root.
1574 ///
1575 /// ## Complexity
1576 /// Same as [`Self::set_validator_count`].
1577 #[pezpallet::call_index(11)]
1578 #[pezpallet::weight(T::WeightInfo::set_validator_count())]
1579 pub fn scale_validator_count(origin: OriginFor<T>, factor: Percent) -> DispatchResult {
1580 ensure_root(origin)?;
1581 let old = ValidatorCount::<T>::get();
1582 let new = old.checked_add(factor.mul_floor(old)).ok_or(ArithmeticError::Overflow)?;
1583
1584 ensure!(new <= T::MaxValidatorSet::get(), Error::<T>::TooManyValidators);
1585
1586 ValidatorCount::<T>::put(new);
1587 Ok(())
1588 }
1589
1590 /// Force there to be no new eras indefinitely.
1591 ///
1592 /// The dispatch origin must be Root.
1593 ///
1594 /// # Warning
1595 ///
1596 /// The election process starts multiple blocks before the end of the era.
1597 /// Thus the election process may be ongoing when this is called. In this case the
1598 /// election will continue until the next era is triggered.
1599 ///
1600 /// ## Complexity
1601 /// - No arguments.
1602 /// - Weight: O(1)
1603 #[pezpallet::call_index(12)]
1604 #[pezpallet::weight(T::WeightInfo::force_no_eras())]
1605 pub fn force_no_eras(origin: OriginFor<T>) -> DispatchResult {
1606 ensure_root(origin)?;
1607 Self::set_force_era(Forcing::ForceNone);
1608 Ok(())
1609 }
1610
1611 /// Force there to be a new era at the end of the next session. After this, it will be
1612 /// reset to normal (non-forced) behaviour.
1613 ///
1614 /// The dispatch origin must be Root.
1615 ///
1616 /// # Warning
1617 ///
1618 /// The election process starts multiple blocks before the end of the era.
1619 /// If this is called just before a new era is triggered, the election process may not
1620 /// have enough blocks to get a result.
1621 ///
1622 /// ## Complexity
1623 /// - No arguments.
1624 /// - Weight: O(1)
1625 #[pezpallet::call_index(13)]
1626 #[pezpallet::weight(T::WeightInfo::force_new_era())]
1627 pub fn force_new_era(origin: OriginFor<T>) -> DispatchResult {
1628 ensure_root(origin)?;
1629 Self::set_force_era(Forcing::ForceNew);
1630 Ok(())
1631 }
1632
1633 /// Set the validators who cannot be slashed (if any).
1634 ///
1635 /// The dispatch origin must be Root.
1636 #[pezpallet::call_index(14)]
1637 #[pezpallet::weight(T::WeightInfo::set_invulnerables(invulnerables.len() as u32))]
1638 pub fn set_invulnerables(
1639 origin: OriginFor<T>,
1640 invulnerables: Vec<T::AccountId>,
1641 ) -> DispatchResult {
1642 ensure_root(origin)?;
1643 <Invulnerables<T>>::put(invulnerables);
1644 Ok(())
1645 }
1646
1647 /// Force a current staker to become completely unstaked, immediately.
1648 ///
1649 /// The dispatch origin must be Root.
1650 ///
1651 /// ## Parameters
1652 ///
1653 /// - `num_slashing_spans`: Refer to comments on [`Call::withdraw_unbonded`] for more
1654 /// details.
1655 #[pezpallet::call_index(15)]
1656 #[pezpallet::weight(T::WeightInfo::force_unstake(*num_slashing_spans))]
1657 pub fn force_unstake(
1658 origin: OriginFor<T>,
1659 stash: T::AccountId,
1660 num_slashing_spans: u32,
1661 ) -> DispatchResult {
1662 ensure_root(origin)?;
1663
1664 // Remove all staking-related information and lock.
1665 Self::kill_stash(&stash, num_slashing_spans)?;
1666
1667 Ok(())
1668 }
1669
1670 /// Force there to be a new era at the end of sessions indefinitely.
1671 ///
1672 /// The dispatch origin must be Root.
1673 ///
1674 /// # Warning
1675 ///
1676 /// The election process starts multiple blocks before the end of the era.
1677 /// If this is called just before a new era is triggered, the election process may not
1678 /// have enough blocks to get a result.
1679 #[pezpallet::call_index(16)]
1680 #[pezpallet::weight(T::WeightInfo::force_new_era_always())]
1681 pub fn force_new_era_always(origin: OriginFor<T>) -> DispatchResult {
1682 ensure_root(origin)?;
1683 Self::set_force_era(Forcing::ForceAlways);
1684 Ok(())
1685 }
1686
1687 /// Cancel enactment of a deferred slash.
1688 ///
1689 /// Can be called by the `T::AdminOrigin`.
1690 ///
1691 /// Parameters: era and indices of the slashes for that era to kill.
1692 /// They **must** be sorted in ascending order, *and* unique.
1693 #[pezpallet::call_index(17)]
1694 #[pezpallet::weight(T::WeightInfo::cancel_deferred_slash(slash_indices.len() as u32))]
1695 pub fn cancel_deferred_slash(
1696 origin: OriginFor<T>,
1697 era: EraIndex,
1698 slash_indices: Vec<u32>,
1699 ) -> DispatchResult {
1700 T::AdminOrigin::ensure_origin(origin)?;
1701
1702 ensure!(!slash_indices.is_empty(), Error::<T>::EmptyTargets);
1703 ensure!(is_sorted_and_unique(&slash_indices), Error::<T>::NotSortedAndUnique);
1704
1705 let mut unapplied = UnappliedSlashes::<T>::get(&era);
1706 let last_item = slash_indices[slash_indices.len() - 1];
1707 ensure!((last_item as usize) < unapplied.len(), Error::<T>::InvalidSlashIndex);
1708
1709 for (removed, index) in slash_indices.into_iter().enumerate() {
1710 let index = (index as usize) - removed;
1711 unapplied.remove(index);
1712 }
1713
1714 UnappliedSlashes::<T>::insert(&era, &unapplied);
1715 Ok(())
1716 }
1717
1718 /// Pay out next page of the stakers behind a validator for the given era.
1719 ///
1720 /// - `validator_stash` is the stash account of the validator.
1721 /// - `era` may be any era between `[current_era - history_depth; current_era]`.
1722 ///
1723 /// The origin of this call must be _Signed_. Any account can call this function, even if
1724 /// it is not one of the stakers.
1725 ///
1726 /// The reward payout could be paged in case there are too many nominators backing the
1727 /// `validator_stash`. This call will payout unpaid pages in an ascending order. To claim a
1728 /// specific page, use `payout_stakers_by_page`.`
1729 ///
1730 /// If all pages are claimed, it returns an error `InvalidPage`.
1731 #[pezpallet::call_index(18)]
1732 #[pezpallet::weight(T::WeightInfo::payout_stakers_alive_staked(
1733 T::MaxExposurePageSize::get()
1734 ))]
1735 pub fn payout_stakers(
1736 origin: OriginFor<T>,
1737 validator_stash: T::AccountId,
1738 era: EraIndex,
1739 ) -> DispatchResultWithPostInfo {
1740 ensure_signed(origin)?;
1741 Self::do_payout_stakers(validator_stash, era)
1742 }
1743
1744 /// Rebond a portion of the stash scheduled to be unlocked.
1745 ///
1746 /// The dispatch origin must be signed by the controller.
1747 ///
1748 /// ## Complexity
1749 /// - Time complexity: O(L), where L is unlocking chunks
1750 /// - Bounded by `MaxUnlockingChunks`.
1751 #[pezpallet::call_index(19)]
1752 #[pezpallet::weight(T::WeightInfo::rebond(T::MaxUnlockingChunks::get() as u32))]
1753 pub fn rebond(
1754 origin: OriginFor<T>,
1755 #[pezpallet::compact] value: BalanceOf<T>,
1756 ) -> DispatchResultWithPostInfo {
1757 let controller = ensure_signed(origin)?;
1758 let ledger = Self::ledger(Controller(controller))?;
1759
1760 ensure!(!T::Filter::contains(&ledger.stash), Error::<T>::Restricted);
1761 ensure!(!ledger.unlocking.is_empty(), Error::<T>::NoUnlockChunk);
1762
1763 let initial_unlocking = ledger.unlocking.len() as u32;
1764 let (ledger, rebonded_value) = ledger.rebond(value);
1765 // Last check: the new active amount of ledger must be more than ED.
1766 ensure!(
1767 ledger.active >= asset::existential_deposit::<T>(),
1768 Error::<T>::InsufficientBond
1769 );
1770
1771 Self::deposit_event(Event::<T>::Bonded {
1772 stash: ledger.stash.clone(),
1773 amount: rebonded_value,
1774 });
1775
1776 let stash = ledger.stash.clone();
1777 let final_unlocking = ledger.unlocking.len();
1778
1779 // NOTE: ledger must be updated prior to calling `Self::weight_of`.
1780 ledger.update()?;
1781 if T::VoterList::contains(&stash) {
1782 let _ = T::VoterList::on_update(&stash, Self::weight_of(&stash)).defensive();
1783 }
1784
1785 let removed_chunks = 1u32 // for the case where the last iterated chunk is not removed
1786 .saturating_add(initial_unlocking)
1787 .saturating_sub(final_unlocking as u32);
1788 Ok(Some(T::WeightInfo::rebond(removed_chunks)).into())
1789 }
1790
1791 /// Remove all data structures concerning a staker/stash once it is at a state where it can
1792 /// be considered `dust` in the staking system. The requirements are:
1793 ///
1794 /// 1. the `total_balance` of the stash is below existential deposit.
1795 /// 2. or, the `ledger.total` of the stash is below existential deposit.
1796 /// 3. or, existential deposit is zero and either `total_balance` or `ledger.total` is zero.
1797 ///
1798 /// The former can happen in cases like a slash; the latter when a fully unbonded account
1799 /// is still receiving staking rewards in `RewardDestination::Staked`.
1800 ///
1801 /// It can be called by anyone, as long as `stash` meets the above requirements.
1802 ///
1803 /// Refunds the transaction fees upon successful execution.
1804 ///
1805 /// ## Parameters
1806 ///
1807 /// - `num_slashing_spans`: Refer to comments on [`Call::withdraw_unbonded`] for more
1808 /// details.
1809 #[pezpallet::call_index(20)]
1810 #[pezpallet::weight(T::WeightInfo::reap_stash(*num_slashing_spans))]
1811 pub fn reap_stash(
1812 origin: OriginFor<T>,
1813 stash: T::AccountId,
1814 num_slashing_spans: u32,
1815 ) -> DispatchResultWithPostInfo {
1816 ensure_signed(origin)?;
1817
1818 // virtual stakers should not be allowed to be reaped.
1819 ensure!(!Self::is_virtual_staker(&stash), Error::<T>::VirtualStakerNotAllowed);
1820
1821 let ed = asset::existential_deposit::<T>();
1822 let origin_balance = asset::total_balance::<T>(&stash);
1823 let ledger_total =
1824 Self::ledger(Stash(stash.clone())).map(|l| l.total).unwrap_or_default();
1825 let reapable = origin_balance < ed
1826 || origin_balance.is_zero()
1827 || ledger_total < ed
1828 || ledger_total.is_zero();
1829 ensure!(reapable, Error::<T>::FundedTarget);
1830
1831 // Remove all staking-related information and lock.
1832 Self::kill_stash(&stash, num_slashing_spans)?;
1833
1834 Ok(Pays::No.into())
1835 }
1836
1837 /// Remove the given nominations from the calling validator.
1838 ///
1839 /// Effects will be felt at the beginning of the next era.
1840 ///
1841 /// The dispatch origin for this call must be _Signed_ by the controller, not the stash.
1842 ///
1843 /// - `who`: A list of nominator stash accounts who are nominating this validator which
1844 /// should no longer be nominating this validator.
1845 ///
1846 /// Note: Making this call only makes sense if you first set the validator preferences to
1847 /// block any further nominations.
1848 #[pezpallet::call_index(21)]
1849 #[pezpallet::weight(T::WeightInfo::kick(who.len() as u32))]
1850 pub fn kick(origin: OriginFor<T>, who: Vec<AccountIdLookupOf<T>>) -> DispatchResult {
1851 let controller = ensure_signed(origin)?;
1852 let ledger = Self::ledger(Controller(controller))?;
1853 let stash = &ledger.stash;
1854
1855 for nom_stash in who
1856 .into_iter()
1857 .map(T::Lookup::lookup)
1858 .collect::<Result<Vec<T::AccountId>, _>>()?
1859 .into_iter()
1860 {
1861 Nominators::<T>::mutate(&nom_stash, |maybe_nom| {
1862 if let Some(ref mut nom) = maybe_nom {
1863 if let Some(pos) = nom.targets.iter().position(|v| v == stash) {
1864 nom.targets.swap_remove(pos);
1865 Self::deposit_event(Event::<T>::Kicked {
1866 nominator: nom_stash.clone(),
1867 stash: stash.clone(),
1868 });
1869 }
1870 }
1871 });
1872 }
1873
1874 Ok(())
1875 }
1876
1877 /// Update the various staking configurations .
1878 ///
1879 /// * `min_nominator_bond`: The minimum active bond needed to be a nominator.
1880 /// * `min_validator_bond`: The minimum active bond needed to be a validator.
1881 /// * `max_nominator_count`: The max number of users who can be a nominator at once. When
1882 /// set to `None`, no limit is enforced.
1883 /// * `max_validator_count`: The max number of users who can be a validator at once. When
1884 /// set to `None`, no limit is enforced.
1885 /// * `chill_threshold`: The ratio of `max_nominator_count` or `max_validator_count` which
1886 /// should be filled in order for the `chill_other` transaction to work.
1887 /// * `min_commission`: The minimum amount of commission that each validators must maintain.
1888 /// This is checked only upon calling `validate`. Existing validators are not affected.
1889 ///
1890 /// RuntimeOrigin must be Root to call this function.
1891 ///
1892 /// NOTE: Existing nominators and validators will not be affected by this update.
1893 /// to kick people under the new limits, `chill_other` should be called.
1894 // We assume the worst case for this call is either: all items are set or all items are
1895 // removed.
1896 #[pezpallet::call_index(22)]
1897 #[pezpallet::weight(
1898 T::WeightInfo::set_staking_configs_all_set()
1899 .max(T::WeightInfo::set_staking_configs_all_remove())
1900 )]
1901 pub fn set_staking_configs(
1902 origin: OriginFor<T>,
1903 min_nominator_bond: ConfigOp<BalanceOf<T>>,
1904 min_validator_bond: ConfigOp<BalanceOf<T>>,
1905 max_nominator_count: ConfigOp<u32>,
1906 max_validator_count: ConfigOp<u32>,
1907 chill_threshold: ConfigOp<Percent>,
1908 min_commission: ConfigOp<Perbill>,
1909 max_staked_rewards: ConfigOp<Percent>,
1910 ) -> DispatchResult {
1911 ensure_root(origin)?;
1912
1913 macro_rules! config_op_exp {
1914 ($storage:ty, $op:ident) => {
1915 match $op {
1916 ConfigOp::Noop => (),
1917 ConfigOp::Set(v) => <$storage>::put(v),
1918 ConfigOp::Remove => <$storage>::kill(),
1919 }
1920 };
1921 }
1922
1923 config_op_exp!(MinNominatorBond<T>, min_nominator_bond);
1924 config_op_exp!(MinValidatorBond<T>, min_validator_bond);
1925 config_op_exp!(MaxNominatorsCount<T>, max_nominator_count);
1926 config_op_exp!(MaxValidatorsCount<T>, max_validator_count);
1927 config_op_exp!(ChillThreshold<T>, chill_threshold);
1928 config_op_exp!(MinCommission<T>, min_commission);
1929 config_op_exp!(MaxStakedRewards<T>, max_staked_rewards);
1930 Ok(())
1931 }
1932 /// Declare a `controller` to stop participating as either a validator or nominator.
1933 ///
1934 /// Effects will be felt at the beginning of the next era.
1935 ///
1936 /// The dispatch origin for this call must be _Signed_, but can be called by anyone.
1937 ///
1938 /// If the caller is the same as the controller being targeted, then no further checks are
1939 /// enforced, and this function behaves just like `chill`.
1940 ///
1941 /// If the caller is different than the controller being targeted, the following conditions
1942 /// must be met:
1943 ///
1944 /// * `controller` must belong to a nominator who has become non-decodable,
1945 ///
1946 /// Or:
1947 ///
1948 /// * A `ChillThreshold` must be set and checked which defines how close to the max
1949 /// nominators or validators we must reach before users can start chilling one-another.
1950 /// * A `MaxNominatorCount` and `MaxValidatorCount` must be set which is used to determine
1951 /// how close we are to the threshold.
1952 /// * A `MinNominatorBond` and `MinValidatorBond` must be set and checked, which determines
1953 /// if this is a person that should be chilled because they have not met the threshold
1954 /// bond required.
1955 ///
1956 /// This can be helpful if bond requirements are updated, and we need to remove old users
1957 /// who do not satisfy these requirements.
1958 #[pezpallet::call_index(23)]
1959 #[pezpallet::weight(T::WeightInfo::chill_other())]
1960 pub fn chill_other(origin: OriginFor<T>, stash: T::AccountId) -> DispatchResult {
1961 // Anyone can call this function.
1962 let caller = ensure_signed(origin)?;
1963 let ledger = Self::ledger(Stash(stash.clone()))?;
1964 let controller = ledger
1965 .controller()
1966 .defensive_proof(
1967 "Ledger's controller field didn't exist. The controller should have been fetched using StakingLedger.",
1968 )
1969 .ok_or(Error::<T>::NotController)?;
1970
1971 // In order for one user to chill another user, the following conditions must be met:
1972 //
1973 // * `controller` belongs to a nominator who has become non-decodable,
1974 //
1975 // Or
1976 //
1977 // * A `ChillThreshold` is set which defines how close to the max nominators or
1978 // validators we must reach before users can start chilling one-another.
1979 // * A `MaxNominatorCount` and `MaxValidatorCount` which is used to determine how close
1980 // we are to the threshold.
1981 // * A `MinNominatorBond` and `MinValidatorBond` which is the final condition checked to
1982 // determine this is a person that should be chilled because they have not met the
1983 // threshold bond required.
1984 //
1985 // Otherwise, if caller is the same as the controller, this is just like `chill`.
1986
1987 if Nominators::<T>::contains_key(&stash) && Nominators::<T>::get(&stash).is_none() {
1988 Self::chill_stash(&stash);
1989 return Ok(());
1990 }
1991
1992 if caller != controller {
1993 let threshold = ChillThreshold::<T>::get().ok_or(Error::<T>::CannotChillOther)?;
1994 let min_active_bond = if Nominators::<T>::contains_key(&stash) {
1995 let max_nominator_count =
1996 MaxNominatorsCount::<T>::get().ok_or(Error::<T>::CannotChillOther)?;
1997 let current_nominator_count = Nominators::<T>::count();
1998 ensure!(
1999 threshold * max_nominator_count < current_nominator_count,
2000 Error::<T>::CannotChillOther
2001 );
2002 MinNominatorBond::<T>::get()
2003 } else if Validators::<T>::contains_key(&stash) {
2004 let max_validator_count =
2005 MaxValidatorsCount::<T>::get().ok_or(Error::<T>::CannotChillOther)?;
2006 let current_validator_count = Validators::<T>::count();
2007 ensure!(
2008 threshold * max_validator_count < current_validator_count,
2009 Error::<T>::CannotChillOther
2010 );
2011 MinValidatorBond::<T>::get()
2012 } else {
2013 Zero::zero()
2014 };
2015
2016 ensure!(ledger.active < min_active_bond, Error::<T>::CannotChillOther);
2017 }
2018
2019 Self::chill_stash(&stash);
2020 Ok(())
2021 }
2022
2023 /// Force a validator to have at least the minimum commission. This will not affect a
2024 /// validator who already has a commission greater than or equal to the minimum. Any account
2025 /// can call this.
2026 #[pezpallet::call_index(24)]
2027 #[pezpallet::weight(T::WeightInfo::force_apply_min_commission())]
2028 pub fn force_apply_min_commission(
2029 origin: OriginFor<T>,
2030 validator_stash: T::AccountId,
2031 ) -> DispatchResult {
2032 ensure_signed(origin)?;
2033 let min_commission = MinCommission::<T>::get();
2034 Validators::<T>::try_mutate_exists(validator_stash, |maybe_prefs| {
2035 maybe_prefs
2036 .as_mut()
2037 .map(|prefs| {
2038 (prefs.commission < min_commission)
2039 .then(|| prefs.commission = min_commission)
2040 })
2041 .ok_or(Error::<T>::NotStash)
2042 })?;
2043 Ok(())
2044 }
2045
2046 /// Sets the minimum amount of commission that each validators must maintain.
2047 ///
2048 /// This call has lower privilege requirements than `set_staking_config` and can be called
2049 /// by the `T::AdminOrigin`. Root can always call this.
2050 #[pezpallet::call_index(25)]
2051 #[pezpallet::weight(T::WeightInfo::set_min_commission())]
2052 pub fn set_min_commission(origin: OriginFor<T>, new: Perbill) -> DispatchResult {
2053 T::AdminOrigin::ensure_origin(origin)?;
2054 MinCommission::<T>::put(new);
2055 Ok(())
2056 }
2057
2058 /// Pay out a page of the stakers behind a validator for the given era and page.
2059 ///
2060 /// - `validator_stash` is the stash account of the validator.
2061 /// - `era` may be any era between `[current_era - history_depth; current_era]`.
2062 /// - `page` is the page index of nominators to pay out with value between 0 and
2063 /// `num_nominators / T::MaxExposurePageSize`.
2064 ///
2065 /// The origin of this call must be _Signed_. Any account can call this function, even if
2066 /// it is not one of the stakers.
2067 ///
2068 /// If a validator has more than [`Config::MaxExposurePageSize`] nominators backing
2069 /// them, then the list of nominators is paged, with each page being capped at
2070 /// [`Config::MaxExposurePageSize`.] If a validator has more than one page of nominators,
2071 /// the call needs to be made for each page separately in order for all the nominators
2072 /// backing a validator to receive the reward. The nominators are not sorted across pages
2073 /// and so it should not be assumed the highest staker would be on the topmost page and vice
2074 /// versa. If rewards are not claimed in [`Config::HistoryDepth`] eras, they are lost.
2075 #[pezpallet::call_index(26)]
2076 #[pezpallet::weight(T::WeightInfo::payout_stakers_alive_staked(
2077 T::MaxExposurePageSize::get()
2078 ))]
2079 pub fn payout_stakers_by_page(
2080 origin: OriginFor<T>,
2081 validator_stash: T::AccountId,
2082 era: EraIndex,
2083 page: Page,
2084 ) -> DispatchResultWithPostInfo {
2085 ensure_signed(origin)?;
2086 Self::do_payout_stakers_by_page(validator_stash, era, page)
2087 }
2088
2089 /// Migrates an account's `RewardDestination::Controller` to
2090 /// `RewardDestination::Account(controller)`.
2091 ///
2092 /// Effects will be felt instantly (as soon as this function is completed successfully).
2093 ///
2094 /// This will waive the transaction fee if the `payee` is successfully migrated.
2095 #[pezpallet::call_index(27)]
2096 #[pezpallet::weight(T::WeightInfo::update_payee())]
2097 pub fn update_payee(
2098 origin: OriginFor<T>,
2099 controller: T::AccountId,
2100 ) -> DispatchResultWithPostInfo {
2101 ensure_signed(origin)?;
2102 let ledger = Self::ledger(StakingAccount::Controller(controller.clone()))?;
2103
2104 ensure!(
2105 (Payee::<T>::get(&ledger.stash) == {
2106 #[allow(deprecated)]
2107 Some(RewardDestination::Controller)
2108 }),
2109 Error::<T>::NotController
2110 );
2111
2112 ledger
2113 .set_payee(RewardDestination::Account(controller))
2114 .defensive_proof("ledger should have been previously retrieved from storage.")?;
2115
2116 Ok(Pays::No.into())
2117 }
2118
2119 /// Updates a batch of controller accounts to their corresponding stash account if they are
2120 /// not the same. Ignores any controller accounts that do not exist, and does not operate if
2121 /// the stash and controller are already the same.
2122 ///
2123 /// Effects will be felt instantly (as soon as this function is completed successfully).
2124 ///
2125 /// The dispatch origin must be `T::AdminOrigin`.
2126 #[pezpallet::call_index(28)]
2127 #[pezpallet::weight(T::WeightInfo::deprecate_controller_batch(controllers.len() as u32))]
2128 pub fn deprecate_controller_batch(
2129 origin: OriginFor<T>,
2130 controllers: BoundedVec<T::AccountId, T::MaxControllersInDeprecationBatch>,
2131 ) -> DispatchResultWithPostInfo {
2132 T::AdminOrigin::ensure_origin(origin)?;
2133
2134 // Ignore controllers that do not exist or are already the same as stash.
2135 let filtered_batch_with_ledger: Vec<_> = controllers
2136 .iter()
2137 .filter_map(|controller| {
2138 let ledger = Self::ledger(StakingAccount::Controller(controller.clone()));
2139 ledger.ok().map_or(None, |ledger| {
2140 // If the controller `RewardDestination` is still the deprecated
2141 // `Controller` variant, skip deprecating this account.
2142 let payee_deprecated = Payee::<T>::get(&ledger.stash) == {
2143 #[allow(deprecated)]
2144 Some(RewardDestination::Controller)
2145 };
2146
2147 if ledger.stash != *controller && !payee_deprecated {
2148 Some(ledger)
2149 } else {
2150 None
2151 }
2152 })
2153 })
2154 .collect();
2155
2156 // Update unique pairs.
2157 let mut failures = 0;
2158 for ledger in filtered_batch_with_ledger {
2159 let _ = ledger.clone().set_controller_to_stash().map_err(|_| failures += 1);
2160 }
2161 Self::deposit_event(Event::<T>::ControllerBatchDeprecated { failures });
2162
2163 Ok(Some(T::WeightInfo::deprecate_controller_batch(controllers.len() as u32)).into())
2164 }
2165
2166 /// Restores the state of a ledger which is in an inconsistent state.
2167 ///
2168 /// The requirements to restore a ledger are the following:
2169 /// * The stash is bonded; or
2170 /// * The stash is not bonded but it has a staking lock left behind; or
2171 /// * If the stash has an associated ledger and its state is inconsistent; or
2172 /// * If the ledger is not corrupted *but* its staking lock is out of sync.
2173 ///
2174 /// The `maybe_*` input parameters will overwrite the corresponding data and metadata of the
2175 /// ledger associated with the stash. If the input parameters are not set, the ledger will
2176 /// be reset values from on-chain state.
2177 #[pezpallet::call_index(29)]
2178 #[pezpallet::weight(T::WeightInfo::restore_ledger())]
2179 pub fn restore_ledger(
2180 origin: OriginFor<T>,
2181 stash: T::AccountId,
2182 maybe_controller: Option<T::AccountId>,
2183 maybe_total: Option<BalanceOf<T>>,
2184 maybe_unlocking: Option<BoundedVec<UnlockChunk<BalanceOf<T>>, T::MaxUnlockingChunks>>,
2185 ) -> DispatchResult {
2186 T::AdminOrigin::ensure_origin(origin)?;
2187
2188 // cannot restore ledger for virtual stakers.
2189 ensure!(!Self::is_virtual_staker(&stash), Error::<T>::VirtualStakerNotAllowed);
2190
2191 let current_lock = asset::staked::<T>(&stash);
2192 let stash_balance = asset::stakeable_balance::<T>(&stash);
2193
2194 let (new_controller, new_total) = match Self::inspect_bond_state(&stash) {
2195 Ok(LedgerIntegrityState::Corrupted) => {
2196 let new_controller = maybe_controller.unwrap_or(stash.clone());
2197
2198 let new_total = if let Some(total) = maybe_total {
2199 let new_total = total.min(stash_balance);
2200 // enforce hold == ledger.amount.
2201 asset::update_stake::<T>(&stash, new_total)?;
2202 new_total
2203 } else {
2204 current_lock
2205 };
2206
2207 Ok((new_controller, new_total))
2208 },
2209 Ok(LedgerIntegrityState::CorruptedKilled) => {
2210 if current_lock == Zero::zero() {
2211 // this case needs to restore both lock and ledger, so the new total needs
2212 // to be given by the called since there's no way to restore the total
2213 // on-chain.
2214 ensure!(maybe_total.is_some(), Error::<T>::CannotRestoreLedger);
2215 Ok((
2216 stash.clone(),
2217 maybe_total.expect("total exists as per the check above; qed."),
2218 ))
2219 } else {
2220 Ok((stash.clone(), current_lock))
2221 }
2222 },
2223 Ok(LedgerIntegrityState::LockCorrupted) => {
2224 // ledger is not corrupted but its locks are out of sync. In this case, we need
2225 // to enforce a new ledger.total and staking lock for this stash.
2226 let new_total =
2227 maybe_total.ok_or(Error::<T>::CannotRestoreLedger)?.min(stash_balance);
2228 asset::update_stake::<T>(&stash, new_total)?;
2229
2230 Ok((stash.clone(), new_total))
2231 },
2232 Err(Error::<T>::BadState) => {
2233 // the stash and ledger do not exist but lock is lingering.
2234 asset::kill_stake::<T>(&stash)?;
2235 ensure!(
2236 Self::inspect_bond_state(&stash) == Err(Error::<T>::NotStash),
2237 Error::<T>::BadState
2238 );
2239
2240 return Ok(());
2241 },
2242 Ok(LedgerIntegrityState::Ok) | Err(_) => Err(Error::<T>::CannotRestoreLedger),
2243 }?;
2244
2245 // re-bond stash and controller tuple.
2246 Bonded::<T>::insert(&stash, &new_controller);
2247
2248 // resoter ledger state.
2249 let mut ledger = StakingLedger::<T>::new(stash.clone(), new_total);
2250 ledger.controller = Some(new_controller);
2251 ledger.unlocking = maybe_unlocking.unwrap_or_default();
2252 ledger.update()?;
2253
2254 ensure!(
2255 Self::inspect_bond_state(&stash) == Ok(LedgerIntegrityState::Ok),
2256 Error::<T>::BadState
2257 );
2258 Ok(())
2259 }
2260
2261 /// Removes the legacy Staking locks if they exist.
2262 ///
2263 /// This removes the legacy lock on the stake with [`Config::OldCurrency`] and creates a
2264 /// hold on it if needed. If all stake cannot be held, the best effort is made to hold as
2265 /// much as possible. The remaining stake is forced withdrawn from the ledger.
2266 ///
2267 /// The fee is waived if the migration is successful.
2268 #[pezpallet::call_index(30)]
2269 #[pezpallet::weight(T::WeightInfo::migrate_currency())]
2270 pub fn migrate_currency(
2271 origin: OriginFor<T>,
2272 stash: T::AccountId,
2273 ) -> DispatchResultWithPostInfo {
2274 ensure_signed(origin)?;
2275 Self::do_migrate_currency(&stash)?;
2276
2277 // Refund the transaction fee if successful.
2278 Ok(Pays::No.into())
2279 }
2280
2281 /// This function allows governance to manually slash a validator and is a
2282 /// **fallback mechanism**.
2283 ///
2284 /// The dispatch origin must be `T::AdminOrigin`.
2285 ///
2286 /// ## Parameters
2287 /// - `validator_stash` - The stash account of the validator to slash.
2288 /// - `era` - The era in which the validator was in the active set.
2289 /// - `slash_fraction` - The percentage of the stake to slash, expressed as a Perbill.
2290 ///
2291 /// ## Behavior
2292 ///
2293 /// The slash will be applied using the standard slashing mechanics, respecting the
2294 /// configured `SlashDeferDuration`.
2295 ///
2296 /// This means:
2297 /// - If the validator was already slashed by a higher percentage for the same era, this
2298 /// slash will have no additional effect.
2299 /// - If the validator was previously slashed by a lower percentage, only the difference
2300 /// will be applied.
2301 /// - The slash will be deferred by `SlashDeferDuration` eras before being enacted.
2302 #[pezpallet::call_index(33)]
2303 #[pezpallet::weight(T::WeightInfo::manual_slash())]
2304 pub fn manual_slash(
2305 origin: OriginFor<T>,
2306 validator_stash: T::AccountId,
2307 era: EraIndex,
2308 slash_fraction: Perbill,
2309 ) -> DispatchResult {
2310 T::AdminOrigin::ensure_origin(origin)?;
2311
2312 // Check era is valid
2313 let current_era = CurrentEra::<T>::get().ok_or(Error::<T>::InvalidEraToReward)?;
2314 let history_depth = T::HistoryDepth::get();
2315 ensure!(
2316 era <= current_era && era >= current_era.saturating_sub(history_depth),
2317 Error::<T>::InvalidEraToReward
2318 );
2319
2320 let offence_details = pezsp_staking::offence::OffenceDetails {
2321 offender: validator_stash.clone(),
2322 reporters: Vec::new(),
2323 };
2324
2325 // Get the session index for the era
2326 let session_index =
2327 ErasStartSessionIndex::<T>::get(era).ok_or(Error::<T>::InvalidEraToReward)?;
2328
2329 // Create the offence and report it through on_offence system
2330 let _ = Self::on_offence(
2331 core::iter::once(offence_details),
2332 &[slash_fraction],
2333 session_index,
2334 );
2335
2336 Ok(())
2337 }
2338 }
2339}
2340
2341/// Check that list is sorted and has no duplicates.
2342fn is_sorted_and_unique(list: &[u32]) -> bool {
2343 list.windows(2).all(|w| w[0] < w[1])
2344}