solana_fee/
lib.rs

1use {
2    solana_feature_set::{enable_secp256r1_precompile, FeatureSet},
3    solana_fee_structure::FeeDetails,
4    solana_svm_transaction::svm_message::SVMMessage,
5};
6
7/// Bools indicating the activation of features relevant
8/// to the fee calculation.
9// DEVELOPER NOTE:
10// This struct may become empty at some point. It is preferable to keep it
11// instead of removing, since fees will naturally be changed via feature-gates
12// in the future. Keeping this struct will help keep things organized.
13#[derive(Copy, Clone)]
14pub struct FeeFeatures {
15    pub enable_secp256r1_precompile: bool,
16}
17
18impl From<&FeatureSet> for FeeFeatures {
19    fn from(feature_set: &FeatureSet) -> Self {
20        Self {
21            enable_secp256r1_precompile: feature_set.is_active(&enable_secp256r1_precompile::ID),
22        }
23    }
24}
25
26/// Calculate fee for `SanitizedMessage`
27pub fn calculate_fee(
28    message: &impl SVMMessage,
29    zero_fees_for_test: bool,
30    lamports_per_signature: u64,
31    prioritization_fee: u64,
32    fee_features: FeeFeatures,
33) -> u64 {
34    calculate_fee_details(
35        message,
36        zero_fees_for_test,
37        lamports_per_signature,
38        prioritization_fee,
39        fee_features,
40    )
41    .total_fee()
42}
43
44pub fn calculate_fee_details(
45    message: &impl SVMMessage,
46    zero_fees_for_test: bool,
47    lamports_per_signature: u64,
48    prioritization_fee: u64,
49    fee_features: FeeFeatures,
50) -> FeeDetails {
51    if zero_fees_for_test {
52        return FeeDetails::default();
53    }
54
55    FeeDetails::new(
56        calculate_signature_fee(
57            SignatureCounts::from(message),
58            lamports_per_signature,
59            fee_features.enable_secp256r1_precompile,
60        ),
61        prioritization_fee,
62    )
63}
64
65/// Calculate fees from signatures.
66fn calculate_signature_fee(
67    SignatureCounts {
68        num_transaction_signatures,
69        num_ed25519_signatures,
70        num_secp256k1_signatures,
71        num_secp256r1_signatures,
72    }: SignatureCounts,
73    lamports_per_signature: u64,
74    enable_secp256r1_precompile: bool,
75) -> u64 {
76    let signature_count = num_transaction_signatures
77        .saturating_add(num_ed25519_signatures)
78        .saturating_add(num_secp256k1_signatures)
79        .saturating_add(
80            u64::from(enable_secp256r1_precompile).wrapping_mul(num_secp256r1_signatures),
81        );
82    signature_count.saturating_mul(lamports_per_signature)
83}
84
85struct SignatureCounts {
86    pub num_transaction_signatures: u64,
87    pub num_ed25519_signatures: u64,
88    pub num_secp256k1_signatures: u64,
89    pub num_secp256r1_signatures: u64,
90}
91
92impl<Tx: SVMMessage> From<&Tx> for SignatureCounts {
93    fn from(message: &Tx) -> Self {
94        Self {
95            num_transaction_signatures: message.num_transaction_signatures(),
96            num_ed25519_signatures: message.num_ed25519_signatures(),
97            num_secp256k1_signatures: message.num_secp256k1_signatures(),
98            num_secp256r1_signatures: message.num_secp256r1_signatures(),
99        }
100    }
101}
102
103#[cfg(test)]
104mod tests {
105    use super::*;
106
107    #[test]
108    fn test_calculate_signature_fee() {
109        const LAMPORTS_PER_SIGNATURE: u64 = 5_000;
110
111        // Impossible case - 0 signatures.
112        assert_eq!(
113            calculate_signature_fee(
114                SignatureCounts {
115                    num_transaction_signatures: 0,
116                    num_ed25519_signatures: 0,
117                    num_secp256k1_signatures: 0,
118                    num_secp256r1_signatures: 0,
119                },
120                LAMPORTS_PER_SIGNATURE,
121                true,
122            ),
123            0
124        );
125
126        // Simple signature
127        assert_eq!(
128            calculate_signature_fee(
129                SignatureCounts {
130                    num_transaction_signatures: 1,
131                    num_ed25519_signatures: 0,
132                    num_secp256k1_signatures: 0,
133                    num_secp256r1_signatures: 0,
134                },
135                LAMPORTS_PER_SIGNATURE,
136                true,
137            ),
138            LAMPORTS_PER_SIGNATURE
139        );
140
141        // Pre-compile signatures.
142        assert_eq!(
143            calculate_signature_fee(
144                SignatureCounts {
145                    num_transaction_signatures: 1,
146                    num_ed25519_signatures: 2,
147                    num_secp256k1_signatures: 3,
148                    num_secp256r1_signatures: 4,
149                },
150                LAMPORTS_PER_SIGNATURE,
151                true,
152            ),
153            10 * LAMPORTS_PER_SIGNATURE
154        );
155
156        // Pre-compile signatures (no secp256r1)
157        assert_eq!(
158            calculate_signature_fee(
159                SignatureCounts {
160                    num_transaction_signatures: 1,
161                    num_ed25519_signatures: 2,
162                    num_secp256k1_signatures: 3,
163                    num_secp256r1_signatures: 4,
164                },
165                LAMPORTS_PER_SIGNATURE,
166                false,
167            ),
168            6 * LAMPORTS_PER_SIGNATURE
169        );
170    }
171}