Swift Lace Rat
Medium
In the NumaOracle.sol
, the getV3SqrtHighestPrice
function and related logic utilize Uniswap V3 oracle-based TWAP (Time Weighted Average Price) and spot prices without validating the freshness of the retrieved data. This creates a potential vulnerability, as the system could rely on stale or outdated price data, leading to inaccurate calculations or financial losses. Additionally, the absence of safeguards against the manipulation of spot prices exacerbates the risk.
The root cause of this vulnerability is the absence of a timestamp check to confirm the freshness of the price data retrieved from Uniswap V3's oracle and spot price. Neither the TWAP retrieval functions (getV3SqrtPriceAvg
) nor the slot0() function for spot prices validate the age of the underlying price data.
function getV3SpotPrice(
address _numaPool,
uint _numaAmount
) external view returns (uint256) {
(uint160 sqrtPriceX96, , , , , , ) = IUniswapV3Pool(_numaPool).slot0();
uint256 numerator = (
IUniswapV3Pool(_numaPool).token0() == token
? sqrtPriceX96
: FixedPoint96.Q96
);
uint256 denominator = (
numerator == sqrtPriceX96 ? FixedPoint96.Q96 : sqrtPriceX96
);
uint256 TokenPerNumaMulAmount = FullMath.mulDivRoundingUp(
FullMath.mulDivRoundingUp(denominator, denominator, numerator),
_numaAmount,
numerator
);
return TokenPerNumaMulAmount;
}
No response
No response
No response
If the Uniswap oracle's observations are outdated or derived from a pool with insufficient activity, calculations will rely on prices that no longer reflect market reality. Incorrect or stale pricing data could result in overestimation or underestimation of asset values, causing potential financial losses to both users and the protocol.
No response
Validate that the TWAP data is up-to-date by checking the last update timestamp from the Uniswap V3 pool.
Example:
function isOracleUpdated(address _uniswapV3Pool, uint32 interval) internal view returns (bool) {
(int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s) =
IUniswapV3Pool(_uniswapV3Pool).observe([interval, 0]);
uint256 lastUpdated = block.timestamp - secondsPerLiquidityCumulativeX128s[1];
require(lastUpdated < FRESHNESS_THRESHOLD, "Price data is stale");
return true;
}