Panoptic - K42's results

Effortless options trading on any token, any strike, any size.

General Information

Platform: Code4rena

Start Date: 27/11/2023

Pot Size: $60,500 USDC

Total HM: 7

Participants: 72

Period: 7 days

Judge: Picodes

Total Solo HM: 2

Id: 309

League: ETH

Panoptic

Findings Distribution

Researcher Performance

Rank: 32/72

Findings: 2

Award: $74.70

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

🌟 Selected for report: sivanesh_808

Also found by: 0x6980, 0x886699, 0xAnah, 0xhex, 0xta, Eurovickk, JCK, K42, SAQ, SY_S, Sathish9098, alix40, arjun16, fnanni, naman1778, nisedo, unique

Awards

19.8173 USDC - $19.82

Labels

bug
G (Gas Optimization)
grade-b
sponsor confirmed
G-14

External Links

Gas Optimization Report for Panoptic by K42

Possible Optimizations in SemiFungiblePositionManager.sol

Possible Optimization 1 =

Here is the optimized code snippet:

function initializeAMMPool(address token0, address token1, uint24 fee) external {
    address univ3pool = FACTORY.getPool(token0, token1, fee);
    require(address(univ3pool) != address(0), "UniswapPoolNotInitialized"); // Replaced if check with require for clarity and gas efficiency
    require(s_AddrToPoolIdData[univ3pool] == 0, "PoolAlreadyInitialized"); // Replaced if check with require for clarity and gas efficiency

    uint64 poolId = PanopticMath.getPoolId(univ3pool);

    require(address(s_poolContext[poolId].pool) == address(0), "PoolContextExists"); // Remove while loop and add a require to ensure pool context does not exist
    s_poolContext[poolId] = PoolAddressAndLock({pool: IUniswapV3Pool(univ3pool), locked: false}); // Direct assignment without change
    s_AddrToPoolIdData[univ3pool] = uint256(poolId) + 2 ** 255; // Direct assignment without change

    emit PoolInitialized(univ3pool); // Emit event without change
}
  • Estimated gas saved = This optimization can save gas by reducing the number of operations and checks. This change is safe as it maintains the core logic of the function while removing redundant operations.

Possible Optimization 2 =

Here is the optimized code:

function afterTokenTransfer(address from, address to, uint256[] memory ids, uint256[] memory amounts) internal override {
    uint256 length = ids.length; // Cache the length of the ids array
    for (uint256 i = 0; i < length; ) { // Use cached length for loop condition
        registerTokenTransfer(from, to, ids[i], amounts[i]);
        unchecked { ++i; }
    }
}
  • Estimated gas saved = This optimization can save gas by reducing the number of SLOAD operations. The exact savings depend on the length of the ids array but could be significant for larger arrays. This change is safe as it does not alter the logic, only the computation method.

Possible Optimizations in ERC1155Minimal.sol

Possible Optimization 1 =

Here is the optimized code snippet:

function safeTransferFrom(
    address from,
    address to,
    uint256 id,
    uint256 amount,
    bytes calldata data
) public {
    if (!(msg.sender == from || isApprovedForAll[from][msg.sender])) revert NotAuthorized();

    balanceOf[from][id] -= amount;

    // balance will never overflow
    unchecked {
        balanceOf[to][id] += amount;
    }

    afterTokenTransfer(from, to, id, amount);

    emit TransferSingle(msg.sender, from, to, id, amount);

    // Consolidate external call and condition check
    if (to.code.length != 0 && ERC1155Holder(to).onERC1155Received(msg.sender, from, id, amount, data) != ERC1155Holder.onERC1155Received.selector) {
        revert UnsafeRecipient();
    }
}
  • Estimated gas saved = This optimization can save a small amount of gas by reducing the number of operations. The exact savings depend on the EVM implementation but could be in the range of tens to hundreds of gas units.

Possible Optimization 2 =

Here is the optimized code:

function balanceOfBatch(
    address[] calldata owners,
    uint256[] calldata ids
) public view returns (uint256[] memory balances) {
    uint256 length = owners.length; // Cache the length of the owners array
    balances = new uint256[](length);
    unchecked {
        for (uint256 i = 0; i < length; ++i) { // Use cached length for loop condition
            address owner = owners[i]; // Cache the owner address
            uint256 id = ids[i]; // Cache the token id
            balances[i] = balanceOf[owner][id]; // Use cached values for balance lookup
        }
    }
}
  • Estimated gas saved = This optimization can save gas by reducing the number of SLOAD operations. The exact savings depend on the length of the owners array but could be significant for larger arrays.

