20,000 USDC
View results
Submission Details
Severity: medium

Missed updating `auctionLength` of loan according to new Pool, when giving or buying that loan to new pool inside `giveLoan` and `buyLoan` functions.

Summary

In giveLoan functions of Lender.sol, when pool lender giving the loan of it's pool to another pool then auctionLength inside transferred loan is not updated according to new pool unlike other variables lender and interestRate. Because of this pool and loan struct's auctionLength value will be different, and this loan's auctionLength value will be according to it's old pool from where that loan is transferred using giveLoan function. Surely it is not the intended behaviour of giveLoan function.
Same auctionLengthis not updated inside buyLoan function also.

Vulnerability Details

In giveLoan function, loan is transferred from old pool to new pool by old pool's lender. But in giveLoan function updating of auctionLength for transferred loan is missing. It is for all those loanIds which were passed in giveLoan function. All loans will have their old Pool auctionLength value which is neither intended behaviour from this function nor is good for lenders of new pools
where passed loanIds loans is transferred respectively. Each one for loop iteration for 1 loan transfer to their new pool respectively.

giveLoan Function

File: src/Lender.sol
355: function giveLoan(
uint256[] calldata loanIds,
bytes32[] calldata poolIds
) external {
359: for (uint256 i = 0; i < loanIds.length; i++) {
...
415: // update the loan with the new info
416: loans[loanId].lender = pool.lender;
417: loans[loanId].interestRate = pool.interestRate;
418: loans[loanId].startTimestamp = block.timestamp;
419: loans[loanId].auctionStartTimestamp = type(uint256).max;
420: loans[loanId].debt = totalDebt;
//@audit missing update of auctionLength,it is also not updated above in this function
emit Borrowed(
loan.borrower,
pool.lender,
loanId,
loans[loanId].debt,
loans[loanId].collateral,
pool.interestRate,
block.timestamp
);
}
432: }

Note: For full function code check above hyperlinks on the code

startAuction Function

Lender start auction by calling startAuction function for those loans whom he wants to auction.

In buyLoan function loan.auctionLength used so if loan holds wrong value of auctionLength then auction will not last according to current lender of loan.

File: src/Lender.sol
465: function buyLoan(uint256 loanId, bytes32 poolId) public {
// get the loan info
Loan memory loan = loans[loanId];
// validate the loan
if (loan.auctionStartTimestamp == type(uint256).max)
revert AuctionNotStarted();
471: if (block.timestamp > loan.auctionStartTimestamp + loan.auctionLength)
revert AuctionEnded();
...
}

https://github.com/Cyfrin/2023-07-beedle/blob/main/src/Lender.sol#L471

Inside buyLoan function auctionLength also not updated.

Impact

After giveLoan function execution, whenever in future new lender will call startAuction to start the auction then the auction will last according to old Pool auctionLength not new pool where the loan is currently present. Because in buyLoan function, auction length is decided based on auctionLength value present inside loan(line 471) not it's current Pool and loan is holding auctionLength value of old pool that's not intended behaviour for new pool lender and creates confusion.
Same for buyLoan function.

Tools Used

Manual Review

Recommendations

Inside giveLoan function update auctionLength also for transferred loan as lender and interestRate are updated according to new pool where the loan is transferred using buyLoan function inside for loop. So it will be updated for all passed loanIds.

File: src/Lender.sol
355: function giveLoan(
uint256[] calldata loanIds,
bytes32[] calldata poolIds
) external {
359: for (uint256 i = 0; i < loanIds.length; i++) {
...
415: // update the loan with the new info
416: loans[loanId].lender = pool.lender;
417: loans[loanId].interestRate = pool.interestRate;
418: loans[loanId].startTimestamp = block.timestamp;
419: loans[loanId].auctionStartTimestamp = type(uint256).max;
420: loans[loanId].debt = totalDebt;
+ loans[loanId].auctionLength = pool.auctionLength;
emit Borrowed(
loan.borrower,
pool.lender,
loanId,
loans[loanId].debt,
loans[loanId].collateral,
pool.interestRate,
block.timestamp
);
}
432: }

Same like this update auctionLength inside buyLoan function also.

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.