Price Oracles

Overview

JAMM DEX has a built-in price oracle feature based on Time-Weighted Average Price (TWAP). Each liquidity pool automatically maintains cumulative price data, providing reliable and manipulation-resistant price information for external contracts and applications. This oracle system is low-cost and serves as a crucial piece of infrastructure for the DeFi ecosystem.

Price Accumulation Mechanism

Price Accumulation Variables

Each JAMMPair contract maintains the following price-related state variables:

// from JAMMPair.sol
uint public price0CumulativeLast;  // token0 anainst token1 cumulative price
uint public price1CumulativeLast;  // token1 anainst token0 cumulative price
uint32 private blockTimestampLast; // timestamp of the last block that had a transaction

Price Update Logic

Price accumulation occurs automatically during the first transaction of each block, implemented in the _update function:

// from JAMMPair.sol
function _update(
    uint balance0,
    uint balance1,
    uint112 _reserve0,
    uint112 _reserve1
) private {
    uint32 blockTimestamp = uint32(block.timestamp % 2**32);
    uint32 timeElapsed = blockTimestamp - blockTimestampLast;
    if (timeElapsed > 0 && _reserve0 != 0 && _reserve1 != 0) {
        // * never overflows, and + overflow is desired
        price0CumulativeLast += uint(UQ112x112.encode(_reserve1).uqdiv(_reserve0)) * timeElapsed;
        price1CumulativeLast += uint(UQ112x112.encode(_reserve0).uqdiv(_reserve1)) * timeElapsed;
    }
    reserve0 = uint112(balance0);
    reserve1 = uint112(balance1);
    blockTimestampLast = blockTimestamp;
    emit Sync(reserve0, reserve1);
}

Fixed-Point Arithmetic

Price calculations use the UQ112x112 fixed-point number format to ensure high precision:

// from UQ112x112.sol
library UQ112x112 {
    uint224 constant Q112 = 2**112;

    // encode a uint112 as a UQ112x112
    function encode(uint112 y) internal pure returns (uint224 z) {
        z = uint224(y) * Q112; // never overflows
    }

    // divide a UQ112x112 by a uint112, returning a UQ112x112
    function uqdiv(uint224 x, uint112 y) internal pure returns (uint224 z) {
        z = x / uint224(y);
    }
}

UQ112x112 Format:

  • 112 bits for the integer part + 112 bits for the fractional part.

  • Provides high-precision price calculations, avoiding the precision issues of floating-point arithmetic.

TWAP Calculation Principle

Time-Weighted Average Price

A TWAP is calculated by reading the cumulative price at two different points in time:

TWAP = (Cumulative Price at End - Cumulative Price at Start) / (Timestamp at End - Timestamp at Start)

Cumulative Price Formula

The cumulative price is derived by multiplying the current price by the time it has been active at that price, and then summing these values over time:

Cumulative Price_t = Cumulative Price_(t-1) + Price_t * Time Interval_t

Manipulation Resistance

The TWAP oracle is inherently resistant to manipulation:

  1. Time Weighting: An attacker would need to manipulate the price over a sustained period to significantly affect the TWAP, which is extremely costly.

  2. Automatic Recovery: Arbitrageurs will quickly spot and correct price deviations, restoring the fair market price.

How to Use the Oracle

Basic Queries

External contracts can query the cumulative price through the IJAMMPair interface:

interface IJAMMPair {
    function price0CumulativeLast() external view returns (uint);
    function price1CumulativeLast() external view returns (uint);
    function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
}

TWAP Calculation Example

Here is a simplified example contract for calculating a TWAP:

contract TWAPOracleExample {
    struct Observation {
        uint timestamp;
        uint price0Cumulative;
    }
    
    mapping(address => Observation) public observations;
    
    // record a price observation
    function update(address pair) external {
        IJAMMPair pairContract = IJAMMPair(pair);
        (, , uint32 blockTimestampLast) = pairContract.getReserves();
        observations[pair] = Observation({
            timestamp: blockTimestampLast,
            price0Cumulative: pairContract.price0CumulativeLast()
        });
    }
    
    // calculate the TWAP over a given period
    function getTWAP(address pair, uint period) external view returns (uint price0) {
        Observation memory obs = observations[pair];
        IJAMMPair pairContract = IJAMMPair(pair);
        (, , uint32 blockTimestampLast) = pairContract.getReserves();
        
        uint timeElapsed = blockTimestampLast - obs.timestamp;
        require(timeElapsed >= period, "Period not yet elapsed");
        
        uint price0CumulativeNow = pairContract.price0CumulativeLast();
        price0 = (price0CumulativeNow - obs.price0Cumulative) / timeElapsed;
    }
}

Advantages of the Oracle

  • Decentralized: Relies entirely on on-chain transaction data, with no need for external price feeds.

  • Manipulation-Resistant: The time-weighting mechanism makes price manipulation extremely expensive.

  • Low Cost: Price updates are automatic, and queries are inexpensive.

  • Real-Time: Price data is updated with transactions in each block.

Usage Considerations

  • Liquidity Requirement: The reliability of the oracle depends on sufficient liquidity. Low-liquidity pools are more susceptible to price manipulation.

  • Time Window Selection: The TWAP time window must balance timeliness and manipulation resistance. Shorter windows are more responsive, while longer windows are more stable.

  • Cold Start Problem: Newly created pairs need time to accumulate sufficient price data.

Summary

The JAMM DEX price oracle system has the following features:

  1. Automated: Price data is maintained automatically without manual intervention.

  2. Reliable: Based on actual transaction data, reflecting true market conditions.

  3. Economical: No extra costs for queries.

  4. Secure: Manipulation-resistant design, suitable for DeFi applications.

This oracle system provides essential price infrastructure for the JAMM DEX ecosystem, supporting the development of various DeFi applications.