xanoutas/solidity-security-findings huggingface dataset:
Position token may be lost if mintTokenizedPosition is executed in constructor of a contract
Audit: 2023-11-panoptic-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-11-panoptic/blob/aa86461c9d6e60ef75ed5a1fe36a748b952c8666/contracts/tokens/ERC1155Minimal.sol#L222-L229


# Vulnerability details

If an contract is used to create a position calling mintTokenizedPosition in its contructor, then the ERC1155 token may be lost.
This is because of use of below method in _mint 

        if (to.code.length != 0) {
            if (
                ERC1155Holder(to).onERC1155Received(msg.sender, address(0), id, amount, "") !=
                ERC1155Holder.onERC1155Received.selector
            ) {
                revert UnsafeRecipient();
            }
        }

## Impact
loss of token minted from ERC1155
 

## Recommended Mitigation Steps
Use assembly to check the to address is a contract or not



## Assessed type

Token-Transfer
Re: Functionality
Audit: 2023-11-panoptic-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-11-panoptic/blob/main/contracts/types/TokenId.sol#L442


# Vulnerability details

## Impact
The function is designed to clear specific bits in the uint256 integer based on the provided index. If the index (i) value is not within the range [0, 3], the function returns the original input without clearing any bits.
## Proof of Concept
Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.

## Tools Used

## Recommended Mitigation Steps

Add a revert statement or error handling logic at the end of the function to handle unexpected indices.
Consider throwing an error or reverting when an index outside the valid range is provided.


## Assessed type

Other
Incorrect Handling of Negative values in toRightSlot() and toLeftSlot() 
Audit: 2023-11-panoptic-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-11-panoptic/blob/aa86461c9d6e60ef75ed5a1fe36a748b952c8666/contracts/types/LeftRight.sol#L108
https://github.com/code-423n4/2023-11-panoptic/blob/aa86461c9d6e60ef75ed5a1fe36a748b952c8666/contracts/types/LeftRight.sol#L75


# Vulnerability details

## Impact
The vulnerability arises when attempting to store a negative value in the right slot using the toRightSlot functions. The code incorrectly adds ```int256(right)``` directly to the original value, potentially causing an arithmetic overflow. The recommended correction involves using the bitwise ```AND``` operation with the ```RIGHT_HALF_BIT_MASK``` constant to prevent overflow and ensure proper handling of negative values.

### Impacts on the project are
1. Arithmetic Overflow:
The incorrect handling of negative values in arithmetic operations may lead to arithmetic overflow. This occurs when the result of an addition operation exceeds the maximum representable value for the data type (int256 in this case). Arithmetic overflow can result in unexpected and incorrect values, potentially compromising the integrity of calculations.

2. Data Corruption:
Mishandling negative values can corrupt the data stored in the smart contract. If the arithmetic overflow occurs during a data update or calculation, the stored values may become inconsistent or incorrect, leading to data corruption.


## Proof of Concept
```solidity
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

library LeftRight {
    int256 internal constant RIGHT_HALF_BIT_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;

    // Vulnerable to arithmetic overflow for negative values
    function vulnerableOperation(int256 x, int128 right) internal pure returns (int256) {
        unchecked {
            // Incorrect: Direct addition without proper handling
            return x + int256(right);
        }
    }
}

contract DemoContract {
    using LeftRight for int256;

    // Function to demonstrate the vulnerability
    function triggerVulnerability(int256 x, int128 right) external pure returns (int256) {
        // Triggering the vulnerable operation
        return x.vulnerableOperation(right);
    }
}

contract DemoAttack {
    // Function to demonstrate the vulnerability
    function attackDemoContract() external pure {
        // Create an instance of DemoContract
        DemoContract demoContract = new DemoContract();

        // Trigger the vulnerability with a negative value
        demoContract.triggerVulnerability(type(int256).min, int128(-1));
    }
}

```

here the ```DemoAttack``` uses an instance of ```DemoContract``` and calls the ```triggerVulnerability``` function with a negative value ```(int128(-1))```. When executed, this should showcase the vulnerability and potentially lead to an arithmetic overflow.

## Tools Used
1. VSCode
2. Slinther

## Recommended Mitigation Steps
Update toRightSlot for int256 Values:

1. Modify the ```toRightSlot``` function to use the bitw
Examining Incorrect Return Values from the add(int256,int256) Function in the LeftRight Library
Audit: 2023-11-panoptic-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-11-panoptic/blob/aa86461c9d6e60ef75ed5a1fe36a748b952c8666/contracts/types/LeftRight.sol#L159-L171


# Vulnerability details

