gmsol_store/instructions/glv/
deposit.rs

1use anchor_lang::prelude::*;
2use anchor_spl::{
3    associated_token::AssociatedToken,
4    token::{Mint, Token, TokenAccount},
5    token_2022::Token2022,
6    token_interface,
7};
8use gmsol_utils::InitSpace;
9
10use crate::{
11    constants,
12    events::EventEmitter,
13    ops::{
14        execution_fee::PayExecutionFeeOperation,
15        glv::{CreateGlvDepositOperation, CreateGlvDepositParams, ExecuteGlvDepositOperation},
16        market::{MarketTransferInOperation, MarketTransferOutOperation},
17    },
18    states::{
19        common::{
20            action::{Action, ActionExt, ActionSigner},
21            swap::SwapActionParamsExt,
22        },
23        feature::{ActionDisabledFlag, DomainDisabledFlag},
24        glv::{GlvMarketFlag, SplitAccountsForGlv},
25        Chainlink, Glv, GlvDeposit, Market, NonceBytes, Oracle, RoleKey, Seed, Store,
26        StoreWalletSigner, TokenMapHeader, TokenMapLoader,
27    },
28    utils::{
29        internal,
30        token::{
31            is_associated_token_account, is_associated_token_account_or_owner,
32            is_associated_token_account_with_program_id,
33        },
34    },
35    CoreError,
36};
37
38/// The accounts definition for [`create_glv_deposit`](crate::create_glv_deposit) instruction.
39#[derive(Accounts)]
40#[instruction(nonce: [u8; 32])]
41pub struct CreateGlvDeposit<'info> {
42    /// The owner of the deposit.
43    #[account(mut)]
44    pub owner: Signer<'info>,
45    /// The receiver of the output funds.
46    /// CHECK: only the address is used.
47    pub receiver: UncheckedAccount<'info>,
48    /// Store.
49    pub store: AccountLoader<'info, Store>,
50    /// Market.
51    #[account(
52        mut,
53        has_one = store,
54        constraint = market.load()?.meta().market_token_mint == market_token.key() @ CoreError::MarketTokenMintMismatched,
55    )]
56    pub market: AccountLoader<'info, Market>,
57    /// GLV.
58    #[account(
59        has_one = store,
60        constraint = glv.load()?.glv_token == glv_token.key() @ CoreError::TokenMintMismatched,
61        constraint = glv.load()?.contains(&market_token.key()) @ CoreError::InvalidArgument,
62    )]
63    pub glv: AccountLoader<'info, Glv>,
64    /// GLV deposit.
65    #[account(
66        init,
67        payer = owner,
68        space = 8 + GlvDeposit::INIT_SPACE,
69        seeds = [GlvDeposit::SEED, store.key().as_ref(), owner.key().as_ref(), &nonce],
70        bump,
71    )]
72    pub glv_deposit: AccountLoader<'info, GlvDeposit>,
73    /// GLV Token.
74    pub glv_token: Box<InterfaceAccount<'info, token_interface::Mint>>,
75    /// Market token.
76    pub market_token: Box<Account<'info, Mint>>,
77    /// Initial long token.
78    pub initial_long_token: Option<Box<Account<'info, Mint>>>,
79    /// initial short token.
80    pub initial_short_token: Option<Box<Account<'info, Mint>>>,
81    /// The source market token account.
82    #[account(mut, token::mint = market_token)]
83    pub market_token_source: Option<Box<Account<'info, TokenAccount>>>,
84    /// The source initial long token account.
85    #[account(mut, token::mint = initial_long_token)]
86    pub initial_long_token_source: Option<Box<Account<'info, TokenAccount>>>,
87    /// The source initial short token account.
88    #[account(mut, token::mint = initial_short_token)]
89    pub initial_short_token_source: Option<Box<Account<'info, TokenAccount>>>,
90    /// The escrow account for GLV tokens.
91    #[account(
92        mut,
93        associated_token::mint = glv_token,
94        associated_token::authority = glv_deposit,
95        associated_token::token_program = glv_token_program,
96    )]
97    pub glv_token_escrow: Box<InterfaceAccount<'info, token_interface::TokenAccount>>,
98    /// The escrow account for market tokens.
99    #[account(
100        mut,
101        associated_token::mint = market_token,
102        associated_token::authority = glv_deposit,
103    )]
104    pub market_token_escrow: Box<Account<'info, TokenAccount>>,
105    /// The escrow account for initial long tokens.
106    #[account(
107        mut,
108        associated_token::mint = initial_long_token,
109        associated_token::authority = glv_deposit,
110    )]
111    pub initial_long_token_escrow: Option<Box<Account<'info, TokenAccount>>>,
112    /// The escrow account for initial short tokens.
113    #[account(
114        mut,
115        associated_token::mint = initial_short_token,
116        associated_token::authority = glv_deposit,
117    )]
118    pub initial_short_token_escrow: Option<Box<Account<'info, TokenAccount>>>,
119    /// The system program.
120    pub system_program: Program<'info, System>,
121    /// The token program.
122    pub token_program: Program<'info, Token>,
123    /// The token program for GLV token.
124    pub glv_token_program: Program<'info, Token2022>,
125    /// The associated token program.
126    pub associated_token_program: Program<'info, AssociatedToken>,
127}
128
129impl<'info> internal::Create<'info, GlvDeposit> for CreateGlvDeposit<'info> {
130    type CreateParams = CreateGlvDepositParams;
131
132    fn action(&self) -> AccountInfo<'info> {
133        self.glv_deposit.to_account_info()
134    }
135
136    fn payer(&self) -> AccountInfo<'info> {
137        self.owner.to_account_info()
138    }
139
140    fn system_program(&self) -> AccountInfo<'info> {
141        self.system_program.to_account_info()
142    }
143
144    fn validate(&self, _params: &Self::CreateParams) -> Result<()> {
145        self.store
146            .load()?
147            .validate_not_restarted()?
148            .validate_feature_enabled(DomainDisabledFlag::GlvDeposit, ActionDisabledFlag::Create)?;
149        let market_token = self.market_token.key();
150        let is_deposit_allowed = self
151            .glv
152            .load()?
153            .market_config(&market_token)
154            .ok_or_else(|| error!(CoreError::Internal))?
155            .get_flag(GlvMarketFlag::IsDepositAllowed);
156        require!(is_deposit_allowed, CoreError::GlvDepositIsNotAllowed);
157        Ok(())
158    }
159
160    fn create_impl(
161        &mut self,
162        params: &Self::CreateParams,
163        nonce: &NonceBytes,
164        bumps: &Self::Bumps,
165        remaining_accounts: &'info [AccountInfo<'info>],
166    ) -> Result<()> {
167        self.transfer_tokens(params)?;
168        CreateGlvDepositOperation::builder()
169            .glv_deposit(self.glv_deposit.clone())
170            .market(self.market.clone())
171            .store(self.store.clone())
172            .owner(&self.owner)
173            .receiver(&self.receiver)
174            .nonce(nonce)
175            .bump(bumps.glv_deposit)
176            .initial_long_token(self.initial_long_token_escrow.as_deref())
177            .initial_short_token(self.initial_short_token_escrow.as_deref())
178            .market_token(&self.market_token_escrow)
179            .glv_token(&self.glv_token_escrow)
180            .params(params)
181            .swap_paths(remaining_accounts)
182            .build()
183            .unchecked_execute()?;
184        Ok(())
185    }
186}
187
188impl CreateGlvDeposit<'_> {
189    fn transfer_tokens(&mut self, params: &CreateGlvDepositParams) -> Result<()> {
190        use anchor_spl::token::{transfer_checked, TransferChecked};
191
192        let amount = params.initial_long_token_amount;
193        if amount != 0 {
194            let Some(source) = self.initial_long_token_source.as_ref() else {
195                return err!(CoreError::TokenAccountNotProvided);
196            };
197            let Some(target) = self.initial_long_token_escrow.as_ref() else {
198                return err!(CoreError::TokenAccountNotProvided);
199            };
200            let Some(mint) = self.initial_long_token.as_ref() else {
201                return err!(CoreError::MintAccountNotProvided);
202            };
203            transfer_checked(
204                CpiContext::new(
205                    self.token_program.to_account_info(),
206                    TransferChecked {
207                        from: source.to_account_info(),
208                        mint: mint.to_account_info(),
209                        to: target.to_account_info(),
210                        authority: self.owner.to_account_info(),
211                    },
212                ),
213                amount,
214                mint.decimals,
215            )?;
216        }
217
218        let amount = params.initial_short_token_amount;
219        if amount != 0 {
220            let Some(source) = self.initial_short_token_source.as_ref() else {
221                return err!(CoreError::TokenAccountNotProvided);
222            };
223            let Some(target) = self.initial_short_token_escrow.as_ref() else {
224                return err!(CoreError::TokenAccountNotProvided);
225            };
226            let Some(mint) = self.initial_short_token.as_ref() else {
227                return err!(CoreError::MintAccountNotProvided);
228            };
229            transfer_checked(
230                CpiContext::new(
231                    self.token_program.to_account_info(),
232                    TransferChecked {
233                        from: source.to_account_info(),
234                        mint: mint.to_account_info(),
235                        to: target.to_account_info(),
236                        authority: self.owner.to_account_info(),
237                    },
238                ),
239                amount,
240                mint.decimals,
241            )?;
242        }
243
244        let amount = params.market_token_amount;
245        if amount != 0 {
246            let Some(source) = self.market_token_source.as_ref() else {
247                return err!(CoreError::TokenAccountNotProvided);
248            };
249            let target = &self.market_token_escrow;
250            let mint = &self.market_token;
251            transfer_checked(
252                CpiContext::new(
253                    self.token_program.to_account_info(),
254                    TransferChecked {
255                        from: source.to_account_info(),
256                        mint: mint.to_account_info(),
257                        to: target.to_account_info(),
258                        authority: self.owner.to_account_info(),
259                    },
260                ),
261                amount,
262                mint.decimals,
263            )?;
264        }
265
266        // Make sure the data for escrow accounts is up-to-date.
267        for escrow in self
268            .initial_long_token_escrow
269            .as_mut()
270            .into_iter()
271            .chain(self.initial_short_token_escrow.as_mut())
272            .chain(Some(&mut self.market_token_escrow))
273        {
274            escrow.reload()?;
275        }
276
277        Ok(())
278    }
279}
280
281/// The accounts definition for [`close_glv_deposit`](crate::gmsol_store::close_glv_deposit) instruction.
282#[event_cpi]
283#[derive(Accounts)]
284pub struct CloseGlvDeposit<'info> {
285    /// The executor of this instruction.
286    pub executor: Signer<'info>,
287    /// The store.
288    pub store: AccountLoader<'info, Store>,
289    /// The store wallet.
290    #[account(mut, seeds = [Store::WALLET_SEED, store.key().as_ref()], bump)]
291    pub store_wallet: SystemAccount<'info>,
292    /// The owner of the deposit.
293    /// CHECK: only use to validate and receive fund.
294    #[account(mut)]
295    pub owner: UncheckedAccount<'info>,
296    /// The recevier of the deposit.
297    /// CHECK: only use to validate and receive fund.
298    #[account(mut)]
299    pub receiver: UncheckedAccount<'info>,
300    /// The GLV deposit to close.
301    #[account(
302        mut,
303        constraint = glv_deposit.load()?.header.store == store.key() @ CoreError::StoreMismatched,
304        constraint = glv_deposit.load()?.header.owner == owner.key() @ CoreError::OwnerMismatched,
305        constraint = glv_deposit.load()?.header.receiver() == receiver.key() @ CoreError::ReceiverMismatched,
306        // The rent receiver of a GLV deposit must be the owner.
307        constraint = glv_deposit.load()?.header.rent_receiver() == owner.key @ CoreError::RentReceiverMismatched,
308        constraint = glv_deposit.load()?.tokens.market_token_account() == market_token_escrow.key() @ CoreError::MarketTokenAccountMismatched,
309        constraint = glv_deposit.load()?.tokens.glv_token_account() == glv_token_escrow.key() @ CoreError::MarketTokenAccountMismatched,
310        constraint = glv_deposit.load()?.tokens.initial_long_token.account() == initial_long_token_escrow.as_ref().map(|a| a.key()) @ CoreError::TokenAccountMismatched,
311        constraint = glv_deposit.load()?.tokens.initial_short_token.account() == initial_short_token_escrow.as_ref().map(|a| a.key()) @ CoreError::TokenAccountMismatched,
312        seeds = [GlvDeposit::SEED, store.key().as_ref(), owner.key().as_ref(), &glv_deposit.load()?.header.nonce],
313        bump = glv_deposit.load()?.header.bump,
314    )]
315    pub glv_deposit: AccountLoader<'info, GlvDeposit>,
316    /// Market token.
317    #[account(
318        constraint = glv_deposit.load()?.tokens.market_token() == market_token.key() @ CoreError::MarketTokenMintMismatched
319    )]
320    pub market_token: Box<Account<'info, Mint>>,
321    /// Initial long token.
322    #[account(
323        constraint = glv_deposit.load()?.tokens.initial_long_token.token().map(|token| initial_long_token.key() == token).unwrap_or(true) @ CoreError::TokenMintMismatched
324    )]
325    pub initial_long_token: Option<Box<Account<'info, Mint>>>,
326    /// Initial short token.
327    #[account(
328        constraint = glv_deposit.load()?.tokens.initial_short_token.token().map(|token| initial_short_token.key() == token).unwrap_or(true) @ CoreError::TokenMintMismatched
329    )]
330    pub initial_short_token: Option<Box<Account<'info, Mint>>>,
331    /// GLV token.
332    #[account(
333        constraint = glv_deposit.load()?.tokens.glv_token() == glv_token.key() @ CoreError::TokenMintMismatched
334    )]
335    pub glv_token: Box<InterfaceAccount<'info, token_interface::Mint>>,
336    /// The escrow account for market tokens.
337    #[account(
338        mut,
339        associated_token::mint = market_token,
340        associated_token::authority = glv_deposit,
341    )]
342    pub market_token_escrow: Box<Account<'info, TokenAccount>>,
343    /// The escrow account for receiving initial long token for deposit.
344    #[account(
345        mut,
346        associated_token::mint = initial_long_token,
347        associated_token::authority = glv_deposit,
348    )]
349    pub initial_long_token_escrow: Option<Box<Account<'info, TokenAccount>>>,
350    /// The escrow account for receiving initial short token for deposit.
351    #[account(
352        mut,
353        associated_token::mint = initial_short_token,
354        associated_token::authority = glv_deposit,
355    )]
356    pub initial_short_token_escrow: Option<Box<Account<'info, TokenAccount>>>,
357    /// The escrow account for GLV tokens.
358    #[account(
359        mut,
360        associated_token::mint = glv_token,
361        associated_token::authority = glv_deposit,
362        associated_token::token_program = glv_token_program,
363    )]
364    pub glv_token_escrow: Box<InterfaceAccount<'info, token_interface::TokenAccount>>,
365    /// The ATA for market token of the owner.
366    /// CHECK: should be checked during the execution.
367    #[account(
368        mut,
369        constraint = is_associated_token_account(market_token_ata.key, owner.key, &market_token.key()) @ CoreError::NotAnATA,
370    )]
371    pub market_token_ata: UncheckedAccount<'info>,
372    /// The ATA for initial long token of the owner.
373    /// CHECK: should be checked during the execution
374    #[account(
375        mut,
376        constraint = is_associated_token_account_or_owner(initial_long_token_ata.key, owner.key, &initial_long_token.as_ref().expect("must provided").key()) @ CoreError::NotAnATA,
377    )]
378    pub initial_long_token_ata: Option<UncheckedAccount<'info>>,
379    /// The ATA for initial short token of the owner.
380    /// CHECK: should be checked during the execution
381    #[account(
382        mut,
383        constraint = is_associated_token_account_or_owner(initial_short_token_ata.key, owner.key, &initial_short_token.as_ref().expect("must provided").key()) @ CoreError::NotAnATA,
384    )]
385    pub initial_short_token_ata: Option<UncheckedAccount<'info>>,
386    /// The ATA for GLV token of the receiver.
387    /// CHECK: should be checked during the execution.
388    #[account(
389        mut,
390        constraint = is_associated_token_account_with_program_id(glv_token_ata.key, receiver.key, &glv_token.key(), &glv_token_program.key()) @ CoreError::NotAnATA,
391    )]
392    pub glv_token_ata: UncheckedAccount<'info>,
393    /// The system program.
394    pub system_program: Program<'info, System>,
395    /// The token program.
396    pub token_program: Program<'info, Token>,
397    /// Token program for GLV token.
398    pub glv_token_program: Program<'info, Token2022>,
399    /// The associated token program.
400    pub associated_token_program: Program<'info, AssociatedToken>,
401}
402
403impl<'info> internal::Close<'info, GlvDeposit> for CloseGlvDeposit<'info> {
404    fn expected_keeper_role(&self) -> &str {
405        RoleKey::ORDER_KEEPER
406    }
407
408    fn rent_receiver(&self) -> AccountInfo<'info> {
409        debug_assert!(
410            self.glv_deposit.load().unwrap().header.rent_receiver() == self.owner.key,
411            "The rent receiver must have been checked to be the owner"
412        );
413        self.owner.to_account_info()
414    }
415
416    fn store_wallet_bump(&self, bumps: &Self::Bumps) -> u8 {
417        bumps.store_wallet
418    }
419
420    fn validate(&self) -> Result<()> {
421        let glv_deposit = self.glv_deposit.load()?;
422        if glv_deposit.header.action_state()?.is_pending() {
423            self.store
424                .load()?
425                .validate_not_restarted()?
426                .validate_feature_enabled(
427                    DomainDisabledFlag::GlvDeposit,
428                    ActionDisabledFlag::Cancel,
429                )?;
430        }
431        Ok(())
432    }
433
434    fn process(
435        &self,
436        init_if_needed: bool,
437        store_wallet_signer: &StoreWalletSigner,
438        _event_emitter: &EventEmitter<'_, 'info>,
439    ) -> Result<internal::Success> {
440        use crate::utils::token::TransferAllFromEscrowToATA;
441
442        // Prepare signer seeds.
443        let signer = self.glv_deposit.load()?.signer();
444        let seeds = signer.as_seeds();
445
446        let builder = TransferAllFromEscrowToATA::builder()
447            .store_wallet(self.store_wallet.to_account_info())
448            .store_wallet_signer(store_wallet_signer)
449            .system_program(self.system_program.to_account_info())
450            .associated_token_program(self.associated_token_program.to_account_info())
451            .payer(self.executor.to_account_info())
452            .escrow_authority(self.glv_deposit.to_account_info())
453            .escrow_authority_seeds(&seeds)
454            .init_if_needed(init_if_needed)
455            .rent_receiver(self.rent_receiver())
456            .should_unwrap_native(
457                self.glv_deposit
458                    .load()?
459                    .header()
460                    .should_unwrap_native_token(),
461            );
462
463        // Transfer market tokens.
464        if !builder
465            .clone()
466            .token_program(self.token_program.to_account_info())
467            .mint(self.market_token.to_account_info())
468            .decimals(self.market_token.decimals)
469            .ata(self.market_token_ata.to_account_info())
470            .escrow(self.market_token_escrow.to_account_info())
471            .owner(self.owner.to_account_info())
472            .build()
473            .unchecked_execute()?
474        {
475            return Ok(false);
476        }
477
478        // Transfer GLV tokens.
479        if !builder
480            .clone()
481            .token_program(self.glv_token_program.to_account_info())
482            .mint(self.glv_token.to_account_info())
483            .decimals(self.glv_token.decimals)
484            .ata(self.glv_token_ata.to_account_info())
485            .escrow(self.glv_token_escrow.to_account_info())
486            .owner(self.receiver.to_account_info())
487            .build()
488            .unchecked_execute()?
489        {
490            return Ok(false);
491        }
492
493        // Prevent closing the same token accounts.
494        let (initial_long_token_escrow, initial_short_token_escrow) =
495            if self.initial_long_token_escrow.as_ref().map(|a| a.key())
496                == self.initial_short_token_escrow.as_ref().map(|a| a.key())
497            {
498                (self.initial_long_token_escrow.as_ref(), None)
499            } else {
500                (
501                    self.initial_long_token_escrow.as_ref(),
502                    self.initial_short_token_escrow.as_ref(),
503                )
504            };
505
506        // Transfer initial long tokens.
507        if let Some(escrow) = initial_long_token_escrow.as_ref() {
508            let Some(ata) = self.initial_long_token_ata.as_ref() else {
509                return err!(CoreError::TokenAccountNotProvided);
510            };
511            let Some(mint) = self.initial_long_token.as_ref() else {
512                return err!(CoreError::MintAccountNotProvided);
513            };
514            if !builder
515                .clone()
516                .token_program(self.token_program.to_account_info())
517                .mint(mint.to_account_info())
518                .decimals(mint.decimals)
519                .ata(ata.to_account_info())
520                .escrow(escrow.to_account_info())
521                .owner(self.owner.to_account_info())
522                .build()
523                .unchecked_execute()?
524            {
525                return Ok(false);
526            }
527        }
528
529        // Transfer initial short tokens.
530        if let Some(escrow) = initial_short_token_escrow.as_ref() {
531            let Some(ata) = self.initial_short_token_ata.as_ref() else {
532                return err!(CoreError::TokenAccountNotProvided);
533            };
534            let Some(mint) = self.initial_short_token.as_ref() else {
535                return err!(CoreError::MintAccountNotProvided);
536            };
537            if !builder
538                .clone()
539                .token_program(self.token_program.to_account_info())
540                .mint(mint.to_account_info())
541                .decimals(mint.decimals)
542                .ata(ata.to_account_info())
543                .escrow(escrow.to_account_info())
544                .owner(self.owner.to_account_info())
545                .build()
546                .unchecked_execute()?
547            {
548                return Ok(false);
549            }
550        }
551
552        Ok(true)
553    }
554
555    fn event_authority(&self, bumps: &Self::Bumps) -> (AccountInfo<'info>, u8) {
556        (
557            self.event_authority.to_account_info(),
558            bumps.event_authority,
559        )
560    }
561
562    fn action(&self) -> &AccountLoader<'info, GlvDeposit> {
563        &self.glv_deposit
564    }
565}
566
567impl<'info> internal::Authentication<'info> for CloseGlvDeposit<'info> {
568    fn authority(&self) -> &Signer<'info> {
569        &self.executor
570    }
571
572    fn store(&self) -> &AccountLoader<'info, Store> {
573        &self.store
574    }
575}
576
577/// The accounts definition for [`execute_glv_deposit`](crate::gmsol_store::execute_glv_deposit) instruction.
578///
579/// Remaining accounts expected by this instruction:
580///
581///   - 0..N. `[]` N market accounts, where N represents the total number of markets managed
582///     by the given GLV.
583///   - N..2N. `[]` N market token accounts (see above for the definition of N).
584///   - 2N..2N+M. `[]` M feed accounts, where M represents the total number of tokens in the
585///     swap params.
586///   - 2N+M..2N+M+L. `[writable]` L market accounts, where L represents the total number of unique
587///     markets excluding the current market in the swap params.
588#[event_cpi]
589#[derive(Accounts)]
590pub struct ExecuteGlvDeposit<'info> {
591    /// Authority.
592    pub authority: Signer<'info>,
593    /// Store.
594    #[account(has_one = token_map)]
595    pub store: AccountLoader<'info, Store>,
596    /// Token Map.
597    #[account(has_one = store)]
598    pub token_map: AccountLoader<'info, TokenMapHeader>,
599    /// Oracle buffer to use.
600    #[account(mut, has_one = store)]
601    pub oracle: AccountLoader<'info, Oracle>,
602    /// GLV account.
603    #[account(
604        mut,
605        has_one = store,
606        constraint = glv.load()?.contains(&market_token.key()) @ CoreError::InvalidArgument,
607    )]
608    pub glv: AccountLoader<'info, Glv>,
609    /// Market.
610    #[account(mut, has_one = store)]
611    pub market: AccountLoader<'info, Market>,
612    /// The GLV deposit to execute.
613    #[account(
614        mut,
615        constraint = glv_deposit.load()?.header.store == store.key() @ CoreError::StoreMismatched,
616        constraint = glv_deposit.load()?.header.market == market.key() @ CoreError::MarketMismatched,
617        constraint = glv_deposit.load()?.tokens.glv_token() == glv_token.key() @ CoreError::TokenMintMismatched,
618        constraint = glv_deposit.load()?.tokens.market_token() == market_token.key() @ CoreError::MarketTokenMintMismatched,
619        constraint = glv_deposit.load()?.tokens.market_token_account() == market_token_escrow.key() @ CoreError::MarketTokenAccountMismatched,
620        constraint = glv_deposit.load()?.tokens.glv_token_account() == glv_token_escrow.key() @ CoreError::MarketTokenAccountMismatched,
621        constraint = glv_deposit.load()?.tokens.initial_long_token.account() == initial_long_token_escrow.as_ref().map(|a| a.key()) @ CoreError::TokenAccountMismatched,
622        constraint = glv_deposit.load()?.tokens.initial_short_token.account() == initial_short_token_escrow.as_ref().map(|a| a.key()) @ CoreError::TokenAccountMismatched,
623        seeds = [GlvDeposit::SEED, store.key().as_ref(), glv_deposit.load()?.header.owner.as_ref(), &glv_deposit.load()?.header.nonce],
624        bump = glv_deposit.load()?.header.bump,
625    )]
626    pub glv_deposit: AccountLoader<'info, GlvDeposit>,
627    /// GLV token mint.
628    #[account(mut, constraint = glv.load()?.glv_token == glv_token.key() @ CoreError::TokenMintMismatched)]
629    pub glv_token: Box<InterfaceAccount<'info, token_interface::Mint>>,
630    /// Market token mint.
631    #[account(mut, constraint = market.load()?.meta().market_token_mint == market_token.key() @ CoreError::MarketTokenMintMismatched)]
632    pub market_token: Box<Account<'info, Mint>>,
633    /// Initial long token.
634    #[account(
635        constraint = glv_deposit.load()?.tokens.initial_long_token.token().map(|token| initial_long_token.key() == token).unwrap_or(true) @ CoreError::TokenMintMismatched
636    )]
637    pub initial_long_token: Option<Box<Account<'info, Mint>>>,
638    /// Initial short token.
639    #[account(
640        constraint = glv_deposit.load()?.tokens.initial_short_token.token().map(|token| initial_short_token.key() == token).unwrap_or(true) @ CoreError::TokenMintMismatched
641    )]
642    pub initial_short_token: Option<Box<Account<'info, Mint>>>,
643    /// The escrow account for GLV tokens.
644    #[account(
645        mut,
646        associated_token::mint = glv_token,
647        associated_token::authority = glv_deposit,
648        associated_token::token_program = glv_token_program,
649    )]
650    pub glv_token_escrow: Box<InterfaceAccount<'info, token_interface::TokenAccount>>,
651    /// The escrow account for market tokens.
652    #[account(
653        mut,
654        associated_token::mint = market_token,
655        associated_token::authority = glv_deposit,
656    )]
657    pub market_token_escrow: Box<Account<'info, TokenAccount>>,
658    /// The escrow account for receiving initial long token for deposit.
659    #[account(
660        mut,
661        associated_token::mint = initial_long_token,
662        associated_token::authority = glv_deposit,
663    )]
664    pub initial_long_token_escrow: Option<Box<Account<'info, TokenAccount>>>,
665    /// The escrow account for receiving initial short token for deposit.
666    #[account(
667        mut,
668        associated_token::mint = initial_short_token,
669        associated_token::authority = glv_deposit,
670    )]
671    pub initial_short_token_escrow: Option<Box<Account<'info, TokenAccount>>>,
672    /// Initial long token vault.
673    #[account(
674        mut,
675        token::mint = initial_long_token,
676        token::authority = store,
677        seeds = [
678            constants::MARKET_VAULT_SEED,
679            store.key().as_ref(),
680            initial_long_token_vault.mint.as_ref(),
681        ],
682        bump,
683    )]
684    pub initial_long_token_vault: Option<Box<Account<'info, TokenAccount>>>,
685    /// Initial short token vault.
686    #[account(
687        mut,
688        token::mint = initial_short_token,
689        token::authority = store,
690        seeds = [
691            constants::MARKET_VAULT_SEED,
692            store.key().as_ref(),
693            initial_short_token_vault.mint.as_ref(),
694        ],
695        bump,
696    )]
697    pub initial_short_token_vault: Option<Box<Account<'info, TokenAccount>>>,
698    /// Market token vault for the GLV.
699    #[account(
700        mut,
701        associated_token::mint = market_token,
702        associated_token::authority = glv,
703    )]
704    pub market_token_vault: Box<Account<'info, TokenAccount>>,
705    /// The token program.
706    pub token_program: Program<'info, Token>,
707    /// The token program for GLV token.
708    pub glv_token_program: Program<'info, Token2022>,
709    /// The system program.
710    pub system_program: Program<'info, System>,
711    /// Chainlink Program.
712    pub chainlink_program: Option<Program<'info, Chainlink>>,
713}
714
715/// CHECK: only ORDER_KEEPER is allowed to call this function.
716pub(crate) fn unchecked_execute_glv_deposit<'info>(
717    ctx: Context<'_, '_, 'info, 'info, ExecuteGlvDeposit<'info>>,
718    execution_lamports: u64,
719    throw_on_execution_error: bool,
720) -> Result<()> {
721    let accounts = ctx.accounts;
722    let remaining_accounts = ctx.remaining_accounts;
723
724    // Validate feature enabled.
725    accounts
726        .store
727        .load()?
728        .validate_feature_enabled(DomainDisabledFlag::GlvDeposit, ActionDisabledFlag::Execute)?;
729
730    let SplitAccountsForGlv {
731        markets,
732        market_tokens,
733        remaining_accounts,
734        tokens,
735    } = {
736        let glv_deposit = accounts.glv_deposit.load()?;
737        let token_map = accounts.token_map.load_token_map()?;
738        accounts.glv.load()?.validate_and_split_remaining_accounts(
739            &accounts.store.key(),
740            remaining_accounts,
741            Some(&*glv_deposit),
742            &token_map,
743        )?
744    };
745
746    let event_authority = accounts.event_authority.clone();
747    let event_emitter = EventEmitter::new(&event_authority, ctx.bumps.event_authority);
748
749    let signer = accounts.glv_deposit.load()?.signer();
750    accounts.transfer_tokens_in(&signer, remaining_accounts, &event_emitter)?;
751
752    let executed = accounts.perform_execution(
753        markets,
754        market_tokens,
755        &tokens,
756        remaining_accounts,
757        throw_on_execution_error,
758        &event_emitter,
759    )?;
760
761    if executed {
762        accounts.glv_deposit.load_mut()?.header.completed()?;
763    } else {
764        accounts.glv_deposit.load_mut()?.header.cancelled()?;
765        accounts.transfer_tokens_out(remaining_accounts, &event_emitter)?;
766    }
767
768    // It must be placed at the end to be executed correctly.
769    accounts.pay_execution_fee(execution_lamports)?;
770    Ok(())
771}
772
773impl<'info> internal::Authentication<'info> for ExecuteGlvDeposit<'info> {
774    fn authority(&self) -> &Signer<'info> {
775        &self.authority
776    }
777
778    fn store(&self) -> &AccountLoader<'info, Store> {
779        &self.store
780    }
781}
782
783impl<'info> ExecuteGlvDeposit<'info> {
784    #[inline(never)]
785    fn pay_execution_fee(&self, execution_fee: u64) -> Result<()> {
786        let execution_lamports = self.glv_deposit.load()?.execution_lamports(execution_fee);
787        PayExecutionFeeOperation::builder()
788            .payer(self.glv_deposit.to_account_info())
789            .receiver(self.authority.to_account_info())
790            .execution_lamports(execution_lamports)
791            .build()
792            .execute()?;
793        Ok(())
794    }
795
796    #[inline(never)]
797    fn transfer_tokens_in(
798        &self,
799        signer: &ActionSigner,
800        remaining_accounts: &'info [AccountInfo<'info>],
801        event_emitter: &EventEmitter<'_, 'info>,
802    ) -> Result<()> {
803        // self.transfer_market_tokens_in(signer)?;
804        self.transfer_initial_tokens_in(signer, remaining_accounts, event_emitter)?;
805        Ok(())
806    }
807
808    #[inline(never)]
809    fn transfer_tokens_out(
810        &self,
811        remaining_accounts: &'info [AccountInfo<'info>],
812        event_emitter: &EventEmitter<'_, 'info>,
813    ) -> Result<()> {
814        // self.transfer_market_tokens_out()?;
815        self.transfer_initial_tokens_out(remaining_accounts, event_emitter)?;
816        Ok(())
817    }
818
819    fn transfer_initial_tokens_in(
820        &self,
821        sigenr: &ActionSigner,
822        remaining_accounts: &'info [AccountInfo<'info>],
823        event_emitter: &EventEmitter<'_, 'info>,
824    ) -> Result<()> {
825        let seeds = sigenr.as_seeds();
826        let builder = MarketTransferInOperation::builder()
827            .store(&self.store)
828            .from_authority(self.glv_deposit.to_account_info())
829            .token_program(self.token_program.to_account_info())
830            .signer_seeds(&seeds)
831            .event_emitter(*event_emitter);
832        let store = &self.store.key();
833
834        for is_primary in [true, false] {
835            let (amount, escrow, vault) = if is_primary {
836                (
837                    self.glv_deposit
838                        .load()?
839                        .params
840                        .deposit
841                        .initial_long_token_amount,
842                    self.initial_long_token_escrow.as_ref(),
843                    self.initial_long_token_vault.as_ref(),
844                )
845            } else {
846                (
847                    self.glv_deposit
848                        .load()?
849                        .params
850                        .deposit
851                        .initial_short_token_amount,
852                    self.initial_short_token_escrow.as_ref(),
853                    self.initial_short_token_vault.as_ref(),
854                )
855            };
856
857            if amount == 0 {
858                continue;
859            }
860
861            let escrow = escrow.ok_or_else(|| error!(CoreError::TokenAccountNotProvided))?;
862            let market = self
863                .glv_deposit
864                .load()?
865                .swap
866                .find_and_unpack_first_market(store, is_primary, remaining_accounts)?
867                .unwrap_or(self.market.clone());
868            let vault = vault.ok_or_else(|| error!(CoreError::TokenAccountNotProvided))?;
869            builder
870                .clone()
871                .market(&market)
872                .from(escrow.to_account_info())
873                .vault(vault)
874                .amount(amount)
875                .build()
876                .execute()?;
877        }
878
879        Ok(())
880    }
881
882    fn transfer_initial_tokens_out(
883        &self,
884        remaining_accounts: &'info [AccountInfo<'info>],
885        event_emitter: &EventEmitter<'_, 'info>,
886    ) -> Result<()> {
887        let builder = MarketTransferOutOperation::builder()
888            .store(&self.store)
889            .token_program(self.token_program.to_account_info())
890            .event_emitter(*event_emitter);
891
892        let store = &self.store.key();
893
894        for is_primary in [true, false] {
895            let (amount, token, escrow, vault) = if is_primary {
896                (
897                    self.glv_deposit
898                        .load()?
899                        .params
900                        .deposit
901                        .initial_long_token_amount,
902                    self.initial_long_token.as_ref(),
903                    self.initial_long_token_escrow.as_ref(),
904                    self.initial_long_token_vault.as_ref(),
905                )
906            } else {
907                (
908                    self.glv_deposit
909                        .load()?
910                        .params
911                        .deposit
912                        .initial_short_token_amount,
913                    self.initial_short_token.as_ref(),
914                    self.initial_short_token_escrow.as_ref(),
915                    self.initial_short_token_vault.as_ref(),
916                )
917            };
918
919            let Some(escrow) = escrow else {
920                continue;
921            };
922
923            let market = self
924                .glv_deposit
925                .load()?
926                .swap
927                .find_and_unpack_first_market(store, is_primary, remaining_accounts)?
928                .unwrap_or(self.market.clone());
929            let token = token.ok_or_else(|| error!(CoreError::TokenMintNotProvided))?;
930            let vault = vault.ok_or_else(|| error!(CoreError::TokenAccountNotProvided))?;
931            builder
932                .clone()
933                .market(&market)
934                .to(escrow.to_account_info())
935                .vault(vault.to_account_info())
936                .amount(amount)
937                .decimals(token.decimals)
938                .token_mint(token.to_account_info())
939                .build()
940                .execute()?;
941        }
942
943        Ok(())
944    }
945
946    fn perform_execution(
947        &mut self,
948        markets: &'info [AccountInfo<'info>],
949        market_tokens: &'info [AccountInfo<'info>],
950        tokens: &[Pubkey],
951        remaining_accounts: &'info [AccountInfo<'info>],
952        throw_on_execution_error: bool,
953        event_emitter: &EventEmitter<'_, 'info>,
954    ) -> Result<bool> {
955        let builder = ExecuteGlvDepositOperation::builder()
956            .glv_deposit(self.glv_deposit.clone())
957            .token_program(self.token_program.to_account_info())
958            .glv_token_program(self.glv_token_program.to_account_info())
959            .throw_on_execution_error(throw_on_execution_error)
960            .store(self.store.clone())
961            .glv(self.glv.clone())
962            .glv_token_mint(&mut self.glv_token)
963            .glv_token_receiver(self.glv_token_escrow.to_account_info())
964            .market(self.market.clone())
965            .market_token_source(&self.market_token_escrow)
966            .market_token_mint(&mut self.market_token)
967            .market_token_vault(self.market_token_vault.to_account_info())
968            .markets(markets)
969            .market_tokens(market_tokens)
970            .event_emitter(*event_emitter);
971
972        self.oracle.load_mut()?.with_prices(
973            &self.store,
974            &self.token_map,
975            tokens,
976            remaining_accounts,
977            self.chainlink_program.as_ref(),
978            |oracle, remaining_accounts| {
979                builder
980                    .oracle(oracle)
981                    .remaining_accounts(remaining_accounts)
982                    .build()
983                    .unchecked_execute()
984            },
985        )
986    }
987}