Skip to content

Commit 9bcfc51

Browse files
Add token rpc endpoints to rpc-client (solana-labs#11315)
1 parent d7e961d commit 9bcfc51

File tree

3 files changed

+167
-19
lines changed

3 files changed

+167
-19
lines changed

client/src/rpc_client.rs

Lines changed: 139 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ use crate::{
22
client_error::{ClientError, ClientErrorKind, Result as ClientResult},
33
http_sender::HttpSender,
44
mock_sender::{MockSender, Mocks},
5-
rpc_config::{RpcLargestAccountsConfig, RpcSendTransactionConfig},
6-
rpc_request::{RpcError, RpcRequest},
5+
rpc_config::{RpcLargestAccountsConfig, RpcSendTransactionConfig, RpcTokenAccountsFilter},
6+
rpc_request::{RpcError, RpcRequest, TokenAccountsFilter},
77
rpc_response::*,
88
rpc_sender::RpcSender,
99
};
@@ -503,17 +503,7 @@ impl RpcClient {
503503
pub fn get_program_accounts(&self, pubkey: &Pubkey) -> ClientResult<Vec<(Pubkey, Account)>> {
504504
let accounts: Vec<RpcKeyedAccount> =
505505
self.send(RpcRequest::GetProgramAccounts, json!([pubkey.to_string()]))?;
506-
let mut pubkey_accounts: Vec<(Pubkey, Account)> = Vec::new();
507-
for RpcKeyedAccount { pubkey, account } in accounts.into_iter() {
508-
let pubkey = pubkey.parse().map_err(|_| {
509-
ClientError::new_with_request(
510-
RpcError::ParseError("Pubkey".to_string()).into(),
511-
RpcRequest::GetProgramAccounts,
512-
)
513-
})?;
514-
pubkey_accounts.push((pubkey, account.decode().unwrap()));
515-
}
516-
Ok(pubkey_accounts)
506+
parse_keyed_accounts(accounts, RpcRequest::GetProgramAccounts)
517507
}
518508

519509
/// Request the transaction count.
@@ -660,6 +650,125 @@ impl RpcClient {
660650
Ok(hash)
661651
}
662652

653+
pub fn get_token_account_balance(&self, pubkey: &Pubkey) -> ClientResult<u64> {
654+
Ok(self
655+
.get_token_account_balance_with_commitment(pubkey, CommitmentConfig::default())?
656+
.value)
657+
}
658+
659+
pub fn get_token_account_balance_with_commitment(
660+
&self,
661+
pubkey: &Pubkey,
662+
commitment_config: CommitmentConfig,
663+
) -> RpcResult<u64> {
664+
self.send(
665+
RpcRequest::GetTokenAccountBalance,
666+
json!([pubkey.to_string(), commitment_config]),
667+
)
668+
}
669+
670+
pub fn get_token_accounts_by_delegate(
671+
&self,
672+
delegate: &Pubkey,
673+
token_account_filter: TokenAccountsFilter,
674+
) -> ClientResult<Vec<(Pubkey, Account)>> {
675+
Ok(self
676+
.get_token_accounts_by_delegate_with_commitment(
677+
delegate,
678+
token_account_filter,
679+
CommitmentConfig::default(),
680+
)?
681+
.value)
682+
}
683+
684+
pub fn get_token_accounts_by_delegate_with_commitment(
685+
&self,
686+
delegate: &Pubkey,
687+
token_account_filter: TokenAccountsFilter,
688+
commitment_config: CommitmentConfig,
689+
) -> RpcResult<Vec<(Pubkey, Account)>> {
690+
let token_account_filter = match token_account_filter {
691+
TokenAccountsFilter::Mint(mint) => RpcTokenAccountsFilter::Mint(mint.to_string()),
692+
TokenAccountsFilter::ProgramId(program_id) => {
693+
RpcTokenAccountsFilter::ProgramId(program_id.to_string())
694+
}
695+
};
696+
let Response {
697+
context,
698+
value: accounts,
699+
} = self.send(
700+
RpcRequest::GetTokenAccountsByDelegate,
701+
json!([
702+
delegate.to_string(),
703+
token_account_filter,
704+
commitment_config
705+
]),
706+
)?;
707+
let pubkey_accounts =
708+
parse_keyed_accounts(accounts, RpcRequest::GetTokenAccountsByDelegate)?;
709+
Ok(Response {
710+
context,
711+
value: pubkey_accounts,
712+
})
713+
}
714+
715+
pub fn get_token_accounts_by_owner(
716+
&self,
717+
owner: &Pubkey,
718+
token_account_filter: TokenAccountsFilter,
719+
) -> ClientResult<Vec<(Pubkey, Account)>> {
720+
Ok(self
721+
.get_token_accounts_by_owner_with_commitment(
722+
owner,
723+
token_account_filter,
724+
CommitmentConfig::default(),
725+
)?
726+
.value)
727+
}
728+
729+
pub fn get_token_accounts_by_owner_with_commitment(
730+
&self,
731+
owner: &Pubkey,
732+
token_account_filter: TokenAccountsFilter,
733+
commitment_config: CommitmentConfig,
734+
) -> RpcResult<Vec<(Pubkey, Account)>> {
735+
let token_account_filter = match token_account_filter {
736+
TokenAccountsFilter::Mint(mint) => RpcTokenAccountsFilter::Mint(mint.to_string()),
737+
TokenAccountsFilter::ProgramId(program_id) => {
738+
RpcTokenAccountsFilter::ProgramId(program_id.to_string())
739+
}
740+
};
741+
let Response {
742+
context,
743+
value: accounts,
744+
} = self.send(
745+
RpcRequest::GetTokenAccountsByOwner,
746+
json!([owner.to_string(), token_account_filter, commitment_config]),
747+
)?;
748+
let pubkey_accounts = parse_keyed_accounts(accounts, RpcRequest::GetTokenAccountsByOwner)?;
749+
Ok(Response {
750+
context,
751+
value: pubkey_accounts,
752+
})
753+
}
754+
755+
pub fn get_token_supply(&self, mint: &Pubkey) -> ClientResult<u64> {
756+
Ok(self
757+
.get_token_supply_with_commitment(mint, CommitmentConfig::default())?
758+
.value)
759+
}
760+
761+
pub fn get_token_supply_with_commitment(
762+
&self,
763+
mint: &Pubkey,
764+
commitment_config: CommitmentConfig,
765+
) -> RpcResult<u64> {
766+
self.send(
767+
RpcRequest::GetTokenSupply,
768+
json!([mint.to_string(), commitment_config]),
769+
)
770+
}
771+
663772
fn poll_balance_with_timeout_and_commitment(
664773
&self,
665774
pubkey: &Pubkey,
@@ -999,6 +1108,23 @@ pub fn get_rpc_request_str(rpc_addr: SocketAddr, tls: bool) -> String {
9991108
}
10001109
}
10011110

1111+
fn parse_keyed_accounts(
1112+
accounts: Vec<RpcKeyedAccount>,
1113+
request: RpcRequest,
1114+
) -> ClientResult<Vec<(Pubkey, Account)>> {
1115+
let mut pubkey_accounts: Vec<(Pubkey, Account)> = Vec::new();
1116+
for RpcKeyedAccount { pubkey, account } in accounts.into_iter() {
1117+
let pubkey = pubkey.parse().map_err(|_| {
1118+
ClientError::new_with_request(
1119+
RpcError::ParseError("Pubkey".to_string()).into(),
1120+
request,
1121+
)
1122+
})?;
1123+
pubkey_accounts.push((pubkey, account.decode().unwrap()));
1124+
}
1125+
Ok(pubkey_accounts)
1126+
}
1127+
10021128
#[cfg(test)]
10031129
mod tests {
10041130
use super::*;

client/src/rpc_request.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use serde_json::{json, Value};
2+
use solana_sdk::pubkey::Pubkey;
23
use std::fmt;
34
use thiserror::Error;
45

@@ -36,6 +37,10 @@ pub enum RpcRequest {
3637
GetSlotsPerSegment,
3738
GetStoragePubkeysForSlot,
3839
GetSupply,
40+
GetTokenAccountBalance,
41+
GetTokenAccountsByDelegate,
42+
GetTokenAccountsByOwner,
43+
GetTokenSupply,
3944
GetTotalSupply,
4045
GetTransactionCount,
4146
GetVersion,
@@ -83,6 +88,10 @@ impl fmt::Display for RpcRequest {
8388
RpcRequest::GetSlotsPerSegment => "getSlotsPerSegment",
8489
RpcRequest::GetStoragePubkeysForSlot => "getStoragePubkeysForSlot",
8590
RpcRequest::GetSupply => "getSupply",
91+
RpcRequest::GetTokenAccountBalance => "getTokenAccountBalance",
92+
RpcRequest::GetTokenAccountsByDelegate => "getTokenAccountsByDelegate",
93+
RpcRequest::GetTokenAccountsByOwner => "getTokenAccountsByOwner",
94+
RpcRequest::GetTokenSupply => "getTokenSupply",
8695
RpcRequest::GetTotalSupply => "getTotalSupply",
8796
RpcRequest::GetTransactionCount => "getTransactionCount",
8897
RpcRequest::GetVersion => "getVersion",
@@ -131,9 +140,16 @@ pub enum RpcError {
131140
ForUser(String), /* "direct-to-user message" */
132141
}
133142

143+
#[derive(Serialize, Deserialize)]
144+
pub enum TokenAccountsFilter {
145+
Mint(Pubkey),
146+
ProgramId(Pubkey),
147+
}
148+
134149
#[cfg(test)]
135150
mod tests {
136151
use super::*;
152+
use crate::rpc_config::RpcTokenAccountsFilter;
137153
use solana_sdk::commitment_config::{CommitmentConfig, CommitmentLevel};
138154

139155
#[test]
@@ -197,5 +213,16 @@ mod tests {
197213
let test_request = RpcRequest::GetBalance;
198214
let request = test_request.build_request_json(1, json!([addr, commitment_config]));
199215
assert_eq!(request["params"], json!([addr, commitment_config]));
216+
217+
// Test request with CommitmentConfig and params
218+
let test_request = RpcRequest::GetTokenAccountsByOwner;
219+
let mint = Pubkey::new_rand();
220+
let token_account_filter = RpcTokenAccountsFilter::Mint(mint.to_string());
221+
let request = test_request
222+
.build_request_json(1, json!([addr, token_account_filter, commitment_config]));
223+
assert_eq!(
224+
request["params"],
225+
json!([addr, token_account_filter, commitment_config])
226+
);
200227
}
201228
}

core/src/rpc.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use solana_client::{
1313
rpc_config::*,
1414
rpc_filter::{Memcmp, MemcmpEncodedBytes, RpcFilterType},
1515
rpc_request::{
16-
DELINQUENT_VALIDATOR_SLOT_DISTANCE, MAX_GET_CONFIRMED_BLOCKS_RANGE,
16+
TokenAccountsFilter, DELINQUENT_VALIDATOR_SLOT_DISTANCE, MAX_GET_CONFIRMED_BLOCKS_RANGE,
1717
MAX_GET_CONFIRMED_SIGNATURES_FOR_ADDRESS_SLOT_RANGE,
1818
MAX_GET_SIGNATURE_STATUSES_QUERY_ITEMS, NUM_LARGEST_ACCOUNTS,
1919
},
@@ -992,11 +992,6 @@ fn verify_signature(input: &str) -> Result<Signature> {
992992
.map_err(|e| Error::invalid_params(format!("Invalid param: {:?}", e)))
993993
}
994994

995-
pub enum TokenAccountsFilter {
996-
Mint(Pubkey),
997-
ProgramId(Pubkey),
998-
}
999-
1000995
fn verify_token_account_filter(
1001996
token_account_filter: RpcTokenAccountsFilter,
1002997
) -> Result<TokenAccountsFilter> {

0 commit comments

Comments
 (0)