clone_solana_system_interface/
error.rs

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