miclockwork_thread_program/instructions/
thread_update.rs

1use crate::{errors::ClockworkError, state::*};
2
3use anchor_lang::{
4    prelude::*,
5    solana_program::system_program,
6    system_program::{transfer, Transfer},
7};
8
9/// Accounts required by the `thread_update` instruction.
10#[derive(Accounts)]
11#[instruction(settings: ThreadSettings)]
12pub struct ThreadUpdate<'info> {
13    /// The authority (owner) of the thread.
14    #[account(mut)]
15    pub authority: Signer<'info>,
16
17    /// The Solana system program
18    #[account(address = system_program::ID)]
19    pub system_program: Program<'info, System>,
20
21    /// The thread to be updated.
22    #[account(
23            mut,
24            seeds = [
25                SEED_THREAD,
26                thread.authority.as_ref(),
27                thread.id.as_slice(),
28            ],
29            bump = thread.bump,
30            has_one = authority,
31        )]
32    pub thread: Account<'info, Thread>,
33}
34
35pub fn handler(ctx: Context<ThreadUpdate>, settings: ThreadSettings) -> Result<()> {
36    // Get accounts
37    let authority = &ctx.accounts.authority;
38    let thread = &mut ctx.accounts.thread;
39    let system_program = &ctx.accounts.system_program;
40
41    // Update the thread.
42    if let Some(fee) = settings.fee {
43        thread.fee = fee;
44    }
45
46    // If provided, update the thread's instruction set.
47    if let Some(instructions) = settings.instructions {
48        thread.instructions = instructions;
49    }
50
51    // If provided, update the rate limit.
52    if let Some(rate_limit) = settings.rate_limit {
53        thread.rate_limit = rate_limit;
54    }
55
56    // If provided, update the thread's trigger and reset the exec context.
57    if let Some(trigger) = settings.trigger {
58        // Require the thread is not in the middle of processing.
59        require!(
60            std::mem::discriminant(&thread.trigger) == std::mem::discriminant(&trigger),
61            ClockworkError::InvalidTriggerVariant
62        );
63        thread.trigger = trigger.clone();
64
65        // If the user updates an account trigger, the trigger context is no longer valid.
66        // Here we reset the trigger context to zero to re-prime the trigger.
67        if thread.exec_context.is_some() {
68            thread.exec_context = Some(ExecContext {
69                trigger_context: match trigger {
70                    Trigger::Account {
71                        address: _,
72                        offset: _,
73                        size: _,
74                    } => TriggerContext::Account { data_hash: 0 },
75                    _ => thread.exec_context.unwrap().trigger_context,
76                },
77                ..thread.exec_context.unwrap()
78            });
79        }
80    }
81
82    // Reallocate mem for the thread account
83    thread.realloc()?;
84
85    // If lamports are required to maintain rent-exemption, pay them
86    let data_len = thread.to_account_info().data_len();
87    let minimum_rent = Rent::get().unwrap().minimum_balance(data_len);
88    if minimum_rent > thread.to_account_info().lamports() {
89        transfer(
90            CpiContext::new(
91                system_program.to_account_info(),
92                Transfer {
93                    from: authority.to_account_info(),
94                    to: thread.to_account_info(),
95                },
96            ),
97            minimum_rent
98                .checked_sub(thread.to_account_info().lamports())
99                .unwrap(),
100        )?;
101    }
102
103    Ok(())
104}