xanoutas/solidity-security-findings huggingface dataset:
Malicious users can stake their NFT in the LandManager contract without paying landlord tax
Audit: 2024-07-munchables-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-07-munchables/blob/main/src/managers/LandManager.sol#L131-L171


# Vulnerability details

## Impact
In the **LandManager** contract users can stake their NFT in a certain plot and receive **schnibbles** as rewards. However when calling the [stakeMunchable()](https://github.com/code-423n4/2024-07-munchables/blob/main/src/managers/LandManager.sol#L131-L171) function, there is no check if a landlord has set a tax for a plot, as can be seen in the code snippet below:
```solidity
    function stakeMunchable(address landlord, uint256 tokenId, uint256 plotId) external override forceFarmPlots(msg.sender) notPaused {
        (address mainAccount, ) = _getMainAccountRequireRegistered(msg.sender);
        if (landlord == mainAccount) revert CantStakeToSelfError();
        if (plotOccupied[landlord][plotId].occupied) revert OccupiedPlotError(landlord, plotId);
        if (munchablesStaked[mainAccount].length > 10) revert TooManyStakedMunchiesError();
        if (munchNFT.ownerOf(tokenId) != mainAccount) revert InvalidOwnerError();

        /// INFO: There is no check if landlord has set tax or not
        uint256 totalPlotsAvail = _getNumPlots(landlord);
        if (plotId >= totalPlotsAvail) revert PlotTooHighError();

        if (!munchNFT.isApprovedForAll(mainAccount, address(this)) && munchNFT.getApproved(tokenId) != address(this)) revert NotApprovedError();
        munchNFT.transferFrom(mainAccount, address(this), tokenId);

        plotOccupied[landlord][plotId] = Plot({
            occupied: true,
            tokenId: tokenId
        });

        munchablesStaked[mainAccount].push(tokenId);
        munchableOwner[tokenId] = mainAccount;

        toilerState[tokenId] = ToilerState({
            lastToilDate: block.timestamp,
            plotId: plotId,
            landlord: landlord,
            latestTaxRate: plotMetadata[landlord].currentTaxRate,
            dirty: false
        });

        emit FarmPlotTaken(toilerState[tokenId], tokenId);
    }
```
As can be seen from the [mainnet deployments](https://github.com/code-423n4/2024-07-munchables/blob/main/deployments/cache/mainnet/deployment-2024-06-01T21-33-57.416Z.json) file, the other manager contracts have already been deployed(it was also confirmed by the sponsor in the public chat of the [contest](https://discordapp.com/channels/810916927919620096/1264356051415732224/1265754814348791828)). The contract has a couple of mechanism that tries to mitigate this issue however they are insufficient. The [updatePlotMetadata()](https://github.com/code-423n4/2024-07-munchables/blob/main/src/managers/LandManager.sol#L116-L129) function is intended to be invoked each time a user updates his lock position in the LockManager contract. However as LandManager is not yet deployed, and LockManager is, this won't set a tax for a landlord's plot when LandManager is first deployed. The second option is for the landlord to manually call the [triggerPlotMetadata()](ht
Missing access control CollateralTracker::startToken
Audit: 2024-04-panoptic-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-panoptic/blob/main/contracts/CollateralTracker.sol#L221-L264


# Vulnerability details

## Impact
There is missing access control, so any user can call the `startToken` and can set arbitary values

## Proof of Concept
https://github.com/code-423n4/2024-04-panoptic/blob/main/contracts/CollateralTracker.sol#L221-L264

## Tools Used
Manual code review

### Recommended Mitigation Steps
Add access control 


## Assessed type

Access Control
Core function can be called externally leading to DOS
Audit: 2024-04-panoptic-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-panoptic/blob/833312ebd600665b577fbd9c03ffa0daf250ed24/contracts/libraries/PanopticMath.sol#L241-L268
https://github.com/code-423n4/2024-04-panoptic/blob/833312ebd600665b577fbd9c03ffa0daf250ed24/contracts/PanopticPool.sol#L1450-L1452
https://github.com/code-423n4/2024-04-panoptic/blob/833312ebd600665b577fbd9c03ffa0daf250ed24/contracts/PanopticPool.sol#L1179
https://github.com/code-423n4/2024-04-panoptic/blob/833312ebd600665b577fbd9c03ffa0daf250ed24/contracts/PanopticPool.sol#L1017


# Vulnerability details

### **Summary**

Function can be called by anyone which could lead to DOS or returning a stale TWAP by providing zero as the twap window

### **Detailed Description**

If an attacker front-runs and calls the `twapFilter` function with 0 as the `twapWindow`, it would result in a division by zero error inside the function. This would cause the function to revert, as Solidity throws an exception when attempting to divide by zero.

Now, let's consider the subsequent behavior when the `liquidate` function attempts to get the TWAP tick using `int24 twapTick = getUniV3TWAP();`. Since the `twapFilter` function reverted due to a division by zero error, it did not produce a valid TWAP tick value. In this case, the value of `twapTick` would be undefined or could potentially revert as well if it doesn't handle the case where the TWAP calculation fails gracefully.

Assuming the value of `twapTick` remains undefined, the comparison `Math.abs(currentTick - twapTick) > MAX_TWAP_DELTA_LIQUIDATION` in the `liquidate` function would likely cause the function to revert. This is because attempting to perform arithmetic operations with an undefined value would result in unpredictable behavior and could lead to a revert due to an invalid comparison or arithmetic operation.

Therefore, in summary:
- The `twapFilter` function would revert due to a division by zero error.
- The value of `twapTick` obtained by the `liquidate` function would be undefined or could potentially revert.
- The comparison in the `liquidate` function would likely result in a revert due to attempting arithmetic operations with an undefined value. 


### **Impact:**

May become impossible to liquidate assets or forceExercise a position

### **Proof of Code**

```javascript
    function twapFilter(IUniswapV3Pool univ3pool, uint32 twapWindow) external view returns (int24) {
        uint32[] memory secondsAgos = new uint32[](20);

        int256[] memory twapMeasurement = new int256[](19);

        unchecked {
            // construct the time stots
        
            for (uint256 i = 0; i < 20; ++i) {
                secondsAgos[i] = uint32(((i + 1) * twapWindow) / 20);
            }
    
            // observe the tickCumulative at the 20 pre-defined time slots
            (int56[] memory tickCumulatives, ) = univ3pool.observe(secondsAgos);

            // compute the average tick per 30s window
            for (uint256 i = 0; i < 19; ++i) {
Use of `slot0` to get `sqrtPriceLimitX96` can lead to price manipulation.
Audit: 2024-04-panoptic-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-panoptic/blob/833312ebd600665b577fbd9c03ffa0daf250ed24/contracts/PanopticFactory.sol#L335-L411


# Vulnerability details

## Impact
use UniswapV3.slot0 to get the value of sqrtPriceX96, which is used to perform the swap. However, the sqrtPriceX96 is pulled from Uniswap.slot0, which is the most recent data point and can be manipulated easily via MEV bots and Flashloans with sandwich attacks; which can cause the loss of funds when interacting with the Uniswap.swap function.


## Proof of Concept
```solidity
    function _mintFullRange(
        IUniswapV3Pool v3Pool,
        address token0,
        address token1,
        uint24 fee
    ) internal returns (uint256, uint256) {
        (uint160 currentSqrtPriceX96, , , , , , ) = v3Pool.slot0();


        // For full range: L = Δx * sqrt(P) = Δy / sqrt(P)
        // We start with fixed token amounts and apply this equation to calculate the liquidity
        // Note that for pools with a tickSpacing that is not a power of 2 or greater than 8 (887272 % ts != 0),
        // a position at the maximum and minimum allowable ticks will be wide, but not necessarily full-range.
        // In this case, the `fullRangeLiquidity` will always be an underestimate in respect to the token amounts required to mint.
        uint128 fullRangeLiquidity;
        unchecked {
            // Since we know one of the tokens is WETH, we simply add 0.1 ETH + worth in tokens
            if (token0 == WETH) {
                fullRangeLiquidity = uint128(
                    Math.mulDiv96RoundingUp(FULL_RANGE_LIQUIDITY_AMOUNT_WETH, currentSqrtPriceX96)
                );
            } else if (token1 == WETH) {
                fullRangeLiquidity = uint128(
                    Math.mulDivRoundingUp(
                        FULL_RANGE_LIQUIDITY_AMOUNT_WETH,
                        Constants.FP96,
                        currentSqrtPriceX96
                    )
                );
            } else {
                // Find the resulting liquidity for providing 1e6 of both tokens
                uint128 liquidity0 = uint128(
                    Math.mulDiv96RoundingUp(FULL_RANGE_LIQUIDITY_AMOUNT_TOKEN, currentSqrtPriceX96)
                );
                uint128 liquidity1 = uint128(
                    Math.mulDivRoundingUp(
                        FULL_RANGE_LIQUIDITY_AMOUNT_TOKEN,
                        Constants.FP96,
                        currentSqrtPriceX96
                    )
                );


                // Pick the greater of the liquidities - i.e the more "expensive" option
                // This ensures that the liquidity added is sufficiently large
                fullRangeLiquidity = liquidity0 > liquidity1 ? liquidity0 : liquidity1;
            }
        }


        // The maximum range we can mint is determined by the tickSpacing of the pool
        // The upper and lower ticks must be divisible by `tickSpacing`, so
        // tickSpacing = 1: tU/L
Incorrect Exponentiation
Audit: 2024-04-panoptic-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-panoptic/blob/833312ebd600665b577fbd9c03ffa0daf250ed24/contracts/libraries/Math.sol#L414


# Vulnerability details

## Impact
The vulnerability identified in the `Math.mulDiv` function within the Solidity contracts and libraries involves the use of the bitwise-xor operator (`^`) instead of the exponentiation operator (`**`). This error is present in the location: `(contracts/libraries/Math.sol#341-434)`. The incorrect use of the bitwise-xor operator for exponentiation can lead to unexpected behavior and incorrect calculations, especially in financial and mathematical operations where precision is crucial.

## Proof of Concept
https://github.com/code-423n4/2024-04-panoptic/blob/833312ebd600665b577fbd9c03ffa0daf250ed24/contracts/libraries/Math.sol#L414

To demonstrate the impact of this vulnerability, consider the following example:
```solidity
// Custom contract
function incorrectExponentiation() public pure returns (uint256) {
    uint256 denominator = 2;
    uint256 inv = (3 * denominator) ^ 2; // Incorrect use of ^ instead of **
    return inv;
}

// OpenZeppelin contract
function incorrectExponentiationOpenZeppelin() public pure returns (uint256) {
    uint256 denominator = 2;
    uint256 inverse = (3 * denominator) ^ 2; // Incorrect use of ^ instead of **
    return inverse;
}
```
In both functions, the intention is to calculate the square of (`3 * denominator`). However, due to the use of the bitwise-xor operator (`^`), the result will not be the square of the intended value but rather the result of a bitwise XOR operation, which is not the intended mathematical operation.

## Tools Used
Slither was used to identify this vulnerability.

## Recommended Mitigation Steps
To mitigate this vulnerability, the bitwise-xor operator (^) should be replaced with the exponentiation operator (**) in the instance.


## Assessed type

Math
First depositor to call `CollateralTracker.deposit` function will lose his funds, since `previewDeposit` will return zero shares to mint
Audit: 2024-04-panoptic-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-panoptic/blob/main/contracts/CollateralTracker.sol#L401-L407
https://github.com/code-423n4/2024-04-panoptic/blob/main/contracts/CollateralTracker.sol#L425-L432


# Vulnerability details

## Impact

The `CollateralTracker.previewDeposit` function is used by the `CollateralTracker.deposit` function and the amount of `shares` to mint is calculated as follows:

```solidity
        unchecked {
            shares = Math.mulDiv( //@audit-info - commissions are only paid when a new position is minted
                assets * (DECIMALS - COMMISSION_FEE), //@audit-info - COMMISSION_FEE -> single payment of the commissionRate on the FINAL (post mev-tax) assets paid
                totalSupply,
                totalAssets() * DECIMALS
            );
```

But the issue here is for the first depositor the `totalSupply == 0` (no shares are minted yet). As a result the calculated number of shares for the provided asset amount will be `0`. The `CollateralTracker.deposit` function will transfer the `assets` amount to the respective `PanopticPool` but the `shares minted to the receiver will be 0`. As a result the `msg.sender or the receiver` has lost his funds.

If another user `mints` shares by calling the `CollateralTracker.mint` function as the first depositor by supplying the required number of assets, then he can withdraw the funds of the previous depositor (who lost the funds) as well by redeeming his shares thus profiting as a result.

## Proof of Concept

```solidity
        unchecked {
            shares = Math.mulDiv(
                assets * (DECIMALS - COMMISSION_FEE),
                totalSupply,
                totalAssets() * DECIMALS
            );
        }
```

https://github.com/code-423n4/2024-04-panoptic/blob/main/contracts/CollateralTracker.sol#L401-L407

```solidity
            s_underlyingToken,
            msg.sender,
            address(s_panopticPool),
            assets
        );

        // mint collateral shares of the Panoptic Pool funds (this ERC20 token)
        _mint(receiver, shares);
```

https://github.com/code-423n4/2024-04-panoptic/blob/main/contracts/CollateralTracker.sol#L425-L432

## Tools Used
Manual Review and VSCode

## Recommended Mitigation Steps

Hence it is recommended to assign `assets` amount to the `shares` variable in the `CollateralTracker.previewDeposit` function, such that the first depositor calling the `deposit` function will not lose his funds.


## Assessed type

ERC4626
Contract can be front run and set with a malicious owner upon deployment
Audit: 2024-04-panoptic-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-panoptic/blob/833312ebd600665b577fbd9c03ffa0daf250ed24/contracts/PanopticFactory.sol#L134-L139


# Vulnerability details

### **Summary**

The `s_owner` variable which is meant to be a delicate variable is lax and can be easily highjacked

### **Detailed Description**

The `s_owner` is a delicate variable and should be handled with care and It should be instantiated upon construction.
It is currently being called by a `initialize` function which has no access control, this could lead to the `s_owner` variable being frontrun and highjacked by an attacker. 


### **Impact:**

Contract can be highjacked by a malicious user leading to break in the overall protocol.

### **Proof of Code**

```javascript
    function initialize(address _owner) public {
        if (!s_initialized) {
            s_owner = _owner;
            s_initialized = true;
        }
    }
```

### **Tool used:**

Manual Review

### **Recommended Mitigation:**

The `s_owner` variable should be set in a constructor or the protocol should implement access control on the `initialize` function.


## Assessed type

Access Control
No check if TWAP is outside of bounds for position exercise allows to steal from users
Audit: 2024-04-panoptic-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-panoptic/blob/833312ebd600665b577fbd9c03ffa0daf250ed24/contracts/PanopticPool.sol#L1231-L1266
https://github.com/code-423n4/2024-04-panoptic/blob/833312ebd600665b577fbd9c03ffa0daf250ed24/contracts/CollateralTracker.sol#L650-L734


# Vulnerability details

When liquidating, current tick is checked against 10 minutes TWAP:
```javascript
        int24 twapTick = getUniV3TWAP();
// [...]
        {
            (, int24 currentTick, , , , , ) = s_univ3pool.slot0();

            // Enforce maximum delta between TWAP and currentTick to prevent extreme price manipulation
            if (Math.abs(currentTick - twapTick) > MAX_TWAP_DELTA_LIQUIDATION)
                revert Errors.StaleTWAP();
```

 This is not done for forceExercise. While both have different tasks, force exercise should pay fees, the farther-the-money, the cheaper it should be to force exercise an option position. Additionally, there's a concept of compensating user for loss in value if chunk has lost money between current and median tick, which can be exploited into user whose position is exercised will pay for the exercise.

## Impact


## Proof of Concept
When position is exercised, current spot price tick is taken together with 10 minutes TWAP:
```javascript
    function forceExercise(
// [...]
        int24 twapTick = getUniV3TWAP();

        (, int24 currentTick, , , , , ) = s_univ3pool.slot0();
// [...]
        // on forced exercise, the price *must* be outside the position's range for at least 1 leg
        touchedId[0].validateIsExercisable(twapTick);

```

In order to proceed, token is validated if it's exercisable, i.e. if at least 1 of the long legs is over tickSpacing apart from strike price:
```javascript
    function validateIsExercisable(TokenId self, int24 currentTick) internal pure {
        //[...]
                if ((currentTick >= _strike + rangeUp) || (currentTick < _strike - rangeDown)) {
                    // if this leg is long and the price beyond the leg's range:
                    // this exercised ID, `self`, appears valid
                    if (self.isLong(i) == 1) return; // validated
                }
            }
        }
        // Fail if position has no legs that is far-out-of-the-money
        revert Errors.NoLegsExercisable();
    }
```

Following, user whose account is exercised is temporarily delegated virtual shares, to make sure that their position can be settled during burning the position from the user. They will have those shares refunded later:
```javascript
        // The protocol delegates some virtual shares to ensure the burn can be settled.
        s_collateralToken0.delegate(account, uint128(delegatedAmounts.rightSlot()));
        s_collateralToken1.delegate(account, uint128(delegatedAmounts.leftSlot()));

        // Exercise the option
        // Note: tick limits are not applied here since it is not the exercisor's position being closed
        _burnAllOptionsFrom(account, 0,
mintTokenizedPosition doesn't use slippage protection although slippageTickLimitLow, slippageTickLimitHigh are provided in parameters
Audit: 2024-04-panoptic-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-panoptic/blob/833312ebd600665b577fbd9c03ffa0daf250ed24/contracts/SemiFungiblePositionManager.sol#L504-L508
https://github.com/code-423n4/2024-04-panoptic/blob/833312ebd600665b577fbd9c03ffa0daf250ed24/contracts/SemiFungiblePositionManager.sol#L718
https://github.com/code-423n4/2024-04-panoptic/blob/833312ebd600665b577fbd9c03ffa0daf250ed24/contracts/SemiFungiblePositionManager.sol#L837-L845


# Vulnerability details

## Impact
Slippage protection won't be provided to users even though they set parameters for doing so.

## Proof of Concept
`mintTokenizedPosition` takes slippageTickLimitLow and slippageTickLimitHigh as parameters to provide slippage protection to user while swapping for ITM options in Uniswap V3, however they're not used as the default MAX and MIN possible sqrtPriceX96 is used instead. Providing zero slippage protection.

`mintTokenizedPosition` -> `_validateAndForwardToAMM` -> `swapInAMM`
### mintTokenizedPosition
```solidity
    function mintTokenizedPosition(
        TokenId tokenId,
        uint128 positionSize,
        int24 slippageTickLimitLow,
        int24 slippageTickLimitHigh
    )
{
    .
    .
        // validate the incoming option position, then forward to the AMM for minting/burning required liquidity chunks
        (collectedByLeg, totalSwapped) = _validateAndForwardToAMM(
            tokenId,
            positionSize,
            slippageTickLimitLow,
            slippageTickLimitHigh,
            MINT
        );
}
```

### _validateAndForwardToAMM

```solidity
    /// @param tokenId the option position
    /// @param positionSize the size of the position to create
@>  /// @param tickLimitLow lower limits on potential slippage // not used much
@>  /// @param tickLimitHigh upper limits on potential slippage
    /// @param isBurn is equal to false for mints and true for burns
    /// @return collectedByLeg An array of LeftRight encoded words containing the amount of token0 and token1 collected as fees for each leg
    /// @return totalMoved the total amount of funds swapped in Uniswap as part of building potential ITM positions
    function _validateAndForwardToAMM(
        TokenId tokenId,
        uint128 positionSize,
        int24 tickLimitLow,
        int24 tickLimitHigh,
        bool isBurn
    ) 
{
    .
    .
        if (tickLimitLow > tickLimitHigh) {
            // if the in-the-money amount is not zero (i.e. positions were minted ITM) and the user did provide tick limits LOW > HIGH, then swap necessary amounts
            if ((LeftRightSigned.unwrap(itmAmounts) != 0)) {
                totalMoved = swapInAMM(univ3pool, itmAmounts).add(totalMoved);
            }

            (tickLimitLow, tickLimitHigh) = (tickLimitHigh, tickLimitLow);
        }
   .
   .
}
```

### Swap in AMM
```solidity
function swapInAMM(
        IUniswapV3Pool univ3pool,
        LeftRightSigned itmAmounts
    ) internal
{
            .
            .
            (int256 swap0, int256
Use of delegatecall in a payable function inside a loop
Audit: 2024-04-panoptic-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-panoptic/blob/833312ebd600665b577fbd9c03ffa0daf250ed24/contracts/multicall/Multicall.sol#L12


# Vulnerability details

## Impact
The Multicall contract uses the delegatecall proxy pattern (which takes user-provided call data) in a payable function within a loop. This means that each delegatecall within the for loop will retain the msg.value of the transaction
 


see :
https://solodit.xyz/issues/use-of-delegatecall-in-a-payable-function-inside-a-loop-trailofbits-yield-v2-pdf
 
## Tools Used
Manual Review

## Recommended Mitigation Steps

- Short term, document the risks associated with the use of msg.value and ensure that all developers are aware of this potential attack vector.
- Long term, detail the security implications of all functions in both the documentation and the code to ensure that potential attack vectors do not become exploitable when code is refactored or added.


## Assessed type

call/delegatecall