If you’re coming from the EVM world, decoding Solana transactions can feel like learning a new language. In Ethereum, you’d use ABIs and tools like ethers.js to decode transaction data. In Solana, the process is different — but once you understand it, it’s just as powerful.
This guide is for:
• EVM developers moving to Solana
• Backend engineers building indexers, bridges, analytics platforms, …
• Anyone decoding on-chain Solana data
I’ll walk you through decoding Solana transaction data using a real-world example: an on-chain auction program (i.e., smart contract). We’ll use the Solana Transaction Parser library from Debridge Finance and learn how to handle custom program instructions that aren’t decoded by default.
Source code: https://github.com/AndreAugusto11/example-usage-solana-parser
Getting StartedBefore we dive in, clone the repository and set up your environment:
git clone https://github.com/AndreAugusto11/example-usage-solana-parser.gitYou’ll also need TypeScript tooling:
npm install -D typescript ts-nodeRequirements:
Before we dive into code, let’s understand how transaction decoding differs between these two ecosystems.
The EVM Approach: ABIs and Function SelectorsIn Ethereum and other EVM chains, transaction decoding relies on:
ABIs (Application Binary Interfaces): JSON files that describe contract interfaces, including function signatures, parameter types, and return values. When you call a contract function, your transaction data includes:
0x23b872dd // Function selector (first 4 bytes of keccak256 hash)Function selectors: The first 4 bytes of the keccak256 hash of the function signature (e.g., transfer(address,uint256)). Tools like ethers.js and web3.js use the ABI to automatically decode these transactions.
The workflow is straightforward:
Solana’s architecture is fundamentally different. Programs are stateless and transactions can call multiple programs in sequence. This requires a different decoding approach:
IDLs (Interface Description Language): Similar to ABIs, but specific to the Anchor framework (the most popular Solana development framework). An IDL describes:
Discriminators: 8-byte identifiers (not 4 bytes like EVM) derived from the sha256 hash of a namespace and name. For instructions, this is global:instruction_name. For accounts, it's account:account_name.
c7385526 92f3259e // 8-byte discriminator (first 8 bytes of sha256)The key differences:
Comparison between EVM and SolanaWhy Solana Decoding is More ManualIn the EVM world, most tools come with built-in ABI decoding. Pass in an ABI, and you’re done. In Solana, while the Anchor framework provides IDLs, you often need to:
This might seem more complex, but it gives you fine-grained control over transaction parsing and a deeper understanding of what’s happening on-chain.
What We’ll BuildIn this tutorial, we’ll decode transactions from Mayan Swift’s auction program (Mayan Swift is a fast bridge between multiple blockchains, serving as a very important piece in the Solana ecosystem, https://docs.mayan.finance/architecture/swift). You’ll learn to:
By the end, you’ll be able to decode any Anchor-based Solana program’s transactions.
The Problem: Unknown InstructionsNow let’s see this in action. We want to decode data from Mayan Swift’s auction program (9w1D9okTM8xNE7Ntb7LpaAaoLc6LfU9nHFs2h2KTpX1H).
Here’s our starting point — a transaction hash from a bid instruction: Ahy9GEyiPzkrw54Js6rw43bD6m6V3zmDDK6nn6e8N2tskrbkiozhsMjcdBLvCgH5JAc8CFyUZiwWpyCNqQ4wmQb.Feel free to choose another from https://solscan.io/account/9w1D9okTM8xNE7Ntb7LpaAaoLc6LfU9nHFs2h2KTpX1H
Let’s try to parse it without any custom decoders. Create a file src/no-custom-decoder.ts:
import { Connection, clusterApiUrl } from "@solana/web3.js";Run it with:
npx ts-node src/no-custom-decoder.tsThe parser successfully decodes some instructions automatically — the compute budget instructions and system program calls are recognized because they’re common programs already loaded into the parser by default:
[That unknown Buffer is your encoded instruction data—255 bytes of Borsh-serialized parameters that the parser can't decode without the program's IDL. This is where our work begins.
Step 1: Get the IDLFirst, fetch the IDL for your program. For Anchor programs, you can find this on Solscan:
Solscan: https://solscan.io/account/9w1D9okTM8xNE7Ntb7LpaAaoLc6LfU9nHFs2h2KTpX1H#anchorProgramIdl
Save the JSON file to src/idl/swift_auction.json.
Step 2: Convert IDL to TypeScriptInstall the Anchor IDL to TypeScript converter, such that the tool can parse its data types:
cargo install simple-anchor-idl-tsRun the converter:
simple-anchor-idl-ts src/idl/swift_auction.jsonThis creates src/idl/swift_auction.ts with TypeScript type definitions.
Step 3: Fix the IDL (Making it Anchor-Compliant)The IDL exported from Solscan is incomplete and won’t work with Anchor’s TypeScript client. We need to fix three critical issues to make it compliant with the Anchor IDL specification. Think of this as the difference between a draft ABI and a production-ready one.
Issue #1: Missing address and metadata fieldsThe Anchor IDL specification requires these fields at the root level, but Solscan’s export omits them. Without these, the Anchor TypeScript client won’t recognize the IDL structure.
Why it matters: The address field ties the IDL to the specific on-chain program, while metadata provides versioning and spec information that tools use for compatibility checks.
At the top of your IDL type definition, add:
export type SwiftAuctionIDLType = {Don’t forget to update the corresponding SwiftAuctionIDL object (around line 408) with the same fields.
Issue #2: Missing discriminatorsThis is the critical part. Remember those 8-byte discriminators we discussed earlier? They’re how Solana programs route instructions and identify account types. Without them, we can’t match instruction data to the correct decoder function.
Why it matters: When a transaction comes in with data starting with c7 38 55 26 92 f3 25 9e, we need to know that it maps to the bid instruction. Discriminators make this possible.
Create a helper script calculate-discriminator.js:
const crypto = require('crypto');const name = process.argv[2];Now calculate discriminators for each instruction using the global: namespace:
node calculate-discriminator.js global:bidAnd for accounts using the account: namespace:
node calculate-discriminator.js account:auctionStateAdd these discriminators to each instruction and account definition in your IDL file. For example:
{Pro tip: Notice how the first bytes of our unknown buffer (c7 38 55 26...) match the bid discriminator? That's how we'll route it to the right decoder!
Issue #3: Fix defined type referencesThe IDL converter sometimes generates incorrect type references. The Anchor specification expects a specific format for custom type definitions.
Why it matters: Without the correct format, the TypeScript types won’t compile, and your decoder won’t work.
Find any instances of:
"defined": "OrderInfo"And change them to:
"defined": {This matches the Anchor IDL specification for type references and ensures proper TypeScript type generation.
VerificationAfter these fixes, your IDL should be fully Anchor-compliant. You can verify this by checking that:
Now we’re ready to build the actual decoder!
Step 4: Build Your Custom DecoderNow comes the exciting part: transforming raw bytes into meaningful data. We’ll build a decoder that mimics how Anchor programs read instruction data on-chain.
Understanding the Decoding StrategyWhen a Solana program receives instruction data, it follows this pattern:
Our decoder will do the same thing, but in TypeScript rather than Rust.
Setting Up the ParserCreate src/custom-parsers/swift-auction-parser.ts:
import { TransactionInstruction } from "@solana/web3.js";What’s happening here:
Now let’s implement the decoder for the bid instruction:
function decodeBidInstruction(Key points:
The OrderInfo struct contains multiple fields with different types. You'll need to create src/decodeOrderInfo.ts to handle this:
import BN from "bn.js";export function decodeOrderInfo(data: Buffer) {The pattern:
This is exactly how Borsh deserialization works — sequential, fixed-layout reads.
Integrating with the ParserFinally, register your custom decoder with the Solana Transaction Parser. Create src/auction-custom-decoder.ts:
import { Connection, clusterApiUrl } from "@solana/web3.js";Run it with:
npx ts-node src/auction-custom-decoder.tsThe parser will now use your custom decoder whenever it encounters an instruction from the Swift Auction program!
Running the ExamplesTo see the difference between parsed and unparsed transactions:
Without custom decoder:
npx ts-node src/no-custom-decoder.tsWith custom decoder:
npx ts-node src/auction-custom-decoder.tsThe second command will show the fully decoded bid instruction with all its parameters visible and readable.
The Result: From Bytes to MeaningLet’s compare what we had before and after implementing our custom decoder.
Before (Unknown Buffer):{Now we can see exactly what this transaction does:
2. The minimum output amount is 250 USDC
3. The bid amount is ~250.12 SOL
4. It’s a Dutch auction (mode 2) with a 0.03% Mayan fee
5. Includes deadline, fee structures, and referral information
What We AccomplishedRemember our theoretical comparison at the beginning? We’ve successfully:
If you’re transitioning from Ethereum to Solana, here are the essential mindset shifts:
1. IDLs vs ABIs: Similar Purpose, Different ImplementationEVM: ABIs are universally supported. Pass one to ethers.js and you’re done.
Solana: IDLs are Anchor-specific and require more manual setup. But this gives you visibility into exactly how data flows through your programs.
2. Discriminators vs Function Selectors: More Bytes, More ContextEVM: 4-byte function selectors from keccak256(functionSignature).slice(0,4)
Solana: 8-byte discriminators from sha256(namespace:name).slice(0,8)
The extra bytes reduce collision risk (which has been used in the past in contract exploits) and support namespacing for instructions, accounts, and events.
3. Decoding Tools: Automatic vs ManualEVM ecosystem:
const contract = new ethers.Contract(address, abi, provider);Solana ecosystem:
const txParser = new SolanaParser([customDecoder]);The Solana approach requires more initial work but teaches you the internals of program communication.
Next StepsNow that you can decode transaction data, you can:
The full source code is available on GitHub: https://github.com/AndreAugusto11/example-usage-solana-parser
Additional Resources:
Welcome to Solana!