#![allow(clippy::arithmetic_side_effects)]
mod helpers;
use {
helpers::*,
solana_native_token::LAMPORTS_PER_SOL,
solana_program_pack::Pack,
solana_program_test::*,
solana_signer::Signer,
solana_stake_interface::program as stake_program,
solana_transaction::Transaction,
spl_single_pool::{error::SinglePoolError, id, instruction},
spl_token_interface::state::Mint,
test_case::test_matrix,
};
#[test_matrix(
[StakeProgramVersion::Stable, StakeProgramVersion::Beta, StakeProgramVersion::Edge]
)]
#[tokio::test]
async fn minimum_pool_balance(stake_version: StakeProgramVersion) {
let Some(program_test) = program_test(stake_version) else {
return;
};
let mut context = program_test.start_with_context().await;
let accounts = SinglePoolAccounts::default();
let minimum_pool_balance = accounts.initialize(&mut context).await;
assert_eq!(
minimum_pool_balance, LAMPORTS_PER_SOL,
"Stake Program minimum delegation has changed, token accounting may need to change \
with it. Please consult the comment for more details.",
);
}
#[test_matrix(
[StakeProgramVersion::Stable, StakeProgramVersion::Beta, StakeProgramVersion::Edge]
)]
#[tokio::test]
async fn success(stake_version: StakeProgramVersion) {
let Some(program_test) = program_test(stake_version) else {
return;
};
let mut context = program_test.start_with_context().await;
let accounts = SinglePoolAccounts::default();
accounts.initialize(&mut context).await;
let mint_account = get_account(&mut context.banks_client, &accounts.mint).await;
Mint::unpack_from_slice(&mint_account.data).unwrap();
let stake_account = get_account(&mut context.banks_client, &accounts.stake_account).await;
assert_eq!(stake_account.owner, stake_program::id());
}
#[tokio::test]
async fn fail_double_init() {
let mut context = program_test_live().start_with_context().await;
let accounts = SinglePoolAccounts::default();
let minimum_pool_balance = accounts.initialize(&mut context).await;
refresh_blockhash(&mut context).await;
let rent = context.banks_client.get_rent().await.unwrap();
let instructions = instruction::initialize(
&id(),
&accounts.vote_account.pubkey(),
&context.payer.pubkey(),
&rent,
minimum_pool_balance,
);
let transaction = Transaction::new_signed_with_payer(
&instructions,
Some(&context.payer.pubkey()),
&[&context.payer],
context.last_blockhash,
);
let e = context
.banks_client
.process_transaction(transaction)
.await
.unwrap_err();
check_error(e, SinglePoolError::PoolAlreadyInitialized);
}
#[test_matrix(
[StakeProgramVersion::Stable, StakeProgramVersion::Beta, StakeProgramVersion::Edge]
)]
#[tokio::test]
async fn fail_below_pool_minimum(stake_version: StakeProgramVersion) {
let Some(program_test) = program_test(stake_version) else {
return;
};
let mut context = program_test.start_with_context().await;
let accounts = SinglePoolAccounts::default();
let slot = context.genesis_config().epoch_schedule.first_normal_slot + 1;
context.warp_to_slot(slot).unwrap();
create_vote(
&mut context.banks_client,
&context.payer,
&context.last_blockhash,
&accounts.validator,
&accounts.voter.pubkey(),
&accounts.withdrawer.pubkey(),
&accounts.vote_account,
)
.await;
let rent = context.banks_client.get_rent().await.unwrap();
let minimum_pool_balance = get_minimum_pool_balance(
&mut context.banks_client,
&context.payer,
&context.last_blockhash,
)
.await;
let instructions = instruction::initialize(
&id(),
&accounts.vote_account.pubkey(),
&context.payer.pubkey(),
&rent,
minimum_pool_balance - 1,
);
let transaction = Transaction::new_signed_with_payer(
&instructions,
Some(&context.payer.pubkey()),
&[&context.payer],
context.last_blockhash,
);
let e = context
.banks_client
.process_transaction(transaction)
.await
.unwrap_err();
check_error(e, SinglePoolError::WrongRentAmount);
}