Skip to content

feat: compound interest #222

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

Merged
merged 10 commits into from
Aug 9, 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
2 changes: 1 addition & 1 deletion src/Blue.sol
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ contract Blue is IBlue {

if (marketTotalBorrow != 0) {
uint256 borrowRate = IIrm(market.irm).borrowRate(market);
uint256 accruedInterests = marketTotalBorrow.mulWadDown(borrowRate * elapsed);
uint256 accruedInterests = marketTotalBorrow.mulWadDown(borrowRate.wTaylorCompounded(elapsed));
totalBorrow[id] = marketTotalBorrow + accruedInterests;
totalSupply[id] += accruedInterests;

Expand Down
21 changes: 21 additions & 0 deletions src/libraries/FixedPointMathLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,16 @@ library FixedPointMathLib {
return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up.
}

/// @dev The sum of the last three terms in a four term taylor series expansion
/// to approximate a compound interest rate: (1 + x)^n - 1.
function wTaylorCompounded(uint256 x, uint256 n) internal pure returns (uint256) {
uint256 firstTerm = x * n;
uint256 secondTerm = mulWadDown(firstTerm, x * zeroFloorSub(n, 1)) / 2;
uint256 thirdTerm = mulWadDown(secondTerm, x * zeroFloorSub(n, 2)) / 3;

return firstTerm + secondTerm + thirdTerm;
}

/*//////////////////////////////////////////////////////////////
LOW LEVEL FIXED POINT OPERATIONS
//////////////////////////////////////////////////////////////*/
Expand All @@ -55,4 +65,15 @@ library FixedPointMathLib {
z := add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator))
}
}

/*//////////////////////////////////////////////////////////////
INTEGER OPERATIONS
//////////////////////////////////////////////////////////////*/

/// @dev Returns max(x - y, 0).
function zeroFloorSub(uint256 x, uint256 y) internal pure returns (uint256 z) {
assembly {
z := mul(gt(x, y), sub(x, y))
}
}
}
31 changes: 31 additions & 0 deletions test/forge/Math.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

import "forge-std/Test.sol";

import "src/libraries/FixedPointMathLib.sol";

contract MathTest is Test {
using FixedPointMathLib for uint256;

function testWTaylorCompounded(uint256 rate, uint256 timeElapsed) public {
// Assume rate is less than a ~500% APY. (~180% APR)
vm.assume(rate < (FixedPointMathLib.WAD / 20_000_000) && timeElapsed < 365 days);
uint256 result = rate.wTaylorCompounded(timeElapsed) + FixedPointMathLib.WAD;
uint256 toCompare = wPow(FixedPointMathLib.WAD + rate, timeElapsed);
assertLe(result, toCompare, "rate should be less than the compounded rate");
assertGe(
result, FixedPointMathLib.WAD + timeElapsed * rate, "rate should be greater than the simple interest rate"
);
assertLe((toCompare - result) * 100_00 / toCompare, 8_00, "The error should be less than or equal to 8%");
}

// Exponentiation by squaring with rounding up.
function wPow(uint256 x, uint256 n) private pure returns (uint256 z) {
z = FixedPointMathLib.WAD;
for (; n != 0; n /= 2) {
z = n % 2 != 0 ? z.mulWadUp(x) : z;
x = x.mulWadUp(x);
}
}
}