solana_rpc_get_stake_activation/
lib.rs

1use {
2    serde::{Deserialize, Serialize},
3    solana_rpc_client::rpc_client::RpcClient,
4    solana_rpc_client_api::client_error::{Error, ErrorKind, Result as ClientResult},
5    solana_sdk::{
6        account::{from_account, ReadableAccount},
7        pubkey::Pubkey,
8        stake::state::{StakeActivationStatus, StakeStateV2},
9        stake_history::StakeHistory,
10        sysvar::stake_history,
11    },
12};
13
14#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
15#[serde(rename_all = "camelCase")]
16pub enum StakeActivationState {
17    Activating,
18    Active,
19    Deactivating,
20    Inactive,
21}
22
23#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
24#[serde(rename_all = "camelCase")]
25pub struct StakeActivation {
26    state: StakeActivationState,
27    active: u64,
28    inactive: u64,
29}
30
31pub fn get_stake_activation(
32    rpc_client: &RpcClient,
33    stake: &Pubkey,
34) -> ClientResult<StakeActivation> {
35    let stake_account = rpc_client.get_account(stake)?;
36    let stake_history_account = rpc_client.get_account(&stake_history::id())?;
37    let epoch_info = rpc_client.get_epoch_info()?;
38    let stake_state = bincode::deserialize::<StakeStateV2>(&stake_account.data).map_err(|_| {
39        Error::from(ErrorKind::Custom(
40            "Invalid param: stake account not initialized".to_string(),
41        ))
42    })?;
43    let delegation = stake_state.delegation();
44    let rent_exempt_reserve = stake_state
45        .meta()
46        .ok_or_else(|| {
47            Error::from(ErrorKind::Custom(
48                "Invalid param: stake account not initialized".to_string(),
49            ))
50        })?
51        .rent_exempt_reserve;
52
53    let delegation = match delegation {
54        None => {
55            return Ok(StakeActivation {
56                state: StakeActivationState::Inactive,
57                active: 0,
58                inactive: stake_account.lamports().saturating_sub(rent_exempt_reserve),
59            })
60        }
61        Some(delegation) => delegation,
62    };
63
64    let stake_history =
65        from_account::<StakeHistory, _>(&stake_history_account).ok_or(Error::from(
66            ErrorKind::Custom("Invalid param: stake history not deserializable".to_string()),
67        ))?;
68
69    let StakeActivationStatus {
70        effective,
71        activating,
72        deactivating,
73    } = delegation.stake_activating_and_deactivating(epoch_info.epoch, &stake_history, Some(0));
74    let stake_activation_state = if deactivating > 0 {
75        StakeActivationState::Deactivating
76    } else if activating > 0 {
77        StakeActivationState::Activating
78    } else if effective > 0 {
79        StakeActivationState::Active
80    } else {
81        StakeActivationState::Inactive
82    };
83    let inactive_stake = stake_account
84        .lamports()
85        .saturating_sub(effective)
86        .saturating_sub(rent_exempt_reserve);
87    Ok(StakeActivation {
88        state: stake_activation_state,
89        active: effective,
90        inactive: inactive_stake,
91    })
92}