Possible Optimization in LeftRight.sol

Possible Optimization =

  • Optimize add() function for uint256.

After Optimization:

function add(uint256 x, uint256 y) internal pure returns (uint256 z) {
    z = x + y;
    if (z < x) revert Errors.UnderOverFlow(); // Simplified overflow check

    // Removed the check for uint128 overflow as it's redundant
    // Solidity automatically checks for overflows in arithmetic operations
}
  • Estimated gas saved = This optimization can save gas by eliminating redundant checks and utilizing Solidity's built-in overflow protection. The exact savings could be in the range of tens to hundreds of gas units.

Possible Optimization in LiquidityChunk.sol

Possible Optimization =

  • Optimize the createChunk() function by directly constructing the chunk instead of chaining multiple function calls.

Here is the optimized code snippet:

function createChunk(
    uint256 self,
    int24 _tickLower,
    int24 _tickUpper,
    uint128 amount
) internal pure returns (uint256) {
    unchecked {
        // Directly construct the chunk with all components
        return uint256(amount) + (uint256(uint24(_tickLower)) << 232) + (uint256(uint24(_tickUpper)) << 208);
    }
}
  • Estimated gas saved = This optimization can save gas by reducing the number of function calls and arithmetic operations. The exact savings could be in the range of tens to hundreds of gas units.

Possible Optimization in TokenId.sol

Possible Optimization =

  • Optimize the validate() function by reducing the number of bitwise operations and condition checks.

After Optimization:

function validate(uint256 self) internal pure returns (uint64) {
    uint256 optionRatios = self & OPTION_RATIO_MASK;
    if (optionRatios == 0) revert Errors.InvalidTokenIdParameter(1);

    unchecked {
        for (uint256 i = 0; i < 4; ++i) {
            uint256 currentOptionRatio = self.optionRatio(i);
            if (currentOptionRatio == 0) {
                // Simplify the check for remaining bits
                if (self.clearLeg(i) != 0) revert Errors.InvalidTokenIdParameter(1);
                break; // we are done iterating over potential legs
            }
            // ... rest of the code
        }
    }
    return self.univ3pool();
}
  • Estimated gas saved = This optimization can save gas by reducing the number of bitwise operations and simplifying condition checks. The exact savings could be in the range of tens to hundreds of gas units.

Possible Optimization in Math.sol

Possible Optimization =

  • Optimize the getSqrtRatioAtTick() function by reducing the number of bitwise operations and simplifying the logic.

Here is the optimized code snippet:

function getSqrtRatioAtTick(int24 tick) internal pure returns (uint160 sqrtPriceX96) {
    unchecked {
        uint256 absTick = uint256(tick < 0 ? -tick : tick); // Simplified absolute value calculation
        if (absTick > uint256(Constants.MAX_V3POOL_TICK)) revert Errors.InvalidTick();
        // ... rest of the function with potential optimizations in bitwise operations
    }
}
  • Estimated gas saved = This optimization can save a small amount of gas by simplifying the arithmetic operation.

#0 - c4-judge

2023-12-14T17:12:27Z

Picodes marked the issue as grade-a

#1 - dyedm1

2023-12-15T18:35:31Z

Estimated gas saved = This optimization can save gas by reducing the number of SLOAD operations. The exact savings depend on the length of the ids array but could be significant for larger arrays. This change is safe as it does not alter the logic, only the computation method.

This is not a storage array.

#2 - c4-sponsor

2023-12-15T18:35:36Z

dyedm1 (sponsor) confirmed

#3 - c4-judge

2023-12-26T23:28:00Z

Picodes marked the issue as grade-b

Findings Information

Labels

analysis-advanced
grade-b
A-05

Awards

54.8805 USDC - $54.88

External Links

Advanced Analysis Report for Panoptic by K42

Overview

  • Panoptic is a sophisticated protocol designed to manage complex, multi-leg Uniswap positions encoded in ERC1155 token IDs. It offers a more advanced and efficient alternative to Uniswap's NonFungiblePositionManager for liquidity providers.

Understanding the Ecosystem:

  • The ecosystem revolves around Uniswap V3 positions, leveraging ERC1155 for enhanced flexibility and efficiency. It caters to both typical liquidity provision and advanced strategies involving long positions and liquidity burning.

Codebase Quality Analysis:

  • Focusing on key contracts:

SemiFungiblePositionManager Contract

