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
ABIAnchor IDL (or an inline schema)
Event / logInstruction
indexer.onEventindexer.onInstruction
event.params.<name>event.instruction.decoded.args.<name>
Event signature / topic0Instruction discriminator
Indexed event argsAccount positions / account_filters
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 decimal strings

Config differences

EVMSolana
ecosystemevm (default)svm
What you listcontracts (with abi_file_path, address, events)programs_experimental (with program_id, optional idl, instructions)
Chain identitychains[].id (public chain ID, required)no id — identified by rpc/hypersync_config (chain id is 0 in handlers)
start_blockblock numberslot number
Matching keyevent signature (from ABI)discriminator (hex, 1/2/4/8 bytes)
Decoding sourceABIAnchor IDL, inline args+accounts, or bundled
Field selectionglobal, rich block/transaction field listsper-instruction toggles only: transaction_fields, token_balance_fields, log_fields (value true)
Reorg optionsrollback_on_reorg, save_full_historynot configurable — finalized data only
chains, not networks

Current HyperIndex uses chains for both EVM and Solana. (Older versions 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:

indexer.onInstruction(
{ program: "Jupiter", instruction: "sharedAccountsRoute" },
async ({ event, context }) => {
const decoded = event.instruction.decoded;
if (!decoded) return; // decode can fail → null-check
const inAmount = BigInt(decoded.args.inAmount); // u64 → decimal string
const sourceMint = decoded.accounts.sourceMint; // base58
const programId = event.instruction.programId; // base58
const slot = event.slot;
const txSig = event.transaction?.signatures[0]; // needs transaction_fields
const isInner = event.instruction.isInner; // CPI?
},
);

Key handler-level differences:

  • decoded is optional. EVM event.params is always populated (the ABI is known); Solana decoding can fail, so always if (!decoded) return;.
  • Numbers as strings. u64/u128/i64/i128 arrive as decimal strings — wrap in BigInt(...). (Smaller ints are JS numbers.)
  • Addresses are base58. No 0x lowercase/checksum concerns; pubkeys are base58 strings.
  • Opt in to context. Transaction metadata, token balances, and logs require field selection; on EVM the event always carries block/transaction context.
  • Chain id is 0. Single-cluster today; context.chain.id === 0.

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 an instructionAddress 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.

Token balances & balance changes

Solana instruction events can carry pre/post SPL Token balance snapshots for the whole transaction (token_balance_fields: true). This gives you net token movement (the balance change) without indexing every transfer instruction — there's no direct EVM equivalent built into the event. See Token balances.

Slots vs blocks

  • start_block is a slot. Use GET https://solana.hypersync.xyz/height to find the current head; HyperSync keeps a rolling window, so don't hard-code an ancient 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)

These EVM features have no Solana equivalent today:

  • Reorg handling (rollback_on_reorg) — Solana indexes finalized data.
  • No-code contract import — no envio init svm contract-import; configure programs by hand.
  • Account-change / log handlers — no onAccount; logs are an event field, not a handler.
  • Dynamic/factory registration — no Solana equivalent of dynamic contracts yet.
  • Wildcard indexing across all programs.
  • ReScript — Solana indexers are TypeScript only.
  • Per-field selection — toggles are true/absent, not field-name lists.

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.