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