solana_stake_interface/
error.rs1#[cfg(feature = "codama")]
2use codama_macros::CodamaErrors;
3use {
4 num_traits::{FromPrimitive, ToPrimitive},
5 solana_program_error::ProgramError,
6};
7
8#[cfg_attr(feature = "codama", derive(CodamaErrors))]
10#[derive(Clone, Debug, PartialEq, Eq)]
11#[cfg_attr(test, derive(strum_macros::FromRepr, strum_macros::EnumIter))]
12#[cfg_attr(
13 feature = "serde",
14 derive(serde_derive::Deserialize, serde_derive::Serialize)
15)]
16#[cfg_attr(feature = "wincode", derive(wincode::SchemaRead, wincode::SchemaWrite))]
17pub enum StakeError {
18 #[cfg_attr(
21 feature = "codama",
22 codama(error(message = "Not enough credits to redeem"))
23 )]
24 NoCreditsToRedeem,
25
26 #[cfg_attr(
29 feature = "codama",
30 codama(error(message = "Lockup has not yet expired"))
31 )]
32 LockupInForce,
33
34 #[cfg_attr(
37 feature = "codama",
38 codama(error(message = "Stake already deactivated"))
39 )]
40 AlreadyDeactivated,
41
42 #[cfg_attr(
45 feature = "codama",
46 codama(error(message = "One re-delegation permitted per epoch"))
47 )]
48 TooSoonToRedelegate,
49
50 #[cfg_attr(
53 feature = "codama",
54 codama(error(message = "Split amount is more than is staked"))
55 )]
56 InsufficientStake,
57
58 #[cfg_attr(
61 feature = "codama",
62 codama(error(message = "Stake account with transient stake cannot be merged"))
63 )]
64 MergeTransientStake,
65
66 #[cfg_attr(
69 feature = "codama",
70 codama(error(
71 message = "Stake account merge failed due to different authority, lockups or state"
72 ))
73 )]
74 MergeMismatch,
75
76 #[cfg_attr(
79 feature = "codama",
80 codama(error(message = "Custodian address not present"))
81 )]
82 CustodianMissing,
83
84 #[cfg_attr(
87 feature = "codama",
88 codama(error(message = "Custodian signature not present"))
89 )]
90 CustodianSignatureMissing,
91
92 #[cfg_attr(
95 feature = "codama",
96 codama(error(message = "Insufficient voting activity in the reference vote account"))
97 )]
98 InsufficientReferenceVotes,
99
100 #[cfg_attr(
103 feature = "codama",
104 codama(error(message = "Stake account is not delegated to the provided vote account"))
105 )]
106 VoteAddressMismatch,
107
108 #[cfg_attr(
112 feature = "codama",
113 codama(error(
114 message = "Stake account has not been delinquent for the minimum epochs required for deactivation"
115 ))
116 )]
117 MinimumDelinquentEpochsForDeactivationNotMet,
118
119 #[cfg_attr(
122 feature = "codama",
123 codama(error(message = "Delegation amount is less than the minimum"))
124 )]
125 InsufficientDelegation,
126
127 #[cfg_attr(
130 feature = "codama",
131 codama(error(
132 message = "Stake account with transient or inactive stake cannot be redelegated"
133 ))
134 )]
135 RedelegateTransientOrInactiveStake,
136
137 #[cfg_attr(
140 feature = "codama",
141 codama(error(message = "Stake redelegation to the same vote account is not permitted"))
142 )]
143 RedelegateToSameVoteAccount,
144
145 #[cfg_attr(
148 feature = "codama",
149 codama(error(message = "Redelegated stake must be fully activated before deactivation"))
150 )]
151 RedelegatedStakeMustFullyActivateBeforeDeactivationIsPermitted,
152
153 #[cfg_attr(
156 feature = "codama",
157 codama(error(
158 message = "Stake action is not permitted while the epoch rewards period is active"
159 ))
160 )]
161 EpochRewardsActive,
162}
163
164impl From<StakeError> for ProgramError {
165 fn from(e: StakeError) -> Self {
166 ProgramError::Custom(e as u32)
167 }
168}
169
170impl FromPrimitive for StakeError {
171 #[inline]
172 fn from_i64(n: i64) -> Option<Self> {
173 if n == Self::NoCreditsToRedeem as i64 {
174 Some(Self::NoCreditsToRedeem)
175 } else if n == Self::LockupInForce as i64 {
176 Some(Self::LockupInForce)
177 } else if n == Self::AlreadyDeactivated as i64 {
178 Some(Self::AlreadyDeactivated)
179 } else if n == Self::TooSoonToRedelegate as i64 {
180 Some(Self::TooSoonToRedelegate)
181 } else if n == Self::InsufficientStake as i64 {
182 Some(Self::InsufficientStake)
183 } else if n == Self::MergeTransientStake as i64 {
184 Some(Self::MergeTransientStake)
185 } else if n == Self::MergeMismatch as i64 {
186 Some(Self::MergeMismatch)
187 } else if n == Self::CustodianMissing as i64 {
188 Some(Self::CustodianMissing)
189 } else if n == Self::CustodianSignatureMissing as i64 {
190 Some(Self::CustodianSignatureMissing)
191 } else if n == Self::InsufficientReferenceVotes as i64 {
192 Some(Self::InsufficientReferenceVotes)
193 } else if n == Self::VoteAddressMismatch as i64 {
194 Some(Self::VoteAddressMismatch)
195 } else if n == Self::MinimumDelinquentEpochsForDeactivationNotMet as i64 {
196 Some(Self::MinimumDelinquentEpochsForDeactivationNotMet)
197 } else if n == Self::InsufficientDelegation as i64 {
198 Some(Self::InsufficientDelegation)
199 } else if n == Self::RedelegateTransientOrInactiveStake as i64 {
200 Some(Self::RedelegateTransientOrInactiveStake)
201 } else if n == Self::RedelegateToSameVoteAccount as i64 {
202 Some(Self::RedelegateToSameVoteAccount)
203 } else if n == Self::RedelegatedStakeMustFullyActivateBeforeDeactivationIsPermitted as i64 {
204 Some(Self::RedelegatedStakeMustFullyActivateBeforeDeactivationIsPermitted)
205 } else if n == Self::EpochRewardsActive as i64 {
206 Some(Self::EpochRewardsActive)
207 } else {
208 None
209 }
210 }
211 #[inline]
212 fn from_u64(n: u64) -> Option<Self> {
213 Self::from_i64(n as i64)
214 }
215}
216
217impl ToPrimitive for StakeError {
218 #[inline]
219 fn to_i64(&self) -> Option<i64> {
220 Some(match *self {
221 Self::NoCreditsToRedeem => Self::NoCreditsToRedeem as i64,
222 Self::LockupInForce => Self::LockupInForce as i64,
223 Self::AlreadyDeactivated => Self::AlreadyDeactivated as i64,
224 Self::TooSoonToRedelegate => Self::TooSoonToRedelegate as i64,
225 Self::InsufficientStake => Self::InsufficientStake as i64,
226 Self::MergeTransientStake => Self::MergeTransientStake as i64,
227 Self::MergeMismatch => Self::MergeMismatch as i64,
228 Self::CustodianMissing => Self::CustodianMissing as i64,
229 Self::CustodianSignatureMissing => Self::CustodianSignatureMissing as i64,
230 Self::InsufficientReferenceVotes => Self::InsufficientReferenceVotes as i64,
231 Self::VoteAddressMismatch => Self::VoteAddressMismatch as i64,
232 Self::MinimumDelinquentEpochsForDeactivationNotMet => {
233 Self::MinimumDelinquentEpochsForDeactivationNotMet as i64
234 }
235 Self::InsufficientDelegation => Self::InsufficientDelegation as i64,
236 Self::RedelegateTransientOrInactiveStake => {
237 Self::RedelegateTransientOrInactiveStake as i64
238 }
239 Self::RedelegateToSameVoteAccount => Self::RedelegateToSameVoteAccount as i64,
240 Self::RedelegatedStakeMustFullyActivateBeforeDeactivationIsPermitted => {
241 Self::RedelegatedStakeMustFullyActivateBeforeDeactivationIsPermitted as i64
242 }
243 Self::EpochRewardsActive => Self::EpochRewardsActive as i64,
244 })
245 }
246 #[inline]
247 fn to_u64(&self) -> Option<u64> {
248 self.to_i64().map(|x| x as u64)
249 }
250}
251
252impl core::error::Error for StakeError {}
253
254impl core::fmt::Display for StakeError {
255 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
256 match self {
257 StakeError::NoCreditsToRedeem => f.write_str("not enough credits to redeem"),
258 StakeError::LockupInForce => f.write_str("lockup has not yet expired"),
259 StakeError::AlreadyDeactivated => f.write_str("stake already deactivated"),
260 StakeError::TooSoonToRedelegate => f.write_str("one re-delegation permitted per epoch"),
261 StakeError::InsufficientStake => f.write_str("split amount is more than is staked"),
262 StakeError::MergeTransientStake => {
263 f.write_str("stake account with transient stake cannot be merged")
264 }
265 StakeError::MergeMismatch => f.write_str(
266 "stake account merge failed due to different authority, lockups or state",
267 ),
268 StakeError::CustodianMissing => f.write_str("custodian address not present"),
269 StakeError::CustodianSignatureMissing => f.write_str("custodian signature not present"),
270 StakeError::InsufficientReferenceVotes => {
271 f.write_str("insufficient voting activity in the reference vote account")
272 }
273 StakeError::VoteAddressMismatch => {
274 f.write_str("stake account is not delegated to the provided vote account")
275 }
276 StakeError::MinimumDelinquentEpochsForDeactivationNotMet => f.write_str(
277 "stake account has not been delinquent for the minimum epochs required for \
278 deactivation",
279 ),
280 StakeError::InsufficientDelegation => {
281 f.write_str("delegation amount is less than the minimum")
282 }
283 StakeError::RedelegateTransientOrInactiveStake => {
284 f.write_str("stake account with transient or inactive stake cannot be redelegated")
285 }
286 StakeError::RedelegateToSameVoteAccount => {
287 f.write_str("stake redelegation to the same vote account is not permitted")
288 }
289 StakeError::RedelegatedStakeMustFullyActivateBeforeDeactivationIsPermitted => {
290 f.write_str("redelegated stake must be fully activated before deactivation")
291 }
292 StakeError::EpochRewardsActive => f.write_str(
293 "stake action is not permitted while the epoch rewards period is active",
294 ),
295 }
296 }
297}
298
299#[cfg(test)]
300mod tests {
301 use {super::StakeError, num_traits::FromPrimitive, strum::IntoEnumIterator};
302
303 #[test]
304 fn test_stake_error_from_primitive_exhaustive() {
305 for variant in StakeError::iter() {
306 let variant_i64 = variant.clone() as i64;
307 assert_eq!(
308 StakeError::from_repr(variant_i64 as usize),
309 StakeError::from_i64(variant_i64)
310 );
311 }
312 }
313}