solana_system_interface/
error.rs

1use num_traits::{FromPrimitive, ToPrimitive};
2use solana_decode_error::DecodeError;
3
4// Use strum when testing to ensure our FromPrimitive
5// impl is exhaustive
6#[cfg_attr(test, derive(strum_macros::FromRepr, strum_macros::EnumIter))]
7#[cfg_attr(
8    feature = "serde",
9    derive(serde_derive::Deserialize, serde_derive::Serialize)
10)]
11#[derive(Clone, Debug, PartialEq, Eq)]
12pub enum SystemError {
13    /// An account with the same address already exists.
14    AccountAlreadyInUse,
15    /// Account does not have enough SOL to perform the operation.
16    ResultWithNegativeLamports,
17    /// Cannot assign account to this program id.
18    InvalidProgramId,
19    /// Cannot allocate account data of this length.
20    InvalidAccountDataLength,
21    /// Length of requested seed is too long.
22    MaxSeedLengthExceeded,
23    /// Provided address does not match addressed derived from seed.
24    AddressWithSeedMismatch,
25    /// Advancing stored nonce requires a populated RecentBlockhashes sysvar.
26    NonceNoRecentBlockhashes,
27    /// Stored nonce is still in recent_blockhashes.
28    NonceBlockhashNotExpired,
29    /// Specified nonce does not match stored nonce.
30    NonceUnexpectedBlockhashValue,
31}
32
33impl FromPrimitive for SystemError {
34    #[inline]
35    fn from_i64(n: i64) -> Option<Self> {
36        if n == Self::AccountAlreadyInUse as i64 {
37            Some(Self::AccountAlreadyInUse)
38        } else if n == Self::ResultWithNegativeLamports as i64 {
39            Some(Self::ResultWithNegativeLamports)
40        } else if n == Self::InvalidProgramId as i64 {
41            Some(Self::InvalidProgramId)
42        } else if n == Self::InvalidAccountDataLength as i64 {
43            Some(Self::InvalidAccountDataLength)
44        } else if n == Self::MaxSeedLengthExceeded as i64 {
45            Some(Self::MaxSeedLengthExceeded)
46        } else if n == Self::AddressWithSeedMismatch as i64 {
47            Some(Self::AddressWithSeedMismatch)
48        } else if n == Self::NonceNoRecentBlockhashes as i64 {
49            Some(Self::NonceNoRecentBlockhashes)
50        } else if n == Self::NonceBlockhashNotExpired as i64 {
51            Some(Self::NonceBlockhashNotExpired)
52        } else if n == Self::NonceUnexpectedBlockhashValue as i64 {
53            Some(Self::NonceUnexpectedBlockhashValue)
54        } else {
55            None
56        }
57    }
58    #[inline]
59    fn from_u64(n: u64) -> Option<Self> {
60        Self::from_i64(n as i64)
61    }
62}
63
64impl ToPrimitive for SystemError {
65    #[inline]
66    fn to_i64(&self) -> Option<i64> {
67        Some(match *self {
68            Self::AccountAlreadyInUse => Self::AccountAlreadyInUse as i64,
69            Self::ResultWithNegativeLamports => Self::ResultWithNegativeLamports as i64,
70            Self::InvalidProgramId => Self::InvalidProgramId as i64,
71            Self::InvalidAccountDataLength => Self::InvalidAccountDataLength as i64,
72            Self::MaxSeedLengthExceeded => Self::MaxSeedLengthExceeded as i64,
73            Self::AddressWithSeedMismatch => Self::AddressWithSeedMismatch as i64,
74            Self::NonceNoRecentBlockhashes => Self::NonceNoRecentBlockhashes as i64,
75            Self::NonceBlockhashNotExpired => Self::NonceBlockhashNotExpired as i64,
76            Self::NonceUnexpectedBlockhashValue => Self::NonceUnexpectedBlockhashValue as i64,
77        })
78    }
79    #[inline]
80    fn to_u64(&self) -> Option<u64> {
81        self.to_i64().map(|x| x as u64)
82    }
83}
84
85impl std::error::Error for SystemError {}
86
87impl core::fmt::Display for SystemError {
88    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
89        match self {
90            SystemError::AccountAlreadyInUse => {
91                f.write_str("an account with the same address already exists")
92            }
93            SystemError::ResultWithNegativeLamports => {
94                f.write_str("account does not have enough SOL to perform the operation")
95            }
96            SystemError::InvalidProgramId => {
97                f.write_str("cannot assign account to this program id")
98            }
99            SystemError::InvalidAccountDataLength => {
100                f.write_str("cannot allocate account data of this length")
101            }
102            SystemError::MaxSeedLengthExceeded => {
103                f.write_str("length of requested seed is too long")
104            }
105            SystemError::AddressWithSeedMismatch => {
106                f.write_str("provided address does not match addressed derived from seed")
107            }
108            SystemError::NonceNoRecentBlockhashes => {
109                f.write_str("advancing stored nonce requires a populated RecentBlockhashes sysvar")
110            }
111            SystemError::NonceBlockhashNotExpired => {
112                f.write_str("stored nonce is still in recent_blockhashes")
113            }
114            SystemError::NonceUnexpectedBlockhashValue => {
115                f.write_str("specified nonce does not match stored nonce")
116            }
117        }
118    }
119}
120
121impl<T> DecodeError<T> for SystemError {
122    fn type_of() -> &'static str {
123        "SystemError"
124    }
125}
126
127impl From<u64> for SystemError {
128    fn from(error: u64) -> Self {
129        match error {
130            0 => SystemError::AccountAlreadyInUse,
131            1 => SystemError::ResultWithNegativeLamports,
132            2 => SystemError::InvalidProgramId,
133            3 => SystemError::InvalidAccountDataLength,
134            4 => SystemError::MaxSeedLengthExceeded,
135            5 => SystemError::AddressWithSeedMismatch,
136            6 => SystemError::NonceNoRecentBlockhashes,
137            7 => SystemError::NonceBlockhashNotExpired,
138            8 => SystemError::NonceUnexpectedBlockhashValue,
139            _ => panic!("Unsupported SystemError"),
140        }
141    }
142}
143
144#[cfg(test)]
145mod tests {
146    use {super::SystemError, num_traits::FromPrimitive, strum::IntoEnumIterator};
147
148    #[test]
149    fn test_system_error_from_primitive_exhaustive() {
150        for variant in SystemError::iter() {
151            let variant_i64 = variant.clone() as i64;
152            assert_eq!(
153                SystemError::from_repr(variant_i64 as usize),
154                SystemError::from_i64(variant_i64)
155            );
156            assert_eq!(SystemError::from(variant_i64 as u64), variant);
157        }
158    }
159}