Skip to content

Instantly share code, notes, and snippets.

@jordaniza
Created September 15, 2025 15:59
Show Gist options
  • Select an option

  • Save jordaniza/c6cb5c5d9570389df40d6028f29f2a42 to your computer and use it in GitHub Desktop.

Select an option

Save jordaniza/c6cb5c5d9570389df40d6028f29f2a42 to your computer and use it in GitHub Desktop.
Aerodrome TWAP Oracle Implementation Guide - Comprehensive analysis of TWAP functionality and price oracle usage

Aerodrome TWAP Oracle Implementation Guide

Overview

Aerodrome implements a Time-Weighted Average Price (TWAP) oracle directly within its liquidity pool contracts, providing a decentralized and manipulation-resistant price feed mechanism. This document provides a comprehensive analysis of the TWAP functionality and how to reliably use Aero pools as price oracles.

TWAP Architecture

Core Data Structures

The TWAP implementation in Aerodrome uses the following key data structure defined in contracts/interfaces/IPool.sol:32-37:

struct Observation {
    uint256 timestamp;
    uint256 reserve0Cumulative;
    uint256 reserve1Cumulative;
}

Each pool maintains:

  • An array of Observation structs stored in contracts/Pool.sol:44
  • Cumulative reserve trackers: reserve0CumulativeLast and reserve1CumulativeLast at contracts/Pool.sol:57-59
  • Fixed observation period of 1800 seconds (30 minutes) defined at contracts/Pool.sol:42

Price Accumulation Mechanism

The price accumulation occurs in the _update function at contracts/Pool.sol:219-236:

  1. Continuous Accumulation: On every block where the pool is interacted with, cumulative reserves are updated:

    reserve0CumulativeLast += _reserve0 * timeElapsed;
    reserve1CumulativeLast += _reserve1 * timeElapsed;
  2. Periodic Snapshots: Every 30 minutes, a new observation is recorded:

    if (timeElapsed > periodSize) {
        observations.push(Observation(blockTimestamp, reserve0CumulativeLast, reserve1CumulativeLast));
    }

This dual mechanism ensures continuous price tracking with periodic checkpoints for efficient historical queries.

TWAP Query Functions

Aerodrome provides multiple functions to query TWAP prices, each suited for different use cases:

1. quote(address tokenIn, uint256 amountIn, uint256 granularity)

Located at contracts/Pool.sol:259-267

  • Purpose: Returns a single TWAP price averaged over multiple observation points
  • Parameters:
    • tokenIn: The input token address
    • amountIn: Amount of input token
    • granularity: Number of observations to average
  • Usage: Best for simple price queries requiring smoothed prices

2. prices(address tokenIn, uint256 amountIn, uint256 points)

Located at contracts/Pool.sol:270-272

  • Purpose: Returns an array of TWAP prices at different observation points
  • Parameters:
    • tokenIn: The input token address
    • amountIn: Amount of input token
    • points: Number of price points to return
  • Usage: Useful for analyzing price trends over time

3. sample(address tokenIn, uint256 amountIn, uint256 points, uint256 window)

Located at contracts/Pool.sol:275-302

  • Purpose: Advanced TWAP query with configurable observation windows
  • Parameters:
    • tokenIn: The input token address
    • amountIn: Amount of input token
    • points: Number of price points to return
    • window: Multiplier for observation intervals (e.g., 2 = 1 hour intervals)
  • Usage: Most flexible option for custom TWAP calculations

4. currentCumulativePrices()

Located at contracts/Pool.sol:239-256

  • Purpose: Returns current cumulative prices with counterfactual calculations
  • Returns: Current cumulative reserves and block timestamp
  • Usage: For protocols needing to calculate custom TWAP intervals

Implementation Example

Based on the test at test/Oracle.t.sol:128-133, here's how to use the TWAP oracle:

// Query TWAP price for USDC -> FRAX conversion
uint256 fraxOut = pool.quote(address(USDC), 1e9, 1); // 1000 USDC with granularity of 1

// Query TWAP price for FRAX -> USDC conversion  
uint256 usdcOut = pool.quote(address(FRAX), 1e21, 1); // 1000 FRAX with granularity of 1

Security Considerations

1. Manipulation Resistance

  • 30-Minute Observation Period: The fixed 30-minute observation window at contracts/Pool.sol:42 provides significant manipulation resistance
  • Cumulative Price Tracking: Continuous accumulation makes spot price manipulation ineffective for TWAP manipulation
  • Multiple Observations: Using higher granularity in quote() increases resistance to short-term manipulation

2. Oracle Freshness

  • Automatic Updates: Prices update on every interaction with the pool via _update() at contracts/Pool.sol:219-236
  • Minimum Observations: Ensure sufficient observations exist before relying on TWAP data
  • Check observationLength(): Use contracts/Pool.sol:114-116 to verify adequate price history

3. Pool Liquidity Requirements

  • Liquidity Depth: TWAP accuracy depends on pool liquidity depth
  • Minimum K Requirement: Stable pools enforce minimum K at contracts/Pool.sol:318 to ensure pricing stability
  • Reserve Validation: Always verify pools have sufficient reserves before using as oracles

Best Practices for Price Oracle Usage

1. Multi-Point Averaging

// Use multiple observation points for better accuracy
uint256 avgPrice = pool.quote(tokenIn, amountIn, 5); // Average over 5 observations

2. Historical Price Validation

// Get multiple price points to detect anomalies
uint256[] memory priceHistory = pool.prices(tokenIn, amountIn, 10);
// Analyze price variance and trends

3. Cross-Pool Validation

For critical applications, validate prices across multiple pools:

  • Compare stable and volatile pool prices
  • Use multiple pool pairs for triangulation
  • Implement sanity checks against expected price ranges

4. Freshness Checks

// Verify recent observations
uint256 obsLength = pool.observationLength();
Observation memory lastObs = pool.lastObservation();
require(block.timestamp - lastObs.timestamp < 3600, "Stale price data");

Differences from Other TWAP Implementations

vs. Uniswap V2

  • Aerodrome stores periodic observations (every 30 min) vs. V2's continuous updates
  • More gas-efficient for oracle queries
  • Built-in multi-point TWAP functions

vs. Uniswap V3

  • Aerodrome uses reserve-based TWAP (like V2) vs. V3's tick-based system
  • Fixed observation periods vs. V3's flexible storage
  • Simpler implementation suitable for both stable and volatile pools

Integration Recommendations

For DeFi Protocols

  1. Lending Protocols: Use quote() with granularity 3-5 for collateral pricing
  2. Derivatives: Use sample() with custom windows for mark price calculations
  3. Liquidations: Implement multiple observation points with sanity checks

For Price Feeds

  1. Regular Updates: Query prices at consistent intervals
  2. Multiple Pools: Aggregate prices from multiple pool sources
  3. Outlier Detection: Implement statistical analysis on price arrays

Error Handling

Always implement proper error handling:

  • Check for zero liquidity pools
  • Validate observation array length
  • Handle pools with insufficient price history
  • Implement fallback oracles for critical systems

Conclusion

Aerodrome's TWAP oracle provides a robust, manipulation-resistant price feed directly integrated into the liquidity pools. By understanding the observation mechanism, utilizing appropriate query functions, and following security best practices, protocols can reliably use Aero pools as decentralized price oracles. The 30-minute observation period strikes a balance between gas efficiency and manipulation resistance, making it suitable for a wide range of DeFi applications.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment