solana-stake-interface 4.0.0

Instructions and constructors for the Stake program
Documentation
#[cfg(feature = "codama")]
use codama_macros::CodamaErrors;
use {
    num_traits::{FromPrimitive, ToPrimitive},
    solana_program_error::ProgramError,
};

/// Reasons the Stake might have had an error.
#[cfg_attr(feature = "codama", derive(CodamaErrors))]
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(test, derive(strum_macros::FromRepr, strum_macros::EnumIter))]
#[cfg_attr(
    feature = "serde",
    derive(serde_derive::Deserialize, serde_derive::Serialize)
)]
#[cfg_attr(feature = "wincode", derive(wincode::SchemaRead, wincode::SchemaWrite))]
pub enum StakeError {
    // 0
    /// Not enough credits to redeem.
    #[cfg_attr(
        feature = "codama",
        codama(error(message = "Not enough credits to redeem"))
    )]
    NoCreditsToRedeem,

    // 1
    /// Lockup has not yet expired.
    #[cfg_attr(
        feature = "codama",
        codama(error(message = "Lockup has not yet expired"))
    )]
    LockupInForce,

    // 2
    /// Stake already deactivated.
    #[cfg_attr(
        feature = "codama",
        codama(error(message = "Stake already deactivated"))
    )]
    AlreadyDeactivated,

    // 3
    /// One re-delegation permitted per epoch.
    #[cfg_attr(
        feature = "codama",
        codama(error(message = "One re-delegation permitted per epoch"))
    )]
    TooSoonToRedelegate,

    // 4
    /// Split amount is more than is staked.
    #[cfg_attr(
        feature = "codama",
        codama(error(message = "Split amount is more than is staked"))
    )]
    InsufficientStake,

    // 5
    /// Stake account with transient stake cannot be merged.
    #[cfg_attr(
        feature = "codama",
        codama(error(message = "Stake account with transient stake cannot be merged"))
    )]
    MergeTransientStake,

    // 6
    /// Stake account merge failed due to different authority, lockups or state.
    #[cfg_attr(
        feature = "codama",
        codama(error(
            message = "Stake account merge failed due to different authority, lockups or state"
        ))
    )]
    MergeMismatch,

    // 7
    /// Custodian address not present.
    #[cfg_attr(
        feature = "codama",
        codama(error(message = "Custodian address not present"))
    )]
    CustodianMissing,

    // 8
    /// Custodian signature not present.
    #[cfg_attr(
        feature = "codama",
        codama(error(message = "Custodian signature not present"))
    )]
    CustodianSignatureMissing,

    // 9
    /// Insufficient voting activity in the reference vote account.
    #[cfg_attr(
        feature = "codama",
        codama(error(message = "Insufficient voting activity in the reference vote account"))
    )]
    InsufficientReferenceVotes,

    // 10
    /// Stake account is not delegated to the provided vote account.
    #[cfg_attr(
        feature = "codama",
        codama(error(message = "Stake account is not delegated to the provided vote account"))
    )]
    VoteAddressMismatch,

    // 11
    /// Stake account has not been delinquent for the minimum epochs required
    /// for deactivation.
    #[cfg_attr(
        feature = "codama",
        codama(error(
            message = "Stake account has not been delinquent for the minimum epochs required for deactivation"
        ))
    )]
    MinimumDelinquentEpochsForDeactivationNotMet,

    // 12
    /// Delegation amount is less than the minimum.
    #[cfg_attr(
        feature = "codama",
        codama(error(message = "Delegation amount is less than the minimum"))
    )]
    InsufficientDelegation,

    // 13
    /// Stake account with transient or inactive stake cannot be redelegated.
    #[cfg_attr(
        feature = "codama",
        codama(error(
            message = "Stake account with transient or inactive stake cannot be redelegated"
        ))
    )]
    RedelegateTransientOrInactiveStake,

    // 14
    /// Stake redelegation to the same vote account is not permitted.
    #[cfg_attr(
        feature = "codama",
        codama(error(message = "Stake redelegation to the same vote account is not permitted"))
    )]
    RedelegateToSameVoteAccount,

    // 15
    /// Redelegated stake must be fully activated before deactivation.
    #[cfg_attr(
        feature = "codama",
        codama(error(message = "Redelegated stake must be fully activated before deactivation"))
    )]
    RedelegatedStakeMustFullyActivateBeforeDeactivationIsPermitted,

    // 16
    /// Stake action is not permitted while the epoch rewards period is active.
    #[cfg_attr(
        feature = "codama",
        codama(error(
            message = "Stake action is not permitted while the epoch rewards period is active"
        ))
    )]
    EpochRewardsActive,
}

impl From<StakeError> for ProgramError {
    fn from(e: StakeError) -> Self {
        ProgramError::Custom(e as u32)
    }
}

