use std::ops::AddAssign;
use solana_sdk::{compute_budget::ComputeBudgetInstruction, instruction::Instruction};
#[derive(Debug, Clone, Copy)]
pub struct ComputeBudget {
min_priority_lamports: Option<u64>,
limit_units: u32,
price_micro_lamports: u64,
}
impl Default for ComputeBudget {
fn default() -> Self {
Self {
min_priority_lamports: Some(Self::DEFAULT_MIN_PRIORITY_LAMPORTS),
limit_units: 200_000,
price_micro_lamports: 50_000,
}
}
}
impl ComputeBudget {
const MICRO_LAMPORTS: u64 = 10u64.pow(6);
pub const DEFAULT_MIN_PRIORITY_LAMPORTS: u64 = 10000;
pub const MAX_COMPUTE_UNIT: u32 = 1_400_000;
#[inline]
pub fn with_limit(mut self, units: u32) -> Self {
self.set_limit(units);
self
}
#[inline]
pub fn with_price(mut self, micro_lamports: u64) -> Self {
self.set_price(micro_lamports);
self
}
#[inline]
pub fn with_min_priority_lamports(mut self, lamports: Option<u64>) -> Self {
self.set_min_priority_lamports(lamports);
self
}
#[inline]
pub fn set_min_priority_lamports(&mut self, lamports: Option<u64>) -> &mut Self {
self.min_priority_lamports = lamports;
self
}
pub fn set_price(&mut self, micro_lamports: u64) -> &mut Self {
self.price_micro_lamports = micro_lamports;
self
}
pub fn set_limit(&mut self, units: u32) -> &mut Self {
self.limit_units = units;
self
}
fn budget_price(
&self,
compute_unit_price_micro_lamports: Option<u64>,
compute_unit_min_priority_lamports: Option<u64>,
extra_compute_units: u32,
) -> u64 {
let mut price = compute_unit_price_micro_lamports.unwrap_or(self.price_micro_lamports);
let min_priority_lamports =
compute_unit_min_priority_lamports.or(self.min_priority_lamports);
if let Some(min_price) = min_priority_lamports.and_then(|min_lamports| {
min_lamports
.checked_mul(Self::MICRO_LAMPORTS)?
.checked_div(self.budget_units(extra_compute_units) as u64)
}) {
price = price.max(min_price)
}
price
}
fn budget_units(&self, extra_compute_units: u32) -> u32 {
(self.limit_units.saturating_add(extra_compute_units)).min(Self::MAX_COMPUTE_UNIT)
}
pub fn compute_budget_instructions(
&self,
compute_unit_price_micro_lamports: Option<u64>,
compute_unit_min_priority_lamports: Option<u64>,
) -> Vec<Instruction> {
self.compute_budget_instructions_with_extra_units(
compute_unit_price_micro_lamports,
compute_unit_min_priority_lamports,
0,
)
}
pub fn compute_budget_instructions_with_extra_units(
&self,
compute_unit_price_micro_lamports: Option<u64>,
compute_unit_min_priority_lamports: Option<u64>,
extra_compute_units: u32,
) -> Vec<Instruction> {
let price = self.budget_price(
compute_unit_price_micro_lamports,
compute_unit_min_priority_lamports,
extra_compute_units,
);
vec![
ComputeBudgetInstruction::set_compute_unit_limit(
self.budget_units(extra_compute_units),
),
ComputeBudgetInstruction::set_compute_unit_price(price),
]
}
pub fn limit(&self) -> u32 {
self.limit_units
}
pub fn price(&self) -> u64 {
self.price_micro_lamports
}
pub fn fee(
&self,
compute_unit_price_micro_lamports: Option<u64>,
compute_unit_min_priority_lamports: Option<u64>,
) -> u64 {
self.fee_with_extra_units(
compute_unit_price_micro_lamports,
compute_unit_min_priority_lamports,
0,
)
}
pub fn fee_with_extra_units(
&self,
compute_unit_price_micro_lamports: Option<u64>,
compute_unit_min_priority_lamports: Option<u64>,
extra_compute_units: u32,
) -> u64 {
self.budget_units(extra_compute_units) as u64
* self.budget_price(
compute_unit_price_micro_lamports,
compute_unit_min_priority_lamports,
extra_compute_units,
)
/ Self::MICRO_LAMPORTS
}
}
impl AddAssign for ComputeBudget {
fn add_assign(&mut self, rhs: Self) {
self.limit_units += rhs.limit_units;
self.price_micro_lamports = self.price_micro_lamports.max(rhs.price_micro_lamports);
let min_lamports = match (self.min_priority_lamports, rhs.min_priority_lamports) {
(Some(lamports), None) => Some(lamports),
(None, Some(lamports)) => Some(lamports),
(Some(lhs), Some(rhs)) => Some(lhs.max(rhs)),
(None, None) => None,
};
self.min_priority_lamports = min_lamports;
}
}