airdrop 0.1.0

Mint and Airdrop Framework on Solana for Sovereign Individuals
Documentation
use airdrop_api::{
    consts::FEE_ACCOUNT, instruction::WithdrawFees, loaders::AirdropAccountInfoValidation,
    pda::fee_account_pda, Config,
};
use solana_program::program::invoke_signed;
use steel::*;

pub fn process_withdraw_fees(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult {
    // Parse instruction data
    let args = WithdrawFees::try_from_bytes(data)?;
    let amount = u64::from_le_bytes(args.amount);

    // Load accounts
    let [admin_info, config_info, fee_account_info, beneficiary_info, system_program] = accounts
    else {
        return Err(ProgramError::NotEnoughAccountKeys);
    };

    // Validate admin
    admin_info.is_signer()?;

    // Load and validate config (mutable for updating total_fees_collected)
    let config = config_info
        .is_config()?
        .is_writable()?
        .as_account_mut::<Config>(&airdrop_api::ID)?
        .assert_mut(|c| c.admin == *admin_info.key)?;

    // Verify fee account PDA
    let (expected_fee_account, fee_bump) = fee_account_pda();
    fee_account_info
        .is_writable()?
        .has_address(&expected_fee_account)?;

    // Verify beneficiary
    beneficiary_info.is_writable()?;

    // Verify system program
    system_program.is_program(&system_program::ID)?;

    // Get current fee account balance
    let current_balance = **fee_account_info.lamports.borrow();

    // Calculate withdrawable amount
    let withdraw_amount = if amount == 0 {
        current_balance // Withdraw all if amount is 0
    } else {
        amount.min(current_balance) // Withdraw specified amount, capped by available
    };

    if withdraw_amount == 0 {
        return Err(ProgramError::InsufficientFunds);
    }

    // Transfer SOL from fee account PDA to beneficiary using PDA-signed transfer (idiomatic Solana way)
    invoke_signed(
        &solana_program::system_instruction::transfer(
            fee_account_info.key,
            beneficiary_info.key,
            withdraw_amount,
        ),
        &[
            fee_account_info.clone(),
            beneficiary_info.clone(),
            system_program.clone(),
        ],
        &[&[FEE_ACCOUNT, &[fee_bump]]],
    )?;

    // Update config total fees collected (subtract withdrawn amount)
    config.total_fees_collected = config.total_fees_collected
        .checked_sub(withdraw_amount)
        .ok_or(ProgramError::ArithmeticOverflow)?;

    // Emit event
    airdrop_api::event::FeesWithdrawnEvent {
        admin: *admin_info.key,
        beneficiary: *beneficiary_info.key,
        amount: withdraw_amount,
        remaining_balance: current_balance - withdraw_amount,
    }
    .log();

    Ok(())
}