1mod 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 async fn sign_data(&self, request: SignDataRequest) -> Result<SignDataResponse, SignerError>;
52
53 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); assert_eq!(sig.s.len(), 64); assert!(sig.v == 27 || sig.v == 28); assert_eq!(sig.sig.len(), 130); }
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 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 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 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 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}