The Graph is an indexing protocol for organizing blockchain knowledge. It makes use of a GraphQL API to offer simpler entry to on-chain info than the normal technique of sending an RPC name.
The community organizes the info with subgraphs; open-source APIs which are created by the neighborhood and are used for retrieving knowledge from Indexers, Curators, and Delegators.
On this article, we’re going to be looking at how you should utilize The Graph and subgraphs for Web3 knowledge querying.
Indexers function the community nodes
Indexers function the nodes of the community, which index knowledge and serve the queries.
For the reason that Graph Community makes use of a proof-of-stake algorithm, indexers stake Graph Tokens (GRT) to offer indexing and question processing companies. In flip, Indexers can earn question charges and indexing rewards.
They choose subgraphs to index based mostly on the subgraph’s curation sign. Functions that eat the Indexers’ knowledge can set parameters for which Indexers they need to course of their queries, together with their preferences for question charge pricing.
Curators sign high-quality subgraphs
Curators manage knowledge from the subgraphs by signaling the subgraphs that needs to be listed by the Graph Community.
They do that utilizing Graph Curation Shares (GCS), which permit them to position the equal of an funding on a subgraph.
Curators stake GRT, which permits them to mint GCS. Every subgraph has a bonding curve that determines the connection between the worth of GRT and the variety of shares that may be minted.
Based on the Graph’s documentation, curating is taken into account dangerous and will solely be carried out after totally researching and investigating the trade-offs concerned.
Delegators safe the community by staking
Delegators stake GRT to a number of Indexers to assist safe the community with out having to run a node themselves.
Delegators earn parts of the Indexer’s question charges and rewards, that are depending on the Indexer’s and Delegator’s stake, together with the worth the Indexer prices for every question.
Allocating extra stake to an Indexer permits extra potential queries to be processed. The Graph’s documentation claims that being a Delegator carries much less danger than being a Curator as a result of they don’t seem to be uncovered to fluctuations within the worth of GRT, because of burning shares of GCS.
The Graph Basis
The Graph is developed and maintained by The Graph Basis. To ensure the community and bigger neighborhood proceed to enhance, the inspiration distributes grants (referred to as Graph Grants) to neighborhood members engaged on protocol infrastructure, tooling, dApps, subgraphs, and neighborhood constructing.
Three totally different graph companies
There are three alternative ways to work together with the Graph in case you are not internet hosting your individual subgraph:
- Graph Explorer: Discover totally different subgraphs and work together with the Graph protocol
- Subgraph Studio: Create, handle, and publish subgraphs and API keys utilizing Ethereum Mainnet
- Hosted Service: Create, handle, and publish subgraphs and API keys utilizing different networks other than Ethereum, reminiscent of Avalanche, Concord, Fantom, and Celo
The hosted service doesn’t require a crypto pockets and can be utilized with a GitHub account. The Graph Explorer and Subgraph Studio will each ask you to attach a pockets reminiscent of MetaMask or Coinbase.
Create a mission on Hosted Service
After creating an account on the hosted service, click on “My Dashboard” on the navigation bar to see your dashboard.
Click on “Add Subgraph” to create a subgraph.
Add a reputation and subtitle on your subgraph. When you’ve stuffed in your subgraph’s info, scroll right down to the underside of the web page and click on “Create subgraph”.
With our subgraph setup on the hosted service, we are able to create our mission recordsdata. Create a brand new listing, initialize a package deal.json
, and set up dependencies.
mkdir graphrocket cd graphrocket yarn init -y yarn add -D @graphprotocol/graph-cli @graphprotocol/graph-ts
Copy the entry token out there in your mission dashboard on Hosted Service. Paste the token after the yarn graph auth --product hosted-service
command.
yarn graph auth --product hosted-service YOURTOKEN
Create configuration recordsdata for TypeScript and Git.
Extra nice articles from LogRocket:
echo '{"extends": "@graphprotocol/graph-ts/sorts/tsconfig.base.json"}' > tsconfig.json echo '.DS_Storennode_modules' > .gitignore
Sensible contracts on the Ethereum blockchain expose an software binary interface (or ABI) as an interface between client-side functions and the Ethereum blockchain. We are going to want this for our subgraph.
Obtain the contract’s ABI with cURL and reserve it to a file referred to as Token.json
.
curl "http://api.etherscan.io/api?module=contract&motion=getabi&tackle=0xe7c29cba93ef8017c7824dd0f25923c38d08065c&format=uncooked" > Token.json
Create three mission recordsdata together with:
token.ts
for AssemblyScript code that interprets Ethereum occasion knowledge into the entities outlined within the schemesubgraph.yaml
for a YAML configuration of the subgraph’s manifestschema.graphql
for a GraphQL schema defining the info saved for the subgraph and the best way to question it through GraphQL
echo > token.ts echo > schema.graphql echo > subgraph.yaml
Outline Token
and Consumer
entities
In schema.graphql
we’ve outlined two sorts, Token
and Consumer
.
# schema.graphql kind Token @entity {} kind Consumer @entity {}
The Token
has a title
and different info reminiscent of when it was created, the content material URI, and the IPFS file path. It additionally contains details about the creator
and proprietor
.
# schema.graphql kind Token @entity { id: ID! tokenID: BigInt! contentURI: String tokenIPFSPath: String title: String! createdAtTimestamp: BigInt! creator: Consumer! proprietor: Consumer! }
The creator
and proprietor
are Consumer
sorts. They’ve an id
, an array of tokens
they personal, and an array of tokens
they’ve created
.
# schema.graphql kind Consumer @entity { id: ID! tokens: [Token!]! @derivedFrom(subject: "proprietor") created: [Token!]! @derivedFrom(subject: "creator") }
@derivedFrom
permits reverse lookups, which suggests we don’t retailer either side of the connection to enhance indexing and question efficiency. For one-to-many relationships, the connection needs to be saved on the “one” aspect with the “many” aspect derived from it.
Create subgraph
The subgraph.yaml
file will comprise the definition of our subgraph. Begin with the model of the specification used and a file path to the entity sorts in schema.graphql
.
# subgraph.yaml specVersion: 0.0.4 schema: file: ./schema.graphql
Subsequent is the community containing our knowledge sources. dataSources.supply
wants the tackle and ABI of the sensible contract.
# subgraph.yaml dataSources: - form: ethereum title: Token community: mainnet supply: tackle: "0x3B3ee1931Dc30C1957379FAc9aba94D1C48a5405" abi: Token startBlock: 11648721
dataSources.mapping.entities
defines the entities that the info supply writes to the shop and is specified by the schema in schema.graphql
.
# subgraph.yaml mapping: form: ethereum/occasions apiVersion: 0.0.5 language: wasm/assemblyscript entities: - Token - Consumer
dataSources.mapping.abis
takes the title
and file
location of the ABI for the supply contract.
# subgraph.yaml abis: - title: Token file: ./Token.json
dataSources.mapping.eventHandlers
lists the sensible contract occasions the subgraph reacts to and the handlers within the mappings that remodel these occasions into entities within the retailer.
# subgraph.yaml eventHandlers: - occasion: TokenIPFSPathUpdated(listed uint256,listed string,string) handler: handleTokenIPFSPathUpdated - occasion: Switch(listed tackle,listed tackle,listed uint256) handler: handleTransfer file: ./token.ts
Full subgraph.yaml
file:
# subgraph.yaml specVersion: 0.0.4 schema: file: ./schema.graphql dataSources: - form: ethereum title: Token community: mainnet supply: tackle: "0x3B3ee1931Dc30C1957379FAc9aba94D1C48a5405" abi: Token startBlock: 11648721 mapping: form: ethereum/occasions apiVersion: 0.0.5 language: wasm/assemblyscript entities: - Token - Consumer abis: - title: Token file: ./Token.json eventHandlers: - occasion: TokenIPFSPathUpdated(listed uint256,listed string,string) handler: handleTokenIPFSPathUpdated - occasion: Switch(listed tackle,listed tackle,listed uint256) handler: handleTransfer file: ./token.ts
Generate sorts
Generate AssemblyScript sorts for the ABI and the subgraph schema.
yarn graph codegen
Write mappings
Import the generated sorts and generated schema and create two features: handleTransfer
and handleTokenURIUpdated
.
When a brand new token is created, transferred, or up to date, an occasion is fired and the mappings save the info into the subgraph.
// token.ts import { TokenIPFSPathUpdated as TokenIPFSPathUpdatedEvent, Switch as TransferEvent, Token as TokenContract, } from "./generated/Token/Token" import { Token, Consumer } from './generated/schema' export perform handleTransfer(occasion: TransferEvent): void {} export perform handleTokenURIUpdated(occasion: TokenIPFSPathUpdatedEvent): void {}
handleTransfer
hundreds the tokenId
and units the proprietor
.
// token.ts export perform handleTransfer(occasion: TransferEvent): void { let token = Token.load(occasion.params.tokenId.toString()) if (!token) { token = new Token(occasion.params.tokenId.toString()) token.creator = occasion.params.to.toHexString() token.tokenID = occasion.params.tokenId let tokenContract = TokenContract.bind(occasion.tackle) token.contentURI = tokenContract.tokenURI(occasion.params.tokenId) token.tokenIPFSPath = tokenContract.getTokenIPFSPath(occasion.params.tokenId) token.title = tokenContract.title() token.createdAtTimestamp = occasion.block.timestamp } token.proprietor = occasion.params.to.toHexString() token.save() let consumer = Consumer.load(occasion.params.to.toHexString()) if (!consumer) { consumer = new Consumer(occasion.params.to.toHexString()) consumer.save() } }
handleTokenURIUpdated
updates the tokenIPFSPath
anytime it adjustments.
// token.ts export perform handleTokenURIUpdated(occasion: TokenIPFSPathUpdatedEvent): void { let token = Token.load(occasion.params.tokenId.toString()) if (!token) return token.tokenIPFSPath = occasion.params.tokenIPFSPath token.save() }
Deploy subgraph
Construct your mission for deployment:
yarn graph construct
Embody your individual GitHub username adopted by the title of your subgraph:
yarn graph deploy --product hosted-service USERNAME/logrocketgraph
The terminal will return a URL with an explorer for the subgraph and an API endpoint for sending queries.
Deployed to https://thegraph.com/explorer/subgraph/ajcwebdev/logrocketgraph Subgraph endpoints: Queries (HTTP): https://api.thegraph.com/subgraphs/title/ajcwebdev/logrocketgraph
You have to to attend on your subgraph to sync with the present state of the blockchain. As soon as the syncing is full, run the next question to indicate the primary two tokens ordered by id
in descending order.
{ tokens(first: 2, orderBy: id, orderDirection: desc) { id tokenID contentURI tokenIPFSPath } }
It will output the next:
{ "knowledge": { "tokens": [ { "id": "99999", "tokenID": "99999", "contentURI": "https://ipfs.foundation.app/ipfs/QmdDdmRAw8zgmN9iE23oz14a55oHGWtqBrR1RbFcFq4Abn/metadata.json", "tokenIPFSPath": "QmdDdmRAw8zgmN9iE23oz14a55oHGWtqBrR1RbFcFq4Abn/metadata.json" }, { "id": "99998", "tokenID": "99998", "contentURI": "https://ipfs.foundation.app/ipfs/QmZwZ5ChjHNwAS5rFDGkom2GpZvTau6xzr8M7gro5HqQhB/metadata.json", "tokenIPFSPath": "QmZwZ5ChjHNwAS5rFDGkom2GpZvTau6xzr8M7gro5HqQhB/metadata.json" } ] } }
Right here’s the question for the primary consumer and their related content material:
{ customers(first: 1, orderBy: id, orderDirection: desc) { id tokens { contentURI } } }
It will output the next:
{ "knowledge": { "customers": [ { "id": "0xfffff449f1a35eb0facca8d4659d8e15cf2f77ba", "tokens": [ { "contentURI": "https://ipfs.foundation.app/ipfs/QmVkXqo2hmC2j18udhZG1KavxaTGrnEX7uuddEbghPKCUW/metadata.json" }, { "contentURI": "https://ipfs.foundation.app/ipfs/QmTSEgtJmptBCpEJKubK6xDZFiCMEHgGQjhrUAsJSXwzKZ/metadata.json" }, { "contentURI": "https://ipfs.foundation.app/ipfs/QmPzSJGhheyyA7MZMYz7VngnZWN8TinH75PTP7M1HAedti/metadata.json" }, { "contentURI": "https://ipfs.foundation.app/ipfs/QmeroC2cWfdN31hLd3JpBQMbbWqnQdUdGx94FGUR4AGBUP/metadata.json" }, { "contentURI": "https://ipfs.foundation.app/ipfs/QmQVkhqEsZvsstfDp6QAPXB4TkxFnpeAc9BWu2eQo6QvZD/metadata.json" }, { "contentURI": "https://ipfs.foundation.app/ipfs/QmRax3fw4skHp95i2v3BzroMoKQVHqAkwbov8FyPdesk3j/metadata.json" }, { "contentURI": "https://ipfs.foundation.app/ipfs/QmViGRnvHFBZ6CWHoxZGJoU9iwnoGwZfqj2vgDN3dgsGv4/metadata.json" }, { "contentURI": "https://ipfs.foundation.app/ipfs/QmdRBPxDF1tUzm1Pczyme24vguUjW28cLwM4n9MvtxAWX6/metadata.json" } ] } ] } }
The question for the 2 most lately created NFTs.
{ tokens( first: 2, orderBy: createdAtTimestamp, orderDirection: desc ) { id tokenID contentURI createdAtTimestamp } }
It will output the next:
{ "knowledge": { "tokens": [ { "id": "133012", "tokenID": "133012", "contentURI": "https://ipfs.foundation.app/ipfs/QmSmk85TjpaegCmHDRWZQqMz18vJtACZdugVx5a1tmfjpv/metadata.json", "createdAtTimestamp": "1656792769" }, { "id": "133011", "tokenID": "133011", "contentURI": "https://ipfs.foundation.app/ipfs/QmU6RFcKFDteUTipg5tg4NFkWKApdVbo9oq9UYMtSmcWVe/metadata.json", "createdAtTimestamp": "1653825764" } ] } }
You can even use the HTTP endpoint and ship GraphQL queries instantly with cURL.
curl --header 'content-type: software/json' --url 'https://api.thegraph.com/subgraphs/title/ajcwebdev/logrocketgraph' --data '{"question":"{ tokens(first: 1) { contentURI tokenIPFSPath } }"}'
Conclusion
On this article, we now have seen the best way to create a GraphQL endpoint that exposes sure info contained on the Ethereum blockchain. By writing a schema containing our entities, we outlined the data that will probably be listed by the subgraph.
The amount and variety of blockchain networks proceed to proliferate in Web3. Having a standardized and extensively adopted question language will allow builders to iterate and check their functions with better effectivity.
Be part of organizations like Bitso and Coinsquare who use LogRocket to proactively monitor their Web3 apps
Consumer-side points that influence customers’ capability to activate and transact in your apps can drastically have an effect on your backside line. Should you’re excited by monitoring UX points, routinely surfacing JavaScript errors, and monitoring gradual community requests and element load time, attempt LogRocket.https://logrocket.com/signup/
LogRocket is sort of a DVR for internet and cellular apps, recording every part that occurs in your internet app or web site. As an alternative of guessing why issues occur, you may mixture and report on key frontend efficiency metrics, replay consumer classes together with software state, log community requests, and routinely floor all errors.
Modernize the way you debug internet and cellular apps — Begin monitoring without cost.