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