QuantAMM

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

Missing Liquidity Validation in QuantAMM Oracle Permits Price Manipulation in Low-Volume Markets

Description

The ChainlinkOracle contract's failure to validate market liquidity creates a critical weakness in QuantAMM's TFMM execution framework. The implementation accepts and applies price data without considering market depth, leaving the automated portfolio management system vulnerable to manipulation in low liquidity conditions.

This vulnerability directly impacts QuantAMM's core functionality because the TFMM mechanism relies on price data to calculate weight adjustments. In low liquidity scenarios, these calculations become unreliable as small trades can cause significant price movements, leading to incorrect weight targets. The temporal function's continuous operation means that these distorted prices directly feed into portfolio rebalancing decisions.

The risk is particularly acute in QuantAMM's composite pool architecture, where liquidity-driven price distortions in one pool can propagate through the system, affecting multiple linked strategies. BTF valuations and rebalancing operations become unreliable during low liquidity periods, potentially triggering unnecessary or harmful portfolio adjustments based on manipulated price data.

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 liquidity validation:

contract ChainlinkOracle is OracleWrapper {
struct LiquidityConfig {
uint256 minimumLiquidity;
uint256 targetLiquidity;
address[] liquidityPools;
}
mapping(address => LiquidityConfig) public liquidityConfigs;
function _validateLiquidity(address token) internal view {
LiquidityConfig memory config = liquidityConfigs[token];
uint256 totalLiquidity;
for(uint i = 0; i < config.liquidityPools.length; i++) {
ILiquidityPool pool = ILiquidityPool(config.liquidityPools[i]);
totalLiquidity += pool.getLiquidity(token);
}
if (totalLiquidity < config.minimumLiquidity) {
revert InsufficientLiquidity(token, totalLiquidity, config.minimumLiquidity);
}
// Apply liquidity factor to price impact tolerance
uint256 liquidityFactor = (totalLiquidity * BASIS_POINTS) / config.targetLiquidity;
maxPriceDeviation = (BASE_DEVIATION * liquidityFactor) / BASIS_POINTS;
}
function _getData() internal view override returns (int216, uint40) {
_validateLiquidity(token);
(, int256 data, , uint256 updatedAt, ) = priceFeed.latestRoundData();
int256 normalizedPrice = data * int256(10 ** normalizationFactor);
return (int216(normalizedPrice), uint40(updatedAt));
}
function setLiquidityConfig(
address token,
uint256 _minimumLiquidity,
uint256 _targetLiquidity,
address[] calldata _liquidityPools
) external onlyOwner {
liquidityConfigs[token] = LiquidityConfig({
minimumLiquidity: _minimumLiquidity,
targetLiquidity: _targetLiquidity,
liquidityPools: _liquidityPools
});
}
}

This mitigation ensures price updates are validated against market depth conditions while maintaining flexibility for different assets and market conditions. The liquidity validation should be calibrated based on historical trading volumes and market-specific characteristics.

Updates

Lead Judging Commences

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

Support

FAQs

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