diff --git a/utils/steward-cli/src/commands/command_args.rs b/utils/steward-cli/src/commands/command_args.rs index 2fe0395f..8700ad6a 100644 --- a/utils/steward-cli/src/commands/command_args.rs +++ b/utils/steward-cli/src/commands/command_args.rs @@ -14,6 +14,7 @@ use crate::commands::{ update_directed_stake_ticket::UpdateDirectedStakeTicket, }, cranks::{ + auto_remove_validator::CrankAutoRemoveValidator, compute_directed_stake_meta::ComputeDirectedStakeMeta, rebalance_directed::CrankRebalanceDirected, }, @@ -363,6 +364,7 @@ pub enum Commands { CrankRebalance(CrankRebalance), CrankRebalanceDirected(CrankRebalanceDirected), CrankUpdateStakePool(CrankUpdateStakePool), + CrankAutoRemoveValidator(CrankAutoRemoveValidator), } // ---------- VIEWS ------------ diff --git a/utils/steward-cli/src/commands/cranks/auto_remove_validator.rs b/utils/steward-cli/src/commands/cranks/auto_remove_validator.rs new file mode 100644 index 00000000..771c1a9c --- /dev/null +++ b/utils/steward-cli/src/commands/cranks/auto_remove_validator.rs @@ -0,0 +1,152 @@ +use std::sync::Arc; + +use anchor_lang::{InstructionData, ToAccountMetas}; +use anyhow::{anyhow, Result}; +use clap::Parser; +use solana_client::nonblocking::rpc_client::RpcClient; +use solana_program::instruction::Instruction; +#[allow(deprecated)] +use solana_sdk::{pubkey::Pubkey, signature::read_keypair_file, stake, system_program}; +use stakenet_sdk::utils::{ + accounts::{ + get_all_steward_accounts, get_all_validator_accounts, get_stake_address, + get_transient_stake_address, + }, + helpers::check_stake_accounts, +}; +use validator_history::constants::MIN_VOTE_EPOCHS; + +use crate::{ + commands::command_args::PermissionlessParameters, + utils::{ + accounts::get_validator_history_address, + transactions::{package_instructions, submit_packaged_transactions}, + }, +}; + +#[derive(Parser)] +#[command(about = "Crank `auto_remove_validator` state")] +pub struct CrankAutoRemoveValidator { + #[command(flatten)] + pub permissionless_parameters: PermissionlessParameters, +} + +pub async fn command_crank_auto_remove_validator( + args: CrankAutoRemoveValidator, + client: &Arc, + program_id: Pubkey, +) -> Result<()> { + let payer = Arc::new( + read_keypair_file(args.permissionless_parameters.payer_keypair_path) + .map_err(|e| anyhow!("Failed reading keypair file ( Payer ): {e}"))?, + ); + let epoch = client.get_epoch_info().await?.epoch; + let all_steward_accounts = get_all_steward_accounts( + client, + &program_id, + &args.permissionless_parameters.steward_config, + ) + .await?; + let vote_accounts = client.get_vote_accounts().await?; + let all_vote_accounts = vote_accounts + .current + .into_iter() + .chain(vote_accounts.delinquent.into_iter()) + .filter(|vote_account| vote_account.epoch_credits.len() >= MIN_VOTE_EPOCHS) + .collect::>(); + let all_validator_accounts = + get_all_validator_accounts(client, &all_vote_accounts, &validator_history::id()).await?; + let checks = check_stake_accounts(&all_validator_accounts, epoch); + + println!( + "Validators to remove count: {}", + all_steward_accounts + .state_account + .state + .validators_to_remove + .count() + ); + + let bad_vote_accounts = checks + .iter() + .filter_map(|(vote_account, check)| { + if !check.has_history || check.is_deactivated || !check.has_vote_account { + Some(*vote_account) + } else { + None + } + }) + .collect::>(); + + let ixs_to_run = bad_vote_accounts + .iter() + .filter_map(|vote_account| { + let validator_index = all_steward_accounts + .validator_list_account + .validators + .iter() + .position(|v| v.vote_account_address == *vote_account)?; + + let history_account = + get_validator_history_address(vote_account, &validator_history::id()); + + let stake_address = + get_stake_address(vote_account, &all_steward_accounts.stake_pool_address); + + let transient_stake_address = get_transient_stake_address( + vote_account, + &all_steward_accounts.stake_pool_address, + &all_steward_accounts.validator_list_account, + validator_index, + )?; + + if let Ok(false) = all_steward_accounts + .state_account + .state + .validators_to_remove + .get(validator_index) + { + return None; + } + + Some(Instruction { + program_id, + accounts: jito_steward::accounts::AutoRemoveValidator { + config: all_steward_accounts.config_address, + state_account: all_steward_accounts.state_address, + stake_pool_program: spl_stake_pool::id(), + stake_pool: all_steward_accounts.stake_pool_address, + validator_history_account: history_account, + withdraw_authority: all_steward_accounts.stake_pool_withdraw_authority, + validator_list: all_steward_accounts.validator_list_address, + reserve_stake: all_steward_accounts.stake_pool_account.reserve_stake, + stake_account: stake_address, + transient_stake_account: transient_stake_address, + vote_account: *vote_account, + system_program: system_program::id(), + stake_program: stake::program::id(), + rent: solana_sdk::sysvar::rent::id(), + clock: solana_sdk::sysvar::clock::id(), + stake_history: solana_sdk::sysvar::stake_history::id(), + stake_config: stake::config::ID, + } + .to_account_metas(None), + data: jito_steward::instruction::AutoRemoveValidatorFromPool { + validator_list_index: validator_index as u64, + } + .data(), + }) + }) + .collect::>(); + + let txs_to_run = package_instructions(&ixs_to_run, 1, None, Some(1_400_000), None); + + log::info!("Submitting {} instructions", ixs_to_run.len()); + log::info!("Submitting {} transactions", txs_to_run.len()); + + let stats = submit_packaged_transactions(client, txs_to_run, &payer, Some(50), None).await?; + + println!("Stats: {stats:?}"); + + Ok(()) +} diff --git a/utils/steward-cli/src/commands/cranks/mod.rs b/utils/steward-cli/src/commands/cranks/mod.rs index a4a121cc..e6eae56d 100644 --- a/utils/steward-cli/src/commands/cranks/mod.rs +++ b/utils/steward-cli/src/commands/cranks/mod.rs @@ -1,3 +1,4 @@ +pub mod auto_remove_validator; pub mod compute_delegations; pub mod compute_directed_stake_meta; pub mod compute_instant_unstake; diff --git a/utils/steward-cli/src/main.rs b/utils/steward-cli/src/main.rs index bd316565..3037cb78 100644 --- a/utils/steward-cli/src/main.rs +++ b/utils/steward-cli/src/main.rs @@ -56,6 +56,7 @@ use crate::{ update_directed_stake_ticket::command_update_directed_stake_ticket, }, cranks::{ + auto_remove_validator::command_crank_auto_remove_validator, compute_directed_stake_meta::command_crank_compute_directed_stake_meta, rebalance_directed::command_crank_rebalance_directed, }, @@ -270,6 +271,9 @@ async fn main() -> Result<()> { Commands::CrankUpdateStakePool(args) => { command_crank_update_stake_pool(args, &client, steward_program_id).await } + Commands::CrankAutoRemoveValidator(args) => { + command_crank_auto_remove_validator(args, &client, steward_program_id).await + } }; if let Err(e) = result {