#![allow(dead_code)]
use {
mpl_token_metadata::{
accounts::Metadata as MetaplexMetadata, instructions::CreateV1Builder, types::TokenStandard,
},
serde_json::Value,
solana_cli_config::Config as SolanaConfig,
solana_client::nonblocking::rpc_client::RpcClient,
solana_commitment_config::CommitmentConfig,
solana_keypair::{write_keypair_file, Keypair},
solana_program_pack::Pack,
solana_pubkey::Pubkey,
solana_sdk_ids::bpf_loader_upgradeable,
solana_signer::Signer,
solana_system_interface::instruction::create_account,
solana_test_validator::{TestValidator, TestValidatorGenesis, UpgradeableProgramInfo},
solana_transaction::Transaction,
spl_associated_token_account_client::address::get_associated_token_address_with_program_id,
spl_token::{self, state::Mint as SplTokenMint},
spl_token_2022::instruction::initialize_mint,
std::{error::Error, path::PathBuf, process::Command, sync::Arc},
tempfile::NamedTempFile,
};
pub const TOKEN_WRAP_CLI_BIN: &str = "../../target/debug/spl-token-wrap";
pub struct TestEnv {
pub rpc_client: Arc<RpcClient>,
pub payer: Keypair,
pub config_file_path: String,
_validator: TestValidator,
_keypair_file: NamedTempFile,
_config_file: NamedTempFile,
}
pub async fn start_validator() -> (TestValidator, Keypair) {
solana_logger::setup();
let mut test_validator_genesis = TestValidatorGenesis::default();
test_validator_genesis.add_upgradeable_programs_with_path(&[
UpgradeableProgramInfo {
program_id: spl_token_wrap::id(),
loader: bpf_loader_upgradeable::id(),
program_path: PathBuf::from("../../target/deploy/spl_token_wrap.so"),
upgrade_authority: Pubkey::default(),
},
UpgradeableProgramInfo {
program_id: mpl_token_metadata::ID,
loader: bpf_loader_upgradeable::id(),
program_path: PathBuf::from("../../program/tests/fixtures/mpl_token_metadata.so"),
upgrade_authority: Pubkey::default(),
},
]);
test_validator_genesis.start_async().await
}
pub async fn setup_test_env() -> TestEnv {
let (validator, payer) = start_validator().await;
let rpc_client = Arc::new(RpcClient::new_with_commitment(
validator.rpc_url(),
CommitmentConfig::confirmed(),
));
let keypair_file = NamedTempFile::new().unwrap();
write_keypair_file(&payer, &keypair_file).unwrap();
let keypair_file_path = keypair_file.path().to_str().unwrap().to_string();
let config_file = NamedTempFile::new().unwrap();
let config_file_path = config_file.path().to_str().unwrap().to_string();
let solana_config = SolanaConfig {
json_rpc_url: validator.rpc_url(),
websocket_url: validator.rpc_pubsub_url(),
keypair_path: keypair_file_path,
..SolanaConfig::default()
};
solana_config.save(&config_file_path).unwrap();
TestEnv {
payer,
rpc_client,
config_file_path,
_keypair_file: keypair_file,
_config_file: config_file,
_validator: validator,
}
}
pub async fn create_unwrapped_mint(env: &TestEnv, token_program_addr: &Pubkey) -> Pubkey {
let mint_account = Keypair::new();
let rent = env
.rpc_client
.get_minimum_balance_for_rent_exemption(SplTokenMint::LEN)
.await
.unwrap();
let blockhash = env.rpc_client.get_latest_blockhash().await.unwrap();
let transaction = Transaction::new_signed_with_payer(
&[
solana_system_interface::instruction::create_account(
&env.payer.pubkey(),
&mint_account.pubkey(),
rent,
SplTokenMint::LEN as u64,
token_program_addr,
),
initialize_mint(
token_program_addr,
&mint_account.pubkey(),
&env.payer.pubkey(),
None,
9,
)
.unwrap(),
],
Some(&env.payer.pubkey()),
&[env.payer.insecure_clone(), mint_account.insecure_clone()],
blockhash,
);
env.rpc_client
.send_and_confirm_transaction(&transaction)
.await
.unwrap();
mint_account.pubkey()
}
pub async fn create_metaplex_metadata(
env: &TestEnv,
mint: &Pubkey,
token_program: Pubkey,
name: String,
symbol: String,
uri: String,
) -> Pubkey {
let (metaplex_pda, _) = MetaplexMetadata::find_pda(mint);
let mut builder = CreateV1Builder::new();
let create_ix = builder
.metadata(metaplex_pda)
.master_edition(None)
.mint(*mint, false)
.authority(env.payer.pubkey())
.payer(env.payer.pubkey())
.update_authority(env.payer.pubkey(), true)
.spl_token_program(Some(token_program))
.name(name)
.symbol(symbol)
.uri(uri)
.seller_fee_basis_points(0)
.primary_sale_happened(false)
.is_mutable(true)
.token_standard(TokenStandard::Fungible)
.decimals(9)
.instruction();
let meta_tx = Transaction::new_signed_with_payer(
&[create_ix],
Some(&env.payer.pubkey()),
&[&env.payer],
env.rpc_client.get_latest_blockhash().await.unwrap(),
);
env.rpc_client
.send_and_confirm_transaction(&meta_tx)
.await
.unwrap();
metaplex_pda
}
pub async fn execute_create_mint(
env: &TestEnv,
unwrapped_mint: &Pubkey,
wrapped_token_program: &Pubkey,
) {
let status = Command::new(TOKEN_WRAP_CLI_BIN)
.args([
"create-mint",
"-C",
&env.config_file_path,
&unwrapped_mint.to_string(),
&wrapped_token_program.to_string(),
"--idempotent",
])
.status()
.unwrap();
assert!(status.success());
}
pub async fn create_associated_token_account(
env: &TestEnv,
token_program: &Pubkey,
mint: &Pubkey,
wallet_addr: &Pubkey,
) -> Pubkey {
let ata = get_associated_token_address_with_program_id(wallet_addr, mint, token_program);
let ata_account = env.rpc_client.get_account(&ata).await;
if ata_account.is_ok() {
return ata; }
let instruction =
spl_associated_token_account_client::instruction::create_associated_token_account(
&env.payer.pubkey(),
wallet_addr,
mint,
token_program,
);
let tx = Transaction::new_signed_with_payer(
&[instruction],
Some(&env.payer.pubkey()),
&[&env.payer],
env.rpc_client.get_latest_blockhash().await.unwrap(),
);
env.rpc_client
.send_and_confirm_transaction(&tx)
.await
.unwrap();
ata
}
pub async fn create_token_account(
env: &TestEnv,
token_program: &Pubkey,
mint: &Pubkey,
owner: &Pubkey,
) -> Pubkey {
let token_account = Keypair::new();
let account_size = spl_token::state::Account::LEN;
let tx = Transaction::new_signed_with_payer(
&[
solana_system_interface::instruction::create_account(
&env.payer.pubkey(),
&token_account.pubkey(),
env.rpc_client
.get_minimum_balance_for_rent_exemption(account_size)
.await
.unwrap(),
account_size as u64,
token_program,
),
spl_token_2022::instruction::initialize_account(
token_program,
&token_account.pubkey(),
mint,
owner,
)
.unwrap(),
],
Some(&env.payer.pubkey()),
&[&env.payer, &token_account],
env.rpc_client.get_latest_blockhash().await.unwrap(),
);
env.rpc_client
.send_and_confirm_transaction(&tx)
.await
.unwrap();
token_account.pubkey()
}
pub async fn mint_to(
env: &TestEnv,
token_program: &Pubkey,
mint: &Pubkey,
token_account: &Pubkey,
amount: u64,
) {
let tx = Transaction::new_signed_with_payer(
&[spl_token::instruction::mint_to(
token_program,
mint,
token_account,
&env.payer.pubkey(),
&[&env.payer.pubkey()],
amount,
)
.unwrap()],
Some(&env.payer.pubkey()),
&[&env.payer],
env.rpc_client.get_latest_blockhash().await.unwrap(),
);
env.rpc_client
.send_and_confirm_transaction(&tx)
.await
.unwrap();
}
pub async fn create_test_multisig(
env: &mut TestEnv,
token_program: &Pubkey,
) -> Result<(Pubkey, Vec<Keypair>), Box<dyn Error>> {
let multisig_keypair = Keypair::new();
let multisig_pubkey = multisig_keypair.pubkey();
let multisig_member1 = Keypair::new();
let multisig_member2 = Keypair::new();
let multisig_member3 = Keypair::new();
let multisig_member_pubkeys = [
&multisig_member1.pubkey(),
&multisig_member2.pubkey(),
&multisig_member3.pubkey(),
];
let rent = env
.rpc_client
.get_minimum_balance_for_rent_exemption(spl_token::state::Multisig::LEN)
.await?;
let create_account_instruction = create_account(
&env.payer.pubkey(),
&multisig_pubkey,
rent,
spl_token::state::Multisig::LEN as u64,
token_program,
);
let initialize_multisig_instruction = spl_token_2022::instruction::initialize_multisig(
token_program,
&multisig_pubkey,
&multisig_member_pubkeys,
2,
)?;
let mut transaction = Transaction::new_with_payer(
&[create_account_instruction, initialize_multisig_instruction],
Some(&env.payer.pubkey()),
);
let recent_blockhash = env.rpc_client.get_latest_blockhash().await?;
transaction.sign(&[&env.payer, &multisig_keypair], recent_blockhash);
env.rpc_client
.send_and_confirm_transaction(&transaction)
.await?;
Ok((
multisig_pubkey,
vec![multisig_member1, multisig_member2, multisig_member3],
))
}
pub fn extract_signers(std_out: &[u8]) -> Vec<String> {
let json_output1 = String::from_utf8(std_out.to_vec()).unwrap();
let parsed_value: Value = serde_json::from_str(&json_output1).unwrap();
parsed_value
.get("signOnlyData")
.unwrap()
.get("signers")
.unwrap()
.as_array()
.unwrap()
.iter()
.map(|v| v.as_str().unwrap().to_string())
.collect()
}