pyra-instructions 0.4.2

Instruction builders for the Pyra protocol on Solana
Documentation
use anchor_lang::{InstructionData, ToAccountMetas};
use solana_program::{instruction::Instruction, pubkey::Pubkey};

use crate::constants::{
    INSTRUCTIONS_SYSVAR_ID, KAMINO_FARMS_PROGRAM_ID, KAMINO_LENDING_PROGRAM_ID, SYSTEM_PROGRAM_ID,
};

pub struct DepositKaminoParams {
    pub owner: Pubkey,
    pub payer: Pubkey,
    pub liquidity_mint: Pubkey,
    pub liquidity_token_program: Pubkey,
    pub kamino_market: Pubkey,
    pub kamino_reserve: Pubkey,
    /// Optional: the deposit address SPL token account. Pass `None` if
    /// depositing SOL (no ATA for raw lamports). For all other tokens, this
    /// is mandatory.
    pub deposit_address_spl: Option<Pubkey>,
}

pub fn deposit_kamino(params: &DepositKaminoParams) -> Instruction {
    let vault = pyra_accounts::get_vault(&params.owner);
    let vault_liquidity_ata = pyra_accounts::get_associated_token_address(
        &vault,
        &params.liquidity_mint,
        &params.liquidity_token_program,
    );
    let deposit_address = pyra_accounts::get_deposit_address(&params.owner);
    let kamino_obligation =
        pyra_accounts::get_kamino_obligation(&params.owner, &params.kamino_market);
    let kamino_market_authority =
        pyra_accounts::get_kamino_lending_market_authority(&params.kamino_market);
    let kamino_reserve_liquidity_supply =
        pyra_accounts::get_kamino_reserve_liquidity_supply(&params.kamino_reserve);
    let kamino_reserve_collateral_mint =
        pyra_accounts::get_kamino_reserve_collateral_mint(&params.kamino_reserve);
    let kamino_reserve_collateral_supply =
        pyra_accounts::get_kamino_reserve_collateral_supply(&params.kamino_reserve);

    let accounts = crate::pyra_program::client::accounts::DepositKamino {
        vault,
        vault_liquidity_ata,
        deposit_address,
        deposit_address_spl: params.deposit_address_spl,
        liquidity_mint: params.liquidity_mint,
        payer: params.payer,
        liquidity_token_program: params.liquidity_token_program,
        associated_token_program: pyra_accounts::ASSOCIATED_TOKEN_PROGRAM_ID,
        kamino_program: KAMINO_LENDING_PROGRAM_ID,
        kamino_obligation,
        kamino_market: params.kamino_market,
        kamino_market_authority,
        kamino_reserve: params.kamino_reserve,
        kamino_reserve_liquidity_supply,
        kamino_reserve_collateral_mint,
        kamino_reserve_collateral_supply,
        // All Kamino collateral tokens use spl-token (not Token-2022)
        collateral_token_program: pyra_tokens::TOKEN_PROGRAM_ID,
        instructions_sysvar_account: INSTRUCTIONS_SYSVAR_ID,
        kamino_farms_program: KAMINO_FARMS_PROGRAM_ID,
        system_program: SYSTEM_PROGRAM_ID,
    }
    .to_account_metas(None);

    let data = crate::pyra_program::client::args::DepositKamino {}.data();

    Instruction {
        program_id: pyra_accounts::PYRA_PROGRAM_ID,
        accounts,
        data,
    }
}

#[cfg(test)]
#[allow(clippy::allow_attributes, clippy::allow_attributes_without_reason, clippy::unwrap_used, reason = "test code")]
mod tests {
    use super::*;
    use solana_pubkey::pubkey;

    #[test]
    fn test_deposit_kamino_instruction() {
        let owner = pubkey!("d4A2prbA2whesmvHaL88BH6Ewn5N4bTSjm4GiKy2eSi");
        let payer = pubkey!("7u3HeHxYDLhnCoErrtycNokbQYbWGzLs6JSDqGAv5PfF");
        let market = pubkey!("Gnt27xtC473ZT2Mw5u8wZ68Z3gULkSTb5DuxJy7eJotD");
        let reserve = pubkey!("AdtRGGhmqvom3Fk5H2LTnGMfcXmUQdhSz8aPJGfhFqHj");
        let mint = pubkey!("DUcTi3rDyS5QEmZ4BNRBejtArmDCWaPYGfN44vBJXKL5");
        let token_program = pubkey!("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");

        let ix = deposit_kamino(&DepositKaminoParams {
            owner,
            payer,
            liquidity_mint: mint,
            liquidity_token_program: token_program,
            kamino_market: market,
            kamino_reserve: reserve,
            deposit_address_spl: None,
        });

        assert_eq!(ix.program_id, pyra_accounts::PYRA_PROGRAM_ID);
        // 20 accounts (deposit_address_spl is optional, adds 1 regardless)
        assert_eq!(ix.accounts.len(), 20);
        // vault is first and writable
        assert_eq!(ix.accounts[0].pubkey, pyra_accounts::get_vault(&owner));
        assert!(ix.accounts[0].is_writable);
        // data should not be empty
        assert!(!ix.data.is_empty());
    }

    #[test]
    fn test_deposit_kamino_with_deposit_address_spl() {
        let owner = pubkey!("d4A2prbA2whesmvHaL88BH6Ewn5N4bTSjm4GiKy2eSi");
        let payer = pubkey!("7u3HeHxYDLhnCoErrtycNokbQYbWGzLs6JSDqGAv5PfF");
        let market = pubkey!("Gnt27xtC473ZT2Mw5u8wZ68Z3gULkSTb5DuxJy7eJotD");
        let reserve = pubkey!("AdtRGGhmqvom3Fk5H2LTnGMfcXmUQdhSz8aPJGfhFqHj");
        let mint = pubkey!("DUcTi3rDyS5QEmZ4BNRBejtArmDCWaPYGfN44vBJXKL5");
        let token_program = pubkey!("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");
        let deposit_spl = pubkey!("3NJYftD5sjVfxFkKFgU7bbtBBhBsBYAg1CEhJkJ4iY3H");

        let ix = deposit_kamino(&DepositKaminoParams {
            owner,
            payer,
            liquidity_mint: mint,
            liquidity_token_program: token_program,
            kamino_market: market,
            kamino_reserve: reserve,
            deposit_address_spl: Some(deposit_spl),
        });

        assert_eq!(ix.program_id, pyra_accounts::PYRA_PROGRAM_ID);
        assert!(!ix.data.is_empty());
    }
}