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
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:
| Event | Table |
|---|---|
OrderOpened |
events_order_opened |
OrderFilled |
events_order_filled |
OrderCompleted |
events_order_completed |
OrderCancelled |
events_order_cancelled |
FillReported |
events_fill_reported |
CancelReported |
events_cancel_reported |
RefundClaimed |
events_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.
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.
| Network | Manifest | Package | initialBlock |
chain_id |
|---|---|---|---|---|
| Solana mainnet | substreams-solana.yaml |
solana-m0-orderbook |
399833693 |
1399811149 |
| Solana devnet | substreams-solana-devnet.yaml |
solana-devnet-m0-orderbook |
436247835 |
1399811150 |
# 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).
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.
Authenticate to the registry (sets SUBSTREAMS_API_TOKEN):
substreams auth
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.)
Build, pack, and publish:
just build-and-publish solana-devnet # or: solana (mainnet)
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:
| Env | Value |
|---|---|
DSN |
Postgres connection string (writes to the solana schema) |
SUBSTREAMS_API_TOKEN |
registry token from substreams auth |
PACKAGE |
published 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.
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).
substreams gui solana-m0-orderbook@v0.2.0 map_svm_eventssubstreams gui solana-m0-orderbook@v0.2.0 solana:blocks_without_votesblocks_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.
substreams gui solana-m0-orderbook@v0.2.0 solana:v020:blocks_without_votessubstreams gui solana-m0-orderbook@v0.2.0 solana:program_ids_without_votesmessage
DeployRequest
message
Parameter
message
DeployResponse
message
UpdateRequest
message
UpdateResponse
message
InfoRequest
message
InfoResponse
message
SinkProgress
message
PackageInfo
message
ListRequest
message
ListResponse
message
DeploymentWithStatus
message
RemoveRequest
message
RemoveResponse
message
PauseRequest
message
PauseResponse
message
StopRequest
message
StopResponse
message
ResumeRequest
message
ResumeResponse
enum
DeploymentStatus
message
Service
message
DBTConfig
message
HasuraFrontend
message
PostgraphileFrontend
message
PGWebFrontend
message
RESTFrontend
message
Table
message
Int128
message
UInt128
message
Int256
message
UInt256
message
Decimal128
message
Decimal256
message
StringConvertion
message
Column
message
ClickhouseTableOptions
message
ClickhousePartitionByField
message
ClickhouseOrderByField
message
ClickhouseIndexField
enum
IndexType
enum
Function
message
Data
message
CancelReported
message
FillReported
message
OrderCancelled
message
OrderCompleted
message
OrderFilled
message
OrderOpened
message
RefundClaimed
message
Modules
message
Binary
message
Module
message
Package
message
PackageMetadata
message
ModuleMetadata
message
Clock
message
BlockRef
message
FileDescriptorSet
message
FileDescriptorProto
message
DescriptorProto
message
ExtensionRangeOptions
message
FieldDescriptorProto
message
OneofDescriptorProto
message
EnumDescriptorProto
message
EnumValueDescriptorProto
message
ServiceDescriptorProto
message
MethodDescriptorProto
message
FileOptions
message
MessageOptions
message
FieldOptions
message
OneofOptions
message
EnumOptions
message
EnumValueOptions
message
ServiceOptions
message
MethodOptions
message
UninterpretedOption
message
SourceCodeInfo
message
GeneratedCodeInfo
message
Any
message
Timestamp
message
Block
message
ConfirmedTransaction
message
Transaction
message
Message
message
MessageHeader
message
MessageAddressTableLookup
message
TransactionStatusMeta
message
TransactionError
message
InnerInstructions
message
InnerInstruction
message
CompiledInstruction
message
TokenBalance
message
UiTokenAmount
message
ReturnData
message
Reward
message
Rewards
message
UnixTimestamp
message
BlockHeight
enum
RewardType
message
Service
message
DBTConfig
message
HasuraFrontend
message
PostgraphileFrontend
message
PGWebFrontend
message
RESTFrontend
message
Request
message
Response
message
BlockUndoSignal
message
BlockScopedData
message
SessionInit
message
InitialSnapshotComplete
message
InitialSnapshotData
message
MapModuleOutput
message
StoreModuleOutput
message
OutputDebugInfo
message
ModulesProgress
message
ModuleProgress
message
BlockRange
message
StoreDelta