From 524e6de89bcc799af24e1fde11968cc3c6c935cb Mon Sep 17 00:00:00 2001 From: Patrick Kim Date: Fri, 6 Jan 2023 14:34:27 -0500 Subject: [PATCH 01/16] feat: add an internal getter for proportion idle --- src/MorphoInternal.sol | 16 ++++++++++++++++ src/libraries/Types.sol | 2 ++ 2 files changed, 18 insertions(+) diff --git a/src/MorphoInternal.sol b/src/MorphoInternal.sol index bfe1492ef..f2bdbced1 100644 --- a/src/MorphoInternal.sol +++ b/src/MorphoInternal.sol @@ -27,6 +27,8 @@ import {ReserveConfiguration} from "./libraries/aave/ReserveConfiguration.sol"; import {MorphoStorage} from "./MorphoStorage.sol"; +import {ERC20} from "@solmate/utils/SafeTransferLib.sol"; + abstract contract MorphoInternal is MorphoStorage { using PoolLib for IPool; using MarketLib for Types.Market; @@ -297,4 +299,18 @@ abstract contract MorphoInternal is MorphoStorage { return liquidityData.debt > 0 ? liquidityData.maxDebt.wadDiv(liquidityData.debt) : type(uint256).max; } + + /// @dev Returns a ray. + function _proportionIdle(address underlying) internal view returns (uint256) { + Types.Market storage market = _market[underlying]; + uint256 idleSupply = market.idleSupply; + if (idleSupply == 0) { + return 0; + } else { + uint256 totalSupplied = ERC20(market.aToken).balanceOf(address(this)).rayMul( + market.indexes.supply.poolIndex + ) + market.deltas.p2pSupplyAmount.rayMul(market.indexes.supply.p2pIndex); + return idleSupply.rayDivUp(totalSupplied + idleSupply); + } + } } diff --git a/src/libraries/Types.sol b/src/libraries/Types.sol index cc5b8fb3c..b3ed9e6f1 100644 --- a/src/libraries/Types.sol +++ b/src/libraries/Types.sol @@ -56,6 +56,8 @@ library Types { uint16 p2pIndexCursor; // 16 bits // SLOT 8 address aToken; // 160 bits + // SLOT 9 + uint256 idleSupply; // 256 bits } // Contains storage-only dynamic arrays and mappings. From 680a968da639a2c5ae78b2c3b98e9ce44f1fa3c0 Mon Sep 17 00:00:00 2001 From: Patrick Kim Date: Fri, 6 Jan 2023 15:23:31 -0500 Subject: [PATCH 02/16] feat: update interest rates model for idle supply --- src/MorphoInternal.sol | 3 ++- src/libraries/InterestRatesLib.sol | 11 +++++++---- src/libraries/Types.sol | 1 + 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/MorphoInternal.sol b/src/MorphoInternal.sol index f2bdbced1..548f5d767 100644 --- a/src/MorphoInternal.sol +++ b/src/MorphoInternal.sol @@ -282,7 +282,8 @@ abstract contract MorphoInternal is MorphoStorage { poolBorrowIndex: indexes.borrow.poolIndex, reserveFactor: market.reserveFactor, p2pIndexCursor: market.p2pIndexCursor, - deltas: market.deltas + deltas: market.deltas, + proportionIdle: _proportionIdle(underlying) }) ); } diff --git a/src/libraries/InterestRatesLib.sol b/src/libraries/InterestRatesLib.sol index d30669ad1..344d850d8 100644 --- a/src/libraries/InterestRatesLib.sol +++ b/src/libraries/InterestRatesLib.sol @@ -29,14 +29,16 @@ library InterestRatesLib { growthFactors.p2pSupplyGrowthFactor, params.lastSupplyIndexes, params.deltas.p2pSupplyDelta, - params.deltas.p2pSupplyAmount + params.deltas.p2pSupplyAmount, + params.proportionIdle ); newP2PBorrowIndex = computeP2PIndex( growthFactors.poolBorrowGrowthFactor, growthFactors.p2pBorrowGrowthFactor, params.lastBorrowIndexes, params.deltas.p2pBorrowDelta, - params.deltas.p2pBorrowAmount + params.deltas.p2pBorrowAmount, + 0 ); } @@ -88,14 +90,15 @@ library InterestRatesLib { uint256 p2pGrowthFactor, Types.MarketSideIndexes256 memory lastIndexes, uint256 p2pDelta, - uint256 p2pAmount + uint256 p2pAmount, + uint256 proportionIdle ) internal pure returns (uint256) { if (p2pAmount == 0 || p2pDelta == 0) { return lastIndexes.p2pIndex.rayMul(p2pGrowthFactor); } uint256 shareOfTheDelta = Math.min( - p2pDelta.rayMul(lastIndexes.poolIndex).rayDivUp(p2pAmount.rayMul(lastIndexes.p2pIndex)), + p2pDelta.rayMul(lastIndexes.poolIndex).rayDivUp(p2pAmount.rayMul(lastIndexes.p2pIndex)) + proportionIdle, WadRayMath.RAY // To avoid shareOfTheDelta > 1 with rounding errors. ); // In ray. diff --git a/src/libraries/Types.sol b/src/libraries/Types.sol index b3ed9e6f1..b2debfd10 100644 --- a/src/libraries/Types.sol +++ b/src/libraries/Types.sol @@ -105,6 +105,7 @@ library Types { uint256 reserveFactor; // The reserve factor percentage (10 000 = 100%). uint256 p2pIndexCursor; // The peer-to-peer index cursor (10 000 = 100%). Deltas deltas; // The deltas and peer-to-peer amounts. + uint256 proportionIdle; // In ray. } struct GrowthFactors { From 93850b41c5ece6087297206fdd6f18921711624c Mon Sep 17 00:00:00 2001 From: Patrick Kim Date: Fri, 6 Jan 2023 16:25:11 -0500 Subject: [PATCH 03/16] feat: handle supply cap --- src/PositionsManagerInternal.sol | 30 ++++++++++++++++++--- src/libraries/aave/ReserveConfiguration.sol | 5 ++++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/PositionsManagerInternal.sol b/src/PositionsManagerInternal.sol index 60268f7f2..56c975077 100644 --- a/src/PositionsManagerInternal.sol +++ b/src/PositionsManagerInternal.sol @@ -22,6 +22,8 @@ import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet import {MatchingEngine} from "./MatchingEngine.sol"; +import {ERC20} from "@solmate/utils/SafeTransferLib.sol"; + abstract contract PositionsManagerInternal is MatchingEngine { using Math for uint256; using WadRayMath for uint256; @@ -110,7 +112,9 @@ abstract contract PositionsManagerInternal is MatchingEngine { // Supply on pool. if (amount > 0) { onPool += amount.rayDiv(indexes.supply.poolIndex); // In scaled balance. - toSupply = amount; + uint256 toIdle; + (toSupply, toIdle) = _handleSupplyCap(underlying, amount); + market.idleSupply += toIdle; } _updateSupplierInDS(underlying, user, onPool, inP2P); @@ -227,7 +231,7 @@ abstract contract PositionsManagerInternal is MatchingEngine { _userBorrows[user].remove(underlying); } - return (onPool, inP2P, toSupply, toRepay); + return (onPool, inP2P, 0, toRepay); } } @@ -292,7 +296,9 @@ abstract contract PositionsManagerInternal is MatchingEngine { deltas.p2pBorrowAmount -= Math.min(amount.rayDiv(indexes.borrow.p2pIndex), deltas.p2pBorrowAmount); emit Events.P2PAmountsUpdated(underlying, deltas.p2pSupplyAmount, deltas.p2pBorrowAmount); - toSupply = amount; + uint256 toIdle; + (toSupply, toIdle) = _handleSupplyCap(underlying, amount); + market.idleSupply += toIdle; } if (inP2P == 0 && onPool == 0) _userBorrows[user].remove(underlying); @@ -478,4 +484,22 @@ abstract contract PositionsManagerInternal is MatchingEngine { ).percentDiv(liquidationBonus); } } + + function _handleSupplyCap(address underlying, uint256 amount) + internal + view + returns (uint256 toSupply, uint256 toIdle) + { + uint256 supplyCap = _POOL.getConfiguration(underlying).getSupplyCap(); + if (supplyCap == 0) return (amount, 0); + uint256 totalSupply = ERC20(_market[underlying].aToken).totalSupply(); + if (totalSupply >= supplyCap) { + return (0, amount); + } else if (totalSupply + amount > supplyCap) { + toSupply = supplyCap - totalSupply; + toIdle = amount - toSupply; + } else { + toSupply = amount; + } + } } diff --git a/src/libraries/aave/ReserveConfiguration.sol b/src/libraries/aave/ReserveConfiguration.sol index c10f8ba94..9a0a905b7 100644 --- a/src/libraries/aave/ReserveConfiguration.sol +++ b/src/libraries/aave/ReserveConfiguration.sol @@ -33,6 +33,7 @@ library ReserveConfiguration { uint256 internal constant RESERVE_DECIMALS_START_BIT_POSITION = 48; uint256 internal constant RESERVE_FACTOR_START_BIT_POSITION = 64; uint256 internal constant EMODE_CATEGORY_START_BIT_POSITION = 168; + uint256 internal constant SUPPLY_CAP_START_BIT_POSITION = 116; function getActive(DataTypes.ReserveConfigurationMap memory self) internal pure returns (bool) { return (self.data & ~ACTIVE_MASK) != 0; @@ -74,4 +75,8 @@ library ReserveConfiguration { (dataLocal & ~EMODE_CATEGORY_MASK) >> EMODE_CATEGORY_START_BIT_POSITION ); } + + function getSupplyCap(DataTypes.ReserveConfigurationMap memory self) internal pure returns (uint256) { + return (self.data & ~SUPPLY_CAP_MASK) >> SUPPLY_CAP_START_BIT_POSITION; + } } From d37c20023803ca8ab7651737c31e0432141785cf Mon Sep 17 00:00:00 2001 From: Patrick Kim Date: Fri, 6 Jan 2023 16:35:29 -0500 Subject: [PATCH 04/16] feat: handle borrow and withdraw with idle supply --- src/PositionsManagerInternal.sol | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/PositionsManagerInternal.sol b/src/PositionsManagerInternal.sol index 56c975077..cb7321a7f 100644 --- a/src/PositionsManagerInternal.sol +++ b/src/PositionsManagerInternal.sol @@ -154,6 +154,17 @@ abstract contract PositionsManagerInternal is MatchingEngine { onPool = marketBalances.scaledPoolBorrowBalance(user); inP2P = marketBalances.scaledP2PBorrowBalance(user); + /// Idle borrow /// + + { + uint256 idleSupply = market.idleSupply; + if (idleSupply > 0) { + uint256 idleToMatch = Math.min(market.idleSupply, amount); // In underlying. + market.idleSupply -= idleToMatch; + amount -= idleToMatch; + } + } + /// Peer-to-peer borrow /// // Match the peer-to-peer supply delta. @@ -341,6 +352,16 @@ abstract contract PositionsManagerInternal is MatchingEngine { onPool = marketBalances.scaledPoolSupplyBalance(user); inP2P = marketBalances.scaledP2PSupplyBalance(user); + /// Idle Withdraw /// + { + uint256 idleSupply = market.idleSupply; + if (idleSupply > 0) { + uint256 idleToMatch = Math.min(market.idleSupply, amount); // In underlying. + market.idleSupply -= idleToMatch; + amount -= idleToMatch; + } + } + /// Pool withdraw /// // Withdraw supply on pool. From 8d7bd55cb907e4cc975edd918019be2ee5acf25f Mon Sep 17 00:00:00 2001 From: Patrick Kim Date: Sat, 7 Jan 2023 10:40:18 -0500 Subject: [PATCH 05/16] fix: handling supply cap --- src/PositionsManagerInternal.sol | 53 +++++++++++++------------------- 1 file changed, 21 insertions(+), 32 deletions(-) diff --git a/src/PositionsManagerInternal.sol b/src/PositionsManagerInternal.sol index cb7321a7f..3f4427102 100644 --- a/src/PositionsManagerInternal.sol +++ b/src/PositionsManagerInternal.sol @@ -112,9 +112,7 @@ abstract contract PositionsManagerInternal is MatchingEngine { // Supply on pool. if (amount > 0) { onPool += amount.rayDiv(indexes.supply.poolIndex); // In scaled balance. - uint256 toIdle; - (toSupply, toIdle) = _handleSupplyCap(underlying, amount); - market.idleSupply += toIdle; + toSupply = _handleSupplyCap(underlying, amount); } _updateSupplierInDS(underlying, user, onPool, inP2P); @@ -156,14 +154,7 @@ abstract contract PositionsManagerInternal is MatchingEngine { /// Idle borrow /// - { - uint256 idleSupply = market.idleSupply; - if (idleSupply > 0) { - uint256 idleToMatch = Math.min(market.idleSupply, amount); // In underlying. - market.idleSupply -= idleToMatch; - amount -= idleToMatch; - } - } + amount = _handleIdleSupply(underlying, amount); /// Peer-to-peer borrow /// @@ -307,9 +298,7 @@ abstract contract PositionsManagerInternal is MatchingEngine { deltas.p2pBorrowAmount -= Math.min(amount.rayDiv(indexes.borrow.p2pIndex), deltas.p2pBorrowAmount); emit Events.P2PAmountsUpdated(underlying, deltas.p2pSupplyAmount, deltas.p2pBorrowAmount); - uint256 toIdle; - (toSupply, toIdle) = _handleSupplyCap(underlying, amount); - market.idleSupply += toIdle; + toSupply = _handleSupplyCap(underlying, amount); } if (inP2P == 0 && onPool == 0) _userBorrows[user].remove(underlying); @@ -353,14 +342,7 @@ abstract contract PositionsManagerInternal is MatchingEngine { inP2P = marketBalances.scaledP2PSupplyBalance(user); /// Idle Withdraw /// - { - uint256 idleSupply = market.idleSupply; - if (idleSupply > 0) { - uint256 idleToMatch = Math.min(market.idleSupply, amount); // In underlying. - market.idleSupply -= idleToMatch; - amount -= idleToMatch; - } - } + amount = _handleIdleSupply(underlying, amount); /// Pool withdraw /// @@ -506,21 +488,28 @@ abstract contract PositionsManagerInternal is MatchingEngine { } } - function _handleSupplyCap(address underlying, uint256 amount) - internal - view - returns (uint256 toSupply, uint256 toIdle) - { + function _handleSupplyCap(address underlying, uint256 amount) internal returns (uint256 toSupply) { uint256 supplyCap = _POOL.getConfiguration(underlying).getSupplyCap(); - if (supplyCap == 0) return (amount, 0); + if (supplyCap == 0) return (amount); + uint256 totalSupply = ERC20(_market[underlying].aToken).totalSupply(); - if (totalSupply >= supplyCap) { - return (0, amount); - } else if (totalSupply + amount > supplyCap) { + if (totalSupply + amount > supplyCap) { + _market[underlying].idleSupply += totalSupply + amount - supplyCap; toSupply = supplyCap - totalSupply; - toIdle = amount - toSupply; } else { toSupply = amount; } } + + /// @return newAmount the new amount to process accounting for supply of tokens already in this contract. + function _handleIdleSupply(address underlying, uint256 amount) internal returns (uint256) { + Types.Market storage market = _market[underlying]; + uint256 idleSupply = market.idleSupply; + if (idleSupply > 0) { + uint256 idleToMatch = Math.min(idleSupply, amount); // In underlying. + market.idleSupply -= idleToMatch; + amount -= idleToMatch; + } + return amount; + } } From 1c7e937fe6cbfe02fbf00cbdb2c731d003264f43 Mon Sep 17 00:00:00 2001 From: Patrick Kim Date: Sat, 7 Jan 2023 12:03:50 -0500 Subject: [PATCH 06/16] fix: remove supply cap handling from supply --- src/PositionsManagerInternal.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/PositionsManagerInternal.sol b/src/PositionsManagerInternal.sol index 3f4427102..1ce187acb 100644 --- a/src/PositionsManagerInternal.sol +++ b/src/PositionsManagerInternal.sol @@ -112,7 +112,7 @@ abstract contract PositionsManagerInternal is MatchingEngine { // Supply on pool. if (amount > 0) { onPool += amount.rayDiv(indexes.supply.poolIndex); // In scaled balance. - toSupply = _handleSupplyCap(underlying, amount); + toSupply = amount; } _updateSupplierInDS(underlying, user, onPool, inP2P); @@ -298,6 +298,7 @@ abstract contract PositionsManagerInternal is MatchingEngine { deltas.p2pBorrowAmount -= Math.min(amount.rayDiv(indexes.borrow.p2pIndex), deltas.p2pBorrowAmount); emit Events.P2PAmountsUpdated(underlying, deltas.p2pSupplyAmount, deltas.p2pBorrowAmount); + /// note: Only used in breaking repay. Suppliers should not be able to supply if the pool is supply capped toSupply = _handleSupplyCap(underlying, amount); } From 6850fdcb0f32fe55805e9e6262e395d908a1dfb6 Mon Sep 17 00:00:00 2001 From: Patrick Kim Date: Sun, 8 Jan 2023 18:23:25 -0500 Subject: [PATCH 07/16] fix: proportion idle calculation --- src/MorphoInternal.sol | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/MorphoInternal.sol b/src/MorphoInternal.sol index 548f5d767..18d510e54 100644 --- a/src/MorphoInternal.sol +++ b/src/MorphoInternal.sol @@ -308,9 +308,8 @@ abstract contract MorphoInternal is MorphoStorage { if (idleSupply == 0) { return 0; } else { - uint256 totalSupplied = ERC20(market.aToken).balanceOf(address(this)).rayMul( - market.indexes.supply.poolIndex - ) + market.deltas.p2pSupplyAmount.rayMul(market.indexes.supply.p2pIndex); + uint256 totalSupplied = ERC20(market.aToken).balanceOf(address(this)) + + market.deltas.p2pSupplyAmount.rayMul(market.indexes.supply.p2pIndex); return idleSupply.rayDivUp(totalSupplied + idleSupply); } } From 0f314fc9cac4aa00bbf79343cdd7a4d9fddf8df3 Mon Sep 17 00:00:00 2001 From: Patrick Kim Date: Sun, 8 Jan 2023 20:27:16 -0500 Subject: [PATCH 08/16] fix: rework to a more correct implementation --- src/PositionsManagerInternal.sol | 34 +++++++++++++++--------------- src/libraries/InterestRatesLib.sol | 11 +++++----- 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/src/PositionsManagerInternal.sol b/src/PositionsManagerInternal.sol index 1ce187acb..c78fe02e6 100644 --- a/src/PositionsManagerInternal.sol +++ b/src/PositionsManagerInternal.sol @@ -154,8 +154,15 @@ abstract contract PositionsManagerInternal is MatchingEngine { /// Idle borrow /// - amount = _handleIdleSupply(underlying, amount); - + { + uint256 idleSupply = market.idleSupply; + if (idleSupply > 0) { + uint256 idleToMatch = Math.min(idleSupply, amount); // In underlying. + market.idleSupply -= idleToMatch; + amount -= idleToMatch; + inP2P += idleToMatch.rayDiv(indexes.borrow.p2pIndex); + } + } /// Peer-to-peer borrow /// // Match the peer-to-peer supply delta. @@ -342,9 +349,6 @@ abstract contract PositionsManagerInternal is MatchingEngine { onPool = marketBalances.scaledPoolSupplyBalance(user); inP2P = marketBalances.scaledP2PSupplyBalance(user); - /// Idle Withdraw /// - amount = _handleIdleSupply(underlying, amount); - /// Pool withdraw /// // Withdraw supply on pool. @@ -365,6 +369,14 @@ abstract contract PositionsManagerInternal is MatchingEngine { } inP2P -= Math.min(inP2P, amount.rayDiv(indexes.supply.p2pIndex)); // In peer-to-peer supply unit. + + /// Idle Withdraw /// + if (amount > 0 && market.idleSupply > 0 && inP2P > 0) { + uint256 matchedIdle = Math.min(Math.min(market.idleSupply, amount), inP2P.rayMul(indexes.supply.p2pIndex)); + market.idleSupply -= matchedIdle; + inP2P -= matchedIdle.rayDiv(indexes.supply.p2pIndex); + } + _updateSupplierInDS(underlying, user, onPool, inP2P); // Reduce the peer-to-peer supply delta. @@ -501,16 +513,4 @@ abstract contract PositionsManagerInternal is MatchingEngine { toSupply = amount; } } - - /// @return newAmount the new amount to process accounting for supply of tokens already in this contract. - function _handleIdleSupply(address underlying, uint256 amount) internal returns (uint256) { - Types.Market storage market = _market[underlying]; - uint256 idleSupply = market.idleSupply; - if (idleSupply > 0) { - uint256 idleToMatch = Math.min(idleSupply, amount); // In underlying. - market.idleSupply -= idleToMatch; - amount -= idleToMatch; - } - return amount; - } } diff --git a/src/libraries/InterestRatesLib.sol b/src/libraries/InterestRatesLib.sol index 344d850d8..9efe0cd5c 100644 --- a/src/libraries/InterestRatesLib.sol +++ b/src/libraries/InterestRatesLib.sol @@ -97,12 +97,13 @@ library InterestRatesLib { return lastIndexes.p2pIndex.rayMul(p2pGrowthFactor); } - uint256 shareOfTheDelta = Math.min( - p2pDelta.rayMul(lastIndexes.poolIndex).rayDivUp(p2pAmount.rayMul(lastIndexes.p2pIndex)) + proportionIdle, - WadRayMath.RAY // To avoid shareOfTheDelta > 1 with rounding errors. + uint256 proportionDelta = Math.min( + p2pDelta.rayMul(lastIndexes.poolIndex).rayDivUp(p2pAmount.rayMul(lastIndexes.p2pIndex)), + WadRayMath.RAY - proportionIdle // To avoid shareOfTheDelta + proportionIdle > 1 with rounding errors. ); // In ray. - return - lastIndexes.p2pIndex.rayMul(WadRayMath.rayWeightedAvg(p2pGrowthFactor, poolGrowthFactor, shareOfTheDelta)); + return lastIndexes.p2pIndex.rayMul( + WadRayMath.rayWeightedAvg(p2pGrowthFactor, poolGrowthFactor, proportionDelta + proportionIdle) + ) + p2pGrowthFactor.rayMul(proportionDelta); } } From 3a822c9ba7166b85f0eafb47c3d4f4971d534e8d Mon Sep 17 00:00:00 2001 From: Patrick Kim Date: Tue, 10 Jan 2023 11:50:10 -0500 Subject: [PATCH 09/16] fix: supply cap formulas --- src/MorphoInternal.sol | 5 ++--- src/PositionsManagerInternal.sol | 2 +- src/libraries/InterestRatesLib.sol | 5 +++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/MorphoInternal.sol b/src/MorphoInternal.sol index 18d510e54..ca4b343d5 100644 --- a/src/MorphoInternal.sol +++ b/src/MorphoInternal.sol @@ -308,9 +308,8 @@ abstract contract MorphoInternal is MorphoStorage { if (idleSupply == 0) { return 0; } else { - uint256 totalSupplied = ERC20(market.aToken).balanceOf(address(this)) - + market.deltas.p2pSupplyAmount.rayMul(market.indexes.supply.p2pIndex); - return idleSupply.rayDivUp(totalSupplied + idleSupply); + uint256 totalP2PSupplied = market.deltas.p2pSupplyAmount.rayMulDown(market.indexes.supply.p2pIndex); + return idleSupply.rayDivUp(totalP2PSupplied + idleSupply); } } } diff --git a/src/PositionsManagerInternal.sol b/src/PositionsManagerInternal.sol index c78fe02e6..d020ca4a1 100644 --- a/src/PositionsManagerInternal.sol +++ b/src/PositionsManagerInternal.sol @@ -503,7 +503,7 @@ abstract contract PositionsManagerInternal is MatchingEngine { function _handleSupplyCap(address underlying, uint256 amount) internal returns (uint256 toSupply) { uint256 supplyCap = _POOL.getConfiguration(underlying).getSupplyCap(); - if (supplyCap == 0) return (amount); + if (supplyCap == 0) return amount; uint256 totalSupply = ERC20(_market[underlying].aToken).totalSupply(); if (totalSupply + amount > supplyCap) { diff --git a/src/libraries/InterestRatesLib.sol b/src/libraries/InterestRatesLib.sol index 9efe0cd5c..1eb4bdee6 100644 --- a/src/libraries/InterestRatesLib.sol +++ b/src/libraries/InterestRatesLib.sol @@ -103,7 +103,8 @@ library InterestRatesLib { ); // In ray. return lastIndexes.p2pIndex.rayMul( - WadRayMath.rayWeightedAvg(p2pGrowthFactor, poolGrowthFactor, proportionDelta + proportionIdle) - ) + p2pGrowthFactor.rayMul(proportionDelta); + p2pGrowthFactor.rayMul(WadRayMath.RAY - proportionDelta - proportionIdle) + + poolGrowthFactor.rayMul(proportionDelta) + proportionIdle + ); } } From c50b6dde0c9c10d457eb92bc80e66957f68a95bf Mon Sep 17 00:00:00 2001 From: Patrick Kim Date: Thu, 12 Jan 2023 10:57:05 -0500 Subject: [PATCH 10/16] fix: supply cap formulas --- src/MorphoInternal.sol | 2 +- src/PositionsManagerInternal.sol | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/MorphoInternal.sol b/src/MorphoInternal.sol index 7b0c319d5..1061da2e5 100644 --- a/src/MorphoInternal.sol +++ b/src/MorphoInternal.sol @@ -340,7 +340,7 @@ abstract contract MorphoInternal is MorphoStorage { return 0; } else { uint256 totalP2PSupplied = market.deltas.p2pSupplyAmount.rayMulDown(market.indexes.supply.p2pIndex); - return idleSupply.rayDivUp(totalP2PSupplied + idleSupply); + return idleSupply.rayDivUp(totalP2PSupplied); } } } diff --git a/src/PositionsManagerInternal.sol b/src/PositionsManagerInternal.sol index 7b26ae386..a460eb298 100644 --- a/src/PositionsManagerInternal.sol +++ b/src/PositionsManagerInternal.sol @@ -516,8 +516,8 @@ abstract contract PositionsManagerInternal is MatchingEngine { uint256 totalSupply = ERC20(_market[underlying].aToken).totalSupply(); if (totalSupply + amount > supplyCap) { - _market[underlying].idleSupply += totalSupply + amount - supplyCap; toSupply = supplyCap - totalSupply; + _market[underlying].idleSupply += amount - toSupply; } else { toSupply = amount; } From 87e851862b2d5907a7d9b43d1d27410180b2284a Mon Sep 17 00:00:00 2001 From: Patrick Kim Date: Thu, 12 Jan 2023 12:43:34 -0500 Subject: [PATCH 11/16] fix: use decimals in supply cap and fix comment in interest rates lib --- src/PositionsManagerInternal.sol | 3 ++- src/libraries/InterestRatesLib.sol | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/PositionsManagerInternal.sol b/src/PositionsManagerInternal.sol index a460eb298..53c77d0ea 100644 --- a/src/PositionsManagerInternal.sol +++ b/src/PositionsManagerInternal.sol @@ -511,7 +511,8 @@ abstract contract PositionsManagerInternal is MatchingEngine { } function _handleSupplyCap(address underlying, uint256 amount) internal returns (uint256 toSupply) { - uint256 supplyCap = _POOL.getConfiguration(underlying).getSupplyCap(); + DataTypes.ReserveConfigurationMap memory config = _POOL.getConfiguration(underlying); + uint256 supplyCap = config.getSupplyCap() * (10 ** config.getDecimals()); if (supplyCap == 0) return amount; uint256 totalSupply = ERC20(_market[underlying].aToken).totalSupply(); diff --git a/src/libraries/InterestRatesLib.sol b/src/libraries/InterestRatesLib.sol index 1eb4bdee6..f257d1596 100644 --- a/src/libraries/InterestRatesLib.sol +++ b/src/libraries/InterestRatesLib.sol @@ -99,7 +99,7 @@ library InterestRatesLib { uint256 proportionDelta = Math.min( p2pDelta.rayMul(lastIndexes.poolIndex).rayDivUp(p2pAmount.rayMul(lastIndexes.p2pIndex)), - WadRayMath.RAY - proportionIdle // To avoid shareOfTheDelta + proportionIdle > 1 with rounding errors. + WadRayMath.RAY - proportionIdle // To avoid proportionDelta + proportionIdle > 1 with rounding errors. ); // In ray. return lastIndexes.p2pIndex.rayMul( From 0cb5fb7667e54228d7fa35e19a861e29a7dbb1c3 Mon Sep 17 00:00:00 2001 From: Patrick Kim Date: Thu, 12 Jan 2023 16:27:27 -0500 Subject: [PATCH 12/16] chore: change erc20 import and refine comment --- src/MorphoInternal.sol | 2 +- src/PositionsManagerInternal.sol | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/MorphoInternal.sol b/src/MorphoInternal.sol index 1061da2e5..8d356ba59 100644 --- a/src/MorphoInternal.sol +++ b/src/MorphoInternal.sol @@ -28,7 +28,7 @@ import {ReserveConfiguration} from "./libraries/aave/ReserveConfiguration.sol"; import {MorphoStorage} from "./MorphoStorage.sol"; -import {ERC20} from "@solmate/utils/SafeTransferLib.sol"; +import {ERC20} from "@solmate/tokens/ERC20.sol"; abstract contract MorphoInternal is MorphoStorage { using PoolLib for IPool; diff --git a/src/PositionsManagerInternal.sol b/src/PositionsManagerInternal.sol index 53c77d0ea..cf9280674 100644 --- a/src/PositionsManagerInternal.sol +++ b/src/PositionsManagerInternal.sol @@ -22,7 +22,7 @@ import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet import {MatchingEngine} from "./MatchingEngine.sol"; -import {ERC20} from "@solmate/utils/SafeTransferLib.sol"; +import {ERC20} from "@solmate/tokens/ERC20.sol"; abstract contract PositionsManagerInternal is MatchingEngine { using Math for uint256; @@ -381,7 +381,7 @@ abstract contract PositionsManagerInternal is MatchingEngine { deltas.p2pBorrowAmount -= Math.min(amount.rayDiv(indexes.borrow.p2pIndex), deltas.p2pBorrowAmount); emit Events.P2PAmountsUpdated(underlying, deltas.p2pSupplyAmount, deltas.p2pBorrowAmount); - /// note: Only used in breaking repay. Suppliers should not be able to supply if the pool is supply capped + /// Note: Only used in breaking repay. Suppliers should not be able to supply if the pool is supply capped. toSupply = _handleSupplyCap(underlying, amount); } From 2a85a248aaad3c55998054ebab2f025f05506369 Mon Sep 17 00:00:00 2001 From: Patrick Kim Date: Fri, 13 Jan 2023 11:10:02 -0500 Subject: [PATCH 13/16] fix: change rounding, change a control struct format, add comments --- src/MorphoInternal.sol | 5 ++--- src/PositionsManagerInternal.sol | 10 ++++++---- src/libraries/InterestRatesLib.sol | 2 ++ 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/MorphoInternal.sol b/src/MorphoInternal.sol index 8d356ba59..ffd8009be 100644 --- a/src/MorphoInternal.sol +++ b/src/MorphoInternal.sol @@ -338,9 +338,8 @@ abstract contract MorphoInternal is MorphoStorage { uint256 idleSupply = market.idleSupply; if (idleSupply == 0) { return 0; - } else { - uint256 totalP2PSupplied = market.deltas.p2pSupplyAmount.rayMulDown(market.indexes.supply.p2pIndex); - return idleSupply.rayDivUp(totalP2PSupplied); } + uint256 totalP2PSupplied = market.deltas.p2pSupplyAmount.rayMul(market.indexes.supply.p2pIndex); + return idleSupply.rayDivUp(totalP2PSupplied); } } diff --git a/src/PositionsManagerInternal.sol b/src/PositionsManagerInternal.sol index cf9280674..03ad0f3fc 100644 --- a/src/PositionsManagerInternal.sol +++ b/src/PositionsManagerInternal.sol @@ -238,12 +238,13 @@ abstract contract PositionsManagerInternal is MatchingEngine { { uint256 idleSupply = market.idleSupply; if (idleSupply > 0) { - uint256 idleToMatch = Math.min(idleSupply, amount); // In underlying. - market.idleSupply -= idleToMatch; - amount -= idleToMatch; - vars.inP2P += idleToMatch.rayDiv(indexes.borrow.p2pIndex); + uint256 matchedIdle = Math.min(idleSupply, amount); // In underlying. + market.idleSupply -= matchedIdle; + amount -= matchedIdle; + vars.inP2P += matchedIdle.rayDiv(indexes.borrow.p2pIndex); } } + /// Peer-to-peer borrow /// // Match the peer-to-peer supply delta. @@ -424,6 +425,7 @@ abstract contract PositionsManagerInternal is MatchingEngine { vars.inP2P -= Math.min(vars.inP2P, amount.rayDiv(indexes.supply.p2pIndex)); // In peer-to-peer supply unit. /// Idle Withdraw /// + if (amount > 0 && market.idleSupply > 0 && vars.inP2P > 0) { uint256 matchedIdle = Math.min(Math.min(market.idleSupply, amount), vars.inP2P.rayMul(indexes.supply.p2pIndex)); diff --git a/src/libraries/InterestRatesLib.sol b/src/libraries/InterestRatesLib.sol index f257d1596..a3327238b 100644 --- a/src/libraries/InterestRatesLib.sol +++ b/src/libraries/InterestRatesLib.sol @@ -102,6 +102,8 @@ library InterestRatesLib { WadRayMath.RAY - proportionIdle // To avoid proportionDelta + proportionIdle > 1 with rounding errors. ); // In ray. + // Equivalent to: + // lastP2PIndex * (p2pGrowthFactor * (1 - proportionDelta - proportionIdle) + poolGrowthFactor * proportionDelta + idleGrowthFactor * proportionIdle) return lastIndexes.p2pIndex.rayMul( p2pGrowthFactor.rayMul(WadRayMath.RAY - proportionDelta - proportionIdle) + poolGrowthFactor.rayMul(proportionDelta) + proportionIdle From 570af385e965242220d073b6ae1309ed5dc7b7fd Mon Sep 17 00:00:00 2001 From: Patrick Kim Date: Fri, 13 Jan 2023 12:41:50 -0500 Subject: [PATCH 14/16] fix: remove unnecessary subtraction --- src/PositionsManagerInternal.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/src/PositionsManagerInternal.sol b/src/PositionsManagerInternal.sol index 03ad0f3fc..3efeeb17c 100644 --- a/src/PositionsManagerInternal.sol +++ b/src/PositionsManagerInternal.sol @@ -430,7 +430,6 @@ abstract contract PositionsManagerInternal is MatchingEngine { uint256 matchedIdle = Math.min(Math.min(market.idleSupply, amount), vars.inP2P.rayMul(indexes.supply.p2pIndex)); market.idleSupply -= matchedIdle; - vars.inP2P -= matchedIdle.rayDiv(indexes.supply.p2pIndex); } _updateSupplierInDS(underlying, user, vars.onPool, vars.inP2P); From 090c7d07f6a139f6cdc0e9bc9d45e74422593205 Mon Sep 17 00:00:00 2001 From: Patrick Kim Date: Fri, 13 Jan 2023 12:45:00 -0500 Subject: [PATCH 15/16] chore: format interest rate comment --- src/libraries/InterestRatesLib.sol | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/libraries/InterestRatesLib.sol b/src/libraries/InterestRatesLib.sol index a3327238b..8074a43e9 100644 --- a/src/libraries/InterestRatesLib.sol +++ b/src/libraries/InterestRatesLib.sol @@ -103,7 +103,11 @@ library InterestRatesLib { ); // In ray. // Equivalent to: - // lastP2PIndex * (p2pGrowthFactor * (1 - proportionDelta - proportionIdle) + poolGrowthFactor * proportionDelta + idleGrowthFactor * proportionIdle) + // lastP2PIndex * ( + // p2pGrowthFactor * + // (1 - proportionDelta - proportionIdle) + // + poolGrowthFactor * proportionDelta + idleGrowthFactor * proportionIdle + // ) return lastIndexes.p2pIndex.rayMul( p2pGrowthFactor.rayMul(WadRayMath.RAY - proportionDelta - proportionIdle) + poolGrowthFactor.rayMul(proportionDelta) + proportionIdle From df46046565cb417ad5a3a2df5c802bd33b0a7a9a Mon Sep 17 00:00:00 2001 From: Patrick Kim Date: Fri, 13 Jan 2023 12:46:36 -0500 Subject: [PATCH 16/16] chore: reformat ineterest rate lib comment further --- src/libraries/InterestRatesLib.sol | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/libraries/InterestRatesLib.sol b/src/libraries/InterestRatesLib.sol index 8074a43e9..b7346a290 100644 --- a/src/libraries/InterestRatesLib.sol +++ b/src/libraries/InterestRatesLib.sol @@ -104,10 +104,9 @@ library InterestRatesLib { // Equivalent to: // lastP2PIndex * ( - // p2pGrowthFactor * - // (1 - proportionDelta - proportionIdle) - // + poolGrowthFactor * proportionDelta + idleGrowthFactor * proportionIdle - // ) + // p2pGrowthFactor * (1 - proportionDelta - proportionIdle) + + // poolGrowthFactor * proportionDelta + + // idleGrowthFactor * proportionIdle) return lastIndexes.p2pIndex.rayMul( p2pGrowthFactor.rayMul(WadRayMath.RAY - proportionDelta - proportionIdle) + poolGrowthFactor.rayMul(proportionDelta) + proportionIdle