-
Notifications
You must be signed in to change notification settings - Fork 45
/
DefaultReserveInterestRateStrategyV2.sol
241 lines (205 loc) · 8.64 KB
/
DefaultReserveInterestRateStrategyV2.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
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol';
import {WadRayMath} from '../protocol/libraries/math/WadRayMath.sol';
import {PercentageMath} from '../protocol/libraries/math/PercentageMath.sol';
import {DataTypes} from '../protocol/libraries/types/DataTypes.sol';
import {Errors} from '../protocol/libraries/helpers/Errors.sol';
import {IDefaultInterestRateStrategyV2} from '../interfaces/IDefaultInterestRateStrategyV2.sol';
import {IReserveInterestRateStrategy} from '../interfaces/IReserveInterestRateStrategy.sol';
import {IPoolAddressesProvider} from '../interfaces/IPoolAddressesProvider.sol';
/**
* @title DefaultReserveInterestRateStrategyV2 contract
* @author BGD Labs
* @notice Default interest rate strategy used by the Aave protocol
* @dev Strategies are pool-specific: each contract CAN'T be used across different Aave pools
* due to the caching of the PoolAddressesProvider and the usage of underlying addresses as
* index of the _interestRateData
*/
contract DefaultReserveInterestRateStrategyV2 is IDefaultInterestRateStrategyV2 {
using WadRayMath for uint256;
using PercentageMath for uint256;
struct CalcInterestRatesLocalVars {
uint256 availableLiquidity;
uint256 currentVariableBorrowRate;
uint256 currentLiquidityRate;
uint256 borrowUsageRatio;
uint256 supplyUsageRatio;
uint256 availableLiquidityPlusDebt;
}
/// @inheritdoc IDefaultInterestRateStrategyV2
IPoolAddressesProvider public immutable ADDRESSES_PROVIDER;
/// @inheritdoc IDefaultInterestRateStrategyV2
uint256 public constant MAX_BORROW_RATE = 1000_00;
/// @inheritdoc IDefaultInterestRateStrategyV2
uint256 public constant MIN_OPTIMAL_POINT = 1_00;
/// @inheritdoc IDefaultInterestRateStrategyV2
uint256 public constant MAX_OPTIMAL_POINT = 99_00;
/// @dev Map of reserves address and their interest rate data (reserveAddress => interestRateData)
mapping(address => InterestRateData) internal _interestRateData;
modifier onlyPoolConfigurator() {
require(
msg.sender == ADDRESSES_PROVIDER.getPoolConfigurator(),
Errors.CALLER_NOT_POOL_CONFIGURATOR
);
_;
}
/**
* @dev Constructor.
* @param provider The address of the PoolAddressesProvider of the associated Aave pool
*/
constructor(address provider) {
require(provider != address(0), Errors.INVALID_ADDRESSES_PROVIDER);
ADDRESSES_PROVIDER = IPoolAddressesProvider(provider);
}
/// @inheritdoc IReserveInterestRateStrategy
function setInterestRateParams(
address reserve,
bytes calldata rateData
) external onlyPoolConfigurator {
_setInterestRateParams(reserve, abi.decode(rateData, (InterestRateData)));
}
/// @inheritdoc IDefaultInterestRateStrategyV2
function setInterestRateParams(
address reserve,
InterestRateData calldata rateData
) external onlyPoolConfigurator {
_setInterestRateParams(reserve, rateData);
}
/// @inheritdoc IDefaultInterestRateStrategyV2
function getInterestRateData(address reserve) external view returns (InterestRateDataRay memory) {
return _rayifyRateData(_interestRateData[reserve]);
}
/// @inheritdoc IDefaultInterestRateStrategyV2
function getInterestRateDataBps(address reserve) external view returns (InterestRateData memory) {
return _interestRateData[reserve];
}
/// @inheritdoc IDefaultInterestRateStrategyV2
function getOptimalUsageRatio(address reserve) external view returns (uint256) {
return _bpsToRay(uint256(_interestRateData[reserve].optimalUsageRatio));
}
/// @inheritdoc IDefaultInterestRateStrategyV2
function getVariableRateSlope1(address reserve) external view returns (uint256) {
return _bpsToRay(uint256(_interestRateData[reserve].variableRateSlope1));
}
/// @inheritdoc IDefaultInterestRateStrategyV2
function getVariableRateSlope2(address reserve) external view returns (uint256) {
return _bpsToRay(uint256(_interestRateData[reserve].variableRateSlope2));
}
/// @inheritdoc IDefaultInterestRateStrategyV2
function getBaseVariableBorrowRate(address reserve) external view override returns (uint256) {
return _bpsToRay(uint256(_interestRateData[reserve].baseVariableBorrowRate));
}
/// @inheritdoc IDefaultInterestRateStrategyV2
function getMaxVariableBorrowRate(address reserve) external view override returns (uint256) {
return
_bpsToRay(
uint256(
_interestRateData[reserve].baseVariableBorrowRate +
_interestRateData[reserve].variableRateSlope1 +
_interestRateData[reserve].variableRateSlope2
)
);
}
/// @inheritdoc IReserveInterestRateStrategy
function calculateInterestRates(
DataTypes.CalculateInterestRatesParams memory params
) external view virtual override returns (uint256, uint256) {
InterestRateDataRay memory rateData = _rayifyRateData(_interestRateData[params.reserve]);
// @note This is a short circuit to allow mintable assets (ex. GHO), which by definition cannot be supplied
// and thus do not use virtual underlying balances.
if (!params.usingVirtualBalance) {
return (0, rateData.baseVariableBorrowRate);
}
CalcInterestRatesLocalVars memory vars;
vars.currentLiquidityRate = 0;
vars.currentVariableBorrowRate = rateData.baseVariableBorrowRate;
if (params.totalDebt != 0) {
vars.availableLiquidity =
params.virtualUnderlyingBalance +
params.liquidityAdded -
params.liquidityTaken;
vars.availableLiquidityPlusDebt = vars.availableLiquidity + params.totalDebt;
vars.borrowUsageRatio = params.totalDebt.rayDiv(vars.availableLiquidityPlusDebt);
vars.supplyUsageRatio = params.totalDebt.rayDiv(
vars.availableLiquidityPlusDebt + params.unbacked
);
} else {
return (0, vars.currentVariableBorrowRate);
}
if (vars.borrowUsageRatio > rateData.optimalUsageRatio) {
uint256 excessBorrowUsageRatio = (vars.borrowUsageRatio - rateData.optimalUsageRatio).rayDiv(
WadRayMath.RAY - rateData.optimalUsageRatio
);
vars.currentVariableBorrowRate +=
rateData.variableRateSlope1 +
rateData.variableRateSlope2.rayMul(excessBorrowUsageRatio);
} else {
vars.currentVariableBorrowRate += rateData
.variableRateSlope1
.rayMul(vars.borrowUsageRatio)
.rayDiv(rateData.optimalUsageRatio);
}
vars.currentLiquidityRate = vars
.currentVariableBorrowRate
.rayMul(vars.supplyUsageRatio)
.percentMul(PercentageMath.PERCENTAGE_FACTOR - params.reserveFactor);
return (vars.currentLiquidityRate, vars.currentVariableBorrowRate);
}
/**
* @dev Doing validations and data update for an asset
* @param reserve address of the underlying asset of the reserve
* @param rateData Encoded reserve interest rate data to apply
*/
function _setInterestRateParams(address reserve, InterestRateData memory rateData) internal {
require(reserve != address(0), Errors.ZERO_ADDRESS_NOT_VALID);
require(
rateData.optimalUsageRatio <= MAX_OPTIMAL_POINT &&
rateData.optimalUsageRatio >= MIN_OPTIMAL_POINT,
Errors.INVALID_OPTIMAL_USAGE_RATIO
);
require(
rateData.variableRateSlope1 <= rateData.variableRateSlope2,
Errors.SLOPE_2_MUST_BE_GTE_SLOPE_1
);
// The maximum rate should not be above certain threshold
require(
uint256(rateData.baseVariableBorrowRate) +
uint256(rateData.variableRateSlope1) +
uint256(rateData.variableRateSlope2) <=
MAX_BORROW_RATE,
Errors.INVALID_MAX_RATE
);
_interestRateData[reserve] = rateData;
emit RateDataUpdate(
reserve,
rateData.optimalUsageRatio,
rateData.baseVariableBorrowRate,
rateData.variableRateSlope1,
rateData.variableRateSlope2
);
}
/**
* @dev Transforms an InterestRateData struct to an InterestRateDataRay struct by multiplying all values
* by 1e23, turning them into ray values
*
* @param data The InterestRateData struct to transform
*
* @return The resulting InterestRateDataRay struct
*/
function _rayifyRateData(
InterestRateData memory data
) internal pure returns (InterestRateDataRay memory) {
return
InterestRateDataRay({
optimalUsageRatio: _bpsToRay(uint256(data.optimalUsageRatio)),
baseVariableBorrowRate: _bpsToRay(uint256(data.baseVariableBorrowRate)),
variableRateSlope1: _bpsToRay(uint256(data.variableRateSlope1)),
variableRateSlope2: _bpsToRay(uint256(data.variableRateSlope2))
});
}
// @dev helper function added here, as generally the protocol doesn't use bps
function _bpsToRay(uint256 n) internal pure returns (uint256) {
return n * 1e23;
}
}