xanoutas/solidity-security-findings huggingface dataset:
Usage of Low-Level .call() Function
Audit: 2024-06-panoptic-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-06-panoptic/blob/153f0d82440b7e63075d55b0659706531431145f/contracts/CollateralTracker.sol#L1


# Vulnerability details

 I have identified another potential vulnerability related to the usage of the `.call()` function:

Smart Contract: CollateralTracker

File: CollateralTracker.sol

Vulnerability: Usage of Low-Level `.call()` Function

Description:
The smart contract uses the low-level `.call()` function in the `_setup()` function, which can introduce potential security risks due to the lack of type safety and the possibility of introducing re-entrancy attacks.

Proof of Concept:
The `.call()` function is used to execute arbitrary code in the `_setup()` function:
```scss
function _setup(address token, uint256 initialAmount, uint256 fee) internal {
    s_underlyingToken = token;
    s_initialized = true;
    _setITMSpreadFee(fee);
    uint256 initialBalance = initialAmount;
    s_underlyingToken.call(bytes4(keccak256("transfer(address,uint256)")), address(this), initialBalance);
}
```
Recommendation:
Avoid using the low-level `.call()` function whenever possible. Instead, use the high-level `.transfer()` or `.transferFrom()` functions. If the `.call()` function must be used, ensure that proper checks are in place to protect against re-entrancy attacks, and use the `.call.value()` function to securely transfer Ether.

Mitigation:
Replace the usage of the low-level `.call()` function with the high-level `.transfer()` function:
```scss
function _setup(address token, uint256 initialAmount, uint256 fee) internal {
    s_underlyingToken = token;
    s_initialized = true;
    _setITMSpreadFee(fee);
    s_underlyingToken.transfer(address(this), initialAmount);
}
```
Disclosure:
The vulnerability described in this report has been discovered by me during a routine code review. I have not exploited it in any way, and I am reporting it to the development team to ensure the security of the protocol.


## Assessed type

call/delegatecall
I will describe a smart way to exploit the smart contract's totalAssets()
Audit: 2024-06-panoptic-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-06-panoptic/blob/153f0d82440b7e63075d55b0659706531431145f/contracts/CollateralTracker.sol#L354


# Vulnerability details

I will describe a smart way to exploit the smart contract's `totalAssets()` unchecked arithmetic operation vulnerability, as previously identified. Here's a step-by-step explanation of the exploit and a report detailing the findings.

**Exploit Scenario:**

1. An attacker identifies the target smart contract with the unchecked arithmetic operation vulnerability in the `totalAssets()` function.

2. The attacker creates a malicious transaction that manipulates the `s_poolAssets` and `s_inAMM` variables to cause an integer overflow or underflow, leading to potential security issues or unexpected contract behavior.

3. The attacker carefully crafts the transaction to set the sum of `s_poolAssets` and `s_inAMM` to a large value, resulting in a significant increase in the contract's perceived total assets.

4. The attacker then interacts with other legitimate smart contracts or external services that rely on the vulnerable contract's `totalAssets()` function to determine the contract's balance or solvency.

5. By exploiting the vulnerability, the attacker tricks these external contracts or services into accepting the inflated total assets value, potentially manipulating them into transferring more funds than they should, siphoning funds, or bypassing security measures based on the faulty total assets value.

**Exploit Report:**

Title: Unchecked Arithmetic Operation Vulnerability in Smart Contract

Introduction:

During our security assessment, we identified a smart contract with a potential vulnerability in the `totalAssets()` function. The unchecked arithmetic operation in this function could be exploited by an attacker to manipulate the contract's total assets value, leading to potential security issues or unexpected contract behavior.

Vulnerability Details:

The `totalAssets()` function contains an unchecked arithmetic operation:

```solidity
unchecked {
    return s_poolAssets + s_inAMM;
}
```

An attacker could exploit this vulnerability by crafting a malicious transaction to manipulate the `s_poolAssets` and `s_inAMM` variables, causing an integer overflow or underflow, and leading to a potential security breach or unexpected contract behavior.

Impact:

By exploiting this vulnerability, an attacker could trick external contracts or services into accepting an inflated total assets value. This manipulation could lead to siphoning funds, bypassing security measures, or other unintended consequences, resulting in financial losses or reputational damage.

Recommendation:

We recommend replacing the unchecked arithmetic operation with a checked one using Solidity's SafeMath library or similar libraries that provide safe arithmetic operations. For example:

```solidity
using SafeMath for uint256;

// ...

function totalAssets() public view returns (uint256 totalManagedAssets) {
Usage of `slot0` is extremely easy to manipulate
Audit: 2024-06-panoptic-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-06-panoptic/blob/153f0d82440b7e63075d55b0659706531431145f/contracts/PanopticFactory.sol#L321
https://github.com/code-423n4/2024-06-panoptic/blob/153f0d82440b7e63075d55b0659706531431145f/contracts/PanopticFactory.sol#L315-L391


# Vulnerability details

## Description
Usage of slot0 is extremely easy to manipulate
## Impact
Pool lp value can be manipulated and cause other users to receive less lp tokens.
## Vulnerability Detail
Panoptic is using slot0 to calculate several variables in their codebase:
slot0 is the most recent data point and is therefore extremely easy to manipulate.
 
      function _mintFullRange(
        IUniswapV3Pool v3Pool,
        address token0,
        address token1,
        uint24 fee
       ) internal returns (uint256, uint256) {
          @audit-issue : use of slot0 can lead to price manipulation .
        (uint160 currentSqrtPriceX96, , , , , , ) = v3Pool.slot0();
      uint128 fullRangeLiquidity;
        unchecked {
            // Since we know one of the tokens is WETH, we simply add 0.1 ETH + 
         worth in tokens
            if (token0 == WETH) {
                fullRangeLiquidity = uint128(
                    Math.mulDiv96RoundingUp(FULL_RANGE_LIQUIDITY_AMOUNT_WETH, 
       currentSqrtPriceX96)
                );
            } else if (token1 == WETH) {
                fullRangeLiquidity = uint128(
                    Math.mulDivRoundingUp(
                        FULL_RANGE_LIQUIDITY_AMOUNT_WETH,
                        Constants.FP96,
                        currentSqrtPriceX96
                    )
                );
            } else {
                // Find the resulting liquidity for providing 1e6 of both tokens
                uint128 liquidity0 = uint128(
                    Math.mulDiv96RoundingUp(FULL_RANGE_LIQUIDITY_AMOUNT_TOKEN, 
            currentSqrtPriceX96)
                );
                uint128 liquidity1 = uint128(
                    Math.mulDivRoundingUp(
                        FULL_RANGE_LIQUIDITY_AMOUNT_TOKEN,
                        Constants.FP96,
                        currentSqrtPriceX96
                    )
                );
         fullRangeLiquidity = liquidity0 > liquidity1 ? liquidity0 : liquidity1;
            }
        }

      // The maximum range we can mint is determined by the tickSpacing of the 
         pool
        // The upper and lower ticks must be divisible by `tickSpacing`, so
        // tickSpacing = 1: tU/L = +/-887272
        // tickSpacing = 10: tU/L = +/-887270
        // tickSpacing = 60: tU/L = +/-887220
        // tickSpacing = 200: tU/L = +/-887200
        int24 tickLower;
        int24 tickUpper;
        unchecked {
            int24 tickSpacing = v3Pool.tickSpacing();
            tickLower = (Constants.MIN_V3POOL_TICK / tickSpacing) * tickSpacing;
            tickUpper = -tickLower;
        }

        bytes memory mintCallback = abi.encode(
            CallbackLib.CallbackData({
SFPM does not update `s_accountPremiumOwed` or 's_accountPremiumGross` accumulators while transferring position
Audit: 2024-06-panoptic-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-06-panoptic/blob/153f0d82440b7e63075d55b0659706531431145f/contracts/SemiFungiblePositionManager.sol#L542


# Vulnerability details

