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