1use crate::eth::error::BlockchainError;
2use alloy_consensus::{Sealed, SignableTransaction};
3use alloy_dyn_abi::TypedData;
4use alloy_network::TxSignerSync;
5use alloy_primitives::{Address, B256, Signature, map::AddressHashMap};
6use alloy_signer::Signer as AlloySigner;
7use alloy_signer_local::PrivateKeySigner;
8use foundry_primitives::{FoundryTxEnvelope, FoundryTypedTx};
9use tempo_primitives::TempoSignature;
10
11#[async_trait::async_trait]
13pub trait Signer: Send + Sync {
14 fn accounts(&self) -> Vec<Address>;
16
17 fn is_signer_for(&self, addr: Address) -> bool {
19 self.accounts().contains(&addr)
20 }
21
22 async fn sign(&self, address: Address, message: &[u8]) -> Result<Signature, BlockchainError>;
24
25 async fn sign_typed_data(
28 &self,
29 address: Address,
30 payload: &TypedData,
31 ) -> Result<Signature, BlockchainError>;
32
33 async fn sign_hash(&self, address: Address, hash: B256) -> Result<Signature, BlockchainError>;
35
36 fn sign_transaction(
38 &self,
39 request: FoundryTypedTx,
40 address: &Address,
41 ) -> Result<Signature, BlockchainError>;
42}
43
44pub struct DevSigner {
46 addresses: Vec<Address>,
47 accounts: AddressHashMap<PrivateKeySigner>,
48}
49
50impl DevSigner {
51 pub fn new(accounts: Vec<PrivateKeySigner>) -> Self {
52 let addresses = accounts.iter().map(|wallet| wallet.address()).collect::<Vec<_>>();
53 let accounts = addresses.iter().copied().zip(accounts).collect();
54 Self { addresses, accounts }
55 }
56}
57
58#[async_trait::async_trait]
59impl Signer for DevSigner {
60 fn accounts(&self) -> Vec<Address> {
61 self.addresses.clone()
62 }
63
64 fn is_signer_for(&self, addr: Address) -> bool {
65 self.accounts.contains_key(&addr)
66 }
67
68 async fn sign(&self, address: Address, message: &[u8]) -> Result<Signature, BlockchainError> {
69 let signer = self.accounts.get(&address).ok_or(BlockchainError::NoSignerAvailable)?;
70
71 Ok(signer.sign_message(message).await?)
72 }
73
74 async fn sign_typed_data(
75 &self,
76 address: Address,
77 payload: &TypedData,
78 ) -> Result<Signature, BlockchainError> {
79 let mut signer =
80 self.accounts.get(&address).ok_or(BlockchainError::NoSignerAvailable)?.to_owned();
81
82 signer.set_chain_id(None);
85
86 Ok(signer.sign_dynamic_typed_data(payload).await?)
87 }
88
89 async fn sign_hash(&self, address: Address, hash: B256) -> Result<Signature, BlockchainError> {
90 let signer = self.accounts.get(&address).ok_or(BlockchainError::NoSignerAvailable)?;
91
92 Ok(signer.sign_hash(&hash).await?)
93 }
94
95 fn sign_transaction(
96 &self,
97 request: FoundryTypedTx,
98 address: &Address,
99 ) -> Result<Signature, BlockchainError> {
100 let signer = self.accounts.get(address).ok_or(BlockchainError::NoSignerAvailable)?;
101 match request {
102 FoundryTypedTx::Legacy(mut tx) => Ok(signer.sign_transaction_sync(&mut tx)?),
103 FoundryTypedTx::Eip2930(mut tx) => Ok(signer.sign_transaction_sync(&mut tx)?),
104 FoundryTypedTx::Eip1559(mut tx) => Ok(signer.sign_transaction_sync(&mut tx)?),
105 FoundryTypedTx::Eip7702(mut tx) => Ok(signer.sign_transaction_sync(&mut tx)?),
106 FoundryTypedTx::Eip4844(mut tx) => Ok(signer.sign_transaction_sync(&mut tx)?),
107 FoundryTypedTx::Deposit(_) => {
108 unreachable!("op deposit txs should not be signed")
109 }
110 FoundryTypedTx::Tempo(mut tx) => Ok(signer.sign_transaction_sync(&mut tx)?),
111 }
112 }
113}
114
115pub fn build_typed_transaction(
121 request: FoundryTypedTx,
122 signature: Signature,
123) -> Result<FoundryTxEnvelope, BlockchainError> {
124 let tx = match request {
125 FoundryTypedTx::Legacy(tx) => FoundryTxEnvelope::Legacy(tx.into_signed(signature)),
126 FoundryTypedTx::Eip2930(tx) => FoundryTxEnvelope::Eip2930(tx.into_signed(signature)),
127 FoundryTypedTx::Eip1559(tx) => FoundryTxEnvelope::Eip1559(tx.into_signed(signature)),
128 FoundryTypedTx::Eip7702(tx) => FoundryTxEnvelope::Eip7702(tx.into_signed(signature)),
129 FoundryTypedTx::Eip4844(tx) => FoundryTxEnvelope::Eip4844(tx.into_signed(signature)),
130 FoundryTypedTx::Deposit(tx) => FoundryTxEnvelope::Deposit(Sealed::new(tx)),
131 FoundryTypedTx::Tempo(tx) => {
132 let tempo_sig: TempoSignature = signature.into();
133 FoundryTxEnvelope::Tempo(tx.into_signed(tempo_sig))
134 }
135 };
136
137 Ok(tx)
138}