signet_evm/sys/
transact.rs

1use crate::sys::{MeteredSysTx, SysBase, SysTx, TransactSysLog};
2use alloy::{
3    consensus::{EthereumTxEnvelope, Transaction},
4    hex,
5    primitives::{utils::format_ether, Address, Bytes, Log, TxKind, U256},
6};
7use core::fmt;
8use signet_extract::ExtractedEvent;
9use signet_types::{primitives::TransactionSigned, MagicSig, MagicSigInfo};
10use signet_zenith::Transactor;
11use trevm::{revm::context::TxEnv, Tx};
12
13/// Shim to impl [`Tx`] for [`Transactor::Transact`].
14#[derive(PartialEq, Eq)]
15pub struct TransactSysTx {
16    /// The transact transaction.
17    tx: TransactionSigned,
18
19    /// The nonce of the transaction.
20    nonce: Option<u64>,
21
22    /// The magic sig. Memoized here to make it a little simpler to
23    /// access. Also available on the [`MagicSig`] in the transaction above.
24    magic_sig: MagicSig,
25}
26
27impl TransactSysTx {
28    /// Instantiate a new [`TransactSysTx`].
29    pub fn new<R>(transact: &ExtractedEvent<'_, R, Transactor::Transact>, aliased: bool) -> Self {
30        let magic_sig = transact.magic_sig(aliased);
31        let tx = transact.make_transaction(0, aliased);
32        Self { tx, nonce: None, magic_sig }
33    }
34
35    /// Check if the sender was aliased (i.e. the sender is a smart contract on
36    /// the host chain).
37    pub fn is_aliased(&self) -> bool {
38        match self.magic_sig.ty {
39            MagicSigInfo::Transact { aliased, .. } => aliased,
40            _ => unreachable!(),
41        }
42    }
43
44    /// Create a [`TransactSysLog`] from the filler.
45    fn make_sys_log(&self) -> TransactSysLog {
46        TransactSysLog {
47            txHash: self.magic_sig.txid,
48            logIndex: self.magic_sig.event_idx as u64,
49            sender: self.evm_sender(),
50            value: self.tx.value(),
51            gas: U256::from(self.tx.gas_limit()),
52            maxFeePerGas: U256::from(self.tx.max_fee_per_gas()),
53        }
54    }
55}
56
57// NB: manual impl because of incorrect auto-derive bound on `R: Debug`
58impl fmt::Debug for TransactSysTx {
59    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60        f.debug_struct("TransactSysTx")
61            .field("transact", &self.tx)
62            .field("nonce", &self.nonce)
63            .field("magic_sig", &self.magic_sig)
64            .finish()
65    }
66}
67
68// NB: manual impl because of incorrect auto-derive bound on `R: Clone`
69impl Clone for TransactSysTx {
70    fn clone(&self) -> Self {
71        Self { tx: self.tx.clone(), nonce: self.nonce, magic_sig: self.magic_sig }
72    }
73}
74
75impl Tx for TransactSysTx {
76    fn fill_tx_env(&self, tx_env: &mut TxEnv) {
77        self.tx.as_eip1559().unwrap().fill_tx_env(tx_env);
78        tx_env.caller = self.magic_sig.rollup_sender();
79    }
80}
81
82impl SysBase for TransactSysTx {
83    fn name() -> &'static str {
84        "TransactSysTx"
85    }
86
87    fn description(&self) -> String {
88        let is_aliased = if self.is_aliased() { " (aliased)" } else { "" };
89
90        format!(
91            "Transact from {}{is_aliased} to {} with value {} and {} bytes of input data: `0x{}{}`",
92            self.magic_sig.rollup_sender(),
93            self.tx.to().expect("creates not allowed"),
94            format_ether(self.tx.value()),
95            self.tx.input().len(),
96            self.tx.input().chunks(4).next().map(hex::encode).unwrap_or_default(),
97            if self.tx.input().len() > 4 { "..." } else { "" },
98        )
99    }
100
101    fn has_nonce(&self) -> bool {
102        self.nonce.is_some()
103    }
104
105    fn populate_nonce(&mut self, nonce: u64) {
106        // NB: we have to set the nonce on the tx as well. Setting the nonce on
107        // the TX will change its hash, but will not invalidate the magic sig
108        // (as it's not a real signature).
109        let EthereumTxEnvelope::Eip1559(signed) = &mut self.tx else {
110            unreachable!("new sets this to 1559");
111        };
112        signed.tx_mut().nonce = nonce;
113        self.nonce = Some(nonce);
114    }
115
116    fn produce_transaction(&self) -> TransactionSigned {
117        self.tx.clone()
118    }
119
120    fn produce_log(&self) -> Log {
121        self.make_sys_log().into()
122    }
123
124    fn evm_sender(&self) -> Address {
125        self.magic_sig.rollup_sender()
126    }
127}
128
129impl SysTx for TransactSysTx {
130    fn callee(&self) -> TxKind {
131        self.tx.kind()
132    }
133
134    fn input(&self) -> Bytes {
135        self.tx.input().clone()
136    }
137
138    fn value(&self) -> U256 {
139        self.tx.value()
140    }
141}
142
143impl MeteredSysTx for TransactSysTx {
144    fn gas_limit(&self) -> u128 {
145        self.tx.gas_limit() as u128
146    }
147
148    fn max_fee_per_gas(&self) -> u128 {
149        self.tx.max_fee_per_gas()
150    }
151}