TWAP oracle extension to mitigate the risk of oracle manipulation under PoS

Oracle manipulation under POS

The transition of Ethereum mainnet from PoW to PoS resulted in significant changes in block building. One of the differences under PoS is that validators/pools with a large market share have a high probability of producing consecutive blocks, statistical analysis by Alvaro Revuelta. Consecutive blocks by the same validator/pool increases the risk of TWAP (Time-Weighted Average Price) oracle manipulation, research by Uniswap.

Because of the increased risk of TWAP oracle manipulation the current design seems insufficient for use in DeFi (Decentralized Finance). For example Euler changed the oracle for multiple markets from TWAP to ChainLink, Proposal 30. To increase the safety of the TWAP oracle under PoS I propose an oracle extension that acts as a guard to only allow price updates for a predefined max delta between price observations. The guard needs to be gas efficient.

Oracle extension guard design

The guard extension defines

  • OBSERVATIONS number of observations
  • SKIP step between two observations
  • MAX_OUTLIERS max number of observations out of range
  • MAX_SINGLE_TICK_DELTA max delta between observations
  • MAX_TOTAL_TICK_DELTA max delta in array with most observations

The guard checks a set number of OBSERVATIONS within the range OBSERVATIONS * SKIP on every i * SKIP slot. For every observation the delta with previous observations is calculated. When delta > MAX_SINGLE_TICK_DELTA it stores the observation in a new array. For the array with most observations the guard checks array length > OBSERVATIONS - MAX_OUTLIERS and total_array_delta < MAX_TOTAL_TICK_DELTA. The guard halts the price update if one of these is not met.

Check the GitHub repository for the POC implementation.

Safety and liveness

In this design the guard halts price updates when outside of safety assumptions, favoring safety over liveness. To manipulate the price more than the predefined delta a manipulator needs manipulated_observations > OBSERVATIONS - MAX_OUTLIERS and to halt price updates the manipulator needs manipulated_observations > MAX_OUTLIERS.


The manipulated observations need to be within the observed range OBSERVATIONS * SKIP. The observed range used a SKIP variable to increase the range without needing to check every observation. A large observed range can be used so validators do not know all the blocks they will propose. More OBSERVATIONS increases safety and more MAX_OUTLIERS increases liveness at the cost of safety (OBSERVATIONS).


The example uses the following boundaries. For simplicity low boundaries are used.

SKIP			=  2

In this example the guard makes 8 observations in a range of 16. The manipulator needs 4 observations to adjust the price outside of the predefined max delta. For these observations a minimal of 1.5 * OBSERVATIONS - MAX_OUTLIERS = 1.5 * 4 = 6 blocks need to be controlled. Notice that an attacker can manipulate the guard observation in block 3 by manipulating the observation in block 2 or block 3 (in the example block 2). This is because prices in the TWAP oracle are stored as a cumulative of all previous price observations and the guard calculates the price based on the difference between two cumulative observations.


The guard is an extension that checks for outliers. This check has to be efficient with gas for it to be used in DeFi. The current POC estimates the example above at ~100k gas. More observations result in more gas usage, for OBSERVATIONS=28, MAX_OUTLIERS=4 and SKIP=4 estimated at ~200k gas. Check the GitHub repository to run gas estimates with adjusted settings.


The oracle guard uses the current TWAP oracle observations allowing it to be an extension without the need for lower level adjustments. It favors safety over liveness by only updating the price if there are a sufficient number of observed prices within a preset delta. These boundaries can be custom set by a protocol for specific needs between safety, liveness and gas usage.