Skip to main content

data_anchor_client/fees/
priority.rs

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