human-program 0.1.0

main human program
Documentation
use std::str::FromStr;

use borsh::BorshDeserialize;
use solana_program::{program_pack::Pack, pubkey::Pubkey, rent, system_instruction};

use human_contract::{
    consts::*,
    contract_vault, contract_wallet, create_buy_instruction, create_drop_instruction,
    create_init_instruction, create_wallets, process_instruction,
    state::{ContractState, STATE_ACC_SIZE},
    InitInstruction,
};
use solana_sdk::signature::Keypair;
use spl_associated_token_account::{
    get_associated_token_address, instruction::create_associated_token_account,
};
use spl_token::instruction::AuthorityType;

use {
    solana_program_test::*,
    solana_sdk::{signature::Signer, transaction::Transaction},
};

const SOL: u64 = 1e9 as u64;

#[tokio::test]
async fn test() {
    test_transaction().await;
}

async fn test_transaction() {
    let program_id = Pubkey::from_str("22222222222222222222222222222222222222222222").unwrap();

    let (mut banks_client, fee_payer, recent_blockhash) = ProgramTest::new(
        "human_contract",
        program_id,
        processor!(process_instruction),
    )
    .start()
    .await;

    // -=-=-=-=-=-=- create token
    let rent = rent::Rent::default();

    let token_mint = Keypair::new();
    let owner = Keypair::new();
    let treasury = Keypair::new();

    let mint_size = spl_token::state::Mint::LEN;

    let create_mint = solana_program::system_instruction::create_account(
        &fee_payer.pubkey(),
        &token_mint.pubkey(),
        rent.minimum_balance(mint_size),
        mint_size as u64,
        &spl_token::ID,
    );

    let initialize_mint = spl_token::instruction::initialize_mint(
        &spl_token::ID,
        &token_mint.pubkey(),
        &fee_payer.pubkey(),
        None,
        4,
    )
    .unwrap();

    let token_mint_addr = token_mint.pubkey();

    let create_wallets = create_wallets(&program_id, &token_mint_addr, &fee_payer.pubkey());

    const EMITTED: u64 = SOL * 100;

    // mint some tokens to contract wallet
    let (wallet, _) = contract_wallet!(&program_id, &token_mint_addr);
    let mint_inst = spl_token::instruction::mint_to(
        &spl_token::ID,
        &token_mint.pubkey(),
        &wallet,
        &fee_payer.pubkey(),
        &[],
        EMITTED,
    )
    .unwrap();

    //
    let (vault, _) = contract_vault!(&program_id, &token_mint_addr);
    let mint_vault_inst = spl_token::instruction::mint_to(
        &spl_token::ID,
        &token_mint.pubkey(),
        &vault,
        &fee_payer.pubkey(),
        &[],
        EMITTED,
    )
    .unwrap();

    let transfer_ownership = spl_token::instruction::set_authority(
        &spl_token::ID,
        &token_mint.pubkey(),
        None,
        AuthorityType::MintTokens,
        &fee_payer.pubkey(),
        &[],
    )
    .unwrap();

    let mut transaction = Transaction::new_with_payer(
        &[
            create_mint,
            initialize_mint,
            create_wallets,
            mint_inst,
            mint_vault_inst,
            transfer_ownership,
        ],
        Some(&fee_payer.pubkey()),
    );

    transaction.sign(&[&fee_payer, &token_mint], recent_blockhash);

    banks_client.process_transaction(transaction).await.unwrap();

    // -=-=-=-=-=-=-=- init state

    let commission = Keypair::new();

    let create_assoc = create_associated_token_account(
        &fee_payer.pubkey(),
        &commission.pubkey(),
        &token_mint.pubkey(),
        &spl_token::ID,
    );

    let (state_addr, _) =
        Pubkey::find_program_address(&[V1, STATE_SEED, token_mint.pubkey().as_ref()], &program_id);

    let init_instruction = create_init_instruction(
        &program_id,
        &state_addr,
        &token_mint.pubkey(),
        &fee_payer.pubkey(),
        InitInstruction {
            admin: fee_payer.pubkey(),
            owner: owner.pubkey(),
            commission: commission.pubkey(),
            treasury: treasury.pubkey(),
            swap_state: Pubkey::new_unique(),
        },
    )
    .unwrap();

    // ------ crate drop
    const BOUGHT: u64 = 10 * SOL;
    const PRICE: u64 = 1;
    const START_DATE: i64 = 1;
    const END_DATE: i64 = i64::MAX;

    let mut transaction =
        Transaction::new_with_payer(&[create_assoc, init_instruction], Some(&fee_payer.pubkey()));

    transaction.sign(&[&fee_payer], recent_blockhash);
    banks_client.process_transaction(transaction).await.unwrap();

    let create_drop = create_drop_instruction(
        &program_id,
        &token_mint.pubkey(),
        &owner.pubkey(),
        1,
        BOUGHT,
        PRICE,
        START_DATE,
        END_DATE,
    )
    .unwrap();

    let mut transaction = Transaction::new_with_payer(&[create_drop], Some(&fee_payer.pubkey()));

    transaction.sign(&[&fee_payer, &owner], recent_blockhash);
    banks_client.process_transaction(transaction).await.unwrap();

    // buy
    let buyer = Keypair::new();

    let transfer = system_instruction::transfer(&fee_payer.pubkey(), &buyer.pubkey(), SOL * 10000);

    let buyer_assoc = get_associated_token_address(&buyer.pubkey(), &token_mint.pubkey());

    let create_assoc = create_associated_token_account(
        &buyer.pubkey(),
        &buyer.pubkey(),
        &token_mint.pubkey(),
        &spl_token::ID,
    );

    let buy_instruction = create_buy_instruction(
        &program_id,
        &state_addr,
        &token_mint.pubkey(),
        &buyer.pubkey(),
        &buyer_assoc,
        &owner.pubkey(),
        &treasury.pubkey(),
        BOUGHT,
        PRICE,
    )
    .unwrap();

    let mut transaction = Transaction::new_with_payer(
        &[transfer, create_assoc, buy_instruction],
        Some(&fee_payer.pubkey()),
    );

    transaction.sign(&[&fee_payer, &buyer], recent_blockhash);

    banks_client.process_transaction(transaction).await.unwrap();

    // check tokens
    let contract_wallet_balance = get_token_balance(&mut banks_client, &wallet).await;
    assert_eq!(contract_wallet_balance, EMITTED - BOUGHT);

    let dest_wallet_balance = get_token_balance(&mut banks_client, &buyer_assoc).await;
    assert_eq!(dest_wallet_balance, BOUGHT);

    let state_balance = banks_client.get_balance(state_addr).await.unwrap();
    assert_eq!(
        state_balance,
        SOL.checked_add(rent.minimum_balance(STATE_ACC_SIZE))
            .unwrap()
    );

    // sols
    let owner_balance = banks_client.get_balance(owner.pubkey()).await.unwrap();
    assert_eq!(owner_balance, SOL * 1);

    let treasury_balance = banks_client.get_balance(treasury.pubkey()).await.unwrap();
    assert_eq!(treasury_balance, SOL * 8);

    let state_acc = banks_client.get_account(state_addr).await.unwrap().unwrap();
    let state = ContractState::deserialize(&mut &state_acc.data[1..]).unwrap();

    dbg!(&state);

    assert_eq!(state.owner, owner.pubkey());
    assert_eq!(state.commission_addr, commission.pubkey());
    assert_eq!(state.sold, BOUGHT);

    let drop = state.drop.unwrap();
    assert_eq!(drop.price, 1);
    assert_ne!(state.vest.deployed_at, 0);
}

async fn get_token_balance(c: &mut BanksClient, addr: &Pubkey) -> u64 {
    c.get_packed_account_data::<spl_token::state::Account>(*addr)
        .await
        .unwrap()
        .amount
}