# Using the new Uniswap v2 as oracle in your contracts

Author: Markus Waas

Published: 2021-01-16T21:02:45.000Z

Updated: 2026-09-13T14:25:30.000Z

Source: [https://soliditydeveloper.com/uniswap-oracle](<https://soliditydeveloper.com/uniswap-oracle>)

## Compatibility and review

Before you start

Historical Uniswap v2 oracle patterns. Kovan is retired. A TWAP is not an unmanipulable price guarantee, and historical proof verification must authenticate the block header and state root. EIP-2935 now provides a separate 8,191-block hash window, while blockhash itself remains limited to 256.

[Official reference](<https://developers.uniswap.org/docs/protocols/v2/guides/building-an-oracle>)

We've covered Uniswap previously [here](<https://soliditydeveloper.com/uniswap-oracle>). But let's go through the basics first again.

## What is UniSwap?

If you're not familiar with [Uniswap](<https://uniswap.exchange/>) yet, it's a fully decentralized protocol for automated liquidity provision on Ethereum. An easier-to-understand description would be that it's a decentralized exchange (DEX) relying on external liquidity providers that can add tokens to smart contract pools and users can trade those directly.

Since it's running on Ethereum, what we can trade are Ethereum ERC-20 tokens. Each pool represents a pair of tokens; in v3 a pair can also have pools at different fee tiers. Uniswap - being fully decentralized - has no restrictions to which tokens can be added. If no contracts for a token pair exist yet, anyone can create one using their factory and anyone can provide liquidity to a pool. The swap fee and any protocol share depend on the protocol version and pool configuration; see the [current fee documentation](<https://developers.uniswap.org/docs/protocols/protocol-fee/concepts/fees>).

The price of a token is determined by the liquidity in a pool. For example if a user is buying *TOKEN1* with *TOKEN2*, the supply of *TOKEN1* in the pool will decrease while the supply of *TOKEN2* will increase and the price of *TOKEN1* will increase. Likewise, if a user is selling *TOKEN1*, the price of *TOKEN1* will decrease. Therefore the token price always reflects the supply and demand.

And of course a user doesn't have to be a person, it can be a smart contract. That allows us to add Uniswap to our own contracts for adding additional payment options for users of our contracts. Uniswap makes this process very convenient, see below for how to integrate it.

![Uniswap UI](<https://cdn0.scrvt.com/b095ee27d37b3d7b6b150adba9ac6ec8/64fffff02c44018e/2b54bfc6c00f/v/f970f09c6aad/uniswap-ui.png>)

You can integrate Uniswap directly for trading with your contracts. For example users could pay in ETH, but your contract will trade it automatically and instead receive DAI. To learn how to do this, see my tutorial [here](<https://soliditydeveloper.com/uniswap-oracle>).

## The Uniswap Oracle

Now let's take a look on how Uniswap can be used as an oracle. You might want to get the price feed for DAI for example, so roughly the USD price for a given ERC-20 token. This can be done with Uniswap, but you need to be aware of a few things here.

### The Problem with Uniswap v1

A pool’s current reserve ratio responds to trades. Using it as an independent external valuation can therefore let temporary liquidity and ordering conditions distort an application’s accounting.

An oracle design needs a price window suited to the application, adequate liquidity, independent bounds or corroboration, and explicit behavior when data is stale or inconsistent. A TWAP can raise manipulation cost, but it does not make every market or consumer safe. The linked oracle discussion remains useful background on these assumptions.

### Uniswap v2: Time-weighted average prices

First of all Uniswap v2 measures the price only at the end of a block. Meaning to manipulate the price, one has to buy a token, wait for the next block and only then is able to sell it back again. This allows for greater arbitrage opportunities by other actors and thus an increased risk/cost for the price manipulator.

Secondly, in Uniswap v2 a time-weighted average price functionality was added. Before we make it too complicated, the basic functionality is very simple.

Each pool has two new functions:

- `price0CumulativeLast()`
- `price1CumulativeLast()`

Those alone won't help you. After all we are interested in an average price over time. So we're missing the historic value of `priceCumulativeLast`.

For example to get the time-weighted average price for token0 over a period of 24 hours:

1. store `price0CumulativeLast()` and the respective timestamp at this time (`block.timestamp`)
2. wait 24 hours
3. compute the 24h-average price as `(price0CumulativeLast() - price0CumulativeOld) / (block.timestamp - timestampOld)`

The stored cumulative values and timestamps must describe the same instants. A raw `price0CumulativeLast` can be stale when no pool update occurred; the oracle library computes counterfactual current values. The original timestamp/cumulative subtraction intentionally uses modular overflow semantics, so migrating it to Solidity 0.8 requires explicit, reviewed handling rather than a pragma-only change.

Having only price0 might be good enough in some cases. However, the time-weighted average of using either token0 or token1 can actually produce different results. That's why Uniswap simply offers both.

### Using Uniswap as oracle in your contracts

Now the tricky part is the historic value. It means you can't just integrate it in your contracts. Depending on your requirements and the complexity of the implementation, you can choose between simple, medium or complex oracle integration:

#### 1. The simple method: Fixed Window Manual

In the manual setup you would call an `update` function regularly yourself. For example for our 24h weighted average, this function needs to be called once per day. The average price is calculated according the the formula above:

- `priceDifference / timeElapsed`

The `FixedPoint.uq112x112` part is not important conceptually. It just represents the results as a fixed point number with 112 bits on either side of the number.

```solidity
function update() external {
    (uint price0Cumulative, , uint32 blockTimestamp) = UniswapV2OracleLibrary.currentCumulativePrices(pairAddress);
    uint32 timeElapsed = blockTimestamp - blockTimestampLast;

    require(timeElapsed >= TIME_PERIOD, 'UniOracle: Time period not yet elapsed');

    price0Average = FixedPoint.uq112x112(uint224((price0Cumulative - price0CumulativeLast) / timeElapsed));
    price0CumulativeLast = price0Cumulative;
    blockTimestampLast = blockTimestamp;
}
```

Now that we have the average price, one can calculate the `amountOut` measured in token1, for a given `amountIn` measured in token0:

```solidity
function convertToken0UsingTimeWeightedPrice(uint amountIn) external view returns (uint amountOut) {
       return price0Average.mul(amountIn).decode144();
}
```

The reciprocal of an arithmetic TWAP is generally not the arithmetic TWAP of the inverse price. Accumulate `price1CumulativeLast` separately when you need that direction’s TWAP. The following is only a reciprocal estimate, with the fixed-point return type corrected.

```solidity
function convertToken1UsingTimeWeightedPrice(uint amountIn) external view returns (uint amountOut) {
       FixedPoint.uq112x112 memory price1Average = price0Average.reciprocal();
       return price1Average.mul(amountIn).decode144();
}
```

The obvious downside of this method is that you have to manually call the contract continuously. Further this fixed window average price reacts slowly to recent changes and places the same weight for historical prices as for more recent ones.

#### 2. The medium method: Moving Window Manual

With the moving window method, you can define a window size. Then you specify a granularity which indicates how many measurement points one should have inside this window. For example given the values:

- window size: 2 months
- granularity: 3

would look like this:

![Moving Average](<https://cdn0.scrvt.com/b095ee27d37b3d7b6b150adba9ac6ec8/a68d03129fcb4ede/fa1d50e5a6b4/v/3bb7879562f5/moving-average.png>)

The average is computed for the current window. The higher your granularity, the more precise the average will be, but also the more times you will need to call `update()`.

The linked Remix example used DAI/WETH on Kovan, which is now retired. It remains a historical illustration of observation windows. Missing or insufficiently old observations cause consultation to revert; choose window size and update policy from the application’s economic requirements, not a short demo interval.

#### 3. The complex method: Moving Window Automatic

Lastly there is a cool project which implemented a solution where you don't need to have any automatic `update()` calls.

How does that work conceptually?

Remember we need the historical value for `price0CumulativeLast()`. This value is not on the chain anymore. So there is no way to just read it from the contract storage again. But there is something on the chain that relates to this value...

At least for the last 256 blocks, we still can read the blockhash from the EVM:

```solidity
blockhash(uint blockNumber) returns (bytes32)
```

The block hash authenticates the encoded block header; it is not itself the state-tree root. A verifier first checks the supplied header against a trusted block hash, extracts its state root, and then checks the account/storage proof. The simple Merkle illustration below explains the inclusion concept.

![Merkle Tree](<https://cdn0.scrvt.com/b095ee27d37b3d7b6b150adba9ac6ec8/3bb6e4646584196b/1e38ecb0a2ec/v/a8e0a9897487/merkle-tree.png>)

At the root of the merkle tree is the root hash. This illustrates a generic Merkle root; Ethereum’s `blockhash` returns the hash of a block header, which contains the separate state root. It's created by hashing each data block and storing it as leaf node. Now two leaf hashes are combined by hashing those together. We do this all the way until we have one tree with a single root hash.

A merkle proof now would be proving to someone that L3 did indeed contain a given value. All one needs to do is provide the Hash 0, Hash 1-1 and the L3 block itself. Now for the proof verification one can compute the hash of L3, then the hash 1 and finally the top hash. We can then compare the root hash against our known root hash. For a visual explanation of a merkle proof, check out [this great explanation](<https://www.youtube.com/watch?v=2kPFSoknlUU>).

In Ethereum one merkle tree is the state tree which contains all state like balances but also the contract storage. That means it will also contain our `price0CumulativeLast` value. So we can create a merkle proof as described above for our historic price value! [EIP-1186](<https://eips.ethereum.org/EIPS/eip-1186>) introduced the `eth_getProof` RPC call which gives you the required proof data automatically from a running Ethereum node. We can pass the proof data to the oracle contract and verify the proof inside the smart contract.

Check out the [repository](<https://github.com/Keydonix/uniswap-oracle/>) for the full details, but be aware that it's unaudited code.

### Future Improvements

[EIP-2935](<https://eips.ethereum.org/EIPS/eip-2935>) is now implemented on Ethereum and serves the previous 8,191 block hashes through a system contract. It does not store all history and does not change the 256-block range of the `blockhash` opcode. An oracle still needs correctly authenticated headers, storage proofs and an appropriate observation window.
