Borsh Parsing for Solana Instructions and Logs
When working with Solana transactions, you need to decode Borsh-encoded data to extract events and instruction data. This guide explains the three main approaches used in the codebase, each suited to different protocol implementations.Why it matters: Different Solana protocols emit events and instructions in different formats. Understanding which parsing method to use is crucial for correctly extracting swap data, liquidity events, and other on-chain information.
Overview: Three Parsing Methods
There are three primary methods to parse Borsh-encoded data from Solana transactions:parseLogs()- Parse events directly from transaction log messagesborsh.events.decode()- Decode events from instruction databorsh.instruction.decode()- Decode instruction data itself
Method 1: parseLogs() - Event Parser from Logs
This method parses events directly from the transaction’s log messages. It’s the most straightforward approach when protocols emit events as log entries.How it works
TheEventParser from Anchor reads the transaction’s logMessages array and extracts structured event data based on the program’s IDL.
Example Implementation
When to use
- Events are emitted as log messages in the transaction
- The protocol uses Anchor’s standard event emission
- You need to parse multiple events from a single transaction
Characteristics
- Works directly with
tx.meta.logMessages - No need to decode instruction data manually
- Events are already structured according to the IDL
- Can parse multiple events from a single transaction
Method 2: borsh.events.decode() - Decode Events from Instruction Data
This method decodes events that are embedded within instruction data. You extract the event portion from the instruction bytes and decode it separately.How it works
- Decode the instruction data from base58
- Skip the first 8 bytes (instruction discriminator)
- Extract the remaining bytes as event data
- Encode to base64 and decode using
borsh.events.decode()
Example Implementation
When to use
- Events are embedded within instruction data
- The protocol doesn’t emit events as separate log messages
- You need to extract events from CPI (Cross-Program Invocation) calls
Characteristics
- Requires manual extraction of event data from instruction bytes
- Must skip the instruction discriminator (first 8 bytes)
- Events are encoded within the instruction data structure
- Useful for parsing events from inner instructions
Method 3: borsh.instruction.decode() - Decode Instruction Data
This method decodes the instruction itself, not events. It’s used when you need to extract instruction parameters or when instructions contain the data you need directly.How it works
- Decode the instruction data from base58
- Convert to Buffer format
- Decode using
borsh.instruction.decode()with the appropriate format (‘hex’ or ‘base64’)
Example Implementation
When to use
- You need instruction parameters, not events
- The protocol embeds data directly in instructions
- You’re detecting instruction types (e.g., swap, deposit, withdraw)
- You need to identify pool creation or migration instructions
Characteristics
- Decodes the entire instruction structure
- Provides instruction name and parameters
- Useful for instruction-based detection (e.g., pool creation)
- Can be combined with event decoding for comprehensive parsing
Fundamental Borsh Differences
At the core Borsh level, the three methods differ in how they handle the instruction discriminator and data structure:Instruction Discriminator (8 bytes)
In Solana, all Anchor instructions start with an 8-byte discriminator that identifies the instruction type. This discriminator is computed from the instruction name using SHA256. Structure of instruction data:Differences at the Borsh Level
-
parseLogs()- Event Parser- Does NOT deal with instruction data directly
- Events are emitted by Anchor’s runtime as structured log messages
- The EventParser reads these log messages (which are already formatted strings)
- No discriminator handling needed - logs are pre-processed by Anchor
-
borsh.events.decode()- Event from Instruction Data- Must skip the 8-byte instruction discriminator
- The event data is embedded after the discriminator in the instruction bytes
- You extract bytes 8+ and decode them as a Borsh event
- The discriminator identifies the instruction, not the event
-
borsh.instruction.decode()- Full Instruction Decode- Includes the 8-byte discriminator in the decode process
- Decodes the entire instruction structure: discriminator + parameters
- Returns both the instruction name (from discriminator) and parameter data
- Used when you need instruction parameters, not events
Why This Matters
The fundamental difference is where the data lives and how Borsh deserializes it:- Logs: Events are serialized by Anchor’s runtime and written as log messages. The EventParser deserializes from these pre-formatted strings.
- Instruction data (events): Events are Borsh-encoded within instruction bytes, but you must skip the instruction discriminator to reach the event data.
- Instruction data (full): The entire instruction (discriminator + params) is Borsh-encoded together and decoded as one structure.
Key Differences Summary
| Method | Data Source | Borsh Structure | Discriminator Handling |
|---|---|---|---|
parseLogs() | tx.meta.logMessages | Pre-formatted log strings | Not applicable (logs are already processed) |
borsh.events.decode() | Instruction data (bytes 8+) | Event data only (after discriminator) | Must skip first 8 bytes |
borsh.instruction.decode() | Instruction data (full) | Discriminator + parameters | Included in decode |