QuantAMM

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

UpdateWeightRunner._getData() misses data validity checks

Summary

The vulnerability exists in the oracle data fetching logic where the code doesn't properly validate the oracleResult.data when switching between backup oracles. When a primary oracle is stale, the code moves to backup oracles but fails to validate if the last successful oracle data is actually valid (non-zero).

Vulnerability Details

for (uint j = 1; j < numAssetOracles; ) {
oracleResult = _getOracleData(
OracleWrapper(poolBackupOracles[_pool][i][j])
);
if (oracleResult.timestamp > block.timestamp - oracleStalenessThreshold) {
// Oracle has fresh values
break; // <-- Bug: Breaks without validating oracleResult.data
}
// ...
}
outputData[i] = oracleResult.data; // <-- Uses potentially invalid data

Consider this scenario:

  1. Primary oracle becomes stale

  2. First backup oracle returns {timestamp: now, data: 0}

  3. The code accepts this as valid data because only the timestamp is checked

  4. Zero or invalid price data gets propagated to weight calculations

Impact

  1. If a backup oracle returns a timestamp that's fresh but with invalid/zero data, the function will accept this data without validation

  2. This could lead to incorrect weight calculations in the pool since the oracle data is used to determine asset weights

  3. An attacker could potentially manipulate pool weights if they can cause oracles to return valid timestamps with manipulated data

Tools Used

Manual review

Recommendations

Add data validity check:

function _getData(address _pool, bool internalCall) private view returns (int256[] memory outputData) {
// ... existing code ...
for (uint i; i < oracleLength; ) {
OracleData memory oracleResult;
oracleResult = _getOracleData(OracleWrapper(optimisedOracles[i]));
// Check both timestamp and data validity for primary oracle
if (oracleResult.timestamp > block.timestamp - oracleStalenessThreshold && oracleResult.data != 0) {
outputData[i] = oracleResult.data;
} else {
unchecked {
numAssetOracles = poolBackupOracles[_pool][i].length;
}
bool foundValidData = false;
for (uint j = 1; j < numAssetOracles; ) {
oracleResult = _getOracleData(
OracleWrapper(poolBackupOracles[_pool][i][j])
);
// Check both timestamp and data validity for backup oracles
if (oracleResult.timestamp > block.timestamp - oracleStalenessThreshold && oracleResult.data != 0) {
outputData[i] = oracleResult.data;
foundValidData = true;
break;
}
unchecked { ++j; }
}
if (!foundValidData) {
revert("No valid oracle data available");
}
}
unchecked { ++i; }
}
// ... rest of the code ...
}
Updates

Lead Judging Commences

n0kto Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement
Assigned finding tags:

invalid_getData_negative_or_zero_price

Multihop will call ChainlinkOracle and the check is in it: `require(data > 0, "INVLDDATA");` MultiHop is just here to combine Chainlinks feed when there is no direct USD price feed for a token.

Support

FAQs

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