Skip to main content

builder_relayer_client_rust/
signer.rs

1use crate::builder::create::{
2    AbstractSignerForCreate, CreateProxy, EIP712Domain, SAFE_FACTORY_NAME,
3};
4use crate::errors::{RelayClientError, Result};
5use alloy::primitives::{Address, B256, Signature, U256, keccak256};
6use alloy::signers::Signer;
7use alloy::signers::local::PrivateKeySigner;
8use alloy::sol_types::SolStruct;
9use async_trait::async_trait;
10use std::str::FromStr;
11
12/// Trait mirroring the expected abstract signer behavior in TS version.
13#[async_trait]
14pub trait AbstractSigner: Send + Sync {
15    fn address(&self) -> Address;
16    async fn sign_hash(&self, hash: B256) -> Result<Signature>;
17    async fn sign_eip712_digest(&self, digest: B256) -> Result<Signature>;
18}
19
20/// DummySigner: simple local wallet wrapper from a provided private key.
21#[derive(Clone)]
22pub struct DummySigner {
23    wallet: PrivateKeySigner,
24}
25
26impl DummySigner {
27    pub fn new(priv_key_hex: &str) -> Result<Self> {
28        let wallet = PrivateKeySigner::from_str(priv_key_hex)
29            .map_err(|_| RelayClientError::SignerUnavailable)?;
30        Ok(Self { wallet })
31    }
32}
33
34#[async_trait]
35impl AbstractSigner for DummySigner {
36    fn address(&self) -> Address {
37        self.wallet.address()
38    }
39
40    async fn sign_hash(&self, hash: B256) -> Result<Signature> {
41        self.wallet
42            .sign_hash(&hash)
43            .await
44            .map_err(|e| RelayClientError::SigningError(e.to_string()))
45    }
46
47    async fn sign_eip712_digest(&self, digest: B256) -> Result<Signature> {
48        // For EIP-712 digest, we just sign the hash.
49        // The digest construction happens outside.
50        self.sign_hash(digest).await
51    }
52}
53
54#[async_trait]
55impl AbstractSignerForCreate for DummySigner {
56    fn address(&self) -> Address {
57        self.wallet.address()
58    }
59
60    async fn sign_typed_create_proxy(
61        &self,
62        safe_factory: &str,
63        chain_id: u64,
64        payment_token: &str,
65        payment: &str,
66        payment_receiver: &str,
67    ) -> Result<String> {
68        let domain = EIP712Domain {
69            name: SAFE_FACTORY_NAME.to_string(),
70            chainId: U256::from(chain_id),
71            verifyingContract: Address::from_str(safe_factory)
72                .map_err(|_| RelayClientError::InvalidAddress)?,
73        };
74
75        let create_proxy = CreateProxy {
76            paymentToken: Address::from_str(payment_token)
77                .map_err(|_| RelayClientError::InvalidAddress)?,
78            payment: U256::from_str(payment).unwrap_or(U256::ZERO),
79            paymentReceiver: Address::from_str(payment_receiver)
80                .map_err(|_| RelayClientError::InvalidAddress)?,
81        };
82
83        let domain_separator = domain.eip712_hash_struct();
84        let struct_hash = create_proxy.eip712_hash_struct();
85
86        let mut digest_input = Vec::with_capacity(2 + 32 + 32);
87        digest_input.extend_from_slice(&[0x19, 0x01]);
88        digest_input.extend_from_slice(&domain_separator.0);
89        digest_input.extend_from_slice(&struct_hash.0);
90
91        let digest = keccak256(&digest_input);
92
93        let sig = self.sign_hash(digest).await?;
94        Ok(format!("0x{}", hex::encode(sig.as_bytes())))
95    }
96}