xanoutas/solidity-security-findings huggingface dataset:
Borrower can set interest rate in close market state 
Audit: 2023-10-wildcat-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-wildcat/blob/c5df665f0bc2ca5df6f06938d66494b11e7bdada/src/market/WildcatMarketConfig.sol#L149-L159
https://github.com/code-423n4/2023-10-wildcat/blob/c5df665f0bc2ca5df6f06938d66494b11e7bdada/src/WildcatMarketController.sol#L468-L488


# Vulnerability details

## Summary 
borrower can set interest rate after closing market .
## Impact
loss of funds for some lenders .

## Vulnerability Details 
borrower can set new interestRateBips after close market , this leads to accrue interest for available assets in market while there aren't enough assets to cover accrued interest so lenders that request withdraw earlier earn additional interest and lenders that request later can't withdraw all of their funds .

## Tools Used
Manual Review 
## Recommended Mitigation Steps
prevent set interestRateBips after market is closed .


## Assessed type

Other
Sanction can be bypassed by transferring market token
Audit: 2023-10-wildcat-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-wildcat/blob/c5df665f0bc2ca5df6f06938d66494b11e7bdada/src/market/WildcatMarketToken.sol#L64
https://github.com/code-423n4/2023-10-wildcat/blob/c5df665f0bc2ca5df6f06938d66494b11e7bdada/src/WildcatMarketController.sol#L182


# Vulnerability details

## Description

