20,000 USDC
View results
Submission Details
Severity: high
Valid

Every time a refinance happens, the pool receiving the loan has its balance decreased by twice the correct amount

Summary

In the function refinance in Lender.sol the new pool has its balance updated twice. This results in the new pool being reduced by 2 * debt, while the loan being given out is only debt. The end result of this ends up being that the lender of that new pool won't be able to withdraw all of their funds since the pool balance will be lower than what it is supposed to be. Those lost funds will stay stuck in the smart contract.

Vulnerability Details

below are the two lines of code seen within the refinance function that causes the problem.

...
// now lets deduct our tokens from the new pool
_updatePoolBalance(poolId, pools[poolId].poolBalance - debt);
...
// update pool balance
pools[poolId].poolBalance -= debt;
...

Below is a test illustrating the issue. This is coded within the test suite of the protocol.

function test_refinance() public {
test_borrow();
vm.startPrank(lender2);
Pool memory p_2 = Pool({
lender: lender2,
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_2 = lender.setPool(p_2);
vm.startPrank(borrower);
Refinance memory r = Refinance({
loanId: 0,
poolId: keccak256(abi.encode(address(lender2), address(loanToken), address(collateralToken))),
debt: 100 * 10 ** 18,
collateral: 100 * 10 ** 18
});
Refinance[] memory rs = new Refinance[](1);
rs[0] = r;
lender.refinance(rs);
assertEq(loanToken.balanceOf(address(borrower)), 995*10**17);
assertEq(collateralToken.balanceOf(address(lender)), 100*10**18);
// adding an extra assert in this test to show that debt is subtracted twice from
(,,,,uint256 poolBalance_2,,,,) = lender.pools(poolId_2);
assertEq(poolBalance_2, 900*10**18); // this assert should fail since the debt is being subtracted twice
}

as expected that extra assert will fail since debt is being subtracted twice.

forge test --match-test test_refinance -vv
Running 1 test for test/Lender.t.sol:LenderTest
[FAIL. Reason: Assertion failed.] test_refinance() (gas: 865619)
Logs:
Error: a == b not satisfied [uint]
Expected: 900000000000000000000
Actual: 800000000000000000000

That lender will then not be able to withdraw all of his funds since the pool balance is lower than it's supposed to be (removeFromPool() reverts if amount requested out is bigger than pool balance).

Impact

Lenders will have part of their funds stuck in the Lender.sol smart contract every time their pool is used as the destination of a refinance.

Tools Used

Foundry & Manual Review.

Recommendations

Only subtract the debt from the new pool's balance once.

Support

FAQs

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