reth_transaction_pool/test_utils/
gen.rs1use crate::EthPooledTransaction;
2use alloy_consensus::{TxEip1559, TxEip4844, TxLegacy};
3use alloy_eips::{eip1559::MIN_PROTOCOL_BASE_FEE, eip2718::Encodable2718, eip2930::AccessList};
4use alloy_primitives::{Address, Bytes, TxKind, B256, U256};
5use rand::Rng;
6use reth_chainspec::MAINNET;
7use reth_primitives::{sign_message, Transaction, TransactionSigned};
8
9#[derive(Debug)]
11pub struct TransactionGenerator<R> {
12 pub rng: R,
14 pub signer_keys: Vec<B256>,
16 pub base_fee: u128,
18 pub gas_limit: u64,
20}
21
22impl<R: Rng> TransactionGenerator<R> {
23 pub fn new(rng: R) -> Self {
25 Self::with_num_signers(rng, 10)
26 }
27
28 pub fn with_num_signers(rng: R, num_signers: usize) -> Self {
30 Self {
31 rng,
32 signer_keys: (0..num_signers).map(|_| B256::random()).collect(),
33 base_fee: MIN_PROTOCOL_BASE_FEE as u128,
34 gas_limit: 300_000,
35 }
36 }
37
38 pub fn push_signer(&mut self, signer: B256) -> &mut Self {
40 self.signer_keys.push(signer);
41 self
42 }
43
44 pub fn set_gas_limit(&mut self, gas_limit: u64) -> &mut Self {
46 self.gas_limit = gas_limit;
47 self
48 }
49
50 pub const fn with_gas_limit(mut self, gas_limit: u64) -> Self {
52 self.gas_limit = gas_limit;
53 self
54 }
55
56 pub fn set_base_fee(&mut self, base_fee: u64) -> &mut Self {
58 self.base_fee = base_fee as u128;
59 self
60 }
61
62 pub const fn with_base_fee(mut self, base_fee: u64) -> Self {
64 self.base_fee = base_fee as u128;
65 self
66 }
67
68 pub fn extend_signers(&mut self, signers: impl IntoIterator<Item = B256>) -> &mut Self {
70 self.signer_keys.extend(signers);
71 self
72 }
73
74 fn rng_signer(&mut self) -> B256 {
76 let idx = self.rng.gen_range(0..self.signer_keys.len());
77 self.signer_keys[idx]
78 }
79
80 pub fn transaction(&mut self) -> TransactionBuilder {
82 TransactionBuilder::default()
83 .signer(self.rng_signer())
84 .max_fee_per_gas(self.base_fee)
85 .max_priority_fee_per_gas(self.base_fee)
86 .gas_limit(self.gas_limit)
87 }
88
89 pub fn gen_eip1559(&mut self) -> TransactionSigned {
91 self.transaction().into_eip1559()
92 }
93
94 pub fn gen_eip4844(&mut self) -> TransactionSigned {
96 self.transaction().into_eip4844()
97 }
98
99 pub fn gen_eip1559_pooled(&mut self) -> EthPooledTransaction {
101 self.gen_eip1559().into_ecrecovered().unwrap().try_into().unwrap()
102 }
103
104 pub fn gen_eip4844_pooled(&mut self) -> EthPooledTransaction {
106 let tx = self.gen_eip4844().into_ecrecovered().unwrap();
107 let encoded_length = tx.encode_2718_len();
108 EthPooledTransaction::new(tx, encoded_length)
109 }
110}
111
112#[derive(Debug)]
114pub struct TransactionBuilder {
115 pub signer: B256,
117 pub chain_id: u64,
119 pub nonce: u64,
121 pub gas_limit: u64,
123 pub max_fee_per_gas: u128,
125 pub max_priority_fee_per_gas: u128,
128 pub to: TxKind,
130 pub value: U256,
132 pub access_list: AccessList,
134 pub input: Bytes,
137}
138
139impl TransactionBuilder {
140 pub fn into_legacy(self) -> TransactionSigned {
142 Self::signed(
143 TxLegacy {
144 chain_id: Some(self.chain_id),
145 nonce: self.nonce,
146 gas_limit: self.gas_limit,
147 gas_price: self.max_fee_per_gas,
148 to: self.to,
149 value: self.value,
150 input: self.input,
151 }
152 .into(),
153 self.signer,
154 )
155 }
156
157 pub fn into_eip1559(self) -> TransactionSigned {
159 Self::signed(
160 TxEip1559 {
161 chain_id: self.chain_id,
162 nonce: self.nonce,
163 gas_limit: self.gas_limit,
164 max_fee_per_gas: self.max_fee_per_gas,
165 max_priority_fee_per_gas: self.max_priority_fee_per_gas,
166 to: self.to,
167 value: self.value,
168 access_list: self.access_list,
169 input: self.input,
170 }
171 .into(),
172 self.signer,
173 )
174 }
175 pub fn into_eip4844(self) -> TransactionSigned {
177 Self::signed(
178 TxEip4844 {
179 chain_id: self.chain_id,
180 nonce: self.nonce,
181 gas_limit: self.gas_limit,
182 max_fee_per_gas: self.max_fee_per_gas,
183 max_priority_fee_per_gas: self.max_priority_fee_per_gas,
184 to: match self.to {
185 TxKind::Call(to) => to,
186 TxKind::Create => Address::default(),
187 },
188 value: self.value,
189 access_list: self.access_list,
190 input: self.input,
191 blob_versioned_hashes: Default::default(),
192 max_fee_per_blob_gas: Default::default(),
193 }
194 .into(),
195 self.signer,
196 )
197 }
198
199 fn signed(transaction: Transaction, signer: B256) -> TransactionSigned {
201 let signature = sign_message(signer, transaction.signature_hash()).unwrap();
202 TransactionSigned::new_unhashed(transaction, signature)
203 }
204
205 pub const fn signer(mut self, signer: B256) -> Self {
207 self.signer = signer;
208 self
209 }
210
211 pub const fn gas_limit(mut self, gas_limit: u64) -> Self {
213 self.gas_limit = gas_limit;
214 self
215 }
216
217 pub const fn nonce(mut self, nonce: u64) -> Self {
219 self.nonce = nonce;
220 self
221 }
222
223 pub const fn inc_nonce(mut self) -> Self {
225 self.nonce += 1;
226 self
227 }
228
229 pub const fn decr_nonce(mut self) -> Self {
231 self.nonce = self.nonce.saturating_sub(1);
232 self
233 }
234
235 pub const fn max_fee_per_gas(mut self, max_fee_per_gas: u128) -> Self {
237 self.max_fee_per_gas = max_fee_per_gas;
238 self
239 }
240
241 pub const fn max_priority_fee_per_gas(mut self, max_priority_fee_per_gas: u128) -> Self {
243 self.max_priority_fee_per_gas = max_priority_fee_per_gas;
244 self
245 }
246
247 pub const fn to(mut self, to: Address) -> Self {
249 self.to = TxKind::Call(to);
250 self
251 }
252
253 pub fn value(mut self, value: u128) -> Self {
255 self.value = U256::from(value);
256 self
257 }
258
259 pub fn access_list(mut self, access_list: AccessList) -> Self {
261 self.access_list = access_list;
262 self
263 }
264
265 pub fn input(mut self, input: impl Into<Bytes>) -> Self {
267 self.input = input.into();
268 self
269 }
270
271 pub const fn chain_id(mut self, chain_id: u64) -> Self {
273 self.chain_id = chain_id;
274 self
275 }
276
277 pub fn set_chain_id(&mut self, chain_id: u64) -> &mut Self {
279 self.chain_id = chain_id;
280 self
281 }
282
283 pub fn set_nonce(&mut self, nonce: u64) -> &mut Self {
285 self.nonce = nonce;
286 self
287 }
288
289 pub fn set_gas_limit(&mut self, gas_limit: u64) -> &mut Self {
291 self.gas_limit = gas_limit;
292 self
293 }
294
295 pub fn set_max_fee_per_gas(&mut self, max_fee_per_gas: u128) -> &mut Self {
297 self.max_fee_per_gas = max_fee_per_gas;
298 self
299 }
300
301 pub fn set_max_priority_fee_per_gas(&mut self, max_priority_fee_per_gas: u128) -> &mut Self {
303 self.max_priority_fee_per_gas = max_priority_fee_per_gas;
304 self
305 }
306
307 pub fn set_to(&mut self, to: Address) -> &mut Self {
309 self.to = to.into();
310 self
311 }
312
313 pub fn set_value(&mut self, value: u128) -> &mut Self {
315 self.value = U256::from(value);
316 self
317 }
318
319 pub fn set_access_list(&mut self, access_list: AccessList) -> &mut Self {
321 self.access_list = access_list;
322 self
323 }
324
325 pub fn set_signer(&mut self, signer: B256) -> &mut Self {
327 self.signer = signer;
328 self
329 }
330
331 pub fn set_input(&mut self, input: impl Into<Bytes>) -> &mut Self {
333 self.input = input.into();
334 self
335 }
336}
337
338impl Default for TransactionBuilder {
339 fn default() -> Self {
340 Self {
341 signer: B256::random(),
342 chain_id: MAINNET.chain.id(),
343 nonce: 0,
344 gas_limit: 0,
345 max_fee_per_gas: 0,
346 max_priority_fee_per_gas: 0,
347 to: Default::default(),
348 value: Default::default(),
349 access_list: Default::default(),
350 input: Default::default(),
351 }
352 }
353}
354
355#[cfg(test)]
356mod tests {
357 use super::*;
358 use rand::thread_rng;
359
360 #[test]
361 fn test_generate_transaction() {
362 let rng = thread_rng();
363 let mut gen = TransactionGenerator::new(rng);
364 let _tx = gen.transaction().into_legacy();
365 let _tx = gen.transaction().into_eip1559();
366 }
367}