xanoutas/solidity-security-findings huggingface dataset:
Potential overflow in `numberOfLeadingHexZeros` 
Audit: 2024-04-panoptic-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-04-panoptic/blob/833312ebd600665b577fbd9c03ffa0daf250ed24/contracts/libraries/PanopticMath.sol#L66-L79
https://github.com/code-423n4/2024-04-panoptic/blob/833312ebd600665b577fbd9c03ffa0daf250ed24/contracts/PanopticFactory.sol#L282-L326


# Vulnerability details

## Vulnerability Description
The `numberOfLeadingHexZeros` function computes `39 - Math.mostSignificantNibble(uint160(addr))`. If `addr` is zero, `mostSignificantNibble` will return 40, causing an overflow in the subtraction `39 - 40`. 

[PanopticMath.sol#numberOfLeadingHexZeros](https://github.com/code-423n4/2024-04-panoptic/blob/833312ebd600665b577fbd9c03ffa0daf250ed24/contracts/libraries/PanopticMath.sol#L66-L79)
```solidity
    /// @notice Get the number of leading hex characters in an address.
    ///     0x0000bababaab...     0xababababab...
    ///          ▲                 ▲
    ///          │                 │
    ///     4 leading hex      0 leading hex
    ///    character zeros    character zeros
    ///
    /// @param addr The address to get the number of leading zero hex characters for
    /// @return The number of leading zero hex characters in the address
    function numberOfLeadingHexZeros(address addr) external pure returns (uint256) {
        unchecked {
            return addr == address(0) ? 40 : 39 - Math.mostSignificantNibble(uint160(addr));
        }
    }
```
## Impact
When the input address is the zero address (0x0000000000000000000000000000000000000000). In this case, the `Math.mostSignificantNibble` function returns 40, indicating that there are no non-zero nibbles in the address.

[PanopticMath.sol#L77](https://github.com/code-423n4/2024-04-panoptic/blob/833312ebd600665b577fbd9c03ffa0daf250ed24/contracts/libraries/PanopticMath.sol#L77)
```solidity
return addr == address(0) ? 40 : 39 - Math.mostSignificantNibble(uint160(addr));
```
When `addr` is the zero address, the condition `addr == address(0)` evaluates to true, and the function returns 40. **In the case where `addr` is not the zero address, the subtraction `39 - Math.mostSignificantNibble(uint160(addr))` is performed.**

If `Math.mostSignificantNibble(uint160(addr))` returns 40 (which is the case for the zero address), the subtraction `39 - 40` results in an integer overflow because the result is negative and cannot be represented by an unsigned integer (uint256).

For example, if the input address is "0x0000000000000000000000000000000000000629", the function should return 38, indicating that there are 38 leading zero hex characters.

When the input address is the zero address (0x0000000000000000000000000000000000000000), the `numberOfLeadingHexZeros` function encounters an integer overflow.

The subtraction `39 - 40` results in a negative value, which cannot be represented by an unsigned integer (uint256).

## Proof of Concept
https://github.com/code-423n4/2024-04-panoptic/blob/833312ebd600665b577fbd9c03ffa0daf250ed24/contracts/PanopticFactory.sol#L28
End User ending up by paying extraa fee
Audit: 2024-04-panoptic-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
refer Description

## Description
`_getExchangedAmount()` is called from `takeCommissionAddData()` which only purpose is to return `amount of funds to be exchanged for minting an option (includes commission, swapFee, and intrinsic value).`

So here it include swapFee in case of ITM minting & commissionFee as usual

But problem is for calculating these 2 fees code implementing `Rounding Up` in both cases and end-user resulting in paying more fee than usual.

```solidity
    function _getExchangedAmount(
        int128 longAmount,
        int128 shortAmount,
        int128 swappedAmount
    ) internal view returns (int256 exchangedAmount) {

        unchecked {
            int256 intrinsicValue = swappedAmount - (shortAmount - longAmount);

            if (intrinsicValue != 0) {
                uint256 swapCommission = Math.unsafeDivRoundingUp( 
                    s_ITMSpreadFee * uint256(Math.abs(intrinsicValue)),
                    DECIMALS
                );

                exchangedAmount = intrinsicValue + int256(swapCommission);
            }

            //compute total commission amount = commission rate + spread fee
            exchangedAmount += int256(
                Math.unsafeDivRoundingUp(
                    uint256(uint128(shortAmount + longAmount)) * COMMISSION_FEE,
                    DECIMALS
                )
            );
        }
    }
```

## Tools Used
Manual Review

## Recommended Mitigation Steps
Should re-consider this implementation


## Assessed type

Math
Use of slot0 to get sqrtPriceX96 before performing swaps
Audit: 2024-05-predy-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-05-predy/blob/a9246db5f874a91fb71c296aac6a66902289306a/src/PredyPool.sol#L346
https://github.com/code-423n4/2024-05-predy/blob/a9246db5f874a91fb71c296aac6a66902289306a/src/libraries/UniHelper.sol#L14
https://github.com/code-423n4/2024-05-predy/blob/a9246db5f874a91fb71c296aac6a66902289306a/src/libraries/Perp.sol#L206


# Vulnerability details


## Impact

In UniHelper.sol, the `getSqrtPrice` function uses uniswap's slot0 to get the value of sqrtPriceX96. In Perp.sol also, in the `reallocate` function, slot0 is also used to get the value of sqrtPriceX96. However, slot0, is the most recent data point and can be manipulated easily via MEV bots and Flashloans with sandwich attacks; which can lead to wrong information being returned, manipulation of the price which can affects liquidations, reallocations and potential loss of funds during trades, when the settlementCallback is queried.

## Proof of Concept

In the `reallocate` function in Perp.sol, slot0 is used to query `currentSqrtPrice` which is then used in `swapForOutOfRange` function when the current tick is out of range. The `currentSqrtPrice` is then used to calculate delta positions, update rebalance position. The returned values from the `reallocate` is further used in ReallocationLogic.sol and by extension, PreddyPool.sol and BaseMarket.sol's `reallocate` function. 

```solidity
    function reallocate(
        DataType.PairStatus storage _assetStatusUnderlying,
        SqrtPerpAssetStatus storage _sqrtAssetStatus
    ) internal returns (bool, bool, int256 deltaPositionBase, int256 deltaPositionQuote) {
        (uint160 currentSqrtPrice, int24 currentTick,,,,,) = IUniswapV3Pool(_sqrtAssetStatus.uniswapPool).slot0(); //
        ...
        // if the current tick is out of range, then swap
        if (isOutOfRange) {
            (deltaPositionBase, deltaPositionQuote) =
                swapForOutOfRange(_assetStatusUnderlying, currentSqrtPrice, tick, totalLiquidityAmount);
        }

        return (true, true, deltaPositionBase, deltaPositionQuote);
    }
```
```solidity
        if (pairStatus.isQuoteZero) {
            deltaPositionQuote = -deltaPosition0;
            deltaPositionBase = -deltaPosition1;
        } else {
            deltaPositionBase = -deltaPosition0;
            deltaPositionQuote = -deltaPosition1;
        }

        updateRebalancePosition(pairStatus, deltaPosition0, deltaPosition1);
```

In ReallocationLogic.sol, the reallocate function, the already manipulated delta position values are then passed on in the `callSettlementCallback` function which [performs](https://github.com/code-423n4/2024-05-predy/blob/a9246db5f874a91fb71c296aac6a66902289306a/src/types/GlobalData.sol#L53) [swaps](https://github.com/code-423n4/2024-05-predy/blob/a9246db5f874a91fb71c296aac6a66902289306a/src/base/BaseMarket.sol#L37) in uniswap router through the [SettlementCallbackLib library](https://gith
Price calculation susceptible to flashloan attacks
Audit: 2024-05-predy-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-05-predy/blob/a9246db5f874a91fb71c296aac6a66902289306a/src/libraries/Perp.sol#L202
https://github.com/code-423n4/2024-05-predy/blob/a9246db5f874a91fb71c296aac6a66902289306a/src/libraries/UniHelper.sol#L13
https://github.com/code-423n4/2024-05-predy/blob/a9246db5f874a91fb71c296aac6a66902289306a/src/libraries/Reallocation.sol#L92
https://github.com/code-423n4/2024-05-predy/blob/a9246db5f874a91fb71c296aac6a66902289306a/src/libraries/UniHelper.sol#L35


# Vulnerability details

## Impact
Contract uses uniswap `slot0` price instead of TWAP price. `slot0` price can be manipulated with flash loans.

## Proof of Concept
The function `reallocate` fetches the current price  from Uniswap's `slot0`, which can be manipulated by flash loans:

```solidty
  function reallocate(
        DataType.PairStatus storage _assetStatusUnderlying,
        SqrtPerpAssetStatus storage _sqrtAssetStatus
    ) internal returns (bool, bool, int256 deltaPositionBase, int256 deltaPositionQuote) {

@--->        (uint160 currentSqrtPrice, int24 currentTick,,,,,) = IUniswapV3Pool(_sqrtAssetStatus.uniswapPool).slot0();

      //  //    //
    }
```


Other instance :

```solidity
    function getSqrtPrice(address uniswapPoolAddress) internal view returns (uint160 sqrtPrice) {
 @-->       (sqrtPrice,,,,,,) = IUniswapV3Pool(uniswapPoolAddress).slot0();
    }
```


```solidty

    function isInRange(Perp.SqrtPerpAssetStatus memory sqrtAssetStatus) internal view returns (bool) {
    @--->>    (, int24 currentTick,,,,,) = IUniswapV3Pool(sqrtAssetStatus.uniswapPool).slot0();

        return _isInRange(sqrtAssetStatus, currentTick);
    }
```
The usage of `slot0` to determine deviation could potentially allow malicious actors to manipulate the deviation calculation by altering the most recent price

## Tools Used
Manual Review


## Recommended Mitigation Steps
Considering the potential risks associated with using `slot0` to calculate deviation, implementing a Time-Weighted Average Price (TWAP) to determine the price is recommended. By providing a more accurate and harder to manipulate price point, TWAP would yield a more accurate deviation calculation. This would reduce the possibility of incorrect rebalancing and the risk of DoS attacks. [Here](https://github.com/charmfinance/alpha-vaults-contracts/blob/07db2b213315eea8182427be4ea51219003b8c1a/contracts/AlphaStrategy.sol#L136-L144) is an example implementation of TWAP.











## Assessed type

Other
Use of slot0 to get sqrtPriceLimitX96 can lead to price manipulation
Audit: 2024-05-predy-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-05-predy/blob/main/src/libraries/Perp.sol#L206
https://github.com/code-423n4/2024-05-predy/blob/main/src/libraries/UniHelper.sol#L14


# Vulnerability details

## Impact

In Perp.sol, the reallocate function uses UniswapV3.slot0 to obtain the sqrtPriceX96 value for allocation decisions. However, since sqrtPriceX96 is derived from the most recent data point, it is susceptible to manipulation by MEV bots and flash loan sandwich attacks. If these values are manipulated, the allocation logic may operate on incorrect price data, resulting in suboptimal or potentially harmful allocations.

Similarly, the UniswapV3.slot0 is also utilized in the getSqrtPrice function in UniHelper.sol. This function is subsequently called by the trade function in Trade.sol. As a result, the trade function is also at risk of executing based on manipulated price data, which could lead to unfavorable trade outcomes.

## Proof of Concept

https://github.com/code-423n4/2024-05-predy/blob/main/src/libraries/Perp.sol#L206

```
    function reallocate(
            DataType.PairStatus storage _assetStatusUnderlying,
            SqrtPerpAssetStatus storage _sqrtAssetStatus
        ) internal returns (bool, bool, int256 deltaPositionBase, int256 deltaPositionQuote) {
@>           (uint160 currentSqrtPrice, int24 currentTick,,,,,) = IUniswapV3Pool(_sqrtAssetStatus.uniswapPool).slot0();
```

https://github.com/code-423n4/2024-05-predy/blob/main/src/libraries/UniHelper.sol#L14

```
    function getSqrtPrice(address uniswapPoolAddress) internal view returns (uint160 sqrtPrice) {
@>        (sqrtPrice,,,,,,) = IUniswapV3Pool(uniswapPoolAddress).slot0();
    }
```

https://github.com/code-423n4/2024-05-predy/blob/main/src/libraries/Trade.sol#L113

```
    function getSqrtPrice(address uniswapPoolAddress, bool isQuoteZero) internal view returns (uint256 sqrtPriceX96) {
@>        return UniHelper.convertSqrtPrice(UniHelper.getSqrtPrice(uniswapPoolAddress), isQuoteZero);
    }

```

https://github.com/code-423n4/2024-05-predy/blob/main/src/libraries/Trade.sol#L49

```
    function trade(
        GlobalDataLibrary.GlobalData storage globalData,
        IPredyPool.TradeParams memory tradeParams,
        bytes memory settlementData
    ) external returns (IPredyPool.TradeResult memory tradeResult) {
        ....
@>        tradeResult.sqrtPrice = getSqrtPrice(pairStatus.sqrtAssetStatus.uniswapPool, pairStatus.isQuoteZero);
```

## Tools Used

Manual Review

## Recommended Mitigation Steps
Use the TWAP function to get the value of sqrtPriceX96. For example:

```
(int24 arithmeticMeanTick, ) = OracleLibrary.consult(uniswapPool, twapInterval);
uint160 sqrtPriceX96 = OracleLibrary.getQuoteAtTick(arithmeticMeanTick, 1e18, address(0), address(0));
```


## Assessed type

Uniswap
Use of `slot0()` to fetch prices can lead to price manipulation
Audit: 2024-05-predy-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-05-predy/blob/2fb1e0ec7a52fc06c2e9c8e561bccba84302e4bb/src/libraries/Perp.sol#L206


# Vulnerability details

## Impact
The `currentSqrtPrice` is used in `Perp.reallocate()` to perform swap if the current tick is out of range. 

However this is calculated based on the `current pool price` i.e (`IUniswapV3Pool(_sqrtAssetStatus.uniswapPool).slot0()`), which can be manipulated and since the `swap` is performed based on this value, it can be manipulated as well resulting in incorrect `swap amounts`.

## Proof of Concept
Under the hood this function fetches the [current price](https://github.com/code-423n4/2024-05-predy/blob/2fb1e0ec7a52fc06c2e9c8e561bccba84302e4bb/src/libraries/Perp.sol#L206) in the pool:

```solidity
        (uint160 currentSqrtPrice, int24 currentTick,,,,,) = IUniswapV3Pool(_sqrtAssetStatus.uniswapPool).slot0();
```
Based on this value, [swap](https://github.com/code-423n4/2024-05-predy/blob/2fb1e0ec7a52fc06c2e9c8e561bccba84302e4bb/src/libraries/Perp.sol#L253-L257) is performed if the current tick is out of range: 
```solidity
        // if the current tick is out of range, then swap
        if (isOutOfRange) {
            (deltaPositionBase, deltaPositionQuote) =
                swapForOutOfRange(_assetStatusUnderlying, currentSqrtPrice, tick, totalLiquidityAmount);
        }
```
If a malicious user decides to distort the pool price in the current block (through a `flash loan` for example), this will directly impact the calculated value.

## Tools Used
Manual Review

## Recommended Mitigation Steps
Use `TWAP` to get the value of `currentSqrtPrice` rather than `slot0()`








## Assessed type

Oracle
Adversaries can create a new pair with the same uniswapPool as exising pair and disrupt reallocation process
Audit: 2024-05-predy-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-05-predy/blob/main/src/libraries/logic/AddPairLogic.sol#L53-L94
https://github.com/code-423n4/2024-05-predy/blob/main/src/libraries/Perp.sol#L262-L289
https://github.com/code-423n4/2024-05-predy/blob/main/src/libraries/Perp.sol#L790-L812


# Vulnerability details

## Impact
- The original pair reallocation process is disrupted, resulting in the following issues:
    - The value of Squart, which should be maintained at `2L*sqrt{x}`, will no longer be accurate.
    - The owner of the Squart position will not be able to unwind their position.
    - The interest calculation for the rebalance position will become dysfunctional.
## Description 
### Duplicate Pair
Each pair/pool carries its own variables to keep track of its accounting.
```
struct PairStatus {
    uint256 id;
    address quoteToken;
    address poolOwner;
    Perp.AssetPoolStatus quotePool;
    Perp.AssetPoolStatus basePool;
    Perp.AssetRiskParams riskParams;
    Perp.SqrtPerpAssetStatus sqrtAssetStatus;
    address priceFeed;
    bool isQuoteZero;
    bool allowlistEnabled;
    uint8 feeRatio;
    uint256 lastUpdateTimestamp;
}
```
`quotePool` and `basePool` track deposit and borrow balance in the pool.  

`sqrtAssetStatus` contains information regarding Squart position of the pair, including its corresponding `uniswapV3Pool`  

When adding a new pair via `PredyPool.sol#registerPair`, the caller must specify the Uniswap pool address to be used for the Squart position. However, there is no validation to ensure that the specified Uniswap pool address is not already associated with another pair.

See: [AddPairLogic.sol#L53-L94](https://github.com/code-423n4/2024-05-predy/blob/main/src/libraries/logic/AddPairLogic.sol#L53-L94)

Therefore, anyone can register a new pair with a Uniswap pool address that is already associated with another pair. Let's define this as **duplicate pair** from this point onward.  

**Note**: It is expected that anyone will be able to create a new pair.  
Ref: https://discord.com/channels/810916927919620096/1242872233345679501/1245440813144477707

### Sharing positionKey for uniswap liquidity position
There should not be any problems because each pool would have their own variables to keep track of their accounting.  

However, there is one thing, if a duplicate pair exists, would be shared between two pairs, and that is `positionKey` on `uniswapV3pool`.  

`positionKey` is constructed from `keccak256(abi.encodePacked(owner, tickLower, tickUpper))`  
The reason that duplicate pairs share the same `positionKey` is that they would have all the same of parameters/varaibles used to calculate `tickLower` and `tickUpper` for Squart position.

Protocol occasionally reallocates `uniswapV3Pool` LP position of each pair to maintain Squart's value at `2*sqrt{x}`.  
Roughly what the process does is the following:  
(1) Calculate new position from `currentTick` of the pool (new `tickLower` and `tickUpper`)  
(2) Remove liqui
Short-circuited evaluation in limit-stop order validation for PerpMarket can lead to unintended order execution
Audit: 2024-05-predy-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-05-predy/blob/main/src/markets/perp/PerpMarketLib.sol#L97-L105


# Vulnerability details

## Impact
- `validateStopPrice` is never evaluated in limit-stop order, leading to an unintended order execution that might cause loss of users' fund.  
## Description
For a limit-stop order, PerpMarket contract validates trade result in this code snippet:  
See: [PerpMarketLib.sol#L97-L105](https://github.com/code-423n4/2024-05-predy/blob/main/src/markets/perp/PerpMarketLib.sol#L97-L105)
```
else if (limitPrice > 0 && stopPrice > 0) {
    // limit & stop order
    if (
        !validateLimitPrice(tradePrice, tradeAmount, limitPrice)
            && !validateStopPrice(oraclePrice, tradePrice, tradeAmount, stopPrice, auctionData)
    ) {
        revert LimitStopOrderDoesNotMatch();
    }
}
```
Due to [short-circuiting rule](https://docs.soliditylang.org/en/latest/types.html#booleans), if `!validateLimitPrice` evaluates to be `false` (limit price is valid) then `validateStopPrice` will never be evaluated. This could lead to an unintended order execution and might cause loss of users' fund.  

### Attack scenario  
A limit-stop order is usually used as a stop loss or take profit order where the order is intended to execute when price reaches to a certain point.  

Imagine a scenario where a signed limit-stop order is publicly published for anyone to be a filler as anyone can fill a close position.  

An advesary can pick up that order and execute it immediately at any price as long as that it satisfies the limit price.  

For instance, if a user opens a short position (Sell) and send this take profit order (Buy):  
```
100 - limitPrice
90  -
80  -
70  - stopPrice
60  -
50  - 
40  - entryPrice
30  - 
```
If the current price is 30, adversary can still execute this take profit order despite the invalid stopPrice (stopPrice > oraclePrice)

## Proof-of-Concept
I wrote a test to demonstrate that a limit-stop buy order is successfully executed even the stopPrice is higher than oraclePrice.  
I actually modified it from the test `testExecuteOrderFailsIfPriceIsLessThanStopPrice` and add a limit price.  
### Steps  
(1) Apply below git diff to the repository  
(2) Run `forge test --match-contract TestPerpExecuteOrderV3 --match-test testExecuteOrderSucceedsEvenPriceIsLessThanStopPrice -vvvv`  
(3) Observe that the trade execute successfully despite the invalid stopPrice
```
diff --git a/test/market/perp/ExecuteOrderV3.t.sol b/test/market/perp/ExecuteOrderV3.t.sol
index 241a1a9..940675f 100644
--- a/test/market/perp/ExecuteOrderV3.t.sol
+++ b/test/market/perp/ExecuteOrderV3.t.sol
@@ -603,6 +603,32 @@ contract TestPerpExecuteOrderV3 is TestPerpMarket {
         perpMarket.executeOrderV3(signedOrder, settlementData);
     }
 
+    function testExecuteOrderSucceedsEvenPriceIsLessThanStopPrice() public {
+        PerpOrderV3 memory order = PerpOrderV3(
+            OrderInfo(address(perpMarket), from1, 0, block.timestamp + 10
Use of `slot0`  in perp.sol can lead to manipulation of reallocation 
Audit: 2024-05-predy-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-05-predy/blob/a9246db5f874a91fb71c296aac6a66902289306a/src/libraries/Perp.sol#L206


# Vulnerability details

## Impact
The uniswap's `slot0` has been used in [perp.sol](https://github.com/code-423n4/2024-05-predy/blob/a9246db5f874a91fb71c296aac6a66902289306a/src/libraries/Perp.sol#L206) to get the `tick` and also the `sqrPrice`:
```
 (uint160 currentSqrtPrice, int24 currentTick,,,,,) = IUniswapV3Pool(_sqrtAssetStatus.uniswapPool).slot0();
```
An attacker can easily manipulate these values to cause the `relocation` to not occur, as in :
```
// If the current tick does not reach the threshold, then do nothing
        if (
            _sqrtAssetStatus.tickLower + _assetStatusUnderlying.riskParams.rebalanceThreshold < currentTick
                && currentTick < _sqrtAssetStatus.tickUpper - _assetStatusUnderlying.riskParams.rebalanceThreshold
        ) {
            saveLastFeeGrowth(_sqrtAssetStatus);

            return (false, false, 0, 0);
        }
```
 and if that didn't  happened, the `sqrPrice` was then used close to end of the function also which can also be manipulated by an attacker:

```
      // if the current tick is out of range, then swap
254        if (isOutOfRange) {
255            (deltaPositionBase, deltaPositionQuote) =
256                swapForOutOfRange(_assetStatusUnderlying, currentSqrtPrice, tick, totalLiquidityAmount);
        }
```

## Proof of Concept
`slot0` used:
https://github.com/code-423n4/2024-05-predy/blob/a9246db5f874a91fb71c296aac6a66902289306a/src/libraries/Perp.sol#L206

`Value` used
https://github.com/code-423n4/2024-05-predy/blob/a9246db5f874a91fb71c296aac6a66902289306a/src/libraries/Perp.sol#L208-216

`sqrPrice` 
https://github.com/code-423n4/2024-05-predy/blob/a9246db5f874a91fb71c296aac6a66902289306a/src/libraries/Perp.sol#L256

## Tools Used

Manual review

## Recommended Mitigation Steps

Use a not easily manipulated value like the the `TWAP`


## Assessed type

MEV