antegen-thread-program 5.2.0

Solana program for Antegen - automation and scheduling threads
Documentation
use crate::{state::*, utils::next_timestamp, *};
use anchor_lang::prelude::*;

/// Parameters for updating a thread
#[derive(AnchorSerialize, AnchorDeserialize, Default)]
pub struct ThreadUpdateParams {
    /// Explicitly set the paused state (not a toggle)
    pub paused: Option<bool>,
    /// Update the thread's trigger
    pub trigger: Option<Trigger>,
}

/// Accounts required by the `thread_update` instruction.
#[derive(Accounts)]
pub struct ThreadUpdate<'info> {
    /// The authority (owner) of the thread.
    #[account(mut)]
    pub authority: Signer<'info>,

    /// The thread to be updated.
    #[account(
        mut,
        constraint = authority.key().eq(&thread.authority),
        seeds = [
            SEED_THREAD,
            thread.authority.as_ref(),
            thread.id.as_slice(),
        ],
        bump = thread.bump,
    )]
    pub thread: Account<'info, Thread>,
}

pub fn thread_update(ctx: Context<ThreadUpdate>, params: ThreadUpdateParams) -> Result<()> {
    let thread = &mut ctx.accounts.thread;

    // Update paused state if provided (explicit, not toggle)
    if let Some(paused) = params.paused {
        thread.paused = paused;
    }

    // Update the trigger if provided
    if let Some(ref trigger) = params.trigger {
        let clock = Clock::get()?;
        let current_timestamp = clock.unix_timestamp;
        let thread_pubkey = thread.key();

        thread.trigger = trigger.clone();

        // Initialize schedule based on trigger type (mirrors thread_create logic)
        thread.schedule = match &trigger {
            Trigger::Account { .. } => Schedule::OnChange { prev: 0 },
            Trigger::Cron {
                schedule, jitter, ..
            } => {
                let base_next = next_timestamp(current_timestamp, schedule.clone())
                    .unwrap_or(current_timestamp);
                let jitter_offset = crate::utils::calculate_jitter_offset(
                    current_timestamp,
                    &thread_pubkey,
                    *jitter,
                );
                Schedule::Timed {
                    prev: current_timestamp,
                    next: base_next.saturating_add(jitter_offset),
                }
            }
            Trigger::Immediate { .. } => Schedule::Timed {
                prev: current_timestamp,
                next: current_timestamp,
            },
            Trigger::Slot { slot } => Schedule::Block {
                prev: clock.slot,
                next: *slot,
            },
            Trigger::Epoch { epoch } => Schedule::Block {
                prev: clock.epoch,
                next: *epoch,
            },
            Trigger::Interval {
                seconds, jitter, ..
            } => {
                let base_next = current_timestamp.saturating_add(*seconds);
                let jitter_offset = crate::utils::calculate_jitter_offset(
                    current_timestamp,
                    &thread_pubkey,
                    *jitter,
                );
                Schedule::Timed {
                    prev: current_timestamp,
                    next: base_next.saturating_add(jitter_offset),
                }
            }
            Trigger::Timestamp { unix_ts, .. } => Schedule::Timed {
                prev: current_timestamp,
                next: *unix_ts,
            },
        };
    }

    // If trigger was updated but paused was NOT explicitly set, auto-unpause.
    // Changing the trigger implies the user wants the thread running.
    if params.trigger.is_some() && params.paused.is_none() {
        thread.paused = false;
    }

    Ok(())
}