Euler diff: contracts/modules/Governance.sol

Before: contract 0x554ee3d9ed7E9ec21E186c7dd636430669812f73
After: git d7f62927eb58592d82302c87205490c26cccca7c
Files changed (9) hide show
  1. contracts/modules/Governance.sol +22 -4
  2. contracts/BaseLogic.sol +12 -6
  3. contracts/BaseModule.sol +3 -11
  4. contracts/Interfaces.sol +6 -0
  5. contracts/IRiskManager.sol +5 -5
  6. contracts/Base.sol +7 -0
  7. contracts/Storage.sol +2 -1
  8. contracts/Events.sol +2 -0
  9. contracts/Constants.sol +11 -1
contracts/modules/Governance.sol CHANGED
@@ -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
- if (amount == type(uint).max) amount = assetStorage.reserveBalance;
87
- require(amount <= assetStorage.reserveBalance, "e/gov/insufficient-reserves");
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;
contracts/BaseLogic.sol CHANGED
@@ -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
- if (assetCache.totalBalances == 0) return 1e18;
289
- return (assetCache.poolSize + (assetCache.totalBorrows / INTERNAL_DEBT_PRECISION)) * 1e18 / assetCache.totalBalances;
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: 20000}(abi.encodeWithSelector(IERC20.balanceOf.selector, account));
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
 
contracts/BaseModule.sol CHANGED
@@ -22,24 +22,16 @@
22
22
  // Accessing parameters
23
23
 
24
24
  function unpackTrailingParamMsgSender() internal pure returns (address msgSender) {
25
25
  assembly {
26
- mstore(0, 0)
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
- mstore(0, 0)
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
 
contracts/Interfaces.sol CHANGED
@@ -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
 
contracts/IRiskManager.sol CHANGED
@@ -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
  }
contracts/Base.sol CHANGED
@@ -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
contracts/Storage.sol CHANGED
@@ -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
  }
contracts/Events.sol CHANGED
@@ -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
  }
contracts/Constants.sol CHANGED
@@ -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 = 10;
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
  }