light_client/rpc/
rpc_trait.rs

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