timechain_runtime/configs/
tokenomics.rsuse smallvec::smallvec;
use polkadot_sdk::*;
use frame_support::weights::{
constants::WEIGHT_REF_TIME_PER_SECOND, WeightToFeeCoefficient, WeightToFeeCoefficients,
WeightToFeePolynomial,
};
use frame_support::{
parameter_types,
traits::{ConstU32, WithdrawReasons},
};
#[cfg(feature = "testnet")]
use frame_support::{
traits::tokens::fungible::Balanced,
traits::{Imbalance, OnUnbalanced},
PalletId,
};
#[cfg(feature = "testnet")]
use sp_runtime::traits::AccountIdConversion;
use sp_runtime::{
traits::{Bounded, ConvertInto},
FixedPointNumber, Perbill, Perquintill,
};
pub use pallet_transaction_payment::{FungibleAdapter, Multiplier, TargetedFeeAdjustment};
use crate::{
weights, Balance, Balances, ExtrinsicBaseWeight, Runtime, RuntimeEvent, RuntimeFreezeReason,
RuntimeHoldReason, System, ANLOG, MAX_BLOCK_LENGTH,
};
#[cfg(feature = "testnet")]
use crate::{Authorship, RuntimeCredit};
#[cfg(feature = "testnet")]
use time_primitives::AccountId;
use time_primitives::{MICROANLOG, MILLIANLOG};
pub struct WeightToFee;
pub const MIN_LINEAR_WEIGHT_FEE: Balance = MILLIANLOG;
pub const MAX_QUADRATIC_WEIGHT_FEE: Balance = 90_000 * ANLOG;
pub const MAXIMUM_BLOCK_WEIGHT_SECONDS: u64 = 2;
impl WeightToFeePolynomial for WeightToFee {
type Balance = Balance;
fn polynomial() -> WeightToFeeCoefficients<Self::Balance> {
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;
let p_1 = MIN_LINEAR_WEIGHT_FEE;
let q_1 = Balance::from(ExtrinsicBaseWeight::get().ref_time());
smallvec![
WeightToFeeCoefficient {
degree: 2,
negative: false,
coeff_frac: Perbill::from_rational(p_2 % q_2, q_2),
coeff_integer: p_2 / q_2,
},
WeightToFeeCoefficient {
degree: 1,
negative: false,
coeff_frac: Perbill::from_rational(p_1 % q_1, q_1),
coeff_integer: p_1 / q_1,
}
]
}
}
pub const MIN_LINEAR_LENGTH_FEE: Balance = MICROANLOG;
pub const MAX_QUADRATIC_LENGTH_FEE: Balance = 10_000 * ANLOG;
pub struct LengthToFee;
impl WeightToFeePolynomial for LengthToFee {
type Balance = Balance;
fn polynomial() -> WeightToFeeCoefficients<Self::Balance> {
let q_2 = MAX_QUADRATIC_LENGTH_FEE * MAX_QUADRATIC_WEIGHT_FEE;
let p_2 = MAX_BLOCK_LENGTH as u128;
let p_1 = MIN_LINEAR_LENGTH_FEE;
let q_1 = 2;
smallvec![
WeightToFeeCoefficient {
degree: 2,
negative: false,
coeff_frac: Perbill::from_rational(p_2 % q_2, q_2),
coeff_integer: p_2 / q_2,
},
WeightToFeeCoefficient {
degree: 1,
negative: false,
coeff_frac: Perbill::from_rational(p_1 % q_1, q_1),
coeff_integer: p_1 / q_1,
}
]
}
}
#[cfg(not(feature = "runtime-benchmarks"))]
parameter_types! {
pub const ExistentialDeposit: Balance = 1 * ANLOG;
}
#[cfg(feature = "runtime-benchmarks")]
parameter_types! {
pub const ExistentialDeposit: Balance = 500;
}
pub struct Treasury;
#[cfg(feature = "testnet")]
impl Treasury {
fn account_id() -> AccountId {
PalletId(*b"timetrsy").into_account_truncating()
}
}
#[cfg(feature = "testnet")]
impl OnUnbalanced<RuntimeCredit> for Treasury {
fn on_nonzero_unbalanced(amount: RuntimeCredit) {
let _ = Balances::resolve(&Self::account_id(), amount);
}
}
pub struct Author;
#[cfg(feature = "testnet")]
impl OnUnbalanced<RuntimeCredit> for Author {
fn on_nonzero_unbalanced(amount: RuntimeCredit) {
if let Some(author) = Authorship::author() {
let _ = Balances::resolve(&author, amount);
}
}
}
pub struct DealWithFees;
#[cfg(feature = "testnet")]
impl OnUnbalanced<RuntimeCredit> for DealWithFees {
fn on_unbalanceds(mut fees_then_tips: impl Iterator<Item = RuntimeCredit>) {
if let Some(fees) = fees_then_tips.next() {
let mut split = fees.ration(80, 20);
if let Some(tips) = fees_then_tips.next() {
tips.ration_merge_into(80, 20, &mut split);
}
Treasury::on_unbalanced(split.0);
Author::on_unbalanced(split.1);
}
}
}
parameter_types! {
pub const MaxLocks: u32 = 50;
pub const MaxReserves: u32 = 50;
}
impl pallet_balances::Config for Runtime {
type RuntimeHoldReason = RuntimeHoldReason;
type RuntimeFreezeReason = RuntimeFreezeReason;
type MaxLocks = MaxLocks;
type MaxReserves = MaxReserves;
type ReserveIdentifier = [u8; 8];
type Balance = Balance;
type DustRemoval = ();
type RuntimeEvent = RuntimeEvent;
type ExistentialDeposit = ExistentialDeposit;
type AccountStore = frame_system::Pallet<Runtime>;
type WeightInfo = weights::pallet_balances::WeightInfo<Runtime>;
type FreezeIdentifier = RuntimeFreezeReason;
type MaxFreezes = ConstU32<1>;
type DoneSlashHandler = ();
}
parameter_types! {
pub const OperationalFeeMultiplier: u8 = 5;
pub const TargetBlockFullness: Perquintill = Perquintill::from_percent(25);
pub AdjustmentVariable: Multiplier = Multiplier::saturating_from_rational(1, 100_000);
pub MinimumMultiplier: Multiplier = Multiplier::saturating_from_rational(1, 1_000_000_000u128);
pub MaximumMultiplier: Multiplier = Bounded::max_value();
}
pub type SlowAdjustingFeeUpdate<R> = TargetedFeeAdjustment<
R,
TargetBlockFullness,
AdjustmentVariable,
MinimumMultiplier,
MaximumMultiplier,
>;
#[allow(deprecated)]
impl pallet_transaction_payment::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
#[cfg(not(feature = "testnet"))]
type OnChargeTransaction = FungibleAdapter<Balances, ()>;
#[cfg(feature = "testnet")]
type OnChargeTransaction = FungibleAdapter<Balances, DealWithFees>;
type OperationalFeeMultiplier = OperationalFeeMultiplier;
type WeightToFee = WeightToFee;
type LengthToFee = LengthToFee;
type FeeMultiplierUpdate = SlowAdjustingFeeUpdate<Self>;
type WeightInfo = pallet_transaction_payment::weights::SubstrateWeight<Runtime>;
}
parameter_types! {
pub const MinVestedTransfer: Balance = 1 * ANLOG;
pub UnvestedFundsAllowedWithdrawReasons: WithdrawReasons =
WithdrawReasons::except(WithdrawReasons::TRANSFER | WithdrawReasons::RESERVE);
}
impl pallet_vesting::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type Currency = Balances;
type BlockNumberToBalance = ConvertInto;
type MinVestedTransfer = MinVestedTransfer;
type WeightInfo = pallet_vesting::weights::SubstrateWeight<Runtime>;
type UnvestedFundsAllowedWithdrawReasons = UnvestedFundsAllowedWithdrawReasons;
type BlockNumberProvider = System;
const MAX_VESTING_SCHEDULES: u32 = 28;
}
#[cfg(test)]
mod test {
use polkadot_sdk::*;
use time_primitives::{MICROANLOG, MILLIANLOG};
use frame_support::{
dispatch::{DispatchClass, DispatchInfo},
traits::OnFinalize,
weights::{Weight, WeightToFee as WeightToFeeT},
};
use pallet_transaction_payment::Multiplier;
use separator::Separatable;
use sp_runtime::BuildStorage;
use super::{MinimumMultiplier, SlowAdjustingFeeUpdate, TargetBlockFullness};
use crate::{
ExtrinsicBaseWeight, Runtime, RuntimeBlockWeights, System, TransactionPayment,
MAXIMUM_BLOCK_WEIGHT,
};
#[test]
fn full_block_fee_is_correct() {
let full_block = crate::WeightToFee::weight_to_fee(&MAXIMUM_BLOCK_WEIGHT);
println!("FULL BLOCK Fee: {}", full_block);
assert!(full_block >= 5_000 * MILLIANLOG);
assert!(full_block <= 10_000 * MILLIANLOG);
}
#[test]
fn extrinsic_base_fee_is_correct() {
println!("Base: {}", ExtrinsicBaseWeight::get());
let x = crate::WeightToFee::weight_to_fee(&ExtrinsicBaseWeight::get());
let y = MILLIANLOG;
assert!(x.max(y) - x.min(y) < MICROANLOG);
}
fn run_with_system_weight<F>(w: Weight, mut assertions: F)
where
F: FnMut(),
{
let mut t: sp_io::TestExternalities = frame_system::GenesisConfig::<Runtime>::default()
.build_storage()
.unwrap()
.into();
t.execute_with(|| {
System::set_block_consumed_resources(w, 0);
assertions()
});
}
#[test]
fn multiplier_can_grow_from_zero() {
let minimum_multiplier = MinimumMultiplier::get();
let target = TargetBlockFullness::get()
* RuntimeBlockWeights::get().get(DispatchClass::Normal).max_total.unwrap();
run_with_system_weight(target.saturating_mul(101) / 100, || {
use sp_runtime::traits::Convert;
let next = SlowAdjustingFeeUpdate::<Runtime>::convert(minimum_multiplier);
assert!(next > minimum_multiplier, "{:?} !>= {:?}", next, minimum_multiplier);
})
}
#[test]
fn multiplier_growth_simulator() {
let mut multiplier = MinimumMultiplier::get();
let block_weight = RuntimeBlockWeights::get().get(DispatchClass::Normal).max_total.unwrap();
let mut blocks = 0;
let mut fees_paid = 0;
let info = DispatchInfo {
call_weight: Weight::MAX,
..Default::default()
};
let mut t: sp_io::TestExternalities = frame_system::GenesisConfig::<Runtime>::default()
.build_storage()
.unwrap()
.into();
t.execute_with(|| {
frame_system::Pallet::<Runtime>::set_block_consumed_resources(Weight::MAX, 0);
pallet_transaction_payment::NextFeeMultiplier::<Runtime>::set(MinimumMultiplier::get());
});
while multiplier <= Multiplier::from_u32(1) {
t.execute_with(|| {
let fee = TransactionPayment::compute_fee(0, &info, 0);
fees_paid += fee;
System::set_block_consumed_resources(block_weight, 0);
TransactionPayment::on_finalize(1);
let next = TransactionPayment::next_fee_multiplier();
assert!(next > multiplier, "{:?} !>= {:?}", next, multiplier);
multiplier = next;
println!(
"block = {} / multiplier {:?} / fee = {:?} / fess so far {:?}",
blocks,
multiplier,
fee.separated_string(),
fees_paid.separated_string()
);
});
blocks += 1;
}
}
}