Web 3 Part II: How to index blockchain events using The Graph Protocol
For so many years the decentralised application (DAPP) development has suffered from data accessibility, querying a specific information or using directly data persisted on the blockchain used to be challenging until a team of three developers decided to build a data-indexing protocol for Web3 that would enable developers to create DAPPs, fully powered by public infrastructure known today as The Graph .
What is The Graph protocol?
As mentioned before, The Graph Protocol is a decentralized protocol for indexing and querying data from blockchains.
Prerequisites
In order to get the most of this tutorial and follow along, kindly read the part one of this series here. Head over the Graph website, create an account, select hosted services as deployment option and create a new subgraph project as follow:
Project setup
Create a new directory to contain the project.
mkdir ongama-nft-subgraph && cd ongama-nft-subgraph
Create a folder in the project directory called abi where you can add the NFT contract abi (NFT.json)
generated in the part one of this series after running truffle compile
Next, install the Graph CLI:
npm install -g @graphprotocol/graph-cli
Given we have already deployed our NFT Smart Contract on the testnet (Mumbai), we can run the following command to initialize the project:
graph init --from-contract <CONTRACT_ADDRESS> \
[--network <ETHEREUM_NETWORK>] \
[--abi <FILE>] \
<GITHUB_USER>/<SUBGRAPH_NAME> [<DIRECTORY>]
We are going to grab the contract address from the deployment we made in part one of this article, the init command should look as follow:
graph init --from-contract 0x0b689AC2f5e1D925A0441cE4B3E949f3A15bD0f4 --protocol ethereum \
--network testnet --contract-name NFT --index-events? Product for which to initialize › hosted-service
? Subgraph name › your-username/your-subgraph-project-name
? Directory to create the subgraph in › your-subgraph-project-name
? Ethereum network › mumbai
? Contract address › 0x0b689AC2f5e1D925A0441cE4B3E949f3A15bD0f4
? ABI file (path) ./abi/NFT.json
? Contract Name · NFT
This command will generate a basic subgraph project based of the contract address passed in as the argument to --from-contract
. By using this contract address, the CLI will initialize a few things in your project to get you started (including fetching the abis
and saving them in the abis directory).
By passing in --index-events
the CLI will automatically populate some code for us both in schema.graphql as well as src/mapping.ts based on the events emitted from the contract.
The subgraph codebase consists of a few files:
- subgraph.yaml: a YAML file containing the main configuration and definition of the subgraph
- schema.graphql: a GraphQL schema that defines what data is stored for your subgraph, and how to query it via GraphQL
- AssemblyScript Mappings: AssemblyScript code that translates from the event data in Ethereum to the entities defined in your schema (e.g. mapping.ts in this tutorial)
Project Configuration
As we can see, the subgraph.yaml file define the following:
name
: define the contract name, in this configuration we have NFT as contract name.network
: we specified Mumbai since it is the Polygon testnet network name.dataSources.source
: the address of the smart contract the subgraph sources, and the abi of the smart contract to use. In this case we have 0xB08347548b9DC9B1211D37913CE0f305FF477AcE.dataSources.source.startBlock
(optional): the number of the block that the data source starts indexing from. We are indexing from block number 25957921.dataSources.mapping.entities
: the entities that the data source writes to the store. The schema for each entity is defined in the the schema.graphql file. We are going to define the User, NFT & Activity entities inside the schema file.dataSources.mapping.abis
: one or more named ABI files for the source contract as well as any other smart contracts that you interact with from within the mappings.dataSources.mapping.eventHandlers
: lists the smart contract events this subgraph reacts to and the handlers in the mapping — ./src/mapping.ts in the example — that transform these events into entities in the store. Our contract only supports two events which we specified under eventHandlers section.
Schema Definition
The entities / data we will be indexing are the NFT
, Activity
and User
. This way we can index the NFTs information, their activities and users associated to them.
Let us update schema.graphql with the following code:
The trailing exclamation mark(!) is used to denote a field non-nullable and @derivedFrom
creates a virtual field on the entity that may be queried but cannot be set manually through the mappings API. Rather, it is derived from the relationship defined on the other entity.
For one-to-many relationships, the relationship should always be stored on the ‘one’ side, and the ‘many’ side should always be derived. We can generate the entities locally to start using in the mappings
created by running this command:
graph codegen
After executing the above command the Graph CLI generates AssemblyScript types from a combination of the subgraph’s GraphQL schema and the contract ABIs included in the data sources.
Mapping Implementation
Now, we are going to update src/mappings.ts by adding the implementation of our two handlers specified inside the configuration file.
These mappings will handle events for when a new NFT is minted and when its price gets updated. The mappings will make sure the data is saved into the subgraph when these events got fired.
Let us create a file under src
folder called util
which will contain the implementation of getOrCreateUser
and getNFTByID
functions.
Project Build
Next, let’s run a build to make sure that everything is configured properly. To do so, run the build
command:
graph build
If the build is successful, you should see a new build folder generated in your root directory.
Project Deployment
To deploy, you will first need to copy the Access token for your account, available in the Graph Explorer:
Next, run the following command:
$ graph auth
✔ Product for which to initialize · hosted-service
✔ Deploy key · ********************************yarn deploy
Once the subgraph is deployed, you should see it show up in your dashboard:
Your Graph endpoint should look like this:
https://thegraph.com/explorer/subgraph/your-username/your-subgraph-namehttps://api.thegraph.com/subgraphs/name/verdotte/ongama
Now, we should be able to start querying for data using various queries we can design based on our schema definition. For example:
{
nfts {
id
price
tokenID
tokenUri
owner {
id
}
creator {
id
}
activities {
price
type
txHash
timestamp
}
}
}
We can also add extra filtering params like:
{
activities (
where: { type: "MINT" },
orderBy: price,
orderDirection: desc,
first: 10,
skip: 100
) {
price
type
txHash
timestamp
}
}
Conclusion
Decentralised applications started to gain popularity among users. As the space continues to grow, DApp development, adoption and use is also expected to grow exponentially. This is where the Graph Protocol could play a big role for developers. By making blockchain data and APIs easily accessible, The Graph Protocol is becoming a goto option for recording blockchain data in a decentralised way.
Check out these resources for more details on Graph Protocol:
https://github.com/dabit3/building-a-subgraph-workshop
https://portal.gitnation.org/contents/building-graphql-apis-on-top-of-ethereum-with-the-graph