Comment on page
Subgraph entity changes
Creating and Updating Subgraph entities
A common convention for Substreams-powered subgraph development is to implement a
graph_out
module emitting sf.substreams.sink.entity.v1.EntityChanges
events. When these events are consumed by the indexer, entities in the subgraph are updated accordingly.Here's a sample declaration of
graph_out
module in substreams.yaml
: - name: graph_out
kind: map
inputs:
- source: sf.substreams.v1.Clock
- store: store_eth_prices
mode: deltas
output:
type: proto:sf.substreams.sink.entity.v1.EntityChanges
This module is for a simple subgraph that tracks ETH/USD rate derived from Uniswap pools.
Clock
input source emits events every block and helps you keep track of the block number and timestamp.store_eth_prices
module stores derived ETH prices and in delta
mode itOur subgraph has a single entity:
# stores for USD calculations
type Bundle @entity {
id: ID!
# price of ETH in usd
ethPriceUSD: BigDecimal!
}
Corresponding Rust code for the
graph_out
module can look like this:use substreams_entity_change::pb::entity::EntityChanges;
use substreams_entity_change::tables::Tables;
#[substreams::handlers::map]
pub fn graph_out(clock: Clock, derived_eth_prices_deltas: Deltas<DeltaBigDecimal>) -> Result<EntityChanges, Error> {
let mut tables = Tables::new();
if clock.number == 12369621 {
tables
.create_row("Bundle", "1")
.set("ethPriceUSD", BigDecimal::zero());
}
for delta in derived_eth_prices_deltas.into_iter(){
tables.update_row("Bundle", "1").set("ethPriceUSD", delta.new_value);
}
Ok(tables.to_entity_changes())
}
Let's break it down.
substreams-entity-change
crate offers Tables
struct to work with the entities. First, we instantiate Tables
object:let mut tables = Tables::new();
Then we check if this is the first block of our substream and if so, we create the entity using
create_row
method.if clock.number == 12369621 {
tables
.create_row("Bundle", "1")
.set("ethPriceUSD", BigDecimal::zero());
}
create_row
takes two arguments: entity name and entity id. In our case, we use "Bundle" entity name - that's the entity we have defined in the subgraph schema.graphql
schema. We use "1" as id
. That's the only price that we will have.Note:
12369621
magic block number is used here for simplicity. Typically you would define it as a module parameter.Next, we iterate through all ETH price deltas within that block and update it in our table.
for delta in derived_eth_prices_deltas.into_iter(){
tables
.update_row("Bundle", "1")
.set("ethPriceUSD", delta.new_value);
}
Here, we use
update_row
method to create an UPDATE
entity operation, and we use set
method to set the corresponding entity field.One last step, we convert our
Tables
helper object into the EntityChanges
object that our subgraph can consume:Ok(tables.to_entity_changes())
Last modified 5mo ago