Mismatched Decimals: A DeFi Oracle Vulnerability
Hey guys, let's dive into a tricky vulnerability found in the Tricky Raspberry Okapi project. This issue revolves around a non-working delta calculation caused by mismatched decimals between different price feeds. It’s a high-severity problem, so let’s get into the details!
Summary
In the MixedPriceOracleV4
contract, a vulnerability exists because of differing decimal precisions between API3 and eOracle price feeds. API3 feeds consistently return prices scaled to 18 decimals, which is pretty standard. However, eOracle feeds on Linea can return prices scaled to either 8 or 18 decimals, depending on the asset. This mismatch throws a wrench in the price delta calculation within the _getLatestPrice
function. When eOracle uses 8 decimals, the contract incorrectly detects large deviations. This leads the contract to always fall back to the eOracle price, bypassing the intended use of API3 as a primary price source. For most assets (those with 8-decimal eOracle feeds), the contract effectively relies solely on eOracle, which defeats the purpose of having a dual-oracle validation system. This reliance exposes the system to single-oracle failures or potential manipulations, which isn't ideal for a robust DeFi protocol.
Think of it like this: you're trying to compare two measurements, but one is in meters and the other is in millimeters without converting them first. The difference will look enormous, even if the actual values are quite close. That's what's happening here, leading to a flawed decision-making process within the smart contract.
Root Cause
The root cause of this vulnerability is the failure to normalize API3 and eOracle prices to the same decimal scale before computing the price delta in _getLatestPrice
. The function directly compares the raw int256
price values from latestRoundData
of both feeds using _absDiff(apiV3Price, eOraclePrice)
and then calculates deltaBps
relative to the eOracle price. It's like comparing apples to oranges without any conversion!
Since API3 prices are always scaled by 10^18 (for example, $2918.5652133 becomes 2918565213300000000000), while eOracle prices for USD-paired assets are typically scaled by 10^8 (e.g., $2918.5652133 becomes 291856521), the raw values differ by a factor of 10^10. This significant difference leads to an artificially inflated delta
and deltaBps
. Consequently, the condition deltaBps > deltaSymbol
almost always evaluates to true. This means the intended logic branch that prefers the API3 price (when it is fresh and within the delta) is never taken for 8-decimal eOracle feeds, and the contract always falls back to eOracle if it's fresh enough. Imagine trying to compare the number 2918565213300000000000 with 291856521—the difference will always appear huge, even if the underlying price is practically the same.
For eOracle feeds with 18 decimals (like weETH/ETH), the scales match, and the delta calculation works as expected. But here's the kicker: since most eOracle feeds on Linea use 8 decimals, this issue affects the majority of configured symbols. It's like having a safety net that only works in very specific scenarios, leaving you vulnerable most of the time.
Internal Pre-conditions
To trigger this vulnerability, certain internal conditions must be met within the contract:
- Symbol Configuration: A symbol must be configured in the
configs
mapping with both an API3 feed (18 decimals) and an eOracle feed (typically 8 decimals for USD pairs). - Delta Threshold: The
deltaPerSymbol
(or fallbackmaxPriceDelta
) must be set to a reasonable value. We're talking about something like 1.5% or 1500 bps. This threshold is easily exceeded because of the scale mismatch between the feeds. It's like setting the bar too low, making it almost impossible to pass. - Staleness Period: The staleness period (
_getStaleness(symbol)
) needs to allow the eOracle price to be considered fresh. This is necessary for the fallback to eOracle to occur without the transaction reverting.
External Pre-conditions
There are also external factors that need to align for this vulnerability to manifest:
- eOracle Decimal Precision: The eOracle feed for the symbol needs to use 8 decimals. This is common for USD-denominated pairs on Linea, like ETH/USD or USDC/USD. These assets form the backbone of many DeFi applications, making the vulnerability widespread.
- API3 Decimal Precision: The API3 feed must use 18 decimals, which is the standard for all API3 dAPIs. This consistency, ironically, contributes to the problem because of the mismatch with the 8-decimal eOracle feeds.
- API3 Feed Freshness: For the vulnerability to manifest in price queries, the API3 feed must be fresh (updated within the staleness period). This triggers the flawed delta check. If the API3 feed is stale, the contract might fall back to eOracle anyway due to staleness, masking the decimal mismatch issue.
Attack Path
This isn't a direct exploit an attacker can use to drain funds immediately, but it leads to unintended behavior that weakens the oracle's robustness. Here’s a breakdown of how it plays out:
- Configuration: An administrator (with the
GUARDIAN_ORACLE
role) configures a symbol, say