switchboard_common/env/
solana.rs

1use crate::SbError;
2use serde::Deserialize;
3use serde::Serialize;
4
5fn default_cluster() -> String {
6    "devnet".to_string()
7}
8
9const DEFAULT_PUBKEY: &str = "11111111111111111111111111111111";
10
11#[derive(Clone, Debug, Default, Deserialize, Serialize)]
12pub enum FunctionTriggerType {
13    #[default]
14    Request,
15    Routine,
16    Service,
17}
18/// The expected environment variables when a solana function is being executed
19#[derive(Clone, Debug, Default, Deserialize, Serialize)]
20pub struct SolanaFunctionSimulationEnvironment {
21    ///  SB_FUNCTION_TRIGGER: request* | routine
22    #[serde(default)]
23    pub sb_function_trigger: FunctionTriggerType,
24    // /// SB_FUNCTION_REQUEST: 0* | 1
25    // pub sb_function_request: bool,
26    // /// SB_FUNCTION_ROUTINE: 0* | 1
27    // pub sb_function_routine: bool,
28    // /// SB_FUNCTION_SERVICE: 0* | 1
29    // pub sb_function_service: bool,
30    /// SB_CONTAINER_PARAMS: 0* | 1
31    #[serde(default)]
32    pub sb_container_params: Vec<u8>,
33}
34impl SolanaFunctionSimulationEnvironment {
35    pub fn parse() -> Result<Self, SbError> {
36        match envy::from_env::<SolanaFunctionSimulationEnvironment>() {
37            Ok(env) => Ok(env),
38            Err(error) => match &error {
39                envy::Error::MissingValue(msg) => Err(SbError::EnvVariableMissing(msg.to_string())),
40                envy::Error::Custom(msg) => Err(SbError::CustomMessage(format!(
41                    "failed to decode environment variables: {}",
42                    msg
43                ))),
44            },
45        }
46    }
47}
48/// The expected environment variables when a solana function is being executed
49#[derive(Clone, Debug, Default, Deserialize, Serialize)]
50pub struct SolanaFunctionEnvironment {
51    /// FUNCTION_KEY: the pubkey of the function being executed
52    pub function_key: String,
53    /// PAYER: The gas payer for this transaction
54    pub payer: String,
55    /// VERIFIER: the pubey of the oracle veriying this call
56    pub verifier: String,
57    /// REWARD_RECEIVER: The escrow to send the reward the oracle will receive
58    /// for executing this function
59    pub reward_receiver: String,
60
61    // can be manually populated from client if missing
62    /// FUNCTION_DATA: The preloaded data of the `FUNCTION_KEY` account
63    #[serde(default)]
64    pub function_data: String,
65    /// VERIFIER_ENCLAVE_SIGNER: The keypair the verifying oracle is using to
66    /// sign this transaction.
67    #[serde(default)]
68    pub verifier_enclave_signer: String,
69    /// QUEUE_AUTHORITY: The authority of the oracle queue this function is
70    /// executing on.
71    #[serde(default)]
72    pub queue_authority: String,
73
74    // only used for routines
75    /// FUNCTION_ROUTINE_KEY: If this function is being called with parameters,
76    /// this variable will hold the pubkey of the request account
77    #[serde(default)]
78    pub function_routine_key: String,
79
80    /// FUNCTION_ROUTINE_DATA: The preloaded data of the `FUNCTION_ROUTINE_KEY`
81    /// account
82    #[serde(default)]
83    pub function_routine_data: String,
84
85    // only used for requests
86    /// FUNCTION_REQUEST_KEY: If this function is being called with parameters,
87    /// this ariable will hold the pubkey of the request account
88    #[serde(default)]
89    pub function_request_key: String,
90    /// FUNCTION_REQUEST_DATA: The preloaded data of the `FUNCTION_REQUEST_KEY`
91    /// account
92    #[serde(default)]
93    pub function_request_data: String,
94
95    // helpers
96    #[serde(default = "default_cluster")]
97    pub cluster: String,
98
99    // optional, we can use this to wait for a slot if the public RPCs are behind
100    pub minimum_context_slot: Option<u64>,
101}
102impl SolanaFunctionEnvironment {
103    pub fn parse() -> Result<Self, SbError> {
104        match envy::from_env::<SolanaFunctionEnvironment>() {
105            Ok(env) => Ok(env),
106            Err(error) => match &error {
107                envy::Error::MissingValue(msg) => Err(SbError::EnvVariableMissing(msg.to_string())),
108                envy::Error::Custom(msg) => Err(SbError::CustomMessage(format!(
109                    "failed to decode environment variables: {}",
110                    msg
111                ))),
112            },
113        }
114    }
115
116    /**
117     * Returns the vec! of environment variable key-value pairs used by bollard
118     */
119    pub fn to_env(&self) -> Vec<String> {
120        let mut env = vec![];
121        env.push(format!("CLUSTER={}", self.cluster));
122        env.push(format!("FUNCTION_KEY={}", self.function_key));
123        env.push(format!("PAYER={}", self.payer));
124        env.push(format!("VERIFIER={}", self.verifier));
125        env.push(format!("REWARD_RECEIVER={}", self.reward_receiver));
126
127        if !self.verifier_enclave_signer.is_empty()
128            && &self.verifier_enclave_signer != &DEFAULT_PUBKEY.to_string()
129        {
130            env.push(format!(
131                "VERIFIER_ENCLAVE_SIGNER={}",
132                self.verifier_enclave_signer
133            ));
134        }
135
136        if !self.queue_authority.is_empty() && &self.queue_authority != &DEFAULT_PUBKEY.to_string()
137        {
138            env.push(format!("QUEUE_AUTHORITY={}", self.queue_authority));
139        }
140
141        if !self.function_data.is_empty() && !self.function_data.chars().all(|c| c == '0') {
142            env.push(format!("FUNCTION_DATA={}", self.function_data));
143        }
144
145        if !self.function_routine_key.is_empty()
146            && &self.function_routine_key != &DEFAULT_PUBKEY.to_string()
147        {
148            env.push(format!(
149                "FUNCTION_ROUTINE_KEY={}",
150                self.function_routine_key
151            ));
152        }
153
154        if !self.function_routine_data.is_empty()
155            && !self.function_routine_data.chars().all(|c| c == '0')
156        {
157            env.push(format!(
158                "FUNCTION_ROUTINE_DATA={}",
159                self.function_routine_data
160            ));
161        }
162
163        if !self.function_request_key.is_empty()
164            && &self.function_request_key != &DEFAULT_PUBKEY.to_string()
165        {
166            env.push(format!(
167                "FUNCTION_REQUEST_KEY={}",
168                self.function_request_key
169            ));
170        }
171
172        if !self.function_request_data.is_empty()
173            && !self.function_request_data.chars().all(|c| c == '0')
174        {
175            env.push(format!(
176                "FUNCTION_REQUEST_DATA={}",
177                self.function_request_data
178            ));
179        }
180
181        if let Some(minimum_context_slot) = self.minimum_context_slot {
182            env.push(format!("MINIMUM_CONTEXT_SLOT={}", minimum_context_slot));
183        }
184
185        env
186    }
187
188    /// Helper method to set all environment variables. Useful to help test your function environment.
189    pub fn set_env(&self) -> Result<(), SbError> {
190        if !self.function_key.is_empty() {
191            std::env::set_var("FUNCTION_KEY", self.function_key.clone());
192        }
193        if !self.payer.is_empty() {
194            std::env::set_var("PAYER", self.payer.clone());
195        }
196        if !self.verifier.is_empty() {
197            std::env::set_var("VERIFIER", self.verifier.clone());
198        }
199        if !self.reward_receiver.is_empty() {
200            std::env::set_var("REWARD_RECEIVER", self.reward_receiver.clone());
201        }
202        if !self.function_data.is_empty() {
203            std::env::set_var("FUNCTION_DATA", self.function_data.clone());
204        }
205        if !self.verifier_enclave_signer.is_empty() {
206            std::env::set_var(
207                "VERIFIER_ENCLAVE_SIGNER",
208                self.verifier_enclave_signer.clone(),
209            );
210        }
211        if !self.queue_authority.is_empty() {
212            std::env::set_var("QUEUE_AUTHORITY", self.queue_authority.clone());
213        }
214        if !self.function_routine_key.is_empty() {
215            std::env::set_var("FUNCTION_ROUTINE_KEY", self.function_routine_key.clone());
216        }
217        if !self.function_routine_data.is_empty() {
218            std::env::set_var("FUNCTION_ROUTINE_DATA", self.function_routine_data.clone());
219        }
220        if !self.function_request_key.is_empty() {
221            std::env::set_var("FUNCTION_REQUEST_KEY", self.function_request_key.clone());
222        }
223        if !self.function_request_data.is_empty() {
224            std::env::set_var("FUNCTION_REQUEST_DATA", self.function_request_data.clone());
225        }
226        if !self.cluster.is_empty() {
227            std::env::set_var("CLUSTER", self.cluster.clone());
228        } else {
229            std::env::set_var("CLUSTER", "devnet");
230        }
231        if let Some(minimum_context_slot) = self.minimum_context_slot {
232            std::env::set_var("MINIMUM_CONTEXT_SLOT", minimum_context_slot.to_string());
233        }
234
235        Ok(())
236    }
237}