xanoutas/solidity-security-findings huggingface dataset:
Arbitrary Code Execution Vulnerability in multicall Function
Audit: 2023-11-panoptic-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-11-panoptic/blob/aa86461c9d6e60ef75ed5a1fe36a748b952c8666/contracts/multicall/Multicall.sol#L12


# Vulnerability details

## Impact
Malicious actors can deploy contracts with harmful code executed  within the SemiFungiblePositionManager contract context . This opens up the system to security risks and  Possibility of overloading the contract causing a denial-of-service.


## Proof of Concept
The `SemiFungiblePositionManager` contract inherits from the `Multicall` abstract contract making the `multicall()` publicly accessible.

The attacker creates malicious data containing a series of calls designed to manipulate the contract state. This data could include calls to transfer assets, update balances or even exploit vulnerabilities within the contract or called functions. The attacker passes the crafted malicious data to the `multicall` function. The function could potentially execute all the included calls. The attacker can then manipulate the contract state to their advantage potentially stealing funds, inflating their token balances or disrupting the system's operation.
```
    function multicall(bytes[] calldata data) public payable returns (bytes[] memory results) {
```

## Tools Used
Manual analysis
## Recommended Mitigation Steps
Implement proper access control mechanisms within the `multicall` function 


## Assessed type

Access Control
`SemiFungiblePositionManager._getFeesBase` may overflow without an error and store a wrong value.
Audit: 2023-11-panoptic-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-11-panoptic/blob/main/contracts/SemiFungiblePositionManager.sol#L1119-L1121
https://github.com/code-423n4/2023-11-panoptic/blob/main/contracts/SemiFungiblePositionManager.sol#L1062
https://github.com/code-423n4/2023-11-panoptic/blob/main/contracts/SemiFungiblePositionManager.sol#L1209-L1211
https://github.com/code-423n4/2023-11-panoptic/blob/main/contracts/SemiFungiblePositionManager.sol#L1403-L1409


# Vulnerability details

## Impact

`SemiFungiblePositionManager._collectAndWritePositionData` uses `_getFeesBase` to determine the amount of fees to collect.
```solidity
    int256 amountToCollect = _getFeesBase(univ3pool, startingLiquidity, liquidityChunk).sub(
        s_accountFeesBase[positionKey]
    );
```
https://github.com/code-423n4/2023-11-panoptic/blob/main/contracts/SemiFungiblePositionManager.sol#L1209-L1211

It is expected to be reverted when `s_accountFeesBase[positionKey]` is a value near 2 ** 128 and the return of `_getFeesBase`
becomes a small number due to an overflow over 2 ** 128.
In this case, `_getFeesBase(univ3pool, startingLiquidity, liquidityChunk).sub(s_accountFeesBase[positionKey]);` reverts due to underflow,
which stops minting and burning the position of SemiFungiblePositionManager.
Also, the fee amount is one of parameters to determine the price of an option, so an error in calculating fees may manipulate the option prices.
https://github.com/code-423n4/2023-11-panoptic/blob/e2f3d3f3b4c60ee403fcd9d19a0b09cd989e96fd/contracts/SemiFungiblePositionManager.sol#L1403-L1409


## Proof of Concept

Uniswap `Position.sol` assumes the fee growth of a position is less than 2 ** 128 between two collection timestamps.
```solidity
uint128 tokensOwed0 =
    uint128(
        FullMath.mulDiv(
            feeGrowthInside0X128 - _self.feeGrowthInside0LastX128,
            _self.liquidity,
            FixedPoint128.Q128
        )
    );
```
https://github.com/Uniswap/v3-core/blob/d8b1c635c275d2a9450bd6a78f3fa2484fef73eb/contracts/libraries/Position.sol#L61-L68

This is achievable by collecting fees regularly and also `feeGrowthInside0X128 - _self.feeGrowthInside0LastX128` is right even if `feeGrowthInside0X128` overflows.
https://github.com/Uniswap/v3-core/issues/573#issuecomment-1218370592

On the other hand, `SemiFungiblePositionManager._getFeesBase` assumes `feeGrowthInside0LastX128 * liquidity` is less than 2 ** 128.
```solidity
feesBase = int256(0)
    .toRightSlot(int128(int256(Math.mulDiv128(feeGrowthInside0LastX128, liquidity))))
    .toLeftSlot(int128(int256(Math.mulDiv128(feeGrowthInside1LastX128, liquidity))));
```

