triggr/instructions/
exec_task.rs1
2pub use anchor_lang::prelude::*;
3pub use anchor_lang::{
4 solana_program::{instruction::Instruction, program::invoke, program::invoke_signed, pubkey},
5 system_program::{transfer, Transfer},
6 solana_program::clock::Clock,
7 InstructionData,
8
9};
10pub use state::*;
11
12use crate::errors::TriggrError;
13use crate::state;
14
15#[derive(Accounts)]
16#[instruction(traverse: Vec<u8>, _trigger_count: u64, _task_count: u8)]
17pub struct ExecTask<'info> {
18 #[account(mut)]
19 initiator: Signer<'info>,
20
21 program_state: Account<'info, ProgramState>,
22
23 #[account()]
25 authority: UncheckedAccount<'info>,
26
27 #[account(mut, seeds = ["user".as_bytes(), &authority.key().to_bytes()[..]], bump)]
28 user: Account<'info, User>,
30
31 #[account(mut, seeds = ["payer".as_bytes(), &authority.key().to_bytes()[..]], bump, constraint = payer.owner == &system_program.key())]
33 payer: UncheckedAccount<'info>,
34
35 #[account(
36 mut,
37 seeds = ["trigger".as_bytes(), &authority.key().to_bytes()[..], &_trigger_count.to_le_bytes()[..]],
38 bump,
39 constraint = trigger.status == Status::Active @ TriggrError::TriggerNotActive
40 )]
41 trigger: Box<Account<'info, Trigger>>,
42
43 #[account(mut, seeds = ["task".as_bytes(), &trigger.key().to_bytes()[..], &get_task_index(traverse, &trigger.conditions).to_le_bytes()[..]], bump)]
45 task: Account<'info, Task>,
46
47 system_program: Program<'info, System>,
48}
49
50pub fn get_task_index(traverse: Vec<u8>, conditions: &AdjacencyTree) -> u8 {
51 if traverse.len() == 0 {
52 0
53 } else {
54 conditions.nodes[*traverse.last().unwrap() as usize]
55 .task_index
56 .unwrap()
57 }
58}
59
60pub fn handler(
61 ctx: Context<ExecTask>,
62 traverse: Vec<u8>,
63 _trigger_count: u64,
64 _task_count: u8,
65) -> Result<()> {
66
67
68 let payer_seeds = &[
69 "payer".as_bytes(),
70 &ctx.accounts.task.authority.as_ref()[..],
71 &[ctx.bumps["payer"]],
72 ];
73
74 let payer = &[&payer_seeds[..]];
75
76 let mut remaining_accounts = ctx.remaining_accounts.to_vec();
77
78 if traverse.len() > 0 {
79 for (index, parent_node_index) in traverse.iter().enumerate() {
81 if index == 0 {
83 assert_eq!(*parent_node_index, 0);
84 }
85
86 ctx.accounts
88 .trigger
89 .conditions
90 .edges
91 .iter()
92 .find(|edge| {
93 traverse.get(index + 1).map_or(true, |next_value| {
94 **edge == [*parent_node_index, *next_value]
95 })
96 })
97 .unwrap_or_else(|| {
98 panic!("Edge with ({}, {}) not found", parent_node_index, index + 1)
99 });
100 }
101
102 for condition_node_index in traverse.iter() {
104 let node = &ctx.accounts.trigger.conditions.nodes[*condition_node_index as usize];
105
106 let program_account = remaining_accounts.remove(0);
107
108 let middleware_program_id = ctx.accounts.program_state.middleware[node.condition.condition_type as usize].program_id;
109
110 assert_eq!(middleware_program_id, program_account.key(), "Unexpected middleware program ID found.");
111
112 let middleware = &ctx.accounts.program_state.middleware[node.condition.condition_type as usize];
113
114 let accounts: Vec<AccountInfo> = remaining_accounts
117 .drain(0..middleware.accounts_count as usize)
118 .collect();
119
120 let account_metas: Vec<AccountMeta> = accounts
121 .into_iter()
122 .map(|account_info| AccountMeta {
123 pubkey: *account_info.key,
124 is_signer: account_info.is_signer,
125 is_writable: account_info.is_writable,
126 })
127 .collect();
128
129 let instruction = Instruction {
130 program_id: program_account.key(),
131 accounts: account_metas,
132 data: node.condition.condition_data.clone(),
133 };
134
135 invoke(&instruction, &ctx.remaining_accounts).unwrap();
136 }
137 }
138 Trigger::evaluate_time(&mut *ctx.accounts.trigger)?;
140
141 for bundle in &ctx.accounts.task.bundles {
143
144 if let Some(expiration_slot) = bundle.expiration_slot {
146 let current_slot = Clock::get()?.slot;
147 if current_slot > expiration_slot {
148 return Err(TriggrError::ExpirationSlotPassed.into());
149 }
150 }
151
152 assert_eq!(bundle.ready, true, "Bundle is not ready");
153
154 bundle.instructions.iter().for_each(|instruction| {
155 msg!("Executing instruction: {:?}", instruction.program_id);
156
157 invoke_signed(
158 &Instruction::from(instruction), ctx.remaining_accounts, payer,
161 )
162 .unwrap();
163 });
164 };
165
166 ctx.accounts.trigger.usage_stats.execution_count += 1;
168 ctx.accounts.trigger.usage_stats.last_executed_at = Clock::get()?.unix_timestamp;
169
170 ctx.accounts.task.usage_stats.execution_count += 1;
172 ctx.accounts.task.usage_stats.last_executed_at = Clock::get()?.unix_timestamp;
173
174 if ctx.accounts.trigger.status != Status::Active {
175 let active_triggers = &mut ctx.accounts.user.active_triggers;
176
177 active_triggers.retain(|&x| x != ctx.accounts.trigger.key());
178
179 ctx.accounts.user.active_triggers = active_triggers.clone();
180
181 }
183 Ok(())
186}