mars/
utils.rs

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