Preliminary Repository's https://cantina.xyz/code/8b94fd62-eea5-46fb-bed6-36ec5adf36af/README.mdhttps://github.com/cantina-forks/thala-modules/tree/mai
Formal verification of key modules within the Aave V3 codebase was conducted using an aggressive threat modeling approach with the prover. The process rigorously tested abort conditions, state consistency, authorization checks, and asset conservation to ensure secure and correct operation under all scenarios. This verification focused on mitigating potential threats and maintaining protocol integrity, ensuring the reliability of staking and reward mechanisms.
Modules include:
aave_math::wad_ray_math.move
Functionality | Details | Verification Status |
---|---|---|
function wade_to_ray |
||
Preconditions | Input a must be non-negative (a >= 0). | ✅ |
Preconditions | Input a must not cause overflow when multiplied by WAD_RAY_RATIO (a <= U256_MAX / WAD_RAY_RATIO). | ✅ |
Postconditions | Result is a * WAD_RAY_RATIO. | ✅ |
Postconditions | Result is non-negative (result >= 0). | ✅ |
Verification Consistency | Overflow check: Ensure multiplication does not exceed u256 max value. | ✅ |
Verification Consistency | Non-negative input: Validate inout non-negative. | ✅ |
Verification Consistency | Non-negative input: Guarantees the result is non-negative. | ✅ |
function wade_mul |
||
Preconditions | Inputs a and b must be non-negative (a >= 0, b >= 0). | ✅ |
Preconditions | The multiplication of a and b must not cause overflow (a <= (U256_MAX - HALF_WAD) / b). | ✅ |
Postconditions | Result is the product of a and b, rounded half up to the nearest Wad. | ✅ |
Postconditions | Result is non-negative (result >= 0). | ✅ |
Verification Conditions | Overflow Check: Ensures the product of a and b does not exceed u256 max value. | ✅ |
Verification Conditions | Non-Negative Inputs: Validates both inputs are non-negative. | ✅ |
Verification Conditions | Non-Negative Result: Guarantees the result is non-negative. | ✅ |
function wade_mul |
||
Preconditions | Denominator b must be non-zero (b > 0). | ✅ |
Preconditions | Inputs a and b must be non-negative (a >= 0, b >= 0). | ✅ |
Preconditions | The division must not cause overflow (a <= (U256_MAX - b / 2) / WAD). | ✅ |
Postconditions | Result is the division of a by b, rounded half up to the nearest Wad. | ✅ |
Postconditions | Result is non-negative (result >= 0). | ✅ |
Postconditions | Result is non-negative (result >= 0). | ✅ |
Verification conditions | Division by Zero Check: Ensures denominator is not zero. | ✅ |
Verification conditions | Non-Negative Inputs: Validates both inputs are non-negative. | ✅ |
Verification conditions | Overflow Check: Ensures the division does not exceed u256 max value. | ✅ |
Verification conditions | Non-Negative Result: Guarantees the result is non-negative. | ✅ |
function ray_div |
||
Preconditions | Denominator b must be non-zero (b > 0). | ✅ |
Preconditions | Inputs a and b must be non-negative (a >= 0, b >= 0). | ✅ |
Preconditions | The division must not cause overflow (a <= (U256_MAX - b / 2) / RAY). | ✅ |
Postconditions | Result is the division of a by b, rounded half up to the nearest Ray. | ✅ |
Postconditions | Result is non-negative (result >= 0). | ✅ |
Verification conditions | Division by Zero Check: Ensures denominator is not zero. | ✅ |
Verification conditions | Non-Negative Inputs: Validates both inputs are non-negative. | ✅ |
Verification conditions | Overflow Check: Ensures the division does not exceed u256 max value. | ✅ |
Verification conditions | Non-Negative Result: Guarantees the result is non-negative. | ✅ |
function ray_mul |
||
Preconditions | Inputs a and b must be non-negative (a >= 0, b >= 0) | ✅ |
Postconditions | Result is the product of a and b, rounded half up to the nearest Ray. | ✅ |
Postconditions | Result is non-negative (result >= 0). | ✅ |
Verification conditions | Non-Negative Result: Guarantees the result is non-negative. | ✅ |
Verification conditions | Non-Negative Inputs: Validates both inputs are non-negative. | ✅ |
function ray_to_wad |
||
Processes stake actions, ensuring the resources and conditions are correct before accepting any stake. Verification is enabled to maintain consistency with the protocol rules. | ✅ | |
Preconditions | nput a must be non-negative (a >= 0). | ✅ |
Postconditions | Result is the input a divided by WAD_RAY_RATIO, rounded half up to the nearest Wad. | ✅ |
Postconditions | Result is non-negative (result >= 0). | ✅ |
Verification conditions | Non-Negative Result: Guarantees the result is non-negative. | ✅ |
Verification conditions | Non-Negative Inputs: Validates both inputs are non-negative. | ✅ |
// Specification for wad_to_ray
spec wad_to_ray {
// Preconditions
requires a >= 0; // Ensure non-negative input
requires a <= U256_MAX / WAD_RAY_RATIO; // Ensure no overflow
// Postconditions
ensures result == a * WAD_RAY_RATIO;
ensures result >= 0; // Result should be non-negative
}
// Attach the specification directly to the function
spec wad_mul {
// Preconditions
requires a <= (U256_MAX - HALF_WAD) / b; // Ensure no overflow
requires a >= 0; // Ensure non-negative input
requires b >= 0; // Ensure non-negative input
// Postconditions
ensures result == if (a == 0 || b == 0) { 0 } else { (a * b + HALF_WAD) / WAD };
ensures result >= 0; // Result should be non-negative
}
// Specification for wad_div
spec wad_div {
// Preconditions
requires b > 0; // Denominator must be non-zero
requires a <= (U256_MAX - b / 2) / WAD; // Ensure no overflow
requires a >= 0; // Ensure non-negative input
requires b >= 0; // Ensure non-negative input
// Postconditions
ensures result == if (a == 0) { 0 } else { (a * WAD + b / 2) / b };
ensures result >= 0; // Result should be non-negative
}
// Specification for ray_div
spec ray_div {
// Preconditions
requires b > 0; // Denominator must be non-zero
requires a <= (U256_MAX - b / 2) / RAY; // Ensure no overflow
requires a >= 0; // Ensure non-negative input
requires b >= 0; // Ensure non-negative input
// Postconditions
ensures result == if (a == 0) { 0 } else { (a * RAY + b / 2) / b };
ensures result >= 0; // Result should be non-negative
}
// Specification for ray_mul
spec ray_mul {
// Preconditions
//this does not hold true here
// requires a <= (U256_MAX - HALF_RAY) / b; // Ensure no overflow
requires a >= 0; // Ensure non-negative input
requires b >= 0; // Ensure non-negative input
// Postconditions
ensures result == if (a == 0 || b == 0) { 0 } else { (a * b + HALF_RAY) / RAY };
ensures result >= 0; // Result should be non-negative
}
// Specification for ray_to_wad
spec ray_to_wad {
// Preconditions
requires a >= 0; // Ensure non-negative input
// Postconditions
ensures result == if (a % WAD_RAY_RATIO >= WAD_RAY_RATIO / 2) { a / WAD_RAY_RATIO + 1 } else { a / WAD_RAY_RATIO };
ensures result >= 0; // Result should be non-negative
}
}
aave_math::math_utilis.move
Functionality | Details | Verification Status |
---|---|---|
function calculate_compounded_interest |
||
Preconditions | The interest rate must be non-negative. | ✅ |
Preconditions | The timestamps must be in chronological order (last update timestamp cannot be in the future) | |
Preconditions | Input a must not cause overflow when multiplied by WAD_RAY_RATIO (a <= U256_MAX / WAD_RAY_RATIO). | ✅ |
Postconditions | The result is guaranteed to be at least equal to the base ray value(1.0 in ray format) ensures result >= wad_ray_math::ray(); | ✅ |
Postconditions | When no time has passed, the result equals exactly the base ray (no interest) ensures exp == 0 ==> result == wad_ray_math::ray(); | ✅ |
Postconditions | When no time has passed, the result equals exactly the base ray (no interest) ensures rate == 0 ==> result == wad_ray_math::ray() | ✅ |
Postconditions | When the interest rate is zero, the result equals exactly the base ray (no interest) | ✅ |
Postconditions | When time has passed and the rate is significant, interest will accrue (result > ray) | ✅ |
Verification Consistency | For special case of exactly 1 time unit passing: Either the result is exactly as calculated by the formula or for very small rates that would round to zero, a minimum interest of 1 is applied. | ✅ |
function pow |
||
Preconditions | The base must be non-negative. | ✅ |
Preconditions | The exponent must be non-negative. | ✅ |
Verification Conditions | base == 0 && exponent > 0 ==> result == 0 should be true mathematically (0 raised to any positive power should be 0) | ❌Acknowledged (Ideal fail) |
Verification conditions | base > 0 ==> result > 0 should also be true mathematically (any positive number raised to any power should be positive) | ❌Acknowledged (Ideal fail) |
function calculate_linear_interest |
||
Preconditions | requires rate >= 0. Ensures non-negative interest rate | ✅ |
Preconditions | equires last_update_timestamp <= timestamp::now_seconds() . Validates timestamp ordering. | ✅ |
Postconditions | ensures result >= wad_ray_math::ray() | |
Guarantees minimum interest result | ✅ | |
Postconditions | ensures last_update_timestamp == timestamp::now_seconds() ==> result == wad_ray_math::ray(). Validates no interest accrual when no time has pass | ✅ |
Postconditions | ensures rate == 0 ==> result == wad_ray_math::ray() | |
Validates zero rate case | ✅ | |
Verification conditions | ensures rate > 0 && last_update_timestamp < timestamp::now_seconds() ==> result … | |
Verifies correct interest calculation formula | ✅ | |
function percent_mul |
||
Preconditions | The value input must be non-negative. | ✅ |
Preconditions | The percentage input must be non-negative. | ✅ |
Postconditions | The result is guaranteed to be non-negative. | ✅ |
Postconditions | If either value or percentage is zero, the result must be zero. | ✅ |
function percent_div |
||
Preconditions | Prevents division by zero by requiring percentage > 0. | ✅ |
Preconditions | Checks for overflow: value > (U256_MAX - HALF_PERCENTAGE_FACTOR) / PERCENTAGE_FACTOR . |
❌ Fixed https://cantina.xyz/code/b6dada12-d99d-4710-b1d3-e8a69b9af616/findings?created_by=movejay&finding=82 |
Postconditions | Guarantees non-negative output: result >= 0 . |
✅ |
Postconditions | Validates zero input case: value == 0 ==> result == 0 . |
|
Postconditions | Verifies correct calculation with proper rounding: result == (value * PERCENTAGE_FACTOR + percentage / 2) / percentage . |
✅ |
Postconditions | Ensures upper bound of result: result <= (value * PERCENTAGE_FACTOR) / percentage + 1 . |
✅ |
// Specification for percent_div
spec percent_div {
// Preconditions
requires value >= 0; // Ensure non-negative value
requires percentage > 0; // Ensure non-zero percentage
// Postconditions
ensures result >= 0; // Result should be non-negative
// Special cases
ensures value == 0 ==> result == 0; // Zero value yields zero
// Rounding behavior
ensures value > 0 ==>
result == (value * PERCENTAGE_FACTOR + percentage / 2) / percentage;
// Bounds checking
ensures value > 0 ==>
result <= (value * PERCENTAGE_FACTOR) / percentage + 1;
ensures result >= (value * PERCENTAGE_FACTOR) / percentage;
}
// Specification for percent_mul
spec percent_mul {
// Preconditions
requires value >= 0; // Ensure non-negative value
requires percentage >= 0; // Ensure non-negative percentage
// Postconditions
ensures result >= 0; // Result should be non-negative
// Special cases
ensures value == 0 || percentage == 0 ==> result == 0; // Zero inputs yield zero
// Rounding behavior
ensures value > 0 && percentage > 0 ==>
result == (value * percentage + HALF_PERCENTAGE_FACTOR) / PERCENTAGE_FACTOR;
// Bounds checking
ensures value > 0 && percentage > 0 ==>
result <= (value * percentage) / PERCENTAGE_FACTOR + 1;
ensures result >= (value * percentage) / PERCENTAGE_FACTOR;
// Overflow protection
aborts_if value > 0 && percentage > 0 &&
value > (U256_MAX - HALF_PERCENTAGE_FACTOR) / percentage
with error_config::get_eoverflow();
}
// Specification for calculate_compounded_interest
spec calculate_compounded_interest {
// Preconditions
requires rate >= 0; // Ensure non-negative rate
requires last_update_timestamp <= current_timestamp; // Ensure valid timestamps
// Postconditions
ensures result >= wad_ray_math::ray(); // Result should be at least the base ray
// Additional postconditions for specific cases
ensures exp == 0 ==> result == wad_ray_math::ray(); // No time passed means no interest
ensures rate == 0 ==> result == wad_ray_math::ray(); // Zero rate means no interest
// Define exp for clarity in the specification
let exp = ((current_timestamp - last_update_timestamp) as u256);
// For the case where time has passed and rate is positive and significant
// Only guarantee interest accrual when the rate is significant enough
ensures exp > 0 && rate > SECONDS_PER_YEAR / exp ==> result > wad_ray_math::ray();
// For the case where exp == 1, we need to account for our minimum interest guarantee
ensures exp == 1 ==> (
result == wad_ray_math::ray() + (rate / SECONDS_PER_YEAR) ||
(rate > 0 && rate / SECONDS_PER_YEAR == 0 && result == wad_ray_math::ray() + 1)
);
}
// Enhanced specification for calculate_linear_interest
spec calculate_linear_interest {
// Preconditions
requires rate >= 0; // Ensure non-negative rate
requires last_update_timestamp <= timestamp::now_seconds(); // Ensure valid timestamp
// Postconditions
ensures result >= wad_ray_math::ray(); // Result should be at least the base ray
// Additional postconditions for specific cases
ensures last_update_timestamp == timestamp::now_seconds() ==> result == wad_ray_math::ray(); // No time passed means no interest
// Relationship between rate and result
ensures rate > 0 && last_update_timestamp < timestamp::now_seconds() ==>
result == wad_ray_math::ray() + (rate * ((timestamp::now_seconds() - last_update_timestamp) as u256)) / SECONDS_PER_YEAR;
// Remove the problematic forall expression for now
// We'll add a simpler property instead
ensures rate == 0 ==> result == wad_ray_math::ray(); // Zero rate means no interest
}
// Specification for calculate_compounded_interest_now
spec calculate_compounded_interest_now {
// Preconditions
requires rate >= 0; // Ensure non-negative rate
requires last_update_timestamp <= timestamp::now_seconds(); // Ensure valid timestamp
// Postconditions
ensures result >= wad_ray_math::ray(); // Result should be at least the base ray
}
// Enhanced specification for pow
spec pow {
// Preconditions
requires base >= 0; // Ensure non-negative base
requires exponent >= 0; // Ensure non-negative exponent
//@audit -- does not hold -- Overflow risk
///aborts_if value > 0 && percentage > 0 &&
///(value * PERCENTAGE_FACTOR + percentage / 2) > U256_MAX
///with error_config::get_eoverflow();
// Additional postconditions for specific cases
//@audit does not hold true here --Looks to be ideal
//ensures base == 0 && exponent > 0 ==> result == 0; // 0^n = 0 for n > 0
//@audit does not hold true here --Looks to be ideal
//ensures base > 0 ==> result > 0; // If base is positive, result is positive
}
// Additional specification for the module constants
spec module {
// Ensure constants are properly defined
invariant SECONDS_PER_YEAR == 365 * 24 * 3600;
invariant PERCENTAGE_FACTOR == 10000;
invariant HALF_PERCENTAGE_FACTOR == 5000;
}
// Specification for u256_max
spec u256_max {
// Postconditions
ensures result == U256_MAX;
}
// Specification for get_percentage_factor
spec get_percentage_factor {
ensures result == PERCENTAGE_FACTOR;
}
}