solmail_program/instructions/credits/
spend.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, ORACLE_PUBKEY, TREASURY_SEED},
10 error::SolMailError,
11 state::credits::UserCredits,
12};
13
14pub fn process(program_id: &Pubkey, accounts: &[AccountInfo], data: &[u8]) -> ProgramResult {
28 if data.len() < 41 {
30 return Err(ProgramError::InvalidInstructionData);
31 }
32
33 let user_pubkey: [u8; 32] = data[0..32].try_into().unwrap();
34 let amount = u64::from_le_bytes(data[32..40].try_into().unwrap());
35 let is_usdc = data[40] != 0;
36
37 let [oracle, user_credits, credits_vault, treasury, _system_program] = accounts else {
39 return Err(ProgramError::NotEnoughAccountKeys);
40 };
41
42 if !oracle.is_signer() {
44 return Err(SolMailError::MissingRequiredSignature.into());
45 }
46
47 if oracle.key().as_ref() != &ORACLE_PUBKEY {
49 return Err(SolMailError::UnauthorizedOracle.into());
50 }
51
52 let (credits_pda, _) =
54 find_program_address(&[CREDITS_SEED, &user_pubkey], program_id);
55
56 if user_credits.key() != &credits_pda {
57 return Err(SolMailError::InvalidPda.into());
58 }
59
60 let (vault_pda, _vault_bump) =
62 find_program_address(&[CREDITS_VAULT_SOL_SEED], program_id);
63
64 if credits_vault.key() != &vault_pda {
65 return Err(SolMailError::InvalidPda.into());
66 }
67
68 let (treasury_pda, _) = find_program_address(&[TREASURY_SEED], program_id);
70
71 if treasury.key() != &treasury_pda {
72 return Err(SolMailError::InvalidPda.into());
73 }
74
75 let credits_data = unsafe { user_credits.borrow_mut_data_unchecked() };
77 let credits = UserCredits::from_bytes_mut(credits_data)?;
78
79 if credits.owner != user_pubkey {
81 return Err(SolMailError::InvalidPayer.into());
82 }
83
84 if is_usdc {
86 credits.sub_usdc(amount)?;
87 } else {
88 credits.sub_sol(amount)?;
89 }
90
91 if !is_usdc {
93 let vault_balance = credits_vault.lamports();
94 if vault_balance < amount {
95 return Err(SolMailError::InsufficientFunds.into());
96 }
97 unsafe {
98 *credits_vault.borrow_mut_lamports_unchecked() -= amount;
99 *treasury.borrow_mut_lamports_unchecked() += amount;
100 }
101 }
102 Ok(())
105}