This article is the first part of a 3-piece series:
- The Graph and Its Contribution to dApps and Blockchain Development
- Demonstrate Subgraph Structure on 2key Subgraph “The Graph”
- Two Blockchains — One Referral Map To Rule Them (blockchains) All. Using 2 Subgraphs on 2key dApp
What is “The Graph”?
According to the description on the company’s site: “The Graph is a decentralized protocol for indexing and querying data from blockchains, starting with Ethereum. It makes it possible to query data that is difficult to query directly.”
But what does that actually mean?
“The Graph” lets you create “Subgraph” which is basically a filter that collects information from Ethereum blockchain according to pre-written conditions. It also allows you to easily index that information from the Blockchain and serve that indexed data using the GraphQL interface to your dApp/Web-App/Backend.
To deploy your own Subgraph you need two things:
- Contract address — The graph-node indexer searches for state-changing actions made from that address, such as events, set functions execution and new block.
- Indexing logic — For every state change spotted on the contract address in Step 1, the indexer needs to execute certain indexing logic. Therefore, we need to specify what to execute in the case of a specific action like when an “on campaign created” event is emitted, then run CampaignCreation handler.
After implementing the above, plus a short configuration you can launch your first Subgraph on TheGraph. Deployment lasts a couple of minutes for indexing the blockchain, varying from project to project depending on the indexing complexity. Then, you gain access to your newly created DB based on the indexing logic you defined.
- The hosted service that The Graph provides to host your Subgraph is, at the moment, centralized but will soon transition to a hybrid network.
During these ~4 minutes, a graph-node instance will traverse the blockchain from the first block until the last mined block. It will execute the indexing logic, which is defined in your Subgraph and continue to do so with every new block.
Short Glossary recap:
- Subgraph: The configurations that are later used by the graph-node to know what information to index and how.
- Graph-node: The engine that traverses the blockchain and indexes the information according to the Subgraph configuration.
- TheGraph: The project that lets you host your Subgraph configuration and serves the queries from their servers. Later, the serving will be a part of the global indexing network.
So what’s the big news?
When your project uses blockchain, it doesn’t save you the need to maintain lots of information on the backend. Due to the cost of Ethereum tx, you will prefer to keep the on-chain data to the minimum.
For each entity tracked on-chain the correlated information persists on the backend and increases exponentially.
Let’s take the 2key network’s campaign creation. For each campaign, a backend campaign record will be created saving the contractor address, type of campaign, fiat/Crypto as well as the tx stats, actor, etc.
How can all the data be persisted in such a way where we will have all the relevant information to provide our services in a scalable manner?
To tackle it we can choose several approaches:
Persist all related data on Backend and serve that data to the dApp
- Requires the developer to maintain lots of blockchain-related information on the backend, which introduces data engineering challenges.
- Requires a mapping between the backend entities and the correlated on-chain and off-chain entities. Lots of web3 entities lead to complex logic, having the potential to cause inconsistencies requiring fall-back mechanisms for malfunctions.
- Produces larger smart-contract interfaces.
- Leads to performance issues. For each tx, there are other multiple processes to make the data available to all your different services.
- Creates overhead communicating relevant state data between Backend <> Smart Contracts <> dApp <>Backend. It gets more complex with every piece of logic added to the contracts/dApp.
Depends solely on the blockchain state that queries it directly from the blockchain:
- Requires maintaining (development-wise) a thick interface to allow the dApp access to the contract’s state.
- Every tx costs gas, and every on-chain change increases that cost and gets you a lot of costly on-chain info.
- Performance-wise, when the on-chain tx status determines the possibilities a user can or can’t do on the dApp, it introduces a bottleneck due to the un-parallel state changes. It is followed by additional calls for the BE, validate state changes, calls to update entities and finally getting the actual state needed for the dApp to proceed.
Working with a Subgraph that indexes automatically and serves your service
As you see, the interaction with the smart contracts is minimal and mainly for set states. All of your smart-contract’s state is automatically updated and easily served by the GraphQL interface.
Example from 2key campaign contract
When a contractor creates a campaign in 2key network, behind the scenes a series of tx are triggered. At the end of them, a campaign contract will be created on-chain, and an event will be emitted with the information related to that campaign:
event AcquisitionCampaignCreated(
address proxyLogicHandler,
address proxyConversionHandler,
address proxyAcquisitionCampaign,
address proxyPurchasesHandler,
address contractor
);
(TwoKeyEventSource.sol contract repo)
Then, we add mapping logic. Each time a “Campaign creation” event is sent, we activate the mapping handler:
mapping:
entities:
- Campaign
abis:
- name: TwoKeyEventSource
file: ./abis/TwoKeyEventSource.json
eventHandlers:
- event: AcquisitionCampaignCreated(address,address,address,address,address)
handler: handleAcquisition(subgraph.yaml at Subgraph repo)
Now, after we deploy that Subgraph, each time an event is sent, the following function will create a campaign entity and store the information:
let campaign = new Campaign(
event.params.proxyAcquisitionCampaign.toHex()
);campaign._purchasesHandler=event.params.proxyPurchasesHandler;
campaign._conversionHandler=event.params.proxyConversionHandler;
campaign._logicHandler = event.params.proxyLogicHandler;
campaign._owner = event.params.contractor;
campaign._type = “Acquisition”;
campaign._timeStamp = event.block.timestamp;
campaign._updatedTimeStamp = event.block.timestamp;
campaign.save();(mapping.ts at Subgraph repo)
This is a simple case. The best practice is to create entities and connect them as campaign members like owner, create user entity, and then set it on campaign owner and not just the address.
After we have an event handler mapping and handler logic, we can deploy the Subgraph, and in around 3 minutes, it will re-index all the events that were fired from our event source contract and create a simple DB, which can be queried in GraphQL.
This “on the go smart contracts state DB” continues to update with every new block, but only if there’s relevant information on that block.
Now, you can easily query the contract’s current state.
The next step is to plan what which entity to track and how to persist the data from those on-chain events.
More from this series:
Demonstrate Subgraph Structure on 2key Subgraph “The Graph”
Two Blockchains — One Referral Map To Rule Them (blockchains) All. Using 2 Subgraphs on 2key dApp
Good word — Chapeau — for TheGraph team
During the journey of exploring their solution with all its aspects, from graph-node indexing power and hosted solution to setting our private infrastructure to run our own graph-node for Plasma blockchain, we receive a warm welcome at The Graph Discord channel. Every issue that popped up - and there were many of them :) - received a fast response and true desire to perfect their product.
A couple of times, we received a specific fix/patch for one of the libs after we reported an issue.
Thanks! It’s an honor to usher in the world of decentralization with projects like these ❤