Skip to main content

Command Palette

Search for a command to run...

Solana Account Types Address Identifier Tool

Updated
10 min read
Solana Account Types Address Identifier Tool
C
I'm a Co Found and CTO who's the lead Web Developers and Dev Ops at N3st3d Labs with 4+ experience .

Solana Has 9 Different Account Types. Here's the Tool That Tells You Which One You're Looking At.

By N3ST3D LABS · freeapptools.co

If you're coming from JavaScript or web2 backend development and you've started building on Solana, you've probably hit this wall: everything is an "account." Your wallet? Account. A token balance? Account. A smart contract? Account. The metadata for an NFT? Also an account. The configuration state for a DEX pool? You guessed it — another account.

In Ethereum, the mental model is simpler. You have wallets (EOAs) and contracts. Two types. Solana has nine — and confusing them during development costs real time, and sometimes real money.

Before we get into the breakdown, let's answer the two questions developers ask most when they hit this for the first time.

Q: Why does Solana use so many different account types when Ethereum only has two?

Solana's architecture is built around a principle called stateless programs. Unlike Ethereum, where a smart contract owns its own storage, Solana programs store absolutely no state inside themselves. All data lives in separate accounts that the program reads from and writes to.

This means a lending protocol needs one account for the program code, another for global config, another per user for their deposit position, another for the pool's token reserve, and so on. Each of those is a different account type with a different owner and a different structure.

The tradeoff is performance — Solana can parallelize transaction execution because it knows upfront exactly which accounts a transaction will touch. But for developers coming from Ethereum or traditional backend work, it means learning a new taxonomy before you can reason about what you're actually looking at on-chain.

Q: I'm a JavaScript developer. Why does inspecting a Solana address feel so much harder than just calling eth_getCode?

Because Solana's RPC returns raw byte data, not a clean abstraction. When you call getAccountInfo on an address, you get back the account's owner, its lamports balance, an executable flag, and a blob of binary data. What that data means depends entirely on the program that owns the account — and there's no universal ABI like Ethereum has.

To actually understand what you're looking at, you need to:

  1. Know which program owns the account

  2. Know that program's data schema

  3. Deserialize the binary data using that schema

For SPL Token accounts, Solana's RPC handles deserialization for you via the jsonParsed encoding — which is why getAccountInfo with { encoding: "jsonParsed" } is so useful. But for custom program accounts, you're on your own unless you have the IDL (Interface Description Language file from Anchor).

That's the gap this tool fills for exploratory work. Paste an address, get a classification and the key fields — without writing a single line of deserialization code.

The 9 Solana Account Types, Explained for Developers

Let's go through each one with enough detail to be actually useful when you're mid-debug at 2am.

1. SOL Wallet (System Account)

Owner: 11111111111111111111111111111111 (System Program)

This is what most people mean when they say "address." It's a keypair-controlled account with a SOL balance, owned by the System Program. No data payload. No executable flag.

The System Program is the only program that can create new accounts and transfer SOL between accounts. Every wallet interaction — creating an account, funding it, signing transactions — goes through here.

In Rust/Anchor terms: When you write #[account(signer)] or #[account(mut)] and pass a wallet, this is what you're referencing.

Common mistake: Sending tokens directly to a wallet address when the recipient doesn't have an ATA for that token yet. The transfer fails — or worse, if you're writing a program that doesn't check this, you silently create an account in an unexpected state.

2. SPL Token Mint

Owner: TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA

This is the canonical definition of a fungible token — think USDC, $BRACKETS, BONK. The Mint account stores:

  • decimals — how many decimal places (USDC uses 6, most memecoins use 9)

  • supply — total number of tokens in existence (raw, before decimal adjustment)

  • mintAuthority — who can mint new tokens (null means supply is frozen forever)

  • freezeAuthority — who can freeze token accounts

The Mint doesn't hold any tokens itself. It's just the definition. Actual token balances live in Token Accounts (ATAs) that reference this Mint.

In Anchor: You validate this with #[account(mint::decimals = 6)] or by checking the owner matches the Token Program.

Common mistake for JS devs: Trying to read a token's balance from its Mint address. The Mint has no balance. You need the Token Account.

3. Token-2022 Mint

Owner: TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb

Everything about a standard SPL Mint, plus a system of "extensions" that enable functionality the original Token Program can't support:

  • Transfer Hooks — run a custom program on every token transfer (useful for royalties, compliance checks, or custom logic)

  • Transfer Fees — take a percentage on every transfer at the protocol level

  • Confidential Transfers — ZK-proof-based private balances

  • Permanent Delegate — a delegate that can never be revoked (used for burn mechanisms, rebasing tokens)

  • Non-Transferable — soulbound tokens

Why it matters for JS devs: If your app integrates token transfers and you're not checking whether a token is Token-2022, you'll hit silent failures. The @solana/spl-token library handles both, but you need to pass the correct program ID. Using the wrong one causes a 0x0 error that tells you nothing useful.

4. NFT Mint

Owner: TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA

Technically identical to an SPL Token Mint, with two specific constraints that define it as an NFT:

  • supply === "1" — exactly one token exists

  • decimals === 0 — it cannot be divided

This means what you see on Magic Eden or Tensor as "the NFT address" is the Mint address. The image, name, and attributes are stored in a separate Metaplex Metadata account (a Data Account, see below) that references this Mint.

Gotcha: The identifier can detect the NFT pattern from the mint account alone, but for full NFT verification you'd also want to check the associated Metaplex metadata account at the PDA derived from ["metadata", metadataProgramId, mintAddress].

