gmsol_sdk/client/ops/
treasury.rs

1use std::{future::Future, ops::Deref};
2
3use anchor_spl::associated_token::get_associated_token_address_with_program_id;
4use gmsol_programs::gmsol_treasury::{
5    accounts::{Config, GtBank, GtExchange, TreasuryVaultConfig},
6    client::{accounts, args},
7    ID,
8};
9use gmsol_solana_utils::{
10    bundle_builder::{BundleBuilder, BundleOptions},
11    make_bundle_builder::{MakeBundleBuilder, SetExecutionFee},
12    transaction_builder::TransactionBuilder,
13};
14use gmsol_utils::{
15    oracle::PriceProviderKind,
16    pubkey::optional_address,
17    token_config::{TokenFlag, TokensWithFeed},
18};
19use solana_account_decoder::UiAccountEncoding;
20use solana_client::rpc_config::RpcAccountInfoConfig;
21use solana_sdk::{instruction::AccountMeta, pubkey::Pubkey, signer::Signer, system_program};
22
23use crate::{
24    builders::{
25        callback::{Callback, CallbackParams},
26        utils::generate_nonce,
27    },
28    client::{
29        feeds_parser::{FeedAddressMap, FeedsParser},
30        program_ids,
31        pull_oracle::{FeedIds, PullOraclePriceConsumer},
32    },
33    pda::NonceBytes,
34    utils::{optional::fix_optional_account_metas, zero_copy::ZeroCopy},
35};
36
37use super::{gt::GtOps, token_account::TokenAccountOps};
38
39/// Operations for treasury.
40pub trait TreasuryOps<C> {
41    /// Initialize [`Config`] account.
42    fn initialize_config(&self, store: &Pubkey) -> TransactionBuilder<C, Pubkey>;
43
44    /// Set treasury vault config.
45    fn set_treasury_vault_config(
46        &self,
47        store: &Pubkey,
48        treasury_vault_config: &Pubkey,
49    ) -> TransactionBuilder<C>;
50
51    /// Set GT factor.
52    fn set_gt_factor(&self, store: &Pubkey, factor: u128) -> crate::Result<TransactionBuilder<C>>;
53
54    /// Set buyback factor.
55    fn set_buyback_factor(
56        &self,
57        store: &Pubkey,
58        factor: u128,
59    ) -> crate::Result<TransactionBuilder<C>>;
60
61    /// Initialize [`TreasuryVaultConfig`].
62    fn initialize_treasury_vault_config(
63        &self,
64        store: &Pubkey,
65        index: u16,
66    ) -> TransactionBuilder<C, Pubkey>;
67
68    /// Insert token to treasury.
69    fn insert_token_to_treasury(
70        &self,
71        store: &Pubkey,
72        treasury_vault_config: Option<&Pubkey>,
73        token_mint: &Pubkey,
74    ) -> impl Future<Output = crate::Result<TransactionBuilder<C>>>;
75
76    /// Remove token from treasury.
77    fn remove_token_from_treasury(
78        &self,
79        store: &Pubkey,
80        treasury_vault_config: Option<&Pubkey>,
81        token_mint: &Pubkey,
82    ) -> impl Future<Output = crate::Result<TransactionBuilder<C>>>;
83
84    /// Toggle token flag.
85    fn toggle_token_flag(
86        &self,
87        store: &Pubkey,
88        treasury_vault_config: Option<&Pubkey>,
89        token_mint: &Pubkey,
90        flag: TokenFlag,
91        value: bool,
92    ) -> impl Future<Output = crate::Result<TransactionBuilder<C>>>;
93
94    /// Deposit to treasury vault.
95    fn deposit_to_treasury_vault(
96        &self,
97        store: &Pubkey,
98        treasury_vault_config_hint: Option<&Pubkey>,
99        token_mint: &Pubkey,
100        token_program_id: Option<&Pubkey>,
101        time_window: u32,
102    ) -> impl Future<Output = crate::Result<TransactionBuilder<C, Pubkey>>>;
103
104    /// Withdraw from treasury vault.
105    #[allow(clippy::too_many_arguments)]
106    fn withdraw_from_treasury_vault(
107        &self,
108        store: &Pubkey,
109        treasury_vault_config_hint: Option<&Pubkey>,
110        token_mint: &Pubkey,
111        token_program_id: Option<&Pubkey>,
112        amount: u64,
113        decimals: u8,
114        target: &Pubkey,
115    ) -> impl Future<Output = crate::Result<TransactionBuilder<C>>>;
116
117    /// Confirm GT buyback.
118    fn confirm_gt_buyback(
119        &self,
120        store: &Pubkey,
121        gt_exchange_vault: &Pubkey,
122        oracle: &Pubkey,
123    ) -> ConfirmGtBuybackBuilder<C>;
124
125    /// Transfer receiver.
126    fn transfer_receiver(&self, store: &Pubkey, new_receiver: &Pubkey) -> TransactionBuilder<C>;
127
128    /// Set referral reward factors.
129    fn set_referral_reward(&self, store: &Pubkey, factors: Vec<u128>) -> TransactionBuilder<C>;
130
131    /// Claim fees to receiver vault.
132    fn claim_fees_to_receiver_vault(
133        &self,
134        store: &Pubkey,
135        market_token: &Pubkey,
136        token_mint: &Pubkey,
137        min_amount: u64,
138    ) -> TransactionBuilder<C>;
139
140    /// Prepare GT bank.
141    fn prepare_gt_bank(
142        &self,
143        store: &Pubkey,
144        treasury_vault_config_hint: Option<&Pubkey>,
145        gt_exchange_vault: &Pubkey,
146    ) -> impl Future<Output = crate::Result<TransactionBuilder<C, Pubkey>>>;
147
148    /// Sync GT bank.
149    fn sync_gt_bank(
150        &self,
151        store: &Pubkey,
152        treasury_vault_config_hint: Option<&Pubkey>,
153        gt_exchange_vault: &Pubkey,
154        token_mint: &Pubkey,
155        token_program_id: Option<&Pubkey>,
156    ) -> impl Future<Output = crate::Result<TransactionBuilder<C>>>;
157
158    /// Complete GT exchange.
159    fn complete_gt_exchange(
160        &self,
161        store: &Pubkey,
162        exchange: &Pubkey,
163        treasury_vault_config_hint: Option<&Pubkey>,
164        tokens_hint: Option<Vec<(Pubkey, Pubkey)>>,
165        gt_exchange_vault_hint: Option<&Pubkey>,
166    ) -> impl Future<Output = crate::Result<TransactionBuilder<C>>>;
167
168    /// Create a swap.
169    fn create_treasury_swap(
170        &self,
171        store: &Pubkey,
172        market_token: &Pubkey,
173        swap_in_token: &Pubkey,
174        swap_out_token: &Pubkey,
175        swap_in_token_amount: u64,
176        options: CreateTreasurySwapOptions,
177    ) -> impl Future<Output = crate::Result<TransactionBuilder<C, Pubkey>>>;
178
179    /// Cancel a swap.
180    fn cancel_treasury_swap(
181        &self,
182        store: &Pubkey,
183        order: &Pubkey,
184        hint: Option<(&Pubkey, &Pubkey)>,
185    ) -> impl Future<Output = crate::Result<TransactionBuilder<C>>>;
186}
187
188impl<C: Deref<Target = impl Signer> + Clone> TreasuryOps<C> for crate::Client<C> {
189    fn initialize_config(&self, store: &Pubkey) -> TransactionBuilder<C, Pubkey> {
190        let config = self.find_treasury_config_address(store);
191        self.treasury_transaction()
192            .anchor_args(args::InitializeConfig {})
193            .anchor_accounts(accounts::InitializeConfig {
194                payer: self.payer(),
195                store: *store,
196                config,
197                receiver: self.find_treasury_receiver_address(&config),
198                store_program: *self.store_program_id(),
199                system_program: system_program::ID,
200            })
201            .output(config)
202    }
203
204    fn set_treasury_vault_config(
205        &self,
206        store: &Pubkey,
207        treasury_vault_config: &Pubkey,
208    ) -> TransactionBuilder<C> {
209        let config = self.find_treasury_config_address(store);
210        self.treasury_transaction()
211            .anchor_args(args::SetTreasuryVaultConfig {})
212            .anchor_accounts(accounts::SetTreasuryVaultConfig {
213                authority: self.payer(),
214                store: *store,
215                config,
216                treasury_vault_config: *treasury_vault_config,
217                store_program: *self.store_program_id(),
218            })
219    }
220
221    fn set_gt_factor(&self, store: &Pubkey, factor: u128) -> crate::Result<TransactionBuilder<C>> {
222        if factor > crate::constants::MARKET_USD_UNIT {
223            return Err(crate::Error::custom("cannot use a factor greater than 1"));
224        }
225        let config = self.find_treasury_config_address(store);
226        Ok(self
227            .treasury_transaction()
228            .anchor_args(args::SetGtFactor { factor })
229            .anchor_accounts(accounts::SetGtFactor {
230                authority: self.payer(),
231                store: *store,
232                config,
233                store_program: *self.store_program_id(),
234            }))
235    }
236
237    fn set_buyback_factor(
238        &self,
239        store: &Pubkey,
240        factor: u128,
241    ) -> crate::Result<TransactionBuilder<C>> {
242        if factor > crate::constants::MARKET_USD_UNIT {
243            return Err(crate::Error::custom("cannot use a factor greater than 1"));
244        }
245        let config = self.find_treasury_config_address(store);
246        Ok(self
247            .treasury_transaction()
248            .anchor_args(args::SetBuybackFactor { factor })
249            .anchor_accounts(accounts::SetBuybackFactor {
250                authority: self.payer(),
251                store: *store,
252                config,
253                store_program: *self.store_program_id(),
254            }))
255    }
256
257    fn initialize_treasury_vault_config(
258        &self,
259        store: &Pubkey,
260        index: u16,
261    ) -> TransactionBuilder<C, Pubkey> {
262        let config = self.find_treasury_config_address(store);
263        let treasury_vault_config = self.find_treasury_vault_config_address(&config, index);
264        self.treasury_transaction()
265            .anchor_args(args::InitializeTreasuryVaultConfig { index })
266            .anchor_accounts(accounts::InitializeTreasuryVaultConfig {
267                authority: self.payer(),
268                store: *store,
269                config,
270                treasury_vault_config,
271                store_program: *self.store_program_id(),
272                system_program: system_program::ID,
273            })
274            .output(treasury_vault_config)
275    }
276
277    async fn insert_token_to_treasury(
278        &self,
279        store: &Pubkey,
280        treasury_vault_config: Option<&Pubkey>,
281        token_mint: &Pubkey,
282    ) -> crate::Result<TransactionBuilder<C>> {
283        let (config, treasury_vault_config) =
284            find_config_addresses(self, store, treasury_vault_config).await?;
285        Ok(self
286            .treasury_transaction()
287            .anchor_args(args::InsertTokenToTreasuryVault {})
288            .anchor_accounts(accounts::InsertTokenToTreasuryVault {
289                authority: self.payer(),
290                store: *store,
291                config,
292                treasury_vault_config,
293                token: *token_mint,
294                store_program: *self.store_program_id(),
295            }))
296    }
297
298    async fn remove_token_from_treasury(
299        &self,
300        store: &Pubkey,
301        treasury_vault_config: Option<&Pubkey>,
302        token_mint: &Pubkey,
303    ) -> crate::Result<TransactionBuilder<C>> {
304        let (config, treasury_vault_config) =
305            find_config_addresses(self, store, treasury_vault_config).await?;
306        Ok(self
307            .treasury_transaction()
308            .anchor_args(args::RemoveTokenFromTreasuryVault {})
309            .anchor_accounts(accounts::RemoveTokenFromTreasuryVault {
310                authority: self.payer(),
311                store: *store,
312                config,
313                treasury_vault_config,
314                token: *token_mint,
315                store_program: *self.store_program_id(),
316            }))
317    }
318
319    async fn toggle_token_flag(
320        &self,
321        store: &Pubkey,
322        treasury_vault_config: Option<&Pubkey>,
323        token_mint: &Pubkey,
324        flag: TokenFlag,
325        value: bool,
326    ) -> crate::Result<TransactionBuilder<C>> {
327        let (config, treasury_vault_config) =
328            find_config_addresses(self, store, treasury_vault_config).await?;
329        Ok(self
330            .treasury_transaction()
331            .anchor_args(args::ToggleTokenFlag {
332                flag: flag.to_string(),
333                value,
334            })
335            .anchor_accounts(accounts::ToggleTokenFlag {
336                authority: self.payer(),
337                store: *store,
338                config,
339                treasury_vault_config,
340                token: *token_mint,
341                store_program: *self.store_program_id(),
342            }))
343    }
344
345    async fn deposit_to_treasury_vault(
346        &self,
347        store: &Pubkey,
348        treasury_vault_config_hint: Option<&Pubkey>,
349        token_mint: &Pubkey,
350        token_program_id: Option<&Pubkey>,
351        time_window: u32,
352    ) -> crate::Result<TransactionBuilder<C, Pubkey>> {
353        let (config, treasury_vault_config) =
354            find_config_addresses(self, store, treasury_vault_config_hint).await?;
355
356        let (prepare_gt_exchange_vault, gt_exchange_vault) = self
357            .prepare_gt_exchange_vault_with_time_window(store, time_window)?
358            .swap_output(());
359
360        let (prepare_gt_bank, gt_bank) = self
361            .prepare_gt_bank(store, Some(&treasury_vault_config), &gt_exchange_vault)
362            .await?
363            .swap_output(());
364
365        let token_program_id = token_program_id.unwrap_or(&anchor_spl::token::ID);
366
367        let receiver = self.find_treasury_receiver_address(&config);
368
369        let receiver_vault =
370            get_associated_token_address_with_program_id(&receiver, token_mint, token_program_id);
371        let treasury_vault = get_associated_token_address_with_program_id(
372            &treasury_vault_config,
373            token_mint,
374            token_program_id,
375        );
376        let gt_bank_vault =
377            get_associated_token_address_with_program_id(&gt_bank, token_mint, token_program_id);
378
379        let prepare_treasury_vault = self.prepare_associated_token_account(
380            token_mint,
381            token_program_id,
382            Some(&treasury_vault_config),
383        );
384        let prepare_gt_bank_vault =
385            self.prepare_associated_token_account(token_mint, token_program_id, Some(&gt_bank));
386
387        let deposit = self
388            .treasury_transaction()
389            .anchor_args(args::DepositToTreasuryVault {})
390            .anchor_accounts(accounts::DepositToTreasuryVault {
391                authority: self.payer(),
392                store: *store,
393                config,
394                treasury_vault_config,
395                receiver,
396                gt_exchange_vault,
397                gt_bank,
398                token: *token_mint,
399                receiver_vault,
400                treasury_vault,
401                gt_bank_vault,
402                store_program: *self.store_program_id(),
403                token_program: *token_program_id,
404                associated_token_program: anchor_spl::associated_token::ID,
405            });
406        Ok(prepare_gt_exchange_vault
407            .merge(prepare_gt_bank)
408            .merge(prepare_treasury_vault)
409            .merge(prepare_gt_bank_vault)
410            .merge(deposit)
411            .output(gt_exchange_vault))
412    }
413
414    async fn withdraw_from_treasury_vault(
415        &self,
416        store: &Pubkey,
417        treasury_vault_config_hint: Option<&Pubkey>,
418        token_mint: &Pubkey,
419        token_program_id: Option<&Pubkey>,
420        amount: u64,
421        decimals: u8,
422        target: &Pubkey,
423    ) -> crate::Result<TransactionBuilder<C>> {
424        let token_program_id = token_program_id.unwrap_or(&anchor_spl::token::ID);
425
426        let (config, treasury_vault_config) =
427            find_config_addresses(self, store, treasury_vault_config_hint).await?;
428
429        let treasury_vault = get_associated_token_address_with_program_id(
430            &treasury_vault_config,
431            token_mint,
432            token_program_id,
433        );
434
435        Ok(self
436            .treasury_transaction()
437            .anchor_args(args::WithdrawFromTreasuryVault { amount, decimals })
438            .anchor_accounts(accounts::WithdrawFromTreasuryVault {
439                authority: self.payer(),
440                store: *store,
441                config,
442                treasury_vault_config,
443                token: *token_mint,
444                treasury_vault,
445                target: *target,
446                store_program: *self.store_program_id(),
447                token_program: *token_program_id,
448            }))
449    }
450
451    fn confirm_gt_buyback(
452        &self,
453        store: &Pubkey,
454        gt_exchange_vault: &Pubkey,
455        oracle: &Pubkey,
456    ) -> ConfirmGtBuybackBuilder<C> {
457        ConfirmGtBuybackBuilder::new(self, store, gt_exchange_vault, oracle)
458    }
459
460    fn transfer_receiver(&self, store: &Pubkey, new_receiver: &Pubkey) -> TransactionBuilder<C> {
461        let config = self.find_treasury_config_address(store);
462        let receiver = self.find_treasury_receiver_address(&config);
463        self.treasury_transaction()
464            .anchor_args(args::TransferReceiver {})
465            .anchor_accounts(accounts::TransferReceiver {
466                authority: self.payer(),
467                store: *store,
468                config,
469                receiver,
470                next_receiver: *new_receiver,
471                store_program: *self.store_program_id(),
472                system_program: system_program::ID,
473            })
474    }
475
476    fn set_referral_reward(&self, store: &Pubkey, factors: Vec<u128>) -> TransactionBuilder<C> {
477        self.treasury_transaction()
478            .anchor_args(args::SetReferralReward { factors })
479            .anchor_accounts(accounts::SetReferralReward {
480                authority: self.payer(),
481                store: *store,
482                config: self.find_treasury_config_address(store),
483                store_program: *self.store_program_id(),
484            })
485    }
486
487    fn claim_fees_to_receiver_vault(
488        &self,
489        store: &Pubkey,
490        market_token: &Pubkey,
491        token_mint: &Pubkey,
492        min_amount: u64,
493    ) -> TransactionBuilder<C> {
494        let config = self.find_treasury_config_address(store);
495        let token_program_id = anchor_spl::token::ID;
496        let receiver = self.find_treasury_receiver_address(&config);
497        let receiver_vault =
498            get_associated_token_address_with_program_id(&receiver, token_mint, &token_program_id);
499        self.treasury_transaction()
500            .anchor_args(args::ClaimFees { min_amount })
501            .anchor_accounts(accounts::ClaimFees {
502                authority: self.payer(),
503                store: *store,
504                config,
505                receiver,
506                market: self.find_market_address(store, market_token),
507                token: *token_mint,
508                vault: self.find_market_vault_address(store, token_mint),
509                receiver_vault,
510                event_authority: self.store_event_authority(),
511                store_program: *self.store_program_id(),
512                token_program: token_program_id,
513                associated_token_program: anchor_spl::associated_token::ID,
514                system_program: system_program::ID,
515            })
516    }
517
518    async fn prepare_gt_bank(
519        &self,
520        store: &Pubkey,
521        treasury_vault_config_hint: Option<&Pubkey>,
522        gt_exchange_vault: &Pubkey,
523    ) -> crate::Result<TransactionBuilder<C, Pubkey>> {
524        let (config, treasury_vault_config) =
525            find_config_addresses(self, store, treasury_vault_config_hint).await?;
526        let gt_bank = self.find_gt_bank_address(&treasury_vault_config, gt_exchange_vault);
527        Ok(self
528            .treasury_transaction()
529            .anchor_args(args::PrepareGtBank {})
530            .anchor_accounts(accounts::PrepareGtBank {
531                authority: self.payer(),
532                store: *store,
533                config,
534                treasury_vault_config,
535                gt_exchange_vault: *gt_exchange_vault,
536                gt_bank,
537                store_program: *self.store_program_id(),
538                system_program: system_program::ID,
539            })
540            .output(gt_bank))
541    }
542
543    async fn sync_gt_bank(
544        &self,
545        store: &Pubkey,
546        treasury_vault_config_hint: Option<&Pubkey>,
547        gt_exchange_vault: &Pubkey,
548        token_mint: &Pubkey,
549        token_program_id: Option<&Pubkey>,
550    ) -> crate::Result<TransactionBuilder<C>> {
551        let (config, treasury_vault_config) =
552            find_config_addresses(self, store, treasury_vault_config_hint).await?;
553        let gt_bank = self.find_gt_bank_address(&treasury_vault_config, gt_exchange_vault);
554        let token_program_id = token_program_id.unwrap_or(&anchor_spl::token::ID);
555
556        let treasury_vault = get_associated_token_address_with_program_id(
557            &treasury_vault_config,
558            token_mint,
559            token_program_id,
560        );
561        let gt_bank_vault =
562            get_associated_token_address_with_program_id(&gt_bank, token_mint, token_program_id);
563
564        let prepare_treasury_vault = self.prepare_associated_token_account(
565            token_mint,
566            token_program_id,
567            Some(&treasury_vault_config),
568        );
569        let prepare_gt_bank_vault =
570            self.prepare_associated_token_account(token_mint, token_program_id, Some(&gt_bank));
571
572        let sync = self
573            .treasury_transaction()
574            .anchor_args(args::SyncGtBankV2 {})
575            .anchor_accounts(accounts::SyncGtBankV2 {
576                authority: self.payer(),
577                store: *store,
578                config,
579                treasury_vault_config,
580                gt_bank,
581                token: *token_mint,
582                treasury_vault,
583                gt_bank_vault,
584                store_program: *self.store_program_id(),
585                token_program: *token_program_id,
586                associated_token_program: anchor_spl::associated_token::ID,
587            });
588
589        Ok(prepare_treasury_vault
590            .merge(prepare_gt_bank_vault)
591            .merge(sync))
592    }
593
594    async fn complete_gt_exchange(
595        &self,
596        store: &Pubkey,
597        exchange: &Pubkey,
598        treasury_vault_config_hint: Option<&Pubkey>,
599        tokens_hint: Option<Vec<(Pubkey, Pubkey)>>,
600        gt_exchange_vault_hint: Option<&Pubkey>,
601    ) -> crate::Result<TransactionBuilder<C>> {
602        let owner = self.payer();
603        let (config, treasury_vault_config) =
604            find_config_addresses(self, store, treasury_vault_config_hint).await?;
605        let gt_exchange_vault = match gt_exchange_vault_hint {
606            Some(address) => *address,
607            None => {
608                let exchange = self
609                    .account::<ZeroCopy<GtExchange>>(exchange)
610                    .await?
611                    .ok_or(crate::Error::NotFound)?
612                    .0;
613                exchange.vault
614            }
615        };
616        let gt_bank = self.find_gt_bank_address(&treasury_vault_config, &gt_exchange_vault);
617
618        let tokens = match tokens_hint {
619            Some(tokens) => tokens,
620            None => {
621                let gt_bank = self
622                    .account::<ZeroCopy<GtBank>>(&gt_bank)
623                    .await?
624                    .ok_or_else(|| crate::Error::custom("treasury vault config not exist"))?
625                    .0;
626
627                let tokens = gt_bank.tokens().collect::<Vec<_>>();
628                self.treasury_program()
629                    .rpc()
630                    .get_multiple_accounts_with_config(
631                        &tokens,
632                        RpcAccountInfoConfig {
633                            encoding: Some(UiAccountEncoding::Base64),
634                            data_slice: Some(solana_account_decoder::UiDataSliceConfig {
635                                offset: 0,
636                                length: 0,
637                            }),
638                            ..Default::default()
639                        },
640                    )
641                    .await
642                    .map_err(crate::Error::custom)?
643                    .value
644                    .into_iter()
645                    .zip(&tokens)
646                    .map(|(account, address)| {
647                        let account = account.ok_or(crate::Error::NotFound)?;
648                        Ok((*address, account.owner))
649                    })
650                    .collect::<crate::Result<Vec<_>>>()?
651            }
652        };
653
654        let token_mints = tokens.iter().map(|pubkey| AccountMeta {
655            pubkey: pubkey.0,
656            is_signer: false,
657            is_writable: false,
658        });
659        let gt_bank_vaults = tokens.iter().map(|(mint, token_program_id)| {
660            let gt_bank_vault =
661                get_associated_token_address_with_program_id(&gt_bank, mint, token_program_id);
662            AccountMeta {
663                pubkey: gt_bank_vault,
664                is_signer: false,
665                is_writable: true,
666            }
667        });
668        let atas = tokens.iter().map(|(mint, token_program_id)| {
669            let ata = get_associated_token_address_with_program_id(&owner, mint, token_program_id);
670            AccountMeta {
671                pubkey: ata,
672                is_signer: false,
673                is_writable: true,
674            }
675        });
676
677        Ok(self
678            .treasury_transaction()
679            .anchor_args(args::CompleteGtExchange {})
680            .anchor_accounts(accounts::CompleteGtExchange {
681                owner,
682                store: *store,
683                config,
684                treasury_vault_config,
685                gt_exchange_vault,
686                gt_bank,
687                exchange: *exchange,
688                store_program: *self.store_program_id(),
689                token_program: anchor_spl::token::ID,
690                token_2022_program: anchor_spl::token_2022::ID,
691            })
692            .accounts(
693                token_mints
694                    .chain(gt_bank_vaults)
695                    .chain(atas)
696                    .collect::<Vec<_>>(),
697            ))
698    }
699
700    async fn create_treasury_swap(
701        &self,
702        store: &Pubkey,
703        market_token: &Pubkey,
704        swap_in_token: &Pubkey,
705        swap_out_token: &Pubkey,
706        swap_in_token_amount: u64,
707        options: CreateTreasurySwapOptions,
708    ) -> crate::Result<TransactionBuilder<C, Pubkey>> {
709        let nonce = options.nonce.unwrap_or_else(|| generate_nonce().to_bytes());
710        let swap_path = options
711            .swap_path
712            .iter()
713            .chain(Some(market_token))
714            .map(|token| {
715                let pubkey = self.find_market_address(store, token);
716                AccountMeta {
717                    pubkey,
718                    is_signer: false,
719                    is_writable: false,
720                }
721            })
722            .collect::<Vec<_>>();
723        let (config, treasury_vault_config) =
724            find_config_addresses(self, store, options.treasury_vault_config_hint.as_ref()).await?;
725
726        let receiver = self.find_treasury_receiver_address(&config);
727
728        // Currently only SPL-Token is supported.
729        let token_program_id = anchor_spl::token::ID;
730
731        let swap_in_token_receiver_vault = get_associated_token_address_with_program_id(
732            &receiver,
733            swap_in_token,
734            &token_program_id,
735        );
736
737        let market = self.find_market_address(store, market_token);
738
739        let user = self.find_user_address(store, &receiver);
740
741        let order = self.find_order_address(store, &receiver, &nonce);
742
743        let swap_in_token_escrow =
744            get_associated_token_address_with_program_id(&order, swap_in_token, &token_program_id);
745        let swap_out_token_escrow =
746            get_associated_token_address_with_program_id(&order, swap_out_token, &token_program_id);
747
748        let prepare_swap_in_escrow =
749            self.prepare_associated_token_account(swap_in_token, &token_program_id, Some(&order));
750        let prepare_swap_out_escrow =
751            self.prepare_associated_token_account(swap_out_token, &token_program_id, Some(&order));
752        let prepare_ata = self.prepare_associated_token_account(
753            swap_out_token,
754            &token_program_id,
755            Some(&receiver),
756        );
757
758        let CallbackParams {
759            callback_version,
760            callback_authority,
761            callback_program,
762            callback_shared_data_account,
763            callback_partitioned_data_account,
764        } = self.get_callback_params(options.callback.as_ref());
765
766        let create = self
767            .treasury_transaction()
768            .anchor_args(args::CreateSwapV2 {
769                nonce,
770                swap_path_length: swap_path
771                    .len()
772                    .try_into()
773                    .map_err(|_| crate::Error::custom("swap path is too long"))?,
774                swap_in_amount: swap_in_token_amount,
775                min_swap_out_amount: options.min_swap_out_amount,
776                callback_version,
777            })
778            .anchor_accounts(accounts::CreateSwapV2 {
779                authority: self.payer(),
780                store: *store,
781                config,
782                treasury_vault_config,
783                swap_in_token: *swap_in_token,
784                swap_out_token: *swap_out_token,
785                swap_in_token_receiver_vault,
786                market,
787                receiver,
788                user,
789                swap_in_token_escrow,
790                swap_out_token_escrow,
791                order,
792                store_program: *self.store_program_id(),
793                token_program: token_program_id,
794                associated_token_program: anchor_spl::associated_token::ID,
795                system_program: system_program::ID,
796                event_authority: self.store_event_authority(),
797                callback_authority,
798                callback_program,
799                callback_shared_data_account,
800                callback_partitioned_data_account,
801            })
802            .accounts(swap_path);
803
804        Ok(prepare_ata
805            .merge(prepare_swap_in_escrow)
806            .merge(prepare_swap_out_escrow)
807            .merge(create)
808            .output(order))
809    }
810
811    async fn cancel_treasury_swap(
812        &self,
813        store: &Pubkey,
814        order: &Pubkey,
815        hint: Option<(&Pubkey, &Pubkey)>,
816    ) -> crate::Result<TransactionBuilder<C>> {
817        let config = self.find_treasury_config_address(store);
818        let receiver = self.find_treasury_receiver_address(&config);
819        let user = self.find_user_address(store, &receiver);
820
821        let (swap_in_token, swap_out_token) = match hint {
822            Some((swap_in_token, swap_out_token)) => (*swap_in_token, *swap_out_token),
823            None => {
824                let order = self.order(order).await?;
825                let swap_in_token = order.tokens.initial_collateral.token().ok_or_else(|| {
826                    crate::Error::custom("invalid swap order: missing swap in token")
827                })?;
828
829                let swap_out_token = order.tokens.final_output_token.token().ok_or_else(|| {
830                    crate::Error::custom("invalid swap order: missing swap out token")
831                })?;
832                (swap_in_token, swap_out_token)
833            }
834        };
835
836        // Currently only SPL-Token is supported.
837        let token_program_id = anchor_spl::token::ID;
838
839        let swap_in_token_receiver_vault = get_associated_token_address_with_program_id(
840            &receiver,
841            &swap_in_token,
842            &token_program_id,
843        );
844        let swap_out_token_receiver_vault = get_associated_token_address_with_program_id(
845            &receiver,
846            &swap_out_token,
847            &token_program_id,
848        );
849        let swap_in_token_escrow =
850            get_associated_token_address_with_program_id(order, &swap_in_token, &token_program_id);
851        let swap_out_token_escrow =
852            get_associated_token_address_with_program_id(order, &swap_out_token, &token_program_id);
853
854        let prepare = self.prepare_associated_token_account(
855            &swap_out_token,
856            &token_program_id,
857            Some(&receiver),
858        );
859
860        let cancel = self
861            .treasury_transaction()
862            .anchor_args(args::CancelSwap {})
863            .anchor_accounts(accounts::CancelSwap {
864                authority: self.payer(),
865                store: *store,
866                store_wallet: self.find_store_wallet_address(store),
867                config,
868                receiver,
869                user,
870                swap_in_token,
871                swap_out_token,
872                swap_in_token_receiver_vault,
873                swap_out_token_receiver_vault,
874                swap_in_token_escrow,
875                swap_out_token_escrow,
876                order: *order,
877                event_authority: self.store_event_authority(),
878                store_program: *self.store_program_id(),
879                token_program: token_program_id,
880                associated_token_program: anchor_spl::associated_token::ID,
881                system_program: system_program::ID,
882            });
883
884        Ok(prepare.merge(cancel))
885    }
886}
887
888/// Create Treasury Swap Options.
889#[derive(Debug, Clone, Default)]
890pub struct CreateTreasurySwapOptions {
891    /// Nonce.
892    pub nonce: Option<NonceBytes>,
893    /// The market tokens of the swap path.
894    pub swap_path: Vec<Pubkey>,
895    /// Min swap out amount.
896    pub min_swap_out_amount: Option<u64>,
897    /// Hint for the treasury vault config address.
898    pub treasury_vault_config_hint: Option<Pubkey>,
899    /// Callback.
900    pub callback: Option<Callback>,
901}
902
903/// Confirm GT buyback builder.
904pub struct ConfirmGtBuybackBuilder<'a, C> {
905    client: &'a crate::Client<C>,
906    store: Pubkey,
907    gt_exchange_vault: Pubkey,
908    oracle: Pubkey,
909    with_chainlink_program: bool,
910    feeds_parser: FeedsParser,
911    hint: Option<ConfirmGtBuybackHint>,
912}
913
914/// Hint for confirming GT buyback.
915#[derive(Debug, Clone)]
916pub struct ConfirmGtBuybackHint {
917    config: Pubkey,
918    treasury_vault_config: Pubkey,
919    token_map: Pubkey,
920    treasury_tokens: Vec<Pubkey>,
921    feeds: TokensWithFeed,
922}
923
924impl<'a, C: Deref<Target = impl Signer> + Clone> ConfirmGtBuybackBuilder<'a, C> {
925    pub(super) fn new(
926        client: &'a crate::Client<C>,
927        store: &Pubkey,
928        gt_exchange_vault: &Pubkey,
929        oracle: &Pubkey,
930    ) -> Self {
931        Self {
932            client,
933            store: *store,
934            gt_exchange_vault: *gt_exchange_vault,
935            oracle: *oracle,
936            with_chainlink_program: false,
937            feeds_parser: Default::default(),
938            hint: None,
939        }
940    }
941
942    /// Prepare [`ConfirmGtBuybackHint`].
943    pub async fn prepare_hint(&mut self) -> crate::Result<ConfirmGtBuybackHint> {
944        match &self.hint {
945            Some(hint) => Ok(hint.clone()),
946            None => {
947                let (config, treasury_vault_config_address) =
948                    find_config_addresses(self.client, &self.store, None).await?;
949                let gt_bank = self
950                    .client
951                    .find_gt_bank_address(&treasury_vault_config_address, &self.gt_exchange_vault);
952                let map_address = self
953                    .client
954                    .authorized_token_map_address(&self.store)
955                    .await?
956                    .ok_or_else(|| crate::Error::custom("token map is not set"))?;
957                let map = self.client.token_map(&map_address).await?;
958                let gt_bank = self
959                    .client
960                    .account::<ZeroCopy<GtBank>>(&gt_bank)
961                    .await?
962                    .ok_or(crate::Error::NotFound)?
963                    .0;
964                let treasury_vault_config = self
965                    .client
966                    .account::<ZeroCopy<TreasuryVaultConfig>>(&treasury_vault_config_address)
967                    .await?
968                    .ok_or(crate::Error::NotFound)?
969                    .0;
970                let hint = ConfirmGtBuybackHint {
971                    config,
972                    treasury_vault_config: treasury_vault_config_address,
973                    token_map: map_address,
974                    treasury_tokens: treasury_vault_config.tokens().collect(),
975                    feeds: gt_bank.to_feeds(&map, &treasury_vault_config)?,
976                };
977                self.hint = Some(hint.clone());
978                Ok(hint)
979            }
980        }
981    }
982
983    async fn build_txn(&mut self) -> crate::Result<TransactionBuilder<'a, C>> {
984        let hint = self.prepare_hint().await?;
985
986        let gt_bank = self
987            .client
988            .find_gt_bank_address(&hint.treasury_vault_config, &self.gt_exchange_vault);
989
990        let token_program_id = anchor_spl::token::ID;
991
992        let feeds = self.feeds_parser.parse_and_sort_by_tokens(&hint.feeds)?;
993        let tokens = hint.treasury_tokens.iter().map(|pubkey| AccountMeta {
994            pubkey: *pubkey,
995            is_signer: false,
996            is_writable: false,
997        });
998        let vaults = hint.treasury_tokens.iter().map(|token| {
999            let pubkey = get_associated_token_address_with_program_id(
1000                &hint.treasury_vault_config,
1001                token,
1002                &token_program_id,
1003            );
1004            AccountMeta {
1005                pubkey,
1006                is_signer: false,
1007                is_writable: false,
1008            }
1009        });
1010
1011        let chainlink_program = if self.with_chainlink_program {
1012            Some(program_ids::CHAINLINK)
1013        } else {
1014            None
1015        };
1016
1017        let rpc = self
1018            .client
1019            .treasury_transaction()
1020            .anchor_args(args::ConfirmGtBuyback {})
1021            .accounts(fix_optional_account_metas(
1022                accounts::ConfirmGtBuyback {
1023                    authority: self.client.payer(),
1024                    store: self.store,
1025                    config: hint.config,
1026                    treasury_vault_config: hint.treasury_vault_config,
1027                    gt_exchange_vault: self.gt_exchange_vault,
1028                    gt_bank,
1029                    token_map: hint.token_map,
1030                    oracle: self.oracle,
1031                    event_authority: self.client.store_event_authority(),
1032                    store_program: *self.client.store_program_id(),
1033                    chainlink_program,
1034                },
1035                &ID,
1036                self.client.treasury_program_id(),
1037            ))
1038            .accounts(feeds)
1039            .accounts(tokens.chain(vaults).collect::<Vec<_>>());
1040
1041        Ok(rpc)
1042    }
1043}
1044
1045impl<'a, C: Deref<Target = impl Signer> + Clone> MakeBundleBuilder<'a, C>
1046    for ConfirmGtBuybackBuilder<'a, C>
1047{
1048    async fn build_with_options(
1049        &mut self,
1050        options: BundleOptions,
1051    ) -> gmsol_solana_utils::Result<BundleBuilder<'a, C>> {
1052        let mut tx = self.client.bundle_with_options(options);
1053        tx.try_push(
1054            self.build_txn()
1055                .await
1056                .map_err(gmsol_solana_utils::Error::custom)?,
1057        )?;
1058
1059        Ok(tx)
1060    }
1061}
1062
1063impl<C: Deref<Target = impl Signer> + Clone> PullOraclePriceConsumer
1064    for ConfirmGtBuybackBuilder<'_, C>
1065{
1066    async fn feed_ids(&mut self) -> crate::Result<FeedIds> {
1067        let hint = self.prepare_hint().await?;
1068        Ok(FeedIds::new(self.store, hint.feeds))
1069    }
1070
1071    fn process_feeds(
1072        &mut self,
1073        provider: PriceProviderKind,
1074        map: FeedAddressMap,
1075    ) -> crate::Result<()> {
1076        self.feeds_parser
1077            .insert_pull_oracle_feed_parser(provider, map);
1078        Ok(())
1079    }
1080}
1081
1082impl<C> SetExecutionFee for ConfirmGtBuybackBuilder<'_, C> {
1083    fn set_execution_fee(&mut self, _lamports: u64) -> &mut Self {
1084        self
1085    }
1086}
1087
1088async fn find_config_addresses<C: Deref<Target = impl Signer> + Clone>(
1089    client: &crate::Client<C>,
1090    store: &Pubkey,
1091    treasury_vault_config: Option<&Pubkey>,
1092) -> crate::Result<(Pubkey, Pubkey)> {
1093    let config = client.find_treasury_config_address(store);
1094    match treasury_vault_config {
1095        Some(address) => Ok((config, *address)),
1096        None => {
1097            let config_account = client
1098                .account::<ZeroCopy<Config>>(&config)
1099                .await?
1100                .ok_or(crate::Error::NotFound)?
1101                .0;
1102            Ok((
1103                config,
1104                *optional_address(&config_account.treasury_vault_config)
1105                    .ok_or_else(|| crate::Error::custom("treasury vault config is not set"))?,
1106            ))
1107        }
1108    }
1109}