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#[account(zero_copy)]
33#[cfg_attr(feature = "debug", derive(derive_more::Debug))]
34pub struct Glv {
35 version: u8,
36 pub(crate) bump: u8,
38 bump_bytes: [u8; 1],
39 #[cfg_attr(feature = "debug", debug(skip))]
40 padding_0: [u8; 3],
41 pub(crate) index: u16,
43 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 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 pub const GLV_TOKEN_SEED: &'static [u8] = b"glv_token";
89
90 pub const MAX_ALLOWED_NUMBER_OF_MARKETS: usize = MAX_ALLOWED_NUMBER_OF_MARKETS;
92
93 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 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 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 pub fn version(&self) -> u8 {
214 self.version
215 }
216
217 pub fn index(&self) -> u16 {
219 self.index
220 }
221
222 pub fn store(&self) -> &Pubkey {
224 &self.store
225 }
226
227 pub fn glv_token(&self) -> &Pubkey {
229 &self.glv_token
230 }
231
232 pub fn long_token(&self) -> &Pubkey {
234 &self.long_token
235 }
236
237 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 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 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 pub fn num_markets(&self) -> usize {
335 self.markets.len()
336 }
337
338 pub fn contains(&self, market_token: &Pubkey) -> bool {
340 self.markets.get(market_token).is_some()
341 }
342
343 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 pub fn tokens_collector(&self, action: Option<&impl HasSwapParams>) -> TokensCollector {
381 TokensCollector::new(action, self.num_markets())
382 }
383
384 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 pub fn shift_last_executed_at(&self) -> i64 {
546 self.shift_last_executed_at
547 }
548
549 pub fn shift_min_interval_secs(&self) -> u32 {
551 self.shift_min_interval_secs
552 }
553
554 pub fn shift_max_price_impact_factor(&self) -> u128 {
556 self.shift_max_price_impact_factor
557 }
558
559 pub fn shift_min_value(&self) -> u128 {
561 self.shift_min_value
562 }
563
564 pub fn min_tokens_for_first_deposit(&self) -> u64 {
566 self.min_tokens_for_first_deposit
567 }
568}
569
570#[derive(AnchorSerialize, AnchorDeserialize, Default)]
572#[cfg_attr(feature = "debug", derive(Debug))]
573pub struct UpdateGlvParams {
574 pub min_tokens_for_first_deposit: Option<u64>,
576 pub shift_min_interval_secs: Option<u32>,
578 pub shift_max_price_impact_factor: Option<u128>,
580 pub shift_min_value: Option<u128>,
582}
583
584impl UpdateGlvParams {
585 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#[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 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 pub fn get_flag(&self, flag: GlvMarketFlag) -> bool {
680 self.flags.get_flag(flag)
681 }
682}
683
684#[cfg(feature = "utils")]
685impl GlvMarketConfig {
686 pub fn max_amount(&self) -> u64 {
688 self.max_amount
689 }
690
691 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#[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 pub(crate) header: ActionHeader,
711 pub(crate) tokens: GlvDepositTokenAccounts,
713 pub(crate) params: GlvDepositActionParams,
715 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 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 #[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 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 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#[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 pub initial_long_token: TokenAndAccount,
856 pub initial_short_token: TokenAndAccount,
858 pub(crate) market_token: TokenAndAccount,
860 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 pub fn market_token(&self) -> Pubkey {
870 self.market_token
871 .token()
872 .expect("uninitialized GLV Deposit account")
873 }
874
875 pub fn market_token_account(&self) -> Pubkey {
877 self.market_token
878 .account()
879 .expect("uninitalized GLV Deposit account")
880 }
881
882 pub fn glv_token(&self) -> Pubkey {
884 self.glv_token
885 .token()
886 .expect("uninitialized GLV Deposit account")
887 }
888
889 pub fn glv_token_account(&self) -> Pubkey {
891 self.glv_token
892 .account()
893 .expect("uninitalized GLV Deposit account")
894 }
895}
896
897#[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 pub(crate) deposit: DepositActionParams,
904 pub(crate) market_token_amount: u64,
906 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#[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 pub(crate) header: ActionHeader,
920 pub(crate) tokens: GlvWithdrawalTokenAccounts,
922 pub(crate) params: GlvWithdrawalActionParams,
924 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 pub fn tokens(&self) -> &GlvWithdrawalTokenAccounts {
936 &self.tokens
937 }
938
939 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#[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 pub(crate) final_long_token: TokenAndAccount,
991 pub(crate) final_short_token: TokenAndAccount,
993 pub(crate) market_token: TokenAndAccount,
995 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 pub fn market_token(&self) -> Pubkey {
1005 self.market_token
1006 .token()
1007 .expect("uninitialized GLV Withdrawal account")
1008 }
1009
1010 pub fn market_token_account(&self) -> Pubkey {
1012 self.market_token
1013 .account()
1014 .expect("uninitalized GLV Withdrawal account")
1015 }
1016
1017 pub fn glv_token(&self) -> Pubkey {
1019 self.glv_token
1020 .token()
1021 .expect("uninitialized GLV Withdrawal account")
1022 }
1023
1024 pub fn glv_token_account(&self) -> Pubkey {
1026 self.glv_token
1027 .account()
1028 .expect("uninitalized GLV Withdrawal account")
1029 }
1030
1031 pub fn final_long_token(&self) -> Pubkey {
1033 self.final_long_token
1034 .token()
1035 .expect("uninitialized GLV Withdrawal account")
1036 }
1037
1038 pub fn final_long_token_account(&self) -> Pubkey {
1040 self.final_long_token
1041 .account()
1042 .expect("uninitalized GLV Withdrawal account")
1043 }
1044
1045 pub fn final_short_token(&self) -> Pubkey {
1047 self.final_short_token
1048 .token()
1049 .expect("uninitialized GLV Withdrawal account")
1050 }
1051
1052 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#[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 pub(crate) glv_token_amount: u64,
1067 pub min_final_long_token_amount: u64,
1069 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#[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 pub fn glv(&self) -> &Pubkey {
1124 &self.shift.header.owner
1125 }
1126
1127 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 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}