diff --git a/src/libraries/adaptative-curve/ExpLib.sol b/src/libraries/adaptative-curve/ExpLib.sol index 70381ed7..e8c6f8ff 100644 --- a/src/libraries/adaptative-curve/ExpLib.sol +++ b/src/libraries/adaptative-curve/ExpLib.sol @@ -19,7 +19,7 @@ library ExpLib { int256 internal constant WEXP_UPPER_BOUND = 93.859467695000404319 ether; /// @dev The value of wExp(`WEXP_UPPER_BOUND`). - int256 internal constant WEXP_UPPER_VALUE = 57716089161558943862588783571184261698504.523000224082296832 ether; + int256 internal constant WEXP_UPPER_VALUE = 57716089161558943949701069502944508345128.422502756744429568 ether; /// @dev Returns an approximation of exp. function wExp(int256 x) internal pure returns (int256) { diff --git a/test/AdaptativeCurveIrmTest.sol b/test/AdaptativeCurveIrmTest.sol index 808e8073..336ef784 100644 --- a/test/AdaptativeCurveIrmTest.sol +++ b/test/AdaptativeCurveIrmTest.sol @@ -215,10 +215,6 @@ contract AdaptativeCurveIrmTest is Test { assertApproxEqRel(irm.rateAtTarget(marketParams.id()), expectedRateAtTarget, 0.001 ether, "rateAtTarget"); } - function testWExpWMulDownMaxRate() public pure { - ExpLib.wExp(ExpLib.WEXP_UPPER_BOUND).wMulDown(ConstantsLib.MAX_RATE_AT_TARGET); - } - /* HANDLERS */ function handleBorrowRate(uint256 totalSupplyAssets, uint256 totalBorrowAssets, uint256 elapsed) external { diff --git a/test/ExpLibTest.sol b/test/ExpLibTest.sol index 4497c409..77bc9e3b 100644 --- a/test/ExpLibTest.sol +++ b/test/ExpLibTest.sol @@ -1,12 +1,18 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0; +import {MathLib, WAD_INT} from "../src/libraries/MathLib.sol"; +import {ConstantsLib} from "../src/libraries/adaptative-curve/ConstantsLib.sol"; import {ExpLib} from "../src/libraries/adaptative-curve/ExpLib.sol"; import {wadExp} from "../lib/solmate/src/utils/SignedWadMath.sol"; +import {MathLib as MorphoMathLib} from "../lib/morpho-blue/src/libraries/MathLib.sol"; import "../lib/forge-std/src/Test.sol"; contract ExpLibTest is Test { + using MathLib for int256; + using MorphoMathLib for uint256; + /// @dev ln(1e-9) truncated at 2 decimal places. int256 internal constant LN_GWEI_INT = -20.72 ether; @@ -41,6 +47,7 @@ contract ExpLibTest is Test { function testWExpContinuousUpperBound() public { assertApproxEqRel(ExpLib.wExp(ExpLib.WEXP_UPPER_BOUND - 1), ExpLib.WEXP_UPPER_VALUE, 1e-10 ether); + assertEq(_wExpUnbounded(ExpLib.WEXP_UPPER_BOUND), ExpLib.WEXP_UPPER_VALUE); } function testWExpPositive(int256 x) public { @@ -54,4 +61,28 @@ contract ExpLibTest is Test { assertLe(ExpLib.wExp(x), 1e18); } + + function testWExpWMulDownMaxRate() public pure { + ExpLib.wExp(ExpLib.WEXP_UPPER_BOUND).wMulDown(ConstantsLib.MAX_RATE_AT_TARGET); + } + + function _wExpUnbounded(int256 x) internal pure returns (int256) { + unchecked { + // Decompose x as x = q * ln(2) + r with q an integer and -ln(2)/2 <= r <= ln(2)/2. + // q = x / ln(2) rounded half toward zero. + int256 roundingAdjustment = (x < 0) ? -(ExpLib.LN_2_INT / 2) : (ExpLib.LN_2_INT / 2); + // Safe unchecked because x is bounded. + int256 q = (x + roundingAdjustment) / ExpLib.LN_2_INT; + // Safe unchecked because |q * ln(2) - x| <= ln(2)/2. + int256 r = x - q * ExpLib.LN_2_INT; + + // Compute e^r with a 2nd-order Taylor polynomial. + // Safe unchecked because |r| < 1e18. + int256 expR = WAD_INT + r + (r * r) / WAD_INT / 2; + + // Return e^x = 2^q * e^r. + if (q >= 0) return expR << uint256(q); + else return expR >> uint256(-q); + } + } }