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}
308
309#[derive(Encode, Decode)]
310pub enum RelayChainRuntimePallets {
312 #[codec(index = 67)]
314 AhClient(AhClientCalls),
315}
316
317#[derive(Encode, Decode)]
318pub enum AhClientCalls {
319 #[codec(index = 0)]
321 ValidatorSet(rc_client::ValidatorSetReport<AccountId>),
322}
323
324pub struct ValidatorSetToXcm;
325impl sp_runtime::traits::Convert<rc_client::ValidatorSetReport<AccountId>, Xcm<()>>
326 for ValidatorSetToXcm
327{
328 fn convert(report: rc_client::ValidatorSetReport<AccountId>) -> Xcm<()> {
329 Xcm(vec![
330 Instruction::UnpaidExecution {
331 weight_limit: WeightLimit::Unlimited,
332 check_origin: None,
333 },
334 Instruction::Transact {
335 origin_kind: OriginKind::Native,
336 fallback_max_weight: None,
337 call: RelayChainRuntimePallets::AhClient(AhClientCalls::ValidatorSet(report))
338 .encode()
339 .into(),
340 },
341 ])
342 }
343}
344
345parameter_types! {
346 pub RelayLocation: Location = Location::parent();
347}
348
349pub struct StakingXcmToRelayChain;
350
351impl rc_client::SendToRelayChain for StakingXcmToRelayChain {
352 type AccountId = AccountId;
353 fn validator_set(report: rc_client::ValidatorSetReport<Self::AccountId>) {
354 rc_client::XCMSender::<
355 xcm_config::XcmRouter,
356 RelayLocation,
357 rc_client::ValidatorSetReport<Self::AccountId>,
358 ValidatorSetToXcm,
359 >::split_then_send(report, Some(8));
360 }
361}
362
363impl pallet_fast_unstake::Config for Runtime {
364 type RuntimeEvent = RuntimeEvent;
365 type Currency = Balances;
366 type BatchSize = ConstU32<64>;
367 type Deposit = ConstU128<{ UNITS }>;
368 type ControlOrigin = EnsureRoot<AccountId>;
369 type Staking = Staking;
370 type MaxErasToCheckPerBlock = ConstU32<1>;
371 type WeightInfo = weights::pallet_fast_unstake::WeightInfo<Runtime>;
372}
373
374parameter_types! {
375 pub const PoolsPalletId: PalletId = PalletId(*b"py/nopls");
376 pub const MaxPointsToBalance: u8 = 10;
377}
378
379impl pallet_nomination_pools::Config for Runtime {
380 type RuntimeEvent = RuntimeEvent;
381 type WeightInfo = weights::pallet_nomination_pools::WeightInfo<Self>;
382 type Currency = Balances;
383 type RuntimeFreezeReason = RuntimeFreezeReason;
384 type RewardCounter = FixedU128;
385 type BalanceToU256 = BalanceToU256;
386 type U256ToBalance = U256ToBalance;
387 type StakeAdapter =
388 pallet_nomination_pools::adapter::DelegateStake<Self, Staking, DelegatedStaking>;
389 type PostUnbondingPoolsWindow = ConstU32<4>;
390 type MaxMetadataLen = ConstU32<256>;
391 type MaxUnbonding = <Self as pallet_staking_async::Config>::MaxUnlockingChunks;
393 type PalletId = PoolsPalletId;
394 type MaxPointsToBalance = MaxPointsToBalance;
395 type AdminOrigin = EitherOf<EnsureRoot<AccountId>, StakingAdmin>;
396 type BlockNumberProvider = RelaychainDataProvider<Runtime>;
397 type Filter = Nothing;
398}
399
400parameter_types! {
401 pub const DelegatedStakingPalletId: PalletId = PalletId(*b"py/dlstk");
402 pub const SlashRewardFraction: Perbill = Perbill::from_percent(1);
403}
404
405impl pallet_delegated_staking::Config for Runtime {
406 type RuntimeEvent = RuntimeEvent;
407 type PalletId = DelegatedStakingPalletId;
408 type Currency = Balances;
409 type OnSlash = ();
410 type SlashRewardFraction = SlashRewardFraction;
411 type RuntimeHoldReason = RuntimeHoldReason;
412 type CoreStaking = Staking;
413}
414
415pub type SignedPayload = generic::SignedPayload<RuntimeCall, TxExtension>;
417pub type UncheckedExtrinsic =
419 generic::UncheckedExtrinsic<Address, RuntimeCall, Signature, TxExtension>;
420
421impl frame_system::offchain::SigningTypes for Runtime {
422 type Public = <Signature as Verify>::Signer;
423 type Signature = Signature;
424}
425
426impl<C> frame_system::offchain::CreateTransactionBase<C> for Runtime
427where
428 RuntimeCall: From<C>,
429{
430 type RuntimeCall = RuntimeCall;
431 type Extrinsic = UncheckedExtrinsic;
432}
433
434impl<LocalCall> frame_system::offchain::CreateTransaction<LocalCall> for Runtime
435where
436 RuntimeCall: From<LocalCall>,
437{
438 type Extension = TxExtension;
439
440 fn create_transaction(call: RuntimeCall, extension: TxExtension) -> UncheckedExtrinsic {
441 UncheckedExtrinsic::new_transaction(call, extension)
442 }
443}
444
445impl<LocalCall> frame_system::offchain::CreateSignedTransaction<LocalCall> for Runtime
448where
449 RuntimeCall: From<LocalCall>,
450{
451 fn create_signed_transaction<
452 C: frame_system::offchain::AppCrypto<Self::Public, Self::Signature>,
453 >(
454 call: RuntimeCall,
455 public: <Signature as Verify>::Signer,
456 account: AccountId,
457 nonce: <Runtime as frame_system::Config>::Nonce,
458 ) -> Option<UncheckedExtrinsic> {
459 use sp_runtime::traits::StaticLookup;
460 let period =
462 BlockHashCount::get().checked_next_power_of_two().map(|c| c / 2).unwrap_or(2) as u64;
463
464 let current_block = System::block_number()
465 .saturated_into::<u64>()
466 .saturating_sub(1);
469 let tip = 0;
470 let tx_ext = TxExtension::from((
471 frame_system::AuthorizeCall::<Runtime>::new(),
472 frame_system::CheckNonZeroSender::<Runtime>::new(),
473 frame_system::CheckSpecVersion::<Runtime>::new(),
474 frame_system::CheckTxVersion::<Runtime>::new(),
475 frame_system::CheckGenesis::<Runtime>::new(),
476 frame_system::CheckEra::<Runtime>::from(generic::Era::mortal(period, current_block)),
477 frame_system::CheckNonce::<Runtime>::from(nonce),
478 frame_system::CheckWeight::<Runtime>::new(),
479 pallet_asset_conversion_tx_payment::ChargeAssetTxPayment::<Runtime>::from(tip, None),
480 frame_metadata_hash_extension::CheckMetadataHash::<Runtime>::new(true),
481 ));
482 let raw_payload = SignedPayload::new(call, tx_ext)
483 .map_err(|e| {
484 tracing::warn!(target: "runtime::staking", error=?e, "Unable to create signed payload");
485 })
486 .ok()?;
487 let signature = raw_payload.using_encoded(|payload| C::sign(payload, public))?;
488 let (call, tx_ext, _) = raw_payload.deconstruct();
489 let address = <Runtime as frame_system::Config>::Lookup::unlookup(account);
490 let transaction = UncheckedExtrinsic::new_signed(call, address, signature, tx_ext);
491 Some(transaction)
492 }
493}
494
495impl<LocalCall> frame_system::offchain::CreateInherent<LocalCall> for Runtime
496where
497 RuntimeCall: From<LocalCall>,
498{
499 fn create_bare(call: RuntimeCall) -> UncheckedExtrinsic {
500 UncheckedExtrinsic::new_bare(call)
501 }
502}