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 RpcError::ClientError(error) => error.kind.get_transaction_error().is_none(),
86 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 async fn confirm_transaction(&self, signature: Signature) -> Result<bool, RpcError>;
104
105 async fn get_account(&self, address: Pubkey) -> Result<Option<Account>, RpcError>;
107
108 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 async fn get_latest_active_state_trees(&mut self) -> Result<Vec<TreeInfo>, RpcError>;
200
201 fn get_state_tree_infos(&self) -> Vec<TreeInfo>;
204
205 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}