Skip to main content

mega_evme/common/
tx.rs

1//! Transaction configuration for mega-evme
2
3use alloy_primitives::{address, Address, Bytes, Signature, B256, U256};
4use clap::Args;
5use mega_evm::{
6    alloy_consensus::{
7        transaction::SignerRecoverable, Sealed, Signed, Transaction as _, TxEip1559, TxEip2930,
8        TxEip7702, TxLegacy,
9    },
10    alloy_eips::{
11        eip2930::{AccessList, AccessListItem},
12        eip7702::{Authorization, RecoveredAuthority, RecoveredAuthorization, SignedAuthorization},
13        Decodable2718, Encodable2718, Typed2718 as _,
14    },
15    op_alloy_consensus::{OpTxEnvelope, TxDeposit},
16    op_revm::transaction::deposit::DepositTransactionParts,
17    revm::{context::tx::TxEnv, primitives::TxKind},
18    Either, MegaTransaction, MegaTxEnvelope, MegaTxType,
19};
20use tracing::{debug, trace};
21
22use super::{load_hex, parse_ether_value, EvmeError, Result};
23
24/// Default sender address (Hardhat account #0).
25pub const DEFAULT_SENDER: Address = address!("f39Fd6e51aad88F6F4ce6aB8827279cffFb92266");
26
27/// Transaction configuration arguments
28#[derive(Args, Debug, Clone)]
29#[command(next_help_heading = "Transaction Options")]
30pub struct TxArgs {
31    /// Transaction type (0=Legacy, 1=EIP-2930, 2=EIP-1559, etc.) [default: 0]
32    #[arg(long = "tx-type", visible_aliases = ["type", "ty"])]
33    pub tx_type: Option<u8>,
34
35    /// Gas limit for the evm [default: 10000000]
36    #[arg(long = "gas", visible_aliases = ["gas-limit"])]
37    pub gas: Option<u64>,
38
39    /// Price set for the evm (gas price) [default: 0]
40    #[arg(long = "basefee", visible_aliases = ["gas-price", "price", "base-fee"])]
41    pub basefee: Option<u64>,
42
43    /// Gas priority fee (EIP-1559)
44    #[arg(long = "priority-fee", visible_aliases = ["priorityfee", "tip"])]
45    pub priority_fee: Option<u64>,
46
47    /// The transaction origin [default: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266]
48    #[arg(long = "sender", visible_aliases = ["from"])]
49    pub sender: Option<Address>,
50
51    /// The transaction receiver (execution context)
52    #[arg(long = "receiver", visible_aliases = ["to"])]
53    pub receiver: Option<Address>,
54
55    /// The transaction nonce
56    #[arg(long = "nonce")]
57    pub nonce: Option<u64>,
58
59    /// Indicates the action should be create rather than call
60    #[arg(long = "create")]
61    pub create: Option<bool>,
62
63    /// Value set for the evm.
64    /// VALUE can be: plain number (wei), or number with suffix (ether, gwei, wei).
65    /// Examples: `--value 1ether`, `--value 100gwei`, `--value 1000000000000000000`
66    #[arg(long = "value")]
67    pub value: Option<String>,
68
69    /// Transaction data (input) as hex string
70    #[arg(long = "input", visible_aliases = ["data"])]
71    pub input: Option<String>,
72
73    /// File containing transaction data (input). If '-' is specified, input is read from stdin
74    #[arg(long = "inputfile", visible_aliases = ["datafile", "input-file", "data-file"])]
75    pub inputfile: Option<String>,
76
77    /// Source hash for deposit transactions (tx-type 126)
78    #[arg(long = "source-hash", visible_aliases = ["sourcehash"], value_name = "HASH")]
79    pub source_hash: Option<B256>,
80
81    /// Amount of ETH to mint for deposit transactions (wei)
82    #[arg(long = "mint")]
83    pub mint: Option<u128>,
84
85    /// EIP-7702 authorization in format `AUTHORITY:NONCE->DELEGATION` (can be repeated)
86    #[arg(long = "auth", visible_aliases = ["authorization"], value_name = "AUTH")]
87    pub auth: Vec<String>,
88
89    /// EIP-2930 access list entry in format `ADDRESS` or `ADDRESS:KEY1,KEY2,...` (can be repeated)
90    #[arg(long = "access", visible_aliases = ["accesslist", "access-list"], value_name = "ACCESS")]
91    pub access: Vec<String>,
92}
93
94impl TxArgs {
95    /// Validates transaction arguments for consistency.
96    ///
97    /// Checks:
98    /// - `source_hash` and `mint` are only set for deposit transactions (tx-type 126)
99    /// - `priority_fee` is not set for legacy or EIP-2930 transactions
100    /// - `receiver` must exist when `create` is false, must not exist when `create` is true
101    /// - `auth` is only set for EIP-7702 transactions (tx-type 4)
102    /// - `access` is only set for EIP-2930, EIP-1559, or EIP-7702 transactions (tx-type 1, 2, 4)
103    pub fn validate(&self) -> Result<()> {
104        let tx_type = self.mega_tx_type()?;
105
106        // 1. source_hash and mint should only be set when tx_type is deposit
107        if tx_type != MegaTxType::Deposit && (self.source_hash.is_some() || self.mint.is_some()) {
108            return Err(EvmeError::InvalidInput(
109                "--source-hash and --mint are only valid for deposit transactions (--tx-type 126)"
110                    .to_string(),
111            ));
112        }
113        if tx_type == MegaTxType::Deposit && self.source_hash.is_none() {
114            return Err(EvmeError::InvalidInput(
115                "--source-hash is required for deposit transactions (--tx-type 126)".to_string(),
116            ));
117        }
118
119        // 2. priority_fee must not be set when tx_type is legacy or eip2930
120        if matches!(tx_type, MegaTxType::Legacy | MegaTxType::Eip2930) &&
121            self.priority_fee.is_some()
122        {
123            return Err(EvmeError::InvalidInput(
124                "--priority-fee is not valid for legacy (0) or EIP-2930 (1) transactions"
125                    .to_string(),
126            ));
127        }
128
129        // 3. receiver must exist when create is false, must not exist when create is true
130        if self.create() && self.receiver.is_some() {
131            return Err(EvmeError::InvalidInput(
132                "--receiver must not be set when --create is specified".to_string(),
133            ));
134        }
135
136        // 4. auth should only be set when tx_type is EIP-7702
137        if tx_type != MegaTxType::Eip7702 && !self.auth.is_empty() {
138            return Err(EvmeError::InvalidInput(
139                "--auth is only valid for EIP-7702 transactions (--tx-type 4)".to_string(),
140            ));
141        }
142
143        // 5. access should only be set when tx_type supports access lists (EIP-2930, EIP-1559,
144        //    EIP-7702)
145        if !self.access.is_empty() &&
146            !matches!(tx_type, MegaTxType::Eip2930 | MegaTxType::Eip1559 | MegaTxType::Eip7702)
147        {
148            return Err(EvmeError::InvalidInput(
149                "--access is only valid for EIP-2930 (1), EIP-1559 (2), or EIP-7702 (4) transactions"
150                    .to_string(),
151            ));
152        }
153
154        Ok(())
155    }
156
157    /// Parses authorization list from CLI arguments.
158    ///
159    /// Format: `AUTHORITY:NONCE->DELEGATION`
160    /// - AUTHORITY: Address of the EOA delegating control
161    /// - NONCE: Authorization nonce (decimal or 0x-prefixed hex)
162    /// - DELEGATION: Address of the contract to delegate to
163    pub(crate) fn parse_authorization_list(
164        &self,
165        chain_id: u64,
166    ) -> Result<Vec<RecoveredAuthorization>> {
167        self.auth.iter().map(|s| Self::parse_authorization(s, chain_id)).collect()
168    }
169
170    /// Parses authorization fields (authority, nonce, delegation) from a single auth string.
171    ///
172    /// Returns `(Authorization, authority_address)`.
173    fn parse_auth_fields(s: &str, chain_id: u64) -> Result<(Authorization, Address)> {
174        // Split by "->" to get authority:nonce and delegation
175        let parts: Vec<&str> = s.split("->").collect();
176        if parts.len() != 2 {
177            return Err(EvmeError::InvalidInput(format!(
178                "Invalid authorization format '{}'. Expected: AUTHORITY:NONCE->DELEGATION",
179                s
180            )));
181        }
182
183        let delegation: Address = parts[1].trim().parse().map_err(|_| {
184            EvmeError::InvalidInput(format!("Invalid delegation address: {}", parts[1].trim()))
185        })?;
186
187        // Split authority:nonce
188        let auth_parts: Vec<&str> = parts[0].split(':').collect();
189        if auth_parts.len() != 2 {
190            return Err(EvmeError::InvalidInput(format!(
191                "Invalid authorization format '{}'. Expected: AUTHORITY:NONCE->DELEGATION",
192                s
193            )));
194        }
195
196        let authority: Address = auth_parts[0].trim().parse().map_err(|_| {
197            EvmeError::InvalidInput(format!("Invalid authority address: {}", auth_parts[0].trim()))
198        })?;
199
200        let nonce: u64 = if auth_parts[1].trim().starts_with("0x") {
201            u64::from_str_radix(auth_parts[1].trim().trim_start_matches("0x"), 16).map_err(
202                |_| EvmeError::InvalidInput(format!("Invalid nonce: {}", auth_parts[1].trim())),
203            )?
204        } else {
205            auth_parts[1].trim().parse().map_err(|_| {
206                EvmeError::InvalidInput(format!("Invalid nonce: {}", auth_parts[1].trim()))
207            })?
208        };
209
210        let auth = Authorization { chain_id: U256::from(chain_id), address: delegation, nonce };
211        Ok((auth, authority))
212    }
213
214    /// Parses a single authorization string.
215    fn parse_authorization(s: &str, chain_id: u64) -> Result<RecoveredAuthorization> {
216        let (auth, authority) = Self::parse_auth_fields(s, chain_id)?;
217
218        trace!(string = %s, chain_id = %chain_id, authority = %authority, delegation = %auth.address, nonce = %auth.nonce, "Parsed authorization");
219        Ok(RecoveredAuthorization::new_unchecked(auth, RecoveredAuthority::Valid(authority)))
220    }
221
222    /// Parses access list from CLI arguments.
223    ///
224    /// Format: `ADDRESS` or `ADDRESS:KEY1,KEY2,...`
225    /// - ADDRESS: The accessed contract address
226    /// - KEY1,KEY2,...: Comma-separated storage keys (B256 hex values)
227    pub(crate) fn parse_access_list(&self) -> Result<AccessList> {
228        let items: Result<Vec<AccessListItem>> =
229            self.access.iter().map(|s| Self::parse_access_list_item(s)).collect();
230        Ok(AccessList(items?))
231    }
232
233    /// Parses a single access list item.
234    fn parse_access_list_item(s: &str) -> Result<AccessListItem> {
235        // Check if there's a colon (storage keys present)
236        if let Some((addr_str, keys_str)) = s.split_once(':') {
237            let address: Address = addr_str.trim().parse().map_err(|_| {
238                EvmeError::InvalidInput(format!("Invalid access list address: {}", addr_str.trim()))
239            })?;
240
241            let storage_keys: Result<Vec<B256>> = keys_str
242                .split(',')
243                .map(|k| {
244                    k.trim().parse().map_err(|_| {
245                        EvmeError::InvalidInput(format!("Invalid storage key: {}", k.trim()))
246                    })
247                })
248                .collect();
249
250            trace!(string = %s, address = %address, storage_keys = ?storage_keys, "Parsed access list item");
251            Ok(AccessListItem { address, storage_keys: storage_keys? })
252        } else {
253            // No storage keys, just address
254            let address: Address = s.trim().parse().map_err(|_| {
255                EvmeError::InvalidInput(format!("Invalid access list address: {}", s.trim()))
256            })?;
257
258            trace!(string = %s, address = %address, "Parsed access list item");
259            Ok(AccessListItem { address, storage_keys: Vec::new() })
260        }
261    }
262
263    /// Returns the gas limit, defaulting to 10,000,000.
264    pub fn gas(&self) -> u64 {
265        self.gas.unwrap_or(10_000_000)
266    }
267
268    /// Returns the base fee, defaulting to 0.
269    pub fn basefee(&self) -> u64 {
270        self.basefee.unwrap_or(0)
271    }
272
273    /// Returns the sender address, defaulting to Hardhat account #0.
274    pub fn sender(&self) -> Address {
275        self.sender.unwrap_or(DEFAULT_SENDER)
276    }
277
278    /// Returns whether this is a create transaction, defaulting to false.
279    pub fn create(&self) -> bool {
280        self.create.unwrap_or(false)
281    }
282
283    /// Returns the receiver address.
284    pub fn receiver(&self) -> Address {
285        self.receiver.unwrap_or_default()
286    }
287
288    /// Returns the parsed value, defaulting to 0.
289    pub fn value(&self) -> Result<U256> {
290        self.value.as_deref().map(parse_ether_value).transpose().map(|v| v.unwrap_or_default())
291    }
292
293    /// Returns the raw transaction type, defaulting to 0 (Legacy).
294    pub fn tx_type(&self) -> u8 {
295        self.tx_type.unwrap_or(0)
296    }
297
298    /// Converts the transaction type to a [`MegaTxType`].
299    pub fn mega_tx_type(&self) -> Result<MegaTxType> {
300        let ty = self.tx_type();
301        match ty {
302            0 => Ok(MegaTxType::Legacy),
303            1 => Ok(MegaTxType::Eip2930),
304            2 => Ok(MegaTxType::Eip1559),
305            4 => Ok(MegaTxType::Eip7702),
306            126 => Ok(MegaTxType::Deposit),
307            _ => Err(EvmeError::UnsupportedTxType(ty)),
308        }
309    }
310
311    /// Creates a [`TxEnv`] from the transaction arguments.
312    ///
313    /// Loads input data from `--input` or `--inputfile` arguments.
314    /// Parses authorization list from `--auth` for EIP-7702 transactions.
315    /// Parses access list from `--access` for EIP-2930/EIP-1559/EIP-7702 transactions.
316    pub fn create_tx_env(&self, chain_id: u64) -> Result<TxEnv> {
317        self.validate()?;
318
319        let data = load_hex(self.input.clone(), self.inputfile.clone())?.unwrap_or_default();
320        let kind = if self.create() { TxKind::Create } else { TxKind::Call(self.receiver()) };
321        let authorization_list =
322            self.parse_authorization_list(chain_id)?.into_iter().map(Either::Right).collect();
323        let access_list = self.parse_access_list()?;
324
325        let tx = TxEnv {
326            caller: self.sender(),
327            gas_price: self.basefee() as u128,
328            gas_priority_fee: self.priority_fee.map(|pf| pf as u128),
329            blob_hashes: Vec::new(),
330            max_fee_per_blob_gas: 0,
331            tx_type: self.tx_type(),
332            gas_limit: self.gas(),
333            data,
334            nonce: self.nonce.unwrap_or(0),
335            value: self.value()?,
336            access_list,
337            authorization_list,
338            kind,
339            chain_id: Some(chain_id),
340        };
341        debug!(tx = ?tx, "Creating TxEnv");
342        Ok(tx)
343    }
344
345    /// Creates a [`MegaTransaction`] from the transaction arguments.
346    ///
347    /// Loads input data from `--input` or `--inputfile` arguments.
348    pub fn create_tx(&self, chain_id: u64) -> Result<MegaTransaction> {
349        let tx_env = self.create_tx_env(chain_id)?;
350        let envelope = create_fake_envelope(&tx_env)?;
351        let mut tx = MegaTransaction::new(tx_env);
352        tx.enveloped_tx = Some(Bytes::from(envelope.encoded_2718()));
353
354        // Set deposit fields if this is a deposit transaction (type 126)
355        if self.mega_tx_type()? == MegaTxType::Deposit {
356            tx.deposit = DepositTransactionParts {
357                source_hash: self.source_hash.unwrap_or(B256::ZERO),
358                mint: self.mint,
359                is_system_transaction: false,
360            };
361        }
362
363        Ok(tx)
364    }
365}
366
367/// Result of decoding a raw EIP-2718 transaction.
368#[derive(Debug)]
369pub struct DecodedRawTx {
370    /// The decoded transaction environment.
371    pub tx_env: TxEnv,
372    /// The original raw EIP-2718 encoded bytes.
373    pub raw_bytes: Bytes,
374    /// Deposit-specific fields, if this is a deposit transaction.
375    /// `(source_hash, mint, is_system_transaction)`
376    pub deposit: Option<(B256, Option<u128>, bool)>,
377}
378
379impl DecodedRawTx {
380    /// Decodes raw EIP-2718 encoded transaction bytes into a [`TxEnv`].
381    ///
382    /// Recovers the signer from the signature (or uses the `from` field for deposits)
383    /// and extracts all transaction fields. No CLI overrides are applied.
384    pub fn from_raw(raw_bytes: impl Into<Bytes>) -> Result<Self> {
385        let raw_bytes = raw_bytes.into();
386        let envelope = OpTxEnvelope::decode_2718(&mut &raw_bytes[..]).map_err(|e| {
387            EvmeError::InvalidInput(format!("Failed to decode raw transaction: {e}"))
388        })?;
389
390        let caller = envelope
391            .recover_signer()
392            .map_err(|e| EvmeError::InvalidInput(format!("Failed to recover signer: {e}")))?;
393
394        let deposit = envelope.as_deposit().map(|d| {
395            let mint = if d.mint == 0 { None } else { Some(d.mint) };
396            (d.source_hash, mint, d.is_system_transaction)
397        });
398
399        let decoded_chain_id = envelope.chain_id();
400        let (gas_price, gas_priority_fee) = match envelope {
401            OpTxEnvelope::Legacy(_) | OpTxEnvelope::Eip2930(_) => {
402                (envelope.gas_price().unwrap_or(0), None)
403            }
404            OpTxEnvelope::Eip1559(_) | OpTxEnvelope::Eip7702(_) => {
405                (envelope.max_fee_per_gas(), envelope.max_priority_fee_per_gas())
406            }
407            OpTxEnvelope::Deposit(_) => (0, None),
408        };
409
410        let authorization_list = envelope
411            .authorization_list()
412            .map(|list| list.iter().map(|sa| Either::Right(sa.clone().into_recovered())).collect())
413            .unwrap_or_default();
414
415        let tx_env = TxEnv {
416            caller,
417            gas_price,
418            gas_priority_fee,
419            blob_hashes: Vec::new(),
420            max_fee_per_blob_gas: 0,
421            tx_type: envelope.ty(),
422            gas_limit: envelope.gas_limit(),
423            data: envelope.input().clone(),
424            nonce: envelope.nonce(),
425            value: envelope.value(),
426            access_list: envelope.access_list().cloned().unwrap_or_default(),
427            authorization_list,
428            kind: envelope.kind(),
429            chain_id: decoded_chain_id,
430        };
431
432        Ok(Self { tx_env, raw_bytes, deposit })
433    }
434
435    /// Applies explicitly-set [`TxArgs`] fields as overrides to the decoded [`TxEnv`].
436    ///
437    /// Only fields that were explicitly provided via CLI flags are overridden;
438    /// `None` / empty fields in `tx_args` leave the base value unchanged.
439    pub fn override_tx_env(mut self, tx_args: &TxArgs) -> Result<Self> {
440        if let Some(tx_type) = tx_args.tx_type {
441            self.tx_env.tx_type = tx_type;
442        }
443        if let Some(gas) = tx_args.gas {
444            self.tx_env.gas_limit = gas;
445        }
446        if let Some(basefee) = tx_args.basefee {
447            self.tx_env.gas_price = basefee as u128;
448        }
449        if let Some(priority_fee) = tx_args.priority_fee {
450            self.tx_env.gas_priority_fee = Some(priority_fee as u128);
451        }
452        if let Some(sender) = tx_args.sender {
453            self.tx_env.caller = sender;
454        }
455        if let Some(ref value) = tx_args.value {
456            self.tx_env.value = parse_ether_value(value)?;
457        }
458        if let Some(nonce) = tx_args.nonce {
459            self.tx_env.nonce = nonce;
460        }
461        if tx_args.input.is_some() || tx_args.inputfile.is_some() {
462            self.tx_env.data =
463                load_hex(tx_args.input.clone(), tx_args.inputfile.clone())?.unwrap_or_default();
464        }
465        if tx_args.create.unwrap_or(false) {
466            self.tx_env.kind = TxKind::Create;
467        } else if let Some(receiver) = tx_args.receiver {
468            self.tx_env.kind = TxKind::Call(receiver);
469        }
470        if !tx_args.access.is_empty() {
471            self.tx_env.access_list = tx_args.parse_access_list()?;
472        }
473        if !tx_args.auth.is_empty() {
474            let chain_id = self.tx_env.chain_id.unwrap_or(0);
475            self.tx_env.authorization_list = tx_args
476                .parse_authorization_list(chain_id)?
477                .into_iter()
478                .map(Either::Right)
479                .collect();
480        }
481        if let Some((ref mut source_hash, ref mut mint, _)) = self.deposit {
482            if let Some(sh) = tx_args.source_hash {
483                *source_hash = sh;
484            }
485            if tx_args.mint.is_some() {
486                *mint = tx_args.mint;
487            }
488        }
489        Ok(self)
490    }
491
492    /// Converts the decoded raw transaction into a [`MegaTransaction`].
493    ///
494    /// Uses the stored raw bytes for `enveloped_tx` (used in L1 fee calculation).
495    pub fn into_tx(self) -> MegaTransaction {
496        let mut tx = MegaTransaction::new(self.tx_env);
497        tx.enveloped_tx = Some(self.raw_bytes);
498        if let Some((source_hash, mint, is_system_transaction)) = self.deposit {
499            tx.deposit = DepositTransactionParts { source_hash, mint, is_system_transaction };
500        }
501        tx
502    }
503}
504
505/// Constructs a fake [`MegaTxEnvelope`] from a [`TxEnv`] for EIP-2718 encoding.
506///
507/// The created envelope only contains the information available in [`TxEnv`] and fills many
508/// fields with placeholder values. The encoded envelope is used for L1 data fee calculation.
509/// A dummy signature is used since the CLI doesn't have access to signing keys — the non-zero
510/// r/s bytes ensure the encoded size is realistic (matching real signed transactions).
511fn create_fake_envelope(tx_env: &TxEnv) -> Result<MegaTxEnvelope> {
512    let dummy_sig = Signature::new(U256::from(1u64), U256::from(1u64), false);
513    let chain_id = tx_env.chain_id.unwrap_or(0);
514    let tx_type = MegaTxType::try_from(tx_env.tx_type)
515        .map_err(|_| EvmeError::UnsupportedTxType(tx_env.tx_type))?;
516
517    match tx_type {
518        MegaTxType::Legacy => {
519            let tx = TxLegacy {
520                chain_id: tx_env.chain_id,
521                nonce: tx_env.nonce,
522                gas_price: tx_env.gas_price,
523                gas_limit: tx_env.gas_limit,
524                to: tx_env.kind,
525                value: tx_env.value,
526                input: tx_env.data.clone(),
527            };
528            Ok(MegaTxEnvelope::Legacy(Signed::new_unchecked(tx, dummy_sig, Default::default())))
529        }
530        MegaTxType::Eip2930 => {
531            let tx = TxEip2930 {
532                chain_id,
533                nonce: tx_env.nonce,
534                gas_price: tx_env.gas_price,
535                gas_limit: tx_env.gas_limit,
536                to: tx_env.kind,
537                value: tx_env.value,
538                access_list: tx_env.access_list.clone(),
539                input: tx_env.data.clone(),
540            };
541            Ok(MegaTxEnvelope::Eip2930(Signed::new_unchecked(tx, dummy_sig, Default::default())))
542        }
543        MegaTxType::Eip1559 => {
544            let tx = TxEip1559 {
545                chain_id,
546                nonce: tx_env.nonce,
547                gas_limit: tx_env.gas_limit,
548                max_fee_per_gas: tx_env.gas_price,
549                max_priority_fee_per_gas: tx_env.gas_priority_fee.unwrap_or(0),
550                to: tx_env.kind,
551                value: tx_env.value,
552                access_list: tx_env.access_list.clone(),
553                input: tx_env.data.clone(),
554            };
555            Ok(MegaTxEnvelope::Eip1559(Signed::new_unchecked(tx, dummy_sig, Default::default())))
556        }
557        MegaTxType::Eip7702 => {
558            let to = match tx_env.kind {
559                TxKind::Call(addr) => addr,
560                TxKind::Create => {
561                    return Err(EvmeError::InvalidInput(
562                        "EIP-7702 transactions cannot be contract creation".to_string(),
563                    ));
564                }
565            };
566
567            let authorization_list: Vec<SignedAuthorization> = tx_env
568                .authorization_list
569                .iter()
570                .map(|either| match either {
571                    Either::Left(signed) => signed.clone(),
572                    Either::Right(recovered) => SignedAuthorization::new_unchecked(
573                        Authorization::clone(recovered),
574                        0,
575                        U256::from(1u64),
576                        U256::from(1u64),
577                    ),
578                })
579                .collect();
580
581            let tx = TxEip7702 {
582                chain_id,
583                nonce: tx_env.nonce,
584                gas_limit: tx_env.gas_limit,
585                max_fee_per_gas: tx_env.gas_price,
586                max_priority_fee_per_gas: tx_env.gas_priority_fee.unwrap_or(0),
587                to,
588                value: tx_env.value,
589                access_list: tx_env.access_list.clone(),
590                authorization_list,
591                input: tx_env.data.clone(),
592            };
593            Ok(MegaTxEnvelope::Eip7702(Signed::new_unchecked(tx, dummy_sig, Default::default())))
594        }
595        MegaTxType::Deposit => {
596            let tx = TxDeposit {
597                source_hash: B256::ZERO,
598                from: tx_env.caller,
599                to: tx_env.kind,
600                mint: 0,
601                value: tx_env.value,
602                gas_limit: tx_env.gas_limit,
603                is_system_transaction: false,
604                input: tx_env.data.clone(),
605            };
606            Ok(MegaTxEnvelope::Deposit(Sealed::new_unchecked(tx, B256::ZERO)))
607        }
608    }
609}