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 is silently wrongIt never errors. An indexer running toward head skips straight to the floor and
syncs happily, writing nothing at all for the slots before it. An indexer whose
end_block is also below the floor stalls instead: the server returns an empty
page with next_slot equal to the from_slot it was sent, so the cursor never
advances. Both endpoints 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.