1use crate::Result;
2use alloy_primitives::{eip191_hash_message, Address, ChainId, Signature, B256};
3use async_trait::async_trait;
4use auto_impl::auto_impl;
5pub use either::Either;
6
7#[cfg(feature = "eip712")]
8use alloy_dyn_abi::eip712::TypedData;
9#[cfg(feature = "eip712")]
10use alloy_sol_types::{Eip712Domain, SolStruct};
11
12#[cfg_attr(target_family = "wasm", async_trait(?Send))]
23#[cfg_attr(not(target_family = "wasm"), async_trait)]
24#[auto_impl(&mut, Box)]
25pub trait Signer<Sig = Signature> {
26 async fn sign_hash(&self, hash: &B256) -> Result<Sig>;
28
29 #[inline]
33 async fn sign_message(&self, message: &[u8]) -> Result<Sig> {
34 self.sign_hash(&eip191_hash_message(message)).await
35 }
36
37 #[cfg(feature = "eip712")]
41 #[inline]
42 #[auto_impl(keep_default_for(&mut, Box))]
43 async fn sign_typed_data<T: SolStruct + Send + Sync>(
44 &self,
45 payload: &T,
46 domain: &Eip712Domain,
47 ) -> Result<Sig>
48 where
49 Self: Sized,
50 {
51 self.sign_hash(&payload.eip712_signing_hash(domain)).await
52 }
53
54 #[cfg(feature = "eip712")]
57 #[inline]
58 async fn sign_dynamic_typed_data(&self, payload: &TypedData) -> Result<Sig> {
59 self.sign_hash(&payload.eip712_signing_hash()?).await
60 }
61
62 fn address(&self) -> Address;
64
65 fn chain_id(&self) -> Option<ChainId>;
67
68 fn set_chain_id(&mut self, chain_id: Option<ChainId>);
70
71 #[inline]
73 #[must_use]
74 #[auto_impl(keep_default_for(&mut, Box))]
75 fn with_chain_id(mut self, chain_id: Option<ChainId>) -> Self
76 where
77 Self: Sized,
78 {
79 self.set_chain_id(chain_id);
80 self
81 }
82}
83
84#[auto_impl(&, &mut, Box, Rc, Arc)]
96pub trait SignerSync<Sig = Signature> {
97 fn sign_hash_sync(&self, hash: &B256) -> Result<Sig>;
99
100 #[inline]
104 fn sign_message_sync(&self, message: &[u8]) -> Result<Sig> {
105 self.sign_hash_sync(&eip191_hash_message(message))
106 }
107
108 #[cfg(feature = "eip712")]
112 #[inline]
113 #[auto_impl(keep_default_for(&, &mut, Box, Rc, Arc))]
114 fn sign_typed_data_sync<T: SolStruct>(&self, payload: &T, domain: &Eip712Domain) -> Result<Sig>
115 where
116 Self: Sized,
117 {
118 self.sign_hash_sync(&payload.eip712_signing_hash(domain))
119 }
120
121 #[cfg(feature = "eip712")]
126 #[inline]
127 fn sign_dynamic_typed_data_sync(&self, payload: &TypedData) -> Result<Sig> {
128 let hash = payload.eip712_signing_hash()?;
129 self.sign_hash_sync(&hash)
130 }
131
132 fn chain_id_sync(&self) -> Option<ChainId>;
134}
135
136#[cfg_attr(target_family = "wasm", async_trait(?Send))]
137#[cfg_attr(not(target_family = "wasm"), async_trait)]
138#[async_trait]
139impl<A, B, Sig> Signer<Sig> for Either<A, B>
140where
141 A: Signer<Sig> + Send + Sync,
142 B: Signer<Sig> + Send + Sync,
143 Sig: Send,
144{
145 async fn sign_hash(&self, hash: &B256) -> Result<Sig> {
146 match self {
147 Self::Left(signer) => signer.sign_hash(hash).await,
148 Self::Right(signer) => signer.sign_hash(hash).await,
149 }
150 }
151
152 fn address(&self) -> Address {
153 match self {
154 Self::Left(signer) => signer.address(),
155 Self::Right(signer) => signer.address(),
156 }
157 }
158
159 fn chain_id(&self) -> Option<ChainId> {
160 match self {
161 Self::Left(signer) => signer.chain_id(),
162 Self::Right(signer) => signer.chain_id(),
163 }
164 }
165
166 fn set_chain_id(&mut self, chain_id: Option<ChainId>) {
167 match self {
168 Self::Left(signer) => signer.set_chain_id(chain_id),
169 Self::Right(signer) => signer.set_chain_id(chain_id),
170 }
171 }
172}
173
174impl<A, B, Sig> SignerSync<Sig> for Either<A, B>
175where
176 A: SignerSync<Sig>,
177 B: SignerSync<Sig>,
178{
179 fn sign_hash_sync(&self, hash: &B256) -> Result<Sig> {
180 match self {
181 Self::Left(signer) => signer.sign_hash_sync(hash),
182 Self::Right(signer) => signer.sign_hash_sync(hash),
183 }
184 }
185
186 fn chain_id_sync(&self) -> Option<ChainId> {
187 match self {
188 Self::Left(signer) => signer.chain_id_sync(),
189 Self::Right(signer) => signer.chain_id_sync(),
190 }
191 }
192}
193
194#[cfg(test)]
195mod tests {
196 use super::*;
197 use crate::{Error, UnsupportedSignerOperation};
198 use assert_matches::assert_matches;
199 use std::sync::Arc;
200
201 struct _ObjectSafe(Box<dyn Signer>, Box<dyn SignerSync>);
202
203 #[tokio::test]
204 async fn unimplemented() {
205 #[cfg(feature = "eip712")]
206 alloy_sol_types::sol! {
207 #[derive(Default, serde::Serialize)]
208 struct Eip712Data {
209 uint64 a;
210 }
211 }
212
213 async fn test_unimplemented_signer<S: Signer + SignerSync + Send + Sync>(s: &S) {
214 test_unsized_unimplemented_signer(s).await;
215 test_unsized_unimplemented_signer_sync(s);
216
217 #[cfg(feature = "eip712")]
218 assert!(s
219 .sign_typed_data_sync(&Eip712Data::default(), &Eip712Domain::default())
220 .is_err());
221 #[cfg(feature = "eip712")]
222 assert!(s
223 .sign_typed_data(&Eip712Data::default(), &Eip712Domain::default())
224 .await
225 .is_err());
226 }
227
228 async fn test_unsized_unimplemented_signer<S: Signer + ?Sized + Send + Sync>(s: &S) {
229 assert_matches!(
230 s.sign_hash(&B256::ZERO).await,
231 Err(Error::UnsupportedOperation(UnsupportedSignerOperation::SignHash))
232 );
233
234 assert_matches!(
235 s.sign_message(&[]).await,
236 Err(Error::UnsupportedOperation(UnsupportedSignerOperation::SignHash))
237 );
238
239 #[cfg(feature = "eip712")]
240 assert_matches!(
241 s.sign_dynamic_typed_data(&TypedData::from_struct(&Eip712Data::default(), None))
242 .await,
243 Err(Error::UnsupportedOperation(UnsupportedSignerOperation::SignHash))
244 );
245
246 assert_eq!(s.chain_id(), None);
247 }
248
249 fn test_unsized_unimplemented_signer_sync<S: SignerSync + ?Sized>(s: &S) {
250 assert_matches!(
251 s.sign_hash_sync(&B256::ZERO),
252 Err(Error::UnsupportedOperation(UnsupportedSignerOperation::SignHash))
253 );
254
255 assert_matches!(
256 s.sign_message_sync(&[]),
257 Err(Error::UnsupportedOperation(UnsupportedSignerOperation::SignHash))
258 );
259
260 #[cfg(feature = "eip712")]
261 assert_matches!(
262 s.sign_dynamic_typed_data_sync(&TypedData::from_struct(
263 &Eip712Data::default(),
264 None
265 )),
266 Err(Error::UnsupportedOperation(UnsupportedSignerOperation::SignHash))
267 );
268
269 assert_eq!(s.chain_id_sync(), None);
270 }
271
272 struct UnimplementedSigner;
273
274 #[cfg_attr(target_family = "wasm", async_trait(?Send))]
275 #[cfg_attr(not(target_family = "wasm"), async_trait)]
276 impl Signer for UnimplementedSigner {
277 async fn sign_hash(&self, _hash: &B256) -> Result<Signature> {
278 Err(Error::UnsupportedOperation(UnsupportedSignerOperation::SignHash))
279 }
280
281 fn address(&self) -> Address {
282 Address::ZERO
283 }
284
285 fn chain_id(&self) -> Option<ChainId> {
286 None
287 }
288
289 fn set_chain_id(&mut self, _chain_id: Option<ChainId>) {}
290 }
291
292 impl SignerSync for UnimplementedSigner {
293 fn sign_hash_sync(&self, _hash: &B256) -> Result<Signature> {
294 Err(Error::UnsupportedOperation(UnsupportedSignerOperation::SignHash))
295 }
296
297 fn chain_id_sync(&self) -> Option<ChainId> {
298 None
299 }
300 }
301
302 test_unimplemented_signer(&UnimplementedSigner).await;
303 test_unsized_unimplemented_signer(&UnimplementedSigner as &(dyn Signer + Send + Sync))
304 .await;
305 test_unsized_unimplemented_signer_sync(
306 &UnimplementedSigner as &(dyn SignerSync + Send + Sync),
307 );
308
309 test_unsized_unimplemented_signer(
310 &(Box::new(UnimplementedSigner) as Box<dyn Signer + Send + Sync>),
311 )
312 .await;
313 test_unsized_unimplemented_signer_sync(
314 &(Box::new(UnimplementedSigner) as Box<dyn SignerSync + Send + Sync>),
315 );
316
317 test_unsized_unimplemented_signer_sync(
318 &(Arc::new(UnimplementedSigner) as Arc<dyn SignerSync + Send + Sync>),
319 );
320 }
321}