solana_stake_interface/
error.rs1use {
2 num_traits::{FromPrimitive, ToPrimitive},
3 solana_program_error::ProgramError,
4};
5
6#[cfg_attr(test, derive(strum_macros::FromRepr, strum_macros::EnumIter))]
8#[cfg_attr(
9 feature = "serde",
10 derive(serde_derive::Deserialize, serde_derive::Serialize)
11)]
12#[derive(Clone, Debug, PartialEq, Eq)]
13pub enum StakeError {
14 NoCreditsToRedeem,
17
18 LockupInForce,
20
21 AlreadyDeactivated,
23
24 TooSoonToRedelegate,
26
27 InsufficientStake,
29
30 MergeTransientStake,
33
34 MergeMismatch,
36
37 CustodianMissing,
39
40 CustodianSignatureMissing,
42
43 InsufficientReferenceVotes,
45
46 VoteAddressMismatch,
49
50 MinimumDelinquentEpochsForDeactivationNotMet,
53
54 InsufficientDelegation,
56
57 RedelegateTransientOrInactiveStake,
59
60 RedelegateToSameVoteAccount,
62
63 RedelegatedStakeMustFullyActivateBeforeDeactivationIsPermitted,
66
67 EpochRewardsActive,
69}
70
71impl From<StakeError> for ProgramError {
72 fn from(e: StakeError) -> Self {
73 ProgramError::Custom(e as u32)
74 }
75}
76
77impl FromPrimitive for StakeError {
78 #[inline]
79 fn from_i64(n: i64) -> Option<Self> {
80 if n == Self::NoCreditsToRedeem as i64 {
81 Some(Self::NoCreditsToRedeem)
82 } else if n == Self::LockupInForce as i64 {
83 Some(Self::LockupInForce)
84 } else if n == Self::AlreadyDeactivated as i64 {
85 Some(Self::AlreadyDeactivated)
86 } else if n == Self::TooSoonToRedelegate as i64 {
87 Some(Self::TooSoonToRedelegate)
88 } else if n == Self::InsufficientStake as i64 {
89 Some(Self::InsufficientStake)
90 } else if n == Self::MergeTransientStake as i64 {
91 Some(Self::MergeTransientStake)
92 } else if n == Self::MergeMismatch as i64 {
93 Some(Self::MergeMismatch)
94 } else if n == Self::CustodianMissing as i64 {
95 Some(Self::CustodianMissing)
96 } else if n == Self::CustodianSignatureMissing as i64 {
97 Some(Self::CustodianSignatureMissing)
98 } else if n == Self::InsufficientReferenceVotes as i64 {
99 Some(Self::InsufficientReferenceVotes)
100 } else if n == Self::VoteAddressMismatch as i64 {
101 Some(Self::VoteAddressMismatch)
102 } else if n == Self::MinimumDelinquentEpochsForDeactivationNotMet as i64 {
103 Some(Self::MinimumDelinquentEpochsForDeactivationNotMet)
104 } else if n == Self::InsufficientDelegation as i64 {
105 Some(Self::InsufficientDelegation)
106 } else if n == Self::RedelegateTransientOrInactiveStake as i64 {
107 Some(Self::RedelegateTransientOrInactiveStake)
108 } else if n == Self::RedelegateToSameVoteAccount as i64 {
109 Some(Self::RedelegateToSameVoteAccount)
110 } else if n == Self::RedelegatedStakeMustFullyActivateBeforeDeactivationIsPermitted as i64 {
111 Some(Self::RedelegatedStakeMustFullyActivateBeforeDeactivationIsPermitted)
112 } else if n == Self::EpochRewardsActive as i64 {
113 Some(Self::EpochRewardsActive)
114 } else {
115 None
116 }
117 }
118 #[inline]
119 fn from_u64(n: u64) -> Option<Self> {
120 Self::from_i64(n as i64)
121 }
122}
123
124impl ToPrimitive for StakeError {
125 #[inline]
126 fn to_i64(&self) -> Option<i64> {
127 Some(match *self {
128 Self::NoCreditsToRedeem => Self::NoCreditsToRedeem as i64,
129 Self::LockupInForce => Self::LockupInForce as i64,
130 Self::AlreadyDeactivated => Self::AlreadyDeactivated as i64,
131 Self::TooSoonToRedelegate => Self::TooSoonToRedelegate as i64,
132 Self::InsufficientStake => Self::InsufficientStake as i64,
133 Self::MergeTransientStake => Self::MergeTransientStake as i64,
134 Self::MergeMismatch => Self::MergeMismatch as i64,
135 Self::CustodianMissing => Self::CustodianMissing as i64,
136 Self::CustodianSignatureMissing => Self::CustodianSignatureMissing as i64,
137 Self::InsufficientReferenceVotes => Self::InsufficientReferenceVotes as i64,
138 Self::VoteAddressMismatch => Self::VoteAddressMismatch as i64,
139 Self::MinimumDelinquentEpochsForDeactivationNotMet => {
140 Self::MinimumDelinquentEpochsForDeactivationNotMet as i64
141 }
142 Self::InsufficientDelegation => Self::InsufficientDelegation as i64,
143 Self::RedelegateTransientOrInactiveStake => {
144 Self::RedelegateTransientOrInactiveStake as i64
145 }
146 Self::RedelegateToSameVoteAccount => Self::RedelegateToSameVoteAccount as i64,
147 Self::RedelegatedStakeMustFullyActivateBeforeDeactivationIsPermitted => {
148 Self::RedelegatedStakeMustFullyActivateBeforeDeactivationIsPermitted as i64
149 }
150 Self::EpochRewardsActive => Self::EpochRewardsActive as i64,
151 })
152 }
153 #[inline]
154 fn to_u64(&self) -> Option<u64> {
155 self.to_i64().map(|x| x as u64)
156 }
157}
158
159impl core::error::Error for StakeError {}
160
161impl core::fmt::Display for StakeError {
162 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
163 match self {
164 StakeError::NoCreditsToRedeem => f.write_str("not enough credits to redeem"),
165 StakeError::LockupInForce => f.write_str("lockup has not yet expired"),
166 StakeError::AlreadyDeactivated => f.write_str("stake already deactivated"),
167 StakeError::TooSoonToRedelegate => f.write_str("one re-delegation permitted per epoch"),
168 StakeError::InsufficientStake => f.write_str("split amount is more than is staked"),
169 StakeError::MergeTransientStake => {
170 f.write_str("stake account with transient stake cannot be merged")
171 }
172 StakeError::MergeMismatch => f.write_str(
173 "stake account merge failed due to different authority, lockups or state",
174 ),
175 StakeError::CustodianMissing => f.write_str("custodian address not present"),
176 StakeError::CustodianSignatureMissing => f.write_str("custodian signature not present"),
177 StakeError::InsufficientReferenceVotes => {
178 f.write_str("insufficient voting activity in the reference vote account")
179 }
180 StakeError::VoteAddressMismatch => {
181 f.write_str("stake account is not delegated to the provided vote account")
182 }
183 StakeError::MinimumDelinquentEpochsForDeactivationNotMet => f.write_str(
184 "stake account has not been delinquent for the minimum epochs required for \
185 deactivation",
186 ),
187 StakeError::InsufficientDelegation => {
188 f.write_str("delegation amount is less than the minimum")
189 }
190 StakeError::RedelegateTransientOrInactiveStake => {
191 f.write_str("stake account with transient or inactive stake cannot be redelegated")
192 }
193 StakeError::RedelegateToSameVoteAccount => {
194 f.write_str("stake redelegation to the same vote account is not permitted")
195 }
196 StakeError::RedelegatedStakeMustFullyActivateBeforeDeactivationIsPermitted => {
197 f.write_str("redelegated stake must be fully activated before deactivation")
198 }
199 StakeError::EpochRewardsActive => f.write_str(
200 "stake action is not permitted while the epoch rewards period is active",
201 ),
202 }
203 }
204}
205
206#[cfg(test)]
207mod tests {
208 use {super::StakeError, num_traits::FromPrimitive, strum::IntoEnumIterator};
209
210 #[test]
211 fn test_stake_error_from_primitive_exhaustive() {
212 for variant in StakeError::iter() {
213 let variant_i64 = variant.clone() as i64;
214 assert_eq!(
215 StakeError::from_repr(variant_i64 as usize),
216 StakeError::from_i64(variant_i64)
217 );
218 }
219 }
220}