1use crate::eth::error::BlockchainError;
2use alloy_consensus::{Sealed, SignableTransaction};
3use alloy_dyn_abi::TypedData;
4use alloy_network::{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 MessageSigner: 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
37pub trait Signer<N: Network>: MessageSigner {
43 fn sign_transaction_from(
47 &self,
48 sender: &Address,
49 tx: N::UnsignedTx,
50 ) -> Result<N::TxEnvelope, BlockchainError>;
51}
52
53pub struct DevSigner {
55 addresses: Vec<Address>,
56 accounts: AddressHashMap<PrivateKeySigner>,
57}
58
59impl DevSigner {
60 pub fn new(accounts: Vec<PrivateKeySigner>) -> Self {
61 let addresses = accounts.iter().map(|wallet| wallet.address()).collect::<Vec<_>>();
62 let accounts = addresses.iter().copied().zip(accounts).collect();
63 Self { addresses, accounts }
64 }
65}
66
67#[async_trait::async_trait]
68impl MessageSigner for DevSigner {
69 fn accounts(&self) -> Vec<Address> {
70 self.addresses.clone()
71 }
72
73 fn is_signer_for(&self, addr: Address) -> bool {
74 self.accounts.contains_key(&addr)
75 }
76
77 async fn sign(&self, address: Address, message: &[u8]) -> Result<Signature, BlockchainError> {
78 let signer = self.accounts.get(&address).ok_or(BlockchainError::NoSignerAvailable)?;
79
80 Ok(signer.sign_message(message).await?)
81 }
82
83 async fn sign_typed_data(
84 &self,
85 address: Address,
86 payload: &TypedData,
87 ) -> Result<Signature, BlockchainError> {
88 let mut signer =
89 self.accounts.get(&address).ok_or(BlockchainError::NoSignerAvailable)?.to_owned();
90
91 signer.set_chain_id(None);
94
95 Ok(signer.sign_dynamic_typed_data(payload).await?)
96 }
97
98 async fn sign_hash(&self, address: Address, hash: B256) -> Result<Signature, BlockchainError> {
99 let signer = self.accounts.get(&address).ok_or(BlockchainError::NoSignerAvailable)?;
100
101 Ok(signer.sign_hash(&hash).await?)
102 }
103}
104
105impl Signer<foundry_primitives::FoundryNetwork> for DevSigner {
106 fn sign_transaction_from(
107 &self,
108 sender: &Address,
109 tx: FoundryTypedTx,
110 ) -> Result<FoundryTxEnvelope, BlockchainError> {
111 let signer = self.accounts.get(sender).ok_or(BlockchainError::NoSignerAvailable)?;
112 let envelope = match tx {
113 FoundryTypedTx::Legacy(mut t) => {
114 let sig = signer.sign_transaction_sync(&mut t)?;
115 FoundryTxEnvelope::Legacy(t.into_signed(sig))
116 }
117 FoundryTypedTx::Eip2930(mut t) => {
118 let sig = signer.sign_transaction_sync(&mut t)?;
119 FoundryTxEnvelope::Eip2930(t.into_signed(sig))
120 }
121 FoundryTypedTx::Eip1559(mut t) => {
122 let sig = signer.sign_transaction_sync(&mut t)?;
123 FoundryTxEnvelope::Eip1559(t.into_signed(sig))
124 }
125 FoundryTypedTx::Eip7702(mut t) => {
126 let sig = signer.sign_transaction_sync(&mut t)?;
127 FoundryTxEnvelope::Eip7702(t.into_signed(sig))
128 }
129 FoundryTypedTx::Eip4844(mut t) => {
130 let sig = signer.sign_transaction_sync(&mut t)?;
131 FoundryTxEnvelope::Eip4844(t.into_signed(sig))
132 }
133 FoundryTypedTx::Deposit(_) => {
134 unreachable!("op deposit txs should not be signed")
135 }
136 FoundryTypedTx::Tempo(mut t) => {
137 let sig = signer.sign_transaction_sync(&mut t)?;
138 FoundryTxEnvelope::Tempo(t.into_signed(sig.into()))
139 }
140 };
141 Ok(envelope)
142 }
143}
144
145pub fn build_impersonated(typed_tx: FoundryTypedTx) -> FoundryTxEnvelope {
149 let signature = Signature::new(Default::default(), Default::default(), false);
150 match typed_tx {
151 FoundryTypedTx::Legacy(tx) => FoundryTxEnvelope::Legacy(tx.into_signed(signature)),
152 FoundryTypedTx::Eip2930(tx) => FoundryTxEnvelope::Eip2930(tx.into_signed(signature)),
153 FoundryTypedTx::Eip1559(tx) => FoundryTxEnvelope::Eip1559(tx.into_signed(signature)),
154 FoundryTypedTx::Eip7702(tx) => FoundryTxEnvelope::Eip7702(tx.into_signed(signature)),
155 FoundryTypedTx::Eip4844(tx) => FoundryTxEnvelope::Eip4844(tx.into_signed(signature)),
156 FoundryTypedTx::Deposit(tx) => FoundryTxEnvelope::Deposit(Sealed::new(tx)),
157 FoundryTypedTx::Tempo(tx) => {
158 let tempo_sig: TempoSignature = signature.into();
159 FoundryTxEnvelope::Tempo(tx.into_signed(tempo_sig))
160 }
161 }
162}