This guide explains how to consume data from a Foundational Store in your Substreams modules. Foundational Stores provide efficient access to pre-processed blockchain data for building complex data processing pipelines.
What is a Foundational Store?
A Foundational Store is a high-performance, multi-backend key-value storage system designed for Substreams ingestion and serving. It provides:
Multiple backends: Supports Badger (embedded) and PostgreSQL
Block-level versioning: Every entry tagged with block number for historical queries
High-performance serving: gRPC API for data retrieval
Streaming ingestion: Continuous processing via Substreams sink
Foundational Stores are typically populated by Substreams modules that extract and transform blockchain data, then serve that data to other Substreams modules for efficient lookups.
Consuming a Foundational Store
To consume data from a Foundational Store in your Substreams module:
use substreams::store::FoundationalStore;
#[substreams::handlers::map]
fn process_data(foundational_store: FoundationalStore) -> Result<YourOutput, Error> {
// Single key lookup
let response = foundational_store.get(key_bytes);
if response.response == ResponseCode::Found as i32 {
// Process found data
}
// Batch lookup (recommended for performance)
let keys = vec![key1, key2, key3];
let queriedEntries = foundational_store.get(keys);
for entry in queriedEntries.entries {
// Process each entry
}
Ok(your_output)
}
use substreams::store::FoundationalStore;
use sf::substreams::foundational_store::model::v2::{QueriedEntry, ResponseCode};
#[substreams::handlers::map]
fn process_data(foundational_store: FoundationalStore) -> Result<YourOutput, Error> {
let keys = vec![key1, key2, key3];
let response = foundational_store.get_all(keys);
for queried_entry in response.entries {
match queried_entry.code {
x if x == ResponseCode::Found as i32 => {
// Successfully found - unpack the value
let account_owner: AccountOwner = queried_entry.entry
.value
.unpack()?;
// Process the data
}
x if x == ResponseCode::NotFound as i32 => {
// Key doesn't exist - handle missing data
substreams::log::debug!("Key not found");
}
x if x == ResponseCode::NotFoundFinalize as i32 => {
// Key was deleted after finality
substreams::log::info!("Key deleted after finality");
}
_ => {
// Handle unexpected response codes
substreams::log::error!("Unexpected response code: {}", queried_entry.code);
}
}
}
Ok(your_output)
}