| @@ -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 | 
             
            }
         |