kabu_types_entities/
signers.rs1use alloy_consensus::SignableTransaction;
2use alloy_network::{TransactionBuilder, TxSigner as AlloyTxSigner, TxSignerSync};
3use alloy_primitives::{Address, B256, Bytes, hex};
4use alloy_rpc_types::TransactionRequest;
5use alloy_signer_local::PrivateKeySigner;
6use eyre::{OptionExt, Result, eyre};
7use indexmap::IndexMap;
8use rand::prelude::IteratorRandom;
9use reth_ethereum_primitives::{EthPrimitives, TransactionSigned};
10use reth_node_types::NodePrimitives;
11use std::fmt;
12use std::fmt::Debug;
13use std::future::Future;
14use std::pin::Pin;
15use std::sync::Arc;
16
17pub trait LoomTxSigner<N: NodePrimitives>: Send + Sync + Debug {
18 fn sign<'a>(&'a self, tx: TransactionRequest) -> Pin<Box<dyn std::future::Future<Output = Result<N::SignedTx>> + Send + 'a>>;
19 fn sign_sync(&self, tx: TransactionRequest) -> Result<N::SignedTx>;
20 fn address(&self) -> Address;
21}
22
23#[derive(Clone)]
24pub struct TxSignerEth {
25 address: Address,
26 wallet: PrivateKeySigner,
27}
28
29impl Default for TxSignerEth {
30 fn default() -> Self {
31 let wallet = PrivateKeySigner::random();
32 Self { address: wallet.address(), wallet }
33 }
34}
35
36impl fmt::Debug for TxSignerEth {
37 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
38 f.debug_struct("TxSigner").field("address", &self.address.to_string()).finish()
39 }
40}
41
42impl LoomTxSigner<EthPrimitives> for TxSignerEth {
43 fn address(&self) -> Address {
44 self.address
45 }
46 fn sign<'a>(&'a self, tx_req: TransactionRequest) -> Pin<Box<dyn Future<Output = Result<TransactionSigned>> + Send + 'a>> {
47 let fut = async move {
48 let mut typed_tx = tx_req
49 .build_typed_tx()
50 .map_err(|e| eyre!("TRANSACTION_TYPE_IS_MISSING"))?
51 .eip1559()
52 .ok_or_eyre("TRANSACTION_IS_NOT_EIP1559")?
53 .clone();
54 let signature = self.wallet.sign_transaction(&mut typed_tx).await?;
55 let signed_tx = typed_tx.clone().into_signed(signature);
56
57 Ok(signed_tx.into())
58 };
59 Box::pin(fut)
60 }
61
62 fn sign_sync(&self, tx_req: TransactionRequest) -> Result<TransactionSigned> {
63 let mut typed_tx = tx_req
64 .build_unsigned()
65 .map_err(|e| eyre!(format!("CANNOT_BUILD_UNSIGNED with error: {}", e)))?
66 .eip1559()
67 .ok_or_eyre("TRANSACTION_IS_NOT_EIP1559")?
68 .clone();
69
70 let signature = self.wallet.sign_transaction_sync(&mut typed_tx)?;
71 let signed_tx = typed_tx.clone().into_signed(signature);
72
73 Ok(signed_tx.into())
74 }
75}
76
77impl TxSignerEth {
78 pub fn new(wallet: PrivateKeySigner) -> TxSignerEth {
79 TxSignerEth { address: wallet.address(), wallet }
80 }
81}
82
83#[derive(Clone, Default)]
84pub struct TxSigners<N: NodePrimitives = EthPrimitives> {
85 signers: IndexMap<Address, Arc<dyn LoomTxSigner<N>>>,
86}
87
88impl TxSigners<EthPrimitives> {
89 pub fn add_privkey(&mut self, priv_key: Bytes) -> TxSignerEth {
90 let wallet = PrivateKeySigner::from_bytes(&B256::from_slice(priv_key.as_ref())).unwrap();
91 self.signers.insert(wallet.address(), Arc::new(TxSignerEth::new(wallet.clone())));
92 TxSignerEth::new(wallet)
93 }
94
95 pub fn add_testkey(&mut self) -> TxSignerEth {
96 self.add_privkey(Bytes::from(hex!("507485ea5bcf6864596cb51b2e727bb2d8ed5e64bb4f3d8c77a734d2fd610c6e")))
97 }
98}
99
100impl<N: NodePrimitives> TxSigners<N> {
101 pub fn new() -> TxSigners<N> {
102 TxSigners { signers: IndexMap::new() }
103 }
104
105 pub fn len(&self) -> usize {
106 self.signers.len()
107 }
108
109 pub fn is_empty(&self) -> bool {
110 self.signers.is_empty()
111 }
112
113 pub fn get_random_signer(&self) -> Option<Arc<dyn LoomTxSigner<N>>> {
114 if self.is_empty() {
115 None
116 } else {
117 let mut rng = rand::rng();
118 self.signers.values().choose(&mut rng).cloned()
119 }
120 }
121 pub fn get_signer_by_index(&self, index: usize) -> Result<Arc<dyn LoomTxSigner<N>>> {
122 match self.signers.get_index(index) {
123 Some((_, s)) => Ok(s.clone()),
124 None => Err(eyre!("SIGNER_NOT_FOUND")),
125 }
126 }
127
128 pub fn get_signer_by_address(&self, address: &Address) -> Result<Arc<dyn LoomTxSigner<N>>> {
129 match self.signers.get(address) {
130 Some(s) => Ok(s.clone()),
131 None => Err(eyre!("SIGNER_NOT_FOUND")),
132 }
133 }
134
135 pub fn get_address_vec(&self) -> Vec<Address> {
136 self.signers.keys().cloned().collect()
137 }
138}
139
140#[cfg(test)]
141mod tests {
142 use super::*;
143 use alloy_network::eip2718::Encodable2718;
144 use alloy_primitives::{TxHash, address};
145 use alloy_rpc_types::TransactionRequest;
146 use eyre::Result;
147
148 #[test]
149 fn test_new_signer() {
150 let wallet = PrivateKeySigner::random();
151 let signer = TxSignerEth::new(wallet.clone());
152 assert_eq!(signer.address(), wallet.address());
153 }
154
155 #[test]
156 fn test_address() {
157 let wallet = PrivateKeySigner::random();
158 let signer = TxSignerEth::new(wallet.clone());
159 assert_eq!(signer.address(), wallet.address());
160 }
161
162 #[tokio::test]
163 async fn test_sign() -> Result<()> {
164 let wallet = PrivateKeySigner::from_bytes(&B256::repeat_byte(1))?;
165 let signer = Box::new(TxSignerEth::new(wallet));
166 let tx_req = TransactionRequest::default()
167 .with_to(Address::ZERO)
168 .with_nonce(1)
169 .with_gas_limit(1)
170 .with_max_fee_per_gas(1)
171 .with_max_priority_fee_per_gas(1);
172 let tx = signer.sign(tx_req).await?;
173 let tx_hash = tx.tx_hash();
174 let tx_rlp = tx.encoded_2718();
175 assert_eq!(tx_hash, &TxHash::from(hex!("a43d09cb299eb6269f5a63fb10ea078c649cbf6a5f159cfd5b6f4be7ad0dfcfd")));
176 assert!(!tx_rlp.is_empty());
177 Ok(())
178 }
179
180 #[test]
181 fn test_sign_sync() -> Result<()> {
182 let wallet = PrivateKeySigner::from_bytes(&B256::repeat_byte(1))?;
183 let signer = TxSignerEth::new(wallet);
184 let tx_req = TransactionRequest::default()
185 .with_to(Address::ZERO)
186 .with_nonce(1)
187 .with_gas_limit(1)
188 .with_max_fee_per_gas(1)
189 .with_max_priority_fee_per_gas(1);
190 let tx = signer.sign_sync(tx_req)?;
191 let tx_hash = tx.tx_hash();
192 let tx_rlp = tx.encoded_2718();
193 assert_eq!(tx_hash, &TxHash::from(hex!("a43d09cb299eb6269f5a63fb10ea078c649cbf6a5f159cfd5b6f4be7ad0dfcfd")));
194 assert!(!tx_rlp.is_empty());
195 Ok(())
196 }
197
198 #[test]
201 fn test_new_signers() {
202 let signers: TxSigners<EthPrimitives> = TxSigners::new();
203 assert!(signers.is_empty());
204 }
205
206 #[test]
207 fn test_len() {
208 let mut signers = TxSigners::new();
209 assert_eq!(signers.len(), 0);
210 signers.add_testkey();
211 assert_eq!(signers.len(), 1);
212 }
213
214 #[test]
215 fn test_is_empty() {
216 let mut signers = TxSigners::new();
217 assert!(signers.is_empty());
218 signers.add_testkey();
219 assert!(!signers.is_empty());
220 }
221
222 #[test]
223 fn test_add_privkey() {
224 let mut signers = TxSigners::new();
225 let priv_key = Bytes::from(hex!("507485ea5bcf6864596cb51b2e727bb2d8ed5e64bb4f3d8c77a734d2fd610c6e"));
226 let signer = signers.add_privkey(priv_key);
227 assert_eq!(signers.len(), 1);
228 assert_eq!(signer.address(), signers.get_address_vec()[0]);
229 assert_eq!(signer.address(), address!("16Df4b25e4E37A9116eb224799c1e0Fb17fd8d30"));
230 }
231
232 #[test]
233 fn test_add_testkey() {
234 let mut signers = TxSigners::new();
235 let signer = signers.add_testkey();
236 assert_eq!(signers.len(), 1);
237 assert_eq!(signer.address(), signers.get_address_vec()[0]);
238 }
239
240 #[test]
241 fn test_get_random_signer() {
242 let mut signers = TxSigners::new();
243 signers.add_testkey();
244 assert!(signers.get_random_signer().is_some());
245 }
246
247 #[test]
248 fn test_get_signer_by_index() {
249 let mut signers = TxSigners::new();
250 let signer = signers.add_testkey();
251 let address = signer.address();
252 assert!(signers.get_signer_by_index(0).is_ok());
253 let unknown_address = Address::random();
255 assert!(signers.get_signer_by_index(1).is_err());
256 }
257
258 #[test]
259 fn test_get_signer_by_address() {
260 let mut signers = TxSigners::new();
261 let signer = signers.add_testkey();
262 let address = signer.address();
263 assert!(signers.get_signer_by_address(&address).is_ok());
264 let unknown_address = Address::random();
266 assert!(signers.get_signer_by_address(&unknown_address).is_err());
267 }
268
269 #[test]
270 fn test_get_address_vec() {
271 let mut signers = TxSigners::new();
272 assert!(signers.get_address_vec().is_empty());
273 let signer = signers.add_testkey();
274 let addresses = signers.get_address_vec();
275 assert_eq!(addresses.len(), 1);
276 assert_eq!(addresses[0], signer.address());
277 }
278}