data_anchor_client/fees/
priority.rs

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