1use bincode::ErrorKind;
2use pyo3::{create_exception, exceptions::PyException, prelude::*, pyclass::CompareOp};
3#[cfg(feature = "banks-client")]
4use solana_banks_client::BanksClientError as BanksClientErrorOriginal;
5use solana_sdk::{
6 commitment_config::ParseCommitmentLevelError as ParseCommitmentLevelErrorOriginal,
7 hash::ParseHashError as ParseHashErrorOriginal,
8 pubkey::Pubkey as PubkeyOriginal,
9 pubkey::PubkeyError as PubkeyErrorOriginal,
10 sanitize::SanitizeError as SanitizeErrorOriginal,
11 signature::Signature as SignatureOriginal,
12 signer::{Signer as SignerTrait, SignerError as SignerErrorOriginal},
13 transaction::TransactionError as TransactionErrorOriginal,
14};
15use solders_traits_core::richcmp_type_error;
16pub struct PyErrWrapper(pub PyErr);
17
18impl From<PyErrWrapper> for PyErr {
19 fn from(e: PyErrWrapper) -> Self {
20 e.0
21 }
22}
23
24create_exception!(
25 solders,
26 ParseCommitmentLevelError,
27 PyException,
28 "Raised when an error is encountered converting a string into a ``CommitmentConfig``."
29);
30
31create_exception!(
32 solders,
33 SerdeJSONError,
34 PyException,
35 "Raised when an error is encountered during JSON (de)serialization."
36);
37
38impl From<serde_json::Error> for PyErrWrapper {
39 fn from(e: serde_json::Error) -> Self {
40 Self(SerdeJSONError::new_err(e.to_string()))
41 }
42}
43
44impl From<ParseCommitmentLevelErrorOriginal> for PyErrWrapper {
45 fn from(e: ParseCommitmentLevelErrorOriginal) -> Self {
46 Self(ParseCommitmentLevelError::new_err(e.to_string()))
47 }
48}
49
50create_exception!(
51 solders,
52 ParseHashError,
53 PyException,
54 "Raised when an error is encountered converting a string into a ``Hash``."
55);
56
57impl From<ParseHashErrorOriginal> for PyErrWrapper {
58 fn from(e: ParseHashErrorOriginal) -> Self {
59 Self(ParseHashError::new_err(e.to_string()))
60 }
61}
62
63create_exception!(
64 solders,
65 SignerError,
66 PyException,
67 "Raised when an error is encountered during transaction signing."
68);
69
70impl From<SignerErrorOriginal> for PyErrWrapper {
71 fn from(e: SignerErrorOriginal) -> Self {
72 Self(SignerError::new_err(e.to_string()))
73 }
74}
75
76create_exception!(
77 solders,
78 TransactionError,
79 PyException,
80 "Umbrella error for the ``Transaction`` object."
81);
82
83impl From<TransactionErrorOriginal> for PyErrWrapper {
84 fn from(e: TransactionErrorOriginal) -> Self {
85 Self(TransactionError::new_err(e.to_string()))
86 }
87}
88
89create_exception!(
90 solders,
91 SanitizeError,
92 PyException,
93 "Raised when an error is encountered during transaction sanitization."
94);
95
96impl From<SanitizeErrorOriginal> for PyErrWrapper {
97 fn from(e: SanitizeErrorOriginal) -> Self {
98 Self(SanitizeError::new_err(e.to_string()))
99 }
100}
101
102pub fn to_py_err<T: Into<PyErrWrapper>>(e: T) -> PyErr {
103 let wrapped: PyErrWrapper = e.into();
104 wrapped.into()
105}
106
107pub fn handle_py_err<T: Into<P>, E: ToString + Into<PyErrWrapper>, P>(
108 res: Result<T, E>,
109) -> PyResult<P> {
110 res.map_or_else(|e| Err(to_py_err(e)), |v| Ok(v.into()))
111}
112
113create_exception!(
114 solders,
115 BincodeError,
116 PyException,
117 "Raised when the Rust bincode library returns an error during (de)serialization."
118);
119
120create_exception!(
121 solders,
122 CborError,
123 PyException,
124 "Raised when the Rust cbor library returns an error during (de)serialization."
125);
126
127create_exception!(
128 solders,
129 PubkeyError,
130 PyException,
131 "Umbrella error for the ``Pubkey`` object."
132);
133
134impl From<PubkeyErrorOriginal> for PyErrWrapper {
135 fn from(e: PubkeyErrorOriginal) -> Self {
136 Self(PubkeyError::new_err(e.to_string()))
137 }
138}
139
140impl From<Box<ErrorKind>> for PyErrWrapper {
141 fn from(e: Box<ErrorKind>) -> Self {
142 Self(BincodeError::new_err(e.to_string()))
143 }
144}
145
146impl From<serde_cbor::Error> for PyErrWrapper {
147 fn from(e: serde_cbor::Error) -> Self {
148 Self(CborError::new_err(e.to_string()))
149 }
150}
151
152#[cfg(feature = "banks-client")]
153create_exception!(
154 solders,
155 BanksClientError,
156 PyException,
157 "Raised when BanksClient encounters an error."
158);
159
160#[cfg(feature = "banks-client")]
161impl From<BanksClientErrorOriginal> for PyErrWrapper {
162 fn from(e: BanksClientErrorOriginal) -> Self {
163 Self(BanksClientError::new_err(e.to_string()))
164 }
165}
166
167pub trait ToSignerOriginal {
168 fn to_inner(&self) -> Box<dyn SignerTrait>;
169}
170
171pub trait SignerTraitWrapper: ToSignerOriginal {
172 fn pubkey(&self) -> PubkeyOriginal {
173 self.to_inner().pubkey()
174 }
175 fn try_pubkey(&self) -> Result<PubkeyOriginal, SignerErrorOriginal> {
176 self.to_inner().try_pubkey()
177 }
178 fn sign_message(&self, message: &[u8]) -> SignatureOriginal {
179 self.to_inner().sign_message(message)
180 }
181 fn try_sign_message(&self, message: &[u8]) -> Result<SignatureOriginal, SignerErrorOriginal> {
182 self.to_inner().try_sign_message(message)
183 }
184 fn is_interactive(&self) -> bool {
185 self.to_inner().is_interactive()
186 }
187}
188
189pub trait RichcmpSigner: SignerTraitWrapper {
190 fn richcmp(&self, other: impl SignerTraitWrapper, op: CompareOp) -> PyResult<bool> {
191 let eq_val = self.pubkey() == other.pubkey();
192 match op {
193 CompareOp::Eq => Ok(eq_val),
194 CompareOp::Ne => Ok(!eq_val),
195 CompareOp::Lt => Err(richcmp_type_error("<")),
196 CompareOp::Gt => Err(richcmp_type_error(">")),
197 CompareOp::Le => Err(richcmp_type_error("<=")),
198 CompareOp::Ge => Err(richcmp_type_error(">=")),
199 }
200 }
201}
202
203#[macro_export]
204macro_rules! impl_signer_hash {
205 ($ident:ident) => {
206 #[allow(clippy::derived_hash_with_manual_eq)]
207 impl std::hash::Hash for $ident {
208 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
209 $crate::SignerTraitWrapper::pubkey(self).hash(state);
210 }
211 }
212 };
213}