Make UiTokenAmount::ui_amount a String (#15447)

* Make UiTokenAmount::ui_amount a String

* Fixup solana-tokens

* Ignore spl downstream-project
This commit is contained in:
Tyera Eulberg 2021-02-22 13:05:45 -07:00 committed by GitHub
parent f7c0b69fd4
commit d14374bc9f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 152 additions and 127 deletions

View File

@ -24,6 +24,7 @@ use {
}; };
pub type StringAmount = String; pub type StringAmount = String;
pub type StringDecimals = String;
/// A duplicate representation of an Account for pretty JSON serialization /// A duplicate representation of an Account for pretty JSON serialization
#[derive(Serialize, Deserialize, Clone, Debug)] #[derive(Serialize, Deserialize, Clone, Debug)]

View File

@ -1,6 +1,6 @@
use crate::{ use crate::{
parse_account_data::{ParsableAccount, ParseAccountError}, parse_account_data::{ParsableAccount, ParseAccountError},
StringAmount, StringAmount, StringDecimals,
}; };
use solana_sdk::pubkey::Pubkey; use solana_sdk::pubkey::Pubkey;
use spl_token_v2_0::{ use spl_token_v2_0::{
@ -158,44 +158,37 @@ impl From<AccountState> for UiAccountState {
} }
} }
pub fn real_number_string(amount: u64, decimals: u8) -> StringDecimals {
let decimals = decimals as usize;
if decimals > 0 {
// Left-pad zeros to decimals + 1, so we at least have an integer zero
let mut s = format!("{:01$}", amount, decimals + 1);
// Add the decimal point (Sorry, "," locales!)
s.insert(s.len() - decimals, '.');
s
} else {
amount.to_string()
}
}
pub fn real_number_string_trimmed(amount: u64, decimals: u8) -> StringDecimals {
let s = real_number_string(amount, decimals);
let zeros_trimmed = s.trim_end_matches('0');
let decimal_trimmed = zeros_trimmed.trim_end_matches('.');
decimal_trimmed.to_string()
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct UiTokenAmount { pub struct UiTokenAmount {
pub ui_amount: f64, pub ui_amount: StringDecimals,
pub decimals: u8, pub decimals: u8,
pub amount: StringAmount, pub amount: StringAmount,
} }
impl UiTokenAmount {
pub fn real_number_string(&self) -> String {
let decimals = self.decimals as usize;
if decimals > 0 {
let amount = u64::from_str(&self.amount).unwrap_or(0);
// Left-pad zeros to decimals + 1, so we at least have an integer zero
let mut s = format!("{:01$}", amount, decimals + 1);
// Add the decimal point (Sorry, "," locales!)
s.insert(s.len() - decimals, '.');
s
} else {
self.amount.clone()
}
}
pub fn real_number_string_trimmed(&self) -> String {
let s = self.real_number_string();
let zeros_trimmed = s.trim_end_matches('0');
let decimal_trimmed = zeros_trimmed.trim_end_matches('.');
decimal_trimmed.to_string()
}
}
pub fn token_amount_to_ui_amount(amount: u64, decimals: u8) -> UiTokenAmount { pub fn token_amount_to_ui_amount(amount: u64, decimals: u8) -> UiTokenAmount {
// 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;
UiTokenAmount { UiTokenAmount {
ui_amount: amount_decimals, ui_amount: real_number_string_trimmed(amount, decimals),
decimals, decimals,
amount: amount.to_string(), amount: amount.to_string(),
} }
@ -253,7 +246,7 @@ mod test {
mint: mint_pubkey.to_string(), mint: mint_pubkey.to_string(),
owner: owner_pubkey.to_string(), owner: owner_pubkey.to_string(),
token_amount: UiTokenAmount { token_amount: UiTokenAmount {
ui_amount: 0.42, ui_amount: "0.42".to_string(),
decimals: 2, decimals: 2,
amount: "42".to_string() amount: "42".to_string()
}, },
@ -336,17 +329,40 @@ mod test {
#[test] #[test]
fn test_ui_token_amount_real_string() { fn test_ui_token_amount_real_string() {
assert_eq!(&real_number_string(1, 0), "1");
assert_eq!(&real_number_string_trimmed(1, 0), "1");
let token_amount = token_amount_to_ui_amount(1, 0); let token_amount = token_amount_to_ui_amount(1, 0);
assert_eq!(&token_amount.real_number_string(), "1"); assert_eq!(token_amount.ui_amount, real_number_string_trimmed(1, 0));
assert_eq!(&token_amount.real_number_string_trimmed(), "1"); assert_eq!(&real_number_string(1, 9), "0.000000001");
assert_eq!(&real_number_string_trimmed(1, 9), "0.000000001");
let token_amount = token_amount_to_ui_amount(1, 9); let token_amount = token_amount_to_ui_amount(1, 9);
assert_eq!(&token_amount.real_number_string(), "0.000000001"); assert_eq!(token_amount.ui_amount, real_number_string_trimmed(1, 9));
assert_eq!(&token_amount.real_number_string_trimmed(), "0.000000001"); assert_eq!(&real_number_string(1_000_000_000, 9), "1.000000000");
assert_eq!(&real_number_string_trimmed(1_000_000_000, 9), "1");
let token_amount = token_amount_to_ui_amount(1_000_000_000, 9); let token_amount = token_amount_to_ui_amount(1_000_000_000, 9);
assert_eq!(&token_amount.real_number_string(), "1.000000000"); assert_eq!(
assert_eq!(&token_amount.real_number_string_trimmed(), "1"); token_amount.ui_amount,
real_number_string_trimmed(1_000_000_000, 9)
);
assert_eq!(&real_number_string(1_234_567_890, 3), "1234567.890");
assert_eq!(&real_number_string_trimmed(1_234_567_890, 3), "1234567.89");
let token_amount = token_amount_to_ui_amount(1_234_567_890, 3); let token_amount = token_amount_to_ui_amount(1_234_567_890, 3);
assert_eq!(&token_amount.real_number_string(), "1234567.890"); assert_eq!(
assert_eq!(&token_amount.real_number_string_trimmed(), "1234567.89"); token_amount.ui_amount,
real_number_string_trimmed(1_234_567_890, 3)
);
assert_eq!(
&real_number_string(1_234_567_890, 25),
"0.0000000000000001234567890"
);
assert_eq!(
&real_number_string_trimmed(1_234_567_890, 25),
"0.000000000000000123456789"
);
let token_amount = token_amount_to_ui_amount(1_234_567_890, 20);
assert_eq!(
token_amount.ui_amount,
real_number_string_trimmed(1_234_567_890, 20)
);
} }
} }

View File

@ -1490,11 +1490,7 @@ impl fmt::Display for CliTokenAccount {
writeln!(f)?; writeln!(f)?;
writeln_name_value(f, "Address:", &self.address)?; writeln_name_value(f, "Address:", &self.address)?;
let account = &self.token_account; let account = &self.token_account;
writeln_name_value( writeln_name_value(f, "Balance:", &account.token_amount.ui_amount)?;
f,
"Balance:",
&account.token_amount.real_number_string_trimmed(),
)?;
let mint = format!( let mint = format!(
"{}{}", "{}{}",
account.mint, account.mint,
@ -1507,7 +1503,7 @@ impl fmt::Display for CliTokenAccount {
writeln!(f, "Delegation:")?; writeln!(f, "Delegation:")?;
writeln_name_value(f, " Delegate:", delegate)?; writeln_name_value(f, " Delegate:", delegate)?;
let allowance = account.delegated_amount.as_ref().unwrap(); let allowance = account.delegated_amount.as_ref().unwrap();
writeln_name_value(f, " Allowance:", &allowance.real_number_string_trimmed())?; writeln_name_value(f, " Allowance:", &allowance.ui_amount)?;
} }
writeln_name_value( writeln_name_value(
f, f,

View File

@ -5631,7 +5631,7 @@ pub mod tests {
let balance: UiTokenAmount = let balance: UiTokenAmount =
serde_json::from_value(result["result"]["value"].clone()).unwrap(); serde_json::from_value(result["result"]["value"].clone()).unwrap();
let error = f64::EPSILON; let error = f64::EPSILON;
assert!((balance.ui_amount - 4.2).abs() < error); assert!((f64::from_str(&balance.ui_amount).unwrap() - 4.2).abs() < error);
assert_eq!(balance.amount, 420.to_string()); assert_eq!(balance.amount, 420.to_string());
assert_eq!(balance.decimals, 2); assert_eq!(balance.decimals, 2);
@ -5656,7 +5656,7 @@ pub mod tests {
let supply: UiTokenAmount = let supply: UiTokenAmount =
serde_json::from_value(result["result"]["value"].clone()).unwrap(); serde_json::from_value(result["result"]["value"].clone()).unwrap();
let error = f64::EPSILON; let error = f64::EPSILON;
assert!((supply.ui_amount - 5.0).abs() < error); assert!((f64::from_str(&supply.ui_amount).unwrap() - 5.0).abs() < error);
assert_eq!(supply.amount, 500.to_string()); assert_eq!(supply.amount, 500.to_string());
assert_eq!(supply.decimals, 2); assert_eq!(supply.decimals, 2);
@ -5954,7 +5954,7 @@ pub mod tests {
RpcTokenAccountBalance { RpcTokenAccountBalance {
address: token_with_different_mint_pubkey.to_string(), address: token_with_different_mint_pubkey.to_string(),
amount: UiTokenAmount { amount: UiTokenAmount {
ui_amount: 0.42, ui_amount: "0.42".to_string(),
decimals: 2, decimals: 2,
amount: "42".to_string(), amount: "42".to_string(),
} }
@ -5962,7 +5962,7 @@ pub mod tests {
RpcTokenAccountBalance { RpcTokenAccountBalance {
address: token_with_smaller_balance.to_string(), address: token_with_smaller_balance.to_string(),
amount: UiTokenAmount { amount: UiTokenAmount {
ui_amount: 0.1, ui_amount: "0.1".to_string(),
decimals: 2, decimals: 2,
amount: "10".to_string(), amount: "10".to_string(),
} }
@ -6036,7 +6036,7 @@ pub mod tests {
"mint": mint.to_string(), "mint": mint.to_string(),
"owner": owner.to_string(), "owner": owner.to_string(),
"tokenAmount": { "tokenAmount": {
"uiAmount": 4.2, "uiAmount": "4.2".to_string(),
"decimals": 2, "decimals": 2,
"amount": "420", "amount": "420",
}, },
@ -6044,12 +6044,12 @@ pub mod tests {
"state": "initialized", "state": "initialized",
"isNative": true, "isNative": true,
"rentExemptReserve": { "rentExemptReserve": {
"uiAmount": 0.1, "uiAmount": "0.1".to_string(),
"decimals": 2, "decimals": 2,
"amount": "10", "amount": "10",
}, },
"delegatedAmount": { "delegatedAmount": {
"uiAmount": 0.3, "uiAmount": "0.3".to_string(),
"decimals": 2, "decimals": 2,
"amount": "30", "amount": "30",
}, },

View File

@ -105,5 +105,5 @@ EOF
_ example_helloworld _ example_helloworld
_ spl # _ spl
_ serum_dex _ serum_dex

View File

@ -104,6 +104,8 @@ pub struct UiTokenAmount {
pub decimals: u32, pub decimals: u32,
#[prost(string, tag = "3")] #[prost(string, tag = "3")]
pub amount: ::prost::alloc::string::String, pub amount: ::prost::alloc::string::String,
#[prost(string, tag = "4")]
pub ui_amount_string: ::prost::alloc::string::String,
} }
#[derive(Clone, PartialEq, ::prost::Message)] #[derive(Clone, PartialEq, ::prost::Message)]
pub struct Reward { pub struct Reward {

View File

@ -70,6 +70,7 @@ message UiTokenAmount {
double ui_amount = 1; double ui_amount = 1;
uint32 decimals = 2; uint32 decimals = 2;
string amount = 3; string amount = 3;
string ui_amount_string = 4;
} }
enum RewardType { enum RewardType {

View File

@ -1,5 +1,5 @@
use crate::StoredExtendedRewards; use crate::StoredExtendedRewards;
use solana_account_decoder::parse_token::UiTokenAmount; use solana_account_decoder::parse_token::{real_number_string_trimmed, UiTokenAmount};
use solana_sdk::{ use solana_sdk::{
hash::Hash, hash::Hash,
instruction::CompiledInstruction, instruction::CompiledInstruction,
@ -14,7 +14,10 @@ use solana_transaction_status::{
ConfirmedBlock, InnerInstructions, Reward, RewardType, TransactionByAddrInfo, ConfirmedBlock, InnerInstructions, Reward, RewardType, TransactionByAddrInfo,
TransactionStatusMeta, TransactionTokenBalance, TransactionWithStatusMeta, TransactionStatusMeta, TransactionTokenBalance, TransactionWithStatusMeta,
}; };
use std::convert::{TryFrom, TryInto}; use std::{
convert::{TryFrom, TryInto},
str::FromStr,
};
pub mod generated { pub mod generated {
include!(concat!( include!(concat!(
@ -383,9 +386,10 @@ impl From<TransactionTokenBalance> for generated::TokenBalance {
account_index: value.account_index as u32, account_index: value.account_index as u32,
mint: value.mint, mint: value.mint,
ui_token_amount: Some(generated::UiTokenAmount { ui_token_amount: Some(generated::UiTokenAmount {
ui_amount: value.ui_token_amount.ui_amount,
decimals: value.ui_token_amount.decimals as u32, decimals: value.ui_token_amount.decimals as u32,
amount: value.ui_token_amount.amount, amount: value.ui_token_amount.amount,
ui_amount_string: value.ui_token_amount.ui_amount,
..generated::UiTokenAmount::default()
}), }),
} }
} }
@ -398,7 +402,14 @@ impl From<generated::TokenBalance> for TransactionTokenBalance {
account_index: value.account_index as u8, account_index: value.account_index as u8,
mint: value.mint, mint: value.mint,
ui_token_amount: UiTokenAmount { ui_token_amount: UiTokenAmount {
ui_amount: ui_token_amount.ui_amount, ui_amount: if !ui_token_amount.ui_amount_string.is_empty() {
ui_token_amount.ui_amount_string
} else {
real_number_string_trimmed(
u64::from_str(&ui_token_amount.amount).unwrap_or(0),
ui_token_amount.decimals as u8,
)
},
decimals: ui_token_amount.decimals as u8, decimals: ui_token_amount.decimals as u8,
amount: ui_token_amount.amount, amount: ui_token_amount.amount,
}, },

View File

@ -12,7 +12,7 @@ use indicatif::{ProgressBar, ProgressStyle};
use pickledb::PickleDb; use pickledb::PickleDb;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use solana_account_decoder::parse_token::{ use solana_account_decoder::parse_token::{
pubkey_from_spl_token_v2_0, spl_token_v2_0_pubkey, token_amount_to_ui_amount, pubkey_from_spl_token_v2_0, real_number_string, spl_token_v2_0_pubkey,
}; };
use solana_client::{ use solana_client::{
client_error::{ClientError, Result as ClientResult}, client_error::{ClientError, Result as ClientResult},
@ -103,12 +103,14 @@ pub enum Error {
ClientError(#[from] ClientError), ClientError(#[from] ClientError),
#[error("Missing lockup authority")] #[error("Missing lockup authority")]
MissingLockupAuthority, MissingLockupAuthority,
#[error("insufficient funds in {0:?}, requires {1} SOL")] #[error("insufficient funds in {0:?}, requires {1}")]
InsufficientFunds(FundingSources, f64), InsufficientFunds(FundingSources, String),
#[error("Program error")] #[error("Program error")]
ProgramError(#[from] ProgramError), ProgramError(#[from] ProgramError),
#[error("Exit signal received")] #[error("Exit signal received")]
ExitSignal, ExitSignal,
#[error("Cannot support mint decimals that would overflow")]
InvalidMintDecimals,
} }
fn merge_allocations(allocations: &[Allocation]) -> Vec<Allocation> { fn merge_allocations(allocations: &[Allocation]) -> Vec<Allocation> {
@ -273,33 +275,34 @@ fn build_messages(
Some(allocation.lockup_date.parse::<DateTime<Utc>>().unwrap()) Some(allocation.lockup_date.parse::<DateTime<Utc>>().unwrap())
}; };
let (display_amount, decimals, do_create_associated_token_account) = let do_create_associated_token_account = if let Some(spl_token_args) = &args.spl_token_args
if let Some(spl_token_args) = &args.spl_token_args { {
let wallet_address = allocation.recipient.parse().unwrap(); let wallet_address = allocation.recipient.parse().unwrap();
let associated_token_address = get_associated_token_address( let associated_token_address = get_associated_token_address(
&wallet_address, &wallet_address,
&spl_token_v2_0_pubkey(&spl_token_args.mint), &spl_token_v2_0_pubkey(&spl_token_args.mint),
); );
let do_create_associated_token_account = let do_create_associated_token_account = client
client.get_multiple_accounts(&[pubkey_from_spl_token_v2_0( .get_multiple_accounts(&[pubkey_from_spl_token_v2_0(&associated_token_address)])?
&associated_token_address, [0]
)])?[0] .is_none();
.is_none(); if do_create_associated_token_account {
if do_create_associated_token_account { *created_accounts += 1;
*created_accounts += 1; }
} println!(
( "{:<44} {:>24}",
token_amount_to_ui_amount(allocation.amount, spl_token_args.decimals).ui_amount, allocation.recipient,
spl_token_args.decimals as usize, real_number_string(allocation.amount, spl_token_args.decimals)
do_create_associated_token_account, );
) do_create_associated_token_account
} else { } else {
(lamports_to_sol(allocation.amount), 9, false) println!(
}; "{:<44} {:>24.9}",
println!( allocation.recipient,
"{:<44} {:>24.2$}", lamports_to_sol(allocation.amount)
allocation.recipient, display_amount, decimals );
); false
};
let instructions = distribution_instructions( let instructions = distribution_instructions(
allocation, allocation,
&new_stake_account_keypair.pubkey(), &new_stake_account_keypair.pubkey(),
@ -719,7 +722,7 @@ fn check_payer_balances(
if staker_balance < undistributed_tokens { if staker_balance < undistributed_tokens {
return Err(Error::InsufficientFunds( return Err(Error::InsufficientFunds(
vec![FundingSource::StakeAccount].into(), vec![FundingSource::StakeAccount].into(),
lamports_to_sol(undistributed_tokens), lamports_to_sol(undistributed_tokens).to_string(),
)); ));
} }
if args.fee_payer.pubkey() == unlocked_sol_source { if args.fee_payer.pubkey() == unlocked_sol_source {
@ -727,7 +730,7 @@ fn check_payer_balances(
if balance < fees + total_unlocked_sol { if balance < fees + total_unlocked_sol {
return Err(Error::InsufficientFunds( return Err(Error::InsufficientFunds(
vec![FundingSource::SystemAccount, FundingSource::FeePayer].into(), vec![FundingSource::SystemAccount, FundingSource::FeePayer].into(),
lamports_to_sol(fees + total_unlocked_sol), lamports_to_sol(fees + total_unlocked_sol).to_string(),
)); ));
} }
} else { } else {
@ -735,14 +738,14 @@ fn check_payer_balances(
if fee_payer_balance < fees { if fee_payer_balance < fees {
return Err(Error::InsufficientFunds( return Err(Error::InsufficientFunds(
vec![FundingSource::FeePayer].into(), vec![FundingSource::FeePayer].into(),
lamports_to_sol(fees), lamports_to_sol(fees).to_string(),
)); ));
} }
let unlocked_sol_balance = client.get_balance(&unlocked_sol_source)?; let unlocked_sol_balance = client.get_balance(&unlocked_sol_source)?;
if unlocked_sol_balance < total_unlocked_sol { if unlocked_sol_balance < total_unlocked_sol {
return Err(Error::InsufficientFunds( return Err(Error::InsufficientFunds(
vec![FundingSource::SystemAccount].into(), vec![FundingSource::SystemAccount].into(),
lamports_to_sol(total_unlocked_sol), lamports_to_sol(total_unlocked_sol).to_string(),
)); ));
} }
} }
@ -751,7 +754,7 @@ fn check_payer_balances(
if balance < fees + undistributed_tokens { if balance < fees + undistributed_tokens {
return Err(Error::InsufficientFunds( return Err(Error::InsufficientFunds(
vec![FundingSource::SystemAccount, FundingSource::FeePayer].into(), vec![FundingSource::SystemAccount, FundingSource::FeePayer].into(),
lamports_to_sol(fees + undistributed_tokens), lamports_to_sol(fees + undistributed_tokens).to_string(),
)); ));
} }
} else { } else {
@ -759,14 +762,14 @@ fn check_payer_balances(
if fee_payer_balance < fees { if fee_payer_balance < fees {
return Err(Error::InsufficientFunds( return Err(Error::InsufficientFunds(
vec![FundingSource::FeePayer].into(), vec![FundingSource::FeePayer].into(),
lamports_to_sol(fees), lamports_to_sol(fees).to_string(),
)); ));
} }
let sender_balance = client.get_balance(&distribution_source)?; let sender_balance = client.get_balance(&distribution_source)?;
if sender_balance < undistributed_tokens { if sender_balance < undistributed_tokens {
return Err(Error::InsufficientFunds( return Err(Error::InsufficientFunds(
vec![FundingSource::SystemAccount].into(), vec![FundingSource::SystemAccount].into(),
lamports_to_sol(undistributed_tokens), lamports_to_sol(undistributed_tokens).to_string(),
)); ));
} }
} }
@ -1415,7 +1418,7 @@ mod tests {
sources, sources,
vec![FundingSource::SystemAccount, FundingSource::FeePayer].into() vec![FundingSource::SystemAccount, FundingSource::FeePayer].into()
); );
assert!((amount - (allocation_amount + fees_in_sol)).abs() < f64::EPSILON); assert_eq!(amount, (allocation_amount + fees_in_sol).to_string());
} else { } else {
panic!("check_payer_balances should have errored"); panic!("check_payer_balances should have errored");
} }
@ -1452,7 +1455,7 @@ mod tests {
sources, sources,
vec![FundingSource::SystemAccount, FundingSource::FeePayer].into() vec![FundingSource::SystemAccount, FundingSource::FeePayer].into()
); );
assert!((amount - (allocation_amount + fees_in_sol)).abs() < f64::EPSILON); assert_eq!(amount, (allocation_amount + fees_in_sol).to_string());
} else { } else {
panic!("check_payer_balances should have errored"); panic!("check_payer_balances should have errored");
} }
@ -1508,7 +1511,7 @@ mod tests {
let err_result = check_payer_balances(1, &allocations, &client, &args).unwrap_err(); let err_result = check_payer_balances(1, &allocations, &client, &args).unwrap_err();
if let Error::InsufficientFunds(sources, amount) = err_result { if let Error::InsufficientFunds(sources, amount) = err_result {
assert_eq!(sources, vec![FundingSource::SystemAccount].into()); assert_eq!(sources, vec![FundingSource::SystemAccount].into());
assert!((amount - allocation_amount).abs() < f64::EPSILON); assert_eq!(amount, allocation_amount.to_string());
} else { } else {
panic!("check_payer_balances should have errored"); panic!("check_payer_balances should have errored");
} }
@ -1522,7 +1525,7 @@ mod tests {
let err_result = check_payer_balances(1, &allocations, &client, &args).unwrap_err(); let err_result = check_payer_balances(1, &allocations, &client, &args).unwrap_err();
if let Error::InsufficientFunds(sources, amount) = err_result { if let Error::InsufficientFunds(sources, amount) = err_result {
assert_eq!(sources, vec![FundingSource::FeePayer].into()); assert_eq!(sources, vec![FundingSource::FeePayer].into());
assert!((amount - fees_in_sol).abs() < f64::EPSILON); assert_eq!(amount, fees_in_sol.to_string());
} else { } else {
panic!("check_payer_balances should have errored"); panic!("check_payer_balances should have errored");
} }
@ -1609,7 +1612,10 @@ mod tests {
check_payer_balances(1, &expensive_allocations, &client, &args).unwrap_err(); check_payer_balances(1, &expensive_allocations, &client, &args).unwrap_err();
if let Error::InsufficientFunds(sources, amount) = err_result { if let Error::InsufficientFunds(sources, amount) = err_result {
assert_eq!(sources, vec![FundingSource::StakeAccount].into()); assert_eq!(sources, vec![FundingSource::StakeAccount].into());
assert!((amount - (expensive_allocation_amount - unlocked_sol)).abs() < f64::EPSILON); assert_eq!(
amount,
(expensive_allocation_amount - unlocked_sol).to_string()
);
} else { } else {
panic!("check_payer_balances should have errored"); panic!("check_payer_balances should have errored");
} }
@ -1631,7 +1637,7 @@ mod tests {
sources, sources,
vec![FundingSource::SystemAccount, FundingSource::FeePayer].into() vec![FundingSource::SystemAccount, FundingSource::FeePayer].into()
); );
assert!((amount - (unlocked_sol + fees_in_sol)).abs() < f64::EPSILON); assert_eq!(amount, (unlocked_sol + fees_in_sol).to_string());
} else { } else {
panic!("check_payer_balances should have errored"); panic!("check_payer_balances should have errored");
} }
@ -1668,7 +1674,7 @@ mod tests {
sources, sources,
vec![FundingSource::SystemAccount, FundingSource::FeePayer].into() vec![FundingSource::SystemAccount, FundingSource::FeePayer].into()
); );
assert!((amount - (unlocked_sol + fees_in_sol)).abs() < f64::EPSILON); assert_eq!(amount, (unlocked_sol + fees_in_sol).to_string());
} else { } else {
panic!("check_payer_balances should have errored"); panic!("check_payer_balances should have errored");
} }
@ -1731,7 +1737,7 @@ mod tests {
let err_result = check_payer_balances(1, &allocations, &client, &args).unwrap_err(); let err_result = check_payer_balances(1, &allocations, &client, &args).unwrap_err();
if let Error::InsufficientFunds(sources, amount) = err_result { if let Error::InsufficientFunds(sources, amount) = err_result {
assert_eq!(sources, vec![FundingSource::SystemAccount].into()); assert_eq!(sources, vec![FundingSource::SystemAccount].into());
assert!((amount - unlocked_sol).abs() < f64::EPSILON); assert_eq!(amount, unlocked_sol.to_string());
} else { } else {
panic!("check_payer_balances should have errored"); panic!("check_payer_balances should have errored");
} }
@ -1745,7 +1751,7 @@ mod tests {
let err_result = check_payer_balances(1, &allocations, &client, &args).unwrap_err(); let err_result = check_payer_balances(1, &allocations, &client, &args).unwrap_err();
if let Error::InsufficientFunds(sources, amount) = err_result { if let Error::InsufficientFunds(sources, amount) = err_result {
assert_eq!(sources, vec![FundingSource::FeePayer].into()); assert_eq!(sources, vec![FundingSource::FeePayer].into());
assert!((amount - fees_in_sol).abs() < f64::EPSILON); assert_eq!(amount, fees_in_sol.to_string());
} else { } else {
panic!("check_payer_balances should have errored"); panic!("check_payer_balances should have errored");
} }

View File

@ -4,7 +4,8 @@ use crate::{
}; };
use console::style; use console::style;
use solana_account_decoder::parse_token::{ use solana_account_decoder::parse_token::{
pubkey_from_spl_token_v2_0, spl_token_v2_0_pubkey, token_amount_to_ui_amount, pubkey_from_spl_token_v2_0, real_number_string, spl_token_v2_0_pubkey,
token_amount_to_ui_amount,
}; };
use solana_client::rpc_client::RpcClient; use solana_client::rpc_client::RpcClient;
use solana_sdk::{instruction::Instruction, native_token::lamports_to_sol}; use solana_sdk::{instruction::Instruction, native_token::lamports_to_sol};
@ -109,7 +110,7 @@ pub fn check_spl_token_balances(
if fee_payer_balance < fees + account_creation_amount { if fee_payer_balance < fees + account_creation_amount {
return Err(Error::InsufficientFunds( return Err(Error::InsufficientFunds(
vec![FundingSource::FeePayer].into(), vec![FundingSource::FeePayer].into(),
lamports_to_sol(fees + account_creation_amount), lamports_to_sol(fees + account_creation_amount).to_string(),
)); ));
} }
let source_token_account = client let source_token_account = client
@ -142,20 +143,12 @@ pub fn print_token_balances(
let (actual, difference) = if let Ok(recipient_token) = let (actual, difference) = if let Ok(recipient_token) =
SplTokenAccount::unpack(&recipient_account.data) SplTokenAccount::unpack(&recipient_account.data)
{ {
let actual_ui_amount = let actual_ui_amount = real_number_string(recipient_token.amount, spl_token_args.decimals);
token_amount_to_ui_amount(recipient_token.amount, spl_token_args.decimals).ui_amount; let delta_string =
let expected_ui_amount = real_number_string(recipient_token.amount - expected, spl_token_args.decimals);
token_amount_to_ui_amount(expected, spl_token_args.decimals).ui_amount;
( (
style(format!( style(format!("{:>24}", actual_ui_amount)),
"{:>24.1$}", format!("{:>24}", delta_string),
actual_ui_amount, spl_token_args.decimals as usize
)),
format!(
"{:>24.1$}",
actual_ui_amount - expected_ui_amount,
spl_token_args.decimals as usize
),
) )
} else { } else {
( (
@ -164,12 +157,11 @@ pub fn print_token_balances(
) )
}; };
println!( println!(
"{:<44} {:>24.4$} {:>24} {:>24}", "{:<44} {:>24} {:>24} {:>24}",
allocation.recipient, allocation.recipient,
token_amount_to_ui_amount(expected, spl_token_args.decimals).ui_amount, real_number_string(expected, spl_token_args.decimals),
actual, actual,
difference, difference,
spl_token_args.decimals as usize
); );
Ok(()) Ok(())
} }

View File

@ -888,7 +888,7 @@ mod test {
"mint": keys[3].to_string(), "mint": keys[3].to_string(),
"authority": keys[0].to_string(), "authority": keys[0].to_string(),
"tokenAmount": { "tokenAmount": {
"uiAmount": 0.42, "uiAmount": "0.42",
"decimals": 2, "decimals": 2,
"amount": "42" "amount": "42"
} }
@ -920,7 +920,7 @@ mod test {
"multisigAuthority": keys[5].to_string(), "multisigAuthority": keys[5].to_string(),
"signers": keys[0..2].iter().map(|key| key.to_string()).collect::<Vec<String>>(), "signers": keys[0..2].iter().map(|key| key.to_string()).collect::<Vec<String>>(),
"tokenAmount": { "tokenAmount": {
"uiAmount": 0.42, "uiAmount": "0.42",
"decimals": 2, "decimals": 2,
"amount": "42" "amount": "42"
} }
@ -952,7 +952,7 @@ mod test {
"delegate": keys[3].to_string(), "delegate": keys[3].to_string(),
"owner": keys[0].to_string(), "owner": keys[0].to_string(),
"tokenAmount": { "tokenAmount": {
"uiAmount": 0.42, "uiAmount": "0.42",
"decimals": 2, "decimals": 2,
"amount": "42" "amount": "42"
} }
@ -984,7 +984,7 @@ mod test {
"multisigOwner": keys[5].to_string(), "multisigOwner": keys[5].to_string(),
"signers": keys[0..2].iter().map(|key| key.to_string()).collect::<Vec<String>>(), "signers": keys[0..2].iter().map(|key| key.to_string()).collect::<Vec<String>>(),
"tokenAmount": { "tokenAmount": {
"uiAmount": 0.42, "uiAmount": "0.42",
"decimals": 2, "decimals": 2,
"amount": "42" "amount": "42"
} }
@ -1014,7 +1014,7 @@ mod test {
"account": keys[2].to_string(), "account": keys[2].to_string(),
"mintAuthority": keys[0].to_string(), "mintAuthority": keys[0].to_string(),
"tokenAmount": { "tokenAmount": {
"uiAmount": 0.42, "uiAmount": "0.42",
"decimals": 2, "decimals": 2,
"amount": "42" "amount": "42"
} }
@ -1044,7 +1044,7 @@ mod test {
"mint": keys[2].to_string(), "mint": keys[2].to_string(),
"authority": keys[0].to_string(), "authority": keys[0].to_string(),
"tokenAmount": { "tokenAmount": {
"uiAmount": 0.42, "uiAmount": "0.42",
"decimals": 2, "decimals": 2,
"amount": "42" "amount": "42"
} }