gemachain_program/
rent.rs1#![allow(clippy::integer_arithmetic)]
2use crate::clock::DEFAULT_SLOTS_PER_EPOCH;
4
5#[repr(C)]
6#[derive(Serialize, Deserialize, PartialEq, Clone, Copy, Debug, AbiExample)]
7pub struct Rent {
8 pub carats_per_byte_year: u64,
10
11 pub exemption_threshold: f64,
13
14 pub burn_percent: u8,
16}
17
18pub const DEFAULT_CARATS_PER_BYTE_YEAR: u64 = 1_000_000_000 / 100 * 365 / (1024 * 1024);
24
25pub const DEFAULT_EXEMPTION_THRESHOLD: f64 = 2.0;
27
28pub const DEFAULT_BURN_PERCENT: u8 = 50;
30
31pub const ACCOUNT_STORAGE_OVERHEAD: u64 = 128;
33
34impl Default for Rent {
35 fn default() -> Self {
36 Self {
37 carats_per_byte_year: DEFAULT_CARATS_PER_BYTE_YEAR,
38 exemption_threshold: DEFAULT_EXEMPTION_THRESHOLD,
39 burn_percent: DEFAULT_BURN_PERCENT,
40 }
41 }
42}
43
44impl Rent {
45 pub fn calculate_burn(&self, rent_collected: u64) -> (u64, u64) {
47 let burned_portion = (rent_collected * u64::from(self.burn_percent)) / 100;
48 (burned_portion, rent_collected - burned_portion)
49 }
50 pub fn minimum_balance(&self, data_len: usize) -> u64 {
56 let bytes = data_len as u64;
57 (((ACCOUNT_STORAGE_OVERHEAD + bytes) * self.carats_per_byte_year) as f64
58 * self.exemption_threshold) as u64
59 }
60
61 pub fn is_exempt(&self, balance: u64, data_len: usize) -> bool {
63 balance >= self.minimum_balance(data_len)
64 }
65
66 pub fn due(&self, balance: u64, data_len: usize, years_elapsed: f64) -> (u64, bool) {
68 if self.is_exempt(balance, data_len) {
69 (0, true)
70 } else {
71 (
72 ((self.carats_per_byte_year * (data_len as u64 + ACCOUNT_STORAGE_OVERHEAD))
73 as f64
74 * years_elapsed) as u64,
75 false,
76 )
77 }
78 }
79
80 pub fn free() -> Self {
81 Self {
82 carats_per_byte_year: 0,
83 ..Rent::default()
84 }
85 }
86
87 pub fn with_slots_per_epoch(slots_per_epoch: u64) -> Self {
88 let ratio = slots_per_epoch as f64 / DEFAULT_SLOTS_PER_EPOCH as f64;
89 let exemption_threshold = DEFAULT_EXEMPTION_THRESHOLD as f64 * ratio;
90 let carats_per_byte_year = (DEFAULT_CARATS_PER_BYTE_YEAR as f64 / ratio) as u64;
91 Self {
92 carats_per_byte_year,
93 exemption_threshold,
94 ..Self::default()
95 }
96 }
97}
98
99#[cfg(test)]
100mod tests {
101 use super::*;
102
103 #[test]
104 fn test_due() {
105 let default_rent = Rent::default();
106
107 assert_eq!(
108 default_rent.due(0, 2, 1.2),
109 (
110 (((2 + ACCOUNT_STORAGE_OVERHEAD) * DEFAULT_CARATS_PER_BYTE_YEAR) as f64 * 1.2)
111 as u64,
112 DEFAULT_CARATS_PER_BYTE_YEAR == 0
113 )
114 );
115 assert_eq!(
116 default_rent.due(
117 (((2 + ACCOUNT_STORAGE_OVERHEAD) * DEFAULT_CARATS_PER_BYTE_YEAR) as f64
118 * DEFAULT_EXEMPTION_THRESHOLD) as u64,
119 2,
120 1.2
121 ),
122 (0, true)
123 );
124
125 let custom_rent = Rent {
126 carats_per_byte_year: 5,
127 exemption_threshold: 2.5,
128 ..Rent::default()
129 };
130
131 assert_eq!(
132 custom_rent.due(0, 2, 1.2),
133 (
134 (((2 + ACCOUNT_STORAGE_OVERHEAD) * custom_rent.carats_per_byte_year) as f64 * 1.2)
135 as u64,
136 false
137 )
138 );
139
140 assert_eq!(
141 custom_rent.due(
142 (((2 + ACCOUNT_STORAGE_OVERHEAD) * custom_rent.carats_per_byte_year) as f64
143 * custom_rent.exemption_threshold) as u64,
144 2,
145 1.2
146 ),
147 (0, true)
148 );
149 }
150
151 #[ignore]
152 #[test]
153 #[should_panic]
154 fn show_rent_model() {
155 use crate::{clock::*, sysvar::Sysvar};
156
157 const SECONDS_PER_YEAR: f64 = 365.242_199 * 24.0 * 60.0 * 60.0;
158 const SLOTS_PER_YEAR: f64 = SECONDS_PER_YEAR / DEFAULT_S_PER_SLOT;
159
160 let rent = Rent::default();
161 panic!(
162 "\n\n\
163 ==================================================\n\
164 empty account, no data:\n\
165 \t{} carats per epoch, {} carats to be rent_exempt\n\n\
166 stake_history, which is {}kB of data:\n\
167 \t{} carats per epoch, {} carats to be rent_exempt\n\
168 ==================================================\n\n",
169 rent.due(
170 0,
171 0,
172 (1.0 / SLOTS_PER_YEAR) * DEFAULT_SLOTS_PER_EPOCH as f64,
173 )
174 .0,
175 rent.minimum_balance(0),
176 crate::sysvar::stake_history::StakeHistory::size_of() / 1024,
177 rent.due(
178 0,
179 crate::sysvar::stake_history::StakeHistory::size_of(),
180 (1.0 / SLOTS_PER_YEAR) * DEFAULT_SLOTS_PER_EPOCH as f64,
181 )
182 .0,
183 rent.minimum_balance(crate::sysvar::stake_history::StakeHistory::size_of()),
184 );
185 }
186}