reth_rpc_eth_api/helpers/
signer.rs1use alloy_dyn_abi::TypedData;
4use alloy_primitives::{Address, Signature};
5use alloy_rpc_types_eth::TransactionRequest;
6use dyn_clone::DynClone;
7use reth_rpc_eth_types::SignError;
8use std::{result, sync::Arc};
9use tokio::sync::Mutex;
10
11pub type Result<T> = result::Result<T, SignError>;
13
14#[async_trait::async_trait]
16pub trait EthSigner<T, TxReq = TransactionRequest>: Send + Sync + DynClone {
17 fn accounts(&self) -> Vec<Address>;
19
20 fn is_signer_for(&self, addr: &Address) -> bool {
22 self.accounts().contains(addr)
23 }
24
25 async fn sign(&self, address: Address, message: &[u8]) -> Result<Signature>;
27
28 async fn sign_transaction(&self, request: TxReq, address: &Address) -> Result<T>;
30
31 fn sign_typed_data(&self, address: Address, payload: &TypedData) -> Result<Signature>;
33
34 fn nonce_lock(&self) -> Option<Arc<Mutex<()>>> {
40 None
41 }
42}
43
44dyn_clone::clone_trait_object!(<T, TxReq> EthSigner<T, TxReq>);
45
46#[cfg(test)]
47mod tests {
48 use super::*;
49
50 #[derive(Clone)]
51 struct MockSigner;
52
53 struct MockSignedTx;
54 struct MockTxReq;
55
56 #[async_trait::async_trait]
57 impl EthSigner<MockSignedTx, MockTxReq> for MockSigner {
58 fn accounts(&self) -> Vec<Address> {
59 Vec::new()
60 }
61
62 async fn sign(&self, _address: Address, _message: &[u8]) -> Result<Signature> {
63 Err(SignError::NoAccount)
64 }
65
66 async fn sign_transaction(
67 &self,
68 _request: MockTxReq,
69 _address: &Address,
70 ) -> Result<MockSignedTx> {
71 Err(SignError::NoAccount)
72 }
73
74 fn sign_typed_data(&self, _address: Address, _payload: &TypedData) -> Result<Signature> {
75 Err(SignError::NoAccount)
76 }
77 }
78
79 #[test]
80 fn clones_trait_object_with_custom_tx_request_type() {
81 let signer: Box<dyn EthSigner<MockSignedTx, MockTxReq>> = Box::new(MockSigner);
82 let cloned: Box<dyn EthSigner<MockSignedTx, MockTxReq>> = dyn_clone::clone_box(&*signer);
83
84 assert!(cloned.accounts().is_empty());
85 }
86}