In solidity, casting int256 to int128 takes the least significant 128 bits of int256.
```solidity
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

import "forge-std/Test.sol";

contract MathTest2 is Test {
    function test_int128() public {
        int256 a = 2 ** 128;
        assertEq(int128(a), 0);
    }
}
```

Therefore, `int128(int256(Math.mulDi
s_accountPremiumOwed and s_accountPremiumGross are not transferred with safeTransferFrom
Audit: 2023-11-panoptic-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
Accrued premium is not transferred to the new owner. Old owner will retain his owned/gross premium.

## Proof of Concept
`s_accountPremiumOwed` and `s_accountPremiumGross` nominate the fees account has accrued from their positions. 

The function call ERC1155Minimal > safeTransferFrom > afterTokenTransfer > registerTokenTransfer transfers many storage variables related to the position, but not the `s_accountPremiumOwed` and `s_accountPremiumGross`

These 2 values are used to calculate account premium in the `getAccountPremium` function

```bash
// Extract the account liquidity for a given uniswap pool, owner, token type, and ticks
uint256 acctPremia = isLong == 1
    ? s_accountPremiumOwed[positionKey]
    : s_accountPremiumGross[positionKey];

premiumToken0 = acctPremia.rightSlot();
premiumToken1 = acctPremia.leftSlot();
```

Since the storage values are not updated on token transfer, the old owner retains the premium owned/gross values.


## Tools Used
Manual review

## Recommended Mitigation Steps
Update the premium storage variables after token transfer.


## Assessed type

Token-Transfer
Liquidity calculations do not take into account decimal values of tokens.
Audit: 2023-11-panoptic-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-11-panoptic/blob/aa86461c9d6e60ef75ed5a1fe36a748b952c8666/contracts/SemiFungiblePositionManager.sol#L526
https://github.com/code-423n4/2023-11-panoptic/blob/aa86461c9d6e60ef75ed5a1fe36a748b952c8666/contracts/SemiFungiblePositionManager.sol#L697
https://github.com/code-423n4/2023-11-panoptic/blob/aa86461c9d6e60ef75ed5a1fe36a748b952c8666/contracts/SemiFungiblePositionManager.sol#L882
https://github.com/code-423n4/2023-11-panoptic/blob/aa86461c9d6e60ef75ed5a1fe36a748b952c8666/contracts/libraries/PanopticMath.sol#L119-L125


# Vulnerability details

## Impact
When generating `options` via `SFPM`, the `liquidity` calculation omits the `decimal` values of `tokens`, resulting in extremely small actual transfer amounts. 
This prevents the precise creation of `put/call` `options` and may lead to `fund` losses due to `rounding` when dealing with small values.

## Proof of Concept
When a user attempts to create `options`, the `mintTokenizedPosition` function is triggered. 
Within this function, the `_validateAndForwardToAMM` function is called. 
https://github.com/code-423n4/2023-11-panoptic/blob/aa86461c9d6e60ef75ed5a1fe36a748b952c8666/contracts/SemiFungiblePositionManager.sol#L526
```
function mintTokenizedPosition() {
    (totalCollected, totalSwapped, newTick) = _validateAndForwardToAMM();
}
```
Ultimately, we invoke the `_createPositionInAMM` function.
https://github.com/code-423n4/2023-11-panoptic/blob/aa86461c9d6e60ef75ed5a1fe36a748b952c8666/contracts/SemiFungiblePositionManager.sol#L697
```
function _validateAndForwardToAMM() internal returns () {
    (totalMoved, totalCollectedFromAMM, itmAmounts) = _createPositionInAMM();
}
```
Inside this function, `liquidity` is calculated based on the `tokenId` and `positionSize`.
https://github.com/code-423n4/2023-11-panoptic/blob/aa86461c9d6e60ef75ed5a1fe36a748b952c8666/contracts/SemiFungiblePositionManager.sol#L882
```
function _createPositionInAMM() {
    uint256 liquidityChunk = PanopticMath.getLiquidityChunk();
}
```
As you can see, we calculate the `token` amount for `liquidity` by simply multiplying the `optionRatio` and `positionSize`.
https://github.com/code-423n4/2023-11-panoptic/blob/aa86461c9d6e60ef75ed5a1fe36a748b952c8666/contracts/libraries/PanopticMath.sol#L119-L125
```
function getLiquidityChunk() {
    uint256 amount = uint256(positionSize) * tokenId.optionRatio(legIndex);
    if (tokenId.asset(legIndex) == 0) {
        legLiquidity = Math.getLiquidityForAmount0(
            uint256(0).addTickLower(tickLower).addTickUpper(tickUpper),
            amount
        );
    }
}
```
In the actual `liquidity` calculation in `Uniswap`, the `decimal` value of the `token` is also taken into consideration.

For instance, if a user intends to create one `call option` for `1 ETH`, the initial approach might involve creating a single `token(SFPM)` with `optionRatio` = `1` and `positionSize` = `1`.
However, this results in only `1` underlying `token`, leading
Invalid Address in getPoolId()
Audit: 2023-11-panoptic-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-11-panoptic/blob/aa86461c9d6e60ef75ed5a1fe36a748b952c8666/contracts/libraries/PanopticMath.sol#L38C5-L40C6#L42


# Vulnerability details

## Impact
The ```getPoolId()``` function fails to check the validity of the provided Uniswap v3 pool address. If an invalid address is passed, it could be used to manipulate calls to unintended pools or cause other unintended consequences.
1. The attacker could obtain the pool ID for a non-existent or malicious pool. This pool ID might then be used to call other functions on that pool, potentially causing unintended actions or losses for users.
2. The attacker could compromise the integrity of the library by injecting malicious code. This could lead to unintended behavior or even compromise the security of the entire library.
3. Denial of Service attacks by repeatedly calling the function with invalid addresses resulting in system unavailability due to continous processing of invalid requests

## Proof of Concept
1. For example we have the vulnerable getPoolId function in the PanopticMath library.

2. We create a contract called MaliciousContract with a trustedPool address.

3. The ```manipulateGetPoolId``` function in MaliciousContract demonstrates how an attacker could manipulate the ```getPoolId()``` function by creating a malicious address ```(maliciousAddress)``` and calling the vulnerable function with this manipulated address.

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

library PanopticMath {
    function getPoolId(address univ3pool) internal pure returns (uint64) {
        return uint64(uint160(univ3pool) >> 96);
    }
}

contract MaliciousContract {
    using PanopticMath for *;

    address public trustedPool;

    constructor(address _trustedPool) {
        trustedPool = _trustedPool;
    }

    function manipulateGetPoolId() public view returns (uint64) {
        // An attacker can manipulate the getPoolId function by passing a malicious address
        address maliciousAddress = address(uint160(trustedPool) + 1);

        // Call the vulnerable function with the manipulated address
        return maliciousAddress.getPoolId();
    }
}

```

