Developing on TheGraph
Blockchain technology can be difficult to navigate for those who are not familiar with it. One issue is retrieving data from the blockchain, which can be challenging due to the nature of blockchain as an event database. This is where TheGraph protocol comes in.
TheGraph protocol is a tool that listens to the blockchain for specific events that you want to track. You can specify the contract to listen to and when that contract is executed, TheGraph will take the data and store it in its database. You can then use GraphQL to query the data.
In this article, we will be using code examples and TheGraph’s hosted services (https://thegraph.com/hosted-service) which means that TheGraph hosts the code and it runs on their centralized servers. The code can be found in this GitHub repository: https://github.com/ingig/liminal.market.thegraph.blog.
What is liminal.market
Liminal.market is a platform that acts as a bridge between the traditional stock market and blockchain technology. It allows users to buy and sell stocks on the stock market using blockchain, enabling them to truly own the stock and benefit from dividends and voting rights. The platform’s goal is to bring the stock market to blockchain enthusiasts by making it more accessible and transparent.
Let’s start
To get started, we will create an empty project using npm as our package manager. First, we initialize npm by running the command “npm init.” in the terminal. Next, we install TheGraph CLI by running “npm install -g @graphprotocol/graph-cli.” Then, we run “graph init — product hosted-service” to set up the project for TheGraph’s hosted services.
The process will prompt us to select the network we are using (in this case, Ethereum as the project is running on the Polygon testnet), give our project a name (ingig/liminal-market-blog), and enter the contract address of the smart contract we want to track (0x6e9C29e416dc9F7A6A03ffebaB3f02Ef62a1baE4).
Note: if you are using the proxy pattern, make sure to use the contract address of the underlying contract and not the proxy contract when you want to generate the code, but you need to go use the proxy address when you want to listen.
It is important to note that the contract must be verified on the blockchain so that TheGraph can retrieve the ABI. This can be done during the contract deployment process.
After the project is set up, TheGraph will create the project folder with the following structure:
- abis: contains the ABIs for the contracts that are being indexed, in this case, one file LiminalMarket.json
- src: the location of our code
- tests: where our tests will be located. Tests are not covered in this blog post.
Networks.json
TheGraph protocol allows you to deploy to multiple networks. In this example, we have deployed to Mumbai, but when we want to move to the mainnet, we don’t need to create a new project. Instead, we can define the contract address and starting block in the networks.json file. The starting block is important because it allows you to specify the point from which TheGraph should begin indexing the blockchain, rather than going through all blocks from the beginning of time, which would take too long. This way, you can index only from the block where your contract was deployed.
As seen in the picture, you can set up one contract to listen to on two different networks, Mumbai and Matic (testnet and mainnet).
You can also listen to more than one contract as you can see in the picture above.
Package.json
Is your node.js project file
It has created 4 scripts that we will use, codegen, build, test and deploy.
Schema.graphql
This is our graphql schema. What do you want to store and query later? For this example, lets make a general info about the system
- Transaction count
- Number of orders executed
- Number of orders failed
- Symbol count
- List of all symbols
- balanceWei — balance represented in Wei
- Balance — balance represented in human readable format
- Date & time of last order
This is our object will look like
Subgraph.yaml
This is where we define the datasource of the graph.
In our case, we change the entities and eventHandlers
Entites is the same name we defined in schema.graphql LiminalMarketInfo
- event: TokenCreated(address,string)
handler: handleTokenCreated - event: OrderExecuted(address,string,uint256,uint256,uint256,string,uint256,uint256,uint256,address)
handler: handleOrderExecuted - event: OrderFailed(address,string,uint256,string,address)
handler: handleOrderFailed
These are the events we are listening to in our contract to be able to create our data. handler is the name of the function that will be executed when the event happens, those functions will be in ./src/liminal-market.ts as define in the file section
tsconfig.json
Is the config for typescript, although this is all written in AssemblyScript which is a subset of Typescript, the configuration is in the tsconfig.json file.
Generate the schema
Now that we have defined our schema in shema.graphql file, we can generate the object so we can use them in our code. Lets run npm run codegen
We can now start to code the logic
.src/liminal-market.ts
You can find the code here
https://github.com/ingig/liminal.market.thegraph.blog/blob/main/src/liminal-market.ts
handleTokenCreated
In this example, we start by handling the token creation in the function “handleTokenCreated” as specified in the subgraph.yaml file. Since a trade cannot be executed until a token exists, this function serves as the first entry point in our code.
To handle this, we create a new Symbol object, set its values, and then store it in the GraphQL database by calling the symbol.save() function. This function is automatically generated when you run “npm run codegen” command.
Additionally, I’ve created a DateHelper class to help with converting the block timestamp to Unix time (milliseconds from 1970), as the blockchain time is in seconds from 1970, but most systems use milliseconds.
When working with arrays in GraphQL, it is important to note that you cannot simply call the push function on the variable itself. Instead, you need to put the array into a variable, push to the end, and then load it back into the array in the object. This is the current method, but it may be improved in the future.
This is how it works today, hopefully be fixed in the future.
handleOrderExecuted
The function “handleOrderExecuted” is called when the “OrderExecuted” event occurs on the contract located at address 0x64dbAdEa7f4c9f962a2C3538c7901BFa29a88Ca1.
In order to understand what is happening in this function, it is necessary to review the code in LiminalMarket.sol and specifically the “executeOrder” function. The code is not too complicated, it finds the security or stock that the user bought, updates the quantity, and sets the aUSD balance. Once these updates have been made on the blockchain, the “OrderExecuted” event is emitted.
This event has the following properties: recipient, symbol, tsl, filledQty, filledAvgPrice, side, filledAt, commission, aUsdBalance, spender. To understand what these properties represent, it is recommended to check the LiminalMarket documentation. Check out for more information in our documentation
In the “handleOrderExecuted” function, we start by retrieving LiminalMarketInfo. If it is null, we need to create a new instance by calling the “getNewLiminalMarketInfo()” function, which creates a new object and initializes it. It is important to set all properties when creating an object.
Next, we load all variables. For buy orders, we increase the TSL, for sell orders we decrease it. Another helper method converts the Wei value into a human-readable format.
handleOrderFailed
It is a simple function that simply increases the failed order count on the liminalMarketInfo data and saves it.
Deploy project
To deploy the subgraph, go to the TheGraph dashboard (https://thegraph.com/hosted-service/dashboard) and click “Add Subgraph.” Fill in the required information and save it. After saving, you will receive information about your token that you will need to deploy.
To start the deployment process, you need to first authenticate by running the command “graph auth — product hosted-service [your access token].” This will allow you to access the TheGraph’s hosted service and deploy your subgraph.
After authentication, you can run the deployment command “npm run deploy” which will deploy the subgraph to TheGraph’s hosted service. The deployment should complete successfully and you can then open the subgraph and query the data using GraphQL by going to the url you deployed to. In my case it is https://thegraph.com/hosted-service/subgraph/ingig/liminal-market-blog
Query the data
Now you want to query the data, in the dashboard, TheGraph gives you an example of graphql that you can use.
{
liminalMarketInfos {
id
txCount
orderExecutedCount
orderFailedCount
}symbols(first: 5) {
id
logo
contract
txCount
}
}
Conclusion
In this example, we have used only a portion of the data available from the smart contract. However, if you take a look at the official Subgraph for Liminal Market or the code repository, you can see how all the data from smart contract events is used to build useful information that can be accessed on info.liminal.market.
You can even see the GraphQL that is retrieving the data if you click the View GraphQL link on each widget
TheGraph enables us to create valuable data from smart contracts without having to set up and maintain our own servers running GraphQL and blockchain listener. This is a significant benefit as it reduces the overhead and resources required to host and manage the data.