@@ -47,17 +47,20 @@
|
|
47
47
|
|
48
48
|
function setPricingConfig(address underlying, uint16 newPricingType, uint32 newPricingParameter) external nonReentrant governorOnly {
|
49
49
|
address eTokenAddr = underlyingLookup[underlying].eTokenAddress;
|
50
50
|
require(eTokenAddr != address(0), "e/gov/underlying-not-activated");
|
51
|
+
require(newPricingType > 0 && newPricingType < PRICINGTYPE__OUT_OF_BOUNDS, "e/gov/bad-pricing-type");
|
51
52
|
|
52
53
|
AssetStorage storage assetStorage = eTokenLookup[eTokenAddr];
|
53
54
|
AssetCache memory assetCache = loadAssetCache(underlying, assetStorage);
|
54
55
|
|
55
|
-
require(newPricingType == assetCache.pricingType, "e/gov/pricing-type-change-not-supported");
|
56
|
-
|
57
56
|
assetStorage.pricingType = assetCache.pricingType = newPricingType;
|
58
57
|
assetStorage.pricingParameters = assetCache.pricingParameters = newPricingParameter;
|
59
58
|
|
59
|
+
if (newPricingType == PRICINGTYPE__CHAINLINK) {
|
60
|
+
require(chainlinkPriceFeedLookup[underlying] != address(0), "e/gov/chainlink-price-feed-not-initialized");
|
61
|
+
}
|
62
|
+
|
60
63
|
emit GovSetPricingConfig(underlying, newPricingType, newPricingParameter);
|
61
64
|
}
|
62
65
|
|
63
66
|
function setReserveFee(address underlying, uint32 newReserveFee) external nonReentrant governorOnly {
|
@@ -80,25 +83,40 @@
|
|
80
83
|
|
81
84
|
updateAverageLiquidity(recipient);
|
82
85
|
|
83
86
|
AssetStorage storage assetStorage = eTokenLookup[eTokenAddress];
|
87
|
+
require(assetStorage.reserveBalance >= INITIAL_RESERVES, "e/gov/reserves-depleted");
|
88
|
+
|
84
89
|
AssetCache memory assetCache = loadAssetCache(underlying, assetStorage);
|
85
90
|
|
86
|
-
|
87
|
-
|
91
|
+
uint maxAmount = assetCache.reserveBalance - INITIAL_RESERVES;
|
92
|
+
if (amount == type(uint).max) amount = maxAmount;
|
93
|
+
require(amount <= maxAmount, "e/gov/insufficient-reserves");
|
88
94
|
|
89
95
|
assetStorage.reserveBalance = assetCache.reserveBalance = assetCache.reserveBalance - uint96(amount);
|
90
96
|
// Decrease totalBalances because increaseBalance will increase it by amount
|
91
97
|
assetStorage.totalBalances = assetCache.totalBalances = encodeAmount(assetCache.totalBalances - amount);
|
92
98
|
|
93
99
|
increaseBalance(assetStorage, assetCache, eTokenAddress, recipient, amount);
|
94
100
|
|
101
|
+
if (assetStorage.users[recipient].owed != 0) checkLiquidity(recipient);
|
102
|
+
|
95
103
|
logAssetStatus(assetCache);
|
96
104
|
|
97
105
|
emit GovConvertReserves(underlying, recipient, balanceToUnderlyingAmount(assetCache, amount));
|
98
106
|
}
|
99
107
|
|
108
|
+
function setChainlinkPriceFeed(address underlying, address chainlinkAggregator) external nonReentrant governorOnly {
|
109
|
+
address eTokenAddr = underlyingLookup[underlying].eTokenAddress;
|
110
|
+
require(eTokenAddr != address(0), "e/gov/underlying-not-activated");
|
111
|
+
require(chainlinkAggregator != address(0), "e/gov/bad-chainlink-address");
|
100
112
|
|
113
|
+
chainlinkPriceFeedLookup[underlying] = chainlinkAggregator;
|
114
|
+
|
115
|
+
emit GovSetChainlinkPriceFeed(underlying, chainlinkAggregator);
|
116
|
+
}
|
117
|
+
|
118
|
+
|
101
119
|
// getters
|
102
120
|
|
103
121
|
function getGovernorAdmin() external view returns (address) {
|
104
122
|
return governorAdmin;
|
@@ -256,13 +256,18 @@
|
|
256
256
|
}
|
257
257
|
}
|
258
258
|
|
259
259
|
function loadAssetCacheRO(address underlying, AssetStorage storage assetStorage) internal view returns (AssetCache memory assetCache) {
|
260
|
+
require(reentrancyLock == REENTRANCYLOCK__UNLOCKED, "e/ro-reentrancy");
|
260
261
|
initAssetCache(underlying, assetStorage, assetCache);
|
261
262
|
}
|
262
263
|
|
264
|
+
function internalLoadAssetCacheRO(address underlying, AssetStorage storage assetStorage) internal view returns (AssetCache memory assetCache) {
|
265
|
+
initAssetCache(underlying, assetStorage, assetCache);
|
266
|
+
}
|
263
267
|
|
264
268
|
|
269
|
+
|
265
270
|
// Utils
|
266
271
|
|
267
272
|
function decodeExternalAmount(AssetCache memory assetCache, uint externalAmount) internal pure returns (uint scaledAmount) {
|
268
273
|
require(externalAmount <= assetCache.maxExternalAmount, "e/amount-too-large");
|
@@ -284,10 +289,11 @@
|
|
284
289
|
return uint144(amount);
|
285
290
|
}
|
286
291
|
|
287
292
|
function computeExchangeRate(AssetCache memory assetCache) private pure returns (uint) {
|
288
|
-
|
289
|
-
|
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;
|
290
296
|
}
|
291
297
|
|
292
298
|
function underlyingAmountToBalance(AssetCache memory assetCache, uint amount) internal pure returns (uint) {
|
293
299
|
uint exchangeRate = computeExchangeRate(assetCache);
|
@@ -306,9 +312,9 @@
|
|
306
312
|
|
307
313
|
function callBalanceOf(AssetCache memory assetCache, address account) internal view FREEMEM returns (uint) {
|
308
314
|
// We set a gas limit so that a malicious token can't eat up all gas and cause a liquidity check to fail.
|
309
315
|
|
310
|
-
(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));
|
311
317
|
|
312
318
|
// If token's balanceOf() call fails for any reason, return 0. This prevents malicious tokens from causing liquidity checks to fail.
|
313
319
|
// If the contract doesn't exist (maybe because selfdestructed), then data.length will be 0 and we will return 0.
|
314
320
|
// Data length > 32 is allowed because some legitimate tokens append extra data that can be safely ignored.
|
@@ -441,13 +447,13 @@
|
|
441
447
|
|
442
448
|
if (owed > prevOwed) {
|
443
449
|
uint change = owed - prevOwed;
|
444
450
|
emit Borrow(assetCache.underlying, account, change);
|
445
|
-
emitViaProxy_Transfer(dTokenAddress, address(0), account, change);
|
451
|
+
emitViaProxy_Transfer(dTokenAddress, address(0), account, change / assetCache.underlyingDecimalsScaler);
|
446
452
|
} else if (prevOwed > owed) {
|
447
453
|
uint change = prevOwed - owed;
|
448
454
|
emit Repay(assetCache.underlying, account, change);
|
449
|
-
emitViaProxy_Transfer(dTokenAddress, account, address(0), change);
|
455
|
+
emitViaProxy_Transfer(dTokenAddress, account, address(0), change / assetCache.underlyingDecimalsScaler);
|
450
456
|
}
|
451
457
|
}
|
452
458
|
|
453
459
|
function increaseBorrow(AssetStorage storage assetStorage, AssetCache memory assetCache, address dTokenAddress, address account, uint amount) internal {
|
@@ -497,9 +503,9 @@
|
|
497
503
|
|
498
504
|
if (toOwed == 0) doEnterMarket(to, assetCache.underlying);
|
499
505
|
|
500
506
|
// If amount was rounded up, transfer exact amount owed
|
501
|
-
if (amount > fromOwed && amount - fromOwed < INTERNAL_DEBT_PRECISION) amount = fromOwed;
|
507
|
+
if (amount > fromOwed && amount - fromOwed < INTERNAL_DEBT_PRECISION * assetCache.underlyingDecimalsScaler) amount = fromOwed;
|
502
508
|
|
503
509
|
require(fromOwed >= amount, "e/insufficient-balance");
|
504
510
|
unchecked { fromOwed -= amount; }
|
505
511
|
|
@@ -22,24 +22,16 @@
|
|
22
22
|
// Accessing parameters
|
23
23
|
|
24
24
|
function unpackTrailingParamMsgSender() internal pure returns (address msgSender) {
|
25
25
|
assembly {
|
26
|
-
|
27
|
-
|
28
|
-
calldatacopy(12, sub(calldatasize(), 40), 20)
|
29
|
-
msgSender := mload(0)
|
26
|
+
msgSender := shr(96, calldataload(sub(calldatasize(), 40)))
|
30
27
|
}
|
31
28
|
}
|
32
29
|
|
33
30
|
function unpackTrailingParams() internal pure returns (address msgSender, address proxyAddr) {
|
34
31
|
assembly {
|
35
|
-
|
36
|
-
|
37
|
-
calldatacopy(12, sub(calldatasize(), 40), 20)
|
38
|
-
msgSender := mload(0)
|
39
|
-
|
40
|
-
calldatacopy(12, sub(calldatasize(), 20), 20)
|
41
|
-
proxyAddr := mload(0)
|
32
|
+
msgSender := shr(96, calldataload(sub(calldatasize(), 40)))
|
33
|
+
proxyAddr := shr(96, calldataload(sub(calldatasize(), 20)))
|
42
34
|
}
|
43
35
|
}
|
44
36
|
|
45
37
|
|
@@ -18,8 +18,14 @@
|
|
18
18
|
function transfer(address to, uint value) external returns (bool);
|
19
19
|
function transferFrom(address from, address to, uint value) external returns (bool);
|
20
20
|
}
|
21
21
|
|
22
|
+
interface IERC20Permit {
|
23
|
+
function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
|
24
|
+
function permit(address holder, address spender, uint256 nonce, uint256 expiry, bool allowed, uint8 v, bytes32 r, bytes32 s) external;
|
25
|
+
function permit(address owner, address spender, uint value, uint deadline, bytes calldata signature) external;
|
26
|
+
}
|
27
|
+
|
22
28
|
interface IERC3156FlashBorrower {
|
23
29
|
function onFlashLoan(address initiator, address token, uint256 amount, uint256 fee, bytes calldata data) external returns (bytes32);
|
24
30
|
}
|
25
31
|
|
@@ -27,11 +27,11 @@
|
|
27
27
|
}
|
28
28
|
|
29
29
|
function getNewMarketParameters(address underlying) external returns (NewMarketParameters memory);
|
30
30
|
|
31
|
-
function requireLiquidity(address account) external;
|
32
|
-
function computeLiquidity(address account) external returns (LiquidityStatus memory status);
|
33
|
-
function computeAssetLiquidities(address account) external returns (AssetLiquidity[] memory assets);
|
31
|
+
function requireLiquidity(address account) external view;
|
32
|
+
function computeLiquidity(address account) external view returns (LiquidityStatus memory status);
|
33
|
+
function computeAssetLiquidities(address account) external view returns (AssetLiquidity[] memory assets);
|
34
34
|
|
35
|
-
function getPrice(address underlying) external returns (uint twap, uint twapPeriod);
|
36
|
-
function getPriceFull(address underlying) external returns (uint twap, uint twapPeriod, uint currPrice);
|
35
|
+
function getPrice(address underlying) external view returns (uint twap, uint twapPeriod);
|
36
|
+
function getPriceFull(address underlying) external view returns (uint twap, uint twapPeriod, uint currPrice);
|
37
37
|
}
|
@@ -52,8 +52,15 @@
|
|
52
52
|
modifier reentrantOK() { // documentation only
|
53
53
|
_;
|
54
54
|
}
|
55
55
|
|
56
|
+
// Used to flag functions which do not modify storage, but do perform a delegate call
|
57
|
+
// to a view function, which prohibits a standard view modifier. The flag is used to
|
58
|
+
// patch state mutability in compiled ABIs and interfaces.
|
59
|
+
modifier staticDelegate() {
|
60
|
+
_;
|
61
|
+
}
|
62
|
+
|
56
63
|
// WARNING: Must be very careful with this modifier. It resets the free memory pointer
|
57
64
|
// to the value it was when the function started. This saves gas if more memory will
|
58
65
|
// be allocated in the future. However, if the memory will be later referenced
|
59
66
|
// (for example because the function has returned a pointer to it) then you cannot
|
@@ -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,13 +18,15 @@
|
|
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
|
-
uint16 internal constant MIN_UNISWAP3_OBSERVATION_CARDINALITY =
|
25
|
+
uint16 internal constant MIN_UNISWAP3_OBSERVATION_CARDINALITY = 144;
|
25
26
|
uint24 internal constant DEFAULT_TWAP_WINDOW_SECONDS = 30 * 60;
|
26
27
|
uint32 internal constant DEFAULT_BORROW_FACTOR = uint32(0.28 * 4_000_000_000);
|
28
|
+
uint32 internal constant SELF_COLLATERAL_FACTOR = uint32(0.95 * 4_000_000_000);
|
27
29
|
|
28
30
|
|
29
31
|
// Implementation internals
|
30
32
|
|
@@ -40,10 +42,14 @@
|
|
40
42
|
|
41
43
|
uint16 internal constant PRICINGTYPE__PEGGED = 1;
|
42
44
|
uint16 internal constant PRICINGTYPE__UNISWAP3_TWAP = 2;
|
43
45
|
uint16 internal constant PRICINGTYPE__FORWARDED = 3;
|
46
|
+
uint16 internal constant PRICINGTYPE__CHAINLINK = 4;
|
44
47
|
|
48
|
+
// Correct pricing types are always less than this value
|
49
|
+
uint16 internal constant PRICINGTYPE__OUT_OF_BOUNDS = 5;
|
45
50
|
|
51
|
+
|
46
52
|
// Modules
|
47
53
|
|
48
54
|
// Public single-proxy modules
|
49
55
|
uint internal constant MODULEID__INSTALLER = 1;
|
@@ -74,12 +80,16 @@
|
|
74
80
|
// Classes
|
75
81
|
uint internal constant MODULEID__IRM_CLASS__STABLE = 2_000_500;
|
76
82
|
uint internal constant MODULEID__IRM_CLASS__MAJOR = 2_000_501;
|
77
83
|
uint internal constant MODULEID__IRM_CLASS__MIDCAP = 2_000_502;
|
84
|
+
uint internal constant MODULEID__IRM_CLASS__MEGA = 2_000_503;
|
78
85
|
|
79
86
|
// Swap types
|
80
87
|
uint internal constant SWAP_TYPE__UNI_EXACT_INPUT_SINGLE = 1;
|
81
88
|
uint internal constant SWAP_TYPE__UNI_EXACT_INPUT = 2;
|
82
89
|
uint internal constant SWAP_TYPE__UNI_EXACT_OUTPUT_SINGLE = 3;
|
83
90
|
uint internal constant SWAP_TYPE__UNI_EXACT_OUTPUT = 4;
|
84
91
|
uint internal constant SWAP_TYPE__1INCH = 5;
|
92
|
+
|
93
|
+
uint internal constant SWAP_TYPE__UNI_EXACT_OUTPUT_SINGLE_REPAY = 6;
|
94
|
+
uint internal constant SWAP_TYPE__UNI_EXACT_OUTPUT_REPAY = 7;
|
85
95
|
}
|