Skip to content

Commit

Permalink
Return token amounts as floats
Browse files Browse the repository at this point in the history
  • Loading branch information
Tyera Eulberg committed Aug 4, 2020
1 parent b5e03d6 commit 5b8e5bb
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 43 deletions.
8 changes: 4 additions & 4 deletions client/src/rpc_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -734,7 +734,7 @@ impl RpcClient {
})
}

pub fn get_token_account_balance(&self, pubkey: &Pubkey) -> ClientResult<u64> {
pub fn get_token_account_balance(&self, pubkey: &Pubkey) -> ClientResult<f64> {
Ok(self
.get_token_account_balance_with_commitment(pubkey, CommitmentConfig::default())?
.value)
Expand All @@ -744,7 +744,7 @@ impl RpcClient {
&self,
pubkey: &Pubkey,
commitment_config: CommitmentConfig,
) -> RpcResult<u64> {
) -> RpcResult<f64> {
self.send(
RpcRequest::GetTokenAccountBalance,
json!([pubkey.to_string(), commitment_config]),
Expand Down Expand Up @@ -841,7 +841,7 @@ impl RpcClient {
})
}

pub fn get_token_supply(&self, mint: &Pubkey) -> ClientResult<u64> {
pub fn get_token_supply(&self, mint: &Pubkey) -> ClientResult<f64> {
Ok(self
.get_token_supply_with_commitment(mint, CommitmentConfig::default())?
.value)
Expand All @@ -851,7 +851,7 @@ impl RpcClient {
&self,
mint: &Pubkey,
commitment_config: CommitmentConfig,
) -> RpcResult<u64> {
) -> RpcResult<f64> {
self.send(
RpcRequest::GetTokenSupply,
json!([mint.to_string(), commitment_config]),
Expand Down
98 changes: 63 additions & 35 deletions core/src/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ use solana_transaction_status::{
ConfirmedBlock, ConfirmedTransaction, TransactionStatus, UiTransactionEncoding,
};
use solana_vote_program::vote_state::{VoteState, MAX_LOCKOUT_HISTORY};
use spl_token_v1_0::state::Account as TokenAccount;
use spl_token_v1_0::state::{Account as TokenAccount, Mint};
use std::{
cmp::{max, min},
collections::{HashMap, HashSet},
Expand Down Expand Up @@ -838,7 +838,7 @@ impl JsonRpcRequestProcessor {
&self,
pubkey: &Pubkey,
commitment: Option<CommitmentConfig>,
) -> Result<RpcResponse<u64>> {
) -> Result<RpcResponse<f64>> {
let bank = self.bank(commitment);
let account = bank.get_account(pubkey).ok_or_else(|| {
Error::invalid_params("Invalid param: could not find account".to_string())
Expand All @@ -850,19 +850,28 @@ impl JsonRpcRequestProcessor {
));
}
let mut data = account.data.to_vec();
let balance = spl_token_v1_0::state::unpack(&mut data)
.map_err(|_| {
let token_account =
spl_token_v1_0::state::unpack::<TokenAccount>(&mut data).map_err(|_| {
Error::invalid_params("Invalid param: not a v1.0 Token account".to_string())
})
.map(|account: &mut TokenAccount| account.amount)?;
})?;
let mint_account = bank
.get_account(
&Pubkey::from_str(&token_account.mint.to_string())
.expect("Token account mint should be convertible to Pubkey"),
)
.ok_or_else(|| {
Error::invalid_params("Invalid param: could not find mint".to_string())
})?;
let decimals = get_mint_decimals(&mint_account.data)?;
let balance = token_amount_to_ui_amount(token_account.amount, decimals);
Ok(new_response(&bank, balance))
}

pub fn get_token_supply(
&self,
mint: &Pubkey,
commitment: Option<CommitmentConfig>,
) -> Result<RpcResponse<u64>> {
) -> Result<RpcResponse<f64>> {
let bank = self.bank(commitment);
let mint_account = bank.get_account(mint).ok_or_else(|| {
Error::invalid_params("Invalid param: could not find mint".to_string())
Expand Down Expand Up @@ -890,6 +899,8 @@ impl JsonRpcRequestProcessor {
.unwrap_or(0)
})
.sum();
let decimals = get_mint_decimals(&mint_account.data)?;
let supply = token_amount_to_ui_amount(supply, decimals);
Ok(new_response(&bank, supply))
}

Expand Down Expand Up @@ -917,20 +928,22 @@ impl JsonRpcRequestProcessor {
// Filter on Token Account state
RpcFilterType::DataSize(size_of::<TokenAccount>() as u64),
];
let decimals = get_mint_decimals(&mint_account.data)?;
let mut token_balances: Vec<RpcTokenAccountBalance> =
get_filtered_program_accounts(&bank, &mint_account.owner, filters)
.map(|(address, account)| {
let mut data = account.data.to_vec();
let amount = spl_token_v1_0::state::unpack(&mut data)
.map(|account: &mut TokenAccount| account.amount)
.unwrap_or(0);
let amount = token_amount_to_ui_amount(amount, decimals);
RpcTokenAccountBalance {
address: address.to_string(),
amount,
}
})
.collect();
token_balances.sort_by(|a, b| a.amount.cmp(&b.amount).reverse());
token_balances.sort_by(|a, b| a.amount.partial_cmp(&b.amount).unwrap().reverse());
token_balances.truncate(NUM_LARGEST_ACCOUNTS);
Ok(new_response(&bank, token_balances))
}
Expand Down Expand Up @@ -1099,6 +1112,19 @@ fn get_token_program_id_and_mint(
}
}

fn get_mint_decimals(data: &[u8]) -> Result<u8> {
let mut data = data.to_vec();
spl_token_v1_0::state::unpack(&mut data)
.map_err(|_| {
Error::invalid_params("Invalid param: Token mint could not be unpacked".to_string())
})
.map(|mint: &mut Mint| mint.decimals)
}

fn token_amount_to_ui_amount(amount: u64, decimals: u8) -> f64 {
amount as f64 / 10_usize.pow(decimals as u32) as f64
}

#[rpc]
pub trait RpcSol {
type Metadata;
Expand Down Expand Up @@ -1382,15 +1408,15 @@ pub trait RpcSol {
meta: Self::Metadata,
pubkey_str: String,
commitment: Option<CommitmentConfig>,
) -> Result<RpcResponse<u64>>;
) -> Result<RpcResponse<f64>>;

#[rpc(meta, name = "getTokenSupply")]
fn get_token_supply(
&self,
meta: Self::Metadata,
mint_str: String,
commitment: Option<CommitmentConfig>,
) -> Result<RpcResponse<u64>>;
) -> Result<RpcResponse<f64>>;

#[rpc(meta, name = "getTokenLargestAccounts")]
fn get_token_largest_accounts(
Expand Down Expand Up @@ -2015,7 +2041,7 @@ impl RpcSol for RpcSolImpl {
meta: Self::Metadata,
pubkey_str: String,
commitment: Option<CommitmentConfig>,
) -> Result<RpcResponse<u64>> {
) -> Result<RpcResponse<f64>> {
debug!(
"get_token_account_balance rpc request received: {:?}",
pubkey_str
Expand All @@ -2029,7 +2055,7 @@ impl RpcSol for RpcSolImpl {
meta: Self::Metadata,
mint_str: String,
commitment: Option<CommitmentConfig>,
) -> Result<RpcResponse<u64>> {
) -> Result<RpcResponse<f64>> {
debug!("get_token_supply rpc request received: {:?}", mint_str);
let mint = verify_pubkey(mint_str)?;
meta.get_token_supply(&mint, commitment)
Expand Down Expand Up @@ -4339,7 +4365,7 @@ pub mod tests {
mint,
owner,
delegate: COption::Some(delegate),
amount: 42,
amount: 420,
is_initialized: true,
is_native: false,
delegated_amount: 30,
Expand All @@ -4353,15 +4379,32 @@ pub mod tests {
let token_account_pubkey = Pubkey::new_rand();
bank.store_account(&token_account_pubkey, &token_account);

// Add the mint
let mut mint_data = [0; size_of::<Mint>()];
let mint_state: &mut Mint =
spl_token_v1_0::state::unpack_unchecked(&mut mint_data).unwrap();
*mint_state = Mint {
owner: COption::Some(owner),
decimals: 2,
is_initialized: true,
};
let mint_account = Account {
lamports: 111,
data: mint_data.to_vec(),
owner: spl_token_id_v1_0(),
..Account::default()
};
bank.store_account(&Pubkey::from_str(&mint.to_string()).unwrap(), &mint_account);

let req = format!(
r#"{{"jsonrpc":"2.0","id":1,"method":"getTokenAccountBalance","params":["{}"]}}"#,
token_account_pubkey,
);
let res = io.handle_request_sync(&req, meta.clone());
let result: Value = serde_json::from_str(&res.expect("actual response"))
.expect("actual response deserialization");
let balance: u64 = serde_json::from_value(result["result"]["value"].clone()).unwrap();
assert_eq!(balance, 42);
let balance: f64 = serde_json::from_value(result["result"]["value"].clone()).unwrap();
assert_eq!(balance, 4.2);

// Test non-existent token account
let req = format!(
Expand All @@ -4373,22 +4416,7 @@ pub mod tests {
.expect("actual response deserialization");
assert!(result.get("error").is_some());

// Add the mint, plus another token account to ensure getTokenSupply sums all mint accounts
let mut mint_data = [0; size_of::<Mint>()];
let mint_state: &mut Mint =
spl_token_v1_0::state::unpack_unchecked(&mut mint_data).unwrap();
*mint_state = Mint {
owner: COption::Some(owner),
decimals: 2,
is_initialized: true,
};
let mint_account = Account {
lamports: 111,
data: mint_data.to_vec(),
owner: spl_token_id_v1_0(),
..Account::default()
};
bank.store_account(&Pubkey::from_str(&mint.to_string()).unwrap(), &mint_account);
// Add another token account to ensure getTokenSupply sums all mint accounts
let other_token_account_pubkey = Pubkey::new_rand();
bank.store_account(&other_token_account_pubkey, &token_account);

Expand All @@ -4399,8 +4427,8 @@ pub mod tests {
let res = io.handle_request_sync(&req, meta.clone());
let result: Value = serde_json::from_str(&res.expect("actual response"))
.expect("actual response deserialization");
let supply: u64 = serde_json::from_value(result["result"]["value"].clone()).unwrap();
assert_eq!(supply, 2 * 42);
let supply: f64 = serde_json::from_value(result["result"]["value"].clone()).unwrap();
assert_eq!(supply, 2.0 * 4.2);

// Test non-existent mint address
let req = format!(
Expand Down Expand Up @@ -4655,11 +4683,11 @@ pub mod tests {
vec![
RpcTokenAccountBalance {
address: token_with_different_mint_pubkey.to_string(),
amount: 42,
amount: 0.42,
},
RpcTokenAccountBalance {
address: token_with_smaller_balance.to_string(),
amount: 10,
amount: 0.1,
}
]
);
Expand Down
8 changes: 4 additions & 4 deletions docs/src/apps/jsonrpc-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1031,15 +1031,15 @@ Returns the token balance of an SPL Token account.
#### Results:
- `RpcResponse<u64>` - RpcResponse JSON object with `value` field set to the balance
- `RpcResponse<f64>` - RpcResponse JSON object with `value` field set to the balance, using mint-prescribed decimals
#### Example:
```bash
// Request
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "id":1, "method":"getTokenAccountBalance", "params": ["7fUAJdStEuGbc3sM84cKRL6yYaaSstyLSU4ve5oovLS7"]}' http://localhost:8899
// Result
{"jsonrpc":"2.0","result":{"context":{"slot":1114},"value":9864,"id":1}
{"jsonrpc":"2.0","result":{"context":{"slot":1114},"value":98.64,"id":1}
```
### getTokenAccountsByDelegate
Expand Down Expand Up @@ -1125,15 +1125,15 @@ Returns the total supply of an SPL Token type.
#### Results:
- `RpcResponse<u64>` - RpcResponse JSON object with `value` field set to the total token supply
- `RpcResponse<f64>` - RpcResponse JSON object with `value` field set to the total token supply, using mint-prescribed decimals
#### Example:
```bash
// Request
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "id":1, "method":"getTokenSupply", "params": ["3wyAj7Rt1TWVPZVteFJPLa26JmLvdb1CAKEFZm3NY75E"]}' http://localhost:8899
// Result
{"jsonrpc":"2.0","result":{"context":{"slot":1114},"value":100000,"id":1}
{"jsonrpc":"2.0","result":{"context":{"slot":1114},"value":1000.0,"id":1}
```
### getTransactionCount
Expand Down

0 comments on commit 5b8e5bb

Please sign in to comment.