Skip to content

Commit

Permalink
Polkadot: Constant yearly emission (#471)
Browse files Browse the repository at this point in the history
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:

<img width="1414" alt="Screenshot 2024-10-08 at 16 51 07"
src="https://github.com/user-attachments/assets/3254f706-c53f-44c2-8ab7-75f08b9ce3f8">

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)

<details><summary>Concrete numbers</summary>
<p>

```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%
```

</p>
</details> 

## 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 <oliver.tale-yazdi@parity.io>
  • Loading branch information
ggwpez authored Oct 17, 2024
1 parent b483d37 commit e8e59d6
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 29 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
17 changes: 13 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ license = "GPL-3.0-only" # TODO <https://

[workspace.dependencies]
assert_matches = { version = "1.5.0" }
approx = { version = "0.5.1" }
asset-hub-kusama-emulated-chain = { path = "integration-tests/emulated/chains/parachains/assets/asset-hub-kusama" }
asset-hub-kusama-runtime = { path = "system-parachains/asset-hubs/asset-hub-kusama" }
asset-hub-polkadot-emulated-chain = { path = "integration-tests/emulated/chains/parachains/assets/asset-hub-polkadot" }
Expand Down
1 change: 1 addition & 0 deletions relay/polkadot/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ sp-debug-derive = { workspace = true }
polkadot-parachain-primitives = { workspace = true }

[dev-dependencies]
approx = { workspace = true }
sp-keyring = { workspace = true }
sp-trie = { workspace = true }
separator = { workspace = true }
Expand Down
150 changes: 125 additions & 25 deletions relay/polkadot/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -700,39 +700,31 @@ impl pallet_parameters::Config for Runtime {
type AdminOrigin = DynamicParameterOrigin;
type WeightInfo = weights::pallet_parameters::WeightInfo<Runtime>;
}

/// Defines how much should the inflation be for an era given its duration.
pub struct EraPayout;
impl pallet_staking::EraPayout<Balance> 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::<Runtime>::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())
}
}

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit e8e59d6

Please sign in to comment.