Panoptic - nisedo's results

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

General Information

Platform: Code4rena

Start Date: 27/11/2023

Pot Size: $60,500 USDC

Total HM: 7

Participants: 72

Period: 7 days

Judge: Picodes

Total Solo HM: 2

Id: 309

League: ETH

Panoptic

Findings Distribution

Researcher Performance

Rank: 51/72

Findings: 1

Award: $19.82

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

🌟 Selected for report: sivanesh_808

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

Awards

19.8173 USDC - $19.82

Labels

bug
G (Gas Optimization)
grade-b
sponsor confirmed
edited-by-warden
G-18

External Links

Note to the Judge:

All gas optimization estimations presented in this report have been determined using the forge test --gas-report feature and compared against the Deployment Cost and Deployment Size of the audit repo commit f75d07c. This tool allows for a precise analysis of gas consumption before and after the proposed changes, ensuring the accuracy of the savings mentioned.

Contract NameDeployment Cost (Before)Deployment Cost (After)Change in Deployment Cost (%)Deployment Size (Before)Deployment Size (After)Change in Deployment Size (%)
FeesCalc2947592947590 (0%)153015300 (0%)
MockERC207599137599130 (0%)491449140 (0%)
MissingReturnToken3458233458230 (0%)155915590 (0%)
ReturnsFalseToken2215002215000 (0%)9389380 (0%)
ReturnsGarbageToken5245915245910 (0%)245224520 (0%)
ReturnsTooLittleToken2247062247060 (0%)9549540 (0%)
ReturnsTooMuchToken3446173446170 (0%)155315530 (0%)
ReturnsTwoToken2183002183000 (0%)9229220 (0%)
RevertingToken2152942152940 (0%)9079070 (0%)
SemiFungiblePositionManagerHarness43302514312225-18026 (-0.42%)2181921729-90 (-0.41%)
UniswapV3FactoryMock1045531045530 (0%)5545540 (0%)
FeesCalcHarness3461863461860 (0%)176117610 (0%)
MathHarness619051611445-7606 (-1.23%)31243086-38 (-1.22%)
PanopticMathHarness14483281442721-5607 (-0.39%)70747046-28 (-0.40%)
MiniPositionManager7159477159470 (0%)360836080 (0%)
ERC1155MinimalHarness9165559165550 (0%)461046100 (0%)
LeftRightHarness3660053660050 (0%)186018600 (0%)
LiquidityChunkHarness1844261844260 (0%)9539530 (0%)
TokenIdHarness8344708344700 (0%)420042000 (0%)
Total-31239-156

G-1: Remove the return statement when the function defines named return variables to save gas

Once the return variable has been assigned (or has its default value), there is no need to explicitly return it at the end of the function, since it's returned automatically.

File: contracts/libraries/Math.sol
211     return result;

278     return result;

307     return result;

369     return result;

431     return result;

493     return result;
File: contracts/libraries/Math.sol
-       return result;

-       return result;

-       return result;

-       return result;

-       return result;

-       return result;
  • Deployment Cost: 611445 (-7606 gas, -1.23%)
  • Deployment Size: 3086 (-38 gas, -1.22%)

G-2: Usage of uints/ints smaller than 32 bytes (256 bits) incurs overhead

When using elements that are smaller than 32 bytes, your contract’s gas usage may be higher. This is because the EVM operates on 32 bytes at a time. Therefore, if the element is smaller than that, the EVM must use more operations in order to reduce the size of the element from 32 bytes to the desired size.

Each operation involving a uint8 costs an extra 22-28 gas (depending on whether the other operand is also a variable of type uint8) as compared to ones involving uint256, due to the compiler having to clear the higher bits of the memory word before operating on the uint8, as well as the associated stack operations of doing so. Use a larger size then downcast where needed.

File: contracts/SemiFungiblePositionManager.sol
1272		uint128 premium0X64_base;
1273		uint128 premium1X64_base;
-		uint128 premium0X64_base;
+		uint256 premium0X64_base;
-		uint128 premium1X64_base;
+		uint256 premium1X64_base;
  • Deployment Cost: 4323042 (-7209 gas, -0.17%)
  • Deployment Size: 21783 (-36 gas, -0.02%)

G-3: Use assembly to check for address(0)

File: contracts/SemiFungiblePositionManager.sol
356		if (address(univ3pool) == address(0)) revert Errors.UniswapPoolNotInitialized();

683		if (univ3pool == IUniswapV3Pool(address(0))) revert Errors.UniswapPoolNotInitialized();
-		if (address(univ3pool) == address(0)) revert Errors.UniswapPoolNotInitialized();

+       bytes4 errorSelector = Errors.UniswapPoolNotInitialized.selector;
+       assembly {
+           let poolAddress := univ3pool

+           if iszero(poolAddress) {
+               mstore(0, errorSelector)
+               revert(0, 4)
+           }
+       }
  • Deployment Cost: 4325242 (-5009 gas, -0.12%)
  • Deployment Size: 21794 (-25 gas, -0.01%)
