20,000 USDC
View results
Submission Details
Severity: medium

Absence of `MIN_AUCTION_LENGTH` allows lenders to bait borrowers with "good deals" and trigger liquidations

Summary

A lender can setPool with very favourable terms for a potential borrower and baiting him into taking out a loan. For example A pool with DAI as the loanToken and savingsDAI as the collateral Token.

Vulnerability Details

The following example has borrowing terms by which a potential borrower can generate yield easily without paying much for borrowing money.

Since the LTV is set very high and the interest rate very low, a borrower might feel he can get a to good to be true deal. Usually interest rate and LTV is much more easy to comprehend than an auctionDuration which a user might not have fully comprehended what it does.

The issue arises from the informational advantage a lender has to the the time when he wants to call startAuction. In this case, the external market has only 1 second of time to buy the Loan and get the lender out of the deal in usual terms. The lender willingly wants the external market not to be able to buy loans (that is why he sets auctionDuration to 1) so that he can liquidate the loan by calling seizeLoan.

For the example below a lender can easily get a finalBalance of

1090e18 tokens (lending + collateral) compared to his initial "investment" of 1000e18 tokens.

function test_forceLiquidate() public {
/* Pool memory p = Pool({
lender: lender1,
loanToken: address(loanToken),
collateralToken: address(collateralToken),
minLoanSize: 1*10**18,
poolBalance: 1000*10**18,
maxLoanRatio: 95*10**17,
auctionLength: 1,
interestRate: 1,
outstandingLoans: 0
}); */
/* Borrow memory b = Borrow({
poolId: poolId,
debt: 94*10**17,
collateral: 100*10**18
}); */
test_borrow();
uint256[] memory loansToSeize = new uint256[](1);
loansToSeize[0] = 0;
printLoan(0);
vm.prank(lender1);
lender.startAuction(loansToSeize);
(,,,,uint256 poolBalance,,,,) = lender.pools(lastPoolIdCreated);
uint256 lenderCollateralBalanceBefore = collateralToken.balanceOf(lender1);
vm.warp(block.timestamp + 1);
lender.seizeLoan(loansToSeize);
vm.prank(lender1);
lender.removeFromPool(lastPoolIdCreated, poolBalance);
}

Impact

competitive market forces for moving/buying loans are likely not effective due to latency for example and allows a lender to liquidate a borrower

Tools Used

manual review.

Recommendations

Consider adding a MIN_AUCTION_DURATION so that the dutch auction can take place and not force liquidations so early.

if (
p.lender != msg.sender ||
p.minLoanSize == 0 ||
p.maxLoanRatio == 0 ||
p.auctionLength < MIN_AUCTION_DURATION ||
p.auctionLength > MAX_AUCTION_LENGTH ||
p.interestRate > MAX_INTEREST_RATE
) revert PoolConfig();

Support

FAQs

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

Give us feedback!