@@ -893,6 +893,48 @@ impl JsonRpcRequestProcessor {
893
893
Ok ( new_response ( & bank, supply) )
894
894
}
895
895
896
+ pub fn get_token_largest_accounts (
897
+ & self ,
898
+ mint : & Pubkey ,
899
+ commitment : Option < CommitmentConfig > ,
900
+ ) -> Result < RpcResponse < Vec < RpcTokenAccountBalance > > > {
901
+ let bank = self . bank ( commitment) ;
902
+ let mint_account = bank. get_account ( mint) . ok_or_else ( || {
903
+ Error :: invalid_params ( "Invalid param: could not find mint" . to_string ( ) )
904
+ } ) ?;
905
+ if mint_account. owner != spl_token_id_v1_0 ( ) {
906
+ return Err ( Error :: invalid_params (
907
+ "Invalid param: not a v1.0 Token mint" . to_string ( ) ,
908
+ ) ) ;
909
+ }
910
+ let filters = vec ! [
911
+ // Filter on Mint address
912
+ RpcFilterType :: Memcmp ( Memcmp {
913
+ offset: 0 ,
914
+ bytes: MemcmpEncodedBytes :: Binary ( mint. to_string( ) ) ,
915
+ encoding: None ,
916
+ } ) ,
917
+ // Filter on Token Account state
918
+ RpcFilterType :: DataSize ( size_of:: <TokenAccount >( ) as u64 ) ,
919
+ ] ;
920
+ let mut token_balances: Vec < RpcTokenAccountBalance > =
921
+ get_filtered_program_accounts ( & bank, & mint_account. owner , filters)
922
+ . map ( |( address, account) | {
923
+ let mut data = account. data . to_vec ( ) ;
924
+ let amount = TokenState :: unpack ( & mut data)
925
+ . map ( |account : & mut TokenAccount | account. amount )
926
+ . unwrap_or ( 0 ) ;
927
+ RpcTokenAccountBalance {
928
+ address : address. to_string ( ) ,
929
+ amount,
930
+ }
931
+ } )
932
+ . collect ( ) ;
933
+ token_balances. sort_by ( |a, b| a. amount . cmp ( & b. amount ) . reverse ( ) ) ;
934
+ token_balances. truncate ( NUM_LARGEST_ACCOUNTS ) ;
935
+ Ok ( new_response ( & bank, token_balances) )
936
+ }
937
+
896
938
pub fn get_token_accounts_by_owner (
897
939
& self ,
898
940
owner : & Pubkey ,
@@ -1346,6 +1388,14 @@ pub trait RpcSol {
1346
1388
commitment : Option < CommitmentConfig > ,
1347
1389
) -> Result < RpcResponse < u64 > > ;
1348
1390
1391
+ #[ rpc( meta, name = "getTokenLargestAccounts" ) ]
1392
+ fn get_token_largest_accounts (
1393
+ & self ,
1394
+ meta : Self :: Metadata ,
1395
+ mint_str : String ,
1396
+ commitment : Option < CommitmentConfig > ,
1397
+ ) -> Result < RpcResponse < Vec < RpcTokenAccountBalance > > > ;
1398
+
1349
1399
#[ rpc( meta, name = "getTokenAccountsByOwner" ) ]
1350
1400
fn get_token_accounts_by_owner (
1351
1401
& self ,
@@ -1981,6 +2031,20 @@ impl RpcSol for RpcSolImpl {
1981
2031
meta. get_token_supply ( & mint, commitment)
1982
2032
}
1983
2033
2034
+ fn get_token_largest_accounts (
2035
+ & self ,
2036
+ meta : Self :: Metadata ,
2037
+ mint_str : String ,
2038
+ commitment : Option < CommitmentConfig > ,
2039
+ ) -> Result < RpcResponse < Vec < RpcTokenAccountBalance > > > {
2040
+ debug ! (
2041
+ "get_token_largest_accounts rpc request received: {:?}" ,
2042
+ mint_str
2043
+ ) ;
2044
+ let mint = verify_pubkey ( mint_str) ?;
2045
+ meta. get_token_largest_accounts ( & mint, commitment)
2046
+ }
2047
+
1984
2048
fn get_token_accounts_by_owner (
1985
2049
& self ,
1986
2050
meta : Self :: Metadata ,
@@ -4514,7 +4578,7 @@ pub mod tests {
4514
4578
. expect ( "actual response deserialization" ) ;
4515
4579
assert ! ( result. get( "error" ) . is_some( ) ) ;
4516
4580
4517
- // Test non-existent Owner
4581
+ // Test non-existent Delegate
4518
4582
let req = format ! (
4519
4583
r#"{{
4520
4584
"jsonrpc":"2.0",
@@ -4525,11 +4589,73 @@ pub mod tests {
4525
4589
Pubkey :: new_rand( ) ,
4526
4590
spl_token_id_v1_0( ) ,
4527
4591
) ;
4528
- let res = io. handle_request_sync ( & req, meta) ;
4592
+ let res = io. handle_request_sync ( & req, meta. clone ( ) ) ;
4529
4593
let result: Value = serde_json:: from_str ( & res. expect ( "actual response" ) )
4530
4594
. expect ( "actual response deserialization" ) ;
4531
4595
let accounts: Vec < RpcKeyedAccount > =
4532
4596
serde_json:: from_value ( result[ "result" ] [ "value" ] . clone ( ) ) . unwrap ( ) ;
4533
4597
assert ! ( accounts. is_empty( ) ) ;
4598
+
4599
+ // Add new_mint, and another token account on new_mint with different balance
4600
+ let mut mint_data = [ 0 ; size_of :: < Mint > ( ) ] ;
4601
+ let mint_state: & mut Mint = TokenState :: unpack_unchecked ( & mut mint_data) . unwrap ( ) ;
4602
+ * mint_state = Mint {
4603
+ owner : COption :: Some ( owner) ,
4604
+ decimals : 2 ,
4605
+ is_initialized : true ,
4606
+ } ;
4607
+ let mint_account = Account {
4608
+ lamports : 111 ,
4609
+ data : mint_data. to_vec ( ) ,
4610
+ owner : spl_token_id_v1_0 ( ) ,
4611
+ ..Account :: default ( )
4612
+ } ;
4613
+ bank. store_account (
4614
+ & Pubkey :: from_str ( & new_mint. to_string ( ) ) . unwrap ( ) ,
4615
+ & mint_account,
4616
+ ) ;
4617
+ let mut account_data = [ 0 ; size_of :: < TokenAccount > ( ) ] ;
4618
+ let account: & mut TokenAccount = TokenState :: unpack_unchecked ( & mut account_data) . unwrap ( ) ;
4619
+ * account = TokenAccount {
4620
+ mint : new_mint,
4621
+ owner,
4622
+ delegate : COption :: Some ( delegate) ,
4623
+ amount : 10 ,
4624
+ is_initialized : true ,
4625
+ is_native : false ,
4626
+ delegated_amount : 30 ,
4627
+ } ;
4628
+ let token_account = Account {
4629
+ lamports : 111 ,
4630
+ data : account_data. to_vec ( ) ,
4631
+ owner : spl_token_id_v1_0 ( ) ,
4632
+ ..Account :: default ( )
4633
+ } ;
4634
+ let token_with_smaller_balance = Pubkey :: new_rand ( ) ;
4635
+ bank. store_account ( & token_with_smaller_balance, & token_account) ;
4636
+
4637
+ // Test largest token accounts
4638
+ let req = format ! (
4639
+ r#"{{"jsonrpc":"2.0","id":1,"method":"getTokenLargestAccounts","params":["{}"]}}"# ,
4640
+ new_mint,
4641
+ ) ;
4642
+ let res = io. handle_request_sync ( & req, meta) ;
4643
+ let result: Value = serde_json:: from_str ( & res. expect ( "actual response" ) )
4644
+ . expect ( "actual response deserialization" ) ;
4645
+ let largest_accounts: Vec < RpcTokenAccountBalance > =
4646
+ serde_json:: from_value ( result[ "result" ] [ "value" ] . clone ( ) ) . unwrap ( ) ;
4647
+ assert_eq ! (
4648
+ largest_accounts,
4649
+ vec![
4650
+ RpcTokenAccountBalance {
4651
+ address: token_with_different_mint_pubkey. to_string( ) ,
4652
+ amount: 42 ,
4653
+ } ,
4654
+ RpcTokenAccountBalance {
4655
+ address: token_with_smaller_balance. to_string( ) ,
4656
+ amount: 10 ,
4657
+ }
4658
+ ]
4659
+ ) ;
4534
4660
}
4535
4661
}
0 commit comments