You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository has been archived by the owner on Nov 3, 2024. It is now read-only.
DuplicateA valid issue that is a duplicate of an issue with `Has Duplicates` labelMediumA valid Medium severity issueRewardA payout will be made for this issue
clearBadDebt function does not accrue earnings from each maturity.
Summary
During the operation of a maturity, Pool.accrueEarnings() is triggered to transfer the backupEarnings from unassignedEarnings of the maturity to the floating pool, which is dripped over time. However, the clearBadDebt() function does not trigger Pool.accrueEarnings() to collect earnings for the backup supply. This results in a loss of the remaining earnings if a maturity ends with a clearBadDebt() call.
Vulnerability Detail
In the FixedLib library, the accrueEarnings() function is used to collect backup earnings from a specific maturity (fixed pool) to the floating pool. These earnings are dripped from the unassignedEarnings of the maturity over time. Pool.accrueEarnings() is called whenever an operation of the maturity (such as deposit, withdrawal, borrowing, or repayment) occurs https://github.com/sherlock-audit/2024-04-interest-rate-model/blob/main/protocol/contracts/utils/FixedLib.sol#L84-L99
Market.clearBadDebt() is a function called by Auditor.handleBadDebt() to clear all the debt of a borrower when this borrower has no collateral. It clears all debt from each maturity that this account has borrowed from. However, it does not trigger accrueEarnings() for each fixed pool.
An issue will occur when a maturity ends and clearBadDebt() is the last operation of this maturity, but the unassignedEarnings() of that maturity have not been fully accrued. This means that although the earnings have been completely dripped because the maturity has ended, they will never be collected into the floating pool because clearBadDebt() does not trigger Pool.accrueEarnings() for that maturity.
Therefore, in this case, floating assets will incur a loss of earnings from that maturity, since the remaining unassignedEarnings of this maturity will still be greater than 0 but will never be accrued. There is no mitigation to claim it since there is no debt remaining in this maturity.
Here is the test function for a PoC:
function testClearBadDebtLastOperation() external {
//setup market
marketWETH.setMaxFuturePools(3);
irm = MockInterestRateModel(address(new MockBorrowRate(0.1e18)));
market.setInterestRateModel(InterestRateModel(address(irm)));
marketWETH.setInterestRateModel(InterestRateModel(address(irm)));
//deposit collateral and borrow at high price
daiPriceFeed.setPrice(50_000_000_000_000e18);
market.deposit(0.0000000001 ether, address(this));
auditor.enterMarket(market);
marketWETH.deposit(1_000 ether, BOB);
marketWETH.borrowAtMaturity(FixedLib.INTERVAL, 10 ether, type(uint256).max, address(this), address(this));
(, , uint256 accruedEarnings, ) = marketWETH.fixedPools(FixedLib.INTERVAL);
console.log(accruedEarnings);
//maturity end
vm.warp(FixedLib.INTERVAL + 1);
//collateral value downs to 0 and bad debt happens
daiPriceFeed.setPrice(1);
auditor.handleBadDebt(address(this));
(, , accruedEarnings, ) = marketWETH.fixedPools(FixedLib.INTERVAL);
console.log(accruedEarnings);
assertGt(accruedEarnings, 0);
}
Please put this function into Market.t.sol test file and run the command:
forge test -vv --match-test testClearBadDebtLastOperation
Impact
When clearBadDebt() is the last operation of a maturity after it ends, the remaining unassignedEarnings in this maturity will never be accrued. Therefore, the floating pool will lose significant funds accrued from maturities
sherlock-admin3
changed the title
Tiny Mulberry Tapir - clearBadDebt function does not accrue earnings from each maturity.
Trumpero - clearBadDebt function does not accrue earnings from each maturity.
May 17, 2024
Sign up for freeto subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Labels
DuplicateA valid issue that is a duplicate of an issue with `Has Duplicates` labelMediumA valid Medium severity issueRewardA payout will be made for this issue
Trumpero
medium
clearBadDebt
function does not accrue earnings from each maturity.Summary
During the operation of a maturity,
Pool.accrueEarnings()
is triggered to transfer thebackupEarnings
fromunassignedEarnings
of the maturity to the floating pool, which is dripped over time. However, theclearBadDebt()
function does not triggerPool.accrueEarnings()
to collect earnings for the backup supply. This results in a loss of the remaining earnings if a maturity ends with aclearBadDebt()
call.Vulnerability Detail
In the FixedLib library, the
accrueEarnings()
function is used to collect backup earnings from a specific maturity (fixed pool) to the floating pool. These earnings are dripped from theunassignedEarnings
of the maturity over time.Pool.accrueEarnings()
is called whenever an operation of the maturity (such as deposit, withdrawal, borrowing, or repayment) occurshttps://github.com/sherlock-audit/2024-04-interest-rate-model/blob/main/protocol/contracts/utils/FixedLib.sol#L84-L99
Market.clearBadDebt()
is a function called byAuditor.handleBadDebt()
to clear all the debt of a borrower when this borrower has no collateral. It clears all debt from each maturity that this account has borrowed from. However, it does not triggeraccrueEarnings()
for each fixed pool.An issue will occur when a maturity ends and
clearBadDebt()
is the last operation of this maturity, but theunassignedEarnings()
of that maturity have not been fully accrued. This means that although the earnings have been completely dripped because the maturity has ended, they will never be collected into the floating pool becauseclearBadDebt()
does not triggerPool.accrueEarnings()
for that maturity.Therefore, in this case, floating assets will incur a loss of earnings from that maturity, since the remaining
unassignedEarnings
of this maturity will still be greater than 0 but will never be accrued. There is no mitigation to claim it since there is no debt remaining in this maturity.Here is the test function for a PoC:
Please put this function into
Market.t.sol
test file and run the command:Impact
When
clearBadDebt()
is the last operation of a maturity after it ends, the remainingunassignedEarnings
in this maturity will never be accrued. Therefore, the floating pool will lose significant funds accrued from maturitiesCode Snippet
https://github.com/sherlock-audit/2024-04-interest-rate-model/blob/main/protocol/contracts/Market.sol#L629-L644
Tool used
Manual Review
Recommendation
The
clearBadDebt()
function should triggerPool.accrueEarnings()
for each maturity when clearing debt as follows:Duplicate of #130
The text was updated successfully, but these errors were encountered: