openzeppelin_relayer/services/signer/evm/
mod.rs

1//! EVM signer implementation for managing Ethereum-compatible private keys and signing operations.
2//!
3//! Provides:
4//! - Local keystore support (encrypted JSON files)
5//! - Turnkey (Turnkey backend)
6//! - AWS KMS support
7//!
8//! # Architecture
9//!
10//! ```text
11//! EvmSigner
12//!   ├── TestSigner (Temporary testing private key)
13//!   ├── LocalSigner (encrypted JSON keystore)
14//!   ├── AwsKmsSigner (AWS KMS backend)
15//!   ├── Vault (HashiCorp Vault backend)
16//!   ├── VaultCould (HashiCorp Vault backend)
17//!   └── Turnkey (Turnkey backend)
18//! ```
19mod aws_kms_signer;
20mod google_cloud_kms_signer;
21mod local_signer;
22mod turnkey_signer;
23use aws_kms_signer::*;
24use google_cloud_kms_signer::*;
25use local_signer::*;
26use turnkey_signer::*;
27
28use async_trait::async_trait;
29use color_eyre::config;
30
31use crate::{
32    domain::{
33        SignDataRequest, SignDataResponse, SignDataResponseEvm, SignTransactionResponse,
34        SignTypedDataRequest,
35    },
36    models::{
37        Address, NetworkTransactionData, SignerConfig, SignerRepoModel, SignerType,
38        TransactionRepoModel,
39    },
40    services::{
41        turnkey::TurnkeyService, AwsKmsService, GoogleCloudKmsService, TurnkeyServiceTrait,
42    },
43};
44use eyre::Result;
45
46use super::{Signer, SignerError, SignerFactoryError};
47
48#[async_trait]
49pub trait DataSignerTrait: Send + Sync {
50    /// Signs arbitrary message data
51    async fn sign_data(&self, request: SignDataRequest) -> Result<SignDataResponse, SignerError>;
52
53    /// Signs EIP-712 typed data
54    async fn sign_typed_data(
55        &self,
56        request: SignTypedDataRequest,
57    ) -> Result<SignDataResponse, SignerError>;
58}
59
60pub enum EvmSigner {
61    Local(LocalSigner),
62    Vault(LocalSigner),
63    VaultCloud(LocalSigner),
64    Turnkey(TurnkeySigner),
65    AwsKms(AwsKmsSigner),
66    GoogleCloudKms(GoogleCloudKmsSigner),
67}
68
69#[async_trait]
70impl Signer for EvmSigner {
71    async fn address(&self) -> Result<Address, SignerError> {
72        match self {
73            Self::Local(signer) => signer.address().await,
74            Self::Vault(signer) => signer.address().await,
75            Self::VaultCloud(signer) => signer.address().await,
76            Self::Turnkey(signer) => signer.address().await,
77            Self::AwsKms(signer) => signer.address().await,
78            Self::GoogleCloudKms(signer) => signer.address().await,
79        }
80    }
81
82    async fn sign_transaction(
83        &self,
84        transaction: NetworkTransactionData,
85    ) -> Result<SignTransactionResponse, SignerError> {
86        match self {
87            Self::Local(signer) => signer.sign_transaction(transaction).await,
88            Self::Vault(signer) => signer.sign_transaction(transaction).await,
89            Self::VaultCloud(signer) => signer.sign_transaction(transaction).await,
90            Self::Turnkey(signer) => signer.sign_transaction(transaction).await,
91            Self::AwsKms(signer) => signer.sign_transaction(transaction).await,
92            Self::GoogleCloudKms(signer) => signer.sign_transaction(transaction).await,
93        }
94    }
95}
96
97#[async_trait]
98impl DataSignerTrait for EvmSigner {
99    async fn sign_data(&self, request: SignDataRequest) -> Result<SignDataResponse, SignerError> {
100        match self {
101            Self::Local(signer) => signer.sign_data(request).await,
102            Self::Vault(signer) => signer.sign_data(request).await,
103            Self::VaultCloud(signer) => signer.sign_data(request).await,
104            Self::Turnkey(signer) => signer.sign_data(request).await,
105            Self::AwsKms(signer) => signer.sign_data(request).await,
106            Self::GoogleCloudKms(signer) => signer.sign_data(request).await,
107        }
108    }
109
110    async fn sign_typed_data(
111        &self,
112        request: SignTypedDataRequest,
113    ) -> Result<SignDataResponse, SignerError> {
114        match self {
115            Self::Local(signer) => signer.sign_typed_data(request).await,
116            Self::Vault(signer) => signer.sign_typed_data(request).await,
117            Self::VaultCloud(signer) => signer.sign_typed_data(request).await,
118            Self::Turnkey(signer) => signer.sign_typed_data(request).await,
119            Self::AwsKms(signer) => signer.sign_typed_data(request).await,
120            Self::GoogleCloudKms(signer) => signer.sign_typed_data(request).await,
121        }
122    }
123}
124
125pub struct EvmSignerFactory;
126
127impl EvmSignerFactory {
128    pub async fn create_evm_signer(
129        signer_model: SignerRepoModel,
130    ) -> Result<EvmSigner, SignerFactoryError> {
131        let signer = match signer_model.config {
132            SignerConfig::Local(_)
133            | SignerConfig::Test(_)
134            | SignerConfig::Vault(_)
135            | SignerConfig::VaultCloud(_) => EvmSigner::Local(LocalSigner::new(&signer_model)?),
136            SignerConfig::AwsKms(ref config) => {
137                let aws_service = AwsKmsService::new(config.clone()).await.map_err(|e| {
138                    SignerFactoryError::CreationFailed(format!("AWS KMS service error: {}", e))
139                })?;
140                EvmSigner::AwsKms(AwsKmsSigner::new(aws_service))
141            }
142            SignerConfig::VaultTransit(_) => {
143                return Err(SignerFactoryError::UnsupportedType("Vault Transit".into()));
144            }
145            SignerConfig::Turnkey(ref config) => {
146                let turnkey_service = TurnkeyService::new(config.clone()).map_err(|e| {
147                    SignerFactoryError::CreationFailed(format!("Turnkey service error: {}", e))
148                })?;
149                EvmSigner::Turnkey(TurnkeySigner::new(turnkey_service))
150            }
151            SignerConfig::GoogleCloudKms(ref config) => {
152                let gcp_service = GoogleCloudKmsService::new(config).map_err(|e| {
153                    SignerFactoryError::CreationFailed(format!(
154                        "Google Cloud KMS service error: {}",
155                        e
156                    ))
157                })?;
158                EvmSigner::GoogleCloudKms(GoogleCloudKmsSigner::new(gcp_service))
159            }
160        };
161
162        Ok(signer)
163    }
164}
165
166#[cfg(test)]
167mod tests {
168    use super::*;
169    use crate::models::{
170        AwsKmsSignerConfig, EvmTransactionData, GoogleCloudKmsSignerConfig,
171        GoogleCloudKmsSignerKeyConfig, GoogleCloudKmsSignerServiceAccountConfig, LocalSignerConfig,
172        SecretString, SignerConfig, SignerRepoModel, TurnkeySignerConfig, VaultTransitSignerConfig,
173        U256,
174    };
175    use futures;
176    use mockall::predicate::*;
177    use secrets::SecretVec;
178    use std::str::FromStr;
179    use std::sync::Arc;
180
181    fn test_key_bytes() -> SecretVec<u8> {
182        let key_bytes =
183            hex::decode("0000000000000000000000000000000000000000000000000000000000000001")
184                .unwrap();
185        SecretVec::new(key_bytes.len(), |v| v.copy_from_slice(&key_bytes))
186    }
187
188    fn test_key_address() -> Address {
189        Address::Evm([
190            126, 95, 69, 82, 9, 26, 105, 18, 93, 93, 252, 183, 184, 194, 101, 144, 41, 57, 91, 223,
191        ])
192    }
193
194    #[tokio::test]
195    async fn test_create_evm_signer_local() {
196        let signer_model = SignerRepoModel {
197            id: "test".to_string(),
198            config: SignerConfig::Local(LocalSignerConfig {
199                raw_key: test_key_bytes(),
200            }),
201        };
202
203        let signer = EvmSignerFactory::create_evm_signer(signer_model)
204            .await
205            .unwrap();
206
207        assert!(matches!(signer, EvmSigner::Local(_)));
208    }
209
210    #[tokio::test]
211    async fn test_create_evm_signer_test() {
212        let signer_model = SignerRepoModel {
213            id: "test".to_string(),
214            config: SignerConfig::Test(LocalSignerConfig {
215                raw_key: test_key_bytes(),
216            }),
217        };
218
219        let signer = EvmSignerFactory::create_evm_signer(signer_model)
220            .await
221            .unwrap();
222
223        assert!(matches!(signer, EvmSigner::Local(_)));
224    }
225
226    #[tokio::test]
227    async fn test_create_evm_signer_vault() {
228        let signer_model = SignerRepoModel {
229            id: "test".to_string(),
230            config: SignerConfig::Vault(LocalSignerConfig {
231                raw_key: test_key_bytes(),
232            }),
233        };
234
235        let signer = EvmSignerFactory::create_evm_signer(signer_model)
236            .await
237            .unwrap();
238
239        assert!(matches!(signer, EvmSigner::Local(_)));
240    }
241
242    #[tokio::test]
243    async fn test_create_evm_signer_vault_cloud() {
244        let signer_model = SignerRepoModel {
245            id: "test".to_string(),
246            config: SignerConfig::VaultCloud(LocalSignerConfig {
247                raw_key: test_key_bytes(),
248            }),
249        };
250
251        let signer = EvmSignerFactory::create_evm_signer(signer_model)
252            .await
253            .unwrap();
254
255        assert!(matches!(signer, EvmSigner::Local(_)));
256    }
257
258    #[tokio::test]
259    async fn test_create_evm_signer_aws_kms() {
260        let signer_model = SignerRepoModel {
261            id: "test".to_string(),
262            config: SignerConfig::AwsKms(AwsKmsSignerConfig {
263                region: Some("us-east-1".to_string()),
264                key_id: "test-key-id".to_string(),
265            }),
266        };
267
268        let signer = EvmSignerFactory::create_evm_signer(signer_model)
269            .await
270            .unwrap();
271
272        assert!(matches!(signer, EvmSigner::AwsKms(_)));
273    }
274
275    #[tokio::test]
276    async fn test_create_evm_signer_vault_transit() {
277        let signer_model = SignerRepoModel {
278            id: "test".to_string(),
279            config: SignerConfig::VaultTransit(VaultTransitSignerConfig {
280                key_name: "test".to_string(),
281                address: "address".to_string(),
282                namespace: None,
283                role_id: SecretString::new("test-role"),
284                secret_id: SecretString::new("test-secret"),
285                pubkey: "pubkey".to_string(),
286                mount_point: None,
287            }),
288        };
289
290        let result = EvmSignerFactory::create_evm_signer(signer_model).await;
291
292        assert!(matches!(
293            result,
294            Err(SignerFactoryError::UnsupportedType(_))
295        ));
296    }
297
298    #[tokio::test]
299    async fn test_create_evm_signer_turnkey() {
300        let signer_model = SignerRepoModel {
301            id: "test".to_string(),
302            config: SignerConfig::Turnkey(TurnkeySignerConfig {
303                api_private_key: SecretString::new("api_private_key"),
304                api_public_key: "api_public_key".to_string(),
305                organization_id: "organization_id".to_string(),
306                private_key_id: "private_key_id".to_string(),
307                public_key: "047d3bb8e0317927700cf19fed34e0627367be1390ec247dddf8c239e4b4321a49aea80090e49b206b6a3e577a4f11d721ab063482001ee10db40d6f2963233eec".to_string(),
308            }),
309        };
310
311        let signer = EvmSignerFactory::create_evm_signer(signer_model)
312            .await
313            .unwrap();
314        let signer_address = signer.address().await.unwrap();
315
316        assert_eq!(
317            "0xb726167dc2ef2ac582f0a3de4c08ac4abb90626a",
318            signer_address.to_string()
319        );
320    }
321
322    #[tokio::test]
323    async fn test_address_evm_signer_local() {
324        let signer_model = SignerRepoModel {
325            id: "test".to_string(),
326            config: SignerConfig::Local(LocalSignerConfig {
327                raw_key: test_key_bytes(),
328            }),
329        };
330
331        let signer = EvmSignerFactory::create_evm_signer(signer_model)
332            .await
333            .unwrap();
334        let signer_address = signer.address().await.unwrap();
335
336        assert_eq!(test_key_address(), signer_address);
337    }
338
339    #[tokio::test]
340    async fn test_address_evm_signer_test() {
341        let signer_model = SignerRepoModel {
342            id: "test".to_string(),
343            config: SignerConfig::Test(LocalSignerConfig {
344                raw_key: test_key_bytes(),
345            }),
346        };
347
348        let signer = EvmSignerFactory::create_evm_signer(signer_model)
349            .await
350            .unwrap();
351        let signer_address = signer.address().await.unwrap();
352
353        assert_eq!(test_key_address(), signer_address);
354    }
355
356    #[tokio::test]
357    async fn test_address_evm_signer_vault() {
358        let signer_model = SignerRepoModel {
359            id: "test".to_string(),
360            config: SignerConfig::Vault(LocalSignerConfig {
361                raw_key: test_key_bytes(),
362            }),
363        };
364
365        let signer = EvmSignerFactory::create_evm_signer(signer_model)
366            .await
367            .unwrap();
368        let signer_address = signer.address().await.unwrap();
369
370        assert_eq!(test_key_address(), signer_address);
371    }
372
373    #[tokio::test]
374    async fn test_address_evm_signer_vault_cloud() {
375        let signer_model = SignerRepoModel {
376            id: "test".to_string(),
377            config: SignerConfig::VaultCloud(LocalSignerConfig {
378                raw_key: test_key_bytes(),
379            }),
380        };
381
382        let signer = EvmSignerFactory::create_evm_signer(signer_model)
383            .await
384            .unwrap();
385        let signer_address = signer.address().await.unwrap();
386
387        assert_eq!(test_key_address(), signer_address);
388    }
389
390    #[tokio::test]
391    async fn test_address_evm_signer_turnkey() {
392        let signer_model = SignerRepoModel {
393            id: "test".to_string(),
394            config: SignerConfig::Turnkey(TurnkeySignerConfig {
395                api_private_key: SecretString::new("api_private_key"),
396                api_public_key: "api_public_key".to_string(),
397                organization_id: "organization_id".to_string(),
398                private_key_id: "private_key_id".to_string(),
399                public_key: "047d3bb8e0317927700cf19fed34e0627367be1390ec247dddf8c239e4b4321a49aea80090e49b206b6a3e577a4f11d721ab063482001ee10db40d6f2963233eec".to_string(),
400            }),
401        };
402
403        let signer = EvmSignerFactory::create_evm_signer(signer_model)
404            .await
405            .unwrap();
406        let signer_address = signer.address().await.unwrap();
407
408        assert_eq!(
409            "0xb726167dc2ef2ac582f0a3de4c08ac4abb90626a",
410            signer_address.to_string()
411        );
412    }
413
414    #[tokio::test]
415    async fn test_sign_data_evm_signer_local() {
416        let signer_model = SignerRepoModel {
417            id: "test".to_string(),
418            config: SignerConfig::Local(LocalSignerConfig {
419                raw_key: test_key_bytes(),
420            }),
421        };
422
423        let signer = EvmSignerFactory::create_evm_signer(signer_model)
424            .await
425            .unwrap();
426        let request = SignDataRequest {
427            message: "Test message".to_string(),
428        };
429
430        let result = signer.sign_data(request).await;
431
432        assert!(result.is_ok());
433
434        let response = result.unwrap();
435        assert!(matches!(response, SignDataResponse::Evm(_)));
436
437        if let SignDataResponse::Evm(sig) = response {
438            assert_eq!(sig.r.len(), 64); // 32 bytes in hex
439            assert_eq!(sig.s.len(), 64); // 32 bytes in hex
440            assert!(sig.v == 27 || sig.v == 28); // Valid v values
441            assert_eq!(sig.sig.len(), 130); // 65 bytes in hex
442        }
443    }
444
445    #[tokio::test]
446    async fn test_sign_transaction_evm() {
447        let signer_model = SignerRepoModel {
448            id: "test".to_string(),
449            config: SignerConfig::Local(LocalSignerConfig {
450                raw_key: test_key_bytes(),
451            }),
452        };
453
454        let signer = EvmSignerFactory::create_evm_signer(signer_model)
455            .await
456            .unwrap();
457
458        let transaction_data = NetworkTransactionData::Evm(EvmTransactionData {
459            from: "0x742d35Cc6634C0532925a3b844Bc454e4438f44e".to_string(),
460            to: Some("0x742d35Cc6634C0532925a3b844Bc454e4438f44f".to_string()),
461            gas_price: Some(20000000000),
462            gas_limit: 21000,
463            nonce: Some(0),
464            value: U256::from(1000000000000000000u64),
465            data: Some("0x".to_string()),
466            chain_id: 1,
467            hash: None,
468            signature: None,
469            raw: None,
470            max_fee_per_gas: None,
471            max_priority_fee_per_gas: None,
472            speed: None,
473        });
474
475        let result = signer.sign_transaction(transaction_data).await;
476
477        assert!(result.is_ok());
478
479        let signed_tx = result.unwrap();
480
481        assert!(matches!(signed_tx, SignTransactionResponse::Evm(_)));
482
483        if let SignTransactionResponse::Evm(evm_tx) = signed_tx {
484            assert!(!evm_tx.hash.is_empty());
485            assert!(!evm_tx.raw.is_empty());
486            assert!(!evm_tx.signature.sig.is_empty());
487        }
488    }
489
490    #[tokio::test]
491    async fn test_create_evm_signer_google_cloud_kms() {
492        let signer_model = SignerRepoModel {
493            id: "test".to_string(),
494            config: SignerConfig::GoogleCloudKms(GoogleCloudKmsSignerConfig {
495                service_account: GoogleCloudKmsSignerServiceAccountConfig {
496                    project_id: "project_id".to_string(),
497                    private_key_id: SecretString::new("private_key_id"),
498                    private_key: SecretString::new("-----BEGIN EXAMPLE PRIVATE KEY-----\nFAKEKEYDATA\n-----END EXAMPLE PRIVATE KEY-----\n"),
499                    client_email: SecretString::new("client_email@example.com"),
500                    client_id: "client_id".to_string(),
501                    auth_uri: "https://accounts.google.com/o/oauth2/auth".to_string(),
502                    token_uri: "https://oauth2.googleapis.com/token".to_string(),
503                    auth_provider_x509_cert_url: "https://www.googleapis.com/oauth2/v1/certs".to_string(),
504                    client_x509_cert_url: "https://www.googleapis.com/robot/v1/metadata/x509/client_email%40example.com".to_string(),
505                    universe_domain: "googleapis.com".to_string(),
506                },
507                key: GoogleCloudKmsSignerKeyConfig {
508                    location: "global".to_string(),
509                    key_id: "id".to_string(),
510                    key_ring_id: "key_ring".to_string(),
511                    key_version: 1,
512                },
513            }),
514        };
515
516        let result = EvmSignerFactory::create_evm_signer(signer_model).await;
517
518        assert!(result.is_ok());
519        assert!(matches!(result.unwrap(), EvmSigner::GoogleCloudKms(_)));
520    }
521
522    #[tokio::test]
523    async fn test_sign_data_with_different_message_types() {
524        let signer_model = SignerRepoModel {
525            id: "test".to_string(),
526            config: SignerConfig::Local(LocalSignerConfig {
527                raw_key: test_key_bytes(),
528            }),
529        };
530
531        let signer = EvmSignerFactory::create_evm_signer(signer_model)
532            .await
533            .unwrap();
534
535        // Test with various message types
536        let long_message = "a".repeat(1000);
537        let test_cases = vec![
538            ("Simple message", "Test message"),
539            ("Empty message", ""),
540            ("Unicode message", "🚀 Test message with émojis"),
541            ("Long message", long_message.as_str()),
542            ("JSON message", r#"{"test": "value", "number": 123}"#),
543        ];
544
545        for (name, message) in test_cases {
546            let request = SignDataRequest {
547                message: message.to_string(),
548            };
549
550            let result = signer.sign_data(request).await;
551            assert!(result.is_ok(), "Failed to sign {}", name);
552
553            if let Ok(SignDataResponse::Evm(sig)) = result {
554                assert_eq!(sig.r.len(), 64, "Invalid r length for {}", name);
555                assert_eq!(sig.s.len(), 64, "Invalid s length for {}", name);
556                assert!(sig.v == 27 || sig.v == 28, "Invalid v value for {}", name);
557                assert_eq!(sig.sig.len(), 130, "Invalid signature length for {}", name);
558            } else {
559                panic!("Expected EVM signature for {}", name);
560            }
561        }
562    }
563
564    #[tokio::test]
565    async fn test_sign_data_with_vault_signer() {
566        let signer_model = SignerRepoModel {
567            id: "test".to_string(),
568            config: SignerConfig::Vault(LocalSignerConfig {
569                raw_key: test_key_bytes(),
570            }),
571        };
572
573        let signer = EvmSignerFactory::create_evm_signer(signer_model)
574            .await
575            .unwrap();
576
577        let request = SignDataRequest {
578            message: "Test vault message".to_string(),
579        };
580
581        let result = signer.sign_data(request).await;
582        assert!(result.is_ok());
583
584        if let Ok(SignDataResponse::Evm(sig)) = result {
585            assert_eq!(sig.r.len(), 64);
586            assert_eq!(sig.s.len(), 64);
587            assert!(sig.v == 27 || sig.v == 28);
588            assert_eq!(sig.sig.len(), 130);
589        } else {
590            panic!("Expected successful EVM signature");
591        }
592    }
593
594    #[tokio::test]
595    async fn test_sign_transaction_with_vault_cloud_signer() {
596        let signer_model = SignerRepoModel {
597            id: "test".to_string(),
598            config: SignerConfig::VaultCloud(LocalSignerConfig {
599                raw_key: test_key_bytes(),
600            }),
601        };
602
603        let signer = EvmSignerFactory::create_evm_signer(signer_model)
604            .await
605            .unwrap();
606
607        let transaction_data = NetworkTransactionData::Evm(EvmTransactionData {
608            from: "0x742d35Cc6634C0532925a3b844Bc454e4438f44e".to_string(),
609            to: Some("0x742d35Cc6634C0532925a3b844Bc454e4438f44f".to_string()),
610            gas_price: Some(20000000000),
611            gas_limit: 21000,
612            nonce: Some(0),
613            value: U256::from(1000000000000000000u64),
614            data: Some("0x".to_string()),
615            chain_id: 1,
616            hash: None,
617            signature: None,
618            raw: None,
619            max_fee_per_gas: None,
620            max_priority_fee_per_gas: None,
621            speed: None,
622        });
623
624        let result = signer.sign_transaction(transaction_data).await;
625        assert!(result.is_ok());
626
627        if let Ok(SignTransactionResponse::Evm(evm_tx)) = result {
628            assert!(!evm_tx.hash.is_empty());
629            assert!(!evm_tx.raw.is_empty());
630            assert!(!evm_tx.signature.sig.is_empty());
631        } else {
632            panic!("Expected successful EVM transaction signing");
633        }
634    }
635
636    #[tokio::test]
637    async fn test_address_consistency_across_vault_types() {
638        // Test that different vault types using the same key produce the same address
639        let key_bytes = test_key_bytes();
640
641        let signers = vec![
642            EvmSignerFactory::create_evm_signer(SignerRepoModel {
643                id: "local".to_string(),
644                config: SignerConfig::Local(LocalSignerConfig {
645                    raw_key: key_bytes.clone(),
646                }),
647            })
648            .await
649            .unwrap(),
650            EvmSignerFactory::create_evm_signer(SignerRepoModel {
651                id: "vault".to_string(),
652                config: SignerConfig::Vault(LocalSignerConfig {
653                    raw_key: key_bytes.clone(),
654                }),
655            })
656            .await
657            .unwrap(),
658            EvmSignerFactory::create_evm_signer(SignerRepoModel {
659                id: "vault_cloud".to_string(),
660                config: SignerConfig::VaultCloud(LocalSignerConfig { raw_key: key_bytes }),
661            })
662            .await
663            .unwrap(),
664        ];
665
666        let addresses: Vec<Address> =
667            futures::future::try_join_all(signers.iter().map(|s| s.address()))
668                .await
669                .unwrap();
670
671        // All addresses should be identical since they use the same key
672        assert_eq!(addresses[0], addresses[1]);
673        assert_eq!(addresses[1], addresses[2]);
674        assert_eq!(addresses[0], test_key_address());
675    }
676
677    #[tokio::test]
678    async fn test_transaction_signing_with_different_vault_types() {
679        // Test that different vault configurations can sign transactions correctly
680        let transaction_data = NetworkTransactionData::Evm(EvmTransactionData {
681            from: "0x742d35Cc6634C0532925a3b844Bc454e4438f44e".to_string(),
682            to: Some("0x742d35Cc6634C0532925a3b844Bc454e4438f44f".to_string()),
683            gas_price: Some(20000000000),
684            gas_limit: 21000,
685            nonce: Some(0),
686            value: U256::from(1000000000000000000u64),
687            data: Some("0x".to_string()),
688            chain_id: 1,
689            hash: None,
690            signature: None,
691            raw: None,
692            max_fee_per_gas: None,
693            max_priority_fee_per_gas: None,
694            speed: None,
695        });
696
697        let vault_configs = vec![
698            (
699                "vault",
700                SignerConfig::Vault(LocalSignerConfig {
701                    raw_key: test_key_bytes(),
702                }),
703            ),
704            (
705                "vault_cloud",
706                SignerConfig::VaultCloud(LocalSignerConfig {
707                    raw_key: test_key_bytes(),
708                }),
709            ),
710        ];
711
712        for (name, config) in vault_configs {
713            let signer_model = SignerRepoModel {
714                id: name.to_string(),
715                config,
716            };
717
718            let signer = EvmSignerFactory::create_evm_signer(signer_model)
719                .await
720                .unwrap();
721
722            let result = signer.sign_transaction(transaction_data.clone()).await;
723            assert!(result.is_ok(), "Failed to sign transaction with {}", name);
724
725            if let Ok(SignTransactionResponse::Evm(evm_tx)) = result {
726                assert!(!evm_tx.hash.is_empty(), "Empty hash for {}", name);
727                assert!(!evm_tx.raw.is_empty(), "Empty raw for {}", name);
728                assert!(
729                    !evm_tx.signature.sig.is_empty(),
730                    "Empty signature for {}",
731                    name
732                );
733            } else {
734                panic!("Expected EVM transaction response for {}", name);
735            }
736        }
737    }
738}