gemachain_client/
rpc_client.rs

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