1use std::str::FromStr;
2
3use serde_json::json;
4use solana_account_decoder::{encode_ui_account, UiAccountEncoding};
5use solana_client::{
6 nonblocking::rpc_client::RpcClient,
7 rpc_client::GetConfirmedSignaturesForAddress2Config,
8 rpc_config::{
9 RpcAccountInfoConfig, RpcLargestAccountsConfig, RpcProgramAccountsConfig,
10 RpcSignaturesForAddressConfig, RpcTokenAccountsFilter,
11 },
12 rpc_filter::RpcFilterType,
13 rpc_request::{RpcRequest, TokenAccountsFilter},
14 rpc_response::{
15 RpcAccountBalance, RpcConfirmedTransactionStatusWithSignature, RpcKeyedAccount, RpcResult,
16 RpcTokenAccountBalance,
17 },
18};
19use solana_commitment_config::CommitmentConfig;
20use solana_epoch_info::EpochInfo;
21use solana_hash::Hash;
22use solana_pubkey::Pubkey;
23use solana_sdk::bpf_loader_upgradeable::get_program_data_address;
24use solana_signature::Signature;
25use solana_transaction_status::UiTransactionEncoding;
26
27use super::GetTransactionResult;
28use crate::{
29 error::{SurfpoolError, SurfpoolResult},
30 surfnet::{locker::is_supported_token_program, GetAccountResult},
31};
32
33pub struct SurfnetRemoteClient {
34 pub client: RpcClient,
35}
36impl Clone for SurfnetRemoteClient {
37 fn clone(&self) -> Self {
38 let remote_rpc_url = self.client.url();
39 SurfnetRemoteClient {
40 client: RpcClient::new(remote_rpc_url),
41 }
42 }
43}
44
45pub trait SomeRemoteCtx {
46 fn get_remote_ctx<T>(&self, input: T) -> Option<(SurfnetRemoteClient, T)>;
47}
48
49impl SomeRemoteCtx for Option<SurfnetRemoteClient> {
50 fn get_remote_ctx<T>(&self, input: T) -> Option<(SurfnetRemoteClient, T)> {
51 self.as_ref()
52 .map(|remote_rpc_client| (remote_rpc_client.clone(), input))
53 }
54}
55
56impl SurfnetRemoteClient {
57 pub fn new(remote_rpc_url: &str) -> Self {
58 SurfnetRemoteClient {
59 client: RpcClient::new(remote_rpc_url.to_string()),
60 }
61 }
62
63 pub async fn get_epoch_info(&self) -> SurfpoolResult<EpochInfo> {
64 self.client.get_epoch_info().await.map_err(Into::into)
65 }
66
67 pub async fn get_account(
68 &self,
69 pubkey: &Pubkey,
70 commitment_config: CommitmentConfig,
71 ) -> SurfpoolResult<GetAccountResult> {
72 let res = self
73 .client
74 .get_account_with_commitment(pubkey, commitment_config)
75 .await
76 .map_err(|e| SurfpoolError::get_account(*pubkey, e))?;
77
78 let result = match res.value {
79 Some(account) => {
80 if !account.executable {
81 GetAccountResult::FoundAccount(
82 *pubkey, account,
83 true,
85 )
86 } else {
87 let program_data_address = get_program_data_address(pubkey);
88
89 let program_data = self
90 .client
91 .get_account_with_commitment(&program_data_address, commitment_config)
92 .await
93 .map_err(|e| SurfpoolError::get_account(*pubkey, e))?;
94
95 GetAccountResult::FoundProgramAccount(
96 (*pubkey, account),
97 (program_data_address, program_data.value),
98 )
99 }
100 }
101 None => GetAccountResult::None(*pubkey),
102 };
103 Ok(result)
104 }
105
106 pub async fn get_multiple_accounts(
107 &self,
108 pubkeys: &[Pubkey],
109 commitment_config: CommitmentConfig,
110 ) -> SurfpoolResult<Vec<GetAccountResult>> {
111 let remote_accounts = self
112 .client
113 .get_multiple_accounts(pubkeys)
114 .await
115 .map_err(SurfpoolError::get_multiple_accounts)?;
116
117 let mut accounts_result = vec![];
118 for (pubkey, remote_account) in pubkeys.iter().zip(remote_accounts) {
119 if let Some(remote_account) = remote_account {
120 if !remote_account.executable {
121 accounts_result.push(GetAccountResult::FoundAccount(
122 *pubkey,
123 remote_account,
124 true,
126 ));
127 } else {
128 let program_data_address = get_program_data_address(pubkey);
129
130 let program_data = self
131 .client
132 .get_account_with_commitment(&program_data_address, commitment_config)
133 .await
134 .map_err(|e| SurfpoolError::get_account(*pubkey, e))?;
135
136 accounts_result.push(GetAccountResult::FoundProgramAccount(
137 (*pubkey, remote_account),
138 (program_data_address, program_data.value),
139 ));
140 }
141 } else {
142 accounts_result.push(GetAccountResult::None(*pubkey));
143 }
144 }
145 Ok(accounts_result)
146 }
147
148 pub async fn get_transaction(
149 &self,
150 signature: Signature,
151 encoding: Option<UiTransactionEncoding>,
152 latest_absolute_slot: u64,
153 ) -> GetTransactionResult {
154 match self
155 .client
156 .get_transaction(
157 &signature,
158 encoding.unwrap_or(UiTransactionEncoding::Base64),
159 )
160 .await
161 {
162 Ok(tx) => GetTransactionResult::found_transaction(signature, tx, latest_absolute_slot),
163 Err(_) => GetTransactionResult::None(signature),
164 }
165 }
166
167 pub async fn get_token_accounts_by_owner(
168 &self,
169 owner: Pubkey,
170 filter: &TokenAccountsFilter,
171 config: &RpcAccountInfoConfig,
172 ) -> SurfpoolResult<Vec<RpcKeyedAccount>> {
173 let token_account_filter = match filter {
174 TokenAccountsFilter::Mint(mint) => RpcTokenAccountsFilter::Mint(mint.to_string()),
175 TokenAccountsFilter::ProgramId(program_id) => {
176 RpcTokenAccountsFilter::ProgramId(program_id.to_string())
177 }
178 };
179
180 let res: RpcResult<Vec<RpcKeyedAccount>> = self
183 .client
184 .send(
185 RpcRequest::GetTokenAccountsByOwner,
186 json!([owner.to_string(), token_account_filter, config]),
187 )
188 .await;
189 res.map_err(|e| SurfpoolError::get_token_accounts(owner, filter, e))
190 .map(|res| res.value)
191 }
192
193 pub async fn get_token_largest_accounts(
194 &self,
195 mint: &Pubkey,
196 commitment_config: CommitmentConfig,
197 ) -> SurfpoolResult<Vec<RpcTokenAccountBalance>> {
198 self.client
199 .get_token_largest_accounts_with_commitment(mint, commitment_config)
200 .await
201 .map(|response| response.value)
202 .map_err(|e| SurfpoolError::get_token_largest_accounts(*mint, e))
203 }
204
205 pub async fn get_token_accounts_by_delegate(
206 &self,
207 delegate: Pubkey,
208 filter: &TokenAccountsFilter,
209 config: &RpcAccountInfoConfig,
210 ) -> SurfpoolResult<Vec<RpcKeyedAccount>> {
211 if let TokenAccountsFilter::ProgramId(program_id) = &filter {
213 if !is_supported_token_program(program_id) {
214 return Err(SurfpoolError::unsupported_token_program(*program_id));
215 }
216 }
217
218 let token_account_filter = match &filter {
219 TokenAccountsFilter::Mint(mint) => RpcTokenAccountsFilter::Mint(mint.to_string()),
220 TokenAccountsFilter::ProgramId(program_id) => {
221 RpcTokenAccountsFilter::ProgramId(program_id.to_string())
222 }
223 };
224
225 let res: RpcResult<Vec<RpcKeyedAccount>> = self
226 .client
227 .send(
228 RpcRequest::GetTokenAccountsByDelegate,
229 json!([delegate.to_string(), token_account_filter, config]),
230 )
231 .await;
232
233 res.map_err(|e| SurfpoolError::get_token_accounts_by_delegate_error(delegate, filter, e))
234 .map(|res| res.value)
235 }
236
237 pub async fn get_program_accounts(
238 &self,
239 program_id: &Pubkey,
240 account_config: RpcAccountInfoConfig,
241 filters: Option<Vec<RpcFilterType>>,
242 ) -> SurfpoolResult<Vec<RpcKeyedAccount>> {
243 let encoding = account_config.encoding.unwrap_or(UiAccountEncoding::Base64);
244 let data_slice = account_config.data_slice;
245 self.client
246 .get_program_accounts_with_config(
247 program_id,
248 RpcProgramAccountsConfig {
249 filters,
250 with_context: Some(false),
251 account_config,
252 ..Default::default()
253 },
254 )
255 .await
256 .map(|accounts| {
257 accounts
258 .iter()
259 .map(|(pubkey, account)| RpcKeyedAccount {
260 pubkey: pubkey.to_string(),
261 account: encode_ui_account(pubkey, account, encoding, None, data_slice),
262 })
263 .collect()
264 })
265 .map_err(|e| SurfpoolError::get_program_accounts(*program_id, e))
266 }
267
268 pub async fn get_largest_accounts(
269 &self,
270 config: Option<RpcLargestAccountsConfig>,
271 ) -> SurfpoolResult<Vec<RpcAccountBalance>> {
272 self.client
273 .get_largest_accounts_with_config(config.unwrap_or_default())
274 .await
275 .map(|res| res.value)
276 .map_err(SurfpoolError::get_largest_accounts)
277 }
278
279 pub async fn get_genesis_hash(&self) -> SurfpoolResult<Hash> {
280 self.client.get_genesis_hash().await.map_err(Into::into)
281 }
282
283 pub async fn get_signatures_for_address(
284 &self,
285 pubkey: &Pubkey,
286 config: Option<RpcSignaturesForAddressConfig>,
287 ) -> SurfpoolResult<Vec<RpcConfirmedTransactionStatusWithSignature>> {
288 let c = match config {
289 Some(c) => GetConfirmedSignaturesForAddress2Config {
290 before: c.before.and_then(|s| Signature::from_str(&s).ok()),
291 commitment: c.commitment,
292 limit: c.limit,
293 until: c.until.and_then(|s| Signature::from_str(&s).ok()),
294 },
295 _ => GetConfirmedSignaturesForAddress2Config::default(),
296 };
297 self.client
298 .get_signatures_for_address_with_config(pubkey, c)
299 .await
300 .map_err(SurfpoolError::get_signatures_for_address)
301 }
302}