Hosted Stores : des recherches clé/valeur conscientes du bloc pour Substreams, entièrement gérées
Substreams excelle à transformer des données blockchain brutes en sortie structurée. Là où cela se complique, c'est quand un module doit chercher une information en cours de traitement : un prix qui vit hors chaîne, les métadonnées d'un token, une correspondance compte → propriétaire qu'un autre package a déjà calculée. Vos options étaient jusqu'ici de recalculer ces données dans chaque pipeline qui en a besoin, de les figer dans un fichier statique, ou de monter et exploiter votre propre service clé/valeur qui reste correct malgré les réorganisations de chaîne. Ces trois options sont du travail que vous préféreriez ne pas porter.
Les Hosted Stores suppriment ce travail. Un Hosted Store est un magasin clé/valeur géré, propre à votre organisation et conscient du bloc, que StreamingFast exploite pour vous sur The Graph Market. Vous y mettez des données, soit en les poussant via gRPC depuis votre propre service (Remote Feed), soit en pointant un package Substreams dessus (Substreams Feed), et n'importe quel module Substreams (ou n'importe quel client gRPC) les relit à une hauteur de bloc précise, en phase avec le flux. Aucun service à exploiter, aucune gestion de réorganisation à écrire.
Les Hosted Stores rejoignent les Hosted Sinks au sein des Hosted Services : la face gérée de Substreams sur The Graph Market, où StreamingFast exploite l'infrastructure et vous n'avez qu'à la configurer.
Qu'est-ce qu'un Hosted Store ?
Un Hosted Store est une carte clé → valeur dotée de sémantique blockchain :
- Les clés sont des octets bruts : une chaîne, une adresse, un hash, ou toute clé composite de votre choix.
- Les valeurs sont des messages protobuf, étiquetés avec leur type (
google.protobuf.Any). C'est vous qui définissez le message ; ce n'est pas une forme définie par StreamingFast. - Les lectures depuis un module Substreams se font au bloc en cours de traitement, automatiquement, de sorte que les recherches restent cohérentes avec le flux même pendant les backfills.
Votre Hosted Store est consommé par votre module Substreams via son identifiant de déploiement et sa version, <deployment-id>@v1.0.0 (la version est toujours v1.0.0 pour l'instant), et seule votre organisation peut y accéder.
À quoi sert le mieux un Hosted Store ?
Un Hosted Store est utile dès qu'un module Substreams a besoin de données de référence qui ne sont pas dans le bloc qu'il traite. Cas typiques :
- résoudre des métadonnées de token comme le symbole et les décimales
- associer une adresse à son propriétaire ou à un protocole
- vérifier une liste d'autorisation ou une liste de blocage
- attacher un prix hors chaîne à un événement on-chain
- lire une valeur qu'un autre package a déjà calculée
Comme le magasin versionne ses données par bloc, une recherche pendant un backfill historique renvoie ce qui était vrai à ce bloc plutôt que seulement la dernière valeur. Un même magasin peut servir plusieurs pipelines à la fois, ce qui en fait un bon foyer pour toute recherche que plus d'une équipe construirait et maintiendrait autrement de son côté.
Deux façons de remplir un magasin
Les Hosted Stores proposent deux modes d'alimentation, tous deux accessibles depuis le nouvel assistant Hosted Store de The Graph Market.
Substreams feed : un package remplit le magasin à partir des données de la chaîne
Choisissez Substreams feed quand la recherche dérive de la chaîne. Un package Substreams lit les blocs et écrit des entrées clé/valeur ; le magasin gère les réorganisations automatiquement puisque Substreams en est la source.
Exemple : un magasin de recherche on-chain partagé. Les métadonnées des tokens ERC-20 (symbole, décimales, nom) sont coûteuses à résoudre et chaque pipeline de prix, de portefeuille ou de comptabilité en a besoin. Plutôt que le package de chaque équipe fasse sa propre danse d'eth_call, un seul package remplit un Hosted Store avec adresse → métadonnées, et chaque module Substreams en aval le lit comme un foundational store. Même schéma pour un registre de pools DEX, une carte compte → propriétaire sur Solana, ou un jeu d'étiquetage contrat → protocole. Calculez-le une fois, réutilisez-le partout, et les nouveaux pipelines partent d'une recherche déjà chaude au lieu d'un backfill à froid.
Remote feed : vous poussez des données via gRPC
Choisissez Remote feed quand les données sont hors chaîne ou détenues par l'application : votre service est la source de vérité et Substreams a seulement besoin de les consulter.
Vous écrivez des lots d'entrées avec l'appel gRPC Feed.Set, supprimez avec Feed.Delete, et une fois que les données dont les consommateurs ont besoin sont en place, vous basculez le magasin en ligne avec Feed.SetReady. Les données poussées de cette manière sont traitées comme finales. Le magasin permet aussi de fixer plusieurs valeurs pour une clé à différentes hauteurs de bloc, ce qui couvre des cas d'usage avancés.
Exemple : un flux de prix hors chaîne. Supposons que votre protocole se règle contre un prix indiciel calculé hors chaîne. Votre service de prix pousse la dernière valeur par actif dans un magasin Remote Feed indexé par identifiant d'actif. Un module Substreams qui évalue des liquidations ou valorise des positions déclare le magasin comme entrée et appelle store.get(...) à chaque bloc ; chaque bloc qu'il traite voit le prix que votre service avait publié à ce moment-là, backfills historiques compris. Aucun contrat d'oracle à déployer, aucune table de prix à recalculer dans chaque pipeline.
Autres bons candidats : listes d'autorisation et de blocage, attestations KYC / d'identité, métadonnées d'ordres hors chaîne, feature flags — toute donnée de référence détenue par l'application que vous voulez joindre à des événements de la chaîne.
Comment lire un Hosted Store depuis un module Substreams ?
Déclarez-le comme entrée de module dans substreams.yaml :
modules:
- name: map_enriched_events
kind: map
inputs:
- source: sf.ethereum.type.v2.Block
- foundational-store: <deployment-id>@v1.0.0
output:
type: proto:my.project.v1.EnrichedEvents
Le runtime injecte un FoundationalStore dans votre handler ; appelez store.get(&[key]) et vous récupérez un résultat par clé, lu au bloc en cours de traitement :
#[substreams::handlers::map]
fn map_enriched_events(block: Block, store: FoundationalStore) -> Result<EnrichedEvents, Error> {
let response = store.get(&[address.as_slice()]);
// response.entries[0].code == ResponseCode::Found -> use entry value
// ...
}
Vous pouvez aussi interroger le magasin directement depuis n'importe quel client gRPC avec Store.Get (clés exactes) ou Store.GetFirst (première clé ≥ celle demandée, pratique pour les balayages de plage).
Un comportement à connaître : un module Substreams qui interroge un magasin n'ayant pas atteint le bloc courant se met en pause à ce bloc (il n'échoue pas et n'expire pas) jusqu'à ce que le magasin rattrape son retard (ou, pour un magasin Remote Feed, jusqu'à ce que vous appeliez Feed.SetReady). C'est délibéré : le runtime traite « pas encore atteint » comme « réessaie sous peu », ce qui est exactement ce qui garde les lectures cohérentes pendant les backfills. Les requêtes gRPC directes renvoient immédiatement block_reached: false à la place. Si un pipeline semble bloqué juste après l'ajout d'une entrée de magasin, c'est presque certainement que le magasin n'est pas encore prêt.
Quand utiliser un Hosted Store plutôt qu'un Hosted Sink ?
Ils déplacent les données dans des directions opposées.
- Un Hosted Sink prend la sortie de Substreams et l'écrit vers votre base Postgres ou ClickHouse, pour que vous puissiez l'interroger en SQL.
- Un Hosted Store contient des données que vous consultez depuis l'intérieur de Substreams pendant qu'un pipeline s'exécute, et les sert aussi via gRPC.
Si le consommateur final est un tableau de bord, un backend d'application ou un pipeline de données qui parle SQL, il vous faut un Hosted Sink. Si un module Substreams a besoin de données de référence pour faire son travail (enrichir un événement, filtrer selon une liste d'autorisation, résoudre une adresse), il vous faut un Hosted Store.
Pour ceux qui hésitent entre construire et acheter
Chaque magasin de recherche que vous exploitez vous-même représente le travail permanent d'une petite équipe de plateforme : un service gRPC à maintenir, une logique de réorganisation à réussir, un backend à sauvegarder, un versionnage par bloc à entretenir, des identifiants à faire tourner. C'est de l'infrastructure non différenciante. Elle ne rend pas votre produit meilleur, elle doit juste ne pas casser.
Les Hosted Services sont la proposition de confier cette catégorie de travail à StreamingFast. Les Hosted Sinks, c'est ne pas exploiter un processus de sink pour faire entrer des données de la chaîne dans votre base. Les Hosted Stores, c'est ne pas exploiter un service clé/valeur pour faire des recherches à l'intérieur de vos pipelines. Les deux tournent sur l'infrastructure de The Graph Market, les deux se résument à un formulaire et un bouton de déploiement, et les deux laissent vos ingénieurs consacrer leurs cycles à ce que vous essayez réellement de livrer.
Pour commencer
- Connectez-vous à The Graph Market et allez dans l'onglet Hosted Services.
- Créez un Hosted Store, choisissez Remote feed ou Substreams feed, donnez-lui un nom et une URL de type de valeur.
- Copiez l'identifiant de déploiement ; il est à la fois le nom d'hôte gRPC et la référence du magasin dans un manifeste.
- Remplissez-le : poussez des entrées avec
Feed.Set+Feed.SetReady, ou déployez le package producteur. - Lisez-le depuis un module Substreams via
foundational-store: <deployment-id>@<version>, ou directement via gRPC.
Les Hosted Stores sont en bêta. Cela fonctionne de bout en bout aujourd'hui ; nous peaufinons encore les détails et aimerions votre retour : rejoignez le Discord et dites-nous ce que vous rencontrez.