mpl-distro 0.1.1

Program for creating token distributions
Documentation
#![cfg(feature = "test-sbf")]
pub mod setup;
use borsh::BorshDeserialize;
use mpl_distro::{accounts::Distribution, instructions::CreateDistributionBuilder, types::Key, ID};
pub use setup::*;
use solana_program::{program_pack::Pack, pubkey::Pubkey, system_instruction};
use solana_program_test::tokio;
use solana_sdk::{
    signature::{Keypair, Signer},
    transaction::Transaction,
};
use spl_token::{
    instruction as token_instruction,
    state::{Account as TokenAccount, Mint},
};

/// Helper function to create a token mint with specified decimals
async fn create_mint(
    context: &mut solana_program_test::ProgramTestContext,
    decimals: u8,
) -> (Keypair, Pubkey) {
    let mint_account = Keypair::new();
    let mint_authority = context.payer.pubkey();

    let rent = context.banks_client.get_rent().await.unwrap();
    let mint_rent = rent.minimum_balance(Mint::LEN);

    let instructions = [
        system_instruction::create_account(
            &context.payer.pubkey(),
            &mint_account.pubkey(),
            mint_rent,
            Mint::LEN as u64,
            &spl_token::id(),
        ),
        token_instruction::initialize_mint(
            &spl_token::id(),
            &mint_account.pubkey(),
            &mint_authority,
            None,
            decimals,
        )
        .unwrap(),
    ];

    let tx = Transaction::new_signed_with_payer(
        &instructions,
        Some(&context.payer.pubkey()),
        &[&context.payer, &mint_account],
        context.last_blockhash,
    );
    context.banks_client.process_transaction(tx).await.unwrap();

    (mint_account, mint_authority)
}

/// Helper function to create a token account
async fn create_token_account(
    context: &mut solana_program_test::ProgramTestContext,
    mint: &Pubkey,
    owner: &Pubkey,
) -> Keypair {
    let token_account = Keypair::new();

    let rent = context.banks_client.get_rent().await.unwrap();
    let token_rent = rent.minimum_balance(TokenAccount::LEN);

    let instructions = [
        system_instruction::create_account(
            &context.payer.pubkey(),
            &token_account.pubkey(),
            token_rent,
            TokenAccount::LEN as u64,
            &spl_token::id(),
        ),
        token_instruction::initialize_account(
            &spl_token::id(),
            &token_account.pubkey(),
            mint,
            owner,
        )
        .unwrap(),
    ];

    let tx = Transaction::new_signed_with_payer(
        &instructions,
        Some(&context.payer.pubkey()),
        &[&context.payer, &token_account],
        context.last_blockhash,
    );
    context.banks_client.process_transaction(tx).await.unwrap();

    token_account
}

#[tokio::test]
async fn test_create_distribution() {
    let mut context = program_test().start_with_context().await;

    // Given a mint
    let (mint_account, _) = create_mint(&mut context, 0).await;
    let mint = mint_account.pubkey();

    // Create a new distribution account keypair
    let seed = Keypair::new();
    let distribution_account = Distribution::find_pda(&mint, &seed.pubkey()).0;

    // Define start and end times
    let now = std::time::SystemTime::now()
        .duration_since(std::time::UNIX_EPOCH)
        .unwrap()
        .as_secs() as i64;
    let start_time = now;
    let end_time = now + 60;

    // Create a merkle root (all zeros in this test)
    let merkle_root = [0u8; 32];
    let total_claimants = 1u64;
    let name = "Test Distribution";
    // Create the distribution with the CreateDistributionBuilder
    let ix = CreateDistributionBuilder::new()
        .distribution(distribution_account)
        .mint(mint)
        .payer(context.payer.pubkey())
        .authority(Some(context.payer.pubkey()))
        .merkle_root(merkle_root)
        .start_time(start_time)
        .end_time(end_time)
        .total_claimants(total_claimants)
        .seed(seed.pubkey())
        .name(name.to_string())
        .instruction();

    let tx = Transaction::new_signed_with_payer(
        &[ix],
        Some(&context.payer.pubkey()),
        &[&context.payer, &seed],
        context.last_blockhash,
    );
    context.banks_client.process_transaction(tx).await.unwrap();

    // Then verify the distribution was created with correct data
    let distribution = context
        .banks_client
        .get_account(distribution_account)
        .await
        .unwrap()
        .unwrap();

    // Deserialize the account data
    let mut account_data = distribution.data.as_ref();
    let distribution = Distribution::deserialize(&mut account_data).unwrap();

    // Assert the distribution has the correct data
    assert_eq!(distribution.key, Key::Distribution);
    assert_eq!(distribution.authority, context.payer.pubkey());
    assert_eq!(distribution.mint, mint);
    assert_eq!(distribution.merkle_root, merkle_root);
    assert_eq!(distribution.start_time, start_time);
    assert_eq!(distribution.end_time, end_time);
    assert_eq!(distribution.total_claimants, total_claimants);
    assert_eq!(distribution.total_amount, 0);
    assert_eq!(distribution.claim_count, 0);
    assert_eq!(distribution.claim_amount, 0);
}