1use alloy_consensus::{
8 crypto::secp256k1, transaction::Recovered, EthereumTxEnvelope, Signed, TxEip1559, TxEip2930,
9 TxEip4844, 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#[auto_impl::auto_impl(&)]
54pub trait ToTxEnv<TxEnv> {
55 fn to_tx_env(&self) -> TxEnv;
57}
58
59impl<T, TxEnv> IntoTxEnv<TxEnv> for T
60where
61 T: ToTxEnv<TxEnv>,
62{
63 fn into_tx_env(self) -> TxEnv {
64 self.to_tx_env()
65 }
66}
67
68impl<L, R, TxEnv> ToTxEnv<TxEnv> for Either<L, R>
69where
70 L: ToTxEnv<TxEnv>,
71 R: ToTxEnv<TxEnv>,
72{
73 fn to_tx_env(&self) -> TxEnv {
74 match self {
75 Self::Left(l) => l.to_tx_env(),
76 Self::Right(r) => r.to_tx_env(),
77 }
78 }
79}
80
81#[cfg(feature = "op")]
82impl<T> IntoTxEnv<Self> for op_revm::OpTransaction<T>
83where
84 T: revm::context_interface::transaction::Transaction,
85{
86 fn into_tx_env(self) -> Self {
87 self
88 }
89}
90
91pub trait FromRecoveredTx<Tx> {
117 fn from_recovered_tx(tx: &Tx, sender: Address) -> Self;
119}
120
121impl<TxEnv, T> FromRecoveredTx<&T> for TxEnv
122where
123 TxEnv: FromRecoveredTx<T>,
124{
125 fn from_recovered_tx(tx: &&T, sender: Address) -> Self {
126 TxEnv::from_recovered_tx(tx, sender)
127 }
128}
129
130impl<T, TxEnv: FromRecoveredTx<T>> ToTxEnv<TxEnv> for Recovered<T> {
131 fn to_tx_env(&self) -> TxEnv {
132 TxEnv::from_recovered_tx(self.inner(), self.signer())
133 }
134}
135
136impl FromRecoveredTx<TxLegacy> for TxEnv {
137 fn from_recovered_tx(tx: &TxLegacy, caller: Address) -> Self {
138 let TxLegacy { chain_id, nonce, gas_price, gas_limit, to, value, 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 nonce: *nonce,
148 chain_id: *chain_id,
149 ..Default::default()
150 }
151 }
152}
153
154impl FromRecoveredTx<Signed<TxLegacy>> for TxEnv {
155 fn from_recovered_tx(tx: &Signed<TxLegacy>, sender: Address) -> Self {
156 Self::from_recovered_tx(tx.tx(), sender)
157 }
158}
159
160impl FromTxWithEncoded<TxLegacy> for TxEnv {
161 fn from_encoded_tx(tx: &TxLegacy, sender: Address, _encoded: Bytes) -> Self {
162 Self::from_recovered_tx(tx, sender)
163 }
164}
165
166impl FromRecoveredTx<TxEip2930> for TxEnv {
167 fn from_recovered_tx(tx: &TxEip2930, caller: Address) -> Self {
168 let TxEip2930 { chain_id, nonce, gas_price, gas_limit, to, value, access_list, input } = tx;
169 Self {
170 tx_type: tx.ty(),
171 caller,
172 gas_limit: *gas_limit,
173 gas_price: *gas_price,
174 kind: *to,
175 value: *value,
176 data: input.clone(),
177 chain_id: Some(*chain_id),
178 nonce: *nonce,
179 access_list: access_list.clone(),
180 ..Default::default()
181 }
182 }
183}
184
185impl FromRecoveredTx<Signed<TxEip2930>> for TxEnv {
186 fn from_recovered_tx(tx: &Signed<TxEip2930>, sender: Address) -> Self {
187 Self::from_recovered_tx(tx.tx(), sender)
188 }
189}
190
191impl FromTxWithEncoded<TxEip2930> for TxEnv {
192 fn from_encoded_tx(tx: &TxEip2930, sender: Address, _encoded: Bytes) -> Self {
193 Self::from_recovered_tx(tx, sender)
194 }
195}
196
197impl FromRecoveredTx<TxEip1559> for TxEnv {
198 fn from_recovered_tx(tx: &TxEip1559, caller: Address) -> Self {
199 let TxEip1559 {
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 } = tx;
210 Self {
211 tx_type: tx.ty(),
212 caller,
213 gas_limit: *gas_limit,
214 gas_price: *max_fee_per_gas,
215 kind: *to,
216 value: *value,
217 data: input.clone(),
218 nonce: *nonce,
219 chain_id: Some(*chain_id),
220 gas_priority_fee: Some(*max_priority_fee_per_gas),
221 access_list: access_list.clone(),
222 ..Default::default()
223 }
224 }
225}
226
227impl FromRecoveredTx<Signed<TxEip1559>> for TxEnv {
228 fn from_recovered_tx(tx: &Signed<TxEip1559>, sender: Address) -> Self {
229 Self::from_recovered_tx(tx.tx(), sender)
230 }
231}
232
233impl FromTxWithEncoded<TxEip1559> for TxEnv {
234 fn from_encoded_tx(tx: &TxEip1559, sender: Address, _encoded: Bytes) -> Self {
235 Self::from_recovered_tx(tx, sender)
236 }
237}
238
239impl FromRecoveredTx<TxEip4844> for TxEnv {
240 fn from_recovered_tx(tx: &TxEip4844, caller: Address) -> Self {
241 let TxEip4844 {
242 chain_id,
243 nonce,
244 gas_limit,
245 to,
246 value,
247 input,
248 max_fee_per_gas,
249 max_priority_fee_per_gas,
250 access_list,
251 blob_versioned_hashes,
252 max_fee_per_blob_gas,
253 } = tx;
254 Self {
255 tx_type: tx.ty(),
256 caller,
257 gas_limit: *gas_limit,
258 gas_price: *max_fee_per_gas,
259 kind: TxKind::Call(*to),
260 value: *value,
261 data: input.clone(),
262 nonce: *nonce,
263 chain_id: Some(*chain_id),
264 gas_priority_fee: Some(*max_priority_fee_per_gas),
265 access_list: access_list.clone(),
266 blob_hashes: blob_versioned_hashes.clone(),
267 max_fee_per_blob_gas: *max_fee_per_blob_gas,
268 ..Default::default()
269 }
270 }
271}
272
273impl FromRecoveredTx<Signed<TxEip4844>> for TxEnv {
274 fn from_recovered_tx(tx: &Signed<TxEip4844>, sender: Address) -> Self {
275 Self::from_recovered_tx(tx.tx(), sender)
276 }
277}
278
279impl FromTxWithEncoded<TxEip4844> for TxEnv {
280 fn from_encoded_tx(tx: &TxEip4844, sender: Address, _encoded: Bytes) -> Self {
281 Self::from_recovered_tx(tx, sender)
282 }
283}
284
285impl FromRecoveredTx<TxEip7702> for TxEnv {
286 fn from_recovered_tx(tx: &TxEip7702, caller: Address) -> Self {
287 let TxEip7702 {
288 chain_id,
289 nonce,
290 gas_limit,
291 to,
292 value,
293 input,
294 max_fee_per_gas,
295 max_priority_fee_per_gas,
296 access_list,
297 authorization_list,
298 } = tx;
299 Self {
300 tx_type: tx.ty(),
301 caller,
302 gas_limit: *gas_limit,
303 gas_price: *max_fee_per_gas,
304 kind: TxKind::Call(*to),
305 value: *value,
306 data: input.clone(),
307 nonce: *nonce,
308 chain_id: Some(*chain_id),
309 gas_priority_fee: Some(*max_priority_fee_per_gas),
310 access_list: access_list.clone(),
311 authorization_list: authorization_list
312 .iter()
313 .map(|auth| {
314 Either::Right(RecoveredAuthorization::new_unchecked(
315 auth.inner().clone(),
316 auth.signature()
317 .ok()
318 .and_then(|signature| {
319 secp256k1::recover_signer(&signature, auth.signature_hash()).ok()
320 })
321 .map_or(RecoveredAuthority::Invalid, RecoveredAuthority::Valid),
322 ))
323 })
324 .collect(),
325 ..Default::default()
326 }
327 }
328}
329
330impl FromRecoveredTx<Signed<TxEip7702>> for TxEnv {
331 fn from_recovered_tx(tx: &Signed<TxEip7702>, sender: Address) -> Self {
332 Self::from_recovered_tx(tx.tx(), sender)
333 }
334}
335
336impl FromTxWithEncoded<TxEip7702> for TxEnv {
337 fn from_encoded_tx(tx: &TxEip7702, sender: Address, _encoded: Bytes) -> Self {
338 Self::from_recovered_tx(tx, sender)
339 }
340}
341
342#[auto_impl::auto_impl(&)]
346pub trait RecoveredTx<T> {
347 fn tx(&self) -> &T;
349
350 fn signer(&self) -> &Address;
352}
353
354impl<T> RecoveredTx<T> for Recovered<&T> {
355 fn tx(&self) -> &T {
356 self.inner()
357 }
358
359 fn signer(&self) -> &Address {
360 self.signer_ref()
361 }
362}
363
364impl<T> RecoveredTx<T> for Recovered<T> {
365 fn tx(&self) -> &T {
366 self.inner()
367 }
368
369 fn signer(&self) -> &Address {
370 self.signer_ref()
371 }
372}
373
374impl<Tx, T: RecoveredTx<Tx>> RecoveredTx<Tx> for WithEncoded<T> {
375 fn tx(&self) -> &Tx {
376 self.1.tx()
377 }
378
379 fn signer(&self) -> &Address {
380 self.1.signer()
381 }
382}
383
384impl<L, R, Tx> RecoveredTx<Tx> for Either<L, R>
385where
386 L: RecoveredTx<Tx>,
387 R: RecoveredTx<Tx>,
388{
389 fn tx(&self) -> &Tx {
390 match self {
391 Self::Left(l) => l.tx(),
392 Self::Right(r) => r.tx(),
393 }
394 }
395
396 fn signer(&self) -> &Address {
397 match self {
398 Self::Left(l) => l.signer(),
399 Self::Right(r) => r.signer(),
400 }
401 }
402}
403
404pub trait FromTxWithEncoded<Tx> {
435 fn from_encoded_tx(tx: &Tx, sender: Address, encoded: Bytes) -> Self;
437}
438
439impl<TxEnv, T> FromTxWithEncoded<&T> for TxEnv
440where
441 TxEnv: FromTxWithEncoded<T>,
442{
443 fn from_encoded_tx(tx: &&T, sender: Address, encoded: Bytes) -> Self {
444 TxEnv::from_encoded_tx(tx, sender, encoded)
445 }
446}
447
448impl<T, TxEnv: FromTxWithEncoded<T>> ToTxEnv<TxEnv> for WithEncoded<Recovered<T>> {
449 fn to_tx_env(&self) -> TxEnv {
450 let recovered = &self.1;
451 TxEnv::from_encoded_tx(recovered.inner(), recovered.signer(), self.encoded_bytes().clone())
452 }
453}
454
455impl<T, TxEnv: FromTxWithEncoded<T>> ToTxEnv<TxEnv> for WithEncoded<&Recovered<T>> {
456 fn to_tx_env(&self) -> TxEnv {
457 TxEnv::from_encoded_tx(self.value(), *self.value().signer(), self.encoded_bytes().clone())
458 }
459}
460
461impl<Eip4844: AsRef<TxEip4844>> FromTxWithEncoded<EthereumTxEnvelope<Eip4844>> for TxEnv {
462 fn from_encoded_tx(tx: &EthereumTxEnvelope<Eip4844>, caller: Address, encoded: Bytes) -> Self {
463 match tx {
464 EthereumTxEnvelope::Legacy(tx) => Self::from_encoded_tx(tx.tx(), caller, encoded),
465 EthereumTxEnvelope::Eip1559(tx) => Self::from_encoded_tx(tx.tx(), caller, encoded),
466 EthereumTxEnvelope::Eip2930(tx) => Self::from_encoded_tx(tx.tx(), caller, encoded),
467 EthereumTxEnvelope::Eip4844(tx) => {
468 Self::from_encoded_tx(tx.tx().as_ref(), caller, encoded)
469 }
470 EthereumTxEnvelope::Eip7702(tx) => Self::from_encoded_tx(tx.tx(), caller, encoded),
471 }
472 }
473}
474
475impl<Eip4844: AsRef<TxEip4844>> FromRecoveredTx<EthereumTxEnvelope<Eip4844>> for TxEnv {
476 fn from_recovered_tx(tx: &EthereumTxEnvelope<Eip4844>, sender: Address) -> Self {
477 match tx {
478 EthereumTxEnvelope::Legacy(tx) => Self::from_recovered_tx(tx.tx(), sender),
479 EthereumTxEnvelope::Eip1559(tx) => Self::from_recovered_tx(tx.tx(), sender),
480 EthereumTxEnvelope::Eip2930(tx) => Self::from_recovered_tx(tx.tx(), sender),
481 EthereumTxEnvelope::Eip4844(tx) => Self::from_recovered_tx(tx.tx().as_ref(), sender),
482 EthereumTxEnvelope::Eip7702(tx) => Self::from_recovered_tx(tx.tx(), sender),
483 }
484 }
485}
486
487#[cfg(test)]
488mod tests {
489 use super::*;
490
491 struct MyTxEnv;
492 struct MyTransaction;
493
494 impl IntoTxEnv<Self> for MyTxEnv {
495 fn into_tx_env(self) -> Self {
496 self
497 }
498 }
499
500 impl FromRecoveredTx<MyTransaction> for MyTxEnv {
501 fn from_recovered_tx(_tx: &MyTransaction, _sender: Address) -> Self {
502 Self
503 }
504 }
505
506 impl FromTxWithEncoded<MyTransaction> for MyTxEnv {
507 fn from_encoded_tx(_tx: &MyTransaction, _sender: Address, _encoded: Bytes) -> Self {
508 Self
509 }
510 }
511
512 const fn assert_env<T: IntoTxEnv<MyTxEnv>>() {}
513 const fn assert_recoverable<T: RecoveredTx<MyTransaction>>() {}
514
515 #[test]
516 const fn test_into_tx_env() {
517 assert_env::<MyTxEnv>();
518 assert_env::<&Recovered<MyTransaction>>();
519 assert_env::<&Recovered<&MyTransaction>>();
520 }
521
522 #[test]
523 const fn test_into_encoded_tx_env() {
524 assert_env::<WithEncoded<Recovered<MyTransaction>>>();
525 assert_env::<&WithEncoded<Recovered<MyTransaction>>>();
526
527 assert_recoverable::<Recovered<MyTransaction>>();
528 assert_recoverable::<WithEncoded<Recovered<MyTransaction>>>();
529 }
530}