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