From 2ddb3556f0ee41955eb9efc2e4c7d2f87e5323df Mon Sep 17 00:00:00 2001 From: YoussefAboutika Date: Fri, 12 Sep 2025 23:45:06 +0200 Subject: [PATCH] Add approxEq function for approximate equality comparison --- foundry.lock | 5 +++++ src/Asserts.sol | 4 ++++ src/CryticAsserts.sol | 22 ++++++++++++++++++++++ src/FoundryAsserts.sol | 8 ++++++++ src/HalmosAsserts.sol | 8 ++++++++ 5 files changed, 47 insertions(+) create mode 100644 foundry.lock diff --git a/foundry.lock b/foundry.lock new file mode 100644 index 0000000..65b6e6a --- /dev/null +++ b/foundry.lock @@ -0,0 +1,5 @@ +{ + "lib/forge-std": { + "rev": "f73c73d2018eb6a111f35e4dae7b4f27401e9421" + } +} \ No newline at end of file diff --git a/src/Asserts.sol b/src/Asserts.sol index 7477660..709614f 100644 --- a/src/Asserts.sol +++ b/src/Asserts.sol @@ -12,6 +12,10 @@ abstract contract Asserts { function eq(uint256 a, uint256 b, string memory reason) internal virtual; + function approxEq(uint256 a, uint256 b, uint256 maxPercentDelta, string memory reason) internal virtual; + + function approxEq(int256 a, int256 b, uint256 maxPercentDelta, string memory reason) internal virtual; + function t(bool b, string memory reason) internal virtual; function between(uint256 value, uint256 low, uint256 high) internal virtual returns (uint256); diff --git a/src/CryticAsserts.sol b/src/CryticAsserts.sol index 76049e6..43bd450 100644 --- a/src/CryticAsserts.sol +++ b/src/CryticAsserts.sol @@ -41,6 +41,28 @@ contract CryticAsserts is Asserts { } } + function approxEq(uint256 a, uint256 b, uint256 maxPercentDelta, string memory message) internal virtual override { + if (b == 0) return eq(a, b, message); // If the right is 0, left must be too. + + uint256 diff = a > b ? a - b : b - a; + uint256 percentDelta = diff * 1e18 / b; + + gt(maxPercentDelta, percentDelta, message); + } + + function approxEq(int256 a, int256 b, uint256 maxPercentDelta, string memory message) internal virtual override { + if (b == 0) return t(a == b, message); // If the right is 0, left must be too. + + int256 d = a - b; + uint256 diff = d >= 0 ? uint256(d) : uint256(-d); + uint256 base = b >= 0 ? uint256(b) : uint256(-b); + + // percentDelta = |a - b| / |b| in 1e18 precision + uint256 percentDelta = (diff * 1e18) / base; + + gt(maxPercentDelta, percentDelta, message); + } + function t(bool b, string memory reason) internal virtual override { if (!b) { emit Log(reason); diff --git a/src/FoundryAsserts.sol b/src/FoundryAsserts.sol index 2cdf57f..20f3dd0 100644 --- a/src/FoundryAsserts.sol +++ b/src/FoundryAsserts.sol @@ -24,6 +24,14 @@ contract FoundryAsserts is Test, Asserts { function eq(uint256 a, uint256 b, string memory reason) internal virtual override { assertEq(a, b, reason); } + + function approxEq(uint256 a, uint256 b, uint256 maxPercentDelta, string memory message) internal virtual override { + assertApproxEqRel(a, b, maxPercentDelta, message); + } + + function approxEq(int256 a, int256 b, uint256 maxPercentDelta, string memory message) internal virtual override { + assertApproxEqRel(a, b, maxPercentDelta, message); + } function t(bool b, string memory reason) internal virtual override { assertTrue(b, reason); diff --git a/src/HalmosAsserts.sol b/src/HalmosAsserts.sol index 8ec3644..43e7552 100644 --- a/src/HalmosAsserts.sol +++ b/src/HalmosAsserts.sol @@ -25,6 +25,14 @@ contract HalmosAsserts is Test, Asserts { assertEq(a, b, reason); } + function approxEq(uint256 a, uint256 b, uint256 maxPercentDelta, string memory message) internal virtual override { + assertApproxEqRel(a, b, maxPercentDelta, message); + } + + function approxEq(int256 a, int256 b, uint256 maxPercentDelta, string memory message) internal virtual override { + assertApproxEqRel(a, b, maxPercentDelta, message); + } + function t(bool b, string memory reason) internal virtual override { assertTrue(b, reason); }