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::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    pub photon_url: Option<String>,
30    pub api_key: Option<String>,
31    pub fetch_active_tree: bool,
32}
33
34impl LightClientConfig {
35    pub fn new(url: String, photon_url: Option<String>, api_key: Option<String>) -> Self {
36        Self {
37            url,
38            photon_url,
39            api_key,
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            api_key: None,
50            fetch_active_tree: false,
51        }
52    }
53
54    pub fn local() -> Self {
55        Self {
56            url: RpcUrl::Localnet.to_string(),
57            commitment_config: Some(CommitmentConfig::processed()),
58            photon_url: Some("http://127.0.0.1:8784".to_string()),
59            api_key: None,
60            fetch_active_tree: false,
61        }
62    }
63
64    pub fn devnet(photon_url: Option<String>, api_key: Option<String>) -> Self {
65        Self {
66            url: RpcUrl::Devnet.to_string(),
67            photon_url,
68            api_key,
69            commitment_config: Some(CommitmentConfig::confirmed()),
70            fetch_active_tree: true,
71        }
72    }
73}
74
75#[async_trait]
76pub trait Rpc: Send + Sync + Debug + 'static {
77    async fn new(config: LightClientConfig) -> Result<Self, RpcError>
78    where
79        Self: Sized;
80
81    fn should_retry(&self, error: &RpcError) -> bool {
82        match error {
83            // Do not retry transaction errors.
84            RpcError::ClientError(error) => error.kind.get_transaction_error().is_none(),
85            // Do not retry signing errors.
86            RpcError::SigningError(_) => false,
87            _ => true,
88        }
89    }
90
91    fn get_payer(&self) -> &Keypair;
92    fn get_url(&self) -> String;
93
94    async fn health(&self) -> Result<(), RpcError>;
95
96    async fn get_program_accounts(
97        &self,
98        program_id: &Pubkey,
99    ) -> Result<Vec<(Pubkey, Account)>, RpcError>;
100    // TODO: add send transaction with config
101
102    async fn confirm_transaction(&self, signature: Signature) -> Result<bool, RpcError>;
103
104    /// Returns an account struct.
105    async fn get_account(&self, address: Pubkey) -> Result<Option<Account>, RpcError>;
106
107    /// Returns multiple account structs.
108    async fn get_multiple_accounts(
109        &self,
110        addresses: &[Pubkey],
111    ) -> Result<Vec<Option<Account>>, RpcError>;
112
113    /// Returns an a borsh deserialized account.
114    /// Deserialization skips the discriminator.
115    async fn get_anchor_account<T: BorshDeserialize>(
116        &self,
117        pubkey: &Pubkey,
118    ) -> Result<Option<T>, RpcError> {
119        match self.get_account(*pubkey).await? {
120            Some(account) => {
121                let data = T::deserialize(&mut &account.data[8..]).map_err(RpcError::from)?;
122                Ok(Some(data))
123            }
124            None => Ok(None),
125        }
126    }
127
128    async fn get_minimum_balance_for_rent_exemption(
129        &self,
130        data_len: usize,
131    ) -> Result<u64, RpcError>;
132
133    async fn airdrop_lamports(&mut self, to: &Pubkey, lamports: u64)
134        -> Result<Signature, RpcError>;
135
136    async fn get_balance(&self, pubkey: &Pubkey) -> Result<u64, RpcError>;
137    async fn get_latest_blockhash(&mut self) -> Result<(Hash, u64), RpcError>;
138    async fn get_slot(&self) -> Result<u64, RpcError>;
139    async fn get_transaction_slot(&self, signature: &Signature) -> Result<u64, RpcError>;
140    async fn get_signature_statuses(
141        &self,
142        signatures: &[Signature],
143    ) -> Result<Vec<Option<TransactionStatus>>, RpcError>;
144
145    async fn send_transaction(&self, transaction: &Transaction) -> Result<Signature, RpcError>;
146
147    async fn send_transaction_with_config(
148        &self,
149        transaction: &Transaction,
150        config: RpcSendTransactionConfig,
151    ) -> Result<Signature, RpcError>;
152
153    async fn process_transaction(
154        &mut self,
155        transaction: Transaction,
156    ) -> Result<Signature, RpcError>;
157
158    async fn process_transaction_with_context(
159        &mut self,
160        transaction: Transaction,
161    ) -> Result<(Signature, Slot), RpcError>;
162
163    async fn create_and_send_transaction_with_event<T>(
164        &mut self,
165        instructions: &[Instruction],
166        authority: &Pubkey,
167        signers: &[&Keypair],
168    ) -> Result<Option<(T, Signature, Slot)>, RpcError>
169    where
170        T: BorshDeserialize + Send + Debug;
171
172    async fn create_and_send_transaction<'a>(
173        &'a mut self,
174        instructions: &'a [Instruction],
175        payer: &'a Pubkey,
176        signers: &'a [&'a Keypair],
177    ) -> Result<Signature, RpcError> {
178        let blockhash = self.get_latest_blockhash().await?.0;
179        let mut transaction = Transaction::new_with_payer(instructions, Some(payer));
180        transaction
181            .try_sign(signers, blockhash)
182            .map_err(|e| RpcError::SigningError(e.to_string()))?;
183        self.process_transaction(transaction).await
184    }
185
186    async fn create_and_send_versioned_transaction<'a>(
187        &'a mut self,
188        instructions: &'a [Instruction],
189        payer: &'a Pubkey,
190        signers: &'a [&'a Keypair],
191        address_lookup_tables: &'a [AddressLookupTableAccount],
192    ) -> Result<Signature, RpcError>;
193
194    async fn create_and_send_transaction_with_public_event(
195        &mut self,
196        instruction: &[Instruction],
197        payer: &Pubkey,
198        signers: &[&Keypair],
199    ) -> Result<Option<(PublicTransactionEvent, Signature, Slot)>, RpcError>;
200
201    async fn create_and_send_transaction_with_batched_event(
202        &mut self,
203        instruction: &[Instruction],
204        payer: &Pubkey,
205        signers: &[&Keypair],
206    ) -> Result<Option<(Vec<BatchPublicTransactionEvent>, Signature, Slot)>, RpcError>;
207
208    fn indexer(&self) -> Result<&impl Indexer, RpcError>;
209    fn indexer_mut(&mut self) -> Result<&mut impl Indexer, RpcError>;
210
211    /// Fetch the latest state tree addresses from the cluster.
212    async fn get_latest_active_state_trees(&mut self) -> Result<Vec<TreeInfo>, RpcError>;
213
214    /// Gets state tree infos.
215    /// State trees are cached and have to be fetched or set.
216    fn get_state_tree_infos(&self) -> Vec<TreeInfo>;
217
218    /// Gets a random state tree info.
219    /// State trees are cached and have to be fetched or set.
220    /// Returns v1 state trees by default, v2 state trees when v2 feature is enabled.
221    fn get_random_state_tree_info(&self) -> Result<TreeInfo, RpcError>;
222
223    /// Gets a random v1 state tree info.
224    /// State trees are cached and have to be fetched or set.
225    fn get_random_state_tree_info_v1(&self) -> Result<TreeInfo, RpcError>;
226
227    fn get_address_tree_v1(&self) -> TreeInfo;
228
229    fn get_address_tree_v2(&self) -> TreeInfo;
230}