Data access · 9 min read

Decoding Solana program activity declaratively, including the calls that failed

Reading Solana usually means learning the SVM data model, registering an IDL, and deploying an indexer before you see a single instruction. There is a shorter path. One declarative Portal query, a program ID and an Anchor discriminator over a slot window, returns the program's instructions directly, in the same request shape used for EVM and Bitcoin. And because the record carries an error, a commit flag, and the compute consumed, it surfaces the failed and uncommitted calls that most tooling silently drops. This guide runs it against real Jupiter instructions, the successful ones and the ones that failed.

Updated 2026-06-18 · By the SQD team

1. One query, no IDL, no deploy

The request filters the instructions table by a programId and a discriminator over a slot window. This reads Jupiter's sharedAccountsRoute instructions, the program's main swap-routing call:

your terminal
curl -s -X POST https://portal.sqd.dev/datasets/solana-mainnet/stream \
-H 'content-type: application/json' \
-d '{
"type": "solana",
"fromBlock": 427287355, "toBlock": 427287360,
"instructions": [{
"programId": ["JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4"],
"d8": ["0xc1209b3341d69c81"]
}],
"fields": {
"instruction": {
"instructionAddress": true, "accounts": true, "data": true,
"error": true, "isCommitted": true, "computeUnitsConsumed": true
}
}
}'

No schema, no IDL registration, no deployed indexer. The matching instructions stream straight back. One thing to size correctly: Solana programs like Jupiter are extremely high volume, so a wide window trips the response-size cap. Keep the slot window small and widen only as needed, the same discipline described in the Portal MCP guide.

Run it and the numbers tell a story most pipelines hide. Over these six slots the query returns 48 sharedAccountsRoute instructions, and only 10 committed. Thirty-eight did not, fifteen of those carrying the slippage error shown later. A pipeline that keeps only the successes silently discards four out of five attempts.

2. The discriminator picks the instruction

A Solana program exposes many instructions, and the first bytes of an instruction's data identify which one it is. Anchor programs use an 8-byte tag, the discriminator, which you match with d8. The Portal also exposes the leading 1, 2, and 4 bytes as d1, d2, and d4, so you can filter at whatever width you need. The same real instruction matches all four, each a prefix of the next:

FilterHex prefixSelects
  • d10xc1
  • d20xc120
  • d40xc1209b33
  • d80xc1209b3341d69c81Anchor: sharedAccountsRoute

For an Anchor program you almost always want d8, which pins one exact instruction. The narrower prefixes are useful for non-Anchor programs, or for grouping a family of instructions that share a leading byte. The mechanics of discriminators and the SVM data model are covered in what is a Solana indexer; here they are just the filter.

3. Tree position, accounts, compute

Each returned instruction is more than its data. It carries an instructionAddress that places it in the transaction's instruction tree, the full ordered accounts list it touched, and the compute it used. A real successful match has instructionAddress: [3], meaning it is the fourth top-level instruction, and an account list that starts with the Token program and includes the mints in the route, USDC (EPjF…Dt1v) and wrapped SOL (So11…1112) among them:

instructionAddress [3]
computeUnitsConsumed 189227
isCommitted true
error null
accounts [
TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA (Token program)
...
EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v (USDC mint)
So11111111111111111111111111111111111111112 (wrapped SOL)
JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 (Jupiter)
]

The ordered account list is what makes an instruction interpretable without a per-program decoder: the positions map to the program's account schema, so you can read the mints, the pools, and the authorities directly. The instructionAddress lets you reconstruct the call tree, the Solana analogue of EVM internal calls.

4. The failure path most tools drop

On Solana, a transaction can be included in a block and still fail. Many data pipelines keep only the successful instructions, which quietly biases anything you count: attempted swaps, slippage failures, and contention all disappear. The Portal record keeps the failure path. The same query above returns successful and failed instructions side by side, tagged by error and isCommitted, two independent signals:

Succeeded
  • isCommittedtrue
  • errornull
  • compute189,227
