surfpool_core/surfnet/
remote.rs

1use solana_account_decoder::{encode_ui_account, UiAccountEncoding};
2use solana_client::{
3    nonblocking::rpc_client::RpcClient,
4    rpc_config::{RpcAccountInfoConfig, RpcProgramAccountsConfig},
5    rpc_filter::RpcFilterType,
6    rpc_request::TokenAccountsFilter,
7    rpc_response::RpcKeyedAccount,
8};
9use solana_commitment_config::CommitmentConfig;
10use solana_epoch_info::EpochInfo;
11use solana_pubkey::Pubkey;
12use solana_sdk::bpf_loader_upgradeable::get_program_data_address;
13use solana_signature::Signature;
14use solana_transaction_status::UiTransactionEncoding;
15
16use super::GetTransactionResult;
17use crate::{
18    error::{SurfpoolError, SurfpoolResult},
19    surfnet::GetAccountResult,
20};
21
22pub struct SurfnetRemoteClient {
23    pub client: RpcClient,
24}
25impl Clone for SurfnetRemoteClient {
26    fn clone(&self) -> Self {
27        let remote_rpc_url = self.client.url();
28        SurfnetRemoteClient {
29            client: RpcClient::new(remote_rpc_url),
30        }
31    }
32}
33
34pub trait SomeRemoteCtx {
35    fn get_remote_ctx<T>(&self, input: T) -> Option<(SurfnetRemoteClient, T)>;
36}
37
38impl SomeRemoteCtx for Option<SurfnetRemoteClient> {
39    fn get_remote_ctx<T>(&self, input: T) -> Option<(SurfnetRemoteClient, T)> {
40        self.as_ref()
41            .map(|remote_rpc_client| (remote_rpc_client.clone(), input))
42    }
43}
44
45impl SurfnetRemoteClient {
46    pub fn new(remote_rpc_url: &str) -> Self {
47        SurfnetRemoteClient {
48            client: RpcClient::new(remote_rpc_url.to_string()),
49        }
50    }
51
52    pub async fn get_epoch_info(&self) -> SurfpoolResult<EpochInfo> {
53        self.client.get_epoch_info().await.map_err(Into::into)
54    }
55
56    pub async fn get_account(
57        &self,
58        pubkey: &Pubkey,
59        commitment_config: CommitmentConfig,
60    ) -> SurfpoolResult<GetAccountResult> {
61        let res = self
62            .client
63            .get_account_with_commitment(pubkey, commitment_config)
64            .await
65            .map_err(|e| SurfpoolError::get_account(*pubkey, e))?;
66
67        let result = match res.value {
68            Some(account) => {
69                if !account.executable {
70                    GetAccountResult::FoundAccount(
71                        *pubkey, account,
72                        // Mark this account as needing to be updated in the SVM, since we fetched it
73                        true,
74                    )
75                } else {
76                    let program_data_address = get_program_data_address(pubkey);
77
78                    let program_data = self
79                        .client
80                        .get_account_with_commitment(&program_data_address, commitment_config)
81                        .await
82                        .map_err(|e| SurfpoolError::get_account(*pubkey, e))?;
83
84                    GetAccountResult::FoundProgramAccount(
85                        (*pubkey, account),
86                        (program_data_address, program_data.value),
87                    )
88                }
89            }
90            None => GetAccountResult::None(*pubkey),
91        };
92        Ok(result)
93    }
94
95    pub async fn get_multiple_accounts(
96        &self,
97        pubkeys: &[Pubkey],
98        commitment_config: CommitmentConfig,
99    ) -> SurfpoolResult<Vec<GetAccountResult>> {
100        let remote_accounts = self
101            .client
102            .get_multiple_accounts(pubkeys)
103            .await
104            .map_err(SurfpoolError::get_multiple_accounts)?;
105
106        let mut accounts_result = vec![];
107        for (pubkey, remote_account) in pubkeys.iter().zip(remote_accounts) {
108            if let Some(remote_account) = remote_account {
109                if !remote_account.executable {
110                    accounts_result.push(GetAccountResult::FoundAccount(
111                        *pubkey,
112                        remote_account,
113                        // Mark this account as needing to be updated in the SVM, since we fetched it
114                        true,
115                    ));
116                } else {
117                    let program_data_address = get_program_data_address(pubkey);
118
119                    let program_data = self
120                        .client
121                        .get_account_with_commitment(&program_data_address, commitment_config)
122                        .await
123                        .map_err(|e| SurfpoolError::get_account(*pubkey, e))?;
124
125                    accounts_result.push(GetAccountResult::FoundProgramAccount(
126                        (*pubkey, remote_account),
127                        (program_data_address, program_data.value),
128                    ));
129                }
130            }
131        }
132        Ok(accounts_result)
133    }
134
135    pub async fn get_transaction(
136        &self,
137        signature: Signature,
138        encoding: Option<UiTransactionEncoding>,
139        latest_absolute_slot: u64,
140    ) -> GetTransactionResult {
141        match self
142            .client
143            .get_transaction(
144                &signature,
145                encoding.unwrap_or(UiTransactionEncoding::Base64),
146            )
147            .await
148        {
149            Ok(tx) => GetTransactionResult::found_transaction(signature, tx, latest_absolute_slot),
150            Err(_) => GetTransactionResult::None(signature),
151        }
152    }
153
154    pub async fn get_token_accounts_by_owner(
155        &self,
156        owner: Pubkey,
157        token_program: Pubkey,
158    ) -> SurfpoolResult<Vec<RpcKeyedAccount>> {
159        self.client
160            .get_token_accounts_by_owner(&owner, TokenAccountsFilter::ProgramId(token_program))
161            .await
162            .map_err(|e| SurfpoolError::get_token_accounts(owner, token_program, e))
163    }
164
165    pub async fn get_program_accounts(
166        &self,
167        program_id: &Pubkey,
168        account_config: RpcAccountInfoConfig,
169        filters: Option<Vec<RpcFilterType>>,
170    ) -> SurfpoolResult<Vec<RpcKeyedAccount>> {
171        let encoding = account_config.encoding.unwrap_or(UiAccountEncoding::Base64);
172        let data_slice = account_config.data_slice;
173        self.client
174            .get_program_accounts_with_config(
175                program_id,
176                RpcProgramAccountsConfig {
177                    filters,
178                    with_context: Some(false),
179                    account_config,
180                    ..Default::default()
181                },
182            )
183            .await
184            .map(|accounts| {
185                accounts
186                    .iter()
187                    .map(|(pubkey, account)| RpcKeyedAccount {
188                        pubkey: pubkey.to_string(),
189                        account: encode_ui_account(pubkey, account, encoding, None, data_slice),
190                    })
191                    .collect()
192            })
193            .map_err(|e| SurfpoolError::get_program_accounts(*program_id, e))
194    }
195}