diff --git a/docs/src/stake-pool.md b/docs/src/stake-pool.md index 44e8503fb30..5dfe742696e 100644 --- a/docs/src/stake-pool.md +++ b/docs/src/stake-pool.md @@ -734,23 +734,25 @@ Stake pools allow SOL withdrawals directly from the reserve and into a normal SOL wallet account, and in exchange burns the provided pool tokens. ```console -$ spl-stake-pool withdraw-sol Zg5YBPAk8RqBR9kaLLSoN5C8Uv7nErBz1WC63HTsCPR 2 +$ spl-stake-pool withdraw-sol Zg5YBPAk8RqBR9kaLLSoN5C8Uv7nErBz1WC63HTsCPR 7VXPpSxneL6JLj18Naw2gkukXtjBZfbmPh18cnoUCMD8 2 Signature: 4bqZKUUrjVspqTGqGqX4zxnHnJB67WbeukKUZRmxJ2yFmr275CtHPjZNzQJD9Pe7Q6mSxnUpcVv9FUdAbGP9RyBc ``` -The stake pool burned 2 pool tokens. In return, the stake pool sent SOL to the -fee payer for the transaction. You can check that the pool tokens have been burned: +The stake pool has burned 2 pool tokens, and in return, sent SOL to +`7VXPpSxneL6JLj18Naw2gkukXtjBZfbmPh18cnoUCMD8`. + +You can check that the pool tokens have been burned: ```console $ spl-token balance BoNneHKDrX9BHjjvSpPfnQyRjsnc9WFH71v8wrgCd7LB 98.00000000 ``` -And you can check that the fee payer has been credited: +And you can check that the recipient has been credited: ```console -$ solana balance -49.660334743 SOL +$ solana balance 7VXPpSxneL6JLj18Naw2gkukXtjBZfbmPh18cnoUCMD8 +2 SOL ``` ### Deposit stake @@ -788,12 +790,15 @@ Signature: 45x2UtA1b49eBPtRHdkvA3k8JneZzfwjptNN1kKQZaPABYiJ4hSA8qwi7qLNN5b3Fr4Z6 ``` The CLI will default to using the fee payer's -[Associated Token Account](associated-token-account.md) for stake pool tokens. +[Associated Token Account](associated-token-account.md) for stake pool tokens +and the withdraw authority on the deposited stake account. + Alternatively, you can create an SPL token account yourself and pass it as the -`token-receiver` for the command. +`token-receiver` for the command, and specify the withdraw authority on the +stake account using the `withdraw-authority` flag. ```console -$ spl-stake-pool deposit-stake Zg5YBPAk8RqBR9kaLLSoN5C8Uv7nErBz1WC63HTsCPR 97wBBiLVA7fUViEew8yV8R6tTdKithZDVz8LHLfF9sTJ --token-receiver 34XMHa3JUPv46ftU4dGHvemZ9oKVjnciRePYMcX3rjEF +$ spl-stake-pool deposit-stake Zg5YBPAk8RqBR9kaLLSoN5C8Uv7nErBz1WC63HTsCPR 97wBBiLVA7fUViEew8yV8R6tTdKithZDVz8LHLfF9sTJ --token-receiver 34XMHa3JUPv46ftU4dGHvemZ9oKVjnciRePYMcX3rjEF --withdraw-authority authority.json Depositing stake 97wBBiLVA7fUViEew8yV8R6tTdKithZDVz8LHLfF9sTJ into stake pool account F8e8Ympp4MkDSPZdvRxdQUZXRkMBDdyqgHa363GShAPt Signature: 4AESGZzqBVfj5xQnMiPWAwzJnAtQDRFK1Ha6jqKKTs46Zm5fw3LqgU1mRAT6CKTywVfFMHZCLm1hcQNScSMwVvjQ ``` diff --git a/stake-pool/cli/src/main.rs b/stake-pool/cli/src/main.rs index 851ee139d2c..6fc1868c5e2 100644 --- a/stake-pool/cli/src/main.rs +++ b/stake-pool/cli/src/main.rs @@ -10,9 +10,9 @@ use { input_parsers::{keypair_of, pubkey_of}, input_validators::{ is_amount, is_keypair, is_keypair_or_ask_keyword, is_parsable, is_pubkey, is_url, - is_valid_percentage, + is_valid_percentage, is_valid_pubkey, }, - keypair::signer_from_path, + keypair::{signer_from_path_with_config, SignerFromPathConfig}, }, solana_client::rpc_client::RpcClient, solana_program::{ @@ -88,12 +88,14 @@ fn get_signer( keypair_name: &str, keypair_path: &str, wallet_manager: &mut Option>, + signer_from_path_config: SignerFromPathConfig, ) -> Box { - signer_from_path( + signer_from_path_with_config( matches, matches.value_of(keypair_name).unwrap_or(keypair_path), keypair_name, wallet_manager, + &signer_from_path_config, ) .unwrap_or_else(|e| { eprintln!("error: {}", e); @@ -596,6 +598,7 @@ fn command_deposit_stake( config: &Config, stake_pool_address: &Pubkey, stake: &Pubkey, + withdraw_authority: Box, pool_token_receiver_account: &Option, referrer_token_account: &Option, ) -> CommandResult { @@ -634,7 +637,7 @@ fn command_deposit_stake( } let mut instructions: Vec = vec![]; - let mut signers = vec![config.fee_payer.as_ref(), config.staker.as_ref()]; + let mut signers = vec![config.fee_payer.as_ref(), withdraw_authority.as_ref()]; let mut total_rent_free_balances: u64 = 0; @@ -672,7 +675,7 @@ fn command_deposit_stake( &stake_deposit_authority.pubkey(), &pool_withdraw_authority, stake, - &config.staker.pubkey(), + &withdraw_authority.pubkey(), &validator_stake_account, &stake_pool.reserve_stake, &pool_token_receiver_account, @@ -688,7 +691,7 @@ fn command_deposit_stake( &stake_pool.validator_list, &pool_withdraw_authority, stake, - &config.staker.pubkey(), + &withdraw_authority.pubkey(), &validator_stake_account, &stake_pool.reserve_stake, &pool_token_receiver_account, @@ -1371,8 +1374,8 @@ fn command_withdraw_stake( fn command_withdraw_sol( config: &Config, stake_pool_address: &Pubkey, - sol_receiver: &Option, pool_token_account: &Option, + sol_receiver: &Pubkey, pool_amount: f64, ) -> CommandResult { if !config.no_update { @@ -1383,7 +1386,6 @@ fn command_withdraw_sol( let pool_mint = get_token_mint(&config.rpc_client, &stake_pool.pool_mint)?; let pool_amount = spl_token::ui_amount_to_amount(pool_amount, pool_mint.decimals); - let sol_receiver = sol_receiver.unwrap_or_else(|| config.fee_payer.pubkey()); let pool_token_account = pool_token_account.unwrap_or(get_associated_token_address( &config.token_owner.pubkey(), &stake_pool.pool_mint, @@ -1450,7 +1452,7 @@ fn command_withdraw_sol( &user_transfer_authority.pubkey(), &pool_token_account, &stake_pool.reserve_stake, - &sol_receiver, + sol_receiver, &stake_pool.manager_fee_account, &stake_pool.pool_mint, &spl_token::id(), @@ -1464,7 +1466,7 @@ fn command_withdraw_sol( &user_transfer_authority.pubkey(), &pool_token_account, &stake_pool.reserve_stake, - &sol_receiver, + sol_receiver, &stake_pool.manager_fee_account, &stake_pool.pool_mint, &spl_token::id(), @@ -2011,6 +2013,15 @@ fn main() { .required(true) .help("Stake address to join the pool"), ) + .arg( + Arg::with_name("withdraw_authority") + .long("withdraw-authority") + .validator(is_keypair) + .value_name("KEYPAIR") + .takes_value(true) + .help("Withdraw authority for the stake account to be deposited. \ + Defaults to the fee payer."), + ) .arg( Arg::with_name("token_receiver") .long("token-receiver") @@ -2181,8 +2192,17 @@ fn main() { .help("Stake pool address."), ) .arg( - Arg::with_name("amount") + Arg::with_name("sol_receiver") .index(2) + .validator(is_valid_pubkey) + .value_name("SYSTEM_ACCOUNT_ADDRESS_OR_KEYPAIR") + .takes_value(true) + .required(true) + .help("System account to receive SOL from the stake pool. Defaults to the payer."), + ) + .arg( + Arg::with_name("amount") + .index(3) .validator(is_amount) .value_name("AMOUNT") .takes_value(true) @@ -2197,14 +2217,6 @@ fn main() { .takes_value(true) .help("Pool token account to withdraw tokens from. Defaults to the token-owner's associated token account."), ) - .arg( - Arg::with_name("sol_receiver") - .long("sol-receiver") - .validator(is_pubkey) - .value_name("SYSTEM_ACCOUNT_ADDRESS") - .takes_value(true) - .help("System account to receive SOL from the stake pool. Defaults to the payer."), - ) ) .subcommand(SubCommand::with_name("set-manager") .about("Change manager or fee receiver account for the stake pool. Must be signed by the current manager.") @@ -2373,12 +2385,12 @@ fn main() { .get_matches(); let mut wallet_manager = None; + let cli_config = if let Some(config_file) = matches.value_of("config_file") { + solana_cli_config::Config::load(config_file).unwrap_or_default() + } else { + solana_cli_config::Config::default() + }; let config = { - let cli_config = if let Some(config_file) = matches.value_of("config_file") { - solana_cli_config::Config::load(config_file).unwrap_or_default() - } else { - solana_cli_config::Config::default() - }; let json_rpc_url = value_t!(matches, "json_rpc_url", String) .unwrap_or_else(|_| cli_config.json_rpc_url.clone()); @@ -2387,6 +2399,9 @@ fn main() { "staker", &cli_config.keypair_path, &mut wallet_manager, + SignerFromPathConfig { + allow_null_signer: false, + }, ); let funding_authority = if matches.is_present("funding_authority") { @@ -2395,6 +2410,9 @@ fn main() { "funding_authority", &cli_config.keypair_path, &mut wallet_manager, + SignerFromPathConfig { + allow_null_signer: false, + }, )) } else { None @@ -2404,18 +2422,27 @@ fn main() { "manager", &cli_config.keypair_path, &mut wallet_manager, + SignerFromPathConfig { + allow_null_signer: false, + }, ); let token_owner = get_signer( &matches, "token_owner", &cli_config.keypair_path, &mut wallet_manager, + SignerFromPathConfig { + allow_null_signer: false, + }, ); let fee_payer = get_signer( &matches, "fee_payer", &cli_config.keypair_path, &mut wallet_manager, + SignerFromPathConfig { + allow_null_signer: false, + }, ); let verbose = matches.is_present("verbose"); let dry_run = matches.is_present("dry_run"); @@ -2523,10 +2550,20 @@ fn main() { let stake_account = pubkey_of(arg_matches, "stake_account").unwrap(); let token_receiver: Option = pubkey_of(arg_matches, "token_receiver"); let referrer: Option = pubkey_of(arg_matches, "referrer"); + let withdraw_authority = get_signer( + arg_matches, + "withdraw_authority", + &cli_config.keypair_path, + &mut wallet_manager, + SignerFromPathConfig { + allow_null_signer: false, + }, + ); command_deposit_stake( &config, &stake_pool_address, &stake_account, + withdraw_authority, &token_receiver, &referrer, ) @@ -2577,12 +2614,21 @@ fn main() { let stake_pool_address = pubkey_of(arg_matches, "pool").unwrap(); let pool_account = pubkey_of(arg_matches, "pool_account"); let pool_amount = value_t_or_exit!(arg_matches, "amount", f64); - let sol_receiver = pubkey_of(arg_matches, "sol_receiver"); + let sol_receiver = get_signer( + arg_matches, + "sol_receiver", + &cli_config.keypair_path, + &mut wallet_manager, + SignerFromPathConfig { + allow_null_signer: true, + }, + ) + .pubkey(); command_withdraw_sol( &config, &stake_pool_address, - &sol_receiver, &pool_account, + &sol_receiver, pool_amount, ) }