Skip to main content

gmsol_sdk/simulation/
deposit.rs

1use gmsol_model::{
2    action::{deposit::DepositReport, swap::SwapReport},
3    LiquidityMarketMutExt, MarketAction,
4};
5use gmsol_programs::gmsol_store::types::CreateDepositParams;
6use solana_sdk::pubkey::Pubkey;
7use typed_builder::TypedBuilder;
8
9use super::{SimulationOptions, Simulator};
10
11/// Deposit simulation output.
12#[derive(Debug)]
13pub struct DepositSimulationOutput {
14    pub(crate) long_swaps: Vec<SwapReport<u128, i128>>,
15    pub(crate) short_swaps: Vec<SwapReport<u128, i128>>,
16    pub(crate) report: Box<DepositReport<u128, i128>>,
17}
18
19impl DepositSimulationOutput {
20    /// Returns long swap reports.
21    pub fn long_swaps(&self) -> &[SwapReport<u128, i128>] {
22        &self.long_swaps
23    }
24
25    /// Returns short swap reports.
26    pub fn short_swaps(&self) -> &[SwapReport<u128, i128>] {
27        &self.short_swaps
28    }
29
30    /// Returns deposit report.
31    pub fn report(&self) -> &DepositReport<u128, i128> {
32        &self.report
33    }
34}
35
36/// Deposit execution simulation.
37#[derive(Debug, TypedBuilder)]
38pub struct DepositSimulation<'a> {
39    simulator: &'a mut Simulator,
40    params: &'a CreateDepositParams,
41    market_token: &'a Pubkey,
42    #[builder(default)]
43    long_pay_token: Option<&'a Pubkey>,
44    #[builder(default)]
45    long_swap_path: &'a [Pubkey],
46    #[builder(default)]
47    short_pay_token: Option<&'a Pubkey>,
48    #[builder(default)]
49    short_swap_path: &'a [Pubkey],
50}
51
52impl DepositSimulation<'_> {
53    /// Execute with options.
54    pub fn execute_with_options(
55        self,
56        options: SimulationOptions,
57    ) -> crate::Result<DepositSimulationOutput> {
58        let Self {
59            simulator,
60            params,
61            market_token,
62            long_pay_token,
63            long_swap_path,
64            short_pay_token,
65            short_swap_path,
66        } = self;
67
68        if params.initial_long_token_amount == 0 && params.initial_short_token_amount == 0 {
69            return Err(crate::Error::custom("[sim] empty deposit"));
70        }
71
72        let (prices, meta) = simulator.get_prices_and_meta_for_market(market_token)?;
73
74        // Execute swaps.
75        let long_token = meta.long_token_mint;
76        let short_token = meta.short_token_mint;
77        let long_pay_token = long_pay_token.copied().unwrap_or(long_token);
78        let short_pay_token = short_pay_token.copied().unwrap_or(short_token);
79
80        let long_swap_output = simulator.swap_along_path_with_options(
81            long_swap_path,
82            &long_pay_token,
83            params.initial_long_token_amount.into(),
84            options.clone(),
85        )?;
86        if long_swap_output.output_token != long_token {
87            return Err(crate::Error::custom("[sim] invalid long swap path"));
88        }
89
90        let short_swap_output = simulator.swap_along_path_with_options(
91            short_swap_path,
92            &short_pay_token,
93            params.initial_short_token_amount.into(),
94            options.clone(),
95        )?;
96        if short_swap_output.output_token != short_token {
97            return Err(crate::Error::custom("[sim] invalid short swap path"));
98        }
99
100        // Execute deposit.
101        let report = if options.disable_vis {
102            let market = simulator
103                .get_market_mut(market_token)
104                .expect("market storage must exist");
105            market.with_vis_disabled(|market| {
106                market
107                    .deposit(long_swap_output.amount, short_swap_output.amount, prices)?
108                    .execute()
109            })?
110        } else {
111            let (market, vi_map) = simulator.get_market_and_vis_mut(market_token)?;
112            market.with_vi_models(vi_map, |market| {
113                market
114                    .deposit(long_swap_output.amount, short_swap_output.amount, prices)?
115                    .execute()
116            })?
117        };
118
119        let minted = report.minted();
120        let min_market_token_amount = u128::from(params.min_market_token_amount);
121        if *minted < min_market_token_amount {
122            return Err(crate::Error::custom(format!(
123                "[sim] insufficient output amount: {minted} < {min_market_token_amount}",
124            )));
125        }
126
127        Ok(DepositSimulationOutput {
128            long_swaps: long_swap_output.reports,
129            short_swaps: short_swap_output.reports,
130            report: Box::new(report),
131        })
132    }
133}