token-cli: Support base token-2022 (without extensions) (#3071)

This commit is contained in:
Jon Cinque 2022-07-27 10:47:43 +02:00 committed by GitHub
parent d4bd86e162
commit 48e6acb53f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 308 additions and 215 deletions

1
Cargo.lock generated
View File

@ -6167,6 +6167,7 @@ dependencies = [
"spl-associated-token-account 1.0.5",
"spl-memo 3.0.1",
"spl-token 3.3.0",
"spl-token-2022 0.4.1",
"strum",
"strum_macros",
"tempfile",

View File

@ -28,6 +28,7 @@ solana-remote-wallet = "=1.10.29"
solana-sdk = "=1.10.29"
solana-transaction-status = "=1.10.29"
spl-token = { version = "3.3", path="../program", features = [ "no-entrypoint" ] }
spl-token-2022 = { version = "0.4", path="../program-2022", features = [ "no-entrypoint" ] }
spl-associated-token-account = { version = "1.0.5", path="../../associated-token-account/program", features = [ "no-entrypoint" ] }
spl-memo = { version = "3.0.1", path="../../memo/program", features = ["no-entrypoint"] }
strum = "0.24"

View File

@ -16,7 +16,8 @@ use {
system_instruction,
},
spl_associated_token_account::*,
spl_token::{
spl_token_2022::{
extension::StateWithExtensions,
instruction,
state::{Account, Mint},
},
@ -265,7 +266,7 @@ async fn get_valid_mint_program_id(
.await
.map_err(|err| format!("Token mint {} does not exist: {}", token, err))?;
Mint::unpack(&mint_account.data)
StateWithExtensions::<Mint>::unpack(&mint_account.data)
.map_err(|err| format!("Invalid token mint {}: {}", token, err))?;
Ok(mint_account.owner)
}
@ -341,9 +342,9 @@ async fn command_close_accounts(
for (account, (address, _seed)) in accounts_chunk.iter().zip(address_chunk) {
if let Some(account) = account {
match Account::unpack(&account.data) {
match StateWithExtensions::<Account>::unpack(&account.data) {
Ok(token_account) => {
if token_account.amount != 0 {
if token_account.base.amount != 0 {
eprintln!(
"Token account {} holds a balance; unable to close it",
address,

View File

@ -7,9 +7,12 @@ use solana_clap_utils::{
use solana_cli_output::OutputFormat;
use solana_client::nonblocking::rpc_client::RpcClient;
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
use solana_sdk::{program_pack::Pack, pubkey::Pubkey, signature::Signer};
use solana_sdk::{pubkey::Pubkey, signature::Signer};
use spl_associated_token_account::*;
use spl_token::state::{Account, Mint};
use spl_token_2022::{
extension::StateWithExtensionsOwned,
state::{Account, Mint},
};
use std::{process::exit, sync::Arc};
#[cfg(test)]
@ -223,13 +226,13 @@ impl<'a> Config<'a> {
} else {
let account = self.rpc_client.get_account(mint).await?;
self.check_owner(mint, &account.owner)?;
let mint_account = Mint::unpack(&account.data)
let mint_account = StateWithExtensionsOwned::<Mint>::unpack(account.data)
.map_err(|_| format!("Could not find mint account {}", mint))?;
if let Some(decimals) = mint_decimals {
if decimals != mint_account.decimals {
if decimals != mint_account.base.decimals {
return Err(format!(
"Mint {:?} has decimals {}, not configured decimals {}",
mint, mint_account.decimals, decimals
mint, mint_account.base.decimals, decimals
)
.into());
}
@ -237,7 +240,7 @@ impl<'a> Config<'a> {
Ok(MintInfo {
program_id: account.owner,
address: *mint,
decimals: mint_account.decimals,
decimals: mint_account.base.decimals,
})
}
}
@ -261,9 +264,9 @@ impl<'a> Config<'a> {
) -> Result<Pubkey, Error> {
if !self.sign_only {
let account = self.rpc_client.get_account(token_account).await?;
let source_account = Account::unpack(&account.data)
let source_account = StateWithExtensionsOwned::<Account>::unpack(account.data)
.map_err(|_| format!("Could not find token account {}", token_account))?;
let source_mint = source_account.mint;
let source_mint = source_account.base.mint;
if let Some(mint) = mint_address {
if source_mint != mint {
return Err(format!(

View File

@ -42,9 +42,9 @@ use solana_sdk::{
use spl_associated_token_account::{
get_associated_token_address_with_program_id, instruction::create_associated_token_account,
};
use spl_token::{
use spl_token_2022::{
extension::StateWithExtensionsOwned,
instruction::*,
native_mint,
state::{Account, Mint, Multisig},
};
use std::{
@ -59,7 +59,7 @@ mod output;
use output::*;
mod sort;
use sort::sort_and_parse_token_accounts;
use sort::{is_supported_program, sort_and_parse_token_accounts};
mod bench;
use bench::*;
@ -533,26 +533,38 @@ async fn command_authorize(
AuthorityType::MintTokens => "mint authority",
AuthorityType::FreezeAccount => "freeze authority",
AuthorityType::AccountOwner => "owner",
AuthorityType::CloseAccount => "close authority",
AuthorityType::CloseAccount => "close account authority",
AuthorityType::CloseMint => "close mint authority",
AuthorityType::TransferFeeConfig => "transfer fee authority",
AuthorityType::WithheldWithdraw => "withdraw withheld authority",
AuthorityType::InterestRate => "interest rate authority",
};
let (previous_authority, program_id) = if !config.sign_only {
let target_account = config.rpc_client.get_account(&account).await?;
config.check_owner(&account, &target_account.owner)?;
let program_id = target_account.owner;
let previous_authority = if let Ok(mint) = Mint::unpack(&target_account.data) {
let previous_authority = if let Ok(mint) =
StateWithExtensionsOwned::<Mint>::unpack(target_account.data.clone())
{
match authority_type {
AuthorityType::AccountOwner | AuthorityType::CloseAccount => Err(format!(
"Authority type `{}` not supported for SPL Token mints",
auth_str
)),
AuthorityType::MintTokens => Ok(mint.mint_authority),
AuthorityType::FreezeAccount => Ok(mint.freeze_authority),
AuthorityType::MintTokens => Ok(mint.base.mint_authority),
AuthorityType::FreezeAccount => Ok(mint.base.freeze_authority),
AuthorityType::CloseMint => unimplemented!(),
AuthorityType::TransferFeeConfig => unimplemented!(),
AuthorityType::WithheldWithdraw => unimplemented!(),
AuthorityType::InterestRate => unimplemented!(),
}
} else if let Ok(token_account) = Account::unpack(&target_account.data) {
} else if let Ok(token_account) =
StateWithExtensionsOwned::<Account>::unpack(target_account.data)
{
let check_associated_token_account = || -> Result<(), Error> {
let maybe_associated_token_account = get_associated_token_address_with_program_id(
&token_account.owner,
&token_account.mint,
&token_account.base.owner,
&token_account.base.mint,
&program_id,
);
if account == maybe_associated_token_account
@ -570,18 +582,26 @@ async fn command_authorize(
};
match authority_type {
AuthorityType::MintTokens | AuthorityType::FreezeAccount => Err(format!(
AuthorityType::MintTokens
| AuthorityType::FreezeAccount
| AuthorityType::CloseMint
| AuthorityType::TransferFeeConfig
| AuthorityType::WithheldWithdraw
| AuthorityType::InterestRate => Err(format!(
"Authority type `{}` not supported for SPL Token accounts",
auth_str
)),
AuthorityType::AccountOwner => {
check_associated_token_account()?;
Ok(COption::Some(token_account.owner))
Ok(COption::Some(token_account.base.owner))
}
AuthorityType::CloseAccount => {
check_associated_token_account()?;
Ok(COption::Some(
token_account.close_authority.unwrap_or(token_account.owner),
token_account
.base
.close_authority
.unwrap_or(token_account.base.owner),
))
}
}
@ -643,7 +663,7 @@ async fn validate_mint(config: &Config<'_>, token: Pubkey) -> Result<Pubkey, Err
.await
.map_err(|_| format!("Mint account not found {:?}", token))?;
config.check_owner(&token, &mint.owner)?;
if Mint::unpack(&mint.data).is_err() {
if StateWithExtensionsOwned::<Mint>::unpack(mint.data).is_err() {
return Err(format!("Invalid mint account {:?}", token).into());
}
Ok(mint.owner)
@ -707,7 +727,10 @@ async fn command_transfer(
if transfer_balance > sender_balance {
return Err(format!(
"Error: Sender has insufficient funds, current balance is {}",
spl_token::amount_to_ui_amount_string_trimmed(sender_balance, mint_info.decimals)
spl_token_2022::amount_to_ui_amount_string_trimmed(
sender_balance,
mint_info.decimals
)
)
.into());
}
@ -1087,6 +1110,16 @@ async fn command_thaw(
})
}
fn native_mint(program_id: &Pubkey) -> Result<Pubkey, Error> {
if program_id == &spl_token_2022::id() {
Ok(spl_token_2022::native_mint::id())
} else if program_id == &spl_token::id() {
Ok(spl_token::native_mint::id())
} else {
Err(format!("Error: unknown token program id {}", program_id).into())
}
}
async fn command_wrap(
config: &Config<'_>,
sol: f64,
@ -1096,6 +1129,7 @@ async fn command_wrap(
) -> CommandResult {
let lamports = sol_to_lamports(sol);
let native_mint = native_mint(&config.program_id)?;
let instructions = if let Some(wrapped_sol_account) = wrapped_sol_account {
println_display(
config,
@ -1112,14 +1146,14 @@ async fn command_wrap(
initialize_account(
&config.program_id,
&wrapped_sol_account,
&native_mint::id(),
&native_mint,
&wallet_address,
)?,
]
} else {
let account = get_associated_token_address_with_program_id(
&wallet_address,
&native_mint::id(),
&native_mint,
&config.program_id,
);
@ -1142,7 +1176,7 @@ async fn command_wrap(
create_associated_token_account(
&config.fee_payer,
&wallet_address,
&native_mint::id(),
&native_mint,
&config.program_id,
),
]
@ -1177,10 +1211,11 @@ async fn command_unwrap(
bulk_signers: BulkSigners,
) -> CommandResult {
let use_associated_account = address.is_none();
let native_mint = native_mint(&config.program_id)?;
let address = address.unwrap_or_else(|| {
get_associated_token_address_with_program_id(
&wallet_address,
&native_mint::id(),
&native_mint,
&config.program_id,
)
});
@ -1302,10 +1337,10 @@ async fn command_revoke(
) -> CommandResult {
let (delegate, program_id) = if !config.sign_only {
let source_account = config.rpc_client.get_account(&account).await?;
let source_state = Account::unpack(&source_account.data)
let source_state = StateWithExtensionsOwned::<Account>::unpack(source_account.data)
.map_err(|_| format!("Could not deserialize token account {}", account))?;
let delegate = if let COption::Some(delegate) = source_state.delegate {
let delegate = if let COption::Some(delegate) = source_state.base.delegate {
Some(delegate)
} else {
None
@ -1366,11 +1401,11 @@ async fn command_close(
(false, config.program_id)
} else {
let source_account = config.rpc_client.get_account(&account).await?;
let source_state = Account::unpack(&source_account.data)
let source_state = StateWithExtensionsOwned::<Account>::unpack(source_account.data)
.map_err(|_| format!("Could not deserialize token account {}", account))?;
let source_amount = source_state.amount;
let source_amount = source_state.base.amount;
if !source_state.is_native() && source_amount > 0 {
if !source_state.base.is_native() && source_amount > 0 {
return Err(format!(
"Account {} still has {} tokens; empty the account in order to close it.",
account, source_amount,
@ -1378,6 +1413,7 @@ async fn command_close(
.into());
}
config.check_owner(&account, &source_account.owner)?;
let recipient_account = config.rpc_client.get_token_account(&recipient).await?;
let is_recipient_wrapped = recipient_account.map(|x| x.is_native).unwrap_or(false);
(is_recipient_wrapped, source_account.owner)
@ -1546,7 +1582,13 @@ async fn command_gc(
close_empty_associated_accounts: bool,
bulk_signers: BulkSigners,
) -> CommandResult {
println_display(config, "Fetching token accounts".to_string());
println_display(
config,
format!(
"Fetching token accounts associated with program {}",
config.program_id
),
);
let accounts = config
.rpc_client
.get_token_accounts_by_owner(&owner, TokenAccountsFilter::ProgramId(config.program_id))
@ -1569,7 +1611,7 @@ async fn command_gc(
for keyed_account in accounts {
if let UiAccountData::Json(parsed_account) = keyed_account.account.data {
if parsed_account.program == "spl-token" {
if is_supported_program(&parsed_account.program) {
if let Ok(TokenAccountType::Account(ui_token_account)) =
serde_json::from_value(parsed_account.parsed)
{
@ -1732,6 +1774,7 @@ async fn command_sync_native(
})?
.owner
};
let tx_return = handle_tx(
&CliSignerInfo {
signers: bulk_signers,
@ -2000,6 +2043,8 @@ fn app<'a, 'b>(
.takes_value(true)
.possible_values(&[
"mint", "freeze", "owner", "close",
"close-mint", "transfer-fee-config", "withheld-withdraw",
"interest-rate",
])
.index(2)
.required(true)
@ -2597,7 +2642,7 @@ fn app<'a, 'b>(
#[tokio::main]
async fn main() -> Result<(), Error> {
let default_decimals = format!("{}", spl_token::native_mint::DECIMALS);
let default_decimals = format!("{}", spl_token_2022::native_mint::DECIMALS);
let default_program_id = spl_token::id().to_string();
let minimum_signers_help = minimum_signers_help_string();
let multisig_member_help = multisig_member_help_string();
@ -2832,6 +2877,10 @@ async fn process_command<'a>(
"freeze" => AuthorityType::FreezeAccount,
"owner" => AuthorityType::AccountOwner,
"close" => AuthorityType::CloseAccount,
"close-mint" => AuthorityType::CloseMint,
"transfer-fee-config" => AuthorityType::TransferFeeConfig,
"withheld-withdraw" => AuthorityType::WithheldWithdraw,
"interest-rate" => AuthorityType::InterestRate,
_ => unreachable!(),
};
@ -3147,12 +3196,14 @@ async fn process_command<'a>(
.await
}
(CommandName::SyncNative, arg_matches) => {
let program_id = config.program_id;
let native_mint = native_mint(&program_id)?;
let address = config
.associated_token_address_for_token_or_override(
arg_matches,
"address",
&mut wallet_manager,
Some(native_mint::id()),
Some(native_mint),
)
.await;
command_sync_native(address, bulk_signers, config).await
@ -3261,6 +3312,11 @@ mod tests {
loader: bpf_loader::id(),
program_path: PathBuf::from("../../target/deploy/spl_associated_token_account.so"),
},
ProgramInfo {
program_id: spl_token_2022::id(),
loader: bpf_loader::id(),
program_path: PathBuf::from("../../target/deploy/spl_token_2022.so"),
},
]);
test_validator_genesis.start_async().await
}
@ -3287,6 +3343,25 @@ mod tests {
}
}
async fn do_create_native_mint(config: &Config<'_>, program_id: &Pubkey, payer: &Keypair) {
if program_id == &spl_token_2022::id() {
let native_mint = spl_token_2022::native_mint::id();
if config.rpc_client.get_account(&native_mint).await.is_err() {
let transaction = Transaction::new_signed_with_payer(
&[create_native_mint(program_id, &payer.pubkey()).unwrap()],
Some(&payer.pubkey()),
&[payer],
config.rpc_client.get_latest_blockhash().await.unwrap(),
);
config
.rpc_client
.send_and_confirm_transaction(&transaction)
.await
.unwrap();
}
}
}
async fn create_token(config: &Config<'_>, payer: &Keypair) -> Pubkey {
let token = Keypair::new();
let token_pubkey = token.pubkey();
@ -3365,7 +3440,7 @@ mod tests {
payer: &Keypair,
args: &[&str],
) -> CommandResult {
let default_decimals = format!("{}", spl_token::native_mint::DECIMALS);
let default_decimals = format!("{}", spl_token_2022::native_mint::DECIMALS);
let default_program_id = spl_token::id().to_string();
let minimum_signers_help = minimum_signers_help_string();
let multisig_member_help = multisig_member_help_string();
@ -3389,7 +3464,7 @@ mod tests {
#[tokio::test]
async fn create_token_default() {
let (test_validator, payer) = new_validator_for_test().await;
for program_id in [spl_token::id()] {
for program_id in [spl_token::id(), spl_token_2022::id()] {
let config = test_config(&test_validator, &payer, &program_id);
let result = process_test_command(
&config,
@ -3408,7 +3483,7 @@ mod tests {
#[tokio::test]
async fn supply() {
let (test_validator, payer) = new_validator_for_test().await;
for program_id in [spl_token::id()] {
for program_id in [spl_token::id(), spl_token_2022::id()] {
let config = test_config(&test_validator, &payer, &program_id);
let token = create_token(&config, &payer).await;
let result = process_test_command(
@ -3426,7 +3501,7 @@ mod tests {
#[tokio::test]
async fn create_account_default() {
let (test_validator, payer) = new_validator_for_test().await;
for program_id in [spl_token::id()] {
for program_id in [spl_token::id(), spl_token_2022::id()] {
let config = test_config(&test_validator, &payer, &program_id);
let token = create_token(&config, &payer).await;
let result = process_test_command(
@ -3446,7 +3521,7 @@ mod tests {
#[tokio::test]
async fn account_info() {
let (test_validator, payer) = new_validator_for_test().await;
for program_id in [spl_token::id()] {
for program_id in [spl_token::id(), spl_token_2022::id()] {
let config = test_config(&test_validator, &payer, &program_id);
let token = create_token(&config, &payer).await;
let _account = create_associated_account(&config, &payer, token).await;
@ -3478,7 +3553,7 @@ mod tests {
#[tokio::test]
async fn balance() {
let (test_validator, payer) = new_validator_for_test().await;
for program_id in [spl_token::id()] {
for program_id in [spl_token::id(), spl_token_2022::id()] {
let config = test_config(&test_validator, &payer, &program_id);
let token = create_token(&config, &payer).await;
let _account = create_associated_account(&config, &payer, token).await;
@ -3497,7 +3572,7 @@ mod tests {
#[tokio::test]
async fn mint() {
let (test_validator, payer) = new_validator_for_test().await;
for program_id in [spl_token::id()] {
for program_id in [spl_token::id(), spl_token_2022::id()] {
let config = test_config(&test_validator, &payer, &program_id);
let token = create_token(&config, &payer).await;
let account = create_associated_account(&config, &payer, token).await;
@ -3514,17 +3589,17 @@ mod tests {
.await;
result.unwrap();
let account = config.rpc_client.get_account(&account).await.unwrap();
let token_account = Account::unpack(&account.data).unwrap();
assert_eq!(token_account.amount, 100);
assert_eq!(token_account.mint, token);
assert_eq!(token_account.owner, payer.pubkey());
let token_account = StateWithExtensionsOwned::<Account>::unpack(account.data).unwrap();
assert_eq!(token_account.base.amount, 100);
assert_eq!(token_account.base.mint, token);
assert_eq!(token_account.base.owner, payer.pubkey());
}
}
#[tokio::test]
async fn balance_after_mint() {
let (test_validator, payer) = new_validator_for_test().await;
for program_id in [spl_token::id()] {
for program_id in [spl_token::id(), spl_token_2022::id()] {
let config = test_config(&test_validator, &payer, &program_id);
let token = create_token(&config, &payer).await;
let account = create_associated_account(&config, &payer, token).await;
@ -3545,7 +3620,7 @@ mod tests {
#[tokio::test]
async fn accounts() {
let (test_validator, payer) = new_validator_for_test().await;
for program_id in [spl_token::id()] {
for program_id in [spl_token::id(), spl_token_2022::id()] {
let config = test_config(&test_validator, &payer, &program_id);
let token1 = create_token(&config, &payer).await;
let _account1 = create_associated_account(&config, &payer, token1).await;
@ -3568,9 +3643,10 @@ mod tests {
#[tokio::test]
async fn wrap() {
let (test_validator, payer) = new_validator_for_test().await;
for program_id in [spl_token::id()] {
let native_mint = native_mint::id();
for program_id in [spl_token::id(), spl_token_2022::id()] {
let native_mint = native_mint(&program_id).unwrap();
let config = test_config(&test_validator, &payer, &program_id);
do_create_native_mint(&config, &program_id, &payer).await;
let _result = process_test_command(
&config,
&payer,
@ -3584,18 +3660,19 @@ mod tests {
&config.program_id,
);
let account = config.rpc_client.get_account(&account).await.unwrap();
let token_account = Account::unpack(&account.data).unwrap();
assert_eq!(token_account.mint, native_mint);
assert_eq!(token_account.owner, payer.pubkey());
assert!(token_account.is_native());
let token_account = StateWithExtensionsOwned::<Account>::unpack(account.data).unwrap();
assert_eq!(token_account.base.mint, native_mint);
assert_eq!(token_account.base.owner, payer.pubkey());
assert!(token_account.base.is_native());
}
}
#[tokio::test]
async fn unwrap() {
let (test_validator, payer) = new_validator_for_test().await;
for program_id in [spl_token::id()] {
for program_id in [spl_token::id(), spl_token_2022::id()] {
let config = test_config(&test_validator, &payer, &program_id);
do_create_native_mint(&config, &program_id, &payer).await;
let (signer, account) = new_throwaway_signer();
let bulk_signers: Vec<Box<dyn Signer>> = vec![Box::new(clone_keypair(&payer)), signer];
command_wrap(&config, 0.5, payer.pubkey(), Some(account), bulk_signers)
@ -3619,7 +3696,7 @@ mod tests {
#[tokio::test]
async fn transfer() {
let (test_validator, payer) = new_validator_for_test().await;
for program_id in [spl_token::id()] {
for program_id in [spl_token::id(), spl_token_2022::id()] {
let config = test_config(&test_validator, &payer, &program_id);
let token = create_token(&config, &payer).await;
let source = create_associated_account(&config, &payer, token).await;
@ -3641,18 +3718,18 @@ mod tests {
result.unwrap();
let account = config.rpc_client.get_account(&source).await.unwrap();
let token_account = Account::unpack(&account.data).unwrap();
assert_eq!(token_account.amount, 90);
let token_account = StateWithExtensionsOwned::<Account>::unpack(account.data).unwrap();
assert_eq!(token_account.base.amount, 90);
let account = config.rpc_client.get_account(&destination).await.unwrap();
let token_account = Account::unpack(&account.data).unwrap();
assert_eq!(token_account.amount, 10);
let token_account = StateWithExtensionsOwned::<Account>::unpack(account.data).unwrap();
assert_eq!(token_account.base.amount, 10);
}
}
#[tokio::test]
async fn transfer_fund_recipient() {
let (test_validator, payer) = new_validator_for_test().await;
for program_id in [spl_token::id()] {
for program_id in [spl_token::id(), spl_token_2022::id()] {
let config = test_config(&test_validator, &payer, &program_id);
let token = create_token(&config, &payer).await;
let source = create_associated_account(&config, &payer, token).await;
@ -3676,8 +3753,8 @@ mod tests {
result.unwrap();
let account = config.rpc_client.get_account(&source).await.unwrap();
let token_account = Account::unpack(&account.data).unwrap();
assert_eq!(token_account.amount, 90);
let token_account = StateWithExtensionsOwned::<Account>::unpack(account.data).unwrap();
assert_eq!(token_account.base.amount, 90);
}
}
@ -3746,12 +3823,14 @@ mod tests {
#[tokio::test]
async fn close_wrapped_sol_account() {
let (test_validator, payer) = new_validator_for_test().await;
for program_id in [spl_token::id()] {
for program_id in [spl_token::id(), spl_token_2022::id()] {
let config = test_config(&test_validator, &payer, &program_id);
let bulk_signers: Vec<Box<dyn Signer>> = vec![Box::new(clone_keypair(&payer))];
let native_mint = native_mint(&program_id).unwrap();
let token = create_token(&config, &payer).await;
let source = create_associated_account(&config, &payer, token).await;
do_create_native_mint(&config, &program_id, &payer).await;
let ui_amount = 10.0;
command_wrap(&config, ui_amount, payer.pubkey(), None, bulk_signers)
.await
@ -3759,7 +3838,7 @@ mod tests {
let recipient = get_associated_token_address_with_program_id(
&payer.pubkey(),
&native_mint::id(),
&native_mint,
&program_id,
);
let result = process_test_command(
@ -3790,7 +3869,7 @@ mod tests {
#[tokio::test]
async fn disable_mint_authority() {
let (test_validator, payer) = new_validator_for_test().await;
for program_id in [spl_token::id()] {
for program_id in [spl_token::id(), spl_token_2022::id()] {
let config = test_config(&test_validator, &payer, &program_id);
let token = create_token(&config, &payer).await;
let result = process_test_command(
@ -3816,7 +3895,7 @@ mod tests {
#[tokio::test]
async fn gc() {
let (test_validator, payer) = new_validator_for_test().await;
for program_id in [spl_token::id()] {
for program_id in [spl_token::id(), spl_token_2022::id()] {
let mut config = test_config(&test_validator, &payer, &program_id);
let token = create_token(&config, &payer).await;
let _account = create_associated_account(&config, &payer, token).await;
@ -3861,7 +3940,7 @@ mod tests {
#[tokio::test]
async fn set_owner() {
let (test_validator, payer) = new_validator_for_test().await;
for program_id in [spl_token::id()] {
for program_id in [spl_token::id(), spl_token_2022::id()] {
let config = test_config(&test_validator, &payer, &program_id);
let token = create_token(&config, &payer).await;
let aux = create_auxiliary_account(&config, &payer, token).await;
@ -3880,175 +3959,179 @@ mod tests {
.await
.unwrap();
let account = config.rpc_client.get_account(&aux).await.unwrap();
let token_account = Account::unpack(&account.data).unwrap();
assert_eq!(token_account.mint, token);
assert_eq!(token_account.owner, aux);
let token_account = StateWithExtensionsOwned::<Account>::unpack(account.data).unwrap();
assert_eq!(token_account.base.mint, token);
assert_eq!(token_account.base.owner, aux);
}
}
#[tokio::test]
async fn transfer_with_account_delegate() {
let (test_validator, payer) = new_validator_for_test().await;
let config = test_config(&test_validator, &payer, &spl_token::id());
for program_id in [spl_token::id(), spl_token_2022::id()] {
let config = test_config(&test_validator, &payer, &program_id);
let token = create_token(&config, &payer).await;
let source = create_associated_account(&config, &payer, token).await;
let destination = create_auxiliary_account(&config, &payer, token).await;
let delegate = Keypair::new();
let token = create_token(&config, &payer).await;
let source = create_associated_account(&config, &payer, token).await;
let destination = create_auxiliary_account(&config, &payer, token).await;
let delegate = Keypair::new();
let file = NamedTempFile::new().unwrap();
write_keypair_file(&delegate, &file).unwrap();
let file = NamedTempFile::new().unwrap();
write_keypair_file(&delegate, &file).unwrap();
let ui_amount = 100.0;
mint_tokens(&config, &payer, token, ui_amount, source).await;
let ui_amount = 100.0;
mint_tokens(&config, &payer, token, ui_amount, source).await;
let ui_account = config
.rpc_client
.get_token_account(&source)
let ui_account = config
.rpc_client
.get_token_account(&source)
.await
.unwrap()
.unwrap();
assert_eq!(ui_account.token_amount.amount, "100");
assert_eq!(ui_account.delegate, None);
assert_eq!(ui_account.delegated_amount, None);
let ui_account = config
.rpc_client
.get_token_account(&destination)
.await
.unwrap()
.unwrap();
assert_eq!(ui_account.token_amount.amount, "0");
process_test_command(
&config,
&payer,
&[
"spl-token",
CommandName::Approve.into(),
&source.to_string(),
"10",
&delegate.pubkey().to_string(),
],
)
.await
.unwrap()
.unwrap();
assert_eq!(ui_account.token_amount.amount, "100");
assert_eq!(ui_account.delegate, None);
assert_eq!(ui_account.delegated_amount, None);
let ui_account = config
.rpc_client
.get_token_account(&destination)
.await
.unwrap()
.unwrap();
assert_eq!(ui_account.token_amount.amount, "0");
process_test_command(
&config,
&payer,
&[
"spl-token",
CommandName::Approve.into(),
&source.to_string(),
"10",
&delegate.pubkey().to_string(),
],
)
.await
.unwrap();
let ui_account = config
.rpc_client
.get_token_account(&source)
.await
.unwrap()
.unwrap();
assert_eq!(ui_account.delegate.unwrap(), delegate.pubkey().to_string());
assert_eq!(ui_account.delegated_amount.unwrap().amount, "10");
let ui_account = config
.rpc_client
.get_token_account(&source)
.await
.unwrap()
.unwrap();
assert_eq!(ui_account.delegate.unwrap(), delegate.pubkey().to_string());
assert_eq!(ui_account.delegated_amount.unwrap().amount, "10");
let result = process_test_command(
&config,
&payer,
&[
"spl-token",
CommandName::Transfer.into(),
&token.to_string(),
"10",
&destination.to_string(),
"--from",
&source.to_string(),
"--owner",
file.path().to_str().unwrap(),
],
)
.await;
result.unwrap();
let result = process_test_command(
&config,
&payer,
&[
"spl-token",
CommandName::Transfer.into(),
&token.to_string(),
"10",
&destination.to_string(),
"--from",
&source.to_string(),
"--owner",
file.path().to_str().unwrap(),
],
)
.await;
result.unwrap();
let ui_account = config
.rpc_client
.get_token_account(&source)
.await
.unwrap()
.unwrap();
assert_eq!(ui_account.token_amount.amount, "90");
assert_eq!(ui_account.delegate, None);
assert_eq!(ui_account.delegated_amount, None);
let ui_account = config
.rpc_client
.get_token_account(&destination)
.await
.unwrap()
.unwrap();
assert_eq!(ui_account.token_amount.amount, "10");
let ui_account = config
.rpc_client
.get_token_account(&source)
.await
.unwrap()
.unwrap();
assert_eq!(ui_account.token_amount.amount, "90");
assert_eq!(ui_account.delegate, None);
assert_eq!(ui_account.delegated_amount, None);
let ui_account = config
.rpc_client
.get_token_account(&destination)
.await
.unwrap()
.unwrap();
assert_eq!(ui_account.token_amount.amount, "10");
}
}
#[tokio::test]
async fn burn_with_account_delegate() {
let (test_validator, payer) = new_validator_for_test().await;
let config = test_config(&test_validator, &payer, &spl_token::id());
for program_id in [spl_token::id(), spl_token_2022::id()] {
let config = test_config(&test_validator, &payer, &program_id);
let token = create_token(&config, &payer).await;
let source = create_associated_account(&config, &payer, token).await;
let delegate = Keypair::new();
let token = create_token(&config, &payer).await;
let source = create_associated_account(&config, &payer, token).await;
let delegate = Keypair::new();
let file = NamedTempFile::new().unwrap();
write_keypair_file(&delegate, &file).unwrap();
let file = NamedTempFile::new().unwrap();
write_keypair_file(&delegate, &file).unwrap();
let ui_amount = 100.0;
mint_tokens(&config, &payer, token, ui_amount, source).await;
let ui_amount = 100.0;
mint_tokens(&config, &payer, token, ui_amount, source).await;
let ui_account = config
.rpc_client
.get_token_account(&source)
let ui_account = config
.rpc_client
.get_token_account(&source)
.await
.unwrap()
.unwrap();
assert_eq!(ui_account.token_amount.amount, "100");
assert_eq!(ui_account.delegate, None);
assert_eq!(ui_account.delegated_amount, None);
process_test_command(
&config,
&payer,
&[
"spl-token",
CommandName::Approve.into(),
&source.to_string(),
"10",
&delegate.pubkey().to_string(),
],
)
.await
.unwrap()
.unwrap();
assert_eq!(ui_account.token_amount.amount, "100");
assert_eq!(ui_account.delegate, None);
assert_eq!(ui_account.delegated_amount, None);
process_test_command(
&config,
&payer,
&[
"spl-token",
CommandName::Approve.into(),
&source.to_string(),
"10",
&delegate.pubkey().to_string(),
],
)
.await
.unwrap();
let ui_account = config
.rpc_client
.get_token_account(&source)
.await
.unwrap()
.unwrap();
assert_eq!(ui_account.delegate.unwrap(), delegate.pubkey().to_string());
assert_eq!(ui_account.delegated_amount.unwrap().amount, "10");
let ui_account = config
.rpc_client
.get_token_account(&source)
.await
.unwrap()
.unwrap();
assert_eq!(ui_account.delegate.unwrap(), delegate.pubkey().to_string());
assert_eq!(ui_account.delegated_amount.unwrap().amount, "10");
let result = process_test_command(
&config,
&payer,
&[
"spl-token",
CommandName::Burn.into(),
&source.to_string(),
"10",
"--owner",
file.path().to_str().unwrap(),
],
)
.await;
result.unwrap();
let result = process_test_command(
&config,
&payer,
&[
"spl-token",
CommandName::Burn.into(),
&source.to_string(),
"10",
"--owner",
file.path().to_str().unwrap(),
],
)
.await;
result.unwrap();
let ui_account = config
.rpc_client
.get_token_account(&source)
.await
.unwrap()
.unwrap();
assert_eq!(ui_account.token_amount.amount, "90");
assert_eq!(ui_account.delegate, None);
assert_eq!(ui_account.delegated_amount, None);
let ui_account = config
.rpc_client
.get_token_account(&source)
.await
.unwrap()
.unwrap();
assert_eq!(ui_account.token_amount.amount, "90");
assert_eq!(ui_account.delegate, None);
assert_eq!(ui_account.delegated_amount, None);
}
}
}

View File

@ -16,6 +16,10 @@ pub(crate) struct UnsupportedAccount {
pub err: String,
}
pub(crate) fn is_supported_program(program_name: &str) -> bool {
program_name == "spl-token" || program_name == "spl-token-2022"
}
pub(crate) fn sort_and_parse_token_accounts(
owner: &Pubkey,
accounts: Vec<RpcKeyedAccount>,
@ -29,7 +33,7 @@ pub(crate) fn sort_and_parse_token_accounts(
let address = keyed_account.pubkey;
if let UiAccountData::Json(parsed_account) = keyed_account.account.data {
if parsed_account.program != "spl-token" {
if !is_supported_program(&parsed_account.program) {
unsupported_accounts.push(UnsupportedAccount {
address,
err: format!("Unsupported account program: {}", parsed_account.program),