jito_bundle/client/
simulate.rs1use crate::bundler::bundle::types::BuiltBundle;
2use crate::client::jito_bundler::JitoBundler;
3use crate::error::JitoError;
4use crate::types::{
5 JsonRpcRequest, SimulateBundleApiResult, SimulateBundleParams, SimulateBundleSummary,
6 SimulateBundleValue,
7};
8use solana_client::rpc_config::RpcSimulateTransactionConfig;
9use solana_sdk::commitment_config::CommitmentConfig;
10
11impl JitoBundler {
12 pub async fn simulate_per_transaction(&self, bundle: &BuiltBundle) -> Result<(), JitoError> {
15 for (i, tx) in bundle.transactions.iter().enumerate() {
16 let sig = bs58::encode(&tx.signatures[0]).into_string();
17 let config = RpcSimulateTransactionConfig {
18 sig_verify: true,
19 replace_recent_blockhash: false,
20 commitment: Some(CommitmentConfig::confirmed()),
21 accounts: None,
22 min_context_slot: None,
23 inner_instructions: false,
24 encoding: None,
25 };
26
27 match self
28 .rpc_client
29 .simulate_transaction_with_config(tx, config)
30 .await
31 {
32 Ok(result) => {
33 if let Some(err) = result.value.err {
34 let logs = result.value.logs.unwrap_or_default();
35 return Err(JitoError::SimulationFailed {
36 details: format!(
37 "transaction {i} simulation failed: {err:?}\nsignature: {sig}\nlogs: {logs:?}"
38 ),
39 });
40 }
41 }
42 Err(e) => {
43 return Err(JitoError::SimulationFailed {
44 details: format!(
45 "transaction {i} RPC simulation error: {e}\nsignature: {sig}"
46 ),
47 });
48 }
49 }
50 }
51
52 Ok(())
53 }
54
55 pub async fn simulate_bundle_helius(
57 &self,
58 bundle: &BuiltBundle,
59 helius_rpc_url: &str,
60 ) -> Result<SimulateBundleValue, JitoError> {
61 let encoded_transactions = Self::encode_transactions_base64(&bundle.transactions)?;
62 let params = SimulateBundleParams {
63 encoded_transactions,
64 };
65 let request = JsonRpcRequest {
66 jsonrpc: "2.0",
67 id: 1,
68 method: "simulateBundle",
69 params: [params],
70 };
71 let (status, response_text) = self
72 .send_json_rpc_request(
73 self.http_client
74 .post(helius_rpc_url)
75 .header("Content-Type", "application/json"),
76 &request,
77 "Helius simulateBundle",
78 )
79 .await?;
80 if !status.is_success() {
81 return Err(JitoError::Network {
82 reason: format!("Helius simulateBundle HTTP {status}: {response_text}"),
83 });
84 }
85 let result: SimulateBundleApiResult = Self::parse_json_rpc_result(
86 &response_text,
87 "Helius simulateBundle",
88 "no result in Helius simulateBundle response",
89 )?;
90
91 if let SimulateBundleSummary::Failed(failure) = &result.value.summary {
92 let tx_count = bundle.transactions.len();
93 let results_count = result.value.transaction_results.len();
94 let failed_tx_index = if results_count < tx_count {
95 results_count
96 } else {
97 result
98 .value
99 .transaction_results
100 .iter()
101 .position(|r| r.err.is_some())
102 .unwrap_or(0)
103 };
104 let mut error_details = format!(
105 "bundle simulation failed at transaction {failed_tx_index}: {}\n\
106 failing tx signature: {:?}\n\
107 bundle size: {tx_count} transactions, results returned: {results_count}",
108 failure.error_message(),
109 failure.tx_signature
110 );
111
112 if results_count < tx_count {
113 error_details.push_str(&format!(
114 "\nHelius stopped after tx {failed_tx_index} failed - no logs for subsequent transactions"
115 ));
116 }
117 for (idx, tx_result) in result.value.transaction_results.iter().enumerate() {
118 let status_str = if tx_result.err.is_some() {
119 "FAILED"
120 } else {
121 "OK"
122 };
123 let units = tx_result
124 .units_consumed
125 .map_or_else(|| "N/A".to_string(), |u| u.to_string());
126 error_details.push_str(&format!("\n\n=== transaction {idx} [{status_str}] ==="));
127 error_details.push_str(&format!("\ncompute units: {units}"));
128 if let Some(err) = &tx_result.err {
129 error_details.push_str(&format!("\nerror: {err}"));
130 }
131 if let Some(logs) = &tx_result.logs {
132 error_details.push_str("\nlogs:");
133 for log in logs {
134 error_details.push_str(&format!("\n {log}"));
135 }
136 } else {
137 error_details.push_str("\nlogs: none");
138 }
139 }
140 return Err(JitoError::SimulationFailed {
141 details: error_details,
142 });
143 }
144 Ok(result.value)
145 }
146}