atlas_rpc_client/nonblocking/
rpc_client.rs

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