@@ -843,6 +843,48 @@ impl JsonRpcRequestProcessor {
843
843
new_response ( & bank, supply)
844
844
}
845
845
846
+ pub fn get_token_largest_accounts (
847
+ & self ,
848
+ mint : & Pubkey ,
849
+ commitment : Option < CommitmentConfig > ,
850
+ ) -> Result < RpcResponse < Vec < RpcTokenAccountBalance > > > {
851
+ let bank = self . bank ( commitment) ?;
852
+ let mint_account = bank. get_account ( mint) . ok_or_else ( || {
853
+ Error :: invalid_params ( "Invalid param: could not find mint" . to_string ( ) )
854
+ } ) ?;
855
+ if mint_account. owner != spl_token_id_v1_0 ( ) {
856
+ return Err ( Error :: invalid_params (
857
+ "Invalid param: not a v1.0 Token mint" . to_string ( ) ,
858
+ ) ) ;
859
+ }
860
+ let filters = vec ! [
861
+ // Filter on Mint address
862
+ RpcFilterType :: Memcmp ( Memcmp {
863
+ offset: 0 ,
864
+ bytes: MemcmpEncodedBytes :: Binary ( mint. to_string( ) ) ,
865
+ encoding: None ,
866
+ } ) ,
867
+ // Filter on Token Account state
868
+ RpcFilterType :: DataSize ( size_of:: <TokenAccount >( ) as u64 ) ,
869
+ ] ;
870
+ let mut token_balances: Vec < RpcTokenAccountBalance > =
871
+ get_filtered_program_accounts ( & bank, & mint_account. owner , filters)
872
+ . map ( |( address, account) | {
873
+ let mut data = account. data . to_vec ( ) ;
874
+ let amount = spl_token_v1_0:: state:: unpack ( & mut data)
875
+ . map ( |account : & mut TokenAccount | account. amount )
876
+ . unwrap_or ( 0 ) ;
877
+ RpcTokenAccountBalance {
878
+ address : address. to_string ( ) ,
879
+ amount,
880
+ }
881
+ } )
882
+ . collect ( ) ;
883
+ token_balances. sort_by ( |a, b| a. amount . cmp ( & b. amount ) . reverse ( ) ) ;
884
+ token_balances. truncate ( NUM_LARGEST_ACCOUNTS ) ;
885
+ new_response ( & bank, token_balances)
886
+ }
887
+
846
888
pub fn get_token_accounts_by_owner (
847
889
& self ,
848
890
owner : & Pubkey ,
@@ -1315,6 +1357,14 @@ pub trait RpcSol {
1315
1357
commitment : Option < CommitmentConfig > ,
1316
1358
) -> Result < RpcResponse < u64 > > ;
1317
1359
1360
+ #[ rpc( meta, name = "getTokenLargestAccounts" ) ]
1361
+ fn get_token_largest_accounts (
1362
+ & self ,
1363
+ meta : Self :: Metadata ,
1364
+ mint_str : String ,
1365
+ commitment : Option < CommitmentConfig > ,
1366
+ ) -> Result < RpcResponse < Vec < RpcTokenAccountBalance > > > ;
1367
+
1318
1368
#[ rpc( meta, name = "getTokenAccountsByOwner" ) ]
1319
1369
fn get_token_accounts_by_owner (
1320
1370
& self ,
@@ -1948,6 +1998,20 @@ impl RpcSol for RpcSolImpl {
1948
1998
meta. get_token_supply ( & mint, commitment)
1949
1999
}
1950
2000
2001
+ fn get_token_largest_accounts (
2002
+ & self ,
2003
+ meta : Self :: Metadata ,
2004
+ mint_str : String ,
2005
+ commitment : Option < CommitmentConfig > ,
2006
+ ) -> Result < RpcResponse < Vec < RpcTokenAccountBalance > > > {
2007
+ debug ! (
2008
+ "get_token_largest_accounts rpc request received: {:?}" ,
2009
+ mint_str
2010
+ ) ;
2011
+ let mint = verify_pubkey ( mint_str) ?;
2012
+ meta. get_token_largest_accounts ( & mint, commitment)
2013
+ }
2014
+
1951
2015
fn get_token_accounts_by_owner (
1952
2016
& self ,
1953
2017
meta : Self :: Metadata ,
@@ -4433,7 +4497,7 @@ pub mod tests {
4433
4497
. expect ( "actual response deserialization" ) ;
4434
4498
assert ! ( result. get( "error" ) . is_some( ) ) ;
4435
4499
4436
- // Test non-existent Owner
4500
+ // Test non-existent Delegate
4437
4501
let req = format ! (
4438
4502
r#"{{
4439
4503
"jsonrpc":"2.0",
@@ -4444,11 +4508,75 @@ pub mod tests {
4444
4508
Pubkey :: new_rand( ) ,
4445
4509
spl_token_id_v1_0( ) ,
4446
4510
) ;
4447
- let res = io. handle_request_sync ( & req, meta) ;
4511
+ let res = io. handle_request_sync ( & req, meta. clone ( ) ) ;
4448
4512
let result: Value = serde_json:: from_str ( & res. expect ( "actual response" ) )
4449
4513
. expect ( "actual response deserialization" ) ;
4450
4514
let accounts: Vec < RpcKeyedAccount > =
4451
4515
serde_json:: from_value ( result[ "result" ] [ "value" ] . clone ( ) ) . unwrap ( ) ;
4452
4516
assert ! ( accounts. is_empty( ) ) ;
4517
+
4518
+ // Add new_mint, and another token account on new_mint with different balance
4519
+ let mut mint_data = [ 0 ; size_of :: < Mint > ( ) ] ;
4520
+ let mint_state: & mut Mint =
4521
+ spl_token_v1_0:: state:: unpack_unchecked ( & mut mint_data) . unwrap ( ) ;
4522
+ * mint_state = Mint {
4523
+ owner : COption :: Some ( owner) ,
4524
+ decimals : 2 ,
4525
+ is_initialized : true ,
4526
+ } ;
4527
+ let mint_account = Account {
4528
+ lamports : 111 ,
4529
+ data : mint_data. to_vec ( ) ,
4530
+ owner : spl_token_id_v1_0 ( ) ,
4531
+ ..Account :: default ( )
4532
+ } ;
4533
+ bank. store_account (
4534
+ & Pubkey :: from_str ( & new_mint. to_string ( ) ) . unwrap ( ) ,
4535
+ & mint_account,
4536
+ ) ;
4537
+ let mut account_data = [ 0 ; size_of :: < TokenAccount > ( ) ] ;
4538
+ let account: & mut TokenAccount =
4539
+ spl_token_v1_0:: state:: unpack_unchecked ( & mut account_data) . unwrap ( ) ;
4540
+ * account = TokenAccount {
4541
+ mint : new_mint,
4542
+ owner,
4543
+ delegate : COption :: Some ( delegate) ,
4544
+ amount : 10 ,
4545
+ is_initialized : true ,
4546
+ is_native : false ,
4547
+ delegated_amount : 30 ,
4548
+ } ;
4549
+ let token_account = Account {
4550
+ lamports : 111 ,
4551
+ data : account_data. to_vec ( ) ,
4552
+ owner : spl_token_id_v1_0 ( ) ,
4553
+ ..Account :: default ( )
4554
+ } ;
4555
+ let token_with_smaller_balance = Pubkey :: new_rand ( ) ;
4556
+ bank. store_account ( & token_with_smaller_balance, & token_account) ;
4557
+
4558
+ // Test largest token accounts
4559
+ let req = format ! (
4560
+ r#"{{"jsonrpc":"2.0","id":1,"method":"getTokenLargestAccounts","params":["{}"]}}"# ,
4561
+ new_mint,
4562
+ ) ;
4563
+ let res = io. handle_request_sync ( & req, meta) ;
4564
+ let result: Value = serde_json:: from_str ( & res. expect ( "actual response" ) )
4565
+ . expect ( "actual response deserialization" ) ;
4566
+ let largest_accounts: Vec < RpcTokenAccountBalance > =
4567
+ serde_json:: from_value ( result[ "result" ] [ "value" ] . clone ( ) ) . unwrap ( ) ;
4568
+ assert_eq ! (
4569
+ largest_accounts,
4570
+ vec![
4571
+ RpcTokenAccountBalance {
4572
+ address: token_with_different_mint_pubkey. to_string( ) ,
4573
+ amount: 42 ,
4574
+ } ,
4575
+ RpcTokenAccountBalance {
4576
+ address: token_with_smaller_balance. to_string( ) ,
4577
+ amount: 10 ,
4578
+ }
4579
+ ]
4580
+ ) ;
4453
4581
}
4454
4582
}
0 commit comments