1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
// This file is part of Darwinia.
//
// Copyright (C) 2018-2022 Darwinia Network
// SPDX-License-Identifier: GPL-3.0
//
// Darwinia is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Darwinia is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Darwinia. If not, see <https://www.gnu.org/licenses/>.

// --- github ---
use substrate_fixed::{
	transcendental::{pow, sqrt},
	types::I64F64,
};
// --- paritytech ---
use frame_support::log;
use sp_arithmetic::helpers_128bit;
use sp_core::U256;
use sp_runtime::{Perbill, SaturatedConversion};
// --- darwinia-network ---
use crate::*;

// Milliseconds per year for the Julian year (365.25 days).
pub const MILLISECONDS_PER_YEAR: TsInMs = (366 * 24 * 60 * 60) * 1000;

/// The total payout to all validators (and their nominators) per era and maximum payout.
///
/// Defined as such:
/// `staker-payout = yearly_inflation(npos_token_staked / total_tokens) * total_tokens /
/// era_per_year` `maximum-payout = max_yearly_inflation * total_tokens / era_per_year`
///
/// `era_duration` is expressed in millisecond.
pub fn compute_total_payout<T: Config>(
	era_duration: TsInMs,
	living_time: TsInMs,
	total_left: RingBalance<T>,
	payout_fraction: Perbill,
) -> (RingBalance<T>, RingBalance<T>) {
	log::info!(
		target: "darwinia-staking",
		"era_duration: {}, living_time: {}, total_left: {:?}, payout_fraction: {:?}",
		era_duration,
		living_time,
		total_left,
		payout_fraction,
	);

	let inflation = {
		let maximum = {
			let total_left = total_left.saturated_into::<Balance>();

			helpers_128bit::multiply_by_rational(
				total_left,
				era_duration as _,
				MILLISECONDS_PER_YEAR as _,
			)
			.unwrap_or(0)
		};
		let year = {
			let year = living_time / MILLISECONDS_PER_YEAR + 1;

			year as u32
		};

		compute_inflation(maximum, year).unwrap_or(0)
	};
	let payout = payout_fraction * inflation;

	(
		<RingBalance<T>>::saturated_from::<Balance>(payout),
		<RingBalance<T>>::saturated_from::<Balance>(inflation),
	)
}

/// Formula:
/// 	1 - (99 / 100) ^ sqrt(year)
pub fn compute_inflation(maximum: Balance, year: u32) -> Option<u128> {
	type F64 = I64F64;

	if let Ok(a) = sqrt::<F64, F64>(F64::from_num(year)) {
		let b: F64 = F64::from_num(99) / 100;

		if let Ok(c) = pow::<F64, F64>(b, a) {
			let d: F64 = F64::from_num(1) - c;
			let e: F64 = F64::from_num(maximum) * d;

			#[cfg(test)]
			{
				let a_f64 = (year as f64).sqrt();
				// eprintln!("{}\n{}", a, a_f64);
				let b_f64 = 0.99_f64;
				// eprintln!("{}\n{}", b, b_f64);
				let c_f64 = b_f64.powf(a_f64);
				// eprintln!("{}\n{}", c, c_f64);
				let d_f64 = 1.00_f64 - c_f64;
				// eprintln!("{}\n{}", d, d_f64);
				let e_f64 = maximum as f64 * d_f64;
				// eprintln!("{}\n{}", e, e_f64);

				sp_runtime::assert_eq_error_rate!(
					e.floor(),
					e_f64 as u128,
					if e_f64 == 0.00_f64 { 0 } else { 3 }
				);
			}

			return Some(e.floor().to_num());
		} else {
			log::error!(target: "darwniia-staking", "Compute Inflation Failed at Step 1");
		}
	} else {
		log::error!(target: "darwniia-staking", "Compute Inflation Failed at Step 0");
	}

	None
}

// consistent with the formula in smart contract in evolution land which can be found in
// https://github.com/evolutionlandorg/bank/blob/master/contracts/GringottsBank.sol#L280
pub fn compute_kton_reward<T: Config>(value: RingBalance<T>, months: u8) -> KtonBalance<T> {
	let value: U256 = value.saturated_into::<Balance>().into();
	let n = U256::from(67).pow(U256::from(months));
	let d = U256::from(66).pow(U256::from(months));
	let quot = n / d;
	let rem = n % d;
	let precision: U256 = 1000.into();

	// `((quot - 1) * precision + rem * precision / d)` is 197 when `months` is 12
	// The default interest is 1_000, so directly use 1_970_000 here instead `interest * 197 * 10^7`
	(value * (precision * (quot - 1) + precision * rem / d) / U256::from(1_970_000))
		.as_u128()
		.saturated_into()
}