From f624290266c05801ec214f523381d45f735e5611 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Thu, 7 Sep 2023 19:14:14 +0200 Subject: [PATCH 1/3] feat: update test naming --- test/forge/MarketTest.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/forge/MarketTest.sol b/test/forge/MarketTest.sol index bebcaadb..aa697e3c 100644 --- a/test/forge/MarketTest.sol +++ b/test/forge/MarketTest.sol @@ -141,7 +141,7 @@ contract MarketTest is BaseTest { assertEq(Id.unwrap(vault.supplyAllocationOrder(2)), Id.unwrap(allMarkets[0].id())); } - function testSetSupplyAllocationOrderRevertWhenMissingAtLeastOneMarket() public { + function testSetSupplyAllocationOrderRevertWhenMissingAtLeastOneMarketInTheAllocationList() public { _submitAndEnableMarket(allMarkets[0], CAP); _submitAndEnableMarket(allMarkets[1], CAP); _submitAndEnableMarket(allMarkets[2], CAP); @@ -201,7 +201,7 @@ contract MarketTest is BaseTest { assertEq(Id.unwrap(vault.withdrawAllocationOrder(2)), Id.unwrap(allMarkets[0].id())); } - function testSetWithdrawAllocationOrderRevertWhenMissingAtLeastOneMarket() public { + function testSetWithdrawAllocationOrderRevertWhenMissingAtLeastOneMarketInTheAllocationList() public { _submitAndEnableMarket(allMarkets[0], CAP); _submitAndEnableMarket(allMarkets[1], CAP); _submitAndEnableMarket(allMarkets[2], CAP); From fb95a8157e00a6a8688f6958d45e2d89bbdaec5d Mon Sep 17 00:00:00 2001 From: Rubilmax Date: Tue, 12 Sep 2023 10:02:11 +0200 Subject: [PATCH 2/3] fix(vault): sync last total assets last --- contracts/SupplyVault.sol | 33 ++++++++++++++++++++----------- contracts/libraries/EventsLib.sol | 5 ----- contracts/mocks/IrmMock.sol | 15 +++++++++----- test/forge/BaseTest.sol | 1 + 4 files changed, 32 insertions(+), 22 deletions(-) diff --git a/contracts/SupplyVault.sol b/contracts/SupplyVault.sol index a0542e8a..8bd5ebb8 100644 --- a/contracts/SupplyVault.sol +++ b/contracts/SupplyVault.sol @@ -55,8 +55,8 @@ contract SupplyVault is ERC4626, Ownable2Step, ISupplyVault { Pending public pendingTimelock; uint256 public timelock; - /// @dev Stores the total assets owned by this vault when the fee was last accrued. - uint256 lastTotalAssets; + /// @dev Stores the total assets this vault manage when it last handled a deposit/withdraw. + uint256 public lastTotalAssets; ConfigSet private _config; @@ -97,6 +97,12 @@ contract SupplyVault is ERC4626, Ownable2Step, ISupplyVault { _; } + modifier syncLastTotalAssets() { + _; + + lastTotalAssets = totalAssets(); + } + /* ONLY OWNER FUNCTIONS */ function submitPendingTimelock(uint256 newTimelock) external onlyOwner { @@ -242,13 +248,13 @@ contract SupplyVault is ERC4626, Ownable2Step, ISupplyVault { return _convertToShares(maxWithdraw(owner), Math.Rounding.Down); } - function deposit(uint256 assets, address receiver) public virtual override returns (uint256) { + function deposit(uint256 assets, address receiver) public virtual override syncLastTotalAssets returns (uint256) { _accrueFee(); return super.deposit(assets, receiver); } - function mint(uint256 shares, address receiver) public virtual override returns (uint256) { + function mint(uint256 shares, address receiver) public virtual override syncLastTotalAssets returns (uint256) { _accrueFee(); return super.mint(shares, receiver); @@ -258,6 +264,7 @@ contract SupplyVault is ERC4626, Ownable2Step, ISupplyVault { public virtual override + syncLastTotalAssets returns (uint256 shares) { _accrueFee(); @@ -268,7 +275,13 @@ contract SupplyVault is ERC4626, Ownable2Step, ISupplyVault { _withdraw(_msgSender(), receiver, owner, assets, shares); } - function redeem(uint256 shares, address receiver, address owner) public virtual override returns (uint256 assets) { + function redeem(uint256 shares, address receiver, address owner) + public + virtual + override + syncLastTotalAssets + returns (uint256 assets) + { _accrueFee(); // Do not call expensive `maxRedeem` and optimistically redeem shares. @@ -458,17 +471,13 @@ contract SupplyVault is ERC4626, Ownable2Step, ISupplyVault { function _accrueFee() internal { if (fee == 0 || feeRecipient == address(0)) return; - (uint256 newTotalAssets, uint256 feeShares) = _accruedFeeShares(); - - lastTotalAssets = newTotalAssets; + uint256 feeShares = _accruedFeeShares(); if (feeShares != 0) _mint(feeRecipient, feeShares); - - emit EventsLib.AccrueFee(newTotalAssets, feeShares); } - function _accruedFeeShares() internal view returns (uint256 newTotalAssets, uint256 feeShares) { - newTotalAssets = totalAssets(); + function _accruedFeeShares() internal view returns (uint256 feeShares) { + uint256 newTotalAssets = totalAssets(); uint256 totalInterest = newTotalAssets.zeroFloorSub(lastTotalAssets); if (totalInterest != 0) { diff --git a/contracts/libraries/EventsLib.sol b/contracts/libraries/EventsLib.sol index b9bff7a8..62cd7611 100644 --- a/contracts/libraries/EventsLib.sol +++ b/contracts/libraries/EventsLib.sol @@ -29,9 +29,4 @@ library EventsLib { event SetCap(uint128 cap); event DisableMarket(Id id); - - /// @notice Emitted when the vault's performance fee is accrued. - /// @param totalAssets The total amount of assets this vault manages. - /// @param feeShares The shares minted corresponding to the fee accrued. - event AccrueFee(uint256 totalAssets, uint256 feeShares); } diff --git a/contracts/mocks/IrmMock.sol b/contracts/mocks/IrmMock.sol index 53d07a0c..bc1bd2f1 100644 --- a/contracts/mocks/IrmMock.sol +++ b/contracts/mocks/IrmMock.sol @@ -9,15 +9,20 @@ import {MathLib} from "@morpho-blue/libraries/MathLib.sol"; contract IrmMock is IIrm { using MathLib for uint128; - function borrowRateView(MarketParams memory, Market memory market) public pure returns (uint256) { + uint256 public rate; + + function setRate(uint256 newRate) external { + rate = newRate; + } + + function borrowRateView(MarketParams memory, Market memory market) public view returns (uint256) { uint256 utilization = market.totalBorrowAssets.wDivDown(market.totalSupplyAssets); - // Divide by the number of seconds in a year. - // This is a very simple model where x% utilization corresponds to x% APR. - return utilization / 365 days; + // When rate is zero, x% utilization corresponds to x% APR. + return rate == 0 ? utilization / 365 days : rate / 365 days; } - function borrowRate(MarketParams memory marketParams, Market memory market) external pure returns (uint256) { + function borrowRate(MarketParams memory marketParams, Market memory market) external view returns (uint256) { return borrowRateView(marketParams, market); } } diff --git a/test/forge/BaseTest.sol b/test/forge/BaseTest.sol index 1cf64c01..bb432600 100644 --- a/test/forge/BaseTest.sol +++ b/test/forge/BaseTest.sol @@ -33,6 +33,7 @@ contract BaseTest is Test { address internal OWNER = _addrFromHashedString("Morpho Owner"); address internal RISK_MANAGER = _addrFromHashedString("Morpho Risk Manager"); address internal ALLOCATOR = _addrFromHashedString("Morpho Allocator"); + address internal FEE_RECIPIENT = _addrFromHashedString("MetaMorpho Fee Recipient"); uint256 internal constant LLTV = 0.8 ether; uint256 internal constant TIMELOCK = 0; From f058644ae415b1e19f389356c23f05648beb9ec4 Mon Sep 17 00:00:00 2001 From: Rubilmax Date: Tue, 12 Sep 2023 13:22:38 +0200 Subject: [PATCH 3/3] refactor(vault): rename sync to update --- contracts/SupplyVault.sol | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/contracts/SupplyVault.sol b/contracts/SupplyVault.sol index 51b2b503..aae006d7 100644 --- a/contracts/SupplyVault.sol +++ b/contracts/SupplyVault.sol @@ -97,7 +97,7 @@ contract SupplyVault is ERC4626, Ownable2Step, ISupplyVault { _; } - modifier syncLastTotalAssets() { + modifier updateLastTotalAssets() { _; lastTotalAssets = totalAssets(); @@ -144,7 +144,7 @@ contract SupplyVault is ERC4626, Ownable2Step, ISupplyVault { emit EventsLib.SubmitPendingFee(newFee); } - function setFee() external timelockElapsed(pendingFee.timestamp) onlyOwner syncLastTotalAssets { + function setFee() external timelockElapsed(pendingFee.timestamp) onlyOwner updateLastTotalAssets { // Accrue interest using the previous fee set before changing it. _accrueFee(); @@ -155,7 +155,7 @@ contract SupplyVault is ERC4626, Ownable2Step, ISupplyVault { delete pendingFee; } - function setFeeRecipient(address newFeeRecipient) external onlyOwner syncLastTotalAssets { + function setFeeRecipient(address newFeeRecipient) external onlyOwner updateLastTotalAssets { require(newFeeRecipient != feeRecipient, ErrorsLib.ALREADY_SET); // Accrue interest to the previous fee recipient set before changing it. @@ -293,7 +293,7 @@ contract SupplyVault is ERC4626, Ownable2Step, ISupplyVault { function _deposit(address caller, address owner, uint256 assets, uint256 shares) internal override - syncLastTotalAssets + updateLastTotalAssets { super._deposit(caller, owner, assets, shares); @@ -304,7 +304,7 @@ contract SupplyVault is ERC4626, Ownable2Step, ISupplyVault { function _withdraw(address caller, address receiver, address owner, uint256 assets, uint256 shares) internal override - syncLastTotalAssets + updateLastTotalAssets { require(_withdrawOrder(assets) == 0, ErrorsLib.WITHDRAW_ORDER_FAILED);