Panoptic - 0x6980'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: 50/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-16

External Links

Report

Gas Optimizations

IssueInstancesGas Saved
G-01++i/i++ should be unchecked{++i}/unchecked{i++} when it is not possible for them to overflow, as is the case when used in for- and while-loops2120
G-02Use assembly to check for address(0)212
G-03Optimize External Calls with Assembly for Memory Efficiency112,420
G-04Simple checks for zero can be done using assembly to save gas1378
G-05Use assembly to revert with an error message61,800
G-06Shorten the array rather than copying to a new one2-
G-07Use assembly to calculate hashes to save gas1120
G-08Use assembly to emit events8304
G-09Use assembly to write address storage values1-
G-10Avoid contract existence checks by using low level calls4400
G-11The result of function calls should be cached rather than re-calling the function484
G-12State variables can be modified to fit in fewer storage slots240,000
G-13It costs more gas to initialize non-constant/non-immutable state variables to zero than to let the default of zero be applied7-
G-14unchecked {} can be used on the division of two uints in order to save gas4-
G-15Optimize Deployment Size by Fine-tuning IPFS Hash1220
G-16Trade-offs Between Modifiers and Internal Functions3-
G-17Optimize Gas by Using Only Named Returns462,024
G-18Unused named return variables without optimizer waste gas518
G-19Pre-increments and pre-decrements are cheaper than post-increments and post-decrements1-
G-20State variables which are not modified within functions should be set as constant or immutable for values set at deployment5100,000
G-21private functions used once can be inlined120
G-22Use upcoming OpenZeppelin contracts1-

Total: 528 instances over 51 issues with an estimate of 343,433 gas saved.

Gas Optimizations

<a name="G-01"></a>[G-01] ++i/i++ should be unchecked{++i}/unchecked{i++} when it is not possible for them to overflow, as is the case when used in for- and while-loops

The unchecked keyword is new in solidity version 0.8.0, so this only applies to that version or higher, which these instances are. This saves 30-40 gas per loop

Instances (2):

File: contracts/tokens/ERC1155Minimal.sol

