Solana development becomes easier once you stop treating every address as a wallet.
An address can identify a wallet, executable program, program-owned state, token mint, or token account. They share a 32-byte address format, but they don't share authority or behavior. This guide gives you a working mental model and an Account Inspection Loop you can use when an unfamiliar address appears in your app.
Start with the account model
Solana stores network state in accounts. You can think of the network as a key-value store where each 32-byte address points to an account record.
Every account exposes five core fields:
- lamports: its SOL balance in the smallest unit.
- data: bytes interpreted by the account's owner program.
- owner: the program allowed to modify the data or debit lamports.
- executable: whether the account contains runnable program code.
- rentEpoch: a legacy field retained in account responses.
The word owner causes frequent confusion. It identifies a program, not the person who controls a wallet. A normal wallet account is owned by the System Program. The wallet's private key authorizes signatures, while the System Program enforces changes to that account.
Programs keep logic separate from state
A Solana program is normally stateless. Its executable account contains code, while mutable application state lives in separate data accounts passed into each instruction.
Suppose a marketplace lists an item. The marketplace program defines the listing rules. A separate listing account stores the seller, price, mint, and status. An instruction names both the program and every account it needs.
This separation lets the runtime see account access before execution. Transactions that write to different accounts can run in parallel.
Learn the four addresses you will meet first
The same base58 appearance hides different account roles. Identify the role before you build a transfer, decode bytes, or display a balance.
Wallet accounts sign transactions
A wallet address is an on-curve Ed25519 public key. A matching private key can produce signatures for it.
Most wallet accounts are System Program accounts. They can hold SOL directly and act as transaction fee payers. A wallet address does not hold an SPL token balance directly.
That last distinction matters. Sending SOL targets a wallet account. Sending USDC normally targets a token account associated with that wallet and the USDC mint.
Program accounts contain executable code
A deployed program has an address called its program ID. Its account is marked executable and owned by a loader program.
Clients include the program ID in an instruction so the runtime knows which code to execute. The program then receives the instruction data and referenced accounts.
Do not store changing product state inside the executable account. Store it in data accounts your program owns.
Program Derived Addresses give programs deterministic authority
A Program Derived Address, or PDA, comes from a program ID plus a set of seeds. The same inputs produce the same address.
PDAs are deliberately off the Ed25519 curve. No private key exists for them. The deriving program can authorize actions for a PDA through the runtime during a cross-program invocation.
A per-user profile might use these seeds:
["profile", user_wallet]The program ID keeps the namespace tied to one program. The text seed separates profile accounts from other state. The wallet seed produces one deterministic address per user.
Use PDAs when state needs a predictable address or a program-controlled authority. Do not describe a PDA as a wallet merely because explorers render both as base58 strings.
Token accounts hold balances for one mint
An SPL token account stores a balance for one token mint and records an authority allowed to control that balance. The Token Program or Token-2022 Program owns the account at the runtime level.
An Associated Token Account, or ATA, is the conventional deterministic token account for a wallet, mint, and token program. A wallet that owns five tokens will usually have at least five token accounts.
The model is:
wallet address
├─ SOL balance on the System Program account
├─ token account for mint A
├─ token account for mint B
└─ token account for mint CA mint account describes a token, including its decimals, supply, and optional authorities. A token account holds units of that mint. Confusing the mint address with a destination token account is a common transfer bug.
Use the Account Inspection Loop
When an address behaves unexpectedly, inspect it in the same order every time.
- Fetch the account from the intended cluster.
- Confirm whether it exists.
- Read its owner program.
- Check the executable flag.
- Inspect the lamport balance and data length.
- Decode data with the owner program's schema.
- Check whether the transaction expects it to sign or be writable.
This sequence prevents a label from becoming an assumption. Explorer labels and product copy are useful hints, but the on-chain owner and data define the account.
With Kit, the read starts with an RPC call. The exact response helpers can vary by installed version, so keep TypeScript aligned with your pinned packages:
import { address, createSolanaRpc } from '@solana/kit';
const rpc = createSolanaRpc('https://api.devnet.solana.com');
const accountAddress = address('ADDRESS_TO_INSPECT');
const response = await rpc
.getAccountInfo(accountAddress, { encoding: 'base64' })
.send();
const account = response.value;
if (!account) {
console.log('No account exists at this address on devnet.');
} else {
console.table({
owner: account.owner,
executable: account.executable,
lamports: account.lamports,
dataLength: account.data[0].length,
});
}Raw base64 data has no universal meaning. Decode a token account with the token program layout, an Anchor account with its IDL and discriminator, or a custom native account with its documented schema.
Ownership and authority answer different questions
Runtime ownership answers: which program may change this account's data or debit its lamports?
Application authority answers: which signer or PDA may request a specific change under that program's rules?
A token account demonstrates both layers. The Token Program owns the account, but the token account data stores an authority. The program checks that authority before approving a transfer.
A custom vault works the same way. Your program may own the vault state account while a PDA acts as its transfer authority. Neither relationship implies that a human has the PDA's private key.
Signer and writable flags are transaction permissions
Each instruction declares the accounts it uses and whether each account must sign or may be written.
A signer flag proves authorization from a transaction signature or program-derived signing path. A writable flag allows state changes during execution. Marking an account writable does not grant ownership, and naming an account as a signer does not make an impossible PDA signature appear.
Treat account metadata as least-privilege input. Extra writable accounts increase lock contention, while missing signer or writable flags cause instruction failure.
Account creation has three separate steps
Creating state is more than generating an address.
First, choose or derive the address. A keypair can supply a new on-curve address. Seeds and a program ID can derive a PDA.
Second, allocate enough bytes and fund the account with the required lamports. Solana currently requires new accounts to maintain a rent-exempt balance proportional to their data size. That balance acts like a refundable deposit and can be recovered when an account is closed correctly.
Third, assign the owner program and initialize the data. An allocated account full of zero bytes is not automatically valid application state.
Frameworks such as Anchor combine these steps behind account constraints. The runtime rules still apply, so understanding them makes initialization errors much easier to diagnose.
Avoid the account mistakes that lose time
Reading from the wrong cluster
An address can exist on mainnet and be absent on devnet. Always display or log the selected cluster alongside debugging output.
Sending tokens to the wallet address
SOL transfers can target a System Program wallet. SPL token transfers usually target a token account for the correct mint. Derive the recipient ATA, check whether it exists, and make an explicit decision about who funds its creation.
Trusting an address string without fetching it
Base58 validity proves only that text can represent bytes. It does not prove that an account exists, belongs to the expected program, or contains the expected type.
For payment flows, fetch the destination and verify its owner and decoded account type. The official Solana payment guidance recommends conservative handling for unknown off-curve destinations because funds can become inaccessible.
Using unstable PDA seeds
Changing seed order, encoding, or labels changes the derived address. Treat a production PDA seed scheme like a database key contract.
Use stable prefixes, document encodings, include domain separation, and test client and program derivation against the same fixtures. Save the canonical bump when your program schema expects it.
Allocating the wrong data size
Too little space breaks serialization or later upgrades. Too much space locks more SOL than needed. Calculate serialized size, include framework discriminators, and plan how future versions will migrate or reallocate data.
A practical account map for your feature
Before writing an instruction, create a short account map:
| Account | Owner program | Signer | Writable | Purpose |
| --- | --- | --- | --- | --- |
| User wallet | System Program | Yes | Maybe | Authority and fee payer |
| Feature PDA | Your program | Program-derived | Yes | Persistent feature state |
| Token mint | Token Program | No | No | Defines the token |
| User ATA | Token Program | User authority | Yes | Holds the user's token balance |
| System Program | Native loader | No | No | Creates accounts and moves SOL |
The map is a design tool, review artifact, and debugging checklist. If you cannot explain why an account is present or writable, the instruction boundary needs another pass.
FAQ
Is every Solana address an account?
An address can be derived or generated before an account exists there. Fetch it from the correct cluster to confirm existence.
Can a PDA hold SOL or tokens?
Yes. A PDA account can hold lamports. It can also be the authority for token accounts. The deriving program authorizes PDA actions through the runtime rather than a private key.
Why does one wallet have many token accounts?
Each token account holds one mint. The wallet authority can control separate accounts for USDC, another fungible token, an NFT, or Token-2022 assets.
What is the difference between an owner and an authority?
The owner is the program allowed to modify runtime account state. An authority is an address recorded by that program and checked under its application rules.
Does rent remove SOL over time?
New accounts are expected to maintain a rent-exempt minimum. Think of the balance as a recoverable deposit tied to data size, not a recurring application subscription.
Primary references
- Solana core concepts
- Solana accounts
- Program Derived Addresses
- SPL Token basics
- Solana payment address verification
Next step: Use the Account Inspection Loop on one address from your app. If the resulting account map exposes unclear authority or ownership, bring it to the Builderz project fit form.