Overview

The SemiFungiblePositionManager contract is a core component of the Panoptic protocol, extending the ERC1155 standard and integrating with Uniswap V3. It manages complex, multi-leg liquidity positions and offers functionalities like minting, burning, and transferring tokenized positions.

Key Functionalities and Technical Insights

  1. Pool Initialization and Management

    • The contract initializes Uniswap V3 pools and maintains a mapping of pool addresses to pool IDs.
    • It employs a locking mechanism (PoolAddressAndLock) to prevent reentrancy, a critical security feature for DeFi protocols.
  2. Tokenized Position Management

    • Functions like mintTokenizedPosition and burnTokenizedPosition handle the creation and destruction of tokenized positions, respectively.
    • These functions interact with Uniswap V3 pools to manage liquidity, collect fees, and handle position adjustments.
  3. Callback Handling

    • Implements Uniswap V3 callbacks (uniswapV3MintCallback, uniswapV3SwapCallback) to facilitate liquidity operations.
    • These callbacks are critical for ensuring that liquidity additions and removals are executed correctly in the Uniswap ecosystem.
  4. Token Transfer Logic

    • Overrides ERC1155's afterTokenTransfer to handle the transfer of tokenized positions.
    • Includes logic to update liquidity and fee information upon transfer, ensuring consistency in position data.
  5. Position Validation and Forwarding

    • The _validateAndForwardToAMM function validates position sizes and tick limits, then forwards the request to the AMM for execution.
    • This function is crucial for maintaining the integrity of tokenized positions and ensuring that they reflect the actual state on Uniswap V3.
  6. Swap Execution

    • The swapInAMM function executes swaps in Uniswap V3 pools, a necessary operation for adjusting positions in response to market movements.
    • This function demonstrates the contract's deep integration with Uniswap V3, allowing users to leverage its AMM features.
  7. Liquidity Chunk Handling

    • Utilizes the LiquidityChunk library to manage liquidity in discrete chunks, facilitating complex position management.
    • This approach allows for more granular control over liquidity positions, essential for advanced trading strategies.
Potential Risks and Recommendations
  1. Complex Interactions with Uniswap V3: The contract's heavy reliance on Uniswap V3 mechanics requires thorough testing, especially for edge cases in liquidity management and fee calculation.

  2. Reentrancy Protection: While the contract employs a locking mechanism, it's crucial to ensure that all external calls are safeguarded against reentrancy attacks.

  3. Token Transfer Consistency: The logic in afterTokenTransfer must be robust to maintain consistent state across transfers, especially in multi-leg positions.

  4. Callback Function Security: The callback functions must be tightly secured to prevent unauthorized calls, which could lead to manipulation or loss of funds.

  5. Position Validation: Rigorous validation of tokenized positions is necessary to prevent incorrect or unauthorized position creation and destruction.

ERC1155Minimal Contract

Overview

The ERC1155 contract is an abstract implementation of the ERC1155 standard, a multi-token standard allowing for the efficient transfer of multiple token types. It is a key component in the Panoptic protocol, enabling the management of diverse tokenized positions.

Key Functionalities and Technical Insights

  1. Event Emission

    • The contract emits TransferSingle and TransferBatch events for single and batch token transfers, respectively, providing transparency and traceability in token movements.
    • The ApprovalForAll event is emitted when an operator is approved to manage all tokens of a user, crucial for delegated token management.
  2. Error Handling

    • Implements custom errors like NotAuthorized and UnsafeRecipient, enhancing the clarity and efficiency of error reporting.
  3. Token Balance Management

    • Maintains a mapping (balanceOf) of token balances for each user, indexed by user and token ID, ensuring accurate tracking of token ownership.
    • The balance update logic in safeTransferFrom and safeBatchTransferFrom is designed to prevent overflows, ensuring the integrity of token balances.
  4. Approval Mechanism

    • The isApprovedForAll mapping and setApprovalForAll function manage operator approvals, allowing for flexible and secure management of token permissions.
  5. Safe Transfer Functions

    • Implements safeTransferFrom and safeBatchTransferFrom for single and batch transfers, including checks for authorization and recipient safety.
    • These functions are critical for ensuring secure and compliant transfers within the ERC1155 framework.
  6. Interface Support

    • The supportsInterface function indicates support for specific interfaces (like ERC1155 and ERC165), a key feature for interface detection and compatibility.
  7. Minting and Burning

    • Provides internal _mint and _burn functions for token creation and destruction, essential for managing the token supply.
  8. Token Transfer Hooks

    • Abstract functions afterTokenTransfer (for both single and batch transfers) allow for custom logic to be executed after token transfers, offering extensibility in token transfer handling.

