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 cli_config: CliConfig,
55
56 restaking_program_id: Pubkey,
58
59 vault_program_id: Pubkey,
61
62 print_tx: bool,
64
65 print_json: bool,
67
68 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 #[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); 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 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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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}