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: add wad_ray_math with tests #215

Merged
merged 4 commits into from
Nov 15, 2023
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
1 change: 1 addition & 0 deletions src/math/src/lib.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ mod sha512;

#[cfg(test)]
mod tests;
mod wad_ray_math;
mod zellers_congruence;
use integer::{
u8_wide_mul, u16_wide_mul, u32_wide_mul, u64_wide_mul, u128_wide_mul, u256_overflow_mul,
Expand Down
1 change: 1 addition & 0 deletions src/math/src/tests.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ mod perfect_number_test;
mod sha256_test;
mod sha512_test;
mod test_keccak256;
mod wad_ray_math_test;
mod zellers_congruence_test;
159 changes: 159 additions & 0 deletions src/math/src/tests/wad_ray_math_test.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
use alexandria_math::wad_ray_math::{
ray_div, ray_mul, wad_div, wad_mul, ray_to_wad, wad_to_ray, ray, wad, half_ray, half_wad
};
use alexandria_math::{pow};

// conversion
#[test]
#[available_gas(2000000)]
fn test_wad_to_ray_conversion() {
let a = 5 * pow(10, 17); // 0.5e18
let expected = 5 * pow(10, 26); // 0.5e27
assert(wad_to_ray(a) == expected, 'Wrong wad_to_ray conversion');
}

#[test]
#[available_gas(2000000)]
fn test_ray_to_wad_conversion() {
let a = 5 * pow(10, 26); // 0.5e27
let expected = 5 * pow(10, 17); // 0.5e18
assert(ray_to_wad(a) == expected, 'Wrong ray_to_wad conversion');
}

// wad
#[test]
#[available_gas(2000000)]
#[should_panic()]
fn test_revertWhen_wad_mul_overflow() {
wad_mul(pow(2, 128), pow(2, 128));
}

#[test]
#[available_gas(2000000)]
fn test_wad_mul_trivial() {
assert(wad_mul(pow(2, 128) - 1, wad()) == pow(2, 128) - 1, 'Wrong result: 2**128 -1 * 1e18');
assert(wad_mul(0, 0) == 0, 'Wrong result: 0 * 0');
assert(wad_mul(0, wad()) == 0, 'Wrong result: 0 * 1e18');
assert(wad_mul(wad(), 0) == 0, 'Wrong result: 1e18 * 0');
assert(wad_mul(wad(), wad()) == wad(), 'Wrong result: 1e18 * 1e18 ');
}

#[test]
#[available_gas(2000000)]
fn test_wad_mul_fractions() {
let val: u256 = 2 * pow(10, 17); // 0.2e18
assert(wad_mul(wad(), val) == val, 'Wrong result: 1e18 * 0.2e18');
assert(wad_mul(wad() * 2, val) == val * 2, 'Wrong result: 2e18 * 0.2e18');
}

#[test]
#[available_gas(2000000)]
#[should_panic()]
fn test_revertWhen_wad_div_zero() {
wad_div(wad(), 0);
}

#[test]
#[available_gas(3000000)]
fn test_wad_div_trivial() {
assert(wad_div(pow(2, 128) - 1, wad()) == pow(2, 128) - 1, 'Wrong result: 2**128 -1 / 1e18');
assert(wad_div(0, pow(2, 128) - 1) == 0, 'Wrong result: 0 / 2**128 -1');
assert(wad_div(wad(), wad()) == wad(), 'Wrong result: 1e18 / 1e18');
}

#[test]
#[available_gas(2000000)]
fn test_wad_div_fractions() {
assert(wad_div(wad() * 2, wad() * 2) == wad(), 'Wrong result: 2e18 / 2e18');
assert(wad_div(wad(), wad() * 2) == half_wad(), 'Wrong result: 1e18 / 2e18');
}

#[test]
#[available_gas(2000000)]
fn test_wad_mul_rounding() {
let a = 950000000000005647;
let b = 1000000000;
let expected = 950000000;
assert(wad_mul(a, b) == expected, 'Wrong rounding down: a * b');
assert(wad_mul(b, a) == expected, 'Wrong rounding down: b * a');
}

#[test]
#[available_gas(2000000)]
fn test_wad_mul_rounding_up() {
let a = pow(10, 18) - 1;
let b = 2;
let expected = 2;
assert(wad_mul(a, b) == expected, 'Wrong rounding: a * b');
assert(wad_mul(b, a) == expected, 'Wrong rounding: b * a');
}


// wad
#[test]
#[available_gas(2000000)]
#[should_panic()]
fn test_revertWhen_ray_mul_overflow() {
ray_mul(pow(2, 128), pow(2, 128));
}

#[test]
#[available_gas(2000000)]
fn test_ray_mul_trivial() {
assert(ray_mul(pow(2, 128) - 1, ray()) == pow(2, 128) - 1, 'Wrong result: 2**128 -1 * 1e27');
assert(ray_mul(0, 0) == 0, 'Wrong result: 0 * 0');
assert(ray_mul(0, ray()) == 0, 'Wrong result: 0 * 1e27');
assert(ray_mul(ray(), 0) == 0, 'Wrong result: 1e27 * 0');
assert(ray_mul(ray(), ray()) == ray(), 'Wrong result: 1e27 * 1e27 ');
}

#[test]
#[available_gas(2000000)]
fn test_ray_mul_fractions() {
let val: u256 = 2 * pow(10, 26); // 0.2e27
assert(ray_mul(ray(), val) == val, 'Wrong result: 1e27 * 0.2e27');
assert(ray_mul(ray() * 2, val) == val * 2, 'Wrong result: 2e27 * 0.2e27');
}

#[test]
#[available_gas(2000000)]
#[should_panic()]
fn test_revertWhen_ray_div_zero() {
ray_div(ray(), 0);
}

#[test]
#[available_gas(3000000)]
fn test_ray_div_trivial() {
assert(ray_div(pow(2, 128) - 1, ray()) == pow(2, 128) - 1, 'Wrong result: 2**128 -1 / 1e27');
assert(ray_div(0, pow(2, 128) - 1) == 0, 'Wrong result: 0 / 2**128 -1');
assert(ray_div(ray(), ray()) == ray(), 'Wrong result: 1e27 / 1e27');
}

#[test]
#[available_gas(2000000)]
fn test_ray_div_fractions() {
assert(ray_div(ray() * 2, ray() * 2) == ray(), 'Wrong result: 2e27 / 2e27');
assert(ray_div(ray(), ray() * 2) == half_ray(), 'Wrong result: 1e27 / 2e27');
}

#[test]
#[available_gas(2000000)]
fn test_ray_mul_rounding() {
let a = pow(10, 18);
let b = 95 * pow(10, 26) + 5647;
let expected = 95 * pow(10, 17);
assert(ray_mul(a, b) == expected, 'Wrong rounding down: a * b');
assert(ray_mul(b, a) == expected, 'Wrong rounding down: b * a');
}


#[test]
#[available_gas(2000000)]
fn test_ray_mul_rounding_up() {
let a = pow(10, 27) - 1;
let b = 2;
let expected = 2;
assert(ray_mul(a, b) == expected, 'Wrong rounding up: a * b');
assert(ray_mul(b, a) == expected, 'Wrong rounding up: b * a');
}
101 changes: 101 additions & 0 deletions src/math/src/wad_ray_math.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/// Provides functions to perform calculations with Wad and Ray units
/// @dev Provides mul and div function for wads (decimal numbers with 18 digits of precision) and rays (decimal numbers
/// with 27 digits of precision)
/// Operations are rounded. If a value is >=.5, will be rounded up, otherwise rounded down.
/// https://github.com/aave/aave-v3-core/blob/master/contracts/protocol/libraries/math/WadRayMath.sol

const WAD: u256 = 1_000_000_000_000_000_000; // 1e18
const HALF_WAD: u256 = 500_000_000_000_000_000; // 0.5e18
const RAY: u256 = 1_000_000_000_000_000_000_000_000_000; // 1e27
const HALF_RAY: u256 = 500_000_000_000_000_000_000_000_000; // 0.5e27
const WAD_RAY_RATIO: u256 = 1_000_000_000; // 1e9
const HALF_WAD_RAY_RATIO: u256 = 500_000_000; // 0.5e9


/// Return the wad value
/// # Returns
/// * `u256` - The value
fn wad() -> u256 {
return WAD;
}

/// Return the ray value
/// # Returns
/// * `u256` - The value
fn ray() -> u256 {
return RAY;
}

/// Return the half wad value
/// # Returns
/// * `u256` - The value
fn half_wad() -> u256 {
return HALF_WAD;
}

/// Return the half ray value
/// # Returns
/// * `u256` - The value
fn half_ray() -> u256 {
return HALF_RAY;
}


/// Multiplies two wad, rounding half up to the nearest wad
/// # Arguments
/// * a Wad
/// * b Wad
/// # Returns
/// * a*b, in wad
fn wad_mul(a: u256, b: u256) -> u256 {
return (a * b + HALF_WAD) / WAD;
}

/// Divides two wad, rounding half up to the nearest wad
/// # Arguments
/// * a Wad
/// * b Wad
/// # Returns
/// * a/b, in wad
fn wad_div(a: u256, b: u256) -> u256 {
return (a * WAD + (b / 2)) / b;
}

/// Multiplies two ray, rounding half up to the nearest ray
/// # Arguments
/// * a Ray
/// * b Ray
/// # Returns
/// * a raymul b
fn ray_mul(a: u256, b: u256) -> u256 {
return (a * b + HALF_RAY) / RAY;
}

/// Divides two ray, rounding half up to the nearest ray
/// # Arguments
/// * a Ray
/// * b Ray
/// # Returns
/// * a raydiv b
fn ray_div(a: u256, b: u256) -> u256 {
return (a * RAY + (b / 2)) / b;
}

/// Casts ray down to wad
/// # Arguments
/// * a Ray
/// # Returns
/// * a converted to wad, rounded half up to the nearest wad
fn ray_to_wad(a: u256) -> u256 {
return (HALF_WAD_RAY_RATIO + a) / WAD_RAY_RATIO;
}

/// Converts wad up to ray
/// # Arguments
/// * a Wad
/// # Returns
/// * a converted to ray
fn wad_to_ray(a: u256) -> u256 {
return a * WAD_RAY_RATIO;
}