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
44pub 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#[derive(Clone)]
71pub struct CreateGlvDepositHint {
72 long_token_mint: Pubkey,
73 short_token_mint: Pubkey,
74}
75
76impl CreateGlvDepositHint {
77 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 pub fn nonce(&mut self, nonce: NonceBytes) -> &mut Self {
121 self.nonce = Some(nonce);
122 self
123 }
124
125 pub fn max_execution_fee(&mut self, lamports: u64) -> &mut Self {
127 self.max_execution_lamports = lamports;
128 self
129 }
130
131 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 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 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 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 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 pub fn min_glv_token_amount(&mut self, amount: u64) -> &mut Self {
182 self.min_glv_token_amount = amount;
183 self
184 }
185
186 pub fn min_market_token_amount(&mut self, amount: u64) -> &mut Self {
188 self.min_market_token_amount = amount;
189 self
190 }
191
192 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 pub fn receiver(&mut self, receiver: Option<Pubkey>) -> &mut Self {
202 self.receiver = receiver;
203 self
204 }
205
206 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 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 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 = 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 = 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 = 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 = 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
407pub struct CloseGlvDepositBuilder<'a, C> {
409 client: &'a crate::Client<C>,
410 glv_deposit: Pubkey,
411 reason: String,
412 hint: Option<CloseGlvDepositHint>,
413}
414
415#[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 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 pub fn hint(&mut self, hint: CloseGlvDepositHint) -> &mut Self {
464 self.hint = Some(hint);
465 self
466 }
467
468 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 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
568pub 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#[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 pub feeds: TokensWithFeed,
601 should_unwrap_native_token: bool,
602}
603
604impl ExecuteGlvDepositHint {
605 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 pub fn hint(&mut self, hint: ExecuteGlvDepositHint) -> &mut Self {
662 self.hint = Some(hint);
663 self
664 }
665
666 pub fn token_map(&mut self, address: &Pubkey) -> &mut Self {
668 self.token_map = Some(*address);
669 self
670 }
671
672 pub fn close(&mut self, close: bool) -> &mut Self {
674 self.close = close;
675 self
676 }
677
678 #[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 pub fn add_alt(&mut self, account: AddressLookupTableAccount) -> &mut Self {
690 self.alts.insert(account.key, account.addresses);
691 self
692 }
693
694 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}