Start Subgraph Development
Let's start by creating a subgraph with commandgraph init --product hosted-service <GITHUB_USERNAME>/<SUBGRAPH_NAME> . This will ask for smart contract address to be used to initialize the subgraph.
For this example we want to develop subgraph for UniswapV2Pair.sol so we find an instance of this contract on Etherscan and use that as input to above command. We found 0x00040a7ebfc9f6fbce4d23bd66b79a603ba1c323 and will use it as input.
This command will also ask for other things like network, name of the contract. Please give a suitable value and wait for it to finish.
Once this command finishes you will have a working subgraph ready in <SUBGRAPH_NAME> directory. This subgraph does not index anything as it's schema and mapping code are empty. it's just a simple template that helps us with subgraph manifest file and ABI for above contract.
If we wanted to index only one SushiSwap pair then we could start filling in schema.graphql and mapping.ts but we need to track all the pairs of SushiSwap exchange. Let's figure out if we can automate the process of creating a subgraph of every pair or we need to add a source entry for every pair manually in the manifest file subgraph.yaml.
Looking at smart contract of SushiSwap we find that there is UniswapV2Facotry.sol this contract is used to deploy a new exchange pair. This contract also emits an event PairCreated(address indexed token0, address indexed token1, address pair, uint) that we can subscribe to create dynamic subgraph sources for every pair deployed.
Please note that there are protocols which don't have a factory contract. For such protocols we need to add a source entry for every market manually in manifest file like we did for curve exchange subgraph

Create a source for factory contract

Let's add following to the subgraph.yaml just below dataSources key.
We assume knowledge of subgraph manifest, please read subgraph documentation at https://thegraph.com/docs/developer/create-subgraph-hosted#the-subgraph-manifest for more information on manifest file.
1
- kind: ethereum/contract
2
name: UniswapV2Factory
3
network: mainnet
4
source:
5
address: "0xC0AEe478e3658e2610c5F7A4A2E1777cE9e4f2Ac"
6
abi: UniswapV2Factory
7
startBlock: 10794228
8
mapping:
9
kind: ethereum/events
10
apiVersion: 0.0.4
11
language: wasm/assemblyscript
12
entities:
13
- Block
14
- Account
15
- Token
16
- Market
17
- Pair
18
abis:
19
- name: ERC20
20
file: ./abis/IERC20.json
21
- name: UniswapV2Factory
22
file: ./abis/IUniswapV2Factory.json
23
eventHandlers:
24
- event: PairCreated(indexed address,indexed address,address,uint256)
25
handler: handlePairCreated
26
file: ./src/uniswapV2Factory.ts
Copied!
The address here is the address of SushiSwap factory contract and the startBlock is the block number of the block in which this factory contract was deployed.
This entry tells subgraph indexer node to listen to PairCreated event on factory contract deployed at address 0xC0AEe478e3658e2610c5F7A4A2E1777cE9e4f2Ac and trigger execution of handler function handlePairCreated in mapping file uniswapV2Factory.ts. We will also need to add ABI json files for both IERC20 token and IUniswapV2Factory.json contracts. We can get these ABIs from Etherscan or by compiling smart contract in SushiSwap github repository.
Please make sure that you use correct ABI json files because if you use an ABI generated from different version/fork of contract then it may have different function and event signatures which will cause trouble in mapping code. To avoid any such issues we recommend downloading ABI json from etherscan from contract page itself because the ABI at contract page is always in sync with the current version of deployed contract.

Implement factory contract event handler

Let's create a file named uniswapV2Factory.ts in src directory of subgraph project and start implementing the handler function with following line
1
export function handlePairCreated(event: PairCreated): void {}
Copied!
We will notice that this PairCreated is showing an error because our IDE (we use vs code for subgraph development) can not find definition of this class. Good news is we don't need to create classes for every event of contract. Graph CLI tool has a command codegen that can generate all required class definitions for us from the given ABI.
Before we run this codegen command we also need to copy entities from common subgraph schema to schema.graphql so that codegen can also generate class definitions for these entities. So let' go ahead an copy code from Common Subgraph Schema to graphql file. Now run following command to generate sources for ABIs and graphql entities
1
npm run codegen
Copied!
It will create a directory named generated in subgraph directory and this will have one file for each ABI and one file for schema. All these files are webassembly code files and are defining the class definitions of smart contract functions, events, input and output arguments along with graphql entities. We will be using these classes directly in our mapping code to keep it type safe and avoid runtime errors.
Yes yes it's a lot of work before we event start with the real indexing code development but it only one time setup, once done we just need to keep repeating following steps -
  1. 1.
    Update schem and mapping code
  2. 2.
    Deploy subgraph
  3. 3.
    Verify correctness of subgraph data
  4. 4.
    Debug subgraph in case of error or incorrect data
  5. 5.
    Update schem and mapping code ...
First let's think what we want to do in the handler of the event PairCreated . We want to create an entry of Market entity and create a Pair template source to listen to UniswapV2Pair.sol contract events. We will write following code in the handler function to create Market and Token entities that the Market entity links to -
1
// Create a tokens and market entity
2
let token0 = getOrCreateERC20Token(event, event.params.token0)
3
let token1 = getOrCreateERC20Token(event, event.params.token1)
4
let lpToken = getOrCreateERC20Token(event, event.params.pair)
5
6
let market = getOrCreateMarket(
7
event,
8
event.params.pair,
9
ProtocolName.SUSHISWAP,
10
ProtocolType.EXCHANGE,
11
[token0, token1],
12
lpToken,
13
[]
14
)
15
16
lpToken.mintedByMarket = market.id
17
lpToken.save()
Copied!
As you can see from code above it's really easy to create entities from SimpleFi common subgraph schema using function imported from SimpleFi mapping code library. Let's explain the code line by line.
Line number 2, 3 and 4 are simply calling getOrCreateERC20 function of library code which will internally create a record of Token entity and populate it's attribute like symbol, name and decimals by making contract calls. It will take care of not creating the record twice if it already exists.
Statement starting at line number 6 calls getOrCreateMarket function of library code which will internally create a Market entity and initialize it's attributes to zero values so that further event handlers can update these values without worrying about these attributes being null. You can check code for both function in common.ts.
We also need to copy both common.ts and constants.ts from SimpleFi library code in src directory. Then addProtocolName.SUSHISWAP enum value in both schema.graphql and constants.ts. We will also need to import required functions and entities from common.ts and constants.ts with following code -
1
import {
2
PairCreated
3
} from "../generated/UniswapV2Factory/UniswapV2Factory"
4
import {
5
getOrCreateAccount,
6
getOrCreateERC20Token,
7
getOrCreateMarket
8
} from "./common"
9
import { ProtocolName, ProtocolType } from "./constants"
Copied!
You will notice that we are using suffix Entity to the imported entity classes, it's just a convention that we follow to avoid confusion between schema entity classes and smart contract classes which often have same names like PairEntity.

Add a Pair template in manifest file

Now if you have been following this guide line by line then you should have two kind entries in dataSources key in subgraph.yaml. One was created automatically when we initialized the subgraph with graph init ... command and one we added above. In this step we need to remove the one that was created automatically because we are converting that to a template. After removing this kind entry we add following to the subgraph.yaml at the end without any tabs (templates is a top level key in manifest just like dataSources) -
1
templates:
2
- kind: ethereum/contract
3
name: UniswapV2Pair
4
network: mainnet
5
source:
6
abi: UniswapV2Pair
7
mapping:
8
kind: ethereum/events
9
apiVersion: 0.0.4
10
language: wasm/assemblyscript
11
file: ./src/uniswapV2Pair.ts
12
entities:
13
- Block
14
- Account
15
- Token
16
- Market
17
- Transaction
18
- Transfer
19
- Position
20
- PositionSnapshot
21
- Pair
22
- PairSnapshot
23
abis:
24
- name: ERC20
25
file: ./abis/IERC20.json
26
- name: UniswapV2Factory
27
file: ./abis/IUniswapV2Factory.json
28
- name: UniswapV2Pair
29
file: ./abis/IUniswapV2Pair.json
30
eventHandlers:
31
- event: Transfer(indexed address,indexed address,uint256)
32
handler: handleTransfer
33
- event: Sync(uint112,uint112)
34
handler: handleSync
35
- event: Mint(indexed address,uint256,uint256)
36
handler: handleMint
37
- event: Burn(indexed address,uint256,uint256,indexed address)
38
handler: handleBurn
Copied!
You can clearly notice that there is no address and startBlock keys in this template kind entry. That's because subgraph does not start the source from this template on starting the subgraph. We need to create the source from this template in our mapping code.
Before we do that we need to run the codegen command again to generate classes for this newly added template.
Please make sure that the final subgraph.yamllooks like the one in our repository here
We also need to remove the generated directory before running codegen command again because we have reorganized the source entries in our manifest file. Otherwise there will be two files with same name and same sources some ABIs and we may import the wrong one by mistake. It's not going to cause trouble till only one developer is working on the subgraph but when someone else clones the repository and runs codegen then they won't get the older (before reorganization) files and their mapping code will not compile. We don't commit generated files in the git repository.

Create a source from Pair template

We just need to add a single like of code in our handler function for PairCreated event in uniswapV2Factory.ts -
1
// Start listening for market events
2
UniswapV2Pair.create(event.params.pair)
Copied!
As always if only writing code was so simple, we also need to import the template class with following statement -
1
import { UniswapV2Pair } from "../generated/templates"
Copied!
y0 guys we have successfully created a subgraph that we can deploy and check if it's working as expected. Was not too tough was it? Wait till you get to the next article. Just kidding
😉
Some of you may have noticed that we did not talk the code that creates the Pair entity. We will talk about it in our next article after we explain the reason for creating this entity.