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: 67/72
Findings: 1
Award: $11.32
π Selected for report: 0
π Solo Findings: 0
π 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
https://github.com/code-423n4/2023-11-panoptic/blob/f75d07c345fd795f907385868c39bafcd6a56624/contracts/libraries/PanopticMath.sol#L149 https://github.com/code-423n4/2023-11-panoptic/blob/f75d07c345fd795f907385868c39bafcd6a56624/contracts/libraries/PanopticMath.sol#L172
The if
/else
condition is incorrectly implemented in convert0to1()
and convert1to0()
.
sqrtPriceX96
should be compared against type(uint128).max
, while it's compared against 340275971719517849884101479065584693834
value.
if (sqrtPriceX96 < 340275971719517849884101479065584693834) {
if (sqrtPriceX96 < 340275971719517849884101479065584693834) {
Given a sqrtPriceX96 and a token amount, convert0to1()
and convert1to0()
return the amount of token received in exchange.
When we examine those functions closely, we can spot, that there are basically forked from Uniswap getQuoteAtTick()
:
https://github.com/Uniswap/v3-periphery/blob/main/contracts/libraries/OracleLibrary.sol#L49-L69 if (sqrtRatioX96 <= type(uint128).max) { uint256 ratioX192 = uint256(sqrtRatioX96) * sqrtRatioX96; quoteAmount = baseToken < quoteToken ? FullMath.mulDiv(ratioX192, baseAmount, 1 << 192) : FullMath.mulDiv(1 << 192, baseAmount, ratioX192); } else { uint256 ratioX128 = FullMath.mulDiv(sqrtRatioX96, sqrtRatioX96, 1 << 64); quoteAmount = baseToken < quoteToken ? FullMath.mulDiv(ratioX128, baseAmount, 1 << 128) : FullMath.mulDiv(1 << 128, baseAmount, ratioX128); }
The Uniswap sets the sqrtRatioX96
boundary to sqrtRatioX96 <= type(uint128).max
, while the current implementation to sqrtPriceX96 < 340275971719517849884101479065584693834
.
This discrepancy will lead to a different calculations and may cause a fund loss.
Manual code review
Change if (sqrtPriceX96 < 340275971719517849884101479065584693834)
to if (sqrtRatioX96 <= type(uint128).max) {
Math
#0 - c4-sponsor
2023-12-15T20:15:01Z
dyedm1 marked the issue as disagree with severity
#1 - c4-sponsor
2023-12-15T20:15:06Z
dyedm1 (sponsor) confirmed
#2 - dyedm1
2023-12-15T20:29:06Z
I have actually never seen that Quoter function before - i suppose great minds think alike ;) I disagree on the severity - a slight loss of potential precision <1 tick early is not at all harmful to the operation of the protocol and has a negligible effect where it's used. We are not dependent on this price estimate being in parity with the quoter in any way shape or form.
Nevertheless, this can still work as a QA because increasing this value a bit for more precision can't hurt.
#3 - c4-judge
2023-12-23T10:10:08Z
Picodes changed the severity to QA (Quality Assurance)
#4 - Picodes
2023-12-23T10:10:28Z
The described impact is of not of Med severity so downgrading to QA
#5 - c4-judge
2023-12-26T23:12:43Z
Picodes marked the issue as grade-b