From e8e59d66a21915d36601b51c6e51bfcd488129e1 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Thu, 17 Oct 2024 16:05:11 +0100 Subject: [PATCH] Polkadot: Constant yearly emission (#471) Sets the inflation to ~120M DOT per year as specified in [Ref 1139](https://polkadot.subsquare.io/referenda/1139). 15% of that goes to the treasury and the rest to stakers. ## Details This MR sets the Polkadot inflation to a fixed amount per year. The yearly increase is ~120,063,259 DOT. The amount was set to 8% of the Total Issuance at block [22810263](https://polkadot.statescan.io/#/blocks/22810263) in which referendum [1139](https://polkadot.subsquare.io/referenda/1139) was executed. You can check this for yourself: Screenshot 2024-10-08 at 16 51 07 Multiplying this by 8% and converting to DOT is about **`120,093,259` DOT per year**. This results in an emissions of `120,093,259 / 365.25` about **`328,797` DOT per day**. The Total Issuance and yearly inflation look like this over the next 25 years: ![inflation](https://github.com/user-attachments/assets/32572833-0a62-4823-9b7e-00350fcf1b9d)
Concrete numbers

```pre Initial 1.501 BDOT Year 1: 1.621 BDOT +7.407% Year 2: 1.741 BDOT +6.897% Year 3: 1.861 BDOT +6.452% Year 4: 1.982 BDOT +6.061% Year 5: 2.102 BDOT +5.714% Year 6: 2.222 BDOT +5.405% Year 7: 2.342 BDOT +5.128% Year 8: 2.462 BDOT +4.878% Year 9: 2.582 BDOT +4.651% Year 10: 2.702 BDOT +4.444% Year 11: 2.822 BDOT +4.255% Year 12: 2.942 BDOT +4.082% Year 13: 3.062 BDOT +3.922% Year 14: 3.182 BDOT +3.774% Year 15: 3.303 BDOT +3.636% Year 16: 3.423 BDOT +3.509% Year 17: 3.543 BDOT +3.390% Year 18: 3.663 BDOT +3.279% Year 19: 3.783 BDOT +3.175% Year 20: 3.903 BDOT +3.077% Year 21: 4.023 BDOT +2.985% Year 22: 4.143 BDOT +2.899% Year 23: 4.263 BDOT +2.817% Year 24: 4.383 BDOT +2.740% Year 25: 4.503 BDOT +2.667% ```

## Implications 15% of the inflation goes to the treasury which results in a yearly treasury inflow of ~18M DOT (1.5M DOT per month). Stakers will receive ~102M DOT per year (~8.5M DOT per month). --------- Signed-off-by: Oliver Tale-Yazdi --- CHANGELOG.md | 6 ++ Cargo.lock | 17 ++++- Cargo.toml | 1 + relay/polkadot/Cargo.toml | 1 + relay/polkadot/src/lib.rs | 150 +++++++++++++++++++++++++++++++------- 5 files changed, 146 insertions(+), 29 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 263d11c1dd..4d9cbc5dfd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ Changelog for the runtimes governed by the Polkadot Fellowship. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +## [Unreleased] + +### Changed + +Change Polkadot inflation to 120M DOT per year ([polkadot-fellows/runtimes#471](https://github.com/polkadot-fellows/runtimes/pull/471)) + ## [1.3.3] 01.10.2024 ### Changed diff --git a/Cargo.lock b/Cargo.lock index 4f4d467771..ef1c6afbbb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7420,6 +7420,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-format" version = "0.4.4" @@ -10063,6 +10069,7 @@ dependencies = [ name = "polkadot-runtime" version = "1.0.0" dependencies = [ + "approx", "binary-merkle-tree", "frame-benchmarking", "frame-election-provider-support", @@ -15088,12 +15095,13 @@ dependencies = [ [[package]] name = "time" -version = "0.3.30" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", + "num-conv", "powerfmt", "serde", "time-core", @@ -15108,10 +15116,11 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.15" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ + "num-conv", "time-core", ] diff --git a/Cargo.toml b/Cargo.toml index 13199f41e3..e96581a7b1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ license = "GPL-3.0-only" # TODO ; } + /// Defines how much should the inflation be for an era given its duration. pub struct EraPayout; impl pallet_staking::EraPayout for EraPayout { fn era_payout( - total_staked: Balance, + _total_staked: Balance, _total_issuance: Balance, era_duration_millis: u64, ) -> (Balance, Balance) { - const MILLISECONDS_PER_YEAR: u64 = 1000 * 3600 * 24 * 36525 / 100; - - let params = relay_common::EraPayoutParams { - total_staked, - total_stakable: Balances::total_issuance(), - ideal_stake: dynamic_params::inflation::IdealStake::get(), - max_annual_inflation: dynamic_params::inflation::MaxInflation::get(), - min_annual_inflation: dynamic_params::inflation::MinInflation::get(), - falloff: dynamic_params::inflation::Falloff::get(), - period_fraction: Perquintill::from_rational(era_duration_millis, MILLISECONDS_PER_YEAR), - legacy_auction_proportion: if dynamic_params::inflation::UseAuctionSlots::get() { - let auctioned_slots = parachains_paras::Parachains::::get() - .into_iter() - // all active para-ids that do not belong to a system chain is the number of - // parachains that we should take into account for inflation. - .filter(|i| *i >= LOWEST_PUBLIC_ID) - .count() as u64; - Some(Perquintill::from_rational(auctioned_slots.min(60), 300u64)) - } else { - None - }, - }; + const MILLISECONDS_PER_YEAR: u64 = (1000 * 3600 * 24 * 36525) / 100; + // A normal-sized era will have 1 / 365.25 here: + let relative_era_len = + FixedU128::from_rational(era_duration_millis.into(), MILLISECONDS_PER_YEAR.into()); + + // TI at the time of execution of [Referendum 1139](https://polkadot.subsquare.io/referenda/1139), block hash: `0x39422610299a75ef69860417f4d0e1d94e77699f45005645ffc5e8e619950f9f`. + let fixed_total_issuance: i128 = 15_011_657_390_566_252_333; + let fixed_inflation_rate = FixedU128::from_rational(8, 100); + let yearly_emission = fixed_inflation_rate.saturating_mul_int(fixed_total_issuance); - log::debug!(target: LOG_TARGET, "params: {:?}", params); - relay_common::relay_era_payout(params) + let era_emission = relative_era_len.saturating_mul_int(yearly_emission); + // 15% to treasury, as per ref 1139. + let to_treasury = FixedU128::from_rational(15, 100).saturating_mul_int(era_emission); + let to_stakers = era_emission.saturating_sub(to_treasury); + + (to_stakers.saturated_into(), to_treasury.saturated_into()) } } @@ -3377,6 +3369,7 @@ mod multiplier_tests { dispatch::DispatchInfo, traits::{OnFinalize, PalletInfoAccess}, }; + use pallet_staking::EraPayout; use polkadot_runtime_common::{MinimumMultiplier, TargetBlockFullness}; use separator::Separatable; use sp_runtime::traits::Convert; @@ -3408,6 +3401,113 @@ mod multiplier_tests { }) } + use approx::assert_relative_eq; + const MILLISECONDS_PER_DAY: u64 = 24 * 60 * 60 * 1000; + + #[test] + fn staking_inflation_correct_single_era() { + let (to_stakers, to_treasury) = super::EraPayout::era_payout( + 123, // ignored + 456, // ignored + MILLISECONDS_PER_DAY, + ); + + // Values are within 0.1% + assert_relative_eq!(to_stakers as f64, (279_477 * UNITS) as f64, max_relative = 0.001); + assert_relative_eq!(to_treasury as f64, (49_320 * UNITS) as f64, max_relative = 0.001); + // Total per day is ~328,797 DOT + assert_relative_eq!( + (to_stakers as f64 + to_treasury as f64), + (328_797 * UNITS) as f64, + max_relative = 0.001 + ); + } + + #[test] + fn staking_inflation_correct_longer_era() { + // Twice the era duration means twice the emission: + let (to_stakers, to_treasury) = super::EraPayout::era_payout( + 123, // ignored + 456, // ignored + 2 * MILLISECONDS_PER_DAY, + ); + + assert_relative_eq!( + to_stakers as f64, + (279_477 * UNITS) as f64 * 2.0, + max_relative = 0.001 + ); + assert_relative_eq!( + to_treasury as f64, + (49_320 * UNITS) as f64 * 2.0, + max_relative = 0.001 + ); + } + + #[test] + fn staking_inflation_correct_whole_year() { + let (to_stakers, to_treasury) = super::EraPayout::era_payout( + 123, // ignored + 456, // ignored + (36525 * MILLISECONDS_PER_DAY) / 100, // 1 year + ); + + // Our yearly emissions is about 120M DOT: + let yearly_emission = 120_093_259 * UNITS; + assert_relative_eq!( + to_stakers as f64 + to_treasury as f64, + yearly_emission as f64, + max_relative = 0.001 + ); + + assert_relative_eq!(to_stakers as f64, yearly_emission as f64 * 0.85, max_relative = 0.001); + assert_relative_eq!( + to_treasury as f64, + yearly_emission as f64 * 0.15, + max_relative = 0.001 + ); + } + + // 10 years into the future, our values do not overflow. + #[test] + fn staking_inflation_correct_not_overflow() { + let (to_stakers, to_treasury) = super::EraPayout::era_payout( + 123, // ignored + 456, // ignored + (36525 * MILLISECONDS_PER_DAY) / 10, // 10 years + ); + let initial_ti: i128 = 15_011_657_390_566_252_333; + let projected_total_issuance = (to_stakers as i128 + to_treasury as i128) + initial_ti; + + // In 2034, there will be about 2.7 billion DOT in existence. + assert_relative_eq!( + projected_total_issuance as f64, + (2_700_000_000 * UNITS) as f64, + max_relative = 0.001 + ); + } + + // Print percent per year, just as convenience. + #[test] + fn staking_inflation_correct_print_percent() { + let (to_stakers, to_treasury) = super::EraPayout::era_payout( + 123, // ignored + 456, // ignored + (36525 * MILLISECONDS_PER_DAY) / 100, // 1 year + ); + let yearly_emission = to_stakers + to_treasury; + let mut ti: i128 = 15_011_657_390_566_252_333; + + for y in 0..10 { + let new_ti = ti + yearly_emission as i128; + let inflation = 100.0 * (new_ti - ti) as f64 / ti as f64; + println!("Year {y} inflation: {inflation}%"); + ti = new_ti; + + assert!(inflation <= 8.0 && inflation > 2.0, "sanity check"); + } + } + #[test] fn fast_unstake_estimate() { use pallet_fast_unstake::WeightInfo;