Substreams for Injective Developers

With Substreams, you can extract data from the Injective blockchain. Then, you can consume the data in several ways, such as a subgraph, through gRPC streaming, using a SQL database and many more.

Getting Started

There are two main concepts in Substreams: packages and modules. Essentially, a module is a Rust function that contains definitions to extract data from the blockchain. Modules are grouped in packages, which are binary files (.spkg) that contain one or several modules. For example, you might have a module called injective-common, which has two modules: get_transactions and get_events.

Developing a Substreams modules requires some knowledge of Rust, but the StreamingFast team has already created several foundational modules, which extract the most basic data from the Injective blockchain, such as transactions or events. You can simply import these modules and start using them!

To consume Injective data with The Graph you have two options:

- Substreams-powered Subgraphs: you import a Substreams module into the subgraph. Essentially, Substreams acts just as the extraction layer, but the business logic lives in the subgraph. The Substreams only provides raw data (you use AssemblyScript to code your subgraph).

- Substreams directly: you develop your own Substreams module (you use Rust to code your Substreams).

Choosing one over the other is up to you. Consider the needs of your application and the skills needed to develop.

Use Substreams-powered Subgraphs

First, if you do not know what subgraphs are, please take a look at The Graph documentation.

Substreams can bridge Injective data to a subgraph through the Substreams triggers, which essentially allow you to import Substreams data into a subgraph. You can easily import any of the Injective Substreams Foundational Modules into your subgraph (for example, Injective transactions or events).

The following YAML file is the definition a subgraph that imports a Substreams module called all_events, which will stream all the events in the Injective blockchain.

specVersion: 1.0.0
  prune: auto
  file: ./schema.graphql
  - kind: substreams
    name: Events
    network: injective-mainnet
        file: injective-foundational-v0.1.0.spkg   # See point 1. below
        moduleName: all_events                     # See point 2. below
      apiVersion: 0.0.7
      kind: substreams/graph-entities
      file: ./src/mapping.ts
      handler: handleEvents
  1. The Substreams package you want to import into your subgraph.

  2. The module (contained within the package) that you want to consume data from.

Although developing a Substreams-powered Subgraph should be easy enough without knowing about the internal of Substreams, we still recommend to go through the following sections of the documentation:

Use Substreams Directly

If you don't want to use subgraphs, you can consume or develop a Substreams module.

First, you must consider whether you want to develop your own Substreams or consume a ready-to-use Substreams. It is possible that someone has already built a Substreams package to extract the data you want; you can explore Substreams packages in the Substreams Registry.

If you have found a Substreams package that fits your needs, then explore the Consume Substreams section. At the most basic level you should cover:

If you can't find a Substreams package that fits your needs, then you can go ahead and develop your own Substreams by writing a Rust program. The Develop Substreams section of the documentation covers everything you need to know about building a Substreams from scratch. At the most basic level, you should cover:

The Injective Data Model

Substreams provides you access to the raw full Injective block through a Protobuf schema. You can use the Block Protobuf to retrieve all the information contained in an Injective block, such as transactions or events.

Note: All Cosmos blockchains share the same data model, so the Block Protobuf used for Injective is the same for any other Cosmos blockchain.

You can use the Rust programming language to access this Block object and select which specific data you want to retrieve from the blockchain. For example, the following example receives the Block object as a parameter and returns a user-defined object, BlockStats.

pub fn block_to_stats(block: Block) -> Result<BlockStats, Error> { // See point 1. below
    let mut stats = BlockStats::default();                         // See point 2. below
    let header =  block.header.as_ref().unwrap();
    let last_block_id = header.last_block_id.as_ref().unwrap();

    stats.block_height = block.height as u64;                      // See point 3. below
    stats.block_hash = hex::encode(block.hash);
    stats.block_time = block.time;
    stats.block_proposer = hex::encode(&header.proposer_address);
    stats.parent_hash = hex::encode(&last_block_id.hash);
    stats.parent_height = block.height - 1i64;

    stats.num_txs = block.txs.len() as u64;

  1. Declaration of the Rust function. Input: Injective block. Output: BlockStats object, which is defined by the user and is consumable from the outside world.

  2. Creation of the BlockStats object.

  3. Add data from the Block Injective object to user-defined BlockStats object.

Next Steps

To start developing Injective Substreams, take a look at the BlockStats tutorial, which inspects the code of a very simple Substreams. It's the best way to get familiar with the Substreams concepts!

Last updated