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