spherenet-program-whitelist-interface 0.3.0

Interface of SphereNet's Program Whitelist Program
Documentation
pub mod account;
pub mod whitelist_entry;

use {bytemuck::Pod, core::mem, pinocchio::program_error::ProgramError, shank::ShankType};

/// Trait for types that have a fixed, known size
pub trait SizeOf {
    /// Size of the type in bytes
    const SIZE_OF: usize;
}

/// Implement SizeOf for all Pod types
impl<T: Pod> SizeOf for T {
    const SIZE_OF: usize = mem::size_of::<T>();
}

/// Trait to represent a type that can be initialized.
pub trait Initializable {
    /// Return `true` if the object is initialized — i.e. in any non-
    /// `Uninitialized` state.
    fn is_initialized(&self) -> bool;
}

/// Init state of the singleton whitelist account (the config PDA). Unlike an
/// entry, the config has no approval lifecycle — it is simply live or not.
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, ShankType)]
pub enum AccountState {
    /// Account is not yet initialized
    Uninitialized,
    /// Account is initialized (live)
    Initialized,
}

/// Lifecycle state of a whitelist entry: a request is created `Pending` and
/// promoted to `Approved` by the authority (the deploy gate checks `Approved`).
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, ShankType)]
pub enum EntryState {
    /// Entry does not exist yet
    Uninitialized,
    /// A whitelist request awaiting authority approval (not yet active)
    Pending,
    /// The entry has been approved — active on the whitelist
    Approved,
}

/// Load a reference for an initialized `T` from the given bytes
#[inline(always)]
pub fn load<T: Pod + Initializable>(input: &[u8]) -> Result<&T, ProgramError> {
    if input.len() != T::SIZE_OF {
        return Err(ProgramError::InvalidAccountData);
    }

    let account = bytemuck::from_bytes::<T>(input);

    if !account.is_initialized() {
        return Err(ProgramError::UninitializedAccount);
    }

    Ok(account)
}

/// Load a mutable reference for an initialized `T` from the given bytes
#[inline(always)]
pub fn load_mut<T: Pod + Initializable>(
    input: &mut [u8],
) -> Result<&mut T, ProgramError> {
    if input.len() != T::SIZE_OF {
        return Err(ProgramError::InvalidAccountData);
    }

    let account = bytemuck::from_bytes_mut::<T>(input);

    if !account.is_initialized() {
        return Err(ProgramError::UninitializedAccount);
    }

    Ok(account)
}

/// Load a reference without checking if the data is initialized
#[inline(always)]
pub fn load_unchecked<T: Pod>(input: &[u8]) -> Result<&T, ProgramError> {
    if input.len() != T::SIZE_OF {
        return Err(ProgramError::InvalidAccountData);
    }

    Ok(bytemuck::from_bytes::<T>(input))
}

/// Load a mutable reference without checking if the data is initialized
#[inline(always)]
pub fn load_mut_unchecked<T: Pod>(
    input: &mut [u8],
) -> Result<&mut T, ProgramError> {
    if input.len() != T::SIZE_OF {
        return Err(ProgramError::InvalidAccountData);
    }

    Ok(bytemuck::from_bytes_mut::<T>(input))
}