Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(big-numbers): Port big-numbers from ethers-rs #7

Merged
merged 4 commits into from
Mar 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ cargo run --example mnemonic_signer
- [ ] Deploy contracts
- [ ] Fork
- [ ] Testing
- [ ] Big numbers
- [ ] Comparison and equivalence
- [ ] Conversion
- [ ] Creating Instances
- [ ] Math operations
- [ ] Utilities
- [x] Big numbers
- [x] [Comparison and equivalence](./examples/big-numbers/examples/comparison_equivalence.rs)
- [x] [Conversion](./examples/big-numbers/examples/conversion.rs)
- [x] [Creating Instances](./examples/big-numbers/examples/create_instances.rs)
- [x] [Math operations](./examples/big-numbers/examples/math_operations.rs)
- [x] [Math utilities](./examples/big-numbers/examples/math_utilities.rs)
- [ ] Contracts
- [ ] Abigen
- [ ] Compile
Expand Down
18 changes: 18 additions & 0 deletions examples/big-numbers/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[package]
name = "examples-big-numbers"

publish.workspace = true
version.workspace = true
edition.workspace = true
rust-version.workspace = true
authors.workspace = true
license.workspace = true
homepage.workspace = true
repository.workspace = true

[dev-dependencies]
alloy-core.workspace = true
alloy-primitives.workspace = true
alloy-serde.workspace = true

eyre.workspace = true
36 changes: 36 additions & 0 deletions examples/big-numbers/examples/comparison_equivalence.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//! Example of comparison and equivalence of `U256` instances.

use alloy_primitives::U256;

/// `U256` implements traits in `std::cmp`, that means `U256` instances
/// can be easily compared using standard Rust operators.
fn main() {
// a == b
let a = U256::from(100_u32);
let b = U256::from(100_u32);
assert!(a == b);

// a < b
let a = U256::from(1_u32);
let b = U256::from(100_u32);
assert!(a < b);

// a <= b
let a = U256::from(100_u32);
let b = U256::from(100_u32);
assert!(a <= b);

// a > b
let a = U256::from(100_u32);
let b = U256::from(1_u32);
assert!(a > b);

// a >= b
let a = U256::from(100_u32);
let b = U256::from(100_u32);
assert!(a >= b);

// a == 0
let a = U256::ZERO;
assert!(a.is_zero());
}
36 changes: 36 additions & 0 deletions examples/big-numbers/examples/conversion.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//! Example of converting `U256` to native Rust types.

use alloy_primitives::{utils::format_units, U256};
use eyre::Result;

/// `U256` provides useful conversion functions to enable transformation into native Rust types.
///
/// It is important to note that converting a big-number to a floating point type (such as a `f32`
/// or `f64`) can result in a loss of precision, since you cannot fit 256 bits of information into
/// 64 bits.
///
/// However, there may be cases where you want to perform conversions for presentation purposes.
/// For example, you may want to display a large number to the user in a more readable format.
fn main() -> Result<()> {
let num = U256::from(42_u8);

let a: u128 = num.to::<u128>();
assert_eq!(a, 42);

let b: u64 = num.to::<u64>();
assert_eq!(b, 42);

let c: u32 = num.to::<u32>();
assert_eq!(c, 42);

let d: usize = num.to::<usize>();
assert_eq!(d, 42);

let e: String = num.to_string();
assert_eq!(e, "42");

let f: String = format_units(num, 4)?;
assert_eq!(f, "0.0042");

Ok(())
}
43 changes: 43 additions & 0 deletions examples/big-numbers/examples/create_instances.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//! Example of creating instances of `U256` from strings and numbers.

use alloy_primitives::{
utils::{parse_units, ParseUnits},
U256,
};
use eyre::Result;
use std::str::FromStr;

fn main() -> Result<()> {
// From strings
let a = U256::from_str("42")?;
assert_eq!(a.to_string(), "42");

let amount = "42";
let units = 4;
let b: ParseUnits = parse_units(amount, units)?;
assert_eq!(b.to_string(), "420000");

// From numbers
let c = U256::from(42_u8);
assert_eq!(c.to_string(), "42");

let d = U256::from(42_u16);
assert_eq!(d.to_string(), "42");

let e = U256::from(42_u32);
assert_eq!(e.to_string(), "42");

let f = U256::from(42_u64);
assert_eq!(f.to_string(), "42");

let g = U256::from(42_u128);
assert_eq!(g.to_string(), "42");

let h = U256::from(0x2a);
assert_eq!(h.to_string(), "42");

let i = U256::from(42);
assert_eq!(i.to_string(), "42");

Ok(())
}
54 changes: 54 additions & 0 deletions examples/big-numbers/examples/math_operations.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
//! 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(())
}
101 changes: 101 additions & 0 deletions examples/big-numbers/examples/math_utilities.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
//! 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(())
}
Loading