Skip to content

Commit 7adf262

Browse files
committed
add interest rate and index math + expand ZTokenHarness + introduce accrue interest tests
- Added newBorrowIndex, borrowerDebt, borrowInterestRatePerSecond, and supplyInterestRatePerSecond to InterestMath - Refactored borrow rate logic to use kink model (utilization threshold) without extra parameters - Added InterestMath usage inside ZTokenHarness and new view helpers for borrow/supply rate - Expanded ZTokenTest with AccrueInterest scenarios (no borrow, first borrow, multiple borrows) - Updated ZToken.tree to include new AccrueInterest branches
1 parent 28bab0d commit 7adf262

4 files changed

Lines changed: 101 additions & 12 deletions

File tree

src/libraries/InterestMath.sol

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,31 +27,54 @@ library InterestMath {
2727
return numerator.divWad(denominator);
2828
}
2929

30-
function newBorrowIndex(uint256 currentBorrowIndex, uint256 borrowRatePerSecond, uint256 deltaTime)
30+
function newBorrowIndex(uint256 _currentBorrowIndex, uint256 _borrowRatePerSecond, uint256 _deltaTime)
3131
internal
3232
pure
3333
returns (uint256)
3434
{
3535
// newBorrowIndex = currentBorrowIndex * (1 + borrowRate * deltaTime)
36-
uint256 simpleInterestFactor = borrowRatePerSecond.mulDiv(deltaTime);
37-
return currentBorrowIndex + currentBorrowIndex.mulDiv(simpleInterestFactor);
36+
// newBorrowIndex = currentBorrowIndex + (currentBorrowIndex * borrowRate * deltaTime)
37+
38+
return _currentBorrowIndex + _currentBorrowIndex.mulWad(_borrowRatePerSecond * _deltaTime);
39+
}
40+
41+
function borrowerDebt(uint256 _principal, uint256 _currentBorrrowIndex, uint256 _snapshotBorrowIndex)
42+
internal
43+
pure
44+
returns (uint256)
45+
{
46+
// _principal * _currentBorrrowIndex / _snapshotBorrowIndex
47+
48+
return (_principal * _currentBorrrowIndex) / _snapshotBorrowIndex;
3849
}
3950

4051
function borrowInterestRatePerSecond(
4152
uint256 _utilization,
4253
uint256 _utilizationThreshold,
4354
uint256 _baseRatePerYear,
4455
uint256 _slopeBeforeKink,
45-
uint256 _slopeAfterKink,
46-
uint256 _kink
56+
uint256 _slopeAfterKink
4757
) internal pure returns (uint256) {
4858
uint256 baseRatePerSec = _baseRatePerYear / 365 days;
4959
if (_utilization <= _utilizationThreshold) {
5060
// rb = base rate + s1 * Utilization, u <= uopt
51-
return baseRatePerSec + (_slopeBeforeKink.mulDiv(_utilization, WAD));
61+
return baseRatePerSec + (_slopeBeforeKink.mulWad(_utilization));
5262
}
5363

54-
// rb = base rate + s1 * k + s2(U - K), u > uopt
55-
return baseRatePerSec + _slopeBeforeKink.mulDiv(_kink, WAD) + _slopeAfterKink.mulDiv(_utilization - _kink, WAD);
64+
// rb = base rate + s1 * k + s2(U - K), u > uopt, where k = utilization threshold(_utilizationThreshold)
65+
return baseRatePerSec + _slopeBeforeKink.mulDiv(_utilizationThreshold, WAD)
66+
+ _slopeAfterKink.mulDiv(_utilization - _utilizationThreshold, WAD);
67+
}
68+
69+
function supplyInterestRatePerSecond(
70+
uint256 _utilization,
71+
uint256 _borrowInterestRatePerSecond,
72+
uint256 _reserveFactor
73+
) internal pure returns (uint256) {
74+
// rs = u * rb * (1-rf)
75+
76+
if (_utilization == 0) return 0;
77+
uint256 rate = _utilization.mulWad(_borrowInterestRatePerSecond);
78+
return rate.mulWad(WAD - _reserveFactor);
5679
}
5780
}

test/mocks/ZTokenHarness.sol

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
// SPDX-License-Identifier: MIT
22
pragma solidity 0.8.30;
33

