surfpool_types/
types.rs

1use std::{
2    collections::{BTreeMap, HashMap},
3    fmt,
4    path::PathBuf,
5};
6
7use blake3::Hash;
8use chrono::{DateTime, Local};
9use crossbeam_channel::{Receiver, Sender};
10// use litesvm::types::TransactionMetadata;
11use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer};
12use serde_with::{serde_as, BytesOrString};
13use solana_account_decoder_client_types::UiAccount;
14use solana_clock::{Clock, Epoch};
15use solana_epoch_info::EpochInfo;
16use solana_message::inner_instruction::InnerInstructionsList;
17use solana_pubkey::Pubkey;
18use solana_signature::Signature;
19use solana_transaction::versioned::VersionedTransaction;
20use solana_transaction_context::TransactionReturnData;
21use solana_transaction_error::TransactionError;
22use txtx_addon_kit::types::types::Value;
23use txtx_addon_network_svm_types::subgraph::SubgraphRequest;
24use uuid::Uuid;
25
26pub const DEFAULT_RPC_URL: &str = "https://api.mainnet-beta.solana.com";
27
28#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
29pub struct TransactionMetadata {
30    pub signature: Signature,
31    pub logs: Vec<String>,
32    pub inner_instructions: InnerInstructionsList,
33    pub compute_units_consumed: u64,
34    pub return_data: TransactionReturnData,
35}
36
37#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
38#[serde(rename_all = "camelCase")]
39pub enum TransactionConfirmationStatus {
40    Processed,
41    Confirmed,
42    Finalized,
43}
44
45#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)]
46pub enum BlockProductionMode {
47    #[default]
48    Clock,
49    Transaction,
50    Manual,
51}
52
53#[derive(Debug, Clone)]
54pub struct Collection {
55    pub uuid: Uuid,
56    pub name: String,
57    pub entries: Vec<SubgraphDataEntry>,
58}
59
60#[derive(Debug, Clone)]
61pub struct SubgraphDataEntry {
62    // The UUID of the entry
63    pub uuid: Uuid,
64    // A map of field names and their values
65    pub values: HashMap<String, Value>,
66    // The slot that the transaction that created this entry was processed in
67    pub block_height: u64,
68    // The transaction hash that created this entry
69    pub transaction_hash: Hash,
70}
71
72impl SubgraphDataEntry {
73    pub fn new(values: HashMap<String, Value>, block_height: u64, tx_hash: [u8; 32]) -> Self {
74        Self {
75            uuid: Uuid::new_v4(),
76            values,
77            block_height,
78            transaction_hash: Hash::from_bytes(tx_hash),
79        }
80    }
81}
82
83#[derive(Debug)]
84pub enum SubgraphEvent {
85    EndpointReady,
86    InfoLog(DateTime<Local>, String),
87    ErrorLog(DateTime<Local>, String),
88    WarnLog(DateTime<Local>, String),
89    DebugLog(DateTime<Local>, String),
90    Shutdown,
91}
92
93impl SubgraphEvent {
94    pub fn info<S>(msg: S) -> Self
95    where
96        S: Into<String>,
97    {
98        Self::InfoLog(Local::now(), msg.into())
99    }
100
101    pub fn warn<S>(msg: S) -> Self
102    where
103        S: Into<String>,
104    {
105        Self::WarnLog(Local::now(), msg.into())
106    }
107
108    pub fn error<S>(msg: S) -> Self
109    where
110        S: Into<String>,
111    {
112        Self::ErrorLog(Local::now(), msg.into())
113    }
114
115    pub fn debug<S>(msg: S) -> Self
116    where
117        S: Into<String>,
118    {
119        Self::DebugLog(Local::now(), msg.into())
120    }
121}
122
123/// Result structure for compute units estimation.
124#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
125#[serde(rename_all = "camelCase")]
126pub struct ComputeUnitsEstimationResult {
127    pub success: bool,
128    pub compute_units_consumed: u64,
129    pub log_messages: Option<Vec<String>>,
130    pub error_message: Option<String>,
131}
132
133/// The struct for storing the profiling results.
134#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
135#[serde(rename_all = "camelCase")]
136pub struct ProfileResult {
137    pub compute_units: ComputeUnitsEstimationResult,
138    pub state: ProfileState,
139}
140
141#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
142#[serde(rename_all = "camelCase")]
143pub struct ProfileState {
144    pub pre_execution: BTreeMap<Pubkey, Option<UiAccount>>,
145    pub post_execution: BTreeMap<Pubkey, Option<UiAccount>>,
146}
147
148impl ProfileState {
149    pub fn new(
150        pre_execution: BTreeMap<Pubkey, Option<UiAccount>>,
151        post_execution: BTreeMap<Pubkey, Option<UiAccount>>,
152    ) -> Self {
153        Self {
154            pre_execution,
155            post_execution,
156        }
157    }
158}
159
160#[derive(Debug, Clone, Deserialize, Serialize)]
161pub enum SchemaDataSourcingEvent {
162    Rountrip(Uuid),
163    ApplyEntry(Uuid, Vec<u8>, u64, [u8; 32]),
164}
165
166#[derive(Debug, Clone)]
167pub enum SubgraphCommand {
168    CreateSubgraph(Uuid, SubgraphRequest, Sender<String>),
169    ObserveSubgraph(Receiver<SchemaDataSourcingEvent>),
170    Shutdown,
171}
172
173#[derive(Debug)]
174pub enum SimnetEvent {
175    Ready,
176    Connected(String),
177    Aborted(String),
178    Shutdown,
179    ClockUpdate(Clock),
180    EpochInfoUpdate(EpochInfo),
181    BlockHashExpired,
182    InfoLog(DateTime<Local>, String),
183    ErrorLog(DateTime<Local>, String),
184    WarnLog(DateTime<Local>, String),
185    DebugLog(DateTime<Local>, String),
186    PluginLoaded(String),
187    TransactionReceived(DateTime<Local>, VersionedTransaction),
188    TransactionProcessed(
189        DateTime<Local>,
190        TransactionMetadata,
191        Option<TransactionError>,
192    ),
193    AccountUpdate(DateTime<Local>, Pubkey),
194    TaggedProfile {
195        result: ProfileResult,
196        tag: String,
197        timestamp: DateTime<Local>,
198    },
199}
200
201impl SimnetEvent {
202    pub fn info<S>(msg: S) -> Self
203    where
204        S: Into<String>,
205    {
206        Self::InfoLog(Local::now(), msg.into())
207    }
208
209    pub fn warn<S>(msg: S) -> Self
210    where
211        S: Into<String>,
212    {
213        Self::WarnLog(Local::now(), msg.into())
214    }
215
216    pub fn error<S>(msg: S) -> Self
217    where
218        S: Into<String>,
219    {
220        Self::ErrorLog(Local::now(), msg.into())
221    }
222
223    pub fn debug<S>(msg: S) -> Self
224    where
225        S: Into<String>,
226    {
227        Self::DebugLog(Local::now(), msg.into())
228    }
229
230    pub fn transaction_processed(meta: TransactionMetadata, err: Option<TransactionError>) -> Self {
231        Self::TransactionProcessed(Local::now(), meta, err)
232    }
233
234    pub fn transaction_received(tx: VersionedTransaction) -> Self {
235        Self::TransactionReceived(Local::now(), tx)
236    }
237
238    pub fn account_update(pubkey: Pubkey) -> Self {
239        Self::AccountUpdate(Local::now(), pubkey)
240    }
241
242    pub fn tagged_profile(result: ProfileResult, tag: String) -> Self {
243        Self::TaggedProfile {
244            result,
245            tag,
246            timestamp: Local::now(),
247        }
248    }
249
250    pub fn account_update_msg(&self) -> String {
251        match self {
252            SimnetEvent::AccountUpdate(_, pubkey) => {
253                format!("Account {} updated.", pubkey)
254            }
255            _ => unreachable!("This function should only be called for AccountUpdate events"),
256        }
257    }
258
259    pub fn epoch_info_update_msg(&self) -> String {
260        match self {
261            SimnetEvent::EpochInfoUpdate(epoch_info) => {
262                format!(
263                    "Connection established. Epoch {} / Slot index {} / Slot {}.",
264                    epoch_info.epoch, epoch_info.slot_index, epoch_info.absolute_slot
265                )
266            }
267            _ => unreachable!("This function should only be called for EpochInfoUpdate events"),
268        }
269    }
270
271    pub fn plugin_loaded_msg(&self) -> String {
272        match self {
273            SimnetEvent::PluginLoaded(plugin_name) => {
274                format!("Plugin {} successfully loaded.", plugin_name)
275            }
276            _ => unreachable!("This function should only be called for PluginLoaded events"),
277        }
278    }
279
280    pub fn clock_update_msg(&self) -> String {
281        match self {
282            SimnetEvent::ClockUpdate(clock) => {
283                format!("Clock ticking (epoch {}, slot {})", clock.epoch, clock.slot)
284            }
285            _ => unreachable!("This function should only be called for ClockUpdate events"),
286        }
287    }
288}
289
290#[derive(Debug)]
291pub enum TransactionStatusEvent {
292    Success(TransactionConfirmationStatus),
293    SimulationFailure((TransactionError, TransactionMetadata)),
294    ExecutionFailure((TransactionError, TransactionMetadata)),
295    VerificationFailure(String),
296}
297
298#[derive(Debug)]
299pub enum SimnetCommand {
300    SlotForward(Option<Hash>),
301    SlotBackward(Option<Hash>),
302    UpdateClock(ClockCommand),
303    UpdateBlockProductionMode(BlockProductionMode),
304    TransactionReceived(
305        Option<Hash>,
306        VersionedTransaction,
307        Sender<TransactionStatusEvent>,
308        bool,
309    ),
310    Terminate(Option<Hash>),
311}
312
313#[derive(Debug)]
314pub enum ClockCommand {
315    Pause,
316    Resume,
317    Toggle,
318    UpdateSlotInterval(u64),
319}
320
321pub enum ClockEvent {
322    Tick,
323    ExpireBlockHash,
324}
325
326#[derive(Clone, Debug, Default)]
327pub struct SurfpoolConfig {
328    pub simnets: Vec<SimnetConfig>,
329    pub rpc: RpcConfig,
330    pub subgraph: SubgraphConfig,
331    pub plugin_config_path: Vec<PathBuf>,
332}
333
334#[derive(Clone, Debug)]
335pub struct SimnetConfig {
336    pub remote_rpc_url: String,
337    pub slot_time: u64,
338    pub block_production_mode: BlockProductionMode,
339    pub airdrop_addresses: Vec<Pubkey>,
340    pub airdrop_token_amount: u64,
341    pub expiry: Option<u64>,
342}
343
344impl Default for SimnetConfig {
345    fn default() -> Self {
346        Self {
347            remote_rpc_url: DEFAULT_RPC_URL.to_string(),
348            slot_time: 0,
349            block_production_mode: BlockProductionMode::Clock,
350            airdrop_addresses: vec![],
351            airdrop_token_amount: 0,
352            expiry: None,
353        }
354    }
355}
356
357#[derive(Clone, Debug, Default)]
358pub struct SubgraphConfig {}
359
360#[derive(Clone, Debug)]
361pub struct RpcConfig {
362    pub bind_host: String,
363    pub bind_port: u16,
364    pub ws_port: u16,
365}
366
367impl RpcConfig {
368    pub fn get_socket_address(&self) -> String {
369        format!("{}:{}", self.bind_host, self.bind_port)
370    }
371    pub fn get_ws_address(&self) -> String {
372        format!("{}:{}", self.bind_host, self.ws_port)
373    }
374}
375
376#[derive(Debug, Clone, Deserialize, Serialize)]
377pub struct SubgraphPluginConfig {
378    pub uuid: Uuid,
379    pub ipc_token: String,
380    pub subgraph_request: SubgraphRequest,
381}
382
383impl Default for RpcConfig {
384    fn default() -> Self {
385        Self {
386            bind_host: "127.0.0.1".to_string(),
387            bind_port: 8899,
388            ws_port: 8900,
389        }
390    }
391}
392
393#[derive(Serialize, Deserialize, Clone, Debug)]
394pub struct SvmSimnetInitializationRequest {
395    pub domain: String,
396    pub block_production_mode: BlockProductionMode,
397    pub datasource_rpc_url: String,
398}
399
400#[derive(Serialize, Deserialize, Clone, Debug)]
401pub enum SvmSimnetCommand {
402    Init(SvmSimnetInitializationRequest),
403}
404
405#[derive(Serialize, Deserialize)]
406pub struct CreateNetworkRequest {
407    pub workspace_id: Uuid,
408    pub name: String,
409    pub description: Option<String>,
410    pub datasource_rpc_url: String,
411    pub block_production_mode: BlockProductionMode,
412}
413
414impl CreateNetworkRequest {
415    pub fn new(
416        workspace_id: Uuid,
417        name: String,
418        description: Option<String>,
419        datasource_rpc_url: String,
420        block_production_mode: BlockProductionMode,
421    ) -> Self {
422        Self {
423            workspace_id,
424            name,
425            description,
426            datasource_rpc_url,
427            block_production_mode,
428        }
429    }
430}
431
432#[derive(Serialize, Deserialize)]
433pub struct CreateNetworkResponse {
434    pub rpc_url: String,
435}
436
437
438#[derive(Serialize, Deserialize)]
439pub struct DeleteNetworkRequest {
440    pub workspace_id: Uuid,
441    pub network_id: Uuid,
442}
443
444impl DeleteNetworkRequest {
445    pub fn new(
446        workspace_id: Uuid,
447        network_id: Uuid,
448    ) -> Self {
449        Self {
450            workspace_id,
451            network_id,
452        }
453    }
454}
455
456#[derive(Serialize, Deserialize)]
457pub struct DeleteNetworkResponse;
458
459
460#[serde_as]
461#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
462#[serde(rename_all = "camelCase")]
463pub struct AccountUpdate {
464    /// providing this value sets the lamports in the account
465    pub lamports: Option<u64>,
466    /// providing this value sets the data held in this account
467    #[serde_as(as = "Option<BytesOrString>")]
468    pub data: Option<Vec<u8>>,
469    ///  providing this value sets the program that owns this account. If executable, the program that loads this account.
470    pub owner: Option<String>,
471    /// providing this value sets whether this account's data contains a loaded program (and is now read-only)
472    pub executable: Option<bool>,
473    /// providing this value sets the epoch at which this account will next owe rent
474    pub rent_epoch: Option<Epoch>,
475}
476
477#[derive(Debug, Clone)]
478pub enum SetSomeAccount {
479    Account(String),
480    NoAccount,
481}
482
483impl<'de> Deserialize<'de> for SetSomeAccount {
484    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
485    where
486        D: Deserializer<'de>,
487    {
488        struct SetSomeAccountVisitor;
489
490        impl<'de> Visitor<'de> for SetSomeAccountVisitor {
491            type Value = SetSomeAccount;
492
493            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
494                formatter.write_str("a Pubkey String or the String 'null'")
495            }
496
497            fn visit_some<D_>(self, deserializer: D_) -> std::result::Result<Self::Value, D_::Error>
498            where
499                D_: Deserializer<'de>,
500            {
501                Deserialize::deserialize(deserializer).map(|v: String| match v.as_str() {
502                    "null" => SetSomeAccount::NoAccount,
503                    _ => SetSomeAccount::Account(v.to_string()),
504                })
505            }
506        }
507
508        deserializer.deserialize_option(SetSomeAccountVisitor)
509    }
510}
511
512impl Serialize for SetSomeAccount {
513    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
514    where
515        S: Serializer,
516    {
517        match self {
518            SetSomeAccount::Account(val) => serializer.serialize_str(val),
519            SetSomeAccount::NoAccount => serializer.serialize_str("null"),
520        }
521    }
522}
523
524#[serde_as]
525#[derive(Debug, Clone, Default, Serialize, Deserialize)]
526#[serde(rename_all = "camelCase")]
527pub struct TokenAccountUpdate {
528    /// providing this value sets the amount of the token in the account data
529    pub amount: Option<u64>,
530    /// providing this value sets the delegate of the token account
531    pub delegate: Option<SetSomeAccount>,
532    /// providing this value sets the state of the token account
533    pub state: Option<String>,
534    /// providing this value sets the amount authorized to the delegate
535    pub delegated_amount: Option<u64>,
536    /// providing this value sets the close authority of the token account
537    pub close_authority: Option<SetSomeAccount>,
538}
539
540// token supply update for set supply method in SVM tricks
541#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
542pub struct SupplyUpdate {
543    pub total: Option<u64>,
544    pub circulating: Option<u64>,
545    pub non_circulating: Option<u64>,
546    pub non_circulating_accounts: Option<Vec<String>>,
547}