Repository: solana-labs/solana-program-library
Pull request #6407 (2024-03-12 12:16:23)

token-2022: Update toggle_freeze to use Pod types
#### Problem

The Pod types exist in token-2022, but toggle_freeze doesn't use them

#### Solution

Use Pod types in toggle_freeze in token-2022



Patch:
diff --git a/libraries/tlv-account-resolution/src/account.rs b/libraries/tlv-account-resolution/src/account.rs
index 38b8c5605..83580abe5 100644
--- a/libraries/tlv-account-resolution/src/account.rs
+++ b/libraries/tlv-account-resolution/src/account.rs
@@ -215,8 +215,7 @@ impl TryFrom<&ExtraAccountMeta> for AccountMeta {
     fn try_from(pod: &ExtraAccountMeta) -> Result<Self, Self::Error> {
         if pod.discriminator == 0 {
             Ok(AccountMeta {
-                pubkey: Pubkey::try_from(pod.address_config)
-                    .map_err(|_| ProgramError::from(AccountResolutionError::InvalidPubkey))?,
+                pubkey: Pubkey::from(pod.address_config),
                 is_signer: pod.is_signer.into(),
                 is_writable: pod.is_writable.into(),
             })
diff --git a/token/program-2022/src/pod.rs b/token/program-2022/src/pod.rs
index 3920322bc..8b4d4ea14 100644
--- a/token/program-2022/src/pod.rs
+++ b/token/program-2022/src/pod.rs
@@ -88,6 +88,12 @@ impl PodAccount {
     pub fn is_native(&self) -> bool {
         self.is_native.is_some()
     }
+    /// Checks if a token Account's owner is the system_program or the
+    /// incinerator
+    pub fn is_owned_by_system_program_or_incinerator(&self) -> bool {
+        solana_program::system_program::check_id(&self.owner)
+            || solana_program::incinerator::check_id(&self.owner)
+    }
 }
 impl IsInitialized for PodAccount {
     fn is_initialized(&self) -> bool {
@@ -179,6 +185,16 @@ impl<T: Pod + Default> PodCOption<T> {
         }
     }
 
+    /// Get the underlying value or another provided value if it isn't set,
+    /// equivalent of `Option::unwrap_or`
+    pub fn unwrap_or(self, default: T) -> T {
+        if self.option == Self::NONE {
+            default
+        } else {
+            self.value
+        }
+    }
+
     /// Checks to see if a value is set, equivalent of `Option::is_some`
     pub fn is_some(&self) -> bool {
         self.option == Self::SOME
diff --git a/token/program-2022/src/processor.rs b/token/program-2022/src/processor.rs
index 027ed839d..8e03f47e2 100644
--- a/token/program-2022/src/processor.rs
+++ b/token/program-2022/src/processor.rs
@@ -1102,8 +1102,10 @@ impl Processor {
         }
 
         let source_account_data = source_account_info.data.borrow();
-        if let Ok(source_account) = StateWithExtensions::<Account>::unpack(&source_account_data) {
-            if !source_account.base.is_native() && source_account.base.amount != 0 {
+        if let Ok(source_account) =
+            PodStateWithExtensions::<PodAccount>::unpack(&source_account_data)
+        {
+            if !source_account.base.is_native() && u64::from(source_account.base.amount) != 0 {
                 return Err(TokenError::NonNativeHasBalance.into());
             }
 
@@ -1151,7 +1153,7 @@ impl Processor {
             if let Ok(transfer_fee_state) = source_account.get_extension::<TransferFeeAmount>() {
                 transfer_fee_state.closable()?
             }
-        } else if let Ok(mint) = StateWithExtensions::<Mint>::unpack(&source_account_data) {
+        } else if let Ok(mint) = PodStateWithExtensions::<PodMint>::unpack(&source_account_data) {
             let extension = mint.get_extension::<MintCloseAuthority>()?;
             let maybe_authority: Option<Pubkey> = extension.close_authority.into();
             let authority = maybe_authority.ok_or(TokenError::AuthorityTypeNotSupported)?;
@@ -1163,7 +1165,7 @@ impl Processor {
                 account_info_iter.as_slice(),
             )?;
 
-            if mint.base.supply != 0 {
+            if u64::from(mint.base.supply) != 0 {
                 return Err(TokenError::MintHasSupply.into());
             }
         } else {
@@ -1196,8 +1198,8 @@ impl Processor {
         let authority_info_data_len = authority_info.data_len();
 
         let mut source_account_data = source_account_info.data.borrow_mut();
-        let mut source_account =
-            StateWithExtensionsMut::<Account>::unpack(&mut source_account_data)?;
+        let source_account =
+            PodStateWithExtensionsMut::<PodAccount>::unpack(&mut source_account_data)?;
         if freeze && source_account.base.is_frozen() || !freeze && !source_account.base.is_frozen()
         {
             return Err(TokenError::InvalidState.into());
@@ -1210,26 +1212,27 @@ impl Processor {
         }
 
         let mint_data = mint_info.data.borrow();
-        let mint = StateWithExtensions::<Mint>::unpack(&mint_data)?;
-        match mint.base.freeze_authority {
-            COption::Some(authority) => Self::validate_owner(
+        let mint = PodStateWithExtensions::<PodMint>::unpack(&mint_data)?;
+        match &mint.base.freeze_authority {
+            PodCOption {
+                option: PodCOption::<Pubkey>::SOME,
+                value: authority,
+            } => Self::validate_owner(
                 program_id,
-                &authority,
+                authority,
                 authority_info,
                 authority_info_data_len,
                 account_info_iter.as_slice(),
             ),
-            COption::None => Err(TokenError::MintCannotFreeze.into()),
+            _ => Err(TokenError::MintCannotFreeze.into()),
         }?;
 
         source_account.base.state = if freeze {
-            AccountState::Frozen
+            AccountState::Frozen.into()
         } else {
-            AccountState::Initialized
+            AccountState::Initialized.into()
         };
 
-        source_account.pack_base();
-
         Ok(())
     }
 
diff --git a/token/transfer-hook/cli/src/main.rs b/token/transfer-hook/cli/src/main.rs
index 237b47e2a..d611ba5cd 100644
--- a/token/transfer-hook/cli/src/main.rs
+++ b/token/transfer-hook/cli/src/main.rs
@@ -51,7 +51,7 @@ async fn build_transaction_with_rent_transfer(
     rpc_client: &RpcClient,
     payer: &dyn Signer,
     extra_account_metas_address: &Pubkey,
-    extra_account_metas: &Vec<ExtraAccountMeta>,
+    extra_account_metas: &[ExtraAccountMeta],
     instruction: Instruction,
 ) -> Result<Transaction, Box<dyn std::error::Error>> {
     let account_size = ExtraAccountMetaList::size_of(extra_account_metas.len())?;


Test patch:
diff --git a/token/program-2022/tests/assert_instruction_count.rs b/token/program-2022/tests/assert_instruction_count.rs
index 960aad4d6..741f02d30 100644
--- a/token/program-2022/tests/assert_instruction_count.rs
+++ b/token/program-2022/tests/assert_instruction_count.rs
@@ -356,7 +356,7 @@ async fn burn() {
 #[tokio::test]
 async fn close_account() {
     let mut pt = ProgramTest::new("spl_token_2022", id(), processor!(Processor::process));
-    pt.set_compute_max_units(8_000); // last known 1990
+    pt.set_compute_max_units(8_000); // last known 1500
     let (mut banks_client, payer, recent_blockhash) = pt.start().await;
 
     let owner = Keypair::new();