QuantAMM

QuantAMM
49,600 OP
View results
Submission Details
Severity: low
Invalid

Missing oracle timestamp freshness check will lead to the stale price been accept

Summary

On some contracts, they get Oracle data but do not check if the timestamp is stale.

Vulnerability Details

On MultiHopOracle::_getData it just checks if the timestamp from firstOracle is greater than oracleConfig then timestamp from firstOracle will be accepted.
But it does not check any freshness logic.

/// @notice Returns the latest data from one oracle hopping across n oracles
/// @return data the latest data from the oracle in the QuantAMM format
/// @return timestamp the timestamp of the data retrieval
function _getData() internal view override returns (int216 data, uint40 timestamp) {
HopConfig memory firstOracle = oracles[0];
@> (data, timestamp) = firstOracle.oracle.getData();
if (firstOracle.invert) {
data = 10 ** 36 / data; // 10^36 (i.e., 1 with 18 decimals * 10^18) to get the inverse with 18 decimals.
// 10**36 is automatically precomputed by the compiler, no explicit caching needed
}
uint256 oracleLength = oracles.length;
for (uint i = 1; i < oracleLength; ) {
HopConfig memory oracleConfig = oracles[i];
(int216 oracleRes, uint40 oracleTimestamp) = oracleConfig.oracle.getData();
@> if (oracleTimestamp < timestamp) {
timestamp = oracleTimestamp; // Return minimum timestamp
}
// depends which way the oracle conversion is happening
if (oracleConfig.invert) {
data = (data * 10 ** 18) / oracleRes;
} else {
data = (data * oracleRes) / 10 ** 18;
}
unchecked {
++i;
}
}
}

Also there should be freshness logic for those:

/// @notice Returns the latest data from the oracle in the QuantAMM format
/// @return data the latest data from the oracle in the QuantAMM format
/// @return timestamp the timestamp of the data retrieval
function _getData() internal view override returns (int216, uint40) {
(, /*uint80 roundID*/ int data, , /*uint startedAt*/ uint timestamp, ) = /*uint80 answeredInRound*/
priceFeed.latestRoundData();
require(data > 0, "INVLDDATA");
data = data * int(10 ** normalizationFactor);
return (int216(data), uint40(timestamp)); // Overflow of data is extremely improbable and uint40 is large enough for timestamps for a very long time
}
/// @notice Get the data of the underlying oracle, interpretation of data depends on oracle type
/// @param data The underlying data (can be negative), normalized to 18 decimals
/// @return data Retrieved oracle data
/// @return timestamp Last update timestamp
function getData() public view returns (int216 data, uint40 timestamp) {
(data, timestamp) = _getData();
require(timestamp > 0, "INVORCLVAL"); // Sanity check in case oracle returns invalid values
}

Impact

It can accept a stale price and this will affect the calculation of the fund logic.

Tools Used

Manually Reviewed

Recommendations

Either add freshness logic on OracleWapper::getData function's timestamp or add on those who are using it.

Add freshness logic like UpdateWeightRunner::_getData

Updates

Lead Judging Commences

n0kto Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Known issue
Assigned finding tags:

invalid_chainlink_staled_data_updateAt_roundId_known_issue

LightChaser: ## [Medium-4] Insufficient oracle validation

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.