safecoin_client/nonblocking/
rpc_client.rs

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