20,000 USDC
View results
Submission Details
Severity: medium

`seizeLoan()` logic seems unfair

Summary

Not sure if by design, but seizeLoan() takes all of borrowers collateral even when not borrowing at max capacity.

Vulnerability Details

seizeLoan() logic takes all of borrower collateral which seems harsh.

Let's assume an existing pool of DAI/USDC(loanToken/collateralToken).
pool.maxLoanRatio is 70%.
pool.minLoanSize is 100.

Ex:
Alice deposits 1000USDC(the max she can borrow is 700DAI) but only borrows at minLoanSize, 100DAI. Suppose Alice forgets to repay(), loan goes into auction() and no one calls buyLoan(). The loan is now up for seizeLoan().

seizeLoan() will proceed to send all collateral to lender. This way, borrowers have no incentive to provide more collateral than what is needed to get borrowed amount.

Impact

Borrower loses all collateral.

Tools Used

Foundry

Recommendations

I recommend taking proportional of the ((debt/pool.maxLoanRatio) + interest + fee) of collateral and sending to appropriate parties. Ex: ((debt / pool.maxLoanRatio) + interest + fee) of collateral.

function seizeLoan(uint256[] calldata loanIds) public {
for (uint256 i = 0; i < loanIds.length; i++) {
uint256 loanId = loanIds[i];
// 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 AuctionNotEnded();
+ //get pool info
+ bytes32 poolId = getPoolId(loan.lender, loan.loanToken, loan.collateralToken);
+ //Calculate amount to return
+ Pool memory pool = pools[poolId];
+ uint256 loanRatio = loan.debt/ pool.maxLoanRatio;
+ (uint256 interest, ) = _calculateInterest(loan);
+ uint256 govFee = (borrowerFee * loan.collateral) / 10000;
+ uint256 amountSendToLender = (loan.collateral * loanRatio) + interest;
+ uint256 amountReturnToBorrower = loan.collateral - amountSendToLender - govFee;
// calculate the fee
uint256 govFee = (borrowerFee * loan.collateral) / 10000;
// transfer the protocol fee to governance
IERC20(loan.collateralToken).transfer(feeReceiver, govFee);
// transfer the collateral tokens from the contract to the lender
IERC20(loan.collateralToken).transfer(
loan.lender,
- loan.collateral - govFee
+ amountSendToLender
);
+ IERC20(loan.collateralToken).transfer(
- loan.borrower,
- amountReturnToBorrower
- );
- bytes32 poolId = keccak256(
- abi.encode(loan.lender, loan.loanToken, loan.collateralToken)
- );
// update the pool outstanding loans
pools[poolId].outstandingLoans -= loan.debt;
emit LoanSiezed(
loan.borrower,
loan.lender,
loanId,
loan.collateral
);
// delete the loan
delete loans[loanId];
}
}

Support

FAQs

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