stake-pool-cli: Address feedback (#2057)

* Fix signer parameters (staker, manager, depositor, fee-payer, and
  token-owner)
* On deposit / withdraw / add / remove validator, show the validator
  stake account and where the stake is going to
This commit is contained in:
Jon Cinque 2021-07-09 23:56:41 +02:00 committed by GitHub
parent b209d4ddc5
commit f35ca3c48c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 103 additions and 72 deletions

1
Cargo.lock generated
View File

@ -3847,6 +3847,7 @@ dependencies = [
"solana-client", "solana-client",
"solana-logger", "solana-logger",
"solana-program", "solana-program",
"solana-remote-wallet",
"solana-sdk", "solana-sdk",
"spl-associated-token-account 1.0.2", "spl-associated-token-account 1.0.2",
"spl-stake-pool", "spl-stake-pool",

View File

@ -17,8 +17,9 @@ solana-clap-utils = "=1.7.4"
solana-cli-config = "=1.7.4" solana-cli-config = "=1.7.4"
solana-client = "=1.7.4" solana-client = "=1.7.4"
solana-logger = "=1.7.4" solana-logger = "=1.7.4"
solana-sdk = "=1.7.4"
solana-program = "=1.7.4" solana-program = "=1.7.4"
solana-remote-wallet = "=1.7.4"
solana-sdk = "=1.7.4"
spl-associated-token-account = { version = "1.0", path="../../associated-token-account/program", features = [ "no-entrypoint" ] } spl-associated-token-account = { version = "1.0", path="../../associated-token-account/program", features = [ "no-entrypoint" ] }
spl-stake-pool = { version = "0.3", path="../program", features = [ "no-entrypoint" ] } spl-stake-pool = { version = "0.3", path="../program", features = [ "no-entrypoint" ] }
spl-token = { version = "3.1", path="../../token/program", features = [ "no-entrypoint" ] } spl-token = { version = "3.1", path="../../token/program", features = [ "no-entrypoint" ] }

View File

@ -7,7 +7,7 @@ use {
crate::client::*, crate::client::*,
clap::{ clap::{
crate_description, crate_name, crate_version, value_t, value_t_or_exit, App, AppSettings, crate_description, crate_name, crate_version, value_t, value_t_or_exit, App, AppSettings,
Arg, ArgGroup, SubCommand, Arg, ArgGroup, ArgMatches, SubCommand,
}, },
solana_clap_utils::{ solana_clap_utils::{
input_parsers::{keypair_of, pubkey_of}, input_parsers::{keypair_of, pubkey_of},
@ -23,6 +23,7 @@ use {
program_pack::Pack, program_pack::Pack,
pubkey::Pubkey, pubkey::Pubkey,
}, },
solana_remote_wallet::remote_wallet::RemoteWalletManager,
solana_sdk::{ solana_sdk::{
commitment_config::CommitmentConfig, commitment_config::CommitmentConfig,
native_token::{self, Sol}, native_token::{self, Sol},
@ -38,7 +39,7 @@ use {
stake_program::{self, StakeState}, stake_program::{self, StakeState},
state::{Fee, StakePool, ValidatorList}, state::{Fee, StakePool, ValidatorList},
}, },
std::process::exit, std::{process::exit, sync::Arc},
}; };
struct Config { struct Config {
@ -83,6 +84,24 @@ fn check_fee_payer_balance(config: &Config, required_balance: u64) -> Result<(),
} }
} }
fn get_signer(
matches: &ArgMatches<'_>,
keypair_name: &str,
keypair_path: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Box<dyn Signer> {
signer_from_path(
matches,
matches.value_of(keypair_name).unwrap_or(keypair_path),
keypair_name,
wallet_manager,
)
.unwrap_or_else(|e| {
eprintln!("error: {}", e);
exit(1);
})
}
fn send_transaction_no_wait( fn send_transaction_no_wait(
config: &Config, config: &Config,
transaction: Transaction, transaction: Transaction,
@ -316,16 +335,22 @@ fn command_vsa_create(
stake_pool_address: &Pubkey, stake_pool_address: &Pubkey,
vote_account: &Pubkey, vote_account: &Pubkey,
) -> CommandResult { ) -> CommandResult {
println!("Creating stake account on {}", vote_account); let (stake_account, _) =
find_stake_program_address(&spl_stake_pool::id(), vote_account, stake_pool_address);
println!(
"Creating stake account {}, delegated to {}",
stake_account, vote_account
);
let transaction = checked_transaction_with_signers( let transaction = checked_transaction_with_signers(
config, config,
&[ &[
// Create new validator stake account address // Create new validator stake account address
spl_stake_pool::instruction::create_validator_stake_account_with_vote( spl_stake_pool::instruction::create_validator_stake_account(
&spl_stake_pool::id(), &spl_stake_pool::id(),
stake_pool_address, stake_pool_address,
&config.staker.pubkey(), &config.staker.pubkey(),
&config.fee_payer.pubkey(), &config.fee_payer.pubkey(),
&stake_account,
vote_account, vote_account,
), ),
], ],
@ -342,6 +367,10 @@ fn command_vsa_add(
) -> CommandResult { ) -> CommandResult {
let (stake_account_address, _) = let (stake_account_address, _) =
find_stake_program_address(&spl_stake_pool::id(), vote_account, stake_pool_address); find_stake_program_address(&spl_stake_pool::id(), vote_account, stake_pool_address);
println!(
"Adding stake account {}, delegated to {}",
stake_account_address, vote_account
);
let stake_pool = get_stake_pool(&config.rpc_client, stake_pool_address)?; let stake_pool = get_stake_pool(&config.rpc_client, stake_pool_address)?;
let validator_list = get_validator_list(&config.rpc_client, &stake_pool.validator_list)?; let validator_list = get_validator_list(&config.rpc_client, &stake_pool.validator_list)?;
if validator_list.contains(vote_account) { if validator_list.contains(vote_account) {
@ -399,6 +428,13 @@ fn command_vsa_remove(
command_update(config, stake_pool_address, false, false)?; command_update(config, stake_pool_address, false, false)?;
} }
let (stake_account_address, _) =
find_stake_program_address(&spl_stake_pool::id(), vote_account, stake_pool_address);
println!(
"Removing stake account {}, delegated to {}",
stake_account_address, vote_account
);
let stake_pool = get_stake_pool(&config.rpc_client, stake_pool_address)?; let stake_pool = get_stake_pool(&config.rpc_client, stake_pool_address)?;
let staker_pubkey = config.staker.pubkey(); let staker_pubkey = config.staker.pubkey();
@ -522,7 +558,7 @@ fn add_associated_token_account(
// Account for tokens not specified, creating one // Account for tokens not specified, creating one
let account = get_associated_token_address(&config.fee_payer.pubkey(), mint); let account = get_associated_token_address(&config.fee_payer.pubkey(), mint);
if get_token_account(&config.rpc_client, &account, mint).is_err() { if get_token_account(&config.rpc_client, &account, mint).is_err() {
println!("Creating account to receive tokens {}", account); println!("Creating associated token account {} to receive stake pool tokens of mint {}, owned by {}", account, mint, config.fee_payer.pubkey());
let min_account_balance = config let min_account_balance = config
.rpc_client .rpc_client
@ -536,6 +572,8 @@ fn add_associated_token_account(
)); ));
*rent_free_balances += min_account_balance; *rent_free_balances += min_account_balance;
} else {
println!("Using existing associated token account {} to receive stake pool tokens of mint {}, owned by {}", account, mint, config.fee_payer.pubkey());
} }
account account
@ -573,7 +611,10 @@ fn command_deposit(
find_stake_program_address(&spl_stake_pool::id(), &vote_account, stake_pool_address); find_stake_program_address(&spl_stake_pool::id(), &vote_account, stake_pool_address);
let validator_stake_state = get_stake_state(&config.rpc_client, &validator_stake_account)?; let validator_stake_state = get_stake_state(&config.rpc_client, &validator_stake_account)?;
println!("Depositing into stake account {}", validator_stake_account); println!(
"Depositing stake {} into stake pool account {}",
stake, validator_stake_account
);
if config.verbose { if config.verbose {
println!("{:?}", validator_stake_state); println!("{:?}", validator_stake_state);
} }
@ -797,7 +838,8 @@ fn command_update(
#[derive(PartialEq, Debug)] #[derive(PartialEq, Debug)]
struct WithdrawAccount { struct WithdrawAccount {
address: Pubkey, stake_address: Pubkey,
vote_address: Option<Pubkey>,
pool_amount: u64, pool_amount: u64,
} }
@ -823,7 +865,7 @@ fn prepare_withdraw_accounts(
let mut remaining_amount = pool_amount; let mut remaining_amount = pool_amount;
// Go through available accounts and withdraw from largest to smallest // Go through available accounts and withdraw from largest to smallest
for (address, lamports, _) in accounts { for (stake_address, lamports, stake) in accounts {
if lamports <= min_balance { if lamports <= min_balance {
continue; continue;
} }
@ -834,7 +876,8 @@ fn prepare_withdraw_accounts(
// Those accounts will be withdrawn completely with `claim` instruction // Those accounts will be withdrawn completely with `claim` instruction
withdraw_from.push(WithdrawAccount { withdraw_from.push(WithdrawAccount {
address, stake_address,
vote_address: stake.delegation().map(|x| x.voter_pubkey),
pool_amount, pool_amount,
}); });
remaining_amount -= pool_amount; remaining_amount -= pool_amount;
@ -898,7 +941,8 @@ fn command_withdraw(
let withdraw_accounts = if use_reserve { let withdraw_accounts = if use_reserve {
vec![WithdrawAccount { vec![WithdrawAccount {
address: stake_pool.reserve_stake, stake_address: stake_pool.reserve_stake,
vote_address: None,
pool_amount, pool_amount,
}] }]
} else if let Some(vote_account_address) = vote_account_address { } else if let Some(vote_account_address) = vote_account_address {
@ -919,7 +963,8 @@ fn command_withdraw(
.into()); .into());
} }
vec![WithdrawAccount { vec![WithdrawAccount {
address: stake_account_address, stake_address: stake_account_address,
vote_address: Some(*vote_account_address),
pool_amount, pool_amount,
}] }]
} else { } else {
@ -940,7 +985,7 @@ fn command_withdraw(
config.token_owner.as_ref(), config.token_owner.as_ref(),
&user_transfer_authority, &user_transfer_authority,
]; ];
let stake_receiver_account = Keypair::new(); // Will be added to signers if creating new account let mut new_stake_keypairs = vec![];
instructions.push( instructions.push(
// Approve spending token // Approve spending token
@ -954,9 +999,6 @@ fn command_withdraw(
)?, )?,
); );
// Use separate mutable variable because withdraw might create a new account
let mut stake_receiver: Option<Pubkey> = *stake_receiver_param;
let mut total_rent_free_balances = 0; let mut total_rent_free_balances = 0;
// Go through prepared accounts and withdraw/claim them // Go through prepared accounts and withdraw/claim them
@ -967,48 +1009,52 @@ fn command_withdraw(
.unwrap(); .unwrap();
println!( println!(
"Withdrawing from account {}, amount {}, {} pool tokens", "Withdrawing {}, or {} pool tokens, from stake account {}, delegated to {:?}, stake / withdraw authority {}",
withdraw_account.address,
Sol(sol_withdraw_amount), Sol(sol_withdraw_amount),
spl_token::amount_to_ui_amount(withdraw_account.pool_amount, pool_mint.decimals), spl_token::amount_to_ui_amount(withdraw_account.pool_amount, pool_mint.decimals),
withdraw_account.stake_address,
withdraw_account.vote_address,
config.staker.pubkey(),
); );
if stake_receiver.is_none() { // Use separate mutable variable because withdraw might create a new account
let stake_receiver = stake_receiver_param.unwrap_or_else(|| {
// Account for tokens not specified, creating one // Account for tokens not specified, creating one
let stake_receiver_account = Keypair::new(); // Will be added to signers if creating new account
let stake_receiver_pubkey = stake_receiver_account.pubkey();
println!( println!(
"Creating account to receive stake {}", "Creating account to receive stake {}",
stake_receiver_account.pubkey() stake_receiver_pubkey
); );
let stake_receiver_account_balance = config let stake_receiver_account_balance = config
.rpc_client .rpc_client
.get_minimum_balance_for_rent_exemption(STAKE_STATE_LEN)?; .get_minimum_balance_for_rent_exemption(STAKE_STATE_LEN)
.unwrap();
instructions.push( instructions.push(
// Creating new account // Creating new account
system_instruction::create_account( system_instruction::create_account(
&config.fee_payer.pubkey(), &config.fee_payer.pubkey(),
&stake_receiver_account.pubkey(), &stake_receiver_pubkey,
stake_receiver_account_balance, stake_receiver_account_balance,
STAKE_STATE_LEN as u64, STAKE_STATE_LEN as u64,
&stake_program::id(), &stake_program::id(),
), ),
); );
signers.push(&stake_receiver_account);
total_rent_free_balances += stake_receiver_account_balance; total_rent_free_balances += stake_receiver_account_balance;
new_stake_keypairs.push(stake_receiver_account);
stake_receiver = Some(stake_receiver_account.pubkey()); stake_receiver_pubkey
} });
instructions.push(spl_stake_pool::instruction::withdraw( instructions.push(spl_stake_pool::instruction::withdraw(
&spl_stake_pool::id(), &spl_stake_pool::id(),
stake_pool_address, stake_pool_address,
&stake_pool.validator_list, &stake_pool.validator_list,
&pool_withdraw_authority, &pool_withdraw_authority,
&withdraw_account.address, &withdraw_account.stake_address,
&stake_receiver.unwrap(), // Cannot be none at this point &stake_receiver,
&config.staker.pubkey(), &config.staker.pubkey(),
&user_transfer_authority.pubkey(), &user_transfer_authority.pubkey(),
&pool_token_account, &pool_token_account,
@ -1026,6 +1072,9 @@ fn command_withdraw(
config, config,
total_rent_free_balances + fee_calculator.calculate_fee(transaction.message()), total_rent_free_balances + fee_calculator.calculate_fee(transaction.message()),
)?; )?;
for new_stake_keypair in &new_stake_keypairs {
signers.push(new_stake_keypair);
}
unique_signers!(signers); unique_signers!(signers);
transaction.sign(&signers, recent_blockhash); transaction.sign(&signers, recent_blockhash);
send_transaction(config, transaction)?; send_transaction(config, transaction)?;
@ -1562,6 +1611,7 @@ fn main() {
.validator(is_pubkey) .validator(is_pubkey)
.value_name("STAKE_ACCOUNT_ADDRESS") .value_name("STAKE_ACCOUNT_ADDRESS")
.takes_value(true) .takes_value(true)
.requires("withdraw_from")
.help("Stake account to receive SOL from the stake pool. Defaults to a new stake account."), .help("Stake account to receive SOL from the stake pool. Defaults to a new stake account."),
) )
.arg( .arg(
@ -1679,62 +1729,41 @@ fn main() {
let json_rpc_url = value_t!(matches, "json_rpc_url", String) let json_rpc_url = value_t!(matches, "json_rpc_url", String)
.unwrap_or_else(|_| cli_config.json_rpc_url.clone()); .unwrap_or_else(|_| cli_config.json_rpc_url.clone());
let staker = signer_from_path( let staker = get_signer(
&matches, &matches,
&cli_config.keypair_path,
"staker", "staker",
&cli_config.keypair_path,
&mut wallet_manager, &mut wallet_manager,
) );
.unwrap_or_else(|e| {
eprintln!("error: {}", e);
exit(1);
});
let depositor = if matches.is_present("depositor") { let depositor = if matches.is_present("depositor") {
Some( Some(get_signer(
signer_from_path( &matches,
&matches, "depositor",
&cli_config.keypair_path, &cli_config.keypair_path,
"depositor", &mut wallet_manager,
&mut wallet_manager, ))
)
.unwrap_or_else(|e| {
eprintln!("error: {}", e);
exit(1);
}),
)
} else { } else {
None None
}; };
let manager = signer_from_path( let manager = get_signer(
&matches, &matches,
&cli_config.keypair_path,
"manager", "manager",
&mut wallet_manager,
)
.unwrap_or_else(|e| {
eprintln!("error: {}", e);
exit(1);
});
let token_owner = signer_from_path(
&matches,
&cli_config.keypair_path, &cli_config.keypair_path,
&mut wallet_manager,
);
let token_owner = get_signer(
&matches,
"token_owner", "token_owner",
&mut wallet_manager,
)
.unwrap_or_else(|e| {
eprintln!("error: {}", e);
exit(1);
});
let fee_payer = signer_from_path(
&matches,
&cli_config.keypair_path, &cli_config.keypair_path,
"fee_payer",
&mut wallet_manager, &mut wallet_manager,
) );
.unwrap_or_else(|e| { let fee_payer = get_signer(
eprintln!("error: {}", e); &matches,
exit(1); "fee_payer",
}); &cli_config.keypair_path,
&mut wallet_manager,
);
let verbose = matches.is_present("verbose"); let verbose = matches.is_present("verbose");
let dry_run = matches.is_present("dry_run"); let dry_run = matches.is_present("dry_run");
let no_update = matches.is_present("no_update"); let no_update = matches.is_present("no_update");