## Tools Used
1. VScode
2. MythX
3. Remix

## Recommended Mitigation Steps
The most important mitigation step is to include proper input validation by adding a check to confirm if it is a valid address in order to fix this vulnerability.

``` solidity
pragma solidity ^0.8.0;

library PanopticMath {
    function getPoolId(address univ3pool) internal pure returns (uint64) {
        require(isValidAddress(univ3pool), "Invalid pool address");

        return uint64(uint160(univ3pool) >> 96);
    }

    function isValidAddress(address addr) internal pure returns (bool) {
        // Check if the address is not zero and has the correct length
        return addr != address(0) && addr == address(uint160(addr));
    }
}

```


## Assessed type

Invalid Validation
rounding error could leads to exploitation
Audit: 2023-11-panoptic-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-11-panoptic/blob/aa86461c9d6e60ef75ed5a1fe36a748b952c8666/contracts/libraries/PanopticMath.sol#L168-L189
https://github.com/code-423n4/2023-11-panoptic/blob/aa86461c9d6e60ef75ed5a1fe36a748b952c8666/contracts/SemiFungiblePositionManager.sol#L804


# Vulnerability details

## Impact
calculating net0 amount might be wrong leads getting more amount in Swap
## Proof of Concept

                  function convert1to0(int256 amount, uint160 sqrtPriceX96) internal pure returns (int256) {
        unchecked {
            // the tick 443636 is the maximum price where (price) * 2**192 fits into a uint256 (< 2**256-1)
            // above that tick, we are forced to reduce the amount of decimals in the final price by 2**64 to 2**128
            if (sqrtPriceX96 < 340275971719517849884101479065584693834) {
                int256 absResult = Math
                    .mulDiv(Math.absUint(amount), 2 ** 192, uint256(sqrtPriceX96) ** 2)
                    .toInt256();
                return amount < 0 ? -absResult : absResult;
            } else {
                int256 absResult = Math
                    .mulDiv(
                        Math.absUint(amount),
                        2 ** 128,
                        Math.mulDiv64(sqrtPriceX96, sqrtPriceX96)
                    )
                    .toInt256();
                return amount < 0 ? -absResult : absResult;
            }
        }
    }
 
