Skip to main content

reth_rpc_eth_api/helpers/
signer.rs

1//! An abstraction over ethereum signers.
2
3use 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;
9
10/// Result returned by [`EthSigner`] methods.
11pub type Result<T> = result::Result<T, SignError>;
12
13/// An Ethereum Signer used via RPC.
14#[async_trait::async_trait]
15pub trait EthSigner<T, TxReq = TransactionRequest>: Send + Sync + DynClone {
16    /// Returns the available accounts for this signer.
17    fn accounts(&self) -> Vec<Address>;
18
19    /// Returns `true` whether this signer can sign for this address
20    fn is_signer_for(&self, addr: &Address) -> bool {
21        self.accounts().contains(addr)
22    }
23
24    /// Returns the signature
25    async fn sign(&self, address: Address, message: &[u8]) -> Result<Signature>;
26
27    /// signs a transaction request using the given account in request
28    async fn sign_transaction(&self, request: TxReq, address: &Address) -> Result<T>;
29
30    /// Encodes and signs the typed data according EIP-712. Payload must implement Eip712 trait.
31    fn sign_typed_data(&self, address: Address, payload: &TypedData) -> Result<Signature>;
32}
33
34dyn_clone::clone_trait_object!(<T, TxReq> EthSigner<T, TxReq>);
35
36#[cfg(test)]
37mod tests {
38    use super::*;
39
40    #[derive(Clone)]
41    struct MockSigner;
42
43    struct MockSignedTx;
44    struct MockTxReq;
45
46    #[async_trait::async_trait]
47    impl EthSigner<MockSignedTx, MockTxReq> for MockSigner {
48        fn accounts(&self) -> Vec<Address> {
49            Vec::new()
50        }
51
52        async fn sign(&self, _address: Address, _message: &[u8]) -> Result<Signature> {
53            Err(SignError::NoAccount)
54        }
55
56        async fn sign_transaction(
57            &self,
58            _request: MockTxReq,
59            _address: &Address,
60        ) -> Result<MockSignedTx> {
61            Err(SignError::NoAccount)
62        }
63
64        fn sign_typed_data(&self, _address: Address, _payload: &TypedData) -> Result<Signature> {
65            Err(SignError::NoAccount)
66        }
67    }
68
69    #[test]
70    fn clones_trait_object_with_custom_tx_request_type() {
71        let signer: Box<dyn EthSigner<MockSignedTx, MockTxReq>> = Box::new(MockSigner);
72        let cloned: Box<dyn EthSigner<MockSignedTx, MockTxReq>> = dyn_clone::clone_box(&*signer);
73
74        assert!(cloned.accounts().is_empty());
75    }
76}