-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathFraxlendPair.sol
338 lines (288 loc) · 13.4 KB
/
FraxlendPair.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
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
// SPDX-License-Identifier: ISC
pragma solidity ^0.8.15;
// ====================================================================
// | ______ _______ |
// | / _____________ __ __ / ____(_____ ____ _____ ________ |
// | / /_ / ___/ __ `| |/_/ / /_ / / __ \/ __ `/ __ \/ ___/ _ \ |
// | / __/ / / / /_/ _> < / __/ / / / / / /_/ / / / / /__/ __/ |
// | /_/ /_/ \__,_/_/|_| /_/ /_/_/ /_/\__,_/_/ /_/\___/\___/ |
// | |
// ====================================================================
// ========================== FraxlendPair ============================
// ====================================================================
// Frax Finance: https://github.com/FraxFinance
// Primary Author
// Drake Evans: https://github.com/DrakeEvans
// Reviewers
// Dennis: https://github.com/denett
// Sam Kazemian: https://github.com/samkazemian
// Travis Moore: https://github.com/FortisFortuna
// Jack Corddry: https://github.com/corddry
// Rich Gee: https://github.com/zer0blockchain
// ====================================================================
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
import "./FraxlendPairConstants.sol";
import "./FraxlendPairCore.sol";
import "./libraries/VaultAccount.sol";
import "./libraries/SafeERC20.sol";
import "./interfaces/IERC4626.sol";
import "./interfaces/IFraxlendWhitelist.sol";
import "./interfaces/IRateCalculator.sol";
import "./interfaces/ISwapper.sol";
contract FraxlendPair is FraxlendPairCore {
using VaultAccountingLibrary for VaultAccount;
using SafeERC20 for IERC20;
constructor(
bytes memory _configData,
bytes memory _immutables,
uint256 _maxLTV,
uint256 _liquidationFee,
uint256 _maturityDate,
uint256 _penaltyRate,
bool _isBorrowerWhitelistActive,
bool _isLenderWhitelistActive
)
FraxlendPairCore(
_configData,
_immutables,
_maxLTV,
_liquidationFee,
_maturityDate,
_penaltyRate,
_isBorrowerWhitelistActive,
_isLenderWhitelistActive
)
ERC20("", "")
Ownable()
Pausable()
{}
// ============================================================================================
// ERC20 Metadata
// ============================================================================================
function name() public view override(ERC20, IERC20Metadata) returns (string memory) {
return nameOfContract;
}
function symbol() public view override(ERC20, IERC20Metadata) returns (string memory) {
// prettier-ignore
// solhint-disable-next-line max-line-length
return string(abi.encodePacked("FraxlendV1 - ", collateralContract.safeSymbol(), "/", assetContract.safeSymbol()));
}
function decimals() public pure override(ERC20, IERC20Metadata) returns (uint8) {
return 18;
}
// totalSupply for fToken ERC20 compatibility
function totalSupply() public view override(ERC20, IERC20) returns (uint256) {
return totalAsset.shares;
}
// ============================================================================================
// ERC4626 Views
// ============================================================================================
function asset() external view returns (address) {
return address(assetContract);
}
function totalAssets() public view virtual returns (uint256) {
return totalAsset.amount;
}
function assetsPerShare() external view returns (uint256 _assetsPerUnitShare) {
_assetsPerUnitShare = totalAsset.toAmount(1e18, false);
}
function assetsOf(address _depositor) external view returns (uint256 _assets) {
_assets = totalAsset.toAmount(balanceOf(_depositor), false);
}
function convertToShares(uint256 _amount) external view returns (uint256) {
return totalAsset.toShares(_amount, false);
}
function convertToAssets(uint256 _shares) external view returns (uint256) {
return totalAsset.toAmount(_shares, false);
}
function previewDeposit(uint256 _amount) external view returns (uint256) {
return totalAsset.toShares(_amount, false);
}
function previewMint(uint256 _shares) external view returns (uint256) {
return totalAsset.toAmount(_shares, true);
}
function previewWithdraw(uint256 _amount) external view returns (uint256) {
return totalAsset.toShares(_amount, true);
}
function previewRedeem(uint256 _shares) external view returns (uint256) {
return totalAsset.toAmount(_shares, false);
}
function maxDeposit(address) external pure returns (uint256) {
return type(uint128).max;
}
function maxMint(address) external pure returns (uint256) {
return type(uint128).max;
}
function maxWithdraw(address owner) external view returns (uint256) {
return totalAsset.toAmount(balanceOf(owner), false);
}
function maxRedeem(address owner) external view returns (uint256) {
return balanceOf(owner);
}
// ============================================================================================
// Functions: Helpers
// ============================================================================================
function getConstants()
external
pure
returns (
uint256 _LTV_PRECISION,
uint256 _LIQ_PRECISION,
uint256 _UTIL_PREC,
uint256 _FEE_PRECISION,
uint256 _EXCHANGE_PRECISION,
uint64 _DEFAULT_INT,
uint16 _DEFAULT_PROTOCOL_FEE,
uint256 _MAX_PROTOCOL_FEE
)
{
_LTV_PRECISION = LTV_PRECISION;
_LIQ_PRECISION = LIQ_PRECISION;
_UTIL_PREC = UTIL_PREC;
_FEE_PRECISION = FEE_PRECISION;
_EXCHANGE_PRECISION = EXCHANGE_PRECISION;
_DEFAULT_INT = DEFAULT_INT;
_DEFAULT_PROTOCOL_FEE = DEFAULT_PROTOCOL_FEE;
_MAX_PROTOCOL_FEE = MAX_PROTOCOL_FEE;
}
/// @notice The ```toBorrowShares``` function converts a given amount of borrow debt into the number of shares
/// @param _amount Amount of borrow
/// @param _roundUp Whether to roundup during division
function toBorrowShares(uint256 _amount, bool _roundUp) external view returns (uint256) {
return totalBorrow.toShares(_amount, _roundUp);
}
/// @notice The ```toBorrtoBorrowAmountowShares``` function converts a given amount of borrow debt into the number of shares
/// @param _shares Shares of borrow
/// @param _roundUp Whether to roundup during division
function toBorrowAmount(uint256 _shares, bool _roundUp) external view returns (uint256) {
return totalBorrow.toAmount(_shares, _roundUp);
}
// ============================================================================================
// Functions: Protocol Configuration (Fees & Swap Contracts)
// ============================================================================================
/// @notice The ```SetTimeLock``` event fires when the TIME_LOCK_ADDRESS is set
/// @param _oldAddress The original address
/// @param _newAddress The new address
event SetTimeLock(address _oldAddress, address _newAddress);
/// @notice The ```setTimeLock``` function sets the TIME_LOCK address
/// @param _newAddress the new time lock address
function setTimeLock(address _newAddress) external onlyOwner {
emit SetTimeLock(TIME_LOCK_ADDRESS, _newAddress);
TIME_LOCK_ADDRESS = _newAddress;
}
/// @notice The ```ChangeFee``` event first when the fee is changed
/// @param _newFee The new fee
event ChangeFee(uint32 _newFee);
/// @notice The ```changeFee``` function changes the protocol fee, max 50%
/// @param _newFee The new fee
function changeFee(uint32 _newFee) external whenNotPaused {
if (msg.sender != TIME_LOCK_ADDRESS) revert OnlyTimeLock();
if (_newFee > MAX_PROTOCOL_FEE) {
revert BadProtocolFee();
}
currentRateInfo.feeToProtocolRate = _newFee;
emit ChangeFee(_newFee);
}
/// @notice The ```WithdrawFees``` event fires when the fees are withdrawn
/// @param _shares Number of _shares (fTokens) redeemed
/// @param _recipient To whom the assets were sent
/// @param _amountToTransfer The amount of fees redeemed
event WithdrawFees(uint128 _shares, address _recipient, uint256 _amountToTransfer);
/// @notice The ```withdrawFees``` function withdraws fees accumulated
/// @param _shares Number of fTokens to redeem
/// @param _recipient Address to send the assets
/// @return _amountToTransfer Amount of assets sent to recipient
function withdrawFees(uint128 _shares, address _recipient) external onlyOwner returns (uint256 _amountToTransfer) {
// Grab some data from state to save gas
VaultAccount memory _totalAsset = totalAsset;
VaultAccount memory _totalBorrow = totalBorrow;
// Take all available if 0 value passed
if (_shares == 0) _shares = uint128(balanceOf(address(this)));
// We must calculate this before we subtract from _totalAsset or invoke _burn
_amountToTransfer = _totalAsset.toAmount(_shares, true);
// Check for sufficient withdraw liquidity
uint256 _assetsAvailable = _totalAssetAvailable(_totalAsset, _totalBorrow);
if (_assetsAvailable < _amountToTransfer) {
revert InsufficientAssetsInContract(_assetsAvailable, _amountToTransfer);
}
// Effects: bookkeeping
_totalAsset.amount -= uint128(_amountToTransfer);
_totalAsset.shares -= _shares;
// Effects: write to states
// NOTE: will revert if _shares > balanceOf(address(this))
_burn(address(this), _shares);
totalAsset = _totalAsset;
// Interactions
assetContract.safeTransfer(_recipient, _amountToTransfer);
emit WithdrawFees(_shares, _recipient, _amountToTransfer);
}
/// @notice The ```SetSwapper``` event fires whenever a swapper is black or whitelisted
/// @param _swapper The swapper address
/// @param _approval The approval
event SetSwapper(address _swapper, bool _approval);
/// @notice The ```setSwapper``` function is called to black or whitelist a given swapper address
/// @dev
/// @param _swapper The swapper address
/// @param _approval The approval
function setSwapper(address _swapper, bool _approval) external onlyOwner {
swappers[_swapper] = _approval;
emit SetSwapper(_swapper, _approval);
}
/// @notice The ```SetApprovedLender``` event fires when a lender is black or whitelisted
/// @param _address The address
/// @param _approval The approval
event SetApprovedLender(address indexed _address, bool _approval);
/// @notice The ```setApprovedLenders``` function sets a given set of addresses to the whitelist
/// @dev Cannot black list self
/// @param _lenders The addresses whos status will be set
/// @param _approval The approcal status
function setApprovedLenders(address[] calldata _lenders, bool _approval) external approvedLender(msg.sender) {
for (uint256 i = 0; i < _lenders.length; i++) {
// Do not set when _approval == false and _lender == msg.sender
if (_approval || _lenders[i] != msg.sender) {
approvedLenders[_lenders[i]] = _approval;
emit SetApprovedLender(_lenders[i], _approval);
}
}
}
/// @notice The ```SetApprovedBorrower``` event fires when a borrower is black or whitelisted
/// @param _address The address
/// @param _approval The approval
event SetApprovedBorrower(address indexed _address, bool _approval);
/// @notice The ```setApprovedBorrowers``` function sets a given array of addresses to the whitelist
/// @dev Cannot black list self
/// @param _borrowers The addresses whos status will be set
/// @param _approval The approcal status
function setApprovedBorrowers(address[] calldata _borrowers, bool _approval) external approvedBorrower {
for (uint256 i = 0; i < _borrowers.length; i++) {
// Do not set when _approval == false and _borrower == msg.sender
if (_approval || _borrowers[i] != msg.sender) {
approvedBorrowers[_borrowers[i]] = _approval;
emit SetApprovedBorrower(_borrowers[i], _approval);
}
}
}
function pause() external {
if (
msg.sender != CIRCUIT_BREAKER_ADDRESS &&
msg.sender != COMPTROLLER_ADDRESS &&
msg.sender != owner() &&
msg.sender != DEPLOYER_ADDRESS
) {
revert ProtocolOrOwnerOnly();
}
_addInterest(); // accrue any interest prior to pausing as it won't accrue during pause
_pause();
}
function unpause() external {
if (msg.sender != COMPTROLLER_ADDRESS && msg.sender != owner()) {
revert ProtocolOrOwnerOnly();
}
// Resets the lastTimestamp which has the effect of no interest accruing over the pause period
_addInterest();
_unpause();
}
}