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

Accounting mismatch of `poolBalance` and `balanceOf(loanToken)` will lead to reverting borrows

Summary

ERC20Tokens with feeOnTransfer won't work with the protocol because accounting is done based on transfer's amount.

Vulnerability Details

Borrow operations for certain tokens will revert due to accounting mismatch of poolBalance and loanToken.balanceOf(lender).

Consider the following POC:

function test_withFeeOnTransferToken() public {
// fee on transfer tokens have the characteristic that the
// mount a sender or a requester (transferFrom) specifies
// is not equal to the balanceOf(receiver). Instead it is lower
// this has implications for balance accounting.

    vm.startPrank(lender1);
    Pool memory p = Pool({
        lender: lender1,
        loanToken: address(loanToken),
        collateralToken: address(collateralToken),
        minLoanSize: 10*10**18,
        poolBalance: 100*10**18,
        maxLoanRatio: 99*10**17, // high efficient liquidations possible
        auctionLength: 1 days,
        interestRate: 1000,
        outstandingLoans: 0
    });
    bytes32 poolId = lender.setPool(p);

    (,,,,uint256 poolBalance,,,,) = lender.pools(poolId);
    assertEq(poolBalance, 100*10**18);
    assertEq(loanToken.balanceOf(address(lender)), 100*10**18);

    // simulate a fee on transfer token by burning 3% of
    // the pools balance 
    uint256 amountToBurn = (loanToken.balanceOf(address(lender)) * 3) / 100;
    loanToken.burn(address(lender), amountToBurn);

    // assertEq(poolBalance, 100*10**18);
    // Fails due to token with fee on transfer. 
    // assertEq(loanToken.balanceOf(address(lender)), 100*10**18);

    vm.startPrank(borrower);
    // this borrow should be allowed according to pool settings
    // however it will revert, because the pool does not have enough
    // ERC20 balance.
    Borrow memory b = Borrow({
        poolId: poolId,
        debt: 98*10**18,
        collateral: 100*10**18
    });
    Borrow[] memory borrows = new Borrow[](1);
    borrows[0] = b;
    vm.expectRevert();
    lender.borrow(borrows);
}

Impact

The protocol is underfunded compared to what funds it thinks are accessible.

Tools Used

Manual review

Recommendations

Usually the preDepositBalance and postDepositBalance are used to account for feeOnTransfer Tokens. However this will become challenging as all funds are stored in one contract. It would be better to assume feeOnTransfer tokens are not valid for this protocol.

Support

FAQs

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

Give us feedback!