Skip to main content

gmsol_sdk/glv/
calculator.rs

1use gmsol_model::utils::market_token_amount_to_usd;
2use solana_sdk::pubkey::Pubkey;
3
4use crate::{
5    glv::{GlvModel, GlvStatus},
6    market::{caluclator::MarketCalculator, MarketCalculations, Value},
7};
8
9/// Performs GLV calculations.
10pub trait GlvCalculator: MarketCalculator {
11    /// Returns [`GlvModel`] corresponding to the given GLV token.
12    fn get_glv_model(&self, glv_token: &Pubkey) -> Option<&GlvModel>;
13
14    /// Calcualtes the market token value in GLV.
15    fn get_market_token_value_in_glv(
16        &self,
17        glv_token: &Pubkey,
18        market_token: &Pubkey,
19        maximize: bool,
20    ) -> crate::Result<u128> {
21        Calculator::new(self, glv_token)?.get_market_token_value_in_glv(market_token, maximize)
22    }
23
24    /// Calculates the total value of the given GLV.
25    fn get_glv_value(&self, glv_token: &Pubkey, maximize: bool) -> crate::Result<u128> {
26        Calculator::new(self, glv_token)?.get_glv_value(maximize)
27    }
28
29    /// Calculates the underlying value represented by the given amount of a GLV token.
30    fn get_glv_token_value(
31        &self,
32        glv_token: &Pubkey,
33        amount: u64,
34        maximize: bool,
35    ) -> crate::Result<u128> {
36        Calculator::new(self, glv_token)?.get_glv_token_value(amount, maximize)
37    }
38
39    /// Calculates the maximum sellable value through the given market within the given GLV.
40    fn get_max_sellable_glv_value_for_market_token(
41        &self,
42        glv_token: &Pubkey,
43        market_token: &Pubkey,
44    ) -> crate::Result<u128> {
45        Calculator::new(self, glv_token)?.get_max_sellable_glv_value_for_market_token(market_token)
46    }
47
48    /// Calculates the maximum sellable value for the given GLV.
49    fn get_max_sellable_glv_value(&self, glv_token: &Pubkey) -> crate::Result<u128> {
50        let calculator = Calculator::new(self, glv_token)?;
51
52        let mut value = 0u128;
53        for market_token in calculator.glv.market_tokens() {
54            value = value
55                .checked_add(calculator.get_max_sellable_glv_value_for_market_token(&market_token)?)
56                .ok_or_else(|| crate::Error::custom("max sellable value overflow"))?;
57        }
58
59        Ok(value)
60    }
61
62    /// Calculates the status of the given GLV.
63    fn get_glv_status(&self, glv_token: &Pubkey) -> crate::Result<GlvStatus> {
64        Calculator::new(self, glv_token)?.get_glv_status()
65    }
66}
67
68struct Calculator<'a, C: ?Sized> {
69    glv: &'a GlvModel,
70    context: &'a C,
71}
72
73impl<'a, C: GlvCalculator + ?Sized> Calculator<'a, C> {
74    fn new(context: &'a C, glv_token: &Pubkey) -> crate::Result<Self> {
75        let glv = context.get_glv_model(glv_token).ok_or_else(|| {
76            crate::Error::custom(format!("[sim] GLV for GLV token `{glv_token}` not found"))
77        })?;
78
79        Ok(Self { glv, context })
80    }
81
82    fn get_market_token_value_in_glv(
83        &self,
84        market_token: &Pubkey,
85        maximize: bool,
86    ) -> crate::Result<u128> {
87        let Self { glv, context } = self;
88        let balance = glv
89            .market_config(market_token)
90            .ok_or_else(|| {
91                crate::Error::custom(format!(
92                    "[sim] the given GLV does not include the specified market token: {market_token}"
93                ))
94            })?
95            .balance;
96        let (market, prices) = context.get_market_model_with_prices(market_token)?;
97        let value_for_market =
98            gmsol_model::glv::get_glv_value_for_market(&prices, market, balance.into(), maximize)?
99                .market_token_value_in_glv;
100        Ok(value_for_market)
101    }
102
103    fn get_max_sellable_glv_value_for_market_token(
104        &self,
105        market_token: &Pubkey,
106    ) -> crate::Result<u128> {
107        let value_in_glv = self.get_market_token_value_in_glv(market_token, false)?;
108        let (market, prices) = self
109            .context
110            .get_market_model_with_prices(market_token)
111            .expect("must exist");
112        let max_sellable_value = market.max_sellable_value(&prices)?;
113        Ok(value_in_glv.min(max_sellable_value))
114    }
115
116    fn get_max_sellable_glv_value(&self) -> crate::Result<u128> {
117        let mut value = 0u128;
118        for market_token in self.glv.market_tokens() {
119            value = value
120                .checked_add(self.get_max_sellable_glv_value_for_market_token(&market_token)?)
121                .ok_or_else(|| crate::Error::custom("max sellable value overflow"))?;
122        }
123
124        Ok(value)
125    }
126
127    fn get_glv_value(&self, maximize: bool) -> crate::Result<u128> {
128        let mut value = 0u128;
129
130        for market_token in self.glv.market_tokens() {
131            let value_for_market = self.get_market_token_value_in_glv(&market_token, maximize)?;
132            value = value
133                .checked_add(value_for_market)
134                .ok_or(crate::Error::custom("[sim] GLV value overflow"))?;
135        }
136
137        Ok(value)
138    }
139
140    fn get_glv_token_value(&self, amount: u64, maximize: bool) -> crate::Result<u128> {
141        let glv_value = self.get_glv_value(maximize)?;
142        let supply = self.glv.supply();
143        let value =
144            market_token_amount_to_usd(&(u128::from(amount)), &glv_value, &u128::from(supply))
145                .ok_or_else(|| {
146                    crate::Error::custom("[sim] failed to convert glv token amount into value")
147                })?;
148        Ok(value)
149    }
150
151    fn get_glv_status(&self) -> crate::Result<GlvStatus> {
152        let max_sellable_value = self.get_max_sellable_glv_value()?;
153        let max_total_value = self.get_glv_value(true)?;
154        let min_total_value = self.get_glv_value(false)?;
155        Ok(GlvStatus {
156            max_sellable_value,
157            total_value: Value {
158                min: min_total_value,
159                max: max_total_value,
160            },
161        })
162    }
163}