diff --git a/cli/tests/it/script.rs b/cli/tests/it/script.rs index 2806a0780d91c..06ae1937dd4fe 100644 --- a/cli/tests/it/script.rs +++ b/cli/tests/it/script.rs @@ -837,7 +837,7 @@ contract Script0 is Script { transactions[0].arguments, vec![ "0x00a329c0648769A73afAc7F9381E08FB43dBEA72".to_string(), - "4294967296".to_string(), + "4294967296 [4.294e9]".to_string(), "-4294967296".to_string(), "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6".to_string(), "true".to_string(), @@ -928,7 +928,7 @@ contract Script0 is Script { transactions[0].arguments, vec![ "0x00a329c0648769A73afAc7F9381E08FB43dBEA72".to_string(), - "4294967296".to_string(), + "4294967296 [4.294e9]".to_string(), "-4294967296".to_string(), "0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6".to_string(), "true".to_string(), diff --git a/common/src/abi.rs b/common/src/abi.rs index 4010e01208350..02f137daf28e3 100644 --- a/common/src/abi.rs +++ b/common/src/abi.rs @@ -11,6 +11,9 @@ use ethers_core::{ use ethers_etherscan::{contract::ContractMetadata, errors::EtherscanError, Client}; use eyre::{ContextCompat, Result, WrapErr}; use std::{future::Future, pin::Pin, str::FromStr}; +use yansi::Paint; + +use crate::calc::to_exponential_notation; /// Given a function and a vector of string arguments, it proceeds to convert the args to ethabi /// Tokens and then ABI encode them. @@ -157,7 +160,7 @@ pub fn format_token(param: &Token) -> String { Token::FixedBytes(bytes) => format!("0x{}", hex::encode(bytes)), Token::Bytes(bytes) => format!("0x{}", hex::encode(bytes)), Token::Int(num) => format!("{}", I256::from_raw(*num)), - Token::Uint(num) => num.to_string(), + Token::Uint(num) => format_uint_with_exponential_notation_hint(*num), Token::Bool(b) => format!("{b}"), Token::String(s) => s.to_string(), Token::FixedArray(tokens) => { @@ -175,6 +178,28 @@ pub fn format_token(param: &Token) -> String { } } +/// Formats a U256 number to string, adding an exponential notation _hint_ if it +/// is larger than `10_000`, with a precision of `4` figures, and trimming the +/// trailing zeros. +/// +/// Examples: +/// +/// ```text +/// 0 -> "0" +/// 1234 -> "1234" +/// 1234567890 -> "1234567890 [1.234e9]" +/// 1000000000000000000 -> "1000000000000000000 [1e18]" +/// 10000000000000000000000 -> "10000000000000000000000 [1e22]" +/// ``` +pub fn format_uint_with_exponential_notation_hint(num: U256) -> String { + if num.lt(&U256::from(10_000)) { + return num.to_string() + } + + let exp = to_exponential_notation(num, 4, true); + format!("{} {}", num, Paint::default(format!("[{}]", exp)).dimmed()) +} + /// Helper trait for converting types to Functions. Helpful for allowing the `call` /// function on the EVM to be generic over `String`, `&str` and `Function`. pub trait IntoFunction { diff --git a/common/src/calc.rs b/common/src/calc.rs index b4d6a0cb40558..295fb0a45d22e 100644 --- a/common/src/calc.rs +++ b/common/src/calc.rs @@ -35,6 +35,41 @@ where } } +/// Returns the number expressed as a string in exponential notation +/// with the given precision (number of significant figures), +/// optionally removing trailing zeros from the mantissa. +/// +/// Examples: +/// +/// ```text +/// precision = 4, trim_end_zeroes = false +/// 1234124124 -> 1.234e9 +/// 10000000 -> 1.000e7 +/// precision = 3, trim_end_zeroes = true +/// 1234124124 -> 1.23e9 +/// 10000000 -> 1e7 +/// ``` +#[inline] +pub fn to_exponential_notation(value: U256, precision: usize, trim_end_zeros: bool) -> String { + let stringified = value.to_string(); + let exponent = stringified.len() - 1; + let mut mantissa = stringified.chars().take(precision).collect::(); + + // optionally remove trailing zeros + if trim_end_zeros { + mantissa = mantissa.trim_end_matches('0').to_string(); + } + + // Place a decimal point only if needed + // e.g. 1234 -> 1.234e3 (needed) + // 5 -> 5 (not needed) + if mantissa.len() > 1 { + mantissa.insert(1, '.'); + } + + format!("{}e{}", mantissa, exponent) +} + #[cfg(test)] mod tests { use super::*; @@ -75,4 +110,23 @@ mod tests { let m = median_sorted(&values); assert_eq!(m, 45); } + + #[test] + fn test_format_to_exponential_notation() { + let value = 1234124124u64; + + let formatted = to_exponential_notation(value.into(), 4, false); + assert_eq!(formatted, "1.234e9"); + + let formatted = to_exponential_notation(value.into(), 3, true); + assert_eq!(formatted, "1.23e9"); + + let value = 10000000u64; + + let formatted = to_exponential_notation(value.into(), 4, false); + assert_eq!(formatted, "1.000e7"); + + let formatted = to_exponential_notation(value.into(), 3, true); + assert_eq!(formatted, "1e7"); + } }