simulator_client/
builders.rs1use std::collections::{BTreeMap, BTreeSet};
2
3use base64::{Engine as _, engine::general_purpose::STANDARD as BASE64};
4use bon::Builder;
5use simulator_api::{
6 AccountData, AccountModifications, AgentParams, BinaryEncoding, ContinueParams,
7 CreateBacktestSessionRequest, CreateBacktestSessionRequestV1, CreateSessionParams,
8 DiscoveryFilter,
9};
10use solana_address::Address;
11use solana_client::rpc_client::SerializableTransaction;
12use thiserror::Error;
13
14use crate::BacktestClientError;
15
16#[derive(Debug, Error)]
18pub enum SerializeEncodeError {
19 #[error("bincode serialization error: {0}")]
20 Bincode(#[from] bincode::error::EncodeError),
21}
22
23fn serialize_to_base64(value: &impl serde::Serialize) -> Result<String, SerializeEncodeError> {
24 let bytes = bincode::serde::encode_to_vec(
25 value,
26 bincode::config::standard()
27 .with_fixed_int_encoding()
28 .with_little_endian(),
29 )?;
30 Ok(BASE64.encode(&bytes))
31}
32
33#[derive(Debug, Clone, Builder)]
38pub struct CreateSession {
39 pub start_slot: u64,
40
41 pub end_slot: Option<u64>,
42
43 pub slot_count: Option<u64>,
44
45 #[builder(default)]
46 pub signer_filter: BTreeSet<Address>,
47
48 #[builder(default)]
50 pub send_summary: bool,
51
52 #[builder(default)]
54 pub parallel: bool,
55
56 pub capacity_wait_timeout_secs: Option<u16>,
57
58 pub disconnect_timeout_secs: Option<u16>,
59
60 pub extra_compute_units: Option<u32>,
62
63 #[builder(default)]
64 pub agents: Vec<AgentParams>,
65
66 #[builder(default)]
71 pub discoveries: Vec<DiscoveryFilter>,
72}
73
74impl CreateSession {
75 pub fn add_signer_filter(mut self, address: Address) -> Self {
77 self.signer_filter.insert(address);
78 self
79 }
80
81 pub fn into_params(self) -> Result<CreateSessionParams, BacktestClientError> {
83 let end_slot = match (self.end_slot, self.slot_count) {
84 (Some(_), Some(_)) => {
85 return Err(BacktestClientError::InvalidParams {
86 message: "CreateSession: set only one of end_slot or slot_count".to_string(),
87 });
88 }
89 (Some(end_slot), None) => end_slot,
90 (None, Some(slot_count)) => {
91 self.start_slot.checked_add(slot_count).ok_or_else(|| {
92 BacktestClientError::InvalidParams {
93 message: "CreateSession: start_slot + slot_count overflow".to_string(),
94 }
95 })?
96 }
97 (None, None) => {
98 return Err(BacktestClientError::InvalidParams {
99 message: "CreateSession: must set end_slot or slot_count".to_string(),
100 });
101 }
102 };
103
104 if end_slot < self.start_slot {
105 return Err(BacktestClientError::InvalidParams {
106 message: format!(
107 "CreateSession: end_slot ({end_slot}) must be >= start_slot ({})",
108 self.start_slot
109 ),
110 });
111 }
112
113 Ok(CreateSessionParams {
114 start_slot: self.start_slot,
115 end_slot,
116 signer_filter: self.signer_filter,
117 send_summary: self.send_summary,
118 capacity_wait_timeout_secs: self.capacity_wait_timeout_secs,
119 disconnect_timeout_secs: self.disconnect_timeout_secs,
120 extra_compute_units: self.extra_compute_units,
121 agents: self.agents,
122 discoveries: self.discoveries,
123 })
124 }
125
126 pub fn into_request(self) -> Result<CreateBacktestSessionRequest, BacktestClientError> {
128 let parallel = self.parallel;
129 let request = self.into_params()?;
130 if parallel {
131 Ok(CreateBacktestSessionRequestV1 { request, parallel }.into())
132 } else {
133 Ok(request.into())
134 }
135 }
136}
137
138#[derive(Debug, Builder)]
142pub struct Continue {
143 #[builder(default = ContinueParams::default_advance_count())]
144 pub advance_count: u64,
145
146 #[builder(default)]
147 pub transactions: Vec<String>,
148
149 #[builder(default)]
150 pub modify_accounts: BTreeMap<Address, AccountData>,
151}
152
153impl Continue {
154 pub fn push_transaction_base64(mut self, data: impl Into<String>) -> Self {
156 self.transactions.push(data.into());
157 self
158 }
159
160 pub fn push_transaction_bytes(mut self, bytes: &[u8]) -> Self {
162 self.transactions.push(BinaryEncoding::Base64.encode(bytes));
163 self
164 }
165
166 pub fn push_transaction(
168 mut self,
169 transaction: &impl SerializableTransaction,
170 ) -> Result<Self, SerializeEncodeError> {
171 self.transactions.push(serialize_to_base64(&transaction)?);
172 Ok(self)
173 }
174
175 pub fn modify_account(mut self, address: Address, account: AccountData) -> Self {
177 self.modify_accounts.insert(address, account);
178 self
179 }
180
181 pub fn into_params(self) -> ContinueParams {
183 ContinueParams {
184 advance_count: self.advance_count,
185 transactions: self.transactions,
186 modify_account_states: AccountModifications(self.modify_accounts),
187 }
188 }
189}