solmail_program/instructions/credits/
withdraw.rs1use pinocchio::{
2 account_info::AccountInfo,
3 program_error::ProgramError,
4 pubkey::{find_program_address, Pubkey},
5 ProgramResult,
6};
7
8use crate::{
9 constants::{CREDITS_SEED, CREDITS_VAULT_SOL_SEED},
10 error::SolMailError,
11 state::credits::UserCredits,
12};
13
14pub fn process(program_id: &Pubkey, accounts: &[AccountInfo], data: &[u8]) -> ProgramResult {
26 if data.len() < 9 {
28 return Err(ProgramError::InvalidInstructionData);
29 }
30
31 let amount = u64::from_le_bytes(data[0..8].try_into().unwrap());
32 let is_usdc = data[8] != 0;
33
34 let [user, user_credits, credits_vault, _system_program] = accounts else {
36 return Err(ProgramError::NotEnoughAccountKeys);
37 };
38
39 if !user.is_signer() {
41 return Err(SolMailError::MissingRequiredSignature.into());
42 }
43
44 let (credits_pda, _) =
46 find_program_address(&[CREDITS_SEED, user.key().as_ref()], program_id);
47
48 if user_credits.key() != &credits_pda {
49 return Err(SolMailError::InvalidPda.into());
50 }
51
52 let (vault_pda, _vault_bump) =
54 find_program_address(&[CREDITS_VAULT_SOL_SEED], program_id);
55
56 if credits_vault.key() != &vault_pda {
57 return Err(SolMailError::InvalidPda.into());
58 }
59
60 let credits_data = unsafe { user_credits.borrow_mut_data_unchecked() };
62 let credits = UserCredits::from_bytes_mut(credits_data)?;
63
64 if credits.owner != user.key().as_ref() {
66 return Err(SolMailError::InvalidPayer.into());
67 }
68
69 if is_usdc {
71 credits.sub_usdc(amount)?;
72 } else {
73 credits.sub_sol(amount)?;
74 }
75
76 if !is_usdc {
78 let vault_balance = credits_vault.lamports();
79 if vault_balance < amount {
80 return Err(SolMailError::InsufficientFunds.into());
81 }
82 unsafe {
83 *credits_vault.borrow_mut_lamports_unchecked() -= amount;
84 *user.borrow_mut_lamports_unchecked() += amount;
85 }
86 }
87 Ok(())
90}