gmsol_sdk/js/
market.rs

1use std::sync::Arc;
2
3use gmsol_model::{LiquidityMarketExt, PnlFactorKind};
4use gmsol_programs::{
5    gmsol_store::accounts::Market,
6    model::{MarketModel, PositionOptions},
7};
8use serde::{Deserialize, Serialize};
9use tsify_next::Tsify;
10use wasm_bindgen::prelude::*;
11
12use crate::{
13    js::position::JsPositionModel,
14    market::{MarketCalculations, MarketStatus},
15    serde::StringPubkey,
16    utils::zero_copy::{
17        try_deserialize_zero_copy, try_deserialize_zero_copy_from_base64_with_options,
18    },
19};
20
21use super::price::Prices;
22
23/// Wrapper of [`Market`].
24#[wasm_bindgen(js_name = Market)]
25#[derive(Clone)]
26pub struct JsMarket {
27    market: Arc<Market>,
28}
29
30#[wasm_bindgen(js_class = Market)]
31impl JsMarket {
32    /// Create from base64 encoded account data with options.
33    pub fn decode_from_base64_with_options(
34        data: &str,
35        no_discriminator: Option<bool>,
36    ) -> crate::Result<Self> {
37        let market = try_deserialize_zero_copy_from_base64_with_options(
38            data,
39            no_discriminator.unwrap_or(false),
40        )?;
41
42        Ok(Self {
43            market: Arc::new(market.0),
44        })
45    }
46
47    /// Create from base64 encoded account data.
48    pub fn decode_from_base64(data: &str) -> crate::Result<Self> {
49        Self::decode_from_base64_with_options(data, None)
50    }
51
52    /// Create from account data.
53    pub fn decode(data: &[u8]) -> crate::Result<Self> {
54        let market = try_deserialize_zero_copy(data)?;
55
56        Ok(Self {
57            market: Arc::new(market.0),
58        })
59    }
60
61    /// Convert into [`JsMarketModel`]
62    pub fn to_model(&self, supply: u64) -> JsMarketModel {
63        JsMarketModel {
64            model: MarketModel::from_parts(self.market.clone(), supply),
65        }
66    }
67
68    /// Get market token address.
69    pub fn market_token_address(&self) -> String {
70        self.market.meta.market_token_mint.to_string()
71    }
72
73    /// Get index token address.
74    pub fn index_token_address(&self) -> String {
75        self.market.meta.index_token_mint.to_string()
76    }
77
78    /// Get long token address.
79    pub fn long_token_address(&self) -> String {
80        self.market.meta.long_token_mint.to_string()
81    }
82
83    /// Get short token address.
84    pub fn short_token_address(&self) -> String {
85        self.market.meta.short_token_mint.to_string()
86    }
87
88    /// Create a clone of this market.
89    #[wasm_bindgen(js_name = clone)]
90    pub fn js_clone(&self) -> Self {
91        self.clone()
92    }
93}
94
95/// Params for calculating market token price.
96#[derive(Debug, Serialize, Deserialize, Tsify)]
97#[tsify(into_wasm_abi, from_wasm_abi)]
98pub struct MarketTokenPriceParams {
99    /// Prices.
100    pub prices: Prices,
101    /// Pnl Factor.
102    #[serde(default = "default_pnl_factor")]
103    pub pnl_factor: PnlFactorKind,
104    /// Maximize.
105    pub maximize: bool,
106}
107
108fn default_pnl_factor() -> PnlFactorKind {
109    PnlFactorKind::MaxAfterDeposit
110}
111
112/// Params for calculating market status.
113#[derive(Debug, Serialize, Deserialize, Tsify)]
114#[tsify(into_wasm_abi, from_wasm_abi)]
115pub struct MarketStatusParams {
116    /// Prices.
117    pub prices: Prices,
118}
119
120/// Wrapper of [`MarketModel`].
121#[wasm_bindgen(js_name = MarketModel)]
122#[derive(Clone)]
123pub struct JsMarketModel {
124    pub(super) model: MarketModel,
125}
126
127#[wasm_bindgen(js_class = MarketModel)]
128impl JsMarketModel {
129    /// Get market token price.
130    pub fn market_token_price(&self, params: MarketTokenPriceParams) -> crate::Result<u128> {
131        Ok(self.model.market_token_price(
132            &params.prices.into(),
133            params.pnl_factor,
134            params.maximize,
135        )?)
136    }
137
138    /// Get market status.
139    pub fn status(&self, params: MarketStatusParams) -> crate::Result<MarketStatus> {
140        let prices = params.prices.into();
141        self.model.status(&prices)
142    }
143
144    /// Create an empty position model.
145    pub fn create_empty_position(
146        &self,
147        args: CreateEmptyPositionArgs,
148    ) -> crate::Result<JsPositionModel> {
149        let CreateEmptyPositionArgs {
150            is_long,
151            collateral_token,
152            owner,
153            created_at,
154            generate_bump,
155            store_program_id,
156        } = args;
157
158        let mut options = PositionOptions::default();
159
160        if let Some(owner) = owner {
161            options.owner = Some(*owner);
162        }
163
164        if let Some(created_at) = created_at {
165            options.created_at = created_at;
166        }
167
168        if let Some(generate_bump) = generate_bump {
169            options.generate_bump = generate_bump;
170        }
171
172        if let Some(program_id) = store_program_id {
173            options.store_program_id = *program_id;
174        }
175
176        let position =
177            self.model
178                .clone()
179                .into_empty_position_opts(is_long, *collateral_token, options)?;
180
181        Ok(position.into())
182    }
183
184    /// Create a clone of this market model.
185    #[wasm_bindgen(js_name = clone)]
186    pub fn js_clone(&self) -> Self {
187        self.clone()
188    }
189}
190
191impl From<MarketModel> for JsMarketModel {
192    fn from(model: MarketModel) -> Self {
193        Self { model }
194    }
195}
196
197/// Parameters for creating empty position model.
198#[derive(Debug, Serialize, Deserialize, Tsify)]
199#[tsify(into_wasm_abi, from_wasm_abi)]
200pub struct CreateEmptyPositionArgs {
201    /// Is long side.
202    pub is_long: bool,
203    /// Collateral token.
204    pub collateral_token: StringPubkey,
205    /// The owner of the position.
206    ///
207    /// If set to `None`, the `owner` will use the default pubkey.
208    #[serde(default)]
209    pub owner: Option<StringPubkey>,
210    /// The timestamp of the position creation.
211    #[serde(default)]
212    pub created_at: Option<i64>,
213    /// Whether to generate a bump seed.
214    ///
215    /// If set `false`, the `bump` will be fixed to `0`.
216    #[serde(default)]
217    pub generate_bump: Option<bool>,
218    /// The store program ID used to generate the bump seed.
219    #[serde(default)]
220    pub store_program_id: Option<StringPubkey>,
221}