1use alloc::sync::Arc;
8use alloy_consensus::{
9 crypto::secp256k1, transaction::Recovered, EthereumTxEnvelope, Signed, TxEip1559, TxEip2930,
10 TxEip4844, TxEip4844Variant, TxEip7702, TxLegacy,
11};
12use alloy_eips::{
13 eip2718::WithEncoded,
14 eip7702::{RecoveredAuthority, RecoveredAuthorization},
15 Typed2718,
16};
17use alloy_primitives::{Address, Bytes, TxKind};
18use revm::{context::TxEnv, context_interface::either::Either};
19
20pub trait IntoTxEnv<TxEnv> {
42 fn into_tx_env(self) -> TxEnv;
44}
45
46impl IntoTxEnv<Self> for TxEnv {
47 fn into_tx_env(self) -> Self {
48 self
49 }
50}
51
52#[auto_impl::auto_impl(&)]
55pub trait ToTxEnv<TxEnv> {
56 fn to_tx_env(&self) -> TxEnv;
58}
59
60impl<T, TxEnv> IntoTxEnv<TxEnv> for T
61where
62 T: ToTxEnv<TxEnv>,
63{
64 fn into_tx_env(self) -> TxEnv {
65 self.to_tx_env()
66 }
67}
68
69impl<L, R, TxEnv> ToTxEnv<TxEnv> for Either<L, R>
70where
71 L: ToTxEnv<TxEnv>,
72 R: ToTxEnv<TxEnv>,
73{
74 fn to_tx_env(&self) -> TxEnv {
75 match self {
76 Self::Left(l) => l.to_tx_env(),
77 Self::Right(r) => r.to_tx_env(),
78 }
79 }
80}
81
82#[cfg(feature = "op")]
83impl<T> IntoTxEnv<Self> for op_revm::OpTransaction<T>
84where
85 T: revm::context_interface::transaction::Transaction,
86{
87 fn into_tx_env(self) -> Self {
88 self
89 }
90}
91
92pub trait FromRecoveredTx<Tx> {
118 fn from_recovered_tx(tx: &Tx, sender: Address) -> Self;
120}
121
122impl<TxEnv, T> FromRecoveredTx<&T> for TxEnv
123where
124 TxEnv: FromRecoveredTx<T>,
125{
126 fn from_recovered_tx(tx: &&T, sender: Address) -> Self {
127 TxEnv::from_recovered_tx(tx, sender)
128 }
129}
130
131impl<T, TxEnv: FromRecoveredTx<T>> ToTxEnv<TxEnv> for Recovered<T> {
132 fn to_tx_env(&self) -> TxEnv {
133 TxEnv::from_recovered_tx(self.inner(), self.signer())
134 }
135}
136
137impl FromRecoveredTx<TxLegacy> for TxEnv {
138 fn from_recovered_tx(tx: &TxLegacy, caller: Address) -> Self {
139 let TxLegacy { chain_id, nonce, gas_price, gas_limit, to, value, input } = tx;
140 Self {
141 tx_type: tx.ty(),
142 caller,
143 gas_limit: *gas_limit,
144 gas_price: *gas_price,
145 kind: *to,
146 value: *value,
147 data: input.clone(),
148 nonce: *nonce,
149 chain_id: *chain_id,
150 ..Default::default()
151 }
152 }
153}
154
155impl FromRecoveredTx<Signed<TxLegacy>> for TxEnv {
156 fn from_recovered_tx(tx: &Signed<TxLegacy>, sender: Address) -> Self {
157 Self::from_recovered_tx(tx.tx(), sender)
158 }
159}
160
161impl FromTxWithEncoded<TxLegacy> for TxEnv {
162 fn from_encoded_tx(tx: &TxLegacy, sender: Address, _encoded: Bytes) -> Self {
163 Self::from_recovered_tx(tx, sender)
164 }
165}
166
167impl FromRecoveredTx<TxEip2930> for TxEnv {
168 fn from_recovered_tx(tx: &TxEip2930, caller: Address) -> Self {
169 let TxEip2930 { chain_id, nonce, gas_price, gas_limit, to, value, access_list, input } = tx;
170 Self {
171 tx_type: tx.ty(),
172 caller,
173 gas_limit: *gas_limit,
174 gas_price: *gas_price,
175 kind: *to,
176 value: *value,
177 data: input.clone(),
178 chain_id: Some(*chain_id),
179 nonce: *nonce,
180 access_list: access_list.clone(),
181 ..Default::default()
182 }
183 }
184}
185
186impl FromRecoveredTx<Signed<TxEip2930>> for TxEnv {
187 fn from_recovered_tx(tx: &Signed<TxEip2930>, sender: Address) -> Self {
188 Self::from_recovered_tx(tx.tx(), sender)
189 }
190}
191
192impl FromTxWithEncoded<TxEip2930> for TxEnv {
193 fn from_encoded_tx(tx: &TxEip2930, sender: Address, _encoded: Bytes) -> Self {
194 Self::from_recovered_tx(tx, sender)
195 }
196}
197
198impl FromRecoveredTx<TxEip1559> for TxEnv {
199 fn from_recovered_tx(tx: &TxEip1559, caller: Address) -> Self {
200 let TxEip1559 {
201 chain_id,
202 nonce,
203 gas_limit,
204 to,
205 value,
206 input,
207 max_fee_per_gas,
208 max_priority_fee_per_gas,
209 access_list,
210 } = tx;
211 Self {
212 tx_type: tx.ty(),
213 caller,
214 gas_limit: *gas_limit,
215 gas_price: *max_fee_per_gas,
216 kind: *to,
217 value: *value,
218 data: input.clone(),
219 nonce: *nonce,
220 chain_id: Some(*chain_id),
221 gas_priority_fee: Some(*max_priority_fee_per_gas),
222 access_list: access_list.clone(),
223 ..Default::default()
224 }
225 }
226}
227
228impl FromRecoveredTx<Signed<TxEip1559>> for TxEnv {
229 fn from_recovered_tx(tx: &Signed<TxEip1559>, sender: Address) -> Self {
230 Self::from_recovered_tx(tx.tx(), sender)
231 }
232}
233
234impl FromTxWithEncoded<TxEip1559> for TxEnv {
235 fn from_encoded_tx(tx: &TxEip1559, sender: Address, _encoded: Bytes) -> Self {
236 Self::from_recovered_tx(tx, sender)
237 }
238}
239
240impl FromRecoveredTx<TxEip4844> for TxEnv {
241 fn from_recovered_tx(tx: &TxEip4844, caller: Address) -> Self {
242 let TxEip4844 {
243 chain_id,
244 nonce,
245 gas_limit,
246 to,
247 value,
248 input,
249 max_fee_per_gas,
250 max_priority_fee_per_gas,
251 access_list,
252 blob_versioned_hashes,
253 max_fee_per_blob_gas,
254 } = tx;
255 Self {
256 tx_type: tx.ty(),
257 caller,
258 gas_limit: *gas_limit,
259 gas_price: *max_fee_per_gas,
260 kind: TxKind::Call(*to),
261 value: *value,
262 data: input.clone(),
263 nonce: *nonce,
264 chain_id: Some(*chain_id),
265 gas_priority_fee: Some(*max_priority_fee_per_gas),
266 access_list: access_list.clone(),
267 blob_hashes: blob_versioned_hashes.clone(),
268 max_fee_per_blob_gas: *max_fee_per_blob_gas,
269 ..Default::default()
270 }
271 }
272}
273
274impl FromRecoveredTx<Signed<TxEip4844>> for TxEnv {
275 fn from_recovered_tx(tx: &Signed<TxEip4844>, sender: Address) -> Self {
276 Self::from_recovered_tx(tx.tx(), sender)
277 }
278}
279
280impl FromTxWithEncoded<TxEip4844> for TxEnv {
281 fn from_encoded_tx(tx: &TxEip4844, sender: Address, _encoded: Bytes) -> Self {
282 Self::from_recovered_tx(tx, sender)
283 }
284}
285
286impl<T> FromRecoveredTx<TxEip4844Variant<T>> for TxEnv {
287 fn from_recovered_tx(tx: &TxEip4844Variant<T>, sender: Address) -> Self {
288 Self::from_recovered_tx(tx.tx(), sender)
289 }
290}
291
292impl<T> FromRecoveredTx<Signed<TxEip4844Variant<T>>> for TxEnv {
293 fn from_recovered_tx(tx: &Signed<TxEip4844Variant<T>>, sender: Address) -> Self {
294 Self::from_recovered_tx(tx.tx(), sender)
295 }
296}
297
298impl<T> FromTxWithEncoded<TxEip4844Variant<T>> for TxEnv {
299 fn from_encoded_tx(tx: &TxEip4844Variant<T>, sender: Address, _encoded: Bytes) -> Self {
300 Self::from_recovered_tx(tx, sender)
301 }
302}
303
304impl FromRecoveredTx<TxEip7702> for TxEnv {
305 fn from_recovered_tx(tx: &TxEip7702, caller: Address) -> Self {
306 let TxEip7702 {
307 chain_id,
308 nonce,
309 gas_limit,
310 to,
311 value,
312 input,
313 max_fee_per_gas,
314 max_priority_fee_per_gas,
315 access_list,
316 authorization_list,
317 } = tx;
318 Self {
319 tx_type: tx.ty(),
320 caller,
321 gas_limit: *gas_limit,
322 gas_price: *max_fee_per_gas,
323 kind: TxKind::Call(*to),
324 value: *value,
325 data: input.clone(),
326 nonce: *nonce,
327 chain_id: Some(*chain_id),
328 gas_priority_fee: Some(*max_priority_fee_per_gas),
329 access_list: access_list.clone(),
330 authorization_list: authorization_list
331 .iter()
332 .map(|auth| {
333 Either::Right(RecoveredAuthorization::new_unchecked(
334 auth.inner().clone(),
335 auth.signature()
336 .ok()
337 .and_then(|signature| {
338 secp256k1::recover_signer(&signature, auth.signature_hash()).ok()
339 })
340 .map_or(RecoveredAuthority::Invalid, RecoveredAuthority::Valid),
341 ))
342 })
343 .collect(),
344 ..Default::default()
345 }
346 }
347}
348
349impl FromRecoveredTx<Signed<TxEip7702>> for TxEnv {
350 fn from_recovered_tx(tx: &Signed<TxEip7702>, sender: Address) -> Self {
351 Self::from_recovered_tx(tx.tx(), sender)
352 }
353}
354
355impl FromTxWithEncoded<TxEip7702> for TxEnv {
356 fn from_encoded_tx(tx: &TxEip7702, sender: Address, _encoded: Bytes) -> Self {
357 Self::from_recovered_tx(tx, sender)
358 }
359}
360
361#[auto_impl::auto_impl(&)]
365pub trait RecoveredTx<T> {
366 fn tx(&self) -> &T;
368
369 fn signer(&self) -> &Address;
371}
372
373impl<T> RecoveredTx<T> for Recovered<&T> {
374 fn tx(&self) -> &T {
375 self.inner()
376 }
377
378 fn signer(&self) -> &Address {
379 self.signer_ref()
380 }
381}
382
383impl<T> RecoveredTx<T> for Recovered<Arc<T>> {
384 fn tx(&self) -> &T {
385 self.inner().as_ref()
386 }
387
388 fn signer(&self) -> &Address {
389 self.signer_ref()
390 }
391}
392
393impl<T> RecoveredTx<T> for Recovered<T> {
394 fn tx(&self) -> &T {
395 self.inner()
396 }
397
398 fn signer(&self) -> &Address {
399 self.signer_ref()
400 }
401}
402
403impl<Tx, T: RecoveredTx<Tx>> RecoveredTx<Tx> for WithEncoded<T> {
404 fn tx(&self) -> &Tx {
405 self.1.tx()
406 }
407
408 fn signer(&self) -> &Address {
409 self.1.signer()
410 }
411}
412
413impl<L, R, Tx> RecoveredTx<Tx> for Either<L, R>
414where
415 L: RecoveredTx<Tx>,
416 R: RecoveredTx<Tx>,
417{
418 fn tx(&self) -> &Tx {
419 match self {
420 Self::Left(l) => l.tx(),
421 Self::Right(r) => r.tx(),
422 }
423 }
424
425 fn signer(&self) -> &Address {
426 match self {
427 Self::Left(l) => l.signer(),
428 Self::Right(r) => r.signer(),
429 }
430 }
431}
432
433pub trait FromTxWithEncoded<Tx> {
464 fn from_encoded_tx(tx: &Tx, sender: Address, encoded: Bytes) -> Self;
466}
467
468impl<TxEnv, T> FromTxWithEncoded<&T> for TxEnv
469where
470 TxEnv: FromTxWithEncoded<T>,
471{
472 fn from_encoded_tx(tx: &&T, sender: Address, encoded: Bytes) -> Self {
473 TxEnv::from_encoded_tx(tx, sender, encoded)
474 }
475}
476
477impl<T, TxEnv: FromTxWithEncoded<T>> ToTxEnv<TxEnv> for WithEncoded<Recovered<T>> {
478 fn to_tx_env(&self) -> TxEnv {
479 let recovered = &self.1;
480 TxEnv::from_encoded_tx(recovered.inner(), recovered.signer(), self.encoded_bytes().clone())
481 }
482}
483
484impl<T, TxEnv: FromTxWithEncoded<T>> ToTxEnv<TxEnv> for WithEncoded<&Recovered<T>> {
485 fn to_tx_env(&self) -> TxEnv {
486 TxEnv::from_encoded_tx(self.value(), *self.value().signer(), self.encoded_bytes().clone())
487 }
488}
489
490impl<Eip4844: AsRef<TxEip4844>> FromTxWithEncoded<EthereumTxEnvelope<Eip4844>> for TxEnv {
491 fn from_encoded_tx(tx: &EthereumTxEnvelope<Eip4844>, caller: Address, encoded: Bytes) -> Self {
492 match tx {
493 EthereumTxEnvelope::Legacy(tx) => Self::from_encoded_tx(tx.tx(), caller, encoded),
494 EthereumTxEnvelope::Eip1559(tx) => Self::from_encoded_tx(tx.tx(), caller, encoded),
495 EthereumTxEnvelope::Eip2930(tx) => Self::from_encoded_tx(tx.tx(), caller, encoded),
496 EthereumTxEnvelope::Eip4844(tx) => {
497 Self::from_encoded_tx(tx.tx().as_ref(), caller, encoded)
498 }
499 EthereumTxEnvelope::Eip7702(tx) => Self::from_encoded_tx(tx.tx(), caller, encoded),
500 }
501 }
502}
503
504impl<Eip4844: AsRef<TxEip4844>> FromRecoveredTx<EthereumTxEnvelope<Eip4844>> for TxEnv {
505 fn from_recovered_tx(tx: &EthereumTxEnvelope<Eip4844>, sender: Address) -> Self {
506 match tx {
507 EthereumTxEnvelope::Legacy(tx) => Self::from_recovered_tx(tx.tx(), sender),
508 EthereumTxEnvelope::Eip1559(tx) => Self::from_recovered_tx(tx.tx(), sender),
509 EthereumTxEnvelope::Eip2930(tx) => Self::from_recovered_tx(tx.tx(), sender),
510 EthereumTxEnvelope::Eip4844(tx) => Self::from_recovered_tx(tx.tx().as_ref(), sender),
511 EthereumTxEnvelope::Eip7702(tx) => Self::from_recovered_tx(tx.tx(), sender),
512 }
513 }
514}
515
516#[cfg(test)]
517mod tests {
518 use super::*;
519
520 struct MyTxEnv;
521 struct MyTransaction;
522
523 impl IntoTxEnv<Self> for MyTxEnv {
524 fn into_tx_env(self) -> Self {
525 self
526 }
527 }
528
529 impl FromRecoveredTx<MyTransaction> for MyTxEnv {
530 fn from_recovered_tx(_tx: &MyTransaction, _sender: Address) -> Self {
531 Self
532 }
533 }
534
535 impl FromTxWithEncoded<MyTransaction> for MyTxEnv {
536 fn from_encoded_tx(_tx: &MyTransaction, _sender: Address, _encoded: Bytes) -> Self {
537 Self
538 }
539 }
540
541 const fn assert_env<T: IntoTxEnv<MyTxEnv>>() {}
542 const fn assert_recoverable<T: RecoveredTx<MyTransaction>>() {}
543
544 #[test]
545 const fn test_into_tx_env() {
546 assert_env::<MyTxEnv>();
547 assert_env::<&Recovered<MyTransaction>>();
548 assert_env::<&Recovered<&MyTransaction>>();
549 }
550
551 #[test]
552 const fn test_into_encoded_tx_env() {
553 assert_env::<WithEncoded<Recovered<MyTransaction>>>();
554 assert_env::<&WithEncoded<Recovered<MyTransaction>>>();
555
556 assert_recoverable::<Recovered<MyTransaction>>();
557 assert_recoverable::<WithEncoded<Recovered<MyTransaction>>>();
558 }
559}