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}