From 382cf869c9940d81acedb2277a94e6e00eb91740 Mon Sep 17 00:00:00 2001 From: jwinterm Date: Thu, 4 Jun 2026 16:53:16 -0400 Subject: [PATCH] Contracts: parse amounts exactly, not via f64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Custom output amounts (--make-outputs) and the CLI amount parser converted grin to nanogrin with `(g * GRIN_BASE as f64) as u64`, which can't represent all values exactly. Use core::amount_from_hr_string — the same exact integer parser already used by `send` and `invoice`. - contract OutputSelectionArgs::output_amounts (covers selection.rs). - wallet_args::parse_grin_amount. Addresses PR #754 review (types.rs, selection.rs, wallet_args.rs). --- libwallet/src/contract/types.rs | 10 ++++------ src/cmd/wallet_args.rs | 11 +++++------ 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/libwallet/src/contract/types.rs b/libwallet/src/contract/types.rs index 1c21668..dd041b8 100644 --- a/libwallet/src/contract/types.rs +++ b/libwallet/src/contract/types.rs @@ -15,7 +15,6 @@ //! Types related to a contract use crate::error::Error; -use crate::grin_core::consensus; use crate::slate_versions::ser as dalek_ser; use ed25519_dalek::PublicKey as DalekPublicKey; @@ -56,11 +55,10 @@ impl OutputSelectionArgs { Some(s) => s .split(",") .map(|amt| { - amt.parse::() - .map(|g| (g * consensus::GRIN_BASE as f64) as u64) - .map_err(|e| { - Error::GenericError(format!("Invalid output amount '{}': {}", amt, e)) - }) + // Exact decimal parsing (integer nanogrin), not f64. + crate::grin_core::core::amount_from_hr_string(amt).map_err(|e| { + Error::GenericError(format!("Invalid output amount '{}': {}", amt, e)) + }) }) .collect(), None => Ok(vec![]), diff --git a/src/cmd/wallet_args.rs b/src/cmd/wallet_args.rs index a1f50c1..e32da42 100644 --- a/src/cmd/wallet_args.rs +++ b/src/cmd/wallet_args.rs @@ -13,7 +13,6 @@ // limitations under the License. /// Argument parsing and error handling for wallet commands -use self::core::consensus; use crate::api::TLSConfig; use crate::cli::command_loop; use crate::config::GRIN_WALLET_DIR; @@ -290,13 +289,13 @@ fn parse_u64_or_none(arg: Option<&str>) -> Option { // Parse a value expressed in Grin (e.g. "1.5") into nanograms. fn parse_grin_amount(arg: &str, name: &str) -> Result { - match arg.parse::() { - Ok(g) => Ok((g * consensus::GRIN_BASE as f64) as u64), - Err(e) => Err(ParseError::ArgumentError(format!( + // Exact decimal parsing (integer nanogrin), not f64. + core::core::amount_from_hr_string(arg).map_err(|e| { + ParseError::ArgumentError(format!( "Could not parse {} '{}' as a number. e={}", name, arg, e - ))), - } + )) + }) } pub fn parse_global_args(