gmsol_store/instructions/glv/
withdrawal.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::{
16            CreateGlvWithdrawalOperation, CreateGlvWithdrawalParams, ExecuteGlvWithdrawalOperation,
17        },
18        market::MarketTransferOutOperation,
19    },
20    states::{
21        common::{
22            action::{Action, ActionExt},
23            swap::SwapActionParamsExt,
24        },
25        feature::{ActionDisabledFlag, DomainDisabledFlag},
26        glv::{GlvWithdrawal, SplitAccountsForGlv},
27        Chainlink, Glv, Market, NonceBytes, Oracle, RoleKey, Seed, Store, StoreWalletSigner,
28        TokenMapHeader, TokenMapLoader,
29    },
30    utils::{
31        internal,
32        token::{
33            is_associated_token_account, is_associated_token_account_or_owner,
34            is_associated_token_account_with_program_id,
35        },
36    },
37    CoreError,
38};
39
40/// The accounts defintion for [`create_glv_withdrawal`](crate::create_glv_withdrawal) instruction.
41#[derive(Accounts)]
42#[instruction(nonce: [u8; 32])]
43pub struct CreateGlvWithdrawal<'info> {
44    /// Owner.
45    #[account(mut)]
46    pub owner: Signer<'info>,
47    /// The receiver of the output funds.
48    /// CHECK: only the address is used.
49    pub receiver: UncheckedAccount<'info>,
50    /// Store.
51    pub store: AccountLoader<'info, Store>,
52    /// Market.
53    #[account(
54        mut,
55        has_one = store,
56        constraint = market.load()?.meta().market_token_mint == market_token.key() @ CoreError::MarketTokenMintMismatched,
57    )]
58    pub market: AccountLoader<'info, Market>,
59    /// GLV.
60    #[account(
61        has_one = store,
62        constraint = glv.load()?.glv_token == glv_token.key() @ CoreError::TokenMintMismatched,
63        constraint = glv.load()?.contains(&market_token.key()) @ CoreError::InvalidArgument,
64    )]
65    pub glv: AccountLoader<'info, Glv>,
66    /// GLV withdrawal.
67    #[account(
68        init,
69        payer = owner,
70        space = 8 + GlvWithdrawal::INIT_SPACE,
71        seeds = [GlvWithdrawal::SEED, store.key().as_ref(), owner.key().as_ref(), &nonce],
72        bump,
73    )]
74    pub glv_withdrawal: AccountLoader<'info, GlvWithdrawal>,
75    /// GLV Token.
76    pub glv_token: Box<InterfaceAccount<'info, token_interface::Mint>>,
77    /// Market token.
78    pub market_token: Box<Account<'info, Mint>>,
79    /// Final long token.
80    pub final_long_token: Box<Account<'info, Mint>>,
81    /// Final short token.
82    pub final_short_token: Box<Account<'info, Mint>>,
83    /// The source GLV token account.
84    #[account(mut, token::mint = glv_token)]
85    pub glv_token_source: Box<InterfaceAccount<'info, token_interface::TokenAccount>>,
86    /// The escrow account for GLV tokens.
87    #[account(
88        mut,
89        associated_token::mint = glv_token,
90        associated_token::authority = glv_withdrawal,
91        associated_token::token_program = glv_token_program,
92    )]
93    pub glv_token_escrow: Box<InterfaceAccount<'info, token_interface::TokenAccount>>,
94    /// The escrow account for market tokens.
95    #[account(
96        mut,
97        associated_token::mint = market_token,
98        associated_token::authority = glv_withdrawal,
99    )]
100    pub market_token_escrow: Box<Account<'info, TokenAccount>>,
101    /// The escrow account for long tokens.
102    #[account(
103        mut,
104        associated_token::mint = final_long_token,
105        associated_token::authority = glv_withdrawal,
106    )]
107    pub final_long_token_escrow: Box<Account<'info, TokenAccount>>,
108    /// The escrow account for short tokens.
109    #[account(
110        mut,
111        associated_token::mint = final_short_token,
112        associated_token::authority = glv_withdrawal,
113    )]
114    pub final_short_token_escrow: Box<Account<'info, TokenAccount>>,
115    /// The system program.
116    pub system_program: Program<'info, System>,
117    /// The token program.
118    pub token_program: Program<'info, Token>,
119    /// The token program for GLV token.
120    pub glv_token_program: Program<'info, Token2022>,
121    /// The associated token program.
122    pub associated_token_program: Program<'info, AssociatedToken>,
123}
124
125impl<'info> internal::Create<'info, GlvWithdrawal> for CreateGlvWithdrawal<'info> {
126    type CreateParams = CreateGlvWithdrawalParams;
127
128    fn action(&self) -> AccountInfo<'info> {
129        self.glv_withdrawal.to_account_info()
130    }
131
132    fn payer(&self) -> AccountInfo<'info> {
133        self.owner.to_account_info()
134    }
135
136    fn system_program(&self) -> AccountInfo<'info> {
137        self.system_program.to_account_info()
138    }
139
140    fn validate(&self, _params: &Self::CreateParams) -> Result<()> {
141        self.store
142            .load()?
143            .validate_not_restarted()?
144            .validate_feature_enabled(
145                DomainDisabledFlag::GlvWithdrawal,
146                ActionDisabledFlag::Create,
147            )?;
148        Ok(())
149    }
150
151    fn create_impl(
152        &mut self,
153        params: &Self::CreateParams,
154        nonce: &NonceBytes,
155        bumps: &Self::Bumps,
156        remaining_accounts: &'info [AccountInfo<'info>],
157    ) -> Result<()> {
158        self.transfer_glv_tokens(params)?;
159        CreateGlvWithdrawalOperation::builder()
160            .glv_withdrawal(self.glv_withdrawal.clone())
161            .market(self.market.clone())
162            .store(self.store.clone())
163            .owner(&self.owner)
164            .receiver(&self.receiver)
165            .nonce(nonce)
166            .bump(bumps.glv_withdrawal)
167            .final_long_token(&self.final_long_token_escrow)
168            .final_short_token(&self.final_short_token_escrow)
169            .market_token(&self.market_token_escrow)
170            .glv_token(&self.glv_token_escrow)
171            .params(params)
172            .swap_paths(remaining_accounts)
173            .build()
174            .unchecked_execute()?;
175        Ok(())
176    }
177}
178
179impl CreateGlvWithdrawal<'_> {
180    fn transfer_glv_tokens(&mut self, params: &CreateGlvWithdrawalParams) -> Result<()> {
181        use anchor_spl::token_interface::{transfer_checked, TransferChecked};
182
183        let amount = params.glv_token_amount;
184        require!(amount != 0, CoreError::EmptyGlvWithdrawal);
185
186        let source = &self.glv_token_source;
187        let target = &mut self.glv_token_escrow;
188        let mint = &self.glv_token;
189
190        transfer_checked(
191            CpiContext::new(
192                self.glv_token_program.to_account_info(),
193                TransferChecked {
194                    from: source.to_account_info(),
195                    mint: mint.to_account_info(),
196                    to: target.to_account_info(),
197                    authority: self.owner.to_account_info(),
198                },
199            ),
200            amount,
201            mint.decimals,
202        )?;
203
204        target.reload()?;
205
206        Ok(())
207    }
208}
209
210/// The accounts defintion for [`close_glv_withdrawal`](crate::gmsol_store::close_glv_withdrawal) instruction.
211#[event_cpi]
212#[derive(Accounts)]
213pub struct CloseGlvWithdrawal<'info> {
214    /// The executor of this instruction.
215    pub executor: Signer<'info>,
216    /// The store.
217    pub store: AccountLoader<'info, Store>,
218    /// The store wallet.
219    #[account(mut, seeds = [Store::WALLET_SEED, store.key().as_ref()], bump)]
220    pub store_wallet: SystemAccount<'info>,
221    /// The owner of the deposit.
222    /// CHECK: only use to validate and receive input funds.
223    #[account(mut)]
224    pub owner: UncheckedAccount<'info>,
225    /// The receiver of the deposit.
226    /// CHECK: only use to validate and receive output funds.
227    #[account(mut)]
228    pub receiver: UncheckedAccount<'info>,
229    /// The GLV withdrawal to close.
230    #[account(
231        mut,
232        constraint = glv_withdrawal.load()?.header.store == store.key() @ CoreError::StoreMismatched,
233        constraint = glv_withdrawal.load()?.header.owner == owner.key() @ CoreError::OwnerMismatched,
234        constraint = glv_withdrawal.load()?.header.receiver() == receiver.key() @ CoreError::ReceiverMismatched,
235        // The rent receiver of a GLV withdrawal must be the owner.
236        constraint = glv_withdrawal.load()?.header.rent_receiver() == owner.key @ CoreError::RentReceiverMismatched,
237        constraint = glv_withdrawal.load()?.tokens.market_token_account() == market_token_escrow.key() @ CoreError::MarketTokenAccountMismatched,
238        constraint = glv_withdrawal.load()?.tokens.glv_token_account() == glv_token_escrow.key() @ CoreError::MarketTokenAccountMismatched,
239        constraint = glv_withdrawal.load()?.tokens.final_long_token_account() == final_long_token_escrow.key() @ CoreError::TokenAccountMismatched,
240        constraint = glv_withdrawal.load()?.tokens.final_short_token_account() == final_short_token_escrow.key() @ CoreError::TokenAccountMismatched,
241        seeds = [GlvWithdrawal::SEED, store.key().as_ref(), owner.key().as_ref(), &glv_withdrawal.load()?.header.nonce],
242        bump = glv_withdrawal.load()?.header.bump,
243    )]
244    pub glv_withdrawal: AccountLoader<'info, GlvWithdrawal>,
245    /// Market token.
246    #[account(
247        constraint = glv_withdrawal.load()?.tokens.market_token() == market_token.key() @ CoreError::MarketTokenMintMismatched
248    )]
249    pub market_token: Box<Account<'info, Mint>>,
250    /// Final long token.
251    #[account(
252        constraint = glv_withdrawal.load()?.tokens.final_long_token() == final_long_token.key() @ CoreError::TokenMintMismatched
253    )]
254    pub final_long_token: Box<Account<'info, Mint>>,
255    /// Final short token.
256    #[account(
257        constraint = glv_withdrawal.load()?.tokens.final_short_token() == final_short_token.key() @ CoreError::TokenMintMismatched
258    )]
259    pub final_short_token: Box<Account<'info, Mint>>,
260    /// GLV token.
261    #[account(
262        constraint = glv_withdrawal.load()?.tokens.glv_token() == glv_token.key() @ CoreError::TokenMintMismatched
263    )]
264    pub glv_token: Box<InterfaceAccount<'info, token_interface::Mint>>,
265    /// The escrow account for market tokens.
266    #[account(
267        mut,
268        associated_token::mint = market_token,
269        associated_token::authority = glv_withdrawal,
270    )]
271    pub market_token_escrow: Box<Account<'info, TokenAccount>>,
272    /// The escrow account for receiving initial long token for deposit.
273    #[account(
274        mut,
275        associated_token::mint = final_long_token,
276        associated_token::authority = glv_withdrawal,
277    )]
278    pub final_long_token_escrow: Box<Account<'info, TokenAccount>>,
279    /// The escrow account for receiving final short token for deposit.
280    #[account(
281        mut,
282        associated_token::mint = final_short_token,
283        associated_token::authority = glv_withdrawal,
284    )]
285    pub final_short_token_escrow: Box<Account<'info, TokenAccount>>,
286    /// The ATA for market token of the owner.
287    /// CHECK: should be checked during the execution.
288    #[account(
289        mut,
290        constraint = is_associated_token_account(market_token_ata.key, owner.key, &market_token.key()) @ CoreError::NotAnATA,
291    )]
292    pub market_token_ata: UncheckedAccount<'info>,
293    /// The ATA for final long token of the receiver.
294    /// CHECK: should be checked during the execution
295    #[account(
296        mut,
297        constraint = is_associated_token_account_or_owner(final_long_token_ata.key, receiver.key, &final_long_token.as_ref().key()) @ CoreError::NotAnATA,
298    )]
299    pub final_long_token_ata: UncheckedAccount<'info>,
300    /// The ATA for final short token of the receiver.
301    /// CHECK: should be checked during the execution
302    #[account(
303        mut,
304        constraint = is_associated_token_account_or_owner(final_short_token_ata.key, receiver.key, &final_short_token.as_ref().key()) @ CoreError::NotAnATA,
305    )]
306    pub final_short_token_ata: UncheckedAccount<'info>,
307    /// The escrow account for GLV tokens.
308    #[account(
309        mut,
310        associated_token::mint = glv_token,
311        associated_token::authority = glv_withdrawal,
312        associated_token::token_program = glv_token_program,
313    )]
314    pub glv_token_escrow: Box<InterfaceAccount<'info, token_interface::TokenAccount>>,
315    /// The ATA for GLV token of the owner.
316    /// CHECK: should be checked during the execution.
317    #[account(
318        mut,
319        constraint = is_associated_token_account_with_program_id(glv_token_ata.key, owner.key, &glv_token.key(), &glv_token_program.key()) @ CoreError::NotAnATA,
320    )]
321    pub glv_token_ata: UncheckedAccount<'info>,
322    /// The system program.
323    pub system_program: Program<'info, System>,
324    /// The token program.
325    pub token_program: Program<'info, Token>,
326    /// Token program for GLV token.
327    pub glv_token_program: Program<'info, Token2022>,
328    /// The associated token program.
329    pub associated_token_program: Program<'info, AssociatedToken>,
330}
331
332impl<'info> internal::Close<'info, GlvWithdrawal> for CloseGlvWithdrawal<'info> {
333    fn expected_keeper_role(&self) -> &str {
334        RoleKey::ORDER_KEEPER
335    }
336
337    fn rent_receiver(&self) -> AccountInfo<'info> {
338        debug_assert!(
339            self.glv_withdrawal.load().unwrap().header.rent_receiver() == self.owner.key,
340            "The rent receiver must have been checked to be the owner"
341        );
342        self.owner.to_account_info()
343    }
344
345    fn store_wallet_bump(&self, bumps: &Self::Bumps) -> u8 {
346        bumps.store_wallet
347    }
348
349    fn validate(&self) -> Result<()> {
350        let glv_withdrawal = self.glv_withdrawal.load()?;
351        if glv_withdrawal.header.action_state()?.is_pending() {
352            self.store
353                .load()?
354                .validate_not_restarted()?
355                .validate_feature_enabled(
356                    DomainDisabledFlag::GlvWithdrawal,
357                    ActionDisabledFlag::Cancel,
358                )?;
359        }
360        Ok(())
361    }
362
363    fn process(
364        &self,
365        init_if_needed: bool,
366        store_wallet_signer: &StoreWalletSigner,
367        _event_emitter: &EventEmitter<'_, 'info>,
368    ) -> Result<internal::Success> {
369        use crate::utils::token::TransferAllFromEscrowToATA;
370
371        // Prepare signer seeds.
372        let signer = self.glv_withdrawal.load()?.signer();
373        let seeds = signer.as_seeds();
374
375        let builder = TransferAllFromEscrowToATA::builder()
376            .store_wallet(self.store_wallet.to_account_info())
377            .store_wallet_signer(store_wallet_signer)
378            .system_program(self.system_program.to_account_info())
379            .associated_token_program(self.associated_token_program.to_account_info())
380            .payer(self.executor.to_account_info())
381            .escrow_authority(self.glv_withdrawal.to_account_info())
382            .escrow_authority_seeds(&seeds)
383            .init_if_needed(init_if_needed)
384            .rent_receiver(self.rent_receiver())
385            .should_unwrap_native(
386                self.glv_withdrawal
387                    .load()?
388                    .header()
389                    .should_unwrap_native_token(),
390            );
391
392        // Transfer market tokens.
393        if !builder
394            .clone()
395            .token_program(self.token_program.to_account_info())
396            .mint(self.market_token.to_account_info())
397            .decimals(self.market_token.decimals)
398            .ata(self.market_token_ata.to_account_info())
399            .escrow(self.market_token_escrow.to_account_info())
400            .owner(self.owner.to_account_info())
401            .build()
402            .unchecked_execute()?
403        {
404            return Ok(false);
405        }
406
407        // Transfer GLV tokens.
408        if !builder
409            .clone()
410            .token_program(self.glv_token_program.to_account_info())
411            .mint(self.glv_token.to_account_info())
412            .decimals(self.glv_token.decimals)
413            .ata(self.glv_token_ata.to_account_info())
414            .escrow(self.glv_token_escrow.to_account_info())
415            .owner(self.owner.to_account_info())
416            .build()
417            .unchecked_execute()?
418        {
419            return Ok(false);
420        }
421
422        // Prevent closing the same token accounts.
423        let (final_long_token_escrow, final_short_token_escrow) =
424            if self.final_long_token_escrow.key() == self.final_short_token_escrow.key() {
425                (Some(&self.final_long_token_escrow), None)
426            } else {
427                (
428                    Some(&self.final_long_token_escrow),
429                    Some(&self.final_short_token_escrow),
430                )
431            };
432
433        // Transfer final long tokens.
434        if let Some(escrow) = final_long_token_escrow.as_ref() {
435            let ata = &self.final_long_token_ata;
436            let mint = &self.final_long_token;
437            if !builder
438                .clone()
439                .token_program(self.token_program.to_account_info())
440                .mint(mint.to_account_info())
441                .decimals(mint.decimals)
442                .ata(ata.to_account_info())
443                .escrow(escrow.to_account_info())
444                .owner(self.receiver.to_account_info())
445                .build()
446                .unchecked_execute()?
447            {
448                return Ok(false);
449            }
450        }
451
452        // Transfer final short tokens.
453        if let Some(escrow) = final_short_token_escrow.as_ref() {
454            let ata = &self.final_short_token_ata;
455            let mint = &self.final_short_token;
456            if !builder
457                .clone()
458                .token_program(self.token_program.to_account_info())
459                .mint(mint.to_account_info())
460                .decimals(mint.decimals)
461                .ata(ata.to_account_info())
462                .escrow(escrow.to_account_info())
463                .owner(self.receiver.to_account_info())
464                .build()
465                .unchecked_execute()?
466            {
467                return Ok(false);
468            }
469        }
470
471        Ok(true)
472    }
473
474    fn event_authority(&self, bumps: &Self::Bumps) -> (AccountInfo<'info>, u8) {
475        (
476            self.event_authority.to_account_info(),
477            bumps.event_authority,
478        )
479    }
480
481    fn action(&self) -> &AccountLoader<'info, GlvWithdrawal> {
482        &self.glv_withdrawal
483    }
484}
485
486impl<'info> internal::Authentication<'info> for CloseGlvWithdrawal<'info> {
487    fn authority(&self) -> &Signer<'info> {
488        &self.executor
489    }
490
491    fn store(&self) -> &AccountLoader<'info, Store> {
492        &self.store
493    }
494}
495
496/// The accounts definition for [`execute_glv_withdrawal`](crate::gmsol_store::execute_glv_withdrawal) instruction.
497///
498/// Remaining accounts expected by this instruction:
499///
500///   - 0..N. `[]` N market accounts, where N represents the total number of markets managed
501///     by the given GLV.
502///   - N..2N. `[]` N market token accounts (see above for the definition of N).
503///   - 2N..2N+M. `[]` M feed accounts, where M represents the total number of tokens in the
504///     swap params.
505///   - 2N+M..2N+M+L. `[writable]` L market accounts, where L represents the total number of unique
506///     markets excluding the current market in the swap params.
507#[event_cpi]
508#[derive(Accounts)]
509pub struct ExecuteGlvWithdrawal<'info> {
510    /// Authority.
511    pub authority: Signer<'info>,
512    /// Store.
513    #[account(has_one = token_map)]
514    pub store: AccountLoader<'info, Store>,
515    /// Token Map.
516    #[account(has_one = store)]
517    pub token_map: AccountLoader<'info, TokenMapHeader>,
518    /// Oracle buffer to use.
519    #[account(mut, has_one = store)]
520    pub oracle: AccountLoader<'info, Oracle>,
521    /// GLV account.
522    #[account(
523        mut,
524        has_one = store,
525        constraint = glv.load()?.contains(&market_token.key()) @ CoreError::InvalidArgument,
526    )]
527    pub glv: AccountLoader<'info, Glv>,
528    /// Market.
529    #[account(mut, has_one = store)]
530    pub market: AccountLoader<'info, Market>,
531    /// The GLV withdrawal to execute.
532    #[account(
533        mut,
534        constraint = glv_withdrawal.load()?.header.store == store.key() @ CoreError::StoreMismatched,
535        constraint = glv_withdrawal.load()?.header.market == market.key() @ CoreError::MarketMismatched,
536        constraint = glv_withdrawal.load()?.tokens.glv_token() == glv_token.key() @ CoreError::TokenMintMismatched,
537        constraint = glv_withdrawal.load()?.tokens.market_token() == market_token.key() @ CoreError::MarketTokenMintMismatched,
538        constraint = glv_withdrawal.load()?.tokens.market_token_account() == market_token_escrow.key() @ CoreError::MarketTokenAccountMismatched,
539        constraint = glv_withdrawal.load()?.tokens.glv_token_account() == glv_token_escrow.key() @ CoreError::MarketTokenAccountMismatched,
540        constraint = glv_withdrawal.load()?.tokens.final_long_token_account() == final_long_token_escrow.key() @ CoreError::TokenAccountMismatched,
541        constraint = glv_withdrawal.load()?.tokens.final_short_token_account() == final_short_token_escrow.key() @ CoreError::TokenAccountMismatched,
542        seeds = [GlvWithdrawal::SEED, store.key().as_ref(), glv_withdrawal.load()?.header.owner.as_ref(), &glv_withdrawal.load()?.header.nonce],
543        bump = glv_withdrawal.load()?.header.bump,
544    )]
545    pub glv_withdrawal: AccountLoader<'info, GlvWithdrawal>,
546    /// GLV token mint.
547    #[account(mut, constraint = glv.load()?.glv_token == glv_token.key() @ CoreError::TokenMintMismatched)]
548    pub glv_token: Box<InterfaceAccount<'info, token_interface::Mint>>,
549    /// Market token mint.
550    #[account(mut, constraint = market.load()?.meta().market_token_mint == market_token.key() @ CoreError::MarketTokenMintMismatched)]
551    pub market_token: Box<Account<'info, Mint>>,
552    /// Final long token.
553    #[account(
554        constraint = glv_withdrawal.load()?.tokens.final_long_token() == final_long_token.key() @ CoreError::TokenMintMismatched
555    )]
556    pub final_long_token: Box<Account<'info, Mint>>,
557    /// Final short token.
558    #[account(
559        constraint = glv_withdrawal.load()?.tokens.final_short_token() == final_short_token.key() @ CoreError::TokenMintMismatched
560    )]
561    pub final_short_token: Box<Account<'info, Mint>>,
562    /// The escrow account for GLV tokens.
563    #[account(
564        mut,
565        associated_token::mint = glv_token,
566        associated_token::authority = glv_withdrawal,
567        associated_token::token_program = glv_token_program,
568    )]
569    pub glv_token_escrow: Box<InterfaceAccount<'info, token_interface::TokenAccount>>,
570    /// The escrow account for market tokens.
571    #[account(
572        mut,
573        associated_token::mint = market_token,
574        associated_token::authority = glv_withdrawal,
575    )]
576    pub market_token_escrow: Box<Account<'info, TokenAccount>>,
577    /// The escrow account for receiving final long token for withdrawal.
578    #[account(
579        mut,
580        associated_token::mint = final_long_token,
581        associated_token::authority = glv_withdrawal,
582    )]
583    pub final_long_token_escrow: Box<Account<'info, TokenAccount>>,
584    /// The escrow account for receiving final short token for withdrawal.
585    #[account(
586        mut,
587        associated_token::mint = final_short_token,
588        associated_token::authority = glv_withdrawal,
589    )]
590    pub final_short_token_escrow: Box<Account<'info, TokenAccount>>,
591    /// Market token wihtdrawal vault.
592    #[account(
593        mut,
594        token::mint = market_token,
595        token::authority = store,
596        seeds = [
597            constants::MARKET_VAULT_SEED,
598            store.key().as_ref(),
599            market_token_withdrawal_vault.mint.as_ref(),
600        ],
601        bump,
602    )]
603    pub market_token_withdrawal_vault: Box<Account<'info, TokenAccount>>,
604    /// Final long token vault.
605    #[account(
606        mut,
607        token::mint = final_long_token,
608        token::authority = store,
609        seeds = [
610            constants::MARKET_VAULT_SEED,
611            store.key().as_ref(),
612            final_long_token_vault.mint.as_ref(),
613        ],
614        bump,
615    )]
616    pub final_long_token_vault: Box<Account<'info, TokenAccount>>,
617    /// Final short token vault.
618    #[account(
619        mut,
620        token::mint = final_short_token,
621        token::authority = store,
622        seeds = [
623            constants::MARKET_VAULT_SEED,
624            store.key().as_ref(),
625            final_short_token_vault.mint.as_ref(),
626        ],
627        bump,
628    )]
629    pub final_short_token_vault: Box<Account<'info, TokenAccount>>,
630    /// Market token vault for the GLV.
631    #[account(
632        mut,
633        associated_token::mint = market_token,
634        associated_token::authority = glv,
635    )]
636    pub market_token_vault: Box<Account<'info, TokenAccount>>,
637    /// The token program.
638    pub token_program: Program<'info, Token>,
639    /// The token program for GLV token.
640    pub glv_token_program: Program<'info, Token2022>,
641    /// The system program.
642    pub system_program: Program<'info, System>,
643    /// Chainlink Program.
644    pub chainlink_program: Option<Program<'info, Chainlink>>,
645}
646
647/// Execute GLV withdrawal.
648///
649/// # CHECK
650/// - Only ORDER_KEEPER is allowed to call this function.
651pub(crate) fn unchecked_execute_glv_withdrawal<'info>(
652    ctx: Context<'_, '_, 'info, 'info, ExecuteGlvWithdrawal<'info>>,
653    execution_lamports: u64,
654    throw_on_execution_error: bool,
655) -> Result<()> {
656    let accounts = ctx.accounts;
657    let remaining_accounts = ctx.remaining_accounts;
658
659    // Validate feature enabled.
660    accounts.store.load()?.validate_feature_enabled(
661        DomainDisabledFlag::GlvWithdrawal,
662        ActionDisabledFlag::Execute,
663    )?;
664
665    let splitted = {
666        let glv_withdrawal = accounts.glv_withdrawal.load()?;
667        let token_map = accounts.token_map.load_token_map()?;
668        accounts.glv.load()?.validate_and_split_remaining_accounts(
669            &accounts.store.key(),
670            remaining_accounts,
671            Some(&*glv_withdrawal),
672            &token_map,
673        )?
674    };
675
676    let event_authority = accounts.event_authority.clone();
677    let event_emitter = EventEmitter::new(&event_authority, ctx.bumps.event_authority);
678
679    let executed =
680        accounts.perform_execution(&splitted, throw_on_execution_error, &event_emitter)?;
681
682    match executed {
683        Some((final_long_token_amount, final_short_token_amount)) => {
684            accounts.glv_withdrawal.load_mut()?.header.completed()?;
685            accounts.transfer_tokens_out(
686                splitted.remaining_accounts,
687                final_long_token_amount,
688                final_short_token_amount,
689                &event_emitter,
690            )?;
691        }
692        None => {
693            accounts.glv_withdrawal.load_mut()?.header.cancelled()?;
694        }
695    }
696
697    // It must be placed at the end to be executed correctly.
698    accounts.pay_execution_fee(execution_lamports)?;
699
700    Ok(())
701}
702
703impl<'info> internal::Authentication<'info> for ExecuteGlvWithdrawal<'info> {
704    fn authority(&self) -> &Signer<'info> {
705        &self.authority
706    }
707
708    fn store(&self) -> &AccountLoader<'info, Store> {
709        &self.store
710    }
711}
712
713impl<'info> ExecuteGlvWithdrawal<'info> {
714    #[inline(never)]
715    fn pay_execution_fee(&self, execution_fee: u64) -> Result<()> {
716        let execution_lamports = self
717            .glv_withdrawal
718            .load()?
719            .execution_lamports(execution_fee);
720        PayExecutionFeeOperation::builder()
721            .payer(self.glv_withdrawal.to_account_info())
722            .receiver(self.authority.to_account_info())
723            .execution_lamports(execution_lamports)
724            .build()
725            .execute()?;
726        Ok(())
727    }
728
729    #[inline(never)]
730    fn perform_execution(
731        &mut self,
732        splitted: &SplitAccountsForGlv<'info>,
733        throw_on_execution_error: bool,
734        event_emitter: &EventEmitter<'_, 'info>,
735    ) -> Result<Option<(u64, u64)>> {
736        let builder = ExecuteGlvWithdrawalOperation::builder()
737            .glv_withdrawal(self.glv_withdrawal.clone())
738            .token_program(self.token_program.to_account_info())
739            .glv_token_program(self.glv_token_program.to_account_info())
740            .throw_on_execution_error(throw_on_execution_error)
741            .store(self.store.clone())
742            .glv(&self.glv)
743            .glv_token_mint(&mut self.glv_token)
744            .glv_token_account(self.glv_token_escrow.to_account_info())
745            .market(self.market.clone())
746            .market_token_mint(&mut self.market_token)
747            .market_token_glv_vault(&self.market_token_vault)
748            .market_token_withdrawal_vault(self.market_token_withdrawal_vault.to_account_info())
749            .markets(splitted.markets)
750            .market_tokens(splitted.market_tokens)
751            .event_emitter(*event_emitter);
752
753        self.oracle.load_mut()?.with_prices(
754            &self.store,
755            &self.token_map,
756            &splitted.tokens,
757            splitted.remaining_accounts,
758            self.chainlink_program.as_ref(),
759            |oracle, remaining_accounts| {
760                builder
761                    .oracle(oracle)
762                    .remaining_accounts(remaining_accounts)
763                    .build()
764                    .unchecked_execute()
765            },
766        )
767    }
768
769    fn transfer_tokens_out(
770        &self,
771        remaining_accounts: &'info [AccountInfo<'info>],
772        final_long_token_amount: u64,
773        final_short_token_amount: u64,
774        event_emitter: &EventEmitter<'_, 'info>,
775    ) -> Result<()> {
776        let builder = MarketTransferOutOperation::builder()
777            .store(&self.store)
778            .token_program(self.token_program.to_account_info())
779            .event_emitter(*event_emitter);
780        let store = &self.store.key();
781
782        if final_long_token_amount != 0 {
783            let market = self
784                .glv_withdrawal
785                .load()?
786                .swap
787                .find_and_unpack_last_market(store, true, remaining_accounts)?
788                .unwrap_or(self.market.clone());
789            let vault = &self.final_long_token_vault;
790            let escrow = &self.final_long_token_escrow;
791            let token = &self.final_long_token;
792            builder
793                .clone()
794                .market(&market)
795                .to(escrow.to_account_info())
796                .vault(vault.to_account_info())
797                .amount(final_long_token_amount)
798                .decimals(token.decimals)
799                .token_mint(token.to_account_info())
800                .build()
801                .execute()?;
802        }
803
804        if final_short_token_amount != 0 {
805            let market = self
806                .glv_withdrawal
807                .load()?
808                .swap
809                .find_and_unpack_last_market(store, false, remaining_accounts)?
810                .unwrap_or(self.market.clone());
811            let vault = &self.final_short_token_vault;
812            let escrow = &self.final_short_token_escrow;
813            let token = &self.final_short_token;
814            builder
815                .market(&market)
816                .to(escrow.to_account_info())
817                .vault(vault.to_account_info())
818                .amount(final_short_token_amount)
819                .decimals(token.decimals)
820                .token_mint(token.to_account_info())
821                .build()
822                .execute()?;
823        }
824        Ok(())
825    }
826}