surfpool_core/rpc/
accounts_scan.rs

1use jsonrpc_core::BoxFuture;
2use jsonrpc_core::Result;
3use jsonrpc_derive::rpc;
4use solana_client::rpc_config::{
5    RpcAccountInfoConfig, RpcLargestAccountsConfig, RpcProgramAccountsConfig, RpcSupplyConfig,
6    RpcTokenAccountsFilter,
7};
8use solana_client::rpc_response::RpcResponseContext;
9use solana_client::rpc_response::{
10    OptionalContext, RpcAccountBalance, RpcKeyedAccount, RpcSupply, RpcTokenAccountBalance,
11};
12use solana_commitment_config::CommitmentConfig;
13use solana_rpc_client_api::response::Response as RpcResponse;
14
15use super::not_implemented_err_async;
16use super::RunloopContext;
17use super::State;
18
19#[rpc]
20pub trait AccountsScan {
21    type Metadata;
22
23    /// Returns all accounts owned by the specified program ID, optionally filtered and configured.
24    ///
25    /// This RPC method retrieves all accounts whose owner is the given program. It is commonly used
26    /// to scan on-chain program state, such as finding all token accounts, order books, or PDAs
27    /// owned by a given program. The results can be filtered using data size, memory comparisons, and
28    /// token-specific criteria.
29    ///
30    /// ## Parameters
31    /// - `program_id_str`: Base-58 encoded program ID to scan for owned accounts.
32    /// - `config`: Optional configuration object allowing filters, encoding options, context inclusion,
33    ///   and sorting of results.
34    ///
35    /// ## Returns
36    /// A future resolving to a vector of [`RpcKeyedAccount`]s wrapped in an [`OptionalContext`].
37    /// Each result includes the account's public key and full account data.
38    ///
39    /// ## Example Request (JSON-RPC)
40    /// ```json
41    /// {
42    ///   "jsonrpc": "2.0",
43    ///   "id": 1,
44    ///   "method": "getProgramAccounts",
45    ///   "params": [
46    ///     "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
47    ///     {
48    ///       "filters": [
49    ///         {
50    ///           "dataSize": 165
51    ///         },
52    ///         {
53    ///           "memcmp": {
54    ///             "offset": 0,
55    ///             "bytes": "3N5kaPhfUGuTQZPQ3mnDZZGkUZ97rS1NVSC94QkgUzKN"
56    ///           }
57    ///         }
58    ///       ],
59    ///       "encoding": "jsonParsed",
60    ///       "commitment": "finalized",
61    ///       "withContext": true
62    ///     }
63    ///   ]
64    /// }
65    /// ```
66    ///
67    /// ## Example Response
68    /// ```json
69    /// {
70    ///   "jsonrpc": "2.0",
71    ///   "result": {
72    ///     "context": {
73    ///       "slot": 12345678
74    ///     },
75    ///     "value": [
76    ///       {
77    ///         "pubkey": "BvckZ2XDJmJLho7LnFnV7zM19fRZqnvfs8Qy3fLo6EEk",
78    ///         "account": {
79    ///           "lamports": 2039280,
80    ///           "data": {...},
81    ///           "owner": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
82    ///           "executable": false,
83    ///           "rentEpoch": 255,
84    ///           "space": 165
85    ///         }
86    ///       },
87    ///       ...
88    ///     ]
89    ///   },
90    ///   "id": 1
91    /// }
92    /// ```
93    ///
94    /// # Filters
95    /// - `DataSize(u64)`: Only include accounts with a matching data length.
96    /// - `Memcmp`: Match byte patterns at specified offsets in account data.
97    /// - `TokenAccountState`: Match on internal token account state (e.g. initialized).
98    ///
99    /// ## See also
100    /// - [`RpcProgramAccountsConfig`]: Main config for filtering and encoding.
101    /// - [`UiAccount`]: Returned data representation.
102    /// - [`RpcKeyedAccount`]: Wrapper struct with both pubkey and account fields.
103    #[rpc(meta, name = "getProgramAccounts")]
104    fn get_program_accounts(
105        &self,
106        meta: Self::Metadata,
107        program_id_str: String,
108        config: Option<RpcProgramAccountsConfig>,
109    ) -> BoxFuture<Result<OptionalContext<Vec<RpcKeyedAccount>>>>;
110
111    /// Returns the 20 largest accounts by lamport balance, optionally filtered by account type.
112    ///
113    /// This RPC endpoint is useful for analytics, network monitoring, or understanding
114    /// the distribution of large token holders. It can also be used for sanity checks on
115    /// protocol activity or whale tracking.
116    ///
117    /// ## Parameters
118    /// - `config`: Optional configuration allowing for filtering on specific account types
119    ///   such as circulating or non-circulating accounts.
120    ///
121    /// ## Returns
122    /// A future resolving to a [`RpcResponse`] containing a list of the 20 largest accounts
123    /// by lamports, each represented as an [`RpcAccountBalance`].
124    ///
125    /// ## Example Request (JSON-RPC)
126    /// ```json
127    /// {
128    ///   "jsonrpc": "2.0",
129    ///   "id": 1,
130    ///   "method": "getLargestAccounts",
131    ///   "params": [
132    ///     {
133    ///       "filter": "circulating"
134    ///     }
135    ///   ]
136    /// }
137    /// ```
138    ///
139    /// ## Example Response
140    /// ```json
141    /// {
142    ///   "jsonrpc": "2.0",
143    ///   "result": {
144    ///     "context": {
145    ///       "slot": 15039284
146    ///     },
147    ///     "value": [
148    ///       {
149    ///         "lamports": 999999999999,
150    ///         "address": "9xQeWvG816bUx9EPaZzdd5eUjuJcN3TBDZcd8DM33zDf"
151    ///       },
152    ///       ...
153    ///     ]
154    ///   },
155    ///   "id": 1
156    /// }
157    /// ```
158    ///
159    /// ## See also
160    /// - [`RpcLargestAccountsConfig`] *(defined elsewhere)*: Config struct that may specify a `filter`.
161    /// - [`RpcAccountBalance`]: Struct representing account address and lamport amount.
162    ///
163    /// # Notes
164    /// This method only returns up to 20 accounts and is primarily intended for inspection or diagnostics.
165    #[rpc(meta, name = "getLargestAccounts")]
166    fn get_largest_accounts(
167        &self,
168        meta: Self::Metadata,
169        config: Option<RpcLargestAccountsConfig>,
170    ) -> BoxFuture<Result<RpcResponse<Vec<RpcAccountBalance>>>>;
171
172    /// Returns information about the current token supply on the network, including
173    /// circulating and non-circulating amounts.
174    ///
175    /// This method provides visibility into the economic state of the chain by exposing
176    /// the total amount of tokens issued, how much is in circulation, and what is held in
177    /// non-circulating accounts.
178    ///
179    /// ## Parameters
180    /// - `config`: Optional [`RpcSupplyConfig`] that allows specifying commitment level and
181    ///   whether to exclude the list of non-circulating accounts from the response.
182    ///
183    /// ## Returns
184    /// A future resolving to a [`RpcResponse`] containing a [`RpcSupply`] struct with
185    /// supply metrics in lamports.
186    ///
187    /// ## Example Request (JSON-RPC)
188    /// ```json
189    /// {
190    ///   "jsonrpc": "2.0",
191    ///   "id": 1,
192    ///   "method": "getSupply",
193    ///   "params": [
194    ///     {
195    ///       "excludeNonCirculatingAccountsList": true
196    ///     }
197    ///   ]
198    /// }
199    /// ```
200    ///
201    /// ## Example Response
202    /// ```json
203    /// {
204    ///   "jsonrpc": "2.0",
205    ///   "result": {
206    ///     "context": {
207    ///       "slot": 18000345
208    ///     },
209    ///     "value": {
210    ///       "total": 510000000000000000,
211    ///       "circulating": 420000000000000000,
212    ///       "nonCirculating": 90000000000000000,
213    ///       "nonCirculatingAccounts": []
214    ///     }
215    ///   },
216    ///   "id": 1
217    /// }
218    /// ```
219    ///
220    /// ## See also
221    /// - [`RpcSupplyConfig`]: Configuration struct for optional parameters.
222    /// - [`RpcSupply`]: Response struct with total, circulating, and non-circulating amounts.
223    ///
224    /// # Notes
225    /// - All values are returned in lamports.
226    /// - Use this method to monitor token inflation, distribution, and locked supply dynamics.
227    #[rpc(meta, name = "getSupply")]
228    fn get_supply(
229        &self,
230        meta: Self::Metadata,
231        config: Option<RpcSupplyConfig>,
232    ) -> BoxFuture<Result<RpcResponse<RpcSupply>>>;
233
234    /// Returns the addresses and balances of the largest accounts for a given SPL token mint.
235    ///
236    /// This method is useful for analyzing token distribution and concentration, especially
237    /// to assess decentralization or identify whales.
238    ///
239    /// ## Parameters
240    /// - `mint_str`: The base-58 encoded public key of the mint account of the SPL token.
241    /// - `commitment`: Optional commitment level to query the state of the ledger at different levels
242    ///   of finality (e.g., `Processed`, `Confirmed`, `Finalized`).
243    ///
244    /// ## Returns
245    /// A [`BoxFuture`] resolving to a [`RpcResponse`] with a vector of [`RpcTokenAccountBalance`]s,
246    /// representing the largest accounts holding the token.
247    ///
248    /// ## Example Request (JSON-RPC)
249    /// ```json
250    /// {
251    ///   "jsonrpc": "2.0",
252    ///   "id": 1,
253    ///   "method": "getTokenLargestAccounts",
254    ///   "params": [
255    ///     "So11111111111111111111111111111111111111112"
256    ///   ]
257    /// }
258    /// ```
259    ///
260    /// ## Example Response
261    /// ```json
262    /// {
263    ///   "jsonrpc": "2.0",
264    ///   "result": {
265    ///     "context": {
266    ///       "slot": 18300000
267    ///     },
268    ///     "value": [
269    ///       {
270    ///         "address": "5xy34...Abcd1",
271    ///         "amount": "100000000000",
272    ///         "decimals": 9,
273    ///         "uiAmount": 100.0,
274    ///         "uiAmountString": "100.0"
275    ///       },
276    ///       {
277    ///         "address": "2aXyZ...Efgh2",
278    ///         "amount": "50000000000",
279    ///         "decimals": 9,
280    ///         "uiAmount": 50.0,
281    ///         "uiAmountString": "50.0"
282    ///       }
283    ///     ]
284    ///   },
285    ///   "id": 1
286    /// }
287    /// ```
288    ///
289    /// ## See also
290    /// - [`UiTokenAmount`]: Describes the token amount in different representations.
291    /// - [`RpcTokenAccountBalance`]: Includes token holder address and amount.
292    ///
293    /// # Notes
294    /// - Balances are sorted in descending order.
295    /// - Token decimals are used to format the raw amount into a user-friendly float string.
296    #[rpc(meta, name = "getTokenLargestAccounts")]
297    fn get_token_largest_accounts(
298        &self,
299        meta: Self::Metadata,
300        mint_str: String,
301        commitment: Option<CommitmentConfig>,
302    ) -> BoxFuture<Result<RpcResponse<Vec<RpcTokenAccountBalance>>>>;
303
304    /// Returns all SPL Token accounts owned by a specific wallet address, optionally filtered by mint or program.
305    ///
306    /// This endpoint is commonly used by wallets and explorers to retrieve all token balances
307    /// associated with a user, and optionally narrow results to a specific token mint or program.
308    ///
309    /// ## Parameters
310    /// - `owner_str`: The base-58 encoded public key of the wallet owner.
311    /// - `token_account_filter`: A [`RpcTokenAccountsFilter`] enum that allows filtering results by:
312    ///   - Mint address
313    ///   - Program ID (usually the SPL Token program)
314    /// - `config`: Optional configuration for encoding, data slicing, and commitment.
315    ///
316    /// ## Returns
317    /// A [`BoxFuture`] resolving to a [`RpcResponse`] containing a vector of [`RpcKeyedAccount`]s.
318    /// Each entry contains the public key of a token account and its deserialized account data.
319    ///
320    /// ## Example Request (JSON-RPC)
321    /// ```json
322    /// {
323    ///   "jsonrpc": "2.0",
324    ///   "id": 1,
325    ///   "method": "getTokenAccountsByOwner",
326    ///   "params": [
327    ///     "4Nd1mKxQmZj...Aa123",
328    ///     {
329    ///       "mint": "So11111111111111111111111111111111111111112"
330    ///     },
331    ///     {
332    ///       "encoding": "jsonParsed"
333    ///     }
334    ///   ]
335    /// }
336    /// ```
337    ///
338    /// ## Example Response
339    /// ```json
340    /// {
341    ///   "jsonrpc": "2.0",
342    ///   "result": {
343    ///     "context": { "slot": 19281234 },
344    ///     "value": [
345    ///       {
346    ///         "pubkey": "2sZp...xyz",
347    ///         "account": {
348    ///           "lamports": 2039280,
349    ///           "data": { /* token info */ },
350    ///           "owner": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
351    ///           "executable": false,
352    ///           "rentEpoch": 123
353    ///         }
354    ///       }
355    ///     ]
356    ///   },
357    ///   "id": 1
358    /// }
359    /// ```
360    ///
361    /// # Filter Enum
362    /// [`RpcTokenAccountsFilter`] can be:
363    /// - `Mint(String)` — return only token accounts associated with the specified mint.
364    /// - `ProgramId(String)` — return only token accounts owned by the specified program (e.g. SPL Token program).
365    ///
366    /// ## See also
367    /// - [`RpcKeyedAccount`]: Contains the pubkey and the associated account data.
368    /// - [`RpcAccountInfoConfig`]: Allows tweaking how account data is returned (encoding, commitment, etc.).
369    /// - [`UiAccountEncoding`], [`CommitmentConfig`]
370    ///
371    /// # Notes
372    /// - The response may contain `Option::None` for accounts that couldn't be fetched or decoded.
373    /// - Encoding `jsonParsed` is recommended when integrating with frontend UIs.
374    #[rpc(meta, name = "getTokenAccountsByOwner")]
375    fn get_token_accounts_by_owner(
376        &self,
377        meta: Self::Metadata,
378        owner_str: String,
379        token_account_filter: RpcTokenAccountsFilter,
380        config: Option<RpcAccountInfoConfig>,
381    ) -> BoxFuture<Result<RpcResponse<Vec<RpcKeyedAccount>>>>;
382
383    /// Returns all SPL Token accounts that have delegated authority to a specific address, with optional filters.
384    ///
385    /// This RPC method is useful for identifying which token accounts have granted delegate rights
386    /// to a particular wallet or program (commonly used in DeFi apps or custodial flows).
387    ///
388    /// ## Parameters
389    /// - `delegate_str`: The base-58 encoded public key of the delegate authority.
390    /// - `token_account_filter`: A [`RpcTokenAccountsFilter`] enum to filter results by mint or program.
391    /// - `config`: Optional [`RpcAccountInfoConfig`] for controlling account encoding, commitment level, etc.
392    ///
393    /// ## Returns
394    /// A [`BoxFuture`] resolving to a [`RpcResponse`] containing a vector of [`RpcKeyedAccount`]s,
395    /// each pairing a token account public key with its associated on-chain data.
396    ///
397    /// ## Example Request (JSON-RPC)
398    /// ```json
399    /// {
400    ///   "jsonrpc": "2.0",
401    ///   "id": 1,
402    ///   "method": "getTokenAccountsByDelegate",
403    ///   "params": [
404    ///     "3qTwHcdK1j...XYZ",
405    ///     { "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" },
406    ///     { "encoding": "jsonParsed" }
407    ///   ]
408    /// }
409    /// ```
410    ///
411    /// ## Example Response
412    /// ```json
413    /// {
414    ///   "jsonrpc": "2.0",
415    ///   "result": {
416    ///     "context": { "slot": 19301523 },
417    ///     "value": [
418    ///       {
419    ///         "pubkey": "8H5k...abc",
420    ///         "account": {
421    ///           "lamports": 2039280,
422    ///           "data": { /* token info */ },
423    ///           "owner": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
424    ///           "executable": false,
425    ///           "rentEpoch": 131
426    ///         }
427    ///       }
428    ///     ]
429    ///   },
430    ///   "id": 1
431    /// }
432    /// ```
433    ///
434    /// # Filters
435    /// Use [`RpcTokenAccountsFilter`] to limit the query scope:
436    /// - `Mint(String)` – return accounts associated with a given token.
437    /// - `ProgramId(String)` – return accounts under a specific program (e.g., SPL Token program).
438    ///
439    /// # Notes
440    /// - Useful for monitoring delegated token activity in governance or trading protocols.
441    /// - If a token account doesn’t have a delegate, it won’t be included in results.
442    ///
443    /// ## See also
444    /// - [`RpcKeyedAccount`], [`RpcAccountInfoConfig`], [`CommitmentConfig`], [`UiAccountEncoding`]
445    #[rpc(meta, name = "getTokenAccountsByDelegate")]
446    fn get_token_accounts_by_delegate(
447        &self,
448        meta: Self::Metadata,
449        delegate_str: String,
450        token_account_filter: RpcTokenAccountsFilter,
451        config: Option<RpcAccountInfoConfig>,
452    ) -> BoxFuture<Result<RpcResponse<Vec<RpcKeyedAccount>>>>;
453}
454
455pub struct SurfpoolAccountsScanRpc;
456impl AccountsScan for SurfpoolAccountsScanRpc {
457    type Metadata = Option<RunloopContext>;
458
459    fn get_program_accounts(
460        &self,
461        _meta: Self::Metadata,
462        _program_id_str: String,
463        _config: Option<RpcProgramAccountsConfig>,
464    ) -> BoxFuture<Result<OptionalContext<Vec<RpcKeyedAccount>>>> {
465        not_implemented_err_async()
466    }
467
468    fn get_largest_accounts(
469        &self,
470        _meta: Self::Metadata,
471        _config: Option<RpcLargestAccountsConfig>,
472    ) -> BoxFuture<Result<RpcResponse<Vec<RpcAccountBalance>>>> {
473        not_implemented_err_async()
474    }
475
476    fn get_supply(
477        &self,
478        meta: Self::Metadata,
479        _config: Option<RpcSupplyConfig>,
480    ) -> BoxFuture<Result<RpcResponse<RpcSupply>>> {
481        let svm_locker = match meta.get_svm_locker() {
482            Ok(locker) => locker,
483            Err(e) => return e.into(),
484        };
485
486        Box::pin(async move {
487            let svm_reader = svm_locker.read().await;
488            let slot = svm_reader.get_latest_absolute_slot();
489            Ok(RpcResponse {
490                context: RpcResponseContext::new(slot),
491                value: RpcSupply {
492                    total: 1,
493                    circulating: 0,
494                    non_circulating: 0,
495                    non_circulating_accounts: vec![],
496                },
497            })
498        })
499    }
500
501    fn get_token_largest_accounts(
502        &self,
503        _meta: Self::Metadata,
504        _mint_str: String,
505        _commitment: Option<CommitmentConfig>,
506    ) -> BoxFuture<Result<RpcResponse<Vec<RpcTokenAccountBalance>>>> {
507        not_implemented_err_async()
508    }
509
510    fn get_token_accounts_by_owner(
511        &self,
512        _meta: Self::Metadata,
513        _owner_str: String,
514        _token_account_filter: RpcTokenAccountsFilter,
515        _config: Option<RpcAccountInfoConfig>,
516    ) -> BoxFuture<Result<RpcResponse<Vec<RpcKeyedAccount>>>> {
517        not_implemented_err_async()
518    }
519
520    fn get_token_accounts_by_delegate(
521        &self,
522        _meta: Self::Metadata,
523        _delegate_str: String,
524        _token_account_filter: RpcTokenAccountsFilter,
525        _config: Option<RpcAccountInfoConfig>,
526    ) -> BoxFuture<Result<RpcResponse<Vec<RpcKeyedAccount>>>> {
527        not_implemented_err_async()
528    }
529}