1use std::collections::HashMap;
2
3use anchor_lang::{prelude::*, solana_program::instruction::Instruction};
4
5pub mod write_return_tasks;
6
7pub use write_return_tasks::write_return_tasks;
8
9declare_program!(tuktuk);
10declare_program!(cron);
11
12#[derive(AnchorSerialize, AnchorDeserialize, Clone, Default)]
13pub struct RunTaskReturnV0 {
14 pub tasks: Vec<TaskReturnV0>,
15 pub accounts: Vec<Pubkey>,
16}
17
18#[derive(AnchorSerialize, AnchorDeserialize, Clone)]
19pub struct TaskReturnV0 {
20 pub trigger: TriggerV0,
21 pub transaction: TransactionSourceV0,
24 pub crank_reward: Option<u64>,
25 pub free_tasks: u8,
28 pub description: String,
30}
31
32impl Default for TaskReturnV0 {
33 fn default() -> Self {
34 TaskReturnV0 {
35 trigger: TriggerV0::Now,
36 transaction: TransactionSourceV0::CompiledV0(CompiledTransactionV0::default()),
37 crank_reward: None,
38 free_tasks: 0,
39 description: "".to_string(),
40 }
41 }
42}
43
44#[allow(clippy::derivable_impls)]
45impl Default for TriggerV0 {
46 fn default() -> Self {
47 TriggerV0::Now
48 }
49}
50
51pub use self::{
52 tuktuk::{
53 accounts::{
54 TaskQueueAuthorityV0, TaskQueueNameMappingV0, TaskQueueV0, TaskV0, TuktukConfigV0,
55 },
56 client, types,
57 },
58 types::{
59 CompiledInstructionV0, CompiledTransactionV0, InitializeTuktukConfigArgsV0,
60 TransactionSourceV0, TriggerV0,
61 },
62};
63
64impl TriggerV0 {
65 pub fn is_active(&self, now: i64) -> bool {
66 match self {
67 TriggerV0::Now => true,
68 TriggerV0::Timestamp(ts) => now >= *ts,
69 }
70 }
71}
72
73impl TaskQueueV0 {
74 pub fn task_exists(&self, task_idx: u16) -> bool {
75 if task_idx >= self.capacity {
76 return false;
77 }
78 self.task_bitmap[task_idx as usize / 8] & (1 << (task_idx % 8)) != 0
79 }
80
81 pub fn next_available_task_id(&self) -> Option<u16> {
82 for (byte_idx, byte) in self.task_bitmap.iter().enumerate() {
83 if *byte != 0xff {
84 for bit_idx in 0..8 {
86 if byte & (1 << bit_idx) == 0 {
87 return Some((byte_idx * 8 + bit_idx) as u16);
88 }
89 }
90 }
91 }
92 None
93 }
94}
95
96pub fn compile_transaction(
97 instructions: Vec<Instruction>,
98 signer_seeds: Vec<Vec<Vec<u8>>>,
99) -> Result<(CompiledTransactionV0, Vec<AccountMeta>)> {
100 let mut pubkeys_to_metadata: HashMap<Pubkey, AccountMeta> = HashMap::new();
101
102 for ix in &instructions {
104 pubkeys_to_metadata
105 .entry(ix.program_id)
106 .or_insert(AccountMeta {
107 pubkey: ix.program_id,
108 is_signer: false,
109 is_writable: false,
110 });
111
112 for key in &ix.accounts {
113 let entry = pubkeys_to_metadata
114 .entry(key.pubkey)
115 .or_insert(AccountMeta {
116 is_signer: false,
117 is_writable: false,
118 pubkey: key.pubkey,
119 });
120 entry.is_writable |= key.is_writable;
121 entry.is_signer |= key.is_signer;
122 }
123 }
124
125 let mut sorted_accounts: Vec<Pubkey> = pubkeys_to_metadata.keys().cloned().collect();
127 sorted_accounts.sort_by(|a, b| {
128 let a_meta = &pubkeys_to_metadata[a];
129 let b_meta = &pubkeys_to_metadata[b];
130
131 fn get_priority(meta: &AccountMeta) -> u8 {
133 match (meta.is_signer, meta.is_writable) {
134 (true, true) => 0, (true, false) => 1, (false, true) => 2, (false, false) => 3, }
139 }
140
141 get_priority(a_meta).cmp(&get_priority(b_meta))
142 });
143
144 let mut num_rw_signers = 0u8;
146 let mut num_ro_signers = 0u8;
147 let mut num_rw = 0u8;
148
149 for k in &sorted_accounts {
150 let metadata = &pubkeys_to_metadata[k];
151 if metadata.is_signer && metadata.is_writable {
152 num_rw_signers += 1;
153 } else if metadata.is_signer && !metadata.is_writable {
154 num_ro_signers += 1;
155 } else if metadata.is_writable {
156 num_rw += 1;
157 }
158 }
159
160 let accounts_to_index: HashMap<Pubkey, u8> = sorted_accounts
162 .iter()
163 .enumerate()
164 .map(|(i, k)| (*k, i as u8))
165 .collect();
166
167 let compiled_instructions: Vec<CompiledInstructionV0> = instructions
169 .iter()
170 .map(|ix| CompiledInstructionV0 {
171 program_id_index: *accounts_to_index.get(&ix.program_id).unwrap(),
172 accounts: ix
173 .accounts
174 .iter()
175 .map(|k| *accounts_to_index.get(&k.pubkey).unwrap())
176 .collect(),
177 data: ix.data.clone(),
178 })
179 .collect();
180
181 let remaining_accounts = sorted_accounts
182 .iter()
183 .enumerate()
184 .map(|(index, k)| AccountMeta {
185 pubkey: *k,
186 is_signer: false,
187 is_writable: index < num_rw_signers as usize
188 || (index >= num_rw_signers as usize + num_ro_signers as usize
189 && index < num_rw_signers as usize + num_ro_signers as usize + num_rw as usize),
190 })
191 .collect();
192
193 Ok((
194 CompiledTransactionV0 {
195 num_ro_signers,
196 num_rw_signers,
197 num_rw,
198 instructions: compiled_instructions,
199 signer_seeds,
200 accounts: sorted_accounts,
201 },
202 remaining_accounts,
203 ))
204}