clockwork_crank/instructions/
queue_crank.rs1use {
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 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 queue.next_instruction.is_none() {
59 match queue.trigger.clone() {
60 Trigger::Cron { schedule } => {
61 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 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 queue.exec_context = Some(ExecContext::Cron { started_at: target_timestamp });
79 },
80 Trigger::Immediate => {
81 require!(queue.exec_context.is_none(), ClockworkError::InvalidQueueState);
83 queue.exec_context = Some(ExecContext::Immediate);
84 },
85 }
86 }
87
88 let bump = ctx.bumps.get("queue").unwrap();
90 queue.crank(ctx.remaining_accounts, *bump, worker)?;
91
92 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