TWAP (Time-Weighted Average Price)

A price calculation method that averages asset prices over a time period to resist short-term manipulation.

Time-Weighted Average Price (TWAP) is a pricing mechanism that calculates the average price of an asset over a specified time period, providing manipulation-resistant price data for DeFi protocols. Unlike spot prices that reflect instantaneous market conditions, TWAP smooths out short-term volatility and makes flash loan attacks economically impractical. TWAP oracles are commonly built on top of DEX liquidity pools, most notably Uniswap, and serve as on-chain price references for lending protocols, derivatives, and other DeFi applications.

Why TWAP Matters

Spot prices can be manipulated within a single transaction:

1Block N:
21. Attacker takes flash loan (1M USDC)
32. Swaps USDC → ETH on Uniswap (moves price up 20%)
43. Uses inflated ETH price in vulnerable protocol
54. Profits from price difference
65. Swaps back, repays flash loan

TWAP defeats this by averaging prices over time—manipulating a 30-minute TWAP requires holding the manipulated price for 30 minutes, which is economically prohibitive.

How TWAP Works

TWAP accumulates price observations over time:

1TWAP = (Price₁ × Time₁ + Price₂ × Time₂ + ... + Priceₙ × Timeₙ) / Total Time

In Uniswap V2/V3, the contract tracks cumulative price-time values:

1// Uniswap V2 accumulator (simplified)
2price0CumulativeLast += price0 * timeElapsed;
3price1CumulativeLast += price1 * timeElapsed;
4
5// To get TWAP between two observations:
6twap = (priceCumulativeEnd - priceCumulativeStart) / (timeEnd - timeStart);

Uniswap V3 TWAP

Uniswap V3 stores observations in a circular buffer:

1import "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol";
2import "@uniswap/v3-periphery/contracts/libraries/OracleLibrary.sol";
3
4function getTWAP(
5 address pool,
6 uint32 period
7) external view returns (uint256 price) {
8 (int24 arithmeticMeanTick,) = OracleLibrary.consult(pool, period);
9
10 price = OracleLibrary.getQuoteAtTick(
11 arithmeticMeanTick,
12 1e18, // Base amount
13 token0, // Base token
14 token1 // Quote token
15 );
16}

TWAP Period Selection

PeriodManipulation ResistanceResponsiveness
1 minuteLowHigh
10 minutesMediumMedium
30 minutesHighLow
1 hour+Very HighVery Low

Trade-off: Longer periods are more secure but respond slower to legitimate price movements.

TWAP vs Chainlink

AspectTWAPChainlink
Data sourceOn-chain DEXOff-chain APIs
DecentralizationFully on-chainNetwork of nodes
Manipulation costTime-basedEconomic (stake)
FreshnessConfigurableHeartbeat-based
CoverageDEX-listed pairs onlyCurated feeds
Gas costLowerHigher

Many protocols use both: Chainlink as primary, TWAP as fallback or sanity check.

Security Considerations

Low Liquidity Pools

TWAP from low-liquidity pools is easier to manipulate:

1// Check pool has sufficient liquidity
2require(pool.liquidity() > MIN_LIQUIDITY, "Insufficient liquidity");

Stale Observations

Pools with low trading activity may have outdated observations:

1// Verify observation is recent
2(,, uint16 observationIndex, uint16 observationCardinality,,,) = pool.slot0();
3(uint32 observationTimestamp,,,) = pool.observations(observationIndex);
4require(block.timestamp - observationTimestamp < MAX_STALENESS, "Stale TWAP");

Price Deviation Attacks

Gradual price manipulation over the TWAP window:

1// Compare TWAP to spot price
2uint256 spotPrice = getSpotPrice();
3uint256 twapPrice = getTWAP(30 minutes);
4uint256 deviation = calculateDeviation(spotPrice, twapPrice);
5require(deviation < MAX_DEVIATION, "Price deviation too high");

Implementation Patterns

Dual Oracle System

1function getPrice() external view returns (uint256) {
2 uint256 chainlinkPrice = getChainlinkPrice();
3 uint256 twapPrice = getTWAP();
4
5 // Require prices within 5% of each other
6 uint256 deviation = diff(chainlinkPrice, twapPrice) * 10000 / chainlinkPrice;
7 require(deviation < 500, "Oracle deviation");
8
9 // Return average or prefer one source
10 return (chainlinkPrice + twapPrice) / 2;
11}

Circuit Breaker

1uint256 public lastPrice;
2
3function getSecurePrice() external returns (uint256 price) {
4 price = getTWAP();
5
6 if (lastPrice > 0) {
7 uint256 change = diff(price, lastPrice) * 10000 / lastPrice;
8 require(change < MAX_PRICE_CHANGE, "Circuit breaker triggered");
9 }
10
11 lastPrice = price;
12}

Audit Checklist

When auditing TWAP implementations:

  • Sufficient observation period for the use case
  • Pool liquidity requirements enforced
  • Staleness checks on observations
  • Deviation checks against other price sources
  • Proper decimal handling
  • Edge cases (pool just created, no observations)

TWAP oracles provide a trust-minimized, manipulation-resistant price source for DeFi protocols, but require careful implementation to avoid edge cases and ensure sufficient security for the specific use case.

Need expert guidance on TWAP (Time-Weighted Average Price)?

Our team at Zealynx has deep expertise in blockchain security and DeFi protocols. Whether you need an audit or consultation, we're here to help.

Get a Quote

oog
zealynx

Subscribe to Our Newsletter

Stay updated with our latest security insights and blog posts

© 2024 Zealynx