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

EVM vs Solana

The HyperIndex workflow is the same on both: define a config.yaml and schema.graphql, write handlers, run codegen, then dev/start. What changes is the unit of work: EVM indexes events emitted by contracts, Solana indexes instructions executed by programs. This page collects every difference in one place.

Concept mapping

EVMSolana
Smart contractProgram
ABIIDL (Anchor, Shank, Codama) or a declared layout
Event / logInstruction
indexer.onEventindexer.onInstruction
event.params.<name>instruction.args.<name>
Event signature / topic0Instruction discriminator
Indexed event argsNamed account slots, filtered with the handler's where.accounts
indexer.onBlockindexer.onSlot
Block numberSlot number
Address 0x… (20 bytes, hex)Pubkey (32 bytes, base58)
Internal calls (not surfaced)Inner instructions / CPIs (first-class)
bigint paramsu64+ args as bigint, smaller ints as number

Config differences

EVMSolana
ecosystemevm (default)svm
What you listcontracts (with abi_file_path, address, events)top-level programs (with program_id, optional idl, instructions)
Chain identitychains[].id (public chain ID)chains[].id is a cluster label: solana (7565164) or solana-devnet (7565165)
Start of indexingstart_block (a block number, or latest)start_slot (a slot number, or latest)
Matching keyevent signature (from ABI)discriminator (a hex data prefix of any byte length)
Decoding sourceABIan IDL (Anchor, Shank, Codama) or an inline args + accounts layout
Field selectionthe handler's fields option, or field_selection in config.yamlthe handler's fields option only, with the extra instruction, accountActivity and log knobs
Data source configHyperSync endpoint inferred from chains[].idinferred from chains[].id too; set hypersync_config.url to pin a specific one
Reorg optionsrollback_on_reorg, save_full_historyhandled automatically on the HyperSync source (rolls back on reorg); the RPC source is finalized-only
chains, not networks

Current HyperIndex uses chains for both EVM and Solana. (V2 used networks for EVM; if you see that in an old guide, it's the same idea.)

Handler differences

EVM:

indexer.onEvent(
{ contract: "ERC20", event: "Transfer" },
async ({ event, context }) => {
const from = event.params.from; // decoded by ABI
const amount = event.params.value; // bigint
const chainId = event.chainId;
const contract = event.srcAddress; // 0x… hex
},
);

Solana:

const fields = {
instruction: ["args", "accounts", "isInner", "programId"],
transaction: ["signature"],
} as const;

indexer.onInstruction(
{ program: "Jupiter", instruction: "sharedAccountsRoute", fields },
async ({ instruction, context }) => {
const inAmount = instruction.args.inAmount; // bigint, decoded from the u64
const sourceMint = instruction.accounts.sourceMint.address; // base58
const programId = instruction.programId; // base58
const slot = instruction.block.slot;
const txSig = instruction.transaction.signature; // needs fields.transaction
const isInner = instruction.isInner; // CPI?
},
);

Key handler-level differences:

  • A failed decode never reaches the handler. EVM event.params is always populated (the ABI is known); on Solana an instruction whose data the configured layout rejects is skipped, so args and accounts need no null checks.
  • Accounts are objects, not strings. instruction.accounts.<name> is { address, accountName, instructionAccountIndex, activity } - read .address for the pubkey.
  • Addresses are base58. No 0x lowercase/checksum concerns; pubkeys are base58 strings.
  • Every field is opt-in. Instruction, transaction, account-activity, block and log fields all come from the registration's fields; on EVM the event always carries some block context.
  • The chain id is a big number. context.chain.id === 7565164 on mainnet (7565165 on devnet).

CPIs vs internal calls

On EVM, a contract calling another contract doesn't emit a separate indexable event for the internal call. On Solana, cross-program invocations are real instructions: if you index the inner program, you receive them, with isInner: true and a path that reconstructs the call tree. This makes Solana's composability directly indexable (e.g. the Raydium/Orca swaps underneath a Jupiter route). See Inner instructions.

Account activity

Solana instructions can carry pre/post lamport and SPL Token balances for the accounts a transaction touched (fields.accountActivity). This gives you net value movement (the balance change) without indexing every transfer instruction; there's no direct EVM equivalent built into the event. The amounts are bigint raw base units, and each token entry carries the mint's decimals. See Account activity.

Slots vs blocks

  • start_slot is a slot, and history doesn't reach genesis yet - see choosing a start slot.
  • Some slots have no block (skipped leader). Slot handlers must handle the empty case.
  • The handler arg for onSlot is { slot: number }, not a block object.

Not supported on Solana (yet)

Contract import, dynamic registration, wildcard indexing across programs, account-change and log handlers, and ReScript codegen have no Solana equivalent today. The overview lists each one and what to reach for instead.

What's the same: the schema file, the entity context API, the Effect API, local Docker dev, the GraphQL/Hasura layer, preload optimization, and Envio Cloud deployment.