clone_solana_inflation/
lib.rs

1//! configuration for network inflation
2#![cfg_attr(feature = "frozen-abi", feature(min_specialization))]
3#[cfg(feature = "serde")]
4use serde_derive::{Deserialize, Serialize};
5
6#[cfg_attr(
7    feature = "frozen-abi",
8    derive(clone_solana_frozen_abi_macro::AbiExample)
9)]
10#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
11#[derive(PartialEq, Clone, Debug, Copy)]
12#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
13pub struct Inflation {
14    /// Initial inflation percentage, from time=0
15    pub initial: f64,
16
17    /// Terminal inflation percentage, to time=INF
18    pub terminal: f64,
19
20    /// Rate per year, at which inflation is lowered until reaching terminal
21    ///  i.e. inflation(year) == MAX(terminal, initial*((1-taper)^year))
22    pub taper: f64,
23
24    /// Percentage of total inflation allocated to the foundation
25    pub foundation: f64,
26    /// Duration of foundation pool inflation, in years
27    pub foundation_term: f64,
28
29    /// DEPRECATED, this field is currently unused
30    __unused: f64,
31}
32
33const DEFAULT_INITIAL: f64 = 0.08;
34const DEFAULT_TERMINAL: f64 = 0.015;
35const DEFAULT_TAPER: f64 = 0.15;
36const DEFAULT_FOUNDATION: f64 = 0.05;
37const DEFAULT_FOUNDATION_TERM: f64 = 7.0;
38
39impl Default for Inflation {
40    fn default() -> Self {
41        Self {
42            initial: DEFAULT_INITIAL,
43            terminal: DEFAULT_TERMINAL,
44            taper: DEFAULT_TAPER,
45            foundation: DEFAULT_FOUNDATION,
46            foundation_term: DEFAULT_FOUNDATION_TERM,
47            __unused: 0.0,
48        }
49    }
50}
51
52impl Inflation {
53    pub fn new_disabled() -> Self {
54        Self {
55            initial: 0.0,
56            terminal: 0.0,
57            taper: 0.0,
58            foundation: 0.0,
59            foundation_term: 0.0,
60            __unused: 0.0,
61        }
62    }
63
64    // fixed inflation rate at `validator` percentage for staking rewards, and none for foundation
65    pub fn new_fixed(validator: f64) -> Self {
66        Self {
67            initial: validator,
68            terminal: validator,
69            taper: 1.0,
70            foundation: 0.0,
71            foundation_term: 0.0,
72            __unused: 0.0,
73        }
74    }
75
76    pub fn pico() -> Self {
77        Self::new_fixed(0.0001) // 0.01% inflation
78    }
79
80    pub fn full() -> Self {
81        Self {
82            initial: DEFAULT_INITIAL,
83            terminal: DEFAULT_TERMINAL,
84            taper: DEFAULT_TAPER,
85            foundation: 0.0,
86            foundation_term: 0.0,
87            __unused: 0.0,
88        }
89    }
90
91    /// inflation rate at year
92    pub fn total(&self, year: f64) -> f64 {
93        assert!(year >= 0.0);
94        let tapered = self.initial * ((1.0 - self.taper).powf(year));
95
96        if tapered > self.terminal {
97            tapered
98        } else {
99            self.terminal
100        }
101    }
102
103    /// portion of total that goes to validators
104    pub fn validator(&self, year: f64) -> f64 {
105        self.total(year) - self.foundation(year)
106    }
107
108    /// portion of total that goes to foundation
109    pub fn foundation(&self, year: f64) -> f64 {
110        if year < self.foundation_term {
111            self.total(year) * self.foundation
112        } else {
113            0.0
114        }
115    }
116}
117
118#[cfg(test)]
119mod tests {
120    use super::*;
121
122    #[test]
123    fn test_inflation_basic() {
124        let inflation = Inflation::default();
125
126        let mut last = inflation.total(0.0);
127
128        for year in &[0.1, 0.5, 1.0, DEFAULT_FOUNDATION_TERM, 100.0] {
129            let total = inflation.total(*year);
130            assert_eq!(
131                total,
132                inflation.validator(*year) + inflation.foundation(*year)
133            );
134            assert!(total < last);
135            assert!(total >= inflation.terminal);
136            last = total;
137        }
138        assert_eq!(last, inflation.terminal);
139    }
140
141    #[test]
142    fn test_inflation_fixed() {
143        let inflation = Inflation::new_fixed(0.001);
144        for year in &[0.1, 0.5, 1.0, DEFAULT_FOUNDATION_TERM, 100.0] {
145            assert_eq!(inflation.total(*year), 0.001);
146        }
147    }
148}