Uniswap V3 provides a price oracle feature that allows users to obtain accurate and real-time price data for token pairs within the Uniswap V3 pools. The price oracle is an important component of the Uniswap V3 protocol and provides a way for developers to access price information without relying on centralized or third-party price feeds.
The price oracle in Uniswap V3 works by storing a snapshot of the current price of a pair of tokens in the pool at specific intervals. These prices are encoded in the form of sqrtPriceX96 (square root of the price, multiplied by 296), which allows for more precise and efficient price calculation.
In this guide, we’ll show how to interact with the Uniswap V3 price oracle using a smart contract or by using ethers.js to fetch the price from the pool.
1. Understanding the Price Oracle in Uniswap V3
The price in Uniswap V3 is stored as a sqrtPriceX96 value in the pool. To retrieve the actual price of the token pair, we need to decode this value into the actual price by taking the square of it and adjusting for the scaling factor of 2^96
.
2. Key Concepts:
sqrtPriceX96: This is the square root of the price, scaled by 296. The formula for the price is:
[
\text{Price} = \left( \frac{\text{sqrtPriceX96}}{2^{96}} \right)2
]
Tick: Uniswap V3 operates on a tick-based system where each price range is defined by ticks. The tick defines the price for a given interval.
Pool Address: To access the price data, you need to interact with the specific Uniswap V3 pool contract for the pair of tokens you're interested in.
3. Accessing the Price using ethers.js
Here’s an example of how you can use ethers.js to get the current price from a Uniswap V3 pool:
3.1. Install Required Dependencies
First, you need to install the necessary dependencies (if you don't have them already):
npm install ethers @uniswap/v3-core
3.2. Sample Code to Get the Current Price from a Uniswap V3 Pool
const { ethers } = require("ethers");
// Define the Uniswap V3 Pool contract ABI for the price oracle
const POOL_ABI = [
"function slot0() external view returns (uint160 sqrtPriceX96, int24 tick, uint16 observationIndex, uint16 observationCardinality, uint16 observationCardinalityNext, uint8 feeProtocol, bool unlocked)"
];
// Uniswap V3 pool address (for example, USDC/ETH pool on Ethereum mainnet)
const POOL_ADDRESS = "0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8"; // Example, change with the actual pool address
const provider = new ethers.JsonRpcProvider("https://mainnet.infura.io/v3/YOUR_INFURA_PROJECT_ID");
async function getPrice() {
const poolContract = new ethers.Contract(POOL_ADDRESS, POOL_ABI, provider);
// Fetch the current price from the pool
const { sqrtPriceX96 } = await poolContract.slot0();
// Decode the sqrtPriceX96 to the actual price
const price = (sqrtPriceX96 ** 2) / (2 ** 192);
console.log(`The current price is: ${price}`);
}
getPrice().catch(console.error);
3.3. Explanation of the Code:
POOL_ABI: This ABI contains the slot0()
function, which returns multiple important data points about the current pool state, including the sqrtPriceX96
(which contains the price).
POOL_ADDRESS: The address of the Uniswap V3 pool for which we want to get the price. For instance, if you want to get the price for the USDC/ETH pair, you need to use the corresponding pool address.
slot0(): This function returns several important parameters, including the square root price (sqrtPriceX96
) and the current tick. We are interested in the sqrtPriceX96
because it gives us the price of the pair.
Price Calculation: The price is calculated by squaring the sqrtPriceX96
and adjusting it with 2^96
(since the price is encoded as the square root).
4. Handling Multiple Pools
You can query the price from different Uniswap V3 pools for various token pairs. You just need to replace the POOL_ADDRESS
in the code with the corresponding pool address.
For example:
- ETH/USDT pool:
0x5C69bEe701ef814a2B6a0e1e1e5d9Bf251DD46AB
- DAI/USDC pool:
0x7a8D8d75a1Aaa7e06e1D4334b3C5a967Fc648F5a
Each pool has its own address on the blockchain, and the price oracle can be queried for the specific pair you’re interested in.
5. Accessing Historical Price Data with Observations
Uniswap V3 pools also store price observations that can be used to fetch historical prices at specific intervals. If you need historical price data, you can use the observations
feature from Uniswap V3.
The slot0()
function provides the current state, while the observations provide historical price data for a given pair.
const { observationIndex, observationCardinality } = await poolContract.slot0();
You can use these to retrieve past price data based on the observation index.
6. Using Uniswap V3's Oracle for Custom Price Feeds
Uniswap V3’s price oracle can be integrated into custom decentralized applications (DApps) or smart contracts to provide reliable price feeds for various use cases, such as:
- Lending and Borrowing Platforms: To track collateral price fluctuations.
- Derivatives: To derive prices for synthetic assets.
- Arbitrage Bots: To take advantage of price discrepancies between different DEXs.
By leveraging the Uniswap V3 price oracle, you can avoid the risks associated with centralized price oracles (e.g., manipulation, downtime) and access decentralized price data directly from the blockchain.
Uniswap V3 provides a robust and efficient price oracle mechanism based on its unique liquidity model. By querying the sqrtPriceX96
value from Uniswap V3 pools, you can retrieve accurate and decentralized price information for token pairs.
With the flexibility to retrieve both current prices and historical data through observations, Uniswap V3's price oracle is a powerful tool for decentralized finance (DeFi) applications.