Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix rewards accrual #860

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 37 additions & 46 deletions src/MorphoInternal.sol
Original file line number Diff line number Diff line change
Expand Up @@ -356,59 +356,39 @@ abstract contract MorphoInternal is MorphoStorage {
}
}

/// @dev Updates a `user`'s position in the data structure.
/// @param poolToken The address of the pool token related to this market (aToken or variable debt token address).
/// @dev Updates a `user`'s supply position in the data structure.
/// @param underlying The address of the underlying asset.
/// @param user The address of the user to update.
/// @param poolBuckets The pool buckets.
/// @param p2pBuckets The peer-to-peer buckets.
/// @param onPool The new scaled balance on pool of the `user`.
/// @param inP2P The new scaled balance in peer-to-peer of the `user`.
/// @param demoting Whether the update is happening during a demoting process or not.
/// @return The actual new scaled balance on pool and in peer-to-peer of the `user` after accounting for dust.
function _updateInDS(
address poolToken,
address user,
LogarithmicBuckets.Buckets storage poolBuckets,
LogarithmicBuckets.Buckets storage p2pBuckets,
uint256 onPool,
uint256 inP2P,
bool demoting
) internal returns (uint256, uint256) {
function _updateSupplierInDS(address underlying, address user, uint256 onPool, uint256 inP2P, bool demoting)
internal
returns (uint256, uint256)
{
if (onPool <= Constants.DUST_THRESHOLD) onPool = 0;
if (inP2P <= Constants.DUST_THRESHOLD) inP2P = 0;

Types.MarketBalances storage marketBalances = _marketBalances[underlying];

LogarithmicBuckets.Buckets storage poolBuckets = marketBalances.poolSuppliers;
uint256 formerOnPool = poolBuckets.valueOf[user];
uint256 formerInP2P = p2pBuckets.valueOf[user];

if (onPool != formerOnPool) {
_updateRewards(user, poolToken, formerOnPool);
_updateRewards(
user, _market[underlying].aToken, formerOnPool + marketBalances.scaledCollateralBalance(user)
);

poolBuckets.update(user, onPool, demoting);
}

LogarithmicBuckets.Buckets storage p2pBuckets = marketBalances.p2pSuppliers;
uint256 formerInP2P = p2pBuckets.valueOf[user];

if (inP2P != formerInP2P) p2pBuckets.update(user, inP2P, true);
return (onPool, inP2P);
}

/// @dev Updates a `user`'s supply position in the data structure.
/// @param underlying The address of the underlying asset.
/// @param user The address of the user to update.
/// @param onPool The new scaled balance on pool of the `user`.
/// @param inP2P The new scaled balance in peer-to-peer of the `user`.
/// @param demoting Whether the update is happening during a demoting process or not.
/// @return The actual new scaled balance on pool and in peer-to-peer of the `user` after accounting for dust.
function _updateSupplierInDS(address underlying, address user, uint256 onPool, uint256 inP2P, bool demoting)
internal
returns (uint256, uint256)
{
return _updateInDS(
_market[underlying].aToken,
user,
_marketBalances[underlying].poolSuppliers,
_marketBalances[underlying].p2pSuppliers,
onPool,
inP2P,
demoting
);
return (onPool, inP2P);
// No need to update the user's list of supplied assets,
// as it cannot be used as collateral and thus there's no need to iterate over it.
}
Expand All @@ -424,17 +404,28 @@ abstract contract MorphoInternal is MorphoStorage {
internal
returns (uint256, uint256)
{
(onPool, inP2P) = _updateInDS(
_market[underlying].variableDebtToken,
user,
_marketBalances[underlying].poolBorrowers,
_marketBalances[underlying].p2pBorrowers,
onPool,
inP2P,
demoting
);
if (onPool <= Constants.DUST_THRESHOLD) onPool = 0;
if (inP2P <= Constants.DUST_THRESHOLD) inP2P = 0;

Types.MarketBalances storage marketBalances = _marketBalances[underlying];

LogarithmicBuckets.Buckets storage poolBuckets = marketBalances.poolBorrowers;
uint256 formerOnPool = poolBuckets.valueOf[user];

if (onPool != formerOnPool) {
_updateRewards(user, _market[underlying].variableDebtToken, formerOnPool);

poolBuckets.update(user, onPool, demoting);
}

LogarithmicBuckets.Buckets storage p2pBuckets = marketBalances.p2pBorrowers;
uint256 formerInP2P = p2pBuckets.valueOf[user];

if (inP2P != formerInP2P) p2pBuckets.update(user, inP2P, true);

if (onPool == 0 && inP2P == 0) _userBorrows[user].remove(underlying);
else _userBorrows[user].add(underlying);

return (onPool, inP2P);
}

Expand Down
8 changes: 6 additions & 2 deletions src/PositionsManagerInternal.sol
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,9 @@ abstract contract PositionsManagerInternal is MatchingEngine {

collateralBalance = marketBalances.collateral[onBehalf];

_updateRewards(onBehalf, _market[underlying].aToken, collateralBalance);
_updateRewards(
onBehalf, _market[underlying].aToken, collateralBalance + marketBalances.scaledPoolSupplyBalance(onBehalf)
Tristan22400 marked this conversation as resolved.
Show resolved Hide resolved
);

collateralBalance += amount.rayDivDown(poolSupplyIndex);

Expand All @@ -434,7 +436,9 @@ abstract contract PositionsManagerInternal is MatchingEngine {

collateralBalance = marketBalances.collateral[onBehalf];

_updateRewards(onBehalf, _market[underlying].aToken, collateralBalance);
_updateRewards(
onBehalf, _market[underlying].aToken, collateralBalance + marketBalances.scaledPoolSupplyBalance(onBehalf)
);

collateralBalance = collateralBalance.zeroFloorSub(amount.rayDivUp(poolSupplyIndex));

Expand Down
23 changes: 23 additions & 0 deletions test/integration/TestIntegrationRewardsManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -444,4 +444,27 @@ contract TestIntegrationRewardsManager is IntegrationTest {
assertEq(amounts[0], accruedRewardCollateral + accruedRewardBorrow, "amount 1");
assertGt(amounts[0], 0, "amount min 1");
}

function testShouldAccrueSupplyAndCollateralRewards() public {
uint256 amount = 1 ether;

user.approve(dai, amount);
user.supplyCollateral(dai, amount);

_forward(1);

address[] memory aDaiArray = new address[](1);
aDaiArray[0] = aDai;

uint256 accruedRewardsBefore = rewardsManager.getUserRewards(aDaiArray, address(user), wNative);

user.approve(dai, amount);
user.supply(dai, amount);

_forward(1);

uint256 accruedRewardsAfter = rewardsManager.getUserRewards(aDaiArray, address(user), wNative);

assertApproxEqAbs(accruedRewardsAfter, accruedRewardsBefore * 3, 1e5);
}
}
2 changes: 1 addition & 1 deletion test/integration/TestIntegrationWithdraw.sol
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ contract TestIntegrationWithdraw is IntegrationTest {

// Assert Morpho's position on pool.
assertApproxEqAbs(
market.supplyOf(address(morpho)), test.morphoSupplyBefore, 1, "morphoSupply != morphoSupplyBefore"
market.supplyOf(address(morpho)), test.morphoSupplyBefore, 2, "morphoSupply != morphoSupplyBefore"
);
assertApproxEqAbs(market.variableBorrowOf(address(morpho)), 0, 2, "morphoVariableBorrow != 0");
assertEq(market.stableBorrowOf(address(morpho)), 0, "morphoStableBorrow != 0");
Expand Down
49 changes: 0 additions & 49 deletions test/internal/TestInternalMorphoInternal.sol
Original file line number Diff line number Diff line change
Expand Up @@ -227,55 +227,6 @@ contract TestInternalMorphoInternal is InternalTest {
assertEq(marketBalances.scaledCollateralBalance(user), scaledCollateral, "scaledCollateral");
}

function testUpdateInDSWithSuppliers(address user, uint256 onPool, uint256 inP2P, bool head) public {
user = _boundAddressNotZero(user);
onPool = bound(onPool, Constants.DUST_THRESHOLD + 1, type(uint96).max);
inP2P = bound(inP2P, Constants.DUST_THRESHOLD + 1, type(uint96).max);

Types.MarketBalances storage marketBalances = _marketBalances[dai];
(uint256 newOnPool, uint256 newInP2P) = _updateInDS(
address(0), user, marketBalances.poolSuppliers, marketBalances.p2pSuppliers, onPool, inP2P, head
);
assertEq(newOnPool, onPool);
assertEq(newInP2P, inP2P);
_assertMarketBalances(marketBalances, user, onPool, inP2P, 0, 0, 0);
}

function testUpdateInDSWithBorrowers(address user, uint256 onPool, uint256 inP2P, bool head) public {
user = _boundAddressNotZero(user);
onPool = bound(onPool, Constants.DUST_THRESHOLD + 1, type(uint96).max);
inP2P = bound(inP2P, Constants.DUST_THRESHOLD + 1, type(uint96).max);

Types.MarketBalances storage marketBalances = _marketBalances[dai];
(uint256 newOnPool, uint256 newInP2P) = _updateInDS(
address(0), user, marketBalances.poolBorrowers, marketBalances.p2pBorrowers, onPool, inP2P, head
);
assertEq(newOnPool, onPool);
assertEq(newInP2P, inP2P);
_assertMarketBalances(marketBalances, user, 0, 0, onPool, inP2P, 0);
}

function testUpdateInDSWithDust(address user, uint256 onPool, uint256 inP2P, bool head) public {
user = _boundAddressNotZero(user);
onPool = bound(onPool, 0, Constants.DUST_THRESHOLD);
inP2P = bound(inP2P, 0, Constants.DUST_THRESHOLD);

Types.MarketBalances storage marketBalances = _marketBalances[dai];
(uint256 newOnPool, uint256 newInP2P) = _updateInDS(
address(0), user, marketBalances.poolSuppliers, marketBalances.p2pSuppliers, onPool, inP2P, head
);
_assertMarketBalances(marketBalances, user, 0, 0, 0, 0, 0);
assertEq(newOnPool, 0);
assertEq(newInP2P, 0);

(newOnPool, newInP2P) = _updateInDS(
address(0), user, marketBalances.poolBorrowers, marketBalances.p2pBorrowers, onPool, inP2P, head
);
assertEq(newOnPool, 0);
assertEq(newInP2P, 0);
_assertMarketBalances(marketBalances, user, 0, 0, 0, 0, 0);
}

function testUpdateSupplierInDS(address user, uint256 onPool, uint256 inP2P, bool head) public {
user = _boundAddressNotZero(user);
onPool = bound(onPool, Constants.DUST_THRESHOLD + 1, type(uint96).max);
Expand Down