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 1 commit
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 @@ -12,6 +12,7 @@ mod mod_arithmetics;
mod perfect_number;
mod sha256;
mod sha512;
mod wad_ray_math;

#[cfg(test)]
mod tests;
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 @@ -14,3 +14,4 @@ mod sha256_test;
mod sha512_test;
mod test_keccak256;
mod zellers_congruence_test;
mod wad_ray_math_test;
160 changes: 160 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,160 @@
use core::debug::PrintTrait;
0xLucqs marked this conversation as resolved.
Show resolved Hide resolved
use alexandria_math::{pow};
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
};

// 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');
}
103 changes: 103 additions & 0 deletions src/math/src/wad_ray_math.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/// 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


/// 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 {
let half_b = b / 2;
return (a * WAD + half_b) / 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 {
let half_b = b / 2;
return (a * RAY + half_b) / 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 {
let half_ratio = WAD_RAY_RATIO / 2;
0xLucqs marked this conversation as resolved.
Show resolved Hide resolved
return (half_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;
}

Loading