Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rename math operations #107

Merged
merged 7 commits into from
Dec 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions src/AdaptiveCurveIrm.sol
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ contract AdaptiveCurveIrm is IAdaptiveCurveIrm {
int256 errNormFactor = utilization > ConstantsLib.TARGET_UTILIZATION
? WAD - ConstantsLib.TARGET_UTILIZATION
: ConstantsLib.TARGET_UTILIZATION;
int256 err = (utilization - ConstantsLib.TARGET_UTILIZATION).wDivDown(errNormFactor);
int256 err = (utilization - ConstantsLib.TARGET_UTILIZATION).wDivTo0(errNormFactor);

int256 startRateAtTarget = rateAtTarget[id];

Expand All @@ -96,7 +96,7 @@ contract AdaptiveCurveIrm is IAdaptiveCurveIrm {
} else {
// Note that the speed is assumed constant between two interactions, but in theory it increases because of
// interests. So the rate will be slightly underestimated.
int256 speed = ConstantsLib.ADJUSTMENT_SPEED.wMulDown(err);
int256 speed = ConstantsLib.ADJUSTMENT_SPEED.wMulTo0(err);
// market.lastUpdate != 0 because it is not the first interaction with this market.
// Safe "unchecked" cast because block.timestamp - market.lastUpdate <= block.timestamp <= type(int256).max.
int256 elapsed = int256(block.timestamp - market.lastUpdate);
Expand Down Expand Up @@ -136,16 +136,16 @@ contract AdaptiveCurveIrm is IAdaptiveCurveIrm {
/// ((C-1)*err + 1) * rateAtTarget else.
function _curve(int256 _rateAtTarget, int256 err) private pure returns (int256) {
// Non negative because 1 - 1/C >= 0, C - 1 >= 0.
int256 coeff = err < 0 ? WAD - WAD.wDivDown(ConstantsLib.CURVE_STEEPNESS) : ConstantsLib.CURVE_STEEPNESS - WAD;
int256 coeff = err < 0 ? WAD - WAD.wDivTo0(ConstantsLib.CURVE_STEEPNESS) : ConstantsLib.CURVE_STEEPNESS - WAD;
// Non negative if _rateAtTarget >= 0 because if err < 0, coeff <= 1.
return (coeff.wMulDown(err) + WAD).wMulDown(int256(_rateAtTarget));
return (coeff.wMulTo0(err) + WAD).wMulTo0(int256(_rateAtTarget));
}

/// @dev Returns the new rate at target, for a given `startRateAtTarget` and a given `linearAdaptation`.
/// The formula is: max(min(startRateAtTarget * exp(linearAdaptation), maxRateAtTarget), minRateAtTarget).
function _newRateAtTarget(int256 startRateAtTarget, int256 linearAdaptation) private pure returns (int256) {
// Non negative because MIN_RATE_AT_TARGET > 0.
return startRateAtTarget.wMulDown(ExpLib.wExp(linearAdaptation)).bound(
return startRateAtTarget.wMulTo0(ExpLib.wExp(linearAdaptation)).bound(
ConstantsLib.MIN_RATE_AT_TARGET, ConstantsLib.MAX_RATE_AT_TARGET
);
}
Expand Down
6 changes: 4 additions & 2 deletions src/libraries/MathLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ int256 constant WAD_INT = int256(WAD);
/// @custom:contact security@morpho.org
/// @notice Library to manage fixed-point arithmetic on signed integers.
library MathLib {
function wMulDown(int256 a, int256 b) internal pure returns (int256) {
/// @dev Returns the multiplication of `a` by `b` (in WAD) rounded towards 0.
function wMulTo0(int256 a, int256 b) internal pure returns (int256) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My two cents/nitpick: using the full word "zero" would maybe be more clean.

  1. it's easy to get confused between char 0 and o and O. Having "zero" directly in the name is also much more explicit
  2. probably it's not comfortable to have a number inside a function when you need to type it

Note: The solidity style guide does not have a strong opinion about having numbers inside the function's name

return a * b / WAD_INT;
}

function wDivDown(int256 a, int256 b) internal pure returns (int256) {
/// @dev Returns the division of `a` by `b` (in WAD) rounded towards 0.
function wDivTo0(int256 a, int256 b) internal pure returns (int256) {
return a * WAD_INT / b;
}
}
33 changes: 16 additions & 17 deletions test/AdaptiveCurveIrmTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ contract AdaptiveCurveIrmTest is Test {
assertApproxEqRel(
irm.borrowRateView(marketParams, market),
uint256(
(ConstantsLib.INITIAL_RATE_AT_TARGET * 4).wMulDown(
(ConstantsLib.INITIAL_RATE_AT_TARGET * 4).wMulTo0(
(1.9836 ether - 1 ether) * WAD / (ConstantsLib.ADJUSTMENT_SPEED * 5 days)
)
),
Expand All @@ -82,7 +82,7 @@ contract AdaptiveCurveIrmTest is Test {
// The average value of exp((50/365)*x) between 0 and 5 is approx. 1.4361.
assertApproxEqRel(
irm.borrowRateView(marketParams, market),
uint256((ConstantsLib.INITIAL_RATE_AT_TARGET * 4).wMulDown(1.4361 ether)),
uint256((ConstantsLib.INITIAL_RATE_AT_TARGET * 4).wMulTo0(1.4361 ether)),
0.1 ether
);
// Expected rate: 22.976%.
Expand All @@ -104,7 +104,7 @@ contract AdaptiveCurveIrmTest is Test {
assertApproxEqRel(
irm.borrowRateView(marketParams, market),
uint256(
(ConstantsLib.INITIAL_RATE_AT_TARGET / 4).wMulDown(
(ConstantsLib.INITIAL_RATE_AT_TARGET / 4).wMulTo0(
(0.5041 ether - 1 ether) * WAD / (-ConstantsLib.ADJUSTMENT_SPEED * 5 days)
)
),
Expand All @@ -113,7 +113,7 @@ contract AdaptiveCurveIrmTest is Test {
// The average value of exp((-50/365*x)) between 0 and 5 is approx. 0.7240.
assertApproxEqRel(
irm.borrowRateView(marketParams, market),
uint256((ConstantsLib.INITIAL_RATE_AT_TARGET / 4).wMulDown(0.724 ether)),
uint256((ConstantsLib.INITIAL_RATE_AT_TARGET / 4).wMulTo0(0.724 ether)),
0.1 ether
);
// Expected rate: 0.7240%.
Expand Down Expand Up @@ -344,11 +344,11 @@ contract AdaptiveCurveIrmTest is Test {

assertGe(
irm.borrowRateView(marketParams, market),
uint256(ConstantsLib.MIN_RATE_AT_TARGET.wDivDown(ConstantsLib.CURVE_STEEPNESS))
uint256(ConstantsLib.MIN_RATE_AT_TARGET.wDivTo0(ConstantsLib.CURVE_STEEPNESS))
);
assertGe(
irm.borrowRate(marketParams, market),
uint256(ConstantsLib.MIN_RATE_AT_TARGET.wDivDown(ConstantsLib.CURVE_STEEPNESS))
uint256(ConstantsLib.MIN_RATE_AT_TARGET.wDivTo0(ConstantsLib.CURVE_STEEPNESS))
);
}

Expand All @@ -359,11 +359,11 @@ contract AdaptiveCurveIrmTest is Test {

assertLe(
irm.borrowRateView(marketParams, market),
uint256(ConstantsLib.MAX_RATE_AT_TARGET.wMulDown(ConstantsLib.CURVE_STEEPNESS))
uint256(ConstantsLib.MAX_RATE_AT_TARGET.wMulTo0(ConstantsLib.CURVE_STEEPNESS))
);
assertLe(
irm.borrowRate(marketParams, market),
uint256(ConstantsLib.MAX_RATE_AT_TARGET.wMulDown(ConstantsLib.CURVE_STEEPNESS))
uint256(ConstantsLib.MAX_RATE_AT_TARGET.wMulTo0(ConstantsLib.CURVE_STEEPNESS))
);
}

Expand All @@ -382,12 +382,12 @@ contract AdaptiveCurveIrmTest is Test {

function _expectedRateAtTarget(Id id, Market memory market) internal view returns (int256) {
int256 rateAtTarget = irm.rateAtTarget(id);
int256 speed = ConstantsLib.ADJUSTMENT_SPEED.wMulDown(_err(market));
int256 speed = ConstantsLib.ADJUSTMENT_SPEED.wMulTo0(_err(market));
uint256 elapsed = (rateAtTarget > 0) ? block.timestamp - market.lastUpdate : 0;
int256 linearAdaptation = speed * int256(elapsed);
int256 adaptationMultiplier = ExpLib.wExp(linearAdaptation);
return (rateAtTarget > 0)
? rateAtTarget.wMulDown(adaptationMultiplier).bound(
? rateAtTarget.wMulTo0(adaptationMultiplier).bound(
ConstantsLib.MIN_RATE_AT_TARGET, ConstantsLib.MAX_RATE_AT_TARGET
)
: ConstantsLib.INITIAL_RATE_AT_TARGET;
Expand All @@ -396,7 +396,7 @@ contract AdaptiveCurveIrmTest is Test {
function _expectedAvgRate(Id id, Market memory market) internal view returns (uint256) {
int256 rateAtTarget = irm.rateAtTarget(id);
int256 err = _err(market);
int256 speed = ConstantsLib.ADJUSTMENT_SPEED.wMulDown(err);
int256 speed = ConstantsLib.ADJUSTMENT_SPEED.wMulTo0(err);
uint256 elapsed = (rateAtTarget > 0) ? block.timestamp - market.lastUpdate : 0;
int256 linearAdaptation = speed * int256(elapsed);
int256 endRateAtTarget = int256(_expectedRateAtTarget(id, market));
Expand All @@ -408,18 +408,17 @@ contract AdaptiveCurveIrmTest is Test {
} else {
// Safe "unchecked" cast to uint256 because linearAdaptation < 0 <=> newBorrowRate <= borrowRateAfterJump.
avgBorrowRate =
uint256((int256(newBorrowRate) - int256(_curve(rateAtTarget, err))).wDivDown(linearAdaptation));
uint256((int256(newBorrowRate) - int256(_curve(rateAtTarget, err))).wDivTo0(linearAdaptation));
}
return avgBorrowRate;
}

function _curve(int256 rateAtTarget, int256 err) internal pure returns (uint256) {
// Safe "unchecked" cast because err >= -1 (in WAD).
if (err < 0) {
return
uint256(((WAD - WAD.wDivDown(ConstantsLib.CURVE_STEEPNESS)).wMulDown(err) + WAD).wMulDown(rateAtTarget));
return uint256(((WAD - WAD.wDivTo0(ConstantsLib.CURVE_STEEPNESS)).wMulTo0(err) + WAD).wMulTo0(rateAtTarget));
} else {
return uint256(((ConstantsLib.CURVE_STEEPNESS - WAD).wMulDown(err) + WAD).wMulDown(rateAtTarget));
return uint256(((ConstantsLib.CURVE_STEEPNESS - WAD).wMulTo0(err) + WAD).wMulTo0(rateAtTarget));
}
}

Expand All @@ -429,9 +428,9 @@ contract AdaptiveCurveIrmTest is Test {
int256 utilization = int256(market.totalBorrowAssets.wDivDown(market.totalSupplyAssets));

if (utilization > ConstantsLib.TARGET_UTILIZATION) {
err = (utilization - ConstantsLib.TARGET_UTILIZATION).wDivDown(WAD - ConstantsLib.TARGET_UTILIZATION);
err = (utilization - ConstantsLib.TARGET_UTILIZATION).wDivTo0(WAD - ConstantsLib.TARGET_UTILIZATION);
} else {
err = (utilization - ConstantsLib.TARGET_UTILIZATION).wDivDown(ConstantsLib.TARGET_UTILIZATION);
err = (utilization - ConstantsLib.TARGET_UTILIZATION).wDivTo0(ConstantsLib.TARGET_UTILIZATION);
}
}
}
4 changes: 2 additions & 2 deletions test/ExpLibTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ 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 testWExpWMulMaxRate() public pure {
ExpLib.wExp(ExpLib.WEXP_UPPER_BOUND).wMulTo0(ConstantsLib.MAX_RATE_AT_TARGET);
}

function _wExpUnbounded(int256 x) internal pure returns (int256) {
Expand Down