Skip to main content

clockwork_crank/instructions/
queue_crank.rs

1use {
2    crate::{errors::*, state::*},
3    anchor_lang::{prelude::*, system_program},
4    chrono::{DateTime, NaiveDateTime, Utc},
5    clockwork_cron::Schedule,
6    clockwork_pool::state::Pool,
7    std::{mem::size_of, str::FromStr},
8};
9
10#[derive(Accounts)]
11pub struct QueueCrank<'info> {
12    #[account(seeds = [SEED_CONFIG], bump)]
13    pub config: Box<Account<'info, Config>>,
14
15    #[account(
16        init_if_needed,
17        seeds = [
18            SEED_FEE,
19            worker.key().as_ref()
20        ],
21        bump,
22        payer = worker,
23        space = 8 + size_of::<Fee>(),
24    )]
25    pub fee: Box<Account<'info, Fee>>,
26
27    #[account(address = config.worker_pool)]
28    pub pool: Box<Account<'info, Pool>>,
29
30    #[account(
31        mut,
32        seeds = [
33            SEED_QUEUE, 
34            queue.authority.as_ref(),
35            queue.id.as_bytes(),
36        ],
37        bump,
38        constraint = !queue.is_paused @ ClockworkError::PausedQueue
39    )]
40    pub queue: Box<Account<'info, Queue>>,
41
42    #[account(address = system_program::ID)]
43    pub system_program: Program<'info, System>,
44
45    #[account(mut)]
46    pub worker: Signer<'info>,
47}
48
49pub fn handler(ctx: Context<QueueCrank>) -> Result<()> {
50    // Get accounts
51    let config = &ctx.accounts.config;
52    let fee = &mut ctx.accounts.fee;
53    let pool = &ctx.accounts.pool;
54    let queue = &mut ctx.accounts.queue;
55    let worker = &ctx.accounts.worker;
56
57    // If this queue does not have a next_instruction, verify the queue's trigger has been met and a new exec_context can be created.
58    if queue.next_instruction.is_none() {
59        match queue.trigger.clone() {
60            Trigger::Cron { schedule } => {
61                // Get the reference timestamp for calculating the queue's scheduled target timestamp.
62                let reference_timestamp = match queue.exec_context.clone() {
63                    None => queue.created_at.unix_timestamp,
64                    Some(exec_context) => {
65                        match exec_context {
66                            ExecContext::Cron { started_at } => started_at,
67                            _ => return Err(ClockworkError::InvalidQueueState.into())
68                        }
69                    }
70                };
71
72                // Verify the current time is greater than or equal to the target timestamp.
73                let target_timestamp = next_timestamp(reference_timestamp, schedule).ok_or(ClockworkError::InvalidTrigger)?;
74                let current_timestamp = Clock::get().unwrap().unix_timestamp;
75                require!(current_timestamp >= target_timestamp, ClockworkError::InvalidTrigger);
76
77                // Set the exec context.
78                queue.exec_context = Some(ExecContext::Cron { started_at: target_timestamp });
79            },
80            Trigger::Immediate => {
81                // Set the exec context.
82                require!(queue.exec_context.is_none(), ClockworkError::InvalidQueueState);
83                queue.exec_context = Some(ExecContext::Immediate);
84            },
85        }
86    }
87
88    // Crank the queue
89    let bump = ctx.bumps.get("queue").unwrap();
90    queue.crank(ctx.remaining_accounts, *bump, worker)?;
91    
92    // If worker is in the pool, pay automation fees.
93    let is_authorized_worker = pool.clone().into_inner().workers.contains(&worker.key());
94    if is_authorized_worker {
95        fee.pay_to_worker(config.crank_fee, queue)?;
96    } else {
97        fee.pay_to_admin(config.crank_fee, queue)?;
98    }
99
100    Ok(())
101}
102
103
104fn next_timestamp(after: i64, schedule: String) -> Option<i64> {
105    Schedule::from_str(&schedule)
106        .unwrap()
107        .after(&DateTime::<Utc>::from_utc(
108            NaiveDateTime::from_timestamp(after, 0),
109            Utc,
110        ))
111        .take(1)
112        .next()
113        .map(|datetime| datetime.timestamp())
114}
115