-		if (univ3pool == IUniswapV3Pool(address(0))) revert Errors.UniswapPoolNotInitialized();

+       bytes4 errorSelector = Errors.UniswapPoolNotInitialized.selector;
+       assembly {
+           let poolAddress := univ3pool

+           if iszero(poolAddress) {
+               mstore(0, errorSelector)
+               revert(0, 4)
+           }
+       }
  • Deployment Cost: 4329451 (-800 gas, -0.02%)
  • Deployment Size: 21815 (-4 gas, -0.01%)

G-4: Use assembly to emit events

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

File: contracts/SemiFungiblePositionManager.sol
386		emit PoolInitialized(univ3pool);

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

523		emit TokenizedPositionMinted(msg.sender, tokenId, positionSize);
File: contracts/tokens/ERC1155Minimal.sol
80              emit ApprovalForAll(msg.sender, operator, approved);

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

G-5: Nesting if-statements is cheaper than using &&

Nesting if-statements avoids the stack operations of setting up and using an extra jumpdest.

File: contracts/SemiFungiblePositionManager.sol
774              if ((itm0 != 0) && (itm1 != 0)) {
775                  (uint160 sqrtPriceX96, , , , , , ) = _univ3pool.slot0();
776  
777                  // implement a single "netting" swap. Thank you @danrobinson for this puzzle/idea
778                  // note: negative ITM amounts denote a surplus of tokens (burning liquidity), while positive amounts denote a shortage of tokens (minting liquidity)
779                  // compute the approximate delta of token0 that should be resolved in the swap at the current tick
780                  // we do this by flipping the signs on the token1 ITM amount converting+deducting it against the token0 ITM amount
781                  // couple examples (price = 2 1/0):
782                  //  - 100 surplus 0, 100 surplus 1 (itm0 = -100, itm1 = -100)
783                  //    normal swap 0: 100 0 => 200 1
784                  //    normal swap 1: 100 1 => 50 0
785                  //    final swap amounts: 50 0 => 100 1
786                  //    netting swap: net0 = -100 - (-100/2) = -50, ZF1 = true, 50 0 => 100 1
787                  // - 100 surplus 0, 100 shortage 1 (itm0 = -100, itm1 = 100)
788                  //    normal swap 0: 100 0 => 200 1
789                  //    normal swap 1: 50 0 => 100 1
790                  //    final swap amounts: 150 0 => 300 1
791                  //    netting swap: net0 = -100 - (100/2) = -150, ZF1 = true, 150 0 => 300 1
792                  // - 100 shortage 0, 100 surplus 1 (itm0 = 100, itm1 = -100)
793                  //    normal swap 0: 200 1 => 100 0
794                  //    normal swap 1: 100 1 => 50 0
795                  //    final swap amounts: 300 1 => 150 0
796                  //    netting swap: net0 = 100 - (-100/2) = 150, ZF1 = false, 300 1 => 150 0
797                  // - 100 shortage 0, 100 shortage 1 (itm0 = 100, itm1 = 100)
798                  //    normal swap 0: 200 1 => 100 0
799                  //    normal swap 1: 50 0 => 100 1
800                  //    final swap amounts: 100 1 => 50 0
801                  //    netting swap: net0 = 100 - (100/2) = 50, ZF1 = false, 100 1 => 50 0
802                  // - = Net surplus of token0
803                  // + = Net shortage of token0
804                  int256 net0 = itm0 - PanopticMath.convert1to0(itm1, sqrtPriceX96);
805  
806                  zeroForOne = net0 < 0;
807  
808                  //compute the swap amount, set as positive (exact input)
809                  swapAmount = -net0;
810              } else if (itm0 != 0) {
811                  zeroForOne = itm0 < 0;
812                  swapAmount = -itm0;
813              } else {
814                  zeroForOne = itm1 > 0;
815                  swapAmount = -itm1;
816              }
+		if (itm0 != 0) {
+			if (itm1 != 0) {
+				(uint160 sqrtPriceX96, , , , , , ) = _univ3pool.slot0();
+				int256 net0 = itm0 - PanopticMath.convert1to0(itm1, sqrtPriceX96);
+				zeroForOne = net0 < 0;
+				swapAmount = -net0;
+			} else {
+				zeroForOne = itm0 < 0;
+				swapAmount = -itm0;
+			}
+		} else {
+			if (itm1 != 0) {
+			zeroForOne = itm1 > 0;
+			swapAmount = -itm1;
+     			}
+		}
  • Deployment Cost: 4328851 (-1400 gas, -0.03%)
  • Deployment Size: 21812 (-7 gas, -0.01%)

#0 - c4-judge

2023-12-14T17:11:13Z

Picodes marked the issue as grade-a

#1 - dyedm1

2023-12-14T18:43:12Z

Good point on the nested if statements :)

#2 - c4-sponsor

2023-12-14T20:00:19Z

dyedm1 (sponsor) confirmed

#3 - c4-judge

2023-12-26T23:29:03Z

Picodes marked the issue as grade-b

AuditHub

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

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter