Getting Started on Solana
This guide takes you from nothing to a running Solana indexer with a live GraphQL API. If you've used HyperIndex on EVM the workflow is identical — only the config and handlers differ.
Prerequisites
- Node.js v20+ and pnpm. The commands below use
pnpm/pnpx; npm, Yarn, and Bun work too if you swap the equivalents - Docker Desktop (for the local Postgres + GraphQL stack)
- A HyperSync API token — the CLI's login flow sets this up for you, or generate one in the Envio Cloud portal. See API tokens.
1. Scaffold a project
pnpx envio init
Choose Solana at the ecosystem prompt, then choose a template:
- Metaplex Token Metadata (instructions) — indexes the Metaplex Token Metadata program's
CreateMetadataAccountV3/UpdateMetadataAccountV2instructions. A realistic instruction-indexing starting point. - Feature: Block Handler (
onSlot) — a minimal slot handler that fetches each block over RPC.
Non-interactive equivalents:
pnpx envio init svm template --template metaplex-token-metadata --name my-indexer
pnpx envio init svm template --template feature-block-handler --name my-indexer
The template scaffolds:
my-indexer/
├── config.yaml # chain + program/instruction selection
├── schema.graphql # the entities you index into
├── src/
│ └── handlers/…ts # your onInstruction / onSlot handlers
├── .env # ENVIO_API_TOKEN, RPC URL
└── package.json
envio init also runs codegen, installs dependencies, and initializes git.
2. Pick an endpoint and a start slot
start_block in config.yaml is a slot number, not a block number. Each
HyperSync endpoint serves history back to its own floor slot, and that floor rolls
forward over time, so don't hard-code an old slot. Query the current head first:
curl -s https://solana.hypersync.xyz/height
# => 440067639
Set start_block to a few tens of thousands of slots below the head for a quick
backfill, or to the slot your program was deployed at for a fuller history -
provided that slot sits above the endpoint's floor.
start_block below the endpoint's floor stalls the syncIt does not error. The server returns an empty page with next_slot equal to the
from_slot you sent, so the cursor never advances and the indexer sits there
retrying the same empty range while reporting itself healthy. Both
https://solana.hypersync.xyz and https://solana-mainnet-history.hypersync.xyz
served from slot 403,000,000 when measured on 2026-08-18, but treat that as a
moving number. See
choosing an endpoint
for how to probe it.
3. Run it
pnpm install # if you didn't let init do it
pnpm envio codegen # regenerate types from config.yaml + schema.graphql
pnpm envio dev # start Postgres + the indexer + GraphQL (Docker)
envio dev brings up the local stack and runs the indexer with hot reload. The
GraphQL playground (Hasura) is at http://localhost:8080 (default admin
secret testing). See Navigating Hasura.
To run the pieces separately:
pnpm envio local docker up # start Postgres + Hasura
pnpm envio codegen
pnpm envio start # run the indexer against the running stack
Editing config.yaml or schema.graphql — including adding a program,
instruction, or IDL — requires pnpm envio codegen to regenerate the typed
envio module and the entity types in .envio/.
4. Add your own program
Open config.yaml and add a program under experimental.programs with the
instructions you want, then write a handler. The shortest path:
- Point at an Anchor IDL if you have one — HyperIndex derives the argument and account layout for you (IDL decoding).
- Or declare an inline schema (
args+accounts) for programs without an IDL (inline schema). - Add a
discriminatorto every instruction. It is how HyperIndex matches the instruction and how it looks up the layout, including when an IDL is set (discriminators). - Register a handler with
indexer.onInstruction.
ecosystem: svm
chains:
- start_block: 437000000 # example only; query /height (step 2) and pick a recent slot above the endpoint's floor
experimental:
hypersync_config:
url: https://solana.hypersync.xyz
programs:
- name: TokenMetadata
program_id: metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s
instructions:
- name: CreateMetadataAccountV3
discriminator: "0x21"
field_selection:
transaction_fields: [signature]
import { indexer } from "envio";
indexer.onInstruction(
{ program: "TokenMetadata", instruction: "CreateMetadataAccountV3" },
async ({ instruction, context }) => {
const params = instruction.params;
if (!params) return; // discriminator matched but decode failed - skip
context.TokenMetadataAccount.set({
id: params.accounts.metadata,
mint: params.accounts.mint ?? "",
createdAtSlot: instruction.block.slot,
lastTxSignature: instruction.transaction.signature,
});
},
);
Next steps
- Configuration — every
config.yamlfield for Solana. - Instruction Handlers — the full instruction object, token balances, CPIs, and testing.
- Decoding & IDLs — discriminators, IDLs, inline schemas, supported types.
- Deploy to Envio Cloud — host your Solana indexer the same way as EVM.