Skip to main content

antegen_thread_program/instructions/
thread_update.rs

1use crate::{state::*, utils::next_timestamp, *};
2use anchor_lang::prelude::*;
3
4/// Parameters for updating a thread
5#[derive(AnchorSerialize, AnchorDeserialize, Default)]
6pub struct ThreadUpdateParams {
7    /// Explicitly set the paused state (not a toggle)
8    pub paused: Option<bool>,
9    /// Update the thread's trigger
10    pub trigger: Option<Trigger>,
11}
12
13/// Accounts required by the `thread_update` instruction.
14#[derive(Accounts)]
15pub struct ThreadUpdate<'info> {
16    /// The authority (owner) of the thread.
17    #[account(mut)]
18    pub authority: Signer<'info>,
19
20    /// The thread to be updated.
21    #[account(
22        mut,
23        constraint = authority.key().eq(&thread.authority),
24        seeds = [
25            SEED_THREAD,
26            thread.authority.as_ref(),
27            thread.id.as_slice(),
28        ],
29        bump = thread.bump,
30    )]
31    pub thread: Account<'info, Thread>,
32}
33
34pub fn thread_update(ctx: Context<ThreadUpdate>, params: ThreadUpdateParams) -> Result<()> {
35    let thread = &mut ctx.accounts.thread;
36
37    // Update paused state if provided (explicit, not toggle)
38    if let Some(paused) = params.paused {
39        thread.paused = paused;
40    }
41
42    // Update the trigger if provided
43    if let Some(ref trigger) = params.trigger {
44        let clock = Clock::get()?;
45        let current_timestamp = clock.unix_timestamp;
46        let thread_pubkey = thread.key();
47
48        thread.trigger = trigger.clone();
49
50        // Initialize schedule based on trigger type (mirrors thread_create logic)
51        thread.schedule = match &trigger {
52            Trigger::Account { .. } => Schedule::OnChange { prev: 0 },
53            Trigger::Cron {
54                schedule, jitter, ..
55            } => {
56                let base_next = next_timestamp(current_timestamp, schedule.clone())
57                    .unwrap_or(current_timestamp);
58                let jitter_offset = crate::utils::calculate_jitter_offset(
59                    current_timestamp,
60                    &thread_pubkey,
61                    *jitter,
62                );
63                Schedule::Timed {
64                    prev: current_timestamp,
65                    next: base_next.saturating_add(jitter_offset),
66                }
67            }
68            Trigger::Immediate { .. } => Schedule::Timed {
69                prev: current_timestamp,
70                next: current_timestamp,
71            },
72            Trigger::Slot { slot } => Schedule::Block {
73                prev: clock.slot,
74                next: *slot,
75            },
76            Trigger::Epoch { epoch } => Schedule::Block {
77                prev: clock.epoch,
78                next: *epoch,
79            },
80            Trigger::Interval {
81                seconds, jitter, ..
82            } => {
83                let base_next = current_timestamp.saturating_add(*seconds);
84                let jitter_offset = crate::utils::calculate_jitter_offset(
85                    current_timestamp,
86                    &thread_pubkey,
87                    *jitter,
88                );
89                Schedule::Timed {
90                    prev: current_timestamp,
91                    next: base_next.saturating_add(jitter_offset),
92                }
93            }
94            Trigger::Timestamp { unix_ts, .. } => Schedule::Timed {
95                prev: current_timestamp,
96                next: *unix_ts,
97            },
98        };
99    }
100
101    // If trigger was updated but paused was NOT explicitly set, auto-unpause.
102    // Changing the trigger implies the user wants the thread running.
103    if params.trigger.is_some() && params.paused.is_none() {
104        thread.paused = false;
105    }
106
107    Ok(())
108}