All packages
solana-m0-orderbook

solana-m0-orderbook

jonalvarezz
v0.2.0/99 downloads/Repository

Package ref

solana-m0-orderbook@v0.2.0

Run package

CLI

Run map_svm_events from the command line.

substreams run solana-m0-orderbook@v0.2.0 map_svm_events -e solana-mainnet --start-block -1
Authenticate by running substreams auth or directly on thegraph.market (see docs).

README

Solana Substreams — M0 OrderBook

A Substreams module that indexes the M0 OrderBook program on Solana and sinks its events into the shared Postgres database (the solana schema). The EVM chains are indexed separately by Envio (see ../evm); the unified cross-chain views live in ../sql/views.sql.

Program ID (devnet + mainnet): MzLoYnJ6sF6eeejs4vV95TNmXqS3W4cAtLGKkjT4ZrK

What it indexes

map_svm_events (in src/lib.rs) scans each block's transactions for the program's Anchor CPI events, decodes them, and emits one row per event. The proto schema (proto/program.proto) maps each event to a table:

EventTable
OrderOpenedevents_order_opened
OrderFilledevents_order_filled
OrderCompletedevents_order_completed
OrderCancelledevents_order_cancelled
FillReportedevents_fill_reported
CancelReportedevents_cancel_reported
RefundClaimedevents_refund_claimed

OrderOpened has two on-chain layouts that share an Anchor discriminator: the legacy (V1, 188-byte) event and the current (V2, 228-byte) event that adds funder and fill_deadline. The handler dispatches on payload length — V1 events synthesize funder = sender and derive fill_deadline from the amount, V2 events read both fields directly. This mirrors the EVM indexer's V1/V2 handling.

Networks

Each network has its own manifest. They share the same Rust/proto code and differ only in package name, start block, network, and the chain_id written to rows.

NetworkManifestPackageinitialBlockchain_id
Solana mainnetsubstreams-solana.yamlsolana-m0-orderbook3998336931399811149
Solana devnetsubstreams-solana-devnet.yamlsolana-devnet-m0-orderbook4362478351399811150

Build & test locally

# Compile the WASM module (output: target/wasm32-unknown-unknown/release/substreams.wasm)
just cargo-build

# Stream 10 blocks from a start block through the GUI to inspect decoded events
just debug-gui solana-devnet <start_block>
just debug-gui solana <start_block>

Pick a <start_block> at or just before a known order to see events in the output. The GUI requires a Substreams API token (see below).

Packages

Published packages live in the Substreams registry under their package names (solana-m0-orderbook, solana-devnet-m0-orderbook). The deployed sink references a package by its .spkg (via the PACKAGE env var), e.g. solana-devnet-m0-orderbook-v0.2.0.spkg.

Publishing a new version

  1. Authenticate to the registry (sets SUBSTREAMS_API_TOKEN):

    substreams auth
    
  2. Bump package.version in the target manifest and keep version in the justfile in sync — substreams pack names the file from the manifest while the publish step uses the justfile var, so they must match. (Or override per run: just version=v0.3.0 build-and-publish solana-devnet.)

  3. Build, pack, and publish:

    just build-and-publish solana-devnet   # or: solana (mainnet)
    

Deployment

The indexer runs as a containerized substreams-sink-sql sink. The image entrypoint is substreams-sink-sql from-proto "$DSN" "$PACKAGE", which creates the tables from the proto schema on first run and streams events into Postgres.

# Build & push the runtime image (ghcr.io/m0-foundation/liquidity-delivery:orderbook-substreams)
just build-docker-image

Run the container (in our AWS/ECS infra) with:

EnvValue
DSNPostgres connection string (writes to the solana schema)
SUBSTREAMS_API_TOKENregistry token from substreams auth
PACKAGEpublished spkg, e.g. solana-m0-orderbook-v0.2.0.spkg

After the sink creates/updates the tables, (re)apply ../sql/views.sql so the unified public.* views expose any new columns.

Schema changes & reindexing

substreams-sink-sql resumes from a stored cursor and does not auto-migrate existing tables when the proto changes. To pick up new columns across history, either drop the solana schema and let the sink recreate + backfill from initialBlock, or ALTER TABLE to add the columns and accept NULLs on rows that predate the change (they are not reprocessed).

Modules

Execution graph

4 modules
Show 3 dependencies
map

solana:v020:blocks_without_votes

`blocks_without_votes` allows you to consume a full Solana Block without Vote instructions (`Vote111111111111111111111111111111111111111`). If you consume it on HISTORICAL data (+1000 blocks from HEAD), you will be reading from the StreamingFast cache, thus saving costs on the amount of TB read.