Skip to content

Commit

Permalink
runtime: add reward pool and re-enable rewards (#1513)
Browse files Browse the repository at this point in the history
  • Loading branch information
FlorianFranzen authored Feb 9, 2025
1 parent 7e11e31 commit ddae285
Show file tree
Hide file tree
Showing 9 changed files with 105 additions and 57 deletions.
73 changes: 35 additions & 38 deletions node/src/chain_spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,14 @@ use time_primitives::{AccountId, Balance, Block, BlockNumber, ANLOG, SS58_PREFIX
use timechain_runtime::{RUNTIME_VARIANT, WASM_BINARY};

/// Stash and float for validators
const PER_VALIDATOR_STASH: Balance = ANLOG * 500_000;
const PER_VALIDATOR_UNLOCKED: Balance = ANLOG * 20_000;
const PER_VALIDATOR_STASH: Balance = ANLOG * 1_001_000;
const PER_VALIDATOR_UNLOCKED: Balance = ANLOG * 1_000;

/// Stash and float for chronicles
const PER_CHRONICLE_STASH: Balance = ANLOG * 100_000;

/// Token supply for prefunded admin accounts
const CONTROLLER_SUPPLY: Balance = ANLOG * 50_000;
const PER_COUNCIL_STASH: Balance = ANLOG * 50_000;

/// Minimum needed validators, currently lowered for testing environments
const MIN_VALIDATOR_COUNT: u32 = 1;
const PER_COUNCIL_STASH: Balance = ANLOG * 100_000;

/// Default telemetry server for all networks
const DEFAULT_TELEMETRY_URL: &str = "wss://telemetry.analog.one/submit";
Expand Down Expand Up @@ -64,8 +60,6 @@ pub struct GenesisKeysConfig {
/// Stashes to be used for chronicles, balances controlled by PER_CHRONICLE_STASH
#[allow(dead_code)]
chronicles: Vec<AccountId>,
/// Optional controller account that will control all nominates stakes
controller: Option<AccountId>,
/// Additional endowed accounts and their balance in ANLOG.
endowments: Vec<(AccountId, Balance)>,
/// Stashes intended to be used to run validators.
Expand All @@ -88,8 +82,6 @@ impl Default for GenesisKeysConfig {
Alice.to_raw_public().unchecked_into(),
)],
chronicles: vec![],
// TODO: Would be better to assign individual controllers
controller: None,
endowments: vec![],
stakes: vec![Alice.into(), Bob.into(), Charlie.into(), Dave.into()],
}
Expand Down Expand Up @@ -185,11 +177,6 @@ impl GenesisKeysConfig {
.collect::<Vec<_>>(),
);

// Endow controller if necessary
if let Some(controller) = self.controller.as_ref() {
endowments.push((controller.clone(), CONTROLLER_SUPPLY));
}

// Endow council members and validators
endowments.append(
&mut self.admins.iter().map(|x| (x.clone(), PER_COUNCIL_STASH)).collect::<Vec<_>>(),
Expand All @@ -210,7 +197,7 @@ impl GenesisKeysConfig {
.enumerate()
.map(|(i, x)| {
(
self.controller.clone().unwrap_or(self.stakes[i].clone()),
self.stakes[i].clone(),
self.stakes[i].clone(),
timechain_runtime::SessionKeys {
babe: x.0.clone(),
Expand All @@ -222,49 +209,59 @@ impl GenesisKeysConfig {
})
.collect();

// Self-stake all authorities
let locked = PER_VALIDATOR_STASH - PER_VALIDATOR_UNLOCKED;
let min_count = if chain_type == ChainType::Local { 1 } else { 4 };
let stakers = authorities
.iter()
.map(|x| {
(
x.1.clone(),
x.0.clone(), // Ignored
locked,
timechain_runtime::StakerStatus::<AccountId>::Validator,
)
})
.collect::<Vec<_>>();

let mut genesis_patch = serde_json::json!({
"balances": {
"balances": endowments,
},
"babe": {
"epochConfig": timechain_runtime::BABE_GENESIS_EPOCH_CONFIG,
},
"nominationPools": {
"minJoinBond": ANLOG,
"minCreateBond": 100_000 * ANLOG,
"maxPools": Some(0),
"maxMembersPerPool": None::<u32>,
"maxMembers": None::<u32>,
},
"session": {
"keys": authorities,
},
"staking": {
"validatorCount": authorities.len() as u32,
"minimumValidatorCount": min_count,
"invulnerables": authorities.iter().map(|x| x.1.clone()).collect::<Vec<_>>(),
"slashRewardFraction": sp_runtime::Perbill::from_percent(10),
"stakers": stakers,
"minNominatorBond": 100_000 * ANLOG,
"minValidatorBond": 1_000_000 * ANLOG,
},
"technicalCommittee": {
"members": Some(self.admins.clone()),
},
});

if cfg!(feature = "testnet") {
// Self-stake all authorities
let locked = PER_VALIDATOR_STASH - PER_VALIDATOR_UNLOCKED;
let stakers = authorities
.iter()
.map(|x| {
(
x.1.clone(),
x.0.clone(),
locked,
timechain_runtime::StakerStatus::<AccountId>::Validator,
)
})
.collect::<Vec<_>>();

json_merge(
&mut genesis_patch,
serde_json::json!({
"networks": {
"networks": [],
},
"staking": {
"validatorCount": authorities.len() as u32,
"minimumValidatorCount": MIN_VALIDATOR_COUNT,
"invulnerables": authorities.iter().map(|x| x.1.clone()).collect::<Vec<_>>(),
"slashRewardFraction": sp_runtime::Perbill::from_percent(10),
"stakers": stakers
},
}),
);
}
Expand Down
3 changes: 1 addition & 2 deletions node/src/chains/internal.keys.json
Original file line number Diff line number Diff line change
Expand Up @@ -123,13 +123,12 @@
"an7Xpup8pGMQAyVs1KFfbtVtZTHau2RwVv7ii5CNFmb1s8dCg",
"anBH2gEs72QEAc4ufppmXBjHSDrj995UHpattE956aUAYXFGJ"
],
"controller": "anAFMd7NCCekftTGrkhv9oymmZ4biPrmbMGj5mtvfXqeqkSST",
"endowments": [
["an5xJhYKVepTVKH3JqeRc126R4iAzV8As9DuXLW9EQkyzhgFf", 100000],
["an8MGkKQtjYr2KKPcP1EoNsUwQ6uwEJikZrTXHJ2f1gy8TV1N", 1000000],
["an9kSckzNGEy9eA9tAs9dPNfYEXf8LJCFBrVDchZVTfjcpxE7", 50000],
["anAW8EGrNU6bA6uMGnoXvr86YTFkB5hxtRitW2WePUv4p2986", 50000],
["anAkNQdVkbM5XgzFxtS6auRRBCv74R2gFhXoYSHi7pQAW74vG", 1440000]
["anAkNQdVkbM5XgzFxtS6auRRBCv74R2gFhXoYSHi7pQAW74vG", 100000000]
],
"stakes": [
"anAjuQVafzzr1PkHkJNq2WAu9LFE2iQwAtgEXc2VXbAyDBW85",
Expand Down
1 change: 1 addition & 0 deletions pallets/launch/src/data/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
// Add underlying data for stage here
pub mod v19;
6 changes: 6 additions & 0 deletions pallets/launch/src/data/v19.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
use crate::deposits::RawDepositStage;

use time_primitives::ANLOG;

pub const DEPOSIT_REWARD_POOL: RawDepositStage =
&[("an83ePjroFcSy8v7zBkaKXydhni4H88F3PG7w8vpVHfhb6Gof", 60_386_473 * ANLOG, None)];
8 changes: 7 additions & 1 deletion pallets/launch/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ pub mod pallet {
use sp_std::{vec, vec::Vec};

/// Updating this number will automatically execute the next launch stages on update
pub const LAUNCH_VERSION: u16 = 18;
pub const LAUNCH_VERSION: u16 = 19;
/// Wrapped version to support substrate interface as well
pub const STORAGE_VERSION: StorageVersion = StorageVersion::new(LAUNCH_VERSION);

Expand Down Expand Up @@ -94,6 +94,12 @@ pub mod pallet {
(17, 0, Stage::Retired),
// Prelaunch Deposit 4
(18, 3_636_364 * ANLOG, Stage::Retired),
// Bootstaking Month 1
(
19,
60_386_473 * ANLOG,
Stage::DepositFromVirtual(b"initiatives", data::v19::DEPOSIT_REWARD_POOL),
),
];

/// TODO: Difference to go to treasury:
Expand Down
1 change: 1 addition & 0 deletions pallets/launch/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ fn launch_ledger_validation() {

// Mint necessary virtual funds
mint_virtual(b"airdrop", 1_336_147_462_613_682_971);
mint_virtual(b"initiatives", 60_386_473 * ANLOG);
mint_virtual(b"ecosystem", 3_636_364 * ANLOG);

// Start new block to collect events
Expand Down
52 changes: 43 additions & 9 deletions runtime/src/configs/staking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,14 @@ use frame_support::{
pallet_prelude::Get,
parameter_types,
//traits::tokens::imbalance::ResolveTo,
traits::ConstU32,
traits::{ConstU32, Currency, ExistenceRequirement, OnUnbalanced, WithdrawReasons},
weights::Weight,
PalletId,
};

use sp_runtime::{
curve::PiecewiseLinear, transaction_validity::TransactionPriority, FixedU128, Perbill, Percent,
curve::PiecewiseLinear, traits::AccountIdConversion, transaction_validity::TransactionPriority,
FixedU128, Perbill, Percent,
};
use sp_std::prelude::*;

Expand All @@ -30,9 +31,9 @@ use time_primitives::BlockNumber;
use crate::{
deposit, weights, AccountId, Balance, Balances, BlockExecutionWeight, BondingDuration,
DelegatedStaking, ElectionProviderMultiPhase, EnsureRootOrHalfTechnical, EpochDuration,
NominationPools, Runtime, RuntimeBlockLength, RuntimeBlockWeights, RuntimeEvent,
RuntimeFreezeReason, RuntimeHoldReason, Session, SessionsPerEra, Staking, Timestamp,
TransactionPayment, VoterList, ANLOG,
NegativeImbalance, NominationPools, PositiveImbalance, Runtime, RuntimeBlockLength,
RuntimeBlockWeights, RuntimeEvent, RuntimeFreezeReason, RuntimeHoldReason, Session,
SessionsPerEra, Staking, Timestamp, TransactionPayment, VoterList, ANLOG,
};

parameter_types! {
Expand Down Expand Up @@ -214,9 +215,39 @@ impl pallet_election_provider_multi_phase::Config for Runtime {
type WeightInfo = pallet_election_provider_multi_phase::weights::SubstrateWeight<Self>;
}

/// Virtual reward pool wallet
pub struct RewardPool;
impl RewardPool {
/// Return internal virtual wallet id
fn account_id() -> AccountId {
PalletId(*b"timerwrd").into_account_truncating()
}
}

impl OnUnbalanced<PositiveImbalance> for RewardPool {
/// Take rewards from special rewards wallet, otherwise mint it via drop
fn on_nonzero_unbalanced(imbalance: PositiveImbalance) {
let _ = Balances::settle(
&Self::account_id(),
imbalance,
WithdrawReasons::TRANSFER,
ExistenceRequirement::AllowDeath,
);
}
}

/// Additional wrapper around reward pool to return funds
pub struct ToRewardPool;
impl OnUnbalanced<NegativeImbalance> for ToRewardPool {
/// Return unspent reward to reward pool
fn on_nonzero_unbalanced(imbalance: NegativeImbalance) {
Balances::resolve_creating(&RewardPool::account_id(), imbalance);
}
}

pallet_staking_reward_curve::build! {
const REWARD_CURVE: PiecewiseLinear<'static> = curve!(
min_inflation: 0_020_000,
min_inflation: 0_030_000,
max_inflation: 0_080_000,
ideal_stake: 0_600_000,
falloff: 0_050_000,
Expand Down Expand Up @@ -254,6 +285,7 @@ parameter_types! {
/// Upper limit on the number of NPOS nominations.
const MAX_QUOTA_NOMINATIONS: u32 = 16;

/// Configuration of benchmarking bounds
pub struct StakingBenchmarkingConfig;
impl pallet_staking::BenchmarkingConfig for StakingBenchmarkingConfig {
type MaxNominators = ConstU32<1000>;
Expand All @@ -268,17 +300,19 @@ impl pallet_staking::Config for Runtime {
type CurrencyBalance = Balance;
type UnixTime = Timestamp;
type CurrencyToVote = sp_staking::currency_to_vote::U128CurrencyToVote;
type RewardRemainder = (); //Treasury;
type RewardRemainder = ToRewardPool;
type RuntimeEvent = RuntimeEvent;
type Slash = (); //Treasury; // send the slashed funds to the treasury.
type Reward = (); // rewards are minted from the void
/// Pay rewards from reward pool, otherwise mint them.
type Reward = RewardPool;
type SessionsPerEra = SessionsPerEra;
type BondingDuration = BondingDuration;
type SlashDeferDuration = SlashDeferDuration;
/// A majority of the council can cancel the slash.
type AdminOrigin = EnsureRootOrHalfTechnical;
type SessionInterface = Self;
type EraPayout = (); //pallet_staking::ConvertCurve<RewardCurve>;
/// Inflation curve that optimizes returned rewards
type EraPayout = pallet_staking::ConvertCurve<RewardCurve>;
type MaxExposurePageSize = ConstU32<256>;
type NextNewSession = Session;
type ElectionProvider = ElectionProviderMultiPhase;
Expand Down
13 changes: 6 additions & 7 deletions runtime/src/configs/tokenomics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ use crate::{
RuntimeHoldReason, System, ANLOG, MAX_BLOCK_LENGTH,
};
#[cfg(feature = "testnet")]
use crate::{AccountId, Authorship, Treasury};
use crate::{Authorship, NegativeImbalance, Treasury};
#[cfg(feature = "testnet")]
use frame_support::traits::Currency;
use time_primitives::{MICROANLOG, MILLIANLOG};
Expand Down Expand Up @@ -61,9 +61,10 @@ pub const MAXIMUM_BLOCK_WEIGHT_SECONDS: u64 = 2;
impl WeightToFeePolynomial for WeightToFee {
type Balance = Balance;
fn polynomial() -> WeightToFeeCoefficients<Self::Balance> {
// Quadratic term maps max weight to max quadratic fee
let q_2: Balance = MAX_QUADRATIC_WEIGHT_FEE * MAX_QUADRATIC_WEIGHT_FEE;
let p_2 = WEIGHT_REF_TIME_PER_SECOND.saturating_mul(MAXIMUM_BLOCK_WEIGHT_SECONDS) as u128;
// in Timechain, extrinsic base weight (smallest non-zero weight) is mapped to MILLIANLOG:
// Linear term map linear minimum to base weight
let p_1 = MIN_LINEAR_WEIGHT_FEE;
let q_1 = Balance::from(ExtrinsicBaseWeight::get().ref_time());
smallvec![
Expand All @@ -90,11 +91,12 @@ pub struct LengthToFee;
impl WeightToFeePolynomial for LengthToFee {
type Balance = Balance;
fn polynomial() -> WeightToFeeCoefficients<Self::Balance> {
// Quadratic term maps max weight to max quadratic fee
let q_2 = MAX_QUADRATIC_LENGTH_FEE * MAX_QUADRATIC_WEIGHT_FEE;
let p_2 = MAX_BLOCK_LENGTH as u128;
// in Timechain, extrinsic base weight (smallest non-zero weight) is mapped to MILLIANLOG:
// Linear minimum is mapped to size of smallest transaction
let p_1 = MIN_LINEAR_LENGTH_FEE;
let q_1 = Balance::from(ExtrinsicBaseWeight::get().ref_time());
let q_1 = 2;
smallvec![
WeightToFeeCoefficient {
degree: 2,
Expand Down Expand Up @@ -134,9 +136,6 @@ impl OnUnbalanced<NegativeImbalance> for Author {
}
}

#[cfg(feature = "testnet")]
type NegativeImbalance = <Balances as Currency<AccountId>>::NegativeImbalance;

pub struct DealWithFees;
#[cfg(feature = "testnet")]
impl OnUnbalanced<NegativeImbalance> for DealWithFees {
Expand Down
5 changes: 5 additions & 0 deletions runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ use polkadot_sdk::*;

use frame_support::{
parameter_types,
traits::Currency,
weights::{constants::WEIGHT_REF_TIME_PER_SECOND, Weight},
};
use pallet_session::historical as pallet_session_historical;
Expand Down Expand Up @@ -220,6 +221,10 @@ pub type Executive = frame_executive::Executive<
Migrations,
>;

// Useful types when handeling currency
pub type NegativeImbalance = <Balances as Currency<AccountId>>::NegativeImbalance;
pub type PositiveImbalance = <Balances as Currency<AccountId>>::PositiveImbalance;

/// Max size for serialized extrinsic params for this testing runtime.
/// This is a quite arbitrary but empirically battle tested value.
#[cfg(test)]
Expand Down

0 comments on commit ddae285

Please sign in to comment.