Skip to content

Commit

Permalink
feat: add wad_ray_math with tests
Browse files Browse the repository at this point in the history
  • Loading branch information
FabienCoutant committed Nov 13, 2023
1 parent db4e021 commit 2021966
Show file tree
Hide file tree
Showing 4 changed files with 265 additions and 0 deletions.
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;
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;
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;
}

0 comments on commit 2021966

Please sign in to comment.