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