Trustlessly Accessing Uniswap TWAP Oracles with Axiom

Explaining our demo of checkpoint-free Uniswap v3 TWAP oracles via Axiom, supported by a grant from the Uniswap Foundation.

Today we are releasing a new demo of checkpoint-free Uniswap v3 TWAP oracles via Axiom at demo.axiom.xyz. Together with our Uniswap v2 TWAP demo, this work enables users to:

  • Prove the TWAP price of the ETH-USDC Uniswap v2 pool between any two historic blocks without checkpointing.
  • Prove the TWAP price and average liquidity of the ETH-USDC Uniswap v3 0.3% or 0.05% pools between any two historic blocks without turning on caching.

In both cases, Axiom allows developers to use Uniswap TWAP oracles without the need to think ahead by checkpointing or caching. This opens up new ways for downstream applications to use these trustless oracles. To see it for yourself:

  • Try out our live mainnet demos for Uniswap v2 TWAP and Uniswap v3 TWAP. You can enter any two historic block numbers, generate your own proof of the TWAP price of ETH-USDC between them, and verify them in our on-chain contracts!
  • We've open-sourced the ZK circuits, smart contracts, and proving server used to produce this demo. You can check it out at our Github repos for Uniswap v2 TWAP and Uniswap v3 TWAP.

We'd like to thank the Uniswap Foundation for providing a grant to support this work. In the rest of this post, we explain how these demos work under the hood, the additional capabilities they enable for developers, and the security properties of the system.

What are Uniswap TWAP oracles?

Uniswap v2 and v3 pools constantly provide spot prices for their constituent assets, which are adjusted towards true global prices by arbitrageurs. In this way, Uniswap spot prices form decentralized price oracles for the assets in each pool. Unfortunately, such oracles can be manipulated by malicious intra-block trades, making them insecure for downstream usage.

Instead, Uniswap v2 and v3 introduced different versions of Time-Weighted Average Price (TWAP) oracles which return the average price of an asset over a specified period of time. Because they incorporate price information across multiple blocks, TWAP oracles are more manipulation-resistant than spot prices alone. However, because the EVM does not provide access to historic information, enabling computation of TWAPs requires recording additional information in contract storage.

Both Uniswap v2 and v3 do this by paying a small additional gas cost on every trade to keep time-weighted sums of certain statistics in accumulators. The accumulator of a statistic statistic is the sum of

statistic_n * timeElapsed_n

where timeElapsed_n is the block timestamp difference between events n and n + 1 relevant for statistic. Uniswap pools store the following accumulators:

  • For Uniswap v2, the accumulator of reserve1 / reserve0, which is the spot price.
  • For Uniswap v3, the accumulator of tick, which is a logarithmic representation of the spot price.

Reading the value of a Uniswap TWAP oracle between t1 and t2 from a pool requires a developer to plan ahead to read historic values of these accumulators. From the Solidity implementations for v2 and v3, we see that:

  • In Uniswap v2, a developer must checkpoint the accumulator value at t1 in local smart contract storage, and then refer back to the checkpoint at t2 to compute the TWAP price by comparing accumulators. This means the developer must remember to checkpoint at t1 to enable TWAPs over intervals starting at t1.
  • In Uniswap v3, the first trade in every block stores an observation of the accumulator in an expandable ring buffer of size up to 65535. Computing the TWAP between t1 and t2 requires reading observations at t1 and t2 from the buffer. This means developers must plan ahead by prepaying gas to expand the buffer. However, this can be expensive (34 ETH for the maximum buffer size) and the maximum cache size can be exhausted in 9 days.

In both v2 and v3, developers need to plan ahead to ensure that the accumulator values they are referring to are accessible to their contracts, either by explicit checkpointing or by expanding the ring buffer sufficiently to ensure they are cached.

Proving Uniswap TWAP oracle values in ZK

