What are NEAR Subgraphs?
The Graph is a blockchain technology that gives developers the ability to extract data in an easy and replicable way via a GraphQL API known as a subgraph. A subgraph defines the data The Graph will index from the NEAR blockchain and how it will store it.
Subgraphs consist of three pieces:
- a manifest - subgraph.yaml
- entities - schema.graphql
- mappings - mapping.ts (AssemblyScript)
We'll go into each of these in more detail throughout the rest of the course.
GraphQL is a query language for APIs and a runtime for fulfilling those queries with our existing data. It provides a complete and understandable description of the data in our APIs so people can ask for exactly what they need and nothing more.
With The Graph's integration with NEAR Protocol, Graph Nodes can now process NEAR activity. Graph Nodes are open source RUST implementations that listen to and process events from Ethereum and now receipts/blocks from NEAR to deterministically update a data store that can be queried via a GraphQL endpoint.
Up to now, the way most applications extract data from the NEAR blockchain is by querying the chain directly or using/running their own indexer. Querying a chain directly places load on RPC endpoints and is not highly performant. Typically, querying the chain directly also only facilitates simple queries built into the smart contract. Anything more complex requires an indexer which comes with its own complexities to setup and maintain. With The Graph integration, we can now easily build and share NEAR subgraphs to index our contracts and enable complex queries using the power of GraphQL.
Subgraphs are event-based meaning they listen for and then process on-chain events. For NEAR, there are currently two types of handlers that are supported:
- Block handlers - run on every new block; and
- Receipt handlers - run every time a message is executed on a specific account.
Blocks and Receipts
A receipt is the only actionable object in the NEAR system. If you hear mention of "processing a transaction" on NEAR it eventually means "applying receipts" at some point.
The NEAR nomicon describes a receipt as a paid message to be executed at its destination (receiver). A transaction is an externally issued request to create the receipt (1:1 relationship)
Receipts get generated in several ways:
- issuing a transaction
- returning a promise (related to cross-contract calls)
- issuing a refund
Receipts are stateful in a sense as they serve not only as messages between accounts but also can be stored in the account storage to await DataReceipts.
Every receipt has a predecessor_id (who sent it) and receiver_id (who it was sent to).
Receipts are either action receipts or data receipts. ActionReceipt is a request to apply Actions (CreateAccount, DeployContract, FunctionCall, Transfer, Stake, AddKey, DeleteKey, DeleteAccount). DataReceipt is a result of the application of these actions.
Receipts are generated during the execution of a SignedTransaction or during application of some ActionReceipt that contains a FunctionCall action. The result of the FunctionCall could be another ActionReceipt or a DataReceipt (returned data).
DataReceipts are currently not supported by The Graph, so we'll ignore them from here on out. It will be important to understand what fields the ActionReceipts provide.
ActionReceipt: a request to apply actions on the receiver_id side. It could be derived as a result of a Transaction execution or another ActionReceipt processing. Each ActionReceipt contains fields:
- signer_id - is the Account Id that signed the original transaction. If it's a refund, the account ID is system.
- signer_public_key - is the public key of an Access Key that was used to sign the original transaction. If it's a deposit refund, the public key is empty (all bytes are 0)
- gas_price - is the gas price that was set in a block where the original transaction was applied.
- output_data_receivers - if the smart contract finishes its execution with some value (not Promise), a DataReceipt is created for each of the output_data_receivers which are DataReceiver with a data_id containing a CryptoHash of data and a receiver_id.
- input_data_ids - are the receipt data dependencies. Each correspond to DataReceipt.data_id.
- actions - the eight listed earlier: CreateAccount, DeployContract, FunctionCall, Transfer, Stake, AddKey, DeleteKey, DeleteAccount
Of these, the actions and specifically the FunctionCall action will be where we focus our attention.
With an understanding of what NEAR subgraphs are and how they interact with ActionReceipts on the NEAR chain, we can move on to learning how to code logs in your NEAR contracts that will facilitate data indexing and extraction. On to the next lesson.
Maybe you'd rather have us build your NEAR subgraph for you?
Not a problem and it can be pretty quick and inexpensive depending on the complexity of your contract. Get in touch using one of the methods below.