diff --git a/cli/src/cli.rs b/cli/src/cli.rs index 99a0de0a719c69..31e9612fc10f3e 100644 --- a/cli/src/cli.rs +++ b/cli/src/cli.rs @@ -2254,7 +2254,7 @@ mod tests { memo: None, split_stake_account: 1, seed: None, - lamports: 30, + lamports: 200_000_000, fee_payer: 0, compute_unit_price: None, rent_exempt_reserve: None, diff --git a/cli/src/stake.rs b/cli/src/stake.rs index ac08fd3425dc65..5d9cbd540e2c29 100644 --- a/cli/src/stake.rs +++ b/cli/src/stake.rs @@ -38,13 +38,14 @@ use { }, solana_rpc_client_nonce_utils::blockhash_query::BlockhashQuery, solana_sdk::{ - account::from_account, + account::{from_account, Account}, account_utils::StateMut, clock::{Clock, UnixTimestamp, SECONDS_PER_DAY}, commitment_config::CommitmentConfig, epoch_schedule::EpochSchedule, feature_set, message::Message, + native_token::Sol, pubkey::Pubkey, stake::{ self, @@ -1980,40 +1981,49 @@ pub fn process_split_stake( }; let rent_exempt_reserve = if !sign_only { - if let Ok(stake_account) = rpc_client.get_account(&split_stake_account_address) { - if stake_account.owner == stake::program::id() { - return Err(CliError::BadParameter(format!( + let stake_minimum_delegation = rpc_client.get_stake_minimum_delegation()?; + if lamports < stake_minimum_delegation { + let lamports = Sol(lamports); + let stake_minimum_delegation = Sol(stake_minimum_delegation); + return Err(CliError::BadParameter(format!( + "need at least {stake_minimum_delegation} for minimum stake delegation, \ + provided: {lamports}" + )) + .into()); + } + + let check_stake_account = |account: Account| -> Result { + match account.owner { + owner if owner == stake::program::id() => Err(CliError::BadParameter(format!( "Stake account {split_stake_account_address} already exists" - )) - .into()); - } else if stake_account.owner == system_program::id() { - if !stake_account.data.is_empty() { - return Err(CliError::BadParameter(format!( - "Account {split_stake_account_address} has data and cannot be used to split stake" - )) - .into()); + ))), + owner if owner == system_program::id() => { + if !account.data.is_empty() { + Err(CliError::BadParameter(format!( + "Account {split_stake_account_address} has data and cannot be used to split stake" + ))) + } else { + // if `stake_account`'s owner is the system_program and its data is + // empty, `stake_account` is allowed to receive the stake split + Ok(account.lamports) + } } - // if `stake_account`'s owner is the system_program and its data is - // empty, `stake_account` is allowed to receive the stake split - } else { - return Err(CliError::BadParameter(format!( + _ => Err(CliError::BadParameter(format!( "Account {split_stake_account_address} already exists and cannot be used to split stake" - )) - .into()); + ))) } - } + }; + let current_balance = + if let Ok(stake_account) = rpc_client.get_account(&split_stake_account_address) { + check_stake_account(stake_account)? + } else { + 0 + }; - let minimum_balance = + let rent_exempt_reserve = rpc_client.get_minimum_balance_for_rent_exemption(StakeStateV2::size_of())?; - if lamports < minimum_balance { - return Err(CliError::BadParameter(format!( - "need at least {minimum_balance} lamports for stake account to be rent exempt, \ - provided lamports: {lamports}" - )) - .into()); - } - minimum_balance + rent_exempt_reserve.saturating_sub(current_balance) } else { rent_exempt_reserve .cloned() @@ -2022,11 +2032,14 @@ pub fn process_split_stake( let recent_blockhash = blockhash_query.get_blockhash(rpc_client, config.commitment)?; - let mut ixs = vec![system_instruction::transfer( - &fee_payer.pubkey(), - &split_stake_account_address, - rent_exempt_reserve, - )]; + let mut ixs = vec![]; + if rent_exempt_reserve > 0 { + ixs.push(system_instruction::transfer( + &fee_payer.pubkey(), + &split_stake_account_address, + rent_exempt_reserve, + )); + } if let Some(seed) = split_stake_account_seed { ixs.append( &mut stake_instruction::split_with_seed(