coal_utils/
lib.rs

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/// Creates a new pda.
10#[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        // If balance is zero, create account
22        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        // Otherwise, if balance is nonzero:
39
40        // 1) transfer sufficient lamports for rent exemption
41        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        // 2) allocate space for the account
60        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        // 3) assign our program as the owner
67        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}