Skip to main content

spherenet_program_whitelist_interface/state/
mod.rs

1pub mod account;
2pub mod whitelist_entry;
3
4use {bytemuck::Pod, core::mem, pinocchio::program_error::ProgramError, shank::ShankType};
5
6/// Trait for types that have a fixed, known size
7pub trait SizeOf {
8    /// Size of the type in bytes
9    const SIZE_OF: usize;
10}
11
12/// Implement SizeOf for all Pod types
13impl<T: Pod> SizeOf for T {
14    const SIZE_OF: usize = mem::size_of::<T>();
15}
16
17/// Trait to represent a type that can be initialized.
18pub trait Initializable {
19    /// Return `true` if the object is initialized — i.e. in any non-
20    /// `Uninitialized` state.
21    fn is_initialized(&self) -> bool;
22}
23
24/// Init state of the singleton whitelist account (the config PDA). Unlike an
25/// entry, the config has no approval lifecycle — it is simply live or not.
26#[repr(u8)]
27#[derive(Clone, Copy, Debug, PartialEq, ShankType)]
28pub enum AccountState {
29    /// Account is not yet initialized
30    Uninitialized,
31    /// Account is initialized (live)
32    Initialized,
33}
34
35/// Lifecycle state of a whitelist entry: a request is created `Pending` and
36/// promoted to `Approved` by the authority (the deploy gate checks `Approved`).
37#[repr(u8)]
38#[derive(Clone, Copy, Debug, PartialEq, ShankType)]
39pub enum EntryState {
40    /// Entry does not exist yet
41    Uninitialized,
42    /// A whitelist request awaiting authority approval (not yet active)
43    Pending,
44    /// The entry has been approved — active on the whitelist
45    Approved,
46}
47
48/// Load a reference for an initialized `T` from the given bytes
49#[inline(always)]
50pub fn load<T: Pod + Initializable>(input: &[u8]) -> Result<&T, ProgramError> {
51    if input.len() != T::SIZE_OF {
52        return Err(ProgramError::InvalidAccountData);
53    }
54
55    let account = bytemuck::from_bytes::<T>(input);
56
57    if !account.is_initialized() {
58        return Err(ProgramError::UninitializedAccount);
59    }
60
61    Ok(account)
62}
63
64/// Load a mutable reference for an initialized `T` from the given bytes
65#[inline(always)]
66pub fn load_mut<T: Pod + Initializable>(
67    input: &mut [u8],
68) -> Result<&mut T, ProgramError> {
69    if input.len() != T::SIZE_OF {
70        return Err(ProgramError::InvalidAccountData);
71    }
72
73    let account = bytemuck::from_bytes_mut::<T>(input);
74
75    if !account.is_initialized() {
76        return Err(ProgramError::UninitializedAccount);
77    }
78
79    Ok(account)
80}
81
82/// Load a reference without checking if the data is initialized
83#[inline(always)]
84pub fn load_unchecked<T: Pod>(input: &[u8]) -> Result<&T, ProgramError> {
85    if input.len() != T::SIZE_OF {
86        return Err(ProgramError::InvalidAccountData);
87    }
88
89    Ok(bytemuck::from_bytes::<T>(input))
90}
91
92/// Load a mutable reference without checking if the data is initialized
93#[inline(always)]
94pub fn load_mut_unchecked<T: Pod>(
95    input: &mut [u8],
96) -> Result<&mut T, ProgramError> {
97    if input.len() != T::SIZE_OF {
98        return Err(ProgramError::InvalidAccountData);
99    }
100
101    Ok(bytemuck::from_bytes_mut::<T>(input))
102}