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 an a borsh deserialized account.
107    /// Deserialization skips the discriminator.
108    async fn get_anchor_account<T: BorshDeserialize>(
109        &self,
110        pubkey: &Pubkey,
111    ) -> Result<Option<T>, RpcError> {
112        match self.get_account(*pubkey).await? {
113            Some(account) => {
114                let data = T::deserialize(&mut &account.data[8..]).map_err(RpcError::from)?;
115                Ok(Some(data))
116            }
117            None => Ok(None),
118        }
119    }
120
121    async fn get_minimum_balance_for_rent_exemption(
122        &self,
123        data_len: usize,
124    ) -> Result<u64, RpcError>;
125
126    async fn airdrop_lamports(&mut self, to: &Pubkey, lamports: u64)
127        -> Result<Signature, RpcError>;
128
129    async fn get_balance(&self, pubkey: &Pubkey) -> Result<u64, RpcError>;
130    async fn get_latest_blockhash(&mut self) -> Result<(Hash, u64), RpcError>;
131    async fn get_slot(&self) -> Result<u64, RpcError>;
132    async fn get_transaction_slot(&self, signature: &Signature) -> Result<u64, RpcError>;
133    async fn get_signature_statuses(
134        &self,
135        signatures: &[Signature],
136    ) -> Result<Vec<Option<TransactionStatus>>, RpcError>;
137
138    async fn send_transaction(&self, transaction: &Transaction) -> Result<Signature, RpcError>;
139
140    async fn send_transaction_with_config(
141        &self,
142        transaction: &Transaction,
143        config: RpcSendTransactionConfig,
144    ) -> Result<Signature, RpcError>;
145
146    async fn process_transaction(
147        &mut self,
148        transaction: Transaction,
149    ) -> Result<Signature, RpcError>;
150
151    async fn process_transaction_with_context(
152        &mut self,
153        transaction: Transaction,
154    ) -> Result<(Signature, Slot), RpcError>;
155
156    async fn create_and_send_transaction_with_event<T>(
157        &mut self,
158        instructions: &[Instruction],
159        authority: &Pubkey,
160        signers: &[&Keypair],
161    ) -> Result<Option<(T, Signature, Slot)>, RpcError>
162    where
163        T: BorshDeserialize + Send + Debug;
164
165    async fn create_and_send_transaction<'a>(
166        &'a mut self,
167        instructions: &'a [Instruction],
168        payer: &'a Pubkey,
169        signers: &'a [&'a Keypair],
170    ) -> Result<Signature, RpcError> {
171        let blockhash = self.get_latest_blockhash().await?.0;
172        let mut transaction = Transaction::new_with_payer(instructions, Some(payer));
173        transaction
174            .try_sign(signers, blockhash)
175            .map_err(|e| RpcError::SigningError(e.to_string()))?;
176        self.process_transaction(transaction).await
177    }
178
179    async fn create_and_send_transaction_with_public_event(
180        &mut self,
181        instruction: &[Instruction],
182        payer: &Pubkey,
183        signers: &[&Keypair],
184    ) -> Result<Option<(PublicTransactionEvent, Signature, Slot)>, RpcError>;
185
186    async fn create_and_send_transaction_with_batched_event(
187        &mut self,
188        instruction: &[Instruction],
189        payer: &Pubkey,
190        signers: &[&Keypair],
191    ) -> Result<Option<(Vec<BatchPublicTransactionEvent>, Signature, Slot)>, RpcError>;
192
193    fn indexer(&self) -> Result<&impl Indexer, RpcError>;
194    fn indexer_mut(&mut self) -> Result<&mut impl Indexer, RpcError>;
195
196    /// Fetch the latest state tree addresses from the cluster.
197    async fn get_latest_active_state_trees(&mut self) -> Result<Vec<TreeInfo>, RpcError>;
198
199    /// Gets state tree infos.
200    /// State trees are cached and have to be fetched or set.
201    fn get_state_tree_infos(&self) -> Vec<TreeInfo>;
202
203    /// Gets a random state tree info.
204    /// State trees are cached and have to be fetched or set.
205    /// Returns v1 state trees by default, v2 state trees when v2 feature is enabled.
206    fn get_random_state_tree_info(&self) -> Result<TreeInfo, RpcError>;
207
208    /// Gets a random v1 state tree info.
209    /// State trees are cached and have to be fetched or set.
210    fn get_random_state_tree_info_v1(&self) -> Result<TreeInfo, RpcError>;
211
212    fn get_address_tree_v1(&self) -> TreeInfo;
213
214    fn get_address_tree_v2(&self) -> TreeInfo;
215}