access-protocol 0.7.15

Access Protocol
Documentation
use solana_sdk::signer::Signer;
use solana_test_framework::*;

use access_protocol::state::STAKE_BUFFER_LEN;

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

pub mod common;

const DAY: u64 = 86400;
const DAILY_INFLATION: u64 = 10_000_000_000_000_000; // 1M of tokens

mod rewards_wrap {
    use super::*;

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

        // Setup 2 pool owners
        let pool_owner = tr.create_user_with_ata().await.unwrap();
        let pool_owner2 = tr.create_user_with_ata().await.unwrap();

        // Setup all the pools
        tr.create_pool(&pool_owner, 1_000_000_000)
            .await
            .unwrap();
        tr.create_pool(&pool_owner2, 1_000_000_000)
            .await
            .unwrap();

        // Activate the pools
        tr.activate_stake_pool(&pool_owner.pubkey()).await.unwrap();
        tr.activate_stake_pool(&pool_owner2.pubkey()).await.unwrap();

        // Setup 1 staker with 5_100_000_000 tokens in his account and appropriate stake accounts pool 1
        let staker = tr.create_user_with_ata().await.unwrap();
        tr.mint(&staker.pubkey(), 5_100_000_000).await.unwrap();
        tr.create_stake_account(&pool_owner.pubkey(), &staker.pubkey())
            .await
            .unwrap();

        // Stake 5_000_000_000 tokens to pool 1 and 5_000_000_000 tokens to pool 2
        tr.stake(&pool_owner.pubkey(), &staker, 5_000_000_000)
            .await
            .unwrap();

        // airdrop 5_000_000_000 tokens to staker to pool 2
        tr.create_bond(
            &pool_owner2.pubkey(),
            &staker.pubkey(),
            5_000_000_000,
            1,
            8 * DAY as i64,
            1,
        )
            .await
            .unwrap();
        tr.claim_bond(&pool_owner2.pubkey(), &staker.pubkey())
            .await
            .unwrap();

        // crank STAKE_BUFFER_LEN times
        for _ in 0..2 * STAKE_BUFFER_LEN + 3 {
            tr.sleep(DAY).await.unwrap();
            tr.crank_pool(&pool_owner.pubkey()).await.unwrap();
            tr.crank_pool(&pool_owner2.pubkey()).await.unwrap();
        }

        // Claim pool 1 rewards - owner gets (DAILY_INFLATION / 4) every day
        tr.claim_pool_rewards(&pool_owner).await.unwrap();
        let pool_stats = tr.pool_stats(pool_owner.pubkey()).await.unwrap();
        assert_eq!(pool_stats.balance, STAKE_BUFFER_LEN * DAILY_INFLATION / 4);

        // Claim staker rewards in pool 1 - staker gets (DAILY_INFLATION / 4) every day
        tr.claim_staker_rewards(&pool_owner.pubkey(), &staker)
            .await
            .unwrap();
        let staker_stats = tr.staker_stats(staker.pubkey()).await.unwrap();
        assert_eq!(staker_stats.balance, STAKE_BUFFER_LEN * DAILY_INFLATION / 4);

        // Claim pool 2 rewards - owner gets (DAILY_INFLATION / 4) every day
        tr.claim_pool_rewards(&pool_owner2).await.unwrap();
        let pool_stats = tr.pool_stats(pool_owner2.pubkey()).await.unwrap();
        assert_eq!(pool_stats.balance, STAKE_BUFFER_LEN * DAILY_INFLATION / 4);

