Oracle Manipulation
Oracle Manipulation
Price oracle manipulation is a critical vulnerability in DeFi protocols that rely on external price data for critical operations.
What are Oracles?
Oracles provide off-chain data to smart contracts. Price oracles are especially critical for:
- Lending protocols (collateralization ratios)
- DEXs (price discovery)
- Derivatives (settlement prices)
- Liquidation mechanisms
Manipulation Risks
Flash Loan Price Manipulation
Attackers can:
1. Take a flash loan
2. Manipulate the price on a DEX
3. Use the manipulated price in your protocol
4. Profit from the difference
5. Repay the flash loan
Example Vulnerable Code
// Vulnerable: Using spot price from single DEX
function getPrice() public view returns (uint) {
return uniswapPair.getReserves(); // Can be manipulated in single tx
}Secure Oracle Patterns
1. Chainlink Price Feeds
import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
contract SecureOracle {
AggregatorV3Interface internal priceFeed;
constructor() {
priceFeed = AggregatorV3Interface(
0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419 // ETH/USD
);
}
function getLatestPrice() public view returns (int) {
(
uint80 roundID,
int price,
uint startedAt,
uint timeStamp,
uint80 answeredInRound
) = priceFeed.latestRoundData();
require(timeStamp > 0, "Round not complete");
require(answeredInRound >= roundID, "Stale price");
return price;
}
}2. Time-Weighted Average Price (TWAP)
// Using Uniswap V3 TWAP
function getTWAP(uint32 twapInterval) public view returns (uint) {
uint32[] memory secondsAgos = new uint32[](2);
secondsAgos[0] = twapInterval;
secondsAgos[1] = 0;
(int56[] memory tickCumulatives, ) = pool.observe(secondsAgos);
int56 tickCumulativesDelta = tickCumulatives[1] - tickCumulatives[0];
int24 arithmeticMeanTick = int24(tickCumulativesDelta / int56(uint56(twapInterval)));
return getQuoteAtTick(arithmeticMeanTick, amountIn, baseToken, quoteToken);
}3. Multiple Oracle Sources
function getPrice() public view returns (uint) {
uint chainlinkPrice = getChainlinkPrice();
uint uniswapTWAP = getUniswapTWAP();
uint bandPrice = getBandPrice();
// Use median or require consistency
require(
abs(chainlinkPrice - uniswapTWAP) < chainlinkPrice / 20,
"Price deviation too high"
);
return (chainlinkPrice + uniswapTWAP) / 2;
}Best Practices
1. Never use spot prices from a single DEX for critical operations
2. Use established oracle solutions (Chainlink, Band Protocol)
3. Implement TWAP for DEX-based prices
4. Use multiple oracle sources and verify consistency
5. Add circuit breakers for abnormal price movements
6. Implement price deviation checks
7. Add time delays for large operations
Circuit Breaker Implementation
uint public constant MAX_PRICE_CHANGE = 10; // 10% max change
function checkPrice(uint newPrice, uint oldPrice) internal pure {
uint percentChange = abs(newPrice - oldPrice) * 100 / oldPrice;
require(percentChange <= MAX_PRICE_CHANGE, "Price change too large");
}Real-World Examples
- **Harvest Finance** (~$34M): Flash loan attack manipulating Curve pool prices
- **Cream Finance** (~$130M): Price oracle manipulation
- **Venus Protocol** (~$11M): Oracle price manipulation
References
- [Chainlink Documentation](https://docs.chain.link/)
- [Uniswap V3 Oracle](https://docs.uniswap.org/concepts/protocol/oracle)