Using big numbers
Ethereum uses big numbers (also known as “bignums” or “arbitrary-precision integers”) to represent certain values in its codebase and in blockchain transactions. This is necessary because the EVM operates on a 256-bit word size, which is different from the usual 32-bit or 64-bit of modern machines. This was chosen for the ease of use with 256-bit cryptography (such as Keccak-256 hashes or secp256k1 signatures).
It is worth noting that Ethereum is not the only blockchain or cryptocurrency that uses big numbers. Many other blockchains and cryptocurrencies also use big numbers to represent values in their respective systems.
Utilities
In order to create an application, it is often necessary to convert between the representation of values that is easily understood by humans (such as ether) and the machine-readable form that is used by contracts and math functions (such as wei). This is particularly important when working with Ethereum, as certain values, such as balances and gas prices, must be expressed in wei when sending transactions, even if they are displayed to the user in a different format, such as ether or gwei. To help with this conversion, alloy::primitives::utils
provides two functions,
parse_units
and
format_units
, which allow you to easily convert between human-readable and machine-readable forms of values. parse_units can be used to convert a string representing a value in ether, such as “1.1”, into a big number in wei, which can be used in contracts and math functions. format_units can be used to convert a big number value into a human-readable string, which is useful for displaying values to users.
Example: math_operations
Example
To run this example:
- Clone the examples repository:
git clone git@github.com:alloy-rs/examples.git
- Run:
cargo run --example math_operations
//! Example of performing arithmetic operations with `U256`.
use std::ops::{Div, Mul};
use alloy::primitives::{utils::format_units, U256};
use eyre::Result;
/// `U256` implements traits in `std::ops`, that means it supports arithmetic operations
/// using standard Rust operators `+`, `-`. `*`, `/`, `%`, along with additional utilities to
/// perform common mathematical tasks.
fn main() -> Result<()> {
let a = U256::from(10);
let b = U256::from(2);
// addition
let sum = a + b;
assert_eq!(sum, U256::from(12));
// subtraction
let difference = a - b;
assert_eq!(difference, U256::from(8));
// multiplication
let product = a * b;
assert_eq!(product, U256::from(20));
// division
let quotient = a / b;
assert_eq!(quotient, U256::from(5));
// modulo
let remainder = a % b;
assert_eq!(remainder, U256::ZERO); // equivalent to `U256::from(0)`
// exponentiation
let power = a.pow(b);
assert_eq!(power, U256::from(100));
// Multiply two 'ether' numbers:
// Big numbers are integers, that can represent fixed point numbers.
// For instance, 1 ether has 18 fixed
// decimal places (1.000000000000000000), and its big number
// representation is 10^18 = 1000000000000000000.
// When we multiply such numbers we are summing up their exponents.
// So if we multiply 10^18 * 10^18 we get 10^36, that is obviously incorrect.
// In order to get the correct result we need to divide by 10^18.
let eth1 = U256::from(10_000000000000000000_u128); // 10 ether
let eth2 = U256::from(20_000000000000000000_u128); // 20 ether
let base = U256::from(10).pow(U256::from(18));
let mul = eth1.mul(eth2).div(base); // We also divide by 10^18
let s: String = format_units(mul, "ether")?;
assert_eq!(s, "200.000000000000000000"); // 200
Ok(())
}
Find the source code on Github here.
Example: math_utilities
Example
To run this example:
- Clone the examples repository:
git clone git@github.com:alloy-rs/examples.git
- Run:
cargo run --example math_utilities
//! Example of using math utilities to handle big numbers in 'wei' units.
use alloy::primitives::{
utils::{format_units, parse_units},
U256,
};
use eyre::Result;
fn main() -> Result<()> {
parse_units_example()?;
format_units_example()?;
Ok(())
}
/// dApps business logics handle big numbers in 'wei' units (i.e. sending transactions, on-chain
/// math, etc.). We provide convenient methods to map user inputs (usually in 'ether' or 'gwei')
/// into 'wei' format.
fn parse_units_example() -> Result<()> {
let pu = parse_units("1.0", "wei")?;
let num: U256 = pu.into();
assert_eq!(num, U256::from(1));
let pu = parse_units("1.0", "kwei")?;
let num: U256 = pu.into();
assert_eq!(num, U256::from(1000));
let pu = parse_units("1.0", "mwei")?;
let num: U256 = pu.into();
assert_eq!(num, U256::from(1000000));
let pu = parse_units("1.0", "gwei")?;
let num: U256 = pu.into();
assert_eq!(num, U256::from(1000000000));
let pu = parse_units("1.0", "szabo")?;
let num: U256 = pu.into();
assert_eq!(num, U256::from(1000000000000_u128));
let pu = parse_units("1.0", "finney")?;
let num: U256 = pu.into();
assert_eq!(num, U256::from(1000000000000000_u128));
let pu = parse_units("1.0", "ether")?;
let num: U256 = pu.into();
assert_eq!(num, U256::from(1000000000000000000_u128));
let pu = parse_units("1.0", 18)?;
let num: U256 = pu.into();
assert_eq!(num, U256::from(1000000000000000000_u128));
Ok(())
}
/// dApps business logics handle big numbers in 'wei' units (i.e. sending transactions, on-chain
/// math, etc.). On the other hand it is useful to convert big numbers into user readable formats
/// when displaying on a UI. Generally dApps display numbers in 'ether' and 'gwei' units,
/// respectively for displaying amounts and gas. The `format_units` function will format a big
/// number into a user readable string.
fn format_units_example() -> Result<()> {
// 1 ETHER = 10^18 WEI
let one_ether = U256::from(1000000000000000000_u128);
let num: String = format_units(one_ether, "wei")?;
assert_eq!(num, "1000000000000000000.0");
let num: String = format_units(one_ether, "gwei")?;
assert_eq!(num, "1000000000.000000000");
let num: String = format_units(one_ether, "ether")?;
assert_eq!(num, "1.000000000000000000");
// 1 GWEI = 10^9 WEI
let one_gwei = U256::from(1000000000_u128);
let num: String = format_units(one_gwei, 0)?;
assert_eq!(num, "1000000000.0");
let num: String = format_units(one_gwei, "wei")?;
assert_eq!(num, "1000000000.0");
let num: String = format_units(one_gwei, "kwei")?;
assert_eq!(num, "1000000.000");
let num: String = format_units(one_gwei, "mwei")?;
assert_eq!(num, "1000.000000");
let num: String = format_units(one_gwei, "gwei")?;
assert_eq!(num, "1.000000000");
let num: String = format_units(one_gwei, "szabo")?;
assert_eq!(num, "0.001000000000");
let num: String = format_units(one_gwei, "finney")?;
assert_eq!(num, "0.000001000000000");
let num: String = format_units(one_gwei, "ether")?;
assert_eq!(num, "0.000000001000000000");
Ok(())
}
Find the source code on Github here.