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