-
Notifications
You must be signed in to change notification settings - Fork 15
/
FeeMath.sol
174 lines (155 loc) · 6.49 KB
/
FeeMath.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.20;
import './MathUtils.sol';
import './SafeCastLib.sol';
import './MarketState.sol';
using SafeCastLib for uint256;
using MathUtils for uint256;
library FeeMath {
/**
* @dev Function to calculate the interest accumulated using a linear interest rate formula
*
* @param rateBip The interest rate, in bips
* @param timeDelta The time elapsed since the last interest accrual
* @return result The interest rate linearly accumulated during the timeDelta, in ray
*/
function calculateLinearInterestFromBips(
uint256 rateBip,
uint256 timeDelta
) internal pure returns (uint256 result) {
uint256 rate = rateBip.bipToRay();
uint256 accumulatedInterestRay = rate * timeDelta;
unchecked {
return accumulatedInterestRay / SECONDS_IN_365_DAYS;
}
}
function calculateBaseInterest(
MarketState memory state,
uint256 timestamp
) internal pure returns (uint256 baseInterestRay) {
baseInterestRay = MathUtils.calculateLinearInterestFromBips(
state.annualInterestBips,
timestamp - state.lastInterestAccruedTimestamp
);
}
function applyProtocolFee(
MarketState memory state,
uint256 baseInterestRay,
uint256 protocolFeeBips
) internal pure returns (uint256 protocolFee) {
// Protocol fee is charged in addition to the interest paid to lenders.
uint256 protocolFeeRay = protocolFeeBips.bipMul(baseInterestRay);
protocolFee = uint256(state.scaledTotalSupply).rayMul(
uint256(state.scaleFactor).rayMul(protocolFeeRay)
);
state.accruedProtocolFees = (state.accruedProtocolFees + protocolFee).toUint128();
}
function updateDelinquency(
MarketState memory state,
uint256 timestamp,
uint256 delinquencyFeeBips,
uint256 delinquencyGracePeriod
) internal pure returns (uint256 delinquencyFeeRay) {
// Calculate the number of seconds the borrower spent in penalized
// delinquency since the last update.
uint256 timeWithPenalty = updateTimeDelinquentAndGetPenaltyTime(
state,
delinquencyGracePeriod,
timestamp - state.lastInterestAccruedTimestamp
);
if (timeWithPenalty > 0) {
// Calculate penalty fees on the interest accrued.
delinquencyFeeRay = calculateLinearInterestFromBips(delinquencyFeeBips, timeWithPenalty);
}
}
/**
* @notice Calculate the number of seconds that the market has been in
* penalized delinquency since the last update, and update
* `timeDelinquent` in state.
*
* @dev When `isDelinquent`, equivalent to:
* max(0, timeDelta - max(0, delinquencyGracePeriod - previousTimeDelinquent))
* When `!isDelinquent`, equivalent to:
* min(timeDelta, max(0, previousTimeDelinquent - delinquencyGracePeriod))
*
* @param state Encoded state parameters
* @param delinquencyGracePeriod Seconds in delinquency before penalties apply
* @param timeDelta Seconds since the last update
* @param `timeWithPenalty` Number of seconds since the last update where
* the market was in delinquency outside of the grace period.
*/
function updateTimeDelinquentAndGetPenaltyTime(
MarketState memory state,
uint256 delinquencyGracePeriod,
uint256 timeDelta
) internal pure returns (uint256 /* timeWithPenalty */) {
// Seconds in delinquency at last update
uint256 previousTimeDelinquent = state.timeDelinquent;
if (state.isDelinquent) {
// Since the borrower is still delinquent, increase the total
// time in delinquency by the time elapsed.
state.timeDelinquent = (previousTimeDelinquent + timeDelta).toUint32();
// Calculate the number of seconds the borrower had remaining
// in the grace period.
uint256 secondsRemainingWithoutPenalty = delinquencyGracePeriod.satSub(
previousTimeDelinquent
);
// Penalties apply for the number of seconds the market spent in
// delinquency outside of the grace period since the last update.
return timeDelta.satSub(secondsRemainingWithoutPenalty);
}
// Reduce the total time in delinquency by the time elapsed, stopping
// when it reaches zero.
state.timeDelinquent = previousTimeDelinquent.satSub(timeDelta).toUint32();
// Calculate the number of seconds the old timeDelinquent had remaining
// outside the grace period, or zero if it was already in the grace period.
uint256 secondsRemainingWithPenalty = previousTimeDelinquent.satSub(delinquencyGracePeriod);
// Only apply penalties for the remaining time outside of the grace period.
return MathUtils.min(secondsRemainingWithPenalty, timeDelta);
}
/**
* @dev Calculates interest and delinquency/protocol fees accrued since last state update
* and applies it to cached state, returning the rates for base interest and delinquency
* fees and the normalized amount of protocol fees accrued.
*
* Takes `timestamp` as input to allow separate calculation of interest
* before and after withdrawal batch expiry.
*
* @param state Market scale parameters
* @param protocolFeeBips Protocol fee rate (in bips)
* @param delinquencyFeeBips Delinquency fee rate (in bips)
* @param delinquencyGracePeriod Grace period (in seconds) before delinquency fees apply
* @param timestamp Time to calculate interest and fees accrued until
* @return baseInterestRay Interest accrued to lenders (ray)
* @return delinquencyFeeRay Penalty fee incurred by borrower for delinquency (ray).
* @return protocolFee Protocol fee charged on interest (normalized token amount).
*/
function updateScaleFactorAndFees(
MarketState memory state,
uint256 protocolFeeBips,
uint256 delinquencyFeeBips,
uint256 delinquencyGracePeriod,
uint256 timestamp
)
internal
pure
returns (uint256 baseInterestRay, uint256 delinquencyFeeRay, uint256 protocolFee)
{
baseInterestRay = state.calculateBaseInterest(timestamp);
if (protocolFeeBips > 0) {
protocolFee = state.applyProtocolFee(baseInterestRay, protocolFeeBips);
}
if (delinquencyFeeBips > 0) {
delinquencyFeeRay = state.updateDelinquency(
timestamp,
delinquencyFeeBips,
delinquencyGracePeriod
);
}
// Calculate new scaleFactor
uint256 prevScaleFactor = state.scaleFactor;
uint256 scaleFactorDelta = prevScaleFactor.rayMul(baseInterestRay + delinquencyFeeRay);
state.scaleFactor = (prevScaleFactor + scaleFactorDelta).toUint112();
state.lastInterestAccruedTimestamp = uint32(timestamp);
}
}