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}