@geeklad/solana-tx-parser
v1.0.0
Published
TypeScript library for parsing Solana transactions with Anchor IDL support
Maintainers
Readme
@geeklad/solana-tx-parser
A TypeScript library for parsing Solana transactions with Anchor IDL support. Decode instructions, extract emitted events, parse transaction logs, and serialize transactions for storage or transport.
Features
- Instruction decoding — decode top-level and inner (CPI) instructions using Anchor IDLs; falls back gracefully to natively-parsed RPC output for well-known programs (System, Token, etc.)
- Event extraction — harvest Anchor events emitted via
emit!andemit_cpi!macros from logs and CPI return data - Log parsing — convert the flat transaction log array into structured per-program contexts with messages, data payloads, compute units, and errors
- Account decoding — deserialize on-chain account data from a named type in an Anchor IDL
- Hydration — serialize
ParsedTransactionWithMetato JSON-safe form and reconstruct it, preservingPublicKeyinstances across the round-trip - Performance — module-level caches for Borsh coders prevent recompilation across repeated calls for the same program
Installation
npm install @geeklad/solana-tx-parser @coral-xyz/anchor @solana/web3.js@solana/web3.js is a peer dependency and must be installed alongside the package.
Quick Start
import { Connection } from "@solana/web3.js";
import { parseInstructions } from "@geeklad/solana-tx-parser";
import { Idl } from "@coral-xyz/anchor";
import idl from "./some-program-idl.json";
const connection = new Connection("https://api.mainnet-beta.solana.com");
const signature = "PUT A VALID SIGNATURE HERE";
const programId = "PUT THE VALID IDL PROGRAM ADDRESS HERE";
const transaction = await connection.getParsedTransaction(signature, {
maxSupportedTransactionVersion: 0,
commitment: "confirmed",
});
// Parse with the IDL
const parsed = parseInstructions([{ idl: idl as Idl, programId }], transaction!);
for (const ix of parsed) {
console.log(ix.programId, ix.parsedInstruction);
}API Reference
parseInstructions(idls, transaction)
Decodes all instructions in a ParsedTransactionWithMeta, including nested CPI calls and Anchor events.
import { parseInstructions } from "@geeklad/solana-tx-parser";
import { Idl } from "@coral-xyz/anchor";
import myProgramIdl from "./my_program.json";
const idls = [
{
idl: myProgramIdl as Idl,
programId: "MyProgram1111111111111111111111111111111111",
},
];
const parsed = parseInstructions(idls, transaction);Each element of the returned array has the shape:
{
index: number; // Position in the transaction
programId: string; // Base58-encoded program ID
parsedInstruction: Instruction // Decoded instruction (Anchor or native RPC)
| ParsedInstruction
| null; // null if no matching IDL was found
accounts?: {
pubkey: string; // Base58-encoded public key
name?: string; // Account name from the IDL (if available)
isSigner: boolean;
isWritable: boolean;
}[];
events?: { name: string; data: any }[] | null; // Anchor events
parsedInnerInstructions?: ParsedInstructionWithEvents[]; // CPI calls
}For natively-decoded instructions (System Program, Token Program, etc.) parsedInstruction contains a parsed field with type and info. For Anchor instructions it contains name and data.
parseAccount(idl, accountData, accountName, programId?)
Deserializes raw on-chain account data using a named struct from an Anchor IDL.
import { parseAccount } from "@geeklad/solana-tx-parser";
const accountInfo = await connection.getAccountInfo(pubkey);
const decoded = parseAccount(
myProgramIdl as Idl,
accountInfo!.data,
"MyAccount"
);Returns the decoded object, or null if deserialization fails.
parseLogs(logs)
Converts the flat logs array from a transaction into structured per-program contexts.
import { parseLogs } from "@geeklad/solana-tx-parser";
const contexts = parseLogs(transaction.meta!.logMessages!);
for (const ctx of contexts) {
console.log(ctx.programId);
console.log(ctx.logMessages); // msg!() output
console.log(ctx.dataLogs); // base64 Anchor event payloads
console.log(ctx.unitsConsumed); // compute units used
console.log(ctx.errors); // error messages, if any
}Each LogContext has:
{
programId: string;
depth: number; // CPI nesting depth (0 = top-level)
id: number; // Unique invocation ID within the transaction
instructionIndex: number;
rawLogs: string[];
logMessages: string[]; // Lines from msg!()
dataLogs: string[]; // Base64 payloads from emit! / emit_cpi!
errors: string[];
unitsConsumed?: number;
invokeResult?: string; // Return value or "Log truncated"
}dehydrateParsedTransactionWithMeta(transaction)
Converts a ParsedTransactionWithMeta into a plain JSON-serializable object by replacing PublicKey instances with base58 strings.
import { dehydrateParsedTransactionWithMeta } from "@geeklad/solana-tx-parser";
const dehydrated = dehydrateParsedTransactionWithMeta(transaction);
const json = JSON.stringify(dehydrated);
// Store in a database, Redis, or send over the networkrehydrateParsedTransactionWithMeta(transaction)
Reconstructs a ParsedTransactionWithMeta from a dehydrated object, restoring PublicKey instances.
import { rehydrateParsedTransactionWithMeta } from "@geeklad/solana-tx-parser";
const loaded = JSON.parse(json);
const transaction = rehydrateParsedTransactionWithMeta(loaded);
// Pass directly to parseInstructions or any @solana/web3.js APIUsage Examples
Parsing native instructions (no IDL)
import { Connection } from "@solana/web3.js";
import { parseInstructions } from "@geeklad/solana-tx-parser";
const connection = new Connection("https://api.mainnet-beta.solana.com");
const transaction = await connection.getParsedTransaction(signature, {
maxSupportedTransactionVersion: 0,
commitment: "confirmed",
});
const parsed = parseInstructions([], transaction!);
for (const ix of parsed) {
console.log(`[${ix.index}] Program: ${ix.programId}`);
if (ix.parsedInstruction && "parsed" in ix.parsedInstruction) {
// Natively decoded (System Program, Token Program, etc.)
const native = ix.parsedInstruction.parsed as { type?: string };
console.log(` Type: ${native.type}`);
} else if (ix.parsedInstruction) {
// Decoded via Anchor IDL
console.log(` Instruction: ${ix.parsedInstruction.name}`);
console.log(` Data:`, ix.parsedInstruction.data);
}
for (const acc of ix.accounts ?? []) {
console.log(` ${acc.name ?? acc.pubkey} [signer=${acc.isSigner}, writable=${acc.isWritable}]`);
}
}Parsing a custom Anchor program
import { Connection } from "@solana/web3.js";
import { Idl } from "@coral-xyz/anchor";
import { parseInstructions } from "@geeklad/solana-tx-parser";
import myProgramIdl from "./my_program.json";
const connection = new Connection("https://api.mainnet-beta.solana.com");
const idls = [
{
idl: myProgramIdl as Idl,
programId: "MyProgram1111111111111111111111111111111111",
},
];
const transaction = await connection.getParsedTransaction(signature, {
maxSupportedTransactionVersion: 0,
commitment: "confirmed",
});
const parsed = parseInstructions(idls, transaction!);
for (const ix of parsed) {
if (!ix.parsedInstruction) continue;
console.log(`Instruction: ${ix.parsedInstruction.name}`);
console.log(`Data:`, ix.parsedInstruction.data);
// Anchor events emitted during this instruction
for (const event of ix.events ?? []) {
console.log(`Event [${event.name}]:`, event.data);
}
// CPI calls made within this instruction
for (const inner of ix.parsedInnerInstructions ?? []) {
console.log(` CPI -> ${inner.programId}: ${inner.parsedInstruction?.name}`);
}
}Dehydrate / rehydrate round-trip
import {
dehydrateParsedTransactionWithMeta,
rehydrateParsedTransactionWithMeta,
} from "@geeklad/solana-tx-parser";
// Fetch and dehydrate
const transaction = await connection.getParsedTransaction(signature, {
maxSupportedTransactionVersion: 0,
});
const dehydrated = dehydrateParsedTransactionWithMeta(transaction!);
// Store (database, cache, message queue, …)
await db.set(signature, JSON.stringify(dehydrated));
// Load and rehydrate
const raw = await db.get(signature);
const rehydrated = rehydrateParsedTransactionWithMeta(JSON.parse(raw));
// Use exactly like a freshly fetched transaction
const parsed = parseInstructions(idls, rehydrated);Running the Examples
npm run exampleThe examples use the public Solana mainnet RPC which is rate-limited. For production workloads use a dedicated RPC provider such as Helius, QuickNode, or Alchemy.
Building
npm run buildCompiled output is written to dist/.
License
MIT
