3. Substream Package Structure
Last updated
Last updated
In this section, we outline the standard approach for structuring a Virtual Machine (VM) integration:
Before proceeding, familiarize yourself with map modules and store modules. Learn more here.
Protocols commonly use factory contracts to deploy contracts for each swap component (often called pools or pairs). We’ll suggest a substreams package structure to efficiently index these contract systems. The methods here are generally applicable, so even if a protocol doesn't use this exact structure, you should still be able to index it and generate the required messages.
The examples and code snippets below are drawn from the Ethereum-Balancer substream, with the full code available here.
Typically, an integration comprises the following modules:
map_components(block)
This module identifies any new components by inspecting the block model (e.g., factory contract logs). The recommended output model is BlockTransactionProtocolComponents
.
store_components(components, components_store)
This module stores information about detected components for downstream modules. For VM integrations, the address is usually sufficient.
map_relative_balances(block, components_store)
:
This module is needed for protocols that don’t emit absolute balance changes (for ERC20 tokens or the native token). If only balance deltas are available, our SDK provides helpers to convert these into absolute balances for our data model. This module extracts relative changes and communicates them, with BlockBalanceDeltas
as the recommended output model.
store_balances(balance_deltas, balance_store)
:
This module stores the relative balance deltas in an additive store, converting them into absolute balances.
map_protocol_changes(balance_deltas, balance_store, components_store, ...)
:
This module integrates all data to build the final output model, BlockChanges
.
The structure of these modules forms a directed acyclic graph (DAG), shown below: