diff --git a/contracts/aave-v2/ExitPositionsManager.sol b/contracts/aave-v2/ExitPositionsManager.sol index 32b450092..bfb08ea30 100644 --- a/contracts/aave-v2/ExitPositionsManager.sol +++ b/contracts/aave-v2/ExitPositionsManager.sol @@ -67,6 +67,11 @@ 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. @@ -250,6 +255,47 @@ 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 increase deltas. + /// @param _amount The maximum amount to add to the deltas (in underlying). + function increaseP2PDeltasLogic(address _poolToken, uint256 _amount) + external + isMarketCreated(_poolToken) + { + _updateIndexes(_poolToken); + + Types.Delta storage deltas = deltas[_poolToken]; + Types.PoolIndexes memory poolIndexes = poolIndexes[_poolToken]; + + _amount = Math.min( + _amount, + Math.min( + deltas.p2pSupplyAmount.rayMul(p2pSupplyIndex[_poolToken]).zeroFloorSub( + deltas.p2pSupplyDelta.rayMul(poolIndexes.poolSupplyIndex) + ), + deltas.p2pBorrowAmount.rayMul(p2pBorrowIndex[_poolToken]).zeroFloorSub( + deltas.p2pBorrowDelta.rayMul(poolIndexes.poolBorrowIndex) + ) + ) + ); + + deltas.p2pSupplyDelta += _amount.rayDiv(poolIndexes.poolSupplyIndex); + deltas.p2pSupplyDelta = deltas.p2pSupplyDelta; + deltas.p2pBorrowDelta += _amount.rayDiv(poolIndexes.poolBorrowIndex); + deltas.p2pBorrowDelta = deltas.p2pBorrowDelta; + emit P2PSupplyDeltaUpdated(_poolToken, deltas.p2pSupplyDelta); + emit P2PBorrowDeltaUpdated(_poolToken, deltas.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. @@ -498,8 +544,9 @@ 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) - - delta.p2pSupplyDelta.rayMul(vars.poolSupplyIndex)) + delta.p2pSupplyAmount.rayMul(vars.p2pSupplyIndex).zeroFloorSub( + delta.p2pSupplyDelta.rayMul(vars.poolSupplyIndex) + ) ); if (vars.feeToRepay > 0) { diff --git a/contracts/aave-v2/MorphoGovernance.sol b/contracts/aave-v2/MorphoGovernance.sol index b78442f12..611278ee1 100644 --- a/contracts/aave-v2/MorphoGovernance.sol +++ b/contracts/aave-v2/MorphoGovernance.sol @@ -13,6 +13,7 @@ abstract contract MorphoGovernance is MorphoUtils { using ReserveConfiguration for DataTypes.ReserveConfigurationMap; using PercentageMath for uint256; using SafeTransferLib for ERC20; + using DelegateCall for address; using WadRayMath for uint256; /// EVENTS /// @@ -327,6 +328,21 @@ abstract contract MorphoGovernance is MorphoUtils { pool.setUserUseReserveAsCollateral(market[_poolToken].underlyingToken, _newStatus); } + /// @notice Increases 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 increase deltas. + /// @param _amount The maximum amount to add to the deltas (in underlying). + function increaseP2PDeltas(address _poolToken, uint256 _amount) external onlyOwner { + address(exitPositionsManager).functionDelegateCall( + abi.encodeWithSelector( + IExitPositionsManager.increaseP2PDeltasLogic.selector, + _poolToken, + _amount + ) + ); + } + /// @notice Transfers the protocol reserve fee to the DAO. /// @param _poolTokens The addresses of the pool token addresses on which to claim the reserve fee. /// @param _amounts The list of amounts of underlying tokens to claim on each market. diff --git a/contracts/aave-v2/interfaces/IExitPositionsManager.sol b/contracts/aave-v2/interfaces/IExitPositionsManager.sol index a5e2415fb..f834cef76 100644 --- a/contracts/aave-v2/interfaces/IExitPositionsManager.sol +++ b/contracts/aave-v2/interfaces/IExitPositionsManager.sol @@ -24,4 +24,6 @@ interface IExitPositionsManager { address _borrower, uint256 _amount ) external; + + function increaseP2PDeltasLogic(address _poolToken, uint256 _amount) external; } diff --git a/contracts/aave-v2/interfaces/aave/IVariableDebtToken.sol b/contracts/aave-v2/interfaces/aave/IVariableDebtToken.sol index 5eee30748..a14a16d3e 100644 --- a/contracts/aave-v2/interfaces/aave/IVariableDebtToken.sol +++ b/contracts/aave-v2/interfaces/aave/IVariableDebtToken.sol @@ -69,4 +69,10 @@ 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-v3/ExitPositionsManager.sol b/contracts/aave-v3/ExitPositionsManager.sol index a9adcf761..3acfd5aec 100644 --- a/contracts/aave-v3/ExitPositionsManager.sol +++ b/contracts/aave-v3/ExitPositionsManager.sol @@ -288,35 +288,35 @@ 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 { + /// @param _poolToken The address of the market on which to increase deltas. + /// @param _amount The maximum amount to add to the deltas (in underlying). + function increaseP2PDeltasLogic(address _poolToken, uint256 _amount) + external + isMarketCreated(_poolToken) + { _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) + deltas.p2pSupplyAmount.rayMul(p2pSupplyIndex[_poolToken]).zeroFloorSub( + deltas.p2pSupplyDelta.rayMul(poolIndexes.poolSupplyIndex) ), - deltasMem.p2pBorrowAmount.rayMul(p2pBorrowIndex).zeroFloorSub( - deltasMem.p2pBorrowDelta.rayMul(poolIndexes.poolBorrowIndex) + deltas.p2pBorrowAmount.rayMul(p2pBorrowIndex[_poolToken]).zeroFloorSub( + deltas.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); + deltas.p2pSupplyDelta += _amount.rayDiv(poolIndexes.poolSupplyIndex); + deltas.p2pSupplyDelta = deltas.p2pSupplyDelta; + deltas.p2pBorrowDelta += _amount.rayDiv(poolIndexes.poolBorrowIndex); + deltas.p2pBorrowDelta = deltas.p2pBorrowDelta; + emit P2PSupplyDeltaUpdated(_poolToken, deltas.p2pSupplyDelta); + emit P2PBorrowDeltaUpdated(_poolToken, deltas.p2pBorrowDelta); ERC20 underlyingToken = ERC20(market[_poolToken].underlyingToken); _borrowFromPool(underlyingToken, _amount); diff --git a/contracts/aave-v3/MorphoGovernance.sol b/contracts/aave-v3/MorphoGovernance.sol index e5e49b2b9..3019475d2 100644 --- a/contracts/aave-v3/MorphoGovernance.sol +++ b/contracts/aave-v3/MorphoGovernance.sol @@ -413,16 +413,12 @@ abstract contract MorphoGovernance is MorphoUtils { emit DeprecatedStatusSet(_poolToken, _isDeprecated); } - /// @notice Creates peer-to-peer deltas, to put some liquidity back on the pool. + /// @notice Increases 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) - { + /// @param _poolToken The address of the market on which to increase deltas. + /// @param _amount The maximum amount to add to the deltas (in underlying). + function increaseP2PDeltas(address _poolToken, uint256 _amount) external onlyOwner { address(exitPositionsManager).functionDelegateCall( abi.encodeWithSelector( IExitPositionsManager.increaseP2PDeltasLogic.selector, diff --git a/contracts/compound/MorphoGovernance.sol b/contracts/compound/MorphoGovernance.sol index d1cc10477..4bd1135c3 100644 --- a/contracts/compound/MorphoGovernance.sol +++ b/contracts/compound/MorphoGovernance.sol @@ -9,6 +9,7 @@ import "./MorphoUtils.sol"; /// @notice Governance functions for Morpho. abstract contract MorphoGovernance is MorphoUtils { using SafeTransferLib for ERC20; + using DelegateCall for address; /// EVENTS /// @@ -274,6 +275,21 @@ abstract contract MorphoGovernance is MorphoUtils { emit ClaimRewardsPauseStatusSet(_newStatus); } + /// @notice Increases 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 increase deltas. + /// @param _amount The maximum amount to add to the deltas (in underlying). + function increaseP2PDeltas(address _poolToken, uint256 _amount) external onlyOwner { + address(positionsManager).functionDelegateCall( + abi.encodeWithSelector( + IPositionsManager.increaseP2PDeltasLogic.selector, + _poolToken, + _amount + ) + ); + } + /// @notice Transfers the protocol reserve fee to the DAO. /// @param _poolTokens The addresses of the pool token addresses on which to claim the reserve fee. /// @param _amounts The list of amounts of underlying tokens to claim on each market. diff --git a/contracts/compound/PositionsManager.sol b/contracts/compound/PositionsManager.sol index 92cdf6511..d0fd12ac1 100644 --- a/contracts/compound/PositionsManager.sol +++ b/contracts/compound/PositionsManager.sol @@ -95,6 +95,11 @@ 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. @@ -493,6 +498,46 @@ 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 increase deltas. + /// @param _amount The maximum amount to add to the deltas (in underlying). + function increaseP2PDeltasLogic(address _poolToken, uint256 _amount) + external + isMarketCreated(_poolToken) + { + _updateP2PIndexes(_poolToken); + + Types.Delta storage deltas = deltas[_poolToken]; + Types.LastPoolIndexes memory lastPoolIndexes = lastPoolIndexes[_poolToken]; + + uint256 poolSupplyIndex = ICToken(_poolToken).exchangeRateStored(); + _amount = Math.min( + _amount, + Math.min( + deltas.p2pSupplyAmount.mul(p2pSupplyIndex[_poolToken]).safeSub( + deltas.p2pSupplyDelta.mul(poolSupplyIndex) + ), + deltas.p2pBorrowAmount.mul(p2pBorrowIndex[_poolToken]).safeSub( + deltas.p2pBorrowDelta.mul(lastPoolIndexes.lastBorrowPoolIndex) + ) + ) + ); + + deltas.p2pSupplyDelta += _amount.div(poolSupplyIndex); + deltas.p2pSupplyDelta = deltas.p2pSupplyDelta; + deltas.p2pBorrowDelta += _amount.div(lastPoolIndexes.lastBorrowPoolIndex); + deltas.p2pBorrowDelta = deltas.p2pBorrowDelta; + emit P2PSupplyDeltaUpdated(_poolToken, deltas.p2pSupplyDelta); + emit P2PBorrowDeltaUpdated(_poolToken, deltas.p2pBorrowDelta); + + _borrowFromPool(_poolToken, _amount); + _supplyToPool(_poolToken, _getUnderlying(_poolToken), _amount); + + emit P2PDeltasIncreased(_poolToken, _amount); + } + /// INTERNAL /// /// @dev Implements withdraw logic without security checks. @@ -754,8 +799,9 @@ 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) - - delta.p2pSupplyDelta.mul(ICToken(_poolToken).exchangeRateStored())) + delta.p2pSupplyAmount.mul(vars.p2pSupplyIndex).safeSub( + delta.p2pSupplyDelta.mul(ICToken(_poolToken).exchangeRateStored()) + ) ); if (vars.feeToRepay > 0) { diff --git a/contracts/compound/interfaces/IPositionsManager.sol b/contracts/compound/interfaces/IPositionsManager.sol index 60491dbb4..8c79526fb 100644 --- a/contracts/compound/interfaces/IPositionsManager.sol +++ b/contracts/compound/interfaces/IPositionsManager.sol @@ -38,4 +38,6 @@ interface IPositionsManager { address _borrower, uint256 _amount ) external; + + function increaseP2PDeltasLogic(address _poolToken, uint256 _amount) external; } diff --git a/test-foundry/aave-v2/TestGovernance.t.sol b/test-foundry/aave-v2/TestGovernance.t.sol index 017367573..1ec32f7dc 100644 --- a/test-foundry/aave-v2/TestGovernance.t.sol +++ b/test-foundry/aave-v2/TestGovernance.t.sol @@ -203,4 +203,138 @@ contract TestGovernance is TestSetup { morpho.setPauseStatusForAllMarkets(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 testShouldNotIncreaseP2PDeltasWhenMarketNotCreated() 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 = 101 ether; + uint256 borrowAmount = 51 ether; + uint256 deltaAmount = 25 ether; + uint256 increaseDeltaAmount = 81 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 = 101 ether; + uint256 borrowAmount = 51 ether; + uint256 deltaAmount = 25 ether; + uint256 increaseDeltaAmount = 81 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)), + borrowAmount - deltaAmount, + 1e8, + "3" + ); + assertApproxEqRel( + IVariableDebtToken(variableDebtDai).balanceOf(address(morpho)), + borrowAmount, + 1e8, + "4" + ); + } + + function testFailCallIncreaseP2PDeltasFromImplementation() public { + exitPositionsManager.increaseP2PDeltasLogic(aDai, 0); + } } diff --git a/test-foundry/aave-v3/TestGovernance.t.sol b/test-foundry/aave-v3/TestGovernance.t.sol index 62d9644e4..211964812 100644 --- a/test-foundry/aave-v3/TestGovernance.t.sol +++ b/test-foundry/aave-v3/TestGovernance.t.sol @@ -241,7 +241,7 @@ contract TestGovernance is TestSetup { morpho.increaseP2PDeltas(aDai, 1 ether); } - function testShouldIncreaseP2PDeltasWhenMarketNotCreated() public { + function testShouldNotIncreaseP2PDeltasWhenMarketNotCreated() public { hevm.expectRevert(abi.encodeWithSignature("MarketNotCreated()")); morpho.increaseP2PDeltas(address(1), 0); } @@ -279,10 +279,10 @@ contract TestGovernance is TestSetup { } function testIncreaseP2PDeltasMoreThanWhatIsPossibleSupply() public { - uint256 supplyAmount = 100 ether; - uint256 borrowAmount = 50 ether; + uint256 supplyAmount = 101 ether; + uint256 borrowAmount = 51 ether; uint256 deltaAmount = 25 ether; - uint256 increaseDeltaAmount = 80 ether; + uint256 increaseDeltaAmount = 81 ether; supplier1.approve(usdc, type(uint256).max); supplier1.supply(aUsdc, to6Decimals(supplyAmount)); @@ -316,10 +316,10 @@ contract TestGovernance is TestSetup { } function testIncreaseP2PDeltasMoreThanWhatIsPossibleBorrow() public { - uint256 supplyAmount = 100 ether; - uint256 borrowAmount = 50 ether; + uint256 supplyAmount = 101 ether; + uint256 borrowAmount = 51 ether; uint256 deltaAmount = 25 ether; - uint256 increaseDeltaAmount = 80 ether; + uint256 increaseDeltaAmount = 81 ether; supplier1.approve(usdc, type(uint256).max); supplier1.supply(aUsdc, to6Decimals(supplyAmount)); @@ -345,7 +345,12 @@ contract TestGovernance is TestSetup { 1e8, "2" ); - assertApproxEqRel(IAToken(aDai).balanceOf(address(morpho)), deltaAmount, 1e8, "3"); + assertApproxEqRel( + IAToken(aDai).balanceOf(address(morpho)), + borrowAmount - deltaAmount, + 1e8, + "3" + ); assertApproxEqRel( IVariableDebtTokenExtended(variableDebtDai).balanceOf(address(morpho)), borrowAmount, diff --git a/test-foundry/compound/TestGovernance.t.sol b/test-foundry/compound/TestGovernance.t.sol index fb89fb232..747cf22a9 100644 --- a/test-foundry/compound/TestGovernance.t.sol +++ b/test-foundry/compound/TestGovernance.t.sol @@ -206,4 +206,133 @@ contract TestGovernance is TestSetup { morpho.setClaimRewardsPauseStatus(true); assertTrue(morpho.isClaimRewardsPaused()); } + + function testOnlyOwnerCanIncreaseP2PDeltas() public { + hevm.prank(address(supplier1)); + hevm.expectRevert("Ownable: caller is not the owner"); + morpho.increaseP2PDeltas(cDai, 0); + + morpho.increaseP2PDeltas(cDai, 0); + } + + function testShouldNotIncreaseP2PDeltasWhenMarketNotCreated() 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); + } }