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#[derive(Accounts)]
42#[instruction(nonce: [u8; 32])]
43pub struct CreateGlvWithdrawal<'info> {
44 #[account(mut)]
46 pub owner: Signer<'info>,
47 pub receiver: UncheckedAccount<'info>,
50 pub store: AccountLoader<'info, Store>,
52 #[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 #[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 #[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 pub glv_token: Box<InterfaceAccount<'info, token_interface::Mint>>,
77 pub market_token: Box<Account<'info, Mint>>,
79 pub final_long_token: Box<Account<'info, Mint>>,
81 pub final_short_token: Box<Account<'info, Mint>>,
83 #[account(mut, token::mint = glv_token)]
85 pub glv_token_source: Box<InterfaceAccount<'info, token_interface::TokenAccount>>,
86 #[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 #[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 #[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 #[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 pub system_program: Program<'info, System>,
117 pub token_program: Program<'info, Token>,
119 pub glv_token_program: Program<'info, Token2022>,
121 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#[event_cpi]
212#[derive(Accounts)]
213pub struct CloseGlvWithdrawal<'info> {
214 pub executor: Signer<'info>,
216 pub store: AccountLoader<'info, Store>,
218 #[account(mut, seeds = [Store::WALLET_SEED, store.key().as_ref()], bump)]
220 pub store_wallet: SystemAccount<'info>,
221 #[account(mut)]
224 pub owner: UncheckedAccount<'info>,
225 #[account(mut)]
228 pub receiver: UncheckedAccount<'info>,
229 #[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 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 #[account(
247 constraint = glv_withdrawal.load()?.tokens.market_token() == market_token.key() @ CoreError::MarketTokenMintMismatched
248 )]
249 pub market_token: Box<Account<'info, Mint>>,
250 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 pub system_program: Program<'info, System>,
324 pub token_program: Program<'info, Token>,
326 pub glv_token_program: Program<'info, Token2022>,
328 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 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 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 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 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 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 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#[event_cpi]
508#[derive(Accounts)]
509pub struct ExecuteGlvWithdrawal<'info> {
510 pub authority: Signer<'info>,
512 #[account(has_one = token_map)]
514 pub store: AccountLoader<'info, Store>,
515 #[account(has_one = store)]
517 pub token_map: AccountLoader<'info, TokenMapHeader>,
518 #[account(mut, has_one = store)]
520 pub oracle: AccountLoader<'info, Oracle>,
521 #[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 #[account(mut, has_one = store)]
530 pub market: AccountLoader<'info, Market>,
531 #[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 #[account(mut, constraint = glv.load()?.glv_token == glv_token.key() @ CoreError::TokenMintMismatched)]
548 pub glv_token: Box<InterfaceAccount<'info, token_interface::Mint>>,
549 #[account(mut, constraint = market.load()?.meta().market_token_mint == market_token.key() @ CoreError::MarketTokenMintMismatched)]
551 pub market_token: Box<Account<'info, Mint>>,
552 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 pub token_program: Program<'info, Token>,
639 pub glv_token_program: Program<'info, Token2022>,
641 pub system_program: Program<'info, System>,
643 pub chainlink_program: Option<Program<'info, Chainlink>>,
645}
646
647pub(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 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 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}