Open c4-bot-3 opened 9 months ago
Picodes marked the issue as primary issue
othernet-global (sponsor) acknowledged
The 2 expressions are not equal but the goal of the protocol is achieved: what they use for the twap price is more resilient to a sudden drop in price than the spot price. I consider that this is an instance of "function not working as in specs" so of Low severity and not an instance of Medium severity in the absence of convincing evidence that this could lead to real harm being done.
Picodes changed the severity to QA (Quality Assurance)
Hi @Picodes,
Thanks for your judgement & the comments provided. I would like to highlight a few important facts and dispute the QA
tag awarded to this finding as well as Issue #283, also raised by me. Since both are related to TWAP (different problems though), I am clubbing my points together here to make it easier to read -
I cannot fully understand the rationale behind downgrading this to QA inspite being sponsor acknowledged
. I would like to understand at what threshold does C4 make a call that "the goal of increasing the robustness", even via incorrect TWAP calculations is good enough and hence whether to mark it as Med
or QA
? Specially when the innovators of the system, Uniswap themselves recommend against it in Section 5.2 which I had mentioned previously too in my report (I had mentioned 5.1 earlier by mistake, but it's in the same vicinity).
Even in the case of the present report, the judgement seems to be okay with the fact that the prices could be evaluated as 0.5% to maybe 2 or 3% different from the actual uniswap-provided weighted average prices. In my mind, it defeats the purpose of using the "mean" price of a TWAP if we are going to go ahead and implement it incorrectly with a X% deviation. Since you have mentioned about exploring 'real harm', let's look at this for a moment:
averagePrice = ( priceA + priceB ) / 2
inside _aggregatePrices()
which still preserves the erroneous price to an extent.We are forcing incorrect prices throughout the protocol, despite the fix being simple enough - use the correct TWAP feeds instead of doing internal calculations. The impact seems quite obvious and apparent, spread to almost all of the protocol. Is the protocol as well as the end-user aware of this impact, this error which has crept in due to use of inverse TWAP calculations or their multiplication/divisions? Put differently, from a business perspective, would an end-user be okay knowing that the prices being calculated for them are almost always not reflecting the sources they have been promised?
Hence my request to please review.
Thank you
Lines of code
https://github.com/code-423n4/2024-01-salty/blob/main/src/price_feed/CoreUniswapFeed.sol#L112
Vulnerability details
Impact
getTwapWBTC() calculates TWAP value of WBTC/USD by first fetching WBTC/WETH and WETH/USDC TWAPS, and then dividing them to return the result. This is incorrect maths and unlike spot prices, two TWAPS can not be multiplied or divided to calculate the third TWAP. Just generally in mathematics, two averages when multiplied or divided are not supposed to necessarily yield the value of a third average:
This particular report will highlight the fact that 2 different feeds can not be divided or multiplied to obtain the TWAP price of another i.e.:
Multiplication style:
Division style:
Attempting to do so results in an incorrect value. The more volatile the price feeds are, the more extreme this result is. Example presented in the PoC section.
Some additional input
Note that this particular issue is distinct from some of the other ones, like the issue titled
"Incorrect maths calculates inverse TWAP"
which highlighted the improper inverting of token0/token1 TWAP feed while calculating token1/token0. That's because in theMultiplication style
mentioned above, there is no "virtual inverse" happening (otherwise one could have made an argument that in the Division style example, we effectively calculated 1/TWAP of tokenY/tokenA TWAP and then multiplied, choosing to represent it as a division, which would make this issue quite similar to the other issue"Incorrect maths calculates inverse TWAP"
).The current issue persists even in the absence of an inverse TWAP and is hence distinct. Consider this:
wbtc_wethFlipped = true
on L106 then TWAPuniswapWBTC_WETH
is flipped. Let's say thatORIGINAL_uniswapWBTC_WETH
feed was flipped to getuniswapWBTC_WETH
.weth_usdcFlipped = true
on L109 then TWAPuniswapWETH_USDC
is NOT flipped. So we are okay here.uniswapWBTC_WETH
which is the same as multiplying with1/uniswapWBTC_WETH
which is the same as multiplying withORIGINAL_uniswapWBTC_WETH
.So basically, no contribution was made by the "inverse issue" to any error which may happen here.
Now we move on to verify the issue at hand - that of incorrectly using 2 TWAPS to calculate a third one.
Proof of Concept
As per the protocol's current logic:
The actual TWAP however is:
Protocol's calculation has a deviation of
0.035%
from the actual TWAP price.One can imagine much more volatile prices or difference in degree of fluctuations between tokens would result in a much higher price deviation from the actual figure.
Mathematical PoC
As per the protocol's current logic:
The actual TWAP however is:
It's trivial to see from here on that the two expressions are not equal.
Tools used
Manual inspection
Recommended Mitigation Steps
Assessed type
Oracle