Program API Reference

The Program API is the core of Rudy Framework. It provides macros and utilities for defining Solana programs with minimal boilerplate.

#[program] Macro

The #[program] macro is the entry point for defining a Solana program. It generates all necessary boilerplate for instruction dispatching and account validation.

Basic Usage

1use rudy::prelude::*;
2
3#[program]
4pub mod my_program {
5    use super::*;
6
7    pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
8        // Your initialization logic here
9        Ok(())
10    }
11
12    pub fn update(ctx: Context<Update>, value: u64) -> Result<()> {
13        // Your update logic here
14        Ok(())
15    }
16}

Context Type

The Context type provides access to accounts, program ID, and other execution context. It ensures type safety and automatic validation.

Context Definition

1pub struct Context<'a, 'b, 'c, 'info, T> {
2    /// The program ID of the currently executing program
3    pub program_id: &'a Pubkey,
4    /// Deserialized accounts
5    pub accounts: &'b mut T,
6    /// Remaining accounts not deserialized
7    pub remaining_accounts: &'c [AccountInfo<'info>],
8}

Accessing Context

1pub fn my_instruction(ctx: Context<MyAccounts>) -> Result<()> {
2    // Access program ID
3    let program_id = ctx.program_id;
4    
5    // Access accounts
6    let my_account = &mut ctx.accounts.my_account;
7    
8    // Access remaining accounts
9    for account in ctx.remaining_accounts.iter() {
10        msg!("Processing account: {}", account.key);
11    }
12    
13    Ok(())
14}

Error Handling

Rudy provides a robust error handling system with custom error types and automatic propagation.

Defining Errors

1use rudy::prelude::*;
2
3#[error_code]
4pub enum MyError {
5    #[msg("The account is not authorized")]
6    Unauthorized,
7    
8    #[msg("Invalid amount provided")]
9    InvalidAmount,
10    
11    #[msg("Account already initialized")]
12    AlreadyInitialized,
13}

Using Errors

1pub fn transfer(ctx: Context<Transfer>, amount: u64) -> Result<()> {
2    if amount == 0 {
3        return Err(MyError::InvalidAmount.into());
4    }
5    
6    require!(
7        ctx.accounts.from.owner == ctx.accounts.authority.key(),
8        MyError::Unauthorized
9    );
10    
11    // Transfer logic here
12    Ok(())
13}

Program Utilities

Logging

1// Basic logging
2msg!("Hello, Solana!");
3
4// Formatted logging
5msg!("Processing {} tokens for {}", amount, user);
6
7// Conditional logging
8if cfg!(feature = "debug") {
9    msg!("Debug: Account balance = {}", balance);
10}

Program Address Derivation

1// Find program derived address
2let (pda, bump) = Pubkey::find_program_address(
3    &[b"seed", user.key().as_ref()],
4    ctx.program_id,
5);
6
7// Create PDA with known bump
8let pda = Pubkey::create_program_address(
9    &[b"seed", user.key().as_ref(), &[bump]],
10    ctx.program_id,
11)?;

CPI (Cross-Program Invocation)

1use rudy::prelude::*;
2use spl_token::instruction as token_instruction;
3
4pub fn transfer_tokens(ctx: Context<TransferTokens>, amount: u64) -> Result<()> {
5    let transfer_ix = token_instruction::transfer(
6        &spl_token::id(),
7        &ctx.accounts.from.key(),
8        &ctx.accounts.to.key(),
9        &ctx.accounts.authority.key(),
10        &[],
11        amount,
12    )?;
13    
14    invoke(
15        &transfer_ix,
16        &[
17            ctx.accounts.from.to_account_info(),
18            ctx.accounts.to.to_account_info(),
19            ctx.accounts.authority.to_account_info(),
20        ],
21    )?;
22    
23    Ok(())
24}

Best Practices

  • Always validate account ownership and signatures
  • Use require! for assertions with custom errors
  • Implement proper error handling for all edge cases
  • Use PDAs for program-owned accounts
  • Minimize compute usage by batching operations