5. Token Account (ATA — Associated Token Account)

Owner: TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA

This is where token balances actually live. Every wallet that holds a given token has exactly one Associated Token Account for that token — its address is deterministically derived from the wallet address and the Mint address.

Key fields:

  • owner — the wallet that controls this ATA

  • mint — which token this account holds

  • amount — the raw token balance (divide by 10^decimals for the human-readable amount)

  • stateinitialized, frozen

  • delegate — optional; another address approved to spend tokens from this account

Derivation (JS):

import { getAssociatedTokenAddress } from "@solana/spl-token";

const ata = await getAssociatedTokenAddress(mintPublicKey, walletPublicKey);

Common mistake: If you're airdropping tokens and the recipient doesn't have an ATA yet, the transfer fails. You need to call createAssociatedTokenAccountInstruction first, or use getOrCreateAssociatedTokenAccount which handles both cases.

6. Program / Smart Contract

Owner: BPF Loader (BPFLoaderUpgradeab1e11111111111111111111111)
Executable: true

A Solana program is a compiled Rust binary (BPF bytecode) deployed to an account with the executable flag set. The program ID is what you reference in transactions and in your Anchor declare_id!() macro.

One important distinction: upgradeable programs actually involve three accounts:

  1. The Program Account — the address you reference (executable: true)

  2. The ProgramData Account — where the actual bytecode lives

  3. The Upgrade Authority — a wallet that can push new versions

If the upgrade authority is set to null, the program is immutable. This is a trust signal — protocols that have renounced upgrade authority can't rug-pull by deploying malicious code.

In JS: This is what you pass to new Program(idl, programId, provider) in Anchor's JS SDK.

7. Liquidity Pool Account

Owner: A DEX program (Raydium, Orca, Meteora, etc.)

These are PDAs (Program Derived Addresses) created and owned by DEX programs. They hold the paired token reserves and track the pool state — prices, fees accumulated, LP token mint address, tick spacing for concentrated liquidity pools.

The identifier recognizes pool accounts from the following programs:

Program Pool Type
675kPX9... Raydium AMM v4
5Q544f... Raydium CLMM
whirLb... Orca Whirlpool
LBUZKh... Meteora DLMM
9xQeWv... Serum DEX v3

For JS devs: If you're building a swap integration and you need to fetch pool state, the pool address is what you pass to the DEX SDK. Raydium's SDK, for example, uses the pool ID to load the LiquidityPoolInfo struct which contains the current reserves and the swap fee.

8. Data Account

Owner: Any custom program

This is the catch-all for program-owned state accounts. Solana programs are stateless — they store all their data in accounts they own. A Data Account could be:

  • A user's position in a lending protocol

  • A DAO's governance proposal with vote counts

  • An NFT's Metaplex metadata (name, URI, attributes, creator royalties)

  • A raffle's participant list and prize configuration

  • A DEX's fee config and authority settings

These accounts are structured as binary data serialized according to the program's schema (usually Borsh serialization in Anchor). You can't read them meaningfully without the IDL.

In Anchor: Every struct you annotate with #[account] becomes a Data Account when initialized.

#[account]
pub struct UserPosition {
    pub owner: Pubkey,
    pub deposited_amount: u64,
    pub last_update_slot: u64,
    pub bump: u8,
}

When someone calls your initialize_position instruction, Anchor creates a new account on-chain with exactly this layout.


9. Invalid / Unfunded Address

Not actually an account type — but worth handling. An address can be syntactically valid Base58 (32-44 chars, correct charset) but have no on-chain presence. This means the keypair exists but has never been funded. Any transaction to this address that tries to reference it as an existing account will fail.

An invalid address fails the Base58 check entirely — wrong character set (0, O, I, or l are not valid Base58), wrong length, or bad encoding.

How This Saves Real Time When Building

Here's the concrete workflow shift. Say you're integrating a third-party Solana protocol and you have a list of addresses from their docs. Before you write a single line of code, you need to know which address is the Program ID, which is the pool, which is the token mint, and which is a config account.

Previously: open Solscan for each one, read the account type from the UI, cross-reference the owner against known programs, check the executable flag manually. For 8-10 addresses that's 20 minutes of tab-switching.

With the identifier: paste each address, get the classification in under a second, move on.

The more useful case is debugging. When a transaction fails with a cryptic error like AccountNotFound or InvalidAccountOwner, you're usually passing the wrong account type to an instruction. Being able to instantly classify an address tells you whether you're passing a Mint where an ATA is expected, or a wallet where a PDA should be.


The Technical Implementation

For anyone curious how the classification logic works under the hood — it's a decision tree built on getAccountInfo with jsonParsed encoding via Alchemy RPC:

No wallet connection required. No private keys involved. Read-only RPC call.

Try It

freeapptools.co — Solana Address Identifier

Three example addresses are pre-loaded in the tool for quick testing: a standard wallet, the USDC Mint, and the Raydium AMM program. Paste any address from a transaction you're debugging and get a full breakdown of type, owner, balance, and relevant fields with a direct link to Solscan.

Built by N3ST3D LABS as part of the freeapptools.co Solana developer toolkit. No account required.


Have an account type edge case the tool doesn't handle? Drop it in the comments — PDAs with unusual owner structures and Token-2022 extension edge cases are the most common gaps worth expanding.

C

Would love to hear your feedback if you have tried our app.

Building on Solana

Part 1 of 1

Developer-focused posts on building with Solana — account types, SPL tokens, wallet integrations, and the things that trip up JS developers coming from web2.