From b897f87b28c15f71ff5f86af6203c25a17514542 Mon Sep 17 00:00:00 2001 From: Jean-Grimal Date: Wed, 29 Nov 2023 17:14:43 +0100 Subject: [PATCH] feat: last fixes --- src/metamorpho/MetamorphoSnippets.sol | 45 +++++++------- .../metamorpho/TestMetamorphoSnippets.sol | 62 ++++++++----------- 2 files changed, 50 insertions(+), 57 deletions(-) diff --git a/src/metamorpho/MetamorphoSnippets.sol b/src/metamorpho/MetamorphoSnippets.sol index da47c0b..b617417 100644 --- a/src/metamorpho/MetamorphoSnippets.sol +++ b/src/metamorpho/MetamorphoSnippets.sol @@ -28,10 +28,9 @@ contract MetaMorphoSnippets { // --- VIEW FUNCTIONS --- /// @notice Returns the total assets deposited into a MetaMorpho `vault`. - /// @dev It doesn't take into account the fees accrued since the last update. /// @param vault The address of the MetaMorpho vault. function totalDepositVault(address vault) public view returns (uint256 totalAssets) { - totalAssets = IMetaMorpho(vault).lastTotalAssets(); + totalAssets = IMetaMorpho(vault).totalAssets(); } /// @notice Returns the total assets supplied into a specific morpho blue market by a MetaMorpho `vault`. @@ -78,18 +77,27 @@ contract MetaMorphoSnippets { return withdrawQueueList; } - /// @notice Returns the supply cap of a market on a MetaMorpho `vault`. + /// @notice Returns the sum of the supply caps of markets with the same collateral `token` on a MetaMorpho `vault`. + /// @dev This is a way to visualize exposure to this token. /// @param vault The address of the MetaMorpho vault. - /// @param marketParams The morpho blue market. - function capMarket(address vault, MarketParams memory marketParams) public view returns (uint192 cap) { - Id id = marketParams.id(); - cap = IMetaMorpho(vault).config(id).cap; + /// @param token The collateral token. + function totalCapAsset(address vault, address token) public view returns (uint192 totalCap) { + uint256 queueLength = IMetaMorpho(vault).withdrawQueueLength(); + + for (uint256 i; i < queueLength; ++i) { + Id idMarket = IMetaMorpho(vault).withdrawQueue(i); + MarketParams memory marketParams = morpho.idToMarketParams(idMarket); + + if (marketParams.collateralToken == token) { + totalCap += IMetaMorpho(vault).config(idMarket).cap; + } + } } - /// @notice Returns the current APR (Annual Percentage Rate) of a morpho blue market. + /// @notice Returns the current APY of a morpho blue market. /// @param marketParams The morpho blue market parameters. /// @param market The morpho blue market state. - function supplyAPRMarket(MarketParams memory marketParams, Market memory market) + function supplyAPYMarket(MarketParams memory marketParams, Market memory market) public view returns (uint256 supplyRate) @@ -102,13 +110,13 @@ contract MetaMorphoSnippets { // Get the supply rate uint256 utilization = totalBorrowAssets == 0 ? 0 : totalBorrowAssets.wDivUp(totalSupplyAssets); - supplyRate = borrowRate.wMulDown(1 ether - market.fee).wMulDown(utilization); + supplyRate = borrowRate.wTaylorCompounded(1).wMulDown(1 ether - market.fee).wMulDown(utilization); } - /// @notice Returns the current APR (Annual Percentage Rate) of a MetaMorpho vault. - /// @dev It is computed as the sum of all APR of enabled markets weighted by the supply on these markets. + /// @notice Returns the current APY of a MetaMorpho vault. + /// @dev It is computed as the sum of all APY of enabled markets weighted by the supply on these markets. /// @param vault The address of the MetaMorpho vault. - function supplyAPRVault(address vault) public view returns (uint256 avgSupplyRate) { + function supplyAPYVault(address vault) public view returns (uint256 avgSupplyRate) { uint256 ratio; uint256 queueLength = IMetaMorpho(vault).withdrawQueueLength(); @@ -120,7 +128,7 @@ contract MetaMorphoSnippets { MarketParams memory marketParams = morpho.idToMarketParams(idMarket); Market memory market = morpho.market(idMarket); - uint256 currentSupplyAPR = supplyAPRMarket(marketParams, market); + uint256 currentSupplyAPR = supplyAPYMarket(marketParams, market); uint256 vaultAsset = vaultAssetsInMarket(vault, marketParams); ratio += currentSupplyAPR.wMulDown(vaultAsset); } @@ -145,6 +153,7 @@ contract MetaMorphoSnippets { /// @notice Withdraws `assets` from the `vault` on behalf of the sender, and sends them to `receiver`. /// @dev Sender must approve the snippets contract to manage his tokens before the call. + /// @dev To withdraw all, it is recommended to use the redeem function. /// @param vault The address of the MetaMorpho vault. /// @param assets the amount to withdraw. /// @param receiver The address that will receive the withdrawn assets. @@ -155,14 +164,6 @@ contract MetaMorphoSnippets { redeemed = IMetaMorpho(vault).withdraw(assets, receiver, msg.sender); } - /// @notice Withdraws the whole sender's position from the `vault`, and sends the withdrawn amount to `receiver`. - /// @param vault The address of the MetaMorpho vault. - /// @param receiver The address that will receive the withdrawn assets. - function withdrawFromVaultAll(address vault, address receiver) public returns (uint256 redeemed) { - uint256 assets = IMetaMorpho(vault).maxWithdraw(msg.sender); - redeemed = IMetaMorpho(vault).withdraw(assets, receiver, msg.sender); - } - /// @notice Redeems the whole sender's position from the `vault`, and sends the withdrawn amount to `receiver`. /// @param vault The address of the MetaMorpho vault. /// @param receiver The address that will receive the withdrawn assets. diff --git a/test/forge/metamorpho/TestMetamorphoSnippets.sol b/test/forge/metamorpho/TestMetamorphoSnippets.sol index 0050294..c7640ee 100644 --- a/test/forge/metamorpho/TestMetamorphoSnippets.sol +++ b/test/forge/metamorpho/TestMetamorphoSnippets.sol @@ -1,13 +1,13 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.8.0; -import {MetamorphoSnippets} from "@snippets/metamorpho/MetamorphoSnippets.sol"; +import {MetaMorphoSnippets} from "@snippets/metamorpho/MetamorphoSnippets.sol"; import "@metamorpho-test/helpers/IntegrationTest.sol"; import {SafeCast} from "@openzeppelin/utils/math/SafeCast.sol"; contract TestIntegrationSnippets is IntegrationTest { - MetamorphoSnippets internal snippets; + MetaMorphoSnippets internal snippets; using MorphoBalancesLib for IMorpho; using MorphoLib for IMorpho; @@ -17,7 +17,7 @@ contract TestIntegrationSnippets is IntegrationTest { function setUp() public virtual override { super.setUp(); - snippets = new MetamorphoSnippets(address(morpho)); + snippets = new MetaMorphoSnippets(address(morpho)); _setCap(allMarkets[0], CAP); _sortSupplyQueueIdleLast(); @@ -119,13 +119,23 @@ contract TestIntegrationSnippets is IntegrationTest { assertEq(Id.unwrap(withdrawQueueList[1]), Id.unwrap(expectedWithdrawQueue[1])); } - function testCapMarket(MarketParams memory marketParams) public { - Id idMarket = marketParams.id(); + function testTotalCapAsset(uint256 capMarket1, uint256 capMarket2, uint256 capMarket3) public { + capMarket1 = bound(capMarket1, MIN_TEST_ASSETS, MAX_TEST_ASSETS); + capMarket2 = bound(capMarket2, MIN_TEST_ASSETS, MAX_TEST_ASSETS); + capMarket3 = bound(capMarket3, MIN_TEST_ASSETS, MAX_TEST_ASSETS); - assertEq(vault.config(idMarket).cap, snippets.capMarket(address(vault), marketParams), "cap per market"); + _setCap(allMarkets[0], capMarket1); + _setCap(allMarkets[1], capMarket2); + _setCap(allMarkets[2], capMarket3); + + assertEq( + capMarket1 + capMarket2 + capMarket3, + snippets.totalCapAsset(address(vault), address(collateralToken)), + "cap per market" + ); } - function testSupplyAPR0(Market memory market) public { + function testSupplyAPY0(Market memory market) public { vm.assume(market.totalBorrowAssets == 0); vm.assume(market.lastUpdate > 0); vm.assume(market.fee < 1 ether); @@ -143,10 +153,10 @@ contract TestIntegrationSnippets is IntegrationTest { 0, "Diff in snippets vs integration supplyAPR test" ); - assertEq(snippets.supplyAPRMarket(marketParams, market), 0, "Diff in snippets vs integration supplyAPR test"); + assertEq(snippets.supplyAPYMarket(marketParams, market), 0, "Diff in snippets vs integration supplyAPR test"); } - function testSupplyAPRMarket(Market memory market) public { + function testSupplyAPYMarket(Market memory market) public { vm.assume(market.totalBorrowAssets > 0); vm.assume(market.totalBorrowShares > 0); vm.assume(market.totalSupplyAssets > 0); @@ -159,21 +169,21 @@ contract TestIntegrationSnippets is IntegrationTest { uint256 borrowTrue = irm.borrowRateView(marketParams, market); uint256 utilization = totalBorrowAssets == 0 ? 0 : totalBorrowAssets.wDivUp(totalSupplyAssets); - uint256 supplyTrue = borrowTrue.wMulDown(1 ether - market.fee).wMulDown(utilization); + uint256 supplyTrue = borrowTrue.wTaylorCompounded(1).wMulDown(1 ether - market.fee).wMulDown(utilization); - uint256 supplyToTest = snippets.supplyAPRMarket(marketParams, market); + uint256 supplyToTest = snippets.supplyAPYMarket(marketParams, market); // handling in if-else the situation where utilization = 0 otherwise too many rejects if (utilization == 0) { - assertEq(supplyTrue, 0, "supply rate ==0"); + assertEq(supplyTrue, 0, "supply rate == 0"); assertEq(supplyTrue, supplyToTest, "Diff in snippets vs integration supplyAPR test"); } else { - assertGt(supplyTrue, 0, "supply rate ==0"); + assertGt(supplyTrue, 0, "supply rate == 0"); assertEq(supplyTrue, supplyToTest, "Diff in snippets vs integration supplyAPR test"); } } - function testSupplyAPRVault(uint256 firstDeposit, uint256 secondDeposit, uint256 firstBorrow, uint256 secondBorrow) + function testSupplyAPYVault(uint256 firstDeposit, uint256 secondDeposit, uint256 firstBorrow, uint256 secondBorrow) public { firstDeposit = bound(firstDeposit, MIN_TEST_ASSETS, MAX_TEST_ASSETS / 2); @@ -212,13 +222,13 @@ contract TestIntegrationSnippets is IntegrationTest { Market memory market0 = morpho.market(id0); Market memory market1 = morpho.market(id1); - uint256 rateMarket0 = snippets.supplyAPRMarket(allMarkets[0], market0); - uint256 rateMarket1 = snippets.supplyAPRMarket(allMarkets[1], market1); + uint256 rateMarket0 = snippets.supplyAPYMarket(allMarkets[0], market0); + uint256 rateMarket1 = snippets.supplyAPYMarket(allMarkets[1], market1); uint256 avgRateNum = rateMarket0.wMulDown(firstDeposit) + rateMarket1.wMulDown(secondDeposit); uint256 expectedAvgRate = avgRateNum.wDivUp(firstDeposit + secondDeposit); - uint256 avgSupplyRateSnippets = snippets.supplyAPRVault(address(vault)); + uint256 avgSupplyRateSnippets = snippets.supplyAPYVault(address(vault)); assertEq(avgSupplyRateSnippets, expectedAvgRate, "avgSupplyRateSnippets == 0"); } @@ -249,24 +259,6 @@ contract TestIntegrationSnippets is IntegrationTest { assertEq(vault.balanceOf(SUPPLIER), shares - redeemed, "balanceOf(SUPPLIER)"); } - function testWithdrawFromVaultAll(uint256 assets) public { - assets = bound(assets, MIN_TEST_ASSETS, MAX_TEST_ASSETS); - - loanToken.setBalance(SUPPLIER, assets); - vm.startPrank(SUPPLIER); - uint256 minted = vault.deposit(assets, SUPPLIER); - - assertEq(vault.maxWithdraw(SUPPLIER), assets, "maxWithdraw(SUPPLIER)"); - - uint256 redeemed = snippets.withdrawFromVaultAll(address(vault), SUPPLIER); - vm.stopPrank(); - - assertEq(redeemed, minted, "shares"); - assertEq(vault.balanceOf(SUPPLIER), 0, "balanceOf(SUPPLIER)"); - assertEq(loanToken.balanceOf(SUPPLIER), assets, "loanToken.balanceOf(SUPPLIER)"); - assertEq(morpho.expectedSupplyAssets(allMarkets[0], address(vault)), 0, "expectedSupplyAssets(vault)"); - } - function testRedeemAllFromVault(uint256 deposited) public { deposited = bound(deposited, MIN_TEST_ASSETS, MAX_TEST_ASSETS);