gemachain_client/rpc_client.rs
1//! Communication with a Gemachain node over RPC.
2//!
3//! Software that interacts with the Gemachain blockchain, whether querying its
4//! state or submitting transactions, communicates with a Gemachain node over
5//! [JSON-RPC], using the [`RpcClient`] type.
6//!
7//! [JSON-RPC]: https://www.jsonrpc.org/specification
8
9#[allow(deprecated)]
10use crate::rpc_deprecated_config::{
11 RpcConfirmedBlockConfig, RpcConfirmedTransactionConfig,
12 RpcGetConfirmedSignaturesForAddress2Config,
13};
14use {
15 crate::{
16 client_error::{ClientError, ClientErrorKind, Result as ClientResult},
17 http_sender::HttpSender,
18 mock_sender::{MockSender, Mocks},
19 rpc_config::RpcAccountInfoConfig,
20 rpc_config::*,
21 rpc_request::{RpcError, RpcRequest, RpcResponseErrorData, TokenAccountsFilter},
22 rpc_response::*,
23 rpc_sender::*,
24 },
25 bincode::serialize,
26 indicatif::{ProgressBar, ProgressStyle},
27 log::*,
28 serde_json::{json, Value},
29 gemachain_account_decoder::{
30 parse_token::{TokenAccountType, UiTokenAccount, UiTokenAmount},
31 UiAccount, UiAccountData, UiAccountEncoding,
32 },
33 gemachain_sdk::{
34 account::Account,
35 clock::{Epoch, Slot, UnixTimestamp, DEFAULT_MS_PER_SLOT, MAX_HASH_AGE_IN_SECONDS},
36 commitment_config::{CommitmentConfig, CommitmentLevel},
37 epoch_info::EpochInfo,
38 epoch_schedule::EpochSchedule,
39 fee_calculator::{FeeCalculator, FeeRateGovernor},
40 hash::Hash,
41 message::Message,
42 pubkey::Pubkey,
43 signature::Signature,
44 transaction::{self, uses_durable_nonce, Transaction},
45 },
46 gemachain_transaction_status::{
47 EncodedConfirmedBlock, EncodedConfirmedTransaction, TransactionStatus, UiConfirmedBlock,
48 UiTransactionEncoding,
49 },
50 gemachain_vote_program::vote_state::MAX_LOCKOUT_HISTORY,
51 std::{
52 cmp::min,
53 net::SocketAddr,
54 str::FromStr,
55 sync::RwLock,
56 thread::sleep,
57 time::{Duration, Instant},
58 },
59};
60
61#[derive(Default)]
62pub struct RpcClientConfig {
63 commitment_config: CommitmentConfig,
64 confirm_transaction_initial_timeout: Option<Duration>,
65}
66
67impl RpcClientConfig {
68 fn with_commitment(commitment_config: CommitmentConfig) -> Self {
69 RpcClientConfig {
70 commitment_config,
71 ..Self::default()
72 }
73 }
74}
75
76/// A client of a remote Gemachain node.
77///
78/// `RpcClient` communicates with a Gemachain node over [JSON-RPC], with the
79/// [Gemachain JSON-RPC protocol][jsonprot]. It is the primary Rust interface for
80/// querying and transacting with the network from external programs.
81///
82/// This type builds on the underlying RPC protocol, adding extra features such
83/// as timeout handling, retries, and waiting on transaction commitment levels.
84/// Some methods simply pass through to the underlying RPC protocol. Not all RPC
85/// methods are encapsulated by this type, but `RpcClient` does expose a generic
86/// [`send`](RpcClient::send) method for making any [`RpcRequest`].
87///
88/// The documentation for most `RpcClient` methods contains an "RPC Reference"
89/// section that links to the documentation for the underlying JSON-RPC method.
90/// The documentation for `RpcClient` does not reproduce the documentation for
91/// the underlying JSON-RPC methods. Thus reading both is necessary for complete
92/// understanding.
93///
94/// `RpcClient`s generally communicate over HTTP on port 8899, a typical server
95/// URL being "http://localhost:8899".
96///
97/// By default, requests to confirm transactions only succeed once those
98/// transactions are finalized, meaning they are definitely permanently
99/// committed. Transactions can be confirmed with less finality by creating
100/// `RpcClient` with an explicit [`CommitmentConfig`], or by calling the various
101/// `_with_commitment` methods, like
102/// [`RpcClient::confirm_transaction_with_commitment`].
103///
104/// [jsonprot]: https://docs.gemachain.com/developing/clients/jsonrpc-api
105/// [JSON-RPC]: https://www.jsonrpc.org/specification
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 gemachain_sdk::system_transaction;
121/// # use gemachain_client::rpc_client::RpcClient;
122/// # use gemachain_client::client_error::ClientError;
123/// # use gemachain_sdk::signature::{Keypair, Signer};
124/// # use gemachain_sdk::hash::Hash;
125/// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
126/// # let key = Keypair::new();
127/// # let to = gemachain_sdk::pubkey::new_rand();
128/// # let carats = 50;
129/// # let latest_blockhash = Hash::default();
130/// # let tx = system_transaction::transfer(&key, &to, carats, 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 [`new`] and [`new_mock`], which create an `RpcClient`
154 /// encapsulating an [`HttpSender`] and [`MockSender`] respectively.
155 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 of [`Finalized`](CommitmentLevel::Finalized).
173 ///
174 /// # Examples
175 ///
176 /// ```
177 /// # use gemachain_client::rpc_client::RpcClient;
178 /// let url = "http://localhost:8899".to_string();
179 /// let client = RpcClient::new(url);
180 /// ```
181 pub fn new(url: String) -> Self {
182 Self::new_with_commitment(url, CommitmentConfig::default())
183 }
184
185 /// Create an HTTP `RpcClient` with specified commitment level.
186 ///
187 /// The URL is an HTTP URL, usually for port 8899, as in
188 /// "http://localhost:8899".
189 ///
190 /// The client has a default timeout of 30 seconds, and a user-specified
191 /// [`CommitmentLevel`] via [`CommitmentConfig`].
192 ///
193 /// # Examples
194 ///
195 /// ```
196 /// # use gemachain_sdk::commitment_config::CommitmentConfig;
197 /// # use gemachain_client::rpc_client::RpcClient;
198 /// let url = "http://localhost:8899".to_string();
199 /// let commitment_config = CommitmentConfig::processed();
200 /// let client = RpcClient::new_with_commitment(url, commitment_config);
201 /// ```
202 pub fn new_with_commitment(url: String, commitment_config: CommitmentConfig) -> Self {
203 Self::new_sender(
204 HttpSender::new(url),
205 RpcClientConfig::with_commitment(commitment_config),
206 )
207 }
208
209 /// Create an HTTP `RpcClient` with specified timeout.
210 ///
211 /// The URL is an HTTP URL, usually for port 8899, as in
212 /// "http://localhost:8899".
213 ///
214 /// The client has and a default commitment level of
215 /// [`Finalized`](CommitmentLevel::Finalized).
216 ///
217 /// # Examples
218 ///
219 /// ```
220 /// # use std::time::Duration;
221 /// # use gemachain_client::rpc_client::RpcClient;
222 /// let url = "http://localhost::8899".to_string();
223 /// let timeout = Duration::from_secs(1);
224 /// let client = RpcClient::new_with_timeout(url, timeout);
225 /// ```
226 pub fn new_with_timeout(url: String, timeout: Duration) -> Self {
227 Self::new_sender(
228 HttpSender::new_with_timeout(url, timeout),
229 RpcClientConfig::with_commitment(CommitmentConfig::default()),
230 )
231 }
232
233 /// Create an HTTP `RpcClient` with specified timeout and commitment level.
234 ///
235 /// The URL is an HTTP URL, usually for port 8899, as in
236 /// "http://localhost:8899".
237 ///
238 /// # Examples
239 ///
240 /// ```
241 /// # use std::time::Duration;
242 /// # use gemachain_client::rpc_client::RpcClient;
243 /// # use gemachain_sdk::commitment_config::CommitmentConfig;
244 /// let url = "http://localhost::8899".to_string();
245 /// let timeout = Duration::from_secs(1);
246 /// let commitment_config = CommitmentConfig::processed();
247 /// let client = RpcClient::new_with_timeout_and_commitment(
248 /// url,
249 /// timeout,
250 /// commitment_config,
251 /// );
252 /// ```
253 pub fn new_with_timeout_and_commitment(
254 url: String,
255 timeout: Duration,
256 commitment_config: CommitmentConfig,
257 ) -> Self {
258 Self::new_sender(
259 HttpSender::new_with_timeout(url, timeout),
260 RpcClientConfig::with_commitment(commitment_config),
261 )
262 }
263
264 /// Create an HTTP `RpcClient` with specified timeout and commitment level.
265 ///
266 /// The URL is an HTTP URL, usually for port 8899, as in
267 /// "http://localhost:8899".
268 ///
269 /// The `confirm_transaction_initial_timeout` argument specifies, when
270 /// confirming a transaction via one of the `_with_spinner` methods, like
271 /// [`RpcClient::send_and_confirm_transaction_with_spinner`], the amount of
272 /// time to allow for the server to initially process a transaction. In
273 /// other words, setting `confirm_transaction_initial_timeout` to > 0 allows
274 /// `RpcClient` to wait for confirmation of a transaction that the server
275 /// has not "seen" yet.
276 ///
277 /// # Examples
278 ///
279 /// ```
280 /// # use std::time::Duration;
281 /// # use gemachain_client::rpc_client::RpcClient;
282 /// # use gemachain_sdk::commitment_config::CommitmentConfig;
283 /// let url = "http://localhost::8899".to_string();
284 /// let timeout = Duration::from_secs(1);
285 /// let commitment_config = CommitmentConfig::processed();
286 /// let confirm_transaction_initial_timeout = Duration::from_secs(10);
287 /// let client = RpcClient::new_with_timeouts_and_commitment(
288 /// url,
289 /// timeout,
290 /// commitment_config,
291 /// confirm_transaction_initial_timeout,
292 /// );
293 /// ```
294 pub fn new_with_timeouts_and_commitment(
295 url: String,
296 timeout: Duration,
297 commitment_config: CommitmentConfig,
298 confirm_transaction_initial_timeout: Duration,
299 ) -> Self {
300 Self::new_sender(
301 HttpSender::new_with_timeout(url, timeout),
302 RpcClientConfig {
303 commitment_config,
304 confirm_transaction_initial_timeout: Some(confirm_transaction_initial_timeout),
305 },
306 )
307 }
308
309 /// Create a mock `RpcClient`.
310 ///
311 /// See the [`MockSender`] documentation for an explanation of
312 /// how it treats the `url` argument.
313 ///
314 /// # Examples
315 ///
316 /// ```
317 /// # use gemachain_client::rpc_client::RpcClient;
318 /// // Create an `RpcClient` that always succeeds
319 /// let url = "succeeds".to_string();
320 /// let successful_client = RpcClient::new_mock(url);
321 /// ```
322 ///
323 /// ```
324 /// # use gemachain_client::rpc_client::RpcClient;
325 /// // Create an `RpcClient` that always fails
326 /// let url = "fails".to_string();
327 /// let successful_client = RpcClient::new_mock(url);
328 /// ```
329 pub fn new_mock(url: String) -> Self {
330 Self::new_sender(
331 MockSender::new(url),
332 RpcClientConfig::with_commitment(CommitmentConfig::default()),
333 )
334 }
335
336 /// Create a mock `RpcClient`.
337 ///
338 /// See the [`MockSender`] documentation for an explanation of how it treats
339 /// the `url` argument.
340 ///
341 /// # Examples
342 ///
343 /// ```
344 /// # use gemachain_client::{
345 /// # rpc_client::RpcClient,
346 /// # rpc_request::RpcRequest,
347 /// # rpc_response::{Response, RpcResponseContext},
348 /// # };
349 /// # use std::collections::HashMap;
350 /// # use serde_json::json;
351 /// // Create a mock with a custom repsonse to the `GetBalance` request
352 /// let account_balance = 50;
353 /// let account_balance_response = json!(Response {
354 /// context: RpcResponseContext { slot: 1 },
355 /// value: json!(account_balance),
356 /// });
357 ///
358 /// let mut mocks = HashMap::new();
359 /// mocks.insert(RpcRequest::GetBalance, account_balance_response);
360 /// let url = "succeeds".to_string();
361 /// let client = RpcClient::new_mock_with_mocks(url, mocks);
362 /// ```
363 pub fn new_mock_with_mocks(url: String, mocks: Mocks) -> Self {
364 Self::new_sender(
365 MockSender::new_with_mocks(url, mocks),
366 RpcClientConfig::with_commitment(CommitmentConfig::default()),
367 )
368 }
369
370 /// Create an HTTP `RpcClient` from a [`SocketAddr`].
371 ///
372 /// The client has a default timeout of 30 seconds, and a default commitment
373 /// level of [`Finalized`](CommitmentLevel::Finalized).
374 ///
375 /// # Examples
376 ///
377 /// ```
378 /// # use std::net::SocketAddr;
379 /// # use gemachain_client::rpc_client::RpcClient;
380 /// let addr = SocketAddr::from(([127, 0, 0, 1], 8899));
381 /// let client = RpcClient::new_socket(addr);
382 /// ```
383 pub fn new_socket(addr: SocketAddr) -> Self {
384 Self::new(get_rpc_request_str(addr, false))
385 }
386
387 /// Create an HTTP `RpcClient` from a [`SocketAddr`] with specified commitment level.
388 ///
389 /// The client has a default timeout of 30 seconds, and a user-specified
390 /// [`CommitmentLevel`] via [`CommitmentConfig`].
391 ///
392 /// # Examples
393 ///
394 /// ```
395 /// # use std::net::SocketAddr;
396 /// # use gemachain_client::rpc_client::RpcClient;
397 /// # use gemachain_sdk::commitment_config::CommitmentConfig;
398 /// let addr = SocketAddr::from(([127, 0, 0, 1], 8899));
399 /// let commitment_config = CommitmentConfig::processed();
400 /// let client = RpcClient::new_socket_with_commitment(
401 /// addr,
402 /// commitment_config
403 /// );
404 /// ```
405 pub fn new_socket_with_commitment(
406 addr: SocketAddr,
407 commitment_config: CommitmentConfig,
408 ) -> Self {
409 Self::new_with_commitment(get_rpc_request_str(addr, false), commitment_config)
410 }
411
412 /// Create an HTTP `RpcClient` from a [`SocketAddr`] with specified timeout.
413 ///
414 /// The client has and a default commitment level of [`Finalized`](CommitmentLevel::Finalized).
415 ///
416 /// # Examples
417 ///
418 /// ```
419 /// # use std::net::SocketAddr;
420 /// # use std::time::Duration;
421 /// # use gemachain_client::rpc_client::RpcClient;
422 /// let addr = SocketAddr::from(([127, 0, 0, 1], 8899));
423 /// let timeout = Duration::from_secs(1);
424 /// let client = RpcClient::new_socket_with_timeout(addr, timeout);
425 /// ```
426 pub fn new_socket_with_timeout(addr: SocketAddr, timeout: Duration) -> Self {
427 let url = get_rpc_request_str(addr, false);
428 Self::new_with_timeout(url, timeout)
429 }
430
431 fn get_node_version(&self) -> Result<semver::Version, RpcError> {
432 let r_node_version = self.node_version.read().unwrap();
433 if let Some(version) = &*r_node_version {
434 Ok(version.clone())
435 } else {
436 drop(r_node_version);
437 let mut w_node_version = self.node_version.write().unwrap();
438 let node_version = self.get_version().map_err(|e| {
439 RpcError::RpcRequestError(format!("cluster version query failed: {}", e))
440 })?;
441 let node_version = semver::Version::parse(&node_version.gemachain_core).map_err(|e| {
442 RpcError::RpcRequestError(format!("failed to parse cluster version: {}", e))
443 })?;
444 *w_node_version = Some(node_version.clone());
445 Ok(node_version)
446 }
447 }
448
449 /// Get the configured default commitment level.
450 ///
451 /// The commitment config may be specified during construction, and
452 /// determines how thoroughly committed a transaction must be when waiting
453 /// for its confirmation or otherwise checking for confirmation. If not
454 /// specified, the default commitment level is
455 /// [`Finalized`](CommitmentLevel::Finalized).
456 ///
457 /// The default commitment level is overridden when calling methods that
458 /// explicitly provide a [`CommitmentConfig`], like
459 /// [`RpcClient::confirm_transaction_with_commitment`].
460 pub fn commitment(&self) -> CommitmentConfig {
461 self.config.commitment_config
462 }
463
464 fn use_deprecated_commitment(&self) -> Result<bool, RpcError> {
465 Ok(self.get_node_version()? < semver::Version::new(1, 5, 5))
466 }
467
468 fn maybe_map_commitment(
469 &self,
470 requested_commitment: CommitmentConfig,
471 ) -> Result<CommitmentConfig, RpcError> {
472 if matches!(
473 requested_commitment.commitment,
474 CommitmentLevel::Finalized | CommitmentLevel::Confirmed | CommitmentLevel::Processed
475 ) && self.use_deprecated_commitment()?
476 {
477 return Ok(CommitmentConfig::use_deprecated_commitment(
478 requested_commitment,
479 ));
480 }
481 Ok(requested_commitment)
482 }
483
484 #[allow(deprecated)]
485 fn maybe_map_request(&self, mut request: RpcRequest) -> Result<RpcRequest, RpcError> {
486 if self.get_node_version()? < semver::Version::new(1, 7, 0) {
487 request = match request {
488 RpcRequest::GetBlock => RpcRequest::GetConfirmedBlock,
489 RpcRequest::GetBlocks => RpcRequest::GetConfirmedBlocks,
490 RpcRequest::GetBlocksWithLimit => RpcRequest::GetConfirmedBlocksWithLimit,
491 RpcRequest::GetSignaturesForAddress => {
492 RpcRequest::GetConfirmedSignaturesForAddress2
493 }
494 RpcRequest::GetTransaction => RpcRequest::GetConfirmedTransaction,
495 _ => request,
496 };
497 }
498 Ok(request)
499 }
500
501 /// Check the confirmation status of a transaction.
502 ///
503 /// Returns `true` if the given transaction succeeded and has been committed
504 /// with the configured commitment level, which can be retrieved with
505 /// the [`commitment`](RpcClient::commitment) method.
506 ///
507 /// Note that this method does not wait for a transaction to be confirmed
508 /// — it only checks whether a transaction has been confirmed. To
509 /// submit a transaction and wait for it to confirm, use
510 /// [`send_and_confirm_transaction`][RpcClient::send_and_confirm_transaction].
511 ///
512 /// _This method returns `false` if the transaction failed, even if it has
513 /// been confirmed._
514 ///
515 /// # RPC Reference
516 ///
517 /// This method is built on the [`getSignatureStatuses`] RPC method.
518 ///
519 /// [`getSignatureStatuses`]: https://docs.gemachain.com/developing/clients/jsonrpc-api#getsignaturestatuses
520 ///
521 /// # Examples
522 ///
523 /// ```
524 /// # use gemachain_client::{
525 /// # client_error::ClientError,
526 /// # rpc_client::RpcClient,
527 /// # };
528 /// # use gemachain_sdk::{
529 /// # signature::Signer,
530 /// # signature::Signature,
531 /// # signer::keypair::Keypair,
532 /// # system_transaction,
533 /// # };
534 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
535 /// // Transfer carats from Alice to Bob and wait for confirmation
536 /// # let alice = Keypair::new();
537 /// # let bob = Keypair::new();
538 /// # let carats = 50;
539 /// let latest_blockhash = rpc_client.get_latest_blockhash()?;
540 /// let tx = system_transaction::transfer(&alice, &bob.pubkey(), carats, latest_blockhash);
541 /// let signature = rpc_client.send_transaction(&tx)?;
542 ///
543 /// loop {
544 /// let confirmed = rpc_client.confirm_transaction(&signature)?;
545 /// if confirmed {
546 /// break;
547 /// }
548 /// }
549 /// # Ok::<(), ClientError>(())
550 /// ```
551 pub fn confirm_transaction(&self, signature: &Signature) -> ClientResult<bool> {
552 Ok(self
553 .confirm_transaction_with_commitment(signature, self.commitment())?
554 .value)
555 }
556
557 /// Check the confirmation status of a transaction.
558 ///
559 /// Returns an [`RpcResult`] with value `true` if the given transaction
560 /// succeeded and has been committed with the given commitment level.
561 ///
562 /// Note that this method does not wait for a transaction to be confirmed
563 /// — it only checks whether a transaction has been confirmed. To
564 /// submit a transaction and wait for it to confirm, use
565 /// [`send_and_confirm_transaction`][RpcClient::send_and_confirm_transaction].
566 ///
567 /// _This method returns an [`RpcResult`] with value `false` if the
568 /// transaction failed, even if it has been confirmed._
569 ///
570 /// # RPC Reference
571 ///
572 /// This method is built on the [`getSignatureStatuses`] RPC method.
573 ///
574 /// [`getSignatureStatuses`]: https://docs.gemachain.com/developing/clients/jsonrpc-api#getsignaturestatuses
575 ///
576 /// # Examples
577 ///
578 /// ```
579 /// # use gemachain_client::{
580 /// # client_error::ClientError,
581 /// # rpc_client::RpcClient,
582 /// # };
583 /// # use gemachain_sdk::{
584 /// # commitment_config::CommitmentConfig,
585 /// # signature::Signer,
586 /// # signature::Signature,
587 /// # signer::keypair::Keypair,
588 /// # system_transaction,
589 /// # };
590 /// # use std::time::Duration;
591 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
592 /// // Transfer carats from Alice to Bob and wait for confirmation
593 /// # let alice = Keypair::new();
594 /// # let bob = Keypair::new();
595 /// # let carats = 50;
596 /// let latest_blockhash = rpc_client.get_latest_blockhash()?;
597 /// let tx = system_transaction::transfer(&alice, &bob.pubkey(), carats, latest_blockhash);
598 /// let signature = rpc_client.send_transaction(&tx)?;
599 ///
600 /// loop {
601 /// let commitment_config = CommitmentConfig::processed();
602 /// let confirmed = rpc_client.confirm_transaction_with_commitment(&signature, commitment_config)?;
603 /// if confirmed.value {
604 /// break;
605 /// }
606 /// }
607 /// # Ok::<(), ClientError>(())
608 /// ```
609 pub fn confirm_transaction_with_commitment(
610 &self,
611 signature: &Signature,
612 commitment_config: CommitmentConfig,
613 ) -> RpcResult<bool> {
614 let Response { context, value } = self.get_signature_statuses(&[*signature])?;
615
616 Ok(Response {
617 context,
618 value: value[0]
619 .as_ref()
620 .filter(|result| result.satisfies_commitment(commitment_config))
621 .map(|result| result.status.is_ok())
622 .unwrap_or_default(),
623 })
624 }
625
626 /// Submits a signed transaction to the network.
627 ///
628 /// Before a transaction is processed, the receiving node runs a "preflight
629 /// check" which verifies signatures, checks that the node is healthy,
630 /// and simulates the transaction. If the preflight check fails then an
631 /// error is returned immediately. Preflight checks can be disabled by
632 /// calling [`send_transaction_with_config`] and setting the
633 /// [`skip_preflight`] field of [`RpcSendTransactionConfig`] to `true`.
634 ///
635 /// This method does not wait for the transaction to be processed or
636 /// confirmed before returning successfully. To wait for the transaction to
637 /// be processed or confirmed, use the [`send_and_confirm_transaction`]
638 /// method.
639 ///
640 /// [`send_transaction_with_config`]: RpcClient::send_transaction_with_config
641 /// [`skip_preflight`]: crate::rpc_config::RpcSendTransactionConfig::skip_preflight
642 /// [`RpcSendTransactionConfig`]: crate::rpc_config::RpcSendTransactionConfig
643 /// [`send_and_confirm_transaction`]: RpcClient::send_and_confirm_transaction
644 ///
645 /// # Errors
646 ///
647 /// If the transaction is not signed then an error with kind [`RpcError`] is
648 /// returned, containing an [`RpcResponseError`] with `code` set to
649 /// [`JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE`].
650 ///
651 /// If the preflight transaction simulation fails then an error with kind
652 /// [`RpcError`] is returned, containing an [`RpcResponseError`] with `code`
653 /// set to [`JSON_RPC_SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE`].
654 ///
655 /// If the receiving node is unhealthy, e.g. it is not fully synced to
656 /// the cluster, then an error with kind [`RpcError`] is returned,
657 /// containing an [`RpcResponseError`] with `code` set to
658 /// [`JSON_RPC_SERVER_ERROR_NODE_UNHEALTHY`].
659 ///
660 /// [`RpcResponseError`]: RpcError::RpcResponseError
661 /// [`JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE`]: crate::rpc_custom_error::JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE
662 /// [`JSON_RPC_SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE`]: crate::rpc_custom_error::JSON_RPC_SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE
663 /// [`JSON_RPC_SERVER_ERROR_NODE_UNHEALTHY`]: crate::rpc_custom_error::JSON_RPC_SERVER_ERROR_NODE_UNHEALTHY
664 ///
665 /// # RPC Reference
666 ///
667 /// This method is built on the [`sendTransaction`] RPC method.
668 ///
669 /// [`sendTransaction`]: https://docs.gemachain.com/developing/clients/jsonrpc-api#sendtransaction
670 ///
671 /// # Examples
672 ///
673 /// ```
674 /// # use gemachain_client::{
675 /// # client_error::ClientError,
676 /// # rpc_client::RpcClient,
677 /// # };
678 /// # use gemachain_sdk::{
679 /// # signature::Signer,
680 /// # signature::Signature,
681 /// # signer::keypair::Keypair,
682 /// # hash::Hash,
683 /// # system_transaction,
684 /// # };
685 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
686 /// // Transfer carats from Alice to Bob
687 /// # let alice = Keypair::new();
688 /// # let bob = Keypair::new();
689 /// # let carats = 50;
690 /// let latest_blockhash = rpc_client.get_latest_blockhash()?;
691 /// let tx = system_transaction::transfer(&alice, &bob.pubkey(), carats, latest_blockhash);
692 /// let signature = rpc_client.send_transaction(&tx)?;
693 /// # Ok::<(), ClientError>(())
694 /// ```
695 pub fn send_transaction(&self, transaction: &Transaction) -> ClientResult<Signature> {
696 self.send_transaction_with_config(
697 transaction,
698 RpcSendTransactionConfig {
699 preflight_commitment: Some(
700 self.maybe_map_commitment(self.commitment())?.commitment,
701 ),
702 ..RpcSendTransactionConfig::default()
703 },
704 )
705 }
706
707 fn default_cluster_transaction_encoding(&self) -> Result<UiTransactionEncoding, RpcError> {
708 if self.get_node_version()? < semver::Version::new(1, 3, 16) {
709 Ok(UiTransactionEncoding::Base58)
710 } else {
711 Ok(UiTransactionEncoding::Base64)
712 }
713 }
714
715 /// Submits a signed transaction to the network.
716 ///
717 /// Before a transaction is processed, the receiving node runs a "preflight
718 /// check" which verifies signatures, checks that the node is healthy, and
719 /// simulates the transaction. If the preflight check fails then an error is
720 /// returned immediately. Preflight checks can be disabled by setting the
721 /// [`skip_preflight`] field of [`RpcSendTransactionConfig`] to `true`.
722 ///
723 /// This method does not wait for the transaction to be processed or
724 /// confirmed before returning successfully. To wait for the transaction to
725 /// be processed or confirmed, use the [`send_and_confirm_transaction`]
726 /// method.
727 ///
728 /// [`send_transaction_with_config`]: RpcClient::send_transaction_with_config
729 /// [`skip_preflight`]: crate::rpc_config::RpcSendTransactionConfig::skip_preflight
730 /// [`RpcSendTransactionConfig`]: crate::rpc_config::RpcSendTransactionConfig
731 /// [`send_and_confirm_transaction`]: RpcClient::send_and_confirm_transaction
732 ///
733 /// # Errors
734 ///
735 /// If preflight checks are enabled, if the transaction is not signed
736 /// then an error with kind [`RpcError`] is returned, containing an
737 /// [`RpcResponseError`] with `code` set to
738 /// [`JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE`].
739 ///
740 /// If preflight checks are enabled, if the preflight transaction simulation
741 /// fails then an error with kind [`RpcError`] is returned, containing an
742 /// [`RpcResponseError`] with `code` set to
743 /// [`JSON_RPC_SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE`].
744 ///
745 /// If the receiving node is unhealthy, e.g. it is not fully synced to
746 /// the cluster, then an error with kind [`RpcError`] is returned,
747 /// containing an [`RpcResponseError`] with `code` set to
748 /// [`JSON_RPC_SERVER_ERROR_NODE_UNHEALTHY`].
749 ///
750 /// [`RpcResponseError`]: RpcError::RpcResponseError
751 /// [`JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE`]: crate::rpc_custom_error::JSON_RPC_SERVER_ERROR_TRANSACTION_SIGNATURE_VERIFICATION_FAILURE
752 /// [`JSON_RPC_SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE`]: crate::rpc_custom_error::JSON_RPC_SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE
753 /// [`JSON_RPC_SERVER_ERROR_NODE_UNHEALTHY`]: crate::rpc_custom_error::JSON_RPC_SERVER_ERROR_NODE_UNHEALTHY
754 ///
755 /// # RPC Reference
756 ///
757 /// This method is built on the [`sendTransaction`] RPC method.
758 ///
759 /// [`sendTransaction`]: https://docs.gemachain.com/developing/clients/jsonrpc-api#sendtransaction
760 ///
761 /// # Examples
762 ///
763 /// ```
764 /// # use gemachain_client::{
765 /// # client_error::ClientError,
766 /// # rpc_client::RpcClient,
767 /// # rpc_config::RpcSendTransactionConfig,
768 /// # };
769 /// # use gemachain_sdk::{
770 /// # signature::Signer,
771 /// # signature::Signature,
772 /// # signer::keypair::Keypair,
773 /// # hash::Hash,
774 /// # system_transaction,
775 /// # };
776 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
777 /// // Transfer carats from Alice to Bob
778 /// # let alice = Keypair::new();
779 /// # let bob = Keypair::new();
780 /// # let carats = 50;
781 /// let latest_blockhash = rpc_client.get_latest_blockhash()?;
782 /// let tx = system_transaction::transfer(&alice, &bob.pubkey(), carats, latest_blockhash);
783 /// let config = RpcSendTransactionConfig {
784 /// skip_preflight: true,
785 /// .. RpcSendTransactionConfig::default()
786 /// };
787 /// let signature = rpc_client.send_transaction_with_config(
788 /// &tx,
789 /// config,
790 /// )?;
791 /// # Ok::<(), ClientError>(())
792 /// ```
793 pub fn send_transaction_with_config(
794 &self,
795 transaction: &Transaction,
796 config: RpcSendTransactionConfig,
797 ) -> ClientResult<Signature> {
798 let encoding = if let Some(encoding) = config.encoding {
799 encoding
800 } else {
801 self.default_cluster_transaction_encoding()?
802 };
803 let preflight_commitment = CommitmentConfig {
804 commitment: config.preflight_commitment.unwrap_or_default(),
805 };
806 let preflight_commitment = self.maybe_map_commitment(preflight_commitment)?;
807 let config = RpcSendTransactionConfig {
808 encoding: Some(encoding),
809 preflight_commitment: Some(preflight_commitment.commitment),
810 ..config
811 };
812 let serialized_encoded = serialize_and_encode::<Transaction>(transaction, encoding)?;
813 let signature_base58_str: String = match self.send(
814 RpcRequest::SendTransaction,
815 json!([serialized_encoded, config]),
816 ) {
817 Ok(signature_base58_str) => signature_base58_str,
818 Err(err) => {
819 if let ClientErrorKind::RpcError(RpcError::RpcResponseError {
820 code,
821 message,
822 data,
823 }) = &err.kind
824 {
825 debug!("{} {}", code, message);
826 if let RpcResponseErrorData::SendTransactionPreflightFailure(
827 RpcSimulateTransactionResult {
828 logs: Some(logs), ..
829 },
830 ) = data
831 {
832 for (i, log) in logs.iter().enumerate() {
833 debug!("{:>3}: {}", i + 1, log);
834 }
835 debug!("");
836 }
837 }
838 return Err(err);
839 }
840 };
841
842 let signature = signature_base58_str
843 .parse::<Signature>()
844 .map_err(|err| Into::<ClientError>::into(RpcError::ParseError(err.to_string())))?;
845 // A mismatching RPC response signature indicates an issue with the RPC node, and
846 // should not be passed along to confirmation methods. The transaction may or may
847 // not have been submitted to the cluster, so callers should verify the success of
848 // the correct transaction signature independently.
849 if signature != transaction.signatures[0] {
850 Err(RpcError::RpcRequestError(format!(
851 "RPC node returned mismatched signature {:?}, expected {:?}",
852 signature, transaction.signatures[0]
853 ))
854 .into())
855 } else {
856 Ok(transaction.signatures[0])
857 }
858 }
859
860 /// Simulates sending a transaction.
861 ///
862 /// If the transaction fails, then the [`err`] field of the returned
863 /// [`RpcSimulateTransactionResult`] will be `Some`. Any logs emitted from
864 /// the transaction are returned in the [`logs`] field.
865 ///
866 /// [`err`]: crate::rpc_response::RpcSimulateTransactionResult::err
867 /// [`logs`]: crate::rpc_response::RpcSimulateTransactionResult::logs
868 ///
869 /// Simulating a transaction is similar to the ["preflight check"] that is
870 /// run by default when sending a transaction.
871 ///
872 /// ["preflight check"]: https://docs.gemachain.com/developing/clients/jsonrpc-api#sendtransaction
873 ///
874 /// By default, signatures are not verified during simulation. To verify
875 /// signatures, call the [`simulate_transaction_with_config`] method, with
876 /// the [`sig_verify`] field of [`RpcSimulateTransactionConfig`] set to
877 /// `true`.
878 ///
879 /// [`simulate_transaction_with_config`]: RpcClient::simulate_transaction_with_config
880 /// [`sig_verify`]: crate::rpc_config::RpcSimulateTransactionConfig::sig_verify
881 ///
882 /// # RPC Reference
883 ///
884 /// This method is built on the [`simulateTransaction`] RPC method.
885 ///
886 /// [`simulateTransaction`]: https://docs.gemachain.com/developing/clients/jsonrpc-api#simulatetransaction
887 ///
888 /// # Examples
889 ///
890 /// ```
891 /// # use gemachain_client::{
892 /// # client_error::ClientError,
893 /// # rpc_client::RpcClient,
894 /// # rpc_response::RpcSimulateTransactionResult,
895 /// # };
896 /// # use gemachain_sdk::{
897 /// # signature::Signer,
898 /// # signature::Signature,
899 /// # signer::keypair::Keypair,
900 /// # hash::Hash,
901 /// # system_transaction,
902 /// # };
903 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
904 /// // Transfer carats from Alice to Bob
905 /// # let alice = Keypair::new();
906 /// # let bob = Keypair::new();
907 /// # let carats = 50;
908 /// let latest_blockhash = rpc_client.get_latest_blockhash()?;
909 /// let tx = system_transaction::transfer(&alice, &bob.pubkey(), carats, latest_blockhash);
910 /// let result = rpc_client.simulate_transaction(&tx)?;
911 /// assert!(result.value.err.is_none());
912 /// # Ok::<(), ClientError>(())
913 /// ```
914 pub fn simulate_transaction(
915 &self,
916 transaction: &Transaction,
917 ) -> RpcResult<RpcSimulateTransactionResult> {
918 self.simulate_transaction_with_config(
919 transaction,
920 RpcSimulateTransactionConfig {
921 commitment: Some(self.commitment()),
922 ..RpcSimulateTransactionConfig::default()
923 },
924 )
925 }
926
927 /// Simulates sending a transaction.
928 ///
929 /// If the transaction fails, then the [`err`] field of the returned
930 /// [`RpcSimulateTransactionResult`] will be `Some`. Any logs emitted from
931 /// the transaction are returned in the [`logs`] field.
932 ///
933 /// [`err`]: crate::rpc_response::RpcSimulateTransactionResult::err
934 /// [`logs`]: crate::rpc_response::RpcSimulateTransactionResult::logs
935 ///
936 /// Simulating a transaction is similar to the ["preflight check"] that is
937 /// run by default when sending a transaction.
938 ///
939 /// ["preflight check"]: https://docs.gemachain.com/developing/clients/jsonrpc-api#sendtransaction
940 ///
941 /// By default, signatures are not verified during simulation. To verify
942 /// signatures, call the [`simulate_transaction_with_config`] method, with
943 /// the [`sig_verify`] field of [`RpcSimulateTransactionConfig`] set to
944 /// `true`.
945 ///
946 /// [`simulate_transaction_with_config`]: RpcClient::simulate_transaction_with_config
947 /// [`sig_verify`]: crate::rpc_config::RpcSimulateTransactionConfig::sig_verify
948 ///
949 /// This method can additionally query information about accounts by
950 /// including them in the [`accounts`] field of the
951 /// [`RpcSimulateTransactionConfig`] argument, in which case those results
952 /// are reported in the [`accounts`][accounts2] field of the returned
953 /// [`RpcSimulateTransactionResult`].
954 ///
955 /// [`accounts`]: crate::rpc_config::RpcSimulateTransactionConfig::accounts
956 /// [accounts2]: crate::rpc_response::RpcSimulateTransactionResult::accounts
957 ///
958 /// # RPC Reference
959 ///
960 /// This method is built on the [`simulateTransaction`] RPC method.
961 ///
962 /// [`simulateTransaction`]: https://docs.gemachain.com/developing/clients/jsonrpc-api#simulatetransaction
963 ///
964 /// # Examples
965 ///
966 /// ```
967 /// # use gemachain_client::{
968 /// # client_error::ClientError,
969 /// # rpc_client::RpcClient,
970 /// # rpc_config::RpcSimulateTransactionConfig,
971 /// # rpc_response::RpcSimulateTransactionResult,
972 /// # };
973 /// # use gemachain_sdk::{
974 /// # signature::Signer,
975 /// # signer::keypair::Keypair,
976 /// # hash::Hash,
977 /// # system_transaction,
978 /// # };
979 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
980 /// // Transfer carats from Alice to Bob
981 /// # let alice = Keypair::new();
982 /// # let bob = Keypair::new();
983 /// # let carats = 50;
984 /// let latest_blockhash = rpc_client.get_latest_blockhash()?;
985 /// let tx = system_transaction::transfer(&alice, &bob.pubkey(), carats, latest_blockhash);
986 /// let config = RpcSimulateTransactionConfig {
987 /// sig_verify: true,
988 /// .. RpcSimulateTransactionConfig::default()
989 /// };
990 /// let result = rpc_client.simulate_transaction_with_config(
991 /// &tx,
992 /// config,
993 /// )?;
994 /// assert!(result.value.err.is_none());
995 /// # Ok::<(), ClientError>(())
996 /// ```
997 pub fn simulate_transaction_with_config(
998 &self,
999 transaction: &Transaction,
1000 config: RpcSimulateTransactionConfig,
1001 ) -> RpcResult<RpcSimulateTransactionResult> {
1002 let encoding = if let Some(encoding) = config.encoding {
1003 encoding
1004 } else {
1005 self.default_cluster_transaction_encoding()?
1006 };
1007 let commitment = config.commitment.unwrap_or_default();
1008 let commitment = self.maybe_map_commitment(commitment)?;
1009 let config = RpcSimulateTransactionConfig {
1010 encoding: Some(encoding),
1011 commitment: Some(commitment),
1012 ..config
1013 };
1014 let serialized_encoded = serialize_and_encode::<Transaction>(transaction, encoding)?;
1015 self.send(
1016 RpcRequest::SimulateTransaction,
1017 json!([serialized_encoded, config]),
1018 )
1019 }
1020
1021 /// Returns the highest slot information that the node has snapshots for.
1022 ///
1023 /// This will find the highest full snapshot slot, and the highest incremental snapshot slot
1024 /// _based on_ the full snapshot slot, if there is one.
1025 ///
1026 /// # RPC Reference
1027 ///
1028 /// This method corresponds directly to the [`getHighestSnapshotSlot`] RPC method.
1029 ///
1030 /// [`getHighestSnapshotSlot`]: https://docs.gemachain.com/developing/clients/jsonrpc-api#gethighestsnapshotslot
1031 ///
1032 /// # Examples
1033 ///
1034 /// ```
1035 /// # use gemachain_client::{
1036 /// # rpc_client::RpcClient,
1037 /// # client_error::ClientError,
1038 /// # };
1039 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
1040 /// let snapshot_slot_info = rpc_client.get_highest_snapshot_slot()?;
1041 /// # Ok::<(), ClientError>(())
1042 /// ```
1043 pub fn get_highest_snapshot_slot(&self) -> ClientResult<RpcSnapshotSlotInfo> {
1044 if self.get_node_version()? < semver::Version::new(1, 8, 0) {
1045 #[allow(deprecated)]
1046 self.get_snapshot_slot().map(|full| RpcSnapshotSlotInfo {
1047 full,
1048 incremental: None,
1049 })
1050 } else {
1051 self.send(RpcRequest::GetHighestSnapshotSlot, Value::Null)
1052 }
1053 }
1054
1055 #[deprecated(
1056 since = "1.8.0",
1057 note = "Please use RpcClient::get_highest_snapshot_slot() instead"
1058 )]
1059 #[allow(deprecated)]
1060 pub fn get_snapshot_slot(&self) -> ClientResult<Slot> {
1061 self.send(RpcRequest::GetSnapshotSlot, Value::Null)
1062 }
1063
1064 /// Check if a transaction has been processed with the default commitment level.
1065 ///
1066 /// If the transaction has been processed with the default commitment level,
1067 /// then this method returns `Ok` of `Some`. If the transaction has not yet
1068 /// been processed with the default commitment level, it returns `Ok` of
1069 /// `None`.
1070 ///
1071 /// If the transaction has been processed with the default commitment level,
1072 /// and the transaction succeeded, this method returns `Ok(Some(Ok(())))`.
1073 /// If the transaction has peen processed with the default commitment level,
1074 /// and the transaction failed, this method returns `Ok(Some(Err(_)))`,
1075 /// where the interior error is type [`TransactionError`].
1076 ///
1077 /// [`TransactionError`]: gemachain_sdk::transaction::TransactionError
1078 ///
1079 /// This function only searches a node's recent history, including all
1080 /// recent slots, plus up to
1081 /// [`MAX_RECENT_BLOCKHASHES`][gemachain_sdk::clock::MAX_RECENT_BLOCKHASHES]
1082 /// rooted slots. To search the full transaction history use the
1083 /// [`get_signature_statuse_with_commitment_and_history`][RpcClient::get_signature_status_with_commitment_and_history]
1084 /// method.
1085 ///
1086 /// # RPC Reference
1087 ///
1088 /// This method is built on the [`getSignatureStatuses`] RPC method.
1089 ///
1090 /// [`getSignatureStatuses`]: https://docs.gemachain.com/developing/clients/jsonrpc-api#gitsignaturestatuses
1091 ///
1092 /// # Examples
1093 ///
1094 /// ```
1095 /// # use gemachain_client::{
1096 /// # rpc_client::RpcClient,
1097 /// # client_error::ClientError,
1098 /// # };
1099 /// # use gemachain_sdk::{
1100 /// # signature::Signer,
1101 /// # signature::Signature,
1102 /// # signer::keypair::Keypair,
1103 /// # hash::Hash,
1104 /// # system_transaction,
1105 /// # };
1106 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
1107 /// # let alice = Keypair::new();
1108 /// # let bob = Keypair::new();
1109 /// # let carats = 50;
1110 /// # let latest_blockhash = rpc_client.get_latest_blockhash()?;
1111 /// # let tx = system_transaction::transfer(&alice, &bob.pubkey(), carats, latest_blockhash);
1112 /// let signature = rpc_client.send_transaction(&tx)?;
1113 /// let status = rpc_client.get_signature_status(&signature)?;
1114 /// # Ok::<(), ClientError>(())
1115 /// ```
1116 pub fn get_signature_status(
1117 &self,
1118 signature: &Signature,
1119 ) -> ClientResult<Option<transaction::Result<()>>> {
1120 self.get_signature_status_with_commitment(signature, self.commitment())
1121 }
1122
1123 /// Gets the statuses of a list of transaction signatures.
1124 ///
1125 /// The returned vector of [`TransactionStatus`] has the same length as the
1126 /// input slice.
1127 ///
1128 /// For any transaction that has not been processed by the network, the
1129 /// value of the corresponding entry in the returned vector is `None`. As a
1130 /// result, a transaction that has recently been submitted will not have a
1131 /// status immediately.
1132 ///
1133 /// To submit a transaction and wait for it to confirm, use
1134 /// [`send_and_confirm_transaction`][RpcClient::send_and_confirm_transaction].
1135 ///
1136 /// This function ignores the configured confirmation level, and returns the
1137 /// transaction status whatever it is. It does not wait for transactions to
1138 /// be processed.
1139 ///
1140 /// This function only searches a node's recent history, including all
1141 /// recent slots, plus up to
1142 /// [`MAX_RECENT_BLOCKHASHES`][gemachain_sdk::clock::MAX_RECENT_BLOCKHASHES]
1143 /// rooted slots. To search the full transaction history use the
1144 /// [`get_signature_statuses_with_history`][RpcClient::get_signature_statuses_with_history]
1145 /// method.
1146 ///
1147 /// # Errors
1148 ///
1149 /// Any individual `TransactionStatus` may have triggered an error during
1150 /// processing, in which case its [`err`][`TransactionStatus::err`] field
1151 /// will be `Some`.
1152 ///
1153 /// # RPC Reference
1154 ///
1155 /// This method corresponds directly to the [`getSignatureStatuses`] RPC method.
1156 ///
1157 /// [`getSignatureStatuses`]: https://docs.gemachain.com/developing/clients/jsonrpc-api#getsignaturestatuses
1158 ///
1159 /// # Examples
1160 ///
1161 /// ```
1162 /// # use gemachain_client::{
1163 /// # rpc_client::RpcClient,
1164 /// # client_error::ClientError,
1165 /// # };
1166 /// # use gemachain_sdk::{
1167 /// # signature::Signer,
1168 /// # signature::Signature,
1169 /// # signer::keypair::Keypair,
1170 /// # hash::Hash,
1171 /// # system_transaction,
1172 /// # };
1173 /// # use std::time::Duration;
1174 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
1175 /// # let alice = Keypair::new();
1176 /// // Send carats from Alice to Bob and wait for the transaction to be processed
1177 /// # let bob = Keypair::new();
1178 /// # let carats = 50;
1179 /// let latest_blockhash = rpc_client.get_latest_blockhash()?;
1180 /// let tx = system_transaction::transfer(&alice, &bob.pubkey(), carats, latest_blockhash);
1181 /// let signature = rpc_client.send_transaction(&tx)?;
1182 ///
1183 /// let status = loop {
1184 /// let statuses = rpc_client.get_signature_statuses(&[signature])?.value;
1185 /// if let Some(status) = statuses[0].clone() {
1186 /// break status;
1187 /// }
1188 /// std::thread::sleep(Duration::from_millis(100));
1189 /// };
1190 ///
1191 /// assert!(status.err.is_none());
1192 /// # Ok::<(), ClientError>(())
1193 /// ```
1194 pub fn get_signature_statuses(
1195 &self,
1196 signatures: &[Signature],
1197 ) -> RpcResult<Vec<Option<TransactionStatus>>> {
1198 let signatures: Vec<_> = signatures.iter().map(|s| s.to_string()).collect();
1199 self.send(RpcRequest::GetSignatureStatuses, json!([signatures]))
1200 }
1201
1202 /// Gets the statuses of a list of transaction signatures.
1203 ///
1204 /// The returned vector of [`TransactionStatus`] has the same length as the
1205 /// input slice.
1206 ///
1207 /// For any transaction that has not been processed by the network, the
1208 /// value of the corresponding entry in the returned vector is `None`. As a
1209 /// result, a transaction that has recently been submitted will not have a
1210 /// status immediately.
1211 ///
1212 /// To submit a transaction and wait for it to confirm, use
1213 /// [`send_and_confirm_transaction`][RpcClient::send_and_confirm_transaction].
1214 ///
1215 /// This function ignores the configured confirmation level, and returns the
1216 /// transaction status whatever it is. It does not wait for transactions to
1217 /// be processed.
1218 ///
1219 /// This function searches a node's full ledger history and (if implemented) long-term storage. To search for
1220 /// transactions in recent slots only use the
1221 /// [`get_signature_statuses`][RpcClient::get_signature_statuses] method.
1222 ///
1223 /// # Errors
1224 ///
1225 /// Any individual `TransactionStatus` may have triggered an error during
1226 /// processing, in which case its [`err`][`TransactionStatus::err`] field
1227 /// will be `Some`.
1228 ///
1229 /// # RPC Reference
1230 ///
1231 /// This method corresponds directly to the [`getSignatureStatuses`] RPC
1232 /// method, with the `searchTransactionHistory` configuration option set to
1233 /// `true`.
1234 ///
1235 /// [`getSignatureStatuses`]: https://docs.gemachain.com/developing/clients/jsonrpc-api#getsignaturestatuses
1236 ///
1237 /// # Examples
1238 ///
1239 /// ```
1240 /// # use gemachain_client::{
1241 /// # rpc_client::RpcClient,
1242 /// # client_error::ClientError,
1243 /// # };
1244 /// # use gemachain_sdk::{
1245 /// # signature::Signer,
1246 /// # signature::Signature,
1247 /// # signer::keypair::Keypair,
1248 /// # hash::Hash,
1249 /// # system_transaction,
1250 /// # };
1251 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
1252 /// # let alice = Keypair::new();
1253 /// # fn get_old_transaction_signature() -> Signature { Signature::default() }
1254 /// // Check if an old transaction exists
1255 /// let signature = get_old_transaction_signature();
1256 /// let latest_blockhash = rpc_client.get_latest_blockhash()?;
1257 /// let statuses = rpc_client.get_signature_statuses_with_history(&[signature])?.value;
1258 /// if statuses[0].is_none() {
1259 /// println!("old transaction does not exist");
1260 /// }
1261 /// # Ok::<(), ClientError>(())
1262 /// ```
1263 pub fn get_signature_statuses_with_history(
1264 &self,
1265 signatures: &[Signature],
1266 ) -> RpcResult<Vec<Option<TransactionStatus>>> {
1267 let signatures: Vec<_> = signatures.iter().map(|s| s.to_string()).collect();
1268 self.send(
1269 RpcRequest::GetSignatureStatuses,
1270 json!([signatures, {
1271 "searchTransactionHistory": true
1272 }]),
1273 )
1274 }
1275
1276 /// Check if a transaction has been processed with the given commitment level.
1277 ///
1278 /// If the transaction has been processed with the given commitment level,
1279 /// then this method returns `Ok` of `Some`. If the transaction has not yet
1280 /// been processed with the given commitment level, it returns `Ok` of
1281 /// `None`.
1282 ///
1283 /// If the transaction has been processed with the given commitment level,
1284 /// and the transaction succeeded, this method returns `Ok(Some(Ok(())))`.
1285 /// If the transaction has peen processed with the given commitment level,
1286 /// and the transaction failed, this method returns `Ok(Some(Err(_)))`,
1287 /// where the interior error is type [`TransactionError`].
1288 ///
1289 /// [`TransactionError`]: gemachain_sdk::transaction::TransactionError
1290 ///
1291 /// This function only searches a node's recent history, including all
1292 /// recent slots, plus up to
1293 /// [`MAX_RECENT_BLOCKHASHES`][gemachain_sdk::clock::MAX_RECENT_BLOCKHASHES]
1294 /// rooted slots. To search the full transaction history use the
1295 /// [`get_signature_statuse_with_commitment_and_history`][RpcClient::get_signature_status_with_commitment_and_history]
1296 /// method.
1297 ///
1298 /// # RPC Reference
1299 ///
1300 /// This method is built on the [`getSignatureStatuses`] RPC method.
1301 ///
1302 /// [`getSignatureStatuses`]: https://docs.gemachain.com/developing/clients/jsonrpc-api#getsignaturestatuses
1303 ///
1304 /// # Examples
1305 ///
1306 /// ```
1307 /// # use gemachain_client::{
1308 /// # rpc_client::RpcClient,
1309 /// # client_error::ClientError,
1310 /// # };
1311 /// # use gemachain_sdk::{
1312 /// # commitment_config::CommitmentConfig,
1313 /// # signature::Signer,
1314 /// # signature::Signature,
1315 /// # signer::keypair::Keypair,
1316 /// # system_transaction,
1317 /// # };
1318 /// # use std::time::Duration;
1319 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
1320 /// # let alice = Keypair::new();
1321 /// # let bob = Keypair::new();
1322 /// # let carats = 50;
1323 /// # let latest_blockhash = rpc_client.get_latest_blockhash()?;
1324 /// # let tx = system_transaction::transfer(&alice, &bob.pubkey(), carats, latest_blockhash);
1325 /// let signature = rpc_client.send_and_confirm_transaction(&tx)?;
1326 /// let commitment_config = CommitmentConfig::processed();
1327 /// let status = rpc_client.get_signature_status_with_commitment(
1328 /// &signature,
1329 /// commitment_config,
1330 /// )?;
1331 /// # Ok::<(), ClientError>(())
1332 /// ```
1333 pub fn get_signature_status_with_commitment(
1334 &self,
1335 signature: &Signature,
1336 commitment_config: CommitmentConfig,
1337 ) -> ClientResult<Option<transaction::Result<()>>> {
1338 let result: Response<Vec<Option<TransactionStatus>>> = self.send(
1339 RpcRequest::GetSignatureStatuses,
1340 json!([[signature.to_string()]]),
1341 )?;
1342 Ok(result.value[0]
1343 .clone()
1344 .filter(|result| result.satisfies_commitment(commitment_config))
1345 .map(|status_meta| status_meta.status))
1346 }
1347
1348 /// Check if a transaction has been processed with the given commitment level.
1349 ///
1350 /// If the transaction has been processed with the given commitment level,
1351 /// then this method returns `Ok` of `Some`. If the transaction has not yet
1352 /// been processed with the given commitment level, it returns `Ok` of
1353 /// `None`.
1354 ///
1355 /// If the transaction has been processed with the given commitment level,
1356 /// and the transaction succeeded, this method returns `Ok(Some(Ok(())))`.
1357 /// If the transaction has peen processed with the given commitment level,
1358 /// and the transaction failed, this method returns `Ok(Some(Err(_)))`,
1359 /// where the interior error is type [`TransactionError`].
1360 ///
1361 /// [`TransactionError`]: gemachain_sdk::transaction::TransactionError
1362 ///
1363 /// This method optionally searches a node's full ledger history and (if
1364 /// implemented) long-term storage.
1365 ///
1366 /// # RPC Reference
1367 ///
1368 /// This method is built on the [`getSignatureStatuses`] RPC method.
1369 ///
1370 /// [`getSignatureStatuses`]: https://docs.gemachain.com/developing/clients/jsonrpc-api#getsignaturestatuses
1371 ///
1372 /// # Examples
1373 ///
1374 /// ```
1375 /// # use gemachain_client::{
1376 /// # rpc_client::RpcClient,
1377 /// # client_error::ClientError,
1378 /// # };
1379 /// # use gemachain_sdk::{
1380 /// # commitment_config::CommitmentConfig,
1381 /// # signature::Signer,
1382 /// # signature::Signature,
1383 /// # signer::keypair::Keypair,
1384 /// # system_transaction,
1385 /// # };
1386 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
1387 /// # let alice = Keypair::new();
1388 /// # let bob = Keypair::new();
1389 /// # let carats = 50;
1390 /// # let latest_blockhash = rpc_client.get_latest_blockhash()?;
1391 /// # let tx = system_transaction::transfer(&alice, &bob.pubkey(), carats, latest_blockhash);
1392 /// let signature = rpc_client.send_transaction(&tx)?;
1393 /// let commitment_config = CommitmentConfig::processed();
1394 /// let search_transaction_history = true;
1395 /// let status = rpc_client.get_signature_status_with_commitment_and_history(
1396 /// &signature,
1397 /// commitment_config,
1398 /// search_transaction_history,
1399 /// )?;
1400 /// # Ok::<(), ClientError>(())
1401 /// ```
1402 pub fn get_signature_status_with_commitment_and_history(
1403 &self,
1404 signature: &Signature,
1405 commitment_config: CommitmentConfig,
1406 search_transaction_history: bool,
1407 ) -> ClientResult<Option<transaction::Result<()>>> {
1408 let result: Response<Vec<Option<TransactionStatus>>> = self.send(
1409 RpcRequest::GetSignatureStatuses,
1410 json!([[signature.to_string()], {
1411 "searchTransactionHistory": search_transaction_history
1412 }]),
1413 )?;
1414 Ok(result.value[0]
1415 .clone()
1416 .filter(|result| result.satisfies_commitment(commitment_config))
1417 .map(|status_meta| status_meta.status))
1418 }
1419
1420 /// # RPC Reference
1421 ///
1422 /// This method corresponds directly to the [`getSlot`] RPC method.
1423 ///
1424 /// [`getSlot`]: https://docs.gemachain.com/developing/clients/jsonrpc-api#getslot
1425 ///
1426 /// # Examples
1427 ///
1428 /// ```
1429 /// # use gemachain_client::{
1430 /// # rpc_client::RpcClient,
1431 /// # client_error::ClientError,
1432 /// # };
1433 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
1434 /// let slot = rpc_client.get_slot()?;
1435 /// # Ok::<(), ClientError>(())
1436 /// ```
1437 pub fn get_slot(&self) -> ClientResult<Slot> {
1438 self.get_slot_with_commitment(self.commitment())
1439 }
1440
1441 /// # RPC Reference
1442 ///
1443 /// This method is built on the [`getSlot`] RPC method.
1444 ///
1445 /// [`getSlot`]: https://docs.gemachain.com/developing/clients/jsonrpc-api#getslot
1446 ///
1447 /// # Examples
1448 ///
1449 /// ```
1450 /// # use gemachain_client::{
1451 /// # rpc_client::RpcClient,
1452 /// # client_error::ClientError,
1453 /// # };
1454 /// # use gemachain_sdk::commitment_config::CommitmentConfig;
1455 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
1456 /// let commitment_config = CommitmentConfig::processed();
1457 /// let slot = rpc_client.get_slot_with_commitment(commitment_config)?;
1458 /// # Ok::<(), ClientError>(())
1459 /// ```
1460 pub fn get_slot_with_commitment(
1461 &self,
1462 commitment_config: CommitmentConfig,
1463 ) -> ClientResult<Slot> {
1464 self.send(
1465 RpcRequest::GetSlot,
1466 json!([self.maybe_map_commitment(commitment_config)?]),
1467 )
1468 }
1469
1470 /// # RPC Reference
1471 ///
1472 /// This method is corresponds directly to the [`getBlockHeight`] RPC method.
1473 ///
1474 /// [`getBlockHeight`]: https://docs.gemachain.com/developing/clients/jsonrpc-api#getblockheight
1475 ///
1476 /// # Examples
1477 ///
1478 /// ```
1479 /// # use gemachain_client::{
1480 /// # rpc_client::RpcClient,
1481 /// # client_error::ClientError,
1482 /// # };
1483 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
1484 /// let block_height = rpc_client.get_block_height()?;
1485 /// # Ok::<(), ClientError>(())
1486 /// ```
1487 pub fn get_block_height(&self) -> ClientResult<u64> {
1488 self.get_block_height_with_commitment(self.commitment())
1489 }
1490
1491 /// # RPC Reference
1492 ///
1493 /// This method is built on the [`getBlockHeight`] RPC method.
1494 ///
1495 /// [`getBlockHeight`]: https://docs.gemachain.com/developing/clients/jsonrpc-api#getblockheight
1496 ///
1497 /// # Examples
1498 ///
1499 /// ```
1500 /// # use gemachain_client::{
1501 /// # rpc_client::RpcClient,
1502 /// # client_error::ClientError,
1503 /// # };
1504 /// # use gemachain_sdk::commitment_config::CommitmentConfig;
1505 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
1506 /// let commitment_config = CommitmentConfig::processed();
1507 /// let block_height = rpc_client.get_block_height_with_commitment(
1508 /// commitment_config,
1509 /// )?;
1510 /// # Ok::<(), ClientError>(())
1511 /// ```
1512 pub fn get_block_height_with_commitment(
1513 &self,
1514 commitment_config: CommitmentConfig,
1515 ) -> ClientResult<u64> {
1516 self.send(
1517 RpcRequest::GetBlockHeight,
1518 json!([self.maybe_map_commitment(commitment_config)?]),
1519 )
1520 }
1521
1522 /// # RPC Reference
1523 ///
1524 /// This method corresponds directly to the [`getSlotLeaders`] RPC method.
1525 ///
1526 /// [`getSlotLeaders`]: https://docs.gemachain.com/developing/clients/jsonrpc-api#getslotleaders
1527 ///
1528 /// # Examples
1529 ///
1530 /// ```
1531 /// # use gemachain_client::{
1532 /// # rpc_client::RpcClient,
1533 /// # client_error::ClientError,
1534 /// # };
1535 /// # use gemachain_sdk::slot_history::Slot;
1536 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
1537 /// let start_slot = 1;
1538 /// let limit = 3;
1539 /// let leaders = rpc_client.get_slot_leaders(start_slot, limit)?;
1540 /// # Ok::<(), ClientError>(())
1541 /// ```
1542 pub fn get_slot_leaders(&self, start_slot: Slot, limit: u64) -> ClientResult<Vec<Pubkey>> {
1543 self.send(RpcRequest::GetSlotLeaders, json!([start_slot, limit]))
1544 .and_then(|slot_leaders: Vec<String>| {
1545 slot_leaders
1546 .iter()
1547 .map(|slot_leader| {
1548 Pubkey::from_str(slot_leader).map_err(|err| {
1549 ClientErrorKind::Custom(format!(
1550 "pubkey deserialization failed: {}",
1551 err
1552 ))
1553 .into()
1554 })
1555 })
1556 .collect()
1557 })
1558 }
1559
1560 /// # RPC Reference
1561 ///
1562 /// This method corresponds directly to the [`getBlockProduction`] RPC method.
1563 ///
1564 /// [`getBlockProduction`]: https://docs.gemachain.com/developing/clients/jsonrpc-api#getblockproduction
1565 ///
1566 /// Get block production for the current epoch.
1567 ///
1568 /// # Examples
1569 ///
1570 /// ```
1571 /// # use gemachain_client::{
1572 /// # rpc_client::RpcClient,
1573 /// # client_error::ClientError,
1574 /// # };
1575 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
1576 /// let production = rpc_client.get_block_production()?;
1577 /// # Ok::<(), ClientError>(())
1578 /// ```
1579 pub fn get_block_production(&self) -> RpcResult<RpcBlockProduction> {
1580 self.send(RpcRequest::GetBlockProduction, Value::Null)
1581 }
1582
1583 /// # RPC Reference
1584 ///
1585 /// This method is built on the [`getBlockProduction`] RPC method.
1586 ///
1587 /// [`getBlockProduction`]: https://docs.gemachain.com/developing/clients/jsonrpc-api#getblockproduction
1588 ///
1589 /// # Examples
1590 ///
1591 /// ```
1592 /// # use gemachain_client::{
1593 /// # rpc_client::RpcClient,
1594 /// # client_error::ClientError,
1595 /// # rpc_config::RpcBlockProductionConfig,
1596 /// # rpc_config::RpcBlockProductionConfigRange,
1597 /// # };
1598 /// # use gemachain_sdk::{
1599 /// # signature::Signer,
1600 /// # signer::keypair::Keypair,
1601 /// # commitment_config::CommitmentConfig,
1602 /// # };
1603 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
1604 /// # let start_slot = 1;
1605 /// # let limit = 3;
1606 /// let leader = rpc_client.get_slot_leaders(start_slot, limit)?;
1607 /// let leader = leader[0];
1608 /// let range = RpcBlockProductionConfigRange {
1609 /// first_slot: 0,
1610 /// last_slot: Some(0),
1611 /// };
1612 /// let config = RpcBlockProductionConfig {
1613 /// identity: Some(leader.to_string()),
1614 /// range: Some(range),
1615 /// commitment: Some(CommitmentConfig::processed()),
1616 /// };
1617 /// let production = rpc_client.get_block_production_with_config(
1618 /// config
1619 /// )?;
1620 /// # Ok::<(), ClientError>(())
1621 /// ```
1622 pub fn get_block_production_with_config(
1623 &self,
1624 config: RpcBlockProductionConfig,
1625 ) -> RpcResult<RpcBlockProduction> {
1626 self.send(RpcRequest::GetBlockProduction, json!([config]))
1627 }
1628
1629 /// # RPC Reference
1630 ///
1631 /// This method corresponds directly to the [`getStakeActivation`] RPC method.
1632 ///
1633 /// [`getStakeActivation`]: https://docs.gemachain.com/developing/clients/jsonrpc-api#getstakeactivation
1634 ///
1635 /// # Examples
1636 ///
1637 /// ```
1638 /// # use gemachain_client::{
1639 /// # rpc_client::RpcClient,
1640 /// # client_error::ClientError,
1641 /// # rpc_response::StakeActivationState,
1642 /// # };
1643 /// # use gemachain_sdk::{
1644 /// # signer::keypair::Keypair,
1645 /// # signature::Signer,
1646 /// # pubkey::Pubkey,
1647 /// # stake,
1648 /// # stake::state::{Authorized, Lockup},
1649 /// # transaction::Transaction
1650 /// # };
1651 /// # use std::str::FromStr;
1652 /// # let alice = Keypair::new();
1653 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
1654 /// // Find some vote account to delegate to
1655 /// let vote_accounts = rpc_client.get_vote_accounts()?;
1656 /// let vote_account = vote_accounts.current.get(0).unwrap_or_else(|| &vote_accounts.delinquent[0]);
1657 /// let vote_account_pubkey = &vote_account.vote_pubkey;
1658 /// let vote_account_pubkey = Pubkey::from_str(vote_account_pubkey).expect("pubkey");
1659 ///
1660 /// // Create a stake account
1661 /// let stake_account = Keypair::new();
1662 /// let stake_account_pubkey = stake_account.pubkey();
1663 ///
1664 /// // Build the instructions to create new stake account,
1665 /// // funded by alice, and delegate to a validator's vote account.
1666 /// let instrs = stake::instruction::create_account_and_delegate_stake(
1667 /// &alice.pubkey(),
1668 /// &stake_account_pubkey,
1669 /// &vote_account_pubkey,
1670 /// &Authorized::auto(&stake_account_pubkey),
1671 /// &Lockup::default(),
1672 /// 1_000_000,
1673 /// );
1674 ///
1675 /// let latest_blockhash = rpc_client.get_latest_blockhash()?;
1676 /// let tx = Transaction::new_signed_with_payer(
1677 /// &instrs,
1678 /// Some(&alice.pubkey()),
1679 /// &[&alice, &stake_account],
1680 /// latest_blockhash,
1681 /// );
1682 ///
1683 /// rpc_client.send_and_confirm_transaction(&tx)?;
1684 ///
1685 /// let epoch_info = rpc_client.get_epoch_info()?;
1686 /// let activation = rpc_client.get_stake_activation(
1687 /// stake_account_pubkey,
1688 /// Some(epoch_info.epoch),
1689 /// )?;
1690 ///
1691 /// assert_eq!(activation.state, StakeActivationState::Activating);
1692 /// # Ok::<(), ClientError>(())
1693 /// ```
1694 pub fn get_stake_activation(
1695 &self,
1696 stake_account: Pubkey,
1697 epoch: Option<Epoch>,
1698 ) -> ClientResult<RpcStakeActivation> {
1699 self.send(
1700 RpcRequest::GetStakeActivation,
1701 json!([
1702 stake_account.to_string(),
1703 RpcEpochConfig {
1704 epoch,
1705 commitment: Some(self.commitment()),
1706 }
1707 ]),
1708 )
1709 }
1710
1711 /// # RPC Reference
1712 ///
1713 /// This method corresponds directly to the [`getSupply`] RPC method.
1714 ///
1715 /// [`getSupply`]: https://docs.gemachain.com/developing/clients/jsonrpc-api#getsupply
1716 ///
1717 /// # Examples
1718 ///
1719 /// ```
1720 /// # use gemachain_client::{
1721 /// # rpc_client::RpcClient,
1722 /// # client_error::ClientError,
1723 /// # };
1724 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
1725 /// let supply = rpc_client.supply()?;
1726 /// # Ok::<(), ClientError>(())
1727 /// ```
1728 pub fn supply(&self) -> RpcResult<RpcSupply> {
1729 self.supply_with_commitment(self.commitment())
1730 }
1731
1732 /// # RPC Reference
1733 ///
1734 /// This method corresponds directly to the [`getSupply`] RPC method.
1735 ///
1736 /// [`getSupply`]: https://docs.gemachain.com/developing/clients/jsonrpc-api#getsupply
1737 ///
1738 /// # Examples
1739 ///
1740 /// ```
1741 /// # use gemachain_client::{
1742 /// # rpc_client::RpcClient,
1743 /// # client_error::ClientError,
1744 /// # };
1745 /// # use gemachain_sdk::commitment_config::CommitmentConfig;
1746 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
1747 /// let commitment_config = CommitmentConfig::processed();
1748 /// let supply = rpc_client.supply_with_commitment(
1749 /// commitment_config,
1750 /// )?;
1751 /// # Ok::<(), ClientError>(())
1752 /// ```
1753 pub fn supply_with_commitment(
1754 &self,
1755 commitment_config: CommitmentConfig,
1756 ) -> RpcResult<RpcSupply> {
1757 self.send(
1758 RpcRequest::GetSupply,
1759 json!([self.maybe_map_commitment(commitment_config)?]),
1760 )
1761 }
1762
1763 /// # RPC Reference
1764 ///
1765 /// This method corresponds directly to the [`getLargestAccounts`] RPC
1766 /// method.
1767 ///
1768 /// [`getLargestAccounts`]: https://docs.gemachain.com/developing/clients/jsonrpc-api#getlargestaccounts
1769 ///
1770 /// # Examples
1771 ///
1772 /// ```
1773 /// # use gemachain_client::{
1774 /// # rpc_client::RpcClient,
1775 /// # client_error::ClientError,
1776 /// # rpc_config::RpcLargestAccountsConfig,
1777 /// # rpc_config::RpcLargestAccountsFilter,
1778 /// # };
1779 /// # use gemachain_sdk::commitment_config::CommitmentConfig;
1780 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
1781 /// let commitment_config = CommitmentConfig::processed();
1782 /// let config = RpcLargestAccountsConfig {
1783 /// commitment: Some(commitment_config),
1784 /// filter: Some(RpcLargestAccountsFilter::Circulating),
1785 /// };
1786 /// let accounts = rpc_client.get_largest_accounts_with_config(
1787 /// config,
1788 /// )?;
1789 /// # Ok::<(), ClientError>(())
1790 /// ```
1791 pub fn get_largest_accounts_with_config(
1792 &self,
1793 config: RpcLargestAccountsConfig,
1794 ) -> RpcResult<Vec<RpcAccountBalance>> {
1795 let commitment = config.commitment.unwrap_or_default();
1796 let commitment = self.maybe_map_commitment(commitment)?;
1797 let config = RpcLargestAccountsConfig {
1798 commitment: Some(commitment),
1799 ..config
1800 };
1801 self.send(RpcRequest::GetLargestAccounts, json!([config]))
1802 }
1803
1804 /// # RPC Reference
1805 ///
1806 /// This method corresponds directly to the [`getVoteAccounts`]
1807 /// RPC method.
1808 ///
1809 /// [`getVoteAccounts`]: https://docs.gemachain.com/developing/clients/jsonrpc-api#getvoteaccounts
1810 ///
1811 /// # Examples
1812 ///
1813 /// ```
1814 /// # use gemachain_client::{
1815 /// # rpc_client::RpcClient,
1816 /// # client_error::ClientError,
1817 /// # };
1818 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
1819 /// let accounts = rpc_client.get_vote_accounts()?;
1820 /// # Ok::<(), ClientError>(())
1821 /// ```
1822 pub fn get_vote_accounts(&self) -> ClientResult<RpcVoteAccountStatus> {
1823 self.get_vote_accounts_with_commitment(self.commitment())
1824 }
1825
1826 /// # RPC Reference
1827 ///
1828 /// This method corresponds directly to the [`getVoteAccounts`] RPC method.
1829 ///
1830 /// [`getVoteAccounts`]: https://docs.gemachain.com/developing/clients/jsonrpc-api#getvoteaccounts
1831 ///
1832 /// # Examples
1833 ///
1834 /// ```
1835 /// # use gemachain_sdk::commitment_config::CommitmentConfig;
1836 /// # use gemachain_client::{
1837 /// # rpc_client::RpcClient,
1838 /// # client_error::ClientError,
1839 /// # };
1840 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
1841 /// let commitment_config = CommitmentConfig::processed();
1842 /// let accounts = rpc_client.get_vote_accounts_with_commitment(
1843 /// commitment_config,
1844 /// )?;
1845 /// # Ok::<(), ClientError>(())
1846 /// ```
1847 pub fn get_vote_accounts_with_commitment(
1848 &self,
1849 commitment_config: CommitmentConfig,
1850 ) -> ClientResult<RpcVoteAccountStatus> {
1851 self.get_vote_accounts_with_config(RpcGetVoteAccountsConfig {
1852 commitment: Some(self.maybe_map_commitment(commitment_config)?),
1853 ..RpcGetVoteAccountsConfig::default()
1854 })
1855 }
1856
1857 /// # RPC Reference
1858 ///
1859 /// This method corresponds directly to the [`getVoteAccounts`]
1860 /// RPC method with the `Commitment` option set to `processed`, the
1861 /// `votePubkey` option set to new-generated `vote_pubkey`, the
1862 /// `keepUnstakedDelinquents` option set to `true`, the
1863 /// `delinquentSlotDistance` option set to `10`
1864 ///
1865 /// [`getVoteAccounts`]: https://docs.gemachain.com/developing/clients/jsonrpc-api#getvoteaccounts
1866 ///
1867 /// # Examples
1868 ///
1869 /// ```
1870 /// # use gemachain_client::{
1871 /// # rpc_client::RpcClient,
1872 /// # client_error::ClientError,
1873 /// # rpc_config::RpcGetVoteAccountsConfig,
1874 /// # };
1875 /// # use gemachain_sdk::{
1876 /// # signer::keypair::Keypair,
1877 /// # signature::Signer,
1878 /// # commitment_config::CommitmentConfig,
1879 /// # };
1880 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
1881 /// # let vote_keypair = Keypair::new();
1882 /// let vote_pubkey = vote_keypair.pubkey();
1883 /// let commitment = CommitmentConfig::processed();
1884 /// let config = RpcGetVoteAccountsConfig {
1885 /// vote_pubkey: Some(vote_pubkey.to_string()),
1886 /// commitment: Some(commitment),
1887 /// keep_unstaked_delinquents: Some(true),
1888 /// delinquent_slot_distance: Some(10),
1889 /// };
1890 /// let accounts = rpc_client.get_vote_accounts_with_config(
1891 /// config,
1892 /// )?;
1893 /// # Ok::<(), ClientError>(())
1894 /// ```
1895 pub fn get_vote_accounts_with_config(
1896 &self,
1897 config: RpcGetVoteAccountsConfig,
1898 ) -> ClientResult<RpcVoteAccountStatus> {
1899 self.send(RpcRequest::GetVoteAccounts, json!([config]))
1900 }
1901
1902 pub fn wait_for_max_stake(
1903 &self,
1904 commitment: CommitmentConfig,
1905 max_stake_percent: f32,
1906 ) -> ClientResult<()> {
1907 let mut current_percent;
1908 loop {
1909 let vote_accounts = self.get_vote_accounts_with_commitment(commitment)?;
1910
1911 let mut max = 0;
1912 let total_active_stake = vote_accounts
1913 .current
1914 .iter()
1915 .chain(vote_accounts.delinquent.iter())
1916 .map(|vote_account| {
1917 max = std::cmp::max(max, vote_account.activated_stake);
1918 vote_account.activated_stake
1919 })
1920 .sum::<u64>();
1921 current_percent = 100f32 * max as f32 / total_active_stake as f32;
1922 if current_percent < max_stake_percent {
1923 break;
1924 }
1925 info!(
1926 "Waiting for stake to drop below {} current: {:.1}",
1927 max_stake_percent, current_percent
1928 );
1929 sleep(Duration::from_secs(10));
1930 }
1931 Ok(())
1932 }
1933
1934 /// # RPC Reference
1935 ///
1936 /// This method corresponds directly to the [`getClusterNodes`]
1937 /// RPC method.
1938 ///
1939 /// [`getClusterNodes`]: https://docs.gemachain.com/developing/clients/jsonrpc-api#getclusternodes
1940 ///
1941 /// # Examples
1942 ///
1943 /// ```
1944 /// # use gemachain_client::{
1945 /// # rpc_client::RpcClient,
1946 /// # client_error::ClientError,
1947 /// # };
1948 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
1949 /// let cluster_nodes = rpc_client.get_cluster_nodes()?;
1950 /// # Ok::<(), ClientError>(())
1951 /// ```
1952 pub fn get_cluster_nodes(&self) -> ClientResult<Vec<RpcContactInfo>> {
1953 self.send(RpcRequest::GetClusterNodes, Value::Null)
1954 }
1955
1956 /// # RPC Reference
1957 ///
1958 /// This method corresponds directly to the [`getBlock`] RPC
1959 /// method.
1960 ///
1961 /// [`getBlock`]: https://docs.gemachain.com/developing/clients/jsonrpc-api#getblock
1962 ///
1963 /// # Examples
1964 ///
1965 /// ```
1966 /// # use gemachain_client::{
1967 /// # rpc_client::RpcClient,
1968 /// # client_error::ClientError,
1969 /// # };
1970 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
1971 /// # let slot = rpc_client.get_slot()?;
1972 /// let block = rpc_client.get_block(slot)?;
1973 /// # Ok::<(), ClientError>(())
1974 /// ```
1975 pub fn get_block(&self, slot: Slot) -> ClientResult<EncodedConfirmedBlock> {
1976 self.get_block_with_encoding(slot, UiTransactionEncoding::Json)
1977 }
1978
1979 /// # RPC Reference
1980 ///
1981 /// This method is built on the [`getBlock`] RPC method.
1982 ///
1983 /// [`getBlock`]: https://docs.gemachain.com/developing/clients/jsonrpc-api#getblock
1984 ///
1985 /// # Examples
1986 ///
1987 /// ```
1988 /// # use gemachain_transaction_status::UiTransactionEncoding;
1989 /// # use gemachain_client::{
1990 /// # rpc_client::RpcClient,
1991 /// # client_error::ClientError,
1992 /// # };
1993 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
1994 /// # let slot = rpc_client.get_slot()?;
1995 /// let encoding = UiTransactionEncoding::Base58;
1996 /// let block = rpc_client.get_block_with_encoding(
1997 /// slot,
1998 /// encoding,
1999 /// )?;
2000 /// # Ok::<(), ClientError>(())
2001 /// ```
2002 pub fn get_block_with_encoding(
2003 &self,
2004 slot: Slot,
2005 encoding: UiTransactionEncoding,
2006 ) -> ClientResult<EncodedConfirmedBlock> {
2007 self.send(
2008 self.maybe_map_request(RpcRequest::GetBlock)?,
2009 json!([slot, encoding]),
2010 )
2011 }
2012
2013 /// # RPC Reference
2014 ///
2015 /// This method is built on the [`getBlock`] RPC method.
2016 ///
2017 /// [`getBlock`]: https://docs.gemachain.com/developing/clients/jsonrpc-api#getblock
2018 ///
2019 /// # Examples
2020 ///
2021 /// ```
2022 /// # use gemachain_transaction_status::{
2023 /// # TransactionDetails,
2024 /// # UiTransactionEncoding,
2025 /// # };
2026 /// # use gemachain_client::{
2027 /// # rpc_client::RpcClient,
2028 /// # rpc_config::RpcBlockConfig,
2029 /// # client_error::ClientError,
2030 /// # };
2031 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
2032 /// # let slot = rpc_client.get_slot()?;
2033 /// let config = RpcBlockConfig {
2034 /// encoding: Some(UiTransactionEncoding::Base58),
2035 /// transaction_details: Some(TransactionDetails::None),
2036 /// rewards: Some(true),
2037 /// commitment: None,
2038 /// };
2039 /// let block = rpc_client.get_block_with_config(
2040 /// slot,
2041 /// config,
2042 /// )?;
2043 /// # Ok::<(), ClientError>(())
2044 /// ```
2045 pub fn get_block_with_config(
2046 &self,
2047 slot: Slot,
2048 config: RpcBlockConfig,
2049 ) -> ClientResult<UiConfirmedBlock> {
2050 self.send(
2051 self.maybe_map_request(RpcRequest::GetBlock)?,
2052 json!([slot, config]),
2053 )
2054 }
2055
2056 #[deprecated(since = "1.7.0", note = "Please use RpcClient::get_block() instead")]
2057 #[allow(deprecated)]
2058 pub fn get_confirmed_block(&self, slot: Slot) -> ClientResult<EncodedConfirmedBlock> {
2059 self.get_confirmed_block_with_encoding(slot, UiTransactionEncoding::Json)
2060 }
2061
2062 #[deprecated(
2063 since = "1.7.0",
2064 note = "Please use RpcClient::get_block_with_encoding() instead"
2065 )]
2066 #[allow(deprecated)]
2067 pub fn get_confirmed_block_with_encoding(
2068 &self,
2069 slot: Slot,
2070 encoding: UiTransactionEncoding,
2071 ) -> ClientResult<EncodedConfirmedBlock> {
2072 self.send(RpcRequest::GetConfirmedBlock, json!([slot, encoding]))
2073 }
2074
2075 #[deprecated(
2076 since = "1.7.0",
2077 note = "Please use RpcClient::get_block_with_config() instead"
2078 )]
2079 #[allow(deprecated)]
2080 pub fn get_confirmed_block_with_config(
2081 &self,
2082 slot: Slot,
2083 config: RpcConfirmedBlockConfig,
2084 ) -> ClientResult<UiConfirmedBlock> {
2085 self.send(RpcRequest::GetConfirmedBlock, json!([slot, config]))
2086 }
2087
2088 /// # RPC Reference
2089 ///
2090 /// This method corresponds directly to the [`getBlocks`] RPC
2091 /// method.
2092 ///
2093 /// [`getBlocks`]: https://docs.gemachain.com/developing/clients/jsonrpc-api#getblocks
2094 ///
2095 /// # Examples
2096 ///
2097 /// ```
2098 /// # use gemachain_client::{
2099 /// # rpc_client::RpcClient,
2100 /// # client_error::ClientError,
2101 /// # };
2102 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
2103 /// # let start_slot = 0;
2104 /// # let end_slot = 3;
2105 /// let blocks = rpc_client.get_blocks(start_slot, Some(end_slot))?;
2106 /// # Ok::<(), ClientError>(())
2107 /// ```
2108 pub fn get_blocks(&self, start_slot: Slot, end_slot: Option<Slot>) -> ClientResult<Vec<Slot>> {
2109 self.send(
2110 self.maybe_map_request(RpcRequest::GetBlocks)?,
2111 json!([start_slot, end_slot]),
2112 )
2113 }
2114
2115 /// # RPC Reference
2116 ///
2117 /// This method is built on the [`getBlocks`] RPC method.
2118 ///
2119 /// [`getBlocks`]: https://docs.gemachain.com/developing/clients/jsonrpc-api#getblocks
2120 ///
2121 /// # Examples
2122 ///
2123 /// ```
2124 /// # use gemachain_sdk::commitment_config::CommitmentConfig;
2125 /// # use gemachain_client::{
2126 /// # rpc_client::RpcClient,
2127 /// # client_error::ClientError,
2128 /// # };
2129 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
2130 /// # let start_slot = 2;
2131 /// # let end_slot = 4;
2132 /// // Method does not support commitment below `confirmed`
2133 /// let commitment_config = CommitmentConfig::confirmed();
2134 /// let blocks = rpc_client.get_blocks_with_commitment(
2135 /// start_slot,
2136 /// Some(end_slot),
2137 /// commitment_config,
2138 /// )?;
2139 /// # Ok::<(), ClientError>(())
2140 /// ```
2141 pub fn get_blocks_with_commitment(
2142 &self,
2143 start_slot: Slot,
2144 end_slot: Option<Slot>,
2145 commitment_config: CommitmentConfig,
2146 ) -> ClientResult<Vec<Slot>> {
2147 let json = if end_slot.is_some() {
2148 json!([
2149 start_slot,
2150 end_slot,
2151 self.maybe_map_commitment(commitment_config)?
2152 ])
2153 } else {
2154 json!([start_slot, self.maybe_map_commitment(commitment_config)?])
2155 };
2156 self.send(self.maybe_map_request(RpcRequest::GetBlocks)?, json)
2157 }
2158
2159 /// # RPC Reference
2160 ///
2161 /// This method corresponds directly to the [`getBlocksWithLimit`]
2162 /// RPC method.
2163 ///
2164 /// [`getBlocksWithLimit`]: https://docs.gemachain.com/developing/clients/jsonrpc-api#getblockswithlimit
2165 ///
2166 /// # Examples
2167 ///
2168 /// ```
2169 /// # use gemachain_client::{
2170 /// # rpc_client::RpcClient,
2171 /// # client_error::ClientError,
2172 /// # };
2173 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
2174 /// # let start_slot = 2;
2175 /// let limit = 3;
2176 /// let blocks = rpc_client.get_blocks_with_limit(start_slot, limit)?;
2177 /// # Ok::<(), ClientError>(())
2178 /// ```
2179 pub fn get_blocks_with_limit(&self, start_slot: Slot, limit: usize) -> ClientResult<Vec<Slot>> {
2180 self.send(
2181 self.maybe_map_request(RpcRequest::GetBlocksWithLimit)?,
2182 json!([start_slot, limit]),
2183 )
2184 }
2185
2186 /// # RPC Reference
2187 ///
2188 /// This method is built on the [`getBlocksWithLimit`] RPC method.
2189 ///
2190 /// [`getBlocksWithLimit`]: https://docs.gemachain.com/developing/clients/jsonrpc-api#getblockswithlimit
2191 ///
2192 /// # Examples
2193 ///
2194 /// ```
2195 /// # use gemachain_sdk::commitment_config::CommitmentConfig;
2196 /// # use gemachain_client::{
2197 /// # rpc_client::RpcClient,
2198 /// # client_error::ClientError,
2199 /// # };
2200 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
2201 /// # let start_slot = 2;
2202 /// let limit = 3;
2203 /// let commitment_config = CommitmentConfig::confirmed();
2204 /// let blocks = rpc_client.get_blocks_with_limit_and_commitment(
2205 /// start_slot,
2206 /// limit,
2207 /// commitment_config,
2208 /// )?;
2209 /// # Ok::<(), ClientError>(())
2210 /// ```
2211 pub fn get_blocks_with_limit_and_commitment(
2212 &self,
2213 start_slot: Slot,
2214 limit: usize,
2215 commitment_config: CommitmentConfig,
2216 ) -> ClientResult<Vec<Slot>> {
2217 self.send(
2218 self.maybe_map_request(RpcRequest::GetBlocksWithLimit)?,
2219 json!([
2220 start_slot,
2221 limit,
2222 self.maybe_map_commitment(commitment_config)?
2223 ]),
2224 )
2225 }
2226
2227 #[deprecated(since = "1.7.0", note = "Please use RpcClient::get_blocks() instead")]
2228 #[allow(deprecated)]
2229 pub fn get_confirmed_blocks(
2230 &self,
2231 start_slot: Slot,
2232 end_slot: Option<Slot>,
2233 ) -> ClientResult<Vec<Slot>> {
2234 self.send(
2235 RpcRequest::GetConfirmedBlocks,
2236 json!([start_slot, end_slot]),
2237 )
2238 }
2239
2240 #[deprecated(
2241 since = "1.7.0",
2242 note = "Please use RpcClient::get_blocks_with_commitment() instead"
2243 )]
2244 #[allow(deprecated)]
2245 pub fn get_confirmed_blocks_with_commitment(
2246 &self,
2247 start_slot: Slot,
2248 end_slot: Option<Slot>,
2249 commitment_config: CommitmentConfig,
2250 ) -> ClientResult<Vec<Slot>> {
2251 let json = if end_slot.is_some() {
2252 json!([
2253 start_slot,
2254 end_slot,
2255 self.maybe_map_commitment(commitment_config)?
2256 ])
2257 } else {
2258 json!([start_slot, self.maybe_map_commitment(commitment_config)?])
2259 };
2260 self.send(RpcRequest::GetConfirmedBlocks, json)
2261 }
2262
2263 #[deprecated(
2264 since = "1.7.0",
2265 note = "Please use RpcClient::get_blocks_with_limit() instead"
2266 )]
2267 #[allow(deprecated)]
2268 pub fn get_confirmed_blocks_with_limit(
2269 &self,
2270 start_slot: Slot,
2271 limit: usize,
2272 ) -> ClientResult<Vec<Slot>> {
2273 self.send(
2274 RpcRequest::GetConfirmedBlocksWithLimit,
2275 json!([start_slot, limit]),
2276 )
2277 }
2278
2279 #[deprecated(
2280 since = "1.7.0",
2281 note = "Please use RpcClient::get_blocks_with_limit_and_commitment() instead"
2282 )]
2283 #[allow(deprecated)]
2284 pub fn get_confirmed_blocks_with_limit_and_commitment(
2285 &self,
2286 start_slot: Slot,
2287 limit: usize,
2288 commitment_config: CommitmentConfig,
2289 ) -> ClientResult<Vec<Slot>> {
2290 self.send(
2291 RpcRequest::GetConfirmedBlocksWithLimit,
2292 json!([
2293 start_slot,
2294 limit,
2295 self.maybe_map_commitment(commitment_config)?
2296 ]),
2297 )
2298 }
2299
2300 /// # RPC Reference
2301 ///
2302 /// This method corresponds directly to the
2303 /// [`getSignaturesForAddress`] RPC method.
2304 ///
2305 /// [`getSignaturesForAddress`]: https://docs.gemachain.com/developing/clients/jsonrpc-api#getsignaturesforaddress
2306 ///
2307 /// # Examples
2308 ///
2309 /// ```
2310 /// # use gemachain_client::{
2311 /// # client_error::ClientError,
2312 /// # rpc_client::RpcClient,
2313 /// # };
2314 /// # use gemachain_sdk::{
2315 /// # signature::Signer,
2316 /// # signer::keypair::Keypair,
2317 /// # system_transaction,
2318 /// # };
2319 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
2320 /// # let alice = Keypair::new();
2321 /// let signatures = rpc_client.get_signatures_for_address(
2322 /// &alice.pubkey(),
2323 /// )?;
2324 /// # Ok::<(), ClientError>(())
2325 /// ```
2326 pub fn get_signatures_for_address(
2327 &self,
2328 address: &Pubkey,
2329 ) -> ClientResult<Vec<RpcConfirmedTransactionStatusWithSignature>> {
2330 self.get_signatures_for_address_with_config(
2331 address,
2332 GetConfirmedSignaturesForAddress2Config::default(),
2333 )
2334 }
2335
2336 /// # RPC Reference
2337 ///
2338 /// This method is built on the [`getSignaturesForAddress`] RPC
2339 /// method.
2340 ///
2341 /// [`getSignaturesForAddress`]: https://docs.gemachain.com/developing/clients/jsonrpc-api#getsignaturesforaddress
2342 ///
2343 /// # Examples
2344 ///
2345 /// ```
2346 /// # use gemachain_client::{
2347 /// # client_error::ClientError,
2348 /// # rpc_client::RpcClient,
2349 /// # rpc_client::GetConfirmedSignaturesForAddress2Config,
2350 /// # };
2351 /// # use gemachain_sdk::{
2352 /// # signature::Signer,
2353 /// # signer::keypair::Keypair,
2354 /// # system_transaction,
2355 /// # commitment_config::CommitmentConfig,
2356 /// # };
2357 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
2358 /// # let alice = Keypair::new();
2359 /// # let bob = Keypair::new();
2360 /// # let carats = 50;
2361 /// # let latest_blockhash = rpc_client.get_latest_blockhash()?;
2362 /// # let tx = system_transaction::transfer(&alice, &bob.pubkey(), carats, latest_blockhash);
2363 /// # let signature = rpc_client.send_and_confirm_transaction(&tx)?;
2364 /// let config = GetConfirmedSignaturesForAddress2Config {
2365 /// before: None,
2366 /// until: None,
2367 /// limit: Some(3),
2368 /// commitment: Some(CommitmentConfig::confirmed()),
2369 /// };
2370 /// let signatures = rpc_client.get_signatures_for_address_with_config(
2371 /// &alice.pubkey(),
2372 /// config,
2373 /// )?;
2374 /// # Ok::<(), ClientError>(())
2375 /// ```
2376 pub fn get_signatures_for_address_with_config(
2377 &self,
2378 address: &Pubkey,
2379 config: GetConfirmedSignaturesForAddress2Config,
2380 ) -> ClientResult<Vec<RpcConfirmedTransactionStatusWithSignature>> {
2381 let config = RpcSignaturesForAddressConfig {
2382 before: config.before.map(|signature| signature.to_string()),
2383 until: config.until.map(|signature| signature.to_string()),
2384 limit: config.limit,
2385 commitment: config.commitment,
2386 };
2387
2388 let result: Vec<RpcConfirmedTransactionStatusWithSignature> = self.send(
2389 self.maybe_map_request(RpcRequest::GetSignaturesForAddress)?,
2390 json!([address.to_string(), config]),
2391 )?;
2392
2393 Ok(result)
2394 }
2395
2396 #[deprecated(
2397 since = "1.7.0",
2398 note = "Please use RpcClient::get_signatures_for_address() instead"
2399 )]
2400 #[allow(deprecated)]
2401 pub fn get_confirmed_signatures_for_address2(
2402 &self,
2403 address: &Pubkey,
2404 ) -> ClientResult<Vec<RpcConfirmedTransactionStatusWithSignature>> {
2405 self.get_confirmed_signatures_for_address2_with_config(
2406 address,
2407 GetConfirmedSignaturesForAddress2Config::default(),
2408 )
2409 }
2410
2411 #[deprecated(
2412 since = "1.7.0",
2413 note = "Please use RpcClient::get_signatures_for_address_with_config() instead"
2414 )]
2415 #[allow(deprecated)]
2416 pub fn get_confirmed_signatures_for_address2_with_config(
2417 &self,
2418 address: &Pubkey,
2419 config: GetConfirmedSignaturesForAddress2Config,
2420 ) -> ClientResult<Vec<RpcConfirmedTransactionStatusWithSignature>> {
2421 let config = RpcGetConfirmedSignaturesForAddress2Config {
2422 before: config.before.map(|signature| signature.to_string()),
2423 until: config.until.map(|signature| signature.to_string()),
2424 limit: config.limit,
2425 commitment: config.commitment,
2426 };
2427
2428 let result: Vec<RpcConfirmedTransactionStatusWithSignature> = self.send(
2429 RpcRequest::GetConfirmedSignaturesForAddress2,
2430 json!([address.to_string(), config]),
2431 )?;
2432
2433 Ok(result)
2434 }
2435
2436 /// # RPC Reference
2437 ///
2438 /// This method corresponds directly to the [`getTransaction`] RPC
2439 /// method.
2440 ///
2441 /// [`getTransaction`]: https://docs.gemachain.com/developing/clients/jsonrpc-api#gettransaction
2442 ///
2443 /// # Examples
2444 ///
2445 /// ```
2446 /// # use gemachain_client::{
2447 /// # client_error::ClientError,
2448 /// # rpc_client::RpcClient,
2449 /// # };
2450 /// # use gemachain_sdk::{
2451 /// # signature::Signer,
2452 /// # signature::Signature,
2453 /// # signer::keypair::Keypair,
2454 /// # system_transaction,
2455 /// # };
2456 /// # use gemachain_transaction_status::UiTransactionEncoding;
2457 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
2458 /// # let alice = Keypair::new();
2459 /// # let bob = Keypair::new();
2460 /// # let carats = 50;
2461 /// # let latest_blockhash = rpc_client.get_latest_blockhash()?;
2462 /// # let tx = system_transaction::transfer(&alice, &bob.pubkey(), carats, latest_blockhash);
2463 /// let signature = rpc_client.send_and_confirm_transaction(&tx)?;
2464 ///
2465 /// let transaction = rpc_client.get_transaction(
2466 /// &signature,
2467 /// UiTransactionEncoding::Json,
2468 /// )?;
2469 /// # Ok::<(), ClientError>(())
2470 /// ```
2471 pub fn get_transaction(
2472 &self,
2473 signature: &Signature,
2474 encoding: UiTransactionEncoding,
2475 ) -> ClientResult<EncodedConfirmedTransaction> {
2476 self.send(
2477 self.maybe_map_request(RpcRequest::GetTransaction)?,
2478 json!([signature.to_string(), encoding]),
2479 )
2480 }
2481
2482 /// # RPC Reference
2483 ///
2484 /// This method is built on the [`getTransaction`] RPC method.
2485 ///
2486 /// [`getTransaction`]: https://docs.gemachain.com/developing/clients/jsonrpc-api#gettransaction
2487 ///
2488 /// # Examples
2489 ///
2490 /// ```
2491 /// # use gemachain_client::{
2492 /// # client_error::ClientError,
2493 /// # rpc_client::RpcClient,
2494 /// # rpc_config::RpcTransactionConfig,
2495 /// # };
2496 /// # use gemachain_sdk::{
2497 /// # signature::Signer,
2498 /// # signature::Signature,
2499 /// # signer::keypair::Keypair,
2500 /// # system_transaction,
2501 /// # commitment_config::CommitmentConfig,
2502 /// # };
2503 /// # use gemachain_transaction_status::UiTransactionEncoding;
2504 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
2505 /// # let alice = Keypair::new();
2506 /// # let bob = Keypair::new();
2507 /// # let carats = 50;
2508 /// # let latest_blockhash = rpc_client.get_latest_blockhash()?;
2509 /// # let tx = system_transaction::transfer(&alice, &bob.pubkey(), carats, latest_blockhash);
2510 /// let signature = rpc_client.send_and_confirm_transaction(&tx)?;
2511 /// let config = RpcTransactionConfig {
2512 /// encoding: Some(UiTransactionEncoding::Json),
2513 /// commitment: Some(CommitmentConfig::confirmed()),
2514 /// };
2515 ///
2516 /// let transaction = rpc_client.get_transaction_with_config(
2517 /// &signature,
2518 /// config,
2519 /// )?;
2520 /// # Ok::<(), ClientError>(())
2521 /// ```
2522 pub fn get_transaction_with_config(
2523 &self,
2524 signature: &Signature,
2525 config: RpcTransactionConfig,
2526 ) -> ClientResult<EncodedConfirmedTransaction> {
2527 self.send(
2528 self.maybe_map_request(RpcRequest::GetTransaction)?,
2529 json!([signature.to_string(), config]),
2530 )
2531 }
2532
2533 #[deprecated(
2534 since = "1.7.0",
2535 note = "Please use RpcClient::get_transaction() instead"
2536 )]
2537 #[allow(deprecated)]
2538 pub fn get_confirmed_transaction(
2539 &self,
2540 signature: &Signature,
2541 encoding: UiTransactionEncoding,
2542 ) -> ClientResult<EncodedConfirmedTransaction> {
2543 self.send(
2544 RpcRequest::GetConfirmedTransaction,
2545 json!([signature.to_string(), encoding]),
2546 )
2547 }
2548
2549 #[deprecated(
2550 since = "1.7.0",
2551 note = "Please use RpcClient::get_transaction_with_config() instead"
2552 )]
2553 #[allow(deprecated)]
2554 pub fn get_confirmed_transaction_with_config(
2555 &self,
2556 signature: &Signature,
2557 config: RpcConfirmedTransactionConfig,
2558 ) -> ClientResult<EncodedConfirmedTransaction> {
2559 self.send(
2560 RpcRequest::GetConfirmedTransaction,
2561 json!([signature.to_string(), config]),
2562 )
2563 }
2564
2565 /// # RPC Reference
2566 ///
2567 /// This method corresponds directly to the [`getBlockTime`] RPC
2568 /// method.
2569 ///
2570 /// [`getBlockTime`]: https://docs.gemachain.com/developing/clients/jsonrpc-api#getblocktime
2571 ///
2572 /// # Examples
2573 ///
2574 /// ```
2575 /// # use gemachain_client::{
2576 /// # client_error::ClientError,
2577 /// # rpc_client::RpcClient,
2578 /// # };
2579 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
2580 /// # let slot = rpc_client.get_slot()?;
2581 /// let block_time = rpc_client.get_block_time(slot)?;
2582 /// # Ok::<(), ClientError>(())
2583 /// ```
2584 pub fn get_block_time(&self, slot: Slot) -> ClientResult<UnixTimestamp> {
2585 let request = RpcRequest::GetBlockTime;
2586 let response = self.sender.send(request, json!([slot]));
2587
2588 response
2589 .map(|result_json| {
2590 if result_json.is_null() {
2591 return Err(RpcError::ForUser(format!("Block Not Found: slot={}", slot)).into());
2592 }
2593 let result = serde_json::from_value(result_json)
2594 .map_err(|err| ClientError::new_with_request(err.into(), request))?;
2595 trace!("Response block timestamp {:?} {:?}", slot, result);
2596 Ok(result)
2597 })
2598 .map_err(|err| err.into_with_request(request))?
2599 }
2600
2601 /// # RPC Reference
2602 ///
2603 /// This method corresponds directly to the [`getEpochInfo`] RPC method.
2604 ///
2605 /// [`getEpochInfo`]: https://docs.gemachain.com/developing/clients/jsonrpc-api#getepochinfo
2606 ///
2607 /// # Examples
2608 ///
2609 /// ```
2610 /// # use gemachain_client::{
2611 /// # client_error::ClientError,
2612 /// # rpc_client::RpcClient,
2613 /// # };
2614 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
2615 /// let epoch_info = rpc_client.get_epoch_info()?;
2616 /// # Ok::<(), ClientError>(())
2617 /// ```
2618 pub fn get_epoch_info(&self) -> ClientResult<EpochInfo> {
2619 self.get_epoch_info_with_commitment(self.commitment())
2620 }
2621
2622 /// # RPC Reference
2623 ///
2624 /// This method is built on the [`getEpochInfo`] RPC method.
2625 ///
2626 /// [`getEpochInfo`]: https://docs.gemachain.com/developing/clients/jsonrpc-api#getepochinfo
2627 ///
2628 /// # Examples
2629 ///
2630 /// ```
2631 /// # use gemachain_client::{
2632 /// # client_error::ClientError,
2633 /// # rpc_client::RpcClient,
2634 /// # };
2635 /// # use gemachain_sdk::commitment_config::CommitmentConfig;
2636 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
2637 /// let commitment_config = CommitmentConfig::confirmed();
2638 /// let epoch_info = rpc_client.get_epoch_info_with_commitment(
2639 /// commitment_config,
2640 /// )?;
2641 /// # Ok::<(), ClientError>(())
2642 /// ```
2643 pub fn get_epoch_info_with_commitment(
2644 &self,
2645 commitment_config: CommitmentConfig,
2646 ) -> ClientResult<EpochInfo> {
2647 self.send(
2648 RpcRequest::GetEpochInfo,
2649 json!([self.maybe_map_commitment(commitment_config)?]),
2650 )
2651 }
2652
2653 /// # RPC Reference
2654 ///
2655 /// This method corresponds directly to the [`getLeaderSchedule`] RPC method.
2656 ///
2657 /// [`getLeaderSchedule`]: https://docs.gemachain.com/developing/clients/jsonrpc-api#getleaderschedule
2658 ///
2659 /// # Examples
2660 ///
2661 /// ```
2662 /// # use gemachain_client::{
2663 /// # client_error::ClientError,
2664 /// # rpc_client::RpcClient,
2665 /// # };
2666 /// # use gemachain_sdk::commitment_config::CommitmentConfig;
2667 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
2668 /// # let slot = rpc_client.get_slot()?;
2669 /// let leader_schedule = rpc_client.get_leader_schedule(
2670 /// Some(slot),
2671 /// )?;
2672 /// # Ok::<(), ClientError>(())
2673 /// ```
2674 pub fn get_leader_schedule(
2675 &self,
2676 slot: Option<Slot>,
2677 ) -> ClientResult<Option<RpcLeaderSchedule>> {
2678 self.get_leader_schedule_with_commitment(slot, self.commitment())
2679 }
2680
2681 /// # RPC Reference
2682 ///
2683 /// This method is built on the [`getLeaderSchedule`] RPC method.
2684 ///
2685 /// [`getLeaderSchedule`]: https://docs.gemachain.com/developing/clients/jsonrpc-api#getleaderschedule
2686 ///
2687 /// # Examples
2688 ///
2689 /// ```
2690 /// # use gemachain_client::{
2691 /// # client_error::ClientError,
2692 /// # rpc_client::RpcClient,
2693 /// # };
2694 /// # use gemachain_sdk::commitment_config::CommitmentConfig;
2695 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
2696 /// # let slot = rpc_client.get_slot()?;
2697 /// let commitment_config = CommitmentConfig::processed();
2698 /// let leader_schedule = rpc_client.get_leader_schedule_with_commitment(
2699 /// Some(slot),
2700 /// commitment_config,
2701 /// )?;
2702 /// # Ok::<(), ClientError>(())
2703 /// ```
2704 pub fn get_leader_schedule_with_commitment(
2705 &self,
2706 slot: Option<Slot>,
2707 commitment_config: CommitmentConfig,
2708 ) -> ClientResult<Option<RpcLeaderSchedule>> {
2709 self.get_leader_schedule_with_config(
2710 slot,
2711 RpcLeaderScheduleConfig {
2712 commitment: Some(self.maybe_map_commitment(commitment_config)?),
2713 ..RpcLeaderScheduleConfig::default()
2714 },
2715 )
2716 }
2717
2718 /// # RPC Reference
2719 ///
2720 /// This method is built on the [`getLeaderSchedule`] RPC method.
2721 ///
2722 /// [`getLeaderSchedule`]: https://docs.gemachain.com/developing/clients/jsonrpc-api#getleaderschedule
2723 ///
2724 /// # Examples
2725 ///
2726 /// ```
2727 /// # use gemachain_client::{
2728 /// # client_error::ClientError,
2729 /// # rpc_client::RpcClient,
2730 /// # };
2731 /// # use gemachain_client::rpc_config::RpcLeaderScheduleConfig;
2732 /// # use gemachain_sdk::commitment_config::CommitmentConfig;
2733 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
2734 /// # let slot = rpc_client.get_slot()?;
2735 /// # let validator_pubkey_str = "7AYmEYBBetok8h5L3Eo3vi3bDWnjNnaFbSXfSNYV5ewB".to_string();
2736 /// let config = RpcLeaderScheduleConfig {
2737 /// identity: Some(validator_pubkey_str),
2738 /// commitment: Some(CommitmentConfig::processed()),
2739 /// };
2740 /// let leader_schedule = rpc_client.get_leader_schedule_with_config(
2741 /// Some(slot),
2742 /// config,
2743 /// )?;
2744 /// # Ok::<(), ClientError>(())
2745 /// ```
2746 pub fn get_leader_schedule_with_config(
2747 &self,
2748 slot: Option<Slot>,
2749 config: RpcLeaderScheduleConfig,
2750 ) -> ClientResult<Option<RpcLeaderSchedule>> {
2751 self.send(RpcRequest::GetLeaderSchedule, json!([slot, config]))
2752 }
2753
2754 /// # RPC Reference
2755 ///
2756 /// This method corresponds directly to the [`getEpochSchedule`]
2757 /// RPC method.
2758 ///
2759 /// [`getEpochSchedule`]: https://docs.gemachain.com/developing/clients/jsonrpc-api#getepochschedule
2760 ///
2761 /// # Examples
2762 ///
2763 /// ```
2764 /// # use gemachain_client::{
2765 /// # client_error::ClientError,
2766 /// # rpc_client::RpcClient,
2767 /// # };
2768 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
2769 /// let epoch_schedule = rpc_client.get_epoch_schedule()?;
2770 /// # Ok::<(), ClientError>(())
2771 /// ```
2772 pub fn get_epoch_schedule(&self) -> ClientResult<EpochSchedule> {
2773 self.send(RpcRequest::GetEpochSchedule, Value::Null)
2774 }
2775
2776 /// # RPC Reference
2777 ///
2778 /// This method corresponds directly to the [`getRecentPerformanceSamples`] RPC method.
2779 ///
2780 /// [`getRecentPerformanceSamples`]: https://docs.gemachain.com/developing/clients/jsonrpc-api#getrecentperformancesamples
2781 ///
2782 /// # Examples
2783 ///
2784 /// ```
2785 /// # use gemachain_client::{
2786 /// # client_error::ClientError,
2787 /// # rpc_client::RpcClient,
2788 /// # };
2789 /// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
2790 /// let limit = Some(10);
2791 /// let performance_samples = rpc_client.get_recent_performance_samples(
2792 /// limit,
2793 /// )?;
2794 /// # Ok::<(), ClientError>(())
2795 /// ```
2796 pub fn get_recent_performance_samples(
2797 &self,
2798 limit: Option<usize>,
2799 ) -> ClientResult<Vec<RpcPerfSample>> {
2800 self.send(RpcRequest::GetRecentPerformanceSamples, json!([limit]))
2801 }
2802
2803 pub fn get_identity(&self) -> ClientResult<Pubkey> {
2804 let rpc_identity: RpcIdentity = self.send(RpcRequest::GetIdentity, Value::Null)?;
2805
2806 rpc_identity.identity.parse::<Pubkey>().map_err(|_| {
2807 ClientError::new_with_request(
2808 RpcError::ParseError("Pubkey".to_string()).into(),
2809 RpcRequest::GetIdentity,
2810 )
2811 })
2812 }
2813
2814 pub fn get_inflation_governor(&self) -> ClientResult<RpcInflationGovernor> {
2815 self.send(RpcRequest::GetInflationGovernor, Value::Null)
2816 }
2817
2818 pub fn get_inflation_rate(&self) -> ClientResult<RpcInflationRate> {
2819 self.send(RpcRequest::GetInflationRate, Value::Null)
2820 }
2821
2822 pub fn get_inflation_reward(
2823 &self,
2824 addresses: &[Pubkey],
2825 epoch: Option<Epoch>,
2826 ) -> ClientResult<Vec<Option<RpcInflationReward>>> {
2827 let addresses: Vec<_> = addresses
2828 .iter()
2829 .map(|address| address.to_string())
2830 .collect();
2831 self.send(
2832 RpcRequest::GetInflationReward,
2833 json!([
2834 addresses,
2835 RpcEpochConfig {
2836 epoch,
2837 commitment: Some(self.commitment()),
2838 }
2839 ]),
2840 )
2841 }
2842
2843 pub fn get_version(&self) -> ClientResult<RpcVersionInfo> {
2844 self.send(RpcRequest::GetVersion, Value::Null)
2845 }
2846
2847 pub fn minimum_ledger_slot(&self) -> ClientResult<Slot> {
2848 self.send(RpcRequest::MinimumLedgerSlot, Value::Null)
2849 }
2850
2851 pub fn send_and_confirm_transaction(
2852 &self,
2853 transaction: &Transaction,
2854 ) -> ClientResult<Signature> {
2855 const SEND_RETRIES: usize = 1;
2856 const GET_STATUS_RETRIES: usize = usize::MAX;
2857
2858 'sending: for _ in 0..SEND_RETRIES {
2859 let signature = self.send_transaction(transaction)?;
2860
2861 let recent_blockhash = if uses_durable_nonce(transaction).is_some() {
2862 let (recent_blockhash, ..) =
2863 self.get_latest_blockhash_with_commitment(CommitmentConfig::processed())?;
2864 recent_blockhash
2865 } else {
2866 transaction.message.recent_blockhash
2867 };
2868
2869 for status_retry in 0..GET_STATUS_RETRIES {
2870 match self.get_signature_status(&signature)? {
2871 Some(Ok(_)) => return Ok(signature),
2872 Some(Err(e)) => return Err(e.into()),
2873 None => {
2874 if !self
2875 .is_blockhash_valid(&recent_blockhash, CommitmentConfig::processed())?
2876 {
2877 // Block hash is not found by some reason
2878 break 'sending;
2879 } else if cfg!(not(test))
2880 // Ignore sleep at last step.
2881 && status_retry < GET_STATUS_RETRIES
2882 {
2883 // Retry twice a second
2884 sleep(Duration::from_millis(500));
2885 continue;
2886 }
2887 }
2888 }
2889 }
2890 }
2891
2892 Err(RpcError::ForUser(
2893 "unable to confirm transaction. \
2894 This can happen in situations such as transaction expiration \
2895 and insufficient fee-payer funds"
2896 .to_string(),
2897 )
2898 .into())
2899 }
2900
2901 /// Note that `get_account` returns `Err(..)` if the account does not exist whereas
2902 /// `get_account_with_commitment` returns `Ok(None)` if the account does not exist.
2903 pub fn get_account(&self, pubkey: &Pubkey) -> ClientResult<Account> {
2904 self.get_account_with_commitment(pubkey, self.commitment())?
2905 .value
2906 .ok_or_else(|| RpcError::ForUser(format!("AccountNotFound: pubkey={}", pubkey)).into())
2907 }
2908
2909 pub fn get_account_with_commitment(
2910 &self,
2911 pubkey: &Pubkey,
2912 commitment_config: CommitmentConfig,
2913 ) -> RpcResult<Option<Account>> {
2914 let config = RpcAccountInfoConfig {
2915 encoding: Some(UiAccountEncoding::Base64Zstd),
2916 commitment: Some(self.maybe_map_commitment(commitment_config)?),
2917 data_slice: None,
2918 };
2919 let response = self.sender.send(
2920 RpcRequest::GetAccountInfo,
2921 json!([pubkey.to_string(), config]),
2922 );
2923
2924 response
2925 .map(|result_json| {
2926 if result_json.is_null() {
2927 return Err(
2928 RpcError::ForUser(format!("AccountNotFound: pubkey={}", pubkey)).into(),
2929 );
2930 }
2931 let Response {
2932 context,
2933 value: rpc_account,
2934 } = serde_json::from_value::<Response<Option<UiAccount>>>(result_json)?;
2935 trace!("Response account {:?} {:?}", pubkey, rpc_account);
2936 let account = rpc_account.and_then(|rpc_account| rpc_account.decode());
2937 Ok(Response {
2938 context,
2939 value: account,
2940 })
2941 })
2942 .map_err(|err| {
2943 Into::<ClientError>::into(RpcError::ForUser(format!(
2944 "AccountNotFound: pubkey={}: {}",
2945 pubkey, err
2946 )))
2947 })?
2948 }
2949
2950 pub fn get_max_retransmit_slot(&self) -> ClientResult<Slot> {
2951 self.send(RpcRequest::GetMaxRetransmitSlot, Value::Null)
2952 }
2953
2954 pub fn get_max_shred_insert_slot(&self) -> ClientResult<Slot> {
2955 self.send(RpcRequest::GetMaxShredInsertSlot, Value::Null)
2956 }
2957
2958 pub fn get_multiple_accounts(&self, pubkeys: &[Pubkey]) -> ClientResult<Vec<Option<Account>>> {
2959 Ok(self
2960 .get_multiple_accounts_with_commitment(pubkeys, self.commitment())?
2961 .value)
2962 }
2963
2964 pub fn get_multiple_accounts_with_commitment(
2965 &self,
2966 pubkeys: &[Pubkey],
2967 commitment_config: CommitmentConfig,
2968 ) -> RpcResult<Vec<Option<Account>>> {
2969 self.get_multiple_accounts_with_config(
2970 pubkeys,
2971 RpcAccountInfoConfig {
2972 encoding: Some(UiAccountEncoding::Base64Zstd),
2973 commitment: Some(self.maybe_map_commitment(commitment_config)?),
2974 data_slice: None,
2975 },
2976 )
2977 }
2978
2979 pub fn get_multiple_accounts_with_config(
2980 &self,
2981 pubkeys: &[Pubkey],
2982 config: RpcAccountInfoConfig,
2983 ) -> RpcResult<Vec<Option<Account>>> {
2984 let config = RpcAccountInfoConfig {
2985 commitment: config.commitment.or_else(|| Some(self.commitment())),
2986 ..config
2987 };
2988 let pubkeys: Vec<_> = pubkeys.iter().map(|pubkey| pubkey.to_string()).collect();
2989 let response = self.send(RpcRequest::GetMultipleAccounts, json!([pubkeys, config]))?;
2990 let Response {
2991 context,
2992 value: accounts,
2993 } = serde_json::from_value::<Response<Vec<Option<UiAccount>>>>(response)?;
2994 let accounts: Vec<Option<Account>> = accounts
2995 .into_iter()
2996 .map(|rpc_account| rpc_account.map(|a| a.decode()).flatten())
2997 .collect();
2998 Ok(Response {
2999 context,
3000 value: accounts,
3001 })
3002 }
3003
3004 pub fn get_account_data(&self, pubkey: &Pubkey) -> ClientResult<Vec<u8>> {
3005 Ok(self.get_account(pubkey)?.data)
3006 }
3007
3008 pub fn get_minimum_balance_for_rent_exemption(&self, data_len: usize) -> ClientResult<u64> {
3009 let request = RpcRequest::GetMinimumBalanceForRentExemption;
3010 let minimum_balance_json = self
3011 .sender
3012 .send(request, json!([data_len]))
3013 .map_err(|err| err.into_with_request(request))?;
3014
3015 let minimum_balance: u64 = serde_json::from_value(minimum_balance_json)
3016 .map_err(|err| ClientError::new_with_request(err.into(), request))?;
3017 trace!(
3018 "Response minimum balance {:?} {:?}",
3019 data_len,
3020 minimum_balance
3021 );
3022 Ok(minimum_balance)
3023 }
3024
3025 /// Request the balance of the account `pubkey`.
3026 pub fn get_balance(&self, pubkey: &Pubkey) -> ClientResult<u64> {
3027 Ok(self
3028 .get_balance_with_commitment(pubkey, self.commitment())?
3029 .value)
3030 }
3031
3032 pub fn get_balance_with_commitment(
3033 &self,
3034 pubkey: &Pubkey,
3035 commitment_config: CommitmentConfig,
3036 ) -> RpcResult<u64> {
3037 self.send(
3038 RpcRequest::GetBalance,
3039 json!([
3040 pubkey.to_string(),
3041 self.maybe_map_commitment(commitment_config)?
3042 ]),
3043 )
3044 }
3045
3046 pub fn get_program_accounts(&self, pubkey: &Pubkey) -> ClientResult<Vec<(Pubkey, Account)>> {
3047 self.get_program_accounts_with_config(
3048 pubkey,
3049 RpcProgramAccountsConfig {
3050 account_config: RpcAccountInfoConfig {
3051 encoding: Some(UiAccountEncoding::Base64Zstd),
3052 ..RpcAccountInfoConfig::default()
3053 },
3054 ..RpcProgramAccountsConfig::default()
3055 },
3056 )
3057 }
3058
3059 pub fn get_program_accounts_with_config(
3060 &self,
3061 pubkey: &Pubkey,
3062 config: RpcProgramAccountsConfig,
3063 ) -> ClientResult<Vec<(Pubkey, Account)>> {
3064 let commitment = config
3065 .account_config
3066 .commitment
3067 .unwrap_or_else(|| self.commitment());
3068 let commitment = self.maybe_map_commitment(commitment)?;
3069 let account_config = RpcAccountInfoConfig {
3070 commitment: Some(commitment),
3071 ..config.account_config
3072 };
3073 let config = RpcProgramAccountsConfig {
3074 account_config,
3075 ..config
3076 };
3077 let accounts: Vec<RpcKeyedAccount> = self.send(
3078 RpcRequest::GetProgramAccounts,
3079 json!([pubkey.to_string(), config]),
3080 )?;
3081 parse_keyed_accounts(accounts, RpcRequest::GetProgramAccounts)
3082 }
3083
3084 /// Request the transaction count.
3085 pub fn get_transaction_count(&self) -> ClientResult<u64> {
3086 self.get_transaction_count_with_commitment(self.commitment())
3087 }
3088
3089 pub fn get_transaction_count_with_commitment(
3090 &self,
3091 commitment_config: CommitmentConfig,
3092 ) -> ClientResult<u64> {
3093 self.send(
3094 RpcRequest::GetTransactionCount,
3095 json!([self.maybe_map_commitment(commitment_config)?]),
3096 )
3097 }
3098
3099 #[deprecated(
3100 since = "1.8.0",
3101 note = "Please use `get_latest_blockhash` and `get_fee_for_message` instead"
3102 )]
3103 #[allow(deprecated)]
3104 pub fn get_fees(&self) -> ClientResult<Fees> {
3105 #[allow(deprecated)]
3106 Ok(self.get_fees_with_commitment(self.commitment())?.value)
3107 }
3108
3109 #[deprecated(
3110 since = "1.8.0",
3111 note = "Please use `get_latest_blockhash_with_commitment` and `get_fee_for_message` instead"
3112 )]
3113 #[allow(deprecated)]
3114 pub fn get_fees_with_commitment(&self, commitment_config: CommitmentConfig) -> RpcResult<Fees> {
3115 let Response {
3116 context,
3117 value: fees,
3118 } = self.send::<Response<RpcFees>>(
3119 RpcRequest::GetFees,
3120 json!([self.maybe_map_commitment(commitment_config)?]),
3121 )?;
3122 let blockhash = fees.blockhash.parse().map_err(|_| {
3123 ClientError::new_with_request(
3124 RpcError::ParseError("Hash".to_string()).into(),
3125 RpcRequest::GetFees,
3126 )
3127 })?;
3128 Ok(Response {
3129 context,
3130 value: Fees {
3131 blockhash,
3132 fee_calculator: fees.fee_calculator,
3133 last_valid_block_height: fees.last_valid_block_height,
3134 },
3135 })
3136 }
3137
3138 #[deprecated(since = "1.8.0", note = "Please use `get_latest_blockhash` instead")]
3139 #[allow(deprecated)]
3140 pub fn get_recent_blockhash(&self) -> ClientResult<(Hash, FeeCalculator)> {
3141 #[allow(deprecated)]
3142 let (blockhash, fee_calculator, _last_valid_slot) = self
3143 .get_recent_blockhash_with_commitment(self.commitment())?
3144 .value;
3145 Ok((blockhash, fee_calculator))
3146 }
3147
3148 #[deprecated(
3149 since = "1.8.0",
3150 note = "Please use `get_latest_blockhash_with_commitment` instead"
3151 )]
3152 #[allow(deprecated)]
3153 pub fn get_recent_blockhash_with_commitment(
3154 &self,
3155 commitment_config: CommitmentConfig,
3156 ) -> RpcResult<(Hash, FeeCalculator, Slot)> {
3157 let (context, blockhash, fee_calculator, last_valid_slot) = if let Ok(Response {
3158 context,
3159 value:
3160 RpcFees {
3161 blockhash,
3162 fee_calculator,
3163 last_valid_slot,
3164 ..
3165 },
3166 }) = self
3167 .send::<Response<RpcFees>>(
3168 RpcRequest::GetFees,
3169 json!([self.maybe_map_commitment(commitment_config)?]),
3170 ) {
3171 (context, blockhash, fee_calculator, last_valid_slot)
3172 } else if let Ok(Response {
3173 context,
3174 value:
3175 DeprecatedRpcFees {
3176 blockhash,
3177 fee_calculator,
3178 last_valid_slot,
3179 },
3180 }) = self.send::<Response<DeprecatedRpcFees>>(
3181 RpcRequest::GetFees,
3182 json!([self.maybe_map_commitment(commitment_config)?]),
3183 ) {
3184 (context, blockhash, fee_calculator, last_valid_slot)
3185 } else if let Ok(Response {
3186 context,
3187 value:
3188 RpcBlockhashFeeCalculator {
3189 blockhash,
3190 fee_calculator,
3191 },
3192 }) = self.send::<Response<RpcBlockhashFeeCalculator>>(
3193 RpcRequest::GetRecentBlockhash,
3194 json!([self.maybe_map_commitment(commitment_config)?]),
3195 ) {
3196 (context, blockhash, fee_calculator, 0)
3197 } else {
3198 return Err(ClientError::new_with_request(
3199 RpcError::ParseError("RpcBlockhashFeeCalculator or RpcFees".to_string()).into(),
3200 RpcRequest::GetRecentBlockhash,
3201 ));
3202 };
3203
3204 let blockhash = blockhash.parse().map_err(|_| {
3205 ClientError::new_with_request(
3206 RpcError::ParseError("Hash".to_string()).into(),
3207 RpcRequest::GetRecentBlockhash,
3208 )
3209 })?;
3210 Ok(Response {
3211 context,
3212 value: (blockhash, fee_calculator, last_valid_slot),
3213 })
3214 }
3215
3216 #[deprecated(since = "1.8.0", note = "Please `get_fee_for_message` instead")]
3217 #[allow(deprecated)]
3218 pub fn get_fee_calculator_for_blockhash(
3219 &self,
3220 blockhash: &Hash,
3221 ) -> ClientResult<Option<FeeCalculator>> {
3222 #[allow(deprecated)]
3223 Ok(self
3224 .get_fee_calculator_for_blockhash_with_commitment(blockhash, self.commitment())?
3225 .value)
3226 }
3227
3228 #[deprecated(
3229 since = "1.8.0",
3230 note = "Please `get_latest_blockhash_with_commitment` and `get_fee_for_message` instead"
3231 )]
3232 #[allow(deprecated)]
3233 pub fn get_fee_calculator_for_blockhash_with_commitment(
3234 &self,
3235 blockhash: &Hash,
3236 commitment_config: CommitmentConfig,
3237 ) -> RpcResult<Option<FeeCalculator>> {
3238 let Response { context, value } = self.send::<Response<Option<RpcFeeCalculator>>>(
3239 RpcRequest::GetFeeCalculatorForBlockhash,
3240 json!([
3241 blockhash.to_string(),
3242 self.maybe_map_commitment(commitment_config)?
3243 ]),
3244 )?;
3245
3246 Ok(Response {
3247 context,
3248 value: value.map(|rf| rf.fee_calculator),
3249 })
3250 }
3251
3252 #[deprecated(
3253 since = "1.8.0",
3254 note = "Please do not use, will no longer be available in the future"
3255 )]
3256 #[allow(deprecated)]
3257 pub fn get_fee_rate_governor(&self) -> RpcResult<FeeRateGovernor> {
3258 let Response {
3259 context,
3260 value: RpcFeeRateGovernor { fee_rate_governor },
3261 } =
3262 self.send::<Response<RpcFeeRateGovernor>>(RpcRequest::GetFeeRateGovernor, Value::Null)?;
3263
3264 Ok(Response {
3265 context,
3266 value: fee_rate_governor,
3267 })
3268 }
3269
3270 #[deprecated(
3271 since = "1.8.0",
3272 note = "Please use `get_new_latest_blockhash` instead"
3273 )]
3274 #[allow(deprecated)]
3275 pub fn get_new_blockhash(&self, blockhash: &Hash) -> ClientResult<(Hash, FeeCalculator)> {
3276 let mut num_retries = 0;
3277 let start = Instant::now();
3278 while start.elapsed().as_secs() < 5 {
3279 #[allow(deprecated)]
3280 if let Ok((new_blockhash, fee_calculator)) = self.get_recent_blockhash() {
3281 if new_blockhash != *blockhash {
3282 return Ok((new_blockhash, fee_calculator));
3283 }
3284 }
3285 debug!("Got same blockhash ({:?}), will retry...", blockhash);
3286
3287 // Retry ~twice during a slot
3288 sleep(Duration::from_millis(DEFAULT_MS_PER_SLOT / 2));
3289 num_retries += 1;
3290 }
3291 Err(RpcError::ForUser(format!(
3292 "Unable to get new blockhash after {}ms (retried {} times), stuck at {}",
3293 start.elapsed().as_millis(),
3294 num_retries,
3295 blockhash
3296 ))
3297 .into())
3298 }
3299
3300 pub fn get_first_available_block(&self) -> ClientResult<Slot> {
3301 self.send(RpcRequest::GetFirstAvailableBlock, Value::Null)
3302 }
3303
3304 pub fn get_genesis_hash(&self) -> ClientResult<Hash> {
3305 let hash_str: String = self.send(RpcRequest::GetGenesisHash, Value::Null)?;
3306 let hash = hash_str.parse().map_err(|_| {
3307 ClientError::new_with_request(
3308 RpcError::ParseError("Hash".to_string()).into(),
3309 RpcRequest::GetGenesisHash,
3310 )
3311 })?;
3312 Ok(hash)
3313 }
3314
3315 pub fn get_health(&self) -> ClientResult<()> {
3316 self.send::<String>(RpcRequest::GetHealth, Value::Null)
3317 .map(|_| ())
3318 }
3319
3320 pub fn get_token_account(&self, pubkey: &Pubkey) -> ClientResult<Option<UiTokenAccount>> {
3321 Ok(self
3322 .get_token_account_with_commitment(pubkey, self.commitment())?
3323 .value)
3324 }
3325
3326 pub fn get_token_account_with_commitment(
3327 &self,
3328 pubkey: &Pubkey,
3329 commitment_config: CommitmentConfig,
3330 ) -> RpcResult<Option<UiTokenAccount>> {
3331 let config = RpcAccountInfoConfig {
3332 encoding: Some(UiAccountEncoding::JsonParsed),
3333 commitment: Some(self.maybe_map_commitment(commitment_config)?),
3334 data_slice: None,
3335 };
3336 let response = self.sender.send(
3337 RpcRequest::GetAccountInfo,
3338 json!([pubkey.to_string(), config]),
3339 );
3340
3341 response
3342 .map(|result_json| {
3343 if result_json.is_null() {
3344 return Err(
3345 RpcError::ForUser(format!("AccountNotFound: pubkey={}", pubkey)).into(),
3346 );
3347 }
3348 let Response {
3349 context,
3350 value: rpc_account,
3351 } = serde_json::from_value::<Response<Option<UiAccount>>>(result_json)?;
3352 trace!("Response account {:?} {:?}", pubkey, rpc_account);
3353 let response = {
3354 if let Some(rpc_account) = rpc_account {
3355 if let UiAccountData::Json(account_data) = rpc_account.data {
3356 let token_account_type: TokenAccountType =
3357 serde_json::from_value(account_data.parsed)?;
3358 if let TokenAccountType::Account(token_account) = token_account_type {
3359 return Ok(Response {
3360 context,
3361 value: Some(token_account),
3362 });
3363 }
3364 }
3365 }
3366 Err(Into::<ClientError>::into(RpcError::ForUser(format!(
3367 "Account could not be parsed as token account: pubkey={}",
3368 pubkey
3369 ))))
3370 };
3371 response?
3372 })
3373 .map_err(|err| {
3374 Into::<ClientError>::into(RpcError::ForUser(format!(
3375 "AccountNotFound: pubkey={}: {}",
3376 pubkey, err
3377 )))
3378 })?
3379 }
3380
3381 pub fn get_token_account_balance(&self, pubkey: &Pubkey) -> ClientResult<UiTokenAmount> {
3382 Ok(self
3383 .get_token_account_balance_with_commitment(pubkey, self.commitment())?
3384 .value)
3385 }
3386
3387 pub fn get_token_account_balance_with_commitment(
3388 &self,
3389 pubkey: &Pubkey,
3390 commitment_config: CommitmentConfig,
3391 ) -> RpcResult<UiTokenAmount> {
3392 self.send(
3393 RpcRequest::GetTokenAccountBalance,
3394 json!([
3395 pubkey.to_string(),
3396 self.maybe_map_commitment(commitment_config)?
3397 ]),
3398 )
3399 }
3400
3401 pub fn get_token_accounts_by_delegate(
3402 &self,
3403 delegate: &Pubkey,
3404 token_account_filter: TokenAccountsFilter,
3405 ) -> ClientResult<Vec<RpcKeyedAccount>> {
3406 Ok(self
3407 .get_token_accounts_by_delegate_with_commitment(
3408 delegate,
3409 token_account_filter,
3410 self.commitment(),
3411 )?
3412 .value)
3413 }
3414
3415 pub fn get_token_accounts_by_delegate_with_commitment(
3416 &self,
3417 delegate: &Pubkey,
3418 token_account_filter: TokenAccountsFilter,
3419 commitment_config: CommitmentConfig,
3420 ) -> RpcResult<Vec<RpcKeyedAccount>> {
3421 let token_account_filter = match token_account_filter {
3422 TokenAccountsFilter::Mint(mint) => RpcTokenAccountsFilter::Mint(mint.to_string()),
3423 TokenAccountsFilter::ProgramId(program_id) => {
3424 RpcTokenAccountsFilter::ProgramId(program_id.to_string())
3425 }
3426 };
3427
3428 let config = RpcAccountInfoConfig {
3429 encoding: Some(UiAccountEncoding::JsonParsed),
3430 commitment: Some(self.maybe_map_commitment(commitment_config)?),
3431 data_slice: None,
3432 };
3433
3434 self.send(
3435 RpcRequest::GetTokenAccountsByOwner,
3436 json!([delegate.to_string(), token_account_filter, config]),
3437 )
3438 }
3439
3440 pub fn get_token_accounts_by_owner(
3441 &self,
3442 owner: &Pubkey,
3443 token_account_filter: TokenAccountsFilter,
3444 ) -> ClientResult<Vec<RpcKeyedAccount>> {
3445 Ok(self
3446 .get_token_accounts_by_owner_with_commitment(
3447 owner,
3448 token_account_filter,
3449 self.commitment(),
3450 )?
3451 .value)
3452 }
3453
3454 pub fn get_token_accounts_by_owner_with_commitment(
3455 &self,
3456 owner: &Pubkey,
3457 token_account_filter: TokenAccountsFilter,
3458 commitment_config: CommitmentConfig,
3459 ) -> RpcResult<Vec<RpcKeyedAccount>> {
3460 let token_account_filter = match token_account_filter {
3461 TokenAccountsFilter::Mint(mint) => RpcTokenAccountsFilter::Mint(mint.to_string()),
3462 TokenAccountsFilter::ProgramId(program_id) => {
3463 RpcTokenAccountsFilter::ProgramId(program_id.to_string())
3464 }
3465 };
3466
3467 let config = RpcAccountInfoConfig {
3468 encoding: Some(UiAccountEncoding::JsonParsed),
3469 commitment: Some(self.maybe_map_commitment(commitment_config)?),
3470 data_slice: None,
3471 };
3472
3473 self.send(
3474 RpcRequest::GetTokenAccountsByOwner,
3475 json!([owner.to_string(), token_account_filter, config]),
3476 )
3477 }
3478
3479 pub fn get_token_supply(&self, mint: &Pubkey) -> ClientResult<UiTokenAmount> {
3480 Ok(self
3481 .get_token_supply_with_commitment(mint, self.commitment())?
3482 .value)
3483 }
3484
3485 pub fn get_token_supply_with_commitment(
3486 &self,
3487 mint: &Pubkey,
3488 commitment_config: CommitmentConfig,
3489 ) -> RpcResult<UiTokenAmount> {
3490 self.send(
3491 RpcRequest::GetTokenSupply,
3492 json!([
3493 mint.to_string(),
3494 self.maybe_map_commitment(commitment_config)?
3495 ]),
3496 )
3497 }
3498
3499 pub fn request_airdrop(&self, pubkey: &Pubkey, carats: u64) -> ClientResult<Signature> {
3500 self.request_airdrop_with_config(
3501 pubkey,
3502 carats,
3503 RpcRequestAirdropConfig {
3504 commitment: Some(self.commitment()),
3505 ..RpcRequestAirdropConfig::default()
3506 },
3507 )
3508 }
3509
3510 pub fn request_airdrop_with_blockhash(
3511 &self,
3512 pubkey: &Pubkey,
3513 carats: u64,
3514 recent_blockhash: &Hash,
3515 ) -> ClientResult<Signature> {
3516 self.request_airdrop_with_config(
3517 pubkey,
3518 carats,
3519 RpcRequestAirdropConfig {
3520 commitment: Some(self.commitment()),
3521 recent_blockhash: Some(recent_blockhash.to_string()),
3522 },
3523 )
3524 }
3525
3526 pub fn request_airdrop_with_config(
3527 &self,
3528 pubkey: &Pubkey,
3529 carats: u64,
3530 config: RpcRequestAirdropConfig,
3531 ) -> ClientResult<Signature> {
3532 let commitment = config.commitment.unwrap_or_default();
3533 let commitment = self.maybe_map_commitment(commitment)?;
3534 let config = RpcRequestAirdropConfig {
3535 commitment: Some(commitment),
3536 ..config
3537 };
3538 self.send(
3539 RpcRequest::RequestAirdrop,
3540 json!([pubkey.to_string(), carats, config]),
3541 )
3542 .and_then(|signature: String| {
3543 Signature::from_str(&signature).map_err(|err| {
3544 ClientErrorKind::Custom(format!("signature deserialization failed: {}", err)).into()
3545 })
3546 })
3547 .map_err(|_| {
3548 RpcError::ForUser(
3549 "airdrop request failed. \
3550 This can happen when the rate limit is reached."
3551 .to_string(),
3552 )
3553 .into()
3554 })
3555 }
3556
3557 fn poll_balance_with_timeout_and_commitment(
3558 &self,
3559 pubkey: &Pubkey,
3560 polling_frequency: &Duration,
3561 timeout: &Duration,
3562 commitment_config: CommitmentConfig,
3563 ) -> ClientResult<u64> {
3564 let now = Instant::now();
3565 loop {
3566 match self.get_balance_with_commitment(pubkey, commitment_config) {
3567 Ok(bal) => {
3568 return Ok(bal.value);
3569 }
3570 Err(e) => {
3571 sleep(*polling_frequency);
3572 if now.elapsed() > *timeout {
3573 return Err(e);
3574 }
3575 }
3576 };
3577 }
3578 }
3579
3580 pub fn poll_get_balance_with_commitment(
3581 &self,
3582 pubkey: &Pubkey,
3583 commitment_config: CommitmentConfig,
3584 ) -> ClientResult<u64> {
3585 self.poll_balance_with_timeout_and_commitment(
3586 pubkey,
3587 &Duration::from_millis(100),
3588 &Duration::from_secs(1),
3589 commitment_config,
3590 )
3591 }
3592
3593 pub fn wait_for_balance_with_commitment(
3594 &self,
3595 pubkey: &Pubkey,
3596 expected_balance: Option<u64>,
3597 commitment_config: CommitmentConfig,
3598 ) -> Option<u64> {
3599 const LAST: usize = 30;
3600 for run in 0..LAST {
3601 let balance_result = self.poll_get_balance_with_commitment(pubkey, commitment_config);
3602 if expected_balance.is_none() {
3603 return balance_result.ok();
3604 }
3605 trace!(
3606 "wait_for_balance_with_commitment [{}] {:?} {:?}",
3607 run,
3608 balance_result,
3609 expected_balance
3610 );
3611 if let (Some(expected_balance), Ok(balance_result)) = (expected_balance, balance_result)
3612 {
3613 if expected_balance == balance_result {
3614 return Some(balance_result);
3615 }
3616 }
3617 }
3618 None
3619 }
3620
3621 /// Poll the server to confirm a transaction.
3622 pub fn poll_for_signature(&self, signature: &Signature) -> ClientResult<()> {
3623 self.poll_for_signature_with_commitment(signature, self.commitment())
3624 }
3625
3626 /// Poll the server to confirm a transaction.
3627 pub fn poll_for_signature_with_commitment(
3628 &self,
3629 signature: &Signature,
3630 commitment_config: CommitmentConfig,
3631 ) -> ClientResult<()> {
3632 let now = Instant::now();
3633 loop {
3634 if let Ok(Some(_)) =
3635 self.get_signature_status_with_commitment(signature, commitment_config)
3636 {
3637 break;
3638 }
3639 if now.elapsed().as_secs() > 15 {
3640 return Err(RpcError::ForUser(format!(
3641 "signature not found after {} seconds",
3642 now.elapsed().as_secs()
3643 ))
3644 .into());
3645 }
3646 sleep(Duration::from_millis(250));
3647 }
3648 Ok(())
3649 }
3650
3651 /// Poll the server to confirm a transaction.
3652 pub fn poll_for_signature_confirmation(
3653 &self,
3654 signature: &Signature,
3655 min_confirmed_blocks: usize,
3656 ) -> ClientResult<usize> {
3657 let mut now = Instant::now();
3658 let mut confirmed_blocks = 0;
3659 loop {
3660 let response = self.get_num_blocks_since_signature_confirmation(signature);
3661 match response {
3662 Ok(count) => {
3663 if confirmed_blocks != count {
3664 info!(
3665 "signature {} confirmed {} out of {} after {} ms",
3666 signature,
3667 count,
3668 min_confirmed_blocks,
3669 now.elapsed().as_millis()
3670 );
3671 now = Instant::now();
3672 confirmed_blocks = count;
3673 }
3674 if count >= min_confirmed_blocks {
3675 break;
3676 }
3677 }
3678 Err(err) => {
3679 debug!("check_confirmations request failed: {:?}", err);
3680 }
3681 };
3682 if now.elapsed().as_secs() > 20 {
3683 info!(
3684 "signature {} confirmed {} out of {} failed after {} ms",
3685 signature,
3686 confirmed_blocks,
3687 min_confirmed_blocks,
3688 now.elapsed().as_millis()
3689 );
3690 if confirmed_blocks > 0 {
3691 return Ok(confirmed_blocks);
3692 } else {
3693 return Err(RpcError::ForUser(format!(
3694 "signature not found after {} seconds",
3695 now.elapsed().as_secs()
3696 ))
3697 .into());
3698 }
3699 }
3700 sleep(Duration::from_millis(250));
3701 }
3702 Ok(confirmed_blocks)
3703 }
3704
3705 pub fn get_num_blocks_since_signature_confirmation(
3706 &self,
3707 signature: &Signature,
3708 ) -> ClientResult<usize> {
3709 let result: Response<Vec<Option<TransactionStatus>>> = self.send(
3710 RpcRequest::GetSignatureStatuses,
3711 json!([[signature.to_string()]]),
3712 )?;
3713
3714 let confirmations = result.value[0]
3715 .clone()
3716 .ok_or_else(|| {
3717 ClientError::new_with_request(
3718 ClientErrorKind::Custom("signature not found".to_string()),
3719 RpcRequest::GetSignatureStatuses,
3720 )
3721 })?
3722 .confirmations
3723 .unwrap_or(MAX_LOCKOUT_HISTORY + 1);
3724 Ok(confirmations)
3725 }
3726
3727 pub fn send_and_confirm_transaction_with_spinner(
3728 &self,
3729 transaction: &Transaction,
3730 ) -> ClientResult<Signature> {
3731 self.send_and_confirm_transaction_with_spinner_and_commitment(
3732 transaction,
3733 self.commitment(),
3734 )
3735 }
3736
3737 pub fn send_and_confirm_transaction_with_spinner_and_commitment(
3738 &self,
3739 transaction: &Transaction,
3740 commitment: CommitmentConfig,
3741 ) -> ClientResult<Signature> {
3742 self.send_and_confirm_transaction_with_spinner_and_config(
3743 transaction,
3744 commitment,
3745 RpcSendTransactionConfig {
3746 preflight_commitment: Some(commitment.commitment),
3747 ..RpcSendTransactionConfig::default()
3748 },
3749 )
3750 }
3751
3752 pub fn send_and_confirm_transaction_with_spinner_and_config(
3753 &self,
3754 transaction: &Transaction,
3755 commitment: CommitmentConfig,
3756 config: RpcSendTransactionConfig,
3757 ) -> ClientResult<Signature> {
3758 let recent_blockhash = if uses_durable_nonce(transaction).is_some() {
3759 self.get_latest_blockhash_with_commitment(CommitmentConfig::processed())?
3760 .0
3761 } else {
3762 transaction.message.recent_blockhash
3763 };
3764 let signature = self.send_transaction_with_config(transaction, config)?;
3765 self.confirm_transaction_with_spinner(&signature, &recent_blockhash, commitment)?;
3766 Ok(signature)
3767 }
3768
3769 pub fn confirm_transaction_with_spinner(
3770 &self,
3771 signature: &Signature,
3772 recent_blockhash: &Hash,
3773 commitment: CommitmentConfig,
3774 ) -> ClientResult<()> {
3775 let desired_confirmations = if commitment.is_finalized() {
3776 MAX_LOCKOUT_HISTORY + 1
3777 } else {
3778 1
3779 };
3780 let mut confirmations = 0;
3781
3782 let progress_bar = new_spinner_progress_bar();
3783
3784 progress_bar.set_message(format!(
3785 "[{}/{}] Finalizing transaction {}",
3786 confirmations, desired_confirmations, signature,
3787 ));
3788
3789 let now = Instant::now();
3790 let confirm_transaction_initial_timeout = self
3791 .config
3792 .confirm_transaction_initial_timeout
3793 .unwrap_or_default();
3794 let (signature, status) = loop {
3795 // Get recent commitment in order to count confirmations for successful transactions
3796 let status = self
3797 .get_signature_status_with_commitment(signature, CommitmentConfig::processed())?;
3798 if status.is_none() {
3799 let blockhash_not_found =
3800 !self.is_blockhash_valid(recent_blockhash, CommitmentConfig::processed())?;
3801 if blockhash_not_found && now.elapsed() >= confirm_transaction_initial_timeout {
3802 break (signature, status);
3803 }
3804 } else {
3805 break (signature, status);
3806 }
3807
3808 if cfg!(not(test)) {
3809 sleep(Duration::from_millis(500));
3810 }
3811 };
3812 if let Some(result) = status {
3813 if let Err(err) = result {
3814 return Err(err.into());
3815 }
3816 } else {
3817 return Err(RpcError::ForUser(
3818 "unable to confirm transaction. \
3819 This can happen in situations such as transaction expiration \
3820 and insufficient fee-payer funds"
3821 .to_string(),
3822 )
3823 .into());
3824 }
3825 let now = Instant::now();
3826 loop {
3827 // Return when specified commitment is reached
3828 // Failed transactions have already been eliminated, `is_some` check is sufficient
3829 if self
3830 .get_signature_status_with_commitment(signature, commitment)?
3831 .is_some()
3832 {
3833 progress_bar.set_message("Transaction confirmed");
3834 progress_bar.finish_and_clear();
3835 return Ok(());
3836 }
3837
3838 progress_bar.set_message(format!(
3839 "[{}/{}] Finalizing transaction {}",
3840 min(confirmations + 1, desired_confirmations),
3841 desired_confirmations,
3842 signature,
3843 ));
3844 sleep(Duration::from_millis(500));
3845 confirmations = self
3846 .get_num_blocks_since_signature_confirmation(signature)
3847 .unwrap_or(confirmations);
3848 if now.elapsed().as_secs() >= MAX_HASH_AGE_IN_SECONDS as u64 {
3849 return Err(
3850 RpcError::ForUser("transaction not finalized. \
3851 This can happen when a transaction lands in an abandoned fork. \
3852 Please retry.".to_string()).into(),
3853 );
3854 }
3855 }
3856 }
3857
3858 pub fn get_latest_blockhash(&self) -> ClientResult<Hash> {
3859 let (blockhash, _) = self.get_latest_blockhash_with_commitment(self.commitment())?;
3860 Ok(blockhash)
3861 }
3862
3863 #[allow(deprecated)]
3864 pub fn get_latest_blockhash_with_commitment(
3865 &self,
3866 commitment: CommitmentConfig,
3867 ) -> ClientResult<(Hash, u64)> {
3868 let (blockhash, last_valid_block_height) =
3869 if self.get_node_version()? < semver::Version::new(1, 8, 0) {
3870 let Fees {
3871 blockhash,
3872 last_valid_block_height,
3873 ..
3874 } = self.get_fees_with_commitment(commitment)?.value;
3875 (blockhash, last_valid_block_height)
3876 } else {
3877 let RpcBlockhash {
3878 blockhash,
3879 last_valid_block_height,
3880 } = self
3881 .send::<Response<RpcBlockhash>>(
3882 RpcRequest::GetLatestBlockhash,
3883 json!([self.maybe_map_commitment(commitment)?]),
3884 )?
3885 .value;
3886 let blockhash = blockhash.parse().map_err(|_| {
3887 ClientError::new_with_request(
3888 RpcError::ParseError("Hash".to_string()).into(),
3889 RpcRequest::GetLatestBlockhash,
3890 )
3891 })?;
3892 (blockhash, last_valid_block_height)
3893 };
3894 Ok((blockhash, last_valid_block_height))
3895 }
3896
3897 #[allow(deprecated)]
3898 pub fn is_blockhash_valid(
3899 &self,
3900 blockhash: &Hash,
3901 commitment: CommitmentConfig,
3902 ) -> ClientResult<bool> {
3903 let result = if self.get_node_version()? < semver::Version::new(1, 8, 0) {
3904 self.get_fee_calculator_for_blockhash_with_commitment(blockhash, commitment)?
3905 .value
3906 .is_some()
3907 } else {
3908 self.send::<Response<bool>>(
3909 RpcRequest::IsBlockhashValid,
3910 json!([blockhash.to_string(), commitment,]),
3911 )?
3912 .value
3913 };
3914 Ok(result)
3915 }
3916
3917 #[allow(deprecated)]
3918 pub fn get_fee_for_message(&self, blockhash: &Hash, message: &Message) -> ClientResult<u64> {
3919 if self.get_node_version()? < semver::Version::new(1, 8, 0) {
3920 let Fees { fee_calculator, .. } = self.get_fees()?;
3921 Ok(fee_calculator
3922 .carats_per_signature
3923 .saturating_mul(message.header.num_required_signatures as u64))
3924 } else {
3925 let serialized_encoded =
3926 serialize_and_encode::<Message>(message, UiTransactionEncoding::Base64)?;
3927 let result = self.send::<Response<Option<u64>>>(
3928 RpcRequest::GetFeeForMessage,
3929 json!([blockhash.to_string(), serialized_encoded, self.commitment()]),
3930 )?;
3931 result
3932 .value
3933 .ok_or_else(|| ClientErrorKind::Custom("Invalid blockhash".to_string()).into())
3934 }
3935 }
3936
3937 pub fn get_new_latest_blockhash(&self, blockhash: &Hash) -> ClientResult<Hash> {
3938 let mut num_retries = 0;
3939 let start = Instant::now();
3940 while start.elapsed().as_secs() < 5 {
3941 if let Ok(new_blockhash) = self.get_latest_blockhash() {
3942 if new_blockhash != *blockhash {
3943 return Ok(new_blockhash);
3944 }
3945 }
3946 debug!("Got same blockhash ({:?}), will retry...", blockhash);
3947
3948 // Retry ~twice during a slot
3949 sleep(Duration::from_millis(DEFAULT_MS_PER_SLOT / 2));
3950 num_retries += 1;
3951 }
3952 Err(RpcError::ForUser(format!(
3953 "Unable to get new blockhash after {}ms (retried {} times), stuck at {}",
3954 start.elapsed().as_millis(),
3955 num_retries,
3956 blockhash
3957 ))
3958 .into())
3959 }
3960
3961 pub fn send<T>(&self, request: RpcRequest, params: Value) -> ClientResult<T>
3962 where
3963 T: serde::de::DeserializeOwned,
3964 {
3965 assert!(params.is_array() || params.is_null());
3966
3967 let response = self
3968 .sender
3969 .send(request, params)
3970 .map_err(|err| err.into_with_request(request))?;
3971 serde_json::from_value(response)
3972 .map_err(|err| ClientError::new_with_request(err.into(), request))
3973 }
3974
3975 pub fn get_transport_stats(&self) -> RpcTransportStats {
3976 self.sender.get_transport_stats()
3977 }
3978}
3979
3980pub fn serialize_and_encode<T>(input: &T, encoding: UiTransactionEncoding) -> ClientResult<String>
3981where
3982 T: serde::ser::Serialize,
3983{
3984 let serialized = serialize(input)
3985 .map_err(|e| ClientErrorKind::Custom(format!("Serialization failed: {}", e)))?;
3986 let encoded = match encoding {
3987 UiTransactionEncoding::Base58 => bs58::encode(serialized).into_string(),
3988 UiTransactionEncoding::Base64 => base64::encode(serialized),
3989 _ => {
3990 return Err(ClientErrorKind::Custom(format!(
3991 "unsupported encoding: {}. Supported encodings: base58, base64",
3992 encoding
3993 ))
3994 .into())
3995 }
3996 };
3997 Ok(encoded)
3998}
3999
4000#[derive(Debug, Default)]
4001pub struct GetConfirmedSignaturesForAddress2Config {
4002 pub before: Option<Signature>,
4003 pub until: Option<Signature>,
4004 pub limit: Option<usize>,
4005 pub commitment: Option<CommitmentConfig>,
4006}
4007
4008fn new_spinner_progress_bar() -> ProgressBar {
4009 let progress_bar = ProgressBar::new(42);
4010 progress_bar
4011 .set_style(ProgressStyle::default_spinner().template("{spinner:.green} {wide_msg}"));
4012 progress_bar.enable_steady_tick(100);
4013 progress_bar
4014}
4015
4016fn get_rpc_request_str(rpc_addr: SocketAddr, tls: bool) -> String {
4017 if tls {
4018 format!("https://{}", rpc_addr)
4019 } else {
4020 format!("http://{}", rpc_addr)
4021 }
4022}
4023
4024fn parse_keyed_accounts(
4025 accounts: Vec<RpcKeyedAccount>,
4026 request: RpcRequest,
4027) -> ClientResult<Vec<(Pubkey, Account)>> {
4028 let mut pubkey_accounts: Vec<(Pubkey, Account)> = Vec::new();
4029 for RpcKeyedAccount { pubkey, account } in accounts.into_iter() {
4030 let pubkey = pubkey.parse().map_err(|_| {
4031 ClientError::new_with_request(
4032 RpcError::ParseError("Pubkey".to_string()).into(),
4033 request,
4034 )
4035 })?;
4036 pubkey_accounts.push((
4037 pubkey,
4038 account.decode().ok_or_else(|| {
4039 ClientError::new_with_request(
4040 RpcError::ParseError("Account from rpc".to_string()).into(),
4041 request,
4042 )
4043 })?,
4044 ));
4045 }
4046 Ok(pubkey_accounts)
4047}
4048
4049#[cfg(test)]
4050mod tests {
4051 use super::*;
4052 use crate::{client_error::ClientErrorKind, mock_sender::PUBKEY};
4053 use assert_matches::assert_matches;
4054 use jsonrpc_core::{futures::prelude::*, Error, IoHandler, Params};
4055 use jsonrpc_http_server::{AccessControlAllowOrigin, DomainsValidation, ServerBuilder};
4056 use serde_json::Number;
4057 use gemachain_sdk::{
4058 instruction::InstructionError,
4059 signature::{Keypair, Signer},
4060 system_transaction,
4061 transaction::TransactionError,
4062 };
4063 use std::{io, sync::mpsc::channel, thread};
4064
4065 #[test]
4066 fn test_send() {
4067 _test_send();
4068 }
4069
4070 #[tokio::test(flavor = "current_thread")]
4071 #[should_panic(expected = "can call blocking only when running on the multi-threaded runtime")]
4072 async fn test_send_async_current_thread_should_panic() {
4073 _test_send();
4074 }
4075
4076 #[tokio::test(flavor = "multi_thread")]
4077 async fn test_send_async_multi_thread() {
4078 _test_send();
4079 }
4080
4081 fn _test_send() {
4082 let (sender, receiver) = channel();
4083 thread::spawn(move || {
4084 let rpc_addr = "0.0.0.0:0".parse().unwrap();
4085 let mut io = IoHandler::default();
4086 // Successful request
4087 io.add_method("getBalance", |_params: Params| {
4088 future::ok(Value::Number(Number::from(50)))
4089 });
4090 // Failed request
4091 io.add_method("getRecentBlockhash", |params: Params| {
4092 if params != Params::None {
4093 future::err(Error::invalid_request())
4094 } else {
4095 future::ok(Value::String(
4096 "deadbeefXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNHhx".to_string(),
4097 ))
4098 }
4099 });
4100
4101 let server = ServerBuilder::new(io)
4102 .threads(1)
4103 .cors(DomainsValidation::AllowOnly(vec![
4104 AccessControlAllowOrigin::Any,
4105 ]))
4106 .start_http(&rpc_addr)
4107 .expect("Unable to start RPC server");
4108 sender.send(*server.address()).unwrap();
4109 server.wait();
4110 });
4111
4112 let rpc_addr = receiver.recv().unwrap();
4113 let rpc_client = RpcClient::new_socket(rpc_addr);
4114
4115 let balance: u64 = rpc_client
4116 .send(
4117 RpcRequest::GetBalance,
4118 json!(["deadbeefXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNHhx"]),
4119 )
4120 .unwrap();
4121 assert_eq!(balance, 50);
4122
4123 #[allow(deprecated)]
4124 let blockhash: String = rpc_client
4125 .send(RpcRequest::GetRecentBlockhash, Value::Null)
4126 .unwrap();
4127 assert_eq!(blockhash, "deadbeefXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNHhx");
4128
4129 // Send erroneous parameter
4130 #[allow(deprecated)]
4131 let blockhash: ClientResult<String> =
4132 rpc_client.send(RpcRequest::GetRecentBlockhash, json!(["parameter"]));
4133 assert!(blockhash.is_err());
4134 }
4135
4136 #[test]
4137 fn test_send_transaction() {
4138 let rpc_client = RpcClient::new_mock("succeeds".to_string());
4139
4140 let key = Keypair::new();
4141 let to = gemachain_sdk::pubkey::new_rand();
4142 let blockhash = Hash::default();
4143 let tx = system_transaction::transfer(&key, &to, 50, blockhash);
4144
4145 let signature = rpc_client.send_transaction(&tx);
4146 assert_eq!(signature.unwrap(), tx.signatures[0]);
4147
4148 let rpc_client = RpcClient::new_mock("fails".to_string());
4149
4150 let signature = rpc_client.send_transaction(&tx);
4151 assert!(signature.is_err());
4152
4153 // Test bad signature returned from rpc node
4154 let rpc_client = RpcClient::new_mock("malicious".to_string());
4155 let signature = rpc_client.send_transaction(&tx);
4156 assert!(signature.is_err());
4157 }
4158
4159 #[test]
4160 fn test_get_recent_blockhash() {
4161 let rpc_client = RpcClient::new_mock("succeeds".to_string());
4162
4163 let expected_blockhash: Hash = PUBKEY.parse().unwrap();
4164
4165 let blockhash = rpc_client.get_latest_blockhash().expect("blockhash ok");
4166 assert_eq!(blockhash, expected_blockhash);
4167
4168 let rpc_client = RpcClient::new_mock("fails".to_string());
4169
4170 #[allow(deprecated)]
4171 let result = rpc_client.get_recent_blockhash();
4172 assert!(result.is_err());
4173 }
4174
4175 #[test]
4176 fn test_custom_request() {
4177 let rpc_client = RpcClient::new_mock("succeeds".to_string());
4178
4179 let slot = rpc_client.get_slot().unwrap();
4180 assert_eq!(slot, 0);
4181
4182 let custom_slot = rpc_client
4183 .send::<Slot>(RpcRequest::Custom { method: "getSlot" }, Value::Null)
4184 .unwrap();
4185
4186 assert_eq!(slot, custom_slot);
4187 }
4188
4189 #[test]
4190 fn test_get_signature_status() {
4191 let signature = Signature::default();
4192
4193 let rpc_client = RpcClient::new_mock("succeeds".to_string());
4194 let status = rpc_client.get_signature_status(&signature).unwrap();
4195 assert_eq!(status, Some(Ok(())));
4196
4197 let rpc_client = RpcClient::new_mock("sig_not_found".to_string());
4198 let status = rpc_client.get_signature_status(&signature).unwrap();
4199 assert_eq!(status, None);
4200
4201 let rpc_client = RpcClient::new_mock("account_in_use".to_string());
4202 let status = rpc_client.get_signature_status(&signature).unwrap();
4203 assert_eq!(status, Some(Err(TransactionError::AccountInUse)));
4204 }
4205
4206 #[test]
4207 fn test_send_and_confirm_transaction() {
4208 let rpc_client = RpcClient::new_mock("succeeds".to_string());
4209
4210 let key = Keypair::new();
4211 let to = gemachain_sdk::pubkey::new_rand();
4212 let blockhash = Hash::default();
4213 let tx = system_transaction::transfer(&key, &to, 50, blockhash);
4214 let result = rpc_client.send_and_confirm_transaction(&tx);
4215 result.unwrap();
4216
4217 let rpc_client = RpcClient::new_mock("account_in_use".to_string());
4218 let result = rpc_client.send_and_confirm_transaction(&tx);
4219 assert!(result.is_err());
4220
4221 let rpc_client = RpcClient::new_mock("instruction_error".to_string());
4222 let result = rpc_client.send_and_confirm_transaction(&tx);
4223 assert_matches!(
4224 result.unwrap_err().kind(),
4225 ClientErrorKind::TransactionError(TransactionError::InstructionError(
4226 0,
4227 InstructionError::UninitializedAccount
4228 ))
4229 );
4230
4231 let rpc_client = RpcClient::new_mock("sig_not_found".to_string());
4232 let result = rpc_client.send_and_confirm_transaction(&tx);
4233 if let ClientErrorKind::Io(err) = result.unwrap_err().kind() {
4234 assert_eq!(err.kind(), io::ErrorKind::Other);
4235 }
4236 }
4237
4238 #[test]
4239 fn test_rpc_client_thread() {
4240 let rpc_client = RpcClient::new_mock("succeeds".to_string());
4241 thread::spawn(move || rpc_client);
4242 }
4243
4244 // Regression test that the get_block_production_with_config
4245 // method internally creates the json params array correctly.
4246 #[test]
4247 fn get_block_production_with_config_no_error() -> ClientResult<()> {
4248 let rpc_client = RpcClient::new_mock("succeeds".to_string());
4249
4250 let config = RpcBlockProductionConfig {
4251 identity: Some(Keypair::new().pubkey().to_string()),
4252 range: None,
4253 commitment: None,
4254 };
4255
4256 let prod = rpc_client.get_block_production_with_config(config)?.value;
4257
4258 assert!(!prod.by_identity.is_empty());
4259
4260 Ok(())
4261 }
4262
4263 #[test]
4264 fn test_get_latest_blockhash() {
4265 let rpc_client = RpcClient::new_mock("succeeds".to_string());
4266
4267 let expected_blockhash: Hash = PUBKEY.parse().unwrap();
4268
4269 let blockhash = rpc_client.get_latest_blockhash().expect("blockhash ok");
4270 assert_eq!(blockhash, expected_blockhash);
4271
4272 let rpc_client = RpcClient::new_mock("fails".to_string());
4273
4274 #[allow(deprecated)]
4275 let is_err = rpc_client.get_latest_blockhash().is_err();
4276 assert!(is_err);
4277 }
4278}