diff --git a/contracts/Comptroller/Diamond/facets/MarketFacet.sol b/contracts/Comptroller/Diamond/facets/MarketFacet.sol index 12e4ca787..f00b830ff 100644 --- a/contracts/Comptroller/Diamond/facets/MarketFacet.sol +++ b/contracts/Comptroller/Diamond/facets/MarketFacet.sol @@ -226,6 +226,15 @@ contract MarketFacet is IMarketFacet, FacetBase { return uint256(Error.NO_ERROR); } + /** + * @notice Alias to _supportMarket to support the Isolated Lending Comptroller Interface + * @param vToken The address of the market (token) to list + * @return uint256 0=success, otherwise a failure. (See enum Error for details) + */ + function supportMarket(VToken vToken) external returns (uint256) { + return __supportMarket(vToken); + } + /** * @notice Add the market to the markets mapping and set it as listed * @dev Allows a privileged role to add and list markets to the Comptroller @@ -233,26 +242,16 @@ contract MarketFacet is IMarketFacet, FacetBase { * @return uint256 0=success, otherwise a failure. (See enum Error for details) */ function _supportMarket(VToken vToken) external returns (uint256) { - ensureAllowed("_supportMarket(address)"); - - if (markets[address(vToken)].isListed) { - return fail(Error.MARKET_ALREADY_LISTED, FailureInfo.SUPPORT_MARKET_EXISTS); - } - - vToken.isVToken(); // Sanity check to make sure its really a VToken - - // Note that isVenus is not in active use anymore - Market storage newMarket = markets[address(vToken)]; - newMarket.isListed = true; - newMarket.isVenus = false; - newMarket.collateralFactorMantissa = 0; - - _addMarketInternal(vToken); - _initializeMarket(address(vToken)); - - emit MarketListed(vToken); + return __supportMarket(vToken); + } - return uint256(Error.NO_ERROR); + /** + * @notice Check if a market is marked as listed (active) + * @param vToken vToken Address for the market to check + * @return listed True if listed otherwise false + */ + function isMarketListed(VToken vToken) external view returns (bool) { + return markets[address(vToken)].isListed; } /** @@ -309,4 +308,27 @@ contract MarketFacet is IMarketFacet, FacetBase { */ supplyState.block = borrowState.block = blockNumber; } + + function __supportMarket(VToken vToken) internal returns (uint256) { + ensureAllowed("_supportMarket(address)"); + + if (markets[address(vToken)].isListed) { + return fail(Error.MARKET_ALREADY_LISTED, FailureInfo.SUPPORT_MARKET_EXISTS); + } + + vToken.isVToken(); // Sanity check to make sure its really a VToken + + // Note that isVenus is not in active use anymore + Market storage newMarket = markets[address(vToken)]; + newMarket.isListed = true; + newMarket.isVenus = false; + newMarket.collateralFactorMantissa = 0; + + _addMarketInternal(vToken); + _initializeMarket(address(vToken)); + + emit MarketListed(vToken); + + return uint256(Error.NO_ERROR); + } } diff --git a/contracts/Comptroller/Diamond/facets/PolicyFacet.sol b/contracts/Comptroller/Diamond/facets/PolicyFacet.sol index ff16ff1a1..63d9b5d60 100644 --- a/contracts/Comptroller/Diamond/facets/PolicyFacet.sol +++ b/contracts/Comptroller/Diamond/facets/PolicyFacet.sol @@ -407,22 +407,26 @@ contract PolicyFacet is IPolicyFacet, XVSRewardsHelper { } } + /** + * @notice Alias to getAccountLiquidity to support the Isolated Lending Comptroller Interface + * @param account The account get liquidity for + */ + function getBorrowingPower( + address account + ) external view returns (uint256 error, uint256 liquidity, uint256 shortfall) { + return _getAccountLiquidity(account); + } + /** * @notice Determine the current account liquidity wrt collateral requirements + * @param account The account get liquidity for * @return (possible error code (semi-opaque), account liquidity in excess of collateral requirements, * account shortfall below collateral requirements) */ function getAccountLiquidity(address account) external view returns (uint256, uint256, uint256) { - (Error err, uint256 liquidity, uint256 shortfall) = getHypotheticalAccountLiquidityInternal( - account, - VToken(address(0)), - 0, - 0 - ); - - return (uint256(err), liquidity, shortfall); - } + return _getAccountLiquidity(account); + } /** * @notice Determine what the account liquidity would be if the given amounts were redeemed/borrowed @@ -473,6 +477,17 @@ contract PolicyFacet is IPolicyFacet, XVSRewardsHelper { } } + function _getAccountLiquidity(address account) internal view returns (uint256, uint256, uint256) { + (Error err, uint256 liquidity, uint256 shortfall) = getHypotheticalAccountLiquidityInternal( + account, + VToken(address(0)), + 0, + 0 + ); + + return (uint256(err), liquidity, shortfall); + } + function setVenusSpeedInternal(VToken vToken, uint256 supplySpeed, uint256 borrowSpeed) internal { ensureListed(markets[address(vToken)]); diff --git a/contracts/Comptroller/Diamond/facets/SetterFacet.sol b/contracts/Comptroller/Diamond/facets/SetterFacet.sol index 7c82a7fea..01899836e 100644 --- a/contracts/Comptroller/Diamond/facets/SetterFacet.sol +++ b/contracts/Comptroller/Diamond/facets/SetterFacet.sol @@ -113,28 +113,39 @@ contract SetterFacet is ISetterFacet, FacetBase { _; } + /** + * @notice Alias to _setPriceOracle to support the Isolated Lending Comptroller Interface + * @param newOracle The new price oracle to set + * @return uint256 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function setPriceOracle( + PriceOracle newOracle + ) external returns (uint256) { + __setPriceOracle(newOracle); + } + /** * @notice Sets a new price oracle for the comptroller * @dev Allows the contract admin to set a new price oracle used by the Comptroller + * @param newOracle The new price oracle to set * @return uint256 0=success, otherwise a failure (see ErrorReporter.sol for details) */ - function _setPriceOracle( + function _setPriceOracle( PriceOracle newOracle - ) external compareAddress(address(oracle), address(newOracle)) returns (uint256) { - // Check caller is admin - ensureAdmin(); - ensureNonzeroAddress(address(newOracle)); - - // Track the old oracle for the comptroller - PriceOracle oldOracle = oracle; - - // Set comptroller's oracle to newOracle - oracle = newOracle; + ) external returns (uint256) { + __setPriceOracle(newOracle); + } - // Emit NewPriceOracle(oldOracle, newOracle) - emit NewPriceOracle(oldOracle, newOracle); - return uint256(Error.NO_ERROR); + /** + * @notice Alias to _setCloseFactor to support the Isolated Lending Comptroller Interface + * @param newCloseFactorMantissa New close factor, scaled by 1e18 + * @return uint256 0=success, otherwise will revert + */ + function setCloseFactor( + uint256 newCloseFactorMantissa + ) external returns (uint256) { + __setCloseFactor(newCloseFactorMantissa); } /** @@ -145,26 +156,8 @@ contract SetterFacet is ISetterFacet, FacetBase { */ function _setCloseFactor( uint256 newCloseFactorMantissa - ) external compareValue(closeFactorMantissa, newCloseFactorMantissa) returns (uint256) { - // Check caller is admin - ensureAdmin(); - - Exp memory newCloseFactorExp = Exp({ mantissa: newCloseFactorMantissa }); - - //-- Check close factor <= 0.9 - Exp memory highLimit = Exp({ mantissa: closeFactorMaxMantissa }); - //-- Check close factor >= 0.05 - Exp memory lowLimit = Exp({ mantissa: closeFactorMinMantissa }); - - if (lessThanExp(highLimit, newCloseFactorExp) || greaterThanExp(lowLimit, newCloseFactorExp)) { - return fail(Error.INVALID_CLOSE_FACTOR, FailureInfo.SET_CLOSE_FACTOR_VALIDATION); - } - - uint256 oldCloseFactorMantissa = closeFactorMantissa; - closeFactorMantissa = newCloseFactorMantissa; - emit NewCloseFactor(oldCloseFactorMantissa, newCloseFactorMantissa); - - return uint256(Error.NO_ERROR); + ) external returns (uint256) { + __setCloseFactor(newCloseFactorMantissa); } /** @@ -188,6 +181,22 @@ contract SetterFacet is ISetterFacet, FacetBase { return uint256(Error.NO_ERROR); } + /** + * @notice Alias to _setCollateralFactor to support the Isolated Lending Comptroller Interface + * @param vToken The market to set the factor on + * @param newCollateralFactorMantissa The new collateral factor, scaled by 1e18 + * @param newLiquidationThresholdMantissa The new liquidation threshold, scaled by 1e18 + * @return uint256 0=success, otherwise a failure. (See ErrorReporter for details) + */ + function setCollateralFactor( + VToken vToken, + uint256 newCollateralFactorMantissa, + uint256 newLiquidationThresholdMantissa + ) external returns (uint256) { + require(newCollateralFactorMantissa == newLiquidationThresholdMantissa, "collateral factor and liquidation threshold must be the same"); + __setCollateralFactor(vToken, newCollateralFactorMantissa); + } + /** * @notice Sets the collateralFactor for a market * @dev Allows a privileged role to set the collateralFactorMantissa @@ -198,40 +207,20 @@ contract SetterFacet is ISetterFacet, FacetBase { function _setCollateralFactor( VToken vToken, uint256 newCollateralFactorMantissa - ) - external - compareValue(markets[address(vToken)].collateralFactorMantissa, newCollateralFactorMantissa) - returns (uint256) - { - // Check caller is allowed by access control manager - ensureAllowed("_setCollateralFactor(address,uint256)"); - ensureNonzeroAddress(address(vToken)); - - // Verify market is listed - Market storage market = markets[address(vToken)]; - ensureListed(market); - - Exp memory newCollateralFactorExp = Exp({ mantissa: newCollateralFactorMantissa }); - - //-- Check collateral factor <= 0.9 - Exp memory highLimit = Exp({ mantissa: collateralFactorMaxMantissa }); - if (lessThanExp(highLimit, newCollateralFactorExp)) { - return fail(Error.INVALID_COLLATERAL_FACTOR, FailureInfo.SET_COLLATERAL_FACTOR_VALIDATION); - } - - // If collateral factor != 0, fail if price == 0 - if (newCollateralFactorMantissa != 0 && oracle.getUnderlyingPrice(vToken) == 0) { - return fail(Error.PRICE_ERROR, FailureInfo.SET_COLLATERAL_FACTOR_WITHOUT_PRICE); - } - - // Set market's collateral factor to new collateral factor, remember old value - uint256 oldCollateralFactorMantissa = market.collateralFactorMantissa; - market.collateralFactorMantissa = newCollateralFactorMantissa; + ) external returns (uint256) { + __setCollateralFactor(vToken, newCollateralFactorMantissa); + } - // Emit event with asset, old collateral factor, and new collateral factor - emit NewCollateralFactor(vToken, oldCollateralFactorMantissa, newCollateralFactorMantissa); - return uint256(Error.NO_ERROR); + /** + * @notice Alias to _setLiquidationIncentive to support the Isolated Lending Comptroller Interface + * @param newLiquidationIncentiveMantissa New liquidationIncentive scaled by 1e18 + * @return uint256 0=success, otherwise a failure. (See ErrorReporter for details) + */ + function setLiquidationIncentive( + uint256 newLiquidationIncentiveMantissa + ) external returns (uint256) { + return __setLiquidationIncentive(newLiquidationIncentiveMantissa); } /** @@ -240,23 +229,12 @@ contract SetterFacet is ISetterFacet, FacetBase { * @param newLiquidationIncentiveMantissa New liquidationIncentive scaled by 1e18 * @return uint256 0=success, otherwise a failure. (See ErrorReporter for details) */ - function _setLiquidationIncentive( + function _setLiquidationIncentive( uint256 newLiquidationIncentiveMantissa - ) external compareValue(liquidationIncentiveMantissa, newLiquidationIncentiveMantissa) returns (uint256) { - ensureAllowed("_setLiquidationIncentive(uint256)"); - - require(newLiquidationIncentiveMantissa >= 1e18, "incentive < 1e18"); - - // Save current value for use in log - uint256 oldLiquidationIncentiveMantissa = liquidationIncentiveMantissa; - // Set liquidation incentive to new incentive - liquidationIncentiveMantissa = newLiquidationIncentiveMantissa; - - // Emit event with old incentive, new incentive - emit NewLiquidationIncentive(oldLiquidationIncentiveMantissa, newLiquidationIncentiveMantissa); - - return uint256(Error.NO_ERROR); + ) external returns (uint256) { + return __setLiquidationIncentive(newLiquidationIncentiveMantissa); } + /** * @notice Update the address of the liquidator contract @@ -297,24 +275,33 @@ contract SetterFacet is ISetterFacet, FacetBase { return uint256(Error.NO_ERROR); } + /** + * @notice Alias to _setMarketBorrowCaps to support the Isolated Lending Comptroller Interface + * @param vTokens The addresses of the markets (tokens) to change the borrow caps for + * @param newBorrowCaps The new borrow cap values in underlying to be set. A value of 0 corresponds to Borrow not allowed + */ + function setMarketBorrowCaps(VToken[] calldata vTokens, uint256[] calldata newBorrowCaps) external { + __setMarketBorrowCaps(vTokens, newBorrowCaps); + } + /** * @notice Set the given borrow caps for the given vToken market Borrowing that brings total borrows to or above borrow cap will revert * @dev Allows a privileged role to set the borrowing cap for a vToken market. A borrow cap of 0 corresponds to Borrow not allowed * @param vTokens The addresses of the markets (tokens) to change the borrow caps for * @param newBorrowCaps The new borrow cap values in underlying to be set. A value of 0 corresponds to Borrow not allowed */ - function _setMarketBorrowCaps(VToken[] calldata vTokens, uint256[] calldata newBorrowCaps) external { - ensureAllowed("_setMarketBorrowCaps(address[],uint256[])"); - - uint256 numMarkets = vTokens.length; - uint256 numBorrowCaps = newBorrowCaps.length; + function _setMarketBorrowCaps(VToken[] calldata vTokens, uint256[] calldata newBorrowCaps) external { + __setMarketBorrowCaps(vTokens, newBorrowCaps); + } - require(numMarkets != 0 && numMarkets == numBorrowCaps, "invalid input"); - for (uint256 i; i < numMarkets; ++i) { - borrowCaps[address(vTokens[i])] = newBorrowCaps[i]; - emit NewBorrowCap(vTokens[i], newBorrowCaps[i]); - } + /** + * @notice Alias to _setMarketSupplyCaps to support the Isolated Lending Comptroller Interface + * @param vTokens The addresses of the markets (tokens) to change the borrow caps for + * @param newSupplyCaps The new borrow cap values in underlying to be set. A value of 0 corresponds to Borrow not allowed + */ + function setMarketSupplyCaps(VToken[] calldata vTokens, uint256[] calldata newSupplyCaps) external { + __setMarketSupplyCaps(vTokens, newSupplyCaps); } /** @@ -324,17 +311,7 @@ contract SetterFacet is ISetterFacet, FacetBase { * @param newSupplyCaps The new supply cap values in underlying to be set. A value of 0 corresponds to Minting NotAllowed */ function _setMarketSupplyCaps(VToken[] calldata vTokens, uint256[] calldata newSupplyCaps) external { - ensureAllowed("_setMarketSupplyCaps(address[],uint256[])"); - - uint256 numMarkets = vTokens.length; - uint256 numSupplyCaps = newSupplyCaps.length; - - require(numMarkets != 0 && numMarkets == numSupplyCaps, "invalid input"); - - for (uint256 i; i < numMarkets; ++i) { - supplyCaps[address(vTokens[i])] = newSupplyCaps[i]; - emit NewSupplyCap(vTokens[i], newSupplyCaps[i]); - } + __setMarketSupplyCaps(vTokens, newSupplyCaps); } /** @@ -351,6 +328,16 @@ contract SetterFacet is ISetterFacet, FacetBase { return state; } + /** + * @notice Alias to _setActionsPaused to support the Isolated Lending Comptroller Interface + * @param markets_ Markets to pause/unpause the actions on + * @param actions_ List of action ids to pause/unpause + * @param paused_ The new paused state (true=paused, false=unpaused) + */ + function setActionsPaused(address[] calldata markets_, Action[] calldata actions_, bool paused_) external { + __setActionsPaused(markets_, actions_, paused_); + } + /** * @notice Pause/unpause certain actions * @dev Allows a privileged role to pause/unpause the protocol action state @@ -359,15 +346,7 @@ contract SetterFacet is ISetterFacet, FacetBase { * @param paused_ The new paused state (true=paused, false=unpaused) */ function _setActionsPaused(address[] calldata markets_, Action[] calldata actions_, bool paused_) external { - ensureAllowed("_setActionsPaused(address[],uint8[],bool)"); - - uint256 numMarkets = markets_.length; - uint256 numActions = actions_.length; - for (uint256 marketIdx; marketIdx < numMarkets; ++marketIdx) { - for (uint256 actionIdx; actionIdx < numActions; ++actionIdx) { - setActionPausedInternal(markets_[marketIdx], actions_[actionIdx], paused_); - } - } + __setActionsPaused(markets_, actions_, paused_); } /** @@ -529,19 +508,30 @@ contract SetterFacet is ISetterFacet, FacetBase { emit NewVAIVaultInfo(vault_, releaseStartBlock_, minReleaseAmount_); } + /** + * @notice Alias to _setPrimeToken to support the Isolated Lending Comptroller Interface + * @param _prime The new prime token contract to be set + */ + function setPrimeToken(IPrime _prime) external returns (uint256) { + return __setPrimeToken(_prime); + } + /** * @notice Sets the prime token contract for the comptroller + * @param _prime The new prime token contract to be set * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) */ - function _setPrimeToken(IPrime _prime) external returns (uint) { - ensureAdmin(); - ensureNonzeroAddress(address(_prime)); - - IPrime oldPrime = prime; - prime = _prime; - emit NewPrimeToken(oldPrime, _prime); + function _setPrimeToken(IPrime _prime) external returns (uint256) { + return __setPrimeToken(_prime); + } - return uint(Error.NO_ERROR); + /** + * @notice Alias to _setForcedLiquidation to support the Isolated Lending Comptroller Interface + * @param vTokenBorrowed Borrowed vToken + * @param enable Whether to enable forced liquidations + */ + function setForcedLiquidation(address vTokenBorrowed, bool enable) external { + __setForcedLiquidation(vTokenBorrowed, enable); } /** @notice Enables forced liquidations for a market. If forced liquidation is enabled, @@ -550,13 +540,8 @@ contract SetterFacet is ISetterFacet, FacetBase { * @param vTokenBorrowed Borrowed vToken * @param enable Whether to enable forced liquidations */ - function _setForcedLiquidation(address vTokenBorrowed, bool enable) external { - ensureAllowed("_setForcedLiquidation(address,bool)"); - if (vTokenBorrowed != address(vaiController)) { - ensureListed(markets[vTokenBorrowed]); - } - isForcedLiquidationEnabled[vTokenBorrowed] = enable; - emit IsForcedLiquidationEnabledUpdated(vTokenBorrowed, enable); + function _setForcedLiquidation(address vTokenBorrowed, bool enable) external { + __setForcedLiquidation(vTokenBorrowed, enable); } /** @@ -603,4 +588,164 @@ contract SetterFacet is ISetterFacet, FacetBase { emit NewXVSVToken(xvsVToken, xvsVToken_); xvsVToken = xvsVToken_; } + + function __setPriceOracle( + PriceOracle newOracle + ) internal compareAddress(address(oracle), address(newOracle)) returns (uint256) { + // Check caller is admin + ensureAdmin(); + ensureNonzeroAddress(address(newOracle)); + + // Track the old oracle for the comptroller + PriceOracle oldOracle = oracle; + + // Set comptroller's oracle to newOracle + oracle = newOracle; + + // Emit NewPriceOracle(oldOracle, newOracle) + emit NewPriceOracle(oldOracle, newOracle); + + return uint256(Error.NO_ERROR); + } + + function __setCloseFactor( + uint256 newCloseFactorMantissa + ) internal compareValue(closeFactorMantissa, newCloseFactorMantissa) returns (uint256) { + // Check caller is admin + ensureAdmin(); + + Exp memory newCloseFactorExp = Exp({ mantissa: newCloseFactorMantissa }); + + //-- Check close factor <= 0.9 + Exp memory highLimit = Exp({ mantissa: closeFactorMaxMantissa }); + //-- Check close factor >= 0.05 + Exp memory lowLimit = Exp({ mantissa: closeFactorMinMantissa }); + + if (lessThanExp(highLimit, newCloseFactorExp) || greaterThanExp(lowLimit, newCloseFactorExp)) { + return fail(Error.INVALID_CLOSE_FACTOR, FailureInfo.SET_CLOSE_FACTOR_VALIDATION); + } + + uint256 oldCloseFactorMantissa = closeFactorMantissa; + closeFactorMantissa = newCloseFactorMantissa; + emit NewCloseFactor(oldCloseFactorMantissa, newCloseFactorMantissa); + + return uint256(Error.NO_ERROR); + } + + function __setCollateralFactor( + VToken vToken, + uint256 newCollateralFactorMantissa + ) + internal + compareValue(markets[address(vToken)].collateralFactorMantissa, newCollateralFactorMantissa) + returns (uint256) + { + // Check caller is allowed by access control manager + ensureAllowed("_setCollateralFactor(address,uint256)"); + ensureNonzeroAddress(address(vToken)); + + // Verify market is listed + Market storage market = markets[address(vToken)]; + ensureListed(market); + + Exp memory newCollateralFactorExp = Exp({ mantissa: newCollateralFactorMantissa }); + + //-- Check collateral factor <= 0.9 + Exp memory highLimit = Exp({ mantissa: collateralFactorMaxMantissa }); + if (lessThanExp(highLimit, newCollateralFactorExp)) { + return fail(Error.INVALID_COLLATERAL_FACTOR, FailureInfo.SET_COLLATERAL_FACTOR_VALIDATION); + } + + // If collateral factor != 0, fail if price == 0 + if (newCollateralFactorMantissa != 0 && oracle.getUnderlyingPrice(vToken) == 0) { + return fail(Error.PRICE_ERROR, FailureInfo.SET_COLLATERAL_FACTOR_WITHOUT_PRICE); + } + + // Set market's collateral factor to new collateral factor, remember old value + uint256 oldCollateralFactorMantissa = market.collateralFactorMantissa; + market.collateralFactorMantissa = newCollateralFactorMantissa; + + // Emit event with asset, old collateral factor, and new collateral factor + emit NewCollateralFactor(vToken, oldCollateralFactorMantissa, newCollateralFactorMantissa); + + return uint256(Error.NO_ERROR); + } + + function __setLiquidationIncentive( + uint256 newLiquidationIncentiveMantissa + ) internal compareValue(liquidationIncentiveMantissa, newLiquidationIncentiveMantissa) returns (uint256) { + ensureAllowed("_setLiquidationIncentive(uint256)"); + + require(newLiquidationIncentiveMantissa >= 1e18, "incentive < 1e18"); + + // Save current value for use in log + uint256 oldLiquidationIncentiveMantissa = liquidationIncentiveMantissa; + // Set liquidation incentive to new incentive + liquidationIncentiveMantissa = newLiquidationIncentiveMantissa; + + // Emit event with old incentive, new incentive + emit NewLiquidationIncentive(oldLiquidationIncentiveMantissa, newLiquidationIncentiveMantissa); + + return uint256(Error.NO_ERROR); + } + + function __setMarketBorrowCaps(VToken[] memory vTokens, uint256[] memory newBorrowCaps) internal { + ensureAllowed("_setMarketBorrowCaps(address[],uint256[])"); + + uint256 numMarkets = vTokens.length; + uint256 numBorrowCaps = newBorrowCaps.length; + + require(numMarkets != 0 && numMarkets == numBorrowCaps, "invalid input"); + + for (uint256 i; i < numMarkets; ++i) { + borrowCaps[address(vTokens[i])] = newBorrowCaps[i]; + emit NewBorrowCap(vTokens[i], newBorrowCaps[i]); + } + } + + function __setMarketSupplyCaps(VToken[] memory vTokens, uint256[] memory newSupplyCaps) internal { + ensureAllowed("_setMarketSupplyCaps(address[],uint256[])"); + + uint256 numMarkets = vTokens.length; + uint256 numSupplyCaps = newSupplyCaps.length; + + require(numMarkets != 0 && numMarkets == numSupplyCaps, "invalid input"); + + for (uint256 i; i < numMarkets; ++i) { + supplyCaps[address(vTokens[i])] = newSupplyCaps[i]; + emit NewSupplyCap(vTokens[i], newSupplyCaps[i]); + } + } + + function __setPrimeToken(IPrime _prime) internal returns (uint) { + ensureAdmin(); + ensureNonzeroAddress(address(_prime)); + + IPrime oldPrime = prime; + prime = _prime; + emit NewPrimeToken(oldPrime, _prime); + + return uint(Error.NO_ERROR); + } + + function __setForcedLiquidation(address vTokenBorrowed, bool enable) internal { + ensureAllowed("_setForcedLiquidation(address,bool)"); + if (vTokenBorrowed != address(vaiController)) { + ensureListed(markets[vTokenBorrowed]); + } + isForcedLiquidationEnabled[vTokenBorrowed] = enable; + emit IsForcedLiquidationEnabledUpdated(vTokenBorrowed, enable); + } + + function __setActionsPaused(address[] memory markets_, Action[] memory actions_, bool paused_) internal { + ensureAllowed("_setActionsPaused(address[],uint8[],bool)"); + + uint256 numMarkets = markets_.length; + uint256 numActions = actions_.length; + for (uint256 marketIdx; marketIdx < numMarkets; ++marketIdx) { + for (uint256 actionIdx; actionIdx < numActions; ++actionIdx) { + setActionPausedInternal(markets_[marketIdx], actions_[actionIdx], paused_); + } + } + } } diff --git a/contracts/Comptroller/Diamond/interfaces/IMarketFacet.sol b/contracts/Comptroller/Diamond/interfaces/IMarketFacet.sol index 5a5496896..98dbc885d 100644 --- a/contracts/Comptroller/Diamond/interfaces/IMarketFacet.sol +++ b/contracts/Comptroller/Diamond/interfaces/IMarketFacet.sol @@ -26,6 +26,10 @@ interface IMarketFacet { function _supportMarket(VToken vToken) external returns (uint256); + function supportMarket(VToken vToken) external returns (uint256); + + function isMarketListed(VToken vToken) external view returns (bool); + function getAssetsIn(address account) external view returns (VToken[] memory); function getAllMarkets() external view returns (VToken[] memory); diff --git a/contracts/Comptroller/Diamond/interfaces/IPolicyFacet.sol b/contracts/Comptroller/Diamond/interfaces/IPolicyFacet.sol index 3eac49c57..1afc1089b 100644 --- a/contracts/Comptroller/Diamond/interfaces/IPolicyFacet.sol +++ b/contracts/Comptroller/Diamond/interfaces/IPolicyFacet.sol @@ -88,4 +88,8 @@ interface IPolicyFacet { uint256[] calldata supplySpeeds, uint256[] calldata borrowSpeeds ) external; + + function getBorrowingPower( + address account + ) external view returns (uint256 error, uint256 liquidity, uint256 shortfall); } diff --git a/contracts/Comptroller/Diamond/interfaces/ISetterFacet.sol b/contracts/Comptroller/Diamond/interfaces/ISetterFacet.sol index 0253c8869..08eac7fe8 100644 --- a/contracts/Comptroller/Diamond/interfaces/ISetterFacet.sol +++ b/contracts/Comptroller/Diamond/interfaces/ISetterFacet.sol @@ -10,22 +10,34 @@ import { ComptrollerLensInterface } from "../../../Comptroller/ComptrollerLensIn import { IPrime } from "../../../Tokens/Prime/IPrime.sol"; interface ISetterFacet { + function setPriceOracle(PriceOracle newOracle) external returns (uint256); + function _setPriceOracle(PriceOracle newOracle) external returns (uint256); + function setCloseFactor(uint256 newCloseFactorMantissa) external returns (uint256); + function _setCloseFactor(uint256 newCloseFactorMantissa) external returns (uint256); function _setAccessControl(address newAccessControlAddress) external returns (uint256); + function setCollateralFactor(VToken vToken, uint256 newCollateralFactorMantissa, uint256 newLiquidationThresholdMantissa) external returns (uint256); + function _setCollateralFactor(VToken vToken, uint256 newCollateralFactorMantissa) external returns (uint256); + function setLiquidationIncentive(uint256 newLiquidationIncentiveMantissa) external returns (uint256); + function _setLiquidationIncentive(uint256 newLiquidationIncentiveMantissa) external returns (uint256); function _setLiquidatorContract(address newLiquidatorContract_) external; function _setPauseGuardian(address newPauseGuardian) external returns (uint256); + function setMarketBorrowCaps(VToken[] calldata vTokens, uint256[] calldata newBorrowCaps) external; + function _setMarketBorrowCaps(VToken[] calldata vTokens, uint256[] calldata newBorrowCaps) external; + function setMarketSupplyCaps(VToken[] calldata vTokens, uint256[] calldata newSupplyCaps) external; + function _setMarketSupplyCaps(VToken[] calldata vTokens, uint256[] calldata newSupplyCaps) external; function _setProtocolPaused(bool state) external returns (bool); @@ -56,8 +68,12 @@ interface ISetterFacet { function _setForcedLiquidation(address vToken, bool enable) external; + function setPrimeToken(IPrime _prime) external returns (uint256); + function _setPrimeToken(IPrime _prime) external returns (uint); + function setForcedLiquidation(address vTokenBorrowed, bool enable) external; + function _setForcedLiquidationForUser(address borrower, address vTokenBorrowed, bool enable) external; function _setXVSToken(address xvs_) external; diff --git a/tests/hardhat/Comptroller/Diamond/accessControl.ts b/tests/hardhat/Comptroller/Diamond/accessControl.ts index 2fbe1469e..e9a0f099f 100644 --- a/tests/hardhat/Comptroller/Diamond/accessControl.ts +++ b/tests/hardhat/Comptroller/Diamond/accessControl.ts @@ -116,7 +116,7 @@ describe("Comptroller", () => { ); }); }); - describe("supportMarket", () => { + describe("_supportMarket", () => { it("Should have AccessControl", async () => { await expect(comptroller.connect(user)._supportMarket(ethers.constants.AddressZero)).to.be.revertedWith( "access denied", @@ -124,6 +124,16 @@ describe("Comptroller", () => { expect(accessControl.isAllowedToCall).to.be.calledOnceWith(userAddress, "_supportMarket(address)"); }); }); + + describe("supportMarket", () => { + it("Should have AccessControl", async () => { + await expect(comptroller.connect(user).supportMarket(ethers.constants.AddressZero)).to.be.revertedWith( + "access denied", + ); + expect(accessControl.isAllowedToCall).to.be.calledOnceWith(userAddress, "_supportMarket(address)"); + }); + }); + describe("seizeVenus", () => { it("Should have AccessControl", async () => { await expect( diff --git a/tests/hardhat/Comptroller/Diamond/comptrollerTest.ts b/tests/hardhat/Comptroller/Diamond/comptrollerTest.ts index a5228d7fc..965a8add7 100644 --- a/tests/hardhat/Comptroller/Diamond/comptrollerTest.ts +++ b/tests/hardhat/Comptroller/Diamond/comptrollerTest.ts @@ -2,7 +2,7 @@ import { FakeContract, MockContract, smock } from "@defi-wonderland/smock"; import { loadFixture } from "@nomicfoundation/hardhat-network-helpers"; import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; import chai from "chai"; -import { constants } from "ethers"; +import { BigNumber, constants } from "ethers"; import { ethers } from "hardhat"; import { convertToUnit } from "../../../../helpers/utils"; @@ -36,7 +36,7 @@ async function deploySimpleComptroller(): Promise { const accessControl = await smock.fake("IAccessControlManagerV5"); accessControl.isAllowedToCall.returns(true); const ComptrollerLensFactory = await smock.mock("ComptrollerLens"); - // const ComptrollerFactory = await smock.mock("ComptrollerMock"); + const result = await deployDiamond(""); const unitroller = result.unitroller; const comptroller = await ethers.getContractAt("ComptrollerMock", unitroller.address); @@ -275,6 +275,13 @@ describe("Comptroller", () => { expect(await comptroller.oracle()).to.equal(newOracle.address); }); + it("setPriceOracle is alias for _setPriceOracle", async () => { + await expect(comptroller.setPriceOracle(newOracle.address)) + .to.emit(comptroller, "NewPriceOracle") + .withArgs(oracle.address, newOracle.address); + expect(await comptroller.oracle()).to.equal(newOracle.address); + }); + it("Should revert on same values", async () => { await expect(comptroller._setPriceOracle(oracle.address)).to.be.revertedWith( "old address is same as new address", @@ -402,12 +409,22 @@ describe("Comptroller", () => { .withArgs(vToken.address, "0", half); }); + it("succeeds and sets market using alias", async () => { + await comptroller.supportMarket(vToken.address); + await expect(comptroller._setCollateralFactor(vToken.address, half)) + .emit(comptroller, "NewCollateralFactor") + .withArgs(vToken.address, "0", half); + + expect(await comptroller.isMarketListed(vToken.address)).to.be.true; + }); + it("should revert on same values", async () => { await comptroller._supportMarket(vToken.address); await comptroller._setCollateralFactor(vToken.address, half); await expect(comptroller._setCollateralFactor(vToken.address, half)).to.be.revertedWith( "old value is same as new value", ); + expect(await comptroller.isMarketListed(vToken.address)).to.be.true; }); }); @@ -449,6 +466,14 @@ describe("Comptroller", () => { expect(await comptroller.isForcedLiquidationEnabled(vToken.address)).to.be.false; }); + it("should alias setForcedLiquidation to _setForcedLiquidation", async () => { + await comptroller.setForcedLiquidation(vToken.address, true); + expect(await comptroller.isForcedLiquidationEnabled(vToken.address)).to.be.true; + + await comptroller.setForcedLiquidation(vToken.address, false); + expect(await comptroller.isForcedLiquidationEnabled(vToken.address)).to.be.false; + }); + it("sets forced liquidation for VAI, even though it is not a listed market", async () => { const vaiController = await smock.fake("VAIController"); await comptroller._setVAIController(vaiController.address); @@ -932,6 +957,16 @@ describe("Comptroller", () => { .callStatic.borrowAllowed(vToken.address, root.address, convertToUnit("0.9999", 18)), ).to.be.revertedWith("market borrow cap is 0"); }); + + it("getBorrowingPower is an alias for getAccountLiquidity", async () => { + console.log(vToken.wallet._address); + const accountLiquidity = await comptroller.getAccountLiquidity(vToken.wallet._address); + const borrowingPower = await comptroller.getBorrowingPower(vToken.wallet._address); + + expect(borrowingPower[0]).to.eq(accountLiquidity[0]); + expect(borrowingPower[1]).to.eq(accountLiquidity[1]); + expect(borrowingPower[2]).to.eq(accountLiquidity[2]); + }); }); }); }); diff --git a/tests/hardhat/Fork/diamondTest.ts b/tests/hardhat/Fork/diamondTest.ts index 642326dbc..38a01f09c 100644 --- a/tests/hardhat/Fork/diamondTest.ts +++ b/tests/hardhat/Fork/diamondTest.ts @@ -288,6 +288,14 @@ forking(31873700, () => { expect(await diamondUnitroller.supplyCaps(vBUSD.address)).to.equals(parseUnits(currentSupplyCap, 0)); }); + it("should alias setMarketSupplyCaps to _setMarketSupplyCaps", async () => { + const currentSupplyCap = (await diamondUnitroller.supplyCaps(vBUSD.address)).toString(); + await diamondUnitroller.connect(owner).setMarketSupplyCaps([vBUSD.address], [parseUnits("100000", 18)]); + expect(await diamondUnitroller.supplyCaps(vBUSD.address)).to.equals(parseUnits("100000", 18)); + await diamondUnitroller.connect(owner).setMarketSupplyCaps([vBUSD.address], [currentSupplyCap]); + expect(await diamondUnitroller.supplyCaps(vBUSD.address)).to.equals(currentSupplyCap); + }); + it("setting close factor", async () => { const currentCloseFactor = (await diamondUnitroller.closeFactorMantissa()).toString(); await diamondUnitroller.connect(owner)._setCloseFactor(parseUnits("8", 17)); @@ -296,6 +304,14 @@ forking(31873700, () => { expect(await diamondUnitroller.closeFactorMantissa()).to.equals(parseUnits(currentCloseFactor, 0)); }); + it("should alias setCloseFactor to _setCloseFactor", async () => { + const currentCloseFactor = (await diamondUnitroller.closeFactorMantissa()).toString(); + await diamondUnitroller.connect(owner).setCloseFactor(parseUnits("8", 17)); + expect(await diamondUnitroller.closeFactorMantissa()).to.equals(parseUnits("8", 17)); + await diamondUnitroller.connect(owner).setCloseFactor(parseUnits(currentCloseFactor, 0)); + expect(await diamondUnitroller.closeFactorMantissa()).to.equals(parseUnits(currentCloseFactor, 0)); + }); + it("setting collateral factor", async () => { await diamondUnitroller.connect(owner)._setCollateralFactor(vUSDT.address, 2); market = await diamondUnitroller.markets(vUSDT.address); @@ -306,6 +322,16 @@ forking(31873700, () => { expect(market.collateralFactorMantissa).to.equal(parseUnits("8", 17)); }); + it("should alias setCollateralFactor to _setCollateralFactor", async () => { + await diamondUnitroller.connect(owner).setCollateralFactor(vUSDT.address, 2); + market = await diamondUnitroller.markets(vUSDT.address); + expect(market.collateralFactorMantissa).to.equal(2); + + await diamondUnitroller.connect(owner).setCollateralFactor(vUSDT.address, parseUnits("8", 17)); + market = await diamondUnitroller.markets(vUSDT.address); + expect(market.collateralFactorMantissa).to.equal(parseUnits("8", 17)); + }); + it("setting setting Liquidation Incentive", async () => { await diamondUnitroller.connect(owner)._setLiquidationIncentive(parseUnits("13", 17)); expect(await diamondUnitroller.liquidationIncentiveMantissa()).to.equal(parseUnits("13", 17)); @@ -314,6 +340,14 @@ forking(31873700, () => { expect(await diamondUnitroller.liquidationIncentiveMantissa()).to.equal(parseUnits("11", 17)); }); + it("should alias setLiquidationIncentive to _setLiquidationIncentive", async () => { + await diamondUnitroller.connect(owner).setLiquidationIncentive(parseUnits("13", 17)); + expect(await diamondUnitroller.liquidationIncentiveMantissa()).to.equal(parseUnits("13", 17)); + + await diamondUnitroller.connect(owner).setLiquidationIncentive(parseUnits("11", 17)); + expect(await diamondUnitroller.liquidationIncentiveMantissa()).to.equal(parseUnits("11", 17)); + }); + it("setting Pause Guardian", async () => { const currentPauseGuardia = (await diamondUnitroller.pauseGuardian()).toString(); @@ -333,6 +367,15 @@ forking(31873700, () => { expect(await diamondUnitroller.borrowCaps(vUSDT.address)).to.equal(currentBorrowCap); }); + it("should alias setMarketBorrowCaps to _setMarketBorrowCaps", async () => { + const currentBorrowCap = (await diamondUnitroller.borrowCaps(vUSDT.address)).toString(); + await diamondUnitroller.connect(owner).setMarketBorrowCaps([vUSDT.address], [parseUnits("10000", 18)]); + expect(await diamondUnitroller.borrowCaps(vUSDT.address)).to.equal(parseUnits("10000", 18)); + + await diamondUnitroller.connect(owner).setMarketBorrowCaps([vUSDT.address], [currentBorrowCap]); + expect(await diamondUnitroller.borrowCaps(vUSDT.address)).to.equal(currentBorrowCap); + }); + it("pausing mint action in vUSDT", async () => { const tx = await accessControlManager .connect(owner) @@ -353,6 +396,26 @@ forking(31873700, () => { await expect(vUSDT.connect(busdHolder).mint(10)).to.be.emit(vUSDT, "Transfer"); }); + it("should alias setActionsPaused to _setActionsPaused", async () => { + const tx = await accessControlManager + .connect(owner) + .giveCallPermission(diamondUnitroller.address, "_setActionsPaused(address[],uint8[],bool)", Owner); + await tx.wait(); + + await expect(diamondUnitroller.connect(owner).setActionsPaused([vUSDT.address], [0], true)).to.emit( + diamondUnitroller, + "ActionPausedMarket", + ); + + await expect(vUSDT.connect(usdtHolder).mint(1000)).to.be.revertedWith("action is paused"); + + await expect(diamondUnitroller.connect(owner).setActionsPaused([vUSDT.address], [0], false)).to.emit( + diamondUnitroller, + "ActionPausedMarket", + ); + await expect(vUSDT.connect(busdHolder).mint(10)).to.be.emit(vUSDT, "Transfer"); + }); + it("sets forced liquidation", async () => { const tx = await accessControlManager .connect(owner) diff --git a/tests/hardhat/integration/index.ts b/tests/hardhat/integration/index.ts index 466c2bcdd..b81aff045 100644 --- a/tests/hardhat/integration/index.ts +++ b/tests/hardhat/integration/index.ts @@ -268,9 +268,14 @@ describe("Prime Token", () => { let prime: Prime; let xvsVault: XVSVault; let xvs: XVS; + let comptroller: MockContract; beforeEach(async () => { - ({ prime, xvsVault, xvs } = await loadFixture(deployProtocol)); + ({ prime, xvsVault, xvs, comptroller } = await loadFixture(deployProtocol)); + }); + + it("should alias setPrimeToken to _setPrimeToken", async () => { + await expect(comptroller.setPrimeToken(prime.address)).to.emit(prime, "NewPrimeToken"); }); it("stake and mint", async () => { diff --git a/yarn.lock b/yarn.lock index 04c9ecd0f..e97401b8b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3487,7 +3487,7 @@ __metadata: languageName: node linkType: hard -"@venusprotocol/governance-contracts@npm:^2.3.0, @venusprotocol/governance-contracts@npm:^2.4.0, @venusprotocol/governance-contracts@npm:^2.6.0": +"@venusprotocol/governance-contracts@npm:^2.3.0, @venusprotocol/governance-contracts@npm:^2.4.0": version: 2.7.0 resolution: "@venusprotocol/governance-contracts@npm:2.7.0" dependencies: @@ -3498,6 +3498,17 @@ __metadata: languageName: node linkType: hard +"@venusprotocol/governance-contracts@npm:^2.6.0": + version: 2.6.0 + resolution: "@venusprotocol/governance-contracts@npm:2.6.0" + dependencies: + "@venusprotocol/solidity-utilities": 2.0.0 + hardhat-deploy-ethers: ^0.3.0-beta.13 + module-alias: ^2.2.2 + checksum: 7b4e8034961d02a3ae1f33b9a771e98d1ae39576ae60222433c2d33d184b7f6d8e1368a050dfe1d40f4ce3a0341e6500f00dd6e64c58a35092c942c125247c77 + languageName: node + linkType: hard + "@venusprotocol/isolated-pools@npm:^3.4.0": version: 3.7.0 resolution: "@venusprotocol/isolated-pools@npm:3.7.0"