Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Solana Rust Interview Questions

Note: These questions test interview performance—concise answers, clear explanations, quick code snippets. Real work involves deeper problem-solving, debugging, reading existing codebases, and iteration. Interview skills ≠ work skills. Use this for interviews, not as a measure of engineering ability.

1. Why can't a program modify an account it doesn't own?

Answer: Every account in Solana has an owner field pointing to a program ID. The runtime enforces that only the owner program can modify account data. This is checked at runtime—if a non-owner tries to write, the transaction fails with IncorrectProgramId.

Why this matters: This is Solana's core security model. Programs own their data. To modify state, you must either be the owner or CPI to the owner program.

Rust snippet:

#![allow(unused)]
fn main() {
if account.owner != program_id {
    return Err(ProgramError::IncorrectProgramId);
}
}

2. How does a DeFi protocol deterministically find a user's vault account?

Answer: Using PDAs (Program Derived Addresses). You derive a deterministic address from seeds + program ID using find_program_address. Same seeds = same address every time.

Why PDAs: They look like public keys but have no private key. Only the program can sign for them, enabling program-controlled state.

Rust snippet:

#![allow(unused)]
fn main() {
let (vault_pda, bump) = Pubkey::find_program_address(
    &[b"vault", user.key().as_ref()],
    program_id
);
}

Common pattern: User vaults, escrow accounts, configuration PDAs.


3. What happens if you don't include a required signer in an instruction?

Answer: The transaction fails with MissingRequiredSignature. The runtime checks that every account marked as signer actually signed the transaction.

Why signers matter: Authorization. Payers sign for fees. Account owners sign for debits. Programs require signers for privileged operations.

Missing signer check = vulnerability:

#![allow(unused)]
fn main() {
// BAD: Anyone can call this
pub fn withdraw(ctx: Context<Withdraw>) -> Result<()> {
    // No signer check!
    let authority = &ctx.accounts.authority;
    // Transfer from authority to user
}

// GOOD: Require signer
pub fn withdraw(ctx: Context<Withdraw>) -> Result<()> {
    require!(ctx.accounts.authority.is_signer);
    // Now only authority can withdraw
}
}

4. Why do some programs fail with "compute budget exceeded"?

Answer: Solana transactions have a compute budget (200k default, 1.4M max). Every operation costs compute units. Expensive operations (deserialization, CPI, loops) can exceed the limit.

Common culprits:

  • Deserializing large accounts (Borsh is expensive)
  • CPI calls (each adds overhead)
  • Loops over many accounts
  • Crypto operations

Optimization strategies:

#![allow(unused)]
fn main() {
// BAD: Deserialize entire account
let account_data: MyAccount = deserialize(&account.data.borrow())?;

// BETTER: Only read what you need
let count = u64::from_le_bytes(
    account.data.borrow()[0..8].try_into().unwrap()
);

// Request more CU if needed
let compute_budget_ix = ComputeBudgetInstruction::request_units(400_000);
}

5. How does Jupiter swap tokens without holding them?

Answer: Jupiter doesn't hold tokens. It uses CPI to call DEX programs (Raydium, Orca, etc.) which hold tokens in their pools. Jupiter finds the best route, quotes the price, and executes the swap via CPI.

The flow:

  1. Jupiter receives swap request
  2. Quotes across all DEXs
  3. Finds best route
  4. CPI to DEX program (e.g., Raydium)
  5. DEX executes swap from its pools
  6. Returns result to caller

Rust snippet (simplified):

#![allow(unused)]
fn main() {
// Jupiter calls DEX via CPI
let swap_ix = raydium::swap(
    from_token_account,
    to_token_account,
    amount_in,
    minimum_amount_out,
);

invoke(
    &swap_ix,
    &[
        from_token_account,
        to_token_account,
        dex_program,
    ],
)?;
}

Key insight: Jupiter is an aggregator, not a vault. It routes to protocols that hold liquidity.


Quick Reference

Core Solana Concepts:

  • Accounts: Everything is an account, programs own accounts
  • PDAs: Program-controlled addresses, no private keys
  • CPI: Cross-program invocation, programs call other programs
  • Signers: Authorization, must sign transactions
  • Compute Units: Resource limits, optimize expensive operations

Security Patterns:

  • Always check account.owner == program_id
  • Always check account.is_signer for authorization
  • Always validate account data before use
  • Never trust caller-provided accounts without validation

Performance Patterns:

  • Minimize deserialization (use bytemuck or direct byte access)
  • Batch operations where possible
  • Use parallel account access when safe
  • Profile compute unit usage