Panoptic - codeslide's results

Permissionless, perpetual options trading on any token, any strike, any size.

General Information

Platform: Code4rena

Start Date: 01/04/2024

Pot Size: $120,000 USDC

Total HM: 11

Participants: 55

Period: 21 days

Judge: Picodes

Total Solo HM: 6

Id: 354

League: ETH

Panoptic

Findings Distribution

Researcher Performance

Rank: 52/55

Findings: 1

Award: $32.96

๐ŸŒŸ Selected for report: 0

๐Ÿš€ Solo Findings: 0

2024-04-panoptic

Table of Contents

  1. Summary

    1. Low Risk Issues

    2. Non-Critical Issues

  2. Details

    1. Low Risk Issues

    2. Non-Critical Issues


<a id="summary"></a> Summary

<a id="summary-low-risk-issues"></a> Low Risk Issues

There are 425 instances over 17 issues.

IDDescriptionCount
L-01Consider disallowing minting/transfers to address(this)1
L-02constructor/initialize function lacks parameter validation1
L-03Contracts use infinite approvals with no means to revoke4
L-04Fee-on-transfer and rebasing tokens will have problems when swapping1
L-05File allows a version of Solidity that is susceptible to .selector-related optimizer bug1
L-06for and while loops in public or external functions should be avoided due to high gas costs and possible DOS18
L-07Functions calling contracts/addresses with transfer hooks are missing reentrancy guards3
L-08internal function calls within loops35
L-09Large approvals may not work with some ERC20 tokens4
L-10Large transfers may not work with some ERC20 tokens2
L-11Missing contract-existence checks before yul call()2
L-12Missing zero address check in constructor/initializer4
L-13Missing zero address check in functions with address parameters83
L-14Some tokens may not consider type(uint256).max as an infinite approval4
L-15Unsafe addition/multiplication in unchecked block177
L-16Unsafe subtraction in unchecked block82
L-17Vulnerable versions of packages are being used3

<a id="summary-non-critical-issues"></a> Non-Critical Issues

There are 1233 instances over 63 issues.

IDDescriptionCount
N-01Add inline comments for unnamed variables2
N-02Array parameter lengths not validated7
N-03Assembly blocks should have extensive comments21
N-04Complex arithmetic40
N-05Complex casting101
N-06Consider adding a block/deny-list6
N-07Consider adding formal verification proofs1
N-08Consider adding validation of user inputs28
N-09Consider bounding input array length4
N-10Consider emitting an event at the end of the constructor4
N-11Consider making contracts Upgradeable4
N-12Consider returning a struct rather than having multiple return values7
N-13Consider splitting complex checks into multiple steps21
N-14Consider using a struct rather than having many function input parameters40
N-15Consider using descriptive constants when passing zero as a function argument62
N-16Consider using named function arguments117
N-17Constants in comparisons should appear on the left side182
N-18Contract should expose an interface71
N-19Contract uses both require()/revert() as well as custom errors1
N-20Custom errors should be used rather than revert()/require()9
N-21Duplicated revert() checks should be refactored to a modifier or function2
N-22else-block not required10
N-23Enable IR-based code generation1
N-24Events are missing sender information5
N-25High cyclomatic complexity5
N-26if-statement can be converted to a ternary6
N-27Inconsistent spacing in comments5
N-28Large contracts should be refactored into multiple, smaller, contracts6
N-29Local variable shadows state variable16
N-30Missing checks for state variable assignments18
N-31Missing empty bytes parameter check4
N-32Missing event emission in initializer1
N-33Missing event for critical parameter change1
N-34Multiple address/ID mappings can be combined into a single mapping of an address/ID to a struct, for readability12
N-35NatSpec: Contract declarations should have @author tags3
N-36NatSpec: Contract declarations should have @dev tags12
N-37NatSpec: Contract declarations should have @title tag5
N-38NatSpec: Contract declarations should have descriptions1
N-39NatSpec: Function declarations should have @notice tag3
N-40NatSpec: Function declarations should have descriptions1
N-41NatSpec: Invalid NatSpec comment style6
N-42NatSpec: State variable declarations should have descriptions9
N-43NatSpec: Use @inheritdoc for overridden functions4
N-44NatSpec: Use appropriate tags for state variable declarations43
N-45Non-library/interface files should use fixed compiler versions, not floating ones7
N-46Not using the named return variables anywhere in the function is confusing15
N-47Numeric values having to do with time should use time units for readability8
N-48Polymorphic functions make security audits more time-consuming and error-prone31
N-49Setters should prevent re-setting of the same value20
N-50String literals used more than once should be replaced with a constant5
N-51Style guide: Horizontal whitespace around binary operators1
N-52Style guide: Modifier names should use lowerCamelCase1
N-53Style guide: State and local variables should be named using lowerCamelCase52
N-54Style guide: Variable names that consist of all capital letters and underscores should be reserved for constants and immutable variables2
N-55Typos68
N-56Unnecessary cast11
N-57Unsafe conversion from unsigned to signed integer79
N-58Unused contract variables3
N-59Unused import1
N-60Use bit shifts rather than long bit masks of a single bit, for readability8
N-61Use more recent OpenZeppelin version for bug fixes and performance improvements5
N-62Variables need not be initialized to false1
N-63Visibility should be set explicitly rather than defaulting to internal8

<a id="details"></a> Details

<a id="details-low-risk-issues"></a> Low Risk Issues

<a id="l-01"></a> [L-01] Consider disallowing minting/transfers to address(this)
Description:

A transfer to the token contract itself is unlikely to be correct and more likely to be a copy and paste error. Proceeding with such a transfer will result in the permanent loss of user tokens.

Instances:

There is 1 instance of this issue.

File: contracts/CollateralTracker.sol

477:     function mint(uint256 shares, address receiver) external returns (uint256 assets) {
File LinkInstance CountInstance Link
CollateralTracker.sol1477

<a id="l-02"></a> [L-02] constructor/initialize function lacks parameter validation
Description:

Constructors and initialization functions play a critical role in contracts by setting important initial states when the contract is first deployed before the system starts. The parameters passed to the constructor and initialization functions directly affect the behavior of the contract / protocol. If incorrect parameters are provided, the system may fail to run, behave abnormally, be unstable, or lack security. Therefore, it is crucial to carefully check each parameter in the constructor and initialization functions. If an exception is found, the transaction should be rolled back.

Recommendation:

Consider whether reasonable bounds checks for variables would be useful.

Instances:

There is 1 instance of this issue.

File: contracts/CollateralTracker.sol

/// @audit parameters not validated: _commissionFee, _sellerCollateralRatio, _buyerCollateralRatio, _forceExerciseCost, _targetPoolUtilization, _saturatedPoolUtilization, _ITMSpreadMultiplier
178:     constructor(
179:         uint256 _commissionFee,
180:         uint256 _sellerCollateralRatio,
181:         uint256 _buyerCollateralRatio,
182:         int256 _forceExerciseCost,
183:         uint256 _targetPoolUtilization,
184:         uint256 _saturatedPoolUtilization,
185:         uint256 _ITMSpreadMultiplier
186:     ) {
File LinkInstance CountInstance Link
CollateralTracker.sol1178

<a id="l-03"></a> [L-03] Contracts use infinite approvals with no means to revoke
Description:

Infinite approvals on external contracts can be dangerous if the target becomes compromised. The following contracts are vulnerable to such attacks since they have no functionality to revoke the approval (call approve with amount 0).

Recommendation:

Consider adding the ability to revoke approval in emergency situations.

Instances:

There are 4 instances of this issue.

File: contracts/libraries/InteractionHelper.sol

32:         IERC20Partial(token0).approve(address(sfpm), type(uint256).max);

33:         IERC20Partial(token1).approve(address(sfpm), type(uint256).max);

36:         IERC20Partial(token0).approve(address(ct0), type(uint256).max);

37:         IERC20Partial(token1).approve(address(ct1), type(uint256).max);
File LinkInstance CountInstance Links
InteractionHelper.sol432,33,36,37

<a id="l-04"></a> [L-04] Fee-on-transfer and rebasing tokens will have problems when swapping
Description:

Uniswap v3 does not support rebasing or fee-on-transfer tokens so using these tokens with Uniswap will result in transfers after the swap failing due to insufficient tokens, or with the contract having a remaining balance after the transfer.

Instances:

There is 1 instance of this issue.

File: contracts/SemiFungiblePositionManager.sol

837:             (int256 swap0, int256 swap1) = _univ3pool.swap(
File LinkInstance CountInstance Link
SemiFungiblePositionManager.sol1837

<a id="l-05"></a> [L-05] File allows a version of Solidity that is susceptible to .selector-related optimizer bug
Description:

In Solidity versions starting with 0.6.2 and before 0.8.21, the legacy code generator was not evaluating complex expressions, like assignments, function calls, or conditionals, whose .selector was being accessed. For example, in an expression like f().g.selector, the result will always be the same regardless of what f() returns. It is listed as low severity because projects usually use the contract name rather than a function call to look up the selector. All files using .selector where the Solidity version is vulnerable have been reported.

Instances:

There is 1 instance of this issue.

File: contracts/tokens/ERC1155Minimal.sol

2: pragma solidity ^0.8.0;
File LinkInstance CountInstance Link
ERC1155Minimal.sol12

<a id="l-06"></a> [L-06] for and while loops in public or external functions should be avoided due to high gas costs and possible DOS
Description:

In Solidity, for and while loops can potentially cause denial of service (DOS) attacks if not handled carefully. DOS attacks can occur when an attacker intentionally exploits the gas cost of a function, causing it to run out of gas or making it too expensive for other users to call. Below are some scenarios where for or while loops can lead to DOS attacks: Nested for and while loops can become exceptionally gas expensive and should be used sparingly.

Instances:

There are 18 instances of this issue.

<details><summary>View 18 Instances</summary>
File: contracts/CollateralTracker.sol

/// @audit external function exerciseCost()
662:             for (uint256 leg = 0; leg < positionId.countLegs(); ++leg) {
File LinkInstance CountInstance Link
CollateralTracker.sol1662

File: contracts/PanopticFactory.sol

/// @audit external function minePoolAddress()
303:         for (; uint256(salt) < maxSalt; ) {
File LinkInstance CountInstance Link
PanopticFactory.sol1303

File: contracts/SemiFungiblePositionManager.sol

/// @audit external function initializeAMMPool()
372:         while (address(s_poolContext[poolId].pool) != address(0)) {

/// @audit public function safeBatchTransferFrom()
575:         for (uint256 i = 0; i < ids.length; ) {
File LinkInstance CountInstance Links
SemiFungiblePositionManager.sol2372,575

File: contracts/libraries/FeesCalc.sol

/// @audit external function getPortfolioValue()
51:         for (uint256 k = 0; k < positionIdList.length; ) {

/// @audit external function getPortfolioValue()
55:             for (uint256 leg = 0; leg < numLegs; ) {
File LinkInstance CountInstance Links
FeesCalc.sol251,55

File: contracts/libraries/PanopticMath.sol

/// @audit external function computeMedianObservedPrice()
137:             for (uint256 i = 0; i < cardinality + 1; ++i) {

/// @audit external function computeMedianObservedPrice()
148:             for (uint256 i = 0; i < cardinality; ++i) {

/// @audit external function computeInternalMedian()
207:                 for (uint8 i; i < 8; ++i) {

/// @audit external function twapFilter()
248:             for (uint256 i = 0; i < 20; ++i) {

/// @audit external function twapFilter()
256:             for (uint256 i = 0; i < 19; ++i) {

/// @audit external function haircutPremia()
781:             for (uint256 i = 0; i < positionIdList.length; ++i) {

/// @audit external function haircutPremia()
784:                 for (uint256 leg = 0; leg < numLegs; ++leg) {

/// @audit external function haircutPremia()
860:             for (uint256 i = 0; i < positionIdList.length; i++) {

/// @audit external function haircutPremia()
863:                 for (uint256 leg = 0; leg < tokenId.countLegs(); ++leg) {
File LinkInstance CountInstance Links
PanopticMath.sol9137,148,207,248,256,781,784,860,863

File: contracts/multicall/Multicall.sol

/// @audit public function multicall()
14:         for (uint256 i = 0; i < data.length; ) {
File LinkInstance CountInstance Link
Multicall.sol114

File: contracts/tokens/ERC1155Minimal.sol

/// @audit public function safeBatchTransferFrom()
143:         for (uint256 i = 0; i < ids.length; ) {

/// @audit public function balanceOfBatch()
187:             for (uint256 i = 0; i < owners.length; ++i) {
File LinkInstance CountInstance Links
ERC1155Minimal.sol2143,187

</details>
<a id="l-07"></a> [L-07] Functions calling contracts/addresses with transfer hooks are missing reentrancy guards
Description:

Even if the function follows the best practice of the check-effects-interactions pattern, not using a reentrancy guard when there may be transfer hooks will open the users of this protocol up to read-only reentrancies with no way to protect against it other than by block-listing the whole protocol.

Recommendation:

Add a reentrancy guard to any function using a transfer hook.

Instances:

There are 3 instances of this issue.

File: contracts/PanopticFactory.sol

/// @audit function: `uniswapV3MintCallback()`
182:             SafeTransferLib.safeTransferFrom(
183:                 decoded.poolFeatures.token0,
184:                 decoded.payer,
185:                 msg.sender,
186:                 amount0Owed
187:             );
File LinkInstance CountInstance Link
PanopticFactory.sol1182

File: contracts/SemiFungiblePositionManager.sol

/// @audit function: `uniswapV3MintCallback()`
413:             SafeTransferLib.safeTransferFrom(
414:                 decoded.poolFeatures.token0,
415:                 decoded.payer,
416:                 msg.sender,
417:                 amount0Owed
418:             );

/// @audit function: `uniswapV3SwapCallback()`
456:         SafeTransferLib.safeTransferFrom(token, decoded.payer, msg.sender, amountToPay);
File LinkInstance CountInstance Links
SemiFungiblePositionManager.sol2413,456

<a id="l-08"></a> [L-08] internal function calls within loops
Description:

Making internal function calls within loops in Solidity can lead to inefficient gas usage, potential bottlenecks, and increased vulnerability to denial of service (DOS) attacks. Each function call consumes gas, and when executed within a loop, the gas cost multiplies, potentially causing the transaction to run out of gas or exceed block gas limits. This can result in transaction failure or unpredictable behavior.

Instances:

There are 35 instances of this issue.

<details><summary>View 35 Instances</summary>
File: contracts/CollateralTracker.sol

/// @audit _getRequiredCollateralAtTickSinglePosition()
1219:             uint256 _tokenRequired = _getRequiredCollateralAtTickSinglePosition(
1220:                 tokenId,
1221:                 positionSize,
1222:                 atTick,
1223:                 poolUtilization
1224:             );

/// @audit _getRequiredCollateralSingleLeg()
1259:                 tokenRequired += _getRequiredCollateralSingleLeg(
1260:                     tokenId,
1261:                     index,
1262:                     positionSize,
1263:                     atTick,
1264:                     poolUtilization
1265:                 );
File LinkInstance CountInstance Links
CollateralTracker.sol21219,1259

File: contracts/PanopticPool.sol

/// @audit _getPremia()
450:             ) = _getPremia(
451:                     tokenId,
452:                     LeftRightUnsigned.wrap(balances[k][1]).rightSlot(),
453:                     c_user,
454:                     computeAllPremia,
455:                     atTick
456:                 );

/// @audit _getAvailablePremium()
469:                     LeftRightUnsigned availablePremium = _getAvailablePremium(
470:                         _getTotalLiquidity(tokenId, leg),
471:                         s_settledTokens[chunkKey],
472:                         s_grossPremiumLast[chunkKey],
473:                         LeftRightUnsigned.wrap(uint256(LeftRightSigned.unwrap(premiaByLeg[leg]))),
474:                         premiumAccumulatorsByLeg[leg]
475:                     );

/// @audit _getAvailablePremium()
469:                     LeftRightUnsigned availablePremium = _getAvailablePremium(
470:                         _getTotalLiquidity(tokenId, leg),
471:                         s_settledTokens[chunkKey],
472:                         s_grossPremiumLast[chunkKey],
473:                         LeftRightUnsigned.wrap(uint256(LeftRightSigned.unwrap(premiaByLeg[leg]))),
474:                         premiumAccumulatorsByLeg[leg]
475:                     );

/// @audit _getTotalLiquidity()
470:                         _getTotalLiquidity(tokenId, leg),

/// @audit _getTotalLiquidity()
470:                         _getTotalLiquidity(tokenId, leg),

/// @audit _checkLiquiditySpread()
770:                 _checkLiquiditySpread(
771:                     tokenId,
772:                     leg,
773:                     tickLower,
774:                     tickUpper,
775:                     uint64(Math.min(effectiveLiquidityLimitX32, MAX_SPREAD))
776:                 );

/// @audit _burnOptions()
804:             (paidAmounts, premiasByLeg[i]) = _burnOptions(
805:                 commitLongSettled,
806:                 positionIdList[i],
807:                 owner,
808:                 tickLimitLow,
809:                 tickLimitHigh
810:             );

/// @audit _checkLiquiditySpread()
868:                 _checkLiquiditySpread(tokenId, leg, tickLower, tickUpper, MAX_SPREAD);

/// @audit _getTotalLiquidity()
1687:                 uint256 totalLiquidity = _getTotalLiquidity(tokenId, leg);

/// @audit _getTotalLiquidity()
1882:                     uint256 totalLiquidity = _getTotalLiquidity(tokenId, leg);

/// @audit _getAvailablePremium()
1888:                     LeftRightUnsigned availablePremium = _getAvailablePremium(
1889:                         totalLiquidity + positionLiquidity,
1890:                         settledTokens,
1891:                         grossPremiumLast,
1892:                         LeftRightUnsigned.wrap(uint256(LeftRightSigned.unwrap(legPremia))),
1893:                         premiumAccumulatorsByLeg[leg]
1894:                     );
File LinkInstance CountInstance Links
PanopticPool.sol11450,469,469,470,470,770,804,868,1687,1882,1888

File: contracts/SemiFungiblePositionManager.sol

/// @audit registerTokenTransfer()
577:             registerTokenTransfer(from, to, TokenId.wrap(ids[i]), amounts[i]);

/// @audit _createLegInAMM()
910:                 (_moved, _itmAmounts, _collectedSingleLeg) = _createLegInAMM(
911:                     _univ3pool,
912:                     _tokenId,
913:                     _leg,
914:                     liquidityChunk,
915:                     _isBurn
916:                 );
File LinkInstance CountInstance Links
SemiFungiblePositionManager.sol2577,910

File: contracts/libraries/PanopticMath.sol

/// @audit _calculateIOAmounts()
397:             (LeftRightSigned longs, LeftRightSigned shorts) = _calculateIOAmounts(
398:                 tokenId,
399:                 positionSize,
400:                 leg
401:             );
File LinkInstance CountInstance Link
PanopticMath.sol1397

File: contracts/types/TokenId.sol

/// @audit optionRatio()
508:                 if (self.optionRatio(i) == 0) {

/// @audit countLegs()
519:                 uint256 numLegs = self.countLegs();

/// @audit width()
528:                 if ((self.width(i) == 0)) revert Errors.InvalidTokenIdParameter(5);

/// @audit strike()
531:                     (self.strike(i) == Constants.MIN_V3POOL_TICK) ||

/// @audit strike()
532:                     (self.strike(i) == Constants.MAX_V3POOL_TICK)

/// @audit riskPartner()
538:                 uint256 riskPartnerIndex = self.riskPartner(i);

/// @audit riskPartner()
541:                     if (self.riskPartner(riskPartnerIndex) != i)

/// @audit asset()
546:                         (self.asset(riskPartnerIndex) != self.asset(i)) ||

/// @audit asset()
546:                         (self.asset(riskPartnerIndex) != self.asset(i)) ||

/// @audit optionRatio()
547:                         (self.optionRatio(riskPartnerIndex) != self.optionRatio(i))

/// @audit optionRatio()
547:                         (self.optionRatio(riskPartnerIndex) != self.optionRatio(i))

/// @audit isLong()
551:                     uint256 _isLong = self.isLong(i);

/// @audit isLong()
552:                     uint256 isLongP = self.isLong(riskPartnerIndex);

/// @audit tokenType()
555:                     uint256 _tokenType = self.tokenType(i);

/// @audit tokenType()
556:                     uint256 tokenTypeP = self.tokenType(riskPartnerIndex);

/// @audit width()
583:                     self.width(i),

/// @audit tickSpacing()
584:                     self.tickSpacing()

/// @audit strike()
587:                 int24 _strike = self.strike(i);

/// @audit isLong()
592:                     if (self.isLong(i) == 1) return; // validated
File LinkInstance CountInstance Links
TokenId.sol19508,519,528,531,532,538,541,546,546,547,547,551,552,555,556,583,584,587,592

</details>
<a id="l-09"></a> [L-09] Large approvals may not work with some ERC20 tokens
Description:

Not all IERC20 implementations are totally compliant, and some (e.g., UNI, COMP) may fail if the valued passed to approve is larger than uint96. If the approval amount is type(uint256).max, this may cause issues with systems that expect the value passed to approve to be reflected in the allowances mapping.

Instances:

There are 4 instances of this issue.

File: contracts/libraries/InteractionHelper.sol

32:         IERC20Partial(token0).approve(address(sfpm), type(uint256).max);

33:         IERC20Partial(token1).approve(address(sfpm), type(uint256).max);

36:         IERC20Partial(token0).approve(address(ct0), type(uint256).max);

37:         IERC20Partial(token1).approve(address(ct1), type(uint256).max);
File LinkInstance CountInstance Links
InteractionHelper.sol432,33,36,37

<a id="l-10"></a> [L-10] Large transfers may not work with some ERC20 tokens
Description:

Some IERC20 implementations (e.g., UNI, COMP) may fail if the valued transferred is larger than uint96. Source.

Recommendation:

Check the actual balance before and after vs. the expected balance.

Instances:

There are 2 instances of this issue.

File: contracts/CollateralTracker.sol

/// @audit uint256 amount
333:         return ERC20Minimal.transfer(recipient, amount);

/// @audit uint256 amount
352:         return ERC20Minimal.transferFrom(from, to, amount);
File LinkInstance CountInstance Links
CollateralTracker.sol2333,352

<a id="l-11"></a> [L-11] Missing contract-existence checks before yul call()
Description:

Low-level calls return success if there is no code present at the specified address. In addition to the zero-address checks, add a check to verify that extcodesize() is non-zero.

Instances:

There are 2 instances of this issue.

File: contracts/libraries/SafeTransferLib.sol

/// @audit token
24:         assembly ("memory-safe") {
25:             // Get free memory pointer - we will store our calldata in scratch space starting at the offset specified here.
26:             let p := mload(0x40)
27:
28:             // Write the abi-encoded calldata into memory, beginning with the function selector.
29:             mstore(p, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
30:             mstore(add(4, p), from) // Append the "from" argument.
31:             mstore(add(36, p), to) // Append the "to" argument.
32:             mstore(add(68, p), amount) // Append the "amount" argument.
33:
34:             success := and(
35:                 // Set success to whether the call reverted, if not we check it either
36:                 // returned exactly 1 (can't just be non-zero data), or had no return data.
37:                 or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
38:                 // We use 100 because that's the total length of our calldata (4 + 32 * 3)
39:                 // Counterintuitively, this call() must be positioned after the or() in the
40:                 // surrounding and() because and() evaluates its arguments from right to left.
41:                 call(gas(), token, 0, p, 100, 0, 32)
42:             )
43:         }

/// @audit token
55:         assembly ("memory-safe") {
56:             // Get free memory pointer - we will store our calldata in scratch space starting at the offset specified here.
57:             let p := mload(0x40)
58:
59:             // Write the abi-encoded calldata into memory, beginning with the function selector.
60:             mstore(p, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
61:             mstore(add(4, p), to) // Append the "to" argument.
62:             mstore(add(36, p), amount) // Append the "amount" argument.
63:
64:             success := and(
65:                 // Set success to whether the call reverted, if not we check it either
66:                 // returned exactly 1 (can't just be non-zero data), or had no return data.
67:                 or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
68:                 // We use 68 because that's the total length of our calldata (4 + 32 * 2)
69:                 // Counterintuitively, this call() must be positioned after the or() in the
70:                 // surrounding and() because and() evaluates its arguments from right to left.
71:                 call(gas(), token, 0, p, 68, 0, 32)
72:             )
73:         }
File LinkInstance CountInstance Links
SafeTransferLib.sol224,55

<a id="l-12"></a> [L-12] Missing zero address check in constructor/initializer
Description:

Constructors and initializer functions often take address parameters to initialize important components of a contract, such as the owner or linked contracts. However, without validation, there is a risk that an address parameter could be mistakenly set to the zero address (0x0) due to an error or an oversight during contract deployment. A zero address in a crucial role can cause serious issues, as it cannot perform actions like a normal address. Any funds sent to it will be irretrievable. It is therefore crucial to include a zero address check in constructors to prevent such problems. If a zero address is detected, the constructor should revert the transaction.

Recommendation:

Consider adding explicit zero-address validation prior to use of address parameters in constructors.

Instances:

There are 4 instances of this issue.

File: contracts/PanopticFactory.sol

/// @audit Not checked against address zero: _WETH9, _SFPM, _univ3Factory, _donorNFT, _poolReference, _collateralReference
115:     constructor(
116:         address _WETH9,
117:         SemiFungiblePositionManager _SFPM,
118:         IUniswapV3Factory _univ3Factory,
119:         IDonorNFT _donorNFT,
120:         address _poolReference,
121:         address _collateralReference
122:     ) {

/// @audit Not checked against address zero: _owner
134:     function initialize(address _owner) public {
File LinkInstance CountInstance Links
PanopticFactory.sol2115,134

File: contracts/PanopticPool.sol

/// @audit Not checked against address zero: _sfpm
280:     constructor(SemiFungiblePositionManager _sfpm) {
File LinkInstance CountInstance Link
PanopticPool.sol1280

File: contracts/SemiFungiblePositionManager.sol

/// @audit Not checked against address zero: _factory
341:     constructor(IUniswapV3Factory _factory) {
File LinkInstance CountInstance Link
SemiFungiblePositionManager.sol1341

<a id="l-13"></a> [L-13] Missing zero address check in functions with address parameters
Description:

A zero address in a crucial role can cause serious issues, as it cannot perform actions like a normal address. Any funds sent to it will be irretrievable. Even for protected functions, an explicit zero address check can prevent user errors due to typos, copy/paste errors, and similar.

Recommendation:

Consider adding explicit zero-address validation prior to use of address parameters in functions.

Instances:

There are 83 instances of this issue.

<details><summary>View 83 Instances</summary>
File: contracts/CollateralTracker.sol

/// @audit Not checked against address zero: token0, token1, panopticPool
221:     function startToken(
222:         bool underlyingIsToken0,
223:         address token0,
224:         address token1,
225:         uint24 fee,
226:         PanopticPool panopticPool
227:     ) external {

/// @audit Not checked against address zero: recipient
323:     function transfer(
324:         address recipient,
325:         uint256 amount
326:     ) public override(ERC20Minimal) returns (bool) {

/// @audit Not checked against address zero: from, to
341:     function transferFrom(
342:         address from,
343:         address to,
344:         uint256 amount
345:     ) public override(ERC20Minimal) returns (bool) {

/// @audit Not checked against address zero:
392:     function maxDeposit(address) external pure returns (uint256 maxAssets) {

/// @audit Not checked against address zero: receiver
417:     function deposit(uint256 assets, address receiver) external returns (uint256 shares) {

/// @audit Not checked against address zero:
444:     function maxMint(address) external view returns (uint256 maxShares) {

/// @audit Not checked against address zero: receiver
477:     function mint(uint256 shares, address receiver) external returns (uint256 assets) {

/// @audit Not checked against address zero: owner
507:     function maxWithdraw(address owner) public view returns (uint256 maxAssets) {

/// @audit Not checked against address zero: receiver, owner
531:     function withdraw(
532:         uint256 assets,
533:         address receiver,
534:         address owner
535:     ) external returns (uint256 shares) {

/// @audit Not checked against address zero: owner
572:     function maxRedeem(address owner) public view returns (uint256 maxShares) {

/// @audit Not checked against address zero: receiver, owner
591:     function redeem(
592:         uint256 shares,
593:         address receiver,
594:         address owner
595:     ) external returns (uint256 assets) {

/// @audit Not checked against address zero: delegator, delegatee
864:     function delegate(
865:         address delegator,
866:         address delegatee,
867:         uint256 assets
868:     ) external onlyPanopticPool {

/// @audit Not checked against address zero: delegatee
894:     function delegate(address delegatee, uint256 assets) external onlyPanopticPool {

/// @audit Not checked against address zero: delegatee
903:     function refund(address delegatee, uint256 assets) external onlyPanopticPool {

/// @audit Not checked against address zero: delegator, delegatee
911:     function revoke(
912:         address delegator,
913:         address delegatee,
914:         uint256 assets
915:     ) external onlyPanopticPool {

/// @audit Not checked against address zero: refunder, refundee
975:     function refund(address refunder, address refundee, int256 assets) external onlyPanopticPool {

/// @audit Not checked against address zero: optionOwner
 995:     function takeCommissionAddData(
 996:         address optionOwner,
 997:         int128 longAmount,
 998:         int128 shortAmount,
 999:         int128 swappedAmount
1000:     ) external onlyPanopticPool returns (int256 utilization) {

/// @audit Not checked against address zero: optionOwner
1043:     function exercise(
1044:         address optionOwner,
1045:         int128 longAmount,
1046:         int128 shortAmount,
1047:         int128 swappedAmount,
1048:         int128 realizedPremium
1049:     ) external onlyPanopticPool returns (int128) {

/// @audit Not checked against address zero: user
1141:     function getAccountMarginDetails(
1142:         address user,
1143:         int24 currentTick,
1144:         uint256[2][] memory positionBalanceArray,
1145:         int128 premiumAllPositions
1146:     ) public view returns (LeftRightUnsigned tokenData) {

/// @audit Not checked against address zero: user
1159:     function _getAccountMargin(
1160:         address user,
1161:         int24 atTick,
1162:         uint256[2][] memory positionBalanceArray,
1163:         int128 premiumAllPositions
1164:     ) internal view returns (LeftRightUnsigned tokenData) {
File LinkInstance CountInstance Links
CollateralTracker.sol20221,323,341,392,417,444,477,507,531,572,591,864,894,903,911,975,995,1043,1141,1159

File: contracts/PanopticFactory.sol

/// @audit Not checked against address zero: newOwner
147:     function transferOwnership(address newOwner) external {

/// @audit Not checked against address zero: token0, token1
210:     function deployNewPool(
211:         address token0,
212:         address token1,
213:         uint24 fee,
214:         bytes32 salt
215:     ) external returns (PanopticPool newPoolContract) {

/// @audit Not checked against address zero: v3Pool, token0, token1
335:     function _mintFullRange(
336:         IUniswapV3Pool v3Pool,
337:         address token0,
338:         address token1,
339:         uint24 fee
340:     ) internal returns (uint256, uint256) {

/// @audit Not checked against address zero: univ3pool
420:     function getPanopticPool(IUniswapV3Pool univ3pool) external view returns (PanopticPool) {
File LinkInstance CountInstance Links
PanopticFactory.sol4147,210,335,420

File: contracts/PanopticPool.sol

/// @audit Not checked against address zero: _univ3pool, token0, token1, collateralTracker0, collateralTracker1
291:     function startPool(
292:         IUniswapV3Pool _univ3pool,
293:         address token0,
294:         address token1,
295:         CollateralTracker collateralTracker0,
296:         CollateralTracker collateralTracker1
297:     ) external {

/// @audit Not checked against address zero: user
352:     function optionPositionBalance(
353:         address user,
354:         TokenId tokenId
355:     ) external view returns (uint128 balance, uint64 poolUtilization0, uint64 poolUtilization1) {

/// @audit Not checked against address zero: user
381:     function calculateAccumulatedFeesBatch(
382:         address user,
383:         bool includePendingPremium,
384:         TokenId[] calldata positionIdList
385:     ) external view returns (int128 premium0, int128 premium1, uint256[2][] memory) {

/// @audit Not checked against address zero: user
410:     function calculatePortfolioValue(
411:         address user,
412:         int24 atTick,
413:         TokenId[] calldata positionIdList
414:     ) external view returns (int256 value0, int256 value1) {

/// @audit Not checked against address zero: user
429:     function _calculateAccumulatedPremia(
430:         address user,
431:         TokenId[] calldata positionIdList,
432:         bool computeAllPremia,
433:         bool includePendingPremium,
434:         int24 atTick
435:     ) internal view returns (LeftRightSigned portfolioPremium, uint256[2][] memory balances) {

/// @audit Not checked against address zero: owner
794:     function _burnAllOptionsFrom(
795:         address owner,
796:         int24 tickLimitLow,
797:         int24 tickLimitHigh,
798:         bool commitLongSettled,
799:         TokenId[] calldata positionIdList
800:     ) internal returns (LeftRightSigned netPaid, LeftRightSigned[4][] memory premiasByLeg) {

/// @audit Not checked against address zero: owner
826:     function _burnOptions(
827:         bool commitLongSettled,
828:         TokenId tokenId,
829:         address owner,
830:         int24 tickLimitLow,
831:         int24 tickLimitHigh
832:     ) internal returns (LeftRightSigned paidAmounts, LeftRightSigned[4] memory premiaByLeg) {

/// @audit Not checked against address zero: owner
859:     function _updatePositionDataBurn(address owner, TokenId tokenId) internal {

/// @audit Not checked against address zero: user
887:     function _validateSolvency(
888:         address user,
889:         TokenId[] calldata positionIdList,
890:         uint256 buffer
891:     ) internal view returns (uint256 medianData) {

/// @audit Not checked against address zero: owner
955:     function _burnAndHandleExercise(
956:         bool commitLongSettled,
957:         int24 tickLimitLow,
958:         int24 tickLimitHigh,
959:         TokenId tokenId,
960:         uint128 positionSize,
961:         address owner
962:     )
963:         internal
964:         returns (
965:             LeftRightSigned realizedPremia,
966:             LeftRightSigned[4] memory premiaByLeg,
967:             LeftRightSigned paidAmounts
968:         )
969:     {

/// @audit Not checked against address zero: liquidatee
1017:     function liquidate(
1018:         TokenId[] calldata positionIdListLiquidator,
1019:         address liquidatee,
1020:         LeftRightUnsigned delegations,
1021:         TokenId[] calldata positionIdList
1022:     ) external {

/// @audit Not checked against address zero: account
1179:     function forceExercise(
1180:         address account,
1181:         TokenId[] calldata touchedId,
1182:         TokenId[] calldata positionIdListExercisee,
1183:         TokenId[] calldata positionIdListExercisor
1184:     ) external {

/// @audit Not checked against address zero: account
1290:     function _checkSolvencyAtTick(
1291:         address account,
1292:         TokenId[] calldata positionIdList,
1293:         int24 currentTick,
1294:         int24 atTick,
1295:         uint256 buffer
1296:     ) internal view returns (bool) {

/// @audit Not checked against address zero: account
1367:     function _validatePositionList(
1368:         address account,
1369:         TokenId[] calldata positionIdList,
1370:         uint256 offset
1371:     ) internal view {

/// @audit Not checked against address zero: account
1405:     function _updatePositionsHash(address account, TokenId tokenId, bool addFlag) internal {

/// @audit Not checked against address zero: user
1444:     function numberOfPositions(address user) public view returns (uint256 _numberOfPositions) {

/// @audit Not checked against address zero: owner
1503:     function _getPremia(
1504:         TokenId tokenId,
1505:         uint128 positionSize,
1506:         address owner,
1507:         bool computeAllPremia,
1508:         int24 atTick
1509:     )
1510:         internal
1511:         view
1512:         returns (
1513:             LeftRightSigned[4] memory premiaByLeg,
1514:             uint256[2][4] memory premiumAccumulatorsByLeg
1515:         )
1516:     {

/// @audit Not checked against address zero: owner
1587:     function settleLongPremium(
1588:         TokenId[] calldata positionIdList,
1589:         address owner,
1590:         uint256 legIndex
1591:     ) external {

/// @audit Not checked against address zero: owner
1833:     function _updateSettlementPostBurn(
1834:         address owner,
1835:         TokenId tokenId,
1836:         LeftRightUnsigned[4] memory collectedByLeg,
1837:         uint128 positionSize,
1838:         bool commitLongSettled
1839:     ) internal returns (LeftRightSigned realizedPremia, LeftRightSigned[4] memory premiaByLeg) {
File LinkInstance CountInstance Links
PanopticPool.sol19291,352,381,410,429,794,826,859,887,955,1017,1179,1290,1367,1405,1444,1503,1587,1833

File: contracts/SemiFungiblePositionManager.sol

/// @audit Not checked against address zero: token0, token1
350:     function initializeAMMPool(address token0, address token1, uint24 fee) external {

/// @audit Not checked against address zero: from, to
540:     function safeTransferFrom(
541:         address from,
542:         address to,
543:         uint256 id,
544:         uint256 amount,
545:         bytes calldata data
546:     ) public override {

/// @audit Not checked against address zero: from, to
566:     function safeBatchTransferFrom(
567:         address from,
568:         address to,
569:         uint256[] calldata ids,
570:         uint256[] calldata amounts,
571:         bytes calldata data
572:     ) public override {

/// @audit Not checked against address zero: from, to
593:     function registerTokenTransfer(address from, address to, TokenId id, uint256 amount) internal {

/// @audit Not checked against address zero: univ3pool
756:     function swapInAMM(
757:         IUniswapV3Pool univ3pool,
758:         LeftRightSigned itmAmounts
759:     ) internal returns (LeftRightSigned totalSwapped) {

/// @audit Not checked against address zero: univ3pool
863:     function _createPositionInAMM(
864:         IUniswapV3Pool univ3pool,
865:         TokenId tokenId,
866:         uint128 positionSize,
867:         bool isBurn
868:     )
869:         internal
870:         returns (
871:             LeftRightSigned totalMoved,
872:             LeftRightUnsigned[4] memory collectedByLeg,
873:             LeftRightSigned itmAmounts
874:         )
875:     {

/// @audit Not checked against address zero: univ3pool
1138:     function _getFeesBase(
1139:         IUniswapV3Pool univ3pool,
1140:         uint128 liquidity,
1141:         LiquidityChunk liquidityChunk,
1142:         bool roundUp
1143:     ) private view returns (LeftRightSigned feesBase) {

/// @audit Not checked against address zero: univ3pool
1185:     function _mintLiquidity(
1186:         LiquidityChunk liquidityChunk,
1187:         IUniswapV3Pool univ3pool
1188:     ) internal returns (LeftRightSigned movedAmounts) {

/// @audit Not checked against address zero: univ3pool
1224:     function _burnLiquidity(
1225:         LiquidityChunk liquidityChunk,
1226:         IUniswapV3Pool univ3pool
1227:     ) internal returns (LeftRightSigned movedAmounts) {

/// @audit Not checked against address zero: univ3pool
1255:     function _collectAndWritePositionData(
1256:         LiquidityChunk liquidityChunk,
1257:         IUniswapV3Pool univ3pool,
1258:         LeftRightUnsigned currentLiquidity,
1259:         bytes32 positionKey,
1260:         LeftRightSigned movedInLeg,
1261:         uint256 isLong
1262:     ) internal returns (LeftRightUnsigned collectedChunk) {

/// @audit Not checked against address zero: univ3pool, owner
1421:     function getAccountLiquidity(
1422:         address univ3pool,
1423:         address owner,
1424:         uint256 tokenType,
1425:         int24 tickLower,
1426:         int24 tickUpper
1427:     ) external view returns (LeftRightUnsigned accountLiquidities) {

/// @audit Not checked against address zero: univ3pool, owner
1449:     function getAccountPremium(
1450:         address univ3pool,
1451:         address owner,
1452:         uint256 tokenType,
1453:         int24 tickLower,
1454:         int24 tickUpper,
1455:         int24 atTick,
1456:         uint256 isLong
1457:     ) external view returns (uint128, uint128) {

/// @audit Not checked against address zero: univ3pool, owner
1535:     function getAccountFeesBase(
1536:         address univ3pool,
1537:         address owner,
1538:         uint256 tokenType,
1539:         int24 tickLower,
1540:         int24 tickUpper
1541:     ) external view returns (int128 feesBase0, int128 feesBase1) {

/// @audit Not checked against address zero: univ3pool
1566:     function getPoolId(address univ3pool) external view returns (uint64 poolId) {

File: contracts/libraries/CallbackLib.sol

/// @audit Not checked against address zero: sender, factory
30:     function validateCallback(
31:         address sender,
32:         IUniswapV3Factory factory,
33:         PoolFeatures memory features
34:     ) internal view {
File LinkInstance CountInstance Link
CallbackLib.sol130

File: contracts/libraries/FeesCalc.sol

/// @audit Not checked against address zero: univ3pool
 97:     function calculateAMMSwapFees(
 98:         IUniswapV3Pool univ3pool,
 99:         int24 currentTick,
100:         int24 tickLower,
101:         int24 tickUpper,
102:         uint128 liquidity
103:     ) public view returns (LeftRightSigned) {

/// @audit Not checked against address zero: univ3pool
130:     function _getAMMSwapFeesPerLiquidityCollected(
131:         IUniswapV3Pool univ3pool,
132:         int24 currentTick,
133:         int24 tickLower,
134:         int24 tickUpper
135:     ) internal view returns (uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) {
File LinkInstance CountInstance Links
FeesCalc.sol297,130

File: contracts/libraries/InteractionHelper.sol

/// @audit Not checked against address zero: token0, token1
24:     function doApprovals(
25:         SemiFungiblePositionManager sfpm,
26:         CollateralTracker ct0,
27:         CollateralTracker ct1,
28:         address token0,
29:         address token1
30:     ) external {

/// @audit Not checked against address zero: token0, token1
48:     function computeName(
49:         address token0,
50:         address token1,
51:         bool isToken0,
52:         uint24 fee,
53:         string memory prefix
54:     ) external view returns (string memory) {

/// @audit Not checked against address zero: token
91:     function computeSymbol(
92:         address token,
93:         string memory prefix
94:     ) external view returns (string memory) {

/// @audit Not checked against address zero: token
107:     function computeDecimals(address token) external view returns (uint8) {
File LinkInstance CountInstance Links
InteractionHelper.sol424,48,91,107

File: contracts/libraries/PanopticMath.sol

/// @audit Not checked against address zero: univ3pool
47:     function getPoolId(address univ3pool) internal view returns (uint64) {

/// @audit Not checked against address zero: univ3pool
125:     function computeMedianObservedPrice(
126:         IUniswapV3Pool univ3pool,
127:         uint256 observationIndex,
128:         uint256 observationCardinality,
129:         uint256 cardinality,
130:         uint256 period
131:     ) external view returns (int24) {

/// @audit Not checked against address zero: univ3pool
168:     function computeInternalMedian(
169:         uint256 observationIndex,
170:         uint256 observationCardinality,
171:         uint256 period,
172:         uint256 medianData,
173:         IUniswapV3Pool univ3pool
174:     ) external view returns (int24 medianTick, uint256 updatedMedianData) {

/// @audit Not checked against address zero: univ3pool
241:     function twapFilter(IUniswapV3Pool univ3pool, uint32 twapWindow) external view returns (int24) {

/// @audit Not checked against address zero: liquidatee, collateral0, collateral1
768:     function haircutPremia(
769:         address liquidatee,
770:         TokenId[] memory positionIdList,
771:         LeftRightSigned[4][] memory premiasByLeg,
772:         LeftRightSigned collateralRemaining,
773:         CollateralTracker collateral0,
774:         CollateralTracker collateral1,
775:         uint160 sqrtPriceX96Final,
776:         mapping(bytes32 chunkKey => LeftRightUnsigned settledTokens) storage settledTokens
777:     ) external returns (int256, int256) {

/// @audit Not checked against address zero: refunder, collateral0, collateral1
917:     function getRefundAmounts(
918:         address refunder,
919:         LeftRightSigned refundValues,
920:         int24 atTick,
921:         CollateralTracker collateral0,
922:         CollateralTracker collateral1
923:     ) external view returns (LeftRightSigned) {
File LinkInstance CountInstance Links
PanopticMath.sol647,125,168,241,768,917

File: contracts/libraries/SafeTransferLib.sol

/// @audit Not checked against address zero: token, from, to
21:     function safeTransferFrom(address token, address from, address to, uint256 amount) internal {

/// @audit Not checked against address zero: token, to
52:     function safeTransfer(address token, address to, uint256 amount) internal {
File LinkInstance CountInstance Links
SafeTransferLib.sol221,52

File: contracts/tokens/ERC1155Minimal.sol

/// @audit Not checked against address zero: operator
81:     function setApprovalForAll(address operator, bool approved) public {

/// @audit Not checked against address zero: from, to
 94:     function safeTransferFrom(
 95:         address from,
 96:         address to,
 97:         uint256 id,
 98:         uint256 amount,
 99:         bytes calldata data
100:     ) public virtual {

/// @audit Not checked against address zero: from, to
130:     function safeBatchTransferFrom(
131:         address from,
132:         address to,
133:         uint256[] calldata ids,
134:         uint256[] calldata amounts,
135:         bytes calldata data
136:     ) public virtual {

/// @audit Not checked against address zero: to
214:     function _mint(address to, uint256 id, uint256 amount) internal {

/// @audit Not checked against address zero: from
236:     function _burn(address from, uint256 id, uint256 amount) internal {
File LinkInstance CountInstance Links
ERC1155Minimal.sol581,94,130,214,236

File: contracts/tokens/ERC20Minimal.sol

/// @audit Not checked against address zero: spender
49:     function approve(address spender, uint256 amount) public returns (bool) {

/// @audit Not checked against address zero: to
61:     function transfer(address to, uint256 amount) public virtual returns (bool) {

/// @audit Not checked against address zero: from, to
81:     function transferFrom(address from, address to, uint256 amount) public virtual returns (bool) {

/// @audit Not checked against address zero: from, to
103:     function _transferFrom(address from, address to, uint256 amount) internal {

/// @audit Not checked against address zero: to
122:     function _mint(address to, uint256 amount) internal {

/// @audit Not checked against address zero: from
136:     function _burn(address from, uint256 amount) internal {
File LinkInstance CountInstance Links
ERC20Minimal.sol649,61,81,103,122,136

</details>
<a id="l-14"></a> [L-14] Some tokens may not consider type(uint256).max as an infinite approval
Description:

Some tokens such as COMP downcast approval amounts of type(uint256).max to uint96 and use that as a the amount rather than interpreting it as an infinite approval. Eventually these approvals will reach zero, at which point the calling contract will no longer function properly.

Instances:

There are 4 instances of this issue.

File: contracts/libraries/InteractionHelper.sol

32:         IERC20Partial(token0).approve(address(sfpm), type(uint256).max);

33:         IERC20Partial(token1).approve(address(sfpm), type(uint256).max);

36:         IERC20Partial(token0).approve(address(ct0), type(uint256).max);

37:         IERC20Partial(token1).approve(address(ct1), type(uint256).max);
File LinkInstance CountInstance Links
InteractionHelper.sol432,33,36,37

<a id="l-15"></a> [L-15] Unsafe addition/multiplication in unchecked block
Description:

These additions/multiplications may silently overflow because they are in unchecked blocks with no preceding value checks, which may lead to unexpected results.

Instances:

There are 177 instances of this issue.

<details><summary>View 177 Instances</summary>
File: contracts/CollateralTracker.sol

/// @audit unchecked block starts at line 197
202:                 2230 +
203:                     (12500 * ratioTick) /
204:                     10_000 +
205:                     (7812 * ratioTick ** 2) /
206:                     10_000 ** 2 +
207:                     (6510 * ratioTick ** 3) /
208:                     10_000 ** 3

/// @audit unchecked block starts at line 197
203:                     (12500 * ratioTick) /

/// @audit unchecked block starts at line 197
205:                     (7812 * ratioTick ** 2) /

/// @audit unchecked block starts at line 197
207:                     (6510 * ratioTick ** 3) /

/// @audit unchecked block starts at line 261
262:             s_ITMSpreadFee = uint128((ITM_SPREAD_MULTIPLIER * _poolFee) / DECIMALS);

/// @audit unchecked block starts at line 371
372:             return s_poolAssets + s_inAMM;

/// @audit unchecked block starts at line 401
403:                 assets * (DECIMALS - COMMISSION_FEE),

/// @audit unchecked block starts at line 401
405:                 totalAssets() * DECIMALS

/// @audit unchecked block starts at line 445
446:             return (convertToShares(type(uint104).max) * DECIMALS) / (DECIMALS + COMMISSION_FEE);

/// @audit unchecked block starts at line 461
463:                 shares * DECIMALS,

/// @audit unchecked block starts at line 461
465:                 totalSupply * (DECIMALS - COMMISSION_FEE)

/// @audit unchecked block starts at line 661
670:                                 uint24(positionId.width(leg) * positionId.tickSpacing()),

/// @audit unchecked block starts at line 661
731:                 .toRightSlot(int128((longAmounts.rightSlot() * fee) / DECIMALS_128))

/// @audit unchecked block starts at line 661
732:                 .toLeftSlot(int128((longAmounts.leftSlot() * fee) / DECIMALS_128));

/// @audit unchecked block starts at line 742
743:             return int256((s_inAMM * DECIMALS) / totalAssets());

/// @audit unchecked block starts at line 794
796:                 min_sell_ratio +
797:                 ((DECIMALS - min_sell_ratio) * (uint256(utilization) - TARGET_POOL_UTIL)) /
798:                 (SATURATED_POOL_UTIL - TARGET_POOL_UTIL);

/// @audit unchecked block starts at line 794
797:                 ((DECIMALS - min_sell_ratio) * (uint256(utilization) - TARGET_POOL_UTIL)) /

/// @audit unchecked block starts at line 847
849:                 (BUYER_COLLATERAL_RATIO +
850:                     (BUYER_COLLATERAL_RATIO * (SATURATED_POOL_UTIL - utilization)) /
851:                     (SATURATED_POOL_UTIL - TARGET_POOL_UTIL)) / 2; // do the division by 2 at the end after all addition and multiplication; b/c y1 = buyCollateralRatio / 2

/// @audit unchecked block starts at line 847
850:                     (BUYER_COLLATERAL_RATIO * (SATURATED_POOL_UTIL - utilization)) /

/// @audit unchecked block starts at line 1001
1029:             s_inAMM = uint128(uint256(int256(uint256(s_inAMM)) + (shortAmount - longAmount)));

/// @audit unchecked block starts at line 1050
1084:             s_poolAssets = uint128(uint256(updatedAssets + realizedPremium));

/// @audit unchecked block starts at line 1103
1110:                     s_ITMSpreadFee * uint256(Math.abs(intrinsicValue)),

/// @audit unchecked block starts at line 1103
1115:                 exchangedAmount = intrinsicValue + int256(swapCommission);

/// @audit unchecked block starts at line 1103
1121:                     uint256(uint128(shortAmount + longAmount)) * COMMISSION_FEE,

/// @audit unchecked block starts at line 1338
1364:                             Math.max24(2 * (atTick - strike), Constants.MIN_V3POOL_TICK)

/// @audit unchecked block starts at line 1338
1367:                             Math.max24(2 * (strike - atTick), Constants.MIN_V3POOL_TICK)

/// @audit unchecked block starts at line 1338
1409:                             (tickUpper - strike) + (strike - tickLower)

/// @audit unchecked block starts at line 1338
1414:                             scaleFactor + Constants.FP96

/// @audit unchecked block starts at line 1485
1486:                 required = Math.unsafeDivRoundingUp(amount * sellCollateral, DECIMALS);

/// @audit unchecked block starts at line 1495
1496:                 required = Math.unsafeDivRoundingUp(amount * buyCollateral, DECIMALS);

/// @audit unchecked block starts at line 1555
1571:                     ? Math.unsafeDivRoundingUp((notionalP - notional) * contracts, notional)

/// @audit unchecked block starts at line 1555
1572:                     : Math.unsafeDivRoundingUp((notional - notionalP) * contracts, notionalP);

/// @audit unchecked block starts at line 1627
1637:                 uint128(uint64(-int64(poolUtilization0 == 0 ? 1 : poolUtilization0))) +
1638:                 (uint128(uint64(-int64(poolUtilization1 == 0 ? 1 : poolUtilization1))) << 64);

File: contracts/PanopticFactory.sol

/// @audit unchecked block starts at line 299
300:             maxSalt = uint256(salt) + loops;

/// @audit unchecked block starts at line 321
323:                 salt = bytes32(uint256(salt) + 1);

/// @audit unchecked block starts at line 390
392:             tickLower = (Constants.MIN_V3POOL_TICK / tickSpacing) * tickSpacing;
File LinkInstance CountInstance Links
PanopticFactory.sol3300,323,392

File: contracts/PanopticPool.sol

/// @audit unchecked block starts at line 307
309:                 (uint256(block.timestamp) << 216) +
310:                 // magic number which adds (7,5,3,1,0,2,4,6) order and minTick in positions 7, 5, 3 and maxTick in 6, 4, 2
311:                 // see comment on s_miniMedian initialization for format of this magic number
312:                 (uint256(0xF590A6F276170D89E9F276170D89E9F276170D89E9000000000000)) +
313:                 (uint256(uint24(currentTick)) << 24) + // add to slot 4
314:                 (uint256(uint24(currentTick))); // add to slot 3

/// @audit unchecked block starts at line 729
730:             return uint128(uint256(utilization0) + uint128(uint256(utilization1) << 64));

/// @audit unchecked block starts at line 1328
1329:             return balanceCross >= Math.unsafeDivRoundingUp(thresholdCross * buffer, 10_000);

/// @audit unchecked block starts at line 1344
1348:                 Math.mulDiv(uint256(tokenData1.rightSlot()), 2 ** 96, sqrtPriceX96) +
1349:                 Math.mulDiv96(tokenData0.rightSlot(), sqrtPriceX96);

/// @audit unchecked block starts at line 1344
1353:                 Math.mulDivRoundingUp(uint256(tokenData1.leftSlot()), 2 ** 96, sqrtPriceX96) +
1354:                 Math.mulDiv96RoundingUp(tokenData0.leftSlot(), sqrtPriceX96);

/// @audit unchecked block starts at line 1486
1487:             effectiveLiquidityFactorX32 = (uint256(totalLiquidity) * 2 ** 32) / netLiquidity;

/// @audit unchecked block starts at line 1539
1550:                                     ((premiumAccumulatorsByLeg[leg][0] -
1551:                                         premiumAccumulatorLast.rightSlot()) *
1552:                                         (liquidityChunk.liquidity())) / 2 ** 64

/// @audit unchecked block starts at line 1539
1559:                                     ((premiumAccumulatorsByLeg[leg][1] -
1560:                                         premiumAccumulatorLast.leftSlot()) *
1561:                                         (liquidityChunk.liquidity())) / 2 ** 64

/// @audit unchecked block starts at line 1631
1635:                 .toRightSlot(int128(int256((accumulatedPremium.rightSlot() * liquidity) / 2 ** 64)))

/// @audit unchecked block starts at line 1631
1636:                 .toLeftSlot(int128(int256((accumulatedPremium.leftSlot() * liquidity) / 2 ** 64)));

/// @audit unchecked block starts at line 1717
1729:                                 (grossCurrent[0] *
1730:                                     positionLiquidity +
1731:                                     grossPremiumLast.rightSlot() *
1732:                                     totalLiquidityBefore) / (totalLiquidity)

/// @audit unchecked block starts at line 1717
1731:                                     grossPremiumLast.rightSlot() *
1732:                                     totalLiquidityBefore) / (totalLiquidity)

/// @audit unchecked block starts at line 1717
1737:                                 (grossCurrent[1] *
1738:                                     positionLiquidity +
1739:                                     grossPremiumLast.leftSlot() *
1740:                                     totalLiquidityBefore) / (totalLiquidity)

/// @audit unchecked block starts at line 1717
1739:                                     grossPremiumLast.leftSlot() *
1740:                                     totalLiquidityBefore) / (totalLiquidity)

/// @audit unchecked block starts at line 1764
1768:             uint256 accumulated0 = ((premiumAccumulators[0] - grossPremiumLast.rightSlot()) *
1769:                 totalLiquidity) / 2 ** 64;

/// @audit unchecked block starts at line 1764
1770:             uint256 accumulated1 = ((premiumAccumulators[1] - grossPremiumLast.leftSlot()) *
1771:                 totalLiquidity) / 2 ** 64;

/// @audit unchecked block starts at line 1764
1779:                                 (uint256(premiumOwed.rightSlot()) * settledTokens.rightSlot()) /

/// @audit unchecked block starts at line 1764
1788:                                 (uint256(premiumOwed.leftSlot()) * settledTokens.leftSlot()) /

/// @audit unchecked block starts at line 1806
1820:             totalLiquidity = accountLiquidities.rightSlot() + accountLiquidities.leftSlot();

/// @audit unchecked block starts at line 1922
1935:                                                 (int256(
1936:                                                     grossPremiumLast.rightSlot() *
1937:                                                         totalLiquidityBefore
1938:                                                 ) -
1939:                                                     int256(
1940:                                                         _premiumAccumulatorsByLeg[_leg][0] *
1941:                                                             positionLiquidity
1942:                                                     )) + int256(legPremia.rightSlot() * 2 ** 64),

/// @audit unchecked block starts at line 1922
1936:                                                     grossPremiumLast.rightSlot() *
1937:                                                         totalLiquidityBefore

/// @audit unchecked block starts at line 1922
1940:                                                         _premiumAccumulatorsByLeg[_leg][0] *
1941:                                                             positionLiquidity

/// @audit unchecked block starts at line 1922
1942:                                                     )) + int256(legPremia.rightSlot() * 2 ** 64),

/// @audit unchecked block starts at line 1922
1952:                                                 (int256(
1953:                                                     grossPremiumLast.leftSlot() *
1954:                                                         totalLiquidityBefore
1955:                                                 ) -
1956:                                                     int256(
1957:                                                         _premiumAccumulatorsByLeg[_leg][1] *
1958:                                                             positionLiquidity
1959:                                                     )) + int256(legPremia.leftSlot()) * 2 ** 64,

/// @audit unchecked block starts at line 1922
1953:                                                     grossPremiumLast.leftSlot() *
1954:                                                         totalLiquidityBefore

/// @audit unchecked block starts at line 1922
1957:                                                         _premiumAccumulatorsByLeg[_leg][1] *
1958:                                                             positionLiquidity

/// @audit unchecked block starts at line 1922
1959:                                                     )) + int256(legPremia.leftSlot()) * 2 ** 64,

File: contracts/SemiFungiblePositionManager.sol

/// @audit unchecked block starts at line 387
388:             s_AddrToPoolIdData[univ3pool] = uint256(poolId) + 2 ** 255;

/// @audit unchecked block starts at line 1339
1340:             uint256 totalLiquidity = netLiquidity + removedLiquidity;

/// @audit unchecked block starts at line 1339
1352:                     totalLiquidity * 2 ** 64,

/// @audit unchecked block starts at line 1339
1357:                     totalLiquidity * 2 ** 64,

/// @audit unchecked block starts at line 1339
1367:                     uint256 numerator = netLiquidity + (removedLiquidity / 2 ** VEGOID);

/// @audit unchecked block starts at line 1339
1388:                     uint256 numerator = totalLiquidity ** 2 -
1389:                         totalLiquidity *
1390:                         removedLiquidity +
1391:                         ((removedLiquidity ** 2) / 2 ** (VEGOID));

/// @audit unchecked block starts at line 1339
1389:                         totalLiquidity *
1390:                         removedLiquidity +
File LinkInstance CountInstance Links
SemiFungiblePositionManager.sol7388,1340,1352,1357,1367,1388,1389

File: contracts/libraries/Math.sol

/// @audit unchecked block starts at line 129
137:             if (absTick & 0x2 != 0) sqrtR = (sqrtR * 0xfff97272373d413259a46990580e213a) >> 128;

/// @audit unchecked block starts at line 129
139:             if (absTick & 0x4 != 0) sqrtR = (sqrtR * 0xfff2e50f5f656932ef12357cf3c7fdcc) >> 128;

/// @audit unchecked block starts at line 129
141:             if (absTick & 0x8 != 0) sqrtR = (sqrtR * 0xffe5caca7e10e4e61c3624eaa0941cd0) >> 128;

/// @audit unchecked block starts at line 129
143:             if (absTick & 0x10 != 0) sqrtR = (sqrtR * 0xffcb9843d60f6159c9db58835c926644) >> 128;

/// @audit unchecked block starts at line 129
145:             if (absTick & 0x20 != 0) sqrtR = (sqrtR * 0xff973b41fa98c081472e6896dfb254c0) >> 128;

/// @audit unchecked block starts at line 129
147:             if (absTick & 0x40 != 0) sqrtR = (sqrtR * 0xff2ea16466c96a3843ec78b326b52861) >> 128;

/// @audit unchecked block starts at line 129
149:             if (absTick & 0x80 != 0) sqrtR = (sqrtR * 0xfe5dee046a99a2a811c461f1969c3053) >> 128;

/// @audit unchecked block starts at line 129
151:             if (absTick & 0x100 != 0) sqrtR = (sqrtR * 0xfcbe86c7900a88aedcffc83b479aa3a4) >> 128;

/// @audit unchecked block starts at line 129
153:             if (absTick & 0x200 != 0) sqrtR = (sqrtR * 0xf987a7253ac413176f2b074cf7815e54) >> 128;

/// @audit unchecked block starts at line 129
155:             if (absTick & 0x400 != 0) sqrtR = (sqrtR * 0xf3392b0822b70005940c7a398e4b70f3) >> 128;

/// @audit unchecked block starts at line 129
157:             if (absTick & 0x800 != 0) sqrtR = (sqrtR * 0xe7159475a2c29b7443b29c7fa6e889d9) >> 128;

/// @audit unchecked block starts at line 129
159:             if (absTick & 0x1000 != 0) sqrtR = (sqrtR * 0xd097f3bdfd2022b8845ad8f792aa5825) >> 128;

/// @audit unchecked block starts at line 129
161:             if (absTick & 0x2000 != 0) sqrtR = (sqrtR * 0xa9f746462d870fdf8a65dc1f90e061e5) >> 128;

/// @audit unchecked block starts at line 129
163:             if (absTick & 0x4000 != 0) sqrtR = (sqrtR * 0x70d869a156d2a1b890bb3df62baf32f7) >> 128;

/// @audit unchecked block starts at line 129
165:             if (absTick & 0x8000 != 0) sqrtR = (sqrtR * 0x31be135f97d08fd981231505542fcfa6) >> 128;

/// @audit unchecked block starts at line 129
167:             if (absTick & 0x10000 != 0) sqrtR = (sqrtR * 0x9aa508b5b7a84e1c677de54f3e99bc9) >> 128;

/// @audit unchecked block starts at line 129
169:             if (absTick & 0x20000 != 0) sqrtR = (sqrtR * 0x5d6af8dedb81196699c329225ee604) >> 128;

/// @audit unchecked block starts at line 129
171:             if (absTick & 0x40000 != 0) sqrtR = (sqrtR * 0x2216e584f5fa1ea926041bedfe98) >> 128;

/// @audit unchecked block starts at line 129
173:             if (absTick & 0x80000 != 0) sqrtR = (sqrtR * 0x48a170391f7dc42444e8fa2) >> 128;

/// @audit unchecked block starts at line 129
179:             return uint160((sqrtR >> 32) + (sqrtR % (1 << 32) == 0 ? 0 : 1));

/// @audit unchecked block starts at line 345
407:             prod0 |= prod1 * twos;

/// @audit unchecked block starts at line 345
414:             uint256 inv = (3 * denominator) ^ 2;

/// @audit unchecked block starts at line 345
418:             inv *= 2 - denominator * inv; // inverse mod 2**8

/// @audit unchecked block starts at line 345
419:             inv *= 2 - denominator * inv; // inverse mod 2**16

/// @audit unchecked block starts at line 345
420:             inv *= 2 - denominator * inv; // inverse mod 2**32

/// @audit unchecked block starts at line 345
421:             inv *= 2 - denominator * inv; // inverse mod 2**64

/// @audit unchecked block starts at line 345
422:             inv *= 2 - denominator * inv; // inverse mod 2**128

/// @audit unchecked block starts at line 345
423:             inv *= 2 - denominator * inv; // inverse mod 2**256

/// @audit unchecked block starts at line 345
431:             result = prod0 * inv;

/// @audit unchecked block starts at line 459
511:             prod0 |= prod1 * 2 ** 192;

/// @audit unchecked block starts at line 522
574:             prod0 |= prod1 * 2 ** 160;

/// @audit unchecked block starts at line 676
728:             prod0 |= prod1 * 2 ** 64;

/// @audit unchecked block starts at line 754
758:             int256 pivot = arr[uint256(left + (right - left) / 2)];

File: contracts/libraries/PanopticMath.sol

/// @audit unchecked block starts at line 60
62:             return (poolId & TICKSPACING_MASK) + (uint48(poolId) + 1);

/// @audit unchecked block starts at line 100
107:                     ? uint256(updatedHash) + (((existingHash >> 248) + 1) << 248)

/// @audit unchecked block starts at line 100
108:                     : uint256(updatedHash) + (((existingHash >> 248) - 1) << 248);

/// @audit unchecked block starts at line 132
133:             int256[] memory tickCumulatives = new int256[](cardinality + 1);

/// @audit unchecked block starts at line 132
135:             uint256[] memory timestamps = new uint256[](cardinality + 1);

/// @audit unchecked block starts at line 132
137:             for (uint256 i = 0; i < cardinality + 1; ++i) {

/// @audit unchecked block starts at line 132
140:                         (int256(observationIndex) - int256(i * period)) +
141:                             int256(observationCardinality)

/// @audit unchecked block starts at line 132
150:                     (tickCumulatives[i] - tickCumulatives[i + 1]) /

/// @audit unchecked block starts at line 132
151:                     int256(timestamps[i] - timestamps[i + 1]);

/// @audit unchecked block starts at line 175
178:                 (int24(uint24(medianData >> ((uint24(medianData >> (192 + 3 * 3)) % 8) * 24))) +
179:                     int24(uint24(medianData >> ((uint24(medianData >> (192 + 3 * 4)) % 8) * 24)))) /

/// @audit unchecked block starts at line 175
179:                     int24(uint24(medianData >> ((uint24(medianData >> (192 + 3 * 4)) % 8) * 24)))) /

/// @audit unchecked block starts at line 175
183:             if (block.timestamp >= uint256(uint40(medianData >> 216)) + period) {

/// @audit unchecked block starts at line 175
188:                             int256(observationIndex) - int256(1) + int256(observationCardinality)

/// @audit unchecked block starts at line 175
209:                     rank = (orderMap >> (3 * i)) % 8;

/// @audit unchecked block starts at line 175
217:                     entry = int24(uint24(medianData >> (rank * 24)));

/// @audit unchecked block starts at line 175
223:                     newOrderMap = newOrderMap + ((rank + 1) << (3 * (i + shift - 1)));

/// @audit unchecked block starts at line 175
227:                     (block.timestamp << 216) +
228:                     (uint256(newOrderMap) << 192) +
229:                     uint256(uint192(medianData << 24)) +
230:                     uint256(uint24(lastObservedTick));

/// @audit unchecked block starts at line 246
249:                 secondsAgos[i] = uint32(((i + 1) * twapWindow) / 20);

/// @audit unchecked block starts at line 246
258:                     (tickCumulatives[i] - tickCumulatives[i + 1]) / int56(uint56(twapWindow / 20))

/// @audit unchecked block starts at line 343
346:             int24 minTick = (Constants.MIN_V3POOL_TICK / tickSpacing) * tickSpacing;

/// @audit unchecked block starts at line 343
347:             int24 maxTick = (Constants.MAX_V3POOL_TICK / tickSpacing) * tickSpacing;

/// @audit unchecked block starts at line 343
351:             (tickLower, tickUpper) = (strike - rangeDown, strike + rangeUp);

/// @audit unchecked block starts at line 474
476:                 ? convert0to1(contractSize, Math.getSqrtRatioAtTick((tickUpper + tickLower) / 2))

/// @audit unchecked block starts at line 474
477:                 : convert1to0(contractSize, Math.getSqrtRatioAtTick((tickUpper + tickLower) / 2));

/// @audit unchecked block starts at line 659
669:                 uint256 requiredRatioX128 = (required0 << 128) / (required0 + required1);

/// @audit unchecked block starts at line 659
698:             int256 paid0 = bonus0 + int256(netExchanged.rightSlot());

/// @audit unchecked block starts at line 659
699:             int256 paid1 = bonus1 + int256(netExchanged.leftSlot());

/// @audit unchecked block starts at line 659
744:             paid0 = bonus0 + int256(netExchanged.rightSlot());

/// @audit unchecked block starts at line 659
745:             paid1 = bonus1 + int256(netExchanged.leftSlot());

/// @audit unchecked block starts at line 778
820:                 haircut1 = protocolLoss1 + collateralDelta1;

/// @audit unchecked block starts at line 778
843:                 haircut0 = collateralDelta0 + protocolLoss0;

/// @audit unchecked block starts at line 778
870:                             uint128(-_premiasByLeg[i][leg].rightSlot()) * uint256(haircut0),

/// @audit unchecked block starts at line 778
874:                             uint128(-_premiasByLeg[i][leg].leftSlot()) * uint256(haircut1),

/// @audit unchecked block starts at line 925
938:                                 int256(
939:                                     PanopticMath.convert0to1(uint256(balanceShortage), sqrtPriceX96)
940:                                 ) + refundValues.leftSlot()

/// @audit unchecked block starts at line 925
956:                                 int256(
957:                                     PanopticMath.convert1to0(uint256(balanceShortage), sqrtPriceX96)
958:                                 ) + refundValues.rightSlot()

File: contracts/types/LeftRight.sol

/// @audit unchecked block starts at line 63
68:                     (LeftRightUnsigned.unwrap(self) & LEFT_HALF_BIT_MASK) +
69:                         uint256(uint128(LeftRightUnsigned.unwrap(self)) + right)

/// @audit unchecked block starts at line 63
69:                         uint256(uint128(LeftRightUnsigned.unwrap(self)) + right)

/// @audit unchecked block starts at line 83
88:                     (LeftRightSigned.unwrap(self) & LEFT_HALF_BIT_MASK_INT) +
89:                         (int256(int128(LeftRightSigned.unwrap(self)) + right) & RIGHT_HALF_BIT_MASK)

/// @audit unchecked block starts at line 83
89:                         (int256(int128(LeftRightSigned.unwrap(self)) + right) & RIGHT_HALF_BIT_MASK)

/// @audit unchecked block starts at line 125
126:             return LeftRightUnsigned.wrap(LeftRightUnsigned.unwrap(self) + (uint256(left) << 128));

/// @audit unchecked block starts at line 135
136:             return LeftRightSigned.wrap(LeftRightSigned.unwrap(self) + (int256(left) << 128));

/// @audit unchecked block starts at line 152
155:             z = LeftRightUnsigned.wrap(LeftRightUnsigned.unwrap(x) + LeftRightUnsigned.unwrap(y));

/// @audit unchecked block starts at line 195
196:             int256 left = int256(uint256(x.leftSlot())) + y.leftSlot();

/// @audit unchecked block starts at line 195
201:             int256 right = int256(uint256(x.rightSlot())) + y.rightSlot();

/// @audit unchecked block starts at line 215
216:             int256 left256 = int256(x.leftSlot()) + y.leftSlot();

/// @audit unchecked block starts at line 215
219:             int256 right256 = int256(x.rightSlot()) + y.rightSlot();
File LinkInstance CountInstance Links
LeftRight.sol1168,69,88,89,126,136,155,196,201,216,219

File: contracts/types/LiquidityChunk.sol

/// @audit unchecked block starts at line 75
78:                     (uint256(uint24(_tickLower)) << 232) +
79:                         (uint256(uint24(_tickUpper)) << 208) +
80:                         uint256(amount)

/// @audit unchecked block starts at line 93
94:             return LiquidityChunk.wrap(LiquidityChunk.unwrap(self) + amount);

/// @audit unchecked block starts at line 106
109:                     LiquidityChunk.unwrap(self) + (uint256(uint24(_tickLower)) << 232)

/// @audit unchecked block starts at line 122
126:                     LiquidityChunk.unwrap(self) + ((uint256(uint24(_tickUpper))) << 208)
File LinkInstance CountInstance Links
LiquidityChunk.sol478,94,109,126

File: contracts/types/TokenId.sol

/// @audit unchecked block starts at line 109
110:             return uint256((TokenId.unwrap(self) >> (64 + legIndex * 48)) % 2);

/// @audit unchecked block starts at line 119
120:             return uint256((TokenId.unwrap(self) >> (64 + legIndex * 48 + 1)) % 128);

/// @audit unchecked block starts at line 129
130:             return uint256((TokenId.unwrap(self) >> (64 + legIndex * 48 + 8)) % 2);

/// @audit unchecked block starts at line 139
140:             return uint256((TokenId.unwrap(self) >> (64 + legIndex * 48 + 9)) % 2);

/// @audit unchecked block starts at line 149
150:             return uint256((TokenId.unwrap(self) >> (64 + legIndex * 48 + 10)) % 4);

/// @audit unchecked block starts at line 159
160:             return int24(int256(TokenId.unwrap(self) >> (64 + legIndex * 48 + 12)));

/// @audit unchecked block starts at line 170
171:             return int24(int256((TokenId.unwrap(self) >> (64 + legIndex * 48 + 36)) % 4096));

/// @audit unchecked block starts at line 184
185:             return TokenId.wrap(TokenId.unwrap(self) + _poolId);

/// @audit unchecked block starts at line 194
195:             return TokenId.wrap(TokenId.unwrap(self) + (uint256(uint24(_tickSpacing)) << 48));

/// @audit unchecked block starts at line 210
212:                 TokenId.wrap(TokenId.unwrap(self) + (uint256(_asset % 2) << (64 + legIndex * 48)));

/// @audit unchecked block starts at line 226
229:                     TokenId.unwrap(self) + (uint256(_optionRatio % 128) << (64 + legIndex * 48 + 1))

/// @audit unchecked block starts at line 245
246:             return TokenId.wrap(TokenId.unwrap(self) + ((_isLong % 2) << (64 + legIndex * 48 + 8)));

/// @audit unchecked block starts at line 260
263:                     TokenId.unwrap(self) + (uint256(_tokenType % 2) << (64 + legIndex * 48 + 9))

/// @audit unchecked block starts at line 278
281:                     TokenId.unwrap(self) + (uint256(_riskPartner % 4) << (64 + legIndex * 48 + 10))

/// @audit unchecked block starts at line 296
299:                     TokenId.unwrap(self) +
300:                         uint256((int256(_strike) & BITMASK_INT24) << (64 + legIndex * 48 + 12))

/// @audit unchecked block starts at line 296
300:                         uint256((int256(_strike) & BITMASK_INT24) << (64 + legIndex * 48 + 12))

/// @audit unchecked block starts at line 316
319:                     TokenId.unwrap(self) +
320:                         (uint256(uint24(_width) % 4096) << (64 + legIndex * 48 + 36))

/// @audit unchecked block starts at line 316
320:                         (uint256(uint24(_width) % 4096) << (64 + legIndex * 48 + 36))

/// @audit unchecked block starts at line 367
395:                         ((LONG_MASK >> (48 * (4 - optionRatios))) & CLEAR_POOLID_MASK)

/// @audit unchecked block starts at line 405
406:             return self.isLong(0) + self.isLong(1) + self.isLong(2) + self.isLong(3);

/// @audit unchecked block starts at line 504
512:                     if ((TokenId.unwrap(self) >> (64 + 48 * i)) != 0)

/// @audit unchecked block starts at line 504
520:                 for (uint256 j = i + 1; j < numLegs; ++j) {

/// @audit unchecked block starts at line 504
521:                     if (uint48(chunkData >> (48 * i)) == uint48(chunkData >> (48 * j))) {

/// @audit unchecked block starts at line 579
589:                 if ((currentTick >= _strike + rangeUp) || (currentTick < _strike - rangeDown)) {
File LinkInstance CountInstance Links
TokenId.sol24110,120,130,140,150,160,171,185,195,212,229,246,263,281,299,300,319,320,395,406,512,520,521,589

</details>
<a id="l-16"></a> [L-16] Unsafe subtraction in unchecked block
Description:

The subtraction may silently underflow because it is in an unchecked block with no preceding or subsequent value checks, which may lead to unexpected results.

Instances:

There are 82 instances of this issue.

<details><summary>View 82 Instances</summary>
File: contracts/CollateralTracker.sol

/// @audit unchecked block starts at line 197
200:             int256 ratioTick = (int256(_sellerCollateralRatio) - 2000);

/// @audit unchecked block starts at line 401
403:                 assets * (DECIMALS - COMMISSION_FEE),

/// @audit unchecked block starts at line 461
465:                 totalSupply * (DECIMALS - COMMISSION_FEE)

/// @audit unchecked block starts at line 661
677:                         uint256(Math.abs(currentTick - positionId.strike(leg)) / range)

/// @audit unchecked block starts at line 661
715:                                 int128(uint128(oracleValue0)) - int128(uint128(currentValue0))

/// @audit unchecked block starts at line 661
718:                                 int128(uint128(oracleValue1)) - int128(uint128(currentValue1))

/// @audit unchecked block starts at line 661
727:             int256 fee = (FORCE_EXERCISE_COST >> (maxNumRangesFromStrike - 1)); // exponential decay of fee based on number of half ranges away from the price

/// @audit unchecked block starts at line 794
797:                 ((DECIMALS - min_sell_ratio) * (uint256(utilization) - TARGET_POOL_UTIL)) /

/// @audit unchecked block starts at line 794
798:                 (SATURATED_POOL_UTIL - TARGET_POOL_UTIL);

/// @audit unchecked block starts at line 847
851:                     (SATURATED_POOL_UTIL - TARGET_POOL_UTIL)) / 2; // do the division by 2 at the end after all addition and multiplication; b/c y1 = buyCollateralRatio / 2

/// @audit unchecked block starts at line 1001
1003:             int256 updatedAssets = int256(uint256(s_poolAssets)) - swappedAmount;

/// @audit unchecked block starts at line 1001
1029:             s_inAMM = uint128(uint256(int256(uint256(s_inAMM)) + (shortAmount - longAmount)));

/// @audit unchecked block starts at line 1050
1052:             int256 updatedAssets = int256(uint256(s_poolAssets)) - swappedAmount;

/// @audit unchecked block starts at line 1050
1058:             int256 intrinsicValue = swappedAmount - (longAmount - shortAmount);

/// @audit unchecked block starts at line 1050
1085:             s_inAMM = uint128(uint256(int256(uint256(s_inAMM)) - (shortAmount - longAmount)));

/// @audit unchecked block starts at line 1103
1105:             int256 intrinsicValue = swappedAmount - (shortAmount - longAmount);

/// @audit unchecked block starts at line 1338
1364:                             Math.max24(2 * (atTick - strike), Constants.MIN_V3POOL_TICK)

/// @audit unchecked block starts at line 1338
1367:                             Math.max24(2 * (strike - atTick), Constants.MIN_V3POOL_TICK)

/// @audit unchecked block starts at line 1338
1397:                         uint256 c2 = Constants.FP96 - ratio;

/// @audit unchecked block starts at line 1338
1409:                             (tickUpper - strike) + (strike - tickLower)

/// @audit unchecked block starts at line 1338
1413:                             scaleFactor - ratio,

File: contracts/PanopticPool.sol

/// @audit unchecked block starts at line 623
624:             tokenId = positionIdList[positionIdList.length - 1];

/// @audit unchecked block starts at line 1250
1255:                 refundAmounts.rightSlot() - delegatedAmounts.rightSlot()

/// @audit unchecked block starts at line 1250
1260:                 refundAmounts.leftSlot() - delegatedAmounts.leftSlot()

/// @audit unchecked block starts at line 1375
1376:             pLength = positionIdList.length - offset;

/// @audit unchecked block starts at line 1539
1550:                                     ((premiumAccumulatorsByLeg[leg][0] -
1551:                                         premiumAccumulatorLast.rightSlot()) *

/// @audit unchecked block starts at line 1539
1559:                                     ((premiumAccumulatorsByLeg[leg][1] -
1560:                                         premiumAccumulatorLast.leftSlot()) *

/// @audit unchecked block starts at line 1717
1723:                     uint256 totalLiquidityBefore = totalLiquidity - positionLiquidity;

/// @audit unchecked block starts at line 1764
1768:             uint256 accumulated0 = ((premiumAccumulators[0] - grossPremiumLast.rightSlot()) *

/// @audit unchecked block starts at line 1764
1770:             uint256 accumulated1 = ((premiumAccumulators[1] - grossPremiumLast.leftSlot()) *

/// @audit unchecked block starts at line 1922
1935:                                                 (int256(
1936:                                                     grossPremiumLast.rightSlot() *
1937:                                                         totalLiquidityBefore
1938:                                                 ) -
1939:                                                     int256(
1940:                                                         _premiumAccumulatorsByLeg[_leg][0] *
1941:                                                             positionLiquidity
1942:                                                     )) + int256(legPremia.rightSlot() * 2 ** 64),

/// @audit unchecked block starts at line 1922
1952:                                                 (int256(
1953:                                                     grossPremiumLast.leftSlot() *
1954:                                                         totalLiquidityBefore
1955:                                                 ) -
1956:                                                     int256(
1957:                                                         _premiumAccumulatorsByLeg[_leg][1] *
1958:                                                             positionLiquidity
1959:                                                     )) + int256(legPremia.leftSlot()) * 2 ** 64,
File LinkInstance CountInstance Links
PanopticPool.sol11624,1255,1260,1376,1550,1559,1723,1768,1770,1935,1952

File: contracts/SemiFungiblePositionManager.sol

/// @audit unchecked block starts at line 895
899:                     _leg = _isBurn ? numLegs - leg - 1 : leg;

/// @audit unchecked block starts at line 1295
1297:                     ? receivedAmount0 - uint128(-movedInLeg.rightSlot())

/// @audit unchecked block starts at line 1295
1300:                     ? receivedAmount1 - uint128(-movedInLeg.leftSlot())

/// @audit unchecked block starts at line 1339
1388:                     uint256 numerator = totalLiquidity ** 2 -
1389:                         totalLiquidity *
1390:                         removedLiquidity +
File LinkInstance CountInstance Links
SemiFungiblePositionManager.sol4899,1297,1300,1388

File: contracts/libraries/FeesCalc.sol

/// @audit unchecked block starts at line 146
166:                 feeGrowthInside0X128 = lowerOut0 - upperOut0; // fee growth inside the chunk

/// @audit unchecked block starts at line 146
167:                 feeGrowthInside1X128 = lowerOut1 - upperOut1;

/// @audit unchecked block starts at line 146
183:                 feeGrowthInside0X128 = upperOut0 - lowerOut0;

/// @audit unchecked block starts at line 146
184:                 feeGrowthInside1X128 = upperOut1 - lowerOut1;

/// @audit unchecked block starts at line 146
204:                 feeGrowthInside0X128 = univ3pool.feeGrowthGlobal0X128() - lowerOut0 - upperOut0;

/// @audit unchecked block starts at line 146
205:                 feeGrowthInside1X128 = univ3pool.feeGrowthGlobal1X128() - lowerOut1 - upperOut1;
File LinkInstance CountInstance Links
FeesCalc.sol6166,167,183,184,204,205

File: contracts/libraries/Math.sol

/// @audit unchecked block starts at line 194
198:                     highPriceX96 - lowPriceX96,

/// @audit unchecked block starts at line 211
212:             return mulDiv96(liquidityChunk.liquidity(), highPriceX96 - lowPriceX96);

/// @audit unchecked block starts at line 249
258:                             highPriceX96 - lowPriceX96

/// @audit unchecked block starts at line 279
284:                     toUint128(mulDiv(amount1, Constants.FP96, highPriceX96 - lowPriceX96))

/// @audit unchecked block starts at line 345
418:             inv *= 2 - denominator * inv; // inverse mod 2**8

/// @audit unchecked block starts at line 345
419:             inv *= 2 - denominator * inv; // inverse mod 2**16

/// @audit unchecked block starts at line 345
420:             inv *= 2 - denominator * inv; // inverse mod 2**32

/// @audit unchecked block starts at line 345
421:             inv *= 2 - denominator * inv; // inverse mod 2**64

/// @audit unchecked block starts at line 345
422:             inv *= 2 - denominator * inv; // inverse mod 2**128

/// @audit unchecked block starts at line 345
423:             inv *= 2 - denominator * inv; // inverse mod 2**256

/// @audit unchecked block starts at line 754
758:             int256 pivot = arr[uint256(left + (right - left) / 2)];

/// @audit unchecked block starts at line 777
778:             quickSort(data, int256(0), int256(data.length - 1));
File LinkInstance CountInstance Links
Math.sol12198,212,258,284,418,419,420,421,422,423,758,778

File: contracts/libraries/PanopticMath.sol

/// @audit unchecked block starts at line 76
77:             return addr == address(0) ? 40 : 39 - Math.mostSignificantNibble(uint160(addr));

/// @audit unchecked block starts at line 100
108:                     : uint256(updatedHash) + (((existingHash >> 248) - 1) << 248);

/// @audit unchecked block starts at line 132
140:                         (int256(observationIndex) - int256(i * period)) +

/// @audit unchecked block starts at line 132
150:                     (tickCumulatives[i] - tickCumulatives[i + 1]) /

/// @audit unchecked block starts at line 132
151:                     int256(timestamps[i] - timestamps[i + 1]);

/// @audit unchecked block starts at line 175
188:                             int256(observationIndex) - int256(1) + int256(observationCardinality)

/// @audit unchecked block starts at line 175
195:                         (tickCumulative_last - tickCumulative_old) /

/// @audit unchecked block starts at line 175
196:                             int256(timestamp_last - timestamp_old)

/// @audit unchecked block starts at line 175
223:                     newOrderMap = newOrderMap + ((rank + 1) << (3 * (i + shift - 1)));

/// @audit unchecked block starts at line 246
258:                     (tickCumulatives[i] - tickCumulatives[i + 1]) / int56(uint56(twapWindow / 20))

/// @audit unchecked block starts at line 343
351:             (tickLower, tickUpper) = (strike - rangeDown, strike + rangeUp);

/// @audit unchecked block starts at line 659
678:                 uint256 bonusCross = Math.min(balanceCross / 2, thresholdCross - balanceCross);

/// @audit unchecked block starts at line 659
685:                         Math.mulDiv128(bonusCross, 2 ** 128 - requiredRatioX128),

/// @audit unchecked block starts at line 659
693:             int256 balance0 = int256(uint256(tokenData0.rightSlot())) -
694:                 Math.max(premia.rightSlot(), 0);

/// @audit unchecked block starts at line 659
695:             int256 balance1 = int256(uint256(tokenData1.rightSlot())) -
696:                 Math.max(premia.leftSlot(), 0);

/// @audit unchecked block starts at line 778
890:                             uint128(-_premiasByLeg[i][leg].rightSlot()) - settled0

/// @audit unchecked block starts at line 778
894:                             uint128(-_premiasByLeg[i][leg].leftSlot()) - settled1

/// @audit unchecked block starts at line 925
928:             int256 balanceShortage = refundValues.rightSlot() -
929:                 int256(collateral0.convertToAssets(collateral0.balanceOf(refunder)));

/// @audit unchecked block starts at line 925
935:                         .toRightSlot(int128(refundValues.rightSlot() - balanceShortage))

/// @audit unchecked block starts at line 925
946:                 refundValues.leftSlot() -
947:                 int256(collateral1.convertToAssets(collateral1.balanceOf(refunder)));

/// @audit unchecked block starts at line 925
953:                         .toLeftSlot(int128(refundValues.leftSlot() - balanceShortage))
File LinkInstance CountInstance Links
PanopticMath.sol2177,108,140,150,151,188,195,196,223,258,351,678,685,693,695,890,894,928,935,946,953

File: contracts/types/LeftRight.sol

/// @audit unchecked block starts at line 175
178:             z = LeftRightUnsigned.wrap(LeftRightUnsigned.unwrap(x) - LeftRightUnsigned.unwrap(y));

/// @audit unchecked block starts at line 233
234:             int256 left256 = int256(x.leftSlot()) - y.leftSlot();

/// @audit unchecked block starts at line 233
237:             int256 right256 = int256(x.rightSlot()) - y.rightSlot();

/// @audit unchecked block starts at line 255
256:             int256 left256 = int256(x.leftSlot()) - y.leftSlot();

/// @audit unchecked block starts at line 255
259:             int256 right256 = int256(x.rightSlot()) - y.rightSlot();
File LinkInstance CountInstance Links
LeftRight.sol5178,234,237,256,259

File: contracts/types/TokenId.sol

/// @audit unchecked block starts at line 367
395:                         ((LONG_MASK >> (48 * (4 - optionRatios))) & CLEAR_POOLID_MASK)

/// @audit unchecked block starts at line 579
589:                 if ((currentTick >= _strike + rangeUp) || (currentTick < _strike - rangeDown)) {
File LinkInstance CountInstance Links
TokenId.sol2395,589

</details>
<a id="l-17"></a> [L-17] Vulnerable versions of packages are being used
Description:

This project's dependencies are vulnerable to the Common Vulnerabilities and Exposures (CVEs) listed below.

Recommendation:

Although the CVEs may involve code not in use by this project, consider switching to more recent versions of these packages that do not have these vulnerabilities, to avoid trying to determine whether there is vulnerable code from these packages in use.

Instances:

There are 3 instances of this issue.

  1. CVE-2023-34234 - Severity: <span style="color:yellow">Medium</span> - OpenZeppelin Contracts is a library for smart contract development. By frontrunning the creation of a proposal, an attacker can become the proposer and gain the ability to cancel it. The attacker can do this repeatedly to try to prevent a proposal from being proposed at all. This impacts the Governor contract in v4.9.0 only, and the GovernorCompatibilityBravo contract since v4.3.0. This problem has been patched in 4.9.1 by introducing opt-in frontrunning protection. Users are advised to upgrade. Users unable to upgrade may submit the proposal creation transaction to an endpoint with frontrunning protection as a workaround.

  2. CVE-2023-34459 - Severity: <span style="color:yellow">Medium</span> - OpenZeppelin Contracts is a library for smart contract development. Starting in version 4.7.0 and prior to version 4.9.2, when the verifyMultiProof, verifyMultiProofCalldata, procesprocessMultiProof, or processMultiProofCalldat functions are in use, it is possible to construct merkle trees that allow forging a valid multiproof for an arbitrary set of leaves. A contract may be vulnerable if it uses multiproofs for verification and the merkle tree that is processed includes a node with value 0 at depth 1 (just under the root). This could happen inadvertedly for balanced trees with 3 leaves or less, if the leaves are not hashed. This could happen deliberately if a malicious tree builder includes such a node in the tree. A contract is not vulnerable if it uses single-leaf proving (verify, verifyCalldata, processProof, or processProofCalldata), or if it uses multiproofs with a known tree that has hashed leaves. Standard merkle trees produced or validated with the @openzeppelin/merkle-tree library are safe. The problem has been patched in version 4.9.2. Some workarounds are available. For those using multiproofs: When constructing merkle trees hash the leaves and do not insert empty nodes in your trees. Using the @openzeppelin/merkle-tree package eliminates this issue. Do not accept user-provided merkle roots without reconstructing at least the first level of the tree. Verify the merkle tree structure by reconstructing it from the leaves.

  3. CVE-2023-40014 - Severity: <span style="color:yellow">Medium</span> - OpenZeppelin Contracts is a library for secure smart contract development. Starting in version 4.0.0 and prior to version 4.9.3, contracts using ERC2771Context along with a custom trusted forwarder may see _msgSender return address(0) in calls that originate from the forwarder with calldata shorter than 20 bytes. This combination of circumstances does not appear to be common, in particular it is not the case for MinimalForwarder from OpenZeppelin Contracts, or any deployed forwarder the team is aware of, given that the signer address is appended to all calls that originate from these forwarders. The problem has been patched in v4.9.3.

<a id="details-non-critical-issues"></a> Non-Critical Issues

<a id="n-01"></a> [N-01] Add inline comments for unnamed variables
Description:

Unnamed function argument variables should have have an inline comment providing a name that will indicate the meaning. For example:

// Before:
function approve(address token, address)
// After:
function approve(address token, address /* spender */)
Instances:

There are 2 instances of this issue.

File: contracts/CollateralTracker.sol

/// @audit parameters: 1st
392:     function maxDeposit(address) external pure returns (uint256 maxAssets) {

/// @audit parameters: 1st
444:     function maxMint(address) external view returns (uint256 maxShares) {
File LinkInstance CountInstance Links
CollateralTracker.sol2392,444

<a id="n-02"></a> [N-02] Array parameter lengths not validated
Description:

A function has two or more arrays passed into it, but the length of the arrays are not validated to be of equal length.

Recommendation:

When two or more array parameters are passed to a function, if their lengths need to match so that the function logic executes successfully, the array lengths should be validated to confirm they are matching prior to use of the arrays.

Instances:

There are 7 instances of this issue.

<details><summary>View 7 Instances</summary>
File: contracts/PanopticPool.sol

586:     function burnOptions(
587:         TokenId[] calldata positionIdList,
588:         TokenId[] calldata newPositionIdList,
589:         int24 tickLimitLow,
590:         int24 tickLimitHigh
591:     ) external {

1017:     function liquidate(
1018:         TokenId[] calldata positionIdListLiquidator,
1019:         address liquidatee,
1020:         LeftRightUnsigned delegations,
1021:         TokenId[] calldata positionIdList
1022:     ) external {

1179:     function forceExercise(
1180:         address account,
1181:         TokenId[] calldata touchedId,
1182:         TokenId[] calldata positionIdListExercisee,
1183:         TokenId[] calldata positionIdListExercisor
1184:     ) external {
File LinkInstance CountInstance Links
PanopticPool.sol3586,1017,1179

File: contracts/SemiFungiblePositionManager.sol

566:     function safeBatchTransferFrom(
567:         address from,
568:         address to,
569:         uint256[] calldata ids,
570:         uint256[] calldata amounts,
571:         bytes calldata data
572:     ) public override {
File LinkInstance CountInstance Link
SemiFungiblePositionManager.sol1566

File: contracts/libraries/PanopticMath.sol

768:     function haircutPremia(
769:         address liquidatee,
770:         TokenId[] memory positionIdList,
771:         LeftRightSigned[4][] memory premiasByLeg,
772:         LeftRightSigned collateralRemaining,
773:         CollateralTracker collateral0,
774:         CollateralTracker collateral1,
775:         uint160 sqrtPriceX96Final,
776:         mapping(bytes32 chunkKey => LeftRightUnsigned settledTokens) storage settledTokens
777:     ) external returns (int256, int256) {
File LinkInstance CountInstance Link
PanopticMath.sol1768

File: contracts/tokens/ERC1155Minimal.sol

130:     function safeBatchTransferFrom(
131:         address from,
132:         address to,
133:         uint256[] calldata ids,
134:         uint256[] calldata amounts,
135:         bytes calldata data
136:     ) public virtual {

178:     function balanceOfBatch(
179:         address[] calldata owners,
180:         uint256[] calldata ids
181:     ) public view returns (uint256[] memory balances) {
File LinkInstance CountInstance Links
ERC1155Minimal.sol2130,178

</details>
<a id="n-03"></a> [N-03] Assembly blocks should have extensive comments
Description:

Assembly code, though generally more efficient than its Solidity equivalent, can be more difficult to understand and audit than normal Solidity code.

Recommendation:

To make code comprehension and maintenance easier, consider adding comments explaining what is being done in the assembly code block.

Instances:

There are 21 instances of this issue.

<details><summary>View 21 Instances</summary>
File: contracts/libraries/Math.sol

353:             assembly ("memory-safe") {
354:                 let mm := mulmod(a, b, not(0))
355:                 prod0 := mul(a, b)
356:                 prod1 := sub(sub(mm, prod0), lt(mm, prod0))
357:             }

362:                 assembly ("memory-safe") {
363:                     result := div(prod0, denominator)
364:                 }

379:             assembly ("memory-safe") {
380:                 remainder := mulmod(a, b, denominator)
381:             }

383:             assembly ("memory-safe") {
384:                 prod1 := sub(prod1, gt(remainder, prod0))
385:                 prod0 := sub(prod0, remainder)
386:             }

393:             assembly ("memory-safe") {
394:                 denominator := div(denominator, twos)
395:             }

398:             assembly ("memory-safe") {
399:                 prod0 := div(prod0, twos)
400:             }

404:             assembly ("memory-safe") {
405:                 twos := add(div(sub(0, twos), twos), 1)
406:             }

467:             assembly ("memory-safe") {
468:                 let mm := mulmod(a, b, not(0))
469:                 prod0 := mul(a, b)
470:                 prod1 := sub(sub(mm, prod0), lt(mm, prod0))
471:             }

493:             assembly ("memory-safe") {
494:                 remainder := mulmod(a, b, 0x10000000000000000)
495:             }

497:             assembly ("memory-safe") {
498:                 prod1 := sub(prod1, gt(remainder, prod0))
499:                 prod0 := sub(prod0, remainder)
500:             }

530:             assembly ("memory-safe") {
531:                 let mm := mulmod(a, b, not(0))
532:                 prod0 := mul(a, b)
533:                 prod1 := sub(sub(mm, prod0), lt(mm, prod0))
534:             }

556:             assembly ("memory-safe") {
557:                 remainder := mulmod(a, b, 0x1000000000000000000000000)
558:             }

560:             assembly ("memory-safe") {
561:                 prod1 := sub(prod1, gt(remainder, prod0))
562:                 prod0 := sub(prod0, remainder)
563:             }

607:             assembly ("memory-safe") {
608:                 let mm := mulmod(a, b, not(0))
609:                 prod0 := mul(a, b)
610:                 prod1 := sub(sub(mm, prod0), lt(mm, prod0))
611:             }

633:             assembly ("memory-safe") {
634:                 remainder := mulmod(a, b, 0x100000000000000000000000000000000)
635:             }

637:             assembly ("memory-safe") {
638:                 prod1 := sub(prod1, gt(remainder, prod0))
639:                 prod0 := sub(prod0, remainder)
640:             }

684:             assembly ("memory-safe") {
685:                 let mm := mulmod(a, b, not(0))
686:                 prod0 := mul(a, b)
687:                 prod1 := sub(sub(mm, prod0), lt(mm, prod0))
688:             }

710:             assembly ("memory-safe") {
711:                 remainder := mulmod(a, b, 0x1000000000000000000000000000000000000000000000000)
712:             }

714:             assembly ("memory-safe") {
715:                 prod1 := sub(prod1, gt(remainder, prod0))
716:                 prod0 := sub(prod0, remainder)
717:             }

739:         assembly ("memory-safe") {
740:             result := add(div(a, b), gt(mod(a, b), 0))
741:         }
File LinkInstance CountInstance Links
Math.sol20353,362,379,383,393,398,404,467,493,497,530,556,560,607,633,637,684,710,714,739

File: contracts/multicall/Multicall.sol

25:                 assembly ("memory-safe") {
26:                     revert(add(result, 32), mload(result))
27:                 }
File LinkInstance CountInstance Link
Multicall.sol125

</details>
<a id="n-04"></a> [N-04] Complex arithmetic
Description:

The longer a string of operations is, the harder it is to understand it. To increase readability and maintainability, particularly in Solidity which often involves complex mathematical operations, it is recommended to limit the number of arithmetic operations to two to three per statement. Too many arithmetic operations in a single statement can make the code difficult to comprehend, increase the likelihood of mistakes, and complicate the process of debugging and reviewing the code.

Recommendation:

Consider splitting complex arithmetic operations into more steps, with more descriptive temporary variable names, and add extensive comments.

Instances:

There are 40 instances of this issue.

<details><summary>View 40 Instances</summary>
File: contracts/CollateralTracker.sol

/// @audit arithmetic operator count: 9
201:             TICK_DEVIATION = uint256(
202:                 2230 +
203:                     (12500 * ratioTick) /
204:                     10_000 +
205:                     (7812 * ratioTick ** 2) /
206:                     10_000 ** 2 +
207:                     (6510 * ratioTick ** 3) /
208:                     10_000 ** 3
209:             );

/// @audit arithmetic operator count: 4
730:             exerciseFees = exerciseFees
731:                 .toRightSlot(int128((longAmounts.rightSlot() * fee) / DECIMALS_128))
732:                 .toLeftSlot(int128((longAmounts.leftSlot() * fee) / DECIMALS_128));

/// @audit arithmetic operator count: 6
795:             return
796:                 min_sell_ratio +
797:                 ((DECIMALS - min_sell_ratio) * (uint256(utilization) - TARGET_POOL_UTIL)) /
798:                 (SATURATED_POOL_UTIL - TARGET_POOL_UTIL);

/// @audit arithmetic operator count: 6
848:             return
849:                 (BUYER_COLLATERAL_RATIO +
850:                     (BUYER_COLLATERAL_RATIO * (SATURATED_POOL_UTIL - utilization)) /
851:                     (SATURATED_POOL_UTIL - TARGET_POOL_UTIL)) / 2; // do the division by 2 at the end after all addition and multiplication; b/c y1 = buyCollateralRatio / 2

/// @audit arithmetic operator count: 4
1362:                     uint160 ratio = tokenType == 1 // tokenType
1363:                         ? Math.getSqrtRatioAtTick(
1364:                             Math.max24(2 * (atTick - strike), Constants.MIN_V3POOL_TICK)
1365:                         ) // puts ->  price/strike
1366:                         : Math.getSqrtRatioAtTick(
1367:                             Math.max24(2 * (strike - atTick), Constants.MIN_V3POOL_TICK)
1368:                         ); // calls -> strike/price

/// @audit arithmetic operator count: 4
1570:                 spreadRequirement = (notional < notionalP)
1571:                     ? Math.unsafeDivRoundingUp((notionalP - notional) * contracts, notional)
1572:                     : Math.unsafeDivRoundingUp((notional - notionalP) * contracts, notionalP);
File LinkInstance CountInstance Links
CollateralTracker.sol6201,730,795,848,1362,1570

File: contracts/PanopticPool.sol

/// @audit arithmetic operator count: 5
308:             s_miniMedian =
309:                 (uint256(block.timestamp) << 216) +
310:                 // magic number which adds (7,5,3,1,0,2,4,6) order and minTick in positions 7, 5, 3 and maxTick in 6, 4, 2
311:                 // see comment on s_miniMedian initialization for format of this magic number
312:                 (uint256(0xF590A6F276170D89E9F276170D89E9F276170D89E9000000000000)) +
313:                 (uint256(uint24(currentTick)) << 24) + // add to slot 4
314:                 (uint256(uint24(currentTick))); // add to slot 3

/// @audit arithmetic operator count: 6
1545:                     premiaByLeg[leg] = LeftRightSigned
1546:                         .wrap(0)
1547:                         .toRightSlot(
1548:                             int128(
1549:                                 int256(
1550:                                     ((premiumAccumulatorsByLeg[leg][0] -
1551:                                         premiumAccumulatorLast.rightSlot()) *
1552:                                         (liquidityChunk.liquidity())) / 2 ** 64
1553:                                 )
1554:                             )
1555:                         )
1556:                         .toLeftSlot(
1557:                             int128(
1558:                                 int256(
1559:                                     ((premiumAccumulatorsByLeg[leg][1] -
1560:                                         premiumAccumulatorLast.leftSlot()) *
1561:                                         (liquidityChunk.liquidity())) / 2 ** 64
1562:                                 )
1563:                             )
1564:                         );

/// @audit arithmetic operator count: 4
1633:             LeftRightSigned realizedPremia = LeftRightSigned
1634:                 .wrap(0)
1635:                 .toRightSlot(int128(int256((accumulatedPremium.rightSlot() * liquidity) / 2 ** 64)))
1636:                 .toLeftSlot(int128(int256((accumulatedPremium.leftSlot() * liquidity) / 2 ** 64)));

/// @audit arithmetic operator count: 8
1725:                     s_grossPremiumLast[chunkKey] = LeftRightUnsigned
1726:                         .wrap(0)
1727:                         .toRightSlot(
1728:                             uint128(
1729:                                 (grossCurrent[0] *
1730:                                     positionLiquidity +
1731:                                     grossPremiumLast.rightSlot() *
1732:                                     totalLiquidityBefore) / (totalLiquidity)
1733:                             )
1734:                         )
1735:                         .toLeftSlot(
1736:                             uint128(
1737:                                 (grossCurrent[1] *
1738:                                     positionLiquidity +
1739:                                     grossPremiumLast.leftSlot() *
1740:                                     totalLiquidityBefore) / (totalLiquidity)
1741:                             )
1742:                         );

/// @audit arithmetic operator count: 4
1773:             return (
1774:                 LeftRightUnsigned
1775:                     .wrap(0)
1776:                     .toRightSlot(
1777:                         uint128(
1778:                             Math.min(
1779:                                 (uint256(premiumOwed.rightSlot()) * settledTokens.rightSlot()) /
1780:                                     (accumulated0 == 0 ? type(uint256).max : accumulated0),
1781:                                 premiumOwed.rightSlot()
1782:                             )
1783:                         )
1784:                     )
1785:                     .toLeftSlot(
1786:                         uint128(
1787:                             Math.min(
1788:                                 (uint256(premiumOwed.leftSlot()) * settledTokens.leftSlot()) /
1789:                                     (accumulated1 == 0 ? type(uint256).max : accumulated1),
1790:                                 premiumOwed.leftSlot()
1791:                             )
1792:                         )
1793:                     )
1794:             );

/// @audit arithmetic operator count: 12
1928:                         s_grossPremiumLast[chunkKey] = totalLiquidity != 0
1929:                             ? LeftRightUnsigned
1930:                                 .wrap(0)
1931:                                 .toRightSlot(
1932:                                     uint128(
1933:                                         uint256(
1934:                                             Math.max(
1935:                                                 (int256(
1936:                                                     grossPremiumLast.rightSlot() *
1937:                                                         totalLiquidityBefore
1938:                                                 ) -
1939:                                                     int256(
1940:                                                         _premiumAccumulatorsByLeg[_leg][0] *
1941:                                                             positionLiquidity
1942:                                                     )) + int256(legPremia.rightSlot() * 2 ** 64),
1943:                                                 0
1944:                                             )
1945:                                         ) / totalLiquidity
1946:                                     )
1947:                                 )
1948:                                 .toLeftSlot(
1949:                                     uint128(
1950:                                         uint256(
1951:                                             Math.max(
1952:                                                 (int256(
1953:                                                     grossPremiumLast.leftSlot() *
1954:                                                         totalLiquidityBefore
1955:                                                 ) -
1956:                                                     int256(
1957:                                                         _premiumAccumulatorsByLeg[_leg][1] *
1958:                                                             positionLiquidity
1959:                                                     )) + int256(legPremia.leftSlot()) * 2 ** 64,
1960:                                                 0
1961:                                             )
1962:                                         ) / totalLiquidity
1963:                                     )
1964:                                 )
1965:                             : LeftRightUnsigned
1966:                                 .wrap(0)
1967:                                 .toRightSlot(uint128(premiumAccumulatorsByLeg[_leg][0]))
1968:                                 .toLeftSlot(uint128(premiumAccumulatorsByLeg[_leg][1]));
File LinkInstance CountInstance Links
PanopticPool.sol6308,1545,1633,1725,1773,1928

File: contracts/SemiFungiblePositionManager.sol

/// @audit arithmetic operator count: 4
1388:                     uint256 numerator = totalLiquidity ** 2 -
1389:                         totalLiquidity *
1390:                         removedLiquidity +
1391:                         ((removedLiquidity ** 2) / 2 ** (VEGOID));
File LinkInstance CountInstance Link
SemiFungiblePositionManager.sol11388

File: contracts/libraries/Math.sol

/// @audit arithmetic operator count: 4
179:             return uint160((sqrtR >> 32) + (sqrtR % (1 << 32) == 0 ? 0 : 1));
File LinkInstance CountInstance Link
Math.sol1179

File: contracts/libraries/PanopticMath.sol

/// @audit arithmetic operator count: 8
105:             return
106:                 addFlag
107:                     ? uint256(updatedHash) + (((existingHash >> 248) + 1) << 248)
108:                     : uint256(updatedHash) + (((existingHash >> 248) - 1) << 248);

/// @audit arithmetic operator count: 4
138:                 (timestamps[i], tickCumulatives[i], , ) = univ3pool.observations(
139:                     uint256(
140:                         (int256(observationIndex) - int256(i * period)) +
141:                             int256(observationCardinality)
142:                     ) % observationCardinality
143:                 );

/// @audit arithmetic operator count: 5
149:                 ticks[i] =
150:                     (tickCumulatives[i] - tickCumulatives[i + 1]) /
151:                     int256(timestamps[i] - timestamps[i + 1]);

/// @audit arithmetic operator count: 14
177:             medianTick =
178:                 (int24(uint24(medianData >> ((uint24(medianData >> (192 + 3 * 3)) % 8) * 24))) +
179:                     int24(uint24(medianData >> ((uint24(medianData >> (192 + 3 * 4)) % 8) * 24)))) /
180:                 2;

/// @audit arithmetic operator count: 6
223:                     newOrderMap = newOrderMap + ((rank + 1) << (3 * (i + shift - 1)));

/// @audit arithmetic operator count: 6
226:                 updatedMedianData =
227:                     (block.timestamp << 216) +
228:                     (uint256(newOrderMap) << 192) +
229:                     uint256(uint192(medianData << 24)) +
230:                     uint256(uint24(lastObservedTick));

/// @audit arithmetic operator count: 4
257:                 twapMeasurement[i] = int24(
258:                     (tickCumulatives[i] - tickCumulatives[i + 1]) / int56(uint56(twapWindow / 20))
259:                 );

/// @audit arithmetic operator count: 4
475:             uint256 notional = asset == 0
476:                 ? convert0to1(contractSize, Math.getSqrtRatioAtTick((tickUpper + tickLower) / 2))
477:                 : convert1to0(contractSize, Math.getSqrtRatioAtTick((tickUpper + tickLower) / 2));

/// @audit arithmetic operator count: 4
802:                 (collateralDelta0, collateralDelta1) = (
803:                     -Math.min(
804:                         collateralDelta0 - longPremium.rightSlot(),
805:                         PanopticMath.convert1to0(
806:                             longPremium.leftSlot() - collateralDelta1,
807:                             sqrtPriceX96Final
808:                         )
809:                     ),
810:                     Math.min(
811:                         longPremium.leftSlot() - collateralDelta1,
812:                         PanopticMath.convert0to1(
813:                             collateralDelta0 - longPremium.rightSlot(),
814:                             sqrtPriceX96Final
815:                         )
816:                     )
817:                 );

/// @audit arithmetic operator count: 4
826:                 (collateralDelta0, collateralDelta1) = (
827:                     Math.min(
828:                         longPremium.rightSlot() - collateralDelta0,
829:                         PanopticMath.convert1to0(
830:                             collateralDelta1 - longPremium.leftSlot(),
831:                             sqrtPriceX96Final
832:                         )
833:                     ),
834:                     -Math.min(
835:                         collateralDelta1 - longPremium.leftSlot(),
836:                         PanopticMath.convert0to1(
837:                             longPremium.rightSlot() - collateralDelta0,
838:                             sqrtPriceX96Final
839:                         )
840:                     )
841:                 );
File LinkInstance CountInstance Links
PanopticMath.sol10105,138,149,177,223,226,257,475,802,826

File: contracts/types/LiquidityChunk.sol

/// @audit arithmetic operator count: 4
76:             return
77:                 LiquidityChunk.wrap(
78:                     (uint256(uint24(_tickLower)) << 232) +
79:                         (uint256(uint24(_tickUpper)) << 208) +
80:                         uint256(amount)
81:                 );
File LinkInstance CountInstance Link
LiquidityChunk.sol176

File: contracts/types/TokenId.sol

/// @audit arithmetic operator count: 4
110:             return uint256((TokenId.unwrap(self) >> (64 + legIndex * 48)) % 2);

/// @audit arithmetic operator count: 5
120:             return uint256((TokenId.unwrap(self) >> (64 + legIndex * 48 + 1)) % 128);

/// @audit arithmetic operator count: 5
130:             return uint256((TokenId.unwrap(self) >> (64 + legIndex * 48 + 8)) % 2);

/// @audit arithmetic operator count: 5
140:             return uint256((TokenId.unwrap(self) >> (64 + legIndex * 48 + 9)) % 2);

/// @audit arithmetic operator count: 5
150:             return uint256((TokenId.unwrap(self) >> (64 + legIndex * 48 + 10)) % 4);

/// @audit arithmetic operator count: 4
160:             return int24(int256(TokenId.unwrap(self) >> (64 + legIndex * 48 + 12)));

/// @audit arithmetic operator count: 5
171:             return int24(int256((TokenId.unwrap(self) >> (64 + legIndex * 48 + 36)) % 4096));

/// @audit arithmetic operator count: 5
211:             return
212:                 TokenId.wrap(TokenId.unwrap(self) + (uint256(_asset % 2) << (64 + legIndex * 48)));

/// @audit arithmetic operator count: 6
227:             return
228:                 TokenId.wrap(
229:                     TokenId.unwrap(self) + (uint256(_optionRatio % 128) << (64 + legIndex * 48 + 1))
230:                 );

/// @audit arithmetic operator count: 6
246:             return TokenId.wrap(TokenId.unwrap(self) + ((_isLong % 2) << (64 + legIndex * 48 + 8)));

/// @audit arithmetic operator count: 6
261:             return
262:                 TokenId.wrap(
263:                     TokenId.unwrap(self) + (uint256(_tokenType % 2) << (64 + legIndex * 48 + 9))
264:                 );

/// @audit arithmetic operator count: 6
279:             return
280:                 TokenId.wrap(
281:                     TokenId.unwrap(self) + (uint256(_riskPartner % 4) << (64 + legIndex * 48 + 10))
282:                 );

/// @audit arithmetic operator count: 5
297:             return
298:                 TokenId.wrap(
299:                     TokenId.unwrap(self) +
300:                         uint256((int256(_strike) & BITMASK_INT24) << (64 + legIndex * 48 + 12))
301:                 );

/// @audit arithmetic operator count: 6
317:             return
318:                 TokenId.wrap(
319:                     TokenId.unwrap(self) +
320:                         (uint256(uint24(_width) % 4096) << (64 + legIndex * 48 + 36))
321:                 );

/// @audit arithmetic operator count: 4
392:             return
393:                 TokenId.wrap(
394:                     TokenId.unwrap(self) ^
395:                         ((LONG_MASK >> (48 * (4 - optionRatios))) & CLEAR_POOLID_MASK)
396:                 );
File LinkInstance CountInstance Links
TokenId.sol15110,120,130,140,150,160,171,211,227,246,261,279,297,317,392

</details>
<a id="n-05"></a> [N-05] Complex casting
Description:

Consider whether the number of casts is really necessary, or whether using a different type would be more appropriate. Alternatively, add comments to explain in detail why the casts are necessary, and any implicit reasons why the cast does not introduce an overflow.

Instances:

There are 101 instances of this issue.

<details><summary>View 101 Instances</summary>
File: contracts/CollateralTracker.sol

667:                     int24 range = int24(
668:                         int256(
669:                             Math.unsafeDivRoundingUp(
670:                                 uint24(positionId.width(leg) * positionId.tickSpacing()),
671:                                 2
672:                             )
673:                         )
674:                     );

668:                         int256(
669:                             Math.unsafeDivRoundingUp(
670:                                 uint24(positionId.width(leg) * positionId.tickSpacing()),
671:                                 2
672:                             )
673:                         )

715:                                 int128(uint128(oracleValue0)) - int128(uint128(currentValue0))

715:                                 int128(uint128(oracleValue0)) - int128(uint128(currentValue0))

718:                                 int128(uint128(oracleValue1)) - int128(uint128(currentValue1))

718:                                 int128(uint128(oracleValue1)) - int128(uint128(currentValue1))

959:                     uint256(Math.max(1, int256(totalAssets()) - int256(assets)))

1003:             int256 updatedAssets = int256(uint256(s_poolAssets)) - swappedAmount;

1028:             s_poolAssets = uint128(uint256(updatedAssets));

1029:             s_inAMM = uint128(uint256(int256(uint256(s_inAMM)) + (shortAmount - longAmount)));

1029:             s_inAMM = uint128(uint256(int256(uint256(s_inAMM)) + (shortAmount - longAmount)));

1029:             s_inAMM = uint128(uint256(int256(uint256(s_inAMM)) + (shortAmount - longAmount)));

1052:             int256 updatedAssets = int256(uint256(s_poolAssets)) - swappedAmount;

1084:             s_poolAssets = uint128(uint256(updatedAssets + realizedPremium));

1085:             s_inAMM = uint128(uint256(int256(uint256(s_inAMM)) - (shortAmount - longAmount)));

1085:             s_inAMM = uint128(uint256(int256(uint256(s_inAMM)) - (shortAmount - longAmount)));

1085:             s_inAMM = uint128(uint256(int256(uint256(s_inAMM)) - (shortAmount - longAmount)));

1119:             exchangedAmount += int256(
1120:                 Math.unsafeDivRoundingUp(
1121:                     uint256(uint128(shortAmount + longAmount)) * COMMISSION_FEE,
1122:                     DECIMALS
1123:                 )
1124:             );

1121:                     uint256(uint128(shortAmount + longAmount)) * COMMISSION_FEE,

1184:                 netBalance += uint256(uint128(premiumAllPositions));

1329:             ? int64(uint64(poolUtilization))

1330:             : int64(uint64(poolUtilization >> 64));

1585:                     ? int64(uint64(poolUtilization))

1586:                     : int64(uint64(poolUtilization >> 64))

1637:                 uint128(uint64(-int64(poolUtilization0 == 0 ? 1 : poolUtilization0))) +

1637:                 uint128(uint64(-int64(poolUtilization0 == 0 ? 1 : poolUtilization0))) +

1638:                 (uint128(uint64(-int64(poolUtilization1 == 0 ? 1 : poolUtilization1))) << 64);

1638:                 (uint128(uint64(-int64(poolUtilization1 == 0 ? 1 : poolUtilization1))) << 64);

File: contracts/PanopticPool.sol

313:                 (uint256(uint24(currentTick)) << 24) + // add to slot 4

314:                 (uint256(uint24(currentTick))); // add to slot 3

730:             return uint128(uint256(utilization0) + uint128(uint256(utilization1) << 64));

730:             return uint128(uint256(utilization0) + uint128(uint256(utilization1) << 64));

1144:             uint256(int256(uint256(_delegations.rightSlot())) + liquidationBonus0)

1144:             uint256(int256(uint256(_delegations.rightSlot())) + liquidationBonus0)

1149:             uint256(int256(uint256(_delegations.leftSlot())) + liquidationBonus1)

1149:             uint256(int256(uint256(_delegations.leftSlot())) + liquidationBonus1)

1548:                             int128(
1549:                                 int256(
1550:                                     ((premiumAccumulatorsByLeg[leg][0] -
1551:                                         premiumAccumulatorLast.rightSlot()) *
1552:                                         (liquidityChunk.liquidity())) / 2 ** 64
1553:                                 )
1554:                             )

1557:                             int128(
1558:                                 int256(
1559:                                     ((premiumAccumulatorsByLeg[leg][1] -
1560:                                         premiumAccumulatorLast.leftSlot()) *
1561:                                         (liquidityChunk.liquidity())) / 2 ** 64
1562:                                 )
1563:                             )

1635:                 .toRightSlot(int128(int256((accumulatedPremium.rightSlot() * liquidity) / 2 ** 64)))

1636:                 .toLeftSlot(int128(int256((accumulatedPremium.leftSlot() * liquidity) / 2 ** 64)));

1777:                         uint128(
1778:                             Math.min(
1779:                                 (uint256(premiumOwed.rightSlot()) * settledTokens.rightSlot()) /
1780:                                     (accumulated0 == 0 ? type(uint256).max : accumulated0),
1781:                                 premiumOwed.rightSlot()
1782:                             )
1783:                         )

1786:                         uint128(
1787:                             Math.min(
1788:                                 (uint256(premiumOwed.leftSlot()) * settledTokens.leftSlot()) /
1789:                                     (accumulated1 == 0 ? type(uint256).max : accumulated1),
1790:                                 premiumOwed.leftSlot()
1791:                             )
1792:                         )

1867:                             uint256(
1868:                                 LeftRightSigned.unwrap(
1869:                                     LeftRightSigned
1870:                                         .wrap(int256(LeftRightUnsigned.unwrap(settledTokens)))
1871:                                         .sub(legPremia)
1872:                                 )
1873:                             )

1932:                                     uint128(
1933:                                         uint256(
1934:                                             Math.max(
1935:                                                 (int256(
1936:                                                     grossPremiumLast.rightSlot() *
1937:                                                         totalLiquidityBefore
1938:                                                 ) -
1939:                                                     int256(
1940:                                                         _premiumAccumulatorsByLeg[_leg][0] *
1941:                                                             positionLiquidity
1942:                                                     )) + int256(legPremia.rightSlot() * 2 ** 64),
1943:                                                 0
1944:                                             )
1945:                                         ) / totalLiquidity
1946:                                     )

1933:                                         uint256(
1934:                                             Math.max(
1935:                                                 (int256(
1936:                                                     grossPremiumLast.rightSlot() *
1937:                                                         totalLiquidityBefore
1938:                                                 ) -
1939:                                                     int256(
1940:                                                         _premiumAccumulatorsByLeg[_leg][0] *
1941:                                                             positionLiquidity
1942:                                                     )) + int256(legPremia.rightSlot() * 2 ** 64),
1943:                                                 0
1944:                                             )
1945:                                         ) / totalLiquidity

1949:                                     uint128(
1950:                                         uint256(
1951:                                             Math.max(
1952:                                                 (int256(
1953:                                                     grossPremiumLast.leftSlot() *
1954:                                                         totalLiquidityBefore
1955:                                                 ) -
1956:                                                     int256(
1957:                                                         _premiumAccumulatorsByLeg[_leg][1] *
1958:                                                             positionLiquidity
1959:                                                     )) + int256(legPremia.leftSlot()) * 2 ** 64,
1960:                                                 0
1961:                                             )
1962:                                         ) / totalLiquidity
1963:                                     )

1950:                                         uint256(
1951:                                             Math.max(
1952:                                                 (int256(
1953:                                                     grossPremiumLast.leftSlot() *
1954:                                                         totalLiquidityBefore
1955:                                                 ) -
1956:                                                     int256(
1957:                                                         _premiumAccumulatorsByLeg[_leg][1] *
1958:                                                             positionLiquidity
1959:                                                     )) + int256(legPremia.leftSlot()) * 2 ** 64,
1960:                                                 0
1961:                                             )
1962:                                         ) / totalLiquidity

File: contracts/SemiFungiblePositionManager.sol

1169:                     int128(int256(Math.mulDiv128RoundingUp(feeGrowthInside0LastX128, liquidity)))

1172:                     int128(int256(Math.mulDiv128RoundingUp(feeGrowthInside1LastX128, liquidity)))

1176:                 .toRightSlot(int128(int256(Math.mulDiv128(feeGrowthInside0LastX128, liquidity))))

1177:                 .toLeftSlot(int128(int256(Math.mulDiv128(feeGrowthInside1LastX128, liquidity))));

1214:         movedAmounts = LeftRightSigned.wrap(0).toRightSlot(int128(int256(amount0))).toLeftSlot(

1215:             int128(int256(amount1))

1241:             movedAmounts = LeftRightSigned.wrap(0).toRightSlot(-int128(int256(amount0))).toLeftSlot(

1242:                 -int128(int256(amount1))
File LinkInstance CountInstance Links
SemiFungiblePositionManager.sol81169,1172,1176,1177,1214,1215,1241,1242

File: contracts/libraries/FeesCalc.sol

117:                 .toRightSlot(int128(int256(Math.mulDiv128(ammFeesPerLiqToken0X128, liquidity))))

118:                 .toLeftSlot(int128(int256(Math.mulDiv128(ammFeesPerLiqToken1X128, liquidity))));
File LinkInstance CountInstance Links
FeesCalc.sol2117,118

File: contracts/libraries/Math.sol

130:             uint256 absTick = tick < 0 ? uint256(-int256(tick)) : uint256(int256(tick));

130:             uint256 absTick = tick < 0 ? uint256(-int256(tick)) : uint256(int256(tick));

131:             if (absTick > uint256(int256(Constants.MAX_V3POOL_TICK))) revert Errors.InvalidTick();
File LinkInstance CountInstance Links
Math.sol3130,130,131

File: contracts/libraries/PanopticMath.sol

50:             uint64 poolId = uint64(uint160(univ3pool) >> 112);

51:             poolId += uint64(uint24(tickSpacing)) << 48;

103:                 (uint248(uint256(keccak256(abi.encode(tokenId)))));

139:                     uint256(
140:                         (int256(observationIndex) - int256(i * period)) +
141:                             int256(observationCardinality)
142:                     ) % observationCardinality

178:                 (int24(uint24(medianData >> ((uint24(medianData >> (192 + 3 * 3)) % 8) * 24))) +

178:                 (int24(uint24(medianData >> ((uint24(medianData >> (192 + 3 * 3)) % 8) * 24))) +

179:                     int24(uint24(medianData >> ((uint24(medianData >> (192 + 3 * 4)) % 8) * 24)))) /

179:                     int24(uint24(medianData >> ((uint24(medianData >> (192 + 3 * 4)) % 8) * 24)))) /

183:             if (block.timestamp >= uint256(uint40(medianData >> 216)) + period) {

187:                         uint256(
188:                             int256(observationIndex) - int256(1) + int256(observationCardinality)
189:                         ) % observationCardinality

194:                     lastObservedTick = int24(
195:                         (tickCumulative_last - tickCumulative_old) /
196:                             int256(timestamp_last - timestamp_old)
197:                     );

217:                     entry = int24(uint24(medianData >> (rank * 24)));

229:                     uint256(uint192(medianData << 24)) +

230:                     uint256(uint24(lastObservedTick));

257:                 twapMeasurement[i] = int24(
258:                     (tickCumulatives[i] - tickCumulatives[i + 1]) / int56(uint56(twapWindow / 20))
259:                 );

258:                     (tickCumulatives[i] - tickCumulatives[i + 1]) / int56(uint56(twapWindow / 20))

377:             int24(int256(Math.unsafeDivRoundingUp(uint24(width) * uint24(tickSpacing), 2)))

377:             int24(int256(Math.unsafeDivRoundingUp(uint24(width) * uint24(tickSpacing), 2)))

693:             int256 balance0 = int256(uint256(tokenData0.rightSlot())) -

695:             int256 balance1 = int256(uint256(tokenData1.rightSlot())) -

937:                             int128(
938:                                 int256(
939:                                     PanopticMath.convert0to1(uint256(balanceShortage), sqrtPriceX96)
940:                                 ) + refundValues.leftSlot()
941:                             )

938:                                 int256(
939:                                     PanopticMath.convert0to1(uint256(balanceShortage), sqrtPriceX96)
940:                                 ) + refundValues.leftSlot()

955:                             int128(
956:                                 int256(
957:                                     PanopticMath.convert1to0(uint256(balanceShortage), sqrtPriceX96)
958:                                 ) + refundValues.rightSlot()
959:                             )

956:                                 int256(
957:                                     PanopticMath.convert1to0(uint256(balanceShortage), sqrtPriceX96)
958:                                 ) + refundValues.rightSlot()
File LinkInstance CountInstance Links
PanopticMath.sol2450,51,103,139,178,178,179,179,183,187,194,217,229,230,257,258,377,377,693,695,937,938,955,956

File: contracts/types/LeftRight.sol

27:         int256(uint256(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000000000000000000000000000));

69:                         uint256(uint128(LeftRightUnsigned.unwrap(self)) + right)

89:                         (int256(int128(LeftRightSigned.unwrap(self)) + right) & RIGHT_HALF_BIT_MASK)

196:             int256 left = int256(uint256(x.leftSlot())) + y.leftSlot();

201:             int256 right = int256(uint256(x.rightSlot())) + y.rightSlot();
File LinkInstance CountInstance Links
LeftRight.sol527,69,89,196,201

File: contracts/types/LiquidityChunk.sol

78:                     (uint256(uint24(_tickLower)) << 232) +

79:                         (uint256(uint24(_tickUpper)) << 208) +

109:                     LiquidityChunk.unwrap(self) + (uint256(uint24(_tickLower)) << 232)

126:                     LiquidityChunk.unwrap(self) + ((uint256(uint24(_tickUpper))) << 208)

173:             return int24(int256(LiquidityChunk.unwrap(self) >> 232));

182:             return int24(int256(LiquidityChunk.unwrap(self) >> 208));
File LinkInstance CountInstance Links
LiquidityChunk.sol678,79,109,126,173,182

File: contracts/types/TokenId.sol

98:             return int24(uint24((TokenId.unwrap(self) >> 48) % 2 ** 16));

160:             return int24(int256(TokenId.unwrap(self) >> (64 + legIndex * 48 + 12)));

171:             return int24(int256((TokenId.unwrap(self) >> (64 + legIndex * 48 + 36)) % 4096));

195:             return TokenId.wrap(TokenId.unwrap(self) + (uint256(uint24(_tickSpacing)) << 48));

300:                         uint256((int256(_strike) & BITMASK_INT24) << (64 + legIndex * 48 + 12))

320:                         (uint256(uint24(_width) % 4096) << (64 + legIndex * 48 + 36))
File LinkInstance CountInstance Links
TokenId.sol698,160,171,195,300,320

</details>
<a id="n-06"></a> [N-06] Consider adding a block/deny-list
Description:

Adding a block/deny list increases centralization, but will help to prevent hackers from using stolen tokens.

Instances:

There are 6 instances of this issue.

<details><summary>View 6 Instances</summary>
File: contracts/CollateralTracker.sol

/// @audit ERC20 handles tokens
36: contract CollateralTracker is ERC20Minimal, Multicall {
File LinkInstance CountInstance Link
CollateralTracker.sol136

File: contracts/PanopticPool.sol

/// @audit ERC1155 handles tokens
27: contract PanopticPool is ERC1155Holder, Multicall {
File LinkInstance CountInstance Link
PanopticPool.sol127

File: contracts/SemiFungiblePositionManager.sol

/// @audit ERC1155 handles tokens
72: contract SemiFungiblePositionManager is ERC1155, Multicall {
File LinkInstance CountInstance Link
SemiFungiblePositionManager.sol172

File: contracts/tokens/ERC1155Minimal.sol

/// @audit ERC1155 handles tokens
11: abstract contract ERC1155 {
File LinkInstance CountInstance Link
ERC1155Minimal.sol111

File: contracts/tokens/ERC20Minimal.sol

/// @audit ERC20 handles tokens
9: abstract contract ERC20Minimal {
File LinkInstance CountInstance Link
ERC20Minimal.sol19

File: contracts/tokens/interfaces/IERC20Partial.sol

/// @audit ERC20 handles tokens
11: interface IERC20Partial {
File LinkInstance CountInstance Link
IERC20Partial.sol111

</details>
<a id="n-07"></a> [N-07] Consider adding formal verification proofs
Description:

Consider using formal verification to mathematically prove that your code does what is intended, and does not have any edge cases with unexpected behavior. The Solidity compiler has this functionality built in. See SMTChecker and Formal Verification for more information. Use of the SMTChecker module was not detected based on the project configuration.

Instances:

There is 1 instance of this issue.

<a id="n-08"></a> [N-08] Consider adding validation of user inputs
Description:

There are no validations done on the arguments below. Input validation helps to ensure that the function receives valid and expected inputs. Without proper validation, malicious users or even accidental errors could pass invalid data, leading to unexpected behavior or vulnerabilities in the contract.

Recommendation:

Add validation to prevent use of an invalid/undesirable value.

Instances:

There are 28 instances of this issue.

<details><summary>View 28 Instances</summary>
File: contracts/CollateralTracker.sol

/// @audit not validated: recipient, amount
323:     function transfer(
324:         address recipient,
325:         uint256 amount
326:     ) public override(ERC20Minimal) returns (bool) {

/// @audit not validated: to, amount
341:     function transferFrom(
342:         address from,
343:         address to,
344:         uint256 amount
345:     ) public override(ERC20Minimal) returns (bool) {

/// @audit not validated: receiver
417:     function deposit(uint256 assets, address receiver) external returns (uint256 shares) {

/// @audit not validated: shares, receiver
477:     function mint(uint256 shares, address receiver) external returns (uint256 assets) {

/// @audit not validated: assets, receiver, owner
531:     function withdraw(
532:         uint256 assets,
533:         address receiver,
534:         address owner
535:     ) external returns (uint256 shares) {

/// @audit not validated: shares, receiver, owner
591:     function redeem(
592:         uint256 shares,
593:         address receiver,
594:         address owner
595:     ) external returns (uint256 assets) {

/// @audit not validated: currentTick, oracleTick, positionId, positionBalance, longAmounts
650:     function exerciseCost(
651:         int24 currentTick,
652:         int24 oracleTick,
653:         TokenId positionId,
654:         uint128 positionBalance,
655:         LeftRightSigned longAmounts
656:     ) external view returns (LeftRightSigned exerciseFees) {

/// @audit not validated: delegator, delegatee, assets
911:     function revoke(
912:         address delegator,
913:         address delegatee,
914:         uint256 assets
915:     ) external onlyPanopticPool {

/// @audit not validated: refunder, refundee
975:     function refund(address refunder, address refundee, int256 assets) external onlyPanopticPool {

/// @audit not validated: optionOwner, longAmount, shortAmount, swappedAmount
 995:     function takeCommissionAddData(
 996:         address optionOwner,
 997:         int128 longAmount,
 998:         int128 shortAmount,
 999:         int128 swappedAmount
1000:     ) external onlyPanopticPool returns (int256 utilization) {

/// @audit not validated: optionOwner, longAmount, shortAmount, swappedAmount, realizedPremium
1043:     function exercise(
1044:         address optionOwner,
1045:         int128 longAmount,
1046:         int128 shortAmount,
1047:         int128 swappedAmount,
1048:         int128 realizedPremium
1049:     ) external onlyPanopticPool returns (int128) {
File LinkInstance CountInstance Links
CollateralTracker.sol11323,341,417,477,531,591,650,911,975,995,1043

File: contracts/PanopticFactory.sol

/// @audit not validated: newOwner
147:     function transferOwnership(address newOwner) external {

/// @audit not validated: amount0Owed, amount1Owed, data
172:     function uniswapV3MintCallback(
173:         uint256 amount0Owed,
174:         uint256 amount1Owed,
175:         bytes calldata data
176:     ) external {

/// @audit not validated: token0, token1, fee, salt
210:     function deployNewPool(
211:         address token0,
212:         address token1,
213:         uint24 fee,
214:         bytes32 salt
215:     ) external returns (PanopticPool newPoolContract) {

/// @audit not validated: salt, loops, minTargetRarity
290:     function minePoolAddress(
291:         bytes32 salt,
292:         uint256 loops,
293:         uint256 minTargetRarity
294:     ) external view returns (bytes32 bestSalt, uint256 highestRarity) {
File LinkInstance CountInstance Links
PanopticFactory.sol4147,172,210,290

File: contracts/PanopticPool.sol

/// @audit not validated: _univ3pool, token0, token1, collateralTracker0, collateralTracker1
291:     function startPool(
292:         IUniswapV3Pool _univ3pool,
293:         address token0,
294:         address token1,
295:         CollateralTracker collateralTracker0,
296:         CollateralTracker collateralTracker1
297:     ) external {

/// @audit not validated: sqrtLowerBound, sqrtUpperBound
338:     function assertPriceWithinBounds(uint160 sqrtLowerBound, uint160 sqrtUpperBound) external view {

/// @audit not validated: positionIdListLiquidator, liquidatee, delegations, positionIdList
1017:     function liquidate(
1018:         TokenId[] calldata positionIdListLiquidator,
1019:         address liquidatee,
1020:         LeftRightUnsigned delegations,
1021:         TokenId[] calldata positionIdList
1022:     ) external {

/// @audit not validated: account, touchedId, positionIdListExercisee, positionIdListExercisor
1179:     function forceExercise(
1180:         address account,
1181:         TokenId[] calldata touchedId,
1182:         TokenId[] calldata positionIdListExercisee,
1183:         TokenId[] calldata positionIdListExercisor
1184:     ) external {
File LinkInstance CountInstance Links
PanopticPool.sol4291,338,1017,1179

File: contracts/SemiFungiblePositionManager.sol

/// @audit not validated: token0, token1, fee
350:     function initializeAMMPool(address token0, address token1, uint24 fee) external {

/// @audit not validated: amount0Owed, amount1Owed, data
402:     function uniswapV3MintCallback(
403:         uint256 amount0Owed,
404:         uint256 amount1Owed,
405:         bytes calldata data
406:     ) external {

/// @audit not validated: amount1Delta, data
435:     function uniswapV3SwapCallback(
436:         int256 amount0Delta,
437:         int256 amount1Delta,
438:         bytes calldata data
439:     ) external {

/// @audit not validated: from, to, amounts, data
566:     function safeBatchTransferFrom(
567:         address from,
568:         address to,
569:         uint256[] calldata ids,
570:         uint256[] calldata amounts,
571:         bytes calldata data
572:     ) public override {

/// @audit not validated: univ3pool, owner, tokenType, tickLower, tickUpper, atTick, isLong
1449:     function getAccountPremium(
1450:         address univ3pool,
1451:         address owner,
1452:         uint256 tokenType,
1453:         int24 tickLower,
1454:         int24 tickUpper,
1455:         int24 atTick,
1456:         uint256 isLong
1457:     ) external view returns (uint128, uint128) {
File LinkInstance CountInstance Links
SemiFungiblePositionManager.sol5350,402,435,566,1449

File: contracts/tokens/ERC1155Minimal.sol

/// @audit not validated: from, to, id, amount, data
 94:     function safeTransferFrom(
 95:         address from,
 96:         address to,
 97:         uint256 id,
 98:         uint256 amount,
 99:         bytes calldata data
100:     ) public virtual {

/// @audit not validated: from, to, ids, amounts, data
130:     function safeBatchTransferFrom(
131:         address from,
132:         address to,
133:         uint256[] calldata ids,
134:         uint256[] calldata amounts,
135:         bytes calldata data
136:     ) public virtual {

/// @audit not validated: ids
178:     function balanceOfBatch(
179:         address[] calldata owners,
180:         uint256[] calldata ids
181:     ) public view returns (uint256[] memory balances) {
File LinkInstance CountInstance Links
ERC1155Minimal.sol394,130,178

File: contracts/tokens/ERC20Minimal.sol

/// @audit not validated: from, to, amount
81:     function transferFrom(address from, address to, uint256 amount) public virtual returns (bool) {
File LinkInstance CountInstance Link
ERC20Minimal.sol181

</details>
<a id="n-09"></a> [N-09] Consider bounding input array length
Description:

The functions below take in an unbounded array, and make function calls for entries in the array. While the function will revert if it eventually runs out of gas, it may be a better user experience to require() that the length of the array is below some reasonable maximum, so that the user does not have to use up a full transaction's gas only to see that the transaction reverts.

Instances:

There are 4 instances of this issue.

File: contracts/PanopticPool.sol

/// @audit function: _burnAllOptionsFrom(), array parameter: positionIdList[]
802:         for (uint256 i = 0; i < positionIdList.length; ) {
803:             LeftRightSigned paidAmounts;
804:             (paidAmounts, premiasByLeg[i]) = _burnOptions(
805:                 commitLongSettled,
806:                 positionIdList[i],
807:                 owner,
808:                 tickLimitLow,
809:                 tickLimitHigh
810:             );
811:             netPaid = netPaid.add(paidAmounts);
812:             unchecked {
813:                 ++i;
814:             }
815:         }
File LinkInstance CountInstance Link
PanopticPool.sol1802

File: contracts/SemiFungiblePositionManager.sol

/// @audit function: safeBatchTransferFrom(), array parameter: ids[]
575:         for (uint256 i = 0; i < ids.length; ) {
576:             if (s_poolContext[TokenId.wrap(ids[i]).poolId()].locked) revert Errors.ReentrantCall();
577:             registerTokenTransfer(from, to, TokenId.wrap(ids[i]), amounts[i]);
578:             unchecked {
579:                 ++i;
580:             }
581:         }
File LinkInstance CountInstance Link
SemiFungiblePositionManager.sol1575

File: contracts/libraries/PanopticMath.sol

/// @audit function: haircutPremia(), array parameter: positionIdList[]
860:             for (uint256 i = 0; i < positionIdList.length; i++) {
861:                 TokenId tokenId = positionIdList[i];
862:                 LeftRightSigned[4][] memory _premiasByLeg = premiasByLeg;
863:                 for (uint256 leg = 0; leg < tokenId.countLegs(); ++leg) {
864:                     if (tokenId.isLong(leg) == 1) {
865:                         mapping(bytes32 chunkKey => LeftRightUnsigned settledTokens)
866:                             storage _settledTokens = settledTokens;
867:
868:                         // calculate amounts to revoke from settled and subtract from haircut req
869:                         uint256 settled0 = Math.unsafeDivRoundingUp(
870:                             uint128(-_premiasByLeg[i][leg].rightSlot()) * uint256(haircut0),
871:                             uint128(longPremium.rightSlot())
872:                         );
873:                         uint256 settled1 = Math.unsafeDivRoundingUp(
874:                             uint128(-_premiasByLeg[i][leg].leftSlot()) * uint256(haircut1),
875:                             uint128(longPremium.leftSlot())
876:                         );
877:
878:                         bytes32 chunkKey = keccak256(
879:                             abi.encodePacked(
880:                                 tokenId.strike(0),
881:                                 tokenId.width(0),
882:                                 tokenId.tokenType(0)
883:                             )
884:                         );
885:
886:                         // The long premium is not commited to storage during the liquidation, so we add the entire adjusted amount
887:                         // for the haircut directly to the accumulator
888:                         settled0 = Math.max(
889:                             0,
890:                             uint128(-_premiasByLeg[i][leg].rightSlot()) - settled0
891:                         );
892:                         settled1 = Math.max(
893:                             0,
894:                             uint128(-_premiasByLeg[i][leg].leftSlot()) - settled1
895:                         );
896:
897:                         _settledTokens[chunkKey] = _settledTokens[chunkKey].add(
898:                             LeftRightUnsigned.wrap(0).toRightSlot(uint128(settled0)).toLeftSlot(
899:                                 uint128(settled1)
900:                             )
901:                         );
902:                     }
903:                 }
904:             }
File LinkInstance CountInstance Link
PanopticMath.sol1860

File: contracts/multicall/Multicall.sol

/// @audit function: multicall(), array parameter: data[]
14:         for (uint256 i = 0; i < data.length; ) {
15:             (bool success, bytes memory result) = address(this).delegatecall(data[i]);
16:
17:             if (!success) {
18:                 // Bubble up the revert reason
19:                 // The bytes type is ABI encoded as a length-prefixed byte array
20:                 // So we simply need to add 32 to the pointer to get the start of the data
21:                 // And then revert with the size loaded from the first 32 bytes
22:                 // Other solutions will do work to differentiate the revert reasons and provide paranthetical information
23:                 // However, we have chosen to simply replicate the the normal behavior of the call
24:                 // NOTE: memory-safe because it reads from memory already allocated by solidity (the bytes memory result)
25:                 assembly ("memory-safe") {
26:                     revert(add(result, 32), mload(result))
27:                 }
28:             }
29:
30:             results[i] = result;
31:
32:             unchecked {
33:                 ++i;
34:             }
35:         }
File LinkInstance CountInstance Link
Multicall.sol114

<a id="n-10"></a> [N-10] Consider emitting an event at the end of the constructor
Description:

Consider emitting an event when the contract is constructed to make it easier for off-chain tools to track when and by whom the contract was constructed.

Instances:

There are 4 instances of this issue.

File: contracts/CollateralTracker.sol

178:     constructor(
179:         uint256 _commissionFee,
180:         uint256 _sellerCollateralRatio,
181:         uint256 _buyerCollateralRatio,
182:         int256 _forceExerciseCost,
183:         uint256 _targetPoolUtilization,
184:         uint256 _saturatedPoolUtilization,
185:         uint256 _ITMSpreadMultiplier
186:     ) {
File LinkInstance CountInstance Link
CollateralTracker.sol1178

File: contracts/PanopticFactory.sol

115:     constructor(
116:         address _WETH9,
117:         SemiFungiblePositionManager _SFPM,
118:         IUniswapV3Factory _univ3Factory,
119:         IDonorNFT _donorNFT,
120:         address _poolReference,
121:         address _collateralReference
122:     ) {
File LinkInstance CountInstance Link
PanopticFactory.sol1115

File: contracts/PanopticPool.sol

280:     constructor(SemiFungiblePositionManager _sfpm) {
File LinkInstance CountInstance Link
PanopticPool.sol1280

File: contracts/SemiFungiblePositionManager.sol

341:     constructor(IUniswapV3Factory _factory) {
File LinkInstance CountInstance Link
SemiFungiblePositionManager.sol1341

<a id="n-11"></a> [N-11] Consider making contracts Upgradeable
Description:

Making a contract Upgradeable allows for bugs to be fixed in production, at the expense of significantly increasing centralization.

Instances:

There are 4 instances of this issue.

File: contracts/CollateralTracker.sol

36: contract CollateralTracker is ERC20Minimal, Multicall {
File LinkInstance CountInstance Link
CollateralTracker.sol136

File: contracts/PanopticFactory.sol

26: contract PanopticFactory is Multicall {
File LinkInstance CountInstance Link
PanopticFactory.sol126

File: contracts/PanopticPool.sol

27: contract PanopticPool is ERC1155Holder, Multicall {
File LinkInstance CountInstance Link
PanopticPool.sol127

File: contracts/SemiFungiblePositionManager.sol

72: contract SemiFungiblePositionManager is ERC1155, Multicall {
File LinkInstance CountInstance Link
SemiFungiblePositionManager.sol172

<a id="n-12"></a> [N-12] Consider returning a struct rather than having multiple return values
Description:

To increase readability and maintainability, it may be more efficient to return a struct rather than having multiple return values.

Instances:

There are 7 instances of this issue.

<details><summary>View 7 Instances</summary>
File: contracts/CollateralTracker.sol

277:     function getPoolData()
278:         external
279:         view
280:         returns (uint256 poolAssets, uint256 insideAMM, int256 currentPoolUtilization)
281:     {
File LinkInstance CountInstance Link
CollateralTracker.sol1277

File: contracts/PanopticPool.sol

352:     function optionPositionBalance(
353:         address user,
354:         TokenId tokenId
355:     ) external view returns (uint128 balance, uint64 poolUtilization0, uint64 poolUtilization1) {

381:     function calculateAccumulatedFeesBatch(
382:         address user,
383:         bool includePendingPremium,
384:         TokenId[] calldata positionIdList
385:     ) external view returns (int128 premium0, int128 premium1, uint256[2][] memory) {

955:     function _burnAndHandleExercise(
956:         bool commitLongSettled,
957:         int24 tickLimitLow,
958:         int24 tickLimitHigh,
959:         TokenId tokenId,
960:         uint128 positionSize,
961:         address owner
962:     )
963:         internal
964:         returns (
965:             LeftRightSigned realizedPremia,
966:             LeftRightSigned[4] memory premiaByLeg,
967:             LeftRightSigned paidAmounts
968:         )
969:     {
File LinkInstance CountInstance Links
PanopticPool.sol3352,381,955

File: contracts/SemiFungiblePositionManager.sol

863:     function _createPositionInAMM(
864:         IUniswapV3Pool univ3pool,
865:         TokenId tokenId,
866:         uint128 positionSize,
867:         bool isBurn
868:     )
869:         internal
870:         returns (
871:             LeftRightSigned totalMoved,
872:             LeftRightUnsigned[4] memory collectedByLeg,
873:             LeftRightSigned itmAmounts
874:         )
875:     {

958:     function _createLegInAMM(
959:         IUniswapV3Pool univ3pool,
960:         TokenId tokenId,
961:         uint256 leg,
962:         LiquidityChunk liquidityChunk,
963:         bool isBurn
964:     )
965:         internal
966:         returns (
967:             LeftRightSigned moved,
968:             LeftRightSigned itmAmounts,
969:             LeftRightUnsigned collectedSingleLeg
970:         )
971:     {
File LinkInstance CountInstance Links
SemiFungiblePositionManager.sol2863,958

File: contracts/libraries/PanopticMath.sol

651:     function getLiquidationBonus(
652:         LeftRightUnsigned tokenData0,
653:         LeftRightUnsigned tokenData1,
654:         uint160 sqrtPriceX96Twap,
655:         uint160 sqrtPriceX96Final,
656:         LeftRightSigned netExchanged,
657:         LeftRightSigned premia
658:     ) external pure returns (int256 bonus0, int256 bonus1, LeftRightSigned) {
File LinkInstance CountInstance Link
PanopticMath.sol1651

</details>
<a id="n-13"></a> [N-13] Consider splitting complex checks into multiple steps
Description:

The longer a string of operations is, the harder it is to understand it. To increase readability and maintainability, particularly in Solidity which often involves complex mathematical operations, it is recommended to limit the number of comparison operations to two to three per statement. Too many comparison operations in a single statement can make the code difficult to comprehend, increase the likelihood of mistakes, and complicate the process of debugging and reviewing the code.

Instances:

There are 21 instances of this issue.

<details><summary>View 21 Instances</summary>
File: contracts/CollateralTracker.sol

/// @audit comparison operator count: 9
662:             for (uint256 leg = 0; leg < positionId.countLegs(); ++leg) {
663:                 // short legs are not counted - exercise is intended to be based on long legs
664:                 if (positionId.isLong(leg) == 0) continue;
665:
666:                 {
667:                     int24 range = int24(
668:                         int256(
669:                             Math.unsafeDivRoundingUp(
670:                                 uint24(positionId.width(leg) * positionId.tickSpacing()),
671:                                 2
672:                             )
673:                         )
674:                     );
675:                     maxNumRangesFromStrike = Math.max(
676:                         maxNumRangesFromStrike,
677:                         uint256(Math.abs(currentTick - positionId.strike(leg)) / range)
678:                     );
679:                 }
680:
681:                 uint256 currentValue0;
682:                 uint256 currentValue1;
683:                 uint256 oracleValue0;
684:                 uint256 oracleValue1;
685:
686:                 {
687:                     LiquidityChunk liquidityChunk = PanopticMath.getLiquidityChunk(
688:                         positionId,
689:                         leg,
690:                         positionBalance
691:                     );
692:
693:                     (currentValue0, currentValue1) = Math.getAmountsForLiquidity(
694:                         currentTick,
695:                         liquidityChunk
696:                     );
697:
698:                     (oracleValue0, oracleValue1) = Math.getAmountsForLiquidity(
699:                         oracleTick,
700:                         liquidityChunk
701:                     );
702:                 }
703:
704:                 uint256 tokenType = positionId.tokenType(leg);
705:                 // compensate user for loss in value if chunk has lost money between current and median tick
706:                 // note: the delta for one token will be positive and the other will be negative. This cancels out any moves in their positions
707:                 if (
708:                     (tokenType == 0 && currentValue1 < oracleValue1) ||
709:                     (tokenType == 1 && currentValue0 < oracleValue0)
710:                 )
711:                     exerciseFees = exerciseFees.sub(
712:                         LeftRightSigned
713:                             .wrap(0)
714:                             .toRightSlot(
715:                                 int128(uint128(oracleValue0)) - int128(uint128(currentValue0))
716:                             )
717:                             .toLeftSlot(
718:                                 int128(uint128(oracleValue1)) - int128(uint128(currentValue1))
719:                             )
720:                     );
721:             }

/// @audit comparison operator count: 7
707:                 if (
708:                     (tokenType == 0 && currentValue1 < oracleValue1) ||
709:                     (tokenType == 1 && currentValue0 < oracleValue0)
710:                 )
711:                     exerciseFees = exerciseFees.sub(
712:                         LeftRightSigned
713:                             .wrap(0)
714:                             .toRightSlot(
715:                                 int128(uint128(oracleValue0)) - int128(uint128(currentValue0))
716:                             )
717:                             .toLeftSlot(
718:                                 int128(uint128(oracleValue1)) - int128(uint128(currentValue1))
719:                             )
720:                     );

/// @audit comparison operator count: 16
1339:             if (isLong == 0) {
1340:                 // if position is short, check whether the position is out-the-money
1341:
1342:                 (int24 tickLower, int24 tickUpper) = tokenId.asTicks(index);
1343:
1344:                 // compute the collateral requirement as a fixed amount that doesn't depend on price
1345:                 if (
1346:                     ((atTick >= tickUpper) && (tokenType == 1)) || // strike OTM when price >= upperTick for tokenType=1
1347:                     ((atTick < tickLower) && (tokenType == 0)) // strike OTM when price < lowerTick for tokenType=0
1348:                 ) {
1349:                     // position is out-the-money, collateral requirement = SCR * amountMoved
1350:                     required;
1351:                 } else {
1352:                     int24 strike = tokenId.strike(index);
1353:                     // if position is ITM or ATM, then the collateral requirement depends on price:
1354:
1355:                     // compute the ratio of strike to price for calls (or price to strike for puts)
1356:                     // (- and * 2 in tick space are / and ^ 2 in price space so sqrtRatioAtTick(2 *(a - b)) = a/b (*2^96)
1357:                     // both of these ratios decrease as the position becomes deeper ITM, and it is possible
1358:                     // for the ratio of the prices to go under the minimum price
1359:                     // (which is the limit of what getSqrtRatioAtTick supports)
1360:                     // so instead we cap it at the minimum price, which is acceptable because
1361:                     // a higher ratio will result in an increased slope for the collateral requirement
1362:                     uint160 ratio = tokenType == 1 // tokenType
1363:                         ? Math.getSqrtRatioAtTick(
1364:                             Math.max24(2 * (atTick - strike), Constants.MIN_V3POOL_TICK)
1365:                         ) // puts ->  price/strike
1366:                         : Math.getSqrtRatioAtTick(
1367:                             Math.max24(2 * (strike - atTick), Constants.MIN_V3POOL_TICK)
1368:                         ); // calls -> strike/price
1369:
1370:                     // compute the collateral requirement depending on whether the position is ITM & out-of-range or ITM and in-range:
1371:
1372:                     /// ITM and out-of-range
1373:                     if (
1374:                         ((atTick < tickLower) && (tokenType == 1)) || // strike ITM but out of range price < lowerTick for tokenType=1
1375:                         ((atTick >= tickUpper) && (tokenType == 0)) // strike ITM but out of range when price >= upperTick for tokenType=0
1376:                     ) {
1377:                         /**
1378:                                     Short put BPR = 100% - (price/strike) + SCR
1379:
1380:                            BUYING
1381:                            POWER
1382:                            REQUIREMENT
1383:
1384:                                          ^               .         .
1385:                                          |        <- ITM . <-ATM-> . OTM ->
1386:                            100% + SCR% - |--__           .    .    .
1387:                                   100% - | . .--__     .    .    .
1388:                                          |    .     --__    .    .
1389:                                    SCR - |    .          .--__________
1390:                                          |    .          .    .    .
1391:                                          +----+----------+----+----+--->   current
1392:                                          0   Liqui-     Pa  strike Pb       price
1393:                                              dation
1394:                                              price = SCR*strike
1395:                          */
1396:
1397:                         uint256 c2 = Constants.FP96 - ratio;
1398:
1399:                         // compute the tokens required
1400:                         // position is in-the-money, collateral requirement = amountMoved*(1-ratio) + SCR*amountMoved
1401:                         required += Math.mulDiv96RoundingUp(amountMoved, c2);
1402:                     } else {
1403:                         // position is in-range (ie. current tick is between upper+lower tick): we draw a line between the
1404:                         // collateral requirement at the lowerTick and the one at the upperTick. We use that interpolation as
1405:                         // the collateral requirement when in-range, which always over-estimates the amount of token required
1406:                         // Specifically:
1407:                         //  required = amountMoved * (scaleFactor - ratio) / (scaleFactor + 1) + sellCollateralRatio*amountMoved
1408:                         uint160 scaleFactor = Math.getSqrtRatioAtTick(
1409:                             (tickUpper - strike) + (strike - tickLower)
1410:                         );
1411:                         uint256 c3 = Math.mulDivRoundingUp(
1412:                             amountMoved,
1413:                             scaleFactor - ratio,
1414:                             scaleFactor + Constants.FP96
1415:                         );
1416:                         // position is in-the-money, collateral requirement = amountMoved*(1-SRC)*(scaleFactor-ratio)/(scaleFactor+1) + SCR*amountMoved
1417:                         required += c3;
1418:                     }
1419:                 }
1420:             }

/// @audit comparison operator count: 15
1345:                 if (
1346:                     ((atTick >= tickUpper) && (tokenType == 1)) || // strike OTM when price >= upperTick for tokenType=1
1347:                     ((atTick < tickLower) && (tokenType == 0)) // strike OTM when price < lowerTick for tokenType=0
1348:                 ) {
1349:                     // position is out-the-money, collateral requirement = SCR * amountMoved
1350:                     required;
1351:                 } else {
1352:                     int24 strike = tokenId.strike(index);
1353:                     // if position is ITM or ATM, then the collateral requirement depends on price:
1354:
1355:                     // compute the ratio of strike to price for calls (or price to strike for puts)
1356:                     // (- and * 2 in tick space are / and ^ 2 in price space so sqrtRatioAtTick(2 *(a - b)) = a/b (*2^96)
1357:                     // both of these ratios decrease as the position becomes deeper ITM, and it is possible
1358:                     // for the ratio of the prices to go under the minimum price
1359:                     // (which is the limit of what getSqrtRatioAtTick supports)
1360:                     // so instead we cap it at the minimum price, which is acceptable because
1361:                     // a higher ratio will result in an increased slope for the collateral requirement
1362:                     uint160 ratio = tokenType == 1 // tokenType
1363:                         ? Math.getSqrtRatioAtTick(
1364:                             Math.max24(2 * (atTick - strike), Constants.MIN_V3POOL_TICK)
1365:                         ) // puts ->  price/strike
1366:                         : Math.getSqrtRatioAtTick(
1367:                             Math.max24(2 * (strike - atTick), Constants.MIN_V3POOL_TICK)
1368:                         ); // calls -> strike/price
1369:
1370:                     // compute the collateral requirement depending on whether the position is ITM & out-of-range or ITM and in-range:
1371:
1372:                     /// ITM and out-of-range
1373:                     if (
1374:                         ((atTick < tickLower) && (tokenType == 1)) || // strike ITM but out of range price < lowerTick for tokenType=1
1375:                         ((atTick >= tickUpper) && (tokenType == 0)) // strike ITM but out of range when price >= upperTick for tokenType=0
1376:                     ) {
1377:                         /**
1378:                                     Short put BPR = 100% - (price/strike) + SCR
1379:
1380:                            BUYING
1381:                            POWER
1382:                            REQUIREMENT
1383:
1384:                                          ^               .         .
1385:                                          |        <- ITM . <-ATM-> . OTM ->
1386:                            100% + SCR% - |--__           .    .    .
1387:                                   100% - | . .--__     .    .    .
1388:                                          |    .     --__    .    .
1389:                                    SCR - |    .          .--__________
1390:                                          |    .          .    .    .
1391:                                          +----+----------+----+----+--->   current
1392:                                          0   Liqui-     Pa  strike Pb       price
1393:                                              dation
1394:                                              price = SCR*strike
1395:                          */
1396:
1397:                         uint256 c2 = Constants.FP96 - ratio;
1398:
1399:                         // compute the tokens required
1400:                         // position is in-the-money, collateral requirement = amountMoved*(1-ratio) + SCR*amountMoved
1401:                         required += Math.mulDiv96RoundingUp(amountMoved, c2);
1402:                     } else {
1403:                         // position is in-range (ie. current tick is between upper+lower tick): we draw a line between the
1404:                         // collateral requirement at the lowerTick and the one at the upperTick. We use that interpolation as
1405:                         // the collateral requirement when in-range, which always over-estimates the amount of token required
1406:                         // Specifically:
1407:                         //  required = amountMoved * (scaleFactor - ratio) / (scaleFactor + 1) + sellCollateralRatio*amountMoved
1408:                         uint160 scaleFactor = Math.getSqrtRatioAtTick(
1409:                             (tickUpper - strike) + (strike - tickLower)
1410:                         );
1411:                         uint256 c3 = Math.mulDivRoundingUp(
1412:                             amountMoved,
1413:                             scaleFactor - ratio,
1414:                             scaleFactor + Constants.FP96
1415:                         );
1416:                         // position is in-the-money, collateral requirement = amountMoved*(1-SRC)*(scaleFactor-ratio)/(scaleFactor+1) + SCR*amountMoved
1417:                         required += c3;
1418:                     }
1419:                 }

/// @audit comparison operator count: 7
1373:                     if (
1374:                         ((atTick < tickLower) && (tokenType == 1)) || // strike ITM but out of range price < lowerTick for tokenType=1
1375:                         ((atTick >= tickUpper) && (tokenType == 0)) // strike ITM but out of range when price >= upperTick for tokenType=0
1376:                     ) {
1377:                         /**
1378:                                     Short put BPR = 100% - (price/strike) + SCR
1379:
1380:                            BUYING
1381:                            POWER
1382:                            REQUIREMENT
1383:
1384:                                          ^               .         .
1385:                                          |        <- ITM . <-ATM-> . OTM ->
1386:                            100% + SCR% - |--__           .    .    .
1387:                                   100% - | . .--__     .    .    .
1388:                                          |    .     --__    .    .
1389:                                    SCR - |    .          .--__________
1390:                                          |    .          .    .    .
1391:                                          +----+----------+----+----+--->   current
1392:                                          0   Liqui-     Pa  strike Pb       price
1393:                                              dation
1394:                                              price = SCR*strike
1395:                          */
1396:
1397:                         uint256 c2 = Constants.FP96 - ratio;
1398:
1399:                         // compute the tokens required
1400:                         // position is in-the-money, collateral requirement = amountMoved*(1-ratio) + SCR*amountMoved
1401:                         required += Math.mulDiv96RoundingUp(amountMoved, c2);
1402:                     } else {
1403:                         // position is in-range (ie. current tick is between upper+lower tick): we draw a line between the
1404:                         // collateral requirement at the lowerTick and the one at the upperTick. We use that interpolation as
1405:                         // the collateral requirement when in-range, which always over-estimates the amount of token required
1406:                         // Specifically:
1407:                         //  required = amountMoved * (scaleFactor - ratio) / (scaleFactor + 1) + sellCollateralRatio*amountMoved
1408:                         uint160 scaleFactor = Math.getSqrtRatioAtTick(
1409:                             (tickUpper - strike) + (strike - tickLower)
1410:                         );
1411:                         uint256 c3 = Math.mulDivRoundingUp(
1412:                             amountMoved,
1413:                             scaleFactor - ratio,
1414:                             scaleFactor + Constants.FP96
1415:                         );
1416:                         // position is in-the-money, collateral requirement = amountMoved*(1-SRC)*(scaleFactor-ratio)/(scaleFactor+1) + SCR*amountMoved
1417:                         required += c3;
1418:                     }

/// @audit comparison operator count: 5
1541:         if (tokenId.asset(index) != tokenType) {
1542:             unchecked {
1543:                 // always take the absolute values of the difference of amounts moved
1544:                 if (tokenType == 0) {
1545:                     spreadRequirement = movedRight < movedPartnerRight
1546:                         ? movedPartnerRight - movedRight
1547:                         : movedRight - movedPartnerRight;
1548:                 } else {
1549:                     spreadRequirement = movedLeft < movedPartnerLeft
1550:                         ? movedPartnerLeft - movedLeft
1551:                         : movedLeft - movedPartnerLeft;
1552:                 }
1553:             }
1554:         } else {
1555:             unchecked {
1556:                 uint256 notional;
1557:                 uint256 notionalP;
1558:                 uint128 contracts;
1559:                 if (tokenType == 1) {
1560:                     notional = movedRight;
1561:                     notionalP = movedPartnerRight;
1562:                     contracts = movedLeft;
1563:                 } else {
1564:                     notional = movedLeft;
1565:                     notionalP = movedPartnerLeft;
1566:                     contracts = movedRight;
1567:                 }
1568:                 // the required amount is the amount of contracts multiplied by (notional1 - notional2)/min(notional1, notional2)
1569:                 // can use unsafe because denominator is always nonzero
1570:                 spreadRequirement = (notional < notionalP)
1571:                     ? Math.unsafeDivRoundingUp((notionalP - notional) * contracts, notional)
1572:                     : Math.unsafeDivRoundingUp((notional - notionalP) * contracts, notionalP);
1573:             }
1574:         }
File LinkInstance CountInstance Links
CollateralTracker.sol6662,707,1339,1345,1373,1541

File: contracts/PanopticPool.sol

/// @audit comparison operator count: 4
441:         for (uint256 k = 0; k < pLength; ) {
442:             TokenId tokenId = positionIdList[k];
443:
444:             balances[k][0] = TokenId.unwrap(tokenId);
445:             balances[k][1] = LeftRightUnsigned.unwrap(s_positionBalance[c_user][tokenId]);
446:
447:             (
448:                 LeftRightSigned[4] memory premiaByLeg,
449:                 uint256[2][4] memory premiumAccumulatorsByLeg
450:             ) = _getPremia(
451:                     tokenId,
452:                     LeftRightUnsigned.wrap(balances[k][1]).rightSlot(),
453:                     c_user,
454:                     computeAllPremia,
455:                     atTick
456:                 );
457:
458:             uint256 numLegs = tokenId.countLegs();
459:             for (uint256 leg = 0; leg < numLegs; ) {
460:                 if (tokenId.isLong(leg) == 0 && !includePendingPremium) {
461:                     bytes32 chunkKey = keccak256(
462:                         abi.encodePacked(
463:                             tokenId.strike(leg),
464:                             tokenId.width(leg),
465:                             tokenId.tokenType(leg)
466:                         )
467:                     );
468:
469:                     LeftRightUnsigned availablePremium = _getAvailablePremium(
470:                         _getTotalLiquidity(tokenId, leg),
471:                         s_settledTokens[chunkKey],
472:                         s_grossPremiumLast[chunkKey],
473:                         LeftRightUnsigned.wrap(uint256(LeftRightSigned.unwrap(premiaByLeg[leg]))),
474:                         premiumAccumulatorsByLeg[leg]
475:                     );
476:                     portfolioPremium = portfolioPremium.add(
477:                         LeftRightSigned.wrap(int256(LeftRightUnsigned.unwrap(availablePremium)))
478:                     );
479:                 } else {
480:                     portfolioPremium = portfolioPremium.add(premiaByLeg[leg]);
481:                 }
482:                 unchecked {
483:                     ++leg;
484:                 }
485:             }
486:
487:             unchecked {
488:                 ++k;
489:             }
490:         }

/// @audit comparison operator count: 4
1518:         for (uint256 leg = 0; leg < numLegs; ) {
1519:             uint256 isLong = tokenId.isLong(leg);
1520:             if ((isLong == 1) || computeAllPremia) {
1521:                 LiquidityChunk liquidityChunk = PanopticMath.getLiquidityChunk(
1522:                     tokenId,
1523:                     leg,
1524:                     positionSize
1525:                 );
1526:                 uint256 tokenType = tokenId.tokenType(leg);
1527:
1528:                 (premiumAccumulatorsByLeg[leg][0], premiumAccumulatorsByLeg[leg][1]) = SFPM
1529:                     .getAccountPremium(
1530:                         address(s_univ3pool),
1531:                         address(this),
1532:                         tokenType,
1533:                         liquidityChunk.tickLower(),
1534:                         liquidityChunk.tickUpper(),
1535:                         atTick,
1536:                         isLong
1537:                     );
1538:
1539:                 unchecked {
1540:                     LeftRightUnsigned premiumAccumulatorLast = s_options[owner][tokenId][leg];
1541:
1542:                     // if the premium accumulatorLast is higher than current, it means the premium accumulator has overflowed and rolled over at least once
1543:                     // we can account for one rollover by doing (acc_cur + (acc_max - acc_last))
1544:                     // if there are multiple rollovers or the rollover goes past the last accumulator, rolled over fees will just remain unclaimed
1545:                     premiaByLeg[leg] = LeftRightSigned
1546:                         .wrap(0)
1547:                         .toRightSlot(
1548:                             int128(
1549:                                 int256(
1550:                                     ((premiumAccumulatorsByLeg[leg][0] -
1551:                                         premiumAccumulatorLast.rightSlot()) *
1552:                                         (liquidityChunk.liquidity())) / 2 ** 64
1553:                                 )
1554:                             )
1555:                         )
1556:                         .toLeftSlot(
1557:                             int128(
1558:                                 int256(
1559:                                     ((premiumAccumulatorsByLeg[leg][1] -
1560:                                         premiumAccumulatorLast.leftSlot()) *
1561:                                         (liquidityChunk.liquidity())) / 2 ** 64
1562:                                 )
1563:                             )
1564:                         );
1565:
1566:                     if (isLong == 1) {
1567:                         premiaByLeg[leg] = LeftRightSigned.wrap(0).sub(premiaByLeg[leg]);
1568:                     }
1569:                 }
1570:             }
1571:             unchecked {
1572:                 ++leg;
1573:             }
1574:         }
File LinkInstance CountInstance Links
PanopticPool.sol2441,1518

File: contracts/SemiFungiblePositionManager.sol

/// @audit comparison operator count: 4
787:             if ((itm0 != 0) && (itm1 != 0)) {
788:                 (uint160 sqrtPriceX96, , , , , , ) = _univ3pool.slot0();
789:
790:                 // implement a single "netting" swap. Thank you @danrobinson for this puzzle/idea
791:                 // note: negative ITM amounts denote a surplus of tokens (burning liquidity), while positive amounts denote a shortage of tokens (minting liquidity)
792:                 // compute the approximate delta of token0 that should be resolved in the swap at the current tick
793:                 // we do this by flipping the signs on the token1 ITM amount converting+deducting it against the token0 ITM amount
794:                 // couple examples (price = 2 1/0):
795:                 //  - 100 surplus 0, 100 surplus 1 (itm0 = -100, itm1 = -100)
796:                 //    normal swap 0: 100 0 => 200 1
797:                 //    normal swap 1: 100 1 => 50 0
798:                 //    final swap amounts: 50 0 => 100 1
799:                 //    netting swap: net0 = -100 - (-100/2) = -50, ZF1 = true, 50 0 => 100 1
800:                 // - 100 surplus 0, 100 shortage 1 (itm0 = -100, itm1 = 100)
801:                 //    normal swap 0: 100 0 => 200 1
802:                 //    normal swap 1: 50 0 => 100 1
803:                 //    final swap amounts: 150 0 => 300 1
804:                 //    netting swap: net0 = -100 - (100/2) = -150, ZF1 = true, 150 0 => 300 1
805:                 // - 100 shortage 0, 100 surplus 1 (itm0 = 100, itm1 = -100)
806:                 //    normal swap 0: 200 1 => 100 0
807:                 //    normal swap 1: 100 1 => 50 0
808:                 //    final swap amounts: 300 1 => 150 0
809:                 //    netting swap: net0 = 100 - (-100/2) = 150, ZF1 = false, 300 1 => 150 0
810:                 // - 100 shortage 0, 100 shortage 1 (itm0 = 100, itm1 = 100)
811:                 //    normal swap 0: 200 1 => 100 0
812:                 //    normal swap 1: 50 0 => 100 1
813:                 //    final swap amounts: 100 1 => 50 0
814:                 //    netting swap: net0 = 100 - (100/2) = 50, ZF1 = false, 100 1 => 50 0
815:                 // - = Net surplus of token0
816:                 // + = Net shortage of token0
817:                 int256 net0 = itm0 - PanopticMath.convert1to0(itm1, sqrtPriceX96);
818:
819:                 zeroForOne = net0 < 0;
820:
821:                 //compute the swap amount, set as positive (exact input)
822:                 swapAmount = -net0;
823:             } else if (itm0 != 0) {
824:                 zeroForOne = itm0 < 0;
825:                 swapAmount = -itm0;
826:             } else {
827:                 zeroForOne = itm1 > 0;
828:                 swapAmount = -itm1;
829:             }
File LinkInstance CountInstance Link
SemiFungiblePositionManager.sol1787

File: contracts/libraries/Math.sol

/// @audit comparison operator count: 4
759:             while (i < j) {
760:                 while (arr[uint256(i)] < pivot) i++;
761:                 while (pivot < arr[uint256(j)]) j--;
762:                 if (i <= j) {
763:                     (arr[uint256(i)], arr[uint256(j)]) = (arr[uint256(j)], arr[uint256(i)]);
764:                     i++;
765:                     j--;
766:                 }
767:             }
File LinkInstance CountInstance Link
Math.sol1759

File: contracts/libraries/PanopticMath.sol

/// @audit comparison operator count: 5
183:             if (block.timestamp >= uint256(uint40(medianData >> 216)) + period) {
184:                 int24 lastObservedTick;
185:                 {
186:                     (uint256 timestamp_old, int56 tickCumulative_old, , ) = univ3pool.observations(
187:                         uint256(
188:                             int256(observationIndex) - int256(1) + int256(observationCardinality)
189:                         ) % observationCardinality
190:                     );
191:
192:                     (uint256 timestamp_last, int56 tickCumulative_last, , ) = univ3pool
193:                         .observations(observationIndex);
194:                     lastObservedTick = int24(
195:                         (tickCumulative_last - tickCumulative_old) /
196:                             int256(timestamp_last - timestamp_old)
197:                     );
198:                 }
199:
200:                 uint24 orderMap = uint24(medianData >> 192);
201:
202:                 uint24 newOrderMap;
203:                 uint24 shift = 1;
204:                 bool below = true;
205:                 uint24 rank;
206:                 int24 entry;
207:                 for (uint8 i; i < 8; ++i) {
208:                     // read the rank from the existing ordering
209:                     rank = (orderMap >> (3 * i)) % 8;
210:
211:                     if (rank == 7) {
212:                         shift -= 1;
213:                         continue;
214:                     }
215:
216:                     // read the corresponding entry
217:                     entry = int24(uint24(medianData >> (rank * 24)));
218:                     if ((below) && (lastObservedTick > entry)) {
219:                         shift += 1;
220:                         below = false;
221:                     }
222:
223:                     newOrderMap = newOrderMap + ((rank + 1) << (3 * (i + shift - 1)));
224:                 }
225:
226:                 updatedMedianData =
227:                     (block.timestamp << 216) +
228:                     (uint256(newOrderMap) << 192) +
229:                     uint256(uint192(medianData << 24)) +
230:                     uint256(uint24(lastObservedTick));
231:             }

/// @audit comparison operator count: 4
207:                 for (uint8 i; i < 8; ++i) {
208:                     // read the rank from the existing ordering
209:                     rank = (orderMap >> (3 * i)) % 8;
210:
211:                     if (rank == 7) {
212:                         shift -= 1;
213:                         continue;
214:                     }
215:
216:                     // read the corresponding entry
217:                     entry = int24(uint24(medianData >> (rank * 24)));
218:                     if ((below) && (lastObservedTick > entry)) {
219:                         shift += 1;
220:                         below = false;
221:                     }
222:
223:                     newOrderMap = newOrderMap + ((rank + 1) << (3 * (i + shift - 1)));
224:                 }

/// @audit comparison operator count: 5
356:             if (
357:                 tickLower % tickSpacing != 0 ||
358:                 tickUpper % tickSpacing != 0 ||
359:                 tickLower < minTick ||
360:                 tickUpper > maxTick
361:             ) revert Errors.TicksNotInitializable();

/// @audit comparison operator count: 5
704:             if (!(paid0 > balance0 && paid1 > balance1)) {
705:                 // liquidatee cannot pay back the liquidator fully in either token, so no protocol loss can be avoided
706:                 if ((paid0 > balance0)) {
707:                     // liquidatee has insufficient token0 but some token1 left over, so we use what they have left to mitigate token0 losses
708:                     // we do this by substituting an equivalent value of token1 in our refund to the liquidator, plus a bonus, for the token0 we convert
709:                     // we want to convert the minimum amount of tokens required to achieve the lowest possible protocol loss (to avoid overpaying on the conversion bonus)
710:                     // the maximum level of protocol loss mitigation that can be achieved is the liquidatee's excess token1 balance: balance1 - paid1
711:                     // and paid0 - balance0 is the amount of token0 that the liquidatee is missing, i.e the protocol loss
712:                     // if the protocol loss is lower than the excess token1 balance, then we can fully mitigate the loss and we should only convert the loss amount
713:                     // if the protocol loss is higher than the excess token1 balance, we can only mitigate part of the loss, so we should convert only the excess token1 balance
714:                     // thus, the value converted should be min(balance1 - paid1, paid0 - balance0)
715:                     bonus1 += Math.min(
716:                         balance1 - paid1,
717:                         PanopticMath.convert0to1(paid0 - balance0, sqrtPriceX96Final)
718:                     );
719:                     bonus0 -= Math.min(
720:                         PanopticMath.convert1to0(balance1 - paid1, sqrtPriceX96Final),
721:                         paid0 - balance0
722:                     );
723:                 }
724:                 if ((paid1 > balance1)) {
725:                     // liquidatee has insufficient token1 but some token0 left over, so we use what they have left to mitigate token1 losses
726:                     // we do this by substituting an equivalent value of token0 in our refund to the liquidator, plus a bonus, for the token1 we convert
727:                     // we want to convert the minimum amount of tokens required to achieve the lowest possible protocol loss (to avoid overpaying on the conversion bonus)
728:                     // the maximum level of protocol loss mitigation that can be achieved is the liquidatee's excess token0 balance: balance0 - paid0
729:                     // and paid1 - balance1 is the amount of token1 that the liquidatee is missing, i.e the protocol loss
730:                     // if the protocol loss is lower than the excess token0 balance, then we can fully mitigate the loss and we should only convert the loss amount
731:                     // if the protocol loss is higher than the excess token0 balance, we can only mitigate part of the loss, so we should convert only the excess token0 balance
732:                     // thus, the value converted should be min(balance0 - paid0, paid1 - balance1)
733:                     bonus0 += Math.min(
734:                         balance0 - paid0,
735:                         PanopticMath.convert1to0(paid1 - balance1, sqrtPriceX96Final)
736:                     );
737:                     bonus1 -= Math.min(
738:                         PanopticMath.convert0to1(balance0 - paid0, sqrtPriceX96Final),
739:                         paid1 - balance1
740:                     );
741:                 }
742:             }

/// @audit comparison operator count: 6
797:             if (
798:                 longPremium.rightSlot() < collateralDelta0 &&
799:                 longPremium.leftSlot() > collateralDelta1
800:             ) {
801:                 int256 protocolLoss1 = collateralDelta1;
802:                 (collateralDelta0, collateralDelta1) = (
803:                     -Math.min(
804:                         collateralDelta0 - longPremium.rightSlot(),
805:                         PanopticMath.convert1to0(
806:                             longPremium.leftSlot() - collateralDelta1,
807:                             sqrtPriceX96Final
808:                         )
809:                     ),
810:                     Math.min(
811:                         longPremium.leftSlot() - collateralDelta1,
812:                         PanopticMath.convert0to1(
813:                             collateralDelta0 - longPremium.rightSlot(),
814:                             sqrtPriceX96Final
815:                         )
816:                     )
817:                 );
818:
819:                 haircut0 = longPremium.rightSlot();
820:                 haircut1 = protocolLoss1 + collateralDelta1;
821:             } else if (
822:                 longPremium.leftSlot() < collateralDelta1 &&
823:                 longPremium.rightSlot() > collateralDelta0
824:             ) {
825:                 int256 protocolLoss0 = collateralDelta0;
826:                 (collateralDelta0, collateralDelta1) = (
827:                     Math.min(
828:                         longPremium.rightSlot() - collateralDelta0,
829:                         PanopticMath.convert1to0(
830:                             collateralDelta1 - longPremium.leftSlot(),
831:                             sqrtPriceX96Final
832:                         )
833:                     ),
834:                     -Math.min(
835:                         collateralDelta1 - longPremium.leftSlot(),
836:                         PanopticMath.convert0to1(
837:                             longPremium.rightSlot() - collateralDelta0,
838:                             sqrtPriceX96Final
839:                         )
840:                     )
841:                 );
842:
843:                 haircut0 = collateralDelta0 + protocolLoss0;
844:                 haircut1 = longPremium.leftSlot();
845:             } else {
846:                 // for each token, haircut until the protocol loss is mitigated or the premium paid is exhausted
847:                 haircut0 = Math.min(collateralDelta0, longPremium.rightSlot());
848:                 haircut1 = Math.min(collateralDelta1, longPremium.leftSlot());
849:
850:                 collateralDelta0 = 0;
851:                 collateralDelta1 = 0;
852:             }
File LinkInstance CountInstance Links
PanopticMath.sol5183,207,356,704,797

File: contracts/types/TokenId.sol

/// @audit comparison operator count: 4
376:             if (optionRatios < 2 ** 64) {
377:                 optionRatios = 0;
378:             } else if (optionRatios < 2 ** 112) {
379:                 optionRatios = 1;
380:             } else if (optionRatios < 2 ** 160) {
381:                 optionRatios = 2;
382:             } else if (optionRatios < 2 ** 208) {
383:                 optionRatios = 3;
384:             } else {
385:                 optionRatios = 4;
386:             }

/// @audit comparison operator count: 4
439:         if (optionRatios < 2 ** 64) {
440:             return 0;
441:         } else if (optionRatios < 2 ** 112) {
442:             return 1;
443:         } else if (optionRatios < 2 ** 160) {
444:             return 2;
445:         } else if (optionRatios < 2 ** 208) {
446:             return 3;
447:         }

/// @audit comparison operator count: 15
507:             for (uint256 i = 0; i < 4; ++i) {
508:                 if (self.optionRatio(i) == 0) {
509:                     // final leg in this position identified;
510:                     // make sure any leg above this are zero as well
511:                     // (we don't allow gaps eg having legs 1 and 4 active without 2 and 3 is not allowed)
512:                     if ((TokenId.unwrap(self) >> (64 + 48 * i)) != 0)
513:                         revert Errors.InvalidTokenIdParameter(1);
514:
515:                     break; // we are done iterating over potential legs
516:                 }
517:
518:                 // prevent legs touching the same chunks - all chunks in the position must be discrete
519:                 uint256 numLegs = self.countLegs();
520:                 for (uint256 j = i + 1; j < numLegs; ++j) {
521:                     if (uint48(chunkData >> (48 * i)) == uint48(chunkData >> (48 * j))) {
522:                         revert Errors.InvalidTokenIdParameter(6);
523:                     }
524:                 }
525:                 // now validate this ith leg in the position:
526:
527:                 // The width cannot be 0; the minimum is 1
528:                 if ((self.width(i) == 0)) revert Errors.InvalidTokenIdParameter(5);
529:                 // Strike cannot be MIN_TICK or MAX_TICK
530:                 if (
531:                     (self.strike(i) == Constants.MIN_V3POOL_TICK) ||
532:                     (self.strike(i) == Constants.MAX_V3POOL_TICK)
533:                 ) revert Errors.InvalidTokenIdParameter(4);
534:
535:                 // In the following, we check whether the risk partner of this leg is itself
536:                 // or another leg in this position.
537:                 // Handles case where riskPartner(i) != i ==> leg i has a risk partner that is another leg
538:                 uint256 riskPartnerIndex = self.riskPartner(i);
539:                 if (riskPartnerIndex != i) {
540:                     // Ensures that risk partners are mutual
541:                     if (self.riskPartner(riskPartnerIndex) != i)
542:                         revert Errors.InvalidTokenIdParameter(3);
543:
544:                     // Ensures that risk partners have 1) the same asset, and 2) the same ratio
545:                     if (
546:                         (self.asset(riskPartnerIndex) != self.asset(i)) ||
547:                         (self.optionRatio(riskPartnerIndex) != self.optionRatio(i))
548:                     ) revert Errors.InvalidTokenIdParameter(3);
549:
550:                     // long/short status of associated legs
551:                     uint256 _isLong = self.isLong(i);
552:                     uint256 isLongP = self.isLong(riskPartnerIndex);
553:
554:                     // token type status of associated legs (call/put)
555:                     uint256 _tokenType = self.tokenType(i);
556:                     uint256 tokenTypeP = self.tokenType(riskPartnerIndex);
557:
558:                     // if the position is the same i.e both long calls, short put's etc.
559:                     // then this is a regular position, not a defined risk position
560:                     if ((_isLong == isLongP) && (_tokenType == tokenTypeP))
561:                         revert Errors.InvalidTokenIdParameter(4);
562:
563:                     // if the two token long-types and the tokenTypes are both different (one is a short call, the other a long put, e.g.), this is a synthetic position
564:                     // A synthetic long or short is more capital efficient than each leg separated because the long+short premia accumulate proportionally
565:                     // unlike short stranlges, long strangles also cannot be partnered, because there is no reduction in risk (both legs can earn premia simultaneously)
566:                     if (((_isLong != isLongP) || _isLong == 1) && (_tokenType != tokenTypeP))
567:                         revert Errors.InvalidTokenIdParameter(5);
568:                 }
569:             } // end for loop over legs

/// @audit comparison operator count: 7
539:                 if (riskPartnerIndex != i) {
540:                     // Ensures that risk partners are mutual
541:                     if (self.riskPartner(riskPartnerIndex) != i)
542:                         revert Errors.InvalidTokenIdParameter(3);
543:
544:                     // Ensures that risk partners have 1) the same asset, and 2) the same ratio
545:                     if (
546:                         (self.asset(riskPartnerIndex) != self.asset(i)) ||
547:                         (self.optionRatio(riskPartnerIndex) != self.optionRatio(i))
548:                     ) revert Errors.InvalidTokenIdParameter(3);
549:
550:                     // long/short status of associated legs
551:                     uint256 _isLong = self.isLong(i);
552:                     uint256 isLongP = self.isLong(riskPartnerIndex);
553:
554:                     // token type status of associated legs (call/put)
555:                     uint256 _tokenType = self.tokenType(i);
556:                     uint256 tokenTypeP = self.tokenType(riskPartnerIndex);
557:
558:                     // if the position is the same i.e both long calls, short put's etc.
559:                     // then this is a regular position, not a defined risk position
560:                     if ((_isLong == isLongP) && (_tokenType == tokenTypeP))
561:                         revert Errors.InvalidTokenIdParameter(4);
562:
563:                     // if the two token long-types and the tokenTypes are both different (one is a short call, the other a long put, e.g.), this is a synthetic position
564:                     // A synthetic long or short is more capital efficient than each leg separated because the long+short premia accumulate proportionally
565:                     // unlike short stranlges, long strangles also cannot be partnered, because there is no reduction in risk (both legs can earn premia simultaneously)
566:                     if (((_isLong != isLongP) || _isLong == 1) && (_tokenType != tokenTypeP))
567:                         revert Errors.InvalidTokenIdParameter(5);
568:                 }

/// @audit comparison operator count: 5
581:             for (uint256 i = 0; i < numLegs; ++i) {
582:                 (int24 rangeDown, int24 rangeUp) = PanopticMath.getRangesFromStrike(
583:                     self.width(i),
584:                     self.tickSpacing()
585:                 );
586:
587:                 int24 _strike = self.strike(i);
588:                 // check if the price is outside this chunk
589:                 if ((currentTick >= _strike + rangeUp) || (currentTick < _strike - rangeDown)) {
590:                     // if this leg is long and the price beyond the leg's range:
591:                     // this exercised ID, `self`, appears valid
592:                     if (self.isLong(i) == 1) return; // validated
593:                 }
594:             }

/// @audit comparison operator count: 4
589:                 if ((currentTick >= _strike + rangeUp) || (currentTick < _strike - rangeDown)) {
590:                     // if this leg is long and the price beyond the leg's range:
591:                     // this exercised ID, `self`, appears valid
592:                     if (self.isLong(i) == 1) return; // validated
593:                 }
File LinkInstance CountInstance Links
TokenId.sol6376,439,507,539,581,589

</details>
<a id="n-14"></a> [N-14] Consider using a struct rather than having many function input parameters
Description:

To increase readability and maintainability, it may be more efficient to pass in a struct rather than having many function input parameters.

Instances:

There are 40 instances of this issue.

<details><summary>View 40 Instances</summary>
File: contracts/CollateralTracker.sol

178:     constructor(
179:         uint256 _commissionFee,
180:         uint256 _sellerCollateralRatio,
181:         uint256 _buyerCollateralRatio,
182:         int256 _forceExerciseCost,
183:         uint256 _targetPoolUtilization,
184:         uint256 _saturatedPoolUtilization,
185:         uint256 _ITMSpreadMultiplier
186:     ) {

221:     function startToken(
222:         bool underlyingIsToken0,
223:         address token0,
224:         address token1,
225:         uint24 fee,
226:         PanopticPool panopticPool
227:     ) external {

650:     function exerciseCost(
651:         int24 currentTick,
652:         int24 oracleTick,
653:         TokenId positionId,
654:         uint128 positionBalance,
655:         LeftRightSigned longAmounts
656:     ) external view returns (LeftRightSigned exerciseFees) {

1043:     function exercise(
1044:         address optionOwner,
1045:         int128 longAmount,
1046:         int128 shortAmount,
1047:         int128 swappedAmount,
1048:         int128 realizedPremium
1049:     ) external onlyPanopticPool returns (int128) {

1278:     function _getRequiredCollateralSingleLeg(
1279:         TokenId tokenId,
1280:         uint256 index,
1281:         uint128 positionSize,
1282:         int24 atTick,
1283:         uint128 poolUtilization
1284:     ) internal view returns (uint256 required) {

1311:     function _getRequiredCollateralSingleLegNoPartner(
1312:         TokenId tokenId,
1313:         uint256 index,
1314:         uint128 positionSize,
1315:         int24 atTick,
1316:         uint128 poolUtilization
1317:     ) internal view returns (uint256 required) {

1439:     function _getRequiredCollateralSingleLegPartner(
1440:         TokenId tokenId,
1441:         uint256 index,
1442:         uint128 positionSize,
1443:         int24 atTick,
1444:         uint128 poolUtilization
1445:     ) internal view returns (uint256 required) {

1510:     function _computeSpread(
1511:         TokenId tokenId,
1512:         uint128 positionSize,
1513:         uint256 index,
1514:         uint256 partnerIndex,
1515:         uint128 poolUtilization
1516:     ) internal view returns (uint256 spreadRequirement) {

1600:     function _computeStrangle(
1601:         TokenId tokenId,
1602:         uint256 index,
1603:         uint128 positionSize,
1604:         int24 atTick,
1605:         uint128 poolUtilization
1606:     ) internal view returns (uint256 strangleRequired) {
File LinkInstance CountInstance Links
CollateralTracker.sol9178,221,650,1043,1278,1311,1439,1510,1600

File: contracts/PanopticFactory.sol

115:     constructor(
116:         address _WETH9,
117:         SemiFungiblePositionManager _SFPM,
118:         IUniswapV3Factory _univ3Factory,
119:         IDonorNFT _donorNFT,
120:         address _poolReference,
121:         address _collateralReference
122:     ) {
File LinkInstance CountInstance Link
PanopticFactory.sol1115

File: contracts/PanopticPool.sol

291:     function startPool(
292:         IUniswapV3Pool _univ3pool,
293:         address token0,
294:         address token1,
295:         CollateralTracker collateralTracker0,
296:         CollateralTracker collateralTracker1
297:     ) external {

429:     function _calculateAccumulatedPremia(
430:         address user,
431:         TokenId[] calldata positionIdList,
432:         bool computeAllPremia,
433:         bool includePendingPremium,
434:         int24 atTick
435:     ) internal view returns (LeftRightSigned portfolioPremium, uint256[2][] memory balances) {

547:     function mintOptions(
548:         TokenId[] calldata positionIdList,
549:         uint128 positionSize,
550:         uint64 effectiveLiquidityLimitX32,
551:         int24 tickLimitLow,
552:         int24 tickLimitHigh
553:     ) external {

614:     function _mintOptions(
615:         TokenId[] calldata positionIdList,
616:         uint128 positionSize,
617:         uint64 effectiveLiquidityLimitX32,
618:         int24 tickLimitLow,
619:         int24 tickLimitHigh
620:     ) internal {

794:     function _burnAllOptionsFrom(
795:         address owner,
796:         int24 tickLimitLow,
797:         int24 tickLimitHigh,
798:         bool commitLongSettled,
799:         TokenId[] calldata positionIdList
800:     ) internal returns (LeftRightSigned netPaid, LeftRightSigned[4][] memory premiasByLeg) {

826:     function _burnOptions(
827:         bool commitLongSettled,
828:         TokenId tokenId,
829:         address owner,
830:         int24 tickLimitLow,
831:         int24 tickLimitHigh
832:     ) internal returns (LeftRightSigned paidAmounts, LeftRightSigned[4] memory premiaByLeg) {

955:     function _burnAndHandleExercise(
956:         bool commitLongSettled,
957:         int24 tickLimitLow,
958:         int24 tickLimitHigh,
959:         TokenId tokenId,
960:         uint128 positionSize,
961:         address owner
962:     )
963:         internal
964:         returns (
965:             LeftRightSigned realizedPremia,
966:             LeftRightSigned[4] memory premiaByLeg,
967:             LeftRightSigned paidAmounts
968:         )
969:     {

1290:     function _checkSolvencyAtTick(
1291:         address account,
1292:         TokenId[] calldata positionIdList,
1293:         int24 currentTick,
1294:         int24 atTick,
1295:         uint256 buffer
1296:     ) internal view returns (bool) {

1465:     function _checkLiquiditySpread(
1466:         TokenId tokenId,
1467:         uint256 leg,
1468:         int24 tickLower,
1469:         int24 tickUpper,
1470:         uint64 effectiveLiquidityLimitX32
1471:     ) internal view {

1503:     function _getPremia(
1504:         TokenId tokenId,
1505:         uint128 positionSize,
1506:         address owner,
1507:         bool computeAllPremia,
1508:         int24 atTick
1509:     )
1510:         internal
1511:         view
1512:         returns (
1513:             LeftRightSigned[4] memory premiaByLeg,
1514:             uint256[2][4] memory premiumAccumulatorsByLeg
1515:         )
1516:     {

1757:     function _getAvailablePremium(
1758:         uint256 totalLiquidity,
1759:         LeftRightUnsigned settledTokens,
1760:         LeftRightUnsigned grossPremiumLast,
1761:         LeftRightUnsigned premiumOwed,
1762:         uint256[2] memory premiumAccumulators
1763:     ) internal pure returns (LeftRightUnsigned) {

1833:     function _updateSettlementPostBurn(
1834:         address owner,
1835:         TokenId tokenId,
1836:         LeftRightUnsigned[4] memory collectedByLeg,
1837:         uint128 positionSize,
1838:         bool commitLongSettled
1839:     ) internal returns (LeftRightSigned realizedPremia, LeftRightSigned[4] memory premiaByLeg) {
File LinkInstance CountInstance Links
PanopticPool.sol12291,429,547,614,794,826,955,1290,1465,1503,1757,1833

File: contracts/SemiFungiblePositionManager.sol

680:     function _validateAndForwardToAMM(
681:         TokenId tokenId,
682:         uint128 positionSize,
683:         int24 tickLimitLow,
684:         int24 tickLimitHigh,
685:         bool isBurn
686:     ) internal returns (LeftRightUnsigned[4] memory collectedByLeg, LeftRightSigned totalMoved) {

958:     function _createLegInAMM(
959:         IUniswapV3Pool univ3pool,
960:         TokenId tokenId,
961:         uint256 leg,
962:         LiquidityChunk liquidityChunk,
963:         bool isBurn
964:     )
965:         internal
966:         returns (
967:             LeftRightSigned moved,
968:             LeftRightSigned itmAmounts,
969:             LeftRightUnsigned collectedSingleLeg
970:         )
971:     {

1255:     function _collectAndWritePositionData(
1256:         LiquidityChunk liquidityChunk,
1257:         IUniswapV3Pool univ3pool,
1258:         LeftRightUnsigned currentLiquidity,
1259:         bytes32 positionKey,
1260:         LeftRightSigned movedInLeg,
1261:         uint256 isLong
1262:     ) internal returns (LeftRightUnsigned collectedChunk) {

1421:     function getAccountLiquidity(
1422:         address univ3pool,
1423:         address owner,
1424:         uint256 tokenType,
1425:         int24 tickLower,
1426:         int24 tickUpper
1427:     ) external view returns (LeftRightUnsigned accountLiquidities) {

1449:     function getAccountPremium(
1450:         address univ3pool,
1451:         address owner,
1452:         uint256 tokenType,
1453:         int24 tickLower,
1454:         int24 tickUpper,
1455:         int24 atTick,
1456:         uint256 isLong
1457:     ) external view returns (uint128, uint128) {

1535:     function getAccountFeesBase(
1536:         address univ3pool,
1537:         address owner,
1538:         uint256 tokenType,
1539:         int24 tickLower,
1540:         int24 tickUpper
1541:     ) external view returns (int128 feesBase0, int128 feesBase1) {
File LinkInstance CountInstance Links
SemiFungiblePositionManager.sol6680,958,1255,1421,1449,1535

File: contracts/libraries/FeesCalc.sol

 97:     function calculateAMMSwapFees(
 98:         IUniswapV3Pool univ3pool,
 99:         int24 currentTick,
100:         int24 tickLower,
101:         int24 tickUpper,
102:         uint128 liquidity
103:     ) public view returns (LeftRightSigned) {
File LinkInstance CountInstance Link
FeesCalc.sol197

File: contracts/libraries/InteractionHelper.sol

24:     function doApprovals(
25:         SemiFungiblePositionManager sfpm,
26:         CollateralTracker ct0,
27:         CollateralTracker ct1,
28:         address token0,
29:         address token1
30:     ) external {

48:     function computeName(
49:         address token0,
50:         address token1,
51:         bool isToken0,
52:         uint24 fee,
53:         string memory prefix
54:     ) external view returns (string memory) {
File LinkInstance CountInstance Links
InteractionHelper.sol224,48

File: contracts/libraries/PanopticMath.sol

125:     function computeMedianObservedPrice(
126:         IUniswapV3Pool univ3pool,
127:         uint256 observationIndex,
128:         uint256 observationCardinality,
129:         uint256 cardinality,
130:         uint256 period
131:     ) external view returns (int24) {

168:     function computeInternalMedian(
169:         uint256 observationIndex,
170:         uint256 observationCardinality,
171:         uint256 period,
172:         uint256 medianData,
173:         IUniswapV3Pool univ3pool
174:     ) external view returns (int24 medianTick, uint256 updatedMedianData) {

651:     function getLiquidationBonus(
652:         LeftRightUnsigned tokenData0,
653:         LeftRightUnsigned tokenData1,
654:         uint160 sqrtPriceX96Twap,
655:         uint160 sqrtPriceX96Final,
656:         LeftRightSigned netExchanged,
657:         LeftRightSigned premia
658:     ) external pure returns (int256 bonus0, int256 bonus1, LeftRightSigned) {

768:     function haircutPremia(
769:         address liquidatee,
770:         TokenId[] memory positionIdList,
771:         LeftRightSigned[4][] memory premiasByLeg,
772:         LeftRightSigned collateralRemaining,
773:         CollateralTracker collateral0,
774:         CollateralTracker collateral1,
775:         uint160 sqrtPriceX96Final,
776:         mapping(bytes32 chunkKey => LeftRightUnsigned settledTokens) storage settledTokens
777:     ) external returns (int256, int256) {

917:     function getRefundAmounts(
918:         address refunder,
919:         LeftRightSigned refundValues,
920:         int24 atTick,
921:         CollateralTracker collateral0,
922:         CollateralTracker collateral1
923:     ) external view returns (LeftRightSigned) {
File LinkInstance CountInstance Links
PanopticMath.sol5125,168,651,768,917

File: contracts/tokens/ERC1155Minimal.sol

 94:     function safeTransferFrom(
 95:         address from,
 96:         address to,
 97:         uint256 id,
 98:         uint256 amount,
 99:         bytes calldata data
100:     ) public virtual {

130:     function safeBatchTransferFrom(
131:         address from,
132:         address to,
133:         uint256[] calldata ids,
134:         uint256[] calldata amounts,
135:         bytes calldata data
136:     ) public virtual {
File LinkInstance CountInstance Links
ERC1155Minimal.sol294,130

File: contracts/tokens/interfaces/IDonorNFT.sol

13:     function issueNFT(
14:         address deployer,
15:         PanopticPool newPoolContract,
16:         address token0,
17:         address token1,
18:         uint24 fee
19:     ) external;
File LinkInstance CountInstance Link
IDonorNFT.sol113

File: contracts/types/TokenId.sol

336:     function addLeg(
337:         TokenId self,
338:         uint256 legIndex,
339:         uint256 _optionRatio,
340:         uint256 _asset,
341:         uint256 _isLong,
342:         uint256 _tokenType,
343:         uint256 _riskPartner,
344:         int24 _strike,
345:         int24 _width
346:     ) internal pure returns (TokenId tokenId) {
File LinkInstance CountInstance Link
TokenId.sol1336

</details>
<a id="n-15"></a> [N-15] Consider using descriptive constants when passing zero as a function argument
Description:

Passing zero as a function argument can sometimes result in a security issue (e.g., passing zero as the slippage parameter).

Recommendation:

Consider using a constant with a descriptive name, so it is clear what the argument represents and how it is being used.

Instances:

There are 62 instances of this issue.

<details><summary>View 62 Instances</summary>
File: contracts/CollateralTracker.sol

712:                         LeftRightSigned
713:                             .wrap(0)
File LinkInstance CountInstance Link
CollateralTracker.sol1712

File: contracts/PanopticPool.sol

634:             revert Errors.InvalidTokenIdParameter(0);

654:         s_positionBalance[msg.sender][tokenId] = LeftRightUnsigned
655:             .wrap(0)

762:                 s_options[msg.sender][tokenId][leg] = LeftRightUnsigned
763:                     .wrap(0)

861:         s_positionBalance[owner][tokenId] = LeftRightUnsigned.wrap(0);

870:             s_options[owner][tokenId][leg] = LeftRightUnsigned.wrap(0);

893:         _validatePositionList(user, positionIdList, 0);

1023:         _validatePositionList(liquidatee, positionIdList, 0);

1153:         _validatePositionList(msg.sender, positionIdListLiquidator, 0);

1165:         LeftRightSigned bonusAmounts = LeftRightSigned
1166:             .wrap(0)

1191:         _validatePositionList(msg.sender, positionIdListExercisor, 0);

1227:         _burnAllOptionsFrom(account, 0, 0, COMMIT_LONG_SETTLED, touchedId);

1545:                     premiaByLeg[leg] = LeftRightSigned
1546:                         .wrap(0)

1567:                         premiaByLeg[leg] = LeftRightSigned.wrap(0).sub(premiaByLeg[leg]);

1592:         _validatePositionList(owner, positionIdList, 0);

1614:             accumulatedPremium = LeftRightUnsigned
1615:                 .wrap(0)

1633:             LeftRightSigned realizedPremia = LeftRightSigned
1634:                 .wrap(0)

1639:             s_collateralToken0.exercise(owner, 0, 0, 0, realizedPremia.rightSlot());

1640:             s_collateralToken1.exercise(owner, 0, 0, 0, realizedPremia.leftSlot());

1707:                 (grossCurrent[0], grossCurrent[1]) = SFPM.getAccountPremium(
1708:                     address(s_univ3pool),
1709:                     address(this),
1710:                     tokenId.tokenType(leg),
1711:                     liquidityChunk.tickLower(),
1712:                     liquidityChunk.tickUpper(),
1713:                     type(int24).max,
1714:                     0
1715:                 );

1725:                     s_grossPremiumLast[chunkKey] = LeftRightUnsigned
1726:                         .wrap(0)

1774:                 LeftRightUnsigned
1775:                     .wrap(0)

1929:                             ? LeftRightUnsigned
1930:                                 .wrap(0)

1934:                                             Math.max(
1935:                                                 (int256(
1936:                                                     grossPremiumLast.rightSlot() *
1937:                                                         totalLiquidityBefore
1938:                                                 ) -
1939:                                                     int256(
1940:                                                         _premiumAccumulatorsByLeg[_leg][0] *
1941:                                                             positionLiquidity
1942:                                                     )) + int256(legPremia.rightSlot() * 2 ** 64),
1943:                                                 0
1944:                                             )

1951:                                             Math.max(
1952:                                                 (int256(
1953:                                                     grossPremiumLast.leftSlot() *
1954:                                                         totalLiquidityBefore
1955:                                                 ) -
1956:                                                     int256(
1957:                                                         _premiumAccumulatorsByLeg[_leg][1] *
1958:                                                             positionLiquidity
1959:                                                     )) + int256(legPremia.leftSlot()) * 2 ** 64,
1960:                                                 0
1961:                                             )

1965:                             : LeftRightUnsigned
1966:                                 .wrap(0)

File: contracts/SemiFungiblePositionManager.sol

645:             s_accountLiquidity[positionKey_from] = LeftRightUnsigned.wrap(0);

648:             s_accountFeesBase[positionKey_from] = LeftRightSigned.wrap(0);

833:             if (swapAmount == 0) return LeftRightSigned.wrap(0);

848:             totalSwapped = LeftRightSigned.wrap(0).toRightSlot(swap0.toInt128()).toLeftSlot(

1038:             s_accountLiquidity[positionKey] = LeftRightUnsigned
1039:                 .wrap(0)

1166:             ? LeftRightSigned
1167:                 .wrap(0)

1174:             : LeftRightSigned
1175:                 .wrap(0)

1214:         movedAmounts = LeftRightSigned.wrap(0).toRightSlot(int128(int256(amount0))).toLeftSlot(

1241:             movedAmounts = LeftRightSigned.wrap(0).toRightSlot(-int128(int256(amount0))).toLeftSlot(

1306:             collectedChunk = LeftRightUnsigned.wrap(0).toRightSlot(collected0).toLeftSlot(

1376:                     deltaPremiumOwed = LeftRightUnsigned
1377:                         .wrap(0)

1400:                     deltaPremiumGross = LeftRightUnsigned
1401:                         .wrap(0)
File LinkInstance CountInstance Links
SemiFungiblePositionManager.sol12645,648,833,848,1038,1166,1174,1214,1241,1306,1376,1400

File: contracts/libraries/FeesCalc.sol

115:             LeftRightSigned
116:                 .wrap(0)
File LinkInstance CountInstance Link
FeesCalc.sol1115

File: contracts/libraries/PanopticMath.sol

598:         return LeftRightUnsigned.wrap(0).toRightSlot(amount0).toLeftSlot(amount1);

671:                 (uint256 balanceCross, uint256 thresholdCross) = PanopticMath.convertCollateralData(
672:                     tokenData0,
673:                     tokenData1,
674:                     0,
675:                     sqrtPriceX96Twap
676:                 );

694:                 Math.max(premia.rightSlot(), 0);

696:                 Math.max(premia.leftSlot(), 0);

749:                 LeftRightSigned.wrap(0).toRightSlot(int128(balance0 - paid0)).toLeftSlot(

791:             int256 collateralDelta0 = -Math.min(collateralRemaining.rightSlot(), 0);

792:             int256 collateralDelta1 = -Math.min(collateralRemaining.leftSlot(), 0);

856:                 if (haircut0 != 0) collateral0.exercise(_liquidatee, 0, 0, 0, int128(haircut0));

857:                 if (haircut1 != 0) collateral1.exercise(_liquidatee, 0, 0, 0, int128(haircut1));

880:                                 tokenId.strike(0),

881:                                 tokenId.width(0),

882:                                 tokenId.tokenType(0)

888:                         settled0 = Math.max(
889:                             0,
890:                             uint128(-_premiasByLeg[i][leg].rightSlot()) - settled0
891:                         );

892:                         settled1 = Math.max(
893:                             0,
894:                             uint128(-_premiasByLeg[i][leg].leftSlot()) - settled1
895:                         );

898:                             LeftRightUnsigned.wrap(0).toRightSlot(uint128(settled0)).toLeftSlot(

933:                     LeftRightSigned
934:                         .wrap(0)

951:                     LeftRightSigned
952:                         .wrap(0)
File LinkInstance CountInstance Links
PanopticMath.sol17598,671,694,696,749,791,792,856,857,880,881,882,888,892,898,933,951

File: contracts/types/LeftRight.sol

265:                 z.toRightSlot(int128(Math.max(right128, 0))).toLeftSlot(

266:                     int128(Math.max(left128, 0))

294:             LeftRightUnsigned.wrap(0).toRightSlot(r_Enabled ? z_xR : x.rightSlot()).toLeftSlot(

297:             LeftRightUnsigned.wrap(0).toRightSlot(r_Enabled ? z_yR : y.rightSlot()).toLeftSlot(
File LinkInstance CountInstance Links
LeftRight.sol4265,266,294,297

File: contracts/types/TokenId.sol

406:             return self.isLong(0) + self.isLong(1) + self.isLong(2) + self.isLong(3);

501:         if (self.optionRatio(0) == 0) revert Errors.InvalidTokenIdParameter(1);
File LinkInstance CountInstance Links
TokenId.sol2406,501

</details>
<a id="n-16"></a> [N-16] Consider using named function arguments
Description:

Function call arguments can be given by name, in any order, if they are enclosed in { and }. The argument list has to coincide by name with the list of parameters from the function declaration, but can be in arbitrary order. Using named function parameters can improve code readability and maintainability by explicitly mapping arguments to their respective parameter names, reducing potential errors due to out of order arguments. The following findings are for function calls with 4 or more parameters.

Instances:

There are 117 instances of this issue.

<details><summary>View 117 Instances</summary>
File: contracts/CollateralTracker.sol

292:             InteractionHelper.computeName(
293:                 s_univ3token0,
294:                 s_univ3token1,
295:                 s_underlyingIsToken0,
296:                 s_poolFee,
297:                 NAME_PREFIX
298:             );

424:         SafeTransferLib.safeTransferFrom(
425:             s_underlyingToken,
426:             msg.sender,
427:             address(s_panopticPool),
428:             assets
429:         );

439:         emit Deposit(msg.sender, receiver, assets, shares);

484:         SafeTransferLib.safeTransferFrom(
485:             s_underlyingToken,
486:             msg.sender,
487:             address(s_panopticPool),
488:             assets
489:         );

499:         emit Deposit(msg.sender, receiver, assets, shares);

556:         SafeTransferLib.safeTransferFrom(
557:             s_underlyingToken,
558:             address(s_panopticPool),
559:             receiver,
560:             assets
561:         );

563:         emit Withdraw(msg.sender, receiver, owner, assets, shares);

616:         SafeTransferLib.safeTransferFrom(
617:             s_underlyingToken,
618:             address(s_panopticPool),
619:             receiver,
620:             assets
621:         );

623:         emit Withdraw(msg.sender, receiver, owner, assets, shares);

1147:         tokenData = _getAccountMargin(user, currentTick, positionBalanceArray, premiumAllPositions);

1219:             uint256 _tokenRequired = _getRequiredCollateralAtTickSinglePosition(
1220:                 tokenId,
1221:                 positionSize,
1222:                 atTick,
1223:                 poolUtilization
1224:             );

1259:                 tokenRequired += _getRequiredCollateralSingleLeg(
1260:                     tokenId,
1261:                     index,
1262:                     positionSize,
1263:                     atTick,
1264:                     poolUtilization
1265:                 );

1287:                 ? _getRequiredCollateralSingleLegNoPartner(
1288:                     tokenId,
1289:                     index,
1290:                     positionSize,
1291:                     atTick,
1292:                     poolUtilization
1293:                 )

1294:                 : _getRequiredCollateralSingleLegPartner(
1295:                     tokenId,
1296:                     index,
1297:                     positionSize,
1298:                     atTick,
1299:                     poolUtilization
1300:                 );

1453:                 required = _computeSpread(
1454:                     tokenId,
1455:                     positionSize,
1456:                     index,
1457:                     partnerIndex,
1458:                     poolUtilization
1459:                 );

1462:             required = _computeStrangle(tokenId, index, positionSize, atTick, poolUtilization);

1641:                 strangleRequired = _getRequiredCollateralSingleLegNoPartner(
1642:                     tokenId,
1643:                     index,
1644:                     positionSize,
1645:                     atTick,
1646:                     poolUtilization
1647:                 );
File LinkInstance CountInstance Links
CollateralTracker.sol17292,424,439,484,499,556,563,616,623,1147,1219,1259,1287,1294,1453,1462,1641

File: contracts/PanopticFactory.sol

182:             SafeTransferLib.safeTransferFrom(
183:                 decoded.poolFeatures.token0,
184:                 decoded.payer,
185:                 msg.sender,
186:                 amount0Owed
187:             );

189:             SafeTransferLib.safeTransferFrom(
190:                 decoded.poolFeatures.token1,
191:                 decoded.payer,
192:                 msg.sender,
193:                 amount1Owed
194:             );

248:         collateralTracker0.startToken(true, token0, token1, fee, newPoolContract);

249:         collateralTracker1.startToken(false, token0, token1, fee, newPoolContract);

251:         newPoolContract.startPool(v3Pool, token0, token1, collateralTracker0, collateralTracker1);

263:         (uint256 amount0, uint256 amount1) = _mintFullRange(v3Pool, token0, token1, fee);

266:         DONOR_NFT.issueNFT(msg.sender, newPoolContract, token0, token1, fee);

268:         emit PoolDeployed(
269:             newPoolContract,
270:             v3Pool,
271:             collateralTracker0,
272:             collateralTracker1,
273:             amount0,
274:             amount1
275:         );

404:             IUniswapV3Pool(v3Pool).mint(
405:                 address(this),
406:                 tickLower,
407:                 tickUpper,
408:                 fullRangeLiquidity,
409:                 mintCallback
410:             );
File LinkInstance CountInstance Links
PanopticFactory.sol9182,189,248,249,251,263,266,268,404

File: contracts/PanopticPool.sol

326:         InteractionHelper.doApprovals(SFPM, collateralTracker0, collateralTracker1, token0, token1);

390:         (LeftRightSigned premia, uint256[2][] memory balances) = _calculateAccumulatedPremia(
391:             user,
392:             positionIdList,
393:             COMPUTE_ALL_PREMIA,
394:             includePendingPremium,
395:             currentTick
396:         );

450:             ) = _getPremia(
451:                     tokenId,
452:                     LeftRightUnsigned.wrap(balances[k][1]).rightSlot(),
453:                     c_user,
454:                     computeAllPremia,
455:                     atTick
456:                 );

469:                     LeftRightUnsigned availablePremium = _getAvailablePremium(
470:                         _getTotalLiquidity(tokenId, leg),
471:                         s_settledTokens[chunkKey],
472:                         s_grossPremiumLast[chunkKey],
473:                         LeftRightUnsigned.wrap(uint256(LeftRightSigned.unwrap(premiaByLeg[leg]))),
474:                         premiumAccumulatorsByLeg[leg]
475:                     );

525:         (, uint256 medianData) = PanopticMath.computeInternalMedian(
526:             observationIndex,
527:             observationCardinality,
528:             MEDIAN_PERIOD,
529:             s_miniMedian,
530:             s_univ3pool
531:         );

554:         _mintOptions(
555:             positionIdList,
556:             positionSize,
557:             effectiveLiquidityLimitX32,
558:             tickLimitLow,
559:             tickLimitHigh
560:         );

575:         _burnOptions(COMMIT_LONG_SETTLED, tokenId, msg.sender, tickLimitLow, tickLimitHigh);

592:         _burnAllOptionsFrom(
593:             msg.sender,
594:             tickLimitLow,
595:             tickLimitHigh,
596:             COMMIT_LONG_SETTLED,
597:             positionIdList
598:         );

642:         uint128 poolUtilizations = _mintInSFPMAndUpdateCollateral(
643:             tokenId,
644:             positionSize,
645:             tickLimitLow,
646:             tickLimitHigh
647:         );

666:         emit OptionMinted(msg.sender, positionSize, tokenId, poolUtilizations);

685:         (LeftRightUnsigned[4] memory collectedByLeg, LeftRightSigned totalSwapped) = SFPM
686:             .mintTokenizedPosition(tokenId, positionSize, tickLimitLow, tickLimitHigh);

715:         int256 utilization0 = s_collateralToken0.takeCommissionAddData(
716:             msg.sender,
717:             longAmounts.rightSlot(),
718:             shortAmounts.rightSlot(),
719:             totalSwapped.rightSlot()
720:         );

721:         int256 utilization1 = s_collateralToken1.takeCommissionAddData(
722:             msg.sender,
723:             longAmounts.leftSlot(),
724:             shortAmounts.leftSlot(),
725:             totalSwapped.leftSlot()
726:         );

751:                 (uint128 premiumAccumulator0, uint128 premiumAccumulator1) = SFPM.getAccountPremium(
752:                     address(s_univ3pool),
753:                     address(this),
754:                     tokenId.tokenType(leg),
755:                     tickLower,
756:                     tickUpper,
757:                     type(int24).max,
758:                     isLong
759:                 );

770:                 _checkLiquiditySpread(
771:                     tokenId,
772:                     leg,
773:                     tickLower,
774:                     tickUpper,
775:                     uint64(Math.min(effectiveLiquidityLimitX32, MAX_SPREAD))
776:                 );

804:             (paidAmounts, premiasByLeg[i]) = _burnOptions(
805:                 commitLongSettled,
806:                 positionIdList[i],
807:                 owner,
808:                 tickLimitLow,
809:                 tickLimitHigh
810:             );

840:         (premiaOwed, premiaByLeg, paidAmounts) = _burnAndHandleExercise(
841:             commitLongSettled,
842:             tickLimitLow,
843:             tickLimitHigh,
844:             tokenId,
845:             positionSize,
846:             owner
847:         );

853:         emit OptionBurnt(owner, positionSize, tokenId, premiaOwed);

868:                 _checkLiquiditySpread(tokenId, leg, tickLower, tickUpper, MAX_SPREAD);

905:         int24 fastOracleTick = PanopticMath.computeMedianObservedPrice(
906:             _univ3pool,
907:             observationIndex,
908:             observationCardinality,
909:             FAST_ORACLE_CARDINALITY,
910:             FAST_ORACLE_PERIOD
911:         );

915:             slowOracleTick = PanopticMath.computeMedianObservedPrice(
916:                 _univ3pool,
917:                 observationIndex,
918:                 observationCardinality,
919:                 SLOW_ORACLE_CARDINALITY,
920:                 SLOW_ORACLE_PERIOD
921:             );

923:             (slowOracleTick, medianData) = PanopticMath.computeInternalMedian(
924:                 observationIndex,
925:                 observationCardinality,
926:                 MEDIAN_PERIOD,
927:                 s_miniMedian,
928:                 _univ3pool
929:             );

933:         bool solventAtFast = _checkSolvencyAtTick(
934:             user,
935:             positionIdList,
936:             currentTick,
937:             fastOracleTick,
938:             buffer
939:         );

944:             if (!_checkSolvencyAtTick(user, positionIdList, currentTick, slowOracleTick, buffer))

970:         (LeftRightUnsigned[4] memory collectedByLeg, LeftRightSigned totalSwapped) = SFPM
971:             .burnTokenizedPosition(tokenId, positionSize, tickLimitLow, tickLimitHigh);

973:         (realizedPremia, premiaByLeg) = _updateSettlementPostBurn(
974:             owner,
975:             tokenId,
976:             collectedByLeg,
977:             positionSize,
978:             commitLongSettled
979:         );

985:             int128 paid0 = s_collateralToken0.exercise(
986:                 owner,
987:                 longAmounts.rightSlot(),
988:                 shortAmounts.rightSlot(),
989:                 totalSwapped.rightSlot(),
990:                 realizedPremia.rightSlot()
991:             );

 996:             int128 paid1 = s_collateralToken1.exercise(
 997:                 owner,
 998:                 longAmounts.leftSlot(),
 999:                 shortAmounts.leftSlot(),
1000:                 totalSwapped.leftSlot(),
1001:                 realizedPremia.leftSlot()
1002:             );

1039:             (premia, positionBalanceArray) = _calculateAccumulatedPremia(
1040:                 liquidatee,
1041:                 positionIdList,
1042:                 COMPUTE_ALL_PREMIA,
1043:                 ONLY_AVAILABLE_PREMIUM,
1044:                 currentTick
1045:             );

1046:             tokenData0 = s_collateralToken0.getAccountMarginDetails(
1047:                 liquidatee,
1048:                 twapTick,
1049:                 positionBalanceArray,
1050:                 premia.rightSlot()
1051:             );

1053:             tokenData1 = s_collateralToken1.getAccountMarginDetails(
1054:                 liquidatee,
1055:                 twapTick,
1056:                 positionBalanceArray,
1057:                 premia.leftSlot()
1058:             );

1086:             (netExchanged, premiasByLeg) = _burnAllOptionsFrom(
1087:                 liquidatee,
1088:                 Constants.MIN_V3POOL_TICK,
1089:                 Constants.MAX_V3POOL_TICK,
1090:                 DONOT_COMMIT_LONG_SETTLED,
1091:                 positionIdList
1092:             );

1098:             (liquidationBonus0, liquidationBonus1, collateralRemaining) = PanopticMath
1099:                 .getLiquidationBonus(
1100:                     tokenData0,
1101:                     tokenData1,
1102:                     Math.getSqrtRatioAtTick(twapTick),
1103:                     Math.getSqrtRatioAtTick(finalTick),
1104:                     netExchanged,
1105:                     premia
1106:                 );

1122:             (deltaBonus0, deltaBonus1) = PanopticMath.haircutPremia(
1123:                 _liquidatee,
1124:                 _positionIdList,
1125:                 premiasByLeg,
1126:                 collateralRemaining,
1127:                 s_collateralToken0,
1128:                 s_collateralToken1,
1129:                 Math.getSqrtRatioAtTick(_finalTick),
1130:                 s_settledTokens
1131:             );

1156:             !_checkSolvencyAtTick(
1157:                 msg.sender,
1158:                 positionIdListLiquidator,
1159:                 finalTick,
1160:                 finalTick,
1161:                 BP_DECREASE_BUFFER
1162:             )

1206:             (LeftRightSigned positionPremia, ) = _calculateAccumulatedPremia(
1207:                 account,
1208:                 touchedId,
1209:                 COMPUTE_LONG_PREMIA,
1210:                 ONLY_AVAILABLE_PREMIUM,
1211:                 currentTick
1212:             );

1227:         _burnAllOptionsFrom(account, 0, 0, COMMIT_LONG_SETTLED, touchedId);

1231:         LeftRightSigned exerciseFees = s_collateralToken0.exerciseCost(
1232:             currentTick,
1233:             twapTick,
1234:             touchedId[0],
1235:             positionBalance,
1236:             longAmounts
1237:         );

1242:         refundAmounts = PanopticMath.getRefundAmounts(
1243:             account,
1244:             refundAmounts,
1245:             twapTick,
1246:             s_collateralToken0,
1247:             s_collateralToken1
1248:         );

1277:         emit ForcedExercised(msg.sender, account, touchedId[0], exerciseFees);

1300:         ) = _calculateAccumulatedPremia(
1301:                 account,
1302:                 positionIdList,
1303:                 COMPUTE_ALL_PREMIA,
1304:                 ONLY_AVAILABLE_PREMIUM,
1305:                 currentTick
1306:             );

1308:         LeftRightUnsigned tokenData0 = s_collateralToken0.getAccountMarginDetails(
1309:             account,
1310:             atTick,
1311:             positionBalanceArray,
1312:             portfolioPremium.rightSlot()
1313:         );

1314:         LeftRightUnsigned tokenData1 = s_collateralToken1.getAccountMarginDetails(
1315:             account,
1316:             atTick,
1317:             positionBalanceArray,
1318:             portfolioPremium.leftSlot()
1319:         );

1472:         LeftRightUnsigned accountLiquidities = SFPM.getAccountLiquidity(
1473:             address(s_univ3pool),
1474:             address(this),
1475:             tokenId.tokenType(leg),
1476:             tickLower,
1477:             tickUpper
1478:         );

1528:                 (premiumAccumulatorsByLeg[leg][0], premiumAccumulatorsByLeg[leg][1]) = SFPM
1529:                     .getAccountPremium(
1530:                         address(s_univ3pool),
1531:                         address(this),
1532:                         tokenType,
1533:                         liquidityChunk.tickLower(),
1534:                         liquidityChunk.tickUpper(),
1535:                         atTick,
1536:                         isLong
1537:                     );

1605:             (uint128 premiumAccumulator0, uint128 premiumAccumulator1) = SFPM.getAccountPremium(
1606:                 address(s_univ3pool),
1607:                 address(this),
1608:                 tokenType,
1609:                 tickLower,
1610:                 tickUpper,
1611:                 currentTick,
1612:                 1
1613:             );

1639:             s_collateralToken0.exercise(owner, 0, 0, 0, realizedPremia.rightSlot());

1640:             s_collateralToken1.exercise(owner, 0, 0, 0, realizedPremia.leftSlot());

1707:                 (grossCurrent[0], grossCurrent[1]) = SFPM.getAccountPremium(
1708:                     address(s_univ3pool),
1709:                     address(this),
1710:                     tokenId.tokenType(leg),
1711:                     liquidityChunk.tickLower(),
1712:                     liquidityChunk.tickUpper(),
1713:                     type(int24).max,
1714:                     0
1715:                 );

1811:             LeftRightUnsigned accountLiquidities = SFPM.getAccountLiquidity(
1812:                 address(s_univ3pool),
1813:                 address(this),
1814:                 tokenType,
1815:                 tickLower,
1816:                 tickUpper
1817:             );

1844:         (premiaByLeg, premiumAccumulatorsByLeg) = _getPremia(
1845:             tokenId,
1846:             positionSize,
1847:             owner,
1848:             COMPUTE_ALL_PREMIA,
1849:             type(int24).max
1850:         );

1888:                     LeftRightUnsigned availablePremium = _getAvailablePremium(
1889:                         totalLiquidity + positionLiquidity,
1890:                         settledTokens,
1891:                         grossPremiumLast,
1892:                         LeftRightUnsigned.wrap(uint256(LeftRightSigned.unwrap(legPremia))),
1893:                         premiumAccumulatorsByLeg[leg]
1894:                     );

File: contracts/SemiFungiblePositionManager.sol

413:             SafeTransferLib.safeTransferFrom(
414:                 decoded.poolFeatures.token0,
415:                 decoded.payer,
416:                 msg.sender,
417:                 amount0Owed
418:             );

420:             SafeTransferLib.safeTransferFrom(
421:                 decoded.poolFeatures.token1,
422:                 decoded.payer,
423:                 msg.sender,
424:                 amount1Owed
425:             );

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

488:         (collectedByLeg, totalSwapped) = _validateAndForwardToAMM(
489:             tokenId,
490:             positionSize,
491:             slippageTickLimitLow,
492:             slippageTickLimitHigh,
493:             BURN
494:         );

520:         (collectedByLeg, totalSwapped) = _validateAndForwardToAMM(
521:             tokenId,
522:             positionSize,
523:             slippageTickLimitLow,
524:             slippageTickLimitHigh,
525:             MINT
526:         );

552:         registerTokenTransfer(from, to, TokenId.wrap(id), amount);

555:         super.safeTransferFrom(from, to, id, amount, data);

577:             registerTokenTransfer(from, to, TokenId.wrap(ids[i]), amounts[i]);

584:         super.safeBatchTransferFrom(from, to, ids, amounts, data);

612:                 abi.encodePacked(
613:                     address(univ3pool),
614:                     from,
615:                     id.tokenType(leg),
616:                     liquidityChunk.tickLower(),
617:                     liquidityChunk.tickUpper()
618:                 )

621:                 abi.encodePacked(
622:                     address(univ3pool),
623:                     to,
624:                     id.tokenType(leg),
625:                     liquidityChunk.tickLower(),
626:                     liquidityChunk.tickUpper()
627:                 )

708:         (totalMoved, collectedByLeg, itmAmounts) = _createPositionInAMM(
709:             univ3pool,
710:             tokenId,
711:             positionSize,
712:             isBurn
713:         );

837:             (int256 swap0, int256 swap1) = _univ3pool.swap(
838:                 msg.sender,
839:                 zeroForOne,
840:                 swapAmount,
841:                 zeroForOne
842:                     ? Constants.MIN_V3POOL_SQRT_RATIO + 1
843:                     : Constants.MAX_V3POOL_SQRT_RATIO - 1,
844:                 data
845:             );

910:                 (_moved, _itmAmounts, _collectedSingleLeg) = _createLegInAMM(
911:                     _univ3pool,
912:                     _tokenId,
913:                     _leg,
914:                     liquidityChunk,
915:                     _isBurn
916:                 );

975:             abi.encodePacked(
976:                 address(univ3pool),
977:                 msg.sender,
978:                 tokenType,
979:                 liquidityChunk.tickLower(),
980:                 liquidityChunk.tickUpper()
981:             )

1086:             collectedSingleLeg = _collectAndWritePositionData(
1087:                 liquidityChunk,
1088:                 univ3pool,
1089:                 currentLiquidity,
1090:                 positionKey,
1091:                 moved,
1092:                 isLong
1093:             );

1098:         s_accountFeesBase[positionKey] = _getFeesBase(
1099:             univ3pool,
1100:             updatedLiquidity,
1101:             liquidityChunk,
1102:             true
1103:         );

1123:         (s_accountPremiumOwed[positionKey], s_accountPremiumGross[positionKey]) = LeftRightLibrary
1124:             .addCapped(
1125:                 s_accountPremiumOwed[positionKey],
1126:                 deltaPremiumOwed,
1127:                 s_accountPremiumGross[positionKey],
1128:                 deltaPremiumGross
1129:             );

1203:         (uint256 amount0, uint256 amount1) = univ3pool.mint(
1204:             address(this),
1205:             liquidityChunk.tickLower(),
1206:             liquidityChunk.tickUpper(),
1207:             liquidityChunk.liquidity(),
1208:             mintdata
1209:         );

1268:         LeftRightSigned amountToCollect = _getFeesBase(
1269:             univ3pool,
1270:             startingLiquidity,
1271:             liquidityChunk,
1272:             false
1273:         ).subRect(s_accountFeesBase[positionKey]);

1284:             (uint128 receivedAmount0, uint128 receivedAmount1) = univ3pool.collect(
1285:                 msg.sender,
1286:                 liquidityChunk.tickLower(),
1287:                 liquidityChunk.tickUpper(),
1288:                 uint128(amountToCollect.rightSlot()),
1289:                 uint128(amountToCollect.leftSlot())
1290:             );

1431:             keccak256(abi.encodePacked(univ3pool, owner, tokenType, tickLower, tickUpper))

1459:             abi.encodePacked(univ3pool, owner, tokenType, tickLower, tickUpper)

1480:                     LeftRightSigned feesBase = FeesCalc.calculateAMMSwapFees(
1481:                         _univ3pool,
1482:                         atTick,
1483:                         _tickLower,
1484:                         _tickUpper,
1485:                         netLiquidity
1486:                     );

1507:                 (premiumOwed, premiumGross) = LeftRightLibrary.addCapped(
1508:                     s_accountPremiumOwed[positionKey],
1509:                     premiumOwed,
1510:                     s_accountPremiumGross[positionKey],
1511:                     premiumGross
1512:                 );

1544:             keccak256(abi.encodePacked(univ3pool, owner, tokenType, tickLower, tickUpper))

File: contracts/libraries/FeesCalc.sol

109:         ) = _getAMMSwapFeesPerLiquidityCollected(univ3pool, currentTick, tickLower, tickUpper);
File LinkInstance CountInstance Link
FeesCalc.sol1109

File: contracts/libraries/InteractionHelper.sol

72:                 string.concat(
73:                     prefix,
74:                     " ",
75:                     isToken0 ? symbol0 : symbol1,
76:                     " LP on ",
77:                     symbol0,
78:                     "/",
79:                     symbol1,
80:                     " ",
81:                     Strings.toString(fee),
82:                     "bps"
83:                 );
File LinkInstance CountInstance Link
InteractionHelper.sol172

File: contracts/libraries/PanopticMath.sol

452:             convertCollateralData(tokenData0, tokenData1, tokenType, Math.getSqrtRatioAtTick(tick));

671:                 (uint256 balanceCross, uint256 thresholdCross) = PanopticMath.convertCollateralData(
672:                     tokenData0,
673:                     tokenData1,
674:                     0,
675:                     sqrtPriceX96Twap
676:                 );

856:                 if (haircut0 != 0) collateral0.exercise(_liquidatee, 0, 0, 0, int128(haircut0));

857:                 if (haircut1 != 0) collateral1.exercise(_liquidatee, 0, 0, 0, int128(haircut1));
File LinkInstance CountInstance Links
PanopticMath.sol4452,671,856,857

File: contracts/tokens/ERC1155Minimal.sol

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

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

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

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

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

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

239:         emit TransferSingle(msg.sender, from, address(0), id, amount);
File LinkInstance CountInstance Links
ERC1155Minimal.sol7110,114,161,165,220,224,239

</details>
<a id="n-17"></a> [N-17] Constants in comparisons should appear on the left side
Description:

Constants or literal values in comparisons should appear on the left side of the comparison operator in order to prevent typo bugs. If an operator character is missing, an unintentional variable assignment can occur. For example:

// Intent - compare value to ten
if (voteCount == 10) {
    // do something
}

// Typo - forgot one equal sign causing variable assignment
if (voteCount = 10) {
    // now voteCount equals ten
}

// Preventative style - a missing equal sign will not compile
if (10 == voteCount) {
   // do something
}
Recommendation:

Place constants and literal values on the left side of the comparison operator.

Instances:

There are 182 instances of this issue.

<details><summary>View 182 Instances</summary>
File: contracts/CollateralTracker.sol

/// @audit literal: 0
331:         if (s_panopticPool.numberOfPositions(msg.sender) != 0) revert Errors.PositionCountNotZero();

/// @audit literal: 0
350:         if (s_panopticPool.numberOfPositions(from) != 0) revert Errors.PositionCountNotZero();

/// @audit literal: 0
512:         return s_panopticPool.numberOfPositions(owner) == 0 ? Math.min(available, balance) : 0;

/// @audit literal: 0
575:         return s_panopticPool.numberOfPositions(owner) == 0 ? Math.min(available, balance) : 0;

/// @audit literal: 0
664:                 if (positionId.isLong(leg) == 0) continue;

/// @audit literal: 0
708:                     (tokenType == 0 && currentValue1 < oracleValue1) ||

/// @audit literal: 1
709:                     (tokenType == 1 && currentValue0 < oracleValue0)

/// @audit literal: 0
776:         if (utilization < 0) {

/// @audit literal: 0
976:         if (assets > 0) {

/// @audit literal: 0
1010:             if (tokenToPay > 0) {

/// @audit literal: 0
1018:             } else if (tokenToPay < 0) {

/// @audit literal: 0
1060:             if ((intrinsicValue != 0) && ((shortAmount != 0) || (longAmount != 0))) {

/// @audit literal: 0
1060:             if ((intrinsicValue != 0) && ((shortAmount != 0) || (longAmount != 0))) {

/// @audit literal: 0
1060:             if ((intrinsicValue != 0) && ((shortAmount != 0) || (longAmount != 0))) {

/// @audit literal: 0
1067:             if (tokenToPay > 0) {

/// @audit literal: 0
1075:             } else if (tokenToPay < 0) {

/// @audit literal: 0
1107:             if (intrinsicValue != 0) {

/// @audit literal: 0
1168:         if (positionBalanceArray.length > 0) {

/// @audit literal: 0
1173:             if (premiumAllPositions < 0) {

/// @audit literal: 0
1182:         if (premiumAllPositions > 0) {

/// @audit literal: 0
1325:         uint128 amountMoved = tokenType == 0 ? amountsMoved.rightSlot() : amountsMoved.leftSlot();

/// @audit literal: 0
1328:         int64 utilization = tokenType == 0

/// @audit literal: 0
1339:             if (isLong == 0) {

/// @audit literal: 1
1346:                     ((atTick >= tickUpper) && (tokenType == 1)) || // strike OTM when price >= upperTick for tokenType=1

/// @audit literal: 0
1347:                     ((atTick < tickLower) && (tokenType == 0)) // strike OTM when price < lowerTick for tokenType=0

/// @audit literal: 1
1362:                     uint160 ratio = tokenType == 1 // tokenType

/// @audit literal: 1
1374:                         ((atTick < tickLower) && (tokenType == 1)) || // strike ITM but out of range price < lowerTick for tokenType=1

/// @audit literal: 0
1375:                         ((atTick >= tickUpper) && (tokenType == 0)) // strike ITM but out of range when price >= upperTick for tokenType=0

/// @audit literal: 1
1451:             if (isLong == 1) {

/// @audit literal: 0
1479:         if (isLong == 0) {

/// @audit literal: 1
1488:         } else if (isLong == 1) {

/// @audit literal: 0
1544:                 if (tokenType == 0) {

/// @audit literal: 1
1559:                 if (tokenType == 1) {

/// @audit literal: 0
1582:                 tokenType == 0 ? movedRight : movedLeft,

/// @audit literal: 0
1584:                 tokenType == 0

/// @audit literal: 0
1637:                 uint128(uint64(-int64(poolUtilization0 == 0 ? 1 : poolUtilization0))) +

/// @audit literal: 0
1638:                 (uint128(uint64(-int64(poolUtilization1 == 0 ? 1 : poolUtilization1))) << 64);

File: contracts/PanopticFactory.sol

/// @audit literal: 0
181:         if (amount0Owed > 0)

/// @audit literal: 0
188:         if (amount1Owed > 0)
File LinkInstance CountInstance Links
PanopticFactory.sol2181,188

File: contracts/PanopticPool.sol

/// @audit literal: 0
460:                 if (tokenId.isLong(leg) == 0 && !includePendingPremium) {

/// @audit literal: 0
533:         if (medianData != 0) s_miniMedian = medianData;

/// @audit literal: 0
638:         if (LeftRightUnsigned.unwrap(s_positionBalance[msg.sender][tokenId]) != 0)

/// @audit literal: 0
664:         if (medianData != 0) s_miniMedian = medianData;

/// @audit literal: 1
768:             if (isLong == 1) {

/// @audit literal: 0
865:             if (tokenId.isLong(leg) == 0) {

/// @audit constant: MAX_SLOW_FAST_DELTA
943:         if (Math.abs(int256(fastOracleTick) - slowOracleTick) > MAX_SLOW_FAST_DELTA)

/// @audit constant: MAX_TWAP_DELTA_LIQUIDATION
1035:             if (Math.abs(currentTick - twapTick) > MAX_TWAP_DELTA_LIQUIDATION)

/// @audit literal: 1
1188:         if (touchedId.length != 1) revert Errors.InputListFail();

/// @audit literal: 0
1274:         if (positionIdListExercisor.length > 0)

/// @audit constant: MAX_POSITIONS
1415:         if ((newHash >> 248) > MAX_POSITIONS) revert Errors.TooManyPositionsOpen();

/// @audit literal: 0
1483:         if (netLiquidity == 0) return;

/// @audit literal: 1
1520:             if ((isLong == 1) || computeAllPremia) {

/// @audit literal: 1
1566:                     if (isLong == 1) {

/// @audit literal: 0
1596:         if (tokenId.isLong(legIndex) == 0 || legIndex > 3) revert Errors.NotALongLeg();

/// @audit literal: 3
1596:         if (tokenId.isLong(legIndex) == 0 || legIndex > 3) revert Errors.NotALongLeg();

/// @audit literal: 0
1679:             if (tokenId.isLong(leg) == 0) {

/// @audit literal: 0
1780:                                     (accumulated0 == 0 ? type(uint256).max : accumulated0),

/// @audit literal: 0
1789:                                     (accumulated1 == 0 ? type(uint256).max : accumulated1),

/// @audit literal: 0
1862:             if (LeftRightSigned.unwrap(legPremia) != 0) {

/// @audit literal: 1
1864:                 if (tokenId.isLong(leg) == 1) {

/// @audit literal: 0
1928:                         s_grossPremiumLast[chunkKey] = totalLiquidity != 0

File: contracts/SemiFungiblePositionManager.sol

/// @audit literal: 0
362:         if (s_AddrToPoolIdData[univ3pool] != 0) return;

/// @audit literal: 0
412:         if (amount0Owed > 0)

/// @audit literal: 0
419:         if (amount1Owed > 0)

/// @audit literal: 0
446:         address token = amount0Delta > 0

/// @audit literal: 0
453:         uint256 amountToPay = amount0Delta > 0 ? uint256(amount0Delta) : uint256(amount1Delta);

/// @audit literal: 0
632:                 (LeftRightUnsigned.unwrap(s_accountLiquidity[positionKey_to]) != 0) ||

/// @audit literal: 0
633:                 (LeftRightSigned.unwrap(s_accountFeesBase[positionKey_to]) != 0)

/// @audit literal: 0
688:         if (positionSize == 0) revert Errors.OptionsBalanceZero();

/// @audit constant: IUniswapV3Pool(address(0))
702:         if (univ3pool == IUniswapV3Pool(address(0))) revert Errors.UniswapPoolNotInitialized();

/// @audit literal: 0
717:             if ((LeftRightSigned.unwrap(itmAmounts) != 0)) {

/// @audit literal: 0
787:             if ((itm0 != 0) && (itm1 != 0)) {

/// @audit literal: 0
787:             if ((itm0 != 0) && (itm1 != 0)) {

/// @audit literal: 0
819:                 zeroForOne = net0 < 0;

/// @audit literal: 0
823:             } else if (itm0 != 0) {

/// @audit literal: 0
824:                 zeroForOne = itm0 < 0;

/// @audit literal: 0
827:                 zeroForOne = itm1 > 0;

/// @audit literal: 0
833:             if (swapAmount == 0) return LeftRightSigned.wrap(0);

/// @audit literal: 0
999:             if (isLong == 0) {

/// @audit literal: 0
1066:             moved = isLong == 0

/// @audit literal: 1
1073:             if (tokenType == 1) {

/// @audit literal: 0
1078:             if (tokenType == 0) {

/// @audit literal: 0
1085:         if (currentLiquidity.rightSlot() > 0) {

/// @audit literal: 1
1275:         if (isLong == 1) {

/// @audit literal: 0
1279:         if (LeftRightSigned.unwrap(amountToCollect) != 0) {

/// @audit literal: 0
1296:                 collected0 = movedInLeg.rightSlot() < 0

/// @audit literal: 0
1299:                 collected1 = movedInLeg.leftSlot() < 0

/// @audit literal: 0
1469:             if (netLiquidity != 0) {

/// @audit literal: 1
1514:                 acctPremia = isLong == 1 ? premiumOwed : premiumGross;

/// @audit literal: 1
1518:             acctPremia = isLong == 1

File: contracts/libraries/FeesCalc.sol

/// @audit literal: 0
67:                 if (tokenId.isLong(leg) == 0) {
File LinkInstance CountInstance Link
FeesCalc.sol167

File: contracts/libraries/Math.sol

/// @audit literal: 0
74:         return x > 0 ? x : -x;

/// @audit literal: 0
83:             return x > 0 ? uint256(x) : uint256(-x);

/// @audit literal: 0x100000000000000000000000000000000
93:             if (x >= 0x100000000000000000000000000000000) {

/// @audit literal: 0x10000000000000000
97:             if (x >= 0x10000000000000000) {

/// @audit literal: 0x100000000
101:             if (x >= 0x100000000) {

/// @audit literal: 0x10000
105:             if (x >= 0x10000) {

/// @audit literal: 0x100
109:             if (x >= 0x100) {

/// @audit literal: 0x10
113:             if (x >= 0x10) {

/// @audit literal: 0
130:             uint256 absTick = tick < 0 ? uint256(-int256(tick)) : uint256(int256(tick));

/// @audit constant: uint256(int256(Constants.MAX_V3POOL_TICK))
131:             if (absTick > uint256(int256(Constants.MAX_V3POOL_TICK))) revert Errors.InvalidTick();

/// @audit literal: 0
133:             uint256 sqrtR = absTick & 0x1 != 0

/// @audit literal: 0
137:             if (absTick & 0x2 != 0) sqrtR = (sqrtR * 0xfff97272373d413259a46990580e213a) >> 128;

/// @audit literal: 0
139:             if (absTick & 0x4 != 0) sqrtR = (sqrtR * 0xfff2e50f5f656932ef12357cf3c7fdcc) >> 128;

/// @audit literal: 0
141:             if (absTick & 0x8 != 0) sqrtR = (sqrtR * 0xffe5caca7e10e4e61c3624eaa0941cd0) >> 128;

/// @audit literal: 0
143:             if (absTick & 0x10 != 0) sqrtR = (sqrtR * 0xffcb9843d60f6159c9db58835c926644) >> 128;

/// @audit literal: 0
145:             if (absTick & 0x20 != 0) sqrtR = (sqrtR * 0xff973b41fa98c081472e6896dfb254c0) >> 128;

/// @audit literal: 0
147:             if (absTick & 0x40 != 0) sqrtR = (sqrtR * 0xff2ea16466c96a3843ec78b326b52861) >> 128;

/// @audit literal: 0
149:             if (absTick & 0x80 != 0) sqrtR = (sqrtR * 0xfe5dee046a99a2a811c461f1969c3053) >> 128;

/// @audit literal: 0
151:             if (absTick & 0x100 != 0) sqrtR = (sqrtR * 0xfcbe86c7900a88aedcffc83b479aa3a4) >> 128;

/// @audit literal: 0
153:             if (absTick & 0x200 != 0) sqrtR = (sqrtR * 0xf987a7253ac413176f2b074cf7815e54) >> 128;

/// @audit literal: 0
155:             if (absTick & 0x400 != 0) sqrtR = (sqrtR * 0xf3392b0822b70005940c7a398e4b70f3) >> 128;

/// @audit literal: 0
157:             if (absTick & 0x800 != 0) sqrtR = (sqrtR * 0xe7159475a2c29b7443b29c7fa6e889d9) >> 128;

/// @audit literal: 0
159:             if (absTick & 0x1000 != 0) sqrtR = (sqrtR * 0xd097f3bdfd2022b8845ad8f792aa5825) >> 128;

/// @audit literal: 0
161:             if (absTick & 0x2000 != 0) sqrtR = (sqrtR * 0xa9f746462d870fdf8a65dc1f90e061e5) >> 128;

/// @audit literal: 0
163:             if (absTick & 0x4000 != 0) sqrtR = (sqrtR * 0x70d869a156d2a1b890bb3df62baf32f7) >> 128;

/// @audit literal: 0
165:             if (absTick & 0x8000 != 0) sqrtR = (sqrtR * 0x31be135f97d08fd981231505542fcfa6) >> 128;

/// @audit literal: 0
167:             if (absTick & 0x10000 != 0) sqrtR = (sqrtR * 0x9aa508b5b7a84e1c677de54f3e99bc9) >> 128;

/// @audit literal: 0
169:             if (absTick & 0x20000 != 0) sqrtR = (sqrtR * 0x5d6af8dedb81196699c329225ee604) >> 128;

/// @audit literal: 0
171:             if (absTick & 0x40000 != 0) sqrtR = (sqrtR * 0x2216e584f5fa1ea926041bedfe98) >> 128;

/// @audit literal: 0
173:             if (absTick & 0x80000 != 0) sqrtR = (sqrtR * 0x48a170391f7dc42444e8fa2) >> 128;

/// @audit literal: 0
176:             if (tick > 0) sqrtR = type(uint256).max / sqrtR;

/// @audit literal: 0
179:             return uint160((sqrtR >> 32) + (sqrtR % (1 << 32) == 0 ? 0 : 1));

/// @audit literal: 0
312:         if ((downcastedInt = int128(toCast)) < 0) revert Errors.CastingError();

/// @audit literal: 0
360:             if (prod1 == 0) {

/// @audit literal: 0
361:                 require(denominator > 0);

/// @audit literal: 0
447:             if (mulmod(a, b, denominator) > 0) {

/// @audit literal: 0
474:             if (prod1 == 0) {

/// @audit literal: 0
537:             if (prod1 == 0) {

/// @audit literal: 0
587:             if (mulmod(a, b, 2 ** 96) > 0) {

/// @audit literal: 0
614:             if (prod1 == 0) {

/// @audit literal: 0
664:             if (mulmod(a, b, 2 ** 128) > 0) {

/// @audit literal: 0
691:             if (prod1 == 0) {

File: contracts/libraries/PanopticMath.sol

/// @audit literal: 8
207:                 for (uint8 i; i < 8; ++i) {

/// @audit literal: 7
211:                     if (rank == 7) {

/// @audit literal: 20
248:             for (uint256 i = 0; i < 20; ++i) {

/// @audit literal: 19
256:             for (uint256 i = 0; i < 19; ++i) {

/// @audit literal: 0
325:         if (tokenId.asset(legIndex) == 0) {

/// @audit literal: 0
357:                 tickLower % tickSpacing != 0 ||

/// @audit literal: 0
358:                 tickUpper % tickSpacing != 0 ||

/// @audit literal: 0
425:         if (tokenType == 0) {

/// @audit literal: 0
475:             uint256 notional = asset == 0

/// @audit literal: 0
479:             if (notional == 0 || notional > type(uint128).max) revert Errors.InvalidNotionalValue();

/// @audit literal: 0
532:                 return amount < 0 ? -absResult : absResult;

/// @audit literal: 0
537:                 return amount < 0 ? -absResult : absResult;

/// @audit literal: 0
555:                 return amount < 0 ? -absResult : absResult;

/// @audit literal: 0
564:                 return amount < 0 ? -absResult : absResult;

/// @audit literal: 0
584:         if (tokenId.asset(legIndex) == 0) {

/// @audit literal: 0
615:         bool isShort = tokenId.isLong(legIndex) == 0;

/// @audit literal: 0
618:         if (tokenId.tokenType(legIndex) == 0) {

/// @audit literal: 1
785:                     if (tokenId.isLong(leg) == 1) {

/// @audit literal: 0
856:                 if (haircut0 != 0) collateral0.exercise(_liquidatee, 0, 0, 0, int128(haircut0));

/// @audit literal: 0
857:                 if (haircut1 != 0) collateral1.exercise(_liquidatee, 0, 0, 0, int128(haircut1));

/// @audit literal: 1
864:                     if (tokenId.isLong(leg) == 1) {

/// @audit literal: 0
931:             if (balanceShortage > 0) {

/// @audit literal: 0
949:             if (balanceShortage > 0) {
File LinkInstance CountInstance Links
PanopticMath.sol23207,211,248,256,325,357,358,425,475,479,532,537,555,564,584,615,618,785,856,857,864,931,949

File: contracts/tokens/ERC1155Minimal.sol

/// @audit literal: 0
112:         if (to.code.length != 0) {

/// @audit literal: 0
163:         if (to.code.length != 0) {

/// @audit literal: 0x01ffc9a7
202:             interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165

/// @audit literal: 0xd9b67a26
203:             interfaceId == 0xd9b67a26; // ERC165 Interface ID for ERC1155

/// @audit literal: 0
222:         if (to.code.length != 0) {
File LinkInstance CountInstance Links
ERC1155Minimal.sol5112,163,202,203,222

File: contracts/types/TokenId.sol

/// @audit constant: 2 ** 64
376:             if (optionRatios < 2 ** 64) {

/// @audit constant: 2 ** 112
378:             } else if (optionRatios < 2 ** 112) {

/// @audit constant: 2 ** 160
380:             } else if (optionRatios < 2 ** 160) {

/// @audit constant: 2 ** 208
382:             } else if (optionRatios < 2 ** 208) {

/// @audit constant: 2 ** 64
439:         if (optionRatios < 2 ** 64) {

/// @audit constant: 2 ** 112
441:         } else if (optionRatios < 2 ** 112) {

/// @audit constant: 2 ** 160
443:         } else if (optionRatios < 2 ** 160) {

/// @audit constant: 2 ** 208
445:         } else if (optionRatios < 2 ** 208) {

/// @audit literal: 0
465:         if (i == 0)

/// @audit literal: 1
471:         if (i == 1)

/// @audit literal: 2
477:         if (i == 2)

/// @audit literal: 3
483:         if (i == 3)

/// @audit literal: 0
501:         if (self.optionRatio(0) == 0) revert Errors.InvalidTokenIdParameter(1);

/// @audit literal: 4
507:             for (uint256 i = 0; i < 4; ++i) {

/// @audit literal: 0
508:                 if (self.optionRatio(i) == 0) {

/// @audit literal: 0
512:                     if ((TokenId.unwrap(self) >> (64 + 48 * i)) != 0)

/// @audit literal: 0
528:                 if ((self.width(i) == 0)) revert Errors.InvalidTokenIdParameter(5);

/// @audit constant: Constants.MIN_V3POOL_TICK
531:                     (self.strike(i) == Constants.MIN_V3POOL_TICK) ||

/// @audit constant: Constants.MAX_V3POOL_TICK
532:                     (self.strike(i) == Constants.MAX_V3POOL_TICK)

/// @audit literal: 1
566:                     if (((_isLong != isLongP) || _isLong == 1) && (_tokenType != tokenTypeP))

/// @audit literal: 1
592:                     if (self.isLong(i) == 1) return; // validated
File LinkInstance CountInstance Links
TokenId.sol21376,378,380,382,439,441,443,445,465,471,477,483,501,507,508,512,528,531,532,566,592

</details>
<a id="n-18"></a> [N-18] Contract should expose an interface
Description:

All external/public functions should extend an interface. This is useful to make sure that the whole API is extracted.

Instances:

There are 71 instances of this issue.

<details><summary>View 71 Instances</summary>
File: contracts/CollateralTracker.sol

221:     function startToken(
222:         bool underlyingIsToken0,
223:         address token0,
224:         address token1,
225:         uint24 fee,
226:         PanopticPool panopticPool
227:     ) external {

277:     function getPoolData()
278:         external
279:         view
280:         returns (uint256 poolAssets, uint256 insideAMM, int256 currentPoolUtilization)
281:     {

289:     function name() external view returns (string memory) {

303:     function symbol() external view returns (string memory) {

310:     function decimals() external view returns (uint8) {

361:     function asset() external view returns (address assetTokenAddress) {

370:     function totalAssets() public view returns (uint256 totalManagedAssets) {

379:     function convertToShares(uint256 assets) public view returns (uint256 shares) {

386:     function convertToAssets(uint256 shares) public view returns (uint256 assets) {

392:     function maxDeposit(address) external pure returns (uint256 maxAssets) {

399:     function previewDeposit(uint256 assets) public view returns (uint256 shares) {

417:     function deposit(uint256 assets, address receiver) external returns (uint256 shares) {

444:     function maxMint(address) external view returns (uint256 maxShares) {

453:     function previewMint(uint256 shares) public view returns (uint256 assets) {

477:     function mint(uint256 shares, address receiver) external returns (uint256 assets) {

507:     function maxWithdraw(address owner) public view returns (uint256 maxAssets) {

518:     function previewWithdraw(uint256 assets) public view returns (uint256 shares) {

531:     function withdraw(
532:         uint256 assets,
533:         address receiver,
534:         address owner
535:     ) external returns (uint256 shares) {

572:     function maxRedeem(address owner) public view returns (uint256 maxShares) {

581:     function previewRedeem(uint256 shares) public view returns (uint256 assets) {

591:     function redeem(
592:         uint256 shares,
593:         address receiver,
594:         address owner
595:     ) external returns (uint256 assets) {

650:     function exerciseCost(
651:         int24 currentTick,
652:         int24 oracleTick,
653:         TokenId positionId,
654:         uint128 positionBalance,
655:         LeftRightSigned longAmounts
656:     ) external view returns (LeftRightSigned exerciseFees) {

864:     function delegate(
865:         address delegator,
866:         address delegatee,
867:         uint256 assets
868:     ) external onlyPanopticPool {

894:     function delegate(address delegatee, uint256 assets) external onlyPanopticPool {

903:     function refund(address delegatee, uint256 assets) external onlyPanopticPool {

911:     function revoke(
912:         address delegator,
913:         address delegatee,
914:         uint256 assets
915:     ) external onlyPanopticPool {

975:     function refund(address refunder, address refundee, int256 assets) external onlyPanopticPool {

 995:     function takeCommissionAddData(
 996:         address optionOwner,
 997:         int128 longAmount,
 998:         int128 shortAmount,
 999:         int128 swappedAmount
1000:     ) external onlyPanopticPool returns (int256 utilization) {

1043:     function exercise(
1044:         address optionOwner,
1045:         int128 longAmount,
1046:         int128 shortAmount,
1047:         int128 swappedAmount,
1048:         int128 realizedPremium
1049:     ) external onlyPanopticPool returns (int128) {

1141:     function getAccountMarginDetails(
1142:         address user,
1143:         int24 currentTick,
1144:         uint256[2][] memory positionBalanceArray,
1145:         int128 premiumAllPositions
1146:     ) public view returns (LeftRightUnsigned tokenData) {

File: contracts/PanopticFactory.sol

147:     function transferOwnership(address newOwner) external {

159:     function owner() external view returns (address) {

172:     function uniswapV3MintCallback(
173:         uint256 amount0Owed,
174:         uint256 amount1Owed,
175:         bytes calldata data
176:     ) external {

210:     function deployNewPool(
211:         address token0,
212:         address token1,
213:         uint24 fee,
214:         bytes32 salt
215:     ) external returns (PanopticPool newPoolContract) {

290:     function minePoolAddress(
291:         bytes32 salt,
292:         uint256 loops,
293:         uint256 minTargetRarity
294:     ) external view returns (bytes32 bestSalt, uint256 highestRarity) {

420:     function getPanopticPool(IUniswapV3Pool univ3pool) external view returns (PanopticPool) {
File LinkInstance CountInstance Links
PanopticFactory.sol6147,159,172,210,290,420

File: contracts/PanopticPool.sol

291:     function startPool(
292:         IUniswapV3Pool _univ3pool,
293:         address token0,
294:         address token1,
295:         CollateralTracker collateralTracker0,
296:         CollateralTracker collateralTracker1
297:     ) external {

338:     function assertPriceWithinBounds(uint160 sqrtLowerBound, uint160 sqrtUpperBound) external view {

352:     function optionPositionBalance(
353:         address user,
354:         TokenId tokenId
355:     ) external view returns (uint128 balance, uint64 poolUtilization0, uint64 poolUtilization1) {

381:     function calculateAccumulatedFeesBatch(
382:         address user,
383:         bool includePendingPremium,
384:         TokenId[] calldata positionIdList
385:     ) external view returns (int128 premium0, int128 premium1, uint256[2][] memory) {

410:     function calculatePortfolioValue(
411:         address user,
412:         int24 atTick,
413:         TokenId[] calldata positionIdList
414:     ) external view returns (int256 value0, int256 value1) {

522:     function pokeMedian() external {

547:     function mintOptions(
548:         TokenId[] calldata positionIdList,
549:         uint128 positionSize,
550:         uint64 effectiveLiquidityLimitX32,
551:         int24 tickLimitLow,
552:         int24 tickLimitHigh
553:     ) external {

569:     function burnOptions(
570:         TokenId tokenId,
571:         TokenId[] calldata newPositionIdList,
572:         int24 tickLimitLow,
573:         int24 tickLimitHigh
574:     ) external {

586:     function burnOptions(
587:         TokenId[] calldata positionIdList,
588:         TokenId[] calldata newPositionIdList,
589:         int24 tickLimitLow,
590:         int24 tickLimitHigh
591:     ) external {

1017:     function liquidate(
1018:         TokenId[] calldata positionIdListLiquidator,
1019:         address liquidatee,
1020:         LeftRightUnsigned delegations,
1021:         TokenId[] calldata positionIdList
1022:     ) external {

1179:     function forceExercise(
1180:         address account,
1181:         TokenId[] calldata touchedId,
1182:         TokenId[] calldata positionIdListExercisee,
1183:         TokenId[] calldata positionIdListExercisor
1184:     ) external {

1425:     function univ3pool() external view returns (IUniswapV3Pool) {

1431:     function collateralToken0() external view returns (CollateralTracker collateralToken) {

1437:     function collateralToken1() external view returns (CollateralTracker) {

1444:     function numberOfPositions(address user) public view returns (uint256 _numberOfPositions) {

1587:     function settleLongPremium(
1588:         TokenId[] calldata positionIdList,
1589:         address owner,
1590:         uint256 legIndex
1591:     ) external {
File LinkInstance CountInstance Links
PanopticPool.sol16291,338,352,381,410,522,547,569,586,1017,1179,1425,1431,1437,1444,1587

File: contracts/SemiFungiblePositionManager.sol

350:     function initializeAMMPool(address token0, address token1, uint24 fee) external {

402:     function uniswapV3MintCallback(
403:         uint256 amount0Owed,
404:         uint256 amount1Owed,
405:         bytes calldata data
406:     ) external {

435:     function uniswapV3SwapCallback(
436:         int256 amount0Delta,
437:         int256 amount1Delta,
438:         bytes calldata data
439:     ) external {

471:     function burnTokenizedPosition(
472:         TokenId tokenId,
473:         uint128 positionSize,
474:         int24 slippageTickLimitLow,
475:         int24 slippageTickLimitHigh
476:     )
477:         external
478:         ReentrancyLock(tokenId.poolId())
479:         returns (LeftRightUnsigned[4] memory collectedByLeg, LeftRightSigned totalSwapped)
480:     {

504:     function mintTokenizedPosition(
505:         TokenId tokenId,
506:         uint128 positionSize,
507:         int24 slippageTickLimitLow,
508:         int24 slippageTickLimitHigh
509:     )
510:         external
511:         ReentrancyLock(tokenId.poolId())
512:         returns (LeftRightUnsigned[4] memory collectedByLeg, LeftRightSigned totalSwapped)
513:     {

1421:     function getAccountLiquidity(
1422:         address univ3pool,
1423:         address owner,
1424:         uint256 tokenType,
1425:         int24 tickLower,
1426:         int24 tickUpper
1427:     ) external view returns (LeftRightUnsigned accountLiquidities) {

1449:     function getAccountPremium(
1450:         address univ3pool,
1451:         address owner,
1452:         uint256 tokenType,
1453:         int24 tickLower,
1454:         int24 tickUpper,
1455:         int24 atTick,
1456:         uint256 isLong
1457:     ) external view returns (uint128, uint128) {

1535:     function getAccountFeesBase(
1536:         address univ3pool,
1537:         address owner,
1538:         uint256 tokenType,
1539:         int24 tickLower,
1540:         int24 tickUpper
1541:     ) external view returns (int128 feesBase0, int128 feesBase1) {

1555:     function getUniswapV3PoolFromId(
1556:         uint64 poolId
1557:     ) external view returns (IUniswapV3Pool UniswapV3Pool) {

1566:     function getPoolId(address univ3pool) external view returns (uint64 poolId) {
File LinkInstance CountInstance Links
SemiFungiblePositionManager.sol10350,402,435,471,504,1421,1449,1535,1555,1566

File: contracts/multicall/Multicall.sol

12:     function multicall(bytes[] calldata data) public payable returns (bytes[] memory results) {
File LinkInstance CountInstance Link
Multicall.sol112

File: contracts/tokens/ERC1155Minimal.sol

81:     function setApprovalForAll(address operator, bool approved) public {

 94:     function safeTransferFrom(
 95:         address from,
 96:         address to,
 97:         uint256 id,
 98:         uint256 amount,
 99:         bytes calldata data
100:     ) public virtual {

130:     function safeBatchTransferFrom(
131:         address from,
132:         address to,
133:         uint256[] calldata ids,
134:         uint256[] calldata amounts,
135:         bytes calldata data
136:     ) public virtual {

178:     function balanceOfBatch(
179:         address[] calldata owners,
180:         uint256[] calldata ids
181:     ) public view returns (uint256[] memory balances) {

200:     function supportsInterface(bytes4 interfaceId) public pure returns (bool) {
File LinkInstance CountInstance Links
ERC1155Minimal.sol581,94,130,178,200

File: contracts/tokens/ERC20Minimal.sol

49:     function approve(address spender, uint256 amount) public returns (bool) {

61:     function transfer(address to, uint256 amount) public virtual returns (bool) {

81:     function transferFrom(address from, address to, uint256 amount) public virtual returns (bool) {
File LinkInstance CountInstance Links
ERC20Minimal.sol349,61,81

</details>
<a id="n-19"></a> [N-19] Contract uses both require()/revert() as well as custom errors
Description:

To increase consistency and maintainability, consider using just one method in a single file.

Recommendation:

Use custom errors.

Instances:

There is 1 instance of this issue.

File: contracts/libraries/Math.sol

13: library Math {
File LinkInstance CountInstance Link
Math.sol113

<a id="n-20"></a> [N-20] Custom errors should be used rather than revert()/require()
Description:

Custom errors have been available since Solidity version 0.8.4. Custom errors are more easily processed in try-catch blocks, and are easier to reuse and maintain.

Recommendation:

Refactor revert("description") and require() calls to use custom errors.

Instances:

There are 9 instances of this issue.

<details><summary>View 9 Instances</summary>
File: contracts/libraries/Math.sol

361:                 require(denominator > 0);

370:             require(denominator > prod1);

448:                 require(result < type(uint256).max);

484:             require(2 ** 64 > prod1);

547:             require(2 ** 96 > prod1);

588:                 require(result < type(uint256).max);

624:             require(2 ** 128 > prod1);

665:                 require(result < type(uint256).max);

701:             require(2 ** 192 > prod1);
File LinkInstance CountInstance Links
Math.sol9361,370,448,484,547,588,624,665,701

</details>
<a id="n-21"></a> [N-21] Duplicated revert() checks should be refactored to a modifier or function
Description:

By factoring out the logic to a modifier, if it needs to change it can be done in just one place. The compiler will inline the function, which will avoid JUMP instructions usually associated with functions.

Instances:

There are 2 instances of this issue.

File: contracts/CollateralTracker.sol

/// @audit same revert also defined at contracts/CollateralTracker.sol:418
480:         if (assets > type(uint104).max) revert Errors.DepositTooLarge();
File LinkInstance CountInstance Link
CollateralTracker.sol1480

File: contracts/tokens/ERC1155Minimal.sol

/// @audit same revert also defined at contracts/tokens/ERC1155Minimal.sol:101
137:         if (!(msg.sender == from || isApprovedForAll[from][msg.sender])) revert NotAuthorized();
File LinkInstance CountInstance Link
ERC1155Minimal.sol1137

<a id="n-22"></a> [N-22] else-block not required
Description:

To improve readability and maintainability, one level of nesting can be removed by not having an else block when the if block always returns.

Recommendation:

Drop the else and pull the else block body code up one level when there is a return in the if block. Example:

// Before:
if (foo) {
  return bar1;
} else {
  return bar2;
}

// After:
if (foo) {
  return bar1;
}

return bar2;
Instances:

There are 10 instances of this issue.

<details><summary>View 10 Instances</summary>
File: contracts/SemiFungiblePositionManager.sol

1013:                 if (startingLiquidity < chunkLiquidity) {
1014:                     // the amount we want to move (liquidityChunk.legLiquidity()) out of uniswap is greater than
1015:                     // what the account that owns the liquidity in uniswap has (startingLiquidity)
1016:                     // we must ensure that an account can only move its own liquidity out of uniswap
1017:                     // so we revert in this case
1018:                     revert Errors.NotEnoughLiquidity();
1019:                 } else {
1020:                     // startingLiquidity is >= chunkLiquidity, so no possible underflow
1021:                     unchecked {
1022:                         // we want to move less than what already sits in uniswap, no problem:
1023:                         updatedLiquidity = startingLiquidity - chunkLiquidity;
1024:                     }
1025:                 }
File LinkInstance CountInstance Link
SemiFungiblePositionManager.sol11013

File: contracts/libraries/PanopticMath.sol

325:         if (tokenId.asset(legIndex) == 0) {
326:             return Math.getLiquidityForAmount0(tickLower, tickUpper, amount);
327:         } else {
328:             return Math.getLiquidityForAmount1(tickLower, tickUpper, amount);
329:         }

425:         if (tokenType == 0) {
426:             return (
427:                 tokenData0.rightSlot() + convert1to0(tokenData1.rightSlot(), sqrtPriceX96),
428:                 tokenData0.leftSlot() + convert1to0(tokenData1.leftSlot(), sqrtPriceX96)
429:             );
430:         } else {
431:             return (
432:                 tokenData1.rightSlot() + convert0to1(tokenData0.rightSlot(), sqrtPriceX96),
433:                 tokenData1.leftSlot() + convert0to1(tokenData0.leftSlot(), sqrtPriceX96)
434:             );
435:         }

494:             if (sqrtPriceX96 < type(uint128).max) {
495:                 return Math.mulDiv192(amount, uint256(sqrtPriceX96) ** 2);
496:             } else {
497:                 return Math.mulDiv128(amount, Math.mulDiv64(sqrtPriceX96, sqrtPriceX96));
498:             }

511:             if (sqrtPriceX96 < type(uint128).max) {
512:                 return Math.mulDiv(amount, 2 ** 192, uint256(sqrtPriceX96) ** 2);
513:             } else {
514:                 return Math.mulDiv(amount, 2 ** 128, Math.mulDiv64(sqrtPriceX96, sqrtPriceX96));
515:             }

528:             if (sqrtPriceX96 < type(uint128).max) {
529:                 int256 absResult = Math
530:                     .mulDiv192(Math.absUint(amount), uint256(sqrtPriceX96) ** 2)
531:                     .toInt256();
532:                 return amount < 0 ? -absResult : absResult;
533:             } else {
534:                 int256 absResult = Math
535:                     .mulDiv128(Math.absUint(amount), Math.mulDiv64(sqrtPriceX96, sqrtPriceX96))
536:                     .toInt256();
537:                 return amount < 0 ? -absResult : absResult;
538:             }

551:             if (sqrtPriceX96 < type(uint128).max) {
552:                 int256 absResult = Math
553:                     .mulDiv(Math.absUint(amount), 2 ** 192, uint256(sqrtPriceX96) ** 2)
554:                     .toInt256();
555:                 return amount < 0 ? -absResult : absResult;
556:             } else {
557:                 int256 absResult = Math
558:                     .mulDiv(
559:                         Math.absUint(amount),
560:                         2 ** 128,
561:                         Math.mulDiv64(sqrtPriceX96, sqrtPriceX96)
562:                     )
563:                     .toInt256();
564:                 return amount < 0 ? -absResult : absResult;
565:             }
File LinkInstance CountInstance Links
PanopticMath.sol6325,425,494,511,528,551

File: contracts/types/TokenId.sol

439:         if (optionRatios < 2 ** 64) {
440:             return 0;
441:         } else if (optionRatios < 2 ** 112) {
442:             return 1;
443:         } else if (optionRatios < 2 ** 160) {
444:             return 2;
445:         } else if (optionRatios < 2 ** 208) {
446:             return 3;
447:         }

441:         } else if (optionRatios < 2 ** 112) {
442:             return 1;
443:         } else if (optionRatios < 2 ** 160) {
444:             return 2;
445:         } else if (optionRatios < 2 ** 208) {
446:             return 3;
447:         }

443:         } else if (optionRatios < 2 ** 160) {
444:             return 2;
445:         } else if (optionRatios < 2 ** 208) {
446:             return 3;
447:         }
File LinkInstance CountInstance Links
TokenId.sol3439,441,443

</details>
<a id="n-23"></a> [N-23] Enable IR-based code generation
Description:

Solidity can generate EVM bytecode in two different ways: Either directly from Solidity to EVM opcodes (โ€œold codegenโ€) or through an intermediate representation (โ€œIRโ€) in Yul (โ€œnew codegenโ€ or โ€œIR-based codegenโ€). The IR-based code generator was introduced with an aim to not only allow code generation to be more transparent and auditable but also to enable more powerful optimization passes that span across functions.

Recommendation:

Use --via-ir (command line) or {"viaIR": true} (in standard-json) to take advantage of the benefits of IR-based codegen including extra gas savings. When using Foundry, set the via_ir property to true.

Instances:

There is 1 instance of this issue.

<a id="n-24"></a> [N-24] Events are missing sender information
Description:

When an event is emitted based on a user's action, not being able to filter based on the address that triggered the event makes event processing more difficult, particularly when msg.sender is not tx.origin.

Recommendation:

Include msg.sender in user-triggered event emissions.

Instances:

There are 5 instances of this issue.

File: contracts/PanopticFactory.sol

154:         emit OwnershipTransferred(currentOwner, newOwner);

268:         emit PoolDeployed(
269:             newPoolContract,
270:             v3Pool,
271:             collateralTracker0,
272:             collateralTracker1,
273:             amount0,
274:             amount1
275:         );
File LinkInstance CountInstance Links
PanopticFactory.sol2154,268

File: contracts/PanopticPool.sol

1654:             emit PremiumSettled(owner, tokenId, realizedPremia);
File LinkInstance CountInstance Link
PanopticPool.sol11654

File: contracts/SemiFungiblePositionManager.sol

390:         emit PoolInitialized(univ3pool, poolId);
File LinkInstance CountInstance Link
SemiFungiblePositionManager.sol1390

File: contracts/tokens/ERC20Minimal.sol

94:         emit Transfer(from, to, amount);
File LinkInstance CountInstance Link
ERC20Minimal.sol194

<a id="n-25"></a> [N-25] High cyclomatic complexity
Description:

Consider breaking down these blocks into more manageable units, by splitting things into utility functions, by reducing nesting, and by using early returns.

Instances:

There are 5 instances of this issue.

File: contracts/SemiFungiblePositionManager.sol

958:     function _createLegInAMM(
959:         IUniswapV3Pool univ3pool,
960:         TokenId tokenId,
961:         uint256 leg,
962:         LiquidityChunk liquidityChunk,
963:         bool isBurn
964:     )
965:         internal
966:         returns (
967:             LeftRightSigned moved,
968:             LeftRightSigned itmAmounts,
969:             LeftRightUnsigned collectedSingleLeg
970:         )
971:     {
File LinkInstance CountInstance Link
SemiFungiblePositionManager.sol1958

File: contracts/libraries/Math.sol

128:     function getSqrtRatioAtTick(int24 tick) internal pure returns (uint160) {

753:     function quickSort(int256[] memory arr, int256 left, int256 right) internal pure {
File LinkInstance CountInstance Links
Math.sol2128,753

File: contracts/libraries/PanopticMath.sol

768:     function haircutPremia(
769:         address liquidatee,
770:         TokenId[] memory positionIdList,
771:         LeftRightSigned[4][] memory premiasByLeg,
772:         LeftRightSigned collateralRemaining,
773:         CollateralTracker collateral0,
774:         CollateralTracker collateral1,
775:         uint160 sqrtPriceX96Final,
776:         mapping(bytes32 chunkKey => LeftRightUnsigned settledTokens) storage settledTokens
777:     ) external returns (int256, int256) {
File LinkInstance CountInstance Link
PanopticMath.sol1768

File: contracts/types/TokenId.sol

500:     function validate(TokenId self) internal pure {
File LinkInstance CountInstance Link
TokenId.sol1500

<a id="n-26"></a> [N-26] if-statement can be converted to a ternary
Description:

The code can be made more compact while also increasing readability by converting the following if-statements to ternaries. For example: foo = (x > y) ? a : b;

Instances:

There are 6 instances of this issue.

<details><summary>View 6 Instances</summary>
File: contracts/CollateralTracker.sol

1544:                 if (tokenType == 0) {
1545:                     spreadRequirement = movedRight < movedPartnerRight
1546:                         ? movedPartnerRight - movedRight
1547:                         : movedRight - movedPartnerRight;
1548:                 } else {
1549:                     spreadRequirement = movedLeft < movedPartnerLeft
1550:                         ? movedPartnerLeft - movedLeft
1551:                         : movedLeft - movedPartnerLeft;
1552:                 }
File LinkInstance CountInstance Link
CollateralTracker.sol11544

File: contracts/libraries/FeesCalc.sol

67:                 if (tokenId.isLong(leg) == 0) {
68:                     unchecked {
69:                         value0 += int256(amount0);
70:                         value1 += int256(amount1);
71:                     }
72:                 } else {
73:                     unchecked {
74:                         value0 -= int256(amount0);
75:                         value1 -= int256(amount1);
76:                     }
77:                 }
File LinkInstance CountInstance Link
FeesCalc.sol167

File: contracts/libraries/PanopticMath.sol

325:         if (tokenId.asset(legIndex) == 0) {
326:             return Math.getLiquidityForAmount0(tickLower, tickUpper, amount);
327:         } else {
328:             return Math.getLiquidityForAmount1(tickLower, tickUpper, amount);
329:         }

425:         if (tokenType == 0) {
426:             return (
427:                 tokenData0.rightSlot() + convert1to0(tokenData1.rightSlot(), sqrtPriceX96),
428:                 tokenData0.leftSlot() + convert1to0(tokenData1.leftSlot(), sqrtPriceX96)
429:             );
430:         } else {
431:             return (
432:                 tokenData1.rightSlot() + convert0to1(tokenData0.rightSlot(), sqrtPriceX96),
433:                 tokenData1.leftSlot() + convert0to1(tokenData0.leftSlot(), sqrtPriceX96)
434:             );
435:         }

494:             if (sqrtPriceX96 < type(uint128).max) {
495:                 return Math.mulDiv192(amount, uint256(sqrtPriceX96) ** 2);
496:             } else {
497:                 return Math.mulDiv128(amount, Math.mulDiv64(sqrtPriceX96, sqrtPriceX96));
498:             }

511:             if (sqrtPriceX96 < type(uint128).max) {
512:                 return Math.mulDiv(amount, 2 ** 192, uint256(sqrtPriceX96) ** 2);
513:             } else {
514:                 return Math.mulDiv(amount, 2 ** 128, Math.mulDiv64(sqrtPriceX96, sqrtPriceX96));
515:             }
File LinkInstance CountInstance Links
PanopticMath.sol4325,425,494,511

</details>
<a id="n-27"></a> [N-27] Inconsistent spacing in comments
Description:

Single line comments should be formatted with one space after the two forward slashes, like // Comment here. The following instances lack the space after the comment-starting slashes.

Recommendation:

Add a space after the two forward slashes for single line comments. Incorporate a tool like forge fmt or Prettier in your toolchain to do this automatically.

Instances:

There are 5 instances of this issue.

File: contracts/CollateralTracker.sol

1117:
1118:             //compute total commission amount = commission rate + spread fee
File LinkInstance CountInstance Link
CollateralTracker.sol11117

File: contracts/SemiFungiblePositionManager.sol

609:
610:             //construct the positionKey for the from and to addresses

642:
643:             //update+store liquidity and fee values between accounts

820:
821:                 //compute the swap amount, set as positive (exact input)

989:         {
990:             // did we have liquidity already deployed in Uniswap for this chunk range from some past mint?
File LinkInstance CountInstance Links
SemiFungiblePositionManager.sol4609,642,820,989

<a id="n-28"></a> [N-28] Large contracts should be refactored into multiple, smaller, contracts
Description:

The larger a contract is, the more difficult it can be to comprehend, test, and maintain. Smaller contracts help reduce the cyclomatic complexity of a protocol. For splitting contracts, ask yourself:

  • Which functions belong together? Each set of functions might be best in its own contract.
  • Which functions do not require reading contract state or just a specific subset of the state?
  • Can you split storage and functionality?
Recommendation:

Separate your contracts into multiple, smaller contracts.

Instances:

There are 6 instances of this issue.

<details><summary>View 6 Instances</summary>
File: contracts/CollateralTracker.sol

/// @audit 1,614 lines in contract
36: contract CollateralTracker is ERC20Minimal, Multicall {
File LinkInstance CountInstance Link
CollateralTracker.sol136

File: contracts/PanopticPool.sol

/// @audit 1,954 lines in contract
27: contract PanopticPool is ERC1155Holder, Multicall {
File LinkInstance CountInstance Link
PanopticPool.sol127

File: contracts/SemiFungiblePositionManager.sol

/// @audit 1,497 lines in contract
72: contract SemiFungiblePositionManager is ERC1155, Multicall {
File LinkInstance CountInstance Link
SemiFungiblePositionManager.sol172

File: contracts/libraries/Math.sol

/// @audit 769 lines in contract
13: library Math {
File LinkInstance CountInstance Link
Math.sol113

File: contracts/libraries/PanopticMath.sol

/// @audit 949 lines in contract
18: library PanopticMath {
File LinkInstance CountInstance Link
PanopticMath.sol118

File: contracts/types/TokenId.sol

/// @audit 540 lines in contract
60: library TokenIdLibrary {
File LinkInstance CountInstance Link
TokenId.sol160

</details>
<a id="n-29"></a> [N-29] Local variable shadows state variable
Description:

Note that this is not the same as shadowing, which is reported with a different compiler warning.

Instances:

There are 16 instances of this issue.

<details><summary>View 16 Instances</summary>
File: contracts/CollateralTracker.sol

704:                 uint256 tokenType = positionId.tokenType(leg);

1319:         uint256 tokenType = tokenId.tokenType(index);

1332:         uint256 isLong = tokenId.isLong(index);

1352:                     int24 strike = tokenId.strike(index);

1449:         uint256 isLong = tokenId.isLong(index);

1535:         uint256 tokenType = tokenId.tokenType(index);
File LinkInstance CountInstance Links
CollateralTracker.sol6704,1319,1332,1352,1449,1535

File: contracts/PanopticFactory.sol

391:             int24 tickSpacing = v3Pool.tickSpacing();
File LinkInstance CountInstance Link
PanopticFactory.sol1391

File: contracts/PanopticPool.sol

749:             uint256 isLong = tokenId.isLong(leg);

1519:             uint256 isLong = tokenId.isLong(leg);

1526:                 uint256 tokenType = tokenId.tokenType(leg);

1604:             uint256 tokenType = tokenId.tokenType(legIndex);

1627:         uint256 liquidity = PanopticMath
1628:             .getLiquidityChunk(tokenId, legIndex, s_positionBalance[owner][tokenId].rightSlot())
1629:             .liquidity();

1810:             uint256 tokenType = tokenId.tokenType(leg);
File LinkInstance CountInstance Links
PanopticPool.sol6749,1519,1526,1604,1627,1810

File: contracts/SemiFungiblePositionManager.sol

972:         uint256 tokenType = tokenId.tokenType(leg);

987:         uint256 isLong = tokenId.isLong(leg);
File LinkInstance CountInstance Links
SemiFungiblePositionManager.sol2972,987

File: contracts/libraries/PanopticMath.sol

49:             int24 tickSpacing = IUniswapV3Pool(univ3pool).tickSpacing();
File LinkInstance CountInstance Link
PanopticMath.sol149

</details>
<a id="n-30"></a> [N-30] Missing checks for state variable assignments
Description:

Consider whether reasonable bounds checks for variables would be useful.

Instances:

There are 18 instances of this issue.

<details><summary>View 18 Instances</summary>
File: contracts/CollateralTracker.sol

895:         balanceOf[delegatee] += convertToShares(assets);

904:         balanceOf[delegatee] -= convertToShares(assets);
File LinkInstance CountInstance Links
CollateralTracker.sol2895,904

File: contracts/PanopticPool.sol

654:         s_positionBalance[msg.sender][tokenId] = LeftRightUnsigned
655:             .wrap(0)
656:             .toLeftSlot(poolUtilizations)
657:             .toRightSlot(positionSize);
File LinkInstance CountInstance Link
PanopticPool.sol1654

File: contracts/tokens/ERC1155Minimal.sol

103:         balanceOf[from][id] -= amount;

107:             balanceOf[to][id] += amount;

217:             balanceOf[to][id] += amount;

237:         balanceOf[from][id] -= amount;
File LinkInstance CountInstance Links
ERC1155Minimal.sol4103,107,217,237

File: contracts/tokens/ERC20Minimal.sol

50:         allowance[msg.sender][spender] = amount;

62:         balanceOf[msg.sender] -= amount;

67:             balanceOf[to] += amount;

86:         balanceOf[from] -= amount;

91:             balanceOf[to] += amount;

104:         balanceOf[from] -= amount;

109:             balanceOf[to] += amount;

126:             balanceOf[to] += amount;

128:         totalSupply += amount;

137:         balanceOf[from] -= amount;

142:             totalSupply -= amount;
File LinkInstance CountInstance Links
ERC20Minimal.sol1150,62,67,86,91,104,109,126,128,137,142

</details>
<a id="n-31"></a> [N-31] Missing empty bytes parameter check
Description:

It is important to validate the bytes inputs of functions to ensure they are not empty, especially when they represent crucial data such as addresses, identifiers, or raw data that the contract needs to process. Missing empty bytes checks can lead to unexpected behavior. For example, certain operations might fail, produce incorrect results, or consume unnecessary gas when performed with empty bytes. Also, missing input validation can potentially expose your contract to malicious activity, including exploitation of unhandled edge cases.

Recommendation:

Always validate that bytes parameters are not empty when the logic of the contract requires that they are not.

Instances:

There are 4 instances of this issue.

File: contracts/PanopticFactory.sol

/// @audit not checked: salt
210:     function deployNewPool(
211:         address token0,
212:         address token1,
213:         uint24 fee,
214:         bytes32 salt
215:     ) external returns (PanopticPool newPoolContract) {

/// @audit not checked: salt
290:     function minePoolAddress(
291:         bytes32 salt,
292:         uint256 loops,
293:         uint256 minTargetRarity
294:     ) external view returns (bytes32 bestSalt, uint256 highestRarity) {
File LinkInstance CountInstance Links
PanopticFactory.sol2210,290

File: contracts/SemiFungiblePositionManager.sol

/// @audit not checked: positionKey
1110:     function _updateStoredPremia(
1111:         bytes32 positionKey,
1112:         LeftRightUnsigned currentLiquidity,
1113:         LeftRightUnsigned collectedAmounts
1114:     ) private {

/// @audit not checked: positionKey
1255:     function _collectAndWritePositionData(
1256:         LiquidityChunk liquidityChunk,
1257:         IUniswapV3Pool univ3pool,
1258:         LeftRightUnsigned currentLiquidity,
1259:         bytes32 positionKey,
1260:         LeftRightSigned movedInLeg,
1261:         uint256 isLong
1262:     ) internal returns (LeftRightUnsigned collectedChunk) {
File LinkInstance CountInstance Links
SemiFungiblePositionManager.sol21110,1255

<a id="n-32"></a> [N-32] Missing event emission in initializer
Description:

Consider emitting an event when the contract is initialized to make it easier for to off-chain tools to track the exact point in time when the contract was initialized.

Instances:

There is 1 instance of this issue.

File: contracts/PanopticFactory.sol

134:     function initialize(address _owner) public {
File LinkInstance CountInstance Link
PanopticFactory.sol1134

<a id="n-33"></a> [N-33] Missing event for critical parameter change
Description:

Events help off-chain tools to track changes.

Recommendation:

Emit an event for critical parameter changes, including the old and new values.

Instances:

There is 1 instance of this issue.

File: contracts/PanopticPool.sol

/// @audit state variable changed: s_settledTokens
1666:     function _updateSettlementPostMint(
1667:         TokenId tokenId,
1668:         LeftRightUnsigned[4] memory collectedByLeg,
1669:         uint128 positionSize
1670:     ) internal {
File LinkInstance CountInstance Link
PanopticPool.sol11666

<a id="n-34"></a> [N-34] Multiple address/ID mappings can be combined into a single mapping of an address/ID to a struct, for readability
Description:

Well-organized data structures make code reviews easier, which may lead to fewer bugs. Consider combining related mappings into mappings to structs, so it is clear what data is related.

Instances:

There are 12 instances of this issue.

<details><summary>View 12 Instances</summary>
File: contracts/PanopticPool.sol

245:     mapping(bytes32 chunkKey => LeftRightUnsigned lastGrossPremium) internal s_grossPremiumLast;

251:     mapping(bytes32 chunkKey => LeftRightUnsigned settledTokens) internal s_settledTokens;

272:     mapping(address account => uint256 positionsHash) internal s_positionsHash;
File LinkInstance CountInstance Links
PanopticPool.sol3245,251,272

File: contracts/SemiFungiblePositionManager.sol

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

177:     mapping(bytes32 positionKey => LeftRightUnsigned removedAndNetLiquidity)

287:     mapping(bytes32 positionKey => LeftRightUnsigned accountPremium) private s_accountPremiumOwed;

289:     mapping(bytes32 positionKey => LeftRightUnsigned accountPremium) private s_accountPremiumGross;

295:     mapping(bytes32 positionKey => LeftRightSigned baseFees0And1) internal s_accountFeesBase;
File LinkInstance CountInstance Links
SemiFungiblePositionManager.sol5145,177,287,289,295

File: contracts/libraries/PanopticMath.sol

776:         mapping(bytes32 chunkKey => LeftRightUnsigned settledTokens) storage settledTokens

865:                         mapping(bytes32 chunkKey => LeftRightUnsigned settledTokens)
File LinkInstance CountInstance Links
PanopticMath.sol2776,865

File: contracts/tokens/ERC20Minimal.sol

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

39:     mapping(address owner => mapping(address spender => uint256 allowance)) public allowance;
File LinkInstance CountInstance Links
ERC20Minimal.sol235,39

</details>
<a id="n-35"></a> [N-35] NatSpec: Contract declarations should have @author tags
Description:

The @author tag provides the name of the author of the contract/interface. If there is a comment with the @author tag, verify that the comment block starts with the NatSpec comment indicator, either /// or /**.

Instances:

There are 3 instances of this issue.

File: contracts/CollateralTracker.sol

36: contract CollateralTracker is ERC20Minimal, Multicall {
File LinkInstance CountInstance Link
CollateralTracker.sol136

File: contracts/tokens/interfaces/IDonorNFT.sol

6: interface IDonorNFT {
File LinkInstance CountInstance Link
IDonorNFT.sol16

File: contracts/types/LiquidityChunk.sol

52: library LiquidityChunkLibrary {
File LinkInstance CountInstance Link
LiquidityChunk.sol152

<a id="n-36"></a> [N-36] NatSpec: Contract declarations should have @dev tags
Description:

@dev tags are used to explain to a developer any extra details. If there is a comment with the @dev tag, verify that the comment block starts with the NatSpec comment indicator, either /// or /**.

Instances:

There are 12 instances of this issue.

<details><summary>View 12 Instances</summary>
File: contracts/CollateralTracker.sol

36: contract CollateralTracker is ERC20Minimal, Multicall {
File LinkInstance CountInstance Link
CollateralTracker.sol136

File: contracts/PanopticFactory.sol

26: contract PanopticFactory is Multicall {
File LinkInstance CountInstance Link
PanopticFactory.sol126

File: contracts/libraries/CallbackLib.sol

12: library CallbackLib {
File LinkInstance CountInstance Link
CallbackLib.sol112

File: contracts/libraries/Constants.sol

7: library Constants {
File LinkInstance CountInstance Link
Constants.sol17

File: contracts/libraries/Errors.sol

7: library Errors {
File LinkInstance CountInstance Link
Errors.sol17

File: contracts/libraries/InteractionHelper.sol

17: library InteractionHelper {
File LinkInstance CountInstance Link
InteractionHelper.sol117

File: contracts/libraries/Math.sol

13: library Math {
File LinkInstance CountInstance Link
Math.sol113

File: contracts/libraries/PanopticMath.sol

18: library PanopticMath {
File LinkInstance CountInstance Link
PanopticMath.sol118

File: contracts/tokens/interfaces/IDonorNFT.sol

6: interface IDonorNFT {
File LinkInstance CountInstance Link
IDonorNFT.sol16

File: contracts/types/LeftRight.sol

17: library LeftRightLibrary {
File LinkInstance CountInstance Link
LeftRight.sol117

File: contracts/types/LiquidityChunk.sol

52: library LiquidityChunkLibrary {
File LinkInstance CountInstance Link
LiquidityChunk.sol152

File: contracts/types/TokenId.sol

60: library TokenIdLibrary {
File LinkInstance CountInstance Link
TokenId.sol160

</details>
<a id="n-37"></a> [N-37] NatSpec: Contract declarations should have @title tag
Description:

The @title tag provides a title that describes the contract/interface. If there is a comment with the @title tag, verify that the comment block starts with the NatSpec comment indicator, either /// or /**.

Instances:

There are 5 instances of this issue.

File: contracts/CollateralTracker.sol

36: contract CollateralTracker is ERC20Minimal, Multicall {
File LinkInstance CountInstance Link
CollateralTracker.sol136

File: contracts/libraries/InteractionHelper.sol

17: library InteractionHelper {
File LinkInstance CountInstance Link
InteractionHelper.sol117

File: contracts/libraries/SafeTransferLib.sol

11: library SafeTransferLib {
File LinkInstance CountInstance Link
SafeTransferLib.sol111

File: contracts/tokens/interfaces/IDonorNFT.sol

6: interface IDonorNFT {
File LinkInstance CountInstance Link
IDonorNFT.sol16

File: contracts/types/LiquidityChunk.sol

52: library LiquidityChunkLibrary {
File LinkInstance CountInstance Link
LiquidityChunk.sol152

<a id="n-38"></a> [N-38] NatSpec: Contract declarations should have descriptions
Description:

The Solidity documentation recommends "that Solidity contracts are fully annotated using NatSpec for all public interfaces (everything in the ABI)." NatSpec documentation should be used for improved readability, a better user experience, enhanced auditability, enablement of automated testing and verification, and to promote standardization and interoperability.

Recommendation:

Add NatSpec documentation to all public contracts. It must appear above the contract definition braces in order to be identified by the compiler as NatSpec

Instances:

There is 1 instance of this issue.

File: contracts/tokens/interfaces/IDonorNFT.sol

6: interface IDonorNFT {
File LinkInstance CountInstance Link
IDonorNFT.sol16

<a id="n-39"></a> [N-39] NatSpec: Function declarations should have @notice tag
Description:

@notice tags are used to explain to end users what the function does. If there is a comment with the @notice tag, verify that the comment block starts with the NatSpec comment indicator, either /// or /**.

Instances:

There are 3 instances of this issue.

File: contracts/CollateralTracker.sol

178:     constructor(
179:         uint256 _commissionFee,
180:         uint256 _sellerCollateralRatio,
181:         uint256 _buyerCollateralRatio,
182:         int256 _forceExerciseCost,
183:         uint256 _targetPoolUtilization,
184:         uint256 _saturatedPoolUtilization,
185:         uint256 _ITMSpreadMultiplier
186:     ) {

323:     function transfer(
324:         address recipient,
325:         uint256 amount
326:     ) public override(ERC20Minimal) returns (bool) {

341:     function transferFrom(
342:         address from,
343:         address to,
344:         uint256 amount
345:     ) public override(ERC20Minimal) returns (bool) {
File LinkInstance CountInstance Links
CollateralTracker.sol3178,323,341

<a id="n-40"></a> [N-40] NatSpec: Function declarations should have descriptions
Description:

The Solidity documentation recommends "that Solidity contracts are fully annotated using NatSpec for all public interfaces (everything in the ABI)." NatSpec documentation should be used for improved readability, a better user experience, enhanced auditability, enablement of automated testing and verification, and to promote standardization and interoperability.

Recommendation:

Add NatSpec documentation to all public functions.

Instances:

There is 1 instance of this issue.

File: contracts/CollateralTracker.sol

178:     constructor(
179:         uint256 _commissionFee,
180:         uint256 _sellerCollateralRatio,
181:         uint256 _buyerCollateralRatio,
182:         int256 _forceExerciseCost,
183:         uint256 _targetPoolUtilization,
184:         uint256 _saturatedPoolUtilization,
185:         uint256 _ITMSpreadMultiplier
186:     ) {
File LinkInstance CountInstance Link
CollateralTracker.sol1178

<a id="n-41"></a> [N-41] NatSpec: Invalid NatSpec comment style
Description:

NatSpec must begin with ///, or use /** ... */ syntax.

Instances:

There are 6 instances of this issue.

<details><summary>View 6 Instances</summary>
File: contracts/SemiFungiblePositionManager.sol

358:         // @dev pools can be initialized from a Panoptic pool or by calling initializeAMMPool directly, reverting

360:         // @dev some pools may not be deployable if the poolId has a collision (since we take only 8 bytes)

603:             // @dev see `contracts/types/LiquidityChunk.sol`

836:             // @dev note this triggers our swap callback function

903:                 // @dev see `contracts/types/LiquidityChunk.sol`
File LinkInstance CountInstance Links
SemiFungiblePositionManager.sol5358,360,603,836,903

File: contracts/libraries/PanopticMath.sol

98:         // @dev 0 ^ x = x
File LinkInstance CountInstance Link
PanopticMath.sol198

</details>
<a id="n-42"></a> [N-42] NatSpec: State variable declarations should have descriptions
Description:

NatSpec documentation should be used for improved readability, a better user experience, enhanced auditability, enablement of automated testing and verification, and to promote standardization and interoperability.

Recommendation:

Use @notice for public state variables, and @dev for non-public ones.

Instances:

There are 9 instances of this issue.

<details><summary>View 9 Instances</summary>
File: contracts/PanopticPool.sol

120:     bool internal constant DONOT_COMMIT_LONG_SETTLED = false;

133:     bool internal constant SLOW_ORACLE_UNISWAP_MODE = false;

136:     uint256 internal constant MEDIAN_PERIOD = 60;

156:     int256 internal constant MAX_TWAP_DELTA_LIQUIDATION = 513;

171:     uint256 internal constant BP_DECREASE_BUFFER = 13_333;

174:     uint256 internal constant NO_BUFFER = 10_000;
File LinkInstance CountInstance Links
PanopticPool.sol6120,133,136,156,171,174

File: contracts/SemiFungiblePositionManager.sol

126:     bool internal constant BURN = true;

133:     uint128 private constant VEGOID = 2;

289:     mapping(bytes32 positionKey => LeftRightUnsigned accountPremium) private s_accountPremiumGross;
File LinkInstance CountInstance Links
SemiFungiblePositionManager.sol3126,133,289

</details>
<a id="n-43"></a> [N-43] NatSpec: Use @inheritdoc for overridden functions
Description:

To ensure all NatSpec documentation is provided, @inheritdoc should be used rather than non-standard annotations.

Recommendation:

When overriding a base function from an overridden contract, use the @inheritdoc notation which will copy all missing tags from the base function.

Instances:

There are 4 instances of this issue.

File: contracts/CollateralTracker.sol

319:     /// @dev See {IERC20-transfer}.
320:     /// Requirements:
321:     /// - the caller must have a balance of at least 'amount'.
322:     /// - the msg.sender must not have any position on the panoptic pool
323:     function transfer(
324:         address recipient,
325:         uint256 amount
326:     ) public override(ERC20Minimal) returns (bool) {

336:     /// @dev See {IERC20-transferFrom}.
337:     /// Requirements:
338:     /// - the 'from' must have a balance of at least 'amount'.
339:     /// - the caller must have allowance for 'from' of at least 'amount' tokens.
340:     /// - 'from' must not have any open positions on the panoptic pool.
341:     function transferFrom(
342:         address from,
343:         address to,
344:         uint256 amount
345:     ) public override(ERC20Minimal) returns (bool) {
File LinkInstance CountInstance Links
CollateralTracker.sol2319,336

File: contracts/SemiFungiblePositionManager.sol

533:     /// @notice Transfer a single token from one user to another
534:     /// @dev supports token approvals
535:     /// @param from the user to transfer tokens from
536:     /// @param to the user to transfer tokens to
537:     /// @param id the ERC1155 token id to transfer
538:     /// @param amount the amount of tokens to transfer
539:     /// @param data optional data to include in the receive hook
540:     function safeTransferFrom(
541:         address from,
542:         address to,
543:         uint256 id,
544:         uint256 amount,
545:         bytes calldata data
546:     ) public override {

558:     /// @notice Transfer multiple tokens from one user to another
559:     /// @dev supports token approvals
560:     /// @dev ids and amounts must be of equal length
561:     /// @param from the user to transfer tokens from
562:     /// @param to the user to transfer tokens to
563:     /// @param ids the ERC1155 token ids to transfer
564:     /// @param amounts the amounts of tokens to transfer
565:     /// @param data optional data to include in the receive hook
566:     function safeBatchTransferFrom(
567:         address from,
568:         address to,
569:         uint256[] calldata ids,
570:         uint256[] calldata amounts,
571:         bytes calldata data
572:     ) public override {
File LinkInstance CountInstance Links
SemiFungiblePositionManager.sol2533,558

<a id="n-44"></a> [N-44] NatSpec: Use appropriate tags for state variable declarations
Description:

The NatSpec documentation indicates that @notice should be used for public state variables, and @dev should be used for non-public ones.

Recommendation:

Use @notice for public state variables, and @dev for non-public ones.

Instances:

There are 43 instances of this issue.

<details><summary>View 43 Instances</summary>
File: contracts/CollateralTracker.sol

70:     string internal constant TICKER_PREFIX = "po";

73:     string internal constant NAME_PREFIX = "POPT-V1";

96:     address internal s_univ3token0;

99:     address internal s_univ3token1;

102:     bool internal s_underlyingIsToken0;

136:     uint256 immutable COMMISSION_FEE;

141:     uint256 immutable SELLER_COLLATERAL_RATIO;

145:     uint256 immutable BUYER_COLLATERAL_RATIO;

149:     int256 immutable FORCE_EXERCISE_COST;

154:     uint256 immutable TARGET_POOL_UTIL;

158:     uint256 immutable SATURATED_POOL_UTIL;

162:     uint256 immutable ITM_SPREAD_MULTIPLIER;
File LinkInstance CountInstance Links
CollateralTracker.sol1270,73,96,99,102,136,141,145,149,154,158,162

File: contracts/PanopticFactory.sol

63:     IUniswapV3Factory internal immutable UNIV3_FACTORY;

66:     SemiFungiblePositionManager internal immutable SFPM;

69:     IDonorNFT internal immutable DONOR_NFT;

72:     address internal immutable POOL_REFERENCE;

75:     address internal immutable COLLATERAL_REFERENCE;

78:     address internal immutable WETH;

89:     uint16 internal constant CARDINALITY_INCREASE = 100;

96:     address internal s_owner;

99:     bool internal s_initialized;

102:     mapping(IUniswapV3Pool univ3pool => PanopticPool panopticPool) internal s_getPanopticPool;
File LinkInstance CountInstance Links
PanopticFactory.sol1063,66,69,72,75,78,89,96,99,102

File: contracts/PanopticPool.sol

160:     int256 internal constant MAX_SLOW_FAST_DELTA = 1800;

179:     SemiFungiblePositionManager internal immutable SFPM;
File LinkInstance CountInstance Links
PanopticPool.sol2160,179

File: contracts/SemiFungiblePositionManager.sol

287:     mapping(bytes32 positionKey => LeftRightUnsigned accountPremium) private s_accountPremiumOwed;
File LinkInstance CountInstance Link
SemiFungiblePositionManager.sol1287

File: contracts/libraries/Constants.sol

9:     uint256 internal constant FP96 = 0x1000000000000000000000000;

12:     int24 internal constant MIN_V3POOL_TICK = -887272;

15:     int24 internal constant MAX_V3POOL_TICK = 887272;

18:     uint160 internal constant MIN_V3POOL_SQRT_RATIO = 4295128739;

21:     uint160 internal constant MAX_V3POOL_SQRT_RATIO =
22:         1461446703485210103287273052203988822378723970342;
File LinkInstance CountInstance Links
Constants.sol59,12,15,18,21

File: contracts/libraries/Math.sol

15:     uint256 internal constant MAX_UINT256 = 2 ** 256 - 1;
File LinkInstance CountInstance Link
Math.sol115

File: contracts/libraries/PanopticMath.sol

23:     uint256 internal constant MAX_UINT256 = 2 ** 256 - 1;

26:     uint64 internal constant TICKSPACING_MASK = 0xFFFF000000000000;
File LinkInstance CountInstance Links
PanopticMath.sol223,26

File: contracts/types/LeftRight.sol

22:     uint256 internal constant LEFT_HALF_BIT_MASK =
23:         0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000000000000000000000000000;

26:     int256 internal constant LEFT_HALF_BIT_MASK_INT =
27:         int256(uint256(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000000000000000000000000000));

30:     int256 internal constant RIGHT_HALF_BIT_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
File LinkInstance CountInstance Links
LeftRight.sol322,26,30

File: contracts/types/LiquidityChunk.sol

54:     uint256 internal constant CLEAR_TL_MASK =
55:         0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;

58:     uint256 internal constant CLEAR_TU_MASK =
59:         0xFFFFFF000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
File LinkInstance CountInstance Links
LiquidityChunk.sol254,58

File: contracts/types/TokenId.sol

62:     uint256 internal constant LONG_MASK =
63:         0x100_000000000100_000000000100_000000000100_0000000000000000;

66:     uint256 internal constant CLEAR_POOLID_MASK =
67:         0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF_0000000000000000;

70:     uint256 internal constant OPTION_RATIO_MASK =
71:         0x0000000000FE_0000000000FE_0000000000FE_0000000000FE_0000000000000000;

74:     uint256 internal constant CHUNK_MASK =
75:         0xFFFFFFFFF200_FFFFFFFFF200_FFFFFFFFF200_FFFFFFFFF200_0000000000000000;

78:     int256 internal constant BITMASK_INT24 = 0xFFFFFF;
File LinkInstance CountInstance Links
TokenId.sol562,66,70,74,78

</details>
<a id="n-45"></a> [N-45] Non-library/interface files should use fixed compiler versions, not floating ones
Description:

Avoid floating pragmas for non-library/interface contracts. While floating pragmas can make sense for libraries to allow them to be included with multiple different versions of applications, it may be a security risk for application implementations. A known vulnerable compiler version may accidentally be selected or security tools might fall-back to an older compiler version. It is recommended to pin to a concrete compiler version. See Locking Pragmas for more information.

Instances:

There are 7 instances of this issue.

<details><summary>View 7 Instances</summary>
File: contracts/CollateralTracker.sol

2: pragma solidity ^0.8.18;
File LinkInstance CountInstance Link
CollateralTracker.sol12

File: contracts/PanopticFactory.sol

2: pragma solidity ^0.8.18;
File LinkInstance CountInstance Link
PanopticFactory.sol12

File: contracts/PanopticPool.sol

2: pragma solidity ^0.8.18;
File LinkInstance CountInstance Link
PanopticPool.sol12

File: contracts/SemiFungiblePositionManager.sol

2: pragma solidity ^0.8.18;
File LinkInstance CountInstance Link
SemiFungiblePositionManager.sol12

File: contracts/multicall/Multicall.sol

2: pragma solidity ^0.8.18;
File LinkInstance CountInstance Link
Multicall.sol12

File: contracts/tokens/ERC1155Minimal.sol

2: pragma solidity ^0.8.0;
File LinkInstance CountInstance Link
ERC1155Minimal.sol12

File: contracts/tokens/ERC20Minimal.sol

2: pragma solidity ^0.8.0;
File LinkInstance CountInstance Link
ERC20Minimal.sol12

</details>
<a id="n-46"></a> [N-46] Not using the named return variables anywhere in the function is confusing
Description:

When specifying a named return variable, the variable should be used within the function.

Recommendation:

Since the return variable is never assigned, nor is it returned by name, consider changing the return value to be unnamed. If the optimizer is not turned on, leaving the code as it is will also waste gas for the stack variable.

Instances:

There are 15 instances of this issue.

<details><summary>View 15 Instances</summary>
File: contracts/CollateralTracker.sol

/// @audit unused return variable: assetTokenAddress
361:     function asset() external view returns (address assetTokenAddress) {

/// @audit unused return variable: totalManagedAssets
370:     function totalAssets() public view returns (uint256 totalManagedAssets) {

/// @audit unused return variable: shares
379:     function convertToShares(uint256 assets) public view returns (uint256 shares) {

/// @audit unused return variable: assets
386:     function convertToAssets(uint256 shares) public view returns (uint256 assets) {

/// @audit unused return variable: maxAssets
392:     function maxDeposit(address) external pure returns (uint256 maxAssets) {

/// @audit unused return variable: maxShares
444:     function maxMint(address) external view returns (uint256 maxShares) {

/// @audit unused return variable: maxAssets
507:     function maxWithdraw(address owner) public view returns (uint256 maxAssets) {

/// @audit unused return variable: shares
518:     function previewWithdraw(uint256 assets) public view returns (uint256 shares) {

/// @audit unused return variable: maxShares
572:     function maxRedeem(address owner) public view returns (uint256 maxShares) {

/// @audit unused return variable: assets
581:     function previewRedeem(uint256 shares) public view returns (uint256 assets) {

/// @audit unused return variable: poolUtilization
741:     function _poolUtilization() internal view returns (int256 poolUtilization) {

/// @audit unused return variable: sellCollateralRatio
751:     function _sellCollateralRatio(
752:         int256 utilization
753:     ) internal view returns (uint256 sellCollateralRatio) {
File LinkInstance CountInstance Links
CollateralTracker.sol12361,370,379,386,392,444,507,518,572,581,741,751

File: contracts/PanopticPool.sol

/// @audit unused return variables: premium0, premium1
381:     function calculateAccumulatedFeesBatch(
382:         address user,
383:         bool includePendingPremium,
384:         TokenId[] calldata positionIdList
385:     ) external view returns (int128 premium0, int128 premium1, uint256[2][] memory) {

/// @audit unused return variable: collateralToken
1431:     function collateralToken0() external view returns (CollateralTracker collateralToken) {
File LinkInstance CountInstance Links
PanopticPool.sol2381,1431

File: contracts/SemiFungiblePositionManager.sol

/// @audit unused return variable: UniswapV3Pool
1555:     function getUniswapV3PoolFromId(
1556:         uint64 poolId
1557:     ) external view returns (IUniswapV3Pool UniswapV3Pool) {
File LinkInstance CountInstance Link
SemiFungiblePositionManager.sol11555

</details>
<a id="n-47"></a> [N-47] Numeric values having to do with time should use time units for readability
Description:

There are time unit suffixes (seconds, minutes, hours, days and weeks) that can be used after literal numbers to specify units of time where seconds are the base unit. Using these suffixes increases the readability and maintainability of the code. For example, uint256 internal constant TRADING_DELAY = 6 hours; is more readable and easier to maintain than uint256 internal constant TRADING_DELAY = 6 * 60 * 60;

Recommendation:

Whenever specifying units of time, use the built in time suffixes.

Instances:

There are 8 instances of this issue.

<details><summary>View 8 Instances</summary>
File: contracts/PanopticPool.sol

/// @audit 60
136:     uint256 internal constant MEDIAN_PERIOD = 60;

/// @audit 7
148:     uint256 internal constant SLOW_ORACLE_CARDINALITY = 7;

/// @audit 24
313:                 (uint256(uint24(currentTick)) << 24) + // add to slot 4
File LinkInstance CountInstance Links
PanopticPool.sol3136,148,313

File: contracts/libraries/PanopticMath.sol

/// @audit 24
178:                 (int24(uint24(medianData >> ((uint24(medianData >> (192 + 3 * 3)) % 8) * 24))) +

/// @audit 24
179:                     int24(uint24(medianData >> ((uint24(medianData >> (192 + 3 * 4)) % 8) * 24)))) /

/// @audit 7
211:                     if (rank == 7) {

/// @audit 24
217:                     entry = int24(uint24(medianData >> (rank * 24)));

/// @audit 24
229:                     uint256(uint192(medianData << 24)) +
File LinkInstance CountInstance Links
PanopticMath.sol5178,179,211,217,229

</details>
<a id="n-48"></a> [N-48] Polymorphic functions make security audits more time-consuming and error-prone
Description:

In Solidity, while function overriding allows for functions with the same name to coexist, it is advisable to avoid this practice to enhance code readability and maintainability. Having multiple functions with the same name, even with different parameters or in inherited contracts, can cause confusion and increase the likelihood of errors during development, testing, and debugging.

Recommendation:

Consider using distinct and descriptive function names to clarify the purpose and behavior of each function, and to help prevent unintended function calls or incorrect overriding.

Instances:

There are 31 instances of this issue.

<details><summary>View 31 Instances</summary>
File: contracts/CollateralTracker.sol

864:     function delegate(
865:         address delegator,
866:         address delegatee,
867:         uint256 assets
868:     ) external onlyPanopticPool {

894:     function delegate(address delegatee, uint256 assets) external onlyPanopticPool {

903:     function refund(address delegatee, uint256 assets) external onlyPanopticPool {

975:     function refund(address refunder, address refundee, int256 assets) external onlyPanopticPool {
File LinkInstance CountInstance Links
CollateralTracker.sol4864,894,903,975

File: contracts/PanopticPool.sol

569:     function burnOptions(
570:         TokenId tokenId,
571:         TokenId[] calldata newPositionIdList,
572:         int24 tickLimitLow,
573:         int24 tickLimitHigh
574:     ) external {

586:     function burnOptions(
587:         TokenId[] calldata positionIdList,
588:         TokenId[] calldata newPositionIdList,
589:         int24 tickLimitLow,
590:         int24 tickLimitHigh
591:     ) external {
File LinkInstance CountInstance Links
PanopticPool.sol2569,586

File: contracts/libraries/Math.sol

41:     function min(uint256 a, uint256 b) internal pure returns (uint256) {

49:     function min(int256 a, int256 b) internal pure returns (int256) {

57:     function max(uint256 a, uint256 b) internal pure returns (uint256) {

65:     function max(int256 a, int256 b) internal pure returns (int256) {

311:     function toInt128(uint128 toCast) internal pure returns (int128 downcastedInt) {

318:     function toInt128(int256 toCast) internal pure returns (int128 downcastedInt) {
File LinkInstance CountInstance Links
Math.sol641,49,57,65,311,318

File: contracts/libraries/PanopticMath.sol

419:     function convertCollateralData(
420:         LeftRightUnsigned tokenData0,
421:         LeftRightUnsigned tokenData1,
422:         uint256 tokenType,
423:         uint160 sqrtPriceX96
424:     ) internal pure returns (uint256, uint256) {

445:     function convertCollateralData(
446:         LeftRightUnsigned tokenData0,
447:         LeftRightUnsigned tokenData1,
448:         uint256 tokenType,
449:         int24 tick
450:     ) internal pure returns (uint256, uint256) {

490:     function convert0to1(uint256 amount, uint160 sqrtPriceX96) internal pure returns (uint256) {

507:     function convert1to0(uint256 amount, uint160 sqrtPriceX96) internal pure returns (uint256) {

524:     function convert0to1(int256 amount, uint160 sqrtPriceX96) internal pure returns (int256) {

547:     function convert1to0(int256 amount, uint160 sqrtPriceX96) internal pure returns (int256) {
File LinkInstance CountInstance Links
PanopticMath.sol6419,445,490,507,524,547

File: contracts/types/LeftRight.sol

39:     function rightSlot(LeftRightUnsigned self) internal pure returns (uint128) {

46:     function rightSlot(LeftRightSigned self) internal pure returns (int128) {

59:     function toRightSlot(
60:         LeftRightUnsigned self,
61:         uint128 right
62:     ) internal pure returns (LeftRightUnsigned) {

78:     function toRightSlot(
79:         LeftRightSigned self,
80:         int128 right
81:     ) internal pure returns (LeftRightSigned) {

101:     function leftSlot(LeftRightUnsigned self) internal pure returns (uint128) {

108:     function leftSlot(LeftRightSigned self) internal pure returns (int128) {

121:     function toLeftSlot(
122:         LeftRightUnsigned self,
123:         uint128 left
124:     ) internal pure returns (LeftRightUnsigned) {

134:     function toLeftSlot(LeftRightSigned self, int128 left) internal pure returns (LeftRightSigned) {

148:     function add(
149:         LeftRightUnsigned x,
150:         LeftRightUnsigned y
151:     ) internal pure returns (LeftRightUnsigned z) {

171:     function sub(
172:         LeftRightUnsigned x,
173:         LeftRightUnsigned y
174:     ) internal pure returns (LeftRightUnsigned z) {

194:     function add(LeftRightUnsigned x, LeftRightSigned y) internal pure returns (LeftRightSigned z) {

214:     function add(LeftRightSigned x, LeftRightSigned y) internal pure returns (LeftRightSigned z) {

232:     function sub(LeftRightSigned x, LeftRightSigned y) internal pure returns (LeftRightSigned z) {
File LinkInstance CountInstance Links
LeftRight.sol1339,46,59,78,101,108,121,134,148,171,194,214,232

</details>
<a id="n-49"></a> [N-49] Setters should prevent re-setting of the same value
Description:

When setting the value of a state variable, validation should be added to prevent resetting it to its current value. This is particularly important if the setter also emits the new (same) value, which may be confusing to off-chain tools that parse events.

Instances:

There are 20 instances of this issue.

<details><summary>View 20 Instances</summary>
File: contracts/CollateralTracker.sol

/// @audit state variables: s_underlyingIsToken0, s_univ3token0, s_univ3token1, s_panopticPool
221:     function startToken(
222:         bool underlyingIsToken0,
223:         address token0,
224:         address token1,
225:         uint24 fee,
226:         PanopticPool panopticPool
227:     ) external {

/// @audit state variable: s_poolAssets
417:     function deposit(uint256 assets, address receiver) external returns (uint256 shares) {

/// @audit state variable: s_poolAssets
531:     function withdraw(
532:         uint256 assets,
533:         address receiver,
534:         address owner
535:     ) external returns (uint256 shares) {

/// @audit state variable: balanceOf
894:     function delegate(address delegatee, uint256 assets) external onlyPanopticPool {

/// @audit state variable: balanceOf
903:     function refund(address delegatee, uint256 assets) external onlyPanopticPool {
File LinkInstance CountInstance Links
CollateralTracker.sol5221,417,531,894,903

File: contracts/PanopticFactory.sol

/// @audit state variable: s_owner
147:     function transferOwnership(address newOwner) external {
File LinkInstance CountInstance Link
PanopticFactory.sol1147

File: contracts/PanopticPool.sol

/// @audit state variables: s_univ3pool, s_collateralToken0, s_collateralToken1
291:     function startPool(
292:         IUniswapV3Pool _univ3pool,
293:         address token0,
294:         address token1,
295:         CollateralTracker collateralTracker0,
296:         CollateralTracker collateralTracker1
297:     ) external {

/// @audit state variable: s_positionBalance
614:     function _mintOptions(
615:         TokenId[] calldata positionIdList,
616:         uint128 positionSize,
617:         uint64 effectiveLiquidityLimitX32,
618:         int24 tickLimitLow,
619:         int24 tickLimitHigh
620:     ) internal {

/// @audit state variable: s_settledTokens
1666:     function _updateSettlementPostMint(
1667:         TokenId tokenId,
1668:         LeftRightUnsigned[4] memory collectedByLeg,
1669:         uint128 positionSize
1670:     ) internal {
File LinkInstance CountInstance Links
PanopticPool.sol3291,614,1666

File: contracts/SemiFungiblePositionManager.sol

/// @audit state variable: s_accountFeesBase
958:     function _createLegInAMM(
959:         IUniswapV3Pool univ3pool,
960:         TokenId tokenId,
961:         uint256 leg,
962:         LiquidityChunk liquidityChunk,
963:         bool isBurn
964:     )
965:         internal
966:         returns (
967:             LeftRightSigned moved,
968:             LeftRightSigned itmAmounts,
969:             LeftRightUnsigned collectedSingleLeg
970:         )
971:     {
File LinkInstance CountInstance Link
SemiFungiblePositionManager.sol1958

File: contracts/tokens/ERC1155Minimal.sol

/// @audit state variable: isApprovedForAll
81:     function setApprovalForAll(address operator, bool approved) public {

/// @audit state variable: balanceOf
 94:     function safeTransferFrom(
 95:         address from,
 96:         address to,
 97:         uint256 id,
 98:         uint256 amount,
 99:         bytes calldata data
100:     ) public virtual {

/// @audit state variable: balanceOf
214:     function _mint(address to, uint256 id, uint256 amount) internal {

/// @audit state variable: balanceOf
236:     function _burn(address from, uint256 id, uint256 amount) internal {
File LinkInstance CountInstance Links
ERC1155Minimal.sol481,94,214,236

File: contracts/tokens/ERC20Minimal.sol

/// @audit state variable: allowance
49:     function approve(address spender, uint256 amount) public returns (bool) {

/// @audit state variable: balanceOf
61:     function transfer(address to, uint256 amount) public virtual returns (bool) {

/// @audit state variable: balanceOf
81:     function transferFrom(address from, address to, uint256 amount) public virtual returns (bool) {

/// @audit state variable: balanceOf
103:     function _transferFrom(address from, address to, uint256 amount) internal {

/// @audit state variable: balanceOf
122:     function _mint(address to, uint256 amount) internal {

/// @audit state variable: balanceOf
136:     function _burn(address from, uint256 amount) internal {
File LinkInstance CountInstance Links
ERC20Minimal.sol649,61,81,103,122,136

</details>
<a id="n-50"></a> [N-50] String literals used more than once should be replaced with a constant
Description:

Using a constant for string literals used more than once will make the code more maintainable. If the string needs to change, it will only have to be changed in one place.

Instances:

There are 5 instances of this issue.

File: contracts/libraries/InteractionHelper.sol

/// @audit "???" also defined at contracts/libraries/InteractionHelper.sol:68
63:             symbol0 = "???";

/// @audit "???" also defined at contracts/libraries/InteractionHelper.sol:63
68:             symbol1 = "???";

/// @audit " " also defined at contracts/libraries/InteractionHelper.sol:80
74:                     " ",

/// @audit " " also defined at contracts/libraries/InteractionHelper.sol:74
80:                     " ",

/// @audit "???" also defined at contracts/libraries/InteractionHelper.sol:63
100:             return string.concat(prefix, "???");
File LinkInstance CountInstance Links
InteractionHelper.sol563,68,74,80,100

<a id="n-51"></a> [N-51] Style guide: Horizontal whitespace around binary operators
Description:

According to the Solidity style guide there should be one space around a binary operator. For example, use x + y instead of x+y.

Recommendation:

Add a space surround all binary operators. Incorporate a tool like forge fmt or Prettier in your toolchain to do this automatically.

Instances:

There is 1 instance of this issue.

File: contracts/PanopticPool.sol

/// @audit operator: >
1415:         if ((newHash >> 248) > MAX_POSITIONS) revert Errors.TooManyPositionsOpen();
File LinkInstance CountInstance Link
PanopticPool.sol11415

<a id="n-52"></a> [N-52] Style guide: Modifier names should use lowerCamelCase
Description:

According to the Solidity style guide modifier names should use mixedCase (lowerCamelCase).

Instances:

There is 1 instance of this issue.

File: contracts/SemiFungiblePositionManager.sol

305:     modifier ReentrancyLock(uint64 poolId) {
File LinkInstance CountInstance Link
SemiFungiblePositionManager.sol1305

<a id="n-53"></a> [N-53] Style guide: State and local variables should be named using lowerCamelCase
Description:

According to the Solidity style guide local and state variable names should use mixedCase (lowerCamelCase). Note: While OpenZeppelin may not follow this advice, it still is the recommended way of naming variables.

Instances:

There are 52 instances of this issue.

<details><summary>View 52 Instances</summary>
File: contracts/CollateralTracker.sol

/// @audit s_underlyingToken
89:     address internal s_underlyingToken;

/// @audit s_initialized
93:     bool internal s_initialized;

/// @audit s_univ3token0
96:     address internal s_univ3token0;

/// @audit s_univ3token1
99:     address internal s_univ3token1;

/// @audit s_underlyingIsToken0
102:     bool internal s_underlyingIsToken0;

/// @audit s_panopticPool
109:     PanopticPool internal s_panopticPool;

/// @audit s_poolAssets
112:     uint128 internal s_poolAssets;

/// @audit s_inAMM
115:     uint128 internal s_inAMM;

/// @audit s_ITMSpreadFee
121:     uint128 internal s_ITMSpreadFee;

/// @audit s_poolFee
124:     uint24 internal s_poolFee;

/// @audit _ITMSpreadMultiplier
185:         uint256 _ITMSpreadMultiplier

/// @audit min_sell_ratio
773:         uint256 min_sell_ratio = SELLER_COLLATERAL_RATIO;
File LinkInstance CountInstance Links
CollateralTracker.sol1289,93,96,99,102,109,112,115,121,124,185,773

File: contracts/PanopticFactory.sol

/// @audit s_owner
96:     address internal s_owner;

/// @audit s_initialized
99:     bool internal s_initialized;

/// @audit s_getPanopticPool
102:     mapping(IUniswapV3Pool univ3pool => PanopticPool panopticPool) internal s_getPanopticPool;

/// @audit _WETH9
116:         address _WETH9,

/// @audit _SFPM
117:         SemiFungiblePositionManager _SFPM,
File LinkInstance CountInstance Links
PanopticFactory.sol596,99,102,116,117

File: contracts/PanopticPool.sol

/// @audit s_univ3pool
186:     IUniswapV3Pool internal s_univ3pool;

/// @audit s_miniMedian
225:     uint256 internal s_miniMedian;

/// @audit s_collateralToken0
231:     CollateralTracker internal s_collateralToken0;

/// @audit s_collateralToken1
233:     CollateralTracker internal s_collateralToken1;

/// @audit s_options
238:     mapping(address account => mapping(TokenId tokenId => mapping(uint256 leg => LeftRightUnsigned premiaGrowth)))
239:         internal s_options;

/// @audit s_grossPremiumLast
245:     mapping(bytes32 chunkKey => LeftRightUnsigned lastGrossPremium) internal s_grossPremiumLast;

/// @audit s_settledTokens
251:     mapping(bytes32 chunkKey => LeftRightUnsigned settledTokens) internal s_settledTokens;

/// @audit s_positionBalance
258:     mapping(address account => mapping(TokenId tokenId => LeftRightUnsigned balanceAndUtilizations))
259:         internal s_positionBalance;

/// @audit s_positionsHash
272:     mapping(address account => uint256 positionsHash) internal s_positionsHash;

/// @audit c_user
439:         address c_user = user;
File LinkInstance CountInstance Links
PanopticPool.sol10186,225,231,233,238,245,251,258,272,439

File: contracts/SemiFungiblePositionManager.sol

/// @audit s_AddrToPoolIdData
145:     mapping(address univ3pool => uint256 poolIdData) internal s_AddrToPoolIdData;

/// @audit s_poolContext
150:     mapping(uint64 poolId => PoolAddressAndLock contextData) internal s_poolContext;

/// @audit s_accountLiquidity
177:     mapping(bytes32 positionKey => LeftRightUnsigned removedAndNetLiquidity)
178:         internal s_accountLiquidity;

/// @audit s_accountPremiumOwed
287:     mapping(bytes32 positionKey => LeftRightUnsigned accountPremium) private s_accountPremiumOwed;

/// @audit s_accountPremiumGross
289:     mapping(bytes32 positionKey => LeftRightUnsigned accountPremium) private s_accountPremiumGross;

/// @audit s_accountFeesBase
295:     mapping(bytes32 positionKey => LeftRightSigned baseFees0And1) internal s_accountFeesBase;

/// @audit positionKey_from
611:             bytes32 positionKey_from = keccak256(

/// @audit positionKey_to
620:             bytes32 positionKey_to = keccak256(

/// @audit premium0X64_base
1342:             uint256 premium0X64_base;

/// @audit premium1X64_base
1343:             uint256 premium1X64_base;

/// @audit premium0X64_owed
1363:                 uint128 premium0X64_owed;

/// @audit premium1X64_owed
1364:                 uint128 premium1X64_owed;

/// @audit premium0X64_gross
1384:                 uint128 premium0X64_gross;

/// @audit premium1X64_gross
1385:                 uint128 premium1X64_gross;

/// @audit UniswapV3Pool
1557:     ) external view returns (IUniswapV3Pool UniswapV3Pool) {

File: contracts/libraries/PanopticMath.sol

/// @audit timestamp_old
186:                     (uint256 timestamp_old, int56 tickCumulative_old, , ) = univ3pool.observations(

/// @audit tickCumulative_old
186:                     (uint256 timestamp_old, int56 tickCumulative_old, , ) = univ3pool.observations(

/// @audit timestamp_last
192:                     (uint256 timestamp_last, int56 tickCumulative_last, , ) = univ3pool

/// @audit tickCumulative_last
192:                     (uint256 timestamp_last, int56 tickCumulative_last, , ) = univ3pool
File LinkInstance CountInstance Links
PanopticMath.sol4186,186,192,192

File: contracts/types/LeftRight.sol

/// @audit z_xR
285:         uint128 z_xR = (uint256(x.rightSlot()) + dx.rightSlot()).toUint128Capped();

/// @audit z_xL
286:         uint128 z_xL = (uint256(x.leftSlot()) + dx.leftSlot()).toUint128Capped();

/// @audit z_yR
287:         uint128 z_yR = (uint256(y.rightSlot()) + dy.rightSlot()).toUint128Capped();

/// @audit z_yL
288:         uint128 z_yL = (uint256(y.leftSlot()) + dy.leftSlot()).toUint128Capped();

/// @audit r_Enabled
290:         bool r_Enabled = !(z_xR == type(uint128).max || z_yR == type(uint128).max);

/// @audit l_Enabled
291:         bool l_Enabled = !(z_xL == type(uint128).max || z_yL == type(uint128).max);
File LinkInstance CountInstance Links
LeftRight.sol6285,286,287,288,290,291

</details>
<a id="n-54"></a> [N-54] Style guide: Variable names that consist of all capital letters and underscores should be reserved for constants and immutable variables
Description:

Per the Solidity Style Guide, constants should be named with all capital letters with underscores separating words. Local and state variables should use mixedCase. The Style Guide does not mention a naming convention for immutable variables, but using the same convention as constants is common.

Recommendation:

Follow the Solidity Style Guide for naming conventions.

Instances:

There are 2 instances of this issue.

File: contracts/PanopticFactory.sol

116:         address _WETH9,

117:         SemiFungiblePositionManager _SFPM,
File LinkInstance CountInstance Links
PanopticFactory.sol2116,117

<a id="n-55"></a> [N-55] Typos
Description:

Typos in comments reflect poorly on the protocol, can indicate a lack of attention to detail by the developers, and can decrease confidence in the project.

Recommendation:

Correct the typos and use an IDE extension/plugin to assist with spelling.

Instances:

There are 68 instances of this issue.

<details><summary>View 68 Instances</summary>
File: contracts/CollateralTracker.sol

/// @audit safecasting
37:     // Used for safecasting

/// @audit initalized
92:     /// @dev As each instance is deployed via proxy clone, initial parameters must only be initalized once via startToken().

/// @audit exercisee
634:     /// - The forceExercisor will have to *pay* the exercisee because their position will be closed "against their will"

/// @audit liquidatee
946:             // T: transferred shares from liquidatee which are a component of N but do not contribute toward protocol loss

/// @audit refundee
971:     /// @dev can handle negative refund amounts that go from refundee to refunder in the case of high exercise fees.

/// @audit refundee
974:     /// @param assets The amount of assets to refund. Positive means a transfer from refunder to refundee, vice versa for negative.

/// @audit premum
1180:         // if premium is positive (ie. user will receive funds due to selling options), add this premum to the user's balance
File LinkInstance CountInstance Links
CollateralTracker.sol737,92,634,946,971,974,1180

File: contracts/PanopticFactory.sol

/// @audit deployers
68:     /// @notice Contract called to issue reward NFT to pool deployers

/// @audit numeraire
77:     /// @notice Address of the Wrapped Ether (or other numeraire token) contract

/// @audit numeraire
109:     /// @param _WETH9 Address of the Wrapped Ether (or other numeraire token) contract

/// @audit permissionless
222:         // restrict pool deployment to owner if contract has not been made permissionless
File LinkInstance CountInstance Links
PanopticFactory.sol468,77,109,222

File: contracts/PanopticPool.sol

/// @audit permissionless
23: /// @title The Panoptic Pool: Create permissionless options on top of a concentrated liquidity AMM like Uniswap v3.

/// @audit exercisor
49:     /// @param exerciseFee LeftRight encoding for the cost paid by the exercisor to force the exercise of the token.

/// @audit caluculation
107:     // Flags used as arguments to premia caluculation functions

/// @audit blocktime
142:     /// Note that the *minimum* total observation time is determined by the blocktime and may need to be adjusted by chain

/// @audit subtick
335:     /// @dev Can also be used for more granular subtick precision on slippage checks

/// @audit slippagelimit
545:     /// @param tickLimitLow The lower tick slippagelimit.

/// @audit slippagelimit
546:     /// @param tickLimitHigh The upper tick slippagelimit.

/// @audit slippagelimit
612:     /// @param tickLimitLow The lower tick slippagelimit.

/// @audit slippagelimit
613:     /// @param tickLimitHigh The upper tick slippagelimit.

/// @audit liquidatee
1015:     /// @param delegations LeftRight amounts of token0 and token1 (token0:token1 right:left) delegated to the liquidatee by the liquidator so the option can be smoothly exercised.

/// @audit liquidatee
1081:             // burn all options from the liquidatee

/// @audit liquidatee
1084:             // This is to prevent any short positions the liquidatee has being settled with tokens that will later be revoked

/// @audit liquidatee
1108:             // premia cannot be paid if there is protocol loss associated with the liquidatee

/// @audit liquidatee
1109:             // otherwise, an economic exploit could occur if the liquidator and liquidatee collude to

/// @audit liquidatee
1112:             // thus, we haircut any premium paid by the liquidatee (converting tokens as necessary) until the protocol loss is covered or the premium is exhausted

/// @audit exercisee
1173:     /// @notice Force the exercise of a single position. Exercisor will have to pay a fee to the force exercisee.

/// @audit exercisor
1264:         // refund the protocol any virtual shares after settling the difference with the exercisor

/// @audit overstimate
1351:             // overstimate by rounding up
File LinkInstance CountInstance Links
PanopticPool.sol1823,49,107,142,335,545,546,612,613,1015,1081,1084,1108,1109,1112,1173,1264,1351

File: contracts/SemiFungiblePositionManager.sol

/// @audit safecasting
108:     // Used for safecasting

/// @audit streamia
130:     // and vegoid modifies the sensitivity of the streamia to changes in that utilization,

/// @audit feesbase, feesbase
1097:         // round up the stored feesbase to minimize feesbase when we next calculate it

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

/// @audit feesbase
1137:     /// @dev stored fees base is rounded up and the current fees base is rounded down to minimize the amount of fees collected (feesbase) in favor of the protocol

/// @audit feesbase
1264:         // round down current fees base to minimize feesbase

/// @audit seperated
1337:         // premia, and is only seperated to simplify the calculation
File LinkInstance CountInstance Links
SemiFungiblePositionManager.sol7108,130,1097,1106,1137,1264,1337

File: contracts/libraries/Errors.sol

/// @audit exercisee
25:     /// @notice PanopticPool: force exercisee is insolvent - liquidatable accounts are not permitted to open or close positions outside of a liquidation

/// @audit irst
31:     /// @notice PanopticFactory: irst 20 bytes of provided salt does not match caller address

/// @audit avaiable
62:     /// @notice PanopticPool: there is not enough avaiable liquidity to buy an option
File LinkInstance CountInstance Links
Errors.sol325,31,62

File: contracts/libraries/InteractionHelper.sol

/// @audit metadada
95:         // not guaranteed that token supports metadada extension

/// @audit metadada
108:         // not guaranteed that token supports metadada extension
File LinkInstance CountInstance Links
InteractionHelper.sol295,108

File: contracts/libraries/PanopticMath.sol

/// @audit safecasting
19:     // Used for safecasting

/// @audit blocktime
116:     /// @dev Each period has a minimum length of blocktime * period, but may be longer if the Uniswap pool is relatively inactive.

/// @audit stots
247:             // construct the time stots

/// @audit consistentcy
663:                 // evaluate at TWAP price to keep consistentcy with solvency calculations

/// @audit liquidatee
691:             // negative premium (owed to the liquidatee) is credited to the collateral balance

/// @audit liquidatee
705:                 // liquidatee cannot pay back the liquidator fully in either token, so no protocol loss can be avoided

/// @audit liquidatee
707:                     // liquidatee has insufficient token0 but some token1 left over, so we use what they have left to mitigate token0 losses

/// @audit liquidatee
711:                     // and paid0 - balance0 is the amount of token0 that the liquidatee is missing, i.e the protocol loss

/// @audit liquidatee
725:                     // liquidatee has insufficient token1 but some token0 left over, so we use what they have left to mitigate token1 losses

/// @audit liquidatee
729:                     // and paid1 - balance1 is the amount of token1 that the liquidatee is missing, i.e the protocol loss

/// @audit liquidatee
760:     /// @param premiasByLeg The premium paid (or received) by the liquidatee for each leg of each position

/// @audit liquidatee
779:             // get the amount of premium paid by the liquidatee

/// @audit liquidatee
790:             // Ignore any surplus collateral - the liquidatee is either solvent or it converts to <1 unit of the other token

/// @audit commited
886:                         // The long premium is not commited to storage during the liquidation, so we add the entire adjusted amount

/// @audit exercisee
911:     /// @param refunder Address of the user the refund is coming from (the force exercisee)

/// @audit refundee
926:             // if the refunder lacks sufficient token0 to pay back the refundee, have them pay back the equivalent value in token1
File LinkInstance CountInstance Links
PanopticMath.sol1619,116,247,663,691,705,707,711,725,729,760,779,790,886,911,926

File: contracts/multicall/Multicall.sol

/// @audit paranthetical
22:                 // Other solutions will do work to differentiate the revert reasons and provide paranthetical information
File LinkInstance CountInstance Link
Multicall.sol122

File: contracts/types/LeftRight.sol

/// @audit safecasting
18:     // Used for safecasting

/// @audit bitmask
21:     /// @notice AND bitmask to isolate the right half of a uint256

/// @audit bitmask
25:     /// @notice AND bitmask to isolate the left half of an int256

/// @audit bitmask
29:     /// @notice AND bitmask to isolate the left half of an int256

/// @audit explictily
153:             // adding leftRight packed uint128's is same as just adding the values explictily

/// @audit occured
159:             // then an overflow has occured

/// @audit explictily
176:             // subtracting leftRight packed uint128's is same as just subtracting the values explictily

/// @audit occured
182:             // then an underflow has occured
File LinkInstance CountInstance Links
LeftRight.sol818,21,25,29,153,159,176,182

File: contracts/types/TokenId.sol

/// @audit stranlges
565:                     // unlike short stranlges, long strangles also cannot be partnered, because there is no reduction in risk (both legs can earn premia simultaneously)

/// @audit exercisability
576:     /// @param self The TokenId to validate for exercisability
File LinkInstance CountInstance Links
TokenId.sol2565,576

</details>
<a id="n-56"></a> [N-56] Unnecessary cast
Description:

The variable is being cast to its own type.

Recommendation:

Remove unnecessary casting to increase code clarity and readability.

Instances:

There are 11 instances of this issue.

<details><summary>View 11 Instances</summary>
File: contracts/PanopticPool.sol

/// @audit uint256()
309:                 (uint256(block.timestamp) << 216) +
File LinkInstance CountInstance Link
PanopticPool.sol1309

File: contracts/SemiFungiblePositionManager.sol

/// @audit address()
447:             ? address(decoded.poolFeatures.token0)
File LinkInstance CountInstance Link
SemiFungiblePositionManager.sol1447

File: contracts/types/TokenId.sol

/// @audit uint256()
110:             return uint256((TokenId.unwrap(self) >> (64 + legIndex * 48)) % 2);

/// @audit uint256()
120:             return uint256((TokenId.unwrap(self) >> (64 + legIndex * 48 + 1)) % 128);

/// @audit uint256()
130:             return uint256((TokenId.unwrap(self) >> (64 + legIndex * 48 + 8)) % 2);

/// @audit uint256()
140:             return uint256((TokenId.unwrap(self) >> (64 + legIndex * 48 + 9)) % 2);

/// @audit uint256()
150:             return uint256((TokenId.unwrap(self) >> (64 + legIndex * 48 + 10)) % 4);

/// @audit uint256()
212:                 TokenId.wrap(TokenId.unwrap(self) + (uint256(_asset % 2) << (64 + legIndex * 48)));

/// @audit uint256()
229:                     TokenId.unwrap(self) + (uint256(_optionRatio % 128) << (64 + legIndex * 48 + 1))

/// @audit uint256()
263:                     TokenId.unwrap(self) + (uint256(_tokenType % 2) << (64 + legIndex * 48 + 9))

/// @audit uint256()
281:                     TokenId.unwrap(self) + (uint256(_riskPartner % 4) << (64 + legIndex * 48 + 10))
File LinkInstance CountInstance Links
TokenId.sol9110,120,130,140,150,212,229,263,281

</details>
<a id="n-57"></a> [N-57] Unsafe conversion from unsigned to signed integer
Description:

Because Solidity uses two's complement for integer representation, converting an unsigned integer to a signed integer may result in a change of the sign and/or magnitude of the value being converted. Because of this, casting an unsigned integer to a signed one may result in a change of the sign and or magnitude of the value. For example, int8(type(uint8).max) is not equal to type(int8).max, but is equal to -1. type(uint8).max in binary is 11111111, which if cast to a signed value, means the first binary 1 indicates a negative value, and the binary 1s, invert to all zeroes, and when one is added, it becomes one, but negative, and therefore the decimal value of binary 11111111 is -1.

Recommendation:

Avoid conversion from unsigned integers to signed integers, or add input validation to prevent unexpected results.

Instances:

There are 79 instances of this issue.

<details><summary>View 79 Instances</summary>
File: contracts/CollateralTracker.sol

/// @audit uint256 _sellerCollateralRatio -> int256
200:             int256 ratioTick = (int256(_sellerCollateralRatio) - 2000);

/// @audit uint256 -> int256
668:                         int256(
669:                             Math.unsafeDivRoundingUp(
670:                                 uint24(positionId.width(leg) * positionId.tickSpacing()),
671:                                 2
672:                             )
673:                         )

/// @audit uint128 -> int128
715:                                 int128(uint128(oracleValue0)) - int128(uint128(currentValue0))

/// @audit uint128 -> int128
715:                                 int128(uint128(oracleValue0)) - int128(uint128(currentValue0))

/// @audit uint128 -> int128
718:                                 int128(uint128(oracleValue1)) - int128(uint128(currentValue1))

/// @audit uint128 -> int128
718:                                 int128(uint128(oracleValue1)) - int128(uint128(currentValue1))

/// @audit uint256 -> int256
743:             return int256((s_inAMM * DECIMALS) / totalAssets());

/// @audit uint256 -> int256
959:                     uint256(Math.max(1, int256(totalAssets()) - int256(assets)))

/// @audit uint256 assets -> int256
959:                     uint256(Math.max(1, int256(totalAssets()) - int256(assets)))

/// @audit uint256 -> int256
1003:             int256 updatedAssets = int256(uint256(s_poolAssets)) - swappedAmount;

/// @audit uint256 -> int256
1029:             s_inAMM = uint128(uint256(int256(uint256(s_inAMM)) + (shortAmount - longAmount)));

/// @audit uint256 -> int256
1052:             int256 updatedAssets = int256(uint256(s_poolAssets)) - swappedAmount;

/// @audit uint256 -> int256
1085:             s_inAMM = uint128(uint256(int256(uint256(s_inAMM)) - (shortAmount - longAmount)));

/// @audit uint256 swapCommission -> int256
1115:                 exchangedAmount = intrinsicValue + int256(swapCommission);

/// @audit uint256 -> int256
1119:             exchangedAmount += int256(
1120:                 Math.unsafeDivRoundingUp(
1121:                     uint256(uint128(shortAmount + longAmount)) * COMMISSION_FEE,
1122:                     DECIMALS
1123:                 )
1124:             );

/// @audit uint64 -> int64
1329:             ? int64(uint64(poolUtilization))

/// @audit uint64 -> int64
1330:             : int64(uint64(poolUtilization >> 64));

/// @audit uint64 -> int64
1585:                     ? int64(uint64(poolUtilization))

/// @audit uint64 -> int64
1586:                     : int64(uint64(poolUtilization >> 64))

/// @audit uint64 -> int64
1637:                 uint128(uint64(-int64(poolUtilization0 == 0 ? 1 : poolUtilization0))) +

/// @audit uint64 -> int64
1638:                 (uint128(uint64(-int64(poolUtilization1 == 0 ? 1 : poolUtilization1))) << 64);

File: contracts/PanopticPool.sol

/// @audit uint256 -> int256
477:                         LeftRightSigned.wrap(int256(LeftRightUnsigned.unwrap(availablePremium)))

/// @audit uint256 -> int256
1144:             uint256(int256(uint256(_delegations.rightSlot())) + liquidationBonus0)

/// @audit uint256 -> int256
1149:             uint256(int256(uint256(_delegations.leftSlot())) + liquidationBonus1)

/// @audit uint256 -> int256
1549:                                 int256(
1550:                                     ((premiumAccumulatorsByLeg[leg][0] -
1551:                                         premiumAccumulatorLast.rightSlot()) *
1552:                                         (liquidityChunk.liquidity())) / 2 ** 64
1553:                                 )

/// @audit uint256 -> int256
1558:                                 int256(
1559:                                     ((premiumAccumulatorsByLeg[leg][1] -
1560:                                         premiumAccumulatorLast.leftSlot()) *
1561:                                         (liquidityChunk.liquidity())) / 2 ** 64
1562:                                 )

/// @audit uint256 -> int256
1635:                 .toRightSlot(int128(int256((accumulatedPremium.rightSlot() * liquidity) / 2 ** 64)))

/// @audit uint256 -> int256
1636:                 .toLeftSlot(int128(int256((accumulatedPremium.leftSlot() * liquidity) / 2 ** 64)));

/// @audit uint256 -> int256
1870:                                         .wrap(int256(LeftRightUnsigned.unwrap(settledTokens)))

/// @audit uint256 -> int256
1901:                         LeftRightSigned.wrap(int256(LeftRightUnsigned.unwrap(availablePremium)))

/// @audit uint256 -> int256
1935:                                                 (int256(
1936:                                                     grossPremiumLast.rightSlot() *
1937:                                                         totalLiquidityBefore
1938:                                                 ) -

/// @audit uint256 -> int256
1939:                                                     int256(
1940:                                                         _premiumAccumulatorsByLeg[_leg][0] *
1941:                                                             positionLiquidity
1942:                                                     )) + int256(legPremia.rightSlot() * 2 ** 64),

/// @audit uint256 -> int256
1952:                                                 (int256(
1953:                                                     grossPremiumLast.leftSlot() *
1954:                                                         totalLiquidityBefore
1955:                                                 ) -

/// @audit uint256 -> int256
1956:                                                     int256(
1957:                                                         _premiumAccumulatorsByLeg[_leg][1] *
1958:                                                             positionLiquidity
1959:                                                     )) + int256(legPremia.leftSlot()) * 2 ** 64,
File LinkInstance CountInstance Links
PanopticPool.sol13477,1144,1149,1549,1558,1635,1636,1870,1901,1935,1939,1952,1956

File: contracts/SemiFungiblePositionManager.sol

/// @audit uint256 -> int256
1169:                     int128(int256(Math.mulDiv128RoundingUp(feeGrowthInside0LastX128, liquidity)))

/// @audit uint256 -> int256
1172:                     int128(int256(Math.mulDiv128RoundingUp(feeGrowthInside1LastX128, liquidity)))

/// @audit uint256 -> int256
1176:                 .toRightSlot(int128(int256(Math.mulDiv128(feeGrowthInside0LastX128, liquidity))))

/// @audit uint256 -> int256
1177:                 .toLeftSlot(int128(int256(Math.mulDiv128(feeGrowthInside1LastX128, liquidity))));

/// @audit uint256 amount0 -> int256
1214:         movedAmounts = LeftRightSigned.wrap(0).toRightSlot(int128(int256(amount0))).toLeftSlot(

/// @audit uint256 amount1 -> int256
1215:             int128(int256(amount1))

/// @audit uint256 amount0 -> int256
1241:             movedAmounts = LeftRightSigned.wrap(0).toRightSlot(-int128(int256(amount0))).toLeftSlot(

/// @audit uint256 amount1 -> int256
1242:                 -int128(int256(amount1))
File LinkInstance CountInstance Links
SemiFungiblePositionManager.sol81169,1172,1176,1177,1214,1215,1241,1242

File: contracts/libraries/FeesCalc.sol

/// @audit uint256 amount0 -> int256
69:                         value0 += int256(amount0);

/// @audit uint256 amount1 -> int256
70:                         value1 += int256(amount1);

/// @audit uint256 amount0 -> int256
74:                         value0 -= int256(amount0);

/// @audit uint256 amount1 -> int256
75:                         value1 -= int256(amount1);

/// @audit uint256 -> int256
117:                 .toRightSlot(int128(int256(Math.mulDiv128(ammFeesPerLiqToken0X128, liquidity))))

/// @audit uint256 -> int256
118:                 .toLeftSlot(int128(int256(Math.mulDiv128(ammFeesPerLiqToken1X128, liquidity))));
File LinkInstance CountInstance Links
FeesCalc.sol669,70,74,75,117,118

File: contracts/libraries/Math.sol

/// @audit uint128 toCast -> int128
312:         if ((downcastedInt = int128(toCast)) < 0) revert Errors.CastingError();

/// @audit uint256 toCast -> int256
327:         return int256(toCast);

/// @audit uint256 -> int256
778:             quickSort(data, int256(0), int256(data.length - 1));
File LinkInstance CountInstance Links
Math.sol3312,327,778

File: contracts/libraries/PanopticMath.sol

/// @audit uint256 observationIndex -> int256
140:                         (int256(observationIndex) - int256(i * period)) +

/// @audit uint256 -> int256
140:                         (int256(observationIndex) - int256(i * period)) +

/// @audit uint256 observationCardinality -> int256
141:                             int256(observationCardinality)

/// @audit uint256 -> int256
151:                     int256(timestamps[i] - timestamps[i + 1]);

/// @audit uint24 -> int24
178:                 (int24(uint24(medianData >> ((uint24(medianData >> (192 + 3 * 3)) % 8) * 24))) +

/// @audit uint24 -> int24
179:                     int24(uint24(medianData >> ((uint24(medianData >> (192 + 3 * 4)) % 8) * 24)))) /

/// @audit uint256 observationIndex -> int256
188:                             int256(observationIndex) - int256(1) + int256(observationCardinality)

/// @audit uint256 observationCardinality -> int256
188:                             int256(observationIndex) - int256(1) + int256(observationCardinality)

/// @audit uint256 -> int256
196:                             int256(timestamp_last - timestamp_old)

/// @audit uint24 -> int24
217:                     entry = int24(uint24(medianData >> (rank * 24)));

/// @audit uint56 -> int56
258:                     (tickCumulatives[i] - tickCumulatives[i + 1]) / int56(uint56(twapWindow / 20))

/// @audit uint256 -> int256
377:             int24(int256(Math.unsafeDivRoundingUp(uint24(width) * uint24(tickSpacing), 2)))

/// @audit uint256 -> int256
681:                 bonus0 = int256(Math.mulDiv128(bonusCross, requiredRatioX128));

/// @audit uint256 -> int256
683:                 bonus1 = int256(
684:                     PanopticMath.convert0to1(
685:                         Math.mulDiv128(bonusCross, 2 ** 128 - requiredRatioX128),
686:                         sqrtPriceX96Final
687:                     )
688:                 );

/// @audit uint256 -> int256
693:             int256 balance0 = int256(uint256(tokenData0.rightSlot())) -

/// @audit uint256 -> int256
695:             int256 balance1 = int256(uint256(tokenData1.rightSlot())) -

/// @audit uint256 -> int256
929:                 int256(collateral0.convertToAssets(collateral0.balanceOf(refunder)));

/// @audit uint256 -> int256
938:                                 int256(
939:                                     PanopticMath.convert0to1(uint256(balanceShortage), sqrtPriceX96)
940:                                 ) + refundValues.leftSlot()

/// @audit uint256 -> int256
947:                 int256(collateral1.convertToAssets(collateral1.balanceOf(refunder)));

/// @audit uint256 -> int256
956:                                 int256(
957:                                     PanopticMath.convert1to0(uint256(balanceShortage), sqrtPriceX96)
958:                                 ) + refundValues.rightSlot()
File LinkInstance CountInstance Links
PanopticMath.sol20140,140,141,151,178,179,188,188,196,217,258,377,681,683,693,695,929,938,947,956

File: contracts/types/LeftRight.sol

/// @audit uint256 -> int256
27:         int256(uint256(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000000000000000000000000000));

/// @audit uint256 -> int256
196:             int256 left = int256(uint256(x.leftSlot())) + y.leftSlot();

/// @audit uint256 -> int256
201:             int256 right = int256(uint256(x.rightSlot())) + y.rightSlot();
File LinkInstance CountInstance Links
LeftRight.sol327,196,201

File: contracts/types/LiquidityChunk.sol

/// @audit uint256 -> int256
173:             return int24(int256(LiquidityChunk.unwrap(self) >> 232));

/// @audit uint256 -> int256
182:             return int24(int256(LiquidityChunk.unwrap(self) >> 208));
File LinkInstance CountInstance Links
LiquidityChunk.sol2173,182

File: contracts/types/TokenId.sol

/// @audit uint24 -> int24
98:             return int24(uint24((TokenId.unwrap(self) >> 48) % 2 ** 16));

/// @audit uint256 -> int256
160:             return int24(int256(TokenId.unwrap(self) >> (64 + legIndex * 48 + 12)));

/// @audit uint256 -> int256
171:             return int24(int256((TokenId.unwrap(self) >> (64 + legIndex * 48 + 36)) % 4096));
File LinkInstance CountInstance Links
TokenId.sol398,160,171

</details>
<a id="n-58"></a> [N-58] Unused contract variables
Description:

Unused variables impair readability of the code.

Recommendation:

Remove unused variables.

Instances:

There are 3 instances of this issue.

File: contracts/libraries/Constants.sol

9:     uint256 internal constant FP96 = 0x1000000000000000000000000;
File LinkInstance CountInstance Link
Constants.sol19

File: contracts/libraries/Math.sol

15:     uint256 internal constant MAX_UINT256 = 2 ** 256 - 1;
File LinkInstance CountInstance Link
Math.sol115

File: contracts/libraries/PanopticMath.sol

23:     uint256 internal constant MAX_UINT256 = 2 ** 256 - 1;
File LinkInstance CountInstance Link
PanopticMath.sol123

<a id="n-59"></a> [N-59] Unused import
Description:

The identifier is imported but never used within the file.

Instances:

There is 1 instance of this issue.

File: contracts/types/LiquidityChunk.sol

/// @audit TokenId
5: import {TokenId} from "@types/TokenId.sol";
File LinkInstance CountInstance Link
LiquidityChunk.sol15

<a id="n-60"></a> [N-60] Use bit shifts rather than long bit masks of a single bit, for readability
Description:

To make the code more readable and maintainable, use bit shifts rather than long bit masks of a single bit.

Instances:

There are 8 instances of this issue.

<details><summary>View 8 Instances</summary>
File: contracts/libraries/Constants.sol

9:     uint256 internal constant FP96 = 0x1000000000000000000000000;
File LinkInstance CountInstance Link
Constants.sol19

File: contracts/libraries/Math.sol

93:             if (x >= 0x100000000000000000000000000000000) {

97:             if (x >= 0x10000000000000000) {

135:                 : 0x100000000000000000000000000000000;

494:                 remainder := mulmod(a, b, 0x10000000000000000)

557:                 remainder := mulmod(a, b, 0x1000000000000000000000000)

634:                 remainder := mulmod(a, b, 0x100000000000000000000000000000000)

711:                 remainder := mulmod(a, b, 0x1000000000000000000000000000000000000000000000000)
File LinkInstance CountInstance Links
Math.sol793,97,135,494,557,634,711

</details>
<a id="n-61"></a> [N-61] Use more recent OpenZeppelin version for bug fixes and performance improvements
Description:

OpenZeppelin version 5.0.0+ provides bug fixes and performance improvements. See the release notes for more info.

Instances:

There are 5 instances of this issue.

File: contracts/PanopticFactory.sol

/// @audit version in use: 4.8.3
14: import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol";
File LinkInstance CountInstance Link
PanopticFactory.sol114

File: contracts/PanopticPool.sol

/// @audit version in use: 4.8.3
9: import {ERC1155Holder} from "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol";
File LinkInstance CountInstance Link
PanopticPool.sol19

File: contracts/libraries/InteractionHelper.sol

/// @audit version in use: 4.8.3
6: import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";

/// @audit version in use: 4.8.3
10: import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
File LinkInstance CountInstance Links
InteractionHelper.sol26,10

File: contracts/tokens/ERC1155Minimal.sol

/// @audit version in use: 4.8.3
5: import {ERC1155Holder} from "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol";
File LinkInstance CountInstance Link
ERC1155Minimal.sol15

<a id="n-62"></a> [N-62] Variables need not be initialized to false
Description:

The default values of variables are the typical โ€œzero-stateโ€ of whatever the type is. The default value for a bool is false, so initializing it to this value is unnecessary.

Instances:

There is 1 instance of this issue.

File: contracts/SemiFungiblePositionManager.sol

125:     bool internal constant MINT = false;
File LinkInstance CountInstance Link
SemiFungiblePositionManager.sol1125

<a id="n-63"></a> [N-63] Visibility should be set explicitly rather than defaulting to internal
Description:

The default visibility for state variables is internal. To clarify the visibility of state variables, it is a best practice is to always explicitly specify a visibility (internal, private, or public).

Recommendation:

Consider always specifying an explicit visibility for state variables.

Instances:

There are 8 instances of this issue.

<details><summary>View 8 Instances</summary>
File: contracts/CollateralTracker.sol

131:     uint256 immutable TICK_DEVIATION;

136:     uint256 immutable COMMISSION_FEE;

141:     uint256 immutable SELLER_COLLATERAL_RATIO;

145:     uint256 immutable BUYER_COLLATERAL_RATIO;

149:     int256 immutable FORCE_EXERCISE_COST;

154:     uint256 immutable TARGET_POOL_UTIL;

158:     uint256 immutable SATURATED_POOL_UTIL;

162:     uint256 immutable ITM_SPREAD_MULTIPLIER;
File LinkInstance CountInstance Links
CollateralTracker.sol8131,136,141,145,149,154,158,162

</details>

#0 - c4-judge

2024-04-26T10:29:31Z

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