1#[cfg(feature = "spl")]
2pub mod spl;
3
4use solana_program::{
5 account_info::AccountInfo, entrypoint::ProgramResult, program_error::ProgramError,
6 pubkey::Pubkey, rent::Rent, sysvar::Sysvar,
7};
8
9#[inline(always)]
11pub fn create_pda<'a, 'info>(
12 target_account: &'a AccountInfo<'info>,
13 owner: &Pubkey,
14 space: usize,
15 pda_seeds: &[&[u8]],
16 system_program: &'a AccountInfo<'info>,
17 payer: &'a AccountInfo<'info>,
18) -> ProgramResult {
19 let rent = Rent::get()?;
20 if target_account.lamports().eq(&0) {
21 solana_program::program::invoke_signed(
23 &solana_program::system_instruction::create_account(
24 payer.key,
25 target_account.key,
26 rent.minimum_balance(space),
27 space as u64,
28 owner,
29 ),
30 &[
31 payer.clone(),
32 target_account.clone(),
33 system_program.clone(),
34 ],
35 &[pda_seeds],
36 )?;
37 } else {
38 let rent_exempt_balance = rent
42 .minimum_balance(space)
43 .saturating_sub(target_account.lamports());
44 if rent_exempt_balance.gt(&0) {
45 solana_program::program::invoke(
46 &solana_program::system_instruction::transfer(
47 payer.key,
48 target_account.key,
49 rent_exempt_balance,
50 ),
51 &[
52 payer.clone(),
53 target_account.clone(),
54 system_program.clone(),
55 ],
56 )?;
57 }
58
59 solana_program::program::invoke_signed(
61 &solana_program::system_instruction::allocate(target_account.key, space as u64),
62 &[target_account.clone(), system_program.clone()],
63 &[pda_seeds],
64 )?;
65
66 solana_program::program::invoke_signed(
68 &solana_program::system_instruction::assign(target_account.key, owner),
69 &[target_account.clone(), system_program.clone()],
70 &[pda_seeds],
71 )?;
72 }
73
74 Ok(())
75}
76
77pub trait Discriminator {
78 fn discriminator() -> u8;
79}
80
81pub trait AccountDeserialize {
82 fn try_from_bytes(data: &[u8]) -> Result<&Self, ProgramError>;
83 fn try_from_bytes_mut(data: &mut [u8]) -> Result<&mut Self, ProgramError>;
84}
85
86#[macro_export]
87macro_rules! impl_to_bytes {
88 ($struct_name:ident) => {
89 impl $struct_name {
90 pub fn to_bytes(&self) -> &[u8] {
91 bytemuck::bytes_of(self)
92 }
93 }
94 };
95}
96
97#[macro_export]
98macro_rules! impl_account_from_bytes {
99 ($struct_name:ident) => {
100 impl crate::utils::AccountDeserialize for $struct_name {
101 fn try_from_bytes(
102 data: &[u8],
103 ) -> Result<&Self, solana_program::program_error::ProgramError> {
104 if Self::discriminator().ne(&data[0]) {
105 return Err(solana_program::program_error::ProgramError::InvalidAccountData);
106 }
107 bytemuck::try_from_bytes::<Self>(&data[8..]).or(Err(
108 solana_program::program_error::ProgramError::InvalidAccountData,
109 ))
110 }
111 fn try_from_bytes_mut(
112 data: &mut [u8],
113 ) -> Result<&mut Self, solana_program::program_error::ProgramError> {
114 if Self::discriminator().ne(&data[0]) {
115 return Err(solana_program::program_error::ProgramError::InvalidAccountData);
116 }
117 bytemuck::try_from_bytes_mut::<Self>(&mut data[8..]).or(Err(
118 solana_program::program_error::ProgramError::InvalidAccountData,
119 ))
120 }
121 }
122 };
123}
124
125#[macro_export]
126macro_rules! impl_instruction_from_bytes {
127 ($struct_name:ident) => {
128 impl $struct_name {
129 pub fn try_from_bytes(
130 data: &[u8],
131 ) -> Result<&Self, solana_program::program_error::ProgramError> {
132 bytemuck::try_from_bytes::<Self>(data).or(Err(
133 solana_program::program_error::ProgramError::InvalidInstructionData,
134 ))
135 }
136 }
137 };
138}