1use std::{
2 collections::{BTreeMap, HashMap},
3 sync::Arc,
4};
5
6use gmsol_model::{
7 action::swap::SwapReport,
8 price::{Price, Prices},
9 MarketAction, SwapMarketMutExt,
10};
11use gmsol_programs::{
12 gmsol_store::types::{
13 CreateDepositParams, CreateGlvDepositParams, CreateGlvWithdrawalParams, CreateShiftParams,
14 CreateWithdrawalParams, MarketMeta,
15 },
16 model::{MarketModel, VirtualInventoryModel},
17};
18use solana_sdk::pubkey::Pubkey;
19
20use crate::{
21 builders::order::{CreateOrderKind, CreateOrderParams},
22 glv::{calculator::GlvCalculator, model::GlvModel},
23 market::caluclator::MarketCalculator,
24 simulation::order::OrderSimulation,
25};
26
27use super::{
28 deposit::{DepositSimulation, DepositSimulationBuilder},
29 glv_deposit::{GlvDepositSimulation, GlvDepositSimulationBuilder},
30 glv_withdrawal::{GlvWithdrawalSimulation, GlvWithdrawalSimulationBuilder},
31 order::OrderSimulationBuilder,
32 shift::{ShiftSimulation, ShiftSimulationBuilder},
33 withdrawal::{WithdrawalSimulation, WithdrawalSimulationBuilder},
34};
35
36pub type OrderSimulationBuilderForSimulator<'a> = OrderSimulationBuilder<
38 'a,
39 (
40 (&'a mut Simulator,),
41 (CreateOrderKind,),
42 (&'a CreateOrderParams,),
43 (&'a Pubkey,),
44 (),
45 (),
46 (),
47 (),
48 ),
49>;
50
51pub type DepositSimulationBuilderForSimulator<'a> = DepositSimulationBuilder<
53 'a,
54 (
55 (&'a mut Simulator,),
56 (&'a CreateDepositParams,),
57 (&'a Pubkey,),
58 (),
59 (),
60 (),
61 (),
62 ),
63>;
64
65pub type WithdrawalSimulationBuilderForSimulator<'a> = WithdrawalSimulationBuilder<
67 'a,
68 (
69 (&'a mut Simulator,),
70 (&'a CreateWithdrawalParams,),
71 (&'a Pubkey,),
72 (),
73 (),
74 (),
75 (),
76 ),
77>;
78
79pub type ShiftSimulationBuilderForSimulator<'a> = ShiftSimulationBuilder<
81 'a,
82 (
83 (&'a mut Simulator,),
84 (&'a CreateShiftParams,),
85 (&'a Pubkey,),
86 (&'a Pubkey,),
87 ),
88>;
89
90pub type GlvDepositSimulationBuilderForSimulator<'a> = GlvDepositSimulationBuilder<
92 'a,
93 (
94 (&'a mut Simulator,),
95 (&'a CreateGlvDepositParams,),
96 (&'a Pubkey,),
97 (&'a Pubkey,),
98 (),
99 (),
100 (),
101 (),
102 ),
103>;
104
105pub type GlvWithdrawalSimulationBuilderForSimulator<'a> = GlvWithdrawalSimulationBuilder<
107 'a,
108 (
109 (&'a mut Simulator,),
110 (&'a CreateGlvWithdrawalParams,),
111 (&'a Pubkey,),
112 (&'a Pubkey,),
113 (),
114 (),
115 (),
116 (),
117 ),
118>;
119
120pub type PriceState = Option<Arc<Price<u128>>>;
122
123#[derive(Debug, Clone)]
125pub struct Simulator {
126 tokens: HashMap<Pubkey, TokenState>,
127 markets: HashMap<Pubkey, MarketModel>,
128 glvs: HashMap<Pubkey, GlvModel>,
129 vis: BTreeMap<Pubkey, VirtualInventoryModel>,
130}
131
132impl Simulator {
133 pub fn from_parts(
135 tokens: HashMap<Pubkey, TokenState>,
136 markets: HashMap<Pubkey, MarketModel>,
137 glvs: HashMap<Pubkey, GlvModel>,
138 vis: BTreeMap<Pubkey, VirtualInventoryModel>,
139 ) -> Self {
140 Self {
141 tokens,
142 markets,
143 glvs,
144 vis,
145 }
146 }
147
148 pub fn get_market(&self, market_token: &Pubkey) -> Option<&MarketModel> {
150 self.markets.get(market_token)
151 }
152
153 pub fn get_market_mut(&mut self, market_token: &Pubkey) -> Option<&mut MarketModel> {
155 self.markets.get_mut(market_token)
156 }
157
158 pub fn get_price(&self, token: &Pubkey) -> Option<Price<u128>> {
160 Some(*self.tokens.get(token)?.price.as_deref()?)
161 }
162
163 pub fn insert_price(
168 &mut self,
169 token: &Pubkey,
170 price: Arc<Price<u128>>,
171 ) -> crate::Result<&mut Self> {
172 let state = self.tokens.get_mut(token).ok_or_else(|| {
173 crate::Error::custom(format!(
174 "[sim] token `{token}` is not found in the simulator"
175 ))
176 })?;
177 state.price = Some(price);
178 Ok(self)
179 }
180
181 pub fn get_prices(&self, meta: &MarketMeta) -> Option<Prices<u128>> {
183 let index_token_price = self.get_price(&meta.index_token_mint)?;
184 let long_token_price = self.get_price(&meta.long_token_mint)?;
185 let short_token_price = self.get_price(&meta.short_token_mint)?;
186 Some(Prices {
187 index_token_price,
188 long_token_price,
189 short_token_price,
190 })
191 }
192
193 pub(crate) fn get_prices_and_meta_for_market(
194 &self,
195 market_token: &Pubkey,
196 ) -> crate::Result<(Prices<u128>, &MarketMeta)> {
197 let market = self.markets.get(market_token).ok_or_else(|| {
198 crate::Error::custom(format!(
199 "[sim] market `{market_token}` not found in the simulator"
200 ))
201 })?;
202 let meta = &market.meta;
203 let prices = self.get_prices(meta).ok_or_else(|| {
204 crate::Error::custom(format!(
205 "[sim] prices for market `{market_token}` are not ready in the simulator"
206 ))
207 })?;
208 Ok((prices, meta))
209 }
210
211 pub(crate) fn get_prices_for_market(
212 &self,
213 market_token: &Pubkey,
214 ) -> crate::Result<Prices<u128>> {
215 Ok(self.get_prices_and_meta_for_market(market_token)?.0)
216 }
217
218 pub(crate) fn get_market_with_prices(
219 &self,
220 market_token: &Pubkey,
221 ) -> crate::Result<(&MarketModel, Prices<u128>)> {
222 let prices = self.get_prices_for_market(market_token)?;
223 let market = self.get_market(market_token).ok_or_else(|| {
224 crate::Error::custom(format!(
225 "[sim] market `{market_token}` not found in the simulator"
226 ))
227 })?;
228 Ok((market, prices))
229 }
230
231 pub(crate) fn get_market_and_vis_mut(
236 &mut self,
237 market_token: &Pubkey,
238 ) -> crate::Result<(
239 &mut MarketModel,
240 &mut BTreeMap<Pubkey, VirtualInventoryModel>,
241 )> {
242 let Simulator {
243 tokens: _,
244 markets,
245 glvs: _,
246 vis,
247 } = self;
248
249 let market = markets.get_mut(market_token).ok_or_else(|| {
250 crate::Error::custom(format!(
251 "[sim] market `{market_token}` not found in the simulator"
252 ))
253 })?;
254
255 Ok((market, vis))
256 }
257
258 pub fn get_glv(&self, glv_token: &Pubkey) -> Option<&GlvModel> {
260 self.glvs.get(glv_token)
261 }
262
263 pub fn get_glv_mut(&mut self, glv_token: &Pubkey) -> Option<&mut GlvModel> {
265 self.glvs.get_mut(glv_token)
266 }
267
268 pub fn insert_glv(&mut self, glv: GlvModel) -> Option<GlvModel> {
270 self.glvs.insert(glv.glv_token, glv)
271 }
272
273 pub(crate) fn vis_mut(&mut self) -> &mut BTreeMap<Pubkey, VirtualInventoryModel> {
279 &mut self.vis
280 }
281
282 pub fn swap_along_path(
290 &mut self,
291 path: &[Pubkey],
292 source_token: &Pubkey,
293 amount: u128,
294 options: Option<SimulationOptions>,
295 ) -> crate::Result<SwapOutput> {
296 self.swap_along_path_with_options(path, source_token, amount, options.unwrap_or_default())
297 }
298
299 pub(crate) fn swap_along_path_with_options(
301 &mut self,
302 path: &[Pubkey],
303 source_token: &Pubkey,
304 mut amount: u128,
305 options: SimulationOptions,
306 ) -> crate::Result<SwapOutput> {
307 let mut current_token = *source_token;
308
309 let mut reports = Vec::with_capacity(path.len());
310 for market_token in path {
311 let prices = self.get_prices_for_market(market_token)?;
313
314 let (market, maybe_vi_map) = if options.disable_vis {
316 (
317 self.get_market_mut(market_token).ok_or_else(|| {
318 crate::Error::custom(format!(
319 "[sim] market `{market_token}` not found in the simulator"
320 ))
321 })?,
322 None,
323 )
324 } else {
325 let (market, vi_map) = self.get_market_and_vis_mut(market_token)?;
326 (market, Some(vi_map))
327 };
328
329 let meta = &market.meta;
330 if meta.long_token_mint == meta.short_token_mint {
331 return Err(crate::Error::custom(format!(
332 "[swap] `{market_token}` is not a swappable market"
333 )));
334 }
335 let is_token_in_long = if meta.long_token_mint == current_token {
336 current_token = meta.short_token_mint;
337 true
338 } else if meta.short_token_mint == current_token {
339 current_token = meta.long_token_mint;
340 false
341 } else {
342 return Err(crate::Error::custom(format!(
343 "[swap] invalid swap step. Current step: {market_token}"
344 )));
345 };
346 let report = match maybe_vi_map {
347 None => market.with_vis_disabled(|market| {
348 market.swap(is_token_in_long, amount, prices)?.execute()
349 })?,
350 Some(vi_map) => market.with_vi_models(vi_map, |market| {
351 market.swap(is_token_in_long, amount, prices)?.execute()
352 })?,
353 };
354 amount = *report.token_out_amount();
355 reports.push(report);
356 }
357
358 Ok(SwapOutput {
359 output_token: current_token,
360 amount,
361 reports,
362 })
363 }
364
365 pub fn tokens(&self) -> impl Iterator<Item = (&Pubkey, &TokenState)> {
367 self.tokens.iter()
368 }
369
370 pub fn markets(&self) -> impl Iterator<Item = (&Pubkey, &MarketModel)> {
372 self.markets.iter()
373 }
374
375 pub fn glvs(&self) -> impl Iterator<Item = (&Pubkey, &GlvModel)> {
377 self.glvs.iter()
378 }
379
380 pub fn insert_vi(
382 &mut self,
383 vi_address: Pubkey,
384 vi: VirtualInventoryModel,
385 ) -> Option<VirtualInventoryModel> {
386 self.vis.insert(vi_address, vi)
387 }
388
389 pub fn get_vi(&self, vi_address: &Pubkey) -> Option<&VirtualInventoryModel> {
391 self.vis.get(vi_address)
392 }
393
394 pub fn vis(&self) -> impl Iterator<Item = (&Pubkey, &VirtualInventoryModel)> {
396 self.vis.iter()
397 }
398
399 pub fn simulate_order<'a>(
401 &'a mut self,
402 kind: CreateOrderKind,
403 params: &'a CreateOrderParams,
404 collateral_or_swap_out_token: &'a Pubkey,
405 ) -> OrderSimulationBuilderForSimulator<'a> {
406 OrderSimulation::builder()
407 .simulator(self)
408 .kind(kind)
409 .params(params)
410 .collateral_or_swap_out_token(collateral_or_swap_out_token)
411 }
412
413 pub fn simulate_deposit<'a>(
415 &'a mut self,
416 market_token: &'a Pubkey,
417 params: &'a CreateDepositParams,
418 ) -> DepositSimulationBuilderForSimulator<'a> {
419 DepositSimulation::builder()
420 .simulator(self)
421 .market_token(market_token)
422 .params(params)
423 }
424
425 pub fn simulate_withdrawal<'a>(
427 &'a mut self,
428 market_token: &'a Pubkey,
429 params: &'a CreateWithdrawalParams,
430 ) -> WithdrawalSimulationBuilderForSimulator<'a> {
431 WithdrawalSimulation::builder()
432 .simulator(self)
433 .market_token(market_token)
434 .params(params)
435 }
436
437 pub fn simulate_glv_deposit<'a>(
439 &'a mut self,
440 glv_token: &'a Pubkey,
441 market_token: &'a Pubkey,
442 params: &'a CreateGlvDepositParams,
443 ) -> GlvDepositSimulationBuilderForSimulator<'a> {
444 GlvDepositSimulation::builder()
445 .simulator(self)
446 .glv_token(glv_token)
447 .market_token(market_token)
448 .params(params)
449 }
450
451 pub fn simulate_glv_withdrawal<'a>(
453 &'a mut self,
454 glv_token: &'a Pubkey,
455 market_token: &'a Pubkey,
456 params: &'a CreateGlvWithdrawalParams,
457 ) -> GlvWithdrawalSimulationBuilderForSimulator<'a> {
458 GlvWithdrawalSimulation::builder()
459 .simulator(self)
460 .glv_token(glv_token)
461 .market_token(market_token)
462 .params(params)
463 }
464
465 pub fn simulate_shift<'a>(
467 &'a mut self,
468 from_market_token: &'a Pubkey,
469 to_market_token: &'a Pubkey,
470 params: &'a CreateShiftParams,
471 ) -> ShiftSimulationBuilderForSimulator<'a> {
472 ShiftSimulation::builder()
473 .simulator(self)
474 .from_market_token(from_market_token)
475 .to_market_token(to_market_token)
476 .params(params)
477 }
478}
479
480#[derive(Debug, Default, Clone)]
482pub struct SimulationOptions {
483 pub skip_limit_price_validation: bool,
485 pub disable_vis: bool,
487}
488
489#[derive(Debug, Clone)]
491pub struct TokenState {
492 price: PriceState,
493}
494
495impl TokenState {
496 pub fn from_price(price: PriceState) -> Self {
498 Self { price }
499 }
500
501 pub fn price(&self) -> &PriceState {
503 &self.price
504 }
505}
506
507#[derive(Debug, Clone)]
509pub struct SwapOutput {
510 pub(crate) output_token: Pubkey,
511 pub(crate) amount: u128,
512 pub(crate) reports: Vec<SwapReport<u128, i128>>,
513}
514
515impl SwapOutput {
516 pub fn output_token(&self) -> &Pubkey {
518 &self.output_token
519 }
520
521 pub fn amount(&self) -> u128 {
523 self.amount
524 }
525
526 pub fn reports(&self) -> &[SwapReport<u128, i128>] {
528 &self.reports
529 }
530}
531
532impl MarketCalculator for Simulator {
533 fn get_market_model(&self, market_token: &Pubkey) -> Option<&MarketModel> {
534 self.get_market(market_token)
535 }
536
537 fn get_token_price(&self, token: &Pubkey) -> Option<Price<u128>> {
538 self.get_price(token)
539 }
540}
541
542impl GlvCalculator for Simulator {
543 fn get_glv_model(&self, glv_token: &Pubkey) -> Option<&GlvModel> {
544 self.get_glv(glv_token)
545 }
546}