use crate::pallet::{BalanceOf, Config, Pallet};
use frame_support::traits::{Currency, Get};
use parity_scale_codec::{Decode, Encode, MaxEncodedLen};
use scale_info::TypeInfo;
use serde::{Deserialize, Serialize};
use sp_runtime::PerThing;
use sp_runtime::{Perbill, RuntimeDebug};
use substrate_fixed::transcendental::pow as floatpow;
use substrate_fixed::types::I64F64;
const MS_PER_YEAR: u64 = 31_557_600_000;
fn rounds_per_year<T: Config>() -> u32 {
let blocks_per_round = <Pallet<T>>::round().length as u64;
let blocks_per_year = MS_PER_YEAR / T::BlockTime::get();
(blocks_per_year / blocks_per_round) as u32
}
#[derive(
Eq,
PartialEq,
Clone,
Copy,
Encode,
Decode,
Default,
Deserialize,
RuntimeDebug,
MaxEncodedLen,
Serialize,
TypeInfo,
)]
pub struct Range<T> {
pub min: T,
pub ideal: T,
pub max: T,
}
impl<T: Ord> Range<T> {
pub fn is_valid(&self) -> bool {
self.max >= self.ideal && self.ideal >= self.min
}
}
impl<T: Ord + Copy> From<T> for Range<T> {
fn from(other: T) -> Range<T> {
Range {
min: other,
ideal: other,
max: other,
}
}
}
pub fn perbill_annual_to_perbill_round(
annual: Range<Perbill>,
rounds_per_year: u32,
) -> Range<Perbill> {
let exponent = I64F64::from_num(1) / I64F64::from_num(rounds_per_year);
let annual_to_round = |annual: Perbill| -> Perbill {
let x = I64F64::from_num(annual.deconstruct()) / I64F64::from_num(Perbill::ACCURACY);
let y: I64F64 = floatpow(I64F64::from_num(1) + x, exponent)
.expect("Cannot overflow since rounds_per_year is u32 so worst case 0; QED");
Perbill::from_parts(
((y - I64F64::from_num(1)) * I64F64::from_num(Perbill::ACCURACY))
.ceil()
.to_num::<u32>(),
)
};
Range {
min: annual_to_round(annual.min),
ideal: annual_to_round(annual.ideal),
max: annual_to_round(annual.max),
}
}
pub fn annual_to_round<T: Config>(annual: Range<Perbill>) -> Range<Perbill> {
let periods = rounds_per_year::<T>();
perbill_annual_to_perbill_round(annual, periods)
}
pub fn round_issuance_range<T: Config>(round: Range<Perbill>) -> Range<BalanceOf<T>> {
let circulating = T::Currency::total_issuance();
Range {
min: round.min * circulating,
ideal: round.ideal * circulating,
max: round.max * circulating,
}
}
#[derive(
Eq, PartialEq, Clone, Encode, Decode, Default, Deserialize, RuntimeDebug, Serialize, TypeInfo,
)]
pub struct InflationInfo<Balance> {
pub expect: Range<Balance>,
pub annual: Range<Perbill>,
pub round: Range<Perbill>,
}
impl<Balance> InflationInfo<Balance> {
pub fn new<T: Config>(
annual: Range<Perbill>,
expect: Range<Balance>,
) -> InflationInfo<Balance> {
InflationInfo {
expect,
annual,
round: annual_to_round::<T>(annual),
}
}
pub fn set_round_from_annual<T: Config>(&mut self, new: Range<Perbill>) {
self.round = annual_to_round::<T>(new);
}
pub fn reset_round<T: Config>(&mut self, new_length: u32) {
let periods = (MS_PER_YEAR / T::BlockTime::get()) / (new_length as u64);
self.round = perbill_annual_to_perbill_round(self.annual, periods as u32);
}
pub fn set_expectations(&mut self, expect: Range<Balance>) {
self.expect = expect;
}
}
#[cfg(test)]
mod tests {
use super::*;
fn mock_annual_to_round(annual: Range<Perbill>, rounds_per_year: u32) -> Range<Perbill> {
perbill_annual_to_perbill_round(annual, rounds_per_year)
}
fn mock_round_issuance_range(
circulating: u128,
round: Range<Perbill>,
) -> Range<u128> {
Range {
min: round.min * circulating,
ideal: round.ideal * circulating,
max: round.max * circulating,
}
}
#[test]
fn simple_issuance_conversion() {
let expected_round_issuance_range: Range<u128> = Range {
min: 48_909,
ideal: 48_909,
max: 48_909,
};
let schedule = Range {
min: Perbill::from_percent(5),
ideal: Perbill::from_percent(5),
max: Perbill::from_percent(5),
};
assert_eq!(
expected_round_issuance_range,
mock_round_issuance_range(10_000_000, mock_annual_to_round(schedule, 10))
);
}
#[test]
fn range_issuance_conversion() {
let expected_round_issuance_range: Range<u128> = Range {
min: 29_603,
ideal: 39298,
max: 48_909,
};
let schedule = Range {
min: Perbill::from_percent(3),
ideal: Perbill::from_percent(4),
max: Perbill::from_percent(5),
};
assert_eq!(
expected_round_issuance_range,
mock_round_issuance_range(10_000_000, mock_annual_to_round(schedule, 10))
);
}
#[test]
fn expected_parameterization() {
let expected_round_schedule: Range<u128> = Range {
min: 45,
ideal: 56,
max: 56,
};
let schedule = Range {
min: Perbill::from_percent(4),
ideal: Perbill::from_percent(5),
max: Perbill::from_percent(5),
};
assert_eq!(
expected_round_schedule,
mock_round_issuance_range(10_000_000, mock_annual_to_round(schedule, 8766))
);
}
#[test]
fn inflation_does_not_panic_at_round_number_limit() {
let schedule = Range {
min: Perbill::from_percent(100),
ideal: Perbill::from_percent(100),
max: Perbill::from_percent(100),
};
mock_round_issuance_range(u32::MAX.into(), mock_annual_to_round(schedule, u32::MAX));
mock_round_issuance_range(u64::MAX.into(), mock_annual_to_round(schedule, u32::MAX));
mock_round_issuance_range(u128::MAX.into(), mock_annual_to_round(schedule, u32::MAX));
mock_round_issuance_range(u32::MAX.into(), mock_annual_to_round(schedule, 1));
mock_round_issuance_range(u64::MAX.into(), mock_annual_to_round(schedule, 1));
mock_round_issuance_range(u128::MAX.into(), mock_annual_to_round(schedule, 1));
}
}