Skip to content

Latest commit

 

History

History
77 lines (53 loc) · 3.63 KB

014.md

File metadata and controls

77 lines (53 loc) · 3.63 KB

Festive Plum Quail

Medium

Failure to Adjust Fixed-Point Exponent in PythOracle.

Summary

The currentValue() function does not properly account for the fixed-point exponent used in the PythOracle contract from the Pyth network. As a result, the price retrieved may be incorrectly scaled unless the exponent is handled explicitly.

The Pyth network provides price data in a fixed-point format, where the price and its confidence interval are both scaled by an exponent. According to the Pyth documentation, the price is represented as an integer multiplied by 10^exponent. This means that to correctly interpret the price, it must be scaled by 10^exponent.

Root Cause

In the PythOracle.sol , the function currentValue() retrieves the price from the Pyth network through the pythOracle.getPriceUnsafe() function. However, the price returned by getPriceUnsafe() is not adjusted by the exponent, which may lead to incorrect price values being returned.

   function currentValue() external view override returns (uint256) {
        IPyth.Price memory price = pythOracle.getPriceUnsafe(tokenId);
        require(
            price.publishTime < block.timestamp - noOlderThan,
            "Stale Price"
        );
        return uint256(uint64(price.price));
    }

The issue arises because price.price is returned without being adjusted for the exponent.

According to the Pyth fixed-point representation:The integer representation of price value can be computed by multiplying by 10^exponent.

For example, ETH/USD price of $3,474.45:

  • pythPrice = 347445000 // $3,474.45
  • pythExponent = -5 // Price must be multiplied by 10^(-5)
  • actualPythPrice = 347445000 * 10^(-5) = 3474.45 USD

Without applying this exponent adjustment, the currentValue() function would return an incorrect value.

The price returned by the currentValue() function is used in the _getExchangeRate() function. This function is a key component that returns the USD price in 1e8 terms. The value is then utilized in the createOrder(), modifyOrder(), and checkInRange() functions.

Internal pre-conditions

No response

External pre-conditions

No response

Attack Path

No response

Impact

A price that does not account for the exponent disrupts the order-matching mechanism. It can cause mispricing of assets, leading to incorrect fund transfers and arbitrage opportunities, ultimately resulting in fund losses.

PoC

No response

Mitigation

 function currentValue() external view override returns (uint256) {
        IPyth.Price memory price = pythOracle.getPriceUnsafe(tokenId);
        require(price.publishTime < block.timestamp - noOlderThan, "Stale Price");

        // Ensure the raw price is non-negative
+        require(price.price > 0, "Price cannot be negative");

+        if (price.expo < 0) {
+            // If the exponent is negative, divide by 10^(-exponent)
+            return uint256(uint64(price.price)) / (10 ** uint32(int32(-price.expo)));
+        } else {
+            // If the exponent is positive, multiply by 10^(exponent)
+            return uint256(uint64(price.price)) * (10 ** uint32(int32(price.expo)));
+        }
-         return uint256(uint64(price.price));
    }