anchor_client/
nonblocking.rs

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