solana_fee_structure/
lib.rs1#![cfg_attr(docsrs, feature(doc_auto_cfg))]
3#![cfg_attr(feature = "frozen-abi", feature(min_specialization))]
4
5#[cfg(not(target_os = "solana"))]
6use solana_message::SanitizedMessage;
7use std::num::NonZeroU32;
8
9#[derive(Debug, Default, Clone, Eq, PartialEq)]
11pub struct FeeBin {
12 pub limit: u64,
14 pub fee: u64,
16}
17
18pub struct FeeBudgetLimits {
19 pub loaded_accounts_data_size_limit: NonZeroU32,
20 pub heap_cost: u64,
21 pub compute_unit_limit: u64,
22 pub prioritization_fee: u64,
23}
24
25#[derive(Debug, Clone, Eq, PartialEq)]
27pub struct FeeStructure {
28 pub lamports_per_signature: u64,
30 pub lamports_per_write_lock: u64,
32 pub compute_fee_bins: Vec<FeeBin>,
34}
35
36#[cfg_attr(
37 feature = "serde",
38 derive(serde_derive::Deserialize, serde_derive::Serialize)
39)]
40#[derive(Debug, Default, Clone, Copy, Eq, PartialEq)]
41pub struct FeeDetails {
42 transaction_fee: u64,
43 prioritization_fee: u64,
44}
45
46impl FeeDetails {
47 pub fn new(transaction_fee: u64, prioritization_fee: u64) -> Self {
48 Self {
49 transaction_fee,
50 prioritization_fee,
51 }
52 }
53
54 pub fn total_fee(&self) -> u64 {
55 self.transaction_fee.saturating_add(self.prioritization_fee)
56 }
57
58 pub fn accumulate(&mut self, fee_details: &FeeDetails) {
59 self.transaction_fee = self
60 .transaction_fee
61 .saturating_add(fee_details.transaction_fee);
62 self.prioritization_fee = self
63 .prioritization_fee
64 .saturating_add(fee_details.prioritization_fee)
65 }
66
67 pub fn transaction_fee(&self) -> u64 {
68 self.transaction_fee
69 }
70
71 pub fn prioritization_fee(&self) -> u64 {
72 self.prioritization_fee
73 }
74}
75
76pub const ACCOUNT_DATA_COST_PAGE_SIZE: u64 = 32_u64.saturating_mul(1024);
77
78impl FeeStructure {
79 #[deprecated(
80 since = "2.3.0",
81 note = "Use FeeStructure::default() and modify fields as needed"
82 )]
83 #[allow(deprecated)]
84 pub fn new(
85 sol_per_signature: f64,
86 sol_per_write_lock: f64,
87 compute_fee_bins: Vec<(u64, f64)>,
88 ) -> Self {
89 let compute_fee_bins = compute_fee_bins
90 .iter()
91 .map(|(limit, sol)| FeeBin {
92 limit: *limit,
93 fee: solana_native_token::sol_to_lamports(*sol),
94 })
95 .collect::<Vec<_>>();
96 FeeStructure {
97 lamports_per_signature: solana_native_token::sol_to_lamports(sol_per_signature),
98 lamports_per_write_lock: solana_native_token::sol_to_lamports(sol_per_write_lock),
99 compute_fee_bins,
100 }
101 }
102
103 pub fn get_max_fee(&self, num_signatures: u64, num_write_locks: u64) -> u64 {
104 num_signatures
105 .saturating_mul(self.lamports_per_signature)
106 .saturating_add(num_write_locks.saturating_mul(self.lamports_per_write_lock))
107 .saturating_add(
108 self.compute_fee_bins
109 .last()
110 .map(|bin| bin.fee)
111 .unwrap_or_default(),
112 )
113 }
114
115 pub fn calculate_memory_usage_cost(
116 loaded_accounts_data_size_limit: u32,
117 heap_cost: u64,
118 ) -> u64 {
119 (loaded_accounts_data_size_limit as u64)
120 .saturating_add(ACCOUNT_DATA_COST_PAGE_SIZE.saturating_sub(1))
121 .saturating_div(ACCOUNT_DATA_COST_PAGE_SIZE)
122 .saturating_mul(heap_cost)
123 }
124
125 #[cfg(not(target_os = "solana"))]
127 #[deprecated(
128 since = "2.1.0",
129 note = "Please use `solana_fee::calculate_fee` instead."
130 )]
131 pub fn calculate_fee(
132 &self,
133 message: &SanitizedMessage,
134 lamports_per_signature: u64,
135 budget_limits: &FeeBudgetLimits,
136 include_loaded_account_data_size_in_fee: bool,
137 ) -> u64 {
138 #[allow(deprecated)]
139 self.calculate_fee_details(
140 message,
141 lamports_per_signature,
142 budget_limits,
143 include_loaded_account_data_size_in_fee,
144 )
145 .total_fee()
146 }
147
148 #[cfg(not(target_os = "solana"))]
150 #[deprecated(
151 since = "2.1.0",
152 note = "Please use `solana_fee::calculate_fee_details` instead."
153 )]
154 pub fn calculate_fee_details(
155 &self,
156 message: &SanitizedMessage,
157 lamports_per_signature: u64,
158 budget_limits: &FeeBudgetLimits,
159 include_loaded_account_data_size_in_fee: bool,
160 ) -> FeeDetails {
161 if lamports_per_signature == 0 {
164 return FeeDetails::default();
165 }
166
167 let signature_fee = message
168 .num_total_signatures()
169 .saturating_mul(self.lamports_per_signature);
170 let write_lock_fee = message
171 .num_write_locks()
172 .saturating_mul(self.lamports_per_write_lock);
173
174 let loaded_accounts_data_size_cost = if include_loaded_account_data_size_in_fee {
177 FeeStructure::calculate_memory_usage_cost(
178 budget_limits.loaded_accounts_data_size_limit.get(),
179 budget_limits.heap_cost,
180 )
181 } else {
182 0_u64
183 };
184 let total_compute_units =
185 loaded_accounts_data_size_cost.saturating_add(budget_limits.compute_unit_limit);
186 let compute_fee = self
187 .compute_fee_bins
188 .iter()
189 .find(|bin| total_compute_units <= bin.limit)
190 .map(|bin| bin.fee)
191 .unwrap_or_else(|| {
192 self.compute_fee_bins
193 .last()
194 .map(|bin| bin.fee)
195 .unwrap_or_default()
196 });
197
198 FeeDetails {
199 transaction_fee: signature_fee
200 .saturating_add(write_lock_fee)
201 .saturating_add(compute_fee),
202 prioritization_fee: budget_limits.prioritization_fee,
203 }
204 }
205}
206
207impl Default for FeeStructure {
208 fn default() -> Self {
209 Self {
210 lamports_per_signature: 5000,
211 lamports_per_write_lock: 0,
212 compute_fee_bins: vec![FeeBin {
213 limit: 1_400_000,
214 fee: 0,
215 }],
216 }
217 }
218}
219
220#[cfg(feature = "frozen-abi")]
221impl ::solana_frozen_abi::abi_example::AbiExample for FeeStructure {
222 fn example() -> Self {
223 FeeStructure::default()
224 }
225}
226
227#[cfg(test)]
228mod tests {
229 use super::*;
230
231 #[test]
232 fn test_calculate_memory_usage_cost() {
233 let heap_cost = 99;
234 const K: u32 = 1024;
235
236 assert_eq!(
240 heap_cost,
241 FeeStructure::calculate_memory_usage_cost(31 * K, heap_cost)
242 );
243
244 assert_eq!(
246 heap_cost,
247 FeeStructure::calculate_memory_usage_cost(32 * K, heap_cost)
248 );
249
250 assert_eq!(
252 heap_cost * 2,
253 FeeStructure::calculate_memory_usage_cost(33 * K, heap_cost)
254 );
255
256 assert_eq!(
258 heap_cost * 2,
259 FeeStructure::calculate_memory_usage_cost(64 * K, heap_cost)
260 );
261 }
262}