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