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