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
| EVM | Solana |
|---|---|
| Smart contract | Program |
| ABI | IDL (Anchor, Shank, Codama) or a declared layout |
| Event / log | Instruction |
indexer.onEvent | indexer.onInstruction |
event.params.<name> | instruction.args.<name> |
| Event signature / topic0 | Instruction discriminator |
| Indexed event args | Named account slots, filtered with the handler's where.accounts |
indexer.onBlock | indexer.onSlot |
| Block number | Slot number |
Address 0x… (20 bytes, hex) | Pubkey (32 bytes, base58) |
| Internal calls (not surfaced) | Inner instructions / CPIs (first-class) |
bigint params | u64+ args as bigint, smaller ints as number |
Config differences
| EVM | Solana | |
|---|---|---|
ecosystem | evm (default) | svm |
| What you list | contracts (with abi_file_path, address, events) | top-level programs (with program_id, optional idl, instructions) |
| Chain identity | chains[].id (public chain ID) | chains[].id is a cluster label: solana (7565164) or solana-devnet (7565165) |
| Start of indexing | start_block (a block number, or latest) | start_slot (a slot number, or latest) |
| Matching key | event signature (from ABI) | discriminator (a hex data prefix of any byte length) |
| Decoding source | ABI | an IDL (Anchor, Shank, Codama) or an inline args + accounts layout |
| Field selection | the handler's fields option, or field_selection in config.yaml | the handler's fields option only, with the extra instruction, accountActivity and log knobs |
| Data source config | HyperSync endpoint inferred from chains[].id | inferred from chains[].id too; set hypersync_config.url to pin a specific one |
| Reorg options | rollback_on_reorg, save_full_history | handled automatically on the HyperSync source (rolls back on reorg); the RPC source is finalized-only |
chains, not networksCurrent 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.paramsis always populated (the ABI is known); on Solana an instruction whose data the configured layout rejects is skipped, soargsandaccountsneed no null checks. - Accounts are objects, not strings.
instruction.accounts.<name>is{ address, accountName, instructionAccountIndex, activity }- read.addressfor the pubkey. - Addresses are base58. No
0xlowercase/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 === 7565164on mainnet (7565165on 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_slotis 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
onSlotis{ slot: number }, not ablockobject.
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.