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

3 stars 1 forks source link

Increasing APR when reserve ratio is zero and borrower uses all the funds moves the account to delinquency #35

Closed howlbot-integration[bot] closed 1 month ago

howlbot-integration[bot] commented 2 months ago

Lines of code

https://github.com/code-423n4/2024-08-wildcat/blob/main/src/market/WildcatMarketConfig.sol#L123

Vulnerability details

Vulnerability Details

The Wildcat lending protocol allows borrowers to increase the Annual Percentage Rate (APR) of a market using the setAnnualInterestAndReserveRatioBips function to encourage additional deposits. However, this behavior is flawed when the borrower's reserve ratio is zero and the market is fully utilized. When the borrower increases the APR without having any reserves, the market immediately moves into a delinquent state. This situation arises because the protocol expects a reserve ratio to be in place to cover interest payments.

As a result, if lenders do not deposit additional funds within the grace period, the borrower will be required to pay the penalty fee. This design flaw can lead to unintended financial consequences for the borrower, who may be penalized even when they increase the APR with the intention of attracting more liquidity.

Impact

Borrower Penalty: Borrowers who increase the APR with zero reserves face penalties when the market enters a delinquent state, even though they may intend to attract additional deposits.

Proof of Concept

function test_increaseInterest() public {

    asset.mint(josh, 200000 ether);
    asset.mint(borrower,  12000 ether);

    vm.startPrank(josh);
    market.deposit(100000 ether);
    vm.stopPrank();

    vm.startPrank(borrower);
    market.borrow(100000 ether);
    vm.stopPrank();

    fastForward(365 days);

    vm.startPrank(borrower);
    market.setAnnualInterestAndReserveRatioBips(1100, 0);
    vm.stopPrank(); 

    assert(market.currentState().isDelinquent == true);

  }

Recommended Mitigation Steps

Assessed type

Other

d1ll0n commented 1 month ago

The reason the market becomes delinquent here is because time has passed (thus interest has accrued) and the market has no remaining assets, not because of the change to the APR. Your POC is incomplete, but if we fill it out we can see this is true by checking isDelinquent before and after:

  function test_increaseInterest() public {
    address josh = address(0x05050505);

    parameters.annualInterestBips = 1_000;
    parameters.withdrawalBatchDuration = 0;
    parameters.reserveRatioBips = 0;
    BaseMarketTest.setUpContracts(true);

    MarketState memory oldState = market.currentState();
    assertEq(oldState.annualInterestBips, 1_000);
    assertEq(oldState.reserveRatioBips, 0);

    _deposit(josh, 100000 ether);
    _borrow(100000 ether);

    fastForward(365 days);

    // Update to trigger delinquency check
    market.updateState();
    assertTrue(market.currentState().isDelinquent, 'market should be delinquent before');

    vm.prank(borrower);
    market.setAnnualInterestAndReserveRatioBips(1100, 0);

    MarketState memory newState = market.currentState();
    assertEq(newState.reserveRatioBips, 0);
    assertEq(newState.annualInterestBips, 1100);
    assertTrue(newState.isDelinquent, 'market should be delinquent after');
  }
3docSec commented 1 month ago

Marking as invalid: the borrower becomes delinquent by not respecting the terms configured by themselves

c4-judge commented 1 month ago

3docSec marked the issue as unsatisfactory: Invalid