xanoutas/solidity-security-findings huggingface dataset:
Liquidations can be DoS by removing liquidity from the respective position, in the UniswapV3 pool by creating long positions
Audit: 2024-04-panoptic-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-panoptic/blob/main/contracts/PanopticPool.sol#L1086-L1092
https://github.com/code-423n4/2024-04-panoptic/blob/main/contracts/SemiFungiblePositionManager.sol#L1013-L1018


# Vulnerability details

## Impact

The `PanopticPool.liquidate` function is used to `liquidate` a `distressed account` by burning all the positions of a `liquidatee`. In the `liquidate` function the `PanopticPool._burnAllOptionsFrom` internal function is called. In the execution flow of the `PanopticPool._burnAllOptionsFrom` function the `SemiFungiblePositionManager._createLegInAMM` function is called to create a the respective position in the `AMM` for a `specific leg` in the `tokenId`. 

In the `SemiFungiblePositionManager._createLegInAMM` function uses the following condition to check whether the respective `position` has `enough liquidity` in the `UniswapV3Pool` to be removed for a respective long position. In the event of a `liquidation` this check will be performed when closing a `short position` by creating a `long position`.

```solidity
                 if (startingLiquidity < chunkLiquidity) {
```

The issue here is if a malicious user front-runs the `liquidation` transaction and create a considerably `large long position` (within the same tick range of tickLower - tickUpper in the UniswapV3 pool) making the `startingLiquidity < chunkLiquidity` then the subsequent liquidation transaction will revert since `startingLiquidity < chunkLiquidity` condition will result in `true` and the `if statement` will revert.

Hence the `liquidations` could revert as a result till more liquidity is added to that `liquidity position` in the respective `UniswapV3Pool`. If the `liquidations` can not be performed as a result of reverting due to above vulnerability, the entire panoptic protocol could become insolvent thus putting the user funds of the `PLPs` and the `options creators` in danger since they could lose the provided funds to the Panoptic protocol.

## Proof of Concept

```solidity
            (netExchanged, premiasByLeg) = _burnAllOptionsFrom(
                liquidatee,
                Constants.MIN_V3POOL_TICK,
                Constants.MAX_V3POOL_TICK,
                DONOT_COMMIT_LONG_SETTLED,
                positionIdList
            );
```

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

```solidity
                if (startingLiquidity < chunkLiquidity) {
                    // the amount we want to move (liquidityChunk.legLiquidity()) out of uniswap is greater than
                    // what the account that owns the liquidity in uniswap has (startingLiquidity)
                    // we must ensure that an account can only move its own liquidity out of uniswap
                    // so we revert in this case
                    revert Errors.NotEnoughLiquidity();
```

https://github.com/code-423n4/2024-04-panoptic/blob/main/contracts/SemiFungiblePositionManager.sol#
Minted positions leg can be override leading to unexpected change in position.
Audit: 2024-04-panoptic-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-panoptic/blob/833312ebd600665b577fbd9c03ffa0daf250ed24/contracts/PanopticPool.sol#L547-L561


# Vulnerability details

### **Summary**

A user can knowingly or unknowingly override a minted position leg index with a different leg containing a different configuration.

### **Detailed Description**

There are checks actually put in place by the protocol to safe guard against this, but it some how still break through by allowing users to knowingly or unknowingly override a minted position leg index with a different leg containing a different configurations such as the options type, assets and so on. This 

Example; 
- Bob mints a position by calling the `mintOptions` function after configuring the legs in the `positionIdList` array by passing in the required values and starting at an index of 0 tokenId leg.

- AFter a while, bob can either knowingly mint another position still in the `positionIdList` by configuring a tokenId with a leg and specifying an index of 0 instead of 1 which is meant to be the next legIndex.

- This overrides the existing index of 1 and clears out the previous index of 0 leading to a deletion of that particular position.

### **Impact:**

This is a break in core contracts logic as the protocol was meant to be resilient against such actions.

### **Proof of Code**

After running the following test, check the ` tokenId.countLegs()` and it will be 1 as opposed to actual value of 2

