access-protocol 0.7.15

Access Protocol
Documentation
use solana_program::pubkey::Pubkey;
use solana_sdk::signer::Signer;

use solana_test_framework::*;

use access_protocol::state::Tag;

use crate::common::test_runner::TestRunner;

pub mod common;

const DAILY_INFLATION: u64 = 1_000_000;

#[tokio::test]
async fn end_to_end() {
    // Setup the token + basic accounts
    let mut tr = TestRunner::new(DAILY_INFLATION).await.unwrap();

    let cs_stats = tr.central_state_stats().await.unwrap();
    assert_eq!(cs_stats.account.tag, Tag::CentralStateV2);
    assert_eq!(cs_stats.account.daily_inflation, DAILY_INFLATION);
    assert_eq!(cs_stats.account.token_mint.to_string(), tr.get_mint().to_string());
    assert_eq!(cs_stats.account.authority, tr.get_authority());
    assert_eq!(cs_stats.account.creation_time, tr.get_current_time().await);
    assert_eq!(cs_stats.account.total_staked, 0);
    assert_eq!(cs_stats.account.total_staked_snapshot, 0);
    assert_eq!(cs_stats.account.last_snapshot_offset, 0);

    // Create users
    let stake_pool_owner = tr.create_user_with_ata().await.unwrap();
    let staker = tr.create_user_with_ata().await.unwrap();

    // Create stake pool
    tr.create_pool(&stake_pool_owner, 1000)
        .await
        .unwrap();
    let pool_stats = tr.pool_stats(stake_pool_owner.pubkey()).await.unwrap();
    assert_eq!(pool_stats.header.tag, Tag::InactiveStakePool as u8);
    assert_eq!(pool_stats.header.current_day_idx, 0);
    assert_eq!(pool_stats.header.minimum_stake_amount, 1000);
    assert_eq!(pool_stats.header.total_staked, 0);
    assert_eq!(pool_stats.header.last_claimed_offset, 0);
    assert_eq!(pool_stats.header.stakers_part, 50);
    assert_eq!(
        Pubkey::from(pool_stats.header.owner).to_string(),
        stake_pool_owner.pubkey().to_string()
    );

    // Activate stake pool
    tr.activate_stake_pool(&stake_pool_owner.pubkey())
        .await
        .unwrap();
    let pool_stats = tr.pool_stats(stake_pool_owner.pubkey()).await.unwrap();
    assert_eq!(pool_stats.header.tag, Tag::StakePool as u8);

    // Create stake account
    tr.create_stake_account(&stake_pool_owner.pubkey(), &staker.pubkey())
        .await
        .unwrap();
    let stake_account_stats = tr
        .stake_account_stats(staker.pubkey(), stake_pool_owner.pubkey())
        .await
        .unwrap();
    assert_eq!(stake_account_stats.tag, Tag::StakeAccount);
    assert_eq!(stake_account_stats.owner, staker.pubkey());
    assert_eq!(stake_account_stats.stake_amount, 0);
    let stake_pool_pda_key = tr.get_pool_pda(&stake_pool_owner.pubkey());
    assert_eq!(stake_account_stats.stake_pool, stake_pool_pda_key);
    assert_eq!(stake_account_stats.last_claimed_offset, 0);
    assert_eq!(stake_account_stats.pool_minimum_at_creation, 1000);

    // Create a bond
    let bond_amount = 5_000_000;
    let unlock_after = 10;
    tr.create_bond(
        &stake_pool_owner.pubkey(),
        &staker.pubkey(),
        bond_amount,
        1,
        unlock_after,
        1,
    )
    .await
    .unwrap();
    let bond_stats = tr
        .bond_stats(staker.pubkey(), stake_pool_owner.pubkey(), bond_amount)
        .await
        .unwrap();
    assert_eq!(bond_stats.tag, Tag::InactiveBondAccount);
    assert_eq!(bond_stats.owner, staker.pubkey());
    assert_eq!(bond_stats.total_amount_sold, bond_amount);
    assert_eq!(bond_stats.total_staked, bond_amount);
    assert_eq!(bond_stats.total_quote_amount, 0);
    assert_eq!(
        bond_stats.unlock_start_date,
        tr.get_current_time().await + unlock_after
    );
    assert_eq!(bond_stats.unlock_period, 1);
    assert_eq!(bond_stats.unlock_amount, bond_amount);
    assert_eq!(
        bond_stats.last_unlock_time,
        tr.get_current_time().await + unlock_after
    );
    assert_eq!(bond_stats.total_unlocked_amount, 0);
    assert_eq!(bond_stats.pool_minimum_at_creation, 1000);
    assert_eq!(bond_stats.stake_pool, stake_pool_pda_key);
    assert_eq!(bond_stats.last_claimed_offset, 0);
    assert_eq!(bond_stats.sellers[0], tr.get_bond_seller());

    // Claim bond
    tr.claim_bond(&stake_pool_owner.pubkey(), &staker.pubkey())
        .await
        .unwrap();
    let bond_stats = tr
        .bond_stats(staker.pubkey(), stake_pool_owner.pubkey(), bond_amount)
        .await
        .unwrap();
    let bond_creation_time = tr.get_current_time().await;
    assert_eq!(bond_stats.tag, Tag::BondAccount);
    assert_eq!(bond_stats.owner, staker.pubkey());
    assert_eq!(bond_stats.total_amount_sold, bond_amount);
    assert_eq!(bond_stats.total_staked, bond_amount);
    assert_eq!(bond_stats.total_quote_amount, 0);
    assert_eq!(bond_stats.quote_mint, tr.get_mint());
    assert_eq!(bond_stats.seller_token_account, tr.get_bond_seller_ata());
    assert_eq!(
        bond_stats.unlock_start_date,
        bond_creation_time + unlock_after
    );
    assert_eq!(bond_stats.unlock_period, 1);
    assert_eq!(bond_stats.unlock_amount, bond_amount);
    assert_eq!(
        bond_stats.last_unlock_time,
        bond_creation_time + unlock_after
    );
    assert_eq!(bond_stats.total_unlocked_amount, 0);
    assert_eq!(bond_stats.pool_minimum_at_creation, 1000);
    assert_eq!(bond_stats.stake_pool, stake_pool_pda_key);
    assert_eq!(bond_stats.last_claimed_offset, 0);
    assert_eq!(bond_stats.sellers[0], tr.get_bond_seller());

    let _stake_pool_stats = tr.pool_stats(stake_pool_owner.pubkey()).await.unwrap();

    // Crank
    let sleep_time = (86_400 * 3 / 2) as u64;
    tr.sleep(sleep_time).await.unwrap();
    tr.crank_pool(&stake_pool_owner.pubkey()).await.unwrap();

    // Unlock bond should fail - unclaimed rewards
    tr.unlock_bond(&stake_pool_owner.pubkey(), &staker)
        .await
        .unwrap_err();
    tr.sleep(1).await.unwrap();

    // Claim bond rewards
    tr.claim_bond_rewards(&stake_pool_owner.pubkey(), &staker)
        .await
        .unwrap();
    let bond_stats = tr
        .bond_stats(staker.pubkey(), stake_pool_owner.pubkey(), bond_amount)
        .await
        .unwrap();
    assert_eq!(bond_stats.tag, Tag::BondAccount);
    assert_eq!(bond_stats.owner, staker.pubkey());
    assert_eq!(bond_stats.total_amount_sold, bond_amount);
    assert_eq!(bond_stats.total_staked, bond_amount);
    assert_eq!(bond_stats.total_quote_amount, 0);
    assert_eq!(bond_stats.quote_mint, tr.get_mint());
    assert_eq!(bond_stats.seller_token_account, tr.get_bond_seller_ata());
    assert_eq!(
        bond_stats.unlock_start_date,
        bond_creation_time + unlock_after
    );
    assert_eq!(bond_stats.unlock_period, 1);
    assert_eq!(bond_stats.unlock_amount, bond_amount);
    // assert_eq!(bond_stats.last_unlock_time, bond_creation_time + unlock_after);
    assert_eq!(bond_stats.total_unlocked_amount, 0);
    assert_eq!(bond_stats.pool_minimum_at_creation, 1000);
    assert_eq!(bond_stats.stake_pool, stake_pool_pda_key);
    assert_eq!(bond_stats.last_claimed_offset, 1);
    assert_eq!(bond_stats.sellers[0], tr.get_bond_seller());
    let staker_stats = tr.staker_stats(staker.pubkey()).await.unwrap();
    assert_eq!(staker_stats.balance, DAILY_INFLATION / 2);

    // Unlock bond
    tr.unlock_bond(&stake_pool_owner.pubkey(), &staker)
        .await
        .unwrap();
    let bond_stats = tr
        .bond_stats(staker.pubkey(), stake_pool_owner.pubkey(), bond_amount)
        .await
        .unwrap();
    assert_eq!(bond_stats.tag, Tag::BondAccount);
    assert_eq!(bond_stats.owner, staker.pubkey());
    assert_eq!(bond_stats.total_amount_sold, bond_amount);
    assert_eq!(bond_stats.total_staked, 0);
    assert_eq!(bond_stats.total_quote_amount, 0);
    assert_eq!(bond_stats.quote_mint, tr.get_mint());
    assert_eq!(bond_stats.seller_token_account, tr.get_bond_seller_ata());
    assert_eq!(
        bond_stats.unlock_start_date,
        bond_creation_time + unlock_after
    );
    assert_eq!(bond_stats.unlock_period, 1);
    assert_eq!(bond_stats.unlock_amount, bond_amount);
    assert_eq!(bond_stats.last_unlock_time, tr.get_current_time().await);
    assert_eq!(bond_stats.total_unlocked_amount, bond_amount);
    assert_eq!(bond_stats.pool_minimum_at_creation, 1000);
    assert_eq!(bond_stats.stake_pool, stake_pool_pda_key);
    assert_eq!(bond_stats.last_claimed_offset, 1);
    assert_eq!(bond_stats.sellers[0], tr.get_bond_seller());

    // Stake
    let stake_amount = 10_000;
    tr.stake(&stake_pool_owner.pubkey(), &staker, stake_amount)
        .await
        .unwrap();
    let staker_stats = tr.staker_stats(staker.pubkey()).await.unwrap();
    assert_eq!(staker_stats.balance, 4989800 + DAILY_INFLATION / 2);
    let stake_account_stats = tr
        .stake_account_stats(staker.pubkey(), stake_pool_owner.pubkey())
        .await
        .unwrap();
    assert_eq!(stake_account_stats.tag, Tag::StakeAccount);
    assert_eq!(stake_account_stats.owner, staker.pubkey());
    assert_eq!(stake_account_stats.stake_amount, stake_amount);
    assert_eq!(stake_account_stats.stake_pool, stake_pool_pda_key);
    assert_eq!(stake_account_stats.last_claimed_offset, 1);
    assert_eq!(stake_account_stats.pool_minimum_at_creation, 1000);

    let _stake_pool_stats = tr.pool_stats(stake_pool_owner.pubkey()).await.unwrap();

    // Crank
    tr.sleep(86_400 / 2 + 1000).await.unwrap();
    tr.crank_pool(&stake_pool_owner.pubkey()).await.unwrap();
    let stake_pool_stats = tr.pool_stats(stake_pool_owner.pubkey()).await.unwrap();
    assert_eq!(stake_pool_stats.header.tag, Tag::StakePool as u8);
    assert_eq!(stake_pool_stats.header.current_day_idx, 2);
    assert_eq!(stake_pool_stats.header.minimum_stake_amount, 1000);
    assert_eq!(stake_pool_stats.header.total_staked, stake_amount);
    assert_eq!(stake_pool_stats.header.last_claimed_offset, 0);
    assert_eq!(stake_pool_stats.header.stakers_part, 50);
    assert_eq!(
        Pubkey::from(pool_stats.header.owner).to_string(),
        stake_pool_owner.pubkey().to_string()
    );

    // Claim bond rewards
    tr.claim_bond_rewards(&stake_pool_owner.pubkey(), &staker)
        .await
        .unwrap();
    let bond_stats = tr
        .bond_stats(staker.pubkey(), stake_pool_owner.pubkey(), bond_amount)
        .await
        .unwrap();
    assert_eq!(bond_stats.tag, Tag::BondAccount);
    assert_eq!(bond_stats.owner, staker.pubkey());
    assert_eq!(bond_stats.total_amount_sold, bond_amount);
    assert_eq!(bond_stats.total_staked, 0);
    assert_eq!(bond_stats.total_quote_amount, 0);
    assert_eq!(bond_stats.quote_mint, tr.get_mint());
    assert_eq!(bond_stats.seller_token_account, tr.get_bond_seller_ata());
    assert_eq!(
        bond_stats.unlock_start_date,
        bond_creation_time + unlock_after
    );
    assert_eq!(bond_stats.unlock_period, 1);
    assert_eq!(bond_stats.unlock_amount, bond_amount);
    // assert_eq!(bond_stats.last_unlock_time, bond_creation_time + unlock_after);
    assert_eq!(bond_stats.total_unlocked_amount, bond_amount);
    assert_eq!(bond_stats.pool_minimum_at_creation, 1000);
    assert_eq!(bond_stats.stake_pool, stake_pool_pda_key);
    assert_eq!(bond_stats.last_claimed_offset, 2);
    assert_eq!(bond_stats.sellers[0], tr.get_bond_seller());
    let staker_stats = tr.staker_stats(staker.pubkey()).await.unwrap();
    assert_eq!(staker_stats.balance, 4989800 + DAILY_INFLATION / 2);

    // Claim pool rewards
    tr.claim_pool_rewards(&stake_pool_owner).await.unwrap();
    let stake_pool_stats = tr.pool_stats(stake_pool_owner.pubkey()).await.unwrap();
    assert_eq!(stake_pool_stats.header.tag, Tag::StakePool as u8);
    assert_eq!(stake_pool_stats.header.current_day_idx, 2);
    assert_eq!(stake_pool_stats.header.minimum_stake_amount, 1000);
    assert_eq!(stake_pool_stats.header.total_staked, stake_amount);
    assert_eq!(stake_pool_stats.header.last_claimed_offset, 2);
    assert_eq!(stake_pool_stats.balance, 2 * DAILY_INFLATION / 2);

    // Claim rewards
    let staker_stats = tr.staker_stats(staker.pubkey()).await.unwrap();
    assert_eq!(staker_stats.balance, 4989800 + DAILY_INFLATION / 2);
    tr.claim_staker_rewards(&stake_pool_owner.pubkey(), &staker)
        .await
        .unwrap();
    let staker_stats = tr.staker_stats(staker.pubkey()).await.unwrap();
    assert_eq!(staker_stats.balance, 4989800 + 2 * DAILY_INFLATION / 2);

    // Change inflation
    tr.change_inflation(DAILY_INFLATION * 2).await.unwrap();
    let stats = tr.central_state_stats().await.unwrap();
    assert_eq!(stats.account.daily_inflation, DAILY_INFLATION * 2);

    // Change pool minimum
    tr.change_pool_minimum(&stake_pool_owner, 2000)
        .await
        .unwrap();
    let stake_pool_stats = tr.pool_stats(stake_pool_owner.pubkey()).await.unwrap();
    assert_eq!(stake_pool_stats.header.minimum_stake_amount, 2000);
    let staker_account_stats = tr
        .stake_account_stats(staker.pubkey(), stake_pool_owner.pubkey())
        .await
        .unwrap();
    assert_eq!(staker_account_stats.pool_minimum_at_creation, 1000);

    // Change pool multiplier
    tr.change_pool_multiplier(&stake_pool_owner, 60)
        .await
        .unwrap();
    let stake_pool_stats = tr.pool_stats(stake_pool_owner.pubkey()).await.unwrap();
    assert_eq!(stake_pool_stats.header.stakers_part, 60);

    // Crank
    tr.sleep(86_400).await.unwrap();
    tr.crank_pool(&stake_pool_owner.pubkey()).await.unwrap();
    let stake_pool_stats = tr.pool_stats(stake_pool_owner.pubkey()).await.unwrap();
    assert_eq!(stake_pool_stats.header.tag, Tag::StakePool as u8);
    assert_eq!(stake_pool_stats.header.current_day_idx, 3);
    assert_eq!(stake_pool_stats.header.minimum_stake_amount, 2000);
    assert_eq!(stake_pool_stats.header.total_staked, stake_amount);
    assert_eq!(stake_pool_stats.header.last_claimed_offset, 2);
    assert_eq!(stake_pool_stats.balance, 2 * DAILY_INFLATION / 2);

    // Claim pool rewards
    tr.claim_pool_rewards(&stake_pool_owner).await.unwrap();
    let stake_pool_stats = tr.pool_stats(stake_pool_owner.pubkey()).await.unwrap();
    assert_eq!(stake_pool_stats.header.tag, Tag::StakePool as u8);
    assert_eq!(stake_pool_stats.header.current_day_idx, 3);
    assert_eq!(stake_pool_stats.header.minimum_stake_amount, 2000);
    assert_eq!(stake_pool_stats.header.total_staked, stake_amount);
    assert_eq!(stake_pool_stats.header.last_claimed_offset, 3);
    assert_eq!(
        stake_pool_stats.balance,
        2 * DAILY_INFLATION / 2 + DAILY_INFLATION * 2 * 2 / 5
    );

    // Claim rewards
    tr.claim_staker_rewards(&stake_pool_owner.pubkey(), &staker)
        .await
        .unwrap();
    let staker_stats = tr.staker_stats(staker.pubkey()).await.unwrap();
    assert_eq!(
        staker_stats.balance,
        4989800 + 2 * DAILY_INFLATION / 2 + DAILY_INFLATION * 2 * 3 / 5
    );
}