jito_restaking_cli/
vault_handler.rs

1use std::{path::PathBuf, str::FromStr};
2
3use anyhow::{anyhow, Result};
4use borsh::BorshDeserialize;
5use jito_bytemuck::AccountDeserialize;
6use jito_jsm_core::get_epoch;
7use jito_restaking_client_common::log::PrettyDisplay;
8use jito_restaking_core::{
9    ncn_vault_ticket::NcnVaultTicket, operator_vault_ticket::OperatorVaultTicket,
10};
11use jito_vault_client::{
12    instructions::{
13        AddDelegationBuilder, BurnWithdrawalTicketBuilder, ChangeWithdrawalTicketOwnerBuilder,
14        CloseVaultUpdateStateTrackerBuilder, CooldownDelegationBuilder,
15        CooldownVaultNcnTicketBuilder, CrankVaultUpdateStateTrackerBuilder,
16        CreateTokenMetadataBuilder, DelegateTokenAccountBuilder, EnqueueWithdrawalBuilder,
17        InitializeConfigBuilder, InitializeVaultBuilder, InitializeVaultNcnTicketBuilder,
18        InitializeVaultOperatorDelegationBuilder, InitializeVaultUpdateStateTrackerBuilder,
19        MintToBuilder, SetAdminBuilder, SetConfigAdminBuilder, SetDepositCapacityBuilder,
20        SetFeesBuilder, SetIsPausedBuilder, SetProgramFeeBuilder, SetProgramFeeWalletBuilder,
21        SetSecondaryAdminBuilder, UpdateTokenMetadataBuilder, UpdateVaultBalanceBuilder,
22        WarmupVaultNcnTicketBuilder,
23    },
24    types::{VaultAdminRole, WithdrawalAllocationMethod},
25};
26use jito_vault_core::{
27    burn_vault::BurnVault, config::Config, vault::Vault, vault_ncn_ticket::VaultNcnTicket,
28    vault_operator_delegation::VaultOperatorDelegation,
29    vault_staker_withdrawal_ticket::VaultStakerWithdrawalTicket,
30    vault_update_state_tracker::VaultUpdateStateTracker,
31};
32use jito_vault_sdk::inline_mpl_token_metadata;
33use log::{debug, info};
34use solana_program::pubkey::Pubkey;
35use solana_rpc_client::rpc_client::SerializableTransaction;
36use solana_sdk::{
37    signature::{read_keypair_file, Keypair, Signer},
38    transaction::Transaction,
39};
40use spl_associated_token_account::{
41    get_associated_token_address, instruction::create_associated_token_account_idempotent,
42};
43use spl_token::instruction::transfer;
44
45use crate::{
46    cli_config::CliConfig,
47    cli_signer::CliSigner,
48    vault::{ConfigActions, VaultActions, VaultCommands},
49    CliHandler,
50};
51
52pub struct VaultCliHandler {
53    /// The configuration of CLI
54    cli_config: CliConfig,
55
56    /// The Pubkey of Jito Restaking Program ID
57    restaking_program_id: Pubkey,
58
59    /// The Pubkey of Jito Vault Program ID
60    vault_program_id: Pubkey,
61
62    /// This will print out the raw TX instead of running it
63    print_tx: bool,
64
65    /// This will print out the account information in JSON format
66    print_json: bool,
67
68    /// This will print out the account information in JSON format with reserved space
69    print_json_with_reserves: bool,
70}
71
72impl CliHandler for VaultCliHandler {
73    fn cli_config(&self) -> &CliConfig {
74        &self.cli_config
75    }
76
77    fn print_tx(&self) -> bool {
78        self.print_tx
79    }
80
81    fn print_json(&self) -> bool {
82        self.print_json
83    }
84
85    fn print_json_with_reserves(&self) -> bool {
86        self.print_json_with_reserves
87    }
88}
89
90impl VaultCliHandler {
91    pub const fn new(
92        cli_config: CliConfig,
93        restaking_program_id: Pubkey,
94        vault_program_id: Pubkey,
95        print_tx: bool,
96        print_json: bool,
97        print_json_with_reserves: bool,
98    ) -> Self {
99        Self {
100            cli_config,
101            restaking_program_id,
102            vault_program_id,
103            print_tx,
104            print_json,
105            print_json_with_reserves,
106        }
107    }
108
109    #[allow(clippy::future_not_send)]
110    pub async fn handle(&self, action: VaultCommands) -> Result<()> {
111        match action {
112            VaultCommands::Config {
113                action:
114                    ConfigActions::Initialize {
115                        program_fee_bps,
116                        program_fee_wallet,
117                    },
118            } => {
119                self.initialize_config(program_fee_bps, program_fee_wallet)
120                    .await
121            }
122            VaultCommands::Config {
123                action: ConfigActions::Get,
124            } => self.get_config().await,
125            VaultCommands::Config {
126                action: ConfigActions::SetAdmin { new_admin },
127            } => self.set_config_admin(new_admin).await,
128            VaultCommands::Config {
129                action: ConfigActions::SetProgramFee { new_fee_bps },
130            } => self.set_program_fee(new_fee_bps).await,
131            VaultCommands::Config {
132                action: ConfigActions::SetProgramFeeWallet { program_fee_wallet },
133            } => self.set_program_fee_wallet(&program_fee_wallet).await,
134            VaultCommands::Vault {
135                action:
136                    VaultActions::Initialize {
137                        token_mint,
138                        deposit_fee_bps,
139                        withdrawal_fee_bps,
140                        reward_fee_bps,
141                        decimals,
142                        initialize_token_amount,
143                        vrt_mint_address_file_path,
144                    },
145            } => {
146                self.initialize_vault(
147                    token_mint,
148                    deposit_fee_bps,
149                    withdrawal_fee_bps,
150                    reward_fee_bps,
151                    decimals,
152                    initialize_token_amount,
153                    vrt_mint_address_file_path,
154                )
155                .await
156            }
157            VaultCommands::Vault {
158                action:
159                    VaultActions::CreateTokenMetadata {
160                        vault,
161                        name,
162                        symbol,
163                        uri,
164                    },
165            } => self.create_token_metadata(vault, name, symbol, uri).await,
166            VaultCommands::Vault {
167                action:
168                    VaultActions::UpdateTokenMetadata {
169                        vault,
170                        name,
171                        symbol,
172                        uri,
173                    },
174            } => self.update_token_metadata(vault, name, symbol, uri).await,
175            VaultCommands::Vault {
176                action: VaultActions::InitializeVaultUpdateStateTracker { vault },
177            } => self.initialize_vault_update_state_tracker(vault).await,
178            VaultCommands::Vault {
179                action: VaultActions::CrankVaultUpdateStateTracker { vault, operator },
180            } => self.crank_vault_update_state_tracker(vault, operator).await,
181            VaultCommands::Vault {
182                action: VaultActions::CloseVaultUpdateStateTracker { vault, ncn_epoch },
183            } => {
184                self.close_vault_update_state_tracker(vault, ncn_epoch)
185                    .await
186            }
187            VaultCommands::Vault {
188                action:
189                    VaultActions::MintVRT {
190                        vault,
191                        amount_in,
192                        min_amount_out,
193                    },
194            } => self.mint_vrt(vault, amount_in, min_amount_out).await,
195            VaultCommands::Vault {
196                action: VaultActions::InitializeVaultNcnTicket { vault, ncn },
197            } => self.initialize_vault_ncn_ticket(vault, ncn).await,
198            VaultCommands::Vault {
199                action: VaultActions::WarmupVaultNcnTicket { vault, ncn },
200            } => self.warmup_vault_ncn_ticket(vault, ncn).await,
201            VaultCommands::Vault {
202                action: VaultActions::CooldownVaultNcnTicket { vault, ncn },
203            } => self.cooldown_vault_ncn_ticket(vault, ncn).await,
204
205            VaultCommands::Vault {
206                action: VaultActions::InitializeOperatorDelegation { vault, operator },
207            } => {
208                self.initialize_vault_operator_delegation(vault, operator)
209                    .await
210            }
211            VaultCommands::Vault {
212                action:
213                    VaultActions::DelegateToOperator {
214                        vault,
215                        operator,
216                        amount,
217                    },
218            } => self.delegate_to_operator(vault, operator, amount).await,
219            VaultCommands::Vault {
220                action:
221                    VaultActions::CooldownOperatorDelegation {
222                        vault,
223                        operator,
224                        amount,
225                    },
226            } => {
227                self.cooldown_operator_delegation(vault, operator, amount)
228                    .await
229            }
230            VaultCommands::Vault {
231                action: VaultActions::EnqueueWithdrawal { vault, amount },
232            } => self.enqueue_withdrawal(vault, amount).await,
233            VaultCommands::Vault {
234                action:
235                    VaultActions::ChangeWithdrawalTicketOwner {
236                        vault,
237                        old_ticket_owner_keypair,
238                        new_ticket_owner,
239                    },
240            } => {
241                self.change_withdrawal_ticket_owner(
242                    &vault,
243                    &old_ticket_owner_keypair,
244                    &new_ticket_owner,
245                )
246                .await
247            }
248            VaultCommands::Vault {
249                action: VaultActions::BurnWithdrawalTicket { vault },
250            } => self.burn_withdrawal_ticket(vault).await,
251            VaultCommands::Vault {
252                action: VaultActions::GetVaultUpdateStateTracker { vault },
253            } => self.get_vault_update_state_tracker(vault).await,
254            VaultCommands::Vault {
255                action: VaultActions::GetOperatorDelegations { vault },
256            } => self.get_vault_operator_delegations(vault, None).await,
257            VaultCommands::Vault {
258                action: VaultActions::GetOperatorDelegation { vault, operator },
259            } => {
260                self.get_vault_operator_delegations(vault, Some(operator))
261                    .await
262            }
263            VaultCommands::Vault {
264                action: VaultActions::GetWithdrawalTicket { vault, staker },
265            } => self.get_withdrawal_ticket(vault, staker).await,
266            VaultCommands::Vault {
267                action: VaultActions::Get { pubkey },
268            } => self.get_vault(pubkey).await,
269            VaultCommands::Vault {
270                action: VaultActions::List,
271            } => self.list_vaults().await,
272            VaultCommands::Vault {
273                action:
274                    VaultActions::SetAdmin {
275                        vault,
276                        old_admin_keypair,
277                        new_admin_keypair,
278                    },
279            } => {
280                self.set_admin(&vault, &old_admin_keypair, &new_admin_keypair)
281                    .await
282            }
283            VaultCommands::Vault {
284                action: VaultActions::SetCapacity { vault, amount },
285            } => self.set_capacity(vault, amount).await,
286            VaultCommands::Vault {
287                action:
288                    VaultActions::SetFees {
289                        vault,
290                        deposit_fee_bps,
291                        withdrawal_fee_bps,
292                        reward_fee_bps,
293                    },
294            } => {
295                self.set_fees(&vault, deposit_fee_bps, withdrawal_fee_bps, reward_fee_bps)
296                    .await
297            }
298            VaultCommands::Vault {
299                action: VaultActions::SetIsPaused { vault, set_pause },
300            } => self.set_is_paused(&vault, set_pause).await,
301            VaultCommands::Vault {
302                action:
303                    VaultActions::SetSecondaryAdmin {
304                        vault,
305                        new_admin,
306                        set_delegation_admin,
307                        set_operator_admin,
308                        set_ncn_admin,
309                        set_slasher_admin,
310                        set_capacity_admin,
311                        set_fee_wallet,
312                        set_mint_burn_admin,
313                        set_delegate_asset_admin,
314                        set_fee_admin,
315                        set_metadata_admin,
316                    },
317            } => {
318                self.set_secondary_admin(
319                    &vault,
320                    &new_admin,
321                    set_delegation_admin,
322                    set_operator_admin,
323                    set_ncn_admin,
324                    set_slasher_admin,
325                    set_capacity_admin,
326                    set_fee_wallet,
327                    set_mint_burn_admin,
328                    set_delegate_asset_admin,
329                    set_fee_admin,
330                    set_metadata_admin,
331                )
332                .await
333            }
334            VaultCommands::Vault {
335                action: VaultActions::UpdateVaultBalance { vault },
336            } => self.update_vault_balance(&vault).await,
337            VaultCommands::Vault {
338                action:
339                    VaultActions::DelegateTokenAccount {
340                        vault,
341                        delegate,
342                        token_mint,
343                        token_account,
344                    },
345            } => {
346                self.delegate_token_account(vault, delegate, token_mint, token_account)
347                    .await
348            }
349            VaultCommands::Vault {
350                action:
351                    VaultActions::DelegatedTokenTransfer {
352                        token_account,
353                        recipient_pubkey,
354                        amount,
355                    },
356            } => {
357                self.delegated_token_transfer(token_account, recipient_pubkey, amount)
358                    .await
359            }
360        }
361    }
362
363    #[allow(clippy::future_not_send)]
364    pub async fn initialize_config(
365        &self,
366        program_fee_bps: u16,
367        program_fee_wallet: Pubkey,
368    ) -> Result<()> {
369        let signer = self
370            .cli_config
371            .signer
372            .as_ref()
373            .ok_or_else(|| anyhow!("No Signer"))?;
374
375        let mut ix_builder = InitializeConfigBuilder::new();
376        let config_address = Config::find_program_address(&self.vault_program_id).0;
377        let ix_builder = ix_builder
378            .config(config_address)
379            .admin(signer.pubkey())
380            .restaking_program(self.restaking_program_id)
381            .program_fee_wallet(program_fee_wallet)
382            .program_fee_bps(program_fee_bps);
383        let mut ix = ix_builder.instruction();
384        ix.program_id = self.vault_program_id;
385
386        info!("Initializing vault config parameters: {:?}", ix_builder);
387
388        self.process_transaction(&[ix], &signer.pubkey(), &[signer])
389            .await?;
390
391        if !self.print_tx {
392            let account = self
393                .get_account::<jito_vault_client::accounts::Config>(&config_address)
394                .await?;
395            info!("{}", account.pretty_display());
396        }
397
398        Ok(())
399    }
400
401    #[allow(clippy::too_many_arguments, clippy::future_not_send)]
402    pub async fn initialize_vault(
403        &self,
404        token_mint: String,
405        deposit_fee_bps: u16,
406        withdrawal_fee_bps: u16,
407        reward_fee_bps: u16,
408        decimals: u8,
409        initialize_token_amount: u64,
410        vrt_mint_address_file_path: Option<PathBuf>,
411    ) -> Result<()> {
412        let token_mint = Pubkey::from_str(&token_mint)?;
413        let signer = self
414            .cli_config
415            .signer
416            .as_ref()
417            .ok_or_else(|| anyhow!("No Signer"))?;
418
419        let admin = signer.pubkey();
420
421        let base_signer = CliSigner::new(Some(Keypair::new()), None);
422        let vault = Vault::find_program_address(&self.vault_program_id, &base_signer.pubkey()).0;
423
424        let vrt_mint_signer = match vrt_mint_address_file_path {
425            Some(file_path) => {
426                let keypair = read_keypair_file(file_path)
427                    .map_err(|e| anyhow!("Could not read VRT mint address file path: {e}"))?;
428                info!("Found VRT mint address: {}", keypair.pubkey());
429
430                CliSigner::new(Some(keypair), None)
431            }
432            None => CliSigner::new(Some(Keypair::new()), None),
433        };
434
435        let admin_st_token_account = get_associated_token_address(&admin, &token_mint);
436        let vault_st_token_account = get_associated_token_address(&vault, &token_mint);
437
438        let (burn_vault, _, _) =
439            BurnVault::find_program_address(&self.vault_program_id, &base_signer.pubkey());
440
441        let burn_vault_vrt_token_account =
442            get_associated_token_address(&burn_vault, &vrt_mint_signer.pubkey());
443
444        let mut ix_builder = InitializeVaultBuilder::new();
445        ix_builder
446            .config(Config::find_program_address(&self.vault_program_id).0)
447            .vault(vault)
448            .vrt_mint(vrt_mint_signer.pubkey())
449            .st_mint(token_mint)
450            .admin(admin)
451            .base(base_signer.pubkey())
452            .admin_st_token_account(admin_st_token_account)
453            .vault_st_token_account(vault_st_token_account)
454            .burn_vault(burn_vault)
455            .burn_vault_vrt_token_account(burn_vault_vrt_token_account)
456            .associated_token_program(spl_associated_token_account::id())
457            .deposit_fee_bps(deposit_fee_bps)
458            .withdrawal_fee_bps(withdrawal_fee_bps)
459            .reward_fee_bps(reward_fee_bps)
460            .decimals(decimals)
461            .initialize_token_amount(initialize_token_amount);
462        let mut ix = ix_builder.instruction();
463        ix.program_id = self.vault_program_id;
464
465        let admin_st_token_account_ix =
466            create_associated_token_account_idempotent(&admin, &admin, &token_mint, &spl_token::ID);
467
468        let vault_st_token_account_ix =
469            create_associated_token_account_idempotent(&admin, &vault, &token_mint, &spl_token::ID);
470
471        info!("Initializing Vault at address: {}", vault);
472
473        let ixs = [admin_st_token_account_ix, vault_st_token_account_ix, ix];
474        self.process_transaction(
475            &ixs,
476            &signer.pubkey(),
477            &[signer, &base_signer, &vrt_mint_signer],
478        )
479        .await?;
480
481        if !self.print_tx {
482            let account = self
483                .get_account::<jito_vault_client::accounts::Vault>(&vault)
484                .await?;
485            info!("{}", account.pretty_display());
486        }
487
488        Ok(())
489    }
490
491    #[allow(clippy::future_not_send)]
492    async fn create_token_metadata(
493        &self,
494        vault: String,
495        name: String,
496        symbol: String,
497        uri: String,
498    ) -> Result<()> {
499        let signer = self
500            .cli_config
501            .signer
502            .as_ref()
503            .ok_or_else(|| anyhow!("No signer"))?;
504        let vault_pubkey = Pubkey::from_str(&vault)?;
505
506        let rpc_client = self.get_rpc_client();
507        let vault_account = rpc_client.get_account(&vault_pubkey).await?;
508        let vault = Vault::try_from_slice_unchecked(&vault_account.data)?;
509
510        let metadata = Pubkey::find_program_address(
511            &[
512                b"metadata",
513                inline_mpl_token_metadata::id().as_ref(),
514                vault.vrt_mint.as_ref(),
515            ],
516            &inline_mpl_token_metadata::id(),
517        )
518        .0;
519
520        let mut ix = CreateTokenMetadataBuilder::new()
521            .vault(vault_pubkey)
522            .admin(signer.pubkey())
523            .vrt_mint(vault.vrt_mint)
524            .payer(signer.pubkey())
525            .metadata(metadata)
526            .name(name)
527            .symbol(symbol)
528            .uri(uri)
529            .instruction();
530        ix.program_id = self.vault_program_id;
531
532        info!("Creating token metadata transaction",);
533
534        self.process_transaction(&[ix], &signer.pubkey(), &[signer])
535            .await?;
536
537        Ok(())
538    }
539
540    #[allow(clippy::future_not_send)]
541    async fn update_token_metadata(
542        &self,
543        vault: String,
544        name: String,
545        symbol: String,
546        uri: String,
547    ) -> Result<()> {
548        let signer = self
549            .cli_config
550            .signer
551            .as_ref()
552            .ok_or_else(|| anyhow!("Keypair not provided"))?;
553        let vault_pubkey = Pubkey::from_str(&vault)?;
554
555        let rpc_client = self.get_rpc_client();
556        let vault_account = rpc_client.get_account(&vault_pubkey).await?;
557        let vault = Vault::try_from_slice_unchecked(&vault_account.data)?;
558
559        let metadata = Pubkey::find_program_address(
560            &[
561                b"metadata",
562                inline_mpl_token_metadata::id().as_ref(),
563                vault.vrt_mint.as_ref(),
564            ],
565            &inline_mpl_token_metadata::id(),
566        )
567        .0;
568
569        let ix = UpdateTokenMetadataBuilder::new()
570            .vault(vault_pubkey)
571            .admin(signer.pubkey())
572            .vrt_mint(vault.vrt_mint)
573            .metadata(metadata)
574            .name(name)
575            .symbol(symbol)
576            .uri(uri)
577            .instruction();
578
579        let recent_blockhash = rpc_client.get_latest_blockhash().await?;
580        let tx = Transaction::new_signed_with_payer(
581            &[ix],
582            Some(&signer.pubkey()),
583            &[signer],
584            recent_blockhash,
585        );
586
587        info!(
588            "Updating token metadata transaction: {:?}",
589            tx.get_signature()
590        );
591        rpc_client
592            .send_and_confirm_transaction(&tx)
593            .await
594            .map_err(|e| anyhow!(e.to_string()))?;
595        info!("Transaction confirmed: {:?}", tx.get_signature());
596
597        Ok(())
598    }
599
600    // ---------- UPDATE ------------
601    #[allow(clippy::future_not_send)]
602    pub async fn initialize_vault_update_state_tracker(&self, vault: String) -> Result<()> {
603        let signer = self
604            .cli_config
605            .signer
606            .as_ref()
607            .ok_or_else(|| anyhow!("Keypair not provided"))?;
608        let rpc_client = self.get_rpc_client();
609
610        let config = Config::find_program_address(&self.vault_program_id).0;
611
612        let config_account_raw = rpc_client.get_account(&config).await?;
613        let config_account = Config::try_from_slice_unchecked(&config_account_raw.data)?;
614
615        let current_slot = rpc_client.get_slot().await?;
616
617        let ncn_epoch = get_epoch(current_slot, config_account.epoch_length())?;
618
619        let vault = Pubkey::from_str(&vault)?;
620        let vault_update_state_tracker = VaultUpdateStateTracker::find_program_address(
621            &self.vault_program_id,
622            &vault,
623            ncn_epoch,
624        )
625        .0;
626
627        let mut ix_builder = InitializeVaultUpdateStateTrackerBuilder::new();
628        ix_builder
629            .config(Config::find_program_address(&self.vault_program_id).0)
630            .vault(vault)
631            .vault_update_state_tracker(vault_update_state_tracker)
632            .payer(signer.pubkey())
633            .withdrawal_allocation_method(WithdrawalAllocationMethod::Greedy); // Only withdrawal allocation method supported for now
634
635        let blockhash = rpc_client.get_latest_blockhash().await?;
636        let tx = Transaction::new_signed_with_payer(
637            &[ix_builder.instruction()],
638            Some(&signer.pubkey()),
639            &[signer],
640            blockhash,
641        );
642        info!(
643            "Initializing vault update state tracker transaction: {:?}",
644            tx.get_signature()
645        );
646
647        let result = rpc_client.send_and_confirm_transaction(&tx).await;
648
649        if result.is_err() {
650            return Err(anyhow::anyhow!("Transaction failed: {:?}", result.err()));
651        }
652
653        info!("Transaction confirmed: {:?}", tx.get_signature());
654
655        info!("\nCreated Update State Tracker");
656        info!("Vault address: {}", vault);
657        info!(
658            "Vault Update State Tracker address: {}",
659            vault_update_state_tracker
660        );
661        info!("NCN Epoch: {}", ncn_epoch);
662
663        Ok(())
664    }
665
666    #[allow(clippy::future_not_send)]
667    pub async fn crank_vault_update_state_tracker(
668        &self,
669        vault: String,
670        operator: String,
671    ) -> Result<()> {
672        //TODO V2: Make it so the operator needed is automatically fetched from the vault
673
674        let signer = self
675            .cli_config
676            .signer
677            .as_ref()
678            .ok_or_else(|| anyhow!("Keypair not provided"))?;
679        let rpc_client = self.get_rpc_client();
680
681        let config = Config::find_program_address(&self.vault_program_id).0;
682
683        let vault = Pubkey::from_str(&vault)?;
684        let operator = Pubkey::from_str(&operator)?;
685
686        let vault_operator_delegation = VaultOperatorDelegation::find_program_address(
687            &self.vault_program_id,
688            &vault,
689            &operator,
690        )
691        .0;
692
693        let ncn_epoch = {
694            let config_account_raw = rpc_client.get_account(&config).await?;
695            let config_account = Config::try_from_slice_unchecked(&config_account_raw.data)?;
696
697            let current_slot = rpc_client.get_slot().await?;
698            get_epoch(current_slot, config_account.epoch_length()).unwrap()
699        };
700
701        let vault_update_state_tracker = VaultUpdateStateTracker::find_program_address(
702            &self.vault_program_id,
703            &vault,
704            ncn_epoch,
705        )
706        .0;
707
708        let mut ix_builder = CrankVaultUpdateStateTrackerBuilder::new();
709        ix_builder
710            .config(config)
711            .vault(vault)
712            .operator(operator)
713            .vault_operator_delegation(vault_operator_delegation)
714            .vault_update_state_tracker(vault_update_state_tracker);
715
716        let blockhash = rpc_client.get_latest_blockhash().await?;
717        let tx = Transaction::new_signed_with_payer(
718            &[ix_builder.instruction()],
719            Some(&signer.pubkey()),
720            &[signer],
721            blockhash,
722        );
723        info!(
724            "Cranking vault update state tracker: {:?}",
725            tx.get_signature()
726        );
727        let result = rpc_client.send_and_confirm_transaction(&tx).await;
728
729        if result.is_err() {
730            return Err(anyhow::anyhow!("Transaction failed: {:?}", result.err()));
731        }
732
733        info!("Transaction confirmed: {:?}", tx.get_signature());
734
735        Ok(())
736    }
737
738    #[allow(clippy::future_not_send)]
739    pub async fn close_vault_update_state_tracker(
740        &self,
741        vault: String,
742        ncn_epoch: Option<u64>,
743    ) -> Result<()> {
744        let signer = self
745            .cli_config
746            .signer
747            .as_ref()
748            .ok_or_else(|| anyhow!("Keypair not provided"))?;
749        let rpc_client = self.get_rpc_client();
750
751        let config = Config::find_program_address(&self.vault_program_id).0;
752
753        let ncn_epoch = match ncn_epoch {
754            Some(ncn_epoch) => ncn_epoch,
755            None => {
756                let config_account_raw = rpc_client.get_account(&config).await?;
757                let config_account = Config::try_from_slice_unchecked(&config_account_raw.data)?;
758
759                let current_slot = rpc_client.get_slot().await?;
760                get_epoch(current_slot, config_account.epoch_length()).unwrap()
761            }
762        };
763
764        let vault = Pubkey::from_str(&vault)?;
765        let vault_update_state_tracker = VaultUpdateStateTracker::find_program_address(
766            &self.vault_program_id,
767            &vault,
768            ncn_epoch,
769        )
770        .0;
771
772        let mut ix_builder = CloseVaultUpdateStateTrackerBuilder::new();
773        ix_builder
774            .config(Config::find_program_address(&self.vault_program_id).0)
775            .vault(vault)
776            .vault_update_state_tracker(vault_update_state_tracker)
777            .ncn_epoch(ncn_epoch)
778            .payer(signer.pubkey());
779
780        let blockhash = rpc_client.get_latest_blockhash().await?;
781        let tx = Transaction::new_signed_with_payer(
782            &[ix_builder.instruction()],
783            Some(&signer.pubkey()),
784            &[signer],
785            blockhash,
786        );
787        info!(
788            "Closing vault update state tracker transaction: {:?}",
789            tx.get_signature()
790        );
791        let result = rpc_client.send_and_confirm_transaction(&tx).await;
792
793        if result.is_err() {
794            return Err(anyhow::anyhow!("Transaction failed: {:?}", result.err()));
795        }
796        info!("Transaction confirmed: {:?}", tx.get_signature());
797
798        info!("\nClose Update State Tracker");
799        Ok(())
800    }
801
802    // ---------- FUNCTIONS --------------
803    #[allow(clippy::future_not_send)]
804    pub async fn mint_vrt(&self, vault: String, amount_in: u64, min_amount_out: u64) -> Result<()> {
805        let signer = self
806            .cli_config
807            .signer
808            .as_ref()
809            .ok_or_else(|| anyhow!("Keypair not provided"))?;
810        let rpc_client = self.get_rpc_client();
811
812        let vault = Pubkey::from_str(&vault)?;
813
814        let vault_account_raw = rpc_client.get_account(&vault).await?;
815        let vault_account = Vault::try_from_slice_unchecked(&vault_account_raw.data)?;
816
817        let depositor = signer.pubkey();
818        let depositor_token_account =
819            get_associated_token_address(&depositor, &vault_account.supported_mint);
820        let depositor_vrt_token_account =
821            get_associated_token_address(&depositor, &vault_account.vrt_mint);
822
823        let vault_token_account =
824            get_associated_token_address(&vault, &vault_account.supported_mint);
825
826        let vault_fee_token_account =
827            get_associated_token_address(&vault_account.fee_wallet, &vault_account.vrt_mint);
828
829        let depositor_ata_ix = create_associated_token_account_idempotent(
830            &depositor,
831            &depositor,
832            &vault_account.supported_mint,
833            &spl_token::ID,
834        );
835        let depositor_vrt_ata_ix = create_associated_token_account_idempotent(
836            &depositor,
837            &depositor,
838            &vault_account.vrt_mint,
839            &spl_token::ID,
840        );
841        let vault_ata_ix = create_associated_token_account_idempotent(
842            &depositor,
843            &vault,
844            &vault_account.supported_mint,
845            &spl_token::ID,
846        );
847        let vault_fee_ata_ix = create_associated_token_account_idempotent(
848            &depositor,
849            &vault_account.fee_wallet,
850            &vault_account.vrt_mint,
851            &spl_token::ID,
852        );
853
854        let mut ix_builder = MintToBuilder::new();
855        ix_builder
856            .config(Config::find_program_address(&self.vault_program_id).0)
857            .vrt_mint(vault_account.vrt_mint)
858            .depositor(depositor)
859            .depositor_token_account(depositor_token_account)
860            .depositor_vrt_token_account(depositor_vrt_token_account)
861            .vault_token_account(vault_token_account)
862            .vault_fee_token_account(vault_fee_token_account)
863            .amount_in(amount_in)
864            .min_amount_out(min_amount_out)
865            .vault(vault);
866
867        let blockhash = rpc_client.get_latest_blockhash().await?;
868        let tx = Transaction::new_signed_with_payer(
869            &[
870                depositor_ata_ix,
871                depositor_vrt_ata_ix,
872                vault_ata_ix,
873                vault_fee_ata_ix,
874                ix_builder.instruction(),
875            ],
876            Some(&signer.pubkey()),
877            &[signer],
878            blockhash,
879        );
880        info!("Mint to transaction: {:?}", tx.get_signature());
881        let result = rpc_client.send_and_confirm_transaction(&tx).await;
882
883        if result.is_err() {
884            return Err(anyhow::anyhow!("Transaction failed: {:?}", result.err()));
885        }
886
887        info!("Transaction confirmed: {:?}", tx.get_signature());
888
889        info!("\nMinted VRT");
890
891        Ok(())
892    }
893
894    #[allow(clippy::future_not_send)]
895    pub async fn initialize_vault_ncn_ticket(&self, vault: String, ncn: String) -> Result<()> {
896        let signer = self
897            .cli_config
898            .signer
899            .as_ref()
900            .ok_or_else(|| anyhow!("Keypair not provided"))?;
901
902        let vault = Pubkey::from_str(&vault)?;
903        let ncn = Pubkey::from_str(&ncn)?;
904
905        let (vault_ncn_ticket, _, _) =
906            VaultNcnTicket::find_program_address(&self.vault_program_id, &vault, &ncn);
907
908        let (ncn_vault_ticket, _, _) =
909            NcnVaultTicket::find_program_address(&self.restaking_program_id, &ncn, &vault);
910
911        let mut ix_builder = InitializeVaultNcnTicketBuilder::new();
912        ix_builder
913            .config(Config::find_program_address(&self.vault_program_id).0)
914            .vault(vault)
915            .ncn(ncn)
916            .vault_ncn_ticket(vault_ncn_ticket)
917            .ncn_vault_ticket(ncn_vault_ticket)
918            .payer(signer.pubkey())
919            .admin(signer.pubkey());
920        let mut ix = ix_builder.instruction();
921        ix.program_id = self.vault_program_id;
922
923        info!("Initialize Vault NCN Ticket");
924
925        self.process_transaction(&[ix], &signer.pubkey(), &[signer])
926            .await?;
927
928        if !self.print_tx {
929            let account = self
930                .get_account::<jito_vault_client::accounts::VaultNcnTicket>(&vault_ncn_ticket)
931                .await?;
932            info!("{}", account.pretty_display());
933        }
934
935        Ok(())
936    }
937
938    #[allow(clippy::future_not_send)]
939    pub async fn warmup_vault_ncn_ticket(&self, vault: String, ncn: String) -> Result<()> {
940        let signer = self
941            .cli_config
942            .signer
943            .as_ref()
944            .ok_or_else(|| anyhow!("Keypair not provided"))?;
945
946        let vault = Pubkey::from_str(&vault)?;
947        let ncn = Pubkey::from_str(&ncn)?;
948
949        let (vault_ncn_ticket, _, _) =
950            VaultNcnTicket::find_program_address(&self.vault_program_id, &vault, &ncn);
951
952        let mut ix_builder = WarmupVaultNcnTicketBuilder::new();
953        ix_builder
954            .config(Config::find_program_address(&self.vault_program_id).0)
955            .vault(vault)
956            .ncn(ncn)
957            .vault_ncn_ticket(vault_ncn_ticket)
958            .admin(signer.pubkey());
959        let mut ix = ix_builder.instruction();
960        ix.program_id = self.vault_program_id;
961
962        info!("Warmup Vault NCN Ticket");
963
964        self.process_transaction(&[ix], &signer.pubkey(), &[signer])
965            .await?;
966
967        if !self.print_tx {
968            let account = self
969                .get_account::<jito_vault_client::accounts::VaultNcnTicket>(&vault_ncn_ticket)
970                .await?;
971            info!("{}", account.pretty_display());
972        }
973
974        Ok(())
975    }
976
977    #[allow(clippy::future_not_send)]
978    pub async fn cooldown_vault_ncn_ticket(&self, vault: String, ncn: String) -> Result<()> {
979        let signer = self
980            .cli_config
981            .signer
982            .as_ref()
983            .ok_or_else(|| anyhow!("Keypair not provided"))?;
984
985        let vault = Pubkey::from_str(&vault)?;
986        let ncn = Pubkey::from_str(&ncn)?;
987
988        let (vault_ncn_ticket, _, _) =
989            VaultNcnTicket::find_program_address(&self.restaking_program_id, &vault, &ncn);
990
991        let mut ix_builder = CooldownVaultNcnTicketBuilder::new();
992        ix_builder
993            .config(Config::find_program_address(&self.vault_program_id).0)
994            .vault(vault)
995            .ncn(ncn)
996            .vault_ncn_ticket(vault_ncn_ticket)
997            .admin(signer.pubkey());
998        let mut ix = ix_builder.instruction();
999        ix.program_id = self.vault_program_id;
1000
1001        info!("Cooldown Vault NCN Ticket");
1002
1003        self.process_transaction(&[ix], &signer.pubkey(), &[signer])
1004            .await?;
1005
1006        if !self.print_tx {
1007            let account = self
1008                .get_account::<jito_vault_client::accounts::VaultNcnTicket>(&vault_ncn_ticket)
1009                .await?;
1010            info!("{}", account.pretty_display());
1011        }
1012
1013        Ok(())
1014    }
1015
1016    #[allow(clippy::future_not_send)]
1017    pub async fn initialize_vault_operator_delegation(
1018        &self,
1019        vault: String,
1020        operator: String,
1021    ) -> Result<()> {
1022        let signer = self
1023            .cli_config
1024            .signer
1025            .as_ref()
1026            .ok_or_else(|| anyhow!("Keypair not provided"))?;
1027
1028        let vault = Pubkey::from_str(&vault)?;
1029        let operator = Pubkey::from_str(&operator)?;
1030
1031        let operator_vault_ticket = OperatorVaultTicket::find_program_address(
1032            &self.restaking_program_id,
1033            &operator,
1034            &vault,
1035        )
1036        .0;
1037
1038        let vault_operator_delegation = VaultOperatorDelegation::find_program_address(
1039            &self.vault_program_id,
1040            &vault,
1041            &operator,
1042        )
1043        .0;
1044
1045        let mut ix_builder = InitializeVaultOperatorDelegationBuilder::new();
1046        ix_builder
1047            .config(Config::find_program_address(&self.vault_program_id).0)
1048            .vault(vault)
1049            .operator(operator)
1050            .operator_vault_ticket(operator_vault_ticket)
1051            .vault_operator_delegation(vault_operator_delegation)
1052            .payer(signer.pubkey())
1053            .admin(signer.pubkey());
1054        let mut ix = ix_builder.instruction();
1055        ix.program_id = self.vault_program_id;
1056
1057        info!("Initializing vault operator delegation",);
1058
1059        self.process_transaction(&[ix], &signer.pubkey(), &[signer])
1060            .await?;
1061
1062        if !self.print_tx {
1063            let account = self
1064                .get_account::<jito_vault_client::accounts::VaultOperatorDelegation>(
1065                    &vault_operator_delegation,
1066                )
1067                .await?;
1068            info!("{}", account.pretty_display());
1069        }
1070
1071        Ok(())
1072    }
1073
1074    #[allow(clippy::future_not_send)]
1075    pub async fn delegate_to_operator(
1076        &self,
1077        vault: String,
1078        operator: String,
1079        amount: u64,
1080    ) -> Result<()> {
1081        let signer = self
1082            .cli_config
1083            .signer
1084            .as_ref()
1085            .ok_or_else(|| anyhow!("Keypair not provided"))?;
1086
1087        let vault = Pubkey::from_str(&vault)?;
1088        let operator = Pubkey::from_str(&operator)?;
1089
1090        let vault_operator_delegation = VaultOperatorDelegation::find_program_address(
1091            &self.vault_program_id,
1092            &vault,
1093            &operator,
1094        )
1095        .0;
1096
1097        let mut ix_builder = AddDelegationBuilder::new();
1098        ix_builder
1099            .config(Config::find_program_address(&self.vault_program_id).0)
1100            .vault(vault)
1101            .operator(operator)
1102            .vault_operator_delegation(vault_operator_delegation)
1103            .admin(signer.pubkey())
1104            .amount(amount);
1105        let mut ix = ix_builder.instruction();
1106        ix.program_id = self.vault_program_id;
1107
1108        info!("Delegating to operator");
1109
1110        self.process_transaction(&[ix], &signer.pubkey(), &[signer])
1111            .await?;
1112
1113        if !self.print_tx {
1114            let account = self
1115                .get_account::<jito_vault_client::accounts::VaultOperatorDelegation>(
1116                    &vault_operator_delegation,
1117                )
1118                .await?;
1119            info!("{}", account.pretty_display());
1120            info!("Delegated {} tokens to {}", amount, operator);
1121        }
1122
1123        Ok(())
1124    }
1125
1126    #[allow(clippy::future_not_send)]
1127    pub async fn cooldown_operator_delegation(
1128        &self,
1129        vault: String,
1130        operator: String,
1131        amount: u64,
1132    ) -> Result<()> {
1133        let signer = self
1134            .cli_config
1135            .signer
1136            .as_ref()
1137            .ok_or_else(|| anyhow!("Keypair not provided"))?;
1138
1139        let vault = Pubkey::from_str(&vault)?;
1140        let operator = Pubkey::from_str(&operator)?;
1141
1142        let vault_operator_delegation = VaultOperatorDelegation::find_program_address(
1143            &self.vault_program_id,
1144            &vault,
1145            &operator,
1146        )
1147        .0;
1148
1149        let mut ix_builder = CooldownDelegationBuilder::new();
1150        ix_builder
1151            .config(Config::find_program_address(&self.vault_program_id).0)
1152            .vault(vault)
1153            .operator(operator)
1154            .vault_operator_delegation(vault_operator_delegation)
1155            .admin(signer.pubkey())
1156            .amount(amount);
1157        let mut ix = ix_builder.instruction();
1158        ix.program_id = self.vault_program_id;
1159
1160        info!("Cooling down delegation");
1161
1162        self.process_transaction(&[ix], &signer.pubkey(), &[signer])
1163            .await?;
1164
1165        if !self.print_tx {
1166            let account = self
1167                .get_account::<jito_vault_client::accounts::VaultOperatorDelegation>(
1168                    &vault_operator_delegation,
1169                )
1170                .await?;
1171            info!("{}", account.pretty_display());
1172            info!("Cooldown {} tokens for {}", amount, operator);
1173        }
1174
1175        Ok(())
1176    }
1177
1178    #[allow(clippy::future_not_send)]
1179    pub async fn delegate_token_account(
1180        &self,
1181        vault: String,
1182        delegate: String,
1183        token_mint: String,
1184        token_account: String,
1185    ) -> Result<()> {
1186        let signer = self
1187            .cli_config
1188            .signer
1189            .as_ref()
1190            .ok_or_else(|| anyhow!("Keypair not provided"))?;
1191        let rpc_client = self.get_rpc_client();
1192
1193        let vault = Pubkey::from_str(&vault)?;
1194        let delegate = Pubkey::from_str(&delegate)?;
1195        let token_mint = Pubkey::from_str(&token_mint)?;
1196        let token_account = Pubkey::from_str(&token_account)?;
1197
1198        let mut ix_builder = DelegateTokenAccountBuilder::new();
1199        ix_builder
1200            .config(Config::find_program_address(&self.vault_program_id).0)
1201            .vault(vault)
1202            .delegate_asset_admin(signer.pubkey())
1203            .token_mint(token_mint)
1204            .token_account(token_account)
1205            .delegate(delegate)
1206            .token_program(spl_token::ID);
1207
1208        let blockhash = rpc_client.get_latest_blockhash().await?;
1209        let tx = Transaction::new_signed_with_payer(
1210            &[ix_builder.instruction()],
1211            Some(&signer.pubkey()),
1212            &[signer],
1213            blockhash,
1214        );
1215        info!("Delegating token account: {:?}", tx.get_signature());
1216        let result = rpc_client.send_and_confirm_transaction(&tx).await;
1217
1218        if result.is_err() {
1219            return Err(anyhow::anyhow!("Transaction failed: {:?}", result.err()));
1220        }
1221
1222        info!("Transaction confirmed: {:?}", tx.get_signature());
1223        info!("Delegated token account: {:?}", token_account);
1224
1225        Ok(())
1226    }
1227
1228    #[allow(clippy::future_not_send)]
1229    pub async fn delegated_token_transfer(
1230        &self,
1231        token_account: String,
1232        recipient_pubkey: String,
1233        amount: u64,
1234    ) -> Result<()> {
1235        let keypair = self
1236            .cli_config
1237            .signer
1238            .as_ref()
1239            .ok_or_else(|| anyhow!("Keypair not provided"))?;
1240        let rpc_client = self.get_rpc_client();
1241
1242        let token_account = Pubkey::from_str(&token_account)?;
1243        let recipient_pubkey = Pubkey::from_str(&recipient_pubkey)?;
1244
1245        let transfer_ix = transfer(
1246            &spl_token::id(),
1247            &token_account,
1248            &recipient_pubkey,
1249            &keypair.pubkey(),
1250            &[],
1251            amount,
1252        )?;
1253
1254        let blockhash = rpc_client.get_latest_blockhash().await?;
1255        let tx = Transaction::new_signed_with_payer(
1256            &[transfer_ix],
1257            Some(&keypair.pubkey()),
1258            &[keypair],
1259            blockhash,
1260        );
1261
1262        info!("Delegating token transfer: {:?}", tx.get_signature());
1263        let result = rpc_client.send_and_confirm_transaction(&tx).await;
1264
1265        if result.is_err() {
1266            return Err(anyhow::anyhow!("Transaction failed: {:?}", result.err()));
1267        }
1268
1269        info!("Transaction confirmed: {:?}", tx.get_signature());
1270        info!("Transferred {} tokens to {}", amount, recipient_pubkey);
1271
1272        Ok(())
1273    }
1274
1275    #[allow(clippy::future_not_send)]
1276    pub async fn enqueue_withdrawal(&self, vault: String, amount: u64) -> Result<()> {
1277        let signer = self
1278            .cli_config
1279            .signer
1280            .as_ref()
1281            .ok_or_else(|| anyhow!("Keypair not provided"))?;
1282        let rpc_client = self.get_rpc_client();
1283
1284        let vault = Pubkey::from_str(&vault)?;
1285        let vault_account_raw = rpc_client.get_account(&vault).await?;
1286        let vault_account = Vault::try_from_slice_unchecked(&vault_account_raw.data)?;
1287
1288        let vault_staker_withdrawal_ticket = VaultStakerWithdrawalTicket::find_program_address(
1289            &self.vault_program_id,
1290            &vault,
1291            &signer.pubkey(),
1292        )
1293        .0;
1294
1295        let vault_staker_withdrawal_ticket_token_account =
1296            get_associated_token_address(&vault_staker_withdrawal_ticket, &vault_account.vrt_mint);
1297
1298        let staker_vrt_token_account =
1299            get_associated_token_address(&signer.pubkey(), &vault_account.vrt_mint);
1300
1301        let vault_staker_withdrawal_ticket_ata_ix = create_associated_token_account_idempotent(
1302            &signer.pubkey(),
1303            &vault_staker_withdrawal_ticket,
1304            &vault_account.vrt_mint,
1305            &spl_token::ID,
1306        );
1307
1308        let mut ix_builder = EnqueueWithdrawalBuilder::new();
1309        ix_builder
1310            .config(Config::find_program_address(&self.vault_program_id).0)
1311            .vault(vault)
1312            .vault_staker_withdrawal_ticket(vault_staker_withdrawal_ticket)
1313            .vault_staker_withdrawal_ticket_token_account(
1314                vault_staker_withdrawal_ticket_token_account,
1315            )
1316            .staker(signer.pubkey())
1317            .staker_vrt_token_account(staker_vrt_token_account)
1318            .base(signer.pubkey())
1319            .amount(amount);
1320
1321        let blockhash = rpc_client.get_latest_blockhash().await?;
1322        let tx = Transaction::new_signed_with_payer(
1323            &[
1324                vault_staker_withdrawal_ticket_ata_ix,
1325                ix_builder.instruction(),
1326            ],
1327            Some(&signer.pubkey()),
1328            &[signer],
1329            blockhash,
1330        );
1331        info!(
1332            "Initializing vault operator delegation transaction: {:?}",
1333            tx.get_signature()
1334        );
1335        let result = rpc_client.send_and_confirm_transaction(&tx).await;
1336
1337        if result.is_err() {
1338            return Err(anyhow::anyhow!("Transaction failed: {:?}", result.err()));
1339        }
1340
1341        info!("Transaction confirmed: {:?}", tx.get_signature());
1342
1343        Ok(())
1344    }
1345
1346    /// Changes the owner of a withdrawal ticket
1347    ///
1348    /// Transfers ownership of a vault staker withdrawal ticket from one account to another.
1349    /// This operation requires the signature of both the current ticket owner and the
1350    /// signer configured in the client.
1351    #[allow(clippy::future_not_send)]
1352    pub async fn change_withdrawal_ticket_owner(
1353        &self,
1354        vault: &Pubkey,
1355        old_ticket_owner: &str,
1356        new_ticket_owner: &Pubkey,
1357    ) -> Result<()> {
1358        let signer = self.signer()?;
1359
1360        let vault_staker_withdrawal_ticket = VaultStakerWithdrawalTicket::find_program_address(
1361            &self.vault_program_id,
1362            vault,
1363            &signer.pubkey(),
1364        )
1365        .0;
1366
1367        let old_ticket_owner_keypair = read_keypair_file(old_ticket_owner)
1368            .map_err(|e| anyhow!("Failed to read old admin keypair: {}", e))?;
1369        let old_ticket_owner_signer = CliSigner::new(Some(old_ticket_owner_keypair), None);
1370
1371        let mut ix_builder = ChangeWithdrawalTicketOwnerBuilder::new();
1372        ix_builder
1373            .config(Config::find_program_address(&self.vault_program_id).0)
1374            .vault(*vault)
1375            .vault_staker_withdrawal_ticket(vault_staker_withdrawal_ticket)
1376            .old_owner(old_ticket_owner_signer.pubkey())
1377            .new_owner(*new_ticket_owner);
1378        let mut ix = ix_builder.instruction();
1379        ix.program_id = self.vault_program_id;
1380
1381        info!("Changing Withdrawal Ticket Owner",);
1382
1383        self.process_transaction(&[ix], &signer.pubkey(), &[signer, &old_ticket_owner_signer])
1384            .await?;
1385
1386        if !self.print_tx {
1387            let account = self
1388                .get_account::<jito_vault_client::accounts::VaultStakerWithdrawalTicket>(
1389                    &vault_staker_withdrawal_ticket,
1390                )
1391                .await?;
1392            info!("{}", account.pretty_display());
1393            info!(
1394                "Change withdrawal ticket owner from {} to {}",
1395                old_ticket_owner_signer.pubkey(),
1396                new_ticket_owner
1397            );
1398        }
1399
1400        Ok(())
1401    }
1402
1403    #[allow(clippy::future_not_send)]
1404    pub async fn burn_withdrawal_ticket(&self, vault: String) -> Result<()> {
1405        let signer = self
1406            .cli_config
1407            .signer
1408            .as_ref()
1409            .ok_or_else(|| anyhow!("Keypair not provided"))?;
1410        let rpc_client = self.get_rpc_client();
1411
1412        let vault = Pubkey::from_str(&vault)?;
1413        let vault_account_raw = rpc_client.get_account(&vault).await?;
1414        let vault_account = Vault::try_from_slice_unchecked(&vault_account_raw.data)?;
1415
1416        let vault_staker_withdrawal_ticket = VaultStakerWithdrawalTicket::find_program_address(
1417            &self.vault_program_id,
1418            &vault,
1419            &signer.pubkey(),
1420        )
1421        .0;
1422
1423        let staker = signer.pubkey();
1424        let staker_token_account =
1425            get_associated_token_address(&staker, &vault_account.supported_mint);
1426
1427        let vault_token_account =
1428            get_associated_token_address(&vault, &vault_account.supported_mint);
1429
1430        let vault_fee_token_account =
1431            get_associated_token_address(&vault_account.fee_wallet, &vault_account.vrt_mint);
1432
1433        let vault_staker_withdrawal_ticket_token_account =
1434            get_associated_token_address(&vault_staker_withdrawal_ticket, &vault_account.vrt_mint);
1435
1436        let config = Config::find_program_address(&self.vault_program_id).0;
1437        let config_account_raw = rpc_client.get_account(&config).await?;
1438        let config_account = Config::try_from_slice_unchecked(&config_account_raw.data)?;
1439
1440        let program_fee_ata = create_associated_token_account_idempotent(
1441            &signer.pubkey(),
1442            &config_account.program_fee_wallet,
1443            &vault_account.vrt_mint,
1444            &spl_token::ID,
1445        );
1446
1447        let program_fee_token_account = get_associated_token_address(
1448            &config_account.program_fee_wallet,
1449            &vault_account.vrt_mint,
1450        );
1451
1452        let mut ix_builder = BurnWithdrawalTicketBuilder::new();
1453        ix_builder
1454            .config(Config::find_program_address(&self.vault_program_id).0)
1455            .vrt_mint(vault_account.vrt_mint)
1456            .vault(vault)
1457            .vault_staker_withdrawal_ticket(vault_staker_withdrawal_ticket)
1458            .vault_staker_withdrawal_ticket_token_account(
1459                vault_staker_withdrawal_ticket_token_account,
1460            )
1461            .program_fee_token_account(program_fee_token_account)
1462            .staker_token_account(staker_token_account)
1463            .vault_fee_token_account(vault_fee_token_account)
1464            .vault_token_account(vault_token_account)
1465            .staker(staker);
1466
1467        let blockhash = rpc_client.get_latest_blockhash().await?;
1468        let tx = Transaction::new_signed_with_payer(
1469            &[program_fee_ata, ix_builder.instruction()],
1470            Some(&signer.pubkey()),
1471            &[signer],
1472            blockhash,
1473        );
1474        info!(
1475            "Initializing vault operator delegation transaction: {:?}",
1476            tx.get_signature()
1477        );
1478        let result = rpc_client.send_and_confirm_transaction(&tx).await;
1479
1480        if result.is_err() {
1481            return Err(anyhow::anyhow!("Transaction failed: {:?}", result.err()));
1482        }
1483
1484        info!("Transaction confirmed: {:?}", tx.get_signature());
1485
1486        Ok(())
1487    }
1488
1489    // ------- GET ACCOUNTS --------------------
1490    #[allow(clippy::future_not_send)]
1491    pub async fn get_vault(&self, pubkey: String) -> Result<()> {
1492        let pubkey = Pubkey::from_str(&pubkey)?;
1493        let rpc_client = self.get_rpc_client();
1494
1495        let vault_account = rpc_client.get_account(&pubkey).await?;
1496        let vault =
1497            jito_vault_client::accounts::Vault::deserialize(&mut vault_account.data.as_slice())?;
1498
1499        let metadata_pubkey = Pubkey::find_program_address(
1500            &[
1501                b"metadata",
1502                inline_mpl_token_metadata::id().as_ref(),
1503                vault.vrt_mint.as_ref(),
1504            ],
1505            &inline_mpl_token_metadata::id(),
1506        )
1507        .0;
1508
1509        self.print_out(None, Some(&pubkey), &vault)?;
1510
1511        if let Ok(metadata) = self
1512            .get_account::<jito_vault_client::log::metadata::Metadata>(&metadata_pubkey)
1513            .await
1514        {
1515            self.print_out(None, None, &metadata)?;
1516        }
1517
1518        Ok(())
1519    }
1520
1521    #[allow(clippy::future_not_send)]
1522    pub async fn list_vaults(&self) -> Result<()> {
1523        let rpc_client = self.get_rpc_client();
1524        let config = self.get_rpc_program_accounts_config::<Vault>(None)?;
1525        let accounts = rpc_client
1526            .get_program_accounts_with_config(&self.vault_program_id, config)
1527            .await
1528            .unwrap();
1529        log::info!("{:?}", accounts);
1530        for (index, (vault_pubkey, vault)) in accounts.iter().enumerate() {
1531            let vault =
1532                jito_vault_client::accounts::Vault::deserialize(&mut vault.data.as_slice())?;
1533
1534            let metadata_pubkey = Pubkey::find_program_address(
1535                &[
1536                    b"metadata",
1537                    inline_mpl_token_metadata::id().as_ref(),
1538                    vault.vrt_mint.as_ref(),
1539                ],
1540                &inline_mpl_token_metadata::id(),
1541            )
1542            .0;
1543
1544            self.print_out(Some(index), Some(vault_pubkey), &vault)?;
1545
1546            if let Ok(metadata) = self
1547                .get_account::<jito_vault_client::log::metadata::Metadata>(&metadata_pubkey)
1548                .await
1549            {
1550                self.print_out(None, None, &metadata)?;
1551            }
1552        }
1553        Ok(())
1554    }
1555
1556    #[allow(clippy::future_not_send)]
1557    async fn get_config(&self) -> Result<()> {
1558        let rpc_client = self.get_rpc_client();
1559
1560        let config_address = Config::find_program_address(&self.vault_program_id).0;
1561        debug!(
1562            "Reading the restaking configuration account at address: {}",
1563            config_address
1564        );
1565
1566        let account = rpc_client.get_account(&config_address).await?;
1567        let config =
1568            jito_vault_client::accounts::Config::deserialize(&mut account.data.as_slice())?;
1569        self.print_out(None, Some(&config_address), &config)?;
1570        Ok(())
1571    }
1572
1573    #[allow(clippy::future_not_send)]
1574    pub async fn get_vault_update_state_tracker(&self, vault: String) -> Result<()> {
1575        let vault = Pubkey::from_str(&vault)?;
1576        let rpc_client = self.get_rpc_client();
1577
1578        let config_address = Config::find_program_address(&self.vault_program_id).0;
1579        let config = self
1580            .get_account::<jito_vault_client::accounts::Config>(&config_address)
1581            .await?;
1582
1583        let slot = rpc_client.get_slot().await?;
1584        let ncn_epoch = get_epoch(slot, config.epoch_length)?;
1585
1586        let vault_update_state_tracker = VaultUpdateStateTracker::find_program_address(
1587            &self.vault_program_id,
1588            &vault,
1589            ncn_epoch,
1590        )
1591        .0;
1592        let account = rpc_client.get_account(&vault_update_state_tracker).await?;
1593        let state_tracker = jito_vault_client::accounts::VaultUpdateStateTracker::deserialize(
1594            &mut account.data.as_slice(),
1595        )?;
1596        self.print_out(None, Some(&vault_update_state_tracker), &state_tracker)?;
1597        Ok(())
1598    }
1599
1600    #[allow(clippy::future_not_send)]
1601    pub async fn get_vault_operator_delegations(
1602        &self,
1603        vault: String,
1604        operator: Option<String>,
1605    ) -> Result<()> {
1606        let rpc_client = self.get_rpc_client();
1607        let vault = Pubkey::from_str(&vault)?;
1608        let operator_pubkey = match operator {
1609            Some(operator) => Some(Pubkey::from_str(&operator)?),
1610            None => None,
1611        };
1612
1613        match operator_pubkey {
1614            Some(operator) => {
1615                let vault_operator_delegation = VaultOperatorDelegation::find_program_address(
1616                    &self.vault_program_id,
1617                    &vault,
1618                    &operator,
1619                )
1620                .0;
1621                let account = rpc_client.get_account(&vault_operator_delegation).await?;
1622
1623                let delegation = jito_vault_client::accounts::VaultOperatorDelegation::deserialize(
1624                    &mut account.data.as_slice(),
1625                )?;
1626
1627                self.print_out(None, Some(&vault_operator_delegation), &delegation)?;
1628            }
1629            None => {
1630                let config = self.get_rpc_program_accounts_config::<VaultOperatorDelegation>(
1631                    Some((&vault, 8)),
1632                )?;
1633                let accounts = rpc_client
1634                    .get_program_accounts_with_config(&self.vault_program_id, config)
1635                    .await?;
1636
1637                for (index, (pubkey, account)) in accounts.iter().enumerate() {
1638                    let vault_operator_delegation =
1639                        jito_vault_client::accounts::VaultOperatorDelegation::deserialize(
1640                            &mut account.data.as_slice(),
1641                        )?;
1642
1643                    self.print_out(Some(index), Some(pubkey), &vault_operator_delegation)?;
1644                }
1645            }
1646        }
1647
1648        Ok(())
1649    }
1650
1651    #[allow(clippy::future_not_send)]
1652    pub async fn get_withdrawal_ticket(&self, vault: String, staker: Option<String>) -> Result<()> {
1653        let rpc_client = self.get_rpc_client();
1654        let vault = Pubkey::from_str(&vault)?;
1655        let staker = if let Some(staker) = staker {
1656            Pubkey::from_str(&staker)?
1657        } else {
1658            let signer = self
1659                .cli_config
1660                .signer
1661                .as_ref()
1662                .ok_or_else(|| anyhow!("Keypair not provided"))?;
1663            signer.pubkey()
1664        };
1665        let vault_staker_withdrawal_ticket = VaultStakerWithdrawalTicket::find_program_address(
1666            &self.vault_program_id,
1667            &vault,
1668            &staker,
1669        )
1670        .0;
1671        let account = rpc_client
1672            .get_account(&vault_staker_withdrawal_ticket)
1673            .await?;
1674        let ticket = jito_vault_client::accounts::VaultStakerWithdrawalTicket::deserialize(
1675            &mut account.data.as_slice(),
1676        )?;
1677        self.print_out(None, Some(&vault_staker_withdrawal_ticket), &ticket)?;
1678
1679        Ok(())
1680    }
1681
1682    /// Sets the primary admin for a Vault
1683    ///
1684    /// This function transfers administrative control of a Vault account from the current admin
1685    /// to a new admin. It supports both file-based keypairs and hardware wallets (USB devices)
1686    /// for both the old and new admin. The function builds and processes a transaction that
1687    /// updates the admin public key in the Vault account.
1688    #[allow(clippy::future_not_send)]
1689    async fn set_admin(
1690        &self,
1691        vault: &Pubkey,
1692        old_admin_keypair: &str,
1693        new_admin_keypair: &str,
1694    ) -> Result<()> {
1695        let mut old_admin_owned = None;
1696        let mut new_admin_owned = None;
1697
1698        let old_admin_signer = self.resolve_keypair(old_admin_keypair, &mut old_admin_owned)?;
1699        let new_admin_signer = self.resolve_keypair(new_admin_keypair, &mut new_admin_owned)?;
1700
1701        let mut ix_builder = SetAdminBuilder::new();
1702        ix_builder
1703            .config(Config::find_program_address(&self.vault_program_id).0)
1704            .vault(*vault)
1705            .old_admin(old_admin_signer.pubkey())
1706            .new_admin(new_admin_signer.pubkey());
1707        let mut ix = ix_builder.instruction();
1708        ix.program_id = self.vault_program_id;
1709
1710        info!("Setting Vault admin to {}", new_admin_signer.pubkey());
1711
1712        self.process_transaction(
1713            &[ix],
1714            &new_admin_signer.pubkey(),
1715            &[new_admin_signer, old_admin_signer],
1716        )
1717        .await?;
1718
1719        if !self.print_tx {
1720            let account = self
1721                .get_account::<jito_vault_client::accounts::Vault>(vault)
1722                .await?;
1723            info!("{}", account.pretty_display());
1724        }
1725
1726        Ok(())
1727    }
1728
1729    /// Set the capacity for Vault
1730    ///
1731    /// Updates the maximum deposit capacity for a specific vault.
1732    /// This operation can only be performed by the vault admin.
1733    #[allow(clippy::future_not_send)]
1734    pub async fn set_capacity(&self, vault: String, amount: u64) -> Result<()> {
1735        let signer = self
1736            .cli_config
1737            .signer
1738            .as_ref()
1739            .ok_or_else(|| anyhow!("Keypair not provided"))?;
1740        let vault_pubkey = Pubkey::from_str(&vault)?;
1741
1742        let mut builder = SetDepositCapacityBuilder::new();
1743        builder
1744            .config(Config::find_program_address(&self.vault_program_id).0)
1745            .vault(vault_pubkey)
1746            .admin(signer.pubkey())
1747            .amount(amount);
1748        let mut ix = builder.instruction();
1749        ix.program_id = self.vault_program_id;
1750
1751        info!("Vault capacity instruction: {:?}", builder);
1752
1753        self.process_transaction(&[ix], &signer.pubkey(), &[signer])
1754            .await?;
1755
1756        if !self.print_tx {
1757            let account = self
1758                .get_account::<jito_vault_client::accounts::Vault>(&vault_pubkey)
1759                .await?;
1760            info!("{}", account.pretty_display());
1761        }
1762
1763        Ok(())
1764    }
1765
1766    /// Sets the primary admin for Config
1767    ///
1768    /// Transfers administrative control of the Config to a new admin.
1769    /// This operation can only be performed by the current admin.
1770    #[allow(clippy::future_not_send)]
1771    async fn set_config_admin(&self, new_admin: Pubkey) -> Result<()> {
1772        let signer = self.signer()?;
1773
1774        let config_address = Config::find_program_address(&self.vault_program_id).0;
1775        let mut ix_builder = SetConfigAdminBuilder::new();
1776        ix_builder
1777            .config(config_address)
1778            .old_admin(signer.pubkey())
1779            .new_admin(new_admin);
1780        let mut ix = ix_builder.instruction();
1781        ix.program_id = self.vault_program_id;
1782
1783        info!("Setting vault config admin parameters: {:?}", ix_builder);
1784
1785        self.process_transaction(&[ix], &signer.pubkey(), &[signer])
1786            .await?;
1787
1788        if !self.print_tx {
1789            let account = self
1790                .get_account::<jito_vault_client::accounts::Config>(&config_address)
1791                .await?;
1792            info!("{}", account.pretty_display());
1793        }
1794
1795        Ok(())
1796    }
1797
1798    /// Set the fees for Vault
1799    ///
1800    /// Updates one or more fee parameters for a specific vault. Each fee type
1801    /// (deposit, withdrawal, reward) is specified in basis points and can be
1802    /// updated independently. Any fee type not provided (None) will remain unchanged.
1803    ///
1804    /// NOTE:
1805    /// - Fee changes are only allowed once per epoch
1806    #[allow(clippy::future_not_send)]
1807    async fn set_fees(
1808        &self,
1809        vault: &Pubkey,
1810        deposit_fee_bps: Option<u16>,
1811        withdrawal_fee_bps: Option<u16>,
1812        reward_fee_bps: Option<u16>,
1813    ) -> Result<()> {
1814        let signer = self.signer()?;
1815
1816        let config_address = Config::find_program_address(&self.vault_program_id).0;
1817        let mut ix_builder = SetFeesBuilder::new();
1818        ix_builder
1819            .config(config_address)
1820            .vault(*vault)
1821            .admin(signer.pubkey());
1822
1823        if let Some(deposit_fee_bps) = deposit_fee_bps {
1824            ix_builder.deposit_fee_bps(deposit_fee_bps);
1825        }
1826
1827        if let Some(withdrawal_fee_bps) = withdrawal_fee_bps {
1828            ix_builder.withdrawal_fee_bps(withdrawal_fee_bps);
1829        }
1830
1831        if let Some(reward_fee_bps) = reward_fee_bps {
1832            ix_builder.reward_fee_bps(reward_fee_bps);
1833        }
1834
1835        let mut ix = ix_builder.instruction();
1836        ix.program_id = self.vault_program_id;
1837
1838        info!("Setting Vault fees: {:?}", ix_builder);
1839
1840        self.process_transaction(&[ix], &signer.pubkey(), &[signer])
1841            .await?;
1842
1843        if !self.print_tx {
1844            let account = self
1845                .get_account::<jito_vault_client::accounts::Vault>(vault)
1846                .await?;
1847            info!("{}", account.pretty_display());
1848        }
1849
1850        Ok(())
1851    }
1852
1853    /// Sets the pause state for a specific vault
1854    ///
1855    /// Enables or disables operations on a vault by setting its pause state.
1856    /// When paused, most interactions with the vault will be rejected.
1857    /// This operation can only be performed by the vault admin.
1858    #[allow(clippy::future_not_send)]
1859    async fn set_is_paused(&self, vault: &Pubkey, set_pause: bool) -> Result<()> {
1860        let signer = self.signer()?;
1861
1862        let config_address = Config::find_program_address(&self.vault_program_id).0;
1863        let mut ix_builder = SetIsPausedBuilder::new();
1864        ix_builder
1865            .config(config_address)
1866            .vault(*vault)
1867            .admin(signer.pubkey())
1868            .is_paused(set_pause);
1869
1870        let mut ix = ix_builder.instruction();
1871        ix.program_id = self.vault_program_id;
1872
1873        info!("Setting Is Paused: {:?}", ix_builder);
1874
1875        self.process_transaction(&[ix], &signer.pubkey(), &[signer])
1876            .await?;
1877
1878        if !self.print_tx {
1879            let account = self
1880                .get_account::<jito_vault_client::accounts::Vault>(vault)
1881                .await?;
1882            info!("{}", account.pretty_display());
1883        }
1884
1885        Ok(())
1886    }
1887
1888    /// Sets a new program fee (in basis points) for the Config
1889    ///
1890    /// Updates the fee percentage (specified in basis points) that the program
1891    /// collects for vault operation. This operation can only be performed by the
1892    /// current admin of the config.
1893    #[allow(clippy::future_not_send)]
1894    async fn set_program_fee(&self, new_fee_bps: u16) -> Result<()> {
1895        let signer = self.signer()?;
1896
1897        let config_address = Config::find_program_address(&self.vault_program_id).0;
1898        let mut ix_builder = SetProgramFeeBuilder::new();
1899        ix_builder
1900            .config(config_address)
1901            .admin(signer.pubkey())
1902            .new_fee_bps(new_fee_bps);
1903        let mut ix = ix_builder.instruction();
1904        ix.program_id = self.vault_program_id;
1905
1906        info!(
1907            "Setting vault config program fee bps parameters: {:?}",
1908            ix_builder
1909        );
1910
1911        self.process_transaction(&[ix], &signer.pubkey(), &[signer])
1912            .await?;
1913
1914        if !self.print_tx {
1915            let account = self
1916                .get_account::<jito_vault_client::accounts::Config>(&config_address)
1917                .await?;
1918            info!("{}", account.pretty_display());
1919        }
1920
1921        Ok(())
1922    }
1923
1924    /// Sets a new program fee wallet for the Config
1925    ///
1926    /// Updates the wallet address that receives program fees collected by the Jito Vault Program.
1927    /// This operation can only be performed by the current program fee admin.
1928    #[allow(clippy::future_not_send)]
1929    async fn set_program_fee_wallet(&self, new_fee_wallet: &Pubkey) -> Result<()> {
1930        let signer = self.signer()?;
1931
1932        let config_address = Config::find_program_address(&self.vault_program_id).0;
1933        let mut ix_builder = SetProgramFeeWalletBuilder::new();
1934        ix_builder
1935            .config(config_address)
1936            .program_fee_admin(signer.pubkey())
1937            .new_fee_wallet(*new_fee_wallet);
1938        let mut ix = ix_builder.instruction();
1939        ix.program_id = self.vault_program_id;
1940
1941        info!(
1942            "Setting vault config program fee wallet parameters: {:?}",
1943            ix_builder
1944        );
1945
1946        self.process_transaction(&[ix], &signer.pubkey(), &[signer])
1947            .await?;
1948
1949        if !self.print_tx {
1950            let account = self
1951                .get_account::<jito_vault_client::accounts::Config>(&config_address)
1952                .await?;
1953            info!("{}", account.pretty_display());
1954        }
1955
1956        Ok(())
1957    }
1958
1959    /// Sets secondary admin roles for Vault
1960    ///
1961    /// This function allows assigning a new administrator to various administrative roles
1962    /// for a specific Vault. Multiple roles can be assigned in a single call by enabling the
1963    /// corresponding boolean flags.
1964    #[allow(clippy::too_many_arguments, clippy::future_not_send)]
1965    async fn set_secondary_admin(
1966        &self,
1967        vault: &Pubkey,
1968        new_admin: &Pubkey,
1969        set_delegation_admin: bool,
1970        set_operator_admin: bool,
1971        set_ncn_admin: bool,
1972        set_slasher_admin: bool,
1973        set_capacity_admin: bool,
1974        set_fee_wallet: bool,
1975        set_mint_burn_admin: bool,
1976        set_delegate_asset_admin: bool,
1977        set_fee_admin: bool,
1978        set_metadata_admin: bool,
1979    ) -> Result<()> {
1980        let signer = self.signer()?;
1981        let config_address = Config::find_program_address(&self.vault_program_id).0;
1982
1983        let mut roles: Vec<VaultAdminRole> = vec![];
1984        if set_delegation_admin {
1985            roles.push(VaultAdminRole::DelegationAdmin);
1986        }
1987        if set_operator_admin {
1988            roles.push(VaultAdminRole::OperatorAdmin);
1989        }
1990        if set_ncn_admin {
1991            roles.push(VaultAdminRole::NcnAdmin);
1992        }
1993        if set_slasher_admin {
1994            roles.push(VaultAdminRole::SlasherAdmin);
1995        }
1996        if set_capacity_admin {
1997            roles.push(VaultAdminRole::CapacityAdmin);
1998        }
1999        if set_fee_wallet {
2000            roles.push(VaultAdminRole::FeeWallet);
2001        }
2002        if set_mint_burn_admin {
2003            roles.push(VaultAdminRole::MintBurnAdmin);
2004        }
2005        if set_delegate_asset_admin {
2006            roles.push(VaultAdminRole::DelegateAssetAdmin);
2007        }
2008        if set_fee_admin {
2009            roles.push(VaultAdminRole::FeeAdmin);
2010        }
2011        if set_metadata_admin {
2012            roles.push(VaultAdminRole::MetadataAdmin);
2013        }
2014
2015        for role in roles.iter() {
2016            let mut ix_builder = SetSecondaryAdminBuilder::new();
2017            ix_builder
2018                .config(config_address)
2019                .new_admin(*new_admin)
2020                .vault(*vault)
2021                .admin(signer.pubkey())
2022                .vault_admin_role(*role)
2023                .instruction();
2024            let mut ix = ix_builder.instruction();
2025            ix.program_id = self.vault_program_id;
2026
2027            info!(
2028                "Setting {:?} Admin to {} for Vault {}",
2029                role, new_admin, vault
2030            );
2031
2032            self.process_transaction(&[ix], &signer.pubkey(), &[signer])
2033                .await?;
2034        }
2035
2036        if !self.print_tx {
2037            let account = self
2038                .get_account::<jito_vault_client::accounts::Vault>(vault)
2039                .await?;
2040            info!("{}", account.pretty_display());
2041        }
2042
2043        Ok(())
2044    }
2045
2046    /// Updates the vault balance
2047    ///
2048    /// Synchronizes the vault's internal token balance with its actual token holdings and
2049    /// calculates rewards. This function:
2050    /// 1. Verifies the vault is not paused and can be updated
2051    /// 2. Calculates rewards based on the difference between current and tracked token balance
2052    /// 3. Applies the reward fee according to the vault's configuration
2053    /// 4. Updates the vault's tracked token balance
2054    /// 5. Mints VRT tokens to the fee wallet as reward fees
2055    #[allow(clippy::future_not_send)]
2056    async fn update_vault_balance(&self, vault: &Pubkey) -> Result<()> {
2057        let signer = self.signer()?;
2058
2059        let config_address = Config::find_program_address(&self.vault_program_id).0;
2060
2061        let vault_account_raw = self.get_rpc_client().get_account(vault).await?;
2062        let vault_account = Vault::try_from_slice_unchecked(&vault_account_raw.data)?;
2063
2064        let vault_token_account =
2065            get_associated_token_address(vault, &vault_account.supported_mint);
2066
2067        let vault_fee_token_account =
2068            get_associated_token_address(&vault_account.fee_wallet, &vault_account.vrt_mint);
2069
2070        let mut ix_builder = UpdateVaultBalanceBuilder::new();
2071        ix_builder
2072            .config(config_address)
2073            .vault(*vault)
2074            .vault_token_account(vault_token_account)
2075            .vrt_mint(vault_account.vrt_mint)
2076            .vault_fee_token_account(vault_fee_token_account);
2077        let mut ix = ix_builder.instruction();
2078        ix.program_id = self.vault_program_id;
2079
2080        info!("Update Vault balance: {:?}", ix_builder);
2081
2082        self.process_transaction(&[ix], &signer.pubkey(), &[signer])
2083            .await?;
2084
2085        if !self.print_tx {
2086            let account = self
2087                .get_account::<jito_vault_client::accounts::Vault>(vault)
2088                .await?;
2089            info!("{}", account.pretty_display());
2090        }
2091
2092        Ok(())
2093    }
2094}