## Impact
The add(int256,int256) function in the LeftRight library is designed to return the addition of two int256 numbers. However, due to incorrect implementation, it returns erroneous values in specific edge cases, resulting in precision loss.

This function is used multiple times in the SemiFungiblePositionManager contract, and its incorrect behavior can cause heavy financial losses.

## Proof of Concept
This function has lot of issues:
- when both x and y are negative there addition will give weird result
Example:
LeftRight.add(-19,-19) -> -340282366920938463463374607431768211494
- when either x or y is negative then there is this condition that results in bug, lets say x = -3 and y = 2
we have to assume both are positive (3,2) and if the negative sign is assigned to
larger number(-3,2) then it is not bug but if the negative sign is assigned to smaller number (3,-2) then it will give weird result
Example:
-3,2 -> -1
3,-2 -> -340282366920938463463374607431768211455



Here is a Foundry POC, run inside test/foundry with command **forge test --match-test heist -vvv**
```solidity
pragma solidity ^0.8.0;

import {LeftRight} from "@types/LeftRight.sol";


import "forge-std/Test.sol";

contract LRTest is Test {
using LeftRight for int256;
using LeftRight for int128;


// when either x || y < 0 ; assume both positive, if smaller number has '-' it will be bug
// lets take two examples
// -3 and 2 ; 3 and - 2
// here we will assume both are positive
// then we will verfiy if the smaller number is negative it is a bug
// in -3,2 its addition would be normal since larger number is negative
// in 3,-2 its addtion would be buggy since smaller number is negative

function test_32_case1_normal_heist() public {
 console.logInt(int256(-3).add(int256(2))); //normal
}

function test_32_case2_bug_heist() public {
 console.logInt(int256(3).add(int256(-2))); //buggy
//  returns -340282366920938463463374607431768211455
}

// bug: when x < 0 and y < 0
function test_xy_both_negative_heist() public {
 console.logInt(int256(-19).add(int256(-19)));
 console.logInt(int256(-91).add(int256(-19)));
 console.logInt(int256(-67).add(int256(-76)));
 
//returns
//   -340282366920938463463374607431768211494
//   -340282366920938463463374607431768211566
//   -340282366920938463463374607431768211599

}
 
}
```
**This add() function also gives fake overflow on int128 max but in other case doesn't revert on int256 overflow (timeUp, couldn't write poc for this one)**

## Tools Used
Echidna, foundry

## Recommended Mitigation Steps





## Assessed type

Math
re: Potential Overflow Issue
Audit: 2023-11-panoptic-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-11-panoptic/blob/main/contracts/types/TokenId.sol#L410


# Vulnerability details

## Impact
The function assumes a specific bit pattern for each leg to determine the number of legs. If the input self contains more active legs than the function expects or if the bit pattern doesn't align with the predefined ranges for legs, it might result in an incorrect count of legs.

This could potentially cause issues such as:

Incorrect calculations: If the function miscalculates the number of legs, subsequent operations dependent on the leg count might produce erroneous results.

Unexpected behavior: An overflow could occur if self indicates a higher number of legs than the function accounts for, leading to unexpected behavior or even contract failures.

## Proof of Concept
Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.

## Tools Used

## Recommended Mitigation Steps

Update the function's logic to handle the maximum possible number of legs, adding an extra condition or increasing the bit threshold for the last leg to prevent potential overflow.

Ensuring that the bit patterns representing legs in the self value match the assumptions made within the function is crucial to avoid miscounting or overflow-related issues. A careful review of the input data and the function's assumptions regarding leg representation is necessary to guarantee accurate leg counting.


## Assessed type

Error
Usage of slot0 is extremely easy to manipulate
Audit: 2023-11-panoptic-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-11-panoptic/blob/main/contracts/SemiFungiblePositionManager.sol#L775


# Vulnerability details

## Impact
In `SemiFungiblePositionManager.sol`, the functions `swapInAMM` use `UniswapV3.slot0` to get the value of `sqrtPriceX96`, which is used to calculate `net0` which is then used to calculate `swapAmount` which is then furthur used to perform the swap. 

