@@ -783,6 +783,7 @@ async fn command_transfer(
783783 let mint_info = config. get_mint_info ( & token_pubkey, mint_decimals) . await ?;
784784
785785 // if the user got the decimals wrong, they may well have calculated the transfer amount wrong
786+ // we only check in online mode, because in offline, mint_info.decimals is always 9
786787 if !config. sign_only && mint_decimals. is_some ( ) && mint_decimals != Some ( mint_info. decimals ) {
787788 return Err ( format ! (
788789 "Decimals {} was provided, but actual value is {}" ,
@@ -793,6 +794,10 @@ async fn command_transfer(
793794 }
794795
795796 // decimals determines whether transfer_checked is used or not
797+ // in online mode, mint_decimals may be None but mint_info.decimals is always correct
798+ // in offline mode, mint_info.decimals may be wrong, but mint_decimals is always provided
799+ // and in online mode, when mint_decimals is provided, it is verified correct
800+ // hence the fallthrough logic here
796801 let decimals = if use_unchecked_instruction {
797802 None
798803 } else if mint_decimals. is_some ( ) {
@@ -808,21 +813,13 @@ async fn command_transfer(
808813 token. get_associated_token_address ( & sender_owner)
809814 } ;
810815
811- // in any sign_only block, we can safely unwrap this
812- let maybe_sender_state = match token. get_account_info ( & sender) . await {
813- Ok ( a) => Some ( a) ,
814- Err ( _) if config. sign_only => None ,
815- Err ( e) => {
816- return Err ( format ! ( "Error: failed to fetch sender account {}: {}" , sender, e) . into ( ) )
817- }
818- } ;
819-
816+ // the amount the user wants to tranfer, as a f64
820817 let maybe_transfer_balance =
821818 ui_amount. map ( |ui_amount| spl_token:: ui_amount_to_amount ( ui_amount, mint_info. decimals ) ) ;
822819
823820 // the amount we will transfer, as a u64
824821 let transfer_balance = if !config. sign_only {
825- let sender_balance = maybe_sender_state . unwrap ( ) . base . amount ;
822+ let sender_balance = token . get_account_info ( & sender ) . await ? . base . amount ;
826823 let transfer_balance = maybe_transfer_balance. unwrap_or ( sender_balance) ;
827824
828825 println_display (
@@ -959,17 +956,30 @@ async fn command_transfer(
959956 }
960957
961958 // ...and, finally, the transfer
962- let res = token
963- . transfer (
964- & sender,
965- & recipient_token_account,
966- & sender_owner,
967- transfer_balance,
968- decimals,
969- fundable_owner,
970- & bulk_signers,
971- )
972- . await ?;
959+ let res = if let Some ( recipient_owner) = fundable_owner {
960+ token
961+ . create_recipient_associated_account_and_transfer (
962+ & sender,
963+ & recipient_token_account,
964+ & recipient_owner,
965+ & sender_owner,
966+ transfer_balance,
967+ decimals,
968+ & bulk_signers,
969+ )
970+ . await ?
971+ } else {
972+ token
973+ . transfer (
974+ & sender,
975+ & recipient_token_account,
976+ & sender_owner,
977+ transfer_balance,
978+ decimals,
979+ & bulk_signers,
980+ )
981+ . await ?
982+ } ;
973983
974984 let tx_return = finish_tx ( config, & res, no_wait) . await ?;
975985 Ok ( match tx_return {
@@ -1367,21 +1377,28 @@ async fn command_close(
13671377 recipient : Pubkey ,
13681378 bulk_signers : BulkSigners ,
13691379) -> CommandResult {
1370- let source_account = config. get_account_checked ( & account) . await ?;
1380+ let mint_pubkey = if !config. sign_only {
1381+ let source_account = config. get_account_checked ( & account) . await ?;
1382+
1383+ let source_state = StateWithExtensionsOwned :: < Account > :: unpack ( source_account. data )
1384+ . map_err ( |_| format ! ( "Could not deserialize token account {}" , account) ) ?;
1385+ let source_amount = source_state. base . amount ;
13711386
1372- let source_state = StateWithExtensionsOwned :: < Account > :: unpack ( source_account. data )
1373- . map_err ( |_| format ! ( "Could not deserialize token account {}" , account) ) ?;
1374- let source_amount = source_state. base . amount ;
1387+ if !source_state. base . is_native ( ) && source_amount > 0 {
1388+ return Err ( format ! (
1389+ "Account {} still has {} tokens; empty the account in order to close it." ,
1390+ account, source_amount,
1391+ )
1392+ . into ( ) ) ;
1393+ }
13751394
1376- if !source_state. base . is_native ( ) && source_amount > 0 {
1377- return Err ( format ! (
1378- "Account {} still has {} tokens; empty the account in order to close it." ,
1379- account, source_amount,
1380- )
1381- . into ( ) ) ;
1382- }
1395+ source_state. base . mint
1396+ } else {
1397+ // default is safe here because close doesnt use it
1398+ Pubkey :: default ( )
1399+ } ;
13831400
1384- let token = token_client_from_config ( config, & source_state . base . mint ) ;
1401+ let token = token_client_from_config ( config, & mint_pubkey ) ;
13851402 let res = token
13861403 . close_account ( & account, & recipient, & close_authority, & bulk_signers)
13871404 . await ?;
@@ -1655,9 +1672,13 @@ async fn command_gc(
16551672 println_display ( config, format ! ( "Processing token: {}" , token_pubkey) ) ;
16561673
16571674 let token = token_client_from_config ( config, & token_pubkey) ;
1658- let associated_token_account = token. get_associated_token_address ( & owner) ;
16591675 let total_balance: u64 = accounts. values ( ) . map ( |account| account. 0 ) . sum ( ) ;
16601676
1677+ let associated_token_account = token. get_associated_token_address ( & owner) ;
1678+ if !accounts. contains_key ( & associated_token_account) && total_balance > 0 {
1679+ token. create_associated_token_account ( & owner) . await ?;
1680+ }
1681+
16611682 for ( address, ( amount, decimals, frozen, close_authority) ) in accounts {
16621683 let is_associated = address == associated_token_account;
16631684
@@ -1676,6 +1697,10 @@ async fn command_gc(
16761697 continue ;
16771698 }
16781699
1700+ if is_associated {
1701+ println ! ( "Closing associated account {}" , address) ;
1702+ }
1703+
16791704 // this logic is quite fiendish, but its more readable this way than if/else
16801705 let maybe_res = match ( close_authority == owner, is_associated, amount == 0 ) {
16811706 // owner authority, associated or auxiliary, empty -> close
@@ -1687,9 +1712,10 @@ async fn command_gc(
16871712 // owner authority, auxiliary, nonempty -> empty and close
16881713 ( true , false , false ) => Some (
16891714 token
1690- . empty_and_close_auxiliary_account (
1715+ . empty_and_close_account (
16911716 & address,
16921717 & owner,
1718+ & associated_token_account,
16931719 & owner,
16941720 decimals,
16951721 & bulk_signers,
@@ -1705,7 +1731,6 @@ async fn command_gc(
17051731 & owner,
17061732 amount,
17071733 Some ( decimals) ,
1708- Some ( owner) ,
17091734 & bulk_signers,
17101735 )
17111736 . await ,
0 commit comments