Skip to main content
Helpful?

Introduction

When building on Uniswap v4, you will often need to read pool state for both onchain and offchain use cases. Onchain contracts can directly invoke the StateLibrary to execute these reads during transactions, but offchain systems—such as frontends or analytics services—require a deployed contract with view functions. This is where StateView comes in.

In short: Use StateLibrary within onchain contracts and use StateView with an RPC for frontends and analytics.

By providing a dedicated interface for offchain reads, StateView helps:

  • Retrieve pool state without paying gas
  • Simplify integration for frontends, dashboards, and analytics
  • Ensure a clean separation between onchain logic and offchain queries

Comparing onchain and offhain Access

If you’re familiar with Reading Pool State, you already know that Uniswap v4 uses extsload for efficient data access. For onchain usage, we rely on StateLibrary within contracts. However, offchain clients cannot rely on an onchain library for state reads.

Instead, StateView provides these same calls in a single contract designed explicitly for offchain consumption.

Because StateLibrary operates via onchain function calls, it’s not directly accessible to offchain clients. Hence, StateView provides a simple, gas-free interface designed for frontends and analytics.

For instance, an onchain contract might use the StateLibrary as follows:

// Onchain contract using StateLibrary
contract MyProtocol {
using StateLibrary for IPoolManager;

function checkPoolPrice(PoolId poolId) external returns (uint160) {
(uint160 sqrtPriceX96, , , ) = poolManager.getSlot0(poolId);
// ... use the price in contract logic ...
return sqrtPriceX96;
}
}

By contrast, an offchain frontend or analytics service should interact with StateView:

// Frontend or analytics client using StateView
const stateView = getContract({
address: STATE_VIEW_ADDRESS,
abi: stateViewABI
});

const { sqrtPriceX96 } = await stateView.read.getSlot0([poolId]);
// ... use the price in your application ...

This separation ensures that each context (onchain vs. offchain) uses the most efficient data reading pattern.

Usage With Frontend Clients

Frontend applications frequently display real-time information about pools, positions, and other market data—without incurring transaction costs. StateView addresses these requirements by exposing read-only functions tailored for offchain integrations.

Setting Up With Viem

We’ll use viem, a TypeScript library for Ethereum, to demonstrate how to connect to StateView.

import { createPublicClient, http } from 'viem';
import { mainnet } from 'viem/chains';

// Initialize the client
const client = createPublicClient({
chain: mainnet,
transport: http()
});

// Set up StateView contract instance
const stateView = getContract({
address: STATE_VIEW_ADDRESS,
abi: stateViewABI,
client
})

Note: The stateView object comes from our getContract call above. Make sure you’ve imported stateViewABI correctly before attempting to read from the contract.

With this setup, you can now:

  • Connect to an Ethereum network
  • Call StateView’s read functions
  • Retrieve pool information offchain at no gas cost

Handling Errors and Invalid Pool IDs

When calling stateView.read.<function>([poolId]), be mindful that:

  • If you pass an invalid poolId (typically a bytes32 in Uniswap v4), the call may revert or return unexpected data.
  • Consider adding try-catch (or equivalent error handling in your framework) to gracefully handle failures if the pool does not exist or if the call fails onchain.

Reading Pool Data

Here are common examples of how to retrieve pool data using StateView.

Getting Pool State

A pool’s core state, such as its current price or fees, is often necessary for frontends. Use getSlot0:

// Example: Reading pool price and fees
const getPoolState = async (poolId: string) => {
// getSlot0 returns:
// - Current price (sqrtPriceX96) in Q64.96 fixed-point format
// - Active tick
// - Protocol and LP fee settings
const {
sqrtPriceX96,
tick,
protocolFee,
lpFee
} = await stateView.read.getSlot0([poolId]);

return {
price: calculatePrice(sqrtPriceX96), // implement your math logic for Q64.96
tick,
protocolFee,
lpFee
};
};

What it Returns:

  • sqrtPriceX96: The current pool price in Q64.96 fixed-point format.
  • tick: The current tick in which the pool is operating.
  • protocolFee and lpFee: Fee parameters for protocol and LP fee tiers.

Getting Pool Liquidity

To understand how much liquidity a pool holds:

// Example: Reading the total active liquidity of a pool
const getPoolLiquidity = async (poolId: string) => {
// getLiquidity returns the total liquidity currently active in the pool
const liquidity = await stateView.read.getLiquidity([poolId]);
return liquidity;
};

Why It Matters:

  • Helps gauge the depth of the pool
  • Influences price impact calculations in trading
  • Provides context for the pool’s capacity to absorb trades

Core Functions and Return Types

While StateView exposes many functions, here are several essential calls for most offchain applications. Each function typically takes a poolId (of type bytes32) as the key input, identifying which pool to query.

  1. getSlot0(poolId)
    • Returns (uint160 sqrtPriceX96, int24 tick, uint8 protocolFee, uint8 lpFee).
    • Essential for displaying real-time price data and fees.
  2. getLiquidity(poolId)
    • Returns uint128 liquidity (the total active pool liquidity).
    • Used to assess trading depth and volatility.
  3. getPositionInfo(positionId)
    • Returns (uint128 liquidity, uint256 feeGrowthInside0Last, uint256 feeGrowthInside1Last).
    • Critical for tracking user positions, especially to calculate earned fees over time.
  4. getFeeGrowthGlobals(poolId)
    • Returns (uint256 feeGrowthGlobal0, uint256 feeGrowthGlobal1).
    • Useful for analytics around total fee accumulation in the pool.

Note on poolId and positionId

  • In Uniswap v4, a poolId is typically a bytes32 that is derived by calling keccak256(abi.encode(poolKey)) where poolKey contains:
    • currency0: The lower currency address of the pool
    • currency1: The higher currency address of the pool
    • fee: The pool LP fee (uint24)
    • tickSpacing: The tick spacing value (int24)
    • hooks: The hooks contract address
  • A positionId may also be a bytes32 or other unique identifier that references a specific position.

Security and Gas Considerations

  • Offchain Reads: Calls to StateView are purely read-only, so they cost no gas. This makes them ideal for frequently refreshing UI/analytics data.
  • Onchain vs. Offchain: Remember that if you need to integrate pool data into a live transaction, you must use StateLibrary within your smart contract.
  • Edge Cases: Always verify the returned data before using it in your application. Network or contract errors could lead to unexpected values.

Conclusion

StateView is a powerful and efficient way to read Uniswap v4 pool data offchain. By separating onchain logic (using StateLibrary) and offchain reads (using StateView), Uniswap ensures the best developer experience for both contexts.

To recap:

  1. Setup: Use libraries like viem to connect to the Ethereum network.
  2. Read: Call getSlot0, getLiquidity, getPositionInfo, and other methods for crucial state data.
  3. Handle Errors: Implement basic checks for invalid poolId or connection failures.
Helpful?