Skip to content

cli: various improvements#639

Open
2501babe wants to merge 5 commits intosolana-program:mainfrom
2501babe:20260421_display
Open

cli: various improvements#639
2501babe wants to merge 5 commits intosolana-program:mainfrom
2501babe:20260421_display

Conversation

@2501babe
Copy link
Copy Markdown
Member

@2501babe 2501babe commented Apr 22, 2026

  • display locked stake and phantom tokens in line with program: unhide minimum balance and back with notional tokens #582
  • display total pool asset value and total excess lamports in line with program: move to lamport-based accounting #601
  • show total network value for DisplayAll in sol rather than lamports
  • display warnings for a dedelegated pool, nonexistent onramp, and potentially valuable replenish
  • fix a bug where all --dry-run commands claimed to succeed
  • remove spl_token_client including ProgramClient
  • replace all uses of spl_token, vote_program, and stake_program with interface crates

most of the meaningful work is in command_display(), get_stake_summaries(), and output.rs changing how Display and DisplayAll work. the rest of the diff is just consequences of changing program_client to rpc_client. no functionality (except the simulate bug lol) for any other command changes except a couple errors are slightly more correct

normal example:

SPL Single-Validator Stake Pool
  Pool address: H1F5CVVCqXVPHJrU45Ewbu227oBnJFfcm3mtpCYhpn6C
  Vote account address: mrgn6ETrBDM8mjjYN8rbVwFqVwF8z6rtmvGLbdGuVUU
  Net asset value: 1515545211
  Notional token supply: 1227210480
/!\ POOL STAKE IS UNDELEGATED /!\ This validator's vote account may be delinquent or closed!

verbose example:

SPL Single-Validator Stake Pool
  Pool address: 76bZpzHEad4UUmS1qX57oeb8eeGk3sku8t8WT3vPJ5To
  Vote account address: gangtRyGPTvYWb8K3xS2feJQaCks4iJ7rytFUPtVqSY
  Pool main stake account address: FFGfgLctyaDMXMiGywivnjZQPKqhTC3PnL9iaoA3RANU
  Pool onramp stake account address: 5BsduezBoR8PcWBb8tRYgkKhEhpYozLb7L9Ubk4NmkTE
  Pool mint address: 6ZS7ZVDw91BVAC8gsz3SZBSeVeF2GtXtL2BHK31Kvyjm
  Pool stake authority address: 88cp4RN952zXMbfKdAM7oqGoBeDPVHDtnTtadw6ZfyEn
  Pool mint authority address: 2eTwHCU6V1jYdfieTKDzRgeoM2ktb6g6qviZmqXkfNVn
  Pool MPL authority address: b4iv4TZjpi3FXLNsFQsdYXkkurLcxV9Eztefvk7GBpo
  Net asset value: 1089625308
  Undelegated lamports: 3393326
  Notional token supply: 1000000000
/!\ This pool has 0.003393326 SOL not earning rewards; use `spl-single-pool manage replenish-pool` to delegate it

closes #118
closes #543

@2501babe 2501babe self-assigned this Apr 22, 2026
Comment thread clients/cli/src/main.rs
Comment on lines -872 to +906
if config.verbose() {
if simulation_data.value.err.is_none() && !config.verbose() {
println_display(config, "Simulation succeeded".to_string());
} else {
let status_str = if simulation_data.value.err.is_some() {
"FAILED"
} else {
"succeeded"
};
Copy link
Copy Markdown
Member Author

@2501babe 2501babe Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

found this by accident when testing my fancy new display warnings. as it turns out, simulate_transaction() does not return Err when the simulation errors. so we were saying every --dry-run succeeded no matter what actually happened. oops!

Comment on lines -1 to -2
// XXX this file will be deleted and replaced with a stake program client once i
// write one
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i originally wrote svsp before i was doing core protocol work so writing a bunch of stake middleware seemed like not a terrible use of time, but im never going to do this

Comment thread clients/cli/src/config.rs
Comment on lines -57 to -61
// and program client
let program_client = Arc::new(ProgramRpcClient::new(
rpc_client.clone(),
ProgramRpcClientSendTransaction,
));
Copy link
Copy Markdown
Member Author

@2501babe 2501babe Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the backstory here is ProgramClient is a quirky rpc wrapper inside a quirky token client. we were originally using ProgramClient directly because it provides a cute generic send_transaction() that can either send or simulate, and we used the token wrapper since we already had to pull in spl_token_client so why not. we stopped using the simulate stuff so we could print simulation logs ourselves, and we dont use token22, so all this has been useless bloat ever since

Comment thread clients/cli/src/main.rs
Comment on lines -638 to +630
// we know the pool exists so the vote account must exist
unreachable!();
return Err(format!("Vote account {} does not exist", vote_account_address).into());
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not actually unreachable lol

Comment thread clients/cli/src/output.rs
Comment on lines +158 to +167
writeln_name_value(
w,
" Undelegated lamports:",
&self.undelegated_lamports.to_string(),
)?;
writeln_name_value(
w,
" Notional token supply:",
&self.token_supply.to_string(),
)?;
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in my original issue description i was imagining printing total value, pool stake, onramp stake, pool extra lamports, blah blah blah. but theres no need. what a user really cares about is a) how much is this pool worth, b) do i have to replenish

@2501babe 2501babe force-pushed the 20260421_display branch 2 times, most recently from 0b876bf to 2c41b37 Compare April 22, 2026 08:41
@2501babe 2501babe marked this pull request as ready for review April 22, 2026 09:01
@2501babe 2501babe requested a review from grod220 April 22, 2026 09:01
Comment thread clients/cli/src/quarantine.rs Outdated
Comment on lines 118 to 122
@@ -86,25 +122,45 @@ pub async fn get_available_balances(
);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this throw in the case of a funded-but-not-initialized onramp account? Perhaps we should display something in that case and not err? Or maybe that's an edge case anyway.

Copy link
Copy Markdown
Member Author

@2501babe 2501babe Apr 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bd99457 sigh halfway through writing an explanatory comment for this i realized the net asset value math has a bug #648

Comment on lines +71 to +73
} else {
Ok(None)
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See this in a few places, but I worry about potentially swallowing network issues/rate limits/timeouts/etc and treating it as a kind of "account does not exist" case. Think we avoid that case if we do:

let account = config
  .rpc_client
  .get_account_with_commitment(token_account_address, config.rpc_client.commitment())
  .await?
  .value;

let Some(account) = account else {
  return Ok(None);
};

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4e03279 maybe i lost the plot but i cant remember why we would ever want to treat a zero-len nonzero-lamport account as existing in any of these cases at all

Comment thread clients/cli/src/output.rs Outdated
"{} Onramp does not exist; use `spl-single-pool manage create-on-ramp` to create it",
style("/!\\").bold(),
)?;
} else if self.undelegated_lamports > self.minimum_delegation {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be >=?

} if deactivating == 0 || onramp_deactivation_epoch == clock.epoch => {
onramp_non_rent_lamports >= minimum_delegation
}

Copy link
Copy Markdown
Member Author

@2501babe 2501babe Apr 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

* display locked stake and phantom tokens in line with solana-program#582
* display total pool asset value and total excess lamports in line with solana-program#601
* show total network value for `DisplayAll` in sol rather than lamports
* display warnings for a dedelegated pool, nonexistent onramp, and potentially valuable replenish
* fix a bug where all `--dry-run` commands claimed to succeed
* remove `spl_token_client` including `ProgramClient`
* replace all uses of `spl_token`, `vote_program`, and `stake_program` with interface crates
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

cli: warn in display if account is deactivated cli: add new information to display

2 participants