acta-maker-sdk 0.4.3

Rust SDK for Acta options market makers — WebSocket client, order signing, message types
Documentation
use super::*;

fn token_account(owner: Pubkey, amount: u64) -> Account {
    let mut data = vec![0; TOKEN_ACCOUNT_BASE_LEN];
    data[TOKEN_ACCOUNT_MINT_OFFSET..TOKEN_ACCOUNT_MINT_OFFSET + 32]
        .copy_from_slice(NATIVE_MINT.as_ref());
    data[TOKEN_ACCOUNT_OWNER_OFFSET..TOKEN_ACCOUNT_OWNER_OFFSET + 32]
        .copy_from_slice(owner.as_ref());
    data[TOKEN_ACCOUNT_AMOUNT_OFFSET..TOKEN_ACCOUNT_AMOUNT_OFFSET + 8]
        .copy_from_slice(&amount.to_le_bytes());
    Account {
        lamports: 0,
        data,
        owner: TOKEN_PROGRAM_ID,
        executable: false,
        rent_epoch: 0,
    }
}

#[test]
fn wrap_decision_accounts_for_existing_wsol_and_reserve() {
    assert_eq!(
        decide_wrap(
            700,
            1_000,
            DEFAULT_SOL_RESERVE_LAMPORTS + 300,
            DEFAULT_SOL_RESERVE_LAMPORTS,
            0,
        ),
        WrapDecision::Wrap { wrap_lamports: 300 }
    );
}

#[test]
fn wrap_decision_rejects_balance_below_reserve_boundary() {
    let needed = DEFAULT_SOL_RESERVE_LAMPORTS + 1_000;
    assert_eq!(
        decide_wrap(0, 1_000, needed - 1, DEFAULT_SOL_RESERVE_LAMPORTS, 0),
        WrapDecision::InsufficientNativeSol {
            needed_lamports: needed,
            available_lamports: needed - 1,
        }
    );
    assert_eq!(
        decide_wrap(0, 1_000, needed, DEFAULT_SOL_RESERVE_LAMPORTS, 0),
        WrapDecision::Wrap {
            wrap_lamports: 1_000
        }
    );
}

#[test]
fn native_budget_includes_wrap_rent_fee_and_reserve() {
    let budget = NativeSolBudget {
        owner: Pubkey::new_unique(),
        wrap_lamports: 1_000,
        ata_rent_lamports: 2_039_280,
        reserve_lamports: 1_200_000,
    };
    let needed = 1_000 + 2_039_280 + 5_000 + 1_200_000;

    assert!(budget.validate(needed, 5_000).is_ok());
    assert!(matches!(
        budget.validate(needed - 1, 5_000),
        Err(ChainError::InsufficientNativeSol {
            needed_lamports,
            available_lamports,
        }) if needed_lamports == needed && available_lamports == needed - 1
    ));
}

#[test]
fn native_budget_overflow_fails_closed() {
    let budget = NativeSolBudget {
        owner: Pubkey::new_unique(),
        wrap_lamports: u64::MAX,
        ata_rent_lamports: 1,
        reserve_lamports: 0,
    };

    assert!(matches!(
        budget.validate(u64::MAX, 0),
        Err(ChainError::NativeSolBudgetOverflow)
    ));
}

#[test]
fn existing_wsol_account_is_validated_before_its_balance_is_used() {
    let owner = Pubkey::new_unique();
    assert_eq!(
        parse_wsol_balance(&token_account(owner, 42), &owner, &TOKEN_PROGRAM_ID).unwrap(),
        42
    );

    let other_owner = Pubkey::new_unique();
    assert!(matches!(
        parse_wsol_balance(&token_account(owner, 42), &other_owner, &TOKEN_PROGRAM_ID),
        Err(ChainError::InvalidAccountData)
    ));
}

#[test]
fn wrap_instruction_order_is_transfer_then_sync_native() {
    let owner = Pubkey::new_unique();
    let ata = Pubkey::new_unique();
    let transfer = build_system_transfer_ix(&owner, &ata, 9);
    let sync = build_sync_native_ix(&TOKEN_PROGRAM_ID, &ata);
    assert_eq!(transfer.program_id, SYSTEM_PROGRAM_ID);
    assert_eq!(
        &transfer.data[..4],
        &SYSTEM_INSTRUCTION_TRANSFER.to_le_bytes()
    );
    assert_eq!(sync.data, vec![SPL_TOKEN_SYNC_NATIVE]);
}