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