For AI agents: the documentation index is at /llms.txt. Markdown versions of pages are available by appending .md to the URL.
Skip to main content

Solana Configuration File

A Solana indexer is defined by a config.yaml with ecosystem: svm. It tells HyperIndex which chain to read, which programs and instructions to match, and how to decode them. This page is the field-by-field reference; for the meaning of discriminators, IDLs and argument types see Decoding & IDLs.

Add this line at the top of the file for editor autocompletion and validation:

# yaml-language-server: $schema=./node_modules/envio/svm.schema.json

A complete example

config.yaml
# yaml-language-server: $schema=./node_modules/envio/svm.schema.json
name: my-solana-indexer
description: Index Jupiter swaps and Metaplex NFT mints
ecosystem: svm
chains:
- start_block: 417995000 # a SLOT number, not a block
experimental:
hypersync_config:
url: https://solana.hypersync.xyz # the HyperSync endpoint serving instructions
programs:
# --- decoded from an Anchor IDL ---
- name: Jupiter
program_id: JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4
idl: idls/jupiter.json
instructions:
- name: sharedAccountsRoute
discriminator: "0xc1209b3341d69c81"
field_selection:
token_balance_fields: true
# --- decoded from an inline schema (no IDL) ---
- name: Raydium
program_id: 675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8
instructions:
- name: swap
discriminator: "0x09"
args:
- { name: amountIn, type: u64 }
- { name: minAmountOut, type: u64 }
accounts:
- tokenProgram
- amm
- userSourceTokenAccount
- userDestTokenAccount
field_selection:
token_balance_fields: true
transaction_fields:
- signatures

Top-level fields

FieldRequiredDefaultNotes
nameProject name.
ecosystemMust be svm.
chainsOne or more chains to index (see below).
descriptionFree-text description.
schemaschema.graphqlPath to your GraphQL schema.
full_batch_size5000Batch size for processing.
storagepostgres: trueStorage backends (postgres, clickhouse).
No EVM-style global fields

For Solana, several EVM top-level fields don't apply: contracts, rollback_on_reorg, save_full_history, raw_events, a global field_selection, and address_format. Reorg-related options are fixed off (Solana indexes finalized data). Field selection is per-instruction only (see field selection).

chains

Each entry is one Solana cluster.

FieldRequiredDefaultNotes
start_block-The slot to start indexing from.
experimental--HyperSync-backed instruction indexing: hypersync_config + programs (see below). The key is named experimental to signal that this shape is still evolving.
rpc--RPC URL. Required only when experimental is not set; ignored in favour of the HyperSync source when it is.
end_block--Stop at this slot (inclusive of the range processed). Useful for finite backfills and tests.
block_lag--Stay this many slots behind the head.
skip-falseSkip this chain.
EVM difference: no chain id

EVM chains require an id (the public chain ID). Solana chains have no id - the cluster is identified by the endpoints you point at. Inside handlers the Solana chain id is 0.

experimental

Everything HyperSync-backed lives under the chain's experimental key:

FieldRequiredDefaultNotes
hypersync_config.url-The HyperSync endpoint that serves instructions (e.g. https://solana.hypersync.xyz).
programs-Solana programs to index on this chain (see below).

programs

FieldRequiredDefaultNotes
name-A unique name you choose. Used in handlers (onInstruction({ program: "<name>" })) and generated types.
program_id-The base58 program address.
instructions-Instructions to match within this program (see below).
idl--Path to an Anchor IDL JSON. If set, HyperIndex derives each instruction's args + accounts from it. Mutually exclusive with per-instruction inline args/accounts.
handler-autoPath to the file that registers this program's handlers. By default handler files are auto-loaded.

instructions

Each entry selects one instruction of the program to index. Only name is required, but in practice you'll add a discriminator so HyperIndex can tell your instruction apart from the program's others.

FieldRequiredDefaultNotes
nameThe instruction name. For Anchor IDL programs this should match the IDL instruction (the snake_cased name is used to derive the default discriminator). Also the key in onInstruction({ instruction: "<name>" }).
discriminatorHex bytes that identify the instruction (e.g. "0xc1209b3341d69c81"). See discriminators. Without it, the instruction matches purely on program + filters and isn't decoded by discriminator.
is_innerunsettrue = inner (CPI) only, false = top-level only, omitted = matches both.
argsInline argument schema (Borsh), { name, type } per arg. Requires accounts too. Mutually exclusive with the program's idl. See supported types.
accountsInline ordered list of account names. The Nth name labels the Nth account. Requires args too.
account_filtersRestrict matches by the pubkey in specific account positions (see below).
field_selectionOpt into extra data on the event (see below).

Field selection

By default a handler receives only the instruction itself, plus its block's slot/time/hash. Opt into more data per instruction:

KeyValueAdds to the handler's instruction
transaction_fieldslist of field namesinstruction.transaction.<field> for each selected field: signatures, feePayer, success, err, fee, computeUnitsConsumed, accountKeys, recentBlockhash, version, transactionIndex.
block_fieldslist of field namesinstruction.block.<field> on top of the always-present slot/time/hash: height, parentSlot, parentHash.
token_balance_fieldstrueinstruction.transaction.tokenBalances: pre/post SPL Token balances. Independent of transaction_fields.
log_fieldstrueinstruction.logs: program logs scoped to this instruction.
field_selection:
transaction_fields:
- signatures
- feePayer
block_fields:
- height
token_balance_fields: true
log_fields: true

Unselected fields are typed as compile errors in the handler (FieldNotSelected), so reading a field you forgot to select fails at tsc time, not silently at runtime.

Account filters

Match an instruction only when specific account positions hold specific pubkeys — useful to index, say, only swaps that touch a particular pool or mint. Positions are 05; within a position values are OR-ed, and across positions they are AND-ed.

instructions:
- name: swap
discriminator: "0x09"
account_filters:
- position: 1
values:
- 58oQChx4yWmvKdwLLZzBi4ChoCc2fqCUWBkwMihLYQo2 # only this pool

Use any_of to OR several AND-groups together:

    account_filters:
any_of:
- [ { position: 0, values: [So11111111111111111111111111111111111111112] } ]
- [ { position: 1, values: [EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v] } ]

Choosing what to index

Solana's highest-frequency programs (SPL Token, System) produce enormous volumes of instructions. Matching them directly can swamp a backfill. Two practical patterns:

  • Index DeFi/protocol instructions and read value flow from token balances. Enabling token_balance_fields on a protocol instruction gives you the transaction's net token movements without indexing every Transfer.
  • Scope high-volume instructions with account_filters or a tight slot window (start_block/end_block) when you do need them.