From b7fc2b10e765dfc0e308594c16fd657cda928ad6 Mon Sep 17 00:00:00 2001 From: Rubilmax Date: Tue, 25 Jul 2023 10:48:15 +0200 Subject: [PATCH 01/24] feat(accrued-interests): add interests library --- src/interfaces/IIrm.sol | 2 +- src/libraries/BlueLib.sol | 59 +++++++++++++++++++++++ src/libraries/BlueStorageSlots.sol | 77 ++++++++++++++++++++++++++++++ 3 files changed, 137 insertions(+), 1 deletion(-) create mode 100644 src/libraries/BlueLib.sol create mode 100644 src/libraries/BlueStorageSlots.sol diff --git a/src/interfaces/IIrm.sol b/src/interfaces/IIrm.sol index 128fcba55..7ddfaa24a 100644 --- a/src/interfaces/IIrm.sol +++ b/src/interfaces/IIrm.sol @@ -4,5 +4,5 @@ pragma solidity >=0.5.0; import {Market} from "src/libraries/MarketLib.sol"; interface IIrm { - function borrowRate(Market calldata market) external returns (uint256); + function borrowRate(Market calldata market) external view returns (uint256); } diff --git a/src/libraries/BlueLib.sol b/src/libraries/BlueLib.sol new file mode 100644 index 000000000..e8d250bac --- /dev/null +++ b/src/libraries/BlueLib.sol @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; + +import {Id, Market, MarketLib} from "src/libraries/MarketLib.sol"; +import {BlueStorageSlots} from "src/libraries/BlueStorageSlots.sol"; +import {FixedPointMathLib} from "src/libraries/FixedPointMathLib.sol"; + +import {Blue} from "src/Blue.sol"; + +library BlueLib { + using MarketLib for Market; + using FixedPointMathLib for uint256; + + function accruedInterests(Blue blue, Market calldata market) + internal + view + returns ( + uint256 interests, + uint256 totalSupply, + uint256 totalBorrow, + uint256 totalSupplyShares, + uint256 fee, + uint256 feeShares, + uint256 lastUpdate + ) + { + Id id = market.id(); + + bytes32[] memory slots = new bytes32[](5); + slots[0] = BlueStorageSlots.totalSupply(id); + slots[1] = BlueStorageSlots.totalBorrow(id); + slots[2] = BlueStorageSlots.totalSupplyShares(id); + slots[3] = BlueStorageSlots.fee(id); + slots[4] = BlueStorageSlots.lastUpdate(id); + + bytes32[] memory values = blue.extsload(slots); + totalSupply = uint256(values[0]); + totalBorrow = uint256(values[1]); + totalSupplyShares = uint256(values[2]); + fee = uint256(values[3]); + lastUpdate = uint256(values[4]); + + if (totalBorrow != 0) { + uint256 borrowRate = market.irm.borrowRate(market); + interests = totalBorrow.mulWadDown(borrowRate * (block.timestamp - lastUpdate)); + + totalBorrow += interests; + totalSupply += interests; + + if (fee != 0) { + uint256 feeAmount = interests.mulWadDown(fee); + // The fee amount is subtracted from the total supply in this calculation to compensate for the fact that total supply is already updated. + feeShares = feeAmount.mulDivDown(totalSupplyShares, totalSupply - feeAmount); + + totalSupplyShares += feeShares; + } + } + } +} diff --git a/src/libraries/BlueStorageSlots.sol b/src/libraries/BlueStorageSlots.sol new file mode 100644 index 000000000..7881fcecb --- /dev/null +++ b/src/libraries/BlueStorageSlots.sol @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; + +import {Id} from "./MarketLib.sol"; + +library BlueStorageSlots { + uint256 internal constant OWNER_SLOT = 0; + uint256 internal constant FEE_RECIPIENT_SLOT = 1; + uint256 internal constant SUPPLY_SHARE_SLOT = 2; + uint256 internal constant BORROW_SHARE_SLOT = 3; + uint256 internal constant COLLATERAL_SLOT = 4; + uint256 internal constant TOTAL_SUPPLY_SLOT = 5; + uint256 internal constant TOTAL_SUPPLY_SHARES_SLOT = 6; + uint256 internal constant TOTAL_BORROW_SLOT = 7; + uint256 internal constant TOTAL_BORROW_SHARES_SLOT = 8; + uint256 internal constant LAST_UPDATE_SLOT = 9; + uint256 internal constant FEE_SLOT = 10; + uint256 internal constant IS_IRM_ENABLED_SLOT = 11; + uint256 internal constant IS_LLTV_ENABLED_SLOT = 12; + uint256 internal constant IS_APPROVED_SLOT = 13; + + function owner() internal pure returns (bytes32) { + return keccak256(abi.encode(OWNER_SLOT)); + } + + function feeRecipient() internal pure returns (bytes32) { + return keccak256(abi.encode(FEE_RECIPIENT_SLOT)); + } + + function supplyShare(Id id, address user) internal pure returns (bytes32) { + return keccak256(abi.encode(user, abi.encode(id, SUPPLY_SHARE_SLOT))); + } + + function borrowShare(Id id, address user) internal pure returns (bytes32) { + return keccak256(abi.encode(user, abi.encode(id, BORROW_SHARE_SLOT))); + } + + function collateral(Id id, address user) internal pure returns (bytes32) { + return keccak256(abi.encode(user, abi.encode(id, COLLATERAL_SLOT))); + } + + function totalSupply(Id id) internal pure returns (bytes32) { + return keccak256(abi.encode(id, TOTAL_SUPPLY_SLOT)); + } + + function totalSupplyShares(Id id) internal pure returns (bytes32) { + return keccak256(abi.encode(id, TOTAL_SUPPLY_SHARES_SLOT)); + } + + function totalBorrow(Id id) internal pure returns (bytes32) { + return keccak256(abi.encode(id, TOTAL_BORROW_SLOT)); + } + + function totalBorrowShares(Id id) internal pure returns (bytes32) { + return keccak256(abi.encode(id, TOTAL_BORROW_SHARES_SLOT)); + } + + function lastUpdate(Id id) internal pure returns (bytes32) { + return keccak256(abi.encode(id, LAST_UPDATE_SLOT)); + } + + function fee(Id id) internal pure returns (bytes32) { + return keccak256(abi.encode(id, FEE_SLOT)); + } + + function isIrmEnabled(address irm) internal pure returns (bytes32) { + return keccak256(abi.encode(irm, IS_IRM_ENABLED_SLOT)); + } + + function isLltvEnabled(uint256 lltv) internal pure returns (bytes32) { + return keccak256(abi.encode(lltv, IS_LLTV_ENABLED_SLOT)); + } + + function isApproved(address delegator, address manager) internal pure returns (bytes32) { + return keccak256(abi.encode(manager, abi.encode(delegator, IS_APPROVED_SLOT))); + } +} From 8198775affde1383d06c33d1bfe4f604b1d73209 Mon Sep 17 00:00:00 2001 From: Rubilmax Date: Mon, 7 Aug 2023 09:35:43 +0200 Subject: [PATCH 02/24] refactor(accrued-interests): remove overwhelming return variables --- src/libraries/BlueLib.sol | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/src/libraries/BlueLib.sol b/src/libraries/BlueLib.sol index 1a4d21eb7..6cc4f741a 100644 --- a/src/libraries/BlueLib.sol +++ b/src/libraries/BlueLib.sol @@ -43,15 +43,7 @@ library BlueLib { function accruedInterests(IBlue blue, Market memory market) internal view - returns ( - uint256 interests, - uint256 totalSupply, - uint256 totalBorrow, - uint256 totalSupplyShares, - uint256 fee, - uint256 feeShares, - uint256 lastUpdate - ) + returns (uint256 totalSupply, uint256 totalBorrow, uint256 totalSupplyShares) { Id id = market.id(); @@ -66,12 +58,12 @@ library BlueLib { totalSupply = uint256(values[0]); totalBorrow = uint256(values[1]); totalSupplyShares = uint256(values[2]); - fee = uint256(values[3]); - lastUpdate = uint256(values[4]); + uint256 fee = uint256(values[3]); + uint256 lastUpdate = uint256(values[4]); if (totalBorrow != 0) { uint256 borrowRate = IIrm(market.irm).borrowRate(market); - interests = totalBorrow.mulWadDown(borrowRate * (block.timestamp - lastUpdate)); + uint256 interests = totalBorrow.mulWadDown(borrowRate * (block.timestamp - lastUpdate)); totalBorrow += interests; totalSupply += interests; @@ -79,7 +71,7 @@ library BlueLib { if (fee != 0) { uint256 feeAmount = interests.mulWadDown(fee); // The fee amount is subtracted from the total supply in this calculation to compensate for the fact that total supply is already updated. - feeShares = feeAmount.mulDivDown(totalSupplyShares, totalSupply - feeAmount); + uint256 feeShares = feeAmount.mulDivDown(totalSupplyShares, totalSupply - feeAmount); totalSupplyShares += feeShares; } From 5378fc054e179616c354da4929f758c8af92ea9d Mon Sep 17 00:00:00 2001 From: Rubilmax Date: Mon, 7 Aug 2023 10:55:07 +0200 Subject: [PATCH 03/24] fix(storage-slots): fix bad slots --- src/libraries/BlueStorageSlots.sol | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/libraries/BlueStorageSlots.sol b/src/libraries/BlueStorageSlots.sol index 7881fcecb..ab5cde512 100644 --- a/src/libraries/BlueStorageSlots.sol +++ b/src/libraries/BlueStorageSlots.sol @@ -6,8 +6,8 @@ import {Id} from "./MarketLib.sol"; library BlueStorageSlots { uint256 internal constant OWNER_SLOT = 0; uint256 internal constant FEE_RECIPIENT_SLOT = 1; - uint256 internal constant SUPPLY_SHARE_SLOT = 2; - uint256 internal constant BORROW_SHARE_SLOT = 3; + uint256 internal constant SUPPLY_SHARES_SLOT = 2; + uint256 internal constant BORROW_SHARES_SLOT = 3; uint256 internal constant COLLATERAL_SLOT = 4; uint256 internal constant TOTAL_SUPPLY_SLOT = 5; uint256 internal constant TOTAL_SUPPLY_SHARES_SLOT = 6; @@ -17,26 +17,26 @@ library BlueStorageSlots { uint256 internal constant FEE_SLOT = 10; uint256 internal constant IS_IRM_ENABLED_SLOT = 11; uint256 internal constant IS_LLTV_ENABLED_SLOT = 12; - uint256 internal constant IS_APPROVED_SLOT = 13; + uint256 internal constant IS_AUTHORIZED_SLOT = 13; function owner() internal pure returns (bytes32) { - return keccak256(abi.encode(OWNER_SLOT)); + return bytes32(OWNER_SLOT); } function feeRecipient() internal pure returns (bytes32) { - return keccak256(abi.encode(FEE_RECIPIENT_SLOT)); + return bytes32(FEE_RECIPIENT_SLOT); } - function supplyShare(Id id, address user) internal pure returns (bytes32) { - return keccak256(abi.encode(user, abi.encode(id, SUPPLY_SHARE_SLOT))); + function supplyShares(Id id, address user) internal pure returns (bytes32) { + return keccak256(abi.encode(user, keccak256(abi.encode(id, SUPPLY_SHARES_SLOT)))); } - function borrowShare(Id id, address user) internal pure returns (bytes32) { - return keccak256(abi.encode(user, abi.encode(id, BORROW_SHARE_SLOT))); + function borrowShares(Id id, address user) internal pure returns (bytes32) { + return keccak256(abi.encode(user, keccak256(abi.encode(id, BORROW_SHARES_SLOT)))); } function collateral(Id id, address user) internal pure returns (bytes32) { - return keccak256(abi.encode(user, abi.encode(id, COLLATERAL_SLOT))); + return keccak256(abi.encode(user, keccak256(abi.encode(id, COLLATERAL_SLOT)))); } function totalSupply(Id id) internal pure returns (bytes32) { @@ -72,6 +72,6 @@ library BlueStorageSlots { } function isApproved(address delegator, address manager) internal pure returns (bytes32) { - return keccak256(abi.encode(manager, abi.encode(delegator, IS_APPROVED_SLOT))); + return keccak256(abi.encode(manager, keccak256(abi.encode(delegator, IS_AUTHORIZED_SLOT)))); } } From 61ead5068e9b230c89a099b88f142c501ab90172 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Tue, 15 Aug 2023 15:46:46 +0200 Subject: [PATCH 04/24] refactor: rename blue -> morpho --- src/libraries/MorphoLib.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/MorphoLib.sol b/src/libraries/MorphoLib.sol index ca307d97b..0a4f214a3 100644 --- a/src/libraries/MorphoLib.sol +++ b/src/libraries/MorphoLib.sol @@ -12,7 +12,7 @@ library MorphoLib { using MathLib for uint256; using MarketLib for Market; - function accruedInterests(IMorpho blue, Market memory market) + function accruedInterests(IMorpho morpho, Market memory market) internal view returns (uint256 totalSupply, uint256 totalBorrow, uint256 totalSupplyShares) @@ -26,7 +26,7 @@ library MorphoLib { slots[3] = MorphoStorageLib.fee(id); slots[4] = MorphoStorageLib.lastUpdate(id); - bytes32[] memory values = blue.extsload(slots); + bytes32[] memory values = morpho.extsload(slots); totalSupply = uint256(values[0]); totalBorrow = uint256(values[1]); totalSupplyShares = uint256(values[2]); From 61563ccb18a4fcc581391922a4b738005064a321 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Tue, 15 Aug 2023 16:01:18 +0200 Subject: [PATCH 05/24] feat: add updated getters --- src/libraries/MorphoLib.sol | 54 ++++++++++++++++++++++++++++++------- 1 file changed, 44 insertions(+), 10 deletions(-) diff --git a/src/libraries/MorphoLib.sol b/src/libraries/MorphoLib.sol index 0a4f214a3..148b0571c 100644 --- a/src/libraries/MorphoLib.sol +++ b/src/libraries/MorphoLib.sol @@ -6,16 +6,18 @@ import {IIrm} from "../interfaces/IIrm.sol"; import {MathLib} from "./MathLib.sol"; import {MarketLib} from "./MarketLib.sol"; +import {SharesMathLib} from "./SharesMathLib.sol"; import {MorphoStorageLib} from "./MorphoStorageLib.sol"; library MorphoLib { using MathLib for uint256; using MarketLib for Market; + using SharesMathLib for uint256; function accruedInterests(IMorpho morpho, Market memory market) internal view - returns (uint256 totalSupply, uint256 totalBorrow, uint256 totalSupplyShares) + returns (uint256 totSupply, uint256 totBorrow, uint256 totSupplyShares) { Id id = market.id(); @@ -27,26 +29,58 @@ library MorphoLib { slots[4] = MorphoStorageLib.lastUpdate(id); bytes32[] memory values = morpho.extsload(slots); - totalSupply = uint256(values[0]); - totalBorrow = uint256(values[1]); - totalSupplyShares = uint256(values[2]); + totSupply = uint256(values[0]); + totBorrow = uint256(values[1]); + totSupplyShares = uint256(values[2]); uint256 fee = uint256(values[3]); uint256 lastUpdate = uint256(values[4]); - if (totalBorrow != 0) { + if (totBorrow != 0) { uint256 borrowRate = IIrm(market.irm).borrowRate(market); - uint256 interests = totalBorrow.wMulDown(borrowRate * (block.timestamp - lastUpdate)); + uint256 interests = totBorrow.wMulDown(borrowRate * (block.timestamp - lastUpdate)); - totalBorrow += interests; - totalSupply += interests; + totBorrow += interests; + totSupply += interests; if (fee != 0) { uint256 feeAmount = interests.wMulDown(fee); // The fee amount is subtracted from the total supply in this calculation to compensate for the fact that total supply is already updated. - uint256 feeShares = feeAmount.mulDivDown(totalSupplyShares, totalSupply - feeAmount); + uint256 feeShares = feeAmount.mulDivDown(totSupplyShares, totSupply - feeAmount); - totalSupplyShares += feeShares; + totSupplyShares += feeShares; } } } + + function totalSupply(IMorpho morpho, Market memory market) internal view returns (uint256) { + (uint256 totSupply,,) = accruedInterests(morpho, market); + return totSupply; + } + + function totalBorrow(IMorpho morpho, Market memory market) internal view returns (uint256) { + (, uint256 totBorrow,) = accruedInterests(morpho, market); + return totBorrow; + } + + function totalSupplyShares(IMorpho morpho, Market memory market) internal view returns (uint256) { + (,, uint256 totSupplyShares) = accruedInterests(morpho, market); + return totSupplyShares; + } + + function supplyBalance(IMorpho morpho, Market memory market, address user) internal view returns (uint256) { + Id id = market.id(); + uint256 shares = morpho.supplyShares(id, user); + (uint256 totSupply,, uint256 totSupplyShares) = accruedInterests(morpho, market); + + return shares.toAssetsDown(totSupply, totSupplyShares); + } + + function borrowBalance(IMorpho morpho, Market memory market, address user) internal view returns (uint256) { + Id id = market.id(); + uint256 shares = morpho.borrowShares(id, user); + uint256 totBorrowShares = morpho.totalBorrowShares(id); + (, uint256 totBorrow,) = accruedInterests(morpho, market); + + return shares.toAssetsUp(totBorrow, totBorrowShares); + } } From 1979afad4425b9ef08a5ffd7fa1488354c683b24 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Tue, 15 Aug 2023 16:25:51 +0200 Subject: [PATCH 06/24] refactor: user view and fix computation --- src/interfaces/IIrm.sol | 3 +++ src/libraries/MorphoLib.sol | 13 +++++++++---- src/mocks/IrmMock.sol | 4 ++++ 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/interfaces/IIrm.sol b/src/interfaces/IIrm.sol index d52ebf161..a76eaae37 100644 --- a/src/interfaces/IIrm.sol +++ b/src/interfaces/IIrm.sol @@ -10,4 +10,7 @@ import {Market} from "./IMorpho.sol"; interface IIrm { /// @notice Returns the borrow rate of a `market`. function borrowRate(Market memory market) external view returns (uint256); + + /// @notice Returns the borrow rate of a `market` without modifying the IRM's storage. + function borrowRateView(Market memory market) external view returns (uint256); } diff --git a/src/libraries/MorphoLib.sol b/src/libraries/MorphoLib.sol index 0a4f214a3..4ed54d3c4 100644 --- a/src/libraries/MorphoLib.sol +++ b/src/libraries/MorphoLib.sol @@ -6,11 +6,13 @@ import {IIrm} from "../interfaces/IIrm.sol"; import {MathLib} from "./MathLib.sol"; import {MarketLib} from "./MarketLib.sol"; +import {SharesMathLib} from "./SharesMathLib.sol"; import {MorphoStorageLib} from "./MorphoStorageLib.sol"; library MorphoLib { using MathLib for uint256; using MarketLib for Market; + using SharesMathLib for uint256; function accruedInterests(IMorpho morpho, Market memory market) internal @@ -33,17 +35,20 @@ library MorphoLib { uint256 fee = uint256(values[3]); uint256 lastUpdate = uint256(values[4]); - if (totalBorrow != 0) { - uint256 borrowRate = IIrm(market.irm).borrowRate(market); - uint256 interests = totalBorrow.wMulDown(borrowRate * (block.timestamp - lastUpdate)); + uint256 elapsed = block.timestamp - lastUpdate; + + if (elapsed == 0) return (totalSupply, totalBorrow, totalSupplyShares); + if (totalBorrow != 0) { + uint256 borrowRate = IIrm(market.irm).borrowRateView(market); + uint256 interests = totalBorrow.wMulDown(borrowRate.wTaylorCompounded(elapsed)); totalBorrow += interests; totalSupply += interests; if (fee != 0) { uint256 feeAmount = interests.wMulDown(fee); // The fee amount is subtracted from the total supply in this calculation to compensate for the fact that total supply is already updated. - uint256 feeShares = feeAmount.mulDivDown(totalSupplyShares, totalSupply - feeAmount); + uint256 feeShares = feeAmount.toSharesDown(totalSupply - feeAmount, totalSupplyShares); totalSupplyShares += feeShares; } diff --git a/src/mocks/IrmMock.sol b/src/mocks/IrmMock.sol index ca98bae2e..963f09578 100644 --- a/src/mocks/IrmMock.sol +++ b/src/mocks/IrmMock.sol @@ -18,6 +18,10 @@ contract IrmMock is IIrm { } function borrowRate(Market memory market) external view returns (uint256) { + return borrowRateView(market); + } + + function borrowRateView(Market memory market) public view returns (uint256) { Id id = market.id(); uint256 utilization = MORPHO.totalBorrow(id).wDivDown(MORPHO.totalSupply(id)); From 84b8889deb13990a1e455ac48950f608a9016870 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Tue, 15 Aug 2023 18:44:08 +0200 Subject: [PATCH 07/24] refactor: rename variables --- src/libraries/MorphoLib.sol | 45 +++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/src/libraries/MorphoLib.sol b/src/libraries/MorphoLib.sol index 498d787d7..7161c3a2d 100644 --- a/src/libraries/MorphoLib.sol +++ b/src/libraries/MorphoLib.sol @@ -17,7 +17,7 @@ library MorphoLib { function accruedInterests(IMorpho morpho, Market memory market) internal view - returns (uint256 totSupply, uint256 totBorrow, uint256 totSupplyShares) + returns (uint256 supply, uint256 borrow, uint256 supplyShares) { Id id = market.id(); @@ -29,61 +29,58 @@ library MorphoLib { slots[4] = MorphoStorageLib.lastUpdate(id); bytes32[] memory values = morpho.extsload(slots); - totSupply = uint256(values[0]); - totBorrow = uint256(values[1]); - totSupplyShares = uint256(values[2]); + supply = uint256(values[0]); + borrow = uint256(values[1]); + supplyShares = uint256(values[2]); uint256 fee = uint256(values[3]); uint256 lastUpdate = uint256(values[4]); uint256 elapsed = block.timestamp - lastUpdate; - if (elapsed == 0) return (totSupply, totBorrow, totSupplyShares); + if (elapsed == 0) return (supply, borrow, supplyShares); - if (totBorrow != 0) { + if (borrow != 0) { uint256 borrowRate = IIrm(market.irm).borrowRateView(market); - uint256 interests = totBorrow.wMulDown(borrowRate.wTaylorCompounded(elapsed)); - totBorrow += interests; - totSupply += interests; + uint256 interests = borrow.wMulDown(borrowRate.wTaylorCompounded(elapsed)); + borrow += interests; + supply += interests; if (fee != 0) { uint256 feeAmount = interests.wMulDown(fee); // The fee amount is subtracted from the total supply in this calculation to compensate for the fact that total supply is already updated. - uint256 feeShares = feeAmount.toSharesDown(totSupply - feeAmount, totSupplyShares); + uint256 feeShares = feeAmount.toSharesDown(supply - feeAmount, supplyShares); - totSupplyShares += feeShares; + supplyShares += feeShares; } } } - function totalSupply(IMorpho morpho, Market memory market) internal view returns (uint256) { - (uint256 totSupply,,) = accruedInterests(morpho, market); - return totSupply; + function totalSupply(IMorpho morpho, Market memory market) internal view returns (uint256 supply) { + (supply,,) = accruedInterests(morpho, market); } - function totalBorrow(IMorpho morpho, Market memory market) internal view returns (uint256) { - (, uint256 totBorrow,) = accruedInterests(morpho, market); - return totBorrow; + function totalBorrow(IMorpho morpho, Market memory market) internal view returns (uint256 borrow) { + (, borrow,) = accruedInterests(morpho, market); } - function totalSupplyShares(IMorpho morpho, Market memory market) internal view returns (uint256) { - (,, uint256 totSupplyShares) = accruedInterests(morpho, market); - return totSupplyShares; + function totalSupplyShares(IMorpho morpho, Market memory market) internal view returns (uint256 supplyShares) { + (,, supplyShares) = accruedInterests(morpho, market); } function supplyBalance(IMorpho morpho, Market memory market, address user) internal view returns (uint256) { Id id = market.id(); uint256 shares = morpho.supplyShares(id, user); - (uint256 totSupply,, uint256 totSupplyShares) = accruedInterests(morpho, market); + (uint256 supply,, uint256 supplyShares) = accruedInterests(morpho, market); - return shares.toAssetsDown(totSupply, totSupplyShares); + return shares.toAssetsDown(supply, supplyShares); } function borrowBalance(IMorpho morpho, Market memory market, address user) internal view returns (uint256) { Id id = market.id(); uint256 shares = morpho.borrowShares(id, user); uint256 totBorrowShares = morpho.totalBorrowShares(id); - (, uint256 totBorrow,) = accruedInterests(morpho, market); + (, uint256 borrow,) = accruedInterests(morpho, market); - return shares.toAssetsUp(totBorrow, totBorrowShares); + return shares.toAssetsUp(borrow, totBorrowShares); } } From 516b72825a75b76565b31c145a44cf87de0bc15c Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Wed, 16 Aug 2023 10:02:57 +0200 Subject: [PATCH 08/24] chore: move files in a periphery folder --- src/libraries/{ => periphery}/MorphoLib.sol | 10 +++++----- src/libraries/{ => periphery}/MorphoStorageLib.sol | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) rename src/libraries/{ => periphery}/MorphoLib.sol (92%) rename src/libraries/{ => periphery}/MorphoStorageLib.sol (98%) diff --git a/src/libraries/MorphoLib.sol b/src/libraries/periphery/MorphoLib.sol similarity index 92% rename from src/libraries/MorphoLib.sol rename to src/libraries/periphery/MorphoLib.sol index 7161c3a2d..345bd01dd 100644 --- a/src/libraries/MorphoLib.sol +++ b/src/libraries/periphery/MorphoLib.sol @@ -1,12 +1,12 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0; -import {Id, Market, IMorpho} from "../interfaces/IMorpho.sol"; -import {IIrm} from "../interfaces/IIrm.sol"; +import {Id, Market, IMorpho} from "../../interfaces/IMorpho.sol"; +import {IIrm} from "../../interfaces/IIrm.sol"; -import {MathLib} from "./MathLib.sol"; -import {MarketLib} from "./MarketLib.sol"; -import {SharesMathLib} from "./SharesMathLib.sol"; +import {MathLib} from "../MathLib.sol"; +import {MarketLib} from "../MarketLib.sol"; +import {SharesMathLib} from "../SharesMathLib.sol"; import {MorphoStorageLib} from "./MorphoStorageLib.sol"; library MorphoLib { diff --git a/src/libraries/MorphoStorageLib.sol b/src/libraries/periphery/MorphoStorageLib.sol similarity index 98% rename from src/libraries/MorphoStorageLib.sol rename to src/libraries/periphery/MorphoStorageLib.sol index bcfc42625..df6f9c43a 100644 --- a/src/libraries/MorphoStorageLib.sol +++ b/src/libraries/periphery/MorphoStorageLib.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0; -import {Id} from "./MarketLib.sol"; +import {Id} from "../MarketLib.sol"; library MorphoStorageLib { uint256 internal constant OWNER_SLOT = 0; From a1ab09c1cfe6d6284695a087bf3cb41da3d3f412 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Wed, 16 Aug 2023 12:13:21 +0200 Subject: [PATCH 09/24] refactor: renaming proposal --- src/libraries/periphery/MorphoLib.sol | 64 ++++++++++++++++----------- 1 file changed, 38 insertions(+), 26 deletions(-) diff --git a/src/libraries/periphery/MorphoLib.sol b/src/libraries/periphery/MorphoLib.sol index 345bd01dd..5b36e98ed 100644 --- a/src/libraries/periphery/MorphoLib.sol +++ b/src/libraries/periphery/MorphoLib.sol @@ -17,7 +17,7 @@ library MorphoLib { function accruedInterests(IMorpho morpho, Market memory market) internal view - returns (uint256 supply, uint256 borrow, uint256 supplyShares) + returns (uint256 marketTotalSupply, uint256 marketToralBorrow, uint256 marketTotalSupplyShares) { Id id = market.id(); @@ -29,58 +29,70 @@ library MorphoLib { slots[4] = MorphoStorageLib.lastUpdate(id); bytes32[] memory values = morpho.extsload(slots); - supply = uint256(values[0]); - borrow = uint256(values[1]); - supplyShares = uint256(values[2]); + marketTotalSupply = uint256(values[0]); + marketToralBorrow = uint256(values[1]); + marketTotalSupplyShares = uint256(values[2]); uint256 fee = uint256(values[3]); uint256 lastUpdate = uint256(values[4]); uint256 elapsed = block.timestamp - lastUpdate; - if (elapsed == 0) return (supply, borrow, supplyShares); + if (elapsed == 0) return (marketTotalSupply, marketToralBorrow, marketTotalSupplyShares); - if (borrow != 0) { + if (marketToralBorrow != 0) { uint256 borrowRate = IIrm(market.irm).borrowRateView(market); - uint256 interests = borrow.wMulDown(borrowRate.wTaylorCompounded(elapsed)); - borrow += interests; - supply += interests; + uint256 interests = marketToralBorrow.wMulDown(borrowRate.wTaylorCompounded(elapsed)); + marketToralBorrow += interests; + marketTotalSupply += interests; if (fee != 0) { uint256 feeAmount = interests.wMulDown(fee); // The fee amount is subtracted from the total supply in this calculation to compensate for the fact that total supply is already updated. - uint256 feeShares = feeAmount.toSharesDown(supply - feeAmount, supplyShares); + uint256 feeShares = feeAmount.toSharesDown(marketTotalSupply - feeAmount, marketTotalSupplyShares); - supplyShares += feeShares; + marketTotalSupplyShares += feeShares; } } } - function totalSupply(IMorpho morpho, Market memory market) internal view returns (uint256 supply) { - (supply,,) = accruedInterests(morpho, market); + function expectedTotalSupply(IMorpho morpho, Market memory market) internal view returns (uint256 totalSupply) { + (totalSupply,,) = accruedInterests(morpho, market); } - function totalBorrow(IMorpho morpho, Market memory market) internal view returns (uint256 borrow) { - (, borrow,) = accruedInterests(morpho, market); + function expectedTotalBorrow(IMorpho morpho, Market memory market) internal view returns (uint256 totalBorrow) { + (, totalBorrow,) = accruedInterests(morpho, market); } - function totalSupplyShares(IMorpho morpho, Market memory market) internal view returns (uint256 supplyShares) { - (,, supplyShares) = accruedInterests(morpho, market); + function expectedTotalSupplyShares(IMorpho morpho, Market memory market) + internal + view + returns (uint256 totalSupplyShares) + { + (,, totalSupplyShares) = accruedInterests(morpho, market); } - function supplyBalance(IMorpho morpho, Market memory market, address user) internal view returns (uint256) { + function expectedSupplyBalance(IMorpho morpho, Market memory market, address user) + internal + view + returns (uint256) + { Id id = market.id(); - uint256 shares = morpho.supplyShares(id, user); - (uint256 supply,, uint256 supplyShares) = accruedInterests(morpho, market); + uint256 userSupplyShares = morpho.supplyShares(id, user); + (uint256 marketTotalSupply,, uint256 marketTotalSupplyShares) = accruedInterests(morpho, market); - return shares.toAssetsDown(supply, supplyShares); + return userSupplyShares.toAssetsDown(marketTotalSupply, marketTotalSupplyShares); } - function borrowBalance(IMorpho morpho, Market memory market, address user) internal view returns (uint256) { + function expectedBorrowBalance(IMorpho morpho, Market memory market, address user) + internal + view + returns (uint256) + { Id id = market.id(); - uint256 shares = morpho.borrowShares(id, user); - uint256 totBorrowShares = morpho.totalBorrowShares(id); - (, uint256 borrow,) = accruedInterests(morpho, market); + uint256 userBorrowShares = morpho.borrowShares(id, user); + uint256 marketTotalBorrowShares = morpho.totalBorrowShares(id); + (, uint256 marketTotalBorrow,) = accruedInterests(morpho, market); - return shares.toAssetsUp(borrow, totBorrowShares); + return userBorrowShares.toAssetsUp(marketTotalBorrow, marketTotalBorrowShares); } } From 7b480c0b0e70c49bf2b7b4b5a1dcf3eaaf49369d Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Wed, 16 Aug 2023 12:23:26 +0200 Subject: [PATCH 10/24] fix: correct naming for is auhtorized --- src/libraries/periphery/MorphoStorageLib.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/periphery/MorphoStorageLib.sol b/src/libraries/periphery/MorphoStorageLib.sol index df6f9c43a..d451a6687 100644 --- a/src/libraries/periphery/MorphoStorageLib.sol +++ b/src/libraries/periphery/MorphoStorageLib.sol @@ -71,7 +71,7 @@ library MorphoStorageLib { return keccak256(abi.encode(lltv, IS_LLTV_ENABLED_SLOT)); } - function isApproved(address delegator, address manager) internal pure returns (bytes32) { + function isAuthorized(address delegator, address manager) internal pure returns (bytes32) { return keccak256(abi.encode(manager, keccak256(abi.encode(delegator, IS_AUTHORIZED_SLOT)))); } } From 0a5bd58f1a6faa78c88282f79ef193101ab9e15f Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Wed, 16 Aug 2023 12:24:24 +0200 Subject: [PATCH 11/24] docs: add comments --- src/libraries/periphery/MorphoLib.sol | 7 +++++++ src/libraries/periphery/MorphoStorageLib.sol | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/src/libraries/periphery/MorphoLib.sol b/src/libraries/periphery/MorphoLib.sol index 5b36e98ed..93dc067a3 100644 --- a/src/libraries/periphery/MorphoLib.sol +++ b/src/libraries/periphery/MorphoLib.sol @@ -9,6 +9,13 @@ import {MarketLib} from "../MarketLib.sol"; import {SharesMathLib} from "../SharesMathLib.sol"; import {MorphoStorageLib} from "./MorphoStorageLib.sol"; +/// @title MorphoLib +/// @author Morpho Labs +/// @custom:contact security@morpho.xyz +/// @notice Helper library exposing getters with the expected value after interests accrual. +/// @dev This library is not used in Morpho itself and is intended to be used by integrators. +/// @dev The getter to retrieve the total borrow shares is not exposed because interests accrual does not apply to it. +/// The value can be queried directly on Morpho using `totalBorrowShares`. library MorphoLib { using MathLib for uint256; using MarketLib for Market; diff --git a/src/libraries/periphery/MorphoStorageLib.sol b/src/libraries/periphery/MorphoStorageLib.sol index d451a6687..ba32d4763 100644 --- a/src/libraries/periphery/MorphoStorageLib.sol +++ b/src/libraries/periphery/MorphoStorageLib.sol @@ -3,6 +3,11 @@ pragma solidity ^0.8.0; import {Id} from "../MarketLib.sol"; +/// @title MorphoStorageLib +/// @author Morpho Labs +/// @custom:contact security@morpho.xyz +/// @notice Helper library exposing getters to access Morpho storage variables' slot. +/// @dev This library is not used in Morpho itself and is intended to be used by integrators. library MorphoStorageLib { uint256 internal constant OWNER_SLOT = 0; uint256 internal constant FEE_RECIPIENT_SLOT = 1; From 38351ab3f4a596c96e876454f5dfd403f9551078 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Wed, 16 Aug 2023 12:26:00 +0200 Subject: [PATCH 12/24] fix: iirm interface --- src/interfaces/IIrm.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interfaces/IIrm.sol b/src/interfaces/IIrm.sol index a76eaae37..0b95fbeaf 100644 --- a/src/interfaces/IIrm.sol +++ b/src/interfaces/IIrm.sol @@ -9,7 +9,7 @@ import {Market} from "./IMorpho.sol"; /// @notice Interface that IRMs used by Morpho must implement. interface IIrm { /// @notice Returns the borrow rate of a `market`. - function borrowRate(Market memory market) external view returns (uint256); + function borrowRate(Market memory market) external returns (uint256); /// @notice Returns the borrow rate of a `market` without modifying the IRM's storage. function borrowRateView(Market memory market) external view returns (uint256); From 53bb41fc760262f5fde69a348f2ac31d39990efe Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Wed, 16 Aug 2023 12:31:38 +0200 Subject: [PATCH 13/24] refactor: harmonize morpho and lib --- src/Morpho.sol | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Morpho.sol b/src/Morpho.sol index d1118f53d..a546e0ad2 100644 --- a/src/Morpho.sol +++ b/src/Morpho.sol @@ -442,20 +442,20 @@ contract Morpho is IMorpho { if (marketTotalBorrow != 0) { uint256 borrowRate = IIrm(market.irm).borrowRate(market); - uint256 accruedInterests = marketTotalBorrow.wMulDown(borrowRate.wTaylorCompounded(elapsed)); - totalBorrow[id] = marketTotalBorrow + accruedInterests; - totalSupply[id] += accruedInterests; + uint256 interests = marketTotalBorrow.wMulDown(borrowRate.wTaylorCompounded(elapsed)); + totalBorrow[id] = marketTotalBorrow + interests; + totalSupply[id] += interests; uint256 feeShares; if (fee[id] != 0) { - uint256 feeAmount = accruedInterests.wMulDown(fee[id]); + uint256 feeAmount = interests.wMulDown(fee[id]); // The fee amount is subtracted from the total supply in this calculation to compensate for the fact that total supply is already updated. feeShares = feeAmount.toSharesDown(totalSupply[id] - feeAmount, totalSupplyShares[id]); supplyShares[id][feeRecipient] += feeShares; totalSupplyShares[id] += feeShares; } - emit EventsLib.AccrueInterests(id, borrowRate, accruedInterests, feeShares); + emit EventsLib.AccrueInterests(id, borrowRate, interests, feeShares); } lastUpdate[id] = block.timestamp; From 2736736d52b0b97f65c694d4e352e5be873c3ba9 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Wed, 16 Aug 2023 12:39:21 +0200 Subject: [PATCH 14/24] refactor: add slot to getters --- src/libraries/periphery/MorphoLib.sol | 32 ++++++++++---------- src/libraries/periphery/MorphoStorageLib.sol | 28 ++++++++--------- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/libraries/periphery/MorphoLib.sol b/src/libraries/periphery/MorphoLib.sol index 93dc067a3..8e5615da4 100644 --- a/src/libraries/periphery/MorphoLib.sol +++ b/src/libraries/periphery/MorphoLib.sol @@ -24,40 +24,40 @@ library MorphoLib { function accruedInterests(IMorpho morpho, Market memory market) internal view - returns (uint256 marketTotalSupply, uint256 marketToralBorrow, uint256 marketTotalSupplyShares) + returns (uint256 totalSupply, uint256 toralBorrow, uint256 totalSupplyShares) { Id id = market.id(); bytes32[] memory slots = new bytes32[](5); - slots[0] = MorphoStorageLib.totalSupply(id); - slots[1] = MorphoStorageLib.totalBorrow(id); - slots[2] = MorphoStorageLib.totalSupplyShares(id); - slots[3] = MorphoStorageLib.fee(id); - slots[4] = MorphoStorageLib.lastUpdate(id); + slots[0] = MorphoStorageLib.totalSupplySlot(id); + slots[1] = MorphoStorageLib.totalBorrowSlot(id); + slots[2] = MorphoStorageLib.totalSupplySharesSlot(id); + slots[3] = MorphoStorageLib.feeSlot(id); + slots[4] = MorphoStorageLib.lastUpdateSlot(id); bytes32[] memory values = morpho.extsload(slots); - marketTotalSupply = uint256(values[0]); - marketToralBorrow = uint256(values[1]); - marketTotalSupplyShares = uint256(values[2]); + totalSupply = uint256(values[0]); + toralBorrow = uint256(values[1]); + totalSupplyShares = uint256(values[2]); uint256 fee = uint256(values[3]); uint256 lastUpdate = uint256(values[4]); uint256 elapsed = block.timestamp - lastUpdate; - if (elapsed == 0) return (marketTotalSupply, marketToralBorrow, marketTotalSupplyShares); + if (elapsed == 0) return (totalSupply, toralBorrow, totalSupplyShares); - if (marketToralBorrow != 0) { + if (toralBorrow != 0) { uint256 borrowRate = IIrm(market.irm).borrowRateView(market); - uint256 interests = marketToralBorrow.wMulDown(borrowRate.wTaylorCompounded(elapsed)); - marketToralBorrow += interests; - marketTotalSupply += interests; + uint256 interests = toralBorrow.wMulDown(borrowRate.wTaylorCompounded(elapsed)); + toralBorrow += interests; + totalSupply += interests; if (fee != 0) { uint256 feeAmount = interests.wMulDown(fee); // The fee amount is subtracted from the total supply in this calculation to compensate for the fact that total supply is already updated. - uint256 feeShares = feeAmount.toSharesDown(marketTotalSupply - feeAmount, marketTotalSupplyShares); + uint256 feeShares = feeAmount.toSharesDown(totalSupply - feeAmount, totalSupplyShares); - marketTotalSupplyShares += feeShares; + totalSupplyShares += feeShares; } } } diff --git a/src/libraries/periphery/MorphoStorageLib.sol b/src/libraries/periphery/MorphoStorageLib.sol index ba32d4763..f3257fd91 100644 --- a/src/libraries/periphery/MorphoStorageLib.sol +++ b/src/libraries/periphery/MorphoStorageLib.sol @@ -24,59 +24,59 @@ library MorphoStorageLib { uint256 internal constant IS_LLTV_ENABLED_SLOT = 12; uint256 internal constant IS_AUTHORIZED_SLOT = 13; - function owner() internal pure returns (bytes32) { + function ownerSlot() internal pure returns (bytes32) { return bytes32(OWNER_SLOT); } - function feeRecipient() internal pure returns (bytes32) { + function feeRecipientSlot() internal pure returns (bytes32) { return bytes32(FEE_RECIPIENT_SLOT); } - function supplyShares(Id id, address user) internal pure returns (bytes32) { + function supplySharesSlot(Id id, address user) internal pure returns (bytes32) { return keccak256(abi.encode(user, keccak256(abi.encode(id, SUPPLY_SHARES_SLOT)))); } - function borrowShares(Id id, address user) internal pure returns (bytes32) { + function borrowSharesSlot(Id id, address user) internal pure returns (bytes32) { return keccak256(abi.encode(user, keccak256(abi.encode(id, BORROW_SHARES_SLOT)))); } - function collateral(Id id, address user) internal pure returns (bytes32) { + function collateralSlot(Id id, address user) internal pure returns (bytes32) { return keccak256(abi.encode(user, keccak256(abi.encode(id, COLLATERAL_SLOT)))); } - function totalSupply(Id id) internal pure returns (bytes32) { + function totalSupplySlot(Id id) internal pure returns (bytes32) { return keccak256(abi.encode(id, TOTAL_SUPPLY_SLOT)); } - function totalSupplyShares(Id id) internal pure returns (bytes32) { + function totalSupplySharesSlot(Id id) internal pure returns (bytes32) { return keccak256(abi.encode(id, TOTAL_SUPPLY_SHARES_SLOT)); } - function totalBorrow(Id id) internal pure returns (bytes32) { + function totalBorrowSlot(Id id) internal pure returns (bytes32) { return keccak256(abi.encode(id, TOTAL_BORROW_SLOT)); } - function totalBorrowShares(Id id) internal pure returns (bytes32) { + function totalBorrowSharesSlot(Id id) internal pure returns (bytes32) { return keccak256(abi.encode(id, TOTAL_BORROW_SHARES_SLOT)); } - function lastUpdate(Id id) internal pure returns (bytes32) { + function lastUpdateSlot(Id id) internal pure returns (bytes32) { return keccak256(abi.encode(id, LAST_UPDATE_SLOT)); } - function fee(Id id) internal pure returns (bytes32) { + function feeSlot(Id id) internal pure returns (bytes32) { return keccak256(abi.encode(id, FEE_SLOT)); } - function isIrmEnabled(address irm) internal pure returns (bytes32) { + function isIrmEnabledSlot(address irm) internal pure returns (bytes32) { return keccak256(abi.encode(irm, IS_IRM_ENABLED_SLOT)); } - function isLltvEnabled(uint256 lltv) internal pure returns (bytes32) { + function isLltvEnabledSlot(uint256 lltv) internal pure returns (bytes32) { return keccak256(abi.encode(lltv, IS_LLTV_ENABLED_SLOT)); } - function isAuthorized(address delegator, address manager) internal pure returns (bytes32) { + function isAuthorizedSlot(address delegator, address manager) internal pure returns (bytes32) { return keccak256(abi.encode(manager, keccak256(abi.encode(delegator, IS_AUTHORIZED_SLOT)))); } } From 8a3eec8e3febd7d5cbbba4d7e1b451a2f6b662f8 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Wed, 16 Aug 2023 16:01:15 +0200 Subject: [PATCH 15/24] refactor: accrued interests -> interests in events --- src/libraries/EventsLib.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/EventsLib.sol b/src/libraries/EventsLib.sol index 5705c1347..5be30fc00 100644 --- a/src/libraries/EventsLib.sol +++ b/src/libraries/EventsLib.sol @@ -141,7 +141,7 @@ library EventsLib { /// @notice Emitted when accruing interests. /// @param id The market id. /// @param prevBorrowRate The previous borrow rate. - /// @param accruedInterests The amount of interests accrued. + /// @param interests The amount of interests accrued. /// @param feeShares The amount of shares minted as fee. - event AccrueInterests(Id indexed id, uint256 prevBorrowRate, uint256 accruedInterests, uint256 feeShares); + event AccrueInterests(Id indexed id, uint256 prevBorrowRate, uint256 interests, uint256 feeShares); } From 56329a3de1f7e7685d03b4a788adc49b35a87c8a Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Wed, 16 Aug 2023 21:18:03 +0200 Subject: [PATCH 16/24] refactor: renaming --- src/libraries/periphery/MorphoLib.sol | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/libraries/periphery/MorphoLib.sol b/src/libraries/periphery/MorphoLib.sol index 8e5615da4..16719aea8 100644 --- a/src/libraries/periphery/MorphoLib.sol +++ b/src/libraries/periphery/MorphoLib.sol @@ -21,7 +21,7 @@ library MorphoLib { using MarketLib for Market; using SharesMathLib for uint256; - function accruedInterests(IMorpho morpho, Market memory market) + function virtualAccrueInterest(IMorpho morpho, Market memory market) internal view returns (uint256 totalSupply, uint256 toralBorrow, uint256 totalSupplyShares) @@ -63,11 +63,11 @@ library MorphoLib { } function expectedTotalSupply(IMorpho morpho, Market memory market) internal view returns (uint256 totalSupply) { - (totalSupply,,) = accruedInterests(morpho, market); + (totalSupply,,) = virtualAccrueInterest(morpho, market); } function expectedTotalBorrow(IMorpho morpho, Market memory market) internal view returns (uint256 totalBorrow) { - (, totalBorrow,) = accruedInterests(morpho, market); + (, totalBorrow,) = virtualAccrueInterest(morpho, market); } function expectedTotalSupplyShares(IMorpho morpho, Market memory market) @@ -75,7 +75,7 @@ library MorphoLib { view returns (uint256 totalSupplyShares) { - (,, totalSupplyShares) = accruedInterests(morpho, market); + (,, totalSupplyShares) = virtualAccrueInterest(morpho, market); } function expectedSupplyBalance(IMorpho morpho, Market memory market, address user) @@ -84,10 +84,10 @@ library MorphoLib { returns (uint256) { Id id = market.id(); - uint256 userSupplyShares = morpho.supplyShares(id, user); - (uint256 marketTotalSupply,, uint256 marketTotalSupplyShares) = accruedInterests(morpho, market); + uint256 supplyShares = morpho.supplyShares(id, user); + (uint256 totalSupply,, uint256 totalSupplyShares) = virtualAccrueInterest(morpho, market); - return userSupplyShares.toAssetsDown(marketTotalSupply, marketTotalSupplyShares); + return supplyShares.toAssetsDown(totalSupply, totalSupplyShares); } function expectedBorrowBalance(IMorpho morpho, Market memory market, address user) @@ -96,10 +96,10 @@ library MorphoLib { returns (uint256) { Id id = market.id(); - uint256 userBorrowShares = morpho.borrowShares(id, user); - uint256 marketTotalBorrowShares = morpho.totalBorrowShares(id); - (, uint256 marketTotalBorrow,) = accruedInterests(morpho, market); + uint256 borrowShares = morpho.borrowShares(id, user); + uint256 totalBorrowShares = morpho.totalBorrowShares(id); + (, uint256 totalBorrow,) = virtualAccrueInterest(morpho, market); - return userBorrowShares.toAssetsUp(marketTotalBorrow, marketTotalBorrowShares); + return borrowShares.toAssetsUp(totalBorrow, totalBorrowShares); } } From 8a5eb4e94e39265e1061790dd1f5de90dd312066 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Wed, 16 Aug 2023 21:20:28 +0200 Subject: [PATCH 17/24] refactor: interests -> interest --- src/Morpho.sol | 36 ++++++++--------- src/interfaces/IMorpho.sol | 6 +-- src/libraries/EventsLib.sol | 6 +-- src/libraries/periphery/MorphoLib.sol | 12 +++--- ...ol => TestIntegrationAccrueInterest.t.sol} | 40 +++++++++---------- test/morpho_tests.tree | 32 +++++++-------- 6 files changed, 66 insertions(+), 66 deletions(-) rename test/forge/integration/{TestIntegrationAccrueInterests.t.sol => TestIntegrationAccrueInterest.t.sol} (85%) diff --git a/src/Morpho.sol b/src/Morpho.sol index a546e0ad2..4807ce5d7 100644 --- a/src/Morpho.sol +++ b/src/Morpho.sol @@ -136,8 +136,8 @@ contract Morpho is IMorpho { require(lastUpdate[id] != 0, ErrorsLib.MARKET_NOT_CREATED); require(newFee <= MAX_FEE, ErrorsLib.MAX_FEE_EXCEEDED); - // Accrue interests using the previous fee set before changing it. - _accrueInterests(market, id); + // Accrue interest using the previous fee set before changing it. + _accrueInterest(market, id); fee[id] = newFee; @@ -178,7 +178,7 @@ contract Morpho is IMorpho { require(UtilsLib.exactlyOneZero(assets, shares), ErrorsLib.INCONSISTENT_INPUT); require(onBehalf != address(0), ErrorsLib.ZERO_ADDRESS); - _accrueInterests(market, id); + _accrueInterest(market, id); if (assets > 0) shares = assets.toSharesDown(totalSupply[id], totalSupplyShares[id]); else assets = shares.toAssetsUp(totalSupply[id], totalSupplyShares[id]); @@ -208,7 +208,7 @@ contract Morpho is IMorpho { require(receiver != address(0), ErrorsLib.ZERO_ADDRESS); require(_isSenderAuthorized(onBehalf), ErrorsLib.UNAUTHORIZED); - _accrueInterests(market, id); + _accrueInterest(market, id); if (assets > 0) shares = assets.toSharesUp(totalSupply[id], totalSupplyShares[id]); else assets = shares.toAssetsDown(totalSupply[id], totalSupplyShares[id]); @@ -240,7 +240,7 @@ contract Morpho is IMorpho { require(receiver != address(0), ErrorsLib.ZERO_ADDRESS); require(_isSenderAuthorized(onBehalf), ErrorsLib.UNAUTHORIZED); - _accrueInterests(market, id); + _accrueInterest(market, id); if (assets > 0) shares = assets.toSharesUp(totalBorrow[id], totalBorrowShares[id]); else assets = shares.toAssetsDown(totalBorrow[id], totalBorrowShares[id]); @@ -269,7 +269,7 @@ contract Morpho is IMorpho { require(UtilsLib.exactlyOneZero(assets, shares), ErrorsLib.INCONSISTENT_INPUT); require(onBehalf != address(0), ErrorsLib.ZERO_ADDRESS); - _accrueInterests(market, id); + _accrueInterest(market, id); if (assets > 0) shares = assets.toSharesDown(totalBorrow[id], totalBorrowShares[id]); else assets = shares.toAssetsUp(totalBorrow[id], totalBorrowShares[id]); @@ -296,7 +296,7 @@ contract Morpho is IMorpho { require(assets != 0, ErrorsLib.ZERO_ASSETS); require(onBehalf != address(0), ErrorsLib.ZERO_ADDRESS); - // Don't accrue interests because it's not required and it saves gas. + // Don't accrue interest because it's not required and it saves gas. collateral[id][onBehalf] += assets; @@ -316,7 +316,7 @@ contract Morpho is IMorpho { require(receiver != address(0), ErrorsLib.ZERO_ADDRESS); require(_isSenderAuthorized(onBehalf), ErrorsLib.UNAUTHORIZED); - _accrueInterests(market, id); + _accrueInterest(market, id); collateral[id][onBehalf] -= assets; @@ -338,7 +338,7 @@ contract Morpho is IMorpho { require(lastUpdate[id] != 0, ErrorsLib.MARKET_NOT_CREATED); require(seized != 0, ErrorsLib.ZERO_ASSETS); - _accrueInterests(market, id); + _accrueInterest(market, id); uint256 collateralPrice = IOracle(market.oracle).price(); @@ -425,15 +425,15 @@ contract Morpho is IMorpho { /* INTEREST MANAGEMENT */ /// @inheritdoc IMorpho - function accrueInterests(Market memory market) external { + function accrueInterest(Market memory market) external { Id id = market.id(); require(lastUpdate[id] != 0, ErrorsLib.MARKET_NOT_CREATED); - _accrueInterests(market, id); + _accrueInterest(market, id); } - /// @dev Accrues interests for `market`. - function _accrueInterests(Market memory market, Id id) internal { + /// @dev Accrues interest for `market`. + function _accrueInterest(Market memory market, Id id) internal { uint256 elapsed = block.timestamp - lastUpdate[id]; if (elapsed == 0) return; @@ -442,20 +442,20 @@ contract Morpho is IMorpho { if (marketTotalBorrow != 0) { uint256 borrowRate = IIrm(market.irm).borrowRate(market); - uint256 interests = marketTotalBorrow.wMulDown(borrowRate.wTaylorCompounded(elapsed)); - totalBorrow[id] = marketTotalBorrow + interests; - totalSupply[id] += interests; + uint256 interest = marketTotalBorrow.wMulDown(borrowRate.wTaylorCompounded(elapsed)); + totalBorrow[id] = marketTotalBorrow + interest; + totalSupply[id] += interest; uint256 feeShares; if (fee[id] != 0) { - uint256 feeAmount = interests.wMulDown(fee[id]); + uint256 feeAmount = interest.wMulDown(fee[id]); // The fee amount is subtracted from the total supply in this calculation to compensate for the fact that total supply is already updated. feeShares = feeAmount.toSharesDown(totalSupply[id] - feeAmount, totalSupplyShares[id]); supplyShares[id][feeRecipient] += feeShares; totalSupplyShares[id] += feeShares; } - emit EventsLib.AccrueInterests(id, borrowRate, interests, feeShares); + emit EventsLib.AccrueInterest(id, borrowRate, interest, feeShares); } lastUpdate[id] = block.timestamp; diff --git a/src/interfaces/IMorpho.sol b/src/interfaces/IMorpho.sol index 0756db018..8754a7837 100644 --- a/src/interfaces/IMorpho.sol +++ b/src/interfaces/IMorpho.sol @@ -194,7 +194,7 @@ interface IMorpho is IFlashLender { /// @notice Supplies the given `assets` of collateral to the given `market` on behalf of `onBehalf`, /// optionally calling back the caller's `onMorphoSupplyCollateral` function with the given `data`. - /// @dev Interests are not accrued since it's not required and it saves gas. + /// @dev Interest are not accrued since it's not required and it saves gas. /// @dev Supplying a large amount can overflow and revert without any error message. /// @param market The market to supply collateral to. /// @param assets The assets of collateral to supply. @@ -235,8 +235,8 @@ interface IMorpho is IFlashLender { /// @param signature The signature. function setAuthorizationWithSig(Authorization calldata authorization, Signature calldata signature) external; - /// @notice Accrues interests for `market`. - function accrueInterests(Market memory market) external; + /// @notice Accrues interest for `market`. + function accrueInterest(Market memory market) external; /// @notice Returns the data stored on the different `slots`. function extsload(bytes32[] memory slots) external view returns (bytes32[] memory res); diff --git a/src/libraries/EventsLib.sol b/src/libraries/EventsLib.sol index 5be30fc00..e697062de 100644 --- a/src/libraries/EventsLib.sol +++ b/src/libraries/EventsLib.sol @@ -138,10 +138,10 @@ library EventsLib { /// @param usedNonce The nonce that was used. event IncrementNonce(address indexed caller, address indexed authorizer, uint256 usedNonce); - /// @notice Emitted when accruing interests. + /// @notice Emitted when accruing interest. /// @param id The market id. /// @param prevBorrowRate The previous borrow rate. - /// @param interests The amount of interests accrued. + /// @param interest The amount of interest accrued. /// @param feeShares The amount of shares minted as fee. - event AccrueInterests(Id indexed id, uint256 prevBorrowRate, uint256 interests, uint256 feeShares); + event AccrueInterest(Id indexed id, uint256 prevBorrowRate, uint256 interest, uint256 feeShares); } diff --git a/src/libraries/periphery/MorphoLib.sol b/src/libraries/periphery/MorphoLib.sol index 16719aea8..826592661 100644 --- a/src/libraries/periphery/MorphoLib.sol +++ b/src/libraries/periphery/MorphoLib.sol @@ -12,9 +12,9 @@ import {MorphoStorageLib} from "./MorphoStorageLib.sol"; /// @title MorphoLib /// @author Morpho Labs /// @custom:contact security@morpho.xyz -/// @notice Helper library exposing getters with the expected value after interests accrual. +/// @notice Helper library exposing getters with the expected value after interest accrual. /// @dev This library is not used in Morpho itself and is intended to be used by integrators. -/// @dev The getter to retrieve the total borrow shares is not exposed because interests accrual does not apply to it. +/// @dev The getter to retrieve the total borrow shares is not exposed because interest accrual does not apply to it. /// The value can be queried directly on Morpho using `totalBorrowShares`. library MorphoLib { using MathLib for uint256; @@ -48,12 +48,12 @@ library MorphoLib { if (toralBorrow != 0) { uint256 borrowRate = IIrm(market.irm).borrowRateView(market); - uint256 interests = toralBorrow.wMulDown(borrowRate.wTaylorCompounded(elapsed)); - toralBorrow += interests; - totalSupply += interests; + uint256 interest = toralBorrow.wMulDown(borrowRate.wTaylorCompounded(elapsed)); + toralBorrow += interest; + totalSupply += interest; if (fee != 0) { - uint256 feeAmount = interests.wMulDown(fee); + uint256 feeAmount = interest.wMulDown(fee); // The fee amount is subtracted from the total supply in this calculation to compensate for the fact that total supply is already updated. uint256 feeShares = feeAmount.toSharesDown(totalSupply - feeAmount, totalSupplyShares); diff --git a/test/forge/integration/TestIntegrationAccrueInterests.t.sol b/test/forge/integration/TestIntegrationAccrueInterest.t.sol similarity index 85% rename from test/forge/integration/TestIntegrationAccrueInterests.t.sol rename to test/forge/integration/TestIntegrationAccrueInterest.t.sol index b8d69daf6..184b43162 100644 --- a/test/forge/integration/TestIntegrationAccrueInterests.t.sol +++ b/test/forge/integration/TestIntegrationAccrueInterest.t.sol @@ -3,18 +3,18 @@ pragma solidity ^0.8.0; import "../BaseTest.sol"; -contract IntegrationAccrueInterestsTest is BaseTest { +contract IntegrationAccrueInterestTest is BaseTest { using MathLib for uint256; using SharesMathLib for uint256; - function testAccrueInterestsMarketNotCreated(Market memory marketFuzz) public { + function testAccrueInterestMarketNotCreated(Market memory marketFuzz) public { vm.assume(neq(market, marketFuzz)); vm.expectRevert(bytes(ErrorsLib.MARKET_NOT_CREATED)); - morpho.accrueInterests(marketFuzz); + morpho.accrueInterest(marketFuzz); } - function testAccrueInterestsNoTimeElapsed(uint256 amountSupplied, uint256 amountBorrowed) public { + function testAccrueInterestNoTimeElapsed(uint256 amountSupplied, uint256 amountBorrowed) public { amountSupplied = bound(amountSupplied, 2, MAX_TEST_AMOUNT); amountBorrowed = bound(amountBorrowed, 1, amountSupplied); @@ -39,7 +39,7 @@ contract IntegrationAccrueInterestsTest is BaseTest { uint256 totalSupplyBeforeAccrued = morpho.totalSupply(id); uint256 totalSupplySharesBeforeAccrued = morpho.totalSupplyShares(id); - morpho.accrueInterests(market); + morpho.accrueInterest(market); assertEq(morpho.totalBorrow(id), totalBorrowBeforeAccrued, "total borrow"); assertEq(morpho.totalSupply(id), totalSupplyBeforeAccrued, "total supply"); @@ -47,7 +47,7 @@ contract IntegrationAccrueInterestsTest is BaseTest { assertEq(morpho.supplyShares(id, OWNER), 0, "feeRecipient's supply shares"); } - function testAccrueInterestsNoBorrow(uint256 amountSupplied, uint256 timeElapsed) public { + function testAccrueInterestNoBorrow(uint256 amountSupplied, uint256 timeElapsed) public { amountSupplied = bound(amountSupplied, 2, MAX_TEST_AMOUNT); timeElapsed = uint32(bound(timeElapsed, 1, type(uint32).max)); @@ -66,7 +66,7 @@ contract IntegrationAccrueInterestsTest is BaseTest { uint256 totalSupplyBeforeAccrued = morpho.totalSupply(id); uint256 totalSupplySharesBeforeAccrued = morpho.totalSupplyShares(id); - morpho.accrueInterests(market); + morpho.accrueInterest(market); assertEq(morpho.totalBorrow(id), totalBorrowBeforeAccrued, "total borrow"); assertEq(morpho.totalSupply(id), totalSupplyBeforeAccrued, "total supply"); @@ -107,14 +107,14 @@ contract IntegrationAccrueInterestsTest is BaseTest { uint256 totalBorrowBeforeAccrued = morpho.totalBorrow(id); uint256 totalSupplyBeforeAccrued = morpho.totalSupply(id); uint256 totalSupplySharesBeforeAccrued = morpho.totalSupplyShares(id); - uint256 expectedAccruedInterests = totalBorrowBeforeAccrued.wMulDown(borrowRate.wTaylorCompounded(timeElapsed)); + uint256 expectedAccruedInterest = totalBorrowBeforeAccrued.wMulDown(borrowRate.wTaylorCompounded(timeElapsed)); vm.expectEmit(true, true, true, true, address(morpho)); - emit EventsLib.AccrueInterests(id, borrowRate, expectedAccruedInterests, 0); - morpho.accrueInterests(market); + emit EventsLib.AccrueInterest(id, borrowRate, expectedAccruedInterest, 0); + morpho.accrueInterest(market); - assertEq(morpho.totalBorrow(id), totalBorrowBeforeAccrued + expectedAccruedInterests, "total borrow"); - assertEq(morpho.totalSupply(id), totalSupplyBeforeAccrued + expectedAccruedInterests, "total supply"); + assertEq(morpho.totalBorrow(id), totalBorrowBeforeAccrued + expectedAccruedInterest, "total borrow"); + assertEq(morpho.totalSupply(id), totalSupplyBeforeAccrued + expectedAccruedInterest, "total supply"); assertEq(morpho.totalSupplyShares(id), totalSupplySharesBeforeAccrued, "total supply shares"); assertEq(morpho.supplyShares(id, OWNER), 0, "feeRecipient's supply shares"); assertEq(morpho.lastUpdate(id), block.timestamp, "last update"); @@ -125,7 +125,7 @@ contract IntegrationAccrueInterestsTest is BaseTest { uint256 totalBorrowBeforeAccrued; uint256 totalSupplyBeforeAccrued; uint256 totalSupplySharesBeforeAccrued; - uint256 expectedAccruedInterests; + uint256 expectedAccruedInterest; uint256 feeAmount; uint256 feeShares; } @@ -170,23 +170,23 @@ contract IntegrationAccrueInterestsTest is BaseTest { params.totalBorrowBeforeAccrued = morpho.totalBorrow(id); params.totalSupplyBeforeAccrued = morpho.totalSupply(id); params.totalSupplySharesBeforeAccrued = morpho.totalSupplyShares(id); - params.expectedAccruedInterests = + params.expectedAccruedInterest = params.totalBorrowBeforeAccrued.wMulDown(params.borrowRate.wTaylorCompounded(timeElapsed)); - params.feeAmount = params.expectedAccruedInterests.wMulDown(fee); + params.feeAmount = params.expectedAccruedInterest.wMulDown(fee); params.feeShares = params.feeAmount.toSharesDown( - params.totalSupplyBeforeAccrued + params.expectedAccruedInterests - params.feeAmount, + params.totalSupplyBeforeAccrued + params.expectedAccruedInterest - params.feeAmount, params.totalSupplySharesBeforeAccrued ); vm.expectEmit(true, true, true, true, address(morpho)); - emit EventsLib.AccrueInterests(id, params.borrowRate, params.expectedAccruedInterests, params.feeShares); - morpho.accrueInterests(market); + emit EventsLib.AccrueInterest(id, params.borrowRate, params.expectedAccruedInterest, params.feeShares); + morpho.accrueInterest(market); assertEq( - morpho.totalBorrow(id), params.totalBorrowBeforeAccrued + params.expectedAccruedInterests, "total borrow" + morpho.totalBorrow(id), params.totalBorrowBeforeAccrued + params.expectedAccruedInterest, "total borrow" ); assertEq( - morpho.totalSupply(id), params.totalSupplyBeforeAccrued + params.expectedAccruedInterests, "total supply" + morpho.totalSupply(id), params.totalSupplyBeforeAccrued + params.expectedAccruedInterest, "total supply" ); assertEq( morpho.totalSupplyShares(id), diff --git a/test/morpho_tests.tree b/test/morpho_tests.tree index da5dc2720..8826c2e0d 100644 --- a/test/morpho_tests.tree +++ b/test/morpho_tests.tree @@ -33,7 +33,7 @@ ├── when newFee > MAX_FEE │ └── revert with MAX_FEE_EXCEEDED └── when newFee <= MAX_FEE - ├── it should accrue the interests + ├── it should accrue the interest ├── it should set fee[market.id] to newFee └── it should emit SetFee(market.id, newFee) . @@ -68,7 +68,7 @@ ├── when onBehalf is the zero address │ └── revert with ZERO_ADDRESS └── when onBehalf is not the zero address - ├── it should accrue the interests + ├── it should accrue the interest ├── when assets is not zero │ └── it should set shares to assets.toSharesUp(totalSupply[market.id], totalSupplyShares[market.id]) ├── when assets is zero @@ -95,7 +95,7 @@ ├── when not sender and not approved │ └── revert with UNAUTHORIZED └── when sender or approved - ├── it should accrue the interests + ├── it should accrue the interest ├── when assets is not zero │ └── it should set shares to assets.toSharesUp(totalSupply[market.id], totalSupplyShares[market.id]) ├── when assets is zero @@ -123,7 +123,7 @@ ├── when not sender and not approved │ └── revert with UNAUTHORIZED └── when sender or approved - ├── it should accrue the interests + ├── it should accrue the interest ├── when assets is not zero │ └── it should set shares to assets.toSharesUp(totalSupply[market.id], totalSupplyShares[market.id]) ├── when assets is zero @@ -152,7 +152,7 @@ ├── when onBehalf is the zero address │ └── revert with ZERO_ADDRESS └── when onBehalf is not the zero address - ├── it should accrue the interests + ├── it should accrue the interest ├── when assets is not zero │ └── it should set shares to assets.toSharesUp(totalSupply[market.id], totalSupplyShares[market.id]) ├── when assets is zero @@ -195,7 +195,7 @@ ├── when not sender and not approved │ └── revert with MANAGER_NOT_APPROVED └── when sender or approved - ├── it should accrue the interests + ├── it should accrue the interest ├── it should remove assets from collateral[market.id][onBehalf] ├── it should emit WithdrawCollateral(market.id, msg.sender, onBehalf, receiver, assets) ├── it should transfer assets of the collateral asset to the receiver @@ -209,7 +209,7 @@ ├── when the assets to seized is zero │ └── revert with ZERO_ASSETS └── when the assets to seized is not zero - ├── it should accrue the interests + ├── it should accrue the interest ├── when position is healthy │ └── revert with HEALTHY_POSITION └── when the position is not healthy @@ -257,24 +257,24 @@ ├── should set isApproved[msg.sender][authorized] to newIsAuthorized └── it should emit SetAuthorization(msg.sender, msg.sender, authorized, newIsAuthorized) . -└── accrueInterests(Market memory market) external +└── accrueInterest(Market memory market) external ├── when market is not created │ └── revert with MARKET_NOT_CREATED └── when market is created - └── it should accrue the interests + └── it should accrue the interest . -└── _accrueInterests(Market memory market, Id id) internal - └── when interests not already accrued in the block +└── _accrueInterest(Market memory market, Id id) internal + └── when interest not already accrued in the block ├── it should set lastUpdate to block.timestamp └── when marketTotalBorrow is not 0 - ├── it should compute accruedInterests = marketTotalBorrow.wMulDown(borrowRate.wTaylorCompounded(elapsed)) - ├── it should add accruedInterests to totalBorrow - ├── it should add accruedInterests to totalSupply + ├── it should compute accruedInterest = marketTotalBorrow.wMulDown(borrowRate.wTaylorCompounded(elapsed)) + ├── it should add accruedInterest to totalBorrow + ├── it should add accruedInterest to totalSupply └── when fee[id] != 0 - │ ├── it should add accruedInterests.wMulDown(fee[id]) to feeAmount + │ ├── it should add accruedInterest.wMulDown(fee[id]) to feeAmount │ ├── it should add feeAmount.mulDivDown(totalSupplyShares[id], totalSupply[id] - feeAmount) to supplyShares[id][feeRecipient] │ └── it should add feeAmount.mulDivDown(totalSupplyShares[id], totalSupply[id] - feeAmount) to totalSupplyShares[id] - └── it should emit AccrueInterests(id, borrowRate, accruedInterests, feeShares) + └── it should emit AccrueInterest(id, borrowRate, accruedInterest, feeShares) . └── _isHealthy(Market memory market, Id id, address user, uint256 collateralPrice) internal ├── it should compute borrowed = borrowShares[id][user].toAssetsUp(totalBorrow[id], totalBorrowShares[id]) From 50852076188e4c8cb52c0c187ba9e5931d1989d1 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Wed, 16 Aug 2023 22:58:57 +0200 Subject: [PATCH 18/24] refactor: remove uselesss line spaces --- src/Morpho.sol | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Morpho.sol b/src/Morpho.sol index 5d8066a8a..ea85c9ba7 100644 --- a/src/Morpho.sol +++ b/src/Morpho.sol @@ -30,10 +30,8 @@ uint256 constant ORACLE_PRICE_SCALE = 1e36; uint256 constant LIQUIDATION_CURSOR = 0.3e18; /// @dev Max liquidation incentive factor. uint256 constant MAX_LIQUIDATION_INCENTIVE_FACTOR = 1.15e18; - /// @dev The EIP-712 typeHash for EIP712Domain. bytes32 constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)"); - /// @dev The EIP-712 typeHash for Authorization. bytes32 constant AUTHORIZATION_TYPEHASH = keccak256("Authorization(address authorizer,address authorized,bool isAuthorized,uint256 nonce,uint256 deadline)"); From dc82f525b2e983bf56fe61f4da49c36d45c355fc Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Thu, 17 Aug 2023 10:15:07 +0200 Subject: [PATCH 19/24] test: add MorphoLib test --- .../TestIntegrationAccrueInterest.t.sol | 4 +- test/forge/periphery/MorphoLib.t.sol | 134 ++++++++++++++++++ 2 files changed, 136 insertions(+), 2 deletions(-) create mode 100644 test/forge/periphery/MorphoLib.t.sol diff --git a/test/forge/integration/TestIntegrationAccrueInterest.t.sol b/test/forge/integration/TestIntegrationAccrueInterest.t.sol index 184b43162..9dced9878 100644 --- a/test/forge/integration/TestIntegrationAccrueInterest.t.sol +++ b/test/forge/integration/TestIntegrationAccrueInterest.t.sol @@ -183,10 +183,10 @@ contract IntegrationAccrueInterestTest is BaseTest { morpho.accrueInterest(market); assertEq( - morpho.totalBorrow(id), params.totalBorrowBeforeAccrued + params.expectedAccruedInterest, "total borrow" + morpho.totalSupply(id), params.totalSupplyBeforeAccrued + params.expectedAccruedInterest, "total supply" ); assertEq( - morpho.totalSupply(id), params.totalSupplyBeforeAccrued + params.expectedAccruedInterest, "total supply" + morpho.totalBorrow(id), params.totalBorrowBeforeAccrued + params.expectedAccruedInterest, "total borrow" ); assertEq( morpho.totalSupplyShares(id), diff --git a/test/forge/periphery/MorphoLib.t.sol b/test/forge/periphery/MorphoLib.t.sol new file mode 100644 index 000000000..53b1abf69 --- /dev/null +++ b/test/forge/periphery/MorphoLib.t.sol @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; + +import {MorphoLib} from "../../../src/libraries/periphery/MorphoLib.sol"; + +import "../BaseTest.sol"; + +contract MorphoLibTest is BaseTest { + using MathLib for uint256; + using MorphoLib for IMorpho; + using SharesMathLib for uint256; + + function testVirtualAccrueInterest(uint256 amountSupplied, uint256 amountBorrowed, uint256 timeElapsed, uint256 fee) + public + { + _generatePendingInterest(amountSupplied, amountBorrowed, timeElapsed, fee); + + (uint256 virtualTotalSupply, uint256 virtualTotalBorrow, uint256 virtualTotalSupplyShares) = + IMorpho(address(morpho)).virtualAccrueInterest(market); + + morpho.accrueInterest(market); + + assertEq(virtualTotalSupply, morpho.totalSupply(id), "total supply"); + assertEq(virtualTotalBorrow, morpho.totalBorrow(id), "total borrow"); + assertEq(virtualTotalSupplyShares, morpho.totalSupplyShares(id), "total supply shares"); + } + + function testExpectedTotalSupply(uint256 amountSupplied, uint256 amountBorrowed, uint256 timeElapsed, uint256 fee) + public + { + _generatePendingInterest(amountSupplied, amountBorrowed, timeElapsed, fee); + + uint256 expectedTotalSupply = IMorpho(address(morpho)).expectedTotalSupply(market); + + morpho.accrueInterest(market); + + assertEq(expectedTotalSupply, morpho.totalSupply(id)); + } + + function testExpectedTotalBorrow(uint256 amountSupplied, uint256 amountBorrowed, uint256 timeElapsed, uint256 fee) + public + { + _generatePendingInterest(amountSupplied, amountBorrowed, timeElapsed, fee); + + uint256 expectedTotalBorrow = IMorpho(address(morpho)).expectedTotalBorrow(market); + + morpho.accrueInterest(market); + + assertEq(expectedTotalBorrow, morpho.totalBorrow(id)); + } + + function testExpectedTotalSupplyShares( + uint256 amountSupplied, + uint256 amountBorrowed, + uint256 timeElapsed, + uint256 fee + ) public { + _generatePendingInterest(amountSupplied, amountBorrowed, timeElapsed, fee); + + uint256 expectedTotalSupplyShares = IMorpho(address(morpho)).expectedTotalSupplyShares(market); + + morpho.accrueInterest(market); + + assertEq(expectedTotalSupplyShares, morpho.totalSupplyShares(id)); + } + + function testExpectedSupplyBalance(uint256 amountSupplied, uint256 amountBorrowed, uint256 timeElapsed, uint256 fee) + internal + { + _generatePendingInterest(amountSupplied, amountBorrowed, timeElapsed, fee); + + uint256 expectedSupplyBalance = IMorpho(address(morpho)).expectedSupplyBalance(market, address(this)); + + morpho.accrueInterest(market); + + uint256 actualSupplyBalance = + morpho.supplyShares(id, address(this)).toAssetsDown(morpho.totalSupply(id), morpho.totalSupplyShares(id)); + + assertEq(expectedSupplyBalance, actualSupplyBalance); + } + + function testExpectedBorrowBalance(uint256 amountSupplied, uint256 amountBorrowed, uint256 timeElapsed, uint256 fee) + internal + { + _generatePendingInterest(amountSupplied, amountBorrowed, timeElapsed, fee); + + uint256 expectedBorrowBalance = IMorpho(address(morpho)).expectedBorrowBalance(market, address(this)); + + morpho.accrueInterest(market); + + uint256 actualBorrowBalance = + morpho.borrowShares(id, address(this)).toAssetsUp(morpho.totalBorrow(id), morpho.totalBorrowShares(id)); + + assertEq(expectedBorrowBalance, actualBorrowBalance); + } + + function _generatePendingInterest(uint256 amountSupplied, uint256 amountBorrowed, uint256 timeElapsed, uint256 fee) + internal + { + amountSupplied = bound(amountSupplied, 0, MAX_TEST_AMOUNT); + amountBorrowed = bound(amountBorrowed, 0, amountSupplied); + timeElapsed = uint32(bound(timeElapsed, 0, 1e8)); + fee = bound(fee, 0, MAX_FEE); + + // Set fee parameters. + vm.startPrank(OWNER); + morpho.setFeeRecipient(OWNER); + morpho.setFee(market, fee); + vm.stopPrank(); + + if (amountSupplied > 0) { + borrowableToken.setBalance(address(this), amountSupplied); + morpho.supply(market, amountSupplied, 0, address(this), hex""); + + if (amountBorrowed > 0) { + uint256 collateralPrice = IOracle(market.oracle).price(); + collateralToken.setBalance( + BORROWER, amountBorrowed.wDivUp(LLTV).mulDivUp(ORACLE_PRICE_SCALE, collateralPrice) + ); + + vm.startPrank(BORROWER); + morpho.supplyCollateral( + market, amountBorrowed.wDivUp(LLTV).mulDivUp(ORACLE_PRICE_SCALE, collateralPrice), BORROWER, hex"" + ); + morpho.borrow(market, amountBorrowed, 0, BORROWER, BORROWER); + vm.stopPrank(); + } + } + + // New block. + vm.roll(block.number + 1); + vm.warp(block.timestamp + timeElapsed); + } +} From be93dd1da477af31c0c5d833e6520bbc38e3c05e Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Thu, 17 Aug 2023 14:08:02 +0200 Subject: [PATCH 20/24] feat: add missing slot getters --- src/libraries/periphery/MorphoStorageLib.sol | 42 +++++++++++++++++++- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/src/libraries/periphery/MorphoStorageLib.sol b/src/libraries/periphery/MorphoStorageLib.sol index f3257fd91..493d8b98a 100644 --- a/src/libraries/periphery/MorphoStorageLib.sol +++ b/src/libraries/periphery/MorphoStorageLib.sol @@ -9,6 +9,8 @@ import {Id} from "../MarketLib.sol"; /// @notice Helper library exposing getters to access Morpho storage variables' slot. /// @dev This library is not used in Morpho itself and is intended to be used by integrators. library MorphoStorageLib { + /* SLOTS */ + uint256 internal constant OWNER_SLOT = 0; uint256 internal constant FEE_RECIPIENT_SLOT = 1; uint256 internal constant SUPPLY_SHARES_SLOT = 2; @@ -23,6 +25,18 @@ library MorphoStorageLib { uint256 internal constant IS_IRM_ENABLED_SLOT = 11; uint256 internal constant IS_LLTV_ENABLED_SLOT = 12; uint256 internal constant IS_AUTHORIZED_SLOT = 13; + uint256 internal constant NONCE_SLOT = 14; + uint256 internal constant ID_TO_MARKET_SLOT = 15; + + /* SLOT OFFSETS */ + + uint256 internal constant BORROWABLE_TOKEN_SLOT_OFFSET = 0; + uint256 internal constant COLLATERAL_TOKEN_SLOT_OFFSET = 1; + uint256 internal constant ORACLE_SLOT_OFFSET = 2; + uint256 internal constant IRM_SLOT_OFFSET = 3; + uint256 internal constant LLTV_SLOT_OFFSET = 4; + + /* GETTERS */ function ownerSlot() internal pure returns (bytes32) { return bytes32(OWNER_SLOT); @@ -76,7 +90,31 @@ library MorphoStorageLib { return keccak256(abi.encode(lltv, IS_LLTV_ENABLED_SLOT)); } - function isAuthorizedSlot(address delegator, address manager) internal pure returns (bytes32) { - return keccak256(abi.encode(manager, keccak256(abi.encode(delegator, IS_AUTHORIZED_SLOT)))); + function isAuthorizedSlot(address authorizer, address authorizee) internal pure returns (bytes32) { + return keccak256(abi.encode(authorizee, keccak256(abi.encode(authorizer, IS_AUTHORIZED_SLOT)))); + } + + function nonceSlot(address authorizer) internal pure returns (bytes32) { + return keccak256(abi.encode(authorizer, NONCE_SLOT)); + } + + function idToBorrowTokenSlot(Id id) internal pure returns (bytes32) { + return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_SLOT))) + BORROWABLE_TOKEN_SLOT_OFFSET); + } + + function idToCollateralTokenSlot(Id id) internal pure returns (bytes32) { + return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_SLOT))) + COLLATERAL_TOKEN_SLOT_OFFSET); + } + + function idToOracleSlot(Id id) internal pure returns (bytes32) { + return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_SLOT))) + ORACLE_SLOT_OFFSET); + } + + function idToIrmSlot(Id id) internal pure returns (bytes32) { + return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_SLOT))) + IRM_SLOT_OFFSET); + } + + function idToLltvSlot(Id id) internal pure returns (bytes32) { + return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_SLOT))) + LLTV_SLOT_OFFSET); } } From e937427552fc765cf673f9f0b68be9d0936360b1 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Thu, 17 Aug 2023 14:08:49 +0200 Subject: [PATCH 21/24] test: add MorphoStorageLib tests --- test/forge/periphery/MorphoStorageLib.t.sol | 108 ++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 test/forge/periphery/MorphoStorageLib.t.sol diff --git a/test/forge/periphery/MorphoStorageLib.t.sol b/test/forge/periphery/MorphoStorageLib.t.sol new file mode 100644 index 000000000..89b238f48 --- /dev/null +++ b/test/forge/periphery/MorphoStorageLib.t.sol @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; + +import {MorphoStorageLib} from "../../../src/libraries/periphery/MorphoStorageLib.sol"; +import {SigUtils} from "test/forge/helpers/SigUtils.sol"; + +import "../BaseTest.sol"; + +contract MorphoStorageLibTest is BaseTest { + using MathLib for uint256; + using SharesMathLib for uint256; + + function testStorage() public { + // Prepare storage layout with non empty values. + + uint256 amountSupplied = 1 ether; + uint256 amountBorrowed = 0.5 ether; + uint256 fee = 0.01 ether; + + // Set fee parameters. + vm.startPrank(OWNER); + morpho.setFeeRecipient(OWNER); + morpho.setFee(market, fee); + vm.stopPrank(); + + borrowableToken.setBalance(address(this), amountSupplied); + morpho.supply(market, amountSupplied, 0, address(this), hex""); + + uint256 collateralPrice = IOracle(market.oracle).price(); + collateralToken.setBalance(BORROWER, amountBorrowed.wDivUp(LLTV).mulDivUp(ORACLE_PRICE_SCALE, collateralPrice)); + + vm.startPrank(BORROWER); + morpho.supplyCollateral( + market, amountBorrowed.wDivUp(LLTV).mulDivUp(ORACLE_PRICE_SCALE, collateralPrice), BORROWER, hex"" + ); + morpho.borrow(market, amountBorrowed, 0, BORROWER, BORROWER); + vm.stopPrank(); + + uint256 privateKey = 1; + address authorizer = vm.addr(privateKey); + Authorization memory authorization = Authorization({ + authorizer: authorizer, + authorized: BORROWER, + isAuthorized: true, + nonce: 0, + deadline: block.timestamp + type(uint32).max + }); + + Signature memory sig; + bytes32 digest = SigUtils.getTypedDataHash(morpho.DOMAIN_SEPARATOR(), authorization); + (sig.v, sig.r, sig.s) = vm.sign(privateKey, digest); + + morpho.setAuthorizationWithSig(authorization, sig); + + bytes32[] memory slots = new bytes32[](20); + slots[0] = MorphoStorageLib.ownerSlot(); + slots[1] = MorphoStorageLib.feeRecipientSlot(); + slots[2] = MorphoStorageLib.supplySharesSlot(id, address(this)); + slots[3] = MorphoStorageLib.borrowSharesSlot(id, address(this)); + slots[4] = MorphoStorageLib.collateralSlot(id, address(this)); + slots[5] = MorphoStorageLib.totalSupplySlot(id); + slots[6] = MorphoStorageLib.totalSupplySharesSlot(id); + slots[7] = MorphoStorageLib.totalBorrowSlot(id); + slots[8] = MorphoStorageLib.totalBorrowSharesSlot(id); + slots[9] = MorphoStorageLib.lastUpdateSlot(id); + slots[10] = MorphoStorageLib.feeSlot(id); + slots[11] = MorphoStorageLib.isIrmEnabledSlot(address(irm)); + slots[12] = MorphoStorageLib.isLltvEnabledSlot(LLTV); + slots[13] = MorphoStorageLib.isAuthorizedSlot(authorizer, BORROWER); + slots[14] = MorphoStorageLib.nonceSlot(authorizer); + slots[15] = MorphoStorageLib.idToBorrowTokenSlot(id); + slots[16] = MorphoStorageLib.idToCollateralTokenSlot(id); + slots[17] = MorphoStorageLib.idToOracleSlot(id); + slots[18] = MorphoStorageLib.idToIrmSlot(id); + slots[19] = MorphoStorageLib.idToLltvSlot(id); + + bytes32[] memory values = morpho.extsload(slots); + + assertEq(abi.decode(abi.encodePacked(values[0]), (address)), morpho.owner()); + assertEq(abi.decode(abi.encodePacked(values[1]), (address)), morpho.feeRecipient()); + assertEq(uint256(values[2]), morpho.supplyShares(id, address(this))); + assertEq(uint256(values[3]), morpho.borrowShares(id, address(this))); + assertEq(uint256(values[4]), morpho.collateral(id, address(this))); + assertEq(uint256(values[5]), morpho.totalSupply(id)); + assertEq(uint256(values[6]), morpho.totalSupplyShares(id)); + assertEq(uint256(values[7]), morpho.totalBorrow(id)); + assertEq(uint256(values[8]), morpho.totalBorrowShares(id)); + assertEq(uint256(values[9]), morpho.lastUpdate(id)); + assertEq(uint256(values[10]), morpho.fee(id)); + assertEq(abi.decode(abi.encodePacked(values[11]), (bool)), morpho.isIrmEnabled(address(irm))); + assertEq(abi.decode(abi.encodePacked(values[12]), (bool)), morpho.isLltvEnabled(LLTV)); + assertEq(abi.decode(abi.encodePacked(values[13]), (bool)), morpho.isAuthorized(authorizer, BORROWER)); + assertEq(uint256(values[14]), morpho.nonce(authorizer)); + + ( + address expectedBorrowableToken, + address expectedCollateralToken, + address expectedOracle, + address expectedIrm, + uint256 expectedLltv + ) = morpho.idToMarket(id); + assertEq(abi.decode(abi.encodePacked(values[15]), (address)), expectedBorrowableToken); + assertEq(abi.decode(abi.encodePacked(values[16]), (address)), expectedCollateralToken); + assertEq(abi.decode(abi.encodePacked(values[17]), (address)), expectedOracle); + assertEq(abi.decode(abi.encodePacked(values[18]), (address)), expectedIrm); + assertEq(uint256(values[19]), expectedLltv); + } +} From e35f684a686744131a4c9e49382ccb17f1521379 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Thu, 17 Aug 2023 14:41:02 +0200 Subject: [PATCH 22/24] refactor: apply suggestions --- src/libraries/periphery/MorphoLib.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libraries/periphery/MorphoLib.sol b/src/libraries/periphery/MorphoLib.sol index 826592661..7927d93cd 100644 --- a/src/libraries/periphery/MorphoLib.sol +++ b/src/libraries/periphery/MorphoLib.sol @@ -78,6 +78,7 @@ library MorphoLib { (,, totalSupplyShares) = virtualAccrueInterest(morpho, market); } + /// @dev Warning: It does not work for `feeRecipient` because the increase of supply shares during the interest accrual is not taken into account. function expectedSupplyBalance(IMorpho morpho, Market memory market, address user) internal view From f9a46f4c2148a853676d984b05d4c59fefbe7321 Mon Sep 17 00:00:00 2001 From: Merlin Egalite <44097430+MerlinEgalite@users.noreply.github.com> Date: Thu, 17 Aug 2023 15:05:26 +0200 Subject: [PATCH 23/24] docs: apply suggestion Co-authored-by: MathisGD <74971347+MathisGD@users.noreply.github.com> Signed-off-by: Merlin Egalite <44097430+MerlinEgalite@users.noreply.github.com> --- src/libraries/periphery/MorphoLib.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/periphery/MorphoLib.sol b/src/libraries/periphery/MorphoLib.sol index 7927d93cd..6c9a80f04 100644 --- a/src/libraries/periphery/MorphoLib.sol +++ b/src/libraries/periphery/MorphoLib.sol @@ -78,7 +78,7 @@ library MorphoLib { (,, totalSupplyShares) = virtualAccrueInterest(morpho, market); } - /// @dev Warning: It does not work for `feeRecipient` because the increase of supply shares during the interest accrual is not taken into account. + /// @dev Warning: It does not work for `feeRecipient` because their supply shares increase is not taken into account. function expectedSupplyBalance(IMorpho morpho, Market memory market, address user) internal view From cc78aed6895a48f2231e955e2525032259a1a142 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Thu, 17 Aug 2023 21:21:59 +0200 Subject: [PATCH 24/24] refactor: bundle suggestions --- src/libraries/periphery/MorphoLib.sol | 14 +++++----- src/libraries/periphery/MorphoStorageLib.sol | 22 +++++++-------- test/forge/periphery/MorphoLib.t.sol | 2 +- test/forge/periphery/MorphoStorageLib.t.sol | 29 ++++++++++---------- 4 files changed, 34 insertions(+), 33 deletions(-) diff --git a/src/libraries/periphery/MorphoLib.sol b/src/libraries/periphery/MorphoLib.sol index 6c9a80f04..b3f1f7727 100644 --- a/src/libraries/periphery/MorphoLib.sol +++ b/src/libraries/periphery/MorphoLib.sol @@ -14,14 +14,14 @@ import {MorphoStorageLib} from "./MorphoStorageLib.sol"; /// @custom:contact security@morpho.xyz /// @notice Helper library exposing getters with the expected value after interest accrual. /// @dev This library is not used in Morpho itself and is intended to be used by integrators. -/// @dev The getter to retrieve the total borrow shares is not exposed because interest accrual does not apply to it. +/// @dev The getter to retrieve the expected total borrow shares is not exposed because interest accrual does not apply to it. /// The value can be queried directly on Morpho using `totalBorrowShares`. library MorphoLib { using MathLib for uint256; using MarketLib for Market; using SharesMathLib for uint256; - function virtualAccrueInterest(IMorpho morpho, Market memory market) + function expectedAccrueInterest(IMorpho morpho, Market memory market) internal view returns (uint256 totalSupply, uint256 toralBorrow, uint256 totalSupplyShares) @@ -63,11 +63,11 @@ library MorphoLib { } function expectedTotalSupply(IMorpho morpho, Market memory market) internal view returns (uint256 totalSupply) { - (totalSupply,,) = virtualAccrueInterest(morpho, market); + (totalSupply,,) = expectedAccrueInterest(morpho, market); } function expectedTotalBorrow(IMorpho morpho, Market memory market) internal view returns (uint256 totalBorrow) { - (, totalBorrow,) = virtualAccrueInterest(morpho, market); + (, totalBorrow,) = expectedAccrueInterest(morpho, market); } function expectedTotalSupplyShares(IMorpho morpho, Market memory market) @@ -75,7 +75,7 @@ library MorphoLib { view returns (uint256 totalSupplyShares) { - (,, totalSupplyShares) = virtualAccrueInterest(morpho, market); + (,, totalSupplyShares) = expectedAccrueInterest(morpho, market); } /// @dev Warning: It does not work for `feeRecipient` because their supply shares increase is not taken into account. @@ -86,7 +86,7 @@ library MorphoLib { { Id id = market.id(); uint256 supplyShares = morpho.supplyShares(id, user); - (uint256 totalSupply,, uint256 totalSupplyShares) = virtualAccrueInterest(morpho, market); + (uint256 totalSupply,, uint256 totalSupplyShares) = expectedAccrueInterest(morpho, market); return supplyShares.toAssetsDown(totalSupply, totalSupplyShares); } @@ -99,7 +99,7 @@ library MorphoLib { Id id = market.id(); uint256 borrowShares = morpho.borrowShares(id, user); uint256 totalBorrowShares = morpho.totalBorrowShares(id); - (, uint256 totalBorrow,) = virtualAccrueInterest(morpho, market); + (, uint256 totalBorrow,) = expectedAccrueInterest(morpho, market); return borrowShares.toAssetsUp(totalBorrow, totalBorrowShares); } diff --git a/src/libraries/periphery/MorphoStorageLib.sol b/src/libraries/periphery/MorphoStorageLib.sol index 493d8b98a..2772207e9 100644 --- a/src/libraries/periphery/MorphoStorageLib.sol +++ b/src/libraries/periphery/MorphoStorageLib.sol @@ -30,11 +30,11 @@ library MorphoStorageLib { /* SLOT OFFSETS */ - uint256 internal constant BORROWABLE_TOKEN_SLOT_OFFSET = 0; - uint256 internal constant COLLATERAL_TOKEN_SLOT_OFFSET = 1; - uint256 internal constant ORACLE_SLOT_OFFSET = 2; - uint256 internal constant IRM_SLOT_OFFSET = 3; - uint256 internal constant LLTV_SLOT_OFFSET = 4; + uint256 internal constant BORROWABLE_TOKEN_OFFSET = 0; + uint256 internal constant COLLATERAL_TOKEN_OFFSET = 1; + uint256 internal constant ORACLE_OFFSET = 2; + uint256 internal constant IRM_OFFSET = 3; + uint256 internal constant LLTV_OFFSET = 4; /* GETTERS */ @@ -98,23 +98,23 @@ library MorphoStorageLib { return keccak256(abi.encode(authorizer, NONCE_SLOT)); } - function idToBorrowTokenSlot(Id id) internal pure returns (bytes32) { - return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_SLOT))) + BORROWABLE_TOKEN_SLOT_OFFSET); + function idToBorrowableTokenSlot(Id id) internal pure returns (bytes32) { + return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_SLOT))) + BORROWABLE_TOKEN_OFFSET); } function idToCollateralTokenSlot(Id id) internal pure returns (bytes32) { - return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_SLOT))) + COLLATERAL_TOKEN_SLOT_OFFSET); + return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_SLOT))) + COLLATERAL_TOKEN_OFFSET); } function idToOracleSlot(Id id) internal pure returns (bytes32) { - return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_SLOT))) + ORACLE_SLOT_OFFSET); + return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_SLOT))) + ORACLE_OFFSET); } function idToIrmSlot(Id id) internal pure returns (bytes32) { - return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_SLOT))) + IRM_SLOT_OFFSET); + return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_SLOT))) + IRM_OFFSET); } function idToLltvSlot(Id id) internal pure returns (bytes32) { - return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_SLOT))) + LLTV_SLOT_OFFSET); + return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_SLOT))) + LLTV_OFFSET); } } diff --git a/test/forge/periphery/MorphoLib.t.sol b/test/forge/periphery/MorphoLib.t.sol index 53b1abf69..92461c5fb 100644 --- a/test/forge/periphery/MorphoLib.t.sol +++ b/test/forge/periphery/MorphoLib.t.sol @@ -16,7 +16,7 @@ contract MorphoLibTest is BaseTest { _generatePendingInterest(amountSupplied, amountBorrowed, timeElapsed, fee); (uint256 virtualTotalSupply, uint256 virtualTotalBorrow, uint256 virtualTotalSupplyShares) = - IMorpho(address(morpho)).virtualAccrueInterest(market); + IMorpho(address(morpho)).expectedAccrueInterest(market); morpho.accrueInterest(market); diff --git a/test/forge/periphery/MorphoStorageLib.t.sol b/test/forge/periphery/MorphoStorageLib.t.sol index 89b238f48..33f25d437 100644 --- a/test/forge/periphery/MorphoStorageLib.t.sol +++ b/test/forge/periphery/MorphoStorageLib.t.sol @@ -10,12 +10,13 @@ contract MorphoStorageLibTest is BaseTest { using MathLib for uint256; using SharesMathLib for uint256; - function testStorage() public { + function testStorage(uint256 amountSupplied, uint256 amountBorrowed, uint256 timeElapsed, uint256 fee) public { // Prepare storage layout with non empty values. - uint256 amountSupplied = 1 ether; - uint256 amountBorrowed = 0.5 ether; - uint256 fee = 0.01 ether; + amountSupplied = bound(amountSupplied, 2, MAX_TEST_AMOUNT); + amountBorrowed = bound(amountBorrowed, 1, amountSupplied); + timeElapsed = uint32(bound(timeElapsed, 1, 1e8)); + fee = bound(fee, 1, MAX_FEE); // Set fee parameters. vm.startPrank(OWNER); @@ -68,7 +69,7 @@ contract MorphoStorageLibTest is BaseTest { slots[12] = MorphoStorageLib.isLltvEnabledSlot(LLTV); slots[13] = MorphoStorageLib.isAuthorizedSlot(authorizer, BORROWER); slots[14] = MorphoStorageLib.nonceSlot(authorizer); - slots[15] = MorphoStorageLib.idToBorrowTokenSlot(id); + slots[15] = MorphoStorageLib.idToBorrowableTokenSlot(id); slots[16] = MorphoStorageLib.idToCollateralTokenSlot(id); slots[17] = MorphoStorageLib.idToOracleSlot(id); slots[18] = MorphoStorageLib.idToIrmSlot(id); @@ -76,8 +77,8 @@ contract MorphoStorageLibTest is BaseTest { bytes32[] memory values = morpho.extsload(slots); - assertEq(abi.decode(abi.encodePacked(values[0]), (address)), morpho.owner()); - assertEq(abi.decode(abi.encodePacked(values[1]), (address)), morpho.feeRecipient()); + assertEq(abi.decode(abi.encode(values[0]), (address)), morpho.owner()); + assertEq(abi.decode(abi.encode(values[1]), (address)), morpho.feeRecipient()); assertEq(uint256(values[2]), morpho.supplyShares(id, address(this))); assertEq(uint256(values[3]), morpho.borrowShares(id, address(this))); assertEq(uint256(values[4]), morpho.collateral(id, address(this))); @@ -87,9 +88,9 @@ contract MorphoStorageLibTest is BaseTest { assertEq(uint256(values[8]), morpho.totalBorrowShares(id)); assertEq(uint256(values[9]), morpho.lastUpdate(id)); assertEq(uint256(values[10]), morpho.fee(id)); - assertEq(abi.decode(abi.encodePacked(values[11]), (bool)), morpho.isIrmEnabled(address(irm))); - assertEq(abi.decode(abi.encodePacked(values[12]), (bool)), morpho.isLltvEnabled(LLTV)); - assertEq(abi.decode(abi.encodePacked(values[13]), (bool)), morpho.isAuthorized(authorizer, BORROWER)); + assertEq(abi.decode(abi.encode(values[11]), (bool)), morpho.isIrmEnabled(address(irm))); + assertEq(abi.decode(abi.encode(values[12]), (bool)), morpho.isLltvEnabled(LLTV)); + assertEq(abi.decode(abi.encode(values[13]), (bool)), morpho.isAuthorized(authorizer, BORROWER)); assertEq(uint256(values[14]), morpho.nonce(authorizer)); ( @@ -99,10 +100,10 @@ contract MorphoStorageLibTest is BaseTest { address expectedIrm, uint256 expectedLltv ) = morpho.idToMarket(id); - assertEq(abi.decode(abi.encodePacked(values[15]), (address)), expectedBorrowableToken); - assertEq(abi.decode(abi.encodePacked(values[16]), (address)), expectedCollateralToken); - assertEq(abi.decode(abi.encodePacked(values[17]), (address)), expectedOracle); - assertEq(abi.decode(abi.encodePacked(values[18]), (address)), expectedIrm); + assertEq(abi.decode(abi.encode(values[15]), (address)), expectedBorrowableToken); + assertEq(abi.decode(abi.encode(values[16]), (address)), expectedCollateralToken); + assertEq(abi.decode(abi.encode(values[17]), (address)), expectedOracle); + assertEq(abi.decode(abi.encode(values[18]), (address)), expectedIrm); assertEq(uint256(values[19]), expectedLltv); } }