Repository: solana-labs/solana-program-library
Pull request #6405 (2024-03-12 12:09:47)

token-2022: Update close_account to use Pod types
#### Problem

The Pod types exist in token-2022, but close_account doesn't use them

#### Solution

Use Pod types in close_account 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..f96de940b 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 {
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();