20,000 USDC
View results
Submission Details
Severity: high
Valid

During the `refinance auction` a `loan` can be forced upon any `lender`

Summary

During the refinance auction a loan can be forced upon another lender as there is no check to ensure that the caller of the buyLoan() function is the owner of the pool (especially by a malicious lender who has an undesirable loan that needs to be liquidated)

Vulnerability Details

During an auction, buyLoan() is called by anyone but must have a pool with tokens according to the comment above the function.

File:Lender.sol
/// @notice buy a loan in a refinance auction
/// can be called by anyone but you must have a pool with tokens
/// @param loanId the id of the loan to refinance
/// @param poolId the pool to accept
function buyLoan(uint256 loanId, bytes32 poolId) public {

This means, lenders that find the loan desirable can buy the loan for themselves or if undesirable is left to be liquidated.
But the function lacks a check to ensure that the msg.sender is the owner of the pool. Therefore it can be called by any anyone to give a loan to any lender who may not want the loan.

File:Lender.sol
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();
if (block.timestamp > loan.auctionStartTimestamp + loan.auctionLength)
revert AuctionEnded();
// calculate the current interest rate
uint256 timeElapsed = block.timestamp - loan.auctionStartTimestamp;
uint256 currentAuctionRate = (MAX_INTEREST_RATE * timeElapsed) /
loan.auctionLength;
// validate the rate
if (pools[poolId].interestRate > currentAuctionRate) revert RateTooHigh();
// calculate the interest
(uint256 lenderInterest, uint256 protocolInterest) = _calculateInterest(
loan
);
// reject if the pool is not big enough
uint256 totalDebt = loan.debt + lenderInterest + protocolInterest;
if (pools[poolId].poolBalance < totalDebt) revert PoolTooSmall();
// if they do have a big enough pool then transfer from their pool
_updatePoolBalance(poolId, pools[poolId].poolBalance - totalDebt);
pools[poolId].outstandingLoans += totalDebt;
// now update the pool balance of the old lender
bytes32 oldPoolId = getPoolId(
// More code...

Impact

Therefore a malicious actor can force a bad loan upon any lender as just a pool id is needed for the function.

Tools Used

Manual Review

Recommendations

Checks should be added to ensure that only the owner of the pool can call and claim a loan during an auction.

if (msg.sender != pools[poolId].lender) revert Unauthorized();

Support

FAQs

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