Potential Risks and Recommendations

  1. Complex Transfer Logic: The contract's transfer logic, especially in batch operations, is complex and requires thorough testing to prevent potential vulnerabilities or logic flaws.

  2. Interface Compliance: Ensuring full compliance with the ERC1155 standard is crucial for interoperability with other contracts and platforms in the Ethereum ecosystem.

  3. Recipient Contract Interaction: The contract interacts with recipient contracts (via ERC1155Holder) during transfers. It's essential to ensure that these interactions are secure and do not introduce vulnerabilities.

  4. Token Minting and Burning: The internal _mint and _burn functions must be used cautiously in derived contracts to prevent unauthorized manipulation of token supply.

TokenId Library

Overview

The TokenId library in the Panoptic protocol is a specialized utility for encoding and decoding token IDs in the context of Uniswap V3 positions. It plays a crucial role in managing complex tokenized positions and ensuring their correct representation and manipulation within the system.

Key Functionalities and Technical Insights

  1. Token ID Encoding and Decoding

    • Provides functions for encoding various parameters into a token ID and decoding them back. This includes univ3pool, asset, optionRatio, isLong, tokenType, riskPartner, strike, width, and more.
    • These functions are essential for the precise representation of complex tokenized positions in Uniswap V3.
  2. Bitwise Operations

    • Extensively uses bitwise operations for efficient data packing and unpacking within a token ID. This approach optimizes storage and gas usage.
  3. Validation and Error Handling

    • Implements a validate function to ensure the integrity and correctness of a token ID. It checks for valid parameters and relationships between different components of a token ID.
  4. Utility Functions

    • Includes utility functions like countLegs, clearLeg, flipToBurnToken, and others, providing essential tools for managing token IDs in various scenarios.
  5. Constants and Masks

    • Defines several constants and bit masks (LONG_MASK, CLEAR_POOLID_MASK, OPTION_RATIO_MASK, etc.) to facilitate the encoding and decoding process.

Potential Risks and Recommendations

  1. Complex Bitwise Logic: The extensive use of bitwise operations, while efficient, introduces complexity. It requires careful handling to avoid errors in encoding and decoding.

  2. Data Integrity: Ensuring the integrity of encoded data is paramount. Rigorous testing and validation are needed to prevent any inconsistencies or inaccuracies in token ID representation.

  3. Error Handling: The library should robustly handle any erroneous inputs or invalid states to prevent any adverse effects on the broader system.

  4. Upgradability and Extensibility: As the protocol evolves, the library should be designed to accommodate future changes or extensions in the token ID structure.

Centralization Risks:

  • Contract Ownership and Upgrade Control: Centralization risks may arise if a small number of entities have control over critical contract functions, such as upgrades or parameter adjustments. This can lead to trust issues and potential manipulation.
  • Dependency on External Protocols: The Panoptic protocol's reliance on Uniswap V3 introduces a dependency risk. Changes in Uniswap's contracts or policies could directly impact Panoptic's operations.

Mechanism Review:

  • Token Encoding/Decoding: The intricate mechanism for encoding and decoding token IDs requires careful scrutiny to ensure accuracy, especially for tokens representing complex positions.
  • Liquidity Management: The process of managing liquidity in multi-leg positions is complex and necessitates precision and thorough understanding.

Recommendations:

  • Decentralization of Control: Implement governance mechanisms or multi-signature controls to decentralize critical decision-making processes and reduce centralization risks.
  • Monitoring and Adaptation: Continuously monitor dependencies like Uniswap V3 and adapt the protocol to changes in external systems to mitigate risks. Use Tenderly and Defender for continued monitoring to prevent un-foreseen risks or actions.
  • Enhanced Security Measures: Employ advanced security measures, including regular audits and bug bounty programs, to strengthen the protocol's resilience against potential vulnerabilities.
  • User Education: Given the complexity of the protocol, provide comprehensive documentation and educational resources to help users understand and safely interact with the system.

Contract Details:

Conclusion:

  • Panoptic presents an advanced and efficient solution for managing Uniswap V3 positions, though its complexity necessitates thorough understanding and more rigorous future testing to ensure long term security and reliability.

Time spent:

20 hours

#0 - c4-judge

2023-12-14T17:17:12Z

Picodes marked the issue as grade-b

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter