> For the complete documentation index, see [llms.txt](https://docs.envio.dev/llms.txt).

# 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 Instructions](/docs/HyperIndex/solana/decoding).

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

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

## A complete example

```yaml title="config.yaml"
# yaml-language-server: $schema=./node_modules/envio/svm.schema.json
name: my-solana-indexer
description: Index Jupiter swaps and SPL Token transfers
ecosystem: svm
chains:
  - id: solana
    start_slot: 437000000 # a SLOT number, not a block number
programs:
  # --- decoded from an IDL ---
  - name: Jupiter
    program_id: JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4
    idl: ./idls/jupiter.json # every usable instruction of the IDL is indexable
  # --- decoded from an inline layout (no IDL) ---
  - name: SplToken
    program_id: TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA
    instructions:
      - name: transfer
        discriminator: "0x03"
        accounts: [source, destination, authority]
        args:
          - name: amount
            type: u64
```

Handlers then select what they index and which fields they read - see
[Instruction Handlers](/docs/HyperIndex/solana/instruction-handlers).

## Top-level fields

| Field | Required | Default | Notes |
| --- | --- | --- | --- |
| `name` | ✅ | - | Project name. |
| `ecosystem` | ✅ | - | Must be `svm`. |
| `chains` | ✅ | - | One or more Solana clusters to index (see below). |
| `programs` | - | - | Programs to index. Declared **once for the whole project**, not per chain. |
| `description` | - | - | Free-text description. |
| `schema` | - | `schema.graphql` | Path to your GraphQL schema. |
| `handlers` | - | `src/handlers` | Directory that handler files are auto-loaded from. |
| `full_batch_size` | - | `5000` | Target number of instructions processed per batch. |
| `storage` | - | `postgres: true` | Storage backends (`postgres`, `clickhouse`). |
| `disable_default_cross_chain` | - | `false` | Make entities and effect caches [per-chain](/docs/HyperIndex/multichain-indexing#per-chain-data-mode) instead of shared. |

:::note No EVM-style global fields
For Solana, several EVM top-level fields don't apply: `contracts`,
`rollback_on_reorg`, `save_full_history`, `raw_events`, `field_selection`,
`address_format` and `bytes_type`. Reorgs are handled automatically on the
HyperSync source (it rolls back on reorg), field selection is per handler (the
[`fields` option](/docs/HyperIndex/solana/instruction-handlers#selecting-fields)),
addresses are base58, and `Bytes` is always a `Uint8Array`.
:::

## `chains`

Each entry is one Solana cluster.

| Field | Required | Default | Notes |
| --- | --- | --- | --- |
| `id` | ✅ | - | The cluster: the label `solana` (7565164), `solana-devnet` (7565165), or an explicit number for another cluster. SVM has no native numeric chain id, so Envio assigns the label ids. |
| `start_slot` | ✅ | - | The **slot** to start indexing from, or `latest` to start from the current head (see below). |
| `end_slot` | - | - | Stop at this slot. Useful for finite backfills. |
| `block_lag` | - | - | Stay this many slots behind the head. |
| `hypersync_config` | - | the chain id's public endpoint | Block containing `url`. Optional for `solana` and `solana-devnet`; required for any other chain id, which otherwise fails with `Chain <id> has no default HyperSync endpoint`. |
| `rpc` | - | - | Accepted but unused: HyperSync is the only Solana data source. |
| `skip` | - | `false` | Exclude the chain from indexing and migrations. |

```yaml
chains:
  - id: solana
    start_slot: 437000000
  - id: solana-devnet
    start_slot: latest
```

A chain entry accepts only the fields above. The EVM names `start_block` and
`end_block` have no alias on Solana: config parsing rejects them as unknown
fields, so write `start_slot` and `end_slot`.

:::tip EVM difference: the id is a label
EVM chains use the public numeric chain ID. Solana clusters have no such id, so
you write `solana` or `solana-devnet` and HyperIndex maps them to `7565164` /
`7565165` - the values you see as `context.chain.id` in handlers and as the key
in [test indexer](/docs/HyperIndex/solana/instruction-handlers#testing) chain
overrides. Before v3.8 the Solana chain id was `0`.
:::

### Starting from the head

`start_slot: latest` resolves the current head once, when the indexer is first
deployed, and persists the concrete slot. A normal resume (a crash or a process
restart) picks up from the stored slot, so downtime is backfilled rather than
skipped; `envio dev`/`envio start` with `-r` re-resolves it against the head at
that time.

### Choosing an endpoint and a start slot

The endpoint follows the chain id, so the only real decision is the start slot:

| Chain | Chain id | Endpoint |
| --- | --- | --- |
| Mainnet | `solana` (7565164) | `https://solana.hypersync.xyz` |
| Devnet | `solana-devnet` (7565165) | `https://solana-devnet.hypersync.xyz` |

Each endpoint serves history back to its earliest indexed slot, not to genesis -
mainnet is around slot **391,000,000** as of September 2026, and we keep
extending it backwards. `GET <endpoint>/height` returns the current head.

:::warning A `start_slot` before the earliest indexed slot doesn't error
The indexer starts from the earliest indexed slot instead, so a backfill can
look healthy while silently skipping every slot before that. To test a candidate
slot, send a one-slot bounded query and compare `next_slot` to `from_slot`:
equal means the slot isn't indexed yet, `from_slot + 1` means served (see the
[HyperSync curl examples](/docs/HyperSync/solana-curl-examples)). Need history
further back? [Tell us on Discord](https://discord.gg/envio).
:::

## `programs`

Programs are defined once for the whole project; `program_id` says where each one
lives on every chain.

| Field | Required | Default | Notes |
| --- | --- | --- | --- |
| `name` | ✅ | - | A unique name you choose. Used in handlers (`onInstruction({ program: "<name>" })`) and in generated types. |
| `program_id` | ✅ | - | Base58 program address. A single value is allowed only when the config defines one chain; with several, give a mapping keyed by chain id, naming every one of them and writing `_` for a chain the program isn't deployed on. |
| `idl` | - | - | Path to an IDL JSON (Anchor 0.30+, legacy Anchor, Shank or Codama), relative to `config.yaml`. Every usable instruction becomes indexable, and `onInstruction` selects by name. |
| `instructions` | - | - | Instructions to index. Required when there's no `idl`; with an `idl`, use it only to override or add instructions (see below). |

```yaml
programs:
  - name: SplToken
    program_id:
      solana: TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA
      solana-devnet: TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA
  - name: MyProgram
    program_id:
      solana: MyPr0gram11111111111111111111111111111111111
      solana-devnet: _ # not deployed there
```

### `instructions`

Each entry declares one instruction of the program.

| Field | Required | Default | Notes |
| --- | --- | --- | --- |
| `name` | ✅ | - | The instruction name, unique per program. It's the key in `onInstruction({ instruction: "<name>" })` and in the generated types. |
| `discriminator` | ✅ | - | `0x`-prefixed hex prefix of the instruction data to dispatch on, of any whole number of bytes; an 8-byte value is the standard Anchor discriminator. The empty prefix `"0x"` matches every instruction of the program. See [discriminators](/docs/HyperIndex/solana/decoding#discriminators). |
| `accounts` | - | - | Positional account slots, in the order the program expects them. The Nth entry names slot N and surfaces it as `instruction.accounts.<name>`. |
| `args` | - | - | Borsh argument layout. Setting it attaches a decoder that also filters - see [`args` is also a filter](/docs/HyperIndex/solana/decoding#args-is-also-a-filter). |

With an `idl`, both `accounts` and `args` are required on a row whose name the
IDL declares (that row replaces the IDL's version of it); a row with a new name
adds an instruction the IDL didn't declare.

```yaml
instructions:
  - name: transferChecked
    discriminator: "0x0c"
    accounts: [source, mint, destination, authority]
    args:
      - name: amount
        type: u64
      - name: decimals
        type: u8
```

### Account slots

Entries in `accounts` are positional, and three forms are accepted:

| Form | Meaning |
| --- | --- |
| `payer` | A slot the call always carries. Surfaces as `instruction.accounts.payer`. |
| `?authority` | An optional slot. The key is absent from `instruction.accounts` when the call omits the slot or fills it with the program id. |
| `_` | Holds a position without naming it, so the slots after it keep theirs. Never surfaced, never filterable, and the list may not end with one. |

Naming a slot is what makes it filterable from a handler's
[`where.accounts`](/docs/HyperIndex/solana/instruction-handlers#filtering-with-where);
the raw positional addresses are available either way through
`instruction.accountArguments`.

## Choosing what to index

Solana's highest-frequency programs (SPL Token, System) produce enormous volumes
of instructions, and matching them unfiltered can swamp a backfill. Narrow a
registration with a
[`where`](/docs/HyperIndex/solana/instruction-handlers#filtering-with-where)
filter on account keys, or read value flow from
[account activity](/docs/HyperIndex/solana/instruction-handlers#account-activity)
on a protocol instruction instead of indexing every `transfer`.

## Related

- [Decoding Instructions](/docs/HyperIndex/solana/decoding): IDLs, discriminators, inline layouts, argument types.
- [Instruction Handlers](/docs/HyperIndex/solana/instruction-handlers): what arrives in the handler, field selection, and filters.
- [Schema file](/docs/HyperIndex/schema): defining the entities you write to (same as EVM).