In this convertion , absResult could be rounding down cause of implementation .So
when calculation net 0 amount , there will be slightly different cause of this rounding problem.

         int256 net0 = itm0 - PanopticMath.convert1to0(itm1, sqrtPriceX96);

                zeroForOne = net0 < 0;

                //compute the swap amount, set as positive (exact input)
                swapAmount = -net0;


this convertion is rounding down So net 0 is probably +1 than actual amount as if there is rounding error in converting 
        

## Tools Used
manual view
## Recommended Mitigation Steps
rounding up when it's less than itm 0 
rounding down when it's greater than it 0


## Assessed type

Math
Malicious user can move excess liquidity out of uniswap than what he/she has
Audit: 2023-11-panoptic-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-11-panoptic/blob/aa86461c9d6e60ef75ed5a1fe36a748b952c8666/contracts/SemiFungiblePositionManager.sol#L996-L999
https://github.com/code-423n4/2023-11-panoptic/blob/aa86461c9d6e60ef75ed5a1fe36a748b952c8666/contracts/SemiFungiblePositionManager.sol#L985-L990


# Vulnerability details

## Impact
User can move liquidity that is greater than what they own in uniswap when opening a new long position.

## Proof of Concept
Long position moves liquidity out of uniswap so when increasing your long position, there is a check that ensures that the amount you want to move is less than what you own to ensure only your liquidity is moved.

 
                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();


chunkLiquidity is the amount to be moved into or out of uniswap depend on if it is a long or short position 

But when opening a new long position it does not check if chunkLiquidity is less than startingLiquidity it just goes head to remove the liquidity.

                    revert Errors.NotEnoughLiquidity();
                } else {
                    // we want to move less than what already sits in uniswap, no problem:
                    updatedLiquidity = startingLiquidity - chunkLiquidity;
                }

                /// @dev If the isLong flag is 1=long and the position is minted, then this is opening a long position
                /// @dev so the amount of short liquidity should increase.
                if (!_isBurn) {
                    removedLiquidity += chunkLiquidity;
                }
            }

 
## Tools Used
Manual Review 
## Recommended Mitigation Steps
Add a check that ensures chunkliquidity is less than startingLiquidity.


## Assessed type

Context
malicious person can set anyone to approve or revoke approval for tokens
Audit: 2023-11-panoptic-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
A malicious person or owner can disrupt the approval for each tokens

## Proof of Concept
In `ERC1155Minimal::setApprovalForAll()` the function visibility is set to public so anyone can call the function, which means that any person can become the **owner**
```
  mapping(address owner => mapping(address operator => bool approvedForAll))
        public isApprovedForAll;
```
So according to the mapping the owner can set the operator of the token 
So , if a malicious person becomes the owner , he can set himself the owner and arbitrarage the whole token operation 

```
  function setApprovalForAll(address operator, bool approved) public {
        isApprovedForAll[msg.sender][operator] = approved;

        emit ApprovalForAll(msg.sender, operator, approved);
    }
```

## Tools Used
Manual Review

## Recommended Mitigation Steps
Change the function visibility , or add specific access control who can call the function or set the operator after verifying


## Assessed type

Access Control
Add two int256 may lead to a Overflow
Audit: 2023-11-panoptic-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
Users can be affected to Overflow due to incorrect addition of int256 numbers. This function is used in multiple places like for calculating: _validateAndForwardToAMM, _createPositionInAMM, _updateStoredPremia and getAccountPremium

## Proof of Concept

Add these 2 functions inside LeftRight.t.sol


     function test_addUint() public {
        uint256 y = type(uint256).max;
        uint256 x = 100;
        harness.add(x,y);
    }

    function test_addInt() public {
        int256 y = type(int256).max;
        int256 x = 100;
        harness.add(x, y);
    }


When testing with Uint256 values, the function reverts as expected:

[5571] LeftRightTest::test_addUint() 
    ├─ [448] LeftRightHarness::add(100, 115792089237316195423570985008687907853269984665640564039457584007913129639935 [1.157e77]) [staticcall]
    │   └─ ← "UnderOverFlow()"
    └─ ← "UnderOverFlow()"

But when we test it with Int256 values, it Overflows which should not happen

  [5898] LeftRightTest::test_addInt() 
    ├─ [662] LeftRightHarness::add(100, 57896044618658097711785492504343953926634992332820282019728792003956564819967) [staticcall]
    │   └─ ← 57896044618658097711785492504343953926294709965899343556265417396524796608611
    └─ ← ()

This is happening due to the different versions of the add function

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

and

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

For Uint256 it is checked if the result is smaller than the inputs. But this check is not done for Int256 inputs.

