data_anchor_client/fees/
priority.rs

1use clap::ValueEnum;
2use itertools::Itertools;
3use schemars::JsonSchema;
4use serde::{Deserialize, Serialize};
5use solana_client::nonblocking::rpc_client::RpcClient;
6use solana_sdk::pubkey::Pubkey;
7
8use super::MicroLamports;
9use crate::BloberClientResult;
10
11/// The percentile of recent prioritization fees to use as the compute unit price for a transaction.
12#[derive(
13    Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize, JsonSchema, ValueEnum,
14)]
15pub enum Priority {
16    /// 0th percentile
17    Min,
18    /// 25th percentile
19    Low,
20    /// 50th percentile
21    #[default]
22    Medium,
23    /// 75th percentile
24    High,
25    /// 95th percentile
26    VeryHigh,
27}
28
29impl Priority {
30    /// Converts the priority enumeration to a percentile value between 0 and 1.
31    pub fn percentile(&self) -> f32 {
32        match self {
33            Self::Min => 0.0,
34            Self::Low => 0.25,
35            Self::Medium => 0.5,
36            Self::High => 0.75,
37            Self::VeryHigh => 0.95,
38        }
39    }
40
41    /// Finds the closest value to a given percentile in a sorted list of values.
42    ///
43    /// # Arguments
44    /// - `sorted_values`: The list of values to search. Must be sorted in ascending order. Must not be empty.
45    fn calculate_percentile(&self, sorted_fees: &[u64]) -> MicroLamports {
46        if sorted_fees.is_empty() {
47            return MicroLamports::MIN;
48        }
49        let percentile = self.percentile();
50        let index = (percentile * (sorted_fees.len() as f32 - 1.0)) as usize;
51        MicroLamports(sorted_fees[index.min(sorted_fees.len() - 1)].max(MicroLamports::MIN.0))
52    }
53
54    /// Calculates a recommended compute unit price for a transaction based on recent prioritization fees.
55    pub async fn get_priority_fee_estimate(
56        &self,
57        client: &RpcClient,
58        mutable_accounts: &[Pubkey],
59        _use_helius: bool,
60    ) -> BloberClientResult<MicroLamports> {
61        self.calculate_compute_unit_price(client, mutable_accounts)
62            .await
63    }
64
65    /// Calculates a recommended compute unit price for a transaction based on recent prioritization fees.
66    ///
67    /// # Arguments
68    /// - `client`: The RPC client to use for looking up recent prioritization fees.
69    /// - `mutable_accounts`: The addresses of the accounts that are mutable in the transaction (and thus need exclusive locks).
70    pub async fn calculate_compute_unit_price(
71        &self,
72        client: &RpcClient,
73        mutable_accounts: &[Pubkey],
74    ) -> BloberClientResult<MicroLamports> {
75        let recent_prioritization_fees = client
76            .get_recent_prioritization_fees(mutable_accounts)
77            .await?;
78        if recent_prioritization_fees.is_empty() {
79            return Ok(MicroLamports::MIN);
80        }
81        let sorted_fees = recent_prioritization_fees
82            .into_iter()
83            .map(|f| f.prioritization_fee)
84            .sorted()
85            .collect::<Vec<_>>();
86        Ok(self.calculate_percentile(&sorted_fees))
87    }
88}
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93
94    #[test]
95    fn medium_is_default() {
96        // This is probably important enough to warrant locking it down with a test.
97        let default = Priority::default();
98        let medium = Priority::Medium;
99        assert_eq!(medium, default);
100    }
101}