timechain_runtime/configs/
staking.rsuse scale_codec::Decode;
use crate::System;
use polkadot_sdk::*;
use frame_election_provider_support::{
bounds::{ElectionBounds, ElectionBoundsBuilder},
onchain, BalancingConfig, ElectionDataProvider, SequentialPhragmen, VoteWeight,
};
use frame_support::{
dispatch::DispatchClass,
pallet_prelude::Get,
parameter_types,
traits::fungible::Balanced,
traits::{tokens::Preservation, ConstU32, Imbalance, OnUnbalanced},
weights::Weight,
PalletId,
};
use sp_runtime::{
curve::PiecewiseLinear, traits::AccountIdConversion, transaction_validity::TransactionPriority,
FixedU128, Perbill, Percent,
};
use sp_std::prelude::*;
use pallet_election_provider_multi_phase::{GeometricDepositBase, SolutionAccuracyOf};
use time_primitives::BlockNumber;
use crate::{
deposit, weights, AccountId, Balance, Balances, BlockExecutionWeight, BondingDuration,
DefaultAdminOrigin, DelegatedStaking, ElectionProviderMultiPhase, EpochDuration,
NominationPools, Runtime, RuntimeBlockLength, RuntimeBlockWeights, RuntimeDebt, RuntimeEvent,
RuntimeFreezeReason, RuntimeHoldReason, Session, SessionsPerEra, Staking, Timestamp,
TransactionPayment, VoterList, ANLOG,
};
parameter_types! {
pub const SignedPhase: u32 = EpochDuration::get() as u32;
pub const UnsignedPhase: u32 = EpochDuration::get() as u32;
pub const SignedRewardBase: Balance = 100 * ANLOG;
pub const SignedFixedDeposit: Balance = 100 * ANLOG;
pub const SignedDepositIncreaseFactor: Percent = Percent::from_percent(10);
pub const SignedDepositByte: Balance = deposit(0, 1);
pub const StakingUnsignedPriority: TransactionPriority = TransactionPriority::MAX / 2;
pub const MultiPhaseUnsignedPriority: TransactionPriority = StakingUnsignedPriority::get() - 1u64;
pub MinerMaxWeight: Weight = RuntimeBlockWeights::get()
.get(DispatchClass::Normal)
.max_extrinsic.expect("Normal extrinsics have a weight limit configured; qed")
.saturating_sub(BlockExecutionWeight::get());
pub MinerMaxLength: u32 = Perbill::from_rational(9u32, 10) *
*RuntimeBlockLength::get()
.max
.get(DispatchClass::Normal);
}
frame_election_provider_support::generate_solution_type!(
#[compact]
pub struct NposSolution32::<
VoterIndex = u32,
TargetIndex = u16,
Accuracy = sp_runtime::PerU16,
MaxVoters = MaxElectingVotersSolution,
>(32)
);
parameter_types! {
pub ElectionBoundsMultiPhase: ElectionBounds = ElectionBoundsBuilder::default()
.voters_count(10_000.into()).targets_count(1_000.into()).build();
pub ElectionBoundsOnChain: ElectionBounds = ElectionBoundsBuilder::default()
.voters_count(5_000.into()).targets_count(1_000.into()).build();
pub MaxNominations: u32 = <NposSolution32 as frame_election_provider_support::NposSolution>::LIMIT as u32;
pub MaxElectingVotersSolution: u32 = 40_000;
pub MaxActiveValidators: u32 = 1000;
}
pub struct ElectionProviderBenchmarkConfig;
impl pallet_election_provider_multi_phase::BenchmarkingConfig for ElectionProviderBenchmarkConfig {
const VOTERS: [u32; 2] = [1000, 2000];
const TARGETS: [u32; 2] = [500, 1000];
const ACTIVE_VOTERS: [u32; 2] = [500, 800];
const DESIRED_TARGETS: [u32; 2] = [200, 400];
const SNAPSHOT_MAXIMUM_VOTERS: u32 = 1000;
const MINER_MAXIMUM_VOTERS: u32 = 1000;
const MAXIMUM_TARGETS: u32 = 300;
}
pub const MINER_MAX_ITERATIONS: u32 = 10;
pub struct OffchainRandomBalancing;
impl Get<Option<BalancingConfig>> for OffchainRandomBalancing {
fn get() -> Option<BalancingConfig> {
use sp_runtime::traits::TrailingZeroInput;
let iterations = match MINER_MAX_ITERATIONS {
0 => 0,
max => {
let seed = sp_io::offchain::random_seed();
let random = <u32>::decode(&mut TrailingZeroInput::new(&seed))
.expect("input is padded with zeroes; qed")
% max.saturating_add(1);
random as usize
},
};
let config = BalancingConfig { iterations, tolerance: 0 };
Some(config)
}
}
pub struct OnChainSeqPhragmen;
impl onchain::Config for OnChainSeqPhragmen {
type System = Runtime;
type Solver = SequentialPhragmen<
AccountId,
pallet_election_provider_multi_phase::SolutionAccuracyOf<Runtime>,
>;
type DataProvider = <Runtime as pallet_election_provider_multi_phase::Config>::DataProvider;
type WeightInfo = frame_election_provider_support::weights::SubstrateWeight<Runtime>;
type MaxWinners = <Runtime as pallet_election_provider_multi_phase::Config>::MaxWinners;
type Bounds = ElectionBoundsOnChain;
}
impl pallet_election_provider_multi_phase::MinerConfig for Runtime {
type AccountId = AccountId;
type MaxLength = MinerMaxLength;
type MaxWeight = MinerMaxWeight;
type Solution = NposSolution32;
type MaxVotesPerVoter =
<<Self as pallet_election_provider_multi_phase::Config>::DataProvider as ElectionDataProvider>::MaxVotesPerVoter;
type MaxWinners = ConstU32<100>;
fn solution_weight(v: u32, t: u32, a: u32, d: u32) -> Weight {
<
<Self as pallet_election_provider_multi_phase::Config>::WeightInfo
as
pallet_election_provider_multi_phase::WeightInfo
>::submit_unsigned(v, t, a, d)
}
}
impl pallet_election_provider_multi_phase::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type Currency = Balances;
type EstimateCallFee = TransactionPayment;
type SignedPhase = SignedPhase;
type UnsignedPhase = UnsignedPhase;
type BetterSignedThreshold = ();
type OffchainRepeat = OffchainRepeat;
type MinerTxPriority = MultiPhaseUnsignedPriority;
type MinerConfig = Self;
type SignedMaxSubmissions = ConstU32<10>;
type SignedRewardBase = SignedRewardBase;
type SignedDepositBase =
GeometricDepositBase<Balance, SignedFixedDeposit, SignedDepositIncreaseFactor>;
type SignedDepositByte = SignedDepositByte;
type SignedMaxRefunds = ConstU32<3>;
type SignedDepositWeight = ();
type SignedMaxWeight = MinerMaxWeight;
type SlashHandler = (); type RewardHandler = (); type DataProvider = Staking;
type Fallback = onchain::OnChainExecution<OnChainSeqPhragmen>;
type GovernanceFallback = onchain::OnChainExecution<OnChainSeqPhragmen>;
type Solver = SequentialPhragmen<AccountId, SolutionAccuracyOf<Self>, OffchainRandomBalancing>;
type ForceOrigin = DefaultAdminOrigin;
type MaxWinners = ConstU32<100>;
type ElectionBounds = ElectionBoundsMultiPhase;
type BenchmarkingConfig = ElectionProviderBenchmarkConfig;
type WeightInfo = pallet_election_provider_multi_phase::weights::SubstrateWeight<Self>;
}
pub struct RewardPool;
impl RewardPool {
fn account_id() -> AccountId {
PalletId(*b"timerwrd").into_account_truncating()
}
}
impl OnUnbalanced<RuntimeDebt> for RewardPool {
fn on_nonzero_unbalanced(amount: RuntimeDebt) {
if let Err(to_mint) =
Balances::settle(&Self::account_id(), amount, Preservation::Expendable)
{
log::warn!("💰 Reward pool drained, to be minted instead: {}", to_mint.peek());
}
}
}
pallet_staking_reward_curve::build! {
const REWARD_CURVE: PiecewiseLinear<'static> = curve!(
min_inflation: 0_010_000,
max_inflation: 0_040_000,
ideal_stake: 0_600_000,
falloff: 0_050_000,
max_piece_count: 40,
test_precision: 0_005_000,
);
}
parameter_types! {
pub const SlashDeferDuration: sp_staking::EraIndex = 2 * 7;
pub const RewardCurve: &'static PiecewiseLinear<'static> = &REWARD_CURVE;
pub const MaxControllersInDeprecationBatch: u32 = 4096;
pub OffchainRepeat: BlockNumber = 5;
pub HistoryDepth: u32 = 84;
}
const MAX_QUOTA_NOMINATIONS: u32 = 32;
pub struct StakingBenchmarkingConfig;
impl pallet_staking::BenchmarkingConfig for StakingBenchmarkingConfig {
type MaxNominators = ConstU32<1000>;
type MaxValidators = ConstU32<1000>;
}
impl pallet_staking::Config for Runtime {
type Currency = Balances;
type CurrencyBalance = Balance;
type UnixTime = Timestamp;
type CurrencyToVote = sp_staking::currency_to_vote::U128CurrencyToVote;
type RewardRemainder = ();
type RuntimeEvent = RuntimeEvent;
type Slash = (); type Reward = RewardPool;
type SessionsPerEra = SessionsPerEra;
type BondingDuration = BondingDuration;
type SlashDeferDuration = SlashDeferDuration;
type AdminOrigin = DefaultAdminOrigin;
type SessionInterface = Self;
type EraPayout = pallet_staking::ConvertCurve<RewardCurve>;
type MaxExposurePageSize = ConstU32<256>;
type NextNewSession = Session;
type ElectionProvider = ElectionProviderMultiPhase;
type GenesisElectionProvider = onchain::OnChainExecution<OnChainSeqPhragmen>;
type VoterList = VoterList;
type TargetList = pallet_staking::UseValidatorsMap<Self>;
type NominationsQuota = pallet_staking::FixedNominationsQuota<MAX_QUOTA_NOMINATIONS>;
type MaxUnlockingChunks = ConstU32<32>;
type HistoryDepth = HistoryDepth;
type MaxControllersInDeprecationBatch = MaxControllersInDeprecationBatch;
type BenchmarkingConfig = StakingBenchmarkingConfig;
type EventListeners = (NominationPools, DelegatedStaking);
type WeightInfo = pallet_staking::weights::SubstrateWeight<Runtime>;
type OldCurrency = Balances;
type RuntimeHoldReason = RuntimeHoldReason;
type Filter = pallet_nomination_pools::AllPoolMembers<Runtime>;
}
parameter_types! {
pub const BagThresholds: &'static [u64] = &crate::staking_bags::THRESHOLDS;
}
type VoterBagsListInstance = pallet_bags_list::Instance1;
impl pallet_bags_list::Config<VoterBagsListInstance> for Runtime {
type RuntimeEvent = RuntimeEvent;
type ScoreProvider = Staking;
type BagThresholds = BagThresholds;
type Score = VoteWeight;
type WeightInfo = weights::pallet_bags_list::WeightInfo<Runtime>;
}
impl pallet_offences::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type IdentificationTuple = pallet_session::historical::IdentificationTuple<Self>;
type OnOffenceHandler = Staking;
}
parameter_types! {
pub const PostUnbondPoolsWindow: u32 = 4;
pub const NominationPoolsPalletId: PalletId = PalletId(*b"timenmpl");
pub const MaxPointsToBalance: u8 = 10;
}
use sp_runtime::traits::Convert;
pub struct BalanceToU256;
impl Convert<Balance, sp_core::U256> for BalanceToU256 {
fn convert(balance: Balance) -> sp_core::U256 {
sp_core::U256::from(balance)
}
}
pub struct U256ToBalance;
impl Convert<sp_core::U256, Balance> for U256ToBalance {
fn convert(n: sp_core::U256) -> Balance {
n.try_into().unwrap_or(Balance::MAX)
}
}
impl pallet_nomination_pools::Config for Runtime {
type WeightInfo = pallet_nomination_pools::weights::SubstrateWeight<Runtime>;
type RuntimeEvent = RuntimeEvent;
type Currency = Balances;
type RuntimeFreezeReason = RuntimeFreezeReason;
type RewardCounter = FixedU128;
type BalanceToU256 = BalanceToU256;
type U256ToBalance = U256ToBalance;
type StakeAdapter =
pallet_nomination_pools::adapter::DelegateStake<Self, Staking, DelegatedStaking>;
type PostUnbondingPoolsWindow = PostUnbondPoolsWindow;
type MaxMetadataLen = ConstU32<256>;
type MaxUnbonding = <Self as pallet_staking::Config>::MaxUnlockingChunks;
type PalletId = NominationPoolsPalletId;
type MaxPointsToBalance = MaxPointsToBalance;
type AdminOrigin = DefaultAdminOrigin;
type BlockNumberProvider = System;
type Filter = pallet_staking::AllStakers<Runtime>;
}
parameter_types! {
pub const DelegatedStakingPalletId: PalletId = PalletId(*b"timedgsk");
pub const SlashRewardFraction: Perbill = Perbill::from_percent(1);
}
impl pallet_delegated_staking::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type PalletId = DelegatedStakingPalletId;
type Currency = Balances;
type OnSlash = (); type SlashRewardFraction = SlashRewardFraction;
type RuntimeHoldReason = RuntimeHoldReason;
type CoreStaking = Staking;
}
#[cfg(test)]
mod tests {
use super::*;
use frame_election_provider_support::NposSolution;
use sp_runtime::UpperOf;
#[test]
fn perbill_as_onchain_accuracy() {
type OnChainAccuracy =
<<Runtime as pallet_election_provider_multi_phase::MinerConfig>::Solution as NposSolution>::Accuracy;
let maximum_chain_accuracy: Vec<UpperOf<OnChainAccuracy>> = (0..MaxNominations::get())
.map(|_| <UpperOf<OnChainAccuracy>>::from(OnChainAccuracy::one().deconstruct()))
.collect();
let _: UpperOf<OnChainAccuracy> =
maximum_chain_accuracy.iter().fold(0, |acc, x| acc.checked_add(*x).unwrap());
}
}