gmsol/store/glv/
withdrawal.rs

1use std::{
2    collections::{BTreeSet, HashMap},
3    ops::Deref,
4};
5
6use anchor_client::{
7    anchor_lang::{prelude::AccountMeta, system_program},
8    solana_sdk::{address_lookup_table::AddressLookupTableAccount, pubkey::Pubkey, signer::Signer},
9};
10use anchor_spl::associated_token::get_associated_token_address_with_program_id;
11use gmsol_solana_utils::{
12    bundle_builder::{BundleBuilder, BundleOptions},
13    compute_budget::ComputeBudget,
14    transaction_builder::TransactionBuilder,
15};
16use gmsol_store::{
17    accounts, instruction,
18    ops::glv::CreateGlvWithdrawalParams,
19    states::{
20        common::{action::Action, swap::SwapActionParams, TokensWithFeed},
21        glv::GlvWithdrawal,
22        Glv, HasMarketMeta, NonceBytes, PriceProviderKind, TokenMapAccess,
23    },
24};
25
26use crate::{
27    exchange::{generate_nonce, get_ata_or_owner_with_program_id},
28    store::{token::TokenAccountOps, utils::FeedsParser},
29    utils::{
30        builder::{
31            FeedAddressMap, FeedIds, MakeBundleBuilder, PullOraclePriceConsumer, SetExecutionFee,
32        },
33        fix_optional_account_metas, ZeroCopy,
34    },
35};
36
37use super::{split_to_accounts, GlvOps};
38
39pub const EXECUTE_GLV_WITHDRAWAL_COMPUTE_BUDGET: u32 = 800_000;
40
41/// Create GLV withdrawal builder.
42pub struct CreateGlvWithdrawalBuilder<'a, C> {
43    client: &'a crate::Client<C>,
44    store: Pubkey,
45    glv_token: Pubkey,
46    market_token: Pubkey,
47    final_long_token: Option<Pubkey>,
48    final_short_token: Option<Pubkey>,
49    long_token_swap_path: Vec<Pubkey>,
50    short_token_swap_path: Vec<Pubkey>,
51    glv_token_amount: u64,
52    min_final_long_token_amount: u64,
53    min_final_short_token_amount: u64,
54    max_execution_lamports: u64,
55    nonce: Option<NonceBytes>,
56    glv_token_source: Option<Pubkey>,
57    hint: Option<CreateGlvWithdrawalHint>,
58    should_unwrap_native_token: bool,
59    receiver: Pubkey,
60}
61
62/// Hint for [`CreateGlvWithdrawalBuilder`]
63#[derive(Clone)]
64pub struct CreateGlvWithdrawalHint {
65    long_token_mint: Pubkey,
66    short_token_mint: Pubkey,
67}
68
69impl CreateGlvWithdrawalHint {
70    /// Create from market meta.
71    pub fn new(meta: &impl HasMarketMeta) -> Self {
72        let meta = meta.market_meta();
73        Self {
74            long_token_mint: meta.long_token_mint,
75            short_token_mint: meta.short_token_mint,
76        }
77    }
78}
79
80impl<'a, C: Deref<Target = impl Signer> + Clone> CreateGlvWithdrawalBuilder<'a, C> {
81    pub(super) fn new(
82        client: &'a crate::Client<C>,
83        store: Pubkey,
84        glv_token: Pubkey,
85        market_token: Pubkey,
86        amount: u64,
87    ) -> Self {
88        Self {
89            client,
90            store,
91            glv_token,
92            market_token,
93            final_long_token: None,
94            final_short_token: None,
95            long_token_swap_path: vec![],
96            short_token_swap_path: vec![],
97            glv_token_amount: amount,
98            min_final_long_token_amount: 0,
99            min_final_short_token_amount: 0,
100            max_execution_lamports: GlvWithdrawal::MIN_EXECUTION_LAMPORTS,
101            nonce: None,
102            glv_token_source: None,
103            hint: None,
104            should_unwrap_native_token: true,
105            receiver: client.payer(),
106        }
107    }
108
109    /// Set the nonce.
110    pub fn nonce(&mut self, nonce: NonceBytes) -> &mut Self {
111        self.nonce = Some(nonce);
112        self
113    }
114
115    /// Set max execution fee allowed to use.
116    pub fn max_execution_fee(&mut self, lamports: u64) -> &mut Self {
117        self.max_execution_lamports = lamports;
118        self
119    }
120
121    /// Final long token config.
122    pub fn final_long_token(
123        &mut self,
124        token: Option<&Pubkey>,
125        min_amount: u64,
126        swap_path: Vec<Pubkey>,
127    ) -> &mut Self {
128        self.final_long_token = token.copied();
129        self.min_final_long_token_amount = min_amount;
130        self.long_token_swap_path = swap_path;
131        self
132    }
133
134    /// Final short token config.
135    pub fn final_short_token(
136        &mut self,
137        token: Option<&Pubkey>,
138        min_amount: u64,
139        swap_path: Vec<Pubkey>,
140    ) -> &mut Self {
141        self.final_short_token = token.copied();
142        self.min_final_short_token_amount = min_amount;
143        self.short_token_swap_path = swap_path;
144        self
145    }
146
147    /// Set GLV token source.
148    pub fn glv_token_source(&mut self, address: &Pubkey) -> &mut Self {
149        self.glv_token_source = Some(*address);
150        self
151    }
152
153    /// Set hint.
154    pub fn hint(&mut self, hint: CreateGlvWithdrawalHint) -> &mut Self {
155        self.hint = Some(hint);
156        self
157    }
158
159    /// Set whether to unwrap native token.
160    /// Defaults to should unwrap.
161    pub fn should_unwrap_native_token(&mut self, should_unwrap: bool) -> &mut Self {
162        self.should_unwrap_native_token = should_unwrap;
163        self
164    }
165
166    /// Set receiver.
167    /// Defaults to the payer.
168    pub fn receiver(&mut self, receiver: Option<Pubkey>) -> &mut Self {
169        self.receiver = receiver.unwrap_or(self.client.payer());
170        self
171    }
172
173    fn market_address(&self) -> Pubkey {
174        self.client
175            .find_market_address(&self.store, &self.market_token)
176    }
177
178    /// Prepare hint.
179    pub async fn prepare_hint(&mut self) -> crate::Result<CreateGlvWithdrawalHint> {
180        match &self.hint {
181            Some(hint) => Ok(hint.clone()),
182            None => {
183                let market = self.market_address();
184                let market = self.client.market(&market).await?;
185                let hint = CreateGlvWithdrawalHint::new(&*market);
186                self.hint = Some(hint.clone());
187                Ok(hint)
188            }
189        }
190    }
191
192    /// Build.
193    pub async fn build_with_address(
194        &mut self,
195    ) -> crate::Result<(TransactionBuilder<'a, C>, Pubkey)> {
196        let hint = self.prepare_hint().await?;
197
198        let nonce = self.nonce.unwrap_or_else(generate_nonce);
199        let owner = self.client.payer();
200        let receiver = self.receiver;
201        let glv_withdrawal = self
202            .client
203            .find_glv_withdrawal_address(&self.store, &owner, &nonce);
204        let market = self.market_address();
205        let glv = self.client.find_glv_address(&self.glv_token);
206        let token_program_id = anchor_spl::token::ID;
207        let glv_token_program_id = anchor_spl::token_2022::ID;
208
209        let final_long_token = self.final_long_token.unwrap_or(hint.long_token_mint);
210        let final_short_token = self.final_short_token.unwrap_or(hint.short_token_mint);
211
212        let glv_token_source = self.glv_token_source.unwrap_or_else(|| {
213            get_associated_token_address_with_program_id(
214                &owner,
215                &self.glv_token,
216                &glv_token_program_id,
217            )
218        });
219
220        let glv_token_escrow = get_associated_token_address_with_program_id(
221            &glv_withdrawal,
222            &self.glv_token,
223            &glv_token_program_id,
224        );
225        let market_token_escrow = get_associated_token_address_with_program_id(
226            &glv_withdrawal,
227            &self.market_token,
228            &token_program_id,
229        );
230        let final_long_token_escrow = get_associated_token_address_with_program_id(
231            &glv_withdrawal,
232            &final_long_token,
233            &token_program_id,
234        );
235        let final_short_token_escrow = get_associated_token_address_with_program_id(
236            &glv_withdrawal,
237            &final_short_token,
238            &token_program_id,
239        );
240
241        // Prepare the ATA for receiving final long tokens.
242        let mut prepare = self.client.prepare_associated_token_account(
243            &final_long_token,
244            &token_program_id,
245            Some(&receiver),
246        );
247
248        // Prepare the ATA for receiving final short tokens.
249        prepare = prepare.merge(self.client.prepare_associated_token_account(
250            &final_short_token,
251            &token_program_id,
252            Some(&receiver),
253        ));
254
255        // Prepare the escrow account for GLV tokens.
256        prepare = prepare.merge(self.client.prepare_associated_token_account(
257            &self.glv_token,
258            &glv_token_program_id,
259            Some(&glv_withdrawal),
260        ));
261
262        // Prepare the escrow account for market tokens.
263        prepare = prepare.merge(self.client.prepare_associated_token_account(
264            &self.market_token,
265            &token_program_id,
266            Some(&glv_withdrawal),
267        ));
268
269        // Prepare the escrow account for final long tokens.
270        prepare = prepare.merge(self.client.prepare_associated_token_account(
271            &final_long_token,
272            &token_program_id,
273            Some(&glv_withdrawal),
274        ));
275        // Prepare the escrow account for final long tokens.
276        prepare = prepare.merge(self.client.prepare_associated_token_account(
277            &final_short_token,
278            &token_program_id,
279            Some(&glv_withdrawal),
280        ));
281
282        let create = self
283            .client
284            .store_transaction()
285            .anchor_accounts(accounts::CreateGlvWithdrawal {
286                owner,
287                receiver,
288                store: self.store,
289                market,
290                glv,
291                glv_withdrawal,
292                glv_token: self.glv_token,
293                market_token: self.market_token,
294                final_long_token,
295                final_short_token,
296                glv_token_source,
297                glv_token_escrow,
298                market_token_escrow,
299                final_long_token_escrow,
300                final_short_token_escrow,
301                system_program: system_program::ID,
302                token_program: token_program_id,
303                glv_token_program: glv_token_program_id,
304                associated_token_program: anchor_spl::associated_token::ID,
305            })
306            .anchor_args(instruction::CreateGlvWithdrawal {
307                nonce,
308                params: CreateGlvWithdrawalParams {
309                    execution_lamports: self.max_execution_lamports,
310                    long_token_swap_length: self
311                        .long_token_swap_path
312                        .len()
313                        .try_into()
314                        .map_err(|_| crate::Error::invalid_argument("swap path too long"))?,
315                    short_token_swap_length: self
316                        .short_token_swap_path
317                        .len()
318                        .try_into()
319                        .map_err(|_| crate::Error::invalid_argument("swap path too long"))?,
320                    glv_token_amount: self.glv_token_amount,
321                    min_final_long_token_amount: self.min_final_long_token_amount,
322                    min_final_short_token_amount: self.min_final_short_token_amount,
323                    should_unwrap_native_token: self.should_unwrap_native_token,
324                },
325            })
326            .accounts(
327                self.long_token_swap_path
328                    .iter()
329                    .chain(self.short_token_swap_path.iter())
330                    .map(|token| AccountMeta {
331                        pubkey: self.client.find_market_address(&self.store, token),
332                        is_signer: false,
333                        is_writable: false,
334                    })
335                    .collect::<Vec<_>>(),
336            );
337
338        Ok((prepare.merge(create), glv_withdrawal))
339    }
340}
341
342/// Close GLV withdrawal builder.
343pub struct CloseGlvWithdrawalBuilder<'a, C> {
344    client: &'a crate::Client<C>,
345    glv_withdrawal: Pubkey,
346    reason: String,
347    hint: Option<CloseGlvWithdrawalHint>,
348}
349
350/// Hint for [`CloseGlvWithdrawalBuilder`].
351#[derive(Clone)]
352pub struct CloseGlvWithdrawalHint {
353    store: Pubkey,
354    owner: Pubkey,
355    receiver: Pubkey,
356    glv_token: Pubkey,
357    market_token: Pubkey,
358    final_long_token: Pubkey,
359    final_short_token: Pubkey,
360    market_token_escrow: Pubkey,
361    final_long_token_escrow: Pubkey,
362    final_short_token_escrow: Pubkey,
363    glv_token_escrow: Pubkey,
364    should_unwrap_native_token: bool,
365}
366
367impl CloseGlvWithdrawalHint {
368    /// Create from the GLV withdrawal.
369    pub fn new(glv_withdrawal: &GlvWithdrawal) -> Self {
370        Self {
371            store: *glv_withdrawal.header().store(),
372            owner: *glv_withdrawal.header().owner(),
373            receiver: glv_withdrawal.header().receiver(),
374            glv_token: glv_withdrawal.tokens().glv_token(),
375            market_token: glv_withdrawal.tokens().market_token(),
376            final_long_token: glv_withdrawal.tokens().final_long_token(),
377            final_short_token: glv_withdrawal.tokens().final_short_token(),
378            market_token_escrow: glv_withdrawal.tokens().market_token_account(),
379            final_long_token_escrow: glv_withdrawal.tokens().final_long_token_account(),
380            final_short_token_escrow: glv_withdrawal.tokens().final_short_token_account(),
381            glv_token_escrow: glv_withdrawal.tokens().glv_token_account(),
382            should_unwrap_native_token: glv_withdrawal.header().should_unwrap_native_token(),
383        }
384    }
385}
386
387impl<'a, C: Deref<Target = impl Signer> + Clone> CloseGlvWithdrawalBuilder<'a, C> {
388    pub(super) fn new(client: &'a crate::Client<C>, glv_withdrawal: Pubkey) -> Self {
389        Self {
390            client,
391            glv_withdrawal,
392            reason: "cancelled".to_string(),
393            hint: None,
394        }
395    }
396
397    /// Set hint.
398    pub fn hint(&mut self, hint: CloseGlvWithdrawalHint) -> &mut Self {
399        self.hint = Some(hint);
400        self
401    }
402
403    /// Set reason.
404    pub fn reason(&mut self, reason: impl ToString) -> &mut Self {
405        self.reason = reason.to_string();
406        self
407    }
408
409    async fn prepare_hint(&mut self) -> crate::Result<CloseGlvWithdrawalHint> {
410        match &self.hint {
411            Some(hint) => Ok(hint.clone()),
412            None => {
413                let glv_deposit = self
414                    .client
415                    .account::<ZeroCopy<GlvWithdrawal>>(&self.glv_withdrawal)
416                    .await?
417                    .ok_or(crate::Error::NotFound)?
418                    .0;
419                let hint = CloseGlvWithdrawalHint::new(&glv_deposit);
420                self.hint = Some(hint.clone());
421                Ok(hint)
422            }
423        }
424    }
425
426    /// Build.
427    pub async fn build(&mut self) -> crate::Result<TransactionBuilder<'a, C>> {
428        let hint = self.prepare_hint().await?;
429
430        let token_program_id = anchor_spl::token::ID;
431        let glv_token_program_id = anchor_spl::token_2022::ID;
432
433        let payer = self.client.payer();
434
435        let market_token_ata = get_associated_token_address_with_program_id(
436            &hint.owner,
437            &hint.market_token,
438            &token_program_id,
439        );
440        let glv_token_ata = get_associated_token_address_with_program_id(
441            &hint.owner,
442            &hint.glv_token,
443            &glv_token_program_id,
444        );
445        let final_long_token_ata = get_ata_or_owner_with_program_id(
446            &hint.receiver,
447            &hint.final_long_token,
448            hint.should_unwrap_native_token,
449            &token_program_id,
450        );
451        let final_short_token_ata = get_ata_or_owner_with_program_id(
452            &hint.receiver,
453            &hint.final_short_token,
454            hint.should_unwrap_native_token,
455            &token_program_id,
456        );
457
458        let rpc = self
459            .client
460            .store_transaction()
461            .anchor_accounts(accounts::CloseGlvWithdrawal {
462                executor: payer,
463                store: hint.store,
464                store_wallet: self.client.find_store_wallet_address(&hint.store),
465                owner: hint.owner,
466                receiver: hint.receiver,
467                glv_withdrawal: self.glv_withdrawal,
468                market_token: hint.market_token,
469                final_long_token: hint.final_long_token,
470                final_short_token: hint.final_short_token,
471                glv_token: hint.glv_token,
472                market_token_escrow: hint.market_token_escrow,
473                final_long_token_escrow: hint.final_long_token_escrow,
474                final_short_token_escrow: hint.final_short_token_escrow,
475                market_token_ata,
476                final_long_token_ata,
477                final_short_token_ata,
478                glv_token_escrow: hint.glv_token_escrow,
479                glv_token_ata,
480                system_program: system_program::ID,
481                token_program: token_program_id,
482                glv_token_program: glv_token_program_id,
483                associated_token_program: anchor_spl::associated_token::ID,
484                event_authority: self.client.store_event_authority(),
485                program: *self.client.store_program_id(),
486            })
487            .anchor_args(instruction::CloseGlvWithdrawal {
488                reason: self.reason.clone(),
489            });
490        Ok(rpc)
491    }
492}
493
494/// Execute GLV withdrawal builder.
495pub struct ExecuteGlvWithdrawalBuilder<'a, C> {
496    client: &'a crate::Client<C>,
497    oracle: Pubkey,
498    glv_withdrawal: Pubkey,
499    execution_lamports: u64,
500    cancel_on_execution_error: bool,
501    hint: Option<ExecuteGlvWithdrawalHint>,
502    token_map: Option<Pubkey>,
503    feeds_parser: FeedsParser,
504    close: bool,
505    alts: HashMap<Pubkey, Vec<Pubkey>>,
506}
507
508/// Hint for [`ExecuteGlvWithdrawalBuilder`].
509#[derive(Clone)]
510pub struct ExecuteGlvWithdrawalHint {
511    close: CloseGlvWithdrawalHint,
512    token_map: Pubkey,
513    glv_market_tokens: BTreeSet<Pubkey>,
514    swap: SwapActionParams,
515    /// Feeds.
516    pub feeds: TokensWithFeed,
517}
518
519impl Deref for ExecuteGlvWithdrawalHint {
520    type Target = CloseGlvWithdrawalHint;
521
522    fn deref(&self) -> &Self::Target {
523        &self.close
524    }
525}
526
527impl ExecuteGlvWithdrawalHint {
528    /// Create a new hint.
529    pub fn new(
530        glv: &Glv,
531        glv_withdrawal: &GlvWithdrawal,
532        token_map_address: &Pubkey,
533        token_map: &impl TokenMapAccess,
534        index_tokens: impl IntoIterator<Item = Pubkey>,
535    ) -> crate::Result<Self> {
536        let glv_market_tokens = glv.market_tokens().collect();
537        let mut collector = glv.tokens_collector(Some(glv_withdrawal));
538        for token in index_tokens {
539            collector.insert_token(&token);
540        }
541        let close = CloseGlvWithdrawalHint::new(glv_withdrawal);
542        Ok(Self {
543            close,
544            token_map: *token_map_address,
545            glv_market_tokens,
546            swap: *glv_withdrawal.swap(),
547            feeds: collector.to_feeds(token_map)?,
548        })
549    }
550}
551
552impl<'a, C: Deref<Target = impl Signer> + Clone> ExecuteGlvWithdrawalBuilder<'a, C> {
553    pub(super) fn new(
554        client: &'a crate::Client<C>,
555        oracle: Pubkey,
556        glv_withdrawal: Pubkey,
557        cancel_on_execution_error: bool,
558    ) -> Self {
559        Self {
560            client,
561            oracle,
562            glv_withdrawal,
563            execution_lamports: 0,
564            cancel_on_execution_error,
565            hint: None,
566            token_map: None,
567            feeds_parser: Default::default(),
568            close: true,
569            alts: Default::default(),
570        }
571    }
572
573    /// Set hint.
574    pub fn hint(&mut self, hint: ExecuteGlvWithdrawalHint) -> &mut Self {
575        self.hint = Some(hint);
576        self
577    }
578
579    /// Set token map address.
580    pub fn token_map(&mut self, address: &Pubkey) -> &mut Self {
581        self.token_map = Some(*address);
582        self
583    }
584
585    /// Set whether to close the GLV deposit after execution.
586    pub fn close(&mut self, close: bool) -> &mut Self {
587        self.close = close;
588        self
589    }
590
591    /// Parse feeds with the given price udpates map.
592    #[cfg(feature = "pyth-pull-oracle")]
593    pub fn parse_with_pyth_price_updates(
594        &mut self,
595        price_updates: crate::pyth::pull_oracle::Prices,
596    ) -> &mut Self {
597        self.feeds_parser.with_pyth_price_updates(price_updates);
598        self
599    }
600
601    /// Insert an Address Lookup Table.
602    pub fn add_alt(&mut self, account: AddressLookupTableAccount) -> &mut Self {
603        self.alts.insert(account.key, account.addresses);
604        self
605    }
606
607    /// Prepare hint.
608    pub async fn prepare_hint(&mut self) -> crate::Result<ExecuteGlvWithdrawalHint> {
609        match &self.hint {
610            Some(hint) => Ok(hint.clone()),
611            None => {
612                let glv_deposit = self
613                    .client
614                    .account::<ZeroCopy<GlvWithdrawal>>(&self.glv_withdrawal)
615                    .await?
616                    .ok_or(crate::Error::NotFound)?
617                    .0;
618
619                let glv_address = self
620                    .client
621                    .find_glv_address(&glv_deposit.tokens().glv_token());
622                let glv = self
623                    .client
624                    .account::<ZeroCopy<Glv>>(&glv_address)
625                    .await?
626                    .ok_or(crate::Error::NotFound)?
627                    .0;
628
629                let mut index_tokens = Vec::with_capacity(glv.num_markets());
630                for token in glv.market_tokens() {
631                    let market = self.client.find_market_address(glv.store(), &token);
632                    let market = self.client.market(&market).await?;
633                    index_tokens.push(market.meta().index_token_mint);
634                }
635
636                let store = glv_deposit.header().store();
637                let token_map_address = self
638                    .client
639                    .authorized_token_map_address(store)
640                    .await?
641                    .ok_or(crate::Error::NotFound)?;
642                let token_map = self.client.token_map(&token_map_address).await?;
643                let hint = ExecuteGlvWithdrawalHint::new(
644                    &glv,
645                    &glv_deposit,
646                    &token_map_address,
647                    &token_map,
648                    index_tokens,
649                )?;
650                self.hint = Some(hint.clone());
651                Ok(hint)
652            }
653        }
654    }
655}
656
657#[cfg(feature = "pyth-pull-oracle")]
658mod pyth {
659    use crate::pyth::{
660        pull_oracle::{ExecuteWithPythPrices, Prices},
661        PythPullOracleContext,
662    };
663
664    use super::*;
665
666    impl<'a, C: Deref<Target = impl Signer> + Clone> ExecuteWithPythPrices<'a, C>
667        for ExecuteGlvWithdrawalBuilder<'a, C>
668    {
669        fn set_execution_fee(&mut self, lamports: u64) {
670            SetExecutionFee::set_execution_fee(self, lamports);
671        }
672
673        async fn context(&mut self) -> crate::Result<PythPullOracleContext> {
674            let hint = self.prepare_hint().await?;
675            let ctx = PythPullOracleContext::try_from_feeds(&hint.feeds)?;
676            Ok(ctx)
677        }
678
679        async fn build_rpc_with_price_updates(
680            &mut self,
681            price_updates: Prices,
682        ) -> crate::Result<Vec<TransactionBuilder<'a, C, ()>>> {
683            let txn = self
684                .parse_with_pyth_price_updates(price_updates)
685                .build()
686                .await?;
687            Ok(txn.into_builders())
688        }
689    }
690}
691
692impl<'a, C: Deref<Target = impl Signer> + Clone> MakeBundleBuilder<'a, C>
693    for ExecuteGlvWithdrawalBuilder<'a, C>
694{
695    async fn build_with_options(
696        &mut self,
697        options: BundleOptions,
698    ) -> gmsol_solana_utils::Result<BundleBuilder<'a, C>> {
699        let hint = self
700            .prepare_hint()
701            .await
702            .map_err(gmsol_solana_utils::Error::custom)?;
703
704        let token_program_id = anchor_spl::token::ID;
705        let glv_token_program_id = anchor_spl::token_2022::ID;
706
707        let authority = self.client.payer();
708        let glv = self.client.find_glv_address(&hint.glv_token);
709        let market = self
710            .client
711            .find_market_address(&hint.close.store, &hint.market_token);
712
713        let feeds = self
714            .feeds_parser
715            .parse(&hint.feeds)
716            .collect::<Result<Vec<_>, _>>()
717            .map_err(gmsol_solana_utils::Error::custom)?;
718        let markets = hint
719            .swap
720            .unique_market_tokens_excluding_current(&hint.market_token)
721            .map(|mint| AccountMeta {
722                pubkey: self.client.find_market_address(&hint.store, mint),
723                is_signer: false,
724                is_writable: true,
725            });
726
727        let glv_accounts = split_to_accounts(
728            hint.glv_market_tokens.iter().copied(),
729            &glv,
730            &hint.store,
731            self.client.store_program_id(),
732            &token_program_id,
733            false,
734        )
735        .0;
736
737        let final_long_token_vault = self
738            .client
739            .find_market_vault_address(&hint.store, &hint.final_long_token);
740        let final_short_token_vault = self
741            .client
742            .find_market_vault_address(&hint.store, &hint.final_short_token);
743
744        let market_token_vault = get_associated_token_address_with_program_id(
745            &glv,
746            &hint.market_token,
747            &token_program_id,
748        );
749
750        let market_token_withdrawal_vault = self
751            .client
752            .find_market_vault_address(&hint.store, &hint.market_token);
753
754        let execute = self
755            .client
756            .store_transaction()
757            .accounts(fix_optional_account_metas(
758                accounts::ExecuteGlvWithdrawal {
759                    authority,
760                    store: hint.store,
761                    token_map: hint.token_map,
762                    oracle: self.oracle,
763                    glv,
764                    market,
765                    glv_withdrawal: self.glv_withdrawal,
766                    glv_token: hint.glv_token,
767                    market_token: hint.market_token,
768                    final_long_token: hint.final_long_token,
769                    final_short_token: hint.final_short_token,
770                    glv_token_escrow: hint.glv_token_escrow,
771                    market_token_escrow: hint.market_token_escrow,
772                    final_long_token_escrow: hint.final_long_token_escrow,
773                    final_short_token_escrow: hint.final_short_token_escrow,
774                    market_token_withdrawal_vault,
775                    final_long_token_vault,
776                    final_short_token_vault,
777                    market_token_vault,
778                    token_program: token_program_id,
779                    glv_token_program: glv_token_program_id,
780                    system_program: system_program::ID,
781                    chainlink_program: None,
782                    event_authority: self.client.store_event_authority(),
783                    program: *self.client.store_program_id(),
784                },
785                &crate::program_ids::DEFAULT_GMSOL_STORE_ID,
786                self.client.store_program_id(),
787            ))
788            .anchor_args(instruction::ExecuteGlvWithdrawal {
789                execution_lamports: self.execution_lamports,
790                throw_on_execution_error: !self.cancel_on_execution_error,
791            })
792            .accounts(glv_accounts)
793            .accounts(feeds.into_iter().chain(markets).collect::<Vec<_>>())
794            .compute_budget(
795                ComputeBudget::default().with_limit(EXECUTE_GLV_WITHDRAWAL_COMPUTE_BUDGET),
796            )
797            .lookup_tables(self.alts.clone());
798
799        let rpc = if self.close {
800            let close = self
801                .client
802                .close_glv_withdrawal(&self.glv_withdrawal)
803                .reason("executed")
804                .hint(hint.close)
805                .build()
806                .await
807                .map_err(gmsol_solana_utils::Error::custom)?;
808            execute.merge(close)
809        } else {
810            execute
811        };
812
813        let mut tx = self.client.bundle_with_options(options);
814
815        tx.try_push(rpc)?;
816
817        Ok(tx)
818    }
819}
820
821impl<C: Deref<Target = impl Signer> + Clone> PullOraclePriceConsumer
822    for ExecuteGlvWithdrawalBuilder<'_, C>
823{
824    async fn feed_ids(&mut self) -> crate::Result<FeedIds> {
825        let hint = self.prepare_hint().await?;
826        Ok(FeedIds::new(hint.store, hint.feeds))
827    }
828
829    fn process_feeds(
830        &mut self,
831        provider: PriceProviderKind,
832        map: FeedAddressMap,
833    ) -> crate::Result<()> {
834        self.feeds_parser
835            .insert_pull_oracle_feed_parser(provider, map);
836        Ok(())
837    }
838}
839
840impl<C> SetExecutionFee for ExecuteGlvWithdrawalBuilder<'_, C> {
841    fn set_execution_fee(&mut self, lamports: u64) -> &mut Self {
842        self.execution_lamports = lamports;
843        self
844    }
845}