diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 98bf1398e..b8d9a0583 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -216,7 +216,7 @@ jobs: runs-on: ubuntu-latest needs: [build-nodes, build-runtimes, publish-docker-image] steps: - - name: Fetch template + - name: Fetch note template uses: actions/checkout@v4 - name: Download nodes and runtimes uses: actions/download-artifact@v4 diff --git a/core/inflation/src/lib.rs b/core/inflation/src/lib.rs index 0723c4ddf..49f4b8605 100644 --- a/core/inflation/src/lib.rs +++ b/core/inflation/src/lib.rs @@ -28,50 +28,129 @@ mod test; // crates.io use primitive_types::U256; // darwinia -use dc_types::{Balance, Moment, UNIT}; +use dc_types::{Balance, Moment}; // github -use substrate_fixed::{ - transcendental, - types::{I95F33, U94F34}, -}; - -/// Inflation's upper limit. -pub const TOTAL_SUPPLY: Balance = 10_000_000_000 * UNIT; +use substrate_fixed::types::U94F34; /// Milliseconds per year. pub const MILLISECS_PER_YEAR: Balance = (366 * 24 * 60 * 60) * 1000; -/// Compute the inflation of a period. +/// Issuing maps for ages 1 to 100 years. +pub const ISSUING_MAP: [Balance; 100] = [ + 80000000000000000000000000, + 111773286000000000000000000, + 134746987000000000000000000, + 152702244000000000000000000, + 167131169000000000000000000, + 178823309000000000000000000, + 188269287000000000000000000, + 195807995000000000000000000, + 201691935000000000000000000, + 206120192000000000000000000, + 209256584000000000000000000, + 211240393000000000000000000, + 212192983000000000000000000, + 212222105000000000000000000, + 211424760000000000000000000, + 209889162000000000000000000, + 207696140000000000000000000, + 204920129000000000000000000, + 201629917000000000000000000, + 197889213000000000000000000, + 193757076000000000000000000, + 189288266000000000000000000, + 184533528000000000000000000, + 179539841000000000000000000, + 174350616000000000000000000, + 169005897000000000000000000, + 163542519000000000000000000, + 157994277000000000000000000, + 152392074000000000000000000, + 146764063000000000000000000, + 141135790000000000000000000, + 135530328000000000000000000, + 129968413000000000000000000, + 124468570000000000000000000, + 119047241000000000000000000, + 113718915000000000000000000, + 108496242000000000000000000, + 103390155000000000000000000, + 98409990000000000000000000, + 93563591000000000000000000, + 88857424000000000000000000, + 84296682000000000000000000, + 79885385000000000000000000, + 75626479000000000000000000, + 71521926000000000000000000, + 67572800000000000000000000, + 63779363000000000000000000, + 60141155000000000000000000, + 56657064000000000000000000, + 53325400000000000000000000, + 50143962000000000000000000, + 47110103000000000000000000, + 44220789000000000000000000, + 41472652000000000000000000, + 38862045000000000000000000, + 36385086000000000000000000, + 34037705000000000000000000, + 31815679000000000000000000, + 29714676000000000000000000, + 27730281000000000000000000, + 25858032000000000000000000, + 24093442000000000000000000, + 22432030000000000000000000, + 20869335000000000000000000, + 19400942000000000000000000, + 18022495000000000000000000, + 16729714000000000000000000, + 15518406000000000000000000, + 14384477000000000000000000, + 13323941000000000000000000, + 12332926000000000000000000, + 11407684000000000000000000, + 10544591000000000000000000, + 9740153000000000000000000, + 8991010000000000000000000, + 8293934000000000000000000, + 7645831000000000000000000, + 7043743000000000000000000, + 6484844000000000000000000, + 5966438000000000000000000, + 5485963000000000000000000, + 5040980000000000000000000, + 4629177000000000000000000, + 4248363000000000000000000, + 3896462000000000000000000, + 3571515000000000000000000, + 3271673000000000000000000, + 2995191000000000000000000, + 2740429000000000000000000, + 2505842000000000000000000, + 2289982000000000000000000, + 2091489000000000000000000, + 1909087000000000000000000, + 1741584000000000000000000, + 1587865000000000000000000, + 1446887000000000000000000, + 1317679000000000000000000, + 1199333000000000000000000, + 1091005000000000000000000, + 991910000000000000000000, +]; + +/// Calculate the issuing of a period. /// /// Use `U94F34` here, because `2^94 > TOTAL_SUPPLY * 10^18`. -pub fn in_period(unminted: Balance, period: Moment, elapsed: Moment) -> Option { - let unminted_per_millisecs = U94F34::checked_from_num(unminted)? / MILLISECS_PER_YEAR; - let x = - (unminted_per_millisecs.checked_mul(U94F34::checked_from_num(period)?)?).floor().to_num(); - let years = (elapsed / MILLISECS_PER_YEAR + 1) as _; - - inflate(x, years) -} - -// Compute the inflation. -// -// Formula: -// ``` -// x * (1 - (99 / 100) ^ sqrt(years)); -// ``` -// -// Use `I95F33` here, because `2^94 > TOTAL_SUPPLY * 10^18`. -fn inflate(x: Balance, years: u8) -> Option { - let sqrt = transcendental::sqrt::(years.into()).ok()?; - let ninety_nine = I95F33::from_num(99_u8) / 100_i128; - let pow = transcendental::pow::(ninety_nine, sqrt).ok()?; - let ratio = I95F33::from_num(1_u8) - pow; - let inflation = I95F33::checked_from_num(x)? * ratio; +pub fn issuing_in_period(period: Moment, elapsed: Moment) -> Option { + let years = (elapsed / MILLISECS_PER_YEAR) as usize; + let to_issue = ISSUING_MAP[years]; + let to_issue_per_millisecs = U94F34::checked_from_num(to_issue)? / MILLISECS_PER_YEAR; - Some(inflation.floor().to_num()) + Some(to_issue_per_millisecs.checked_mul(U94F34::checked_from_num(period)?)?.floor().to_num()) } -/// Compute the reward of a deposit. +/// Calculate the reward of a deposit. /// /// Reference(s): /// - diff --git a/core/inflation/src/test.rs b/core/inflation/src/test.rs index 8fff6d4af..163c936ae 100644 --- a/core/inflation/src/test.rs +++ b/core/inflation/src/test.rs @@ -18,9 +18,43 @@ // darwinia use crate::*; +use dc_types::UNIT; +// github +use substrate_fixed::{transcendental, types::I95F33}; + +// Generate the issuing map. +// +// Formula: +// ``` +// unissued * (1 - (99 / 100) ^ sqrt(years)); +// ``` +// +// Use `I95F33` here, because `2^94 > TOTAL_SUPPLY * 10^18`. +fn issuing_map() -> Vec { + let ninety_nine = I95F33::from_num(99_u8) / 100_i128; + let max = 10_000_000_000_u128; + let mut supply = 2_000_000_000_u128; + + (1_u8..=100) + .map(|years| { + let sqrt = transcendental::sqrt::(years.into()).unwrap(); + let pow = transcendental::pow::(ninety_nine, sqrt).unwrap(); + let ratio = I95F33::from_num(1_u8) - pow; + let unissued = max - supply; + let to_issue = + (I95F33::checked_from_num(unissued).unwrap() * ratio).floor().to_num::(); + + supply += to_issue; + + to_issue * UNIT + }) + .collect() +} #[test] -fn inflate_should_work() { +fn issuing_map_should_work() { + assert_eq!(issuing_map(), ISSUING_MAP); + let max = 10_000_000_000_u128 * UNIT; let init = 2_000_000_000_u128 * UNIT; #[allow(clippy::approx_constant)] @@ -33,21 +67,19 @@ fn inflate_should_work() { 0.08, 0.07, 0.07, 0.06, 0.06, 0.05, 0.05, 0.04, 0.04, 0.04, 0.03, 0.03, 0.03, 0.03, 0.02, 0.02, 0.02, 0.02, 0.02, 0.01, 0.01, 0.01, 0.01, 0.01, ]; - let mut unminted = max - init; + let mut unissued = max - init; - for (&rate, years) in rates.iter().zip(1..) { - let inflation = - in_period(unminted, MILLISECS_PER_YEAR, (years - 1) as u128 * MILLISECS_PER_YEAR) - .unwrap(); + rates.iter().zip(0..).for_each(|(rate, years)| { + let issued = issuing_in_period(MILLISECS_PER_YEAR, years * MILLISECS_PER_YEAR).unwrap(); sp_arithmetic::assert_eq_error_rate!( - inflation as f64 / (max - unminted) as f64, - rate / 100_f64, + issued as f64 / (max - unissued) as f64, + *rate / 100_f64, 0.0001_f64 ); - unminted -= inflation; - } + unissued -= issued; + }); } #[test] diff --git a/pallet/account-migration/src/mock.rs b/pallet/account-migration/src/mock.rs index a0edb2558..59befed95 100644 --- a/pallet/account-migration/src/mock.rs +++ b/pallet/account-migration/src/mock.rs @@ -162,7 +162,7 @@ impl darwinia_deposit::Config for Runtime { impl darwinia_staking::Config for Runtime { type Currency = Balances; type Deposit = Deposit; - type InflationManager = (); + type IssuingManager = (); type Kton = Dummy; type MaxDeposits = (); type MaxUnstakings = (); diff --git a/pallet/staking/src/lib.rs b/pallet/staking/src/lib.rs index 63007369b..4e4c1ca63 100644 --- a/pallet/staking/src/lib.rs +++ b/pallet/staking/src/lib.rs @@ -123,7 +123,7 @@ pub mod pallet { type Currency: Currency; /// Inflation and reward manager. - type InflationManager: InflationManager; + type IssuingManager: IssuingManager; /// Pass [`pallet_session::Config::ShouldEndSession`]'s result to here. type ShouldEndSession: Get; @@ -902,7 +902,7 @@ pub mod pallet { >::mutate(&c, |u| *u = u.map(|u| u + r).or(Some(r))); // TODO: merge into one call - if T::InflationManager::reward(&staking_pot, r).is_ok() { + if T::IssuingManager::reward(&staking_pot, r).is_ok() { actual_reward += r; } else { unpaid += r; @@ -936,14 +936,14 @@ pub mod pallet { // If the collator nominated themselves. c_payout += n_payout; - } else if T::InflationManager::reward(&n_exposure.who, n_payout).is_ok() { + } else if T::IssuingManager::reward(&n_exposure.who, n_payout).is_ok() { Self::deposit_event(Event::Payout { staker: n_exposure.who, amount: n_payout }); } else { Self::deposit_event(Event::Unpaid { staker: n_exposure.who, amount: n_payout }); } } - if T::InflationManager::reward(&collator, c_payout).is_ok() { + if T::IssuingManager::reward(&collator, c_payout).is_ok() { Self::deposit_event(Event::Payout { staker: collator, amount: c_payout }); } else { Self::deposit_event(Event::Unpaid { staker: collator, amount: c_payout }); @@ -1055,8 +1055,8 @@ type Vote = u32; type DepositId = <::Deposit as Stake>::Item; -/// Inflation and reward manager. -pub trait InflationManager +/// Issuing and reward manager. +pub trait IssuingManager where T: Config, { @@ -1077,7 +1077,7 @@ where } /// Calculate the reward. - fn calculate_reward(inflation: Balance) -> Balance; + fn calculate_reward(issued: Balance) -> Balance; /// The reward function. fn reward(who: &T::AccountId, amount: Balance) -> DispatchResult; @@ -1085,7 +1085,7 @@ where /// Clear the remaining inflation. fn clear(_remaining: Balance) {} } -impl InflationManager for () +impl IssuingManager for () where T: Config, { @@ -1191,7 +1191,7 @@ where T: Config, { fn end_session(_: u32) { - T::InflationManager::on_session_end(); + T::IssuingManager::on_session_end(); } fn start_session(_: u32) {} diff --git a/pallet/staking/src/mock.rs b/pallet/staking/src/mock.rs index 9806e9499..8c16c2943 100644 --- a/pallet/staking/src/mock.rs +++ b/pallet/staking/src/mock.rs @@ -258,7 +258,7 @@ impl darwinia_staking::Stake for KtonStaking { } } pub enum StatedOnSessionEnd {} -impl darwinia_staking::InflationManager for StatedOnSessionEnd { +impl darwinia_staking::IssuingManager for StatedOnSessionEnd { fn inflate() -> Balance { if INFLATION_TYPE.with(|v| *v.borrow()) == 0 { OnDarwiniaSessionEnd::inflate() @@ -267,11 +267,11 @@ impl darwinia_staking::InflationManager for StatedOnSessionEnd { } } - fn calculate_reward(inflation: Balance) -> Balance { + fn calculate_reward(issued: Balance) -> Balance { if INFLATION_TYPE.with(|v| *v.borrow()) == 0 { - OnDarwiniaSessionEnd::calculate_reward(inflation) + OnDarwiniaSessionEnd::calculate_reward(issued) } else { - OnCrabSessionEnd::calculate_reward(inflation) + OnCrabSessionEnd::calculate_reward(issued) } } @@ -292,7 +292,7 @@ impl darwinia_staking::InflationManager for StatedOnSessionEnd { } } pub enum OnDarwiniaSessionEnd {} -impl darwinia_staking::InflationManager for OnDarwiniaSessionEnd { +impl darwinia_staking::IssuingManager for OnDarwiniaSessionEnd { fn inflate() -> Balance { let now = Timestamp::now(); let session_duration = now - >::get(); @@ -304,13 +304,11 @@ impl darwinia_staking::InflationManager for OnDarwiniaSessionEnd { >::put(now); - let unminted = dc_inflation::TOTAL_SUPPLY.saturating_sub(Balances::total_issuance()); - - dc_inflation::in_period(unminted, session_duration, elapsed_time).unwrap_or_default() + dc_inflation::issuing_in_period(session_duration, elapsed_time).unwrap_or_default() } - fn calculate_reward(inflation: Balance) -> Balance { - PayoutFraction::get() * inflation + fn calculate_reward(issued: Balance) -> Balance { + PayoutFraction::get() * issued } fn reward(who: &AccountId, amount: Balance) -> sp_runtime::DispatchResult { @@ -324,7 +322,7 @@ impl darwinia_staking::InflationManager for OnDarwiniaSessionEnd { } } pub enum OnCrabSessionEnd {} -impl darwinia_staking::InflationManager for OnCrabSessionEnd { +impl darwinia_staking::IssuingManager for OnCrabSessionEnd { fn calculate_reward(_inflation: Balance) -> Balance { 10_000 * UNIT } @@ -352,7 +350,7 @@ impl frame_support::traits::Get for ShouldEndSession { impl darwinia_staking::Config for Runtime { type Currency = Balances; type Deposit = Deposit; - type InflationManager = StatedOnSessionEnd; + type IssuingManager = StatedOnSessionEnd; type Kton = KtonStaking; type MaxDeposits = ::MaxDeposits; type MaxUnstakings = frame_support::traits::ConstU32<16>; diff --git a/pallet/staking/src/tests.rs b/pallet/staking/src/tests.rs index b9395cece..a78154eed 100644 --- a/pallet/staking/src/tests.rs +++ b/pallet/staking/src/tests.rs @@ -638,7 +638,6 @@ fn payout_should_work() { new_session(); (1..=10).for_each(|i| assert_eq!(Balances::free_balance(i), 1_000 * UNIT)); - let total_issuance = Balances::total_issuance(); let session_duration = Duration::new(12 * 600, 0).as_millis(); Efflux::time(session_duration - >::get() as Moment); Staking::note_authors(&[1, 2, 3, 4, 5]); @@ -647,16 +646,16 @@ fn payout_should_work() { payout(); let rewards = vec![ - 455327412809459795649, - 849944493808794062101, - 1183851276145838531437, - 1457047723758662022603, - 1669533853403313367198, - 1365982241160343870361, - 971365161800188293957, - 637458379463143824620, - 364261930757534540090, - 151775801295014161055, + 364298724080145719490, + 680024276867030965392, + 947176684881602914390, + 1165755919271402550091, + 1335761993442622950820, + 1092896174426229508197, + 777170622950819672131, + 510018214936247723133, + 291438979672131147541, + 121432905646630236794, ]; assert_eq!( rewards, @@ -664,12 +663,7 @@ fn payout_should_work() { ); assert_eq_error_rate!( PayoutFraction::get() - * dc_inflation::in_period( - dc_inflation::TOTAL_SUPPLY - total_issuance, - session_duration, - Timestamp::now() - ) - .unwrap(), + * dc_inflation::issuing_in_period(session_duration, Timestamp::now()).unwrap(), rewards.iter().sum::(), // Error rate 1 RING. UNIT @@ -787,22 +781,22 @@ fn auto_payout_should_work() { (1..=4).for_each(|i| assert_eq!(Balances::free_balance(i), 1_000 * UNIT)); Efflux::block(1); - assert_eq!(Balances::free_balance(1), 1000000758879022790250); + assert_eq!(Balances::free_balance(1), 1000000607164541287188); assert_eq!(Balances::free_balance(2), 1000000000000000000000); - assert_eq!(Balances::free_balance(3), 1000003035516089643241); + assert_eq!(Balances::free_balance(3), 1000002428658163934426); assert_eq!(Balances::free_balance(4), 1000000000000000000000); Efflux::block(1); - assert_eq!(Balances::free_balance(1), 1000000758879022790250); - assert_eq!(Balances::free_balance(2), 1000001433438163308069); - assert_eq!(Balances::free_balance(3), 1000003035516089643241); - assert_eq!(Balances::free_balance(4), 1000002360956952540378); + assert_eq!(Balances::free_balance(1), 1000000607164541287188); + assert_eq!(Balances::free_balance(2), 1000001146866363084396); + assert_eq!(Balances::free_balance(3), 1000002428658163934426); + assert_eq!(Balances::free_balance(4), 1000001888956344869459); Efflux::block(1); - assert_eq!(Balances::free_balance(1), 1000000758879022790250); - assert_eq!(Balances::free_balance(2), 1000001433438163308069); - assert_eq!(Balances::free_balance(3), 1000003035516089643241); - assert_eq!(Balances::free_balance(4), 1000002360956952540378); + assert_eq!(Balances::free_balance(1), 1000000607164541287188); + assert_eq!(Balances::free_balance(2), 1000001146866363084396); + assert_eq!(Balances::free_balance(3), 1000002428658163934426); + assert_eq!(Balances::free_balance(4), 1000001888956344869459); }); } diff --git a/precompile/staking/src/mock.rs b/precompile/staking/src/mock.rs index b78128593..aba2a5b6c 100644 --- a/precompile/staking/src/mock.rs +++ b/precompile/staking/src/mock.rs @@ -234,7 +234,7 @@ impl darwinia_staking::Stake for KtonStaking { impl darwinia_staking::Config for Runtime { type Currency = Balances; type Deposit = Deposit; - type InflationManager = (); + type IssuingManager = (); type Kton = KtonStaking; type MaxDeposits = ::MaxDeposits; type MaxUnstakings = frame_support::traits::ConstU32<16>; diff --git a/runtime/crab/src/pallets/staking.rs b/runtime/crab/src/pallets/staking.rs index 39b62f56c..6a617aefa 100644 --- a/runtime/crab/src/pallets/staking.rs +++ b/runtime/crab/src/pallets/staking.rs @@ -73,7 +73,7 @@ impl darwinia_staking::Stake for KtonStaking { } pub enum OnCrabSessionEnd {} -impl darwinia_staking::InflationManager for OnCrabSessionEnd { +impl darwinia_staking::IssuingManager for OnCrabSessionEnd { fn calculate_reward(_inflation: Balance) -> Balance { 20_000 * UNIT } @@ -103,7 +103,7 @@ impl frame_support::traits::Get for ShouldEndSession { impl darwinia_staking::Config for Runtime { type Currency = Balances; type Deposit = Deposit; - type InflationManager = OnCrabSessionEnd; + type IssuingManager = OnCrabSessionEnd; type Kton = KtonStaking; type MaxDeposits = ::MaxDeposits; type MaxUnstakings = ConstU32<16>; diff --git a/runtime/darwinia/src/pallets/staking.rs b/runtime/darwinia/src/pallets/staking.rs index 473598b21..785e19d53 100644 --- a/runtime/darwinia/src/pallets/staking.rs +++ b/runtime/darwinia/src/pallets/staking.rs @@ -73,7 +73,7 @@ impl darwinia_staking::Stake for KtonStaking { } pub enum OnDarwiniaSessionEnd {} -impl darwinia_staking::InflationManager for OnDarwiniaSessionEnd { +impl darwinia_staking::IssuingManager for OnDarwiniaSessionEnd { fn inflate() -> Balance { let now = Timestamp::now() as Moment; let session_duration = now - >::get(); @@ -85,13 +85,11 @@ impl darwinia_staking::InflationManager for OnDarwiniaSessionEnd { >::put(now); - let unminted = dc_inflation::TOTAL_SUPPLY.saturating_sub(Balances::total_issuance()); - - dc_inflation::in_period(unminted, session_duration, elapsed_time).unwrap_or_default() + dc_inflation::issuing_in_period(session_duration, elapsed_time).unwrap_or_default() } - fn calculate_reward(inflation: Balance) -> Balance { - sp_runtime::Perbill::from_percent(40) * inflation + fn calculate_reward(issued: Balance) -> Balance { + sp_runtime::Perbill::from_percent(40) * issued } fn reward(who: &AccountId, amount: Balance) -> sp_runtime::DispatchResult { @@ -120,7 +118,7 @@ impl frame_support::traits::Get for ShouldEndSession { impl darwinia_staking::Config for Runtime { type Currency = Balances; type Deposit = Deposit; - type InflationManager = OnDarwiniaSessionEnd; + type IssuingManager = OnDarwiniaSessionEnd; type Kton = KtonStaking; type MaxDeposits = ::MaxDeposits; type MaxUnstakings = ConstU32<16>; diff --git a/runtime/pangolin/src/pallets/staking.rs b/runtime/pangolin/src/pallets/staking.rs index 28bf71b61..bdc9a2bfd 100644 --- a/runtime/pangolin/src/pallets/staking.rs +++ b/runtime/pangolin/src/pallets/staking.rs @@ -67,7 +67,7 @@ impl darwinia_staking::Stake for KtonStaking { } pub enum OnPangolinSessionEnd {} -impl darwinia_staking::InflationManager for OnPangolinSessionEnd { +impl darwinia_staking::IssuingManager for OnPangolinSessionEnd { fn calculate_reward(_inflation: Balance) -> Balance { 20_000 * UNIT } @@ -97,7 +97,7 @@ impl frame_support::traits::Get for ShouldEndSession { impl darwinia_staking::Config for Runtime { type Currency = Balances; type Deposit = Deposit; - type InflationManager = OnPangolinSessionEnd; + type IssuingManager = OnPangolinSessionEnd; type Kton = KtonStaking; type MaxDeposits = ::MaxDeposits; type MaxUnstakings = ConstU32<16>; diff --git a/runtime/pangoro/src/pallets/staking.rs b/runtime/pangoro/src/pallets/staking.rs index 06e3f63b0..242dfc9c4 100644 --- a/runtime/pangoro/src/pallets/staking.rs +++ b/runtime/pangoro/src/pallets/staking.rs @@ -69,7 +69,7 @@ impl darwinia_staking::Stake for KtonStaking { } pub enum OnPangoroSessionEnd {} -impl darwinia_staking::InflationManager for OnPangoroSessionEnd { +impl darwinia_staking::IssuingManager for OnPangoroSessionEnd { fn inflate() -> Balance { let now = Timestamp::now() as Moment; let session_duration = now - >::get() as Moment; @@ -81,13 +81,11 @@ impl darwinia_staking::InflationManager for OnPangoroSessionEnd { >::put(now); - let unminted = dc_inflation::TOTAL_SUPPLY.saturating_sub(Balances::total_issuance()); - - dc_inflation::in_period(unminted, session_duration, elapsed_time).unwrap_or_default() + dc_inflation::issuing_in_period(session_duration, elapsed_time).unwrap_or_default() } - fn calculate_reward(inflation: Balance) -> Balance { - sp_runtime::Perbill::from_percent(40) * inflation + fn calculate_reward(issued: Balance) -> Balance { + sp_runtime::Perbill::from_percent(40) * issued } fn reward(who: &AccountId, amount: Balance) -> sp_runtime::DispatchResult { @@ -116,7 +114,7 @@ impl frame_support::traits::Get for ShouldEndSession { impl darwinia_staking::Config for Runtime { type Currency = Balances; type Deposit = Deposit; - type InflationManager = OnPangoroSessionEnd; + type IssuingManager = OnPangoroSessionEnd; type Kton = KtonStaking; type MaxDeposits = ::MaxDeposits; type MaxUnstakings = ConstU32<16>;