QuantAMM

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

Missing Oracle Price Staleness Validation in QuantAMM's TFMM Mechanism Enables Trading on Outdated Prices

Summary

The ChainlinkOracle contract's _getData() function lacks staleness validation when fetching price feed data for QuantAMM's Temporal Function Market Making (TFMM) operations. The function accepts price data without validating the time difference between block.timestamp and the oracle's updatedAt timestamp.

The severity of this vulnerability is magnified by QuantAMM's automated portfolio management architecture. The TFMM mechanism relies on continuous and accurate price data to perform weight updates and portfolio rebalancing. When stale prices are accepted, the entire rebalancing mechanism becomes compromised, leading to systematically incorrect portfolio adjustments.

In QuantAMM's composite pool structure, price staleness can create a contagion effect where incorrect pricing in one pool propagates through dependent pools, amplifying the impact across the protocol. Strategy execution becomes particularly vulnerable as automated trading decisions are made based on potentially outdated price information, undermining the protocol's core value proposition of quantitative asset management.

During periods of market volatility, this vulnerability becomes especially dangerous as the gap between stale and actual prices widens, potentially triggering incorrect rebalancing actions that compound losses across managed portfolios. The BTF unit value calculations, critical for investor deposits and withdrawals, would also be compromised, affecting the protocol's ability to maintain accurate asset valuations.

Proof of Concept

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

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));
}

Recommended mitigation steps

Implement comprehensive staleness protection:

contract ChainlinkOracle is OracleWrapper {
uint256 public immutable maxPriceDelay;
mapping(address => uint256) public assetDelays; // Asset-specific delays
constructor(address _chainlinkFeed, uint256 _maxDelay) {
require(_maxDelay > 0, "INVALID_DELAY");
maxPriceDelay = _maxDelay;
}
function _getData() internal view override returns (int216, uint40) {
(, int data, , uint updatedAt, ) = priceFeed.latestRoundData();
// Asset-specific staleness check
uint256 maxDelay = assetDelays[address(priceFeed)] != 0 ?
assetDelays[address(priceFeed)] : maxPriceDelay;
if (block.timestamp - updatedAt > maxDelay) {
revert StalePrice(updatedAt, maxDelay);
}
// Pause strategy execution if price is stale
if (isStrategyActive && block.timestamp - updatedAt > warningThreshold) {
pauseStrategyExecution();
}
require(data > 0, "INVLDDATA");
data = data * int(10 ** normalizationFactor);
return (int216(data), uint40(updatedAt));
}
}
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.