Skip to content

Commit 5bf0d98

Browse files
authored
Substreams-powered subgraphs cookbook (#401)
* substreams cookbook * Update route-lockfile.txt * toml, not js config * no "graph build" * substreams cookbook: update to dedicated example * substreams cookbook: small fixes * examples: comments and mermaid diagram * examples: use package.json commands * examples: reference subgraph on main branch
1 parent 1243ba9 commit 5bf0d98

File tree

3 files changed

+215
-0
lines changed

3 files changed

+215
-0
lines changed

website/pages/en/cookbook/_meta.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ export default {
88
arweave: '',
99
grafting: '',
1010
'subgraph-uncrashable': '',
11+
'substreams-powered-subgraphs': '',
1112
}
Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
---
2+
title: Substreams-powered subgraphs
3+
---
4+
5+
[Substreams](/substreams/README) is a new framework for processing blockchain data, developed by StreamingFast for The Graph Network. A substreams modules can output entity changes, which are compatible with Subgraph entities. A subgraph can use such a Substreams module as a data source, bringing the indexing speed and additional data of Substreams to subgraph developers.
6+
7+
> This cookbook uses this [Substreams-powered subgraph as a reference](https://github.com/graphprotocol/graph-tooling/tree/main/examples/substreams-powered-subgraph).
8+
9+
## Requirements
10+
11+
This cookbook requires [yarn](https://yarnpkg.com/) as well as [the dependencies necessary for local Substreams development](https://substreams.streamingfast.io/developers-guide/installation-requirements).
12+
13+
## Defining a Substreams package
14+
15+
A Substreams package is composed of types (defined as [Protocol Buffers](https://protobuf.dev/)), modules (written in Rust), and a `substreams.yaml` file which references the types, and specifies how modules are triggered. [Learn more about Substreams development](/substreams/README).
16+
17+
The Substreams package in question detects contract deployments on Mainnet Ethereum, tracking the creation block and timestamp for all newly deployed contracts. To do this, there is a dedicated `Contract` type in `/proto/example.proto` ([learn more about defining Protocol Buffers](https://protobuf.dev/programming-guides/proto3/#simple)):
18+
19+
```proto
20+
syntax = "proto3";
21+
22+
package example;
23+
24+
message Contracts {
25+
repeated Contract contracts = 1;
26+
}
27+
28+
message Contract {
29+
string address = 1;
30+
uint64 blockNumber = 2;
31+
string timestamp = 3;
32+
uint64 ordinal = 4;
33+
}
34+
```
35+
36+
The core logic of the Substreams package is a `map_contract` module in `lib.rs`, which processes every block, filtering for Create calls which did not revert, returning `Contracts`:
37+
38+
```
39+
#[substreams::handlers::map]
40+
fn map_contract(block: eth::v2::Block) -> Result<Contracts, substreams::errors::Error> {
41+
let contracts = block
42+
.transactions()
43+
.flat_map(|tx| {
44+
tx.calls
45+
.iter()
46+
.filter(|call| !call.state_reverted)
47+
.filter(|call| call.call_type == eth::v2::CallType::Create as i32)
48+
.map(|call| Contract {
49+
address: format!("0x{}", Hex(&call.address)),
50+
block_number: block.number,
51+
timestamp: block.timestamp_seconds().to_string(),
52+
ordinal: tx.begin_ordinal,
53+
})
54+
})
55+
.collect();
56+
Ok(Contracts { contracts })
57+
}
58+
```
59+
60+
A Substreams package can be used by a subgraph as long as it has a module which outputs compatible entity changes. The example Substreams package has an additional `graph_out` module in `lib.rs` which returns a `substreams_entity_change::pb::entity::EntityChanges` output, which can be processed by Graph Node.
61+
62+
> The `substreams_entity_change` crate also has a dedicated `Tables` function for simply generating entity changes ([documentation](https://docs.rs/substreams-entity-change/1.2.2/substreams_entity_change/tables/index.html)). The Entity Changes generated must be compatible with the `schema.graphql` entities defined in the `subgraph.graphql` of the corresponding subgraph.
63+
64+
```
65+
#[substreams::handlers::map]
66+
pub fn graph_out(contracts: Contracts) -> Result<EntityChanges, substreams::errors::Error> {
67+
// hash map of name to a table
68+
let mut tables = Tables::new();
69+
70+
for contract in contracts.contracts.into_iter() {
71+
tables
72+
.create_row("Contract", contract.address)
73+
.set("timestamp", contract.timestamp)
74+
.set("blockNumber", contract.block_number);
75+
}
76+
77+
Ok(tables.to_entity_changes())
78+
}
79+
```
80+
81+
These types and modules are pulled together in `substreams.yaml`:
82+
83+
```
84+
specVersion: v0.1.0
85+
package:
86+
name: 'substreams_test' # the name to be used in the .spkg
87+
version: v1.0.1 # the version to use when creating the .spkg
88+
89+
imports: # dependencies
90+
entity: https://github.com/streamingfast/substreams-entity-change/releases/download/v0.2.1/substreams-entity-change-v0.2.1.spkg
91+
92+
protobuf: # specifies custom types for use by Substreams modules
93+
files:
94+
- example.proto
95+
importPaths:
96+
- ./proto
97+
98+
binaries:
99+
default:
100+
type: wasm/rust-v1
101+
file: ./target/wasm32-unknown-unknown/release/substreams.wasm
102+
103+
modules: # specify modules with their inputs and outputs.
104+
- name: map_contract
105+
kind: map
106+
inputs:
107+
- source: sf.ethereum.type.v2.Block
108+
output:
109+
type: proto:test.Contracts
110+
111+
- name: graph_out
112+
kind: map
113+
inputs:
114+
- map: map_contract
115+
output:
116+
type: proto:substreams.entity.v1.EntityChanges # this type can be consumed by Graph Node
117+
118+
```
119+
120+
You can check the overall "flow" from a Block, to `map_contract` to `graph_out` by running `substreams graph`:
121+
122+
```mermaid
123+
graph TD;
124+
map_contract[map: map_contract];
125+
sf.ethereum.type.v2.Block[source: sf.ethereum.type.v2.Block] --> map_contract;
126+
graph_out[map: graph_out];
127+
map_contract --> graph_out;
128+
```
129+
130+
To prepare this Substreams package for consumption by a subgraph, you must run the following commands:
131+
132+
```bash
133+
yarn substreams:protogen # generates types in /src/pb
134+
yarn substreams:build # builds the substreams
135+
yarn substreams:package # packages the substreams in a .spkg file
136+
137+
# alternatively, yarn substreams:prepare calls all of the above commands
138+
```
139+
140+
> These scripts are defined in the `package.json` file if you want to understand the underlying substreams commands
141+
142+
This generates a `spkg` file based on the package name and version from `substreams.yaml`. The `spkg` file has all the information which Graph Node needs to ingest this Substreams package.
143+
144+
> If you update the Substreams package, depending on the changes you make, you may need to run some or all of the above commands so that the `spkg` is up to date.
145+
146+
## Defining a Substreams-powered subgraph
147+
148+
Substreams-powered subgraphs introduce a new `kind` of data source, "substreams". Such subgraphs can only have one data source. This data source must specify the Substreams network, the Substreams package (`spkg`) as a relative file location, and the module within that Substreams package which produces subgraph-compatible entity changes (in this case `map_entity_changes`, from the Substreams package above). The mapping is specified, but simply identifies the mapping kind ("substreams/graph-entities") and the apiVersion.
149+
150+
```yaml
151+
specVersion: 0.0.4
152+
description: Ethereum Contract Tracking Subgraph (powered by Substreams)
153+
repository: https://github.com/graphprotocol/graph-tooling
154+
schema:
155+
file: schema.graphql
156+
dataSources:
157+
- kind: substreams
158+
name: substream_test
159+
network: mainnet
160+
source:
161+
package:
162+
moduleName: graph_out
163+
file: substreams-test-v1.0.1.spkg
164+
mapping:
165+
kind: substreams/graph-entities
166+
apiVersion: 0.0.5
167+
```
168+
169+
The `subgraph.yaml` also references a schema file. The requirements for this file are unchanged, but the entities specified must be compatible with the entity changes produced by the Substreams module referenced in the `subgraph.yaml`.
170+
171+
```graphql
172+
type Contract @entity {
173+
id: ID!
174+
175+
"The timestamp when the contract was deployed"
176+
timestamp: String!
177+
178+
"The block number of the contract deployment"
179+
blockNumber: BigInt!
180+
}
181+
```
182+
183+
Given the above, subgraph developers can use Graph CLI to deploy this Substreams-powered subgraph.
184+
185+
> Substreams-powered subgraphs indexing mainnet Ethereum can be deployed to the [Subgraph Studio](https://thegraph.com/studio/).
186+
187+
```bash
188+
yarn install # install graph-cli
189+
yarn subgraph:build # build the subgraph
190+
yarn subgraph:deploy # deploy the subgraph
191+
```
192+
193+
That's it! You have built and deployed a Substreams-powered subgraph.
194+
195+
## Serving Substreams-powered subgraphs
196+
197+
In order to serve Substreams-powered subgraphs, Graph Node must be configured with a Substreams provider for the relevant network, as well as a Firehose or RPC to track the chain head. These providers can be configured via a `config.toml` file:
198+
199+
```toml
200+
[chains.mainnet]
201+
shard = "main"
202+
protocol = "ethereum"
203+
provider = [
204+
{ label = "substreams-provider-mainnet",
205+
details = { type = "substreams",
206+
url = "https://mainnet-substreams-url.grpc.substreams.io/",
207+
token = "exampletokenhere" }},
208+
{ label = "firehose-provider-mainnet",
209+
details = { type = "firehose",
210+
url = "https://mainnet-firehose-url.grpc.firehose.io/",
211+
token = "exampletokenhere" }},
212+
]
213+
```

website/route-lockfile.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@
116116
/en/cookbook/quick-start/
117117
/en/cookbook/subgraph-debug-forking/
118118
/en/cookbook/subgraph-uncrashable/
119+
/en/cookbook/substreams-powered-subgraphs/
119120
/en/deploying/deploying-a-subgraph-to-hosted/
120121
/en/deploying/deploying-a-subgraph-to-studio/
121122
/en/deploying/hosted-service/

0 commit comments

Comments
 (0)