DEXTutorialUniswap

How to Track Liquidity for Token Pairs on Uniswap

List every pool a token or token pair trades in, read a pool's reserves in USD, count its trades per day and catch new pools as they are created, with GraphQL queries that run on the current Bitquery API.

Cover Image for How to Track Liquidity for Token Pairs on Uniswap

Introduction

Uniswap is an automated market maker: instead of an order book, every pair trades against a liquidity pool, and the depth of that pool decides how much a trade moves the price. Tracking liquidity therefore comes down to a handful of questions. Which pools does a token trade in? Which of them belong to Uniswap rather than a fork? What do they hold right now, in tokens and in USD? How busy are they? And which pools were just created?

The queries below answer each of those on the Bitquery V2 API, and every one was run against the live API before this update. Run them in the IDE with a free token.

Understanding token-pair liquidity

There are two broad kinds of DEX. An AMM like Uniswap prices trades algorithmically against pooled tokens; an order-book DEX matches buyers and sellers directly. On an AMM, liquidity means the tokens sitting in the pool. Liquidity providers deposit them in exchange for a share of trading fees, and the more a pool holds, the larger the trade it can absorb without a large price move.

Two things about Uniswap pools matter for the queries that follow:

  • There are many pools per token. Anyone can create one, so a token usually has pools across Uniswap v2, v3 (one per fee tier), v4 and other DEXs, at very different depths.
  • Pool identity differs by version. v2 pairs and v3 pools are contracts with their own address. v4 pools live inside one PoolManager contract and are identified by a PoolId. On the chain-level cubes the pool contract is Trade.Dex.SmartContract; Trade.Dex.Pair.SmartContract is populated for v2 pairs and comes back as the zero address for v3, so filter on Dex.SmartContract when you mean a specific pool.

With that settled, on to the data, all of it from the DEX API.

Get all liquidity pools for a token

The first question is which pools a token trades in. This query takes every trade where USDT was bought in the last week and keeps one row per counter-token, which gives you the token's pools across all DEXs with the pool address, protocol and factory. The time window keeps the sweep fast; widen it for a fuller list.

Open this query in the GraphQL IDE.

query PoolsForToken {
  EVM(dataset: combined, network: eth) {
    DEXTrades(
      where: {
        Trade: {
          Buy: { Currency: { SmartContract: { is: "0xdac17f958d2ee523a2206206994597c13d831ec7" } } }
        }
        Block: { Time: { since_relative: { days_ago: 7 } } }
      }
      limit: { count: 10 }
      limitBy: { by: Trade_Sell_Currency_SmartContract, count: 1 }
    ) {
      Trade {
        Dex {
          ProtocolName
          OwnerAddress
          SmartContract
        }
        Buy {
          Currency {
            Symbol
            SmartContract
          }
        }
        Sell {
          Currency {
            Symbol
            SmartContract
          }
        }
      }
    }
  }
}

OwnerAddress is the factory that deployed the pool. It matters more than the protocol name, as the next section shows.

Get all pools for a token on a specific DEX

Protocol names describe a pool's interface, so uniswap_v2 also covers v2-compatible pools deployed by other factories, SushiSwap's included. To restrict the list to pools Uniswap itself deployed, filter on the factory as well as the protocol name. On Ethereum the v2 factory is 0x5c69…aa6f and the v3 factory 0x1f98…f984; v4 pools all sit under the PoolManager and are best filtered by ProtocolName: uniswap_v4.

Open this query in the GraphQL IDE.

query PoolsForTokenOnUniswapV2 {
  EVM(dataset: combined, network: eth) {
    DEXTrades(
      where: {
        Trade: {
          Buy: { Currency: { SmartContract: { is: "0xdac17f958d2ee523a2206206994597c13d831ec7" } } }
          Dex: {
            ProtocolName: { is: "uniswap_v2" }
            OwnerAddress: { is: "0x5c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f" }
          }
        }
        Block: { Time: { since_relative: { days_ago: 7 } } }
      }
      limit: { count: 10 }
      limitBy: { by: Trade_Sell_Currency_SmartContract, count: 1 }
    ) {
      Trade {
        Dex {
          ProtocolName
          OwnerAddress
          SmartContract
        }
        Buy {
          Currency {
            Symbol
            SmartContract
          }
        }
        Sell {
          Currency {
            Symbol
            SmartContract
          }
        }
      }
    }
  }
}

Get the liquidity of a pool

Once you have a pool address, its liquidity is what the pool holds. The Balances cube returns the current balance per token for any address, so filter to the pool and its two tokens. The example is the Uniswap v3 USDC/WETH pool at the lowest fee tier.

Open this query in the GraphQL IDE.

query PoolLiquidity {
  EVM(network: eth, dataset: combined) {
    Balances(
      where: {
        Balance: { Address: { is: "0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640" } }
        Currency: {
          SmartContract: {
            in: [
              "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
              "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"
            ]
          }
        }
      }
    ) {
      Currency {
        Symbol
      }
      Balance {
        Amount
        AmountInUSD
      }
    }
  }
}

