Skip to content
This repository was archived by the owner on Mar 11, 2025. It is now read-only.

Commit 2c6fbf7

Browse files
committed
token-cli: make gc transfer tokens from uncloseable
also change client transfer to use idempotent associated account instruction
1 parent f8d82e5 commit 2c6fbf7

File tree

2 files changed

+79
-24
lines changed

2 files changed

+79
-24
lines changed

token/cli/src/main.rs

Lines changed: 77 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -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

token/client/src/token.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use {
1616
},
1717
spl_associated_token_account::{
1818
get_associated_token_address_with_program_id, instruction::create_associated_token_account,
19+
instruction::create_associated_token_account_idempotent,
1920
},
2021
spl_token_2022::{
2122
extension::{
@@ -724,7 +725,7 @@ where
724725
return Err(TokenError::AccountInvalidAccount);
725726
}
726727

727-
instructions.push(create_associated_token_account(
728+
instructions.push(create_associated_token_account_idempotent(
728729
&self.payer.pubkey(),
729730
&recipient,
730731
&self.pubkey,

0 commit comments

Comments
 (0)