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