1#![cfg_attr(not(feature = "std"), no_std)]
51#![recursion_limit = "256"]
52
53#[cfg(feature = "runtime-benchmarks")]
54pub mod benchmarking;
55#[cfg(any(feature = "runtime-benchmarks", test))]
56pub mod testing_utils;
57
58#[cfg(test)]
59pub(crate) mod mock;
60#[cfg(test)]
61mod tests;
62
63pub mod asset;
64pub mod election_size_tracker;
65pub mod ledger;
66mod pallet;
67pub mod session_rotation;
68pub mod slashing;
69pub mod weights;
70
71extern crate alloc;
72use alloc::{vec, vec::Vec};
73use codec::{Decode, DecodeWithMemTracking, Encode, HasCompact, MaxEncodedLen};
74use frame_election_provider_support::ElectionProvider;
75use frame_support::{
76 traits::{
77 tokens::fungible::{Credit, Debt},
78 ConstU32, Contains, Get, LockIdentifier,
79 },
80 BoundedVec, DebugNoBound, DefaultNoBound, EqNoBound, PartialEqNoBound, RuntimeDebugNoBound,
81 WeakBoundedVec,
82};
83use frame_system::pallet_prelude::BlockNumberFor;
84use ledger::LedgerIntegrityState;
85use scale_info::TypeInfo;
86use sp_runtime::{
87 traits::{AtLeast32BitUnsigned, One, StaticLookup, UniqueSaturatedInto},
88 BoundedBTreeMap, Perbill, RuntimeDebug, Saturating,
89};
90use sp_staking::{EraIndex, ExposurePage, PagedExposureMetadata, SessionIndex};
91pub use sp_staking::{Exposure, IndividualExposure, StakerStatus};
92pub use weights::WeightInfo;
93
94pub use ledger::{StakingLedger, UnlockChunk};
96pub use pallet::{pallet::*, UseNominatorsAndValidatorsMap, UseValidatorsMap};
97
98pub(crate) const STAKING_ID: LockIdentifier = *b"staking ";
99pub(crate) const LOG_TARGET: &str = "runtime::staking-async";
100
101#[macro_export]
103macro_rules! log {
104 ($level:tt, $patter:expr $(, $values:expr)* $(,)?) => {
105 log::$level!(
106 target: crate::LOG_TARGET,
107 concat!("[{:?}] 💸 ", $patter), <frame_system::Pallet<T>>::block_number() $(, $values)*
108 )
109 };
110}
111
112pub type BoundedExposuresOf<T> = BoundedVec<
115 (
116 <T as frame_system::Config>::AccountId,
117 Exposure<<T as frame_system::Config>::AccountId, BalanceOf<T>>,
118 ),
119 MaxWinnersPerPageOf<<T as Config>::ElectionProvider>,
120>;
121
122pub type MaxWinnersOf<T> = <T as Config>::MaxValidatorSet;
125
126pub type MaxWinnersPerPageOf<P> = <P as ElectionProvider>::MaxWinnersPerPage;
128
129pub type MaxNominationsOf<T> =
131 <<T as Config>::NominationsQuota as NominationsQuota<BalanceOf<T>>>::MaxNominations;
132
133pub type RewardPoint = u32;
135
136pub type BalanceOf<T> = <T as Config>::CurrencyBalance;
138
139type PositiveImbalanceOf<T> = Debt<<T as frame_system::Config>::AccountId, <T as Config>::Currency>;
140pub type NegativeImbalanceOf<T> =
141 Credit<<T as frame_system::Config>::AccountId, <T as Config>::Currency>;
142
143type AccountIdLookupOf<T> = <<T as frame_system::Config>::Lookup as StaticLookup>::Source;
144
145#[derive(Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen, PartialEq, Eq, Clone)]
147pub struct ActiveEraInfo {
148 pub index: EraIndex,
150 pub start: Option<u64>,
155}
156
157#[derive(
161 PartialEqNoBound, Encode, Decode, DebugNoBound, TypeInfo, MaxEncodedLen, DefaultNoBound,
162)]
163#[codec(mel_bound())]
164#[scale_info(skip_type_params(T))]
165pub struct EraRewardPoints<T: Config> {
166 pub total: RewardPoint,
168 pub individual: BoundedBTreeMap<T::AccountId, RewardPoint, T::MaxValidatorSet>,
170}
171
172#[derive(
174 PartialEq,
175 Eq,
176 Copy,
177 Clone,
178 Encode,
179 Decode,
180 DecodeWithMemTracking,
181 RuntimeDebug,
182 TypeInfo,
183 MaxEncodedLen,
184)]
185pub enum RewardDestination<AccountId> {
186 Staked,
188 Stash,
190 #[deprecated(
191 note = "`Controller` will be removed after January 2024. Use `Account(controller)` instead."
192 )]
193 Controller,
194 Account(AccountId),
196 None,
198}
199
200#[derive(
202 PartialEq,
203 Eq,
204 Clone,
205 Encode,
206 Decode,
207 DecodeWithMemTracking,
208 RuntimeDebug,
209 TypeInfo,
210 Default,
211 MaxEncodedLen,
212)]
213pub struct ValidatorPrefs {
214 #[codec(compact)]
217 pub commission: Perbill,
218 pub blocked: bool,
222}
223
224#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen, Default)]
226pub enum SnapshotStatus<AccountId> {
227 Ongoing(AccountId),
229 Consumed,
231 #[default]
233 Waiting,
234}
235
236#[derive(
238 PartialEqNoBound, EqNoBound, Clone, Encode, Decode, RuntimeDebugNoBound, TypeInfo, MaxEncodedLen,
239)]
240#[codec(mel_bound())]
241#[scale_info(skip_type_params(T))]
242pub struct Nominations<T: Config> {
243 pub targets: BoundedVec<T::AccountId, MaxNominationsOf<T>>,
245 pub submitted_in: EraIndex,
249 pub suppressed: bool,
254}
255
256#[derive(Encode, Decode, RuntimeDebug, TypeInfo, PartialEq, Eq)]
261pub struct PagedExposure<AccountId, Balance: HasCompact + codec::MaxEncodedLen> {
262 exposure_metadata: PagedExposureMetadata<Balance>,
263 exposure_page: ExposurePage<AccountId, Balance>,
264}
265
266impl<AccountId, Balance: HasCompact + Copy + AtLeast32BitUnsigned + codec::MaxEncodedLen>
267 PagedExposure<AccountId, Balance>
268{
269 pub fn from_clipped(exposure: Exposure<AccountId, Balance>) -> Self {
271 Self {
272 exposure_metadata: PagedExposureMetadata {
273 total: exposure.total,
274 own: exposure.own,
275 nominator_count: exposure.others.len() as u32,
276 page_count: 1,
277 },
278 exposure_page: ExposurePage { page_total: exposure.total, others: exposure.others },
279 }
280 }
281
282 pub fn total(&self) -> Balance {
284 self.exposure_metadata.total
285 }
286
287 pub fn page_total(&self) -> Balance {
289 self.exposure_page.page_total + self.exposure_metadata.own
290 }
291
292 pub fn own(&self) -> Balance {
294 self.exposure_metadata.own
295 }
296
297 pub fn others(&self) -> &Vec<IndividualExposure<AccountId, Balance>> {
299 &self.exposure_page.others
300 }
301}
302
303#[derive(Encode, Decode, RuntimeDebugNoBound, TypeInfo, MaxEncodedLen, PartialEqNoBound)]
306#[scale_info(skip_type_params(T))]
307pub struct UnappliedSlash<T: Config> {
308 pub validator: T::AccountId,
310 pub own: BalanceOf<T>,
312 pub others: WeakBoundedVec<(T::AccountId, BalanceOf<T>), T::MaxExposurePageSize>,
314 pub reporter: Option<T::AccountId>,
316 pub payout: BalanceOf<T>,
318}
319
320pub trait NominationsQuota<Balance> {
326 type MaxNominations: Get<u32>;
329
330 fn get_quota(balance: Balance) -> u32 {
333 Self::curve(balance).clamp(1, Self::MaxNominations::get())
334 }
335
336 fn curve(balance: Balance) -> u32;
338}
339
340pub struct FixedNominationsQuota<const MAX: u32>;
342impl<Balance, const MAX: u32> NominationsQuota<Balance> for FixedNominationsQuota<MAX> {
343 type MaxNominations = ConstU32<MAX>;
344
345 fn curve(_: Balance) -> u32 {
346 MAX
347 }
348}
349
350pub trait EraPayout<Balance> {
352 fn era_payout(
357 total_staked: Balance,
358 total_issuance: Balance,
359 era_duration_millis: u64,
360 ) -> (Balance, Balance);
361}
362
363impl<Balance: Default> EraPayout<Balance> for () {
364 fn era_payout(
365 _total_staked: Balance,
366 _total_issuance: Balance,
367 _era_duration_millis: u64,
368 ) -> (Balance, Balance) {
369 (Default::default(), Default::default())
370 }
371}
372
373#[derive(
375 Copy,
376 Clone,
377 PartialEq,
378 Eq,
379 Encode,
380 Decode,
381 DecodeWithMemTracking,
382 RuntimeDebug,
383 TypeInfo,
384 MaxEncodedLen,
385 serde::Serialize,
386 serde::Deserialize,
387)]
388pub enum Forcing {
389 NotForcing,
391 ForceNew,
395 ForceNone,
397 ForceAlways,
399}
400
401impl Default for Forcing {
402 fn default() -> Self {
403 Forcing::NotForcing
404 }
405}
406
407pub struct AllStakers<T: Config>(core::marker::PhantomData<T>);
415
416impl<T: Config> Contains<T::AccountId> for AllStakers<T> {
417 fn contains(account: &T::AccountId) -> bool {
423 Ledger::<T>::contains_key(account)
424 }
425}
426
427pub struct PlanningEraOffsetOf<T, RS, S>(core::marker::PhantomData<(T, RS, S)>);
436impl<T: Config, RS: Get<BlockNumberFor<T>>, S: Get<BlockNumberFor<T>>> Get<SessionIndex>
437 for PlanningEraOffsetOf<T, RS, S>
438{
439 fn get() -> SessionIndex {
440 let election_duration = <T::ElectionProvider as ElectionProvider>::duration_with_export();
441 let sessions_needed = (election_duration + S::get()) / RS::get();
442 sessions_needed
445 .saturating_add(One::one())
446 .saturating_add(One::one())
447 .unique_saturated_into()
448 }
449}