# Built-in Programs This reference provides comprehensive coverage of Solana's built-in programs for native Rust development, focusing on the System Program and Compute Budget Program. ## Table of Contents 1. [Overview of Built-in Programs](#overview-of-built-in-programs) 2. [System Program](#system-program) 3. [Compute Budget Program](#compute-budget-program) 4. [Other Built-in Programs](#other-built-in-programs) 5. [CPI Patterns](#cpi-patterns) 6. [Best Practices](#best-practices) --- ## Overview of Built-in Programs **Built-in programs** (also called native programs) are fundamental Solana programs that provide core blockchain functionality. ### Key Built-in Programs | Program | Program ID | Purpose | |---------|-----------|---------| | **System Program** | `11111111111111111111111111111111` | Account creation, transfers, allocation | | **Compute Budget** | `ComputeBudget111111111111111111111111111111` | CU limits, heap size, priority fees | | **BPF Loader** | Various | Loading and executing programs | | **Config Program** | `Config1111111111111111111111111111111111111` | Validator configuration | | **Stake Program** | `Stake11111111111111111111111111111111111111` | Staking and delegation | | **Vote Program** | `Vote111111111111111111111111111111111111111` | Validator voting | This reference focuses on the two most commonly used in program development: **System Program** and **Compute Budget Program**. --- ## System Program **Program ID:** `solana_program::system_program::ID` (`11111111111111111111111111111111`) The System Program is responsible for account creation, lamport transfers, and account management. ### Core Functionality 1. **Create accounts** (regular and PDAs) 2. **Transfer lamports** between accounts 3. **Allocate space** for account data 4. **Assign ownership** to programs 5. **Create nonce accounts** for durable transactions ### System Program Instructions ```rust use solana_program::system_instruction; pub enum SystemInstruction { CreateAccount, // Create new account Assign, // Assign account to program Transfer, // Transfer lamports CreateAccountWithSeed,// Create account with seed AdvanceNonceAccount, // Advance nonce WithdrawNonceAccount, // Withdraw from nonce InitializeNonceAccount, // Initialize nonce Allocate, // Allocate account space AllocateWithSeed, // Allocate with seed AssignWithSeed, // Assign with seed TransferWithSeed, // Transfer with seed UpgradeNonceAccount, // Upgrade nonce (v4) } ``` --- ### CreateAccount **Creates a new account with lamports and data space.** #### Function Signature ```rust pub fn create_account( from_pubkey: &Pubkey, // Funding account (must be signer) to_pubkey: &Pubkey, // New account address lamports: u64, // Lamports to fund account space: u64, // Bytes of data space owner: &Pubkey, // Program that will own the account ) -> Instruction ``` #### Usage in Native Rust ```rust use solana_program::{ system_instruction, program::invoke, }; pub fn create_new_account( payer: &AccountInfo, new_account: &AccountInfo, system_program: &AccountInfo, program_id: &Pubkey, ) -> ProgramResult { let space = 100; // Account data size let rent = Rent::get()?; let lamports = rent.minimum_balance(space); let create_account_ix = system_instruction::create_account( payer.key, new_account.key, lamports, space as u64, program_id, ); invoke( &create_account_ix, &[ payer.clone(), new_account.clone(), system_program.clone(), ], )?; msg!("Created account with {} bytes", space); Ok(()) } ``` #### Creating PDA Accounts ```rust use solana_program::program::invoke_signed; pub fn create_pda_account( payer: &AccountInfo, pda_account: &AccountInfo, system_program: &AccountInfo, program_id: &Pubkey, seeds: &[&[u8]], bump: u8, ) -> ProgramResult { // Verify PDA let (expected_pda, _bump) = Pubkey::find_program_address(seeds, program_id); if expected_pda != *pda_account.key { return Err(ProgramError::InvalidSeeds); } let space = 200; let rent = Rent::get()?; let lamports = rent.minimum_balance(space); let create_account_ix = system_instruction::create_account( payer.key, pda_account.key, lamports, space as u64, program_id, ); // Create full seeds with bump let mut full_seeds = seeds.to_vec(); full_seeds.push(&[bump]); let signer_seeds: &[&[&[u8]]] = &[&full_seeds]; invoke_signed( &create_account_ix, &[payer.clone(), pda_account.clone(), system_program.clone()], signer_seeds, )?; msg!("Created PDA account at {}", pda_account.key); Ok(()) } ``` --- ### Transfer **Transfers lamports from one account to another.** #### Function Signature ```rust pub fn transfer( from_pubkey: &Pubkey, // Source account (must be signer) to_pubkey: &Pubkey, // Destination account lamports: u64, // Amount to transfer ) -> Instruction ``` #### Usage in Native Rust ```rust pub fn transfer_lamports( from: &AccountInfo, to: &AccountInfo, system_program: &AccountInfo, amount: u64, ) -> ProgramResult { let transfer_ix = system_instruction::transfer( from.key, to.key, amount, ); invoke( &transfer_ix, &[from.clone(), to.clone(), system_program.clone()], )?; msg!("Transferred {} lamports from {} to {}", amount, from.key, to.key); Ok(()) } ``` #### Transfer from PDA ```rust pub fn transfer_from_pda( pda: &AccountInfo, to: &AccountInfo, system_program: &AccountInfo, amount: u64, seeds: &[&[u8]], bump: u8, ) -> ProgramResult { let transfer_ix = system_instruction::transfer( pda.key, to.key, amount, ); let mut full_seeds = seeds.to_vec(); full_seeds.push(&[bump]); let signer_seeds: &[&[&[u8]]] = &[&full_seeds]; invoke_signed( &transfer_ix, &[pda.clone(), to.clone(), system_program.clone()], signer_seeds, )?; Ok(()) } ``` --- ### Allocate **Allocates space for an account's data.** #### Function Signature ```rust pub fn allocate( pubkey: &Pubkey, // Account to allocate (must be signer) space: u64, // Bytes to allocate ) -> Instruction ``` #### Usage in Native Rust ```rust pub fn allocate_account_space( account: &AccountInfo, system_program: &AccountInfo, space: u64, ) -> ProgramResult { let allocate_ix = system_instruction::allocate( account.key, space, ); invoke( &allocate_ix, &[account.clone(), system_program.clone()], )?; msg!("Allocated {} bytes for account", space); Ok(()) } ``` **⚠️ Note:** The account must be owned by the System Program before allocating. Most programs use `create_account` instead, which combines allocation with ownership assignment. --- ### Assign **Assigns an account to a program (changes owner).** #### Function Signature ```rust pub fn assign( pubkey: &Pubkey, // Account to assign (must be signer) owner: &Pubkey, // New owner program ) -> Instruction ``` #### Usage in Native Rust ```rust pub fn assign_to_program( account: &AccountInfo, system_program: &AccountInfo, new_owner: &Pubkey, ) -> ProgramResult { let assign_ix = system_instruction::assign( account.key, new_owner, ); invoke( &assign_ix, &[account.clone(), system_program.clone()], )?; msg!("Assigned account to program {}", new_owner); Ok(()) } ``` **⚠️ Note:** Most programs use `create_account` which handles assignment during creation. --- ### Complete Example: Account Lifecycle ```rust use solana_program::{ account_info::{next_account_info, AccountInfo}, entrypoint::ProgramResult, program::invoke_signed, pubkey::Pubkey, system_instruction, sysvar::{rent::Rent, Sysvar}, }; #[derive(BorshSerialize, BorshDeserialize)] pub struct UserData { pub user: Pubkey, pub balance: u64, pub created_at: i64, } pub fn create_user_account( program_id: &Pubkey, accounts: &[AccountInfo], user_pubkey: Pubkey, ) -> ProgramResult { let account_info_iter = &mut accounts.iter(); let payer = next_account_info(account_info_iter)?; let user_account = next_account_info(account_info_iter)?; let system_program = next_account_info(account_info_iter)?; // 1. Derive PDA let seeds = &[b"user", user_pubkey.as_ref()]; let (pda, bump) = Pubkey::find_program_address(seeds, program_id); if pda != *user_account.key { return Err(ProgramError::InvalidSeeds); } // 2. Calculate space and rent let space = std::mem::size_of::(); let rent = Rent::get()?; let lamports = rent.minimum_balance(space); // 3. Create account via System Program CPI let create_ix = system_instruction::create_account( payer.key, user_account.key, lamports, space as u64, program_id, ); let signer_seeds: &[&[&[u8]]] = &[&[b"user", user_pubkey.as_ref(), &[bump]]]; invoke_signed( &create_ix, &[payer.clone(), user_account.clone(), system_program.clone()], signer_seeds, )?; // 4. Initialize account data let clock = Clock::get()?; let user_data = UserData { user: user_pubkey, balance: 0, created_at: clock.unix_timestamp, }; user_data.serialize(&mut &mut user_account.data.borrow_mut()[..])?; msg!("Created user account for {}", user_pubkey); Ok(()) } ``` --- ## Compute Budget Program **Program ID:** `solana_program::compute_budget::ID` (`ComputeBudget111111111111111111111111111111`) The Compute Budget Program allows transactions to request specific compute unit limits, heap sizes, and priority fees. ### Core Functionality 1. **Set compute unit limit** - Maximum CUs for transaction 2. **Set compute unit price** - Priority fee per CU 3. **Request heap size** - Heap memory allocation ### Compute Budget Instructions ```rust use solana_program::compute_budget::ComputeBudgetInstruction; pub enum ComputeBudgetInstruction { RequestUnitsDeprecated, // Deprecated RequestHeapFrame(u32), // Request heap frame (bytes) SetComputeUnitLimit(u32), // Set max CUs SetComputeUnitPrice(u64), // Set priority fee (microlamports per CU) SetLoadedAccountsDataSizeLimit(u32), // Set loaded accounts data limit } ``` --- ### SetComputeUnitLimit **Sets the maximum compute units available to the transaction.** #### Function Signature ```rust pub fn set_compute_unit_limit(units: u32) -> Instruction ``` #### Default Limits - **Default per instruction:** 200,000 CUs - **Default per transaction:** 1,400,000 CUs (with requested CU limit) - **Maximum:** 1,400,000 CUs #### Usage in Native Rust **Important:** Compute Budget instructions are added to the transaction by the **client**, not inside the program. **Client-side example (for reference):** ```rust // This code runs CLIENT-SIDE, not in the program use solana_sdk::{ compute_budget::ComputeBudgetInstruction, transaction::Transaction, }; let compute_budget_ix = ComputeBudgetInstruction::set_compute_unit_limit(400_000); let transaction = Transaction::new_signed_with_payer( &[ compute_budget_ix, // Must be first your_program_ix, ], Some(&payer.pubkey()), &[&payer], recent_blockhash, ); ``` **⚠️ Note:** Programs cannot modify their own compute budget. These instructions must be added client-side before sending the transaction. --- ### SetComputeUnitPrice **Sets the priority fee per compute unit (for transaction prioritization).** #### Function Signature ```rust pub fn set_compute_unit_price(microlamports: u64) -> Instruction ``` #### Priority Fee Calculation ``` Total Priority Fee = (CUs Used × microlamports) / 1,000,000 ``` **Example:** - CUs used: 50,000 - Price: 10,000 microlamports per CU - Fee: (50,000 × 10,000) / 1,000,000 = 500 lamports #### Usage (Client-side) ```rust // Client-side code let compute_unit_price_ix = ComputeBudgetInstruction::set_compute_unit_price(20_000); let transaction = Transaction::new_signed_with_payer( &[ compute_unit_price_ix, // Set priority fee your_program_ix, ], Some(&payer.pubkey()), &[&payer], recent_blockhash, ); ``` **Use cases:** - High-priority transactions (arbitrage, liquidations) - Congested network periods - Time-sensitive operations --- ### RequestHeapFrame **Requests additional heap memory for the transaction.** #### Function Signature ```rust pub fn request_heap_frame(bytes: u32) -> Instruction ``` #### Default Heap - **Default:** 32 KB - **Maximum:** 256 KB #### Usage (Client-side) ```rust // Client-side code let heap_size_ix = ComputeBudgetInstruction::request_heap_frame(256 * 1024); // 256 KB let transaction = Transaction::new_signed_with_payer( &[ heap_size_ix, // Request more heap your_program_ix, ], Some(&payer.pubkey()), &[&payer], recent_blockhash, ); ``` **When to use:** - Large data structures - Complex deserialization - Temporary buffers **⚠️ Cost:** Requesting heap increases CU consumption. --- ### SetLoadedAccountsDataSizeLimit **Sets the maximum total size of loaded account data.** #### Function Signature ```rust pub fn set_loaded_accounts_data_size_limit(bytes: u32) -> Instruction ``` #### Default Limit - **Default:** 64 MB per transaction #### Usage (Client-side) ```rust // Client-side code let accounts_data_limit_ix = ComputeBudgetInstruction::set_loaded_accounts_data_size_limit(128 * 1024 * 1024); let transaction = Transaction::new_signed_with_payer( &[ accounts_data_limit_ix, your_program_ix, ], Some(&payer.pubkey()), &[&payer], recent_blockhash, ); ``` **Use cases:** - Transactions with many large accounts - Bulk processing operations --- ### Complete Client-side Example ```rust use solana_sdk::{ compute_budget::ComputeBudgetInstruction, transaction::Transaction, signature::{Keypair, Signer}, pubkey::Pubkey, }; pub fn build_optimized_transaction( payer: &Keypair, program_id: &Pubkey, program_ix_data: &[u8], accounts: Vec, recent_blockhash: Hash, ) -> Transaction { // 1. Set compute unit limit (if default 200k is insufficient) let compute_limit_ix = ComputeBudgetInstruction::set_compute_unit_limit(300_000); // 2. Set priority fee (for faster processing) let compute_price_ix = ComputeBudgetInstruction::set_compute_unit_price(10_000); // 3. Request additional heap if needed let heap_size_ix = ComputeBudgetInstruction::request_heap_frame(128 * 1024); // 128 KB // 4. Your program instruction let program_ix = Instruction { program_id: *program_id, accounts, data: program_ix_data.to_vec(), }; // 5. Build transaction (compute budget instructions FIRST) Transaction::new_signed_with_payer( &[ compute_limit_ix, compute_price_ix, heap_size_ix, program_ix, ], Some(&payer.pubkey()), &[payer], recent_blockhash, ) } ``` --- ## Other Built-in Programs ### BPF Loader **Purpose:** Loads and executes Solana programs. **Program IDs:** - `BPFLoader1111111111111111111111111111111111` (deprecated) - `BPFLoader2111111111111111111111111111111111` (upgradeable) - `BPFLoaderUpgradeab1e11111111111111111111111` (current) **Usage:** Primarily used by the runtime. Programs rarely interact with BPF Loader directly. ### Stake Program **Program ID:** `Stake11111111111111111111111111111111111111` **Purpose:** Staking SOL to validators. **Common operations:** - Create stake accounts - Delegate stake - Deactivate stake - Withdraw stake **Use case:** Staking pools, liquid staking protocols. ### Vote Program **Program ID:** `Vote111111111111111111111111111111111111111` **Purpose:** Validator voting and consensus. **Use case:** Validator operations, rarely used by general programs. --- ## CPI Patterns ### System Program CPI Pattern **Standard pattern for calling System Program:** ```rust use solana_program::{ program::invoke, system_instruction, }; pub fn system_program_cpi( from: &AccountInfo, to: &AccountInfo, system_program: &AccountInfo, ) -> ProgramResult { // 1. Verify System Program if system_program.key != &solana_program::system_program::ID { return Err(ProgramError::IncorrectProgramId); } // 2. Create instruction let ix = system_instruction::transfer(from.key, to.key, 1_000_000); // 3. Invoke invoke(&ix, &[from.clone(), to.clone(), system_program.clone()])?; Ok(()) } ``` ### PDA Signing Pattern **When PDAs need to sign:** ```rust pub fn pda_system_cpi( pda: &AccountInfo, to: &AccountInfo, system_program: &AccountInfo, program_id: &Pubkey, seeds: &[&[u8]], bump: u8, ) -> ProgramResult { // 1. Verify PDA let (expected_pda, _) = Pubkey::find_program_address(seeds, program_id); if expected_pda != *pda.key { return Err(ProgramError::InvalidSeeds); } // 2. Create instruction let ix = system_instruction::transfer(pda.key, to.key, 500_000); // 3. Prepare signer seeds let mut full_seeds = seeds.to_vec(); full_seeds.push(&[bump]); let signer_seeds: &[&[&[u8]]] = &[&full_seeds]; // 4. Invoke with PDA signature invoke_signed( &ix, &[pda.clone(), to.clone(), system_program.clone()], signer_seeds, )?; Ok(()) } ``` ### Validation Pattern **Always validate accounts before CPI:** ```rust pub fn safe_system_cpi( from: &AccountInfo, to: &AccountInfo, system_program: &AccountInfo, amount: u64, ) -> ProgramResult { // ✅ Validate System Program if system_program.key != &solana_program::system_program::ID { msg!("Invalid System Program"); return Err(ProgramError::IncorrectProgramId); } // ✅ Validate signer if !from.is_signer { msg!("From account must be signer"); return Err(ProgramError::MissingRequiredSignature); } // ✅ Validate sufficient balance if from.lamports() < amount { msg!("Insufficient balance"); return Err(ProgramError::InsufficientFunds); } // Execute CPI let ix = system_instruction::transfer(from.key, to.key, amount); invoke(&ix, &[from.clone(), to.clone(), system_program.clone()])?; Ok(()) } ``` --- ## Best Practices ### 1. Always Validate Program IDs ```rust // ✅ Validate before CPI if system_program.key != &solana_program::system_program::ID { return Err(ProgramError::IncorrectProgramId); } ``` ### 2. Use Rent Exemption ```rust // ✅ Always create accounts with rent exemption let rent = Rent::get()?; let lamports = rent.minimum_balance(space); // ❌ Don't use arbitrary amounts let lamports = 1_000_000; // May not be rent-exempt! ``` ### 3. Verify PDA Before Creation ```rust // ✅ Verify PDA derivation let (expected_pda, bump) = Pubkey::find_program_address(seeds, program_id); if expected_pda != *pda_account.key { return Err(ProgramError::InvalidSeeds); } ``` ### 4. Use invoke_signed for PDAs ```rust // ✅ PDAs sign with invoke_signed invoke_signed(&ix, accounts, signer_seeds)?; // ❌ Regular invoke won't work for PDA signers invoke(&ix, accounts)?; // Fails if PDA needs to sign ``` ### 5. Set Compute Budget Client-side ```rust // ✅ Add compute budget instructions in client let ixs = vec![ ComputeBudgetInstruction::set_compute_unit_limit(400_000), your_program_ix, ]; // ❌ Cannot set from within program // Programs cannot modify their own compute budget ``` ### 6. Order Compute Budget Instructions First ```rust // ✅ Compute budget instructions FIRST let ixs = vec![ compute_limit_ix, compute_price_ix, heap_size_ix, program_ix, ]; // ❌ Wrong order - may not apply let ixs = vec![ program_ix, compute_limit_ix, // Too late! ]; ``` ### 7. Check Account Ownership Before Transfer ```rust // ✅ Validate ownership for security if from_account.owner != &solana_program::system_program::ID { msg!("Can only transfer from System-owned accounts"); return Err(ProgramError::IllegalOwner); } ``` --- ## Summary **Key Takeaways:** 1. **System Program** handles account creation, transfers, and allocation 2. **Compute Budget Program** instructions are added **client-side**, not in programs 3. **Always validate** program IDs before CPI 4. **Use rent exemption** when creating accounts 5. **PDAs require invoke_signed** for signing operations **Most Common Operations:** | Operation | Instruction | Use Case | |-----------|------------|----------| | Create account | `create_account` | New program accounts | | Transfer lamports | `transfer` | SOL transfers | | Set CU limit | `set_compute_unit_limit` | High-CU transactions | | Set priority fee | `set_compute_unit_price` | Fast transaction processing | | Request heap | `request_heap_frame` | Large data operations | **System Program CPI Template:** ```rust // Validate if system_program.key != &solana_program::system_program::ID { return Err(ProgramError::IncorrectProgramId); } // Create instruction let ix = system_instruction::transfer(from.key, to.key, amount); // Invoke (or invoke_signed for PDAs) invoke(&ix, &[from.clone(), to.clone(), system_program.clone()])?; ``` **Compute Budget Client Template:** ```rust // Client-side let ixs = vec![ ComputeBudgetInstruction::set_compute_unit_limit(300_000), ComputeBudgetInstruction::set_compute_unit_price(10_000), your_program_ix, ]; ``` Master these built-in programs for efficient account management and transaction optimization in production Solana programs.