xanoutas/solidity-security-findings huggingface dataset:
H-04 Unmitigated
Audit: 2024-09-karak-mitigation-findings
Severity: MEDIUM

# Lines of code

https://github.com/karak-network/karak-arena-mitigations/blob/475cfd73744cabe239720feec4a227a739910119/src/Core.sol#L261


# Vulnerability details

### H-04: **Violation of Invariant Allowing DSSs to Slash Unregistered Operators**

[Link to issue](https://github.com/code-423n4/2024-07-karak-findings/issues/4)

## **Comments**

The original implementation does not account for cases where an operator unregisters from a vault before a pending slash is finalized. This oversight allows slashing to occur on unregistered operators, violating the protocol's invariant that only registered operators should be subject to slashing.

## **Mitigation**

 [Fix link](https://github.com/karak-network/karak-arena-mitigations/commit/69644a7b1c3607aea5f876d9ee6be24035c1d9d2)

The mitigation addresses the issue by incorporating the `checkIfOperatorIsRegInRegDSS` function into the `finalizeSlashing` process. This ensures that the function checks whether the operator is still registered in the relevant DSS before finalizing the slash.

```solidity
    function finalizeSlashing(SlasherLib.QueuedSlashing memory queuedSlashing)
        external
        nonReentrant
        whenFunctionNotPaused(Constants.PAUSE_CORE_FINALIZE_SLASHING)
    {
        CoreLib.Storage storage self = _self();
        self.checkIfOperatorIsRegInRegDSS(queuedSlashing.operator, queuedSlashing.dss);
        self.finalizeSlashing(queuedSlashing);

        emit FinalizedSlashing(msg.sender, queuedSlashing);
    }
```

## **New issue**

The current mitigation introduces a new issue: when a slash is requested, the count of queued slashes for that vault is incremented. However, if the `finalizeSlashing` function reverts due to an operator being unregistered, the queued slash remains unaddressed, leading users to believe there are pending slashes when calling `isVaultQueuedForSlashing.` This situation may act as a deterrent for new deposits, as users would assume a pending slash.

```solidity
    /// @notice checks whether a slashing in requested on the given vault.
    /// @param vault address of the vault.
    /// @return boolean where true indicating vault is queued for slashing.
    function isVaultQueuedForSlashing(address vault) public view returns (bool) {
        uint256 count = _self().getSlashingsQueuedForVault(vault);
        return count != 0;
    }
```

Furthermore, since pending slashes have no deadline on them, if the operator re-registers in the future, the slash could incorrectly be finalized by anyone.

## **Recommended additional mitigation:**

To address this, the `finalizeSlashing` function could be modified to call the internal `cancelSlashing` function when the operator is no longer registered. This adjustment treats such cases similarly to cancelled slashes, preventing invalid slashes from lingering indefinitely and ensuring the queued slashes count reflects the true state of the vault.


## Assessed type

Other
M-05 Unmitigated
Audit: 2024-09-reserve-mitigation-findings
Severity: MEDIUM

We found the finding [M-05: Users can dodge losses due to StRSR era changes with instant operations
](https://github.com/code-423n4/2024-07-reserve-findings/issues/21) only partially mitigated.

The root cause of the original finding is that:
- stake and draft eras are reset independently - one can be reset without the other resetting
- stakers can move their funds between the stake and draft buckets without any cooldown/unbonding period

Both these aspects are still present in the code.
We acknowledge that allowing governance to reset both rates together decreases the likelihood of a single bucket resetting without the other.
However, the reason why this finding was judged as Medium severity, that is, [quoting the judge](https://github.com/code-423n4/2024-07-reserve-findings/issues/21#issuecomment-2322910972):

> [...] users can frontrun resetStakes to avoid losses or get benefits. if this is correct, I would consider this issue to be low likelihood + high impact = medium severity

still applies in full. The likelihood is lowered, but the fix relies solely on a Governance action, which may not be able to come in time due to its organic voting period and subsequent timelock execution.

For the issue to be mitigated in full, we recommend changing the `seizeRSR` logic to reset both rates whenever one passes its safe zone: this way, even if Governance doesn't (or can't) act with a `resetStakes` call, the attack is never possible.
Borrower cannot change market capacity (max total supply) after creating a 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

## Impact
As per docs, "Subsequent to launch, base APR and capacities can be adjusted by the borrower at will".

After creating a market, a borrower cannot increase the `maximum total supply` which contradicts the documentation.

## Proof of Concept

The [WildcatMarketConfig.setMaxTotalSupply()](https://github.com/code-423n4/2023-10-wildcat/blob/main/src/market/WildcatMarketConfig.sol#L134) only allows the `MarketController` to set the maximum total supply, however, no such function exist in the `WildcatMarketController.sol` contract that can call the `setMaxTotalSupply()` function in the deployed market.

So a borrower cannot change the max total supply of a market after deployment.

```solidity
 // WildcatMarketConfig.sol
  function setMaxTotalSupply(uint256 _maxTotalSupply) external onlyController nonReentrant {
    ...
    ...
  }

```

## Tools Used
Manual review

## Recommended Mitigation Steps
Add a function in `WildcatMarketController.sol` that allows the borrower to call [WildcatMarketConfig.setMaxTotalSupply()](https://github.com/code-423n4/2023-10-wildcat/blob/main/src/market/WildcatMarketConfig.sol#L134)


## Assessed type

Other
The `approve` function can be frontrun
Audit: 2023-10-wildcat-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-wildcat/blob/c5df665f0bc2ca5df6f06938d66494b11e7bdada/src/market/WildcatMarketToken.sol#L31-L34
https://github.com/code-423n4/2023-10-wildcat/blob/c5df665f0bc2ca5df6f06938d66494b11e7bdada/src/market/WildcatMarketToken.sol#L41-L57


# Vulnerability details

## Impact
Bob steals tokens from Alice.

## Proof of Concept
In the file `WildcatMarketToken.sol` there is an `approve` function:

```
  function approve(address spender, uint256 amount) external virtual nonReentrant returns (bool) {
    _approve(msg.sender, spender, amount);
    return true;
  }
```

that is susceptible to a frontrunning attack. Let's suppose Alice calls the `approve` function with `spender == address(Bob)` and `amount == 100`. Soon after that Alice decides that her allowance to Bob is too big and decides to reduce it - Alice calls the `approve` function again with `spender == address(Bob)` and reduced amount `amount == 50`. Bob monitors the Ethereum mempool and sees that Alice gave him some allowance and then reduced it. Bob is not happy with that. Bob waits for Alice's first transaction with `amount == 100` to pass. After this transaction is complete Bob has an allowance of `amount == 100`. Right after that Bob calls `transferFrom` function from the file `WildcatMarketToken.sol` frontrunning Alice's second transaction with `amount == 50`. As a result Bob transfers `amount == 100` to his address. After that Alice's second transaction with `amount == 50` is completed and Bob is allowed to spend 50 more units of a token. Bob calls `transferFrom` again and takes these 50 units of a token as well. As a result, Alice intended to give Bob an allowance of 50 units of a token, but Bob managed to take from Alice 150 units of a token, essentially stealing 100 units of a token.

## Tools Used
Manual review.

## Recommended Mitigation Steps
Use OZ libraries.


## Assessed type

MEV
potential DOS cause of rounding up at rayMul and rayDiv
Audit: 2023-10-wildcat-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-wildcat/blob/c5df665f0bc2ca5df6f06938d66494b11e7bdada/src/libraries/MathUtils.sol#L134-L149
https://github.com/code-423n4/2023-10-wildcat/blob/c5df665f0bc2ca5df6f06938d66494b11e7bdada/src/libraries/MathUtils.sol#L151-L166


# Vulnerability details

## Impact
In rayMul and rayDiv , there is always rounding up ,cause of that , there will be potential DOS

## Proof of Concept
    function normalizeAmount(
    MarketState memory state,
    uint256 amount
  ) internal pure returns (uint256) {
    return amount.rayMul(state.scaleFactor);
  }

  function scaleAmount(MarketState memory state, uint256 amount) internal pure returns (uint256) {
    return amount.rayDiv(state.scaleFactor);
  }

Cause of rounding up , they will take more than they should get , let protocol be insolvency

## Tools Used
manual view
## Recommended Mitigation Steps
use rounding down 


## Assessed type

DoS
Borrower escapes delinquency penalty if no intermittent action happens
Audit: 2023-10-wildcat-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
Once the market goes below required reserves, it is marked as delinquent only if an `updateState()` action happens. Actions like `market.updateState()`, `executeWithdrawal()`, `deposit()`, etc. have to happen else the protocol remains unaware of the market's delinquency and no penalty fees is applied on the borrower. 
The borrower can repay before any state updates happen and escape a penalty altogether.

## Proof of Concept
Save the following code in a new file inside the `test/market/` folder. 
- We'll first run `forge test --mt test_t0x1c_SomeAction_TimeLapse -vv` to confirm that the market goes into delinquency within 1 second due to interest accrual & protocol fee. The test should pass.
- Then run `forge test --mt test_t0x1c_NoAction_TimeLapse -vv` to see that in the absence of any explicit state updates, market is never delinquent. Then the borrower himself calls `market.stateUpdate()` after repayment and escapes his 2 hours of penalty.

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

import {MockERC20} from "solmate/test/utils/mocks/MockERC20.sol";

import "src/market/WildcatMarket.sol";
import "../shared/TestConstants.sol";
import "../helpers/Assertions.sol";
import "../shared/Test.sol";
import "../helpers/VmUtils.sol";
import "../helpers/MockController.sol";

contract t0x1cEscapePenalty is Test {
    using stdStorage for StdStorage;
    using FeeMath for MarketState;
    using SafeCastLib for uint256;
    using MathUtils for uint256;
    using FeeMath for uint256;

    MarketParameters internal t0x1cParameters;
    MockERC20 internal asset;

    function setUp() public {
        t0x1cParameters = MarketParameters({
            asset: address(0),
            namePrefix: "Wildcat ",
            symbolPrefix: "WC",
            borrower: borrower,
            controller: address(0),
            feeRecipient: feeRecipient,
            sentinel: address(sanctionsSentinel),
            maxTotalSupply: uint128(10_000e18),
            protocolFeeBips: 5000,
            annualInterestBips: 2000,
            delinquencyFeeBips: 5000,
            withdrawalBatchDuration: 0,
            reserveRatioBips: 1000,
            delinquencyGracePeriod: 0
        });

        if (address(controller) == address(0)) {
            deployController(t0x1cParameters.borrower, false, false);
        }
        t0x1cParameters.controller = address(controller);
        t0x1cParameters.asset = address(asset = new MockERC20('Token', 'TKN', 18));
        deployMarket(t0x1cParameters);
        _authorizeLender(alice);

        asset.mint(alice, 100e18);

        _approve(alice, address(market), type(uint256).max);
    }

    function _authorizeLender(address account) internal asAccount(t0x1cParameters.borrower) {
        address[] memory lenders = new address[](1);
        lenders[0] = account;
        controller.a
ERC20 tokens can be incorrectly burnt because of insufficient validation
Audit: 2023-10-wildcat-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-wildcat/blob/c5df665f0bc2ca5df6f06938d66494b11e7bdada/src/WildcatSanctionsSentinel.sol#L24-L32
https://github.com/code-423n4/2023-10-wildcat/blob/c5df665f0bc2ca5df6f06938d66494b11e7bdada/src/WildcatSanctionsEscrow.sol#L16-L19
https://github.com/code-423n4/2023-10-wildcat/blob/c5df665f0bc2ca5df6f06938d66494b11e7bdada/src/WildcatSanctionsEscrow.sol#L33-L42


# Vulnerability details

## Impact
ERC20 tokens are incorrectly burnt.

## Proof of Concept
In the file `WildcatSanctionsEscrow.sol` there is a `constructor` function:

```
  constructor() {
    sentinel = msg.sender;
    (borrower, account, asset) = WildcatSanctionsSentinel(sentinel).tmpEscrowParams();
  }
```

Let's suppose this `constructor` function is called. In such a case `sentinel` is set to `msg.sender` and the line:

```
   (borrower, account, asset) = WildcatSanctionsSentinel(sentinel).tmpEscrowParams();
```

is executed. Let's now suppose on the address of `sentinel` an instance of `WildcatSanctionsSentinel` has been deployed - this means that the `constructor` function from the file `WildcatSanctionsSentinel.sol` has been run:

```
  constructor(address _archController, address _chainalysisSanctionsList) {
    archController = _archController;
    chainalysisSanctionsList = _chainalysisSanctionsList;
    _resetTmpEscrowParams();
  }
```

At the end of the `constructor`, the function `_resetTmpEscrowParams`:

```
  function _resetTmpEscrowParams() internal {
    tmpEscrowParams = TmpEscrowParams(address(1), address(1), address(1));
  }
```

is run and `tmpEscrowParams` is set to `TmpEscrowParams(address(1), address(1), address(1));`.

Let's suppose the function `releaseEscrow` from the file `WildcatSanctionsEscrow.sol` is called next:

```
  function releaseEscrow() public override {
    if (!canReleaseEscrow()) revert CanNotReleaseEscrow();

    uint256 amount = balance();

    IERC20(asset).transfer(account, amount);

    emit EscrowReleased(account, asset, amount);
  }
}
```

and on the line:

```
IERC20(asset).transfer(account, amount);
```

the tokens are sent to `account`. However, before that, on the line:

```
(borrower, account, asset) = WildcatSanctionsSentinel(sentinel).tmpEscrowParams();
```

`account` has been set to `address(1)` and the `transfer` function sends the tokens to `address(1)`, essentially burning them, which is not the desired behavior.

## Tools Used
Manual review.

## Recommended Mitigation Steps
The function `releaseEscrow` should ensure that `account != address(1)`. That can be achieved with adding a `require` statement:

```
  function releaseEscrow() public override {
    if (!canReleaseEscrow()) revert CanNotReleaseEscrow();
    require(account != address(1), "Should not send tokens to reset account");

    uint256 amount = balance();

    IERC20(asset).transfer(account, amount);

    emit EscrowReleased(account, asset, amount);
  }
}
```





## Assessed type

Invalid Validation
Borrower cannot change the maximum supply
Audit: 2023-10-wildcat-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-wildcat/blob/main/src/market/WildcatMarketConfig.sol#L128-L144
https://github.com/code-423n4/2023-10-wildcat/blob/main/src/libraries/MarketState.sol#L59-L61
https://github.com/code-423n4/2023-10-wildcat/blob/main/src/market/WildcatMarket.sol#L53


# Vulnerability details

## Impact
Maximum supply cannot be increased and by extension the max deposit limit too. 

## Proof of Concept
`WildcatMarketConfig@setMaxTotalSupply` is only callable by the [WildcatMarketController](https://github.com/code-423n4/2023-10-wildcat/blob/main/src/WildcatMarketController.sol) but is not used anywhere there.

##### **`WildcatMarketConfig.sol`**
```solidity
  function setMaxTotalSupply(uint256 _maxTotalSupply) external onlyController nonReentrant {
    // ...
  }
```

## Tools Used
Manual Review

## Recommended Mitigation Steps
Add the missing logic to allow the borrower to set the maximum total supply in the `WildcatMarketController` or change the modifier from `onlyController` to `onlyBorrower` for `WildcatMarketConfig@setMaxTotalSupply`.


## Assessed type

Other
Usury from simple mistake
Audit: 2023-10-wildcat-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-wildcat/blob/c5df665f0bc2ca5df6f06938d66494b11e7bdada/src/WildcatMarketController.sol#L474-L481
https://github.com/code-423n4/2023-10-wildcat/blob/c5df665f0bc2ca5df6f06938d66494b11e7bdada/src/market/WildcatMarketConfig.sol#L186-L190


# Vulnerability details



## Impact
A borrower making a simple mistake might be forced to pay an extortionate interest rate for en extended period of time.

**Severity rating**
I was hovering between Medium and High on this one. Medium because it is based on a user mistake. On the other hand, the mistake seems very likely to happen with very likely significant losses. It seems that this would happen on such a regular basis that it must be considered an unacceptable risk within the protocol, and as such I think a High rating is warranted.

## Proof of concept
Suppose a borrower has borrowed most of what he can, and that the reserve ratio is at a normal relatively low level. He might want to attract more lenders by increasing the interest rate:
```solidity
/**
* @dev Modify the interest rate for a market.
* If the new interest rate is lower than the current interest rate,
* the reserve ratio is set to 90% for the next two weeks.
*/
function setAnnualInterestBips(
    address market,
    uint16 annualInterestBips
) external virtual onlyBorrower onlyControlledMarket(market) {
    // If borrower is reducing the interest rate, increase the reserve
    // ratio for the next two weeks.
    if (annualInterestBips < WildcatMarket(market).annualInterestBips()) {
    TemporaryReserveRatio storage tmp = temporaryExcessReserveRatio[market];

    if (tmp.expiry == 0) {
        tmp.reserveRatioBips = uint128(WildcatMarket(market).reserveRatioBips());

        // Require 90% liquidity coverage for the next 2 weeks
        WildcatMarket(market).setReserveRatioBips(9000);
    }

    tmp.expiry = uint128(block.timestamp + 2 weeks);
    }

    WildcatMarket(market).setAnnualInterestBips(annualInterestBips);
}
```
As can be seen he is free to do so. However, he might accidentally enter a significantly too high `annualInterestBips`, up to 100%. Then if he wants to correct his mistake, i.e. lower the interest rate, the reserve ratio must be set to 90%. [This reverts if it makes him delinquent](https://github.com/code-423n4/2023-10-wildcat/blob/c5df665f0bc2ca5df6f06938d66494b11e7bdada/src/market/WildcatMarketConfig.sol#L167-L169), which it most likely will since he was borrowing without excessive margin to delinquency at a lower reserve ratio such as 20%.
Therefore he will be forced to pay this exorbitant interest rate until he can return most of his loan, which he was not prepared to do, and which might be exceedingly difficult to do very quickly. Thus, he will be forced to pay an exorbitant interest rate until he can return most of his loan. This might take a long time.

Note that this is a trivial mistake that may very easily happen, and probably will happen regularly, and that when
New approved lender can receive other peoples accrued interest fees 
Audit: 2023-10-wildcat-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-10-wildcat/blob/c5df665f0bc2ca5df6f06938d66494b11e7bdada/src/libraries/FeeMath.sol#L142-L174


# Vulnerability details

A new approved Lender by the borrower, getting into the market at the right time can make huge profits in the market due to activity in the market of others, and accruing interest, which make the lender withdraw immediately, without being in the market for a long time or as long as others in the market have being.

## Proof of Concept
Lets explore how this will work

The market has being created for a while and people have being participating in the market and everything has being progressing as usual,

Then the borrower adds the LENDER, the LENDER is looking to make huge cash gains and fast studies how the market, and spots an opportunity, 

The opportunity comes, in the form of the Protocol using a scaled system to calculate accurately interest accrual for all participants in the market, so their initial deposit will also benefit from any interest accrual that happens in the market, so their rebasing market token will also reflect that profit

This interesting system Updates the scale factors from time to time, to try to keep updates on how much a participant in the system,

```solidity
function _getUpdatedState() internal returns (MarketState memory state) {
    state = _state;
    // Handle expired withdrawal batch
    if (state.hasPendingExpiredBatch()) {
      uint256 expiry = state.pendingWithdrawalExpiry;
      // Only accrue interest if time has passed since last update.
      // This will only be false if withdrawalBatchDuration is 0.
      if (expiry != state.lastInterestAccruedTimestamp) {
        (uint256 baseInterestRay, uint256 delinquencyFeeRay, uint256 protocolFee) = state
          .updateScaleFactorAndFees(
            protocolFeeBips,
            delinquencyFeeBips,
            delinquencyGracePeriod,
            expiry
          );
        emit ScaleFactorUpdated(state.scaleFactor, baseInterestRay, delinquencyFeeRay, protocolFee);
      }
      _processExpiredWithdrawalBatch(state);
    }
```

The vulnerability exploit can happen in the form, when the state is about to get updated in the Market Base Contract, the LENDER sees an action that the state is about to get updated, then they get involved, why ? Because they know that in the process of updating the state the scale factor also gets increased based on occurring actions in the market based on factors like base interest fee and the delinquency fee, Which will make the scale factor increase.

```solidity
function updateScaleFactorAndFees(
    MarketState memory state,
    uint256 protocolFeeBips,
    uint256 delinquencyFeeBips,
    uint256 delinquencyGracePeriod,
    uint256 timestamp
  )
    internal
    pure
    returns (uint256 baseInterestRay, uint256 delinquencyFeeRay, uint256 protocolFee)
  {
    baseInterestRay = state.calculateBaseInterest(timestamp);

    if (protocolFeeBips > 0) {
      protoco