Run the following test in the projects test suit using the same setup to validate the bug
```solidity
    function test_overridingLegIndex(uint256 x, uint104 amount, uint256 widthSeed, int256 strikeSeed, uint128 positionSizeSeed ) public {
        _initWorld(x);

        // Invoke all interactions with the Collateral Tracker from user Bob
        vm.startPrank(Bob);

        // give Bob the max amount of tokens
        _grantTokens(Bob);

        // approve collateral tracker to move tokens on Bob's behalf
        IERC20Partial(token0).approve(address(collateralToken0), amount);
        IERC20Partial(token1).approve(address(collateralToken1), amount);

        // deposit a number of assets determined via fuzzing
        // equal deposits for both collateral token pairs for testing purposes
        collateralToken0.deposit(amount, Bob);
        collateralToken1.deposit(amount, Bob);

        // call will be minted in range
        (width, strike) = PositionUtils.getOTMSW(
            widthSeed,
            strikeSeed,
            uint24(tickSpacing),
            currentTick,
            1
        );

        // open SELL(short) POSITION as Bob - isLong = 0
        tokenId = TokenId.wrap(0).addPoolId(poolId).addLeg(0, 1, 1, 0, 0, 0, strike, width);
        positionIdList.push(tokenId);

        /// calculate position size
        (legLowerTick, legUpperTick) = tokenId.asTicks(0);
        positionSize0 = uint128(bound(positionSizeSeed, 2, 2 ** 104));
        _assumePositionValidity(Bob, tokenId, pos
Unsafe casting of `int256` to `uint256` can prompt the `s_settledTokens` mapping into a broken state
Audit: 2024-04-panoptic-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-panoptic/blob/main/contracts/PanopticPool.sol#L1650-L1652
https://github.com/code-423n4/2024-04-panoptic/blob/main/contracts/PanopticPool.sol#L473
https://github.com/code-423n4/2024-04-panoptic/blob/main/contracts/PanopticPool.sol#L1892


# Vulnerability details

## Impact

The `PanopticPool.settleLongPremium` function is used to settle all unpaid premium for `long legs` on `tokenIds` of `owners`. In the function exeuction of the `PanopticPool.settleLongPremium`, the `realizedPremia` is deducted from the `owner's balance` and then added to the cumulative settled token delta as shown below:

```solidity
            s_collateralToken0.exercise(owner, 0, 0, 0, realizedPremia.rightSlot());
            s_collateralToken1.exercise(owner, 0, 0, 0, realizedPremia.leftSlot());

            ...
            ...

            s_settledTokens[chunkKey] = s_settledTokens[chunkKey].add(
                LeftRightUnsigned.wrap(uint256(LeftRightSigned.unwrap(realizedPremia)))
            );
```

But the issue here is that the `LeftRightSigned.unwrap(realizedPremia)` value which is a `int256` is converted into a `uint256` unsafely and then added onto the `s_settledTokens[chunkKey]` storage. Since the `int256` holds negative values in `two's complement format`, unsafe casting of a negative `LeftRightSigned.unwrap(realizedPremia)` value to a `uint256` will result in a large positive value.  

Hence this will add a large value to the `s_settledTokens[chunkKey]` (where as it should reduce by that value since the value is negative) thus breaking the state of the `s_settledTokens` mapping. As a result the amount of tokens owed to sellers that have been settled for that specific chunk is now errorneous. 

Since the `s_settledTokens[chunkKey]` storage value is broken, the critical functions such as `_calculateAccumulatedPremia`, `liquidate`, `_updateSettlementPostMint` and `_updateSettlementPostBurn` will also prompt into undesirable state. Since this errorneous `s_settledTokens` value is used in the `_calculateAccumulatedPremia` function to calculate the `portfolioPremium` value, and this value is used as the `premia` value to calculate the margin requirement in the `PanopticPool.liquidate` function by calling the `CollateralTracker.getAccountMarginDetails`, this could result in errorneous values for `balanceCross` and `thresholdCross`. Due to these errorneous values the positions which are not liquidatable could appear as liquidatable positions and vice versa. If positions which are liqudatable can not be liquidated as a result of the above vulnerability this could prompt the Panoptic protocol into insolvency.

