From ebddee7582916d6b42a3181059adc95f27fbc280 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Mon, 14 Aug 2023 16:17:06 +0200 Subject: [PATCH 1/4] feat: add return values liquidate --- src/Morpho.sol | 21 ++++++++++++--------- src/interfaces/IMorpho.sol | 8 ++++++-- test/forge/Morpho.t.sol | 6 ++++-- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/Morpho.sol b/src/Morpho.sol index 0fb6eb187..cace6d16f 100644 --- a/src/Morpho.sol +++ b/src/Morpho.sol @@ -327,7 +327,10 @@ contract Morpho is IMorpho { /* LIQUIDATION */ /// @inheritdoc IMorpho - function liquidate(Market memory market, address borrower, uint256 seized, bytes calldata data) external { + function liquidate(Market memory market, address borrower, uint256 seized, bytes calldata data) + external + returns (uint256 assetsRepaid, uint256 sharesRepaid) + { Id id = market.id(); require(lastUpdate[id] != 0, ErrorsLib.MARKET_NOT_CREATED); require(seized != 0, ErrorsLib.ZERO_ASSETS); @@ -338,13 +341,13 @@ contract Morpho is IMorpho { require(!_isHealthy(market, id, borrower, collateralPrice), ErrorsLib.HEALTHY_POSITION); - uint256 repaid = + assetsRepaid = seized.mulDivUp(collateralPrice, ORACLE_PRICE_SCALE).wDivUp(liquidationIncentiveFactor(market.lltv)); - uint256 repaidShares = repaid.toSharesDown(totalBorrow[id], totalBorrowShares[id]); + sharesRepaid = assetsRepaid.toSharesDown(totalBorrow[id], totalBorrowShares[id]); - borrowShares[id][borrower] -= repaidShares; - totalBorrowShares[id] -= repaidShares; - totalBorrow[id] -= repaid; + borrowShares[id][borrower] -= sharesRepaid; + totalBorrowShares[id] -= sharesRepaid; + totalBorrow[id] -= assetsRepaid; collateral[id][borrower] -= seized; @@ -361,11 +364,11 @@ contract Morpho is IMorpho { IERC20(market.collateralToken).safeTransfer(msg.sender, seized); - emit EventsLib.Liquidate(id, msg.sender, borrower, repaid, repaidShares, seized, badDebtShares); + emit EventsLib.Liquidate(id, msg.sender, borrower, assetsRepaid, sharesRepaid, seized, badDebtShares); - if (data.length > 0) IMorphoLiquidateCallback(msg.sender).onMorphoLiquidate(repaid, data); + if (data.length > 0) IMorphoLiquidateCallback(msg.sender).onMorphoLiquidate(assetsRepaid, data); - IERC20(market.borrowableToken).safeTransferFrom(msg.sender, address(this), repaid); + IERC20(market.borrowableToken).safeTransferFrom(msg.sender, address(this), assetsRepaid); } /* FLASH LOANS */ diff --git a/src/interfaces/IMorpho.sol b/src/interfaces/IMorpho.sol index ac6d476d6..5967e9333 100644 --- a/src/interfaces/IMorpho.sol +++ b/src/interfaces/IMorpho.sol @@ -212,8 +212,12 @@ interface IMorpho is IFlashLender { /// @param market The market of the position. /// @param borrower The owner of the position. /// @param seized The assets of collateral to seize. - /// @param data Arbitrary data to pass to the `onMorphoLiquidate` callback. Pass empty data if not needed - function liquidate(Market memory market, address borrower, uint256 seized, bytes memory data) external; + /// @param data Arbitrary data to pass to the `onMorphoLiquidate` callback. Pass empty data if not needed. + /// @return assetsRepaid The assets of assets repaid. + /// @return sharesRepaid The assets of shares repaid. + function liquidate(Market memory market, address borrower, uint256 seized, bytes memory data) + external + returns (uint256 assetsRepaid, uint256 sharesRepaid); /// @notice Sets the authorization for `authorized` to manage `msg.sender`'s positions. /// @param authorized The authorized address. diff --git a/test/forge/Morpho.t.sol b/test/forge/Morpho.t.sol index 8acb6f8a7..cd628df74 100644 --- a/test/forge/Morpho.t.sol +++ b/test/forge/Morpho.t.sol @@ -641,7 +641,7 @@ contract MorphoTest is // Liquidate vm.prank(LIQUIDATOR); - morpho.liquidate(market, BORROWER, toSeize, hex""); + (uint256 assetsRepaid,) = morpho.liquidate(market, BORROWER, toSeize, hex""); uint256 liquidatorNetWorthAfter = netWorth(LIQUIDATOR); uint256 collateralPrice = IOracle(market.oracle).price(); @@ -650,6 +650,7 @@ contract MorphoTest is toSeize.mulDivUp(collateralPrice, ORACLE_PRICE_SCALE).wDivUp(liquidationIncentiveFactor); uint256 expectedNetWorthAfter = liquidatorNetWorthBefore + toSeize.mulDivDown(collateralPrice, ORACLE_PRICE_SCALE) - expectedRepaid; + assertEq(assetsRepaid, expectedRepaid, "wrong return repaid value"); assertEq(liquidatorNetWorthAfter, expectedNetWorthAfter, "LIQUIDATOR net worth"); assertApproxEqAbs(borrowBalance(BORROWER), assetsBorrowed - expectedRepaid, 100, "BORROWER balance"); assertEq(morpho.collateral(id, BORROWER), assetsCollateral - toSeize, "BORROWER collateral"); @@ -686,7 +687,7 @@ contract MorphoTest is // Liquidate vm.prank(LIQUIDATOR); - morpho.liquidate(market, BORROWER, toSeize, hex""); + (uint256 assetsRepaid,) = morpho.liquidate(market, BORROWER, toSeize, hex""); uint256 liquidatorNetWorthAfter = netWorth(LIQUIDATOR); uint256 collateralPrice = IOracle(market.oracle).price(); @@ -695,6 +696,7 @@ contract MorphoTest is toSeize.mulDivUp(collateralPrice, ORACLE_PRICE_SCALE).wDivUp(liquidationIncentiveFactor); uint256 expectedNetWorthAfter = liquidatorNetWorthBefore + toSeize.mulDivDown(collateralPrice, ORACLE_PRICE_SCALE) - expectedRepaid; + assertEq(assetsRepaid, expectedRepaid, "wrong return repaid value"); assertEq(liquidatorNetWorthAfter, expectedNetWorthAfter, "LIQUIDATOR net worth"); assertEq(borrowBalance(BORROWER), 0, "BORROWER balance"); assertEq(morpho.collateral(id, BORROWER), 0, "BORROWER collateral"); From 7a066b1e3706dccc97930b18c0bc2c9bdafd8747 Mon Sep 17 00:00:00 2001 From: Merlin Egalite <44097430+MerlinEgalite@users.noreply.github.com> Date: Mon, 14 Aug 2023 17:13:10 +0200 Subject: [PATCH 2/4] docs: apply suggestions Co-authored-by: makcandrov Signed-off-by: Merlin Egalite <44097430+MerlinEgalite@users.noreply.github.com> --- src/interfaces/IMorpho.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/interfaces/IMorpho.sol b/src/interfaces/IMorpho.sol index 5967e9333..af5ad7168 100644 --- a/src/interfaces/IMorpho.sol +++ b/src/interfaces/IMorpho.sol @@ -213,8 +213,8 @@ interface IMorpho is IFlashLender { /// @param borrower The owner of the position. /// @param seized The assets of collateral to seize. /// @param data Arbitrary data to pass to the `onMorphoLiquidate` callback. Pass empty data if not needed. - /// @return assetsRepaid The assets of assets repaid. - /// @return sharesRepaid The assets of shares repaid. + /// @return assetsRepaid The amount of assets repaid. + /// @return sharesRepaid The amount of shares repaid. function liquidate(Market memory market, address borrower, uint256 seized, bytes memory data) external returns (uint256 assetsRepaid, uint256 sharesRepaid); From c221e0ec051024d2b30fd85bf3fe07be17b79c7c Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Mon, 14 Aug 2023 17:21:58 +0200 Subject: [PATCH 3/4] docs: fix amount --- src/interfaces/IMorpho.sol | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/interfaces/IMorpho.sol b/src/interfaces/IMorpho.sol index af5ad7168..5a51d9bbd 100644 --- a/src/interfaces/IMorpho.sol +++ b/src/interfaces/IMorpho.sol @@ -125,11 +125,11 @@ interface IMorpho is IFlashLender { /// for full compatibility and precision. /// @dev Supplying a large amount can overflow and revert without any error message. /// @param market The market to supply assets to. - /// @param assets The assets of assets to supply. + /// @param assets The amount of assets to supply. /// @param shares The assets of shares to mint. /// @param onBehalf The address that will receive the position. /// @param data Arbitrary data to pass to the `onMorphoSupply` callback. Pass empty data if not needed. - /// @return assetsSupplied The assets of assets supplied. + /// @return assetsSupplied The amount of assets supplied. /// @return sharesSupplied The assets of shares supplied. function supply(Market memory market, uint256 assets, uint256 shares, address onBehalf, bytes memory data) external @@ -141,11 +141,11 @@ interface IMorpho is IFlashLender { /// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions. /// @dev Withdrawing an amount corresponding to more shares than supplied will underflow and revert without any error message. /// @param market The market to withdraw assets from. - /// @param shares The assets of assets to withdraw. + /// @param shares The amount of assets to withdraw. /// @param shares The assets of shares to burn. /// @param onBehalf The address of the owner of the withdrawn assets. /// @param receiver The address that will receive the withdrawn assets. - /// @return assetsWithdrawn The assets of assets withdrawn. + /// @return assetsWithdrawn The amount of assets withdrawn. /// @return sharesWithdrawn The assets of shares withdrawn. function withdraw(Market memory market, uint256 assets, uint256 shares, address onBehalf, address receiver) external @@ -160,11 +160,11 @@ interface IMorpho is IFlashLender { /// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions. /// @dev Borrowing a large amount can overflow and revert without any error message. /// @param market The market to borrow assets from. - /// @param assets The assets of assets to borrow. + /// @param assets The amount of assets to borrow. /// @param shares The assets of shares to mint. /// @param onBehalf The address of the owner of the debt. /// @param receiver The address that will receive the debt. - /// @return assetsBorrowed The assets of assets borrowed. + /// @return assetsBorrowed The amount of assets borrowed. /// @return sharesBorrowed The assets of shares borrowed. function borrow(Market memory market, uint256 assets, uint256 shares, address onBehalf, address receiver) external @@ -176,11 +176,11 @@ interface IMorpho is IFlashLender { /// To repay the whole debt, pass the `shares`'s balance of `onBehalf`. /// @dev Repaying an amount corresponding to more shares than borrowed will underflow and revert without any error message. /// @param market The market to repay assets to. - /// @param assets The assets of assets to repay. + /// @param assets The amount of assets to repay. /// @param shares The assets of shares to burn. /// @param onBehalf The address of the owner of the debt. /// @param data Arbitrary data to pass to the `onMorphoRepay` callback. Pass empty data if not needed. - /// @return assetsRepaid The assets of assets repaid. + /// @return assetsRepaid The amount of assets repaid. /// @return sharesRepaid The assets of shares repaid. function repay(Market memory market, uint256 assets, uint256 shares, address onBehalf, bytes memory data) external From b050b66069038794bb8b0197f20f8b3829776be1 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Mon, 14 Aug 2023 18:12:11 +0200 Subject: [PATCH 4/4] docs: fix amount again --- src/interfaces/IMorpho.sol | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/interfaces/IMorpho.sol b/src/interfaces/IMorpho.sol index 5a51d9bbd..27ae16378 100644 --- a/src/interfaces/IMorpho.sol +++ b/src/interfaces/IMorpho.sol @@ -121,16 +121,16 @@ interface IMorpho is IFlashLender { /// @dev Either `assets` or `shares` should be zero. /// Most usecases should rely on `assets` as an input so the caller /// is guaranteed to have `assets` tokens pulled from their balance, - /// but the possibility to mint a specific assets of shares is given + /// but the possibility to mint a specific amount of shares is given /// for full compatibility and precision. /// @dev Supplying a large amount can overflow and revert without any error message. /// @param market The market to supply assets to. /// @param assets The amount of assets to supply. - /// @param shares The assets of shares to mint. + /// @param shares The amount of shares to mint. /// @param onBehalf The address that will receive the position. /// @param data Arbitrary data to pass to the `onMorphoSupply` callback. Pass empty data if not needed. /// @return assetsSupplied The amount of assets supplied. - /// @return sharesSupplied The assets of shares supplied. + /// @return sharesSupplied The amount of shares supplied. function supply(Market memory market, uint256 assets, uint256 shares, address onBehalf, bytes memory data) external returns (uint256 assetsSupplied, uint256 sharesSupplied); @@ -141,12 +141,12 @@ interface IMorpho is IFlashLender { /// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions. /// @dev Withdrawing an amount corresponding to more shares than supplied will underflow and revert without any error message. /// @param market The market to withdraw assets from. - /// @param shares The amount of assets to withdraw. - /// @param shares The assets of shares to burn. + /// @param assets The amount of assets to withdraw. + /// @param shares The amount of shares to burn. /// @param onBehalf The address of the owner of the withdrawn assets. /// @param receiver The address that will receive the withdrawn assets. /// @return assetsWithdrawn The amount of assets withdrawn. - /// @return sharesWithdrawn The assets of shares withdrawn. + /// @return sharesWithdrawn The amount of shares withdrawn. function withdraw(Market memory market, uint256 assets, uint256 shares, address onBehalf, address receiver) external returns (uint256 assetsWithdrawn, uint256 sharesWithdrawn); @@ -155,17 +155,17 @@ interface IMorpho is IFlashLender { /// @dev Either `assets` or `shares` should be zero. /// Most usecases should rely on `assets` as an input so the caller /// is guaranteed to borrow `assets` of tokens, - /// but the possibility to burn a specific assets of shares is given + /// but the possibility to burn a specific amount of shares is given /// for full compatibility and precision. /// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions. /// @dev Borrowing a large amount can overflow and revert without any error message. /// @param market The market to borrow assets from. /// @param assets The amount of assets to borrow. - /// @param shares The assets of shares to mint. + /// @param shares The amount of shares to mint. /// @param onBehalf The address of the owner of the debt. /// @param receiver The address that will receive the debt. /// @return assetsBorrowed The amount of assets borrowed. - /// @return sharesBorrowed The assets of shares borrowed. + /// @return sharesBorrowed The amount of shares borrowed. function borrow(Market memory market, uint256 assets, uint256 shares, address onBehalf, address receiver) external returns (uint256 assetsBorrowed, uint256 sharesBorrowed); @@ -177,11 +177,11 @@ interface IMorpho is IFlashLender { /// @dev Repaying an amount corresponding to more shares than borrowed will underflow and revert without any error message. /// @param market The market to repay assets to. /// @param assets The amount of assets to repay. - /// @param shares The assets of shares to burn. + /// @param shares The amount of shares to burn. /// @param onBehalf The address of the owner of the debt. /// @param data Arbitrary data to pass to the `onMorphoRepay` callback. Pass empty data if not needed. /// @return assetsRepaid The amount of assets repaid. - /// @return sharesRepaid The assets of shares repaid. + /// @return sharesRepaid The amount of shares repaid. function repay(Market memory market, uint256 assets, uint256 shares, address onBehalf, bytes memory data) external returns (uint256 assetsRepaid, uint256 sharesRepaid);