triggr-program 0.1.1

Created with Anchor
Documentation
use crate::errors::TriggrError;
use crate::state::*;
use anchor_lang::prelude::*;
use anchor_lang::solana_program::pubkey;
use anchor_lang::system_program::{transfer, Transfer};

#[derive(Accounts)]
pub struct PopulateInstruction<'info> {
    // should be owned by program within whitelist
    #[account()]
    signer: Signer<'info>,

    /// CHECK: account is owned by system prorgam
    #[account(mut, seeds = ["payer".as_bytes(), &task.authority.key().to_bytes()[..]], bump)]
    payer: UncheckedAccount<'info>,

    #[account(mut, seeds = ["task".as_bytes(), &task.parent_trigger.key().to_bytes()[..], &task.own_index.to_le_bytes()[..]], bump)]
    task: Box<Account<'info, Task>>,

    /// CHECK: account is owned by annother program
    #[account(owner = pubkey!("2rYeU7ibwWSVFrw29hRtRpnDvBSgUrz8HBQYYnzFKpkt"))]
    instruction_account: UncheckedAccount<'info>,

    system_program: Program<'info, System>,
}

pub fn handler(ctx: Context<PopulateInstruction>, ix_index: u8) -> Result<()> {
    let payer_seeds = &[
        "payer".as_bytes(),
        &ctx.accounts.task.authority.to_bytes()[..],
        &[ctx.bumps["payer"]],
    ];

    let account = ctx.accounts.instruction_account.try_borrow_mut_data()?;
    let storage_account = TempStore::try_deserialize(&mut account.as_ref()).unwrap();

    let buffer = storage_account.construct_complete_buffer();
    let content = TempInstructions::try_deserialize(&mut &*buffer.as_slice())?;

    let formated_payer_seeds = &[&payer_seeds[..]];

    let task_seeds = &[
        "task".as_bytes(),
        &ctx.accounts.task.parent_trigger.to_bytes()[..],
        &[ctx.accounts.task.own_index],
        &[ctx.bumps["task"]],
    ];

    let formated_task_seeds = &[&task_seeds[..]];

    let mut next_task = *ctx.accounts.task.clone();

    next_task.bundles[ix_index as usize].instructions = content.bundles.clone();

    let new_len = next_task.try_to_vec()?.len() + 50;

    let task_acc_info = ctx.accounts.task.to_account_info();

    let rent = Rent::get()?;
    let new_minimum_balance = rent.minimum_balance(new_len);
    let current_balance = task_acc_info.lamports();
    let diff = new_minimum_balance.checked_sub(current_balance).unwrap();

    if diff > 0 {
        let cpi_context = CpiContext::new_with_signer(
            ctx.accounts.system_program.to_account_info(),
            Transfer {
                from: ctx.accounts.payer.to_account_info(),
                to: ctx.accounts.task.to_account_info(),
            },
            formated_payer_seeds,
        );

        transfer(cpi_context, diff)?;

        task_acc_info.realloc(new_len, true)?;
    } else {
        // todo: test this case
        let cpi_context = CpiContext::new_with_signer(
            ctx.accounts.system_program.to_account_info(),
            Transfer {
                from: ctx.accounts.task.to_account_info(),
                to: ctx.accounts.payer.to_account_info(),
            },
            formated_task_seeds,
        );

        transfer(cpi_context, diff)?;

        task_acc_info.realloc(new_len, true)?;
    }

    // todo: This check should match the ix's programId with the allowed middleware program id
    // right now this is hard coded to the jup programs id
    assert_eq!(
        *ctx.accounts.signer.to_account_info().owner,
        pubkey!("2rYeU7ibwWSVFrw29hRtRpnDvBSgUrz8HBQYYnzFKpkt")
    );

    if ctx.accounts.task.bundles[ix_index as usize].ready {
        return Err(TriggrError::InvalidPopulate.into());
    }

    ctx.accounts.task.bundles[ix_index as usize].instructions = content.bundles.clone();
    ctx.accounts.task.bundles[ix_index as usize].ready = true;

    let current_slot = Clock::get()?.slot;

    msg!("Current slot: {}", current_slot);

    // avg slot is 500ms so 120 slots is 1 minute
    ctx.accounts.task.bundles[ix_index as usize].expiration_slot = Some(current_slot + 120);

    if ctx.accounts.task.bundles.iter().all(|ix| ix.ready) {
        ctx.accounts.task.status = TaskStatus::Complete;

    // todo: create LUT
    } else {
        ctx.accounts.task.status = TaskStatus::Incomplete;
    }

    Ok(())
}