From 48e6acb53f283dbc9f9ce02df25cce46310be2a5 Mon Sep 17 00:00:00 2001 From: Jon Cinque Date: Wed, 27 Jul 2022 10:47:43 +0200 Subject: [PATCH] token-cli: Support base token-2022 (without extensions) (#3071) --- Cargo.lock | 1 + token/cli/Cargo.toml | 1 + token/cli/src/bench.rs | 9 +- token/cli/src/config.rs | 19 +- token/cli/src/main.rs | 487 +++++++++++++++++++++++----------------- token/cli/src/sort.rs | 6 +- 6 files changed, 308 insertions(+), 215 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 894f56ee..16157cd8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", diff --git a/token/cli/Cargo.toml b/token/cli/Cargo.toml index d5249c34..170e50b3 100644 --- a/token/cli/Cargo.toml +++ b/token/cli/Cargo.toml @@ -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" diff --git a/token/cli/src/bench.rs b/token/cli/src/bench.rs index 55d8e184..c724191f 100644 --- a/token/cli/src/bench.rs +++ b/token/cli/src/bench.rs @@ -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::::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::::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, diff --git a/token/cli/src/config.rs b/token/cli/src/config.rs index b6faf02c..dae8ba79 100644 --- a/token/cli/src/config.rs +++ b/token/cli/src/config.rs @@ -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::::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 { 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::::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!( diff --git a/token/cli/src/main.rs b/token/cli/src/main.rs index 9583c6c7..ad17a6b3 100644 --- a/token/cli/src/main.rs +++ b/token/cli/src/main.rs @@ -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::::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::::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::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 { + 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::::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::::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::::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::::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> = 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::::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::::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::::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> = 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::::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); + } } } diff --git a/token/cli/src/sort.rs b/token/cli/src/sort.rs index 08cef6d8..725a7ba7 100644 --- a/token/cli/src/sort.rs +++ b/token/cli/src/sort.rs @@ -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, @@ -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),