1use alloy_consensus::{
8 crypto::secp256k1, transaction::Recovered, EthereumTxEnvelope, TxEip1559, TxEip2930, TxEip4844,
9 TxEip7702, TxLegacy,
10};
11use alloy_eips::{
12 eip2718::WithEncoded,
13 eip7702::{RecoveredAuthority, RecoveredAuthorization},
14 Typed2718,
15};
16use alloy_primitives::{Address, Bytes, TxKind};
17use revm::{context::TxEnv, context_interface::either::Either};
18
19pub trait IntoTxEnv<TxEnv> {
41 fn into_tx_env(self) -> TxEnv;
43}
44
45impl IntoTxEnv<Self> for TxEnv {
46 fn into_tx_env(self) -> Self {
47 self
48 }
49}
50
51#[cfg(feature = "op")]
52impl<T> IntoTxEnv<Self> for op_revm::OpTransaction<T>
53where
54 T: revm::context_interface::transaction::Transaction,
55{
56 fn into_tx_env(self) -> Self {
57 self
58 }
59}
60
61pub trait FromRecoveredTx<Tx> {
87 fn from_recovered_tx(tx: &Tx, sender: Address) -> Self;
89}
90
91impl<TxEnv, T> FromRecoveredTx<&T> for TxEnv
92where
93 TxEnv: FromRecoveredTx<T>,
94{
95 fn from_recovered_tx(tx: &&T, sender: Address) -> Self {
96 TxEnv::from_recovered_tx(tx, sender)
97 }
98}
99
100impl<T, TxEnv: FromRecoveredTx<T>> IntoTxEnv<TxEnv> for Recovered<T> {
101 fn into_tx_env(self) -> TxEnv {
102 IntoTxEnv::into_tx_env(&self)
103 }
104}
105
106impl<T, TxEnv: FromRecoveredTx<T>> IntoTxEnv<TxEnv> for &Recovered<T> {
107 fn into_tx_env(self) -> TxEnv {
108 TxEnv::from_recovered_tx(self.inner(), self.signer())
109 }
110}
111
112impl FromRecoveredTx<TxLegacy> for TxEnv {
113 fn from_recovered_tx(tx: &TxLegacy, caller: Address) -> Self {
114 let TxLegacy { chain_id, nonce, gas_price, gas_limit, to, value, input } = tx;
115 Self {
116 tx_type: tx.ty(),
117 caller,
118 gas_limit: *gas_limit,
119 gas_price: *gas_price,
120 kind: *to,
121 value: *value,
122 data: input.clone(),
123 nonce: *nonce,
124 chain_id: *chain_id,
125 ..Default::default()
126 }
127 }
128}
129
130impl FromTxWithEncoded<TxLegacy> for TxEnv {
131 fn from_encoded_tx(tx: &TxLegacy, sender: Address, _encoded: Bytes) -> Self {
132 Self::from_recovered_tx(tx, sender)
133 }
134}
135
136impl FromRecoveredTx<TxEip2930> for TxEnv {
137 fn from_recovered_tx(tx: &TxEip2930, caller: Address) -> Self {
138 let TxEip2930 { chain_id, nonce, gas_price, gas_limit, to, value, access_list, input } = tx;
139 Self {
140 tx_type: tx.ty(),
141 caller,
142 gas_limit: *gas_limit,
143 gas_price: *gas_price,
144 kind: *to,
145 value: *value,
146 data: input.clone(),
147 chain_id: Some(*chain_id),
148 nonce: *nonce,
149 access_list: access_list.clone(),
150 ..Default::default()
151 }
152 }
153}
154
155impl FromTxWithEncoded<TxEip2930> for TxEnv {
156 fn from_encoded_tx(tx: &TxEip2930, sender: Address, _encoded: Bytes) -> Self {
157 Self::from_recovered_tx(tx, sender)
158 }
159}
160
161impl FromRecoveredTx<TxEip1559> for TxEnv {
162 fn from_recovered_tx(tx: &TxEip1559, caller: Address) -> Self {
163 let TxEip1559 {
164 chain_id,
165 nonce,
166 gas_limit,
167 to,
168 value,
169 input,
170 max_fee_per_gas,
171 max_priority_fee_per_gas,
172 access_list,
173 } = tx;
174 Self {
175 tx_type: tx.ty(),
176 caller,
177 gas_limit: *gas_limit,
178 gas_price: *max_fee_per_gas,
179 kind: *to,
180 value: *value,
181 data: input.clone(),
182 nonce: *nonce,
183 chain_id: Some(*chain_id),
184 gas_priority_fee: Some(*max_priority_fee_per_gas),
185 access_list: access_list.clone(),
186 ..Default::default()
187 }
188 }
189}
190
191impl FromTxWithEncoded<TxEip1559> for TxEnv {
192 fn from_encoded_tx(tx: &TxEip1559, sender: Address, _encoded: Bytes) -> Self {
193 Self::from_recovered_tx(tx, sender)
194 }
195}
196
197impl FromRecoveredTx<TxEip4844> for TxEnv {
198 fn from_recovered_tx(tx: &TxEip4844, caller: Address) -> Self {
199 let TxEip4844 {
200 chain_id,
201 nonce,
202 gas_limit,
203 to,
204 value,
205 input,
206 max_fee_per_gas,
207 max_priority_fee_per_gas,
208 access_list,
209 blob_versioned_hashes,
210 max_fee_per_blob_gas,
211 } = tx;
212 Self {
213 tx_type: tx.ty(),
214 caller,
215 gas_limit: *gas_limit,
216 gas_price: *max_fee_per_gas,
217 kind: TxKind::Call(*to),
218 value: *value,
219 data: input.clone(),
220 nonce: *nonce,
221 chain_id: Some(*chain_id),
222 gas_priority_fee: Some(*max_priority_fee_per_gas),
223 access_list: access_list.clone(),
224 blob_hashes: blob_versioned_hashes.clone(),
225 max_fee_per_blob_gas: *max_fee_per_blob_gas,
226 ..Default::default()
227 }
228 }
229}
230
231impl FromTxWithEncoded<TxEip4844> for TxEnv {
232 fn from_encoded_tx(tx: &TxEip4844, sender: Address, _encoded: Bytes) -> Self {
233 Self::from_recovered_tx(tx, sender)
234 }
235}
236
237impl FromRecoveredTx<TxEip7702> for TxEnv {
238 fn from_recovered_tx(tx: &TxEip7702, caller: Address) -> Self {
239 let TxEip7702 {
240 chain_id,
241 nonce,
242 gas_limit,
243 to,
244 value,
245 input,
246 max_fee_per_gas,
247 max_priority_fee_per_gas,
248 access_list,
249 authorization_list,
250 } = tx;
251 Self {
252 tx_type: tx.ty(),
253 caller,
254 gas_limit: *gas_limit,
255 gas_price: *max_fee_per_gas,
256 kind: TxKind::Call(*to),
257 value: *value,
258 data: input.clone(),
259 nonce: *nonce,
260 chain_id: Some(*chain_id),
261 gas_priority_fee: Some(*max_priority_fee_per_gas),
262 access_list: access_list.clone(),
263 authorization_list: authorization_list
264 .iter()
265 .map(|auth| {
266 Either::Right(RecoveredAuthorization::new_unchecked(
267 auth.inner().clone(),
268 auth.signature()
269 .ok()
270 .and_then(|signature| {
271 secp256k1::recover_signer(&signature, auth.signature_hash()).ok()
272 })
273 .map_or(RecoveredAuthority::Invalid, RecoveredAuthority::Valid),
274 ))
275 })
276 .collect(),
277 ..Default::default()
278 }
279 }
280}
281
282impl FromTxWithEncoded<TxEip7702> for TxEnv {
283 fn from_encoded_tx(tx: &TxEip7702, sender: Address, _encoded: Bytes) -> Self {
284 Self::from_recovered_tx(tx, sender)
285 }
286}
287
288#[auto_impl::auto_impl(&)]
292pub trait RecoveredTx<T> {
293 fn tx(&self) -> &T;
295
296 fn signer(&self) -> &Address;
298}
299
300impl<T> RecoveredTx<T> for Recovered<&T> {
301 fn tx(&self) -> &T {
302 self.inner()
303 }
304
305 fn signer(&self) -> &Address {
306 self.signer_ref()
307 }
308}
309
310impl<T> RecoveredTx<T> for Recovered<T> {
311 fn tx(&self) -> &T {
312 self.inner()
313 }
314
315 fn signer(&self) -> &Address {
316 self.signer_ref()
317 }
318}
319
320impl<Tx, T: RecoveredTx<Tx>> RecoveredTx<Tx> for WithEncoded<T> {
321 fn tx(&self) -> &Tx {
322 self.1.tx()
323 }
324
325 fn signer(&self) -> &Address {
326 self.1.signer()
327 }
328}
329
330pub trait FromTxWithEncoded<Tx> {
361 fn from_encoded_tx(tx: &Tx, sender: Address, encoded: Bytes) -> Self;
363}
364
365impl<TxEnv, T> FromTxWithEncoded<&T> for TxEnv
366where
367 TxEnv: FromTxWithEncoded<T>,
368{
369 fn from_encoded_tx(tx: &&T, sender: Address, encoded: Bytes) -> Self {
370 TxEnv::from_encoded_tx(tx, sender, encoded)
371 }
372}
373
374impl<T, TxEnv: FromTxWithEncoded<T>> IntoTxEnv<TxEnv> for WithEncoded<Recovered<T>> {
375 fn into_tx_env(self) -> TxEnv {
376 let recovered = &self.1;
377 TxEnv::from_encoded_tx(recovered.inner(), recovered.signer(), self.encoded_bytes().clone())
378 }
379}
380
381impl<T, TxEnv: FromTxWithEncoded<T>> IntoTxEnv<TxEnv> for &WithEncoded<Recovered<T>> {
382 fn into_tx_env(self) -> TxEnv {
383 let recovered = &self.1;
384 TxEnv::from_encoded_tx(recovered.inner(), recovered.signer(), self.encoded_bytes().clone())
385 }
386}
387
388impl<T, TxEnv: FromTxWithEncoded<T>> IntoTxEnv<TxEnv> for WithEncoded<&Recovered<T>> {
389 fn into_tx_env(self) -> TxEnv {
390 TxEnv::from_encoded_tx(self.value(), *self.value().signer(), self.encoded_bytes().clone())
391 }
392}
393
394impl<T, TxEnv: FromTxWithEncoded<T>> IntoTxEnv<TxEnv> for &WithEncoded<&Recovered<T>> {
395 fn into_tx_env(self) -> TxEnv {
396 TxEnv::from_encoded_tx(self.value(), *self.value().signer(), self.encoded_bytes().clone())
397 }
398}
399
400impl<Eip4844: AsRef<TxEip4844>> FromTxWithEncoded<EthereumTxEnvelope<Eip4844>> for TxEnv {
401 fn from_encoded_tx(tx: &EthereumTxEnvelope<Eip4844>, caller: Address, encoded: Bytes) -> Self {
402 match tx {
403 EthereumTxEnvelope::Legacy(tx) => Self::from_encoded_tx(tx.tx(), caller, encoded),
404 EthereumTxEnvelope::Eip1559(tx) => Self::from_encoded_tx(tx.tx(), caller, encoded),
405 EthereumTxEnvelope::Eip2930(tx) => Self::from_encoded_tx(tx.tx(), caller, encoded),
406 EthereumTxEnvelope::Eip4844(tx) => {
407 Self::from_encoded_tx(tx.tx().as_ref(), caller, encoded)
408 }
409 EthereumTxEnvelope::Eip7702(tx) => Self::from_encoded_tx(tx.tx(), caller, encoded),
410 }
411 }
412}
413
414impl<Eip4844: AsRef<TxEip4844>> FromRecoveredTx<EthereumTxEnvelope<Eip4844>> for TxEnv {
415 fn from_recovered_tx(tx: &EthereumTxEnvelope<Eip4844>, sender: Address) -> Self {
416 match tx {
417 EthereumTxEnvelope::Legacy(tx) => Self::from_recovered_tx(tx.tx(), sender),
418 EthereumTxEnvelope::Eip1559(tx) => Self::from_recovered_tx(tx.tx(), sender),
419 EthereumTxEnvelope::Eip2930(tx) => Self::from_recovered_tx(tx.tx(), sender),
420 EthereumTxEnvelope::Eip4844(tx) => Self::from_recovered_tx(tx.tx().as_ref(), sender),
421 EthereumTxEnvelope::Eip7702(tx) => Self::from_recovered_tx(tx.tx(), sender),
422 }
423 }
424}
425
426#[cfg(feature = "op")]
427mod op {
428 use super::*;
429 use alloy_eips::{Encodable2718, Typed2718};
430 use alloy_primitives::{Address, Bytes};
431 use op_alloy_consensus::{OpTxEnvelope, TxDeposit};
432 use op_revm::{transaction::deposit::DepositTransactionParts, OpTransaction};
433 use revm::context::TxEnv;
434
435 impl FromRecoveredTx<OpTxEnvelope> for TxEnv {
436 fn from_recovered_tx(tx: &OpTxEnvelope, caller: Address) -> Self {
437 match tx {
438 OpTxEnvelope::Legacy(tx) => Self::from_recovered_tx(tx.tx(), caller),
439 OpTxEnvelope::Eip1559(tx) => Self::from_recovered_tx(tx.tx(), caller),
440 OpTxEnvelope::Eip2930(tx) => Self::from_recovered_tx(tx.tx(), caller),
441 OpTxEnvelope::Eip7702(tx) => Self::from_recovered_tx(tx.tx(), caller),
442 OpTxEnvelope::Deposit(tx) => Self::from_recovered_tx(tx.inner(), caller),
443 }
444 }
445 }
446
447 impl FromRecoveredTx<TxDeposit> for TxEnv {
448 fn from_recovered_tx(tx: &TxDeposit, caller: Address) -> Self {
449 let TxDeposit {
450 to,
451 value,
452 gas_limit,
453 input,
454 source_hash: _,
455 from: _,
456 mint: _,
457 is_system_transaction: _,
458 } = tx;
459 Self {
460 tx_type: tx.ty(),
461 caller,
462 gas_limit: *gas_limit,
463 kind: *to,
464 value: *value,
465 data: input.clone(),
466 ..Default::default()
467 }
468 }
469 }
470
471 impl FromTxWithEncoded<OpTxEnvelope> for TxEnv {
472 fn from_encoded_tx(tx: &OpTxEnvelope, caller: Address, _encoded: Bytes) -> Self {
473 Self::from_recovered_tx(tx, caller)
474 }
475 }
476
477 impl FromTxWithEncoded<OpTxEnvelope> for OpTransaction<TxEnv> {
478 fn from_encoded_tx(tx: &OpTxEnvelope, caller: Address, encoded: Bytes) -> Self {
479 let base = TxEnv::from_recovered_tx(tx, caller);
480
481 let deposit = if let OpTxEnvelope::Deposit(tx) = tx {
482 DepositTransactionParts {
483 source_hash: tx.source_hash,
484 mint: Some(tx.mint),
485 is_system_transaction: tx.is_system_transaction,
486 }
487 } else {
488 Default::default()
489 };
490
491 Self { base, enveloped_tx: Some(encoded), deposit }
492 }
493 }
494
495 impl FromRecoveredTx<OpTxEnvelope> for OpTransaction<TxEnv> {
496 fn from_recovered_tx(tx: &OpTxEnvelope, sender: Address) -> Self {
497 let encoded = tx.encoded_2718();
498 Self::from_encoded_tx(tx, sender, encoded.into())
499 }
500 }
501}
502
503#[cfg(test)]
504mod tests {
505 use super::*;
506
507 struct MyTxEnv;
508 struct MyTransaction;
509
510 impl IntoTxEnv<Self> for MyTxEnv {
511 fn into_tx_env(self) -> Self {
512 self
513 }
514 }
515
516 impl FromRecoveredTx<MyTransaction> for MyTxEnv {
517 fn from_recovered_tx(_tx: &MyTransaction, _sender: Address) -> Self {
518 Self
519 }
520 }
521
522 impl FromTxWithEncoded<MyTransaction> for MyTxEnv {
523 fn from_encoded_tx(_tx: &MyTransaction, _sender: Address, _encoded: Bytes) -> Self {
524 Self
525 }
526 }
527
528 const fn assert_env<T: IntoTxEnv<MyTxEnv>>() {}
529 const fn assert_recoverable<T: RecoveredTx<MyTransaction>>() {}
530
531 #[test]
532 const fn test_into_tx_env() {
533 assert_env::<MyTxEnv>();
534 assert_env::<&Recovered<MyTransaction>>();
535 assert_env::<&Recovered<&MyTransaction>>();
536 }
537
538 #[test]
539 const fn test_into_encoded_tx_env() {
540 assert_env::<WithEncoded<Recovered<MyTransaction>>>();
541 assert_env::<&WithEncoded<Recovered<MyTransaction>>>();
542
543 assert_recoverable::<Recovered<MyTransaction>>();
544 assert_recoverable::<WithEncoded<Recovered<MyTransaction>>>();
545 }
546}