Skip to content

Commit

Permalink
feat: add wad_ray_math with tests (keep-starknet-strange#215)
Browse files Browse the repository at this point in the history
<!--- Please provide a general summary of your changes in the title
above -->

## Pull Request type

<!-- Please try to limit your pull request to one type; submit multiple
pull requests if needed. -->

Please check the type of change your PR introduces:

- [ ] Bugfix
- [X] Feature
- [ ] Code style update (formatting, renaming)
- [ ] Refactoring (no functional changes, no API changes)
- [ ] Build-related changes
- [ ] Documentation content changes
- [ ] Other (please describe):

## What is the current behavior?

Handle Wad & Ray mul/div 
<!-- Please describe the current behavior that you are modifying, or
link to a relevant issue. -->

Issue Number: keep-starknet-strange#211 

## What is the new behavior?

<!-- Please describe the behavior or changes that are being added by
this PR. -->

Add new functions to handle 18 & 27 decimals base maths

## Does this introduce a breaking change?

- [ ] Yes
- [X] No

<!-- If this does introduce a breaking change, please describe the
impact and migration path for existing applications below. -->

## Other information

<!-- Any other information that is important to this PR, such as
screenshots of how the component looks before and after the change. -->
  • Loading branch information
FabienCoutant authored Nov 15, 2023
1 parent d8473bb commit d32162a
Show file tree
Hide file tree
Showing 4 changed files with 262 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 @@ -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;
}

0 comments on commit d32162a

Please sign in to comment.