        // Claim bond rewards - staker gets another (DAILY_INFLATION / 4) every day, but he already had (DAILY_INFLATION / 4) from previous claim
        tr.claim_bond_rewards(&pool_owner2.pubkey(), &staker)
            .await
            .unwrap();
        let staker_stats = tr.staker_stats(staker.pubkey()).await.unwrap();
        assert_eq!(
            staker_stats.balance,
            2 * (STAKE_BUFFER_LEN * DAILY_INFLATION / 4)
        );
    }

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

        // Setup 2 pool owners
        let pool_owner = tr.create_user_with_ata().await.unwrap();
        let pool_owner2 = tr.create_user_with_ata().await.unwrap();

        // Setup all the pools
        tr.create_pool(&pool_owner, 1_000_000_000)
            .await
            .unwrap();
        tr.create_pool(&pool_owner2, 1_000_000_000)
            .await
            .unwrap();

        // Activate the pools
        tr.activate_stake_pool(&pool_owner.pubkey()).await.unwrap();
        tr.activate_stake_pool(&pool_owner2.pubkey()).await.unwrap();

        // Setup 1 staker with 5_100_000_000 tokens in his account and appropriate stake accounts pool 1
        let staker = tr.create_user_with_ata().await.unwrap();
        tr.mint(&staker.pubkey(), 5_100_000_000).await.unwrap();
        tr.create_stake_account(&pool_owner.pubkey(), &staker.pubkey())
            .await
            .unwrap();

        // Stake 5_000_000_000 tokens to pool 1 and 5_000_000_000 tokens to pool 2
        tr.stake(&pool_owner.pubkey(), &staker, 5_000_000_000)
            .await
            .unwrap();

        // airdrop 5_000_000_000 tokens to staker to pool 2
        tr.create_bond(
            &pool_owner2.pubkey(),
            &staker.pubkey(),
            5_000_000_000,
            1,
            8 * DAY as i64,
            1,
        )
            .await
            .unwrap();
        tr.claim_bond(&pool_owner2.pubkey(), &staker.pubkey())
            .await
            .unwrap();

        // crank STAKE_BUFFER_LEN times
        for i in 0..STAKE_BUFFER_LEN + 10 {
            tr.sleep(DAY).await.unwrap();
            tr.crank_pool(&pool_owner.pubkey()).await.unwrap();
            tr.crank_pool(&pool_owner2.pubkey()).await.unwrap();

            // Claim pool 1 rewards  - owner gets (DAILY_INFLATION / 4) every day
            tr.claim_pool_rewards(&pool_owner).await.unwrap();
            let pool_stats = tr.pool_stats(pool_owner.pubkey()).await.unwrap();
            assert_eq!(pool_stats.balance, (i + 1) * DAILY_INFLATION / 4);

            // Claim staker rewards in pool 1 - staker gets (DAILY_INFLATION / 4) every day and he already had (2 * i * DAILY_INFLATION / 4) from previous iteration
            tr.claim_staker_rewards(&pool_owner.pubkey(), &staker)
                .await
                .unwrap();
            let staker_stats = tr.staker_stats(staker.pubkey()).await.unwrap();
            assert_eq!(
                staker_stats.balance,
                (2 * i + 1) * DAILY_INFLATION / 4
            );

            // Claim pool 2 rewards - owner gets (DAILY_INFLATION / 4) every day
            tr.claim_pool_rewards(&pool_owner2).await.unwrap();
            let pool_stats = tr.pool_stats(pool_owner2.pubkey()).await.unwrap();
            assert_eq!(pool_stats.balance, (i + 1) * DAILY_INFLATION / 4);

            // Claim bond rewards - staker gets another (DAILY_INFLATION / 4) every day
            tr.claim_bond_rewards(&pool_owner2.pubkey(), &staker)
                .await
                .unwrap();
            let staker_stats = tr.staker_stats(staker.pubkey()).await.unwrap();
            assert_eq!(staker_stats.balance, (i + 1) * DAILY_INFLATION / 2);
        }
    }

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

        // Setup 2 pool owners
        let pool_owner = tr.create_user_with_ata().await.unwrap();
        let pool_owner2 = tr.create_user_with_ata().await.unwrap();

        // Setup all the pools
        tr.create_pool(&pool_owner, 1_000_000_000)
            .await
            .unwrap();
        tr.create_pool(&pool_owner2, 1_000_000_000)
            .await
            .unwrap();

        // Activate the pools
        tr.activate_stake_pool(&pool_owner.pubkey()).await.unwrap();
        tr.activate_stake_pool(&pool_owner2.pubkey()).await.unwrap();

        // Setup 1 staker with 5_100_000_000 tokens in his account and appropriate stake accounts pool 1
        let staker = tr.create_user_with_ata().await.unwrap();
        tr.mint(&staker.pubkey(), 5_100_000_000).await.unwrap();
        tr.create_stake_account(&pool_owner.pubkey(), &staker.pubkey())
            .await
            .unwrap();

        // Stake 5_000_000_000 tokens to pool 1 and 5_000_000_000 tokens to pool 2
        tr.stake(&pool_owner.pubkey(), &staker, 5_000_000_000)
            .await
            .unwrap();

        // airdrop 5_000_000_000 tokens to staker to pool 2
        tr.create_bond(
            &pool_owner2.pubkey(),
            &staker.pubkey(),
            5_000_000_000,
            1,
            8 * DAY as i64,
            1,
        )
            .await
            .unwrap();
        tr.claim_bond(&pool_owner2.pubkey(), &staker.pubkey())
            .await
            .unwrap();

        // crank STAKE_BUFFER_LEN times
        for i in 0..STAKE_BUFFER_LEN - 5 {
            tr.sleep(DAY).await.unwrap();
            tr.crank_pool(&pool_owner.pubkey()).await.unwrap();
            tr.crank_pool(&pool_owner2.pubkey()).await.unwrap();

            // Claim pool 1 rewards - owner gets (DAILY_INFLATION / 4) every day
            tr.claim_pool_rewards(&pool_owner).await.unwrap();
            let pool_stats = tr.pool_stats(pool_owner.pubkey()).await.unwrap();
            assert_eq!(pool_stats.balance, (i + 1) * DAILY_INFLATION / 4);

            // Claim staker rewards - staker gets (DAILY_INFLATION / 4) every day and he already had (i * DAILY_INFLATION / 4) from previous iteration
            tr.claim_staker_rewards(&pool_owner.pubkey(), &staker)
                .await
                .unwrap();
            let staker_stats = tr.staker_stats(staker.pubkey()).await.unwrap();
            assert_eq!(
                staker_stats.balance,
                2 * i * DAILY_INFLATION / 4 + DAILY_INFLATION / 4
            );

            // Claim pool 2 rewards - owner gets (DAILY_INFLATION / 4) every day
            tr.claim_pool_rewards(&pool_owner2).await.unwrap();
            let pool_stats = tr.pool_stats(pool_owner2.pubkey()).await.unwrap();
            assert_eq!(pool_stats.balance, (i + 1) * DAILY_INFLATION / 4);

            // Claim bond rewards - staker gets another (DAILY_INFLATION / 4) every day
            tr.claim_bond_rewards(&pool_owner2.pubkey(), &staker)
                .await
                .unwrap();
            let staker_stats = tr.staker_stats(staker.pubkey()).await.unwrap();
            assert_eq!(staker_stats.balance, 2 * (i + 1) * DAILY_INFLATION / 4);
        }

        // crank 10 more times without claim
        for _ in 0..10 {
            tr.sleep(DAY).await.unwrap();
            tr.crank_pool(&pool_owner.pubkey()).await.unwrap();
            tr.crank_pool(&pool_owner2.pubkey()).await.unwrap();
        }

        // Claim pool 1 rewards - owner gets another 10 * (DAILY_INFLATION / 4)
        tr.claim_pool_rewards(&pool_owner).await.unwrap();
        let pool_stats = tr.pool_stats(pool_owner.pubkey()).await.unwrap();
        assert_eq!(
            pool_stats.balance,
            (STAKE_BUFFER_LEN + 5) * DAILY_INFLATION / 4
        );

        // Claim staker rewards - staker gets another 10 * (DAILY_INFLATION / 4)
        tr.claim_staker_rewards(&pool_owner.pubkey(), &staker)
            .await
            .unwrap();
        let staker_stats = tr.staker_stats(staker.pubkey()).await.unwrap();
        assert_eq!(
            staker_stats.balance,
            (STAKE_BUFFER_LEN - 5) * DAILY_INFLATION / 2 + 10 * DAILY_INFLATION / 4
        );

        // Claim pool 2 rewards - owner gets another 10 * (DAILY_INFLATION / 4)
        tr.claim_pool_rewards(&pool_owner2).await.unwrap();
        let pool_stats = tr.pool_stats(pool_owner2.pubkey()).await.unwrap();
        assert_eq!(
            pool_stats.balance,
            (STAKE_BUFFER_LEN + 5) * DAILY_INFLATION / 4
        );

        // Claim bond rewards - staker gets another 10 * (DAILY_INFLATION / 4)
        tr.claim_bond_rewards(&pool_owner2.pubkey(), &staker)
            .await
            .unwrap();
        let staker_stats = tr.staker_stats(staker.pubkey()).await.unwrap();
        assert_eq!(
            staker_stats.balance,
            (STAKE_BUFFER_LEN + 5) * DAILY_INFLATION / 2
        );

        // create pool 3
        let pool_owner3 = tr.create_user_with_ata().await.unwrap();
        tr.create_pool(&pool_owner3, 1_000_000_000)
            .await
            .unwrap();
        tr.activate_stake_pool(&pool_owner3.pubkey()).await.unwrap();
        let pool_stats = tr.pool_stats(pool_owner3.pubkey()).await.unwrap();
        assert_eq!(pool_stats.balance, 0);
        assert_eq!(pool_stats.header.last_claimed_offset, STAKE_BUFFER_LEN + 5);
        tr.create_stake_account(&pool_owner3.pubkey(), &staker.pubkey())
            .await
            .unwrap();

        // mint to staker
        tr.mint(&staker.pubkey(), 5_100_000_000).await.unwrap();

        // stake to pool 3
        tr.stake(&pool_owner3.pubkey(), &staker, 5_000_000_000)
            .await
            .unwrap();

        // wait and crank
        tr.sleep(DAY).await.unwrap();
        tr.crank_pool(&pool_owner3.pubkey()).await.unwrap();

        // claim pool 3 rewards
        tr.claim_pool_rewards(&pool_owner3).await.unwrap();
        let pool_stats = tr.pool_stats(pool_owner3.pubkey()).await.unwrap();
        assert_eq!(pool_stats.balance, DAILY_INFLATION / 6 + 1); // +1 to mitigate a rounding mistake
        assert_eq!(
            pool_stats.header.current_day_idx as u64,
            STAKE_BUFFER_LEN + 5 + 1
        );
    }
}