Uniswap V3 Subgraph Development and Deployment

Author

Reads 539

Exterior of a building featuring a prominent BTC and exchange sign, indicating a cryptocurrency location.
Credit: pexels.com, Exterior of a building featuring a prominent BTC and exchange sign, indicating a cryptocurrency location.

Developing a Uniswap V3 subgraph is a crucial step in building a robust and scalable decentralized application.

You can create a Uniswap V3 subgraph using The Graph, a decentralized protocol for building and deploying blockchain data APIs.

To start, you'll need to create a new subgraph on The Graph by specifying the blockchain, contract, and other relevant details in your schema.

This schema will serve as the foundation for your subgraph, defining the data that will be indexed and made available to users.

The subgraph will then be deployed to a node on The Graph network, where it will be continuously updated as new data is added to the blockchain.

Broaden your view: Uniswap V3

Getting Started

Uniswap is a constellation of smart contracts, and we're going to index just one of them, so let's start with a manageable task.

The logic of various Uniswap functionalities is split across these contracts, making it easier to focus on one at a time.

Uniswap contracts handle different transactions, and these transactions emit various events and logs that carry useful information.

This approach will give us a solid foundation to build upon, and we can always come back to indexing the other contracts later.

Data Preparation

Blockchain technology and scrabble letters
Credit: pexels.com, Blockchain technology and scrabble letters

Data Preparation is a crucial step in creating a Uniswap V3 Subgraph. It involves collecting and processing data from various sources, including the Uniswap V3 contract.

To start, you'll need to set up a Subgraph using the Uniswap V3 schema, which is a pre-defined template for collecting data from the Uniswap V3 contract. This schema includes fields for token information, liquidity pools, and trading activity.

The Uniswap V3 contract has a complex architecture, with multiple contracts and interfaces that need to be accounted for during data preparation. This includes the Uniswap V3 Factory contract, which is responsible for creating new liquidity pools, and the Uniswap V3 Position Manager contract, which manages user positions in liquidity pools.

Consider reading: Uniswap Contract Address

Preparing the Subgraph Project

To prepare your subgraph project, you'll need to create a new directory and open a terminal in it. Use the command `graph init` to create a new subgraph project from a predefined example or an existing contract.

Smartphone displaying blockchain application resting on a laptop, symbolizing modern technology and finance.
Credit: pexels.com, Smartphone displaying blockchain application resting on a laptop, symbolizing modern technology and finance.

This command will prompt you for several details, including the protocol, product, subgraph slug, and Ethereum network. You'll need to provide the address of the UniswapV3Factory contract and its name.

Here are the specific details you'll need to provide:

Once you've provided all the details, the command will generate a new subgraph project. The graph-cli tool will automatically fetch the corresponding contract ABI and store it in the /abis directory, and install all the required npm packages for the project.

Defining the Schema

Defining the schema is the first step in preparing your data. The schema file, usually named schema.graphql, contains the data schema that helps define the data to be stored by the subgraph and how to query it using GraphQL.

The schema file already contains some schema definitions that define various entity types and their relationships. An entity is essentially a data object, and we can use the schema.graphql file to define multiple entities.

tablet stock cryptocurrency exchange market
Credit: pexels.com, tablet stock cryptocurrency exchange market

Each entity in the schema file should be annotated with the @entity directive. Within each entity, we can add certain fields that act like the entity's properties. Each entity field should have a name to identify it and a type that defines the kind of data that should be stored against that field.

The id field is mandatory among entities, and the ID should always be of the type Bytes or String. We can make certain fields in the entity mandatory by marking them with an exclamation mark (!).

Entities are mutable by default, meaning we might modify existing entities while mapping the data to an entity. To prevent this, we can make entities immutable by using the annotation: @entity(immutable: true).

Mapping the Data

Mapping the data is a crucial step in preparing your data for use. We write the mapping code using AssemblyScript, which closely resembles the syntax of TypeScript.

The mapping file is where we define the code for the handler. The event handler is defined as a function within the mapping file, and the function will have the same name as the handler.

tablet display stock cryptocurrency exchange market
Credit: pexels.com, tablet display stock cryptocurrency exchange market

Each handler function accepts a single parameter called the event. The type of event is set to the event that it is handling, such as handlePoolCreated.

The structure of the mapping code is quite straightforward. We can find the default mapping file inside the /src directory: uniswap-v-3-factory.ts.

The event handler function is where we get the token details. We can create some additional code for getting the token details, which will be useful for our data preparation.

Token Data

Token data can be fetched using the token contract address as an ID. This allows you to access aggregated information for a specific token across all pairs that token is included in.