Sellers will lose premia collected on their positions and buyers can avoid paying premia on their positions by transferring the position to another account. This is because the `registerTokenTransfer()` function in SFPM does not update the premia accumulators.
```solidity
function safeTransferFrom(
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes calldata data
    ) public override {
        // we don't need to reentrancy lock on transfers, but we can't allow transfers for a pool during mint/burn with a reentrant call
        // so just check if there is an active reentrancy lock for the relevant pool on the token we're transferring
        if (s_poolContext[TokenId.wrap(id).poolId()].locked) revert Errors.ReentrantCall();

        // update the position data
        registerTokenTransfer(from, to, TokenId.wrap(id), amount);

        // transfer the token (note that all state updates are completed before reentrancy is possible through onReceived callbacks)
        super.safeTransferFrom(from, to, id, amount, data);
    }
```
```solidity
function registerTokenTransfer(address from, address to, TokenId id, uint256 amount) internal {
        id.validate();

        // Extract univ3pool from the poolId map to Uniswap Pool
        IUniswapV3Pool univ3pool = s_poolContext[id.poolId()].pool;

        uint256 numLegs = id.countLegs();
        for (uint256 leg = 0; leg < numLegs; ) {
            LiquidityChunk liquidityChunk = PanopticMath.getLiquidityChunk(
                id,
                leg,
                uint128(amount)
            );

            // construct the positionKey for the from and to addresses
            bytes32 positionKey_from = keccak256(
                abi.encodePacked(
                    address(univ3pool),
                    from,
                    id.tokenType(leg),
                    liquidityChunk.tickLower(),
                    liquidityChunk.tickUpper()
                )
            );
            bytes32 positionKey_to = keccak256(
                abi.encodePacked(
                    address(univ3pool),
                    to,
                    id.tokenType(leg),
                    liquidityChunk.tickLower(),
                    liquidityChunk.tickUpper()
                )
            );

            // Revert if recipient already has that position
            if (
                (LeftRightUnsigned.unwrap(s_accountLiquidity[positionKey_to]) != 0) ||
                (LeftRightSigned.unwrap(s_accountFeesBase[positionKey_to]) != 0)
            ) revert Errors.TransferFailed();

            // Revert if sender has long positions in that chunk or the entire liquidity is not being transferred
            LeftRightUnsigned fromLiq = s_accountLiquidity[positionKey_from];
            if
Use of delegatecall Inside Loop in Payable Function within Multicall contract
Audit: 2024-06-panoptic-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-06-panoptic/blob/153f0d82440b7e63075d55b0659706531431145f/contracts/base/Multicall.sol#L12-L36


# Vulnerability details

## Impact
Detailed description of the impact of this finding.

Contract:
Multicall.sol

Function:
multicall(bytes[])

Lines:
#12-36

Issue:
Use of delegatecall Inside Loop in Payable Function

The implementation of delegatecall inside a loop within a payable function is a pattern that can lead to significant security risks, including DOS attacks and unexpected behaviour due to external control over contract logic. This pattern is particularly dangerous because it can be exploited to drain gas.
## Proof of Concept
Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.

The function multicall(bytes[]) contains the following line of code:
```sol
(success, result) = address(this).delegatecall(data[i]);
```
This line indicates that multiple delegate calls are made within a loop. If any of the called contracts are malicious or compromised, they could potentially hijack the control flow, leading to severe consequences.

```txt
Test File Name: test/foundry/core/Multicall.t.sol
Prerequisite: Remove the "abstract" prefix from the Multicall.sol file contract name.
And save the below foundry test code in the file path test/foundry/core/Multicall.t.sol of your github codebase.
```
```sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

import "ds-test/test.sol";
import {Multicall} from "@contracts/base/Multicall.sol"; 

