QuantAMM

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

Single Oracle Dependency Without Cross-Validation

Description

The ChainlinkOracle contract's reliance on a single price feed without cross-validation creates a fundamental security risk in QuantAMM's TFMM architecture. The _getData() function's exclusive dependence on one Chainlink feed introduces a critical single point of failure in the protocol's automated portfolio management system. While MultiHopOracle does combine multiple oracles, it's still using single sources for each hop. A compromised oracle at any hop affects the entire derived price.

For QuantAMM's TFMM mechanism, this architectural weakness is particularly severe because the temporal function continuously calculates weight adjustments based on oracle inputs. Without cross-validation against multiple price sources, the entire rebalancing system becomes vulnerable to manipulation or failure of the single oracle. The automated nature of these adjustments means that corrupted price data from the sole oracle would immediately translate into incorrect portfolio allocations.

This vulnerability becomes especially critical in QuantAMM's composite pool structure, where a compromised oracle affects not just individual strategies but entire networks of interconnected pools. The temporal function's reliance on accurate price data means that oracle manipulation could cause systematic strategy drift across multiple portfolios simultaneously, potentially leading to large-scale misallocation of assets under management.

Proof of Concept

https://github.com/Cyfrin/2024-12-quantamm/blob/main/pkg/pool-quantamm/contracts/ChainlinkOracle.sol#L27

contract ChainlinkOracle is OracleWrapper {
AggregatorV3Interface internal immutable priceFeed;
function _getData() internal view override returns (int216, uint40) {
(, int data, , uint timestamp, ) = priceFeed.latestRoundData();
require(data > 0, "INVLDDATA");
data = data * int(10 ** normalizationFactor);
return (int216(data), uint40(timestamp));
}
}

Recommended mitigation steps

Implement multi-oracle cross-validation with weighted consensus:

contract ChainlinkOracle is OracleWrapper {
struct OracleSource {
AggregatorV3Interface feed;
uint256 weight;
uint8 decimals;
}
OracleSource[] public oracleSources;
uint256 public constant MINIMUM_CONSENSUS_WEIGHT = 7000; // 70%
uint256 public constant MAXIMUM_DEVIATION = 300; // 3%
constructor(
address[] memory _feeds,
uint256[] memory _weights
) {
require(_feeds.length >= 3, "MIN_ORACLES");
require(_feeds.length == _weights.length, "LENGTH_MISMATCH");
uint256 totalWeight = 0;
for(uint i = 0; i < _feeds.length; i++) {
AggregatorV3Interface feed = AggregatorV3Interface(_feeds[i]);
oracleSources.push(OracleSource({
feed: feed,
weight: _weights[i],
decimals: feed.decimals()
}));
totalWeight += _weights[i];
}
require(totalWeight == 10000, "INVALID_WEIGHTS"); // Weights must sum to 100%
}
function _validatePrices() internal view returns (int256) {
uint256 consensusWeight = 0;
int256 primaryPrice = _getPrimaryPrice();
for(uint i = 1; i < oracleSources.length; i++) {
(bool isValid, uint256 weight) = _validateOraclePrice(
primaryPrice,
oracleSources[i]
);
if(isValid) {
consensusWeight += weight;
}
}
require(consensusWeight >= MINIMUM_CONSENSUS_WEIGHT, "INSUFFICIENT_CONSENSUS");
return primaryPrice;
}
function _validateOraclePrice(
int256 primaryPrice,
OracleSource memory source
) internal view returns (bool, uint256) {
(, int256 price, , uint256 updatedAt, ) = source.feed.latestRoundData();
// Normalize to 18 decimals
price = price * int256(10 ** (18 - source.decimals));
int256 deviation = ((price - primaryPrice) * 10000) / primaryPrice;
if(deviation < 0) deviation = -deviation;
return (deviation <= int256(MAXIMUM_DEVIATION), source.weight);
}
function _getData() internal view override returns (int216, uint40) {
int256 validatedPrice = _validatePrices();
return (int216(validatedPrice), uint40(block.timestamp));
}
}
Updates

Lead Judging Commences

n0kto Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

Informational or Gas / Admin is trusted / Pool creation is trusted / User mistake / Suppositions

Please read the CodeHawks documentation to know which submissions are valid. If you disagree, provide a coded PoC and explain the real likelyhood and the detailed impact on the mainnet without any supposition (if, it could, etc) to prove your point.

Support

FAQs

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

Give us feedback!