Similar issue of errorneous casting of `LeftRightSigned.unwrap(realizedPremia)` to a `uint256` are found in the `PanopticPool._calculateAccumulatedPremia` and `PanopticPool._updateSettlementPostBurn` functions as well.


## Proof of Concept

```solidity
            s_settledTokens[chunkKey] = s_settledTokens[ch
Discrepancies in in Token Conversion
Audit: 2024-04-panoptic-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
this is can  affect liquidity providers by misrepresenting the value of provided liquidity, and impact traders by calculating incorrect token amounts during swaps, and influence margin calculations for leveraged positions.
##  Root of the bug and summary 
there is a  logical flaw exists within the token conversion functions, convert0to1 and convert1to0, due to the handling of reduced precision at high sqrtPriceX96 values. The functions employ two different formulas to convert amounts between token0 and token1, the issue is arises in the convert0to1 and convert1to0 functions when handling conversions involving very high sqrtPriceX96 values.
```solidity
  // above that tick, we are forced to reduce the amount of decimals in the final price by 2**64 to 2**128
            if (sqrtPriceX96 < type(uint128).max) {
                return Math.mulDiv192(amount, uint256(sqrtPriceX96) ** 2);
            } else {
                return Math.mulDiv128(amount, Math.mulDiv64(sqrtPriceX96, sqrtPriceX96));
            }
        }
```
the bug is cause is the conditional that use of different mathematical precision levels based on the sqrtPriceX96 value, and the reduced precision formula drastically alters the output for high sqrtPriceX96 values, resulting in conversions that diverge substantially from those calculated with normal precision.

## Proof of Concept

let's say a scenario that we have 
- token Amount: 1,000 tokens of token0
- Square Root Price (sqrtPriceX96) => the square root of the price ratio between token0 and token1 scaled by 296296.
For this scenario, let's consider a very high value, such as 270270, to test the system at extreme conditions.
- Using two different methods for the conversion, where:
Full Precision method uses the formula:
$Converted Amount=Amount×(sqrt(PriceX96))2×2^{-192} $
Reduced Precision method for high sqrtPriceX96 values:
$Converted Amount=Amount×(sqrt(PriceX96×2^{64}))2×2^{-128} $

in full Precision Calculation the result is extremely small non-zero value close to zero but not zero
and in Reduced Precision Calculation the result in a value that rounds down to zero due to the large negative exponent.
## Tools Used
manual review
## Recommended Mitigation Steps

need to ensures that the precision is only reduced when absolutely necessary and does so in a manner that lessens the impact on the final conversion outcome


## Assessed type

Other
Accounts with a large amount of position can become unliquidable, resulting in bad debt
Audit: 2024-04-panoptic-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-panoptic/blob/833312ebd600665b577fbd9c03ffa0daf250ed24/contracts/PanopticPool.sol#L547
https://github.com/code-423n4/2024-04-panoptic/blob/833312ebd600665b577fbd9c03ffa0daf250ed24/contracts/PanopticPool.sol#L1017


# Vulnerability details

## Impact
An account can become unliquidatable if it holds a large number of positions, as there is no maximum cap. Users can intentionally make their accounts unliquidatable in this manner, resulting in bad debt for the protocol.

## Proof of Concept

Run `export FOUNDRY_PROFILE=ci_test && forge test --match-test test_success_liquidate --gas-report`:

```
	| test/foundry/core/PanopticPool.t.sol:PanopticPoolHarness contract |                 |         |         |         |         |
	|-------------------------------------------------------------------|-----------------|---------|---------|---------|---------|
	| Deployment Cost                                                   | Deployment Size |         |         |         |         |
	| 5221337                                                           | 24167           |         |         |         |         |
	| Function Name                                                     | min             | avg     | median  | max     | # calls |
	| burnAllOptionsFrom                                                | 482399          | 524409  | 524409  | 566420  | 2       |
	| calculateAccumulatedFeesBatch                                     | 52381           | 52381   | 52381   | 52381   | 6       |
	| collateralToken0                                                  | 553             | 553     | 553     | 553     | 4       |
	| collateralToken1                                                  | 432             | 432     | 432     | 432     | 4       |
	| getUniV3TWAP_                                                     | 333699          | 343199  | 333699  | 371699  | 8       |
