asset_hub_westend_runtime/
staking.rs1use super::*;
18use cumulus_primitives_core::relay_chain::SessionIndex;
19use frame_election_provider_support::{ElectionDataProvider, SequentialPhragmen};
20use frame_support::traits::{ConstU128, EitherOf};
21use pallet_election_provider_multi_block::{self as multi_block, SolutionAccuracyOf};
22use pallet_staking_async::UseValidatorsMap;
23use pallet_staking_async_rc_client as rc_client;
24use polkadot_runtime_common::{prod_or_fast, BalanceToU256, U256ToBalance};
25use sp_runtime::{
26 transaction_validity::TransactionPriority, FixedPointNumber, FixedU128, SaturatedConversion,
27};
28use xcm::latest::prelude::*;
29
30parameter_types! {
31 pub Pages: u32 = 32;
33
34 pub MaxElectingVoters: u32 = 22_500;
36
37 pub const MaxValidatorSet: u32 = 1000;
39
40 pub VoterSnapshotPerBlock: u32 = MaxElectingVoters::get() / Pages::get();
42
43 pub TargetSnapshotPerBlock: u32 = MaxValidatorSet::get();
45
46 pub storage SignedPhase: u32 = prod_or_fast!(
48 10 * MINUTES,
49 4 * MINUTES
50 );
51 pub storage UnsignedPhase: u32 = prod_or_fast!(
52 10 * MINUTES,
53 (1 * MINUTES)
54 );
55
56 pub storage SignedValidationPhase: u32 = prod_or_fast!(Pages::get() * 4, Pages::get());
58
59 pub MaxWinnersPerPage: u32 = MaxValidatorSet::get();
61
62 pub MaxBackersPerWinner: u32 = VoterSnapshotPerBlock::get();
64
65 pub MaxBackersPerWinnerFinal: u32 = MaxElectingVoters::get();
67
68 pub MaxExposurePageSize: u32 = 64;
70
71 pub SolutionImprovementThreshold: Perbill = Perbill::from_rational(1u32, 10_000);
73}
74
75frame_election_provider_support::generate_solution_type!(
76 #[compact]
77 pub struct NposCompactSolution16::<
78 VoterIndex = u32,
80 TargetIndex = u16,
82 Accuracy = sp_runtime::PerU16,
83 MaxVoters = VoterSnapshotPerBlock,
84 >(16)
85);
86
87ord_parameter_types! {
88 pub const WestendStakingMiner: AccountId = AccountId::from(hex_literal::hex!("b65991822483a6c3bd24b1dcf6afd3e270525da1f9c8c22a4373d1e1079e236a"));
90}
91
92#[cfg(feature = "runtime-benchmarks")]
93parameter_types! {
94 pub BenchElectionBounds: frame_election_provider_support::bounds::ElectionBounds =
95 frame_election_provider_support::bounds::ElectionBoundsBuilder::default().build();
96}
97
98#[cfg(feature = "runtime-benchmarks")]
99pub struct OnChainConfig;
100
101#[cfg(feature = "runtime-benchmarks")]
102impl frame_election_provider_support::onchain::Config for OnChainConfig {
103 type Bounds = BenchElectionBounds;
105 type Sort = ConstBool<false>;
108 type DataProvider = Staking;
109 type MaxBackersPerWinner = MaxBackersPerWinner;
110 type MaxWinnersPerPage = MaxWinnersPerPage;
111 type Solver = frame_election_provider_support::SequentialPhragmen<AccountId, Perbill>;
112 type System = Runtime;
113 type WeightInfo = ();
114}
115
116impl multi_block::Config for Runtime {
117 type Pages = Pages;
118 type UnsignedPhase = UnsignedPhase;
119 type SignedPhase = SignedPhase;
120 type SignedValidationPhase = SignedValidationPhase;
121 type VoterSnapshotPerBlock = VoterSnapshotPerBlock;
122 type TargetSnapshotPerBlock = TargetSnapshotPerBlock;
123 type AdminOrigin =
124 EitherOfDiverse<EnsureRoot<AccountId>, EnsureSignedBy<WestendStakingMiner, AccountId>>;
125 type DataProvider = Staking;
126 type MinerConfig = Self;
127 type Verifier = MultiBlockElectionVerifier;
128 #[cfg(not(feature = "runtime-benchmarks"))]
130 type Fallback = multi_block::Continue<Self>;
131 #[cfg(feature = "runtime-benchmarks")]
132 type Fallback = frame_election_provider_support::onchain::OnChainExecution<OnChainConfig>;
133 type AreWeDone = multi_block::RevertToSignedIfNotQueuedOf<Self>;
135 type OnRoundRotation = multi_block::CleanRound<Self>;
136 type WeightInfo = multi_block::weights::westend::MultiBlockWeightInfo<Self>;
137}
138
139impl multi_block::verifier::Config for Runtime {
140 type MaxWinnersPerPage = MaxWinnersPerPage;
141 type MaxBackersPerWinner = MaxBackersPerWinner;
142 type MaxBackersPerWinnerFinal = MaxBackersPerWinnerFinal;
143 type SolutionDataProvider = MultiBlockElectionSigned;
144 type SolutionImprovementThreshold = SolutionImprovementThreshold;
145 type WeightInfo = multi_block::weights::westend::MultiBlockVerifierWeightInfo<Self>;
146}
147
148parameter_types! {
149 pub BailoutGraceRatio: Perbill = Perbill::from_percent(50);
150 pub EjectGraceRatio: Perbill = Perbill::from_percent(50);
151 pub DepositBase: Balance = 5 * UNITS;
152 pub DepositPerPage: Balance = 1 * UNITS;
153 pub RewardBase: Balance = 10 * UNITS;
154 pub MaxSubmissions: u32 = 8;
155}
156
157impl multi_block::signed::Config for Runtime {
158 type Currency = Balances;
159 type BailoutGraceRatio = BailoutGraceRatio;
160 type EjectGraceRatio = EjectGraceRatio;
161 type DepositBase = DepositBase;
162 type DepositPerPage = DepositPerPage;
163 type InvulnerableDeposit = ();
164 type RewardBase = RewardBase;
165 type MaxSubmissions = MaxSubmissions;
166 type EstimateCallFee = TransactionPayment;
167 type WeightInfo = multi_block::weights::westend::MultiBlockSignedWeightInfo<Self>;
168}
169
170parameter_types! {
171 pub MinerTxPriority: TransactionPriority = TransactionPriority::max_value() / 2;
173 pub OffchainRepeat: BlockNumber = UnsignedPhase::get() / 4;
175 pub storage MinerPages: u32 = 2;
176}
177
178impl multi_block::unsigned::Config for Runtime {
179 type MinerPages = MinerPages;
180 type OffchainStorage = ConstBool<true>;
181 type OffchainSolver = SequentialPhragmen<AccountId, SolutionAccuracyOf<Runtime>>;
182 type MinerTxPriority = MinerTxPriority;
183 type OffchainRepeat = OffchainRepeat;
184 type WeightInfo = multi_block::weights::westend::MultiBlockUnsignedWeightInfo<Self>;
185}
186
187parameter_types! {
188 pub MinerMaxLength: u32 = Perbill::from_rational(75u32, 100) *
190 *RuntimeBlockLength::get()
191 .max
192 .get(DispatchClass::Normal);
193}
194
195impl multi_block::unsigned::miner::MinerConfig for Runtime {
196 type AccountId = AccountId;
197 type Hash = Hash;
198 type MaxBackersPerWinner = <Self as multi_block::verifier::Config>::MaxBackersPerWinner;
199 type MaxBackersPerWinnerFinal =
200 <Self as multi_block::verifier::Config>::MaxBackersPerWinnerFinal;
201 type MaxWinnersPerPage = <Self as multi_block::verifier::Config>::MaxWinnersPerPage;
202 type MaxVotesPerVoter =
203 <<Self as multi_block::Config>::DataProvider as ElectionDataProvider>::MaxVotesPerVoter;
204 type MaxLength = MinerMaxLength;
205 type Solver = <Runtime as multi_block::unsigned::Config>::OffchainSolver;
206 type Pages = Pages;
207 type Solution = NposCompactSolution16;
208 type VoterSnapshotPerBlock = <Runtime as multi_block::Config>::VoterSnapshotPerBlock;
209 type TargetSnapshotPerBlock = <Runtime as multi_block::Config>::TargetSnapshotPerBlock;
210}
211
212parameter_types! {
213 pub const BagThresholds: &'static [u64] = &bag_thresholds::THRESHOLDS;
214 pub const AutoRebagNumber: u32 = 10;
215}
216
217type VoterBagsListInstance = pallet_bags_list::Instance1;
218impl pallet_bags_list::Config<VoterBagsListInstance> for Runtime {
219 type RuntimeEvent = RuntimeEvent;
220 type ScoreProvider = Staking;
221 type WeightInfo = weights::pallet_bags_list::WeightInfo<Runtime>;
222 type BagThresholds = BagThresholds;
223 type Score = sp_npos_elections::VoteWeight;
224 type MaxAutoRebagPerBlock = AutoRebagNumber;
225}
226
227pub struct EraPayout;
228impl pallet_staking_async::EraPayout<Balance> for EraPayout {
229 fn era_payout(
230 _total_staked: Balance,
231 _total_issuance: Balance,
232 era_duration_millis: u64,
233 ) -> (Balance, Balance) {
234 const MILLISECONDS_PER_YEAR: u64 = (1000 * 3600 * 24 * 36525) / 100;
235 let relative_era_len =
237 FixedU128::from_rational(era_duration_millis.into(), MILLISECONDS_PER_YEAR.into());
238
239 let fixed_total_issuance: i128 = 5_216_342_402_773_185_773;
241 let fixed_inflation_rate = FixedU128::from_rational(8, 100);
242 let yearly_emission = fixed_inflation_rate.saturating_mul_int(fixed_total_issuance);
243
244 let era_emission = relative_era_len.saturating_mul_int(yearly_emission);
245 let to_treasury = FixedU128::from_rational(15, 100).saturating_mul_int(era_emission);
247 let to_stakers = era_emission.saturating_sub(to_treasury);
248
249 (to_stakers.saturated_into(), to_treasury.saturated_into())
250 }
251}
252
253parameter_types! {
254 pub const SessionsPerEra: SessionIndex = prod_or_fast!(6, 2);
256 pub const RelaySessionDuration: BlockNumber = 1 * HOURS;
258 pub const BondingDuration: sp_staking::EraIndex = 2;
260 pub const SlashDeferDuration: sp_staking::EraIndex = 1;
262 pub const MaxControllersInDeprecationBatch: u32 = 751;
263 pub const MaxNominations: u32 = <NposCompactSolution16 as frame_election_provider_support::NposSolution>::LIMIT as u32;
265 pub const MaxEraDuration: u64 = RelaySessionDuration::get() as u64 * RELAY_CHAIN_SLOT_DURATION_MILLIS as u64 * SessionsPerEra::get() as u64;
266 pub MaxPruningItems: u32 = 100;
267}
268
269impl pallet_staking_async::Config for Runtime {
270 type Filter = ();
271 type OldCurrency = Balances;
272 type Currency = Balances;
273 type CurrencyBalance = Balance;
274 type RuntimeHoldReason = RuntimeHoldReason;
275 type CurrencyToVote = sp_staking::currency_to_vote::SaturatingCurrencyToVote;
276 type RewardRemainder = ();
277 type Slash = ();
278 type Reward = ();
279 type SessionsPerEra = SessionsPerEra;
280 type BondingDuration = BondingDuration;
281 type SlashDeferDuration = SlashDeferDuration;
282 type AdminOrigin = EitherOf<EnsureRoot<AccountId>, StakingAdmin>;
283 type EraPayout = EraPayout;
284 type MaxExposurePageSize = MaxExposurePageSize;
285 type ElectionProvider = MultiBlockElection;
286 type VoterList = VoterList;
287 type TargetList = UseValidatorsMap<Self>;
288 type MaxValidatorSet = MaxValidatorSet;
289 type NominationsQuota = pallet_staking_async::FixedNominationsQuota<{ MaxNominations::get() }>;
290 type MaxUnlockingChunks = frame_support::traits::ConstU32<32>;
291 type HistoryDepth = frame_support::traits::ConstU32<84>;
292 type MaxControllersInDeprecationBatch = MaxControllersInDeprecationBatch;
293 type EventListeners = (NominationPools, DelegatedStaking);
294 type WeightInfo = weights::pallet_staking_async::WeightInfo<Runtime>;
295 type MaxInvulnerables = frame_support::traits::ConstU32<20>;
296 type PlanningEraOffset =
297 pallet_staking_async::PlanningEraOffsetOf<Runtime, RelaySessionDuration, ConstU32<5>>;
298 type RcClientInterface = StakingRcClient;
299 type MaxEraDuration = MaxEraDuration;
300 type MaxPruningItems = MaxPruningItems;
301}
302
303impl pallet_staking_async_rc_client::Config for Runtime {
304 type RelayChainOrigin = EnsureRoot<AccountId>;
305 type AHStakingInterface = Staking;
306 type SendToRelayChain = StakingXcmToRelayChain;
307 type MaxValidatorSetRetries = ConstU32<64>;
308}
309
310#[derive(Encode, Decode)]
311pub enum RelayChainRuntimePallets {
313 #[codec(index = 67)]
315 AhClient(AhClientCalls),
316}
317
318#[derive(Encode, Decode)]
319pub enum AhClientCalls {
320 #[codec(index = 0)]
322 ValidatorSet(rc_client::ValidatorSetReport<AccountId>),
323}
324
325pub struct ValidatorSetToXcm;
326impl sp_runtime::traits::Convert<rc_client::ValidatorSetReport<AccountId>, Xcm<()>>
327 for ValidatorSetToXcm
328{
329 fn convert(report: rc_client::ValidatorSetReport<AccountId>) -> Xcm<()> {
330 Xcm(vec![
331 Instruction::UnpaidExecution {
332 weight_limit: WeightLimit::Unlimited,
333 check_origin: None,
334 },
335 Instruction::Transact {
336 origin_kind: OriginKind::Native,
337 fallback_max_weight: None,
338 call: RelayChainRuntimePallets::AhClient(AhClientCalls::ValidatorSet(report))
339 .encode()
340 .into(),
341 },
342 ])
343 }
344}
345
346parameter_types! {
347 pub RelayLocation: Location = Location::parent();
348}
349
350pub struct StakingXcmToRelayChain;
351
352impl rc_client::SendToRelayChain for StakingXcmToRelayChain {
353 type AccountId = AccountId;
354 fn validator_set(report: rc_client::ValidatorSetReport<Self::AccountId>) -> Result<(), ()> {
355 rc_client::XCMSender::<
356 xcm_config::XcmRouter,
357 RelayLocation,
358 rc_client::ValidatorSetReport<Self::AccountId>,
359 ValidatorSetToXcm,
360 >::send(report)
361 }
362}
363
364impl pallet_fast_unstake::Config for Runtime {
365 type RuntimeEvent = RuntimeEvent;
366 type Currency = Balances;
367 type BatchSize = ConstU32<64>;
368 type Deposit = ConstU128<{ UNITS }>;
369 type ControlOrigin = EnsureRoot<AccountId>;
370 type Staking = Staking;
371 type MaxErasToCheckPerBlock = ConstU32<1>;
372 type WeightInfo = weights::pallet_fast_unstake::WeightInfo<Runtime>;
373}
374
375parameter_types! {
376 pub const PoolsPalletId: PalletId = PalletId(*b"py/nopls");
377 pub const MaxPointsToBalance: u8 = 10;
378}
379
380impl pallet_nomination_pools::Config for Runtime {
381 type RuntimeEvent = RuntimeEvent;
382 type WeightInfo = weights::pallet_nomination_pools::WeightInfo<Self>;
383 type Currency = Balances;
384 type RuntimeFreezeReason = RuntimeFreezeReason;
385 type RewardCounter = FixedU128;
386 type BalanceToU256 = BalanceToU256;
387 type U256ToBalance = U256ToBalance;
388 type StakeAdapter =
389 pallet_nomination_pools::adapter::DelegateStake<Self, Staking, DelegatedStaking>;
390 type PostUnbondingPoolsWindow = ConstU32<4>;
391 type MaxMetadataLen = ConstU32<256>;
392 type MaxUnbonding = <Self as pallet_staking_async::Config>::MaxUnlockingChunks;
394 type PalletId = PoolsPalletId;
395 type MaxPointsToBalance = MaxPointsToBalance;
396 type AdminOrigin = EitherOf<EnsureRoot<AccountId>, StakingAdmin>;
397 type BlockNumberProvider = RelaychainDataProvider<Runtime>;
398 type Filter = Nothing;
399}
400
401parameter_types! {
402 pub const DelegatedStakingPalletId: PalletId = PalletId(*b"py/dlstk");
403 pub const SlashRewardFraction: Perbill = Perbill::from_percent(1);
404}
405
406impl pallet_delegated_staking::Config for Runtime {
407 type RuntimeEvent = RuntimeEvent;
408 type PalletId = DelegatedStakingPalletId;
409 type Currency = Balances;
410 type OnSlash = ();
411 type SlashRewardFraction = SlashRewardFraction;
412 type RuntimeHoldReason = RuntimeHoldReason;
413 type CoreStaking = Staking;
414}
415
416pub type SignedPayload = generic::SignedPayload<RuntimeCall, TxExtension>;
418pub type UncheckedExtrinsic =
420 generic::UncheckedExtrinsic<Address, RuntimeCall, Signature, TxExtension>;
421
422impl frame_system::offchain::SigningTypes for Runtime {
423 type Public = <Signature as Verify>::Signer;
424 type Signature = Signature;
425}
426
427impl<C> frame_system::offchain::CreateTransactionBase<C> for Runtime
428where
429 RuntimeCall: From<C>,
430{
431 type RuntimeCall = RuntimeCall;
432 type Extrinsic = UncheckedExtrinsic;
433}
434
435impl<LocalCall> frame_system::offchain::CreateTransaction<LocalCall> for Runtime
436where
437 RuntimeCall: From<LocalCall>,
438{
439 type Extension = TxExtension;
440
441 fn create_transaction(call: RuntimeCall, extension: TxExtension) -> UncheckedExtrinsic {
442 UncheckedExtrinsic::new_transaction(call, extension)
443 }
444}
445
446impl<LocalCall> frame_system::offchain::CreateSignedTransaction<LocalCall> for Runtime
449where
450 RuntimeCall: From<LocalCall>,
451{
452 fn create_signed_transaction<
453 C: frame_system::offchain::AppCrypto<Self::Public, Self::Signature>,
454 >(
455 call: RuntimeCall,
456 public: <Signature as Verify>::Signer,
457 account: AccountId,
458 nonce: <Runtime as frame_system::Config>::Nonce,
459 ) -> Option<UncheckedExtrinsic> {
460 use sp_runtime::traits::StaticLookup;
461 let period =
463 BlockHashCount::get().checked_next_power_of_two().map(|c| c / 2).unwrap_or(2) as u64;
464
465 let current_block = System::block_number()
466 .saturated_into::<u64>()
467 .saturating_sub(1);
470 let tip = 0;
471 let tx_ext = TxExtension::from((
472 frame_system::AuthorizeCall::<Runtime>::new(),
473 frame_system::CheckNonZeroSender::<Runtime>::new(),
474 frame_system::CheckSpecVersion::<Runtime>::new(),
475 frame_system::CheckTxVersion::<Runtime>::new(),
476 frame_system::CheckGenesis::<Runtime>::new(),
477 frame_system::CheckEra::<Runtime>::from(generic::Era::mortal(period, current_block)),
478 frame_system::CheckNonce::<Runtime>::from(nonce),
479 frame_system::CheckWeight::<Runtime>::new(),
480 pallet_asset_conversion_tx_payment::ChargeAssetTxPayment::<Runtime>::from(tip, None),
481 frame_metadata_hash_extension::CheckMetadataHash::<Runtime>::new(true),
482 ));
483 let raw_payload = SignedPayload::new(call, tx_ext)
484 .map_err(|e| {
485 tracing::warn!(target: "runtime::staking", error=?e, "Unable to create signed payload");
486 })
487 .ok()?;
488 let signature = raw_payload.using_encoded(|payload| C::sign(payload, public))?;
489 let (call, tx_ext, _) = raw_payload.deconstruct();
490 let address = <Runtime as frame_system::Config>::Lookup::unlookup(account);
491 let transaction = UncheckedExtrinsic::new_signed(call, address, signature, tx_ext);
492 Some(transaction)
493 }
494}
495
496impl<LocalCall> frame_system::offchain::CreateInherent<LocalCall> for Runtime
497where
498 RuntimeCall: From<LocalCall>,
499{
500 fn create_bare(call: RuntimeCall) -> UncheckedExtrinsic {
501 UncheckedExtrinsic::new_bare(call)
502 }
503}