@@ -1676,18 +1676,16 @@ async fn command_gc(
16761676 continue ;
16771677 }
16781678
1679- // Sanity check!
1680- // we shouldn't ever be here, but if we are here, abort!
1681- if is_associated && amount > 0 {
1682- panic ! ( "gc should NEVER attempt to close a nonempty ata" ) ;
1683- }
1684-
1685- if close_authority == owner {
1686- let res = if is_associated || amount == 0 {
1679+ // this logic is quite fiendish, but its more readable this way than if/else
1680+ let maybe_res = match ( close_authority == owner, is_associated, amount == 0 ) {
1681+ // owner authority, associated or auxiliary, empty -> close
1682+ ( true , _, true ) => Some (
16871683 token
16881684 . close_account ( & address, & owner, & owner, & bulk_signers)
1689- . await
1690- } else {
1685+ . await ,
1686+ ) ,
1687+ // owner authority, auxiliary, nonempty -> empty and close
1688+ ( true , false , false ) => Some (
16911689 token
16921690 . empty_and_close_auxiliary_account (
16931691 & address,
@@ -1696,10 +1694,40 @@ async fn command_gc(
16961694 decimals,
16971695 & bulk_signers,
16981696 )
1699- . await
1700- } ?;
1697+ . await ,
1698+ ) ,
1699+ // separate authority, auxiliary, nonempty -> transfer
1700+ ( false , false , false ) => Some (
1701+ token
1702+ . transfer (
1703+ & address,
1704+ & associated_token_account,
1705+ & owner,
1706+ amount,
1707+ Some ( decimals) ,
1708+ Some ( owner) ,
1709+ & bulk_signers,
1710+ )
1711+ . await ,
1712+ ) ,
1713+ // separate authority, associated or auxiliary, empty -> print warning
1714+ ( false , _, true ) => {
1715+ println_display (
1716+ config,
1717+ format ! (
1718+ "Note: skipping {} due to separate close authority {}; \
1719+ revoke authority and rerun gc, or rerun gc with --owner",
1720+ address, close_authority
1721+ ) ,
1722+ ) ;
1723+ None
1724+ }
1725+ // anything else, including a nonempty associated account -> unreachable
1726+ ( _, _, _) => unreachable ! ( ) ,
1727+ } ;
17011728
1702- let tx_return = finish_tx ( config, & res, false ) . await ?;
1729+ if let Some ( res) = maybe_res {
1730+ let tx_return = finish_tx ( config, & res?, false ) . await ?;
17031731
17041732 results. push ( match tx_return {
17051733 TransactionReturnData :: CliSignature ( signature) => {
@@ -1709,16 +1737,7 @@ async fn command_gc(
17091737 config. output_format . formatted_string ( & sign_only_data)
17101738 }
17111739 } ) ;
1712- } else {
1713- println_display (
1714- config,
1715- format ! (
1716- "Note: skipping {} due to separate close authority {}; \
1717- revoke authority and rerun gc, or rerun gc with --owner",
1718- address, close_authority
1719- ) ,
1720- ) ;
1721- }
1740+ } ;
17221741 }
17231742 }
17241743
@@ -4352,6 +4371,41 @@ mod tests {
43524371 // aux is gone and its tokens are in ata, and ata has not been closed
43534372 assert_eq ! ( ui_ata. token_amount. amount, "1" ) ;
43544373 config. rpc_client . get_account ( & aux) . await . unwrap_err ( ) ;
4374+
4375+ // test that balance moves off an uncloseable account
4376+ let token = create_token ( & config, & payer) . await ;
4377+ let ata = create_associated_account ( & config, & payer, token) . await ;
4378+ let aux = create_auxiliary_account ( & config, & payer, token) . await ;
4379+ let close_authority = Keypair :: new ( ) . pubkey ( ) ;
4380+ mint_tokens ( & config, & payer, token, 1.0 , aux) . await ;
4381+
4382+ process_test_command (
4383+ & config,
4384+ & payer,
4385+ & [
4386+ "spl-token" ,
4387+ CommandName :: Authorize . into ( ) ,
4388+ & aux. to_string ( ) ,
4389+ "close" ,
4390+ & close_authority. to_string ( ) ,
4391+ ] ,
4392+ )
4393+ . await
4394+ . unwrap ( ) ;
4395+
4396+ process_test_command ( & config, & payer, & [ "spl-token" , CommandName :: Gc . into ( ) ] )
4397+ . await
4398+ . unwrap ( ) ;
4399+
4400+ let ui_ata = config
4401+ . rpc_client
4402+ . get_token_account ( & ata)
4403+ . await
4404+ . unwrap ( )
4405+ . unwrap ( ) ;
4406+
4407+ // aux tokens are now in ata
4408+ assert_eq ! ( ui_ata. token_amount. amount, "1" ) ;
43554409 }
43564410 }
43574411
0 commit comments