@>	| liquidate                                                         | 1059873         | 1087657 | 1087657 | 1115442 | 2       |
@>	| mintOptions                                                       | 440870          | 555757  | 560100  | 755233  | 8       |
	| onERC1155Received                                                 | 1471            | 1471    | 1471    | 1471    | 8       |
	| settledTokens                                                     | 614             | 614     | 614     | 614     | 14      |
	| startPool                                                         | 219089          | 219089  | 219089  | 219089  | 2       |
```

`mintOptions` is cheaper than `liquidate`, so a user can continue to add positions to their account until `liquidate` runs out of gas. There isn't a hard cap on the number of positions that an account can have, so at one point, [`mintOptions`](https://github.com/code-423n4/2024-04-panoptic/blob/833312ebd600665b577fbd9c03ffa0daf250ed24/contracts/PanopticPool.sol#L547) will not run OOS, but [`liquidat
Unsafe casting could prompt the critical functions of the Panoptic protocol into `DoS`
Audit: 2024-04-panoptic-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact

The `CollateralTracker.takeCommissionAddData` function and `CollateralTracker.exercise` functions are used to take the commission amount during option creation and exercise an option and pay to the seller what is owed from the buyer, respectively.

Both the above two functions update the `s_inAMM` (The total assets in the UniswapV3Pool related to the Panoptic protocol) value at the end of their function execution to account for the updated asset values as a result of the operations as shown below:

            s_inAMM = uint128(uint256(int256(uint256(s_inAMM)) - (shortAmount - longAmount)));

The issue here is that `int256 to uint256` type casting is unsafe and could result in erroneous value thus breaking the state of the `s_inAMM` value.

Let's assume the following scenario:

1. The `CollateralTracker` contract has been deployed and the `s_inAMM` is initially set to 0 (This would be the case when the s_inAMM is a small value when enough funds are transferred to the UniswapV3 pool at the beginning) . 
2. A malicious user front-runs other transactions with a `PanopticPool.mintOptions` transaction, which makes a subsequent call to the `CollateralTracker.takeCommissionAddData` function such that `shortAmount > longAmount`.
3. As a result the `(shortAmount - longAmount)` value is `> 0` for the current option being minted.
4. Since the `s_inAMM == 0` the `int256(uint256(s_inAMM)) - (shortAmount - longAmount)` will result in a negative value and this negative value is represented in two's complement.
5. For example the `-1` will be represented with all bits set to `1` in the `int256` binary representation.
6. The issue is when this gets casted to `uint256` this two's complement of negative representation is not taken into account and directly casts it to `uint256`. Hence if the converted value is `-1` then the converted value into uint256 will be the `type(uint256).max` which gets downcast to the uint128 and as a result the `s_inAMM` will have the maximum value of `type(uint128).max`.
7. As a result the `s_inAMM` enters into an erroneous and broken state that can not be rectified and will negatively affect the subsequent critical transactions of the panoptic protocol as described below.

This could prompt all the subsequent operations which uses the `totalAssets()` function such as `previewDeposit, previewMint, previewWithdraw, _poolUtilization, revoke, takeCommissionAddData, excercise` functions performing erroneous operations (with wrong totalAssets() returned value) which results in broken state of the panoptic protocol.

Further if the `-1` is casted into `uint256` then since the `s_inAMM` is assigned `type(uint128).max` value any subsequent addition to the `s_inAMM` will result in transaction revert due to
Option positions can be forced to be always `in-the-money` by manipulating UniswapV3 pool via flash loans
Audit: 2024-04-panoptic-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-panoptic/blob/833312ebd600665b577fbd9c03ffa0daf250ed24/contracts/PanopticPool.sol#L547-L561


# Vulnerability details

## Description
Opening an option position will result into two things: `in-the-money` and `out-of-the-money`. In the traditional finance, if a trader correctly predicts the direction of the price of an asset, his option position will be `in-the-money` and at that point, factoring his transaction costs, he/she can close his/her position (by selling the option) or exercise the option for immediate profit. 

