solana_cli/
compute_budget.rs1use {
2 solana_borsh::v1::try_from_slice_unchecked,
3 solana_clap_utils::compute_budget::ComputeUnitLimit,
4 solana_compute_budget::compute_budget_limits::MAX_COMPUTE_UNIT_LIMIT,
5 solana_compute_budget_interface::{self as compute_budget, ComputeBudgetInstruction},
6 solana_instruction::Instruction,
7 solana_message::Message,
8 solana_rpc_client::rpc_client::RpcClient,
9 solana_rpc_client_api::config::RpcSimulateTransactionConfig,
10 solana_transaction::Transaction,
11};
12
13pub(crate) enum UpdateComputeUnitLimitResult {
16 UpdatedInstructionIndex(usize),
17 NoInstructionFound,
18 SimulationNotConfigured,
19}
20
21fn get_compute_unit_limit_instruction_index(message: &Message) -> Option<usize> {
22 message
23 .instructions
24 .iter()
25 .enumerate()
26 .find_map(|(ix_index, instruction)| {
27 let ix_program_id = message.program_id(ix_index)?;
28 if ix_program_id != &compute_budget::id() {
29 return None;
30 }
31
32 matches!(
33 try_from_slice_unchecked(&instruction.data),
34 Ok(ComputeBudgetInstruction::SetComputeUnitLimit(_))
35 )
36 .then_some(ix_index)
37 })
38}
39
40fn simulate_for_compute_unit_limit_unchecked(
43 rpc_client: &RpcClient,
44 message: &Message,
45) -> Result<u32, Box<dyn std::error::Error>> {
46 let transaction = Transaction::new_unsigned(message.clone());
47 let simulate_result = rpc_client
48 .simulate_transaction_with_config(
49 &transaction,
50 RpcSimulateTransactionConfig {
51 replace_recent_blockhash: true,
52 commitment: Some(rpc_client.commitment()),
53 ..RpcSimulateTransactionConfig::default()
54 },
55 )?
56 .value;
57
58 if let Some(err) = simulate_result.err {
60 return Err(err.into());
61 }
62
63 let units_consumed = simulate_result
64 .units_consumed
65 .expect("compute units unavailable");
66
67 u32::try_from(units_consumed).map_err(Into::into)
68}
69
70pub(crate) fn simulate_for_compute_unit_limit(
75 rpc_client: &RpcClient,
76 message: &Message,
77) -> Result<u32, Box<dyn std::error::Error>> {
78 if get_compute_unit_limit_instruction_index(message).is_none() {
79 return Err("No compute unit limit instruction found".into());
80 }
81 simulate_for_compute_unit_limit_unchecked(rpc_client, message)
82}
83
84pub(crate) fn simulate_and_update_compute_unit_limit(
91 compute_unit_limit: &ComputeUnitLimit,
92 rpc_client: &RpcClient,
93 message: &mut Message,
94) -> Result<UpdateComputeUnitLimitResult, Box<dyn std::error::Error>> {
95 let Some(compute_unit_limit_ix_index) = get_compute_unit_limit_instruction_index(message)
96 else {
97 return Ok(UpdateComputeUnitLimitResult::NoInstructionFound);
98 };
99
100 match compute_unit_limit {
101 ComputeUnitLimit::Simulated => {
102 let compute_unit_limit =
103 simulate_for_compute_unit_limit_unchecked(rpc_client, message)?;
104
105 message.instructions[compute_unit_limit_ix_index].data =
107 ComputeBudgetInstruction::set_compute_unit_limit(compute_unit_limit).data;
108
109 Ok(UpdateComputeUnitLimitResult::UpdatedInstructionIndex(
110 compute_unit_limit_ix_index,
111 ))
112 }
113 ComputeUnitLimit::Static(_) | ComputeUnitLimit::Default => {
114 Ok(UpdateComputeUnitLimitResult::SimulationNotConfigured)
115 }
116 }
117}
118
119pub(crate) struct ComputeUnitConfig {
120 pub(crate) compute_unit_price: Option<u64>,
121 pub(crate) compute_unit_limit: ComputeUnitLimit,
122}
123
124pub(crate) trait WithComputeUnitConfig {
125 fn with_compute_unit_config(self, config: &ComputeUnitConfig) -> Self;
126}
127
128impl WithComputeUnitConfig for Vec<Instruction> {
129 fn with_compute_unit_config(mut self, config: &ComputeUnitConfig) -> Self {
130 if let Some(compute_unit_price) = config.compute_unit_price {
131 self.push(ComputeBudgetInstruction::set_compute_unit_price(
132 compute_unit_price,
133 ));
134 match config.compute_unit_limit {
135 ComputeUnitLimit::Default => {}
136 ComputeUnitLimit::Static(compute_unit_limit) => {
137 self.push(ComputeBudgetInstruction::set_compute_unit_limit(
138 compute_unit_limit,
139 ));
140 }
141 ComputeUnitLimit::Simulated => {
142 self.push(ComputeBudgetInstruction::set_compute_unit_limit(
145 MAX_COMPUTE_UNIT_LIMIT,
146 ));
147 }
148 }
149 }
150 self
151 }
152}