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 alloy::primitives::{utils::format_units, U256};
use eyre::Result;
use std::ops::{Div, Mul};

/// `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.