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
96impl From<CompiledTransactionV0> for cron::types::CompiledTransactionV0 {
97 fn from(value: CompiledTransactionV0) -> Self {
98 cron::types::CompiledTransactionV0 {
99 num_ro_signers: value.num_ro_signers,
100 num_rw_signers: value.num_rw_signers,
101 num_rw: value.num_rw,
102 instructions: value.instructions.into_iter().map(|ix| ix.into()).collect(),
103 signer_seeds: value.signer_seeds,
104 accounts: value.accounts,
105 }
106 }
107}
108
109impl From<CompiledInstructionV0> for cron::types::CompiledInstructionV0 {
110 fn from(value: CompiledInstructionV0) -> Self {
111 cron::types::CompiledInstructionV0 {
112 program_id_index: value.program_id_index,
113 accounts: value.accounts,
114 data: value.data,
115 }
116 }
117}
118
119pub fn compile_transaction(
120 instructions: Vec<Instruction>,
121 signer_seeds: Vec<Vec<Vec<u8>>>,
122) -> Result<(CompiledTransactionV0, Vec<AccountMeta>)> {
123 let mut pubkeys_to_metadata: HashMap<Pubkey, AccountMeta> = HashMap::new();
124
125 for ix in &instructions {
127 pubkeys_to_metadata
128 .entry(ix.program_id)
129 .or_insert(AccountMeta {
130 pubkey: ix.program_id,
131 is_signer: false,
132 is_writable: false,
133 });
134
135 for key in &ix.accounts {
136 let entry = pubkeys_to_metadata
137 .entry(key.pubkey)
138 .or_insert(AccountMeta {
139 is_signer: false,
140 is_writable: false,
141 pubkey: key.pubkey,
142 });
143 entry.is_writable |= key.is_writable;
144 entry.is_signer |= key.is_signer;
145 }
146 }
147
148 let mut sorted_accounts: Vec<Pubkey> = pubkeys_to_metadata.keys().cloned().collect();
150 sorted_accounts.sort_by(|a, b| {
151 let a_meta = &pubkeys_to_metadata[a];
152 let b_meta = &pubkeys_to_metadata[b];
153
154 fn get_priority(meta: &AccountMeta) -> u8 {
156 match (meta.is_signer, meta.is_writable) {
157 (true, true) => 0, (true, false) => 1, (false, true) => 2, (false, false) => 3, }
162 }
163
164 get_priority(a_meta).cmp(&get_priority(b_meta))
165 });
166
167 let mut num_rw_signers = 0u8;
169 let mut num_ro_signers = 0u8;
170 let mut num_rw = 0u8;
171
172 for k in &sorted_accounts {
173 let metadata = &pubkeys_to_metadata[k];
174 if metadata.is_signer && metadata.is_writable {
175 num_rw_signers += 1;
176 } else if metadata.is_signer && !metadata.is_writable {
177 num_ro_signers += 1;
178 } else if metadata.is_writable {
179 num_rw += 1;
180 }
181 }
182
183 let accounts_to_index: HashMap<Pubkey, u8> = sorted_accounts
185 .iter()
186 .enumerate()
187 .map(|(i, k)| (*k, i as u8))
188 .collect();
189
190 let compiled_instructions: Vec<CompiledInstructionV0> = instructions
192 .iter()
193 .map(|ix| CompiledInstructionV0 {
194 program_id_index: *accounts_to_index.get(&ix.program_id).unwrap(),
195 accounts: ix
196 .accounts
197 .iter()
198 .map(|k| *accounts_to_index.get(&k.pubkey).unwrap())
199 .collect(),
200 data: ix.data.clone(),
201 })
202 .collect();
203
204 let remaining_accounts = sorted_accounts
205 .iter()
206 .enumerate()
207 .map(|(index, k)| AccountMeta {
208 pubkey: *k,
209 is_signer: false,
210 is_writable: index < num_rw_signers as usize
211 || (index >= num_rw_signers as usize + num_ro_signers as usize
212 && index < num_rw_signers as usize + num_ro_signers as usize + num_rw as usize),
213 })
214 .collect();
215
216 Ok((
217 CompiledTransactionV0 {
218 num_ro_signers,
219 num_rw_signers,
220 num_rw,
221 instructions: compiled_instructions,
222 signer_seeds,
223 accounts: sorted_accounts,
224 },
225 remaining_accounts,
226 ))
227}