14
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										14
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							@@ -4584,6 +4584,20 @@ dependencies = [
 | 
			
		||||
 "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "solana-stake-accounts"
 | 
			
		||||
version = "1.2.0"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "solana-clap-utils 1.2.0",
 | 
			
		||||
 "solana-cli-config 1.2.0",
 | 
			
		||||
 "solana-client 1.2.0",
 | 
			
		||||
 "solana-remote-wallet 1.2.0",
 | 
			
		||||
 "solana-runtime 1.2.0",
 | 
			
		||||
 "solana-sdk 1.2.0",
 | 
			
		||||
 "solana-stake-program 1.2.0",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "solana-stake-monitor"
 | 
			
		||||
version = "1.1.0"
 | 
			
		||||
 
 | 
			
		||||
@@ -52,6 +52,7 @@ members = [
 | 
			
		||||
    "sdk",
 | 
			
		||||
    "sdk-c",
 | 
			
		||||
    "scripts",
 | 
			
		||||
    "stake-accounts",
 | 
			
		||||
    "stake-monitor",
 | 
			
		||||
    "sys-tuner",
 | 
			
		||||
    "transaction-status",
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,7 @@
 | 
			
		||||
  * [Generate Keys](cli/generate-keys.md)
 | 
			
		||||
  * [Send and Receive Tokens](cli/transfer-tokens.md)
 | 
			
		||||
  * [Delegate Stake](cli/delegate-stake.md)
 | 
			
		||||
  * [Manage Stake Accounts](cli/manage-stake-accounts.md)
 | 
			
		||||
  * [Offline Signing](offline-signing/README.md)
 | 
			
		||||
    * [Durable Transaction Nonces](offline-signing/durable-nonce.md)
 | 
			
		||||
  * [Command-line Reference](cli/usage.md)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										74
									
								
								docs/src/cli/manage-stake-accounts.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								docs/src/cli/manage-stake-accounts.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,74 @@
 | 
			
		||||
# Manage Stake Accounts
 | 
			
		||||
 | 
			
		||||
If you want to delegate stake to many different validators, you will need
 | 
			
		||||
to create a separate stake account for each. If you follow the convention
 | 
			
		||||
of creating the first stake account at seed "0", the second at "1", the
 | 
			
		||||
third at "2", and so on, then the `solana-stake-accounts` tool will allow
 | 
			
		||||
you to operate on all accounts with single invocations. You can use it to
 | 
			
		||||
sum up the balances of all accounts, move accounts to a new wallet, or set
 | 
			
		||||
new authorities.
 | 
			
		||||
 | 
			
		||||
## Usage
 | 
			
		||||
 | 
			
		||||
### Create a stake account
 | 
			
		||||
 | 
			
		||||
Create and fund a derived stake account at the stake authority public key:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
solana-stake-accounts new <FUNDING_KEYPAIR> <BASE_KEYPAIR> <AMOUNT> \
 | 
			
		||||
    --stake-authority <PUBKEY> --withdraw-authority <PUBKEY>
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Count accounts
 | 
			
		||||
 | 
			
		||||
Count the number of derived accounts:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
solana-stake-accounts count <BASE_PUBKEY>
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Get stake account balances
 | 
			
		||||
 | 
			
		||||
Sum the balance of derived stake accounts:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
solana-stake-accounts balance <BASE_PUBKEY> --num-accounts <NUMBER>
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Get stake account addresses
 | 
			
		||||
 | 
			
		||||
List the address of each stake account derived from the given public key:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
solana-stake-accounts addresses <BASE_PUBKEY> --num-accounts <NUMBER>
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Set new authorities
 | 
			
		||||
 | 
			
		||||
Set new authorities on each derived stake account:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
solana-stake-accounts authorize <BASE_PUBKEY> \
 | 
			
		||||
    --stake-authority <KEYPAIR> --withdraw-authority <KEYPAIR> \
 | 
			
		||||
    --new-stake-authority <PUBKEY> --new-withdraw-authority <PUBKEY> \
 | 
			
		||||
    --num-accounts <NUMBER>
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Relocate stake accounts
 | 
			
		||||
 | 
			
		||||
Relocate stake accounts:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
solana-stake-accounts rebase <BASE_PUBKEY> <NEW_BASE_KEYPAIR> \
 | 
			
		||||
    --stake-authority <KEYPAIR> --num-accounts <NUMBER>
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
To atomically rebase and authorize each stake account, use the 'move'
 | 
			
		||||
command:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
solana-stake-accounts move <BASE_PUBKEY> <NEW_BASE_KEYPAIR> \
 | 
			
		||||
    --stake-authority <KEYPAIR> --withdraw-authority <KEYPAIR> \
 | 
			
		||||
    --new-stake-authority <PUBKEY> --new-withdraw-authority <PUBKEY> \
 | 
			
		||||
    --num-accounts <NUMBER>
 | 
			
		||||
```
 | 
			
		||||
							
								
								
									
										21
									
								
								stake-accounts/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								stake-accounts/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
[package]
 | 
			
		||||
name = "solana-stake-accounts"
 | 
			
		||||
description = "Blockchain, Rebuilt for Scale"
 | 
			
		||||
authors = ["Solana Maintainers <maintainers@solana.com>"]
 | 
			
		||||
edition = "2018"
 | 
			
		||||
version = "1.1.0"
 | 
			
		||||
repository = "https://github.com/solana-labs/solana"
 | 
			
		||||
license = "Apache-2.0"
 | 
			
		||||
homepage = "https://solana.com/"
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
clap = "2.33.0"
 | 
			
		||||
solana-clap-utils = { path = "../clap-utils", version = "1.1.0" }
 | 
			
		||||
solana-cli-config = { path = "../cli-config", version = "1.1.0" }
 | 
			
		||||
solana-client = { path = "../client", version = "1.1.0" }
 | 
			
		||||
solana-remote-wallet = { path = "../remote-wallet", version = "1.1.0" }
 | 
			
		||||
solana-sdk = { path = "../sdk", version = "1.1.0" }
 | 
			
		||||
solana-stake-program = { path = "../programs/stake", version = "1.1.0" }
 | 
			
		||||
 | 
			
		||||
[dev-dependencies]
 | 
			
		||||
solana-runtime = { path = "../runtime", version = "1.1.0" }
 | 
			
		||||
							
								
								
									
										382
									
								
								stake-accounts/src/args.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										382
									
								
								stake-accounts/src/args.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,382 @@
 | 
			
		||||
use clap::{value_t_or_exit, App, Arg, ArgMatches, SubCommand};
 | 
			
		||||
use solana_clap_utils::input_validators::{is_amount, is_valid_pubkey, is_valid_signer};
 | 
			
		||||
use solana_cli_config::CONFIG_FILE;
 | 
			
		||||
use solana_sdk::native_token::sol_to_lamports;
 | 
			
		||||
use std::ffi::OsString;
 | 
			
		||||
use std::process::exit;
 | 
			
		||||
 | 
			
		||||
pub(crate) struct NewCommandConfig {
 | 
			
		||||
    pub fee_payer: String,
 | 
			
		||||
    pub funding_keypair: String,
 | 
			
		||||
    pub base_keypair: String,
 | 
			
		||||
    pub lamports: u64,
 | 
			
		||||
    pub stake_authority: String,
 | 
			
		||||
    pub withdraw_authority: String,
 | 
			
		||||
    pub index: usize,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) struct CountCommandConfig {
 | 
			
		||||
    pub base_pubkey: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) struct QueryCommandConfig {
 | 
			
		||||
    pub base_pubkey: String,
 | 
			
		||||
    pub num_accounts: usize,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) struct AuthorizeCommandConfig {
 | 
			
		||||
    pub fee_payer: String,
 | 
			
		||||
    pub base_pubkey: String,
 | 
			
		||||
    pub stake_authority: String,
 | 
			
		||||
    pub withdraw_authority: String,
 | 
			
		||||
    pub new_stake_authority: String,
 | 
			
		||||
    pub new_withdraw_authority: String,
 | 
			
		||||
    pub num_accounts: usize,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) struct RebaseCommandConfig {
 | 
			
		||||
    pub fee_payer: String,
 | 
			
		||||
    pub base_pubkey: String,
 | 
			
		||||
    pub new_base_keypair: String,
 | 
			
		||||
    pub stake_authority: String,
 | 
			
		||||
    pub num_accounts: usize,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) struct MoveCommandConfig {
 | 
			
		||||
    pub rebase_config: RebaseCommandConfig,
 | 
			
		||||
    pub authorize_config: AuthorizeCommandConfig,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) enum Command {
 | 
			
		||||
    New(NewCommandConfig),
 | 
			
		||||
    Count(CountCommandConfig),
 | 
			
		||||
    Addresses(QueryCommandConfig),
 | 
			
		||||
    Balance(QueryCommandConfig),
 | 
			
		||||
    Authorize(AuthorizeCommandConfig),
 | 
			
		||||
    Rebase(RebaseCommandConfig),
 | 
			
		||||
    Move(Box<MoveCommandConfig>),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) struct CommandConfig {
 | 
			
		||||
    pub config_file: String,
 | 
			
		||||
    pub url: Option<String>,
 | 
			
		||||
    pub command: Command,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn fee_payer_arg<'a, 'b>() -> Arg<'a, 'b> {
 | 
			
		||||
    Arg::with_name("fee_payer")
 | 
			
		||||
        .long("fee-payer")
 | 
			
		||||
        .required(true)
 | 
			
		||||
        .takes_value(true)
 | 
			
		||||
        .value_name("KEYPAIR")
 | 
			
		||||
        .validator(is_valid_signer)
 | 
			
		||||
        .help("Fee payer")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn funding_keypair_arg<'a, 'b>() -> Arg<'a, 'b> {
 | 
			
		||||
    Arg::with_name("funding_keypair")
 | 
			
		||||
        .required(true)
 | 
			
		||||
        .takes_value(true)
 | 
			
		||||
        .value_name("FUNDING_KEYPAIR")
 | 
			
		||||
        .validator(is_valid_signer)
 | 
			
		||||
        .help("Keypair to fund accounts")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn base_pubkey_arg<'a, 'b>() -> Arg<'a, 'b> {
 | 
			
		||||
    Arg::with_name("base_pubkey")
 | 
			
		||||
        .required(true)
 | 
			
		||||
        .takes_value(true)
 | 
			
		||||
        .value_name("BASE_PUBKEY")
 | 
			
		||||
        .validator(is_valid_pubkey)
 | 
			
		||||
        .help("Public key which stake account addresses are derived from")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn new_base_keypair_arg<'a, 'b>() -> Arg<'a, 'b> {
 | 
			
		||||
    Arg::with_name("new_base_keypair")
 | 
			
		||||
        .required(true)
 | 
			
		||||
        .takes_value(true)
 | 
			
		||||
        .value_name("NEW_BASE_KEYPAIR")
 | 
			
		||||
        .validator(is_valid_signer)
 | 
			
		||||
        .help("New keypair which stake account addresses are derived from")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn stake_authority_arg<'a, 'b>() -> Arg<'a, 'b> {
 | 
			
		||||
    Arg::with_name("stake_authority")
 | 
			
		||||
        .long("stake-authority")
 | 
			
		||||
        .required(true)
 | 
			
		||||
        .takes_value(true)
 | 
			
		||||
        .value_name("KEYPAIR")
 | 
			
		||||
        .validator(is_valid_signer)
 | 
			
		||||
        .help("Stake authority")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn withdraw_authority_arg<'a, 'b>() -> Arg<'a, 'b> {
 | 
			
		||||
    Arg::with_name("withdraw_authority")
 | 
			
		||||
        .long("withdraw-authority")
 | 
			
		||||
        .required(true)
 | 
			
		||||
        .takes_value(true)
 | 
			
		||||
        .value_name("KEYPAIR")
 | 
			
		||||
        .validator(is_valid_signer)
 | 
			
		||||
        .help("Withdraw authority")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn new_stake_authority_arg<'a, 'b>() -> Arg<'a, 'b> {
 | 
			
		||||
    Arg::with_name("new_stake_authority")
 | 
			
		||||
        .long("new-stake-authority")
 | 
			
		||||
        .required(true)
 | 
			
		||||
        .takes_value(true)
 | 
			
		||||
        .value_name("PUBKEY")
 | 
			
		||||
        .validator(is_valid_pubkey)
 | 
			
		||||
        .help("New stake authority")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn new_withdraw_authority_arg<'a, 'b>() -> Arg<'a, 'b> {
 | 
			
		||||
    Arg::with_name("new_withdraw_authority")
 | 
			
		||||
        .long("new-withdraw-authority")
 | 
			
		||||
        .required(true)
 | 
			
		||||
        .takes_value(true)
 | 
			
		||||
        .value_name("PUBKEY")
 | 
			
		||||
        .validator(is_valid_pubkey)
 | 
			
		||||
        .help("New withdraw authority")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn num_accounts_arg<'a, 'b>() -> Arg<'a, 'b> {
 | 
			
		||||
    Arg::with_name("num_accounts")
 | 
			
		||||
        .long("num-accounts")
 | 
			
		||||
        .required(true)
 | 
			
		||||
        .takes_value(true)
 | 
			
		||||
        .value_name("NUMBER")
 | 
			
		||||
        .help("Number of derived stake accounts")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) fn get_matches<'a, I, T>(args: I) -> ArgMatches<'a>
 | 
			
		||||
where
 | 
			
		||||
    I: IntoIterator<Item = T>,
 | 
			
		||||
    T: Into<OsString> + Clone,
 | 
			
		||||
{
 | 
			
		||||
    let default_config_file = CONFIG_FILE.as_ref().unwrap();
 | 
			
		||||
    App::new("solana-stake-accounts")
 | 
			
		||||
        .about("about")
 | 
			
		||||
        .version("version")
 | 
			
		||||
        .arg(
 | 
			
		||||
            Arg::with_name("config_file")
 | 
			
		||||
                .long("config")
 | 
			
		||||
                .takes_value(true)
 | 
			
		||||
                .value_name("FILEPATH")
 | 
			
		||||
                .default_value(default_config_file)
 | 
			
		||||
                .help("Config file"),
 | 
			
		||||
        )
 | 
			
		||||
        .arg(
 | 
			
		||||
            Arg::with_name("url")
 | 
			
		||||
                .long("url")
 | 
			
		||||
                .global(true)
 | 
			
		||||
                .takes_value(true)
 | 
			
		||||
                .value_name("URL")
 | 
			
		||||
                .help("RPC entrypoint address. i.e. http://devnet.solana.com"),
 | 
			
		||||
        )
 | 
			
		||||
        .subcommand(
 | 
			
		||||
            SubCommand::with_name("new")
 | 
			
		||||
                .about("Create derived stake accounts")
 | 
			
		||||
                .arg(fee_payer_arg())
 | 
			
		||||
                .arg(funding_keypair_arg().index(1))
 | 
			
		||||
                .arg(
 | 
			
		||||
                    Arg::with_name("base_keypair")
 | 
			
		||||
                        .required(true)
 | 
			
		||||
                        .index(2)
 | 
			
		||||
                        .takes_value(true)
 | 
			
		||||
                        .value_name("BASE_KEYPAIR")
 | 
			
		||||
                        .validator(is_valid_signer)
 | 
			
		||||
                        .help("Keypair which stake account addresses are derived from"),
 | 
			
		||||
                )
 | 
			
		||||
                .arg(
 | 
			
		||||
                    Arg::with_name("amount")
 | 
			
		||||
                        .required(true)
 | 
			
		||||
                        .index(3)
 | 
			
		||||
                        .takes_value(true)
 | 
			
		||||
                        .value_name("AMOUNT")
 | 
			
		||||
                        .validator(is_amount)
 | 
			
		||||
                        .help("Amount to move into the new stake accounts, in SOL"),
 | 
			
		||||
                )
 | 
			
		||||
                .arg(
 | 
			
		||||
                    Arg::with_name("stake_authority")
 | 
			
		||||
                        .long("stake-authority")
 | 
			
		||||
                        .required(true)
 | 
			
		||||
                        .takes_value(true)
 | 
			
		||||
                        .value_name("PUBKEY")
 | 
			
		||||
                        .validator(is_valid_pubkey)
 | 
			
		||||
                        .help("Stake authority"),
 | 
			
		||||
                )
 | 
			
		||||
                .arg(
 | 
			
		||||
                    Arg::with_name("withdraw_authority")
 | 
			
		||||
                        .long("withdraw-authority")
 | 
			
		||||
                        .required(true)
 | 
			
		||||
                        .takes_value(true)
 | 
			
		||||
                        .value_name("PUBKEY")
 | 
			
		||||
                        .validator(is_valid_pubkey)
 | 
			
		||||
                        .help("Withdraw authority"),
 | 
			
		||||
                )
 | 
			
		||||
                .arg(
 | 
			
		||||
                    Arg::with_name("index")
 | 
			
		||||
                        .long("index")
 | 
			
		||||
                        .takes_value(true)
 | 
			
		||||
                        .default_value("0")
 | 
			
		||||
                        .value_name("NUMBER")
 | 
			
		||||
                        .help("Index of the derived account to create"),
 | 
			
		||||
                ),
 | 
			
		||||
        )
 | 
			
		||||
        .subcommand(
 | 
			
		||||
            SubCommand::with_name("count")
 | 
			
		||||
                .about("Count derived stake accounts")
 | 
			
		||||
                .arg(base_pubkey_arg().index(1)),
 | 
			
		||||
        )
 | 
			
		||||
        .subcommand(
 | 
			
		||||
            SubCommand::with_name("addresses")
 | 
			
		||||
                .about("Show public keys of all derived stake accounts")
 | 
			
		||||
                .arg(base_pubkey_arg().index(1))
 | 
			
		||||
                .arg(num_accounts_arg()),
 | 
			
		||||
        )
 | 
			
		||||
        .subcommand(
 | 
			
		||||
            SubCommand::with_name("balance")
 | 
			
		||||
                .about("Sum balances of all derived stake accounts")
 | 
			
		||||
                .arg(base_pubkey_arg().index(1))
 | 
			
		||||
                .arg(num_accounts_arg()),
 | 
			
		||||
        )
 | 
			
		||||
        .subcommand(
 | 
			
		||||
            SubCommand::with_name("authorize")
 | 
			
		||||
                .about("Set new authorities in all derived stake accounts")
 | 
			
		||||
                .arg(fee_payer_arg())
 | 
			
		||||
                .arg(base_pubkey_arg().index(1))
 | 
			
		||||
                .arg(stake_authority_arg())
 | 
			
		||||
                .arg(withdraw_authority_arg())
 | 
			
		||||
                .arg(new_stake_authority_arg())
 | 
			
		||||
                .arg(new_withdraw_authority_arg())
 | 
			
		||||
                .arg(num_accounts_arg()),
 | 
			
		||||
        )
 | 
			
		||||
        .subcommand(
 | 
			
		||||
            SubCommand::with_name("rebase")
 | 
			
		||||
                .about("Relocate derived stake accounts")
 | 
			
		||||
                .arg(fee_payer_arg())
 | 
			
		||||
                .arg(base_pubkey_arg().index(1))
 | 
			
		||||
                .arg(new_base_keypair_arg().index(2))
 | 
			
		||||
                .arg(stake_authority_arg())
 | 
			
		||||
                .arg(num_accounts_arg()),
 | 
			
		||||
        )
 | 
			
		||||
        .subcommand(
 | 
			
		||||
            SubCommand::with_name("move")
 | 
			
		||||
                .about("Rebase and set new authorities in all derived stake accounts")
 | 
			
		||||
                .arg(fee_payer_arg())
 | 
			
		||||
                .arg(base_pubkey_arg().index(1))
 | 
			
		||||
                .arg(new_base_keypair_arg().index(2))
 | 
			
		||||
                .arg(stake_authority_arg())
 | 
			
		||||
                .arg(withdraw_authority_arg())
 | 
			
		||||
                .arg(new_stake_authority_arg())
 | 
			
		||||
                .arg(new_withdraw_authority_arg())
 | 
			
		||||
                .arg(num_accounts_arg()),
 | 
			
		||||
        )
 | 
			
		||||
        .get_matches_from(args)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn parse_new_args(matches: &ArgMatches<'_>) -> NewCommandConfig {
 | 
			
		||||
    let fee_payer = value_t_or_exit!(matches, "fee_payer", String);
 | 
			
		||||
    let funding_keypair = value_t_or_exit!(matches, "funding_keypair", String);
 | 
			
		||||
    let lamports = sol_to_lamports(value_t_or_exit!(matches, "amount", f64));
 | 
			
		||||
    let base_keypair = value_t_or_exit!(matches, "base_keypair", String);
 | 
			
		||||
    let stake_authority = value_t_or_exit!(matches, "stake_authority", String);
 | 
			
		||||
    let withdraw_authority = value_t_or_exit!(matches, "withdraw_authority", String);
 | 
			
		||||
    let index = value_t_or_exit!(matches, "index", usize);
 | 
			
		||||
    NewCommandConfig {
 | 
			
		||||
        fee_payer,
 | 
			
		||||
        funding_keypair,
 | 
			
		||||
        lamports,
 | 
			
		||||
        base_keypair,
 | 
			
		||||
        stake_authority,
 | 
			
		||||
        withdraw_authority,
 | 
			
		||||
        index,
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn parse_count_args(matches: &ArgMatches<'_>) -> CountCommandConfig {
 | 
			
		||||
    let base_pubkey = value_t_or_exit!(matches, "base_pubkey", String);
 | 
			
		||||
    CountCommandConfig { base_pubkey }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn parse_query_args(matches: &ArgMatches<'_>) -> QueryCommandConfig {
 | 
			
		||||
    let base_pubkey = value_t_or_exit!(matches, "base_pubkey", String);
 | 
			
		||||
    let num_accounts = value_t_or_exit!(matches, "num_accounts", usize);
 | 
			
		||||
    QueryCommandConfig {
 | 
			
		||||
        base_pubkey,
 | 
			
		||||
        num_accounts,
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn parse_authorize_args(matches: &ArgMatches<'_>) -> AuthorizeCommandConfig {
 | 
			
		||||
    let fee_payer = value_t_or_exit!(matches, "fee_payer", String);
 | 
			
		||||
    let base_pubkey = value_t_or_exit!(matches, "base_pubkey", String);
 | 
			
		||||
    let stake_authority = value_t_or_exit!(matches, "stake_authority", String);
 | 
			
		||||
    let withdraw_authority = value_t_or_exit!(matches, "withdraw_authority", String);
 | 
			
		||||
    let new_stake_authority = value_t_or_exit!(matches, "new_stake_authority", String);
 | 
			
		||||
    let new_withdraw_authority = value_t_or_exit!(matches, "new_withdraw_authority", String);
 | 
			
		||||
    let num_accounts = value_t_or_exit!(matches, "num_accounts", usize);
 | 
			
		||||
    AuthorizeCommandConfig {
 | 
			
		||||
        fee_payer,
 | 
			
		||||
        base_pubkey,
 | 
			
		||||
        stake_authority,
 | 
			
		||||
        withdraw_authority,
 | 
			
		||||
        new_stake_authority,
 | 
			
		||||
        new_withdraw_authority,
 | 
			
		||||
        num_accounts,
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn parse_rebase_args(matches: &ArgMatches<'_>) -> RebaseCommandConfig {
 | 
			
		||||
    let fee_payer = value_t_or_exit!(matches, "fee_payer", String);
 | 
			
		||||
    let base_pubkey = value_t_or_exit!(matches, "base_pubkey", String);
 | 
			
		||||
    let new_base_keypair = value_t_or_exit!(matches, "new_base_keypair", String);
 | 
			
		||||
    let stake_authority = value_t_or_exit!(matches, "stake_authority", String);
 | 
			
		||||
    let num_accounts = value_t_or_exit!(matches, "num_accounts", usize);
 | 
			
		||||
    RebaseCommandConfig {
 | 
			
		||||
        fee_payer,
 | 
			
		||||
        base_pubkey,
 | 
			
		||||
        new_base_keypair,
 | 
			
		||||
        stake_authority,
 | 
			
		||||
        num_accounts,
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn parse_move_args(matches: &ArgMatches<'_>) -> MoveCommandConfig {
 | 
			
		||||
    let rebase_config = parse_rebase_args(matches);
 | 
			
		||||
    let authorize_config = parse_authorize_args(matches);
 | 
			
		||||
    MoveCommandConfig {
 | 
			
		||||
        rebase_config,
 | 
			
		||||
        authorize_config,
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) fn parse_args<I, T>(args: I) -> CommandConfig
 | 
			
		||||
where
 | 
			
		||||
    I: IntoIterator<Item = T>,
 | 
			
		||||
    T: Into<OsString> + Clone,
 | 
			
		||||
{
 | 
			
		||||
    let matches = get_matches(args);
 | 
			
		||||
    let config_file = matches.value_of("config_file").unwrap().to_string();
 | 
			
		||||
    let url = matches.value_of("url").map(|x| x.to_string());
 | 
			
		||||
 | 
			
		||||
    let command = match matches.subcommand() {
 | 
			
		||||
        ("new", Some(matches)) => Command::New(parse_new_args(matches)),
 | 
			
		||||
        ("count", Some(matches)) => Command::Count(parse_count_args(matches)),
 | 
			
		||||
        ("addresses", Some(matches)) => Command::Addresses(parse_query_args(matches)),
 | 
			
		||||
        ("balance", Some(matches)) => Command::Balance(parse_query_args(matches)),
 | 
			
		||||
        ("authorize", Some(matches)) => Command::Authorize(parse_authorize_args(matches)),
 | 
			
		||||
        ("rebase", Some(matches)) => Command::Rebase(parse_rebase_args(matches)),
 | 
			
		||||
        ("move", Some(matches)) => Command::Move(Box::new(parse_move_args(matches))),
 | 
			
		||||
        _ => {
 | 
			
		||||
            eprintln!("{}", matches.usage());
 | 
			
		||||
            exit(1);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
    CommandConfig {
 | 
			
		||||
        config_file,
 | 
			
		||||
        url,
 | 
			
		||||
        command,
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										306
									
								
								stake-accounts/src/main.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										306
									
								
								stake-accounts/src/main.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,306 @@
 | 
			
		||||
mod args;
 | 
			
		||||
mod stake_accounts;
 | 
			
		||||
 | 
			
		||||
use crate::args::{
 | 
			
		||||
    parse_args, AuthorizeCommandConfig, Command, MoveCommandConfig, NewCommandConfig,
 | 
			
		||||
    RebaseCommandConfig,
 | 
			
		||||
};
 | 
			
		||||
use clap::ArgMatches;
 | 
			
		||||
use solana_clap_utils::keypair::{pubkey_from_path, signer_from_path};
 | 
			
		||||
use solana_cli_config::Config;
 | 
			
		||||
use solana_client::client_error::ClientError;
 | 
			
		||||
use solana_client::rpc_client::RpcClient;
 | 
			
		||||
use solana_remote_wallet::remote_wallet::{maybe_wallet_manager, RemoteWalletManager};
 | 
			
		||||
use solana_sdk::{
 | 
			
		||||
    message::Message,
 | 
			
		||||
    native_token::lamports_to_sol,
 | 
			
		||||
    pubkey::Pubkey,
 | 
			
		||||
    signature::{Signature, Signer},
 | 
			
		||||
    signers::Signers,
 | 
			
		||||
    transaction::Transaction,
 | 
			
		||||
};
 | 
			
		||||
use std::env;
 | 
			
		||||
use std::error::Error;
 | 
			
		||||
use std::sync::Arc;
 | 
			
		||||
 | 
			
		||||
fn resolve_stake_authority(
 | 
			
		||||
    wallet_manager: Option<&Arc<RemoteWalletManager>>,
 | 
			
		||||
    key_url: &str,
 | 
			
		||||
) -> Result<Box<dyn Signer>, Box<dyn Error>> {
 | 
			
		||||
    let matches = ArgMatches::default();
 | 
			
		||||
    signer_from_path(&matches, key_url, "stake authority", wallet_manager)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn resolve_withdraw_authority(
 | 
			
		||||
    wallet_manager: Option<&Arc<RemoteWalletManager>>,
 | 
			
		||||
    key_url: &str,
 | 
			
		||||
) -> Result<Box<dyn Signer>, Box<dyn Error>> {
 | 
			
		||||
    let matches = ArgMatches::default();
 | 
			
		||||
    signer_from_path(&matches, key_url, "withdraw authority", wallet_manager)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn resolve_new_stake_authority(
 | 
			
		||||
    wallet_manager: Option<&Arc<RemoteWalletManager>>,
 | 
			
		||||
    key_url: &str,
 | 
			
		||||
) -> Result<Pubkey, Box<dyn Error>> {
 | 
			
		||||
    let matches = ArgMatches::default();
 | 
			
		||||
    pubkey_from_path(&matches, key_url, "new stake authority", wallet_manager)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn resolve_new_withdraw_authority(
 | 
			
		||||
    wallet_manager: Option<&Arc<RemoteWalletManager>>,
 | 
			
		||||
    key_url: &str,
 | 
			
		||||
) -> Result<Pubkey, Box<dyn Error>> {
 | 
			
		||||
    let matches = ArgMatches::default();
 | 
			
		||||
    pubkey_from_path(&matches, key_url, "new withdraw authority", wallet_manager)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn resolve_fee_payer(
 | 
			
		||||
    wallet_manager: Option<&Arc<RemoteWalletManager>>,
 | 
			
		||||
    key_url: &str,
 | 
			
		||||
) -> Result<Box<dyn Signer>, Box<dyn Error>> {
 | 
			
		||||
    let matches = ArgMatches::default();
 | 
			
		||||
    signer_from_path(&matches, key_url, "fee-payer", wallet_manager)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn resolve_base_pubkey(
 | 
			
		||||
    wallet_manager: Option<&Arc<RemoteWalletManager>>,
 | 
			
		||||
    key_url: &str,
 | 
			
		||||
) -> Result<Pubkey, Box<dyn Error>> {
 | 
			
		||||
    let matches = ArgMatches::default();
 | 
			
		||||
    pubkey_from_path(&matches, key_url, "base pubkey", wallet_manager)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn get_balance_at(client: &RpcClient, pubkey: &Pubkey, i: usize) -> Result<u64, ClientError> {
 | 
			
		||||
    let address = stake_accounts::derive_stake_account_address(pubkey, i);
 | 
			
		||||
    client.get_balance(&address)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Return the number of derived stake accounts with balances
 | 
			
		||||
fn count_stake_accounts(client: &RpcClient, base_pubkey: &Pubkey) -> Result<usize, ClientError> {
 | 
			
		||||
    let mut i = 0;
 | 
			
		||||
    while get_balance_at(client, base_pubkey, i)? > 0 {
 | 
			
		||||
        i += 1;
 | 
			
		||||
    }
 | 
			
		||||
    Ok(i)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn get_balances(
 | 
			
		||||
    client: &RpcClient,
 | 
			
		||||
    addresses: Vec<Pubkey>,
 | 
			
		||||
) -> Result<Vec<(Pubkey, u64)>, ClientError> {
 | 
			
		||||
    addresses
 | 
			
		||||
        .into_iter()
 | 
			
		||||
        .map(|pubkey| client.get_balance(&pubkey).map(|bal| (pubkey, bal)))
 | 
			
		||||
        .collect()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn process_new_stake_account(
 | 
			
		||||
    client: &RpcClient,
 | 
			
		||||
    wallet_manager: Option<&Arc<RemoteWalletManager>>,
 | 
			
		||||
    new_config: &NewCommandConfig,
 | 
			
		||||
) -> Result<Signature, Box<dyn Error>> {
 | 
			
		||||
    let matches = ArgMatches::default();
 | 
			
		||||
    let fee_payer_keypair = resolve_fee_payer(wallet_manager, &new_config.fee_payer)?;
 | 
			
		||||
    let funding_keypair = signer_from_path(
 | 
			
		||||
        &matches,
 | 
			
		||||
        &new_config.funding_keypair,
 | 
			
		||||
        "funding keypair",
 | 
			
		||||
        wallet_manager,
 | 
			
		||||
    )?;
 | 
			
		||||
    let base_keypair = signer_from_path(
 | 
			
		||||
        &matches,
 | 
			
		||||
        &new_config.base_keypair,
 | 
			
		||||
        "base keypair",
 | 
			
		||||
        wallet_manager,
 | 
			
		||||
    )?;
 | 
			
		||||
    let stake_authority_pubkey = pubkey_from_path(
 | 
			
		||||
        &matches,
 | 
			
		||||
        &new_config.stake_authority,
 | 
			
		||||
        "stake authority",
 | 
			
		||||
        wallet_manager,
 | 
			
		||||
    )?;
 | 
			
		||||
    let withdraw_authority_pubkey = pubkey_from_path(
 | 
			
		||||
        &matches,
 | 
			
		||||
        &new_config.withdraw_authority,
 | 
			
		||||
        "withdraw authority",
 | 
			
		||||
        wallet_manager,
 | 
			
		||||
    )?;
 | 
			
		||||
    let message = stake_accounts::new_stake_account(
 | 
			
		||||
        &fee_payer_keypair.pubkey(),
 | 
			
		||||
        &funding_keypair.pubkey(),
 | 
			
		||||
        &base_keypair.pubkey(),
 | 
			
		||||
        new_config.lamports,
 | 
			
		||||
        &stake_authority_pubkey,
 | 
			
		||||
        &withdraw_authority_pubkey,
 | 
			
		||||
        new_config.index,
 | 
			
		||||
    );
 | 
			
		||||
    let signers = vec![&*fee_payer_keypair, &*funding_keypair, &*base_keypair];
 | 
			
		||||
    let signature = send_message(client, message, &signers)?;
 | 
			
		||||
    Ok(signature)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn process_authorize_stake_accounts(
 | 
			
		||||
    client: &RpcClient,
 | 
			
		||||
    wallet_manager: Option<&Arc<RemoteWalletManager>>,
 | 
			
		||||
    authorize_config: &AuthorizeCommandConfig,
 | 
			
		||||
) -> Result<(), Box<dyn Error>> {
 | 
			
		||||
    let fee_payer_keypair = resolve_fee_payer(wallet_manager, &authorize_config.fee_payer)?;
 | 
			
		||||
    let base_pubkey = resolve_base_pubkey(wallet_manager, &authorize_config.base_pubkey)?;
 | 
			
		||||
    let stake_authority_keypair =
 | 
			
		||||
        resolve_stake_authority(wallet_manager, &authorize_config.stake_authority)?;
 | 
			
		||||
    let withdraw_authority_keypair =
 | 
			
		||||
        resolve_withdraw_authority(wallet_manager, &authorize_config.withdraw_authority)?;
 | 
			
		||||
    let new_stake_authority_pubkey =
 | 
			
		||||
        resolve_new_stake_authority(wallet_manager, &authorize_config.new_stake_authority)?;
 | 
			
		||||
    let new_withdraw_authority_pubkey =
 | 
			
		||||
        resolve_new_withdraw_authority(wallet_manager, &authorize_config.new_withdraw_authority)?;
 | 
			
		||||
    let messages = stake_accounts::authorize_stake_accounts(
 | 
			
		||||
        &fee_payer_keypair.pubkey(),
 | 
			
		||||
        &base_pubkey,
 | 
			
		||||
        &stake_authority_keypair.pubkey(),
 | 
			
		||||
        &withdraw_authority_keypair.pubkey(),
 | 
			
		||||
        &new_stake_authority_pubkey,
 | 
			
		||||
        &new_withdraw_authority_pubkey,
 | 
			
		||||
        authorize_config.num_accounts,
 | 
			
		||||
    );
 | 
			
		||||
    let signers = vec![
 | 
			
		||||
        &*fee_payer_keypair,
 | 
			
		||||
        &*stake_authority_keypair,
 | 
			
		||||
        &*withdraw_authority_keypair,
 | 
			
		||||
    ];
 | 
			
		||||
    for message in messages {
 | 
			
		||||
        let signature = send_message(client, message, &signers)?;
 | 
			
		||||
        println!("{}", signature);
 | 
			
		||||
    }
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn process_rebase_stake_accounts(
 | 
			
		||||
    client: &RpcClient,
 | 
			
		||||
    wallet_manager: Option<&Arc<RemoteWalletManager>>,
 | 
			
		||||
    rebase_config: &RebaseCommandConfig,
 | 
			
		||||
) -> Result<(), Box<dyn Error>> {
 | 
			
		||||
    let fee_payer_keypair = resolve_fee_payer(wallet_manager, &rebase_config.fee_payer)?;
 | 
			
		||||
    let base_pubkey = resolve_base_pubkey(wallet_manager, &rebase_config.base_pubkey)?;
 | 
			
		||||
    let stake_authority_keypair =
 | 
			
		||||
        resolve_stake_authority(wallet_manager, &rebase_config.stake_authority)?;
 | 
			
		||||
    let addresses =
 | 
			
		||||
        stake_accounts::derive_stake_account_addresses(&base_pubkey, rebase_config.num_accounts);
 | 
			
		||||
    let balances = get_balances(&client, addresses)?;
 | 
			
		||||
 | 
			
		||||
    let messages = stake_accounts::rebase_stake_accounts(
 | 
			
		||||
        &fee_payer_keypair.pubkey(),
 | 
			
		||||
        &base_pubkey,
 | 
			
		||||
        &stake_authority_keypair.pubkey(),
 | 
			
		||||
        &balances,
 | 
			
		||||
    );
 | 
			
		||||
    let signers = vec![&*fee_payer_keypair, &*stake_authority_keypair];
 | 
			
		||||
    for message in messages {
 | 
			
		||||
        let signature = send_message(client, message, &signers)?;
 | 
			
		||||
        println!("{}", signature);
 | 
			
		||||
    }
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn process_move_stake_accounts(
 | 
			
		||||
    client: &RpcClient,
 | 
			
		||||
    wallet_manager: Option<&Arc<RemoteWalletManager>>,
 | 
			
		||||
    move_config: &MoveCommandConfig,
 | 
			
		||||
) -> Result<(), Box<dyn Error>> {
 | 
			
		||||
    let authorize_config = &move_config.authorize_config;
 | 
			
		||||
    let fee_payer_keypair = resolve_fee_payer(wallet_manager, &authorize_config.fee_payer)?;
 | 
			
		||||
    let base_pubkey = resolve_base_pubkey(wallet_manager, &authorize_config.base_pubkey)?;
 | 
			
		||||
    let stake_authority_keypair =
 | 
			
		||||
        resolve_stake_authority(wallet_manager, &authorize_config.stake_authority)?;
 | 
			
		||||
    let withdraw_authority_keypair =
 | 
			
		||||
        resolve_withdraw_authority(wallet_manager, &authorize_config.withdraw_authority)?;
 | 
			
		||||
    let new_stake_authority_pubkey =
 | 
			
		||||
        resolve_new_stake_authority(wallet_manager, &authorize_config.new_stake_authority)?;
 | 
			
		||||
    let new_withdraw_authority_pubkey =
 | 
			
		||||
        resolve_new_withdraw_authority(wallet_manager, &authorize_config.new_withdraw_authority)?;
 | 
			
		||||
    let addresses =
 | 
			
		||||
        stake_accounts::derive_stake_account_addresses(&base_pubkey, authorize_config.num_accounts);
 | 
			
		||||
    let balances = get_balances(&client, addresses)?;
 | 
			
		||||
 | 
			
		||||
    let messages = stake_accounts::move_stake_accounts(
 | 
			
		||||
        &fee_payer_keypair.pubkey(),
 | 
			
		||||
        &base_pubkey,
 | 
			
		||||
        &stake_authority_keypair.pubkey(),
 | 
			
		||||
        &withdraw_authority_keypair.pubkey(),
 | 
			
		||||
        &new_stake_authority_pubkey,
 | 
			
		||||
        &new_withdraw_authority_pubkey,
 | 
			
		||||
        &balances,
 | 
			
		||||
    );
 | 
			
		||||
    let signers = vec![
 | 
			
		||||
        &*fee_payer_keypair,
 | 
			
		||||
        &*stake_authority_keypair,
 | 
			
		||||
        &*withdraw_authority_keypair,
 | 
			
		||||
    ];
 | 
			
		||||
    for message in messages {
 | 
			
		||||
        let signature = send_message(client, message, &signers)?;
 | 
			
		||||
        println!("{}", signature);
 | 
			
		||||
    }
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn send_message<S: Signers>(
 | 
			
		||||
    client: &RpcClient,
 | 
			
		||||
    message: Message,
 | 
			
		||||
    signers: &S,
 | 
			
		||||
) -> Result<Signature, ClientError> {
 | 
			
		||||
    let mut transaction = Transaction::new_unsigned(message);
 | 
			
		||||
    client.resign_transaction(&mut transaction, signers)?;
 | 
			
		||||
    client.send_and_confirm_transaction_with_spinner(&mut transaction, signers)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn main() -> Result<(), Box<dyn Error>> {
 | 
			
		||||
    let command_config = parse_args(env::args_os());
 | 
			
		||||
    let config = Config::load(&command_config.config_file)?;
 | 
			
		||||
    let json_rpc_url = command_config.url.unwrap_or(config.json_rpc_url);
 | 
			
		||||
    let client = RpcClient::new(json_rpc_url);
 | 
			
		||||
 | 
			
		||||
    let wallet_manager = maybe_wallet_manager()?;
 | 
			
		||||
    let wallet_manager = wallet_manager.as_ref();
 | 
			
		||||
    match command_config.command {
 | 
			
		||||
        Command::New(new_config) => {
 | 
			
		||||
            process_new_stake_account(&client, wallet_manager, &new_config)?;
 | 
			
		||||
        }
 | 
			
		||||
        Command::Count(count_config) => {
 | 
			
		||||
            let base_pubkey = resolve_base_pubkey(wallet_manager, &count_config.base_pubkey)?;
 | 
			
		||||
            let num_accounts = count_stake_accounts(&client, &base_pubkey)?;
 | 
			
		||||
            println!("{}", num_accounts);
 | 
			
		||||
        }
 | 
			
		||||
        Command::Addresses(query_config) => {
 | 
			
		||||
            let base_pubkey = resolve_base_pubkey(wallet_manager, &query_config.base_pubkey)?;
 | 
			
		||||
            let addresses = stake_accounts::derive_stake_account_addresses(
 | 
			
		||||
                &base_pubkey,
 | 
			
		||||
                query_config.num_accounts,
 | 
			
		||||
            );
 | 
			
		||||
            for address in addresses {
 | 
			
		||||
                println!("{:?}", address);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        Command::Balance(query_config) => {
 | 
			
		||||
            let base_pubkey = resolve_base_pubkey(wallet_manager, &query_config.base_pubkey)?;
 | 
			
		||||
            let addresses = stake_accounts::derive_stake_account_addresses(
 | 
			
		||||
                &base_pubkey,
 | 
			
		||||
                query_config.num_accounts,
 | 
			
		||||
            );
 | 
			
		||||
            let balances = get_balances(&client, addresses)?;
 | 
			
		||||
            let lamports: u64 = balances.into_iter().map(|(_, bal)| bal).sum();
 | 
			
		||||
            let sol = lamports_to_sol(lamports);
 | 
			
		||||
            println!("{} SOL", sol);
 | 
			
		||||
        }
 | 
			
		||||
        Command::Authorize(authorize_config) => {
 | 
			
		||||
            process_authorize_stake_accounts(&client, wallet_manager, &authorize_config)?;
 | 
			
		||||
        }
 | 
			
		||||
        Command::Rebase(rebase_config) => {
 | 
			
		||||
            process_rebase_stake_accounts(&client, wallet_manager, &rebase_config)?;
 | 
			
		||||
        }
 | 
			
		||||
        Command::Move(move_config) => {
 | 
			
		||||
            process_move_stake_accounts(&client, wallet_manager, &move_config)?;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										463
									
								
								stake-accounts/src/stake_accounts.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										463
									
								
								stake-accounts/src/stake_accounts.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,463 @@
 | 
			
		||||
use solana_sdk::{instruction::Instruction, message::Message, pubkey::Pubkey};
 | 
			
		||||
use solana_stake_program::{
 | 
			
		||||
    stake_instruction,
 | 
			
		||||
    stake_state::{Authorized, Lockup, StakeAuthorize},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
pub(crate) fn derive_stake_account_address(base_pubkey: &Pubkey, i: usize) -> Pubkey {
 | 
			
		||||
    Pubkey::create_with_seed(base_pubkey, &i.to_string(), &solana_stake_program::id()).unwrap()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Return derived addresses
 | 
			
		||||
pub(crate) fn derive_stake_account_addresses(
 | 
			
		||||
    base_pubkey: &Pubkey,
 | 
			
		||||
    num_accounts: usize,
 | 
			
		||||
) -> Vec<Pubkey> {
 | 
			
		||||
    (0..num_accounts)
 | 
			
		||||
        .map(|i| derive_stake_account_address(base_pubkey, i))
 | 
			
		||||
        .collect()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) fn new_stake_account(
 | 
			
		||||
    fee_payer_pubkey: &Pubkey,
 | 
			
		||||
    funding_pubkey: &Pubkey,
 | 
			
		||||
    base_pubkey: &Pubkey,
 | 
			
		||||
    lamports: u64,
 | 
			
		||||
    stake_authority_pubkey: &Pubkey,
 | 
			
		||||
    withdraw_authority_pubkey: &Pubkey,
 | 
			
		||||
    index: usize,
 | 
			
		||||
) -> Message {
 | 
			
		||||
    let stake_account_address = derive_stake_account_address(base_pubkey, index);
 | 
			
		||||
    let authorized = Authorized {
 | 
			
		||||
        staker: *stake_authority_pubkey,
 | 
			
		||||
        withdrawer: *withdraw_authority_pubkey,
 | 
			
		||||
    };
 | 
			
		||||
    let instructions = stake_instruction::create_account_with_seed(
 | 
			
		||||
        funding_pubkey,
 | 
			
		||||
        &stake_account_address,
 | 
			
		||||
        &base_pubkey,
 | 
			
		||||
        &index.to_string(),
 | 
			
		||||
        &authorized,
 | 
			
		||||
        &Lockup::default(),
 | 
			
		||||
        lamports,
 | 
			
		||||
    );
 | 
			
		||||
    Message::new_with_payer(&instructions, Some(fee_payer_pubkey))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn authorize_stake_accounts_instructions(
 | 
			
		||||
    stake_account_address: &Pubkey,
 | 
			
		||||
    stake_authority_pubkey: &Pubkey,
 | 
			
		||||
    withdraw_authority_pubkey: &Pubkey,
 | 
			
		||||
    new_stake_authority_pubkey: &Pubkey,
 | 
			
		||||
    new_withdraw_authority_pubkey: &Pubkey,
 | 
			
		||||
) -> Vec<Instruction> {
 | 
			
		||||
    let instruction0 = stake_instruction::authorize(
 | 
			
		||||
        &stake_account_address,
 | 
			
		||||
        stake_authority_pubkey,
 | 
			
		||||
        new_stake_authority_pubkey,
 | 
			
		||||
        StakeAuthorize::Staker,
 | 
			
		||||
    );
 | 
			
		||||
    let instruction1 = stake_instruction::authorize(
 | 
			
		||||
        &stake_account_address,
 | 
			
		||||
        withdraw_authority_pubkey,
 | 
			
		||||
        new_withdraw_authority_pubkey,
 | 
			
		||||
        StakeAuthorize::Withdrawer,
 | 
			
		||||
    );
 | 
			
		||||
    vec![instruction0, instruction1]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn rebase_stake_account(
 | 
			
		||||
    stake_account_address: &Pubkey,
 | 
			
		||||
    new_base_pubkey: &Pubkey,
 | 
			
		||||
    i: usize,
 | 
			
		||||
    fee_payer_pubkey: &Pubkey,
 | 
			
		||||
    stake_authority_pubkey: &Pubkey,
 | 
			
		||||
    lamports: u64,
 | 
			
		||||
) -> Message {
 | 
			
		||||
    let new_stake_account_address = derive_stake_account_address(new_base_pubkey, i);
 | 
			
		||||
    let instructions = stake_instruction::split_with_seed(
 | 
			
		||||
        stake_account_address,
 | 
			
		||||
        stake_authority_pubkey,
 | 
			
		||||
        lamports,
 | 
			
		||||
        &new_stake_account_address,
 | 
			
		||||
        new_base_pubkey,
 | 
			
		||||
        &i.to_string(),
 | 
			
		||||
    );
 | 
			
		||||
    Message::new_with_payer(&instructions, Some(&fee_payer_pubkey))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn move_stake_account(
 | 
			
		||||
    stake_account_address: &Pubkey,
 | 
			
		||||
    new_base_pubkey: &Pubkey,
 | 
			
		||||
    i: usize,
 | 
			
		||||
    fee_payer_pubkey: &Pubkey,
 | 
			
		||||
    stake_authority_pubkey: &Pubkey,
 | 
			
		||||
    withdraw_authority_pubkey: &Pubkey,
 | 
			
		||||
    new_stake_authority_pubkey: &Pubkey,
 | 
			
		||||
    new_withdraw_authority_pubkey: &Pubkey,
 | 
			
		||||
    lamports: u64,
 | 
			
		||||
) -> Message {
 | 
			
		||||
    let new_stake_account_address = derive_stake_account_address(new_base_pubkey, i);
 | 
			
		||||
    let mut instructions = stake_instruction::split_with_seed(
 | 
			
		||||
        stake_account_address,
 | 
			
		||||
        stake_authority_pubkey,
 | 
			
		||||
        lamports,
 | 
			
		||||
        &new_stake_account_address,
 | 
			
		||||
        new_base_pubkey,
 | 
			
		||||
        &i.to_string(),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    let authorize_instructions = authorize_stake_accounts_instructions(
 | 
			
		||||
        &new_stake_account_address,
 | 
			
		||||
        stake_authority_pubkey,
 | 
			
		||||
        withdraw_authority_pubkey,
 | 
			
		||||
        new_stake_authority_pubkey,
 | 
			
		||||
        new_withdraw_authority_pubkey,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    instructions.extend(authorize_instructions.into_iter());
 | 
			
		||||
    Message::new_with_payer(&instructions, Some(&fee_payer_pubkey))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) fn authorize_stake_accounts(
 | 
			
		||||
    fee_payer_pubkey: &Pubkey,
 | 
			
		||||
    base_pubkey: &Pubkey,
 | 
			
		||||
    stake_authority_pubkey: &Pubkey,
 | 
			
		||||
    withdraw_authority_pubkey: &Pubkey,
 | 
			
		||||
    new_stake_authority_pubkey: &Pubkey,
 | 
			
		||||
    new_withdraw_authority_pubkey: &Pubkey,
 | 
			
		||||
    num_accounts: usize,
 | 
			
		||||
) -> Vec<Message> {
 | 
			
		||||
    let stake_account_addresses = derive_stake_account_addresses(base_pubkey, num_accounts);
 | 
			
		||||
    stake_account_addresses
 | 
			
		||||
        .iter()
 | 
			
		||||
        .map(|stake_account_address| {
 | 
			
		||||
            let instructions = authorize_stake_accounts_instructions(
 | 
			
		||||
                stake_account_address,
 | 
			
		||||
                stake_authority_pubkey,
 | 
			
		||||
                withdraw_authority_pubkey,
 | 
			
		||||
                new_stake_authority_pubkey,
 | 
			
		||||
                new_withdraw_authority_pubkey,
 | 
			
		||||
            );
 | 
			
		||||
            Message::new_with_payer(&instructions, Some(&fee_payer_pubkey))
 | 
			
		||||
        })
 | 
			
		||||
        .collect::<Vec<_>>()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) fn rebase_stake_accounts(
 | 
			
		||||
    fee_payer_pubkey: &Pubkey,
 | 
			
		||||
    new_base_pubkey: &Pubkey,
 | 
			
		||||
    stake_authority_pubkey: &Pubkey,
 | 
			
		||||
    balances: &[(Pubkey, u64)],
 | 
			
		||||
) -> Vec<Message> {
 | 
			
		||||
    balances
 | 
			
		||||
        .iter()
 | 
			
		||||
        .enumerate()
 | 
			
		||||
        .map(|(i, (stake_account_address, lamports))| {
 | 
			
		||||
            rebase_stake_account(
 | 
			
		||||
                stake_account_address,
 | 
			
		||||
                new_base_pubkey,
 | 
			
		||||
                i,
 | 
			
		||||
                fee_payer_pubkey,
 | 
			
		||||
                stake_authority_pubkey,
 | 
			
		||||
                *lamports,
 | 
			
		||||
            )
 | 
			
		||||
        })
 | 
			
		||||
        .collect()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) fn move_stake_accounts(
 | 
			
		||||
    fee_payer_pubkey: &Pubkey,
 | 
			
		||||
    new_base_pubkey: &Pubkey,
 | 
			
		||||
    stake_authority_pubkey: &Pubkey,
 | 
			
		||||
    withdraw_authority_pubkey: &Pubkey,
 | 
			
		||||
    new_stake_authority_pubkey: &Pubkey,
 | 
			
		||||
    new_withdraw_authority_pubkey: &Pubkey,
 | 
			
		||||
    balances: &[(Pubkey, u64)],
 | 
			
		||||
) -> Vec<Message> {
 | 
			
		||||
    balances
 | 
			
		||||
        .iter()
 | 
			
		||||
        .enumerate()
 | 
			
		||||
        .map(|(i, (stake_account_address, lamports))| {
 | 
			
		||||
            move_stake_account(
 | 
			
		||||
                stake_account_address,
 | 
			
		||||
                new_base_pubkey,
 | 
			
		||||
                i,
 | 
			
		||||
                fee_payer_pubkey,
 | 
			
		||||
                stake_authority_pubkey,
 | 
			
		||||
                withdraw_authority_pubkey,
 | 
			
		||||
                new_stake_authority_pubkey,
 | 
			
		||||
                new_withdraw_authority_pubkey,
 | 
			
		||||
                *lamports,
 | 
			
		||||
            )
 | 
			
		||||
        })
 | 
			
		||||
        .collect()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use super::*;
 | 
			
		||||
    use solana_runtime::{bank::Bank, bank_client::BankClient};
 | 
			
		||||
    use solana_sdk::{
 | 
			
		||||
        account::Account,
 | 
			
		||||
        client::SyncClient,
 | 
			
		||||
        genesis_config::create_genesis_config,
 | 
			
		||||
        signature::{Keypair, Signer},
 | 
			
		||||
    };
 | 
			
		||||
    use solana_stake_program::stake_state::StakeState;
 | 
			
		||||
 | 
			
		||||
    fn create_bank(lamports: u64) -> (Bank, Keypair, u64) {
 | 
			
		||||
        let (genesis_config, mint_keypair) = create_genesis_config(lamports);
 | 
			
		||||
        let mut bank = Bank::new(&genesis_config);
 | 
			
		||||
        bank.add_instruction_processor(
 | 
			
		||||
            solana_stake_program::id(),
 | 
			
		||||
            solana_stake_program::stake_instruction::process_instruction,
 | 
			
		||||
        );
 | 
			
		||||
        let rent = bank.get_minimum_balance_for_rent_exemption(std::mem::size_of::<StakeState>());
 | 
			
		||||
        (bank, mint_keypair, rent)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn create_account<C: SyncClient>(
 | 
			
		||||
        client: &C,
 | 
			
		||||
        funding_keypair: &Keypair,
 | 
			
		||||
        lamports: u64,
 | 
			
		||||
    ) -> Keypair {
 | 
			
		||||
        let fee_payer_keypair = Keypair::new();
 | 
			
		||||
        client
 | 
			
		||||
            .transfer(lamports, &funding_keypair, &fee_payer_keypair.pubkey())
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        fee_payer_keypair
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn get_account_at<C: SyncClient>(client: &C, base_pubkey: &Pubkey, i: usize) -> Account {
 | 
			
		||||
        let account_address = derive_stake_account_address(&base_pubkey, i);
 | 
			
		||||
        client.get_account(&account_address).unwrap().unwrap()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn get_balances<C: SyncClient>(
 | 
			
		||||
        client: &C,
 | 
			
		||||
        base_pubkey: &Pubkey,
 | 
			
		||||
        num_accounts: usize,
 | 
			
		||||
    ) -> Vec<(Pubkey, u64)> {
 | 
			
		||||
        (0..num_accounts)
 | 
			
		||||
            .into_iter()
 | 
			
		||||
            .map(|i| {
 | 
			
		||||
                let address = derive_stake_account_address(&base_pubkey, i);
 | 
			
		||||
                (address, client.get_balance(&address).unwrap())
 | 
			
		||||
            })
 | 
			
		||||
            .collect()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_new_derived_stake_account() {
 | 
			
		||||
        let (bank, funding_keypair, rent) = create_bank(10_000_000);
 | 
			
		||||
        let funding_pubkey = funding_keypair.pubkey();
 | 
			
		||||
        let bank_client = BankClient::new(bank);
 | 
			
		||||
        let fee_payer_keypair = create_account(&bank_client, &funding_keypair, 1);
 | 
			
		||||
        let fee_payer_pubkey = fee_payer_keypair.pubkey();
 | 
			
		||||
 | 
			
		||||
        let base_keypair = Keypair::new();
 | 
			
		||||
        let base_pubkey = base_keypair.pubkey();
 | 
			
		||||
        let lamports = rent + 1;
 | 
			
		||||
        let stake_authority_pubkey = Pubkey::new_rand();
 | 
			
		||||
        let withdraw_authority_pubkey = Pubkey::new_rand();
 | 
			
		||||
 | 
			
		||||
        let message = new_stake_account(
 | 
			
		||||
            &fee_payer_pubkey,
 | 
			
		||||
            &funding_pubkey,
 | 
			
		||||
            &base_pubkey,
 | 
			
		||||
            lamports,
 | 
			
		||||
            &stake_authority_pubkey,
 | 
			
		||||
            &withdraw_authority_pubkey,
 | 
			
		||||
            0,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        let signers = [&funding_keypair, &fee_payer_keypair, &base_keypair];
 | 
			
		||||
        bank_client.send_message(&signers, message).unwrap();
 | 
			
		||||
 | 
			
		||||
        let account = get_account_at(&bank_client, &base_pubkey, 0);
 | 
			
		||||
        assert_eq!(account.lamports, lamports);
 | 
			
		||||
        let authorized = StakeState::authorized_from(&account).unwrap();
 | 
			
		||||
        assert_eq!(authorized.staker, stake_authority_pubkey);
 | 
			
		||||
        assert_eq!(authorized.withdrawer, withdraw_authority_pubkey);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_authorize_stake_accounts() {
 | 
			
		||||
        let (bank, funding_keypair, rent) = create_bank(10_000_000);
 | 
			
		||||
        let funding_pubkey = funding_keypair.pubkey();
 | 
			
		||||
        let bank_client = BankClient::new(bank);
 | 
			
		||||
        let fee_payer_keypair = create_account(&bank_client, &funding_keypair, 1);
 | 
			
		||||
        let fee_payer_pubkey = fee_payer_keypair.pubkey();
 | 
			
		||||
 | 
			
		||||
        let base_keypair = Keypair::new();
 | 
			
		||||
        let base_pubkey = base_keypair.pubkey();
 | 
			
		||||
        let lamports = rent + 1;
 | 
			
		||||
 | 
			
		||||
        let stake_authority_keypair = Keypair::new();
 | 
			
		||||
        let stake_authority_pubkey = stake_authority_keypair.pubkey();
 | 
			
		||||
        let withdraw_authority_keypair = Keypair::new();
 | 
			
		||||
        let withdraw_authority_pubkey = withdraw_authority_keypair.pubkey();
 | 
			
		||||
 | 
			
		||||
        let message = new_stake_account(
 | 
			
		||||
            &fee_payer_pubkey,
 | 
			
		||||
            &funding_pubkey,
 | 
			
		||||
            &base_pubkey,
 | 
			
		||||
            lamports,
 | 
			
		||||
            &stake_authority_pubkey,
 | 
			
		||||
            &withdraw_authority_pubkey,
 | 
			
		||||
            0,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        let signers = [&funding_keypair, &fee_payer_keypair, &base_keypair];
 | 
			
		||||
        bank_client.send_message(&signers, message).unwrap();
 | 
			
		||||
 | 
			
		||||
        let new_stake_authority_pubkey = Pubkey::new_rand();
 | 
			
		||||
        let new_withdraw_authority_pubkey = Pubkey::new_rand();
 | 
			
		||||
        let messages = authorize_stake_accounts(
 | 
			
		||||
            &fee_payer_pubkey,
 | 
			
		||||
            &base_pubkey,
 | 
			
		||||
            &stake_authority_pubkey,
 | 
			
		||||
            &withdraw_authority_pubkey,
 | 
			
		||||
            &new_stake_authority_pubkey,
 | 
			
		||||
            &new_withdraw_authority_pubkey,
 | 
			
		||||
            1,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        let signers = [
 | 
			
		||||
            &fee_payer_keypair,
 | 
			
		||||
            &stake_authority_keypair,
 | 
			
		||||
            &withdraw_authority_keypair,
 | 
			
		||||
        ];
 | 
			
		||||
        for message in messages {
 | 
			
		||||
            bank_client.send_message(&signers, message).unwrap();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let account = get_account_at(&bank_client, &base_pubkey, 0);
 | 
			
		||||
        let authorized = StakeState::authorized_from(&account).unwrap();
 | 
			
		||||
        assert_eq!(authorized.staker, new_stake_authority_pubkey);
 | 
			
		||||
        assert_eq!(authorized.withdrawer, new_withdraw_authority_pubkey);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_rebase_stake_accounts() {
 | 
			
		||||
        let (bank, funding_keypair, rent) = create_bank(10_000_000);
 | 
			
		||||
        let funding_pubkey = funding_keypair.pubkey();
 | 
			
		||||
        let bank_client = BankClient::new(bank);
 | 
			
		||||
        let fee_payer_keypair = create_account(&bank_client, &funding_keypair, 1);
 | 
			
		||||
        let fee_payer_pubkey = fee_payer_keypair.pubkey();
 | 
			
		||||
 | 
			
		||||
        let base_keypair = Keypair::new();
 | 
			
		||||
        let base_pubkey = base_keypair.pubkey();
 | 
			
		||||
        let lamports = rent + 1;
 | 
			
		||||
 | 
			
		||||
        let stake_authority_keypair = Keypair::new();
 | 
			
		||||
        let stake_authority_pubkey = stake_authority_keypair.pubkey();
 | 
			
		||||
        let withdraw_authority_keypair = Keypair::new();
 | 
			
		||||
        let withdraw_authority_pubkey = withdraw_authority_keypair.pubkey();
 | 
			
		||||
 | 
			
		||||
        let num_accounts = 1;
 | 
			
		||||
        let message = new_stake_account(
 | 
			
		||||
            &fee_payer_pubkey,
 | 
			
		||||
            &funding_pubkey,
 | 
			
		||||
            &base_pubkey,
 | 
			
		||||
            lamports,
 | 
			
		||||
            &stake_authority_pubkey,
 | 
			
		||||
            &withdraw_authority_pubkey,
 | 
			
		||||
            0,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        let signers = [&funding_keypair, &fee_payer_keypair, &base_keypair];
 | 
			
		||||
        bank_client.send_message(&signers, message).unwrap();
 | 
			
		||||
 | 
			
		||||
        let new_base_keypair = Keypair::new();
 | 
			
		||||
        let new_base_pubkey = new_base_keypair.pubkey();
 | 
			
		||||
        let balances = get_balances(&bank_client, &base_pubkey, num_accounts);
 | 
			
		||||
        let messages = rebase_stake_accounts(
 | 
			
		||||
            &fee_payer_pubkey,
 | 
			
		||||
            &new_base_pubkey,
 | 
			
		||||
            &stake_authority_pubkey,
 | 
			
		||||
            &balances,
 | 
			
		||||
        );
 | 
			
		||||
        assert_eq!(messages.len(), num_accounts);
 | 
			
		||||
 | 
			
		||||
        let signers = [
 | 
			
		||||
            &fee_payer_keypair,
 | 
			
		||||
            &new_base_keypair,
 | 
			
		||||
            &stake_authority_keypair,
 | 
			
		||||
        ];
 | 
			
		||||
        for message in messages {
 | 
			
		||||
            bank_client.send_message(&signers, message).unwrap();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Ensure the new accounts are duplicates of the previous ones.
 | 
			
		||||
        let account = get_account_at(&bank_client, &new_base_pubkey, 0);
 | 
			
		||||
        let authorized = StakeState::authorized_from(&account).unwrap();
 | 
			
		||||
        assert_eq!(authorized.staker, stake_authority_pubkey);
 | 
			
		||||
        assert_eq!(authorized.withdrawer, withdraw_authority_pubkey);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_move_stake_accounts() {
 | 
			
		||||
        let (bank, funding_keypair, rent) = create_bank(10_000_000);
 | 
			
		||||
        let funding_pubkey = funding_keypair.pubkey();
 | 
			
		||||
        let bank_client = BankClient::new(bank);
 | 
			
		||||
        let fee_payer_keypair = create_account(&bank_client, &funding_keypair, 1);
 | 
			
		||||
        let fee_payer_pubkey = fee_payer_keypair.pubkey();
 | 
			
		||||
 | 
			
		||||
        let base_keypair = Keypair::new();
 | 
			
		||||
        let base_pubkey = base_keypair.pubkey();
 | 
			
		||||
        let lamports = rent + 1;
 | 
			
		||||
 | 
			
		||||
        let stake_authority_keypair = Keypair::new();
 | 
			
		||||
        let stake_authority_pubkey = stake_authority_keypair.pubkey();
 | 
			
		||||
        let withdraw_authority_keypair = Keypair::new();
 | 
			
		||||
        let withdraw_authority_pubkey = withdraw_authority_keypair.pubkey();
 | 
			
		||||
 | 
			
		||||
        let num_accounts = 1;
 | 
			
		||||
        let message = new_stake_account(
 | 
			
		||||
            &fee_payer_pubkey,
 | 
			
		||||
            &funding_pubkey,
 | 
			
		||||
            &base_pubkey,
 | 
			
		||||
            lamports,
 | 
			
		||||
            &stake_authority_pubkey,
 | 
			
		||||
            &withdraw_authority_pubkey,
 | 
			
		||||
            0,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        let signers = [&funding_keypair, &fee_payer_keypair, &base_keypair];
 | 
			
		||||
        bank_client.send_message(&signers, message).unwrap();
 | 
			
		||||
 | 
			
		||||
        let new_base_keypair = Keypair::new();
 | 
			
		||||
        let new_base_pubkey = new_base_keypair.pubkey();
 | 
			
		||||
        let new_stake_authority_pubkey = Pubkey::new_rand();
 | 
			
		||||
        let new_withdraw_authority_pubkey = Pubkey::new_rand();
 | 
			
		||||
        let balances = get_balances(&bank_client, &base_pubkey, num_accounts);
 | 
			
		||||
        let messages = move_stake_accounts(
 | 
			
		||||
            &fee_payer_pubkey,
 | 
			
		||||
            &new_base_pubkey,
 | 
			
		||||
            &stake_authority_pubkey,
 | 
			
		||||
            &withdraw_authority_pubkey,
 | 
			
		||||
            &new_stake_authority_pubkey,
 | 
			
		||||
            &new_withdraw_authority_pubkey,
 | 
			
		||||
            &balances,
 | 
			
		||||
        );
 | 
			
		||||
        assert_eq!(messages.len(), num_accounts);
 | 
			
		||||
 | 
			
		||||
        let signers = [
 | 
			
		||||
            &fee_payer_keypair,
 | 
			
		||||
            &new_base_keypair,
 | 
			
		||||
            &stake_authority_keypair,
 | 
			
		||||
            &withdraw_authority_keypair,
 | 
			
		||||
        ];
 | 
			
		||||
        for message in messages {
 | 
			
		||||
            bank_client.send_message(&signers, message).unwrap();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Ensure the new accounts have the new authorities.
 | 
			
		||||
        let account = get_account_at(&bank_client, &new_base_pubkey, 0);
 | 
			
		||||
        let authorized = StakeState::authorized_from(&account).unwrap();
 | 
			
		||||
        assert_eq!(authorized.staker, new_stake_authority_pubkey);
 | 
			
		||||
        assert_eq!(authorized.withdrawer, new_withdraw_authority_pubkey);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user