solana_system_interface/
error.rs

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