diff --git a/Cargo.lock b/Cargo.lock index d0b8faced993..1bef1012d305 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3291,7 +3291,6 @@ dependencies = [ "pallet-session-benchmarking", "pallet-society", "pallet-staking", - "pallet-staking-reward-fn", "pallet-timestamp", "pallet-tips", "pallet-transaction-payment", @@ -7032,6 +7031,7 @@ dependencies = [ "pallet-election-provider-multi-phase", "pallet-session", "pallet-staking", + "pallet-staking-reward-fn", "pallet-timestamp", "pallet-transaction-payment", "pallet-treasury", diff --git a/runtime/common/Cargo.toml b/runtime/common/Cargo.toml index 9ee5de41188d..96f29eae9257 100644 --- a/runtime/common/Cargo.toml +++ b/runtime/common/Cargo.toml @@ -31,6 +31,7 @@ pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "m pallet-session = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-staking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-staking-reward-fn = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-timestamp = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-vesting = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } @@ -84,6 +85,7 @@ std = [ "pallet-beefy-mmr/std", "pallet-session/std", "pallet-staking/std", + "pallet-staking-reward-fn/std", "pallet-timestamp/std", "pallet-vesting/std", "pallet-transaction-payment/std", diff --git a/runtime/common/src/impls.rs b/runtime/common/src/impls.rs index 939b753092f5..2b10c79cfdff 100644 --- a/runtime/common/src/impls.rs +++ b/runtime/common/src/impls.rs @@ -18,6 +18,8 @@ use crate::NegativeImbalance; use frame_support::traits::{Currency, Imbalance, OnUnbalanced}; +use primitives::v2::Balance; +use sp_runtime::Perquintill; /// Logic for the author to get a portion of fees. pub struct ToAuthor(sp_std::marker::PhantomData); @@ -57,6 +59,45 @@ where } } +pub fn era_payout( + total_staked: Balance, + non_gilt_issuance: Balance, + max_annual_inflation: Perquintill, + period_fraction: Perquintill, + auctioned_slots: u64, +) -> (Balance, Balance) { + use pallet_staking_reward_fn::compute_inflation; + use sp_runtime::traits::Saturating; + + let min_annual_inflation = Perquintill::from_rational(25u64, 1000u64); + let delta_annual_inflation = max_annual_inflation.saturating_sub(min_annual_inflation); + + // 30% reserved for up to 60 slots. + let auction_proportion = Perquintill::from_rational(auctioned_slots.min(60), 200u64); + + // Therefore the ideal amount at stake (as a percentage of total issuance) is 75% less the + // amount that we expect to be taken up with auctions. + let ideal_stake = Perquintill::from_percent(75).saturating_sub(auction_proportion); + + let stake = Perquintill::from_rational(total_staked, non_gilt_issuance); + let falloff = Perquintill::from_percent(5); + let adjustment = compute_inflation(stake, ideal_stake, falloff); + let staking_inflation = + min_annual_inflation.saturating_add(delta_annual_inflation * adjustment); + + let max_payout = period_fraction * max_annual_inflation * non_gilt_issuance; + let staking_payout = (period_fraction * staking_inflation) * non_gilt_issuance; + let rest = max_payout.saturating_sub(staking_payout); + + let other_issuance = non_gilt_issuance.saturating_sub(total_staked); + if total_staked > other_issuance { + let _cap_rest = Perquintill::from_rational(other_issuance, total_staked) * staking_payout; + // We don't do anything with this, but if we wanted to, we could introduce a cap on the + // treasury amount with: `rest = rest.min(cap_rest);` + } + (staking_payout, rest) +} + #[cfg(test)] mod tests { use super::*; @@ -209,4 +250,44 @@ mod tests { assert_eq!(Balances::free_balance(Treasury::account_id()), 8); }); } + + #[test] + fn compute_inflation_should_give_sensible_results() { + assert_eq!( + pallet_staking_reward_fn::compute_inflation( + Perquintill::from_percent(75), + Perquintill::from_percent(75), + Perquintill::from_percent(5), + ), + Perquintill::one() + ); + assert_eq!( + pallet_staking_reward_fn::compute_inflation( + Perquintill::from_percent(50), + Perquintill::from_percent(75), + Perquintill::from_percent(5), + ), + Perquintill::from_rational(2u64, 3u64) + ); + assert_eq!( + pallet_staking_reward_fn::compute_inflation( + Perquintill::from_percent(80), + Perquintill::from_percent(75), + Perquintill::from_percent(5), + ), + Perquintill::from_rational(1u64, 2u64) + ); + } + + #[test] + fn era_payout_should_give_sensible_results() { + assert_eq!( + era_payout(75, 100, Perquintill::from_percent(10), Perquintill::one(), 0,), + (10, 0) + ); + assert_eq!( + era_payout(80, 100, Perquintill::from_percent(10), Perquintill::one(), 0,), + (6, 4) + ); + } } diff --git a/runtime/kusama/Cargo.toml b/runtime/kusama/Cargo.toml index 72500677c534..ec3de40ec5e6 100644 --- a/runtime/kusama/Cargo.toml +++ b/runtime/kusama/Cargo.toml @@ -72,7 +72,6 @@ pallet-session = { git = "https://github.com/paritytech/substrate", branch = "ma pallet-society = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-staking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } -pallet-staking-reward-fn = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } frame-system = {git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } frame-system-rpc-runtime-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } pallet-timestamp = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } @@ -170,7 +169,6 @@ std = [ "pallet-session/std", "pallet-society/std", "pallet-staking/std", - "pallet-staking-reward-fn/std", "pallet-timestamp/std", "pallet-tips/std", "pallet-treasury/std", diff --git a/runtime/kusama/src/lib.rs b/runtime/kusama/src/lib.rs index 0eb8cc0b4944..fa3aac2e7639 100644 --- a/runtime/kusama/src/lib.rs +++ b/runtime/kusama/src/lib.rs @@ -26,6 +26,7 @@ use primitives::v2::{ CoreState, GroupRotationInfo, Hash, Id as ParaId, InboundDownwardMessage, InboundHrmpMessage, Moment, Nonce, OccupiedCoreAssumption, PersistedValidationData, ScrapedOnChainVotes, SessionInfo, Signature, ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, + LOWEST_PUBLIC_ID, }; use runtime_common::{ auctions, claims, crowdloan, impl_runtime_weights, impls::DealWithFees, paras_registrar, @@ -504,45 +505,6 @@ impl pallet_bags_list::Config for Runtime { type Score = sp_npos_elections::VoteWeight; } -fn era_payout( - total_staked: Balance, - non_gilt_issuance: Balance, - max_annual_inflation: Perquintill, - period_fraction: Perquintill, - auctioned_slots: u64, -) -> (Balance, Balance) { - use pallet_staking_reward_fn::compute_inflation; - use sp_arithmetic::traits::Saturating; - - let min_annual_inflation = Perquintill::from_rational(25u64, 1000u64); - let delta_annual_inflation = max_annual_inflation.saturating_sub(min_annual_inflation); - - // 30% reserved for up to 60 slots. - let auction_proportion = Perquintill::from_rational(auctioned_slots.min(60), 200u64); - - // Therefore the ideal amount at stake (as a percentage of total issuance) is 75% less the amount that we expect - // to be taken up with auctions. - let ideal_stake = Perquintill::from_percent(75).saturating_sub(auction_proportion); - - let stake = Perquintill::from_rational(total_staked, non_gilt_issuance); - let falloff = Perquintill::from_percent(5); - let adjustment = compute_inflation(stake, ideal_stake, falloff); - let staking_inflation = - min_annual_inflation.saturating_add(delta_annual_inflation * adjustment); - - let max_payout = period_fraction * max_annual_inflation * non_gilt_issuance; - let staking_payout = (period_fraction * staking_inflation) * non_gilt_issuance; - let rest = max_payout.saturating_sub(staking_payout); - - let other_issuance = non_gilt_issuance.saturating_sub(total_staked); - if total_staked > other_issuance { - let _cap_rest = Perquintill::from_rational(other_issuance, total_staked) * staking_payout; - // We don't do anything with this, but if we wanted to, we could introduce a cap on the treasury amount - // with: `rest = rest.min(cap_rest);` - } - (staking_payout, rest) -} - pub struct EraPayout; impl pallet_staking::EraPayout for EraPayout { fn era_payout( @@ -550,13 +512,18 @@ impl pallet_staking::EraPayout for EraPayout { _total_issuance: Balance, era_duration_millis: u64, ) -> (Balance, Balance) { - // TODO: #3011 Update with proper auctioned slots tracking. - // This should be fine for the first year of parachains. - let auctioned_slots: u64 = auctions::Pallet::::auction_counter().into(); + // all para-ids that are currently active. + let auctioned_slots = Paras::parachains() + .into_iter() + // all active para-ids that do not belong to a system or common good chain is the number + // of parachains that we should take into account for inflation. + .filter(|i| *i >= LOWEST_PUBLIC_ID) + .count() as u64; + const MAX_ANNUAL_INFLATION: Perquintill = Perquintill::from_percent(10); const MILLISECONDS_PER_YEAR: u64 = 1000 * 3600 * 24 * 36525 / 100; - era_payout( + runtime_common::impls::era_payout( total_staked, Gilt::issuance().non_gilt, MAX_ANNUAL_INFLATION, diff --git a/runtime/kusama/src/tests.rs b/runtime/kusama/src/tests.rs index 12d02da131f8..ef09a9dc6131 100644 --- a/runtime/kusama/src/tests.rs +++ b/runtime/kusama/src/tests.rs @@ -137,40 +137,6 @@ fn nominator_limit() { println!("can support {} nominators to yield a weight of {}", active, weight_with(active)); } -#[test] -fn compute_inflation_should_give_sensible_results() { - assert_eq!( - pallet_staking_reward_fn::compute_inflation( - Perquintill::from_percent(75), - Perquintill::from_percent(75), - Perquintill::from_percent(5), - ), - Perquintill::one() - ); - assert_eq!( - pallet_staking_reward_fn::compute_inflation( - Perquintill::from_percent(50), - Perquintill::from_percent(75), - Perquintill::from_percent(5), - ), - Perquintill::from_rational(2u64, 3u64) - ); - assert_eq!( - pallet_staking_reward_fn::compute_inflation( - Perquintill::from_percent(80), - Perquintill::from_percent(75), - Perquintill::from_percent(5), - ), - Perquintill::from_rational(1u64, 2u64) - ); -} - -#[test] -fn era_payout_should_give_sensible_results() { - assert_eq!(era_payout(75, 100, Perquintill::from_percent(10), Perquintill::one(), 0,), (10, 0)); - assert_eq!(era_payout(80, 100, Perquintill::from_percent(10), Perquintill::one(), 0,), (6, 4)); -} - #[test] fn call_size() { RuntimeCall::assert_size_under(230); diff --git a/runtime/polkadot/src/lib.rs b/runtime/polkadot/src/lib.rs index 5adf4e11e67f..ebcf9ec1d8a6 100644 --- a/runtime/polkadot/src/lib.rs +++ b/runtime/polkadot/src/lib.rs @@ -58,6 +58,7 @@ use primitives::v2::{ CoreState, GroupRotationInfo, Hash, Id as ParaId, InboundDownwardMessage, InboundHrmpMessage, Moment, Nonce, OccupiedCoreAssumption, PersistedValidationData, ScrapedOnChainVotes, SessionInfo, Signature, ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, + LOWEST_PUBLIC_ID, }; use sp_core::OpaqueMetadata; use sp_mmr_primitives as mmr; @@ -536,6 +537,35 @@ type SlashCancelOrigin = EitherOfDiverse< pallet_collective::EnsureProportionAtLeast, >; +pub struct EraPayout; +impl pallet_staking::EraPayout for EraPayout { + fn era_payout( + total_staked: Balance, + total_issuance: Balance, + era_duration_millis: u64, + ) -> (Balance, Balance) { + // all para-ids that are not active. + let auctioned_slots = Paras::parachains() + .into_iter() + // all active para-ids that do not belong to a system or common good chain is the number + // of parachains that we should take into account for inflation. + .filter(|i| *i >= LOWEST_PUBLIC_ID) + .count() as u64; + + const MAX_ANNUAL_INFLATION: Perquintill = Perquintill::from_percent(10); + const MILLISECONDS_PER_YEAR: u64 = 1000 * 3600 * 24 * 36525 / 100; + + runtime_common::impls::era_payout( + total_staked, + // Polkadot has no notion of gilts, the entire issuance is non-guilt. + total_issuance, + MAX_ANNUAL_INFLATION, + Perquintill::from_rational(era_duration_millis, MILLISECONDS_PER_YEAR), + auctioned_slots, + ) + } +} + impl pallet_staking::Config for Runtime { type MaxNominations = MaxNominations; type Currency = Balances; @@ -552,7 +582,7 @@ impl pallet_staking::Config for Runtime { // A super-majority of the council can cancel the slash. type SlashCancelOrigin = SlashCancelOrigin; type SessionInterface = Self; - type EraPayout = pallet_staking::ConvertCurve; + type EraPayout = EraPayout; type MaxNominatorRewardedPerValidator = MaxNominatorRewardedPerValidator; type OffendingValidatorsThreshold = OffendingValidatorsThreshold; type NextNewSession = Session;