This document describes the Time-Weighted Average Price (TWAP) oracle implementation for the TradeFlow Core AMM pool. The TWAP oracle provides on-chain price feeds that are resistant to flash loan manipulation by tracking cumulative prices over time.
Flash loan attackers can manipulate pool prices within a single ledger by executing large trades and reversing them in the same transaction. A TWAP oracle prevents this by:
- Recording cumulative prices over time periods
- Making manipulation economically infeasible (requires sustained price pressure)
- Providing reliable price feeds for other DeFi protocols
The following state variables have been added to the PoolState struct:
pub price_0_cumulative_last: u128, // Cumulative price for token_0
pub price_1_cumulative_last: u128, // Cumulative price for token_1
pub block_timestamp_last: u32, // Last update timestampprice_0_cumulative_last: Cumulative price of token_a in terms of token_bprice_1_cumulative_last: Cumulative price of token_b in terms of token_ablock_timestamp_last: Timestamp of the last oracle update
Calculates the time elapsed since the last oracle update.
Returns: Time elapsed in seconds
Edge Cases:
- Returns 0 if current timestamp ≤ last timestamp
- Prevents negative time calculations
Updates the TWAP oracle with current pool prices. This function should be called after any swap operation.
Logic:
- Skip update if pool has no reserves
- Calculate time elapsed since last update
- Skip update if no time has passed
- Calculate current prices (scaled to 18 decimals for precision)
- Update cumulative prices:
cumulative += price * time_elapsed - Update last timestamp to current time
- Emit debug events for monitoring
Price Calculation:
// Price of token_a in terms of token_b
price_a = (reserve_b * 1e18) / reserve_a
// Price of token_b in terms of token_a
price_b = (reserve_a * 1e18) / reserve_bReturns the current TWAP oracle state.
Returns: Tuple of (price_0_cumulative_last, price_1_cumulative_last, block_timestamp_last)
The TWAP oracle tracks cumulative prices using the formula:
cumulative_price += current_price * time_elapsed
Where:
current_priceis the instantaneous pool price (scaled to 18 decimals)time_elapsedis the time since the last update (in seconds)
To calculate the TWAP over a time period [t1, t2]:
twap = (cumulative_price_t2 - cumulative_price_t1) / (t2 - t1)
This provides the average price over the period, weighted by time.
The TWAP oracle is resistant to flash loan manipulation because:
- Time Weighting: Price manipulation requires sustained pressure over multiple ledgers
- Economic Cost: Attackers would need to maintain large positions for extended periods
- Cumulative Nature: Single-transaction price spikes have minimal impact on the cumulative average
- Uses
u128for cumulative prices to prevent overflow over long time periods - Uses saturating arithmetic to prevent panic conditions
- Prices are scaled to 18 decimals to maintain precision
- Skips updates when pool has no reserves
- Returns 0 time elapsed for timestamp edge cases
- Prevents division by zero in price calculations
The update_twap_oracle function should be called after:
- Swap operations: After any token swap completes
- Liquidity operations: After significant liquidity changes
- Periodic updates: On a regular schedule (e.g., every ledger)
Other contracts can consume TWAP prices by:
- Calling
get_twap_oracle_stateto get current cumulative values - Storing snapshots at different time points
- Calculating TWAP over desired time periods
The implementation is designed for gas efficiency:
- Minimal storage updates (only 3 state variables)
- Efficient arithmetic operations
- Conditional updates (skips when no time elapsed)
- Batchable with other pool operations
Potential improvements for future versions:
- Multiple TWAP periods: Track different time windows simultaneously
- Price confidence intervals: Add volatility metrics
- External price feeds: Integration with off-chain oracles
- Update throttling: Rate limiting to prevent spam
When testing the TWAP oracle:
- Time manipulation: Test with different time intervals
- Price volatility: Test with extreme price movements
- Edge cases: Empty pools, zero reserves, timestamp edge cases
- Long-running: Test cumulative behavior over extended periods
- Integration: Test with actual swap operations
// After a swap operation
amm_pool::update_twap_oracle(env);
// Later, to calculate TWAP over a period
let (cumulative_now, _, timestamp_now) = amm_pool::get_twap_oracle_state(env);
let (cumulative_before, _, timestamp_before) = stored_snapshot;
let time_elapsed = timestamp_now - timestamp_before;
let twap_price = (cumulative_now - cumulative_before) / time_elapsed;This implementation provides a solid foundation for reliable on-chain price feeds that can be used throughout the TradeFlow ecosystem.