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
Rank: 55/55
Findings: 1
Award: $32.96
š Selected for report: 0
š Solo Findings: 0
š Selected for report: DadeKuma
Also found by: 0xStalin, 0xhacksmithh, 99Crits, Aymen0909, Bauchibred, CodeWasp, Dup1337, IllIllI, John_Femi, K42, KupiaSec, Naresh, Rhaydden, Rolezn, Sathish9098, Topmark, ZanyBonzy, albahaca, bareli, blockchainbuttonmasher, cheatc0d3, codeslide, crc32, d3e4, favelanky, grearlake, hihen, jasonxiale, jesjupyter, lanrebayode77, lirezArAzAvi, lsaudit, mining_mario, oualidpro, pfapostol, radin100, rbserver, sammy, satoshispeedrunner, slvDev, twcctop, zabihullahazadzoi
32.9585 USDC - $32.96
Note: I've removed the specific instances flagged by the 4naly3er report
Issue | Instances | |
---|---|---|
[Lā01] | Events may be emitted out of order due to reentrancy | 71 |
[Lā02] | File allows a version of solidity that is susceptible to .selector -related optimizer bug | 1 |
[Lā03] | Input array lengths may differ | 4 |
Total: 76 instances over 3 issues
Issue | Instances | |
---|---|---|
[Nā01] | 2**<n> - 1 should be re-written as type(uint<n>).max | 23 |
[Nā02] | constant s should be defined rather than using magic numbers | 2 |
[Nā03] | else -block not required | 1 |
[Nā04] | if -statement can be converted to a ternary | 5 |
[Nā05] | public functions not called by the contract should be declared external instead | 2 |
[Nā06] | Add inline comments for unnamed variables | 2 |
[Nā07] | Assembly blocks should have extensive comments | 21 |
[Nā08] | Avoid mutating function /modifier parameters of non-utility functions | 9 |
[Nā09] | Common functions should be refactored to a common base contract | 2 |
[Nā10] | Consider defining system-wide constants in a single file | 28 |
[Nā11] | Consider emitting an event at the end of the constructor | 4 |
[Nā12] | Consider making contracts Upgradeable | 4 |
[Nā13] | Consider returning a struct rather than having multiple return values | 7 |
[Nā14] | Consider splitting complex checks into multiple steps | 1 |
[Nā15] | Consider using delete rather than assigning zero/false to clear values | 5 |
[Nā16] | Consider using a struct rather than having many function input parameters | 37 |
[Nā17] | Consider using descriptive constant s when passing zero as a function argument | 77 |
[Nā18] | Consider using named function arguments | 47 |
[Nā19] | Consider using named returns | 89 |
[Nā20] | Consider using the using -for syntax | 193 |
[Nā21] | Constants in comparisons should appear on the left side | 178 |
[Nā22] | Contract uses both require() /revert() as well as custom errors | 1 |
[Nā23] | Contracts should have all public /external functions exposed by interface s | 4 |
[Nā24] | Custom error has no parameters showing error details | 2 |
[Nā25] | Custom errors should be used rather than revert() /require() | 9 |
[Nā26] | Duplicated require() /revert() checks should be refactored to a modifier or function | 3 |
[Nā27] | Expressions for constant values should use immutable rather than constant | 7 |
[Nā28] | Imports could be organized more systematically | 1 |
[Nā29] | Inconsistent spacing in comments | 5 |
[Nā30] | Large multiples of ten should use scientific notation | 8 |
[Nā31] | Memory-safe annotation unnecessary | 28 |
[Nā32] | Missing event for critical parameter change | 7 |
[Nā33] | Multiple address /ID mappings can be combined into a single mapping of an address /ID to a struct , for readability | 12 |
[Nā34] | NatSpec: Contract declarations should have @author tags | 3 |
[Nā35] | NatSpec: Contract declarations should have @dev tags | 12 |
[Nā36] | NatSpec: Contract declarations should have @notice tags | 1 |
[Nā37] | NatSpec: Contract declarations should have @title tags | 5 |
[Nā38] | NatSpec: Contract declarations should have descriptions | 1 |
[Nā39] | NatSpec: Error declarations should have @dev tags | 33 |
[Nā40] | NatSpec: Event @param tag is missing | 62 |
[Nā41] | NatSpec: Event declarations should have @dev tags | 11 |
[Nā42] | NatSpec: Function @param tag is missing | 2 |
[Nā43] | NatSpec: Function @return tag is missing | 10 |
[Nā44] | NatSpec: Function declarations should have @dev tags | 147 |
[Nā45] | NatSpec: Function declarations should have @notice tags | 1 |
[Nā46] | NatSpec: Function declarations should have descriptions | 1 |
[Nā47] | NatSpec: Modifier declarations should have @dev tags | 1 |
[Nā48] | NatSpec: Non-public state variable declarations should use @dev tags | 52 |
[Nā49] | NatSpec: State variable declarations should have descriptions | 9 |
[Nā50] | NatSpec: Use @inheritdoc rather than using a non-standard tags | 2 |
[Nā51] | Non-library/interface files should use fixed compiler versions, not floating ones | 7 |
[Nā52] | Not using the named return variables anywhere in the function is confusing | 18 |
[Nā53] | Numeric values having to do with time should use time units for readability | 3 |
[Nā54] | Overflows in unchecked blocks | 3 |
[Nā55] | Style guide: Extraneous whitespace | 18 |
[Nā56] | Style guide: Lines are too long | 334 |
[Nā57] | Style guide: Local and state variables should be named using lowerCamelCase | 3 |
[Nā58] | Style guide: Non-external /public function names should begin with an underscore | 5 |
[Nā59] | Style guide: Top-level declarations should be separated by at least two lines | 1 |
[Nā60] | Style guide: Variable names that consist of all capital letters should be reserved for constant /immutable variables | 1 |
[Nā61] | Unnecessary cast | 15 |
[Nā62] | Unused contract variables | 2 |
[Nā63] | Unused import | 1 |
[Nā64] | Use bit shifts to represent powers of two | 28 |
[Nā65] | Use of override is unnecessary | 4 |
[Nā66] | Use the latest solidity (prior to 0.8.20 if on L2s) for deployment | 7 |
[Nā67] | Variables need not be initialized to false | 5 |
[Nā68] | Visibility should be set explicitly rather than defaulting to internal | 8 |
Total: 1640 instances over 68 issues
Ensure that events follow the best practice of check-effects-interaction, and are emitted before external calls
There are 71 instances of this issue:
File: contracts/CollateralTracker.sol /// @audit numberOfPositions() called prior to this emission in withdraw(), via: withdraw(), maxWithdraw() 563: emit Withdraw(msg.sender, receiver, owner, assets, shares); /// @audit numberOfPositions() called prior to this emission in redeem(), via: redeem(), maxRedeem() 623: emit Withdraw(msg.sender, receiver, owner, assets, shares);
File: contracts/PanopticFactory.sol /// @audit issueNFT() called prior to this emission in deployNewPool(), via: deployNewPool() /// @audit slot0() called prior to this emission in deployNewPool(), via: deployNewPool(), _mintFullRange() /// @audit mint() called prior to this emission in deployNewPool(), via: deployNewPool(), _mintFullRange() /// @audit tickSpacing() called prior to this emission in deployNewPool(), via: deployNewPool(), _mintFullRange() /// @audit increaseObservationCardinalityNext() called prior to this emission in deployNewPool(), via: deployNewPool() /// @audit startPool() called prior to this emission in deployNewPool(), via: deployNewPool() /// @audit startToken() called prior to this emission in deployNewPool(), via: deployNewPool() /// @audit initializeAMMPool() called prior to this emission in deployNewPool(), via: deployNewPool() /// @audit getPool() called prior to this emission in deployNewPool(), via: deployNewPool() 268 emit PoolDeployed( 269 newPoolContract, 270 v3Pool, 271 collateralTracker0, 272 collateralTracker1, 273 amount0, 274 amount1 275: );
GitHub: 268, 268, 268, 268, 268, 268, 268, 268, 268
File: contracts/PanopticPool.sol /// @audit slot0() called prior to this emission in _mintOptions(), via: _mintOptions(), _validateSolvency() /// @audit getAccountMarginDetails() called prior to this emission in _mintOptions(), via: _mintOptions(), _validateSolvency(), _checkSolvencyAtTick() /// @audit observations() called prior to this emission in _mintOptions(), via: _mintOptions(), _validateSolvency(), computeMedianObservedPrice() /// @audit observations() called prior to this emission in _mintOptions(), via: _mintOptions(), _validateSolvency(), computeInternalMedian() /// @audit getAccountPremium() called prior to this emission in _mintOptions(), via: _mintOptions(), _validateSolvency(), _checkSolvencyAtTick(), _calculateAccumulatedPremia(), _getPremia() /// @audit getAccountLiquidity() called prior to this emission in _mintOptions(), via: _mintOptions(), _validateSolvency(), _checkSolvencyAtTick(), _calculateAccumulatedPremia(), _getTotalLiquidity() /// @audit getAccountPremium() called prior to this emission in _mintOptions(), via: _mintOptions(), _addUserOption() /// @audit getAccountLiquidity() called prior to this emission in _mintOptions(), via: _mintOptions(), _addUserOption(), _checkLiquiditySpread() /// @audit mintTokenizedPosition() called prior to this emission in _mintOptions(), via: _mintOptions(), _mintInSFPMAndUpdateCollateral() /// @audit takeCommissionAddData() called prior to this emission in _mintOptions(), via: _mintOptions(), _mintInSFPMAndUpdateCollateral(), _payCommissionAndWriteData() /// @audit getAccountPremium() called prior to this emission in _mintOptions(), via: _mintOptions(), _mintInSFPMAndUpdateCollateral(), _updateSettlementPostMint() /// @audit getAccountLiquidity() called prior to this emission in _mintOptions(), via: _mintOptions(), _mintInSFPMAndUpdateCollateral(), _updateSettlementPostMint(), _getTotalLiquidity() /// @audit getPoolId() called prior to this emission in _mintOptions(), via: _mintOptions() 666: emit OptionMinted(msg.sender, positionSize, tokenId, poolUtilizations); /// @audit getAccountLiquidity() called prior to this emission in _burnOptions(), via: _burnOptions(), _updatePositionDataBurn(), _checkLiquiditySpread() /// @audit burnTokenizedPosition() called prior to this emission in _burnOptions(), via: _burnOptions(), _burnAndHandleExercise() /// @audit exercise() called prior to this emission in _burnOptions(), via: _burnOptions(), _burnAndHandleExercise() /// @audit getAccountPremium() called prior to this emission in _burnOptions(), via: _burnOptions(), _burnAndHandleExercise(), _updateSettlementPostBurn(), _getPremia() /// @audit getAccountLiquidity() called prior to this emission in _burnOptions(), via: _burnOptions(), _burnAndHandleExercise(), _updateSettlementPostBurn(), _getTotalLiquidity() 853: emit OptionBurnt(owner, positionSize, tokenId, premiaOwed); /// @audit getAccountMarginDetails() called prior to this emission in liquidate(), via: liquidate(), _checkSolvencyAtTick() /// @audit getAccountPremium() called prior to this emission in liquidate(), via: liquidate(), _checkSolvencyAtTick(), _calculateAccumulatedPremia(), _getPremia() /// @audit getAccountLiquidity() called prior to this emission in liquidate(), via: liquidate(), _checkSolvencyAtTick(), _calculateAccumulatedPremia(), _getTotalLiquidity() /// @audit revoke() called prior to this emission in liquidate(), via: liquidate() /// @audit slot0() called prior to this emission in liquidate(), via: liquidate() /// @audit exercise() called prior to this emission in liquidate(), via: liquidate(), haircutPremia() /// @audit burnTokenizedPosition() called prior to this emission in liquidate(), via: liquidate(), _burnAllOptionsFrom(), _burnOptions(), _burnAndHandleExercise() /// @audit exercise() called prior to this emission in liquidate(), via: liquidate(), _burnAllOptionsFrom(), _burnOptions(), _burnAndHandleExercise() /// @audit getAccountLiquidity() called prior to this emission in liquidate(), via: liquidate(), _burnAllOptionsFrom(), _burnOptions(), _updatePositionDataBurn(), _checkLiquiditySpread() /// @audit getAccountPremium() called prior to this emission in liquidate(), via: liquidate(), _burnAllOptionsFrom(), _burnOptions(), _burnAndHandleExercise(), _updateSettlementPostBurn(), _getPremia() /// @audit getAccountLiquidity() called prior to this emission in liquidate(), via: liquidate(), _burnAllOptionsFrom(), _burnOptions(), _burnAndHandleExercise(), _updateSettlementPostBurn(), _getTotalLiquidity() /// @audit delegate() called prior to this emission in liquidate(), via: liquidate() /// @audit getAccountMarginDetails() called prior to this emission in liquidate(), via: liquidate() /// @audit getAccountPremium() called prior to this emission in liquidate(), via: liquidate(), _calculateAccumulatedPremia(), _getPremia() /// @audit getAccountLiquidity() called prior to this emission in liquidate(), via: liquidate(), _calculateAccumulatedPremia(), _getTotalLiquidity() /// @audit observe() called prior to this emission in liquidate(), via: liquidate(), getUniV3TWAP(), twapFilter() 1170: emit AccountLiquidated(msg.sender, liquidatee, bonusAmounts); /// @audit slot0() called prior to this emission in forceExercise(), via: forceExercise(), _validateSolvency() /// @audit getAccountMarginDetails() called prior to this emission in forceExercise(), via: forceExercise(), _validateSolvency(), _checkSolvencyAtTick() /// @audit observations() called prior to this emission in forceExercise(), via: forceExercise(), _validateSolvency(), computeMedianObservedPrice() /// @audit observations() called prior to this emission in forceExercise(), via: forceExercise(), _validateSolvency(), computeInternalMedian() /// @audit getAccountPremium() called prior to this emission in forceExercise(), via: forceExercise(), _validateSolvency(), _checkSolvencyAtTick(), _calculateAccumulatedPremia(), _getPremia() /// @audit getAccountLiquidity() called prior to this emission in forceExercise(), via: forceExercise(), _validateSolvency(), _checkSolvencyAtTick(), _calculateAccumulatedPremia(), _getTotalLiquidity() /// @audit refund() called prior to this emission in forceExercise(), via: forceExercise() /// @audit convertToAssets() called prior to this emission in forceExercise(), via: forceExercise(), getRefundAmounts() /// @audit balanceOf() called prior to this emission in forceExercise(), via: forceExercise(), getRefundAmounts() /// @audit exerciseCost() called prior to this emission in forceExercise(), via: forceExercise() /// @audit burnTokenizedPosition() called prior to this emission in forceExercise(), via: forceExercise(), _burnAllOptionsFrom(), _burnOptions(), _burnAndHandleExercise() /// @audit exercise() called prior to this emission in forceExercise(), via: forceExercise(), _burnAllOptionsFrom(), _burnOptions(), _burnAndHandleExercise() /// @audit getAccountLiquidity() called prior to this emission in forceExercise(), via: forceExercise(), _burnAllOptionsFrom(), _burnOptions(), _updatePositionDataBurn(), _checkLiquiditySpread() /// @audit getAccountPremium() called prior to this emission in forceExercise(), via: forceExercise(), _burnAllOptionsFrom(), _burnOptions(), _burnAndHandleExercise(), _updateSettlementPostBurn(), _getPremia() /// @audit getAccountLiquidity() called prior to this emission in forceExercise(), via: forceExercise(), _burnAllOptionsFrom(), _burnOptions(), _burnAndHandleExercise(), _updateSettlementPostBurn(), _getTotalLiquidity() /// @audit delegate() called prior to this emission in forceExercise(), via: forceExercise() /// @audit getAccountPremium() called prior to this emission in forceExercise(), via: forceExercise(), _calculateAccumulatedPremia(), _getPremia() /// @audit getAccountLiquidity() called prior to this emission in forceExercise(), via: forceExercise(), _calculateAccumulatedPremia(), _getTotalLiquidity() /// @audit slot0() called prior to this emission in forceExercise(), via: forceExercise() /// @audit observe() called prior to this emission in forceExercise(), via: forceExercise(), getUniV3TWAP(), twapFilter() 1277: emit ForcedExercised(msg.sender, account, touchedId[0], exerciseFees); /// @audit getAccountPremium() called prior to this emission in settleLongPremium(), via: settleLongPremium() /// @audit slot0() called prior to this emission in settleLongPremium(), via: settleLongPremium() /// @audit exercise() called prior to this emission in settleLongPremium(), via: settleLongPremium() 1654: emit PremiumSettled(owner, tokenId, realizedPremia);
GitHub: 666, 666, 666, 666, 666, 666, 666, 666, 666, 666, 666, 666, 666, 853, 853, 853, 853, 853, 1170, 1170, 1170, 1170, 1170, 1170, 1170, 1170, 1170, 1170, 1170, 1170, 1170, 1170, 1170, 1170, 1277, 1277, 1277, 1277, 1277, 1277, 1277, 1277, 1277, 1277, 1277, 1277, 1277, 1277, 1277, 1277, 1277, 1277, 1277, 1277, 1654, 1654, 1654
File: contracts/SemiFungiblePositionManager.sol /// @audit tickSpacing() called prior to this emission in initializeAMMPool(), via: initializeAMMPool(), getPoolId() /// @audit getPool() called prior to this emission in initializeAMMPool(), via: initializeAMMPool() 390: emit PoolInitialized(univ3pool, poolId); /// @audit onERC1155Received() called prior to this emission in mintTokenizedPosition(), via: mintTokenizedPosition(), _mint() 517: emit TokenizedPositionMinted(msg.sender, tokenId, positionSize);
.selector
-related optimizer bugIn solidity versions prior to 0.8.21, there is a legacy code generation bug where if foo().selector
is called, foo()
doesn't actually get evaluated. It is listed as low-severity, because projects usually use the contract name rather than a function call to look up the selector. I've flagged all files using .selector
where the version is vulnerable.
There is one instance of this issue:
File: contracts/tokens/ERC1155Minimal.sol 2: pragma solidity ^0.8.0;
GitHub: 2
If the caller makes a copy-paste error, the lengths may be mismatched and an operation believed to have been completed may not in fact have been completed (e.g. if the array being iterated over is shorter than the one being indexed into).
There are 4 instances of this issue:
File: contracts/SemiFungiblePositionManager.sol /// @audit amounts[] 577: registerTokenTransfer(from, to, TokenId.wrap(ids[i]), amounts[i]);
GitHub: 577
File: contracts/libraries/PanopticMath.sol /// @audit premiasByLeg[] 786: longPremium = longPremium.sub(premiasByLeg[i][leg]);
GitHub: 786
File: contracts/tokens/ERC1155Minimal.sol /// @audit amounts[] 145: amount = amounts[i]; /// @audit ids[] 188: balances[i] = balanceOf[owners[i]][ids[i]];
2**<n> - 1
should be re-written as type(uint<n>).max
Earlier versions of solidity can use uint<n>(-1)
instead. Expressions not including the - 1
can often be re-written to accommodate the change (e.g. by using a >
rather than a >=
, which will also save some gas)
There are 23 instances of this issue:
File: contracts/PanopticPool.sol 165: uint64 internal constant MAX_SPREAD = 9 * (2 ** 32); 1348: Math.mulDiv(uint256(tokenData1.rightSlot()), 2 ** 96, sqrtPriceX96) + 1353: Math.mulDivRoundingUp(uint256(tokenData1.leftSlot()), 2 ** 96, sqrtPriceX96) + 1487: effectiveLiquidityFactorX32 = (uint256(totalLiquidity) * 2 ** 32) / netLiquidity; 1552: (liquidityChunk.liquidity())) / 2 ** 64 1561: (liquidityChunk.liquidity())) / 2 ** 64 1635: .toRightSlot(int128(int256((accumulatedPremium.rightSlot() * liquidity) / 2 ** 64))) 1636: .toLeftSlot(int128(int256((accumulatedPremium.leftSlot() * liquidity) / 2 ** 64))); 1769: totalLiquidity) / 2 ** 64; 1771: totalLiquidity) / 2 ** 64; 1942: )) + int256(legPremia.rightSlot() * 2 ** 64), 1959: )) + int256(legPremia.leftSlot()) * 2 ** 64,
GitHub: 165, 1348, 1353, 1487, 1552, 1561, 1635, 1636, 1769, 1771, 1942, 1959
File: contracts/SemiFungiblePositionManager.sol 1352: totalLiquidity * 2 ** 64, 1357: totalLiquidity * 2 ** 64,
File: contracts/types/TokenId.sol 98: return int24(uint24((TokenId.unwrap(self) >> 48) % 2 ** 16)); 376: if (optionRatios < 2 ** 64) { 378: } else if (optionRatios < 2 ** 112) { 380: } else if (optionRatios < 2 ** 160) { 382: } else if (optionRatios < 2 ** 208) { 439: if (optionRatios < 2 ** 64) { 441: } else if (optionRatios < 2 ** 112) { 443: } else if (optionRatios < 2 ** 160) { 445: } else if (optionRatios < 2 ** 208) {
GitHub: 98, 376, 378, 380, 382, 439, 441, 443, 445
constant
s should be defined rather than using magic numbersEven assembly can benefit from using readable constants instead of hex/numeric literals
There are 2 instances of this issue:
File: contracts/tokens/ERC1155Minimal.sol /// @audit 0x01ffc9a7 202: interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165 /// @audit 0xd9b67a26 203: interfaceId == 0xd9b67a26; // ERC165 Interface ID for ERC1155
else
-block not requiredOne level of nesting can be removed by not having an else
block when the if
-block returns, and if (foo) { return 1; } else { return 2; }
becomes if (foo) { return 1; } return 2;
. A following else if
can become if
There is one instance of this issue:
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
GitHub: 1013
if
-statement can be converted to a ternaryThe code can be made more compact while also increasing readability by converting the following if
-statements to ternaries (e.g. foo += (x > y) ? a : b
)
There are 5 instances of this issue:
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: }
GitHub: 1544
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: }
public
functions not called by the contract should be declared external
insteadContracts are allowed to override their parents' functions and change the visibility from external
to public
.
There are 2 instances of this issue:
File: contracts/SemiFungiblePositionManager.sol 540 function safeTransferFrom( 541 address from, 542 address to, 543 uint256 id, 544 uint256 amount, 545: bytes calldata data 566 function safeBatchTransferFrom( 567 address from, 568 address to, 569 uint256[] calldata ids, 570 uint256[] calldata amounts, 571: bytes calldata data
function foo(address x, address)
-> function foo(address x, address /* y */)
There are 2 instances of this issue:
File: contracts/CollateralTracker.sol 391 /// @return maxAssets The maximum amount of assets that can be deposited. 392: function maxDeposit(address) external pure returns (uint256 maxAssets) { 443 /// @return maxShares The maximum amount of shares that can be minted. 444: function maxMint(address) external view returns (uint256 maxShares) {
Assembly blocks take a lot more time to audit than normal Solidity code, and often have gotchas and side-effects that the Solidity versions of the same code do not. Consider adding more comments explaining what is being done in every step of the assembly code, and describe why assembly is being used instead of Solidity.
There are 21 instances of this issue:
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: }
GitHub: 353, 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: }
GitHub: 25
function
/modifier
parameters of non-utility functionsUse a local variable instead
There are 9 instances of this issue:
File: contracts/PanopticFactory.sol 217: (token0, token1) = token0 < token1 ? (token0, token1) : (token1, token0); 217: (token0, token1) = token0 < token1 ? (token0, token1) : (token1, token0);
File: contracts/PanopticPool.sol 630: (tickLimitLow, tickLimitHigh) = _getSlippageLimits(tickLimitLow, tickLimitHigh); 630: (tickLimitLow, tickLimitHigh) = _getSlippageLimits(tickLimitLow, tickLimitHigh); 834: (tickLimitLow, tickLimitHigh) = _getSlippageLimits(tickLimitLow, tickLimitHigh); 834: (tickLimitLow, tickLimitHigh) = _getSlippageLimits(tickLimitLow, tickLimitHigh);
File: contracts/SemiFungiblePositionManager.sol 692: tokenId = tokenId.flipToBurnToken(); 721: (tickLimitLow, tickLimitHigh) = (tickLimitHigh, tickLimitLow); 721: (tickLimitLow, tickLimitHigh) = (tickLimitHigh, tickLimitLow);
The functions below have the same implementation as is seen in other files. The functions should be refactored into functions of a common base contract
There are 2 instances of this issue:
File: contracts/libraries/Math.sol 41 function min(uint256 a, uint256 b) internal pure returns (uint256) { 42 return a < b ? a : b; 43: } 57 function max(uint256 a, uint256 b) internal pure returns (uint256) { 58 return a > b ? a : b; 59: }
ContA.X = 0
, ContB.Y = 1
, ContC.Z = 2
-> ContConstants.X = 0
, ContConstants.Y = 1
, ContConstants.Z = 2
; ContA.X = X
, ContB.Y = Y
, ContC.Z = Z
,
There are 28 instances of this issue:
File: contracts/CollateralTracker.sol 70: string internal constant TICKER_PREFIX = "po"; 73: string internal constant NAME_PREFIX = "POPT-V1"; 77: uint256 internal constant DECIMALS = 10_000; 81: int128 internal constant DECIMALS_128 = 10_000;
File: contracts/PanopticFactory.sol 82: uint256 internal constant FULL_RANGE_LIQUIDITY_AMOUNT_WETH = 0.1 ether; 86: uint256 internal constant FULL_RANGE_LIQUIDITY_AMOUNT_TOKEN = 1e6; 89: uint16 internal constant CARDINALITY_INCREASE = 100;
File: contracts/PanopticPool.sol 109: bool internal constant COMPUTE_ALL_PREMIA = true; 111: bool internal constant COMPUTE_LONG_PREMIA = false; 114: bool internal constant ONLY_AVAILABLE_PREMIUM = false; 119: bool internal constant COMMIT_LONG_SETTLED = true; 120: bool internal constant DONOT_COMMIT_LONG_SETTLED = false; 123: bool internal constant ADD = true; 128: uint32 internal constant TWAP_WINDOW = 600; 133: bool internal constant SLOW_ORACLE_UNISWAP_MODE = false; 136: uint256 internal constant MEDIAN_PERIOD = 60; 139: uint256 internal constant FAST_ORACLE_CARDINALITY = 3; 145: uint256 internal constant FAST_ORACLE_PERIOD = 1; 148: uint256 internal constant SLOW_ORACLE_CARDINALITY = 7; 152: uint256 internal constant SLOW_ORACLE_PERIOD = 5; 156: int256 internal constant MAX_TWAP_DELTA_LIQUIDATION = 513; 160: int256 internal constant MAX_SLOW_FAST_DELTA = 1800; 168: uint64 internal constant MAX_POSITIONS = 32; 171: uint256 internal constant BP_DECREASE_BUFFER = 13_333; 174: uint256 internal constant NO_BUFFER = 10_000;
GitHub: 109, 111, 114, 119, 120, 123, 128, 133, 136, 139, 145, 148, 152, 156, 160, 168, 171, 174
File: contracts/SemiFungiblePositionManager.sol 125: bool internal constant MINT = false; 126: bool internal constant BURN = true; 133: uint128 private constant VEGOID = 2;
This will allow users to easily exactly pinpoint when and by whom a contract was constructed
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: ) {
GitHub: 178
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: ) {
GitHub: 115
File: contracts/PanopticPool.sol 280: constructor(SemiFungiblePositionManager _sfpm) {
GitHub: 280
File: contracts/SemiFungiblePositionManager.sol 341: constructor(IUniswapV3Factory _factory) {
GitHub: 341
Upgradeable
This allows for bugs to be fixed in production, at the expense of significantly increasing centralization.
There are 4 instances of this issue:
File: contracts/CollateralTracker.sol 36 contract CollateralTracker is ERC20Minimal, Multicall { 37: // Used for safecasting
GitHub: 36
File: contracts/PanopticFactory.sol 26 contract PanopticFactory is Multicall { 27 /*////////////////////////////////////////////////////////////// 28 EVENTS 29 //////////////////////////////////////////////////////////////*/ 30 31 /// @notice Emitted when the ownership of the factory is transferred. 32 /// @param oldOwner The previous owner of the factory 33: /// @param newOwner The new owner of the factory
GitHub: 26
File: contracts/PanopticPool.sol 27 contract PanopticPool is ERC1155Holder, Multicall { 28 /*////////////////////////////////////////////////////////////// 29 EVENTS 30 //////////////////////////////////////////////////////////////*/ 31 32 /// @notice Emitted when an account is liquidated. 33 /// @dev Need to unpack bonusAmounts to get raw numbers, which are always positive. 34 /// @param liquidator Address of the caller whom is liquidating the distressed account. 35 /// @param liquidatee Address of the distressed/liquidatable account. 36 /// @param bonusAmounts LeftRight encoding for the the bonus paid for token 0 (right slot) and 1 (left slot) from the Panoptic Pool to the liquidator. 37: /// The token0 bonus is in the right slot, and token1 bonus is in the left slot.
GitHub: 27
File: contracts/SemiFungiblePositionManager.sol 72 contract SemiFungiblePositionManager is ERC1155, Multicall { 73 /*////////////////////////////////////////////////////////////// 74 EVENTS 75 //////////////////////////////////////////////////////////////*/ 76 77 /// @notice Emitted when a UniswapV3Pool is initialized. 78 /// @param uniswapPool Address of the underlying Uniswap v3 pool 79: /// @param poolId The SFPM's pool identifier for the pool, including the 16-bit tick spacing and 48-bit pool pattern
GitHub: 72
struct
rather than having multiple return
valuesThere are 7 instances of this issue:
File: contracts/CollateralTracker.sol 277 function getPoolData() 278 external 279 view 280 returns (uint256 poolAssets, uint256 insideAMM, int256 currentPoolUtilization) 281: {
GitHub: 277
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: 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: 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) {
GitHub: 651
Assign the expression's parts to intermediate local variables, and check against those instead
There is one instance of this issue:
File: contracts/types/TokenId.sol 566: if (((_isLong != isLongP) || _isLong == 1) && (_tokenType != tokenTypeP))
GitHub: 566
delete
rather than assigning zero/false to clear valuesThe delete
keyword more closely matches the semantics of what is being done, and draws more attention to the changing of state, which may lead to a more thorough audit of its associated logic
There are 5 instances of this issue:
File: contracts/SemiFungiblePositionManager.sol 332: s_poolContext[poolId].locked = false;
GitHub: 332
File: contracts/libraries/PanopticMath.sol 220: below = false; 850: collateralDelta0 = 0; 851: collateralDelta1 = 0;
File: contracts/types/TokenId.sol 377: optionRatios = 0;
GitHub: 377
struct
rather than having many function input parametersOften times, a subset of the parameters would be more clear if they were passed as a struct
There are 37 instances of this issue:
<details> <summary>see 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) { 657: // find the leg furthest to the strike price 'currentTick'; this will have the lowest exercise cost 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) {
GitHub: 178, 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: ) {
GitHub: 115
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) {
GitHub: 291, 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) {
GitHub: 680, 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) { 104 // extract the amount of AMM fees collected within the liquidity chunk` 105: // note: the fee variables are *per unit of liquidity*; so more "rate" variables
GitHub: 97
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: 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) {
GitHub: 125, 168, 651, 768, 917
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) {
GitHub: 336
</details>constant
s when passing zero as a function argumentPassing zero as a function argument can sometimes result in a security issue (e.g. passing zero as the slippage parameter). Consider using a constant
variable with a descriptive name, so it's clear that the argument is intentionally being used, and for the right reasons.
There are 77 instances of this issue:
<details> <summary>see instances</summary>File: contracts/CollateralTracker.sol 713: .wrap(0)
GitHub: 713
File: contracts/PanopticPool.sol 634: revert Errors.InvalidTokenIdParameter(0); 655: .wrap(0) 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); 1166: .wrap(0) 1191: _validatePositionList(msg.sender, positionIdListExercisor, 0); 1227: _burnAllOptionsFrom(account, 0, 0, COMMIT_LONG_SETTLED, touchedId); 1227: _burnAllOptionsFrom(account, 0, 0, COMMIT_LONG_SETTLED, touchedId); 1546: .wrap(0) 1567: premiaByLeg[leg] = LeftRightSigned.wrap(0).sub(premiaByLeg[leg]); 1592: _validatePositionList(owner, positionIdList, 0); 1615: .wrap(0) 1634: .wrap(0) 1639: s_collateralToken0.exercise(owner, 0, 0, 0, realizedPremia.rightSlot()); 1639: s_collateralToken0.exercise(owner, 0, 0, 0, realizedPremia.rightSlot()); 1639: s_collateralToken0.exercise(owner, 0, 0, 0, realizedPremia.rightSlot()); 1640: s_collateralToken1.exercise(owner, 0, 0, 0, realizedPremia.leftSlot()); 1640: s_collateralToken1.exercise(owner, 0, 0, 0, realizedPremia.leftSlot()); 1640: s_collateralToken1.exercise(owner, 0, 0, 0, realizedPremia.leftSlot()); 1714: 0 1726: .wrap(0) 1775: .wrap(0) 1930: .wrap(0) 1943: 0 1960: 0 1966: .wrap(0)
GitHub: 634, 655, 763, 861, 870, 893, 1023, 1153, 1166, 1191, 1227, 1227, 1546, 1567, 1592, 1615, 1634, 1639, 1639, 1639, 1640, 1640, 1640, 1714, 1726, 1775, 1930, 1943, 1960, 1966
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( 1039: .wrap(0) 1167: .wrap(0) 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( 1377: .wrap(0) 1401: .wrap(0)
GitHub: 645, 648, 833, 848, 1039, 1167, 1175, 1214, 1241, 1306, 1377, 1401
File: contracts/libraries/FeesCalc.sol 116: .wrap(0)
GitHub: 116
File: contracts/libraries/Math.sol 778: quickSort(data, int256(0), int256(data.length - 1));
GitHub: 778
File: contracts/libraries/PanopticMath.sol 598: return LeftRightUnsigned.wrap(0).toRightSlot(amount0).toLeftSlot(amount1); 674: 0, 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)); 856: if (haircut0 != 0) collateral0.exercise(_liquidatee, 0, 0, 0, int128(haircut0)); 856: if (haircut0 != 0) collateral0.exercise(_liquidatee, 0, 0, 0, int128(haircut0)); 857: if (haircut1 != 0) collateral1.exercise(_liquidatee, 0, 0, 0, int128(haircut1)); 857: if (haircut1 != 0) collateral1.exercise(_liquidatee, 0, 0, 0, int128(haircut1)); 857: if (haircut1 != 0) collateral1.exercise(_liquidatee, 0, 0, 0, int128(haircut1)); 880: tokenId.strike(0), 881: tokenId.width(0), 882: tokenId.tokenType(0) 889: 0, 893: 0, 898: LeftRightUnsigned.wrap(0).toRightSlot(uint128(settled0)).toLeftSlot( 934: .wrap(0) 952: .wrap(0)
GitHub: 598, 674, 694, 696, 749, 791, 792, 856, 856, 856, 857, 857, 857, 880, 881, 882, 889, 893, 898, 934, 952
File: contracts/tokens/ERC1155Minimal.sol 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: contracts/tokens/ERC20Minimal.sol 130: emit Transfer(address(0), to, amount); 145: emit Transfer(from, address(0), amount);
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(
</details>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);
When calling functions in external contracts with multiple arguments, consider using named function parameters, rather than positional ones.
There are 47 instances of this issue:
File: contracts/PanopticFactory.sol 226: IUniswapV3Pool v3Pool = IUniswapV3Pool(UNIV3_FACTORY.getPool(token0, token1, fee)); 233: SFPM.initializeAMMPool(token0, token1, fee); 248: collateralTracker0.startToken(true, token0, token1, fee, newPoolContract); 249: collateralTracker1.startToken(false, token0, token1, fee, newPoolContract); 251: newPoolContract.startPool(v3Pool, token0, token1, collateralTracker0, collateralTracker1); 266: DONOR_NFT.issueNFT(msg.sender, newPoolContract, token0, token1, fee); 404 IUniswapV3Pool(v3Pool).mint( 405 address(this), 406 tickLower, 407 tickUpper, 408 fullRangeLiquidity, 409 mintCallback 410: );
GitHub: 226, 233, 248, 249, 251, 266, 404
File: contracts/PanopticPool.sol 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: ); 970 (LeftRightUnsigned[4] memory collectedByLeg, LeftRightSigned totalSwapped) = SFPM 971: .burnTokenizedPosition(tokenId, positionSize, tickLimitLow, tickLimitHigh); 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: ); 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: ); 1072: s_collateralToken0.delegate(msg.sender, liquidatee, delegations.rightSlot()); 1073: s_collateralToken1.delegate(msg.sender, liquidatee, delegations.leftSlot()); 1141 s_collateralToken0.revoke( 1142 msg.sender, 1143 liquidatee, 1144 uint256(int256(uint256(_delegations.rightSlot())) + liquidationBonus0) 1145: ); 1146 s_collateralToken1.revoke( 1147 msg.sender, 1148 liquidatee, 1149 uint256(int256(uint256(_delegations.leftSlot())) + liquidationBonus1) 1150: ); 1222: s_collateralToken0.delegate(account, uint128(delegatedAmounts.rightSlot())); 1223: s_collateralToken1.delegate(account, uint128(delegatedAmounts.leftSlot())); 1231 LeftRightSigned exerciseFees = s_collateralToken0.exerciseCost( 1232 currentTick, 1233 twapTick, 1234 touchedId[0], 1235 positionBalance, 1236 longAmounts 1237: ); 1252 s_collateralToken0.refund( 1253 account, 1254 msg.sender, 1255 refundAmounts.rightSlot() - delegatedAmounts.rightSlot() 1256: ); 1257 s_collateralToken1.refund( 1258 account, 1259 msg.sender, 1260 refundAmounts.leftSlot() - delegatedAmounts.leftSlot() 1261: ); 1265: s_collateralToken0.refund(account, uint128(delegatedAmounts.rightSlot())); 1266: s_collateralToken1.refund(account, uint128(delegatedAmounts.leftSlot())); 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: );
GitHub: 685, 715, 721, 751, 970, 985, 996, 1046, 1053, 1072, 1073, 1141, 1146, 1222, 1223, 1231, 1252, 1257, 1265, 1266, 1308, 1314, 1472, 1528, 1605, 1639, 1640, 1707, 1811
File: contracts/SemiFungiblePositionManager.sol 352: address univ3pool = FACTORY.getPool(token0, token1, fee); 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: ); 1203 (uint256 amount0, uint256 amount1) = univ3pool.mint( 1204 address(this), 1205 liquidityChunk.tickLower(), 1206 liquidityChunk.tickUpper(), 1207 liquidityChunk.liquidity(), 1208 mintdata 1209: ); 1230 (uint256 amount0, uint256 amount1) = univ3pool.burn( 1231 liquidityChunk.tickLower(), 1232 liquidityChunk.tickUpper(), 1233 liquidityChunk.liquidity() 1234: ); 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: );
GitHub: 352, 837, 1203, 1230, 1284
File: contracts/libraries/CallbackLib.sol 36: if (factory.getPool(features.token0, features.token1, features.fee) != sender)
GitHub: 36
File: contracts/libraries/PanopticMath.sol 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: contracts/tokens/ERC1155Minimal.sol 114: ERC1155Holder(to).onERC1155Received(msg.sender, from, id, amount, data) != 165: ERC1155Holder(to).onERC1155BatchReceived(msg.sender, from, ids, amounts, data) != 224: ERC1155Holder(to).onERC1155Received(msg.sender, address(0), id, amount, "") !=
Using named returns makes the code more self-documenting, makes it easier to fill out NatSpec, and in some cases can save gas. The cases below are where there currently is at most one return statement, which is ideal for named returns.
There are 89 instances of this issue:
<details> <summary>see instances</summary>File: contracts/CollateralTracker.sol 289: function name() external view returns (string memory) { 303: function symbol() external view returns (string memory) { 310: function decimals() external view returns (uint8) { 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) { 1043 function exercise( 1044 address optionOwner, 1045 int128 longAmount, 1046 int128 shortAmount, 1047 int128 swappedAmount, 1048 int128 realizedPremium 1049: ) external onlyPanopticPool returns (int128) {
GitHub: 289, 303, 310, 323, 341, 1043
File: contracts/PanopticFactory.sol 159: function owner() external view returns (address) { 335 function _mintFullRange( 336 IUniswapV3Pool v3Pool, 337 address token0, 338 address token1, 339 uint24 fee 340: ) internal returns (uint256, uint256) { 420: function getPanopticPool(IUniswapV3Pool univ3pool) external view returns (PanopticPool) {
File: contracts/PanopticPool.sol 381 function calculateAccumulatedFeesBatch( 382 address user, 383 bool includePendingPremium, 384 TokenId[] calldata positionIdList 385: ) external view returns (int128 premium0, int128 premium1, uint256[2][] memory) { 677 function _mintInSFPMAndUpdateCollateral( 678 TokenId tokenId, 679 uint128 positionSize, 680 int24 tickLimitLow, 681 int24 tickLimitHigh 682: ) internal returns (uint128) { 706 function _payCommissionAndWriteData( 707 TokenId tokenId, 708 uint128 positionSize, 709 LeftRightSigned totalSwapped 710: ) internal returns (uint128) { 1290 function _checkSolvencyAtTick( 1291 address account, 1292 TokenId[] calldata positionIdList, 1293 int24 currentTick, 1294 int24 atTick, 1295 uint256 buffer 1296: ) internal view returns (bool) { 1425: function univ3pool() external view returns (IUniswapV3Pool) { 1437: function collateralToken1() external view returns (CollateralTracker) { 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) {
GitHub: 381, 677, 706, 1290, 1425, 1437, 1757
File: contracts/SemiFungiblePositionManager.sol 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) {
GitHub: 1449
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) { 104 // extract the amount of AMM fees collected within the liquidity chunk` 105: // note: the fee variables are *per unit of liquidity*; so more "rate" variables
GitHub: 97
File: contracts/libraries/InteractionHelper.sol 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) {
GitHub: 48
File: contracts/libraries/Math.sol 25: function min24(int24 a, int24 b) internal pure returns (int24) { 33: function max24(int24 a, int24 b) internal pure returns (int24) { 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) { 73: function abs(int256 x) internal pure returns (int256) { 81: function absUint(int256 x) internal pure returns (uint256) { 128: function getSqrtRatioAtTick(int24 tick) internal pure returns (uint160) { 191: function getAmount0ForLiquidity(LiquidityChunk liquidityChunk) internal pure returns (uint256) { 207: function getAmount1ForLiquidity(LiquidityChunk liquidityChunk) internal pure returns (uint256) { 241 function getLiquidityForAmount0( 242 int24 tickLower, 243 int24 tickUpper, 244 uint256 amount0 245: ) internal pure returns (LiquidityChunk) { 271 function getLiquidityForAmount1( 272 int24 tickLower, 273 int24 tickUpper, 274 uint256 amount1 275: ) internal pure returns (LiquidityChunk) { 325: function toInt256(uint256 toCast) internal pure returns (int256) { 776: function sort(int256[] memory data) internal pure returns (int256[] memory) {
GitHub: 25, 33, 41, 49, 57, 65, 73, 81, 128, 191, 207, 241, 271, 325, 776
File: contracts/libraries/PanopticMath.sol 47: function getPoolId(address univ3pool) internal view returns (uint64) { 59: function incrementPoolPattern(uint64 poolId) internal pure returns (uint64) { 75: function numberOfLeadingHexZeros(address addr) external pure returns (uint256) { 92 function updatePositionsHash( 93 uint256 existingHash, 94 TokenId tokenId, 95 bool addFlag 96: ) internal pure returns (uint256) { 125 function computeMedianObservedPrice( 126 IUniswapV3Pool univ3pool, 127 uint256 observationIndex, 128 uint256 observationCardinality, 129 uint256 cardinality, 130 uint256 period 131: ) external view returns (int24) { 241: function twapFilter(IUniswapV3Pool univ3pool, uint32 twapWindow) external view returns (int24) { 371 function getRangesFromStrike( 372 int24 width, 373 int24 tickSpacing 374: ) internal pure returns (int24, int24) { 445 function convertCollateralData( 446 LeftRightUnsigned tokenData0, 447 LeftRightUnsigned tokenData1, 448 uint256 tokenType, 449 int24 tick 450: ) internal pure returns (uint256, uint256) { 468 function convertNotional( 469 uint128 contractSize, 470 int24 tickLower, 471 int24 tickUpper, 472 uint256 asset 473: ) internal pure returns (uint128) { 574 function getAmountsMoved( 575 TokenId tokenId, 576 uint128 positionSize, 577 uint256 legIndex 578: ) internal pure returns (LeftRightUnsigned) { 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) {
GitHub: 47, 59, 75, 92, 125, 241, 371, 445, 468, 574, 651, 768
File: contracts/tokens/ERC1155Minimal.sol 200: function supportsInterface(bytes4 interfaceId) public pure returns (bool) {
GitHub: 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: contracts/tokens/interfaces/IERC20Partial.sol 16: function balanceOf(address account) external view returns (uint256);
GitHub: 16
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) { 279 function addCapped( 280 LeftRightUnsigned x, 281 LeftRightUnsigned dx, 282 LeftRightUnsigned y, 283 LeftRightUnsigned dy 284: ) internal pure returns (LeftRightUnsigned, LeftRightUnsigned) {
GitHub: 39, 46, 59, 78, 101, 108, 121, 134, 279
File: contracts/types/LiquidityChunk.sol 70 function createChunk( 71 int24 _tickLower, 72 int24 _tickUpper, 73 uint128 amount 74: ) internal pure returns (LiquidityChunk) { 89 function addLiquidity( 90 LiquidityChunk self, 91 uint128 amount 92: ) internal pure returns (LiquidityChunk) { 102 function addTickLower( 103 LiquidityChunk self, 104 int24 _tickLower 105: ) internal pure returns (LiquidityChunk) { 118 function addTickUpper( 119 LiquidityChunk self, 120 int24 _tickUpper 121: ) internal pure returns (LiquidityChunk) { 135 function updateTickLower( 136 LiquidityChunk self, 137 int24 _tickLower 138: ) internal pure returns (LiquidityChunk) { 151 function updateTickUpper( 152 LiquidityChunk self, 153 int24 _tickUpper 154: ) internal pure returns (LiquidityChunk) { 171: function tickLower(LiquidityChunk self) internal pure returns (int24) { 180: function tickUpper(LiquidityChunk self) internal pure returns (int24) { 189: function liquidity(LiquidityChunk self) internal pure returns (uint128) {
GitHub: 70, 89, 102, 118, 135, 151, 171, 180, 189
File: contracts/types/TokenId.sol 87: function poolId(TokenId self) internal pure returns (uint64) { 96: function tickSpacing(TokenId self) internal pure returns (int24) { 108: function asset(TokenId self, uint256 legIndex) internal pure returns (uint256) { 118: function optionRatio(TokenId self, uint256 legIndex) internal pure returns (uint256) { 128: function isLong(TokenId self, uint256 legIndex) internal pure returns (uint256) { 138: function tokenType(TokenId self, uint256 legIndex) internal pure returns (uint256) { 148: function riskPartner(TokenId self, uint256 legIndex) internal pure returns (uint256) { 158: function strike(TokenId self, uint256 legIndex) internal pure returns (int24) { 169: function width(TokenId self, uint256 legIndex) internal pure returns (int24) { 183: function addPoolId(TokenId self, uint64 _poolId) internal pure returns (TokenId) { 193: function addTickSpacing(TokenId self, int24 _tickSpacing) internal pure returns (TokenId) { 205 function addAsset( 206 TokenId self, 207 uint256 _asset, 208 uint256 legIndex 209: ) internal pure returns (TokenId) { 221 function addOptionRatio( 222 TokenId self, 223 uint256 _optionRatio, 224 uint256 legIndex 225: ) internal pure returns (TokenId) { 240 function addIsLong( 241 TokenId self, 242 uint256 _isLong, 243 uint256 legIndex 244: ) internal pure returns (TokenId) { 255 function addTokenType( 256 TokenId self, 257 uint256 _tokenType, 258 uint256 legIndex 259: ) internal pure returns (TokenId) { 273 function addRiskPartner( 274 TokenId self, 275 uint256 _riskPartner, 276 uint256 legIndex 277: ) internal pure returns (TokenId) { 291 function addStrike( 292 TokenId self, 293 int24 _strike, 294 uint256 legIndex 295: ) internal pure returns (TokenId) { 310 function addWidth( 311 TokenId self, 312 int24 _width, 313 uint256 legIndex 314: ) internal pure returns (TokenId) { 366: function flipToBurnToken(TokenId self) internal pure returns (TokenId) { 404: function countLongs(TokenId self) internal pure returns (uint256) {
GitHub: 87, 96, 108, 118, 128, 138, 148, 158, 169, 183, 193, 205, 221, 240, 255, 273, 291, 310, 366, 404
</details>using
-for
syntaxThe using
-for
syntax is the more common way of calling library functions.
There are 193 instances of this issue:
<details> <summary>see 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: ); 305: return InteractionHelper.computeSymbol(s_underlyingToken, TICKER_PREFIX); 312: return InteractionHelper.computeDecimals(s_underlyingToken); 380: return Math.mulDiv(assets, totalSupply, totalAssets()); 387: return Math.mulDiv(shares, totalAssets(), totalSupply); 402 shares = Math.mulDiv( 403 assets * (DECIMALS - COMMISSION_FEE), 404 totalSupply, 405 totalAssets() * DECIMALS 406: ); 424 SafeTransferLib.safeTransferFrom( 425 s_underlyingToken, 426 msg.sender, 427 address(s_panopticPool), 428 assets 429: ); 462 assets = Math.mulDivRoundingUp( 463 shares * DECIMALS, 464 totalAssets(), 465 totalSupply * (DECIMALS - COMMISSION_FEE) 466: ); 484 SafeTransferLib.safeTransferFrom( 485 s_underlyingToken, 486 msg.sender, 487 address(s_panopticPool), 488 assets 489: ); 512: return s_panopticPool.numberOfPositions(owner) == 0 ? Math.min(available, balance) : 0; 521: return Math.mulDivRoundingUp(assets, supply, totalAssets()); 556 SafeTransferLib.safeTransferFrom( 557 s_underlyingToken, 558 address(s_panopticPool), 559 receiver, 560 assets 561: ); 575: return s_panopticPool.numberOfPositions(owner) == 0 ? Math.min(available, balance) : 0; 616 SafeTransferLib.safeTransferFrom( 617 s_underlyingToken, 618 address(s_panopticPool), 619 receiver, 620 assets 621: ); 669 Math.unsafeDivRoundingUp( 670 uint24(positionId.width(leg) * positionId.tickSpacing()), 671 2 672: ) 677: uint256(Math.abs(currentTick - positionId.strike(leg)) / range) 675 maxNumRangesFromStrike = Math.max( 676 maxNumRangesFromStrike, 677 uint256(Math.abs(currentTick - positionId.strike(leg)) / range) 678: ); 687 LiquidityChunk liquidityChunk = PanopticMath.getLiquidityChunk( 688 positionId, 689 leg, 690 positionBalance 691: ); 693 (currentValue0, currentValue1) = Math.getAmountsForLiquidity( 694 currentTick, 695 liquidityChunk 696: ); 698 (oracleValue0, oracleValue1) = Math.getAmountsForLiquidity( 699 oracleTick, 700 liquidityChunk 701: ); 959: uint256(Math.max(1, int256(totalAssets()) - int256(assets))) 956 Math.mulDiv( 957 assets, 958 totalSupply - delegateeBalance, 959 uint256(Math.max(1, int256(totalAssets()) - int256(assets))) 960: ) - delegateeBalance 1012 uint256 sharesToBurn = Math.mulDivRoundingUp( 1013 uint256(tokenToPay), 1014 totalSupply, 1015 totalAssets() 1016: ); 1069 uint256 sharesToBurn = Math.mulDivRoundingUp( 1070 uint256(tokenToPay), 1071 totalSupply, 1072 totalAssets() 1073: ); 1110: s_ITMSpreadFee * uint256(Math.abs(intrinsicValue)), 1109 uint256 swapCommission = Math.unsafeDivRoundingUp( 1110 s_ITMSpreadFee * uint256(Math.abs(intrinsicValue)), 1111 DECIMALS 1112: ); 1120 Math.unsafeDivRoundingUp( 1121 uint256(uint128(shortAmount + longAmount)) * COMMISSION_FEE, 1122 DECIMALS 1123: ) 1322: LeftRightUnsigned amountsMoved = PanopticMath.getAmountsMoved(tokenId, positionSize, index); 1364: Math.max24(2 * (atTick - strike), Constants.MIN_V3POOL_TICK) 1363 ? Math.getSqrtRatioAtTick( 1364 Math.max24(2 * (atTick - strike), Constants.MIN_V3POOL_TICK) 1365: ) // puts -> price/strike 1367: Math.max24(2 * (strike - atTick), Constants.MIN_V3POOL_TICK) 1366 : Math.getSqrtRatioAtTick( 1367 Math.max24(2 * (strike - atTick), Constants.MIN_V3POOL_TICK) 1368: ); // calls -> strike/price 1401: required += Math.mulDiv96RoundingUp(amountMoved, c2); 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: ); 1486: required = Math.unsafeDivRoundingUp(amount * sellCollateral, DECIMALS); 1496: required = Math.unsafeDivRoundingUp(amount * buyCollateral, DECIMALS); 1518: LeftRightUnsigned amountsMoved = PanopticMath.getAmountsMoved(tokenId, positionSize, index); 1521 LeftRightUnsigned amountsMovedPartner = PanopticMath.getAmountsMoved( 1522 tokenId, 1523 positionSize, 1524 partnerIndex 1525: ); 1571: ? Math.unsafeDivRoundingUp((notionalP - notional) * contracts, notional) 1572: : Math.unsafeDivRoundingUp((notional - notionalP) * contracts, notionalP); 1579 spreadRequirement = Math.max( 1580 spreadRequirement, 1581 _getRequiredCollateralAtUtilization( 1582 tokenType == 0 ? movedRight : movedLeft, 1583 1, 1584 tokenType == 0 1585 ? int64(uint64(poolUtilization)) 1586 : int64(uint64(poolUtilization >> 64)) 1587 ) 1588: );
GitHub: 292, 305, 312, 380, 387, 402, 424, 462, 484, 512, 521, 556, 575, 616, 669, 677, 675, 687, 693, 698, 959, 956, 1012, 1069, 1110, 1109, 1120, 1322, 1364, 1363, 1367, 1366, 1401, 1408, 1411, 1486, 1496, 1518, 1521, 1571, 1572, 1579
File: contracts/PanopticFactory.sol 179: CallbackLib.validateCallback(msg.sender, UNIV3_FACTORY, decoded.poolFeatures); 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: ); 241: Clones.clone(COLLATERAL_REFERENCE) 244: Clones.clone(COLLATERAL_REFERENCE) 304 uint256 rarity = PanopticMath.numberOfLeadingHexZeros( 305 POOL_REFERENCE.predictDeterministicAddress(salt) 306: ); 353: Math.mulDiv96RoundingUp(FULL_RANGE_LIQUIDITY_AMOUNT_WETH, currentSqrtPriceX96) 357 Math.mulDivRoundingUp( 358 FULL_RANGE_LIQUIDITY_AMOUNT_WETH, 359 Constants.FP96, 360 currentSqrtPriceX96 361: ) 366: Math.mulDiv96RoundingUp(FULL_RANGE_LIQUIDITY_AMOUNT_TOKEN, currentSqrtPriceX96) 369 Math.mulDivRoundingUp( 370 FULL_RANGE_LIQUIDITY_AMOUNT_TOKEN, 371 Constants.FP96, 372 currentSqrtPriceX96 373: )
GitHub: 179, 182, 189, 241, 244, 304, 353, 357, 366, 369
File: contracts/PanopticPool.sol 326: InteractionHelper.doApprovals(SFPM, collateralTracker0, collateralTracker1, token0, token1); 415 (value0, value1) = FeesCalc.getPortfolioValue( 416 atTick, 417 s_positionBalance[user], 418 positionIdList 419: ); 525 (, uint256 medianData) = PanopticMath.computeInternalMedian( 526 observationIndex, 527 observationCardinality, 528 MEDIAN_PERIOD, 529 s_miniMedian, 530 s_univ3pool 531: ); 712 (LeftRightSigned longAmounts, LeftRightSigned shortAmounts) = PanopticMath 713: .computeExercisedAmounts(tokenId, positionSize); 775: uint64(Math.min(effectiveLiquidityLimitX32, 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: ); 943: if (Math.abs(int256(fastOracleTick) - slowOracleTick) > MAX_SLOW_FAST_DELTA) 981 (LeftRightSigned longAmounts, LeftRightSigned shortAmounts) = PanopticMath 982: .computeExercisedAmounts(tokenId, positionSize); 1035: if (Math.abs(currentTick - twapTick) > MAX_TWAP_DELTA_LIQUIDATION) 1063: Math.getSqrtRatioAtTick(twapTick) 1102: Math.getSqrtRatioAtTick(twapTick), 1103: Math.getSqrtRatioAtTick(finalTick), 1098 (liquidationBonus0, liquidationBonus1, collateralRemaining) = PanopticMath 1099 .getLiquidationBonus( 1100 tokenData0, 1101 tokenData1, 1102 Math.getSqrtRatioAtTick(twapTick), 1103 Math.getSqrtRatioAtTick(finalTick), 1104 netExchanged, 1105 premia 1106: ); 1129: Math.getSqrtRatioAtTick(_finalTick), 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: ); 1197 (LeftRightSigned longAmounts, LeftRightSigned delegatedAmounts) = PanopticMath 1198: .computeExercisedAmounts(touchedId[0], positionBalance); 1242 refundAmounts = PanopticMath.getRefundAmounts( 1243 account, 1244 refundAmounts, 1245 twapTick, 1246 s_collateralToken0, 1247 s_collateralToken1 1248: ); 1324: Math.getSqrtRatioAtTick(atTick) 1329: return balanceCross >= Math.unsafeDivRoundingUp(thresholdCross * buffer, 10_000); 1348: Math.mulDiv(uint256(tokenData1.rightSlot()), 2 ** 96, sqrtPriceX96) + 1349: Math.mulDiv96(tokenData0.rightSlot(), sqrtPriceX96); 1353: Math.mulDivRoundingUp(uint256(tokenData1.leftSlot()), 2 ** 96, sqrtPriceX96) + 1354: Math.mulDiv96RoundingUp(tokenData0.leftSlot(), sqrtPriceX96); 1383 fingerprintIncomingList = PanopticMath.updatePositionsHash( 1384 fingerprintIncomingList, 1385 positionIdList[i], 1386 ADD 1387: ); 1410 uint256 newHash = PanopticMath.updatePositionsHash( 1411 s_positionsHash[account], 1412 tokenId, 1413 addFlag 1414: ); 1451: twapTick = PanopticMath.twapFilter(s_univ3pool, TWAP_WINDOW); 1521 LiquidityChunk liquidityChunk = PanopticMath.getLiquidityChunk( 1522 tokenId, 1523 leg, 1524 positionSize 1525: ); 1627 uint256 liquidity = PanopticMath 1628: .getLiquidityChunk(tokenId, legIndex, s_positionBalance[owner][tokenId].rightSlot()) 1680 LiquidityChunk liquidityChunk = PanopticMath.getLiquidityChunk( 1681 tokenId, 1682 leg, 1683 positionSize 1684: ); 1778 Math.min( 1779 (uint256(premiumOwed.rightSlot()) * settledTokens.rightSlot()) / 1780 (accumulated0 == 0 ? type(uint256).max : accumulated0), 1781 premiumOwed.rightSlot() 1782: ) 1787 Math.min( 1788 (uint256(premiumOwed.leftSlot()) * settledTokens.leftSlot()) / 1789 (accumulated1 == 0 ? type(uint256).max : accumulated1), 1790 premiumOwed.leftSlot() 1791: ) 1877 uint256 positionLiquidity = PanopticMath 1878: .getLiquidityChunk(tokenId, leg, positionSize) 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: )
GitHub: 326, 415, 525, 712, 775, 905, 915, 923, 943, 981, 1035, 1063, 1102, 1103, 1098, 1129, 1122, 1197, 1242, 1324, 1329, 1348, 1349, 1353, 1354, 1383, 1410, 1451, 1521, 1627, 1680, 1778, 1787, 1877, 1934, 1951
File: contracts/SemiFungiblePositionManager.sol 367: uint64 poolId = PanopticMath.getPoolId(univ3pool); 373: poolId = PanopticMath.incrementPoolPattern(poolId); 410: CallbackLib.validateCallback(msg.sender, FACTORY, decoded.poolFeatures); 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: ); 443: CallbackLib.validateCallback(msg.sender, FACTORY, decoded.poolFeatures); 456: SafeTransferLib.safeTransferFrom(token, decoded.payer, msg.sender, amountToPay); 604 LiquidityChunk liquidityChunk = PanopticMath.getLiquidityChunk( 605 id, 606 leg, 607 uint128(amount) 608: ); 817: int256 net0 = itm0 - PanopticMath.convert1to0(itm1, sqrtPriceX96); 904 LiquidityChunk liquidityChunk = PanopticMath.getLiquidityChunk( 905 _tokenId, 906 _leg, 907 _positionSize 908: ); 922: amount0 += Math.getAmount0ForLiquidity(liquidityChunk); 924: amount1 += Math.getAmount1ForLiquidity(liquidityChunk); 1123 (s_accountPremiumOwed[positionKey], s_accountPremiumGross[positionKey]) = LeftRightLibrary 1124 .addCapped( 1125 s_accountPremiumOwed[positionKey], 1126 deltaPremiumOwed, 1127 s_accountPremiumGross[positionKey], 1128 deltaPremiumGross 1129: ); 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)))); 1350 premium0X64_base = Math.mulDiv( 1351 collected0, 1352 totalLiquidity * 2 ** 64, 1353 netLiquidity ** 2 1354: ); 1355 premium1X64_base = Math.mulDiv( 1356 collected1, 1357 totalLiquidity * 2 ** 64, 1358 netLiquidity ** 2 1359: ); 1369 premium0X64_owed = Math 1370: .mulDiv(premium0X64_base, numerator, totalLiquidity) 1372 premium1X64_owed = Math 1373: .mulDiv(premium1X64_base, numerator, totalLiquidity) 1393 premium0X64_gross = Math 1394: .mulDiv(premium0X64_base, numerator, totalLiquidity ** 2) 1396 premium1X64_gross = Math 1397: .mulDiv(premium1X64_base, numerator, totalLiquidity ** 2) 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: );
GitHub: 367, 373, 410, 413, 420, 443, 456, 604, 817, 904, 922, 924, 1123, 1169, 1172, 1176, 1177, 1350, 1355, 1369, 1372, 1393, 1396, 1480, 1507
File: contracts/libraries/FeesCalc.sol 56 LiquidityChunk liquidityChunk = PanopticMath.getLiquidityChunk( 57 tokenId, 58 leg, 59 positionSize 60: ); 62 (uint256 amount0, uint256 amount1) = Math.getAmountsForLiquidity( 63 atTick, 64 liquidityChunk 65: ); 117: .toRightSlot(int128(int256(Math.mulDiv128(ammFeesPerLiqToken0X128, liquidity)))) 118: .toLeftSlot(int128(int256(Math.mulDiv128(ammFeesPerLiqToken1X128, liquidity))));
File: contracts/libraries/InteractionHelper.sol 81: Strings.toString(fee),
GitHub: 81
File: contracts/libraries/Math.sol 251 LiquidityChunkLibrary.createChunk( 252 tickLower, 253 tickUpper, 254 toUint128( 255 mulDiv( 256 amount0, 257 mulDiv96(highPriceX96, lowPriceX96), 258 highPriceX96 - lowPriceX96 259 ) 260 ) 261: ); 281 LiquidityChunkLibrary.createChunk( 282 tickLower, 283 tickUpper, 284 toUint128(mulDiv(amount1, Constants.FP96, highPriceX96 - lowPriceX96)) 285: );
File: contracts/libraries/PanopticMath.sol 77: return addr == address(0) ? 40 : 39 - Math.mostSignificantNibble(uint160(addr)); 155: return int24(Math.sort(ticks)[cardinality / 2]); 263: int256[] memory sortedTicks = Math.sort(twapMeasurement); 326: return Math.getLiquidityForAmount0(tickLower, tickUpper, amount); 328: return Math.getLiquidityForAmount1(tickLower, tickUpper, amount); 349: (int24 rangeDown, int24 rangeUp) = PanopticMath.getRangesFromStrike(width, tickSpacing); 377: int24(int256(Math.unsafeDivRoundingUp(uint24(width) * uint24(tickSpacing), 2))) 452: convertCollateralData(tokenData0, tokenData1, tokenType, Math.getSqrtRatioAtTick(tick)); 476: ? convert0to1(contractSize, Math.getSqrtRatioAtTick((tickUpper + tickLower) / 2)) 477: : convert1to0(contractSize, Math.getSqrtRatioAtTick((tickUpper + tickLower) / 2)); 495: return Math.mulDiv192(amount, uint256(sqrtPriceX96) ** 2); 497: return Math.mulDiv128(amount, Math.mulDiv64(sqrtPriceX96, sqrtPriceX96)); 497: return Math.mulDiv128(amount, Math.mulDiv64(sqrtPriceX96, sqrtPriceX96)); 512: return Math.mulDiv(amount, 2 ** 192, uint256(sqrtPriceX96) ** 2); 514: return Math.mulDiv(amount, 2 ** 128, Math.mulDiv64(sqrtPriceX96, sqrtPriceX96)); 514: return Math.mulDiv(amount, 2 ** 128, Math.mulDiv64(sqrtPriceX96, sqrtPriceX96)); 530: .mulDiv192(Math.absUint(amount), uint256(sqrtPriceX96) ** 2) 529 int256 absResult = Math 530: .mulDiv192(Math.absUint(amount), uint256(sqrtPriceX96) ** 2) 535: .mulDiv128(Math.absUint(amount), Math.mulDiv64(sqrtPriceX96, sqrtPriceX96)) 535: .mulDiv128(Math.absUint(amount), Math.mulDiv64(sqrtPriceX96, sqrtPriceX96)) 534 int256 absResult = Math 535: .mulDiv128(Math.absUint(amount), Math.mulDiv64(sqrtPriceX96, sqrtPriceX96)) 553: .mulDiv(Math.absUint(amount), 2 ** 192, uint256(sqrtPriceX96) ** 2) 552 int256 absResult = Math 553: .mulDiv(Math.absUint(amount), 2 ** 192, uint256(sqrtPriceX96) ** 2) 559: Math.absUint(amount), 561: Math.mulDiv64(sqrtPriceX96, sqrtPriceX96) 557 int256 absResult = Math 558 .mulDiv( 559 Math.absUint(amount), 560 2 ** 128, 561 Math.mulDiv64(sqrtPriceX96, sqrtPriceX96) 562: ) 588: .getAmount1ForLiquidity(Math.getLiquidityForAmount0(tickLower, tickUpper, amount0)) 587 amount1 = Math 588: .getAmount1ForLiquidity(Math.getLiquidityForAmount0(tickLower, tickUpper, amount0)) 594: .getAmount0ForLiquidity(Math.getLiquidityForAmount1(tickLower, tickUpper, amount1)) 593 amount0 = Math 594: .getAmount0ForLiquidity(Math.getLiquidityForAmount1(tickLower, tickUpper, amount1)) 621: shorts = shorts.toRightSlot(Math.toInt128(amountsMoved.rightSlot())); 624: longs = longs.toRightSlot(Math.toInt128(amountsMoved.rightSlot())); 629: shorts = shorts.toLeftSlot(Math.toInt128(amountsMoved.leftSlot())); 632: longs = longs.toLeftSlot(Math.toInt128(amountsMoved.leftSlot())); 664 uint256 required0 = PanopticMath.convert0to1( 665 tokenData0.leftSlot(), 666 sqrtPriceX96Twap 667: ); 671 (uint256 balanceCross, uint256 thresholdCross) = PanopticMath.convertCollateralData( 672 tokenData0, 673 tokenData1, 674 0, 675 sqrtPriceX96Twap 676: ); 678: uint256 bonusCross = Math.min(balanceCross / 2, thresholdCross - balanceCross); 681: bonus0 = int256(Math.mulDiv128(bonusCross, requiredRatioX128)); 685: Math.mulDiv128(bonusCross, 2 ** 128 - requiredRatioX128), 684 PanopticMath.convert0to1( 685 Math.mulDiv128(bonusCross, 2 ** 128 - requiredRatioX128), 686 sqrtPriceX96Final 687: ) 694: Math.max(premia.rightSlot(), 0); 696: Math.max(premia.leftSlot(), 0); 717: PanopticMath.convert0to1(paid0 - balance0, sqrtPriceX96Final) 715 bonus1 += Math.min( 716 balance1 - paid1, 717 PanopticMath.convert0to1(paid0 - balance0, sqrtPriceX96Final) 718: ); 720: PanopticMath.convert1to0(balance1 - paid1, sqrtPriceX96Final), 719 bonus0 -= Math.min( 720 PanopticMath.convert1to0(balance1 - paid1, sqrtPriceX96Final), 721 paid0 - balance0 722: ); 735: PanopticMath.convert1to0(paid1 - balance1, sqrtPriceX96Final) 733 bonus0 += Math.min( 734 balance0 - paid0, 735 PanopticMath.convert1to0(paid1 - balance1, sqrtPriceX96Final) 736: ); 738: PanopticMath.convert0to1(balance0 - paid0, sqrtPriceX96Final), 737 bonus1 -= Math.min( 738 PanopticMath.convert0to1(balance0 - paid0, sqrtPriceX96Final), 739 paid1 - balance1 740: ); 791: int256 collateralDelta0 = -Math.min(collateralRemaining.rightSlot(), 0); 792: int256 collateralDelta1 = -Math.min(collateralRemaining.leftSlot(), 0); 805 PanopticMath.convert1to0( 806 longPremium.leftSlot() - collateralDelta1, 807 sqrtPriceX96Final 808: ) 803 -Math.min( 804 collateralDelta0 - longPremium.rightSlot(), 805 PanopticMath.convert1to0( 806 longPremium.leftSlot() - collateralDelta1, 807 sqrtPriceX96Final 808 ) 809: ), 812 PanopticMath.convert0to1( 813 collateralDelta0 - longPremium.rightSlot(), 814 sqrtPriceX96Final 815: ) 810 Math.min( 811 longPremium.leftSlot() - collateralDelta1, 812 PanopticMath.convert0to1( 813 collateralDelta0 - longPremium.rightSlot(), 814 sqrtPriceX96Final 815 ) 816: ) 829 PanopticMath.convert1to0( 830 collateralDelta1 - longPremium.leftSlot(), 831 sqrtPriceX96Final 832: ) 827 Math.min( 828 longPremium.rightSlot() - collateralDelta0, 829 PanopticMath.convert1to0( 830 collateralDelta1 - longPremium.leftSlot(), 831 sqrtPriceX96Final 832 ) 833: ), 836 PanopticMath.convert0to1( 837 longPremium.rightSlot() - collateralDelta0, 838 sqrtPriceX96Final 839: ) 834 -Math.min( 835 collateralDelta1 - longPremium.leftSlot(), 836 PanopticMath.convert0to1( 837 longPremium.rightSlot() - collateralDelta0, 838 sqrtPriceX96Final 839 ) 840: ) 847: haircut0 = Math.min(collateralDelta0, longPremium.rightSlot()); 848: haircut1 = Math.min(collateralDelta1, longPremium.leftSlot()); 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: ); 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: ); 924: uint160 sqrtPriceX96 = Math.getSqrtRatioAtTick(atTick); 939: PanopticMath.convert0to1(uint256(balanceShortage), sqrtPriceX96) 957: PanopticMath.convert1to0(uint256(balanceShortage), sqrtPriceX96)
GitHub: 77, 155, 263, 326, 328, 349, 377, 452, 476, 477, 495, 497, 497, 512, 514, 514, 530, 529, 535, 535, 534, 553, 552, 559, 561, 557, 588, 587, 594, 593, 621, 624, 629, 632, 664, 671, 678, 681, 685, 684, 694, 696, 717, 715, 720, 719, 735, 733, 738, 737, 791, 792, 805, 803, 812, 810, 829, 827, 836, 834, 847, 848, 869, 873, 888, 892, 924, 939, 957
File: contracts/types/LeftRight.sol 265: z.toRightSlot(int128(Math.max(right128, 0))).toLeftSlot( 266: int128(Math.max(left128, 0))
</details>File: contracts/types/TokenId.sol 420 (legLowerTick, legUpperTick) = PanopticMath.getTicks( 421 self.strike(legIndex), 422 self.width(legIndex), 423 self.tickSpacing() 424: ); 582 (int24 rangeDown, int24 rangeUp) = PanopticMath.getRangesFromStrike( 583 self.width(i), 584 self.tickSpacing() 585: );
Doing so will prevent typo bugs where the first '!', '>', '<', or '=' at the beginning of the operator is missing.
There are 178 instances of this issue:
<details> <summary>see instances</summary>File: contracts/CollateralTracker.sol 331: if (s_panopticPool.numberOfPositions(msg.sender) != 0) revert Errors.PositionCountNotZero(); 350: if (s_panopticPool.numberOfPositions(from) != 0) revert Errors.PositionCountNotZero(); 512: return s_panopticPool.numberOfPositions(owner) == 0 ? Math.min(available, balance) : 0; 575: return s_panopticPool.numberOfPositions(owner) == 0 ? Math.min(available, balance) : 0; 664: if (positionId.isLong(leg) == 0) continue; 708: (tokenType == 0 && currentValue1 < oracleValue1) || 709: (tokenType == 1 && currentValue0 < oracleValue0) 776: if (utilization < 0) { 784: if (uint256(utilization) < TARGET_POOL_UTIL) { 790: if (uint256(utilization) > SATURATED_POOL_UTIL) { 835: if (utilization < TARGET_POOL_UTIL) { 841: if (utilization > SATURATED_POOL_UTIL) { 976: if (assets > 0) { 1010: if (tokenToPay > 0) { 1018: } else if (tokenToPay < 0) { 1060: if ((intrinsicValue != 0) && ((shortAmount != 0) || (longAmount != 0))) { 1060: if ((intrinsicValue != 0) && ((shortAmount != 0) || (longAmount != 0))) { 1060: if ((intrinsicValue != 0) && ((shortAmount != 0) || (longAmount != 0))) { 1067: if (tokenToPay > 0) { 1075: } else if (tokenToPay < 0) { 1107: if (intrinsicValue != 0) { 1168: if (positionBalanceArray.length > 0) { 1173: if (premiumAllPositions < 0) { 1182: if (premiumAllPositions > 0) { 1325: uint128 amountMoved = tokenType == 0 ? amountsMoved.rightSlot() : amountsMoved.leftSlot(); 1328: int64 utilization = tokenType == 0 1339: if (isLong == 0) { 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 1362: uint160 ratio = tokenType == 1 // tokenType 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 1451: if (isLong == 1) { 1479: if (isLong == 0) { 1488: } else if (isLong == 1) { 1544: if (tokenType == 0) { 1559: if (tokenType == 1) { 1582: tokenType == 0 ? movedRight : movedLeft, 1584: tokenType == 0 1637: uint128(uint64(-int64(poolUtilization0 == 0 ? 1 : poolUtilization0))) + 1638: (uint128(uint64(-int64(poolUtilization1 == 0 ? 1 : poolUtilization1))) << 64);
GitHub: 331, 350, 512, 575, 664, 708, 709, 776, 784, 790, 835, 841, 976, 1010, 1018, 1060, 1060, 1060, 1067, 1075, 1107, 1168, 1173, 1182, 1325, 1328, 1339, 1346, 1347, 1362, 1374, 1375, 1451, 1479, 1488, 1544, 1559, 1582, 1584, 1637, 1638
File: contracts/PanopticFactory.sol 181: if (amount0Owed > 0) 188: if (amount1Owed > 0) 351: if (token0 == WETH) { 355: } else if (token1 == WETH) {
File: contracts/PanopticPool.sol 460: if (tokenId.isLong(leg) == 0 && !includePendingPremium) { 533: if (medianData != 0) s_miniMedian = medianData; 638: if (LeftRightUnsigned.unwrap(s_positionBalance[msg.sender][tokenId]) != 0) 664: if (medianData != 0) s_miniMedian = medianData; 768: if (isLong == 1) { 865: if (tokenId.isLong(leg) == 0) { 943: if (Math.abs(int256(fastOracleTick) - slowOracleTick) > MAX_SLOW_FAST_DELTA) 1035: if (Math.abs(currentTick - twapTick) > MAX_TWAP_DELTA_LIQUIDATION) 1188: if (touchedId.length != 1) revert Errors.InputListFail(); 1274: if (positionIdListExercisor.length > 0) 1415: if ((newHash >> 248) > MAX_POSITIONS) revert Errors.TooManyPositionsOpen(); 1483: if (netLiquidity == 0) return; 1520: if ((isLong == 1) || computeAllPremia) { 1566: if (isLong == 1) { 1596: if (tokenId.isLong(legIndex) == 0 || legIndex > 3) revert Errors.NotALongLeg(); 1596: if (tokenId.isLong(legIndex) == 0 || legIndex > 3) revert Errors.NotALongLeg(); 1679: if (tokenId.isLong(leg) == 0) { 1780: (accumulated0 == 0 ? type(uint256).max : accumulated0), 1789: (accumulated1 == 0 ? type(uint256).max : accumulated1), 1862: if (LeftRightSigned.unwrap(legPremia) != 0) { 1864: if (tokenId.isLong(leg) == 1) { 1928: s_grossPremiumLast[chunkKey] = totalLiquidity != 0
GitHub: 460, 533, 638, 664, 768, 865, 943, 1035, 1188, 1274, 1415, 1483, 1520, 1566, 1596, 1596, 1679, 1780, 1789, 1862, 1864, 1928
File: contracts/SemiFungiblePositionManager.sol 362: if (s_AddrToPoolIdData[univ3pool] != 0) return; 412: if (amount0Owed > 0) 419: if (amount1Owed > 0) 446: address token = amount0Delta > 0 453: uint256 amountToPay = amount0Delta > 0 ? uint256(amount0Delta) : uint256(amount1Delta); 632: (LeftRightUnsigned.unwrap(s_accountLiquidity[positionKey_to]) != 0) || 633: (LeftRightSigned.unwrap(s_accountFeesBase[positionKey_to]) != 0) 688: if (positionSize == 0) revert Errors.OptionsBalanceZero(); 717: if ((LeftRightSigned.unwrap(itmAmounts) != 0)) { 787: if ((itm0 != 0) && (itm1 != 0)) { 787: if ((itm0 != 0) && (itm1 != 0)) { 819: zeroForOne = net0 < 0; 823: } else if (itm0 != 0) { 824: zeroForOne = itm0 < 0; 827: zeroForOne = itm1 > 0; 833: if (swapAmount == 0) return LeftRightSigned.wrap(0); 999: if (isLong == 0) { 1066: moved = isLong == 0 1073: if (tokenType == 1) { 1078: if (tokenType == 0) { 1085: if (currentLiquidity.rightSlot() > 0) { 1275: if (isLong == 1) { 1279: if (LeftRightSigned.unwrap(amountToCollect) != 0) { 1296: collected0 = movedInLeg.rightSlot() < 0 1299: collected1 = movedInLeg.leftSlot() < 0 1469: if (netLiquidity != 0) { 1514: acctPremia = isLong == 1 ? premiumOwed : premiumGross; 1518: acctPremia = isLong == 1
GitHub: 362, 412, 419, 446, 453, 632, 633, 688, 717, 787, 787, 819, 823, 824, 827, 833, 999, 1066, 1073, 1078, 1085, 1275, 1279, 1296, 1299, 1469, 1514, 1518
File: contracts/libraries/FeesCalc.sol 67: if (tokenId.isLong(leg) == 0) {
GitHub: 67
File: contracts/libraries/Math.sol 74: return x > 0 ? x : -x; 83: return x > 0 ? uint256(x) : uint256(-x); 93: if (x >= 0x100000000000000000000000000000000) { 97: if (x >= 0x10000000000000000) { 101: if (x >= 0x100000000) { 105: if (x >= 0x10000) { 109: if (x >= 0x100) { 113: if (x >= 0x10) { 130: uint256 absTick = tick < 0 ? uint256(-int256(tick)) : uint256(int256(tick)); 133: uint256 sqrtR = absTick & 0x1 != 0 137: if (absTick & 0x2 != 0) sqrtR = (sqrtR * 0xfff97272373d413259a46990580e213a) >> 128; 139: if (absTick & 0x4 != 0) sqrtR = (sqrtR * 0xfff2e50f5f656932ef12357cf3c7fdcc) >> 128; 141: if (absTick & 0x8 != 0) sqrtR = (sqrtR * 0xffe5caca7e10e4e61c3624eaa0941cd0) >> 128; 143: if (absTick & 0x10 != 0) sqrtR = (sqrtR * 0xffcb9843d60f6159c9db58835c926644) >> 128; 145: if (absTick & 0x20 != 0) sqrtR = (sqrtR * 0xff973b41fa98c081472e6896dfb254c0) >> 128; 147: if (absTick & 0x40 != 0) sqrtR = (sqrtR * 0xff2ea16466c96a3843ec78b326b52861) >> 128; 149: if (absTick & 0x80 != 0) sqrtR = (sqrtR * 0xfe5dee046a99a2a811c461f1969c3053) >> 128; 151: if (absTick & 0x100 != 0) sqrtR = (sqrtR * 0xfcbe86c7900a88aedcffc83b479aa3a4) >> 128; 153: if (absTick & 0x200 != 0) sqrtR = (sqrtR * 0xf987a7253ac413176f2b074cf7815e54) >> 128; 155: if (absTick & 0x400 != 0) sqrtR = (sqrtR * 0xf3392b0822b70005940c7a398e4b70f3) >> 128; 157: if (absTick & 0x800 != 0) sqrtR = (sqrtR * 0xe7159475a2c29b7443b29c7fa6e889d9) >> 128; 159: if (absTick & 0x1000 != 0) sqrtR = (sqrtR * 0xd097f3bdfd2022b8845ad8f792aa5825) >> 128; 161: if (absTick & 0x2000 != 0) sqrtR = (sqrtR * 0xa9f746462d870fdf8a65dc1f90e061e5) >> 128; 163: if (absTick & 0x4000 != 0) sqrtR = (sqrtR * 0x70d869a156d2a1b890bb3df62baf32f7) >> 128; 165: if (absTick & 0x8000 != 0) sqrtR = (sqrtR * 0x31be135f97d08fd981231505542fcfa6) >> 128; 167: if (absTick & 0x10000 != 0) sqrtR = (sqrtR * 0x9aa508b5b7a84e1c677de54f3e99bc9) >> 128; 169: if (absTick & 0x20000 != 0) sqrtR = (sqrtR * 0x5d6af8dedb81196699c329225ee604) >> 128; 171: if (absTick & 0x40000 != 0) sqrtR = (sqrtR * 0x2216e584f5fa1ea926041bedfe98) >> 128; 173: if (absTick & 0x80000 != 0) sqrtR = (sqrtR * 0x48a170391f7dc42444e8fa2) >> 128; 176: if (tick > 0) sqrtR = type(uint256).max / sqrtR; 179: return uint160((sqrtR >> 32) + (sqrtR % (1 << 32) == 0 ? 0 : 1)); 312: if ((downcastedInt = int128(toCast)) < 0) revert Errors.CastingError(); 360: if (prod1 == 0) { 361: require(denominator > 0); 447: if (mulmod(a, b, denominator) > 0) { 474: if (prod1 == 0) { 537: if (prod1 == 0) { 587: if (mulmod(a, b, 2 ** 96) > 0) { 614: if (prod1 == 0) { 664: if (mulmod(a, b, 2 ** 128) > 0) { 691: if (prod1 == 0) {
GitHub: 74, 83, 93, 97, 101, 105, 109, 113, 130, 133, 137, 139, 141, 143, 145, 147, 149, 151, 153, 155, 157, 159, 161, 163, 165, 167, 169, 171, 173, 176, 179, 312, 360, 361, 447, 474, 537, 587, 614, 664, 691
File: contracts/libraries/PanopticMath.sol 207: for (uint8 i; i < 8; ++i) { 211: if (rank == 7) { 248: for (uint256 i = 0; i < 20; ++i) { 256: for (uint256 i = 0; i < 19; ++i) { 325: if (tokenId.asset(legIndex) == 0) { 357: tickLower % tickSpacing != 0 || 358: tickUpper % tickSpacing != 0 || 425: if (tokenType == 0) { 475: uint256 notional = asset == 0 479: if (notional == 0 || notional > type(uint128).max) revert Errors.InvalidNotionalValue(); 532: return amount < 0 ? -absResult : absResult; 537: return amount < 0 ? -absResult : absResult; 555: return amount < 0 ? -absResult : absResult; 564: return amount < 0 ? -absResult : absResult; 584: if (tokenId.asset(legIndex) == 0) { 615: bool isShort = tokenId.isLong(legIndex) == 0; 618: if (tokenId.tokenType(legIndex) == 0) { 785: if (tokenId.isLong(leg) == 1) { 856: if (haircut0 != 0) collateral0.exercise(_liquidatee, 0, 0, 0, int128(haircut0)); 857: if (haircut1 != 0) collateral1.exercise(_liquidatee, 0, 0, 0, int128(haircut1)); 864: if (tokenId.isLong(leg) == 1) { 931: if (balanceShortage > 0) { 949: if (balanceShortage > 0) {
GitHub: 207, 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 112: if (to.code.length != 0) { 163: if (to.code.length != 0) { 202: interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165 203: interfaceId == 0xd9b67a26; // ERC165 Interface ID for ERC1155 222: if (to.code.length != 0) {
GitHub: 112, 163, 202, 203, 222
File: contracts/types/TokenId.sol 465: if (i == 0) 471: if (i == 1) 477: if (i == 2) 483: if (i == 3) 501: if (self.optionRatio(0) == 0) revert Errors.InvalidTokenIdParameter(1); 507: for (uint256 i = 0; i < 4; ++i) { 508: if (self.optionRatio(i) == 0) { 512: if ((TokenId.unwrap(self) >> (64 + 48 * i)) != 0) 528: if ((self.width(i) == 0)) revert Errors.InvalidTokenIdParameter(5); 531: (self.strike(i) == Constants.MIN_V3POOL_TICK) || 532: (self.strike(i) == Constants.MAX_V3POOL_TICK) 566: if (((_isLong != isLongP) || _isLong == 1) && (_tokenType != tokenTypeP)) 592: if (self.isLong(i) == 1) return; // validated
GitHub: 465, 471, 477, 483, 501, 507, 508, 512, 528, 531, 532, 566, 592
</details>require()
/revert()
as well as custom errorsConsider using just one method in a single file
There is one instance of this issue:
File: contracts/libraries/Math.sol 13 library Math { 14: /// @notice This is equivalent to type(uint256).max ā used in assembly blocks as a replacement.
GitHub: 13
public
/external
functions exposed by interface
sThe contract
s should expose an interface
so that other projects can more easily integrate with it, without having to develop their own non-standard variants.
There are 4 instances of this issue:
File: contracts/CollateralTracker.sol /// @audit startToken(), getPoolData(), name(), symbol(), decimals(), asset(), totalAssets(), convertToShares(), convertToAssets(), maxDeposit(), previewDeposit(), deposit(), maxMint(), previewMint(), mint(), maxWithdraw(), previewWithdraw(), withdraw(), maxRedeem(), previewRedeem(), redeem(), exerciseCost(), delegate(), delegate(), refund(), refund(), revoke(), takeCommissionAddData(), exercise(), getAccountMarginDetails() 36 contract CollateralTracker is ERC20Minimal, Multicall { 37: // Used for safecasting
GitHub: 36
File: contracts/PanopticFactory.sol /// @audit initialize(), transferOwnership(), owner(), uniswapV3MintCallback(), deployNewPool(), minePoolAddress(), getPanopticPool() 26 contract PanopticFactory is Multicall { 27 /*////////////////////////////////////////////////////////////// 28 EVENTS 29 //////////////////////////////////////////////////////////////*/ 30 31 /// @notice Emitted when the ownership of the factory is transferred. 32 /// @param oldOwner The previous owner of the factory 33: /// @param newOwner The new owner of the factory
GitHub: 26
File: contracts/PanopticPool.sol /// @audit startPool(), assertPriceWithinBounds(), optionPositionBalance(), calculateAccumulatedFeesBatch(), calculatePortfolioValue(), pokeMedian(), mintOptions(), burnOptions(), burnOptions(), liquidate(), forceExercise(), univ3pool(), collateralToken0(), collateralToken1(), numberOfPositions(), settleLongPremium() 27 contract PanopticPool is ERC1155Holder, Multicall { 28 /*////////////////////////////////////////////////////////////// 29 EVENTS 30 //////////////////////////////////////////////////////////////*/ 31 32 /// @notice Emitted when an account is liquidated. 33 /// @dev Need to unpack bonusAmounts to get raw numbers, which are always positive. 34 /// @param liquidator Address of the caller whom is liquidating the distressed account. 35 /// @param liquidatee Address of the distressed/liquidatable account. 36 /// @param bonusAmounts LeftRight encoding for the the bonus paid for token 0 (right slot) and 1 (left slot) from the Panoptic Pool to the liquidator. 37: /// The token0 bonus is in the right slot, and token1 bonus is in the left slot.
GitHub: 27
File: contracts/SemiFungiblePositionManager.sol /// @audit initializeAMMPool(), uniswapV3MintCallback(), uniswapV3SwapCallback(), burnTokenizedPosition(), mintTokenizedPosition(), getAccountLiquidity(), getAccountPremium(), getAccountFeesBase(), getUniswapV3PoolFromId(), getPoolId() 72 contract SemiFungiblePositionManager is ERC1155, Multicall { 73 /*////////////////////////////////////////////////////////////// 74 EVENTS 75 //////////////////////////////////////////////////////////////*/ 76 77 /// @notice Emitted when a UniswapV3Pool is initialized. 78 /// @param uniswapPool Address of the underlying Uniswap v3 pool 79: /// @param poolId The SFPM's pool identifier for the pool, including the 16-bit tick spacing and 48-bit pool pattern
GitHub: 72
Consider adding parameters to the error to indicate which user or values caused the failure
There are 2 instances of this issue:
File: contracts/libraries/Errors.sol 26: error ExerciseeNotSolvent(); 48: error LeftRightInputError();
revert()
/require()
Custom errors are available from solidity version 0.8.4. Custom errors are more easily processed in try
-catch
blocks, and are easier to re-use and maintain.
There are 9 instances of this issue:
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);
GitHub: 361, 370, 448, 484, 547, 588, 624, 665, 701
require()
/revert()
checks should be refactored to a modifier or functionThere are 3 instances of this issue:
File: contracts/libraries/Math.sol 448: require(result < type(uint256).max); 588: require(result < type(uint256).max); 665: require(result < type(uint256).max);
immutable
rather than constant
While it does not save gas for some simple binary expressions because the compiler knows that developers often make this mistake, it's still best to use the right tool for the task at hand. There is a difference between constant
variables and immutable
variables, and they should each be used in their appropriate contexts. constants
should be used for literal values written into the code, and immutable
variables should be used for expressions, or values calculated in, or passed into the constructor. As is the case with const correctness, the code will still work, but the source code is more standard if the proper types are used. The compiler has included a lot of special-cased expression-in-constant-related handling code, and at the very least, by using immutable
s rather than constant
s, you can avoid any sub-optimial optimizations/bugs related to this less-frequently-used code.
There are 7 instances of this issue:
File: contracts/PanopticPool.sol 103: int24 internal constant MIN_SWAP_TICK = Constants.MIN_V3POOL_TICK + 1; 105: int24 internal constant MAX_SWAP_TICK = Constants.MAX_V3POOL_TICK - 1; 165: uint64 internal constant MAX_SPREAD = 9 * (2 ** 32);
File: contracts/libraries/Constants.sol 12: int24 internal constant MIN_V3POOL_TICK = -887272;
GitHub: 12
File: contracts/libraries/Math.sol 15: uint256 internal constant MAX_UINT256 = 2 ** 256 - 1;
GitHub: 15
File: contracts/libraries/PanopticMath.sol 23: uint256 internal constant MAX_UINT256 = 2 ** 256 - 1;
GitHub: 23
File: contracts/types/LeftRight.sol 26 int256 internal constant LEFT_HALF_BIT_MASK_INT = 27: int256(uint256(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000000000000000000000000000));
GitHub: 26
The contract's interface should be imported first, followed by each of the interfaces it uses, followed by all other files. The examples below do not follow this layout.
There is one instance of this issue:
File: contracts/PanopticFactory.sol 8: import {IDonorNFT} from "@contracts/tokens/interfaces/IDonorNFT.sol";
GitHub: 8
Some lines use // x
and some use //x
. The instances below point out the usages that don't follow the majority, within each file
There are 5 instances of this issue:
File: contracts/CollateralTracker.sol 1118: //compute total commission amount = commission rate + spread fee
GitHub: 1118
File: contracts/SemiFungiblePositionManager.sol 610: //construct the positionKey for the from and to addresses 643: //update+store liquidity and fee values between accounts 821: //compute the swap amount, set as positive (exact input) 988: LeftRightUnsigned currentLiquidity = s_accountLiquidity[positionKey]; //cache
Large multiples of ten should use scientific notation (e.g. 1e6
) rather than decimal literals (e.g. 1000000
), for readability
There are 8 instances of this issue:
File: contracts/CollateralTracker.sol /// @audit 10_000 77: uint256 internal constant DECIMALS = 10_000; /// @audit 10_000 81: int128 internal constant DECIMALS_128 = 10_000; /// @audit 2000 200: int256 ratioTick = (int256(_sellerCollateralRatio) - 2000); /// @audit 10_000 203 (12500 * ratioTick) / 204: 10_000 + /// @audit 10_000 206: 10_000 ** 2 + /// @audit 10_000 208: 10_000 ** 3
GitHub: 77, 81, 200, 203, 206, 208
File: contracts/PanopticPool.sol /// @audit 10_000 174: uint256 internal constant NO_BUFFER = 10_000; /// @audit 10_000 1329: return balanceCross >= Math.unsafeDivRoundingUp(thresholdCross * buffer, 10_000);
The documentation states that Inline assembly that neither involves any operations that access memory nor assigns to any Solidity variables in memory is automatically considered memory-safe and does not need to be annotated
, and the blocks do none of those operations.
There are 28 instances of this issue:
File: contracts/libraries/Math.sol 352 uint256 prod1; // Most significant 256 bits of the product 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: } 361 require(denominator > 0); 362 assembly ("memory-safe") { 363 result := div(prod0, denominator) 364: } 378 uint256 remainder; 379 assembly ("memory-safe") { 380 remainder := mulmod(a, b, denominator) 381: } 382 // Subtract 256 bit number from 512 bit number 383 assembly ("memory-safe") { 384 prod1 := sub(prod1, gt(remainder, prod0)) 385 prod0 := sub(prod0, remainder) 386: } 392 // Divide denominator by power of two 393 assembly ("memory-safe") { 394 denominator := div(denominator, twos) 395: } 397 // Divide [prod1 prod0] by the factors of two 398 assembly ("memory-safe") { 399 prod0 := div(prod0, twos) 400: } 403 // If twos is zero, then it becomes one 404 assembly ("memory-safe") { 405 twos := add(div(sub(0, twos), twos), 1) 406: } 466 uint256 prod1; // Most significant 256 bits of the product 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: } 475 uint256 res; 476 assembly ("memory-safe") { 477 // Right shift by n is equivalent and 2 gas cheaper than division by 2^n 478 res := shr(64, prod0) 479: } 492 uint256 remainder; 493 assembly ("memory-safe") { 494 remainder := mulmod(a, b, 0x10000000000000000) 495: } 496 // Subtract 256 bit number from 512 bit number 497 assembly ("memory-safe") { 498 prod1 := sub(prod1, gt(remainder, prod0)) 499 prod0 := sub(prod0, remainder) 500: } 502 // Divide [prod1 prod0] by the factors of two (note that this is just 2**96 since the denominator is a power of 2 itself) 503 assembly ("memory-safe") { 504 // Right shift by n is equivalent and 2 gas cheaper than division by 2^n 505 prod0 := shr(64, prod0) 506: } 529 uint256 prod1; // Most significant 256 bits of the product 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: } 538 uint256 res; 539 assembly ("memory-safe") { 540 // Right shift by n is equivalent and 2 gas cheaper than division by 2^n 541 res := shr(96, prod0) 542: } 555 uint256 remainder; 556 assembly ("memory-safe") { 557 remainder := mulmod(a, b, 0x1000000000000000000000000) 558: } 559 // Subtract 256 bit number from 512 bit number 560 assembly ("memory-safe") { 561 prod1 := sub(prod1, gt(remainder, prod0)) 562 prod0 := sub(prod0, remainder) 563: } 565 // Divide [prod1 prod0] by the factors of two (note that this is just 2**96 since the denominator is a power of 2 itself) 566 assembly ("memory-safe") { 567 // Right shift by n is equivalent and 2 gas cheaper than division by 2^n 568 prod0 := shr(96, prod0) 569: } 606 uint256 prod1; // Most significant 256 bits of the product 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: } 615 uint256 res; 616 assembly ("memory-safe") { 617 // Right shift by n is equivalent and 2 gas cheaper than division by 2^n 618 res := shr(128, prod0) 619: } 632 uint256 remainder; 633 assembly ("memory-safe") { 634 remainder := mulmod(a, b, 0x100000000000000000000000000000000) 635: } 636 // Subtract 256 bit number from 512 bit number 637 assembly ("memory-safe") { 638 prod1 := sub(prod1, gt(remainder, prod0)) 639 prod0 := sub(prod0, remainder) 640: } 642 // Divide [prod1 prod0] by the factors of two (note that this is just 2**128 since the denominator is a power of 2 itself) 643 assembly ("memory-safe") { 644 // Right shift by n is equivalent and 2 gas cheaper than division by 2^n 645 prod0 := shr(128, prod0) 646: } 683 uint256 prod1; // Most significant 256 bits of the product 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: } 692 uint256 res; 693 assembly ("memory-safe") { 694 // Right shift by n is equivalent and 2 gas cheaper than division by 2^n 695 res := shr(192, prod0) 696: } 709 uint256 remainder; 710 assembly ("memory-safe") { 711 remainder := mulmod(a, b, 0x1000000000000000000000000000000000000000000000000) 712: } 713 // Subtract 256 bit number from 512 bit number 714 assembly ("memory-safe") { 715 prod1 := sub(prod1, gt(remainder, prod0)) 716 prod0 := sub(prod0, remainder) 717: } 719 // Divide [prod1 prod0] by the factors of two (note that this is just 2**96 since the denominator is a power of 2 itself) 720 assembly ("memory-safe") { 721 // Right shift by n is equivalent and 2 gas cheaper than division by 2^n 722 prod0 := shr(192, prod0) 723: } 738 function unsafeDivRoundingUp(uint256 a, uint256 b) internal pure returns (uint256 result) { 739 assembly ("memory-safe") { 740 result := add(div(a, b), gt(mod(a, b), 0)) 741: }
GitHub: 352, 361, 378, 382, 392, 397, 403, 466, 475, 492, 496, 502, 529, 538, 555, 559, 565, 606, 615, 632, 636, 642, 683, 692, 709, 713, 719, 738
Events help non-contract tools to track changes
There are 7 instances of this issue:
File: contracts/CollateralTracker.sol /// @audit s_underlyingIsToken0: startToken() /// @audit s_univ3token1: startToken() /// @audit s_univ3token0: startToken() /// @audit s_panopticPool: startToken() 221 function startToken( 222 bool underlyingIsToken0, 223 address token0, 224 address token1, 225 uint24 fee, 226 PanopticPool panopticPool 227: ) external {
File: contracts/PanopticFactory.sol /// @audit s_owner: initialize() 134: function initialize(address _owner) public {
GitHub: 134
File: contracts/PanopticPool.sol /// @audit s_collateralToken1: startPool() /// @audit s_collateralToken0: startPool() 291 function startPool( 292 IUniswapV3Pool _univ3pool, 293 address token0, 294 address token1, 295 CollateralTracker collateralTracker0, 296 CollateralTracker collateralTracker1 297: ) external {
address
/ID mappings can be combined into a single mapping
of an address
/ID to a struct
, for readabilityWell-organized data structures make code reviews easier, which may lead to fewer bugs. Consider combining related mappings into mappings to structs, so it's clear what data is related. The instances below refer to both mappings using the same key in the same function, so the mappings are related.
There are 12 instances of this issue:
File: contracts/PanopticPool.sol /// @audit combine into a `struct`: s_grossPremiumLast,s_settledTokens 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) { 436 uint256 pLength = positionIdList.length; 437 balances = new uint256[2][](pLength); 438 439 address c_user = user; 440 // loop through each option position/tokenId 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 } 491 return (portfolioPremium, balances); 492: } /// @audit combine into a `struct`: s_options,s_positionBalance 859 function _updatePositionDataBurn(address owner, TokenId tokenId) internal { 860 // reset balances and delete stored option data 861 s_positionBalance[owner][tokenId] = LeftRightUnsigned.wrap(0); 862 863 uint256 numLegs = tokenId.countLegs(); 864 for (uint256 leg = 0; leg < numLegs; ) { 865 if (tokenId.isLong(leg) == 0) { 866 // Check the liquidity spread, make sure that closing the option does not exceed the MAX_SPREAD allowed 867 (int24 tickLower, int24 tickUpper) = tokenId.asTicks(leg); 868 _checkLiquiditySpread(tokenId, leg, tickLower, tickUpper, MAX_SPREAD); 869 } 870 s_options[owner][tokenId][leg] = LeftRightUnsigned.wrap(0); 871 unchecked { 872 ++leg; 873 } 874 } 875 876 // Update the position list hash (hash = XOR of all keccak256(tokenId)). Remove hash by XOR'ing again 877 _updatePositionsHash(owner, tokenId, !ADD); 878: } /// @audit combine into a `struct`: s_options,s_positionBalance 1587 function settleLongPremium( 1588 TokenId[] calldata positionIdList, 1589 address owner, 1590 uint256 legIndex 1591 ) external { 1592 _validatePositionList(owner, positionIdList, 0); 1593 1594 TokenId tokenId = positionIdList[positionIdList.length - 1]; 1595 1596 if (tokenId.isLong(legIndex) == 0 || legIndex > 3) revert Errors.NotALongLeg(); 1597 1598 (, int24 currentTick, , , , , ) = s_univ3pool.slot0(); 1599 1600 LeftRightUnsigned accumulatedPremium; 1601 { 1602 (int24 tickLower, int24 tickUpper) = tokenId.asTicks(legIndex); 1603 1604 uint256 tokenType = tokenId.tokenType(legIndex); 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 ); 1614 accumulatedPremium = LeftRightUnsigned 1615 .wrap(0) 1616 .toRightSlot(premiumAccumulator0) 1617 .toLeftSlot(premiumAccumulator1); 1618 1619 // update the premium accumulator for the long position to the latest value 1620 // (the entire premia delta will be settled) 1621 LeftRightUnsigned premiumAccumulatorsLast = s_options[owner][tokenId][legIndex]; 1622 s_options[owner][tokenId][legIndex] = accumulatedPremium; 1623 1624 accumulatedPremium = accumulatedPremium.sub(premiumAccumulatorsLast); 1625 } 1626 1627 uint256 liquidity = PanopticMath 1628 .getLiquidityChunk(tokenId, legIndex, s_positionBalance[owner][tokenId].rightSlot()) 1629 .liquidity(); 1630 1631 unchecked { 1632 // update the realized premia 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))); 1637 1638 // deduct the paid premium tokens from the owner's balance and add them to the cumulative settled token delta 1639 s_collateralToken0.exercise(owner, 0, 0, 0, realizedPremia.rightSlot()); 1640 s_collateralToken1.exercise(owner, 0, 0, 0, realizedPremia.leftSlot()); 1641 1642 bytes32 chunkKey = keccak256( 1643 abi.encodePacked( 1644 tokenId.strike(legIndex), 1645 tokenId.width(legIndex), 1646 tokenId.tokenType(legIndex) 1647 ) 1648 ); 1649 // commit the delta in settled tokens (all of the premium paid by long chunks in the tokenIds list) to storage 1650 s_settledTokens[chunkKey] = s_settledTokens[chunkKey].add( 1651 LeftRightUnsigned.wrap(uint256(LeftRightSigned.unwrap(realizedPremia))) 1652 ); 1653 1654 emit PremiumSettled(owner, tokenId, realizedPremia); 1655 } 1656 1657 // ensure the owner is solvent (insolvent accounts are not permitted to pay premium unless they are being liquidated) 1658 _validateSolvency(owner, positionIdList, NO_BUFFER); 1659: } /// @audit combine into a `struct`: s_grossPremiumLast,s_settledTokens 1666 function _updateSettlementPostMint( 1667 TokenId tokenId, 1668 LeftRightUnsigned[4] memory collectedByLeg, 1669 uint128 positionSize 1670 ) internal { 1671 uint256 numLegs = tokenId.countLegs(); 1672 for (uint256 leg = 0; leg < numLegs; ++leg) { 1673 bytes32 chunkKey = keccak256( 1674 abi.encodePacked(tokenId.strike(leg), tokenId.width(leg), tokenId.tokenType(leg)) 1675 ); 1676 // add any tokens collected from Uniswap in a given chunk to the settled tokens available for withdrawal by sellers 1677 s_settledTokens[chunkKey] = s_settledTokens[chunkKey].add(collectedByLeg[leg]); 1678 1679 if (tokenId.isLong(leg) == 0) { 1680 LiquidityChunk liquidityChunk = PanopticMath.getLiquidityChunk( 1681 tokenId, 1682 leg, 1683 positionSize 1684 ); 1685 1686 // new totalLiquidity (total sold) = removedLiquidity + netLiquidity (R + N) 1687 uint256 totalLiquidity = _getTotalLiquidity(tokenId, leg); 1688 1689 // We need to adjust the grossPremiumLast value such that the result of 1690 // (grossPremium - adjustedGrossPremiumLast)*updatedTotalLiquidityPostMint/2**64 is equal to (grossPremium - grossPremiumLast)*totalLiquidityBeforeMint/2**64 1691 // G: total gross premium 1692 // T: totalLiquidityBeforeMint 1693 // R: positionLiquidity 1694 // C: current grossPremium value 1695 // L: current grossPremiumLast value 1696 // Ln: updated grossPremiumLast value 1697 // T * (C - L) = G 1698 // (T + R) * (C - Ln) = G 1699 // 1700 // T * (C - L) = (T + R) * (C - Ln) 1701 // (TC - TL) / (T + R) = C - Ln 1702 // Ln = C - (TC - TL)/(T + R) 1703 // Ln = (CT + CR - TC + TL)/(T+R) 1704 // Ln = (CR + TL)/(T+R) 1705 1706 uint256[2] memory grossCurrent; 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 ); 1716 1717 unchecked { 1718 // L 1719 LeftRightUnsigned grossPremiumLast = s_grossPremiumLast[chunkKey]; 1720 // R 1721 uint256 positionLiquidity = liquidityChunk.liquidity(); 1722 // T (totalLiquidity is (T + R) after minting) 1723 uint256 totalLiquidityBefore = totalLiquidity - positionLiquidity; 1724 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 ); 1743 } 1744 } 1745 } 1746: } /// @audit combine into a `struct`: s_grossPremiumLast,s_settledTokens 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) { 1840 uint256 numLegs = tokenId.countLegs(); 1841 uint256[2][4] memory premiumAccumulatorsByLeg; 1842 1843 // compute accumulated fees 1844 (premiaByLeg, premiumAccumulatorsByLeg) = _getPremia( 1845 tokenId, 1846 positionSize, 1847 owner, 1848 COMPUTE_ALL_PREMIA, 1849 type(int24).max 1850 ); 1851 1852 for (uint256 leg = 0; leg < numLegs; ) { 1853 LeftRightSigned legPremia = premiaByLeg[leg]; 1854 1855 bytes32 chunkKey = keccak256( 1856 abi.encodePacked(tokenId.strike(leg), tokenId.width(leg), tokenId.tokenType(leg)) 1857 ); 1858 1859 // collected from Uniswap 1860 LeftRightUnsigned settledTokens = s_settledTokens[chunkKey].add(collectedByLeg[leg]); 1861 1862 if (LeftRightSigned.unwrap(legPremia) != 0) { 1863 // (will be) paid by long legs 1864 if (tokenId.isLong(leg) == 1) { 1865 if (commitLongSettled) 1866 settledTokens = LeftRightUnsigned.wrap( 1867 uint256( 1868 LeftRightSigned.unwrap( 1869 LeftRightSigned 1870 .wrap(int256(LeftRightUnsigned.unwrap(settledTokens))) 1871 .sub(legPremia) 1872 ) 1873 ) 1874 ); 1875 realizedPremia = realizedPremia.add(legPremia); 1876 } else { 1877 uint256 positionLiquidity = PanopticMath 1878 .getLiquidityChunk(tokenId, leg, positionSize) 1879 .liquidity(); 1880 1881 // new totalLiquidity (total sold) = removedLiquidity + netLiquidity (T - R) 1882 uint256 totalLiquidity = _getTotalLiquidity(tokenId, leg); 1883 // T (totalLiquidity is (T - R) after burning) 1884 uint256 totalLiquidityBefore = totalLiquidity + positionLiquidity; 1885 1886 LeftRightUnsigned grossPremiumLast = s_grossPremiumLast[chunkKey]; 1887 1888 LeftRightUnsigned availablePremium = _getAvailablePremium( 1889 totalLiquidity + positionLiquidity, 1890 settledTokens, 1891 grossPremiumLast, 1892 LeftRightUnsigned.wrap(uint256(LeftRightSigned.unwrap(legPremia))), 1893 premiumAccumulatorsByLeg[leg] 1894 ); 1895 1896 // subtract settled tokens sent to seller 1897 settledTokens = settledTokens.sub(availablePremium); 1898 1899 // add available premium to amount that should be settled 1900 realizedPremia = realizedPremia.add( 1901 LeftRightSigned.wrap(int256(LeftRightUnsigned.unwrap(availablePremium))) 1902 ); 1903 1904 // We need to adjust the grossPremiumLast value such that the result of 1905 // (grossPremium - adjustedGrossPremiumLast)*updatedTotalLiquidityPostBurn/2**64 is equal to 1906 // (grossPremium - grossPremiumLast)*totalLiquidityBeforeBurn/2**64 - premiumOwedToPosition 1907 // G: total gross premium (- premiumOwedToPosition) 1908 // T: totalLiquidityBeforeMint 1909 // R: positionLiquidity 1910 // C: current grossPremium value 1911 // L: current grossPremiumLast value 1912 // Ln: updated grossPremiumLast value 1913 // T * (C - L) = G 1914 // (T - R) * (C - Ln) = G - P 1915 // 1916 // T * (C - L) = (T - R) * (C - Ln) + P 1917 // (TC - TL - P) / (T - R) = C - Ln 1918 // Ln = C - (TC - TL - P) / (T - R) 1919 // Ln = (TC - CR - TC + LT + P) / (T-R) 1920 // Ln = (LT - CR + P) / (T-R) 1921 1922 unchecked { 1923 uint256[2][4] memory _premiumAccumulatorsByLeg = premiumAccumulatorsByLeg; 1924 uint256 _leg = leg; 1925 1926 // if there's still liquidity, compute the new grossPremiumLast 1927 // otherwise, we just reset grossPremiumLast to the current grossPremium 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])); 1969 } 1970 } 1971 } 1972 1973 // update settled tokens in storage with all local deltas 1974 s_settledTokens[chunkKey] = settledTokens; 1975 1976 unchecked { 1977 ++leg; 1978 } 1979 } 1980: }
GitHub: 429, 859, 1587, 1666, 1833
File: contracts/SemiFungiblePositionManager.sol /// @audit combine into a `struct`: s_accountFeesBase,s_accountLiquidity 593 function registerTokenTransfer(address from, address to, TokenId id, uint256 amount) internal { 594 // Validate tokenId 595 id.validate(); 596 597 // Extract univ3pool from the poolId map to Uniswap Pool 598 IUniswapV3Pool univ3pool = s_poolContext[id.poolId()].pool; 599 600 uint256 numLegs = id.countLegs(); 601 for (uint256 leg = 0; leg < numLegs; ) { 602 // for this leg index: extract the liquidity chunk: a 256bit word containing the liquidity amount and upper/lower tick 603 // @dev see `contracts/types/LiquidityChunk.sol` 604 LiquidityChunk liquidityChunk = PanopticMath.getLiquidityChunk( 605 id, 606 leg, 607 uint128(amount) 608 ); 609 610 //construct the positionKey for the from and to addresses 611 bytes32 positionKey_from = keccak256( 612 abi.encodePacked( 613 address(univ3pool), 614 from, 615 id.tokenType(leg), 616 liquidityChunk.tickLower(), 617 liquidityChunk.tickUpper() 618 ) 619 ); 620 bytes32 positionKey_to = keccak256( 621 abi.encodePacked( 622 address(univ3pool), 623 to, 624 id.tokenType(leg), 625 liquidityChunk.tickLower(), 626 liquidityChunk.tickUpper() 627 ) 628 ); 629 630 // Revert if recipient already has that position 631 if ( 632 (LeftRightUnsigned.unwrap(s_accountLiquidity[positionKey_to]) != 0) || 633 (LeftRightSigned.unwrap(s_accountFeesBase[positionKey_to]) != 0) 634 ) revert Errors.TransferFailed(); 635 636 // Revert if sender has long positions in that chunk or the entire liquidity is not being transferred 637 LeftRightUnsigned fromLiq = s_accountLiquidity[positionKey_from]; 638 if (LeftRightUnsigned.unwrap(fromLiq) != liquidityChunk.liquidity()) 639 revert Errors.TransferFailed(); 640 641 LeftRightSigned fromBase = s_accountFeesBase[positionKey_from]; 642 643 //update+store liquidity and fee values between accounts 644 s_accountLiquidity[positionKey_to] = fromLiq; 645 s_accountLiquidity[positionKey_from] = LeftRightUnsigned.wrap(0); 646 647 s_accountFeesBase[positionKey_to] = fromBase; 648 s_accountFeesBase[positionKey_from] = LeftRightSigned.wrap(0); 649 unchecked { 650 ++leg; 651 } 652 } 653: } /// @audit combine into a `struct`: s_accountFeesBase,s_accountLiquidity 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 { 972 uint256 tokenType = tokenId.tokenType(leg); 973 // unique key to identify the liquidity chunk in this uniswap pool 974 bytes32 positionKey = keccak256( 975 abi.encodePacked( 976 address(univ3pool), 977 msg.sender, 978 tokenType, 979 liquidityChunk.tickLower(), 980 liquidityChunk.tickUpper() 981 ) 982 ); 983 984 // update our internal bookkeeping of how much liquidity we have deployed in the AMM 985 // for example: if this _leg is short, we add liquidity to the amm, make sure to add that to our tracking 986 uint128 updatedLiquidity; 987 uint256 isLong = tokenId.isLong(leg); 988 LeftRightUnsigned currentLiquidity = s_accountLiquidity[positionKey]; //cache 989 { 990 // did we have liquidity already deployed in Uniswap for this chunk range from some past mint? 991 992 // s_accountLiquidity is a LeftRight. The right slot represents the liquidity currently sold (added) in the AMM owned by the user 993 // the left slot represents the amount of liquidity currently bought (removed) that has been removed from the AMM - the user owes it to a seller 994 // the reason why it is called "removedLiquidity" is because long options are created by removing -ie.short selling LP positions 995 uint128 startingLiquidity = currentLiquidity.rightSlot(); 996 uint128 removedLiquidity = currentLiquidity.leftSlot(); 997 uint128 chunkLiquidity = liquidityChunk.liquidity(); 998 999 if (isLong == 0) { 1000 // selling/short: so move from msg.sender *to* uniswap 1001 // we're minting more liquidity in uniswap: so add the incoming liquidity chunk to the existing liquidity chunk 1002 updatedLiquidity = startingLiquidity + chunkLiquidity; 1003 1004 /// @dev If the isLong flag is 0=short but the position was burnt, then this is closing a long position 1005 /// @dev so the amount of removed liquidity should decrease. 1006 if (isBurn) { 1007 removedLiquidity -= chunkLiquidity; 1008 } 1009 } else { 1010 // the _leg is long (buying: moving *from* uniswap to msg.sender) 1011 // so we seek to move the incoming liquidity chunk *out* of uniswap - but was there sufficient liquidity sitting in uniswap 1012 // in the first place? 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 } 1026 1027 /// @dev If the isLong flag is 1=long and the position is minted, then this is opening a long position 1028 /// @dev so the amount of removed liquidity should increase. 1029 if (!isBurn) { 1030 // we can't remove more liquidity than we add in the first place, so this can't overflow 1031 unchecked { 1032 removedLiquidity += chunkLiquidity; 1033 } 1034 } 1035 } 1036 1037 // update the starting liquidity for this position for next time around 1038 s_accountLiquidity[positionKey] = LeftRightUnsigned 1039 .wrap(0) 1040 .toLeftSlot(removedLiquidity) 1041 .toRightSlot(updatedLiquidity); 1042 } 1043 1044 // track how much liquidity we need to collect from uniswap 1045 // add the fees that accumulated in uniswap within the liquidityChunk: 1046 { 1047 /** if the position is NOT long (selling a put or a call), then _mintLiquidity to move liquidity 1048 from the msg.sender to the uniswap v3 pool: 1049 Selling(isLong=0): Mint chunk of liquidity in Uniswap (defined by upper tick, lower tick, and amount) 1050 āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā 1051 ā² āā¼ā liquidityChunk ā 1052 ā āāāā“āā“āāā āāāāā“āāā 1053 ā ā ā ā ā 1054 āāāā“āāāāāāāā“āāāŗ āāāāāāāā 1055 Uniswap v3 msg.sender 1056 1057 else: the position is long (buying a put or a call), then _burnLiquidity to remove liquidity from univ3 1058 Buying(isLong=1): Burn in Uniswap 1059 āāāāāāāāāāāāāāāāāāā 1060 ā² āā¼ā ā 1061 ā āāāā“āā“āāā āāāāā¼āāā 1062 ā ā ā ā ā 1063 āāāā“āāāāāāāā“āāāŗ āāāāāāāā 1064 Uniswap v3 msg.sender 1065 */ 1066 moved = isLong == 0 1067 ? _mintLiquidity(liquidityChunk, univ3pool) 1068 : _burnLiquidity(liquidityChunk, univ3pool); // from msg.sender to Uniswap 1069 // add the moved liquidity chunk to amount we need to collect from uniswap: 1070 1071 // Is this _leg ITM? 1072 // if tokenType is 1, and we transacted some token0: then this leg is ITM! 1073 if (tokenType == 1) { 1074 // extract amount moved out of UniswapV3 pool 1075 itmAmounts = itmAmounts.toRightSlot(moved.rightSlot()); 1076 } 1077 // if tokenType is 0, and we transacted some token1: then this leg is ITM 1078 if (tokenType == 0) { 1079 // Add this in-the-money amount transacted. 1080 itmAmounts = itmAmounts.toLeftSlot(moved.leftSlot()); 1081 } 1082 } 1083 1084 // if there was liquidity at that tick before the transaction, collect any accumulated fees 1085 if (currentLiquidity.rightSlot() > 0) { 1086 collectedSingleLeg = _collectAndWritePositionData( 1087 liquidityChunk, 1088 univ3pool, 1089 currentLiquidity, 1090 positionKey, 1091 moved, 1092 isLong 1093 ); 1094 } 1095 1096 // position has been touched, update s_accountFeesBase with the latest values from the pool.positions 1097 // round up the stored feesbase to minimize Īfeesbase when we next calculate it 1098 s_accountFeesBase[positionKey] = _getFeesBase( 1099 univ3pool, 1100 updatedLiquidity, 1101 liquidityChunk, 1102 true 1103 ); 1104: } /// @audit combine into a `struct`: s_accountPremiumGross,s_accountPremiumOwed 1110 function _updateStoredPremia( 1111 bytes32 positionKey, 1112 LeftRightUnsigned currentLiquidity, 1113 LeftRightUnsigned collectedAmounts 1114 ) private { 1115 ( 1116 LeftRightUnsigned deltaPremiumOwed, 1117 LeftRightUnsigned deltaPremiumGross 1118 ) = _getPremiaDeltas(currentLiquidity, collectedAmounts); 1119 1120 // add deltas to accumulators and freeze both accumulators (for a token) if one of them overflows 1121 // (i.e if only token0 (right slot) of the owed premium overflows, then stop accumulating both token0 owed premium and token0 gross premium for the chunk) 1122 // this prevents situations where the owed premium gets out of sync with the gross premium due to one of them overflowing 1123 (s_accountPremiumOwed[positionKey], s_accountPremiumGross[positionKey]) = LeftRightLibrary 1124 .addCapped( 1125 s_accountPremiumOwed[positionKey], 1126 deltaPremiumOwed, 1127 s_accountPremiumGross[positionKey], 1128 deltaPremiumGross 1129 ); 1130: } /// @audit combine into a `struct`: s_accountFeesBase,s_accountLiquidity,s_accountPremiumGross,s_accountPremiumOwed 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) { 1458 bytes32 positionKey = keccak256( 1459 abi.encodePacked(univ3pool, owner, tokenType, tickLower, tickUpper) 1460 ); 1461 1462 LeftRightUnsigned acctPremia; 1463 1464 // Compute the premium up to the current block (ie. after last touch until now). Do not proceed if atTick == type(int24).max = 8388608 1465 if (atTick < type(int24).max) { 1466 // unique key to identify the liquidity chunk in this uniswap pool 1467 LeftRightUnsigned accountLiquidities = s_accountLiquidity[positionKey]; 1468 uint128 netLiquidity = accountLiquidities.rightSlot(); 1469 if (netLiquidity != 0) { 1470 LeftRightUnsigned amountToCollect; 1471 { 1472 IUniswapV3Pool _univ3pool = IUniswapV3Pool(univ3pool); 1473 int24 _tickLower = tickLower; 1474 int24 _tickUpper = tickUpper; 1475 1476 // how much fees have been accumulated within the liquidity chunk since last time we updated this chunk? 1477 // Compute (currentFeesGrowth - oldFeesGrowth), the amount to collect 1478 // currentFeesGrowth (calculated from FeesCalc.calculateAMMSwapFeesLiquidityChunk) is (ammFeesCollectedPerLiquidity * liquidityChunk.liquidity()) 1479 // oldFeesGrowth is the last stored update of fee growth within the position range in the past (feeGrowthRange*liquidityChunk.liquidity()) (s_accountFeesBase[positionKey]) 1480 LeftRightSigned feesBase = FeesCalc.calculateAMMSwapFees( 1481 _univ3pool, 1482 atTick, 1483 _tickLower, 1484 _tickUpper, 1485 netLiquidity 1486 ); 1487 1488 // If the current feesBase is close or identical to the stored one, the amountToCollect can be negative. 1489 // This is because the stored feesBase is rounded up, and the current feesBase is rounded down. 1490 // When this is the case, we want to behave as if there are 0 fees, so we just rectify the values. 1491 // Guaranteed to be positive, so swap to unsigned type 1492 amountToCollect = LeftRightUnsigned.wrap( 1493 uint256( 1494 LeftRightSigned.unwrap(feesBase.subRect(s_accountFeesBase[positionKey])) 1495 ) 1496 ); 1497 } 1498 1499 (LeftRightUnsigned premiumOwed, LeftRightUnsigned premiumGross) = _getPremiaDeltas( 1500 accountLiquidities, 1501 amountToCollect 1502 ); 1503 1504 // add deltas to accumulators and freeze both accumulators (for a token) if one of them overflows 1505 // (i.e if only token0 (right slot) of the owed premium overflows, then stop accumulating both token0 owed premium and token0 gross premium for the chunk) 1506 // this prevents situations where the owed premium gets out of sync with the gross premium due to one of them overflowing 1507 (premiumOwed, premiumGross) = LeftRightLibrary.addCapped( 1508 s_accountPremiumOwed[positionKey], 1509 premiumOwed, 1510 s_accountPremiumGross[positionKey], 1511 premiumGross 1512 ); 1513 1514 acctPremia = isLong == 1 ? premiumOwed : premiumGross; 1515 } 1516 } else { 1517 // Extract the account liquidity for a given uniswap pool, owner, token type, and ticks 1518 acctPremia = isLong == 1 1519 ? s_accountPremiumOwed[positionKey] 1520 : s_accountPremiumGross[positionKey]; 1521 } 1522 return (acctPremia.rightSlot(), acctPremia.leftSlot()); 1523: }
File: contracts/tokens/ERC1155Minimal.sol /// @audit combine into a `struct`: balanceOf,isApprovedForAll 94 function safeTransferFrom( 95 address from, 96 address to, 97 uint256 id, 98 uint256 amount, 99 bytes calldata data 100 ) public virtual { 101 if (!(msg.sender == from || isApprovedForAll[from][msg.sender])) revert NotAuthorized(); 102 103 balanceOf[from][id] -= amount; 104 105 // balance will never overflow 106 unchecked { 107 balanceOf[to][id] += amount; 108 } 109 110 emit TransferSingle(msg.sender, from, to, id, amount); 111 112 if (to.code.length != 0) { 113 if ( 114 ERC1155Holder(to).onERC1155Received(msg.sender, from, id, amount, data) != 115 ERC1155Holder.onERC1155Received.selector 116 ) { 117 revert UnsafeRecipient(); 118 } 119 } 120: } /// @audit combine into a `struct`: balanceOf,isApprovedForAll 130 function safeBatchTransferFrom( 131 address from, 132 address to, 133 uint256[] calldata ids, 134 uint256[] calldata amounts, 135 bytes calldata data 136 ) public virtual { 137 if (!(msg.sender == from || isApprovedForAll[from][msg.sender])) revert NotAuthorized(); 138 139 // Storing these outside the loop saves ~15 gas per iteration. 140 uint256 id; 141 uint256 amount; 142 143 for (uint256 i = 0; i < ids.length; ) { 144 id = ids[i]; 145 amount = amounts[i]; 146 147 balanceOf[from][id] -= amount; 148 149 // balance will never overflow 150 unchecked { 151 balanceOf[to][id] += amount; 152 } 153 154 // An array can't have a total length 155 // larger than the max uint256 value. 156 unchecked { 157 ++i; 158 } 159 } 160 161 emit TransferBatch(msg.sender, from, to, ids, amounts); 162 163 if (to.code.length != 0) { 164 if ( 165 ERC1155Holder(to).onERC1155BatchReceived(msg.sender, from, ids, amounts, data) != 166 ERC1155Holder.onERC1155BatchReceived.selector 167 ) { 168 revert UnsafeRecipient(); 169 } 170 } 171: }
File: contracts/tokens/ERC20Minimal.sol /// @audit combine into a `struct`: allowance,balanceOf 81 function transferFrom(address from, address to, uint256 amount) public virtual returns (bool) { 82 uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals. 83 84 if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount; 85 86 balanceOf[from] -= amount; 87 88 // Cannot overflow because the sum of all user 89 // balances can't exceed the max uint256 value. 90 unchecked { 91 balanceOf[to] += amount; 92 } 93 94 emit Transfer(from, to, amount); 95 96 return true; 97: }
GitHub: 81
@author
tagsThere are 3 instances of this issue:
File: contracts/CollateralTracker.sol 36 contract CollateralTracker is ERC20Minimal, Multicall { 37: // Used for safecasting
GitHub: 36
File: contracts/tokens/interfaces/IDonorNFT.sol 6 interface IDonorNFT { 7 /// @notice Called to issue reward NFT to the deployer of a new `PanopticPool` through `PanopticFactory`. 8 /// @param deployer The address that deployed `newPoolContract` and donated funds for full-range liquidity 9 /// @param newPoolContract The address of the `PanopticPool` that was deployed 10 /// @param token0 Token0 of the Uniswap pool `newPoolContract` was deployed on 11 /// @param token1 Token1 of the Uniswap pool `newPoolContract` was deployed on 12: /// @param fee The fee tier, in hundredths of bips, of the Uniswap pool `newPoolContract` was deployed on
GitHub: 6
File: contracts/types/LiquidityChunk.sol 52 library LiquidityChunkLibrary { 53: /// @notice AND mask to strip the `tickLower` value from a packed LiquidityChunk
GitHub: 52
@dev
tags@dev
is used to explain extra details to developers
There are 12 instances of this issue:
<details> <summary>see instances</summary>File: contracts/CollateralTracker.sol 36 contract CollateralTracker is ERC20Minimal, Multicall { 37: // Used for safecasting
GitHub: 36
File: contracts/PanopticFactory.sol 26 contract PanopticFactory is Multicall { 27 /*////////////////////////////////////////////////////////////// 28 EVENTS 29 //////////////////////////////////////////////////////////////*/ 30 31 /// @notice Emitted when the ownership of the factory is transferred. 32 /// @param oldOwner The previous owner of the factory 33: /// @param newOwner The new owner of the factory
GitHub: 26
File: contracts/libraries/CallbackLib.sol 12 library CallbackLib { 13: /// @notice Defining characteristics of a Uni V3 pool
GitHub: 12
File: contracts/libraries/Constants.sol 7 library Constants { 8: /// @notice Fixed point multiplier: 2**96
GitHub: 7
File: contracts/libraries/Errors.sol 7 library Errors { 8 /// @notice Casting error 9: /// @dev e.g. uint128(uint256(a)) fails
GitHub: 7
File: contracts/libraries/InteractionHelper.sol 17 library InteractionHelper { 18 /// @notice Function that performs approvals on behalf of the PanopticPool for CollateralTracker and SemiFungiblePositionManager. 19 /// @param sfpm The SemiFungiblePositionManager being approved for both token0 and token1 20 /// @param ct0 The CollateralTracker (token0) being approved for token0 21 /// @param ct1 The CollateralTracker (token1) being approved for token1 22 /// @param token0 The token0 (in Uniswap) being approved for 23: /// @param token1 The token1 (in Uniswap) being approved for
GitHub: 17
File: contracts/libraries/Math.sol 13 library Math { 14: /// @notice This is equivalent to type(uint256).max ā used in assembly blocks as a replacement.
GitHub: 13
File: contracts/libraries/PanopticMath.sol 18 library PanopticMath { 19: // Used for safecasting
GitHub: 18
File: contracts/tokens/interfaces/IDonorNFT.sol 6 interface IDonorNFT { 7 /// @notice Called to issue reward NFT to the deployer of a new `PanopticPool` through `PanopticFactory`. 8 /// @param deployer The address that deployed `newPoolContract` and donated funds for full-range liquidity 9 /// @param newPoolContract The address of the `PanopticPool` that was deployed 10 /// @param token0 Token0 of the Uniswap pool `newPoolContract` was deployed on 11 /// @param token1 Token1 of the Uniswap pool `newPoolContract` was deployed on 12: /// @param fee The fee tier, in hundredths of bips, of the Uniswap pool `newPoolContract` was deployed on
GitHub: 6
File: contracts/types/LeftRight.sol 17 library LeftRightLibrary { 18: // Used for safecasting
GitHub: 17
File: contracts/types/LiquidityChunk.sol 52 library LiquidityChunkLibrary { 53: /// @notice AND mask to strip the `tickLower` value from a packed LiquidityChunk
GitHub: 52
File: contracts/types/TokenId.sol 60 library TokenIdLibrary { 61: /// @notice AND mask to extract all `isLong` bits for each leg from a TokenId
GitHub: 60
</details>@notice
tags@notice
is used to explain to end users what the contract does, and the compiler interprets ///
or /**
comments (but not //
or /*
) as this tag if one wasn't explicitly provided. Note that the NatSpec comment must be above the contract definition.
There is one instance of this issue:
File: contracts/tokens/interfaces/IDonorNFT.sol 6 interface IDonorNFT { 7 /// @notice Called to issue reward NFT to the deployer of a new `PanopticPool` through `PanopticFactory`. 8 /// @param deployer The address that deployed `newPoolContract` and donated funds for full-range liquidity 9 /// @param newPoolContract The address of the `PanopticPool` that was deployed 10 /// @param token0 Token0 of the Uniswap pool `newPoolContract` was deployed on 11 /// @param token1 Token1 of the Uniswap pool `newPoolContract` was deployed on 12: /// @param fee The fee tier, in hundredths of bips, of the Uniswap pool `newPoolContract` was deployed on
GitHub: 6
@title
tagsThere are 5 instances of this issue:
File: contracts/CollateralTracker.sol 36 contract CollateralTracker is ERC20Minimal, Multicall { 37: // Used for safecasting
GitHub: 36
File: contracts/libraries/InteractionHelper.sol 17 library InteractionHelper { 18 /// @notice Function that performs approvals on behalf of the PanopticPool for CollateralTracker and SemiFungiblePositionManager. 19 /// @param sfpm The SemiFungiblePositionManager being approved for both token0 and token1 20 /// @param ct0 The CollateralTracker (token0) being approved for token0 21 /// @param ct1 The CollateralTracker (token1) being approved for token1 22 /// @param token0 The token0 (in Uniswap) being approved for 23: /// @param token1 The token1 (in Uniswap) being approved for
GitHub: 17
File: contracts/libraries/SafeTransferLib.sol 11 library SafeTransferLib { 12 /*////////////////////////////////////////////////////////////// 13 ERC20 OPERATIONS 14 //////////////////////////////////////////////////////////////*/ 15 16 /// @notice Safely transfers ERC20 tokens from one address to another. 17 /// @param token The address of the ERC20 token 18 /// @param from The address to transfer tokens from 19 /// @param to The address to transfer tokens to 20: /// @param amount The amount of tokens to transfer
GitHub: 11
File: contracts/tokens/interfaces/IDonorNFT.sol 6 interface IDonorNFT { 7 /// @notice Called to issue reward NFT to the deployer of a new `PanopticPool` through `PanopticFactory`. 8 /// @param deployer The address that deployed `newPoolContract` and donated funds for full-range liquidity 9 /// @param newPoolContract The address of the `PanopticPool` that was deployed 10 /// @param token0 Token0 of the Uniswap pool `newPoolContract` was deployed on 11 /// @param token1 Token1 of the Uniswap pool `newPoolContract` was deployed on 12: /// @param fee The fee tier, in hundredths of bips, of the Uniswap pool `newPoolContract` was deployed on
GitHub: 6
File: contracts/types/LiquidityChunk.sol 52 library LiquidityChunkLibrary { 53: /// @notice AND mask to strip the `tickLower` value from a packed LiquidityChunk
GitHub: 52
e.g. @dev
or @notice
, and it must appear above the contract definition braces in order to be identified by the compiler as NatSpec
There is one instance of this issue:
File: contracts/tokens/interfaces/IDonorNFT.sol 6 interface IDonorNFT { 7 /// @notice Called to issue reward NFT to the deployer of a new `PanopticPool` through `PanopticFactory`. 8 /// @param deployer The address that deployed `newPoolContract` and donated funds for full-range liquidity 9 /// @param newPoolContract The address of the `PanopticPool` that was deployed 10 /// @param token0 Token0 of the Uniswap pool `newPoolContract` was deployed on 11 /// @param token1 Token1 of the Uniswap pool `newPoolContract` was deployed on 12: /// @param fee The fee tier, in hundredths of bips, of the Uniswap pool `newPoolContract` was deployed on
GitHub: 6
@dev
tags@dev
is used to explain extra details to developers
There are 33 instances of this issue:
File: contracts/libraries/Errors.sol 13: error CollateralTokenAlreadyInitialized(); 16: error DepositTooLarge(); 20: error EffectiveLiquidityAboveThreshold(); 23: error ExceedsMaximumRedemption(); 26: error ExerciseeNotSolvent(); 29: error InputListFail(); 32: error InvalidSalt(); 35: error InvalidTick(); 38: error InvalidNotionalValue(); 42: error InvalidTokenIdParameter(uint256 parameterType); 45: error InvalidUniswapCallback(); 48: error LeftRightInputError(); 51: error NoLegsExercisable(); 54: error NotEnoughCollateral(); 57: error PositionTooLarge(); 60: error NotALongLeg(); 63: error NotEnoughLiquidity(); 66: error NotMarginCalled(); 73: error NotPanopticPool(); 76: error OptionsBalanceZero(); 79: error PoolAlreadyInitialized(); 82: error PositionAlreadyMinted(); 85: error PositionCountNotZero(); 88: error PriceBoundFail(); 91: error ReentrantCall(); 95: error StaleTWAP(); 98: error TooManyPositionsOpen(); 101: error TransferFailed(); 105: error TicksNotInitializable(); 108: error UnderOverFlow(); 111: error UniswapPoolNotInitialized();
GitHub: 13, 16, 20, 23, 26, 29, 32, 35, 38, 42, 45, 48, 51, 54, 57, 60, 63, 66, 73, 76, 79, 82, 85, 88, 91, 95, 98, 101, 105, 108, 111
File: contracts/tokens/ERC1155Minimal.sol 55: error NotAuthorized(); 58: error UnsafeRecipient();
@param
tag is missingThere are 62 instances of this issue:
File: contracts/CollateralTracker.sol /// @audit Missing '@param sender' /// @audit Missing '@param owner' /// @audit Missing '@param assets' /// @audit Missing '@param shares' 44 /// @notice Emitted when assets are deposited into the Collateral Tracker. 45 /// @param sender The address of the caller (and depositor) 46 /// @param owner The address of the recipient of the newly minted shares 47 /// @param assets The amount of assets deposited by 'sender' in exchange for 'shares' 48 /// @param shares The amount of shares minted to 'owner' 49: event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares); /// @audit Missing '@param sender' 53 /// @param receiver The address of the recipient of the withdrawn assets 54 /// @param owner The address of the owner of the shares being burned 55 /// @param assets The amount of assets withdrawn to 'receiver' 56 /// @param shares The amount of shares burned by 'owner' in exchange for 'assets' 57 event Withdraw( 58: address indexed sender, /// @audit Missing '@param receiver' 54 /// @param owner The address of the owner of the shares being burned 55 /// @param assets The amount of assets withdrawn to 'receiver' 56 /// @param shares The amount of shares burned by 'owner' in exchange for 'assets' 57 event Withdraw( 58 address indexed sender, 59: address indexed receiver, /// @audit Missing '@param owner' 55 /// @param assets The amount of assets withdrawn to 'receiver' 56 /// @param shares The amount of shares burned by 'owner' in exchange for 'assets' 57 event Withdraw( 58 address indexed sender, 59 address indexed receiver, 60: address indexed owner, /// @audit Missing '@param assets' 56 /// @param shares The amount of shares burned by 'owner' in exchange for 'assets' 57 event Withdraw( 58 address indexed sender, 59 address indexed receiver, 60 address indexed owner, 61: uint256 assets, /// @audit Missing '@param shares' 57 event Withdraw( 58 address indexed sender, 59 address indexed receiver, 60 address indexed owner, 61 uint256 assets, 62: uint256 shares
GitHub: 44, 44, 44, 44, 53, 54, 55, 56, 57
File: contracts/PanopticFactory.sol /// @audit Missing '@param oldOwner' /// @audit Missing '@param newOwner' 29 //////////////////////////////////////////////////////////////*/ 30 31 /// @notice Emitted when the ownership of the factory is transferred. 32 /// @param oldOwner The previous owner of the factory 33 /// @param newOwner The new owner of the factory 34: event OwnershipTransferred(address indexed oldOwner, address indexed newOwner); /// @audit Missing '@param poolAddress' 39 /// @param collateralTracker0 Address of the collateral tracker contract for token0 40 /// @param collateralTracker1 Address of the collateral tracker contract for token1 41 /// @param amount0 The amount of token0 deployed at full range 42 /// @param amount1 The amount of token1 deployed at full range 43 event PoolDeployed( 44: PanopticPool indexed poolAddress, /// @audit Missing '@param uniswapPool' 40 /// @param collateralTracker1 Address of the collateral tracker contract for token1 41 /// @param amount0 The amount of token0 deployed at full range 42 /// @param amount1 The amount of token1 deployed at full range 43 event PoolDeployed( 44 PanopticPool indexed poolAddress, 45: IUniswapV3Pool indexed uniswapPool, /// @audit Missing '@param collateralTracker0' 41 /// @param amount0 The amount of token0 deployed at full range 42 /// @param amount1 The amount of token1 deployed at full range 43 event PoolDeployed( 44 PanopticPool indexed poolAddress, 45 IUniswapV3Pool indexed uniswapPool, 46: CollateralTracker collateralTracker0, /// @audit Missing '@param collateralTracker1' 42 /// @param amount1 The amount of token1 deployed at full range 43 event PoolDeployed( 44 PanopticPool indexed poolAddress, 45 IUniswapV3Pool indexed uniswapPool, 46 CollateralTracker collateralTracker0, 47: CollateralTracker collateralTracker1, /// @audit Missing '@param amount0' 43 event PoolDeployed( 44 PanopticPool indexed poolAddress, 45 IUniswapV3Pool indexed uniswapPool, 46 CollateralTracker collateralTracker0, 47 CollateralTracker collateralTracker1, 48: uint256 amount0, /// @audit Missing '@param amount1' 44 PanopticPool indexed poolAddress, 45 IUniswapV3Pool indexed uniswapPool, 46 CollateralTracker collateralTracker0, 47 CollateralTracker collateralTracker1, 48 uint256 amount0, 49: uint256 amount1
GitHub: 29, 29, 39, 40, 41, 42, 43, 44
File: contracts/PanopticPool.sol /// @audit Missing '@param liquidator' 34 /// @param liquidator Address of the caller whom is liquidating the distressed account. 35 /// @param liquidatee Address of the distressed/liquidatable account. 36 /// @param bonusAmounts LeftRight encoding for the the bonus paid for token 0 (right slot) and 1 (left slot) from the Panoptic Pool to the liquidator. 37 /// The token0 bonus is in the right slot, and token1 bonus is in the left slot. 38 event AccountLiquidated( 39: address indexed liquidator, /// @audit Missing '@param liquidatee' 35 /// @param liquidatee Address of the distressed/liquidatable account. 36 /// @param bonusAmounts LeftRight encoding for the the bonus paid for token 0 (right slot) and 1 (left slot) from the Panoptic Pool to the liquidator. 37 /// The token0 bonus is in the right slot, and token1 bonus is in the left slot. 38 event AccountLiquidated( 39 address indexed liquidator, 40: address indexed liquidatee, /// @audit Missing '@param bonusAmounts' 36 /// @param bonusAmounts LeftRight encoding for the the bonus paid for token 0 (right slot) and 1 (left slot) from the Panoptic Pool to the liquidator. 37 /// The token0 bonus is in the right slot, and token1 bonus is in the left slot. 38 event AccountLiquidated( 39 address indexed liquidator, 40 address indexed liquidatee, 41: LeftRightSigned bonusAmounts /// @audit Missing '@param exercisor' 47 /// @param user Address of the owner of the liquidated position 48 /// @param tokenId TokenId of the liquidated position. 49 /// @param exerciseFee LeftRight encoding for the cost paid by the exercisor to force the exercise of the token. 50 /// The token0 fee is in the right slot, and token1 fee is in the left slot. 51 event ForcedExercised( 52: address indexed exercisor, /// @audit Missing '@param user' 48 /// @param tokenId TokenId of the liquidated position. 49 /// @param exerciseFee LeftRight encoding for the cost paid by the exercisor to force the exercise of the token. 50 /// The token0 fee is in the right slot, and token1 fee is in the left slot. 51 event ForcedExercised( 52 address indexed exercisor, 53: address indexed user, /// @audit Missing '@param tokenId' 49 /// @param exerciseFee LeftRight encoding for the cost paid by the exercisor to force the exercise of the token. 50 /// The token0 fee is in the right slot, and token1 fee is in the left slot. 51 event ForcedExercised( 52 address indexed exercisor, 53 address indexed user, 54: TokenId indexed tokenId, /// @audit Missing '@param exerciseFee' 50 /// The token0 fee is in the right slot, and token1 fee is in the left slot. 51 event ForcedExercised( 52 address indexed exercisor, 53 address indexed user, 54 TokenId indexed tokenId, 55: LeftRightSigned exerciseFee /// @audit Missing '@param user' 58 /// @notice Emitted when premium is settled independent of a mint/burn (e.g. during `settleLongPremium`) 59 /// @param user Address of the owner of the settled position. 60 /// @param tokenId TokenId of the settled position. 61 /// @param settledAmounts LeftRight encoding for the amount of premium settled for token0 (right slot) and token1 (left slot). 62 event PremiumSettled( 63: address indexed user, /// @audit Missing '@param tokenId' 59 /// @param user Address of the owner of the settled position. 60 /// @param tokenId TokenId of the settled position. 61 /// @param settledAmounts LeftRight encoding for the amount of premium settled for token0 (right slot) and token1 (left slot). 62 event PremiumSettled( 63 address indexed user, 64: TokenId indexed tokenId, /// @audit Missing '@param settledAmounts' 60 /// @param tokenId TokenId of the settled position. 61 /// @param settledAmounts LeftRight encoding for the amount of premium settled for token0 (right slot) and token1 (left slot). 62 event PremiumSettled( 63 address indexed user, 64 TokenId indexed tokenId, 65: LeftRightSigned settledAmounts /// @audit Missing '@param recipient' 71 /// @param positionSize The number of contracts burnt, expressed in terms of the asset. 72 /// @param tokenId TokenId of the burnt option. 73 /// @param premia LeftRight packing for the amount of premia collected for token0 and token1. 74 /// The token0 premia is in the right slot, and token1 premia is in the left slot. 75 event OptionBurnt( 76: address indexed recipient, /// @audit Missing '@param positionSize' 72 /// @param tokenId TokenId of the burnt option. 73 /// @param premia LeftRight packing for the amount of premia collected for token0 and token1. 74 /// The token0 premia is in the right slot, and token1 premia is in the left slot. 75 event OptionBurnt( 76 address indexed recipient, 77: uint128 positionSize, /// @audit Missing '@param tokenId' 73 /// @param premia LeftRight packing for the amount of premia collected for token0 and token1. 74 /// The token0 premia is in the right slot, and token1 premia is in the left slot. 75 event OptionBurnt( 76 address indexed recipient, 77 uint128 positionSize, 78: TokenId indexed tokenId, /// @audit Missing '@param premia' 74 /// The token0 premia is in the right slot, and token1 premia is in the left slot. 75 event OptionBurnt( 76 address indexed recipient, 77 uint128 positionSize, 78 TokenId indexed tokenId, 79: LeftRightSigned premia /// @audit Missing '@param recipient' 86 /// @param tokenId TokenId of the created option. 87 /// @param poolUtilizations Packing of the pool utilization (how much funds are in the Panoptic pool versus the AMM pool at the time of minting), 88 /// right 64bits for token0 and left 64bits for token1, defined as (inAMM * 10_000) / totalAssets(). 89 /// Where totalAssets is the total tracked assets in the AMM and PanopticPool minus fees and donations to the Panoptic pool. 90 event OptionMinted( 91: address indexed recipient, /// @audit Missing '@param positionSize' 87 /// @param poolUtilizations Packing of the pool utilization (how much funds are in the Panoptic pool versus the AMM pool at the time of minting), 88 /// right 64bits for token0 and left 64bits for token1, defined as (inAMM * 10_000) / totalAssets(). 89 /// Where totalAssets is the total tracked assets in the AMM and PanopticPool minus fees and donations to the Panoptic pool. 90 event OptionMinted( 91 address indexed recipient, 92: uint128 positionSize, /// @audit Missing '@param tokenId' 88 /// right 64bits for token0 and left 64bits for token1, defined as (inAMM * 10_000) / totalAssets(). 89 /// Where totalAssets is the total tracked assets in the AMM and PanopticPool minus fees and donations to the Panoptic pool. 90 event OptionMinted( 91 address indexed recipient, 92 uint128 positionSize, 93: TokenId indexed tokenId, /// @audit Missing '@param poolUtilizations' 89 /// Where totalAssets is the total tracked assets in the AMM and PanopticPool minus fees and donations to the Panoptic pool. 90 event OptionMinted( 91 address indexed recipient, 92 uint128 positionSize, 93 TokenId indexed tokenId, 94: uint128 poolUtilizations
GitHub: 34, 35, 36, 47, 48, 49, 50, 58, 59, 60, 71, 72, 73, 74, 86, 87, 88, 89
File: contracts/SemiFungiblePositionManager.sol /// @audit Missing '@param uniswapPool' /// @audit Missing '@param poolId' 75 //////////////////////////////////////////////////////////////*/ 76 77 /// @notice Emitted when a UniswapV3Pool is initialized. 78 /// @param uniswapPool Address of the underlying Uniswap v3 pool 79 /// @param poolId The SFPM's pool identifier for the pool, including the 16-bit tick spacing and 48-bit pool pattern 80: event PoolInitialized(address indexed uniswapPool, uint64 poolId); /// @audit Missing '@param recipient' 83 /// @dev Recipient is used to track whether it was burned directly by the user or through an option contract 84 /// @param recipient The address of the user who burned the position 85 /// @param tokenId The tokenId of the burned position 86 /// @param positionSize The number of contracts burnt, expressed in terms of the asset 87 event TokenizedPositionBurnt( 88: address indexed recipient, /// @audit Missing '@param tokenId' 84 /// @param recipient The address of the user who burned the position 85 /// @param tokenId The tokenId of the burned position 86 /// @param positionSize The number of contracts burnt, expressed in terms of the asset 87 event TokenizedPositionBurnt( 88 address indexed recipient, 89: TokenId indexed tokenId, /// @audit Missing '@param positionSize' 85 /// @param tokenId The tokenId of the burned position 86 /// @param positionSize The number of contracts burnt, expressed in terms of the asset 87 event TokenizedPositionBurnt( 88 address indexed recipient, 89 TokenId indexed tokenId, 90: uint128 positionSize /// @audit Missing '@param caller' 94 /// @dev Recipient is used to track whether it was minted directly by the user or through an option contract 95 /// @param caller the caller who created the position. In 99% of cases `caller` == `recipient`. 96 /// @param tokenId The tokenId of the minted position 97 /// @param positionSize The number of contracts minted, expressed in terms of the asset 98 event TokenizedPositionMinted( 99: address indexed caller, /// @audit Missing '@param tokenId' 95 /// @param caller the caller who created the position. In 99% of cases `caller` == `recipient`. 96 /// @param tokenId The tokenId of the minted position 97 /// @param positionSize The number of contracts minted, expressed in terms of the asset 98 event TokenizedPositionMinted( 99 address indexed caller, 100: TokenId indexed tokenId, /// @audit Missing '@param positionSize' 96 /// @param tokenId The tokenId of the minted position 97 /// @param positionSize The number of contracts minted, expressed in terms of the asset 98 event TokenizedPositionMinted( 99 address indexed caller, 100 TokenId indexed tokenId, 101: uint128 positionSize
GitHub: 75, 75, 83, 84, 85, 94, 95, 96
File: contracts/tokens/ERC1155Minimal.sol /// @audit Missing '@param operator' 18 /// @param from The user who sent the tokens 19 /// @param to The user who received the tokens 20 /// @param id The ERC1155 token id 21 /// @param amount The amount of tokens transferred 22 event TransferSingle( 23: address indexed operator, /// @audit Missing '@param from' 19 /// @param to The user who received the tokens 20 /// @param id The ERC1155 token id 21 /// @param amount The amount of tokens transferred 22 event TransferSingle( 23 address indexed operator, 24: address indexed from, /// @audit Missing '@param to' 20 /// @param id The ERC1155 token id 21 /// @param amount The amount of tokens transferred 22 event TransferSingle( 23 address indexed operator, 24 address indexed from, 25: address indexed to, /// @audit Missing '@param id' 21 /// @param amount The amount of tokens transferred 22 event TransferSingle( 23 address indexed operator, 24 address indexed from, 25 address indexed to, 26: uint256 id, /// @audit Missing '@param amount' 22 event TransferSingle( 23 address indexed operator, 24 address indexed from, 25 address indexed to, 26 uint256 id, 27: uint256 amount /// @audit Missing '@param operator' 32 /// @param from The user who sent the tokens 33 /// @param to The user who received the tokens 34 /// @param ids The ERC1155 token ids 35 /// @param amounts The amounts of tokens transferred 36 event TransferBatch( 37: address indexed operator, /// @audit Missing '@param from' 33 /// @param to The user who received the tokens 34 /// @param ids The ERC1155 token ids 35 /// @param amounts The amounts of tokens transferred 36 event TransferBatch( 37 address indexed operator, 38: address indexed from, /// @audit Missing '@param to' 34 /// @param ids The ERC1155 token ids 35 /// @param amounts The amounts of tokens transferred 36 event TransferBatch( 37 address indexed operator, 38 address indexed from, 39: address indexed to, /// @audit Missing '@param ids' 35 /// @param amounts The amounts of tokens transferred 36 event TransferBatch( 37 address indexed operator, 38 address indexed from, 39 address indexed to, 40: uint256[] ids, /// @audit Missing '@param amounts' 36 event TransferBatch( 37 address indexed operator, 38 address indexed from, 39 address indexed to, 40 uint256[] ids, 41: uint256[] amounts /// @audit Missing '@param owner' /// @audit Missing '@param operator' /// @audit Missing '@param approved' 43 44 /// @notice Emitted when the approval status of an operator to transfer all tokens on behalf of a user is modified. 45 /// @param owner The user who approved or disapproved `operator` to transfer their tokens 46 /// @param operator The user who was approved or disapproved to transfer all tokens on behalf of `owner` 47 /// @param approved Whether `operator` is approved or disapproved to transfer all tokens on behalf of `owner` 48: event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
GitHub: 18, 19, 20, 21, 22, 32, 33, 34, 35, 36, 43, 43, 43
File: contracts/tokens/ERC20Minimal.sol /// @audit Missing '@param from' /// @audit Missing '@param to' /// @audit Missing '@param amount' 13 14 /// @notice Emitted when tokens are transferred 15 /// @param from The sender of the tokens 16 /// @param to The recipient of the tokens 17 /// @param amount The amount of tokens transferred 18: event Transfer(address indexed from, address indexed to, uint256 amount); /// @audit Missing '@param owner' /// @audit Missing '@param spender' /// @audit Missing '@param amount' 19 20 /// @notice Emitted when a user approves another user to spend tokens on their behalf 21 /// @param owner The user who approved the spender 22 /// @param spender The user who was approved to spend tokens 23 /// @param amount The amount of tokens approved to spend 24: event Approval(address indexed owner, address indexed spender, uint256 amount);
GitHub: 13, 13, 13, 19, 19, 19
@dev
tags@dev
is used to explain extra details to developers
There are 11 instances of this issue:
File: contracts/CollateralTracker.sol 49: event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares); 57 event Withdraw( 58 address indexed sender, 59 address indexed receiver, 60 address indexed owner, 61 uint256 assets, 62 uint256 shares 63: );
File: contracts/PanopticFactory.sol 34: event OwnershipTransferred(address indexed oldOwner, address indexed newOwner); 43 event PoolDeployed( 44 PanopticPool indexed poolAddress, 45 IUniswapV3Pool indexed uniswapPool, 46 CollateralTracker collateralTracker0, 47 CollateralTracker collateralTracker1, 48 uint256 amount0, 49 uint256 amount1 50: );
File: contracts/PanopticPool.sol 62 event PremiumSettled( 63 address indexed user, 64 TokenId indexed tokenId, 65 LeftRightSigned settledAmounts 66: );
GitHub: 62
File: contracts/SemiFungiblePositionManager.sol 80: event PoolInitialized(address indexed uniswapPool, uint64 poolId);
GitHub: 80
File: contracts/tokens/ERC1155Minimal.sol 22 event TransferSingle( 23 address indexed operator, 24 address indexed from, 25 address indexed to, 26 uint256 id, 27 uint256 amount 28: ); 36 event TransferBatch( 37 address indexed operator, 38 address indexed from, 39 address indexed to, 40 uint256[] ids, 41 uint256[] amounts 42: ); 48: event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
File: contracts/tokens/ERC20Minimal.sol 18: event Transfer(address indexed from, address indexed to, uint256 amount); 24: event Approval(address indexed owner, address indexed spender, uint256 amount);
@param
tag is missingThere are 2 instances of this issue:
File: contracts/PanopticPool.sol /// @audit Missing '@param atTick' 429 function _calculateAccumulatedPremia( 430 address user, 431 TokenId[] calldata positionIdList, 432 bool computeAllPremia, 433 bool includePendingPremium, 434: int24 atTick
GitHub: 429
File: contracts/libraries/Math.sol /// @audit Missing '@param toDowncast' 297 if ((downcastedInt = uint128(toDowncast)) != toDowncast) revert Errors.CastingError(); 298 } 299 300 /// @notice Downcast uint256 to uint128, but cap at type(uint128).max on overflow. 301 /// @return downcastedInt The downcasted uint (uint128 now) 302: function toUint128Capped(uint256 toDowncast) internal pure returns (uint128 downcastedInt) {
GitHub: 297
@return
tag is missingThere are 10 instances of this issue:
File: contracts/PanopticPool.sol /// @audit Missing '@return netPaid' /// @audit Missing '@return premiasByLeg' 788 /// @notice Helper to burn option during a liquidation from an account _owner. 789 /// @param owner the owner of the option position to be liquidated. 790 /// @param tickLimitLow Price slippage limit when burning an ITM option 791 /// @param tickLimitHigh Price slippage limit when burning an ITM option 792 /// @param commitLongSettled Whether to commit the long premium that will be settled to storage 793 /// @param positionIdList the option position to liquidate. 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 Missing '@return realizedPremia' /// @audit Missing '@return paidAmounts' /// @audit Missing '@return premiaByLeg' 948 /// @notice Burns and handles the exercise of options. 949 /// @param commitLongSettled Whether to commit the long premium that will be settled to storage 950 /// @param tickLimitLow The lower slippage limit on the tick. 951 /// @param tickLimitHigh The upper slippage limit on the tick. 952 /// @param tokenId The option position to burn. 953 /// @param positionSize The size of the option position, expressed in terms of the asset. 954 /// @param owner The owner of the option position. 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 Missing '@return ' 1284 /// @notice check whether an account is solvent at a given `atTick` with a collateral requirement of `buffer`/10_000 multiplied by the requirement of `positionIdList`. 1285 /// @param account The account to check solvency for. 1286 /// @param positionIdList The list of positions to check solvency for. 1287 /// @param currentTick The current tick of the Uniswap pool (needed for fee calculations). 1288 /// @param atTick The tick to check solvency at. 1289 /// @param buffer The buffer to apply to the collateral requirement. 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 Missing '@return premiumAccumulatorsByLeg' /// @audit Missing '@return premiaByLeg' 1496 /// @notice Compute the premia collected for a single option position 'tokenId'. 1497 /// @param tokenId The option position. 1498 /// @param positionSize The number of contracts (size) of the option position. 1499 /// @param owner The holder of the tokenId option. 1500 /// @param computeAllPremia Whether to compute accumulated premia for all legs held by the user (true), or just owed premia for long legs (false). 1501 /// @param atTick The tick at which the premia is calculated -> use (atTick < type(int24).max) to compute it 1502 /// up to current block. atTick = type(int24).max will only consider fees as of the last on-chain transaction. 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 Missing '@return totalLiquidity' 1798 /// @notice Query the total amount of liquidity sold in the corresponding chunk for a position leg 1799 /// @dev totalLiquidity (total sold) = removedLiquidity + netLiquidity (in AMM) 1800 /// @param tokenId The option position 1801 /// @param leg The leg of the option position to get `totalLiquidity for 1802 function _getTotalLiquidity( 1803 TokenId tokenId, 1804 uint256 leg 1805: ) internal view returns (uint256 totalLiquidity) {
GitHub: 788, 788, 948, 948, 948, 1284, 1496, 1496, 1798
File: contracts/SemiFungiblePositionManager.sol /// @audit Missing '@return feesBase' 1132 /// @notice Compute the feesGrowth * liquidity / 2**128 by reading feeGrowthInside0LastX128 and feeGrowthInside1LastX128 from univ3pool.positions. 1133 /// @param univ3pool the Uniswap pool. 1134 /// @param liquidity the total amount of liquidity in the AMM for the specific position 1135 /// @param liquidityChunk has lower tick, upper tick, and liquidity amount to mint 1136 /// @param roundUp if true, round up the feesBase, otherwise round down 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 1138 function _getFeesBase( 1139 IUniswapV3Pool univ3pool, 1140 uint128 liquidity, 1141 LiquidityChunk liquidityChunk, 1142 bool roundUp 1143: ) private view returns (LeftRightSigned feesBase) {
GitHub: 1132
@dev
tags@dev
is used to explain to developers what the potential integration issues/foot-guns are
There are 147 instances of this issue:
<details> <summary>see 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: ) { 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) { 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) { 444: function maxMint(address) external view returns (uint256 maxShares) { 453: function previewMint(uint256 shares) public view returns (uint256 assets) { 518: function previewWithdraw(uint256 assets) public view 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) { 911 function revoke( 912 address delegator, 913 address delegatee, 914 uint256 assets 915: ) external onlyPanopticPool { 995 function takeCommissionAddData( 996 address optionOwner, 997 int128 longAmount, 998 int128 shortAmount, 999 int128 swappedAmount 1000: ) external onlyPanopticPool returns (int256 utilization) { 1096 function _getExchangedAmount( 1097 int128 longAmount, 1098 int128 shortAmount, 1099 int128 swappedAmount 1100: ) internal view returns (int256 exchangedAmount) { 1245 function _getRequiredCollateralAtTickSinglePosition( 1246 TokenId tokenId, 1247 uint128 positionSize, 1248 int24 atTick, 1249 uint128 poolUtilization 1250: ) internal view returns (uint256 tokenRequired) {
GitHub: 178, 289, 303, 310, 361, 379, 386, 392, 399, 444, 453, 518, 572, 581, 591, 911, 995, 1096, 1245
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: ) { 134: function initialize(address _owner) public { 147: function transferOwnership(address newOwner) external { 159: function owner() external view returns (address) { 335 function _mintFullRange( 336 IUniswapV3Pool v3Pool, 337 address token0, 338 address token1, 339 uint24 fee 340: ) internal returns (uint256, uint256) { 420: function getPanopticPool(IUniswapV3Pool univ3pool) external view returns (PanopticPool) {
GitHub: 115, 134, 147, 159, 335, 420
File: contracts/PanopticPool.sol 280: constructor(SemiFungiblePositionManager _sfpm) { 352 function optionPositionBalance( 353 address user, 354 TokenId tokenId 355: ) external view returns (uint128 balance, uint64 poolUtilization0, uint64 poolUtilization1) { 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) { 499 function _getSlippageLimits( 500 int24 tickLimitLow, 501 int24 tickLimitHigh 502: ) internal pure returns (int24, int24) { 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 { 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) { 859: function _updatePositionDataBurn(address owner, TokenId tokenId) internal { 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) { 1339 function _getSolvencyBalances( 1340 LeftRightUnsigned tokenData0, 1341 LeftRightUnsigned tokenData1, 1342 uint160 sqrtPriceX96 1343: ) internal pure returns (uint256 balanceCross, uint256 thresholdCross) { 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) { 1450: function getUniV3TWAP() internal view returns (int24 twapTick) { 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: {
GitHub: 280, 352, 429, 499, 522, 547, 614, 794, 826, 859, 955, 1290, 1339, 1425, 1431, 1437, 1444, 1450, 1465, 1503
File: contracts/SemiFungiblePositionManager.sol 330: function endReentrancyLock(uint64 poolId) internal { 341: constructor(IUniswapV3Factory _factory) { 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: { 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) { 756 function swapInAMM( 757 IUniswapV3Pool univ3pool, 758 LeftRightSigned itmAmounts 759: ) internal returns (LeftRightSigned totalSwapped) { 1110 function _updateStoredPremia( 1111 bytes32 positionKey, 1112 LeftRightUnsigned currentLiquidity, 1113 LeftRightUnsigned collectedAmounts 1114: ) private { 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) {
GitHub: 330, 341, 504, 680, 756, 1110, 1255
File: contracts/libraries/CallbackLib.sol 30 function validateCallback( 31 address sender, 32 IUniswapV3Factory factory, 33 PoolFeatures memory features 34: ) internal view {
GitHub: 30
File: contracts/libraries/FeesCalc.sol 46 function getPortfolioValue( 47 int24 atTick, 48 mapping(TokenId tokenId => LeftRightUnsigned balance) storage userBalance, 49 TokenId[] calldata positionIdList 50: ) external view returns (int256 value0, int256 value1) {
GitHub: 46
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 { 91 function computeSymbol( 92 address token, 93 string memory prefix 94: ) external view returns (string memory) { 107: function computeDecimals(address token) external view returns (uint8) {
File: contracts/libraries/Math.sol 25: function min24(int24 a, int24 b) internal pure returns (int24) { 33: function max24(int24 a, int24 b) internal pure returns (int24) { 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) { 91: function mostSignificantNibble(uint160 x) internal pure returns (uint256 r) { 207: function getAmount1ForLiquidity(LiquidityChunk liquidityChunk) internal pure returns (uint256) { 221 function getAmountsForLiquidity( 222 int24 currentTick, 223 LiquidityChunk liquidityChunk 224: ) internal pure returns (uint256 amount0, uint256 amount1) { 296: function toUint128(uint256 toDowncast) internal pure returns (uint128 downcastedInt) { 302: function toUint128Capped(uint256 toDowncast) internal pure returns (uint128 downcastedInt) { 311: function toInt128(uint128 toCast) internal pure returns (int128 downcastedInt) { 318: function toInt128(int256 toCast) internal pure returns (int128 downcastedInt) { 325: function toInt256(uint256 toCast) internal pure returns (int256) { 440 function mulDivRoundingUp( 441 uint256 a, 442 uint256 b, 443 uint256 denominator 444: ) internal pure returns (uint256 result) { 458: function mulDiv64(uint256 a, uint256 b) internal pure returns (uint256) { 521: function mulDiv96(uint256 a, uint256 b) internal pure returns (uint256) { 584: function mulDiv96RoundingUp(uint256 a, uint256 b) internal pure returns (uint256 result) { 598: function mulDiv128(uint256 a, uint256 b) internal pure returns (uint256) { 661: function mulDiv128RoundingUp(uint256 a, uint256 b) internal pure returns (uint256 result) { 675: function mulDiv192(uint256 a, uint256 b) internal pure returns (uint256) { 738: function unsafeDivRoundingUp(uint256 a, uint256 b) internal pure returns (uint256 result) { 753: function quickSort(int256[] memory arr, int256 left, int256 right) internal pure { 776: function sort(int256[] memory data) internal pure returns (int256[] memory) {
GitHub: 25, 33, 41, 49, 57, 65, 91, 207, 221, 296, 302, 311, 318, 325, 440, 458, 521, 584, 598, 661, 675, 738, 753, 776
File: contracts/libraries/PanopticMath.sol 59: function incrementPoolPattern(uint64 poolId) internal pure returns (uint64) { 75: function numberOfLeadingHexZeros(address addr) external pure returns (uint256) { 289 function getLiquidityChunk( 290 TokenId tokenId, 291 uint256 legIndex, 292 uint128 positionSize 293: ) internal pure returns (LiquidityChunk) { 338 function getTicks( 339 int24 strike, 340 int24 width, 341 int24 tickSpacing 342: ) internal pure returns (int24 tickLower, int24 tickUpper) { 390 function computeExercisedAmounts( 391 TokenId tokenId, 392 uint128 positionSize 393: ) internal pure returns (LeftRightSigned longAmounts, LeftRightSigned shortAmounts) { 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) { 574 function getAmountsMoved( 575 TokenId tokenId, 576 uint128 positionSize, 577 uint256 legIndex 578: ) internal pure returns (LeftRightUnsigned) { 607 function _calculateIOAmounts( 608 TokenId tokenId, 609 uint128 positionSize, 610 uint256 legIndex 611: ) internal pure returns (LeftRightSigned longs, LeftRightSigned shorts) { 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) { 917 function getRefundAmounts( 918 address refunder, 919 LeftRightSigned refundValues, 920 int24 atTick, 921 CollateralTracker collateral0, 922 CollateralTracker collateral1 923: ) external view returns (LeftRightSigned) {
GitHub: 59, 75, 289, 338, 390, 419, 445, 574, 607, 651, 917
File: contracts/libraries/SafeTransferLib.sol 21: function safeTransferFrom(address token, address from, address to, uint256 amount) internal { 52: function safeTransfer(address token, address to, uint256 amount) internal {
File: contracts/multicall/Multicall.sol 12: function multicall(bytes[] calldata data) public payable returns (bytes[] memory results) {
GitHub: 12
File: contracts/tokens/ERC1155Minimal.sol 81: function setApprovalForAll(address operator, bool approved) public { 200: function supportsInterface(bytes4 interfaceId) public pure returns (bool) { 214: function _mint(address to, uint256 id, uint256 amount) internal { 236: function _burn(address from, uint256 id, uint256 amount) internal {
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) { 103: function _transferFrom(address from, address to, uint256 amount) internal { 122: function _mint(address to, uint256 amount) internal { 136: function _burn(address from, uint256 amount) internal {
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;
GitHub: 13
File: contracts/tokens/interfaces/IERC20Partial.sol 27: function transfer(address to, uint256 amount) external;
GitHub: 27
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) { 251 function subRect( 252 LeftRightSigned x, 253 LeftRightSigned y 254: ) internal pure returns (LeftRightSigned z) {
GitHub: 39, 46, 59, 78, 101, 108, 121, 134, 148, 171, 194, 214, 232, 251
File: contracts/types/LiquidityChunk.sol 70 function createChunk( 71 int24 _tickLower, 72 int24 _tickUpper, 73 uint128 amount 74: ) internal pure returns (LiquidityChunk) { 89 function addLiquidity( 90 LiquidityChunk self, 91 uint128 amount 92: ) internal pure returns (LiquidityChunk) { 102 function addTickLower( 103 LiquidityChunk self, 104 int24 _tickLower 105: ) internal pure returns (LiquidityChunk) { 118 function addTickUpper( 119 LiquidityChunk self, 120 int24 _tickUpper 121: ) internal pure returns (LiquidityChunk) { 135 function updateTickLower( 136 LiquidityChunk self, 137 int24 _tickLower 138: ) internal pure returns (LiquidityChunk) { 151 function updateTickUpper( 152 LiquidityChunk self, 153 int24 _tickUpper 154: ) internal pure returns (LiquidityChunk) { 171: function tickLower(LiquidityChunk self) internal pure returns (int24) { 180: function tickUpper(LiquidityChunk self) internal pure returns (int24) { 189: function liquidity(LiquidityChunk self) internal pure returns (uint128) {
GitHub: 70, 89, 102, 118, 135, 151, 171, 180, 189
File: contracts/types/TokenId.sol 87: function poolId(TokenId self) internal pure returns (uint64) { 96: function tickSpacing(TokenId self) internal pure returns (int24) { 118: function optionRatio(TokenId self, uint256 legIndex) internal pure returns (uint256) { 128: function isLong(TokenId self, uint256 legIndex) internal pure returns (uint256) { 138: function tokenType(TokenId self, uint256 legIndex) internal pure returns (uint256) { 148: function riskPartner(TokenId self, uint256 legIndex) internal pure returns (uint256) { 158: function strike(TokenId self, uint256 legIndex) internal pure returns (int24) { 183: function addPoolId(TokenId self, uint64 _poolId) internal pure returns (TokenId) { 193: function addTickSpacing(TokenId self, int24 _tickSpacing) internal pure returns (TokenId) { 221 function addOptionRatio( 222 TokenId self, 223 uint256 _optionRatio, 224 uint256 legIndex 225: ) internal pure returns (TokenId) { 240 function addIsLong( 241 TokenId self, 242 uint256 _isLong, 243 uint256 legIndex 244: ) internal pure returns (TokenId) { 255 function addTokenType( 256 TokenId self, 257 uint256 _tokenType, 258 uint256 legIndex 259: ) internal pure returns (TokenId) { 273 function addRiskPartner( 274 TokenId self, 275 uint256 _riskPartner, 276 uint256 legIndex 277: ) internal pure returns (TokenId) { 291 function addStrike( 292 TokenId self, 293 int24 _strike, 294 uint256 legIndex 295: ) internal pure returns (TokenId) { 310 function addWidth( 311 TokenId self, 312 int24 _width, 313 uint256 legIndex 314: ) internal pure returns (TokenId) { 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) { 404: function countLongs(TokenId self) internal pure returns (uint256) { 464: function clearLeg(TokenId self, uint256 i) internal pure returns (TokenId) {
GitHub: 87, 96, 118, 128, 138, 148, 158, 183, 193, 221, 240, 255, 273, 291, 310, 336, 404, 464
</details>@notice
tags@notice
is used to explain to end users what the function does, and the compiler interprets ///
or /**
comments (but not //
or /*
) as this tag if one wasn't explicitly provided
There is one 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: ) {
GitHub: 178
There is one 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: ) {
GitHub: 178
@dev
tags@dev
is used to explain extra details to developers
There is one instance of this issue:
File: contracts/CollateralTracker.sol 168 /// @notice Ensure that the associated Panoptic pool is the caller. Revert if not. 169 modifier onlyPanopticPool() { 170 if (msg.sender != address(s_panopticPool)) revert Errors.NotPanopticPool(); 171 _; 172: }
GitHub: 168
@dev
tagsi.e. @dev
tags. Note that since they're non-public, @notice
is not the right tag to use.
There are 52 instances of this issue:
<details> <summary>see 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;
GitHub: 70, 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;
GitHub: 63, 66, 69, 72, 75, 78, 89, 96, 99, 102
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; 160: int256 internal constant MAX_SLOW_FAST_DELTA = 1800; 171: uint256 internal constant BP_DECREASE_BUFFER = 13_333; 174: uint256 internal constant NO_BUFFER = 10_000; 179: SemiFungiblePositionManager internal immutable SFPM;
GitHub: 120, 133, 136, 156, 160, 171, 174, 179
File: contracts/SemiFungiblePositionManager.sol 126: bool internal constant BURN = true; 133: uint128 private constant VEGOID = 2; 287: mapping(bytes32 positionKey => LeftRightUnsigned accountPremium) private s_accountPremiumOwed; 289: mapping(bytes32 positionKey => LeftRightUnsigned accountPremium) private s_accountPremiumGross;
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: contracts/libraries/Math.sol 15: uint256 internal constant MAX_UINT256 = 2 ** 256 - 1;
GitHub: 15
File: contracts/libraries/PanopticMath.sol 23: uint256 internal constant MAX_UINT256 = 2 ** 256 - 1; 26: uint64 internal constant TICKSPACING_MASK = 0xFFFF000000000000;
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: contracts/types/LiquidityChunk.sol 54 uint256 internal constant CLEAR_TL_MASK = 55: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; 58 uint256 internal constant CLEAR_TU_MASK = 59: 0xFFFFFF000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
</details>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;
e.g. @notice
tags
There are 9 instances of this issue:
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;
GitHub: 120, 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;
@inheritdoc
rather than using a non-standard tagsThere are 2 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) {
Note that some file names may indicate an interface, but actually contain abstract contracts
There are 7 instances of this issue:
<details> <summary>see instances</summary>File: contracts/CollateralTracker.sol 2: pragma solidity ^0.8.18;
GitHub: 2
File: contracts/PanopticFactory.sol 2: pragma solidity ^0.8.18;
GitHub: 2
File: contracts/PanopticPool.sol 2: pragma solidity ^0.8.18;
GitHub: 2
File: contracts/SemiFungiblePositionManager.sol 2: pragma solidity ^0.8.18;
GitHub: 2
File: contracts/multicall/Multicall.sol 2: pragma solidity ^0.8.18;
GitHub: 2
File: contracts/tokens/ERC1155Minimal.sol 2: pragma solidity ^0.8.0;
GitHub: 2
File: contracts/tokens/ERC20Minimal.sol 2: pragma solidity ^0.8.0;
GitHub: 2
</details>Consider changing the variable to be an unnamed one, since the variable is never assigned, nor is it returned by name. If the optimizer is not turned on, leaving the code as it is will also waste gas for the stack variable.
There are 18 instances of this issue:
File: contracts/CollateralTracker.sol /// @audit assetTokenAddress 361: function asset() external view returns (address assetTokenAddress) { /// @audit totalManagedAssets 370: function totalAssets() public view returns (uint256 totalManagedAssets) { /// @audit shares 379: function convertToShares(uint256 assets) public view returns (uint256 shares) { /// @audit assets 386: function convertToAssets(uint256 shares) public view returns (uint256 assets) { /// @audit maxAssets 392: function maxDeposit(address) external pure returns (uint256 maxAssets) { /// @audit maxShares 444: function maxMint(address) external view returns (uint256 maxShares) { /// @audit maxAssets 507: function maxWithdraw(address owner) public view returns (uint256 maxAssets) { /// @audit shares 518: function previewWithdraw(uint256 assets) public view returns (uint256 shares) { /// @audit maxShares 572: function maxRedeem(address owner) public view returns (uint256 maxShares) { /// @audit assets 581: function previewRedeem(uint256 shares) public view returns (uint256 assets) { /// @audit poolUtilization 741: function _poolUtilization() internal view returns (int256 poolUtilization) { /// @audit sellCollateralRatio 751 function _sellCollateralRatio( 752 int256 utilization 753: ) internal view returns (uint256 sellCollateralRatio) { /// @audit buyCollateralRatio 806 function _buyCollateralRatio( 807 uint256 utilization 808 ) internal view returns (uint256 buyCollateralRatio) { 809 // linear from BUY to BUY/2 between 50% and 90% 810 // the buy ratio is on a straight line defined between two points (x0,y0) and (x1,y1): 811 // (x0,y0) = (targetPoolUtilization,buyCollateralRatio) and 812 // (x1,y1) = (saturatedPoolUtilization,buyCollateralRatio / 2) 813 // note that y1<y0 so the slope is negative: 814: // aka the buy ratio starts high and drops to a lower value with increased utilization; the sell ratio does the opposite (slope is positive) /// @audit required 1278 function _getRequiredCollateralSingleLeg( 1279 TokenId tokenId, 1280 uint256 index, 1281 uint128 positionSize, 1282 int24 atTick, 1283 uint128 poolUtilization 1284: ) internal view returns (uint256 required) {
GitHub: 361, 370, 379, 386, 392, 444, 507, 518, 572, 581, 741, 751, 806, 1278
File: contracts/PanopticPool.sol /// @audit premium1 /// @audit premium0 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 collateralToken 1431: function collateralToken0() external view returns (CollateralTracker collateralToken) {
File: contracts/SemiFungiblePositionManager.sol /// @audit UniswapV3Pool 1555 function getUniswapV3PoolFromId( 1556 uint64 poolId 1557: ) external view returns (IUniswapV3Pool UniswapV3Pool) {
GitHub: 1555
There are units for seconds, minutes, hours, days, and weeks, and since they're defined, they should be used
There are 3 instances of this issue:
File: contracts/PanopticPool.sol /// @audit 600 128: uint32 internal constant TWAP_WINDOW = 600; /// @audit 60 136: uint256 internal constant MEDIAN_PERIOD = 60; /// @audit 1800 160: int256 internal constant MAX_SLOW_FAST_DELTA = 1800;
While integers with a large number of bits are unlikely to overflow on human time scales, it is not strictly correct to use an unchecked
block around them, because eventually they will overflow, and unchecked
blocks are meant for cases where it's mathematically impossible for an operation to trigger an overflow (e.g. a prior require()
statement prevents the overflow case)
There are 3 instances of this issue:
File: contracts/libraries/Math.sol 449: result++; 589: result++; 666: result++;
See the whitespace section of the Solidity Style Guide
There are 18 instances of this issue:
<details> <summary>see instances</summary>File: contracts/CollateralTracker.sol 1206 // (a potentially newly minted position) 1207 uint256 totalIterations = positionBalanceArray.length; 1208: for (uint256 i = 0; i < totalIterations; ) {
GitHub: 1206
File: contracts/PanopticFactory.sol 297 // Runs until 'bestSalt' reaches 'minTargetRarity' or for 'loops', whichever comes first 298 uint256 maxSalt; 299 unchecked { 300 maxSalt = uint256(salt) + loops; 301 } 302 303: for (; uint256(salt) < maxSalt; ) {
GitHub: 297
File: contracts/PanopticPool.sol 1206: (LeftRightSigned positionPremia, ) = _calculateAccumulatedPremia( 440 // loop through each option position/tokenId 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; ) { 744 // compute upper and lower tick and liquidity 745: for (uint256 leg = 0; leg < numLegs; ) { 793 /// @param positionIdList the option position to liquidate. 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) { 801 premiasByLeg = new LeftRightSigned[4][](positionIdList.length); 802: for (uint256 i = 0; i < positionIdList.length; ) { 860 // reset balances and delete stored option data 861 s_positionBalance[owner][tokenId] = LeftRightUnsigned.wrap(0); 862 863 uint256 numLegs = tokenId.countLegs(); 864: for (uint256 leg = 0; leg < numLegs; ) { 1379 // Check that position hash (the fingerprint of option positions) matches the one stored for the '_account' 1380 uint256 fingerprintIncomingList; 1381 1382: for (uint256 i = 0; i < pLength; ) { 1502 /// up to current block. atTick = type(int24).max will only consider fees as of the last on-chain transaction. 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 { 1517 uint256 numLegs = tokenId.countLegs(); 1518: for (uint256 leg = 0; leg < numLegs; ) { 1843 // compute accumulated fees 1844 (premiaByLeg, premiumAccumulatorsByLeg) = _getPremia( 1845 tokenId, 1846 positionSize, 1847 owner, 1848 COMPUTE_ALL_PREMIA, 1849 type(int24).max 1850 ); 1851 1852: for (uint256 leg = 0; leg < numLegs; ) {
GitHub: 1206, 440, 744, 793, 860, 1379, 1502, 1843
File: contracts/SemiFungiblePositionManager.sol 574 // so just check if there is an active reentrancy lock for the relevant pool on each token 575: for (uint256 i = 0; i < ids.length; ) { 597 // Extract univ3pool from the poolId map to Uniswap Pool 598 IUniswapV3Pool univ3pool = s_poolContext[id.poolId()].pool; 599 600 uint256 numLegs = id.countLegs(); 601: for (uint256 leg = 0; leg < numLegs; ) { 881 // loop through up to the 4 potential legs in the tokenId 882: for (uint256 leg = 0; leg < numLegs; ) {
File: contracts/libraries/FeesCalc.sol 45 /// @return value1 The amount of token1 owned by portfolio 46 function getPortfolioValue( 47 int24 atTick, 48 mapping(TokenId tokenId => LeftRightUnsigned balance) storage userBalance, 49 TokenId[] calldata positionIdList 50 ) external view returns (int256 value0, int256 value1) { 51 for (uint256 k = 0; k < positionIdList.length; ) { 52 TokenId tokenId = positionIdList[k]; 53 uint128 positionSize = userBalance[tokenId].rightSlot(); 54 uint256 numLegs = tokenId.countLegs(); 55: for (uint256 leg = 0; leg < numLegs; ) {
GitHub: 45
File: contracts/libraries/PanopticMath.sol 253: (int56[] memory tickCumulatives, ) = univ3pool.observe(secondsAgos); 389 /// @return shortAmounts Left-right packed word where the right contains the total contract size and the left total notional 390 function computeExercisedAmounts( 391 TokenId tokenId, 392 uint128 positionSize 393 ) internal pure returns (LeftRightSigned longAmounts, LeftRightSigned shortAmounts) { 394 uint256 numLegs = tokenId.countLegs(); 395: for (uint256 leg = 0; leg < numLegs; ) {
File: contracts/multicall/Multicall.sol 11 /// @return results The data returned by each call 12 function multicall(bytes[] calldata data) public payable returns (bytes[] memory results) { 13 results = new bytes[](data.length); 14: for (uint256 i = 0; i < data.length; ) {
GitHub: 11
File: contracts/tokens/ERC1155Minimal.sol 139 // Storing these outside the loop saves ~15 gas per iteration. 140 uint256 id; 141 uint256 amount; 142 143: for (uint256 i = 0; i < ids.length; ) {
GitHub: 139
</details>Usually lines in source code are limited to 80 characters. Today's screens are much larger so it's reasonable to stretch this in some cases. The solidity style guide recommends a maximumum line length of 120 characters, so the lines below should be split when they reach that length.
There are 334 instances of this issue:
<details> <summary>see instances</summary>File: contracts/PanopticFactory.sol 101: /// @notice Mapping from address(UniswapV3Pool) to address(PanopticPool) that stores the address of all deployed Panoptic Pools 201: /// @notice Create a new Panoptic Pool linked to the given Uniswap pool identified uniquely by the incoming parameters. 256: // If this is not the case, we increase the next cardinality during deployment so the cardinality can catch up over time 285: /// @param salt Salt value ([160-bit deployer address][96-bit nonce]) to start from, useful as a checkpoint across multiple calls 287: /// @param minTargetRarity The minimum target rarity to mine for. The internal loop stops when this is reached *or* when no more iterations 347: // In this case, the `fullRangeLiquidity` will always be an underestimate in respect to the token amounts required to mint.
GitHub: 101, 201, 256, 285, 287, 347
File: contracts/PanopticPool.sol 36: /// @param bonusAmounts LeftRight encoding for the the bonus paid for token 0 (right slot) and 1 (left slot) from the Panoptic Pool to the liquidator. 61: /// @param settledAmounts LeftRight encoding for the amount of premium settled for token0 (right slot) and token1 (left slot). 87: /// @param poolUtilizations Packing of the pool utilization (how much funds are in the Panoptic pool versus the AMM pool at the time of minting), 89: /// Where totalAssets is the total tracked assets in the AMM and PanopticPool minus fees and donations to the Panoptic pool. 113: /// @dev Only include the share of (settled) premium that is available to collect when calling `_calculateAccumulatedPremia` 131: // This oracle is updated with the last Uniswap observation during `mintOptions` if MEDIAN_PERIOD has elapsed past the last observation 132: // If true, the "slow" oracle price is instead computed on-the-fly from 7 Uniswap observations (spaced 5 observations apart) irrespective of the frequency of `mintOptions` calls 142: /// Note that the *minimum* total observation time is determined by the blocktime and may need to be adjusted by chain 144: /// In this case, if there is an interaction every block, the "fast" oracle can consider 3 consecutive block end prices (min=36 seconds on Ethereum) 151: /// @dev Structured such that the minimum total observation time is 7 minutes on Ethereum (similar to internal median mode) 154: // The maximum allowed delta between the currentTick and the Uniswap TWAP tick during a liquidation (~5% down, ~5.26% up) 159: /// Falls back on the more conservative (less solvent) tick during times of extreme volatility (to ensure the account is always solvent) 170: // multiplier (x10k) for the collateral requirement in the event of a buying power decrease, such as minting or force exercising 237: // collected for every chunk per unit of liquidity (net or short, depending on the isLong value of the specific leg index) 241: /// @dev Per-chunk `last` value that gives the aggregate amount of premium owed to all sellers when multiplied by the total amount of liquidity `totalLiquidity` 253: /// @dev Tracks the amount of liquidity for a user+tokenId (right slot) and the initial pool utilizations when that position was minted (left slot) 267: /// The accumulator also tracks the total number of positions (ie. makes sure the length of the provided positionIdList matches); 270: /// this hash can be cheaply verified on every operation with a user provided positionIdList - and we can use that for operations 278: /// @notice During construction: sets the address of the panoptic factory smart contract and the SemiFungiblePositionMananger (SFPM). 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 368: // the 64 most significant bits are the utilization of token1, so we can shift the number to the right by 64 to extract it 377: /// @param includePendingPremium true = include premium that is owed to the user but has not yet settled, false = only include premium that is available to collect. 380: /// @return balances A list of balances and pool utilization for each position, of the form [[tokenId0, balances0], [tokenId1, balances1], ...]. 425: /// @param computeAllPremia Whether to compute accumulated premia for all legs held by the user (true), or just owed premia for long legs (false). 426: /// @param includePendingPremium true = include premium that is owed to the user but has not yet settled, false = only include premium that is available to collect. 427: /// @return portfolioPremium The computed premia of the user's positions, where premia contains the accumulated premia for token0 in the right slot and for token1 in the left slot. 428: /// @return balances A list of balances and pool utilization for each position, of the form [[tokenId0, balances0], [tokenId1, balances1], ...]. 494: /// @notice Disable slippage checks if tickLimitLow == tickLimitHigh and reverses ticks if given in correct order to enable ITM swaps 541: /// @param positionIdList the list of currently held positions by the user, where the newly minted position(token) will be the last element in 'positionIdList'. 543: /// @param effectiveLiquidityLimitX32 Maximum amount of "spread" defined as totalLiquidity/netLiquidity for a new position. 608: /// @param positionIdList the list of currently held positions by the user, where the newly minted position(token) will be the last element in 'positionIdList'. 610: /// @param effectiveLiquidityLimitX32 Maximum amount of "spread" defined as totalLiquidity/netLiquidity for a new position. 660: // Add an initial buffer to the collateral requirement to prevent users from minting their account close to insolvency 663: // Update `s_miniMedian` with a new observation if the last observation is old enough (returned medianData is nonzero) 675: /// @return poolUtilizations Packing of the pool utilization (how much funds are in the Panoptic pool versus the AMM pool) at the time of minting, 703: /// @return poolUtilizations Packing of the pool utilization (how much funds are in the Panoptic pool versus the AMM pool at the time of minting), 705: /// Where totalAssets is the total tracked assets in the AMM and PanopticPool minus fees and donations to the Panoptic pool. 737: /// @param effectiveLiquidityLimitX32 Maximum amount of "spread" defined as totalLiquidity/netLiquidity for a new position 881: /// @notice Falls back to the more conservative tick if the delta between the fast and slow oracle exceeds `MAX_SLOW_FAST_DELTA`. 882: /// @dev Effectively, this means that the users must be solvent at both the fast and slow oracle ticks if one of them is stale to mint or burn options. 886: /// @return medianData If nonzero (enough time has passed since last observation), the updated value for `s_miniMedian` with a new observation 942: // If one of the ticks is too stale, we fall back to the more conservative tick, i.e, the user must be solvent at both the fast and slow oracle ticks. 1012: /// @dev Will revert if liquidated account is solvent at the TWAP tick or if TWAP tick is too far away from the current tick. 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. 1070: // Works like a transfer, so the liquidator must possess all the tokens they are delegating, resulting in no net supply change 1083: // Do not commit any settled long premium to storage - we will do this after we determine if any long premium must be revoked 1084: // This is to prevent any short positions the liquidatee has being settled with tokens that will later be revoked 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 1113: // note that the haircutPremia function also commits the settled amounts (adjusted for the haircut) to storage, so it will be called even if there is no haircut 1115: // if premium is haircut from a token that is not in protocol loss, some of the liquidation bonus will be converted into that token 1187: // '_calculateAccumulatedPremia' expects a list of positions to be touched, and this is the only way to pass a single position 1190: // validate the exercisor's position list (the exercisee's list will be evaluated after their position is force exercised) 1284: /// @notice check whether an account is solvent at a given `atTick` with a collateral requirement of `buffer`/10_000 multiplied by the requirement of `positionIdList`. 1334: /// @param tokenData0 Leftright encoded word with balance of token0 in the right slot, and required balance in left slot. 1335: /// @param tokenData1 Leftright encoded word with balance of token1 in the right slot, and required balance in left slot. 1350: // the amount of cross-collateral balance needed for the account to be solvent, computed in terms of liquidity 1363: /// @dev Check whether the list of positionId 1) has duplicates and 2) matches the length stored in the positionsHash. 1366: /// @param offset Changes depending on whether this is a new mint or a liquidation (=1 if new mint, 0 if liquidation). 1378: // note that if pLength == 0 even if a user has existing position(s) the below will fail b/c the fingerprints will mismatch 1397: /// @notice Updates the hash for all positions owned by an account. This fingerprints the list of all incoming options with a single hash. 1400: /// @dev The positions hash is stored as the XOR of the keccak256 of each tokenId. Updating will XOR the existing hash with the new tokenId. 1401: /// The same update can either add a new tokenId (when minting an option), or remove an existing one (when burning it) - this happens through the XOR. 1404: /// @param addFlag Pass addFlag=true when this is adding a position, needed to ensure the number of positions increases or decreases. 1463: /// @param effectiveLiquidityLimitX32 Maximum amount of "spread" defined as totalLiquidity/netLiquidity for a new position 1491: // the effective liquidity measures how many times more the newly added liquidity is compared to the existing/base liquidity 1500: /// @param computeAllPremia Whether to compute accumulated premia for all legs held by the user (true), or just owed premia for long legs (false). 1542: // if the premium accumulatorLast is higher than current, it means the premium accumulator has overflowed and rolled over at least once 1544: // if there are multiple rollovers or the rollover goes past the last accumulator, rolled over fees will just remain unclaimed 1582: /// @dev Called by sellers on buyers of their chunk to increase the available premium for withdrawal (before closing their position). 1584: /// @param positionIdList Exhaustive list of open positions for the `owners` used for solvency checks where the tokenId to be settled is the last element. 1638: // deduct the paid premium tokens from the owner's balance and add them to the cumulative settled token delta 1649: // commit the delta in settled tokens (all of the premium paid by long chunks in the tokenIds list) to storage 1657: // ensure the owner is solvent (insolvent accounts are not permitted to pay premium unless they are being liquidated) 1676: // add any tokens collected from Uniswap in a given chunk to the settled tokens available for withdrawal by sellers 1690: // (grossPremium - adjustedGrossPremiumLast)*updatedTotalLiquidityPostMint/2**64 is equal to (grossPremium - grossPremiumLast)*totalLiquidityBeforeMint/2**64 1753: /// @param grossPremiumLast The `last` values used with `premiumAccumulators` to compute the total premium owed to sellers
GitHub: 36, 61, 87, 89, 113, 131, 132, 142, 144, 151, 154, 159, 170, 237, 241, 253, 267, 270, 278, 310, 368, 377, 380, 425, 426, 427, 428, 494, 541, 543, 608, 610, 660, 663, 675, 703, 705, 737, 881, 882, 886, 942, 1012, 1015, 1070, 1083, 1084, 1112, 1113, 1115, 1187, 1190, 1284, 1334, 1335, 1350, 1363, 1366, 1378, 1397, 1400, 1401, 1404, 1463, 1491, 1500, 1542, 1544, 1582, 1584, 1638, 1649, 1657, 1676, 1690, 1753
File: contracts/SemiFungiblePositionManager.sol 24: // ,. .,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,. ,, 25: // ,,,,,,, ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, ,,,,,, 26: // .,,,,,,,,,,. ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, ,,,,,,,,,,, 27: // .,,,,,,,,,,,,,,, ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,. ,,,,,,,,,,,,,,, 28: // ,,,,,,,,,,,,,,. ,,,,,,,,,,,,,,,,,,,,,,,,,,, ,,,,,,,,,,,,,,,,,,,,,,,,,,. ,,,,,,,,,,,,,,, 29: // ,,,,,,,,,,,,,,, ,,,,,,,,,,,,,,,,,,,,,, ,,,,,,,,,,,,,,,,,,,,,, ,,,,,,,,,,,,,,, 30: // ,,,,,,,,,,,,,. ,,,,,,,,,,,,,,,,,, .,,,,,,,,,,,,,,,,,, ,,,,,,,,,,,,,, 31: // ,,,,,,,,,,,,,, ,,,,,,,,,,,,,,,,,. ,,,,,,,,,,,,,,,,, .,,,,,,,,,,,,, 32: // ,,,,,,,,,,,,,. .,,,,,,,,,,,,,,,. ,,,,,,,,,,,,,,,, ,,,,,,,,,,,,,. 33: // ,,,,,,,,,,,,, ,,,,,,,,,,,,,,, ,,,,,,,,,,,,,,, ,,,,,,,,,,,,, 34: // ,,,,,,,,,,,,, ,,,,,,,,,,,,,,. ,,,,,,,,,,,,,, ,,,,,,,,,,,,, 35: // ,,,,,,,,,,,,, ,,,,,,,,,,,,,, ,,,,,,,,,,,,,, ,,,,,,,,,,,,, 36: // ,,,,,,,,,,,,, ,,,,,,,,,,,,, ,,,,,,,,,,,,,, ,,,,,,,,,,,,. 37: // .,,,,,,,,,,,, .,,,,,,,,,,,,, ,,,,,,,,,,,,, ,,,,,,,,,,,, 38: // ,,,,,,,,,,,, ,,,,,,,,,,,, ,,,,,,,,,,,,, .,,,,,,,,,,,, 39: // ,,,,,,,,,,,, ,,,,,,,,,,,, ,,,,,,,,,,,,. ,,,,,,,,,,,, 40: // ,,,,,,,,,,,, ,,,,,,,,,,,,. āāāāāāāāā āāāāāāāāāāā āāāāāāāāāāā āāāāāā āāāāāā ,,,,,,,,,,,, ,,,,,,,,,,,, 41: // .,,,,,,,,,,,, ,,,,,,,,,,,, āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā āāāāāā .,,,,,,,,,,,, ,,,,,,,,,,,, 42: // ,,,,,,,,,,,, ,,,,,,,,,,,, āāāā āāā āāāā ā ā āāāā āāāā āāāāāāāāāāāāāā ,,,,,,,,,,,, ,,,,,,,,,,,,. 43: // ,,,,,,,,,,,, ,,,,,,,,,,,, āāāāāāāāāāā āāāāāāāā āāāāāāāāāāā āāāāāāāāā āāāā .,,,,,,,,,,, ,,,,,,,,,,,. 44: // ,,,,,,,,,,,, ,,,,,,,,,,,, āāāāāāāāāāā āāāāāāāā āāāāāāāāāā āāāā āāā āāāā ,,,,,,,,,,,. ,,,,,,,,,,,, 45: // ,,,,,,,,,,,, ,,,,,,,,,,,, āāā āāāā āāāā ā āāāā āāāā āāāā ,,,,,,,,,,,, ,,,,,,,,,,,, 46: // ,,,,,,,,,,,, ,,,,,,,,,,,, āāāāāāāāāāā āāāāā āāāāā āāāāā āāāāā ,,,,,,,,,,, ,,,,,,,,,,,, 47: // ,,,,,,,,,,,, ,,,,,,,,,,,, āāāāāāāāā āāāāā āāāāā āāāāā āāāāā ,,,,,,,,,,,, ,,,,,,,,,,,. 48: // ,,,,,,,,,,,, .,,,,,,,,,,,. ,,,,,,,,,,,, ,,,,,,,,,,,, 49: // .,,,,,,,,,,,, ,,,,,,,,,,,, .,,,,,,,,,,,, ,,,,,,,,,,,, 50: // ,,,,,,,,,,,, ,,,,,,,,,,,,, ,,,,,,,,,,,, ,,,,,,,,,,,, 51: // ,,,,,,,,,,,,. ,,,,,,,,,,,,. ,,,,,,,,,,,,. ,,,,,,,,,,,, 52: // ,,,,,,,,,,,, ,,,,,,,,,,,,, ,,,,,,,,,,,,, .,,,,,,,,,,,, 53: // ,,,,,,,,,,,, ,,,,,,,,,,,,, ,,,,,,,,,,,,, .,,,,,,,,,,,, 54: // .,,,,,,,,,,,, ,,,,,,,,,,,,, ,,,,,,,,,,,,,. ,,,,,,,,,,,, 55: // ,,,,,,,,,,,,, ,,,,,,,,,,,,,, .,,,,,,,,,,,,,. ,,,,,,,,,,,, 56: // ,,,,,,,,,,,,, .,,,,,,,,,,,,,, .,,,,,,,,,,,,,, .,,,,,,,,,,,, 57: // ,,,,,,,,,,,,, ,,,,,,,,,,,,,,, ,,,,,,,,,,,,,,,. ,,,,,,,,,,,,,. 58: // ,,,,,,,,,,,,,, ,,,,,,,,,,,,,,,, .,,,,,,,,,,,,,,,, ,,,,,,,,,,,,, 59: // .,,,,,,,,,,,,, ,,,,,,,,,,,,,,,,, .,,,,,,,,,,,,,,,,, ,,,,,,,,,,,,,, 60: // ,,,,,,,,,,,,,, ,,,,,,,,,,,,,,,,,,,. ,,,,,,,,,,,,,,,,,,,. ,,,,,,,,,,,,,, 61: // ,,,,,,,,,,,,,,, ,,,,,,,,,,,,,,,,,,,,,, .,,,,,,,,,,,,,,,,,,,,,, ,,,,,,,,,,,,,, 62: // ,,,,,,,,,,,,,,, .,,,,,,,,,,,,,,,,,,,,,,,,,,,,,. ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, .,,,,,,,,,,,,,,. 63: // ,,,,,,,,,,,,,,. ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, ,,,,,,,,,,,,,,, 64: // ,,,,,,,,,, ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, .,,,,,,,,,, 65: // ,,,,,. ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, ,,,,,, 129: // Similar to vega in options because the liquidity utilization is somewhat reflective of the implied volatility (IV), 132: // The effect of vegoid on the long premium multiplier can be explored here: https://www.desmos.com/calculator/mdeqob2m04 173: /// @dev mapping that stores the liquidity data of keccak256(abi.encodePacked(address poolAddress, address owner, int24 tickLower, int24 tickUpper)) 174: // liquidityData is a LeftRight. The right slot represents the liquidity currently sold (added) in the AMM owned by the user 175: // the left slot represents the amount of liquidity currently bought (removed) that has been removed from the AMM - the user owes it to a seller 176: // the reason why it is called "removedLiquidity" is because long options are created by removed liquidity -ie. short selling LP positions 219: For an arbitrary parameter 0 <= ν <= 1 (ν = 1/2^VEGOID). This way, the gross_feesCollectedX128 will be given by: 291: /// @dev mapping that stores a LeftRight packing of feesBase of keccak256(abi.encodePacked(address poolAddress, address owner, int24 tickLower, int24 tickUpper)) 292: /// @dev Base fees is stored as int128((feeGrowthInside0LastX128 * liquidity) / 2**128), which allows us to store the accumulated fees as int128 instead of uint256 294: /// feesBase represents the baseline fees collected by the position last time it was updated - this is recalculated every time the position is collected from with the new value 378: // note: we preserve the state of `locked` to prevent reentering a pool by initializing it during the reentrant call 467: /// @param slippageTickLimitLow The lower price slippage limit when minting an ITM position (set to larger than slippageTickLimitHigh for swapping when minting) 468: /// @param slippageTickLimitHigh The higher slippage limit when minting an ITM position (set to lower than slippageTickLimitLow for swapping when minting) 469: /// @return collectedByLeg An array of LeftRight encoded words containing the amount of token0 and token1 collected as fees for each leg 470: /// @return totalSwapped A LeftRight encoded word containing the total amount of token0 and token1 swapped if minting ITM 500: /// @param slippageTickLimitLow The lower price slippage limit when minting an ITM position (set to larger than slippageTickLimitHigh for swapping when minting) 501: /// @param slippageTickLimitHigh The higher slippage limit when minting an ITM position (set to lower than slippageTickLimitLow for swapping when minting) 502: /// @return collectedByLeg An array of LeftRight encoded words containing the amount of token0 and token1 collected as fees for each leg 503: /// @return totalSwapped A LeftRight encoded word containing the total amount of token0 and token1 swapped if minting ITM 547: // we don't need to reentrancy lock on transfers, but we can't allow transfers for a pool during mint/burn with a reentrant call 554: // transfer the token (note that all state updates are completed before reentrancy is possible through onReceived callbacks) 573: // we don't need to reentrancy lock on transfers, but we can't allow transfers for a pool during mint/burn with a reentrant call 588: /// @dev token transfers are only allowed if you transfer your entire liquidity of a given chunk and the recipient has none 602: // for this leg index: extract the liquidity chunk: a 256bit word containing the liquidity amount and upper/lower tick 659: /// @notice Helper that checks the proposed option position and size and forwards the minting and potential swapping tasks. 664: /// @notice and then forwards the minting/burning/swapping to another internal helper functions which perform the AMM pool actions. 678: /// @return collectedByLeg An array of LeftRight encoded words containing the amount of token0 and token1 collected as fees for each leg 716: // if the in-the-money amount is not zero (i.e. positions were minted ITM) and the user did provide tick limits LOW > HIGH, then swap necessary amounts 731: /// @notice When a position is minted or burnt in-the-money (ITM) we are *not* 100% token0 or 100% token1: we have a mix of both tokens. 732: /// @notice The swapping for ITM options is needed because only one of the tokens are "borrowed" by a user to create the position. 750: /// If we take token0 as an example, we deploy it to the AMM pool and *then* swap to get the right mix of token0 and token1 752: /// It that position is burnt, then we remove a mix of the two tokens and swap one of them so that the user receives only one. 784: // note: upstream users of this function such as the Panoptic Pool should ensure users always compensate for the ITM amount delta 785: // the netting swap is not perfectly accurate, and it is possible for swaps to run out of liquidity, so we do not want to rely on it 791: // note: negative ITM amounts denote a surplus of tokens (burning liquidity), while positive amounts denote a shortage of tokens (minting liquidity) 793: // we do this by flipping the signs on the token1 ITM amount converting+deducting it against the token0 ITM amount 861: /// @return collectedByLeg An array of LeftRight encoded words containing the amount of token0 and token1 collected as fees for each leg 897: // We loop in reverse order if burning a position so that any dependent long liquidity is returned to the pool first, 902: // for this _leg index: extract the liquidity chunk: a 256bit word containing the liquidity amount and upper/lower tick 921: // increment accumulators of the upper bound on tokens contained across all legs of the position at any given tick 936: // Ensure upper bound on amount of tokens contained across all legs of the position on any given tick does not exceed a maximum of (2**127-1). 937: // This is the maximum value of the `int128` type we frequently use to hold token amounts, so a given position's size should be guaranteed to 992: // s_accountLiquidity is a LeftRight. The right slot represents the liquidity currently sold (added) in the AMM owned by the user 993: // the left slot represents the amount of liquidity currently bought (removed) that has been removed from the AMM - the user owes it to a seller 994: // the reason why it is called "removedLiquidity" is because long options are created by removing -ie.short selling LP positions 1001: // we're minting more liquidity in uniswap: so add the incoming liquidity chunk to the existing liquidity chunk 1011: // so we seek to move the incoming liquidity chunk *out* of uniswap - but was there sufficient liquidity sitting in uniswap 1121: // (i.e if only token0 (right slot) of the owed premium overflows, then stop accumulating both token0 owed premium and token0 gross premium for the chunk) 1122: // this prevents situations where the owed premium gets out of sync with the gross premium due to one of them overflowing 1132: /// @notice Compute the feesGrowth * liquidity / 2**128 by reading feeGrowthInside0LastX128 and feeGrowthInside1LastX128 from univ3pool.positions. 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 1160: /// @dev here we're converting the value to an int128 even though all values (feeGrowth, liquidity, Q128) are strictly positive. 1161: /// That's because of the way feeGrowthInside works in Uniswap v3, where it can underflow when stored for the first time. 1162: /// This is not a problem in Uniswap v3 because the fees are always calculated by taking the difference of the feeGrowths, 1164: /// So by using int128 instead of uint128, we remove the need to handle extremely large underflows and simply allow it to be negative 1219: /// @notice Burn a chunk of liquidity (`liquidityChunk`) in the Uniswap v3 pool and send to msg.sender; return the amount moved. 1247: /// @notice Helper to collect amounts between msg.sender and Uniswap and also to update the Uniswap fees collected to date from the AMM. 1250: /// @param currentLiquidity the existing liquidity msg.sender owns in the AMM for this chunk before the SFPM was called 1254: /// @return collectedChunk the incoming amount collected with potentially whatever is collected in this function added to it 1281: // Collect only if there was existing startingLiquidity=liquidities.rightSlot() at that position: collect all fees 1319: /// @return deltaPremiumOwed The extra premium (per liquidity X64) to be added to the owed accumulator for token0 (right) and token1 (left) 1320: /// @return deltaPremiumGross The extra premium (per liquidity X64) to be added to the gross accumulator for token0 (right) and token1 (left) 1334: // explains how we get from the premium per liquidity (calculated here) to the total premia collected and the multiplier 1420: /// @return accountLiquidities The amount of liquidity that has been shorted/added to the Uniswap contract (netLiquidity:removedLiquidity -> rightSlot:leftSlot) 1429: /// @dev tokenType input here is the asset of the positions minted, this avoids put liquidity to be used for call, and vice-versa 1435: /// @notice Return the premium associated with a given position, where Premium is an accumulator of feeGrowth for the touched position. 1436: /// @dev Computes s_accountPremium{isLong ? Owed : Gross}[keccak256(abi.encodePacked(univ3pool, owner, tokenType, tickLower, tickUpper))] 1437: /// @dev if an atTick parameter is provided that is different from type(int24).max, then it will update the premium up to the current 1438: /// @dev block at the provided atTick value. We do this because this may be called immediately after the Uni v3 pool has been touched 1445: /// @param atTick The current tick. Set atTick < type(int24).max = 8388608 to get latest premium up to the current block 1447: /// @return premiumToken0 The amount of premium (per liquidity X64) for token0 = sum (feeGrowthLast0X128) over every block where the position has been touched 1448: /// @return premiumToken1 The amount of premium (per liquidity X64) for token1 = sum (feeGrowthLast0X128) over every block where the position has been touched 1464: // Compute the premium up to the current block (ie. after last touch until now). Do not proceed if atTick == type(int24).max = 8388608 1476: // how much fees have been accumulated within the liquidity chunk since last time we updated this chunk? 1478: // currentFeesGrowth (calculated from FeesCalc.calculateAMMSwapFeesLiquidityChunk) is (ammFeesCollectedPerLiquidity * liquidityChunk.liquidity()) 1479: // oldFeesGrowth is the last stored update of fee growth within the position range in the past (feeGrowthRange*liquidityChunk.liquidity()) (s_accountFeesBase[positionKey]) 1488: // If the current feesBase is close or identical to the stored one, the amountToCollect can be negative. 1505: // (i.e if only token0 (right slot) of the owed premium overflows, then stop accumulating both token0 owed premium and token0 gross premium for the chunk) 1506: // this prevents situations where the owed premium gets out of sync with the gross premium due to one of them overflowing
GitHub: 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 129, 132, 173, 174, 175, 176, 219, 291, 292, 294, 378, 467, 468, 469, 470, 500, 501, 502, 503, 547, 554, 573, 588, 602, 659, 664, 678, 716, 731, 732, 750, 752, 784, 785, 791, 793, 861, 897, 902, 921, 936, 937, 992, 993, 994, 1001, 1011, 1121, 1122, 1132, 1137, 1160, 1161, 1162, 1164, 1219, 1247, 1250, 1254, 1281, 1319, 1320, 1334, 1420, 1429, 1435, 1436, 1437, 1438, 1445, 1447, 1448, 1464, 1476, 1478, 1479, 1488, 1505, 1506
File: contracts/libraries/CallbackLib.sol 11: /// @notice This library provides functions to verify that a callback came from a canonical Uniswap V3 pool with a claimed set of features. 35: // Call getPool on the factory to verify that the sender corresponds to the canonical pool with the claimed features
File: contracts/libraries/Errors.sol 18: /// @notice PanopticPool: the effective liquidity (X32) is greater than min(`MAX_SPREAD`, `USER_PROVIDED_THRESHOLD`) during a long mint or short burn 22: /// @notice CollateralTracker: attempted to withdraw/redeem more than available liquidity, owned shares, or open positions would allow for 25: /// @notice PanopticPool: force exercisee is insolvent - liquidatable accounts are not permitted to open or close positions outside of a liquidation 41: /// @param parameterType poolId=0, ratio=1, tokenType=2, risk_partner=3 , strike=4, width=5, two identical strike/width/tokenType chunks=6 44: /// @notice A mint or swap callback was attempted from an address that did not match the canonical Uniswap V3 pool with the claimed features 50: /// @notice PanopticPool: one of the legs in a position are force-exercisable (they are all either short or ITM long)
GitHub: 18, 22, 25, 41, 44, 50
File: contracts/libraries/FeesCalc.sol 17: /// @dev Some options positions involve moving liquidity chunks to the AMM/Uniswap. Those chunks can then earn AMM swap fees. 96: /// @return The fees collected from the AMM for each token (LeftRight-packed) with token0 in the right slot and token1 in the left slot 137: // lowerOut0: For token0: fee growth per unit of liquidity on the _other_ side of tickLower (relative to currentTick)
File: contracts/libraries/InteractionHelper.sol 18: /// @notice Function that performs approvals on behalf of the PanopticPool for CollateralTracker and SemiFungiblePositionManager. 40: /// @notice Computes the name of a CollateralTracker based on the token composition and fee of the underlying Uniswap Pool. 41: /// @dev Some tokens do not have proper symbols so error handling is required - this logic takes up significant bytecode size, which is why it is in a library.
File: contracts/libraries/Math.sol 124: /// @dev Implemented using Uniswap's "incorrect" constants. Supplying commented-out real values for an accurate calculation. 216: /// @notice Calculates the amount of token0 and token1 received for a given LiquidityChunk at the provided currentTick. 334: /// @notice Calculates floor(aĆbĆ·denominator) with full precision. Throws if result overflows a uint256 or denominator == 0. 435: /// @notice Calculates ceil(aĆbĆ·denominator) with full precision. Throws if result overflows a uint256 or denominator == 0. 502: // Divide [prod1 prod0] by the factors of two (note that this is just 2**96 since the denominator is a power of 2 itself) 565: // Divide [prod1 prod0] by the factors of two (note that this is just 2**96 since the denominator is a power of 2 itself) 642: // Divide [prod1 prod0] by the factors of two (note that this is just 2**128 since the denominator is a power of 2 itself) 719: // Divide [prod1 prod0] by the factors of two (note that this is just 2**96 since the denominator is a power of 2 itself) 748: /// @notice QuickSort is a sorting algorithm that employs the Divide and Conquer strategy. It selects a pivot element and arranges the given array around
GitHub: 124, 216, 334, 435, 502, 565, 642, 719, 748
File: contracts/libraries/PanopticMath.sol 34: /// the 64 bits are the 48 *last* (most significant) bits - and thus corresponds to the *first* 12 hex characters (reading left to right) 35: /// of the Uniswap v3 pool address, with the tickSpacing written in the highest 16 bits (i.e, max tickSpacing is 32768) 86: /// @notice The positions hash contains a single fingerprint of all positions created by an account/user as well as a tally of the positions. 88: /// @param existingHash The existing position hash containing all historical N positions created and the count of the positions 89: /// @param tokenId The new position to add to the existing hash: existingHash = uint248(existingHash) ^ hashOf(tokenId) 90: /// @param addFlag Whether to mint (add) the tokenId to the count of positions or burn (subtract) it from the count (existingHash >> 248) +/- 1 114: /// @dev Uniswap observations snapshot the closing price of the last block before the first interaction of a given block. 115: /// @dev The maximum frequency of observations is 1 per block, but there is no guarantee that the pool will be observed at every block. 116: /// @dev Each period has a minimum length of blocktime * period, but may be longer if the Uniswap pool is relatively inactive. 117: /// @dev The final price used in the array (of length `cardinality`) is the average of all observations comprising `period` (which is itself a number of observations). 136: // get the last 4 timestamps/tickCumulatives (if observationIndex < cardinality, the index will wrap back from observationCardinality) 147: // use cardinality periods given by cardinality + 1 accumulator observations to compute the last cardinality observed ticks spaced by period 159: /// @notice Takes a packed structure representing a sorted 7-slot ring buffer of ticks and returns the median of those values. 160: /// @dev Also inserts the latest Uniswap observation into the buffer, resorts, and returns if the last entry is at least `period` seconds old. 163: /// @param period The minimum time in seconds that must have passed since the last observation was inserted into the buffer 167: /// @return updatedMedianData The updated 7-slot ring buffer of ticks with the latest observation inserted if the last entry is at least `period` seconds old (returns 0 otherwise) 237: /// @dev We instead observe the average price over a series of time intervals, and define the TWAP as the median of those averages. 274: /// @notice For a given option position (`tokenId`), leg index within that position (`legIndex`), and `positionSize` get the tick range spanned and its 310: // Because Uni v3 chooses token0 and token1 from the alphanumeric order, there is no consistency as to whether token0 is 311: // stablecoin, ETH, or an ERC20. Some pools may want ETH to be the asset (e.g. ETH-DAI) and some may wish the stablecoin to 315: // To solve this, we encode the asset value in tokenId. This parameter specifies which of token0 or token1 is the 344: // The max/min ticks that can be initialized are the closest multiple of tickSpacing to the actual max/min tick abs()=887272 365: /// @notice Returns the distances of the upper and lower ticks from the strike for a position with the given width and tickSpacing. 385: /// @notice Compute the amount of funds that are underlying this option position. This is useful when exercising a position. 388: /// @return longAmounts Left-right packed word where the right contains the total contract size and the left total notional 389: /// @return shortAmounts Left-right packed word where the right contains the total contract size and the left total notional 412: /// @notice Adds required collateral and collateral balance from collateralTracker0 and collateralTracker1 and converts to single values in terms of `tokenType`. 413: /// @param tokenData0 LeftRight type container holding the collateralBalance (right slot) and requiredCollateral (left slot) for a user in CollateralTracker0 (expressed in terms of token0) 414: /// @param tokenData1 LeftRight type container holding the collateralBalance (right slot) and requiredCollateral (left slot) for a user in CollateralTracker0 (expressed in terms of token1) 438: /// @notice Adds required collateral and collateral balance from collateralTracker0 and collateralTracker1 and converts to single values in terms of `tokenType`. 439: /// @param tokenData0 LeftRight type container holding the collateralBalance (right slot) and requiredCollateral (left slot) for a user in CollateralTracker0 (expressed in terms of token0) 440: /// @param tokenData1 LeftRight type container holding the collateralBalance (right slot) and requiredCollateral (left slot) for a user in CollateralTracker0 (expressed in terms of token1) 455: /// @notice Compute the notional amount given an incoming total number of `contracts` deployed between `tickLower` and `tickUpper`. 456: /// @dev The notional value of an option is the value of the crypto assets that are controlled (rather than the cost of the transaction). 461: /// @dev Thus, `contracts` refer to "100" in this example. The $20 is the strike price. We get the strike price from `tickLower` and `tickUpper`. 462: /// @dev From TradFi: [https://www.investopedia.com/terms/n/notionalvalue.asp](https://www.investopedia.com/terms/n/notionalvalue.asp). 485: /// @notice Convert an amount of token0 into an amount of token1 given the sqrtPriceX96 in a Uniswap pool defined as sqrt(1/0)*2^96. 502: /// @notice Convert an amount of token1 into an amount of token0 given the sqrtPriceX96 in a Uniswap pool defined as sqrt(1/0)*2^96. 519: /// @notice Convert an amount of token0 into an amount of token1 given the sqrtPriceX96 in a Uniswap pool defined as sqrt(1/0)*2^96. 542: /// @notice Convert an amount of token0 into an amount of token1 given the sqrtPriceX96 in a Uniswap pool defined as sqrt(1/0)*2^96. 569: /// @notice Compute the amount of token0 and token1 moved. Given an option position `tokenId`, leg index `legIndex`, and how many contracts are in the leg `positionSize`. 571: /// @param positionSize The number of option contracts held in this position (each contract can control multiple tokens) 573: /// @return A LeftRight encoded variable containing the amount0 and the amount1 value controlled by this option position's leg 642: /// @param tokenData0 Leftright encoded word with balance of token0 in the right slot, and required balance in left slot 643: /// @param tokenData1 Leftright encoded word with balance of token1 in the right slot, and required balance in left slot 650: /// @return The LeftRight-packed protocol loss for both tokens, i.e., the delta between the user's balance and expended tokens 692: // this is already present in the netExchanged amount, so to avoid double-counting we remove it from the balance 701: // note that "balance0" and "balance1" are the liquidatee's original balances before token delegation by a liquidator 702: // their actual balances at the time of computation may be higher, but these are a buffer representing the amount of tokens we 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 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 756: /// @notice Haircut/clawback any premium paid by `liquidatee` on `positionIdList` over the protocol loss threshold during a liquidation. 757: /// @dev Note that the storage mapping provided as the `settledTokens` parameter WILL be modified on the caller by this function. 765: /// @param settledTokens The per-chunk accumulator of settled tokens in storage from which to subtract the haircut premium 790: // Ignore any surplus collateral - the liquidatee is either solvent or it converts to <1 unit of the other token 795: // if the premium in the same token is not enough to cover the loss and there is a surplus of the other token, 796: // the liquidator will provide the tokens (reflected in the bonus amount) & receive compensation in the other token 886: // The long premium is not commited to storage during the liquidation, so we add the entire adjusted amount 910: /// @notice Returns the original delegated value to a user at a certain tick based on the available collateral from the exercised user. 926: // if the refunder lacks sufficient token0 to pay back the refundee, have them pay back the equivalent value in token1 927: // note: it is possible for refunds to be negative when the exercise fee is higher than the delegated amounts. This is expected behavior
GitHub: 34, 35, 86, 88, 89, 90, 114, 115, 116, 117, 136, 147, 159, 160, 163, 167, 237, 274, 310, 311, 315, 344, 365, 385, 388, 389, 412, 413, 414, 438, 439, 440, 455, 456, 461, 462, 485, 502, 519, 542, 569, 571, 573, 642, 643, 650, 692, 701, 702, 707, 708, 709, 710, 711, 712, 713, 725, 726, 727, 728, 729, 730, 731, 756, 757, 765, 790, 795, 796, 886, 910, 926, 927
File: contracts/libraries/SafeTransferLib.sol 25: // Get free memory pointer - we will store our calldata in scratch space starting at the offset specified here. 56: // Get free memory pointer - we will store our calldata in scratch space starting at the offset specified here.
File: contracts/multicall/Multicall.sol 22: // Other solutions will do work to differentiate the revert reasons and provide paranthetical information 24: // NOTE: memory-safe because it reads from memory already allocated by solidity (the bytes memory result)
File: contracts/tokens/ERC1155Minimal.sol 57: /// @notice Emitted when an attempt is made to initiate a transfer to a contract recipient that fails to signal support for ERC1155.
GitHub: 57
File: contracts/tokens/interfaces/IERC20Partial.sol 8: /// @dev However, we cannot productively handle a failed approval and such a situation would surely cause a revert later in execution. 9: /// @dev In addition, no notable instances exist of tokens that both i) contain a failure case for `approve` and ii) return `false` instead of reverting.
File: contracts/types/LeftRight.sol 51: // Typically, the slot is already clear when writing to it, but if it is not, the bits will be added to the existing bits 53: // Note that the values *within* the slots are allowed to overflow, but overflows are contained and will not leak into the other slot 113: // Typically, the slot is already clear when writing to it, but if it is not, the bits will be added to the existing bits 115: // Note that the values *within* the slots are allowed to overflow, but overflows are contained and will not leak into the other slot 181: // type cast z to uint128 to isolate the right slot and if it's higher than a value that was subtracted from (x) 272: /// @dev Used for linked accumulators, so if the accumulator for one side overflows for a token, both cease to accumulate.
GitHub: 51, 53, 113, 115, 181, 272
File: contracts/types/LiquidityChunk.sol 10: /// @title A Panoptic Liquidity Chunk. Tracks Tick Range and Liquidity Information for a "chunk." Used to track movement of chunks. 13: /// @notice A liquidity chunk is an amount of `liquidity` (an amount of WETH, e.g.) deployed between two ticks: `tickLower` and `tickUpper`
File: contracts/types/TokenId.sol 24: // (0) univ3pool 48bits 0bits : first 6 bytes of the Uniswap v3 pool address (first 48 bits; little-endian), plus a pseudorandom number in the event of a collision 43: // <---- 48 bits ----> <---- 48 bits ----> <---- 48 bits ----> <---- 48 bits ----> <- 16 bits -> <- 48 bits -> 44: // Leg 4 Leg 3 Leg 2 Leg 1 tickSpacing Univ3 Pool Address 46: // <--- most significant bit least significant bit ---> 51: // - if a leg is active (e.g. leg 1) there can be no gaps in other legs meaning: if leg 1 is active then leg 3 cannot be active if leg 2 is inactive. 55: // We also refer to the legs via their index, so leg number 2 has leg index 1 (legIndex) (counting from zero), and in general leg number N has leg index N-1. 56: // - the underlying strike price of the 2nd leg (leg index = 1) in this option position starts at bit index (64 + 12 + 48 * (leg index=1))=123 73: /// @notice AND mask to clear all bits except for the components of the chunk key (strike, width, tokenType) for each leg 86: /// @return The `poolId` (Panoptic's pool fingerprint, contains the whole 64 bit sequence with the tickSpacing) of the Uniswap V3 pool 144: /// @notice Get the associated risk partner of the leg index (generally another leg index in the position if enabled or the same leg index if no partner). 164: /// @notice Get the width of the nth leg (index `legIndex`). This is half the tick-range covered by the leg (tickUpper - tickLower)/2. 369: // We copy the logic from the countLegs function, using it here adds 5K to the contract size with IR for some reason 375: // Since only the option ratios remain, we can be sure that no bits above the start of the inactive legs will be 1 390: // i.e the whole mask is used to flip all legs with 4 legs, but only the first leg is flipped with 1 leg so we shift by 3 legs 391: // We also clear the poolId area of the mask to ensure the bits that are shifted right into the area don't flip and cause issues 430: /// @dev ASSUMPTION: For any leg, the option ratio is always > 0 (the leg always has a number of contracts associated with it). 438: // Since only the option ratios remain, we can be sure that no bits above the start of the inactive legs will be 1 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)
GitHub: 24, 43, 44, 46, 51, 55, 56, 73, 86, 144, 164, 369, 375, 390, 391, 430, 438, 563, 564, 565
</details>The Solidity style guide says to use mixedCase for function argument names
There are 3 instances of this issue:
File: contracts/CollateralTracker.sol 185: uint256 _ITMSpreadMultiplier
GitHub: 185
File: contracts/PanopticFactory.sol 116: address _WETH9, 117: SemiFungiblePositionManager _SFPM,
external
/public
function names should begin with an underscoreAccording to the Solidity Style Guide, non-external
/public
function names should begin with an underscore
There are 5 instances of this issue:
File: contracts/PanopticPool.sol 1450: function getUniV3TWAP() internal view returns (int24 twapTick) {
GitHub: 1450
File: contracts/SemiFungiblePositionManager.sol 320: function beginReentrancyLock(uint64 poolId) internal { 330: function endReentrancyLock(uint64 poolId) internal { 593: function registerTokenTransfer(address from, address to, TokenId id, uint256 amount) internal { 756 function swapInAMM( 757 IUniswapV3Pool univ3pool, 758 LeftRightSigned itmAmounts 759: ) internal returns (LeftRightSigned totalSwapped) {
And functions within contracts should be separate by a single line
There is one instance of this issue:
File: contracts/tokens/interfaces/IDonorNFT.sol 4 import {PanopticPool} from "@contracts/PanopticPool.sol"; 5 6: interface IDonorNFT {
GitHub: 4
constant
/immutable
variablesIf the variable needs to be different based on which class it comes from, a view
/pure
function should be used instead (e.g. like this).
There is one instance of this issue:
File: contracts/PanopticFactory.sol 116: address _WETH9,
GitHub: 116
The variable is being cast to its own type
There are 15 instances of this issue:
File: contracts/PanopticFactory.sol /// @audit contract IUniswapV3Pool 404: IUniswapV3Pool(v3Pool).mint(
GitHub: 404
File: contracts/PanopticPool.sol /// @audit contract IUniswapV3Pool 302: s_univ3pool = IUniswapV3Pool(_univ3pool); /// @audit contract IUniswapV3Pool 304: (, int24 currentTick, , , , , ) = IUniswapV3Pool(_univ3pool).slot0(); /// @audit uint256 309: (uint256(block.timestamp) << 216) +
File: contracts/SemiFungiblePositionManager.sol /// @audit address 447: ? address(decoded.poolFeatures.token0) /// @audit address 448: : address(decoded.poolFeatures.token1);
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))
GitHub: 110, 120, 130, 140, 150, 212, 229, 263, 281
Note that there may be cases where a variable appears to be used, but this is only because there are multiple definitions of the varible in different files. In such cases, the variable definition should be moved into a separate file. The instances below are the unused variables.
There are 2 instances of this issue:
File: contracts/libraries/Math.sol 15: uint256 internal constant MAX_UINT256 = 2 ** 256 - 1;
GitHub: 15
File: contracts/libraries/PanopticMath.sol 23: uint256 internal constant MAX_UINT256 = 2 ** 256 - 1;
GitHub: 23
The identifier is imported but never used within the file
There is one instance of this issue:
File: contracts/types/LiquidityChunk.sol /// @audit TokenId 5: import {TokenId} from "@types/TokenId.sol";
GitHub: 5
Use bit shifts in an immutable variable rather than long hex strings for powers of two, for readability (e.g. 0x1000000000000000000000000
-> 1 << 96
)
There are 28 instances of this issue:
File: contracts/libraries/Constants.sol 9: uint256 internal constant FP96 = 0x1000000000000000000000000;
GitHub: 9
File: contracts/libraries/Math.sol 93: if (x >= 0x100000000000000000000000000000000) { 97: if (x >= 0x10000000000000000) { 101: if (x >= 0x100000000) { 105: if (x >= 0x10000) { 109: if (x >= 0x100) { 113: if (x >= 0x10) { 133: uint256 sqrtR = absTick & 0x1 != 0 135: : 0x100000000000000000000000000000000; 137: if (absTick & 0x2 != 0) sqrtR = (sqrtR * 0xfff97272373d413259a46990580e213a) >> 128; 139: if (absTick & 0x4 != 0) sqrtR = (sqrtR * 0xfff2e50f5f656932ef12357cf3c7fdcc) >> 128; 141: if (absTick & 0x8 != 0) sqrtR = (sqrtR * 0xffe5caca7e10e4e61c3624eaa0941cd0) >> 128; 143: if (absTick & 0x10 != 0) sqrtR = (sqrtR * 0xffcb9843d60f6159c9db58835c926644) >> 128; 145: if (absTick & 0x20 != 0) sqrtR = (sqrtR * 0xff973b41fa98c081472e6896dfb254c0) >> 128; 147: if (absTick & 0x40 != 0) sqrtR = (sqrtR * 0xff2ea16466c96a3843ec78b326b52861) >> 128; 149: if (absTick & 0x80 != 0) sqrtR = (sqrtR * 0xfe5dee046a99a2a811c461f1969c3053) >> 128; 151: if (absTick & 0x100 != 0) sqrtR = (sqrtR * 0xfcbe86c7900a88aedcffc83b479aa3a4) >> 128; 153: if (absTick & 0x200 != 0) sqrtR = (sqrtR * 0xf987a7253ac413176f2b074cf7815e54) >> 128; 155: if (absTick & 0x400 != 0) sqrtR = (sqrtR * 0xf3392b0822b70005940c7a398e4b70f3) >> 128; 157: if (absTick & 0x800 != 0) sqrtR = (sqrtR * 0xe7159475a2c29b7443b29c7fa6e889d9) >> 128; 159: if (absTick & 0x1000 != 0) sqrtR = (sqrtR * 0xd097f3bdfd2022b8845ad8f792aa5825) >> 128; 161: if (absTick & 0x2000 != 0) sqrtR = (sqrtR * 0xa9f746462d870fdf8a65dc1f90e061e5) >> 128; 163: if (absTick & 0x4000 != 0) sqrtR = (sqrtR * 0x70d869a156d2a1b890bb3df62baf32f7) >> 128; 165: if (absTick & 0x8000 != 0) sqrtR = (sqrtR * 0x31be135f97d08fd981231505542fcfa6) >> 128; 167: if (absTick & 0x10000 != 0) sqrtR = (sqrtR * 0x9aa508b5b7a84e1c677de54f3e99bc9) >> 128; 169: if (absTick & 0x20000 != 0) sqrtR = (sqrtR * 0x5d6af8dedb81196699c329225ee604) >> 128; 171: if (absTick & 0x40000 != 0) sqrtR = (sqrtR * 0x2216e584f5fa1ea926041bedfe98) >> 128; 173: if (absTick & 0x80000 != 0) sqrtR = (sqrtR * 0x48a170391f7dc42444e8fa2) >> 128;
GitHub: 93, 97, 101, 105, 109, 113, 133, 135, 137, 139, 141, 143, 145, 147, 149, 151, 153, 155, 157, 159, 161, 163, 165, 167, 169, 171, 173
override
is unnecessaryStarting with Solidity version 0.8.8, using the override
keyword when the function solely overrides an interface function, and the function doesn't exist in multiple base contracts, is unnecessary.
There are 4 instances of this issue:
File: contracts/CollateralTracker.sol 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: contracts/SemiFungiblePositionManager.sol 540 function safeTransferFrom( 541 address from, 542 address to, 543 uint256 id, 544 uint256 amount, 545 bytes calldata data 546: ) public override { 566 function safeBatchTransferFrom( 567 address from, 568 address to, 569 uint256[] calldata ids, 570 uint256[] calldata amounts, 571 bytes calldata data 572: ) public override {
When deploying contracts, you should use the latest released version of Solidity. Apart from exceptional cases, only the latest version receives security fixes.
https://docs.soliditylang.org/en/v0.8.20/
Since deployed contracts should not use floating pragmas, I've flagged all instances where a version prior to 0.8.19 is allowed by the version pragma
There are 7 instances of this issue:
<details> <summary>see instances</summary>File: contracts/CollateralTracker.sol 2: pragma solidity ^0.8.18;
GitHub: 2
File: contracts/PanopticFactory.sol 2: pragma solidity ^0.8.18;
GitHub: 2
File: contracts/PanopticPool.sol 2: pragma solidity ^0.8.18;
GitHub: 2
File: contracts/SemiFungiblePositionManager.sol 2: pragma solidity ^0.8.18;
GitHub: 2
File: contracts/multicall/Multicall.sol 2: pragma solidity ^0.8.18;
GitHub: 2
File: contracts/tokens/ERC1155Minimal.sol 2: pragma solidity ^0.8.0;
GitHub: 2
File: contracts/tokens/ERC20Minimal.sol 2: pragma solidity ^0.8.0;
GitHub: 2
</details>The default value for boolean variables is false
, so initializing them to false
is superfluous.
There are 5 instances of this issue:
File: contracts/PanopticPool.sol 111: bool internal constant COMPUTE_LONG_PREMIA = false; 114: bool internal constant ONLY_AVAILABLE_PREMIUM = false; 120: bool internal constant DONOT_COMMIT_LONG_SETTLED = false; 133: bool internal constant SLOW_ORACLE_UNISWAP_MODE = false;
File: contracts/SemiFungiblePositionManager.sol 125: bool internal constant MINT = false;
GitHub: 125
internal
There are 8 instances of this issue:
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;
#0 - c4-judge
2024-04-26T10:36:00Z
Picodes marked the issue as grade-b