gmsol_sdk/client/ops/exchange/
mod.rs

1/// Builders for transactions related to deposits.
2pub mod deposit;
3
4/// Builders for transactions related to withdrawals.
5pub mod withdrawal;
6
7/// Builders for transactions related to shifts.
8pub mod shift;
9
10/// Builders for transactions related to orders.
11pub mod order;
12
13/// Builders for transactions related to GLV deposits.
14pub mod glv_deposit;
15
16/// Builders for transactions related to GLV withdrawals.
17pub mod glv_withdrawal;
18
19/// Builders for transactions related to GLV shifts.
20pub mod glv_shift;
21
22/// Builders for transactions related to market state.
23pub mod market_state;
24
25use std::{
26    collections::{BTreeSet, HashSet},
27    future::Future,
28    ops::Deref,
29};
30
31use deposit::{CloseDepositBuilder, CreateDepositBuilder, ExecuteDepositBuilder};
32use glv_deposit::{CloseGlvDepositBuilder, CreateGlvDepositBuilder, ExecuteGlvDepositBuilder};
33use glv_shift::{CloseGlvShiftBuilder, CreateGlvShiftBuilder, ExecuteGlvShiftBuilder};
34use glv_withdrawal::{
35    CloseGlvWithdrawalBuilder, CreateGlvWithdrawalBuilder, ExecuteGlvWithdrawalBuilder,
36};
37use gmsol_programs::gmsol_store::{
38    client::{accounts, args},
39    types::UpdateOrderParams,
40};
41use gmsol_solana_utils::{transaction_builder::TransactionBuilder, IntoAtomicGroup};
42use gmsol_utils::{
43    order::{OrderKind, PositionCutKind},
44    pubkey::optional_address,
45    swap::SwapActionParams,
46};
47use market_state::{UpdateClosedStateBuilder, UpdateFeesStateBuilder};
48use order::{
49    CloseOrderBuilder, CreateOrderBuilder, ExecuteOrderBuilder, OrderParams, PositionCutBuilder,
50    UpdateAdlBuilder,
51};
52use shift::{CloseShiftBuilder, CreateShiftBuilder, ExecuteShiftBuilder};
53use solana_sdk::{pubkey::Pubkey, signer::Signer};
54use withdrawal::{CloseWithdrawalBuilder, CreateWithdrawalBuilder, ExecuteWithdrawalBuilder};
55
56use crate::{
57    builders::{
58        callback::{Callback, CallbackParams},
59        market_state::{UpdateClosedState, UpdateFeesState},
60        order::update::SetShouldKeepPositionAccount,
61        position::CloseEmptyPosition,
62    },
63    client::Client,
64};
65
66/// Exchange operations.
67pub trait ExchangeOps<C> {
68    /// Create a deposit.
69    fn create_deposit(&self, store: &Pubkey, market_token: &Pubkey) -> CreateDepositBuilder<C>;
70
71    /// Create first deposit.
72    fn create_first_deposit(
73        &self,
74        store: &Pubkey,
75        market_token: &Pubkey,
76    ) -> CreateDepositBuilder<C>;
77
78    /// Cancel a deposit.
79    fn close_deposit(&self, store: &Pubkey, deposit: &Pubkey) -> CloseDepositBuilder<C>;
80
81    /// Execute a deposit.
82    fn execute_deposit(
83        &self,
84        store: &Pubkey,
85        oracle: &Pubkey,
86        deposit: &Pubkey,
87        cancel_on_execution_error: bool,
88    ) -> ExecuteDepositBuilder<C>;
89
90    /// Create a withdrawal.
91    fn create_withdrawal(
92        &self,
93        store: &Pubkey,
94        market_token: &Pubkey,
95        amount: u64,
96    ) -> CreateWithdrawalBuilder<C>;
97
98    /// Close a withdrawal.
99    fn close_withdrawal(&self, store: &Pubkey, withdrawal: &Pubkey) -> CloseWithdrawalBuilder<C>;
100
101    /// Execute a withdrawal.
102    fn execute_withdrawal(
103        &self,
104        store: &Pubkey,
105        oracle: &Pubkey,
106        withdrawal: &Pubkey,
107        cancel_on_execution_error: bool,
108    ) -> ExecuteWithdrawalBuilder<C>;
109
110    /// Create shift.
111    fn create_shift(
112        &self,
113        store: &Pubkey,
114        from_market_token: &Pubkey,
115        to_market_token: &Pubkey,
116        amount: u64,
117    ) -> CreateShiftBuilder<C>;
118
119    /// Close shift.
120    fn close_shift(&self, shift: &Pubkey) -> CloseShiftBuilder<C>;
121
122    /// Execute shift.
123    fn execute_shift(
124        &self,
125        oracle: &Pubkey,
126        shift: &Pubkey,
127        cancel_on_execution_error: bool,
128    ) -> ExecuteShiftBuilder<C>;
129
130    /// Create an order.
131    fn create_order(
132        &self,
133        store: &Pubkey,
134        market_token: &Pubkey,
135        is_output_token_long: bool,
136        params: OrderParams,
137    ) -> CreateOrderBuilder<C>;
138
139    /// Create a market increase position order.
140    fn market_increase(
141        &self,
142        store: &Pubkey,
143        market_token: &Pubkey,
144        is_collateral_token_long: bool,
145        initial_collateral_amount: u64,
146        is_long: bool,
147        increment_size_in_usd: u128,
148    ) -> CreateOrderBuilder<C> {
149        let params = OrderParams {
150            kind: OrderKind::MarketIncrease,
151            decrease_position_swap_type: None,
152            min_output_amount: 0,
153            size_delta_usd: increment_size_in_usd,
154            initial_collateral_delta_amount: initial_collateral_amount,
155            acceptable_price: None,
156            trigger_price: None,
157            is_long,
158            valid_from_ts: None,
159        };
160        self.create_order(store, market_token, is_collateral_token_long, params)
161    }
162
163    /// Create a market decrease position order.
164    fn market_decrease(
165        &self,
166        store: &Pubkey,
167        market_token: &Pubkey,
168        is_collateral_token_long: bool,
169        collateral_withdrawal_amount: u64,
170        is_long: bool,
171        decrement_size_in_usd: u128,
172    ) -> CreateOrderBuilder<C> {
173        let params = OrderParams {
174            kind: OrderKind::MarketDecrease,
175            decrease_position_swap_type: None,
176            min_output_amount: 0,
177            size_delta_usd: decrement_size_in_usd,
178            initial_collateral_delta_amount: collateral_withdrawal_amount,
179            acceptable_price: None,
180            trigger_price: None,
181            is_long,
182            valid_from_ts: None,
183        };
184        self.create_order(store, market_token, is_collateral_token_long, params)
185    }
186
187    /// Create a market swap order.
188    fn market_swap<'a, S>(
189        &self,
190        store: &Pubkey,
191        market_token: &Pubkey,
192        is_output_token_long: bool,
193        initial_swap_in_token: &Pubkey,
194        initial_swap_in_token_amount: u64,
195        swap_path: impl IntoIterator<Item = &'a Pubkey>,
196    ) -> CreateOrderBuilder<C>
197    where
198        C: Deref<Target = S> + Clone,
199        S: Signer,
200    {
201        let params = OrderParams {
202            kind: OrderKind::MarketSwap,
203            decrease_position_swap_type: None,
204            min_output_amount: 0,
205            size_delta_usd: 0,
206            initial_collateral_delta_amount: initial_swap_in_token_amount,
207            acceptable_price: None,
208            trigger_price: None,
209            is_long: true,
210            valid_from_ts: None,
211        };
212        let mut builder = self.create_order(store, market_token, is_output_token_long, params);
213        builder
214            .initial_collateral_token(initial_swap_in_token, None)
215            .swap_path(swap_path.into_iter().copied().collect());
216        builder
217    }
218
219    /// Create a limit increase order.
220    #[allow(clippy::too_many_arguments)]
221    fn limit_increase(
222        &self,
223        store: &Pubkey,
224        market_token: &Pubkey,
225        is_long: bool,
226        increment_size_in_usd: u128,
227        price: u128,
228        is_collateral_token_long: bool,
229        initial_collateral_amount: u64,
230    ) -> CreateOrderBuilder<C> {
231        let params = OrderParams {
232            kind: OrderKind::LimitIncrease,
233            decrease_position_swap_type: None,
234            min_output_amount: 0,
235            size_delta_usd: increment_size_in_usd,
236            initial_collateral_delta_amount: initial_collateral_amount,
237            acceptable_price: None,
238            trigger_price: Some(price),
239            is_long,
240            valid_from_ts: None,
241        };
242        self.create_order(store, market_token, is_collateral_token_long, params)
243    }
244
245    /// Create a limit decrease order.
246    #[allow(clippy::too_many_arguments)]
247    fn limit_decrease(
248        &self,
249        store: &Pubkey,
250        market_token: &Pubkey,
251        is_long: bool,
252        decrement_size_in_usd: u128,
253        price: u128,
254        is_collateral_token_long: bool,
255        collateral_withdrawal_amount: u64,
256    ) -> CreateOrderBuilder<C> {
257        let params = OrderParams {
258            kind: OrderKind::LimitDecrease,
259            decrease_position_swap_type: None,
260            min_output_amount: 0,
261            size_delta_usd: decrement_size_in_usd,
262            initial_collateral_delta_amount: collateral_withdrawal_amount,
263            acceptable_price: None,
264            trigger_price: Some(price),
265            is_long,
266            valid_from_ts: None,
267        };
268        self.create_order(store, market_token, is_collateral_token_long, params)
269    }
270
271    /// Create a stop-loss decrease order.
272    #[allow(clippy::too_many_arguments)]
273    fn stop_loss(
274        &self,
275        store: &Pubkey,
276        market_token: &Pubkey,
277        is_long: bool,
278        decrement_size_in_usd: u128,
279        price: u128,
280        is_collateral_token_long: bool,
281        collateral_withdrawal_amount: u64,
282    ) -> CreateOrderBuilder<C> {
283        let params = OrderParams {
284            kind: OrderKind::StopLossDecrease,
285            decrease_position_swap_type: None,
286            min_output_amount: 0,
287            size_delta_usd: decrement_size_in_usd,
288            initial_collateral_delta_amount: collateral_withdrawal_amount,
289            acceptable_price: None,
290            trigger_price: Some(price),
291            is_long,
292            valid_from_ts: None,
293        };
294        self.create_order(store, market_token, is_collateral_token_long, params)
295    }
296
297    /// Create a limit swap order.
298    #[allow(clippy::too_many_arguments)]
299    fn limit_swap<'a, S>(
300        &self,
301        store: &Pubkey,
302        market_token: &Pubkey,
303        is_output_token_long: bool,
304        min_output_amount: u64,
305        initial_swap_in_token: &Pubkey,
306        initial_swap_in_token_amount: u64,
307        swap_path: impl IntoIterator<Item = &'a Pubkey>,
308    ) -> CreateOrderBuilder<C>
309    where
310        C: Deref<Target = S> + Clone,
311        S: Signer,
312    {
313        let params = OrderParams {
314            kind: OrderKind::LimitSwap,
315            decrease_position_swap_type: None,
316            min_output_amount: u128::from(min_output_amount),
317            size_delta_usd: 0,
318            initial_collateral_delta_amount: initial_swap_in_token_amount,
319            acceptable_price: None,
320            trigger_price: None,
321            is_long: true,
322            valid_from_ts: None,
323        };
324        let mut builder = self.create_order(store, market_token, is_output_token_long, params);
325        builder
326            .initial_collateral_token(initial_swap_in_token, None)
327            .swap_path(swap_path.into_iter().copied().collect());
328        builder
329    }
330
331    /// Update an order.
332    fn update_order(
333        &self,
334        store: &Pubkey,
335        market_token: &Pubkey,
336        order: &Pubkey,
337        params: UpdateOrderParams,
338        hint: Option<Option<Callback>>,
339    ) -> impl Future<Output = crate::Result<TransactionBuilder<C>>>;
340
341    /// Execute an order.
342    fn execute_order(
343        &self,
344        store: &Pubkey,
345        oracle: &Pubkey,
346        order: &Pubkey,
347        cancel_on_execution_error: bool,
348    ) -> crate::Result<ExecuteOrderBuilder<C>>;
349
350    /// Close an order.
351    fn close_order(&self, order: &Pubkey) -> crate::Result<CloseOrderBuilder<C>>;
352
353    /// Cancel order if the position does not exist.
354    fn cancel_order_if_no_position(
355        &self,
356        store: &Pubkey,
357        order: &Pubkey,
358        position_hint: Option<&Pubkey>,
359    ) -> impl Future<Output = crate::Result<TransactionBuilder<C>>>;
360
361    /// Close empty position.
362    fn close_empty_position(
363        &self,
364        store: &Pubkey,
365        position: &Pubkey,
366    ) -> crate::Result<TransactionBuilder<C>>;
367
368    /// Liquidate a position.
369    fn liquidate(&self, oracle: &Pubkey, position: &Pubkey)
370        -> crate::Result<PositionCutBuilder<C>>;
371
372    /// Auto-deleverage a position.
373    fn auto_deleverage(
374        &self,
375        oracle: &Pubkey,
376        position: &Pubkey,
377        size_delta_usd: u128,
378    ) -> crate::Result<PositionCutBuilder<C>>;
379
380    /// Update ADL state.
381    fn update_adl(
382        &self,
383        store: &Pubkey,
384        oracle: &Pubkey,
385        market_token: &Pubkey,
386        for_long: bool,
387        for_short: bool,
388    ) -> crate::Result<UpdateAdlBuilder<C>>;
389
390    /// Create a GLV deposit.
391    fn create_glv_deposit(
392        &self,
393        store: &Pubkey,
394        glv_token: &Pubkey,
395        market_token: &Pubkey,
396    ) -> CreateGlvDepositBuilder<C>;
397
398    /// Close a GLV deposit.
399    fn close_glv_deposit(&self, glv_deposit: &Pubkey) -> CloseGlvDepositBuilder<C>;
400
401    /// Execute the given GLV deposit.
402    fn execute_glv_deposit(
403        &self,
404        oracle: &Pubkey,
405        glv_deposit: &Pubkey,
406        cancel_on_execution_error: bool,
407    ) -> ExecuteGlvDepositBuilder<C>;
408
409    fn create_glv_withdrawal(
410        &self,
411        store: &Pubkey,
412        glv_token: &Pubkey,
413        market_token: &Pubkey,
414        amount: u64,
415    ) -> CreateGlvWithdrawalBuilder<C>;
416
417    /// Close a GLV withdrawal.
418    fn close_glv_withdrawal(&self, glv_withdrawal: &Pubkey) -> CloseGlvWithdrawalBuilder<C>;
419
420    /// Execute the given GLV deposit.
421    fn execute_glv_withdrawal(
422        &self,
423        oracle: &Pubkey,
424        glv_withdrawal: &Pubkey,
425        cancel_on_execution_error: bool,
426    ) -> ExecuteGlvWithdrawalBuilder<C>;
427
428    fn create_glv_shift(
429        &self,
430        store: &Pubkey,
431        glv_token: &Pubkey,
432        from_market_token: &Pubkey,
433        to_market_token: &Pubkey,
434        amount: u64,
435    ) -> CreateGlvShiftBuilder<C>;
436
437    fn close_glv_shift(&self, glv_shift: &Pubkey) -> CloseGlvShiftBuilder<C>;
438
439    fn execute_glv_shift(
440        &self,
441        oracle: &Pubkey,
442        glv_shift: &Pubkey,
443        cancel_on_execution_error: bool,
444    ) -> ExecuteGlvShiftBuilder<C>;
445
446    fn update_closed_state(
447        &self,
448        store: &Pubkey,
449        oracle: &Pubkey,
450        market_token: &Pubkey,
451    ) -> UpdateClosedStateBuilder<C>;
452
453    fn update_fees_state(
454        &self,
455        store: &Pubkey,
456        oracle: &Pubkey,
457        market_token: &Pubkey,
458    ) -> UpdateFeesStateBuilder<C>;
459
460    fn set_should_keep_position_account(
461        &self,
462        store: &Pubkey,
463        order: &Pubkey,
464        keep: bool,
465    ) -> crate::Result<TransactionBuilder<C>>;
466}
467
468impl<C: Deref<Target = impl Signer> + Clone> ExchangeOps<C> for Client<C> {
469    fn create_deposit(&self, store: &Pubkey, market_token: &Pubkey) -> CreateDepositBuilder<C> {
470        CreateDepositBuilder::new(self, *store, *market_token)
471    }
472
473    fn create_first_deposit(
474        &self,
475        store: &Pubkey,
476        market_token: &Pubkey,
477    ) -> CreateDepositBuilder<C> {
478        let mut builder = self.create_deposit(store, market_token);
479        builder.receiver(Some(self.find_first_deposit_owner_address()));
480        builder
481    }
482
483    fn close_deposit(&self, store: &Pubkey, deposit: &Pubkey) -> CloseDepositBuilder<C> {
484        CloseDepositBuilder::new(self, store, deposit)
485    }
486
487    fn execute_deposit(
488        &self,
489        store: &Pubkey,
490        oracle: &Pubkey,
491        deposit: &Pubkey,
492        cancel_on_execution_error: bool,
493    ) -> ExecuteDepositBuilder<C> {
494        ExecuteDepositBuilder::new(self, store, oracle, deposit, cancel_on_execution_error)
495    }
496
497    fn create_withdrawal(
498        &self,
499        store: &Pubkey,
500        market_token: &Pubkey,
501        amount: u64,
502    ) -> CreateWithdrawalBuilder<C> {
503        CreateWithdrawalBuilder::new(self, *store, *market_token, amount)
504    }
505
506    fn close_withdrawal(&self, store: &Pubkey, withdrawal: &Pubkey) -> CloseWithdrawalBuilder<C> {
507        CloseWithdrawalBuilder::new(self, store, withdrawal)
508    }
509
510    fn execute_withdrawal(
511        &self,
512        store: &Pubkey,
513        oracle: &Pubkey,
514        withdrawal: &Pubkey,
515        cancel_on_execution_error: bool,
516    ) -> ExecuteWithdrawalBuilder<C> {
517        ExecuteWithdrawalBuilder::new(self, store, oracle, withdrawal, cancel_on_execution_error)
518    }
519
520    fn create_shift(
521        &self,
522        store: &Pubkey,
523        from_market_token: &Pubkey,
524        to_market_token: &Pubkey,
525        amount: u64,
526    ) -> CreateShiftBuilder<C> {
527        CreateShiftBuilder::new(self, store, from_market_token, to_market_token, amount)
528    }
529
530    fn close_shift(&self, shift: &Pubkey) -> CloseShiftBuilder<C> {
531        CloseShiftBuilder::new(self, shift)
532    }
533
534    fn execute_shift(
535        &self,
536        oracle: &Pubkey,
537        shift: &Pubkey,
538        cancel_on_execution_error: bool,
539    ) -> ExecuteShiftBuilder<C> {
540        ExecuteShiftBuilder::new(self, oracle, shift, cancel_on_execution_error)
541    }
542
543    fn create_order(
544        &self,
545        store: &Pubkey,
546        market_token: &Pubkey,
547        is_output_token_long: bool,
548        params: OrderParams,
549    ) -> CreateOrderBuilder<C> {
550        CreateOrderBuilder::new(self, store, market_token, params, is_output_token_long)
551    }
552
553    async fn update_order(
554        &self,
555        store: &Pubkey,
556        market_token: &Pubkey,
557        order: &Pubkey,
558        params: UpdateOrderParams,
559        hint: Option<Option<Callback>>,
560    ) -> crate::Result<TransactionBuilder<C>> {
561        let callback = match hint {
562            Some(callback) => callback,
563            None => {
564                let order = self.order(order).await?;
565                Callback::from_header(&order.header)?
566            }
567        };
568        let CallbackParams {
569            callback_authority,
570            callback_program,
571            callback_shared_data_account,
572            callback_partitioned_data_account,
573            ..
574        } = self.get_callback_params(callback.as_ref());
575        Ok(self
576            .store_transaction()
577            .anchor_accounts(accounts::UpdateOrderV2 {
578                owner: self.payer(),
579                store: *store,
580                market: self.find_market_address(store, market_token),
581                order: *order,
582                event_authority: self.store_event_authority(),
583                program: *self.store_program_id(),
584                callback_authority,
585                callback_program,
586                callback_shared_data_account,
587                callback_partitioned_data_account,
588            })
589            .anchor_args(args::UpdateOrderV2 { params }))
590    }
591
592    fn execute_order(
593        &self,
594        store: &Pubkey,
595        oracle: &Pubkey,
596        order: &Pubkey,
597        cancel_on_execution_error: bool,
598    ) -> crate::Result<ExecuteOrderBuilder<C>> {
599        ExecuteOrderBuilder::try_new(self, store, oracle, order, cancel_on_execution_error)
600    }
601
602    fn close_order(&self, order: &Pubkey) -> crate::Result<CloseOrderBuilder<C>> {
603        Ok(CloseOrderBuilder::new(self, order))
604    }
605
606    async fn cancel_order_if_no_position(
607        &self,
608        store: &Pubkey,
609        order: &Pubkey,
610        position_hint: Option<&Pubkey>,
611    ) -> crate::Result<TransactionBuilder<C>> {
612        let position = match position_hint {
613            Some(position) => *position,
614            None => {
615                let order = self.order(order).await?;
616
617                let position = order
618                    .params
619                    .position()
620                    .ok_or_else(|| crate::Error::custom("this order does not have position"))?;
621
622                *position
623            }
624        };
625
626        Ok(self
627            .store_transaction()
628            .anchor_args(args::CancelOrderIfNoPosition {})
629            .anchor_accounts(accounts::CancelOrderIfNoPosition {
630                authority: self.payer(),
631                store: *store,
632                order: *order,
633                position,
634            }))
635    }
636
637    fn close_empty_position(
638        &self,
639        store: &Pubkey,
640        position: &Pubkey,
641    ) -> crate::Result<TransactionBuilder<C>> {
642        let ag = CloseEmptyPosition::builder()
643            .payer(self.payer())
644            .program(self.store_program_for_builders(store))
645            .position(*position)
646            .build()
647            .into_atomic_group(&())?;
648        let txn = self.store_transaction().pre_atomic_group(ag, true);
649        Ok(txn)
650    }
651
652    fn liquidate(
653        &self,
654        oracle: &Pubkey,
655        position: &Pubkey,
656    ) -> crate::Result<PositionCutBuilder<C>> {
657        PositionCutBuilder::try_new(self, PositionCutKind::Liquidate, oracle, position)
658    }
659
660    fn auto_deleverage(
661        &self,
662        oracle: &Pubkey,
663        position: &Pubkey,
664        size_delta_usd: u128,
665    ) -> crate::Result<PositionCutBuilder<C>> {
666        PositionCutBuilder::try_new(
667            self,
668            PositionCutKind::AutoDeleverage(size_delta_usd),
669            oracle,
670            position,
671        )
672    }
673
674    fn update_adl(
675        &self,
676        store: &Pubkey,
677        oracle: &Pubkey,
678        market_token: &Pubkey,
679        for_long: bool,
680        for_short: bool,
681    ) -> crate::Result<UpdateAdlBuilder<C>> {
682        UpdateAdlBuilder::try_new(self, store, oracle, market_token, for_long, for_short)
683    }
684
685    fn create_glv_deposit(
686        &self,
687        store: &Pubkey,
688        glv_token: &Pubkey,
689        market_token: &Pubkey,
690    ) -> CreateGlvDepositBuilder<C> {
691        CreateGlvDepositBuilder::new(self, *store, *glv_token, *market_token)
692    }
693
694    fn close_glv_deposit(&self, glv_deposit: &Pubkey) -> CloseGlvDepositBuilder<C> {
695        CloseGlvDepositBuilder::new(self, *glv_deposit)
696    }
697
698    fn execute_glv_deposit(
699        &self,
700        oracle: &Pubkey,
701        glv_deposit: &Pubkey,
702        cancel_on_execution_error: bool,
703    ) -> ExecuteGlvDepositBuilder<C> {
704        ExecuteGlvDepositBuilder::new(self, *oracle, *glv_deposit, cancel_on_execution_error)
705    }
706
707    fn create_glv_withdrawal(
708        &self,
709        store: &Pubkey,
710        glv_token: &Pubkey,
711        market_token: &Pubkey,
712        amount: u64,
713    ) -> CreateGlvWithdrawalBuilder<C> {
714        CreateGlvWithdrawalBuilder::new(self, *store, *glv_token, *market_token, amount)
715    }
716
717    fn close_glv_withdrawal(&self, glv_withdrawal: &Pubkey) -> CloseGlvWithdrawalBuilder<C> {
718        CloseGlvWithdrawalBuilder::new(self, *glv_withdrawal)
719    }
720
721    fn execute_glv_withdrawal(
722        &self,
723        oracle: &Pubkey,
724        glv_withdrawal: &Pubkey,
725        cancel_on_execution_error: bool,
726    ) -> ExecuteGlvWithdrawalBuilder<C> {
727        ExecuteGlvWithdrawalBuilder::new(self, *oracle, *glv_withdrawal, cancel_on_execution_error)
728    }
729
730    fn create_glv_shift(
731        &self,
732        store: &Pubkey,
733        glv_token: &Pubkey,
734        from_market_token: &Pubkey,
735        to_market_token: &Pubkey,
736        amount: u64,
737    ) -> CreateGlvShiftBuilder<C> {
738        CreateGlvShiftBuilder::new(
739            self,
740            store,
741            glv_token,
742            from_market_token,
743            to_market_token,
744            amount,
745        )
746    }
747
748    fn close_glv_shift(&self, glv_shift: &Pubkey) -> CloseGlvShiftBuilder<C> {
749        CloseGlvShiftBuilder::new(self, glv_shift)
750    }
751
752    fn execute_glv_shift(
753        &self,
754        oracle: &Pubkey,
755        glv_shift: &Pubkey,
756        cancel_on_execution_error: bool,
757    ) -> ExecuteGlvShiftBuilder<C> {
758        let mut builder = ExecuteGlvShiftBuilder::new(self, oracle, glv_shift);
759        builder.cancel_on_execution_error(cancel_on_execution_error);
760        builder
761    }
762
763    fn update_closed_state(
764        &self,
765        store: &Pubkey,
766        oracle: &Pubkey,
767        market_token: &Pubkey,
768    ) -> UpdateClosedStateBuilder<C> {
769        UpdateClosedStateBuilder::new(
770            self,
771            UpdateClosedState::builder()
772                .payer(self.payer())
773                .market_token(*market_token)
774                .store_program(self.store_program_for_builders(store))
775                .oracle(*oracle)
776                .build(),
777        )
778    }
779
780    fn update_fees_state(
781        &self,
782        store: &Pubkey,
783        oracle: &Pubkey,
784        market_token: &Pubkey,
785    ) -> UpdateFeesStateBuilder<C> {
786        UpdateFeesStateBuilder::new(
787            self,
788            UpdateFeesState::builder()
789                .payer(self.payer())
790                .market_token(*market_token)
791                .store_program(self.store_program_for_builders(store))
792                .oracle(*oracle)
793                .build(),
794        )
795    }
796
797    fn set_should_keep_position_account(
798        &self,
799        store: &Pubkey,
800        order: &Pubkey,
801        keep: bool,
802    ) -> crate::Result<TransactionBuilder<C>> {
803        let set = SetShouldKeepPositionAccount::builder()
804            .keep(keep)
805            .order(*order)
806            .payer(self.payer())
807            .program(self.store_program_for_builders(store))
808            .build()
809            .into_atomic_group(&())?;
810        Ok(self.store_transaction().pre_atomic_group(set, false))
811    }
812}
813
814#[derive(Default)]
815struct VirtualInventoryCollector {
816    market_tokens: HashSet<Pubkey>,
817}
818
819impl VirtualInventoryCollector {
820    fn insert_market_token(&mut self, market_token: &Pubkey) -> &mut Self {
821        self.market_tokens.insert(*market_token);
822        self
823    }
824
825    fn from_swap(swap: &SwapActionParams) -> Self {
826        let mut this = Self::default();
827
828        this.insert_market_token(&swap.current_market_token);
829
830        for market_token in swap.unique_market_tokens_excluding_current(&swap.current_market_token)
831        {
832            this.insert_market_token(market_token);
833        }
834
835        this
836    }
837
838    async fn collect<C: Deref<Target = impl Signer> + Clone>(
839        &self,
840        client: &crate::Client<C>,
841        store: &Pubkey,
842    ) -> crate::Result<BTreeSet<Pubkey>> {
843        let mut addresses = BTreeSet::new();
844        for market_token in self.market_tokens.iter() {
845            let market = client.market_by_token(store, market_token).await?;
846            if let Some(address) = optional_address(&market.virtual_inventory_for_swaps) {
847                addresses.insert(*address);
848            }
849            if let Some(address) = optional_address(&market.virtual_inventory_for_positions) {
850                addresses.insert(*address);
851            }
852        }
853        Ok(addresses)
854    }
855}