Platform: Code4rena
Start Date: 11/12/2023
Pot Size: $90,500 USDC
Total HM: 29
Participants: 127
Period: 17 days
Judge: TrungOre
Total Solo HM: 4
Id: 310
League: ETH
Rank: 47/127
Findings: 2
Award: $289.20
🌟 Selected for report: 0
🚀 Solo Findings: 0
286.1479 USDC - $286.15
https://github.com/code-423n4/2023-12-ethereumcreditguild/blob/main/src/loan/LendingTerm.sol#L766
The protocol may incur bad debt than necessary, since it may not always be feasible to achieve the optimal arrangement for ECG, which involves repaying as much of the entire called loan as possible, due to a failing contract in certain conditions.
Called loans in the LendingTerm contract undergo auctioning in the AuctionHouse contract. The outstanding debt is calculated prior to submission to the AuctionHouse.
Upon completion of a bid in an auction, the auction concludes. If the bid occurs within the auction period where the requested "callDept" remains constant but the offered collateral increases, the bid may not be settled if there have been additional badDept events since the start of the auction. This is due to the recalculation of the principal in the onBid function, leading to the contract failure in line 766, as the collateralToBorrower would not be zero in this scenario.
The loanDebt calculated in the getLoanDebt function when starting an auction is determined by:
uint256 borrowAmount = loan.borrowAmount; uint256 interest = (borrowAmount * params.interestRate * (block.timestamp - borrowTime)) / YEAR / 1e18; uint256 loanDebt = borrowAmount + interest; uint256 _openingFee = params.openingFee; if (_openingFee != 0) { loanDebt += (borrowAmount * _openingFee) / 1e18; } uint256 creditMultiplier = ProfitManager(refs.profitManager) .creditMultiplier(); loanDebt = (loanDebt * loan.borrowCreditMultiplier) / creditMultiplier;
The principal in onBid is calculated as:
uint256 creditMultiplier = ProfitManager(refs.profitManager) .creditMultiplier(); uint256 borrowAmount = loans[loanId].borrowAmount; uint256 principal = (borrowAmount * loans[loanId].borrowCreditMultiplier) / creditMultiplier;
Therefore, the case 'creditFromBidder < principal' in line 761 occurs when the "creditMultiplier," responsible for accounting for bad debt, sets off the interests and openingFee. This happens when the creditMultiplier decreases by the formula (InterestFee * creditMultiplier_at_auction_start) / (borrowAmount + InterestFee), where InterestFee is calculated as (borrowAmount * _openFee + interest).
None. Manual review
Consider reflecting such a case where 'creditFromBidder < principal' by setting collateralToBorrower value to zero instead failing, if the value is not zero.
Other
#0 - c4-pre-sort
2024-01-05T16:45:06Z
0xSorryNotSorry marked the issue as sufficient quality report
#1 - c4-pre-sort
2024-01-05T16:45:21Z
0xSorryNotSorry marked the issue as duplicate of #1069
#2 - c4-judge
2024-01-29T19:47:46Z
Trumpero marked the issue as satisfactory
#3 - c4-judge
2024-01-31T13:44:24Z
Trumpero changed the severity to 3 (High Risk)
🌟 Selected for report: JCN
Also found by: 0xadrii, 0xaltego, 0xdice91, 0xivas, 0xpiken, Akali, AlexCzm, Chinmay, DanielArmstrong, HighDuty, Infect3d, Inference, KupiaSec, PENGUN, SECURITISE, Stormreckson, SweetDream, TheSchnilch, Timeless, Varun_05, XDZIBECX, alexzoid, asui, beber89, btk, carrotsmuggler, cats, cccz, developerjordy, ether_sky, grearlake, imare, jasonxiale, kaden, klau5, santipu_, serial-coder, sl1, smiling_heretic, stackachu, wangxx2026, whitehat-boys
3.0466 USDC - $3.05
The user loses their staked CREDITs when they start staking in a LendingTerm that has previously incurred a loss, as the tokens are written off within the SurPlusGuildMinter contract.
The getRewards function in the SurplusGuildMinter contract initializes an empty UserStake object within the definition of the function's return values.
Thus, in L229 within the getRewards function, the code literally checks the following:
lastGaugeLoss = GuildToken(guild).lastGaugeLoss(term); if (lastGaugeLoss > 0) { slashed = true; }
Thus, 'slashed' is set to 'true' as soon as the corresponding LendingTerm has already incurred a loss, regardless of when the user started staking their CREDITs.
Therefore, when a user unstakes, all of their CREDITS and GUILD tokens are written off within the getRewards function, even if there was no loss since the user started staking.
Manual review.
Load the user's userStake before check the "lastGaugeLoss" value.
--- SurplusGuildMinter.sol 2023-12-20 11:11:08.380882238 +0100 +++ SurplusGuildMinter_fixed.sol 2023-12-20 11:16:41.511329264 +0100 @@ -226,12 +226,12 @@ { bool updateState; lastGaugeLoss = GuildToken(guild).lastGaugeLoss(term); + userStake = _stakes[user][term]; if (lastGaugeLoss > uint256(userStake.lastGaugeLoss)) { slashed = true; } // if the user is not staking, do nothing - userStake = _stakes[user][term]; if (userStake.stakeTime == 0) return (lastGaugeLoss, userStake, slashed);
Invalid Validation
#0 - c4-pre-sort
2023-12-29T14:29:28Z
0xSorryNotSorry marked the issue as sufficient quality report
#1 - c4-pre-sort
2023-12-29T14:29:49Z
0xSorryNotSorry marked the issue as duplicate of #1164
#2 - c4-judge
2024-01-28T20:18:10Z
Trumpero marked the issue as satisfactory