4-
import { IZToken, ZToken } from "src/ZToken.sol";
4+
import { IZToken, ZToken, InterestMath } from "src/ZToken.sol";
55

66
contract ZTokenHarness is ZToken {
7+
using InterestMath for uint256;
8+
79
uint256 public cash;
810

911
uint256 private constant _TOTAL_SUPPLY_SLOT = 0x05345cdf77eb68f44c;
@@ -32,6 +34,28 @@ contract ZTokenHarness is ZToken {
3234
}
3335
}
3436

37+
// function borrowInterestRatePerSecond(
38+
// uint256 _utilization,
39+
// uint256 _utilizationThreshold,
40+
// uint256 _baseRatePerYear,
41+
// uint256 _slopeBeforeKink,
42+
// uint256 _slopeAfterKink,
43+
// uint256 _kink
44+
45+
function getBorrowInterestRatePerSecond(uint256 _utilization) external view returns (uint256) {
46+
return _utilization.borrowInterestRatePerSecond(
47+
UTILIZATION_THRESHOLD, BASE_BORROW_RATE, SLOPE_BEFORE_KINK, SLOPE_AFTER_KINK
48+
);
49+
}
50+
51+
function getSupplyInterestRatePerSecond(uint256 _utilization, uint256 _borrowInterestRatePerSecond)
52+
external
53+
view
54+
returns (uint256)
55+
{
56+
return _utilization.supplyInterestRatePerSecond(_borrowInterestRatePerSecond, RESERVE_FACTOR);
57+
}
58+
3559
function getTotalCash() external view returns (uint256) {
3660
return _getCash();
3761
}

test/unit/ZToken.t.sol

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,39 @@ contract ZTokenTest is Test {
111111
assertEq(zToken.getBorrowIndex(), 1e18);
112112
}
113113

114-
function test_GetBorrowIndex_WhenThereIsBorrowActivity() external {
115-
// it should rise with interest
116-
vm.skip(true);
114+
function test_AccrueInterest_WhenNoBorrowActivity() external view {
115+
uint256 utilization = zToken.getUtilization();
116+
uint256 borrowInterestRate = zToken.getBorrowInterestRatePerSecond(utilization);
117+
uint256 supplyInterestRate = zToken.getSupplyInterestRatePerSecond(utilization, borrowInterestRate);
118+
119+
assertEq(borrowInterestRate, zToken.BASE_BORROW_RATE() / 365 days);
120+
assertEq(supplyInterestRate, 0);
121+
}
122+
123+
modifier whenThereIsBorrowActivity(uint256 _borrow) {
124+
zToken.setTotalBorrow(_borrow);
125+
_;
126+
}
127+
128+
function test_AccrueInterest_WhenThereIsBorrowActivity(uint256 _borrow)
129+
external
130+
whenThereIsBorrowActivity(_borrow)
131+
{
132+
// it borrow interest rate should be greater than zero
133+
// it supply interest rate should be greater than zero
134+
}
135+
136+
function test_AccrueInterest_WhenItTheFirstBorrowActivity(uint256 _borrow)
137+
external
138+
whenThereIsBorrowActivity(_borrow)
139+
{
140+
// it borrow index should not change
141+
}
142+
143+
function test_AccrueInterest_WhenThereIsMultipleBorrowActivity(uint256 _borrow)
144+
external
145+
whenThereIsBorrowActivity(_borrow)
146+
{
147+
// it borrow index should be greater than zero
117148
}
118149
}

test/unit/ZToken.tree

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,14 @@ ZTokenTest::getBorrowIndex
3636
└── when no borrowing activity
3737
└── it should match the initial borrow index
3838

39+
ZTokenTest::AccrueInterest
40+
├── when no borrow activity
41+
│ ├── it borrow interest rate should be base rate
42+
│ └── it supply interest rate should be zero
43+
└── when there is borrow activity
44+
├── it borrow interest rate should be greater than zero
45+
├── it supply interest rate should be greater than zero
46+
├── when it the first borrow activity
47+
│ └── it borrow index should not change
48+
└── when there is multiple borrow activity
49+
└── it borrow index should be greater than zero

0 commit comments

Comments
 (0)