Token data includes fields such as token address, symbol, name, decimals, trade volume, and liquidity. You can use the token contract address as an ID to retrieve this data, making it easy to track and analyze token performance.

Here's a breakdown of the fields included in token data:

Token Data

Cryptocurrency Chart Displayed on a Laptop
Credit: pexels.com, Cryptocurrency Chart Displayed on a Laptop

Token data can be fetched using the token contract address as an ID. This is because token data is aggregated across all pairs the token is included in, making it easy to query.

In Uniswap, any token that is included in some pair can be queried. This means you can get the latest data on a specific token, even if it's part of multiple pairs.

Token data includes fields like name, symbol, and decimals, which can be fetched using the token contract address. The ABI of the token contract is used to bind the contract to its address and call public read-only functions, such as the getter functions for the token name and symbol.

Here are some key fields that make up token data:

This data can be used to track the performance and activity of a specific token, making it a valuable tool for investors and researchers.

TokenDayData

TokenDayData is a crucial component of token data, providing a comprehensive overview of a token's performance across all pairs that include it. It's like a daily report card for your token.

Cryptocurrencies Rate Chart
Credit: pexels.com, Cryptocurrencies Rate Chart

The data is aggregated across all pairs, giving you a broad understanding of the token's activity. This includes the token's address and day ID, which is a unique identifier for each day.

TokenDayData includes various metrics such as daily volume, daily transactions, and total liquidity. These metrics are essential for understanding the token's usage and popularity.

Here's a breakdown of the key metrics included in TokenDayData:

These metrics give you a clear picture of the token's trading activity and its value in different currencies.

Pair

The Pair entity is a crucial part of token data, containing information about a specific token pair. It's like a snapshot of the pair's status at a particular moment.

The Pair entity has a unique ID, which is actually the pair contract address. This ID is used to identify the pair and access its associated data.

The Pair entity also references the Uniswap factory entity, which is responsible for creating and managing the pair. This factory entity is like a central hub that oversees all the pairs in the system.

Laptop, bitcoins, and notes on a desk representing cryptocurrency investment concept.
Credit: pexels.com, Laptop, bitcoins, and notes on a desk representing cryptocurrency investment concept.

Each token in the pair is represented by a reference to its corresponding token entity. This means you can access detailed information about each token, such as its contract address, symbol, and decimals.

The Pair entity also tracks the reserves of each token, which is the amount of each token held in the pair. This information is crucial for calculating prices and volumes.

Here's a summary of the key fields in the Pair entity:

The Pair entity also provides information about the total supply of liquidity tokens distributed to liquidity providers, as well as the total liquidity in the pair stored as an amount of ETH and USD. This information is essential for understanding the pair's market dynamics.

Intriguing read: Uniswap Liquidity Pool

Subgraph Configuration

The manifest file is the entry point to our Uniswap V3 subgraph, specifying all the necessary parameters for indexing and querying. It's generated by graph-cli when we initialize the project.

This file contains crucial information, including the schema, data sources, and mappings of our subgraph. We need to make some tweaks to the generated manifest to include our new entities and ABI file details.

Here are the key changes we make to the manifest:

  • Adding details of our newly defined entities.
  • Including our new ABI file details.
  • Adding a startBlock property in the dataSources section.

Editing the Manifest

Blockchain Sign by Smartphone
Credit: pexels.com, Blockchain Sign by Smartphone

The manifest file, subgraph.yaml, is the entry point to our subgraph, specifying its parameters like schema, data sources, and mappings. It contains all the information required for indexing and querying our subgraph.

Graph-cli generates a manifest file when we initialize the project, but we need to make a few tweaks. This involves adding details of our newly defined entities and including a startBlock property in the dataSources section.

The manifest file should contain the ABI file details, which graph-cli will automatically fetch based on the contract address provided during initialization. This is stored in the /abis directory.

Here's what our new and improved manifest should look like:

  • Contains the details of our newly defined entities.
  • Added our new ABI file details.
  • Includes a startBlock property in the dataSources section.

This updated manifest will help us create a more accurate and efficient subgraph.

Subsquid

Subsquid is a Node.js application that can ingest blockchain data from two sources: archives and direct blockchain RPC endpoints. Archives are open source services that filter and forward data to squids, but setting up the Ethereum archive is still in alpha and outside the scope of this article.

Influencer Discussing Temporary Cryptocurrency Declines
Credit: pexels.com, Influencer Discussing Temporary Cryptocurrency Declines

The procedure for setting up a squid is straightforward, and the instructions in the README just work. A PostgreSQL database is required for caching blockchain data, and a database Docker container stores the data in a volume automatically created at the Docker data folder.

