DEV Community

Samuel Akoji
Samuel Akoji

Posted on

Decoding Raw Account Data with Borsh

Day 24 of #100DaysOfSolana

Yesterday I built a terminal account explorer. But that data field kept staring back at me a blob of base64 with no obvious meaning.

Today I cracked it open.


The problem

Every Solana account stores its state as a flat byte array. There's no JSON, no column names, no built-in schema. The program that owns the account defines how those bytes are laid out using a serialization format called Borsh (Binary Object Representation Serializer for Hashing).

To read account data, you need either:

  • A pre-built codec for that account type, or
  • The byte-level specification to decode it manually

What I decoded

I used the Wrapped SOL mint account on mainnet as my target a well-known SPL Token account with a fixed 82-byte layout.


Method 1 Pre-built codec (the easy path)

import { getMintDecoder } from "@solana-program/token";

const decoded = getMintDecoder().decode(accountData);
console.log(decoded);
Enter fullscreen mode Exit fullscreen mode

One line. Returns a fully structured object with supply, decimals, mintAuthority, and more. This is what you'd use in production.


Method 2 Manual byte decoding (the learning path)

The 82-byte Mint layout:

Bytes Field Type
0–3 mintAuthorityOption u32
4–35 mintAuthority 32-byte pubkey
36–43 supply u64 (little-endian)
44 decimals u8
45 isInitialized bool
46–49 freezeAuthorityOption u32
50–81 freezeAuthority 32-byte pubkey
const view = new DataView(data.buffer);

const supply = view.getBigUint64(36, true); // true = little-endian
const decimals = view.getUint8(44);
const isInitialized = view.getUint8(45) === 1;
Enter fullscreen mode Exit fullscreen mode

Two things to understand here:

DataView lets you read multiple adjacent bytes as a single typed value (u32, u64, etc). You'd use it to parse any binary format, not just Solana.

Little-endian flag (true) Solana stores all multi-byte numbers least significant byte first. Forgetting this flag is the single most common decoding bug. Every multi-byte read needs true.


Method 3 RPC jsonParsed (the shortcut)

const { value } = await rpc.getAccountInfo(address, {
  encoding: "jsonParsed"
}).send();
Enter fullscreen mode Exit fullscreen mode

The RPC decodes it server-side for known programs. Great for quick checks but only works for well-known programs. For custom programs, you bring your own decoder.

All three methods returned identical values. ✅


The three encodings you'll see everywhere

Encoding When you see it
Base64 RPC account data responses
Base16 (hex) Debugging 1 byte = 2 chars, easy to count
Base58 Solana addresses

They're all just different spellings of the same underlying bytes.


Why this matters

When you write your own Solana programs, you'll define Rust structs → Borsh serializes them into byte arrays → your frontend deserializes them back. Today I proved I can do that translation by hand.

No more mystery blobs.

Day 24 down. Still shipping daily as part of 100 Days of Solana with MLH

solana #web3 #blockchain #100daysofsolana

Top comments (0)