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