> For the complete documentation index, see [llms.txt](https://docs.envio.dev/llms.txt).

# How to Index Uniswap v4 Swaps

Uniswap v4 keeps every pool inside one contract. There is no pool contract per pair and no factory to follow, so indexing v4 means indexing a single address, the `PoolManager`, and reading the pool id carried on each event.

This page has both routes. Build a minimal indexer yourself, which is the walkthrough below, or start from the [official Uniswap V4 indexer](https://github.com/enviodev/uniswap-v4-indexer), the production implementation that powers [v4.xyz](https://v4.xyz), which is described further down.

In a hurry, skip to [the agent prompt](#hand-this-to-your-coding-agent).

## Prerequisites

- Node.js 22 or higher
- [Docker](https://www.docker.com/products/docker-desktop/), which `envio dev` uses for Postgres and Hasura
- An [API token](/docs/HyperSync/api-tokens)

## PoolManager addresses

Two of the deployments, from [Uniswap's own deployment list](https://github.com/Uniswap/docs/blob/main/content/protocols/v4/deployments.mdx):

| Chain | Chain ID | PoolManager |
| --- | --- | --- |
| Ethereum | 1 | `0x000000000004444c5dc75cB358380D2e3dE08A90` |
| Base | 8453 | `0x498581fF718922c3f8e6A244956aF099B2652b2b` |

## Set up

```bash
mkdir uniswap-v4-swaps && cd uniswap-v4-swaps
pnpm init
pnpm pkg set type=module
pnpm add envio
```

`config.yaml`

```yaml
# yaml-language-server: $schema=./node_modules/envio/evm.schema.json
name: uniswap-v4-swaps
contracts:
  - name: PoolManager
    handler: src/EventHandlers.ts
    events:
      - event: "Initialize(bytes32 indexed id, address indexed currency0, address indexed currency1, uint24 fee, int24 tickSpacing, address hooks, uint160 sqrtPriceX96, int24 tick)"
      - event: "Swap(bytes32 indexed id, address indexed sender, int128 amount0, int128 amount1, uint160 sqrtPriceX96, uint128 liquidity, int24 tick, uint24 fee)"
chains:
  - id: 8453 # Base
    start_block: 25350000
    contracts:
      - name: PoolManager
        address: "0x498581fF718922c3f8e6A244956aF099B2652b2b"
```

The signatures come from [`IPoolManager.sol`](https://github.com/Uniswap/v4-core/blob/main/src/interfaces/IPoolManager.sol). Solidity declares `id` as `PoolId`, the currencies as `Currency` and the hook as `IHooks`, which are `bytes32`, `address` and `address` underneath, so that is what the config uses. The `Swap` signature above hashes to `0x40e9cecb9f5f1f1c5b9c97dec2917b7ee92e57ba5563708daca94dd84ad7112f`, which is the topic the PoolManager emits.

`schema.graphql`

```graphql
type Pool {
  id: ID!
  currency0: String!
  currency1: String!
  fee: Int!
  tickSpacing: Int!
  hooks: String!
  swapCount: Int!
}

type Swap {
  id: ID!
  pool_id: String!
  sender: String!
  amount0: BigInt!
  amount1: BigInt!
  tick: Int!
  blockNumber: Int!
  blockTimestamp: Int!
}
```

`src/EventHandlers.ts`

```typescript
import { indexer } from "envio";

indexer.onEvent(
  { contract: "PoolManager", event: "Initialize" },
  async ({ event, context }) => {
    context.Pool.set({
      id: event.params.id,
      currency0: event.params.currency0,
      currency1: event.params.currency1,
      fee: Number(event.params.fee),
      tickSpacing: Number(event.params.tickSpacing),
      hooks: event.params.hooks,
      swapCount: 0,
    });
  },
);

indexer.onEvent(
  { contract: "PoolManager", event: "Swap" },
  async ({ event, context }) => {
    context.Swap.set({
      id: `${event.chainId}_${event.block.number}_${event.logIndex}`,
      pool_id: event.params.id,
      sender: event.params.sender,
      amount0: event.params.amount0,
      amount1: event.params.amount1,
      tick: Number(event.params.tick),
      blockNumber: event.block.number,
      blockTimestamp: event.block.timestamp,
    });

    const pool = await context.Pool.get(event.params.id);
    if (pool) {
      context.Pool.set({ ...pool, swapCount: pool.swapCount + 1 });
    }
  },
);
```

Every numeric event parameter arrives as a BigInt, including the small ones like `fee`, `tickSpacing` and `tick`. Writing one of those straight into an `Int!` field fails with `cannot cast type bigint to integer[]`, so wrap them in `Number()`, and keep `BigInt!` for the amounts.

Put your [API token](/docs/HyperSync/api-tokens) in a `.env` file in the project root, which is where the indexer reads it from:

```bash
ENVIO_API_TOKEN=your_token_here
```

Then start it:

```bash
pnpm envio dev
```

## Querying the result

Busiest pools, from the counter the handler keeps:

```graphql
query BusiestPools {
  Pool(limit: 3, order_by: { swapCount: desc }) {
    id
    currency0
    currency1
    fee
    swapCount
  }
}
```

Indexing Base from block 25,350,000 to 26,418,699 gave 4,344 pools and 750,230 swaps, and the busiest pool came back like this:

```json
{
  "id": "0x7af84d60777413f90cc511a83cf702b128bc885f84d6ca8be4b60063328d907a",
  "currency0": "0x0000000000000000000000000000000000000000",
  "currency1": "0x000000000D564D5be76f7f0d28fE52605afC7Cf8",
  "fee": 0,
  "swapCount": 159420
}
```

Recent swaps for one pool:

```graphql
query PoolSwaps {
  Swap(
    where: { pool_id: { _eq: "0x96d4b53a38337a5733179751781178a2613306063c511b78cd02684739288c0a" } }
    order_by: { blockNumber: desc }
    limit: 3
  ) {
    sender
    amount0
    amount1
    blockNumber
  }
}
```

```json
{
  "sender": "0x6fF5693b99212Da76ad316178A184AB56D299b43",
  "amount0": "995365987060141",
  "amount1": "-2701188",
  "blockNumber": 26418690
}
```

Amounts are signed and written from the pool's side, so one is negative and the other positive on every swap.

## Reading v4 data

- **Native ETH is the zero address.** v4 handles ETH directly rather than through WETH. Currencies are sorted numerically in the pool key, and the zero address sorts first, so a pool that trades ETH always carries it as `currency0` and never as `currency1`. In the run above, 723 of the 4,344 pools had native ETH as `currency0`
- **Most pools carry hooks.** `hooks` is the address of the contract attached to the pool, and it is the zero address when there is none. In the same run, 3,107 of the 4,344 pools had one
- **`fee` is in millionths**, capped at 1,000,000, so `3000` is 0.30% and `500` is 0.05%. A fee of exactly `0x800000` means the pool sets its fee dynamically, which [`PoolKey.sol`](https://github.com/Uniswap/v4-core/blob/main/src/types/PoolKey.sol) describes as the highest bit being set. A fee of `0` is a pool whose LP fee is zero, which is common where a hook charges instead
- **`Swap` carries its own `fee`**, which is the fee taken on that swap rather than the pool's LP fee from `Initialize`. The handler above ignores it, so do not confuse the two if you add it to the schema
- **The pool id is a hash**, not an address. `Initialize` is the only event that maps it to the currencies, fee, tick spacing and hook, which is why this indexer stores pools as well as swaps

## Adding more chains

Add another entry under `chains` with that chain's id, start block and PoolManager address. The contract and handler are already shared, so nothing else changes. See [multichain indexing](/docs/HyperIndex/multichain-indexing).

## Hand this to your coding agent

```text
Set up an Envio HyperIndex indexer for Uniswap v4 swaps on Base.

- pnpm init, pnpm pkg set type=module, pnpm add envio
- config.yaml: contract PoolManager at 0x498581fF718922c3f8e6A244956aF099B2652b2b on
  chain 8453, with the Initialize and Swap event signatures from IPoolManager.sol.
  PoolId is bytes32, Currency is address and IHooks is address in the signature
- schema.graphql: a Pool entity (currencies, fee, tickSpacing, hooks, swapCount) and a
  Swap entity (pool_id, sender, amount0, amount1, tick, block number and timestamp)
- src/EventHandlers.ts: import { indexer } from "envio" and register both events with
  indexer.onEvent. Wrap fee, tickSpacing and tick in Number(), since every numeric
  param arrives as a BigInt, and keep the amounts as BigInt
- Put ENVIO_API_TOKEN in a .env file in the project root
- Run it with pnpm envio dev, which needs Docker

Docs: https://docs.envio.dev/docs/HyperIndex/example-uniswap-v4-multi-chain-indexer
```

## The production indexer

The [official Uniswap V4 indexer](https://github.com/enviodev/uniswap-v4-indexer) goes much further than the walkthrough above. It is the same indexer that powers the [v4.xyz](https://v4.xyz) website, providing real-time data for the Uniswap V4 interface.

### Key Features

- **Multichain Support**: Indexes Uniswap V4 across the 18 chains listed in its `config.yaml`, in real time
- **Complete Pool Metrics**: Tracks pool statistics including volume, TVL, fees, and other critical metrics
- **Swap Analysis**: Monitors swap events and liquidity changes with high precision
- **Hook Stats**: Tracks every hook with the pools and swaps it touches and its TVL, volume and fees
- **Production Ready**: Powers the official v4.xyz interface with production-grade reliability
- **Fast Syncing**: HyperIndex reading HyperSync leads most scenarios in our [benchmarks](/docs/HyperIndex/benchmarks), which run in CI and check each tool's output against ground truth

![V4 gif](/img/v4.gif)

### Technical Overview

This indexer is built using TypeScript and provides a unified GraphQL API for accessing Uniswap V4 data across all supported chains. The architecture is designed to handle high throughput and maintain consistency across different chains.

#### Performance Advantages

- **Reads from HyperSync rather than RPC**, which is where the speed comes from. The [benchmarks](/docs/HyperIndex/benchmarks) page has the measured figures per scenario
- **Real-time Updates**: Maintains low latency for new blocks while efficiently managing historical data

### Use Cases

- Power analytics dashboards and trading interfaces
- Monitor DeFi positions and protocol health
- Track historical performance of Uniswap V4 pools
- Build custom notifications and alerts
- Analyze hook interactions and their impact

### Getting Started

To use this indexer, you can:

1. Clone the [repository](https://github.com/enviodev/uniswap-v4-indexer)
2. Follow the installation instructions in the README
3. Run the indexer locally or deploy it to a production environment
4. Access indexed data through the GraphQL API

### Contribution

The Uniswap V4 indexer is actively maintained and welcomes contributions from the community. If you'd like to contribute or report issues, please visit the [GitHub repository](https://github.com/enviodev/uniswap-v4-indexer).

:::note
This is an official reference implementation that powers the v4.xyz website. While extensively tested in production, remember to validate the data for your specific use case. The indexer is continuously updated to support the latest Uniswap V4 features and optimizations.
:::
