When the auction of a loan is at the its beginning no one can buy the loan because of the currentAuctionRate
.
Current auction rate is calculated wrong in the buyLoan
function. This prevents the users from buying a loan that is put for an auction.
Let's say that Alice (a lender) puts Bob's (borrower) loan for an auction. Alice set the auction length to be 2 days which means that the loan's auctionLength
would be 2 days as well (it is set in the borrow
function). Now let's imagine that a third person (we will call him Andy) wants to buy the loan as soon as the auction starts so after the startAuction
function is called by Alice, Andy call buyLoan
function to buy the loan in his already created pool. Andy won't be able to buy the loan because of the calculations inside the buyLoan
function:
uint256 currentAuctionRate = (MAX_INTEREST_RATE * timeElapsed) /
loan.auctionLength; //@ audit rate is too high
// validate the rate
if (pools[poolId].interestRate > currentAuctionRate) revert RateTooHigh();
Let's see what the value of currentAuctionRate
will be. The MAX_INTEREST_RATE
= 100,000 and if timeElapsed
is lower than loan.autionLength / 100
the function will revert with RateTooHigh
exception. This means that 1/100 of the loan.autionLength
should have passed in order for the loan to be bought.
You need to add this uint256[] loanIds;
and this Borrow[] borrowArray;
as a storage variables in the test to work properly.
Add this test in the Lender.t.sol
to see what is happening:
function testBuyLoan() public {
vm.startPrank(lender1);
Pool memory p = Pool({
lender: lender1,
loanToken: address(loanToken),
collateralToken: address(collateralToken),
minLoanSize: 100*10**18,
poolBalance: 1000*10**18,
maxLoanRatio: 2*10**18,
auctionLength: 2 days,
interestRate: 1000,
outstandingLoans: 0
});
lender.setPool(p);
vm.stopPrank();
vm.startPrank(lender2);
Pool memory p1 = Pool({
lender: lender2,
loanToken: address(loanToken),
collateralToken: address(collateralToken),
minLoanSize: 100*10**18,
poolBalance: 1000*10**18,
maxLoanRatio: 2*10**18,
auctionLength: 1,
interestRate: 1000,
outstandingLoans: 0
});
lender.setPool(p1);
vm.stopPrank();
bytes32 poolId = lender.getPoolId(lender1, address(loanToken), address(collateralToken));
bytes32 poolId2 = lender.getPoolId(lender2, address(loanToken), address(collateralToken));
vm.startPrank(borrower);
Borrow memory borrow = Borrow({
poolId: poolId,
debt: 200e18,
collateral: 100e18
});
borrowArray.push(borrow);
lender.borrow(borrowArray);
vm.stopPrank();
loanIds.push(0);
vm.startPrank(lender1);
lender.startAuction(loanIds);
vm.stopPrank();
vm.warp(block.timestamp + (2 days / 100) - 1);
vm.startPrank(borrower);
lender.buyLoan(0, poolId2);
vm.stopPrank();
(,,,,uint256 poolBalance,,,,) = lender.pools(poolId);
(,,,,uint256 poolBalance2,,,,) = lender.pools(poolId2);
console.log("Pool 1 balance: ", poolBalance);
console.log("Pool 2 balanace: ", poolBalance2);
}
In this test we try to buy the loan to another pool but we encounter a RateTooHigh
exception. If we move the block.timestamp
by one more second then we will be able to buy the loan and the transaction will succeed.
This leads to the buyLoan
function being blocked at the beginning of an auction.
Visual Studio Code, Foundry
Instead of doing this calculations just check if the pools[poolId].interestRate > MAX_INTEREST_RATE
.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.