pinocchio_util/lib.rs
1use pinocchio::{account_info::AccountInfo, program_error::ProgramError};
2
3/// Get the length of an account's data.
4pub trait DataLen {
5 const LEN: usize;
6}
7
8/// Generate an enum and associated function for updating fields
9/// on an account struct.
10pub trait AccountUpdates {
11 type Update;
12 fn updates(&mut self, updates: Self::Update) -> Result<(), ProgramError>;
13}
14
15/// Validate surface level account attributes like keys, data length, and more.
16pub trait Validate<'info> {
17 fn validate(&self) -> Result<(), ProgramError>;
18}
19
20/// Build an instruction context with both accounts and instruction data
21pub trait Context<'info>: Sized {
22 const ACCOUNTS_LEN: usize;
23 fn build(accounts: &'info [AccountInfo]) -> Result<Self, ProgramError>;
24}
25
26/// Load an immutable reference to an account's data as an arbitrary type. This requires
27/// that the provided type implements the `DataLen` trait so there's assurance that
28/// no out of bounds access will occur.
29///
30/// # Example
31///
32/// ```rust
33/// let account = AccountInfo::new(
34/// &account,
35/// false,
36/// false,
37/// false,
38/// &mut accounts,
39/// &mut ctx,
40/// );
41///
42/// let account_data = load::<UserData>(&account)?;
43/// ```
44#[inline]
45pub fn load<T: DataLen>(account: &AccountInfo) -> Result<&T, ProgramError> {
46 if account.data_len() != T::LEN {
47 return Err(ProgramError::InvalidAccountData);
48 }
49 Ok(unsafe {
50 &*core::mem::transmute::<*const u8, *const T>(account.borrow_data_unchecked().as_ptr())
51 })
52}
53
54/// Load a mutable reference to an account's data as an arbitrary type. This requires
55/// that the provided type implements the `DataLen` trait so there's assurance that
56/// no out of bounds access will occur.
57///
58/// # Example
59///
60/// ```rust
61/// let mut account = AccountInfo::new(
62/// &account,
63/// false,
64/// false,
65/// false,
66/// &mut accounts,
67/// &mut ctx,
68/// );
69///
70/// let mut account_data = load_mut::<UserData>(&account)?;
71/// ```
72#[inline]
73pub fn load_mut<T: DataLen>(account: &AccountInfo) -> Result<&mut T, ProgramError> {
74 if account.data_len() != T::LEN {
75 return Err(ProgramError::InvalidAccountData);
76 }
77 Ok(unsafe {
78 &mut *core::mem::transmute::<*mut u8, *mut T>(
79 account.borrow_mut_data_unchecked().as_mut_ptr(),
80 )
81 })
82}
83
84/// Extract an account's discriminator. This is useful if working with Anchor programs,
85/// and you need to validate that a provided account is of a specific type.
86///
87/// You can optionally provide a custom length for the discriminator, and if not provided
88/// the length will be defaulted to 8 bytes.
89///
90/// # Example
91///
92/// ```rust
93/// let discriminator = load_discriminator(&account, None).unwrap();
94/// assert_eq!(discriminator, &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
95///
96/// let discriminator = load_discriminator(&account, Some(4)).unwrap();
97/// assert_eq!(discriminator, &[0x00, 0x00, 0x00, 0x00]);
98/// ```
99///
100#[inline]
101pub fn load_discriminator(
102 account: &AccountInfo,
103 len: Option<usize>,
104) -> Result<&[u8; 8], ProgramError> {
105 let discriminator_len = len.unwrap_or(8);
106 unsafe {
107 account.borrow_data_unchecked()[0..discriminator_len]
108 .try_into()
109 .map_err(|_| ProgramError::InvalidAccountData)
110 }
111}