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
39pub trait TreasuryOps<C> {
41 fn initialize_config(&self, store: &Pubkey) -> TransactionBuilder<C, Pubkey>;
43
44 fn set_treasury_vault_config(
46 &self,
47 store: &Pubkey,
48 treasury_vault_config: &Pubkey,
49 ) -> TransactionBuilder<C>;
50
51 fn set_gt_factor(&self, store: &Pubkey, factor: u128) -> crate::Result<TransactionBuilder<C>>;
53
54 fn set_buyback_factor(
56 &self,
57 store: &Pubkey,
58 factor: u128,
59 ) -> crate::Result<TransactionBuilder<C>>;
60
61 fn initialize_treasury_vault_config(
63 &self,
64 store: &Pubkey,
65 index: u16,
66 ) -> TransactionBuilder<C, Pubkey>;
67
68 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 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 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 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 #[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 fn confirm_gt_buyback(
119 &self,
120 store: &Pubkey,
121 gt_exchange_vault: &Pubkey,
122 oracle: &Pubkey,
123 ) -> ConfirmGtBuybackBuilder<C>;
124
125 fn transfer_receiver(&self, store: &Pubkey, new_receiver: &Pubkey) -> TransactionBuilder<C>;
127
128 fn set_referral_reward(&self, store: &Pubkey, factors: Vec<u128>) -> TransactionBuilder<C>;
130
131 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 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 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 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 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 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), >_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(>_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(>_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(>_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(>_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, >_exchange_vault);
617
618 let tokens = match tokens_hint {
619 Some(tokens) => tokens,
620 None => {
621 let gt_bank = self
622 .account::<ZeroCopy<GtBank>>(>_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(>_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 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 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#[derive(Debug, Clone, Default)]
890pub struct CreateTreasurySwapOptions {
891 pub nonce: Option<NonceBytes>,
893 pub swap_path: Vec<Pubkey>,
895 pub min_swap_out_amount: Option<u64>,
897 pub treasury_vault_config_hint: Option<Pubkey>,
899 pub callback: Option<Callback>,
901}
902
903pub 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#[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 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>>(>_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}