Wildcat protocol queries Chainalysis oracle for checking whether lender is sanctioned or not. However according to docs(https://go.chainalysis.com/chainalysis-oracle-docs.html) provided by Chainalysis, they do not guarantee the timeliness of the data. What can happen here is as soon as the lender finds out that the address is sanctioned, he can transfer “market token” to other address he owns and request withdraw with that address. Unless sanction list from Chainalysis is updated within the batch duration period, lender can successfully withdraw his asset. 

One assumption here is that “market token” has to be transferred before ```nukeFromOrbit``` is called(which can be easily done by front-running) as nukeFromOrbit moves all the balance to escrow contract. 

## PoC

```solidity
// SPDX-License-Identifier: NONE
pragma solidity >=0.8.20;

import "forge-std/console.sol";

import './BaseMarketTest.sol';

contract Exploit is BaseMarketTest {
    function testSanctionBypass() external {
        address alice_bck = address(0xaaaa);

        _deposit(alice, 1e18);

        sanctionsSentinel.sanction(alice);

        // Before nukeFromOrbit is called, alice transfers balance to alice_bck
        vm.startPrank(alice);
        market.transfer(alice_bck, market.balanceOf(alice));
        vm.stopPrank();
        market.nukeFromOrbit(alice);

        // Grant alice_bck with withdrawOnly role
        address[] memory markets = new address[](1);
        markets[0] = address(market);
        controller.updateLenderAuthorization(alice_bck, markets);

        _requestWithdrawal(alice_bck, 1e18);
        uint32 expiry = uint32(block.timestamp + parameters.withdrawalBatchDuration);

        // alice_bck address shouldn't be sanctioned by sentinel in this period
        fastForward(parameters.withdrawalBatchDuration);

        market.executeWithdrawal(alice_bck, expiry);

        console.log("Balance of alice_bck : %s", asset.balanceOf(alice_bck));
    }
}
```

## Tools Used

Visual Studio Code, Foundry 

## Recommended Mitigation Steps

Check whether lender is sanctioned before transferring token.


## Assessed type

Token-Transfer
calling `closeMarket` is not possible because of the `onlyController` modifier
Audit: 2023-10-wildcat-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-wildcat/blob/c5df665f0bc2ca5df6f06938d66494b11e7bdada/src/market/WildcatMarket.sol#L142
https://github.com/code-423n4/2023-10-wildcat/blob/c5df665f0bc2ca5df6f06938d66494b11e7bdada/src/market/WildcatMarketBase.sol#L136-L139


# Vulnerability details

## Impact
The closeMarket function has the [onlyController](https://github.com/code-423n4/2023-10-wildcat/blob/c5df665f0bc2ca5df6f06938d66494b11e7bdada/src/market/WildcatMarketBase.sol#L136-L139) modifier, which restricts calls to the closeMarket function to the controller only. However, the controller contract does not contain a function that invokes the closeMarket function. As a result, it is currently impossible to close the market by either the borrower or the controller.

## Proof of Concept
the function `closeMarket` have the onlyController modifier which allows calls from the controller only:
```solidity 
    function closeMarket() external onlyController nonReentrant {
        MarketState memory state = _getUpdatedState();
        state.annualInterestBips = 0;
        state.isClosed = true;
        state.reserveRatioBips = 0;
        if (_withdrawalData.unpaidBatches.length() > 0) {
            revert CloseMarketWithUnpaidWithdrawals();
        }
        uint256 currentlyHeld = totalAssets();
        uint256 totalDebts = state.totalDebts();
        if (currentlyHeld < totalDebts) {
            // Transfer remaining debts from borrower
            asset.safeTransferFrom(
                borrower,
                address(this),
                totalDebts - currentlyHeld
            );
        } else if (currentlyHeld > totalDebts) {
            // Transfer excess assets to borrower
            asset.safeTransfer(borrower, currentlyHeld - totalDebts);
        }
        _writeState(state);
        emit MarketClosed(block.timestamp);
    }
```

the `onlyController` modifier:
```solidity 
modifier onlyController() {
    if (msg.sender != controller) revert NotController();
    _;
  }
```

Since the controller contract does not include a function to invoke the closeMarket function, market closure is currently impossible.

## Tools Used
manual review

## Recommended Mitigation Steps

To address this issue, it is recommended to modify the modifier to use the onlyBorrower modifier instead, or create a function inside the controller contract that invokes the closeMarket function. This adjustment will enable market closure by the borrower or the controller as intended.


## Assessed type

Other
Borrower can never close the market
Audit: 2023-10-wildcat-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-wildcat/blob/c5df665f0bc2ca5df6f06938d66494b11e7bdada/src/market/WildcatMarket.sol#L142-L149


# Vulnerability details

## Impact
The function ``closeMarket()`` sets the APR to 0% and does the necessary fund transfers.
This function implements a modifier ``onlyController``, this requires that only the controller can make this call. However the controller contract has no method that calls the function ``closeMarket()``.
## Proof of Concept
```solidity
  modifier onlyController() {
    if (msg.sender != controller) revert NotController();
    _;
  }

 function closeMarket() external onlyController nonReentrant {}
```
In the Market Controller contract available at https://github.com/code-423n4/2023-10-wildcat/blob/main/src/WildcatMarketController.sol, there exists no function to call the ``closeMarket()``

## Tools Used
Manual Review
## Recommended Mitigation Steps
Add a function in the Market Controller contract that calls ``WildcatMarket.closeMarket()``


## Assessed type

Other
Wrong argument order when calling createEscrow
Audit: 2023-10-wildcat-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-wildcat/blob/c5df665f0bc2ca5df6f06938d66494b11e7bdada/src/market/WildcatMarketWithdrawals.sol#L166
https://github.com/code-423n4/2023-10-wildcat/blob/c5df665f0bc2ca5df6f06938d66494b11e7bdada/src/market/WildcatMarketBase.sol#L172


# Vulnerability details

## Description

```createEscrow``` function in ```WildcatSanctionsSentinel``` is called to create an escrow contract responsible for holding the asset of sanctioned account. Definition for this function is as follows

```solidity
function createEscrow(
    address account,
    address borrower,
    address asset
  ) external returns (address escrowContract);
```

This function is called by ```executeWithdrawal``` and ```_blockAccount``` function, both of which are calling the function with wrong argument order.

```solidity
// Inside executeWithdraw

address escrow = IWildcatSanctionsSentinel(sentinel).createEscrow(
  accountAddress,
  borrower,
  address(asset)
);

// Inside _blockAccount

address escrow = IWildcatSanctionsSentinel(sentinel).createEscrow(
  accountAddress,
  borrower,
  address(this)
);
```

What happens from this is that ```canReleaseEscrow``` function in escrow contract returns true which means that borrower can call ```releaseEscrow``` function to transfer asset to their wallet(instead of lenders wallet).

## PoC

```solidity
// SPDX-License-Identifier: NONE
pragma solidity >=0.8.20;

import "forge-std/console.sol";

import './BaseMarketTest.sol';

contract Exploit is BaseMarketTest {
    function testCreateEscrowArg() external {
        address escrow;

        _deposit(alice, 1e18);
        _requestWithdrawal(alice, 1e18);
        uint32 expiry = uint32(block.timestamp + parameters.withdrawalBatchDuration);

        sanctionsSentinel.sanction(alice);

        fastForward(parameters.withdrawalBatchDuration);

        vm.recordLogs();
        market.executeWithdrawal(alice, expiry); 

        Vm.Log[] memory entries = vm.getRecordedLogs();
        for (uint256 i=0; i<entries.length; i++) {
            if (entries[i].topics[0] == keccak256("SanctionOverride(address,address)")) {
                escrow = address(uint160(uint256(entries[i].topics[2])));
            }
        }

        console.log("Balance of borrower before releaseEscrow : %s", asset.balanceOf(borrower));
        WildcatSanctionsEscrow(escrow).releaseEscrow();
        console.log("Balance of borrower after releaseEscrow : %s", asset.balanceOf(borrower));
    }
}
```

### Tools Used
Visual Studio Code, Foundry

### Recommended Mitigation Steps
Call ```createEscrow``` with correct argument order



## Assessed type

Other
Core functionality of the WildcatMarket, including `closeMarket` can never be called based on the current implementation
Audit: 2023-10-wildcat-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-wildcat/blob/main/src/market/WildcatMarket.sol#L142
https://github.com/code-423n4/2023-10-wildcat/blob/main/src/market/WildcatMarketConfig.sol#L134


# Vulnerability details

## Impact

Core functions of the WildcatMarket contract can never be called (such as `WildcatMarket:closeMarket` or `WildcatMarketConfig:setMaxTotalSupply`) due to them being protected by the `onlyController` modifier, while the WildcatMarketController contract doesn't actually have any logic which can actually call these functions. This ultimately means that certain core logic can never be used.

## Proof of Concept

The `WildcatMarket:closeMarket` function is implemented as follows:
```solidity
function closeMarket() external onlyController nonReentrant {
  MarketState memory state = _getUpdatedState();
  state.annualInterestBips = 0;
  state.isClosed = true;
  state.reserveRatioBips = 0;
  if (_withdrawalData.unpaidBatches.length() > 0) {
    revert CloseMarketWithUnpaidWithdrawals();
  }
  uint256 currentlyHeld = totalAssets();
  uint256 totalDebts = state.totalDebts();
  if (currentlyHeld < totalDebts) {
    // Transfer remaining debts from borrower
    asset.safeTransferFrom(borrower, address(this), totalDebts - currentlyHeld);
  } else if (currentlyHeld > totalDebts) {
    // Transfer excess assets to borrower
    asset.safeTransfer(borrower, currentlyHeld - totalDebts);
  }
  _writeState(state);
  emit MarketClosed(block.timestamp);
}
```

It uses the `onlyController` modifier which is defined as follows:
```solidity
modifier onlyController() {
  if (msg.sender != controller) revert NotController();
  _;
}
```

This is then fundamentally a problem, as the controller does not have any function that can, for example, call `WildcatMarket:closeMarket`. Additionally, it's clear that they intended the controller to be the WildcatMarketController based on the following tests: [BaseMarketTest](https://github.com/code-423n4/2023-10-wildcat/blob/main/test/BaseMarketTest.sol#L28-L30) and [Test](https://github.com/code-423n4/2023-10-wildcat/blob/main/test/shared/Test.sol#L50-L74).

## Tools Used

Manual review

## Recommended Mitigation Steps

For all `onlyController` protected functions of the WildcatMarket contract, ensure that there is an associated function on the WildcatMarketController contract which can call them.


## Assessed type

Other
market can become delinquent after being closed, which result in lenders being unable to withdraw their funds
Audit: 2023-10-wildcat-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-wildcat/blob/main/src/market/WildcatMarketWithdrawals.sol#L77-L121


# Vulnerability details

## Impact

The issue arises because the market state is continually updated when lenders queue their withdrawals using `queueWithdrawal`, even if the market is closed. In this scenario, there is a case where the condition `state.liquidityRequired() > totalAssets()` becomes true, making the market delinquent. This, in turn, leads to the accrual of delinquency fees, which increases the `scaleFactor`. 

As a consequence, even though the market is closed interest is still being accrued and as borrower won't pay for it (has no incentive to do so as he can't borrow anymore when market is closed) the total assets held in the market contract at the time of closure become insufficient to repay all the lenders.

## Proof of Concept

When a market is closed in `WildcatMarket.closeMarket`, the function makes sure that the total amount of assets held in the contract is sufficient to repay all the lenders, for doing so the following condition must be satisfied  `totalAssets() == state.totalDebts()` where the totals amount of debts is given as : 

```solidity
function totalDebts(MarketState memory state) internal pure returns (uint256) {
    return
      state.normalizeAmount(state.scaledTotalSupply) +
      state.normalizedUnclaimedWithdrawals +
      state.accruedProtocolFees;
}
```

Thus the `totalDebts` function assumes that the `scaleFactor` value will not increase anymore which is why it makes the calculation `state.normalizeAmount(state.scaledTotalSupply)` to get the current lended funds value.

Now when lenders want to withdraw their funds from the closed market they will call `queueWithdrawal` below :  

```solidity
function queueWithdrawal(uint256 amount) external nonReentrant {
    MarketState memory state = _getUpdatedState();

    // Cache account data and revert if not authorized to withdraw.
    Account memory account = _getAccountWithRole(
        msg.sender,
        AuthRole.WithdrawOnly
    );

    uint104 scaledAmount = state.scaleAmount(amount).toUint104();
    if (scaledAmount == 0) {
        revert NullBurnAmount();
    }

    // Reduce caller's balance and emit transfer event.
    account.scaledBalance -= scaledAmount;
    _accounts[msg.sender] = account;
    emit Transfer(msg.sender, address(this), amount);

    // If there is no pending withdrawal batch, create a new one.
    if (state.pendingWithdrawalExpiry == 0) {
        state.pendingWithdrawalExpiry = uint32(
            block.timestamp + withdrawalBatchDuration
        );
        emit WithdrawalBatchCreated(state.pendingWithdrawalExpiry);
    }
    // Cache batch expiry on the stack for gas savings.
    uint32 expiry = state.pendingWithdrawalExpiry;

    WithdrawalBatch memory batch = _withdrawalData.batches[expiry];

    // Add scaled withdrawal amount to account withdrawal status, withdrawal batch and market state.
    _withdrawalDa
Lack of appropriate function in `WildcatMarketController` to set maxTotalSupply in the market
Audit: 2023-10-wildcat-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-wildcat/blob/main/src/market/WildcatMarketConfig.sol#L134


# Vulnerability details

The `WildcatMarketConfig` contract has a `setMaxTotalSupply` function that should allow the borrower to increase maxTotalSupply if needed and the required conditions are met:
```solidity
File: WildcatMarketConfig.sol
128:   /**
129:    * @dev Sets the maximum total supply - this only limits deposits and
130:    *      does not affect interest accrual.
131:    *
132:    *      Can not be set lower than current total supply.
133:    */
134:   function setMaxTotalSupply(uint256 _maxTotalSupply) external onlyController nonReentrant { 
...
```

The `setMaxTotalSupply` function could be called only through the market controller due to its access control modifier.

However, if we check the code of `WildcatMarketController` we can't find an appropriate function that would allow the borrower to call `setMaxTotalSupply` at the market address. 

## Impact
Borrowers cannot increase the market `maxTotalSupply` while it should be able, which means a lack of expected functionality.

## Proof of Concept

1. Borrower creates a new market.
2. Lenders deposit their funds into the market.
3. Borrower can't increase `maxTotalSupply` on the market if needed.

This bug is missed in tests since they implemented in the way the caller is pranked as a `controller` address, while it should be an actual call through not existed `WildcatMarketController#setMaxTotalSupply` function - L24:
```solidity
File: WildcatMarketConfig.t.sol
22:   function test_maxTotalSupply() external returns (uint256) {
23:     assertEq(market.maxTotalSupply(), parameters.maxTotalSupply);
24:     vm.prank(parameters.controller);
25:     market.setMaxTotalSupply(10000);
26:     assertEq(market.maxTotalSupply(), 10000);
27:   }
28: 
``` 

## Recommended Mitigation Steps

Consider implementing a function in `WildcatMarketController` that would allow the borrower to call the `setMaxTotalSupply` function on the market contract. 





## Assessed type

Other
Almost all functions of a Market can be DoS'd with stuck funds if the chosen asset uses high decimals or has a low numerary value due to an overflow
Audit: 2023-10-wildcat-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-wildcat/blob/c5df665f0bc2ca5df6f06938d66494b11e7bdada/src/market/WildcatMarketBase.sol#L504
https://github.com/code-423n4/2023-10-wildcat/blob/c5df665f0bc2ca5df6f06938d66494b11e7bdada/src/market/WildcatMarketBase.sol#L473
https://github.com/code-423n4/2023-10-wildcat/blob/c5df665f0bc2ca5df6f06938d66494b11e7bdada/src/market/WildcatMarketBase.sol#L375
https://github.com/code-423n4/2023-10-wildcat/blob/c5df665f0bc2ca5df6f06938d66494b11e7bdada/src/market/WildcatMarketBase.sol#L375
https://github.com/code-423n4/2023-10-wildcat/blob/c5df665f0bc2ca5df6f06938d66494b11e7bdada/src/market/WildcatMarketBase.sol#L429
https://github.com/code-423n4/2023-10-wildcat/blob/c5df665f0bc2ca5df6f06938d66494b11e7bdada/src/market/WildcatMarketBase.sol#L528


# Vulnerability details

## Impact

If a Market underlying asset total supply is  higher than `type(uint104).max` (~20.3e30 tokens, for example 21M NEAR tokens which has 24 decimals = 21e30), it is possible to have a geenral DoS because of an overflow error, i.e making all these actions to revert:

- updateState
- depositUpTo
- collectFee
- borrow
- closeMarket
- nukeFromOrbit
- updateAccountAuthorization
- setMaxTotalSupply/AnnualInterestBips/ReserveRatioBips
- transfer/transferFrom
- balanceOf
- totalSupply
- queueWithdrawal
- executeWithdrawal
- processUnpaidWithdrawalBatch
- getWithdrawalBatch
- getAvailableWithdrawalAmount
- currentState (and then every functions calling it, like scaleFactor() and many other)

This comes from the fact that all of these function calls at their very beginning `_getUpdateState` or `_calculateCurrentState`, which both lead to an overflow inside `_applyWithdrawalBatchPayment` or `_applyWithdrawalBatchPaymentView`

If we take `_getUpdateState`, inside this function:

1) `_processExpiredWithdrawalBatch` [is called](https://github.com/code-423n4/2023-10-wildcat/blob/c5df665f0bc2ca5df6f06938d66494b11e7bdada/src/market/WildcatMarketBase.sol#L375)

2) which itself [calls](https://github.com/code-423n4/2023-10-wildcat/blob/c5df665f0bc2ca5df6f06938d66494b11e7bdada/src/market/WildcatMarketBase.sol#L471) `availableLiquidityForPendingBatch(state, totalAssets())`  returning `availableLiquidity` which is a uint256

3) `availableLiquidity` is then [passed to](https://github.com/code-423n4/2023-10-wildcat/blob/c5df665f0bc2ca5df6f06938d66494b11e7bdada/src/market/WildcatMarketBase.sol#L473) `_applyWithdrawalBatchPayment`

```solidity
  function _applyWithdrawalBatchPayment(
    WithdrawalBatch memory batch,
    MarketState memory state,
    uint32 expiry,
    uint256 availableLiquidity
  ) internal {
    uint104 scaledAvailableLiquidity = state.scaleAmount(availableLiquidity).toUint104(); //@audit revert if availableLiquidity > type(uint104).max;
```
4) **and this is where this it can break**: if `availableLiquidity` is bigger than `type(uint104).max`, the call will revert.
This is because of `SafeCastLib::toUint104()` which [revert on overflow](
When borrower closes the market, he won't pay all incurring debt while he was delinquency
Audit: 2023-10-wildcat-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-wildcat/blob/c5df665f0bc2ca5df6f06938d66494b11e7bdada/src/market/WildcatMarket.sol#L142
https://github.com/code-423n4/2023-10-wildcat/blob/c5df665f0bc2ca5df6f06938d66494b11e7bdada/src/libraries/MarketState.sol#L138
https://github.com/code-423n4/2023-10-wildcat/blob/c5df665f0bc2ca5df6f06938d66494b11e7bdada/src/libraries/FeeMath.sol#L89


# Vulnerability details

# The borrower does not pay all debts when he closes the market

## Lines of code
- https://github.com/code-423n4/2023-10-wildcat/blob/c5df665f0bc2ca5df6f06938d66494b11e7bdada/src/market/WildcatMarket.sol#L142
- https://github.com/code-423n4/2023-10-wildcat/blob/c5df665f0bc2ca5df6f06938d66494b11e7bdada/src/libraries/MarketState.sol#L138
- https://github.com/code-423n4/2023-10-wildcat/blob/c5df665f0bc2ca5df6f06938d66494b11e7bdada/src/libraries/FeeMath.sol#L89

## Impact

When borrower closes the market using the [WildMarket::closeMarket()](https://github.com/code-423n4/2023-10-wildcat/blob/c5df665f0bc2ca5df6f06938d66494b11e7bdada/src/market/WildcatMarket.sol#L142), he [must pay all debts](https://github.com/code-423n4/2023-10-wildcat/blob/c5df665f0bc2ca5df6f06938d66494b11e7bdada/src/market/WildcatMarket.sol#L154):

```solidity
File: WildcatMarket.sol
142:   function closeMarket() external onlyController nonReentrant {
...
...
151:     uint256 totalDebts = state.totalDebts();
152:     if (currentlyHeld < totalDebts) {
153:       // Transfer remaining debts from borrower
154:       asset.safeTransferFrom(borrower, address(this), totalDebts - currentlyHeld);
155:     } else if (currentlyHeld > totalDebts) {
...
...
```

The [totalDebts()](https://github.com/code-423n4/2023-10-wildcat/blob/c5df665f0bc2ca5df6f06938d66494b11e7bdada/src/libraries/MarketState.sol#L138) function calculates how much the borrower needs to pay back to the market. The problem is that the function does not count all the incurring debt when the borrower was in delinquent process. The penalty is increased when the [user is `delinquent` code line 97-111](https://github.com/code-423n4/2023-10-wildcat/blob/c5df665f0bc2ca5df6f06938d66494b11e7bdada/src/libraries/FeeMath.sol#L97) and the penalty is being decreased when the [time elapsed code line 115](https://github.com/code-423n4/2023-10-wildcat/blob/c5df665f0bc2ca5df6f06938d66494b11e7bdada/src/libraries/FeeMath.sol#L115).

```solidity
File: FeeMath.sol
089:   function updateTimeDelinquentAndGetPenaltyTime(
090:     MarketState memory state,
091:     uint256 delinquencyGracePeriod,
092:     uint256 timeDelta
093:   ) internal pure returns (uint256 /* timeWithPenalty */) {
094:     // Seconds in delinquency at last update
095:     uint256 previousTimeDelinquent = state.timeDelinquent;
096: 
097:     if (state.isDelinquent) {
098:       // Since the borrower is still delinquent, increase the total
099:       // time in delinquency by the time elapsed.
100:       state.timeDelinquent = (previousTimeDelinquent + timeDelta).toUi