A small caveat is that the Docker data folder can fill up quickly, so it's essential to have at least 30Gb of free space available. I learned this the hard way and had to move the data folder to a drive with enough space.

To set up a squid, you'll also need to establish a connection to an Ethereum RPC endpoint over websocket. This can be done by setting up a private Ethereum beacon node or registering with a cloud endpoint provider.

Event Handling

Event handling is a crucial part of building a Uniswap v3 subgraph. It involves writing event handlers that contain the logic for sourcing data to our entities.

Blockchain Sign by Bitcoins
Credit: pexels.com, Blockchain Sign by Bitcoins

To write an event handler, we need to import all the required classes and code from the generated files, as shown in the uniswap-v-3-factory.ts file. We also use functions from the tokenUtils.ts file to fetch token details, passing the token address as a parameter.

The event handler function is defined as a function within the mapping file, and it accepts a single parameter called the event. The type of event is set to the event that it is handling, such as the PoolCreated event. This event is emitted by the UniswapV3Factory contract upon the creation of a new pool, carrying the details of the newly created pool.

Readers also liked: Uniswap Usdc Liquidity Pool

Event Handler Writing

Writing an event handler is a crucial step in event handling. It involves defining a function that will contain the logic for sourcing the data to our entities.

In the UniswapV3Factory contract, the event handler for the PoolCreated event is defined in the mapping file. This file is written in AssemblyScript, which closely resembles TypeScript syntax.

Bitcoin Gold Cryptocurrency Trading Chart
Credit: pexels.com, Bitcoin Gold Cryptocurrency Trading Chart

The event handler function, handlePoolCreated, accepts a single parameter called the event. The type of event is set to the event that it is handling, which in this case is the PoolCreated event.

To write the handler for the PoolCreated event, you'll need to open the uniswap-v-3-factory.ts file and replace the existing code with the new logic. This involves importing the required classes and code from the generated files.

The event handler function uses the functions from the tokenUtils.ts file to fetch the token details. You can see that while calling those functions, the address of the tokens is passed as parameters. The token address itself is given as an event parameter event.params.token0.toHexString().

While creating new entities, the address of the token and the pool are taken from the event parameters. It is then converted to a string with toHexString() and passed onto the class afterward in order to be stored as the ID of that particular entity.

The event handler function is where the magic happens, and the data from the blockchain is sourced to our entities. It's a critical component of event handling, and with the right logic, you can unlock a world of possibilities.

Swap

Free stock photo of blockchain, blockchain finance, blockchain network
Credit: pexels.com, Free stock photo of blockchain, blockchain finance, blockchain network

Handling swaps is a crucial part of event handling in Uniswap. Swaps are created for each token swap within a pair.

The Swap entity can be used to get information about a swap, such as the swap size in tokens and USD, the sender and recipient, and more. To get specific data about a swap, you can use the Swap entity's fields, like timestamp, pair, sender, and amount0In.

A Swap entity is created for each token swap within a pair, and it contains a reference to the transaction swap was included in. You can use this reference to get more information about the transaction.

The Swap entity's fields include timestamp, pair, sender, amount0In, amount0Out, and amountUSD. The timestamp field is used for sorted lookups, and the pair field references the pair that the swap occurred in.

Here's a summary of the Swap entity's fields:

Liquidity Position

In event handling, understanding a user's liquidity position is crucial for making informed decisions. The Liquidity Position entity is used to store data about a user's liquidity position.

Golden Bitcoin coins placed on a flat surface, symbolizing cryptocurrency and digital finance.
Credit: pexels.com, Golden Bitcoin coins placed on a flat surface, symbolizing cryptocurrency and digital finance.

This information is used in conjunction with data from the pair itself to provide position sizes, token deposits, and more. The Liquidity Position entity is made up of several key fields, including id, user, pair, and liquidityTokenBalance.

The id field is a unique identifier that is created by concatenating the user address and pair address with a dash. The user field is a reference to the user who has the liquidity position. The pair field is a reference to the pair that the liquidity is being provided on.

The liquidityTokenBalance field stores the amount of LP tokens minted for this position. This is a crucial piece of information for users who want to know how much liquidity they have in a particular pair.

Here's a breakdown of the fields in the Liquidity Position entity:

Deployment

Deployment is a crucial step in making your Uniswap V3 subgraph accessible to others.

To deploy your subgraph, you'll need to create a project in Subgraphs, if you haven't already.

Four Assorted Cryptocurrency Coins
Credit: pexels.com, Four Assorted Cryptocurrency Coins

First, click Add subgraph in Subgraphs. Then, in the Choose network section, select the network you want to deploy to.

Next, in the Create subgraph section, you'll need to provide some details about your subgraph.

Once your subgraph is created, scroll down to the part where it shows the subgraph Deployment command and copy the command.

You'll then need to open a terminal in your project directory and paste and run the deployment command.

The command will prompt you for the version labelling, so be sure to have that ready.

Once you've provided the version labelling, the command will automatically deploy your subgraph.

The time it takes for your subgraph to sync will depend on the number of blocks that you are trying to index and your code.

Here are the steps to deploy your subgraph in a concise list:

  1. Click Add subgraph in Subgraphs.
  2. Copy the subgraph Deployment command.
  3. Open a terminal in your project directory and paste the command.
  4. Provide the version labelling when prompted.

Querying the Subgraph

Querying the subgraph is a convenient way to access your Uniswap V3 subgraph data. You can do this by modeling a query and hitting the run button in the GraphQL UI URL.

Close-Up of a Smart Phone Screen Displaying a Cryptocurrency Stock Market Values
Credit: pexels.com, Close-Up of a Smart Phone Screen Displaying a Cryptocurrency Stock Market Values

To get started, you can use a query like the one shown in the example, which retrieves the first 10 liquidity pool details. This query is a great starting point for exploring your subgraph data.

To make requests to the Uniswap subgraph and receive data, you'll need to set up some middleware using a library like Apollo. This involves creating a GraphQL client to handle the requests.

Here's a list of the steps to create a GraphQL client:

  1. Add the imports shown below and instantiate a new client instance.
  2. Notice how we use the link to the Uniswap subgraph here.

Querying the Subgraph

Querying the subgraph is a convenient way to get data from your subgraph. You can use the GraphQL UI URL to query your subgraph by modeling a query and hitting the run button.

The GraphQL UI URL provides a straightforward way to get data. For example, you can use a query to get the first 10 liquidity pool details.

To make requests to the Uniswap subgraph and receive data, you'll need to set up some middleware. This involves using Apollo and creating a GraphQL client to handle requests.

Here are the steps to create a GraphQL client:

  1. Add the imports shown below and instantiate a new client instance.
  2. Notice how we use the link to the Uniswap subgraph here.

Query Using Curl

Shiny Cryptocurrency Coins on Reflective Surface
Credit: pexels.com, Shiny Cryptocurrency Coins on Reflective Surface

To query your subgraph using curl, you'll need to have curl installed in your system.

You can use the Query URL provided in the Subgraph query section of your Chainstack Subgraphs to query your subgraph.

First, make sure you have installed curl in your system.

Once you install curl, you can use the following command to query your subgraph:

Here's an interesting read: Is It Illegal to Use Uniswap in Usa

Displaying Data

To display the data from your Uniswap V3 subgraph, you'll need to hydrate the UI with the parsed response data. This involves creating loading states to detect when queries are still pending a response.

You can add two loading states, one for the Dai price and one for the Dai total liquidity, to detect when queries are still loading. These loading states may flicker fast because the time to query is fast.

Once the queries have finished loading, you can populate the UI with the real data. To do this, you'll need to add the following lines in the return function of your App.js file.

Close-up of two golden Bitcoin coins on a dark reflective surface, highlighting cryptocurrency symbolism.
Credit: pexels.com, Close-up of two golden Bitcoin coins on a dark reflective surface, highlighting cryptocurrency symbolism.

Here's a step-by-step guide to populate the UI with loaded data:

  1. Create loading states for the Dai price and Dai total liquidity.
  2. Populate the UI with the real data once the queries have finished loading.

By following these steps, you can effectively display the data from your Uniswap V3 subgraph in the UI.

Next Steps

Now that you've set up your Uniswap V3 subgraph and have a basic page rendering, it's time to take it to the next level. You can visit our analytics site to see a more advanced analytics page, or visit the GitHub for more detailed examples of using the Uniswap subgraph to create UIs.

To get started, you'll need to follow these steps:

  • Setup and Installs
  • Graphql Client
  • Writing the queries
  • Fetch data
  • Formatting Response
  • Displaying in the UI

Review your work carefully to ensure everything is in place before moving forward.

Matthew McKenzie

Lead Writer

Matthew McKenzie is a seasoned writer with a passion for finance and technology. He has honed his skills in crafting engaging content that educates and informs readers on various topics related to the stock market. Matthew's expertise lies in breaking down complex concepts into easily digestible information, making him a sought-after writer in the finance niche.

Love What You Read? Stay Updated!

Join our community for insights, tips, and more.