contract DOSAttackTest is DSTest {
    Multicall multicall;

    function setUp() public {
        multicall = new Multicall();
    }

    function testDOSAttack() public {
        // Prepare a large array of calldata that would exhaust gas limits
        uint256[] memory datae = new uint256[](1);
        datae[0] = uint256(115792089237316195423570985008687907853269984665640564039457584007913129639935);
        bytes[] memory data = new bytes[](1); // Adjust the size as needed for the test
        data[0] = abi.encodePacked(datae);
        for (uint256 i = 0; i < data.length; i++) {
            // Fill the array with calldata that calls a function in the Multicall contract
            data[i] = abi.encodeWithSelector(Multicall.multicall.selector);
        }

        // Expect the transaction to fail due to out of gas
        (bool success, bytes memory returnedData) = address(multicall).call{gas: 1000000}(abi.encodeWithSelector(Multicall.multicall.selector, data));

        assertTrue(!success, "DOS attack should fail the transaction");
    }
}

```
```bash
forge test -vvvvv --match-contract DOSAttackTest  --fork-url "https://eth-mainnet.g.alchemy.com/v2/{TOKEN}"
[⠊] Compiling...
No files changed, compilation skipped

Ran 1 test for test/foundry/core/Multicall.t.sol:DOSAttackTest
[PASS] testDOSAttack() (gas: 8475)
Traces:
  [198337] DOSAttackTest::setUp()
    ├─ [160808] → new Multica
Math.sol library uses the bitwise-xor operator instead of the exponentiation operator
Audit: 2024-06-panoptic-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-06-panoptic/blob/153f0d82440b7e63075d55b0659706531431145f/contracts/libraries/Math.sol#L340-L433
https://github.com/code-423n4/2024-06-panoptic/blob/153f0d82440b7e63075d55b0659706531431145f/contracts/libraries/Math.sol#L414


# Vulnerability details

## Impact
Detailed description of the impact of this finding.

File: Math.sol

Function: mulDiv(uint256,uint256,uint256)

Location: Line 340-433

The function mulDiv in the Math.sol library uses the bitwise-xor operator `^` instead of the exponentiation operator `**`. This occurs at line 414 in the expression ```inv = (3 * denominator) ^ 2```. This misuse of operators can lead to incorrect calculations and potentially severe arithmetic errors in smart contracts that rely on this function for mathematical operations.

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

The incorrect operator is used in the following line:
```sol
// Incorrect exponentiation
inv = (3 * denominator) ^ 2; // Line 414
```

The correct implementation should use the exponentiation operator `**` to raise the result of (3 `*` denominator) to the power of 2.

```txt
File Name: test/foundry/libraries/Maths.t.sol
Prerequisites:
1. Copy the solidity code below into the file and path: test/foundry/libraries/Maths.t.sol
2. Save the file.
3. Within terminal, in the panoptic root folder run forge test -vvvvv --match-contract MathsTest  --fork-url "https://eth-mainnet.g.alchemy.com/v2/{Token}"
Please see POC below.
```

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

import "ds-test/test.sol";
import "../../../contracts/libraries/Math.sol";

contract MathsTest is DSTest {

    function testMulDivWithFailure() public {
        // Values that typically cause failure due to incorrect operator
        uint256 a = 123456789;
        uint256 b = 987654321;
        uint256 c = 2;

        // Expected value with correct exponentiation
        uint256 expected = (a * b) ** c;

        // Actual value using the incorrect operator
        uint256 actual = Math.mulDiv(a, b, c);

        // Assert that the actual value does not match the expected value
        assertEq(expected, actual, "The mulDiv function returned the incorrect value.");
    }
}
```
```bash
forge test -vvvvv --match-contract MathsTest  --fork-url "https://eth-mainnet.g.alchemy.com/v2/{Token}"
[⠊] Compiling...
No files changed, compilation skipped

Ran 1 test for test/foundry/libraries/Maths.t.sol:MathsTest
[FAIL. Reason: assertion failed] testMulDivWithFailure() (gas: 18701)
Logs:
  Error: The mulDiv function returned the incorrect value.
  Error: a == b not satisfied [uint]
        Left: 14867566530049990397812181822702361
       Right: 60966315556317634

