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