pinocchio_toolkit/
lib.rs

1#![no_std]
2
3//! Utilities for Pinocchio Solana programs.
4
5use core::mem::MaybeUninit;
6use pinocchio::{
7    account_info::AccountInfo,
8    instruction::{Seed, Signer},
9    program_error::ProgramError,
10    pubkey::Pubkey,
11    sysvars::{rent::Rent, Sysvar},
12    ProgramResult,
13};
14use pinocchio_pubkey::derive_address;
15use pinocchio_system::instructions::CreateAccount;
16
17pub fn create_pda_account<const N: usize>(
18    payer: &AccountInfo,
19    pda: &AccountInfo,
20    program_id: &Pubkey,
21    space: u64,
22    seeds: &[&[u8]],
23    bump: u8,
24) -> ProgramResult {
25    // Basic checks
26    if !payer.is_signer() {
27        return Err(ProgramError::MissingRequiredSignature);
28    }
29    if pda.lamports() != 0 {
30        return Err(ProgramError::AccountAlreadyInitialized);
31    }
32
33    if seeds.len() + 1 > N {
34        return Err(ProgramError::InvalidArgument);
35    }
36
37    let bump_seed = [bump];
38    let mut seeds_with_bump: [&[u8]; N] = [&[]; N];
39
40    // Copy seeds using slice operations
41    let seeds_count = seeds.len();
42    seeds_with_bump[..seeds_count].copy_from_slice(seeds);
43    seeds_with_bump[seeds_count] = &bump_seed;
44
45    // Verify PDA derivation (None because bump is already in seeds_with_bump)
46    let expected_pda = derive_address(&seeds_with_bump, None, program_id);
47    if &expected_pda != pda.key() {
48        return Err(ProgramError::InvalidSeeds);
49    }
50
51    // Calculate rent
52    let rent = Rent::get()?;
53    let lamports = rent.minimum_balance(space as usize);
54
55    // Convert &[u8] slices to Seed types using MaybeUninit
56    let mut pinocchio_seeds: [MaybeUninit<Seed>; N] =
57        unsafe { MaybeUninit::uninit().assume_init() };
58    for i in 0..=seeds_count {
59        pinocchio_seeds[i] = MaybeUninit::new(Seed::from(seeds_with_bump[i]));
60    }
61
62    let signer_seeds: &[Seed] = unsafe {
63        core::slice::from_raw_parts(pinocchio_seeds.as_ptr() as *const Seed, seeds_count + 1)
64    };
65
66    // Create account using pinocchio-system CPI
67    CreateAccount {
68        from: payer,
69        to: pda,
70        lamports,
71        space,
72        owner: program_id,
73    }
74    .invoke_signed(&[Signer::from(signer_seeds)])?;
75
76    Ok(())
77}