191 lines
5.7 KiB
Rust
191 lines
5.7 KiB
Rust
#![cfg(feature = "test-sbf")]
|
|
|
|
mod program_test;
|
|
use {
|
|
program_test::{TestContext, TokenContext},
|
|
solana_program_test::tokio,
|
|
solana_sdk::{
|
|
instruction::InstructionError, program_option::COption, pubkey::Pubkey, signature::Signer,
|
|
signer::keypair::Keypair, transaction::TransactionError, transport::TransportError,
|
|
},
|
|
spl_token_2022::{error::TokenError, extension::ExtensionType, state::Account},
|
|
spl_token_client::token::{ExtensionInitializationParams, TokenError as TokenClientError},
|
|
std::convert::TryInto,
|
|
};
|
|
|
|
#[tokio::test]
|
|
async fn reallocate() {
|
|
let mut context = TestContext::new().await;
|
|
context.init_token_with_mint(vec![]).await.unwrap();
|
|
let TokenContext {
|
|
token,
|
|
alice,
|
|
mint_authority,
|
|
..
|
|
} = context.token_context.unwrap();
|
|
|
|
// reallocate fails on wrong account type
|
|
let error = token
|
|
.reallocate(
|
|
token.get_address(),
|
|
&mint_authority.pubkey(),
|
|
&[ExtensionType::ImmutableOwner],
|
|
&[&mint_authority],
|
|
)
|
|
.await
|
|
.unwrap_err();
|
|
assert_eq!(
|
|
error,
|
|
TokenClientError::Client(Box::new(TransportError::TransactionError(
|
|
TransactionError::InstructionError(0, InstructionError::InvalidAccountData)
|
|
)))
|
|
);
|
|
|
|
// create account just large enough for base
|
|
let alice_account = Keypair::new();
|
|
token
|
|
.create_auxiliary_token_account(&alice_account, &alice.pubkey())
|
|
.await
|
|
.unwrap();
|
|
let alice_account = alice_account.pubkey();
|
|
|
|
// reallocate fails on invalid extension type
|
|
let error = token
|
|
.reallocate(
|
|
&alice_account,
|
|
&alice.pubkey(),
|
|
&[ExtensionType::MintCloseAuthority],
|
|
&[&alice],
|
|
)
|
|
.await
|
|
.unwrap_err();
|
|
assert_eq!(
|
|
error,
|
|
TokenClientError::Client(Box::new(TransportError::TransactionError(
|
|
TransactionError::InstructionError(
|
|
0,
|
|
InstructionError::Custom(TokenError::InvalidState as u32)
|
|
)
|
|
)))
|
|
);
|
|
|
|
// reallocate fails on invalid authority
|
|
let error = token
|
|
.reallocate(
|
|
&alice_account,
|
|
&mint_authority.pubkey(),
|
|
&[ExtensionType::ImmutableOwner],
|
|
&[&mint_authority],
|
|
)
|
|
.await
|
|
.unwrap_err();
|
|
assert_eq!(
|
|
error,
|
|
TokenClientError::Client(Box::new(TransportError::TransactionError(
|
|
TransactionError::InstructionError(
|
|
0,
|
|
InstructionError::Custom(TokenError::OwnerMismatch as u32)
|
|
)
|
|
)))
|
|
);
|
|
|
|
// reallocate succeeds
|
|
token
|
|
.reallocate(
|
|
&alice_account,
|
|
&alice.pubkey(),
|
|
&[ExtensionType::ImmutableOwner],
|
|
&[&alice],
|
|
)
|
|
.await
|
|
.unwrap();
|
|
let account = token.get_account(&alice_account).await.unwrap();
|
|
assert_eq!(
|
|
account.data.len(),
|
|
ExtensionType::get_account_len::<Account>(&[ExtensionType::ImmutableOwner])
|
|
);
|
|
|
|
// reallocate succeeds with noop if account is already large enough
|
|
token.get_new_latest_blockhash().await.unwrap();
|
|
token
|
|
.reallocate(
|
|
&alice_account,
|
|
&alice.pubkey(),
|
|
&[ExtensionType::ImmutableOwner],
|
|
&[&alice],
|
|
)
|
|
.await
|
|
.unwrap();
|
|
let account = token.get_account(&alice_account).await.unwrap();
|
|
assert_eq!(
|
|
account.data.len(),
|
|
ExtensionType::get_account_len::<Account>(&[ExtensionType::ImmutableOwner])
|
|
);
|
|
|
|
// reallocate only reallocates enough for new extension, and dedupes extensions
|
|
token
|
|
.reallocate(
|
|
&alice_account,
|
|
&alice.pubkey(),
|
|
&[
|
|
ExtensionType::ImmutableOwner,
|
|
ExtensionType::ImmutableOwner,
|
|
ExtensionType::TransferFeeAmount,
|
|
ExtensionType::TransferFeeAmount,
|
|
],
|
|
&[&alice],
|
|
)
|
|
.await
|
|
.unwrap();
|
|
let account = token.get_account(&alice_account).await.unwrap();
|
|
assert_eq!(
|
|
account.data.len(),
|
|
ExtensionType::get_account_len::<Account>(&[
|
|
ExtensionType::ImmutableOwner,
|
|
ExtensionType::TransferFeeAmount
|
|
])
|
|
);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn reallocate_without_current_extension_knowledge() {
|
|
let mut context = TestContext::new().await;
|
|
context
|
|
.init_token_with_mint(vec![ExtensionInitializationParams::TransferFeeConfig {
|
|
transfer_fee_config_authority: COption::Some(Pubkey::new_unique()).try_into().unwrap(),
|
|
withdraw_withheld_authority: COption::Some(Pubkey::new_unique()).try_into().unwrap(),
|
|
transfer_fee_basis_points: 250,
|
|
maximum_fee: 10_000_000,
|
|
}])
|
|
.await
|
|
.unwrap();
|
|
let TokenContext { token, alice, .. } = context.token_context.unwrap();
|
|
|
|
// create account just large enough for TransferFeeAmount extension
|
|
let alice_account = Keypair::new();
|
|
token
|
|
.create_auxiliary_token_account(&alice_account, &alice.pubkey())
|
|
.await
|
|
.unwrap();
|
|
let alice_account = alice_account.pubkey();
|
|
|
|
// reallocate resizes account to accommodate new and existing extensions
|
|
token
|
|
.reallocate(
|
|
&alice_account,
|
|
&alice.pubkey(),
|
|
&[ExtensionType::ImmutableOwner],
|
|
&[&alice],
|
|
)
|
|
.await
|
|
.unwrap();
|
|
let account = token.get_account(&alice_account).await.unwrap();
|
|
assert_eq!(
|
|
account.data.len(),
|
|
ExtensionType::get_account_len::<Account>(&[
|
|
ExtensionType::TransferFeeAmount,
|
|
ExtensionType::ImmutableOwner
|
|
])
|
|
);
|
|
}
|