Return token amounts as floats (#11370)
* Return token amounts as floats * Floating-point equality * Return float and raw token amounts * Fix decimals and token rpcs for native-mint tokens * Fixup docs and review comments
This commit is contained in:
		| @@ -7,11 +7,18 @@ use spl_token_v1_0::{ | ||||
| }; | ||||
| use std::{mem::size_of, str::FromStr}; | ||||
|  | ||||
| // A helper function to convert spl_token_v1_0::id() as spl_sdk::pubkey::Pubkey to solana_sdk::pubkey::Pubkey | ||||
| // A helper function to convert spl_token_v1_0::id() as spl_sdk::pubkey::Pubkey to | ||||
| // solana_sdk::pubkey::Pubkey | ||||
| pub fn spl_token_id_v1_0() -> Pubkey { | ||||
|     Pubkey::from_str(&spl_token_v1_0::id().to_string()).unwrap() | ||||
| } | ||||
|  | ||||
| // A helper function to convert spl_token_v1_0::native_mint::id() as spl_sdk::pubkey::Pubkey to | ||||
| // solana_sdk::pubkey::Pubkey | ||||
| pub fn spl_token_v1_0_native_mint() -> Pubkey { | ||||
|     Pubkey::from_str(&spl_token_v1_0::native_mint::id().to_string()).unwrap() | ||||
| } | ||||
|  | ||||
| pub fn parse_token(data: &[u8]) -> Result<TokenAccountType, ParseAccountError> { | ||||
|     let mut data = data.to_vec(); | ||||
|     if data.len() == size_of::<Account>() { | ||||
|   | ||||
| @@ -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<RpcTokenAmount> { | ||||
|         Ok(self | ||||
|             .get_token_account_balance_with_commitment(pubkey, CommitmentConfig::default())? | ||||
|             .value) | ||||
| @@ -744,7 +744,7 @@ impl RpcClient { | ||||
|         &self, | ||||
|         pubkey: &Pubkey, | ||||
|         commitment_config: CommitmentConfig, | ||||
|     ) -> RpcResult<u64> { | ||||
|     ) -> RpcResult<RpcTokenAmount> { | ||||
|         self.send( | ||||
|             RpcRequest::GetTokenAccountBalance, | ||||
|             json!([pubkey.to_string(), commitment_config]), | ||||
| @@ -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<RpcTokenAmount> { | ||||
|         Ok(self | ||||
|             .get_token_supply_with_commitment(mint, CommitmentConfig::default())? | ||||
|             .value) | ||||
| @@ -851,7 +851,7 @@ impl RpcClient { | ||||
|         &self, | ||||
|         mint: &Pubkey, | ||||
|         commitment_config: CommitmentConfig, | ||||
|     ) -> RpcResult<u64> { | ||||
|     ) -> RpcResult<RpcTokenAmount> { | ||||
|         self.send( | ||||
|             RpcRequest::GetTokenSupply, | ||||
|             json!([mint.to_string(), commitment_config]), | ||||
|   | ||||
| @@ -9,6 +9,7 @@ use solana_sdk::{ | ||||
| use std::{collections::HashMap, net::SocketAddr}; | ||||
|  | ||||
| pub type RpcResult<T> = client_error::Result<Response<T>>; | ||||
| pub type RpcAmount = String; | ||||
|  | ||||
| #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] | ||||
| pub struct RpcResponseContext { | ||||
| @@ -220,9 +221,18 @@ pub struct RpcStakeActivation { | ||||
|     pub inactive: u64, | ||||
| } | ||||
|  | ||||
| #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] | ||||
| #[serde(rename_all = "camelCase")] | ||||
| pub struct RpcTokenAmount { | ||||
|     pub ui_amount: f64, | ||||
|     pub decimals: u8, | ||||
|     pub amount: RpcAmount, | ||||
| } | ||||
|  | ||||
| #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] | ||||
| #[serde(rename_all = "camelCase")] | ||||
| pub struct RpcTokenAccountBalance { | ||||
|     pub address: String, | ||||
|     pub amount: u64, | ||||
|     #[serde(flatten)] | ||||
|     pub amount: RpcTokenAmount, | ||||
| } | ||||
|   | ||||
							
								
								
									
										171
									
								
								core/src/rpc.rs
									
									
									
									
									
								
							
							
						
						
									
										171
									
								
								core/src/rpc.rs
									
									
									
									
									
								
							| @@ -8,7 +8,10 @@ use crate::{ | ||||
| use bincode::{config::Options, serialize}; | ||||
| use jsonrpc_core::{Error, Metadata, Result}; | ||||
| use jsonrpc_derive::rpc; | ||||
| use solana_account_decoder::{parse_token::spl_token_id_v1_0, UiAccount, UiAccountEncoding}; | ||||
| use solana_account_decoder::{ | ||||
|     parse_token::{spl_token_id_v1_0, spl_token_v1_0_native_mint}, | ||||
|     UiAccount, UiAccountEncoding, | ||||
| }; | ||||
| use solana_client::{ | ||||
|     rpc_config::*, | ||||
|     rpc_filter::{Memcmp, MemcmpEncodedBytes, RpcFilterType}, | ||||
| @@ -50,7 +53,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}, | ||||
| @@ -838,7 +841,7 @@ impl JsonRpcRequestProcessor { | ||||
|         &self, | ||||
|         pubkey: &Pubkey, | ||||
|         commitment: Option<CommitmentConfig>, | ||||
|     ) -> Result<RpcResponse<u64>> { | ||||
|     ) -> Result<RpcResponse<RpcTokenAmount>> { | ||||
|         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()) | ||||
| @@ -850,11 +853,14 @@ 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 = &Pubkey::from_str(&token_account.mint.to_string()) | ||||
|             .expect("Token account mint should be convertible to Pubkey"); | ||||
|         let (_, decimals) = get_mint_owner_and_decimals(&bank, &mint)?; | ||||
|         let balance = token_amount_to_ui_amount(token_account.amount, decimals); | ||||
|         Ok(new_response(&bank, balance)) | ||||
|     } | ||||
|  | ||||
| @@ -862,16 +868,15 @@ impl JsonRpcRequestProcessor { | ||||
|         &self, | ||||
|         mint: &Pubkey, | ||||
|         commitment: Option<CommitmentConfig>, | ||||
|     ) -> Result<RpcResponse<u64>> { | ||||
|     ) -> Result<RpcResponse<RpcTokenAmount>> { | ||||
|         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()) | ||||
|         })?; | ||||
|         if mint_account.owner != spl_token_id_v1_0() { | ||||
|         let (mint_owner, decimals) = get_mint_owner_and_decimals(&bank, mint)?; | ||||
|         if mint_owner != spl_token_id_v1_0() { | ||||
|             return Err(Error::invalid_params( | ||||
|                 "Invalid param: not a v1.0 Token mint".to_string(), | ||||
|             )); | ||||
|         } | ||||
|  | ||||
|         let filters = vec![ | ||||
|             // Filter on Mint address | ||||
|             RpcFilterType::Memcmp(Memcmp { | ||||
| @@ -882,7 +887,7 @@ impl JsonRpcRequestProcessor { | ||||
|             // Filter on Token Account state | ||||
|             RpcFilterType::DataSize(size_of::<TokenAccount>() as u64), | ||||
|         ]; | ||||
|         let supply = get_filtered_program_accounts(&bank, &mint_account.owner, filters) | ||||
|         let supply = get_filtered_program_accounts(&bank, &mint_owner, filters) | ||||
|             .map(|(_pubkey, account)| { | ||||
|                 let mut data = account.data.to_vec(); | ||||
|                 spl_token_v1_0::state::unpack(&mut data) | ||||
| @@ -890,6 +895,7 @@ impl JsonRpcRequestProcessor { | ||||
|                     .unwrap_or(0) | ||||
|             }) | ||||
|             .sum(); | ||||
|         let supply = token_amount_to_ui_amount(supply, decimals); | ||||
|         Ok(new_response(&bank, supply)) | ||||
|     } | ||||
|  | ||||
| @@ -899,10 +905,8 @@ impl JsonRpcRequestProcessor { | ||||
|         commitment: Option<CommitmentConfig>, | ||||
|     ) -> Result<RpcResponse<Vec<RpcTokenAccountBalance>>> { | ||||
|         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()) | ||||
|         })?; | ||||
|         if mint_account.owner != spl_token_id_v1_0() { | ||||
|         let (mint_owner, decimals) = get_mint_owner_and_decimals(&bank, mint)?; | ||||
|         if mint_owner != spl_token_id_v1_0() { | ||||
|             return Err(Error::invalid_params( | ||||
|                 "Invalid param: not a v1.0 Token mint".to_string(), | ||||
|             )); | ||||
| @@ -918,19 +922,27 @@ impl JsonRpcRequestProcessor { | ||||
|             RpcFilterType::DataSize(size_of::<TokenAccount>() as u64), | ||||
|         ]; | ||||
|         let mut token_balances: Vec<RpcTokenAccountBalance> = | ||||
|             get_filtered_program_accounts(&bank, &mint_account.owner, filters) | ||||
|             get_filtered_program_accounts(&bank, &mint_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 | ||||
|                 .amount | ||||
|                 .parse::<u64>() | ||||
|                 .unwrap() | ||||
|                 .cmp(&b.amount.amount.parse::<u64>().unwrap()) | ||||
|                 .reverse() | ||||
|         }); | ||||
|         token_balances.truncate(NUM_LARGEST_ACCOUNTS); | ||||
|         Ok(new_response(&bank, token_balances)) | ||||
|     } | ||||
| @@ -1077,15 +1089,13 @@ fn get_token_program_id_and_mint( | ||||
| ) -> Result<(Pubkey, Option<Pubkey>)> { | ||||
|     match token_account_filter { | ||||
|         TokenAccountsFilter::Mint(mint) => { | ||||
|             let mint_account = bank.get_account(&mint).ok_or_else(|| { | ||||
|                 Error::invalid_params("Invalid param: could not find mint".to_string()) | ||||
|             })?; | ||||
|             if mint_account.owner != spl_token_id_v1_0() { | ||||
|             let (mint_owner, _) = get_mint_owner_and_decimals(&bank, &mint)?; | ||||
|             if mint_owner != spl_token_id_v1_0() { | ||||
|                 return Err(Error::invalid_params( | ||||
|                     "Invalid param: not a v1.0 Token mint".to_string(), | ||||
|                 )); | ||||
|             } | ||||
|             Ok((mint_account.owner, Some(mint))) | ||||
|             Ok((mint_owner, Some(mint))) | ||||
|         } | ||||
|         TokenAccountsFilter::ProgramId(program_id) => { | ||||
|             if program_id == spl_token_id_v1_0() { | ||||
| @@ -1099,6 +1109,41 @@ fn get_token_program_id_and_mint( | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// Analyze a mint Pubkey that may be the native_mint and get the mint-account owner (token | ||||
| /// program_id) and decimals | ||||
| fn get_mint_owner_and_decimals(bank: &Arc<Bank>, mint: &Pubkey) -> Result<(Pubkey, u8)> { | ||||
|     if mint == &spl_token_v1_0_native_mint() { | ||||
|         // Uncomment the following once spl_token is bumped to a version that includes native_mint::DECIMALS | ||||
|         // Ok((spl_token_id_v1_0(), spl_token_v1_0::native_mint::DECIMALS)) | ||||
|         Ok((spl_token_id_v1_0(), 9)) | ||||
|     } else { | ||||
|         let mint_account = bank.get_account(mint).ok_or_else(|| { | ||||
|             Error::invalid_params("Invalid param: could not find mint".to_string()) | ||||
|         })?; | ||||
|         let decimals = get_mint_decimals(&mint_account.data)?; | ||||
|         Ok((mint_account.owner, decimals)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| 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) -> RpcTokenAmount { | ||||
|     // Use `amount_to_ui_amount()` once spl_token is bumped to a version that supports it: https://github.com/solana-labs/solana-program-library/pull/211 | ||||
|     let amount_decimals = amount as f64 / 10_usize.pow(decimals as u32) as f64; | ||||
|     RpcTokenAmount { | ||||
|         ui_amount: amount_decimals, | ||||
|         decimals, | ||||
|         amount: amount.to_string(), | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[rpc] | ||||
| pub trait RpcSol { | ||||
|     type Metadata; | ||||
| @@ -1382,7 +1427,7 @@ pub trait RpcSol { | ||||
|         meta: Self::Metadata, | ||||
|         pubkey_str: String, | ||||
|         commitment: Option<CommitmentConfig>, | ||||
|     ) -> Result<RpcResponse<u64>>; | ||||
|     ) -> Result<RpcResponse<RpcTokenAmount>>; | ||||
|  | ||||
|     #[rpc(meta, name = "getTokenSupply")] | ||||
|     fn get_token_supply( | ||||
| @@ -1390,7 +1435,7 @@ pub trait RpcSol { | ||||
|         meta: Self::Metadata, | ||||
|         mint_str: String, | ||||
|         commitment: Option<CommitmentConfig>, | ||||
|     ) -> Result<RpcResponse<u64>>; | ||||
|     ) -> Result<RpcResponse<RpcTokenAmount>>; | ||||
|  | ||||
|     #[rpc(meta, name = "getTokenLargestAccounts")] | ||||
|     fn get_token_largest_accounts( | ||||
| @@ -2015,7 +2060,7 @@ impl RpcSol for RpcSolImpl { | ||||
|         meta: Self::Metadata, | ||||
|         pubkey_str: String, | ||||
|         commitment: Option<CommitmentConfig>, | ||||
|     ) -> Result<RpcResponse<u64>> { | ||||
|     ) -> Result<RpcResponse<RpcTokenAmount>> { | ||||
|         debug!( | ||||
|             "get_token_account_balance rpc request received: {:?}", | ||||
|             pubkey_str | ||||
| @@ -2029,7 +2074,7 @@ impl RpcSol for RpcSolImpl { | ||||
|         meta: Self::Metadata, | ||||
|         mint_str: String, | ||||
|         commitment: Option<CommitmentConfig>, | ||||
|     ) -> Result<RpcResponse<u64>> { | ||||
|     ) -> Result<RpcResponse<RpcTokenAmount>> { | ||||
|         debug!("get_token_supply rpc request received: {:?}", mint_str); | ||||
|         let mint = verify_pubkey(mint_str)?; | ||||
|         meta.get_token_supply(&mint, commitment) | ||||
| @@ -4353,7 +4398,7 @@ pub mod tests { | ||||
|             mint, | ||||
|             owner, | ||||
|             delegate: COption::Some(delegate), | ||||
|             amount: 42, | ||||
|             amount: 420, | ||||
|             is_initialized: true, | ||||
|             is_native: false, | ||||
|             delegated_amount: 30, | ||||
| @@ -4367,27 +4412,7 @@ pub mod tests { | ||||
|         let token_account_pubkey = Pubkey::new_rand(); | ||||
|         bank.store_account(&token_account_pubkey, &token_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); | ||||
|  | ||||
|         // Test non-existent token account | ||||
|         let req = format!( | ||||
|             r#"{{"jsonrpc":"2.0","id":1,"method":"getTokenAccountBalance","params":["{}"]}}"#, | ||||
|             Pubkey::new_rand(), | ||||
|         ); | ||||
|         let res = io.handle_request_sync(&req, meta.clone()); | ||||
|         let result: Value = serde_json::from_str(&res.expect("actual response")) | ||||
|             .expect("actual response deserialization"); | ||||
|         assert!(result.get("error").is_some()); | ||||
|  | ||||
|         // Add the mint, plus another token account to ensure getTokenSupply sums all mint accounts | ||||
|         // 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(); | ||||
| @@ -4403,6 +4428,32 @@ pub mod tests { | ||||
|             ..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: RpcTokenAmount = | ||||
|             serde_json::from_value(result["result"]["value"].clone()).unwrap(); | ||||
|         let error = f64::EPSILON; | ||||
|         assert!((balance.ui_amount - 4.2).abs() < error); | ||||
|         assert_eq!(balance.amount, 420.to_string()); | ||||
|         assert_eq!(balance.decimals, 2); | ||||
|  | ||||
|         // Test non-existent token account | ||||
|         let req = format!( | ||||
|             r#"{{"jsonrpc":"2.0","id":1,"method":"getTokenAccountBalance","params":["{}"]}}"#, | ||||
|             Pubkey::new_rand(), | ||||
|         ); | ||||
|         let res = io.handle_request_sync(&req, meta.clone()); | ||||
|         let result: Value = serde_json::from_str(&res.expect("actual response")) | ||||
|             .expect("actual response deserialization"); | ||||
|         assert!(result.get("error").is_some()); | ||||
|  | ||||
|         // 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); | ||||
|  | ||||
| @@ -4413,8 +4464,12 @@ 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: RpcTokenAmount = | ||||
|             serde_json::from_value(result["result"]["value"].clone()).unwrap(); | ||||
|         let error = f64::EPSILON; | ||||
|         assert!((supply.ui_amount - 2.0 * 4.2).abs() < error); | ||||
|         assert_eq!(supply.amount, (2 * 420).to_string()); | ||||
|         assert_eq!(supply.decimals, 2); | ||||
|  | ||||
|         // Test non-existent mint address | ||||
|         let req = format!( | ||||
| @@ -4669,11 +4724,19 @@ pub mod tests { | ||||
|             vec![ | ||||
|                 RpcTokenAccountBalance { | ||||
|                     address: token_with_different_mint_pubkey.to_string(), | ||||
|                     amount: 42, | ||||
|                     amount: RpcTokenAmount { | ||||
|                         ui_amount: 0.42, | ||||
|                         decimals: 2, | ||||
|                         amount: "42".to_string(), | ||||
|                     } | ||||
|                 }, | ||||
|                 RpcTokenAccountBalance { | ||||
|                     address: token_with_smaller_balance.to_string(), | ||||
|                     amount: 10, | ||||
|                     amount: RpcTokenAmount { | ||||
|                         ui_amount: 0.1, | ||||
|                         decimals: 2, | ||||
|                         amount: "10".to_string(), | ||||
|                     } | ||||
|                 } | ||||
|             ] | ||||
|         ); | ||||
|   | ||||
| @@ -1031,7 +1031,11 @@ Returns the token balance of an SPL Token account. | ||||
|  | ||||
| #### Results: | ||||
|  | ||||
| - `RpcResponse<u64>` - RpcResponse JSON object with `value` field set to the balance | ||||
| The result will be an RpcResponse JSON object with `value` equal to a JSON object containing: | ||||
|  | ||||
| - `uiAmount: <f64>` - the balance, using mint-prescribed decimals | ||||
| - `amount: <string>` - the raw balance without decimals, a string representation of u64 | ||||
| - `decimals: <u8>` - number of base 10 digits to the right of the decimal place | ||||
|  | ||||
| #### Example: | ||||
|  | ||||
| @@ -1039,7 +1043,7 @@ Returns the token balance of an SPL Token account. | ||||
| // 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":{"uiAmount":98.64,"amount":"9864","decimals":2},"id":1} | ||||
| ``` | ||||
|  | ||||
| ### getTokenAccountsByDelegate | ||||
| @@ -1125,7 +1129,11 @@ Returns the total supply of an SPL Token type. | ||||
|  | ||||
| #### Results: | ||||
|  | ||||
| - `RpcResponse<u64>` - RpcResponse JSON object with `value` field set to the total token supply | ||||
| The result will be an RpcResponse JSON object with `value` equal to a JSON object containing: | ||||
|  | ||||
| - `uiAmount: <f64>` - the total token supply, using mint-prescribed decimals | ||||
| - `amount: <string>` - the raw total token supply without decimals, a string representation of u64 | ||||
| - `decimals: <u8>` - number of base 10 digits to the right of the decimal place | ||||
|  | ||||
| #### Example: | ||||
|  | ||||
| @@ -1133,7 +1141,7 @@ Returns the total supply of an SPL Token type. | ||||
| // 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":{"uiAmount":1000.0,"amount":"100000","decimals":2},"id":1} | ||||
| ``` | ||||
|  | ||||
| ### getTransactionCount | ||||
|   | ||||
		Reference in New Issue
	
	Block a user