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: 110/127
Findings: 1
Award: $20.82
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: SBSecurity
Also found by: 0xaltego, 0xbepresent, Aymen0909, Bauchibred, Cosine, EVDoc, EloiManuel, HighDuty, Sathish9098, Tendency, Timeless, ZanyBonzy, beber89, deliriusz, ether_sky, grearlake, hals, klau5, lsaudit, nadin, rvierdiiev, tsvetanovv
20.8157 USDC - $20.82
LendingTerm
has received enough weightsupportOffboard()
function in the LendingTermOffboarding.sol
allow users to support the offboarding of a specific Lending Term:
  function supportOffboard(     uint256 snapshotBlock,     address term   ) external whenNotPaused {     require(       block.number <= snapshotBlock + POLL_DURATION_BLOCKS,       "LendingTermOffboarding: poll expired"     );     uint256 _weight = polls[snapshotBlock][term];     require(_weight != 0, "LendingTermOffboarding: poll not found");     uint256 userWeight = GuildToken(guildToken).getPastVotes(       msg.sender,       snapshotBlock     );     require(userWeight != 0, "LendingTermOffboarding: zero weight");     require(       userPollVotes[msg.sender][snapshotBlock][term] == 0,       "LendingTermOffboarding: already voted"     );     userPollVotes[msg.sender][snapshotBlock][term] = userWeight;     polls[snapshotBlock][term] = _weight + userWeight;     if (_weight + userWeight >= quorum) {       canOffboard[term] = true;     }     emit OffboardSupport(       block.timestamp,       term,       snapshotBlock,       msg.sender,       userWeight     );   }
If quorum is reached then canOffboard[term]
settles to true.:
    if (_weight + userWeight >= quorum) {       canOffboard[term] = true;     }
However, users can still continue to vote because there is no check at the beginning of the function whether the quorum has been reached.
Add a check at the beginning of the function if the quorum is reached.
forgive()
in AuctionHouse.sol
When the auction is over and no one has placed a bid then the protocol has a bad debt and forgive()
should be called:
  function forgive(bytes32 loanId) external {     // this view function will revert if the auction is not started,     // or if the auction is already ended.     (, uint256 creditAsked) = getBidDetail(loanId);     require(creditAsked == 0, "AuctionHouse: ongoing auction");     // close the auction in state     auctions[loanId].endTime = block.timestamp;     nAuctionsInProgress--;     // notify LendingTerm of auction result     address _lendingTerm = auctions[loanId].lendingTerm;     LendingTerm(_lendingTerm).onBid(       loanId,       msg.sender,       0, // collateralToBorrower       0, // collateralToBidder       0 // creditFromBidder     );     // emit event     emit AuctionEnd(       block.timestamp,       loanId,       LendingTerm(_lendingTerm).collateralToken(),       0, // collateralSold       0 // debtRecovered     );   }
The protocol has written that someone can get the collateral for 0 tokens but this is not true:
    // second phase fully elapsed, anyone can receive the full collateral and give 0 CREDIT     // in practice, somebody should have taken the arb before we reach this condition.     else {       // receive the full collateral       collateralReceived = auctions[loanId].collateralAmount;       //creditAsked = 0; // implicit     }
Since no one gets anything in return when calling the forgive
function then no one will call it.
https://github.com/code-423n4/2023-12-ethereumcreditguild/blob/main/src/loan/AuctionHouse.sol#L154-L160 https://github.com/code-423n4/2023-12-ethereumcreditguild/blob/main/src/loan/AuctionHouse.sol#L198-L230
Add some sort of reward in the forgive()
function to incentivize someone to call it.
getBidDetail()
function is used in bid()
and forgive()
function to get the bid details for an active auction.
We are interested in the second phase of the auction:
    else if (block.timestamp < _startTime + auctionDuration) { //@audit not include last block bid       // receive the full collateral       collateralReceived = auctions[loanId].collateralAmount;       // compute amount of CREDIT to ask       uint256 PHASE_2_DURATION = auctionDuration - midPoint;       uint256 elapsed = block.timestamp - _startTime - midPoint; // [0, PHASE_2_DURATION[       uint256 _callDebt = auctions[loanId].callDebt; // SLOAD       creditAsked = _callDebt - (_callDebt * elapsed) / PHASE_2_DURATION;     }
We can see from the else if
this phase does not include the last block bid.
If someone decides to place a bid right in the last 12 seconds (Ethereum) of the auction, they will get nothing.
https://github.com/code-423n4/2023-12-ethereumcreditguild/blob/main/src/loan/AuctionHouse.sol#L144
Change conditioning from:
else if (block.timestamp < _startTime + auctionDuration) {
To:
else if (block.timestamp <= _startTime + auctionDuration) {
#0 - c4-pre-sort
2024-01-05T18:08:29Z
0xSorryNotSorry marked the issue as sufficient quality report
#1 - Trumpero
2024-01-31T02:15:10Z
There is no check whether a LendingTerm has received enough weight -> low
No one will have the initiative to call forgive() in AuctionHouse.sol -> info
The second phase of the auction does not include the last block
-> NC, since creditAsked in else if
block will still be 0
#2 - Trumpero
2024-01-31T12:03:58Z
+L from https://github.com/code-423n4/2023-12-ethereumcreditguild-findings/issues/739 +L from https://github.com/code-423n4/2023-12-ethereumcreditguild-findings/issues/698 +L from https://github.com/code-423n4/2023-12-ethereumcreditguild-findings/issues/696 +L from https://github.com/code-423n4/2023-12-ethereumcreditguild-findings/issues/687
#3 - c4-judge
2024-01-31T12:04:02Z
Trumpero marked the issue as grade-b