token-cli: Support base token-2022 (without extensions) (#3071)
This commit is contained in:
parent
d4bd86e162
commit
48e6acb53f
|
@ -6167,6 +6167,7 @@ dependencies = [
|
||||||
"spl-associated-token-account 1.0.5",
|
"spl-associated-token-account 1.0.5",
|
||||||
"spl-memo 3.0.1",
|
"spl-memo 3.0.1",
|
||||||
"spl-token 3.3.0",
|
"spl-token 3.3.0",
|
||||||
|
"spl-token-2022 0.4.1",
|
||||||
"strum",
|
"strum",
|
||||||
"strum_macros",
|
"strum_macros",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
|
|
|
@ -28,6 +28,7 @@ solana-remote-wallet = "=1.10.29"
|
||||||
solana-sdk = "=1.10.29"
|
solana-sdk = "=1.10.29"
|
||||||
solana-transaction-status = "=1.10.29"
|
solana-transaction-status = "=1.10.29"
|
||||||
spl-token = { version = "3.3", path="../program", features = [ "no-entrypoint" ] }
|
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-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"] }
|
spl-memo = { version = "3.0.1", path="../../memo/program", features = ["no-entrypoint"] }
|
||||||
strum = "0.24"
|
strum = "0.24"
|
||||||
|
|
|
@ -16,7 +16,8 @@ use {
|
||||||
system_instruction,
|
system_instruction,
|
||||||
},
|
},
|
||||||
spl_associated_token_account::*,
|
spl_associated_token_account::*,
|
||||||
spl_token::{
|
spl_token_2022::{
|
||||||
|
extension::StateWithExtensions,
|
||||||
instruction,
|
instruction,
|
||||||
state::{Account, Mint},
|
state::{Account, Mint},
|
||||||
},
|
},
|
||||||
|
@ -265,7 +266,7 @@ async fn get_valid_mint_program_id(
|
||||||
.await
|
.await
|
||||||
.map_err(|err| format!("Token mint {} does not exist: {}", token, err))?;
|
.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))?;
|
.map_err(|err| format!("Invalid token mint {}: {}", token, err))?;
|
||||||
Ok(mint_account.owner)
|
Ok(mint_account.owner)
|
||||||
}
|
}
|
||||||
|
@ -341,9 +342,9 @@ async fn command_close_accounts(
|
||||||
|
|
||||||
for (account, (address, _seed)) in accounts_chunk.iter().zip(address_chunk) {
|
for (account, (address, _seed)) in accounts_chunk.iter().zip(address_chunk) {
|
||||||
if let Some(account) = account {
|
if let Some(account) = account {
|
||||||
match Account::unpack(&account.data) {
|
match StateWithExtensions::<Account>::unpack(&account.data) {
|
||||||
Ok(token_account) => {
|
Ok(token_account) => {
|
||||||
if token_account.amount != 0 {
|
if token_account.base.amount != 0 {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"Token account {} holds a balance; unable to close it",
|
"Token account {} holds a balance; unable to close it",
|
||||||
address,
|
address,
|
||||||
|
|
|
@ -7,9 +7,12 @@ use solana_clap_utils::{
|
||||||
use solana_cli_output::OutputFormat;
|
use solana_cli_output::OutputFormat;
|
||||||
use solana_client::nonblocking::rpc_client::RpcClient;
|
use solana_client::nonblocking::rpc_client::RpcClient;
|
||||||
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
|
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_associated_token_account::*;
|
||||||
use spl_token::state::{Account, Mint};
|
use spl_token_2022::{
|
||||||
|
extension::StateWithExtensionsOwned,
|
||||||
|
state::{Account, Mint},
|
||||||
|
};
|
||||||
use std::{process::exit, sync::Arc};
|
use std::{process::exit, sync::Arc};
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -223,13 +226,13 @@ impl<'a> Config<'a> {
|
||||||
} else {
|
} else {
|
||||||
let account = self.rpc_client.get_account(mint).await?;
|
let account = self.rpc_client.get_account(mint).await?;
|
||||||
self.check_owner(mint, &account.owner)?;
|
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))?;
|
.map_err(|_| format!("Could not find mint account {}", mint))?;
|
||||||
if let Some(decimals) = mint_decimals {
|
if let Some(decimals) = mint_decimals {
|
||||||
if decimals != mint_account.decimals {
|
if decimals != mint_account.base.decimals {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"Mint {:?} has decimals {}, not configured decimals {}",
|
"Mint {:?} has decimals {}, not configured decimals {}",
|
||||||
mint, mint_account.decimals, decimals
|
mint, mint_account.base.decimals, decimals
|
||||||
)
|
)
|
||||||
.into());
|
.into());
|
||||||
}
|
}
|
||||||
|
@ -237,7 +240,7 @@ impl<'a> Config<'a> {
|
||||||
Ok(MintInfo {
|
Ok(MintInfo {
|
||||||
program_id: account.owner,
|
program_id: account.owner,
|
||||||
address: *mint,
|
address: *mint,
|
||||||
decimals: mint_account.decimals,
|
decimals: mint_account.base.decimals,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -261,9 +264,9 @@ impl<'a> Config<'a> {
|
||||||
) -> Result<Pubkey, Error> {
|
) -> Result<Pubkey, Error> {
|
||||||
if !self.sign_only {
|
if !self.sign_only {
|
||||||
let account = self.rpc_client.get_account(token_account).await?;
|
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))?;
|
.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 let Some(mint) = mint_address {
|
||||||
if source_mint != mint {
|
if source_mint != mint {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
|
|
|
@ -42,9 +42,9 @@ use solana_sdk::{
|
||||||
use spl_associated_token_account::{
|
use spl_associated_token_account::{
|
||||||
get_associated_token_address_with_program_id, instruction::create_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::*,
|
instruction::*,
|
||||||
native_mint,
|
|
||||||
state::{Account, Mint, Multisig},
|
state::{Account, Mint, Multisig},
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -59,7 +59,7 @@ mod output;
|
||||||
use output::*;
|
use output::*;
|
||||||
|
|
||||||
mod sort;
|
mod sort;
|
||||||
use sort::sort_and_parse_token_accounts;
|
use sort::{is_supported_program, sort_and_parse_token_accounts};
|
||||||
|
|
||||||
mod bench;
|
mod bench;
|
||||||
use bench::*;
|
use bench::*;
|
||||||
|
@ -533,26 +533,38 @@ async fn command_authorize(
|
||||||
AuthorityType::MintTokens => "mint authority",
|
AuthorityType::MintTokens => "mint authority",
|
||||||
AuthorityType::FreezeAccount => "freeze authority",
|
AuthorityType::FreezeAccount => "freeze authority",
|
||||||
AuthorityType::AccountOwner => "owner",
|
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 (previous_authority, program_id) = if !config.sign_only {
|
||||||
let target_account = config.rpc_client.get_account(&account).await?;
|
let target_account = config.rpc_client.get_account(&account).await?;
|
||||||
config.check_owner(&account, &target_account.owner)?;
|
config.check_owner(&account, &target_account.owner)?;
|
||||||
let program_id = 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 {
|
match authority_type {
|
||||||
AuthorityType::AccountOwner | AuthorityType::CloseAccount => Err(format!(
|
AuthorityType::AccountOwner | AuthorityType::CloseAccount => Err(format!(
|
||||||
"Authority type `{}` not supported for SPL Token mints",
|
"Authority type `{}` not supported for SPL Token mints",
|
||||||
auth_str
|
auth_str
|
||||||
)),
|
)),
|
||||||
AuthorityType::MintTokens => Ok(mint.mint_authority),
|
AuthorityType::MintTokens => Ok(mint.base.mint_authority),
|
||||||
AuthorityType::FreezeAccount => Ok(mint.freeze_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 check_associated_token_account = || -> Result<(), Error> {
|
||||||
let maybe_associated_token_account = get_associated_token_address_with_program_id(
|
let maybe_associated_token_account = get_associated_token_address_with_program_id(
|
||||||
&token_account.owner,
|
&token_account.base.owner,
|
||||||
&token_account.mint,
|
&token_account.base.mint,
|
||||||
&program_id,
|
&program_id,
|
||||||
);
|
);
|
||||||
if account == maybe_associated_token_account
|
if account == maybe_associated_token_account
|
||||||
|
@ -570,18 +582,26 @@ async fn command_authorize(
|
||||||
};
|
};
|
||||||
|
|
||||||
match authority_type {
|
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",
|
"Authority type `{}` not supported for SPL Token accounts",
|
||||||
auth_str
|
auth_str
|
||||||
)),
|
)),
|
||||||
AuthorityType::AccountOwner => {
|
AuthorityType::AccountOwner => {
|
||||||
check_associated_token_account()?;
|
check_associated_token_account()?;
|
||||||
Ok(COption::Some(token_account.owner))
|
Ok(COption::Some(token_account.base.owner))
|
||||||
}
|
}
|
||||||
AuthorityType::CloseAccount => {
|
AuthorityType::CloseAccount => {
|
||||||
check_associated_token_account()?;
|
check_associated_token_account()?;
|
||||||
Ok(COption::Some(
|
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
|
.await
|
||||||
.map_err(|_| format!("Mint account not found {:?}", token))?;
|
.map_err(|_| format!("Mint account not found {:?}", token))?;
|
||||||
config.check_owner(&token, &mint.owner)?;
|
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());
|
return Err(format!("Invalid mint account {:?}", token).into());
|
||||||
}
|
}
|
||||||
Ok(mint.owner)
|
Ok(mint.owner)
|
||||||
|
@ -707,7 +727,10 @@ async fn command_transfer(
|
||||||
if transfer_balance > sender_balance {
|
if transfer_balance > sender_balance {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"Error: Sender has insufficient funds, current balance is {}",
|
"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());
|
.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(
|
async fn command_wrap(
|
||||||
config: &Config<'_>,
|
config: &Config<'_>,
|
||||||
sol: f64,
|
sol: f64,
|
||||||
|
@ -1096,6 +1129,7 @@ async fn command_wrap(
|
||||||
) -> CommandResult {
|
) -> CommandResult {
|
||||||
let lamports = sol_to_lamports(sol);
|
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 {
|
let instructions = if let Some(wrapped_sol_account) = wrapped_sol_account {
|
||||||
println_display(
|
println_display(
|
||||||
config,
|
config,
|
||||||
|
@ -1112,14 +1146,14 @@ async fn command_wrap(
|
||||||
initialize_account(
|
initialize_account(
|
||||||
&config.program_id,
|
&config.program_id,
|
||||||
&wrapped_sol_account,
|
&wrapped_sol_account,
|
||||||
&native_mint::id(),
|
&native_mint,
|
||||||
&wallet_address,
|
&wallet_address,
|
||||||
)?,
|
)?,
|
||||||
]
|
]
|
||||||
} else {
|
} else {
|
||||||
let account = get_associated_token_address_with_program_id(
|
let account = get_associated_token_address_with_program_id(
|
||||||
&wallet_address,
|
&wallet_address,
|
||||||
&native_mint::id(),
|
&native_mint,
|
||||||
&config.program_id,
|
&config.program_id,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1142,7 +1176,7 @@ async fn command_wrap(
|
||||||
create_associated_token_account(
|
create_associated_token_account(
|
||||||
&config.fee_payer,
|
&config.fee_payer,
|
||||||
&wallet_address,
|
&wallet_address,
|
||||||
&native_mint::id(),
|
&native_mint,
|
||||||
&config.program_id,
|
&config.program_id,
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
@ -1177,10 +1211,11 @@ async fn command_unwrap(
|
||||||
bulk_signers: BulkSigners,
|
bulk_signers: BulkSigners,
|
||||||
) -> CommandResult {
|
) -> CommandResult {
|
||||||
let use_associated_account = address.is_none();
|
let use_associated_account = address.is_none();
|
||||||
|
let native_mint = native_mint(&config.program_id)?;
|
||||||
let address = address.unwrap_or_else(|| {
|
let address = address.unwrap_or_else(|| {
|
||||||
get_associated_token_address_with_program_id(
|
get_associated_token_address_with_program_id(
|
||||||
&wallet_address,
|
&wallet_address,
|
||||||
&native_mint::id(),
|
&native_mint,
|
||||||
&config.program_id,
|
&config.program_id,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
@ -1302,10 +1337,10 @@ async fn command_revoke(
|
||||||
) -> CommandResult {
|
) -> CommandResult {
|
||||||
let (delegate, program_id) = if !config.sign_only {
|
let (delegate, program_id) = if !config.sign_only {
|
||||||
let source_account = config.rpc_client.get_account(&account).await?;
|
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))?;
|
.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)
|
Some(delegate)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -1366,11 +1401,11 @@ async fn command_close(
|
||||||
(false, config.program_id)
|
(false, config.program_id)
|
||||||
} else {
|
} else {
|
||||||
let source_account = config.rpc_client.get_account(&account).await?;
|
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))?;
|
.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!(
|
return Err(format!(
|
||||||
"Account {} still has {} tokens; empty the account in order to close it.",
|
"Account {} still has {} tokens; empty the account in order to close it.",
|
||||||
account, source_amount,
|
account, source_amount,
|
||||||
|
@ -1378,6 +1413,7 @@ async fn command_close(
|
||||||
.into());
|
.into());
|
||||||
}
|
}
|
||||||
config.check_owner(&account, &source_account.owner)?;
|
config.check_owner(&account, &source_account.owner)?;
|
||||||
|
|
||||||
let recipient_account = config.rpc_client.get_token_account(&recipient).await?;
|
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);
|
let is_recipient_wrapped = recipient_account.map(|x| x.is_native).unwrap_or(false);
|
||||||
(is_recipient_wrapped, source_account.owner)
|
(is_recipient_wrapped, source_account.owner)
|
||||||
|
@ -1546,7 +1582,13 @@ async fn command_gc(
|
||||||
close_empty_associated_accounts: bool,
|
close_empty_associated_accounts: bool,
|
||||||
bulk_signers: BulkSigners,
|
bulk_signers: BulkSigners,
|
||||||
) -> CommandResult {
|
) -> 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
|
let accounts = config
|
||||||
.rpc_client
|
.rpc_client
|
||||||
.get_token_accounts_by_owner(&owner, TokenAccountsFilter::ProgramId(config.program_id))
|
.get_token_accounts_by_owner(&owner, TokenAccountsFilter::ProgramId(config.program_id))
|
||||||
|
@ -1569,7 +1611,7 @@ async fn command_gc(
|
||||||
|
|
||||||
for keyed_account in accounts {
|
for keyed_account in accounts {
|
||||||
if let UiAccountData::Json(parsed_account) = keyed_account.account.data {
|
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)) =
|
if let Ok(TokenAccountType::Account(ui_token_account)) =
|
||||||
serde_json::from_value(parsed_account.parsed)
|
serde_json::from_value(parsed_account.parsed)
|
||||||
{
|
{
|
||||||
|
@ -1732,6 +1774,7 @@ async fn command_sync_native(
|
||||||
})?
|
})?
|
||||||
.owner
|
.owner
|
||||||
};
|
};
|
||||||
|
|
||||||
let tx_return = handle_tx(
|
let tx_return = handle_tx(
|
||||||
&CliSignerInfo {
|
&CliSignerInfo {
|
||||||
signers: bulk_signers,
|
signers: bulk_signers,
|
||||||
|
@ -2000,6 +2043,8 @@ fn app<'a, 'b>(
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.possible_values(&[
|
.possible_values(&[
|
||||||
"mint", "freeze", "owner", "close",
|
"mint", "freeze", "owner", "close",
|
||||||
|
"close-mint", "transfer-fee-config", "withheld-withdraw",
|
||||||
|
"interest-rate",
|
||||||
])
|
])
|
||||||
.index(2)
|
.index(2)
|
||||||
.required(true)
|
.required(true)
|
||||||
|
@ -2597,7 +2642,7 @@ fn app<'a, 'b>(
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<(), Error> {
|
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 default_program_id = spl_token::id().to_string();
|
||||||
let minimum_signers_help = minimum_signers_help_string();
|
let minimum_signers_help = minimum_signers_help_string();
|
||||||
let multisig_member_help = multisig_member_help_string();
|
let multisig_member_help = multisig_member_help_string();
|
||||||
|
@ -2832,6 +2877,10 @@ async fn process_command<'a>(
|
||||||
"freeze" => AuthorityType::FreezeAccount,
|
"freeze" => AuthorityType::FreezeAccount,
|
||||||
"owner" => AuthorityType::AccountOwner,
|
"owner" => AuthorityType::AccountOwner,
|
||||||
"close" => AuthorityType::CloseAccount,
|
"close" => AuthorityType::CloseAccount,
|
||||||
|
"close-mint" => AuthorityType::CloseMint,
|
||||||
|
"transfer-fee-config" => AuthorityType::TransferFeeConfig,
|
||||||
|
"withheld-withdraw" => AuthorityType::WithheldWithdraw,
|
||||||
|
"interest-rate" => AuthorityType::InterestRate,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -3147,12 +3196,14 @@ async fn process_command<'a>(
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
(CommandName::SyncNative, arg_matches) => {
|
(CommandName::SyncNative, arg_matches) => {
|
||||||
|
let program_id = config.program_id;
|
||||||
|
let native_mint = native_mint(&program_id)?;
|
||||||
let address = config
|
let address = config
|
||||||
.associated_token_address_for_token_or_override(
|
.associated_token_address_for_token_or_override(
|
||||||
arg_matches,
|
arg_matches,
|
||||||
"address",
|
"address",
|
||||||
&mut wallet_manager,
|
&mut wallet_manager,
|
||||||
Some(native_mint::id()),
|
Some(native_mint),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
command_sync_native(address, bulk_signers, config).await
|
command_sync_native(address, bulk_signers, config).await
|
||||||
|
@ -3261,6 +3312,11 @@ mod tests {
|
||||||
loader: bpf_loader::id(),
|
loader: bpf_loader::id(),
|
||||||
program_path: PathBuf::from("../../target/deploy/spl_associated_token_account.so"),
|
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
|
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 {
|
async fn create_token(config: &Config<'_>, payer: &Keypair) -> Pubkey {
|
||||||
let token = Keypair::new();
|
let token = Keypair::new();
|
||||||
let token_pubkey = token.pubkey();
|
let token_pubkey = token.pubkey();
|
||||||
|
@ -3365,7 +3440,7 @@ mod tests {
|
||||||
payer: &Keypair,
|
payer: &Keypair,
|
||||||
args: &[&str],
|
args: &[&str],
|
||||||
) -> CommandResult {
|
) -> 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 default_program_id = spl_token::id().to_string();
|
||||||
let minimum_signers_help = minimum_signers_help_string();
|
let minimum_signers_help = minimum_signers_help_string();
|
||||||
let multisig_member_help = multisig_member_help_string();
|
let multisig_member_help = multisig_member_help_string();
|
||||||
|
@ -3389,7 +3464,7 @@ mod tests {
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn create_token_default() {
|
async fn create_token_default() {
|
||||||
let (test_validator, payer) = new_validator_for_test().await;
|
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 config = test_config(&test_validator, &payer, &program_id);
|
||||||
let result = process_test_command(
|
let result = process_test_command(
|
||||||
&config,
|
&config,
|
||||||
|
@ -3408,7 +3483,7 @@ mod tests {
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn supply() {
|
async fn supply() {
|
||||||
let (test_validator, payer) = new_validator_for_test().await;
|
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 config = test_config(&test_validator, &payer, &program_id);
|
||||||
let token = create_token(&config, &payer).await;
|
let token = create_token(&config, &payer).await;
|
||||||
let result = process_test_command(
|
let result = process_test_command(
|
||||||
|
@ -3426,7 +3501,7 @@ mod tests {
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn create_account_default() {
|
async fn create_account_default() {
|
||||||
let (test_validator, payer) = new_validator_for_test().await;
|
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 config = test_config(&test_validator, &payer, &program_id);
|
||||||
let token = create_token(&config, &payer).await;
|
let token = create_token(&config, &payer).await;
|
||||||
let result = process_test_command(
|
let result = process_test_command(
|
||||||
|
@ -3446,7 +3521,7 @@ mod tests {
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn account_info() {
|
async fn account_info() {
|
||||||
let (test_validator, payer) = new_validator_for_test().await;
|
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 config = test_config(&test_validator, &payer, &program_id);
|
||||||
let token = create_token(&config, &payer).await;
|
let token = create_token(&config, &payer).await;
|
||||||
let _account = create_associated_account(&config, &payer, token).await;
|
let _account = create_associated_account(&config, &payer, token).await;
|
||||||
|
@ -3478,7 +3553,7 @@ mod tests {
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn balance() {
|
async fn balance() {
|
||||||
let (test_validator, payer) = new_validator_for_test().await;
|
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 config = test_config(&test_validator, &payer, &program_id);
|
||||||
let token = create_token(&config, &payer).await;
|
let token = create_token(&config, &payer).await;
|
||||||
let _account = create_associated_account(&config, &payer, token).await;
|
let _account = create_associated_account(&config, &payer, token).await;
|
||||||
|
@ -3497,7 +3572,7 @@ mod tests {
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn mint() {
|
async fn mint() {
|
||||||
let (test_validator, payer) = new_validator_for_test().await;
|
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 config = test_config(&test_validator, &payer, &program_id);
|
||||||
let token = create_token(&config, &payer).await;
|
let token = create_token(&config, &payer).await;
|
||||||
let account = create_associated_account(&config, &payer, token).await;
|
let account = create_associated_account(&config, &payer, token).await;
|
||||||
|
@ -3514,17 +3589,17 @@ mod tests {
|
||||||
.await;
|
.await;
|
||||||
result.unwrap();
|
result.unwrap();
|
||||||
let account = config.rpc_client.get_account(&account).await.unwrap();
|
let account = config.rpc_client.get_account(&account).await.unwrap();
|
||||||
let token_account = Account::unpack(&account.data).unwrap();
|
let token_account = StateWithExtensionsOwned::<Account>::unpack(account.data).unwrap();
|
||||||
assert_eq!(token_account.amount, 100);
|
assert_eq!(token_account.base.amount, 100);
|
||||||
assert_eq!(token_account.mint, token);
|
assert_eq!(token_account.base.mint, token);
|
||||||
assert_eq!(token_account.owner, payer.pubkey());
|
assert_eq!(token_account.base.owner, payer.pubkey());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn balance_after_mint() {
|
async fn balance_after_mint() {
|
||||||
let (test_validator, payer) = new_validator_for_test().await;
|
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 config = test_config(&test_validator, &payer, &program_id);
|
||||||
let token = create_token(&config, &payer).await;
|
let token = create_token(&config, &payer).await;
|
||||||
let account = create_associated_account(&config, &payer, token).await;
|
let account = create_associated_account(&config, &payer, token).await;
|
||||||
|
@ -3545,7 +3620,7 @@ mod tests {
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn accounts() {
|
async fn accounts() {
|
||||||
let (test_validator, payer) = new_validator_for_test().await;
|
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 config = test_config(&test_validator, &payer, &program_id);
|
||||||
let token1 = create_token(&config, &payer).await;
|
let token1 = create_token(&config, &payer).await;
|
||||||
let _account1 = create_associated_account(&config, &payer, token1).await;
|
let _account1 = create_associated_account(&config, &payer, token1).await;
|
||||||
|
@ -3568,9 +3643,10 @@ mod tests {
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn wrap() {
|
async fn wrap() {
|
||||||
let (test_validator, payer) = new_validator_for_test().await;
|
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 native_mint = native_mint::id();
|
let native_mint = native_mint(&program_id).unwrap();
|
||||||
let config = test_config(&test_validator, &payer, &program_id);
|
let config = test_config(&test_validator, &payer, &program_id);
|
||||||
|
do_create_native_mint(&config, &program_id, &payer).await;
|
||||||
let _result = process_test_command(
|
let _result = process_test_command(
|
||||||
&config,
|
&config,
|
||||||
&payer,
|
&payer,
|
||||||
|
@ -3584,18 +3660,19 @@ mod tests {
|
||||||
&config.program_id,
|
&config.program_id,
|
||||||
);
|
);
|
||||||
let account = config.rpc_client.get_account(&account).await.unwrap();
|
let account = config.rpc_client.get_account(&account).await.unwrap();
|
||||||
let token_account = Account::unpack(&account.data).unwrap();
|
let token_account = StateWithExtensionsOwned::<Account>::unpack(account.data).unwrap();
|
||||||
assert_eq!(token_account.mint, native_mint);
|
assert_eq!(token_account.base.mint, native_mint);
|
||||||
assert_eq!(token_account.owner, payer.pubkey());
|
assert_eq!(token_account.base.owner, payer.pubkey());
|
||||||
assert!(token_account.is_native());
|
assert!(token_account.base.is_native());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn unwrap() {
|
async fn unwrap() {
|
||||||
let (test_validator, payer) = new_validator_for_test().await;
|
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 config = test_config(&test_validator, &payer, &program_id);
|
||||||
|
do_create_native_mint(&config, &program_id, &payer).await;
|
||||||
let (signer, account) = new_throwaway_signer();
|
let (signer, account) = new_throwaway_signer();
|
||||||
let bulk_signers: Vec<Box<dyn Signer>> = vec![Box::new(clone_keypair(&payer)), 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)
|
command_wrap(&config, 0.5, payer.pubkey(), Some(account), bulk_signers)
|
||||||
|
@ -3619,7 +3696,7 @@ mod tests {
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn transfer() {
|
async fn transfer() {
|
||||||
let (test_validator, payer) = new_validator_for_test().await;
|
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 config = test_config(&test_validator, &payer, &program_id);
|
||||||
let token = create_token(&config, &payer).await;
|
let token = create_token(&config, &payer).await;
|
||||||
let source = create_associated_account(&config, &payer, token).await;
|
let source = create_associated_account(&config, &payer, token).await;
|
||||||
|
@ -3641,18 +3718,18 @@ mod tests {
|
||||||
result.unwrap();
|
result.unwrap();
|
||||||
|
|
||||||
let account = config.rpc_client.get_account(&source).await.unwrap();
|
let account = config.rpc_client.get_account(&source).await.unwrap();
|
||||||
let token_account = Account::unpack(&account.data).unwrap();
|
let token_account = StateWithExtensionsOwned::<Account>::unpack(account.data).unwrap();
|
||||||
assert_eq!(token_account.amount, 90);
|
assert_eq!(token_account.base.amount, 90);
|
||||||
let account = config.rpc_client.get_account(&destination).await.unwrap();
|
let account = config.rpc_client.get_account(&destination).await.unwrap();
|
||||||
let token_account = Account::unpack(&account.data).unwrap();
|
let token_account = StateWithExtensionsOwned::<Account>::unpack(account.data).unwrap();
|
||||||
assert_eq!(token_account.amount, 10);
|
assert_eq!(token_account.base.amount, 10);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn transfer_fund_recipient() {
|
async fn transfer_fund_recipient() {
|
||||||
let (test_validator, payer) = new_validator_for_test().await;
|
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 config = test_config(&test_validator, &payer, &program_id);
|
||||||
let token = create_token(&config, &payer).await;
|
let token = create_token(&config, &payer).await;
|
||||||
let source = create_associated_account(&config, &payer, token).await;
|
let source = create_associated_account(&config, &payer, token).await;
|
||||||
|
@ -3676,8 +3753,8 @@ mod tests {
|
||||||
result.unwrap();
|
result.unwrap();
|
||||||
|
|
||||||
let account = config.rpc_client.get_account(&source).await.unwrap();
|
let account = config.rpc_client.get_account(&source).await.unwrap();
|
||||||
let token_account = Account::unpack(&account.data).unwrap();
|
let token_account = StateWithExtensionsOwned::<Account>::unpack(account.data).unwrap();
|
||||||
assert_eq!(token_account.amount, 90);
|
assert_eq!(token_account.base.amount, 90);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3746,12 +3823,14 @@ mod tests {
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn close_wrapped_sol_account() {
|
async fn close_wrapped_sol_account() {
|
||||||
let (test_validator, payer) = new_validator_for_test().await;
|
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 config = test_config(&test_validator, &payer, &program_id);
|
||||||
let bulk_signers: Vec<Box<dyn Signer>> = vec![Box::new(clone_keypair(&payer))];
|
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 token = create_token(&config, &payer).await;
|
||||||
let source = create_associated_account(&config, &payer, token).await;
|
let source = create_associated_account(&config, &payer, token).await;
|
||||||
|
do_create_native_mint(&config, &program_id, &payer).await;
|
||||||
let ui_amount = 10.0;
|
let ui_amount = 10.0;
|
||||||
command_wrap(&config, ui_amount, payer.pubkey(), None, bulk_signers)
|
command_wrap(&config, ui_amount, payer.pubkey(), None, bulk_signers)
|
||||||
.await
|
.await
|
||||||
|
@ -3759,7 +3838,7 @@ mod tests {
|
||||||
|
|
||||||
let recipient = get_associated_token_address_with_program_id(
|
let recipient = get_associated_token_address_with_program_id(
|
||||||
&payer.pubkey(),
|
&payer.pubkey(),
|
||||||
&native_mint::id(),
|
&native_mint,
|
||||||
&program_id,
|
&program_id,
|
||||||
);
|
);
|
||||||
let result = process_test_command(
|
let result = process_test_command(
|
||||||
|
@ -3790,7 +3869,7 @@ mod tests {
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn disable_mint_authority() {
|
async fn disable_mint_authority() {
|
||||||
let (test_validator, payer) = new_validator_for_test().await;
|
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 config = test_config(&test_validator, &payer, &program_id);
|
||||||
let token = create_token(&config, &payer).await;
|
let token = create_token(&config, &payer).await;
|
||||||
let result = process_test_command(
|
let result = process_test_command(
|
||||||
|
@ -3816,7 +3895,7 @@ mod tests {
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn gc() {
|
async fn gc() {
|
||||||
let (test_validator, payer) = new_validator_for_test().await;
|
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 mut config = test_config(&test_validator, &payer, &program_id);
|
||||||
let token = create_token(&config, &payer).await;
|
let token = create_token(&config, &payer).await;
|
||||||
let _account = create_associated_account(&config, &payer, token).await;
|
let _account = create_associated_account(&config, &payer, token).await;
|
||||||
|
@ -3861,7 +3940,7 @@ mod tests {
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn set_owner() {
|
async fn set_owner() {
|
||||||
let (test_validator, payer) = new_validator_for_test().await;
|
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 config = test_config(&test_validator, &payer, &program_id);
|
||||||
let token = create_token(&config, &payer).await;
|
let token = create_token(&config, &payer).await;
|
||||||
let aux = create_auxiliary_account(&config, &payer, token).await;
|
let aux = create_auxiliary_account(&config, &payer, token).await;
|
||||||
|
@ -3880,175 +3959,179 @@ mod tests {
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let account = config.rpc_client.get_account(&aux).await.unwrap();
|
let account = config.rpc_client.get_account(&aux).await.unwrap();
|
||||||
let token_account = Account::unpack(&account.data).unwrap();
|
let token_account = StateWithExtensionsOwned::<Account>::unpack(account.data).unwrap();
|
||||||
assert_eq!(token_account.mint, token);
|
assert_eq!(token_account.base.mint, token);
|
||||||
assert_eq!(token_account.owner, aux);
|
assert_eq!(token_account.base.owner, aux);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn transfer_with_account_delegate() {
|
async fn transfer_with_account_delegate() {
|
||||||
let (test_validator, payer) = new_validator_for_test().await;
|
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 token = create_token(&config, &payer).await;
|
||||||
let source = create_associated_account(&config, &payer, token).await;
|
let source = create_associated_account(&config, &payer, token).await;
|
||||||
let destination = create_auxiliary_account(&config, &payer, token).await;
|
let destination = create_auxiliary_account(&config, &payer, token).await;
|
||||||
let delegate = Keypair::new();
|
let delegate = Keypair::new();
|
||||||
|
|
||||||
let file = NamedTempFile::new().unwrap();
|
let file = NamedTempFile::new().unwrap();
|
||||||
write_keypair_file(&delegate, &file).unwrap();
|
write_keypair_file(&delegate, &file).unwrap();
|
||||||
|
|
||||||
let ui_amount = 100.0;
|
let ui_amount = 100.0;
|
||||||
mint_tokens(&config, &payer, token, ui_amount, source).await;
|
mint_tokens(&config, &payer, token, ui_amount, source).await;
|
||||||
|
|
||||||
let ui_account = config
|
let ui_account = config
|
||||||
.rpc_client
|
.rpc_client
|
||||||
.get_token_account(&source)
|
.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
|
.await
|
||||||
.unwrap()
|
|
||||||
.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(
|
let ui_account = config
|
||||||
&config,
|
.rpc_client
|
||||||
&payer,
|
.get_token_account(&source)
|
||||||
&[
|
.await
|
||||||
"spl-token",
|
.unwrap()
|
||||||
CommandName::Approve.into(),
|
.unwrap();
|
||||||
&source.to_string(),
|
assert_eq!(ui_account.delegate.unwrap(), delegate.pubkey().to_string());
|
||||||
"10",
|
assert_eq!(ui_account.delegated_amount.unwrap().amount, "10");
|
||||||
&delegate.pubkey().to_string(),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let ui_account = config
|
let result = process_test_command(
|
||||||
.rpc_client
|
&config,
|
||||||
.get_token_account(&source)
|
&payer,
|
||||||
.await
|
&[
|
||||||
.unwrap()
|
"spl-token",
|
||||||
.unwrap();
|
CommandName::Transfer.into(),
|
||||||
assert_eq!(ui_account.delegate.unwrap(), delegate.pubkey().to_string());
|
&token.to_string(),
|
||||||
assert_eq!(ui_account.delegated_amount.unwrap().amount, "10");
|
"10",
|
||||||
|
&destination.to_string(),
|
||||||
|
"--from",
|
||||||
|
&source.to_string(),
|
||||||
|
"--owner",
|
||||||
|
file.path().to_str().unwrap(),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
result.unwrap();
|
||||||
|
|
||||||
let result = process_test_command(
|
let ui_account = config
|
||||||
&config,
|
.rpc_client
|
||||||
&payer,
|
.get_token_account(&source)
|
||||||
&[
|
.await
|
||||||
"spl-token",
|
.unwrap()
|
||||||
CommandName::Transfer.into(),
|
.unwrap();
|
||||||
&token.to_string(),
|
assert_eq!(ui_account.token_amount.amount, "90");
|
||||||
"10",
|
assert_eq!(ui_account.delegate, None);
|
||||||
&destination.to_string(),
|
assert_eq!(ui_account.delegated_amount, None);
|
||||||
"--from",
|
let ui_account = config
|
||||||
&source.to_string(),
|
.rpc_client
|
||||||
"--owner",
|
.get_token_account(&destination)
|
||||||
file.path().to_str().unwrap(),
|
.await
|
||||||
],
|
.unwrap()
|
||||||
)
|
.unwrap();
|
||||||
.await;
|
assert_eq!(ui_account.token_amount.amount, "10");
|
||||||
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");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn burn_with_account_delegate() {
|
async fn burn_with_account_delegate() {
|
||||||
let (test_validator, payer) = new_validator_for_test().await;
|
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 token = create_token(&config, &payer).await;
|
||||||
let source = create_associated_account(&config, &payer, token).await;
|
let source = create_associated_account(&config, &payer, token).await;
|
||||||
let delegate = Keypair::new();
|
let delegate = Keypair::new();
|
||||||
|
|
||||||
let file = NamedTempFile::new().unwrap();
|
let file = NamedTempFile::new().unwrap();
|
||||||
write_keypair_file(&delegate, &file).unwrap();
|
write_keypair_file(&delegate, &file).unwrap();
|
||||||
|
|
||||||
let ui_amount = 100.0;
|
let ui_amount = 100.0;
|
||||||
mint_tokens(&config, &payer, token, ui_amount, source).await;
|
mint_tokens(&config, &payer, token, ui_amount, source).await;
|
||||||
|
|
||||||
let ui_account = config
|
let ui_account = config
|
||||||
.rpc_client
|
.rpc_client
|
||||||
.get_token_account(&source)
|
.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
|
.await
|
||||||
.unwrap()
|
|
||||||
.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(
|
let ui_account = config
|
||||||
&config,
|
.rpc_client
|
||||||
&payer,
|
.get_token_account(&source)
|
||||||
&[
|
.await
|
||||||
"spl-token",
|
.unwrap()
|
||||||
CommandName::Approve.into(),
|
.unwrap();
|
||||||
&source.to_string(),
|
assert_eq!(ui_account.delegate.unwrap(), delegate.pubkey().to_string());
|
||||||
"10",
|
assert_eq!(ui_account.delegated_amount.unwrap().amount, "10");
|
||||||
&delegate.pubkey().to_string(),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let ui_account = config
|
let result = process_test_command(
|
||||||
.rpc_client
|
&config,
|
||||||
.get_token_account(&source)
|
&payer,
|
||||||
.await
|
&[
|
||||||
.unwrap()
|
"spl-token",
|
||||||
.unwrap();
|
CommandName::Burn.into(),
|
||||||
assert_eq!(ui_account.delegate.unwrap(), delegate.pubkey().to_string());
|
&source.to_string(),
|
||||||
assert_eq!(ui_account.delegated_amount.unwrap().amount, "10");
|
"10",
|
||||||
|
"--owner",
|
||||||
|
file.path().to_str().unwrap(),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
result.unwrap();
|
||||||
|
|
||||||
let result = process_test_command(
|
let ui_account = config
|
||||||
&config,
|
.rpc_client
|
||||||
&payer,
|
.get_token_account(&source)
|
||||||
&[
|
.await
|
||||||
"spl-token",
|
.unwrap()
|
||||||
CommandName::Burn.into(),
|
.unwrap();
|
||||||
&source.to_string(),
|
assert_eq!(ui_account.token_amount.amount, "90");
|
||||||
"10",
|
assert_eq!(ui_account.delegate, None);
|
||||||
"--owner",
|
assert_eq!(ui_account.delegated_amount, None);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,10 @@ pub(crate) struct UnsupportedAccount {
|
||||||
pub err: String,
|
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(
|
pub(crate) fn sort_and_parse_token_accounts(
|
||||||
owner: &Pubkey,
|
owner: &Pubkey,
|
||||||
accounts: Vec<RpcKeyedAccount>,
|
accounts: Vec<RpcKeyedAccount>,
|
||||||
|
@ -29,7 +33,7 @@ pub(crate) fn sort_and_parse_token_accounts(
|
||||||
let address = keyed_account.pubkey;
|
let address = keyed_account.pubkey;
|
||||||
|
|
||||||
if let UiAccountData::Json(parsed_account) = keyed_account.account.data {
|
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 {
|
unsupported_accounts.push(UnsupportedAccount {
|
||||||
address,
|
address,
|
||||||
err: format!("Unsupported account program: {}", parsed_account.program),
|
err: format!("Unsupported account program: {}", parsed_account.program),
|
||||||
|
|
Loading…
Reference in New Issue