openzeppelin_relayer/services/signer/
mod.rs

1//! Signer service module for handling cryptographic operations across different blockchain
2//! networks.
3//!
4//! This module provides:
5//! - Common signer traits for different blockchain networks
6//! - Network-specific signer implementations (EVM, Solana, Stellar)
7//! - Factory methods for creating signers
8//! - Error handling for signing operations
9//!
10//! # Architecture
11//!
12//! ```text
13//! Signer Trait (Common Interface)
14//!   ├── EvmSigner
15//!   │   |── LocalSigner
16//!   |   |── TurnkeySigner
17//!   |   └── AwsKmsSigner
18//!   |   └── AzureKeyVaultSigner
19//!   ├── SolanaSigner
20//!   │   |── LocalSigner
21//!   |   |── GoogleCloudKmsSigner
22//!   │   └── VaultTransitSigner
23//!   └── StellarSigner
24
25#![allow(unused_imports)]
26use async_trait::async_trait;
27use eyre::Result;
28#[cfg(test)]
29use mockall::automock;
30use serde::Serialize;
31use thiserror::Error;
32
33pub(crate) mod evm;
34pub use evm::*;
35
36mod solana;
37pub use solana::*;
38
39mod stellar;
40pub use stellar::*;
41
42use crate::{
43    domain::{
44        SignDataRequest, SignDataResponse, SignTransactionResponse, SignTypedDataRequest,
45        SignXdrTransactionResponseStellar,
46    },
47    models::{
48        Address, DecoratedSignature, NetworkTransactionData, NetworkType,
49        Signer as SignerDomainModel, SignerError, SignerFactoryError, SignerType, TransactionError,
50        TransactionRepoModel,
51    },
52};
53
54/// Response from signing an XDR transaction
55#[derive(Debug, Clone, Serialize)]
56pub struct XdrSigningResponse {
57    /// The signed XDR in base64 format
58    pub signed_xdr: String,
59    /// The signature that was applied
60    pub signature: DecoratedSignature,
61}
62
63#[async_trait]
64#[cfg_attr(test, automock)]
65pub trait Signer: Send + Sync {
66    /// Returns the signer's ethereum address
67    async fn address(&self) -> Result<Address, SignerError>;
68
69    /// Signs a transaction
70    async fn sign_transaction(
71        &self,
72        transaction: NetworkTransactionData,
73    ) -> Result<SignTransactionResponse, SignerError>;
74}
75
76#[allow(dead_code)]
77#[allow(clippy::large_enum_variant)]
78pub enum NetworkSigner {
79    Evm(EvmSigner),
80    Solana(SolanaSigner),
81    Stellar(StellarSigner),
82}
83
84#[async_trait]
85impl Signer for NetworkSigner {
86    async fn address(&self) -> Result<Address, SignerError> {
87        match self {
88            Self::Evm(signer) => signer.address().await,
89            Self::Solana(signer) => signer.address().await,
90            Self::Stellar(signer) => signer.address().await,
91        }
92    }
93
94    async fn sign_transaction(
95        &self,
96        transaction: NetworkTransactionData,
97    ) -> Result<SignTransactionResponse, SignerError> {
98        match self {
99            Self::Evm(signer) => signer.sign_transaction(transaction).await,
100            Self::Solana(signer) => signer.sign_transaction(transaction).await,
101            Self::Stellar(signer) => signer.sign_transaction(transaction).await,
102        }
103    }
104}
105
106#[async_trait]
107impl DataSignerTrait for NetworkSigner {
108    async fn sign_data(&self, request: SignDataRequest) -> Result<SignDataResponse, SignerError> {
109        match self {
110            Self::Evm(signer) => {
111                let signature = signer
112                    .sign_data(request)
113                    .await
114                    .map_err(|e| SignerError::SigningError(e.to_string()))?;
115
116                Ok(signature)
117            }
118            Self::Solana(_) => Err(SignerError::UnsupportedTypeError(
119                "Solana: sign data not supported".into(),
120            )),
121            Self::Stellar(_) => Err(SignerError::UnsupportedTypeError(
122                "Stellar: sign data not supported".into(),
123            )),
124        }
125    }
126
127    async fn sign_typed_data(
128        &self,
129        request: SignTypedDataRequest,
130    ) -> Result<SignDataResponse, SignerError> {
131        match self {
132            Self::Evm(signer) => signer
133                .sign_typed_data(request)
134                .await
135                .map_err(|e| SignerError::SigningError(e.to_string())),
136            Self::Solana(_) => Err(SignerError::UnsupportedTypeError(
137                "Solana: Signing typed data not supported".into(),
138            )),
139            Self::Stellar(_) => Err(SignerError::UnsupportedTypeError(
140                "Stellar: Signing typed data not supported".into(),
141            )),
142        }
143    }
144}
145
146pub struct SignerFactory;
147
148impl SignerFactory {
149    pub async fn create_signer(
150        network_type: &NetworkType,
151        signer_model: &SignerDomainModel,
152    ) -> Result<NetworkSigner, SignerFactoryError> {
153        let signer = match network_type {
154            NetworkType::Evm => {
155                let evm_signer = EvmSignerFactory::create_evm_signer(signer_model.clone()).await?;
156                NetworkSigner::Evm(evm_signer)
157            }
158            NetworkType::Solana => {
159                let solana_signer =
160                    SolanaSignerFactory::create_solana_signer(signer_model.clone()).await?;
161                NetworkSigner::Solana(solana_signer)
162            }
163            NetworkType::Stellar => {
164                let stellar_signer =
165                    StellarSignerFactory::create_stellar_signer(signer_model.clone()).await?;
166                NetworkSigner::Stellar(stellar_signer)
167            }
168        };
169
170        Ok(signer)
171    }
172}