Skip to main content

solana_fee_structure/
lib.rs

1//! Fee structures.
2#![cfg_attr(docsrs, feature(doc_cfg))]
3#![cfg_attr(feature = "frozen-abi", feature(min_specialization))]
4
5use std::num::NonZeroU32;
6
7/// A fee and its associated compute unit limit
8#[derive(Debug, Default, Clone, Eq, PartialEq)]
9pub struct FeeBin {
10    /// maximum compute units for which this fee will be charged
11    pub limit: u64,
12    /// fee in lamports
13    pub fee: u64,
14}
15
16pub struct FeeBudgetLimits {
17    pub loaded_accounts_data_size_limit: NonZeroU32,
18    pub heap_cost: u64,
19    pub compute_unit_limit: u64,
20    pub prioritization_fee: u64,
21}
22
23/// Information used to calculate fees
24#[derive(Debug, Clone, Eq, PartialEq)]
25pub struct FeeStructure {
26    /// lamports per signature
27    pub lamports_per_signature: u64,
28    /// lamports_per_write_lock
29    pub lamports_per_write_lock: u64,
30    /// Compute unit fee bins
31    pub compute_fee_bins: Vec<FeeBin>,
32}
33
34#[cfg_attr(
35    feature = "serde",
36    derive(serde_derive::Deserialize, serde_derive::Serialize)
37)]
38#[derive(Debug, Default, Clone, Copy, Eq, PartialEq)]
39pub struct FeeDetails {
40    transaction_fee: u64,
41    prioritization_fee: u64,
42    /// Introduced with SIMD-0553.
43    resource_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            resource_fee: 0,
52        }
53    }
54
55    pub fn new_with_resource_fee(
56        transaction_fee: u64,
57        prioritization_fee: u64,
58        resource_fee: u64,
59    ) -> Self {
60        Self {
61            transaction_fee,
62            prioritization_fee,
63            resource_fee,
64        }
65    }
66
67    pub fn total_fee(&self) -> u64 {
68        self.transaction_fee
69            .saturating_add(self.prioritization_fee)
70            .saturating_add(self.resource_fee)
71    }
72
73    pub fn accumulate(&mut self, fee_details: &FeeDetails) {
74        self.transaction_fee = self
75            .transaction_fee
76            .saturating_add(fee_details.transaction_fee);
77        self.prioritization_fee = self
78            .prioritization_fee
79            .saturating_add(fee_details.prioritization_fee);
80        self.resource_fee = self.resource_fee.saturating_add(fee_details.resource_fee);
81    }
82
83    pub fn transaction_fee(&self) -> u64 {
84        self.transaction_fee
85    }
86
87    pub fn prioritization_fee(&self) -> u64 {
88        self.prioritization_fee
89    }
90
91    pub fn resource_fee(&self) -> u64 {
92        self.resource_fee
93    }
94}
95
96pub const ACCOUNT_DATA_COST_PAGE_SIZE: u64 = 32_u64.saturating_mul(1024);
97
98impl FeeStructure {
99    pub fn get_max_fee(&self, num_signatures: u64, num_write_locks: u64) -> u64 {
100        num_signatures
101            .saturating_mul(self.lamports_per_signature)
102            .saturating_add(num_write_locks.saturating_mul(self.lamports_per_write_lock))
103            .saturating_add(
104                self.compute_fee_bins
105                    .last()
106                    .map(|bin| bin.fee)
107                    .unwrap_or_default(),
108            )
109    }
110
111    pub fn calculate_memory_usage_cost(
112        loaded_accounts_data_size_limit: u32,
113        heap_cost: u64,
114    ) -> u64 {
115        (loaded_accounts_data_size_limit as u64)
116            .saturating_add(ACCOUNT_DATA_COST_PAGE_SIZE.saturating_sub(1))
117            .saturating_div(ACCOUNT_DATA_COST_PAGE_SIZE)
118            .saturating_mul(heap_cost)
119    }
120}
121
122impl Default for FeeStructure {
123    fn default() -> Self {
124        Self {
125            lamports_per_signature: 5000,
126            lamports_per_write_lock: 0,
127            compute_fee_bins: vec![FeeBin {
128                limit: 1_400_000,
129                fee: 0,
130            }],
131        }
132    }
133}
134
135#[cfg(feature = "frozen-abi")]
136impl ::solana_frozen_abi::abi_example::AbiExample for FeeStructure {
137    fn example() -> Self {
138        FeeStructure::default()
139    }
140}
141
142#[cfg(test)]
143mod tests {
144    use super::*;
145
146    #[test]
147    fn test_calculate_memory_usage_cost() {
148        let heap_cost = 99;
149        const K: u32 = 1024;
150
151        // accounts data size are priced in block of 32K, ...
152
153        // ... requesting less than 32K should still be charged as one block
154        assert_eq!(
155            heap_cost,
156            FeeStructure::calculate_memory_usage_cost(31 * K, heap_cost)
157        );
158
159        // ... requesting exact 32K should be charged as one block
160        assert_eq!(
161            heap_cost,
162            FeeStructure::calculate_memory_usage_cost(32 * K, heap_cost)
163        );
164
165        // ... requesting slightly above 32K should be charged as 2 block
166        assert_eq!(
167            heap_cost * 2,
168            FeeStructure::calculate_memory_usage_cost(33 * K, heap_cost)
169        );
170
171        // ... requesting exact 64K should be charged as 2 block
172        assert_eq!(
173            heap_cost * 2,
174            FeeStructure::calculate_memory_usage_cost(64 * K, heap_cost)
175        );
176    }
177}