@@ -273,9 +273,9 @@
|
|
273
273
|
/// @notice Allow spender to access an amount of your eTokens in a particular sub-account
|
274
274
|
/// @param subAccountId 0 for primary, 1-255 for a sub-account
|
275
275
|
/// @param spender Trusted address
|
276
276
|
/// @param amount Use max uint256 for "infinite" allowance
|
277
|
-
function approveSubAccount(uint subAccountId, address spender, uint amount) public
|
277
|
+
function approveSubAccount(uint subAccountId, address spender, uint amount) public nonReentrant returns (bool) {
|
278
278
|
(, AssetStorage storage assetStorage, address proxyAddr, address msgSender) = CALLER();
|
279
279
|
address account = getSubAccount(msgSender, subAccountId);
|
280
280
|
|
281
281
|
require(!isSubAccountOf(spender, account), "e/self-approval");
|
@@ -300,16 +300,16 @@
|
|
300
300
|
|
301
301
|
/// @notice Transfer eTokens to another address (from sub-account 0)
|
302
302
|
/// @param to Xor with the desired sub-account ID (if applicable)
|
303
303
|
/// @param amount In internal book-keeping units (as returned from balanceOf).
|
304
|
-
function transfer(address to, uint amount) external returns (bool) {
|
304
|
+
function transfer(address to, uint amount) external reentrantOK returns (bool) {
|
305
305
|
return transferFrom(address(0), to, amount);
|
306
306
|
}
|
307
307
|
|
308
308
|
/// @notice Transfer the full eToken balance of an address to another
|
309
309
|
/// @param from This address must've approved the to address, or be a sub-account of msg.sender
|
310
310
|
/// @param to Xor with the desired sub-account ID (if applicable)
|
311
|
-
function transferFromMax(address from, address to) external returns (bool) {
|
311
|
+
function transferFromMax(address from, address to) external reentrantOK returns (bool) {
|
312
312
|
(, AssetStorage storage assetStorage,,) = CALLER();
|
313
313
|
|
314
314
|
return transferFrom(from, to, assetStorage.users[from].balance);
|
315
315
|
}
|
@@ -346,5 +346,37 @@
|
|
346
346
|
logAssetStatus(assetCache);
|
347
347
|
|
348
348
|
return true;
|
349
349
|
}
|
350
|
+
|
351
|
+
/// @notice Donate eTokens to the reserves
|
352
|
+
/// @param subAccountId 0 for primary, 1-255 for a sub-account
|
353
|
+
/// @param amount In internal book-keeping units (as returned from balanceOf).
|
354
|
+
function donateToReserves(uint subAccountId, uint amount) external nonReentrant {
|
355
|
+
(address underlying, AssetStorage storage assetStorage, address proxyAddr, address msgSender) = CALLER();
|
356
|
+
address account = getSubAccount(msgSender, subAccountId);
|
357
|
+
|
358
|
+
updateAverageLiquidity(account);
|
359
|
+
emit RequestDonate(account, amount);
|
360
|
+
|
361
|
+
AssetCache memory assetCache = loadAssetCache(underlying, assetStorage);
|
362
|
+
|
363
|
+
uint origBalance = assetStorage.users[account].balance;
|
364
|
+
uint newBalance;
|
365
|
+
|
366
|
+
if (amount == type(uint).max) {
|
367
|
+
amount = origBalance;
|
368
|
+
newBalance = 0;
|
369
|
+
} else {
|
370
|
+
require(origBalance >= amount, "e/insufficient-balance");
|
371
|
+
unchecked { newBalance = origBalance - amount; }
|
372
|
+
}
|
373
|
+
|
374
|
+
assetStorage.users[account].balance = encodeAmount(newBalance);
|
375
|
+
assetStorage.reserveBalance = assetCache.reserveBalance = encodeSmallAmount(assetCache.reserveBalance + amount);
|
376
|
+
|
377
|
+
emit Withdraw(assetCache.underlying, account, amount);
|
378
|
+
emitViaProxy_Transfer(proxyAddr, account, address(0), amount);
|
379
|
+
|
380
|
+
logAssetStatus(assetCache);
|
381
|
+
}
|
350
382
|
}
|
@@ -289,10 +289,11 @@
|
|
289
289
|
return uint144(amount);
|
290
290
|
}
|
291
291
|
|
292
292
|
function computeExchangeRate(AssetCache memory assetCache) private pure returns (uint) {
|
293
|
-
|
294
|
-
|
293
|
+
uint totalAssets = assetCache.poolSize + (assetCache.totalBorrows / INTERNAL_DEBT_PRECISION);
|
294
|
+
if (totalAssets == 0 || assetCache.totalBalances == 0) return 1e18;
|
295
|
+
return totalAssets * 1e18 / assetCache.totalBalances;
|
295
296
|
}
|
296
297
|
|
297
298
|
function underlyingAmountToBalance(AssetCache memory assetCache, uint amount) internal pure returns (uint) {
|
298
299
|
uint exchangeRate = computeExchangeRate(assetCache);
|
@@ -311,9 +312,9 @@
|
|
311
312
|
|
312
313
|
function callBalanceOf(AssetCache memory assetCache, address account) internal view FREEMEM returns (uint) {
|
313
314
|
// We set a gas limit so that a malicious token can't eat up all gas and cause a liquidity check to fail.
|
314
315
|
|
315
|
-
(bool success, bytes memory data) = assetCache.underlying.staticcall{gas:
|
316
|
+
(bool success, bytes memory data) = assetCache.underlying.staticcall{gas: 200000}(abi.encodeWithSelector(IERC20.balanceOf.selector, account));
|
316
317
|
|
317
318
|
// If token's balanceOf() call fails for any reason, return 0. This prevents malicious tokens from causing liquidity checks to fail.
|
318
319
|
// If the contract doesn't exist (maybe because selfdestructed), then data.length will be 0 and we will return 0.
|
319
320
|
// Data length > 32 is allowed because some legitimate tokens append extra data that can be safely ignored.
|
@@ -6,9 +6,9 @@
|
|
6
6
|
|
7
7
|
abstract contract Storage is Constants {
|
8
8
|
// Dispatcher and upgrades
|
9
9
|
|
10
|
-
uint reentrancyLock;
|
10
|
+
uint internal reentrancyLock;
|
11
11
|
|
12
12
|
address upgradeAdmin;
|
13
13
|
address governorAdmin;
|
14
14
|
|
@@ -91,5 +91,6 @@
|
|
91
91
|
mapping(address => AssetStorage) internal eTokenLookup; // EToken => AssetStorage
|
92
92
|
mapping(address => address) internal dTokenLookup; // DToken => EToken
|
93
93
|
mapping(address => address) internal pTokenLookup; // PToken => underlying
|
94
94
|
mapping(address => address) internal reversePTokenLookup; // underlying => PToken
|
95
|
+
mapping(address => address) internal chainlinkPriceFeedLookup; // underlying => chainlinkAggregator
|
95
96
|
}
|
@@ -36,8 +36,9 @@
|
|
36
36
|
event RequestWithdraw(address indexed account, uint amount);
|
37
37
|
event RequestMint(address indexed account, uint amount);
|
38
38
|
event RequestBurn(address indexed account, uint amount);
|
39
39
|
event RequestTransferEToken(address indexed from, address indexed to, uint amount);
|
40
|
+
event RequestDonate(address indexed account, uint amount);
|
40
41
|
|
41
42
|
event RequestBorrow(address indexed account, uint amount);
|
42
43
|
event RequestRepay(address indexed account, uint amount);
|
43
44
|
event RequestTransferDToken(address indexed from, address indexed to, uint amount);
|
@@ -54,7 +55,8 @@
|
|
54
55
|
event GovSetIRM(address indexed underlying, uint interestRateModel, bytes resetParams);
|
55
56
|
event GovSetPricingConfig(address indexed underlying, uint16 newPricingType, uint32 newPricingParameter);
|
56
57
|
event GovSetReserveFee(address indexed underlying, uint32 newReserveFee);
|
57
58
|
event GovConvertReserves(address indexed underlying, address indexed recipient, uint amount);
|
59
|
+
event GovSetChainlinkPriceFeed(address indexed underlying, address chainlinkAggregator);
|
58
60
|
|
59
61
|
event RequestSwap(address indexed accountIn, address indexed accountOut, address indexed underlyingIn, address underlyingOut, uint amount, uint swapType);
|
60
62
|
}
|
@@ -18,8 +18,9 @@
|
|
18
18
|
uint internal constant MAX_POSSIBLE_ENTERED_MARKETS = 2**32; // limited by size of AccountStorage.numMarketsEntered
|
19
19
|
uint internal constant CONFIG_FACTOR_SCALE = 4_000_000_000; // must fit into a uint32
|
20
20
|
uint internal constant RESERVE_FEE_SCALE = 4_000_000_000; // must fit into a uint32
|
21
21
|
uint32 internal constant DEFAULT_RESERVE_FEE = uint32(0.23 * 4_000_000_000);
|
22
|
+
uint internal constant INITIAL_RESERVES = 1e6;
|
22
23
|
uint internal constant INITIAL_INTEREST_ACCUMULATOR = 1e27;
|
23
24
|
uint internal constant AVERAGE_LIQUIDITY_PERIOD = 24 * 60 * 60;
|
24
25
|
uint16 internal constant MIN_UNISWAP3_OBSERVATION_CARDINALITY = 144;
|
25
26
|
uint24 internal constant DEFAULT_TWAP_WINDOW_SECONDS = 30 * 60;
|
@@ -41,10 +42,14 @@
|
|
41
42
|
|
42
43
|
uint16 internal constant PRICINGTYPE__PEGGED = 1;
|
43
44
|
uint16 internal constant PRICINGTYPE__UNISWAP3_TWAP = 2;
|
44
45
|
uint16 internal constant PRICINGTYPE__FORWARDED = 3;
|
46
|
+
uint16 internal constant PRICINGTYPE__CHAINLINK = 4;
|
45
47
|
|
48
|
+
// Correct pricing types are always less than this value
|
49
|
+
uint16 internal constant PRICINGTYPE__OUT_OF_BOUNDS = 5;
|
46
50
|
|
51
|
+
|
47
52
|
// Modules
|
48
53
|
|
49
54
|
// Public single-proxy modules
|
50
55
|
uint internal constant MODULEID__INSTALLER = 1;
|