The currency filter matters: well-known pool addresses receive airdropped junk tokens, and without it those show up too. For pools that hold their funds in a shared contract, which is every Uniswap v4 pool, use DEXPoolEvents instead. It records reserves, their USD value and the spot price in both directions after every swap, mint and burn, and its latest row for a pool is the current state:

query PoolReservesLatest {
  EVM(network: eth) {
    DEXPoolEvents(
      limit: { count: 1 }
      orderBy: { descending: Block_Time }
      where: {
        PoolEvent: {
          Pool: { SmartContract: { is: "0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640" } }
        }
      }
    ) {
      Block {
        Time
      }
      PoolEvent {
        AtoBPrice
        BtoAPrice
        Liquidity {
          AmountCurrencyA
          AmountCurrencyAInUSD
          AmountCurrencyB
          AmountCurrencyBInUSD
        }
        Pool {
          CurrencyA {
            Symbol
          }
          CurrencyB {
            Symbol
          }
          PoolId
        }
      }
    }
  }
}

DEXPoolEvents keeps a rolling window of a few days and streams, which makes it the cube for live monitoring; the Ethereum liquidity API page covers its stream and Kafka forms.

Get the number of trades in a pool

How often a pool is used is the other half of its health. This counts successful trades per day for the same pool, filtering on Dex.SmartContract and counting distinct transaction hashes so that multi-hop swaps are not counted twice.

Open this query in the GraphQL IDE.

query PoolTradesPerDay {
  EVM(network: eth, dataset: combined) {
    DEXTrades(
      orderBy: { descending: Block_Date }
      where: {
        Trade: { Dex: { SmartContract: { is: "0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640" } } }
        Block: { Time: { since_relative: { days_ago: 30 } } }
      }
      limit: { count: 30 }
    ) {
      Block {
        Time(interval: { in: days })
      }
      Trades: count(distinct: Transaction_Hash, if: { TransactionStatus: { Success: true } })
    }
  }
}

Get the recent pools created on Uniswap

New pools appear every day. The v3 factory emits a PoolCreated event for each one, so the Events API with the factory address and the event name lists them with both tokens, the tick spacing and the new pool address. The v2 factory emits PairCreated and the v4 PoolManager emits Initialize; the Uniswap pools post has one query that watches all three.

Open this query in the GraphQL IDE.

query NewUniswapV3Pools {
  EVM(network: eth) {
    Events(
      where: {
        Log: {
          SmartContract: { is: "0x1f98431c8ad98523631ae4a59f267346ea31f984" }
          Signature: { Name: { is: "PoolCreated" } }
        }
      }
      limit: { count: 10 }
      orderBy: { descending: Block_Number }
    ) {
      Block {
        Time
      }
      Transaction {
        Hash
      }
      Arguments {
        Name
        Value {
          ... on EVM_ABI_Address_Value_Arg {
            address
          }
          ... on EVM_ABI_BigInt_Value_Arg {
            bigInteger
          }
        }
      }
    }
  }
}

Swap query for subscription to receive each new pool as it is created.

Get all pools for a token pair

The same two tokens can have several pools at very different depths. For the current state, DEXPoolEvents filtered on both tokens, in either order, with one row per pool gives every pool and what it holds in USD; v4 pools appear under the PoolManager address with their PoolId.

Open this query in the GraphQL IDE.

query PoolsForPair {
  EVM(network: eth) {
    DEXPoolEvents(
      limit: { count: 20 }
      limitBy: { by: PoolEvent_Pool_SmartContract, count: 1 }
      orderBy: { descending: Block_Time }
      where: {
        any: [
          {
            PoolEvent: {
              Pool: {
                CurrencyA: { SmartContract: { is: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48" } }
                CurrencyB: { SmartContract: { is: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2" } }
              }
            }
          }
          {
            PoolEvent: {
              Pool: {
                CurrencyA: { SmartContract: { is: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2" } }
                CurrencyB: { SmartContract: { is: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48" } }
              }
            }
          }
        ]
      }
    ) {
      PoolEvent {
        Dex {
          ProtocolName
        }
        Pool {
          SmartContract
          PoolId
          CurrencyA {
            Symbol
          }
          CurrencyB {
            Symbol
          }
        }
        Liquidity {
          AmountCurrencyAInUSD
          AmountCurrencyBInUSD
        }
      }
    }
  }
}

For pools that have not changed within the rolling window, or for history, run the same pair filter on DEXTrades with limitBy: { by: Trade_Dex_SmartContract, count: 1 }, which returns one row per pool that traded in the period you choose.

Uniswap data from other chains

Every query above works on Base, Arbitrum, Optimism, Polygon and BNB Chain by changing the network value and using that chain's token and factory addresses. The Uniswap API docs link the per-chain variants.

Liquidity data from DEXs besides Uniswap

Nothing here is Uniswap-specific except the factory filter. Drop it and the same queries cover SushiSwap, Curve, Balancer and the other venues indexed on each chain; the price impact each pool can absorb is covered by the slippage API.

Also read

Subscribe to our newsletter

Subscribe and never miss any updates related to our APIs, new developments & latest news etc. Our newsletter is sent once a week on Monday.