solders_traits/
lib.rs

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}