From 413b83387d67b3b7d93b2e49ea45ff4c0695c35a Mon Sep 17 00:00:00 2001 From: febo Date: Thu, 21 Nov 2024 21:51:36 +0000 Subject: [PATCH 1/7] Use instruction args --- program/src/entrypoint.rs | 153 +++++------------- program/src/processor/amount_to_ui_amount.rs | 7 +- program/src/processor/approve.rs | 11 +- program/src/processor/approve_checked.rs | 39 +---- program/src/processor/burn.rs | 15 +- program/src/processor/burn_checked.rs | 39 +---- program/src/processor/close_account.rs | 6 +- program/src/processor/freeze_account.rs | 6 +- .../src/processor/get_account_data_size.rs | 1 + program/src/processor/initialize_account.rs | 6 +- program/src/processor/initialize_account2.rs | 3 +- program/src/processor/initialize_account3.rs | 3 +- .../processor/initialize_immutable_owner.rs | 10 +- program/src/processor/initialize_mint.rs | 12 +- program/src/processor/initialize_mint2.rs | 12 +- program/src/processor/initialize_multisig.rs | 64 ++------ program/src/processor/initialize_multisig2.rs | 15 +- program/src/processor/mint_to.rs | 11 +- program/src/processor/mint_to_checked.rs | 39 +---- program/src/processor/revoke.rs | 6 +- program/src/processor/set_authority.rs | 64 ++++---- .../processor/shared/initialize_account.rs | 2 +- .../src/processor/shared/initialize_mint.rs | 6 +- program/src/processor/sync_native.rs | 6 +- program/src/processor/thaw_account.rs | 6 +- program/src/processor/transfer.rs | 11 +- program/src/processor/transfer_checked.rs | 39 +---- program/src/processor/ui_amount_to_amount.rs | 7 +- 28 files changed, 240 insertions(+), 359 deletions(-) diff --git a/program/src/entrypoint.rs b/program/src/entrypoint.rs index 021227c..a46ee23 100644 --- a/program/src/entrypoint.rs +++ b/program/src/entrypoint.rs @@ -1,37 +1,24 @@ -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, + amount_to_ui_amount::process_amount_to_ui_amount, approve::process_approve, + approve_checked::process_approve_checked, burn::process_burn, + burn_checked::process_burn_checked, 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_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, + initialize_multisig2::process_initialize_multisig2, mint_to::process_mint_to, + mint_to_checked::process_mint_to_checked, revoke::process_revoke, + set_authority::process_set_authority, sync_native::process_sync_native, + thaw_account::process_thaw_account, transfer::process_transfer, + transfer_checked::process_transfer_checked, ui_amount_to_amount::process_ui_amount_to_amount, }; entrypoint!(process_instruction); @@ -48,229 +35,175 @@ pub fn process_instruction( #[cfg(feature = "logging")] pinocchio::msg!("Instruction: InitializeMint"); - let instruction = InitializeMint::try_from_bytes(data)?; - - process_initialize_mint(accounts, &instruction) + process_initialize_mint(program_id, accounts, data) } // 1 - InitializeAccount - Some((&1, _)) => { + Some((&1, data)) => { #[cfg(feature = "logging")] pinocchio::msg!("Instruction: InitializeAccount"); - process_initialize_account(program_id, accounts) + process_initialize_account(program_id, accounts, data) } // 2 - InitializeMultisig Some((&2, data)) => { #[cfg(feature = "logging")] pinocchio::msg!("Instruction: InitializeMultisig"); - let m = data.first().ok_or(ProgramError::InvalidInstructionData)?; - - process_initialize_multisig(accounts, *m, true) + process_initialize_multisig(program_id, accounts, data) } // 3 - Transfer Some((&3, data)) => { #[cfg(feature = "logging")] pinocchio::msg!("Instruction: Transfer"); - let amount = u64::from_le_bytes( - data.try_into() - .map_err(|_error| ProgramError::InvalidInstructionData)?, - ); - - process_transfer(program_id, accounts, amount) + process_transfer(program_id, accounts, data) } // 4 - Approve Some((&4, data)) => { #[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(program_id, accounts, data) } // 5 - Revoke - Some((&5, _)) => { + Some((&5, data)) => { #[cfg(feature = "logging")] pinocchio::msg!("Instruction: Revoke"); - process_revoke(program_id, accounts) + process_revoke(program_id, accounts, data) } // 6 - SetAuthority Some((&6, data)) => { #[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, - ) + process_set_authority(program_id, accounts, data) } // 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_mint_to(program_id, accounts, data) } // 8 - Burn Some((&8, data)) => { #[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) + process_burn(program_id, accounts, data) } // 9 - CloseAccount - Some((&9, _)) => { + Some((&9, data)) => { #[cfg(feature = "logging")] pinocchio::msg!("Instruction: CloseAccount"); - process_close_account(program_id, accounts) + process_close_account(program_id, accounts, data) } // 10 - FreezeAccount - Some((&10, _)) => { + Some((&10, data)) => { #[cfg(feature = "logging")] pinocchio::msg!("Instruction: FreezeAccount"); - process_freeze_account(program_id, accounts) + process_freeze_account(program_id, accounts, data) } // 11 - ThawAccount - Some((&11, _)) => { + Some((&11, data)) => { #[cfg(feature = "logging")] pinocchio::msg!("Instruction: ThawAccount"); - process_thaw_account(program_id, accounts) + process_thaw_account(program_id, accounts, data) } // 12 - TransferChecked Some((&12, data)) => { #[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(program_id, accounts, data) } // 13 - ApproveChecked Some((&13, data)) => { #[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(program_id, accounts, data) } // 14 - MintToChecked Some((&14, data)) => { #[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(program_id, accounts, data) } // 15 - BurnChecked Some((&15, data)) => { #[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(program_id, accounts, data) } // 16 - InitializeAccount2 Some((&16, data)) => { #[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(program_id, accounts, data) } // 17 - SyncNative - Some((&17, _)) => { + Some((&17, data)) => { #[cfg(feature = "logging")] pinocchio::msg!("Instruction: SyncNative"); - process_sync_native(program_id, accounts) + process_sync_native(program_id, accounts, data) } // 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_initialize_account3(program_id, accounts, data) } // 19 - InitializeMultisig2 Some((&19, data)) => { #[cfg(feature = "logging")] pinocchio::msg!("Instruction: InitializeMultisig2"); - let m = data.first().ok_or(ProgramError::InvalidInstructionData)?; - - process_initialize_multisig2(accounts, *m) + process_initialize_multisig2(program_id, accounts, data) } // 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_mint2(program_id, accounts, data) } // 21 - GetAccountDataSize - Some((&21, _)) => { + Some((&21, data)) => { #[cfg(feature = "logging")] pinocchio::msg!("Instruction: GetAccountDataSize"); - process_get_account_data_size(program_id, accounts) + process_get_account_data_size(program_id, accounts, data) } // 22 - InitializeImmutableOwner - Some((&22, _)) => { + Some((&22, data)) => { #[cfg(feature = "logging")] pinocchio::msg!("Instruction: InitializeImmutableOwner"); - process_initialize_immutable_owner(accounts) + process_initialize_immutable_owner(program_id, accounts, data) } // 23 - AmountToUiAmount Some((&23, data)) => { #[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(program_id, accounts, data) } // 24 - UiAmountToAmount Some((&24, data)) => { #[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(program_id, accounts, data) } _ => Err(ProgramError::InvalidInstructionData), } diff --git a/program/src/processor/amount_to_ui_amount.rs b/program/src/processor/amount_to_ui_amount.rs index 432c256..f11dd06 100644 --- a/program/src/processor/amount_to_ui_amount.rs +++ b/program/src/processor/amount_to_ui_amount.rs @@ -10,8 +10,13 @@ use super::{amount_to_ui_amount_string_trimmed, check_account_owner}; pub fn process_amount_to_ui_amount( program_id: &Pubkey, accounts: &[AccountInfo], - amount: u64, + instruction_data: &[u8], ) -> ProgramResult { + if instruction_data.len() != 8 { + return Err(ProgramError::InvalidInstructionData); + } + let amount = unsafe { (instruction_data.as_ptr() as *const u64).read_unaligned() }; + let mint_info = accounts.first().ok_or(ProgramError::NotEnoughAccountKeys)?; check_account_owner(program_id, mint_info)?; diff --git a/program/src/processor/approve.rs b/program/src/processor/approve.rs index 2f8029d..d363d11 100644 --- a/program/src/processor/approve.rs +++ b/program/src/processor/approve.rs @@ -1,4 +1,6 @@ -use pinocchio::{account_info::AccountInfo, pubkey::Pubkey, ProgramResult}; +use pinocchio::{ + account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey, ProgramResult, +}; use super::shared; @@ -6,7 +8,12 @@ use super::shared; pub fn process_approve( program_id: &Pubkey, accounts: &[AccountInfo], - amount: u64, + instruction_data: &[u8], ) -> ProgramResult { + if instruction_data.len() != 8 { + return Err(ProgramError::InvalidInstructionData); + } + let amount = unsafe { (instruction_data.as_ptr() as *const u64).read_unaligned() }; + shared::approve::process_approve(program_id, accounts, amount, None) } diff --git a/program/src/processor/approve_checked.rs b/program/src/processor/approve_checked.rs index e871b26..dd95fe0 100644 --- a/program/src/processor/approve_checked.rs +++ b/program/src/processor/approve_checked.rs @@ -1,5 +1,3 @@ -use std::marker::PhantomData; - use pinocchio::{ account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey, ProgramResult, }; @@ -10,38 +8,13 @@ use super::shared; pub fn process_approve_checked( program_id: &Pubkey, accounts: &[AccountInfo], - amount: u64, - decimals: u8, + instruction_data: &[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 { - 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() - } + if instruction_data.len() != 9 { + return Err(ProgramError::InvalidInstructionData); } + let amount = unsafe { (instruction_data.as_ptr() as *const u64).read_unaligned() }; + let decimals = unsafe { *instruction_data.as_ptr().add(8) }; - pub fn decimals(&self) -> u8 { - unsafe { *self.raw.add(8) } - } + shared::approve::process_approve(program_id, accounts, amount, Some(decimals)) } diff --git a/program/src/processor/burn.rs b/program/src/processor/burn.rs index 42614e1..8c420c5 100644 --- a/program/src/processor/burn.rs +++ b/program/src/processor/burn.rs @@ -1,8 +1,19 @@ -use pinocchio::{account_info::AccountInfo, pubkey::Pubkey, ProgramResult}; +use pinocchio::{ + account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey, ProgramResult, +}; use super::shared; #[inline(never)] -pub fn process_burn(program_id: &Pubkey, accounts: &[AccountInfo], amount: u64) -> ProgramResult { +pub fn process_burn( + program_id: &Pubkey, + accounts: &[AccountInfo], + instruction_data: &[u8], +) -> ProgramResult { + if instruction_data.len() != 8 { + return Err(ProgramError::InvalidInstructionData); + } + let amount = unsafe { (instruction_data.as_ptr() as *const u64).read_unaligned() }; + shared::burn::process_burn(program_id, accounts, amount, None) } diff --git a/program/src/processor/burn_checked.rs b/program/src/processor/burn_checked.rs index a32b753..36a479a 100644 --- a/program/src/processor/burn_checked.rs +++ b/program/src/processor/burn_checked.rs @@ -1,5 +1,3 @@ -use std::marker::PhantomData; - use pinocchio::{ account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey, ProgramResult, }; @@ -10,38 +8,13 @@ use super::shared; pub fn process_burn_checked( program_id: &Pubkey, accounts: &[AccountInfo], - amount: u64, - decimals: u8, + instruction_data: &[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 { - 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() - } + if instruction_data.len() != 9 { + return Err(ProgramError::InvalidInstructionData); } + let amount = unsafe { (instruction_data.as_ptr() as *const u64).read_unaligned() }; + let decimals = unsafe { *instruction_data.as_ptr().add(8) }; - pub fn decimals(&self) -> u8 { - unsafe { *self.raw.add(8) } - } + shared::burn::process_burn(program_id, accounts, amount, Some(decimals)) } diff --git a/program/src/processor/close_account.rs b/program/src/processor/close_account.rs index 62850ab..85a0c9e 100644 --- a/program/src/processor/close_account.rs +++ b/program/src/processor/close_account.rs @@ -6,7 +6,11 @@ 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 { +pub fn process_close_account( + program_id: &Pubkey, + accounts: &[AccountInfo], + _instruction_data: &[u8], +) -> ProgramResult { let [source_account_info, destination_account_info, authority_info, remaining @ ..] = accounts else { return Err(ProgramError::NotEnoughAccountKeys); diff --git a/program/src/processor/freeze_account.rs b/program/src/processor/freeze_account.rs index 4348ccf..952c50e 100644 --- a/program/src/processor/freeze_account.rs +++ b/program/src/processor/freeze_account.rs @@ -3,6 +3,10 @@ use pinocchio::{account_info::AccountInfo, pubkey::Pubkey, ProgramResult}; use super::shared::toggle_account_state::process_toggle_account_state; #[inline(never)] -pub fn process_freeze_account(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult { +pub fn process_freeze_account( + program_id: &Pubkey, + accounts: &[AccountInfo], + _instruction_data: &[u8], +) -> ProgramResult { process_toggle_account_state(program_id, accounts, true) } diff --git a/program/src/processor/get_account_data_size.rs b/program/src/processor/get_account_data_size.rs index ff6e3d1..2b89767 100644 --- a/program/src/processor/get_account_data_size.rs +++ b/program/src/processor/get_account_data_size.rs @@ -10,6 +10,7 @@ use super::check_account_owner; pub fn process_get_account_data_size( program_id: &Pubkey, accounts: &[AccountInfo], + _instruction_data: &[u8], ) -> ProgramResult { let [mint_info, _remaning @ ..] = accounts else { return Err(ProgramError::NotEnoughAccountKeys); diff --git a/program/src/processor/initialize_account.rs b/program/src/processor/initialize_account.rs index a046f6a..0c37c7e 100644 --- a/program/src/processor/initialize_account.rs +++ b/program/src/processor/initialize_account.rs @@ -3,6 +3,10 @@ use pinocchio::{account_info::AccountInfo, pubkey::Pubkey, ProgramResult}; use super::shared; #[inline(never)] -pub fn process_initialize_account(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult { +pub fn process_initialize_account( + program_id: &Pubkey, + accounts: &[AccountInfo], + _instruction_data: &[u8], +) -> ProgramResult { shared::initialize_account::process_initialize_account(program_id, accounts, None, true) } diff --git a/program/src/processor/initialize_account2.rs b/program/src/processor/initialize_account2.rs index ab2f65d..f3c17e8 100644 --- a/program/src/processor/initialize_account2.rs +++ b/program/src/processor/initialize_account2.rs @@ -6,7 +6,8 @@ use super::shared; pub fn process_initialize_account2( program_id: &Pubkey, accounts: &[AccountInfo], - owner: &Pubkey, + instruction_data: &[u8], ) -> ProgramResult { + let owner = unsafe { &*(instruction_data.as_ptr() as *const Pubkey) }; shared::initialize_account::process_initialize_account(program_id, accounts, Some(owner), true) } diff --git a/program/src/processor/initialize_account3.rs b/program/src/processor/initialize_account3.rs index 4287389..8902078 100644 --- a/program/src/processor/initialize_account3.rs +++ b/program/src/processor/initialize_account3.rs @@ -6,7 +6,8 @@ use super::shared; pub fn process_initialize_account3( program_id: &Pubkey, accounts: &[AccountInfo], - owner: &Pubkey, + instruction_data: &[u8], ) -> ProgramResult { + let owner = unsafe { &*(instruction_data.as_ptr() as *const Pubkey) }; shared::initialize_account::process_initialize_account(program_id, accounts, Some(owner), false) } diff --git a/program/src/processor/initialize_immutable_owner.rs b/program/src/processor/initialize_immutable_owner.rs index cdbc688..7bf5394 100644 --- a/program/src/processor/initialize_immutable_owner.rs +++ b/program/src/processor/initialize_immutable_owner.rs @@ -1,8 +1,14 @@ -use pinocchio::{account_info::AccountInfo, msg, program_error::ProgramError, ProgramResult}; +use pinocchio::{ + account_info::AccountInfo, msg, program_error::ProgramError, pubkey::Pubkey, ProgramResult, +}; use token_interface::{error::TokenError, state::account::Account}; #[inline(never)] -pub fn process_initialize_immutable_owner(accounts: &[AccountInfo]) -> ProgramResult { +pub fn process_initialize_immutable_owner( + _program_id: &Pubkey, + accounts: &[AccountInfo], + _instruction_data: &[u8], +) -> ProgramResult { let token_account_info = accounts.first().ok_or(ProgramError::NotEnoughAccountKeys)?; let account = bytemuck::try_from_bytes_mut::(unsafe { diff --git a/program/src/processor/initialize_mint.rs b/program/src/processor/initialize_mint.rs index 4d6de79..74a7dfb 100644 --- a/program/src/processor/initialize_mint.rs +++ b/program/src/processor/initialize_mint.rs @@ -1,8 +1,12 @@ -use pinocchio::{account_info::AccountInfo, ProgramResult}; +use pinocchio::{account_info::AccountInfo, pubkey::Pubkey, ProgramResult}; -use super::shared::{self, initialize_mint::InitializeMint}; +use super::shared; #[inline(never)] -pub fn process_initialize_mint(accounts: &[AccountInfo], args: &InitializeMint) -> ProgramResult { - shared::initialize_mint::process_initialize_mint(accounts, args, true) +pub fn process_initialize_mint( + _program_id: &Pubkey, + accounts: &[AccountInfo], + instruction_data: &[u8], +) -> ProgramResult { + shared::initialize_mint::process_initialize_mint(accounts, instruction_data, true) } diff --git a/program/src/processor/initialize_mint2.rs b/program/src/processor/initialize_mint2.rs index 5387566..dfdfc4e 100644 --- a/program/src/processor/initialize_mint2.rs +++ b/program/src/processor/initialize_mint2.rs @@ -1,8 +1,12 @@ -use pinocchio::{account_info::AccountInfo, ProgramResult}; +use pinocchio::{account_info::AccountInfo, pubkey::Pubkey, ProgramResult}; -use super::shared::{self, initialize_mint::InitializeMint}; +use super::shared; #[inline(never)] -pub fn process_initialize_mint2(accounts: &[AccountInfo], args: &InitializeMint) -> ProgramResult { - shared::initialize_mint::process_initialize_mint(accounts, args, false) +pub fn process_initialize_mint2( + _program_id: &Pubkey, + accounts: &[AccountInfo], + instruction_data: &[u8], +) -> ProgramResult { + shared::initialize_mint::process_initialize_mint(accounts, instruction_data, false) } diff --git a/program/src/processor/initialize_multisig.rs b/program/src/processor/initialize_multisig.rs index 7fddab5..995b5af 100644 --- a/program/src/processor/initialize_multisig.rs +++ b/program/src/processor/initialize_multisig.rs @@ -1,64 +1,18 @@ use pinocchio::{ - account_info::AccountInfo, - program_error::ProgramError, - sysvars::{rent::Rent, Sysvar}, - ProgramResult, + account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey, ProgramResult, }; -use token_interface::{error::TokenError, state::multisig::Multisig}; + +use super::shared; #[inline(never)] pub fn process_initialize_multisig( + _program_id: &Pubkey, 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::(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) } diff --git a/program/src/processor/initialize_multisig2.rs b/program/src/processor/initialize_multisig2.rs index 9dfbf96..ebbd5d3 100644 --- a/program/src/processor/initialize_multisig2.rs +++ b/program/src/processor/initialize_multisig2.rs @@ -1,8 +1,17 @@ -use pinocchio::{account_info::AccountInfo, ProgramResult}; +use pinocchio::{ + account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey, 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) +pub fn process_initialize_multisig2( + _program_id: &Pubkey, + accounts: &[AccountInfo], + instruction_data: &[u8], +) -> ProgramResult { + let m = instruction_data + .first() + .ok_or(ProgramError::InvalidInstructionData)?; + shared::initialize_multisig::process_initialize_multisig(accounts, *m, false) } diff --git a/program/src/processor/mint_to.rs b/program/src/processor/mint_to.rs index 3dadbc1..f5b3df2 100644 --- a/program/src/processor/mint_to.rs +++ b/program/src/processor/mint_to.rs @@ -1,4 +1,6 @@ -use pinocchio::{account_info::AccountInfo, pubkey::Pubkey, ProgramResult}; +use pinocchio::{ + account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey, ProgramResult, +}; use super::shared; @@ -6,7 +8,12 @@ use super::shared; pub fn process_mint_to( program_id: &Pubkey, accounts: &[AccountInfo], - amount: u64, + instruction_data: &[u8], ) -> ProgramResult { + if instruction_data.len() != 8 { + return Err(ProgramError::InvalidInstructionData); + } + let amount = unsafe { (instruction_data.as_ptr() as *const u64).read_unaligned() }; + shared::mint_to::process_mint_to(program_id, accounts, amount, None) } diff --git a/program/src/processor/mint_to_checked.rs b/program/src/processor/mint_to_checked.rs index 25684d4..846dd41 100644 --- a/program/src/processor/mint_to_checked.rs +++ b/program/src/processor/mint_to_checked.rs @@ -1,5 +1,3 @@ -use std::marker::PhantomData; - use pinocchio::{ account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey, ProgramResult, }; @@ -10,38 +8,13 @@ use super::shared; pub fn process_mint_to_checked( program_id: &Pubkey, accounts: &[AccountInfo], - amount: u64, - decimals: u8, + instruction_data: &[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 { - 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() - } + if instruction_data.len() != 9 { + return Err(ProgramError::InvalidInstructionData); } + let amount = unsafe { (instruction_data.as_ptr() as *const u64).read_unaligned() }; + let decimals = unsafe { *instruction_data.as_ptr().add(8) }; - pub fn decimals(&self) -> u8 { - unsafe { *self.raw.add(8) } - } + shared::mint_to::process_mint_to(program_id, accounts, amount, Some(decimals)) } diff --git a/program/src/processor/revoke.rs b/program/src/processor/revoke.rs index c77d8b2..864ab21 100644 --- a/program/src/processor/revoke.rs +++ b/program/src/processor/revoke.rs @@ -6,7 +6,11 @@ use token_interface::{error::TokenError, state::account::Account}; use super::validate_owner; #[inline(never)] -pub fn process_revoke(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult { +pub fn process_revoke( + program_id: &Pubkey, + accounts: &[AccountInfo], + _instruction_data: &[u8], +) -> ProgramResult { let [source_account_info, owner_info, remaning @ ..] = accounts else { return Err(ProgramError::NotEnoughAccountKeys); }; diff --git a/program/src/processor/set_authority.rs b/program/src/processor/set_authority.rs index f0ed452..be9afc0 100644 --- a/program/src/processor/set_authority.rs +++ b/program/src/processor/set_authority.rs @@ -1,8 +1,7 @@ +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, @@ -16,9 +15,12 @@ use super::validate_owner; pub fn process_set_authority( program_id: &Pubkey, accounts: &[AccountInfo], - authority_type: AuthorityType, - new_authority: Option<&Pubkey>, + instruction_data: &[u8], ) -> ProgramResult { + let args = SetAuthority::try_from_bytes(instruction_data)?; + let authority_type = args.authority_type(); + let new_authority = args.new_authority(); + let [account_info, authority_info, remaning @ ..] = accounts else { return Err(ProgramError::NotEnoughAccountKeys); }; @@ -99,35 +101,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 { - // 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 { + // 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)) - } - _ => return Err(ProgramError::InvalidInstructionData), - }; + #[inline(always)] + pub fn authority_type(&self) -> AuthorityType { + 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(33) == 0 { + Option::None + } else { + Option::Some(&*(self.raw.add(34) as *const Pubkey)) + } + } } } diff --git a/program/src/processor/shared/initialize_account.rs b/program/src/processor/shared/initialize_account.rs index 8176ed3..cdae359 100644 --- a/program/src/processor/shared/initialize_account.rs +++ b/program/src/processor/shared/initialize_account.rs @@ -1,3 +1,4 @@ +use core::mem::size_of; use pinocchio::{ account_info::AccountInfo, program_error::ProgramError, @@ -5,7 +6,6 @@ use pinocchio::{ sysvars::{rent::Rent, Sysvar}, ProgramResult, }; -use std::mem::size_of; use token_interface::{ error::TokenError, native_mint::is_native_mint, diff --git a/program/src/processor/shared/initialize_mint.rs b/program/src/processor/shared/initialize_mint.rs index 845b64b..ae179d3 100644 --- a/program/src/processor/shared/initialize_mint.rs +++ b/program/src/processor/shared/initialize_mint.rs @@ -1,3 +1,4 @@ +use core::{marker::PhantomData, mem::size_of}; use pinocchio::{ account_info::AccountInfo, program_error::ProgramError, @@ -5,7 +6,6 @@ use pinocchio::{ sysvars::{rent::Rent, Sysvar}, ProgramResult, }; -use std::{marker::PhantomData, mem::size_of}; use token_interface::{ error::TokenError, state::{mint::Mint, PodCOption}, @@ -14,9 +14,11 @@ use token_interface::{ #[inline(always)] pub fn process_initialize_mint( accounts: &[AccountInfo], - args: &InitializeMint, + instruction_data: &[u8], rent_sysvar_account: bool, ) -> ProgramResult { + let args = InitializeMint::try_from_bytes(instruction_data)?; + let (mint_info, rent_sysvar_info) = if rent_sysvar_account { let [mint_info, rent_sysvar_info, _remaining @ ..] = accounts else { return Err(ProgramError::NotEnoughAccountKeys); diff --git a/program/src/processor/sync_native.rs b/program/src/processor/sync_native.rs index 25b2029..699970d 100644 --- a/program/src/processor/sync_native.rs +++ b/program/src/processor/sync_native.rs @@ -6,7 +6,11 @@ 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 { +pub fn process_sync_native( + program_id: &Pubkey, + accounts: &[AccountInfo], + _instruction_data: &[u8], +) -> ProgramResult { let native_account_info = accounts.first().ok_or(ProgramError::NotEnoughAccountKeys)?; check_account_owner(program_id, native_account_info)?; diff --git a/program/src/processor/thaw_account.rs b/program/src/processor/thaw_account.rs index 00ea088..3303a5f 100644 --- a/program/src/processor/thaw_account.rs +++ b/program/src/processor/thaw_account.rs @@ -3,6 +3,10 @@ use pinocchio::{account_info::AccountInfo, pubkey::Pubkey, ProgramResult}; use super::shared::toggle_account_state::process_toggle_account_state; #[inline(never)] -pub fn process_thaw_account(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult { +pub fn process_thaw_account( + program_id: &Pubkey, + accounts: &[AccountInfo], + _instruction_data: &[u8], +) -> ProgramResult { process_toggle_account_state(program_id, accounts, false) } diff --git a/program/src/processor/transfer.rs b/program/src/processor/transfer.rs index 254134b..5ad9d7d 100644 --- a/program/src/processor/transfer.rs +++ b/program/src/processor/transfer.rs @@ -1,4 +1,6 @@ -use pinocchio::{account_info::AccountInfo, pubkey::Pubkey, ProgramResult}; +use pinocchio::{ + account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey, ProgramResult, +}; use super::shared; @@ -6,7 +8,12 @@ use super::shared; pub fn process_transfer( program_id: &Pubkey, accounts: &[AccountInfo], - amount: u64, + instruction_data: &[u8], ) -> ProgramResult { + if instruction_data.len() != 8 { + return Err(ProgramError::InvalidInstructionData); + } + let amount = unsafe { (instruction_data.as_ptr() as *const u64).read_unaligned() }; + shared::transfer::process_transfer(program_id, accounts, amount, None) } diff --git a/program/src/processor/transfer_checked.rs b/program/src/processor/transfer_checked.rs index 1ebcdcd..22199f4 100644 --- a/program/src/processor/transfer_checked.rs +++ b/program/src/processor/transfer_checked.rs @@ -1,5 +1,3 @@ -use std::marker::PhantomData; - use pinocchio::{ account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey, ProgramResult, }; @@ -10,38 +8,13 @@ use super::shared; 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 { - 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() - } + if instruction_data.len() != 9 { + return Err(ProgramError::InvalidInstructionData); } + let amount = unsafe { (instruction_data.as_ptr() as *const u64).read_unaligned() }; + let decimals = unsafe { *instruction_data.as_ptr().add(8) }; - pub fn decimals(&self) -> u8 { - unsafe { *self.raw.add(8) } - } + shared::transfer::process_transfer(program_id, accounts, amount, Some(decimals)) } diff --git a/program/src/processor/ui_amount_to_amount.rs b/program/src/processor/ui_amount_to_amount.rs index 4f3dbca..3d94257 100644 --- a/program/src/processor/ui_amount_to_amount.rs +++ b/program/src/processor/ui_amount_to_amount.rs @@ -1,3 +1,5 @@ +use core::str; + use pinocchio::{ account_info::AccountInfo, program::set_return_data, program_error::ProgramError, pubkey::Pubkey, ProgramResult, @@ -10,8 +12,11 @@ use super::{check_account_owner, try_ui_amount_into_amount}; pub fn process_ui_amount_to_amount( program_id: &Pubkey, accounts: &[AccountInfo], - ui_amount: &str, + instruction_data: &[u8], ) -> ProgramResult { + let ui_amount = + 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)?; From d68272b173c337f4345e7324bb1170679b5e9610 Mon Sep 17 00:00:00 2001 From: febo Date: Fri, 22 Nov 2024 22:14:12 +0000 Subject: [PATCH 2/7] Refactor processor --- README.md | 16 +- program/src/entrypoint.rs | 208 ++++++++++-------- program/src/processor/amount_to_ui_amount.rs | 6 +- program/src/processor/approve.rs | 21 +- program/src/processor/approve_checked.rs | 27 ++- program/src/processor/burn.rs | 21 +- program/src/processor/burn_checked.rs | 31 +-- program/src/processor/close_account.rs | 14 +- program/src/processor/freeze_account.rs | 10 +- .../src/processor/get_account_data_size.rs | 11 +- program/src/processor/initialize_account.rs | 12 +- program/src/processor/initialize_account2.rs | 3 +- program/src/processor/initialize_account3.rs | 5 +- .../processor/initialize_immutable_owner.rs | 10 +- program/src/processor/initialize_mint.rs | 108 ++++++++- program/src/processor/initialize_mint2.rs | 9 +- program/src/processor/initialize_multisig.rs | 5 +- program/src/processor/initialize_multisig2.rs | 5 +- program/src/processor/mint_to.rs | 23 +- program/src/processor/mint_to_checked.rs | 28 +-- program/src/processor/mod.rs | 45 +++- program/src/processor/revoke.rs | 12 +- program/src/processor/set_authority.rs | 14 +- program/src/processor/shared/approve.rs | 7 +- program/src/processor/shared/burn.rs | 13 +- .../processor/shared/initialize_account.rs | 3 +- .../src/processor/shared/initialize_mint.rs | 108 --------- program/src/processor/shared/mint_to.rs | 11 +- program/src/processor/shared/mod.rs | 1 - .../processor/shared/toggle_account_state.rs | 12 +- program/src/processor/shared/transfer.rs | 15 +- program/src/processor/sync_native.rs | 12 +- program/src/processor/thaw_account.rs | 10 +- program/src/processor/transfer.rs | 23 +- program/src/processor/transfer_checked.rs | 26 ++- program/src/processor/ui_amount_to_amount.rs | 6 +- 36 files changed, 417 insertions(+), 474 deletions(-) delete mode 100644 program/src/processor/shared/initialize_mint.rs diff --git a/README.md b/README.md index fab4746..bb1fbcc 100644 --- a/README.md +++ b/README.md @@ -23,10 +23,10 @@ This repository contains a **proof-of-concept** of a reimplementation of the SPL | Instruction | Completed | CU (`p-token`) | CU (`spl-token`) | |----------------------------|-----------|----------------|------------------| -| `InitializeMint` | ✅ | 389 | 2967 | -| `InitializeAccount` | ✅ | 409 | 4527 | -| `InitializeMultisig` | ✅ | 458 | 2973 | -| `Transfer` | ✅ | 194 | 4645 | +| `InitializeMint` | ✅ | 378 | 2967 | +| `InitializeAccount` | ✅ | 445 | 4527 | +| `InitializeMultisig` | ✅ | 464 | 2973 | +| `Transfer` | ✅ | 197 | 4645 | | `Approve` | ✅ | 151 | 2904 | | `Revoke` | ✅ | 93 | 2677 | | `SetAuthority` | ✅ | 171 | 3167 | @@ -35,14 +35,14 @@ This repository contains a **proof-of-concept** of a reimplementation of the SPL | `CloseAccount` | ✅ | 163 | 2916 | | `FreezeAccount` | ✅ | 131 | 4265 | | `ThawAccount` | ✅ | 132 | 4267 | -| `TransferChecked` | ✅ | 207 | 6201 | +| `TransferChecked` | ✅ | 254 | 6201 | | `ApproveChecked` | ✅ | 166 | 4459 | | `MintToChecked` | ✅ | 180 | 4546 | | `BurnChecked` | ✅ | 166 | 4755 | -| `InitializeAccount2` | ✅ | 394 | 4388 | +| `InitializeAccount2` | ✅ | 430 | 4388 | | `SyncNative` | ✅ | | | -| `InitializeAccount3` | ✅ | 523 | 4240 | -| `InitializeMultisig2` | ✅ | 563 | 2826 | +| `InitializeAccount3` | ✅ | 560 | 4240 | +| `InitializeMultisig2` | ✅ | 591 | 2826 | | `InitializeMint2` | ✅ | 500 | 2827 | | `GetAccountDataSize` | ✅ | | | | `InitializeImmutableOwner` | ✅ | | | diff --git a/program/src/entrypoint.rs b/program/src/entrypoint.rs index a46ee23..f72845e 100644 --- a/program/src/entrypoint.rs +++ b/program/src/entrypoint.rs @@ -3,207 +3,227 @@ use pinocchio::{ ProgramResult, }; -use crate::processor::{ - amount_to_ui_amount::process_amount_to_ui_amount, approve::process_approve, - approve_checked::process_approve_checked, burn::process_burn, - burn_checked::process_burn_checked, 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, revoke::process_revoke, - set_authority::process_set_authority, sync_native::process_sync_native, - thaw_account::process_thaw_account, transfer::process_transfer, - transfer_checked::process_transfer_checked, 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"); - process_initialize_mint(program_id, accounts, data) + process_initialize_mint(accounts, instruction_data, true) + } + + // 3 - Transfer + 3 => { + #[cfg(feature = "logging")] + pinocchio::msg!("Instruction: Transfer"); + + process_transfer(accounts, instruction_data) + } + // 7 - MintTo + 7 => { + #[cfg(feature = "logging")] + pinocchio::msg!("Instruction: MintTo"); + + process_mint_to(accounts, instruction_data) + } + // 9 - CloseAccount + 9 => { + #[cfg(feature = "logging")] + pinocchio::msg!("Instruction: CloseAccount"); + + process_close_account(accounts) + } + // 18 - InitializeAccount3 + 18 => { + #[cfg(feature = "logging")] + pinocchio::msg!("Instruction: InitializeAccount3"); + + process_initialize_account3(accounts, instruction_data) } + // 20 - InitializeMint2 + 20 => { + #[cfg(feature = "logging")] + pinocchio::msg!("Instruction: InitializeMint2"); + + process_initialize_mint2(accounts, instruction_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 - Some((&1, data)) => { + 1 => { #[cfg(feature = "logging")] pinocchio::msg!("Instruction: InitializeAccount"); - process_initialize_account(program_id, accounts, data) + process_initialize_account(accounts) } // 2 - InitializeMultisig - Some((&2, data)) => { + 2 => { #[cfg(feature = "logging")] pinocchio::msg!("Instruction: InitializeMultisig"); - process_initialize_multisig(program_id, accounts, data) - } - // 3 - Transfer - Some((&3, data)) => { - #[cfg(feature = "logging")] - pinocchio::msg!("Instruction: Transfer"); - - process_transfer(program_id, accounts, data) + process_initialize_multisig(accounts, instruction_data) } // 4 - Approve - Some((&4, data)) => { + 4 => { #[cfg(feature = "logging")] pinocchio::msg!("Instruction: Approve"); - process_approve(program_id, accounts, data) + process_approve(accounts, instruction_data) } // 5 - Revoke - Some((&5, data)) => { + 5 => { #[cfg(feature = "logging")] pinocchio::msg!("Instruction: Revoke"); - process_revoke(program_id, accounts, data) + process_revoke(accounts, instruction_data) } // 6 - SetAuthority - Some((&6, data)) => { + 6 => { #[cfg(feature = "logging")] pinocchio::msg!("Instruction: SetAuthority"); - process_set_authority(program_id, accounts, data) - } - // 7 - MintTo - Some((&7, data)) => { - #[cfg(feature = "logging")] - pinocchio::msg!("Instruction: MintTo"); - - process_mint_to(program_id, accounts, data) + process_set_authority(accounts, instruction_data) } // 8 - Burn - Some((&8, data)) => { + 8 => { #[cfg(feature = "logging")] pinocchio::msg!("Instruction: Burn"); - process_burn(program_id, accounts, data) - } - // 9 - CloseAccount - Some((&9, data)) => { - #[cfg(feature = "logging")] - pinocchio::msg!("Instruction: CloseAccount"); - - process_close_account(program_id, accounts, data) + process_burn(accounts, instruction_data) } // 10 - FreezeAccount - Some((&10, data)) => { + 10 => { #[cfg(feature = "logging")] pinocchio::msg!("Instruction: FreezeAccount"); - process_freeze_account(program_id, accounts, data) + process_freeze_account(accounts) } // 11 - ThawAccount - Some((&11, data)) => { + 11 => { #[cfg(feature = "logging")] pinocchio::msg!("Instruction: ThawAccount"); - process_thaw_account(program_id, accounts, data) + process_thaw_account(accounts) } // 12 - TransferChecked - Some((&12, data)) => { + 12 => { #[cfg(feature = "logging")] pinocchio::msg!("Instruction: TransferChecked"); - process_transfer_checked(program_id, accounts, data) + process_transfer_checked(accounts, instruction_data) } // 13 - ApproveChecked - Some((&13, data)) => { + 13 => { #[cfg(feature = "logging")] pinocchio::msg!("Instruction: ApproveChecked"); - process_approve_checked(program_id, accounts, data) + process_approve_checked(accounts, instruction_data) } // 14 - MintToChecked - Some((&14, data)) => { + 14 => { #[cfg(feature = "logging")] pinocchio::msg!("Instruction: MintToChecked"); - process_mint_to_checked(program_id, accounts, data) + process_mint_to_checked(accounts, instruction_data) } // 15 - BurnChecked - Some((&15, data)) => { + 15 => { #[cfg(feature = "logging")] pinocchio::msg!("Instruction: BurnChecked"); - process_burn_checked(program_id, accounts, data) + process_burn_checked(accounts, instruction_data) } // 16 - InitializeAccount2 - Some((&16, data)) => { + 16 => { #[cfg(feature = "logging")] pinocchio::msg!("Instruction: InitializeAccount2"); - process_initialize_account2(program_id, accounts, data) + process_initialize_account2(accounts, instruction_data) } // 17 - SyncNative - Some((&17, data)) => { + 17 => { #[cfg(feature = "logging")] pinocchio::msg!("Instruction: SyncNative"); - process_sync_native(program_id, accounts, data) - } - // 18 - InitializeAccount3 - Some((&18, data)) => { - #[cfg(feature = "logging")] - pinocchio::msg!("Instruction: InitializeAccount3"); - - process_initialize_account3(program_id, accounts, data) + process_sync_native(accounts) } // 19 - InitializeMultisig2 - Some((&19, data)) => { + 19 => { #[cfg(feature = "logging")] pinocchio::msg!("Instruction: InitializeMultisig2"); - process_initialize_multisig2(program_id, accounts, data) - } - // 20 - InitializeMint2 - Some((&20, data)) => { - #[cfg(feature = "logging")] - pinocchio::msg!("Instruction: InitializeMint2"); - - process_initialize_mint2(program_id, accounts, data) + process_initialize_multisig2(accounts, instruction_data) } // 21 - GetAccountDataSize - Some((&21, data)) => { + 21 => { #[cfg(feature = "logging")] pinocchio::msg!("Instruction: GetAccountDataSize"); - process_get_account_data_size(program_id, accounts, data) + process_get_account_data_size(accounts) } // 22 - InitializeImmutableOwner - Some((&22, data)) => { + 22 => { #[cfg(feature = "logging")] pinocchio::msg!("Instruction: InitializeImmutableOwner"); - process_initialize_immutable_owner(program_id, accounts, data) + process_initialize_immutable_owner(accounts) } // 23 - AmountToUiAmount - Some((&23, data)) => { + 23 => { #[cfg(feature = "logging")] pinocchio::msg!("Instruction: AmountToUiAmount"); - process_amount_to_ui_amount(program_id, accounts, data) + process_amount_to_ui_amount(accounts, instruction_data) } // 24 - UiAmountToAmount - Some((&24, data)) => { + 24 => { #[cfg(feature = "logging")] pinocchio::msg!("Instruction: UiAmountToAmount"); - process_ui_amount_to_amount(program_id, accounts, data) + process_ui_amount_to_amount(accounts, instruction_data) } _ => Err(ProgramError::InvalidInstructionData), } diff --git a/program/src/processor/amount_to_ui_amount.rs b/program/src/processor/amount_to_ui_amount.rs index f11dd06..6c61906 100644 --- a/program/src/processor/amount_to_ui_amount.rs +++ b/program/src/processor/amount_to_ui_amount.rs @@ -1,6 +1,5 @@ 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}; @@ -8,7 +7,6 @@ use super::{amount_to_ui_amount_string_trimmed, check_account_owner}; #[inline(never)] pub fn process_amount_to_ui_amount( - program_id: &Pubkey, accounts: &[AccountInfo], instruction_data: &[u8], ) -> ProgramResult { @@ -18,7 +16,7 @@ pub fn process_amount_to_ui_amount( let amount = unsafe { (instruction_data.as_ptr() as *const u64).read_unaligned() }; 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::(unsafe { mint_info.borrow_mut_data_unchecked() }) diff --git a/program/src/processor/approve.rs b/program/src/processor/approve.rs index d363d11..4284e09 100644 --- a/program/src/processor/approve.rs +++ b/program/src/processor/approve.rs @@ -1,19 +1,14 @@ -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( - program_id: &Pubkey, - accounts: &[AccountInfo], - instruction_data: &[u8], -) -> ProgramResult { - if instruction_data.len() != 8 { - return Err(ProgramError::InvalidInstructionData); - } - let amount = unsafe { (instruction_data.as_ptr() as *const u64).read_unaligned() }; +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(program_id, accounts, amount, None) + shared::approve::process_approve(accounts, amount, None) } diff --git a/program/src/processor/approve_checked.rs b/program/src/processor/approve_checked.rs index dd95fe0..0eacc01 100644 --- a/program/src/processor/approve_checked.rs +++ b/program/src/processor/approve_checked.rs @@ -1,20 +1,19 @@ -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], - instruction_data: &[u8], -) -> ProgramResult { - if instruction_data.len() != 9 { - return Err(ProgramError::InvalidInstructionData); - } - let amount = unsafe { (instruction_data.as_ptr() as *const u64).read_unaligned() }; - let decimals = unsafe { *instruction_data.as_ptr().add(8) }; +pub fn process_approve_checked(accounts: &[AccountInfo], instruction_data: &[u8]) -> ProgramResult { + let (amount, decimals) = instruction_data.split_at(core::mem::size_of::()); + let amount = u64::from_le_bytes( + amount + .try_into() + .map_err(|_error| ProgramError::InvalidInstructionData)?, + ); - shared::approve::process_approve(program_id, accounts, amount, Some(decimals)) + shared::approve::process_approve( + accounts, + amount, + Some(*decimals.first().ok_or(ProgramError::InvalidAccountData)?), + ) } diff --git a/program/src/processor/burn.rs b/program/src/processor/burn.rs index 8c420c5..31d05a2 100644 --- a/program/src/processor/burn.rs +++ b/program/src/processor/burn.rs @@ -1,19 +1,14 @@ -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( - program_id: &Pubkey, - accounts: &[AccountInfo], - instruction_data: &[u8], -) -> ProgramResult { - if instruction_data.len() != 8 { - return Err(ProgramError::InvalidInstructionData); - } - let amount = unsafe { (instruction_data.as_ptr() as *const u64).read_unaligned() }; +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(program_id, accounts, amount, None) + shared::burn::process_burn(accounts, amount, None) } diff --git a/program/src/processor/burn_checked.rs b/program/src/processor/burn_checked.rs index 36a479a..07e2002 100644 --- a/program/src/processor/burn_checked.rs +++ b/program/src/processor/burn_checked.rs @@ -1,20 +1,23 @@ -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], - instruction_data: &[u8], -) -> ProgramResult { - if instruction_data.len() != 9 { - return Err(ProgramError::InvalidInstructionData); - } - let amount = unsafe { (instruction_data.as_ptr() as *const u64).read_unaligned() }; - let decimals = unsafe { *instruction_data.as_ptr().add(8) }; +pub fn process_burn_checked(accounts: &[AccountInfo], instruction_data: &[u8]) -> ProgramResult { + let (amount, decimals) = instruction_data.split_at(core::mem::size_of::()); + let amount = u64::from_le_bytes( + amount + .try_into() + .map_err(|_error| ProgramError::InvalidInstructionData)?, + ); - shared::burn::process_burn(program_id, accounts, amount, Some(decimals)) + shared::burn::process_burn( + accounts, + amount, + Some( + *decimals + .first() + .ok_or(ProgramError::InvalidInstructionData)?, + ), + ) } diff --git a/program/src/processor/close_account.rs b/program/src/processor/close_account.rs index 85a0c9e..7a6737f 100644 --- a/program/src/processor/close_account.rs +++ b/program/src/processor/close_account.rs @@ -1,16 +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], - _instruction_data: &[u8], -) -> 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); @@ -34,7 +28,7 @@ pub fn process_close_account( .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); } diff --git a/program/src/processor/freeze_account.rs b/program/src/processor/freeze_account.rs index 952c50e..cff2a3d 100644 --- a/program/src/processor/freeze_account.rs +++ b/program/src/processor/freeze_account.rs @@ -1,12 +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], - _instruction_data: &[u8], -) -> ProgramResult { - process_toggle_account_state(program_id, accounts, true) +pub fn process_freeze_account(accounts: &[AccountInfo]) -> ProgramResult { + process_toggle_account_state(accounts, true) } diff --git a/program/src/processor/get_account_data_size.rs b/program/src/processor/get_account_data_size.rs index 2b89767..2d51862 100644 --- a/program/src/processor/get_account_data_size.rs +++ b/program/src/processor/get_account_data_size.rs @@ -1,22 +1,17 @@ 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], - _instruction_data: &[u8], -) -> ProgramResult { +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)?; + check_account_owner(mint_info)?; let _ = bytemuck::try_from_bytes::(unsafe { mint_info.borrow_data_unchecked() }) .map_err(|_error| ProgramError::InvalidAccountData)?; diff --git a/program/src/processor/initialize_account.rs b/program/src/processor/initialize_account.rs index 0c37c7e..2c12509 100644 --- a/program/src/processor/initialize_account.rs +++ b/program/src/processor/initialize_account.rs @@ -1,12 +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], - _instruction_data: &[u8], -) -> 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) } diff --git a/program/src/processor/initialize_account2.rs b/program/src/processor/initialize_account2.rs index f3c17e8..ed3130a 100644 --- a/program/src/processor/initialize_account2.rs +++ b/program/src/processor/initialize_account2.rs @@ -4,10 +4,9 @@ use super::shared; #[inline(never)] pub fn process_initialize_account2( - program_id: &Pubkey, accounts: &[AccountInfo], instruction_data: &[u8], ) -> ProgramResult { let owner = unsafe { &*(instruction_data.as_ptr() as *const Pubkey) }; - shared::initialize_account::process_initialize_account(program_id, accounts, Some(owner), true) + shared::initialize_account::process_initialize_account(accounts, Some(owner), true) } diff --git a/program/src/processor/initialize_account3.rs b/program/src/processor/initialize_account3.rs index 8902078..4eaa7a5 100644 --- a/program/src/processor/initialize_account3.rs +++ b/program/src/processor/initialize_account3.rs @@ -2,12 +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], instruction_data: &[u8], ) -> ProgramResult { let owner = unsafe { &*(instruction_data.as_ptr() as *const Pubkey) }; - shared::initialize_account::process_initialize_account(program_id, accounts, Some(owner), false) + shared::initialize_account::process_initialize_account(accounts, Some(owner), false) } diff --git a/program/src/processor/initialize_immutable_owner.rs b/program/src/processor/initialize_immutable_owner.rs index 7bf5394..cdbc688 100644 --- a/program/src/processor/initialize_immutable_owner.rs +++ b/program/src/processor/initialize_immutable_owner.rs @@ -1,14 +1,8 @@ -use pinocchio::{ - account_info::AccountInfo, msg, program_error::ProgramError, pubkey::Pubkey, ProgramResult, -}; +use pinocchio::{account_info::AccountInfo, msg, program_error::ProgramError, ProgramResult}; use token_interface::{error::TokenError, state::account::Account}; #[inline(never)] -pub fn process_initialize_immutable_owner( - _program_id: &Pubkey, - accounts: &[AccountInfo], - _instruction_data: &[u8], -) -> ProgramResult { +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::(unsafe { diff --git a/program/src/processor/initialize_mint.rs b/program/src/processor/initialize_mint.rs index 74a7dfb..ae179d3 100644 --- a/program/src/processor/initialize_mint.rs +++ b/program/src/processor/initialize_mint.rs @@ -1,12 +1,108 @@ -use pinocchio::{account_info::AccountInfo, pubkey::Pubkey, 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, PodCOption}, +}; -use super::shared; - -#[inline(never)] +#[inline(always)] pub fn process_initialize_mint( - _program_id: &Pubkey, accounts: &[AccountInfo], instruction_data: &[u8], + rent_sysvar_account: bool, ) -> ProgramResult { - shared::initialize_mint::process_initialize_mint(accounts, instruction_data, true) + let args = InitializeMint::try_from_bytes(instruction_data)?; + + 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::(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::()) + } else { + Rent::get()?.is_exempt(mint_info.lamports(), size_of::()) + }; + + 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 { + // 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)) + } + } + } } diff --git a/program/src/processor/initialize_mint2.rs b/program/src/processor/initialize_mint2.rs index dfdfc4e..0f1f07d 100644 --- a/program/src/processor/initialize_mint2.rs +++ b/program/src/processor/initialize_mint2.rs @@ -1,12 +1,11 @@ -use pinocchio::{account_info::AccountInfo, pubkey::Pubkey, ProgramResult}; +use pinocchio::{account_info::AccountInfo, ProgramResult}; -use super::shared; +use super::initialize_mint::process_initialize_mint; -#[inline(never)] +#[inline(always)] pub fn process_initialize_mint2( - _program_id: &Pubkey, accounts: &[AccountInfo], instruction_data: &[u8], ) -> ProgramResult { - shared::initialize_mint::process_initialize_mint(accounts, instruction_data, false) + process_initialize_mint(accounts, instruction_data, false) } diff --git a/program/src/processor/initialize_multisig.rs b/program/src/processor/initialize_multisig.rs index 995b5af..703abb1 100644 --- a/program/src/processor/initialize_multisig.rs +++ b/program/src/processor/initialize_multisig.rs @@ -1,12 +1,9 @@ -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_initialize_multisig( - _program_id: &Pubkey, accounts: &[AccountInfo], instruction_data: &[u8], ) -> ProgramResult { diff --git a/program/src/processor/initialize_multisig2.rs b/program/src/processor/initialize_multisig2.rs index ebbd5d3..ada297e 100644 --- a/program/src/processor/initialize_multisig2.rs +++ b/program/src/processor/initialize_multisig2.rs @@ -1,12 +1,9 @@ -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_initialize_multisig2( - _program_id: &Pubkey, accounts: &[AccountInfo], instruction_data: &[u8], ) -> ProgramResult { diff --git a/program/src/processor/mint_to.rs b/program/src/processor/mint_to.rs index f5b3df2..59db533 100644 --- a/program/src/processor/mint_to.rs +++ b/program/src/processor/mint_to.rs @@ -1,19 +1,14 @@ -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( - program_id: &Pubkey, - accounts: &[AccountInfo], - instruction_data: &[u8], -) -> ProgramResult { - if instruction_data.len() != 8 { - return Err(ProgramError::InvalidInstructionData); - } - let amount = unsafe { (instruction_data.as_ptr() as *const u64).read_unaligned() }; +#[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(program_id, accounts, amount, None) + shared::mint_to::process_mint_to(accounts, amount, None) } diff --git a/program/src/processor/mint_to_checked.rs b/program/src/processor/mint_to_checked.rs index 846dd41..bc4dfb8 100644 --- a/program/src/processor/mint_to_checked.rs +++ b/program/src/processor/mint_to_checked.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 super::shared; #[inline(never)] -pub fn process_mint_to_checked( - program_id: &Pubkey, - accounts: &[AccountInfo], - instruction_data: &[u8], -) -> ProgramResult { - if instruction_data.len() != 9 { - return Err(ProgramError::InvalidInstructionData); - } - let amount = unsafe { (instruction_data.as_ptr() as *const u64).read_unaligned() }; - let decimals = unsafe { *instruction_data.as_ptr().add(8) }; +pub fn process_mint_to_checked(accounts: &[AccountInfo], instruction_data: &[u8]) -> ProgramResult { + let (amount, decimals) = instruction_data.split_at(core::mem::size_of::()); - shared::mint_to::process_mint_to(program_id, accounts, amount, Some(decimals)) + 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)?), + ) } diff --git a/program/src/processor/mod.rs b/program/src/processor/mod.rs index 9527cc7..0494d79 100644 --- a/program/src/processor/mod.rs +++ b/program/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,7 +93,7 @@ pub fn validate_owner( return Err(TokenError::OwnerMismatch.into()); } - if owner_account_info.data_len() == Multisig::LEN && program_id != owner_account_info.owner() { + if owner_account_info.data_len() == Multisig::LEN && &crate::ID != owner_account_info.owner() { let multisig_data = owner_account_info.try_borrow_data()?; let multisig = bytemuck::from_bytes::(&multisig_data); @@ -99,7 +124,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 +136,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 +151,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 { +fn try_ui_amount_into_amount(ui_amount: String, decimals: u8) -> Result { 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 diff --git a/program/src/processor/revoke.rs b/program/src/processor/revoke.rs index 864ab21..f4ac1ae 100644 --- a/program/src/processor/revoke.rs +++ b/program/src/processor/revoke.rs @@ -1,16 +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::validate_owner; #[inline(never)] -pub fn process_revoke( - program_id: &Pubkey, - accounts: &[AccountInfo], - _instruction_data: &[u8], -) -> ProgramResult { +pub fn process_revoke(accounts: &[AccountInfo], _instruction_data: &[u8]) -> ProgramResult { let [source_account_info, owner_info, remaning @ ..] = accounts else { return Err(ProgramError::NotEnoughAccountKeys); }; @@ -24,7 +18,7 @@ pub fn process_revoke( 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(); diff --git a/program/src/processor/set_authority.rs b/program/src/processor/set_authority.rs index be9afc0..e286de5 100644 --- a/program/src/processor/set_authority.rs +++ b/program/src/processor/set_authority.rs @@ -12,11 +12,7 @@ use token_interface::{ use super::validate_owner; #[inline(never)] -pub fn process_set_authority( - program_id: &Pubkey, - accounts: &[AccountInfo], - instruction_data: &[u8], -) -> ProgramResult { +pub fn process_set_authority(accounts: &[AccountInfo], instruction_data: &[u8]) -> ProgramResult { let args = SetAuthority::try_from_bytes(instruction_data)?; let authority_type = args.authority_type(); let new_authority = args.new_authority(); @@ -37,7 +33,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; @@ -54,7 +50,7 @@ pub fn process_set_authority( } AuthorityType::CloseAccount => { let authority = account.close_authority.as_ref().unwrap_or(&account.owner); - validate_owner(program_id, authority, authority_info, remaning)?; + validate_owner(authority, authority_info, remaning)?; account.close_authority = PodCOption::from(new_authority.copied()); } _ => { @@ -76,7 +72,7 @@ pub fn process_set_authority( .as_ref() .ok_or(TokenError::FixedSupply)?; - validate_owner(program_id, mint_authority, authority_info, remaning)?; + validate_owner(mint_authority, authority_info, remaning)?; mint.mint_authority = PodCOption::from(new_authority.copied()); } AuthorityType::FreezeAccount => { @@ -87,7 +83,7 @@ pub fn process_set_authority( .as_ref() .ok_or(TokenError::MintCannotFreeze)?; - validate_owner(program_id, freeze_authority, authority_info, remaning)?; + validate_owner(freeze_authority, authority_info, remaning)?; mint.freeze_authority = PodCOption::from(new_authority.copied()); } _ => { diff --git a/program/src/processor/shared/approve.rs b/program/src/processor/shared/approve.rs index 303d943..b0f0061 100644 --- a/program/src/processor/shared/approve.rs +++ b/program/src/processor/shared/approve.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, PodCOption}, @@ -10,7 +8,6 @@ use crate::processor::validate_owner; #[inline(always)] pub fn process_approve( - program_id: &Pubkey, accounts: &[AccountInfo], amount: u64, expected_decimals: Option, @@ -65,7 +62,7 @@ pub fn process_approve( } } - validate_owner(program_id, &source_account.owner, owner_info, remaining)?; + validate_owner(&source_account.owner, owner_info, remaining)?; source_account.delegate = PodCOption::some(*delegate_info.key()); source_account.delegated_amount = amount.into(); diff --git a/program/src/processor/shared/burn.rs b/program/src/processor/shared/burn.rs index c46d0e3..f34c8e5 100644 --- a/program/src/processor/shared/burn.rs +++ b/program/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, @@ -58,7 +55,7 @@ pub fn process_burn( if !is_owned_by_system_program_or_incinerator(&source_account.owner) { match source_account.delegate.as_ref() { 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) .checked_sub(amount) @@ -70,14 +67,14 @@ pub fn process_burn( } } _ => { - 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)?; + check_account_owner(source_account_info)?; + check_account_owner(mint_info)?; } source_account.amount = updated_source_amount.into(); diff --git a/program/src/processor/shared/initialize_account.rs b/program/src/processor/shared/initialize_account.rs index cdae359..45e3506 100644 --- a/program/src/processor/shared/initialize_account.rs +++ b/program/src/processor/shared/initialize_account.rs @@ -20,7 +20,6 @@ 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, @@ -62,7 +61,7 @@ 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_data) diff --git a/program/src/processor/shared/initialize_mint.rs b/program/src/processor/shared/initialize_mint.rs deleted file mode 100644 index ae179d3..0000000 --- a/program/src/processor/shared/initialize_mint.rs +++ /dev/null @@ -1,108 +0,0 @@ -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, PodCOption}, -}; - -#[inline(always)] -pub fn process_initialize_mint( - accounts: &[AccountInfo], - instruction_data: &[u8], - rent_sysvar_account: bool, -) -> ProgramResult { - let args = InitializeMint::try_from_bytes(instruction_data)?; - - 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::(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::()) - } else { - Rent::get()?.is_exempt(mint_info.lamports(), size_of::()) - }; - - 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 { - // 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)) - } - } - } -} diff --git a/program/src/processor/shared/mint_to.rs b/program/src/processor/shared/mint_to.rs index 62ca9d7..2fa2776 100644 --- a/program/src/processor/shared/mint_to.rs +++ b/program/src/processor/shared/mint_to.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, native_mint::is_native_mint, @@ -11,7 +9,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, @@ -49,13 +46,13 @@ pub fn process_mint_to( } match mint.mint_authority.get() { - Some(mint_authority) => validate_owner(program_id, &mint_authority, owner_info, remaining)?, + 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) diff --git a/program/src/processor/shared/mod.rs b/program/src/processor/shared/mod.rs index 634ccab..58e042f 100644 --- a/program/src/processor/shared/mod.rs +++ b/program/src/processor/shared/mod.rs @@ -1,7 +1,6 @@ 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; diff --git a/program/src/processor/shared/toggle_account_state.rs b/program/src/processor/shared/toggle_account_state.rs index e55ea63..5bb73d0 100644 --- a/program/src/processor/shared/toggle_account_state.rs +++ b/program/src/processor/shared/toggle_account_state.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::{ @@ -12,11 +10,7 @@ use token_interface::{ 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); }; @@ -40,7 +34,7 @@ pub fn process_toggle_account_state( .map_err(|_error| ProgramError::InvalidAccountData)?; match mint.freeze_authority.as_ref() { - Option::Some(authority) => validate_owner(program_id, authority, authority_info, remaining), + Option::Some(authority) => validate_owner(authority, authority_info, remaining), Option::None => Err(TokenError::MintCannotFreeze.into()), }?; diff --git a/program/src/processor/shared/transfer.rs b/program/src/processor/shared/transfer.rs index 766011b..4840b93 100644 --- a/program/src/processor/shared/transfer.rs +++ b/program/src/processor/shared/transfer.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, native_mint::is_native_mint, @@ -11,7 +9,6 @@ use crate::processor::{check_account_owner, validate_owner}; #[inline(always)] pub fn process_transfer( - program_id: &Pubkey, accounts: &[AccountInfo], amount: u64, expected_decimals: Option, @@ -96,12 +93,12 @@ pub fn process_transfer( } } - let self_transfer = source_account_info.key() == destination_account_info.key(); + 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)?; + validate_owner(authority_info.key(), authority_info, remaning)?; let delegated_amount = u64::from(source_account.delegated_amount) .checked_sub(amount) @@ -115,12 +112,12 @@ pub fn process_transfer( } } } 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)?; + check_account_owner(source_account_info)?; + check_account_owner(destination_account_info)?; // No need to move tokens around. return Ok(()); diff --git a/program/src/processor/sync_native.rs b/program/src/processor/sync_native.rs index 699970d..446a64a 100644 --- a/program/src/processor/sync_native.rs +++ b/program/src/processor/sync_native.rs @@ -1,19 +1,13 @@ -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], - _instruction_data: &[u8], -) -> ProgramResult { +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::(unsafe { native_account_info.borrow_mut_data_unchecked() diff --git a/program/src/processor/thaw_account.rs b/program/src/processor/thaw_account.rs index 3303a5f..22f7963 100644 --- a/program/src/processor/thaw_account.rs +++ b/program/src/processor/thaw_account.rs @@ -1,12 +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], - _instruction_data: &[u8], -) -> ProgramResult { - process_toggle_account_state(program_id, accounts, false) +pub fn process_thaw_account(accounts: &[AccountInfo]) -> ProgramResult { + process_toggle_account_state(accounts, false) } diff --git a/program/src/processor/transfer.rs b/program/src/processor/transfer.rs index 5ad9d7d..52af330 100644 --- a/program/src/processor/transfer.rs +++ b/program/src/processor/transfer.rs @@ -1,19 +1,14 @@ -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_transfer( - program_id: &Pubkey, - accounts: &[AccountInfo], - instruction_data: &[u8], -) -> ProgramResult { - if instruction_data.len() != 8 { - return Err(ProgramError::InvalidInstructionData); - } - let amount = unsafe { (instruction_data.as_ptr() as *const u64).read_unaligned() }; +#[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(program_id, accounts, amount, None) + shared::transfer::process_transfer(accounts, amount, None) } diff --git a/program/src/processor/transfer_checked.rs b/program/src/processor/transfer_checked.rs index 22199f4..38ff9b9 100644 --- a/program/src/processor/transfer_checked.rs +++ b/program/src/processor/transfer_checked.rs @@ -1,20 +1,26 @@ -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_transfer_checked( - program_id: &Pubkey, accounts: &[AccountInfo], instruction_data: &[u8], ) -> ProgramResult { - if instruction_data.len() != 9 { - return Err(ProgramError::InvalidInstructionData); - } - let amount = unsafe { (instruction_data.as_ptr() as *const u64).read_unaligned() }; - let decimals = unsafe { *instruction_data.as_ptr().add(8) }; + let (amount, decimals) = instruction_data.split_at(core::mem::size_of::()); + let amount = u64::from_le_bytes( + amount + .try_into() + .map_err(|_error| ProgramError::InvalidInstructionData)?, + ); - shared::transfer::process_transfer(program_id, accounts, amount, Some(decimals)) + shared::transfer::process_transfer( + accounts, + amount, + Some( + *decimals + .first() + .ok_or(ProgramError::InvalidInstructionData)?, + ), + ) } diff --git a/program/src/processor/ui_amount_to_amount.rs b/program/src/processor/ui_amount_to_amount.rs index 3d94257..7d67d6b 100644 --- a/program/src/processor/ui_amount_to_amount.rs +++ b/program/src/processor/ui_amount_to_amount.rs @@ -1,8 +1,7 @@ use core::str; 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}; @@ -10,7 +9,6 @@ use super::{check_account_owner, try_ui_amount_into_amount}; #[inline(never)] pub fn process_ui_amount_to_amount( - program_id: &Pubkey, accounts: &[AccountInfo], instruction_data: &[u8], ) -> ProgramResult { @@ -18,7 +16,7 @@ pub fn process_ui_amount_to_amount( 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::(unsafe { mint_info.borrow_mut_data_unchecked() }) From b62292024a078020668f01fe395dc9630883665d Mon Sep 17 00:00:00 2001 From: febo Date: Sat, 23 Nov 2024 01:17:33 +0000 Subject: [PATCH 3/7] Update compute table --- README.md | 40 +++++++++---------- program/src/processor/amount_to_ui_amount.rs | 2 +- program/src/processor/approve.rs | 2 +- program/src/processor/approve_checked.rs | 2 +- program/src/processor/burn.rs | 2 +- program/src/processor/burn_checked.rs | 2 +- program/src/processor/freeze_account.rs | 2 +- .../src/processor/get_account_data_size.rs | 2 +- program/src/processor/initialize_account2.rs | 2 +- .../processor/initialize_immutable_owner.rs | 2 +- program/src/processor/initialize_multisig.rs | 2 +- program/src/processor/initialize_multisig2.rs | 2 +- program/src/processor/mint_to_checked.rs | 2 +- program/src/processor/revoke.rs | 2 +- program/src/processor/set_authority.rs | 6 +-- program/src/processor/sync_native.rs | 2 +- program/src/processor/thaw_account.rs | 2 +- program/src/processor/transfer_checked.rs | 2 +- program/src/processor/ui_amount_to_amount.rs | 2 +- 19 files changed, 40 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index bb1fbcc..b00fc81 100644 --- a/README.md +++ b/README.md @@ -23,27 +23,27 @@ This repository contains a **proof-of-concept** of a reimplementation of the SPL | Instruction | Completed | CU (`p-token`) | CU (`spl-token`) | |----------------------------|-----------|----------------|------------------| -| `InitializeMint` | ✅ | 378 | 2967 | -| `InitializeAccount` | ✅ | 445 | 4527 | -| `InitializeMultisig` | ✅ | 464 | 2973 | -| `Transfer` | ✅ | 197 | 4645 | -| `Approve` | ✅ | 151 | 2904 | -| `Revoke` | ✅ | 93 | 2677 | -| `SetAuthority` | ✅ | 171 | 3167 | -| `MintTo` | ✅ | 196 | 4538 | -| `Burn` | ✅ | 184 | 4753 | -| `CloseAccount` | ✅ | 163 | 2916 | -| `FreezeAccount` | ✅ | 131 | 4265 | -| `ThawAccount` | ✅ | 132 | 4267 | -| `TransferChecked` | ✅ | 254 | 6201 | -| `ApproveChecked` | ✅ | 166 | 4459 | -| `MintToChecked` | ✅ | 180 | 4546 | -| `BurnChecked` | ✅ | 166 | 4755 | -| `InitializeAccount2` | ✅ | 430 | 4388 | +| `InitializeMint` | ✅ | 361 | 2967 | +| `InitializeAccount` | ✅ | 430 | 4527 | +| `InitializeMultisig` | ✅ | 454 | 2973 | +| `Transfer` | ✅ | 159 | 4645 | +| `Approve` | ✅ | 144 | 2904 | +| `Revoke` | ✅ | 110 | 2677 | +| `SetAuthority` | ✅ | 153 | 3167 | +| `MintTo` | ✅ | 160 | 4538 | +| `Burn` | ✅ | 180 | 4753 | +| `CloseAccount` | ✅ | 158 | 2916 | +| `FreezeAccount` | ✅ | 149 | 4265 | +| `ThawAccount` | ✅ | 150 | 4267 | +| `TransferChecked` | ✅ | 224 | 6201 | +| `ApproveChecked` | ✅ | 185 | 4459 | +| `MintToChecked` | ✅ | 191 | 4546 | +| `BurnChecked` | ✅ | 182 | 4755 | +| `InitializeAccount2` | ✅ | 414 | 4388 | | `SyncNative` | ✅ | | | -| `InitializeAccount3` | ✅ | 560 | 4240 | -| `InitializeMultisig2` | ✅ | 591 | 2826 | -| `InitializeMint2` | ✅ | 500 | 2827 | +| `InitializeAccount3` | ✅ | 518 | 4240 | +| `InitializeMultisig2` | ✅ | 584 | 2826 | +| `InitializeMint2` | ✅ | 495 | 2827 | | `GetAccountDataSize` | ✅ | | | | `InitializeImmutableOwner` | ✅ | | | | `AmountToUiAmount` | ✅ | | | diff --git a/program/src/processor/amount_to_ui_amount.rs b/program/src/processor/amount_to_ui_amount.rs index 6c61906..e92c86c 100644 --- a/program/src/processor/amount_to_ui_amount.rs +++ b/program/src/processor/amount_to_ui_amount.rs @@ -5,7 +5,7 @@ use token_interface::{error::TokenError, 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( accounts: &[AccountInfo], instruction_data: &[u8], diff --git a/program/src/processor/approve.rs b/program/src/processor/approve.rs index 4284e09..10c61ed 100644 --- a/program/src/processor/approve.rs +++ b/program/src/processor/approve.rs @@ -2,7 +2,7 @@ use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramR use super::shared; -#[inline(never)] +#[inline(always)] pub fn process_approve(accounts: &[AccountInfo], instruction_data: &[u8]) -> ProgramResult { let amount = u64::from_le_bytes( instruction_data diff --git a/program/src/processor/approve_checked.rs b/program/src/processor/approve_checked.rs index 0eacc01..580a93d 100644 --- a/program/src/processor/approve_checked.rs +++ b/program/src/processor/approve_checked.rs @@ -2,7 +2,7 @@ use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramR use super::shared; -#[inline(never)] +#[inline(always)] pub fn process_approve_checked(accounts: &[AccountInfo], instruction_data: &[u8]) -> ProgramResult { let (amount, decimals) = instruction_data.split_at(core::mem::size_of::()); let amount = u64::from_le_bytes( diff --git a/program/src/processor/burn.rs b/program/src/processor/burn.rs index 31d05a2..6ceed84 100644 --- a/program/src/processor/burn.rs +++ b/program/src/processor/burn.rs @@ -2,7 +2,7 @@ use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramR use super::shared; -#[inline(never)] +#[inline(always)] pub fn process_burn(accounts: &[AccountInfo], instruction_data: &[u8]) -> ProgramResult { let amount = u64::from_le_bytes( instruction_data diff --git a/program/src/processor/burn_checked.rs b/program/src/processor/burn_checked.rs index 07e2002..f9ef06c 100644 --- a/program/src/processor/burn_checked.rs +++ b/program/src/processor/burn_checked.rs @@ -2,7 +2,7 @@ use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramR use super::shared; -#[inline(never)] +#[inline(always)] pub fn process_burn_checked(accounts: &[AccountInfo], instruction_data: &[u8]) -> ProgramResult { let (amount, decimals) = instruction_data.split_at(core::mem::size_of::()); let amount = u64::from_le_bytes( diff --git a/program/src/processor/freeze_account.rs b/program/src/processor/freeze_account.rs index cff2a3d..5637611 100644 --- a/program/src/processor/freeze_account.rs +++ b/program/src/processor/freeze_account.rs @@ -2,7 +2,7 @@ use pinocchio::{account_info::AccountInfo, ProgramResult}; use super::shared::toggle_account_state::process_toggle_account_state; -#[inline(never)] +#[inline(always)] pub fn process_freeze_account(accounts: &[AccountInfo]) -> ProgramResult { process_toggle_account_state(accounts, true) } diff --git a/program/src/processor/get_account_data_size.rs b/program/src/processor/get_account_data_size.rs index 2d51862..33aed74 100644 --- a/program/src/processor/get_account_data_size.rs +++ b/program/src/processor/get_account_data_size.rs @@ -5,7 +5,7 @@ use token_interface::state::{account::Account, mint::Mint}; use super::check_account_owner; -#[inline(never)] +#[inline(always)] pub fn process_get_account_data_size(accounts: &[AccountInfo]) -> ProgramResult { let [mint_info, _remaning @ ..] = accounts else { return Err(ProgramError::NotEnoughAccountKeys); diff --git a/program/src/processor/initialize_account2.rs b/program/src/processor/initialize_account2.rs index ed3130a..9200ee0 100644 --- a/program/src/processor/initialize_account2.rs +++ b/program/src/processor/initialize_account2.rs @@ -2,7 +2,7 @@ use pinocchio::{account_info::AccountInfo, pubkey::Pubkey, ProgramResult}; use super::shared; -#[inline(never)] +#[inline(always)] pub fn process_initialize_account2( accounts: &[AccountInfo], instruction_data: &[u8], diff --git a/program/src/processor/initialize_immutable_owner.rs b/program/src/processor/initialize_immutable_owner.rs index cdbc688..bb730f9 100644 --- a/program/src/processor/initialize_immutable_owner.rs +++ b/program/src/processor/initialize_immutable_owner.rs @@ -1,7 +1,7 @@ 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)?; diff --git a/program/src/processor/initialize_multisig.rs b/program/src/processor/initialize_multisig.rs index 703abb1..a5f888b 100644 --- a/program/src/processor/initialize_multisig.rs +++ b/program/src/processor/initialize_multisig.rs @@ -2,7 +2,7 @@ use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramR use super::shared; -#[inline(never)] +#[inline(always)] pub fn process_initialize_multisig( accounts: &[AccountInfo], instruction_data: &[u8], diff --git a/program/src/processor/initialize_multisig2.rs b/program/src/processor/initialize_multisig2.rs index ada297e..138a91b 100644 --- a/program/src/processor/initialize_multisig2.rs +++ b/program/src/processor/initialize_multisig2.rs @@ -2,7 +2,7 @@ use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramR use super::shared; -#[inline(never)] +#[inline(always)] pub fn process_initialize_multisig2( accounts: &[AccountInfo], instruction_data: &[u8], diff --git a/program/src/processor/mint_to_checked.rs b/program/src/processor/mint_to_checked.rs index bc4dfb8..6fb3ae9 100644 --- a/program/src/processor/mint_to_checked.rs +++ b/program/src/processor/mint_to_checked.rs @@ -2,7 +2,7 @@ use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramR use super::shared; -#[inline(never)] +#[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::()); diff --git a/program/src/processor/revoke.rs b/program/src/processor/revoke.rs index f4ac1ae..fcd06a0 100644 --- a/program/src/processor/revoke.rs +++ b/program/src/processor/revoke.rs @@ -3,7 +3,7 @@ use token_interface::{error::TokenError, state::account::Account}; use super::validate_owner; -#[inline(never)] +#[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); diff --git a/program/src/processor/set_authority.rs b/program/src/processor/set_authority.rs index e286de5..1a39dce 100644 --- a/program/src/processor/set_authority.rs +++ b/program/src/processor/set_authority.rs @@ -11,7 +11,7 @@ use token_interface::{ use super::validate_owner; -#[inline(never)] +#[inline(always)] pub fn process_set_authority(accounts: &[AccountInfo], instruction_data: &[u8]) -> ProgramResult { let args = SetAuthority::try_from_bytes(instruction_data)?; let authority_type = args.authority_type(); @@ -127,10 +127,10 @@ impl SetAuthority<'_> { #[inline(always)] pub fn new_authority(&self) -> Option<&Pubkey> { unsafe { - if *self.raw.add(33) == 0 { + if *self.raw.add(1) == 0 { Option::None } else { - Option::Some(&*(self.raw.add(34) as *const Pubkey)) + Option::Some(&*(self.raw.add(2) as *const Pubkey)) } } } diff --git a/program/src/processor/sync_native.rs b/program/src/processor/sync_native.rs index 446a64a..d4961b8 100644 --- a/program/src/processor/sync_native.rs +++ b/program/src/processor/sync_native.rs @@ -3,7 +3,7 @@ use token_interface::{error::TokenError, state::account::Account}; use super::check_account_owner; -#[inline(never)] +#[inline(always)] pub fn process_sync_native(accounts: &[AccountInfo]) -> ProgramResult { let native_account_info = accounts.first().ok_or(ProgramError::NotEnoughAccountKeys)?; diff --git a/program/src/processor/thaw_account.rs b/program/src/processor/thaw_account.rs index 22f7963..924d3b6 100644 --- a/program/src/processor/thaw_account.rs +++ b/program/src/processor/thaw_account.rs @@ -2,7 +2,7 @@ use pinocchio::{account_info::AccountInfo, ProgramResult}; use super::shared::toggle_account_state::process_toggle_account_state; -#[inline(never)] +#[inline(always)] pub fn process_thaw_account(accounts: &[AccountInfo]) -> ProgramResult { process_toggle_account_state(accounts, false) } diff --git a/program/src/processor/transfer_checked.rs b/program/src/processor/transfer_checked.rs index 38ff9b9..4c23ee5 100644 --- a/program/src/processor/transfer_checked.rs +++ b/program/src/processor/transfer_checked.rs @@ -2,7 +2,7 @@ use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramR use super::shared; -#[inline(never)] +#[inline(always)] pub fn process_transfer_checked( accounts: &[AccountInfo], instruction_data: &[u8], diff --git a/program/src/processor/ui_amount_to_amount.rs b/program/src/processor/ui_amount_to_amount.rs index 7d67d6b..3dfea69 100644 --- a/program/src/processor/ui_amount_to_amount.rs +++ b/program/src/processor/ui_amount_to_amount.rs @@ -7,7 +7,7 @@ use token_interface::{error::TokenError, state::mint::Mint}; use super::{check_account_owner, try_ui_amount_into_amount}; -#[inline(never)] +#[inline(always)] pub fn process_ui_amount_to_amount( accounts: &[AccountInfo], instruction_data: &[u8], From 55a6fee0000a987c0758a6aa00546740b203edd2 Mon Sep 17 00:00:00 2001 From: febo Date: Sun, 24 Nov 2024 11:02:09 +0000 Subject: [PATCH 4/7] Remove bytemuck --- Cargo.lock | 1 - interface/src/instruction.rs | 20 +- program/Cargo.toml | 1 - program/src/lib.rs | 2 + program/src/processor/amount_to_ui_amount.rs | 16 +- program/src/processor/close_account.rs | 16 +- .../src/processor/get_account_data_size.rs | 7 +- .../processor/initialize_immutable_owner.rs | 10 +- program/src/processor/initialize_mint.rs | 27 ++- program/src/processor/mod.rs | 10 +- program/src/processor/revoke.rs | 14 +- program/src/processor/set_authority.rs | 70 ++++--- program/src/processor/shared/approve.rs | 30 +-- program/src/processor/shared/burn.rs | 53 +++-- .../processor/shared/initialize_account.rs | 52 ++--- .../processor/shared/initialize_multisig.rs | 19 +- program/src/processor/shared/mint_to.rs | 36 ++-- program/src/processor/shared/mod.rs | 5 + .../processor/shared/toggle_account_state.rs | 32 ++- program/src/processor/shared/transfer.rs | 75 +++---- program/src/processor/sync_native.rs | 18 +- program/src/processor/ui_amount_to_amount.rs | 6 +- program/src/state/account.rs | 196 ++++++++++++++++++ program/src/state/account_state.rs | 15 ++ program/src/state/mint.rs | 157 ++++++++++++++ program/src/state/mod.rs | 7 + program/src/state/multisig.rs | 108 ++++++++++ 27 files changed, 739 insertions(+), 264 deletions(-) create mode 100644 program/src/state/account.rs create mode 100644 program/src/state/account_state.rs create mode 100644 program/src/state/mint.rs create mode 100644 program/src/state/mod.rs create mode 100644 program/src/state/multisig.rs diff --git a/Cargo.lock b/Cargo.lock index 5e0fd7b..649a9fc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5216,7 +5216,6 @@ name = "token-program" version = "0.0.0" dependencies = [ "assert_matches", - "bytemuck", "pinocchio", "pinocchio-pubkey", "solana-program-test", diff --git a/interface/src/instruction.rs b/interface/src/instruction.rs index c78e6bb..6c5e8bc 100644 --- a/interface/src/instruction.rs +++ b/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, state::PodCOption}; /// Instructions supported by the token program. #[repr(C)] @@ -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 { 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()), } } } diff --git a/program/Cargo.toml b/program/Cargo.toml index 1e2af78..a2608d7 100644 --- a/program/Cargo.toml +++ b/program/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" } diff --git a/program/src/lib.rs b/program/src/lib.rs index 3a174fb..914afde 100644 --- a/program/src/lib.rs +++ b/program/src/lib.rs @@ -2,5 +2,7 @@ mod entrypoint; mod processor; +#[allow(unused)] +mod state; pinocchio_pubkey::declare_id!("PToken1111111111111111111111111111111111111"); diff --git a/program/src/processor/amount_to_ui_amount.rs b/program/src/processor/amount_to_ui_amount.rs index e92c86c..088a97b 100644 --- a/program/src/processor/amount_to_ui_amount.rs +++ b/program/src/processor/amount_to_ui_amount.rs @@ -1,7 +1,8 @@ use pinocchio::{ account_info::AccountInfo, program::set_return_data, program_error::ProgramError, ProgramResult, }; -use token_interface::{error::TokenError, state::mint::Mint}; + +use crate::state::mint::Mint; use super::{amount_to_ui_amount_string_trimmed, check_account_owner}; @@ -10,17 +11,16 @@ pub fn process_amount_to_ui_amount( accounts: &[AccountInfo], instruction_data: &[u8], ) -> ProgramResult { - if instruction_data.len() != 8 { - return Err(ProgramError::InvalidInstructionData); - } - let amount = unsafe { (instruction_data.as_ptr() as *const u64).read_unaligned() }; + 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(mint_info)?; - let mint = - bytemuck::try_from_bytes_mut::(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()); diff --git a/program/src/processor/close_account.rs b/program/src/processor/close_account.rs index 7a6737f..d93f62a 100644 --- a/program/src/processor/close_account.rs +++ b/program/src/processor/close_account.rs @@ -1,5 +1,7 @@ use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramResult}; -use token_interface::{error::TokenError, state::account::Account}; +use token_interface::error::TokenError; + +use crate::state::account::Account; use super::{is_owned_by_system_program_or_incinerator, validate_owner, INCINERATOR_ID}; @@ -15,20 +17,18 @@ pub fn process_close_account(accounts: &[AccountInfo]) -> ProgramResult { } let source_account = - bytemuck::try_from_bytes::(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(&authority, authority_info, remaining)?; + validate_owner(authority, authority_info, remaining)?; } else if destination_account_info.key() != &INCINERATOR_ID { return Err(ProgramError::InvalidAccountData); } diff --git a/program/src/processor/get_account_data_size.rs b/program/src/processor/get_account_data_size.rs index 33aed74..345476e 100644 --- a/program/src/processor/get_account_data_size.rs +++ b/program/src/processor/get_account_data_size.rs @@ -1,7 +1,8 @@ use pinocchio::{ account_info::AccountInfo, program::set_return_data, program_error::ProgramError, ProgramResult, }; -use token_interface::state::{account::Account, mint::Mint}; + +use crate::state::{account::Account, mint::Mint}; use super::check_account_owner; @@ -11,10 +12,10 @@ pub fn process_get_account_data_size(accounts: &[AccountInfo]) -> ProgramResult return Err(ProgramError::NotEnoughAccountKeys); }; + // Make sure the mint is valid. check_account_owner(mint_info)?; - let _ = bytemuck::try_from_bytes::(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()); diff --git a/program/src/processor/initialize_immutable_owner.rs b/program/src/processor/initialize_immutable_owner.rs index bb730f9..cd34438 100644 --- a/program/src/processor/initialize_immutable_owner.rs +++ b/program/src/processor/initialize_immutable_owner.rs @@ -1,14 +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; + +use crate::state::account::Account; #[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::(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()); diff --git a/program/src/processor/initialize_mint.rs b/program/src/processor/initialize_mint.rs index ae179d3..59f7c5b 100644 --- a/program/src/processor/initialize_mint.rs +++ b/program/src/processor/initialize_mint.rs @@ -6,10 +6,9 @@ use pinocchio::{ sysvars::{rent::Rent, Sysvar}, ProgramResult, }; -use token_interface::{ - error::TokenError, - state::{mint::Mint, PodCOption}, -}; +use token_interface::error::TokenError; + +use crate::state::mint::Mint; #[inline(always)] pub fn process_initialize_mint( @@ -17,8 +16,12 @@ pub fn process_initialize_mint( instruction_data: &[u8], rent_sysvar_account: bool, ) -> ProgramResult { + // Validates the instruction data. + 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); @@ -31,11 +34,9 @@ pub fn process_initialize_mint( (mint_info, None) }; - let mint = - bytemuck::try_from_bytes_mut::(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.is_initialized.into() { + if mint.is_initialized() { return Err(TokenError::AlreadyInUse.into()); } @@ -54,12 +55,12 @@ pub fn process_initialize_mint( // Initialize the mint. - mint.mint_authority = PodCOption::from(Some(*args.mint_authority())); + mint.set_initialized(true); + mint.set_mint_authority(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)); + mint.set_freeze_authority(freeze_authority); } Ok(()) @@ -73,6 +74,7 @@ pub struct InitializeMint<'a> { } impl InitializeMint<'_> { + #[inline] pub fn try_from_bytes(bytes: &[u8]) -> Result { // The minimum expected size of the instruction data. // - decimals (1 byte) @@ -88,14 +90,17 @@ impl InitializeMint<'_> { }) } + #[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 { diff --git a/program/src/processor/mod.rs b/program/src/processor/mod.rs index 0494d79..6156364 100644 --- a/program/src/processor/mod.rs +++ b/program/src/processor/mod.rs @@ -1,10 +1,7 @@ use pinocchio::{ account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey, ProgramResult, }; -use token_interface::{ - error::TokenError, - state::multisig::{Multisig, MAX_SIGNERS}, -}; +use token_interface::error::TokenError; pub mod amount_to_ui_amount; pub mod approve; @@ -60,6 +57,8 @@ pub use transfer::process_transfer; pub use transfer_checked::process_transfer_checked; pub use ui_amount_to_amount::process_ui_amount_to_amount; +use crate::state::multisig::{Multisig, MAX_SIGNERS}; + /// Incinerator address. const INCINERATOR_ID: Pubkey = pinocchio_pubkey::pubkey!("1nc1nerator11111111111111111111111111111111"); @@ -94,8 +93,7 @@ fn validate_owner( } if owner_account_info.data_len() == Multisig::LEN && &crate::ID != owner_account_info.owner() { - let multisig_data = owner_account_info.try_borrow_data()?; - let multisig = bytemuck::from_bytes::(&multisig_data); + let multisig = unsafe { Multisig::from_bytes(owner_account_info.borrow_data_unchecked()) }; let mut num_signers = 0; let mut matched = [false; MAX_SIGNERS]; diff --git a/program/src/processor/revoke.rs b/program/src/processor/revoke.rs index fcd06a0..4d6b38f 100644 --- a/program/src/processor/revoke.rs +++ b/program/src/processor/revoke.rs @@ -1,5 +1,7 @@ use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramResult}; -use token_interface::{error::TokenError, state::account::Account}; +use token_interface::error::TokenError; + +use crate::state::account::Account; use super::validate_owner; @@ -9,10 +11,8 @@ pub fn process_revoke(accounts: &[AccountInfo], _instruction_data: &[u8]) -> Pro return Err(ProgramError::NotEnoughAccountKeys); }; - let source_account = bytemuck::try_from_bytes_mut::(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()); @@ -20,8 +20,8 @@ pub fn process_revoke(accounts: &[AccountInfo], _instruction_data: &[u8]) -> Pro 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(()) } diff --git a/program/src/processor/set_authority.rs b/program/src/processor/set_authority.rs index 1a39dce..fc56905 100644 --- a/program/src/processor/set_authority.rs +++ b/program/src/processor/set_authority.rs @@ -3,29 +3,29 @@ use core::marker::PhantomData; use pinocchio::{ account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey, ProgramResult, }; -use token_interface::{ - error::TokenError, - instruction::AuthorityType, - state::{account::Account, mint::Mint, PodCOption}, -}; +use token_interface::{error::TokenError, instruction::AuthorityType}; + +use crate::state::{account::Account, mint::Mint}; use super::validate_owner; #[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 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::(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()); @@ -41,50 +41,58 @@ pub fn process_set_authority(accounts: &[AccountInfo], instruction_data: &[u8]) 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); + let authority = account.close_authority().unwrap_or(&account.owner); validate_owner(authority, authority_info, remaning)?; - account.close_authority = PodCOption::from(new_authority.copied()); + + 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::(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)?; + // mint_authority. + let mint_authority = mint.mint_authority().ok_or(TokenError::FixedSupply)?; validate_owner(mint_authority, authority_info, remaning)?; - mint.mint_authority = PodCOption::from(new_authority.copied()); + + 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(freeze_authority, authority_info, remaning)?; - mint.freeze_authority = PodCOption::from(new_authority.copied()); + + if let Some(authority) = new_authority { + mint.set_freeze_authority(authority); + } else { + mint.clear_freeze_authority(); + } } _ => { return Err(TokenError::AuthorityTypeNotSupported.into()); @@ -120,7 +128,7 @@ impl SetAuthority<'_> { } #[inline(always)] - pub fn authority_type(&self) -> AuthorityType { + pub fn authority_type(&self) -> Result { unsafe { AuthorityType::from(*self.raw) } } diff --git a/program/src/processor/shared/approve.rs b/program/src/processor/shared/approve.rs index b0f0061..db3b907 100644 --- a/program/src/processor/shared/approve.rs +++ b/program/src/processor/shared/approve.rs @@ -1,10 +1,10 @@ use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramResult}; -use token_interface::{ - error::TokenError, - state::{account::Account, mint::Mint, PodCOption}, -}; +use token_interface::error::TokenError; -use crate::processor::validate_owner; +use crate::{ + processor::validate_owner, + state::{account::Account, mint::Mint}, +}; #[inline(always)] pub fn process_approve( @@ -12,6 +12,9 @@ pub fn process_approve( amount: u64, expected_decimals: Option, ) -> 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 @ ..] = @@ -40,10 +43,10 @@ pub fn process_approve( ) }; - let source_account = bytemuck::try_from_bytes_mut::(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()); @@ -54,8 +57,7 @@ pub fn process_approve( return Err(TokenError::MintMismatch.into()); } - let mint = bytemuck::try_from_bytes::(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()); @@ -64,8 +66,10 @@ pub fn process_approve( validate_owner(&source_account.owner, owner_info, remaining)?; - source_account.delegate = PodCOption::some(*delegate_info.key()); - source_account.delegated_amount = amount.into(); + // Sets the delegate and delegated amount. + + source_account.set_delegate(delegate_info.key()); + source_account.set_delegated_amount(amount); Ok(()) } diff --git a/program/src/processor/shared/burn.rs b/program/src/processor/shared/burn.rs index f34c8e5..06c98ef 100644 --- a/program/src/processor/shared/burn.rs +++ b/program/src/processor/shared/burn.rs @@ -1,11 +1,9 @@ use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramResult}; -use token_interface::{ - error::TokenError, - state::{account::Account, mint::Mint}, -}; +use token_interface::error::TokenError; -use crate::processor::{ - check_account_owner, is_owned_by_system_program_or_incinerator, validate_owner, +use crate::{ + processor::{check_account_owner, is_owned_by_system_program_or_incinerator, validate_owner}, + state::{account::Account, mint::Mint}, }; #[inline(always)] @@ -18,29 +16,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::(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::(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()); @@ -53,17 +46,18 @@ 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(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(); } } _ => { @@ -72,17 +66,20 @@ pub fn process_burn( } } + // Updates the source account and mint supply. + if amount == 0 { check_account_owner(source_account_info)?; check_account_owner(mint_info)?; - } - - source_account.amount = updated_source_amount.into(); + } 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(()) } diff --git a/program/src/processor/shared/initialize_account.rs b/program/src/processor/shared/initialize_account.rs index 45e3506..86e9356 100644 --- a/program/src/processor/shared/initialize_account.rs +++ b/program/src/processor/shared/initialize_account.rs @@ -6,17 +6,12 @@ use pinocchio::{ sysvars::{rent::Rent, Sysvar}, ProgramResult, }; -use token_interface::{ - error::TokenError, - native_mint::is_native_mint, - state::{ - account::{Account, AccountState}, - mint::Mint, - PodCOption, - }, -}; +use token_interface::{error::TokenError, native_mint::is_native_mint}; -use crate::processor::check_account_owner; +use crate::{ + processor::check_account_owner, + state::{account::Account, account_state::AccountState, mint::Mint}, +}; #[inline(always)] pub fn process_initialize_account( @@ -24,6 +19,8 @@ pub fn process_initialize_account( 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); @@ -50,9 +47,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_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()); @@ -63,38 +60,31 @@ pub fn process_initialize_account( if !is_native_mint { check_account_owner(mint_info)?; - let mint_data = unsafe { mint_info.borrow_data_unchecked() }; - let mint = bytemuck::try_from_bytes::(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.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(()) } diff --git a/program/src/processor/shared/initialize_multisig.rs b/program/src/processor/shared/initialize_multisig.rs index 8db95f1..f828631 100644 --- a/program/src/processor/shared/initialize_multisig.rs +++ b/program/src/processor/shared/initialize_multisig.rs @@ -4,7 +4,9 @@ use pinocchio::{ sysvars::{rent::Rent, Sysvar}, ProgramResult, }; -use token_interface::{error::TokenError, state::multisig::Multisig}; +use token_interface::error::TokenError; + +use crate::state::multisig::Multisig; #[inline(always)] pub fn process_initialize_multisig( @@ -12,6 +14,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 +41,14 @@ pub fn process_initialize_multisig( return Err(TokenError::NotRentExempt.into()); } - let multisig = bytemuck::try_from_bytes_mut::(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 +58,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(()) } diff --git a/program/src/processor/shared/mint_to.rs b/program/src/processor/shared/mint_to.rs index 2fa2776..2158a1d 100644 --- a/program/src/processor/shared/mint_to.rs +++ b/program/src/processor/shared/mint_to.rs @@ -1,12 +1,11 @@ use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramResult}; -use token_interface::{ - error::TokenError, - native_mint::is_native_mint, +use token_interface::error::TokenError; + +use crate::{ + processor::{check_account_owner, validate_owner}, state::{account::Account, mint::Mint}, }; -use crate::processor::{check_account_owner, validate_owner}; - #[inline(always)] pub fn process_mint_to( accounts: &[AccountInfo], @@ -17,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_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()); } @@ -35,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_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 { @@ -45,8 +41,8 @@ pub fn process_mint_to( } } - match mint.mint_authority.get() { - Some(mint_authority) => validate_owner(&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()), } @@ -55,15 +51,17 @@ pub fn process_mint_to( 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(()) } diff --git a/program/src/processor/shared/mod.rs b/program/src/processor/shared/mod.rs index 58e042f..e55f129 100644 --- a/program/src/processor/shared/mod.rs +++ b/program/src/processor/shared/mod.rs @@ -1,3 +1,8 @@ +//! 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; diff --git a/program/src/processor/shared/toggle_account_state.rs b/program/src/processor/shared/toggle_account_state.rs index 5bb73d0..e9fd186 100644 --- a/program/src/processor/shared/toggle_account_state.rs +++ b/program/src/processor/shared/toggle_account_state.rs @@ -1,13 +1,10 @@ use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramResult}; -use token_interface::{ - error::TokenError, - state::{ - account::{Account, AccountState}, - mint::Mint, - }, -}; +use token_interface::error::TokenError; -use crate::processor::validate_owner; +use crate::{ + processor::validate_owner, + state::{account::Account, account_state::AccountState, mint::Mint}, +}; #[inline(always)] pub fn process_toggle_account_state(accounts: &[AccountInfo], freeze: bool) -> ProgramResult { @@ -15,34 +12,31 @@ pub fn process_toggle_account_state(accounts: &[AccountInfo], freeze: bool) -> P return Err(ProgramError::NotEnoughAccountKeys); }; - let source_account = bytemuck::try_from_bytes_mut::(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::(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(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(()) } diff --git a/program/src/processor/shared/transfer.rs b/program/src/processor/shared/transfer.rs index 4840b93..e13940e 100644 --- a/program/src/processor/shared/transfer.rs +++ b/program/src/processor/shared/transfer.rs @@ -1,11 +1,10 @@ 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}, -}; +use token_interface::error::TokenError; -use crate::processor::{check_account_owner, validate_owner}; +use crate::{ + processor::{check_account_owner, validate_owner}, + state::{account::Account, mint::Mint}, +}; #[inline(always)] pub fn process_transfer( @@ -13,7 +12,7 @@ pub fn process_transfer( amount: u64, expected_decimals: Option, ) -> 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 ( @@ -52,24 +51,21 @@ pub fn process_transfer( // Validates source and destination accounts. - let source_account = bytemuck::try_from_bytes_mut::(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::(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)?; @@ -84,31 +80,32 @@ pub fn process_transfer( return Err(TokenError::MintMismatch.into()); } - let mint = - bytemuck::try_from_bytes_mut::(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()); } } + // 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()) { + 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 { @@ -116,40 +113,32 @@ pub fn process_transfer( } if self_transfer || amount == 0 { + // 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)?; - // No need to move tokens around. - return Ok(()); - } - - // 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)?; diff --git a/program/src/processor/sync_native.rs b/program/src/processor/sync_native.rs index d4961b8..a8e4dac 100644 --- a/program/src/processor/sync_native.rs +++ b/program/src/processor/sync_native.rs @@ -1,5 +1,7 @@ use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramResult}; -use token_interface::{error::TokenError, state::account::Account}; +use token_interface::error::TokenError; + +use crate::state::account::Account; use super::check_account_owner; @@ -9,21 +11,19 @@ pub fn process_sync_native(accounts: &[AccountInfo]) -> ProgramResult { check_account_owner(native_account_info)?; - let native_account = bytemuck::try_from_bytes_mut::(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()); } diff --git a/program/src/processor/ui_amount_to_amount.rs b/program/src/processor/ui_amount_to_amount.rs index 3dfea69..312cc64 100644 --- a/program/src/processor/ui_amount_to_amount.rs +++ b/program/src/processor/ui_amount_to_amount.rs @@ -1,9 +1,9 @@ use core::str; +use crate::state::mint::Mint; use pinocchio::{ account_info::AccountInfo, program::set_return_data, program_error::ProgramError, ProgramResult, }; -use token_interface::{error::TokenError, state::mint::Mint}; use super::{check_account_owner, try_ui_amount_into_amount}; @@ -18,9 +18,7 @@ pub fn process_ui_amount_to_amount( let mint_info = accounts.first().ok_or(ProgramError::NotEnoughAccountKeys)?; check_account_owner(mint_info)?; - let mint = - bytemuck::try_from_bytes_mut::(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()); diff --git a/program/src/state/account.rs b/program/src/state/account.rs new file mode 100644 index 0000000..2b7da6d --- /dev/null +++ b/program/src/state/account.rs @@ -0,0 +1,196 @@ +use pinocchio::{ + account_info::{AccountInfo, Ref}, + program_error::ProgramError, + pubkey::Pubkey, +}; + +use crate::ID; + +use super::{account_state::AccountState, COption}; + +/// Internal representation of a token account data. +#[repr(C)] +pub struct Account { + /// The mint associated with this account + pub mint: Pubkey, + + /// The owner of this account. + pub owner: Pubkey, + + /// The amount of tokens this account holds. + amount: [u8; 8], + + /// If `delegate` is `Some` then `delegated_amount` represents + /// the amount authorized by the delegate. + delegate: COption, + + /// The account's state. + pub state: AccountState, + + /// 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. + native_amount: [u8; 8], + + /// The amount delegated. + delegated_amount: [u8; 8], + + /// Optional authority to close the account. + close_authority: COption, +} + +impl Account { + pub const LEN: usize = core::mem::size_of::(); + + /// 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, 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] + pub fn amount(&self) -> u64 { + u64::from_le_bytes(self.amount) + } + + #[inline] + pub fn clear_delegate(&mut self) { + self.delegate.0[0] = 0; + } + + #[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 + } + } + + #[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 { + 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 + } +} diff --git a/program/src/state/account_state.rs b/program/src/state/account_state.rs new file mode 100644 index 0000000..6747757 --- /dev/null +++ b/program/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, +} diff --git a/program/src/state/mint.rs b/program/src/state/mint.rs new file mode 100644 index 0000000..0b628a7 --- /dev/null +++ b/program/src/state/mint.rs @@ -0,0 +1,157 @@ +use pinocchio::{ + account_info::{AccountInfo, Ref}, + program_error::ProgramError, + pubkey::Pubkey, +}; + +use crate::ID; + +use super::COption; + +/// Internal representation of a mint data. +#[repr(C)] +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: COption, + + /// Total supply of tokens. + 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. + 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: COption, +} + +impl Mint { + /// The length of the `Mint` account data. + pub const LEN: usize = core::mem::size_of::(); + + /// 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, 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 + } + } +} diff --git a/program/src/state/mod.rs b/program/src/state/mod.rs new file mode 100644 index 0000000..04c0342 --- /dev/null +++ b/program/src/state/mod.rs @@ -0,0 +1,7 @@ +pub mod account; +pub mod account_state; +pub mod mint; +pub mod multisig; + +/// Type alias for fields represented as `COption`. +pub type COption = ([u8; 4], T); diff --git a/program/src/state/multisig.rs b/program/src/state/multisig.rs new file mode 100644 index 0000000..1543bec --- /dev/null +++ b/program/src/state/multisig.rs @@ -0,0 +1,108 @@ +use pinocchio::{ + account_info::{AccountInfo, Ref}, + program_error::ProgramError, + pubkey::Pubkey, +}; + +use crate::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)] +pub struct Multisig { + /// Number of signers required. + pub m: u8, + + /// Number of valid signers. + pub n: u8, + + /// Is `true` if this structure has been initialized + 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::(); + + /// 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, 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 + } +} From 70f39754b76d00159a9a6d35b0516705da54360d Mon Sep 17 00:00:00 2001 From: febo Date: Sun, 24 Nov 2024 11:11:22 +0000 Subject: [PATCH 5/7] Move state to interface crate --- Cargo.lock | 1 - interface/Cargo.toml | 1 - interface/src/instruction.rs | 8 +- interface/src/state/account.rs | 207 +++++++++++++----- .../src/state/account_state.rs | 0 interface/src/state/mint.rs | 149 ++++++++++++- interface/src/state/mod.rs | 161 +------------- interface/src/state/multisig.rs | 90 +++++++- program/src/lib.rs | 2 - program/src/processor/amount_to_ui_amount.rs | 3 +- program/src/processor/close_account.rs | 4 +- .../src/processor/get_account_data_size.rs | 3 +- .../processor/initialize_immutable_owner.rs | 4 +- program/src/processor/initialize_mint.rs | 4 +- program/src/processor/mod.rs | 7 +- program/src/processor/revoke.rs | 4 +- program/src/processor/set_authority.rs | 8 +- program/src/processor/shared/approve.rs | 8 +- program/src/processor/shared/burn.rs | 10 +- .../processor/shared/initialize_account.rs | 9 +- .../processor/shared/initialize_multisig.rs | 4 +- program/src/processor/shared/mint_to.rs | 8 +- .../processor/shared/toggle_account_state.rs | 8 +- program/src/processor/shared/transfer.rs | 8 +- program/src/processor/sync_native.rs | 4 +- program/src/processor/ui_amount_to_amount.rs | 8 +- program/src/state/account.rs | 196 ----------------- program/src/state/mint.rs | 157 ------------- program/src/state/mod.rs | 7 - program/src/state/multisig.rs | 108 --------- 30 files changed, 428 insertions(+), 763 deletions(-) rename {program => interface}/src/state/account_state.rs (100%) delete mode 100644 program/src/state/account.rs delete mode 100644 program/src/state/mint.rs delete mode 100644 program/src/state/mod.rs delete mode 100644 program/src/state/multisig.rs diff --git a/Cargo.lock b/Cargo.lock index 649a9fc..5325d01 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5206,7 +5206,6 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" name = "token-interface" version = "0.0.0" dependencies = [ - "bytemuck", "pinocchio", "pinocchio-pubkey", ] diff --git a/interface/Cargo.toml b/interface/Cargo.toml index 628cb2a..fb26855 100644 --- a/interface/Cargo.toml +++ b/interface/Cargo.toml @@ -8,6 +8,5 @@ repository = { workspace = true } publish = false [dependencies] -bytemuck = { workspace = true } pinocchio = { workspace = true } pinocchio-pubkey = { workspace = true } diff --git a/interface/src/instruction.rs b/interface/src/instruction.rs index 6c5e8bc..a829e9a 100644 --- a/interface/src/instruction.rs +++ b/interface/src/instruction.rs @@ -2,7 +2,7 @@ use pinocchio::{program_error::ProgramError, pubkey::Pubkey}; -use crate::{error::TokenError, 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, + freeze_authority: Option, }, /// 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, + new_authority: Option, }, /// 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, + freeze_authority: Option, }, /// Gets the required size of an account for the given mint as a diff --git a/interface/src/state/account.rs b/interface/src/state/account.rs index e88b48d..fbe6377 100644 --- a/interface/src/state/account.rs +++ b/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, + /// the amount authorized by the delegate. + delegate: COption, + + /// 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, + 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, + close_authority: COption, } impl Account { - /// Size of the `Account` account. - pub const LEN: usize = core::mem::size_of::(); + pub const LEN: usize = core::mem::size_of::(); + /// 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, 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 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 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 { + 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 + } } diff --git a/program/src/state/account_state.rs b/interface/src/state/account_state.rs similarity index 100% rename from program/src/state/account_state.rs rename to interface/src/state/account_state.rs diff --git a/interface/src/state/mint.rs b/interface/src/state/mint.rs index 50896c6..1ca3d90 100644 --- a/interface/src/state/mint.rs +++ b/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, + pub mint_authority: COption, /// 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, + pub freeze_authority: COption, } impl Mint { - /// Size of the `Mint` account. - pub const LEN: usize = core::mem::size_of::(); + /// The length of the `Mint` account data. + pub const LEN: usize = core::mem::size_of::(); + + /// 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, 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 + } + } } diff --git a/interface/src/state/mod.rs b/interface/src/state/mod.rs index a0f8d9b..04c0342 100644 --- a/interface/src/state/mod.rs +++ b/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 { - /// Indicates if the option is `Some` or `None`. - tag: [u8; 4], - - /// The value of the option. - value: T, -} - -impl From> for PodCOption { - fn from(value: Option) -> Self { - if align_of::() != 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 PodCOption { - 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 { - 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 Pod for PodCOption {} - -/// ## Safety -/// -/// `PodCOption` requires a `Pod` type `T` with alignment of 1. -unsafe impl Zeroable for PodCOption {} - -#[repr(C)] -#[derive(Copy, Clone, Default, Pod, Zeroable)] -pub struct PodBool(u8); - -impl From 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 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 for PodU64 { - fn from(n: u64) -> Self { - Self::from_primitive(n) - } -} -impl From for u64 { - fn from(pod: PodU64) -> Self { - Self::from_le_bytes(pod.0) - } -} +/// Type alias for fields represented as `COption`. +pub type COption = ([u8; 4], T); diff --git a/interface/src/state/multisig.rs b/interface/src/state/multisig.rs index d34d2e6..362c614 100644 --- a/interface/src/state/multisig.rs +++ b/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::(); + /// 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, 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 + } } diff --git a/program/src/lib.rs b/program/src/lib.rs index 914afde..3a174fb 100644 --- a/program/src/lib.rs +++ b/program/src/lib.rs @@ -2,7 +2,5 @@ mod entrypoint; mod processor; -#[allow(unused)] -mod state; pinocchio_pubkey::declare_id!("PToken1111111111111111111111111111111111111"); diff --git a/program/src/processor/amount_to_ui_amount.rs b/program/src/processor/amount_to_ui_amount.rs index 088a97b..3328fdb 100644 --- a/program/src/processor/amount_to_ui_amount.rs +++ b/program/src/processor/amount_to_ui_amount.rs @@ -1,8 +1,7 @@ use pinocchio::{ account_info::AccountInfo, program::set_return_data, program_error::ProgramError, ProgramResult, }; - -use crate::state::mint::Mint; +use token_interface::state::mint::Mint; use super::{amount_to_ui_amount_string_trimmed, check_account_owner}; diff --git a/program/src/processor/close_account.rs b/program/src/processor/close_account.rs index d93f62a..31432e7 100644 --- a/program/src/processor/close_account.rs +++ b/program/src/processor/close_account.rs @@ -1,7 +1,5 @@ use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramResult}; -use token_interface::error::TokenError; - -use crate::state::account::Account; +use token_interface::{error::TokenError, state::account::Account}; use super::{is_owned_by_system_program_or_incinerator, validate_owner, INCINERATOR_ID}; diff --git a/program/src/processor/get_account_data_size.rs b/program/src/processor/get_account_data_size.rs index 345476e..5e96960 100644 --- a/program/src/processor/get_account_data_size.rs +++ b/program/src/processor/get_account_data_size.rs @@ -1,8 +1,7 @@ use pinocchio::{ account_info::AccountInfo, program::set_return_data, program_error::ProgramError, ProgramResult, }; - -use crate::state::{account::Account, mint::Mint}; +use token_interface::state::{account::Account, mint::Mint}; use super::check_account_owner; diff --git a/program/src/processor/initialize_immutable_owner.rs b/program/src/processor/initialize_immutable_owner.rs index cd34438..766e5b5 100644 --- a/program/src/processor/initialize_immutable_owner.rs +++ b/program/src/processor/initialize_immutable_owner.rs @@ -1,7 +1,5 @@ use pinocchio::{account_info::AccountInfo, msg, program_error::ProgramError, ProgramResult}; -use token_interface::error::TokenError; - -use crate::state::account::Account; +use token_interface::{error::TokenError, state::account::Account}; #[inline(always)] pub fn process_initialize_immutable_owner(accounts: &[AccountInfo]) -> ProgramResult { diff --git a/program/src/processor/initialize_mint.rs b/program/src/processor/initialize_mint.rs index 59f7c5b..752537d 100644 --- a/program/src/processor/initialize_mint.rs +++ b/program/src/processor/initialize_mint.rs @@ -6,9 +6,7 @@ use pinocchio::{ sysvars::{rent::Rent, Sysvar}, ProgramResult, }; -use token_interface::error::TokenError; - -use crate::state::mint::Mint; +use token_interface::{error::TokenError, state::mint::Mint}; #[inline(always)] pub fn process_initialize_mint( diff --git a/program/src/processor/mod.rs b/program/src/processor/mod.rs index 6156364..a8dd6c4 100644 --- a/program/src/processor/mod.rs +++ b/program/src/processor/mod.rs @@ -1,7 +1,10 @@ use pinocchio::{ account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey, ProgramResult, }; -use token_interface::error::TokenError; +use token_interface::{ + error::TokenError, + state::multisig::{Multisig, MAX_SIGNERS}, +}; pub mod amount_to_ui_amount; pub mod approve; @@ -57,8 +60,6 @@ pub use transfer::process_transfer; pub use transfer_checked::process_transfer_checked; pub use ui_amount_to_amount::process_ui_amount_to_amount; -use crate::state::multisig::{Multisig, MAX_SIGNERS}; - /// Incinerator address. const INCINERATOR_ID: Pubkey = pinocchio_pubkey::pubkey!("1nc1nerator11111111111111111111111111111111"); diff --git a/program/src/processor/revoke.rs b/program/src/processor/revoke.rs index 4d6b38f..4ed5016 100644 --- a/program/src/processor/revoke.rs +++ b/program/src/processor/revoke.rs @@ -1,7 +1,5 @@ use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramResult}; -use token_interface::error::TokenError; - -use crate::state::account::Account; +use token_interface::{error::TokenError, state::account::Account}; use super::validate_owner; diff --git a/program/src/processor/set_authority.rs b/program/src/processor/set_authority.rs index fc56905..4661342 100644 --- a/program/src/processor/set_authority.rs +++ b/program/src/processor/set_authority.rs @@ -3,9 +3,11 @@ use core::marker::PhantomData; use pinocchio::{ account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey, ProgramResult, }; -use token_interface::{error::TokenError, instruction::AuthorityType}; - -use crate::state::{account::Account, mint::Mint}; +use token_interface::{ + error::TokenError, + instruction::AuthorityType, + state::{account::Account, mint::Mint}, +}; use super::validate_owner; diff --git a/program/src/processor/shared/approve.rs b/program/src/processor/shared/approve.rs index db3b907..5715a12 100644 --- a/program/src/processor/shared/approve.rs +++ b/program/src/processor/shared/approve.rs @@ -1,11 +1,11 @@ use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramResult}; -use token_interface::error::TokenError; - -use crate::{ - processor::validate_owner, +use token_interface::{ + error::TokenError, state::{account::Account, mint::Mint}, }; +use crate::processor::validate_owner; + #[inline(always)] pub fn process_approve( accounts: &[AccountInfo], diff --git a/program/src/processor/shared/burn.rs b/program/src/processor/shared/burn.rs index 06c98ef..d23872f 100644 --- a/program/src/processor/shared/burn.rs +++ b/program/src/processor/shared/burn.rs @@ -1,11 +1,13 @@ use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramResult}; -use token_interface::error::TokenError; - -use crate::{ - processor::{check_account_owner, is_owned_by_system_program_or_incinerator, validate_owner}, +use token_interface::{ + error::TokenError, state::{account::Account, mint::Mint}, }; +use crate::processor::{ + check_account_owner, is_owned_by_system_program_or_incinerator, validate_owner, +}; + #[inline(always)] pub fn process_burn( accounts: &[AccountInfo], diff --git a/program/src/processor/shared/initialize_account.rs b/program/src/processor/shared/initialize_account.rs index 86e9356..fc66467 100644 --- a/program/src/processor/shared/initialize_account.rs +++ b/program/src/processor/shared/initialize_account.rs @@ -6,13 +6,14 @@ use pinocchio::{ sysvars::{rent::Rent, Sysvar}, ProgramResult, }; -use token_interface::{error::TokenError, native_mint::is_native_mint}; - -use crate::{ - processor::check_account_owner, +use token_interface::{ + error::TokenError, + native_mint::is_native_mint, state::{account::Account, account_state::AccountState, mint::Mint}, }; +use crate::processor::check_account_owner; + #[inline(always)] pub fn process_initialize_account( accounts: &[AccountInfo], diff --git a/program/src/processor/shared/initialize_multisig.rs b/program/src/processor/shared/initialize_multisig.rs index f828631..cae5084 100644 --- a/program/src/processor/shared/initialize_multisig.rs +++ b/program/src/processor/shared/initialize_multisig.rs @@ -4,9 +4,7 @@ use pinocchio::{ sysvars::{rent::Rent, Sysvar}, ProgramResult, }; -use token_interface::error::TokenError; - -use crate::state::multisig::Multisig; +use token_interface::{error::TokenError, state::multisig::Multisig}; #[inline(always)] pub fn process_initialize_multisig( diff --git a/program/src/processor/shared/mint_to.rs b/program/src/processor/shared/mint_to.rs index 2158a1d..cc891bd 100644 --- a/program/src/processor/shared/mint_to.rs +++ b/program/src/processor/shared/mint_to.rs @@ -1,11 +1,11 @@ use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramResult}; -use token_interface::error::TokenError; - -use crate::{ - processor::{check_account_owner, validate_owner}, +use token_interface::{ + error::TokenError, state::{account::Account, mint::Mint}, }; +use crate::processor::{check_account_owner, validate_owner}; + #[inline(always)] pub fn process_mint_to( accounts: &[AccountInfo], diff --git a/program/src/processor/shared/toggle_account_state.rs b/program/src/processor/shared/toggle_account_state.rs index e9fd186..f52b619 100644 --- a/program/src/processor/shared/toggle_account_state.rs +++ b/program/src/processor/shared/toggle_account_state.rs @@ -1,11 +1,11 @@ use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramResult}; -use token_interface::error::TokenError; - -use crate::{ - processor::validate_owner, +use token_interface::{ + error::TokenError, state::{account::Account, account_state::AccountState, mint::Mint}, }; +use crate::processor::validate_owner; + #[inline(always)] pub fn process_toggle_account_state(accounts: &[AccountInfo], freeze: bool) -> ProgramResult { let [source_account_info, mint_info, authority_info, remaining @ ..] = accounts else { diff --git a/program/src/processor/shared/transfer.rs b/program/src/processor/shared/transfer.rs index e13940e..5624076 100644 --- a/program/src/processor/shared/transfer.rs +++ b/program/src/processor/shared/transfer.rs @@ -1,11 +1,11 @@ use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramResult}; -use token_interface::error::TokenError; - -use crate::{ - processor::{check_account_owner, validate_owner}, +use token_interface::{ + error::TokenError, state::{account::Account, mint::Mint}, }; +use crate::processor::{check_account_owner, validate_owner}; + #[inline(always)] pub fn process_transfer( accounts: &[AccountInfo], diff --git a/program/src/processor/sync_native.rs b/program/src/processor/sync_native.rs index a8e4dac..fd90f2b 100644 --- a/program/src/processor/sync_native.rs +++ b/program/src/processor/sync_native.rs @@ -1,7 +1,5 @@ use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramResult}; -use token_interface::error::TokenError; - -use crate::state::account::Account; +use token_interface::{error::TokenError, state::account::Account}; use super::check_account_owner; diff --git a/program/src/processor/ui_amount_to_amount.rs b/program/src/processor/ui_amount_to_amount.rs index 312cc64..5eb0bd7 100644 --- a/program/src/processor/ui_amount_to_amount.rs +++ b/program/src/processor/ui_amount_to_amount.rs @@ -1,9 +1,7 @@ -use core::str; - -use crate::state::mint::Mint; use pinocchio::{ account_info::AccountInfo, program::set_return_data, program_error::ProgramError, ProgramResult, }; +use token_interface::state::mint::Mint; use super::{check_account_owner, try_ui_amount_into_amount}; @@ -12,8 +10,8 @@ pub fn process_ui_amount_to_amount( accounts: &[AccountInfo], instruction_data: &[u8], ) -> ProgramResult { - let ui_amount = - str::from_utf8(instruction_data).map_err(|_error| ProgramError::InvalidInstructionData)?; + 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(mint_info)?; diff --git a/program/src/state/account.rs b/program/src/state/account.rs deleted file mode 100644 index 2b7da6d..0000000 --- a/program/src/state/account.rs +++ /dev/null @@ -1,196 +0,0 @@ -use pinocchio::{ - account_info::{AccountInfo, Ref}, - program_error::ProgramError, - pubkey::Pubkey, -}; - -use crate::ID; - -use super::{account_state::AccountState, COption}; - -/// Internal representation of a token account data. -#[repr(C)] -pub struct Account { - /// The mint associated with this account - pub mint: Pubkey, - - /// The owner of this account. - pub owner: Pubkey, - - /// The amount of tokens this account holds. - amount: [u8; 8], - - /// If `delegate` is `Some` then `delegated_amount` represents - /// the amount authorized by the delegate. - delegate: COption, - - /// The account's state. - pub state: AccountState, - - /// 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. - native_amount: [u8; 8], - - /// The amount delegated. - delegated_amount: [u8; 8], - - /// Optional authority to close the account. - close_authority: COption, -} - -impl Account { - pub const LEN: usize = core::mem::size_of::(); - - /// 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, 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] - pub fn amount(&self) -> u64 { - u64::from_le_bytes(self.amount) - } - - #[inline] - pub fn clear_delegate(&mut self) { - self.delegate.0[0] = 0; - } - - #[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 - } - } - - #[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 { - 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 - } -} diff --git a/program/src/state/mint.rs b/program/src/state/mint.rs deleted file mode 100644 index 0b628a7..0000000 --- a/program/src/state/mint.rs +++ /dev/null @@ -1,157 +0,0 @@ -use pinocchio::{ - account_info::{AccountInfo, Ref}, - program_error::ProgramError, - pubkey::Pubkey, -}; - -use crate::ID; - -use super::COption; - -/// Internal representation of a mint data. -#[repr(C)] -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: COption, - - /// Total supply of tokens. - 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. - 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: COption, -} - -impl Mint { - /// The length of the `Mint` account data. - pub const LEN: usize = core::mem::size_of::(); - - /// 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, 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 - } - } -} diff --git a/program/src/state/mod.rs b/program/src/state/mod.rs deleted file mode 100644 index 04c0342..0000000 --- a/program/src/state/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -pub mod account; -pub mod account_state; -pub mod mint; -pub mod multisig; - -/// Type alias for fields represented as `COption`. -pub type COption = ([u8; 4], T); diff --git a/program/src/state/multisig.rs b/program/src/state/multisig.rs deleted file mode 100644 index 1543bec..0000000 --- a/program/src/state/multisig.rs +++ /dev/null @@ -1,108 +0,0 @@ -use pinocchio::{ - account_info::{AccountInfo, Ref}, - program_error::ProgramError, - pubkey::Pubkey, -}; - -use crate::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)] -pub struct Multisig { - /// Number of signers required. - pub m: u8, - - /// Number of valid signers. - pub n: u8, - - /// Is `true` if this structure has been initialized - 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::(); - - /// 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, 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 - } -} From 17924d290afeb50eaf71d67b1e6e689644a896e6 Mon Sep 17 00:00:00 2001 From: febo Date: Sun, 24 Nov 2024 11:16:56 +0000 Subject: [PATCH 6/7] Update lib comment --- program/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/src/lib.rs b/program/src/lib.rs index 3a174fb..8b786ad 100644 --- a/program/src/lib.rs +++ b/program/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; From b89f6862f1849db0ecac003c235e762a7c9639fd Mon Sep 17 00:00:00 2001 From: febo Date: Sun, 24 Nov 2024 11:25:58 +0000 Subject: [PATCH 7/7] Update CU values --- README.md | 43 ++++++++++++++++++++----------------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index b00fc81..57d38c0 100644 --- a/README.md +++ b/README.md @@ -12,9 +12,6 @@

-> [!WARNING] -> The program is not yet fully-optimized. There are still opportunities to improve the compute units consumption. - ## Overview This repository contains a **proof-of-concept** of a reimplementation of the SPL Token program, one of the most used programs on Solana, using [`pinocchio`](https://github.com/febo/pinocchio). The purpose is to have an implementation that optimizes the compute units, while being fully compatible with the original implementation — i.e., support the exact same instruction and account layouts as SPL Token, byte for byte. @@ -23,27 +20,27 @@ This repository contains a **proof-of-concept** of a reimplementation of the SPL | Instruction | Completed | CU (`p-token`) | CU (`spl-token`) | |----------------------------|-----------|----------------|------------------| -| `InitializeMint` | ✅ | 361 | 2967 | -| `InitializeAccount` | ✅ | 430 | 4527 | -| `InitializeMultisig` | ✅ | 454 | 2973 | -| `Transfer` | ✅ | 159 | 4645 | -| `Approve` | ✅ | 144 | 2904 | -| `Revoke` | ✅ | 110 | 2677 | -| `SetAuthority` | ✅ | 153 | 3167 | -| `MintTo` | ✅ | 160 | 4538 | -| `Burn` | ✅ | 180 | 4753 | -| `CloseAccount` | ✅ | 158 | 2916 | -| `FreezeAccount` | ✅ | 149 | 4265 | -| `ThawAccount` | ✅ | 150 | 4267 | -| `TransferChecked` | ✅ | 224 | 6201 | -| `ApproveChecked` | ✅ | 185 | 4459 | -| `MintToChecked` | ✅ | 191 | 4546 | -| `BurnChecked` | ✅ | 182 | 4755 | -| `InitializeAccount2` | ✅ | 414 | 4388 | +| `InitializeMint` | ✅ | 343 | 2967 | +| `InitializeAccount` | ✅ | 416 | 4527 | +| `InitializeMultisig` | ✅ | 499 | 2973 | +| `Transfer` | ✅ | 140 | 4645 | +| `Approve` | ✅ | 133 | 2904 | +| `Revoke` | ✅ | 106 | 2677 | +| `SetAuthority` | ✅ | 142 | 3167 | +| `MintTo` | ✅ | 143 | 4538 | +| `Burn` | ✅ | 175 | 4753 | +| `CloseAccount` | ✅ | 147 | 2916 | +| `FreezeAccount` | ✅ | 141 | 4265 | +| `ThawAccount` | ✅ | 142 | 4267 | +| `TransferChecked` | ✅ | 211 | 6201 | +| `ApproveChecked` | ✅ | 169 | 4459 | +| `MintToChecked` | ✅ | 178 | 4546 | +| `BurnChecked` | ✅ | 181 | 4755 | +| `InitializeAccount2` | ✅ | 399 | 4388 | | `SyncNative` | ✅ | | | -| `InitializeAccount3` | ✅ | 518 | 4240 | -| `InitializeMultisig2` | ✅ | 584 | 2826 | -| `InitializeMint2` | ✅ | 495 | 2827 | +| `InitializeAccount3` | ✅ | 508 | 4240 | +| `InitializeMultisig2` | ✅ | 579 | 2826 | +| `InitializeMint2` | ✅ | 477 | 2827 | | `GetAccountDataSize` | ✅ | | | | `InitializeImmutableOwner` | ✅ | | | | `AmountToUiAmount` | ✅ | | |