use solana_program::{pubkey, pubkey::Pubkey, system_program};
use solana_program_test::{processor, ProgramTest};
use solana_sdk::signer::{keypair::Keypair, Signer};
use spl_associated_token_account::{
get_associated_token_address, instruction::create_associated_token_account,
};
pub mod common;
use crate::common::utils::{mint_bootstrap, sign_send_instructions};
use access_protocol::{
entrypoint::process_instruction,
instruction::{
activate_stake_pool, admin_freeze, admin_mint, change_central_state_authority,
change_inflation, change_pool_minimum, change_pool_multiplier, claim_bond,
claim_bond_rewards, claim_pool_rewards, claim_rewards, close_stake_account,
close_stake_pool, crank, create_bond, create_central_state, create_stake_account,
create_stake_pool, edit_metadata, stake, unlock_bond_tokens, unstake,
},
state::BondAccount,
};
use mpl_token_metadata::instruction::update_metadata_accounts_v2;
use mpl_token_metadata::{instruction::create_metadata_accounts_v3, pda::find_metadata_account};
use spl_token::{instruction::set_authority, instruction::AuthorityType};
use access_protocol::instruction::migrate_central_state_v2;
use access_protocol::state::{ACCESS_NFT_PROGRAM_SIGNER, RoyaltyAccount};
#[tokio::test]
async fn functional_10s() {
let program_id = pubkey!("hxrotrKwueSFofXvCmCpYyKMjn1BhmwKtPxA1nLcv8m");
let mut program_test = ProgramTest::new(
"access_protocol",
program_id,
processor!(process_instruction),
);
program_test.add_program("mpl_token_metadata", mpl_token_metadata::ID, None);
let (central_state, _nonce) =
Pubkey::find_program_address(&[&program_id.to_bytes()], &program_id);
let authority = Keypair::new();
let (mint, _) = mint_bootstrap(None, 6, &mut program_test, &authority.pubkey());
let mut prg_test_ctx = program_test.start_with_context().await;
let (metadata_key, _) = find_metadata_account(&mint);
let daily_inflation: u64 = 1_000_000 * 500_000;
let create_central_state_ix = create_central_state(
program_id,
create_central_state::Accounts {
central_state: ¢ral_state,
system_program: &system_program::ID,
fee_payer: &prg_test_ctx.payer.pubkey(),
mint: &mint,
},
create_central_state::Params {
daily_inflation,
authority: prg_test_ctx.payer.pubkey(),
},
);
sign_send_instructions(&mut prg_test_ctx, vec![create_central_state_ix], vec![])
.await
.unwrap();
let create_metadata_ix = create_metadata_accounts_v3(
mpl_token_metadata::ID,
metadata_key,
mint,
authority.pubkey(),
prg_test_ctx.payer.pubkey(),
authority.pubkey(),
"Access Protocol".to_string(),
"ACS".to_string(),
"URI".to_string(),
None,
0,
false,
true,
None,
None,
None,
);
sign_send_instructions(
&mut prg_test_ctx,
vec![create_metadata_ix],
vec![&authority],
)
.await
.unwrap();
let metaplex_set_authority_to_cs_ix = update_metadata_accounts_v2(
mpl_token_metadata::ID,
metadata_key,
authority.pubkey(),
Some(central_state),
None,
None,
None,
);
sign_send_instructions(
&mut prg_test_ctx,
vec![metaplex_set_authority_to_cs_ix],
vec![&authority],
)
.await
.unwrap();
let set_authority_to_cs_ix = set_authority(
&spl_token::ID,
&mint,
Some(¢ral_state),
AuthorityType::MintTokens,
&authority.pubkey(),
&[],
)
.unwrap();
sign_send_instructions(
&mut prg_test_ctx,
vec![set_authority_to_cs_ix],
vec![&authority],
)
.await
.unwrap();
let ix = create_associated_token_account(
&prg_test_ctx.payer.pubkey(),
¢ral_state,
&mint,
&spl_token::ID,
);
sign_send_instructions(&mut prg_test_ctx, vec![ix], vec![]).await.unwrap();
let _central_state_vault = get_associated_token_address(¢ral_state, &mint);
let migrate_ix = migrate_central_state_v2(
program_id,
migrate_central_state_v2::Accounts {
fee_payer: &prg_test_ctx.payer.pubkey(),
central_state: ¢ral_state,
system_program: &system_program::ID,
},
migrate_central_state_v2::Params {},
);
sign_send_instructions(&mut prg_test_ctx, vec![migrate_ix], vec![]).await.unwrap();
let ix = edit_metadata(
program_id,
edit_metadata::Accounts {
central_state: ¢ral_state,
authority: &prg_test_ctx.payer.pubkey(),
metadata: &metadata_key,
metadata_program: &mpl_token_metadata::ID,
},
edit_metadata::Params {
name: "New name".to_string(),
symbol: "New symbol".to_string(),
uri: "New uri".to_string(),
},
);
sign_send_instructions(&mut prg_test_ctx, vec![ix], vec![])
.await
.unwrap();
let ix = create_associated_token_account(
&prg_test_ctx.payer.pubkey(),
¢ral_state,
&mint,
&spl_token::ID,
);
sign_send_instructions(&mut prg_test_ctx, vec![ix], vec![])
.await
.unwrap();
let authority_ata = get_associated_token_address(¢ral_state, &mint);
let stake_pool_owner = Keypair::new();
let staker = Keypair::new();
let create_ata_stake_pool_owner_ix = create_associated_token_account(
&prg_test_ctx.payer.pubkey(),
&stake_pool_owner.pubkey(),
&mint,
&spl_token::ID,
);
sign_send_instructions(
&mut prg_test_ctx,
vec![create_ata_stake_pool_owner_ix],
vec![],
)
.await
.unwrap();
let create_ata_staker_ix = create_associated_token_account(
&prg_test_ctx.payer.pubkey(),
&staker.pubkey(),
&mint,
&spl_token::ID,
);
sign_send_instructions(&mut prg_test_ctx, vec![create_ata_staker_ix], vec![])
.await
.unwrap();
let staker_token_acc = get_associated_token_address(&staker.pubkey(), &mint);
let stake_pool_owner_token_acc = get_associated_token_address(&staker.pubkey(), &mint);
let admin_mint_ix = admin_mint(
program_id,
admin_mint::Accounts {
authority: &prg_test_ctx.payer.pubkey(),
mint: &mint,
access_token_destination: &staker_token_acc,
central_state: ¢ral_state,
spl_token_program: &spl_token::ID,
},
admin_mint::Params {
amount: 10_000_000_000 * 1_000_000,
},
);
sign_send_instructions(&mut prg_test_ctx, vec![admin_mint_ix], vec![])
.await
.unwrap();
let (stake_pool_key, _) = Pubkey::find_program_address(
&[
"stake_pool".as_bytes(),
&stake_pool_owner.pubkey().to_bytes(),
],
&program_id,
);
let create_associated_instruction = create_associated_token_account(
&prg_test_ctx.payer.pubkey(),
&stake_pool_key,
&mint,
&spl_token::ID,
);
let pool_vault = get_associated_token_address(&stake_pool_key, &mint);
sign_send_instructions(
&mut prg_test_ctx,
vec![create_associated_instruction],
vec![],
)
.await
.unwrap();
let create_stake_pool_ix = create_stake_pool(
program_id,
create_stake_pool::Accounts {
stake_pool_account: &stake_pool_key,
system_program: &system_program::ID,
owner: &stake_pool_owner.pubkey(),
fee_payer: &prg_test_ctx.payer.pubkey(),
vault: &pool_vault,
central_state: ¢ral_state,
},
create_stake_pool::Params {
minimum_stake_amount: 10_000_000,
},
);
sign_send_instructions(&mut prg_test_ctx, vec![create_stake_pool_ix], vec![&stake_pool_owner])
.await
.unwrap();
let activate_stake_pool_ix = activate_stake_pool(
program_id,
activate_stake_pool::Accounts {
stake_pool: &stake_pool_key,
central_state: ¢ral_state,
},
activate_stake_pool::Params {},
);
sign_send_instructions(&mut prg_test_ctx, vec![activate_stake_pool_ix], vec![])
.await
.unwrap();
let ix = change_pool_multiplier(
program_id,
change_pool_multiplier::Accounts {
stake_pool: &stake_pool_key,
stake_pool_owner: &stake_pool_owner.pubkey(),
central_state: ¢ral_state,
},
change_pool_multiplier::Params { new_multiplier: 2 },
);
sign_send_instructions(&mut prg_test_ctx, vec![ix], vec![&stake_pool_owner])
.await
.unwrap();
let bond_amount = 50_000 * 1_000_000;
let (bond_key, _bond_nonce) =
BondAccount::create_key(&staker.pubkey(), bond_amount, &program_id);
let create_bond_ix = create_bond(
program_id,
create_bond::Accounts {
stake_pool: &stake_pool_key,
seller: &prg_test_ctx.payer.pubkey(),
bond_account: &bond_key,
system_program: &system_program::ID,
fee_payer: &prg_test_ctx.payer.pubkey(),
central_state: ¢ral_state,
},
create_bond::Params {
buyer: staker.pubkey(),
total_amount_sold: bond_amount,
seller_token_account: stake_pool_owner_token_acc,
total_quote_amount: 0,
quote_mint: Pubkey::default(),
unlock_period: 1,
unlock_amount: bond_amount,
unlock_start_date: 0,
seller_index: 0,
},
);
sign_send_instructions(&mut prg_test_ctx, vec![create_bond_ix], vec![])
.await
.unwrap();
let create_bond_with_unlock_period_zero_ix = create_bond(
program_id,
create_bond::Accounts {
stake_pool: &stake_pool_key,
seller: &prg_test_ctx.payer.pubkey(),
bond_account: &bond_key,
system_program: &system_program::ID,
fee_payer: &prg_test_ctx.payer.pubkey(),
central_state: ¢ral_state,
},
create_bond::Params {
buyer: staker.pubkey(),
total_amount_sold: bond_amount,
seller_token_account: stake_pool_owner_token_acc,
total_quote_amount: 0,
quote_mint: Pubkey::default(),
unlock_period: 0, unlock_amount: bond_amount,
unlock_start_date: 0,
seller_index: 0,
},
);
assert!(sign_send_instructions(
&mut prg_test_ctx,
vec![create_bond_with_unlock_period_zero_ix],
vec![]
)
.await
.is_err());
let claim_bond_ix = claim_bond(
program_id,
claim_bond::Accounts {
bond_account: &bond_key,
buyer: &staker.pubkey(),
quote_token_source: &staker_token_acc,
quote_token_destination: &stake_pool_owner_token_acc,
spl_token_program: &spl_token::ID,
stake_pool: &stake_pool_key,
access_mint: &mint,
pool_vault: &pool_vault,
central_state: ¢ral_state,
},
claim_bond::Params {},
);
sign_send_instructions(&mut prg_test_ctx, vec![claim_bond_ix], vec![&staker])
.await
.unwrap();
let (stake_acc_key, stake_nonce) = Pubkey::find_program_address(
&[
"stake_account".as_bytes(),
&staker.pubkey().to_bytes(),
&stake_pool_key.to_bytes(),
],
&program_id,
);
let create_stake_account_ix = create_stake_account(
program_id,
create_stake_account::Accounts {
stake_account: &stake_acc_key,
system_program: &system_program::ID,
fee_payer: &prg_test_ctx.payer.pubkey(),
stake_pool: &stake_pool_key,
central_state: ¢ral_state,
},
create_stake_account::Params {
nonce: stake_nonce,
owner: staker.pubkey(),
},
);
sign_send_instructions(&mut prg_test_ctx, vec![create_stake_account_ix], vec![])
.await
.unwrap();
let token_amount = 10_000_000;
let stake_ix = stake(
program_id,
stake::Accounts {
stake_account: &stake_acc_key,
stake_pool: &stake_pool_key,
token_owner: &staker.pubkey(),
source_token: &staker_token_acc,
spl_token_program: &spl_token::ID,
vault: &pool_vault,
central_state: ¢ral_state,
central_state_vault: &authority_ata,
},
stake::Params {
amount: token_amount,
},
);
sign_send_instructions(&mut prg_test_ctx, vec![stake_ix], vec![&staker])
.await
.unwrap();
let mut current_slot = 5_000;
prg_test_ctx.warp_to_slot(current_slot).unwrap();
for _ in 0..10 {
current_slot += 5_000;
prg_test_ctx.warp_to_slot(current_slot).unwrap();
}
let crank_ix = crank(
program_id,
crank::Accounts {
stake_pool: &stake_pool_key,
central_state: ¢ral_state,
},
crank::Params {},
);
sign_send_instructions(&mut prg_test_ctx, vec![crank_ix], vec![])
.await
.unwrap();
current_slot += 5_000;
prg_test_ctx.warp_to_slot(current_slot).unwrap();
let claim_stake_pool_ix = claim_pool_rewards(
program_id,
claim_pool_rewards::Accounts {
stake_pool: &stake_pool_key,
owner: &stake_pool_owner.pubkey(),
rewards_destination: &stake_pool_owner_token_acc,
central_state: ¢ral_state,
mint: &mint,
spl_token_program: &spl_token::ID,
owner_royalty_account: &RoyaltyAccount::create_key(&stake_pool_owner.pubkey(), &program_id).0,
royalty_ata: None,
},
claim_pool_rewards::Params {},
true,
);
sign_send_instructions(
&mut prg_test_ctx,
vec![claim_stake_pool_ix],
vec![&stake_pool_owner],
)
.await
.unwrap();
let claim_bond_rewards_ix = claim_bond_rewards(
program_id,
claim_bond_rewards::Accounts {
stake_pool: &stake_pool_key,
bond_account: &bond_key,
bond_owner: &staker.pubkey(),
rewards_destination: &staker_token_acc,
central_state: ¢ral_state,
mint: &mint,
spl_token_program: &spl_token::ID,
},
claim_bond_rewards::Params {},
false,
);
sign_send_instructions(&mut prg_test_ctx, vec![claim_bond_rewards_ix], vec![])
.await
.unwrap();
let claim_ix = claim_rewards(
program_id,
claim_rewards::Accounts {
stake_pool: &stake_pool_key,
stake_account: &stake_acc_key,
owner: &staker.pubkey(),
rewards_destination: &staker_token_acc,
central_state: ¢ral_state,
mint: &mint,
access_nft_signer: &ACCESS_NFT_PROGRAM_SIGNER,
spl_token_program: &spl_token::ID,
owner_royalty_account: &RoyaltyAccount::create_key(&staker.pubkey(), &program_id).0,
royalty_ata: None,
},
claim_rewards::Params {
allow_zero_rewards: false,
},
false,
);
sign_send_instructions(&mut prg_test_ctx, vec![claim_ix], vec![&staker])
.await
.unwrap();
let crank_ix = crank(
program_id,
crank::Accounts {
stake_pool: &stake_pool_key,
central_state: ¢ral_state,
},
crank::Params {},
);
sign_send_instructions(&mut prg_test_ctx, vec![crank_ix], vec![])
.await
.unwrap();
current_slot += 1;
prg_test_ctx.warp_to_slot(current_slot).unwrap();
let claim_bond_rewards_ix = claim_bond_rewards(
program_id,
claim_bond_rewards::Accounts {
stake_pool: &stake_pool_key,
bond_account: &bond_key,
bond_owner: &staker.pubkey(),
rewards_destination: &staker_token_acc,
central_state: ¢ral_state,
mint: &mint,
spl_token_program: &spl_token::ID,
},
claim_bond_rewards::Params {},
false,
);
sign_send_instructions(&mut prg_test_ctx, vec![claim_bond_rewards_ix], vec![])
.await
.unwrap();
let unlock_ix = unlock_bond_tokens(
program_id,
unlock_bond_tokens::Accounts {
bond_account: &bond_key,
bond_owner: &staker.pubkey(),
mint: &mint,
access_token_destination: &staker_token_acc,
central_state: ¢ral_state,
spl_token_program: &spl_token::ID,
stake_pool: &stake_pool_key,
pool_vault: &pool_vault,
},
unlock_bond_tokens::Params {},
);
sign_send_instructions(&mut prg_test_ctx, vec![unlock_ix], vec![&staker])
.await
.unwrap();
let new_inflation = 2 * daily_inflation;
let change_inflation_ix = change_inflation(
program_id,
change_inflation::Accounts {
central_state: ¢ral_state,
authority: &prg_test_ctx.payer.pubkey(),
mint: &mint,
},
change_inflation::Params {
daily_inflation: new_inflation,
},
);
sign_send_instructions(&mut prg_test_ctx, vec![change_inflation_ix], vec![])
.await
.unwrap();
let change_min_ix = change_pool_minimum(
program_id,
change_pool_minimum::Accounts {
stake_pool: &stake_pool_key,
stake_pool_owner: &stake_pool_owner.pubkey(),
central_state: ¢ral_state,
},
change_pool_minimum::Params {
new_minimum: 10_000_000 / 2,
},
);
sign_send_instructions(
&mut prg_test_ctx,
vec![change_min_ix],
vec![&stake_pool_owner],
)
.await
.unwrap();
let claim_ix = claim_rewards(
program_id,
claim_rewards::Accounts {
stake_pool: &stake_pool_key,
stake_account: &stake_acc_key,
owner: &staker.pubkey(),
rewards_destination: &staker_token_acc,
central_state: ¢ral_state,
mint: &mint,
access_nft_signer: &ACCESS_NFT_PROGRAM_SIGNER,
spl_token_program: &spl_token::ID,
owner_royalty_account: &RoyaltyAccount::create_key(&staker.pubkey(), &program_id).0,
royalty_ata: None,
},
claim_rewards::Params {
allow_zero_rewards: false,
},
false,
);
sign_send_instructions(&mut prg_test_ctx, vec![claim_ix], vec![&staker])
.await
.unwrap();
let unstake_ix = unstake(
program_id,
unstake::Accounts {
stake_account: &stake_acc_key,
stake_pool: &stake_pool_key,
owner: &staker.pubkey(),
destination_token: &staker_token_acc,
spl_token_program: &spl_token::ID,
vault: &pool_vault,
central_state: ¢ral_state,
},
unstake::Params {
amount: token_amount,
},
);
sign_send_instructions(&mut prg_test_ctx, vec![unstake_ix], vec![&staker])
.await
.unwrap();
current_slot += 5_000;
prg_test_ctx.warp_to_slot(current_slot).unwrap();
let freeze_stake_acc_ix = admin_freeze(
program_id,
admin_freeze::Accounts {
central_state: ¢ral_state,
account_to_freeze: &stake_pool_key,
authority: &prg_test_ctx.payer.pubkey(),
},
admin_freeze::Params {},
);
sign_send_instructions(&mut prg_test_ctx, vec![freeze_stake_acc_ix], vec![])
.await
.unwrap();
current_slot += 5000;
prg_test_ctx.warp_to_slot(current_slot).unwrap();
let freeze_stake_acc_ix = admin_freeze(
program_id,
admin_freeze::Accounts {
central_state: ¢ral_state,
account_to_freeze: &stake_pool_key,
authority: &prg_test_ctx.payer.pubkey(),
},
admin_freeze::Params {},
);
sign_send_instructions(&mut prg_test_ctx, vec![freeze_stake_acc_ix], vec![])
.await
.unwrap();
let freeze_stake_acc_ix = admin_freeze(
program_id,
admin_freeze::Accounts {
central_state: ¢ral_state,
account_to_freeze: ¢ral_state,
authority: &prg_test_ctx.payer.pubkey(),
},
admin_freeze::Params {},
);
assert!(
sign_send_instructions(&mut prg_test_ctx, vec![freeze_stake_acc_ix], vec![])
.await
.is_err()
);
let close_stake_account_ix = close_stake_account(
program_id,
close_stake_account::Accounts {
stake_account: &stake_acc_key,
owner: &staker.pubkey(),
central_state: ¢ral_state,
},
close_stake_account::Params {},
);
sign_send_instructions(
&mut prg_test_ctx,
vec![close_stake_account_ix],
vec![&staker],
)
.await
.unwrap();
let close_stake_pool_ix = close_stake_pool(
program_id,
close_stake_pool::Accounts {
pool_vault: &pool_vault,
stake_pool_account: &stake_pool_key,
owner: &stake_pool_owner.pubkey(),
central_state: ¢ral_state,
},
close_stake_pool::Params {},
);
sign_send_instructions(
&mut prg_test_ctx,
vec![close_stake_pool_ix],
vec![&stake_pool_owner],
)
.await
.unwrap();
let ix = change_central_state_authority(
program_id,
change_central_state_authority::Accounts {
central_state: ¢ral_state,
authority: &prg_test_ctx.payer.pubkey(),
},
change_central_state_authority::Params {
new_authority: Keypair::new().pubkey(),
},
);
sign_send_instructions(&mut prg_test_ctx, vec![ix], vec![])
.await
.unwrap();
}