The hirarcy is as follow
sqrtPriceX96 ---> net0 ---> swapAmount ---> `_univ3pool.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 _univ3pool.swap function.


## Proof of Concept
The contract `SemiFungiblePositionManager.sol` in the function `swapInAMM` is using `uniswap.slot0` to compute its swap but there is a huge issue with it. The function is using `slot0` to derive `sqrtPriceX96`. Slot0 is the most recent data point and is therefore extremely easy to manipulate.

Here, the code derive `sqrtPriceX96` from `slot0`
```solidity
 if ((itm0 != 0) && (itm1 != 0)) {
                    (uint160 sqrtPriceX96, , , , , , ) = _univ3pool.slot0(); 
```

The used that value of `sqrtPriceX96` to compute `net0`
```solidity
                    int256 net0 = itm0 - PanopticMath.convert1to0(itm1, sqrtPriceX96);
```

using that `net0` to compute `swapAmount`
```solidity
                    swapAmount = -net0;
```

And finally using that `swapAmount` to swap tokens in uniswap
```solidity
// 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_SQRT_RATIO - 1,
                    data
                );
```

According to uniswap itself:

`sqrtPriceX96 is the current price of the pool as a sqrt(token1/token0) Q64.96 value`

you can see it [here](https://docs.uniswap.org/contracts/v3/reference/core/interfaces/pool/IUniswapV3PoolState#return-values)




An attacker can simply manipulate the `sqrtPriceX96` and if the _univ3pool.swap function uses the values that are based on the manipulated `sqrtPriceX96` then it can cause the contract to make unfavorable swaps and resulting in potential loss of funds.

## Tools Used
Manual Review

## Recommended Mitigation Steps
Use the `TWAP` function to get the value of `sqrtPriceX96`







## Assessed type

Uniswap
Re: Revert Usage on Casting Error
Audit: 2023-11-panoptic-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-11-panoptic/blob/main/contracts/libraries/Math.sol#L172


# Vulnerability details

## Impact

The revert operation on casting error might abruptly halt execution, making error handling less informative or graceful.


## Proof of Concept
Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.

## Tools Used

## Recommended Mitigation Steps

Use require statements with custom error messages to provide more context on why the casting failed.

function toUint128(uint256 toDowncast) internal pure returns (uint128 downcastedInt) {
    require(toDowncast <= type(uint128).max, "Value exceeds uint128 range");
    downcastedInt = uint128(toDowncast);
    require(uint256(downcastedInt) == toDowncast, "CastingError: downcast failed");
}
In this scenario, the first require checks if the value is within the range of uint128. The subsequent line performs the downcast. The second require statement verifies if the downcast was successful, and if not, it reverts with the specified error message, indicating that the casting failed. This pattern helps provide context for different failure scenarios in the casting process.


## Assessed type

Error
Lack of validation for PoolId
Audit: 2023-11-panoptic-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-11-panoptic/blob/main/contracts/libraries/PanopticMath.sol#L38


# Vulnerability details

## Impact
PoolId is used for indexing or tracking specific pools, validating that the conversion accurately represents the intended pool ID range and that it's used consistently across the system would be essential
## Proof of Concept
Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.

## Tools Used

## Recommended Mitigation Steps

Depending on the purpose, consider adding checks or validations to ensure the correctness and validity of the converted pool ID

function getPoolId(address univ3pool) internal pure returns (uint64) {
    require(univ3pool != address(0), "Invalid pool address");
    uint64 poolId = uint64(uint160(univ3pool) >> 96);
    // Additional checks or logging related to poolId usage
    // ...
    return poolId;
}



## Assessed type

Invalid Validation
Re: Hash Collision	
Audit: 2023-11-panoptic-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-11-panoptic/blob/main/contracts/libraries/PanopticMath.sol#L38


# Vulnerability details

## Impact
The method generates a pool ID from hashing parameters without collision checks.

## Proof of Concept
Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.

## Tools Used

## Recommended Mitigation Steps
This modified version alters the method of concatenation and hash derivation, potentially reducing the risk of hash collisions

function getFinalPoolId(
    uint64 basePoolId,
    address token0,
    address token1,
    uint24 fee
) internal pure returns (uint64) {
    bytes memory poolIdData = abi.encodePacked(token0, token1, fee);
    bytes32 hash = keccak256(poolIdData);
    uint64 finalPoolId = basePoolId + (uint64(hash) >> 32);
    return finalPoolId;
}



## Assessed type

Other
Re: Unauthorized Fee Collection
Audit: 2023-11-panoptic-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-11-panoptic/blob/main/contracts/SemiFungiblePositionManager.sol#L1201


# Vulnerability details

## Impact

The function collects fees from the Uniswap V3 pool without checking if the caller is authorized to collect fees for the specified position.


## Proof of Concept
Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.

## Tools Used

## Recommended Mitigation Steps

modifier onlyPositionOwner(bytes32 positionKey) {
require(isPositionOwner(positionKey, msg.sender), 'Caller is not the position owner');
_;
}
function _collectAndWritePositionData(...) internal onlyPositionOwner(positionKey) {
// existing logic
}


## Assessed type

Other
liquidity of chunk liquidity might be wrong cause of rounding error in asTicks
Audit: 2023-11-panoptic-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-11-panoptic/blob/aa86461c9d6e60ef75ed5a1fe36a748b952c8666/contracts/types/TokenId.sol#L389


# Vulnerability details

## Impact
cause of rounding error in asticks , legLowerTick and legUpperTick might be  a little bit different .

## Proof of Concept

            int24 oneSidedRange = (selfWidth * tickSpacing) / 2;

it could be rounding error in oneSideRange

              (legLowerTick, legUpperTick) = (selfStrike - oneSidedRange, selfStrike + 
                oneSidedRange);

legLowerTick and legupperTick might be differs a little bit . cause potential revert 


                   if (
                legLowerTick % tickSpacing != 0 ||
                legUpperTick % tickSpacing != 0 ||
                legLowerTick < minTick || //*@audit minTick at least must be 1
                legUpperTick > maxTick
            ) revert Errors.TicksNotInitializable();
        }
    }


## Tools Used
manual view 
## Recommended Mitigation Steps
pls handle rounding error


## Assessed type

Math
Potential Division by Zero
Audit: 2023-11-panoptic-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-11-panoptic/blob/main/contracts/SemiFungiblePositionManager.sol#L1200


# Vulnerability details

## Impact
If amountToCollect becomes zero in certain scenarios, the function might revert if the division operation is attempted

## Proof of Concept
contract ExampleContract {
    function _collectAndWritePositionData(uint256 amountToCollect) internal returns (uint256 collectedOut) {
        if (amountToCollect != 0) {
            // Perform operations based on amountToCollect
            collectedOut = 100 / amountToCollect; // Division operation
        }
    }
    
    // Function to demonstrate the issue
    function triggerDivisionByZero() external returns (uint256) {
        uint256 zeroAmount = 0; // Simulating scenario where amountToCollect becomes zero
        
        // Calling the function with zero amount
        // This will result in a division by zero issue
        uint256 result = _collectAndWritePositionData(zeroAmount);
        
        return result;
    }
}

## Tools Used

## Recommended Mitigation Steps

Add a check before performing the division operation.


contract ExampleContract {
    function _collectAndWritePositionData(uint256 amountToCollect) internal returns (uint256 collectedOut) {
        if (amountToCollect != 0) {
            // Perform operations based on amountToCollect
            collectedOut = 100 / amountToCollect; // Division operation
        } else {
            // Handle the scenario where amountToCollect is zero
            // For example, revert, set a default value, or perform an alternative action
            revert("Division by zero: amountToCollect is zero");
        }
    }
    
    // Function to avoid triggering division by zero
    function safeOperation() external returns (uint256) {
        uint256 nonZeroAmount = 5; // Non-zero value
        
        // Call the function with a non-zero amount
        // This will safely execute the division operation
        uint256 result = _collectAndWritePositionData(nonZeroAmount);
        
        return result;
    }
}



## Assessed type

Math
Division by Zero in Premium Calculation
Audit: 2023-11-panoptic-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-11-panoptic/blob/main/contracts/SemiFungiblePositionManager.sol#L1255


# Vulnerability details

## Impact
The function does not check for zero values in the denominator before performing division operations.

A division by zero could cause transaction reversion or result in an invalid state.


## Proof of Concept
To demonstrate a potential division by zero issue in the given function _getPremiaDeltas, we can set up a scenario where either netLiquidity or totalLiquidity becomes zero, causing a division operation to fail. Here's an example:

function _getPremiaDeltas(uint256 currentLiquidity, int256 collectedAmounts)
        private
        pure
        returns (uint256 deltaPremiumOwed, uint256 deltaPremiumGross)
    {
        uint256 removedLiquidity = currentLiquidity >> 128; // Extract left slot
        uint256 netLiquidity = currentLiquidity & type(uint128).max; // Extract right slot

        unchecked {
            uint256 totalLiquidity = netLiquidity + removedLiquidity;

            // Ensure one of the denominators becomes zero
            if (netLiquidity == 0) {
                // Emulate division by zero
                totalLiquidity = 0;
            }

            uint128 premium0X64_base;
            // Rest of the function logic...
            // (For simplicity, the rest of the function logic is omitted for demonstration purposes)
        }
    }
## Tools Used

## Recommended Mitigation Steps

Add checks to prevent division by zero.

if (netLiquidity != 0 && totalLiquidity != 0) {
    // Perform division operations here
} else {
    // Handle the scenario where the denominator is zero
    // This can include reverting the transaction or taking alternative actions
}



## Assessed type

Math