Traces:
  [18701] MathsTest::testMulDivWithFailure()
    ├─ emit log_named_string(key: "Error", val: "The mulDiv function returned the incorrect value.")
Incorrect Event Emission in Redeem Function
Audit: 2024-06-panoptic-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-06-panoptic/blob/153f0d82440b7e63075d55b0659706531431145f/contracts/CollateralTracker.sol#L652


# Vulnerability details

## Impact
The `redeem` function is designed to convert a specified number of shares into the corresponding amount of underlying assets. The current implementation emits a `Withdraw` event, which is misleading and inconsistent with the function's purpose. This discrepancy can cause confusion for developers, users, and external systems that rely on event logs to track and audit contract actions. It could lead to incorrect assumptions about the nature of the transaction, complicating monitoring, debugging, and integration processes.

## Proof of Concept
https://github.com/code-423n4/2024-06-panoptic/blob/153f0d82440b7e63075d55b0659706531431145f/contracts/CollateralTracker.sol#L652

## Tools Used

## Recommended Mitigation Steps
Implement `Redeem` event and  replace the `Withdraw` event with a `Redeem` event to accurately reflect the function's action.


## Assessed type

Other
Incorrect Assumption in FactoryNFT Can Lead to Reverts During Token URI Retrieval.
Audit: 2024-06-panoptic-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-06-panoptic/blob/153f0d82440b7e63075d55b0659706531431145f/contracts/base/FactoryNFT.sol#L40-L49


# Vulnerability details

## Impact
`tokenURI` function assumes that the first 160 bits of the `tokenId` represent a valid `PanopticPool` address. However, it does not perform any validation to ensure that the address is indeed a valid `PanopticPool` contract.

If a `tokenId` is passed that does not correspond to a valid `PanopticPoo`l address, the function will still attempt to interact with the contract at that address. This can lead to even revert if the address does not implement the expected `PanopticPool` interface.

## Proof of Concept
[FactoryNFT contract (@contracts\base\FactoryNFT.sol:40-49)](https://github.com/code-423n4/2024-06-panoptic/blob/153f0d82440b7e63075d55b0659706531431145f/contracts/base/FactoryNFT.sol#L40-L49)
```solidity
function tokenURI(uint256 tokenId) public view override returns (string memory) {
    address panopticPool = address(uint160(tokenId));

    return
        constructMetadata(
            panopticPool,
            PanopticMath.safeERC20Symbol(PanopticPool(panopticPool).univ3pool().token0()),
            PanopticMath.safeERC20Symbol(PanopticPool(panopticPool).univ3pool().token1()),
            PanopticPool(panopticPool).univ3pool().fee()
        );
}
```
As you can see, the function directly converts the first 160 bits of `tokenId` into an address using `address(uint160(tokenId))`. It then proceeds to interact with the contract at that address, assuming it is a valid `PanopticPool` contract.

Let's consider a scenario where a `tokenId` is passed that does not correspond to a valid `PanopticPool` address. For example:
```solidity
uint256 invalidTokenId = 123456789; // An arbitrary token ID that does not represent a valid PanopticPool address

string memory metadata = factoryNFT.tokenURI(invalidTokenId);
```
In this case, the `tokenURI` function will attempt to interact with the contract at the address represented by `invalidTokenId`. If that address does not implement the `PanopticPool` interface or does not have the required functions, the function calls will revert, causing the transaction to fail.

## Tools Used
Vs Code
## Recommended Mitigation Steps
Add a validation check to ensure that the extracted address is a valid `PanopticPool` contract before interacting with it, can be done by adding a mapping or a whitelist of valid `PanopticPool` addresses and checking against it, or by using a factory pattern where only valid `PanopticPool` contracts are created and tracked.
```solidity
function tokenURI(uint256 tokenId) public view override returns (string memory) {
    address panopticPool = address(uint160(tokenId));

    // Validate that the extracted address is a valid PanopticPool contract
    require(isValidPanopticPool(panopticPool), "Invalid PanopticPool address");

    return
        constructMetadata(
            panopticPool,
            PanopticMath.sa