Просмотр исходного кода

processor: Split processor (#2)

* Use instruction args

* Refactor processor

* Update compute table

* Remove bytemuck

* Move state to interface crate

* Update lib comment

* Update CU values
Fernando Otero 10 месяцев назад
Родитель
Сommit
a556e62361
45 измененных файлов с 1084 добавлено и 1118 удалено
  1. 0 1
      interface/Cargo.toml
  2. 13 13
      interface/src/instruction.rs
  3. 154 53
      interface/src/state/account.rs
  4. 15 0
      interface/src/state/account_state.rs
  5. 137 12
      interface/src/state/mint.rs
  6. 3 158
      interface/src/state/mod.rs
  7. 83 7
      interface/src/state/multisig.rs
  8. 0 1
      p-token/Cargo.toml
  9. 111 158
      p-token/src/entrypoint.rs
  10. 1 1
      p-token/src/lib.rs
  11. 12 10
      p-token/src/processor/amount_to_ui_amount.rs
  12. 10 8
      p-token/src/processor/approve.rs
  13. 15 43
      p-token/src/processor/approve_checked.rs
  14. 10 4
      p-token/src/processor/burn.rs
  15. 19 43
      p-token/src/processor/burn_checked.rs
  16. 8 12
      p-token/src/processor/close_account.rs
  17. 4 4
      p-token/src/processor/freeze_account.rs
  18. 6 10
      p-token/src/processor/get_account_data_size.rs
  19. 4 4
      p-token/src/processor/initialize_account.rs
  20. 4 4
      p-token/src/processor/initialize_account2.rs
  21. 4 4
      p-token/src/processor/initialize_account3.rs
  22. 3 5
      p-token/src/processor/initialize_immutable_owner.rs
  23. 108 5
      p-token/src/processor/initialize_mint.rs
  24. 7 4
      p-token/src/processor/initialize_mint2.rs
  25. 9 58
      p-token/src/processor/initialize_multisig.rs
  26. 10 4
      p-token/src/processor/initialize_multisig2.rs
  27. 10 8
      p-token/src/processor/mint_to.rs
  28. 16 43
      p-token/src/processor/mint_to_checked.rs
  29. 36 12
      p-token/src/processor/mod.rs
  30. 8 12
      p-token/src/processor/revoke.rs
  31. 77 63
      p-token/src/processor/set_authority.rs
  32. 15 14
      p-token/src/processor/shared/approve.rs
  33. 26 30
      p-token/src/processor/shared/burn.rs
  34. 19 29
      p-token/src/processor/shared/initialize_account.rs
  35. 0 106
      p-token/src/processor/shared/initialize_mint.rs
  36. 9 6
      p-token/src/processor/shared/initialize_multisig.rs
  37. 16 21
      p-token/src/processor/shared/mint_to.rs
  38. 5 1
      p-token/src/processor/shared/mod.rs
  39. 11 23
      p-token/src/processor/shared/toggle_account_state.rs
  40. 34 48
      p-token/src/processor/shared/transfer.rs
  41. 10 14
      p-token/src/processor/sync_native.rs
  42. 4 4
      p-token/src/processor/thaw_account.rs
  43. 10 8
      p-token/src/processor/transfer.rs
  44. 19 40
      p-token/src/processor/transfer_checked.rs
  45. 9 10
      p-token/src/processor/ui_amount_to_amount.rs

+ 0 - 1
interface/Cargo.toml

@@ -8,6 +8,5 @@ repository = { workspace = true }
 publish = false
 
 [dependencies]
-bytemuck = { workspace = true }
 pinocchio = { workspace = true }
 pinocchio-pubkey = { workspace = true }

+ 13 - 13
interface/src/instruction.rs

@@ -1,8 +1,8 @@
-//! Instruction types
+//! Instruction types.
 
-use pinocchio::pubkey::Pubkey;
+use pinocchio::{program_error::ProgramError, pubkey::Pubkey};
 
-use crate::state::PodCOption;
+use crate::error::TokenError;
 
 /// Instructions supported by the token program.
 #[repr(C)]
@@ -27,7 +27,7 @@ pub enum TokenInstruction<'a> {
         /// The authority/multisignature to mint tokens.
         mint_authority: Pubkey,
         /// The freeze authority/multisignature of the mint.
-        freeze_authority: PodCOption<Pubkey>,
+        freeze_authority: Option<Pubkey>,
     },
 
     /// Initializes a new account to hold tokens.  If this account is associated
@@ -147,7 +147,7 @@ pub enum TokenInstruction<'a> {
         /// The type of authority to update.
         authority_type: AuthorityType,
         /// The new authority
-        new_authority: PodCOption<Pubkey>,
+        new_authority: Option<Pubkey>,
     },
 
     /// Mints new tokens to an account.  The native mint does not support
@@ -416,7 +416,7 @@ pub enum TokenInstruction<'a> {
         /// The authority/multisignature to mint tokens.
         mint_authority: Pubkey,
         /// The freeze authority/multisignature of the mint.
-        freeze_authority: PodCOption<Pubkey>,
+        freeze_authority: Option<Pubkey>,
     },
 
     /// Gets the required size of an account for the given mint as a
@@ -482,7 +482,7 @@ pub enum TokenInstruction<'a> {
     // token/js/src/instructions/types.ts to maintain @solana/spl-token compatibility
 }
 
-/// Specifies the authority type for SetAuthority instructions
+/// Specifies the authority type for `SetAuthority` instructions
 #[repr(u8)]
 #[derive(Clone, Debug, PartialEq)]
 pub enum AuthorityType {
@@ -506,13 +506,13 @@ impl AuthorityType {
         }
     }
 
-    pub fn from(index: u8) -> Self {
+    pub fn from(index: u8) -> Result<Self, ProgramError> {
         match index {
-            0 => AuthorityType::MintTokens,
-            1 => AuthorityType::FreezeAccount,
-            2 => AuthorityType::AccountOwner,
-            3 => AuthorityType::CloseAccount,
-            _ => panic!("invalid authority type: {index}"),
+            0 => Ok(AuthorityType::MintTokens),
+            1 => Ok(AuthorityType::FreezeAccount),
+            2 => Ok(AuthorityType::AccountOwner),
+            3 => Ok(AuthorityType::CloseAccount),
+            _ => Err(TokenError::InvalidInstruction.into()),
         }
     }
 }

+ 154 - 53
interface/src/state/account.rs

@@ -1,11 +1,15 @@
-use bytemuck::{Pod, Zeroable};
-use pinocchio::pubkey::Pubkey;
+use pinocchio::{
+    account_info::{AccountInfo, Ref},
+    program_error::ProgramError,
+    pubkey::Pubkey,
+};
 
-use super::{PodCOption, PodU64};
+use crate::program::ID;
 
-/// Account data.
+use super::{account_state::AccountState, COption};
+
+/// Internal representation of a token account data.
 #[repr(C)]
