use bytemuck::{Pod, Zeroable}; use num_enum::{IntoPrimitive, TryFromPrimitive}; use solana_program::{ account_info::AccountInfo, entrypoint::ProgramResult, program_error::ProgramError, pubkey::Pubkey, rent::Rent, sysvar::Sysvar, }; /// Creates a new pda #[inline(always)] pub(crate) fn create_pda<'a, 'info>( target_account: &'a AccountInfo<'info>, owner: &Pubkey, space: usize, pda_seeds: &[&[u8]], system_program: &'a AccountInfo<'info>, payer: &'a AccountInfo<'info>, ) -> ProgramResult { let rent = Rent::get()?; if target_account.lamports().eq(&0) { // If balance is zero, create account solana_program::program::invoke_signed( &solana_program::system_instruction::create_account( payer.key, target_account.key, rent.minimum_balance(space), space as u64, owner, ), &[ payer.clone(), target_account.clone(), system_program.clone(), ], &[pda_seeds], )?; } else { // Otherwise, if balance is nonzero: // 1) transfer sufficient lamports for rent exemption let rent_exempt_balance = rent .minimum_balance(space) .saturating_sub(target_account.lamports()); if rent_exempt_balance.gt(&0) { solana_program::program::invoke( &solana_program::system_instruction::transfer( payer.key, target_account.key, rent_exempt_balance, ), &[ payer.as_ref().clone(), target_account.as_ref().clone(), system_program.as_ref().clone(), ], )?; } // 2) allocate space for the account solana_program::program::invoke_signed( &solana_program::system_instruction::allocate(target_account.key, space as u64), &[ target_account.as_ref().clone(), system_program.as_ref().clone(), ], &[pda_seeds], )?; // 3) assign our program as the owner solana_program::program::invoke_signed( &solana_program::system_instruction::assign(target_account.key, owner), &[ target_account.as_ref().clone(), system_program.as_ref().clone(), ], &[pda_seeds], )?; } Ok(()) } #[repr(C)] #[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)] pub struct MineEvent { pub difficulty: u64, pub reward: u64, pub timing: i64, } #[repr(u8)] #[derive(Clone, Copy, Debug, Eq, PartialEq, IntoPrimitive, TryFromPrimitive)] pub enum AccountDiscriminator { Bus = 100, Config = 101, Proof = 102, Treasury = 103, } pub trait Discriminator { fn discriminator() -> AccountDiscriminator; } pub trait AccountDeserialize { fn try_from_bytes(data: &[u8]) -> Result<&Self, ProgramError>; fn try_from_bytes_mut(data: &mut [u8]) -> Result<&mut Self, ProgramError>; } #[macro_export] macro_rules! impl_to_bytes { ($struct_name:ident) => { impl $struct_name { pub fn to_bytes(&self) -> &[u8] { bytemuck::bytes_of(self) } } }; } #[macro_export] macro_rules! impl_account_from_bytes { ($struct_name:ident) => { impl crate::utils::AccountDeserialize for $struct_name { fn try_from_bytes( data: &[u8], ) -> Result<&Self, solana_program::program_error::ProgramError> { if (Self::discriminator() as u8).ne(&data[0]) { return Err(solana_program::program_error::ProgramError::InvalidAccountData); } bytemuck::try_from_bytes::(&data[8..]).or(Err( solana_program::program_error::ProgramError::InvalidAccountData, )) } fn try_from_bytes_mut( data: &mut [u8], ) -> Result<&mut Self, solana_program::program_error::ProgramError> { if (Self::discriminator() as u8).ne(&data[0]) { return Err(solana_program::program_error::ProgramError::InvalidAccountData); } bytemuck::try_from_bytes_mut::(&mut data[8..]).or(Err( solana_program::program_error::ProgramError::InvalidAccountData, )) } } }; } #[macro_export] macro_rules! impl_instruction_from_bytes { ($struct_name:ident) => { impl $struct_name { pub fn try_from_bytes( data: &[u8], ) -> Result<&Self, solana_program::program_error::ProgramError> { bytemuck::try_from_bytes::(data).or(Err( solana_program::program_error::ProgramError::InvalidInstructionData, )) } } }; }