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