Our demos use Axiom to compute Uniswap v2 and v3 TWAP oracle values between any two historic blocks without developer pre-planning to cache accumulator values.

  • For Uniswap v2, this allows developers to query TWAP oracle values between arbitrary historic blocks without worrying about checkpointing.
  • For Uniswap v3, this eliminates the need to pay gas to expand the ring buffer of accumulator observations or check that historic accumulator values are still cached.

Because all historic queries into Axiom are verified by a ZK proof, this allows developers to access arbitrary historic TWAP values at any time without additional trust assumptions.

To compute Uniswap TWAPs with Axiom, we use the fact that Uniswap v2 and v3 TWAP oracle values between blocks t1 and t2 depend only on the value of accumulators at t1 and t2. We can therefore divide the query into Axiom into two pieces, data access and computation:

  • Historic Data Reads: Read historic values of the accumulators at t1 and t2 using verified historic reads to Ethereum storage via Axiom.
    • For Uniswap v2, this consists of reserve0, reserve1, blockTimestampLast, price1CumulativeLast, and blockTimestamp.
    • For Uniswap v3, this consists of observations[0].tickCumulative and blockTimestamp.
  • Verified Compute: Verify the Axiom-provided ZK proof for the historic accumulator values on-chain, and compute the TWAP value as a function of these historic storage reads. This can be done in ZK, or in the verifier smart contract. To illustrate the different methods, we have done the computation in ZK for Uniswap v2 and in the smart contract for Uniswap v3.

The end results of these queries are Uniswap v2 and v3 TWAP values verified by ZK proofs on-chain. To access these, developers can read from the verified TWAP oracle values in our UniswapV2Twap and UniswapV3Oracle smart contracts deployed on-chain.

Security analysis

The security of the Uniswap TWAP oracles we provide can be divided into two pieces, the cryptographic security of the TWAP values and the economic security of TWAP itself. For the former, though Axiom uses off-chain compute to fetch historic values of Uniswap TWAP oracles, we prove validity of all values in ZK on-chain, meaning we impose no additional trust assumptions beyond the correct operation of our ZK proofs and smart contracts. We encourage you to check these for yourself by verifying:

We have open-sourced every piece of our toolchain in order to allow and encourage independent analysis and reproduction of precisely what Axiom is proving in ZK. Don't just trust us, check out the code and verify for yourself!

Beyond the validity of the Uniswap TWAP values themselves, downstream users of our historic TWAP oracles should be aware of their economic security properties. Because these oracles are built on top of trustless systems, there are manipulation risks if malicious validators are able to propose multiple consecutive Ethereum blocks. Our system inherits the same economic security properties as the original Uniswap v2 and v3 TWAP oracles themselves, but because we can read TWAP values over any historic range of blocks, we may be able to choose this range of blocks on the basis of some difficult-to-manipulate randomness to make TWAP manipulation more economically costly.

We hope that these new ZK-powered capabilities can make trust-minimized Uniswap TWAP oracles more economically secure, and we leave a detailed analysis of any improved guarantees to future work.

Warning: Our Uniswap v2 and v3 TWAP demos are unaudited and intended only to illustrate use cases for Axiom. They should not be relied upon for production usage or value transfer.

What's next?

With this Uniswap TWAP oracle demo release, we are excited to enable experimentation with more permissionless and trust-minimized oracles. We hope to work with the community to understand the manipulation-resistance of Axiom-enabled historic oracle access and to help developers looking to integrate these oracles. More broadly, at Axiom we are working towards a production release, and we will release more information in the coming weeks about how to use Axiom to access historic on-chain data in your application.

In the meantime, if you are a developer and would like to build on Axiom, we are looking for early integration partners! To discuss possible applications or learn more:

If you'd like to join us in empowering smart contract developers with ZK:

  • We are hiring developers and developer advocates to join us in tackling the hard technical problems necessary to develop, scale, and optimize Axiom. Check out our jobs page here or reach out directly at jobs@intrinsictech.xyz.
  • If you want to get straight to the code, check out our Github repos. We are open to extensions or contributions!

To stay in touch about Axiom, join our community on Twitter, Telegram, or Discord.