Failed, still recorded
  • isCommittedfalse
  • errorcustom program error: 0x1771
  • compute82,395
Two real sharedAccountsRoute instructions from the same slot (427,287,359), one committed and one not. 0x1771 is decimal 6001, the program's slippage-tolerance error: the swap was attempted, burned 82,395 compute units, and failed. A pipeline that keeps only committed instructions never sees it.

The two fields are not the same signal. Across this one query, 38 of the 48 matching instructions are uncommitted, and 22 of those carry no instruction-level error at all (error: null with isCommitted: false), having still burned compute. Solana transactions are atomic, so an instruction that did not itself fault is rolled back when its transaction fails elsewhere. Read isCommitted for what landed and error for what faulted; they answer different questions.

For accurate analytics that distinction is the whole game. A swap-success rate, a measure of slippage pressure, or a count of contention during a volatile minute all depend on seeing the attempts, not just the wins. The data set keeps them, so an agent can answer "what was tried" as well as "what landed".

5. The boundary on balances

Solana datasets include balances and token_balances tables, and it is worth being precise about what they are. They are per-block change records: how a balance changed in a given block, not the standing balance of an account at a point in time. They are excellent for tracking flows and deltas. They are not a balance-at-slot lookup, and summing them into a point-in-time holding is a reconstruction you would do yourself. Treat them as movement data, in the same spirit as the rest of the Portal.

6. Why this is harder elsewhere

A lot of onchain tooling is EVM-first, and the parts that do cover Solana often keep only the committed instructions, because the failure path is extra work to retain and expose. So the common state is either no declarative Solana access at all, or access that hides what was attempted.

Serving decoded Solana instructions, including the failed and uncommitted ones, with their compute and tree position, in the same declarative shape as EVM and Bitcoin, is the harder thing. It means an agent reasons about Solana with the same query model it uses everywhere else, and does not lose the attempts that make the analytics honest.

For the data model and finality background, read what is a Solana indexer. For the cross-machine query model, one query shape, every VM. To drive these queries from an agent, see AI agents and onchain data.

Frequently asked questions

How do you query Solana program activity without setting up an IDL or deploying an indexer?
You send one declarative Portal request that filters instructions by programId and an Anchor discriminator, with a block (slot) window. The matching instructions stream back with their account lists and data, with no schema authored, no IDL registered, and no indexer deployed first. You add a decoding layer later if you want named fields, but reading the activity needs only the program ID and a discriminator.
What is an Anchor discriminator?
Anchor programs prefix each instruction's data with an 8-byte tag that identifies which instruction it is, the discriminator. In a Portal query you filter on it with d8, an 8-byte hex value, so you select one instruction kind out of a program's many. The Portal also exposes 1, 2, and 4-byte prefixes (d1, d2, d4) for narrowing in stages or for non-Anchor programs.
Can SQD show me Solana instructions that failed?
Yes, and this is the part most tooling drops. Each instruction record carries an error field, an isCommitted flag, and computeUnitsConsumed. A failed swap shows the error string (for example, a custom program error) and isCommitted false, while still reporting the compute it burned. That lets an agent tell what actually succeeded from what was attempted, which matters for accurate analytics.
Do the Solana balance tables give me an account's current balance?
No. The balances and token_balances tables are per-block change records: they capture how a balance changed in a given block, not the standing balance of an account. Summing them to a point-in-time balance is a reconstruction you would do yourself. The data set does not provide a balance-at-slot lookup, so do not treat it as a holdings feed.
How is this different from a Solana indexer framework?
A framework has you define a schema and run a deployed indexer that decodes instructions into your tables before you can query. The declarative Portal query returns the raw instructions for any program immediately, in the same request shape used for EVM and Bitcoin. The definitional background, the data model, discriminators, and fork handling, is in the what is a Solana indexer guide.

Building Solana analytics or trading tools?

See how Solana data feeds analytics and trading on the analytics and DeFi and trading pages.