Panoptic - 0xhex'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: 41/72

Findings: 1

Award: $19.82

🌟 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
G-02

External Links

Gas Optimization

Summary

NumberGas OptimizationContext
[G-01]Can Make The Variable Outside The Loop To Save Gas8
[G-02]Use Assembly To Check For address(0)1
[G-03]Use uint256(1) and uint256(2) for true/false to avoid a Gwarmaccess (100 gas) for the extra SLOAD3
[G-04]State variables that are used multiple times in a function should be cached in stack variables9
[G-05]The result of function calls should be cached rather than re-calling the function2
[G-06]Use assembly in place of abi.decode to extract calldata values more efficiently2
[G-07]Amounts should be checked for 0 before calling a transfer1
[G-08]Use hardcode address instead address(this)2
[G-09]Multiple Address/id Mappings Can Be Combined Into A Single Mapping Of An Address/id To A Struct2

[G-01] Can Make The Variable Outside The Loop To Save Gas

When you declare a variable inside a loop, Solidity creates a new instance of the variable for each iteration of the loop. This can lead to unnecessary gas costs, especially if the loop is executed frequently or iterates over a large number of elements.

By declaring the variable outside the loop, you can avoid the creation of multiple instances of the variable and reduce the gas cost of your contract. Here's an example:

File:  SemiFungiblePositionManager.sol
586  uint256 liquidityChunk = PanopticMath.getLiquidityChunk(

594  bytes32 positionKey_from = keccak256(

603  bytes32 positionKey_to = keccak256(

620  uint256 fromLiq = s_accountLiquidity[positionKey_from];

623  int256 fromBase = s_accountFeesBase[positionKey_from];

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

File:  SemiFungiblePositionManager.sol
            int256 _moved;
            int256 _itmAmounts;
            int256 _totalCollected;

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

File:  multicall/Multicall.sol
15  (bool success, bytes memory result) = address(this).delegatecall(data[i]);

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

[G-02] Use Assembly To Check For address(0)

File:  SemiFungiblePositionManager.sol

356  if (address(univ3pool) == address(0)) revert Errors.UniswapPoolNotInitialized();

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

[G-03] Use uint256(1) and uint256(2) for true/false to avoid a Gwarmaccess (100 gas) for the extra SLOAD, and to avoid Gsset (20000 gas) when changing from 'false' to 'true', after having been 'true' in the past

File:  SemiFungiblePositionManager.sol

226   s_poolContext[poolId].locked = true;

333   s_poolContext[poolId].locked = false;

688    swapAtMint = true;

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

[G‑04] State variables that are used multiple times in a function should be cached in stack variables

File:  SemiFungiblePositionManager.sol

615   (s_accountLiquidity[positionKey_to] != 0) ||

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

File:  SemiFungiblePositionManager.sol

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

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

File:  SemiFungiblePositionManager.sol

        bytes32 positionKey = keccak256(
            abi.encodePacked(
                address(_univ3pool),
                msg.sender,
                _tokenType,
                _liquidityChunk.tickLower(),
                _liquidityChunk.tickUpper()
            )
        );

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

[G‑05] The result of function calls should be cached rather than re-calling the function

File: SemiFungiblePositionManager.sol

599           liquidityChunk.tickLower(),
600           liquidityChunk.tickUpper()

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

[G-06] Use assembly in place of abi.decode to extract calldata values more efficiently

Instead of using abi.decode, we can use assembly to decode our desired calldata values directly. This will allow us to avoid decoding calldata values that we will not use.

File:  SemiFungiblePositionManager.sol

413   CallbackLib.CallbackData memory decoded = abi.decode(data, (CallbackLib.CallbackData));

447   CallbackLib.CallbackData memory decoded = abi.decode(data, (CallbackLib.CallbackData));

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

[G-07] Amounts should be checked for 0 before calling a transfer

Checking non-zero transfer values can avoid an expensive external call and save gas.

There are 1 instances of this issue:

File:  SemiFungiblePositionManager.sol

460   SafeTransferLib.safeTransferFrom(token, decoded.payer, msg.sender, amountToPay);

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

becuase if uint256 amountToPay = amount0Delta > 0 ? uint256(amount0Delta) : uint256(amount1Delta); this condition is true work will but if condition is false there is need of > 0 check.

[G-08] Use hardcode address instead address(this)

it can be more gas-efficient to use a hardcoded address instead of the address(this) expression, especially if you need to use the same address multiple times in your contract.

The reason for this is that using address(this) requires an additional EXTCODESIZE operation to retrieve the contract's address from its bytecode, which can increase the gas cost of your contract. By pre-calculating and using a hardcoded address, you can avoid this additional operation and reduce the overall gas cost of your contract.

Here's an example of how you can use a hardcoded address instead of address(this):

contract MyContract { address public immutable myAddress = 0x1234567890123456789012345678901234567890; #L function doSomething() public { // Use myAddress instead of address(this) require(msg.sender == myAddress, "Caller is not authorized"); // Do something } }

In the above example, we have a contract MyContract with a public address variable myAddress. Instead of using address(this) to retrieve the contract's address, we have pre-calculated and hardcoded the address in the variable. This can help to reduce the gas cost of our contract and make our code more efficient.

References

There are 2 instances of this issue:

File:  SemiFungiblePositionManager.sol

1106            address(this),

1148            address(this),

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

[G-09] Multiple Address/id Mappings Can Be Combined Into A Single Mapping Of An Address/id To A Struct, Where Appropriate

Saves a storage slot for the mapping. Depending on the circumstances and sizes of types, can avoid a Gsset (20000 gas) per mapping combined. Reads and subsequent writes can also be cheaper when a function requires both values and they both fit in the same storage slot. Finally, if both fields are accessed in the same function, can save ~42 gas per access due to not having to recalculate the key’s keccak256 hash (Gkeccak256 - 30 gas) and that calculation’s associated stack operations.

Note: this insteance is missed form bots

File:  tokens/ERC1155Minimal.sol

    mapping(address account => mapping(uint256 tokenId => uint256 balance)) public balanceOf;

    /// @notice Approved addresses for each user
    /// @dev indexed by user, then by operator
    /// @dev operator is approved to transfer all tokens on behalf of user
    mapping(address owner => mapping(address operator => bool approvedForAll))
        public isApprovedForAll;

https://github.com/code-423n4/2023-11-panoptic/blob/main/contracts/tokens/ERC1155Minimal.sol#L62-L68

#0 - c4-judge

2023-12-14T17:05:29Z

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