This article is a translation of this story.
I will try to tell you about my experience of creating a subgraph, what I learned and which difficulties I encountered.
Let’s start with the fact that I am a developer on Android OS, and before creating my subgraph I was not very familiar with the Solidity language in which Ethereum smart contracts are written, as well as the TypeScript language in which mappers for subgraphs are written, and also had not used GraphQl to create subgraphs before. Therefore I apologize in advance for any inaccuracies if there are any.
In this article I will not retell the official documentation about the construction of subgraphs, which you can find here. I will try to focus on my experience and what I learned in the process.
To begin with, I pulled an example project that already contains the main folders and files, you can find more information about it here.
I had the idea to create a subgraph for the DODO DEX (exchange and liquidity provide) project, since in the process of researching of the already created subgraphs for this project, I found that they create a separate subgraph for each pair, not using all the possibilities that the graph protocol provides.
I decided to try listening to the main events from the main contract of the factory and, when creating a new pair, add a new contract dynamically using templates.
Now in more detail. I found all smart contracts for the dodo project in their documentation. Their main contract is DODO Zoo, I went to the Etherscan abi section and copied its file Zoo.json to the abi folder, we will need it in the future.
In smart contracts, we are mainly interested in the events that these contracts can send, this is the main source of information for subgraphs. This contract has one main event DODOBirth, the code from the smart contract:
which is called every time a new pair is created, and we will track it when creating a subgraph.
Next, I set up the manifest using these data:
Address — the address of the contract of our factory
StartBlock — block number for creating a smart contract (usually the block number can be found in the first transaction in this contract), you can leave it blank, then synchronization will start from the very first block
abi: abi of this contract which we copied to the corresponding file
abis: we indicate the name and path to our Zoo abi, you can also see three other abis, these are auxiliary abis that I added in order to extract information about tokens, in this article we will not touch on this.
eventHandlers: the event that we track from the contract, together with its parameters that we can see in the smart contract and the handler is the handler for this event, which I will talk about a little later, and the path to the file where we registered the handler.
Next, in the schema.graphql file, I created the DODOPair and Token entities (there is an example of an entity token in the screenshot)
Next, I create file main.ts in the src package, the code of which looks something like this:
Here we write the code in TypeScript, since I worked with Java and other object-oriented languages, it was intuitively clear to me, sometimes I checked the tutorials, if you don’t understand what is going on here, I advise you to look at some basic lessons on typescript, I think this will be enough to build a subgraph.
The main point to which I would like to draw your attention, from above, we import the entities that we will create (entities from the schema) and receive (events) from the Ethereum network. And so I imported the event DODOBirth and it is an input parameter to our handleDododBirth handler function, and our method will be executed every time this event occurs in the Ethereum blockchain. Then we can get parameters from the event, this is done through event.params, in my case, I can get the addresses of the base token and quote token and, based on them, create the token objects that I declared in the schema.
Also pay attention to let mainStatistic = MainStatistic.load (FACTORY_ADDRESS). We are trying to get such an entity at the address, if it exists we will get it, if not, then it will be null, we do a check and if it is null then create a new object using the operator new.
This concludes the first part.
As I said above, the factory contract creates other contracts (contracts of pairs) that essentially have the same code, and in order not to write each such contract in the manifest, we can use data source templates for dynamically created contracts.
It is enough for us to load the abi of one of these contracts and create another file in the abi folder (in my case it is dodopair.ts) and insert the abi there.
Next, we update the manifest
we add a template, specify the abi for this template, the file where we declared the mapper for the events from this contract, the path to the abi and, accordingly, the events (in my subgraph I also process functions, but they may not be there) and their handler functions (the name of the handler and the functions in the mapper must match, otherwise we will get an error at the compilation stage).
Important: when processing DODOBirth in the mapper for the factory, one of the lines is DODOPairTemplate.create (event.params.newBorn), this line just takes the address of the newly created contract and creates a source based on the template.
Also note that in this template we process many more events than a factory, this is because these contracts are more complicated and have many events from which you can get useful data, you can find the contract code here.
Generally, this may be enough to try to deploy your own subgraph, and then gradually add more entities and complicate their structure.
I think this is it within the scope of this material, it turned out, probably, very chaotic and confusing, sorry — I have only little experience in writing articles and subgraphs. If there is anything, ask questions, I will be glad to answer. Hope this material was helpful to someone.
You can find the full code of my subgraph here