Browse Source

rafactor: Add trait types (#3)

* Add trait types

* Use load functions

* Fix decimals value
Fernando Otero 10 months ago
parent
commit
4ef1daa418

+ 35 - 84
interface/src/state/account.rs

@@ -1,12 +1,13 @@
-use pinocchio::{
-    account_info::{AccountInfo, Ref},
-    program_error::ProgramError,
-    pubkey::Pubkey,
-};
+use pinocchio::pubkey::Pubkey;
 
-use crate::program::ID;
+use super::{account_state::AccountState, COption, Initializable, RawType};
 
-use super::{account_state::AccountState, COption};
+/// Incinerator address.
+const INCINERATOR_ID: Pubkey =
+    pinocchio_pubkey::pubkey!("1nc1nerator11111111111111111111111111111111");
+
+/// System program id.
+const SYSTEM_PROGRAM_ID: Pubkey = pinocchio_pubkey::pubkey!("11111111111111111111111111111111");
 
 /// Internal representation of a token account data.
 #[repr(C)]
@@ -44,89 +45,28 @@ pub struct Account {
 }
 
 impl Account {
-    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 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 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]
+    #[inline(always)]
     pub fn amount(&self) -> u64 {
         u64::from_le_bytes(self.amount)
     }
 
-    #[inline]
+    #[inline(always)]
     pub fn clear_delegate(&mut self) {
         self.delegate.0[0] = 0;
     }
 
-    #[inline]
+    #[inline(always)]
     pub fn set_delegate(&mut self, delegate: &Pubkey) {
         self.delegate.0[0] = 1;
         self.delegate.1 = *delegate;
     }
 
-    #[inline]
+    #[inline(always)]
     pub fn delegate(&self) -> Option<&Pubkey> {
         if self.delegate.0[0] == 1 {
             Some(&self.delegate.1)
@@ -135,17 +75,17 @@ impl Account {
         }
     }
 
-    #[inline]
+    #[inline(always)]
     pub fn set_native(&mut self, value: bool) {
         self.is_native[0] = value as u8;
     }
 
-    #[inline]
+    #[inline(always)]
     pub fn is_native(&self) -> bool {
         self.is_native[0] == 1
     }
 
-    #[inline]
+    #[inline(always)]
     pub fn native_amount(&self) -> Option<u64> {
         if self.is_native() {
             Some(u64::from_le_bytes(self.native_amount))
@@ -154,28 +94,28 @@ impl Account {
         }
     }
 
-    #[inline]
+    #[inline(always)]
     pub fn set_delegated_amount(&mut self, amount: u64) {
         self.delegated_amount = amount.to_le_bytes();
     }
 
-    #[inline]
+    #[inline(always)]
     pub fn delegated_amount(&self) -> u64 {
         u64::from_le_bytes(self.delegated_amount)
     }
 
-    #[inline]
+    #[inline(always)]
     pub fn clear_close_authority(&mut self) {
         self.close_authority.0[0] = 0;
     }
 
-    #[inline]
+    #[inline(always)]
     pub fn set_close_authority(&mut self, value: &Pubkey) {
         self.close_authority.0[0] = 1;
         self.close_authority.1 = *value;
     }
 
-    #[inline]
+    #[inline(always)]
     pub fn close_authority(&self) -> Option<&Pubkey> {
         if self.close_authority.0[0] == 1 {
             Some(&self.close_authority.1)
@@ -185,12 +125,23 @@ impl Account {
     }
 
     #[inline(always)]
-    pub fn is_initialized(&self) -> bool {
-        self.state != AccountState::Uninitialized
+    pub fn is_frozen(&self) -> bool {
+        self.state == AccountState::Frozen
     }
 
     #[inline(always)]
-    pub fn is_frozen(&self) -> bool {
-        self.state == AccountState::Frozen
+    pub fn is_owned_by_system_program_or_incinerator(&self) -> bool {
+        SYSTEM_PROGRAM_ID == self.owner || INCINERATOR_ID == self.owner
+    }
+}
+
+impl RawType for Account {
+    const LEN: usize = core::mem::size_of::<Account>();
+}
+
+impl Initializable for Account {
+    #[inline(always)]
+    fn is_initialized(&self) -> bool {
+        self.state != AccountState::Uninitialized
     }
 }

+ 23 - 84
interface/src/state/mint.rs

@@ -1,12 +1,6 @@
-use pinocchio::{
-    account_info::{AccountInfo, Ref},
-    program_error::ProgramError,
-    pubkey::Pubkey,
-};
+use pinocchio::pubkey::Pubkey;
 
-use crate::program::ID;
-
-use super::COption;
+use super::{COption, Initializable, RawType};
 
 /// Internal representation of a mint data.
 #[repr(C)]
@@ -33,100 +27,33 @@ pub struct Mint {
 }
 
 impl Mint {
-    /// 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]
+    #[inline(always)]
     pub fn set_supply(&mut self, supply: u64) {
         self.supply = supply.to_le_bytes();
     }
 
-    #[inline]
+    #[inline(always)]
     pub fn supply(&self) -> u64 {
         u64::from_le_bytes(self.supply)
     }
 
-    #[inline]
+    #[inline(always)]
     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]
+    #[inline(always)]
     pub fn clear_mint_authority(&mut self) {
         self.mint_authority.0[0] = 0;
     }
 
-    #[inline]
+    #[inline(always)]
     pub fn set_mint_authority(&mut self, mint_authority: &Pubkey) {
         self.mint_authority.0[0] = 1;
         self.mint_authority.1 = *mint_authority;
     }
 
-    #[inline]
+    #[inline(always)]
     pub fn mint_authority(&self) -> Option<&Pubkey> {
         if self.mint_authority.0[0] == 1 {
             Some(&self.mint_authority.1)
@@ -135,18 +62,18 @@ impl Mint {
         }
     }
 
-    #[inline]
+    #[inline(always)]
     pub fn clear_freeze_authority(&mut self) {
         self.freeze_authority.0[0] = 0;
     }
 
-    #[inline]
+    #[inline(always)]
     pub fn set_freeze_authority(&mut self, freeze_authority: &Pubkey) {
         self.freeze_authority.0[0] = 1;
         self.freeze_authority.1 = *freeze_authority;
     }
 
-    #[inline]
+    #[inline(always)]
     pub fn freeze_authority(&self) -> Option<&Pubkey> {
         if self.freeze_authority.0[0] == 1 {
             Some(&self.freeze_authority.1)
@@ -155,3 +82,15 @@ impl Mint {
         }
     }
 }
+
+impl RawType for Mint {
+    /// The length of the `Mint` account data.
+    const LEN: usize = core::mem::size_of::<Mint>();
+}
+
+impl Initializable for Mint {
+    #[inline(always)]
+    fn is_initialized(&self) -> bool {
+        self.is_initialized == 1
+    }
+}

+ 85 - 0
interface/src/state/mod.rs

@@ -1,3 +1,5 @@
+use pinocchio::program_error::ProgramError;
+
 pub mod account;
 pub mod account_state;
 pub mod mint;
@@ -5,3 +7,86 @@ pub mod multisig;
 
 /// Type alias for fields represented as `COption`.
 pub type COption<T> = ([u8; 4], T);
+
+/// Marker trait for types that can cast from a raw pointer.
+///
+/// It is up to the type implementing this trait to guarantee that the cast is safe,
+/// i.e., that the fields of the type are well aligned and there are no padding bytes.
+pub trait RawType {
+    /// The length of the type.
+    ///
+    /// This must be equal to the size of each individual field in the type.
+    const LEN: usize;
+}
+
+/// Trait to represent a type that can be initialized.
+pub trait Initializable {
+    /// Return `true` if the object is initialized.
+    fn is_initialized(&self) -> bool;
+}
+
+/// Return a reference for an initialized `T` from the given bytes.
+///
+/// # Safety
+///
+/// The caller must ensure that `bytes` contains a valid representation of `T`.
+#[inline(always)]
+pub unsafe fn load<T: Initializable + RawType>(bytes: &[u8]) -> Result<&T, ProgramError> {
+    load_unchecked(bytes).and_then(|t: &T| {
+        // checks if the data is initialized
+        if t.is_initialized() {
+            Ok(t)
+        } else {
+            Err(ProgramError::UninitializedAccount)
+        }
+    })
+}
+
+/// Return a `T` reference from the given bytes.
+///
+/// This function does not check if the data is initialized.
+///
+/// # Safety
+///
+/// The caller must ensure that `bytes` contains a valid representation of `T`.
+#[inline(always)]
+pub unsafe fn load_unchecked<T: RawType>(bytes: &[u8]) -> Result<&T, ProgramError> {
+    if bytes.len() != T::LEN {
+        return Err(ProgramError::InvalidAccountData);
+    }
+    Ok(&*(bytes.as_ptr() as *const T))
+}
+
+/// Return a mutable reference for an initialized `T` from the given bytes.
+///
+/// # Safety
+///
+/// The caller must ensure that `bytes` contains a valid representation of `T`.
+#[inline(always)]
+pub unsafe fn load_mut<T: Initializable + RawType>(
+    bytes: &mut [u8],
+) -> Result<&mut T, ProgramError> {
+    load_mut_unchecked(bytes).and_then(|t: &mut T| {
+        // checks if the data is initialized
+        if t.is_initialized() {
+            Ok(t)
+        } else {
+            Err(ProgramError::UninitializedAccount)
+        }
+    })
+}
+
+/// Return a mutable `T` reference from the given bytes.
+///
+/// This function does not check if the data is initialized.
+///
+/// # Safety
+///
+/// The caller must ensure that `bytes` contains a valid representation of `T`.
+#[inline(always)]
+pub unsafe fn load_mut_unchecked<T: RawType>(bytes: &mut [u8]) -> Result<&mut T, ProgramError> {
+    if bytes.len() != T::LEN {
+        return Err(ProgramError::InvalidAccountData);
+    }
+    Ok(&mut *(bytes.as_mut_ptr() as *mut T))
+}

+ 11 - 70
interface/src/state/multisig.rs

@@ -1,10 +1,6 @@
-use pinocchio::{
-    account_info::{AccountInfo, Ref},
-    program_error::ProgramError,
-    pubkey::Pubkey,
-};
+use pinocchio::pubkey::Pubkey;
 
-use crate::program::ID;
+use super::{Initializable, RawType};
 
 /// Minimum number of multisignature signers (min N)
 pub const MIN_SIGNERS: usize = 1;
@@ -29,68 +25,6 @@ pub struct Multisig {
 }
 
 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)
@@ -100,9 +34,16 @@ impl Multisig {
     pub fn set_initialized(&mut self, value: bool) {
         self.is_initialized = value as u8;
     }
+}
 
-    #[inline]
-    pub fn is_initialized(&self) -> bool {
+impl RawType for Multisig {
+    /// The length of the `Mint` account data.
+    const LEN: usize = core::mem::size_of::<Multisig>();
+}
+
+impl Initializable for Multisig {
+    #[inline(always)]
+    fn is_initialized(&self) -> bool {
         self.is_initialized == 1
     }
 }

+ 7 - 2
p-token/src/processor/amount_to_ui_amount.rs

@@ -3,7 +3,10 @@ use pinocchio::{
     account_info::AccountInfo, program::set_return_data, program_error::ProgramError, ProgramResult,
 };
 use pinocchio_log::logger::{Argument, Logger};
-use token_interface::state::mint::Mint;
+use token_interface::{
+    error::TokenError,
+    state::{load, mint::Mint},
+};
 
 use super::{check_account_owner, MAX_DIGITS_U64};
 
@@ -27,7 +30,9 @@ pub fn process_amount_to_ui_amount(
     let mint_info = accounts.first().ok_or(ProgramError::NotEnoughAccountKeys)?;
     check_account_owner(mint_info)?;
     // SAFETY: there is a single borrow to the `Mint` account.
-    let mint = unsafe { Mint::from_bytes(mint_info.borrow_data_unchecked()) };
+    let mint = unsafe {
+        load::<Mint>(mint_info.borrow_data_unchecked()).map_err(|_| TokenError::InvalidMint)?
+    };
 
     let mut logger = Logger::<MAX_UI_AMOUNT_LENGTH>::default();
     logger.append_with_args(amount, &[Argument::Precision(mint.decimals)]);

+ 18 - 6
p-token/src/processor/close_account.rs

@@ -1,7 +1,16 @@
-use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramResult};
-use token_interface::{error::TokenError, state::account::Account};
+use pinocchio::{
+    account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey, ProgramResult,
+};
+use token_interface::{
+    error::TokenError,
+    state::{account::Account, load_mut},
+};
 
-use super::{is_owned_by_system_program_or_incinerator, validate_owner, INCINERATOR_ID};
+use super::validate_owner;
+
+/// Incinerator address.
+const INCINERATOR_ID: Pubkey =
+    pinocchio_pubkey::pubkey!("1nc1nerator11111111111111111111111111111111");
 
 #[inline(always)]
 pub fn process_close_account(accounts: &[AccountInfo]) -> ProgramResult {
@@ -10,12 +19,15 @@ pub fn process_close_account(accounts: &[AccountInfo]) -> ProgramResult {
         return Err(ProgramError::NotEnoughAccountKeys);
     };
 
-    if source_account_info.key() == destination_account_info.key() {
+    // Comparing whether the AccountInfo's "point" to the same account or
+    // not - this is a faster comparison since it just checks the internal
+    // raw pointer.
+    if source_account_info == destination_account_info {
         return Err(ProgramError::InvalidAccountData);
     }
 
     let source_account =
-        unsafe { Account::from_bytes_mut(source_account_info.borrow_mut_data_unchecked()) };
+        unsafe { load_mut::<Account>(source_account_info.borrow_mut_data_unchecked())? };
 
     if !source_account.is_native() && source_account.amount() != 0 {
         return Err(TokenError::NonNativeHasBalance.into());
@@ -25,7 +37,7 @@ pub fn process_close_account(accounts: &[AccountInfo]) -> ProgramResult {
         .close_authority()
         .unwrap_or(&source_account.owner);
 
-    if !is_owned_by_system_program_or_incinerator(source_account_info.owner()) {
+    if !source_account.is_owned_by_system_program_or_incinerator() {
         validate_owner(authority, authority_info, remaining)?;
     } else if destination_account_info.key() != &INCINERATOR_ID {
         return Err(ProgramError::InvalidAccountData);

+ 7 - 2
p-token/src/processor/get_account_data_size.rs

@@ -1,7 +1,10 @@
 use pinocchio::{
     account_info::AccountInfo, program::set_return_data, program_error::ProgramError, ProgramResult,
 };
-use token_interface::state::{account::Account, mint::Mint};
+use token_interface::{
+    error::TokenError,
+    state::{account::Account, load, mint::Mint, RawType},
+};
 
 use super::check_account_owner;
 
@@ -14,7 +17,9 @@ pub fn process_get_account_data_size(accounts: &[AccountInfo]) -> ProgramResult
     // Make sure the mint is valid.
     check_account_owner(mint_info)?;
 
-    let _ = unsafe { Mint::from_bytes(mint_info.borrow_data_unchecked()) };
+    let _ = unsafe {
+        load::<Mint>(mint_info.borrow_data_unchecked()).map_err(|_| TokenError::InvalidMint)
+    };
 
     set_return_data(&Account::LEN.to_le_bytes());
 

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

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

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

@@ -6,7 +6,10 @@ use pinocchio::{
     sysvars::{rent::Rent, Sysvar},
     ProgramResult,
 };
-use token_interface::{error::TokenError, state::mint::Mint};
+use token_interface::{
+    error::TokenError,
+    state::{load_mut_unchecked, mint::Mint, Initializable},
+};
 
 #[inline(always)]
 pub fn process_initialize_mint(
@@ -32,7 +35,7 @@ pub fn process_initialize_mint(
         (mint_info, None)
     };
 
-    let mint = unsafe { Mint::from_bytes_mut(mint_info.borrow_mut_data_unchecked()) };
+    let mint = unsafe { load_mut_unchecked::<Mint>(mint_info.borrow_mut_data_unchecked())? };
 
     if mint.is_initialized() {
         return Err(TokenError::AlreadyInUse.into());

+ 6 - 14
p-token/src/processor/mod.rs

@@ -10,7 +10,11 @@ use pinocchio::{
 };
 use token_interface::{
     error::TokenError,
-    state::multisig::{Multisig, MAX_SIGNERS},
+    state::{
+        load,
+        multisig::{Multisig, MAX_SIGNERS},
+        RawType,
+    },
 };
 
 pub mod amount_to_ui_amount;
@@ -67,24 +71,12 @@ 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");
-
-/// System program id.
-const SYSTEM_PROGRAM_ID: Pubkey = pinocchio_pubkey::pubkey!("11111111111111111111111111111111");
-
 /// An uninitialized byte.
 const UNINIT_BYTE: MaybeUninit<u8> = MaybeUninit::uninit();
 
 /// Maximum number of digits in a `u64``.
 const MAX_DIGITS_U64: usize = 20;
 
-#[inline(always)]
-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)]
 fn check_account_owner(account_info: &AccountInfo) -> ProgramResult {
@@ -107,7 +99,7 @@ fn validate_owner(
     }
 
     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 multisig = unsafe { load::<Multisig>(owner_account_info.borrow_data_unchecked())? };
 
         let mut num_signers = 0;
         let mut matched = [false; MAX_SIGNERS];

+ 5 - 2
p-token/src/processor/revoke.rs

@@ -1,5 +1,8 @@
 use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramResult};
-use token_interface::{error::TokenError, state::account::Account};
+use token_interface::{
+    error::TokenError,
+    state::{account::Account, load_mut},
+};
 
 use super::validate_owner;
 
@@ -10,7 +13,7 @@ pub fn process_revoke(accounts: &[AccountInfo], _instruction_data: &[u8]) -> Pro
     };
 
     let source_account =
-        unsafe { Account::from_bytes_mut(source_account_info.borrow_mut_data_unchecked()) };
+        unsafe { load_mut::<Account>(source_account_info.borrow_mut_data_unchecked())? };
 
     if source_account.is_frozen() {
         return Err(TokenError::AccountFrozen.into());

+ 3 - 3
p-token/src/processor/set_authority.rs

@@ -6,7 +6,7 @@ use pinocchio::{
 use token_interface::{
     error::TokenError,
     instruction::AuthorityType,
-    state::{account::Account, mint::Mint},
+    state::{account::Account, load_mut, mint::Mint, RawType},
 };
 
 use super::validate_owner;
@@ -27,7 +27,7 @@ pub fn process_set_authority(accounts: &[AccountInfo], instruction_data: &[u8])
     };
 
     if account_info.data_len() == Account::LEN {
-        let account = unsafe { Account::from_bytes_mut(account_info.borrow_mut_data_unchecked()) };
+        let account = unsafe { load_mut::<Account>(account_info.borrow_mut_data_unchecked())? };
 
         if account.is_frozen() {
             return Err(TokenError::AccountFrozen.into());
@@ -65,7 +65,7 @@ pub fn process_set_authority(accounts: &[AccountInfo], instruction_data: &[u8])
             }
         }
     } else if account_info.data_len() == Mint::LEN {
-        let mint = unsafe { Mint::from_bytes_mut(account_info.borrow_mut_data_unchecked()) };
+        let mint = unsafe { load_mut::<Mint>(account_info.borrow_mut_data_unchecked())? };
 
         match authority_type {
             AuthorityType::MintTokens => {

+ 3 - 3
p-token/src/processor/shared/approve.rs

@@ -1,7 +1,7 @@
 use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramResult};
 use token_interface::{
     error::TokenError,
-    state::{account::Account, mint::Mint},
+    state::{account::Account, load, load_mut, mint::Mint},
 };
 
 use crate::processor::validate_owner;
@@ -46,7 +46,7 @@ pub fn process_approve(
     // Validates source account.
 
     let source_account =
-        unsafe { Account::from_bytes_mut(source_account_info.borrow_mut_data_unchecked()) };
+        unsafe { load_mut::<Account>(source_account_info.borrow_mut_data_unchecked())? };
 
     if source_account.is_frozen() {
         return Err(TokenError::AccountFrozen.into());
@@ -57,7 +57,7 @@ pub fn process_approve(
             return Err(TokenError::MintMismatch.into());
         }
 
-        let mint = unsafe { Mint::from_bytes(mint_info.borrow_data_unchecked()) };
+        let mint = unsafe { load::<Mint>(mint_info.borrow_data_unchecked())? };
 
         if expected_decimals != mint.decimals {
             return Err(TokenError::MintDecimalsMismatch.into());

+ 5 - 7
p-token/src/processor/shared/burn.rs

@@ -1,12 +1,10 @@
 use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramResult};
 use token_interface::{
     error::TokenError,
-    state::{account::Account, mint::Mint},
+    state::{account::Account, load_mut, mint::Mint},
 };
 
-use crate::processor::{
-    check_account_owner, is_owned_by_system_program_or_incinerator, validate_owner,
-};
+use crate::processor::{check_account_owner, validate_owner};
 
 #[inline(always)]
 pub fn process_burn(
@@ -19,7 +17,7 @@ pub fn process_burn(
     };
 
     let source_account =
-        unsafe { Account::from_bytes_mut(source_account_info.borrow_mut_data_unchecked()) };
+        unsafe { load_mut::<Account>(source_account_info.borrow_mut_data_unchecked())? };
 
     if source_account.is_frozen() {
         return Err(TokenError::AccountFrozen.into());
@@ -35,7 +33,7 @@ pub fn process_burn(
         .checked_sub(amount)
         .ok_or(TokenError::InsufficientFunds)?;
 
-    let mint = unsafe { Mint::from_bytes_mut(mint_info.borrow_mut_data_unchecked()) };
+    let mint = unsafe { load_mut::<Mint>(mint_info.borrow_mut_data_unchecked())? };
 
     if mint_info.key() != &source_account.mint {
         return Err(TokenError::MintMismatch.into());
@@ -47,7 +45,7 @@ pub fn process_burn(
         }
     }
 
-    if !is_owned_by_system_program_or_incinerator(&source_account.owner) {
+    if !source_account.is_owned_by_system_program_or_incinerator() {
         match source_account.delegate() {
             Some(delegate) if authority_info.key() == delegate => {
                 validate_owner(delegate, authority_info, remaining)?;

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

@@ -1,4 +1,3 @@
-use core::mem::size_of;
 use pinocchio::{
     account_info::AccountInfo,
     program_error::ProgramError,
@@ -9,7 +8,10 @@ use pinocchio::{
 use token_interface::{
     error::TokenError,
     native_mint::is_native_mint,
-    state::{account::Account, account_state::AccountState, mint::Mint},
+    state::{
+        account::Account, account_state::AccountState, load, load_mut_unchecked, mint::Mint,
+        Initializable,
+    },
 };
 
 use crate::processor::check_account_owner;
@@ -36,36 +38,37 @@ pub fn process_initialize_account(
 
     // Check rent-exempt status of the token account.
 
-    let is_exempt = if rent_sysvar_account {
+    let new_account_info_data_len = new_account_info.data_len();
+
+    let minimum_balance = if rent_sysvar_account {
         let rent_sysvar_info = remaning.first().ok_or(ProgramError::NotEnoughAccountKeys)?;
         let rent = unsafe { Rent::from_bytes(rent_sysvar_info.borrow_data_unchecked()) };
-        rent.is_exempt(new_account_info.lamports(), size_of::<Account>())
+        rent.minimum_balance(new_account_info_data_len)
     } else {
-        Rent::get()?.is_exempt(new_account_info.lamports(), size_of::<Account>())
+        Rent::get()?.minimum_balance(new_account_info_data_len)
     };
 
-    if !is_exempt {
-        return Err(TokenError::NotRentExempt.into());
-    }
+    let is_native_mint = is_native_mint(mint_info.key());
 
     // Initialize the account.
 
-    let account = unsafe { Account::from_bytes_mut(new_account_info.borrow_mut_data_unchecked()) };
+    let account =
+        unsafe { load_mut_unchecked::<Account>(new_account_info.borrow_mut_data_unchecked())? };
 
     if account.is_initialized() {
         return Err(TokenError::AlreadyInUse.into());
     }
 
-    let is_native_mint = is_native_mint(mint_info.key());
+    if new_account_info.lamports() < minimum_balance {
+        return Err(TokenError::NotRentExempt.into());
+    }
 
     if !is_native_mint {
         check_account_owner(mint_info)?;
 
-        let mint = unsafe { Mint::from_bytes(mint_info.borrow_data_unchecked()) };
-
-        if !mint.is_initialized() {
-            return Err(TokenError::InvalidMint.into());
-        }
+        let _ = unsafe {
+            load::<Mint>(mint_info.borrow_data_unchecked()).map_err(|_| TokenError::InvalidMint)?
+        };
     }
 
     account.state = AccountState::Initialized;
@@ -73,15 +76,12 @@ pub fn process_initialize_account(
     account.owner = *owner;
 
     if is_native_mint {
-        let rent = Rent::get()?;
-        let rent_exempt_reserve = rent.minimum_balance(size_of::<Account>());
-
         account.set_native(true);
         unsafe {
             account.set_amount(
                 new_account_info
                     .borrow_lamports_unchecked()
-                    .checked_sub(rent_exempt_reserve)
+                    .checked_sub(minimum_balance)
                     .ok_or(TokenError::Overflow)?,
             );
         }

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

@@ -4,7 +4,10 @@ use pinocchio::{
     sysvars::{rent::Rent, Sysvar},
     ProgramResult,
 };
-use token_interface::{error::TokenError, state::multisig::Multisig};
+use token_interface::{
+    error::TokenError,
+    state::{load_mut_unchecked, multisig::Multisig, Initializable},
+};
 
 #[inline(always)]
 pub fn process_initialize_multisig(
@@ -35,16 +38,17 @@ pub fn process_initialize_multisig(
         Rent::get()?.is_exempt(multisig_info.lamports(), multisig_info_data_len)
     };
 
-    if !is_exempt {
-        return Err(TokenError::NotRentExempt.into());
-    }
-
-    let multisig = unsafe { Multisig::from_bytes_mut(multisig_info.borrow_mut_data_unchecked()) };
+    let multisig =
+        unsafe { load_mut_unchecked::<Multisig>(multisig_info.borrow_mut_data_unchecked())? };
 
     if multisig.is_initialized() {
         return Err(TokenError::AlreadyInUse.into());
     }
 
+    if !is_exempt {
+        return Err(TokenError::NotRentExempt.into());
+    }
+
     // Initialize the multisig account.
 
     multisig.m = m;

+ 15 - 15
p-token/src/processor/shared/mint_to.rs

@@ -1,7 +1,7 @@
 use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramResult};
 use token_interface::{
     error::TokenError,
-    state::{account::Account, mint::Mint},
+    state::{account::Account, load_mut, mint::Mint},
 };
 
 use crate::processor::{check_account_owner, validate_owner};
@@ -19,7 +19,7 @@ pub fn process_mint_to(
     // Validates the destination account.
 
     let destination_account =
-        unsafe { Account::from_bytes_mut(destination_account_info.borrow_mut_data_unchecked()) };
+        unsafe { load_mut::<Account>(destination_account_info.borrow_mut_data_unchecked())? };
 
     if destination_account.is_frozen() {
         return Err(TokenError::AccountFrozen.into());
@@ -33,7 +33,7 @@ pub fn process_mint_to(
         return Err(TokenError::MintMismatch.into());
     }
 
-    let mint = unsafe { Mint::from_bytes_mut(mint_info.borrow_mut_data_unchecked()) };
+    let mint = unsafe { load_mut::<Mint>(mint_info.borrow_mut_data_unchecked())? };
 
     if let Some(expected_decimals) = expected_decimals {
         if expected_decimals != mint.decimals {
@@ -49,19 +49,19 @@ pub fn process_mint_to(
     if amount == 0 {
         check_account_owner(mint_info)?;
         check_account_owner(destination_account_info)?;
-    }
-
-    let destination_amount = destination_account
-        .amount()
-        .checked_add(amount)
-        .ok_or(ProgramError::InvalidAccountData)?;
-    destination_account.set_amount(destination_amount);
+    } else {
+        let destination_amount = destination_account
+            .amount()
+            .checked_add(amount)
+            .ok_or(ProgramError::InvalidAccountData)?;
+        destination_account.set_amount(destination_amount);
 
-    let mint_supply = mint
-        .supply()
-        .checked_add(amount)
-        .ok_or(ProgramError::InvalidAccountData)?;
-    mint.set_supply(mint_supply);
+        let mint_supply = mint
+            .supply()
+            .checked_add(amount)
+            .ok_or(ProgramError::InvalidAccountData)?;
+        mint.set_supply(mint_supply);
+    }
 
     Ok(())
 }

+ 3 - 3
p-token/src/processor/shared/toggle_account_state.rs

@@ -1,7 +1,7 @@
 use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramResult};
 use token_interface::{
     error::TokenError,
-    state::{account::Account, account_state::AccountState, mint::Mint},
+    state::{account::Account, account_state::AccountState, load, load_mut, mint::Mint},
 };
 
 use crate::processor::validate_owner;
@@ -13,7 +13,7 @@ pub fn process_toggle_account_state(accounts: &[AccountInfo], freeze: bool) -> P
     };
 
     let source_account =
-        unsafe { Account::from_bytes_mut(source_account_info.borrow_mut_data_unchecked()) };
+        unsafe { load_mut::<Account>(source_account_info.borrow_mut_data_unchecked())? };
 
     if freeze && source_account.is_frozen() || !freeze && !source_account.is_frozen() {
         return Err(TokenError::InvalidState.into());
@@ -25,7 +25,7 @@ pub fn process_toggle_account_state(accounts: &[AccountInfo], freeze: bool) -> P
         return Err(TokenError::MintMismatch.into());
     }
 
-    let mint = unsafe { Mint::from_bytes(mint_info.borrow_data_unchecked()) };
+    let mint = unsafe { load::<Mint>(mint_info.borrow_data_unchecked())? };
 
     match mint.freeze_authority() {
         Some(authority) => validate_owner(authority, authority_info, remaining),

+ 23 - 25
p-token/src/processor/shared/transfer.rs

@@ -1,7 +1,7 @@
 use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramResult};
 use token_interface::{
     error::TokenError,
-    state::{account::Account, mint::Mint},
+    state::{account::Account, load, load_mut, mint::Mint},
 };
 
 use crate::processor::{check_account_owner, validate_owner};
@@ -52,10 +52,10 @@ pub fn process_transfer(
     // Validates source and destination accounts.
 
     let source_account =
-        unsafe { Account::from_bytes_mut(source_account_info.borrow_mut_data_unchecked()) };
+        unsafe { load_mut::<Account>(source_account_info.borrow_mut_data_unchecked())? };
 
     let destination_account =
-        unsafe { Account::from_bytes_mut(destination_account_info.borrow_mut_data_unchecked()) };
+        unsafe { load_mut::<Account>(destination_account_info.borrow_mut_data_unchecked())? };
 
     if source_account.is_frozen() || destination_account.is_frozen() {
         return Err(TokenError::AccountFrozen.into());
@@ -80,7 +80,7 @@ pub fn process_transfer(
             return Err(TokenError::MintMismatch.into());
         }
 
-        let mint = unsafe { Mint::from_bytes(mint_info.borrow_data_unchecked()) };
+        let mint = unsafe { load::<Mint>(mint_info.borrow_data_unchecked())? };
 
         if decimals != mint.decimals {
             return Err(TokenError::MintDecimalsMismatch.into());
@@ -118,31 +118,29 @@ pub fn process_transfer(
         // to these account.
         check_account_owner(source_account_info)?;
         check_account_owner(destination_account_info)?;
+    } else {
+        // Moves the tokens.
 
-        return Ok(());
-    }
-
-    // Moves the tokens.
-
-    source_account.set_amount(remaining_amount);
-
-    let destination_amount = destination_account
-        .amount()
-        .checked_add(amount)
-        .ok_or(TokenError::Overflow)?;
-    destination_account.set_amount(destination_amount);
-
-    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)?;
+        source_account.set_amount(remaining_amount);
 
-        let destination_lamports =
-            unsafe { destination_account_info.borrow_mut_lamports_unchecked() };
-        *destination_lamports = destination_lamports
+        let destination_amount = destination_account
+            .amount()
             .checked_add(amount)
             .ok_or(TokenError::Overflow)?;
+        destination_account.set_amount(destination_amount);
+
+        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 destination_lamports =
+                unsafe { destination_account_info.borrow_mut_lamports_unchecked() };
+            *destination_lamports = destination_lamports
+                .checked_add(amount)
+                .ok_or(TokenError::Overflow)?;
+        }
     }
 
     Ok(())

+ 5 - 2
p-token/src/processor/sync_native.rs

@@ -1,5 +1,8 @@
 use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramResult};
-use token_interface::{error::TokenError, state::account::Account};
+use token_interface::{
+    error::TokenError,
+    state::{account::Account, load_mut},
+};
 
 use super::check_account_owner;
 
@@ -10,7 +13,7 @@ pub fn process_sync_native(accounts: &[AccountInfo]) -> ProgramResult {
     check_account_owner(native_account_info)?;
 
     let native_account =
-        unsafe { Account::from_bytes_mut(native_account_info.borrow_mut_data_unchecked()) };
+        unsafe { load_mut::<Account>(native_account_info.borrow_mut_data_unchecked())? };
 
     if let Option::Some(rent_exempt_reserve) = native_account.native_amount() {
         let new_amount = native_account_info

+ 2 - 2
p-token/src/processor/ui_amount_to_amount.rs

@@ -2,7 +2,7 @@ use core::str::from_utf8;
 use pinocchio::{
     account_info::AccountInfo, program::set_return_data, program_error::ProgramError, ProgramResult,
 };
-use token_interface::state::mint::Mint;
+use token_interface::state::{load, mint::Mint};
 
 use super::{check_account_owner, try_ui_amount_into_amount};
 
@@ -17,7 +17,7 @@ pub fn process_ui_amount_to_amount(
     let mint_info = accounts.first().ok_or(ProgramError::NotEnoughAccountKeys)?;
     check_account_owner(mint_info)?;
     // SAFETY: there is a single borrow to the `Mint` account.
-    let mint = unsafe { Mint::from_bytes(mint_info.borrow_data_unchecked()) };
+    let mint = unsafe { load::<Mint>(mint_info.borrow_data_unchecked())? };
 
     let amount = try_ui_amount_into_amount(ui_amount, mint.decimals)?;
     set_return_data(&amount.to_le_bytes());

+ 1 - 1
p-token/tests/approve_checked.rs

@@ -63,7 +63,7 @@ async fn approve_checked(token_program: Pubkey) {
         &owner.pubkey(),
         &[],
         50,
-        0,
+        4,
     )
     .unwrap();
     approve_ix.program_id = token_program;

+ 1 - 1
p-token/tests/burn_checked.rs

@@ -60,7 +60,7 @@ async fn burn_checked(token_program: Pubkey) {
         &owner.pubkey(),
         &[],
         50,
-        0,
+        4,
     )
     .unwrap();
     burn_ix.program_id = token_program;

+ 1 - 1
p-token/tests/mint_to_checked.rs

@@ -49,7 +49,7 @@ async fn mint_to_checked(token_program: Pubkey) {
         &mint_authority.pubkey(),
         &[],
         100,
-        0,
+        4,
     )
     .unwrap();
     // Switches the program id to the token program.

+ 1 - 1
p-token/tests/transfer_checked.rs

@@ -66,7 +66,7 @@ async fn transfer_checked(token_program: Pubkey) {
         &owner.pubkey(),
         &[],
         100,
-        0,
+        4,
     )
     .unwrap();
     transfer_ix.program_id = token_program;