Contracts: parse amounts exactly, not via f64

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).
This commit is contained in:
jwinterm
2026-06-04 16:53:16 -04:00
parent 8c7c0d049d
commit 382cf869c9
2 changed files with 9 additions and 12 deletions
+4 -6
View File
@@ -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::<f64>()
.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![]),
+5 -6
View File
@@ -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<u64> {
// Parse a value expressed in Grin (e.g. "1.5") into nanograms.
fn parse_grin_amount(arg: &str, name: &str) -> Result<u64, ParseError> {
match arg.parse::<f64>() {
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(