blocking.rs 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. use crate::{
  2. ClientError, Config, EventContext, EventUnsubscriber, Program, ProgramAccountsIterator,
  3. RequestBuilder,
  4. };
  5. use anchor_lang::{prelude::Pubkey, AccountDeserialize, Discriminator};
  6. #[cfg(not(feature = "mock"))]
  7. use solana_client::rpc_client::RpcClient;
  8. use solana_client::{
  9. nonblocking::rpc_client::RpcClient as AsyncRpcClient, rpc_config::RpcSendTransactionConfig,
  10. rpc_filter::RpcFilterType,
  11. };
  12. use solana_sdk::{
  13. commitment_config::CommitmentConfig, signature::Signature, signer::Signer,
  14. transaction::Transaction,
  15. };
  16. use std::{marker::PhantomData, ops::Deref, sync::Arc};
  17. use tokio::{
  18. runtime::{Builder, Handle},
  19. sync::RwLock,
  20. };
  21. impl<'a> EventUnsubscriber<'a> {
  22. /// Unsubscribe gracefully.
  23. pub fn unsubscribe(self) {
  24. self.runtime_handle.block_on(self.unsubscribe_internal())
  25. }
  26. }
  27. impl<C: Deref<Target = impl Signer> + Clone> Program<C> {
  28. pub fn new(
  29. program_id: Pubkey,
  30. cfg: Config<C>,
  31. #[cfg(feature = "mock")] rpc_client: AsyncRpcClient,
  32. ) -> Result<Self, ClientError> {
  33. let rt: tokio::runtime::Runtime = Builder::new_multi_thread().enable_all().build()?;
  34. #[cfg(not(feature = "mock"))]
  35. let rpc_client = {
  36. let comm_config = cfg.options.unwrap_or_default();
  37. let cluster_url = cfg.cluster.url().to_string();
  38. AsyncRpcClient::new_with_commitment(cluster_url.clone(), comm_config)
  39. };
  40. Ok(Self {
  41. program_id,
  42. cfg,
  43. sub_client: Arc::new(RwLock::new(None)),
  44. internal_rpc_client: rpc_client,
  45. rt,
  46. })
  47. }
  48. // We disable the `rpc` method for `mock` feature because otherwise we'd either have to
  49. // return a new `RpcClient` instance (which is different to the one used internally)
  50. // or require the user to pass another one in for blocking (since we use the non-blocking one under the hood).
  51. // The former of these would be confusing and the latter would be very annoying, especially since a user
  52. // using the mock feature likely already has a `RpcClient` instance at hand anyway.
  53. #[cfg(not(feature = "mock"))]
  54. pub fn rpc(&self) -> RpcClient {
  55. RpcClient::new_with_commitment(
  56. self.cfg.cluster.url().to_string(),
  57. self.cfg.options.unwrap_or_default(),
  58. )
  59. }
  60. /// Returns a request builder.
  61. pub fn request(&self) -> RequestBuilder<'_, C, Box<dyn Signer + '_>> {
  62. RequestBuilder::from(
  63. self.program_id,
  64. self.cfg.cluster.url(),
  65. self.cfg.payer.clone(),
  66. self.cfg.options,
  67. #[cfg(not(feature = "async"))]
  68. self.rt.handle(),
  69. &self.internal_rpc_client,
  70. )
  71. }
  72. /// Returns the account at the given address.
  73. pub fn account<T: AccountDeserialize>(&self, address: Pubkey) -> Result<T, ClientError> {
  74. self.rt.block_on(self.account_internal(address))
  75. }
  76. /// Returns all program accounts of the given type matching the given filters
  77. pub fn accounts<T: AccountDeserialize + Discriminator>(
  78. &self,
  79. filters: Vec<RpcFilterType>,
  80. ) -> Result<Vec<(Pubkey, T)>, ClientError> {
  81. self.accounts_lazy(filters)?.collect()
  82. }
  83. /// Returns all program accounts of the given type matching the given filters as an iterator
  84. /// Deserialization is executed lazily
  85. pub fn accounts_lazy<T: AccountDeserialize + Discriminator>(
  86. &self,
  87. filters: Vec<RpcFilterType>,
  88. ) -> Result<ProgramAccountsIterator<T>, ClientError> {
  89. self.rt.block_on(self.accounts_lazy_internal(filters))
  90. }
  91. pub fn on<T: anchor_lang::Event + anchor_lang::AnchorDeserialize>(
  92. &self,
  93. f: impl Fn(&EventContext, T) + Send + 'static,
  94. ) -> Result<EventUnsubscriber, ClientError> {
  95. let (handle, rx) = self.rt.block_on(self.on_internal(f))?;
  96. Ok(EventUnsubscriber {
  97. handle,
  98. rx,
  99. runtime_handle: self.rt.handle(),
  100. _lifetime_marker: PhantomData,
  101. })
  102. }
  103. }
  104. impl<'a, C: Deref<Target = impl Signer> + Clone> RequestBuilder<'a, C, Box<dyn Signer + 'a>> {
  105. pub fn from(
  106. program_id: Pubkey,
  107. cluster: &str,
  108. payer: C,
  109. options: Option<CommitmentConfig>,
  110. handle: &'a Handle,
  111. rpc_client: &'a AsyncRpcClient,
  112. ) -> Self {
  113. Self {
  114. program_id,
  115. payer,
  116. cluster: cluster.to_string(),
  117. accounts: Vec::new(),
  118. options: options.unwrap_or_default(),
  119. instructions: Vec::new(),
  120. instruction_data: None,
  121. signers: Vec::new(),
  122. handle,
  123. internal_rpc_client: rpc_client,
  124. _phantom: PhantomData,
  125. }
  126. }
  127. #[must_use]
  128. pub fn signer<T: Signer + 'a>(mut self, signer: T) -> Self {
  129. self.signers.push(Box::new(signer));
  130. self
  131. }
  132. pub fn signed_transaction(&self) -> Result<Transaction, ClientError> {
  133. self.handle.block_on(self.signed_transaction_internal())
  134. }
  135. pub fn send(&self) -> Result<Signature, ClientError> {
  136. self.handle.block_on(self.send_internal())
  137. }
  138. pub fn send_with_spinner_and_config(
  139. &self,
  140. config: RpcSendTransactionConfig,
  141. ) -> Result<Signature, ClientError> {
  142. self.handle
  143. .block_on(self.send_with_spinner_and_config_internal(config))
  144. }
  145. }