sablier_thread_program/state/
thread.rs

1use anchor_lang::{prelude::*, AnchorDeserialize, AnchorSerialize};
2use sablier_utils::{
3    account::AccountInfoExt,
4    thread::{ClockData, SerializableInstruction, Trigger},
5    MinSpace, Space,
6};
7
8use crate::constants::SEED_THREAD;
9
10/// Tracks the current state of a transaction thread on Solana.
11#[account]
12#[derive(Debug)]
13pub struct Thread {
14    /// The owner of this thread.
15    pub authority: Pubkey,
16    /// The bump, used for PDA validation.
17    pub bump: u8,
18    /// The cluster clock at the moment the thread was created.
19    pub created_at: ClockData,
20    pub domain: Option<Vec<u8>>,
21    /// The context of the thread's current execution state.
22    pub exec_context: Option<ExecContext>,
23    /// The number of lamports to payout to workers per execution.
24    pub fee: u64,
25    /// The id of the thread, given by the authority.
26    pub id: Vec<u8>,
27    /// The instructions to be executed.
28    pub instructions: Vec<SerializableInstruction>,
29    /// The next instruction to be executed.
30    pub next_instruction: Option<SerializableInstruction>,
31    /// Whether or not the thread is currently paused.
32    pub paused: bool,
33    /// The maximum number of execs allowed per slot.
34    pub rate_limit: u64,
35    /// The triggering event to kickoff a thread.
36    pub trigger: Trigger,
37}
38
39impl Thread {
40    /// Derive the pubkey of a thread account.
41    pub fn pubkey(authority: Pubkey, id: Vec<u8>, domain: Option<Vec<u8>>) -> Pubkey {
42        Pubkey::find_program_address(
43            &[
44                SEED_THREAD,
45                authority.as_ref(),
46                id.as_slice(),
47                domain.unwrap_or_default().as_slice(),
48            ],
49            &crate::ID,
50        )
51        .0
52    }
53}
54
55impl PartialEq for Thread {
56    fn eq(&self, other: &Self) -> bool {
57        self.authority.eq(&other.authority) && self.id.eq(&other.id)
58    }
59}
60
61impl Eq for Thread {}
62
63/// Trait for reading and writing to a thread account.
64pub trait ThreadAccount {
65    /// Get the pubkey of the thread account.
66    fn pubkey(&self) -> Pubkey;
67
68    /// Allocate more memory for the account.
69    fn realloc_account(&mut self) -> Result<()>;
70}
71
72impl Thread {
73    pub fn min_space(instructions: &[SerializableInstruction]) -> Result<usize> {
74        let ins_space = instructions.try_to_vec()?.len();
75        let max_ins_size = instructions
76            .iter()
77            .map(|ins| ins.try_to_vec().map(|v| v.len()).unwrap_or(0))
78            .max()
79            .unwrap_or(0);
80
81        Ok(
82            8
83            + Pubkey::MIN_SPACE // authority
84            + u8::MIN_SPACE // bump
85            + ClockData::MIN_SPACE // created_at
86            + (1 + 4 + 32) // domain
87            + <Option<ExecContext>>::MIN_SPACE // exec_context
88            + u64::MIN_SPACE // fee
89            + (4 + 32) // id
90            + (4 + ins_space) // instructions
91            + (1 + max_ins_size) // next_instruction
92            + bool::MIN_SPACE // paused
93            + u64::MIN_SPACE // rate_limit
94            + Trigger::MIN_SPACE, // trigger
95        )
96    }
97}
98
99impl ThreadAccount for Account<'_, Thread> {
100    fn pubkey(&self) -> Pubkey {
101        Thread::pubkey(self.authority, self.id.clone(), self.domain.clone())
102    }
103
104    fn realloc_account(&mut self) -> Result<()> {
105        // Realloc memory for the thread account
106        let data_len = 8 + self.try_to_vec()?.len();
107
108        self.realloc(data_len, false)?;
109        Ok(())
110    }
111}
112
113/// The execution context of a particular transaction thread.
114#[derive(AnchorDeserialize, AnchorSerialize, MinSpace, Clone, Copy, Debug, PartialEq, Eq)]
115pub struct ExecContext {
116    /// Index of the next instruction to be executed.
117    pub exec_index: u64,
118
119    /// Number of execs since the last tx reimbursement.
120    /// To be deprecated in v3 since we now reimburse for every transaction.
121    pub execs_since_reimbursement: u64,
122
123    /// Number of execs in this slot.
124    pub execs_since_slot: u64,
125
126    /// Slot of the last exec
127    pub last_exec_at: u64,
128
129    /// Context for the triggering condition
130    pub trigger_context: TriggerContext,
131}
132
133/// The event which allowed a particular transaction thread to be triggered.
134#[derive(AnchorDeserialize, AnchorSerialize, MinSpace, Clone, Copy, Debug, PartialEq, Eq)]
135pub enum TriggerContext {
136    /// A running hash of the observed account data.
137    Account {
138        /// The account's data hash.
139        data_hash: u64,
140    },
141
142    /// A cron execution context.
143    Cron {
144        /// The threshold moment the schedule was waiting for.
145        started_at: i64,
146    },
147
148    /// The trigger context for threads with a "now" trigger.
149    Now,
150
151    /// The trigger context for threads with a "slot" trigger.
152    Slot {
153        /// The threshold slot the schedule was waiting for.
154        started_at: u64,
155    },
156
157    /// The trigger context for threads with an "epoch" trigger.
158    Epoch {
159        /// The threshold epoch the schedule was waiting for.
160        started_at: u64,
161    },
162
163    /// The trigger context for threads with an "timestamp" trigger.
164    Timestamp {
165        /// The threshold moment the schedule was waiting for.
166        started_at: i64,
167    },
168
169    /// The trigger context for threads with a "pyth" trigger.
170    Pyth { price: i64 },
171
172    /// The trigger context for threads with a periodic timestamp trigger.
173    Periodic {
174        /// The threshold moment the schedule was waiting for.
175        started_at: i64,
176    },
177}
178
179/// The properties of threads which are updatable.
180#[derive(AnchorSerialize, AnchorDeserialize)]
181pub struct ThreadSettings {
182    pub fee: Option<u64>,
183    pub instructions: Option<Vec<SerializableInstruction>>,
184    pub name: Option<String>,
185    pub rate_limit: Option<u64>,
186    pub trigger: Option<Trigger>,
187}