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 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
use std::{convert::TryFrom, fmt::Debug, hash::Hash};
use anchor_lang::{
prelude::borsh::BorshSchema,
prelude::Pubkey,
prelude::*,
solana_program::{self, instruction::Instruction},
AnchorDeserialize,
};
use serde::{Deserialize, Serialize};
use static_pubkey::static_pubkey;
/// The stand-in pubkey for delegating a payer address to a worker. All workers are re-imbursed by the user for lamports spent during this delegation.
pub static PAYER_PUBKEY: Pubkey = static_pubkey!("C1ockworkPayer11111111111111111111111111111");
/// The clock object, representing a specific moment in time recorded by a Solana cluster.
#[derive(AnchorDeserialize, AnchorSerialize, BorshSchema, Clone, Debug, PartialEq)]
pub struct ClockData {
/// The current slot.
pub slot: u64,
/// The bank epoch.
pub epoch: u64,
/// The current unix timestamp.
pub unix_timestamp: i64,
}
impl From<Clock> for ClockData {
fn from(clock: Clock) -> Self {
ClockData {
slot: clock.slot,
epoch: clock.epoch,
unix_timestamp: clock.unix_timestamp,
}
}
}
impl TryFrom<Vec<u8>> for ClockData {
type Error = Error;
fn try_from(data: Vec<u8>) -> std::result::Result<Self, Self::Error> {
Ok(
borsh::try_from_slice_with_schema::<ClockData>(data.as_slice())
.map_err(|_err| ErrorCode::AccountDidNotDeserialize)?,
)
}
}
/// The triggering conditions of a thread.
#[derive(AnchorDeserialize, AnchorSerialize, Debug, Clone, PartialEq)]
pub enum Trigger {
/// Allows a thread to be kicked off whenever the data of an account changes.
Account {
/// The address of the account to monitor.
address: Pubkey,
/// The byte offset of the account data to monitor.
offset: u64,
/// The size of the byte slice to monitor (must be less than 1kb)
size: u64,
},
/// Allows a thread to be kicked off according to a one-time or recurring schedule.
Cron {
/// The schedule in cron syntax. Value must be parsable by the `clockwork_cron` package.
schedule: String,
/// Boolean value indicating whether triggering moments may be skipped if they are missed (e.g. due to network downtime).
/// If false, any "missed" triggering moments will simply be executed as soon as the network comes back online.
skippable: bool,
},
/// Allows a thread to be kicked off as soon as it's created.
Now,
/// Allows a thread to be kicked off according to a slot.
Slot { slot: u64 },
/// Allows a thread to be kicked off according to an epoch number.
Epoch { epoch: u64 },
/// Allows a thread to be kicked off according to a unix timestamp.
Timestamp { unix_ts: i64 },
/// Allows a thread to be kicked off according to a Pyth price feed movement.
Pyth {
/// The address of the price feed to monitor.
price_feed: Pubkey,
/// The equality operator (gte or lte) used to compare prices.
equality: Equality,
/// The limit price to compare the Pyth feed to.
limit: i64,
},
}
/// Operators for describing how to compare two values to one another.
#[repr(u8)]
#[derive(AnchorDeserialize, AnchorSerialize, Clone, Debug, Eq, PartialEq, Hash)]
pub enum Equality {
GreaterThanOrEqual,
LessThanOrEqual,
}
/// A response value target programs can return to update the thread.
#[derive(AnchorDeserialize, AnchorSerialize, Clone, Debug)]
pub struct ThreadResponse {
/// If set, the thread will automatically close and return lamports to the provided address.
/// If dynamic_instruction is also set, close_to will take precedence and the dynamic instruction will not be executed.
pub close_to: Option<Pubkey>,
/// A dynamic instruction to execute next.
/// If close_to is also set, it will take precedence and the dynamic instruction will not be executed.
pub dynamic_instruction: Option<SerializableInstruction>,
/// Value to update the thread trigger to.
pub trigger: Option<Trigger>,
}
impl Default for ThreadResponse {
fn default() -> Self {
return Self {
close_to: None,
dynamic_instruction: None,
trigger: None,
};
}
}
/// The data needed execute an instruction on Solana.
#[derive(
AnchorDeserialize,
AnchorSerialize,
Serialize,
Deserialize,
BorshSchema,
Clone,
Debug,
Hash,
PartialEq,
)]
pub struct SerializableInstruction {
/// Pubkey of the instruction processor that executes this instruction
pub program_id: Pubkey,
/// Metadata for what accounts should be passed to the instruction processor
pub accounts: Vec<SerializableAccount>,
/// Opaque data passed to the instruction processor
pub data: Vec<u8>,
}
impl From<Instruction> for SerializableInstruction {
fn from(instruction: Instruction) -> Self {
SerializableInstruction {
program_id: instruction.program_id,
accounts: instruction
.accounts
.iter()
.map(|a| SerializableAccount {
pubkey: a.pubkey,
is_signer: a.is_signer,
is_writable: a.is_writable,
})
.collect(),
data: instruction.data,
}
}
}
impl From<&SerializableInstruction> for Instruction {
fn from(instruction: &SerializableInstruction) -> Self {
Instruction {
program_id: instruction.program_id,
accounts: instruction
.accounts
.iter()
.map(|a| AccountMeta {
pubkey: a.pubkey,
is_signer: a.is_signer,
is_writable: a.is_writable,
})
.collect(),
data: instruction.data.clone(),
}
}
}
impl TryFrom<Vec<u8>> for SerializableInstruction {
type Error = Error;
fn try_from(data: Vec<u8>) -> std::result::Result<Self, Self::Error> {
Ok(
borsh::try_from_slice_with_schema::<SerializableInstruction>(data.as_slice())
.map_err(|_err| ErrorCode::AccountDidNotDeserialize)?,
)
}
}
/// Account metadata needed to execute an instruction on Solana.
#[derive(
AnchorDeserialize,
AnchorSerialize,
Serialize,
Deserialize,
BorshSchema,
Clone,
Debug,
Hash,
PartialEq,
)]
pub struct SerializableAccount {
/// An account's public key
pub pubkey: Pubkey,
/// True if an Instruction requires a Transaction signature matching `pubkey`.
pub is_signer: bool,
/// True if the `pubkey` can be loaded as a read-write account.
pub is_writable: bool,
}
impl SerializableAccount {
/// Construct metadata for a writable account.
pub fn mutable(pubkey: Pubkey, signer: bool) -> Self {
Self {
pubkey,
is_signer: signer,
is_writable: true,
}
}
/// Construct metadata for a read-only account.
pub fn readonly(pubkey: Pubkey, signer: bool) -> Self {
Self {
pubkey,
is_signer: signer,
is_writable: false,
}
}
}