mod program_test;
use {
program_test::TestContext,
solana_program_test::tokio,
solana_sdk::{
instruction::InstructionError,
program_pack::Pack,
pubkey::Pubkey,
signature::Signer,
signer::keypair::Keypair,
transaction::{Transaction, TransactionError},
},
solana_system_interface::instruction as system_instruction,
spl_token_2022_interface::{
error::TokenError,
extension::{
transfer_fee::{self, TransferFeeAmount},
BaseStateWithExtensions, ExtensionType, StateWithExtensions,
},
instruction,
state::{Account, Mint},
},
};
#[tokio::test]
async fn no_extensions() {
let context = TestContext::new().await;
let ctx = context.context.lock().await;
let rent = ctx.banks_client.get_rent().await.unwrap();
let mint_account = Keypair::new();
let mint_authority_pubkey = Pubkey::new_unique();
let space = ExtensionType::try_calculate_account_len::<Mint>(&[]).unwrap();
let instructions = vec![
system_instruction::create_account(
&ctx.payer.pubkey(),
&mint_account.pubkey(),
rent.minimum_balance(space),
space as u64,
&spl_token_2022_interface::id(),
),
instruction::initialize_mint(
&spl_token_2022_interface::id(),
&mint_account.pubkey(),
&mint_authority_pubkey,
None,
9,
)
.unwrap(),
];
let tx = Transaction::new_signed_with_payer(
&instructions,
Some(&ctx.payer.pubkey()),
&[&ctx.payer, &mint_account],
ctx.last_blockhash,
);
ctx.banks_client.process_transaction(tx).await.unwrap();
let account = Keypair::new();
let account_owner_pubkey = Pubkey::new_unique();
let space = ExtensionType::try_calculate_account_len::<Account>(&[]).unwrap();
let instructions = vec![
system_instruction::create_account(
&ctx.payer.pubkey(),
&account.pubkey(),
rent.minimum_balance(space),
space as u64,
&spl_token_2022_interface::id(),
),
instruction::initialize_account3(
&spl_token_2022_interface::id(),
&account.pubkey(),
&mint_account.pubkey(),
&account_owner_pubkey,
)
.unwrap(),
];
let tx = Transaction::new_signed_with_payer(
&instructions,
Some(&ctx.payer.pubkey()),
&[&ctx.payer, &account],
ctx.last_blockhash,
);
ctx.banks_client.process_transaction(tx).await.unwrap();
let account_info = ctx
.banks_client
.get_account(account.pubkey())
.await
.expect("get_account")
.expect("account not none");
assert_eq!(
account_info.data.len(),
spl_token_2022_interface::state::Account::LEN
);
assert_eq!(account_info.owner, spl_token_2022_interface::id());
assert_eq!(account_info.lamports, rent.minimum_balance(space));
}
#[tokio::test]
async fn fail_on_invalid_mint() {
let context = TestContext::new().await;
let ctx = context.context.lock().await;
let rent = ctx.banks_client.get_rent().await.unwrap();
let mint_account = Keypair::new();
let space = ExtensionType::try_calculate_account_len::<Mint>(&[]).unwrap();
let instructions = vec![system_instruction::create_account(
&ctx.payer.pubkey(),
&mint_account.pubkey(),
rent.minimum_balance(space),
space as u64,
&spl_token_2022_interface::id(),
)];
let tx = Transaction::new_signed_with_payer(
&instructions,
Some(&ctx.payer.pubkey()),
&[&ctx.payer, &mint_account],
ctx.last_blockhash,
);
ctx.banks_client.process_transaction(tx).await.unwrap();
let account = Keypair::new();
let account_owner_pubkey = Pubkey::new_unique();
let space = ExtensionType::try_calculate_account_len::<Account>(&[]).unwrap();
let instructions = vec![
system_instruction::create_account(
&ctx.payer.pubkey(),
&account.pubkey(),
rent.minimum_balance(space),
space as u64,
&spl_token_2022_interface::id(),
),
instruction::initialize_account3(
&spl_token_2022_interface::id(),
&account.pubkey(),
&mint_account.pubkey(),
&account_owner_pubkey,
)
.unwrap(),
];
let tx = Transaction::new_signed_with_payer(
&instructions,
Some(&ctx.payer.pubkey()),
&[&ctx.payer, &account],
ctx.last_blockhash,
);
let err = ctx
.banks_client
.process_transaction(tx)
.await
.unwrap_err()
.unwrap();
assert_eq!(
err,
TransactionError::InstructionError(
1,
InstructionError::Custom(TokenError::InvalidMint as u32)
)
);
}
#[tokio::test]
async fn single_extension() {
let context = TestContext::new().await;
let ctx = context.context.lock().await;
let rent = ctx.banks_client.get_rent().await.unwrap();
let mint_account = Keypair::new();
let mint_authority_pubkey = Pubkey::new_unique();
let space =
ExtensionType::try_calculate_account_len::<Mint>(&[ExtensionType::TransferFeeConfig])
.unwrap();
let instructions = vec![
system_instruction::create_account(
&ctx.payer.pubkey(),
&mint_account.pubkey(),
rent.minimum_balance(space),
space as u64,
&spl_token_2022_interface::id(),
),
transfer_fee::instruction::initialize_transfer_fee_config(
&spl_token_2022_interface::id(),
&mint_account.pubkey(),
None,
None,
10,
4242,
)
.unwrap(),
instruction::initialize_mint(
&spl_token_2022_interface::id(),
&mint_account.pubkey(),
&mint_authority_pubkey,
None,
9,
)
.unwrap(),
];
let tx = Transaction::new_signed_with_payer(
&instructions,
Some(&ctx.payer.pubkey()),
&[&ctx.payer, &mint_account],
ctx.last_blockhash,
);
ctx.banks_client.process_transaction(tx).await.unwrap();
let account = Keypair::new();
let account_owner_pubkey = Pubkey::new_unique();
let space =
ExtensionType::try_calculate_account_len::<Account>(&[ExtensionType::TransferFeeAmount])
.unwrap();
let instructions = vec![
system_instruction::create_account(
&ctx.payer.pubkey(),
&account.pubkey(),
rent.minimum_balance(space),
space as u64,
&spl_token_2022_interface::id(),
),
instruction::initialize_account3(
&spl_token_2022_interface::id(),
&account.pubkey(),
&mint_account.pubkey(),
&account_owner_pubkey,
)
.unwrap(),
];
let tx = Transaction::new_signed_with_payer(
&instructions,
Some(&ctx.payer.pubkey()),
&[&ctx.payer, &account],
ctx.last_blockhash,
);
ctx.banks_client.process_transaction(tx).await.unwrap();
let account_info = ctx
.banks_client
.get_account(account.pubkey())
.await
.expect("get_account")
.expect("account not none");
assert_eq!(
account_info.data.len(),
ExtensionType::try_calculate_account_len::<Account>(&[ExtensionType::TransferFeeAmount])
.unwrap(),
);
assert_eq!(account_info.owner, spl_token_2022_interface::id());
assert_eq!(account_info.lamports, rent.minimum_balance(space));
let state = StateWithExtensions::<Account>::unpack(&account_info.data).unwrap();
assert_eq!(state.base.mint, mint_account.pubkey());
assert_eq!(
&state.get_extension_types().unwrap(),
&[ExtensionType::TransferFeeAmount]
);
let unpacked_extension = state.get_extension::<TransferFeeAmount>().unwrap();
assert_eq!(
*unpacked_extension,
TransferFeeAmount {
withheld_amount: 0.into()
}
);
}