code-423n4 / 2024-08-wildcat-findings

3 stars 1 forks source link

debit owned by borrower not cleared after full repayment of loan #75

Closed howlbot-integration[bot] closed 2 months ago

howlbot-integration[bot] commented 2 months ago

Lines of code

https://github.com/code-423n4/2024-08-wildcat/blob/fe746cc0fbedc4447a981a50e6ba4c95f98b9fe1/src/market/WildcatMarket.sol#L226

Vulnerability details

Current implementation lacks the mechanism to clear borrowers debt either by full repayment of debt owned or closing of market .

This is problematic because if the same market approves a new depositor , internal calculations that rely of the debt amount will be affected since the market will still have pending debt.

Proof of Concept

https://github.com/code-423n4/2024-08-wildcat/blob/fe746cc0fbedc4447a981a50e6ba4c95f98b9fe1/src/market/WildcatMarket.sol#L226

function test_Poc() external {
    // Create a lender address
    address lender = vm.addr(0x123);

   uint owed = market.totalDebts() - market.totalAssets();
   assertEq(owed, 0 );

    // Authorize the lender to interact with the market
    _authorizeLender(lender);

    // Lender deposits 100 units into the market
    _deposit(lender, 100 ether);

    // Verify the lender's balance in the market and the market's asset balance
    assertEq(market.balanceOf(lender), 100 ether);
    assertEq(asset.balanceOf(address(market)), 100 ether);

    // Borrower borrows 70 units from the market
    vm.startPrank(borrower);
    market.borrow( 70 ether);
  uint owedAfterBorrow = market.totalDebts() - market.totalAssets();
   assertEq(owedAfterBorrow, 70 ether);
    vm.stopPrank();

    // Fast forward time to allow for withdrawal
    fastForward(parameters.withdrawalBatchDuration + 1);
    market.updateState();

    // Lender requests a full withdrawal of their funds
    vm.prank(lender);
    market.queueFullWithdrawal();
    uint256 expiry = block.timestamp + parameters.withdrawalBatchDuration;

    // Fast forward time to make the market delinquent
    fastForward(20 days);
    market.updateState();

    // Borrower prepares to repay delinquent debt
    vm.startPrank(borrower);
    asset.mint(borrower, 100 ether);
    asset.approve(address(market), 100 ether);

    // Verify the market is in a delinquent state
    MarketState memory state0 = market.previousState();
    assertEq(state0.isDelinquent, true);

    // Borrower repays the delinquent debt
    market.repayDelinquentDebt();

    // Borrower prepares to execute the withdrawal
    asset.mint(borrower, 100 ether);
    asset.approve(address(market), 100 ether);

    // Execute the withdrawal for the lender
    market.executeWithdrawal(lender, uint32(expiry));
    vm.stopPrank();

    // Update and check the market state
    market.updateState();
    MarketState memory state = market.previousState();
    assertEq(state.isDelinquent, false);

    // Check the lender's final balance (should be 0 in the market)
    uint256 finalBalance = asset.balanceOf(lender);
    assertEq(market.balanceOf(lender), 0);    

    // Borrower repays the remaining debt
    vm.startPrank(borrower);
    asset.mint(borrower, 100 ether);
    asset.approve(address(market), 100 ether);
    market.repay(70 ether);
    vm.stopPrank();

  assertTrue(market.totalDebts() > 0 );

    vm.startPrank(borrower);
    asset.mint(borrower, 100 ether);
    asset.approve(address(market), 100 ether);
    market.closeMarket(); 
    vm.stopPrank();

   //@audit-issue : Debt is not cleared
    assertTrue(market.totalDebts() > 0 );
}

This will affect future deposits on the same market, as the debt will continue to accrue.

Recommended Mitigation Steps

Add a final settlement step in the closeMarket() function to clear any remaining debts

Assessed type

Other

c4-judge commented 1 month ago

3docSec marked the issue as unsatisfactory: Invalid