jito_bundle/client/
status.rs1use crate::client::jito_bundler::JitoBundler;
2use crate::constants::DEFAULT_INITIAL_CONFIRM_DELAY_SECS;
3use crate::error::JitoError;
4use crate::types::{BundleStatus, JsonRpcRequest, JsonRpcResponse};
5use serde::Deserialize;
6use solana_sdk::signature::Signature;
7use solana_transaction_status_client_types::TransactionConfirmationStatus;
8use std::str::FromStr;
9use std::time::Duration;
10
11impl JitoBundler {
12 pub async fn get_bundle_status(&self, bundle_id: &str) -> BundleStatus {
13 let endpoint = self.config.network.block_engine_url();
14 let request = JsonRpcRequest {
15 jsonrpc: "2.0",
16 id: 1,
17 method: "getBundleStatuses",
18 params: [[bundle_id]],
19 };
20
21 let response = match self.jito_post(endpoint).json(&request).send().await {
22 Ok(r) => r,
23 Err(_) => return BundleStatus::Unknown,
24 };
25
26 let status_code = response.status();
27 if status_code.as_u16() == 429 {
28 return BundleStatus::Pending;
29 }
30 if !status_code.is_success() {
31 return BundleStatus::Unknown;
32 }
33
34 let response_text = match response.text().await {
35 Ok(t) => t,
36 Err(_) => return BundleStatus::Unknown,
37 };
38
39 #[derive(Debug, Deserialize)]
40 struct BundleStatusValue {
41 confirmation_status: Option<String>,
42 slot: Option<u64>,
43 err: Option<serde_json::Value>,
44 }
45
46 #[derive(Debug, Deserialize)]
47 struct StatusResult {
48 value: Vec<BundleStatusValue>,
49 }
50
51 let parsed: JsonRpcResponse<StatusResult> = match serde_json::from_str(&response_text) {
52 Ok(p) => p,
53 Err(_) => return BundleStatus::Unknown,
54 };
55
56 if let Some(result) = parsed.result
57 && let Some(status) = result.value.first()
58 {
59 if let Some(err) = &status.err {
60 return BundleStatus::Failed {
61 error: Some(err.to_string()),
62 };
63 }
64
65 if let Some(confirmation_status) = &status.confirmation_status
66 && (confirmation_status == "confirmed" || confirmation_status == "finalized")
67 {
68 return BundleStatus::Landed { slot: status.slot };
69 }
70
71 return BundleStatus::Pending;
72 }
73
74 BundleStatus::Unknown
75 }
76
77 pub async fn wait_for_landing_on_chain(
78 &self,
79 signatures: &[String],
80 ) -> Result<BundleStatus, JitoError> {
81 let parsed_signatures: Vec<Signature> = signatures
82 .iter()
83 .map(|s| Signature::from_str(s))
84 .collect::<Result<Vec<_>, _>>()
85 .map_err(|e| JitoError::InvalidSignature {
86 reason: e.to_string(),
87 })?;
88
89 tokio::time::sleep(Duration::from_secs(DEFAULT_INITIAL_CONFIRM_DELAY_SECS)).await;
90
91 let max_attempts = self.config.confirm_policy.max_attempts;
92 let interval_ms = self.config.confirm_policy.interval_ms;
93
94 for _attempt in 0..max_attempts {
95 if let Ok(statuses) = self
96 .rpc_client
97 .get_signature_statuses(&parsed_signatures)
98 .await
99 {
100 for (j, status) in statuses.value.iter().enumerate() {
101 if let Some(s) = status
102 && let Some(err) = &s.err
103 {
104 return Ok(BundleStatus::Failed {
105 error: Some(format!("transaction {j} failed: {err:?}")),
106 });
107 }
108 }
109 let all_confirmed = statuses.value.iter().all(|status| {
110 status.as_ref().is_some_and(|s| {
111 s.confirmation_status.as_ref().is_some_and(|cs| {
112 cs == &TransactionConfirmationStatus::Confirmed
113 || cs == &TransactionConfirmationStatus::Finalized
114 })
115 })
116 });
117 if all_confirmed {
118 let slot = statuses
119 .value
120 .first()
121 .and_then(|s| s.as_ref().map(|s| s.slot));
122 return Ok(BundleStatus::Landed { slot });
123 }
124 }
125 tokio::time::sleep(Duration::from_millis(interval_ms)).await;
126 }
127
128 Err(JitoError::ConfirmationTimeout {
129 attempts: max_attempts,
130 })
131 }
132
133 pub async fn wait_for_landing_via_jito(
134 &self,
135 bundle_id: &str,
136 ) -> Result<BundleStatus, JitoError> {
137 tokio::time::sleep(Duration::from_secs(DEFAULT_INITIAL_CONFIRM_DELAY_SECS)).await;
138
139 let max_attempts = self.config.confirm_policy.max_attempts;
140 let interval_ms = self.config.confirm_policy.interval_ms;
141
142 for _attempt in 0..max_attempts {
143 let status = self.get_bundle_status(bundle_id).await;
144
145 match &status {
146 BundleStatus::Landed { .. } | BundleStatus::Failed { .. } => {
147 return Ok(status);
148 }
149 _ => {}
150 }
151
152 tokio::time::sleep(Duration::from_millis(interval_ms)).await;
153 }
154
155 Err(JitoError::ConfirmationTimeout {
156 attempts: max_attempts,
157 })
158 }
159}