Skip to main content

solana_rpc_client/nonblocking/
rpc_client.rs

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