## Tools Used

Foundry

## Recommended Mitigation Steps

Use the prevention from the add function before


## Assessed type

Under/Overflow
nonReentrant modifier not used on external getZetaFromETH() function
Audit: 2023-11-panoptic-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-11-zetachain/blob/b237708ed5e86f12c4bddabddfd42f001e81941a/repos/protocol-contracts/contracts/evm/tools/ZetaTokenConsumerPancakeV3.strategy.sol#L103-L124


# Vulnerability details

## Impact
getZetaFromEth function does not use the nonReentrant modifier. This could potentially leave the contract vulnerable to reentrancy attacks. 

## Tools Used
Manual Review

## Recommended Mitigation Steps
To fix this, the nonReentrant modifier should be used in all external functions that modify the contract's state.


## Assessed type

Reentrancy
Attacker can steal all fees from SFPM in pools with ERC777 tokens.
Audit: 2023-11-panoptic-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-11-panoptic/blob/aa86461c9d6e60ef75ed5a1fe36a748b952c8666/contracts/SemiFungiblePositionManager.sol#L521
https://github.com/code-423n4/2023-11-panoptic/blob/aa86461c9d6e60ef75ed5a1fe36a748b952c8666/contracts/SemiFungiblePositionManager.sol#L1004-L1006
https://github.com/code-423n4/2023-11-panoptic/blob/aa86461c9d6e60ef75ed5a1fe36a748b952c8666/contracts/SemiFungiblePositionManager.sol#L1031-L1033
https://github.com/code-423n4/2023-11-panoptic/blob/aa86461c9d6e60ef75ed5a1fe36a748b952c8666/contracts/SemiFungiblePositionManager.sol#L1062-L1066
https://github.com/code-423n4/2023-11-panoptic/blob/aa86461c9d6e60ef75ed5a1fe36a748b952c8666/contracts/SemiFungiblePositionManager.sol#L1209-L1211
https://github.com/code-423n4/2023-11-panoptic/blob/aa86461c9d6e60ef75ed5a1fe36a748b952c8666/contracts/SemiFungiblePositionManager.sol#L626-L630


# Vulnerability details


## Impact
An attacker can steal all outstanding fees belonging to the SFPM in a uniswap pool if a token in the pool is an ERC777. 

## Proof of Concept

The attack is possible due to the following sequence of events when minting a short option with `minTokenizedPosition()`: 

1. ERC1155 is minted. [L521](https://github.com/code-423n4/2023-11-panoptic/blob/aa86461c9d6e60ef75ed5a1fe36a748b952c8666/contracts/SemiFungiblePositionManager.sol#L521)
```solidity
_mint(msg.sender, tokenId, positionSize);
```

2. Liquidity is updated. [L1004](https://github.com/code-423n4/2023-11-panoptic/blob/aa86461c9d6e60ef75ed5a1fe36a748b952c8666/contracts/SemiFungiblePositionManager.sol#L1004-L1006)
```solidity
            s_accountLiquidity[positionKey] = uint256(0).toLeftSlot(removedLiquidity).toRightSlot(
```

3. An LP position is minted and tokens are transferred from `msg.sender` to uniswap. [L1031](https://github.com/code-423n4/2023-11-panoptic/blob/aa86461c9d6e60ef75ed5a1fe36a748b952c8666/contracts/SemiFungiblePositionManager.sol#L1031-L1033)
```solidity
            _moved = isLong == 0
                ? _mintLiquidity(_liquidityChunk, _univ3pool) 
                : _burnLiquidity(_liquidityChunk, _univ3pool); 
```

4. feesBase is updated. [L1062](https://github.com/code-423n4/2023-11-panoptic/blob/aa86461c9d6e60ef75ed5a1fe36a748b952c8666/contracts/SemiFungiblePositionManager.sol#L1062-L1066)
```solidity
s_accountFeesBase[positionKey] = _getFeesBase(
            _univ3pool,
            updatedLiquidity,
            _liquidityChunk
        );
```

If at least one of the tokens transferred at step 3 is an ERC777 `msg.sender` can implement a `tokensToSender()` hook and transfer the ERC1155 before `s_accountFeesBase[positionKey]` has been updated. `registerTokenTransfer()` will copy  `s_accountLiquidity[positionKey]>0` and `s_accountFeesBase[positionKey] = 0` such that the receiver now has a ERC1155 position with non-zero liquidity but a `feesBase = 0`.

When this position is burned the fees collected are calculated based on: [L209](https://github.com/code-423n4/20