diff --git a/fee-structure/src/lib.rs b/fee-structure/src/lib.rs index 17dae1e53..5f458c14d 100644 --- a/fee-structure/src/lib.rs +++ b/fee-structure/src/lib.rs @@ -4,7 +4,7 @@ #[cfg(not(target_os = "solana"))] use solana_message::SanitizedMessage; -use {solana_native_token::sol_to_lamports, std::num::NonZeroU32}; +use std::num::NonZeroU32; /// A fee and its associated compute unit limit #[derive(Debug, Default, Clone, Eq, PartialEq)] @@ -76,6 +76,11 @@ impl FeeDetails { pub const ACCOUNT_DATA_COST_PAGE_SIZE: u64 = 32_u64.saturating_mul(1024); impl FeeStructure { + #[deprecated( + since = "2.3.0", + note = "Use FeeStructure::default() and modify fields as needed" + )] + #[allow(deprecated)] pub fn new( sol_per_signature: f64, sol_per_write_lock: f64, @@ -85,12 +90,12 @@ impl FeeStructure { .iter() .map(|(limit, sol)| FeeBin { limit: *limit, - fee: sol_to_lamports(*sol), + fee: solana_native_token::sol_to_lamports(*sol), }) .collect::>(); FeeStructure { - lamports_per_signature: sol_to_lamports(sol_per_signature), - lamports_per_write_lock: sol_to_lamports(sol_per_write_lock), + lamports_per_signature: solana_native_token::sol_to_lamports(sol_per_signature), + lamports_per_write_lock: solana_native_token::sol_to_lamports(sol_per_write_lock), compute_fee_bins, } } @@ -201,7 +206,14 @@ impl FeeStructure { impl Default for FeeStructure { fn default() -> Self { - Self::new(0.000005, 0.0, vec![(1_400_000, 0.0)]) + Self { + lamports_per_signature: 5000, + lamports_per_write_lock: 0, + compute_fee_bins: vec![FeeBin { + limit: 1_400_000, + fee: 0, + }], + } } } diff --git a/genesis-config/src/lib.rs b/genesis-config/src/lib.rs index a265fe954..6ea80c520 100644 --- a/genesis-config/src/lib.rs +++ b/genesis-config/src/lib.rs @@ -15,7 +15,7 @@ use { chrono::{TimeZone, Utc}, memmap2::Mmap, solana_hash::Hash, - solana_native_token::lamports_to_sol, + solana_native_token::lamports_to_sol_str, solana_sha256_hasher::hash, solana_shred_version::compute_shred_version, std::{ @@ -259,7 +259,7 @@ impl fmt::Display for GenesisConfig { self.inflation, self.rent, self.fee_rate_governor, - lamports_to_sol( + lamports_to_sol_str( self.accounts .iter() .map(|(pubkey, account)| { diff --git a/native-token/src/lib.rs b/native-token/src/lib.rs index 98bbac2a8..1ca68c5d3 100644 --- a/native-token/src/lib.rs +++ b/native-token/src/lib.rs @@ -5,19 +5,54 @@ /// There are 10^9 lamports in one SOL pub const LAMPORTS_PER_SOL: u64 = 1_000_000_000; const LAMPORTS_PER_SOL_F64: f64 = LAMPORTS_PER_SOL as f64; +const SOL_DECIMALS: usize = 9; /// Approximately convert fractional native tokens (lamports) into native tokens (SOL) +#[deprecated(since = "2.3.0", note = "lamports_to_sol_str")] pub fn lamports_to_sol(lamports: u64) -> f64 { lamports as f64 / LAMPORTS_PER_SOL_F64 } /// Approximately convert native tokens (SOL) into fractional native tokens (lamports) +#[deprecated(since = "2.3.0", note = "sol_str_to_lamports")] pub fn sol_to_lamports(sol: f64) -> u64 { // NaNs return zero, negative values saturate to u64::MIN (i.e. zero), positive values saturate to u64::MAX // https://doc.rust-lang.org/reference/expressions/operator-expr.html#r-expr.as.numeric.float-as-int (sol * LAMPORTS_PER_SOL_F64).round() as u64 } +/// Convert fractional native tokens (lamports) into native tokens (SOL) +pub fn lamports_to_sol_str(lamports: u64) -> String { + // Left-pad zeros to decimals + 1, so we at least have an integer zero + let mut s = format!("{:01$}", lamports, SOL_DECIMALS + 1); + // Add the decimal point (Sorry, "," locales!) + s.insert(s.len() - SOL_DECIMALS, '.'); + let zeros_trimmed = s.trim_end_matches('0'); + zeros_trimmed.trim_end_matches('.').to_string() +} + +/// Convert native tokens (SOL) into fractional native tokens (lamports) +pub fn sol_str_to_lamports(sol_str: &str) -> Option { + if sol_str == "." { + None + } else { + let (sol, lamports) = sol_str.split_once('.').unwrap_or((sol_str, "")); + let sol = if sol.is_empty() { + 0 + } else { + sol.parse::().ok()? + }; + let lamports = if lamports.is_empty() { + 0 + } else { + format!("{:0<9}", lamports)[..SOL_DECIMALS].parse().ok()? + }; + LAMPORTS_PER_SOL + .checked_mul(sol) + .and_then(|x| x.checked_add(lamports)) + } +} + use std::fmt::{Debug, Display, Formatter, Result}; pub struct Sol(pub u64); @@ -50,6 +85,7 @@ mod tests { #[test] #[allow(clippy::excessive_precision)] + #[allow(deprecated)] fn test_lamports_to_sol() { assert_eq!(0.0, lamports_to_sol(0)); assert_eq!(0.000000001, lamports_to_sol(1)); @@ -86,6 +122,7 @@ mod tests { #[test] #[allow(clippy::excessive_precision)] + #[allow(deprecated)] fn test_sol_to_lamports() { assert_eq!(0, sol_to_lamports(0.0)); assert_eq!(1, sol_to_lamports(0.000000001)); @@ -115,4 +152,52 @@ mod tests { sol_to_lamports(18446744073.70954513549804687500) ); } + + #[test] + fn test_lamports_to_sol_str() { + assert_eq!("0", lamports_to_sol_str(0)); + assert_eq!("0.000000001", lamports_to_sol_str(1)); + assert_eq!("0.00000001", lamports_to_sol_str(10)); + assert_eq!("0.0000001", lamports_to_sol_str(100)); + assert_eq!("0.000001", lamports_to_sol_str(1000)); + assert_eq!("0.00001", lamports_to_sol_str(10000)); + assert_eq!("0.0001", lamports_to_sol_str(100000)); + assert_eq!("0.001", lamports_to_sol_str(1000000)); + assert_eq!("0.01", lamports_to_sol_str(10000000)); + assert_eq!("0.1", lamports_to_sol_str(100000000)); + assert_eq!("1", lamports_to_sol_str(1000000000)); + assert_eq!("4.1", lamports_to_sol_str(4_100_000_000)); + assert_eq!("8.2", lamports_to_sol_str(8_200_000_000)); + assert_eq!("8.50228288", lamports_to_sol_str(8_502_282_880)); + assert_eq!("18446744073.709551615", lamports_to_sol_str(u64::MAX)); + } + + #[test] + fn test_sol_str_to_lamports() { + assert_eq!(0, sol_str_to_lamports("0.0").unwrap()); + assert_eq!(1, sol_str_to_lamports("0.000000001").unwrap()); + assert_eq!(10, sol_str_to_lamports("0.00000001").unwrap()); + assert_eq!(100, sol_str_to_lamports("0.0000001").unwrap()); + assert_eq!(1000, sol_str_to_lamports("0.000001").unwrap()); + assert_eq!(10000, sol_str_to_lamports("0.00001").unwrap()); + assert_eq!(100000, sol_str_to_lamports("0.0001").unwrap()); + assert_eq!(1000000, sol_str_to_lamports("0.001").unwrap()); + assert_eq!(10000000, sol_str_to_lamports("0.01").unwrap()); + assert_eq!(100000000, sol_str_to_lamports("0.1").unwrap()); + assert_eq!(1000000000, sol_str_to_lamports("1").unwrap()); + assert_eq!(4_100_000_000, sol_str_to_lamports("4.1").unwrap()); + assert_eq!(8_200_000_000, sol_str_to_lamports("8.2").unwrap()); + assert_eq!(8_502_282_880, sol_str_to_lamports("8.50228288").unwrap()); + + assert_eq!( + u64::MAX, + sol_str_to_lamports("18446744073.709551615").unwrap() + ); + // bigger than u64::MAX, error + assert_eq!(None, sol_str_to_lamports("18446744073.709551616")); + // Negative, error + assert_eq!(None, sol_str_to_lamports("-0.000000001")); + // i64::MIN as string, error + assert_eq!(None, sol_str_to_lamports("-9223372036.854775808")); + } }