1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
use crate::cli::{CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult};
use clap::{App, Arg, ArgMatches, SubCommand};
use solana_clap_utils::{
    input_parsers::{pubkeys_of, value_of},
    input_validators::is_valid_pubkey,
    keypair::*,
};
use solana_cli_output::{
    CliEpochRewardshMetadata, CliInflation, CliKeyedEpochReward, CliKeyedEpochRewards,
};
use solana_client::rpc_client::RpcClient;
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
use solana_sdk::{clock::Epoch, pubkey::Pubkey};
use std::sync::Arc;

#[derive(Debug, PartialEq)]
pub enum InflationCliCommand {
    Show,
    Rewards(Vec<Pubkey>, Option<Epoch>),
}

pub trait InflationSubCommands {
    fn inflation_subcommands(self) -> Self;
}

impl InflationSubCommands for App<'_, '_> {
    fn inflation_subcommands(self) -> Self {
        self.subcommand(
            SubCommand::with_name("inflation")
                .about("Show inflation information")
                .subcommand(
                    SubCommand::with_name("rewards")
                        .about("Show inflation rewards for a set of addresses")
                        .arg(pubkey!(
                            Arg::with_name("addresses")
                                .value_name("ADDRESS")
                                .index(1)
                                .multiple(true)
                                .required(true),
                            "Address of account to query for rewards. "
                        ))
                        .arg(
                            Arg::with_name("rewards_epoch")
                                .long("rewards-epoch")
                                .takes_value(true)
                                .value_name("EPOCH")
                                .help("Display rewards for specific epoch [default: latest epoch]"),
                        ),
                ),
        )
    }
}

pub fn parse_inflation_subcommand(
    matches: &ArgMatches<'_>,
    _default_signer: &DefaultSigner,
    _wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<CliCommandInfo, CliError> {
    let command = match matches.subcommand() {
        ("rewards", Some(matches)) => {
            let addresses = pubkeys_of(matches, "addresses").unwrap();
            let rewards_epoch = value_of(matches, "rewards_epoch");
            InflationCliCommand::Rewards(addresses, rewards_epoch)
        }
        _ => InflationCliCommand::Show,
    };
    Ok(CliCommandInfo {
        command: CliCommand::Inflation(command),
        signers: vec![],
    })
}

pub fn process_inflation_subcommand(
    rpc_client: &RpcClient,
    config: &CliConfig,
    inflation_subcommand: &InflationCliCommand,
) -> ProcessResult {
    match inflation_subcommand {
        InflationCliCommand::Show => process_show(rpc_client, config),
        InflationCliCommand::Rewards(ref addresses, rewards_epoch) => {
            process_rewards(rpc_client, config, addresses, *rewards_epoch)
        }
    }
}

fn process_show(rpc_client: &RpcClient, config: &CliConfig) -> ProcessResult {
    let governor = rpc_client.get_inflation_governor()?;
    let current_rate = rpc_client.get_inflation_rate()?;

    let inflation = CliInflation {
        governor,
        current_rate,
    };

    Ok(config.output_format.formatted_string(&inflation))
}

fn process_rewards(
    rpc_client: &RpcClient,
    config: &CliConfig,
    addresses: &[Pubkey],
    rewards_epoch: Option<Epoch>,
) -> ProcessResult {
    let rewards = rpc_client
        .get_inflation_reward(&addresses, rewards_epoch)
        .map_err(|err| {
            if let Some(epoch) = rewards_epoch {
                format!("Rewards not available for epoch {}", epoch)
            } else {
                format!("Rewards not available {}", err)
            }
        })?;
    let epoch_schedule = rpc_client.get_epoch_schedule()?;

    let mut epoch_rewards: Vec<CliKeyedEpochReward> = vec![];
    let epoch_metadata = if let Some(Some(first_reward)) = rewards.iter().find(|&v| v.is_some()) {
        let (epoch_start_time, epoch_end_time) =
            crate::stake::get_epoch_boundary_timestamps(rpc_client, first_reward, &epoch_schedule)?;
        for (reward, address) in rewards.iter().zip(addresses) {
            let cli_reward = reward.as_ref().and_then(|reward| {
                crate::stake::make_cli_reward(reward, epoch_start_time, epoch_end_time)
            });
            epoch_rewards.push(CliKeyedEpochReward {
                address: address.to_string(),
                reward: cli_reward,
            });
        }
        let block_time = rpc_client.get_block_time(first_reward.effective_slot)?;
        Some(CliEpochRewardshMetadata {
            epoch: first_reward.epoch,
            effective_slot: first_reward.effective_slot,
            block_time,
        })
    } else {
        None
    };
    let cli_rewards = CliKeyedEpochRewards {
        epoch_metadata,
        rewards: epoch_rewards,
    };
    Ok(config.output_format.formatted_string(&cli_rewards))
}