solana_fee_structure/
lib.rs

1//! Fee structures.
2#![cfg_attr(docsrs, feature(doc_auto_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}
43
44impl FeeDetails {
45    pub fn new(transaction_fee: u64, prioritization_fee: u64) -> Self {
46        Self {
47            transaction_fee,
48            prioritization_fee,
49        }
50    }
51
52    pub fn total_fee(&self) -> u64 {
53        self.transaction_fee.saturating_add(self.prioritization_fee)
54    }
55
56    pub fn accumulate(&mut self, fee_details: &FeeDetails) {
57        self.transaction_fee = self
58            .transaction_fee
59            .saturating_add(fee_details.transaction_fee);
60        self.prioritization_fee = self
61            .prioritization_fee
62            .saturating_add(fee_details.prioritization_fee)
63    }
64
65    pub fn transaction_fee(&self) -> u64 {
66        self.transaction_fee
67    }
68
69    pub fn prioritization_fee(&self) -> u64 {
70        self.prioritization_fee
71    }
72}
73
74pub const ACCOUNT_DATA_COST_PAGE_SIZE: u64 = 32_u64.saturating_mul(1024);
75
76impl FeeStructure {
77    pub fn get_max_fee(&self, num_signatures: u64, num_write_locks: u64) -> u64 {
78        num_signatures
79            .saturating_mul(self.lamports_per_signature)
80            .saturating_add(num_write_locks.saturating_mul(self.lamports_per_write_lock))
81            .saturating_add(
82                self.compute_fee_bins
83                    .last()
84                    .map(|bin| bin.fee)
85                    .unwrap_or_default(),
86            )
87    }
88
89    pub fn calculate_memory_usage_cost(
90        loaded_accounts_data_size_limit: u32,
91        heap_cost: u64,
92    ) -> u64 {
93        (loaded_accounts_data_size_limit as u64)
94            .saturating_add(ACCOUNT_DATA_COST_PAGE_SIZE.saturating_sub(1))
95            .saturating_div(ACCOUNT_DATA_COST_PAGE_SIZE)
96            .saturating_mul(heap_cost)
97    }
98}
99
100impl Default for FeeStructure {
101    fn default() -> Self {
102        Self {
103            lamports_per_signature: 5000,
104            lamports_per_write_lock: 0,
105            compute_fee_bins: vec![FeeBin {
106                limit: 1_400_000,
107                fee: 0,
108            }],
109        }
110    }
111}
112
113#[cfg(feature = "frozen-abi")]
114impl ::solana_frozen_abi::abi_example::AbiExample for FeeStructure {
115    fn example() -> Self {
116        FeeStructure::default()
117    }
118}
119
120#[cfg(test)]
121mod tests {
122    use super::*;
123
124    #[test]
125    fn test_calculate_memory_usage_cost() {
126        let heap_cost = 99;
127        const K: u32 = 1024;
128
129        // accounts data size are priced in block of 32K, ...
130
131        // ... requesting less than 32K should still be charged as one block
132        assert_eq!(
133            heap_cost,
134            FeeStructure::calculate_memory_usage_cost(31 * K, heap_cost)
135        );
136
137        // ... requesting exact 32K should be charged as one block
138        assert_eq!(
139            heap_cost,
140            FeeStructure::calculate_memory_usage_cost(32 * K, heap_cost)
141        );
142
143        // ... requesting slightly above 32K should be charged as 2 block
144        assert_eq!(
145            heap_cost * 2,
146            FeeStructure::calculate_memory_usage_cost(33 * K, heap_cost)
147        );
148
149        // ... requesting exact 64K should be charged as 2 block
150        assert_eq!(
151            heap_cost * 2,
152            FeeStructure::calculate_memory_usage_cost(64 * K, heap_cost)
153        );
154    }
155}