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