xanoutas/solidity-security-findings huggingface dataset:
option seller will only get dust amount of premia if position liquidity is too large
Audit: 2024-04-panoptic-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-04-panoptic/blob/833312ebd600665b577fbd9c03ffa0daf250ed24/contracts/PanopticPool.sol#L1717-L1723
https://github.com/code-423n4/2024-04-panoptic/blob/833312ebd600665b577fbd9c03ffa0daf250ed24/contracts/PanopticPool.sol#L1886-L1894
https://github.com/code-423n4/2024-04-panoptic/blob/833312ebd600665b577fbd9c03ffa0daf250ed24/contracts/PanopticPool.sol#L1764-L1794


# Vulnerability details

## Impact
option sellers will only get dust amount if they provide too large liquidity and total liquidity for specific tick range is small.
## Proof of Concept


      function _updateSettlementPostMint(
        TokenId tokenId,
        LeftRightUnsigned[4] memory collectedByLeg,
        uint128 positionSize
    ) internal {
        .....

         uint256 totalLiquidity = _getTotalLiquidity(tokenId, leg);
         
         .....

           unchecked {
                    // L
                    LeftRightUnsigned grossPremiumLast = 
                   s_grossPremiumLast[chunkKey];
                    // R
                    uint256 positionLiquidity = liquidityChunk.liquidity();
                    // T (totalLiquidity is (T + R) after minting)
                    //*@audit-info ------->>> total liquidity is less than 
                  position liquidity , there will be underflow 
                    uint256 totalLiquidityBefore = totalLiquidity - 
                   positionLiquidity;



Protocol use unchecked before calculation totalLiquidityBefore . So if position liquidty provide from option sellers is larger than total liquidty , there will not be reverted and there will be underflow . let's assume option sellers provide liquidity when total liquidity for specific tick range is not too large and sellers position size is too large . This may lead to underflow and total liquidity before will be large number .



                s_grossPremiumLast[chunkKey] = LeftRightUnsigned
                        .wrap(0)
                        .toRightSlot(
                            uint128(
                                (grossCurrent[0] *
                                    positionLiquidity +
                                    grossPremiumLast.rightSlot() *
                                    totalLiquidityBefore) / (totalLiquidity)
                            )
                        )
                        .toLeftSlot(
                            uint128(
                                (grossCurrent[1] *
                                    positionLiquidity +
                                    grossPremiumLast.leftSlot() *
                                    totalLiquidityBefore) / (totalLiquidity)
                            )
                        );


 Cause of total liquidity before is too large  , Gross premium last will also be large numbers(assuming option sellers provide liquidity previously) . and s gross premium last will also be large.





              function _getAvailablePremium(
FeesCalc::_getAMMSwapFeesPerLiquidityCollected results in double subtraction
Audit: 2024-04-panoptic-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
`https://github.com/code-423n4/2024-04-panoptic/blob/833312ebd600665b577fbd9c03ffa0daf250ed24/contracts/libraries/FeesCalc.sol#L130-L208`
The current implementation potentially results in double subtraction, which will negate the actual fees collected between the specified ticks.

## Proof of Concept
    `function _getAMMSwapFeesPerLiquidityCollected(
        IUniswapV3Pool univ3pool,
        int24 currentTick,
        int24 tickLower,
        int24 tickUpper
    ) internal view returns (uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) {
        // Get feesGrowths from the option position's lower+upper ticks
        // lowerOut0: For token0: fee growth per unit of liquidity on the _other_ side of tickLower (relative to currentTick)
        // only has relative meaning, not absolute — the value depends on when the tick is initialized
        // (...)
        // upperOut1: For token1: fee growth on the _other_ side of tickUpper (again: relative to currentTick)
        // the point is: the range covered by lowerOut0 changes depending on where currentTick is.
        (, , uint256 lowerOut0, uint256 lowerOut1, , , , ) = univ3pool.ticks(tickLower);
        (, , uint256 upperOut0, uint256 upperOut1, , , , ) = univ3pool.ticks(tickUpper);


        // compute the effective feeGrowth, depending on whether price is above/below/within range
        unchecked {
            if (currentTick < tickLower) {
                /**
                  Diagrams shown for token0, and applies for token1 the same
                  L = lowerTick, U = upperTick


                    liquidity         lowerOut0 (all fees collected in this price tick range for token0)
                        ▲            ◄──────────────^v───► (to MAX_TICK)
                        │
                        │                      upperOut0
                        │                     ◄─────^v───►
                        │           ┌────────┐
                        │           │ chunk  │
                        │           │        │
                        └─────▲─────┴────────┴────────► price tick
                              │     L        U
                              │
                           current
                            tick
                */
                feeGrowthInside0X128 = lowerOut0 - upperOut0; // fee growth inside the chunk
                feeGrowthInside1X128 = lowerOut1 - upperOut1;
            } else if (currentTick >= tickUpper) {
                /**
                    liquidity
                        ▲           upperOut0
                        │◄─^v─────────────────────►
                        │     
                        │     lowerOut0  ┌────────┐
                        │◄─^v───────────►│ chunk  │
                        │                │        │
Missing Input Validation
Audit: 2024-04-panoptic-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-04-panoptic/blob/main/contracts/PanopticFactory.sol#L210-L227


# Vulnerability details

## Impact

The input parameters `token0`, `token1`, and `fee` are not validated in the `deployNewPool` function within the `PanopticFactory` contract. This omission can potentially lead to unexpected behavior and effects when the contract interacts with the SemiFungiblePositionManager contract and the UniswapV3Factory contract.

In particular, if the `token0`, `token1`, and `fee` parameters lead to a `v3Pool` address that doesn't represent an existing and properly initialized Uniswap V3 pool, it could cause runtime errors or result in incorrect pool initialization.



## Proof of Concept


```solidity
function deployNewPool(
    address token0,
    address token1,
    uint24 fee,
    bytes32 salt
) external returns (PanopticPool newPoolContract) {
    (token0, token1) = token0 < token1 ? (token0, token1) : (token1, token0);
    // ... 
    IUniswapV3Pool v3Pool = IUniswapV3Pool(UNIV3_FACTORY.getPool(token0, token1, fee));
    if (address(v3Pool) == address(0)) revert Errors.UniswapPoolNotInitialized();
```

## Tools Used

Manual review

## Recommended Mitigation Steps

Before invoking the UniswapV3Factory's `getPool` function and initializing the `v3Pool` reference, assert that `token0`, `token1`, and `fee` are valid. Consider defining and using a function that verifies token addresses and fee amounts per the business rules of your smart contract system. 
This will help ensure that all function inputs are sanitized, and it will prevent unexpected behavior in later execution. Note that sometimes, input validation is not possible if there's no way to predict all valid cases a priori (e.g., token address verification if any ERC20 token can be used). In such cases, ensure that other parts of the contract code are capable of properly handling any possible input.


## Assessed type

Invalid Validation
getAmountsForLiquidity returns incorrect price
Audit: 2024-04-panoptic-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Vulnerability Detail
This `getAmountsForLiquidity()` function is designed to get the `amount0` and `amount1` values depending on the current tick and the amount of liquidity in the liquidityChunk. However, the function does not check the correctness of the transferred data, in particular if tickLower < tickUpper. Function mistakenly believes that the lower is always less than the upper limit, which is not always the case. This will lead to errors in calculations - the real upper price will be used in the calculation of the amount0, and e.g:
```solidity
function getAmount0ForLiquidity(LiquidityChunk liquidityChunk) internal pure returns (uint256) {
        uint160 lowPriceX96 = getSqrtRatioAtTick(liquidityChunk.tickLower());
        uint160 highPriceX96 = getSqrtRatioAtTick(liquidityChunk.tickUpper());
        unchecked {
            return
                mulDiv(
                    uint256(liquidityChunk.liquidity()) << 96,
                    highPriceX96 - lowPriceX96,
                    highPriceX96
                ) / lowPriceX96;
        }
    }
```
Here's how it's implemented in UniV3:
```solidity
if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);
```
## Impact
Price miscalculation.
## Tools Used
Manual review.
## Recommended Mitigation Steps
Add this check:
```solidity
 if (lowPriceX96 > highPriceX96) (lowPriceX96 , highPriceX96) = (highPriceX96, lowPriceX96 );
```


## Assessed type

Uniswap
Reentrancy in ERC777 tokens in collateralTokens.sol
Audit: 2024-04-panoptic-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
Re-entrancy in deposit and mint for ERC777 tokens leads to minting more than expected tokens

## Proof of Concept
```solidity
    function deposit(uint256 assets, address receiver) external returns (uint256 shares) {
        .
        .
        // in return for the shares to be minted
        SafeTransferLib.safeTransferFrom(
            s_underlyingToken,
            msg.sender,
            address(s_panopticPool),
            assets
        );

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

        // update tracked asset balance
        unchecked {
            s_poolAssets += uint128(assets);
        }
        .
        .
}
```
Here we see the checks-effects-interactions pattern not followed and in case of ERC777 tokens can be fatal as _mint can be called many times

## Tools Used
Manual analysis

## Recommended Mitigation Steps
Add noReentrant modifier or follow checks effects interactions patterns like withdraw and redeem


## Assessed type

Reentrancy
Precision Loss Due to Division Before Multiplication.
Audit: 2024-04-panoptic-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
The impact of this vulnerability can lead to incorrect calculations, which might result in financial losses, incorrect state updates, or even allow for exploits.

## Proof of Concept


The provided code snippets demonstrate the vulnerability through various instances where division is performed before multiplication. For example, in the `CollateralTracker.startToken` function, the `_poolFee` is calculated by dividing `fee` by 100 and then multiplying by `ITM_SPREAD_MULTIPLIER` and `DECIMALS`. Similarly, in the `PanopticFactory._mintFullRange` function, `tickLower` is calculated by dividing `Constants.MIN_V3POOL_TICK` by `tickSpacing` and then multiplying by `tickSpacing`. These operations can lead to precision loss due to the truncation of integer division in Solidity.

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

## Recommended Mitigation Steps
To mitigate this vulnerability, it's recommended to rearrange arithmetic operations to perform multiplication before division, unless the limit of a smaller type makes this dangerous. This approach helps to preserve precision and avoid unintended results. Here are some general guidelines for mitigation:

1. Reorder Operations: Whenever possible, rearrange the order of operations to perform multiplication before division. This can be particularly important in financial calculations where precision is critical.

2. Use SafeMath or Solidity 0.8.x: If you're using a version of Solidity prior to 0.8.0, consider using the SafeMath library for arithmetic operations to prevent overflows and underflows. Solidity 0.8.0 and later versions include built-in overflow and underflow checks, which can help mitigate some of these risks.


## Assessed type

Math
Missing initializer in SemiFungiblePositionManager::initializeAMMPool
Audit: 2024-04-panoptic-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-04-panoptic/blob/main/contracts/SemiFungiblePositionManager.sol#L350-L391


# Vulnerability details

## Impact
The absence of an initializer in the `initializeAMMPool` function poses a moderate risk to the stability and integrity of the contract. Without proper initialization control, there is a potential for re-initialization issues, which could compromise the functionality and security of the contract.

## Proof of Concept
Add this function in SemiFungiblePositionManager.t.sol
```
 function test_Success_initializeAMMPool_multipleInitialize(uint256 x) public {
        _initPool(x);
        _initPool(x);  
    }
```
Output
```
Ran 1 test for test/foundry/core/SemiFungiblePositionManager.t.sol:SemiFungiblePositionManagerTest
[PASS] test_Success_initializeAMMPool_multipleInitialize(uint256) (runs: 2, μ: 1823079, ~: 1823079)
```

https://github.com/code-423n4/2024-04-panoptic/blob/main/contracts/SemiFungiblePositionManager.sol#L350-L391

## Tools Used
Code review

## Recommended Mitigation Steps
Add initializer modifier from openzeppeline library or use mutex to change value from false to true


## Assessed type

Invalid Validation
Insecure use of SafeTransferFrom() function
Audit: 2024-04-panoptic-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-04-panoptic/blob/main/contracts/PanopticFactory.sol#L172-L195


# Vulnerability details

## Impact

The PanopticFactory contract uses `safeTransferFrom` (in `uniswapV3MintCallback` function) to transfer tokens from the payer to the sender. 

However, `safeTransferFrom` can't prevent re-entrancy, meaning the function can call itself while it's still "in progress." This can potentially create malicious recursive calls and drain the contract balances.

## Proof of Concept

```solidity
function uniswapV3MintCallback( 
    uint256 amount0Owed, 
    uint256 amount1Owed, 
    bytes calldata data 
) external { 
    // ...
    if (amount0Owed > 0) 
        SafeTransferLib.safeTransferFrom( decoded.poolFeatures.token0, decoded.payer, msg.sender, amount0Owed ); 
    if (amount1Owed > 0) 
        SafeTransferLib.safeTransferFrom( decoded.poolFeatures.token1, decoded.payer, msg.sender, amount1Owed ); 
}
```

## Tools Used

Manual Review

## Recommended Mitigation Steps

To mitigate reentrancy risks, it's recommended to adopt the Checks-Effects-Interactions (CEI) pattern, where external contract calls should be the last part of the function, after all checks and state changes. In this case, it would be useful to apply the ReentrancyGuard from the OpenZeppelin library to safeguard these external calls.


## Assessed type

Reentrancy
convertToAssets can return zero when totalAsset * shares is lesser than TotalSupply
Audit: 2024-04-panoptic-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
Users that redeemShares will get 0 tokens while having all their share amount removed if their share amount is lesser than total supply when multiplied with total assets.

## POC

### Preview redeem
```
    function previewRedeem(uint256 shares) public view returns (uint256 assets) {
        return convertToAssets(shares);
    }
```

### convertToAssets
```solidity
    function convertToAssets(uint256 shares) public view returns (uint256 assets) 
    {
        return Math.mulDiv(shares, totalAssets(), totalSupply);
    }
```

### redeem function when shares is given lower than totalsupply/totalAssets

```solidity

 function redeem(
        uint256 shares,
        address receiver,
        address owner
    )
{
    .
        assets = previewRedeem(shares);

        // burn collateral shares of the Panoptic Pool funds (this ERC20 token)
        _burn(owner, shares);
    .

```

Here we see that previewRedeem can return zero but owner's shares are still burned.


## Recommended mitigation steps

Add zero value checks against return value of previewRedeem


## Assessed type

Math
Incorrect ERC20 Function Interface Definitions.
Audit: 2024-04-panoptic-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-04-panoptic/blob/833312ebd600665b577fbd9c03ffa0daf250ed24/contracts/tokens/interfaces/IERC20Partial.sol#L22-L27


# Vulnerability details

## Impact
The incorrect ERC20 function interfaces in `IERC20Partial` as identified by Slither can lead to compatibility issues and unexpected behavior when interacting with other contracts or services that expect standard ERC20 functionality. Specifically, the `approve` and `transfer` functions are missing the correct return types, which is a violation of the ERC20 standard. This can result in failed transactions or incorrect token transfers, potentially leading to loss of funds or other operational issues within applications that rely on these functions.

## Proof of Concept
https://github.com/code-423n4/2024-04-panoptic/blob/833312ebd600665b577fbd9c03ffa0daf250ed24/contracts/tokens/interfaces/IERC20Partial.sol#L22-L27

The issue is highlighted in the `IERC20Partial` interface, where the `approve` and `transfer` functions are defined without the expected return types. According to the ERC20 standard, the `approve` function should return a boolean indicating success or failure, and the `transfer` function should return a boolean indicating whether the transfer was successful. The absence of these return types in the interface definition can lead to compatibility issues when interacting with other contracts or services that expect these return values.

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

## Recommended Mitigation Steps
To mitigate this issue, it's recommended to update the `IERC20Partial` interface to correctly define the return types for the `approve` and `transfer` functions according to the ERC20 standard. This ensures compatibility and correct behavior when interacting with other contracts or services. Here's how the corrected interface might look:

```solidity
interface IERC20Partial {
    function approve(address spender, uint256 amount) external returns (bool);
    function transfer(address recipient, uint256 amount) external returns (bool);
}
```
By ensuring that the `approve` and `transfer` functions return boolean values, you align the `IERC20Partial` interface with the ERC20 standard, reducing the risk of compatibility issues and ensuring that transactions and token transfers behave as expected.


## Assessed type

ERC20
Missing access control in PanopticFactory::initialize can be manipulated by MEV
Audit: 2024-04-panoptic-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
There is no access control, so mev bot can manipulate `s_owner` address and become address of `PanopticFactory` contract

## Proof of Concept
```
function initialize(address _owner) public {
        if (!s_initialized) {
            s_owner = _owner;
            s_initialized = true;
        }
    }
```

## Tools Used
Manual code review
## Recommended Mitigation Steps
Add access control or call it in `constructor`


## Assessed type

MEV
Attacker can deploy pool with fake token to drain other token in the pair from user who mint option is short call
Audit: 2024-04-panoptic-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Vulnerability details
With the current design of protocol, it is able for anyone to deploy pool with any token pair. And the name of them is computed as below:

    function computeName(
        address token0,
        address token1,
        bool isToken0,
        uint24 fee,
        string memory prefix
    ) external view returns (string memory) {
        // get the underlying token symbols
        // it's not guaranteed that they support the metadata extension
        // so we need to let them fail and return placeholder if not
        string memory symbol0;
        string memory symbol1;
        try IERC20Metadata(token0).symbol() returns (string memory _symbol) {
            symbol0 = _symbol;
        } catch {
            symbol0 = "???";
        }
        try IERC20Metadata(token1).symbol() returns (string memory _symbol) {
            symbol1 = _symbol;
        } catch {
            symbol1 = "???";
        }
        unchecked {
            return
                string.concat(
                    prefix,
                    " ",
                    isToken0 ? symbol0 : symbol1,
                    " LP on ",
                    symbol0,
                    "/",
                    symbol1,
                    " ",
                    Strings.toString(fee),
                    "bps"
                );
        }
    }

It can be seen that it only fetch name of 2 tokens. Which will open the scenario that attacker can trap user like below:

Create a Uniswap V3 pool with one of them is fake token that have special `transferFrom()` function and attacker is able to mint token for themselves
Seed it with an incredibly low amount of liquidity and maintain this price to be same as actual price, which shouldn't be hard with a new, unused pool and low liquidity that do not make profit when doing swap
Waiting anyone who make mint options with short call and drain liquidity in the uniswap v3 pool

As there is no mechanism to remove pool, user can trap anyone again and again.

## Impact
Attacker can trap user by creating fake pool

## Tools Used
Manual review

## Recommended Mitigation Steps
Owner should be only one that can deploy pool.


## Assessed type

Other