diff --git a/src/protocol/methods/tempo/method.rs b/src/protocol/methods/tempo/method.rs index 004281a0..9290f54c 100644 --- a/src/protocol/methods/tempo/method.rs +++ b/src/protocol/methods/tempo/method.rs @@ -103,7 +103,7 @@ fn parse_b256_hex(s: &str) -> Option { #[derive(Clone)] pub struct ChargeMethod

{ provider: Arc

, - fee_payer_signer: Option>, + fee_payer_signer: Option>, store: Option>, } @@ -136,7 +136,13 @@ where /// /// When set, requests with `feePayer: true` will be accepted and /// broadcast. Without a fee payer signer, such requests are rejected. - pub fn with_fee_payer(mut self, signer: alloy::signers::local::PrivateKeySigner) -> Self { + /// + /// Accepts any type implementing alloy's [`Signer`](alloy::signers::Signer) + /// trait — local private keys, KMS-backed signers, hardware wallets, etc. + pub fn with_fee_payer( + mut self, + signer: impl alloy::signers::Signer + Send + Sync + 'static, + ) -> Self { self.fee_payer_signer = Some(Arc::new(signer)); self } @@ -559,7 +565,8 @@ where ) })?; - self.cosign_fee_payer_transaction(&tx_bytes, fee_payer_signer, currency)? + self.cosign_fee_payer_transaction(&tx_bytes, &**fee_payer_signer, currency) + .await? } else { tx_bytes.to_vec() }; @@ -638,16 +645,15 @@ where /// Accepts a `0x78` fee payer envelope, recovers the sender via /// ecrecover, validates fee-payer invariants, then co-signs and /// returns a complete `0x76` transaction ready for broadcast. - fn cosign_fee_payer_transaction( + async fn cosign_fee_payer_transaction( &self, tx_bytes: &[u8], - fee_payer_signer: &alloy::signers::local::PrivateKeySigner, + fee_payer_signer: &(dyn alloy::signers::Signer + Send + Sync), fee_token: Address, ) -> Result, VerificationError> { use super::fee_payer_envelope::{FeePayerEnvelope78, TEMPO_FEE_PAYER_ENVELOPE_TYPE_ID}; use alloy::consensus::transaction::SignerRecoverable; use alloy::eips::Encodable2718; - use alloy::signers::SignerSync; use tempo_primitives::transaction::TEMPO_EXPIRING_NONCE_KEY; if tx_bytes.is_empty() { @@ -724,7 +730,8 @@ where // Compute the fee payer signature hash and co-sign let fp_hash = tx.fee_payer_signature_hash(sender); let fp_sig = fee_payer_signer - .sign_hash_sync(&fp_hash) + .sign_hash(&fp_hash) + .await .map_err(|e| VerificationError::new(format!("Failed to co-sign transaction: {e}")))?; tx.fee_payer_signature = Some(fp_sig); @@ -1042,8 +1049,8 @@ mod tests { /// Round-trip: sign 0x78 envelope → cosign_fee_payer_transaction /// succeeds and produces a valid co-signed 0x76 transaction. - #[test] - fn test_fee_payer_round_trip_0x78_envelope() { + #[tokio::test] + async fn test_fee_payer_round_trip_0x78_envelope() { use super::super::{FeePayerEnvelope78, TEMPO_FEE_PAYER_ENVELOPE_TYPE_ID}; use alloy::eips::Decodable2718; use alloy::signers::SignerSync; @@ -1073,11 +1080,13 @@ mod tests { let result = method.cosign_fee_payer_transaction( &encoded, - method.fee_payer_signer.as_ref().unwrap(), + &**method.fee_payer_signer.as_ref().unwrap(), fee_token, ); - let co_signed = result.expect("cosign should succeed for valid 0x78 envelope"); + let co_signed = result + .await + .expect("cosign should succeed for valid 0x78 envelope"); // Result should be a valid 0x76 transaction assert_eq!( @@ -1099,8 +1108,8 @@ mod tests { } /// cosign_fee_payer_transaction rejects txs with wrong nonce_key. - #[test] - fn test_cosign_rejects_wrong_nonce_key() { + #[tokio::test] + async fn test_cosign_rejects_wrong_nonce_key() { let client_signer = alloy::signers::local::PrivateKeySigner::random(); let fee_payer_signer = alloy::signers::local::PrivateKeySigner::random(); let fee_token: Address = "0x20c0000000000000000000000000000000000000" @@ -1120,11 +1129,11 @@ mod tests { let result = method.cosign_fee_payer_transaction( &encoded, - method.fee_payer_signer.as_ref().unwrap(), + &**method.fee_payer_signer.as_ref().unwrap(), fee_token, ); - let err = result.expect_err("should reject wrong nonce_key"); + let err = result.await.expect_err("should reject wrong nonce_key"); assert!( err.to_string().contains("expiring nonce key"), "error should mention expiring nonce key, got: {err}" @@ -1132,8 +1141,8 @@ mod tests { } /// cosign_fee_payer_transaction rejects txs without valid_before. - #[test] - fn test_cosign_rejects_missing_valid_before() { + #[tokio::test] + async fn test_cosign_rejects_missing_valid_before() { let client_signer = alloy::signers::local::PrivateKeySigner::random(); let fee_payer_signer = alloy::signers::local::PrivateKeySigner::random(); let fee_token: Address = "0x20c0000000000000000000000000000000000000" @@ -1153,11 +1162,13 @@ mod tests { let result = method.cosign_fee_payer_transaction( &encoded, - method.fee_payer_signer.as_ref().unwrap(), + &**method.fee_payer_signer.as_ref().unwrap(), fee_token, ); - let err = result.expect_err("should reject missing valid_before"); + let err = result + .await + .expect_err("should reject missing valid_before"); assert!( err.to_string().contains("must include valid_before"), "error should mention valid_before, got: {err}" @@ -1165,8 +1176,8 @@ mod tests { } /// cosign_fee_payer_transaction rejects txs with expired valid_before. - #[test] - fn test_cosign_rejects_expired_valid_before() { + #[tokio::test] + async fn test_cosign_rejects_expired_valid_before() { let client_signer = alloy::signers::local::PrivateKeySigner::random(); let fee_payer_signer = alloy::signers::local::PrivateKeySigner::random(); let fee_token: Address = "0x20c0000000000000000000000000000000000000" @@ -1193,11 +1204,13 @@ mod tests { let result = method.cosign_fee_payer_transaction( &encoded, - method.fee_payer_signer.as_ref().unwrap(), + &**method.fee_payer_signer.as_ref().unwrap(), fee_token, ); - let err = result.expect_err("should reject expired valid_before"); + let err = result + .await + .expect_err("should reject expired valid_before"); assert!( err.to_string().contains("expired"), "error should mention expiration, got: {err}" @@ -1205,8 +1218,8 @@ mod tests { } /// cosign_fee_payer_transaction rejects empty input. - #[test] - fn test_cosign_rejects_empty_input() { + #[tokio::test] + async fn test_cosign_rejects_empty_input() { let fee_payer_signer = alloy::signers::local::PrivateKeySigner::random(); let fee_token: Address = "0x20c0000000000000000000000000000000000000" .parse() @@ -1220,11 +1233,11 @@ mod tests { let result = method.cosign_fee_payer_transaction( &[], - method.fee_payer_signer.as_ref().unwrap(), + &**method.fee_payer_signer.as_ref().unwrap(), fee_token, ); - let err = result.expect_err("should reject empty input"); + let err = result.await.expect_err("should reject empty input"); assert!( err.to_string().contains("Empty transaction bytes"), "error should mention empty, got: {err}" @@ -1232,8 +1245,8 @@ mod tests { } /// cosign_fee_payer_transaction rejects non-0x78 type byte. - #[test] - fn test_cosign_rejects_wrong_type_byte() { + #[tokio::test] + async fn test_cosign_rejects_wrong_type_byte() { let fee_payer_signer = alloy::signers::local::PrivateKeySigner::random(); let fee_token: Address = "0x20c0000000000000000000000000000000000000" .parse() @@ -1247,11 +1260,11 @@ mod tests { let result = method.cosign_fee_payer_transaction( &[0x79, 0xc0], // wrong type byte - method.fee_payer_signer.as_ref().unwrap(), + &**method.fee_payer_signer.as_ref().unwrap(), fee_token, ); - let err = result.expect_err("should reject wrong type"); + let err = result.await.expect_err("should reject wrong type"); assert!( err.to_string() .contains("Expected fee payer envelope (0x78)"), diff --git a/src/protocol/methods/tempo/session_method.rs b/src/protocol/methods/tempo/session_method.rs index 75bdbca1..62a8ba5c 100644 --- a/src/protocol/methods/tempo/session_method.rs +++ b/src/protocol/methods/tempo/session_method.rs @@ -213,7 +213,9 @@ pub struct SessionMethod

{ store: Arc, config: SessionMethodConfig, /// Optional signer for submitting on-chain close transactions. - close_signer: Option>, + /// + /// Accepts any EVM signer (local key, KMS, hardware wallet, etc.). + close_signer: Option>, } impl

SessionMethod

{ @@ -254,7 +256,13 @@ where } /// Set the signer used for submitting on-chain close transactions. - pub fn with_close_signer(mut self, signer: alloy::signers::local::PrivateKeySigner) -> Self { + /// + /// Accepts any type implementing alloy's [`Signer`](alloy::signers::Signer) + /// trait — local private keys, KMS-backed signers, hardware wallets, etc. + pub fn with_close_signer( + mut self, + signer: impl alloy::signers::Signer + Send + Sync + 'static, + ) -> Self { self.close_signer = Some(Arc::new(signer)); self } @@ -705,7 +713,6 @@ where let close_tx_hash = if let Some(ref signer) = self.close_signer { use alloy::eips::Encodable2718; use alloy::primitives::Bytes; - use alloy::signers::SignerSync; use alloy::sol_types::SolCall; use tempo_primitives::transaction::Call; use tempo_primitives::TempoTransaction; @@ -749,7 +756,7 @@ where }; let sig_hash = tempo_tx.signature_hash(); - let signature = signer.sign_hash_sync(&sig_hash).map_err(|e| { + let signature = signer.sign_hash(&sig_hash).await.map_err(|e| { VerificationError::network_error(format!("failed to sign close tx: {}", e)) })?; let signed_tx = tempo_tx.into_signed(signature.into()); diff --git a/src/server/tempo.rs b/src/server/tempo.rs index 951a1913..02b1050c 100644 --- a/src/server/tempo.rs +++ b/src/server/tempo.rs @@ -32,7 +32,7 @@ pub struct TempoBuilder { pub(crate) decimals: u32, pub(crate) fee_payer: bool, pub(crate) chain_id: Option, - pub(crate) fee_payer_signer: Option, + pub(crate) fee_payer_signer: Option>, } impl TempoBuilder { @@ -96,8 +96,14 @@ impl TempoBuilder { /// When clients send transactions with `feePayer: true`, the server /// uses this signer to co-sign and sponsor the transaction gas fees. /// The signer's account must have sufficient balance for gas. - pub fn fee_payer_signer(mut self, signer: alloy::signers::local::PrivateKeySigner) -> Self { - self.fee_payer_signer = Some(signer); + /// + /// Accepts any type implementing alloy's [`Signer`](alloy::signers::Signer) + /// trait — local private keys, KMS-backed signers, hardware wallets, etc. + pub fn fee_payer_signer( + mut self, + signer: impl alloy::signers::Signer + Send + Sync + 'static, + ) -> Self { + self.fee_payer_signer = Some(Box::new(signer)); self } }