gmsol_store/states/
glv.rs

1use std::{
2    borrow::{Borrow, BorrowMut},
3    collections::BTreeSet,
4};
5
6use anchor_lang::prelude::*;
7use anchor_spl::token_interface;
8use gmsol_utils::{glv::MAX_GLV_MARKET_FLAGS, InitSpace};
9
10use crate::{
11    constants,
12    events::{GlvDepositRemoved, GlvWithdrawalRemoved, ShiftRemoved},
13    states::{Deposit, Market},
14    CoreError,
15};
16
17use super::{
18    common::{
19        action::{Action, ActionHeader, Closable},
20        swap::{unpack_markets, HasSwapParams, SwapActionParams},
21        token::{TokenAndAccount, TokensCollector},
22    },
23    deposit::DepositActionParams,
24    shift, Seed, Shift, TokenMapAccess,
25};
26
27pub use gmsol_utils::glv::GlvMarketFlag;
28
29const MAX_ALLOWED_NUMBER_OF_MARKETS: usize = 96;
30
31/// Glv.
32#[account(zero_copy)]
33#[cfg_attr(feature = "debug", derive(derive_more::Debug))]
34pub struct Glv {
35    version: u8,
36    /// Bump seed.
37    pub(crate) bump: u8,
38    bump_bytes: [u8; 1],
39    #[cfg_attr(feature = "debug", debug(skip))]
40    padding_0: [u8; 3],
41    /// Index.
42    pub(crate) index: u16,
43    /// Store.
44    pub store: Pubkey,
45    pub(crate) glv_token: Pubkey,
46    pub(crate) long_token: Pubkey,
47    pub(crate) short_token: Pubkey,
48    shift_last_executed_at: i64,
49    pub(crate) min_tokens_for_first_deposit: u64,
50    shift_min_interval_secs: u32,
51    #[cfg_attr(feature = "debug", debug(skip))]
52    padding_1: [u8; 4],
53    shift_max_price_impact_factor: u128,
54    shift_min_value: u128,
55    #[cfg_attr(feature = "debug", debug(skip))]
56    reserved: [u8; 256],
57    /// Market config map with market token addresses as keys.
58    markets: GlvMarkets,
59}
60
61gmsol_utils::fixed_map!(
62    GlvMarkets,
63    Pubkey,
64    crate::utils::pubkey::to_bytes,
65    GlvMarketConfig,
66    MAX_ALLOWED_NUMBER_OF_MARKETS,
67    12
68);
69
70impl Default for Glv {
71    fn default() -> Self {
72        use bytemuck::Zeroable;
73
74        Self::zeroed()
75    }
76}
77
78impl Seed for Glv {
79    const SEED: &'static [u8] = b"glv";
80}
81
82impl InitSpace for Glv {
83    const INIT_SPACE: usize = std::mem::size_of::<Self>();
84}
85
86impl Glv {
87    /// GLV token seed.
88    pub const GLV_TOKEN_SEED: &'static [u8] = b"glv_token";
89
90    /// Max allowed number of markets.
91    pub const MAX_ALLOWED_NUMBER_OF_MARKETS: usize = MAX_ALLOWED_NUMBER_OF_MARKETS;
92
93    /// Find GLV token address.
94    pub fn find_glv_token_pda(store: &Pubkey, index: u16, program_id: &Pubkey) -> (Pubkey, u8) {
95        Pubkey::find_program_address(
96            &[Self::GLV_TOKEN_SEED, store.as_ref(), &index.to_le_bytes()],
97            program_id,
98        )
99    }
100
101    /// Find GLV address.
102    pub fn find_glv_pda(glv_token: &Pubkey, program_id: &Pubkey) -> (Pubkey, u8) {
103        Pubkey::find_program_address(&[Self::SEED, glv_token.as_ref()], program_id)
104    }
105
106    pub(crate) fn signer_seeds(&self) -> [&[u8]; 3] {
107        [Self::SEED, self.glv_token().as_ref(), &self.bump_bytes]
108    }
109
110    pub(crate) fn vec_signer_seeds(&self) -> Vec<Vec<u8>> {
111        vec![
112            Self::SEED.to_vec(),
113            self.glv_token.to_bytes().to_vec(),
114            self.bump_bytes.to_vec(),
115        ]
116    }
117
118    /// Initialize the [`Glv`] account.
119    ///
120    /// # CHECK
121    /// - The [`Glv`] account must be uninitialized.
122    /// - The `bump` must be the bump deriving the address of the [`Glv`] account.
123    /// - The `glv_token` must be used to derive the address of the [`Glv`] account.
124    /// - The market tokens must be valid and unique, and their corresponding markets
125    ///   must use the given tokens as long token and short token.
126    /// - The `store` must be the address of the store owning the corresponding markets.
127    ///
128    /// # Errors
129    /// - The `glv_token` address must be derived from [`GLV_TOKEN_SEED`](Self::GLV_TOKEN_SEED), `store` and `index`.
130    /// - The total number of the market tokens must not exceed the max allowed number of markets.
131    pub(crate) fn unchecked_init(
132        &mut self,
133        bump: u8,
134        index: u16,
135        store: &Pubkey,
136        glv_token: &Pubkey,
137        long_token: &Pubkey,
138        short_token: &Pubkey,
139        market_tokens: &BTreeSet<Pubkey>,
140    ) -> Result<()> {
141        let expected_glv_token = Self::find_glv_token_pda(store, index, &crate::ID).0;
142        require_keys_eq!(expected_glv_token, *glv_token, CoreError::InvalidArgument);
143
144        self.version = 0;
145        self.bump = bump;
146        self.bump_bytes = [bump];
147        self.index = index;
148        self.store = *store;
149        self.glv_token = *glv_token;
150        self.long_token = *long_token;
151        self.short_token = *short_token;
152
153        self.shift_min_interval_secs = constants::DEFAULT_GLV_MIN_SHIFT_INTERVAL_SECS;
154        self.shift_max_price_impact_factor = constants::DEFAULT_GLV_MAX_SHIFT_PRICE_IMPACT_FACTOR;
155        self.shift_min_value = constants::DEFAULT_GLV_MIN_SHIFT_VALUE;
156
157        require_gte!(
158            Self::MAX_ALLOWED_NUMBER_OF_MARKETS,
159            market_tokens.len(),
160            CoreError::ExceedMaxLengthLimit
161        );
162
163        for market_token in market_tokens {
164            self.markets
165                .insert_with_options(market_token, Default::default(), true)?;
166        }
167        Ok(())
168    }
169
170    pub(crate) fn process_and_validate_markets_for_init<'info>(
171        markets: &'info [AccountInfo<'info>],
172        store: &Pubkey,
173    ) -> Result<(Pubkey, Pubkey, BTreeSet<Pubkey>)> {
174        let mut tokens = None;
175
176        let mut market_tokens = BTreeSet::default();
177        for market in unpack_markets(markets) {
178            let market = market?;
179            let market = market.load()?;
180            let meta = market.validated_meta(store)?;
181            match &mut tokens {
182                Some((long_token, short_token)) => {
183                    require_keys_eq!(
184                        *long_token,
185                        meta.long_token_mint,
186                        CoreError::TokenMintMismatched
187                    );
188                    require_keys_eq!(
189                        *short_token,
190                        meta.short_token_mint,
191                        CoreError::TokenMintMismatched
192                    );
193                }
194                none => {
195                    *none = Some((meta.long_token_mint, meta.short_token_mint));
196                }
197            }
198            require!(
199                market_tokens.insert(meta.market_token_mint),
200                CoreError::InvalidArgument
201            );
202        }
203
204        if let Some((long_token, short_token)) = tokens {
205            require_eq!(markets.len(), market_tokens.len(), CoreError::Internal);
206            Ok((long_token, short_token, market_tokens))
207        } else {
208            err!(CoreError::InvalidArgument)
209        }
210    }
211
212    /// Get the version of the [`Glv`] account format.
213    pub fn version(&self) -> u8 {
214        self.version
215    }
216
217    /// Get the index of the glv token.
218    pub fn index(&self) -> u16 {
219        self.index
220    }
221
222    /// Get the store address.
223    pub fn store(&self) -> &Pubkey {
224        &self.store
225    }
226
227    /// Get the GLV token address.
228    pub fn glv_token(&self) -> &Pubkey {
229        &self.glv_token
230    }
231
232    /// Get the long token address.
233    pub fn long_token(&self) -> &Pubkey {
234        &self.long_token
235    }
236
237    /// Get the short token address.
238    pub fn short_token(&self) -> &Pubkey {
239        &self.short_token
240    }
241
242    pub(crate) fn update_config(&mut self, params: &UpdateGlvParams) -> Result<()> {
243        if let Some(amount) = params.min_tokens_for_first_deposit {
244            require_neq!(
245                self.min_tokens_for_first_deposit,
246                amount,
247                CoreError::PreconditionsAreNotMet
248            );
249            self.min_tokens_for_first_deposit = amount;
250        }
251
252        if let Some(secs) = params.shift_min_interval_secs {
253            require_neq!(
254                self.shift_min_interval_secs,
255                secs,
256                CoreError::PreconditionsAreNotMet
257            );
258            self.shift_min_interval_secs = secs;
259        }
260
261        if let Some(factor) = params.shift_max_price_impact_factor {
262            require_neq!(
263                self.shift_max_price_impact_factor,
264                factor,
265                CoreError::PreconditionsAreNotMet
266            );
267            self.shift_max_price_impact_factor = factor;
268        }
269
270        if let Some(value) = params.shift_min_value {
271            require_neq!(
272                self.shift_min_value,
273                value,
274                CoreError::PreconditionsAreNotMet
275            );
276            self.shift_min_value = value;
277        }
278
279        Ok(())
280    }
281
282    pub(crate) fn insert_market(&mut self, store: &Pubkey, market: &Market) -> Result<()> {
283        let meta = market.validated_meta(store)?;
284
285        require_keys_eq!(
286            meta.long_token_mint,
287            self.long_token,
288            CoreError::InvalidArgument
289        );
290
291        require_keys_eq!(
292            meta.short_token_mint,
293            self.short_token,
294            CoreError::InvalidArgument
295        );
296
297        let market_token = meta.market_token_mint;
298        self.markets
299            .insert_with_options(&market_token, GlvMarketConfig::default(), true)?;
300
301        Ok(())
302    }
303
304    /// Remove market from the GLV.
305    ///
306    /// # CHECK
307    /// - The balance of the vault must be zero.
308    pub(crate) fn unchecked_remove_market(&mut self, market_token: &Pubkey) -> Result<()> {
309        let config = self
310            .market_config(market_token)
311            .ok_or_else(|| error!(CoreError::NotFound))?;
312
313        require!(
314            !config.get_flag(GlvMarketFlag::IsDepositAllowed),
315            CoreError::PreconditionsAreNotMet
316        );
317
318        require!(
319            self.markets.remove(market_token).is_some(),
320            CoreError::Internal
321        );
322
323        Ok(())
324    }
325
326    /// Get all market tokens.
327    pub fn market_tokens(&self) -> impl Iterator<Item = Pubkey> + '_ {
328        self.markets
329            .entries()
330            .map(|(key, _)| Pubkey::new_from_array(*key))
331    }
332
333    /// Get the total number of markets.
334    pub fn num_markets(&self) -> usize {
335        self.markets.len()
336    }
337
338    /// Return whether the given market token is contained in this GLV.
339    pub fn contains(&self, market_token: &Pubkey) -> bool {
340        self.markets.get(market_token).is_some()
341    }
342
343    /// Get [`GlvMarketConfig`] for the given market.
344    pub fn market_config(&self, market_token: &Pubkey) -> Option<&GlvMarketConfig> {
345        self.markets.get(market_token)
346    }
347
348    pub(crate) fn update_market_config(
349        &mut self,
350        market_token: &Pubkey,
351        max_amount: Option<u64>,
352        max_value: Option<u128>,
353    ) -> Result<()> {
354        let config = self
355            .markets
356            .get_mut(market_token)
357            .ok_or_else(|| error!(CoreError::NotFound))?;
358        if let Some(amount) = max_amount {
359            config.max_amount = amount;
360        }
361        if let Some(value) = max_value {
362            config.max_value = value;
363        }
364        Ok(())
365    }
366
367    pub(crate) fn toggle_market_config_flag(
368        &mut self,
369        market_token: &Pubkey,
370        flag: GlvMarketFlag,
371        enable: bool,
372    ) -> Result<bool> {
373        self.markets
374            .get_mut(market_token)
375            .ok_or_else(|| error!(CoreError::NotFound))?
376            .toggle_flag(flag, enable)
377    }
378
379    /// Create a new [`TokensCollector`].
380    pub fn tokens_collector(&self, action: Option<&impl HasSwapParams>) -> TokensCollector {
381        TokensCollector::new(action, self.num_markets())
382    }
383
384    /// Split remaining accounts.
385    pub(crate) fn validate_and_split_remaining_accounts<'info>(
386        &self,
387        store: &Pubkey,
388        remaining_accounts: &'info [AccountInfo<'info>],
389        action: Option<&impl HasSwapParams>,
390        token_map: &impl TokenMapAccess,
391    ) -> Result<SplitAccountsForGlv<'info>> {
392        let len = self.num_markets();
393
394        let markets_end = len;
395        let market_tokens_end = markets_end + len;
396
397        require_gte!(
398            remaining_accounts.len(),
399            market_tokens_end,
400            CoreError::InvalidArgument
401        );
402
403        let markets = &remaining_accounts[0..markets_end];
404        let market_tokens = &remaining_accounts[markets_end..market_tokens_end];
405        let remaining_accounts = &remaining_accounts[market_tokens_end..];
406
407        let mut tokens_collector = self.tokens_collector(action);
408
409        for idx in 0..len {
410            let market = &markets[idx];
411            let market_token = &market_tokens[idx];
412            let expected_market_token = Pubkey::new_from_array(
413                *self
414                    .markets
415                    .get_entry_by_index(idx)
416                    .expect("never out of range")
417                    .0,
418            );
419
420            require_keys_eq!(
421                market_token.key(),
422                expected_market_token,
423                CoreError::MarketTokenMintMismatched
424            );
425
426            {
427                let mint = Account::<anchor_spl::token::Mint>::try_from(market_token)?;
428                require!(
429                    mint.mint_authority == Some(*store).into(),
430                    CoreError::StoreMismatched
431                );
432            }
433
434            {
435                let market = AccountLoader::<Market>::try_from(market)?;
436                let market = market.load()?;
437                let meta = market.validated_meta(store)?;
438                require_keys_eq!(
439                    meta.market_token_mint,
440                    expected_market_token,
441                    CoreError::MarketTokenMintMismatched
442                );
443                tokens_collector.insert_token(&meta.index_token_mint);
444            }
445        }
446
447        Ok(SplitAccountsForGlv {
448            markets,
449            market_tokens,
450            remaining_accounts,
451            tokens: tokens_collector
452                .into_vec(token_map)
453                .map_err(CoreError::from)?,
454        })
455    }
456
457    pub(crate) fn validate_market_token_balance(
458        &self,
459        market_token: &Pubkey,
460        new_balance: u64,
461        market_pool_value: &i128,
462        market_token_supply: &u128,
463    ) -> Result<()> {
464        let config = self
465            .markets
466            .get(market_token)
467            .ok_or_else(|| error!(CoreError::NotFound))?;
468        config.validate_balance(new_balance, market_pool_value, market_token_supply)
469    }
470
471    pub(crate) fn update_market_token_balance(
472        &mut self,
473        market_token: &Pubkey,
474        new_balance: u64,
475    ) -> Result<()> {
476        let config = self
477            .markets
478            .get_mut(market_token)
479            .ok_or_else(|| error!(CoreError::NotFound))?;
480        config.update_balance(new_balance);
481        Ok(())
482    }
483
484    pub(crate) fn validate_shift_interval(&self) -> Result<()> {
485        let interval = self.shift_min_interval_secs;
486        if interval == 0 {
487            Ok(())
488        } else {
489            let current = Clock::get()?.unix_timestamp;
490            let after = self
491                .shift_last_executed_at
492                .checked_add(interval as i64)
493                .ok_or_else(|| error!(CoreError::ValueOverflow))?;
494            require_gte!(current, after, CoreError::GlvShiftIntervalNotYetPassed);
495            Ok(())
496        }
497    }
498
499    pub(crate) fn validate_shift_price_impact(
500        &self,
501        from_market_token_value: u128,
502        to_market_token_value: u128,
503    ) -> Result<()> {
504        use gmsol_model::utils::div_to_factor;
505
506        if from_market_token_value < to_market_token_value {
507            Ok(())
508        } else {
509            let max_factor = self.shift_max_price_impact_factor;
510            let diff = from_market_token_value.abs_diff(to_market_token_value);
511            let effective_price_impact_factor = div_to_factor::<_, { constants::MARKET_DECIMALS }>(
512                &diff,
513                &from_market_token_value,
514                false,
515            )
516            .ok_or_else(|| error!(CoreError::Internal))?;
517            require_gte!(
518                max_factor,
519                effective_price_impact_factor,
520                CoreError::GlvShiftMaxPriceImpactExceeded
521            );
522            Ok(())
523        }
524    }
525
526    pub(crate) fn validate_shift_value(&self, from_market_token_value: u128) -> Result<()> {
527        require_gte!(
528            from_market_token_value,
529            self.shift_min_value,
530            CoreError::GlvShiftValueNotLargeEnough
531        );
532        Ok(())
533    }
534
535    pub(crate) fn update_shift_last_executed_ts(&mut self) -> Result<()> {
536        let clock = Clock::get()?;
537        self.shift_last_executed_at = clock.unix_timestamp;
538        Ok(())
539    }
540}
541
542#[cfg(feature = "utils")]
543impl Glv {
544    /// Get last shift executed ts.
545    pub fn shift_last_executed_at(&self) -> i64 {
546        self.shift_last_executed_at
547    }
548
549    /// Get min shift interval.
550    pub fn shift_min_interval_secs(&self) -> u32 {
551        self.shift_min_interval_secs
552    }
553
554    /// Get max shift price impact factor.
555    pub fn shift_max_price_impact_factor(&self) -> u128 {
556        self.shift_max_price_impact_factor
557    }
558
559    /// Get min shift vaule.
560    pub fn shift_min_value(&self) -> u128 {
561        self.shift_min_value
562    }
563
564    /// Get min tokens for first deposit.
565    pub fn min_tokens_for_first_deposit(&self) -> u64 {
566        self.min_tokens_for_first_deposit
567    }
568}
569
570/// GLV Update Params.
571#[derive(AnchorSerialize, AnchorDeserialize, Default)]
572#[cfg_attr(feature = "debug", derive(Debug))]
573pub struct UpdateGlvParams {
574    /// Minimum amount for the first GLV deposit.
575    pub min_tokens_for_first_deposit: Option<u64>,
576    /// Minimum shift interval seconds.
577    pub shift_min_interval_secs: Option<u32>,
578    /// Maximum price impact factor after shift.
579    pub shift_max_price_impact_factor: Option<u128>,
580    /// Minimum shift value.
581    pub shift_min_value: Option<u128>,
582}
583
584impl UpdateGlvParams {
585    /// Returns whether the update is empty.
586    pub fn is_empty(&self) -> bool {
587        self.min_tokens_for_first_deposit.is_none()
588            && self.shift_min_interval_secs.is_none()
589            && self.shift_max_price_impact_factor.is_none()
590            && self.shift_min_value.is_none()
591    }
592
593    pub(crate) fn validate(&self) -> Result<()> {
594        require!(!self.is_empty(), CoreError::InvalidArgument);
595        Ok(())
596    }
597}
598
599gmsol_utils::flags!(GlvMarketFlag, MAX_GLV_MARKET_FLAGS, u8);
600
601/// Market Config for GLV.
602#[zero_copy]
603#[cfg_attr(feature = "debug", derive(derive_more::Debug))]
604pub struct GlvMarketConfig {
605    max_amount: u64,
606    flags: GlvMarketFlagContainer,
607    #[cfg_attr(feature = "debug", debug(skip))]
608    padding_0: [u8; 7],
609    max_value: u128,
610    balance: u64,
611    #[cfg_attr(feature = "debug", debug(skip))]
612    padding_1: [u8; 8],
613}
614
615impl Default for GlvMarketConfig {
616    fn default() -> Self {
617        use bytemuck::Zeroable;
618
619        Self::zeroed()
620    }
621}
622
623impl GlvMarketConfig {
624    fn validate_balance(
625        &self,
626        new_balance: u64,
627        market_pool_value: &i128,
628        market_token_supply: &u128,
629    ) -> Result<()> {
630        if self.max_amount == 0 && self.max_value == 0 {
631            return Ok(());
632        }
633
634        if self.max_amount > 0 {
635            require_gte!(
636                self.max_amount,
637                new_balance,
638                CoreError::ExceedMaxGlvMarketTokenBalanceAmount
639            );
640        }
641
642        if self.max_value > 0 {
643            if market_pool_value.is_negative() {
644                return err!(CoreError::GlvNegativeMarketPoolValue);
645            }
646
647            let value = gmsol_model::utils::market_token_amount_to_usd(
648                &(new_balance as u128),
649                &market_pool_value.unsigned_abs(),
650                market_token_supply,
651            )
652            .ok_or_else(|| error!(CoreError::FailedToCalculateGlvValueForMarket))?;
653            require_gte!(
654                self.max_value,
655                value,
656                CoreError::ExceedMaxGlvMarketTokenBalanceValue
657            );
658        }
659
660        Ok(())
661    }
662
663    fn update_balance(&mut self, new_balance: u64) {
664        self.balance = new_balance;
665    }
666
667    /// Get balance.
668    pub fn balance(&self) -> u64 {
669        self.balance
670    }
671
672    pub(crate) fn toggle_flag(&mut self, flag: GlvMarketFlag, enable: bool) -> Result<bool> {
673        let current = self.flags.get_flag(flag);
674        require_neq!(current, enable, CoreError::PreconditionsAreNotMet);
675        Ok(self.flags.set_flag(flag, enable))
676    }
677
678    /// Get flag.
679    pub fn get_flag(&self, flag: GlvMarketFlag) -> bool {
680        self.flags.get_flag(flag)
681    }
682}
683
684#[cfg(feature = "utils")]
685impl GlvMarketConfig {
686    /// Get max amount.
687    pub fn max_amount(&self) -> u64 {
688        self.max_amount
689    }
690
691    /// Get max value.
692    pub fn max_value(&self) -> u128 {
693        self.max_value
694    }
695}
696
697pub(crate) struct SplitAccountsForGlv<'info> {
698    pub(crate) markets: &'info [AccountInfo<'info>],
699    pub(crate) market_tokens: &'info [AccountInfo<'info>],
700    pub(crate) remaining_accounts: &'info [AccountInfo<'info>],
701    pub(crate) tokens: Vec<Pubkey>,
702}
703
704/// Glv Deposit.
705#[account(zero_copy)]
706#[cfg_attr(feature = "debug", derive(derive_more::Debug))]
707#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
708pub struct GlvDeposit {
709    /// Header.
710    pub(crate) header: ActionHeader,
711    /// Token accounts.
712    pub(crate) tokens: GlvDepositTokenAccounts,
713    /// Params.
714    pub(crate) params: GlvDepositActionParams,
715    /// Swap params.
716    pub(crate) swap: SwapActionParams,
717    #[cfg_attr(feature = "debug", debug(skip))]
718    padding_1: [u8; 4],
719    #[cfg_attr(feature = "debug", debug(skip))]
720    #[cfg_attr(feature = "serde", serde(with = "serde_bytes"))]
721    reserved: [u8; 128],
722}
723
724impl Action for GlvDeposit {
725    const MIN_EXECUTION_LAMPORTS: u64 = 200_000;
726
727    fn header(&self) -> &ActionHeader {
728        &self.header
729    }
730}
731
732impl Closable for GlvDeposit {
733    type ClosedEvent = GlvDepositRemoved;
734
735    fn to_closed_event(&self, address: &Pubkey, reason: &str) -> Result<Self::ClosedEvent> {
736        GlvDepositRemoved::new(
737            self.header.id(),
738            *self.header.store(),
739            *address,
740            self.tokens().market_token(),
741            self.tokens().glv_token(),
742            *self.header.owner(),
743            self.header.action_state()?,
744            reason,
745        )
746    }
747}
748
749impl Seed for GlvDeposit {
750    const SEED: &'static [u8] = b"glv_deposit";
751}
752
753impl gmsol_utils::InitSpace for GlvDeposit {
754    const INIT_SPACE: usize = std::mem::size_of::<Self>();
755}
756
757impl GlvDeposit {
758    /// Validate the GLV deposit before execution.
759    ///
760    /// # CHECK
761    /// - This deposit must have been initialized.
762    /// - The `glv` and `glv_token` must match.
763    /// - The `market_token` must be a valid token account.
764    /// - The `glv_token` must be a valid token account.
765    ///
766    /// # Errors
767    /// - The address of `market_token` must match the market token address of this deposit.
768    /// - The address of `glv_token` must match the glv token address of this deposit.
769    pub(crate) fn unchecked_validate_for_execution(
770        &self,
771        glv_token: &InterfaceAccount<token_interface::Mint>,
772        glv: &Glv,
773    ) -> Result<()> {
774        require_keys_eq!(
775            glv_token.key(),
776            self.tokens.glv_token(),
777            CoreError::TokenMintMismatched,
778        );
779
780        let supply = glv_token.supply;
781
782        if supply == 0 {
783            Self::validate_first_deposit(
784                &self.header().receiver(),
785                self.params.min_glv_token_amount,
786                glv,
787            )?;
788        }
789
790        Ok(())
791    }
792
793    pub(crate) fn is_market_deposit_required(&self) -> bool {
794        self.params.deposit.initial_long_token_amount != 0
795            || self.params.deposit.initial_short_token_amount != 0
796    }
797
798    /// Get first deposit receiver.
799    #[inline]
800    pub fn first_deposit_receiver() -> Pubkey {
801        Deposit::first_deposit_receiver()
802    }
803
804    fn validate_first_deposit(receiver: &Pubkey, min_amount: u64, glv: &Glv) -> Result<()> {
805        let min_tokens_for_first_deposit = glv.min_tokens_for_first_deposit;
806
807        // Skip first deposit check if the amount is zero.
808        if min_tokens_for_first_deposit == 0 {
809            return Ok(());
810        }
811
812        require_keys_eq!(
813            *receiver,
814            Self::first_deposit_receiver(),
815            CoreError::InvalidReceiverForFirstDeposit
816        );
817
818        require_gte!(
819            min_amount,
820            min_tokens_for_first_deposit,
821            CoreError::NotEnoughGlvTokenAmountForFirstDeposit,
822        );
823
824        Ok(())
825    }
826
827    pub(crate) fn validate_output_amount(&self, amount: u64) -> Result<()> {
828        require_gte!(
829            amount,
830            self.params.min_glv_token_amount,
831            CoreError::InsufficientOutputAmount
832        );
833
834        Ok(())
835    }
836
837    /// Get token infos.
838    pub fn tokens(&self) -> &GlvDepositTokenAccounts {
839        &self.tokens
840    }
841}
842
843impl HasSwapParams for GlvDeposit {
844    fn swap(&self) -> &SwapActionParams {
845        &self.swap
846    }
847}
848
849/// Token and accounts.
850#[zero_copy]
851#[cfg_attr(feature = "debug", derive(derive_more::Debug))]
852#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
853pub struct GlvDepositTokenAccounts {
854    /// Initial long token and account.
855    pub initial_long_token: TokenAndAccount,
856    /// Initial short token and account.
857    pub initial_short_token: TokenAndAccount,
858    /// Market token and account.
859    pub(crate) market_token: TokenAndAccount,
860    /// GLV token and account.
861    pub(crate) glv_token: TokenAndAccount,
862    #[cfg_attr(feature = "debug", debug(skip))]
863    #[cfg_attr(feature = "serde", serde(with = "serde_bytes"))]
864    reserved: [u8; 128],
865}
866
867impl GlvDepositTokenAccounts {
868    /// Get market token.
869    pub fn market_token(&self) -> Pubkey {
870        self.market_token
871            .token()
872            .expect("uninitialized GLV Deposit account")
873    }
874
875    /// Get market token account.
876    pub fn market_token_account(&self) -> Pubkey {
877        self.market_token
878            .account()
879            .expect("uninitalized GLV Deposit account")
880    }
881
882    /// Get GLV token.
883    pub fn glv_token(&self) -> Pubkey {
884        self.glv_token
885            .token()
886            .expect("uninitialized GLV Deposit account")
887    }
888
889    /// Get GLV token account.
890    pub fn glv_token_account(&self) -> Pubkey {
891        self.glv_token
892            .account()
893            .expect("uninitalized GLV Deposit account")
894    }
895}
896
897/// GLV Deposit Params.
898#[zero_copy]
899#[cfg_attr(feature = "debug", derive(derive_more::Debug))]
900#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
901pub struct GlvDepositActionParams {
902    /// Deposit params.
903    pub(crate) deposit: DepositActionParams,
904    /// The amount of market tokens to deposit.
905    pub(crate) market_token_amount: u64,
906    /// The minimum acceptable amount of glv tokens to receive.
907    pub(crate) min_glv_token_amount: u64,
908    #[cfg_attr(feature = "debug", debug(skip))]
909    #[cfg_attr(feature = "serde", serde(with = "serde_bytes"))]
910    reserved: [u8; 64],
911}
912
913/// Glv Withdrawal.
914#[account(zero_copy)]
915#[cfg_attr(feature = "debug", derive(derive_more::Debug))]
916#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
917pub struct GlvWithdrawal {
918    /// Header.
919    pub(crate) header: ActionHeader,
920    /// Token accounts.
921    pub(crate) tokens: GlvWithdrawalTokenAccounts,
922    /// Params.
923    pub(crate) params: GlvWithdrawalActionParams,
924    /// Swap params.
925    pub(crate) swap: SwapActionParams,
926    #[cfg_attr(feature = "debug", debug(skip))]
927    padding_1: [u8; 4],
928    #[cfg_attr(feature = "debug", debug(skip))]
929    #[cfg_attr(feature = "serde", serde(with = "serde_bytes"))]
930    reserved: [u8; 128],
931}
932
933impl GlvWithdrawal {
934    /// Get tokens.
935    pub fn tokens(&self) -> &GlvWithdrawalTokenAccounts {
936        &self.tokens
937    }
938
939    /// Get swap params.
940    pub fn swap(&self) -> &SwapActionParams {
941        &self.swap
942    }
943}
944
945impl Action for GlvWithdrawal {
946    const MIN_EXECUTION_LAMPORTS: u64 = 200_000;
947
948    fn header(&self) -> &ActionHeader {
949        &self.header
950    }
951}
952
953impl Closable for GlvWithdrawal {
954    type ClosedEvent = GlvWithdrawalRemoved;
955
956    fn to_closed_event(&self, address: &Pubkey, reason: &str) -> Result<Self::ClosedEvent> {
957        GlvWithdrawalRemoved::new(
958            self.header.id,
959            self.header.store,
960            *address,
961            self.tokens.market_token(),
962            self.tokens.glv_token(),
963            self.header.owner,
964            self.header.action_state()?,
965            reason,
966        )
967    }
968}
969
970impl Seed for GlvWithdrawal {
971    const SEED: &'static [u8] = b"glv_withdrawal";
972}
973
974impl gmsol_utils::InitSpace for GlvWithdrawal {
975    const INIT_SPACE: usize = std::mem::size_of::<Self>();
976}
977
978impl HasSwapParams for GlvWithdrawal {
979    fn swap(&self) -> &SwapActionParams {
980        &self.swap
981    }
982}
983
984/// Token and accounts.
985#[zero_copy]
986#[cfg_attr(feature = "debug", derive(derive_more::Debug))]
987#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
988pub struct GlvWithdrawalTokenAccounts {
989    /// Final ong token and account.
990    pub(crate) final_long_token: TokenAndAccount,
991    /// Final short token and account.
992    pub(crate) final_short_token: TokenAndAccount,
993    /// Market token and account.
994    pub(crate) market_token: TokenAndAccount,
995    /// GLV token and account.
996    pub(crate) glv_token: TokenAndAccount,
997    #[cfg_attr(feature = "debug", debug(skip))]
998    #[cfg_attr(feature = "serde", serde(with = "serde_bytes"))]
999    reserved: [u8; 128],
1000}
1001
1002impl GlvWithdrawalTokenAccounts {
1003    /// Get market token.
1004    pub fn market_token(&self) -> Pubkey {
1005        self.market_token
1006            .token()
1007            .expect("uninitialized GLV Withdrawal account")
1008    }
1009
1010    /// Get market token account.
1011    pub fn market_token_account(&self) -> Pubkey {
1012        self.market_token
1013            .account()
1014            .expect("uninitalized GLV Withdrawal account")
1015    }
1016
1017    /// Get GLV token.
1018    pub fn glv_token(&self) -> Pubkey {
1019        self.glv_token
1020            .token()
1021            .expect("uninitialized GLV Withdrawal account")
1022    }
1023
1024    /// Get GLV token account.
1025    pub fn glv_token_account(&self) -> Pubkey {
1026        self.glv_token
1027            .account()
1028            .expect("uninitalized GLV Withdrawal account")
1029    }
1030
1031    /// Get final long token.
1032    pub fn final_long_token(&self) -> Pubkey {
1033        self.final_long_token
1034            .token()
1035            .expect("uninitialized GLV Withdrawal account")
1036    }
1037
1038    /// Get final long token account.
1039    pub fn final_long_token_account(&self) -> Pubkey {
1040        self.final_long_token
1041            .account()
1042            .expect("uninitalized GLV Withdrawal account")
1043    }
1044
1045    /// Get final short token.
1046    pub fn final_short_token(&self) -> Pubkey {
1047        self.final_short_token
1048            .token()
1049            .expect("uninitialized GLV Withdrawal account")
1050    }
1051
1052    /// Get final short token account.
1053    pub fn final_short_token_account(&self) -> Pubkey {
1054        self.final_short_token
1055            .account()
1056            .expect("uninitalized GLV Withdrawal account")
1057    }
1058}
1059
1060/// GLV Withdrawal Params.
1061#[zero_copy]
1062#[cfg_attr(feature = "debug", derive(derive_more::Debug))]
1063#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1064pub struct GlvWithdrawalActionParams {
1065    /// The amount of GLV tokens to burn.
1066    pub(crate) glv_token_amount: u64,
1067    /// The minimum acceptable amount of final long tokens to receive.
1068    pub min_final_long_token_amount: u64,
1069    /// The minimum acceptable amount of final short tokens to receive.
1070    pub min_final_short_token_amount: u64,
1071    #[cfg_attr(feature = "debug", debug(skip))]
1072    #[cfg_attr(feature = "serde", serde(with = "serde_bytes"))]
1073    reserved: [u8; 64],
1074}
1075
1076/// Glv Shift.
1077#[account(zero_copy)]
1078#[cfg_attr(feature = "debug", derive(derive_more::Debug))]
1079#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1080pub struct GlvShift {
1081    pub(crate) shift: Shift,
1082    #[cfg_attr(feature = "debug", debug(skip))]
1083    #[cfg_attr(feature = "serde", serde(with = "serde_bytes"))]
1084    reserved: [u8; 128],
1085}
1086
1087impl Action for GlvShift {
1088    const MIN_EXECUTION_LAMPORTS: u64 = 0;
1089
1090    fn header(&self) -> &ActionHeader {
1091        &self.shift.header
1092    }
1093}
1094
1095impl Closable for GlvShift {
1096    type ClosedEvent = ShiftRemoved;
1097
1098    fn to_closed_event(&self, address: &Pubkey, reason: &str) -> Result<Self::ClosedEvent> {
1099        let header = self.header();
1100        let tokens = self.tokens();
1101        ShiftRemoved::new(
1102            header.id,
1103            header.store,
1104            *address,
1105            tokens.from_market_token(),
1106            header.owner,
1107            header.action_state()?,
1108            reason,
1109        )
1110    }
1111}
1112
1113impl Seed for GlvShift {
1114    const SEED: &'static [u8] = Shift::SEED;
1115}
1116
1117impl gmsol_utils::InitSpace for GlvShift {
1118    const INIT_SPACE: usize = std::mem::size_of::<Self>();
1119}
1120
1121impl GlvShift {
1122    /// Get the GLV address.
1123    pub fn glv(&self) -> &Pubkey {
1124        &self.shift.header.owner
1125    }
1126
1127    /// Get token infos.
1128    pub fn tokens(&self) -> &shift::ShiftTokenAccounts {
1129        self.shift.tokens()
1130    }
1131
1132    pub(crate) fn header_mut(&mut self) -> &mut ActionHeader {
1133        &mut self.shift.header
1134    }
1135
1136    /// Get the funder.
1137    pub fn funder(&self) -> &Pubkey {
1138        self.shift.header().rent_receiver()
1139    }
1140}
1141
1142impl Borrow<Shift> for GlvShift {
1143    fn borrow(&self) -> &Shift {
1144        &self.shift
1145    }
1146}
1147
1148impl BorrowMut<Shift> for GlvShift {
1149    fn borrow_mut(&mut self) -> &mut Shift {
1150        &mut self.shift
1151    }
1152}