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, ScheduledAction,
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)]
55 pub reroute_order_flow: bool,
56
57 #[builder(default)]
59 pub parallel: bool,
60
61 pub capacity_wait_timeout_secs: Option<u16>,
62
63 pub disconnect_timeout_secs: Option<u16>,
64
65 pub extra_compute_units: Option<u32>,
67
68 #[builder(default)]
69 pub agents: Vec<AgentParams>,
70
71 #[builder(default)]
76 pub discoveries: Vec<DiscoveryFilter>,
77
78 #[builder(default)]
81 pub actions: Vec<ScheduledAction>,
82}
83
84impl CreateSession {
85 pub fn add_signer_filter(mut self, address: Address) -> Self {
87 self.signer_filter.insert(address);
88 self
89 }
90
91 pub fn into_params(self) -> Result<CreateSessionParams, BacktestClientError> {
93 let end_slot = match (self.end_slot, self.slot_count) {
94 (Some(_), Some(_)) => {
95 return Err(BacktestClientError::InvalidParams {
96 message: "CreateSession: set only one of end_slot or slot_count".to_string(),
97 });
98 }
99 (Some(end_slot), None) => end_slot,
100 (None, Some(slot_count)) => {
101 self.start_slot.checked_add(slot_count).ok_or_else(|| {
102 BacktestClientError::InvalidParams {
103 message: "CreateSession: start_slot + slot_count overflow".to_string(),
104 }
105 })?
106 }
107 (None, None) => {
108 return Err(BacktestClientError::InvalidParams {
109 message: "CreateSession: must set end_slot or slot_count".to_string(),
110 });
111 }
112 };
113
114 if end_slot < self.start_slot {
115 return Err(BacktestClientError::InvalidParams {
116 message: format!(
117 "CreateSession: end_slot ({end_slot}) must be >= start_slot ({})",
118 self.start_slot
119 ),
120 });
121 }
122
123 Ok(CreateSessionParams::builder()
124 .start_slot(self.start_slot)
125 .end_slot(end_slot)
126 .signer_filter(self.signer_filter)
127 .send_summary(self.send_summary)
128 .reroute_order_flow(self.reroute_order_flow)
129 .maybe_capacity_wait_timeout_secs(self.capacity_wait_timeout_secs)
130 .maybe_disconnect_timeout_secs(self.disconnect_timeout_secs)
131 .maybe_extra_compute_units(self.extra_compute_units)
132 .agents(self.agents)
133 .discoveries(self.discoveries)
134 .actions(self.actions)
135 .build())
136 }
137
138 pub fn into_request(self) -> Result<CreateBacktestSessionRequest, BacktestClientError> {
140 let parallel = self.parallel;
141 let request = self.into_params()?;
142 if parallel {
143 Ok(CreateBacktestSessionRequestV1 { request, parallel }.into())
144 } else {
145 Ok(request.into())
146 }
147 }
148}
149
150#[derive(Debug, Builder)]
154pub struct Continue {
155 #[builder(default = ContinueParams::default_advance_count())]
156 pub advance_count: u64,
157
158 #[builder(default)]
159 pub transactions: Vec<String>,
160
161 #[builder(default)]
162 pub modify_accounts: BTreeMap<Address, AccountData>,
163}
164
165impl Continue {
166 pub fn push_transaction_base64(mut self, data: impl Into<String>) -> Self {
168 self.transactions.push(data.into());
169 self
170 }
171
172 pub fn push_transaction_bytes(mut self, bytes: &[u8]) -> Self {
174 self.transactions.push(BinaryEncoding::Base64.encode(bytes));
175 self
176 }
177
178 pub fn push_transaction(
180 mut self,
181 transaction: &impl SerializableTransaction,
182 ) -> Result<Self, SerializeEncodeError> {
183 self.transactions.push(serialize_to_base64(&transaction)?);
184 Ok(self)
185 }
186
187 pub fn modify_account(mut self, address: Address, account: AccountData) -> Self {
189 self.modify_accounts.insert(address, account);
190 self
191 }
192
193 pub fn into_params(self) -> ContinueParams {
195 ContinueParams {
196 advance_count: self.advance_count,
197 transactions: self.transactions,
198 modify_account_states: AccountModifications(self.modify_accounts),
199 }
200 }
201}