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
Rank: 29/72
Findings: 2
Award: $115.49
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: hash
Also found by: 0xCiphky, Neon2835, Topmark, Udsen, critical-or-high, lanrebayode77, ptsanev
104.1662 USDC - $104.17
removedLiquidity -= chunkLiquidity;
will underflow when there is no removedLiquidity in a particular leg.
This is a result of the fact that, when a token is being burnt, the tokenId is flipped to change the isLong
to from 1 to 0, this happens for all the legs.
The problem with here is that not all legs will be 1, some will retain their zero(0 when no liquidity was previously removed). For this reason, removedLiquidity = 0
for such legs, but it was assumed that once the flag is BURN, removeLiquidity will be positive.
Since the calculation happened in an unchecked block, it won't revert but underflow, increasing removedLiquidity to a very large amount. here
return self ^ ((LONG_MASK >> (48 * (4 - optionRatios))) & CLEAR_POOLID_MASK);
When _createPositionInAMM
loops through every leg to create a position in AMM, it calls _createLegInAMM
which checks the
For a tokenId with two legs for instance, the first leg has isLeg = 1
, the second leg has isLong = 0
, when flipped, 1 changes to 0, in the first leg, but the second leg remains 0, but removedLiquidity -= chunkLiquidity;
is still computed assuming that the zero was as a result of the flipToBurnToken
code
/// @dev If the isLong flag is 0=short but the position was burnt, then this is closing a long position /// @dev so the amount of short liquidity should decrease. if (_isBurn) { removedLiquidity -= chunkLiquidity; }
Manual review.
Add a check to ensure removedLiquidity >= chunkLiquidity
Math
#0 - c4-judge
2023-12-14T14:05:59Z
Picodes marked the issue as duplicate of #516
#1 - c4-judge
2023-12-26T21:56:52Z
Picodes marked the issue as partial-50
#2 - c4-judge
2023-12-26T21:56:59Z
Picodes marked the issue as partial-25
#3 - Picodes
2023-12-26T21:57:07Z
No valid scenario or impact are described
🌟 Selected for report: osmanozdemir1
Also found by: 0xCiphky, Audinarey, Banditx0x, CRYP70, Cryptor, D1r3Wolf, KupiaSec, LokiThe5th, Sathish9098, Skylice, ThenPuli, Topmark, Udsen, ZanyBonzy, baice, ether_sky, fatherOfBlocks, foxb868, grearlake, hihen, hubble, hunter_w3b, lanrebayode77, leegh, lsaudit, minhtrng, nocoder, onchain-guardians, ptsanev, ro1sharkm, seaton0x1, sivanesh_808, t4sk, tapir, tpiliposian, ustas
11.3163 USDC - $11.32
/// - 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
By specification the strike price of the second leg will commence at bit index 124
--- /// - 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 +++ /// - 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))=124
#L2 Redundant casting https://github.com/code-423n4/2023-11-panoptic/blob/aa86461c9d6e60ef75ed5a1fe36a748b952c8666/contracts/SemiFungiblePositionManager.sol#L1381 The variable came in form of an address, so there is no need to cast it in an address type
bytes32 positionKey = keccak256( --- abi.encodePacked(address(univ3pool), owner, tokenType, tickLower, tickUpper) +++ abi.encodePacked(univ3pool, owner, tokenType, tickLower, tickUpper) );
#0 - c4-judge
2023-12-14T14:08:48Z
Picodes marked the issue as grade-c
#1 - c4-judge
2024-01-03T11:31:25Z
Picodes marked the issue as grade-b