#![cfg_attr(not(feature = "std"), no_std)]
#![recursion_limit = "256"]
#[cfg(feature = "runtime-benchmarks")]
pub mod benchmarking;
#[cfg(any(feature = "runtime-benchmarks", test))]
pub mod testing_utils;
#[cfg(test)]
pub(crate) mod mock;
#[cfg(test)]
mod tests;
pub mod asset;
pub mod election_size_tracker;
pub mod ledger;
mod pallet;
pub mod session_rotation;
pub mod slashing;
pub mod weights;
extern crate alloc;
use alloc::{vec, vec::Vec};
use codec::{Decode, DecodeWithMemTracking, Encode, HasCompact, MaxEncodedLen};
use frame_election_provider_support::ElectionProvider;
use frame_support::{
traits::{
tokens::fungible::{Credit, Debt},
ConstU32, Contains, Get, LockIdentifier,
},
BoundedVec, DebugNoBound, DefaultNoBound, EqNoBound, PartialEqNoBound, WeakBoundedVec,
};
use frame_system::pallet_prelude::BlockNumberFor;
use ledger::LedgerIntegrityState;
use scale_info::TypeInfo;
use sp_runtime::{
traits::{AtLeast32BitUnsigned, One, StaticLookup, UniqueSaturatedInto},
BoundedBTreeMap, Debug, Perbill, Saturating,
};
use sp_staking::{EraIndex, ExposurePage, PagedExposureMetadata, SessionIndex};
pub use sp_staking::{Exposure, IndividualExposure, StakerStatus};
pub use weights::WeightInfo;
pub use ledger::{StakingLedger, UnlockChunk};
pub use pallet::{pallet::*, UseNominatorsAndValidatorsMap, UseValidatorsMap};
pub(crate) const STAKING_ID: LockIdentifier = *b"staking ";
pub(crate) const LOG_TARGET: &str = "runtime::staking-async";
#[macro_export]
macro_rules! log {
($level:tt, $patter:expr $(, $values:expr)* $(,)?) => {
log::$level!(
target: crate::LOG_TARGET,
concat!("[{:?}] 💸 ", $patter), <frame_system::Pallet<T>>::block_number() $(, $values)*
)
};
}
pub type BoundedExposuresOf<T> = BoundedVec<
(
<T as frame_system::Config>::AccountId,
Exposure<<T as frame_system::Config>::AccountId, BalanceOf<T>>,
),
MaxWinnersPerPageOf<<T as Config>::ElectionProvider>,
>;
pub type MaxWinnersOf<T> = <T as Config>::MaxValidatorSet;
pub type MaxWinnersPerPageOf<P> = <P as ElectionProvider>::MaxWinnersPerPage;
pub type MaxNominationsOf<T> =
<<T as Config>::NominationsQuota as NominationsQuota<BalanceOf<T>>>::MaxNominations;
pub type RewardPoint = u32;
pub type BalanceOf<T> = <T as Config>::CurrencyBalance;
type PositiveImbalanceOf<T> = Debt<<T as frame_system::Config>::AccountId, <T as Config>::Currency>;
pub type NegativeImbalanceOf<T> =
Credit<<T as frame_system::Config>::AccountId, <T as Config>::Currency>;
type AccountIdLookupOf<T> = <<T as frame_system::Config>::Lookup as StaticLookup>::Source;
#[derive(Encode, Decode, Debug, TypeInfo, MaxEncodedLen, PartialEq, Eq, Clone)]
pub struct ActiveEraInfo {
pub index: EraIndex,
pub start: Option<u64>,
}
#[derive(
PartialEqNoBound, Encode, Decode, DebugNoBound, TypeInfo, MaxEncodedLen, DefaultNoBound,
)]
#[codec(mel_bound())]
#[scale_info(skip_type_params(T))]
pub struct EraRewardPoints<T: Config> {
pub total: RewardPoint,
pub individual: BoundedBTreeMap<T::AccountId, RewardPoint, T::MaxValidatorSet>,
}
#[derive(
PartialEq,
Eq,
Copy,
Clone,
Encode,
Decode,
DecodeWithMemTracking,
Debug,
TypeInfo,
MaxEncodedLen,
)]
pub enum RewardDestination<AccountId> {
Staked,
Stash,
#[deprecated(
note = "`Controller` will be removed after January 2024. Use `Account(controller)` instead."
)]
Controller,
Account(AccountId),
None,
}
#[derive(
PartialEq,
Eq,
Clone,
Encode,
Decode,
DecodeWithMemTracking,
Debug,
TypeInfo,
Default,
MaxEncodedLen,
)]
pub struct ValidatorPrefs {
#[codec(compact)]
pub commission: Perbill,
pub blocked: bool,
}
#[derive(PartialEq, Eq, Clone, Encode, Decode, Debug, TypeInfo, MaxEncodedLen, Default)]
pub enum SnapshotStatus<AccountId> {
Ongoing(AccountId),
Consumed,
#[default]
Waiting,
}
#[derive(
PartialEqNoBound, EqNoBound, Clone, Encode, Decode, DebugNoBound, TypeInfo, MaxEncodedLen,
)]
#[codec(mel_bound())]
#[scale_info(skip_type_params(T))]
pub struct Nominations<T: Config> {
pub targets: BoundedVec<T::AccountId, MaxNominationsOf<T>>,
pub submitted_in: EraIndex,
pub suppressed: bool,
}
#[derive(Encode, Decode, Debug, TypeInfo, PartialEq, Eq)]
pub struct PagedExposure<AccountId, Balance: HasCompact + codec::MaxEncodedLen> {
exposure_metadata: PagedExposureMetadata<Balance>,
exposure_page: ExposurePage<AccountId, Balance>,
}
impl<AccountId, Balance: HasCompact + Copy + AtLeast32BitUnsigned + codec::MaxEncodedLen>
PagedExposure<AccountId, Balance>
{
pub fn from_clipped(exposure: Exposure<AccountId, Balance>) -> Self {
Self {
exposure_metadata: PagedExposureMetadata {
total: exposure.total,
own: exposure.own,
nominator_count: exposure.others.len() as u32,
page_count: 1,
},
exposure_page: ExposurePage { page_total: exposure.total, others: exposure.others },
}
}
pub fn from_overview(overview: PagedExposureMetadata<Balance>) -> Self {
Self {
exposure_metadata: PagedExposureMetadata {
total: overview.total,
own: overview.own,
nominator_count: overview.nominator_count,
page_count: 1,
},
exposure_page: ExposurePage { page_total: overview.total, others: vec![] },
}
}
pub fn total(&self) -> Balance {
self.exposure_metadata.total
}
pub fn page_total(&self) -> Balance {
self.exposure_page.page_total + self.exposure_metadata.own
}
pub fn own(&self) -> Balance {
self.exposure_metadata.own
}
pub fn others(&self) -> &Vec<IndividualExposure<AccountId, Balance>> {
&self.exposure_page.others
}
}
#[derive(Encode, Decode, DebugNoBound, TypeInfo, MaxEncodedLen, PartialEqNoBound, EqNoBound)]
#[scale_info(skip_type_params(T))]
pub struct UnappliedSlash<T: Config> {
pub validator: T::AccountId,
pub own: BalanceOf<T>,
pub others: WeakBoundedVec<(T::AccountId, BalanceOf<T>), T::MaxExposurePageSize>,
pub reporter: Option<T::AccountId>,
pub payout: BalanceOf<T>,
}
pub trait NominationsQuota<Balance> {
type MaxNominations: Get<u32>;
fn get_quota(balance: Balance) -> u32 {
Self::curve(balance).clamp(1, Self::MaxNominations::get())
}
fn curve(balance: Balance) -> u32;
}
pub struct FixedNominationsQuota<const MAX: u32>;
impl<Balance, const MAX: u32> NominationsQuota<Balance> for FixedNominationsQuota<MAX> {
type MaxNominations = ConstU32<MAX>;
fn curve(_: Balance) -> u32 {
MAX
}
}
pub trait EraPayout<Balance> {
fn era_payout(
total_staked: Balance,
total_issuance: Balance,
era_duration_millis: u64,
) -> (Balance, Balance);
}
impl<Balance: Default> EraPayout<Balance> for () {
fn era_payout(
_total_staked: Balance,
_total_issuance: Balance,
_era_duration_millis: u64,
) -> (Balance, Balance) {
(Default::default(), Default::default())
}
}
#[derive(
Copy,
Clone,
PartialEq,
Eq,
Encode,
Decode,
DecodeWithMemTracking,
Debug,
TypeInfo,
MaxEncodedLen,
serde::Serialize,
serde::Deserialize,
)]
pub enum Forcing {
NotForcing,
ForceNew,
ForceNone,
ForceAlways,
}
impl Default for Forcing {
fn default() -> Self {
Forcing::NotForcing
}
}
pub struct AllStakers<T: Config>(core::marker::PhantomData<T>);
impl<T: Config> Contains<T::AccountId> for AllStakers<T> {
fn contains(account: &T::AccountId) -> bool {
Ledger::<T>::contains_key(account)
}
}
pub struct PlanningEraOffsetOf<T, RS, S>(core::marker::PhantomData<(T, RS, S)>);
impl<T: Config, RS: Get<BlockNumberFor<T>>, S: Get<BlockNumberFor<T>>> Get<SessionIndex>
for PlanningEraOffsetOf<T, RS, S>
{
fn get() -> SessionIndex {
let election_duration = <T::ElectionProvider as ElectionProvider>::duration_with_export();
let sessions_needed = (election_duration + S::get()) / RS::get();
sessions_needed
.saturating_add(One::one())
.saturating_add(One::one())
.unique_saturated_into()
}
}