Making your positions to be always `in-the-money` and make profit is not a guarantee. That is the probabilistic nature of financial markets in general. 

That might not be the case for perpetual options in blockchain in general. Since liquidity pools can be manipulated, the trader's options position can always be forced to be `in-the-money`.

Let's take this scenario and assume that the following is in an atomic transaction:
- A trader is bullish on WETH/USDC, decides to open a long call position. 
- The trader flash loans and imbalances the pool such that WETH price increases relative to USDC, basically manipulating the WETH to temporarily increase in price. 
- Now that the call position is `in-the-money` the trader can either close the position or exercise the option for profit.
- Repeat

Options are innately leveraged. A few tweaks and there the traders can tune the right amount of flash loan and options positions to produce a profit. They can also do flash loans on both sides: for manipulating the liquidity pool and the opening of the options positions itself.

## Impact
If most options trades results to wins, at some point the protocol will suffer insolvency. 

## Proof of Concept
## Tools Used
Manual Review

## Recommended Mitigation Steps
Do not allow the `opening of the options positions` and `closing the positions (or exercising options)` to be in the same transaction block. As to how many blocks distance in the future should it be, the protocol team has to decide on this.








## Assessed type

Other
User can mint free shares by opening positions in the pool
Audit: 2024-04-panoptic-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
An attacker can mint large amount of shares in the CollateralTracker with no deposit simply by placing a carefully constructed position.

## Proof of Concept
Unfortunately, I didn't have much time to research this exploit in depth, there may be other ways to reproduce it, and it's exact nature is unknown to me. I'll just leave steps to reproduce this bug and some of my speculations on it's origin below.

We start with a clear pool, `totalSupply` = 10e6, `totalAssets` = 1, the forge test is `PanopticPool.t.sol`:

```solidity
    function test_FreeMint(
    ) public {
        uint256 x = 111;
        _initPool(x);

        // It seems like a negative strike value causes the bug, the lower the strike the number of shares minted
        TokenId tokenId = TokenId.wrap(0).addPoolId(poolId).addLeg(0,1,1,0,0,0,-300000,5760000);

        TokenId[] memory posIdList = new TokenId[](1);
        posIdList[0] = tokenId;

        console.log("\nSTART MINT\n");

        console.log("SHARES BEFORE: ", ct0.balanceOf(Alice));
        // Attacker sends tokens directly to the PP
        deal(token0, address(pp), 100000);
        deal(token1, address(pp), 100000);

        // Deposit in CT1 so we are solvent
        ct1.deposit(100e18, Alice);

        // Mint a position in PP
        pp.mintOptions(posIdList, 100000, 0, 0, 0);
        console.log("SHARES AFTER: ", ct0.balanceOf(Alice));
    }
```
In the following test, Alice starts with 0 shares, donates 100000 underlying tokens to the PP and mints a position, after this her balance is 1065765340334735623000000.

Some logs:
On position mint the pool calls collateral tracker `takeCommissionAddData` function with following parameters:
- longAmount = 0
- shortAmount = 1067045795289168526
- swappedAmount = 85917
It appears that a huge shortAmount allows minting tokens to the caller
```solidity
int256 tokenToPay = _getExchangedAmount(longAmount, shortAmount, swappedAmount);
```
The returned tokenToPay value is -1065765340334735623.

https://github.com/code-423n4/2024-04-panoptic/blob/main/contracts/PanopticPool.sol#L712-L713
https://github.com/code-423n4/2024-04-panoptic/blob/main/contracts/libraries/PanopticMath.sol#L591-L596
shortAmount is received from `PanopticMath.getAmountsMoved`:
- amount0 = 1067045795289168526
- amount1 = 100000
- liquidity = 6379635286082
It seems like the error originates here, since the actual moved amounts are way smaller, see swappedAmount that is received when position is minted in SFPM.

State of the collateral tracker after attack
s_poolAssets = 340282366920938463463374607431768125538
s_inAMM = 1067045795289168526
totalAssets = 1067045795289082608

## Tools Used
Forge

## Recommended Mitigation Steps
-





## Assessed type

Error