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