1use crate::state::{ManagerAccount, QueueAccount, QueueStatus};
2
3use super::{Config, Fee, Manager};
4
5use {
6 super::Queue,
7 crate::{errors::CronosError, pda::PDA},
8 anchor_lang::{
9 prelude::borsh::BorshSchema, prelude::*, solana_program::instruction::Instruction,
10 AnchorDeserialize,
11 },
12 std::convert::TryFrom,
13};
14
15pub const SEED_TASK: &[u8] = b"task";
16
17#[account]
22#[derive(Debug)]
23pub struct Task {
24 pub id: u128,
25 pub ixs: Vec<InstructionData>,
26 pub queue: Pubkey,
27}
28
29impl Task {
30 pub fn pda(queue: Pubkey, id: u128) -> PDA {
31 Pubkey::find_program_address(
32 &[SEED_TASK, queue.as_ref(), id.to_be_bytes().as_ref()],
33 &crate::ID,
34 )
35 }
36}
37
38impl TryFrom<Vec<u8>> for Task {
39 type Error = Error;
40 fn try_from(data: Vec<u8>) -> std::result::Result<Self, Self::Error> {
41 Task::try_deserialize(&mut data.as_slice())
42 }
43}
44
45pub trait TaskAccount {
50 fn new(&mut self, ixs: Vec<InstructionData>, queue: &mut Account<Queue>) -> Result<()>;
51
52 fn exec(
53 &mut self,
54 account_infos: &Vec<AccountInfo>,
55 config: &Account<Config>,
56 delegate: &mut Signer,
57 fee: &mut Account<Fee>,
58 manager: &Account<Manager>,
59 manager_bump: u8,
60 queue: &mut Account<Queue>,
61 ) -> Result<()>;
62}
63
64impl TaskAccount for Account<'_, Task> {
65 fn new(&mut self, ixs: Vec<InstructionData>, queue: &mut Account<Queue>) -> Result<()> {
66 for ix in ixs.iter() {
68 for acc in ix.accounts.iter() {
69 if acc.is_signer {
70 require!(
71 acc.pubkey == queue.manager || acc.pubkey == crate::payer::ID,
72 CronosError::InvalidSignatory
73 );
74 }
75 }
76 }
77
78 self.id = queue.task_count;
80 self.ixs = ixs;
81 self.queue = queue.key();
82
83 queue.task_count = queue.task_count.checked_add(1).unwrap();
85
86 Ok(())
87 }
88
89 fn exec(
90 &mut self,
91 account_infos: &Vec<AccountInfo>,
92 config: &Account<Config>,
93 delegate: &mut Signer,
94 fee: &mut Account<Fee>,
95 manager: &Account<Manager>,
96 manager_bump: u8,
97 queue: &mut Account<Queue>,
98 ) -> Result<()> {
99 require!(
101 self.id
102 == match queue.status {
103 QueueStatus::Processing { task_id } => task_id,
104 _ => return Err(CronosError::InvalidQueueStatus.into()),
105 },
106 CronosError::InvalidTask
107 );
108
109 require!(delegate.data_is_empty(), CronosError::DelegateDataNotEmpty);
111
112 let delegate_lamports_pre = delegate.lamports();
114
115 let dyanmic_ixs: &mut Vec<InstructionData> = &mut vec![];
117
118 for ix in &self.ixs {
120 let accs: &mut Vec<AccountMetaData> = &mut vec![];
131 ix.accounts.iter().for_each(|acc| {
132 if acc.pubkey == crate::payer::ID {
133 accs.push(AccountMetaData {
134 pubkey: delegate.key(),
135 is_signer: acc.is_signer,
136 is_writable: acc.is_writable,
137 });
138 } else {
139 accs.push(acc.clone());
140 }
141 });
142
143 let exec_response = manager.sign(
149 &account_infos,
150 manager_bump,
151 &InstructionData {
152 program_id: ix.program_id,
153 accounts: accs.clone(),
154 data: ix.data.clone(),
155 },
156 )?;
157
158 match exec_response {
160 None => (),
161 Some(exec_response) => match exec_response.dynamic_accounts {
162 None => (),
163 Some(dynamic_accounts) => {
164 require!(
165 dynamic_accounts.len() == ix.accounts.len(),
166 CronosError::InvalidDynamicAccounts
167 );
168 dyanmic_ixs.push(InstructionData {
169 program_id: ix.program_id,
170 accounts: dynamic_accounts
171 .iter()
172 .enumerate()
173 .map(|(i, pubkey)| {
174 let acc = ix.accounts.get(i).unwrap();
175 AccountMetaData {
176 pubkey: match pubkey {
177 _ if *pubkey == delegate.key() => crate::payer::ID,
178 _ => *pubkey,
179 },
180 is_signer: acc.is_signer,
181 is_writable: acc.is_writable,
182 }
183 })
184 .collect::<Vec<AccountMetaData>>(),
185 data: ix.data.clone(),
186 });
187 }
188 },
189 }
190 }
191
192 require!(delegate.data_is_empty(), CronosError::DelegateDataNotEmpty);
194
195 if !dyanmic_ixs.is_empty() {
197 self.ixs = dyanmic_ixs.clone();
198 }
199
200 let delegate_lamports_post = delegate.lamports();
202 let delegate_reimbursement = delegate_lamports_pre
203 .checked_sub(delegate_lamports_post)
204 .unwrap();
205
206 let total_delegate_fee = config
208 .delegate_fee
209 .checked_add(delegate_reimbursement)
210 .unwrap();
211 **manager.to_account_info().try_borrow_mut_lamports()? = manager
212 .to_account_info()
213 .lamports()
214 .checked_sub(total_delegate_fee)
215 .unwrap();
216 **delegate.to_account_info().try_borrow_mut_lamports()? = delegate
217 .to_account_info()
218 .lamports()
219 .checked_add(total_delegate_fee)
220 .unwrap();
221
222 **manager.to_account_info().try_borrow_mut_lamports()? = manager
224 .to_account_info()
225 .lamports()
226 .checked_sub(config.program_fee)
227 .unwrap();
228 **fee.to_account_info().try_borrow_mut_lamports()? = fee
229 .to_account_info()
230 .lamports()
231 .checked_add(config.program_fee)
232 .unwrap();
233
234 fee.balance = fee.balance.checked_add(config.program_fee).unwrap();
236
237 let next_task_id = self.id.checked_add(1).unwrap();
239 if next_task_id == queue.task_count {
240 queue.roll_forward()?;
241 } else {
242 queue.status = QueueStatus::Processing {
243 task_id: next_task_id,
244 };
245 }
246
247 Ok(())
248 }
249}
250
251#[derive(AnchorDeserialize, AnchorSerialize, BorshSchema, Clone, Debug, PartialEq)]
256pub struct InstructionData {
257 pub program_id: Pubkey,
259 pub accounts: Vec<AccountMetaData>,
261 pub data: Vec<u8>,
263}
264
265impl From<Instruction> for InstructionData {
266 fn from(instruction: Instruction) -> Self {
267 InstructionData {
268 program_id: instruction.program_id,
269 accounts: instruction
270 .accounts
271 .iter()
272 .map(|a| AccountMetaData {
273 pubkey: a.pubkey,
274 is_signer: a.is_signer,
275 is_writable: a.is_writable,
276 })
277 .collect(),
278 data: instruction.data,
279 }
280 }
281}
282
283impl From<&InstructionData> for Instruction {
284 fn from(instruction: &InstructionData) -> Self {
285 Instruction {
286 program_id: instruction.program_id,
287 accounts: instruction
288 .accounts
289 .iter()
290 .map(|a| AccountMeta {
291 pubkey: a.pubkey,
292 is_signer: a.is_signer,
293 is_writable: a.is_writable,
294 })
295 .collect(),
296 data: instruction.data.clone(),
297 }
298 }
299}
300
301impl TryFrom<Vec<u8>> for InstructionData {
302 type Error = Error;
303 fn try_from(data: Vec<u8>) -> std::result::Result<Self, Self::Error> {
304 Ok(
305 borsh::try_from_slice_with_schema::<InstructionData>(data.as_slice())
306 .map_err(|_err| ErrorCode::AccountDidNotDeserialize)?,
307 )
308 }
309}
310
311#[derive(AnchorDeserialize, AnchorSerialize, BorshSchema, Clone, Debug, PartialEq)]
316pub struct AccountMetaData {
317 pub pubkey: Pubkey,
319 pub is_signer: bool,
321 pub is_writable: bool,
323}