Skip to main content

solana_rpc_client/nonblocking/
rpc_client.rs

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