Closed c4-bot-4 closed 4 months ago
This report describes how the PriceFeed
contract does not account for the exponent of the base price
retrieved from the Pyth oracle
.
As mentioned in the Pyth Network Docs:
"The returned price and confidence are decimal numbers written in the form a * 10^e, where e is an exponent included in the result. For example, a price of 1234 with an exponent of -2 represents the number 12.34. The result also includes a publishTime which is the unix timestamp for the price update."
The base price
simply uses the price
but does not account for the exponent. The recommended mitigation suggests to apply the exponent and account for both positive and negative exponents, since the oracle can return either one. As mentioned in the report, this will lead to an incorrect sqrtPrice
calculation, affecting many different parts of the protocol, such as the ability for bad positions to be liquidated.
Here's a similar finding on solodit, where the protocol correctly applied the exponents to the price returned from the Pyth
oracle, but were not account for negative exponents: #1
Therefore, I believe this finding should be valid.
Lines of code
https://github.com/code-423n4/2024-05-predy/blob/main/src/PriceFeed.sol#L45-L58 https://github.com/code-423n4/2024-05-predy/blob/main/src/vendors/IPyth.sol#L5-L14 https://github.com/code-423n4/2024-05-predy/blob/main/src/libraries/PositionCalculator.sol#L142-L144
Vulnerability details
Impact
The protocol utilizes
getPriceNoOlderThan
from thePyth
oracle to determine the price of thebase token
, which is subsequently used to calculate thesqrtPrice
.The
Pyth
oracle returns aPrice struct
that contains theprice
andexponent
.The protocol does not take into account the
exponent
returned and proceeds to utilize theprice
without theexponent
for thesqrt price
calculation, therefore thesqrt price
will be incorrect.This will impact any calls to
PriceFeed::getSqrtPrice()
. An example is to check if positions are liquidatable if they are unsafe, if thesqrt price
is incorrect, then safe positions may be liquidated or unsafe positions may be unable to be liquidated.Proof of Concept
PriceFeed.sol#L45-L58
The
IPyth.Price
struct contains the following parameters:IPyth.sol#L5-L14
As mentioned in the Pyth Network Docs:
"The returned price and confidence are decimal numbers written in the form a * 10^e, where e is an exponent included in the result. For example, a price of 1234 with an exponent of -2 represents the number 12.34. The result also includes a publishTime which is the unix timestamp for the price update."
This means that the exponent must be applied to the
basePrice.price
to get the actual price. MostPyth Oracles
return a negative exponent, but it's possible that it may be positive, so both cases need to be handled.Currently, the protocol uses the returned
basePrice.price
for thesqrtPrice
calculation, which will return an incorrectsqrtPrice
.An example of where this price feed is used is in
PositionCalculator::getSqrtIndexPrice
PositionCalculator.sol#L142-L144
The flow of the call in this example is
LiquidationLogic::liquidate
=>LiquidationLogic::checkVaultIsDanger
=>PositionCalculator::isLiquidatable
=>PositionCalculator::calculateMinMargin
=>PositionCalculator::getSqrtIndexPrice
In that case, this would not correctly be able to determine if a position is unsafe and should be forced liquidated.
Tools Used
Manual Review
Recommended Mitigation Steps
Apply the returned
expo
of thePrice struct
to theprice
. Theexpo
may be positive or negative, so both cases must be handled. If exponent is negative, ensure to negate it when dividing.Assessed type
Oracle