-#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)]
 pub struct Account {
     /// The mint associated with this account
     pub mint: Pubkey,
@@ -14,82 +18,179 @@ pub struct Account {
     pub owner: Pubkey,
 
     /// The amount of tokens this account holds.
-    pub amount: PodU64,
+    amount: [u8; 8],
 
     /// If `delegate` is `Some` then `delegated_amount` represents
-    /// the amount authorized by the delegate
-    pub delegate: PodCOption<Pubkey>,
+    /// the amount authorized by the delegate.
+    delegate: COption<Pubkey>,
+
+    /// The account's state.
+    pub state: AccountState,
 
-    /// The account's state
-    pub state: u8,
+    /// Indicates whether this account represents a native token or not.
+    is_native: [u8; 4],
 
     /// If is_native.is_some, this is a native token, and the value logs the
     /// rent-exempt reserve. An Account is required to be rent-exempt, so
     /// the value is used by the Processor to ensure that wrapped SOL
     /// accounts do not drop below this threshold.
-    pub is_native: PodCOption<PodU64>,
+    native_amount: [u8; 8],
 
-    /// The amount delegated
-    pub delegated_amount: PodU64,
+    /// The amount delegated.
+    delegated_amount: [u8; 8],
 
     /// Optional authority to close the account.
-    pub close_authority: PodCOption<Pubkey>,
+    close_authority: COption<Pubkey>,
 }
 
 impl Account {
-    /// Size of the `Account` account.
-    pub const LEN: usize = core::mem::size_of::<Self>();
+    pub const LEN: usize = core::mem::size_of::<Account>();
 
+    /// Return a `TokenAccount` from the given account info.
+    ///
+    /// This method performs owner and length validation on `AccountInfo`, safe borrowing
+    /// the account data.
     #[inline]
-    pub fn is_initialized(&self) -> bool {
-        self.state != AccountState::Uninitialized as u8
+    pub fn from_account_info(account_info: &AccountInfo) -> Result<Ref<Account>, ProgramError> {
+        if account_info.data_len() != Self::LEN {
+            return Err(ProgramError::InvalidAccountData);
+        }
+        if account_info.owner() != &ID {
+            return Err(ProgramError::InvalidAccountData);
+        }
+        Ok(Ref::map(account_info.try_borrow_data()?, |data| unsafe {
+            Self::from_bytes(data)
+        }))
     }
 
+    /// Return a `TokenAccount` from the given account info.
+    ///
+    /// This method performs owner and length validation on `AccountInfo`, but does not
+    /// perform the borrow check.
+    ///
+    /// # Safety
+    ///
+    /// The caller must ensure that it is safe to borrow the account data – e.g., there are
+    /// no mutable borrows of the account data.
     #[inline]
-    pub fn is_frozen(&self) -> bool {
-        self.state == AccountState::Frozen as u8
+    pub unsafe fn from_account_info_unchecked(
+        account_info: &AccountInfo,
+    ) -> Result<&Account, ProgramError> {
+        if account_info.data_len() != Self::LEN {
+            return Err(ProgramError::InvalidAccountData);
+        }
+        if account_info.owner() != &ID {
+            return Err(ProgramError::InvalidAccountData);
+        }
+        Ok(Self::from_bytes(account_info.borrow_data_unchecked()))
+    }
+
+    /// Return a `TokenAccount` from the given bytes.
+    ///
+    /// # Safety
+    ///
+    /// The caller must ensure that `bytes` contains a valid representation of `TokenAccount`.
+    #[inline(always)]
+    pub unsafe fn from_bytes(bytes: &[u8]) -> &Self {
+        &*(bytes.as_ptr() as *const Account)
     }
 
+    /// Return a mutable `Mint` reference from the given bytes.
+    ///
+    /// # Safety
+    ///
+    /// The caller must ensure that `bytes` contains a valid representation of `Mint`.
+    #[inline(always)]
+    pub unsafe fn from_bytes_mut(bytes: &mut [u8]) -> &mut Self {
+        &mut *(bytes.as_mut_ptr() as *mut Account)
+    }
+
+    #[inline]
+    pub fn set_amount(&mut self, amount: u64) {
+        self.amount = amount.to_le_bytes();
+    }
+
+    #[inline]
     pub fn amount(&self) -> u64 {
-        self.amount.into()
+        u64::from_le_bytes(self.amount)
     }
-}
 
-/// Account state.
-#[repr(u8)]
-#[derive(Clone, Copy, Debug, Default, PartialEq)]
-pub enum AccountState {
-    /// Account is not yet initialized
-    #[default]
-    Uninitialized,
-
-    /// Account is initialized; the account owner and/or delegate may perform
-    /// permitted operations on this account
-    Initialized,
-
-    /// Account has been frozen by the mint freeze authority. Neither the
-    /// account owner nor the delegate are able to perform operations on
-    /// this account.
-    Frozen,
-}
+    #[inline]
+    pub fn clear_delegate(&mut self) {
+        self.delegate.0[0] = 0;
+    }
 
-impl From<u8> for AccountState {
-    fn from(value: u8) -> Self {
-        match value {
-            0 => AccountState::Uninitialized,
-            1 => AccountState::Initialized,
-            2 => AccountState::Frozen,
-            _ => panic!("invalid account state value: {value}"),
+    #[inline]
+    pub fn set_delegate(&mut self, delegate: &Pubkey) {
+        self.delegate.0[0] = 1;
+        self.delegate.1 = *delegate;
+    }
+
+    #[inline]
+    pub fn delegate(&self) -> Option<&Pubkey> {
+        if self.delegate.0[0] == 1 {
+            Some(&self.delegate.1)
+        } else {
+            None
         }
     }
-}
 
-impl From<AccountState> for u8 {
-    fn from(value: AccountState) -> Self {
-        match value {
-            AccountState::Uninitialized => 0,
-            AccountState::Initialized => 1,
-            AccountState::Frozen => 2,
+    #[inline]
+    pub fn set_native(&mut self, value: bool) {
+        self.is_native[0] = value as u8;
+    }
+
+    #[inline]
+    pub fn is_native(&self) -> bool {
+        self.is_native[0] == 1
+    }
+
+    #[inline]
+    pub fn native_amount(&self) -> Option<u64> {
+        if self.is_native() {
+            Some(u64::from_le_bytes(self.native_amount))
+        } else {
+            None
         }
     }
+
+    #[inline]
+    pub fn set_delegated_amount(&mut self, amount: u64) {
+        self.delegated_amount = amount.to_le_bytes();
+    }
+
+    #[inline]
+    pub fn delegated_amount(&self) -> u64 {
+        u64::from_le_bytes(self.delegated_amount)
+    }
+
+    #[inline]
+    pub fn clear_close_authority(&mut self) {
+        self.close_authority.0[0] = 0;
+    }
+
+    #[inline]
+    pub fn set_close_authority(&mut self, value: &Pubkey) {
+        self.close_authority.0[0] = 1;
+        self.close_authority.1 = *value;
+    }
+
+    #[inline]
+    pub fn close_authority(&self) -> Option<&Pubkey> {
+        if self.close_authority.0[0] == 1 {
+            Some(&self.close_authority.1)
+        } else {
+            None
+        }
+    }
+
+    #[inline(always)]
+    pub fn is_initialized(&self) -> bool {
+        self.state != AccountState::Uninitialized
+    }
+
+    #[inline(always)]
+    pub fn is_frozen(&self) -> bool {
+        self.state == AccountState::Frozen
+    }
 }

+ 15 - 0
interface/src/state/account_state.rs

@@ -0,0 +1,15 @@
+#[repr(u8)]
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub enum AccountState {
+    /// Account is not yet initialized
+    Uninitialized,
+
+    /// Account is initialized; the account owner and/or delegate may perform
+    /// permitted operations on this account
+    Initialized,
+
+    /// Account has been frozen by the mint freeze authority. Neither the
+    /// account owner nor the delegate are able to perform operations on
+    /// this account.
+    Frozen,
+}

+ 137 - 12
interface/src/state/mint.rs

@@ -1,32 +1,157 @@
-use bytemuck::{Pod, Zeroable};
-use pinocchio::pubkey::Pubkey;
+use pinocchio::{
+    account_info::{AccountInfo, Ref},
+    program_error::ProgramError,
+    pubkey::Pubkey,
+};
 
-use super::{PodBool, PodCOption, PodU64};
+use crate::program::ID;
 
-/// Mint data.
+use super::COption;
+
+/// Internal representation of a mint data.
 #[repr(C)]
-#[derive(Clone, Copy, Default, Pod, Zeroable)]
 pub struct Mint {
     /// Optional authority used to mint new tokens. The mint authority may only
     /// be provided during mint creation. If no mint authority is present
     /// then the mint has a fixed supply and no further tokens may be
     /// minted.
-    pub mint_authority: PodCOption<Pubkey>,
+    pub mint_authority: COption<Pubkey>,
 
     /// Total supply of tokens.
-    pub supply: PodU64,
+    supply: [u8; 8],
 
     /// Number of base 10 digits to the right of the decimal place.
     pub decimals: u8,
 
-    /// Is `true` if this structure has been initialized
-    pub is_initialized: PodBool,
+    /// Is `true` if this structure has been initialized.
+    is_initialized: u8,
 
+    // Indicates whether the freeze authority is present or not.
+    //freeze_authority_option: [u8; 4],
     /// Optional authority to freeze token accounts.
-    pub freeze_authority: PodCOption<Pubkey>,
+    pub freeze_authority: COption<Pubkey>,
 }
 
 impl Mint {
-    /// Size of the `Mint` account.
-    pub const LEN: usize = core::mem::size_of::<Self>();
+    /// The length of the `Mint` account data.
+    pub const LEN: usize = core::mem::size_of::<Mint>();
+
+    /// Return a `Mint` from the given account info.
+    ///
+    /// This method performs owner and length validation on `AccountInfo`, safe borrowing
+    /// the account data.
+    #[inline]
+    pub fn from_account_info(account_info: &AccountInfo) -> Result<Ref<Mint>, ProgramError> {
+        if account_info.data_len() != Self::LEN {
+            return Err(ProgramError::InvalidAccountData);
+        }
+        if account_info.owner() != &ID {
+            return Err(ProgramError::InvalidAccountOwner);
+        }
+        Ok(Ref::map(account_info.try_borrow_data()?, |data| unsafe {
+            Self::from_bytes(data)
+        }))
+    }
+
+    /// Return a `Mint` from the given account info.
+    ///
+    /// This method performs owner and length validation on `AccountInfo`, but does not
+    /// perform the borrow check.
+    ///
+    /// # Safety
+    ///
+    /// The caller must ensure that it is safe to borrow the account data – e.g., there are
+    /// no mutable borrows of the account data.
+    #[inline]
+    pub unsafe fn from_account_info_unchecked(
+        account_info: &AccountInfo,
+    ) -> Result<&Self, ProgramError> {
+        if account_info.data_len() != Self::LEN {
+            return Err(ProgramError::InvalidAccountData);
+        }
+        if account_info.owner() != &ID {
+            return Err(ProgramError::InvalidAccountOwner);
+        }
+        Ok(Self::from_bytes(account_info.borrow_data_unchecked()))
+    }
+
+    /// Return a `Mint` reference from the given bytes.
+    ///
+    /// # Safety
+    ///
+    /// The caller must ensure that `bytes` contains a valid representation of `Mint`.
+    #[inline]
+    pub unsafe fn from_bytes(bytes: &[u8]) -> &Self {
+        &*(bytes.as_ptr() as *const Mint)
+    }
+
+    /// Return a mutable `Mint` reference from the given bytes.
+    ///
+    /// # Safety
+    ///
+    /// The caller must ensure that `bytes` contains a valid representation of `Mint`.
+    #[inline]
+    pub unsafe fn from_bytes_mut(bytes: &mut [u8]) -> &mut Self {
+        &mut *(bytes.as_mut_ptr() as *mut Mint)
+    }
+
+    #[inline]
+    pub fn set_supply(&mut self, supply: u64) {
+        self.supply = supply.to_le_bytes();
+    }
+
+    #[inline]
+    pub fn supply(&self) -> u64 {
+        u64::from_le_bytes(self.supply)
+    }
+
+    #[inline]
+    pub fn set_initialized(&mut self, value: bool) {
+        self.is_initialized = value as u8;
+    }
+
+    #[inline]
+    pub fn is_initialized(&self) -> bool {
+        self.is_initialized == 1
+    }
+
+    #[inline]
+    pub fn clear_mint_authority(&mut self) {
+        self.mint_authority.0[0] = 0;
+    }
+
+    #[inline]
+    pub fn set_mint_authority(&mut self, mint_authority: &Pubkey) {
+        self.mint_authority.0[0] = 1;
+        self.mint_authority.1 = *mint_authority;
+    }
+
+    #[inline]
+    pub fn mint_authority(&self) -> Option<&Pubkey> {
+        if self.mint_authority.0[0] == 1 {
+            Some(&self.mint_authority.1)
+        } else {
+            None
+        }
+    }
+
+    #[inline]
+    pub fn clear_freeze_authority(&mut self) {
+        self.freeze_authority.0[0] = 0;
+    }
+
+    #[inline]
+    pub fn set_freeze_authority(&mut self, freeze_authority: &Pubkey) {
+        self.freeze_authority.0[0] = 1;
+        self.freeze_authority.1 = *freeze_authority;
+    }
+
+    #[inline]
+    pub fn freeze_authority(&self) -> Option<&Pubkey> {
+        if self.freeze_authority.0[0] == 1 {
+            Some(&self.freeze_authority.1)
+        } else {
+            None
+        }
+    }
 }

+ 3 - 158
interface/src/state/mod.rs

@@ -1,162 +1,7 @@
-use std::mem::align_of;
-
-use bytemuck::{Pod, Zeroable};
-
 pub mod account;
+pub mod account_state;
 pub mod mint;
 pub mod multisig;
 
-#[repr(C)]
-#[derive(Clone, Copy, Debug, Default, PartialEq)]
-pub struct PodCOption<T: Default + PartialEq + Pod + Sized> {
-    /// Indicates if the option is `Some` or `None`.
-    tag: [u8; 4],
-
-    /// The value of the option.
-    value: T,
-}
-
-impl<T: Default + PartialEq + Pod + Sized> From<Option<T>> for PodCOption<T> {
-    fn from(value: Option<T>) -> Self {
-        if align_of::<T>() != 1 {
-            panic!("PodCOption only supports Pod types with alignment 1");
-        }
-
-        match value {
-            Some(value) => Self {
-                tag: [1, 0, 0, 0],
-                value,
-            },
-            None => Self {
-                tag: [0, 0, 0, 0],
-                value: T::default(),
-            },
-        }
-    }
-}
-
-impl<T: Default + PartialEq + Pod + Sized> PodCOption<T> {
-    pub const NONE: [u8; 4] = [0, 0, 0, 0];
-
-    pub const SOME: [u8; 4] = [1, 0, 0, 0];
-
-    pub fn some(value: T) -> Self {
-        Self {
-            tag: [1, 0, 0, 0],
-            value,
-        }
-    }
-
-    /// Returns `true` if the option is a `None` value.
-    #[inline]
-    pub fn is_none(&self) -> bool {
-        self.tag == Self::NONE
-    }
-
-    /// Returns `true` if the option is a `Some` value.
-    #[inline]
-    pub fn is_some(&self) -> bool {
-        !self.is_none()
-    }
-
-    /// Returns the contained value as an `Option`.
-    #[inline]
-    pub fn get(self) -> Option<T> {
-        if self.is_none() {
-            None
-        } else {
-            Some(self.value)
-        }
-    }
-
-    /// Returns the contained value as an `Option`.
-    #[inline]
-    pub fn as_ref(&self) -> Option<&T> {
-        if self.is_none() {
-            None
-        } else {
-            Some(&self.value)
-        }
-    }
-
-    /// Returns the contained value as a mutable `Option`.
-    #[inline]
-    pub fn as_mut(&mut self) -> Option<&mut T> {
-        if self.is_none() {
-            None
-        } else {
-            Some(&mut self.value)
-        }
-    }
-
-    #[inline]
-    pub fn set(&mut self, value: T) {
-        self.tag = Self::SOME;
-        self.value = value;
-    }
-
-    #[inline]
-    pub fn clear(&mut self) {
-        self.tag = Self::NONE;
-        // we don't need to zero the value since the tag
-        // indicates it is a `None` value
-    }
-}
-
-/// ## Safety
-///
-/// `PodCOption` requires a `Pod` type `T` with alignment of 1.
-unsafe impl<T: Default + PartialEq + Pod + Sized> Pod for PodCOption<T> {}
-
-/// ## Safety
-///
-/// `PodCOption` requires a `Pod` type `T` with alignment of 1.
-unsafe impl<T: Default + PartialEq + Pod + Sized> Zeroable for PodCOption<T> {}
-
-#[repr(C)]
-#[derive(Copy, Clone, Default, Pod, Zeroable)]
-pub struct PodBool(u8);
-
-impl From<bool> for PodBool {
-    fn from(b: bool) -> Self {
-        Self(b.into())
-    }
-}
-
-impl From<&bool> for PodBool {
-    fn from(b: &bool) -> Self {
-        Self((*b).into())
-    }
-}
-
-impl From<&PodBool> for bool {
-    fn from(b: &PodBool) -> Self {
-        b.0 != 0
-    }
-}
-
-impl From<PodBool> for bool {
-    fn from(b: PodBool) -> Self {
-        b.0 != 0
-    }
-}
-
-#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)]
-#[repr(C)]
-pub struct PodU64(pub [u8; 8]);
-
-impl PodU64 {
-    pub const fn from_primitive(n: u64) -> Self {
-        Self(n.to_le_bytes())
-    }
-}
-impl From<u64> for PodU64 {
-    fn from(n: u64) -> Self {
-        Self::from_primitive(n)
-    }
-}
-impl From<PodU64> for u64 {
-    fn from(pod: PodU64) -> Self {
-        Self::from_le_bytes(pod.0)
-    }
-}
+/// Type alias for fields represented as `COption`.
+pub type COption<T> = ([u8; 4], T);

+ 83 - 7
interface/src/state/multisig.rs

@@ -1,32 +1,108 @@
-use bytemuck::{Pod, Zeroable};
-use pinocchio::pubkey::Pubkey;
+use pinocchio::{
+    account_info::{AccountInfo, Ref},
+    program_error::ProgramError,
+    pubkey::Pubkey,
+};
 
-use super::PodBool;
+use crate::program::ID;
 
 /// Minimum number of multisignature signers (min N)
 pub const MIN_SIGNERS: usize = 1;
+
 /// Maximum number of multisignature signers (max N)
 pub const MAX_SIGNERS: usize = 11;
 
 /// Multisignature data.
 #[repr(C)]
-#[derive(Clone, Copy, Default, Pod, Zeroable)]
 pub struct Multisig {
-    /// Number of signers required
+    /// Number of signers required.
     pub m: u8,
-    /// Number of valid signers
+
+    /// Number of valid signers.
     pub n: u8,
+
     /// Is `true` if this structure has been initialized
-    pub is_initialized: PodBool,
+    is_initialized: u8,
+
     /// Signer public keys
     pub signers: [Pubkey; MAX_SIGNERS],
 }
 
 impl Multisig {
+    /// The length of the `Multisig` account data.
     pub const LEN: usize = core::mem::size_of::<Multisig>();
 
+    /// Return a `Multisig` from the given account info.
+    ///
+    /// This method performs owner and length validation on `AccountInfo`, safe borrowing
+    /// the account data.
+    #[inline]
+    pub fn from_account_info(account_info: &AccountInfo) -> Result<Ref<Self>, ProgramError> {
+        if account_info.data_len() != Self::LEN {
+            return Err(ProgramError::InvalidAccountData);
+        }
+        if account_info.owner() != &ID {
+            return Err(ProgramError::InvalidAccountOwner);
+        }
+        Ok(Ref::map(account_info.try_borrow_data()?, |data| unsafe {
+            Self::from_bytes(data)
+        }))
+    }
+
+    /// Return a `Multisig` from the given account info.
+    ///
+    /// This method performs owner and length validation on `AccountInfo`, but does not
+    /// perform the borrow check.
+    ///
+    /// # Safety
+    ///
+    /// The caller must ensure that it is safe to borrow the account data – e.g., there are
+    /// no mutable borrows of the account data.
+    #[inline]
+    pub unsafe fn from_account_info_unchecked(
+        account_info: &AccountInfo,
+    ) -> Result<&Self, ProgramError> {
+        if account_info.data_len() != Self::LEN {
+            return Err(ProgramError::InvalidAccountData);
+        }
+        if account_info.owner() != &ID {
+            return Err(ProgramError::InvalidAccountOwner);
+        }
+        Ok(Self::from_bytes(account_info.borrow_data_unchecked()))
+    }
+
+    /// Return a `Multisig` reference from the given bytes.
+    ///
+    /// # Safety
+    ///
+    /// The caller must ensure that `bytes` contains a valid representation of `Multisig`.
+    #[inline]
+    pub unsafe fn from_bytes(bytes: &[u8]) -> &Self {
+        &*(bytes.as_ptr() as *const Multisig)
+    }
+
+    /// Return a mutable `Multisig` reference from the given bytes.
+    ///
+    /// # Safety
+    ///
+    /// The caller must ensure that `bytes` contains a valid representation of `Multisig`.
+    #[inline]
+    pub unsafe fn from_bytes_mut(bytes: &mut [u8]) -> &mut Self {
+        &mut *(bytes.as_mut_ptr() as *mut Multisig)
+    }
+
     /// Utility function that checks index is between [`MIN_SIGNERS`] and [`MAX_SIGNERS`].
     pub fn is_valid_signer_index(index: usize) -> bool {
         (MIN_SIGNERS..=MAX_SIGNERS).contains(&index)
     }
+
+    #[inline]
+    pub fn set_initialized(&mut self, value: bool) {
+        self.is_initialized = value as u8;
+    }
+
+    #[inline]
+    pub fn is_initialized(&self) -> bool {
+        self.is_initialized == 1
+    }
 }

+ 0 - 1
p-token/Cargo.toml

@@ -18,7 +18,6 @@ logging = []
 test-sbf = []
 
 [dependencies]
-bytemuck = { workspace = true }
 pinocchio = { workspace = true }
 pinocchio-pubkey = { workspace = true }
 token-interface = { version = "^0", path = "../interface" }

+ 111 - 158
p-token/src/entrypoint.rs

@@ -1,276 +1,229 @@
-use core::str;
-
 use pinocchio::{
     account_info::AccountInfo, entrypoint, program_error::ProgramError, pubkey::Pubkey,
     ProgramResult,
 };
 
-use crate::processor::{
-    amount_to_ui_amount::process_amount_to_ui_amount,
-    approve::process_approve,
-    approve_checked::{process_approve_checked, ApproveChecked},
-    burn::process_burn,
-    burn_checked::{process_burn_checked, BurnChecked},
-    close_account::process_close_account,
-    freeze_account::process_freeze_account,
-    get_account_data_size::process_get_account_data_size,
-    initialize_account::process_initialize_account,
-    initialize_account2::process_initialize_account2,
-    initialize_account3::process_initialize_account3,
-    initialize_immutable_owner::process_initialize_immutable_owner,
-    initialize_mint::process_initialize_mint,
-    initialize_mint2::process_initialize_mint2,
-    initialize_multisig::process_initialize_multisig,
-    initialize_multisig2::process_initialize_multisig2,
-    mint_to::process_mint_to,
-    mint_to_checked::{process_mint_to_checked, MintToChecked},
-    revoke::process_revoke,
-    set_authority::{process_set_authority, SetAuthority},
-    shared::initialize_mint::InitializeMint,
-    sync_native::process_sync_native,
-    thaw_account::process_thaw_account,
-    transfer::process_transfer,
-    transfer_checked::{process_transfer_checked, TransferChecked},
-    ui_amount_to_amount::process_ui_amount_to_amount,
-};
+use crate::processor::*;
 
 entrypoint!(process_instruction);
 
+/// Process an instruction.
+///
+/// The processor of the token program is divided into two parts to reduce the overhead
+/// of having a large `match` statement. The first part of the processor handles the
+/// most common instructions, while the second part handles the remaining instructions.
+/// The rationale is to reduce the overhead of making multiple comparisons for popular
+/// instructions.
+///
+/// Instructions on the first part of the processor:
+///
+/// - `0`: `InitializeMint`
+/// - `3`:  `Transfer`
+/// - `7`:  `MintTo`
+/// - `9`:  `CloseAccount`
+/// - `18`: `InitializeAccount3`
+/// - `20`: `InitializeMint2`
 #[inline(always)]
 pub fn process_instruction(
-    program_id: &Pubkey,
+    _program_id: &Pubkey,
     accounts: &[AccountInfo],
     instruction_data: &[u8],
 ) -> ProgramResult {
-    match instruction_data.split_first() {
+    let (discriminator, instruction_data) = instruction_data
+        .split_first()
+        .ok_or(ProgramError::InvalidInstructionData)?;
+
+    match *discriminator {
         // 0 - InitializeMint
-        Some((&0, data)) => {
+        0 => {
             #[cfg(feature = "logging")]
             pinocchio::msg!("Instruction: InitializeMint");
 
-            let instruction = InitializeMint::try_from_bytes(data)?;
+            process_initialize_mint(accounts, instruction_data, true)
+        }
 
-            process_initialize_mint(accounts, &instruction)
+        // 3 - Transfer
+        3 => {
+            #[cfg(feature = "logging")]
+            pinocchio::msg!("Instruction: Transfer");
+
+            process_transfer(accounts, instruction_data)
         }
-        // 1 - InitializeAccount
-        Some((&1, _)) => {
+        // 7 - MintTo
+        7 => {
             #[cfg(feature = "logging")]
-            pinocchio::msg!("Instruction: InitializeAccount");
+            pinocchio::msg!("Instruction: MintTo");
 
-            process_initialize_account(program_id, accounts)
+            process_mint_to(accounts, instruction_data)
         }
-        // 2 - InitializeMultisig
-        Some((&2, data)) => {
+        // 9 - CloseAccount
+        9 => {
             #[cfg(feature = "logging")]
-            pinocchio::msg!("Instruction: InitializeMultisig");
+            pinocchio::msg!("Instruction: CloseAccount");
+
+            process_close_account(accounts)
+        }
+        // 18 - InitializeAccount3
+        18 => {
+            #[cfg(feature = "logging")]
+            pinocchio::msg!("Instruction: InitializeAccount3");
 
-            let m = data.first().ok_or(ProgramError::InvalidInstructionData)?;
+            process_initialize_account3(accounts, instruction_data)
+        }
+        // 20 - InitializeMint2
+        20 => {
+            #[cfg(feature = "logging")]
+            pinocchio::msg!("Instruction: InitializeMint2");
 
-            process_initialize_multisig(accounts, *m, true)
+            process_initialize_mint2(accounts, instruction_data)
         }
-        // 3 - Transfer
-        Some((&3, data)) => {
+        _ => process_remaining_instruction(accounts, instruction_data, *discriminator),
+    }
+}
+
+/// Process the remaining instructions.
+///
+/// This function is called by the `process_instruction` function if the discriminator
+/// does not match any of the common instructions. This function is used to reduce the
+/// overhead of having a large `match` statement in the `process_instruction` function.
+fn process_remaining_instruction(
+    accounts: &[AccountInfo],
+    instruction_data: &[u8],
+    discriminator: u8,
+) -> ProgramResult {
+    match discriminator {
+        // 1 - InitializeAccount
+        1 => {
             #[cfg(feature = "logging")]
-            pinocchio::msg!("Instruction: Transfer");
+            pinocchio::msg!("Instruction: InitializeAccount");
 
-            let amount = u64::from_le_bytes(
-                data.try_into()
-                    .map_err(|_error| ProgramError::InvalidInstructionData)?,
-            );
+            process_initialize_account(accounts)
+        }
+        // 2 - InitializeMultisig
+        2 => {
+            #[cfg(feature = "logging")]
+            pinocchio::msg!("Instruction: InitializeMultisig");
 
-            process_transfer(program_id, accounts, amount)
+            process_initialize_multisig(accounts, instruction_data)
         }
         // 4 - Approve
-        Some((&4, data)) => {
+        4 => {
             #[cfg(feature = "logging")]
             pinocchio::msg!("Instruction: Approve");
 
-            let amount = u64::from_le_bytes(
-                data.try_into()
-                    .map_err(|_error| ProgramError::InvalidInstructionData)?,
-            );
-
-            process_approve(program_id, accounts, amount)
+            process_approve(accounts, instruction_data)
         }
         // 5 - Revoke
-        Some((&5, _)) => {
+        5 => {
             #[cfg(feature = "logging")]
             pinocchio::msg!("Instruction: Revoke");
 
-            process_revoke(program_id, accounts)
+            process_revoke(accounts, instruction_data)
         }
         // 6 - SetAuthority
-        Some((&6, data)) => {
+        6 => {
             #[cfg(feature = "logging")]
             pinocchio::msg!("Instruction: SetAuthority");
 
-            let instruction = SetAuthority::try_from_bytes(data)?;
-            process_set_authority(
-                program_id,
-                accounts,
-                instruction.authority_type,
-                instruction.new_authority,
-            )
-        }
-        // 7 - MintTo
-        Some((&7, data)) => {
-            #[cfg(feature = "logging")]
-            pinocchio::msg!("Instruction: MintTo");
-
-            let amount = u64::from_le_bytes(
-                data.try_into()
-                    .map_err(|_error| ProgramError::InvalidInstructionData)?,
-            );
-
-            process_mint_to(program_id, accounts, amount)
+            process_set_authority(accounts, instruction_data)
         }
         // 8 - Burn
-        Some((&8, data)) => {
+        8 => {
             #[cfg(feature = "logging")]
             pinocchio::msg!("Instruction: Burn");
 
-            let amount = u64::from_le_bytes(
-                data.try_into()
-                    .map_err(|_error| ProgramError::InvalidInstructionData)?,
-            );
-
-            process_burn(program_id, accounts, amount)
-        }
-        // 9 - CloseAccount
-        Some((&9, _)) => {
-            #[cfg(feature = "logging")]
-            pinocchio::msg!("Instruction: CloseAccount");
-
-            process_close_account(program_id, accounts)
+            process_burn(accounts, instruction_data)
         }
         // 10 - FreezeAccount
-        Some((&10, _)) => {
+        10 => {
             #[cfg(feature = "logging")]
             pinocchio::msg!("Instruction: FreezeAccount");
 
-            process_freeze_account(program_id, accounts)
+            process_freeze_account(accounts)
         }
         // 11 - ThawAccount
-        Some((&11, _)) => {
+        11 => {
             #[cfg(feature = "logging")]
             pinocchio::msg!("Instruction: ThawAccount");
 
-            process_thaw_account(program_id, accounts)
+            process_thaw_account(accounts)
         }
         // 12 - TransferChecked
-        Some((&12, data)) => {
+        12 => {
             #[cfg(feature = "logging")]
             pinocchio::msg!("Instruction: TransferChecked");
 
-            let args = TransferChecked::try_from_bytes(data)?;
-
-            process_transfer_checked(program_id, accounts, args.amount(), args.decimals())
+            process_transfer_checked(accounts, instruction_data)
         }
         // 13 - ApproveChecked
-        Some((&13, data)) => {
+        13 => {
             #[cfg(feature = "logging")]
             pinocchio::msg!("Instruction: ApproveChecked");
 
-            let args = ApproveChecked::try_from_bytes(data)?;
-
-            process_approve_checked(program_id, accounts, args.amount(), args.decimals())
+            process_approve_checked(accounts, instruction_data)
         }
         // 14 - MintToChecked
-        Some((&14, data)) => {
+        14 => {
             #[cfg(feature = "logging")]
             pinocchio::msg!("Instruction: MintToChecked");
 
-            let args = MintToChecked::try_from_bytes(data)?;
-
-            process_mint_to_checked(program_id, accounts, args.amount(), args.decimals())
+            process_mint_to_checked(accounts, instruction_data)
         }
         // 15 - BurnChecked
-        Some((&15, data)) => {
+        15 => {
             #[cfg(feature = "logging")]
             pinocchio::msg!("Instruction: BurnChecked");
 
-            let args = BurnChecked::try_from_bytes(data)?;
-
-            process_burn_checked(program_id, accounts, args.amount(), args.decimals())
+            process_burn_checked(accounts, instruction_data)
         }
         // 16 - InitializeAccount2
-        Some((&16, data)) => {
+        16 => {
             #[cfg(feature = "logging")]
             pinocchio::msg!("Instruction: InitializeAccount2");
 
-            let owner = unsafe { &*(data.as_ptr() as *const Pubkey) };
-
-            process_initialize_account2(program_id, accounts, owner)
+            process_initialize_account2(accounts, instruction_data)
         }
         // 17 - SyncNative
-        Some((&17, _)) => {
+        17 => {
             #[cfg(feature = "logging")]
             pinocchio::msg!("Instruction: SyncNative");
 
-            process_sync_native(program_id, accounts)
-        }
-        // 18 - InitializeAccount3
-        Some((&18, data)) => {
-            #[cfg(feature = "logging")]
-            pinocchio::msg!("Instruction: InitializeAccount3");
-
-            let owner = unsafe { &*(data.as_ptr() as *const Pubkey) };
-
-            process_initialize_account3(program_id, accounts, owner)
+            process_sync_native(accounts)
         }
         // 19 - InitializeMultisig2
-        Some((&19, data)) => {
+        19 => {
             #[cfg(feature = "logging")]
             pinocchio::msg!("Instruction: InitializeMultisig2");
 
-            let m = data.first().ok_or(ProgramError::InvalidInstructionData)?;
-
-            process_initialize_multisig2(accounts, *m)
-        }
-        // 20 - InitializeMint2
-        Some((&20, data)) => {
-            #[cfg(feature = "logging")]
-            pinocchio::msg!("Instruction: InitializeMint2");
-
-            let instruction = InitializeMint::try_from_bytes(data)?;
-
-            process_initialize_mint2(accounts, &instruction)
+            process_initialize_multisig2(accounts, instruction_data)
         }
         // 21 - GetAccountDataSize
-        Some((&21, _)) => {
+        21 => {
             #[cfg(feature = "logging")]
             pinocchio::msg!("Instruction: GetAccountDataSize");
 
-            process_get_account_data_size(program_id, accounts)
+            process_get_account_data_size(accounts)
         }
         // 22 - InitializeImmutableOwner
-        Some((&22, _)) => {
+        22 => {
             #[cfg(feature = "logging")]
             pinocchio::msg!("Instruction: InitializeImmutableOwner");
 
             process_initialize_immutable_owner(accounts)
         }
         // 23 - AmountToUiAmount
-        Some((&23, data)) => {
+        23 => {
             #[cfg(feature = "logging")]
             pinocchio::msg!("Instruction: AmountToUiAmount");
 
-            let amount = u64::from_le_bytes(
-                data.try_into()
-                    .map_err(|_error| ProgramError::InvalidInstructionData)?,
-            );
-
-            process_amount_to_ui_amount(program_id, accounts, amount)
+            process_amount_to_ui_amount(accounts, instruction_data)
         }
         // 24 - UiAmountToAmount
-        Some((&24, data)) => {
+        24 => {
             #[cfg(feature = "logging")]
             pinocchio::msg!("Instruction: UiAmountToAmount");
 
-            let ui_amount =
-                str::from_utf8(data).map_err(|_error| ProgramError::InvalidInstructionData)?;
-
-            process_ui_amount_to_amount(program_id, accounts, ui_amount)
+            process_ui_amount_to_amount(accounts, instruction_data)
         }
         _ => Err(ProgramError::InvalidInstructionData),
     }

+ 1 - 1
p-token/src/lib.rs

@@ -1,4 +1,4 @@
-//! A lighter Token program for SVM.
+//! An ERC20-like Token program for the Solana blockchain.
 
 mod entrypoint;
 mod processor;

+ 12 - 10
p-token/src/processor/amount_to_ui_amount.rs

@@ -1,23 +1,25 @@
 use pinocchio::{
-    account_info::AccountInfo, program::set_return_data, program_error::ProgramError,
-    pubkey::Pubkey, ProgramResult,
+    account_info::AccountInfo, program::set_return_data, program_error::ProgramError, ProgramResult,
 };
-use token_interface::{error::TokenError, state::mint::Mint};
+use token_interface::state::mint::Mint;
 
 use super::{amount_to_ui_amount_string_trimmed, check_account_owner};
 
-#[inline(never)]
+#[inline(always)]
 pub fn process_amount_to_ui_amount(
-    program_id: &Pubkey,
     accounts: &[AccountInfo],
-    amount: u64,
+    instruction_data: &[u8],
 ) -> ProgramResult {
+    let amount = u64::from_le_bytes(
+        instruction_data
+            .try_into()
+            .map_err(|_error| ProgramError::InvalidInstructionData)?,
+    );
+
     let mint_info = accounts.first().ok_or(ProgramError::NotEnoughAccountKeys)?;
-    check_account_owner(program_id, mint_info)?;
+    check_account_owner(mint_info)?;
 
-    let mint =
-        bytemuck::try_from_bytes_mut::<Mint>(unsafe { mint_info.borrow_mut_data_unchecked() })
-            .map_err(|_error| TokenError::InvalidMint)?;
+    let mint = unsafe { Mint::from_bytes(mint_info.borrow_data_unchecked()) };
 
     let ui_amount = amount_to_ui_amount_string_trimmed(amount, mint.decimals);
     set_return_data(&ui_amount.into_bytes());

+ 10 - 8
p-token/src/processor/approve.rs

@@ -1,12 +1,14 @@
-use pinocchio::{account_info::AccountInfo, pubkey::Pubkey, ProgramResult};
+use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramResult};
 
 use super::shared;
 
-#[inline(never)]
-pub fn process_approve(
-    program_id: &Pubkey,
-    accounts: &[AccountInfo],
-    amount: u64,
-) -> ProgramResult {
-    shared::approve::process_approve(program_id, accounts, amount, None)
+#[inline(always)]
+pub fn process_approve(accounts: &[AccountInfo], instruction_data: &[u8]) -> ProgramResult {
+    let amount = u64::from_le_bytes(
+        instruction_data
+            .try_into()
+            .map_err(|_error| ProgramError::InvalidInstructionData)?,
+    );
+
+    shared::approve::process_approve(accounts, amount, None)
 }

+ 15 - 43
p-token/src/processor/approve_checked.rs

@@ -1,47 +1,19 @@
-use std::marker::PhantomData;
-
-use pinocchio::{
-    account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey, ProgramResult,
-};
+use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramResult};
 
 use super::shared;
 
-#[inline(never)]
-pub fn process_approve_checked(
-    program_id: &Pubkey,
-    accounts: &[AccountInfo],
-    amount: u64,
-    decimals: u8,
-) -> ProgramResult {
-    shared::approve::process_approve(program_id, accounts, amount, Some(decimals))
-}
-
-pub struct ApproveChecked<'a> {
-    raw: *const u8,
-
-    _data: PhantomData<&'a [u8]>,
-}
-
-impl ApproveChecked<'_> {
-    pub fn try_from_bytes(bytes: &[u8]) -> Result<ApproveChecked, ProgramError> {
-        if bytes.len() != 9 {
-            return Err(ProgramError::InvalidInstructionData);
-        }
-
-        Ok(ApproveChecked {
-            raw: bytes.as_ptr(),
-            _data: PhantomData,
-        })
-    }
-
-    pub fn amount(&self) -> u64 {
-        unsafe {
-            let amount = self.raw as *const u64;
-            amount.read_unaligned()
-        }
-    }
-
-    pub fn decimals(&self) -> u8 {
-        unsafe { *self.raw.add(8) }
-    }
+#[inline(always)]
+pub fn process_approve_checked(accounts: &[AccountInfo], instruction_data: &[u8]) -> ProgramResult {
+    let (amount, decimals) = instruction_data.split_at(core::mem::size_of::<u64>());
+    let amount = u64::from_le_bytes(
+        amount
+            .try_into()
+            .map_err(|_error| ProgramError::InvalidInstructionData)?,
+    );
+
+    shared::approve::process_approve(
+        accounts,
+        amount,
+        Some(*decimals.first().ok_or(ProgramError::InvalidAccountData)?),
+    )
 }

+ 10 - 4
p-token/src/processor/burn.rs

@@ -1,8 +1,14 @@
-use pinocchio::{account_info::AccountInfo, pubkey::Pubkey, ProgramResult};
+use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramResult};
 
 use super::shared;
 
-#[inline(never)]
-pub fn process_burn(program_id: &Pubkey, accounts: &[AccountInfo], amount: u64) -> ProgramResult {
-    shared::burn::process_burn(program_id, accounts, amount, None)
+#[inline(always)]
+pub fn process_burn(accounts: &[AccountInfo], instruction_data: &[u8]) -> ProgramResult {
+    let amount = u64::from_le_bytes(
+        instruction_data
+            .try_into()
+            .map_err(|_error| ProgramError::InvalidInstructionData)?,
+    );
+
+    shared::burn::process_burn(accounts, amount, None)
 }

+ 19 - 43
p-token/src/processor/burn_checked.rs

@@ -1,47 +1,23 @@
-use std::marker::PhantomData;
-
-use pinocchio::{
-    account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey, ProgramResult,
-};
+use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramResult};
 
 use super::shared;
 
-#[inline(never)]
-pub fn process_burn_checked(
-    program_id: &Pubkey,
-    accounts: &[AccountInfo],
-    amount: u64,
-    decimals: u8,
-) -> ProgramResult {
-    shared::burn::process_burn(program_id, accounts, amount, Some(decimals))
-}
-
-pub struct BurnChecked<'a> {
-    raw: *const u8,
-
-    _data: PhantomData<&'a [u8]>,
-}
-
-impl BurnChecked<'_> {
-    pub fn try_from_bytes(bytes: &[u8]) -> Result<BurnChecked, ProgramError> {
-        if bytes.len() != 9 {
-            return Err(ProgramError::InvalidInstructionData);
-        }
-
-        Ok(BurnChecked {
-            raw: bytes.as_ptr(),
-            _data: PhantomData,
-        })
-    }
-
-    pub fn amount(&self) -> u64 {
-        unsafe {
-            let amount = self.raw as *const u64;
-            amount.read_unaligned()
-        }
-    }
-
-    pub fn decimals(&self) -> u8 {
-        unsafe { *self.raw.add(8) }
-    }
+#[inline(always)]
+pub fn process_burn_checked(accounts: &[AccountInfo], instruction_data: &[u8]) -> ProgramResult {
+    let (amount, decimals) = instruction_data.split_at(core::mem::size_of::<u64>());
+    let amount = u64::from_le_bytes(
+        amount
+            .try_into()
+            .map_err(|_error| ProgramError::InvalidInstructionData)?,
+    );
+
+    shared::burn::process_burn(
+        accounts,
+        amount,
+        Some(
+            *decimals
+                .first()
+                .ok_or(ProgramError::InvalidInstructionData)?,
+        ),
+    )
 }

+ 8 - 12
p-token/src/processor/close_account.rs

@@ -1,12 +1,10 @@
-use pinocchio::{
-    account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey, ProgramResult,
-};
+use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramResult};
 use token_interface::{error::TokenError, state::account::Account};
 
 use super::{is_owned_by_system_program_or_incinerator, validate_owner, INCINERATOR_ID};
 
-#[inline(never)]
-pub fn process_close_account(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
+#[inline(always)]
+pub fn process_close_account(accounts: &[AccountInfo]) -> ProgramResult {
     let [source_account_info, destination_account_info, authority_info, remaining @ ..] = accounts
     else {
         return Err(ProgramError::NotEnoughAccountKeys);
@@ -17,20 +15,18 @@ pub fn process_close_account(program_id: &Pubkey, accounts: &[AccountInfo]) -> P
     }
 
     let source_account =
-        bytemuck::try_from_bytes::<Account>(unsafe { source_account_info.borrow_data_unchecked() })
-            .map_err(|_error| ProgramError::InvalidAccountData)?;
+        unsafe { Account::from_bytes_mut(source_account_info.borrow_mut_data_unchecked()) };
 
-    if source_account.is_native.is_none() && source_account.amount() != 0 {
+    if !source_account.is_native() && source_account.amount() != 0 {
         return Err(TokenError::NonNativeHasBalance.into());
     }
 
     let authority = source_account
-        .close_authority
-        .get()
-        .unwrap_or(source_account.owner);
+        .close_authority()
+        .unwrap_or(&source_account.owner);
 
     if !is_owned_by_system_program_or_incinerator(source_account_info.owner()) {
-        validate_owner(program_id, &authority, authority_info, remaining)?;
+        validate_owner(authority, authority_info, remaining)?;
     } else if destination_account_info.key() != &INCINERATOR_ID {
         return Err(ProgramError::InvalidAccountData);
     }

+ 4 - 4
p-token/src/processor/freeze_account.rs

@@ -1,8 +1,8 @@
-use pinocchio::{account_info::AccountInfo, pubkey::Pubkey, ProgramResult};
+use pinocchio::{account_info::AccountInfo, ProgramResult};
 
 use super::shared::toggle_account_state::process_toggle_account_state;
 
-#[inline(never)]
-pub fn process_freeze_account(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
-    process_toggle_account_state(program_id, accounts, true)
+#[inline(always)]
+pub fn process_freeze_account(accounts: &[AccountInfo]) -> ProgramResult {
+    process_toggle_account_state(accounts, true)
 }

+ 6 - 10
p-token/src/processor/get_account_data_size.rs

@@ -1,24 +1,20 @@
 use pinocchio::{
-    account_info::AccountInfo, program::set_return_data, program_error::ProgramError,
-    pubkey::Pubkey, ProgramResult,
+    account_info::AccountInfo, program::set_return_data, program_error::ProgramError, ProgramResult,
 };
 use token_interface::state::{account::Account, mint::Mint};
 
 use super::check_account_owner;
 
-#[inline(never)]
-pub fn process_get_account_data_size(
-    program_id: &Pubkey,
-    accounts: &[AccountInfo],
-) -> ProgramResult {
+#[inline(always)]
+pub fn process_get_account_data_size(accounts: &[AccountInfo]) -> ProgramResult {
     let [mint_info, _remaning @ ..] = accounts else {
         return Err(ProgramError::NotEnoughAccountKeys);
     };
 
-    check_account_owner(program_id, mint_info)?;
+    // Make sure the mint is valid.
+    check_account_owner(mint_info)?;
 
-    let _ = bytemuck::try_from_bytes::<Mint>(unsafe { mint_info.borrow_data_unchecked() })
-        .map_err(|_error| ProgramError::InvalidAccountData)?;
+    let _ = unsafe { Mint::from_bytes(mint_info.borrow_data_unchecked()) };
 
     set_return_data(&Account::LEN.to_le_bytes());
 

+ 4 - 4
p-token/src/processor/initialize_account.rs

@@ -1,8 +1,8 @@
-use pinocchio::{account_info::AccountInfo, pubkey::Pubkey, ProgramResult};
+use pinocchio::{account_info::AccountInfo, ProgramResult};
 
 use super::shared;
 
-#[inline(never)]
-pub fn process_initialize_account(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
-    shared::initialize_account::process_initialize_account(program_id, accounts, None, true)
+#[inline(always)]
+pub fn process_initialize_account(accounts: &[AccountInfo]) -> ProgramResult {
+    shared::initialize_account::process_initialize_account(accounts, None, true)
 }

+ 4 - 4
p-token/src/processor/initialize_account2.rs

@@ -2,11 +2,11 @@ use pinocchio::{account_info::AccountInfo, pubkey::Pubkey, ProgramResult};
 
 use super::shared;
 
-#[inline(never)]
+#[inline(always)]
 pub fn process_initialize_account2(
-    program_id: &Pubkey,
     accounts: &[AccountInfo],
-    owner: &Pubkey,
+    instruction_data: &[u8],
 ) -> ProgramResult {
-    shared::initialize_account::process_initialize_account(program_id, accounts, Some(owner), true)
+    let owner = unsafe { &*(instruction_data.as_ptr() as *const Pubkey) };
+    shared::initialize_account::process_initialize_account(accounts, Some(owner), true)
 }

+ 4 - 4
p-token/src/processor/initialize_account3.rs

@@ -2,11 +2,11 @@ use pinocchio::{account_info::AccountInfo, pubkey::Pubkey, ProgramResult};
 
 use super::shared;
 
-#[inline(never)]
+#[inline(always)]
 pub fn process_initialize_account3(
-    program_id: &Pubkey,
     accounts: &[AccountInfo],
-    owner: &Pubkey,
+    instruction_data: &[u8],
 ) -> ProgramResult {
-    shared::initialize_account::process_initialize_account(program_id, accounts, Some(owner), false)
+    let owner = unsafe { &*(instruction_data.as_ptr() as *const Pubkey) };
+    shared::initialize_account::process_initialize_account(accounts, Some(owner), false)
 }

+ 3 - 5
p-token/src/processor/initialize_immutable_owner.rs

@@ -1,14 +1,12 @@
 use pinocchio::{account_info::AccountInfo, msg, program_error::ProgramError, ProgramResult};
 use token_interface::{error::TokenError, state::account::Account};
 
-#[inline(never)]
+#[inline(always)]
 pub fn process_initialize_immutable_owner(accounts: &[AccountInfo]) -> ProgramResult {
     let token_account_info = accounts.first().ok_or(ProgramError::NotEnoughAccountKeys)?;
 
-    let account = bytemuck::try_from_bytes_mut::<Account>(unsafe {
-        token_account_info.borrow_mut_data_unchecked()
-    })
-    .map_err(|_error| ProgramError::InvalidAccountData)?;
+    let account =
+        unsafe { Account::from_bytes_mut(token_account_info.borrow_mut_data_unchecked()) };
 
     if account.is_initialized() {
         return Err(TokenError::AlreadyInUse.into());

+ 108 - 5
p-token/src/processor/initialize_mint.rs

@@ -1,8 +1,111 @@
-use pinocchio::{account_info::AccountInfo, ProgramResult};
+use core::{marker::PhantomData, mem::size_of};
+use pinocchio::{
+    account_info::AccountInfo,
+    program_error::ProgramError,
+    pubkey::Pubkey,
+    sysvars::{rent::Rent, Sysvar},
+    ProgramResult,
+};
+use token_interface::{error::TokenError, state::mint::Mint};
 
-use super::shared::{self, initialize_mint::InitializeMint};
+#[inline(always)]
+pub fn process_initialize_mint(
+    accounts: &[AccountInfo],
+    instruction_data: &[u8],
+    rent_sysvar_account: bool,
+) -> ProgramResult {
+    // Validates the instruction data.
 
-#[inline(never)]
-pub fn process_initialize_mint(accounts: &[AccountInfo], args: &InitializeMint) -> ProgramResult {
-    shared::initialize_mint::process_initialize_mint(accounts, args, true)
+    let args = InitializeMint::try_from_bytes(instruction_data)?;
+
+    // Validates the accounts.
+
+    let (mint_info, rent_sysvar_info) = if rent_sysvar_account {
+        let [mint_info, rent_sysvar_info, _remaining @ ..] = accounts else {
+            return Err(ProgramError::NotEnoughAccountKeys);
+        };
+        (mint_info, Some(rent_sysvar_info))
+    } else {
+        let [mint_info, _remaining @ ..] = accounts else {
+            return Err(ProgramError::NotEnoughAccountKeys);
+        };
+        (mint_info, None)
+    };
+
+    let mint = unsafe { Mint::from_bytes_mut(mint_info.borrow_mut_data_unchecked()) };
+
+    if mint.is_initialized() {
+        return Err(TokenError::AlreadyInUse.into());
+    }
+
+    // Check rent-exempt status of the mint account.
+
+    let is_exempt = if let Some(rent_sysvar_info) = rent_sysvar_info {
+        let rent = unsafe { Rent::from_bytes(rent_sysvar_info.borrow_data_unchecked()) };
+        rent.is_exempt(mint_info.lamports(), size_of::<Mint>())
+    } else {
+        Rent::get()?.is_exempt(mint_info.lamports(), size_of::<Mint>())
+    };
+
+    if !is_exempt {
+        return Err(TokenError::NotRentExempt.into());
+    }
+
+    // Initialize the mint.
+
+    mint.set_initialized(true);
+    mint.set_mint_authority(args.mint_authority());
+    mint.decimals = args.decimals();
+
+    if let Some(freeze_authority) = args.freeze_authority() {
+        mint.set_freeze_authority(freeze_authority);
+    }
+
+    Ok(())
+}
+
+/// Instruction data for the `InitializeMint` instruction.
+pub struct InitializeMint<'a> {
+    raw: *const u8,
+
+    _data: PhantomData<&'a [u8]>,
+}
+
+impl InitializeMint<'_> {
+    #[inline]
+    pub fn try_from_bytes(bytes: &[u8]) -> Result<InitializeMint, ProgramError> {
+        // The minimum expected size of the instruction data.
+        // - decimals (1 byte)
+        // - mint_authority (32 bytes)
+        // - option + freeze_authority (1 byte + 32 bytes)
+        if bytes.len() < 34 {
+            return Err(ProgramError::InvalidInstructionData);
+        }
+
+        Ok(InitializeMint {
+            raw: bytes.as_ptr(),
+            _data: PhantomData,
+        })
+    }
+
+    #[inline]
+    pub fn decimals(&self) -> u8 {
+        unsafe { *self.raw }
+    }
+
+    #[inline]
+    pub fn mint_authority(&self) -> &Pubkey {
+        unsafe { &*(self.raw.add(1) as *const Pubkey) }
+    }
+
+    #[inline]
+    pub fn freeze_authority(&self) -> Option<&Pubkey> {
+        unsafe {
+            if *self.raw.add(33) == 0 {
+                Option::None
+            } else {
+                Option::Some(&*(self.raw.add(34) as *const Pubkey))
+            }
+        }
+    }
 }

+ 7 - 4
p-token/src/processor/initialize_mint2.rs

@@ -1,8 +1,11 @@
 use pinocchio::{account_info::AccountInfo, ProgramResult};
 
-use super::shared::{self, initialize_mint::InitializeMint};
+use super::initialize_mint::process_initialize_mint;
 
-#[inline(never)]
-pub fn process_initialize_mint2(accounts: &[AccountInfo], args: &InitializeMint) -> ProgramResult {
-    shared::initialize_mint::process_initialize_mint(accounts, args, false)
+#[inline(always)]
+pub fn process_initialize_mint2(
+    accounts: &[AccountInfo],
+    instruction_data: &[u8],
+) -> ProgramResult {
+    process_initialize_mint(accounts, instruction_data, false)
 }

+ 9 - 58
p-token/src/processor/initialize_multisig.rs

@@ -1,64 +1,15 @@
-use pinocchio::{
-    account_info::AccountInfo,
-    program_error::ProgramError,
-    sysvars::{rent::Rent, Sysvar},
-    ProgramResult,
-};
-use token_interface::{error::TokenError, state::multisig::Multisig};
+use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramResult};
 
-#[inline(never)]
+use super::shared;
+
+#[inline(always)]
 pub fn process_initialize_multisig(
     accounts: &[AccountInfo],
-    m: u8,
-    rent_sysvar_account: bool,
+    instruction_data: &[u8],
 ) -> ProgramResult {
-    let (multisig_info, rent_sysvar_info, remaining) = if rent_sysvar_account {
-        let [multisig_info, rent_sysvar_info, remaining @ ..] = accounts else {
-            return Err(ProgramError::NotEnoughAccountKeys);
-        };
-        (multisig_info, Some(rent_sysvar_info), remaining)
-    } else {
-        let [multisig_info, remaining @ ..] = accounts else {
-            return Err(ProgramError::NotEnoughAccountKeys);
-        };
-        (multisig_info, None, remaining)
-    };
-
-    let multisig_info_data_len = multisig_info.data_len();
-
-    let is_exempt = if let Some(rent_sysvar_info) = rent_sysvar_info {
-        let rent = unsafe { Rent::from_bytes(rent_sysvar_info.borrow_data_unchecked()) };
-        rent.is_exempt(multisig_info.lamports(), multisig_info_data_len)
-    } else {
-        Rent::get()?.is_exempt(multisig_info.lamports(), multisig_info_data_len)
-    };
-
-    if !is_exempt {
-        return Err(TokenError::NotRentExempt.into());
-    }
-
-    let multisig = bytemuck::try_from_bytes_mut::<Multisig>(unsafe {
-        multisig_info.borrow_mut_data_unchecked()
-    })
-    .map_err(|_error| ProgramError::InvalidAccountData)?;
-
-    if multisig.is_initialized.into() {
-        return Err(TokenError::AlreadyInUse.into());
-    }
-
-    multisig.m = m;
-    multisig.n = remaining.len() as u8;
-
-    if !Multisig::is_valid_signer_index(multisig.n as usize) {
-        return Err(TokenError::InvalidNumberOfProvidedSigners.into());
-    }
-    if !Multisig::is_valid_signer_index(multisig.m as usize) {
-        return Err(TokenError::InvalidNumberOfRequiredSigners.into());
-    }
-    for (i, signer_info) in remaining.iter().enumerate() {
-        multisig.signers[i] = *signer_info.key();
-    }
-    multisig.is_initialized = true.into();
+    let m = instruction_data
+        .first()
+        .ok_or(ProgramError::InvalidInstructionData)?;
 
-    Ok(())
+    shared::initialize_multisig::process_initialize_multisig(accounts, *m, true)
 }

+ 10 - 4
p-token/src/processor/initialize_multisig2.rs

@@ -1,8 +1,14 @@
-use pinocchio::{account_info::AccountInfo, ProgramResult};
+use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramResult};
 
 use super::shared;
 
-#[inline(never)]
-pub fn process_initialize_multisig2(accounts: &[AccountInfo], m: u8) -> ProgramResult {
-    shared::initialize_multisig::process_initialize_multisig(accounts, m, false)
+#[inline(always)]
+pub fn process_initialize_multisig2(
+    accounts: &[AccountInfo],
+    instruction_data: &[u8],
+) -> ProgramResult {
+    let m = instruction_data
+        .first()
+        .ok_or(ProgramError::InvalidInstructionData)?;
+    shared::initialize_multisig::process_initialize_multisig(accounts, *m, false)
 }

+ 10 - 8
p-token/src/processor/mint_to.rs

@@ -1,12 +1,14 @@
-use pinocchio::{account_info::AccountInfo, pubkey::Pubkey, ProgramResult};
+use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramResult};
 
 use super::shared;
 
-#[inline(never)]
-pub fn process_mint_to(
-    program_id: &Pubkey,
-    accounts: &[AccountInfo],
-    amount: u64,
-) -> ProgramResult {
-    shared::mint_to::process_mint_to(program_id, accounts, amount, None)
+#[inline(always)]
+pub fn process_mint_to(accounts: &[AccountInfo], instruction_data: &[u8]) -> ProgramResult {
+    let amount = u64::from_le_bytes(
+        instruction_data
+            .try_into()
+            .map_err(|_error| ProgramError::InvalidInstructionData)?,
+    );
+
+    shared::mint_to::process_mint_to(accounts, amount, None)
 }

+ 16 - 43
p-token/src/processor/mint_to_checked.rs

@@ -1,47 +1,20 @@
-use std::marker::PhantomData;
-
-use pinocchio::{
-    account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey, ProgramResult,
-};
+use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramResult};
 
 use super::shared;
 
-#[inline(never)]
-pub fn process_mint_to_checked(
-    program_id: &Pubkey,
-    accounts: &[AccountInfo],
-    amount: u64,
-    decimals: u8,
-) -> ProgramResult {
-    shared::mint_to::process_mint_to(program_id, accounts, amount, Some(decimals))
-}
-
-pub struct MintToChecked<'a> {
-    raw: *const u8,
-
-    _data: PhantomData<&'a [u8]>,
-}
-
-impl MintToChecked<'_> {
-    pub fn try_from_bytes(bytes: &[u8]) -> Result<MintToChecked, ProgramError> {
-        if bytes.len() != 9 {
-            return Err(ProgramError::InvalidInstructionData);
-        }
-
-        Ok(MintToChecked {
-            raw: bytes.as_ptr(),
-            _data: PhantomData,
-        })
-    }
-
-    pub fn amount(&self) -> u64 {
-        unsafe {
-            let amount = self.raw as *const u64;
-            amount.read_unaligned()
-        }
-    }
-
-    pub fn decimals(&self) -> u8 {
-        unsafe { *self.raw.add(8) }
-    }
+#[inline(always)]
+pub fn process_mint_to_checked(accounts: &[AccountInfo], instruction_data: &[u8]) -> ProgramResult {
+    let (amount, decimals) = instruction_data.split_at(core::mem::size_of::<u64>());
+
+    let amount = u64::from_le_bytes(
+        amount
+            .try_into()
+            .map_err(|_error| ProgramError::InvalidInstructionData)?,
+    );
+
+    shared::mint_to::process_mint_to(
+        accounts,
+        amount,
+        Some(*decimals.first().ok_or(ProgramError::InvalidAccountData)?),
+    )
 }

+ 36 - 12
p-token/src/processor/mod.rs

@@ -34,6 +34,32 @@ pub mod ui_amount_to_amount;
 // Shared processors.
 pub mod shared;
 
+pub use amount_to_ui_amount::process_amount_to_ui_amount;
+pub use approve::process_approve;
+pub use approve_checked::process_approve_checked;
+pub use burn::process_burn;
+pub use burn_checked::process_burn_checked;
+pub use close_account::process_close_account;
+pub use freeze_account::process_freeze_account;
+pub use get_account_data_size::process_get_account_data_size;
+pub use initialize_account::process_initialize_account;
+pub use initialize_account2::process_initialize_account2;
+pub use initialize_account3::process_initialize_account3;
+pub use initialize_immutable_owner::process_initialize_immutable_owner;
+pub use initialize_mint::process_initialize_mint;
+pub use initialize_mint2::process_initialize_mint2;
+pub use initialize_multisig::process_initialize_multisig;
+pub use initialize_multisig2::process_initialize_multisig2;
+pub use mint_to::process_mint_to;
+pub use mint_to_checked::process_mint_to_checked;
+pub use revoke::process_revoke;
+pub use set_authority::process_set_authority;
+pub use sync_native::process_sync_native;
+pub use thaw_account::process_thaw_account;
+pub use transfer::process_transfer;
+pub use transfer_checked::process_transfer_checked;
+pub use ui_amount_to_amount::process_ui_amount_to_amount;
+
 /// Incinerator address.
 const INCINERATOR_ID: Pubkey =
     pinocchio_pubkey::pubkey!("1nc1nerator11111111111111111111111111111111");
@@ -42,14 +68,14 @@ const INCINERATOR_ID: Pubkey =
 const SYSTEM_PROGRAM_ID: Pubkey = pinocchio_pubkey::pubkey!("11111111111111111111111111111111");
 
 #[inline(always)]
-pub fn is_owned_by_system_program_or_incinerator(owner: &Pubkey) -> bool {
-    SYSTEM_PROGRAM_ID == *owner || INCINERATOR_ID == *owner
+fn is_owned_by_system_program_or_incinerator(owner: &Pubkey) -> bool {
+    &SYSTEM_PROGRAM_ID == owner || &INCINERATOR_ID == owner
 }
 
 /// Checks that the account is owned by the expected program.
 #[inline(always)]
-pub fn check_account_owner(program_id: &Pubkey, account_info: &AccountInfo) -> ProgramResult {
-    if program_id != account_info.owner() {
+fn check_account_owner(account_info: &AccountInfo) -> ProgramResult {
+    if &crate::ID != account_info.owner() {
         Err(ProgramError::IncorrectProgramId)
     } else {
         Ok(())
@@ -58,8 +84,7 @@ pub fn check_account_owner(program_id: &Pubkey, account_info: &AccountInfo) -> P
 
 /// Validates owner(s) are present
 #[inline(always)]
-pub fn validate_owner(
-    program_id: &Pubkey,
+fn validate_owner(
     expected_owner: &Pubkey,
     owner_account_info: &AccountInfo,
     signers: &[AccountInfo],
@@ -68,9 +93,8 @@ pub fn validate_owner(
         return Err(TokenError::OwnerMismatch.into());
     }
 
-    if owner_account_info.data_len() == Multisig::LEN && program_id != owner_account_info.owner() {
-        let multisig_data = owner_account_info.try_borrow_data()?;
-        let multisig = bytemuck::from_bytes::<Multisig>(&multisig_data);
+    if owner_account_info.data_len() == Multisig::LEN && &crate::ID != owner_account_info.owner() {
+        let multisig = unsafe { Multisig::from_bytes(owner_account_info.borrow_data_unchecked()) };
 
         let mut num_signers = 0;
         let mut matched = [false; MAX_SIGNERS];
@@ -99,7 +123,7 @@ pub fn validate_owner(
 /// Convert a raw amount to its UI representation using the given decimals field
 /// Excess zeroes or unneeded decimal point are trimmed.
 #[inline(always)]
-pub fn amount_to_ui_amount_string_trimmed(amount: u64, decimals: u8) -> String {
+fn amount_to_ui_amount_string_trimmed(amount: u64, decimals: u8) -> String {
     let mut s = amount_to_ui_amount_string(amount, decimals);
     if decimals > 0 {
         let zeros_trimmed = s.trim_end_matches('0');
@@ -111,7 +135,7 @@ pub fn amount_to_ui_amount_string_trimmed(amount: u64, decimals: u8) -> String {
 /// Convert a raw amount to its UI representation (using the decimals field
 /// defined in its mint)
 #[inline(always)]
-pub fn amount_to_ui_amount_string(amount: u64, decimals: u8) -> String {
+fn amount_to_ui_amount_string(amount: u64, decimals: u8) -> String {
     let decimals = decimals as usize;
     if decimals > 0 {
         // Left-pad zeros to decimals + 1, so we at least have an integer zero
@@ -126,7 +150,7 @@ pub fn amount_to_ui_amount_string(amount: u64, decimals: u8) -> String {
 
 /// Try to convert a UI representation of a token amount to its raw amount using
 /// the given decimals field
-pub fn try_ui_amount_into_amount(ui_amount: String, decimals: u8) -> Result<u64, ProgramError> {
+fn try_ui_amount_into_amount(ui_amount: String, decimals: u8) -> Result<u64, ProgramError> {
     let decimals = decimals as usize;
     let mut parts = ui_amount.split('.');
     // splitting a string, even an empty one, will always yield an iterator of at

+ 8 - 12
p-token/src/processor/revoke.rs

@@ -1,29 +1,25 @@
-use pinocchio::{
-    account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey, ProgramResult,
-};
+use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramResult};
 use token_interface::{error::TokenError, state::account::Account};
 
 use super::validate_owner;
 
-#[inline(never)]
-pub fn process_revoke(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
+#[inline(always)]
+pub fn process_revoke(accounts: &[AccountInfo], _instruction_data: &[u8]) -> ProgramResult {
     let [source_account_info, owner_info, remaning @ ..] = accounts else {
         return Err(ProgramError::NotEnoughAccountKeys);
     };
 
-    let source_account = bytemuck::try_from_bytes_mut::<Account>(unsafe {
-        source_account_info.borrow_mut_data_unchecked()
-    })
-    .map_err(|_error| ProgramError::InvalidAccountData)?;
+    let source_account =
+        unsafe { Account::from_bytes_mut(source_account_info.borrow_mut_data_unchecked()) };
 
     if source_account.is_frozen() {
         return Err(TokenError::AccountFrozen.into());
     }
 
-    validate_owner(program_id, &source_account.owner, owner_info, remaning)?;
+    validate_owner(&source_account.owner, owner_info, remaning)?;
 
-    source_account.delegate.clear();
-    source_account.delegated_amount = 0.into();
+    source_account.clear_delegate();
+    source_account.set_delegated_amount(0);
 
     Ok(())
 }

+ 77 - 63
p-token/src/processor/set_authority.rs

@@ -1,33 +1,33 @@
+use core::marker::PhantomData;
+
 use pinocchio::{
-    account_info::AccountInfo,
-    program_error::ProgramError,
-    pubkey::{Pubkey, PUBKEY_BYTES},
-    ProgramResult,
+    account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey, ProgramResult,
 };
 use token_interface::{
     error::TokenError,
     instruction::AuthorityType,
-    state::{account::Account, mint::Mint, PodCOption},
+    state::{account::Account, mint::Mint},
 };
 
 use super::validate_owner;
 
-#[inline(never)]
-pub fn process_set_authority(
-    program_id: &Pubkey,
-    accounts: &[AccountInfo],
-    authority_type: AuthorityType,
-    new_authority: Option<&Pubkey>,
-) -> ProgramResult {
+#[inline(always)]
+pub fn process_set_authority(accounts: &[AccountInfo], instruction_data: &[u8]) -> ProgramResult {
+    // Validates the instruction data.
+
+    let args = SetAuthority::try_from_bytes(instruction_data)?;
+
+    let authority_type = args.authority_type()?;
+    let new_authority = args.new_authority();
+
+    // Validates the accounts.
+
     let [account_info, authority_info, remaning @ ..] = accounts else {
         return Err(ProgramError::NotEnoughAccountKeys);
     };
 
     if account_info.data_len() == Account::LEN {
-        let account = bytemuck::try_from_bytes_mut::<Account>(unsafe {
-            account_info.borrow_mut_data_unchecked()
-        })
-        .map_err(|_error| ProgramError::InvalidAccountData)?;
+        let account = unsafe { Account::from_bytes_mut(account_info.borrow_mut_data_unchecked()) };
 
         if account.is_frozen() {
             return Err(TokenError::AccountFrozen.into());
@@ -35,7 +35,7 @@ pub fn process_set_authority(
 
         match authority_type {
             AuthorityType::AccountOwner => {
-                validate_owner(program_id, &account.owner, authority_info, remaning)?;
+                validate_owner(&account.owner, authority_info, remaning)?;
 
                 if let Some(authority) = new_authority {
                     account.owner = *authority;
@@ -43,50 +43,58 @@ pub fn process_set_authority(
                     return Err(TokenError::InvalidInstruction.into());
                 }
 
-                account.delegate.clear();
-                account.delegated_amount = 0.into();
+                account.clear_delegate();
+                account.set_delegated_amount(0);
 
-                if account.is_native.is_some() {
-                    account.close_authority.clear();
+                if account.is_native() {
+                    account.clear_close_authority();
                 }
             }
             AuthorityType::CloseAccount => {
-                let authority = account.close_authority.as_ref().unwrap_or(&account.owner);
-                validate_owner(program_id, authority, authority_info, remaning)?;
-                account.close_authority = PodCOption::from(new_authority.copied());
+                let authority = account.close_authority().unwrap_or(&account.owner);
+                validate_owner(authority, authority_info, remaning)?;
+
+                if let Some(authority) = new_authority {
+                    account.set_close_authority(authority);
+                } else {
+                    account.clear_close_authority();
+                }
             }
             _ => {
                 return Err(TokenError::AuthorityTypeNotSupported.into());
             }
         }
     } else if account_info.data_len() == Mint::LEN {
-        let mint = bytemuck::try_from_bytes_mut::<Mint>(unsafe {
-            account_info.borrow_mut_data_unchecked()
-        })
-        .map_err(|_error| ProgramError::InvalidAccountData)?;
+        let mint = unsafe { Mint::from_bytes_mut(account_info.borrow_mut_data_unchecked()) };
 
         match authority_type {
             AuthorityType::MintTokens => {
                 // Once a mint's supply is fixed, it cannot be undone by setting a new
-                // mint_authority
-                let mint_authority = mint
-                    .mint_authority
-                    .as_ref()
-                    .ok_or(TokenError::FixedSupply)?;
-
-                validate_owner(program_id, mint_authority, authority_info, remaning)?;
-                mint.mint_authority = PodCOption::from(new_authority.copied());
+                // mint_authority.
+                let mint_authority = mint.mint_authority().ok_or(TokenError::FixedSupply)?;
+
+                validate_owner(mint_authority, authority_info, remaning)?;
+
+                if let Some(authority) = new_authority {
+                    mint.set_mint_authority(authority);
+                } else {
+                    mint.clear_mint_authority();
+                }
             }
             AuthorityType::FreezeAccount => {
                 // Once a mint's freeze authority is disabled, it cannot be re-enabled by
-                // setting a new freeze_authority
+                // setting a new freeze_authority.
                 let freeze_authority = mint
-                    .freeze_authority
-                    .as_ref()
+                    .freeze_authority()
                     .ok_or(TokenError::MintCannotFreeze)?;
 
-                validate_owner(program_id, freeze_authority, authority_info, remaning)?;
-                mint.freeze_authority = PodCOption::from(new_authority.copied());
+                validate_owner(freeze_authority, authority_info, remaning)?;
+
+                if let Some(authority) = new_authority {
+                    mint.set_freeze_authority(authority);
+                } else {
+                    mint.clear_freeze_authority();
+                }
             }
             _ => {
                 return Err(TokenError::AuthorityTypeNotSupported.into());
@@ -99,35 +107,41 @@ pub fn process_set_authority(
     Ok(())
 }
 
-/// Instruction data for the `InitializeMint` instruction.
-pub struct SetAuthority<'a> {
-    pub authority_type: AuthorityType,
+struct SetAuthority<'a> {
+    raw: *const u8,
 
-    /// New authority.
-    pub new_authority: Option<&'a Pubkey>,
+    _data: PhantomData<&'a [u8]>,
 }
 
-impl<'a> SetAuthority<'a> {
-    pub fn try_from_bytes(data: &'a [u8]) -> Result<Self, ProgramError> {
-        // We expect the data to be at least the size of the u8 (authority_type)
-        // plus one byte for the authority option.
-        if data.len() <= 2 {
+impl SetAuthority<'_> {
+    #[inline(always)]
+    pub fn try_from_bytes(bytes: &[u8]) -> Result<SetAuthority, ProgramError> {
+        // The minimum expected size of the instruction data.
+        // - authority_type (1 byte)
+        // - option + new_authority (1 byte + 32 bytes)
+        if bytes.len() < 2 {
             return Err(ProgramError::InvalidInstructionData);
         }
 
-        let (authority_type, remaining) = data.split_at(1);
+        Ok(SetAuthority {
+            raw: bytes.as_ptr(),
+            _data: PhantomData,
+        })
+    }
 
-        let new_authority = match remaining.split_first() {
-            Some((&0, _)) => None,
-            Some((&1, pubkey)) if pubkey.len() == PUBKEY_BYTES => {
-                Some(bytemuck::from_bytes::<Pubkey>(pubkey))
-            }
-            _ => return Err(ProgramError::InvalidInstructionData),
-        };
+    #[inline(always)]
+    pub fn authority_type(&self) -> Result<AuthorityType, ProgramError> {
+        unsafe { AuthorityType::from(*self.raw) }
+    }
 
-        Ok(Self {
-            authority_type: AuthorityType::from(authority_type[0]),
-            new_authority,
-        })
+    #[inline(always)]
+    pub fn new_authority(&self) -> Option<&Pubkey> {
+        unsafe {
+            if *self.raw.add(1) == 0 {
+                Option::None
+            } else {
+                Option::Some(&*(self.raw.add(2) as *const Pubkey))
+            }
+        }
     }
 }

+ 15 - 14
p-token/src/processor/shared/approve.rs

@@ -1,20 +1,20 @@
-use pinocchio::{
-    account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey, ProgramResult,
-};
+use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramResult};
 use token_interface::{
     error::TokenError,
-    state::{account::Account, mint::Mint, PodCOption},
+    state::{account::Account, mint::Mint},
 };
 
 use crate::processor::validate_owner;
 
 #[inline(always)]
 pub fn process_approve(
-    program_id: &Pubkey,
     accounts: &[AccountInfo],
     amount: u64,
     expected_decimals: Option<u8>,
 ) -> ProgramResult {
+    // Accounts expected depend on whether we have the mint `decimals` or not; when we have the
+    // mint `decimals`, we expect the mint account to be present.
+
     let (source_account_info, expected_mint_info, delegate_info, owner_info, remaining) =
         if let Some(expected_decimals) = expected_decimals {
             let [source_account_info, expected_mint_info, delegate_info, owner_info, remaning @ ..] =
@@ -43,10 +43,10 @@ pub fn process_approve(
             )
         };
 
-    let source_account = bytemuck::try_from_bytes_mut::<Account>(unsafe {
-        source_account_info.borrow_mut_data_unchecked()
-    })
-    .map_err(|_error| ProgramError::InvalidAccountData)?;
+    // Validates source account.
+
+    let source_account =
+        unsafe { Account::from_bytes_mut(source_account_info.borrow_mut_data_unchecked()) };
 
     if source_account.is_frozen() {
         return Err(TokenError::AccountFrozen.into());
@@ -57,18 +57,19 @@ pub fn process_approve(
             return Err(TokenError::MintMismatch.into());
         }
 
-        let mint = bytemuck::try_from_bytes::<Mint>(unsafe { mint_info.borrow_data_unchecked() })
-            .map_err(|_error| ProgramError::InvalidAccountData)?;
+        let mint = unsafe { Mint::from_bytes(mint_info.borrow_data_unchecked()) };
 
         if expected_decimals != mint.decimals {
             return Err(TokenError::MintDecimalsMismatch.into());
         }
     }
 
-    validate_owner(program_id, &source_account.owner, owner_info, remaining)?;
+    validate_owner(&source_account.owner, owner_info, remaining)?;
+
+    // Sets the delegate and delegated amount.
 
-    source_account.delegate = PodCOption::some(*delegate_info.key());
-    source_account.delegated_amount = amount.into();
+    source_account.set_delegate(delegate_info.key());
+    source_account.set_delegated_amount(amount);
 
     Ok(())
 }

+ 26 - 30
p-token/src/processor/shared/burn.rs

@@ -1,6 +1,4 @@
-use pinocchio::{
-    account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey, ProgramResult,
-};
+use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramResult};
 use token_interface::{
     error::TokenError,
     state::{account::Account, mint::Mint},
@@ -12,7 +10,6 @@ use crate::processor::{
 
 #[inline(always)]
 pub fn process_burn(
-    program_id: &Pubkey,
     accounts: &[AccountInfo],
     amount: u64,
     expected_decimals: Option<u8>,
@@ -21,29 +18,24 @@ pub fn process_burn(
         return Err(ProgramError::NotEnoughAccountKeys);
     };
 
-    // Safety: There are no conflicting borrows – the source account is only borrowed once.
-    let source_account = bytemuck::try_from_bytes_mut::<Account>(unsafe {
-        source_account_info.borrow_mut_data_unchecked()
-    })
-    .map_err(|_error| ProgramError::InvalidAccountData)?;
+    let source_account =
+        unsafe { Account::from_bytes_mut(source_account_info.borrow_mut_data_unchecked()) };
 
     if source_account.is_frozen() {
         return Err(TokenError::AccountFrozen.into());
     }
-    if source_account.is_native.is_some() {
+    if source_account.is_native() {
         return Err(TokenError::NativeNotSupported.into());
     }
 
     // Ensure the source account has the sufficient amount. This is done before
     // the value is updated on the account.
-    let updated_source_amount = u64::from(source_account.amount)
+    let updated_source_amount = source_account
+        .amount()
         .checked_sub(amount)
         .ok_or(TokenError::InsufficientFunds)?;
 
-    // Safety: There are no conflicting borrows – the mint account is only borrowed once.
-    let mint =
-        bytemuck::try_from_bytes_mut::<Mint>(unsafe { mint_info.borrow_mut_data_unchecked() })
-            .map_err(|_error| ProgramError::InvalidAccountData)?;
+    let mint = unsafe { Mint::from_bytes_mut(mint_info.borrow_mut_data_unchecked()) };
 
     if mint_info.key() != &source_account.mint {
         return Err(TokenError::MintMismatch.into());
@@ -56,36 +48,40 @@ pub fn process_burn(
     }
 
     if !is_owned_by_system_program_or_incinerator(&source_account.owner) {
-        match source_account.delegate.as_ref() {
+        match source_account.delegate() {
             Some(delegate) if authority_info.key() == delegate => {
-                validate_owner(program_id, delegate, authority_info, remaining)?;
+                validate_owner(delegate, authority_info, remaining)?;
 
-                let delegated_amount = u64::from(source_account.delegated_amount)
+                let delegated_amount = source_account
+                    .delegated_amount()
                     .checked_sub(amount)
                     .ok_or(TokenError::InsufficientFunds)?;
-                source_account.delegated_amount = delegated_amount.into();
+                source_account.set_delegated_amount(delegated_amount);
 
                 if delegated_amount == 0 {
-                    source_account.delegate.clear();
+                    source_account.clear_delegate();
                 }
             }
             _ => {
-                validate_owner(program_id, &source_account.owner, authority_info, remaining)?;
+                validate_owner(&source_account.owner, authority_info, remaining)?;
             }
         }
     }
 
-    if amount == 0 {
-        check_account_owner(program_id, source_account_info)?;
-        check_account_owner(program_id, mint_info)?;
-    }
+    // Updates the source account and mint supply.
 
-    source_account.amount = updated_source_amount.into();
+    if amount == 0 {
+        check_account_owner(source_account_info)?;
+        check_account_owner(mint_info)?;
+    } else {
+        source_account.set_amount(updated_source_amount);
 
-    let mint_supply = u64::from(mint.supply)
-        .checked_sub(amount)
-        .ok_or(TokenError::Overflow)?;
-    mint.supply = mint_supply.into();
+        let mint_supply = mint
+            .supply()
+            .checked_sub(amount)
+            .ok_or(TokenError::Overflow)?;
+        mint.set_supply(mint_supply);
+    }
 
     Ok(())
 }

+ 19 - 29
p-token/src/processor/shared/initialize_account.rs

@@ -1,3 +1,4 @@
+use core::mem::size_of;
 use pinocchio::{
     account_info::AccountInfo,
     program_error::ProgramError,
@@ -5,26 +6,22 @@ use pinocchio::{
     sysvars::{rent::Rent, Sysvar},
     ProgramResult,
 };
-use std::mem::size_of;
 use token_interface::{
     error::TokenError,
     native_mint::is_native_mint,
-    state::{
-        account::{Account, AccountState},
-        mint::Mint,
-        PodCOption,
-    },
+    state::{account::Account, account_state::AccountState, mint::Mint},
 };
 
 use crate::processor::check_account_owner;
 
 #[inline(always)]
 pub fn process_initialize_account(
-    program_id: &Pubkey,
     accounts: &[AccountInfo],
     owner: Option<&Pubkey>,
     rent_sysvar_account: bool,
 ) -> ProgramResult {
+    // Accounts expected depend on whether we have the `rent_sysvar` account or not.
+
     let (new_account_info, mint_info, owner, remaning) = if let Some(owner) = owner {
         let [new_account_info, mint_info, remaning @ ..] = accounts else {
             return Err(ProgramError::NotEnoughAccountKeys);
@@ -51,9 +48,9 @@ pub fn process_initialize_account(
         return Err(TokenError::NotRentExempt.into());
     }
 
-    let account_data = unsafe { new_account_info.borrow_mut_data_unchecked() };
-    let account = bytemuck::try_from_bytes_mut::<Account>(account_data)
-        .map_err(|_error| ProgramError::InvalidAccountData)?;
+    // Initialize the account.
+
+    let account = unsafe { Account::from_bytes_mut(new_account_info.borrow_mut_data_unchecked()) };
 
     if account.is_initialized() {
         return Err(TokenError::AlreadyInUse.into());
@@ -62,40 +59,33 @@ pub fn process_initialize_account(
     let is_native_mint = is_native_mint(mint_info.key());
 
     if !is_native_mint {
-        check_account_owner(program_id, mint_info)?;
+        check_account_owner(mint_info)?;
 
-        let mint_data = unsafe { mint_info.borrow_data_unchecked() };
-        let mint = bytemuck::try_from_bytes::<Mint>(mint_data)
-            .map_err(|_error| ProgramError::InvalidAccountData)?;
+        let mint = unsafe { Mint::from_bytes(mint_info.borrow_data_unchecked()) };
 
-        if !bool::from(mint.is_initialized) {
+        if !mint.is_initialized() {
             return Err(TokenError::InvalidMint.into());
         }
     }
 
+    account.state = AccountState::Initialized;
     account.mint = *mint_info.key();
     account.owner = *owner;
-    account.close_authority.clear();
-    account.delegate.clear();
-    account.delegated_amount = 0u64.into();
-    account.state = AccountState::Initialized as u8;
 
     if is_native_mint {
         let rent = Rent::get()?;
         let rent_exempt_reserve = rent.minimum_balance(size_of::<Account>());
 
-        account.is_native = PodCOption::from(Some(rent_exempt_reserve.into()));
+        account.set_native(true);
         unsafe {
-            account.amount = new_account_info
-                .borrow_lamports_unchecked()
-                .checked_sub(rent_exempt_reserve)
-                .ok_or(TokenError::Overflow)?
-                .into()
+            account.set_amount(
+                new_account_info
+                    .borrow_lamports_unchecked()
+                    .checked_sub(rent_exempt_reserve)
+                    .ok_or(TokenError::Overflow)?,
+            );
         }
-    } else {
-        account.is_native.clear();
-        account.amount = 0u64.into();
-    };
+    }
 
     Ok(())
 }

+ 0 - 106
p-token/src/processor/shared/initialize_mint.rs

@@ -1,106 +0,0 @@
-use pinocchio::{
-    account_info::AccountInfo,
-    program_error::ProgramError,
-    pubkey::Pubkey,
-    sysvars::{rent::Rent, Sysvar},
-    ProgramResult,
-};
-use std::{marker::PhantomData, mem::size_of};
-use token_interface::{
-    error::TokenError,
-    state::{mint::Mint, PodCOption},
-};
-
-#[inline(always)]
-pub fn process_initialize_mint(
-    accounts: &[AccountInfo],
-    args: &InitializeMint,
-    rent_sysvar_account: bool,
-) -> ProgramResult {
-    let (mint_info, rent_sysvar_info) = if rent_sysvar_account {
-        let [mint_info, rent_sysvar_info, _remaining @ ..] = accounts else {
-            return Err(ProgramError::NotEnoughAccountKeys);
-        };
-        (mint_info, Some(rent_sysvar_info))
-    } else {
-        let [mint_info, _remaining @ ..] = accounts else {
-            return Err(ProgramError::NotEnoughAccountKeys);
-        };
-        (mint_info, None)
-    };
-
-    let mint =
-        bytemuck::try_from_bytes_mut::<Mint>(unsafe { mint_info.borrow_mut_data_unchecked() })
-            .map_err(|_error| ProgramError::InvalidAccountData)?;
-
-    if mint.is_initialized.into() {
-        return Err(TokenError::AlreadyInUse.into());
-    }
-
-    // Check rent-exempt status of the mint account.
-
-    let is_exempt = if let Some(rent_sysvar_info) = rent_sysvar_info {
-        let rent = unsafe { Rent::from_bytes(rent_sysvar_info.borrow_data_unchecked()) };
-        rent.is_exempt(mint_info.lamports(), size_of::<Mint>())
-    } else {
-        Rent::get()?.is_exempt(mint_info.lamports(), size_of::<Mint>())
-    };
-
-    if !is_exempt {
-        return Err(TokenError::NotRentExempt.into());
-    }
-
-    // Initialize the mint.
-
-    mint.mint_authority = PodCOption::from(Some(*args.mint_authority()));
-    mint.decimals = args.decimals();
-    mint.is_initialized = true.into();
-
-    if let Some(freeze_authority) = args.freeze_authority() {
-        mint.freeze_authority = PodCOption::from(Some(*freeze_authority));
-    }
-
-    Ok(())
-}
-
-/// Instruction data for the `InitializeMint` instruction.
-pub struct InitializeMint<'a> {
-    raw: *const u8,
-
-    _data: PhantomData<&'a [u8]>,
-}
-
-impl InitializeMint<'_> {
-    pub fn try_from_bytes(bytes: &[u8]) -> Result<InitializeMint, ProgramError> {
-        // The minimum expected size of the instruction data.
-        // - decimals (1 byte)
-        // - mint_authority (32 bytes)
-        // - option + freeze_authority (1 byte + 32 bytes)
-        if bytes.len() < 34 {
-            return Err(ProgramError::InvalidInstructionData);
-        }
-
-        Ok(InitializeMint {
-            raw: bytes.as_ptr(),
-            _data: PhantomData,
-        })
-    }
-
-    pub fn decimals(&self) -> u8 {
-        unsafe { *self.raw }
-    }
-
-    pub fn mint_authority(&self) -> &Pubkey {
-        unsafe { &*(self.raw.add(1) as *const Pubkey) }
-    }
-
-    pub fn freeze_authority(&self) -> Option<&Pubkey> {
-        unsafe {
-            if *self.raw.add(33) == 0 {
-                Option::None
-            } else {
-                Option::Some(&*(self.raw.add(34) as *const Pubkey))
-            }
-        }
-    }
-}

+ 9 - 6
p-token/src/processor/shared/initialize_multisig.rs

@@ -12,6 +12,8 @@ pub fn process_initialize_multisig(
     m: u8,
     rent_sysvar_account: bool,
 ) -> ProgramResult {
+    // Accounts expected depend on whether we have the `rent_sysvar` account or not.
+
     let (multisig_info, rent_sysvar_info, remaining) = if rent_sysvar_account {
         let [multisig_info, rent_sysvar_info, remaining @ ..] = accounts else {
             return Err(ProgramError::NotEnoughAccountKeys);
@@ -37,15 +39,14 @@ pub fn process_initialize_multisig(
         return Err(TokenError::NotRentExempt.into());
     }
 
-    let multisig = bytemuck::try_from_bytes_mut::<Multisig>(unsafe {
-        multisig_info.borrow_mut_data_unchecked()
-    })
-    .map_err(|_error| ProgramError::InvalidAccountData)?;
+    let multisig = unsafe { Multisig::from_bytes_mut(multisig_info.borrow_mut_data_unchecked()) };
 
-    if multisig.is_initialized.into() {
+    if multisig.is_initialized() {
         return Err(TokenError::AlreadyInUse.into());
     }
 
+    // Initialize the multisig account.
+
     multisig.m = m;
     multisig.n = remaining.len() as u8;
 
@@ -55,10 +56,12 @@ pub fn process_initialize_multisig(
     if !Multisig::is_valid_signer_index(multisig.m as usize) {
         return Err(TokenError::InvalidNumberOfRequiredSigners.into());
     }
+
     for (i, signer_info) in remaining.iter().enumerate() {
         multisig.signers[i] = *signer_info.key();
     }
-    multisig.is_initialized = true.into();
+
+    multisig.set_initialized(true);
 
     Ok(())
 }

+ 16 - 21
p-token/src/processor/shared/mint_to.rs

@@ -1,9 +1,6 @@
-use pinocchio::{
-    account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey, ProgramResult,
-};
+use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramResult};
 use token_interface::{
     error::TokenError,
-    native_mint::is_native_mint,
     state::{account::Account, mint::Mint},
 };
 
@@ -11,7 +8,6 @@ use crate::processor::{check_account_owner, validate_owner};
 
 #[inline(always)]
 pub fn process_mint_to(
-    program_id: &Pubkey,
     accounts: &[AccountInfo],
     amount: u64,
     expected_decimals: Option<u8>,
@@ -20,17 +16,16 @@ pub fn process_mint_to(
         return Err(ProgramError::NotEnoughAccountKeys);
     };
 
-    // destination account
+    // Validates the destination account.
 
-    let account_data = unsafe { destination_account_info.borrow_mut_data_unchecked() };
-    let destination_account = bytemuck::try_from_bytes_mut::<Account>(account_data)
-        .map_err(|_error| ProgramError::InvalidAccountData)?;
+    let destination_account =
+        unsafe { Account::from_bytes_mut(destination_account_info.borrow_mut_data_unchecked()) };
 
     if destination_account.is_frozen() {
         return Err(TokenError::AccountFrozen.into());
     }
 
-    if is_native_mint(mint_info.key()) {
+    if destination_account.is_native() {
         return Err(TokenError::NativeNotSupported.into());
     }
 
@@ -38,9 +33,7 @@ pub fn process_mint_to(
         return Err(TokenError::MintMismatch.into());
     }
 
-    let mint_data = unsafe { mint_info.borrow_mut_data_unchecked() };
-    let mint = bytemuck::try_from_bytes_mut::<Mint>(mint_data)
-        .map_err(|_error| ProgramError::InvalidAccountData)?;
+    let mint = unsafe { Mint::from_bytes_mut(mint_info.borrow_mut_data_unchecked()) };
 
     if let Some(expected_decimals) = expected_decimals {
         if expected_decimals != mint.decimals {
@@ -48,25 +41,27 @@ pub fn process_mint_to(
         }
     }
 
-    match mint.mint_authority.get() {
-        Some(mint_authority) => validate_owner(program_id, &mint_authority, owner_info, remaining)?,
+    match mint.mint_authority() {
+        Some(mint_authority) => validate_owner(mint_authority, owner_info, remaining)?,
         None => return Err(TokenError::FixedSupply.into()),
     }
 
     if amount == 0 {
-        check_account_owner(program_id, mint_info)?;
-        check_account_owner(program_id, destination_account_info)?;
+        check_account_owner(mint_info)?;
+        check_account_owner(destination_account_info)?;
     }
 
-    let destination_amount = u64::from(destination_account.amount)
+    let destination_amount = destination_account
+        .amount()
         .checked_add(amount)
         .ok_or(ProgramError::InvalidAccountData)?;
-    destination_account.amount = destination_amount.into();
+    destination_account.set_amount(destination_amount);
 
-    let mint_supply = u64::from(mint.supply)
+    let mint_supply = mint
+        .supply()
         .checked_add(amount)
         .ok_or(ProgramError::InvalidAccountData)?;
-    mint.supply = mint_supply.into();
+    mint.set_supply(mint_supply);
 
     Ok(())
 }

+ 5 - 1
p-token/src/processor/shared/mod.rs

@@ -1,7 +1,11 @@
+//! Shared processor functions.
+//!
+//! This module contains the shared processor functions that are used by
+//! the multiple instruction processors.
+
 pub mod approve;
 pub mod burn;
 pub mod initialize_account;
-pub mod initialize_mint;
 pub mod initialize_multisig;
 pub mod mint_to;
 pub mod toggle_account_state;

+ 11 - 23
p-token/src/processor/shared/toggle_account_state.rs

@@ -1,54 +1,42 @@
-use pinocchio::{
-    account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey, ProgramResult,
-};
+use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramResult};
 use token_interface::{
     error::TokenError,
-    state::{
-        account::{Account, AccountState},
-        mint::Mint,
-    },
+    state::{account::Account, account_state::AccountState, mint::Mint},
 };
 
 use crate::processor::validate_owner;
 
 #[inline(always)]
-pub fn process_toggle_account_state(
-    program_id: &Pubkey,
-    accounts: &[AccountInfo],
-    freeze: bool,
-) -> ProgramResult {
+pub fn process_toggle_account_state(accounts: &[AccountInfo], freeze: bool) -> ProgramResult {
     let [source_account_info, mint_info, authority_info, remaining @ ..] = accounts else {
         return Err(ProgramError::NotEnoughAccountKeys);
     };
 
-    let source_account = bytemuck::try_from_bytes_mut::<Account>(unsafe {
-        source_account_info.borrow_mut_data_unchecked()
-    })
-    .map_err(|_error| ProgramError::InvalidAccountData)?;
+    let source_account =
+        unsafe { Account::from_bytes_mut(source_account_info.borrow_mut_data_unchecked()) };
 
     if freeze && source_account.is_frozen() || !freeze && !source_account.is_frozen() {
         return Err(TokenError::InvalidState.into());
     }
-    if source_account.is_native.is_some() {
+    if source_account.is_native() {
         return Err(TokenError::NativeNotSupported.into());
     }
     if mint_info.key() != &source_account.mint {
         return Err(TokenError::MintMismatch.into());
     }
 
-    let mint = bytemuck::try_from_bytes::<Mint>(unsafe { mint_info.borrow_data_unchecked() })
-        .map_err(|_error| ProgramError::InvalidAccountData)?;
+    let mint = unsafe { Mint::from_bytes(mint_info.borrow_data_unchecked()) };
 
-    match mint.freeze_authority.as_ref() {
-        Option::Some(authority) => validate_owner(program_id, authority, authority_info, remaining),
-        Option::None => Err(TokenError::MintCannotFreeze.into()),
+    match mint.freeze_authority() {
+        Some(authority) => validate_owner(authority, authority_info, remaining),
+        None => Err(TokenError::MintCannotFreeze.into()),
     }?;
 
     source_account.state = if freeze {
         AccountState::Frozen
     } else {
         AccountState::Initialized
-    } as u8;
+    };
 
     Ok(())
 }

+ 34 - 48
p-token/src/processor/shared/transfer.rs

@@ -1,22 +1,18 @@
-use pinocchio::{
-    account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey, ProgramResult,
-};
+use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramResult};
 use token_interface::{
     error::TokenError,
-    native_mint::is_native_mint,
-    state::{account::Account, mint::Mint, PodCOption},
+    state::{account::Account, mint::Mint},
 };
 
 use crate::processor::{check_account_owner, validate_owner};
 
 #[inline(always)]
 pub fn process_transfer(
-    program_id: &Pubkey,
     accounts: &[AccountInfo],
     amount: u64,
     expected_decimals: Option<u8>,
 ) -> ProgramResult {
-    // Accounts expected depends on whether we have the mint `decimals` or not; when we have the
+    // Accounts expected depend on whether we have the mint `decimals` or not; when we have the
     // mint `decimals`, we expect the mint account to be present.
 
     let (
@@ -55,24 +51,21 @@ pub fn process_transfer(
 
     // Validates source and destination accounts.
 
-    let source_account = bytemuck::try_from_bytes_mut::<Account>(unsafe {
-        source_account_info.borrow_mut_data_unchecked()
-    })
-    .map_err(|_error| ProgramError::InvalidAccountData)?;
+    let source_account =
+        unsafe { Account::from_bytes_mut(source_account_info.borrow_mut_data_unchecked()) };
 
-    let destination_account = bytemuck::try_from_bytes_mut::<Account>(unsafe {
-        destination_account_info.borrow_mut_data_unchecked()
-    })
-    .map_err(|_error| ProgramError::InvalidAccountData)?;
+    let destination_account =
+        unsafe { Account::from_bytes_mut(destination_account_info.borrow_mut_data_unchecked()) };
 
     if source_account.is_frozen() || destination_account.is_frozen() {
         return Err(TokenError::AccountFrozen.into());
     }
 
-    // FEBO: Implicitly validates that the account has enough tokens by calculating the
-    // remaining amount. The amount is only updated on the account if the transfer
+    // Implicitly validates that the account has enough tokens by calculating the
+    // remaining amount - the amount is only updated on the account if the transfer
     // is successful.
-    let remaining_amount = u64::from(source_account.amount)
+    let remaining_amount = source_account
+        .amount()
         .checked_sub(amount)
         .ok_or(TokenError::InsufficientFunds)?;
 
@@ -87,72 +80,65 @@ pub fn process_transfer(
             return Err(TokenError::MintMismatch.into());
         }
 
-        let mint =
-            bytemuck::try_from_bytes_mut::<Mint>(unsafe { mint_info.borrow_mut_data_unchecked() })
-                .map_err(|_error| ProgramError::InvalidAccountData)?;
+        let mint = unsafe { Mint::from_bytes(mint_info.borrow_data_unchecked()) };
 
         if decimals != mint.decimals {
             return Err(TokenError::MintDecimalsMismatch.into());
         }
     }
 
-    let self_transfer = source_account_info.key() == destination_account_info.key();
+    // Comparing whether the `AccountInfo`s "point" to the same acount - this
+    // is a faster comparison since it just checks the internal raw pointer.
+    let self_transfer = source_account_info == destination_account_info;
 
     // Validates the authority (delegate or owner).
 
-    if source_account.delegate.as_ref() == Some(authority_info.key()) {
-        validate_owner(program_id, authority_info.key(), authority_info, remaning)?;
+    if source_account.delegate() == Some(authority_info.key()) {
+        validate_owner(authority_info.key(), authority_info, remaning)?;
 
-        let delegated_amount = u64::from(source_account.delegated_amount)
+        let delegated_amount = source_account
+            .delegated_amount()
             .checked_sub(amount)
             .ok_or(TokenError::InsufficientFunds)?;
 
         if !self_transfer {
-            source_account.delegated_amount = delegated_amount.into();
+            source_account.set_delegated_amount(delegated_amount);
 
             if delegated_amount == 0 {
-                source_account.delegate = PodCOption::from(None);
+                source_account.clear_delegate();
             }
         }
     } else {
-        validate_owner(program_id, &source_account.owner, authority_info, remaning)?;
+        validate_owner(&source_account.owner, authority_info, remaning)?;
     }
 
     if self_transfer || amount == 0 {
-        check_account_owner(program_id, source_account_info)?;
-        check_account_owner(program_id, destination_account_info)?;
-
-        // No need to move tokens around.
-        return Ok(());
-    }
+        // Validates the token accounts owner since we are not writing
+        // to these account.
+        check_account_owner(source_account_info)?;
+        check_account_owner(destination_account_info)?;
 
-    // FEBO: This was moved to the if statement above since we can skip the amount
-    // manipulation if it is a self-transfer or the amount is zero.
-    //
-    // This check MUST occur just before the amounts are manipulated
-    // to ensure self-transfers are fully validated
-    /*
-    if self_transfer {
         return Ok(());
     }
-    */
 
     // Moves the tokens.
 
-    source_account.amount = remaining_amount.into();
+    source_account.set_amount(remaining_amount);
 
-    let destination_amount = u64::from(destination_account.amount)
+    let destination_amount = destination_account
+        .amount()
         .checked_add(amount)
         .ok_or(TokenError::Overflow)?;
-    destination_account.amount = destination_amount.into();
+    destination_account.set_amount(destination_amount);
 
-    if is_native_mint(&source_account.mint) {
-        let mut source_lamports = source_account_info.try_borrow_mut_lamports()?;
+    if source_account.is_native() {
+        let source_lamports = unsafe { source_account_info.borrow_mut_lamports_unchecked() };
         *source_lamports = source_lamports
             .checked_sub(amount)
             .ok_or(TokenError::Overflow)?;
 
-        let mut destination_lamports = destination_account_info.try_borrow_mut_lamports()?;
+        let destination_lamports =
+            unsafe { destination_account_info.borrow_mut_lamports_unchecked() };
         *destination_lamports = destination_lamports
             .checked_add(amount)
             .ok_or(TokenError::Overflow)?;

+ 10 - 14
p-token/src/processor/sync_native.rs

@@ -1,31 +1,27 @@
-use pinocchio::{
-    account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey, ProgramResult,
-};
+use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramResult};
 use token_interface::{error::TokenError, state::account::Account};
 
 use super::check_account_owner;
 
-#[inline(never)]
-pub fn process_sync_native(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
+#[inline(always)]
+pub fn process_sync_native(accounts: &[AccountInfo]) -> ProgramResult {
     let native_account_info = accounts.first().ok_or(ProgramError::NotEnoughAccountKeys)?;
 
-    check_account_owner(program_id, native_account_info)?;
+    check_account_owner(native_account_info)?;
 
-    let native_account = bytemuck::try_from_bytes_mut::<Account>(unsafe {
-        native_account_info.borrow_mut_data_unchecked()
-    })
-    .map_err(|_error| ProgramError::InvalidAccountData)?;
+    let native_account =
+        unsafe { Account::from_bytes_mut(native_account_info.borrow_mut_data_unchecked()) };
 
-    if let Option::Some(rent_exempt_reserve) = native_account.is_native.get() {
+    if let Option::Some(rent_exempt_reserve) = native_account.native_amount() {
         let new_amount = native_account_info
             .lamports()
-            .checked_sub(u64::from(rent_exempt_reserve))
+            .checked_sub(rent_exempt_reserve)
             .ok_or(TokenError::Overflow)?;
 
-        if new_amount < native_account.amount.into() {
+        if new_amount < native_account.amount() {
             return Err(TokenError::InvalidState.into());
         }
-        native_account.amount = new_amount.into();
+        native_account.set_amount(new_amount);
     } else {
         return Err(TokenError::NonNativeNotSupported.into());
     }

+ 4 - 4
p-token/src/processor/thaw_account.rs

@@ -1,8 +1,8 @@
-use pinocchio::{account_info::AccountInfo, pubkey::Pubkey, ProgramResult};
+use pinocchio::{account_info::AccountInfo, ProgramResult};
 
 use super::shared::toggle_account_state::process_toggle_account_state;
 
-#[inline(never)]
-pub fn process_thaw_account(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
-    process_toggle_account_state(program_id, accounts, false)
+#[inline(always)]
+pub fn process_thaw_account(accounts: &[AccountInfo]) -> ProgramResult {
+    process_toggle_account_state(accounts, false)
 }

+ 10 - 8
p-token/src/processor/transfer.rs

@@ -1,12 +1,14 @@
-use pinocchio::{account_info::AccountInfo, pubkey::Pubkey, ProgramResult};
+use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramResult};
 
 use super::shared;
 
-#[inline(never)]
-pub fn process_transfer(
-    program_id: &Pubkey,
-    accounts: &[AccountInfo],
-    amount: u64,
-) -> ProgramResult {
-    shared::transfer::process_transfer(program_id, accounts, amount, None)
+#[inline(always)]
+pub fn process_transfer(accounts: &[AccountInfo], instruction_data: &[u8]) -> ProgramResult {
+    let amount = u64::from_le_bytes(
+        instruction_data
+            .try_into()
+            .map_err(|_error| ProgramError::InvalidInstructionData)?,
+    );
+
+    shared::transfer::process_transfer(accounts, amount, None)
 }

+ 19 - 40
p-token/src/processor/transfer_checked.rs

@@ -1,47 +1,26 @@
-use std::marker::PhantomData;
-
-use pinocchio::{
-    account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey, ProgramResult,
-};
+use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramResult};
 
 use super::shared;
 
-#[inline(never)]
+#[inline(always)]
 pub fn process_transfer_checked(
-    program_id: &Pubkey,
     accounts: &[AccountInfo],
-    amount: u64,
-    decimals: u8,
+    instruction_data: &[u8],
 ) -> ProgramResult {
-    shared::transfer::process_transfer(program_id, accounts, amount, Some(decimals))
-}
-
-pub struct TransferChecked<'a> {
-    raw: *const u8,
-
-    _data: PhantomData<&'a [u8]>,
-}
-
-impl TransferChecked<'_> {
-    pub fn try_from_bytes(bytes: &[u8]) -> Result<TransferChecked, ProgramError> {
-        if bytes.len() != 9 {
-            return Err(ProgramError::InvalidInstructionData);
-        }
-
-        Ok(TransferChecked {
-            raw: bytes.as_ptr(),
-            _data: PhantomData,
-        })
-    }
-
-    pub fn amount(&self) -> u64 {
-        unsafe {
-            let amount = self.raw as *const u64;
-            amount.read_unaligned()
-        }
-    }
-
-    pub fn decimals(&self) -> u8 {
-        unsafe { *self.raw.add(8) }
-    }
+    let (amount, decimals) = instruction_data.split_at(core::mem::size_of::<u64>());
+    let amount = u64::from_le_bytes(
+        amount
+            .try_into()
+            .map_err(|_error| ProgramError::InvalidInstructionData)?,
+    );
+
+    shared::transfer::process_transfer(
+        accounts,
+        amount,
+        Some(
+            *decimals
+                .first()
+                .ok_or(ProgramError::InvalidInstructionData)?,
+        ),
+    )
 }

+ 9 - 10
p-token/src/processor/ui_amount_to_amount.rs

@@ -1,23 +1,22 @@
 use pinocchio::{
-    account_info::AccountInfo, program::set_return_data, program_error::ProgramError,
-    pubkey::Pubkey, ProgramResult,
+    account_info::AccountInfo, program::set_return_data, program_error::ProgramError, ProgramResult,
 };
-use token_interface::{error::TokenError, state::mint::Mint};
+use token_interface::state::mint::Mint;
 
 use super::{check_account_owner, try_ui_amount_into_amount};
 
-#[inline(never)]
+#[inline(always)]
 pub fn process_ui_amount_to_amount(
-    program_id: &Pubkey,
     accounts: &[AccountInfo],
-    ui_amount: &str,
+    instruction_data: &[u8],
 ) -> ProgramResult {
+    let ui_amount = core::str::from_utf8(instruction_data)
+        .map_err(|_error| ProgramError::InvalidInstructionData)?;
+
     let mint_info = accounts.first().ok_or(ProgramError::NotEnoughAccountKeys)?;
-    check_account_owner(program_id, mint_info)?;
+    check_account_owner(mint_info)?;
 
-    let mint =
-        bytemuck::try_from_bytes_mut::<Mint>(unsafe { mint_info.borrow_mut_data_unchecked() })
-            .map_err(|_error| TokenError::InvalidMint)?;
+    let mint = unsafe { Mint::from_bytes(mint_info.borrow_data_unchecked()) };
 
     let amount = try_ui_amount_into_amount(ui_amount.to_string(), mint.decimals)?;
     set_return_data(&amount.to_le_bytes());