Skip to main content

light_client/rpc/
rpc_trait.rs

1use std::fmt::Debug;
2
3use async_trait::async_trait;
4use borsh::BorshDeserialize;
5use light_event::event::{BatchPublicTransactionEvent, PublicTransactionEvent};
6use solana_account::Account;
7use solana_clock::Slot;
8use solana_commitment_config::CommitmentConfig;
9use solana_hash::Hash;
10use solana_instruction::Instruction;
11use solana_keypair::Keypair;
12use solana_message::AddressLookupTableAccount;
13use solana_pubkey::Pubkey;
14use solana_rpc_client_api::config::RpcSendTransactionConfig;
15use solana_signature::Signature;
16use solana_transaction::{versioned::VersionedTransaction, Transaction};
17use solana_transaction_status_client_types::TransactionStatus;
18
19use super::client::RpcUrl;
20use crate::{
21    indexer::{Indexer, TreeInfo},
22    rpc::errors::RpcError,
23};
24
25#[derive(Debug, Clone)]
26pub struct LightClientConfig {
27    pub url: String,
28    pub commitment_config: Option<CommitmentConfig>,
29    /// Photon indexer URL. To pass an API key, include it in the URL:
30    /// `https://photon.helius.com?api-key=YOUR_KEY`
31    pub photon_url: Option<String>,
32    pub fetch_active_tree: bool,
33}
34
35impl LightClientConfig {
36    pub fn new(url: String, photon_url: Option<String>) -> Self {
37        Self {
38            url,
39            photon_url,
40            commitment_config: Some(CommitmentConfig::confirmed()),
41            fetch_active_tree: true,
42        }
43    }
44    pub fn local_no_indexer() -> Self {
45        Self {
46            url: RpcUrl::Localnet.to_string(),
47            commitment_config: Some(CommitmentConfig::confirmed()),
48            photon_url: None,
49            fetch_active_tree: false,
50        }
51    }
52
53    pub fn local() -> Self {
54        Self {
55            url: RpcUrl::Localnet.to_string(),
56            commitment_config: Some(CommitmentConfig::processed()),
57            photon_url: Some("http://127.0.0.1:8784".to_string()),
58            fetch_active_tree: false,
59        }
60    }
61
62    pub fn devnet(photon_url: Option<String>) -> Self {
63        Self {
64            url: RpcUrl::Devnet.to_string(),
65            photon_url,
66            commitment_config: Some(CommitmentConfig::confirmed()),
67            fetch_active_tree: true,
68        }
69    }
70}
71
72#[async_trait]
73pub trait Rpc: Send + Sync + Debug + 'static {
74    async fn new(config: LightClientConfig) -> Result<Self, RpcError>
75    where
76        Self: Sized;
77
78    fn should_retry(&self, error: &RpcError) -> bool {
79        match error {
80            // Do not retry transaction errors.
81            RpcError::ClientError(error) => error.kind.get_transaction_error().is_none(),
82            // Do not retry local transaction construction/signing errors.
83            RpcError::SigningError(_) | RpcError::TransactionBuildError(_) => false,
84            _ => true,
85        }
86    }
87
88    fn get_payer(&self) -> &Keypair;
89    fn get_url(&self) -> String;
90
91    async fn health(&self) -> Result<(), RpcError>;
92
93    async fn get_program_accounts(
94        &self,
95        program_id: &Pubkey,
96    ) -> Result<Vec<(Pubkey, Account)>, RpcError>;
97
98    async fn get_program_accounts_with_discriminator(
99        &self,
100        program_id: &Pubkey,
101        discriminator: &[u8],
102    ) -> Result<Vec<(Pubkey, Account)>, RpcError>;
103
104    // TODO: add send transaction with config
105
106    async fn confirm_transaction(&self, signature: Signature) -> Result<bool, RpcError>;
107
108    /// Returns an account struct.
109    async fn get_account(&self, address: Pubkey) -> Result<Option<Account>, RpcError>;
110
111    /// Returns multiple account structs.
112    async fn get_multiple_accounts(
113        &self,
114        addresses: &[Pubkey],
115    ) -> Result<Vec<Option<Account>>, RpcError>;
116
117    /// Returns an a borsh deserialized account.
118    /// Deserialization skips the discriminator.
119    async fn get_anchor_account<T: BorshDeserialize>(
120        &self,
121        pubkey: &Pubkey,
122    ) -> Result<Option<T>, RpcError> {
123        match self.get_account(*pubkey).await? {
124            Some(account) => {
125                let data = T::deserialize(&mut &account.data[8..]).map_err(RpcError::from)?;
126                Ok(Some(data))
127            }
128            None => Ok(None),
129        }
130    }
131
132    async fn get_minimum_balance_for_rent_exemption(
133        &self,
134        data_len: usize,
135    ) -> Result<u64, RpcError>;
136
137    async fn airdrop_lamports(&mut self, to: &Pubkey, lamports: u64)
138        -> Result<Signature, RpcError>;
139
140    async fn get_balance(&self, pubkey: &Pubkey) -> Result<u64, RpcError>;
141    async fn get_latest_blockhash(&mut self) -> Result<(Hash, u64), RpcError>;
142    async fn get_block_height(&self) -> Result<u64, RpcError>;
143    async fn get_slot(&self) -> Result<u64, RpcError>;
144    async fn get_transaction_slot(&self, signature: &Signature) -> Result<u64, RpcError>;
145    async fn get_signature_statuses(
146        &self,
147        signatures: &[Signature],
148    ) -> Result<Vec<Option<TransactionStatus>>, RpcError>;
149
150    async fn send_transaction(&self, transaction: &Transaction) -> Result<Signature, RpcError>;
151
152    async fn send_transaction_with_config(
153        &self,
154        transaction: &Transaction,
155        config: RpcSendTransactionConfig,
156    ) -> Result<Signature, RpcError>;
157
158    async fn send_versioned_transaction_with_config(
159        &self,
160        transaction: &VersionedTransaction,
161        config: RpcSendTransactionConfig,
162    ) -> Result<Signature, RpcError>;
163
164    async fn process_transaction(
165        &mut self,
166        transaction: Transaction,
167    ) -> Result<Signature, RpcError>;
168
169    async fn process_versioned_transaction(
170        &mut self,
171        transaction: VersionedTransaction,
172    ) -> Result<Signature, RpcError>;
173
174    async fn process_transaction_with_context(
175        &mut self,
176        transaction: Transaction,
177    ) -> Result<(Signature, Slot), RpcError>;
178
179    async fn create_and_send_transaction_with_event<T>(
180        &mut self,
181        instructions: &[Instruction],
182        authority: &Pubkey,
183        signers: &[&Keypair],
184    ) -> Result<Option<(T, Signature, Slot)>, RpcError>
185    where
186        T: BorshDeserialize + Send + Debug;
187
188    async fn create_and_send_transaction<'a>(
189        &'a mut self,
190        instructions: &'a [Instruction],
191        payer: &'a Pubkey,
192        signers: &'a [&'a Keypair],
193    ) -> Result<Signature, RpcError> {
194        let blockhash = self.get_latest_blockhash().await?.0;
195        let mut transaction = Transaction::new_with_payer(instructions, Some(payer));
196        transaction
197            .try_sign(signers, blockhash)
198            .map_err(|e| RpcError::SigningError(e.to_string()))?;
199        self.process_transaction(transaction).await
200    }
201
202    async fn create_and_send_versioned_transaction<'a>(
203        &'a mut self,
204        instructions: &'a [Instruction],
205        payer: &'a Pubkey,
206        signers: &'a [&'a Keypair],
207        address_lookup_tables: &'a [AddressLookupTableAccount],
208    ) -> Result<Signature, RpcError>;
209
210    async fn create_and_send_transaction_with_public_event(
211        &mut self,
212        instruction: &[Instruction],
213        payer: &Pubkey,
214        signers: &[&Keypair],
215    ) -> Result<Option<(PublicTransactionEvent, Signature, Slot)>, RpcError>;
216
217    async fn create_and_send_transaction_with_batched_event(
218        &mut self,
219        instruction: &[Instruction],
220        payer: &Pubkey,
221        signers: &[&Keypair],
222    ) -> Result<Option<(Vec<BatchPublicTransactionEvent>, Signature, Slot)>, RpcError>;
223
224    fn indexer(&self) -> Result<&impl Indexer, RpcError>;
225    fn indexer_mut(&mut self) -> Result<&mut impl Indexer, RpcError>;
226
227    /// Fetch the latest state tree addresses from the cluster.
228    async fn get_latest_active_state_trees(&mut self) -> Result<Vec<TreeInfo>, RpcError>;
229
230    /// Gets state tree infos.
231    /// State trees are cached and have to be fetched or set.
232    fn get_state_tree_infos(&self) -> Vec<TreeInfo>;
233
234    /// Gets a random state tree info.
235    /// State trees are cached and have to be fetched or set.
236    /// Returns v1 state trees by default, v2 state trees when v2 feature is enabled.
237    fn get_random_state_tree_info(&self) -> Result<TreeInfo, RpcError>;
238
239    /// Gets a random v1 state tree info.
240    /// State trees are cached and have to be fetched or set.
241    fn get_random_state_tree_info_v1(&self) -> Result<TreeInfo, RpcError>;
242
243    fn get_address_tree_v1(&self) -> TreeInfo;
244
245    fn get_address_tree_v2(&self) -> TreeInfo;
246}