diff --git a/README.md b/README.md index 60f0f96be..48c51ee43 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # Morpho Core Protocol V1 -[![Test](https://github.com/morpho-labs/morpho-contracts/actions/workflows/ci-foundry.yml/badge.svg)](https://github.com/morpho-dao/morpho-v1/actions/workflows/ci-foundry.yml) +[![Morpho-Compound](https://github.com/morpho-dao/morpho-v1/actions/workflows/ci-foundry-compound.yml/badge.svg)](https://github.com/morpho-dao/morpho-v1/actions/workflows/ci-foundry-compound.yml) +[![Morpho-AaveV2](https://github.com/morpho-dao/morpho-v1/actions/workflows/ci-foundry-aave-v2.yml/badge.svg)](https://github.com/morpho-dao/morpho-v1/actions/workflows/ci-foundry-aave-v2.yml) +[![Morpho-AaveV3](https://github.com/morpho-dao/morpho-v1/actions/workflows/ci-foundry-aave-v3.yml/badge.svg)](https://github.com/morpho-dao/morpho-v1/actions/workflows/ci-foundry-aave-v3.yml) diff --git a/config/eth-mainnet/aave-v2/Config.sol b/config/eth-mainnet/aave-v2/Config.sol index 0253a9c55..834258636 100644 --- a/config/eth-mainnet/aave-v2/Config.sol +++ b/config/eth-mainnet/aave-v2/Config.sol @@ -49,7 +49,7 @@ contract Config is BaseConfig { ProxyAdmin public proxyAdmin = ProxyAdmin(0x99917ca0426fbC677e84f873Fb0b726Bb4799cD8); TransparentUpgradeableProxy public lensProxy = - TransparentUpgradeableProxy(payable(0x8706256509684E9cD93B7F19254775CE9324c226)); + TransparentUpgradeableProxy(payable(0x507fA343d0A90786d86C7cd885f5C49263A91FF4)); TransparentUpgradeableProxy public morphoProxy = TransparentUpgradeableProxy(payable(0x777777c9898D384F785Ee44Acfe945efDFf5f3E0)); TransparentUpgradeableProxy public rewardsManagerProxy; diff --git a/contracts/aave-v2/EntryPositionsManager.sol b/contracts/aave-v2/EntryPositionsManager.sol index 3d9723eee..222377708 100644 --- a/contracts/aave-v2/EntryPositionsManager.sol +++ b/contracts/aave-v2/EntryPositionsManager.sol @@ -14,7 +14,6 @@ contract EntryPositionsManager is IEntryPositionsManager, PositionsManagerUtils using HeapOrdering for HeapOrdering.HeapArray; using PercentageMath for uint256; using SafeTransferLib for ERC20; - using MarketLib for Types.Market; using WadRayMath for uint256; using Math for uint256; @@ -58,12 +57,6 @@ contract EntryPositionsManager is IEntryPositionsManager, PositionsManagerUtils /// @notice Thrown when the user does not have enough collateral for the borrow. error UnauthorisedBorrow(); - /// @notice Thrown when the supply is paused. - error SupplyPaused(); - - /// @notice Thrown when the borrow is paused. - error BorrowPaused(); - /// STRUCTS /// // Struct to avoid stack too deep. @@ -98,9 +91,6 @@ contract EntryPositionsManager is IEntryPositionsManager, PositionsManagerUtils ) external { if (_onBehalf == address(0)) revert AddressIsZero(); if (_amount == 0) revert AmountIsZero(); - Types.Market memory market = market[_poolToken]; - if (!market.isCreatedMemory()) revert MarketNotCreated(); - if (market.isSupplyPaused) revert SupplyPaused(); _updateIndexes(_poolToken); SupplyVars memory vars; @@ -108,7 +98,7 @@ contract EntryPositionsManager is IEntryPositionsManager, PositionsManagerUtils if (!_isSupplying(userMarkets[_onBehalf], vars.borrowMask)) _setSupplying(_onBehalf, vars.borrowMask, true); - ERC20 underlyingToken = ERC20(market.underlyingToken); + ERC20 underlyingToken = ERC20(market[_poolToken].underlyingToken); underlyingToken.safeTransferFrom(_from, address(this), _amount); Types.Delta storage delta = deltas[_poolToken]; @@ -135,7 +125,7 @@ contract EntryPositionsManager is IEntryPositionsManager, PositionsManagerUtils // Promote pool borrowers. if ( vars.remainingToSupply > 0 && - !market.isP2PDisabled && + !market[_poolToken].isP2PDisabled && borrowersOnPool[_poolToken].getHead() != address(0) ) { (uint256 matched, ) = _matchBorrowers( @@ -195,16 +185,16 @@ contract EntryPositionsManager is IEntryPositionsManager, PositionsManagerUtils uint256 _maxGasForMatching ) external { if (_amount == 0) revert AmountIsZero(); - Types.Market memory market = market[_poolToken]; - if (!market.isCreatedMemory()) revert MarketNotCreated(); - if (market.isBorrowPaused) revert BorrowPaused(); - ERC20 underlyingToken = ERC20(market.underlyingToken); + ERC20 underlyingToken = ERC20(market[_poolToken].underlyingToken); if (!pool.getConfiguration(address(underlyingToken)).getBorrowingEnabled()) revert BorrowingNotEnabled(); _updateIndexes(_poolToken); - _setBorrowing(msg.sender, borrowMask[_poolToken], true); + + bytes32 borrowMask = borrowMask[_poolToken]; + if (!_isBorrowing(userMarkets[msg.sender], borrowMask)) + _setBorrowing(msg.sender, borrowMask, true); if (!_borrowAllowed(msg.sender, _poolToken, _amount)) revert UnauthorisedBorrow(); @@ -233,7 +223,7 @@ contract EntryPositionsManager is IEntryPositionsManager, PositionsManagerUtils // Promote pool suppliers. if ( remainingToBorrow > 0 && - !market.isP2PDisabled && + !market[_poolToken].isP2PDisabled && suppliersOnPool[_poolToken].getHead() != address(0) ) { (uint256 matched, ) = _matchSuppliers( diff --git a/contracts/aave-v2/ExitPositionsManager.sol b/contracts/aave-v2/ExitPositionsManager.sol index e889a124d..2aaf21504 100644 --- a/contracts/aave-v2/ExitPositionsManager.sol +++ b/contracts/aave-v2/ExitPositionsManager.sol @@ -14,7 +14,6 @@ contract ExitPositionsManager is IExitPositionsManager, PositionsManagerUtils { using HeapOrdering for HeapOrdering.HeapArray; using PercentageMath for uint256; using SafeTransferLib for ERC20; - using MarketLib for Types.Market; using WadRayMath for uint256; using Math for uint256; @@ -68,11 +67,6 @@ contract ExitPositionsManager is IExitPositionsManager, PositionsManagerUtils { uint256 _amountSeized ); - /// @notice Emitted when the peer-to-peer deltas are increased by the governance. - /// @param _poolToken The address of the market on which the deltas were increased. - /// @param _amount The amount that has been added to the deltas (in underlying). - event P2PDeltasIncreased(address indexed _poolToken, uint256 _amount); - /// ERRORS /// /// @notice Thrown when user is not a member of the market. @@ -84,18 +78,6 @@ contract ExitPositionsManager is IExitPositionsManager, PositionsManagerUtils { /// @notice Thrown when the positions of the user is not liquidatable. error UnauthorisedLiquidate(); - /// @notice Thrown when the withdraw is paused. - error WithdrawPaused(); - - /// @notice Thrown when the repay is paused. - error RepayPaused(); - - /// @notice Thrown when the liquidation on this asset as collateral is paused. - error LiquidateCollateralPaused(); - - /// @notice Thrown when the liquidation on this asset as debt is paused. - error LiquidateBorrowPaused(); - /// STRUCTS /// // Struct to avoid stack too deep. @@ -128,8 +110,6 @@ contract ExitPositionsManager is IExitPositionsManager, PositionsManagerUtils { uint256 collateralTokenUnit; // The collateral token unit considering its decimals. uint256 borrowedReserveDecimals; // The number of decimals of the borrowed asset in the reserve. uint256 borrowedTokenUnit; // The unit of borrowed token considering its decimals. - uint256 closeFactor; // The close factor used during the liquidation. - bool liquidationAllowed; // Whether the liquidation is allowed or not. } // Struct to avoid stack too deep. @@ -156,9 +136,6 @@ contract ExitPositionsManager is IExitPositionsManager, PositionsManagerUtils { ) external { if (_amount == 0) revert AmountIsZero(); if (_receiver == address(0)) revert AddressIsZero(); - Types.Market memory market = market[_poolToken]; - if (!market.isCreatedMemory()) revert MarketNotCreated(); - if (market.isWithdrawPaused) revert WithdrawPaused(); _updateIndexes(_poolToken); uint256 toWithdraw = Math.min(_getUserSupplyBalanceInOf(_poolToken, _supplier), _amount); @@ -183,9 +160,6 @@ contract ExitPositionsManager is IExitPositionsManager, PositionsManagerUtils { uint256 _maxGasForMatching ) external { if (_amount == 0) revert AmountIsZero(); - Types.Market memory market = market[_poolToken]; - if (!market.isCreatedMemory()) revert MarketNotCreated(); - if (market.isRepayPaused) revert RepayPaused(); _updateIndexes(_poolToken); uint256 toRepay = Math.min(_getUserBorrowBalanceInOf(_poolToken, _onBehalf), _amount); @@ -205,13 +179,6 @@ contract ExitPositionsManager is IExitPositionsManager, PositionsManagerUtils { address _borrower, uint256 _amount ) external { - Types.Market memory collateralMarket = market[_poolTokenCollateral]; - if (!collateralMarket.isCreatedMemory()) revert MarketNotCreated(); - if (collateralMarket.isLiquidateCollateralPaused) revert LiquidateCollateralPaused(); - Types.Market memory borrowedMarket = market[_poolTokenBorrowed]; - if (!borrowedMarket.isCreatedMemory()) revert MarketNotCreated(); - if (borrowedMarket.isLiquidateBorrowPaused) revert LiquidateBorrowPaused(); - if ( !_isBorrowingAndSupplying( userMarkets[_borrower], @@ -223,22 +190,22 @@ contract ExitPositionsManager is IExitPositionsManager, PositionsManagerUtils { _updateIndexes(_poolTokenBorrowed); _updateIndexes(_poolTokenCollateral); - LiquidateVars memory vars; - (vars.closeFactor, vars.liquidationAllowed) = _liquidationAllowed( - _borrower, - borrowedMarket.isDeprecated - ); - if (!vars.liquidationAllowed) revert UnauthorisedLiquidate(); + if (!_liquidationAllowed(_borrower)) revert UnauthorisedLiquidate(); + + address tokenBorrowedAddress = market[_poolTokenBorrowed].underlyingToken; uint256 amountToLiquidate = Math.min( _amount, - _getUserBorrowBalanceInOf(_poolTokenBorrowed, _borrower).percentMul(vars.closeFactor) // Max liquidatable debt. + _getUserBorrowBalanceInOf(_poolTokenBorrowed, _borrower).percentMul( + DEFAULT_LIQUIDATION_CLOSE_FACTOR + ) // Max liquidatable debt. ); + address tokenCollateralAddress = market[_poolTokenCollateral].underlyingToken; + IPriceOracleGetter oracle = IPriceOracleGetter(addressesProvider.getPriceOracle()); - address tokenCollateralAddress = market[_poolTokenCollateral].underlyingToken; - address tokenBorrowedAddress = market[_poolTokenBorrowed].underlyingToken; + LiquidateVars memory vars; { ILendingPool poolMem = pool; (, , vars.liquidationBonus, vars.collateralReserveDecimals, ) = poolMem @@ -283,46 +250,6 @@ contract ExitPositionsManager is IExitPositionsManager, PositionsManagerUtils { ); } - /// @notice Implements increaseP2PDeltas logic. - /// @dev The current Morpho supply on the pool might not be enough to borrow `_amount` before resupplying it. - /// In this case, consider calling this function multiple times. - /// @param _poolToken The address of the market on which to create deltas. - /// @param _amount The amount to add to the deltas (in underlying). - function increaseP2PDeltasLogic(address _poolToken, uint256 _amount) external { - _updateIndexes(_poolToken); - - Types.Delta storage deltas = deltas[_poolToken]; - Types.Delta memory deltasMem = deltas; - Types.PoolIndexes memory poolIndexes = poolIndexes[_poolToken]; - uint256 p2pSupplyIndex = p2pSupplyIndex[_poolToken]; - uint256 p2pBorrowIndex = p2pBorrowIndex[_poolToken]; - - _amount = Math.min( - _amount, - Math.min( - deltasMem.p2pSupplyAmount.rayMul(p2pSupplyIndex).zeroFloorSub( - deltasMem.p2pSupplyDelta.rayMul(poolIndexes.poolSupplyIndex) - ), - deltasMem.p2pBorrowAmount.rayMul(p2pBorrowIndex).zeroFloorSub( - deltasMem.p2pBorrowDelta.rayMul(poolIndexes.poolBorrowIndex) - ) - ) - ); - - deltasMem.p2pSupplyDelta += _amount.rayDiv(poolIndexes.poolSupplyIndex); - deltas.p2pSupplyDelta = deltasMem.p2pSupplyDelta; - deltasMem.p2pBorrowDelta += _amount.rayDiv(poolIndexes.poolBorrowIndex); - deltas.p2pBorrowDelta = deltasMem.p2pBorrowDelta; - emit P2PSupplyDeltaUpdated(_poolToken, deltasMem.p2pSupplyDelta); - emit P2PBorrowDeltaUpdated(_poolToken, deltasMem.p2pBorrowDelta); - - ERC20 underlyingToken = ERC20(market[_poolToken].underlyingToken); - _borrowFromPool(underlyingToken, _amount); - _supplyToPool(underlyingToken, _amount); - - emit P2PDeltasIncreased(_poolToken, _amount); - } - /// INTERNAL /// /// @dev Implements withdraw logic without security checks. @@ -571,9 +498,8 @@ contract ExitPositionsManager is IExitPositionsManager, PositionsManagerUtils { // No need to subtract p2pBorrowDelta as it is zero. vars.feeToRepay = Math.zeroFloorSub( delta.p2pBorrowAmount.rayMul(vars.p2pBorrowIndex), - delta.p2pSupplyAmount.rayMul(vars.p2pSupplyIndex).zeroFloorSub( - delta.p2pSupplyDelta.rayMul(vars.poolSupplyIndex) - ) + (delta.p2pSupplyAmount.rayMul(vars.p2pSupplyIndex) - + delta.p2pSupplyDelta.rayMul(vars.poolSupplyIndex)) ); if (vars.feeToRepay > 0) { @@ -691,21 +617,8 @@ contract ExitPositionsManager is IExitPositionsManager, PositionsManagerUtils { /// @dev Checks if the user is liquidatable. /// @param _user The user to check. - /// @param _isDeprecated Whether the market is deprecated or not. - /// @return closeFactor The close factor to apply. - /// @return liquidationAllowed Whether the liquidation is allowed or not. - function _liquidationAllowed(address _user, bool _isDeprecated) - internal - returns (uint256 closeFactor, bool liquidationAllowed) - { - if (_isDeprecated) { - // Allow liquidation of the whole debt. - closeFactor = MAX_BASIS_POINTS; - liquidationAllowed = true; - } else { - closeFactor = DEFAULT_LIQUIDATION_CLOSE_FACTOR; - liquidationAllowed = (_getUserHealthFactor(_user, address(0), 0) < - HEALTH_FACTOR_LIQUIDATION_THRESHOLD); - } + /// @return Whether the user is liquidatable or not. + function _liquidationAllowed(address _user) internal returns (bool) { + return _getUserHealthFactor(_user, address(0), 0) < HEALTH_FACTOR_LIQUIDATION_THRESHOLD; } } diff --git a/contracts/aave-v2/InterestRatesManager.sol b/contracts/aave-v2/InterestRatesManager.sol index 6ec2cc602..2cd6a8219 100644 --- a/contracts/aave-v2/InterestRatesManager.sol +++ b/contracts/aave-v2/InterestRatesManager.sol @@ -19,8 +19,6 @@ contract InterestRatesManager is IInterestRatesManager, MorphoStorage { using PercentageMath for uint256; using WadRayMath for uint256; - /// STORAGE /// - address public constant ST_ETH = 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84; uint256 public immutable ST_ETH_BASE_REBASE_INDEX; @@ -149,9 +147,8 @@ contract InterestRatesManager is IInterestRatesManager, MorphoStorage { p2pGrowthFactor + _params.reserveFactor.percentMul(poolBorrowGrowthFactor - p2pGrowthFactor); } else { - // The case poolSupplyGrowthFactor > poolBorrowGrowthFactor happens because someone has done a flashloan on Aave, or the interests - // generated by the stable rate borrowing are high (making the supply rate higher than the variable borrow rate): the peer-to-peer - // growth factors are set to the pool borrow growth factor. + // The case poolSupplyGrowthFactor > poolBorrowGrowthFactor happens because someone has done a flashloan on Aave: + // the peer-to-peer growth factors are set to the pool borrow growth factor. p2pSupplyGrowthFactor = poolBorrowGrowthFactor; p2pBorrowGrowthFactor = poolBorrowGrowthFactor; } diff --git a/contracts/aave-v2/Morpho.sol b/contracts/aave-v2/Morpho.sol index a6ef14559..09227f083 100644 --- a/contracts/aave-v2/Morpho.sol +++ b/contracts/aave-v2/Morpho.sol @@ -31,7 +31,11 @@ contract Morpho is MorphoGovernance { /// @dev `msg.sender` must have approved Morpho's contract to spend the underlying `_amount`. /// @param _poolToken The address of the market the user wants to interact with. /// @param _amount The amount of token (in underlying) to supply. - function supply(address _poolToken, uint256 _amount) external nonReentrant { + function supply(address _poolToken, uint256 _amount) + external + nonReentrant + isMarketCreatedAndNotPausedNorPartiallyPaused(_poolToken) + { _supply(_poolToken, msg.sender, _amount, defaultMaxGasForMatching.supply); } @@ -44,7 +48,7 @@ contract Morpho is MorphoGovernance { address _poolToken, address _onBehalf, uint256 _amount - ) external nonReentrant { + ) external nonReentrant isMarketCreatedAndNotPausedNorPartiallyPaused(_poolToken) { _supply(_poolToken, _onBehalf, _amount, defaultMaxGasForMatching.supply); } @@ -60,14 +64,18 @@ contract Morpho is MorphoGovernance { address _onBehalf, uint256 _amount, uint256 _maxGasForMatching - ) external nonReentrant { + ) external nonReentrant isMarketCreatedAndNotPausedNorPartiallyPaused(_poolToken) { _supply(_poolToken, _onBehalf, _amount, _maxGasForMatching); } /// @notice Borrows underlying tokens from a specific market. /// @param _poolToken The address of the market the user wants to interact with. /// @param _amount The amount of token (in underlying). - function borrow(address _poolToken, uint256 _amount) external nonReentrant { + function borrow(address _poolToken, uint256 _amount) + external + nonReentrant + isMarketCreatedAndNotPausedNorPartiallyPaused(_poolToken) + { _borrow(_poolToken, _amount, defaultMaxGasForMatching.borrow); } @@ -79,14 +87,18 @@ contract Morpho is MorphoGovernance { address _poolToken, uint256 _amount, uint256 _maxGasForMatching - ) external nonReentrant { + ) external nonReentrant isMarketCreatedAndNotPausedNorPartiallyPaused(_poolToken) { _borrow(_poolToken, _amount, _maxGasForMatching); } /// @notice Withdraws underlying tokens from a specific market. /// @param _poolToken The address of the market the user wants to interact with. /// @param _amount The amount of tokens (in underlying) to withdraw from supply. - function withdraw(address _poolToken, uint256 _amount) external nonReentrant { + function withdraw(address _poolToken, uint256 _amount) + external + nonReentrant + isMarketCreatedAndNotPaused(_poolToken) + { _withdraw(_poolToken, _amount, msg.sender, defaultMaxGasForMatching.withdraw); } @@ -98,7 +110,7 @@ contract Morpho is MorphoGovernance { address _poolToken, uint256 _amount, address _receiver - ) external nonReentrant { + ) external nonReentrant isMarketCreatedAndNotPaused(_poolToken) { _withdraw(_poolToken, _amount, _receiver, defaultMaxGasForMatching.withdraw); } @@ -106,7 +118,11 @@ contract Morpho is MorphoGovernance { /// @dev `msg.sender` must have approved Morpho's contract to spend the underlying `_amount`. /// @param _poolToken The address of the market the user wants to interact with. /// @param _amount The amount of token (in underlying) to repay from borrow. - function repay(address _poolToken, uint256 _amount) external nonReentrant { + function repay(address _poolToken, uint256 _amount) + external + nonReentrant + isMarketCreatedAndNotPaused(_poolToken) + { _repay(_poolToken, msg.sender, _amount, defaultMaxGasForMatching.repay); } @@ -119,7 +135,7 @@ contract Morpho is MorphoGovernance { address _poolToken, address _onBehalf, uint256 _amount - ) external nonReentrant { + ) external nonReentrant isMarketCreatedAndNotPaused(_poolToken) { _repay(_poolToken, _onBehalf, _amount, defaultMaxGasForMatching.repay); } @@ -133,7 +149,12 @@ contract Morpho is MorphoGovernance { address _poolTokenCollateral, address _borrower, uint256 _amount - ) external nonReentrant { + ) + external + nonReentrant + isMarketCreatedAndNotPaused(_poolTokenBorrowed) + isMarketCreatedAndNotPaused(_poolTokenCollateral) + { address(exitPositionsManager).functionDelegateCall( abi.encodeWithSelector( IExitPositionsManager.liquidateLogic.selector, diff --git a/contracts/aave-v2/MorphoGovernance.sol b/contracts/aave-v2/MorphoGovernance.sol index bec5c0384..b78442f12 100644 --- a/contracts/aave-v2/MorphoGovernance.sol +++ b/contracts/aave-v2/MorphoGovernance.sol @@ -13,8 +13,6 @@ abstract contract MorphoGovernance is MorphoUtils { using ReserveConfiguration for DataTypes.ReserveConfigurationMap; using PercentageMath for uint256; using SafeTransferLib for ERC20; - using DelegateCall for address; - using MarketLib for Types.Market; using WadRayMath for uint256; /// EVENTS /// @@ -73,46 +71,21 @@ abstract contract MorphoGovernance is MorphoUtils { /// @notice Emitted when the value of `isP2PDisabled` is set. /// @param _poolToken The address of the concerned market. /// @param _isP2PDisabled The new value of `_isP2PDisabled` adopted. - event IsP2PDisabledSet(address indexed _poolToken, bool _isP2PDisabled); + event P2PStatusSet(address indexed _poolToken, bool _isP2PDisabled); - /// @notice Emitted when a supply is paused or unpaused. + /// @notice Emitted when a market is paused or unpaused. /// @param _poolToken The address of the concerned market. - /// @param _isPaused The new pause status of the market. - event IsSupplyPausedSet(address indexed _poolToken, bool _isPaused); + /// @param _newStatus The new pause status of the market. + event PauseStatusSet(address indexed _poolToken, bool _newStatus); - /// @notice Emitted when a borrow is paused or unpaused. + /// @notice Emitted when a market is partially paused or unpaused. /// @param _poolToken The address of the concerned market. - /// @param _isPaused The new pause status of the market. - event IsBorrowPausedSet(address indexed _poolToken, bool _isPaused); - - /// @notice Emitted when a withdraw is paused or unpaused. - /// @param _poolToken The address of the concerned market. - /// @param _isPaused The new pause status of the market. - event IsWithdrawPausedSet(address indexed _poolToken, bool _isPaused); - - /// @notice Emitted when a repay is paused or unpaused. - /// @param _poolToken The address of the concerned market. - /// @param _isPaused The new pause status of the market. - event IsRepayPausedSet(address indexed _poolToken, bool _isPaused); - - /// @notice Emitted when a liquidate as collateral is paused or unpaused. - /// @param _poolToken The address of the concerned market. - /// @param _isPaused The new pause status of the market. - event IsLiquidateCollateralPausedSet(address indexed _poolToken, bool _isPaused); - - /// @notice Emitted when a liquidate as borrow is paused or unpaused. - /// @param _poolToken The address of the concerned market. - /// @param _isPaused The new pause status of the market. - event IsLiquidateBorrowPausedSet(address indexed _poolToken, bool _isPaused); - - /// @notice Emitted when a market is set as deprecated or not. - /// @param _poolToken The address of the concerned market. - /// @param _isDeprecated The new deprecated status. - event DeprecatedStatusSet(address indexed _poolToken, bool _isDeprecated); + /// @param _newStatus The new partial pause status of the market. + event PartialPauseStatusSet(address indexed _poolToken, bool _newStatus); /// @notice Emitted when claiming rewards is paused or unpaused. - /// @param _isPaused The new claiming rewards status. - event IsClaimRewardsPausedSet(bool _isPaused); + /// @param _newStatus The new claiming rewards status. + event ClaimRewardsPauseStatusSet(bool _newStatus); /// @notice Emitted when a new market is created. /// @param _poolToken The address of the market that has been created. @@ -283,87 +256,16 @@ abstract contract MorphoGovernance is MorphoUtils { emit P2PIndexCursorSet(_poolToken, _p2pIndexCursor); } - /// @notice Sets `isSupplyPaused` for a given market. - /// @param _poolToken The address of the market to update. - /// @param _isPaused The new pause status, true to pause the mechanism. - function setIsSupplyPaused(address _poolToken, bool _isPaused) - external - onlyOwner - isMarketCreated(_poolToken) - { - market[_poolToken].isSupplyPaused = _isPaused; - emit IsSupplyPausedSet(_poolToken, _isPaused); - } - - /// @notice Sets `isBorrowPaused` for a given market. - /// @param _poolToken The address of the market to update. - /// @param _isPaused The new pause status, true to pause the mechanism. - function setIsBorrowPaused(address _poolToken, bool _isPaused) - external - onlyOwner - isMarketCreated(_poolToken) - { - market[_poolToken].isBorrowPaused = _isPaused; - emit IsBorrowPausedSet(_poolToken, _isPaused); - } - - /// @notice Sets `isWithdrawPaused` for a given market. - /// @param _poolToken The address of the market to update. - /// @param _isPaused The new pause status, true to pause the mechanism. - function setIsWithdrawPaused(address _poolToken, bool _isPaused) - external - onlyOwner - isMarketCreated(_poolToken) - { - market[_poolToken].isWithdrawPaused = _isPaused; - emit IsWithdrawPausedSet(_poolToken, _isPaused); - } - - /// @notice Sets `isRepayPaused` for a given market. - /// @param _poolToken The address of the market to update. - /// @param _isPaused The new pause status, true to pause the mechanism. - function setIsRepayPaused(address _poolToken, bool _isPaused) - external - onlyOwner - isMarketCreated(_poolToken) - { - market[_poolToken].isRepayPaused = _isPaused; - emit IsRepayPausedSet(_poolToken, _isPaused); - } - - /// @notice Sets `isLiquidateCollateralPaused` for a given market. - /// @param _poolToken The address of the market to update. - /// @param _isPaused The new pause status, true to pause the mechanism. - function setIsLiquidateCollateralPaused(address _poolToken, bool _isPaused) - external - onlyOwner - isMarketCreated(_poolToken) - { - market[_poolToken].isLiquidateCollateralPaused = _isPaused; - emit IsLiquidateCollateralPausedSet(_poolToken, _isPaused); - } - - /// @notice Sets `isLiquidateBorrowPaused` for a given market. - /// @param _poolToken The address of the market to update. - /// @param _isPaused The new pause status, true to pause the mechanism. - function setIsLiquidateBorrowPaused(address _poolToken, bool _isPaused) - external - onlyOwner - isMarketCreated(_poolToken) - { - market[_poolToken].isLiquidateBorrowPaused = _isPaused; - emit IsLiquidateBorrowPausedSet(_poolToken, _isPaused); - } - /// @notice Sets the pause status for all markets. - /// @param _isPaused The new pause status, true to pause the mechanism. - function setIsPausedForAllMarkets(bool _isPaused) external onlyOwner { + /// @param _newStatus The new status to set. + function setPauseStatusForAllMarkets(bool _newStatus) external onlyOwner { uint256 numberOfMarketsCreated = marketsCreated.length; for (uint256 i; i < numberOfMarketsCreated; ) { address poolToken = marketsCreated[i]; - _setPauseStatus(poolToken, _isPaused); + market[poolToken].isPaused = _newStatus; + emit PauseStatusSet(poolToken, _newStatus); unchecked { ++i; @@ -371,65 +273,58 @@ abstract contract MorphoGovernance is MorphoUtils { } } - /// @notice Sets `isP2PDisabled` for a given market. - /// @param _poolToken The address of the market of which to enable/disable peer-to-peer matching. - /// @param _isP2PDisabled True to disable the peer-to-peer market. - function setIsP2PDisabled(address _poolToken, bool _isP2PDisabled) + /// @notice Sets the pause status on a specific market in case of emergency. + /// @param _poolToken The address of the market to pause/unpause. + /// @param _newStatus The new status to set. + function setPauseStatus(address _poolToken, bool _newStatus) external onlyOwner isMarketCreated(_poolToken) { - market[_poolToken].isP2PDisabled = _isP2PDisabled; - emit IsP2PDisabledSet(_poolToken, _isP2PDisabled); + market[_poolToken].isPaused = _newStatus; + emit PauseStatusSet(_poolToken, _newStatus); } - /// @notice Sets `isClaimRewardsPaused`. - /// @param _isPaused The new pause status, true to pause the mechanism. - function setIsClaimRewardsPaused(bool _isPaused) external onlyOwner { - isClaimRewardsPaused = _isPaused; - emit IsClaimRewardsPausedSet(_isPaused); - } - - /// @notice Sets a market's asset as collateral. - /// @param _poolToken The address of the market to (un)set as collateral. - /// @param _assetAsCollateral True to set the asset as collateral (True by default). - function setAssetAsCollateral(address _poolToken, bool _assetAsCollateral) + /// @notice Sets the partial pause status on a specific market in case of emergency. + /// @param _poolToken The address of the market to partially pause/unpause. + /// @param _newStatus The new status to set. + function setPartialPauseStatus(address _poolToken, bool _newStatus) external onlyOwner isMarketCreated(_poolToken) { - pool.setUserUseReserveAsCollateral(market[_poolToken].underlyingToken, _assetAsCollateral); + market[_poolToken].isPartiallyPaused = _newStatus; + emit PartialPauseStatusSet(_poolToken, _newStatus); } - /// @notice Sets a market as deprecated (allows liquidation of every positions on this market). - /// @param _poolToken The address of the market to update. - /// @param _isDeprecated True to set the market as deprecated. - function setIsDeprecated(address _poolToken, bool _isDeprecated) + /// @notice Sets the peer-to-peer disable status. + /// @param _poolToken The address of the market of which to enable/disable peer-to-peer matching. + /// @param _newStatus The new status to set. + function setP2PDisabledStatus(address _poolToken, bool _newStatus) external onlyOwner isMarketCreated(_poolToken) { - market[_poolToken].isDeprecated = _isDeprecated; - emit DeprecatedStatusSet(_poolToken, _isDeprecated); + market[_poolToken].isP2PDisabled = _newStatus; + emit P2PStatusSet(_poolToken, _newStatus); } - /// @notice Creates peer-to-peer deltas, to put some liquidity back on the pool. - /// @dev The current Morpho supply on the pool might not be enough to borrow `_amount` before resuppling it. - /// In this case, consider calling multiple times this function. - /// @param _poolToken The address of the market on which to create deltas. - /// @param _amount The amount to add to the deltas (in underlying). - function increaseP2PDeltas(address _poolToken, uint256 _amount) + /// @notice Sets the pause status on claiming rewards. + /// @param _newStatus The new status to set. + function setClaimRewardsPauseStatus(bool _newStatus) external onlyOwner { + isClaimRewardsPaused = _newStatus; + emit ClaimRewardsPauseStatusSet(_newStatus); + } + + /// @notice Sets a market's asset as collateral. + /// @param _poolToken The address of the market to (un)set as collateral. + /// @param _newStatus The new status to set. + function setAssetAsCollateral(address _poolToken, bool _newStatus) external onlyOwner isMarketCreated(_poolToken) { - address(exitPositionsManager).functionDelegateCall( - abi.encodeWithSelector( - IExitPositionsManager.increaseP2PDeltasLogic.selector, - _poolToken, - _amount - ) - ); + pool.setUserUseReserveAsCollateral(market[_poolToken].underlyingToken, _newStatus); } /// @notice Transfers the protocol reserve fee to the DAO. @@ -447,7 +342,7 @@ abstract contract MorphoGovernance is MorphoUtils { address poolToken = _poolTokens[i]; Types.Market memory market = market[poolToken]; - if (!market.isCreatedMemory()) continue; + if (!market.isCreated || market.isPaused || market.isPartiallyPaused) continue; ERC20 underlyingToken = ERC20(market.underlyingToken); uint256 underlyingBalance = underlyingToken.balanceOf(address(this)); @@ -479,7 +374,7 @@ abstract contract MorphoGovernance is MorphoUtils { address poolToken = pool.getReserveData(_underlyingToken).aTokenAddress; - if (market[poolToken].isCreated()) revert MarketAlreadyCreated(); + if (market[poolToken].isCreated) revert MarketAlreadyCreated(); p2pSupplyIndex[poolToken] = WadRayMath.RAY; p2pBorrowIndex[poolToken] = WadRayMath.RAY; @@ -496,14 +391,10 @@ abstract contract MorphoGovernance is MorphoUtils { underlyingToken: _underlyingToken, reserveFactor: _reserveFactor, p2pIndexCursor: _p2pIndexCursor, - isSupplyPaused: false, - isBorrowPaused: false, - isP2PDisabled: false, - isWithdrawPaused: false, - isRepayPaused: false, - isLiquidateCollateralPaused: false, - isLiquidateBorrowPaused: false, - isDeprecated: false + isCreated: true, + isPaused: false, + isPartiallyPaused: false, + isP2PDisabled: false }); borrowMask[poolToken] = ONE << (marketsCreated.length << 1); @@ -513,27 +404,4 @@ abstract contract MorphoGovernance is MorphoUtils { emit MarketCreated(poolToken, _reserveFactor, _p2pIndexCursor); } - - /// INTERNAL /// - - /// @notice Sets the different pause status for a given market. - /// @param _poolToken The address of the market to update. - /// @param _isPaused The new pause status, true to pause the mechanism. - function _setPauseStatus(address _poolToken, bool _isPaused) internal { - Types.Market storage market = market[_poolToken]; - - market.isSupplyPaused = _isPaused; - market.isBorrowPaused = _isPaused; - market.isWithdrawPaused = _isPaused; - market.isRepayPaused = _isPaused; - market.isLiquidateCollateralPaused = _isPaused; - market.isLiquidateBorrowPaused = _isPaused; - - emit IsSupplyPausedSet(_poolToken, _isPaused); - emit IsBorrowPausedSet(_poolToken, _isPaused); - emit IsWithdrawPausedSet(_poolToken, _isPaused); - emit IsRepayPausedSet(_poolToken, _isPaused); - emit IsLiquidateCollateralPausedSet(_poolToken, _isPaused); - emit IsLiquidateBorrowPausedSet(_poolToken, _isPaused); - } } diff --git a/contracts/aave-v2/MorphoUtils.sol b/contracts/aave-v2/MorphoUtils.sol index 1a730d019..b1e2b8151 100644 --- a/contracts/aave-v2/MorphoUtils.sol +++ b/contracts/aave-v2/MorphoUtils.sol @@ -5,8 +5,6 @@ import "./interfaces/aave/IPriceOracleGetter.sol"; import "./interfaces/aave/IAToken.sol"; import "./libraries/aave/ReserveConfiguration.sol"; -import "./libraries/aave/UserConfiguration.sol"; - import "@morpho-dao/morpho-utils/DelegateCall.sol"; import "@morpho-dao/morpho-utils/math/PercentageMath.sol"; import "@morpho-dao/morpho-utils/math/WadRayMath.sol"; @@ -20,10 +18,8 @@ import "./MorphoStorage.sol"; /// @notice Modifiers, getters and other util functions for Morpho. abstract contract MorphoUtils is MorphoStorage { using ReserveConfiguration for DataTypes.ReserveConfigurationMap; - using UserConfiguration for DataTypes.UserConfigurationMap; using HeapOrdering for HeapOrdering.HeapArray; using PercentageMath for uint256; - using MarketLib for Types.Market; using DelegateCall for address; using WadRayMath for uint256; using Math for uint256; @@ -33,12 +29,33 @@ abstract contract MorphoUtils is MorphoStorage { /// @notice Thrown when the market is not created yet. error MarketNotCreated(); + /// @notice Thrown when the market is paused. + error MarketPaused(); + /// MODIFIERS /// /// @notice Prevents to update a market not created yet. /// @param _poolToken The address of the market to check. modifier isMarketCreated(address _poolToken) { - if (!market[_poolToken].isCreated()) revert MarketNotCreated(); + if (!market[_poolToken].isCreated) revert MarketNotCreated(); + _; + } + + /// @notice Prevents a user to trigger a function when market is not created or paused. + /// @param _poolToken The address of the market to check. + modifier isMarketCreatedAndNotPaused(address _poolToken) { + Types.Market memory market = market[_poolToken]; + if (!market.isCreated) revert MarketNotCreated(); + if (market.isPaused) revert MarketPaused(); + _; + } + + /// @notice Prevents a user to trigger a function when market is not created or paused or partial paused. + /// @param _poolToken The address of the market to check. + modifier isMarketCreatedAndNotPausedNorPartiallyPaused(address _poolToken) { + Types.Market memory market = market[_poolToken]; + if (!market.isCreated) revert MarketNotCreated(); + if (market.isPaused || market.isPartiallyPaused) revert MarketPaused(); _; } @@ -257,11 +274,6 @@ abstract contract MorphoUtils is MorphoStorage { IPriceOracleGetter oracle = IPriceOracleGetter(addressesProvider.getPriceOracle()); Types.AssetLiquidityData memory assetData; Types.LiquidityStackVars memory vars; - - DataTypes.UserConfigurationMap memory morphoUserConfig = pool.getUserConfiguration( - address(this) - ); - vars.poolTokensLength = marketsCreated.length; vars.userMarkets = userMarkets[_user]; @@ -284,14 +296,6 @@ abstract contract MorphoUtils is MorphoStorage { assetData.tokenUnit = 10**assetData.decimals; } - // LTV and liquidation threshold should be zero if Morpho has not enabled this asset as collateral - if ( - !morphoUserConfig.isUsingAsCollateral(pool.getReserveData(vars.underlyingToken).id) - ) { - assetData.ltv = 0; - assetData.liquidationThreshold = 0; - } - if (_isBorrowing(vars.userMarkets, vars.borrowMask)) { values.debt += _debtValue( vars.poolToken, diff --git a/contracts/aave-v2/interfaces/IEntryPositionsManager.sol b/contracts/aave-v2/interfaces/IEntryPositionsManager.sol index c5478af7f..88f5519c5 100644 --- a/contracts/aave-v2/interfaces/IEntryPositionsManager.sol +++ b/contracts/aave-v2/interfaces/IEntryPositionsManager.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GNU AGPLv3 -pragma solidity >=0.8.0; +pragma solidity ^0.8.0; interface IEntryPositionsManager { function supplyLogic( diff --git a/contracts/aave-v2/interfaces/IExitPositionsManager.sol b/contracts/aave-v2/interfaces/IExitPositionsManager.sol index 62bcbe1a8..419e0dcbd 100644 --- a/contracts/aave-v2/interfaces/IExitPositionsManager.sol +++ b/contracts/aave-v2/interfaces/IExitPositionsManager.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GNU AGPLv3 -pragma solidity >=0.8.0; +pragma solidity ^0.8.0; interface IExitPositionsManager { function withdrawLogic( @@ -24,6 +24,4 @@ interface IExitPositionsManager { address _borrower, uint256 _amount ) external; - - function increaseP2PDeltasLogic(address _poolToken, uint256 _amount) external; } diff --git a/contracts/aave-v2/interfaces/IGetterUnderlyingAsset.sol b/contracts/aave-v2/interfaces/IGetterUnderlyingAsset.sol index 893fbb01d..1b18c16d2 100644 --- a/contracts/aave-v2/interfaces/IGetterUnderlyingAsset.sol +++ b/contracts/aave-v2/interfaces/IGetterUnderlyingAsset.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GNU AGPLv3 -pragma solidity >=0.8.0; +pragma solidity ^0.8.0; interface IGetterUnderlyingAsset { function UNDERLYING_ASSET_ADDRESS() external view returns (address); diff --git a/contracts/aave-v2/interfaces/IIncentivesVault.sol b/contracts/aave-v2/interfaces/IIncentivesVault.sol index c0a38c3df..febeba642 100644 --- a/contracts/aave-v2/interfaces/IIncentivesVault.sol +++ b/contracts/aave-v2/interfaces/IIncentivesVault.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GNU AGPLv3 -pragma solidity >=0.8.0; +pragma solidity ^0.8.0; import "./IOracle.sol"; diff --git a/contracts/aave-v2/interfaces/IInterestRatesManager.sol b/contracts/aave-v2/interfaces/IInterestRatesManager.sol index d6d743e9e..406b43da0 100644 --- a/contracts/aave-v2/interfaces/IInterestRatesManager.sol +++ b/contracts/aave-v2/interfaces/IInterestRatesManager.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GNU AGPLv3 -pragma solidity >=0.8.0; +pragma solidity ^0.8.0; interface IInterestRatesManager { function ST_ETH() external view returns (address); diff --git a/contracts/aave-v2/interfaces/ILens.sol b/contracts/aave-v2/interfaces/ILens.sol index e83073e29..20eaf0698 100644 --- a/contracts/aave-v2/interfaces/ILens.sol +++ b/contracts/aave-v2/interfaces/ILens.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GNU AGPLv3 -pragma solidity >=0.8.0; +pragma solidity ^0.8.0; import "./aave/IPriceOracleGetter.sol"; import "./aave/ILendingPool.sol"; diff --git a/contracts/aave-v2/interfaces/IMorpho.sol b/contracts/aave-v2/interfaces/IMorpho.sol index f9803f5f7..d85b72af2 100644 --- a/contracts/aave-v2/interfaces/IMorpho.sol +++ b/contracts/aave-v2/interfaces/IMorpho.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GNU AGPLv3 -pragma solidity >=0.8.0; +pragma solidity ^0.8.0; import "./aave/ILendingPoolAddressesProvider.sol"; import "./aave/ILendingPool.sol"; @@ -64,13 +64,13 @@ interface IMorpho { function setTreasuryVault(address _newTreasuryVaultAddress) external; function setIncentivesVault(address _newIncentivesVault) external; function setRewardsManager(address _rewardsManagerAddress) external; - function setIsP2PDisabled(address _poolToken, bool _isP2PDisabled) external; + function setP2PDisabledStatus(address _poolToken, bool _isP2PDisabled) external; function setReserveFactor(address _poolToken, uint256 _newReserveFactor) external; function setP2PIndexCursor(address _poolToken, uint16 _p2pIndexCursor) external; - function setIsPausedForAllMarkets(bool _isPaused) external; - function setIsClaimRewardsPaused(bool _isPaused) external; - function setPauseStatus(address _poolToken, bool _isPaused) external; - function setPartialPauseStatus(address _poolToken, bool _isPaused) external; + function setPauseStatusForAllMarkets(bool _newStatus) external; + function setClaimRewardsPauseStatus(bool _newStatus) external; + function setPauseStatus(address _poolToken, bool _newStatus) external; + function setPartialPauseStatus(address _poolToken, bool _newStatus) external; function setExitPositionsManager(IExitPositionsManager _exitPositionsManager) external; function setEntryPositionsManager(IEntryPositionsManager _entryPositionsManager) external; function setInterestRatesManager(IInterestRatesManager _interestRatesManager) external; diff --git a/contracts/aave-v2/interfaces/IOracle.sol b/contracts/aave-v2/interfaces/IOracle.sol index 57ad8744a..22f465867 100644 --- a/contracts/aave-v2/interfaces/IOracle.sol +++ b/contracts/aave-v2/interfaces/IOracle.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GNU AGPLv3 -pragma solidity >=0.8.0; +pragma solidity ^0.8.0; interface IOracle { function consult(uint256 _amountIn) external returns (uint256); diff --git a/contracts/aave-v2/interfaces/IRewardsManager.sol b/contracts/aave-v2/interfaces/IRewardsManager.sol index 950a6d4ff..34b520879 100644 --- a/contracts/aave-v2/interfaces/IRewardsManager.sol +++ b/contracts/aave-v2/interfaces/IRewardsManager.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GNU AGPLv3 -pragma solidity >=0.8.0; +pragma solidity ^0.8.0; import "./aave/IAaveIncentivesController.sol"; diff --git a/contracts/aave-v2/interfaces/aave/IAToken.sol b/contracts/aave-v2/interfaces/aave/IAToken.sol index d2b5ce35b..38aaad6a4 100644 --- a/contracts/aave-v2/interfaces/aave/IAToken.sol +++ b/contracts/aave-v2/interfaces/aave/IAToken.sol @@ -1,5 +1,5 @@ -// SPDX-License-Identifier: GNU AGPLv3 -pragma solidity >=0.8.0; +// SPDX-License-Identifier: agpl-3.0 +pragma solidity ^0.8.0; import {IERC20} from "./IERC20.sol"; import {IScaledBalanceToken} from "./IScaledBalanceToken.sol"; diff --git a/contracts/aave-v2/interfaces/aave/IAaveIncentivesController.sol b/contracts/aave-v2/interfaces/aave/IAaveIncentivesController.sol index 67820aad1..bc61009b2 100644 --- a/contracts/aave-v2/interfaces/aave/IAaveIncentivesController.sol +++ b/contracts/aave-v2/interfaces/aave/IAaveIncentivesController.sol @@ -1,5 +1,5 @@ -// SPDX-License-Identifier: GNU AGPLv3 -pragma solidity >=0.8.0; +// SPDX-License-Identifier: agpl-3.0 +pragma solidity ^0.8.0; interface IAaveDistributionManager { event AssetConfigUpdated(address indexed asset, uint256 emission); diff --git a/contracts/aave-v2/interfaces/aave/IERC20.sol b/contracts/aave-v2/interfaces/aave/IERC20.sol index 27d677316..f68ed2bce 100644 --- a/contracts/aave-v2/interfaces/aave/IERC20.sol +++ b/contracts/aave-v2/interfaces/aave/IERC20.sol @@ -1,5 +1,5 @@ -// SPDX-License-Identifier: GNU AGPLv3 -pragma solidity >=0.8.0; +// SPDX-License-Identifier: agpl-3.0 +pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. diff --git a/contracts/aave-v2/interfaces/aave/ILendingPool.sol b/contracts/aave-v2/interfaces/aave/ILendingPool.sol index b7afb1749..0d8f56d86 100644 --- a/contracts/aave-v2/interfaces/aave/ILendingPool.sol +++ b/contracts/aave-v2/interfaces/aave/ILendingPool.sol @@ -1,5 +1,5 @@ -// SPDX-License-Identifier: GNU AGPLv3 -pragma solidity >=0.8.0; +// SPDX-License-Identifier: agpl-3.0 +pragma solidity ^0.8.0; pragma experimental ABIEncoderV2; import {ILendingPoolAddressesProvider} from "./ILendingPoolAddressesProvider.sol"; diff --git a/contracts/aave-v2/interfaces/aave/ILendingPoolAddressesProvider.sol b/contracts/aave-v2/interfaces/aave/ILendingPoolAddressesProvider.sol index 8280312c9..daab1180c 100644 --- a/contracts/aave-v2/interfaces/aave/ILendingPoolAddressesProvider.sol +++ b/contracts/aave-v2/interfaces/aave/ILendingPoolAddressesProvider.sol @@ -1,5 +1,5 @@ -// SPDX-License-Identifier: GNU AGPLv3 -pragma solidity >=0.8.0; +// SPDX-License-Identifier: agpl-3.0 +pragma solidity ^0.8.0; /** * @title LendingPoolAddressesProvider contract diff --git a/contracts/aave-v2/interfaces/aave/IPriceOracleGetter.sol b/contracts/aave-v2/interfaces/aave/IPriceOracleGetter.sol index 81ba2fdde..572a1e147 100644 --- a/contracts/aave-v2/interfaces/aave/IPriceOracleGetter.sol +++ b/contracts/aave-v2/interfaces/aave/IPriceOracleGetter.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity >=0.8.0; +pragma solidity ^0.8.0; /************ @title IPriceOracleGetter interface diff --git a/contracts/aave-v2/interfaces/aave/IScaledBalanceToken.sol b/contracts/aave-v2/interfaces/aave/IScaledBalanceToken.sol index 2d8f66b31..8ba23e0b3 100644 --- a/contracts/aave-v2/interfaces/aave/IScaledBalanceToken.sol +++ b/contracts/aave-v2/interfaces/aave/IScaledBalanceToken.sol @@ -1,5 +1,5 @@ -// SPDX-License-Identifier: GNU AGPLv3 -pragma solidity >=0.8.0; +// SPDX-License-Identifier: agpl-3.0 +pragma solidity ^0.8.0; interface IScaledBalanceToken { /** diff --git a/contracts/aave-v2/interfaces/aave/IVariableDebtToken.sol b/contracts/aave-v2/interfaces/aave/IVariableDebtToken.sol index 1e5a3326b..f6bd7322e 100644 --- a/contracts/aave-v2/interfaces/aave/IVariableDebtToken.sol +++ b/contracts/aave-v2/interfaces/aave/IVariableDebtToken.sol @@ -1,5 +1,5 @@ -// SPDX-License-Identifier: GNU AGPLv3 -pragma solidity >=0.8.0; +// SPDX-License-Identifier: agpl-3.0 +pragma solidity ^0.8.0; import {IScaledBalanceToken} from "./IScaledBalanceToken.sol"; @@ -69,10 +69,4 @@ interface IVariableDebtToken is IScaledBalanceToken { uint256 amount, uint256 index ) external; - - /** - * @dev Returns the debt balance of the user - * @return The debt balance of the user. - **/ - function balanceOf(address user) external view returns (uint256); } diff --git a/contracts/aave-v2/lens/IndexesLens.sol b/contracts/aave-v2/lens/IndexesLens.sol index 0436bd3e2..f0218799c 100644 --- a/contracts/aave-v2/lens/IndexesLens.sol +++ b/contracts/aave-v2/lens/IndexesLens.sol @@ -68,7 +68,7 @@ abstract contract IndexesLens is LensStorage { /// @return poolSupplyIndex The updated pool supply index. /// @return poolBorrowIndex The updated pool borrow index. function _getIndexes(address _poolToken) - public + internal view returns ( address underlyingToken, diff --git a/contracts/aave-v2/lens/MarketsLens.sol b/contracts/aave-v2/lens/MarketsLens.sol index c2705c88b..6319d4374 100644 --- a/contracts/aave-v2/lens/MarketsLens.sol +++ b/contracts/aave-v2/lens/MarketsLens.sol @@ -9,7 +9,6 @@ import "./RatesLens.sol"; /// @notice Intermediary layer exposing endpoints to query live data related to the Morpho Protocol markets. abstract contract MarketsLens is RatesLens { using ReserveConfiguration for DataTypes.ReserveConfigurationMap; - using MarketLib for Types.Market; using WadRayMath for uint256; /// EXTERNAL /// @@ -18,7 +17,7 @@ abstract contract MarketsLens is RatesLens { /// @param _poolToken The address of the market to check. /// @return true if the market is created and not paused, otherwise false. function isMarketCreated(address _poolToken) external view returns (bool) { - return morpho.market(_poolToken).isCreatedMemory(); + return morpho.market(_poolToken).isCreated; } /// @notice Checks if a market is created and not paused. @@ -26,12 +25,7 @@ abstract contract MarketsLens is RatesLens { /// @return true if the market is created and not paused, otherwise false. function isMarketCreatedAndNotPaused(address _poolToken) external view returns (bool) { Types.Market memory market = morpho.market(_poolToken); - return - market.isCreatedMemory() && - !(market.isSupplyPaused && - market.isBorrowPaused && - market.isWithdrawPaused && - market.isRepayPaused); + return market.isCreated && !market.isPaused; } /// @notice Checks if a market is created and not paused or partially paused. @@ -43,9 +37,7 @@ abstract contract MarketsLens is RatesLens { returns (bool) { Types.Market memory market = morpho.market(_poolToken); - return - market.underlyingToken != address(0) && - !(market.isSupplyPaused && market.isBorrowPaused); + return market.isCreated && !market.isPaused && !market.isPartiallyPaused; } /// @notice Returns all created markets. @@ -145,14 +137,10 @@ abstract contract MarketsLens is RatesLens { underlying = IAToken(_poolToken).UNDERLYING_ASSET_ADDRESS(); Types.Market memory market = morpho.market(_poolToken); - isCreated = market.underlyingToken != address(0); + isCreated = market.isCreated; isP2PDisabled = market.isP2PDisabled; - isPaused = - market.isSupplyPaused && - market.isBorrowPaused && - market.isWithdrawPaused && - market.isRepayPaused; - isPartiallyPaused = market.isSupplyPaused && market.isBorrowPaused; + isPaused = market.isPaused; + isPartiallyPaused = market.isPartiallyPaused; reserveFactor = market.reserveFactor; p2pIndexCursor = market.p2pIndexCursor; @@ -195,7 +183,7 @@ abstract contract MarketsLens is RatesLens { /// @return p2pSupplyAmount The total supplied amount matched peer-to-peer, subtracting the supply delta (in underlying). /// @return poolSupplyAmount The total supplied amount on the underlying pool, adding the supply delta (in underlying). function _getTotalMarketSupply(address _poolToken) - public + internal view returns ( address underlyingToken, @@ -224,7 +212,7 @@ abstract contract MarketsLens is RatesLens { /// @return p2pBorrowAmount The total borrowed amount matched peer-to-peer, subtracting the borrow delta (in underlying). /// @return poolBorrowAmount The total borrowed amount on the underlying pool, adding the borrow delta (in underlying). function _getTotalMarketBorrow(address _poolToken) - public + internal view returns ( address underlyingToken, diff --git a/contracts/aave-v2/lens/UsersLens.sol b/contracts/aave-v2/lens/UsersLens.sol index 77d6322a4..044825b56 100644 --- a/contracts/aave-v2/lens/UsersLens.sol +++ b/contracts/aave-v2/lens/UsersLens.sol @@ -358,7 +358,7 @@ abstract contract UsersLens is IndexesLens { /// @return balanceOnPool The balance on pool of the user (in underlying). /// @return totalBalance The total balance of the user (in underlying). function _getCurrentSupplyBalanceInOf(address _poolToken, address _user) - public + internal view returns ( address underlyingToken, @@ -391,7 +391,7 @@ abstract contract UsersLens is IndexesLens { /// @return balanceOnPool The balance on pool of the user (in underlying). /// @return totalBalance The total balance of the user (in underlying). function _getCurrentBorrowBalanceInOf(address _poolToken, address _user) - public + internal view returns ( address underlyingToken, diff --git a/contracts/aave-v2/libraries/InterestRatesModel.sol b/contracts/aave-v2/libraries/InterestRatesModel.sol index f76dad29a..bbfb34097 100644 --- a/contracts/aave-v2/libraries/InterestRatesModel.sol +++ b/contracts/aave-v2/libraries/InterestRatesModel.sol @@ -11,11 +11,6 @@ library InterestRatesModel { using PercentageMath for uint256; using WadRayMath for uint256; - /// ERRORS /// - - // Thrown when percentage is above 100%. - error PercentageTooHigh(); - /// STRUCTS /// struct GrowthFactors { diff --git a/contracts/aave-v2/libraries/Types.sol b/contracts/aave-v2/libraries/Types.sol index 0e29676af..112569381 100644 --- a/contracts/aave-v2/libraries/Types.sol +++ b/contracts/aave-v2/libraries/Types.sol @@ -1,16 +1,6 @@ // SPDX-License-Identifier: GNU AGPLv3 pragma solidity ^0.8.0; -library MarketLib { - function isCreated(Types.Market storage _market) internal view returns (bool) { - return _market.underlyingToken != address(0); - } - - function isCreatedMemory(Types.Market memory _market) internal pure returns (bool) { - return _market.underlyingToken != address(0); - } -} - /// @title Types. /// @author Morpho Labs. /// @custom:contact security@morpho.xyz @@ -72,22 +62,18 @@ library Types { // Variables are packed together to save gas (will not exceed their limit during Morpho's lifetime). struct PoolIndexes { uint32 lastUpdateTimestamp; // The last time the local pool and peer-to-peer indexes were updated. - uint112 poolSupplyIndex; // Last pool supply index. Note that for the stEth market, the pool supply index is tweaked to take into account the staking rewards. - uint112 poolBorrowIndex; // Last pool borrow index. Note that for the stEth market, the pool borrow index is tweaked to take into account the staking rewards. + uint112 poolSupplyIndex; // Last pool supply index. + uint112 poolBorrowIndex; // Last pool borrow index. } struct Market { address underlyingToken; // The underlying address of the market. uint16 reserveFactor; // Proportion of the additional interest earned being matched peer-to-peer on Morpho compared to being on the pool. It is sent to the DAO for each market. The default value is 0. In basis point (100% = 10 000). uint16 p2pIndexCursor; // Position of the peer-to-peer rate in the pool's spread. Determine the weights of the weighted arithmetic average in the indexes computations ((1 - p2pIndexCursor) * r^S + p2pIndexCursor * r^B) (in basis point). + bool isCreated; // Whether or not this market is created. + bool isPaused; // Whether the market is paused or not (all entry points on Morpho are frozen; supply, borrow, withdraw, repay and liquidate). + bool isPartiallyPaused; // Whether the market is partially paused or not (only supply and borrow are frozen). bool isP2PDisabled; // Whether the market's peer-to-peer is open or not. - bool isSupplyPaused; // Whether the supply is paused or not. - bool isBorrowPaused; // Whether the borrow is paused or not - bool isWithdrawPaused; // Whether the withdraw is paused or not. Note that a "withdraw" is still possible using a liquidation (if not paused). - bool isRepayPaused; // Whether the repay is paused or not. - bool isLiquidateCollateralPaused; // Whether the liquidation on this market as collateral is paused or not. - bool isLiquidateBorrowPaused; // Whether the liquidatation on this market as borrow is paused or not. - bool isDeprecated; // Whether a market is deprecated or not. } struct LiquidityStackVars { diff --git a/contracts/aave-v2/libraries/aave/DataTypes.sol b/contracts/aave-v2/libraries/aave/DataTypes.sol index 0e3bd739c..8f9354a18 100644 --- a/contracts/aave-v2/libraries/aave/DataTypes.sol +++ b/contracts/aave-v2/libraries/aave/DataTypes.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GNU AGPLv3 +// SPDX-License-Identifier: agpl-3.0 pragma solidity ^0.8.0; library DataTypes { diff --git a/contracts/aave-v2/libraries/aave/Errors.sol b/contracts/aave-v2/libraries/aave/Errors.sol index 44573ab0a..36fde0f80 100644 --- a/contracts/aave-v2/libraries/aave/Errors.sol +++ b/contracts/aave-v2/libraries/aave/Errors.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GNU AGPLv3 +// SPDX-License-Identifier: agpl-3.0 pragma solidity ^0.8.0; /** diff --git a/contracts/aave-v2/libraries/aave/ReserveConfiguration.sol b/contracts/aave-v2/libraries/aave/ReserveConfiguration.sol index 063a8740b..6c5659720 100644 --- a/contracts/aave-v2/libraries/aave/ReserveConfiguration.sol +++ b/contracts/aave-v2/libraries/aave/ReserveConfiguration.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GNU AGPLv3 +// SPDX-License-Identifier: agpl-3.0 pragma solidity ^0.8.0; import {Errors} from "./Errors.sol"; diff --git a/contracts/aave-v2/libraries/aave/UserConfiguration.sol b/contracts/aave-v2/libraries/aave/UserConfiguration.sol deleted file mode 100644 index 931a6d6e9..000000000 --- a/contracts/aave-v2/libraries/aave/UserConfiguration.sol +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-License-Identifier: GNU AGPLv3 -pragma solidity ^0.8.0; - -import {DataTypes} from "./DataTypes.sol"; - -library UserConfiguration { - function isUsingAsCollateral(DataTypes.UserConfigurationMap memory self, uint256 reserveIndex) - internal - pure - returns (bool) - { - unchecked { - return (self.data >> ((reserveIndex << 1) + 1)) & 1 != 0; - } - } -} diff --git a/contracts/compound/Morpho.sol b/contracts/compound/Morpho.sol index 21824e2ef..370e8d289 100644 --- a/contracts/compound/Morpho.sol +++ b/contracts/compound/Morpho.sol @@ -30,7 +30,11 @@ contract Morpho is MorphoGovernance { /// @dev `msg.sender` must have approved Morpho's contract to spend the underlying `_amount`. /// @param _poolToken The address of the market the user wants to interact with. /// @param _amount The amount of token (in underlying) to supply. - function supply(address _poolToken, uint256 _amount) external nonReentrant { + function supply(address _poolToken, uint256 _amount) + external + nonReentrant + isMarketCreatedAndNotPausedNorPartiallyPaused(_poolToken) + { _supply(_poolToken, msg.sender, _amount, defaultMaxGasForMatching.supply); } @@ -43,7 +47,7 @@ contract Morpho is MorphoGovernance { address _poolToken, address _onBehalf, uint256 _amount - ) external nonReentrant { + ) external nonReentrant isMarketCreatedAndNotPausedNorPartiallyPaused(_poolToken) { _supply(_poolToken, _onBehalf, _amount, defaultMaxGasForMatching.supply); } @@ -59,14 +63,18 @@ contract Morpho is MorphoGovernance { address _onBehalf, uint256 _amount, uint256 _maxGasForMatching - ) external nonReentrant { + ) external nonReentrant isMarketCreatedAndNotPausedNorPartiallyPaused(_poolToken) { _supply(_poolToken, _onBehalf, _amount, _maxGasForMatching); } /// @notice Borrows underlying tokens from a specific market. /// @param _poolToken The address of the market the user wants to interact with. /// @param _amount The amount of token (in underlying). - function borrow(address _poolToken, uint256 _amount) external nonReentrant { + function borrow(address _poolToken, uint256 _amount) + external + nonReentrant + isMarketCreatedAndNotPausedNorPartiallyPaused(_poolToken) + { _borrow(_poolToken, _amount, defaultMaxGasForMatching.borrow); } @@ -78,14 +86,18 @@ contract Morpho is MorphoGovernance { address _poolToken, uint256 _amount, uint256 _maxGasForMatching - ) external nonReentrant { + ) external nonReentrant isMarketCreatedAndNotPausedNorPartiallyPaused(_poolToken) { _borrow(_poolToken, _amount, _maxGasForMatching); } /// @notice Withdraws underlying tokens from a specific market. /// @param _poolToken The address of the market the user wants to interact with. /// @param _amount The amount of tokens (in underlying) to withdraw from supply. - function withdraw(address _poolToken, uint256 _amount) external nonReentrant { + function withdraw(address _poolToken, uint256 _amount) + external + nonReentrant + isMarketCreatedAndNotPaused(_poolToken) + { _withdraw(_poolToken, _amount, msg.sender, defaultMaxGasForMatching.withdraw); } @@ -97,7 +109,7 @@ contract Morpho is MorphoGovernance { address _poolToken, uint256 _amount, address _receiver - ) external nonReentrant { + ) external nonReentrant isMarketCreatedAndNotPaused(_poolToken) { _withdraw(_poolToken, _amount, _receiver, defaultMaxGasForMatching.withdraw); } @@ -105,7 +117,11 @@ contract Morpho is MorphoGovernance { /// @dev `msg.sender` must have approved Morpho's contract to spend the underlying `_amount`. /// @param _poolToken The address of the market the user wants to interact with. /// @param _amount The amount of token (in underlying) to repay from borrow. - function repay(address _poolToken, uint256 _amount) external nonReentrant { + function repay(address _poolToken, uint256 _amount) + external + nonReentrant + isMarketCreatedAndNotPaused(_poolToken) + { _repay(_poolToken, msg.sender, _amount, defaultMaxGasForMatching.repay); } @@ -118,7 +134,7 @@ contract Morpho is MorphoGovernance { address _poolToken, address _onBehalf, uint256 _amount - ) external nonReentrant { + ) external nonReentrant isMarketCreatedAndNotPaused(_poolToken) { _repay(_poolToken, _onBehalf, _amount, defaultMaxGasForMatching.repay); } @@ -132,7 +148,12 @@ contract Morpho is MorphoGovernance { address _poolTokenCollateral, address _borrower, uint256 _amount - ) external nonReentrant { + ) + external + nonReentrant + isMarketCreatedAndNotPaused(_poolTokenBorrowed) + isMarketCreatedAndNotPaused(_poolTokenCollateral) + { address(positionsManager).functionDelegateCall( abi.encodeWithSelector( IPositionsManager.liquidateLogic.selector, diff --git a/contracts/compound/MorphoGovernance.sol b/contracts/compound/MorphoGovernance.sol index 4947a7b75..d1cc10477 100644 --- a/contracts/compound/MorphoGovernance.sol +++ b/contracts/compound/MorphoGovernance.sol @@ -9,7 +9,6 @@ import "./MorphoUtils.sol"; /// @notice Governance functions for Morpho. abstract contract MorphoGovernance is MorphoUtils { using SafeTransferLib for ERC20; - using DelegateCall for address; /// EVENTS /// @@ -60,49 +59,24 @@ abstract contract MorphoGovernance is MorphoUtils { /// @param _amountClaimed The amount of reward token claimed. event ReserveFeeClaimed(address indexed _poolToken, uint256 _amountClaimed); - /// @notice Emitted when the value of `isP2PDisabled` is set. + /// @notice Emitted when the value of `p2pDisabled` is set. /// @param _poolToken The address of the concerned market. - /// @param _isP2PDisabled The new value of `_isP2PDisabled` adopted. - event IsP2PDisabledSet(address indexed _poolToken, bool _isP2PDisabled); + /// @param _p2pDisabled The new value of `_p2pDisabled` adopted. + event P2PStatusSet(address indexed _poolToken, bool _p2pDisabled); - /// @notice Emitted when a supply is paused or unpaused. + /// @notice Emitted when a market is paused or unpaused. /// @param _poolToken The address of the concerned market. - /// @param _isPaused The new pause status of the market. - event IsSupplyPausedSet(address indexed _poolToken, bool _isPaused); + /// @param _newStatus The new pause status of the market. + event PauseStatusSet(address indexed _poolToken, bool _newStatus); - /// @notice Emitted when a borrow is paused or unpaused. + /// @notice Emitted when a market is partially paused or unpaused. /// @param _poolToken The address of the concerned market. - /// @param _isPaused The new pause status of the market. - event IsBorrowPausedSet(address indexed _poolToken, bool _isPaused); - - /// @notice Emitted when a withdraw is paused or unpaused. - /// @param _poolToken The address of the concerned market. - /// @param _isPaused The new pause status of the market. - event IsWithdrawPausedSet(address indexed _poolToken, bool _isPaused); - - /// @notice Emitted when a repay is paused or unpaused. - /// @param _poolToken The address of the concerned market. - /// @param _isPaused The new pause status of the market. - event IsRepayPausedSet(address indexed _poolToken, bool _isPaused); - - /// @notice Emitted when a liquidate as collateral is paused or unpaused. - /// @param _poolToken The address of the concerned market. - /// @param _isPaused The new pause status of the market. - event IsLiquidateCollateralPausedSet(address indexed _poolToken, bool _isPaused); - - /// @notice Emitted when a liquidate as borrow is paused or unpaused. - /// @param _poolToken The address of the concerned market. - /// @param _isPaused The new pause status of the market. - event IsLiquidateBorrowPausedSet(address indexed _poolToken, bool _isPaused); - - /// @notice Emitted when a market is set as deprecated or not. - /// @param _poolToken The address of the concerned market. - /// @param _isDeprecated The new deprecated status. - event DeprecatedStatusSet(address indexed _poolToken, bool _isDeprecated); + /// @param _newStatus The new partial pause status of the market. + event PartialPauseStatusSet(address indexed _poolToken, bool _newStatus); /// @notice Emitted when claiming rewards is paused or unpaused. - /// @param _isPaused The new claiming rewards status. - event IsClaimRewardsPausedSet(bool _isPaused); + /// @param _newStatus The new claiming rewards status. + event ClaimRewardsPauseStatusSet(bool _newStatus); /// @notice Emitted when a new market is created. /// @param _poolToken The address of the market that has been created. @@ -257,142 +231,47 @@ abstract contract MorphoGovernance is MorphoUtils { emit P2PIndexCursorSet(_poolToken, _p2pIndexCursor); } - /// @notice Sets `isSupplyPaused` for a given market. - /// @param _poolToken The address of the market to update. - /// @param _isPaused The new pause status, true to pause the mechanism. - function setIsSupplyPaused(address _poolToken, bool _isPaused) - external - onlyOwner - isMarketCreated(_poolToken) - { - marketStatus[_poolToken].isSupplyPaused = _isPaused; - emit IsSupplyPausedSet(_poolToken, _isPaused); - } - - /// @notice Sets `isBorrowPaused` for a given market. - /// @param _poolToken The address of the market to update. - /// @param _isPaused The new pause status, true to pause the mechanism. - function setIsBorrowPaused(address _poolToken, bool _isPaused) - external - onlyOwner - isMarketCreated(_poolToken) - { - marketStatus[_poolToken].isBorrowPaused = _isPaused; - emit IsBorrowPausedSet(_poolToken, _isPaused); - } - - /// @notice Sets `isWithdrawPaused` for a given market. - /// @param _poolToken The address of the market to update. - /// @param _isPaused The new pause status, true to pause the mechanism. - function setIsWithdrawPaused(address _poolToken, bool _isPaused) - external - onlyOwner - isMarketCreated(_poolToken) - { - marketStatus[_poolToken].isWithdrawPaused = _isPaused; - emit IsWithdrawPausedSet(_poolToken, _isPaused); - } - - /// @notice Sets `isRepayPaused` for a given market. - /// @param _poolToken The address of the market to update. - /// @param _isPaused The new pause status, true to pause the mechanism. - function setIsRepayPaused(address _poolToken, bool _isPaused) - external - onlyOwner - isMarketCreated(_poolToken) - { - marketStatus[_poolToken].isRepayPaused = _isPaused; - emit IsRepayPausedSet(_poolToken, _isPaused); - } - - /// @notice Sets `isLiquidateCollateralPaused` for a given market. - /// @param _poolToken The address of the market to update. - /// @param _isPaused The new pause status, true to pause the mechanism. - function setIsLiquidateCollateralPaused(address _poolToken, bool _isPaused) - external - onlyOwner - isMarketCreated(_poolToken) - { - marketStatus[_poolToken].isLiquidateCollateralPaused = _isPaused; - emit IsLiquidateCollateralPausedSet(_poolToken, _isPaused); - } - - /// @notice Sets `isLiquidateBorrowPaused` for a given market. - /// @param _poolToken The address of the market to update. - /// @param _isPaused The new pause status, true to pause the mechanism. - function setIsLiquidateBorrowPaused(address _poolToken, bool _isPaused) + /// @notice Sets the pause status on a specific market in case of emergency. + /// @param _poolToken The address of the market to pause/unpause. + /// @param _newStatus The new status to set. + function setPauseStatus(address _poolToken, bool _newStatus) external onlyOwner isMarketCreated(_poolToken) { - marketStatus[_poolToken].isLiquidateBorrowPaused = _isPaused; - emit IsLiquidateBorrowPausedSet(_poolToken, _isPaused); - } - - /// @notice Sets the pause status for all markets. - /// @param _isPaused The new pause status, true to pause the mechanism. - function setIsPausedForAllMarkets(bool _isPaused) external onlyOwner { - uint256 numberOfMarketsCreated = marketsCreated.length; - - for (uint256 i; i < numberOfMarketsCreated; ) { - address poolToken = marketsCreated[i]; - - _setPauseStatus(poolToken, _isPaused); - - unchecked { - ++i; - } - } + marketStatus[_poolToken].isPaused = _newStatus; + emit PauseStatusSet(_poolToken, _newStatus); } - /// @notice Sets `isP2PDisabled` for a given market. - /// @param _poolToken The address of the market to update. - /// @param _isP2PDisabled True to disable the peer-to-peer market. - function setIsP2PDisabled(address _poolToken, bool _isP2PDisabled) + /// @notice Sets the partial pause status on a specific market in case of emergency. + /// @param _poolToken The address of the market to partially pause/unpause. + /// @param _newStatus The new status to set. + function setPartialPauseStatus(address _poolToken, bool _newStatus) external onlyOwner isMarketCreated(_poolToken) { - p2pDisabled[_poolToken] = _isP2PDisabled; - emit IsP2PDisabledSet(_poolToken, _isP2PDisabled); - } - - /// @notice Sets `isClaimRewardsPaused`. - /// @param _isPaused The new pause status, true to pause the mechanism. - function setIsClaimRewardsPaused(bool _isPaused) external onlyOwner { - isClaimRewardsPaused = _isPaused; - emit IsClaimRewardsPausedSet(_isPaused); + marketStatus[_poolToken].isPartiallyPaused = _newStatus; + emit PartialPauseStatusSet(_poolToken, _newStatus); } - /// @notice Sets a market as deprecated (allows liquidation of every positions on this market). - /// @param _poolToken The address of the market to update. - /// @param _isDeprecated True to set the market as deprecated. - function setIsDeprecated(address _poolToken, bool _isDeprecated) + /// @notice Sets the peer-to-peer disable status. + /// @param _poolToken The address of the market to able/disable P2P. + /// @param _newStatus The new status to set. + function setP2PDisabled(address _poolToken, bool _newStatus) external onlyOwner isMarketCreated(_poolToken) { - marketStatus[_poolToken].isDeprecated = _isDeprecated; - emit DeprecatedStatusSet(_poolToken, _isDeprecated); + p2pDisabled[_poolToken] = _newStatus; + emit P2PStatusSet(_poolToken, _newStatus); } - /// @notice Creates peer-to-peer deltas, to put some liquidity back on the pool. - /// @dev The current Morpho supply on the pool might not be enough to borrow `_amount` before resuppling it. - /// In this case, consider calling multiple times this function. - /// @param _poolToken The address of the market on which to create deltas. - /// @param _amount The amount to add to the deltas (in underlying). - function increaseP2PDeltas(address _poolToken, uint256 _amount) - external - onlyOwner - isMarketCreated(_poolToken) - { - address(positionsManager).functionDelegateCall( - abi.encodeWithSelector( - IPositionsManager.increaseP2PDeltasLogic.selector, - _poolToken, - _amount - ) - ); + /// @notice Sets the pause status on claiming rewards. + /// @param _newStatus The new status to set. + function setClaimRewardsPauseStatus(bool _newStatus) external onlyOwner { + isClaimRewardsPaused = _newStatus; + emit ClaimRewardsPauseStatusSet(_newStatus); } /// @notice Transfers the protocol reserve fee to the DAO. @@ -410,7 +289,7 @@ abstract contract MorphoGovernance is MorphoUtils { address poolToken = _poolTokens[i]; Types.MarketStatus memory status = marketStatus[poolToken]; - if (!status.isCreated) continue; + if (!status.isCreated || status.isPaused || status.isPartiallyPaused) continue; ERC20 underlyingToken = _getUnderlying(poolToken); uint256 underlyingBalance = underlyingToken.balanceOf(address(this)); @@ -462,27 +341,4 @@ abstract contract MorphoGovernance is MorphoUtils { marketsCreated.push(_poolToken); emit MarketCreated(_poolToken, _marketParams.reserveFactor, _marketParams.p2pIndexCursor); } - - /// INTERNAL /// - - /// @notice Sets the different pause status for a given market. - /// @param _poolToken The address of the market to update. - /// @param _isPaused The new pause status, true to pause the mechanism. - function _setPauseStatus(address _poolToken, bool _isPaused) internal { - Types.MarketStatus storage market = marketStatus[_poolToken]; - - market.isSupplyPaused = _isPaused; - market.isBorrowPaused = _isPaused; - market.isWithdrawPaused = _isPaused; - market.isRepayPaused = _isPaused; - market.isLiquidateCollateralPaused = _isPaused; - market.isLiquidateBorrowPaused = _isPaused; - - emit IsSupplyPausedSet(_poolToken, _isPaused); - emit IsBorrowPausedSet(_poolToken, _isPaused); - emit IsWithdrawPausedSet(_poolToken, _isPaused); - emit IsRepayPausedSet(_poolToken, _isPaused); - emit IsLiquidateCollateralPausedSet(_poolToken, _isPaused); - emit IsLiquidateBorrowPausedSet(_poolToken, _isPaused); - } } diff --git a/contracts/compound/MorphoUtils.sol b/contracts/compound/MorphoUtils.sol index 0250ef36f..a5b59415f 100644 --- a/contracts/compound/MorphoUtils.sol +++ b/contracts/compound/MorphoUtils.sol @@ -25,6 +25,9 @@ abstract contract MorphoUtils is MorphoStorage { /// @notice Thrown when the market is not created yet. error MarketNotCreated(); + /// @notice Thrown when the market is paused. + error MarketPaused(); + /// MODIFIERS /// /// @notice Prevents to update a market not created yet. @@ -34,6 +37,24 @@ abstract contract MorphoUtils is MorphoStorage { _; } + /// @notice Prevents a user to trigger a function when market is not created or paused. + /// @param _poolToken The address of the market to check. + modifier isMarketCreatedAndNotPaused(address _poolToken) { + Types.MarketStatus memory marketStatus_ = marketStatus[_poolToken]; + if (!marketStatus_.isCreated) revert MarketNotCreated(); + if (marketStatus_.isPaused) revert MarketPaused(); + _; + } + + /// @notice Prevents a user to trigger a function when market is not created or paused or partial paused. + /// @param _poolToken The address of the market to check. + modifier isMarketCreatedAndNotPausedNorPartiallyPaused(address _poolToken) { + Types.MarketStatus memory marketStatus_ = marketStatus[_poolToken]; + if (!marketStatus_.isCreated) revert MarketNotCreated(); + if (marketStatus_.isPaused || marketStatus_.isPartiallyPaused) revert MarketPaused(); + _; + } + /// EXTERNAL /// /// @notice Returns all markets entered by a given user. diff --git a/contracts/compound/PositionsManager.sol b/contracts/compound/PositionsManager.sol index d62ff2954..5436931ed 100644 --- a/contracts/compound/PositionsManager.sol +++ b/contracts/compound/PositionsManager.sol @@ -95,11 +95,6 @@ contract PositionsManager is IPositionsManager, MatchingEngine { uint256 _amountSeized ); - /// @notice Emitted when the peer-to-peer deltas are increased by the governance. - /// @param _poolToken The address of the market on which the deltas were increased. - /// @param _amount The amount that has been added to the deltas (in underlying). - event P2PDeltasIncreased(address indexed _poolToken, uint256 _amount); - /// @notice Emitted when the borrow peer-to-peer delta is updated. /// @param _poolToken The address of the market. /// @param _p2pBorrowDelta The borrow peer-to-peer delta after update. @@ -161,24 +156,6 @@ contract PositionsManager is IPositionsManager, MatchingEngine { /// @notice Thrown when a user tries to repay its debt after borrowing in the same block. error SameBlockBorrowRepay(); - /// @notice Thrown when the supply is paused. - error SupplyPaused(); - - /// @notice Thrown when the borrow is paused. - error BorrowPaused(); - - /// @notice Thrown when the withdraw is paused. - error WithdrawPaused(); - - /// @notice Thrown when the repay is paused. - error RepayPaused(); - - /// @notice Thrown when the liquidation on this asset as collateral is paused. - error LiquidateCollateralPaused(); - - /// @notice Thrown when the liquidation on this asset as debt is paused. - error LiquidateBorrowPaused(); - /// STRUCTS /// // Struct to avoid stack too deep. @@ -218,7 +195,6 @@ contract PositionsManager is IPositionsManager, MatchingEngine { uint256 supplyBalance; uint256 borrowedPrice; uint256 amountToSeize; - uint256 closeFactor; } /// LOGIC /// @@ -238,11 +214,8 @@ contract PositionsManager is IPositionsManager, MatchingEngine { ) external { if (_onBehalf == address(0)) revert AddressIsZero(); if (_amount == 0) revert AmountIsZero(); - Types.MarketStatus memory market = marketStatus[_poolToken]; - if (!market.isCreated) revert MarketNotCreated(); - if (market.isSupplyPaused) revert SupplyPaused(); - _updateP2PIndexes(_poolToken); + _enterMarketIfNeeded(_poolToken, _onBehalf); ERC20 underlyingToken = _getUnderlying(_poolToken); underlyingToken.safeTransferFrom(_supplier, address(this), _amount); @@ -334,11 +307,8 @@ contract PositionsManager is IPositionsManager, MatchingEngine { uint256 _maxGasForMatching ) external { if (_amount == 0) revert AmountIsZero(); - Types.MarketStatus memory market = marketStatus[_poolToken]; - if (!market.isCreated) revert MarketNotCreated(); - if (market.isBorrowPaused) revert BorrowPaused(); - _updateP2PIndexes(_poolToken); + _enterMarketIfNeeded(_poolToken, msg.sender); lastBorrowBlock[msg.sender] = block.number; @@ -437,9 +407,6 @@ contract PositionsManager is IPositionsManager, MatchingEngine { uint256 _maxGasForMatching ) external { if (_amount == 0) revert AmountIsZero(); - Types.MarketStatus memory market = marketStatus[_poolToken]; - if (!market.isCreated) revert MarketNotCreated(); - if (market.isWithdrawPaused) revert WithdrawPaused(); if (!userMembership[_poolToken][_supplier]) revert UserNotMemberOfMarket(); _updateP2PIndexes(_poolToken); @@ -464,9 +431,6 @@ contract PositionsManager is IPositionsManager, MatchingEngine { uint256 _maxGasForMatching ) external { if (_amount == 0) revert AmountIsZero(); - Types.MarketStatus memory market = marketStatus[_poolToken]; - if (!market.isCreated) revert MarketNotCreated(); - if (market.isRepayPaused) revert RepayPaused(); if (!userMembership[_poolToken][_onBehalf]) revert UserNotMemberOfMarket(); _updateP2PIndexes(_poolToken); @@ -486,13 +450,6 @@ contract PositionsManager is IPositionsManager, MatchingEngine { address _borrower, uint256 _amount ) external { - Types.MarketStatus memory collateralMarket = marketStatus[_poolTokenCollateral]; - if (!collateralMarket.isCreated) revert MarketNotCreated(); - if (collateralMarket.isLiquidateCollateralPaused) revert LiquidateCollateralPaused(); - Types.MarketStatus memory borrowedMarket = marketStatus[_poolTokenBorrowed]; - if (!borrowedMarket.isCreated) revert MarketNotCreated(); - if (borrowedMarket.isLiquidateBorrowPaused) revert LiquidateBorrowPaused(); - if ( !userMembership[_poolTokenBorrowed][_borrower] || !userMembership[_poolTokenCollateral][_borrower] @@ -501,17 +458,12 @@ contract PositionsManager is IPositionsManager, MatchingEngine { _updateP2PIndexes(_poolTokenBorrowed); _updateP2PIndexes(_poolTokenCollateral); - LiquidateVars memory vars; - if (borrowedMarket.isDeprecated) - vars.closeFactor = WAD; // Allow liquidation of the whole debt. - else { - if (!_isLiquidatable(_borrower, address(0), 0, 0)) revert UnauthorisedLiquidate(); - vars.closeFactor = comptroller.closeFactorMantissa(); - } + if (!_isLiquidatable(_borrower, address(0), 0, 0)) revert UnauthorisedLiquidate(); + LiquidateVars memory vars; vars.borrowBalance = _getUserBorrowBalanceInOf(_poolTokenBorrowed, _borrower); - if (_amount > vars.borrowBalance.mul(vars.closeFactor)) + if (_amount > vars.borrowBalance.mul(comptroller.closeFactorMantissa())) revert AmountAboveWhatAllowedToRepay(); // Same mechanism as Compound. Liquidator cannot repay more than part of the debt (cf close factor on Compound). _safeRepayLogic(_poolTokenBorrowed, msg.sender, _borrower, _amount, 0); @@ -541,45 +493,6 @@ contract PositionsManager is IPositionsManager, MatchingEngine { ); } - /// @notice Implements increaseP2PDeltas logic. - /// @dev The current Morpho supply on the pool might not be enough to borrow `_amount` before resupplying it. - /// In this case, consider calling this function multiple times. - /// @param _poolToken The address of the market on which to create deltas. - /// @param _amount The amount to add to the deltas (in underlying). - function increaseP2PDeltasLogic(address _poolToken, uint256 _amount) external { - _updateP2PIndexes(_poolToken); - - Types.Delta storage deltas = deltas[_poolToken]; - Types.Delta memory deltasMem = deltas; - Types.LastPoolIndexes memory lastPoolIndexes = lastPoolIndexes[_poolToken]; - uint256 p2pSupplyIndex = p2pSupplyIndex[_poolToken]; - uint256 p2pBorrowIndex = p2pBorrowIndex[_poolToken]; - - _amount = Math.min( - _amount, - Math.min( - deltasMem.p2pSupplyAmount.mul(p2pSupplyIndex).safeSub( - deltasMem.p2pSupplyDelta.mul(lastPoolIndexes.lastSupplyPoolIndex) - ), - deltasMem.p2pBorrowAmount.mul(p2pBorrowIndex).safeSub( - deltasMem.p2pBorrowDelta.mul(lastPoolIndexes.lastBorrowPoolIndex) - ) - ) - ); - - deltasMem.p2pSupplyDelta += _amount.div(lastPoolIndexes.lastSupplyPoolIndex); - deltas.p2pSupplyDelta = deltasMem.p2pSupplyDelta; - deltasMem.p2pBorrowDelta += _amount.div(lastPoolIndexes.lastBorrowPoolIndex); - deltas.p2pBorrowDelta = deltasMem.p2pBorrowDelta; - emit P2PSupplyDeltaUpdated(_poolToken, deltasMem.p2pSupplyDelta); - emit P2PBorrowDeltaUpdated(_poolToken, deltasMem.p2pBorrowDelta); - - _borrowFromPool(_poolToken, _amount); - _supplyToPool(_poolToken, _getUnderlying(_poolToken), _amount); - - emit P2PDeltasIncreased(_poolToken, _amount); - } - /// INTERNAL /// /// @dev Implements withdraw logic without security checks. @@ -841,9 +754,8 @@ contract PositionsManager is IPositionsManager, MatchingEngine { // No need to subtract p2pBorrowDelta as it is zero. vars.feeToRepay = CompoundMath.safeSub( delta.p2pBorrowAmount.mul(vars.p2pBorrowIndex), - delta.p2pSupplyAmount.mul(vars.p2pSupplyIndex).safeSub( - delta.p2pSupplyDelta.mul(ICToken(_poolToken).exchangeRateStored()) - ) + (delta.p2pSupplyAmount.mul(vars.p2pSupplyIndex) - + delta.p2pSupplyDelta.mul(ICToken(_poolToken).exchangeRateStored())) ); if (vars.feeToRepay > 0) { diff --git a/contracts/compound/interfaces/IIncentivesVault.sol b/contracts/compound/interfaces/IIncentivesVault.sol index c28a2b7da..faa7ced89 100644 --- a/contracts/compound/interfaces/IIncentivesVault.sol +++ b/contracts/compound/interfaces/IIncentivesVault.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GNU AGPLv3 -pragma solidity >=0.8.0; +pragma solidity ^0.8.0; import "./IOracle.sol"; diff --git a/contracts/compound/interfaces/IInterestRatesManager.sol b/contracts/compound/interfaces/IInterestRatesManager.sol index 5d7e23306..c429048e6 100644 --- a/contracts/compound/interfaces/IInterestRatesManager.sol +++ b/contracts/compound/interfaces/IInterestRatesManager.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GNU AGPLv3 -pragma solidity >=0.8.0; +pragma solidity ^0.8.0; interface IInterestRatesManager { function updateP2PIndexes(address _marketAddress) external; diff --git a/contracts/compound/interfaces/ILens.sol b/contracts/compound/interfaces/ILens.sol index 9bf0da9a3..eeb03697b 100644 --- a/contracts/compound/interfaces/ILens.sol +++ b/contracts/compound/interfaces/ILens.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GNU AGPLv3 -pragma solidity >=0.8.0; +pragma solidity ^0.8.0; import "./compound/ICompound.sol"; import "./IRewardsManager.sol"; diff --git a/contracts/compound/interfaces/IMorpho.sol b/contracts/compound/interfaces/IMorpho.sol index 510f1b6e1..63181f2d5 100644 --- a/contracts/compound/interfaces/IMorpho.sol +++ b/contracts/compound/interfaces/IMorpho.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GNU AGPLv3 -pragma solidity >=0.8.0; +pragma solidity ^0.8.0; import "./IInterestRatesManager.sol"; import "./IPositionsManager.sol"; @@ -53,12 +53,12 @@ interface IMorpho { function setInterestRatesManager(IInterestRatesManager _interestRatesManager) external; function setTreasuryVault(address _treasuryVault) external; function setDustThreshold(uint256 _dustThreshold) external; - function setIsP2PDisabled(address _poolToken, bool _p2pDisabled) external; + function setP2PDisabled(address _poolToken, bool _p2pDisabled) external; function setReserveFactor(address _poolToken, uint256 _newReserveFactor) external; function setP2PIndexCursor(address _poolToken, uint16 _p2pIndexCursor) external; - function setIsPausedForAllMarkets(bool _isPaused) external; - function setPauseStatus(address _poolToken, bool _isPaused) external; - function setPartialPauseStatus(address _poolToken, bool _isPaused) external; + function setPauseStatusForAllMarkets(bool _newStatus) external; + function setPauseStatus(address _poolToken, bool _newStatus) external; + function setPartialPauseStatus(address _poolToken, bool _newStatus) external; function setPauseStatus(address _poolToken) external; function setPartialPauseStatus(address _poolToken) external; function claimToTreasury(address[] calldata _poolTokens, uint256[] calldata _amounts) external; diff --git a/contracts/compound/interfaces/IOracle.sol b/contracts/compound/interfaces/IOracle.sol index 57ad8744a..22f465867 100644 --- a/contracts/compound/interfaces/IOracle.sol +++ b/contracts/compound/interfaces/IOracle.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GNU AGPLv3 -pragma solidity >=0.8.0; +pragma solidity ^0.8.0; interface IOracle { function consult(uint256 _amountIn) external returns (uint256); diff --git a/contracts/compound/interfaces/IPositionsManager.sol b/contracts/compound/interfaces/IPositionsManager.sol index aa3cc8a97..448dc1466 100644 --- a/contracts/compound/interfaces/IPositionsManager.sol +++ b/contracts/compound/interfaces/IPositionsManager.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GNU AGPLv3 -pragma solidity >=0.8.0; +pragma solidity ^0.8.0; interface IPositionsManager { function supplyLogic( @@ -38,6 +38,4 @@ interface IPositionsManager { address _borrower, uint256 _amount ) external; - - function increaseP2PDeltasLogic(address _poolToken, uint256 _amount) external; } diff --git a/contracts/compound/interfaces/IRewardsManager.sol b/contracts/compound/interfaces/IRewardsManager.sol index e39c1d912..261078b18 100644 --- a/contracts/compound/interfaces/IRewardsManager.sol +++ b/contracts/compound/interfaces/IRewardsManager.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GNU AGPLv3 -pragma solidity >=0.8.0; +pragma solidity ^0.8.0; import "./compound/ICompound.sol"; diff --git a/contracts/compound/interfaces/IWETH.sol b/contracts/compound/interfaces/IWETH.sol index 099e4d151..e82bff47e 100644 --- a/contracts/compound/interfaces/IWETH.sol +++ b/contracts/compound/interfaces/IWETH.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GNU AGPLv3 -pragma solidity >=0.8.0; +pragma solidity ^0.8.0; interface IWETH { function deposit() external payable; diff --git a/contracts/compound/interfaces/compound/ICompound.sol b/contracts/compound/interfaces/compound/ICompound.sol index 419738551..6f7678880 100644 --- a/contracts/compound/interfaces/compound/ICompound.sol +++ b/contracts/compound/interfaces/compound/ICompound.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GNU AGPLv3 -pragma solidity >=0.8.0; +pragma solidity ^0.8.0; interface ICEth { function accrueInterest() external returns (uint256); diff --git a/contracts/compound/lens/MarketsLens.sol b/contracts/compound/lens/MarketsLens.sol index b2954804a..5119007f9 100644 --- a/contracts/compound/lens/MarketsLens.sol +++ b/contracts/compound/lens/MarketsLens.sol @@ -24,12 +24,7 @@ abstract contract MarketsLens is RatesLens { /// @return true if the market is created and not paused, otherwise false. function isMarketCreatedAndNotPaused(address _poolToken) external view returns (bool) { Types.MarketStatus memory marketStatus = morpho.marketStatus(_poolToken); - return - marketStatus.isCreated && - !(marketStatus.isSupplyPaused && - marketStatus.isBorrowPaused && - marketStatus.isWithdrawPaused && - marketStatus.isRepayPaused); + return marketStatus.isCreated && !marketStatus.isPaused; } /// @notice Checks if a market is created and not paused or partially paused. @@ -41,8 +36,7 @@ abstract contract MarketsLens is RatesLens { returns (bool) { Types.MarketStatus memory marketStatus = morpho.marketStatus(_poolToken); - return - marketStatus.isCreated && !(marketStatus.isSupplyPaused && marketStatus.isBorrowPaused); + return marketStatus.isCreated && !marketStatus.isPaused && !marketStatus.isPartiallyPaused; } /// @notice Returns all created markets. @@ -143,12 +137,8 @@ abstract contract MarketsLens is RatesLens { Types.MarketStatus memory marketStatus = morpho.marketStatus(_poolToken); isCreated = marketStatus.isCreated; p2pDisabled = morpho.p2pDisabled(_poolToken); - isPaused = - marketStatus.isSupplyPaused && - marketStatus.isBorrowPaused && - marketStatus.isWithdrawPaused && - marketStatus.isRepayPaused; - isPartiallyPaused = marketStatus.isSupplyPaused && marketStatus.isBorrowPaused; + isPaused = marketStatus.isPaused; + isPartiallyPaused = marketStatus.isPartiallyPaused; Types.MarketParameters memory marketParams = morpho.marketParameters(_poolToken); reserveFactor = marketParams.reserveFactor; diff --git a/contracts/compound/libraries/Types.sol b/contracts/compound/libraries/Types.sol index 00ba2cce8..702abc108 100644 --- a/contracts/compound/libraries/Types.sol +++ b/contracts/compound/libraries/Types.sol @@ -70,12 +70,7 @@ library Types { struct MarketStatus { bool isCreated; // Whether or not this market is created. - bool isSupplyPaused; // Whether the supply is paused or not. - bool isBorrowPaused; // Whether the borrow is paused or not - bool isWithdrawPaused; // Whether the withdraw is paused or not. Note that a "withdraw" is still possible using a liquidation (if not paused). - bool isRepayPaused; // Whether the repay is paused or not. - bool isLiquidateCollateralPaused; // Whether the liquidation on this market as collateral is paused or not. - bool isLiquidateBorrowPaused; // Whether the liquidatation on this market as borrow is paused or not. - bool isDeprecated; // Whether a market is deprecated or not. + bool isPaused; // Whether the market is paused or not (all entry points on Morpho are frozen; supply, borrow, withdraw, repay and liquidate). + bool isPartiallyPaused; // Whether the market is partially paused or not (only supply and borrow are frozen). } } diff --git a/test-foundry/aave-v2/TestBorrow.t.sol b/test-foundry/aave-v2/TestBorrow.t.sol index b2ef3d02c..ce2a653a8 100644 --- a/test-foundry/aave-v2/TestBorrow.t.sol +++ b/test-foundry/aave-v2/TestBorrow.t.sol @@ -231,21 +231,4 @@ contract TestBorrow is TestSetup { hevm.expectRevert(EntryPositionsManager.UnauthorisedBorrow.selector); borrower1.borrow(aDai, (amount * ltv) / 10_000 + 1e9); } - - function testShouldNotBorrowWithDisabledCollateral() public { - uint256 amount = 100 ether; - - borrower1.approve(dai, type(uint256).max); - borrower1.supply(aDai, amount * 10); - - // Give morpho enough of a position size - supplier1.approve(usdc, to6Decimals(amount)); - supplier1.supply(aUsdc, to6Decimals(amount)); - - // Cannot disable collateral without some underlying balance that Morpho has - morpho.setAssetAsCollateral(aDai, false); - - hevm.expectRevert(EntryPositionsManager.UnauthorisedBorrow.selector); - borrower1.borrow(aUsdc, to6Decimals(amount)); - } } diff --git a/test-foundry/aave-v2/TestFees.t.sol b/test-foundry/aave-v2/TestFees.t.sol index cc731ebb2..72cc5771b 100644 --- a/test-foundry/aave-v2/TestFees.t.sol +++ b/test-foundry/aave-v2/TestFees.t.sol @@ -85,6 +85,32 @@ contract TestFees is TestSetup { assertEq(balanceAfter, balanceBefore); } + function testShouldNotClaimFeesIfMarketIsPaused() public { + uint256 balanceBefore = ERC20(dai).balanceOf(address(this)); + _createFeeOnMorpho(1_000); + + // Pause market. + morpho.setPauseStatus(aDai, true); + + morpho.claimToTreasury(aDaiArray, maxAmountArray); + + uint256 balanceAfter = ERC20(dai).balanceOf(address(this)); + assertEq(balanceAfter, balanceBefore); + } + + function testShouldNotClaimFeesIfMarketIsPartiallyPaused() public { + uint256 balanceBefore = ERC20(dai).balanceOf(address(this)); + _createFeeOnMorpho(1_000); + + // Partially pause market. + morpho.setPartialPauseStatus(aDai, true); + + morpho.claimToTreasury(aDaiArray, maxAmountArray); + + uint256 balanceAfter = ERC20(dai).balanceOf(address(this)); + assertEq(balanceAfter, balanceBefore); + } + function testShouldPayFee() public { uint16 reserveFactor = 1_000; uint256 bigAmount = 100_000 ether; diff --git a/test-foundry/aave-v2/TestGovernance.t.sol b/test-foundry/aave-v2/TestGovernance.t.sol index 939a41bbf..a53713658 100644 --- a/test-foundry/aave-v2/TestGovernance.t.sol +++ b/test-foundry/aave-v2/TestGovernance.t.sol @@ -41,19 +41,8 @@ contract TestGovernance is TestSetup { morpho.createMarket(wEth, 0, 10_001); morpho.createMarket(wEth, 1_000, 3_333); - ( - address underlyingToken, - uint16 reserveFactor, - uint256 p2pIndexCursor, - , - , - , - , - , - , - , - - ) = morpho.market(aWeth); + (address underlyingToken, uint16 reserveFactor, uint256 p2pIndexCursor, , , , ) = morpho + .market(aWeth); assertEq(reserveFactor, 1_000); assertEq(p2pIndexCursor, 3_333); assertTrue(underlyingToken == wEth); @@ -73,13 +62,16 @@ contract TestGovernance is TestSetup { function testReserveFactorShouldBeUpdatedWithRightValue() public { morpho.setReserveFactor(aDai, 1111); - (, uint16 reserveFactor, , , , , , , , , ) = morpho.market(aDai); + (, uint16 reserveFactor, , , , , ) = morpho.market(aDai); assertEq(reserveFactor, 1111); } function testShouldCreateMarketWithTheRightValues() public { morpho.createMarket(wEth, 3_333, 0); + (, , , bool isCreated, , , ) = morpho.market(aWeth); + + assertTrue(isCreated); assertEq(morpho.p2pSupplyIndex(aWeth), WadRayMath.RAY); assertEq(morpho.p2pBorrowIndex(aWeth), WadRayMath.RAY); } @@ -121,16 +113,16 @@ contract TestGovernance is TestSetup { } function testOnlyOwnerShouldFlipMarketStrategy() public { - hevm.prank(address(supplier1)); hevm.expectRevert("Ownable: caller is not the owner"); - morpho.setIsP2PDisabled(aDai, true); + hevm.prank(address(supplier1)); + morpho.setP2PDisabledStatus(aDai, true); - hevm.prank(address(supplier2)); hevm.expectRevert("Ownable: caller is not the owner"); - morpho.setIsP2PDisabled(aDai, true); + hevm.prank(address(supplier2)); + morpho.setP2PDisabledStatus(aDai, true); - morpho.setIsP2PDisabled(aDai, true); - (, , , bool isP2PDisabled, , , , , , , ) = morpho.market(aDai); + morpho.setP2PDisabledStatus(aDai, true); + (, , , , , , bool isP2PDisabled) = morpho.market(aDai); assertTrue(isP2PDisabled); } @@ -195,209 +187,20 @@ contract TestGovernance is TestSetup { assertEq(address(morpho.treasuryVault()), treasuryVaultV2); } - function testOnlyOwnerCanSetIsClaimRewardsPaused() public { + function testOnlyOwnerCanSetClaimRewardsStatus() public { hevm.prank(address(0)); hevm.expectRevert("Ownable: caller is not the owner"); - morpho.setIsClaimRewardsPaused(true); + morpho.setClaimRewardsPauseStatus(true); - morpho.setIsClaimRewardsPaused(true); + morpho.setClaimRewardsPauseStatus(true); assertTrue(morpho.isClaimRewardsPaused()); } - function testOnlyOwnerShouldSetDeprecatedMarket() public { - hevm.prank(address(supplier1)); - hevm.expectRevert("Ownable: caller is not the owner"); - morpho.setIsDeprecated(aDai, true); - - hevm.prank(address(supplier2)); - hevm.expectRevert("Ownable: caller is not the owner"); - morpho.setIsDeprecated(aDai, true); - - morpho.setIsDeprecated(aDai, true); - (, , , , , , , , , , bool isDeprecated) = morpho.market(aDai); - assertTrue(isDeprecated); - - morpho.setIsDeprecated(aDai, false); - (, , , , , , , , , , isDeprecated) = morpho.market(aDai); - assertFalse(isDeprecated); - } - function testOnlyOwnerCanSetPauseStatusForAllMarkets() public { hevm.prank(address(0)); hevm.expectRevert("Ownable: caller is not the owner"); - morpho.setIsPausedForAllMarkets(true); - - morpho.setIsPausedForAllMarkets(true); - } - - function testOnlyOwnerCanIncreaseP2PDeltas() public { - hevm.prank(address(supplier1)); - hevm.expectRevert("Ownable: caller is not the owner"); - morpho.increaseP2PDeltas(aDai, 0); - - supplier1.approve(dai, type(uint256).max); - supplier1.supply(aDai, 1_000 ether); - supplier1.borrow(aDai, 2 ether); - - morpho.increaseP2PDeltas(aDai, 1 ether); - } - - function testShouldIncreaseP2PDeltasWhenMarketNotCreated() public { - hevm.expectRevert(abi.encodeWithSignature("MarketNotCreated()")); - morpho.increaseP2PDeltas(address(1), 0); - } - - function testIncreaseP2PDeltas() public { - uint256 supplyAmount = 100 ether; - uint256 borrowAmount = 50 ether; - uint256 increaseDeltaAmount = 30 ether; - - supplier1.approve(usdc, type(uint256).max); - supplier1.supply(aUsdc, to6Decimals(supplyAmount)); - supplier1.approve(dai, supplyAmount); - supplier1.supply(aDai, supplyAmount); - supplier1.borrow(aDai, borrowAmount); - - morpho.increaseP2PDeltas(aDai, increaseDeltaAmount); - - (uint256 p2pSupplyDelta, uint256 p2pBorrowDelta, , ) = morpho.deltas(aDai); - - assertEq(p2pSupplyDelta, increaseDeltaAmount.rayDiv(pool.getReserveNormalizedIncome(dai))); - assertEq( - p2pBorrowDelta, - increaseDeltaAmount.rayDiv(pool.getReserveNormalizedVariableDebt(dai)) - ); - assertApproxEqRel( - IAToken(aDai).balanceOf(address(morpho)), - supplyAmount - borrowAmount + increaseDeltaAmount, - 1e8 - ); - assertApproxEqRel( - IVariableDebtToken(variableDebtDai).balanceOf(address(morpho)), - increaseDeltaAmount, - 1e8 - ); - } - - function testIncreaseP2PDeltasMoreThanWhatIsPossibleSupply() public { - uint256 supplyAmount = 100 ether; - uint256 borrowAmount = 50 ether; - uint256 deltaAmount = 25 ether; - uint256 increaseDeltaAmount = 80 ether; - - supplier1.approve(usdc, type(uint256).max); - supplier1.supply(aUsdc, to6Decimals(supplyAmount)); - supplier1.approve(dai, type(uint256).max); - supplier1.supply(aDai, supplyAmount); - supplier1.borrow(aDai, borrowAmount); - _setDefaultMaxGasForMatching(0, 0, 0, 0); - hevm.roll(block.number + 1); - supplier1.repay(aDai, deltaAmount); // Creates a peer-to-peer supply delta. - - morpho.increaseP2PDeltas(aDai, increaseDeltaAmount); - - (uint256 p2pSupplyDelta, uint256 p2pBorrowDelta, , ) = morpho.deltas(aDai); - - assertApproxEqRel( - p2pSupplyDelta, - borrowAmount.rayDiv(pool.getReserveNormalizedIncome(dai)), - 1e12 - ); - assertApproxEqRel( - p2pBorrowDelta, - (borrowAmount - deltaAmount).rayDiv(pool.getReserveNormalizedVariableDebt(dai)), - 1e12 - ); - assertApproxEqRel(IAToken(aDai).balanceOf(address(morpho)), supplyAmount, 1e12); - assertApproxEqRel( - IVariableDebtToken(variableDebtDai).balanceOf(address(morpho)), - borrowAmount - deltaAmount, - 1e12 - ); - } - - function testIncreaseP2PDeltasMoreThanWhatIsPossibleBorrow() public { - uint256 supplyAmount = 100 ether; - uint256 borrowAmount = 50 ether; - uint256 deltaAmount = 25 ether; - uint256 increaseDeltaAmount = 80 ether; - - supplier1.approve(usdc, type(uint256).max); - supplier1.supply(aUsdc, to6Decimals(supplyAmount)); - supplier1.approve(dai, supplyAmount); - supplier1.supply(aDai, supplyAmount); - supplier1.borrow(aDai, borrowAmount); - _setDefaultMaxGasForMatching(0, 0, 0, 0); - supplier1.withdraw(aDai, supplyAmount - borrowAmount + deltaAmount); // Creates a peer-to-peer borrow delta. - - morpho.increaseP2PDeltas(aDai, increaseDeltaAmount); - - (uint256 p2pSupplyDelta, uint256 p2pBorrowDelta, , ) = morpho.deltas(aDai); - - assertApproxEqRel( - p2pSupplyDelta, - (borrowAmount - deltaAmount).rayDiv(pool.getReserveNormalizedIncome(dai)), - 1e8, - "1" - ); - assertApproxEqRel( - p2pBorrowDelta, - borrowAmount.rayDiv(pool.getReserveNormalizedVariableDebt(dai)), - 1e8, - "2" - ); - assertApproxEqRel(IAToken(aDai).balanceOf(address(morpho)), deltaAmount, 1e8, "3"); - assertApproxEqRel( - IVariableDebtToken(variableDebtDai).balanceOf(address(morpho)), - borrowAmount, - 1e8, - "4" - ); - } - - function testFailCallIncreaseP2PDeltasFromImplementation() public { - exitPositionsManager.increaseP2PDeltasLogic(aDai, 0); - } - - function testOnlyOwnerShouldDisableSupply() public { - vm.expectRevert("Ownable: caller is not the owner"); - supplier1.setIsSupplyPaused(aDai, true); - - morpho.setIsSupplyPaused(aDai, true); - } - - function testOnlyOwnerShouldDisableBorrow() public { - vm.expectRevert("Ownable: caller is not the owner"); - supplier1.setIsBorrowPaused(aDai, true); - - morpho.setIsBorrowPaused(aDai, true); - } - - function testOnlyOwnerShouldDisableWithdraw() public { - vm.expectRevert("Ownable: caller is not the owner"); - supplier1.setIsWithdrawPaused(aDai, true); - - morpho.setIsWithdrawPaused(aDai, true); - } - - function testOnlyOwnerShouldDisableRepay() public { - vm.expectRevert("Ownable: caller is not the owner"); - supplier1.setIsRepayPaused(aDai, true); - - morpho.setIsRepayPaused(aDai, true); - } - - function testOnlyOwnerShouldDisableLiquidateOnCollateral() public { - vm.expectRevert("Ownable: caller is not the owner"); - supplier1.setIsLiquidateCollateralPaused(aDai, true); - - morpho.setIsLiquidateCollateralPaused(aDai, true); - } - - function testOnlyOwnerShouldDisableLiquidateOnBorrow() public { - vm.expectRevert("Ownable: caller is not the owner"); - supplier1.setIsLiquidateBorrowPaused(aDai, true); + morpho.setPauseStatusForAllMarkets(true); - morpho.setIsLiquidateBorrowPaused(aDai, true); + morpho.setPauseStatusForAllMarkets(true); } } diff --git a/test-foundry/aave-v2/TestLens.t.sol b/test-foundry/aave-v2/TestLens.t.sol index 1decd8bc7..c87e48b40 100644 --- a/test-foundry/aave-v2/TestLens.t.sol +++ b/test-foundry/aave-v2/TestLens.t.sol @@ -696,105 +696,54 @@ contract TestLens is TestSetup { } function testGetMarketConfiguration() public { - { - ( - address underlying, - bool isCreated, - bool isP2PDisabled, - , - , - uint16 reserveFactor, - uint16 p2pIndexCursor, - , - , - , - - ) = lens.getMarketConfiguration(aDai); - assertEq(underlying, dai); - - Types.Market memory expectedConfig; - ( - expectedConfig.underlyingToken, - expectedConfig.reserveFactor, - expectedConfig.p2pIndexCursor, - expectedConfig.isP2PDisabled, - , - , - , - , - , - , - - ) = morpho.market(aDai); - - assertTrue(isCreated == (expectedConfig.underlyingToken != address(0))); - assertTrue(isP2PDisabled == expectedConfig.isP2PDisabled); - assertEq(reserveFactor, expectedConfig.reserveFactor); - assertEq(p2pIndexCursor, expectedConfig.p2pIndexCursor); - } - { - (address underlying, , , bool isPaused, bool isPartiallyPaused, , , , , , ) = lens - .getMarketConfiguration(aDai); - assertEq(underlying, dai); - - Types.Market memory expectedConfig; - ( - , - , - , - , - expectedConfig.isSupplyPaused, - expectedConfig.isBorrowPaused, - expectedConfig.isWithdrawPaused, - expectedConfig.isRepayPaused, - expectedConfig.isLiquidateCollateralPaused, - expectedConfig.isLiquidateBorrowPaused, - - ) = morpho.market(aDai); - - assertTrue( - isPaused == - (expectedConfig.isSupplyPaused && - expectedConfig.isBorrowPaused && - expectedConfig.isWithdrawPaused && - expectedConfig.isRepayPaused && - expectedConfig.isLiquidateCollateralPaused && - expectedConfig.isLiquidateBorrowPaused) - ); - assertTrue( - isPartiallyPaused == - (expectedConfig.isSupplyPaused && expectedConfig.isBorrowPaused) - ); - } + ( + address underlying, + bool isCreated, + bool isP2PDisabled, + bool isPaused, + bool isPartiallyPaused, + uint16 reserveFactor, + uint16 p2pIndexCursor, + uint256 ltv, + uint256 liquidationThreshold, + uint256 liquidationBonus, + uint256 decimals + ) = lens.getMarketConfiguration(aDai); + assertEq(underlying, dai); - { - ( - , - , - , - , - , - , - , - uint256 ltv, - uint256 liquidationThreshold, - uint256 liquidationBonus, - uint256 decimals - ) = lens.getMarketConfiguration(aDai); - - ( - uint256 expectedLtv, - uint256 expectedLiquidationThreshold, - uint256 expectedLiquidationBonus, - uint256 expectedDecimals, - - ) = pool.getConfiguration(dai).getParamsMemory(); - - assertEq(ltv, expectedLtv); - assertEq(liquidationThreshold, expectedLiquidationThreshold); - assertEq(liquidationBonus, expectedLiquidationBonus); - assertEq(decimals, expectedDecimals); - } + ( + , + , + , + bool isCreated_, + bool isPaused_, + bool isPartiallyPaused_, + bool isP2PDisabled_ + ) = morpho.market(aDai); + + assertEq(isCreated, isCreated_); + assertEq(isP2PDisabled, isP2PDisabled_); + + assertEq(isPaused, isPaused_); + assertEq(isPartiallyPaused, isPartiallyPaused_); + (, uint16 expectedReserveFactor, uint16 expectedP2PIndexCursor, , , , ) = morpho.market( + aDai + ); + assertEq(reserveFactor, expectedReserveFactor); + assertEq(p2pIndexCursor, expectedP2PIndexCursor); + + ( + uint256 expectedLtv, + uint256 expectedLiquidationThreshold, + uint256 expectedLiquidationBonus, + uint256 expectedDecimals, + + ) = pool.getConfiguration(dai).getParamsMemory(); + + assertEq(ltv, expectedLtv); + assertEq(liquidationThreshold, expectedLiquidationThreshold); + assertEq(liquidationBonus, expectedLiquidationBonus); + assertEq(decimals, expectedDecimals); } function testGetOutdatedIndexes() public { @@ -858,6 +807,51 @@ contract TestLens is TestSetup { ); } + function testGetUpdatedP2PIndexesWithSupplyDelta() public { + _createSupplyDelta(); + hevm.warp(block.timestamp + 365 days); + (uint256 newP2PSupplyIndex, uint256 newP2PBorrowIndex, , ) = lens.getIndexes(aDai); + + morpho.updateIndexes(aDai); + assertApproxEqAbs(newP2PBorrowIndex, morpho.p2pBorrowIndex(aDai), 1); + assertApproxEqAbs(newP2PSupplyIndex, morpho.p2pSupplyIndex(aDai), 1); + } + + function testGetUpdatedP2PIndexesWithBorrowDelta() public { + _createBorrowDelta(); + hevm.warp(block.timestamp + 365 days); + (uint256 newP2PSupplyIndex, uint256 newP2PBorrowIndex, , ) = lens.getIndexes(aDai); + + morpho.updateIndexes(aDai); + assertApproxEqAbs(newP2PBorrowIndex, morpho.p2pBorrowIndex(aDai), 1); + assertApproxEqAbs(newP2PSupplyIndex, morpho.p2pSupplyIndex(aDai), 1); + } + + function testGetUpdatedP2PSupplyIndex() public { + hevm.warp(block.timestamp + 365 days); + uint256 newP2PSupplyIndex = lens.getCurrentP2PSupplyIndex(aDai); + + morpho.updateIndexes(aDai); + assertApproxEqAbs(newP2PSupplyIndex, morpho.p2pSupplyIndex(aDai), 1); + } + + function testGetUpdatedP2PSupplyIndexWithDelta() public { + _createSupplyDelta(); + hevm.warp(block.timestamp + 365 days); + uint256 newP2PSupplyIndex = lens.getCurrentP2PSupplyIndex(aDai); + + morpho.updateIndexes(aDai); + assertEq(newP2PSupplyIndex, morpho.p2pSupplyIndex(aDai)); + } + + function testGetUpdatedP2PBorrowIndex() public { + hevm.warp(block.timestamp + 365 days); + uint256 newP2PBorrowIndex = lens.getCurrentP2PBorrowIndex(aDai); + + morpho.updateIndexes(aDai); + assertApproxEqAbs(newP2PBorrowIndex, morpho.p2pBorrowIndex(aDai), 1); + } + function testGetUpdatedIndexesOnStEth() public { createMarket(aStEth); @@ -906,51 +900,6 @@ contract TestLens is TestSetup { ); } - function testGetUpdatedP2PIndexesWithSupplyDelta() public { - _createSupplyDelta(); - hevm.warp(block.timestamp + 365 days); - (uint256 newP2PSupplyIndex, uint256 newP2PBorrowIndex, , ) = lens.getIndexes(aDai); - - morpho.updateIndexes(aDai); - assertApproxEqAbs(newP2PBorrowIndex, morpho.p2pBorrowIndex(aDai), 1); - assertApproxEqAbs(newP2PSupplyIndex, morpho.p2pSupplyIndex(aDai), 1); - } - - function testGetUpdatedP2PIndexesWithBorrowDelta() public { - _createBorrowDelta(); - hevm.warp(block.timestamp + 365 days); - (uint256 newP2PSupplyIndex, uint256 newP2PBorrowIndex, , ) = lens.getIndexes(aDai); - - morpho.updateIndexes(aDai); - assertApproxEqAbs(newP2PBorrowIndex, morpho.p2pBorrowIndex(aDai), 1); - assertApproxEqAbs(newP2PSupplyIndex, morpho.p2pSupplyIndex(aDai), 1); - } - - function testGetUpdatedP2PSupplyIndex() public { - hevm.warp(block.timestamp + 365 days); - uint256 newP2PSupplyIndex = lens.getCurrentP2PSupplyIndex(aDai); - - morpho.updateIndexes(aDai); - assertApproxEqAbs(newP2PSupplyIndex, morpho.p2pSupplyIndex(aDai), 1); - } - - function testGetUpdatedP2PSupplyIndexWithDelta() public { - _createSupplyDelta(); - hevm.warp(block.timestamp + 365 days); - uint256 newP2PSupplyIndex = lens.getCurrentP2PSupplyIndex(aDai); - - morpho.updateIndexes(aDai); - assertEq(newP2PSupplyIndex, morpho.p2pSupplyIndex(aDai)); - } - - function testGetUpdatedP2PBorrowIndex() public { - hevm.warp(block.timestamp + 365 days); - uint256 newP2PBorrowIndex = lens.getCurrentP2PBorrowIndex(aDai); - - morpho.updateIndexes(aDai); - assertApproxEqAbs(newP2PBorrowIndex, morpho.p2pBorrowIndex(aDai), 1); - } - function testGetUpdatedP2PBorrowIndexWithDelta() public { _createBorrowDelta(); hevm.warp(block.timestamp + 365 days); @@ -1498,4 +1447,46 @@ contract TestLens is TestSetup { assertEq(amounts.ethPoolSupply, expectedEthUSDOnPool / 2, "unexpected eth pool supply"); assertEq(amounts.ethPoolBorrow, 0, "unexpected eth pool borrow"); } + + function testBalanceShouldBeReflectedWhenStethSlashed() public { + createMarket(aStEth); + + deal(address(supplier1), 1_000 ether); + uint256 totalEthBalance = address(supplier1).balance; + uint256 totalBalance = totalEthBalance / 2; + vm.prank(address(supplier1)); + ILido(stEth).submit{value: totalBalance}(address(0)); + + totalBalance = ERC20(stEth).balanceOf(address(supplier1)); + + supplier1.approve(stEth, type(uint256).max); + supplier1.supply(aStEth, totalBalance); + + (uint256 p2pBalanceBefore, uint256 poolBalanceBefore, ) = lens.getCurrentSupplyBalanceInOf( + aStEth, + address(supplier1) + ); + + // Update the beacon balance to slash. + // bytes32 internal constant BEACON_BALANCE_POSITION = keccak256("lido.Lido.beaconBalance"); + uint256 beaconBalanceBefore = uint256(vm.load(stEth, keccak256("lido.Lido.beaconBalance"))); + vm.store(stEth, keccak256("lido.Lido.beaconBalance"), bytes32(beaconBalanceBefore / 10)); + uint256 beaconBalanceAfter = uint256(vm.load(stEth, keccak256("lido.Lido.beaconBalance"))); + assertEq(beaconBalanceBefore / 10, beaconBalanceAfter); + + (uint256 p2pBalanceAfter, uint256 poolBalanceAfter, ) = lens.getCurrentSupplyBalanceInOf( + aStEth, + address(supplier1) + ); + assertEq(p2pBalanceBefore, 0, "P2P balance before"); + assertEq(p2pBalanceAfter, 0, "P2P balance after"); + assertApproxEqAbs(poolBalanceBefore, totalBalance, 1, "pool balance before"); + // Not exact because the total assets of stEth includes other variables + assertApproxEqAbs( + poolBalanceAfter, + totalBalance / 10, + totalBalance / 50, + "pool balance before" + ); + } } diff --git a/test-foundry/aave-v2/TestLiquidate.t.sol b/test-foundry/aave-v2/TestLiquidate.t.sol index 8e99efecf..2157e2b6a 100644 --- a/test-foundry/aave-v2/TestLiquidate.t.sol +++ b/test-foundry/aave-v2/TestLiquidate.t.sol @@ -26,52 +26,6 @@ contract TestLiquidate is TestSetup { liquidator.liquidate(aDai, aUsdc, address(borrower1), toRepay); } - function testLiquidateWhenMarketDeprecated() public { - uint256 amount = 10_000 ether; - uint256 collateral = to6Decimals(2 * amount); - - morpho.setIsDeprecated(aDai, true); - - borrower1.approve(usdc, address(morpho), collateral); - borrower1.supply(aUsdc, collateral); - borrower1.borrow(aDai, amount); - - (, uint256 supplyOnPoolBefore) = morpho.supplyBalanceInOf(aUsdc, address(borrower1)); - (, uint256 borrowOnPoolBefore) = morpho.borrowBalanceInOf(aDai, address(borrower1)); - - // Liquidate - uint256 toRepay = borrowOnPoolBefore.rayMul(pool.getReserveNormalizedVariableDebt(dai)); // Full liquidation. - User liquidator = borrower3; - liquidator.approve(dai, address(morpho), toRepay); - liquidator.liquidate(aDai, aUsdc, address(borrower1), toRepay); - - (, uint256 supplyOnPoolAfter) = morpho.supplyBalanceInOf(aUsdc, address(borrower1)); - (, uint256 borrowOnPoolAfter) = morpho.borrowBalanceInOf(aDai, address(borrower1)); - - ExitPositionsManager.LiquidateVars memory vars; - (, , vars.liquidationBonus, vars.collateralReserveDecimals, ) = pool - .getConfiguration(usdc) - .getParamsMemory(); - uint256 collateralPrice = oracle.getAssetPrice(usdc); - vars.collateralTokenUnit = 10**vars.collateralReserveDecimals; - - { - (, , , vars.borrowedReserveDecimals, ) = pool.getConfiguration(dai).getParamsMemory(); - uint256 borrowedPrice = oracle.getAssetPrice(dai); - vars.borrowedTokenUnit = 10**vars.borrowedReserveDecimals; - - uint256 amountToSeize = (toRepay * borrowedPrice * vars.collateralTokenUnit) / - (vars.borrowedTokenUnit * collateralPrice).percentMul(vars.liquidationBonus); - - uint256 expectedSupplyOnPoolAfter = supplyOnPoolBefore - - amountToSeize.rayDiv(pool.getReserveNormalizedIncome(usdc)); - - assertApproxEqAbs(supplyOnPoolAfter, expectedSupplyOnPoolAfter, 1e10); - } - - assertEq(borrowOnPoolAfter, 0); - } - // A user liquidates a borrower that has not enough collateral to cover for his debt. function testShouldLiquidateUser() public { uint256 collateral = 100_000 ether; diff --git a/test-foundry/aave-v2/TestMarketStrategy.t.sol b/test-foundry/aave-v2/TestMarketStrategy.t.sol index 74fd43182..55cd20409 100644 --- a/test-foundry/aave-v2/TestMarketStrategy.t.sol +++ b/test-foundry/aave-v2/TestMarketStrategy.t.sol @@ -9,7 +9,7 @@ contract TestMarketStrategy is TestSetup { uint256 toBorrow = 500 ether; // Disable peer-to-peer - morpho.setIsP2PDisabled(aDai, true); + morpho.setP2PDisabledStatus(aDai, true); borrower1.approve(usdc, to6Decimals(amount)); borrower1.supply(aUsdc, to6Decimals(amount)); @@ -40,7 +40,7 @@ contract TestMarketStrategy is TestSetup { uint256 toBorrow = 500 ether; // Disable peer-to-peer - morpho.setIsP2PDisabled(aDai, true); + morpho.setP2PDisabledStatus(aDai, true); supplier1.approve(dai, toBorrow); supplier1.supply(aDai, toBorrow); @@ -71,7 +71,7 @@ contract TestMarketStrategy is TestSetup { uint256 toBorrow = 100 ether; // Disable peer-to-peer - morpho.setIsP2PDisabled(aDai, true); + morpho.setP2PDisabledStatus(aDai, true); for (uint256 i = 0; i < 3; i++) { borrowers[i].approve(usdc, to6Decimals(amount)); @@ -106,7 +106,7 @@ contract TestMarketStrategy is TestSetup { uint256 toSupply = 100 ether; // Disable peer-to-peer - morpho.setIsP2PDisabled(aDai, true); + morpho.setP2PDisabledStatus(aDai, true); for (uint256 i = 0; i < 3; i++) { suppliers[i].approve(dai, toSupply); diff --git a/test-foundry/aave-v2/TestP2PDisable.t.sol b/test-foundry/aave-v2/TestP2PDisable.t.sol index 99aca4159..f2689b2c3 100644 --- a/test-foundry/aave-v2/TestP2PDisable.t.sol +++ b/test-foundry/aave-v2/TestP2PDisable.t.sol @@ -29,7 +29,7 @@ contract TestP2PDisable is TestSetup { assertGt(p2pSupplyDelta, 0); // Disable peer-to-peer. - morpho.setIsP2PDisabled(aDai, true); + morpho.setP2PDisabledStatus(aDai, true); // Delta must be reduce to 0. borrower1.borrow(aDai, borrowedAmount); @@ -62,7 +62,7 @@ contract TestP2PDisable is TestSetup { assertGt(p2pBorrowDelta, 0); // Disable peer-to-peer. - morpho.setIsP2PDisabled(aUsdc, true); + morpho.setP2PDisabledStatus(aUsdc, true); // Delta must be reduce to 0. supplier1.supply(aUsdc, to6Decimals(supplyAmount * 2)); diff --git a/test-foundry/aave-v2/TestPausableMarket.t.sol b/test-foundry/aave-v2/TestPausableMarket.t.sol index 6324c9b7d..eb6b9aa3c 100644 --- a/test-foundry/aave-v2/TestPausableMarket.t.sol +++ b/test-foundry/aave-v2/TestPausableMarket.t.sol @@ -8,56 +8,60 @@ contract TestPausableMarket is TestSetup { address[] public aAaveArray = [aAave]; uint256[] public amountArray = [1 ether]; + function testOnlyOwnerShouldTriggerPauseFunction() public { + hevm.expectRevert("Ownable: caller is not the owner"); + supplier1.setPauseStatus(aDai, true); + + morpho.setPauseStatus(aDai, true); + (, , , , bool isPaused, , ) = morpho.market(aDai); + assertTrue(isPaused, "paused is false"); + } + + function testOnlyOwnerShouldTriggerPartialPauseFunction() public { + hevm.expectRevert("Ownable: caller is not the owner"); + supplier1.setPartialPauseStatus(aDai, true); + + morpho.setPartialPauseStatus(aDai, true); + (, , , , , bool isPartiallyPaused, ) = morpho.market(aDai); + assertTrue(isPartiallyPaused, "partial paused is false"); + } + function testAllMarketsPauseUnpause() public { - morpho.setIsPausedForAllMarkets(true); + morpho.setPauseStatusForAllMarkets(true); for (uint256 i; i < pools.length; ++i) { - ( - , - , - , - , - bool isSupplyPaused, - bool isBorrowPaused, - bool isWithdrawPaused, - bool isRepayPaused, - bool isLiquidateCollateralPaused, - bool isLiquidateBorrowPaused, - - ) = morpho.market(pools[i]); - assertTrue(isSupplyPaused); - assertTrue(isBorrowPaused); - assertTrue(isWithdrawPaused); - assertTrue(isRepayPaused); - assertTrue(isLiquidateCollateralPaused); - assertTrue(isLiquidateBorrowPaused); + (, , , , bool isPaused, , ) = morpho.market(pools[i]); + assertTrue(isPaused, "paused is false"); } - morpho.setIsPausedForAllMarkets(false); + morpho.setPauseStatusForAllMarkets(false); for (uint256 i; i < pools.length; ++i) { - ( - , - , - , - , - bool isSupplyPaused, - bool isBorrowPaused, - bool isWithdrawPaused, - bool isRepayPaused, - bool isLiquidateCollateralPaused, - bool isLiquidateBorrowPaused, - - ) = morpho.market(pools[i]); - assertFalse(isSupplyPaused); - assertFalse(isBorrowPaused); - assertFalse(isWithdrawPaused); - assertFalse(isRepayPaused); - assertFalse(isLiquidateCollateralPaused); - assertFalse(isLiquidateBorrowPaused); + (, , , , bool isPaused, , ) = morpho.market(pools[i]); + assertFalse(isPaused, "paused is true"); } } + function testPauseUnpause() public { + morpho.setPauseStatus(aDai, true); + (, , , , bool isPaused, , ) = morpho.market(aDai); + assertTrue(isPaused, "paused is false"); + + morpho.setPauseStatus(aDai, false); + (, , , , isPaused, , ) = morpho.market(aDai); + assertFalse(isPaused, "paused is true"); + } + + function testPartialPausePartialUnpause() public { + morpho.setPartialPauseStatus(aDai, true); + (, , , , , bool isPartiallyPaused, ) = morpho.market(aDai); + assertTrue(isPartiallyPaused, "partial paused is false"); + + morpho.setPartialPauseStatus(aDai, false); + (, , , , , isPartiallyPaused, ) = morpho.market(aDai); + assertFalse(isPartiallyPaused, "partial paused is true"); + } + function testShouldTriggerFunctionsWhenNotPaused() public { uint256 amount = 10_000 ether; uint256 toBorrow = to6Decimals(amount / 2); @@ -88,107 +92,143 @@ contract TestPausableMarket is TestSetup { } function testShouldDisableAllMarketsWhenGloballyPaused() public { - morpho.setIsPausedForAllMarkets(true); + morpho.setPauseStatusForAllMarkets(true); uint256 poolsLength = pools.length; for (uint256 i; i < poolsLength; ++i) { - vm.expectRevert(abi.encodeWithSignature("SupplyPaused()")); + hevm.expectRevert(abi.encodeWithSignature("MarketPaused()")); supplier1.supply(pools[i], 1); - vm.expectRevert(abi.encodeWithSignature("BorrowPaused()")); + hevm.expectRevert(abi.encodeWithSignature("MarketPaused()")); supplier1.borrow(pools[i], 1); - vm.expectRevert(abi.encodeWithSignature("WithdrawPaused()")); + hevm.expectRevert(abi.encodeWithSignature("MarketPaused()")); supplier1.withdraw(pools[i], 1); - vm.expectRevert(abi.encodeWithSignature("RepayPaused()")); + hevm.expectRevert(abi.encodeWithSignature("MarketPaused()")); supplier1.repay(pools[i], 1); - vm.expectRevert(abi.encodeWithSignature("LiquidateCollateralPaused()")); + hevm.expectRevert(abi.encodeWithSignature("MarketPaused()")); supplier1.liquidate(pools[i], pools[0], address(supplier1), 1); + + hevm.expectRevert(abi.encodeWithSignature("MarketPaused()")); + supplier1.liquidate(pools[0], pools[i], address(supplier1), 1); } } - function testPauseSupply() public { + function testShouldDisableMarketWhenPaused() public { uint256 amount = 10_000 ether; - morpho.setIsSupplyPaused(aDai, true); - vm.expectRevert(abi.encodeWithSignature("SupplyPaused()")); + supplier1.approve(dai, 2 * amount); supplier1.supply(aDai, amount); - } - function testPauseBorrow() public { - uint256 amount = 10_000 ether; - morpho.setIsBorrowPaused(aDai, true); + (, uint256 toBorrow) = lens.getUserMaxCapacitiesForAsset(address(supplier1), aUsdc); + supplier1.borrow(aUsdc, toBorrow); - vm.expectRevert(abi.encodeWithSignature("BorrowPaused()")); - supplier1.borrow(aDai, amount); - } + morpho.setPauseStatus(aDai, true); + morpho.setPauseStatus(aUsdc, true); - function testPauseWithdraw() public { - uint256 amount = 10_000 ether; - morpho.setIsWithdrawPaused(aDai, true); + hevm.expectRevert(abi.encodeWithSignature("MarketPaused()")); + supplier1.supply(aDai, amount); - vm.expectRevert(abi.encodeWithSignature("WithdrawPaused()")); - supplier1.withdraw(aDai, amount); - } + hevm.expectRevert(abi.encodeWithSignature("MarketPaused()")); + supplier1.borrow(aUsdc, 1); - function testPauseRepay() public { - uint256 amount = 10_000 ether; - morpho.setIsRepayPaused(aDai, true); + supplier1.approve(usdc, toBorrow); + hevm.expectRevert(abi.encodeWithSignature("MarketPaused()")); + supplier1.repay(aUsdc, toBorrow); + hevm.expectRevert(abi.encodeWithSignature("MarketPaused()")); + supplier1.withdraw(aDai, 1); - vm.expectRevert(abi.encodeWithSignature("RepayPaused()")); - supplier1.repay(aDai, amount); - } + // Change Oracle + SimplePriceOracle customOracle = createAndSetCustomPriceOracle(); + customOracle.setDirectPrice(dai, (oracle.getAssetPrice(dai) * 93) / 100); - function testPauseLiquidateCollateral() public { - uint256 amount = 10_000 ether; - morpho.setIsLiquidateCollateralPaused(aDai, true); + uint256 toLiquidate = toBorrow / 2; + User liquidator = borrower3; + liquidator.approve(usdc, toLiquidate); + + hevm.expectRevert(abi.encodeWithSignature("MarketPaused()")); + liquidator.liquidate(aUsdc, aDai, address(supplier1), toLiquidate); - vm.expectRevert(abi.encodeWithSignature("LiquidateCollateralPaused()")); - supplier1.liquidate(aUsdc, aDai, address(supplier2), amount); + morpho.claimToTreasury(aDaiArray, amountArray); + + // Functions on other markets should still be enabled. + amount = 10 ether; + toBorrow = to6Decimals(amount / 2); + + supplier1.approve(aave, amount); + supplier1.supply(aAave, amount); + + supplier1.borrow(aUsdt, toBorrow); + + supplier1.approve(usdt, toBorrow); + supplier1.repay(aUsdt, toBorrow / 2); + + toLiquidate = 1_000; + liquidator.approve(usdt, toLiquidate); + hevm.expectRevert(ExitPositionsManager.UnauthorisedLiquidate.selector); + liquidator.liquidate(aUsdt, aAave, address(supplier1), toLiquidate); + + supplier1.withdraw(aAave, 1 ether); + + morpho.claimToTreasury(aAaveArray, amountArray); } - function testPauseLiquidateBorrow() public { + function testShouldOnlyEnableRepayWithdrawLiquidateWhenPartiallyPaused() public { uint256 amount = 10_000 ether; - morpho.setIsLiquidateBorrowPaused(aDai, true); - vm.expectRevert(abi.encodeWithSignature("LiquidateBorrowPaused()")); - supplier1.liquidate(aDai, aUsdc, address(supplier2), amount); - } + supplier1.approve(dai, 2 * amount); + supplier1.supply(aDai, amount); - function testShouldNotPauseSupplyOnMarketNotCreated() public { - vm.expectRevert(abi.encodeWithSignature("MarketNotCreated()")); - morpho.setIsSupplyPaused(address(1), true); - } + (, uint256 toBorrow) = lens.getUserMaxCapacitiesForAsset(address(supplier1), aUsdc); + supplier1.borrow(aUsdc, toBorrow); - function testShouldNotPauseBorrowOnMarketNotCreated() public { - vm.expectRevert(abi.encodeWithSignature("MarketNotCreated()")); - morpho.setIsBorrowPaused(address(1), true); - } + morpho.setPartialPauseStatus(aDai, true); + morpho.setPartialPauseStatus(aUsdc, true); - function testShouldNotPauseWithdrawOnMarketNotCreated() public { - vm.expectRevert(abi.encodeWithSignature("MarketNotCreated()")); - morpho.setIsWithdrawPaused(address(1), true); - } + hevm.expectRevert(abi.encodeWithSignature("MarketPaused()")); + supplier1.supply(aDai, amount); - function testShouldNotPauseRepayMarketNotCreated() public { - vm.expectRevert(abi.encodeWithSignature("MarketNotCreated()")); - morpho.setIsRepayPaused(address(1), true); - } + hevm.expectRevert(abi.encodeWithSignature("MarketPaused()")); + supplier1.borrow(aUsdc, 1); - function testShouldNotPauseLiquidateCollateralOnMarketNotCreated() public { - vm.expectRevert(abi.encodeWithSignature("MarketNotCreated()")); - morpho.setIsLiquidateCollateralPaused(address(1), true); - } + supplier1.approve(usdc, toBorrow); + supplier1.repay(aUsdc, 1e6); + supplier1.withdraw(aDai, 1 ether); - function testShouldNotPauseLiquidateBorrowOnMarketNotCreated() public { - vm.expectRevert(abi.encodeWithSignature("MarketNotCreated()")); - morpho.setIsLiquidateBorrowPaused(address(1), true); - } + // Change Oracle + SimplePriceOracle customOracle = createAndSetCustomPriceOracle(); + customOracle.setDirectPrice(dai, (oracle.getAssetPrice(dai) * 93) / 100); + + uint256 toLiquidate = toBorrow / 3; + User liquidator = borrower3; + liquidator.approve(usdc, toLiquidate); + liquidator.liquidate(aUsdc, aDai, address(supplier1), toLiquidate); + + morpho.claimToTreasury(aDaiArray, amountArray); + + // Functions on other markets should still be enabled. + amount = 10 ether; + toBorrow = to6Decimals(amount / 2); + + supplier1.approve(aave, amount); + supplier1.supply(aAave, amount); + + supplier1.borrow(aUsdt, toBorrow); + + supplier1.approve(usdt, toBorrow); + supplier1.repay(aUsdt, toBorrow / 2); + + customOracle.setDirectPrice(aave, (oracle.getAssetPrice(aave) * 97) / 100); + + toLiquidate = 10_000; + liquidator.approve(usdt, toLiquidate); + hevm.expectRevert(ExitPositionsManager.UnauthorisedLiquidate.selector); + liquidator.liquidate(aUsdt, aAave, address(supplier1), toLiquidate); + + supplier1.withdraw(aAave, 1 ether); - function testShouldNotDeprecatedMarketWhenNotCreated() public { - vm.expectRevert(abi.encodeWithSignature("MarketNotCreated()")); - morpho.setIsDeprecated(address(1), true); + morpho.claimToTreasury(aAaveArray, amountArray); } } diff --git a/test-foundry/aave-v2/TestRatesLens.t.sol b/test-foundry/aave-v2/TestRatesLens.t.sol index 669a3ec4c..91c378762 100644 --- a/test-foundry/aave-v2/TestRatesLens.t.sol +++ b/test-foundry/aave-v2/TestRatesLens.t.sol @@ -479,7 +479,7 @@ contract TestRatesLens is TestSetup { hevm.roll(block.number + 1000); - morpho.setIsP2PDisabled(aDai, true); + morpho.setP2PDisabledStatus(aDai, true); ( uint256 supplyRatePerYear, @@ -519,7 +519,7 @@ contract TestRatesLens is TestSetup { hevm.roll(block.number + 1000); - morpho.setIsP2PDisabled(aDai, true); + morpho.setP2PDisabledStatus(aDai, true); ( uint256 borrowRatePerYear, diff --git a/test-foundry/aave-v2/TestRewards.t.sol b/test-foundry/aave-v2/TestRewards.t.sol index c0b0276bc..8007a9fd3 100644 --- a/test-foundry/aave-v2/TestRewards.t.sol +++ b/test-foundry/aave-v2/TestRewards.t.sol @@ -8,7 +8,7 @@ contract TestRewards is TestSetup { address[] memory aDaiInArray = new address[](1); aDaiInArray[0] = aDai; - morpho.setIsClaimRewardsPaused(true); + morpho.setClaimRewardsPauseStatus(true); hevm.expectRevert(abi.encodeWithSignature("ClaimRewardsPaused()")); morpho.claimRewards(aDaiInArray, false); diff --git a/test-foundry/aave-v2/TestSupply.t.sol b/test-foundry/aave-v2/TestSupply.t.sol index f5f1b1ff0..68aaaa9dc 100644 --- a/test-foundry/aave-v2/TestSupply.t.sol +++ b/test-foundry/aave-v2/TestSupply.t.sol @@ -263,47 +263,4 @@ contract TestSupply is TestSetup { vm.warp(block.timestamp + 1); supplier1.supply(aDai, amount); } - - function testAStakedEthShouldAccrueInterest() public { - createMarket(aStEth); - - deal(address(supplier1), 1_000 ether); - uint256 totalEthBalance = address(supplier1).balance; - uint256 totalBalance = totalEthBalance / 2; - vm.prank(address(supplier1)); - ILido(stEth).submit{value: totalBalance}(address(0)); - totalBalance = ERC20(stEth).balanceOf(address(supplier1)); - - // Handle roundings. - vm.prank(address(supplier1)); - ERC20(stEth).transfer(address(morpho), 100); - - uint256 deposited = totalBalance / 2; - supplier1.approve(stEth, type(uint256).max); - supplier1.supply(aStEth, deposited); - - // Update the beacon balance to accrue rewards on the stETH token. - // bytes32 internal constant BEACON_BALANCE_POSITION = keccak256("lido.Lido.beaconBalance"); - uint256 beaconBalanceBefore = uint256(vm.load(stEth, keccak256("lido.Lido.beaconBalance"))); - vm.store( - stEth, - keccak256("lido.Lido.beaconBalance"), - bytes32(beaconBalanceBefore + 10_000 ether) - ); - uint256 beaconBalanceAfter = uint256(vm.load(stEth, keccak256("lido.Lido.beaconBalance"))); - assertGt(beaconBalanceAfter, beaconBalanceBefore); - - // Update timestamp to update indexes. - vm.warp(block.timestamp + 1); - - uint256 balanceBeforeWithdraw = ERC20(stEth).balanceOf(address(supplier1)); - uint256 aTokenBalance = ERC20(aStEth).balanceOf(address(morpho)); - supplier1.withdraw(aStEth, type(uint256).max); - uint256 balanceAfterWithdraw = ERC20(stEth).balanceOf(address(supplier1)); - uint256 withdrawn = balanceAfterWithdraw - balanceBeforeWithdraw; - - // Rewards should accrue on stETH even if there's is no supply interest rate on Aave. - assertGt(withdrawn, deposited); - assertApproxEqAbs(withdrawn, aTokenBalance, 1); - } } diff --git a/test-foundry/aave-v2/TestWithdraw.t.sol b/test-foundry/aave-v2/TestWithdraw.t.sol index ed779a529..abac3ce15 100644 --- a/test-foundry/aave-v2/TestWithdraw.t.sol +++ b/test-foundry/aave-v2/TestWithdraw.t.sol @@ -620,7 +620,7 @@ contract TestWithdraw is TestSetup { borrower1.supply(aWeth, 10 * amount); borrower1.borrow(aDai, 10 * amount); - morpho.setIsWithdrawPaused(aDai, true); + morpho.setPauseStatus(aDai, true); supplier1.withdraw(aDai, amount); supplier1.withdraw(aDai, amount); diff --git a/test-foundry/aave-v2/helpers/User.sol b/test-foundry/aave-v2/helpers/User.sol index f39a6cb20..c99c0262a 100644 --- a/test-foundry/aave-v2/helpers/User.sol +++ b/test-foundry/aave-v2/helpers/User.sol @@ -155,31 +155,15 @@ contract User { return morpho.claimRewards(_assets, _toSwap); } - function setTreasuryVault(address _newTreasuryVault) external { - morpho.setTreasuryVault(_newTreasuryVault); - } - - function setIsSupplyPaused(address _poolToken, bool _isPaused) external { - morpho.setIsSupplyPaused(_poolToken, _isPaused); - } - - function setIsBorrowPaused(address _poolToken, bool _isPaused) external { - morpho.setIsBorrowPaused(_poolToken, _isPaused); + function setPauseStatus(address _marketAddress, bool _newStatus) external { + morpho.setPauseStatus(_marketAddress, _newStatus); } - function setIsWithdrawPaused(address _poolToken, bool _isPaused) external { - morpho.setIsWithdrawPaused(_poolToken, _isPaused); + function setPartialPauseStatus(address _poolToken, bool _newStatus) external { + morpho.setPartialPauseStatus(_poolToken, _newStatus); } - function setIsRepayPaused(address _poolToken, bool _isPaused) external { - morpho.setIsRepayPaused(_poolToken, _isPaused); - } - - function setIsLiquidateCollateralPaused(address _poolToken, bool _isPaused) external { - morpho.setIsLiquidateCollateralPaused(_poolToken, _isPaused); - } - - function setIsLiquidateBorrowPaused(address _poolToken, bool _isPaused) external { - morpho.setIsLiquidateBorrowPaused(_poolToken, _isPaused); + function setTreasuryVault(address _newTreasuryVault) external { + morpho.setTreasuryVault(_newTreasuryVault); } } diff --git a/test-foundry/aave-v2/setup/TestSetup.sol b/test-foundry/aave-v2/setup/TestSetup.sol index 697c3aeb7..009d0d493 100644 --- a/test-foundry/aave-v2/setup/TestSetup.sol +++ b/test-foundry/aave-v2/setup/TestSetup.sol @@ -5,7 +5,6 @@ import "@contracts/aave-v2/interfaces/aave/IAaveIncentivesController.sol"; import "@contracts/aave-v2/interfaces/aave/IVariableDebtToken.sol"; import "@contracts/aave-v2/interfaces/aave/IAToken.sol"; import "@contracts/aave-v2/interfaces/IMorpho.sol"; -import "@contracts/aave-v2/interfaces/lido/ILido.sol"; import {ReserveConfiguration} from "@contracts/aave-v2/libraries/aave/ReserveConfiguration.sol"; import "@rari-capital/solmate/src/utils/SafeTransferLib.sol"; @@ -257,9 +256,7 @@ contract TestSetup is Config, Utils { uint256 poolSupplyAPR = reserveData.currentLiquidityRate; uint256 poolBorrowAPR = reserveData.currentVariableBorrowRate; - (, uint16 reserveFactor, uint256 p2pIndexCursor, , , , , , , , ) = morpho.market( - _poolToken - ); + (, uint16 reserveFactor, uint256 p2pIndexCursor, , , , ) = morpho.market(_poolToken); // rate = (1 - p2pIndexCursor) * poolSupplyRate + p2pIndexCursor * poolBorrowRate. uint256 rate = ((10_000 - p2pIndexCursor) * diff --git a/test-foundry/compound/TestGovernance.t.sol b/test-foundry/compound/TestGovernance.t.sol index be7a11d4a..d67393b2a 100644 --- a/test-foundry/compound/TestGovernance.t.sol +++ b/test-foundry/compound/TestGovernance.t.sol @@ -77,7 +77,7 @@ contract TestGovernance is TestSetup { Types.MarketParameters memory marketParams = Types.MarketParameters(3_333, 0); morpho.createMarket(cAave, marketParams); - (bool isCreated, , , , , , , ) = morpho.marketStatus(cAave); + (bool isCreated, , ) = morpho.marketStatus(cAave); assertTrue(isCreated); assertEq( @@ -128,12 +128,12 @@ contract TestGovernance is TestSetup { function testOnlyOwnerShouldFlipMarketStrategy() public { hevm.expectRevert("Ownable: caller is not the owner"); - supplier1.setIsP2PDisabled(cDai, true); + supplier1.setP2PDisabled(cDai, true); hevm.expectRevert("Ownable: caller is not the owner"); - supplier2.setIsP2PDisabled(cDai, true); + supplier2.setP2PDisabled(cDai, true); - morpho.setIsP2PDisabled(cDai, true); + morpho.setP2PDisabled(cDai, true); assertTrue(morpho.p2pDisabled(cDai)); } @@ -198,210 +198,12 @@ contract TestGovernance is TestSetup { assertEq(address(morpho.treasuryVault()), treasuryVaultV2); } - function testOnlyOwnerCanSetIsClaimRewardsPaused() public { + function testOnlyOwnerCanSetClaimRewardsStatus() public { hevm.prank(address(0)); hevm.expectRevert("Ownable: caller is not the owner"); - morpho.setIsClaimRewardsPaused(true); + morpho.setClaimRewardsPauseStatus(true); - morpho.setIsClaimRewardsPaused(true); + morpho.setClaimRewardsPauseStatus(true); assertTrue(morpho.isClaimRewardsPaused()); } - - function testOnlyOwnerCanSetPauseStatusForAllMarkets() public { - hevm.prank(address(0)); - hevm.expectRevert("Ownable: caller is not the owner"); - morpho.setIsPausedForAllMarkets(true); - - morpho.setIsPausedForAllMarkets(true); - } - - function testOnlyOwnerShouldSetDeprecatedMarket() public { - hevm.prank(address(supplier1)); - hevm.expectRevert("Ownable: caller is not the owner"); - morpho.setIsDeprecated(cDai, true); - - hevm.prank(address(supplier2)); - hevm.expectRevert("Ownable: caller is not the owner"); - morpho.setIsDeprecated(cDai, true); - - morpho.setIsDeprecated(cDai, true); - - (, , , , , , , bool isDeprecated) = morpho.marketStatus(cDai); - assertTrue(isDeprecated); - - morpho.setIsDeprecated(cDai, false); - (, , , , , , , isDeprecated) = morpho.marketStatus(cDai); - assertFalse(isDeprecated); - } - - function testOnlyOwnerCanIncreaseP2PDeltas() public { - hevm.prank(address(supplier1)); - hevm.expectRevert("Ownable: caller is not the owner"); - morpho.increaseP2PDeltas(cDai, 0); - - morpho.increaseP2PDeltas(cDai, 0); - } - - function testShouldIncreaseP2PDeltasWhenMarketNotCreated() public { - hevm.expectRevert(abi.encodeWithSignature("MarketNotCreated()")); - morpho.increaseP2PDeltas(address(1), 0); - } - - function testIncreaseP2PDeltas() public { - uint256 supplyAmount = 100 ether; - uint256 borrowAmount = 50 ether; - uint256 increaseDeltaAmount = 30 ether; - - supplier1.approve(wEth, supplyAmount); - supplier1.supply(cEth, supplyAmount); - supplier1.approve(dai, supplyAmount); - supplier1.supply(cDai, supplyAmount); - supplier1.borrow(cDai, borrowAmount); - - morpho.increaseP2PDeltas(cDai, increaseDeltaAmount); - - (uint256 p2pSupplyDelta, uint256 p2pBorrowDelta, , ) = morpho.deltas(cDai); - - assertEq(p2pSupplyDelta, increaseDeltaAmount.div(ICToken(cDai).exchangeRateStored())); - assertEq(p2pBorrowDelta, increaseDeltaAmount.div(ICToken(cDai).borrowIndex())); - assertApproxEqRel( - ICToken(cDai).balanceOfUnderlying(address(morpho)), - supplyAmount - borrowAmount + increaseDeltaAmount, - 1e8 - ); - assertApproxEqRel( - ICToken(cDai).borrowBalanceCurrent(address(morpho)), - increaseDeltaAmount, - 1e8 - ); - } - - function testIncreaseP2PDeltasMoreThanWhatIsPossibleSupply() public { - uint256 supplyAmount = 100 ether; - uint256 borrowAmount = 50 ether; - uint256 deltaAmount = 25 ether; - uint256 increaseDeltaAmount = 80 ether; - - supplier1.approve(wEth, type(uint256).max); - supplier1.supply(cEth, supplyAmount); - supplier1.approve(dai, type(uint256).max); - supplier1.supply(cDai, supplyAmount); - supplier1.borrow(cDai, borrowAmount); - _setDefaultMaxGasForMatching(0, 0, 0, 0); - hevm.roll(block.number + 1); - supplier1.repay(cDai, deltaAmount); // Creates a peer-to-peer supply delta. - - morpho.increaseP2PDeltas(cDai, increaseDeltaAmount); - - (uint256 p2pSupplyDelta, uint256 p2pBorrowDelta, , ) = morpho.deltas(cDai); - - assertApproxEqRel( - p2pSupplyDelta, - borrowAmount.div(ICToken(cDai).exchangeRateStored()), - 1e12 - ); - assertApproxEqRel( - p2pBorrowDelta, - (borrowAmount - deltaAmount).div(ICToken(cDai).borrowIndex()), - 1e12 - ); - assertApproxEqRel(ICToken(cDai).balanceOfUnderlying(address(morpho)), supplyAmount, 1e12); - assertApproxEqRel( - ICToken(cDai).borrowBalanceCurrent(address(morpho)), - borrowAmount - deltaAmount, - 1e12 - ); - } - - function testIncreaseP2PDeltasMoreThanWhatIsPossibleBorrow() public { - uint256 supplyAmount = 100 ether; - uint256 borrowAmount = 50 ether; - uint256 deltaAmount = 25 ether; - uint256 increaseDeltaAmount = 80 ether; - - supplier1.approve(wEth, supplyAmount); - supplier1.supply(cEth, supplyAmount); - supplier1.approve(dai, supplyAmount); - supplier1.supply(cDai, supplyAmount); - supplier1.borrow(cDai, borrowAmount); - _setDefaultMaxGasForMatching(0, 0, 0, 0); - supplier1.withdraw(cDai, supplyAmount - borrowAmount + deltaAmount); // Creates a peer-to-peer borrow delta. - - morpho.increaseP2PDeltas(cDai, increaseDeltaAmount); - - (uint256 p2pSupplyDelta, uint256 p2pBorrowDelta, , ) = morpho.deltas(cDai); - - assertApproxEqRel( - p2pSupplyDelta, - (borrowAmount - deltaAmount).div(ICToken(cDai).exchangeRateStored()), - 1e8 - ); - assertApproxEqRel(p2pBorrowDelta, borrowAmount.div(ICToken(cDai).borrowIndex()), 1e8); - assertApproxEqRel(ICToken(cDai).balanceOfUnderlying(address(morpho)), deltaAmount, 1e8); - assertApproxEqRel(ICToken(cDai).borrowBalanceCurrent(address(morpho)), borrowAmount, 1e8); - } - - function testIncreaseP2PDeltasWithMaxBorrowDelta() public { - uint256 supplyAmount = 100 ether; - uint256 borrowAmount = 50 ether; - uint256 increaseDeltaAmount = 80 ether; - - supplier1.approve(wEth, supplyAmount); - supplier1.supply(cEth, supplyAmount); - supplier1.approve(dai, supplyAmount); - supplier1.supply(cDai, supplyAmount); - supplier1.borrow(cDai, borrowAmount); - _setDefaultMaxGasForMatching(0, 0, 0, 0); - supplier1.withdraw(cDai, type(uint256).max); // Creates a 100% peer-to-peer borrow delta. - - hevm.roll(block.number + 1000); - - morpho.increaseP2PDeltas(cDai, increaseDeltaAmount); - } - - function testFailCallIncreaseP2PDeltasFromImplementation() public { - positionsManager.increaseP2PDeltasLogic(cDai, 0); - } - - function testOnlyOwnerShouldDisableSupply() public { - vm.expectRevert("Ownable: caller is not the owner"); - supplier1.setIsSupplyPaused(cDai, true); - - morpho.setIsSupplyPaused(cDai, true); - } - - function testOnlyOwnerShouldDisableBorrow() public { - vm.expectRevert("Ownable: caller is not the owner"); - supplier1.setIsBorrowPaused(cDai, true); - - morpho.setIsBorrowPaused(cDai, true); - } - - function testOnlyOwnerShouldDisableWithdraw() public { - vm.expectRevert("Ownable: caller is not the owner"); - supplier1.setIsWithdrawPaused(cDai, true); - - morpho.setIsWithdrawPaused(cDai, true); - } - - function testOnlyOwnerShouldDisableRepay() public { - vm.expectRevert("Ownable: caller is not the owner"); - supplier1.setIsRepayPaused(cDai, true); - - morpho.setIsRepayPaused(cDai, true); - } - - function testOnlyOwnerShouldDisableLiquidateOnCollateral() public { - vm.expectRevert("Ownable: caller is not the owner"); - supplier1.setIsLiquidateCollateralPaused(cDai, true); - - morpho.setIsLiquidateCollateralPaused(cDai, true); - } - - function testOnlyOwnerShouldDisableLiquidateOnBorrow() public { - vm.expectRevert("Ownable: caller is not the owner"); - supplier1.setIsLiquidateBorrowPaused(cDai, true); - - morpho.setIsLiquidateBorrowPaused(cDai, true); - } } diff --git a/test-foundry/compound/TestLens.t.sol b/test-foundry/compound/TestLens.t.sol index dd55bd3af..c998712d8 100644 --- a/test-foundry/compound/TestLens.t.sol +++ b/test-foundry/compound/TestLens.t.sol @@ -680,23 +680,13 @@ contract TestLens is TestSetup { ) = lens.getMarketConfiguration(cDai); assertTrue(underlying == ICToken(cDai).underlying()); - ( - bool isCreated_, - bool isSupplyPaused_, - bool isBorrowPaused_, - bool isWithdrawPaused_, - bool isRepayPaused_, - , - , - - ) = morpho.marketStatus(cDai); + (bool isCreated_, bool isPaused_, bool isPartiallyPaused_) = morpho.marketStatus(cDai); assertTrue(isCreated == isCreated_); assertTrue(p2pDisabled == morpho.p2pDisabled(cDai)); - assertTrue( - isPaused == (isSupplyPaused_ && isBorrowPaused_ && isWithdrawPaused_ && isRepayPaused_) - ); - assertTrue(isPartiallyPaused == (isSupplyPaused_ && isBorrowPaused_)); + + assertTrue(isPaused == isPaused_); + assertTrue(isPartiallyPaused == isPartiallyPaused_); (uint16 expectedReserveFactor, uint16 expectedP2PIndexCursor) = morpho.marketParameters( cDai ); diff --git a/test-foundry/compound/TestLiquidate.t.sol b/test-foundry/compound/TestLiquidate.t.sol index f95dbd01e..a0f069954 100644 --- a/test-foundry/compound/TestLiquidate.t.sol +++ b/test-foundry/compound/TestLiquidate.t.sol @@ -25,45 +25,6 @@ contract TestLiquidate is TestSetup { liquidator.liquidate(cDai, cUsdc, address(borrower1), toRepay); } - function testLiquidateWhenMarketDeprecated() public { - uint256 amount = 10_000 ether; - uint256 collateral = to6Decimals(2 * amount); - - morpho.setIsDeprecated(cDai, true); - - borrower1.approve(usdc, address(morpho), collateral); - borrower1.supply(cUsdc, collateral); - borrower1.borrow(cDai, amount); - - moveOneBlockForwardBorrowRepay(); - - (, uint256 supplyOnPoolBefore) = morpho.supplyBalanceInOf(cUsdc, address(borrower1)); - (, uint256 borrowOnPoolBefore) = morpho.borrowBalanceInOf(cDai, address(borrower1)); - - // Liquidate - uint256 toRepay = amount; // Full liquidation. - User liquidator = borrower3; - liquidator.approve(dai, address(morpho), toRepay); - liquidator.liquidate(cDai, cUsdc, address(borrower1), toRepay); - - (, uint256 supplyOnPoolAfter) = morpho.supplyBalanceInOf(cUsdc, address(borrower1)); - (, uint256 borrowOnPoolAfter) = morpho.borrowBalanceInOf(cDai, address(borrower1)); - - uint256 collateralPrice = oracle.getUnderlyingPrice(cUsdc); - uint256 borrowedPrice = oracle.getUnderlyingPrice(cDai); - - uint256 amountToSeize = toRepay - .mul(comptroller.liquidationIncentiveMantissa()) - .mul(borrowedPrice) - .div(collateralPrice); - - uint256 expectedSupplyOnPoolAfter = supplyOnPoolBefore - - amountToSeize.div(ICToken(cUsdc).exchangeRateCurrent()); - - assertApproxEqAbs(supplyOnPoolAfter, expectedSupplyOnPoolAfter, 2); - assertApproxEqAbs(borrowOnPoolAfter, 0, 1e15); - } - // A user liquidates a borrower that has not enough collateral to cover for his debt. function testShouldLiquidateUser() public { uint256 collateral = 100_000 ether; diff --git a/test-foundry/compound/TestMarketStrategy.t.sol b/test-foundry/compound/TestMarketStrategy.t.sol index 64c8dc8f2..f318b1a17 100644 --- a/test-foundry/compound/TestMarketStrategy.t.sol +++ b/test-foundry/compound/TestMarketStrategy.t.sol @@ -9,7 +9,7 @@ contract TestMarketStrategy is TestSetup { uint256 toBorrow = 500 ether; // Flip strategy - morpho.setIsP2PDisabled(cDai, true); + morpho.setP2PDisabled(cDai, true); borrower1.approve(usdc, to6Decimals(amount)); borrower1.supply(cUsdc, to6Decimals(amount)); @@ -40,7 +40,7 @@ contract TestMarketStrategy is TestSetup { uint256 toBorrow = 500 ether; // Flip strategy - morpho.setIsP2PDisabled(cDai, true); + morpho.setP2PDisabled(cDai, true); supplier1.approve(dai, toBorrow); supplier1.supply(cDai, toBorrow); @@ -71,7 +71,7 @@ contract TestMarketStrategy is TestSetup { uint256 toBorrow = 100 ether; // Flip strategy - morpho.setIsP2PDisabled(cDai, true); + morpho.setP2PDisabled(cDai, true); for (uint256 i = 0; i < 3; i++) { borrowers[i].approve(usdc, to6Decimals(amount)); @@ -106,7 +106,7 @@ contract TestMarketStrategy is TestSetup { uint256 toSupply = 100 ether; // Flip strategy - morpho.setIsP2PDisabled(cDai, true); + morpho.setP2PDisabled(cDai, true); for (uint256 i = 0; i < 3; i++) { suppliers[i].approve(dai, toSupply); diff --git a/test-foundry/compound/TestP2PDisable.t.sol b/test-foundry/compound/TestP2PDisable.t.sol index 6643e792f..e82438050 100644 --- a/test-foundry/compound/TestP2PDisable.t.sol +++ b/test-foundry/compound/TestP2PDisable.t.sol @@ -31,7 +31,7 @@ contract TestP2PDisable is TestSetup { assertGt(p2pSupplyDelta, 0); // Disable peer-to-peer. - morpho.setIsP2PDisabled(cDai, true); + morpho.setP2PDisabled(cDai, true); // Delta must be reduce to 0. borrower1.borrow(cDai, borrowedAmount); @@ -64,7 +64,7 @@ contract TestP2PDisable is TestSetup { assertGt(p2pBorrowDelta, 0); // Disable peer-to-peer. - morpho.setIsP2PDisabled(cUsdc, true); + morpho.setP2PDisabled(cUsdc, true); // Delta must be reduce to 0. supplier1.supply(cUsdc, to6Decimals(supplyAmount * 2)); diff --git a/test-foundry/compound/TestPausableMarket.t.sol b/test-foundry/compound/TestPausableMarket.t.sol index 3bf3a7bd5..134b4f53c 100644 --- a/test-foundry/compound/TestPausableMarket.t.sol +++ b/test-foundry/compound/TestPausableMarket.t.sol @@ -6,52 +6,46 @@ import "./setup/TestSetup.sol"; contract TestPausableMarket is TestSetup { using CompoundMath for uint256; - address[] public cEthArray = [cEth]; - address[] public cDaiArray = [cDai]; + address[] cEthArray = [cEth]; + address[] cDaiArray = [cDai]; uint256[] public amountArray = [1 ether]; - function testAllMarketsPauseUnpause() public { - morpho.setIsPausedForAllMarkets(true); - - for (uint256 i; i < pools.length; ++i) { - ( - , - bool isSupplyPaused, - bool isBorrowPaused, - bool isWithdrawPaused, - bool isRepayPaused, - bool isLiquidateCollateralPaused, - bool isLiquidateBorrowPaused, - - ) = morpho.marketStatus(pools[i]); - assertTrue(isSupplyPaused); - assertTrue(isBorrowPaused); - assertTrue(isWithdrawPaused); - assertTrue(isRepayPaused); - assertTrue(isLiquidateCollateralPaused); - assertTrue(isLiquidateBorrowPaused); - } - - morpho.setIsPausedForAllMarkets(false); - - for (uint256 i; i < pools.length; ++i) { - ( - , - bool isSupplyPaused, - bool isBorrowPaused, - bool isWithdrawPaused, - bool isRepayPaused, - bool isLiquidateCollateralPaused, - bool isLiquidateBorrowPaused, - - ) = morpho.marketStatus(pools[i]); - assertFalse(isSupplyPaused); - assertFalse(isBorrowPaused); - assertFalse(isWithdrawPaused); - assertFalse(isRepayPaused); - assertFalse(isLiquidateCollateralPaused); - assertFalse(isLiquidateBorrowPaused); - } + function testOnlyOwnerShouldTriggerPauseFunction() public { + hevm.expectRevert("Ownable: caller is not the owner"); + supplier1.setPauseStatus(cDai, true); + + morpho.setPauseStatus(cDai, true); + (, bool isPaused, ) = morpho.marketStatus(cDai); + assertTrue(isPaused, "paused is false"); + } + + function testOnlyOwnerShouldTriggerPartialPauseFunction() public { + hevm.expectRevert("Ownable: caller is not the owner"); + supplier1.setPartialPauseStatus(cDai, true); + + morpho.setPartialPauseStatus(cDai, true); + (, , bool isPartiallyPaused) = morpho.marketStatus(cDai); + assertTrue(isPartiallyPaused, "partial paused is false"); + } + + function testPauseUnpause() public { + morpho.setPauseStatus(cDai, true); + (, bool isPaused, ) = morpho.marketStatus(cDai); + assertTrue(isPaused, "paused is false"); + + morpho.setPauseStatus(cDai, false); + (, isPaused, ) = morpho.marketStatus(cDai); + assertFalse(isPaused, "paused is true"); + } + + function testPartialPausePartialUnpause() public { + morpho.setPartialPauseStatus(cDai, true); + (, , bool isPartiallyPaused) = morpho.marketStatus(cDai); + assertTrue(isPartiallyPaused, "partial paused is false"); + + morpho.setPartialPauseStatus(cDai, false); + (, , isPartiallyPaused) = morpho.marketStatus(cDai); + assertFalse(isPartiallyPaused, "partial paused is true"); } function testShouldTriggerFunctionsWhenNotPaused() public { @@ -87,108 +81,128 @@ contract TestPausableMarket is TestSetup { morpho.claimToTreasury(cDaiArray, amountArray); } - function testShouldDisableAllMarketsWhenGloballyPaused() public { - morpho.setIsPausedForAllMarkets(true); + function testShouldDisableMarketWhenPaused() public { + uint256 amount = 10_000 ether; + + supplier1.approve(dai, 2 * amount); + supplier1.supply(cDai, amount); + + (, uint256 toBorrow) = lens.getUserMaxCapacitiesForAsset(address(supplier1), cUsdc); + supplier1.borrow(cUsdc, toBorrow); + + morpho.setPauseStatus(cDai, true); + morpho.setPauseStatus(cUsdc, true); + + hevm.expectRevert(abi.encodeWithSignature("MarketPaused()")); + supplier1.supply(cDai, amount); + + hevm.expectRevert(abi.encodeWithSignature("MarketPaused()")); + supplier1.borrow(cUsdc, 1); + + supplier1.approve(usdc, toBorrow); + moveOneBlockForwardBorrowRepay(); + hevm.expectRevert(abi.encodeWithSignature("MarketPaused()")); + supplier1.repay(cUsdc, toBorrow); + hevm.expectRevert(abi.encodeWithSignature("MarketPaused()")); + supplier1.withdraw(cDai, 1); + + // Change Oracle + SimplePriceOracle customOracle = createAndSetCustomPriceOracle(); + customOracle.setUnderlyingPrice(cDai, (oracle.getUnderlyingPrice(cDai) * 95) / 100); + + uint256 toLiquidate = toBorrow / 3; + User liquidator = borrower3; + liquidator.approve(usdc, toLiquidate); + + hevm.expectRevert(abi.encodeWithSignature("MarketPaused()")); + liquidator.liquidate(cUsdc, cDai, address(supplier1), toLiquidate); + + morpho.claimToTreasury(cDaiArray, amountArray); + + // Functions on other markets should still be enabled. + amount = 10 ether; + to6Decimals(amount / 2); + + supplier1.approve(wEth, amount); + supplier1.supply(cEth, amount); - uint256 poolsLength = pools.length; - for (uint256 i; i < poolsLength; ++i) { - vm.expectRevert(abi.encodeWithSignature("SupplyPaused()")); - supplier1.supply(pools[i], 1); + supplier1.borrow(cUsdt, toBorrow); - vm.expectRevert(abi.encodeWithSignature("BorrowPaused()")); - supplier1.borrow(pools[i], 1); + moveOneBlockForwardBorrowRepay(); + + supplier1.approve(usdt, toBorrow); + supplier1.repay(cUsdt, toBorrow / 2); + + customOracle.setUnderlyingPrice(cEth, (oracle.getUnderlyingPrice(cEth) * 97) / 100); - vm.expectRevert(abi.encodeWithSignature("WithdrawPaused()")); - supplier1.withdraw(pools[i], 1); + toLiquidate = 1_000; + liquidator.approve(usdt, toLiquidate); + hevm.expectRevert(PositionsManager.UnauthorisedLiquidate.selector); + liquidator.liquidate(cUsdt, cEth, address(supplier1), toLiquidate); - vm.expectRevert(abi.encodeWithSignature("RepayPaused()")); - supplier1.repay(pools[i], 1); + supplier1.withdraw(cEth, 1 ether); - vm.expectRevert(abi.encodeWithSignature("LiquidateCollateralPaused()")); - supplier1.liquidate(pools[i], pools[0], address(supplier1), 1); - } + morpho.claimToTreasury(cEthArray, amountArray); } - function testPauseSupply() public { + function testShouldOnlyEnableRepayWithdrawLiquidateWhenPartiallyPaused() public { uint256 amount = 10_000 ether; - morpho.setIsSupplyPaused(cDai, true); - vm.expectRevert(abi.encodeWithSignature("SupplyPaused()")); + supplier1.approve(dai, 2 * amount); supplier1.supply(cDai, amount); - } - function testPauseBorrow() public { - uint256 amount = 10_000 ether; - morpho.setIsBorrowPaused(cDai, true); + (, uint256 toBorrow) = lens.getUserMaxCapacitiesForAsset(address(supplier1), cUsdc); + supplier1.borrow(cUsdc, toBorrow); - vm.expectRevert(abi.encodeWithSignature("BorrowPaused()")); - supplier1.borrow(cDai, amount); - } + morpho.setPartialPauseStatus(cDai, true); + morpho.setPartialPauseStatus(cUsdc, true); - function testPauseWithdraw() public { - uint256 amount = 10_000 ether; - morpho.setIsWithdrawPaused(cDai, true); + hevm.expectRevert(abi.encodeWithSignature("MarketPaused()")); + supplier1.supply(cDai, amount); - vm.expectRevert(abi.encodeWithSignature("WithdrawPaused()")); - supplier1.withdraw(cDai, amount); - } + hevm.expectRevert(abi.encodeWithSignature("MarketPaused()")); + supplier1.borrow(cUsdc, 1); - function testPauseRepay() public { - uint256 amount = 10_000 ether; - morpho.setIsRepayPaused(cDai, true); + moveOneBlockForwardBorrowRepay(); - vm.expectRevert(abi.encodeWithSignature("RepayPaused()")); - supplier1.repay(cDai, amount); - } + supplier1.approve(usdc, toBorrow); + supplier1.repay(cUsdc, 1e6); + supplier1.withdraw(cDai, 1 ether); - function testPauseLiquidateCollateral() public { - uint256 amount = 10_000 ether; - morpho.setIsLiquidateCollateralPaused(cDai, true); + // Change Oracle + SimplePriceOracle customOracle = createAndSetCustomPriceOracle(); + customOracle.setUnderlyingPrice(cDai, (oracle.getUnderlyingPrice(cDai) * 97) / 100); - vm.expectRevert(abi.encodeWithSignature("LiquidateCollateralPaused()")); - supplier1.liquidate(cUsdc, cDai, address(supplier2), amount); - } + uint256 toLiquidate = toBorrow / 3; + User liquidator = borrower3; + liquidator.approve(usdc, toLiquidate); + liquidator.liquidate(cUsdc, cDai, address(supplier1), toLiquidate); - function testPauseLiquidateBorrow() public { - uint256 amount = 10_000 ether; - morpho.setIsLiquidateBorrowPaused(cDai, true); + morpho.claimToTreasury(cDaiArray, amountArray); - vm.expectRevert(abi.encodeWithSignature("LiquidateBorrowPaused()")); - supplier1.liquidate(cDai, cUsdc, address(supplier2), amount); - } + // Functions on other markets should still be enabled. + amount = 10 ether; + toBorrow = to6Decimals(amount / 2); - function testShouldNotPauseSupplyOnMarketNotCreated() public { - vm.expectRevert(abi.encodeWithSignature("MarketNotCreated()")); - morpho.setIsSupplyPaused(address(1), true); - } + supplier1.approve(wEth, amount); + supplier1.supply(cEth, amount); - function testShouldNotPauseBorrowOnMarketNotCreated() public { - vm.expectRevert(abi.encodeWithSignature("MarketNotCreated()")); - morpho.setIsBorrowPaused(address(1), true); - } + supplier1.borrow(cUsdt, toBorrow); - function testShouldNotPauseWithdrawOnMarketNotCreated() public { - vm.expectRevert(abi.encodeWithSignature("MarketNotCreated()")); - morpho.setIsWithdrawPaused(address(1), true); - } + moveOneBlockForwardBorrowRepay(); - function testShouldNotPauseRepayMarketNotCreated() public { - vm.expectRevert(abi.encodeWithSignature("MarketNotCreated()")); - morpho.setIsRepayPaused(address(1), true); - } + supplier1.approve(usdt, toBorrow); + supplier1.repay(cUsdt, toBorrow / 2); - function testShouldNotPauseLiquidateCollateralOnMarketNotCreated() public { - vm.expectRevert(abi.encodeWithSignature("MarketNotCreated()")); - morpho.setIsLiquidateCollateralPaused(address(1), true); - } + customOracle.setUnderlyingPrice(cEth, (oracle.getUnderlyingPrice(cEth) * 97) / 100); - function testShouldNotPauseLiquidateBorrowOnMarketNotCreated() public { - vm.expectRevert(abi.encodeWithSignature("MarketNotCreated()")); - morpho.setIsLiquidateBorrowPaused(address(1), true); - } + toLiquidate = 10_000; + liquidator.approve(usdt, toLiquidate); + hevm.expectRevert(PositionsManager.UnauthorisedLiquidate.selector); + liquidator.liquidate(cUsdt, cEth, address(supplier1), toLiquidate); + + supplier1.withdraw(cEth, 1 ether); - function testShouldNotDeprecatedMarketWhenNotCreated() public { - vm.expectRevert(abi.encodeWithSignature("MarketNotCreated()")); - morpho.setIsDeprecated(address(1), true); + morpho.claimToTreasury(cEthArray, amountArray); } } diff --git a/test-foundry/compound/TestRatesLens.t.sol b/test-foundry/compound/TestRatesLens.t.sol index 565988dad..4020dc0b2 100644 --- a/test-foundry/compound/TestRatesLens.t.sol +++ b/test-foundry/compound/TestRatesLens.t.sol @@ -501,7 +501,7 @@ contract TestRatesLens is TestSetup { hevm.roll(block.number + 1000); - morpho.setIsP2PDisabled(cDai, true); + morpho.setP2PDisabled(cDai, true); ( uint256 supplyRatePerBlock, @@ -540,7 +540,7 @@ contract TestRatesLens is TestSetup { hevm.roll(block.number + 1000); - morpho.setIsP2PDisabled(cDai, true); + morpho.setP2PDisabled(cDai, true); ( uint256 borrowRatePerBlock, diff --git a/test-foundry/compound/TestRewards.t.sol b/test-foundry/compound/TestRewards.t.sol index aeddfff6c..92a26658d 100644 --- a/test-foundry/compound/TestRewards.t.sol +++ b/test-foundry/compound/TestRewards.t.sol @@ -41,7 +41,7 @@ contract TestRewards is TestSetup { address[] memory cDaiInArray = new address[](1); cDaiInArray[0] = cDai; - morpho.setIsClaimRewardsPaused(true); + morpho.setClaimRewardsPauseStatus(true); hevm.expectRevert(abi.encodeWithSignature("ClaimRewardsPaused()")); morpho.claimRewards(cDaiInArray, false); diff --git a/test-foundry/compound/TestWithdraw.t.sol b/test-foundry/compound/TestWithdraw.t.sol index 579d2be1a..dacadc5d4 100644 --- a/test-foundry/compound/TestWithdraw.t.sol +++ b/test-foundry/compound/TestWithdraw.t.sol @@ -705,7 +705,7 @@ contract TestWithdraw is TestSetup { borrower1.supply(cEth, 10 * amount); borrower1.borrow(cDai, 10 * amount); - morpho.setIsP2PDisabled(cDai, true); + morpho.setP2PDisabled(cDai, true); supplier1.withdraw(cDai, amount); supplier1.withdraw(cDai, amount); diff --git a/test-foundry/compound/helpers/User.sol b/test-foundry/compound/helpers/User.sol index 8382d6142..a64dbc6e5 100644 --- a/test-foundry/compound/helpers/User.sol +++ b/test-foundry/compound/helpers/User.sol @@ -168,35 +168,19 @@ contract User { return morpho.claimRewards(_assets, _toSwap); } - function setIsP2PDisabled(address _marketAddress, bool _isPaused) external { - morpho.setIsP2PDisabled(_marketAddress, _isPaused); + function setP2PDisabled(address _marketAddress, bool _newStatus) external { + morpho.setP2PDisabled(_marketAddress, _newStatus); } function setTreasuryVault(address _newTreasuryVault) external { morpho.setTreasuryVault(_newTreasuryVault); } - function setIsSupplyPaused(address _poolToken, bool _isPaused) external { - morpho.setIsSupplyPaused(_poolToken, _isPaused); + function setPauseStatus(address _poolToken, bool _newStatus) external { + morpho.setPauseStatus(_poolToken, _newStatus); } - function setIsBorrowPaused(address _poolToken, bool _isPaused) external { - morpho.setIsBorrowPaused(_poolToken, _isPaused); - } - - function setIsWithdrawPaused(address _poolToken, bool _isPaused) external { - morpho.setIsWithdrawPaused(_poolToken, _isPaused); - } - - function setIsRepayPaused(address _poolToken, bool _isPaused) external { - morpho.setIsRepayPaused(_poolToken, _isPaused); - } - - function setIsLiquidateCollateralPaused(address _poolToken, bool _isPaused) external { - morpho.setIsLiquidateCollateralPaused(_poolToken, _isPaused); - } - - function setIsLiquidateBorrowPaused(address _poolToken, bool _isPaused) external { - morpho.setIsLiquidateBorrowPaused(_poolToken, _isPaused); + function setPartialPauseStatus(address _poolToken, bool _newStatus) external { + morpho.setPartialPauseStatus(_poolToken, _newStatus); } } diff --git a/test-foundry/prod/aave-v2/TestLens.t.sol b/test-foundry/prod/aave-v2/TestLens.t.sol new file mode 100644 index 000000000..fe3df0491 --- /dev/null +++ b/test-foundry/prod/aave-v2/TestLens.t.sol @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GNU AGPLv3 +pragma solidity 0.8.13; + +import "./setup/TestSetup.sol"; + +contract TestLens is TestSetup { + using ReserveConfiguration for DataTypes.ReserveConfigurationMap; + using WadRayMath for uint256; + using Math for uint256; + + struct Vars { + uint256 p2pSupplyIndex; + uint256 p2pBorrowIndex; + uint256 poolSupplyIndex; + uint256 poolBorrowIndex; + } + + function testIndexesShouldBeEqualAfterUpgrade() public { + Lens lensImplV2 = new Lens(address(morpho)); + + address[] memory marketsCreated = lens.getAllMarkets(); + Vars[] memory expectedValues = new Vars[](marketsCreated.length); + + for (uint256 i; i < marketsCreated.length; ++i) { + address market = marketsCreated[i]; + if (market == aStEth) continue; + + ( + uint256 p2pSupplyIndex, + uint256 p2pBorrowIndex, + uint256 poolSupplyIndex, + uint256 poolBorrowIndex + ) = lens.getIndexes(market); + + expectedValues[i].p2pSupplyIndex = p2pSupplyIndex; + expectedValues[i].p2pBorrowIndex = p2pBorrowIndex; + expectedValues[i].poolSupplyIndex = poolSupplyIndex; + expectedValues[i].poolBorrowIndex = poolBorrowIndex; + } + + vm.prank(proxyAdmin.owner()); + proxyAdmin.upgrade(lensProxy, address(lensImplV2)); + + for (uint256 i; i < marketsCreated.length; ++i) { + address market = marketsCreated[i]; + if (market == aStEth) continue; + + ( + uint256 p2pSupplyIndex, + uint256 p2pBorrowIndex, + uint256 poolSupplyIndex, + uint256 poolBorrowIndex + ) = lens.getIndexes(market); + + assertEq(expectedValues[i].p2pSupplyIndex, p2pSupplyIndex); + assertEq(expectedValues[i].p2pBorrowIndex, p2pBorrowIndex); + assertEq(expectedValues[i].poolSupplyIndex, poolSupplyIndex); + assertEq(expectedValues[i].poolBorrowIndex, poolBorrowIndex); + } + } +}