sol_rpc_router/lib.rs
1//! Solana RPC Router
2//!
3//! This router splits Solana RPC requests into several RPC providers, taking into account
4//! rate limits of each provider.
5
6use base64::Engine;
7use base64::prelude::BASE64_STANDARD;
8use bincode::serialize;
9use futures::join;
10use log::{debug, info, trace};
11use serde::de::DeserializeOwned;
12use serde_json::{Value, json};
13use solana_account::Account;
14use solana_account_decoder_client_types::UiAccountData;
15use solana_account_decoder_client_types::token::{TokenAccountType, UiTokenAccount};
16use solana_clock::{DEFAULT_MS_PER_SLOT, Epoch, Slot, UnixTimestamp};
17use solana_epoch_info::EpochInfo;
18use solana_epoch_schedule::EpochSchedule;
19use solana_hash::Hash;
20use solana_pubkey::Pubkey;
21use solana_rpc_client::api::client_error::{
22 Error as ClientError, ErrorKind as ClientErrorKind, Result as ClientResult,
23};
24use solana_rpc_client::api::request::RpcRequest;
25pub use solana_rpc_client::rpc_client::{
26 GetConfirmedSignaturesForAddress2Config, SerializableMessage, SerializableTransaction,
27};
28use solana_rpc_client_api::config::{
29 CommitmentConfig, RpcAccountInfoConfig, RpcBlockConfig, RpcBlockProductionConfig,
30 RpcEpochConfig, RpcGetVoteAccountsConfig, RpcLargestAccountsConfig, RpcLeaderScheduleConfig,
31 RpcProgramAccountsConfig, RpcRequestAirdropConfig, RpcSendTransactionConfig,
32 RpcSignaturesForAddressConfig, RpcSimulateTransactionConfig, RpcTokenAccountsFilter,
33 RpcTransactionConfig, UiAccountEncoding, UiTransactionEncoding,
34};
35use solana_rpc_client_api::request::{RpcError, RpcResponseErrorData, TokenAccountsFilter};
36use solana_rpc_client_api::response::transaction::Signature;
37use solana_rpc_client_api::response::{
38 OptionalContext, Response, RpcAccountBalance, RpcBlockProduction, RpcBlockhash,
39 RpcConfirmedTransactionStatusWithSignature, RpcContactInfo, RpcIdentity, RpcInflationGovernor,
40 RpcInflationRate, RpcInflationReward, RpcKeyedAccount, RpcLeaderSchedule, RpcPerfSample,
41 RpcPrioritizationFee, RpcResult, RpcSimulateTransactionResult, RpcSnapshotSlotInfo, RpcSupply,
42 RpcTokenAccountBalance, RpcVersionInfo, RpcVoteAccountStatus, TransactionResult, UiAccount,
43 UiTokenAmount,
44};
45use solana_transaction_status_client_types::{
46 EncodedConfirmedBlock, EncodedConfirmedTransactionWithStatusMeta, TransactionStatus,
47 UiConfirmedBlock,
48};
49use solana_vote_interface::state::MAX_LOCKOUT_HISTORY;
50use tokio::time::sleep;
51
52use std::str::FromStr;
53use std::sync::Arc;
54use std::time::{Duration, Instant};
55use tokio::sync::{Mutex, mpsc, oneshot};
56
57use crate::config::RpcClientConfig;
58use crate::provider::RpcRequestWrapper;
59
60pub mod config;
61pub mod provider;
62
63/// Main RPC client that routes requests to multiple providers.
64#[derive(Debug, Clone)]
65pub struct RpcClient {
66 config: RpcClientConfig,
67 sender: mpsc::Sender<RpcRequestWrapper>,
68}
69
70impl RpcClient {
71 /// Create a new RPC client with the given configuration
72 pub fn new(config: RpcClientConfig) -> Self {
73 let (sender, receiver) = mpsc::channel(10_000);
74 let shared_receiver = Arc::new(Mutex::new(receiver));
75
76 for provider_config in &config.providers {
77 let provider = provider::RpcProvider::new(provider_config.clone());
78 let shared_receiver_clone = Arc::clone(&shared_receiver);
79
80 tokio::spawn(provider.run(shared_receiver_clone));
81 }
82
83 Self { config, sender }
84 }
85
86 pub fn commitment(&self) -> CommitmentConfig {
87 self.config.commitment
88 }
89
90 /// Send a request to one of the providers
91 pub async fn send<T: DeserializeOwned>(
92 &self,
93 request: RpcRequest,
94 params: serde_json::Value,
95 ) -> ClientResult<T> {
96 let (sender, receiver) = oneshot::channel();
97 let request_wrapper = RpcRequestWrapper {
98 request,
99 params,
100 sender,
101 };
102 self.sender.send(request_wrapper).await.map_err(|e| {
103 solana_rpc_client::api::client_error::Error {
104 request: None,
105 kind: Box::new(ClientErrorKind::Custom(e.to_string())),
106 }
107 })?;
108
109 let value = receiver
110 .await
111 .map_err(|e| solana_rpc_client::api::client_error::Error {
112 request: None,
113 kind: Box::new(ClientErrorKind::Custom(e.to_string())),
114 })??;
115
116 Ok(serde_json::from_value(value).map_err(serde_err_to_rpc_err)?)
117 }
118
119 /// Submit a transaction and wait for confirmation.
120 ///
121 /// Once this function returns successfully, the given transaction is
122 /// guaranteed to be processed with the configured [commitment level][cl].
123 ///
124 /// [cl]: https://solana.com/docs/rpc#configuring-state-commitment
125 ///
126 /// After sending the transaction, this method polls in a loop for the
127 /// status of the transaction until it has ben confirmed.
128 ///
129 /// # Errors
130 ///
131 /// If the transaction is not signed then an error with kind [`RpcError`] is
132 /// returned, containing an [`RpcResponseError`] with `code` set to
133 /// [`JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE`].
134 ///
135 /// If the preflight transaction simulation fails then an error with kind
136 /// [`RpcError`] is returned, containing an [`RpcResponseError`] with `code`
137 /// set to [`JSON_RPC_SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE`].
138 ///
139 /// If the receiving node is unhealthy, e.g. it is not fully synced to
140 /// the cluster, then an error with kind [`RpcError`] is returned,
141 /// containing an [`RpcResponseError`] with `code` set to
142 /// [`JSON_RPC_SERVER_ERROR_NODE_UNHEALTHY`].
143 ///
144 /// [`RpcResponseError`]: RpcError::RpcResponseError
145 /// [`JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE`]: solana_rpc_client_api::custom_error::JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE
146 /// [`JSON_RPC_SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE`]: solana_rpc_client_api::custom_error::JSON_RPC_SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE
147 /// [`JSON_RPC_SERVER_ERROR_NODE_UNHEALTHY`]: solana_rpc_client_api::custom_error::JSON_RPC_SERVER_ERROR_NODE_UNHEALTHY
148 ///
149 /// # RPC Reference
150 ///
151 /// This method is built on the [`sendTransaction`] RPC method, and the
152 /// [`getLatestBlockhash`] RPC method.
153 ///
154 /// [`sendTransaction`]: https://solana.com/docs/rpc/http/sendtransaction
155 /// [`getLatestBlockhash`]: https://solana.com/docs/rpc/http/getlatestblockhash
156 ///
157 /// # Examples
158 ///
159 /// ```ignore
160 /// # use solana_rpc_client_api::client_error::Error;
161 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
162 /// # use solana_keypair::Keypair;
163 /// # use solana_system_transaction as system_transaction;
164 /// # use solana_signature::Signature;
165 /// # use solana_signer::Signer;
166 /// # futures::executor::block_on(async {
167 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
168 /// # let alice = Keypair::new();
169 /// # let bob = Keypair::new();
170 /// # let lamports = 50;
171 /// # let latest_blockhash = rpc_client.get_latest_blockhash().await?;
172 /// let tx = system_transaction::transfer(&alice, &bob.pubkey(), lamports, latest_blockhash);
173 /// let signature = rpc_client.send_and_confirm_transaction(&tx).await?;
174 /// # Ok::<(), Error>(())
175 /// # })?;
176 /// # Ok::<(), Error>(())
177 /// ```
178 pub async fn send_and_confirm_transaction(
179 &self,
180 transaction: &impl SerializableTransaction,
181 ) -> ClientResult<Signature> {
182 const SEND_RETRIES: usize = 1;
183 const GET_STATUS_RETRIES: usize = usize::MAX;
184
185 'sending: for _ in 0..SEND_RETRIES {
186 let (latest_blockhash, signature) = self
187 .send_transaction_and_get_latest_blockhash(transaction, None)
188 .await?;
189
190 for status_retry in 0..GET_STATUS_RETRIES {
191 match self.get_signature_status(&signature).await? {
192 Some(Ok(_)) => return Ok(signature),
193 Some(Err(e)) => return Err(e.into()),
194 None => {
195 if !self
196 .is_blockhash_valid(&latest_blockhash, CommitmentConfig::processed())
197 .await?
198 {
199 // Block hash is not found by some reason
200 break 'sending;
201 } else if cfg!(not(test))
202 // Ignore sleep at last step.
203 && status_retry < GET_STATUS_RETRIES
204 {
205 // Retry twice a second
206 sleep(Duration::from_millis(500)).await;
207 continue;
208 }
209 }
210 }
211 }
212 }
213
214 Err(RpcError::ForUser(
215 "unable to confirm transaction. This can happen in situations such as transaction \
216 expiration and insufficient fee-payer funds"
217 .to_string(),
218 )
219 .into())
220 }
221
222 async fn send_transaction_and_get_latest_blockhash(
223 &self,
224 transaction: &impl SerializableTransaction,
225 config: Option<RpcSendTransactionConfig>,
226 ) -> ClientResult<(Hash, Signature)> {
227 let (latest_blockhash, signature) = join!(
228 async {
229 if transaction.uses_durable_nonce() {
230 self.get_latest_blockhash_with_commitment(CommitmentConfig::processed())
231 .await
232 .map(|v| v.0)
233 } else {
234 Ok(*transaction.get_recent_blockhash())
235 }
236 },
237 async {
238 if let Some(config) = config {
239 self.send_transaction_with_config(transaction, config).await
240 } else {
241 self.send_transaction(transaction).await
242 }
243 },
244 );
245
246 Ok((latest_blockhash?, signature?))
247 }
248
249 /// Submits a signed transaction to the network.
250 ///
251 /// Before a transaction is processed, the receiving node runs a "preflight
252 /// check" which verifies signatures, checks that the node is healthy,
253 /// and simulates the transaction. If the preflight check fails then an
254 /// error is returned immediately. Preflight checks can be disabled by
255 /// calling [`send_transaction_with_config`] and setting the
256 /// [`skip_preflight`] field of [`RpcSendTransactionConfig`] to `true`.
257 ///
258 /// This method does not wait for the transaction to be processed or
259 /// confirmed before returning successfully. To wait for the transaction to
260 /// be processed or confirmed, use the [`send_and_confirm_transaction`]
261 /// method.
262 ///
263 /// [`send_transaction_with_config`]: RpcClient::send_transaction_with_config
264 /// [`skip_preflight`]: solana_rpc_client_api::config::RpcSendTransactionConfig::skip_preflight
265 /// [`RpcSendTransactionConfig`]: solana_rpc_client_api::config::RpcSendTransactionConfig
266 /// [`send_and_confirm_transaction`]: RpcClient::send_and_confirm_transaction
267 ///
268 /// # Errors
269 ///
270 /// If the transaction is not signed then an error with kind [`RpcError`] is
271 /// returned, containing an [`RpcResponseError`] with `code` set to
272 /// [`JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE`].
273 ///
274 /// If the preflight transaction simulation fails then an error with kind
275 /// [`RpcError`] is returned, containing an [`RpcResponseError`] with `code`
276 /// set to [`JSON_RPC_SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE`].
277 ///
278 /// If the receiving node is unhealthy, e.g. it is not fully synced to
279 /// the cluster, then an error with kind [`RpcError`] is returned,
280 /// containing an [`RpcResponseError`] with `code` set to
281 /// [`JSON_RPC_SERVER_ERROR_NODE_UNHEALTHY`].
282 ///
283 /// [`RpcResponseError`]: RpcError::RpcResponseError
284 /// [`JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE`]: solana_rpc_client_api::custom_error::JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE
285 /// [`JSON_RPC_SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE`]: solana_rpc_client_api::custom_error::JSON_RPC_SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE
286 /// [`JSON_RPC_SERVER_ERROR_NODE_UNHEALTHY`]: solana_rpc_client_api::custom_error::JSON_RPC_SERVER_ERROR_NODE_UNHEALTHY
287 ///
288 /// # RPC Reference
289 ///
290 /// This method is built on the [`sendTransaction`] RPC method.
291 ///
292 /// [`sendTransaction`]: https://solana.com/docs/rpc/http/sendtransaction
293 ///
294 /// # Examples
295 ///
296 /// ```ignore
297 /// # use solana_rpc_client_api::client_error::Error;
298 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
299 /// # use solana_hash::Hash;
300 /// # use solana_keypair::Keypair;
301 /// # use solana_system_transaction as system_transaction;
302 /// # use solana_signature::Signature;
303 /// # use solana_signer::Signer;
304 /// # futures::executor::block_on(async {
305 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
306 /// // Transfer lamports from Alice to Bob
307 /// # let alice = Keypair::new();
308 /// # let bob = Keypair::new();
309 /// # let lamports = 50;
310 /// let latest_blockhash = rpc_client.get_latest_blockhash().await?;
311 /// let tx = system_transaction::transfer(&alice, &bob.pubkey(), lamports, latest_blockhash);
312 /// let signature = rpc_client.send_transaction(&tx).await?;
313 /// # Ok::<(), Error>(())
314 /// # })?;
315 /// # Ok::<(), Error>(())
316 /// ```
317 pub async fn send_transaction(
318 &self,
319 transaction: &impl SerializableTransaction,
320 ) -> ClientResult<Signature> {
321 self.send_transaction_with_config(
322 transaction,
323 RpcSendTransactionConfig {
324 preflight_commitment: Some(self.commitment().commitment),
325 ..RpcSendTransactionConfig::default()
326 },
327 )
328 .await
329 }
330
331 /// Submits a signed transaction to the network.
332 ///
333 /// Before a transaction is processed, the receiving node runs a "preflight
334 /// check" which verifies signatures, checks that the node is healthy, and
335 /// simulates the transaction. If the preflight check fails then an error is
336 /// returned immediately. Preflight checks can be disabled by setting the
337 /// [`skip_preflight`] field of [`RpcSendTransactionConfig`] to `true`.
338 ///
339 /// This method does not wait for the transaction to be processed or
340 /// confirmed before returning successfully. To wait for the transaction to
341 /// be processed or confirmed, use the [`send_and_confirm_transaction`]
342 /// method.
343 ///
344 /// [`send_transaction_with_config`]: RpcClient::send_transaction_with_config
345 /// [`skip_preflight`]: solana_rpc_client_api::config::RpcSendTransactionConfig::skip_preflight
346 /// [`RpcSendTransactionConfig`]: solana_rpc_client_api::config::RpcSendTransactionConfig
347 /// [`send_and_confirm_transaction`]: RpcClient::send_and_confirm_transaction
348 ///
349 /// # Errors
350 ///
351 /// If preflight checks are enabled, if the transaction is not signed
352 /// then an error with kind [`RpcError`] is returned, containing an
353 /// [`RpcResponseError`] with `code` set to
354 /// [`JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE`].
355 ///
356 /// If preflight checks are enabled, if the preflight transaction simulation
357 /// fails then an error with kind [`RpcError`] is returned, containing an
358 /// [`RpcResponseError`] with `code` set to
359 /// [`JSON_RPC_SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE`].
360 ///
361 /// If the receiving node is unhealthy, e.g. it is not fully synced to
362 /// the cluster, then an error with kind [`RpcError`] is returned,
363 /// containing an [`RpcResponseError`] with `code` set to
364 /// [`JSON_RPC_SERVER_ERROR_NODE_UNHEALTHY`].
365 ///
366 /// [`RpcResponseError`]: RpcError::RpcResponseError
367 /// [`JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE`]: solana_rpc_client_api::custom_error::JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE
368 /// [`JSON_RPC_SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE`]: solana_rpc_client_api::custom_error::JSON_RPC_SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE
369 /// [`JSON_RPC_SERVER_ERROR_NODE_UNHEALTHY`]: solana_rpc_client_api::custom_error::JSON_RPC_SERVER_ERROR_NODE_UNHEALTHY
370 ///
371 /// # RPC Reference
372 ///
373 /// This method is built on the [`sendTransaction`] RPC method.
374 ///
375 /// [`sendTransaction`]: https://solana.com/docs/rpc/http/sendtransaction
376 ///
377 /// # Examples
378 ///
379 /// ```ignore
380 /// # use solana_rpc_client_api::{
381 /// # client_error::Error,
382 /// # config::RpcSendTransactionConfig,
383 /// # };
384 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
385 /// # use solana_hash::Hash;
386 /// # use solana_keypair::Keypair;
387 /// # use solana_system_transaction as system_transaction;
388 /// # use solana_signature::Signature;
389 /// # use solana_signer::Signer;
390 /// # futures::executor::block_on(async {
391 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
392 /// // Transfer lamports from Alice to Bob
393 /// # let alice = Keypair::new();
394 /// # let bob = Keypair::new();
395 /// # let lamports = 50;
396 /// let latest_blockhash = rpc_client.get_latest_blockhash().await?;
397 /// let tx = system_transaction::transfer(&alice, &bob.pubkey(), lamports, latest_blockhash);
398 /// let config = RpcSendTransactionConfig {
399 /// skip_preflight: true,
400 /// .. RpcSendTransactionConfig::default()
401 /// };
402 /// let signature = rpc_client.send_transaction_with_config(
403 /// &tx,
404 /// config,
405 /// ).await?;
406 /// # Ok::<(), Error>(())
407 /// # })?;
408 /// # Ok::<(), Error>(())
409 /// ```
410 pub async fn send_transaction_with_config(
411 &self,
412 transaction: &impl SerializableTransaction,
413 config: RpcSendTransactionConfig,
414 ) -> ClientResult<Signature> {
415 let encoding = config.encoding.unwrap_or(UiTransactionEncoding::Base64);
416 let preflight_commitment = CommitmentConfig {
417 commitment: config.preflight_commitment.unwrap_or_default(),
418 };
419 let config = RpcSendTransactionConfig {
420 encoding: Some(encoding),
421 preflight_commitment: Some(preflight_commitment.commitment),
422 ..config
423 };
424 let serialized_encoded = serialize_and_encode(transaction, encoding)?;
425 let signature_base58_str: String = match self
426 .send(
427 RpcRequest::SendTransaction,
428 json!([serialized_encoded, config]),
429 )
430 .await
431 {
432 Ok(signature_base58_str) => signature_base58_str,
433 Err(err) => {
434 if let ClientErrorKind::RpcError(RpcError::RpcResponseError {
435 code,
436 message,
437 data,
438 }) = err.kind()
439 {
440 debug!("{code} {message}");
441 if let RpcResponseErrorData::SendTransactionPreflightFailure(
442 RpcSimulateTransactionResult {
443 logs: Some(logs), ..
444 },
445 ) = data
446 {
447 for (i, log) in logs.iter().enumerate() {
448 debug!("{:>3}: {}", i + 1, log);
449 }
450 debug!("");
451 }
452 }
453 return Err(err);
454 }
455 };
456
457 let signature = signature_base58_str
458 .parse::<Signature>()
459 .map_err(|err| Into::<ClientError>::into(RpcError::ParseError(err.to_string())))?;
460 // A mismatching RPC response signature indicates an issue with the RPC node, and
461 // should not be passed along to confirmation methods. The transaction may or may
462 // not have been submitted to the cluster, so callers should verify the success of
463 // the correct transaction signature independently.
464 if signature != *transaction.get_signature() {
465 Err(RpcError::RpcRequestError(format!(
466 "RPC node returned mismatched signature {:?}, expected {:?}",
467 signature,
468 transaction.get_signature()
469 ))
470 .into())
471 } else {
472 Ok(*transaction.get_signature())
473 }
474 }
475
476 /// Check the confirmation status of a transaction.
477 ///
478 /// Returns `true` if the given transaction succeeded and has been committed
479 /// with the configured [commitment level][cl], which can be retrieved with
480 /// the [`commitment`](RpcClient::commitment) method.
481 ///
482 /// [cl]: https://solana.com/docs/rpc#configuring-state-commitment
483 ///
484 /// Note that this method does not wait for a transaction to be confirmed
485 /// — it only checks whether a transaction has been confirmed. To
486 /// submit a transaction and wait for it to confirm, use
487 /// [`send_and_confirm_transaction`][RpcClient::send_and_confirm_transaction].
488 ///
489 /// _This method returns `false` if the transaction failed, even if it has
490 /// been confirmed._
491 ///
492 /// # RPC Reference
493 ///
494 /// This method is built on the [`getSignatureStatuses`] RPC method.
495 ///
496 /// [`getSignatureStatuses`]: https://solana.com/docs/rpc/http/getsignaturestatuses
497 ///
498 /// # Examples
499 ///
500 /// ```ignore
501 /// # use solana_rpc_client_api::client_error::Error;
502 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
503 /// # use solana_keypair::Keypair;
504 /// # use solana_system_transaction as system_transaction;
505 /// # use solana_signature::Signature;
506 /// # use solana_signer::Signer;
507 /// # futures::executor::block_on(async {
508 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
509 /// // Transfer lamports from Alice to Bob and wait for confirmation
510 /// # let alice = Keypair::new();
511 /// # let bob = Keypair::new();
512 /// # let lamports = 50;
513 /// let latest_blockhash = rpc_client.get_latest_blockhash().await?;
514 /// let tx = system_transaction::transfer(&alice, &bob.pubkey(), lamports, latest_blockhash);
515 /// let signature = rpc_client.send_transaction(&tx).await?;
516 ///
517 /// loop {
518 /// let confirmed = rpc_client.confirm_transaction(&signature).await?;
519 /// if confirmed {
520 /// break;
521 /// }
522 /// }
523 /// # Ok::<(), Error>(())
524 /// # })?;
525 /// # Ok::<(), Error>(())
526 /// ```
527 pub async fn confirm_transaction(&self, signature: &Signature) -> ClientResult<bool> {
528 Ok(self
529 .confirm_transaction_with_commitment(signature, self.commitment())
530 .await?
531 .value)
532 }
533
534 /// Check the confirmation status of a transaction.
535 ///
536 /// Returns an [`RpcResult`] with value `true` if the given transaction
537 /// succeeded and has been committed with the given [commitment level][cl].
538 ///
539 /// [cl]: https://solana.com/docs/rpc#configuring-state-commitment
540 ///
541 /// Note that this method does not wait for a transaction to be confirmed
542 /// — it only checks whether a transaction has been confirmed. To
543 /// submit a transaction and wait for it to confirm, use
544 /// [`send_and_confirm_transaction`][RpcClient::send_and_confirm_transaction].
545 ///
546 /// _This method returns an [`RpcResult`] with value `false` if the
547 /// transaction failed, even if it has been confirmed._
548 ///
549 /// # RPC Reference
550 ///
551 /// This method is built on the [`getSignatureStatuses`] RPC method.
552 ///
553 /// [`getSignatureStatuses`]: https://solana.com/docs/rpc/http/getsignaturestatuses
554 ///
555 /// # Examples
556 ///
557 /// ```ignore
558 /// # use solana_rpc_client_api::client_error::Error;
559 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
560 /// # use solana_commitment_config::CommitmentConfig;
561 /// # use solana_keypair::Keypair;
562 /// # use solana_signature::Signature;
563 /// # use solana_signer::Signer;
564 /// # use solana_system_transaction as system_transaction;
565 /// # use std::time::Duration;
566 /// # futures::executor::block_on(async {
567 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
568 /// // Transfer lamports from Alice to Bob and wait for confirmation
569 /// # let alice = Keypair::new();
570 /// # let bob = Keypair::new();
571 /// # let lamports = 50;
572 /// let latest_blockhash = rpc_client.get_latest_blockhash().await?;
573 /// let tx = system_transaction::transfer(&alice, &bob.pubkey(), lamports, latest_blockhash);
574 /// let signature = rpc_client.send_transaction(&tx).await?;
575 ///
576 /// loop {
577 /// let commitment_config = CommitmentConfig::processed();
578 /// let confirmed = rpc_client.confirm_transaction_with_commitment(&signature, commitment_config).await?;
579 /// if confirmed.value {
580 /// break;
581 /// }
582 /// }
583 /// # Ok::<(), Error>(())
584 /// # })?;
585 /// # Ok::<(), Error>(())
586 /// ```
587 pub async fn confirm_transaction_with_commitment(
588 &self,
589 signature: &Signature,
590 commitment_config: CommitmentConfig,
591 ) -> RpcResult<bool> {
592 let Response { context, value } = self.get_signature_statuses(&[*signature]).await?;
593
594 Ok(Response {
595 context,
596 value: value[0]
597 .as_ref()
598 .filter(|result| result.satisfies_commitment(commitment_config))
599 .map(|result| result.status.is_ok())
600 .unwrap_or_default(),
601 })
602 }
603
604 /// Simulates sending a transaction.
605 ///
606 /// If the transaction fails, then the [`err`] field of the returned
607 /// [`RpcSimulateTransactionResult`] will be `Some`. Any logs emitted from
608 /// the transaction are returned in the [`logs`] field.
609 ///
610 /// [`err`]: solana_rpc_client_api::response::RpcSimulateTransactionResult::err
611 /// [`logs`]: solana_rpc_client_api::response::RpcSimulateTransactionResult::logs
612 ///
613 /// Simulating a transaction is similar to the ["preflight check"] that is
614 /// run by default when sending a transaction.
615 ///
616 /// ["preflight check"]: https://solana.com/docs/rpc/http/sendtransaction
617 ///
618 /// By default, signatures are not verified during simulation. To verify
619 /// signatures, call the [`simulate_transaction_with_config`] method, with
620 /// the [`sig_verify`] field of [`RpcSimulateTransactionConfig`] set to
621 /// `true`.
622 ///
623 /// [`simulate_transaction_with_config`]: RpcClient::simulate_transaction_with_config
624 /// [`sig_verify`]: solana_rpc_client_api::config::RpcSimulateTransactionConfig::sig_verify
625 ///
626 /// # RPC Reference
627 ///
628 /// This method is built on the [`simulateTransaction`] RPC method.
629 ///
630 /// [`simulateTransaction`]: https://solana.com/docs/rpc/http/simulatetransaction
631 ///
632 /// # Examples
633 ///
634 /// ```ignore
635 /// # use solana_rpc_client_api::{
636 /// # client_error::Error,
637 /// # response::RpcSimulateTransactionResult,
638 /// # };
639 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
640 /// # use solana_hash::Hash;
641 /// # use solana_keypair::Keypair;
642 /// # use solana_system_transaction as system_transaction;
643 /// # use solana_signature::Signature;
644 /// # use solana_signer::Signer;
645 /// # futures::executor::block_on(async {
646 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
647 /// // Transfer lamports from Alice to Bob
648 /// # let alice = Keypair::new();
649 /// # let bob = Keypair::new();
650 /// # let lamports = 50;
651 /// let latest_blockhash = rpc_client.get_latest_blockhash().await?;
652 /// let tx = system_transaction::transfer(&alice, &bob.pubkey(), lamports, latest_blockhash);
653 /// let result = rpc_client.simulate_transaction(&tx).await?;
654 /// assert!(result.value.err.is_none());
655 /// # Ok::<(), Error>(())
656 /// # })?;
657 /// # Ok::<(), Error>(())
658 /// ```
659 pub async fn simulate_transaction(
660 &self,
661 transaction: &impl SerializableTransaction,
662 ) -> RpcResult<RpcSimulateTransactionResult> {
663 self.simulate_transaction_with_config(
664 transaction,
665 RpcSimulateTransactionConfig {
666 commitment: Some(self.commitment()),
667 ..RpcSimulateTransactionConfig::default()
668 },
669 )
670 .await
671 }
672
673 /// Simulates sending a transaction.
674 ///
675 /// If the transaction fails, then the [`err`] field of the returned
676 /// [`RpcSimulateTransactionResult`] will be `Some`. Any logs emitted from
677 /// the transaction are returned in the [`logs`] field.
678 ///
679 /// [`err`]: solana_rpc_client_api::response::RpcSimulateTransactionResult::err
680 /// [`logs`]: solana_rpc_client_api::response::RpcSimulateTransactionResult::logs
681 ///
682 /// Simulating a transaction is similar to the ["preflight check"] that is
683 /// run by default when sending a transaction.
684 ///
685 /// ["preflight check"]: https://solana.com/docs/rpc/http/sendtransaction
686 ///
687 /// By default, signatures are not verified during simulation. To verify
688 /// signatures, call the [`simulate_transaction_with_config`] method, with
689 /// the [`sig_verify`] field of [`RpcSimulateTransactionConfig`] set to
690 /// `true`.
691 ///
692 /// [`simulate_transaction_with_config`]: RpcClient::simulate_transaction_with_config
693 /// [`sig_verify`]: solana_rpc_client_api::config::RpcSimulateTransactionConfig::sig_verify
694 ///
695 /// This method can additionally query information about accounts by
696 /// including them in the [`accounts`] field of the
697 /// [`RpcSimulateTransactionConfig`] argument, in which case those results
698 /// are reported in the [`accounts`][accounts2] field of the returned
699 /// [`RpcSimulateTransactionResult`].
700 ///
701 /// [`accounts`]: solana_rpc_client_api::config::RpcSimulateTransactionConfig::accounts
702 /// [accounts2]: solana_rpc_client_api::response::RpcSimulateTransactionResult::accounts
703 ///
704 /// # RPC Reference
705 ///
706 /// This method is built on the [`simulateTransaction`] RPC method.
707 ///
708 /// [`simulateTransaction`]: https://solana.com/docs/rpc/http/simulatetransaction
709 ///
710 /// # Examples
711 ///
712 /// ```ignore
713 /// # use solana_hash::Hash;
714 /// # use solana_keypair::Keypair;
715 /// # use solana_rpc_client_api::{
716 /// # client_error::Error,
717 /// # config::RpcSimulateTransactionConfig,
718 /// # response::RpcSimulateTransactionResult,
719 /// # };
720 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
721 /// # use solana_signer::Signer;
722 /// # use solana_system_transaction as system_transaction;
723 /// # futures::executor::block_on(async {
724 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
725 /// // Transfer lamports from Alice to Bob
726 /// # let alice = Keypair::new();
727 /// # let bob = Keypair::new();
728 /// # let lamports = 50;
729 /// let latest_blockhash = rpc_client.get_latest_blockhash().await?;
730 /// let tx = system_transaction::transfer(&alice, &bob.pubkey(), lamports, latest_blockhash);
731 /// let config = RpcSimulateTransactionConfig {
732 /// sig_verify: true,
733 /// .. RpcSimulateTransactionConfig::default()
734 /// };
735 /// let result = rpc_client.simulate_transaction_with_config(
736 /// &tx,
737 /// config,
738 /// ).await?;
739 /// assert!(result.value.err.is_none());
740 /// # Ok::<(), Error>(())
741 /// # })?;
742 /// # Ok::<(), Error>(())
743 /// ```
744 pub async fn simulate_transaction_with_config(
745 &self,
746 transaction: &impl SerializableTransaction,
747 config: RpcSimulateTransactionConfig,
748 ) -> RpcResult<RpcSimulateTransactionResult> {
749 let encoding = config.encoding.unwrap_or(UiTransactionEncoding::Base64);
750 let commitment = config.commitment.unwrap_or_default();
751 let config = RpcSimulateTransactionConfig {
752 encoding: Some(encoding),
753 commitment: Some(commitment),
754 ..config
755 };
756 let serialized_encoded = serialize_and_encode(transaction, encoding)?;
757 self.send(
758 RpcRequest::SimulateTransaction,
759 json!([serialized_encoded, config]),
760 )
761 .await
762 }
763
764 /// Returns the highest slot information that the node has snapshots for.
765 ///
766 /// This will find the highest full snapshot slot, and the highest incremental snapshot slot
767 /// _based on_ the full snapshot slot, if there is one.
768 ///
769 /// # RPC Reference
770 ///
771 /// This method corresponds directly to the [`getHighestSnapshotSlot`] RPC method.
772 ///
773 /// [`getHighestSnapshotSlot`]: https://solana.com/docs/rpc/http/gethighestsnapshotslot
774 ///
775 /// # Examples
776 ///
777 /// ```ignore
778 /// # use solana_rpc_client_api::client_error::Error;
779 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
780 /// # futures::executor::block_on(async {
781 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
782 /// let snapshot_slot_info = rpc_client.get_highest_snapshot_slot().await?;
783 /// # Ok::<(), Error>(())
784 /// # })?;
785 /// # Ok::<(), Error>(())
786 /// ```
787 pub async fn get_highest_snapshot_slot(&self) -> ClientResult<RpcSnapshotSlotInfo> {
788 self.send(RpcRequest::GetHighestSnapshotSlot, Value::Null)
789 .await
790 }
791
792 /// Check if a transaction has been processed with the default [commitment level][cl].
793 ///
794 /// [cl]: https://solana.com/docs/rpc#configuring-state-commitment
795 ///
796 /// If the transaction has been processed with the default commitment level,
797 /// then this method returns `Ok` of `Some`. If the transaction has not yet
798 /// been processed with the default commitment level, it returns `Ok` of
799 /// `None`.
800 ///
801 /// If the transaction has been processed with the default commitment level,
802 /// and the transaction succeeded, this method returns `Ok(Some(Ok(())))`.
803 /// If the transaction has been processed with the default commitment level,
804 /// and the transaction failed, this method returns `Ok(Some(Err(_)))`,
805 /// where the interior error is type [`TransactionError`].
806 ///
807 /// [`TransactionError`]: solana_transaction_error::TransactionError
808 ///
809 /// This function only searches a node's recent history, including all
810 /// recent slots, plus up to
811 /// [`MAX_RECENT_BLOCKHASHES`][solana_clock::MAX_RECENT_BLOCKHASHES]
812 /// rooted slots. To search the full transaction history use the
813 /// [`get_signature_status_with_commitment_and_history`][RpcClient::get_signature_status_with_commitment_and_history]
814 /// method.
815 ///
816 /// # RPC Reference
817 ///
818 /// This method is built on the [`getSignatureStatuses`] RPC method.
819 ///
820 /// [`getSignatureStatuses`]: https://solana.com/docs/rpc/http/getsignaturestatuses
821 ///
822 /// # Examples
823 ///
824 /// ```ignore
825 /// # use solana_rpc_client_api::client_error::Error;
826 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
827 /// # use solana_hash::Hash;
828 /// # use solana_keypair::Keypair;
829 /// # use solana_system_transaction as system_transaction;
830 /// # use solana_signature::Signature;
831 /// # use solana_signer::Signer;
832 /// # futures::executor::block_on(async {
833 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
834 /// # let alice = Keypair::new();
835 /// # let bob = Keypair::new();
836 /// # let lamports = 50;
837 /// # let latest_blockhash = rpc_client.get_latest_blockhash().await?;
838 /// # let tx = system_transaction::transfer(&alice, &bob.pubkey(), lamports, latest_blockhash);
839 /// let signature = rpc_client.send_transaction(&tx).await?;
840 /// let status = rpc_client.get_signature_status(&signature).await?;
841 /// # Ok::<(), Error>(())
842 /// # })?;
843 /// # Ok::<(), Error>(())
844 /// ```
845 pub async fn get_signature_status(
846 &self,
847 signature: &Signature,
848 ) -> ClientResult<Option<TransactionResult<()>>> {
849 self.get_signature_status_with_commitment(signature, self.commitment())
850 .await
851 }
852
853 /// Gets the statuses of a list of transaction signatures.
854 ///
855 /// The returned vector of [`TransactionStatus`] has the same length as the
856 /// input slice.
857 ///
858 /// For any transaction that has not been processed by the network, the
859 /// value of the corresponding entry in the returned vector is `None`. As a
860 /// result, a transaction that has recently been submitted will not have a
861 /// status immediately.
862 ///
863 /// To submit a transaction and wait for it to confirm, use
864 /// [`send_and_confirm_transaction`][RpcClient::send_and_confirm_transaction].
865 ///
866 /// This function ignores the configured confirmation level, and returns the
867 /// transaction status whatever it is. It does not wait for transactions to
868 /// be processed.
869 ///
870 /// This function only searches a node's recent history, including all
871 /// recent slots, plus up to
872 /// [`MAX_RECENT_BLOCKHASHES`][solana_clock::MAX_RECENT_BLOCKHASHES]
873 /// rooted slots. To search the full transaction history use the
874 /// [`get_signature_statuses_with_history`][RpcClient::get_signature_statuses_with_history]
875 /// method.
876 ///
877 /// # Errors
878 ///
879 /// Any individual `TransactionStatus` may have triggered an error during
880 /// processing, in which case its [`err`][`TransactionStatus::err`] field
881 /// will be `Some`.
882 ///
883 /// # RPC Reference
884 ///
885 /// This method corresponds directly to the [`getSignatureStatuses`] RPC method.
886 ///
887 /// [`getSignatureStatuses`]: https://solana.com/docs/rpc/http/getsignaturestatuses
888 ///
889 /// # Examples
890 ///
891 /// ```ignore
892 /// # use solana_rpc_client_api::client_error::Error;
893 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
894 /// # use solana_hash::Hash;
895 /// # use solana_keypair::Keypair;
896 /// # use solana_system_transaction as system_transaction;
897 /// # use solana_signature::Signature;
898 /// # use solana_signer::Signer;
899 /// # use std::time::Duration;
900 /// # futures::executor::block_on(async {
901 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
902 /// # let alice = Keypair::new();
903 /// // Send lamports from Alice to Bob and wait for the transaction to be processed
904 /// # let bob = Keypair::new();
905 /// # let lamports = 50;
906 /// let latest_blockhash = rpc_client.get_latest_blockhash().await?;
907 /// let tx = system_transaction::transfer(&alice, &bob.pubkey(), lamports, latest_blockhash);
908 /// let signature = rpc_client.send_transaction(&tx).await?;
909 ///
910 /// let status = loop {
911 /// let statuses = rpc_client.get_signature_statuses(&[signature]).await?.value;
912 /// if let Some(status) = statuses[0].clone() {
913 /// break status;
914 /// }
915 /// std::thread::sleep(Duration::from_millis(100));
916 /// };
917 ///
918 /// assert!(status.err.is_none());
919 /// # Ok::<(), Error>(())
920 /// # })?;
921 /// # Ok::<(), Error>(())
922 /// ```
923 pub async fn get_signature_statuses(
924 &self,
925 signatures: &[Signature],
926 ) -> RpcResult<Vec<Option<TransactionStatus>>> {
927 let signatures: Vec<_> = signatures.iter().map(|s| s.to_string()).collect();
928 self.send(RpcRequest::GetSignatureStatuses, json!([signatures]))
929 .await
930 }
931
932 /// Gets the statuses of a list of transaction signatures.
933 ///
934 /// The returned vector of [`TransactionStatus`] has the same length as the
935 /// input slice.
936 ///
937 /// For any transaction that has not been processed by the network, the
938 /// value of the corresponding entry in the returned vector is `None`. As a
939 /// result, a transaction that has recently been submitted will not have a
940 /// status immediately.
941 ///
942 /// To submit a transaction and wait for it to confirm, use
943 /// [`send_and_confirm_transaction`][RpcClient::send_and_confirm_transaction].
944 ///
945 /// This function ignores the configured confirmation level, and returns the
946 /// transaction status whatever it is. It does not wait for transactions to
947 /// be processed.
948 ///
949 /// This function searches a node's full ledger history and (if implemented) long-term storage. To search for
950 /// transactions in recent slots only use the
951 /// [`get_signature_statuses`][RpcClient::get_signature_statuses] method.
952 ///
953 /// # Errors
954 ///
955 /// Any individual `TransactionStatus` may have triggered an error during
956 /// processing, in which case its [`err`][`TransactionStatus::err`] field
957 /// will be `Some`.
958 ///
959 /// # RPC Reference
960 ///
961 /// This method corresponds directly to the [`getSignatureStatuses`] RPC
962 /// method, with the `searchTransactionHistory` configuration option set to
963 /// `true`.
964 ///
965 /// [`getSignatureStatuses`]: https://solana.com/docs/rpc/http/getsignaturestatuses
966 ///
967 /// # Examples
968 ///
969 /// ```ignore
970 /// # use solana_rpc_client_api::client_error::Error;
971 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
972 /// # use solana_hash::Hash;
973 /// # use solana_keypair::Keypair;
974 /// # use solana_system_transaction as system_transaction;
975 /// # use solana_signature::Signature;
976 /// # use solana_signer::Signer;
977 /// # futures::executor::block_on(async {
978 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
979 /// # let alice = Keypair::new();
980 /// # fn get_old_transaction_signature() -> Signature { Signature::default() }
981 /// // Check if an old transaction exists
982 /// let signature = get_old_transaction_signature();
983 /// let latest_blockhash = rpc_client.get_latest_blockhash().await?;
984 /// let statuses = rpc_client.get_signature_statuses_with_history(&[signature]).await?.value;
985 /// if statuses[0].is_none() {
986 /// println!("old transaction does not exist");
987 /// }
988 /// # Ok::<(), Error>(())
989 /// # })?;
990 /// # Ok::<(), Error>(())
991 /// ```
992 pub async fn get_signature_statuses_with_history(
993 &self,
994 signatures: &[Signature],
995 ) -> RpcResult<Vec<Option<TransactionStatus>>> {
996 let signatures: Vec<_> = signatures.iter().map(|s| s.to_string()).collect();
997 self.send(
998 RpcRequest::GetSignatureStatuses,
999 json!([signatures, {
1000 "searchTransactionHistory": true
1001 }]),
1002 )
1003 .await
1004 }
1005
1006 /// Check if a transaction has been processed with the given [commitment level][cl].
1007 ///
1008 /// [cl]: https://solana.com/docs/rpc#configuring-state-commitment
1009 ///
1010 /// If the transaction has been processed with the given commitment level,
1011 /// then this method returns `Ok` of `Some`. If the transaction has not yet
1012 /// been processed with the given commitment level, it returns `Ok` of
1013 /// `None`.
1014 ///
1015 /// If the transaction has been processed with the given commitment level,
1016 /// and the transaction succeeded, this method returns `Ok(Some(Ok(())))`.
1017 /// If the transaction has been processed with the given commitment level,
1018 /// and the transaction failed, this method returns `Ok(Some(Err(_)))`,
1019 /// where the interior error is type [`TransactionError`].
1020 ///
1021 /// [`TransactionError`]: solana_transaction_error::TransactionError
1022 ///
1023 /// This function only searches a node's recent history, including all
1024 /// recent slots, plus up to
1025 /// [`MAX_RECENT_BLOCKHASHES`][solana_clock::MAX_RECENT_BLOCKHASHES]
1026 /// rooted slots. To search the full transaction history use the
1027 /// [`get_signature_status_with_commitment_and_history`][RpcClient::get_signature_status_with_commitment_and_history]
1028 /// method.
1029 ///
1030 /// # RPC Reference
1031 ///
1032 /// This method is built on the [`getSignatureStatuses`] RPC method.
1033 ///
1034 /// [`getSignatureStatuses`]: https://solana.com/docs/rpc/http/getsignaturestatuses
1035 ///
1036 /// # Examples
1037 ///
1038 /// ```ignore
1039 /// # use solana_rpc_client_api::client_error::Error;
1040 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
1041 /// # use solana_commitment_config::CommitmentConfig;
1042 /// # use solana_keypair::Keypair;
1043 /// # use solana_signature::Signature;
1044 /// # use solana_signer::Signer;
1045 /// # use solana_system_transaction as system_transaction;
1046 /// # futures::executor::block_on(async {
1047 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
1048 /// # let alice = Keypair::new();
1049 /// # let bob = Keypair::new();
1050 /// # let lamports = 50;
1051 /// # let latest_blockhash = rpc_client.get_latest_blockhash().await?;
1052 /// # let tx = system_transaction::transfer(&alice, &bob.pubkey(), lamports, latest_blockhash);
1053 /// let signature = rpc_client.send_and_confirm_transaction(&tx).await?;
1054 /// let commitment_config = CommitmentConfig::processed();
1055 /// let status = rpc_client.get_signature_status_with_commitment(
1056 /// &signature,
1057 /// commitment_config,
1058 /// ).await?;
1059 /// # Ok::<(), Error>(())
1060 /// # })?;
1061 /// # Ok::<(), Error>(())
1062 /// ```
1063 pub async fn get_signature_status_with_commitment(
1064 &self,
1065 signature: &Signature,
1066 commitment_config: CommitmentConfig,
1067 ) -> ClientResult<Option<TransactionResult<()>>> {
1068 let result: Response<Vec<Option<TransactionStatus>>> = self
1069 .send(
1070 RpcRequest::GetSignatureStatuses,
1071 json!([[signature.to_string()]]),
1072 )
1073 .await?;
1074 Ok(result.value[0]
1075 .clone()
1076 .filter(|result| result.satisfies_commitment(commitment_config))
1077 .map(|status_meta| status_meta.status))
1078 }
1079
1080 /// Check if a transaction has been processed with the given [commitment level][cl].
1081 ///
1082 /// [cl]: https://solana.com/docs/rpc#configuring-state-commitment
1083 ///
1084 /// If the transaction has been processed with the given commitment level,
1085 /// then this method returns `Ok` of `Some`. If the transaction has not yet
1086 /// been processed with the given commitment level, it returns `Ok` of
1087 /// `None`.
1088 ///
1089 /// If the transaction has been processed with the given commitment level,
1090 /// and the transaction succeeded, this method returns `Ok(Some(Ok(())))`.
1091 /// If the transaction has been processed with the given commitment level,
1092 /// and the transaction failed, this method returns `Ok(Some(Err(_)))`,
1093 /// where the interior error is type [`TransactionError`].
1094 ///
1095 /// [`TransactionError`]: solana_transaction_error::TransactionError
1096 ///
1097 /// This method optionally searches a node's full ledger history and (if
1098 /// implemented) long-term storage.
1099 ///
1100 /// # RPC Reference
1101 ///
1102 /// This method is built on the [`getSignatureStatuses`] RPC method.
1103 ///
1104 /// [`getSignatureStatuses`]: https://solana.com/docs/rpc/http/getsignaturestatuses
1105 ///
1106 /// # Examples
1107 ///
1108 /// ```ignore
1109 /// # use solana_rpc_client_api::client_error::Error;
1110 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
1111 /// # use solana_commitment_config::CommitmentConfig;
1112 /// # use solana_keypair::Keypair;
1113 /// # use solana_signature::Signature;
1114 /// # use solana_signer::Signer;
1115 /// # use solana_system_transaction as system_transaction;
1116 /// # futures::executor::block_on(async {
1117 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
1118 /// # let alice = Keypair::new();
1119 /// # let bob = Keypair::new();
1120 /// # let lamports = 50;
1121 /// # let latest_blockhash = rpc_client.get_latest_blockhash().await?;
1122 /// # let tx = system_transaction::transfer(&alice, &bob.pubkey(), lamports, latest_blockhash);
1123 /// let signature = rpc_client.send_transaction(&tx).await?;
1124 /// let commitment_config = CommitmentConfig::processed();
1125 /// let search_transaction_history = true;
1126 /// let status = rpc_client.get_signature_status_with_commitment_and_history(
1127 /// &signature,
1128 /// commitment_config,
1129 /// search_transaction_history,
1130 /// ).await?;
1131 /// # Ok::<(), Error>(())
1132 /// # })?;
1133 /// # Ok::<(), Error>(())
1134 /// ```
1135 pub async fn get_signature_status_with_commitment_and_history(
1136 &self,
1137 signature: &Signature,
1138 commitment_config: CommitmentConfig,
1139 search_transaction_history: bool,
1140 ) -> ClientResult<Option<TransactionResult<()>>> {
1141 let result: Response<Vec<Option<TransactionStatus>>> = self
1142 .send(
1143 RpcRequest::GetSignatureStatuses,
1144 json!([[signature.to_string()], {
1145 "searchTransactionHistory": search_transaction_history
1146 }]),
1147 )
1148 .await?;
1149 Ok(result.value[0]
1150 .clone()
1151 .filter(|result| result.satisfies_commitment(commitment_config))
1152 .map(|status_meta| status_meta.status))
1153 }
1154
1155 /// Returns the slot that has reached the configured [commitment level][cl].
1156 ///
1157 /// [cl]: https://solana.com/docs/rpc#configuring-state-commitment
1158 ///
1159 /// # RPC Reference
1160 ///
1161 /// This method corresponds directly to the [`getSlot`] RPC method.
1162 ///
1163 /// [`getSlot`]: https://solana.com/docs/rpc/http/getslot
1164 ///
1165 /// # Examples
1166 ///
1167 /// ```ignore
1168 /// # use solana_rpc_client_api::client_error::Error;
1169 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
1170 /// # futures::executor::block_on(async {
1171 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
1172 /// let slot = rpc_client.get_slot().await?;
1173 /// # Ok::<(), Error>(())
1174 /// # })?;
1175 /// # Ok::<(), Error>(())
1176 /// ```
1177 pub async fn get_slot(&self) -> ClientResult<Slot> {
1178 self.get_slot_with_commitment(self.commitment()).await
1179 }
1180
1181 /// Returns the slot that has reached the given [commitment level][cl].
1182 ///
1183 /// [cl]: https://solana.com/docs/rpc#configuring-state-commitment
1184 ///
1185 /// # RPC Reference
1186 ///
1187 /// This method corresponds directly to the [`getSlot`] RPC method.
1188 ///
1189 /// [`getSlot`]: https://solana.com/docs/rpc/http/getslot
1190 ///
1191 /// # Examples
1192 ///
1193 /// ```ignore
1194 /// # use solana_rpc_client_api::client_error::Error;
1195 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
1196 /// # use solana_commitment_config::CommitmentConfig;
1197 /// # futures::executor::block_on(async {
1198 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
1199 /// let commitment_config = CommitmentConfig::processed();
1200 /// let slot = rpc_client.get_slot_with_commitment(commitment_config).await?;
1201 /// # Ok::<(), Error>(())
1202 /// # })?;
1203 /// # Ok::<(), Error>(())
1204 /// ```
1205 pub async fn get_slot_with_commitment(
1206 &self,
1207 commitment_config: CommitmentConfig,
1208 ) -> ClientResult<Slot> {
1209 self.send(RpcRequest::GetSlot, json!([commitment_config]))
1210 .await
1211 }
1212
1213 /// Returns the block height that has reached the configured [commitment level][cl].
1214 ///
1215 /// [cl]: https://solana.com/docs/rpc#configuring-state-commitment
1216 ///
1217 /// # RPC Reference
1218 ///
1219 /// This method is corresponds directly to the [`getBlockHeight`] RPC method.
1220 ///
1221 /// [`getBlockHeight`]: https://solana.com/docs/rpc/http/getblockheight
1222 ///
1223 /// # Examples
1224 ///
1225 /// ```ignore
1226 /// # use solana_rpc_client_api::client_error::Error;
1227 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
1228 /// # futures::executor::block_on(async {
1229 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
1230 /// let block_height = rpc_client.get_block_height().await?;
1231 /// # Ok::<(), Error>(())
1232 /// # })?;
1233 /// # Ok::<(), Error>(())
1234 /// ```
1235 pub async fn get_block_height(&self) -> ClientResult<u64> {
1236 self.get_block_height_with_commitment(self.commitment())
1237 .await
1238 }
1239
1240 /// Returns the block height that has reached the given [commitment level][cl].
1241 ///
1242 /// [cl]: https://solana.com/docs/rpc#configuring-state-commitment
1243 ///
1244 /// # RPC Reference
1245 ///
1246 /// This method is corresponds directly to the [`getBlockHeight`] RPC method.
1247 ///
1248 /// [`getBlockHeight`]: https://solana.com/docs/rpc/http/getblockheight
1249 ///
1250 /// # Examples
1251 ///
1252 /// ```ignore
1253 /// # use solana_rpc_client_api::client_error::Error;
1254 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
1255 /// # use solana_commitment_config::CommitmentConfig;
1256 /// # futures::executor::block_on(async {
1257 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
1258 /// let commitment_config = CommitmentConfig::processed();
1259 /// let block_height = rpc_client.get_block_height_with_commitment(
1260 /// commitment_config,
1261 /// ).await?;
1262 /// # Ok::<(), Error>(())
1263 /// # })?;
1264 /// # Ok::<(), Error>(())
1265 /// ```
1266 pub async fn get_block_height_with_commitment(
1267 &self,
1268 commitment_config: CommitmentConfig,
1269 ) -> ClientResult<u64> {
1270 self.send(RpcRequest::GetBlockHeight, json!([commitment_config]))
1271 .await
1272 }
1273
1274 /// Returns the slot leaders for a given slot range.
1275 ///
1276 /// # RPC Reference
1277 ///
1278 /// This method corresponds directly to the [`getSlotLeaders`] RPC method.
1279 ///
1280 /// [`getSlotLeaders`]: https://solana.com/docs/rpc/http/getslotleaders
1281 ///
1282 /// # Examples
1283 ///
1284 /// ```ignore
1285 /// # use solana_rpc_client_api::client_error::Error;
1286 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
1287 /// # use solana_clock::Slot;
1288 /// # futures::executor::block_on(async {
1289 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
1290 /// let start_slot = 1;
1291 /// let limit = 3;
1292 /// let leaders = rpc_client.get_slot_leaders(start_slot, limit).await?;
1293 /// # Ok::<(), Error>(())
1294 /// # })?;
1295 /// # Ok::<(), Error>(())
1296 /// ```
1297 pub async fn get_slot_leaders(
1298 &self,
1299 start_slot: Slot,
1300 limit: u64,
1301 ) -> ClientResult<Vec<Pubkey>> {
1302 self.send(RpcRequest::GetSlotLeaders, json!([start_slot, limit]))
1303 .await
1304 .and_then(|slot_leaders: Vec<String>| {
1305 slot_leaders
1306 .iter()
1307 .map(|slot_leader| {
1308 Pubkey::from_str(slot_leader).map_err(|err| {
1309 ClientErrorKind::Custom(format!("pubkey deserialization failed: {err}"))
1310 .into()
1311 })
1312 })
1313 .collect()
1314 })
1315 }
1316
1317 /// Get block production for the current epoch.
1318 ///
1319 /// # RPC Reference
1320 ///
1321 /// This method corresponds directly to the [`getBlockProduction`] RPC method.
1322 ///
1323 /// [`getBlockProduction`]: https://solana.com/docs/rpc/http/getblockproduction
1324 ///
1325 /// # Examples
1326 ///
1327 /// ```ignore
1328 /// # use solana_rpc_client_api::client_error::Error;
1329 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
1330 /// # futures::executor::block_on(async {
1331 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
1332 /// let production = rpc_client.get_block_production().await?;
1333 /// # Ok::<(), Error>(())
1334 /// # })?;
1335 /// # Ok::<(), Error>(())
1336 /// ```
1337 pub async fn get_block_production(&self) -> RpcResult<RpcBlockProduction> {
1338 self.send(RpcRequest::GetBlockProduction, Value::Null).await
1339 }
1340
1341 /// Get block production for the current or previous epoch.
1342 ///
1343 /// # RPC Reference
1344 ///
1345 /// This method corresponds directly to the [`getBlockProduction`] RPC method.
1346 ///
1347 /// [`getBlockProduction`]: https://solana.com/docs/rpc/http/getblockproduction
1348 ///
1349 /// # Examples
1350 ///
1351 /// ```ignore
1352 /// # use solana_rpc_client_api::{
1353 /// # client_error::Error,
1354 /// # config::RpcBlockProductionConfig,
1355 /// # config::RpcBlockProductionConfigRange,
1356 /// # };
1357 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
1358 /// # use solana_commitment_config::CommitmentConfig;
1359 /// # use solana_keypair::Keypair;
1360 /// # use solana_signer::Signer;
1361 /// # futures::executor::block_on(async {
1362 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
1363 /// # let start_slot = 1;
1364 /// # let limit = 3;
1365 /// let leader = rpc_client.get_slot_leaders(start_slot, limit).await?;
1366 /// let leader = leader[0];
1367 /// let range = RpcBlockProductionConfigRange {
1368 /// first_slot: start_slot,
1369 /// last_slot: Some(start_slot + limit),
1370 /// };
1371 /// let config = RpcBlockProductionConfig {
1372 /// identity: Some(leader.to_string()),
1373 /// range: Some(range),
1374 /// commitment: Some(CommitmentConfig::processed()),
1375 /// };
1376 /// let production = rpc_client.get_block_production_with_config(
1377 /// config
1378 /// ).await?;
1379 /// # Ok::<(), Error>(())
1380 /// # })?;
1381 /// # Ok::<(), Error>(())
1382 /// ```
1383 pub async fn get_block_production_with_config(
1384 &self,
1385 config: RpcBlockProductionConfig,
1386 ) -> RpcResult<RpcBlockProduction> {
1387 self.send(RpcRequest::GetBlockProduction, json!([config]))
1388 .await
1389 }
1390
1391 /// Returns information about the current supply.
1392 ///
1393 /// This method uses the configured [commitment level][cl].
1394 ///
1395 /// [cl]: https://solana.com/docs/rpc#configuring-state-commitment
1396 ///
1397 /// # RPC Reference
1398 ///
1399 /// This method corresponds directly to the [`getSupply`] RPC method.
1400 ///
1401 /// [`getSupply`]: https://solana.com/docs/rpc/http/getsupply
1402 ///
1403 /// # Examples
1404 ///
1405 /// ```ignore
1406 /// # use solana_rpc_client_api::client_error::Error;
1407 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
1408 /// # futures::executor::block_on(async {
1409 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
1410 /// let supply = rpc_client.supply().await?;
1411 /// # Ok::<(), Error>(())
1412 /// # })?;
1413 /// # Ok::<(), Error>(())
1414 /// ```
1415 pub async fn supply(&self) -> RpcResult<RpcSupply> {
1416 self.supply_with_commitment(self.commitment()).await
1417 }
1418
1419 /// Returns information about the current supply.
1420 ///
1421 /// # RPC Reference
1422 ///
1423 /// This method corresponds directly to the [`getSupply`] RPC method.
1424 ///
1425 /// [`getSupply`]: https://solana.com/docs/rpc/http/getsupply
1426 ///
1427 /// # Examples
1428 ///
1429 /// ```ignore
1430 /// # use solana_rpc_client_api::client_error::Error;
1431 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
1432 /// # use solana_commitment_config::CommitmentConfig;
1433 /// # futures::executor::block_on(async {
1434 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
1435 /// let commitment_config = CommitmentConfig::processed();
1436 /// let supply = rpc_client.supply_with_commitment(
1437 /// commitment_config,
1438 /// ).await?;
1439 /// # Ok::<(), Error>(())
1440 /// # })?;
1441 /// # Ok::<(), Error>(())
1442 /// ```
1443 pub async fn supply_with_commitment(
1444 &self,
1445 commitment_config: CommitmentConfig,
1446 ) -> RpcResult<RpcSupply> {
1447 self.send(RpcRequest::GetSupply, json!([commitment_config]))
1448 .await
1449 }
1450
1451 /// Returns the 20 largest accounts, by lamport balance.
1452 ///
1453 /// # RPC Reference
1454 ///
1455 /// This method corresponds directly to the [`getLargestAccounts`] RPC
1456 /// method.
1457 ///
1458 /// [`getLargestAccounts`]: https://solana.com/docs/rpc/http/getlargestaccounts
1459 ///
1460 /// # Examples
1461 ///
1462 /// ```ignore
1463 /// # use solana_rpc_client_api::{
1464 /// # client_error::Error,
1465 /// # config::RpcLargestAccountsConfig,
1466 /// # config::RpcLargestAccountsFilter,
1467 /// # };
1468 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
1469 /// # use solana_commitment_config::CommitmentConfig;
1470 /// # futures::executor::block_on(async {
1471 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
1472 /// let commitment_config = CommitmentConfig::processed();
1473 /// let config = RpcLargestAccountsConfig {
1474 /// commitment: Some(commitment_config),
1475 /// filter: Some(RpcLargestAccountsFilter::Circulating),
1476 /// sort_results: None,
1477 /// };
1478 /// let accounts = rpc_client.get_largest_accounts_with_config(
1479 /// config,
1480 /// ).await?;
1481 /// # Ok::<(), Error>(())
1482 /// # })?;
1483 /// # Ok::<(), Error>(())
1484 /// ```
1485 pub async fn get_largest_accounts_with_config(
1486 &self,
1487 config: RpcLargestAccountsConfig,
1488 ) -> RpcResult<Vec<RpcAccountBalance>> {
1489 let commitment = config.commitment.unwrap_or_default();
1490 let config = RpcLargestAccountsConfig {
1491 commitment: Some(commitment),
1492 ..config
1493 };
1494 self.send(RpcRequest::GetLargestAccounts, json!([config]))
1495 .await
1496 }
1497
1498 /// Returns the account info and associated stake for all the voting accounts
1499 /// that have reached the configured [commitment level][cl].
1500 ///
1501 /// [cl]: https://solana.com/docs/rpc#configuring-state-commitment
1502 ///
1503 /// # RPC Reference
1504 ///
1505 /// This method corresponds directly to the [`getVoteAccounts`]
1506 /// RPC method.
1507 ///
1508 /// [`getVoteAccounts`]: https://solana.com/docs/rpc/http/getvoteaccounts
1509 ///
1510 /// # Examples
1511 ///
1512 /// ```ignore
1513 /// # use solana_rpc_client_api::client_error::Error;
1514 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
1515 /// # futures::executor::block_on(async {
1516 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
1517 /// let accounts = rpc_client.get_vote_accounts().await?;
1518 /// # Ok::<(), Error>(())
1519 /// # })?;
1520 /// # Ok::<(), Error>(())
1521 /// ```
1522 pub async fn get_vote_accounts(&self) -> ClientResult<RpcVoteAccountStatus> {
1523 self.get_vote_accounts_with_commitment(self.commitment())
1524 .await
1525 }
1526
1527 /// Returns the account info and associated stake for all the voting accounts
1528 /// that have reached the given [commitment level][cl].
1529 ///
1530 /// [cl]: https://solana.com/docs/rpc#configuring-state-commitment
1531 ///
1532 /// # RPC Reference
1533 ///
1534 /// This method corresponds directly to the [`getVoteAccounts`] RPC method.
1535 ///
1536 /// [`getVoteAccounts`]: https://solana.com/docs/rpc/http/getvoteaccounts
1537 ///
1538 /// # Examples
1539 ///
1540 /// ```ignore
1541 /// # use solana_commitment_config::CommitmentConfig;
1542 /// # use solana_rpc_client_api::client_error::Error;
1543 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
1544 /// # futures::executor::block_on(async {
1545 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
1546 /// let commitment_config = CommitmentConfig::processed();
1547 /// let accounts = rpc_client.get_vote_accounts_with_commitment(
1548 /// commitment_config,
1549 /// ).await?;
1550 /// # Ok::<(), Error>(())
1551 /// # })?;
1552 /// # Ok::<(), Error>(())
1553 /// ```
1554 pub async fn get_vote_accounts_with_commitment(
1555 &self,
1556 commitment_config: CommitmentConfig,
1557 ) -> ClientResult<RpcVoteAccountStatus> {
1558 self.get_vote_accounts_with_config(RpcGetVoteAccountsConfig {
1559 commitment: Some(commitment_config),
1560 ..RpcGetVoteAccountsConfig::default()
1561 })
1562 .await
1563 }
1564
1565 /// Returns the account info and associated stake for all the voting accounts
1566 /// that have reached the given [commitment level][cl].
1567 ///
1568 /// [cl]: https://solana.com/docs/rpc#configuring-state-commitment
1569 ///
1570 /// # RPC Reference
1571 ///
1572 /// This method corresponds directly to the [`getVoteAccounts`] RPC method.
1573 ///
1574 /// [`getVoteAccounts`]: https://solana.com/docs/rpc/http/getvoteaccounts
1575 ///
1576 /// # Examples
1577 ///
1578 /// ```ignore
1579 /// # use solana_rpc_client_api::{
1580 /// # client_error::Error,
1581 /// # config::RpcGetVoteAccountsConfig,
1582 /// # };
1583 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
1584 /// # use solana_commitment_config::CommitmentConfig;
1585 /// # use solana_keypair::Keypair;
1586 /// # use solana_signer::Signer;
1587 /// # futures::executor::block_on(async {
1588 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
1589 /// # let vote_keypair = Keypair::new();
1590 /// let vote_pubkey = vote_keypair.pubkey();
1591 /// let commitment = CommitmentConfig::processed();
1592 /// let config = RpcGetVoteAccountsConfig {
1593 /// vote_pubkey: Some(vote_pubkey.to_string()),
1594 /// commitment: Some(commitment),
1595 /// keep_unstaked_delinquents: Some(true),
1596 /// delinquent_slot_distance: Some(10),
1597 /// };
1598 /// let accounts = rpc_client.get_vote_accounts_with_config(
1599 /// config,
1600 /// ).await?;
1601 /// # Ok::<(), Error>(())
1602 /// # })?;
1603 /// # Ok::<(), Error>(())
1604 /// ```
1605 pub async fn get_vote_accounts_with_config(
1606 &self,
1607 config: RpcGetVoteAccountsConfig,
1608 ) -> ClientResult<RpcVoteAccountStatus> {
1609 self.send(RpcRequest::GetVoteAccounts, json!([config]))
1610 .await
1611 }
1612
1613 pub async fn wait_for_max_stake(
1614 &self,
1615 commitment: CommitmentConfig,
1616 max_stake_percent: f32,
1617 ) -> ClientResult<()> {
1618 self.wait_for_max_stake_below_threshold_with_timeout_helper(
1619 commitment,
1620 max_stake_percent,
1621 None,
1622 )
1623 .await
1624 }
1625
1626 pub async fn wait_for_max_stake_below_threshold_with_timeout(
1627 &self,
1628 commitment: CommitmentConfig,
1629 max_stake_percent: f32,
1630 timeout: Duration,
1631 ) -> ClientResult<()> {
1632 self.wait_for_max_stake_below_threshold_with_timeout_helper(
1633 commitment,
1634 max_stake_percent,
1635 Some(timeout),
1636 )
1637 .await
1638 }
1639
1640 async fn wait_for_max_stake_below_threshold_with_timeout_helper(
1641 &self,
1642 commitment: CommitmentConfig,
1643 max_stake_percent: f32,
1644 timeout: Option<Duration>,
1645 ) -> ClientResult<()> {
1646 let mut current_percent;
1647 let start = Instant::now();
1648 loop {
1649 let vote_accounts = self.get_vote_accounts_with_commitment(commitment).await?;
1650
1651 let mut max = 0;
1652 let total_active_stake = vote_accounts
1653 .current
1654 .iter()
1655 .chain(vote_accounts.delinquent.iter())
1656 .map(|vote_account| {
1657 max = std::cmp::max(max, vote_account.activated_stake);
1658 vote_account.activated_stake
1659 })
1660 .sum::<u64>();
1661 current_percent = 100f32 * max as f32 / total_active_stake as f32;
1662 if current_percent < max_stake_percent {
1663 break;
1664 } else if let Some(timeout) = timeout {
1665 if start.elapsed() > timeout {
1666 return Err(ClientErrorKind::Custom(
1667 "timed out waiting for max stake to drop".to_string(),
1668 )
1669 .into());
1670 }
1671 }
1672
1673 info!(
1674 "Waiting for stake to drop below {max_stake_percent} current: {current_percent:.1}"
1675 );
1676 sleep(Duration::from_secs(5)).await;
1677 }
1678 Ok(())
1679 }
1680
1681 /// Returns information about all the nodes participating in the cluster.
1682 ///
1683 /// # RPC Reference
1684 ///
1685 /// This method corresponds directly to the [`getClusterNodes`]
1686 /// RPC method.
1687 ///
1688 /// [`getClusterNodes`]: https://solana.com/docs/rpc/http/getclusternodes
1689 ///
1690 /// # Examples
1691 ///
1692 /// ```ignore
1693 /// # use solana_rpc_client_api::client_error::Error;
1694 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
1695 /// # futures::executor::block_on(async {
1696 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
1697 /// let cluster_nodes = rpc_client.get_cluster_nodes().await?;
1698 /// # Ok::<(), Error>(())
1699 /// # })?;
1700 /// # Ok::<(), Error>(())
1701 /// ```
1702 pub async fn get_cluster_nodes(&self) -> ClientResult<Vec<RpcContactInfo>> {
1703 self.send(RpcRequest::GetClusterNodes, Value::Null).await
1704 }
1705
1706 /// Returns identity and transaction information about a confirmed block in the ledger.
1707 ///
1708 /// The encodings are returned in [`UiTransactionEncoding::Json`][uite]
1709 /// format. To return transactions in other encodings, use
1710 /// [`get_block_with_encoding`].
1711 ///
1712 /// [`get_block_with_encoding`]: RpcClient::get_block_with_encoding
1713 /// [uite]: UiTransactionEncoding::Json
1714 ///
1715 /// # RPC Reference
1716 ///
1717 /// This method corresponds directly to the [`getBlock`] RPC
1718 /// method.
1719 ///
1720 /// [`getBlock`]: https://solana.com/docs/rpc/http/getblock
1721 ///
1722 /// # Examples
1723 ///
1724 /// ```ignore
1725 /// # use solana_rpc_client_api::client_error::Error;
1726 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
1727 /// # futures::executor::block_on(async {
1728 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
1729 /// # let slot = rpc_client.get_slot().await?;
1730 /// let block = rpc_client.get_block(slot).await?;
1731 /// # Ok::<(), Error>(())
1732 /// # })?;
1733 /// # Ok::<(), Error>(())
1734 /// ```
1735 pub async fn get_block(&self, slot: Slot) -> ClientResult<EncodedConfirmedBlock> {
1736 self.get_block_with_encoding(slot, UiTransactionEncoding::Json)
1737 .await
1738 }
1739
1740 /// Returns identity and transaction information about a confirmed block in the ledger.
1741 ///
1742 /// # RPC Reference
1743 ///
1744 /// This method corresponds directly to the [`getBlock`] RPC method.
1745 ///
1746 /// [`getBlock`]: https://solana.com/docs/rpc/http/getblock
1747 ///
1748 /// # Examples
1749 ///
1750 /// ```ignore
1751 /// # use solana_transaction_status_client_types::UiTransactionEncoding;
1752 /// # use solana_rpc_client_api::client_error::Error;
1753 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
1754 /// # futures::executor::block_on(async {
1755 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
1756 /// # let slot = rpc_client.get_slot().await?;
1757 /// let encoding = UiTransactionEncoding::Base58;
1758 /// let block = rpc_client.get_block_with_encoding(
1759 /// slot,
1760 /// encoding,
1761 /// ).await?;
1762 /// # Ok::<(), Error>(())
1763 /// # })?;
1764 /// # Ok::<(), Error>(())
1765 /// ```
1766 pub async fn get_block_with_encoding(
1767 &self,
1768 slot: Slot,
1769 encoding: UiTransactionEncoding,
1770 ) -> ClientResult<EncodedConfirmedBlock> {
1771 self.send(RpcRequest::GetBlock, json!([slot, encoding]))
1772 .await
1773 }
1774
1775 /// Returns identity and transaction information about a confirmed block in the ledger.
1776 ///
1777 /// # RPC Reference
1778 ///
1779 /// This method corresponds directly to the [`getBlock`] RPC method.
1780 ///
1781 /// [`getBlock`]: https://solana.com/docs/rpc/http/getblock
1782 ///
1783 /// # Examples
1784 ///
1785 /// ```ignore
1786 /// # use solana_transaction_status_client_types::{
1787 /// # TransactionDetails,
1788 /// # UiTransactionEncoding,
1789 /// # };
1790 /// # use solana_rpc_client_api::{
1791 /// # config::RpcBlockConfig,
1792 /// # client_error::Error,
1793 /// # };
1794 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
1795 /// # futures::executor::block_on(async {
1796 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
1797 /// # let slot = rpc_client.get_slot().await?;
1798 /// let config = RpcBlockConfig {
1799 /// encoding: Some(UiTransactionEncoding::Base58),
1800 /// transaction_details: Some(TransactionDetails::None),
1801 /// rewards: Some(true),
1802 /// commitment: None,
1803 /// max_supported_transaction_version: Some(0),
1804 /// };
1805 /// let block = rpc_client.get_block_with_config(
1806 /// slot,
1807 /// config,
1808 /// ).await?;
1809 /// # Ok::<(), Error>(())
1810 /// # })?;
1811 /// # Ok::<(), Error>(())
1812 /// ```
1813 pub async fn get_block_with_config(
1814 &self,
1815 slot: Slot,
1816 config: RpcBlockConfig,
1817 ) -> ClientResult<UiConfirmedBlock> {
1818 self.send(RpcRequest::GetBlock, json!([slot, config])).await
1819 }
1820
1821 /// Returns a list of finalized blocks between two slots.
1822 ///
1823 /// The range is inclusive, with results including the block for both
1824 /// `start_slot` and `end_slot`.
1825 ///
1826 /// If `end_slot` is not provided, then the end slot is for the latest
1827 /// finalized block.
1828 ///
1829 /// This method may not return blocks for the full range of slots if some
1830 /// slots do not have corresponding blocks. To simply get a specific number
1831 /// of sequential blocks, use the [`get_blocks_with_limit`] method.
1832 ///
1833 /// This method uses the [`Finalized`] [commitment level][cl].
1834 ///
1835 /// [`Finalized`]: CommitmentLevel::Finalized
1836 /// [`get_blocks_with_limit`]: RpcClient::get_blocks_with_limit.
1837 /// [cl]: https://solana.com/docs/rpc#configuring-state-commitment
1838 ///
1839 /// # Errors
1840 ///
1841 /// This method returns an error if the range is greater than 500,000 slots.
1842 ///
1843 /// # RPC Reference
1844 ///
1845 /// This method corresponds directly to the [`getBlocks`] RPC method.
1846 ///
1847 /// [`getBlocks`]: https://solana.com/docs/rpc/http/getblocks
1848 ///
1849 /// # Examples
1850 ///
1851 /// ```ignore
1852 /// # use solana_rpc_client_api::client_error::Error;
1853 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
1854 /// # futures::executor::block_on(async {
1855 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
1856 /// // Get up to the first 10 blocks
1857 /// let start_slot = 0;
1858 /// let end_slot = 9;
1859 /// let blocks = rpc_client.get_blocks(start_slot, Some(end_slot)).await?;
1860 /// # Ok::<(), Error>(())
1861 /// # })?;
1862 /// # Ok::<(), Error>(())
1863 /// ```
1864 pub async fn get_blocks(
1865 &self,
1866 start_slot: Slot,
1867 end_slot: Option<Slot>,
1868 ) -> ClientResult<Vec<Slot>> {
1869 self.send(RpcRequest::GetBlocks, json!([start_slot, end_slot]))
1870 .await
1871 }
1872
1873 /// Returns a list of confirmed blocks between two slots.
1874 ///
1875 /// The range is inclusive, with results including the block for both
1876 /// `start_slot` and `end_slot`.
1877 ///
1878 /// If `end_slot` is not provided, then the end slot is for the latest
1879 /// block with the given [commitment level][cl].
1880 ///
1881 /// [cl]: https://solana.com/docs/rpc#configuring-state-commitment
1882 ///
1883 /// This method may not return blocks for the full range of slots if some
1884 /// slots do not have corresponding blocks. To simply get a specific number
1885 /// of sequential blocks, use the [`get_blocks_with_limit_and_commitment`]
1886 /// method.
1887 ///
1888 /// [`get_blocks_with_limit_and_commitment`]: RpcClient::get_blocks_with_limit_and_commitment.
1889 ///
1890 /// # Errors
1891 ///
1892 /// This method returns an error if the range is greater than 500,000 slots.
1893 ///
1894 /// This method returns an error if the given commitment level is below
1895 /// [`Confirmed`].
1896 ///
1897 /// [`Confirmed`]: CommitmentLevel::Confirmed
1898 ///
1899 /// # RPC Reference
1900 ///
1901 /// This method corresponds directly to the [`getBlocks`] RPC method.
1902 ///
1903 /// [`getBlocks`]: https://solana.com/docs/rpc/http/getblocks
1904 ///
1905 /// # Examples
1906 ///
1907 /// ```ignore
1908 /// # use solana_commitment_config::CommitmentConfig;
1909 /// # use solana_rpc_client_api::client_error::Error;
1910 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
1911 /// # futures::executor::block_on(async {
1912 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
1913 /// // Get up to the first 10 blocks
1914 /// let start_slot = 0;
1915 /// let end_slot = 9;
1916 /// // Method does not support commitment below `confirmed`
1917 /// let commitment_config = CommitmentConfig::confirmed();
1918 /// let blocks = rpc_client.get_blocks_with_commitment(
1919 /// start_slot,
1920 /// Some(end_slot),
1921 /// commitment_config,
1922 /// ).await?;
1923 /// # Ok::<(), Error>(())
1924 /// # })?;
1925 /// # Ok::<(), Error>(())
1926 /// ```
1927 pub async fn get_blocks_with_commitment(
1928 &self,
1929 start_slot: Slot,
1930 end_slot: Option<Slot>,
1931 commitment_config: CommitmentConfig,
1932 ) -> ClientResult<Vec<Slot>> {
1933 let json = if end_slot.is_some() {
1934 json!([start_slot, end_slot, commitment_config])
1935 } else {
1936 json!([start_slot, commitment_config])
1937 };
1938 self.send(RpcRequest::GetBlocks, json).await
1939 }
1940
1941 /// Returns a list of finalized blocks starting at the given slot.
1942 ///
1943 /// This method uses the [`Finalized`] [commitment level][cl].
1944 ///
1945 /// [`Finalized`]: CommitmentLevel::Finalized.
1946 /// [cl]: https://solana.com/docs/rpc#configuring-state-commitment
1947 ///
1948 /// # Errors
1949 ///
1950 /// This method returns an error if the limit is greater than 500,000 slots.
1951 ///
1952 /// # RPC Reference
1953 ///
1954 /// This method corresponds directly to the [`getBlocksWithLimit`] RPC
1955 /// method.
1956 ///
1957 /// [`getBlocksWithLimit`]: https://solana.com/docs/rpc/http/getblockswithlimit
1958 ///
1959 /// # Examples
1960 ///
1961 /// ```ignore
1962 /// # use solana_rpc_client_api::client_error::Error;
1963 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
1964 /// # futures::executor::block_on(async {
1965 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
1966 /// // Get the first 10 blocks
1967 /// let start_slot = 0;
1968 /// let limit = 10;
1969 /// let blocks = rpc_client.get_blocks_with_limit(start_slot, limit).await?;
1970 /// # Ok::<(), Error>(())
1971 /// # })?;
1972 /// # Ok::<(), Error>(())
1973 /// ```
1974 pub async fn get_blocks_with_limit(
1975 &self,
1976 start_slot: Slot,
1977 limit: usize,
1978 ) -> ClientResult<Vec<Slot>> {
1979 self.send(RpcRequest::GetBlocksWithLimit, json!([start_slot, limit]))
1980 .await
1981 }
1982
1983 /// Returns a list of confirmed blocks starting at the given slot.
1984 ///
1985 /// # Errors
1986 ///
1987 /// This method returns an error if the limit is greater than 500,000 slots.
1988 ///
1989 /// This method returns an error if the given [commitment level][cl] is below
1990 /// [`Confirmed`].
1991 ///
1992 /// [cl]: https://solana.com/docs/rpc#configuring-state-commitment
1993 /// [`Confirmed`]: CommitmentLevel::Confirmed
1994 ///
1995 /// # RPC Reference
1996 ///
1997 /// This method corresponds directly to the [`getBlocksWithLimit`] RPC
1998 /// method.
1999 ///
2000 /// [`getBlocksWithLimit`]: https://solana.com/docs/rpc/http/getblockswithlimit
2001 ///
2002 /// # Examples
2003 ///
2004 /// ```ignore
2005 /// # use solana_commitment_config::CommitmentConfig;
2006 /// # use solana_rpc_client_api::client_error::Error;
2007 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
2008 /// # futures::executor::block_on(async {
2009 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
2010 /// // Get the first 10 blocks
2011 /// let start_slot = 0;
2012 /// let limit = 10;
2013 /// let commitment_config = CommitmentConfig::confirmed();
2014 /// let blocks = rpc_client.get_blocks_with_limit_and_commitment(
2015 /// start_slot,
2016 /// limit,
2017 /// commitment_config,
2018 /// ).await?;
2019 /// # Ok::<(), Error>(())
2020 /// # })?;
2021 /// # Ok::<(), Error>(())
2022 /// ```
2023 pub async fn get_blocks_with_limit_and_commitment(
2024 &self,
2025 start_slot: Slot,
2026 limit: usize,
2027 commitment_config: CommitmentConfig,
2028 ) -> ClientResult<Vec<Slot>> {
2029 self.send(
2030 RpcRequest::GetBlocksWithLimit,
2031 json!([start_slot, limit, commitment_config]),
2032 )
2033 .await
2034 }
2035
2036 /// Get confirmed signatures for transactions involving an address.
2037 ///
2038 /// Returns up to 1000 signatures, ordered from newest to oldest.
2039 ///
2040 /// This method uses the [`Finalized`] [commitment level][cl].
2041 ///
2042 /// [`Finalized`]: CommitmentLevel::Finalized.
2043 /// [cl]: https://solana.com/docs/rpc#configuring-state-commitment
2044 ///
2045 /// # RPC Reference
2046 ///
2047 /// This method corresponds directly to the [`getSignaturesForAddress`] RPC
2048 /// method.
2049 ///
2050 /// [`getSignaturesForAddress`]: https://solana.com/docs/rpc/http/getsignaturesforaddress
2051 ///
2052 /// # Examples
2053 ///
2054 /// ```ignore
2055 /// # use solana_rpc_client_api::client_error::Error;
2056 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
2057 /// # use solana_keypair::Keypair;
2058 /// # use solana_system_transaction as system_transaction;
2059 /// # use solana_signer::Signer;
2060 /// # futures::executor::block_on(async {
2061 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
2062 /// # let alice = Keypair::new();
2063 /// let signatures = rpc_client.get_signatures_for_address(
2064 /// &alice.pubkey(),
2065 /// ).await?;
2066 /// # Ok::<(), Error>(())
2067 /// # })?;
2068 /// # Ok::<(), Error>(())
2069 /// ```
2070 pub async fn get_signatures_for_address(
2071 &self,
2072 address: &Pubkey,
2073 ) -> ClientResult<Vec<RpcConfirmedTransactionStatusWithSignature>> {
2074 self.get_signatures_for_address_with_config(
2075 address,
2076 GetConfirmedSignaturesForAddress2Config::default(),
2077 )
2078 .await
2079 }
2080
2081 /// Get confirmed signatures for transactions involving an address.
2082 ///
2083 /// # Errors
2084 ///
2085 /// This method returns an error if the given [commitment level][cl] is below
2086 /// [`Confirmed`].
2087 ///
2088 /// [cl]: https://solana.com/docs/rpc#configuring-state-commitment
2089 /// [`Confirmed`]: CommitmentLevel::Confirmed
2090 ///
2091 /// # RPC Reference
2092 ///
2093 /// This method corresponds directly to the [`getSignaturesForAddress`] RPC
2094 /// method.
2095 ///
2096 /// [`getSignaturesForAddress`]: https://solana.com/docs/rpc/http/getsignaturesforaddress
2097 ///
2098 /// # Examples
2099 ///
2100 /// ```ignore
2101 /// # use solana_rpc_client_api::client_error::Error;
2102 /// # use solana_rpc_client::{
2103 /// # nonblocking::rpc_client::RpcClient,
2104 /// # rpc_client::GetConfirmedSignaturesForAddress2Config,
2105 /// # };
2106 /// # use solana_commitment_config::CommitmentConfig;
2107 /// # use solana_keypair::Keypair;
2108 /// # use solana_system_transaction as system_transaction;
2109 /// # use solana_signer::Signer;
2110 /// # futures::executor::block_on(async {
2111 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
2112 /// # let alice = Keypair::new();
2113 /// # let bob = Keypair::new();
2114 /// # let lamports = 50;
2115 /// # let latest_blockhash = rpc_client.get_latest_blockhash().await?;
2116 /// # let tx = system_transaction::transfer(&alice, &bob.pubkey(), lamports, latest_blockhash);
2117 /// # let signature = rpc_client.send_and_confirm_transaction(&tx).await?;
2118 /// let config = GetConfirmedSignaturesForAddress2Config {
2119 /// before: None,
2120 /// until: None,
2121 /// limit: Some(3),
2122 /// commitment: Some(CommitmentConfig::confirmed()),
2123 /// };
2124 /// let signatures = rpc_client.get_signatures_for_address_with_config(
2125 /// &alice.pubkey(),
2126 /// config,
2127 /// ).await?;
2128 /// # Ok::<(), Error>(())
2129 /// # })?;
2130 /// # Ok::<(), Error>(())
2131 /// ```
2132 pub async fn get_signatures_for_address_with_config(
2133 &self,
2134 address: &Pubkey,
2135 config: GetConfirmedSignaturesForAddress2Config,
2136 ) -> ClientResult<Vec<RpcConfirmedTransactionStatusWithSignature>> {
2137 let config = RpcSignaturesForAddressConfig {
2138 before: config.before.map(|signature| signature.to_string()),
2139 until: config.until.map(|signature| signature.to_string()),
2140 limit: config.limit,
2141 commitment: config.commitment,
2142 min_context_slot: None,
2143 };
2144
2145 let result: Vec<RpcConfirmedTransactionStatusWithSignature> = self
2146 .send(
2147 RpcRequest::GetSignaturesForAddress,
2148 json!([address.to_string(), config]),
2149 )
2150 .await?;
2151
2152 Ok(result)
2153 }
2154
2155 /// Returns transaction details for a confirmed transaction.
2156 ///
2157 /// This method uses the [`Finalized`] [commitment level][cl].
2158 ///
2159 /// [`Finalized`]: CommitmentLevel::Finalized
2160 /// [cl]: https://solana.com/docs/rpc#configuring-state-commitment
2161 ///
2162 /// # RPC Reference
2163 ///
2164 /// This method corresponds directly to the [`getTransaction`] RPC method.
2165 ///
2166 /// [`getTransaction`]: https://solana.com/docs/rpc/http/gettransaction
2167 ///
2168 /// # Examples
2169 ///
2170 /// ```ignore
2171 /// # use solana_rpc_client_api::client_error::Error;
2172 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
2173 /// # use solana_keypair::Keypair;
2174 /// # use solana_system_transaction as system_transaction;
2175 /// # use solana_signature::Signature;
2176 /// # use solana_signer::Signer;
2177 /// # use solana_transaction_status_client_types::UiTransactionEncoding;
2178 /// # futures::executor::block_on(async {
2179 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
2180 /// # let alice = Keypair::new();
2181 /// # let bob = Keypair::new();
2182 /// # let lamports = 50;
2183 /// # let latest_blockhash = rpc_client.get_latest_blockhash().await?;
2184 /// # let tx = system_transaction::transfer(&alice, &bob.pubkey(), lamports, latest_blockhash);
2185 /// let signature = rpc_client.send_and_confirm_transaction(&tx).await?;
2186 /// let transaction = rpc_client.get_transaction(
2187 /// &signature,
2188 /// UiTransactionEncoding::Json,
2189 /// ).await?;
2190 /// # Ok::<(), Error>(())
2191 /// # })?;
2192 /// # Ok::<(), Error>(())
2193 /// ```
2194 pub async fn get_transaction(
2195 &self,
2196 signature: &Signature,
2197 encoding: UiTransactionEncoding,
2198 ) -> ClientResult<EncodedConfirmedTransactionWithStatusMeta> {
2199 self.send(
2200 RpcRequest::GetTransaction,
2201 json!([signature.to_string(), encoding]),
2202 )
2203 .await
2204 }
2205
2206 /// Returns transaction details for a confirmed transaction.
2207 ///
2208 /// # Errors
2209 ///
2210 /// This method returns an error if the given [commitment level][cl] is below
2211 /// [`Confirmed`].
2212 ///
2213 /// [cl]: https://solana.com/docs/rpc#configuring-state-commitment
2214 /// [`Confirmed`]: CommitmentLevel::Confirmed
2215 ///
2216 /// # RPC Reference
2217 ///
2218 /// This method corresponds directly to the [`getTransaction`] RPC method.
2219 ///
2220 /// [`getTransaction`]: https://solana.com/docs/rpc/http/gettransaction
2221 ///
2222 /// # Examples
2223 ///
2224 /// ```ignore
2225 /// # use solana_rpc_client_api::{
2226 /// # client_error::Error,
2227 /// # config::RpcTransactionConfig,
2228 /// # };
2229 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
2230 /// # use solana_commitment_config::CommitmentConfig;
2231 /// # use solana_keypair::Keypair;
2232 /// # use solana_system_transaction as system_transaction;
2233 /// # use solana_signature::Signature;
2234 /// # use solana_signer::Signer;
2235 /// # use solana_transaction_status_client_types::UiTransactionEncoding;
2236 /// # futures::executor::block_on(async {
2237 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
2238 /// # let alice = Keypair::new();
2239 /// # let bob = Keypair::new();
2240 /// # let lamports = 50;
2241 /// # let latest_blockhash = rpc_client.get_latest_blockhash().await?;
2242 /// # let tx = system_transaction::transfer(&alice, &bob.pubkey(), lamports, latest_blockhash);
2243 /// let signature = rpc_client.send_and_confirm_transaction(&tx).await?;
2244 /// let config = RpcTransactionConfig {
2245 /// encoding: Some(UiTransactionEncoding::Json),
2246 /// commitment: Some(CommitmentConfig::confirmed()),
2247 /// max_supported_transaction_version: Some(0),
2248 /// };
2249 /// let transaction = rpc_client.get_transaction_with_config(
2250 /// &signature,
2251 /// config,
2252 /// ).await?;
2253 /// # Ok::<(), Error>(())
2254 /// # })?;
2255 /// # Ok::<(), Error>(())
2256 /// ```
2257 pub async fn get_transaction_with_config(
2258 &self,
2259 signature: &Signature,
2260 config: RpcTransactionConfig,
2261 ) -> ClientResult<EncodedConfirmedTransactionWithStatusMeta> {
2262 self.send(
2263 RpcRequest::GetTransaction,
2264 json!([signature.to_string(), config]),
2265 )
2266 .await
2267 }
2268
2269 /// Returns the estimated production time of a block.
2270 ///
2271 /// # RPC Reference
2272 ///
2273 /// This method corresponds directly to the [`getBlockTime`] RPC method.
2274 ///
2275 /// [`getBlockTime`]: https://solana.com/docs/rpc/http/getblocktime
2276 ///
2277 /// # Examples
2278 ///
2279 /// ```ignore
2280 /// # use solana_rpc_client_api::client_error::Error;
2281 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
2282 /// # futures::executor::block_on(async {
2283 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
2284 /// // Get the time of the most recent finalized block
2285 /// let slot = rpc_client.get_slot().await?;
2286 /// let block_time = rpc_client.get_block_time(slot).await?;
2287 /// # Ok::<(), Error>(())
2288 /// # })?;
2289 /// # Ok::<(), Error>(())
2290 /// ```
2291 pub async fn get_block_time(&self, slot: Slot) -> ClientResult<UnixTimestamp> {
2292 let request = RpcRequest::GetBlockTime;
2293 let response = self.send(request, json!([slot])).await;
2294
2295 response
2296 .map(|result_json: Value| {
2297 if result_json.is_null() {
2298 return Err(RpcError::ForUser(format!("Block Not Found: slot={slot}")).into());
2299 }
2300 let result = serde_json::from_value(result_json)
2301 .map_err(|err| ClientError::new_with_request(err.into(), request))?;
2302 trace!("Response block timestamp {slot:?} {result:?}");
2303 Ok(result)
2304 })
2305 .map_err(|err| err.into_with_request(request))?
2306 }
2307
2308 /// Returns information about the current epoch.
2309 ///
2310 /// This method uses the configured default [commitment level][cl].
2311 ///
2312 /// [cl]: https://solana.com/docs/rpc#configuring-state-commitment
2313 ///
2314 /// # RPC Reference
2315 ///
2316 /// This method corresponds directly to the [`getEpochInfo`] RPC method.
2317 ///
2318 /// [`getEpochInfo`]: https://solana.com/docs/rpc/http/getepochinfo
2319 ///
2320 /// # Examples
2321 ///
2322 /// ```ignore
2323 /// # use solana_rpc_client_api::client_error::Error;
2324 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
2325 /// # futures::executor::block_on(async {
2326 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
2327 /// let epoch_info = rpc_client.get_epoch_info().await?;
2328 /// # Ok::<(), Error>(())
2329 /// # })?;
2330 /// # Ok::<(), Error>(())
2331 /// ```
2332 pub async fn get_epoch_info(&self) -> ClientResult<EpochInfo> {
2333 self.get_epoch_info_with_commitment(self.commitment()).await
2334 }
2335
2336 /// Returns information about the current epoch.
2337 ///
2338 /// # RPC Reference
2339 ///
2340 /// This method corresponds directly to the [`getEpochInfo`] RPC method.
2341 ///
2342 /// [`getEpochInfo`]: https://solana.com/docs/rpc/http/getepochinfo
2343 ///
2344 /// # Examples
2345 ///
2346 /// ```ignore
2347 /// # use solana_rpc_client_api::client_error::Error;
2348 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
2349 /// # use solana_commitment_config::CommitmentConfig;
2350 /// # futures::executor::block_on(async {
2351 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
2352 /// let commitment_config = CommitmentConfig::confirmed();
2353 /// let epoch_info = rpc_client.get_epoch_info_with_commitment(
2354 /// commitment_config,
2355 /// ).await?;
2356 /// # Ok::<(), Error>(())
2357 /// # })?;
2358 /// # Ok::<(), Error>(())
2359 /// ```
2360 pub async fn get_epoch_info_with_commitment(
2361 &self,
2362 commitment_config: CommitmentConfig,
2363 ) -> ClientResult<EpochInfo> {
2364 self.send(RpcRequest::GetEpochInfo, json!([commitment_config]))
2365 .await
2366 }
2367
2368 /// Returns the leader schedule for an epoch.
2369 ///
2370 /// This method uses the configured default [commitment level][cl].
2371 ///
2372 /// [cl]: https://solana.com/docs/rpc#configuring-state-commitment
2373 ///
2374 /// # RPC Reference
2375 ///
2376 /// This method corresponds directly to the [`getLeaderSchedule`] RPC method.
2377 ///
2378 /// [`getLeaderSchedule`]: https://solana.com/docs/rpc/http/getleaderschedule
2379 ///
2380 /// # Examples
2381 ///
2382 /// ```ignore
2383 /// # use solana_rpc_client_api::client_error::Error;
2384 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
2385 /// # use solana_commitment_config::CommitmentConfig;
2386 /// # futures::executor::block_on(async {
2387 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
2388 /// # let slot = rpc_client.get_slot().await?;
2389 /// let leader_schedule = rpc_client.get_leader_schedule(
2390 /// Some(slot),
2391 /// ).await?;
2392 /// # Ok::<(), Error>(())
2393 /// # })?;
2394 /// # Ok::<(), Error>(())
2395 /// ```
2396 pub async fn get_leader_schedule(
2397 &self,
2398 slot: Option<Slot>,
2399 ) -> ClientResult<Option<RpcLeaderSchedule>> {
2400 self.get_leader_schedule_with_commitment(slot, self.commitment())
2401 .await
2402 }
2403
2404 /// Returns the leader schedule for an epoch.
2405 ///
2406 /// # RPC Reference
2407 ///
2408 /// This method corresponds directly to the [`getLeaderSchedule`] RPC method.
2409 ///
2410 /// [`getLeaderSchedule`]: https://solana.com/docs/rpc/http/getleaderschedule
2411 ///
2412 /// # Examples
2413 ///
2414 /// ```ignore
2415 /// # use solana_rpc_client_api::client_error::Error;
2416 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
2417 /// # use solana_commitment_config::CommitmentConfig;
2418 /// # futures::executor::block_on(async {
2419 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
2420 /// # let slot = rpc_client.get_slot().await?;
2421 /// let commitment_config = CommitmentConfig::processed();
2422 /// let leader_schedule = rpc_client.get_leader_schedule_with_commitment(
2423 /// Some(slot),
2424 /// commitment_config,
2425 /// ).await?;
2426 /// # Ok::<(), Error>(())
2427 /// # })?;
2428 /// # Ok::<(), Error>(())
2429 /// ```
2430 pub async fn get_leader_schedule_with_commitment(
2431 &self,
2432 slot: Option<Slot>,
2433 commitment_config: CommitmentConfig,
2434 ) -> ClientResult<Option<RpcLeaderSchedule>> {
2435 self.get_leader_schedule_with_config(
2436 slot,
2437 RpcLeaderScheduleConfig {
2438 commitment: Some(commitment_config),
2439 ..RpcLeaderScheduleConfig::default()
2440 },
2441 )
2442 .await
2443 }
2444
2445 /// Returns the leader schedule for an epoch.
2446 ///
2447 /// # RPC Reference
2448 ///
2449 /// This method corresponds directly to the [`getLeaderSchedule`] RPC method.
2450 ///
2451 /// [`getLeaderSchedule`]: https://solana.com/docs/rpc/http/getleaderschedule
2452 ///
2453 /// # Examples
2454 ///
2455 /// ```ignore
2456 /// # use solana_rpc_client_api::{
2457 /// # client_error::Error,
2458 /// # config::RpcLeaderScheduleConfig,
2459 /// # };
2460 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
2461 /// # use solana_commitment_config::CommitmentConfig;
2462 /// # futures::executor::block_on(async {
2463 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
2464 /// # let slot = rpc_client.get_slot().await?;
2465 /// # let validator_pubkey_str = "7AYmEYBBetok8h5L3Eo3vi3bDWnjNnaFbSXfSNYV5ewB".to_string();
2466 /// let config = RpcLeaderScheduleConfig {
2467 /// identity: Some(validator_pubkey_str),
2468 /// commitment: Some(CommitmentConfig::processed()),
2469 /// };
2470 /// let leader_schedule = rpc_client.get_leader_schedule_with_config(
2471 /// Some(slot),
2472 /// config,
2473 /// ).await?;
2474 /// # Ok::<(), Error>(())
2475 /// # })?;
2476 /// # Ok::<(), Error>(())
2477 /// ```
2478 pub async fn get_leader_schedule_with_config(
2479 &self,
2480 slot: Option<Slot>,
2481 config: RpcLeaderScheduleConfig,
2482 ) -> ClientResult<Option<RpcLeaderSchedule>> {
2483 self.send(RpcRequest::GetLeaderSchedule, json!([slot, config]))
2484 .await
2485 }
2486
2487 /// Returns epoch schedule information from this cluster's genesis config.
2488 ///
2489 /// # RPC Reference
2490 ///
2491 /// This method corresponds directly to the [`getEpochSchedule`] RPC method.
2492 ///
2493 /// [`getEpochSchedule`]: https://solana.com/docs/rpc/http/getepochschedule
2494 ///
2495 /// # Examples
2496 ///
2497 /// ```ignore
2498 /// # use solana_rpc_client_api::client_error::Error;
2499 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
2500 /// # futures::executor::block_on(async {
2501 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
2502 /// let epoch_schedule = rpc_client.get_epoch_schedule().await?;
2503 /// # Ok::<(), Error>(())
2504 /// # })?;
2505 /// # Ok::<(), Error>(())
2506 /// ```
2507 pub async fn get_epoch_schedule(&self) -> ClientResult<EpochSchedule> {
2508 self.send(RpcRequest::GetEpochSchedule, Value::Null).await
2509 }
2510
2511 /// Returns a list of recent performance samples, in reverse slot order.
2512 ///
2513 /// Performance samples are taken every 60 seconds and include the number of
2514 /// transactions and slots that occur in a given time window.
2515 ///
2516 /// # RPC Reference
2517 ///
2518 /// This method corresponds directly to the [`getRecentPerformanceSamples`] RPC method.
2519 ///
2520 /// [`getRecentPerformanceSamples`]: https://solana.com/docs/rpc/http/getrecentperformancesamples
2521 ///
2522 /// # Examples
2523 ///
2524 /// ```ignore
2525 /// # use solana_rpc_client_api::client_error::Error;
2526 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
2527 /// # futures::executor::block_on(async {
2528 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
2529 /// let limit = 10;
2530 /// let performance_samples = rpc_client.get_recent_performance_samples(
2531 /// Some(limit),
2532 /// ).await?;
2533 /// # Ok::<(), Error>(())
2534 /// # })?;
2535 /// # Ok::<(), Error>(())
2536 /// ```
2537 pub async fn get_recent_performance_samples(
2538 &self,
2539 limit: Option<usize>,
2540 ) -> ClientResult<Vec<RpcPerfSample>> {
2541 self.send(RpcRequest::GetRecentPerformanceSamples, json!([limit]))
2542 .await
2543 }
2544
2545 /// Returns a list of minimum prioritization fees from recent blocks.
2546 /// Takes an optional vector of addresses; if any addresses are provided, the response will
2547 /// reflect the minimum prioritization fee to land a transaction locking all of the provided
2548 /// accounts as writable.
2549 ///
2550 /// Currently, a node's prioritization-fee cache stores data from up to 150 blocks.
2551 ///
2552 /// # RPC Reference
2553 ///
2554 /// This method corresponds directly to the [`getRecentPrioritizationFees`] RPC method.
2555 ///
2556 /// [`getRecentPrioritizationFees`]: https://solana.com/docs/rpc/http/getrecentprioritizationfees
2557 ///
2558 /// # Examples
2559 ///
2560 /// ```ignore
2561 /// # use solana_rpc_client_api::client_error::Error;
2562 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
2563 /// # use solana_keypair::Keypair;
2564 /// # use solana_signer::Signer;
2565 /// # futures::executor::block_on(async {
2566 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
2567 /// # let alice = Keypair::new();
2568 /// # let bob = Keypair::new();
2569 /// let addresses = vec![alice.pubkey(), bob.pubkey()];
2570 /// let prioritization_fees = rpc_client.get_recent_prioritization_fees(
2571 /// &addresses,
2572 /// ).await?;
2573 /// # Ok::<(), Error>(())
2574 /// # })?;
2575 /// # Ok::<(), Error>(())
2576 /// ```
2577 pub async fn get_recent_prioritization_fees(
2578 &self,
2579 addresses: &[Pubkey],
2580 ) -> ClientResult<Vec<RpcPrioritizationFee>> {
2581 let addresses: Vec<_> = addresses
2582 .iter()
2583 .map(|address| address.to_string())
2584 .collect();
2585 self.send(RpcRequest::GetRecentPrioritizationFees, json!([addresses]))
2586 .await
2587 }
2588
2589 /// Returns the identity pubkey for the current node.
2590 ///
2591 /// # RPC Reference
2592 ///
2593 /// This method corresponds directly to the [`getIdentity`] RPC method.
2594 ///
2595 /// [`getIdentity`]: https://solana.com/docs/rpc/http/getidentity
2596 ///
2597 /// # Examples
2598 ///
2599 /// ```ignore
2600 /// # use solana_rpc_client_api::client_error::Error;
2601 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
2602 /// # futures::executor::block_on(async {
2603 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
2604 /// let identity = rpc_client.get_identity().await?;
2605 /// # Ok::<(), Error>(())
2606 /// # })?;
2607 /// # Ok::<(), Error>(())
2608 /// ```
2609 pub async fn get_identity(&self) -> ClientResult<Pubkey> {
2610 let rpc_identity: RpcIdentity = self.send(RpcRequest::GetIdentity, Value::Null).await?;
2611
2612 rpc_identity.identity.parse::<Pubkey>().map_err(|_| {
2613 ClientError::new_with_request(
2614 RpcError::ParseError("Pubkey".to_string()).into(),
2615 RpcRequest::GetIdentity,
2616 )
2617 })
2618 }
2619
2620 /// Returns the current inflation governor.
2621 ///
2622 /// This method uses the [`Finalized`] [commitment level][cl].
2623 ///
2624 /// [`Finalized`]: CommitmentLevel::Finalized
2625 /// [cl]: https://solana.com/docs/rpc#configuring-state-commitment
2626 ///
2627 /// # RPC Reference
2628 ///
2629 /// This method corresponds directly to the [`getInflationGovernor`] RPC
2630 /// method.
2631 ///
2632 /// [`getInflationGovernor`]: https://solana.com/docs/rpc/http/getinflationgovernor
2633 ///
2634 /// # Examples
2635 ///
2636 /// ```ignore
2637 /// # use solana_rpc_client_api::client_error::Error;
2638 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
2639 /// # futures::executor::block_on(async {
2640 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
2641 /// let inflation_governor = rpc_client.get_inflation_governor().await?;
2642 /// # Ok::<(), Error>(())
2643 /// # })?;
2644 /// # Ok::<(), Error>(())
2645 /// ```
2646 pub async fn get_inflation_governor(&self) -> ClientResult<RpcInflationGovernor> {
2647 self.send(RpcRequest::GetInflationGovernor, Value::Null)
2648 .await
2649 }
2650
2651 /// Returns the specific inflation values for the current epoch.
2652 ///
2653 /// # RPC Reference
2654 ///
2655 /// This method corresponds directly to the [`getInflationRate`] RPC method.
2656 ///
2657 /// [`getInflationRate`]: https://solana.com/docs/rpc/http/getinflationrate
2658 ///
2659 /// # Examples
2660 ///
2661 /// ```ignore
2662 /// # use solana_rpc_client_api::client_error::Error;
2663 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
2664 /// # futures::executor::block_on(async {
2665 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
2666 /// let inflation_rate = rpc_client.get_inflation_rate().await?;
2667 /// # Ok::<(), Error>(())
2668 /// # })?;
2669 /// # Ok::<(), Error>(())
2670 /// ```
2671 pub async fn get_inflation_rate(&self) -> ClientResult<RpcInflationRate> {
2672 self.send(RpcRequest::GetInflationRate, Value::Null).await
2673 }
2674
2675 /// Returns the inflation reward for a list of addresses for an epoch.
2676 ///
2677 /// This method uses the configured [commitment level][cl].
2678 ///
2679 /// [cl]: https://solana.com/docs/rpc#configuring-state-commitment
2680 ///
2681 /// # RPC Reference
2682 ///
2683 /// This method corresponds directly to the [`getInflationReward`] RPC method.
2684 ///
2685 /// [`getInflationReward`]: https://solana.com/docs/rpc/http/getinflationreward
2686 ///
2687 /// # Examples
2688 ///
2689 /// ```ignore
2690 /// # use solana_rpc_client_api::client_error::Error;
2691 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
2692 /// # use solana_keypair::Keypair;
2693 /// # use solana_signer::Signer;
2694 /// # futures::executor::block_on(async {
2695 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
2696 /// # let epoch_info = rpc_client.get_epoch_info().await?;
2697 /// # let epoch = epoch_info.epoch;
2698 /// # let alice = Keypair::new();
2699 /// # let bob = Keypair::new();
2700 /// let addresses = vec![alice.pubkey(), bob.pubkey()];
2701 /// let inflation_reward = rpc_client.get_inflation_reward(
2702 /// &addresses,
2703 /// Some(epoch),
2704 /// ).await?;
2705 /// # Ok::<(), Error>(())
2706 /// # })?;
2707 /// # Ok::<(), Error>(())
2708 /// ```
2709 pub async fn get_inflation_reward(
2710 &self,
2711 addresses: &[Pubkey],
2712 epoch: Option<Epoch>,
2713 ) -> ClientResult<Vec<Option<RpcInflationReward>>> {
2714 let addresses: Vec<_> = addresses
2715 .iter()
2716 .map(|address| address.to_string())
2717 .collect();
2718 self.send(
2719 RpcRequest::GetInflationReward,
2720 json!([
2721 addresses,
2722 RpcEpochConfig {
2723 epoch,
2724 commitment: Some(self.commitment()),
2725 min_context_slot: None,
2726 }
2727 ]),
2728 )
2729 .await
2730 }
2731
2732 /// Returns the current solana version running on the node.
2733 ///
2734 /// # RPC Reference
2735 ///
2736 /// This method corresponds directly to the [`getVersion`] RPC method.
2737 ///
2738 /// [`getVersion`]: https://solana.com/docs/rpc/http/getversion
2739 ///
2740 /// # Examples
2741 ///
2742 /// ```ignore
2743 /// # use solana_rpc_client_api::client_error::Error;
2744 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
2745 /// # use solana_keypair::Keypair;
2746 /// # use solana_signer::Signer;
2747 /// # futures::executor::block_on(async {
2748 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
2749 /// let expected_version = semver::Version::new(1, 7, 0);
2750 /// let version = rpc_client.get_version().await?;
2751 /// let version = semver::Version::parse(&version.solana_core)?;
2752 /// assert!(version >= expected_version);
2753 /// # Ok::<(), Box<dyn std::error::Error>>(())
2754 /// # })?;
2755 /// # Ok::<(), Box<dyn std::error::Error>>(())
2756 /// ```
2757 pub async fn get_version(&self) -> ClientResult<RpcVersionInfo> {
2758 self.send(RpcRequest::GetVersion, Value::Null).await
2759 }
2760
2761 /// Returns the lowest slot that the node has information about in its ledger.
2762 ///
2763 /// This value may increase over time if the node is configured to purge
2764 /// older ledger data.
2765 ///
2766 /// # RPC Reference
2767 ///
2768 /// This method corresponds directly to the [`minimumLedgerSlot`] RPC
2769 /// method.
2770 ///
2771 /// [`minimumLedgerSlot`]: https://solana.com/docs/rpc/http/minimumledgerslot
2772 ///
2773 /// # Examples
2774 ///
2775 /// ```ignore
2776 /// # use solana_rpc_client_api::client_error::Error;
2777 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
2778 /// # futures::executor::block_on(async {
2779 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
2780 /// let slot = rpc_client.minimum_ledger_slot().await?;
2781 /// # Ok::<(), Error>(())
2782 /// # })?;
2783 /// # Ok::<(), Error>(())
2784 /// ```
2785 pub async fn minimum_ledger_slot(&self) -> ClientResult<Slot> {
2786 self.send(RpcRequest::MinimumLedgerSlot, Value::Null).await
2787 }
2788
2789 /// Returns all information associated with the account of the provided pubkey.
2790 ///
2791 /// This method uses the configured [commitment level][cl].
2792 ///
2793 /// [cl]: https://solana.com/docs/rpc#configuring-state-commitment
2794 ///
2795 /// To get multiple accounts at once, use the [`get_multiple_accounts`] method.
2796 ///
2797 /// [`get_multiple_accounts`]: RpcClient::get_multiple_accounts
2798 ///
2799 /// # Errors
2800 ///
2801 /// If the account does not exist, this method returns
2802 /// [`RpcError::ForUser`]. This is unlike [`get_account_with_commitment`],
2803 /// which returns `Ok(None)` if the account does not exist.
2804 ///
2805 /// [`get_account_with_commitment`]: RpcClient::get_account_with_commitment
2806 ///
2807 /// # RPC Reference
2808 ///
2809 /// This method is built on the [`getAccountInfo`] RPC method.
2810 ///
2811 /// [`getAccountInfo`]: https://solana.com/docs/rpc/http/getaccountinfo
2812 ///
2813 /// # Examples
2814 ///
2815 /// ```ignore
2816 /// # use solana_rpc_client_api::client_error::Error;
2817 /// # use solana_rpc_client::nonblocking::rpc_client::{self, RpcClient};
2818 /// # use solana_keypair::Keypair;
2819 /// # use solana_pubkey::Pubkey;
2820 /// # use solana_signer::Signer;
2821 /// # use std::str::FromStr;
2822 /// # futures::executor::block_on(async {
2823 /// # let mocks = rpc_client::create_rpc_client_mocks();
2824 /// # let rpc_client = RpcClient::new_mock_with_mocks("succeeds".to_string(), mocks);
2825 /// let alice_pubkey = Pubkey::from_str("BgvYtJEfmZYdVKiptmMjxGzv8iQoo4MWjsP3QsTkhhxa").unwrap();
2826 /// let account = rpc_client.get_account(&alice_pubkey).await?;
2827 /// # Ok::<(), Error>(())
2828 /// # })?;
2829 /// # Ok::<(), Error>(())
2830 /// ```
2831 pub async fn get_account(&self, pubkey: &Pubkey) -> ClientResult<Account> {
2832 self.get_account_with_commitment(pubkey, self.commitment())
2833 .await?
2834 .value
2835 .ok_or_else(|| RpcError::ForUser(format!("AccountNotFound: pubkey={pubkey}")).into())
2836 }
2837
2838 /// Returns all information associated with the account of the provided pubkey.
2839 ///
2840 /// If the account does not exist, this method returns `Ok(None)`.
2841 ///
2842 /// To get multiple accounts at once, use the [`get_multiple_accounts_with_commitment`] method.
2843 ///
2844 /// [`get_multiple_accounts_with_commitment`]: RpcClient::get_multiple_accounts_with_commitment
2845 ///
2846 /// # RPC Reference
2847 ///
2848 /// This method is built on the [`getAccountInfo`] RPC method.
2849 ///
2850 /// [`getAccountInfo`]: https://solana.com/docs/rpc/http/getaccountinfo
2851 ///
2852 /// # Examples
2853 ///
2854 /// ```ignore
2855 /// # use solana_rpc_client_api::client_error::Error;
2856 /// # use solana_rpc_client::nonblocking::rpc_client::{self, RpcClient};
2857 /// # use solana_commitment_config::CommitmentConfig;
2858 /// # use solana_keypair::Keypair;
2859 /// # use solana_signer::Signer;
2860 /// # use solana_pubkey::Pubkey;
2861 /// # use std::str::FromStr;
2862 /// # futures::executor::block_on(async {
2863 /// # let mocks = rpc_client::create_rpc_client_mocks();
2864 /// # let rpc_client = RpcClient::new_mock_with_mocks("succeeds".to_string(), mocks);
2865 /// let alice_pubkey = Pubkey::from_str("BgvYtJEfmZYdVKiptmMjxGzv8iQoo4MWjsP3QsTkhhxa").unwrap();
2866 /// let commitment_config = CommitmentConfig::processed();
2867 /// let account = rpc_client.get_account_with_commitment(
2868 /// &alice_pubkey,
2869 /// commitment_config,
2870 /// ).await?;
2871 /// assert!(account.value.is_some());
2872 /// # Ok::<(), Error>(())
2873 /// # })?;
2874 /// # Ok::<(), Error>(())
2875 /// ```
2876 pub async fn get_account_with_commitment(
2877 &self,
2878 pubkey: &Pubkey,
2879 commitment_config: CommitmentConfig,
2880 ) -> RpcResult<Option<Account>> {
2881 let config = RpcAccountInfoConfig {
2882 encoding: Some(UiAccountEncoding::Base64Zstd),
2883 commitment: Some(commitment_config),
2884 data_slice: None,
2885 min_context_slot: None,
2886 };
2887
2888 self.get_ui_account_with_config(pubkey, config)
2889 .await
2890 .map(|response| Response {
2891 context: response.context,
2892 value: response.value.map(|ui_account| {
2893 ui_account.decode().expect(
2894 "It should be impossible at this point for the account data not to be \
2895 decodable. Ensure that the account was fetched using a binary encoding.",
2896 )
2897 }),
2898 })
2899 }
2900
2901 #[deprecated(
2902 note = "Use `get_ui_account_with_config()` instead. This function will be removed in a \
2903 future version of `solana_rpc_client`."
2904 )]
2905 pub async fn get_account_with_config(
2906 &self,
2907 pubkey: &Pubkey,
2908 config: RpcAccountInfoConfig,
2909 ) -> RpcResult<Option<Account>> {
2910 #[allow(deprecated)]
2911 let response = self
2912 .send(
2913 RpcRequest::GetAccountInfo,
2914 json!([pubkey.to_string(), config]),
2915 )
2916 .await;
2917
2918 response
2919 .map(|result_json: Value| {
2920 if result_json.is_null() {
2921 return Err(
2922 RpcError::ForUser(format!("AccountNotFound: pubkey={pubkey}")).into(),
2923 );
2924 }
2925 let Response {
2926 context,
2927 value: rpc_account,
2928 } = serde_json::from_value::<Response<Option<UiAccount>>>(result_json)?;
2929 trace!("Response account {pubkey:?} {rpc_account:?}");
2930 let account = rpc_account.and_then(|rpc_account| rpc_account.decode());
2931
2932 Ok(Response {
2933 context,
2934 value: account,
2935 })
2936 })
2937 .map_err(|err| {
2938 Into::<ClientError>::into(RpcError::ForUser(format!(
2939 "AccountNotFound: pubkey={pubkey}: {err}"
2940 )))
2941 })?
2942 }
2943
2944 /// Returns all information associated with the account of the provided pubkey.
2945 ///
2946 /// If the account does not exist, this method returns `Ok(None)`.
2947 ///
2948 /// To get multiple accounts at once, use the [`get_multiple_ui_accounts_with_config`] method.
2949 ///
2950 /// [`get_multiple_ui_accounts_with_config`]: RpcClient::get_multiple_ui_accounts_with_config
2951 ///
2952 /// # RPC Reference
2953 ///
2954 /// This method is built on the [`getAccountInfo`] RPC method.
2955 ///
2956 /// [`getAccountInfo`]: https://solana.com/docs/rpc/http/getaccountinfo
2957 ///
2958 /// # Examples
2959 ///
2960 /// ```ignore
2961 /// # use solana_rpc_client_api::{
2962 /// # config::RpcAccountInfoConfig,
2963 /// # client_error::Error,
2964 /// # };
2965 /// # use solana_rpc_client::nonblocking::rpc_client::{self, RpcClient};
2966 /// # use solana_commitment_config::CommitmentConfig;
2967 /// # use solana_keypair::Keypair;
2968 /// # use solana_signer::Signer;
2969 /// # use solana_pubkey::Pubkey;
2970 /// # use solana_account_decoder_client_types::UiAccountEncoding;
2971 /// # use std::str::FromStr;
2972 /// # futures::executor::block_on(async {
2973 /// # let mocks = rpc_client::create_rpc_client_mocks();
2974 /// # let rpc_client = RpcClient::new_mock_with_mocks("succeeds".to_string(), mocks);
2975 /// let alice_pubkey = Pubkey::from_str("BgvYtJEfmZYdVKiptmMjxGzv8iQoo4MWjsP3QsTkhhxa").unwrap();
2976 /// let commitment_config = CommitmentConfig::processed();
2977 /// let config = RpcAccountInfoConfig {
2978 /// encoding: Some(UiAccountEncoding::Base64),
2979 /// commitment: Some(commitment_config),
2980 /// .. RpcAccountInfoConfig::default()
2981 /// };
2982 /// let ui_account = rpc_client.get_ui_account_with_config(
2983 /// &alice_pubkey,
2984 /// config,
2985 /// ).await?;
2986 /// assert!(ui_account.value.is_some());
2987 /// # Ok::<(), Error>(())
2988 /// # })?;
2989 /// # Ok::<(), Error>(())
2990 /// ```
2991 pub async fn get_ui_account_with_config(
2992 &self,
2993 pubkey: &Pubkey,
2994 config: RpcAccountInfoConfig,
2995 ) -> RpcResult<Option<UiAccount>> {
2996 let response = self
2997 .send(
2998 RpcRequest::GetAccountInfo,
2999 json!([pubkey.to_string(), config]),
3000 )
3001 .await;
3002
3003 response
3004 .map(|result_json: Value| {
3005 if result_json.is_null() {
3006 return Err(
3007 RpcError::ForUser(format!("AccountNotFound: pubkey={pubkey}")).into(),
3008 );
3009 }
3010 let Response {
3011 context,
3012 value: ui_account,
3013 } = serde_json::from_value::<Response<Option<UiAccount>>>(result_json)?;
3014 trace!("Response account {pubkey:?} {ui_account:?}");
3015 Ok(Response {
3016 context,
3017 value: ui_account,
3018 })
3019 })
3020 .map_err(|err| {
3021 Into::<ClientError>::into(RpcError::ForUser(format!(
3022 "AccountNotFound: pubkey={pubkey}: {err}"
3023 )))
3024 })?
3025 }
3026
3027 /// Get the max slot seen from retransmit stage.
3028 ///
3029 /// # RPC Reference
3030 ///
3031 /// This method corresponds directly to the [`getMaxRetransmitSlot`] RPC
3032 /// method.
3033 ///
3034 /// [`getMaxRetransmitSlot`]: https://solana.com/docs/rpc/http/getmaxretransmitslot
3035 ///
3036 /// # Examples
3037 ///
3038 /// ```ignore
3039 /// # use solana_rpc_client_api::client_error::Error;
3040 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
3041 /// # futures::executor::block_on(async {
3042 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
3043 /// let slot = rpc_client.get_max_retransmit_slot().await?;
3044 /// # Ok::<(), Error>(())
3045 /// # })?;
3046 /// # Ok::<(), Error>(())
3047 pub async fn get_max_retransmit_slot(&self) -> ClientResult<Slot> {
3048 self.send(RpcRequest::GetMaxRetransmitSlot, Value::Null)
3049 .await
3050 }
3051
3052 /// Get the max slot seen from after [shred](https://solana.com/docs/terminology#shred) insert.
3053 ///
3054 /// # RPC Reference
3055 ///
3056 /// This method corresponds directly to the
3057 /// [`getMaxShredInsertSlot`] RPC method.
3058 ///
3059 /// [`getMaxShredInsertSlot`]: https://solana.com/docs/rpc/http/getmaxshredinsertslot
3060 ///
3061 /// # Examples
3062 ///
3063 /// ```ignore
3064 /// # use solana_rpc_client_api::client_error::Error;
3065 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
3066 /// # futures::executor::block_on(async {
3067 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
3068 /// let slot = rpc_client.get_max_shred_insert_slot().await?;
3069 /// # Ok::<(), Error>(())
3070 /// # })?;
3071 /// # Ok::<(), Error>(())
3072 pub async fn get_max_shred_insert_slot(&self) -> ClientResult<Slot> {
3073 self.send(RpcRequest::GetMaxShredInsertSlot, Value::Null)
3074 .await
3075 }
3076
3077 /// Returns the account information for a list of pubkeys.
3078 ///
3079 /// This method uses the configured [commitment level][cl].
3080 ///
3081 /// [cl]: https://solana.com/docs/rpc#configuring-state-commitment
3082 ///
3083 /// # RPC Reference
3084 ///
3085 /// This method is built on the [`getMultipleAccounts`] RPC method.
3086 ///
3087 /// [`getMultipleAccounts`]: https://solana.com/docs/rpc/http/getmultipleaccounts
3088 ///
3089 /// # Examples
3090 ///
3091 /// ```ignore
3092 /// # use solana_rpc_client_api::client_error::Error;
3093 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
3094 /// # use solana_keypair::Keypair;
3095 /// # use solana_signer::Signer;
3096 /// # futures::executor::block_on(async {
3097 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
3098 /// # let alice = Keypair::new();
3099 /// # let bob = Keypair::new();
3100 /// let pubkeys = vec![alice.pubkey(), bob.pubkey()];
3101 /// let accounts = rpc_client.get_multiple_accounts(&pubkeys).await?;
3102 /// # Ok::<(), Error>(())
3103 /// # })?;
3104 /// # Ok::<(), Error>(())
3105 /// ```
3106 pub async fn get_multiple_accounts(
3107 &self,
3108 pubkeys: &[Pubkey],
3109 ) -> ClientResult<Vec<Option<Account>>> {
3110 Ok(self
3111 .get_multiple_accounts_with_commitment(pubkeys, self.commitment())
3112 .await?
3113 .value)
3114 }
3115
3116 /// Returns the account information for a list of pubkeys.
3117 ///
3118 /// # RPC Reference
3119 ///
3120 /// This method is built on the [`getMultipleAccounts`] RPC method.
3121 ///
3122 /// [`getMultipleAccounts`]: https://solana.com/docs/rpc/http/getmultipleaccounts
3123 ///
3124 /// # Examples
3125 ///
3126 /// ```ignore
3127 /// # use solana_rpc_client_api::client_error::Error;
3128 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
3129 /// # use solana_commitment_config::CommitmentConfig;
3130 /// # use solana_keypair::Keypair;
3131 /// # use solana_signer::Signer;
3132 /// # futures::executor::block_on(async {
3133 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
3134 /// # let alice = Keypair::new();
3135 /// # let bob = Keypair::new();
3136 /// let pubkeys = vec![alice.pubkey(), bob.pubkey()];
3137 /// let commitment_config = CommitmentConfig::processed();
3138 /// let accounts = rpc_client.get_multiple_accounts_with_commitment(
3139 /// &pubkeys,
3140 /// commitment_config,
3141 /// ).await?;
3142 /// # Ok::<(), Error>(())
3143 /// # })?;
3144 /// # Ok::<(), Error>(())
3145 /// ```
3146 pub async fn get_multiple_accounts_with_commitment(
3147 &self,
3148 pubkeys: &[Pubkey],
3149 commitment_config: CommitmentConfig,
3150 ) -> RpcResult<Vec<Option<Account>>> {
3151 self.get_multiple_ui_accounts_with_config(
3152 pubkeys,
3153 RpcAccountInfoConfig {
3154 encoding: Some(UiAccountEncoding::Base64Zstd),
3155 commitment: Some(commitment_config),
3156 data_slice: None,
3157 min_context_slot: None,
3158 },
3159 )
3160 .await
3161 .map(|response| Response {
3162 context: response.context,
3163 value: response
3164 .value
3165 .into_iter()
3166 .map(|ui_account| {
3167 ui_account.map(|ui_account| {
3168 ui_account.decode().expect(
3169 "It should be impossible at this point for the account data not to be \
3170 decodable. Ensure that the account was fetched using a binary \
3171 encoding.",
3172 )
3173 })
3174 })
3175 .collect(),
3176 })
3177 }
3178
3179 #[deprecated(
3180 note = "Use `get_multiple_ui_accounts_with_config()` instead. This function will be \
3181 removed in a future version of `solana_rpc_client`."
3182 )]
3183 pub async fn get_multiple_accounts_with_config(
3184 &self,
3185 pubkeys: &[Pubkey],
3186 config: RpcAccountInfoConfig,
3187 ) -> RpcResult<Vec<Option<Account>>> {
3188 #[allow(deprecated)]
3189 {
3190 let config = RpcAccountInfoConfig {
3191 commitment: config.commitment.or_else(|| Some(self.commitment())),
3192 ..config
3193 };
3194 let pubkeys: Vec<_> = pubkeys.iter().map(|pubkey| pubkey.to_string()).collect();
3195 let response = self
3196 .send(RpcRequest::GetMultipleAccounts, json!([pubkeys, config]))
3197 .await?;
3198 let Response {
3199 context,
3200 value: accounts,
3201 } = serde_json::from_value::<Response<Vec<Option<UiAccount>>>>(response)?;
3202 let accounts: Vec<Option<Account>> = accounts
3203 .into_iter()
3204 .map(|rpc_account| rpc_account.and_then(|a| a.decode()))
3205 .collect();
3206 Ok(Response {
3207 context,
3208 value: accounts,
3209 })
3210 }
3211 }
3212
3213 /// Returns the account information for a list of pubkeys.
3214 ///
3215 /// # RPC Reference
3216 ///
3217 /// This method is built on the [`getMultipleAccounts`] RPC method.
3218 ///
3219 /// [`getMultipleAccounts`]: https://solana.com/docs/rpc/http/getmultipleaccounts
3220 ///
3221 /// # Examples
3222 ///
3223 /// ```ignore
3224 /// # use solana_rpc_client_api::{
3225 /// # config::RpcAccountInfoConfig,
3226 /// # client_error::Error,
3227 /// # };
3228 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
3229 /// # use solana_commitment_config::CommitmentConfig;
3230 /// # use solana_keypair::Keypair;
3231 /// # use solana_signer::Signer;
3232 /// # use solana_account_decoder_client_types::UiAccountEncoding;
3233 /// # futures::executor::block_on(async {
3234 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
3235 /// # let alice = Keypair::new();
3236 /// # let bob = Keypair::new();
3237 /// let pubkeys = vec![alice.pubkey(), bob.pubkey()];
3238 /// let commitment_config = CommitmentConfig::processed();
3239 /// let config = RpcAccountInfoConfig {
3240 /// encoding: Some(UiAccountEncoding::Base64),
3241 /// commitment: Some(commitment_config),
3242 /// .. RpcAccountInfoConfig::default()
3243 /// };
3244 /// let ui_accounts = rpc_client.get_multiple_ui_accounts_with_config(
3245 /// &pubkeys,
3246 /// config,
3247 /// ).await?;
3248 /// # Ok::<(), Error>(())
3249 /// # })?;
3250 /// # Ok::<(), Error>(())
3251 /// ```
3252 pub async fn get_multiple_ui_accounts_with_config(
3253 &self,
3254 pubkeys: &[Pubkey],
3255 config: RpcAccountInfoConfig,
3256 ) -> RpcResult<Vec<Option<UiAccount>>> {
3257 let config = RpcAccountInfoConfig {
3258 commitment: config.commitment.or_else(|| Some(self.commitment())),
3259 ..config
3260 };
3261 let pubkeys: Vec<_> = pubkeys.iter().map(|pubkey| pubkey.to_string()).collect();
3262 let response = self
3263 .send(RpcRequest::GetMultipleAccounts, json!([pubkeys, config]))
3264 .await?;
3265 let Response {
3266 context,
3267 value: ui_accounts,
3268 } = serde_json::from_value::<Response<Vec<Option<UiAccount>>>>(response)?;
3269 Ok(Response {
3270 context,
3271 value: ui_accounts,
3272 })
3273 }
3274
3275 /// Gets the raw data associated with an account.
3276 ///
3277 /// This is equivalent to calling [`get_account`] and then accessing the
3278 /// [`data`] field of the returned [`Account`].
3279 ///
3280 /// [`get_account`]: RpcClient::get_account
3281 /// [`data`]: Account::data
3282 ///
3283 /// # RPC Reference
3284 ///
3285 /// This method is built on the [`getAccountInfo`] RPC method.
3286 ///
3287 /// [`getAccountInfo`]: https://solana.com/docs/rpc/http/getaccountinfo
3288 ///
3289 /// # Examples
3290 ///
3291 /// ```ignore
3292 /// # use solana_rpc_client_api::client_error::Error;
3293 /// # use solana_rpc_client::nonblocking::rpc_client::{self, RpcClient};
3294 /// # use solana_keypair::Keypair;
3295 /// # use solana_pubkey::Pubkey;
3296 /// # use solana_signer::Signer;
3297 /// # use std::str::FromStr;
3298 /// # futures::executor::block_on(async {
3299 /// # let mocks = rpc_client::create_rpc_client_mocks();
3300 /// # let rpc_client = RpcClient::new_mock_with_mocks("succeeds".to_string(), mocks);
3301 /// let alice_pubkey = Pubkey::from_str("BgvYtJEfmZYdVKiptmMjxGzv8iQoo4MWjsP3QsTkhhxa").unwrap();
3302 /// let account_data = rpc_client.get_account_data(&alice_pubkey).await?;
3303 /// # Ok::<(), Error>(())
3304 /// # })?;
3305 /// # Ok::<(), Error>(())
3306 /// ```
3307 pub async fn get_account_data(&self, pubkey: &Pubkey) -> ClientResult<Vec<u8>> {
3308 Ok(self.get_account(pubkey).await?.data)
3309 }
3310
3311 /// Returns minimum balance required to make an account with specified data length rent exempt.
3312 ///
3313 /// # RPC Reference
3314 ///
3315 /// This method corresponds directly to the
3316 /// [`getMinimumBalanceForRentExemption`] RPC method.
3317 ///
3318 /// [`getMinimumBalanceForRentExemption`]: https://solana.com/docs/rpc/http/getminimumbalanceforrentexemption
3319 ///
3320 /// # Examples
3321 ///
3322 /// ```ignore
3323 /// # use solana_rpc_client_api::client_error::Error;
3324 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
3325 /// # futures::executor::block_on(async {
3326 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
3327 /// let data_len = 300;
3328 /// let balance = rpc_client.get_minimum_balance_for_rent_exemption(data_len).await?;
3329 /// # Ok::<(), Error>(())
3330 /// # })?;
3331 /// # Ok::<(), Error>(())
3332 /// ```
3333 pub async fn get_minimum_balance_for_rent_exemption(
3334 &self,
3335 data_len: usize,
3336 ) -> ClientResult<u64> {
3337 let request = RpcRequest::GetMinimumBalanceForRentExemption;
3338 let minimum_balance_json: Value = self
3339 .send(request, json!([data_len]))
3340 .await
3341 .map_err(|err| err.into_with_request(request))?;
3342
3343 let minimum_balance: u64 = serde_json::from_value(minimum_balance_json)
3344 .map_err(|err| ClientError::new_with_request(err.into(), request))?;
3345 trace!("Response minimum balance {data_len:?} {minimum_balance:?}");
3346 Ok(minimum_balance)
3347 }
3348
3349 /// Request the balance of the provided account pubkey.
3350 ///
3351 /// This method uses the configured [commitment level][cl].
3352 ///
3353 /// [cl]: https://solana.com/docs/rpc#configuring-state-commitment
3354 ///
3355 /// # RPC Reference
3356 ///
3357 /// This method corresponds directly to the [`getBalance`] RPC method.
3358 ///
3359 /// [`getBalance`]: https://solana.com/docs/rpc/http/getbalance
3360 ///
3361 /// # Examples
3362 ///
3363 /// ```ignore
3364 /// # use solana_rpc_client_api::client_error::Error;
3365 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
3366 /// # use solana_keypair::Keypair;
3367 /// # use solana_signer::Signer;
3368 /// # futures::executor::block_on(async {
3369 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
3370 /// # let alice = Keypair::new();
3371 /// let balance = rpc_client.get_balance(&alice.pubkey()).await?;
3372 /// # Ok::<(), Error>(())
3373 /// # })?;
3374 /// # Ok::<(), Error>(())
3375 /// ```
3376 pub async fn get_balance(&self, pubkey: &Pubkey) -> ClientResult<u64> {
3377 Ok(self
3378 .get_balance_with_commitment(pubkey, self.commitment())
3379 .await?
3380 .value)
3381 }
3382
3383 /// Request the balance of the provided account pubkey.
3384 ///
3385 /// # RPC Reference
3386 ///
3387 /// This method corresponds directly to the [`getBalance`] RPC method.
3388 ///
3389 /// [`getBalance`]: https://solana.com/docs/rpc/http/getbalance
3390 ///
3391 /// # Examples
3392 ///
3393 /// ```ignore
3394 /// # use solana_rpc_client_api::client_error::Error;
3395 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
3396 /// # use solana_commitment_config::CommitmentConfig;
3397 /// # use solana_keypair::Keypair;
3398 /// # use solana_signer::Signer;
3399 /// # futures::executor::block_on(async {
3400 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
3401 /// # let alice = Keypair::new();
3402 /// let commitment_config = CommitmentConfig::processed();
3403 /// let balance = rpc_client.get_balance_with_commitment(
3404 /// &alice.pubkey(),
3405 /// commitment_config,
3406 /// ).await?;
3407 /// # Ok::<(), Error>(())
3408 /// # })?;
3409 /// # Ok::<(), Error>(())
3410 /// ```
3411 pub async fn get_balance_with_commitment(
3412 &self,
3413 pubkey: &Pubkey,
3414 commitment_config: CommitmentConfig,
3415 ) -> RpcResult<u64> {
3416 self.send(
3417 RpcRequest::GetBalance,
3418 json!([pubkey.to_string(), commitment_config]),
3419 )
3420 .await
3421 }
3422
3423 /// Returns all accounts owned by the provided program pubkey.
3424 ///
3425 /// This method uses the configured [commitment level][cl].
3426 ///
3427 /// [cl]: https://solana.com/docs/rpc#configuring-state-commitment
3428 ///
3429 /// # RPC Reference
3430 ///
3431 /// This method corresponds directly to the [`getProgramAccounts`] RPC
3432 /// method.
3433 ///
3434 /// [`getProgramAccounts`]: https://solana.com/docs/rpc/http/getprogramaccounts
3435 ///
3436 /// # Examples
3437 ///
3438 /// ```ignore
3439 /// # use solana_rpc_client_api::client_error::Error;
3440 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
3441 /// # use solana_keypair::Keypair;
3442 /// # use solana_signer::Signer;
3443 /// # futures::executor::block_on(async {
3444 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
3445 /// # let alice = Keypair::new();
3446 /// let accounts = rpc_client.get_program_accounts(&alice.pubkey()).await?;
3447 /// # Ok::<(), Error>(())
3448 /// # })?;
3449 /// # Ok::<(), Error>(())
3450 /// ```
3451 pub async fn get_program_accounts(
3452 &self,
3453 pubkey: &Pubkey,
3454 ) -> ClientResult<Vec<(Pubkey, Account)>> {
3455 self.get_program_ui_accounts_with_config(
3456 pubkey,
3457 RpcProgramAccountsConfig {
3458 account_config: RpcAccountInfoConfig {
3459 encoding: Some(UiAccountEncoding::Base64Zstd),
3460 ..RpcAccountInfoConfig::default()
3461 },
3462 ..RpcProgramAccountsConfig::default()
3463 },
3464 )
3465 .await
3466 .map(|response| {
3467 response
3468 .into_iter()
3469 .map(|(pubkey, ui_account)| {
3470 (
3471 pubkey,
3472 ui_account.decode().expect(
3473 "It should be impossible at this point for the account data not to be \
3474 decodable. Ensure that the account was fetched using a binary \
3475 encoding.",
3476 ),
3477 )
3478 })
3479 .collect()
3480 })
3481 }
3482
3483 #[deprecated(
3484 note = "Use `get_program_ui_accounts_with_config()` instead. This function will be \
3485 removed in a future version of `solana_rpc_client`."
3486 )]
3487 pub async fn get_program_accounts_with_config(
3488 &self,
3489 pubkey: &Pubkey,
3490 mut config: RpcProgramAccountsConfig,
3491 ) -> ClientResult<Vec<(Pubkey, Account)>> {
3492 #[allow(deprecated)]
3493 {
3494 let commitment = config
3495 .account_config
3496 .commitment
3497 .unwrap_or_else(|| self.commitment());
3498 config.account_config.commitment = Some(commitment);
3499
3500 let accounts = self
3501 .send::<OptionalContext<Vec<RpcKeyedAccount>>>(
3502 RpcRequest::GetProgramAccounts,
3503 json!([pubkey.to_string(), config]),
3504 )
3505 .await?
3506 .parse_value();
3507 parse_keyed_accounts(accounts, RpcRequest::GetProgramAccounts)
3508 }
3509 }
3510
3511 /// Returns all accounts owned by the provided program pubkey.
3512 ///
3513 /// # RPC Reference
3514 ///
3515 /// This method is built on the [`getProgramAccounts`] RPC method.
3516 ///
3517 /// [`getProgramAccounts`]: https://solana.com/docs/rpc/http/getprogramaccounts
3518 ///
3519 /// # Examples
3520 ///
3521 /// ```ignore
3522 /// # use solana_rpc_client_api::{
3523 /// # client_error::Error,
3524 /// # config::{RpcAccountInfoConfig, RpcProgramAccountsConfig},
3525 /// # filter::{MemcmpEncodedBytes, RpcFilterType, Memcmp},
3526 /// # };
3527 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
3528 /// # use solana_commitment_config::CommitmentConfig;
3529 /// # use solana_keypair::Keypair;
3530 /// # use solana_signer::Signer;
3531 /// # use solana_account_decoder_client_types::{UiDataSliceConfig, UiAccountEncoding};
3532 /// # futures::executor::block_on(async {
3533 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
3534 /// # let alice = Keypair::new();
3535 /// # let base64_bytes = "\
3536 /// # AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
3537 /// # AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
3538 /// # AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
3539 /// let memcmp = RpcFilterType::Memcmp(Memcmp::new(
3540 /// 0, // offset
3541 /// MemcmpEncodedBytes::Base64(base64_bytes.to_string()), // encoded bytes
3542 /// ));
3543 /// let config = RpcProgramAccountsConfig {
3544 /// filters: Some(vec![
3545 /// RpcFilterType::DataSize(128),
3546 /// memcmp,
3547 /// ]),
3548 /// account_config: RpcAccountInfoConfig {
3549 /// encoding: Some(UiAccountEncoding::Base64),
3550 /// data_slice: Some(UiDataSliceConfig {
3551 /// offset: 0,
3552 /// length: 5,
3553 /// }),
3554 /// commitment: Some(CommitmentConfig::processed()),
3555 /// min_context_slot: Some(1234),
3556 /// },
3557 /// with_context: Some(false),
3558 /// sort_results: Some(true),
3559 /// };
3560 /// let ui_accounts = rpc_client.get_program_ui_accounts_with_config(
3561 /// &alice.pubkey(),
3562 /// config,
3563 /// ).await?;
3564 /// # Ok::<(), Error>(())
3565 /// # })?;
3566 /// # Ok::<(), Error>(())
3567 /// ```
3568 pub async fn get_program_ui_accounts_with_config(
3569 &self,
3570 pubkey: &Pubkey,
3571 mut config: RpcProgramAccountsConfig,
3572 ) -> ClientResult<Vec<(Pubkey, UiAccount)>> {
3573 let commitment = config
3574 .account_config
3575 .commitment
3576 .unwrap_or_else(|| self.commitment());
3577 config.account_config.commitment = Some(commitment);
3578
3579 let accounts = self
3580 .send::<OptionalContext<Vec<RpcKeyedAccount>>>(
3581 RpcRequest::GetProgramAccounts,
3582 json!([pubkey.to_string(), config]),
3583 )
3584 .await?
3585 .parse_value();
3586 pubkey_ui_account_client_result_from_keyed_accounts(
3587 accounts,
3588 RpcRequest::GetProgramAccounts,
3589 )
3590 }
3591
3592 /// Returns the stake minimum delegation, in lamports.
3593 ///
3594 /// # RPC Reference
3595 ///
3596 /// This method corresponds directly to the [`getStakeMinimumDelegation`] RPC method.
3597 ///
3598 /// [`getStakeMinimumDelegation`]: https://solana.com/docs/rpc/http/getstakeminimumdelegation
3599 ///
3600 /// # Examples
3601 ///
3602 /// ```ignore
3603 /// # use solana_rpc_client_api::client_error::Error;
3604 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
3605 /// # futures::executor::block_on(async {
3606 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
3607 /// let stake_minimum_delegation = rpc_client.get_stake_minimum_delegation().await?;
3608 /// # Ok::<(), Error>(())
3609 /// # })?;
3610 /// # Ok::<(), Error>(())
3611 /// ```
3612 pub async fn get_stake_minimum_delegation(&self) -> ClientResult<u64> {
3613 self.get_stake_minimum_delegation_with_commitment(self.commitment())
3614 .await
3615 }
3616
3617 /// Returns the stake minimum delegation, in lamports, based on the commitment level.
3618 ///
3619 /// # RPC Reference
3620 ///
3621 /// This method corresponds directly to the [`getStakeMinimumDelegation`] RPC method.
3622 ///
3623 /// [`getStakeMinimumDelegation`]: https://solana.com/docs/rpc/http/getstakeminimumdelegation
3624 ///
3625 /// # Examples
3626 ///
3627 /// ```ignore
3628 /// # use solana_rpc_client_api::client_error::Error;
3629 /// # use solana_rpc_client::nonblocking::rpc_client::RpcClient;
3630 /// # use solana_commitment_config::CommitmentConfig;
3631 /// # futures::executor::block_on(async {
3632 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
3633 /// let stake_minimum_delegation = rpc_client.get_stake_minimum_delegation_with_commitment(CommitmentConfig::confirmed()).await?;
3634 /// # Ok::<(), Error>(())
3635 /// # })?;
3636 /// # Ok::<(), Error>(())
3637 /// ```
3638 pub async fn get_stake_minimum_delegation_with_commitment(
3639 &self,
3640 commitment_config: CommitmentConfig,
3641 ) -> ClientResult<u64> {
3642 Ok(self
3643 .send::<Response<u64>>(
3644 RpcRequest::GetStakeMinimumDelegation,
3645 json!([commitment_config]),
3646 )
3647 .await?
3648 .value)
3649 }
3650
3651 /// Request the transaction count.
3652 pub async fn get_transaction_count(&self) -> ClientResult<u64> {
3653 self.get_transaction_count_with_commitment(self.commitment())
3654 .await
3655 }
3656
3657 pub async fn get_transaction_count_with_commitment(
3658 &self,
3659 commitment_config: CommitmentConfig,
3660 ) -> ClientResult<u64> {
3661 self.send(RpcRequest::GetTransactionCount, json!([commitment_config]))
3662 .await
3663 }
3664
3665 pub async fn get_first_available_block(&self) -> ClientResult<Slot> {
3666 self.send(RpcRequest::GetFirstAvailableBlock, Value::Null)
3667 .await
3668 }
3669
3670 pub async fn get_genesis_hash(&self) -> ClientResult<Hash> {
3671 let hash_str: String = self.send(RpcRequest::GetGenesisHash, Value::Null).await?;
3672 let hash = hash_str.parse().map_err(|_| {
3673 ClientError::new_with_request(
3674 RpcError::ParseError("Hash".to_string()).into(),
3675 RpcRequest::GetGenesisHash,
3676 )
3677 })?;
3678 Ok(hash)
3679 }
3680
3681 pub async fn get_health(&self) -> ClientResult<()> {
3682 self.send::<String>(RpcRequest::GetHealth, Value::Null)
3683 .await
3684 .map(|_| ())
3685 }
3686
3687 pub async fn get_token_account(&self, pubkey: &Pubkey) -> ClientResult<Option<UiTokenAccount>> {
3688 Ok(self
3689 .get_token_account_with_commitment(pubkey, self.commitment())
3690 .await?
3691 .value)
3692 }
3693
3694 pub async fn get_token_account_with_commitment(
3695 &self,
3696 pubkey: &Pubkey,
3697 commitment_config: CommitmentConfig,
3698 ) -> RpcResult<Option<UiTokenAccount>> {
3699 let config = RpcAccountInfoConfig {
3700 encoding: Some(UiAccountEncoding::JsonParsed),
3701 commitment: Some(commitment_config),
3702 data_slice: None,
3703 min_context_slot: None,
3704 };
3705 let response = self
3706 .send(
3707 RpcRequest::GetAccountInfo,
3708 json!([pubkey.to_string(), config]),
3709 )
3710 .await;
3711
3712 response
3713 .map(|result_json: Value| {
3714 if result_json.is_null() {
3715 return Err(
3716 RpcError::ForUser(format!("AccountNotFound: pubkey={pubkey}")).into(),
3717 );
3718 }
3719 let Response {
3720 context,
3721 value: rpc_account,
3722 } = serde_json::from_value::<Response<Option<UiAccount>>>(result_json)?;
3723 trace!("Response account {pubkey:?} {rpc_account:?}");
3724 let response = {
3725 if let Some(rpc_account) = rpc_account {
3726 if let UiAccountData::Json(account_data) = rpc_account.data {
3727 let token_account_type: TokenAccountType =
3728 serde_json::from_value(account_data.parsed)?;
3729 if let TokenAccountType::Account(token_account) = token_account_type {
3730 return Ok(Response {
3731 context,
3732 value: Some(token_account),
3733 });
3734 }
3735 }
3736 }
3737 Err(Into::<ClientError>::into(RpcError::ForUser(format!(
3738 "Account could not be parsed as token account: pubkey={pubkey}"
3739 ))))
3740 };
3741 response?
3742 })
3743 .map_err(|err| {
3744 Into::<ClientError>::into(RpcError::ForUser(format!(
3745 "AccountNotFound: pubkey={pubkey}: {err}"
3746 )))
3747 })?
3748 }
3749
3750 pub async fn get_token_account_balance(&self, pubkey: &Pubkey) -> ClientResult<UiTokenAmount> {
3751 Ok(self
3752 .get_token_account_balance_with_commitment(pubkey, self.commitment())
3753 .await?
3754 .value)
3755 }
3756
3757 pub async fn get_token_account_balance_with_commitment(
3758 &self,
3759 pubkey: &Pubkey,
3760 commitment_config: CommitmentConfig,
3761 ) -> RpcResult<UiTokenAmount> {
3762 self.send(
3763 RpcRequest::GetTokenAccountBalance,
3764 json!([pubkey.to_string(), commitment_config]),
3765 )
3766 .await
3767 }
3768
3769 pub async fn get_token_accounts_by_delegate(
3770 &self,
3771 delegate: &Pubkey,
3772 token_account_filter: TokenAccountsFilter,
3773 ) -> ClientResult<Vec<RpcKeyedAccount>> {
3774 Ok(self
3775 .get_token_accounts_by_delegate_with_commitment(
3776 delegate,
3777 token_account_filter,
3778 self.commitment(),
3779 )
3780 .await?
3781 .value)
3782 }
3783
3784 pub async fn get_token_accounts_by_delegate_with_commitment(
3785 &self,
3786 delegate: &Pubkey,
3787 token_account_filter: TokenAccountsFilter,
3788 commitment_config: CommitmentConfig,
3789 ) -> RpcResult<Vec<RpcKeyedAccount>> {
3790 let token_account_filter = match token_account_filter {
3791 TokenAccountsFilter::Mint(mint) => RpcTokenAccountsFilter::Mint(mint.to_string()),
3792 TokenAccountsFilter::ProgramId(program_id) => {
3793 RpcTokenAccountsFilter::ProgramId(program_id.to_string())
3794 }
3795 };
3796
3797 let config = RpcAccountInfoConfig {
3798 encoding: Some(UiAccountEncoding::JsonParsed),
3799 commitment: Some(commitment_config),
3800 data_slice: None,
3801 min_context_slot: None,
3802 };
3803
3804 self.send(
3805 RpcRequest::GetTokenAccountsByOwner,
3806 json!([delegate.to_string(), token_account_filter, config]),
3807 )
3808 .await
3809 }
3810
3811 pub async fn get_token_accounts_by_owner(
3812 &self,
3813 owner: &Pubkey,
3814 token_account_filter: TokenAccountsFilter,
3815 ) -> ClientResult<Vec<RpcKeyedAccount>> {
3816 Ok(self
3817 .get_token_accounts_by_owner_with_commitment(
3818 owner,
3819 token_account_filter,
3820 self.commitment(),
3821 )
3822 .await?
3823 .value)
3824 }
3825
3826 pub async fn get_token_accounts_by_owner_with_commitment(
3827 &self,
3828 owner: &Pubkey,
3829 token_account_filter: TokenAccountsFilter,
3830 commitment_config: CommitmentConfig,
3831 ) -> RpcResult<Vec<RpcKeyedAccount>> {
3832 let token_account_filter = match token_account_filter {
3833 TokenAccountsFilter::Mint(mint) => RpcTokenAccountsFilter::Mint(mint.to_string()),
3834 TokenAccountsFilter::ProgramId(program_id) => {
3835 RpcTokenAccountsFilter::ProgramId(program_id.to_string())
3836 }
3837 };
3838
3839 let config = RpcAccountInfoConfig {
3840 encoding: Some(UiAccountEncoding::JsonParsed),
3841 commitment: Some(commitment_config),
3842 data_slice: None,
3843 min_context_slot: None,
3844 };
3845
3846 self.send(
3847 RpcRequest::GetTokenAccountsByOwner,
3848 json!([owner.to_string(), token_account_filter, config]),
3849 )
3850 .await
3851 }
3852
3853 pub async fn get_token_largest_accounts(
3854 &self,
3855 mint: &Pubkey,
3856 ) -> ClientResult<Vec<RpcTokenAccountBalance>> {
3857 Ok(self
3858 .get_token_largest_accounts_with_commitment(mint, self.commitment())
3859 .await?
3860 .value)
3861 }
3862
3863 pub async fn get_token_largest_accounts_with_commitment(
3864 &self,
3865 mint: &Pubkey,
3866 commitment_config: CommitmentConfig,
3867 ) -> RpcResult<Vec<RpcTokenAccountBalance>> {
3868 self.send(
3869 RpcRequest::GetTokenLargestAccounts,
3870 json!([mint.to_string(), commitment_config]),
3871 )
3872 .await
3873 }
3874
3875 pub async fn get_token_supply(&self, mint: &Pubkey) -> ClientResult<UiTokenAmount> {
3876 Ok(self
3877 .get_token_supply_with_commitment(mint, self.commitment())
3878 .await?
3879 .value)
3880 }
3881
3882 pub async fn get_token_supply_with_commitment(
3883 &self,
3884 mint: &Pubkey,
3885 commitment_config: CommitmentConfig,
3886 ) -> RpcResult<UiTokenAmount> {
3887 self.send(
3888 RpcRequest::GetTokenSupply,
3889 json!([mint.to_string(), commitment_config]),
3890 )
3891 .await
3892 }
3893
3894 pub async fn request_airdrop(&self, pubkey: &Pubkey, lamports: u64) -> ClientResult<Signature> {
3895 self.request_airdrop_with_config(
3896 pubkey,
3897 lamports,
3898 RpcRequestAirdropConfig {
3899 commitment: Some(self.commitment()),
3900 ..RpcRequestAirdropConfig::default()
3901 },
3902 )
3903 .await
3904 }
3905
3906 pub async fn request_airdrop_with_blockhash(
3907 &self,
3908 pubkey: &Pubkey,
3909 lamports: u64,
3910 recent_blockhash: &Hash,
3911 ) -> ClientResult<Signature> {
3912 self.request_airdrop_with_config(
3913 pubkey,
3914 lamports,
3915 RpcRequestAirdropConfig {
3916 commitment: Some(self.commitment()),
3917 recent_blockhash: Some(recent_blockhash.to_string()),
3918 },
3919 )
3920 .await
3921 }
3922
3923 pub async fn request_airdrop_with_config(
3924 &self,
3925 pubkey: &Pubkey,
3926 lamports: u64,
3927 config: RpcRequestAirdropConfig,
3928 ) -> ClientResult<Signature> {
3929 let commitment = config.commitment.unwrap_or_default();
3930 let config = RpcRequestAirdropConfig {
3931 commitment: Some(commitment),
3932 ..config
3933 };
3934 self.send(
3935 RpcRequest::RequestAirdrop,
3936 json!([pubkey.to_string(), lamports, config]),
3937 )
3938 .await
3939 .and_then(|signature: String| {
3940 Signature::from_str(&signature).map_err(|err| {
3941 ClientErrorKind::Custom(format!("signature deserialization failed: {err}")).into()
3942 })
3943 })
3944 .map_err(|_| {
3945 RpcError::ForUser(
3946 "airdrop request failed. This can happen when the rate limit is reached."
3947 .to_string(),
3948 )
3949 .into()
3950 })
3951 }
3952
3953 pub(crate) async fn poll_balance_with_timeout_and_commitment(
3954 &self,
3955 pubkey: &Pubkey,
3956 polling_frequency: &Duration,
3957 timeout: &Duration,
3958 commitment_config: CommitmentConfig,
3959 ) -> ClientResult<u64> {
3960 let now = Instant::now();
3961 loop {
3962 match self
3963 .get_balance_with_commitment(pubkey, commitment_config)
3964 .await
3965 {
3966 Ok(bal) => {
3967 return Ok(bal.value);
3968 }
3969 Err(e) => {
3970 sleep(*polling_frequency).await;
3971 if now.elapsed() > *timeout {
3972 return Err(e);
3973 }
3974 }
3975 };
3976 }
3977 }
3978
3979 pub async fn poll_get_balance_with_commitment(
3980 &self,
3981 pubkey: &Pubkey,
3982 commitment_config: CommitmentConfig,
3983 ) -> ClientResult<u64> {
3984 self.poll_balance_with_timeout_and_commitment(
3985 pubkey,
3986 &Duration::from_millis(100),
3987 &Duration::from_secs(1),
3988 commitment_config,
3989 )
3990 .await
3991 }
3992
3993 pub async fn wait_for_balance_with_commitment(
3994 &self,
3995 pubkey: &Pubkey,
3996 expected_balance: Option<u64>,
3997 commitment_config: CommitmentConfig,
3998 ) -> ClientResult<u64> {
3999 const LAST: usize = 30;
4000 let mut run = 0;
4001 loop {
4002 let balance_result = self
4003 .poll_get_balance_with_commitment(pubkey, commitment_config)
4004 .await;
4005 if expected_balance.is_none() || (balance_result.is_err() && run == LAST) {
4006 return balance_result;
4007 }
4008 trace!(
4009 "wait_for_balance_with_commitment [{run}] {balance_result:?} {expected_balance:?}"
4010 );
4011 if let (Some(expected_balance), Ok(balance_result)) = (expected_balance, balance_result)
4012 {
4013 if expected_balance == balance_result {
4014 return Ok(balance_result);
4015 }
4016 }
4017 run += 1;
4018 }
4019 }
4020
4021 /// Poll the server to confirm a transaction.
4022 pub async fn poll_for_signature(&self, signature: &Signature) -> ClientResult<()> {
4023 self.poll_for_signature_with_commitment(signature, self.commitment())
4024 .await
4025 }
4026
4027 /// Poll the server to confirm a transaction.
4028 pub async fn poll_for_signature_with_commitment(
4029 &self,
4030 signature: &Signature,
4031 commitment_config: CommitmentConfig,
4032 ) -> ClientResult<()> {
4033 let now = Instant::now();
4034 loop {
4035 if let Ok(Some(_)) = self
4036 .get_signature_status_with_commitment(signature, commitment_config)
4037 .await
4038 {
4039 break;
4040 }
4041 if now.elapsed().as_secs() > 15 {
4042 return Err(RpcError::ForUser(format!(
4043 "signature not found after {} seconds",
4044 now.elapsed().as_secs()
4045 ))
4046 .into());
4047 }
4048 sleep(Duration::from_millis(250)).await;
4049 }
4050 Ok(())
4051 }
4052
4053 /// Poll the server to confirm a transaction.
4054 pub async fn poll_for_signature_confirmation(
4055 &self,
4056 signature: &Signature,
4057 min_confirmed_blocks: usize,
4058 ) -> ClientResult<usize> {
4059 let mut now = Instant::now();
4060 let mut confirmed_blocks = 0;
4061 loop {
4062 let response = self
4063 .get_num_blocks_since_signature_confirmation(signature)
4064 .await;
4065 match response {
4066 Ok(count) => {
4067 if confirmed_blocks != count {
4068 info!(
4069 "signature {} confirmed {} out of {} after {} ms",
4070 signature,
4071 count,
4072 min_confirmed_blocks,
4073 now.elapsed().as_millis()
4074 );
4075 now = Instant::now();
4076 confirmed_blocks = count;
4077 }
4078 if count >= min_confirmed_blocks {
4079 break;
4080 }
4081 }
4082 Err(err) => {
4083 debug!("check_confirmations request failed: {err:?}");
4084 }
4085 };
4086 if now.elapsed().as_secs() > 20 {
4087 info!(
4088 "signature {} confirmed {} out of {} failed after {} ms",
4089 signature,
4090 confirmed_blocks,
4091 min_confirmed_blocks,
4092 now.elapsed().as_millis()
4093 );
4094 if confirmed_blocks > 0 {
4095 return Ok(confirmed_blocks);
4096 } else {
4097 return Err(RpcError::ForUser(format!(
4098 "signature not found after {} seconds",
4099 now.elapsed().as_secs()
4100 ))
4101 .into());
4102 }
4103 }
4104 sleep(Duration::from_millis(250)).await;
4105 }
4106 Ok(confirmed_blocks)
4107 }
4108
4109 pub async fn get_num_blocks_since_signature_confirmation(
4110 &self,
4111 signature: &Signature,
4112 ) -> ClientResult<usize> {
4113 let result: Response<Vec<Option<TransactionStatus>>> = self
4114 .send(
4115 RpcRequest::GetSignatureStatuses,
4116 json!([[signature.to_string()]]),
4117 )
4118 .await?;
4119
4120 let confirmations = result.value[0]
4121 .clone()
4122 .ok_or_else(|| {
4123 ClientError::new_with_request(
4124 ClientErrorKind::Custom("signature not found".to_string()),
4125 RpcRequest::GetSignatureStatuses,
4126 )
4127 })?
4128 .confirmations
4129 .unwrap_or(MAX_LOCKOUT_HISTORY + 1);
4130 Ok(confirmations)
4131 }
4132
4133 pub async fn get_latest_blockhash(&self) -> ClientResult<Hash> {
4134 let (blockhash, _) = self
4135 .get_latest_blockhash_with_commitment(self.commitment())
4136 .await?;
4137 Ok(blockhash)
4138 }
4139
4140 pub async fn get_latest_blockhash_with_commitment(
4141 &self,
4142 commitment: CommitmentConfig,
4143 ) -> ClientResult<(Hash, u64)> {
4144 let RpcBlockhash {
4145 blockhash,
4146 last_valid_block_height,
4147 } = self
4148 .send::<Response<RpcBlockhash>>(RpcRequest::GetLatestBlockhash, json!([commitment]))
4149 .await?
4150 .value;
4151 let blockhash = blockhash.parse().map_err(|_| {
4152 ClientError::new_with_request(
4153 RpcError::ParseError("Hash".to_string()).into(),
4154 RpcRequest::GetLatestBlockhash,
4155 )
4156 })?;
4157 Ok((blockhash, last_valid_block_height))
4158 }
4159
4160 pub async fn is_blockhash_valid(
4161 &self,
4162 blockhash: &Hash,
4163 commitment: CommitmentConfig,
4164 ) -> ClientResult<bool> {
4165 Ok(self
4166 .send::<Response<bool>>(
4167 RpcRequest::IsBlockhashValid,
4168 json!([blockhash.to_string(), commitment,]),
4169 )
4170 .await?
4171 .value)
4172 }
4173
4174 pub async fn get_fee_for_message(
4175 &self,
4176 message: &impl SerializableMessage,
4177 ) -> ClientResult<u64> {
4178 let serialized = message.serialize();
4179 let serialized_encoded = BASE64_STANDARD.encode(serialized);
4180 let result = self
4181 .send::<Response<Option<u64>>>(
4182 RpcRequest::GetFeeForMessage,
4183 json!([serialized_encoded, self.commitment()]),
4184 )
4185 .await?;
4186 result
4187 .value
4188 .ok_or_else(|| ClientErrorKind::Custom("Invalid blockhash".to_string()).into())
4189 }
4190
4191 pub async fn get_new_latest_blockhash(&self, blockhash: &Hash) -> ClientResult<Hash> {
4192 let mut num_retries = 0;
4193 let start = Instant::now();
4194 while start.elapsed().as_secs() < 5 {
4195 if let Ok(new_blockhash) = self.get_latest_blockhash().await {
4196 if new_blockhash != *blockhash {
4197 return Ok(new_blockhash);
4198 }
4199 }
4200 debug!("Got same blockhash ({blockhash:?}), will retry...");
4201
4202 // Retry ~twice during a slot
4203 sleep(Duration::from_millis(DEFAULT_MS_PER_SLOT / 2)).await;
4204 num_retries += 1;
4205 }
4206 Err(RpcError::ForUser(format!(
4207 "Unable to get new blockhash after {}ms (retried {} times), stuck at {}",
4208 start.elapsed().as_millis(),
4209 num_retries,
4210 blockhash
4211 ))
4212 .into())
4213 }
4214}
4215
4216fn serde_err_to_rpc_err(err: serde_json::Error) -> ClientError {
4217 ClientError::from(ClientErrorKind::from(err))
4218}
4219
4220fn serialize_and_encode<T>(input: &T, encoding: UiTransactionEncoding) -> ClientResult<String>
4221where
4222 T: serde::ser::Serialize,
4223{
4224 let serialized = serialize(input)
4225 .map_err(|e| ClientErrorKind::Custom(format!("Serialization failed: {e}")))?;
4226 let encoded = match encoding {
4227 UiTransactionEncoding::Base58 => bs58::encode(serialized).into_string(),
4228 UiTransactionEncoding::Base64 => BASE64_STANDARD.encode(serialized),
4229 _ => {
4230 return Err(ClientErrorKind::Custom(format!(
4231 "unsupported encoding: {encoding}. Supported encodings: base58, base64"
4232 ))
4233 .into());
4234 }
4235 };
4236 Ok(encoded)
4237}
4238
4239#[deprecated(
4240 note = "Parsing accounts whose data is of type `UiAccountData::Json` will yield `None` when \
4241 it should not. Do not use this function."
4242)]
4243pub(crate) fn parse_keyed_accounts(
4244 accounts: Vec<RpcKeyedAccount>,
4245 request: RpcRequest,
4246) -> ClientResult<Vec<(Pubkey, Account)>> {
4247 let mut pubkey_accounts: Vec<(Pubkey, Account)> = Vec::with_capacity(accounts.len());
4248 for RpcKeyedAccount { pubkey, account } in accounts.into_iter() {
4249 let pubkey = pubkey.parse().map_err(|_| {
4250 ClientError::new_with_request(
4251 RpcError::ParseError("Pubkey".to_string()).into(),
4252 request,
4253 )
4254 })?;
4255 pubkey_accounts.push((
4256 pubkey,
4257 account.decode().ok_or_else(|| {
4258 ClientError::new_with_request(
4259 RpcError::ParseError("Account from rpc".to_string()).into(),
4260 request,
4261 )
4262 })?,
4263 ));
4264 }
4265 Ok(pubkey_accounts)
4266}
4267
4268fn pubkey_ui_account_client_result_from_keyed_accounts(
4269 accounts: Vec<RpcKeyedAccount>,
4270 request: RpcRequest,
4271) -> ClientResult<Vec<(Pubkey, UiAccount)>> {
4272 let mut pubkey_ui_accounts: Vec<(Pubkey, UiAccount)> = Vec::with_capacity(accounts.len());
4273 for RpcKeyedAccount { account, pubkey } in accounts.iter() {
4274 let pubkey = pubkey.parse().map_err(|_| {
4275 ClientError::new_with_request(
4276 RpcError::ParseError("Pubkey".to_string()).into(),
4277 request,
4278 )
4279 })?;
4280 pubkey_ui_accounts.push((pubkey, account.clone()));
4281 }
4282 Ok(pubkey_ui_accounts)
4283}