I recently started building with Turbin3, and before getting deeper into SPL tokens, NFTs and Anchor, I wanted to properly understand what is actually happening underneath the code.
I’ve worked with other ecosystems before, including Sui and Cardano, and one thing I’ve noticed is that every blockchain has its own way of structuring things. With Solana, concepts like accounts, programs, instructions, transactions, PDAs and CPI come up almost everywhere. Once I started seeing how they connect instead of learning each one separately, the architecture became much easier to understand.
So before getting into tokens and writing more code, I wanted to start with the mental model I currently use for Solana.
The simplest way I understand Solana
When I perform an action on Solana, I picture the flow like this:
Wallet
↓
Transaction
↓
Instruction(s)
↓
Program
↓
Accounts
↓
State changes
My wallet signs a transaction. That transaction contains one or more instructions, and each instruction tells a particular program what action I want it to perform and which accounts are involved. The program processes that instruction, and if everything is valid, the relevant accounts are updated.
That simple flow helped me connect most of the concepts I was learning.
Programs contain logic, accounts contain state
This was one of the first distinctions that made Solana easier for me to reason about.
A program contains the logic and rules for what can happen, while accounts are where data or state lives.
For example, imagine I’m building an escrow. The program could contain the rules for depositing, releasing and refunding funds, while an account could store information such as the sender, receiver, amount and current status of that escrow.
Coming from backend development, I loosely think about it like this:
Program ≈ backend logic
Account ≈ stored state/data
It’s not a perfect comparison, but it gives me a useful starting point.
Another thing that initially needed some clarification was account ownership. When we say an account is owned by a program, we are not talking about the person who controls a wallet. The owner is the program that has authority to modify that account’s data according to Solana’s rules.
Instructions tell programs what to do
Once programs and accounts make sense, instructions become much easier to understand.
An instruction is basically a request for a specific program to perform an action. For example:
System Program → Create an account
Token Program → Mint tokens
Token Program → Transfer tokens
An instruction needs to identify the program being called, the accounts involved, and the data describing what should happen.
So I think about an instruction as:
Program → Who should execute this?
Accounts → What accounts are involved?
Data → What should the program do?
The accounts passed into an instruction can also have different roles. A signer means authorization from that account is required, while a writable account is one whose state may be changed by the instruction.
Transactions bring instructions together
An instruction describes an action, but a transaction is what packages one or more of those actions together and sends them to Solana.
For example, when we create an SPL Token Mint later, one transaction can contain:
Transaction
│
├── Instruction 1
│ System Program → Create Account
│
└── Instruction 2
Token Program → Initialize Mint
This is something I found useful to understand early: creating the account and initializing it as a Mint are actually two different operations, even though we can put both instructions inside the same transaction.
The transaction also contains things like the required signatures, a fee payer and a recent blockhash. The fee payer pays for the transaction, while the recent blockhash helps keep the transaction fresh and prevents old transactions from being reused indefinitely.
Once the required signatures are there, the transaction is sent to the network, validators process its instructions, and the state of the relevant accounts can change.
Where do PDAs fit into this?
PDAs, or Program Derived Addresses, sounded more complicated to me at first than they actually turned out to be.
A PDA is an address derived from:
Program ID + Seeds + Bump → PDA
Unlike a normal wallet address, a PDA has no private key. It is derived to be off the Ed25519 curve, so there isn’t a normal keypair sitting somewhere that can sign for it.
Instead, the Solana runtime allows the program associated with that PDA to act with its authority when the correct seeds and bump are provided.
This makes PDAs useful for things like program state, vaults and escrows.
For example, an escrow program could derive an address using:
"escrow" + user wallet + escrow id
Because the derivation is deterministic, the same inputs will always lead to the same PDA.
The bump is an extra byte used during this process to find a valid off-curve PDA.
One distinction I also found important is that deriving a PDA does not create an account. Derivation only gives us an address. If we want an actual account there to hold data or lamports, that account still needs to be created.
CPI is how programs work with other programs
The last piece of this mental model is CPI, or Cross Program Invocation.
CPI simply means one Solana program calling another program.
Suppose I build an escrow program that needs to transfer tokens. My program doesn’t need to implement the Token Program’s functionality itself. It can invoke the Token Program:
Escrow Program
↓ CPI
Token Program
↓
Transfer tokens
That ability for programs to invoke other programs is a big part of how programs compose on Solana.
PDAs also become useful here. If a PDA needs to act as an authority during a CPI, the program can provide the PDA’s seeds and bump, allowing the runtime to verify that the PDA was derived by that program.
How I approach Solana code now
One thing I’m trying to avoid while learning Solana is memorizing SDK functions without understanding why I’m calling them.
Instead, when I see a piece of Solana code, I first try to figure out:
- What program am I calling?
- What accounts does it need?
- Which accounts need to sign?
- Which accounts can change?
- What instruction am I asking the program to perform?
- Is another program being called through CPI?
- Is there a PDA involved?
Once I can answer those questions, the SDK functions start to feel more like tools for expressing something I already understand rather than random functions I need to memorize.
So the mental model I’m taking forward is still:
Wallet
↓
Transaction
↓
Instruction(s)
↓
Program
↓
Accounts
↓
State changes
With two important additions:
Program A → CPI → Program B
Program ID + Seeds + Bump → PDA
That’s the foundation I wanted to get right before moving deeper into SPL tokens and NFTs.
In the next part, we’ll put this architecture into practice by creating an SPL Token Mint on Solana Devnet. That’s where we’ll see the System Program, Token Program, accounts, instructions, signers and transactions actually come together in code.
Top comments (0)