impl FromPrimitive for StakeError {
    #[inline]
    fn from_i64(n: i64) -> Option<Self> {
        if n == Self::NoCreditsToRedeem as i64 {
            Some(Self::NoCreditsToRedeem)
        } else if n == Self::LockupInForce as i64 {
            Some(Self::LockupInForce)
        } else if n == Self::AlreadyDeactivated as i64 {
            Some(Self::AlreadyDeactivated)
        } else if n == Self::TooSoonToRedelegate as i64 {
            Some(Self::TooSoonToRedelegate)
        } else if n == Self::InsufficientStake as i64 {
            Some(Self::InsufficientStake)
        } else if n == Self::MergeTransientStake as i64 {
            Some(Self::MergeTransientStake)
        } else if n == Self::MergeMismatch as i64 {
            Some(Self::MergeMismatch)
        } else if n == Self::CustodianMissing as i64 {
            Some(Self::CustodianMissing)
        } else if n == Self::CustodianSignatureMissing as i64 {
            Some(Self::CustodianSignatureMissing)
        } else if n == Self::InsufficientReferenceVotes as i64 {
            Some(Self::InsufficientReferenceVotes)
        } else if n == Self::VoteAddressMismatch as i64 {
            Some(Self::VoteAddressMismatch)
        } else if n == Self::MinimumDelinquentEpochsForDeactivationNotMet as i64 {
            Some(Self::MinimumDelinquentEpochsForDeactivationNotMet)
        } else if n == Self::InsufficientDelegation as i64 {
            Some(Self::InsufficientDelegation)
        } else if n == Self::RedelegateTransientOrInactiveStake as i64 {
            Some(Self::RedelegateTransientOrInactiveStake)
        } else if n == Self::RedelegateToSameVoteAccount as i64 {
            Some(Self::RedelegateToSameVoteAccount)
        } else if n == Self::RedelegatedStakeMustFullyActivateBeforeDeactivationIsPermitted as i64 {
            Some(Self::RedelegatedStakeMustFullyActivateBeforeDeactivationIsPermitted)
        } else if n == Self::EpochRewardsActive as i64 {
            Some(Self::EpochRewardsActive)
        } else {
            None
        }
    }
    #[inline]
    fn from_u64(n: u64) -> Option<Self> {
        Self::from_i64(n as i64)
    }
}

impl ToPrimitive for StakeError {
    #[inline]
    fn to_i64(&self) -> Option<i64> {
        Some(match *self {
            Self::NoCreditsToRedeem => Self::NoCreditsToRedeem as i64,
            Self::LockupInForce => Self::LockupInForce as i64,
            Self::AlreadyDeactivated => Self::AlreadyDeactivated as i64,
            Self::TooSoonToRedelegate => Self::TooSoonToRedelegate as i64,
            Self::InsufficientStake => Self::InsufficientStake as i64,
            Self::MergeTransientStake => Self::MergeTransientStake as i64,
            Self::MergeMismatch => Self::MergeMismatch as i64,
            Self::CustodianMissing => Self::CustodianMissing as i64,
            Self::CustodianSignatureMissing => Self::CustodianSignatureMissing as i64,
            Self::InsufficientReferenceVotes => Self::InsufficientReferenceVotes as i64,
            Self::VoteAddressMismatch => Self::VoteAddressMismatch as i64,
            Self::MinimumDelinquentEpochsForDeactivationNotMet => {
                Self::MinimumDelinquentEpochsForDeactivationNotMet as i64
            }
            Self::InsufficientDelegation => Self::InsufficientDelegation as i64,
            Self::RedelegateTransientOrInactiveStake => {
                Self::RedelegateTransientOrInactiveStake as i64
            }
            Self::RedelegateToSameVoteAccount => Self::RedelegateToSameVoteAccount as i64,
            Self::RedelegatedStakeMustFullyActivateBeforeDeactivationIsPermitted => {
                Self::RedelegatedStakeMustFullyActivateBeforeDeactivationIsPermitted as i64
            }
            Self::EpochRewardsActive => Self::EpochRewardsActive as i64,
        })
    }
    #[inline]
    fn to_u64(&self) -> Option<u64> {
        self.to_i64().map(|x| x as u64)
    }
}

impl core::error::Error for StakeError {}

impl core::fmt::Display for StakeError {
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
        match self {
            StakeError::NoCreditsToRedeem => f.write_str("not enough credits to redeem"),
            StakeError::LockupInForce => f.write_str("lockup has not yet expired"),
            StakeError::AlreadyDeactivated => f.write_str("stake already deactivated"),
            StakeError::TooSoonToRedelegate => f.write_str("one re-delegation permitted per epoch"),
            StakeError::InsufficientStake => f.write_str("split amount is more than is staked"),
            StakeError::MergeTransientStake => {
                f.write_str("stake account with transient stake cannot be merged")
            }
            StakeError::MergeMismatch => f.write_str(
                "stake account merge failed due to different authority, lockups or state",
            ),
            StakeError::CustodianMissing => f.write_str("custodian address not present"),
            StakeError::CustodianSignatureMissing => f.write_str("custodian signature not present"),
            StakeError::InsufficientReferenceVotes => {
                f.write_str("insufficient voting activity in the reference vote account")
            }
            StakeError::VoteAddressMismatch => {
                f.write_str("stake account is not delegated to the provided vote account")
            }
            StakeError::MinimumDelinquentEpochsForDeactivationNotMet => f.write_str(
                "stake account has not been delinquent for the minimum epochs required for \
                     deactivation",
            ),
            StakeError::InsufficientDelegation => {
                f.write_str("delegation amount is less than the minimum")
            }
            StakeError::RedelegateTransientOrInactiveStake => {
                f.write_str("stake account with transient or inactive stake cannot be redelegated")
            }
            StakeError::RedelegateToSameVoteAccount => {
                f.write_str("stake redelegation to the same vote account is not permitted")
            }
            StakeError::RedelegatedStakeMustFullyActivateBeforeDeactivationIsPermitted => {
                f.write_str("redelegated stake must be fully activated before deactivation")
            }
            StakeError::EpochRewardsActive => f.write_str(
                "stake action is not permitted while the epoch rewards period is active",
            ),
        }
    }
}

#[cfg(test)]
mod tests {
    use {super::StakeError, num_traits::FromPrimitive, strum::IntoEnumIterator};

    #[test]
    fn test_stake_error_from_primitive_exhaustive() {
        for variant in StakeError::iter() {
            let variant_i64 = variant.clone() as i64;
            assert_eq!(
                StakeError::from_repr(variant_i64 as usize),
                StakeError::from_i64(variant_i64)
            );
        }
    }
}