Skip to content

Commit

Permalink
StateView & Flash Accounting guides added (#848)
Browse files Browse the repository at this point in the history
Co-authored-by: saucepoint <98790946+saucepoint@users.noreply.github.com>
Co-authored-by: ant <0xdevant@gmail.com>
  • Loading branch information
3 people authored Jan 17, 2025
1 parent d5ce3fa commit 70c7e8a
Show file tree
Hide file tree
Showing 2 changed files with 544 additions and 0 deletions.
197 changes: 197 additions & 0 deletions docs/contracts/v4/guides/12-state-view.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
---
title: StateView
---

# 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**](https://github.com/Uniswap/v4-core/blob/main/src/libraries/StateLibrary.sol) 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**](https://github.com/Uniswap/v4-periphery/blob/main/src/lens/StateView.sol) 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](/contracts/v4/guides/read-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:

```solidity
// 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`:

```tsx
// 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**](https://viem.sh/), a TypeScript library for Ethereum, to demonstrate how to connect to **StateView**.

```tsx
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`](https://github.com/Uniswap/v4-core/blob/main/src/types/PoolId.sol#L6) 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`:

```tsx
// 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:

```tsx
// 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)`](/contracts/v4/reference/periphery/lens/StateView#getslot0)**
- Returns `(uint160 sqrtPriceX96, int24 tick, uint8 protocolFee, uint8 lpFee)`.
- Essential for displaying real-time price data and fees.
2. **[`getLiquidity(poolId)`](/contracts/v4/reference/periphery/lens/StateView#getliquidity)**
- Returns `uint128 liquidity` (the total active pool liquidity).
- Used to assess trading depth and volatility.
3. **[`getPositionInfo(positionId)`](/contracts/v4/reference/periphery/lens/StateView#getpositioninfo)**
- Returns `(uint128 liquidity, uint256 feeGrowthInside0Last, uint256 feeGrowthInside1Last)`.
- Critical for tracking user positions, especially to calculate earned fees over time.
4. **[`getFeeGrowthGlobals(poolId)`](/contracts/v4/reference/periphery/lens/StateView#getfeegrowthglobals)**
- 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.
Loading

0 comments on commit 70c7e8a

Please sign in to comment.