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)]
138impl<A, B, Sig> Signer<Sig> for Either<A, B>
139where
140 A: Signer<Sig> + Send + Sync,
141 B: Signer<Sig> + Send + Sync,
142 Sig: Send,
143{
144 async fn sign_hash(&self, hash: &B256) -> Result<Sig> {
145 match self {
146 Self::Left(signer) => signer.sign_hash(hash).await,
147 Self::Right(signer) => signer.sign_hash(hash).await,
148 }
149 }
150
151 fn address(&self) -> Address {
152 match self {
153 Self::Left(signer) => signer.address(),
154 Self::Right(signer) => signer.address(),
155 }
156 }
157
158 fn chain_id(&self) -> Option<ChainId> {
159 match self {
160 Self::Left(signer) => signer.chain_id(),
161 Self::Right(signer) => signer.chain_id(),
162 }
163 }
164
165 fn set_chain_id(&mut self, chain_id: Option<ChainId>) {
166 match self {
167 Self::Left(signer) => signer.set_chain_id(chain_id),
168 Self::Right(signer) => signer.set_chain_id(chain_id),
169 }
170 }
171}
172
173impl<A, B, Sig> SignerSync<Sig> for Either<A, B>
174where
175 A: SignerSync<Sig>,
176 B: SignerSync<Sig>,
177{
178 fn sign_hash_sync(&self, hash: &B256) -> Result<Sig> {
179 match self {
180 Self::Left(signer) => signer.sign_hash_sync(hash),
181 Self::Right(signer) => signer.sign_hash_sync(hash),
182 }
183 }
184
185 fn chain_id_sync(&self) -> Option<ChainId> {
186 match self {
187 Self::Left(signer) => signer.chain_id_sync(),
188 Self::Right(signer) => signer.chain_id_sync(),
189 }
190 }
191}
192
193#[cfg(test)]
194mod tests {
195 use super::*;
196 use crate::{Error, UnsupportedSignerOperation};
197 use assert_matches::assert_matches;
198 use std::sync::Arc;
199
200 struct _ObjectSafe(Box<dyn Signer>, Box<dyn SignerSync>);
201
202 #[tokio::test]
203 async fn unimplemented() {
204 #[cfg(feature = "eip712")]
205 alloy_sol_types::sol! {
206 #[derive(Default, serde::Serialize)]
207 struct Eip712Data {
208 uint64 a;
209 }
210 }
211
212 async fn test_unimplemented_signer<S: Signer + SignerSync + Send + Sync>(s: &S) {
213 test_unsized_unimplemented_signer(s).await;
214 test_unsized_unimplemented_signer_sync(s);
215
216 #[cfg(feature = "eip712")]
217 assert!(s
218 .sign_typed_data_sync(&Eip712Data::default(), &Eip712Domain::default())
219 .is_err());
220 #[cfg(feature = "eip712")]
221 assert!(s
222 .sign_typed_data(&Eip712Data::default(), &Eip712Domain::default())
223 .await
224 .is_err());
225 }
226
227 async fn test_unsized_unimplemented_signer<S: Signer + ?Sized + Send + Sync>(s: &S) {
228 assert_matches!(
229 s.sign_hash(&B256::ZERO).await,
230 Err(Error::UnsupportedOperation(UnsupportedSignerOperation::SignHash))
231 );
232
233 assert_matches!(
234 s.sign_message(&[]).await,
235 Err(Error::UnsupportedOperation(UnsupportedSignerOperation::SignHash))
236 );
237
238 #[cfg(feature = "eip712")]
239 assert_matches!(
240 s.sign_dynamic_typed_data(&TypedData::from_struct(&Eip712Data::default(), None))
241 .await,
242 Err(Error::UnsupportedOperation(UnsupportedSignerOperation::SignHash))
243 );
244
245 assert_eq!(s.chain_id(), None);
246 }
247
248 fn test_unsized_unimplemented_signer_sync<S: SignerSync + ?Sized>(s: &S) {
249 assert_matches!(
250 s.sign_hash_sync(&B256::ZERO),
251 Err(Error::UnsupportedOperation(UnsupportedSignerOperation::SignHash))
252 );
253
254 assert_matches!(
255 s.sign_message_sync(&[]),
256 Err(Error::UnsupportedOperation(UnsupportedSignerOperation::SignHash))
257 );
258
259 #[cfg(feature = "eip712")]
260 assert_matches!(
261 s.sign_dynamic_typed_data_sync(&TypedData::from_struct(
262 &Eip712Data::default(),
263 None
264 )),
265 Err(Error::UnsupportedOperation(UnsupportedSignerOperation::SignHash))
266 );
267
268 assert_eq!(s.chain_id_sync(), None);
269 }
270
271 struct UnimplementedSigner;
272
273 #[cfg_attr(target_family = "wasm", async_trait(?Send))]
274 #[cfg_attr(not(target_family = "wasm"), async_trait)]
275 impl Signer for UnimplementedSigner {
276 async fn sign_hash(&self, _hash: &B256) -> Result<Signature> {
277 Err(Error::UnsupportedOperation(UnsupportedSignerOperation::SignHash))
278 }
279
280 fn address(&self) -> Address {
281 Address::ZERO
282 }
283
284 fn chain_id(&self) -> Option<ChainId> {
285 None
286 }
287
288 fn set_chain_id(&mut self, _chain_id: Option<ChainId>) {}
289 }
290
291 impl SignerSync for UnimplementedSigner {
292 fn sign_hash_sync(&self, _hash: &B256) -> Result<Signature> {
293 Err(Error::UnsupportedOperation(UnsupportedSignerOperation::SignHash))
294 }
295
296 fn chain_id_sync(&self) -> Option<ChainId> {
297 None
298 }
299 }
300
301 test_unimplemented_signer(&UnimplementedSigner).await;
302 test_unsized_unimplemented_signer(&UnimplementedSigner as &(dyn Signer + Send + Sync))
303 .await;
304 test_unsized_unimplemented_signer_sync(
305 &UnimplementedSigner as &(dyn SignerSync + Send + Sync),
306 );
307
308 test_unsized_unimplemented_signer(
309 &(Box::new(UnimplementedSigner) as Box<dyn Signer + Send + Sync>),
310 )
311 .await;
312 test_unsized_unimplemented_signer_sync(
313 &(Box::new(UnimplementedSigner) as Box<dyn SignerSync + Send + Sync>),
314 );
315
316 test_unsized_unimplemented_signer_sync(
317 &(Arc::new(UnimplementedSigner) as Arc<dyn SignerSync + Send + Sync>),
318 );
319 }
320}