187:             for (uint256 i = 0; i < owners.length; ++i) {

187

File: contracts/types/TokenId.sol

468:             for (uint256 i = 0; i < 4; ++i) {

468

<a name="G-02"></a>[G-02] Use assembly to check for address(0)

Saves 6 gas per instance

Instances (2):

File: contracts/SemiFungiblePositionManager.sol

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

370:         while (address(s_poolContext[poolId].pool) != address(0)) {

356, 370

<a name="G-03"></a>[G-03] Optimize External Calls with Assembly for Memory Efficiency

Using interfaces to make external contract calls in Solidity is convenient but can be inefficient in terms of memory utilization. Each such call involves creating a new memory location to store the data being passed, thus incurring memory expansion costs. Inline assembly allows for optimized memory usage by re-using already allocated memory spaces or using the scratch space for smaller datasets. This can result in notable gas savings, especially for contracts that make frequent external calls. Additionally, using inline assembly enables important safety checks like verifying if the target address has code deployed to it using extcodesize(addr) before making the call, mitigating risks associated with contract interactions.

Instances (11):

<details> <summary>see instances</summary>
File: contracts/SemiFungiblePositionManager.sol

354: 
355:             // reverts if the Uni v3 pool has not been initialized

773:             // this is simply a convenience feature, and should be treated as such

774:             if ((itm0 != 0) && (itm1 != 0)) {

840:     /// @dev Loops over each leg in the tokenId and calls _createLegInAMM for each, which does the mint/burn in the AMM.
841:         /// @param univ3pool the Uniswap pool.
842:         /// @param tokenId the option position
843:         /// @param positionSize the size of the option position
844:         /// @param isBurn is true if the position is burnt

1158:         // from msg.sender to the uniswap pool, stored as negative value to represent amount debited

1158:         // from msg.sender to the uniswap pool, stored as negative value to represent amount debited

1165:     /// @dev note that "moved" means: burn position in Uniswap and send tokens to msg.sender.
1166:          /// @param liquidityChunk the chunk of liquidity to burn given by tick upper, tick lower, and its size
1167:          /// @param univ3pool the Uniswap v3 pool to burn liquidity in/from

1242:             // CollectedOut is the amount of fees accumulated+collected (received - burnt)
1243:                  // That's because receivedAmount contains the burnt tokens and whatever amount of fees collected
1244:                  collectedOut = int256(0).toRightSlot(collected0).toLeftSlot(collected1);
1245:      
1246:                  _updateStoredPremia(positionKey, currentLiquidity, collectedOut);

354-355, 773, 774, 840-844, 1158, 1158, 1165-1167, 1242-1246

File: contracts/tokens/ERC1155Minimal.sol

112:                 ERC1155Holder(to).onERC1155Received(msg.sender, from, id, amount, data) !=

165:                 ERC1155Holder(to).onERC1155BatchReceived(msg.sender, from, ids, amounts, data) !=

224:                 ERC1155Holder(to).onERC1155Received(msg.sender, address(0), id, amount, "") !=

112, 165, 224

</details>

<a name="G-04"></a>[G-04] Simple checks for zero can be done using assembly to save gas

Instances (13):

<details> <summary>see instances</summary>
File: contracts/SemiFungiblePositionManager.sol

682:         // Revert if the pool not been previously initialized

835:             totalSwapped = int256(0).toRightSlot(swap0.toInt128()).toLeftSlot(swap1.toInt128());

979:                     removedLiquidity -= chunkLiquidity;

1069:     /// @notice caches/stores the accumulated premia values for the specified postion.

682, 835, 979, 1069

File: contracts/libraries/Math.sol

206:             if (prod1 == 0) {

302:             if (prod1 == 0) {

364:             if (prod1 == 0) {

426:             if (prod1 == 0) {

488:             if (prod1 == 0) {

206, 302, 364, 426, 488

File: contracts/libraries/PanopticMath.sol

121:             legLiquidity = Math.getLiquidityForAmount0(

121

File: contracts/types/TokenId.sol

443:         if (i == 0)

464:         if (self.optionRatio(0) == 0) revert Errors.InvalidTokenIdParameter(1);

469:                 if (self.optionRatio(i) == 0) {

443, 464, 469

</details>

<a name="G-05"></a>[G-05] Use assembly to revert with an error message

When reverting in solidity code, it is common practice to use a require or revert statement to revert execution with an error message. This can in most cases be further optimized by using assembly to revert with the error message. Instead of


    require(owner == msg.sender, "caller is not owner");

use the below assembly code


    assembly {
        if sub(caller(), sload(owner.slot)) {
            mstore(0x00, 0x20) // store offset to where length of revert message is stored
            mstore(0x20, 0x13) // store length (19)
            mstore(0x40,0x63616c6c6572206973206e6f74206f776e657200000000000000000000000000) // store hex representation of message
            revert(0x00, 0x60) // revert with data
         }
    }


*Instances (6)*:

```solidity
File: contracts/libraries/Math.sol

207:                 require(denominator > 0);

216:             require(denominator > prod1);

311:             require(2 ** 64 > prod1);
312:     

373:             require(2 ** 96 > prod1);
374:     
375:                 ///////////////////////////////////////////////

435:             require(2 ** 128 > prod1);
436:     
437:                 ///////////////////////////////////////////////

497:             require(2 ** 192 > prod1);
498:     
499:                 ///////////////////////////////////////////////

207, 216, 311-312, 373-375, 435-437, 497-499

<a name="G-06"></a>[G-06] Shorten the array rather than copying to a new one

Inline-assembly can be used to shorten the array by changing the length slot, so that the entries don't have to be copied to a new, shorter array.

Instances (2):

File: contracts/multicall/Multicall.sol

13:         results = new bytes[](data.length);

13

File: contracts/tokens/ERC1155Minimal.sol

182:         balances = new uint256[](owners.length);

182

<a name="G-07"></a>[G-07] Use assembly to calculate hashes to save gas

Use assembly to calculate hashes to save gas

Instances (1):

File: contracts/libraries/CallbackLib.sol

43:                                 keccak256(abi.encode(features)),

43

<a name="G-08"></a>[G-08] Use assembly to emit events

We can use assembly to emit events efficiently by utilizing scratch space and the free memory pointer. This will allow us to potentially avoid memory expansion costs. Note: In order to do this optimization safely, we will need to cache and restore the free memory pointer.

For example, for a generic emit event for eventSentAmountExample:


// uint256 id, uint256 value, uint256 amount
emit eventSentAmountExample(id, value, amount);


        assembly {
            let memptr := mload(0x40)
            mstore(0x00, calldataload(0x44))
            mstore(0x20, calldataload(0xa4))
            mstore(0x40, amount)            log1(                0x00,                0x60,                // keccak256("eventSentAmountExample(uint256,uint256,uint256)")                0xa622cf392588fbf2cd020ff96b2f4ebd9c76d7a4bc7f3e6b2f18012312e76bc3            )
            mstore(0x40, memptr)        }

Instances (8):

<details> <summary>see instances</summary>
File: contracts/SemiFungiblePositionManager.sol

388:         return;

490:         emit TokenizedPositionBurnt(msg.sender, tokenId, positionSize);

523:         emit TokenizedPositionMinted(msg.sender, tokenId, positionSize);

388, 490, 523

File: contracts/tokens/ERC1155Minimal.sol

80:         emit ApprovalForAll(msg.sender, operator, approved);

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

161:         emit TransferBatch(msg.sender, from, to, ids, amounts);

220:         emit TransferSingle(msg.sender, address(0), to, id, amount);

239:         emit TransferSingle(msg.sender, from, address(0), id, amount);

80, 108, 161, 220, 239

</details>

<a name="G-09"></a>[G-09] Use assembly to write address storage values

Instances (1):

File: contracts/SemiFungiblePositionManager.sol

342:     constructor(IUniswapV3Factory _factory) {
343:             FACTORY = _factory;
344:         }
345:     
346:         /// @notice Initialize a Uniswap v3 pool in the SemifungiblePositionManager contract

342-346

<a name="G-10"></a>[G-10] Avoid contract existence checks by using low level calls

Prior to 0.8.10 the compiler inserted extra code, including EXTCODESIZE (100 gas), to check for contract existence for external function calls. In more recent solidity versions, the compiler will not insert these checks if the external call has a return value. Similar behavior can be achieved in earlier versions by using low-level calls, since low level calls never check for contract existence

Instances (4):

File: contracts/libraries/FeesCalc.sol

/// @audit ticks()
105:         unchecked {

/// @audit ticks()
121: 

/// @audit feeGrowthGlobal0X128()
169: 

/// @audit feeGrowthGlobal1X128()
169: 

105, 121, 169, 169

<a name="G-11"></a>[G-11] The result of function calls should be cached rather than re-calling the function

The instances below point to the second+ call of the function within a single function.

Instances (4):

File: contracts/libraries/Math.sol

/// @audit used on line 104
105:         uint160 highPriceX96 = getSqrtRatioAtTick(liquidityChunk.tickUpper());

/// @audit used on line 122
123:         uint160 highPriceX96 = getSqrtRatioAtTick(liquidityChunk.tickUpper());

/// @audit used on line 139
140:         uint160 highPriceX96 = getSqrtRatioAtTick(liquidityChunk.tickUpper());

/// @audit used on line 158
159:         uint160 highPriceX96 = getSqrtRatioAtTick(liquidityChunk.tickUpper());

105, 123, 140, 159

<a name="G-12"></a>[G-12] State variables can be modified to fit in fewer storage slots

Some state variables can be safely modified, and as result, the contract will use fewer storage slots. Each slot saved can avoid an extra Gsset (20000 gas) for the first setting of the struct. Subsequent reads as well as writes have smaller gas savings.

Instances (2):

File: contracts/SemiFungiblePositionManager.sol

/// @audit Variable ordering with 2 slots instead of the current 3
///    IUniswapV3Factory(20) FACTORY
///    bool(1) BURN
///    bool(1) MINT
///    uint128(16) VEGOID

2: pragma solidity =0.8.18;

2

File: contracts/libraries/Constants.sol

/// @audit Variable ordering with 4 slots instead of the current 5
///    uint256(32) FP96
///    bytes32(32) V3POOL_INIT_CODE_HASH
///    uint160(20) MAX_V3POOL_SQRT_RATIO
///    int24(3) MAX_V3POOL_TICK
///    int24(3) MIN_V3POOL_TICK
///    uint160(20) MIN_V3POOL_SQRT_RATIO

2: pragma solidity ^0.8.0;

2

<a name="G-13"></a>[G-13] It costs more gas to initialize non-constant/non-immutable state variables to zero than to let the default of zero be applied

Not overwriting the default for storage variables avoids a Gsreset (2900 gas) during deployment

Instances (7):

File: contracts/SemiFungiblePositionManager.sol

551:             registerTokenTransfer(from, to, ids[i], amounts[i]);

584:             // for this leg index: extract the liquidity chunk: a 256bit word containing the liquidity amount and upper/lower tick

874:                     // Reverse the order of the legs if this call is burning a position (LIFO)

551, 584, 874

File: contracts/multicall/Multicall.sol

14:         for (uint256 i = 0; i < data.length; ) {

14

File: contracts/tokens/ERC1155Minimal.sol

141:         for (uint256 i = 0; i < ids.length; ) {

187:             for (uint256 i = 0; i < owners.length; ++i) {

141, 187

File: contracts/types/TokenId.sol

468:             for (uint256 i = 0; i < 4; ++i) {

468

<a name="G-14"></a>[G-14] unchecked {} can be used on the division of two uints in order to save gas

Make such found divisions are unchecked when ensured it is safe to do so.

Instances (4):

File: contracts/SemiFungiblePositionManager.sol

1315:                         removedLiquidity +
1316:                              ((removedLiquidity ** 2) / 2 ** (VEGOID));

1335:     /// @notice Return the liquidity associated with a given position.
1336:          /// @dev Computes accountLiquidity[keccak256(abi.encodePacked(univ3pool, owner, tokenType, tickLower, tickUpper))]

1315-1316, 1335-1336

File: contracts/libraries/Math.sol

86:             if (tick > 0) sqrtR = type(uint256).max / sqrtR;

108:                 mulDiv(
109:                         uint256(liquidityChunk.liquidity()) << 96,
110:                         highPriceX96 - lowPriceX96,
111:                         highPriceX96
112:                     ) / lowPriceX96;

86, 108-112

<a name="G-15"></a>[G-15] Optimize Deployment Size by Fine-tuning IPFS Hash

The Solidity compiler appends 53 bytes of metadata to the smart contract code which translates to an extra 10,600 gas (200 per bytecode) + the calldata cost (16 gas per non-zero bytes, 4 gas per zero-byte). This translates to up to 848 additional gas in calldata cost. One way to reduce this cost is by optimizing the IPFS hash that gets appended to the smart contract code.

Why is this important?- The metadata adds an extra 53 bytes, resulting in an additional 10,600 gas cost for deployment.- It also incurs up to 848 additional gas in calldata cost.Options to Reduce Gas:- Use the --no-cbor-metadata compiler option to exclude metadata, but this might affect contract verification.- Mine for code comments that lead to an IPFS hash with more zeros, reducing calldata costs.

Instances (1):

File: Various Files

1: 

1

<a name="G-16"></a>[G-16] Trade-offs Between Modifiers and Internal Functions

In Solidity, both internal functions and modifiers are used to refactor and manage code, but they come with their own trade-offs, especially in terms of gas cost and flexibility.

Modifiers:

  • Less runtime gas cost (saves around 24 gas per function call).- Increases deployment gas cost due to repetitive code.- Can only be executed at the start or end of a function.Internal Functions:
  • Lower deployment cost.- Can be executed at any point in a function.- Slightly higher runtime gas cost (24 gas) due to the need to jump to the function's location in bytecode.Recommendations:
  • Use modifiers for high-frequency functions where runtime gas cost matters the most.- Use internal functions where the priority is reducing deployment gas cost or when you need more flexibility in the function's logic.Example analysis shows that using modifiers can increase deployment costs by over 35k gas but save 24 gas per function call during runtime. Choose wisely based on your specific use case.

Instances (3):

File: contracts/SemiFungiblePositionManager.sol

306:     modifier ReentrancyLock(uint64 poolId) {
307:             // check if the pool is already locked

477:         uint256 tokenId,
478:             uint128 positionSize,
479:             int24 slippageTickLimitLow,
480:             int24 slippageTickLimitHigh
481:         )
482:             external
483:             ReentrancyLock(tokenId.univ3pool())
484:             returns (int256 totalCollected, int256 totalSwapped, int24 newTick)
485:         {
486:             // burn this ERC1155 token id

511:         uint256 tokenId,
512:             uint128 positionSize,
513:             int24 slippageTickLimitLow,
514:             int24 slippageTickLimitHigh
515:         )
516:             external
517:             ReentrancyLock(tokenId.univ3pool())
518:             returns (int256 totalCollected, int256 totalSwapped, int24 newTick)
519:         {
520:             // create the option position via its ID in this erc1155

306-307, 477-486, 511-520

<a name="G-17"></a>[G-17] Optimize Gas by Using Only Named Returns

The Solidity compiler can generate more efficient bytecode when using named returns. It's recommended to replace anonymous returns with named returns for potential gas savings.Example:


/// 985 gas cost
function add(uint256 x, uint256 y) public pure returns (uint256) {
  return x + y;
}
/// 941 gas cost
function addNamed(uint256 x, uint256 y) public pure returns (uint256 res) {
  res = x + y;
}

Instances (46):

<details> <summary>see instances</summary>
File: contracts/libraries/Math.sol

23:     function absUint(int256 x) internal pure returns (uint256) {

23

File: contracts/libraries/PanopticMath.sol

38:     function getPoolId(address univ3pool) internal pure returns (uint64) {

48:     function getFinalPoolId(
49:            uint64 basePoolId,
50:            address token0,
51:            address token1,
52:            uint24 fee
53:        ) internal pure returns (uint64) {

145:     function convert0to1(int256 amount, uint160 sqrtPriceX96) internal pure returns (int256) {
146:             unchecked {
147:                 // the tick 443636 is the maximum price where (price) * 2**192 fits into a uint256 (< 2**256-1)

168:     function convert1to0(int256 amount, uint160 sqrtPriceX96) internal pure returns (int256) {
169:             unchecked {
170:                 // the tick 443636 is the maximum price where (price) * 2**192 fits into a uint256 (< 2**256-1)

38, 48-53, 145-147, 168-170

File: contracts/tokens/ERC1155Minimal.sol

200:     function supportsInterface(bytes4 interfaceId) public pure returns (bool) {

200

File: contracts/types/LeftRight.sol

25:     function rightSlot(uint256 self) internal pure returns (uint128) {

32:     function rightSlot(int256 self) internal pure returns (int128) {

44:     function toRightSlot(uint256 self, uint128 right) internal pure returns (uint256) {

54:     function toRightSlot(uint256 self, int128 right) internal pure returns (uint256) {

65:     function toRightSlot(int256 self, uint128 right) internal pure returns (int256) {

75:     function toRightSlot(int256 self, int128 right) internal pure returns (int256) {

89:     function leftSlot(uint256 self) internal pure returns (uint128) {

96:     function leftSlot(int256 self) internal pure returns (int128) {

108:     function toLeftSlot(uint256 self, uint128 left) internal pure returns (uint256) {

118:     function toLeftSlot(int256 self, uint128 left) internal pure returns (int256) {

128:     function toLeftSlot(int256 self, int128 left) internal pure returns (int256) {

212:     function toInt256(uint256 self) internal pure returns (int256) {

25, 32, 44, 54, 65, 75, 89, 96, 108, 118, 128, 212

File: contracts/types/LiquidityChunk.sol

68:     ) internal pure returns (uint256) {
69:            unchecked {
70:                return self.addLiquidity(amount).addTickLower(_tickLower).addTickUpper(_tickUpper);
71:            }
72:        }
73:    
74:        /// @notice Add liquidity to the chunk.

80:             return self + uint256(amount);
81:            }
82:        }
83:    
84:        /// @notice Add the lower tick to this chunk.
85:        /// @param self the LiquidityChunk

90:             return self + (uint256(uint24(_tickLower)) << 232);
91:            }
92:        }
93:    
94:        /// @notice Add the upper tick to this chunk.

100:             // convert tick upper to uint24 as explicit conversion from int24 to uint256 is not allowed
101:                 return self + ((uint256(uint24(_tickUpper))) << 208);

116:     }
117:     
118:         /// @notice Get the upper tick of a chunk.
119:         /// @param self the LiquidityChunk uint256

125:     }
126:     
127:         /// @notice Get the amount of liquidity/size of a chunk.

136: 

68-74, 80-85, 90-94, 100-101, 116-119, 125-127, 136

File: contracts/types/TokenId.sol

80:     function univ3pool(uint256 self) internal pure returns (uint64) {

93:     function asset(uint256 self, uint256 legIndex) internal pure returns (uint256) {

103:     function optionRatio(uint256 self, uint256 legIndex) internal pure returns (uint256) {

113:     function isLong(uint256 self, uint256 legIndex) internal pure returns (uint256) {

123:     function tokenType(uint256 self, uint256 legIndex) internal pure returns (uint256) {

139:     function riskPartner(uint256 self, uint256 legIndex) internal pure returns (uint256) {

149:     function strike(uint256 self, uint256 legIndex) internal pure returns (int24) {

160:     function width(uint256 self, uint256 legIndex) internal pure returns (int24) {

173:     function addUniv3pool(uint256 self, uint64 _poolId) internal pure returns (uint256) {

189:     function addAsset(
190:             uint256 self,
191:             uint256 _asset,
192:             uint256 legIndex
193:         ) internal pure returns (uint256) {

204:     function addOptionRatio(
205:             uint256 self,
206:             uint256 _optionRatio,
207:             uint256 legIndex
208:         ) internal pure returns (uint256) {

220:     function addIsLong(
221:             uint256 self,
222:             uint256 _isLong,
223:             uint256 legIndex
224:         ) internal pure returns (uint256) {

234:     function addTokenType(
235:             uint256 self,
236:             uint256 _tokenType,
237:             uint256 legIndex
238:         ) internal pure returns (uint256) {

248:     function addRiskPartner(
249:             uint256 self,
250:             uint256 _riskPartner,
251:             uint256 legIndex
252:         ) internal pure returns (uint256) {

262:     function addStrike(
263:             uint256 self,
264:             int24 _strike,
265:             uint256 legIndex
266:         ) internal pure returns (uint256) {

276:     function addWidth(
277:             uint256 self,
278:             int24 _width,
279:             uint256 legIndex
280:         ) internal pure returns (uint256) {

327:     function flipToBurnToken(uint256 self) internal pure returns (uint256) {

361:     function countLongs(uint256 self) internal pure returns (uint256) {

410:     function countLegs(uint256 self) internal pure returns (uint256) {

442:     function clearLeg(uint256 self, uint256 i) internal pure returns (uint256) {

463:     function validate(uint256 self) internal pure returns (uint64) {

80, 93, 103, 113, 123, 139, 149, 160, 173, 189-193, 204-208, 220-224, 234-238, 248-252, 262-266, 276-280, 327, 361, 410, 442, 463

</details>

<a name="G-18"></a>[G-18] Unused named return variables without optimizer waste gas

Consider changing the variable to be an unnamed one, since the variable is never assigned, nor is it returned by name. If the optimizer is not turned on, leaving the code as it is will also waste gas for the stack variable.

Instances (5):

File: contracts/SemiFungiblePositionManager.sol

1472: 

1472

File: contracts/libraries/Math.sol

101:     function getAmount0ForLiquidity(
102:             uint256 liquidityChunk
103:         ) internal pure returns (uint256 amount0) {

119:     function getAmount1ForLiquidity(
120:             uint256 liquidityChunk
121:         ) internal pure returns (uint256 amount1) {

135:     function getLiquidityForAmount0(
136:             uint256 liquidityChunk,
137:             uint256 amount0
138:         ) internal pure returns (uint128 liquidity) {

154:     function getLiquidityForAmount1(
155:             uint256 liquidityChunk,
156:             uint256 amount1
157:         ) internal pure returns (uint128 liquidity) {

101-103, 119-121, 135-138, 154-157

<a name="G-19"></a>[G-19] Pre-increments and pre-decrements are cheaper than post-increments and post-decrements

Saves 5 gas per iteration

Instances (1):

File: contracts/SemiFungiblePositionManager.sol

261:         However, since we require that Eqn 2 holds up-- ie. the gross fees collected should be equal

261

<a name="G-20"></a>[G-20] State variables which are not modified within functions should be set as constant or immutable for values set at deployment

Cache such variables and perform operations on them, if operations include modifications to the state variable(s) then remember to equate the state variable to it's cached counterpart at the end

Instances (5):

File: contracts/SemiFungiblePositionManager.sol

147:     mapping(address univ3pool => uint256 poolIdData) internal s_AddrToPoolIdData;

152:     mapping(uint64 poolId => PoolAddressAndLock contextData) internal s_poolContext;

179:     mapping(bytes32 positionKey => uint256 removedAndNetLiquidity) internal s_accountLiquidity;

288:     mapping(bytes32 positionKey => uint256 accountPremium) private s_accountPremiumOwed;

290:     mapping(bytes32 positionKey => uint256 accountPremium) private s_accountPremiumGross;

147, 152, 179, 288, 290

<a name="G-21"></a>[G-21] private functions used once can be inlined

Private functions which are only called once can be inlined to save GAS.

Instances (1):

File: contracts/SemiFungiblePositionManager.sol

1092:     /// @param liquidityChunk has lower tick, upper tick, and liquidity amount to mint
1093:          function _getFeesBase(
1094:              IUniswapV3Pool univ3pool,
1095:              uint128 liquidity,
1096:              uint256 liquidityChunk

1092-1096

<a name="G-22"></a>[G-22] Use upcoming OpenZeppelin contracts

The upcoming version of OpenZeppelin provides many small gas optimizations.

Instances (1):

File: package.json

/// @audit path: 
1: version: 4.8.3

1

#0 - c4-judge

2023-12-14T17:12:02Z

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