diff --git a/account-decoder/src/lib.rs b/account-decoder/src/lib.rs index d3222128a9..611be374e4 100644 --- a/account-decoder/src/lib.rs +++ b/account-decoder/src/lib.rs @@ -24,6 +24,7 @@ use { }; pub type StringAmount = String; +pub type StringDecimals = String; /// A duplicate representation of an Account for pretty JSON serialization #[derive(Serialize, Deserialize, Clone, Debug)] diff --git a/account-decoder/src/parse_token.rs b/account-decoder/src/parse_token.rs index 2b416a519b..b9eebf8589 100644 --- a/account-decoder/src/parse_token.rs +++ b/account-decoder/src/parse_token.rs @@ -1,6 +1,6 @@ use crate::{ parse_account_data::{ParsableAccount, ParseAccountError}, - StringAmount, + StringAmount, StringDecimals, }; use solana_sdk::pubkey::Pubkey; use spl_token_v2_0::{ @@ -158,44 +158,37 @@ impl From 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)] #[serde(rename_all = "camelCase")] pub struct UiTokenAmount { - pub ui_amount: f64, + pub ui_amount: StringDecimals, pub decimals: u8, 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 { - // 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 { - ui_amount: amount_decimals, + ui_amount: real_number_string_trimmed(amount, decimals), decimals, amount: amount.to_string(), } @@ -253,7 +246,7 @@ mod test { mint: mint_pubkey.to_string(), owner: owner_pubkey.to_string(), token_amount: UiTokenAmount { - ui_amount: 0.42, + ui_amount: "0.42".to_string(), decimals: 2, amount: "42".to_string() }, @@ -336,17 +329,40 @@ mod test { #[test] 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); - assert_eq!(&token_amount.real_number_string(), "1"); - assert_eq!(&token_amount.real_number_string_trimmed(), "1"); + assert_eq!(token_amount.ui_amount, real_number_string_trimmed(1, 0)); + 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); - assert_eq!(&token_amount.real_number_string(), "0.000000001"); - assert_eq!(&token_amount.real_number_string_trimmed(), "0.000000001"); + assert_eq!(token_amount.ui_amount, real_number_string_trimmed(1, 9)); + 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); - assert_eq!(&token_amount.real_number_string(), "1.000000000"); - assert_eq!(&token_amount.real_number_string_trimmed(), "1"); + assert_eq!( + 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); - assert_eq!(&token_amount.real_number_string(), "1234567.890"); - assert_eq!(&token_amount.real_number_string_trimmed(), "1234567.89"); + assert_eq!( + 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) + ); } } diff --git a/cli-output/src/cli_output.rs b/cli-output/src/cli_output.rs index cc7be5fd63..916c9a12f9 100644 --- a/cli-output/src/cli_output.rs +++ b/cli-output/src/cli_output.rs @@ -1490,11 +1490,7 @@ impl fmt::Display for CliTokenAccount { writeln!(f)?; writeln_name_value(f, "Address:", &self.address)?; let account = &self.token_account; - writeln_name_value( - f, - "Balance:", - &account.token_amount.real_number_string_trimmed(), - )?; + writeln_name_value(f, "Balance:", &account.token_amount.ui_amount)?; let mint = format!( "{}{}", account.mint, @@ -1507,7 +1503,7 @@ impl fmt::Display for CliTokenAccount { writeln!(f, "Delegation:")?; writeln_name_value(f, " Delegate:", delegate)?; 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( f, diff --git a/core/src/rpc.rs b/core/src/rpc.rs index 8c43c5bef3..47f3f78f17 100644 --- a/core/src/rpc.rs +++ b/core/src/rpc.rs @@ -5631,7 +5631,7 @@ pub mod tests { let balance: UiTokenAmount = serde_json::from_value(result["result"]["value"].clone()).unwrap(); 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.decimals, 2); @@ -5656,7 +5656,7 @@ pub mod tests { let supply: UiTokenAmount = serde_json::from_value(result["result"]["value"].clone()).unwrap(); 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.decimals, 2); @@ -5954,7 +5954,7 @@ pub mod tests { RpcTokenAccountBalance { address: token_with_different_mint_pubkey.to_string(), amount: UiTokenAmount { - ui_amount: 0.42, + ui_amount: "0.42".to_string(), decimals: 2, amount: "42".to_string(), } @@ -5962,7 +5962,7 @@ pub mod tests { RpcTokenAccountBalance { address: token_with_smaller_balance.to_string(), amount: UiTokenAmount { - ui_amount: 0.1, + ui_amount: "0.1".to_string(), decimals: 2, amount: "10".to_string(), } @@ -6036,7 +6036,7 @@ pub mod tests { "mint": mint.to_string(), "owner": owner.to_string(), "tokenAmount": { - "uiAmount": 4.2, + "uiAmount": "4.2".to_string(), "decimals": 2, "amount": "420", }, @@ -6044,12 +6044,12 @@ pub mod tests { "state": "initialized", "isNative": true, "rentExemptReserve": { - "uiAmount": 0.1, + "uiAmount": "0.1".to_string(), "decimals": 2, "amount": "10", }, "delegatedAmount": { - "uiAmount": 0.3, + "uiAmount": "0.3".to_string(), "decimals": 2, "amount": "30", }, diff --git a/scripts/build-downstream-projects.sh b/scripts/build-downstream-projects.sh index bce8a02d31..d83750bb06 100755 --- a/scripts/build-downstream-projects.sh +++ b/scripts/build-downstream-projects.sh @@ -105,5 +105,5 @@ EOF _ example_helloworld -_ spl +# _ spl _ serum_dex diff --git a/storage-proto/proto/solana.storage.confirmed_block.rs b/storage-proto/proto/solana.storage.confirmed_block.rs index 929237c52d..3ca842dad7 100644 --- a/storage-proto/proto/solana.storage.confirmed_block.rs +++ b/storage-proto/proto/solana.storage.confirmed_block.rs @@ -104,6 +104,8 @@ pub struct UiTokenAmount { pub decimals: u32, #[prost(string, tag = "3")] pub amount: ::prost::alloc::string::String, + #[prost(string, tag = "4")] + pub ui_amount_string: ::prost::alloc::string::String, } #[derive(Clone, PartialEq, ::prost::Message)] pub struct Reward { diff --git a/storage-proto/src/confirmed_block.proto b/storage-proto/src/confirmed_block.proto index 5875c60782..d492a359d6 100644 --- a/storage-proto/src/confirmed_block.proto +++ b/storage-proto/src/confirmed_block.proto @@ -70,6 +70,7 @@ message UiTokenAmount { double ui_amount = 1; uint32 decimals = 2; string amount = 3; + string ui_amount_string = 4; } enum RewardType { diff --git a/storage-proto/src/convert.rs b/storage-proto/src/convert.rs index aa78ef6332..ce36ef8e0c 100644 --- a/storage-proto/src/convert.rs +++ b/storage-proto/src/convert.rs @@ -1,5 +1,5 @@ use crate::StoredExtendedRewards; -use solana_account_decoder::parse_token::UiTokenAmount; +use solana_account_decoder::parse_token::{real_number_string_trimmed, UiTokenAmount}; use solana_sdk::{ hash::Hash, instruction::CompiledInstruction, @@ -14,7 +14,10 @@ use solana_transaction_status::{ ConfirmedBlock, InnerInstructions, Reward, RewardType, TransactionByAddrInfo, TransactionStatusMeta, TransactionTokenBalance, TransactionWithStatusMeta, }; -use std::convert::{TryFrom, TryInto}; +use std::{ + convert::{TryFrom, TryInto}, + str::FromStr, +}; pub mod generated { include!(concat!( @@ -383,9 +386,10 @@ impl From for generated::TokenBalance { account_index: value.account_index as u32, mint: value.mint, ui_token_amount: Some(generated::UiTokenAmount { - ui_amount: value.ui_token_amount.ui_amount, decimals: value.ui_token_amount.decimals as u32, amount: value.ui_token_amount.amount, + ui_amount_string: value.ui_token_amount.ui_amount, + ..generated::UiTokenAmount::default() }), } } @@ -398,7 +402,14 @@ impl From for TransactionTokenBalance { account_index: value.account_index as u8, mint: value.mint, 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, amount: ui_token_amount.amount, }, diff --git a/tokens/src/commands.rs b/tokens/src/commands.rs index d2a6187a5a..914f20ed58 100644 --- a/tokens/src/commands.rs +++ b/tokens/src/commands.rs @@ -12,7 +12,7 @@ use indicatif::{ProgressBar, ProgressStyle}; use pickledb::PickleDb; use serde::{Deserialize, Serialize}; 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::{ client_error::{ClientError, Result as ClientResult}, @@ -103,12 +103,14 @@ pub enum Error { ClientError(#[from] ClientError), #[error("Missing lockup authority")] MissingLockupAuthority, - #[error("insufficient funds in {0:?}, requires {1} SOL")] - InsufficientFunds(FundingSources, f64), + #[error("insufficient funds in {0:?}, requires {1}")] + InsufficientFunds(FundingSources, String), #[error("Program error")] ProgramError(#[from] ProgramError), #[error("Exit signal received")] ExitSignal, + #[error("Cannot support mint decimals that would overflow")] + InvalidMintDecimals, } fn merge_allocations(allocations: &[Allocation]) -> Vec { @@ -273,33 +275,34 @@ fn build_messages( Some(allocation.lockup_date.parse::>().unwrap()) }; - let (display_amount, decimals, do_create_associated_token_account) = - if let Some(spl_token_args) = &args.spl_token_args { - let wallet_address = allocation.recipient.parse().unwrap(); - let associated_token_address = get_associated_token_address( - &wallet_address, - &spl_token_v2_0_pubkey(&spl_token_args.mint), - ); - let do_create_associated_token_account = - client.get_multiple_accounts(&[pubkey_from_spl_token_v2_0( - &associated_token_address, - )])?[0] - .is_none(); - if do_create_associated_token_account { - *created_accounts += 1; - } - ( - token_amount_to_ui_amount(allocation.amount, spl_token_args.decimals).ui_amount, - spl_token_args.decimals as usize, - do_create_associated_token_account, - ) - } else { - (lamports_to_sol(allocation.amount), 9, false) - }; - println!( - "{:<44} {:>24.2$}", - allocation.recipient, display_amount, decimals - ); + let do_create_associated_token_account = if let Some(spl_token_args) = &args.spl_token_args + { + let wallet_address = allocation.recipient.parse().unwrap(); + let associated_token_address = get_associated_token_address( + &wallet_address, + &spl_token_v2_0_pubkey(&spl_token_args.mint), + ); + let do_create_associated_token_account = client + .get_multiple_accounts(&[pubkey_from_spl_token_v2_0(&associated_token_address)])? + [0] + .is_none(); + if do_create_associated_token_account { + *created_accounts += 1; + } + println!( + "{:<44} {:>24}", + allocation.recipient, + real_number_string(allocation.amount, spl_token_args.decimals) + ); + do_create_associated_token_account + } else { + println!( + "{:<44} {:>24.9}", + allocation.recipient, + lamports_to_sol(allocation.amount) + ); + false + }; let instructions = distribution_instructions( allocation, &new_stake_account_keypair.pubkey(), @@ -719,7 +722,7 @@ fn check_payer_balances( if staker_balance < undistributed_tokens { return Err(Error::InsufficientFunds( vec![FundingSource::StakeAccount].into(), - lamports_to_sol(undistributed_tokens), + lamports_to_sol(undistributed_tokens).to_string(), )); } if args.fee_payer.pubkey() == unlocked_sol_source { @@ -727,7 +730,7 @@ fn check_payer_balances( if balance < fees + total_unlocked_sol { return Err(Error::InsufficientFunds( vec![FundingSource::SystemAccount, FundingSource::FeePayer].into(), - lamports_to_sol(fees + total_unlocked_sol), + lamports_to_sol(fees + total_unlocked_sol).to_string(), )); } } else { @@ -735,14 +738,14 @@ fn check_payer_balances( if fee_payer_balance < fees { return Err(Error::InsufficientFunds( vec![FundingSource::FeePayer].into(), - lamports_to_sol(fees), + lamports_to_sol(fees).to_string(), )); } let unlocked_sol_balance = client.get_balance(&unlocked_sol_source)?; if unlocked_sol_balance < total_unlocked_sol { return Err(Error::InsufficientFunds( 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 { return Err(Error::InsufficientFunds( vec![FundingSource::SystemAccount, FundingSource::FeePayer].into(), - lamports_to_sol(fees + undistributed_tokens), + lamports_to_sol(fees + undistributed_tokens).to_string(), )); } } else { @@ -759,14 +762,14 @@ fn check_payer_balances( if fee_payer_balance < fees { return Err(Error::InsufficientFunds( vec![FundingSource::FeePayer].into(), - lamports_to_sol(fees), + lamports_to_sol(fees).to_string(), )); } let sender_balance = client.get_balance(&distribution_source)?; if sender_balance < undistributed_tokens { return Err(Error::InsufficientFunds( vec![FundingSource::SystemAccount].into(), - lamports_to_sol(undistributed_tokens), + lamports_to_sol(undistributed_tokens).to_string(), )); } } @@ -1415,7 +1418,7 @@ mod tests { sources, 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 { panic!("check_payer_balances should have errored"); } @@ -1452,7 +1455,7 @@ mod tests { sources, 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 { 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(); if let Error::InsufficientFunds(sources, amount) = err_result { assert_eq!(sources, vec![FundingSource::SystemAccount].into()); - assert!((amount - allocation_amount).abs() < f64::EPSILON); + assert_eq!(amount, allocation_amount.to_string()); } else { 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(); if let Error::InsufficientFunds(sources, amount) = err_result { assert_eq!(sources, vec![FundingSource::FeePayer].into()); - assert!((amount - fees_in_sol).abs() < f64::EPSILON); + assert_eq!(amount, fees_in_sol.to_string()); } else { panic!("check_payer_balances should have errored"); } @@ -1609,7 +1612,10 @@ mod tests { check_payer_balances(1, &expensive_allocations, &client, &args).unwrap_err(); if let Error::InsufficientFunds(sources, amount) = err_result { 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 { panic!("check_payer_balances should have errored"); } @@ -1631,7 +1637,7 @@ mod tests { sources, 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 { panic!("check_payer_balances should have errored"); } @@ -1668,7 +1674,7 @@ mod tests { sources, 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 { 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(); if let Error::InsufficientFunds(sources, amount) = err_result { assert_eq!(sources, vec![FundingSource::SystemAccount].into()); - assert!((amount - unlocked_sol).abs() < f64::EPSILON); + assert_eq!(amount, unlocked_sol.to_string()); } else { 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(); if let Error::InsufficientFunds(sources, amount) = err_result { assert_eq!(sources, vec![FundingSource::FeePayer].into()); - assert!((amount - fees_in_sol).abs() < f64::EPSILON); + assert_eq!(amount, fees_in_sol.to_string()); } else { panic!("check_payer_balances should have errored"); } diff --git a/tokens/src/spl_token.rs b/tokens/src/spl_token.rs index bc9aa2a45f..cdcbb334f9 100644 --- a/tokens/src/spl_token.rs +++ b/tokens/src/spl_token.rs @@ -4,7 +4,8 @@ use crate::{ }; use console::style; 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_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 { return Err(Error::InsufficientFunds( 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 @@ -142,20 +143,12 @@ pub fn print_token_balances( let (actual, difference) = if let Ok(recipient_token) = SplTokenAccount::unpack(&recipient_account.data) { - let actual_ui_amount = - token_amount_to_ui_amount(recipient_token.amount, spl_token_args.decimals).ui_amount; - let expected_ui_amount = - token_amount_to_ui_amount(expected, spl_token_args.decimals).ui_amount; + let actual_ui_amount = real_number_string(recipient_token.amount, spl_token_args.decimals); + let delta_string = + real_number_string(recipient_token.amount - expected, spl_token_args.decimals); ( - style(format!( - "{:>24.1$}", - actual_ui_amount, spl_token_args.decimals as usize - )), - format!( - "{:>24.1$}", - actual_ui_amount - expected_ui_amount, - spl_token_args.decimals as usize - ), + style(format!("{:>24}", actual_ui_amount)), + format!("{:>24}", delta_string), ) } else { ( @@ -164,12 +157,11 @@ pub fn print_token_balances( ) }; println!( - "{:<44} {:>24.4$} {:>24} {:>24}", + "{:<44} {:>24} {:>24} {:>24}", allocation.recipient, - token_amount_to_ui_amount(expected, spl_token_args.decimals).ui_amount, + real_number_string(expected, spl_token_args.decimals), actual, difference, - spl_token_args.decimals as usize ); Ok(()) } diff --git a/transaction-status/src/parse_token.rs b/transaction-status/src/parse_token.rs index bf512b03ea..9e020032ae 100644 --- a/transaction-status/src/parse_token.rs +++ b/transaction-status/src/parse_token.rs @@ -888,7 +888,7 @@ mod test { "mint": keys[3].to_string(), "authority": keys[0].to_string(), "tokenAmount": { - "uiAmount": 0.42, + "uiAmount": "0.42", "decimals": 2, "amount": "42" } @@ -920,7 +920,7 @@ mod test { "multisigAuthority": keys[5].to_string(), "signers": keys[0..2].iter().map(|key| key.to_string()).collect::>(), "tokenAmount": { - "uiAmount": 0.42, + "uiAmount": "0.42", "decimals": 2, "amount": "42" } @@ -952,7 +952,7 @@ mod test { "delegate": keys[3].to_string(), "owner": keys[0].to_string(), "tokenAmount": { - "uiAmount": 0.42, + "uiAmount": "0.42", "decimals": 2, "amount": "42" } @@ -984,7 +984,7 @@ mod test { "multisigOwner": keys[5].to_string(), "signers": keys[0..2].iter().map(|key| key.to_string()).collect::>(), "tokenAmount": { - "uiAmount": 0.42, + "uiAmount": "0.42", "decimals": 2, "amount": "42" } @@ -1014,7 +1014,7 @@ mod test { "account": keys[2].to_string(), "mintAuthority": keys[0].to_string(), "tokenAmount": { - "uiAmount": 0.42, + "uiAmount": "0.42", "decimals": 2, "amount": "42" } @@ -1044,7 +1044,7 @@ mod test { "mint": keys[2].to_string(), "authority": keys[0].to_string(), "tokenAmount": { - "uiAmount": 0.42, + "uiAmount": "0.42", "decimals": 2, "amount": "42" }