QuantAMM

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

Precision loss in `_getData` function of `MultiHopOracle` Contract

Summary

The _getData function in the MultiHopOracle contract combines multiple oracle price feeds to compute a price for tokens that do not have direct price feeds. However, the repeated division or multiplication during each step of the oracle path causes precision loss, which can significantly impact accuracy.
This issue is exacerbated when dealing with very large or very small intermediate oracle results (oracleRes), especially when the invert flag is true.
Calculating the numerator and denominator separately and performing the division only at the end is a safer approach to mitigate precision loss.

https://github.com/Cyfrin/2024-12-quantamm/blob/a775db4273eb36e7b4536c5b60207c9f17541b92/pkg/pool-quantamm/contracts/MultiHopOracle.sol#L45-L49

Vulnerability Details

  1. Precision Loss in Repeated Operations:

    • The repeated division by large numbers (oracleRes) when invert is true causes the accumulated result to lose precision due to rounding errors inherent in integer division.

    • Conversely, repeated multiplication by small numbers (oracleRes) amplifies rounding errors.

  2. Accumulation of Errors:

    • The error grows with each additional oracle hop, leading to potentially inaccurate results, especially with longer oracle paths.

  3. Improper Use of Fixed-Point Arithmetic:

    • Using division or multiplication directly in each loop iteration without maintaining precision compromises the accuracy of the final result.

Impact

  1. Degraded Accuracy:

    • Results in inaccurate token prices, which can lead to incorrect decision-making by protocols or users relying on this data.

    • Potentially allows arbitrage opportunities or erroneous behavior in systems utilizing this oracle.

  2. Increased Risk in Edge Cases:

    • High variance in oracleRes values (very large or very small) may lead to drastic deviations in computed prices.

Tools Used

Recommendations

Separate Numerator and Denominator Calculation:

  • Maintain two variables for the numerator and denominator during the loop.

  • Compute the final result by dividing the total numerator by the total denominator at the end of the loop.

Proposed Implementation:

function _getData() internal view override returns (int216 data, uint40 timestamp) {
HopConfig memory firstOracle = oracles[0];
(data, timestamp) = firstOracle.oracle.getData();
uint256 numerator = uint256(data);
uint256 denominator = 1; // Initial denominator set to 1e18 for scaling.
if (firstOracle.invert) {
numerator = 10**36; // Initial numerator for inverse calculation
denominator = uint256(data); // Replace denominator with the first oracle's result
}
uint256 oracleLength = oracles.length;
for (uint256 i = 1; i < oracleLength; ) {
HopConfig memory oracleConfig = oracles[i];
(int216 oracleRes, uint40 oracleTimestamp) = oracleConfig.oracle.getData();
if (oracleTimestamp < timestamp) {
timestamp = oracleTimestamp;
}
uint256 oracleResAbs = uint256(oracleRes); // Convert to uint for calculations
if (oracleConfig.invert) {
numerator *= 10**18; // Scale numerator
denominator *= oracleResAbs; // Multiply denominator
} else {
numerator *= oracleResAbs; // Multiply numerator
denominator *= 10**18; // Scale denominator
}
unchecked {
++i;
}
}
// Final division at the end to avoid intermediate precision loss
data = int216(numerator / denominator);
}
Updates

Lead Judging Commences

n0kto Lead Judge 4 months ago
Submission Judgement Published
Validated
Assigned finding tags:

finding_MultiHopOracle_getData_invert_precision_loss_low_decimals

Likelihood: Informational/Very Low, admin should use a price feed with 18 decimals and this feed should compare a assets with a very small value and an asset with a biggest amount to have the smallest price possible. Admin wouldn't do that intentionally, but one token could collapse, and with multiple hop, it increases a bit the probability. Impact: High, complete loss of precision. Probability near 0 but not 0: deserve a Low

Support

FAQs

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