# Solana Program Development Fundamentals ## Solana vs EVM Core Differences | Concept | EVM (Ethereum) | Solana | |------|------------|--------| | **Smart Contracts** | Contract (code + state combined) | Program (code only) + Account (state only) | | **State Storage** | Internal contract storage | Independent Accounts (rent-based) | | **Concurrency** | Sequential execution | Parallel (can parallelize if AccountSets don't overlap) | | **Fees** | gas | transaction fee + rent | | **Token Standard** | ERC-20 | SPL Token (single official standard) | ## Account Model Everything on Solana is an Account: Regular account (user wallet): lamports(balance) + data(empty) + owner(System Program) + executable(false) Program Account: data(BPF bytecode) + executable(true) Data Account: data(custom serialized data) + owner(your program) — only the owner program can modify ### Rent (Account Rent) Accounts must maintain rent-exempt status (store enough SOL), otherwise they get garbage collected. 128 bytes ≈ 0.00204 SOL, 1KB ≈ 0.00714 SOL. ```bash solana rent 128 # Calculate rent-exempt amount ``` ## PDA (Program Derived Address) PDAs are program-controlled account addresses with no private key; only the program can sign. ```rust // Define PDA account in Anchor #[account( init, payer = user, space = 8 + 32 + 8, seeds = [b"vault", user.key().as_ref()], bump )] pub vault: Account<'info, Vault>, // Derive PDA off-chain let (pda, bump) = Pubkey::find_program_address( &[b"vault", user_pubkey.as_ref()], &program_id ); ``` Common PDA use cases: Token vault custody, user state storage, cross-program invocation signing. ## Anchor Framework Quick Start ```bash cargo install --git https://github.com/coral-xyz/anchor avm --locked avm install latest && avm use latest anchor init my-program && cd my-program anchor test # Compile + local test ``` ```rust use anchor_lang::prelude::*; declare_id!("YourProgramId"); #[program] pub mod counter { use super::*; pub fn initialize(ctx: Context) -> Result<()> { ctx.accounts.counter.count = 0; Ok(()) } pub fn increment(ctx: Context) -> Result<()> { ctx.accounts.counter.count += 1; Ok(()) } } #[derive(Accounts)] pub struct Initialize<'info> { #[account(init, payer = user, space = 8 + 8)] pub counter: Account<'info, Counter>, #[account(mut)] pub user: Signer<'info>, pub system_program: Program<'info, System>, } #[derive(Accounts)] pub struct Increment<'info> { #[account(mut)] pub counter: Account<'info, Counter>, } #[account] pub struct Counter { pub count: u64 } ``` ## SPL Token Each user + each token = separate Token Account (ATA). ```typescript import { getOrCreateAssociatedTokenAccount, transfer } from "@solana/spl-token" const ata = await getOrCreateAssociatedTokenAccount(connection, payer, mintAddress, ownerAddress) await transfer(connection, payer, senderATA.address, recipientATA.address, owner, 1000n) ``` ## Common Errors Quick Reference | Error | Cause | Solution | |------|------|------| | AccountNotFound | Account not initialized | Initialize account first | | InsufficientFunds | Insufficient lamports | Ensure rent-exempt | | ConstraintMut | Missing mut marker | Add mut constraint | | InvalidAccountOwner | Incorrect account ownership | Check account owner | | AccountAlreadyInitialized | Duplicate init | Use init_if_needed instead |