miclockwork_thread_program/state/
versioned_thread.rs

1use anchor_lang::{prelude::*, AccountDeserialize};
2use miclockwork_thread_program_v1::{
3    state::Thread as ThreadV1,
4    typedefs::{Trigger as TriggerV1, TriggerContext as TriggerContextV1},
5};
6use miclockwork_utils::thread::SerializableAccount;
7
8use crate::{
9    ClockData, ExecContext, SerializableInstruction, Thread as ThreadV2, Trigger, TriggerContext,
10};
11
12#[derive(Clone, Debug, PartialEq)]
13pub enum VersionedThread {
14    V1(ThreadV1),
15    V2(ThreadV2),
16}
17
18impl VersionedThread {
19    pub fn authority(&self) -> Pubkey {
20        match self {
21            Self::V1(t) => t.authority,
22            Self::V2(t) => t.authority,
23        }
24    }
25
26    pub fn created_at(&self) -> ClockData {
27        match self {
28            Self::V1(t) => ClockData {
29                slot: t.created_at.slot,
30                epoch: t.created_at.epoch,
31                unix_timestamp: t.created_at.unix_timestamp,
32            },
33            Self::V2(t) => t.created_at.clone(),
34        }
35    }
36
37    pub fn exec_context(&self) -> Option<ExecContext> {
38        match self {
39            Self::V1(t) => t.exec_context.map(|e| ExecContext {
40                exec_index: 0,
41                execs_since_reimbursement: e.execs_since_reimbursement,
42                execs_since_slot: e.execs_since_slot,
43                last_exec_at: e.last_exec_at,
44                trigger_context: unsafe {
45                    std::mem::transmute::<TriggerContextV1, TriggerContext>(e.trigger_context)
46                },
47            }),
48            Self::V2(t) => t.exec_context,
49        }
50    }
51
52    pub fn id(&self) -> Vec<u8> {
53        match self {
54            Self::V1(t) => t.id.as_bytes().to_vec(),
55            Self::V2(t) => t.id.clone(),
56        }
57    }
58
59    pub fn next_instruction(&self) -> Option<SerializableInstruction> {
60        match self {
61            Self::V1(t) => match &t.next_instruction {
62                None => None,
63                Some(ix) => Some(SerializableInstruction {
64                    program_id: ix.program_id,
65                    accounts: ix
66                        .accounts
67                        .iter()
68                        .map(|a| unsafe {
69                            std::mem::transmute_copy::<
70                                miclockwork_thread_program_v1::typedefs::AccountMetaData,
71                                SerializableAccount,
72                            >(a)
73                        })
74                        .collect::<Vec<SerializableAccount>>(),
75                    data: ix.data.clone(),
76                }),
77            },
78            Self::V2(t) => t.next_instruction.clone(),
79        }
80    }
81
82    pub fn paused(&self) -> bool {
83        match self {
84            Self::V1(t) => t.paused,
85            Self::V2(t) => t.paused,
86        }
87    }
88
89    pub fn program_id(&self) -> Pubkey {
90        match self {
91            Self::V1(_) => miclockwork_thread_program_v1::ID,
92            Self::V2(_) => crate::ID,
93        }
94    }
95
96    pub fn pubkey(&self) -> Pubkey {
97        match self {
98            Self::V1(_) => {
99                ThreadV1::pubkey(self.authority(), String::from_utf8(self.id()).unwrap())
100            }
101            Self::V2(_) => ThreadV2::pubkey(self.authority(), self.id()),
102        }
103    }
104
105    pub fn rate_limit(&self) -> u64 {
106        match self {
107            Self::V1(t) => t.rate_limit,
108            Self::V2(t) => t.rate_limit,
109        }
110    }
111
112    pub fn trigger(&self) -> Trigger {
113        match self {
114            Self::V1(t) => match &t.trigger {
115                TriggerV1::Account {
116                    address,
117                    offset,
118                    size,
119                } => Trigger::Account {
120                    address: *address,
121                    offset: *offset as u64,
122                    size: *size as u64,
123                },
124                TriggerV1::Cron {
125                    schedule,
126                    skippable,
127                } => Trigger::Cron {
128                    schedule: schedule.clone(),
129                    skippable: *skippable,
130                },
131                TriggerV1::Immediate => Trigger::Now,
132            },
133            Self::V2(t) => t.trigger.clone(),
134        }
135    }
136}
137
138impl AccountDeserialize for VersionedThread {
139    fn try_deserialize(buf: &mut &[u8]) -> anchor_lang::Result<Self> {
140        Self::try_deserialize_unchecked(buf)
141    }
142
143    fn try_deserialize_unchecked(buf: &mut &[u8]) -> anchor_lang::Result<Self> {
144        // Try first to deserialize into ThreadV2.
145        // If this fails, try to deserialize into ThreadV1.
146        match ThreadV2::try_deserialize(buf) {
147            Err(_err) => Ok(VersionedThread::V1(ThreadV1::try_deserialize(buf)?)),
148            Ok(t) => Ok(VersionedThread::V2(t)),
149        }
150    }
151}
152
153impl TryFrom<Vec<u8>> for VersionedThread {
154    type Error = Error;
155    fn try_from(data: Vec<u8>) -> std::result::Result<Self, Self::Error> {
156        VersionedThread::try_deserialize(&mut data.as_slice())
157    }
158}